|
|
|
|
@@ -416,7 +416,7 @@ static void ufs_mtk_dbg_sel(struct ufs_hba *hba)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ufs_mtk_wait_idle_state(struct ufs_hba *hba,
|
|
|
|
|
static int ufs_mtk_wait_idle_state(struct ufs_hba *hba,
|
|
|
|
|
unsigned long retry_ms)
|
|
|
|
|
{
|
|
|
|
|
u64 timeout, time_checked;
|
|
|
|
|
@@ -452,8 +452,12 @@ static void ufs_mtk_wait_idle_state(struct ufs_hba *hba,
|
|
|
|
|
break;
|
|
|
|
|
} while (time_checked < timeout);
|
|
|
|
|
|
|
|
|
|
if (wait_idle && sm != VS_HCE_BASE)
|
|
|
|
|
if (wait_idle && sm != VS_HCE_BASE) {
|
|
|
|
|
dev_info(hba->dev, "wait idle tmo: 0x%x\n", val);
|
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ufs_mtk_wait_link_state(struct ufs_hba *hba, u32 state,
|
|
|
|
|
@@ -1429,19 +1433,53 @@ static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba,
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ufs_mtk_auto_hibern8_disable(struct ufs_hba *hba)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/* disable auto-hibern8 */
|
|
|
|
|
ufshcd_writel(hba, 0, REG_AUTO_HIBERNATE_IDLE_TIMER);
|
|
|
|
|
|
|
|
|
|
/* wait host return to idle state when auto-hibern8 off */
|
|
|
|
|
ret = ufs_mtk_wait_idle_state(hba, 5);
|
|
|
|
|
if (ret)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
ret = ufs_mtk_wait_link_state(hba, VS_LINK_UP, 100);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
if (ret) {
|
|
|
|
|
dev_warn(hba->dev, "exit h8 state fail, ret=%d\n", ret);
|
|
|
|
|
|
|
|
|
|
ufshcd_force_error_recovery(hba);
|
|
|
|
|
|
|
|
|
|
/* trigger error handler and break suspend */
|
|
|
|
|
ret = -EBUSY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ufs_mtk_pwr_change_notify(struct ufs_hba *hba,
|
|
|
|
|
enum ufs_notify_change_status stage,
|
|
|
|
|
const struct ufs_pa_layer_attr *dev_max_params,
|
|
|
|
|
struct ufs_pa_layer_attr *dev_req_params)
|
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
static u32 reg;
|
|
|
|
|
|
|
|
|
|
switch (stage) {
|
|
|
|
|
case PRE_CHANGE:
|
|
|
|
|
if (ufshcd_is_auto_hibern8_supported(hba)) {
|
|
|
|
|
reg = ufshcd_readl(hba, REG_AUTO_HIBERNATE_IDLE_TIMER);
|
|
|
|
|
ufs_mtk_auto_hibern8_disable(hba);
|
|
|
|
|
}
|
|
|
|
|
ret = ufs_mtk_pre_pwr_change(hba, dev_max_params,
|
|
|
|
|
dev_req_params);
|
|
|
|
|
break;
|
|
|
|
|
case POST_CHANGE:
|
|
|
|
|
if (ufshcd_is_auto_hibern8_supported(hba))
|
|
|
|
|
ufshcd_writel(hba, reg, REG_AUTO_HIBERNATE_IDLE_TIMER);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
@@ -1513,8 +1551,19 @@ static int ufs_mtk_pre_link(struct ufs_hba *hba)
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ufs_mtk_post_link(struct ufs_hba *hba)
|
|
|
|
|
{
|
|
|
|
|
struct ufs_mtk_host *host = ufshcd_get_variant(hba);
|
|
|
|
|
u32 tmp;
|
|
|
|
|
|
|
|
|
|
/* fix device PA_INIT no adapt */
|
|
|
|
|
if (host->ip_ver >= IP_VER_MT6899) {
|
|
|
|
|
ufshcd_dme_get(hba, UIC_ARG_MIB(VS_DEBUGOMC), &tmp);
|
|
|
|
|
tmp |= 0x100;
|
|
|
|
|
ufshcd_dme_set(hba, UIC_ARG_MIB(VS_DEBUGOMC), tmp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* enable unipro clock gating feature */
|
|
|
|
|
ufs_mtk_cfg_unipro_cg(hba, true);
|
|
|
|
|
}
|
|
|
|
|
@@ -1584,7 +1633,11 @@ static int ufs_mtk_link_set_hpm(struct ufs_hba *hba)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
/* Check link state to make sure exit h8 success */
|
|
|
|
|
ufs_mtk_wait_idle_state(hba, 5);
|
|
|
|
|
err = ufs_mtk_wait_idle_state(hba, 5);
|
|
|
|
|
if (err) {
|
|
|
|
|
dev_warn(hba->dev, "wait idle fail, err=%d\n", err);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
err = ufs_mtk_wait_link_state(hba, VS_LINK_UP, 100);
|
|
|
|
|
if (err) {
|
|
|
|
|
dev_warn(hba->dev, "exit h8 state fail, err=%d\n", err);
|
|
|
|
|
@@ -1686,21 +1739,6 @@ static void ufs_mtk_dev_vreg_set_lpm(struct ufs_hba *hba, bool lpm)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ufs_mtk_auto_hibern8_disable(struct ufs_hba *hba)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/* disable auto-hibern8 */
|
|
|
|
|
ufshcd_writel(hba, 0, REG_AUTO_HIBERNATE_IDLE_TIMER);
|
|
|
|
|
|
|
|
|
|
/* wait host return to idle state when auto-hibern8 off */
|
|
|
|
|
ufs_mtk_wait_idle_state(hba, 5);
|
|
|
|
|
|
|
|
|
|
ret = ufs_mtk_wait_link_state(hba, VS_LINK_UP, 100);
|
|
|
|
|
if (ret)
|
|
|
|
|
dev_warn(hba->dev, "exit h8 state fail, ret=%d\n", ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
|
|
|
|
|
enum ufs_notify_change_status status)
|
|
|
|
|
{
|
|
|
|
|
@@ -1709,7 +1747,7 @@ static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
|
|
|
|
|
|
|
|
|
|
if (status == PRE_CHANGE) {
|
|
|
|
|
if (ufshcd_is_auto_hibern8_supported(hba))
|
|
|
|
|
ufs_mtk_auto_hibern8_disable(hba);
|
|
|
|
|
return ufs_mtk_auto_hibern8_disable(hba);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1767,8 +1805,21 @@ static int ufs_mtk_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
return ufshcd_link_recovery(hba);
|
|
|
|
|
/*
|
|
|
|
|
* Check if the platform (parent) device has resumed, and ensure that
|
|
|
|
|
* power, clock, and MTCMOS are all turned on.
|
|
|
|
|
*/
|
|
|
|
|
err = ufshcd_link_recovery(hba);
|
|
|
|
|
if (err) {
|
|
|
|
|
dev_err(hba->dev, "Device PM: req=%d, status:%d, err:%d\n",
|
|
|
|
|
hba->dev->power.request,
|
|
|
|
|
hba->dev->power.runtime_status,
|
|
|
|
|
hba->dev->power.runtime_error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0; /* Cannot return a failure, otherwise, the I/O will hang. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ufs_mtk_dbg_register_dump(struct ufs_hba *hba)
|
|
|
|
|
@@ -2139,6 +2190,7 @@ static int ufs_mtk_config_mcq_irq(struct ufs_hba *hba)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
host->is_mcq_intr_enabled = true;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
@@ -2222,10 +2274,12 @@ static const struct ufs_hba_variant_ops ufs_hba_mtk_vops = {
|
|
|
|
|
static int ufs_mtk_probe(struct platform_device *pdev)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct device *dev = &pdev->dev;
|
|
|
|
|
struct device_node *reset_node;
|
|
|
|
|
struct platform_device *reset_pdev;
|
|
|
|
|
struct device *dev = &pdev->dev, *phy_dev = NULL;
|
|
|
|
|
struct device_node *reset_node, *phy_node = NULL;
|
|
|
|
|
struct platform_device *reset_pdev, *phy_pdev = NULL;
|
|
|
|
|
struct device_link *link;
|
|
|
|
|
struct ufs_hba *hba;
|
|
|
|
|
struct ufs_mtk_host *host;
|
|
|
|
|
|
|
|
|
|
reset_node = of_find_compatible_node(NULL, NULL,
|
|
|
|
|
"ti,syscon-reset");
|
|
|
|
|
@@ -2252,13 +2306,51 @@ static int ufs_mtk_probe(struct platform_device *pdev)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
skip_reset:
|
|
|
|
|
/* find phy node */
|
|
|
|
|
phy_node = of_parse_phandle(dev->of_node, "phys", 0);
|
|
|
|
|
|
|
|
|
|
if (phy_node) {
|
|
|
|
|
phy_pdev = of_find_device_by_node(phy_node);
|
|
|
|
|
if (!phy_pdev)
|
|
|
|
|
goto skip_phy;
|
|
|
|
|
phy_dev = &phy_pdev->dev;
|
|
|
|
|
|
|
|
|
|
pm_runtime_set_active(phy_dev);
|
|
|
|
|
pm_runtime_enable(phy_dev);
|
|
|
|
|
pm_runtime_get_sync(phy_dev);
|
|
|
|
|
|
|
|
|
|
put_device(phy_dev);
|
|
|
|
|
dev_info(dev, "phys node found\n");
|
|
|
|
|
} else {
|
|
|
|
|
dev_notice(dev, "phys node not found\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
skip_phy:
|
|
|
|
|
/* perform generic probe */
|
|
|
|
|
err = ufshcd_pltfrm_init(pdev, &ufs_hba_mtk_vops);
|
|
|
|
|
if (err) {
|
|
|
|
|
dev_err(dev, "probe failed %d\n", err);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hba = platform_get_drvdata(pdev);
|
|
|
|
|
if (!hba)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (phy_node && phy_dev) {
|
|
|
|
|
host = ufshcd_get_variant(hba);
|
|
|
|
|
host->phy_dev = phy_dev;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Because the default power setting of VSx (the upper layer of
|
|
|
|
|
* VCCQ/VCCQ2) is HWLP, we need to prevent VCCQ/VCCQ2 from
|
|
|
|
|
* entering LPM.
|
|
|
|
|
*/
|
|
|
|
|
ufs_mtk_dev_vreg_set_lpm(hba, false);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
if (err)
|
|
|
|
|
dev_err(dev, "probe failed %d\n", err);
|
|
|
|
|
|
|
|
|
|
of_node_put(phy_node);
|
|
|
|
|
of_node_put(reset_node);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
@@ -2283,27 +2375,38 @@ static int ufs_mtk_system_suspend(struct device *dev)
|
|
|
|
|
|
|
|
|
|
ret = ufshcd_system_suspend(dev);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (pm_runtime_suspended(hba->dev))
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
ufs_mtk_dev_vreg_set_lpm(hba, true);
|
|
|
|
|
|
|
|
|
|
if (ufs_mtk_is_rtff_mtcmos(hba))
|
|
|
|
|
ufs_mtk_mtcmos_ctrl(false, res);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
out:
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ufs_mtk_system_resume(struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
struct arm_smccc_res res;
|
|
|
|
|
|
|
|
|
|
ufs_mtk_dev_vreg_set_lpm(hba, false);
|
|
|
|
|
if (pm_runtime_suspended(hba->dev))
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (ufs_mtk_is_rtff_mtcmos(hba))
|
|
|
|
|
ufs_mtk_mtcmos_ctrl(true, res);
|
|
|
|
|
|
|
|
|
|
return ufshcd_system_resume(dev);
|
|
|
|
|
ufs_mtk_dev_vreg_set_lpm(hba, false);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
ret = ufshcd_system_resume(dev);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
@@ -2311,6 +2414,7 @@ static int ufs_mtk_system_resume(struct device *dev)
|
|
|
|
|
static int ufs_mtk_runtime_suspend(struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
struct ufs_mtk_host *host = ufshcd_get_variant(hba);
|
|
|
|
|
struct arm_smccc_res res;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
@@ -2323,17 +2427,24 @@ static int ufs_mtk_runtime_suspend(struct device *dev)
|
|
|
|
|
if (ufs_mtk_is_rtff_mtcmos(hba))
|
|
|
|
|
ufs_mtk_mtcmos_ctrl(false, res);
|
|
|
|
|
|
|
|
|
|
if (host->phy_dev)
|
|
|
|
|
pm_runtime_put_sync(host->phy_dev);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ufs_mtk_runtime_resume(struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
struct ufs_mtk_host *host = ufshcd_get_variant(hba);
|
|
|
|
|
struct arm_smccc_res res;
|
|
|
|
|
|
|
|
|
|
if (ufs_mtk_is_rtff_mtcmos(hba))
|
|
|
|
|
ufs_mtk_mtcmos_ctrl(true, res);
|
|
|
|
|
|
|
|
|
|
if (host->phy_dev)
|
|
|
|
|
pm_runtime_get_sync(host->phy_dev);
|
|
|
|
|
|
|
|
|
|
ufs_mtk_dev_vreg_set_lpm(hba, false);
|
|
|
|
|
|
|
|
|
|
return ufshcd_runtime_resume(dev);
|
|
|
|
|
|