diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h index ec4b6674684c..6238f7f25fb6 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, 2019, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2015, 2019-2020, Linux Foundation. All rights reserved. */ #ifndef UFS_QCOM_PHY_I_H_ @@ -137,6 +137,7 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, struct ufs_qcom_phy *common_cfg, const struct phy_ops *ufs_qcom_phy_gen_ops, struct ufs_qcom_phy_specific_ops *phy_spec_ops); +int ufs_qcom_phy_get_reset(struct ufs_qcom_phy *phy_common); int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A, struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B, diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c index 7f5b03cfdaff..324736a1d032 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, 2019, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2015, 2019-2020, Linux Foundation. All rights reserved. */ #include "phy-qcom-ufs-qmp-14nm.h" @@ -9,13 +9,16 @@ #define UFS_PHY_VDDA_PHY_UV (925000) static -int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B, bool is_g4) +int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct phy *generic_phy) { + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); + bool is_rate_B; int err; + is_rate_B = (ufs_qcom_phy->mode == PHY_MODE_UFS_HS_B) ? true : false; + err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A, tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B); @@ -98,11 +101,11 @@ static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { .power_on = ufs_qcom_phy_power_on, .power_off = ufs_qcom_phy_power_off, .set_mode = ufs_qcom_phy_qmp_14nm_set_mode, + .calibrate = ufs_qcom_phy_qmp_14nm_phy_calibrate, .owner = THIS_MODULE, }; static struct ufs_qcom_phy_specific_ops phy_14nm_ops = { - .calibrate = ufs_qcom_phy_qmp_14nm_phy_calibrate, .start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes, .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready, .set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable, diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c index 5113a6e70fbd..5fa23de873ff 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, 2019, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2015, 2019-2020, Linux Foundation. All rights reserved. */ #include "phy-qcom-ufs-qmp-20nm.h" @@ -8,11 +8,12 @@ #define UFS_PHY_NAME "ufs_phy_qmp_20nm" static -int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B, bool is_g4) +int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct phy *generic_phy) { + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; int tbl_size_A, tbl_size_B; + bool is_rate_B; u8 major = ufs_qcom_phy->host_ctrl_rev_major; u16 minor = ufs_qcom_phy->host_ctrl_rev_minor; u16 step = ufs_qcom_phy->host_ctrl_rev_step; @@ -33,6 +34,7 @@ int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); tbl_B = phy_cal_table_rate_B; + is_rate_B = (ufs_qcom_phy->mode == PHY_MODE_UFS_HS_B) ? true : false; err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A, tbl_B, tbl_size_B, is_rate_B); @@ -156,11 +158,11 @@ static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { .power_on = ufs_qcom_phy_power_on, .power_off = ufs_qcom_phy_power_off, .set_mode = ufs_qcom_phy_qmp_20nm_set_mode, + .calibrate = ufs_qcom_phy_qmp_20nm_phy_calibrate, .owner = THIS_MODULE, }; static struct ufs_qcom_phy_specific_ops phy_20nm_ops = { - .calibrate = ufs_qcom_phy_qmp_20nm_phy_calibrate, .start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes, .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready, .set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable, diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4-lahaina.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4-lahaina.c index 907cfd99fb2d..adb5ab39127a 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4-lahaina.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4-lahaina.c @@ -1,16 +1,32 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2019, Linux Foundation. All rights reserved. + * Copyright (c) 2019-2020, Linux Foundation. All rights reserved. */ #include "phy-qcom-ufs-qmp-v4-lahaina.h" #define UFS_PHY_NAME "ufs_phy_qmp_v4_lahaina" -static -int ufs_qcom_phy_qmp_v4_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B, bool is_g4) +static inline void ufs_qcom_phy_qmp_v4_start_serdes(struct ufs_qcom_phy *phy); +static int ufs_qcom_phy_qmp_v4_is_pcs_ready(struct ufs_qcom_phy *phy_common); + +static int ufs_qcom_phy_qmp_v4_phy_calibrate(struct phy *generic_phy) { + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); + struct device *dev = ufs_qcom_phy->dev; + bool is_g4, is_rate_B; + int err; + + err = reset_control_assert(ufs_qcom_phy->ufs_reset); + if (err) { + dev_err(dev, "Failed to assert UFS PHY reset %d\n", err); + goto out; + } + + /* For UFS PHY's submode, 1 = G4, 0 = non-G4 */ + is_g4 = !!ufs_qcom_phy->submode; + is_rate_B = (ufs_qcom_phy->mode == PHY_MODE_UFS_HS_B) ? true : false; + writel_relaxed(0x01, ufs_qcom_phy->mmio + UFS_PHY_SW_RESET); /* Ensure PHY is in reset before writing PHY calibration data */ wmb(); @@ -43,7 +59,18 @@ int ufs_qcom_phy_qmp_v4_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, /* flush buffered writes */ wmb(); - return 0; + err = reset_control_deassert(ufs_qcom_phy->ufs_reset); + if (err) { + dev_err(dev, "Failed to deassert UFS PHY reset %d\n", err); + goto out; + } + + ufs_qcom_phy_qmp_v4_start_serdes(ufs_qcom_phy); + + err = ufs_qcom_phy_qmp_v4_is_pcs_ready(ufs_qcom_phy); + +out: + return err; } static int ufs_qcom_phy_qmp_v4_init(struct phy *generic_phy) @@ -66,6 +93,9 @@ static int ufs_qcom_phy_qmp_v4_init(struct phy *generic_phy) goto out; } + /* Optional */ + ufs_qcom_phy_get_reset(phy_common); + out: return err; } @@ -200,6 +230,7 @@ out: return err; } + static void ufs_qcom_phy_qmp_v4_dbg_register_dump(struct ufs_qcom_phy *phy) { ufs_qcom_phy_dump_regs(phy, COM_BASE, COM_SIZE, @@ -222,11 +253,11 @@ static const struct phy_ops ufs_qcom_phy_qmp_v4_phy_ops = { .power_on = ufs_qcom_phy_power_on, .power_off = ufs_qcom_phy_power_off, .set_mode = ufs_qcom_phy_qmp_v4_set_mode, + .calibrate = ufs_qcom_phy_qmp_v4_phy_calibrate, .owner = THIS_MODULE, }; static struct ufs_qcom_phy_specific_ops phy_v4_ops = { - .calibrate = ufs_qcom_phy_qmp_v4_phy_calibrate, .start_serdes = ufs_qcom_phy_qmp_v4_start_serdes, .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_v4_is_pcs_ready, .set_tx_lane_enable = ufs_qcom_phy_qmp_v4_set_tx_lane_enable, diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qrbtc-sdm845.c b/drivers/phy/qualcomm/phy-qcom-ufs-qrbtc-sdm845.c index 8b53a81ec7db..0d493442c77f 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, 2019, Linux Foundation. All rights reserved. + * Copyright (c) 2016, 2019-2020, 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 @@ -18,12 +18,13 @@ #define UFS_PHY_NAME "ufs_phy_qrbtc_sdm845" static -int ufs_qcom_phy_qrbtc_sdm845_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B, bool is_g4) +int ufs_qcom_phy_qrbtc_sdm845_phy_calibrate(struct phy *generic_phy) { int err; int tbl_size_A, tbl_size_B; struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); + bool is_rate_B; tbl_A = phy_cal_table_rate_A; tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); @@ -31,6 +32,8 @@ int ufs_qcom_phy_qrbtc_sdm845_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); tbl_B = phy_cal_table_rate_B; + is_rate_B = (ufs_qcom_phy->mode == PHY_MODE_UFS_HS_B) ? true : false; + err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A, tbl_B, tbl_size_B, @@ -85,6 +88,13 @@ static void ufs_qcom_phy_qrbtc_sdm845_start_serdes(struct ufs_qcom_phy *phy) static int ufs_qcom_phy_qrbtc_sdm845_init(struct phy *generic_phy) { + struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); + int ret; + + ret = ufs_qcom_phy_get_reset(phy_common); + if (ret) + dev_err(phy_common->dev, "Failed to get reset control\n", ret); + return 0; } @@ -96,11 +106,11 @@ static int ufs_qcom_phy_qrbtc_sdm845_exit(struct phy *generic_phy) static struct phy_ops ufs_qcom_phy_qrbtc_sdm845_phy_ops = { .init = ufs_qcom_phy_qrbtc_sdm845_init, .exit = ufs_qcom_phy_qrbtc_sdm845_exit, + .calibrate = ufs_qcom_phy_qrbtc_sdm845_phy_calibrate, .owner = THIS_MODULE, }; static struct ufs_qcom_phy_specific_ops phy_qrbtc_sdm845_ops = { - .calibrate = ufs_qcom_phy_qrbtc_sdm845_phy_calibrate, .start_serdes = ufs_qcom_phy_qrbtc_sdm845_start_serdes, .is_physical_coding_sublayer_ready = ufs_qcom_phy_qrbtc_sdm845_is_pcs_ready, diff --git a/drivers/phy/qualcomm/phy-qcom-ufs.c b/drivers/phy/qualcomm/phy-qcom-ufs.c index 508f6baf2f92..db6b19857f82 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2013-2019, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2020, Linux Foundation. All rights reserved. */ #include "phy-qcom-ufs-i.h" @@ -15,6 +15,9 @@ #define UFS_PHY_DEFAULT_LANES_PER_DIRECTION 1 +static int ufs_qcom_phy_start_serdes(struct ufs_qcom_phy *ufs_qcom_phy); +static int ufs_qcom_phy_is_pcs_ready(struct ufs_qcom_phy *ufs_qcom_phy); + void ufs_qcom_phy_write_tbl(struct ufs_qcom_phy *ufs_qcom_phy, struct ufs_qcom_phy_calibration *tbl, int tbl_size) @@ -33,10 +36,17 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B, bool is_rate_B) { + struct device *dev = ufs_qcom_phy->dev; int ret = 0; + ret = reset_control_assert(ufs_qcom_phy->ufs_reset); + if (ret) { + dev_err(dev, "Failed to assert UFS PHY reset %d\n", ret); + goto out; + } + if (!tbl_A) { - dev_err(ufs_qcom_phy->dev, "%s: tbl_A is NULL", __func__); + dev_err(dev, "%s: tbl_A is NULL\n", __func__); ret = EINVAL; goto out; } @@ -51,7 +61,7 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, */ if (is_rate_B) { if (!tbl_B) { - dev_err(ufs_qcom_phy->dev, "%s: tbl_B is NULL", + dev_err(dev, "%s: tbl_B is NULL\n", __func__); ret = EINVAL; goto out; @@ -63,6 +73,16 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, /* flush buffered writes */ mb(); + ret = reset_control_deassert(ufs_qcom_phy->ufs_reset); + if (ret) + dev_err(dev, "Failed to deassert UFS PHY reset %d\n", ret); + + ret = ufs_qcom_phy_start_serdes(ufs_qcom_phy); + if (ret) + goto out; + + ret = ufs_qcom_phy_is_pcs_ready(ufs_qcom_phy); + out: return ret; } @@ -153,7 +173,7 @@ out: } EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe); -static int ufs_qcom_phy_get_reset(struct ufs_qcom_phy *phy_common) +int ufs_qcom_phy_get_reset(struct ufs_qcom_phy *phy_common) { struct reset_control *reset; @@ -167,6 +187,7 @@ static int ufs_qcom_phy_get_reset(struct ufs_qcom_phy *phy_common) phy_common->ufs_reset = reset; return 0; } +EXPORT_SYMBOL(ufs_qcom_phy_get_reset); static int __ufs_qcom_phy_clk_get(struct device *dev, const char *name, struct clk **clk_out, bool err_print) @@ -605,8 +626,6 @@ 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); @@ -651,40 +670,6 @@ 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; - - 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; - - 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: @@ -714,7 +699,6 @@ int ufs_qcom_phy_power_off(struct phy *generic_phy) ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_pll); ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_phy); - reset_control_assert(phy_common->ufs_reset); return 0; } EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off);