From: Qiang Yu <quic_qianyu@xxxxxxxxxxx> Some QCOM PCIe PHYs support no_csr reset. Unlike BCR reset which resets the whole PHY (hardware and register), no_csr reset only resets PHY hardware but retains register values, which means PHY setting can be skipped during PHY init if PCIe link is enabled in booltloader and only no_csr is toggled after that. Hence, determine whether the PHY has been enabled in bootloader by verifying QPHY_START_CTRL register. If it's programmed and no_csr reset is available, skip BCR reset and PHY register setting to establish the PCIe link with bootloader - programmed PHY settings. Signed-off-by: Qiang Yu <quic_qianyu@xxxxxxxxxxx> Signed-off-by: Wenbin Yao <quic_wenbyao@xxxxxxxxxxx> --- drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 91 +++++++++++++++--------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index ac42e4b01065..7f0802d09812 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -2805,6 +2805,7 @@ struct qmp_pcie { const struct qmp_phy_cfg *cfg; bool tcsr_4ln_config; + bool phy_initialized; void __iomem *serdes; void __iomem *pcs; @@ -3976,6 +3977,7 @@ static int qmp_pcie_init(struct phy *phy) { struct qmp_pcie *qmp = phy_get_drvdata(phy); const struct qmp_phy_cfg *cfg = qmp->cfg; + void __iomem *pcs = qmp->pcs; int ret; ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); @@ -3984,10 +3986,17 @@ static int qmp_pcie_init(struct phy *phy) return ret; } - ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets); - if (ret) { - dev_err(qmp->dev, "reset assert failed\n"); - goto err_disable_regulators; + qmp->phy_initialized = !!(readl(pcs + cfg->regs[QPHY_START_CTRL])); + /* + * Toggle BCR reset for phy that doesn't support no_csr + * reset or has not been initialized + */ + if (!qmp->nocsr_reset || !qmp->phy_initialized) { + ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets); + if (ret) { + dev_err(qmp->dev, "reset assert failed\n"); + goto err_disable_regulators; + } } ret = reset_control_assert(qmp->nocsr_reset); @@ -3998,10 +4007,12 @@ static int qmp_pcie_init(struct phy *phy) usleep_range(200, 300); - ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets); - if (ret) { - dev_err(qmp->dev, "reset deassert failed\n"); - goto err_assert_reset; + if (!qmp->nocsr_reset || !qmp->phy_initialized) { + ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets); + if (ret) { + dev_err(qmp->dev, "reset deassert failed\n"); + goto err_assert_reset; + } } ret = clk_bulk_prepare_enable(ARRAY_SIZE(qmp_pciephy_clk_l), qmp->clks); @@ -4011,7 +4022,8 @@ static int qmp_pcie_init(struct phy *phy) return 0; err_assert_reset: - reset_control_bulk_assert(cfg->num_resets, qmp->resets); + if (!qmp->nocsr_reset || !qmp->phy_initialized) + reset_control_bulk_assert(cfg->num_resets, qmp->resets); err_disable_regulators: regulator_bulk_disable(cfg->num_vregs, qmp->vregs); @@ -4023,7 +4035,10 @@ static int qmp_pcie_exit(struct phy *phy) struct qmp_pcie *qmp = phy_get_drvdata(phy); const struct qmp_phy_cfg *cfg = qmp->cfg; - reset_control_bulk_assert(cfg->num_resets, qmp->resets); + if (!qmp->nocsr_reset) + reset_control_bulk_assert(cfg->num_resets, qmp->resets); + else + reset_control_assert(qmp->nocsr_reset); clk_bulk_disable_unprepare(ARRAY_SIZE(qmp_pciephy_clk_l), qmp->clks); @@ -4042,16 +4057,22 @@ static int qmp_pcie_power_on(struct phy *phy) unsigned int mask, val; int ret; - qphy_setbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], - cfg->pwrdn_ctrl); + /* + * Write CSR register for phy that doesn't support no_csr + * reset or has not been initialized + */ + if (!qmp->nocsr_reset || !qmp->phy_initialized) { + qphy_setbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], + cfg->pwrdn_ctrl); - if (qmp->mode == PHY_MODE_PCIE_RC) - mode_tbls = cfg->tbls_rc; - else - mode_tbls = cfg->tbls_ep; + if (qmp->mode == PHY_MODE_PCIE_RC) + mode_tbls = cfg->tbls_rc; + else + mode_tbls = cfg->tbls_ep; - qmp_pcie_init_registers(qmp, &cfg->tbls); - qmp_pcie_init_registers(qmp, mode_tbls); + qmp_pcie_init_registers(qmp, &cfg->tbls); + qmp_pcie_init_registers(qmp, mode_tbls); + } ret = clk_bulk_prepare_enable(qmp->num_pipe_clks, qmp->pipe_clks); if (ret) @@ -4063,15 +4084,16 @@ static int qmp_pcie_power_on(struct phy *phy) goto err_disable_pipe_clk; } - /* Pull PHY out of reset state */ - qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + if (!qmp->nocsr_reset || !qmp->phy_initialized) { + /* Pull PHY out of reset state */ + qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); - /* start SerDes and Phy-Coding-Sublayer */ - qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], SERDES_START | PCS_START); - - if (!cfg->skip_start_delay) - usleep_range(1000, 1200); + /* start SerDes and Phy-Coding-Sublayer */ + qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], SERDES_START | PCS_START); + if (!cfg->skip_start_delay) + usleep_range(1000, 1200); + } status = pcs + cfg->regs[QPHY_PCS_STATUS]; mask = cfg->phy_status; ret = readl_poll_timeout(status, val, !(val & mask), 200, @@ -4096,16 +4118,19 @@ static int qmp_pcie_power_off(struct phy *phy) clk_bulk_disable_unprepare(qmp->num_pipe_clks, qmp->pipe_clks); - /* PHY reset */ - qphy_setbits(qmp->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); - /* stop SerDes and Phy-Coding-Sublayer */ - qphy_clrbits(qmp->pcs, cfg->regs[QPHY_START_CTRL], - SERDES_START | PCS_START); + if (!qmp->nocsr_reset) { + /* PHY reset */ + qphy_setbits(qmp->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); - /* Put PHY into POWER DOWN state: active low */ - qphy_clrbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], - cfg->pwrdn_ctrl); + /* stop SerDes and Phy-Coding-Sublayer */ + qphy_clrbits(qmp->pcs, cfg->regs[QPHY_START_CTRL], + SERDES_START | PCS_START); + + /* Put PHY into POWER DOWN state: active low */ + qphy_clrbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], + cfg->pwrdn_ctrl); + } return 0; } -- 2.34.1