From: Neil Armstrong <neil.armstrong@linaro.org>
To: Mattijs Korpershoek <mkorpershoek@kernel.org>,
Vinod Koul <vkoul@kernel.org>, Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>
Cc: Maxime Ripard <mripard@kernel.org>,
linux-arm-msm@vger.kernel.org, linux-phy@lists.infradead.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Sriram Dash <quic_sriramd@quicinc.com>,
Shazad Hussain <quic_shazhuss@quicinc.com>
Subject: Re: [PATCH v5 2/2] phy: qcom: qmp-usb: Add support for SA8255P
Date: Thu, 24 Sep 2026 10:13:54 +0200 [thread overview]
Message-ID: <a72169ad-b1ae-4502-8997-4b86999b31eb@linaro.org> (raw)
In-Reply-To: <20260903-phy-qcom-qmp-usb-8255-v5-2-e251e22b341e@kernel.org>
On 9/3/26 09:44, Mattijs Korpershoek wrote:
> From: Sriram Dash <quic_sriramd@quicinc.com>
>
> The Qualcomm automotive SA8255p SoC relies on firmware to configure
> platform resources, including clocks, interconnects and TLMM.
> The driver requests resources operations over SCMI using power protocols.
>
> The SCMI power protocol enables or disables resources like clocks,
> interconnect paths, and TLMM (GPIOs) using runtime PM framework APIs,
> such as resume/suspend, to control power on/off.
>
> Power domain "core" controls the GDSC, resets, and TLMM (GPIOs)
> whereas power domain "transfer" controls clocks and interconnects.
>
> In this variant, two disjoint sets of resources are modeled against
> the power domain suppliers which can be turned on/off to achieve modeled
> device states. Two modeled low power states d1 and d3 are supported.
>
> transition: d0 <--> d3 :: both core and transfer suppliers
> transition: d0 <--> d1 :: only transfer supplier
>
> Signed-off-by: Sriram Dash <quic_sriramd@quicinc.com>
> Signed-off-by: Shazad Hussain <quic_shazhuss@quicinc.com>
> Signed-off-by: Mattijs Korpershoek <mkorpershoek@kernel.org>
> ---
> drivers/phy/qualcomm/phy-qcom-qmp-usb.c | 182 ++++++++++++++++++++++++++++----
> 1 file changed, 160 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c
> index b0790bcf0bc8..c18680b60803 100644
> --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c
> @@ -15,6 +15,7 @@
> #include <linux/of_address.h>
> #include <linux/phy/phy.h>
> #include <linux/platform_device.h>
> +#include <linux/pm_domain.h>
> #include <linux/regulator/consumer.h>
> #include <linux/reset.h>
> #include <linux/slab.h>
> @@ -1411,6 +1412,9 @@ struct qmp_phy_cfg {
>
> /* Offset from PCS to PCS_USB region */
> unsigned int pcs_usb_offset;
> +
> + /* true, if the device resources are fw managed */
> + bool fw_managed;
> };
>
> struct qmp_usb {
> @@ -1439,6 +1443,8 @@ struct qmp_usb {
> struct phy *phy;
>
> struct clk_fixed_rate pipe_clk_fixed;
> +
> + struct dev_pm_domain_list *pd_list;
> };
>
> static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
> @@ -1633,6 +1639,25 @@ static const struct qmp_phy_cfg qdu1000_usb3_uniphy_cfg = {
> .has_pwrdn_delay = true,
> };
>
> +static const struct qmp_phy_cfg sa8255p_usb3_uniphy_cfg = {
> + .offsets = &qmp_usb_offsets_v5,
> +
> + .serdes_tbl = sc8280xp_usb3_uniphy_serdes_tbl,
> + .serdes_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_serdes_tbl),
> + .tx_tbl = sc8280xp_usb3_uniphy_tx_tbl,
> + .tx_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_tx_tbl),
> + .rx_tbl = sc8280xp_usb3_uniphy_rx_tbl,
> + .rx_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_rx_tbl),
> + .pcs_tbl = sa8775p_usb3_uniphy_pcs_tbl,
> + .pcs_tbl_num = ARRAY_SIZE(sa8775p_usb3_uniphy_pcs_tbl),
> + .pcs_usb_tbl = sa8775p_usb3_uniphy_pcs_usb_tbl,
> + .pcs_usb_tbl_num = ARRAY_SIZE(sa8775p_usb3_uniphy_pcs_usb_tbl),
> + .vreg_list = NULL,
> + .num_vregs = 0, /* Regulators are handled by the firmware */
> + .regs = qmp_v5_usb3phy_regs_layout,
> + .fw_managed = true,
> +};
> +
> static const struct qmp_phy_cfg sa8775p_usb3_uniphy_cfg = {
> .offsets = &qmp_usb_offsets_v5,
>
> @@ -1867,6 +1892,77 @@ static const struct qmp_phy_cfg glymur_usb3_uniphy_cfg = {
> .regs = qmp_v7_usb3phy_regs_layout,
> };
>
> +static int qmp_usb_domain_attach(struct qmp_usb *qmp)
> +{
> + struct dev_pm_domain_attach_data pd_data = {
> + .pd_flags = PD_FLAG_NO_DEV_LINK,
> + .pd_names = (const char *[]) { "core", "transfer" },
> + .num_pd_names = 2,
> + };
> + struct device *dev = qmp->dev;
> + int ret;
> +
> + ret = dev_pm_domain_attach_list(dev, &pd_data, &qmp->pd_list);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "domain attach failed\n");
> +
> + return 0;
> +}
> +
> +static void qmp_usb_domain_detach(void *data)
> +{
> + struct qmp_usb *qmp = data;
> +
> + dev_pm_domain_detach_list(qmp->pd_list);
> +}
> +
> +/* d3_to_d0 transition by turning on all the suppliers */
> +static int qmp_usb_d3_to_d0(struct qmp_usb *qmp)
> +{
> + int ret;
> +
> + if (!qmp->pd_list)
> + return 0;
> +
> + ret = pm_runtime_resume_and_get(qmp->pd_list->pd_devs[0]);
> + if (ret)
> + return ret;
> +
> + ret = pm_runtime_resume_and_get(qmp->pd_list->pd_devs[1]);
> + if (ret)
> + pm_runtime_put_sync(qmp->pd_list->pd_devs[0]);
> +
> + return ret;
> +}
> +
> +/* d0_to_d3 transition by turning off all the suppliers */
> +static void qmp_usb_d0_to_d3(struct qmp_usb *qmp)
> +{
> + if (!qmp->pd_list)
> + return;
> +
> + pm_runtime_put_sync(qmp->pd_list->pd_devs[1]);
> + pm_runtime_put_sync(qmp->pd_list->pd_devs[0]);
> +}
> +
> +/* d1_to_d0 transition by turning on the 'transfer' supplier */
> +static int qmp_usb_d1_to_d0(struct qmp_usb *qmp)
> +{
> + if (!qmp->pd_list)
> + return 0;
> +
> + return pm_runtime_resume_and_get(qmp->pd_list->pd_devs[1]);
> +}
> +
> +/* d0_to_d1 transition by turning off the 'transfer' supplier */
> +static void qmp_usb_d0_to_d1(struct qmp_usb *qmp)
> +{
> + if (!qmp->pd_list)
> + return;
> +
> + pm_runtime_put_sync(qmp->pd_list->pd_devs[1]);
> +}
> +
> static int qmp_usb_serdes_init(struct qmp_usb *qmp)
> {
> const struct qmp_phy_cfg *cfg = qmp->cfg;
> @@ -1886,10 +1982,16 @@ static int qmp_usb_init(struct phy *phy)
> void __iomem *pcs = qmp->pcs;
> int ret;
>
> + ret = qmp_usb_d3_to_d0(qmp);
> + if (ret < 0) {
> + dev_err(qmp->dev, "Failed to transition to d0 state\n");
> + return ret;
> + }
> +
> 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;
> + goto err_transition_to_d3;
> }
>
> ret = reset_control_bulk_assert(qmp->num_resets, qmp->resets);
> @@ -1916,6 +2018,8 @@ static int qmp_usb_init(struct phy *phy)
> reset_control_bulk_assert(qmp->num_resets, qmp->resets);
> err_disable_regulators:
> regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
> +err_transition_to_d3:
> + qmp_usb_d0_to_d3(qmp);
>
> return ret;
> }
> @@ -1931,6 +2035,8 @@ static int qmp_usb_exit(struct phy *phy)
>
> regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
>
> + qmp_usb_d0_to_d3(qmp);
> +
> return 0;
> }
>
> @@ -2119,6 +2225,8 @@ static int __maybe_unused qmp_usb_runtime_suspend(struct device *dev)
> clk_disable_unprepare(qmp->pipe_clk);
> clk_bulk_disable_unprepare(qmp->num_clks, qmp->clks);
>
> + qmp_usb_d0_to_d1(qmp);
> +
> return 0;
> }
>
> @@ -2134,14 +2242,23 @@ static int __maybe_unused qmp_usb_runtime_resume(struct device *dev)
> return 0;
> }
>
> + ret = qmp_usb_d1_to_d0(qmp);
> + if (ret < 0) {
> + dev_err(qmp->dev, "Failed to transition to d0 state\n");
> + return ret;
> + }
> +
> ret = clk_bulk_prepare_enable(qmp->num_clks, qmp->clks);
> - if (ret)
> + if (ret) {
> + qmp_usb_d0_to_d1(qmp);
> return ret;
> + }
>
> ret = clk_prepare_enable(qmp->pipe_clk);
> if (ret) {
> dev_err(dev, "pipe_clk enable failed, err=%d\n", ret);
> clk_bulk_disable_unprepare(qmp->num_clks, qmp->clks);
> + qmp_usb_d0_to_d1(qmp);
> return ret;
> }
>
> @@ -2321,10 +2438,12 @@ static int qmp_usb_parse_dt_legacy(struct qmp_usb *qmp, struct device_node *np)
> qmp->pcs_misc = NULL;
> }
>
> - qmp->pipe_clk = devm_get_clk_from_child(dev, np, NULL);
> - if (IS_ERR(qmp->pipe_clk)) {
> - return dev_err_probe(dev, PTR_ERR(qmp->pipe_clk),
> - "failed to get pipe clock\n");
> + if (!qmp->cfg->fw_managed) {
> + qmp->pipe_clk = devm_get_clk_from_child(dev, np, NULL);
> + if (IS_ERR(qmp->pipe_clk)) {
> + return dev_err_probe(dev, PTR_ERR(qmp->pipe_clk),
> + "failed to get pipe clock\n");
> + }
> }
>
> ret = devm_clk_bulk_get_all(qmp->dev, &qmp->clks);
> @@ -2333,10 +2452,12 @@ static int qmp_usb_parse_dt_legacy(struct qmp_usb *qmp, struct device_node *np)
>
> qmp->num_clks = ret;
>
> - ret = qmp_usb_reset_init(qmp, usb3phy_legacy_reset_l,
> - ARRAY_SIZE(usb3phy_legacy_reset_l));
> - if (ret)
> - return ret;
> + if (!qmp->cfg->fw_managed) {
> + ret = qmp_usb_reset_init(qmp, usb3phy_legacy_reset_l,
> + ARRAY_SIZE(usb3phy_legacy_reset_l));
> + if (ret)
> + return ret;
> + }
>
> return 0;
> }
> @@ -2370,17 +2491,19 @@ static int qmp_usb_parse_dt(struct qmp_usb *qmp)
> if (ret)
> return ret;
>
> - qmp->pipe_clk = devm_clk_get(dev, "pipe");
> - if (IS_ERR(qmp->pipe_clk)) {
> - return dev_err_probe(dev, PTR_ERR(qmp->pipe_clk),
> - "failed to get pipe clock\n");
> + if (!qmp->cfg->fw_managed) {
> + qmp->pipe_clk = devm_clk_get(dev, "pipe");
> + if (IS_ERR(qmp->pipe_clk)) {
> + return dev_err_probe(dev, PTR_ERR(qmp->pipe_clk),
> + "failed to get pipe clock\n");
> + }
> +
> + ret = qmp_usb_reset_init(qmp, usb3phy_reset_l,
> + ARRAY_SIZE(usb3phy_reset_l));
> + if (ret)
> + return ret;
> }
>
> - ret = qmp_usb_reset_init(qmp, usb3phy_reset_l,
> - ARRAY_SIZE(usb3phy_reset_l));
> - if (ret)
> - return ret;
> -
> return 0;
> }
>
> @@ -2403,6 +2526,16 @@ static int qmp_usb_probe(struct platform_device *pdev)
> if (!qmp->cfg)
> return -EINVAL;
>
> + if (qmp->cfg->fw_managed) {
> + ret = qmp_usb_domain_attach(qmp);
> + if (ret)
> + return ret;
> +
> + ret = devm_add_action_or_reset(dev, qmp_usb_domain_detach, qmp);
> + if (ret)
> + return ret;
> + }
> +
> ret = devm_regulator_bulk_get_const(dev, qmp->cfg->num_vregs,
> qmp->cfg->vreg_list, &qmp->vregs);
> if (ret)
> @@ -2435,9 +2568,11 @@ static int qmp_usb_probe(struct platform_device *pdev)
> */
> pm_runtime_forbid(dev);
>
> - ret = phy_pipe_clk_register(qmp, np);
> - if (ret)
> - goto err_pm_put;
> + if (!qmp->cfg->fw_managed) {
> + ret = phy_pipe_clk_register(qmp, np);
> + if (ret)
> + goto err_pm_put;
> + }
>
> qmp->phy = devm_phy_create(dev, np, &qmp_usb_phy_ops);
> if (IS_ERR(qmp->phy)) {
> @@ -2492,6 +2627,9 @@ static const struct of_device_id qmp_usb_of_match_table[] = {
> }, {
> .compatible = "qcom,qdu1000-qmp-usb3-uni-phy",
> .data = &qdu1000_usb3_uniphy_cfg,
> + }, {
> + .compatible = "qcom,sa8255p-qmp-usb3-uni-phy",
> + .data = &sa8255p_usb3_uniphy_cfg,
> }, {
> .compatible = "qcom,sa8775p-qmp-usb3-uni-phy",
> .data = &sa8775p_usb3_uniphy_cfg,
>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Thanks,
Neil
prev parent reply other threads:[~2026-09-24 8:13 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-03 7:44 [PATCH v5 0/2] Add support for SA8255P in the qcom-qmp-usb phy driver Mattijs Korpershoek
2026-09-03 7:44 ` [PATCH v5 1/2] dt-bindings: phy: Add sa8255p USB PHY Mattijs Korpershoek
2026-09-07 8:39 ` Krzysztof Kozlowski
2026-09-03 7:44 ` [PATCH v5 2/2] phy: qcom: qmp-usb: Add support for SA8255P Mattijs Korpershoek
2026-09-24 8:13 ` Neil Armstrong [this message]
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=a72169ad-b1ae-4502-8997-4b86999b31eb@linaro.org \
--to=neil.armstrong@linaro.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-phy@lists.infradead.org \
--cc=mkorpershoek@kernel.org \
--cc=mripard@kernel.org \
--cc=quic_shazhuss@quicinc.com \
--cc=quic_sriramd@quicinc.com \
--cc=robh@kernel.org \
--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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®