return 0;
}
-/* PHY Initialization */
-static int qcom_qmp_phy_init(struct phy *phy)
+static int qcom_qmp_phy_enable(struct phy *phy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
if (ret)
goto err_lane_rst;
- /*
- * UFS PHY requires the deassert of software reset before serdes start.
- * For UFS PHYs that do not have software reset control bits, defer
- * starting serdes until the power on callback.
- */
- if ((cfg->type == PHY_TYPE_UFS) && cfg->no_pcs_sw_reset)
- goto out;
-
/*
* Pull out PHY from POWER DOWN state.
* This is active low enable signal to power-down PHY.
usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
/* Pull PHY out of reset state */
- qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+ if (!cfg->no_pcs_sw_reset)
+ qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+
if (cfg->has_phy_dp_com_ctrl)
qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
goto err_pcs_ready;
}
qmp->phy_initialized = true;
-
-out:
- return ret;
+ return 0;
err_pcs_ready:
+ reset_control_assert(qmp->ufs_reset);
clk_disable_unprepare(qphy->pipe_clk);
err_clk_enable:
if (cfg->has_lane_rst)
return ret;
}
-static int qcom_qmp_phy_exit(struct phy *phy)
+static int qcom_qmp_phy_disable(struct phy *phy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
return 0;
}
-static int qcom_qmp_phy_poweron(struct phy *phy)
-{
- struct qmp_phy *qphy = phy_get_drvdata(phy);
- struct qcom_qmp *qmp = qphy->qmp;
- const struct qmp_phy_cfg *cfg = qmp->cfg;
- void __iomem *pcs = qphy->pcs;
- void __iomem *status;
- unsigned int mask, val;
- int ret = 0;
-
- if (cfg->type != PHY_TYPE_UFS)
- return 0;
-
- /*
- * For UFS PHY that has not software reset control, serdes start
- * should only happen when UFS driver explicitly calls phy_power_on
- * after it deasserts software reset.
- */
- if (cfg->no_pcs_sw_reset && !qmp->phy_initialized &&
- (qmp->init_count != 0)) {
- /* start SerDes and Phy-Coding-Sublayer */
- qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
-
- status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
- mask = cfg->mask_pcs_ready;
-
- ret = readl_poll_timeout(status, val, !(val & mask), 1,
- PHY_INIT_COMPLETE_TIMEOUT);
- if (ret) {
- dev_err(qmp->dev, "phy initialization timed-out\n");
- return ret;
- }
- qmp->phy_initialized = true;
- }
-
- return ret;
-}
-
static int qcom_qmp_phy_set_mode(struct phy *phy,
enum phy_mode mode, int submode)
{
}
static const struct phy_ops qcom_qmp_phy_gen_ops = {
- .init = qcom_qmp_phy_init,
- .exit = qcom_qmp_phy_exit,
- .power_on = qcom_qmp_phy_poweron,
+ .init = qcom_qmp_phy_enable,
+ .exit = qcom_qmp_phy_disable,
+ .set_mode = qcom_qmp_phy_set_mode,
+ .owner = THIS_MODULE,
+};
+
+static const struct phy_ops qcom_qmp_ufs_ops = {
+ .power_on = qcom_qmp_phy_enable,
+ .power_off = qcom_qmp_phy_disable,
.set_mode = qcom_qmp_phy_set_mode,
.owner = THIS_MODULE,
};
struct qcom_qmp *qmp = dev_get_drvdata(dev);
struct phy *generic_phy;
struct qmp_phy *qphy;
+ const struct phy_ops *ops = &qcom_qmp_phy_gen_ops;
char prop_name[MAX_PROP_NAME];
int ret;
}
}
- generic_phy = devm_phy_create(dev, np, &qcom_qmp_phy_gen_ops);
+ if (qmp->cfg->type == PHY_TYPE_UFS)
+ ops = &qcom_qmp_ufs_ops;
+
+ generic_phy = devm_phy_create(dev, np, ops);
if (IS_ERR(generic_phy)) {
ret = PTR_ERR(generic_phy);
dev_err(dev, "failed to create qphy %d\n", ret);
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;
* 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);
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,
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;
-
- ret = ufs_qcom_phy_get_reset(phy_common);
- if (ret)
- return ret;
-
- ret = reset_control_assert(phy_common->ufs_reset);
- if (ret)
- return 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)
}
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,
};
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,
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;
-
- ret = ufs_qcom_phy_get_reset(phy_common);
- if (ret)
- return ret;
-
- ret = reset_control_assert(phy_common->ufs_reset);
- if (ret)
- return 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)
}
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,
};
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,
}
EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe);
-int ufs_qcom_phy_get_reset(struct ufs_qcom_phy *phy_common)
+static int ufs_qcom_phy_get_reset(struct ufs_qcom_phy *phy_common)
{
struct reset_control *reset;
phy_common->ufs_reset = reset;
return 0;
}
-EXPORT_SYMBOL_GPL(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)
{
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;
+
+ 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) {
return err;
}
- if (!phy_common->is_started) {
- 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_start_serdes(phy_common);
+ if (err)
+ return err;
- phy_common->is_started = true;
- }
+ 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) {
}
}
- phy_common->is_powered_on = true;
goto out;
out_disable_ref_clk:
{
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)
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);
- phy_common->is_powered_on = false;
-
return 0;
}
EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off);
*/
ufs_qcom_disable_lane_clks(host);
phy_power_off(phy);
- goto out;
- }
- /*
- * If UniPro link is not active, PHY ref_clk, main PHY analog power
- * rail and low noise analog power rail for PLL can be switched off.
- */
- if (!ufs_qcom_is_link_active(hba)) {
+ } else if (!ufs_qcom_is_link_active(hba)) {
ufs_qcom_disable_lane_clks(host);
- phy_power_off(phy);
}
-out:
return ret;
}
struct phy *phy = host->generic_phy;
int err;
- err = phy_power_on(phy);
- if (err) {
- dev_err(hba->dev, "%s: failed enabling regs, err = %d\n",
- __func__, err);
- goto out;
- }
+ if (ufs_qcom_is_link_off(hba)) {
+ err = phy_power_on(phy);
+ if (err) {
+ dev_err(hba->dev, "%s: failed PHY power on: %d\n",
+ __func__, err);
+ return err;
+ }
- err = ufs_qcom_enable_lane_clks(host);
- if (err)
- goto out;
+ err = ufs_qcom_enable_lane_clks(host);
+ if (err)
+ return err;
- hba->is_sys_suspended = false;
+ } else if (!ufs_qcom_is_link_active(hba)) {
+ err = ufs_qcom_enable_lane_clks(host);
+ if (err)
+ return err;
+ }
-out:
- return err;
+ hba->is_sys_suspended = false;
+ return 0;
}
struct ufs_qcom_dev_params {
return 0;
if (on && (status == POST_CHANGE)) {
- phy_power_on(host->generic_phy);
-
/* enable the device ref clock for HS mode*/
if (ufshcd_is_hs_mode(&hba->pwr_info))
ufs_qcom_dev_ref_clk_ctrl(host, true);
if (!ufs_qcom_is_link_active(hba)) {
/* disable device ref_clk */
ufs_qcom_dev_ref_clk_ctrl(host, false);
-
- /* powering off PHY during aggressive clk gating */
- phy_power_off(host->generic_phy);
}
vote = host->bus_vote.min_bw_vote;