lkml.org 
[lkml]   [2019]   [Jan]   [23]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v2 9/9] phy: qcom-ufs: Refactor all init steps into phy_poweron
    Date
    The phy code was using implicit sequencing between the PHY driver
    and the UFS driver to implement certain hardware requirements.
    Specifically, the PHY reset register in the UFS controller needs
    to be deasserted before serdes start occurs in the PHY.

    Before this change, the code was doing this by utilizing the two
    phy callbacks, phy_init and phy_poweron, as "init step 1" and
    "init step 2", where the UFS driver would deassert reset between
    these two steps.

    This makes it challenging to power off the regulators in suspend,
    as regulators are initialized in init, not in poweron, but only
    poweroff is called during suspend, not exit.

    Consolidate the initialization code into phy_poweron, and utilize
    the reset controller exported from the UFS driver to explicitly
    perform all the steps needed to initialize the PHY.

    Signed-off-by: Evan Green <evgreen@chromium.org>
    ---

    Changes in v2:
    - Use devm_ to get the reset (Stephen)

    drivers/phy/qualcomm/phy-qcom-ufs-i.h | 5 +-
    drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c | 25 +--------
    drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c | 25 +--------
    drivers/phy/qualcomm/phy-qcom-ufs.c | 57 +++++++++++++++-----
    4 files changed, 49 insertions(+), 63 deletions(-)

    diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h
    index f798fb64de94e..109ddd67be829 100644
    --- a/drivers/phy/qualcomm/phy-qcom-ufs-i.h
    +++ b/drivers/phy/qualcomm/phy-qcom-ufs-i.h
    @@ -19,6 +19,7 @@
    #include <linux/clk.h>
    #include <linux/phy/phy.h>
    #include <linux/regulator/consumer.h>
    +#include <linux/reset.h>
    #include <linux/slab.h>
    #include <linux/platform_device.h>
    #include <linux/io.h>
    @@ -96,11 +97,10 @@ struct ufs_qcom_phy {
    char name[UFS_QCOM_PHY_NAME_LEN];
    struct ufs_qcom_phy_calibration *cached_regs;
    int cached_regs_table_size;
    - bool is_powered_on;
    - bool is_started;
    struct ufs_qcom_phy_specific_ops *phy_spec_ops;

    enum phy_mode mode;
    + struct reset_control *ufs_reset;
    };

    /**
    @@ -115,6 +115,7 @@ struct ufs_qcom_phy {
    * and writes to QSERDES_RX_SIGDET_CNTRL attribute
    */
    struct ufs_qcom_phy_specific_ops {
    + int (*calibrate)(struct ufs_qcom_phy *ufs_qcom_phy, bool is_rate_B);
    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 1e0d4f2046a45..4815546f228cd 100644
    --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c
    +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c
    @@ -42,28 +42,6 @@ void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
    UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
    }

    -static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy)
    -{
    - struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
    - bool is_rate_B = false;
    - int ret;
    -
    - if (phy_common->mode == PHY_MODE_UFS_HS_B)
    - is_rate_B = true;
    -
    - ret = ufs_qcom_phy_qmp_14nm_phy_calibrate(phy_common, is_rate_B);
    - if (!ret)
    - /* phy calibrated, but yet to be started */
    - phy_common->is_started = false;
    -
    - return ret;
    -}
    -
    -static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy)
    -{
    - return 0;
    -}
    -
    static
    int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy,
    enum phy_mode mode, int submode)
    @@ -124,8 +102,6 @@ static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
    }

    static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = {
    - .init = ufs_qcom_phy_qmp_14nm_init,
    - .exit = ufs_qcom_phy_qmp_14nm_exit,
    .power_on = ufs_qcom_phy_power_on,
    .power_off = ufs_qcom_phy_power_off,
    .set_mode = ufs_qcom_phy_qmp_14nm_set_mode,
    @@ -133,6 +109,7 @@ static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = {
    };

    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 aef40f7a41d49..f1cf819e12eae 100644
    --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c
    +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c
    @@ -61,28 +61,6 @@ void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
    UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
    }

    -static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy)
    -{
    - struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
    - bool is_rate_B = false;
    - int ret;
    -
    - if (phy_common->mode == PHY_MODE_UFS_HS_B)
    - is_rate_B = true;
    -
    - ret = ufs_qcom_phy_qmp_20nm_phy_calibrate(phy_common, is_rate_B);
    - if (!ret)
    - /* phy calibrated, but yet to be started */
    - phy_common->is_started = false;
    -
    - return ret;
    -}
    -
    -static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy)
    -{
    - return 0;
    -}
    -
    static
    int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy,
    enum phy_mode mode, int submode)
    @@ -182,8 +160,6 @@ static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
    }

    static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
    - .init = ufs_qcom_phy_qmp_20nm_init,
    - .exit = ufs_qcom_phy_qmp_20nm_exit,
    .power_on = ufs_qcom_phy_power_on,
    .power_off = ufs_qcom_phy_power_off,
    .set_mode = ufs_qcom_phy_qmp_20nm_set_mode,
    @@ -191,6 +167,7 @@ static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
    };

    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.c b/drivers/phy/qualcomm/phy-qcom-ufs.c
    index f2979ccad00a3..8860ced486f35 100644
    --- a/drivers/phy/qualcomm/phy-qcom-ufs.c
    +++ b/drivers/phy/qualcomm/phy-qcom-ufs.c
    @@ -147,6 +147,21 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
    }
    EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe);

    +static int ufs_qcom_phy_get_reset(struct ufs_qcom_phy *phy_common)
    +{
    + struct reset_control *reset;
    +
    + if (phy_common->ufs_reset)
    + return 0;
    +
    + reset = devm_reset_control_get_exclusive_by_index(phy_common->dev, 0);
    + if (IS_ERR(reset))
    + return PTR_ERR(reset);
    +
    + phy_common->ufs_reset = reset;
    + return 0;
    +}
    +
    static int __ufs_qcom_phy_clk_get(struct device *dev,
    const char *name, struct clk **clk_out, bool err_print)
    {
    @@ -528,23 +543,42 @@ 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;
    int err;

    - if (phy_common->is_powered_on)
    - return 0;
    + err = ufs_qcom_phy_get_reset(phy_common);
    + if (err)
    + return err;

    - if (!phy_common->is_started) {
    - err = ufs_qcom_phy_start_serdes(phy_common);
    + if (phy_common->ufs_reset) {
    + err = reset_control_assert(phy_common->ufs_reset);
    if (err)
    return err;
    + }

    - err = ufs_qcom_phy_is_pcs_ready(phy_common);
    - if (err)
    - return err;
    + if (phy_common->mode == PHY_MODE_UFS_HS_B)
    + is_rate_B = true;

    - phy_common->is_started = true;
    + err = phy_common->phy_spec_ops->calibrate(phy_common, is_rate_B);
    + if (err)
    + return err;
    +
    + if (phy_common->ufs_reset) {
    + 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",
    @@ -587,7 +621,6 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy)
    }
    }

    - phy_common->is_powered_on = true;
    goto out;

    out_disable_ref_clk:
    @@ -607,9 +640,6 @@ int ufs_qcom_phy_power_off(struct phy *generic_phy)
    {
    struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);

    - if (!phy_common->is_powered_on)
    - return 0;
    -
    phy_common->phy_spec_ops->power_control(phy_common, false);

    if (phy_common->vddp_ref_clk.reg)
    @@ -620,7 +650,8 @@ 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);
    - phy_common->is_powered_on = false;
    + if (phy_common->ufs_reset)
    + reset_control_assert(phy_common->ufs_reset);

    return 0;
    }
    --
    2.18.1
    \
     
     \ /
      Last update: 2019-01-23 23:12    [W:8.213 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site