mirror of https://github.com/torvalds/linux.git
phy: qcom: qmp-pcie: Add PHY register retention support
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 bootloader 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@quicinc.com> Signed-off-by: Wenbin Yao <quic_wenbyao@quicinc.com> Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Tested-by: Aleksandrs Vinarskis <alex.vinarskis@gmail.com> Link: https://lore.kernel.org/r/20250411113120.651363-3-quic_wenbyao@quicinc.com Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
parent
ea57d7fe4f
commit
0cc22f5a86
|
|
@ -3033,6 +3033,7 @@ struct qmp_pcie {
|
|||
|
||||
const struct qmp_phy_cfg *cfg;
|
||||
bool tcsr_4ln_config;
|
||||
bool skip_init;
|
||||
|
||||
void __iomem *serdes;
|
||||
void __iomem *pcs;
|
||||
|
|
@ -4330,18 +4331,38 @@ 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;
|
||||
bool phy_initialized = !!(readl(pcs + cfg->regs[QPHY_START_CTRL]));
|
||||
int ret;
|
||||
|
||||
qmp->skip_init = qmp->nocsr_reset && phy_initialized;
|
||||
/*
|
||||
* We need to check the existence of init sequences in two cases:
|
||||
* 1. The PHY doesn't support no_csr reset.
|
||||
* 2. The PHY supports no_csr reset but isn't initialized by bootloader.
|
||||
* As we can't skip init in these two cases.
|
||||
*/
|
||||
if (!qmp->skip_init && !cfg->tbls.serdes_num) {
|
||||
dev_err(qmp->dev, "Init sequence not available\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
|
||||
if (ret) {
|
||||
dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
|
||||
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;
|
||||
/*
|
||||
* Toggle BCR reset for PHY that doesn't support no_csr reset or has not
|
||||
* been initialized.
|
||||
*/
|
||||
if (!qmp->skip_init) {
|
||||
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);
|
||||
|
|
@ -4352,10 +4373,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->skip_init) {
|
||||
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);
|
||||
|
|
@ -4365,7 +4388,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->skip_init)
|
||||
reset_control_bulk_assert(cfg->num_resets, qmp->resets);
|
||||
err_disable_regulators:
|
||||
regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
|
||||
|
||||
|
|
@ -4377,7 +4401,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_assert(qmp->nocsr_reset);
|
||||
else
|
||||
reset_control_bulk_assert(cfg->num_resets, qmp->resets);
|
||||
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(qmp_pciephy_clk_l), qmp->clks);
|
||||
|
||||
|
|
@ -4396,6 +4423,13 @@ static int qmp_pcie_power_on(struct phy *phy)
|
|||
unsigned int mask, val;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Write CSR register for PHY that doesn't support no_csr reset or has not
|
||||
* been initialized.
|
||||
*/
|
||||
if (qmp->skip_init)
|
||||
goto skip_tbls_init;
|
||||
|
||||
qphy_setbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
|
||||
cfg->pwrdn_ctrl);
|
||||
|
||||
|
|
@ -4407,6 +4441,7 @@ static int qmp_pcie_power_on(struct phy *phy)
|
|||
qmp_pcie_init_registers(qmp, &cfg->tbls);
|
||||
qmp_pcie_init_registers(qmp, mode_tbls);
|
||||
|
||||
skip_tbls_init:
|
||||
ret = clk_bulk_prepare_enable(qmp->num_pipe_clks, qmp->pipe_clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -4417,6 +4452,9 @@ static int qmp_pcie_power_on(struct phy *phy)
|
|||
goto err_disable_pipe_clk;
|
||||
}
|
||||
|
||||
if (qmp->skip_init)
|
||||
goto skip_serdes_start;
|
||||
|
||||
/* Pull PHY out of reset state */
|
||||
qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
|
||||
|
||||
|
|
@ -4426,6 +4464,7 @@ static int qmp_pcie_power_on(struct phy *phy)
|
|||
if (!cfg->skip_start_delay)
|
||||
usleep_range(1000, 1200);
|
||||
|
||||
skip_serdes_start:
|
||||
status = pcs + cfg->regs[QPHY_PCS_STATUS];
|
||||
mask = cfg->phy_status;
|
||||
ret = readl_poll_timeout(status, val, !(val & mask), 200,
|
||||
|
|
@ -4450,6 +4489,15 @@ static int qmp_pcie_power_off(struct phy *phy)
|
|||
|
||||
clk_bulk_disable_unprepare(qmp->num_pipe_clks, qmp->pipe_clks);
|
||||
|
||||
/*
|
||||
* While powering off the PHY, only qmp->nocsr_reset needs to be checked. In
|
||||
* this way, no matter whether the PHY settings were initially programmed by
|
||||
* bootloader or PHY driver itself, we can reuse them when PHY is powered on
|
||||
* next time.
|
||||
*/
|
||||
if (qmp->nocsr_reset)
|
||||
goto skip_phy_deinit;
|
||||
|
||||
/* PHY reset */
|
||||
qphy_setbits(qmp->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
|
||||
|
||||
|
|
@ -4461,6 +4509,7 @@ static int qmp_pcie_power_off(struct phy *phy)
|
|||
qphy_clrbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
|
||||
cfg->pwrdn_ctrl);
|
||||
|
||||
skip_phy_deinit:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue