summaryrefslogtreecommitdiffstats
path: root/drivers/ufs/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ufs/host')
-rw-r--r--drivers/ufs/host/cdns-pltfrm.c5
-rw-r--r--drivers/ufs/host/tc-dwc-g210-pltfrm.c6
-rw-r--r--drivers/ufs/host/ti-j721e-ufs.c6
-rw-r--r--drivers/ufs/host/ufs-exynos.c15
-rw-r--r--drivers/ufs/host/ufs-hisi.c5
-rw-r--r--drivers/ufs/host/ufs-mediatek.c7
-rw-r--r--drivers/ufs/host/ufs-qcom.c217
-rw-r--r--drivers/ufs/host/ufs-qcom.h20
-rw-r--r--drivers/ufs/host/ufs-renesas.c6
-rw-r--r--drivers/ufs/host/ufs-sprd.c5
-rw-r--r--drivers/ufs/host/ufshcd-pci.c5
-rw-r--r--drivers/ufs/host/ufshcd-pltfrm.c147
-rw-r--r--drivers/ufs/host/ufshcd-pltfrm.h2
13 files changed, 351 insertions, 95 deletions
diff --git a/drivers/ufs/host/cdns-pltfrm.c b/drivers/ufs/host/cdns-pltfrm.c
index 2491e7e870..bb30267da4 100644
--- a/drivers/ufs/host/cdns-pltfrm.c
+++ b/drivers/ufs/host/cdns-pltfrm.c
@@ -305,12 +305,11 @@ static int cdns_ufs_pltfrm_probe(struct platform_device *pdev)
*
* Return: 0 (success).
*/
-static int cdns_ufs_pltfrm_remove(struct platform_device *pdev)
+static void cdns_ufs_pltfrm_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
ufshcd_remove(hba);
- return 0;
}
static const struct dev_pm_ops cdns_ufs_dev_pm_ops = {
@@ -322,7 +321,7 @@ static const struct dev_pm_ops cdns_ufs_dev_pm_ops = {
static struct platform_driver cdns_ufs_pltfrm_driver = {
.probe = cdns_ufs_pltfrm_probe,
- .remove = cdns_ufs_pltfrm_remove,
+ .remove_new = cdns_ufs_pltfrm_remove,
.driver = {
.name = "cdns-ufshcd",
.pm = &cdns_ufs_dev_pm_ops,
diff --git a/drivers/ufs/host/tc-dwc-g210-pltfrm.c b/drivers/ufs/host/tc-dwc-g210-pltfrm.c
index 4d5389dd95..a387759260 100644
--- a/drivers/ufs/host/tc-dwc-g210-pltfrm.c
+++ b/drivers/ufs/host/tc-dwc-g210-pltfrm.c
@@ -74,14 +74,12 @@ static int tc_dwc_g210_pltfm_probe(struct platform_device *pdev)
* @pdev: pointer to platform device structure
*
*/
-static int tc_dwc_g210_pltfm_remove(struct platform_device *pdev)
+static void tc_dwc_g210_pltfm_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
pm_runtime_get_sync(&(pdev)->dev);
ufshcd_remove(hba);
-
- return 0;
}
static const struct dev_pm_ops tc_dwc_g210_pltfm_pm_ops = {
@@ -91,7 +89,7 @@ static const struct dev_pm_ops tc_dwc_g210_pltfm_pm_ops = {
static struct platform_driver tc_dwc_g210_pltfm_driver = {
.probe = tc_dwc_g210_pltfm_probe,
- .remove = tc_dwc_g210_pltfm_remove,
+ .remove_new = tc_dwc_g210_pltfm_remove,
.driver = {
.name = "tc-dwc-g210-pltfm",
.pm = &tc_dwc_g210_pltfm_pm_ops,
diff --git a/drivers/ufs/host/ti-j721e-ufs.c b/drivers/ufs/host/ti-j721e-ufs.c
index 117eb7da92..250c22df00 100644
--- a/drivers/ufs/host/ti-j721e-ufs.c
+++ b/drivers/ufs/host/ti-j721e-ufs.c
@@ -65,13 +65,11 @@ disable_pm:
return ret;
}
-static int ti_j721e_ufs_remove(struct platform_device *pdev)
+static void ti_j721e_ufs_remove(struct platform_device *pdev)
{
of_platform_depopulate(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
static const struct of_device_id ti_j721e_ufs_of_match[] = {
@@ -85,7 +83,7 @@ MODULE_DEVICE_TABLE(of, ti_j721e_ufs_of_match);
static struct platform_driver ti_j721e_ufs_driver = {
.probe = ti_j721e_ufs_probe,
- .remove = ti_j721e_ufs_remove,
+ .remove_new = ti_j721e_ufs_remove,
.driver = {
.name = "ti-j721e-ufs",
.of_match_table = ti_j721e_ufs_of_match,
diff --git a/drivers/ufs/host/ufs-exynos.c b/drivers/ufs/host/ufs-exynos.c
index 3396e03885..71bd6dbc05 100644
--- a/drivers/ufs/host/ufs-exynos.c
+++ b/drivers/ufs/host/ufs-exynos.c
@@ -1511,6 +1511,11 @@ static int fsd_ufs_pre_link(struct exynos_ufs *ufs)
return 0;
}
+static void exynos_ufs_config_scsi_dev(struct scsi_device *sdev)
+{
+ blk_queue_update_dma_alignment(sdev->request_queue, SZ_4K - 1);
+}
+
static int fsd_ufs_post_link(struct exynos_ufs *ufs)
{
int i;
@@ -1579,6 +1584,7 @@ static const struct ufs_hba_variant_ops ufs_hba_exynos_ops = {
.hibern8_notify = exynos_ufs_hibern8_notify,
.suspend = exynos_ufs_suspend,
.resume = exynos_ufs_resume,
+ .config_scsi_dev = exynos_ufs_config_scsi_dev,
};
static struct ufs_hba_variant_ops ufs_hba_exynosauto_vh_ops = {
@@ -1605,7 +1611,7 @@ static int exynos_ufs_probe(struct platform_device *pdev)
return err;
}
-static int exynos_ufs_remove(struct platform_device *pdev)
+static void exynos_ufs_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
struct exynos_ufs *ufs = ufshcd_get_variant(hba);
@@ -1615,8 +1621,6 @@ static int exynos_ufs_remove(struct platform_device *pdev)
phy_power_off(ufs->phy);
phy_exit(ufs->phy);
-
- return 0;
}
static struct exynos_ufs_uic_attr exynos7_uic_attr = {
@@ -1680,8 +1684,7 @@ static const struct exynos_ufs_drv_data exynos_ufs_drvs = {
UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR |
UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR |
UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL |
- UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING |
- UFSHCD_QUIRK_4KB_DMA_ALIGNMENT,
+ UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING,
.opts = EXYNOS_UFS_OPT_HAS_APB_CLK_CTRL |
EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL |
EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX |
@@ -1756,7 +1759,7 @@ static const struct dev_pm_ops exynos_ufs_pm_ops = {
static struct platform_driver exynos_ufs_pltform = {
.probe = exynos_ufs_probe,
- .remove = exynos_ufs_remove,
+ .remove_new = exynos_ufs_remove,
.driver = {
.name = "exynos-ufshc",
.pm = &exynos_ufs_pm_ops,
diff --git a/drivers/ufs/host/ufs-hisi.c b/drivers/ufs/host/ufs-hisi.c
index 5b3060cd0a..0229ac0a8d 100644
--- a/drivers/ufs/host/ufs-hisi.c
+++ b/drivers/ufs/host/ufs-hisi.c
@@ -575,12 +575,11 @@ static int ufs_hisi_probe(struct platform_device *pdev)
return ufshcd_pltfrm_init(pdev, of_id->data);
}
-static int ufs_hisi_remove(struct platform_device *pdev)
+static void ufs_hisi_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
ufshcd_remove(hba);
- return 0;
}
static const struct dev_pm_ops ufs_hisi_pm_ops = {
@@ -592,7 +591,7 @@ static const struct dev_pm_ops ufs_hisi_pm_ops = {
static struct platform_driver ufs_hisi_pltform = {
.probe = ufs_hisi_probe,
- .remove = ufs_hisi_remove,
+ .remove_new = ufs_hisi_remove,
.driver = {
.name = "ufshcd-hisi",
.pm = &ufs_hisi_pm_ops,
diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c
index 2383ecd88f..fc61790d28 100644
--- a/drivers/ufs/host/ufs-mediatek.c
+++ b/drivers/ufs/host/ufs-mediatek.c
@@ -806,7 +806,7 @@ static int ufs_mtk_vreg_fix_vcc(struct ufs_hba *hba)
return 0;
}
- err = ufshcd_populate_vreg(dev, vcc_name, &info->vcc);
+ err = ufshcd_populate_vreg(dev, vcc_name, &info->vcc, false);
if (err)
return err;
@@ -1748,13 +1748,12 @@ out:
*
* Always return 0
*/
-static int ufs_mtk_remove(struct platform_device *pdev)
+static void ufs_mtk_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
pm_runtime_get_sync(&(pdev)->dev);
ufshcd_remove(hba);
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -1818,7 +1817,7 @@ static const struct dev_pm_ops ufs_mtk_pm_ops = {
static struct platform_driver ufs_mtk_pltform = {
.probe = ufs_mtk_probe,
- .remove = ufs_mtk_remove,
+ .remove_new = ufs_mtk_remove,
.driver = {
.name = "ufshcd-mtk",
.pm = &ufs_mtk_pm_ops,
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index 0cbe14aca8..0f4b3f16d3 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -93,8 +93,7 @@ static const struct __ufs_qcom_bw_table {
static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS];
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 int ufs_qcom_set_core_clk_ctrl(struct ufs_hba *hba, bool is_scale_up);
static struct ufs_qcom_host *rcdev_to_ufs_host(struct reset_controller_dev *rcd)
{
@@ -460,7 +459,7 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
return ret;
}
- phy_set_mode_ext(phy, PHY_MODE_UFS_HS_B, host->hs_gear);
+ phy_set_mode_ext(phy, PHY_MODE_UFS_HS_B, host->phy_gear);
/* power on phy - start serdes and phy's power and clocks */
ret = phy_power_on(phy);
@@ -528,11 +527,20 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
return err;
}
-/*
+/**
+ * ufs_qcom_cfg_timers - Configure ufs qcom cfg timers
+ *
+ * @hba: host controller instance
+ * @gear: Current operating gear
+ * @hs: current power mode
+ * @rate: current operating rate (A or B)
+ * @update_link_startup_timer: indicate if link_start ongoing
+ * @is_pre_scale_up: flag to check if pre scale up condition.
* Return: zero for success and non-zero in case of a failure.
*/
static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
- u32 hs, u32 rate, bool update_link_startup_timer)
+ u32 hs, u32 rate, bool update_link_startup_timer,
+ bool is_pre_scale_up)
{
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
struct ufs_clk_info *clki;
@@ -563,11 +571,14 @@ static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
/*
* The Qunipro controller does not use following registers:
* SYS1CLK_1US_REG, TX_SYMBOL_CLK_1US_REG, CLK_NS_REG &
- * UFS_REG_PA_LINK_STARTUP_TIMER
- * But UTP controller uses SYS1CLK_1US_REG register for Interrupt
+ * UFS_REG_PA_LINK_STARTUP_TIMER.
+ * However UTP controller uses SYS1CLK_1US_REG register for Interrupt
* Aggregation logic.
- */
- if (ufs_qcom_cap_qunipro(host) && !ufshcd_is_intr_aggr_allowed(hba))
+ * It is mandatory to write SYS1CLK_1US_REG register on UFS host
+ * controller V4.0.0 onwards.
+ */
+ if (host->hw_ver.major < 4 && ufs_qcom_cap_qunipro(host) &&
+ !ufshcd_is_intr_aggr_allowed(hba))
return 0;
if (gear == 0) {
@@ -576,8 +587,14 @@ static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
}
list_for_each_entry(clki, &hba->clk_list_head, list) {
- if (!strcmp(clki->name, "core_clk"))
- core_clk_rate = clk_get_rate(clki->clk);
+ if (!strcmp(clki->name, "core_clk")) {
+ if (is_pre_scale_up)
+ core_clk_rate = clki->max_freq;
+ else
+ core_clk_rate = clk_get_rate(clki->clk);
+ break;
+ }
+
}
/* If frequency is smaller than 1MHz, set to 1MHz */
@@ -679,20 +696,17 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba,
switch (status) {
case PRE_CHANGE:
if (ufs_qcom_cfg_timers(hba, UFS_PWM_G1, SLOWAUTO_MODE,
- 0, true)) {
+ 0, true, false)) {
dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n",
__func__);
return -EINVAL;
}
- 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);
-
+ if (ufs_qcom_cap_qunipro(host)) {
+ err = ufs_qcom_set_core_clk_ctrl(hba, true);
+ if (err)
+ dev_err(hba->dev, "cfg core clk ctrl failed\n");
+ }
/*
* Some UFS devices (and may be host) have issues if LCC is
* enabled. So we are setting PA_Local_TX_LCC_Enable to 0
@@ -910,12 +924,12 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
}
/*
- * Update hs_gear only when the gears are scaled to a higher value. This is because,
- * the PHY gear settings are backwards compatible and we only need to change the PHY
- * settings while scaling to higher gears.
+ * Update phy_gear only when the gears are scaled to a higher value. This is
+ * because, the PHY gear settings are backwards compatible and we only need to
+ * change the PHY gear settings while scaling to higher gears.
*/
- if (dev_req_params->gear_tx > host->hs_gear)
- host->hs_gear = dev_req_params->gear_tx;
+ if (dev_req_params->gear_tx > host->phy_gear)
+ host->phy_gear = dev_req_params->gear_tx;
/* enable the device ref clock before changing to HS mode */
if (!ufshcd_is_hs_mode(&hba->pwr_info) &&
@@ -931,7 +945,7 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
case POST_CHANGE:
if (ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx,
dev_req_params->pwr_rx,
- dev_req_params->hs_rate, false)) {
+ dev_req_params->hs_rate, false, false)) {
dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n",
__func__);
/*
@@ -1282,7 +1296,7 @@ static int ufs_qcom_init(struct ufs_hba *hba)
* Power up the PHY using the minimum supported gear (UFS_HS_G2).
* Switching to max gear will be performed during reinit if supported.
*/
- host->hs_gear = UFS_HS_G2;
+ host->phy_gear = UFS_HS_G2;
return 0;
@@ -1301,14 +1315,96 @@ static void ufs_qcom_exit(struct ufs_hba *hba)
phy_exit(host->generic_phy);
}
-static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba,
- u32 clk_cycles)
+/**
+ * ufs_qcom_set_clk_40ns_cycles - Configure 40ns clk cycles
+ *
+ * @hba: host controller instance
+ * @cycles_in_1us: No of cycles in 1us to be configured
+ *
+ * Returns error if dme get/set configuration for 40ns fails
+ * and returns zero on success.
+ */
+static int ufs_qcom_set_clk_40ns_cycles(struct ufs_hba *hba,
+ u32 cycles_in_1us)
{
+ struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+ u32 cycles_in_40ns;
+ u32 reg;
int err;
- u32 core_clk_ctrl_reg;
- if (clk_cycles > DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK)
+ /*
+ * UFS host controller V4.0.0 onwards needs to program
+ * PA_VS_CORE_CLK_40NS_CYCLES attribute per programmed
+ * frequency of unipro core clk of UFS host controller.
+ */
+ if (host->hw_ver.major < 4)
+ return 0;
+
+ /*
+ * Generic formulae for cycles_in_40ns = (freq_unipro/25) is not
+ * applicable for all frequencies. For ex: ceil(37.5 MHz/25) will
+ * be 2 and ceil(403 MHZ/25) will be 17 whereas Hardware
+ * specification expect to be 16. Hence use exact hardware spec
+ * mandated value for cycles_in_40ns instead of calculating using
+ * generic formulae.
+ */
+ switch (cycles_in_1us) {
+ case UNIPRO_CORE_CLK_FREQ_403_MHZ:
+ cycles_in_40ns = 16;
+ break;
+ case UNIPRO_CORE_CLK_FREQ_300_MHZ:
+ cycles_in_40ns = 12;
+ break;
+ case UNIPRO_CORE_CLK_FREQ_201_5_MHZ:
+ cycles_in_40ns = 8;
+ break;
+ case UNIPRO_CORE_CLK_FREQ_150_MHZ:
+ cycles_in_40ns = 6;
+ break;
+ case UNIPRO_CORE_CLK_FREQ_100_MHZ:
+ cycles_in_40ns = 4;
+ break;
+ case UNIPRO_CORE_CLK_FREQ_75_MHZ:
+ cycles_in_40ns = 3;
+ break;
+ case UNIPRO_CORE_CLK_FREQ_37_5_MHZ:
+ cycles_in_40ns = 2;
+ break;
+ default:
+ dev_err(hba->dev, "UNIPRO clk freq %u MHz not supported\n",
+ cycles_in_1us);
return -EINVAL;
+ }
+
+ err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_VS_CORE_CLK_40NS_CYCLES), &reg);
+ if (err)
+ return err;
+
+ reg &= ~PA_VS_CORE_CLK_40NS_CYCLES_MASK;
+ reg |= cycles_in_40ns;
+
+ return ufshcd_dme_set(hba, UIC_ARG_MIB(PA_VS_CORE_CLK_40NS_CYCLES), reg);
+}
+
+static int ufs_qcom_set_core_clk_ctrl(struct ufs_hba *hba, bool is_scale_up)
+{
+ struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+ struct list_head *head = &hba->clk_list_head;
+ struct ufs_clk_info *clki;
+ u32 cycles_in_1us;
+ u32 core_clk_ctrl_reg;
+ int err;
+
+ list_for_each_entry(clki, head, list) {
+ if (!IS_ERR_OR_NULL(clki->clk) &&
+ !strcmp(clki->name, "core_clk_unipro")) {
+ if (is_scale_up)
+ cycles_in_1us = ceil(clki->max_freq, (1000 * 1000));
+ else
+ cycles_in_1us = ceil(clk_get_rate(clki->clk), (1000 * 1000));
+ break;
+ }
+ }
err = ufshcd_dme_get(hba,
UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL),
@@ -1316,32 +1412,54 @@ static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba,
if (err)
return err;
- core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK;
- core_clk_ctrl_reg |= clk_cycles;
+ /* Bit mask is different for UFS host controller V4.0.0 onwards */
+ if (host->hw_ver.major >= 4) {
+ if (!FIELD_FIT(CLK_1US_CYCLES_MASK_V4, cycles_in_1us))
+ return -ERANGE;
+ core_clk_ctrl_reg &= ~CLK_1US_CYCLES_MASK_V4;
+ core_clk_ctrl_reg |= FIELD_PREP(CLK_1US_CYCLES_MASK_V4, cycles_in_1us);
+ } else {
+ if (!FIELD_FIT(CLK_1US_CYCLES_MASK, cycles_in_1us))
+ return -ERANGE;
+ core_clk_ctrl_reg &= ~CLK_1US_CYCLES_MASK;
+ core_clk_ctrl_reg |= FIELD_PREP(CLK_1US_CYCLES_MASK, cycles_in_1us);
+ }
/* Clear CORE_CLK_DIV_EN */
core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT;
- return ufshcd_dme_set(hba,
+ err = ufshcd_dme_set(hba,
UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL),
core_clk_ctrl_reg);
-}
+ if (err)
+ return err;
-static int ufs_qcom_clk_scale_up_pre_change(struct ufs_hba *hba)
-{
- /* nothing to do as of now */
- return 0;
+ /* Configure unipro core clk 40ns attribute */
+ return ufs_qcom_set_clk_40ns_cycles(hba, cycles_in_1us);
}
-static int ufs_qcom_clk_scale_up_post_change(struct ufs_hba *hba)
+static int ufs_qcom_clk_scale_up_pre_change(struct ufs_hba *hba)
{
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+ struct ufs_pa_layer_attr *attr = &host->dev_req_params;
+ int ret;
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);
+ ret = ufs_qcom_cfg_timers(hba, attr->gear_rx, attr->pwr_rx,
+ attr->hs_rate, false, true);
+ if (ret) {
+ dev_err(hba->dev, "%s ufs cfg timer failed\n", __func__);
+ return ret;
+ }
+ /* set unipro core clock attributes and clear clock divider */
+ return ufs_qcom_set_core_clk_ctrl(hba, true);
+}
+
+static int ufs_qcom_clk_scale_up_post_change(struct ufs_hba *hba)
+{
+ return 0;
}
static int ufs_qcom_clk_scale_down_pre_change(struct ufs_hba *hba)
@@ -1376,15 +1494,14 @@ static int ufs_qcom_clk_scale_down_post_change(struct ufs_hba *hba)
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);
+ /* set unipro core clock attributes and clear clock divider */
+ return ufs_qcom_set_core_clk_ctrl(hba, false);
}
static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba,
bool scale_up, enum ufs_notify_change_status status)
{
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
- struct ufs_pa_layer_attr *dev_req_params = &host->dev_req_params;
int err = 0;
/* check the host controller state before sending hibern8 cmd */
@@ -1416,11 +1533,6 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba,
return err;
}
- ufs_qcom_cfg_timers(hba,
- dev_req_params->gear_rx,
- dev_req_params->pwr_rx,
- dev_req_params->hs_rate,
- false);
ufs_qcom_icc_update_bw(host);
ufshcd_uic_hibern8_exit(hba);
}
@@ -1917,14 +2029,13 @@ static int ufs_qcom_probe(struct platform_device *pdev)
*
* Always returns 0
*/
-static int ufs_qcom_remove(struct platform_device *pdev)
+static void ufs_qcom_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
pm_runtime_get_sync(&(pdev)->dev);
ufshcd_remove(hba);
platform_msi_domain_free_irqs(hba->dev);
- return 0;
}
static const struct of_device_id ufs_qcom_of_match[] __maybe_unused = {
@@ -1956,7 +2067,7 @@ static const struct dev_pm_ops ufs_qcom_pm_ops = {
static struct platform_driver ufs_qcom_pltform = {
.probe = ufs_qcom_probe,
- .remove = ufs_qcom_remove,
+ .remove_new = ufs_qcom_remove,
.driver = {
.name = "ufshcd-qcom",
.pm = &ufs_qcom_pm_ops,
diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h
index d6f8e74bd5..9950a00894 100644
--- a/drivers/ufs/host/ufs-qcom.h
+++ b/drivers/ufs/host/ufs-qcom.h
@@ -129,8 +129,21 @@ enum {
#define PA_VS_CONFIG_REG1 0x9000
#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
+#define CLK_1US_CYCLES_MASK_V4 GENMASK(27, 16)
+#define CLK_1US_CYCLES_MASK GENMASK(7, 0)
+#define DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT BIT(8)
+#define PA_VS_CORE_CLK_40NS_CYCLES 0x9007
+#define PA_VS_CORE_CLK_40NS_CYCLES_MASK GENMASK(6, 0)
+
+
+/* QCOM UFS host controller core clk frequencies */
+#define UNIPRO_CORE_CLK_FREQ_37_5_MHZ 38
+#define UNIPRO_CORE_CLK_FREQ_75_MHZ 75
+#define UNIPRO_CORE_CLK_FREQ_100_MHZ 100
+#define UNIPRO_CORE_CLK_FREQ_150_MHZ 150
+#define UNIPRO_CORE_CLK_FREQ_300_MHZ 300
+#define UNIPRO_CORE_CLK_FREQ_201_5_MHZ 202
+#define UNIPRO_CORE_CLK_FREQ_403_MHZ 403
static inline void
ufs_qcom_get_controller_revision(struct ufs_hba *hba,
@@ -227,7 +240,7 @@ struct ufs_qcom_host {
struct gpio_desc *device_reset;
- u32 hs_gear;
+ u32 phy_gear;
bool esi_enabled;
};
@@ -244,6 +257,7 @@ ufs_qcom_get_debug_reg_offset(struct ufs_qcom_host *host, u32 reg)
#define ufs_qcom_is_link_off(hba) ufshcd_is_link_off(hba)
#define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba)
#define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba)
+#define ceil(freq, div) ((freq) % (div) == 0 ? ((freq)/(div)) : ((freq)/(div) + 1))
int ufs_qcom_testbus_config(struct ufs_qcom_host *host);
diff --git a/drivers/ufs/host/ufs-renesas.c b/drivers/ufs/host/ufs-renesas.c
index cc94970b86..8711e5cbc9 100644
--- a/drivers/ufs/host/ufs-renesas.c
+++ b/drivers/ufs/host/ufs-renesas.c
@@ -388,18 +388,16 @@ static int ufs_renesas_probe(struct platform_device *pdev)
return ufshcd_pltfrm_init(pdev, &ufs_renesas_vops);
}
-static int ufs_renesas_remove(struct platform_device *pdev)
+static void ufs_renesas_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
ufshcd_remove(hba);
-
- return 0;
}
static struct platform_driver ufs_renesas_platform = {
.probe = ufs_renesas_probe,
- .remove = ufs_renesas_remove,
+ .remove_new = ufs_renesas_remove,
.driver = {
.name = "ufshcd-renesas",
.of_match_table = of_match_ptr(ufs_renesas_of_match),
diff --git a/drivers/ufs/host/ufs-sprd.c b/drivers/ufs/host/ufs-sprd.c
index 2bad75dd6d..d8b1659088 100644
--- a/drivers/ufs/host/ufs-sprd.c
+++ b/drivers/ufs/host/ufs-sprd.c
@@ -425,13 +425,12 @@ static int ufs_sprd_probe(struct platform_device *pdev)
return err;
}
-static int ufs_sprd_remove(struct platform_device *pdev)
+static void ufs_sprd_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
pm_runtime_get_sync(&(pdev)->dev);
ufshcd_remove(hba);
- return 0;
}
static const struct dev_pm_ops ufs_sprd_pm_ops = {
@@ -443,7 +442,7 @@ static const struct dev_pm_ops ufs_sprd_pm_ops = {
static struct platform_driver ufs_sprd_pltform = {
.probe = ufs_sprd_probe,
- .remove = ufs_sprd_remove,
+ .remove_new = ufs_sprd_remove,
.driver = {
.name = "ufshcd-sprd",
.pm = &ufs_sprd_pm_ops,
diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c
index 248a49e5e7..0aca666d21 100644
--- a/drivers/ufs/host/ufshcd-pci.c
+++ b/drivers/ufs/host/ufshcd-pci.c
@@ -58,11 +58,12 @@ static int __intel_dsm(struct intel_host *intel_host, struct device *dev,
int err = 0;
size_t len;
- obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &intel_dsm_guid, 0, fn, NULL);
+ obj = acpi_evaluate_dsm_typed(ACPI_HANDLE(dev), &intel_dsm_guid, 0, fn, NULL,
+ ACPI_TYPE_BUFFER);
if (!obj)
return -EOPNOTSUPP;
- if (obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 1) {
+ if (obj->buffer.length < 1) {
err = -EINVAL;
goto out;
}
diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
index 797a4dfe45..db9d9365ff 100644
--- a/drivers/ufs/host/ufshcd-pltfrm.c
+++ b/drivers/ufs/host/ufshcd-pltfrm.c
@@ -8,8 +8,10 @@
* Vinayak Holikatti <h.vinayak@samsung.com>
*/
+#include <linux/clk.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
@@ -121,7 +123,7 @@ static bool phandle_exists(const struct device_node *np,
#define MAX_PROP_SIZE 32
int ufshcd_populate_vreg(struct device *dev, const char *name,
- struct ufs_vreg **out_vreg)
+ struct ufs_vreg **out_vreg, bool skip_current)
{
char prop_name[MAX_PROP_SIZE];
struct ufs_vreg *vreg = NULL;
@@ -147,6 +149,11 @@ int ufshcd_populate_vreg(struct device *dev, const char *name,
if (!vreg->name)
return -ENOMEM;
+ if (skip_current) {
+ vreg->max_uA = 0;
+ goto out;
+ }
+
snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name);
if (of_property_read_u32(np, prop_name, &vreg->max_uA)) {
dev_info(dev, "%s: unable to find %s\n", __func__, prop_name);
@@ -175,19 +182,19 @@ static int ufshcd_parse_regulator_info(struct ufs_hba *hba)
struct device *dev = hba->dev;
struct ufs_vreg_info *info = &hba->vreg_info;
- err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba);
+ err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba, true);
if (err)
goto out;
- err = ufshcd_populate_vreg(dev, "vcc", &info->vcc);
+ err = ufshcd_populate_vreg(dev, "vcc", &info->vcc, false);
if (err)
goto out;
- err = ufshcd_populate_vreg(dev, "vccq", &info->vccq);
+ err = ufshcd_populate_vreg(dev, "vccq", &info->vccq, false);
if (err)
goto out;
- err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2);
+ err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2, false);
out:
return err;
}
@@ -208,6 +215,130 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
}
/**
+ * ufshcd_parse_clock_min_max_freq - Parse MIN and MAX clocks freq
+ * @hba: per adapter instance
+ *
+ * This function parses MIN and MAX frequencies of all clocks required
+ * by the host drivers.
+ *
+ * Returns 0 for success and non-zero for failure
+ */
+static int ufshcd_parse_clock_min_max_freq(struct ufs_hba *hba)
+{
+ struct list_head *head = &hba->clk_list_head;
+ struct ufs_clk_info *clki;
+ struct dev_pm_opp *opp;
+ unsigned long freq;
+ u8 idx = 0;
+
+ list_for_each_entry(clki, head, list) {
+ if (!clki->name)
+ continue;
+
+ clki->clk = devm_clk_get(hba->dev, clki->name);
+ if (IS_ERR(clki->clk))
+ continue;
+
+ /* Find Max Freq */
+ freq = ULONG_MAX;
+ opp = dev_pm_opp_find_freq_floor_indexed(hba->dev, &freq, idx);
+ if (IS_ERR(opp)) {
+ dev_err(hba->dev, "Failed to find OPP for MAX frequency\n");
+ return PTR_ERR(opp);
+ }
+ clki->max_freq = dev_pm_opp_get_freq_indexed(opp, idx);
+ dev_pm_opp_put(opp);
+
+ /* Find Min Freq */
+ freq = 0;
+ opp = dev_pm_opp_find_freq_ceil_indexed(hba->dev, &freq, idx);
+ if (IS_ERR(opp)) {
+ dev_err(hba->dev, "Failed to find OPP for MIN frequency\n");
+ return PTR_ERR(opp);
+ }
+ clki->min_freq = dev_pm_opp_get_freq_indexed(opp, idx++);
+ dev_pm_opp_put(opp);
+ }
+
+ return 0;
+}
+
+static int ufshcd_parse_operating_points(struct ufs_hba *hba)
+{
+ struct device *dev = hba->dev;
+ struct device_node *np = dev->of_node;
+ struct dev_pm_opp_config config = {};
+ struct ufs_clk_info *clki;
+ const char **clk_names;
+ int cnt, i, ret;
+
+ if (!of_find_property(np, "operating-points-v2", NULL))
+ return 0;
+
+ if (of_find_property(np, "freq-table-hz", NULL)) {
+ dev_err(dev, "%s: operating-points and freq-table-hz are incompatible\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ cnt = of_property_count_strings(np, "clock-names");
+ if (cnt <= 0) {
+ dev_err(dev, "%s: Missing clock-names\n", __func__);
+ return -ENODEV;
+ }
+
+ /* OPP expects clk_names to be NULL terminated */
+ clk_names = devm_kcalloc(dev, cnt + 1, sizeof(*clk_names), GFP_KERNEL);
+ if (!clk_names)
+ return -ENOMEM;
+
+ /*
+ * We still need to get reference to all clocks as the UFS core uses
+ * them separately.
+ */
+ for (i = 0; i < cnt; i++) {
+ ret = of_property_read_string_index(np, "clock-names", i,
+ &clk_names[i]);
+ if (ret)
+ return ret;
+
+ clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
+ if (!clki)
+ return -ENOMEM;
+
+ clki->name = devm_kstrdup(dev, clk_names[i], GFP_KERNEL);
+ if (!clki->name)
+ return -ENOMEM;
+
+ if (!strcmp(clk_names[i], "ref_clk"))
+ clki->keep_link_active = true;
+
+ list_add_tail(&clki->list, &hba->clk_list_head);
+ }
+
+ config.clk_names = clk_names,
+ config.config_clks = ufshcd_opp_config_clks;
+
+ ret = devm_pm_opp_set_config(dev, &config);
+ if (ret)
+ return ret;
+
+ ret = devm_pm_opp_of_add_table(dev);
+ if (ret) {
+ dev_err(dev, "Failed to add OPP table: %d\n", ret);
+ return ret;
+ }
+
+ ret = ufshcd_parse_clock_min_max_freq(hba);
+ if (ret)
+ return ret;
+
+ hba->use_pm_opp = true;
+
+ return 0;
+}
+
+/**
* ufshcd_get_pwr_dev_param - get finally agreed attributes for
* power mode change
* @pltfrm_param: pointer to platform parameters
@@ -373,6 +504,12 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
ufshcd_init_lanes_per_dir(hba);
+ err = ufshcd_parse_operating_points(hba);
+ if (err) {
+ dev_err(dev, "%s: OPP parse failed %d\n", __func__, err);
+ goto dealloc_host;
+ }
+
err = ufshcd_init(hba, mmio_base, irq);
if (err) {
dev_err_probe(dev, err, "Initialization failed with error %d\n",
diff --git a/drivers/ufs/host/ufshcd-pltfrm.h b/drivers/ufs/host/ufshcd-pltfrm.h
index 2df108f4ac..a86a3ada4b 100644
--- a/drivers/ufs/host/ufshcd-pltfrm.h
+++ b/drivers/ufs/host/ufshcd-pltfrm.h
@@ -32,6 +32,6 @@ void ufshcd_init_pwr_dev_param(struct ufs_dev_params *dev_param);
int ufshcd_pltfrm_init(struct platform_device *pdev,
const struct ufs_hba_variant_ops *vops);
int ufshcd_populate_vreg(struct device *dev, const char *name,
- struct ufs_vreg **out_vreg);
+ struct ufs_vreg **out_vreg, bool skip_current);
#endif /* UFSHCD_PLTFRM_H_ */