The WCN39xx family of WiFi/BT chips incorporates a simple PMU, spreading
voltages over internal rails. Implement support for using powersequencer
for this family of ATH10k devices in addition to using regulators.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath10k/snoc.c | 43 +++++++++++++++++++++++++++++++---
drivers/net/wireless/ath/ath10k/snoc.h | 2 ++
2 files changed, 42 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
index b3f6424c17d3..919d4b0b87cd 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.c
+++ b/drivers/net/wireless/ath/ath10k/snoc.c
@@ -11,6 +11,7 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/property.h>
+#include <linux/pwrseq/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/remoteproc/qcom_rproc.h>
#include <linux/of_reserved_mem.h>
@@ -1023,9 +1024,15 @@ static int ath10k_hw_power_on(struct ath10k *ar)
ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power on\n");
+ if (ar_snoc->pwrseq) {
+ ret = pwrseq_power_on(ar_snoc->pwrseq);
+ if (ret)
+ return ret;
+ }
+
ret = regulator_bulk_enable(ar_snoc->num_vregs, ar_snoc->vregs);
if (ret)
- return ret;
+ goto pwrseq_off;
ret = clk_bulk_prepare_enable(ar_snoc->num_clks, ar_snoc->clks);
if (ret)
@@ -1035,18 +1042,28 @@ static int ath10k_hw_power_on(struct ath10k *ar)
vreg_off:
regulator_bulk_disable(ar_snoc->num_vregs, ar_snoc->vregs);
+pwrseq_off:
+ pwrseq_power_off(ar_snoc->pwrseq);
+
return ret;
}
static int ath10k_hw_power_off(struct ath10k *ar)
{
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+ int ret_seq = 0;
+ int ret_vreg;
ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power off\n");
clk_bulk_disable_unprepare(ar_snoc->num_clks, ar_snoc->clks);
- return regulator_bulk_disable(ar_snoc->num_vregs, ar_snoc->vregs);
+ ret_vreg = regulator_bulk_disable(ar_snoc->num_vregs, ar_snoc->vregs);
+
+ if (ar_snoc->pwrseq)
+ ret_seq = pwrseq_power_off(ar_snoc->pwrseq);
+
+ return ret_vreg ? : ret_seq;
}
static void ath10k_snoc_wlan_disable(struct ath10k *ar)
@@ -1762,7 +1779,27 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
goto err_release_resource;
}
- ar_snoc->num_vregs = ARRAY_SIZE(ath10k_regulators);
+ /*
+ * Backwards compatibility, ignore the defer error from pwrseq, if it
+ * should be used, we will get an error from regulator get.
+ */
+ ar_snoc->pwrseq = devm_pwrseq_get(&pdev->dev, "wlan");
+ if (IS_ERR(ar_snoc->pwrseq)) {
+ ret = PTR_ERR(ar_snoc->pwrseq);
+ ar_snoc->pwrseq = NULL;
+ if (ret != -EPROBE_DEFER)
+ goto err_free_irq;
+
+ ar_snoc->num_vregs = ARRAY_SIZE(ath10k_regulators);
+ } else {
+ /*
+ * The first regulator (vdd-0.8-cx-mx) is used to power on part
+ * of the SoC rather than the PMU on WCN399x, the rest are
+ * handled via pwrseq.
+ */
+ ar_snoc->num_vregs = 1;
+ }
+
ar_snoc->vregs = devm_kcalloc(&pdev->dev, ar_snoc->num_vregs,
sizeof(*ar_snoc->vregs), GFP_KERNEL);
if (!ar_snoc->vregs) {
diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h
index d4bce1707696..eeaa1c009cb0 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.h
+++ b/drivers/net/wireless/ath/ath10k/snoc.h
@@ -53,6 +53,7 @@ enum ath10k_snoc_flags {
};
struct clk_bulk_data;
+struct pwrseq_desc;
struct regulator_bulk_data;
struct ath10k_snoc {
@@ -73,6 +74,7 @@ struct ath10k_snoc {
struct ath10k_snoc_ce_irq ce_irqs[CE_COUNT_MAX];
struct ath10k_ce ce;
struct timer_list rx_post_retry;
+ struct pwrseq_desc *pwrseq;
struct regulator_bulk_data *vregs;
size_t num_vregs;
struct clk_bulk_data *clks;
--
2.47.3
On Wed, Dec 31, 2025 at 12:36 AM Dmitry Baryshkov
<dmitry.baryshkov@oss.qualcomm.com> wrote:
>
> The WCN39xx family of WiFi/BT chips incorporates a simple PMU, spreading
> voltages over internal rails. Implement support for using powersequencer
> for this family of ATH10k devices in addition to using regulators.
>
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> ---
[snip]
>
> static void ath10k_snoc_wlan_disable(struct ath10k *ar)
> @@ -1762,7 +1779,27 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
> goto err_release_resource;
> }
>
> - ar_snoc->num_vregs = ARRAY_SIZE(ath10k_regulators);
> + /*
> + * Backwards compatibility, ignore the defer error from pwrseq, if it
> + * should be used, we will get an error from regulator get.
> + */
Can you elaborate on this? I'm not exactly following. I suppose you
mean the regulator_get() will return -EPROBE_DEFER? One of the
supplies exposed by the PMU?
Bart
> + ar_snoc->pwrseq = devm_pwrseq_get(&pdev->dev, "wlan");
> + if (IS_ERR(ar_snoc->pwrseq)) {
> + ret = PTR_ERR(ar_snoc->pwrseq);
> + ar_snoc->pwrseq = NULL;
> + if (ret != -EPROBE_DEFER)
> + goto err_free_irq;
> +
> + ar_snoc->num_vregs = ARRAY_SIZE(ath10k_regulators);
> + } else {
> + /*
> + * The first regulator (vdd-0.8-cx-mx) is used to power on part
> + * of the SoC rather than the PMU on WCN399x, the rest are
> + * handled via pwrseq.
> + */
> + ar_snoc->num_vregs = 1;
> + }
> +
On Fri, 2 Jan 2026 at 13:07, Bartosz Golaszewski <brgl@kernel.org> wrote:
>
> On Wed, Dec 31, 2025 at 12:36 AM Dmitry Baryshkov
> <dmitry.baryshkov@oss.qualcomm.com> wrote:
> >
> > The WCN39xx family of WiFi/BT chips incorporates a simple PMU, spreading
> > voltages over internal rails. Implement support for using powersequencer
> > for this family of ATH10k devices in addition to using regulators.
> >
> > Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> > ---
>
> [snip]
>
> >
> > static void ath10k_snoc_wlan_disable(struct ath10k *ar)
> > @@ -1762,7 +1779,27 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
> > goto err_release_resource;
> > }
> >
> > - ar_snoc->num_vregs = ARRAY_SIZE(ath10k_regulators);
> > + /*
> > + * Backwards compatibility, ignore the defer error from pwrseq, if it
> > + * should be used, we will get an error from regulator get.
> > + */
>
> Can you elaborate on this? I'm not exactly following. I suppose you
> mean the regulator_get() will return -EPROBE_DEFER? One of the
> supplies exposed by the PMU?
Yes. devm_pwrseq_get() can return -EPROBE_DEFER in two cases:
- it is not supposed to be used
- it is supposed to be used, but the driver hasn't probed yet.
There is no simple way to distinguish between these two cases, but:
- if it is not supposed to be used, then regulator_bulk_get() will
return all regulators as expected, continuing the probe
- if it is supposed to be used, but wasn't probed yet, we will get
-EPROBE_DEFER from regulator_bulk_get() too.
I can write that in a comment, if you think that it makes the code more obvious.
>
> Bart
>
> > + ar_snoc->pwrseq = devm_pwrseq_get(&pdev->dev, "wlan");
> > + if (IS_ERR(ar_snoc->pwrseq)) {
> > + ret = PTR_ERR(ar_snoc->pwrseq);
> > + ar_snoc->pwrseq = NULL;
> > + if (ret != -EPROBE_DEFER)
> > + goto err_free_irq;
> > +
> > + ar_snoc->num_vregs = ARRAY_SIZE(ath10k_regulators);
> > + } else {
> > + /*
> > + * The first regulator (vdd-0.8-cx-mx) is used to power on part
> > + * of the SoC rather than the PMU on WCN399x, the rest are
> > + * handled via pwrseq.
> > + */
> > + ar_snoc->num_vregs = 1;
> > + }
> > +
--
With best wishes
Dmitry
On Fri, Jan 2, 2026 at 4:10 PM Dmitry Baryshkov
<dmitry.baryshkov@oss.qualcomm.com> wrote:
>
> On Fri, 2 Jan 2026 at 13:07, Bartosz Golaszewski <brgl@kernel.org> wrote:
> >
> > On Wed, Dec 31, 2025 at 12:36 AM Dmitry Baryshkov
> > <dmitry.baryshkov@oss.qualcomm.com> wrote:
> > >
> > > The WCN39xx family of WiFi/BT chips incorporates a simple PMU, spreading
> > > voltages over internal rails. Implement support for using powersequencer
> > > for this family of ATH10k devices in addition to using regulators.
> > >
> > > Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> > > ---
> >
> > [snip]
> >
> > >
> > > static void ath10k_snoc_wlan_disable(struct ath10k *ar)
> > > @@ -1762,7 +1779,27 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
> > > goto err_release_resource;
> > > }
> > >
> > > - ar_snoc->num_vregs = ARRAY_SIZE(ath10k_regulators);
> > > + /*
> > > + * Backwards compatibility, ignore the defer error from pwrseq, if it
> > > + * should be used, we will get an error from regulator get.
> > > + */
> >
> > Can you elaborate on this? I'm not exactly following. I suppose you
> > mean the regulator_get() will return -EPROBE_DEFER? One of the
> > supplies exposed by the PMU?
>
> Yes. devm_pwrseq_get() can return -EPROBE_DEFER in two cases:
> - it is not supposed to be used
> - it is supposed to be used, but the driver hasn't probed yet.
>
Yes but normally driver core would still create a devlink between the
device binding to the PMU node and the consumer of its regulators -
this device - so we can expect that it will always be the first one,
no? Unless we need this driver to be firmware-agnostic.
> There is no simple way to distinguish between these two cases, but:
> - if it is not supposed to be used, then regulator_bulk_get() will
> return all regulators as expected, continuing the probe
> - if it is supposed to be used, but wasn't probed yet, we will get
> -EPROBE_DEFER from regulator_bulk_get() too.
>
> I can write that in a comment, if you think that it makes the code more obvious.
>
Yes, please make it more descriptive. Ideally I'd like to improve the
API to avoid such confusion in the future.
Bartosz
> >
> > Bart
> >
> > > + ar_snoc->pwrseq = devm_pwrseq_get(&pdev->dev, "wlan");
> > > + if (IS_ERR(ar_snoc->pwrseq)) {
> > > + ret = PTR_ERR(ar_snoc->pwrseq);
> > > + ar_snoc->pwrseq = NULL;
> > > + if (ret != -EPROBE_DEFER)
> > > + goto err_free_irq;
> > > +
> > > + ar_snoc->num_vregs = ARRAY_SIZE(ath10k_regulators);
> > > + } else {
> > > + /*
> > > + * The first regulator (vdd-0.8-cx-mx) is used to power on part
> > > + * of the SoC rather than the PMU on WCN399x, the rest are
> > > + * handled via pwrseq.
> > > + */
> > > + ar_snoc->num_vregs = 1;
> > > + }
> > > +
>
>
>
> --
> With best wishes
> Dmitry
On Fri, Jan 02, 2026 at 06:06:51PM +0100, Bartosz Golaszewski wrote: > On Fri, Jan 2, 2026 at 4:10 PM Dmitry Baryshkov > <dmitry.baryshkov@oss.qualcomm.com> wrote: > > > > On Fri, 2 Jan 2026 at 13:07, Bartosz Golaszewski <brgl@kernel.org> wrote: > > > > > > On Wed, Dec 31, 2025 at 12:36 AM Dmitry Baryshkov > > > <dmitry.baryshkov@oss.qualcomm.com> wrote: > > > > > > > > The WCN39xx family of WiFi/BT chips incorporates a simple PMU, spreading > > > > voltages over internal rails. Implement support for using powersequencer > > > > for this family of ATH10k devices in addition to using regulators. > > > > > > > > Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> > > > > --- > > > > > > [snip] > > > > > > > > > > > static void ath10k_snoc_wlan_disable(struct ath10k *ar) > > > > @@ -1762,7 +1779,27 @@ static int ath10k_snoc_probe(struct platform_device *pdev) > > > > goto err_release_resource; > > > > } > > > > > > > > - ar_snoc->num_vregs = ARRAY_SIZE(ath10k_regulators); > > > > + /* > > > > + * Backwards compatibility, ignore the defer error from pwrseq, if it > > > > + * should be used, we will get an error from regulator get. > > > > + */ > > > > > > Can you elaborate on this? I'm not exactly following. I suppose you > > > mean the regulator_get() will return -EPROBE_DEFER? One of the > > > supplies exposed by the PMU? > > > > Yes. devm_pwrseq_get() can return -EPROBE_DEFER in two cases: > > - it is not supposed to be used > > - it is supposed to be used, but the driver hasn't probed yet. > > > > Yes but normally driver core would still create a devlink between the > device binding to the PMU node and the consumer of its regulators - fw_devlink, which are not mandatory, time out, etc. So, no, it is not granted that the PMU is always available during the probe. > this device - so we can expect that it will always be the first one, > no? Unless we need this driver to be firmware-agnostic. > > > There is no simple way to distinguish between these two cases, but: > > - if it is not supposed to be used, then regulator_bulk_get() will > > return all regulators as expected, continuing the probe > > - if it is supposed to be used, but wasn't probed yet, we will get > > -EPROBE_DEFER from regulator_bulk_get() too. > > > > I can write that in a comment, if you think that it makes the code more obvious. > > > > Yes, please make it more descriptive. Ideally I'd like to improve the > API to avoid such confusion in the future. The prolem is that we can't (or I don't see a way to). Power sequencing core has no way to distinguish these two cases. -- With best wishes Dmitry
On Sat, Jan 3, 2026 at 12:24 AM Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> wrote: > rite that in a comment, if you think that it makes the code more obvious. > > > > > > > Yes, please make it more descriptive. Ideally I'd like to improve the > > API to avoid such confusion in the future. > > The prolem is that we can't (or I don't see a way to). Power sequencing > core has no way to distinguish these two cases. > I know, it's by design, but if we ever make fw_devlink mandatory for all types of drivers (there was a discussion about this during LPC), we may be able to improve this. And with this comment improved: Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com> Bart
© 2016 - 2026 Red Hat, Inc.