From: Aleksandrs Vinarskis <alex.vinarskis@gmail.com>
To: Wenbin Yao <quic_wenbyao@quicinc.com>,
vkoul@kernel.org, kishon@kernel.org, p.zabel@pengutronix.de,
dmitry.baryshkov@linaro.org, abel.vesa@linaro.org,
quic_qianyu@quicinc.com, neil.armstrong@linaro.org,
manivannan.sadhasivam@linaro.org, quic_devipriy@quicinc.com,
konrad.dybcio@oss.qualcomm.com, linux-arm-msm@vger.kernel.org,
linux-phy@lists.infradead.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v6 2/2] phy: qcom: qmp-pcie: Add PHY register retention support
Date: Tue, 1 Apr 2025 17:21:04 +0200 [thread overview]
Message-ID: <152fd6fc-41a3-4a2b-bc84-04e309db87cc@gmail.com> (raw)
In-Reply-To: <20250319094544.3980357-3-quic_wenbyao@quicinc.com>
On 3/19/25 10:45, Wenbin Yao wrote:
> From: Qiang Yu <quic_qianyu@quicinc.com>
>
> 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>
Successfully tested on Snapdragon X1-26-100 on Asus Zenbook A14.
This fixes pcie6a_phy with "qcom,x1p42100-qmp-gen4x4-pcie-phy" as
compatible, which was not working before.
Tested-by: Aleksandrs Vinarskis <alex.vinarskis@gmail.com>
> ---
> drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 69 ++++++++++++++++++++----
> 1 file changed, 59 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
> index 38dbe690f2d5..23a57152e8fd 100644
> --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
> @@ -2981,6 +2981,7 @@ struct qmp_pcie {
>
> const struct qmp_phy_cfg *cfg;
> bool tcsr_4ln_config;
> + bool skip_init;
>
> void __iomem *serdes;
> void __iomem *pcs;
> @@ -4229,18 +4230,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);
> @@ -4251,10 +4272,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);
> @@ -4264,7 +4287,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);
>
> @@ -4276,7 +4300,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);
>
> @@ -4295,6 +4322,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);
>
> @@ -4306,6 +4340,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;
> @@ -4316,6 +4351,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);
>
> @@ -4325,6 +4363,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,
> @@ -4349,6 +4388,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);
>
> @@ -4360,6 +4408,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;
> }
>
WARNING: multiple messages have this Message-ID (diff)
From: Aleksandrs Vinarskis <alex.vinarskis@gmail.com>
To: Wenbin Yao <quic_wenbyao@quicinc.com>,
vkoul@kernel.org, kishon@kernel.org, p.zabel@pengutronix.de,
dmitry.baryshkov@linaro.org, abel.vesa@linaro.org,
quic_qianyu@quicinc.com, neil.armstrong@linaro.org,
manivannan.sadhasivam@linaro.org, quic_devipriy@quicinc.com,
konrad.dybcio@oss.qualcomm.com, linux-arm-msm@vger.kernel.org,
linux-phy@lists.infradead.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v6 2/2] phy: qcom: qmp-pcie: Add PHY register retention support
Date: Tue, 1 Apr 2025 17:21:04 +0200 [thread overview]
Message-ID: <152fd6fc-41a3-4a2b-bc84-04e309db87cc@gmail.com> (raw)
In-Reply-To: <20250319094544.3980357-3-quic_wenbyao@quicinc.com>
On 3/19/25 10:45, Wenbin Yao wrote:
> From: Qiang Yu <quic_qianyu@quicinc.com>
>
> 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>
Successfully tested on Snapdragon X1-26-100 on Asus Zenbook A14.
This fixes pcie6a_phy with "qcom,x1p42100-qmp-gen4x4-pcie-phy" as
compatible, which was not working before.
Tested-by: Aleksandrs Vinarskis <alex.vinarskis@gmail.com>
> ---
> drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 69 ++++++++++++++++++++----
> 1 file changed, 59 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
> index 38dbe690f2d5..23a57152e8fd 100644
> --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
> @@ -2981,6 +2981,7 @@ struct qmp_pcie {
>
> const struct qmp_phy_cfg *cfg;
> bool tcsr_4ln_config;
> + bool skip_init;
>
> void __iomem *serdes;
> void __iomem *pcs;
> @@ -4229,18 +4230,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);
> @@ -4251,10 +4272,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);
> @@ -4264,7 +4287,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);
>
> @@ -4276,7 +4300,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);
>
> @@ -4295,6 +4322,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);
>
> @@ -4306,6 +4340,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;
> @@ -4316,6 +4351,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);
>
> @@ -4325,6 +4363,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,
> @@ -4349,6 +4388,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);
>
> @@ -4360,6 +4408,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;
> }
>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
next prev parent reply other threads:[~2025-04-01 15:21 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-19 9:45 [PATCH v6 0/2] phy: qcom: qmp-pcie: Add PCIe PHY no_csr reset support Wenbin Yao
2025-03-19 9:45 ` Wenbin Yao
2025-03-19 9:45 ` [PATCH v6 1/2] phy: qcom: pcie: Determine has_nocsr_reset dynamically Wenbin Yao
2025-03-19 9:45 ` Wenbin Yao
2025-04-01 15:20 ` Aleksandrs Vinarskis
2025-04-01 15:20 ` Aleksandrs Vinarskis
2025-03-19 9:45 ` [PATCH v6 2/2] phy: qcom: qmp-pcie: Add PHY register retention support Wenbin Yao
2025-03-19 9:45 ` Wenbin Yao
2025-04-01 15:21 ` Aleksandrs Vinarskis [this message]
2025-04-01 15:21 ` Aleksandrs Vinarskis
2025-03-31 8:58 ` [PATCH v6 0/2] phy: qcom: qmp-pcie: Add PCIe PHY no_csr reset support Wenbin Yao (Consultant)
2025-03-31 8:58 ` Wenbin Yao (Consultant)
2025-04-11 7:29 ` Vinod Koul
2025-04-11 7:29 ` Vinod Koul
2025-04-11 12:10 ` Vinod Koul
2025-04-11 12:10 ` Vinod Koul
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=152fd6fc-41a3-4a2b-bc84-04e309db87cc@gmail.com \
--to=alex.vinarskis@gmail.com \
--cc=abel.vesa@linaro.org \
--cc=dmitry.baryshkov@linaro.org \
--cc=kishon@kernel.org \
--cc=konrad.dybcio@oss.qualcomm.com \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-phy@lists.infradead.org \
--cc=manivannan.sadhasivam@linaro.org \
--cc=neil.armstrong@linaro.org \
--cc=p.zabel@pengutronix.de \
--cc=quic_devipriy@quicinc.com \
--cc=quic_qianyu@quicinc.com \
--cc=quic_wenbyao@quicinc.com \
--cc=vkoul@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.