From 3ad271f0fa994e432a07bacdfe935f7dcbd232c9 Mon Sep 17 00:00:00 2001 From: Can Guo Date: Tue, 20 Aug 2019 01:06:36 -0700 Subject: [PATCH 1/2] phy: qcom-ufs: move PHY configurations after vregs and clocks are enabled PHY congifuration actions should come only after regulators and clocks are enabled. Change-Id: Ib1c10d0b1d25d41b59d724f0a2d301ac6cd7f219 Signed-off-by: Can Guo --- drivers/phy/qualcomm/phy-qcom-ufs.c | 60 +++++++++++++++-------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-ufs.c b/drivers/phy/qualcomm/phy-qcom-ufs.c index fbf9a2d81b7c..ef6d7d6071ac 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs.c @@ -601,35 +601,6 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy) bool is_rate_B = false; int err; - err = ufs_qcom_phy_get_reset(phy_common); - if (err) - return err; - - err = reset_control_assert(phy_common->ufs_reset); - if (err) - return err; - - if (phy_common->mode == PHY_MODE_UFS_HS_B) - is_rate_B = true; - - err = phy_common->phy_spec_ops->calibrate(phy_common, is_rate_B); - if (err) - return err; - - err = reset_control_deassert(phy_common->ufs_reset); - if (err) { - dev_err(dev, "Failed to assert UFS PHY reset"); - return err; - } - - err = ufs_qcom_phy_start_serdes(phy_common); - if (err) - return err; - - err = ufs_qcom_phy_is_pcs_ready(phy_common); - if (err) - return err; - err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy); if (err) { dev_err(dev, "%s enable vdda_phy failed, err=%d\n", @@ -672,6 +643,37 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy) } } + err = ufs_qcom_phy_get_reset(phy_common); + if (err) + goto out_disable_ref_clk; + + err = reset_control_assert(phy_common->ufs_reset); + if (err) { + dev_err(dev, "Failed to assert UFS PHY reset\n"); + goto out_disable_ref_clk; + } + + if (phy_common->mode == PHY_MODE_UFS_HS_B) + is_rate_B = true; + + err = phy_common->phy_spec_ops->calibrate(phy_common, is_rate_B); + if (err) + goto out_disable_ref_clk; + + err = reset_control_deassert(phy_common->ufs_reset); + if (err) { + dev_err(dev, "Failed to deassert UFS PHY reset\n"); + goto out_disable_ref_clk; + } + + err = ufs_qcom_phy_start_serdes(phy_common); + if (err) + goto out_disable_ref_clk; + + err = ufs_qcom_phy_is_pcs_ready(phy_common); + if (err) + goto out_disable_ref_clk; + goto out; out_disable_ref_clk: From 47bdb5f1ce578ae34f78db56d77c11459ebffcb2 Mon Sep 17 00:00:00 2001 From: Can Guo Date: Tue, 20 Aug 2019 01:08:34 -0700 Subject: [PATCH 2/2] phy: qcom-ufs: add UFS HS-G4 support New generation (3.0) UFS devices are capable of running at HS-G4 (High Speed Gear 4) mode which is 2x faster than previous generation UFS devices. However, if the connected UFS device is only capable of HS-G3 at maximum, putting the UFS PHY to its HS-G4 settings would cause power penalty. This change adds a boolean variable to the PHY calibration API to allow the callers tell the calibration API whether HS-G3 or HS-G4 settings should be configured. The value of this boolean variable is decided by the UFS PHY's submode. Change-Id: Id2e5fc2285e71ae07ab3b9a19c2aa3937ad465c8 Signed-off-by: Can Guo --- drivers/phy/qualcomm/phy-qcom-ufs-i.h | 6 ++++-- drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c | 6 ++++-- drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c | 6 ++++-- drivers/phy/qualcomm/phy-qcom-ufs-qrbtc-sdm845.c | 4 ++-- drivers/phy/qualcomm/phy-qcom-ufs.c | 6 +++++- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h index 84e3aa525a4a..99447cbf8c97 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-i.h +++ b/drivers/phy/qualcomm/phy-qcom-ufs-i.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2015, 2019, Linux Foundation. All rights reserved. */ #ifndef UFS_QCOM_PHY_I_H_ @@ -97,6 +97,7 @@ struct ufs_qcom_phy { struct ufs_qcom_phy_specific_ops *phy_spec_ops; enum phy_mode mode; + int submode; struct reset_control *ufs_reset; }; @@ -114,7 +115,8 @@ struct ufs_qcom_phy { * Rx line config */ struct ufs_qcom_phy_specific_ops { - int (*calibrate)(struct ufs_qcom_phy *ufs_qcom_phy, bool is_rate_B); + int (*calibrate)(struct ufs_qcom_phy *ufs_qcom_phy, bool is_rate_B, + bool is_g4); void (*start_serdes)(struct ufs_qcom_phy *phy); int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy); void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c index 54b355bfc24c..7f5b03cfdaff 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2015, 2019, Linux Foundation. All rights reserved. */ #include "phy-qcom-ufs-qmp-14nm.h" @@ -10,7 +10,7 @@ static int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B) + bool is_rate_B, bool is_g4) { int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); @@ -44,6 +44,8 @@ int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy, if (mode > 0) phy_common->mode = mode; + phy_common->submode = submode; + return 0; } diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c index 3e9d8b71e995..5113a6e70fbd 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2015, 2019, Linux Foundation. All rights reserved. */ #include "phy-qcom-ufs-qmp-20nm.h" @@ -9,7 +9,7 @@ static int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B) + bool is_rate_B, bool is_g4) { struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; int tbl_size_A, tbl_size_B; @@ -63,6 +63,8 @@ int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy, if (mode > 0) phy_common->mode = mode; + phy_common->submode = submode; + return 0; } diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qrbtc-sdm845.c b/drivers/phy/qualcomm/phy-qcom-ufs-qrbtc-sdm845.c index f6c211426489..8b53a81ec7db 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qrbtc-sdm845.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qrbtc-sdm845.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016, Linux Foundation. All rights reserved. + * Copyright (c) 2016, 2019, Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -19,7 +19,7 @@ static int ufs_qcom_phy_qrbtc_sdm845_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B) + bool is_rate_B, bool is_g4) { int err; int tbl_size_A, tbl_size_B; diff --git a/drivers/phy/qualcomm/phy-qcom-ufs.c b/drivers/phy/qualcomm/phy-qcom-ufs.c index ef6d7d6071ac..a692465d8f68 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs.c @@ -599,6 +599,7 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy) struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); struct device *dev = phy_common->dev; bool is_rate_B = false; + bool is_gear4 = false; int err; err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy); @@ -656,7 +657,10 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy) if (phy_common->mode == PHY_MODE_UFS_HS_B) is_rate_B = true; - err = phy_common->phy_spec_ops->calibrate(phy_common, is_rate_B); + is_gear4 = !!phy_common->submode; + + err = phy_common->phy_spec_ops->calibrate(phy_common, is_rate_B, + is_gear4); if (err) goto out_disable_ref_clk;