Linux Power Management development
 help / color / mirror / Atom feed
* Re: [PATCH] cpuidle: Deny idle entry when CPU already have IPI interrupt pending
From: Rafael J. Wysocki @ 2026-03-24 16:07 UTC (permalink / raw)
  To: Maulik Shah (mkshah)
  Cc: Rafael J. Wysocki, Daniel Lezcano, Christian Loehle, Ulf Hansson,
	linux-pm, linux-kernel, linux-arm-msm
In-Reply-To: <fcb05fb8-ed4f-4df1-9aa2-18bc3320cad5@oss.qualcomm.com>

On Mon, Mar 23, 2026 at 1:13 PM Maulik Shah (mkshah)
<maulik.shah@oss.qualcomm.com> wrote:
>
>
>
> On 3/20/2026 11:59 PM, Rafael J. Wysocki wrote:
> > On Mon, Mar 16, 2026 at 8:38 AM Maulik Shah
> > <maulik.shah@oss.qualcomm.com> wrote:
> >>
> >> CPU can get IPI interrupt from another CPU while it is executing
> >> cpuidle_select() or about to execute same. The selection do not account
> >> for pending interrupts and may continue to enter selected idle state only
> >> to exit immediately.
> >>
> >> Example trace collected when there is cross CPU IPI.
> >>
> >>  [000] 154.892148: sched_waking: comm=sugov:4 pid=491 prio=-1 target_cpu=007
> >>  [000] 154.892148: ipi_raise: target_mask=00000000,00000080 (Function call interrupts)
> >>  [007] 154.892162: cpu_idle: state=2 cpu_id=7
> >>  [007] 154.892208: cpu_idle: state=4294967295 cpu_id=7
> >>  [007] 154.892211: irq_handler_entry: irq=2 name=IPI
> >>  [007] 154.892211: ipi_entry: (Function call interrupts)
> >>  [007] 154.892213: sched_wakeup: comm=sugov:4 pid=491 prio=-1 target_cpu=007
> >>  [007] 154.892214: ipi_exit: (Function call interrupts)
> >>
> >> This impacts performance and the above count increments.
> >>
> >> commit ccde6525183c ("smp: Introduce a helper function to check for pending
> >> IPIs") already introduced a helper function to check the pending IPIs and
> >> it is used in pmdomain governor to deny the cluster level idle state when
> >> there is a pending IPI on any of cluster CPUs.
> >
> > You seem to be overlooking the fact that resched wakeups need not be
> > signaled via IPIs, but they may be updates of a monitored cache line.
> >
> >> This however does not stop CPU to enter CPU level idle state. Make use of
> >> same at CPUidle to deny the idle entry when there is already IPI pending.
> >>
> >> With change observing glmark2 [1] off screen scores improving in the range
> >> of 25% to 30% on Qualcomm lemans-evk board which is arm64 based having two
> >> clusters each with 4 CPUs.
> >>
> >> [1] https://github.com/glmark2/glmark2
> >>
> >> Signed-off-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
> >> ---
> >>  drivers/cpuidle/cpuidle.c | 3 +++
> >>  1 file changed, 3 insertions(+)
> >>
> >> diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
> >> index c7876e9e024f9076663063ad21cfc69343fdbbe7..c88c0cbf910d6c2c09697e6a3ac78c081868c2ad 100644
> >> --- a/drivers/cpuidle/cpuidle.c
> >> +++ b/drivers/cpuidle/cpuidle.c
> >> @@ -224,6 +224,9 @@ noinstr int cpuidle_enter_state(struct cpuidle_device *dev,
> >>         bool broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP);
> >>         ktime_t time_start, time_end;
> >>
> >> +       if (cpus_peek_for_pending_ipi(drv->cpumask))
> >> +               return -EBUSY;
> >> +
> >
> > So what if the driver handles all CPUs in the system and there are
> > many of them (say ~500) and if IPIs occur rarely (because resched
> > events are not IPIs)?
>
> Missed the case of driver handling multiple CPUs,
> In v2 would fix this as below, which checks pending IPI on single
> CPU trying to enter idle.
>
>      if (cpus_peek_for_pending_ipi(cpumask_of(dev->cpu)))

And the for_each_cpu() loop in cpus_peek_for_pending_ipi() would then
become useless overhead, wouldn't ir?

> I see IPIs do occur often, in the glmark2 offscreen case
> mentioned in commit text, out of total ~12.2k IPIs across all 8 CPUs,
> ~9.6k are function call IPIs, ~2k are IRQ work IPIs, ~560 Timer broadcast
> IPIs while rescheduling IPIs are only 82.

So how many of those IPIs actually wake up CPUs from idle prematurely?

^ permalink raw reply

* [PATCH] dt-bindings: reset: st: convert to dtschema
From: Gopi Krishna Menon @ 2026-03-24 15:59 UTC (permalink / raw)
  To: sre, robh, krzk+dt, lee, conor+dt
  Cc: Gopi Krishna Menon, daniel.baluta, simona.toaca, d-gole,
	m-chawdhry, linux-pm, devicetree, linux-kernel

Convert the STiH4xx reset controller bindings to DT schema.

Suggested-by: Daniel Baluta <daniel.baluta@nxp.com>
Suggested-by: Dhruva Gole <d-gole@ti.com>
Signed-off-by: Gopi Krishna Menon <krishnagopi487@gmail.com>
---
Note:
* This patch is part of the GSoC2026 application process for device tree bindings conversions
* https://github.com/LinuxFoundationGSoC/ProjectIdeas/wiki/GSoC-2026-Device-Tree-Bindings

 .../power/reset/st,stih407-restart.yaml       | 31 +++++++++++++++++++
 .../bindings/power/reset/st-reset.txt         | 11 -------
 2 files changed, 31 insertions(+), 11 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/power/reset/st,stih407-restart.yaml
 delete mode 100644 Documentation/devicetree/bindings/power/reset/st-reset.txt

diff --git a/Documentation/devicetree/bindings/power/reset/st,stih407-restart.yaml b/Documentation/devicetree/bindings/power/reset/st,stih407-restart.yaml
new file mode 100644
index 000000000000..d7adbc00f5c3
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/reset/st,stih407-restart.yaml
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/reset/st,stih407-restart.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ST SW reset controller
+
+maintainers:
+  - Lee Jones <lee@kernel.org>
+
+properties:
+  compatible:
+    const: st,stih407-restart
+
+  st,syscfg:
+    description: phandle of the syscfg node
+    $ref: /schemas/types.yaml#/definitions/phandle
+
+required:
+  - compatible
+  - st,syscfg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    reset {
+      compatible = "st,stih407-restart";
+      st,syscfg = <&syscfg_sbc_reg>;
+    };
diff --git a/Documentation/devicetree/bindings/power/reset/st-reset.txt b/Documentation/devicetree/bindings/power/reset/st-reset.txt
deleted file mode 100644
index b63948737d80..000000000000
--- a/Documentation/devicetree/bindings/power/reset/st-reset.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-*Device-Tree bindings for ST SW reset functionality
-
-Required properties:
-- compatible: should be "stih407-restart".
-- st,syscfg: should be a phandle of the syscfg node.
-
-Example node:
-	restart {
-		compatible = "st,stih407-restart";
-		st,syscfg = <&syscfg_sbc_reg>;
-	};
-- 
2.52.0


^ permalink raw reply related

* Re: [PATCH] cpuidle: Deny idle entry when CPU already have IPI interrupt pending
From: Ulf Hansson @ 2026-03-24 15:46 UTC (permalink / raw)
  To: Maulik Shah
  Cc: Rafael J. Wysocki, Daniel Lezcano, Christian Loehle, linux-pm,
	linux-kernel, linux-arm-msm
In-Reply-To: <20260316-cpuidle_ipi-v1-1-d0ff6350f4e2@oss.qualcomm.com>

On Mon, 16 Mar 2026 at 08:38, Maulik Shah <maulik.shah@oss.qualcomm.com> wrote:
>
> CPU can get IPI interrupt from another CPU while it is executing
> cpuidle_select() or about to execute same. The selection do not account
> for pending interrupts and may continue to enter selected idle state only
> to exit immediately.
>
> Example trace collected when there is cross CPU IPI.
>
>  [000] 154.892148: sched_waking: comm=sugov:4 pid=491 prio=-1 target_cpu=007
>  [000] 154.892148: ipi_raise: target_mask=00000000,00000080 (Function call interrupts)
>  [007] 154.892162: cpu_idle: state=2 cpu_id=7
>  [007] 154.892208: cpu_idle: state=4294967295 cpu_id=7
>  [007] 154.892211: irq_handler_entry: irq=2 name=IPI
>  [007] 154.892211: ipi_entry: (Function call interrupts)
>  [007] 154.892213: sched_wakeup: comm=sugov:4 pid=491 prio=-1 target_cpu=007
>  [007] 154.892214: ipi_exit: (Function call interrupts)
>
> This impacts performance and the above count increments.
>
> commit ccde6525183c ("smp: Introduce a helper function to check for pending
> IPIs") already introduced a helper function to check the pending IPIs and
> it is used in pmdomain governor to deny the cluster level idle state when
> there is a pending IPI on any of cluster CPUs.
>
> This however does not stop CPU to enter CPU level idle state. Make use of
> same at CPUidle to deny the idle entry when there is already IPI pending.
>
> With change observing glmark2 [1] off screen scores improving in the range
> of 25% to 30% on Qualcomm lemans-evk board which is arm64 based having two
> clusters each with 4 CPUs.
>
> [1] https://github.com/glmark2/glmark2
>
> Signed-off-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
> ---
>  drivers/cpuidle/cpuidle.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
> index c7876e9e024f9076663063ad21cfc69343fdbbe7..c88c0cbf910d6c2c09697e6a3ac78c081868c2ad 100644
> --- a/drivers/cpuidle/cpuidle.c
> +++ b/drivers/cpuidle/cpuidle.c
> @@ -224,6 +224,9 @@ noinstr int cpuidle_enter_state(struct cpuidle_device *dev,
>         bool broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP);
>         ktime_t time_start, time_end;
>
> +       if (cpus_peek_for_pending_ipi(drv->cpumask))
> +               return -EBUSY;

As other reviews already pointed out, this must be called only for the
current CPU.

That said, did you play with bailing out just before the call to the
target_state->enter()? It would be interesting to know if that changes
the "stats" somehow.

> +
>         instrumentation_begin();
>
>         /*
>
> ---
> base-commit: b84a0ebe421ca56995ff78b66307667b62b3a900
> change-id: 20260316-cpuidle_ipi-4c64036f9a48
>
> Best regards,
> --
> Maulik Shah <maulik.shah@oss.qualcomm.com>
>

Kind regards
Uffe

^ permalink raw reply

* [GIT PULL] interconnect fix for 7.0-rc
From: Georgi Djakov @ 2026-03-24 15:25 UTC (permalink / raw)
  To: gregkh; +Cc: linux-pm, linux-kernel, djakov

Hello Greg,

This pull request contains one driver fix for v7.0. The details are
in the signed tag. It have been also in linux-next for a week. Please
pull into char-misc-linus when you get a chance.

Thanks,
Georgi

The following changes since commit 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f:

  Linux 7.0-rc1 (2026-02-22 13:18:59 -0800)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/djakov/icc.git tags/icc-7.0-rc6

for you to fetch changes up to dbbd550d7c8d90d3af9fe8a12a9caff077ddb8e3:

  interconnect: qcom: sm8450: Fix NULL pointer dereference in icc_link_nodes() (2026-03-14 12:53:13 +0200)

----------------------------------------------------------------
interconnect fix for v7.0-rc

This contains one driver fix for the current cycle.

- interconnect: qcom: sm8450: Fix NULL pointer dereference in icc_link_nodes()

Signed-off-by: Georgi Djakov <djakov@kernel.org>

----------------------------------------------------------------
Vladimir Zapolskiy (1):
      interconnect: qcom: sm8450: Fix NULL pointer dereference in icc_link_nodes()

 drivers/interconnect/qcom/sm8450.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

^ permalink raw reply

* Re: [PATCH 15/15] sched/cputime: Handle dyntick-idle steal time correctly
From: Frederic Weisbecker @ 2026-03-24 14:53 UTC (permalink / raw)
  To: Shrikanth Hegde
  Cc: LKML, Christophe Leroy (CS GROUP), Rafael J. Wysocki,
	Alexander Gordeev, Anna-Maria Behnsen, Ben Segall, Boqun Feng,
	Christian Borntraeger, Dietmar Eggemann, Heiko Carstens,
	Ingo Molnar, Jan Kiszka, Joel Fernandes, Juri Lelli,
	Kieran Bingham, Madhavan Srinivasan, Mel Gorman, Michael Ellerman,
	Neeraj Upadhyay, Nicholas Piggin, Paul E . McKenney,
	Peter Zijlstra, Steven Rostedt, Sven Schnelle, Thomas Gleixner,
	Uladzislau Rezki, Valentin Schneider, Vasily Gorbik,
	Vincent Guittot, Viresh Kumar, Xin Zhao, linux-pm, linux-s390,
	linuxppc-dev
In-Reply-To: <be30e259-a690-4530-996c-0c092833bb66@linux.ibm.com>

Le Tue, Mar 03, 2026 at 04:47:45PM +0530, Shrikanth Hegde a écrit :
> 
> 
> On 2/6/26 7:52 PM, Frederic Weisbecker wrote:
> > The dyntick-idle steal time is currently accounted when the tick
> > restarts but the stolen idle time is not substracted from the idle time
> > that was already accounted. This is to avoid observing the idle time
> > going backward as the dyntick-idle cputime accessors can't reliably know
> > in advance the stolen idle time.
> > 
> > In order to maintain a forward progressing idle cputime while
> > substracting idle steal time from it, keep track of the previously
> > accounted idle stolen time and substract it from _later_ idle cputime
> > accounting.
> > 
> 
> s/substract/subtract ?

Right.

> 
> > Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
> > ---
> >   include/linux/kernel_stat.h |  1 +
> >   kernel/sched/cputime.c      | 21 +++++++++++++++------
> >   2 files changed, 16 insertions(+), 6 deletions(-)
> > 
> > diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h
> > index 512104b0ff49..24a54a6151ba 100644
> > --- a/include/linux/kernel_stat.h
> > +++ b/include/linux/kernel_stat.h
> > @@ -39,6 +39,7 @@ struct kernel_cpustat {
> >   	bool		idle_elapse;
> >   	seqcount_t	idle_sleeptime_seq;
> >   	u64		idle_entrytime;
> > +	u64		idle_stealtime;
> >   #endif
> >   	u64		cpustat[NR_STATS];
> >   };
> > diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
> > index 92fa2f037b6e..7e79288eb327 100644
> > --- a/kernel/sched/cputime.c
> > +++ b/kernel/sched/cputime.c
> > @@ -424,19 +424,25 @@ static inline void irqtime_account_process_tick(struct task_struct *p, int user_
> >   static void kcpustat_idle_stop(struct kernel_cpustat *kc, u64 now)
> >   {
> >   	u64 *cpustat = kc->cpustat;
> > -	u64 delta;
> > +	u64 delta, steal, steal_delta;
> >   	if (!kc->idle_elapse)
> >   		return;
> >   	delta = now - kc->idle_entrytime;
> > +	steal = steal_account_process_time(delta);
> >   	write_seqcount_begin(&kc->idle_sleeptime_seq);
> > +	steal_delta = min_t(u64, kc->idle_stealtime, delta);
> > +	delta -= steal_delta;
> 
> I didn;t get this logic. Why do we need idle_stealtime?
> 
> Lets say 10ms was steal time and 50ms was delta. but idle_stealtime is
> sum of past accumulated steal time. we only need to subtract steal time there no?
> 
> Shouldn't this be delta -= steal ?

That would be a risk to observe backward idle accounting:

Time        CPU 0                                  CPU 1
----        -----                                  -----
0 sec       kcpustat_idle_start()
            <#VMEXIT>
            ...
1 sec       </#VMEXIT>                             
            arch_cpu_idle()                        // returns 2
2 sec       kcpustat_idle_stop()                   kcpustat_field(CPUTIME_IDLE, 0)
               cpustat[CPUTIME_IDLE] = 2 - 1
                                                   // returns 1
                                                   kcpustat_field(CPUTIME_IDLE, 0)

We could instead read remotely the paravirt clock, but then
steal_account_process_time() would need to always hold the ->idle_sleeptime_seq,
though it should happen to work without given the ordering.

Anyway to avoid any surprise I accumulate the steal time of an idle cycle to be
substracted on the next idle cycle.

Thanks.

-- 
Frederic Weisbecker
SUSE Labs

^ permalink raw reply

* Re: [PATCH v2 00/19] tracepoint: Avoid double static_branch evaluation at guarded call sites
From: Steven Rostedt @ 2026-03-24 14:28 UTC (permalink / raw)
  To: Vineeth Pillai (Google)
  Cc: Peter Zijlstra, Dmitry Ilvokhin, Masami Hiramatsu,
	Mathieu Desnoyers, Ingo Molnar, Jens Axboe, io-uring,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Alexei Starovoitov, Daniel Borkmann, Marcelo Ricardo Leitner,
	Xin Long, Jon Maloy, Aaron Conole, Eelco Chaudron, Ilya Maximets,
	netdev, bpf, linux-sctp, tipc-discussion, dev, Jiri Pirko,
	Oded Gabbay, Koby Elbaz, dri-devel, Rafael J. Wysocki,
	Viresh Kumar, Gautham R. Shenoy, Huang Rui, Mario Limonciello,
	Len Brown, Srinivas Pandruvada, linux-pm, MyungJoo Ham,
	Kyungmin Park, Chanwoo Choi, Christian König, Sumit Semwal,
	linaro-mm-sig, Eddie James, Andrew Jeffery, Joel Stanley,
	linux-fsi, David Airlie, Simona Vetter, Alex Deucher,
	Danilo Krummrich, Matthew Brost, Philipp Stanner, Harry Wentland,
	Leo Li, amd-gfx, Jiri Kosina, Benjamin Tissoires, linux-input,
	Wolfram Sang, linux-i2c, Mark Brown, Michael Hennerich,
	Nuno Sá, linux-spi, James E.J. Bottomley, Martin K. Petersen,
	linux-scsi, Chris Mason, David Sterba, linux-btrfs,
	Thomas Gleixner, Andrew Morton, SeongJae Park, linux-mm,
	Borislav Petkov, Dave Hansen, x86, linux-trace-kernel,
	linux-kernel
In-Reply-To: <20260323160052.17528-1-vineeth@bitbyteword.org>

On Mon, 23 Mar 2026 12:00:19 -0400
"Vineeth Pillai (Google)" <vineeth@bitbyteword.org> wrote:

> When a caller already guards a tracepoint with an explicit enabled check:
> 
>   if (trace_foo_enabled() && cond)
>       trace_foo(args);

Thanks Vineeth!

I'm going to start pulling in this series. I'll take the first patch, and
then any patch that has an Acked-by or Reviewed-by from the maintainer.

For patches without acks, I'll leave alone and then after the first patch
gets merged into mainline, the maintainers could pull in their own patches
at their own convenience. Unless of course they speak up now if they want
me to take them ;-)

-- Steve

^ permalink raw reply

* Re: [PATCH v6 3/9] cxl/region: Skip decoder reset on detach for autodiscovered regions
From: Alejandro Lucero Palau @ 2026-03-24 14:06 UTC (permalink / raw)
  To: Alison Schofield, Dan Williams
  Cc: Smita Koralahalli, linux-cxl, linux-kernel, nvdimm, linux-fsdevel,
	linux-pm, Ard Biesheuvel, Vishal Verma, Ira Weiny,
	Jonathan Cameron, Yazen Ghannam, Dave Jiang, Davidlohr Bueso,
	Matthew Wilcox, Jan Kara, Rafael J . Wysocki, Len Brown,
	Pavel Machek, Li Ming, Jeff Johnson, Ying Huang, Yao Xingtao,
	Peter Zijlstra, Greg Kroah-Hartman, Nathan Fontenot, Terry Bowman,
	Robert Richter, Benjamin Cheatham, Zhijian Li, Borislav Petkov,
	Tomasz Wolski
In-Reply-To: <absY10LzUqb3vK7A@aschofie-mobl2.lan>


On 3/18/26 21:27, Alison Schofield wrote:

<snip>

> As a step in the direction you suggest, AND  aiming to address Type2
> need, here is what I'd like a direction check on:
>
> Start separating decode-reset policy rom CXL_REGION_F_AUTO:
> - keep CXL_REGION_F_AUTO as origin / assembly semantics
> - introduce CXL_REGION_F_PRESERVE_DECODE as a region-scoped policy
> - initialize that policy from auto-assembly
> - clear it on explicit decommit in commit_store()
> - use it to gate cxl_region_decode_reset() in __cxl_decoder_detach()
>
> The decode-reset decision is factored through a small helper,
> cxl_region_preserve_decode(), so the policy can be extended independent
> of the detach mechanics. Maybe overkill in this simple case, but I
> wanted to acknowledge the 'policy' direction.


I like this approach which separates AUTO flag from this need.


>
> Compiled but not yet tested, pending a direction check:


I have tested it using the Type2 v24 and adding some debug lines for 
seeing the proper flag check works when decoder detach.


Maybe there are some other aspects of this approach I can not envision, 
but I'm happy with this change for current Type2 needs. Hopefully this 
plus v24 can go through before the next kernel window closes.


Thank you,

Alejandro


>
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 42874948b589..f99e4aca72f0 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -432,6 +432,12 @@ static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
>          if (rc)
>                  return rc;
>
> +       /*
> +        * Explicit decommit is destructive. Clear preserve bit before
> +        * unbinding so detach paths do not skip decoder reset.
> +        */
> +       clear_bit(CXL_REGION_F_PRESERVE_DECODE, &cxlr->flags);
> +
>          /*
>           * Unmap the region and depend the reset-pending state to ensure
>           * it does not go active again until post reset
> @@ -2153,6 +2159,12 @@ static int cxl_region_attach(struct cxl_region *cxlr,
>          return 0;
>   }
>
> +/* Region-scoped policy for preserving decoder programming across detach */
> +static bool cxl_region_preserve_decode(struct cxl_region *cxlr)
> +{
> +       return test_bit(CXL_REGION_F_PRESERVE_DECODE, &cxlr->flags);
> +}
> +
>   static struct cxl_region *
>   __cxl_decoder_detach(struct cxl_region *cxlr,
>                       struct cxl_endpoint_decoder *cxled, int pos,
> @@ -2185,7 +2197,8 @@ __cxl_decoder_detach(struct cxl_region *cxlr,
>                  cxled->part = -1;
>
>          if (p->state > CXL_CONFIG_ACTIVE) {
> -               cxl_region_decode_reset(cxlr, p->interleave_ways);
> +               if (!cxl_region_preserve_decode(cxlr))
> +                       cxl_region_decode_reset(cxlr, p->interleave_ways);
>                  p->state = CXL_CONFIG_ACTIVE;
>          }
>
> @@ -3833,6 +3846,7 @@ static int __construct_region(struct cxl_region *cxlr,
>          }
>
>          set_bit(CXL_REGION_F_AUTO, &cxlr->flags);
> +       set_bit(CXL_REGION_F_PRESERVE_DECODE, &cxlr->flags);
>          cxlr->hpa_range = *hpa_range;
>
>          res = kmalloc_obj(*res);
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 9b947286eb9b..e6fbbee37252 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -532,6 +532,16 @@ enum cxl_partition_mode {
>    */
>   #define CXL_REGION_F_NORMALIZED_ADDRESSING 3
>
> +/*
> + * Indicate that decoder programming should be preserved when endpoint
> + * decoders detach from this region. This allows region decode state to
> + * survive endpoint removal and be recovered by subsequent enumeration.
> + * Automatic assembly may set this flag, and future userspace control
> + * may allow it to be set explicitly. Explicit region decommit should
> + * clear this flag before destructive cleanup.
> + */
> +#define CXL_REGION_F_PRESERVE_DECODE 4
> +
>   /**
>    * struct cxl_region - CXL region
>    * @dev: This region's device
>
>
>

^ permalink raw reply

* Re: [PATCH 2/2] ARM: dts: st: spear: fix dtbs warning on spear thermal sensor
From: Gopi Krishna Menon @ 2026-03-24 13:34 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Daniel Baluta, rafael, daniel.lezcano, rui.zhang, lukasz.luba,
	robh, krzk+dt, vireshk, conor+dt, linux-pm, devicetree,
	linux-kernel, linux-arm-kernel, soc, simona.toaca, d-gole,
	m-chawdhry
In-Reply-To: <acKEJrB6vc9wfd1k@toolbx>

On Tue, Mar 24, 2026 at 07:00:49PM +0530, Gopi Krishna Menon wrote:

> On Tue, Mar 24, 2026 at 11:01:05AM +0100, Krzysztof Kozlowski wrote:
> 
> > On 24/03/2026 11:00, Daniel Baluta wrote:
> > > On 3/24/26 11:26, Krzysztof Kozlowski wrote:
> > >> On Mon, Mar 23, 2026 at 07:08:09PM +0530, Gopi Krishna Menon wrote:
> > >>> Running DTBS checks on st/spear1340-evb.dtb results in the following
> > >>> warning:
> > >>>
> > >>> thermal@e07008c4 (st,thermal-spear1340): Unevaluated properties are not allowed ('thermal_flags' was unexpected)
> > >>>   from schema $id: http://devicetree.org/schemas/thermal/st,thermal-spear1340.yaml
> > >> How is it possible if there is no such file?
> > >>
> > >> Did you just add new warning in patch #1 and then claim in patch #2 that
> > >> you fix it?
> > >>
> > >> You completely miss the point why this change is needed: how could the
> > >> DTS work before? It could not. And that should be your justification for
> > >> the patch, with explanation why it could not work.
> > > 
> > > Correct me if I'm wrong but I think there was a hidden bug here 
> > > 
> > > drivers/thermal/spear_thermal.c:spear_thermal_probe:
> > > 
> > >       if (!np || !of_property_read_u32(np, "st,thermal-flags", &val)) {
> > > »       »       dev_err(&pdev->dev, "Failed: DT Pdata not passed\n");                                                                                                                         
> > > »       »       return -EINVAL;
> > > »       }
> > > 
> > > So, the driver was checking for the correct property as pointed by 
> > 
> > Yes
> > 
> > > 
> > > bindings/thermal/spear-thermal.txt but the dts was using the wrong
> > 
> > No, DTS had two properties - correct one and incorrect.
> > 
> 
> Yup, spear13xx is included by spear1310.dtsi and spear1340.dtsi both of
> which have st,thermal-flags correctly defined. When working on this
> patch, after converting the binding, I ran dtbs check and upon seeing
> dtbs warning immediately fixed it thinking it was complaining because
> st,thermal-flags (thermal_flags) was written incorrectly. (Which is not
> incorrect as st,thermal-flags was there in the final node but there was one

I meant 'incorrect' here not 'not incorrect'

> additional property named thermal_flags as well which is not allowed as
> UnevaluatedProperty is set to false). 
> 
> Also I dont think we have to override the st,thermal-flags property in
> spear1310.dtsi as the flags value is same in spear13xx.dtsi and
> spear1310.dtsi (0x7000).
> 
> > > 
> > > property name: arch/arm/boot/dts/st/spear13xx.dtsi » » » thermal@e07008c4 { » » » » compatible = "st,thermal-spear1340"; » » » » reg = <0xe07008c4 0x4>; » » » » thermal_flags = <0x7000>; » » » }; And because this check is wrong:
> > > 
> > >       if (!np || !of_property_read_u32(np, "st,thermal-flags", &val)) {
> > > 
> > > people really didn't notice it.
> > > 
> > > The check should be:
> > > 
> > >       if (!np || of_property_read_u32(np, "st,thermal-flags", &val)) {
> > > »       »       dev_err(&pdev->dev, "Failed: DT Pdata not passed\n");                                                                                                                         
> > > »       »       return -EINVAL;
> > > »       }
> > > 
> > > So, this actual patch has uncovered a bug! 
> > 
> > Yes. Driver also has bug, so probably was never working. The point is
> > whatever commit is doing, the dtbs_check warning is not the
> > justification, because it was introduced by this patchset.
> > 
> 
> Please correct me if I am wrong Krzysztof but should i send a seperate patch
> fixing the thermal_flags property (Not connected to these converted
> bindings)?.
> 
> Also, is it necessary to set additionalProperties to true? I am not able to
> figure out if that is needed.
> 
> > Best regards,
> > Krzysztof
> 
> Thanks,
> Gopi Krishna Menon

^ permalink raw reply

* Re: [PATCH 2/2] ARM: dts: st: spear: fix dtbs warning on spear thermal sensor
From: Gopi Krishna Menon @ 2026-03-24 13:30 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Daniel Baluta, rafael, daniel.lezcano, rui.zhang, lukasz.luba,
	robh, krzk+dt, vireshk, conor+dt, linux-pm, devicetree,
	linux-kernel, linux-arm-kernel, soc, simona.toaca, d-gole,
	m-chawdhry
In-Reply-To: <5a9f84fe-5827-48e7-8e4e-699cf8ae4776@kernel.org>

On Tue, Mar 24, 2026 at 11:01:05AM +0100, Krzysztof Kozlowski wrote:

> On 24/03/2026 11:00, Daniel Baluta wrote:
> > On 3/24/26 11:26, Krzysztof Kozlowski wrote:
> >> On Mon, Mar 23, 2026 at 07:08:09PM +0530, Gopi Krishna Menon wrote:
> >>> Running DTBS checks on st/spear1340-evb.dtb results in the following
> >>> warning:
> >>>
> >>> thermal@e07008c4 (st,thermal-spear1340): Unevaluated properties are not allowed ('thermal_flags' was unexpected)
> >>>   from schema $id: http://devicetree.org/schemas/thermal/st,thermal-spear1340.yaml
> >> How is it possible if there is no such file?
> >>
> >> Did you just add new warning in patch #1 and then claim in patch #2 that
> >> you fix it?
> >>
> >> You completely miss the point why this change is needed: how could the
> >> DTS work before? It could not. And that should be your justification for
> >> the patch, with explanation why it could not work.
> > 
> > Correct me if I'm wrong but I think there was a hidden bug here 
> > 
> > drivers/thermal/spear_thermal.c:spear_thermal_probe:
> > 
> >       if (!np || !of_property_read_u32(np, "st,thermal-flags", &val)) {
> > »       »       dev_err(&pdev->dev, "Failed: DT Pdata not passed\n");                                                                                                                         
> > »       »       return -EINVAL;
> > »       }
> > 
> > So, the driver was checking for the correct property as pointed by 
> 
> Yes
> 
> > 
> > bindings/thermal/spear-thermal.txt but the dts was using the wrong
> 
> No, DTS had two properties - correct one and incorrect.
> 

Yup, spear13xx is included by spear1310.dtsi and spear1340.dtsi both of
which have st,thermal-flags correctly defined. When working on this
patch, after converting the binding, I ran dtbs check and upon seeing
dtbs warning immediately fixed it thinking it was complaining because
st,thermal-flags (thermal_flags) was written incorrectly. (Which is not
incorrect as st,thermal-flags was there in the final node but there was one
additional property named thermal_flags as well which is not allowed as
UnevaluatedProperty is set to false). 

Also I dont think we have to override the st,thermal-flags property in
spear1310.dtsi as the flags value is same in spear13xx.dtsi and
spear1310.dtsi (0x7000).

> > 
> > property name: arch/arm/boot/dts/st/spear13xx.dtsi » » » thermal@e07008c4 { » » » » compatible = "st,thermal-spear1340"; » » » » reg = <0xe07008c4 0x4>; » » » » thermal_flags = <0x7000>; » » » }; And because this check is wrong:
> > 
> >       if (!np || !of_property_read_u32(np, "st,thermal-flags", &val)) {
> > 
> > people really didn't notice it.
> > 
> > The check should be:
> > 
> >       if (!np || of_property_read_u32(np, "st,thermal-flags", &val)) {
> > »       »       dev_err(&pdev->dev, "Failed: DT Pdata not passed\n");                                                                                                                         
> > »       »       return -EINVAL;
> > »       }
> > 
> > So, this actual patch has uncovered a bug! 
> 
> Yes. Driver also has bug, so probably was never working. The point is
> whatever commit is doing, the dtbs_check warning is not the
> justification, because it was introduced by this patchset.
> 

Please correct me if I am wrong Krzysztof but should i send a seperate patch
fixing the thermal_flags property (Not connected to these converted
bindings)?.

Also, is it necessary to set additionalProperties to true? I am not able to
figure out if that is needed.

> Best regards,
> Krzysztof

Thanks,
Gopi Krishna Menon

^ permalink raw reply

* Re: [PATCH v5 00/21] Virtual Swap Space
From: Askar Safin @ 2026-03-24 13:19 UTC (permalink / raw)
  To: nphamcs
  Cc: Liam.Howlett, akpm, apopple, axelrasmussen, baohua, baolin.wang,
	bhe, byungchul, cgroups, chengming.zhou, chrisl, corbet, david,
	dev.jain, gourry, hannes, hughd, jannh, joshua.hahnjy, kasong,
	kernel-team, lance.yang, lenb, linux-doc, linux-kernel, linux-mm,
	linux-pm, lorenzo.stoakes, matthew.brost, mhocko, muchun.song,
	npache, pavel, peterx, peterz, pfalcato, rafael, rakie.kim, riel,
	roman.gushchin, rppt, ryan.roberts, shakeel.butt, shikemeng,
	surenb, tglx, vbabka, weixugc, ying.huang, yosry.ahmed, yuanchu,
	zhengqi.arch, ziy, Kairui Song, Matthew Wilcox
In-Reply-To: <20260320192735.748051-1-nphamcs@gmail.com>

Nhat Pham <nphamcs@gmail.com>:
> We can even perform compressed writeback
> (i.e writing these pages without decompressing them) (see [12]).

> [12]: https://lore.kernel.org/linux-mm/ZeZSDLWwDed0CgT3@casper.infradead.org/

This is supported in zram. The support was added here:
https://lore.kernel.org/all/20251201094754.4149975-1-senozhatsky@chromium.org/ .
It is already in mainline.

-- 
Askar Safin

^ permalink raw reply

* Re: [PATCH v8 1/2] mm/swap, PM: hibernate: fix swapoff race in uswsusp by pinning swap device
From: YoungJun Park @ 2026-03-24 12:48 UTC (permalink / raw)
  To: Kairui Song
  Cc: Rafael J . Wysocki, Andrew Morton, Chris Li, Kairui Song,
	Pavel Machek, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Usama Arif, linux-pm, linux-mm
In-Reply-To: <acH_svpUwaNtFeJN@KASONG-MC4>

On Tue, Mar 24, 2026 at 01:53:33PM +0800, Kairui Song wrote:
> On Tue, Mar 24, 2026 at 01:08:21AM +0800, Youngjun Park wrote:
> > Hibernation via uswsusp (/dev/snapshot ioctls) has a race window:
> > after selecting the resume swap area but before user space is frozen,
> > swapoff may run and invalidate the selected swap device.
> > 
> > Fix this by pinning the swap device with SWP_HIBERNATION while it is
> > in use.  The pin is exclusive, which is sufficient since
> > hibernate_acquire() already prevents concurrent hibernation sessions.
> > 
> > The kernel swsusp path (sysfs-based hibernate/resume) uses
> > find_hibernation_swap_type() which is not affected by the pin.  It
> > freezes user space before touching swap, so swapoff cannot race.
> > 
> > Introduce dedicated helpers:
> > - pin_hibernation_swap_type(): Look up and pin the swap device.
> >   Used by the uswsusp path.
> > - find_hibernation_swap_type(): Lookup without pinning.
> >   Used by the kernel swsusp path.
> > - unpin_hibernation_swap_type(): Clear the hibernation pin.
> 
> Looks good to me, thanks!
> 
> Reviewed-by: Kairui Song <kasong@tencent.com>

Thanks for the review, Kairui, and for all your feedback throughout
the revisions!

> Just one trivial nit picks below.

> > +/**
> > + * unpin_hibernation_swap_type - Unpin the swap device for hibernation
> > + * @type: Swap type previously returned by pin_hibernation_swap_type()
> > + *
> > + * Clear the hibernation pin on the given swap device, allowing
> > + * swapoff() to proceed normally.
> > + *
> > + * If @type does not refer to a valid swap device, this function
> > + * does nothing.
> > + */
> > +void unpin_hibernation_swap_type(int type)
> > +{
> > +	struct swap_info_struct *si;
> > +
> > +	spin_lock(&swap_lock);
> > +	si = swap_type_to_info(type);
> > +	if (!si) {
> > +		spin_unlock(&swap_lock);
> > +		return;
> > +	}
> > +	si->flags &= ~SWP_HIBERNATION;
> 
> Will the code will be simpler if you just:
> 
> if (si)
> 	si->flags &= ~SWP_HIBERNATION;
> 
> Just personal taste, free feel to ignore.

Noted on the style preference. I'll keep it in mind. :D

> And as you mentioned this is on top of swap table p3 so you based
> it on mm-new - but isn't p3 already in mm-unstable? Maybe we can
> have it there? Not sure how much conflict are there with PM.
> 
> The code and design looks OK.

Regarding the base branch. Andrew is already aware of the
potential conflicts in linux-next, and we've discussed possibly
parking this for the next cycle depending on Rafael's input.
So I think we can keep it as-is for now and see how things go!

Best regards,
Youngjun Park

^ permalink raw reply

* Re: [PATCH v2 6/9] interconnect: qcom: let platforms declare their bugginess
From: Luca Weiss @ 2026-03-24 12:14 UTC (permalink / raw)
  To: Dmitry Baryshkov, Georgi Djakov, Konrad Dybcio, Bjorn Andersson,
	Luca Weiss, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Brian Masney
  Cc: linux-arm-msm, linux-pm, devicetree, linux-kernel, Brian Masney,
	Konrad Dybcio
In-Reply-To: <20260324-msm8974-icc-v2-6-527280043ad8@oss.qualcomm.com>

On Tue Mar 24, 2026 at 1:10 AM CET, Dmitry Baryshkov wrote:
> On MSM8974 programming some of the RPM resources results in the
> "resource does not exist" messages from the firmware. This occurs even
> with the downstream bus driver, which happily ignores the errors. My
> assumption is that these resources existed in the earlier firmware
> revisions but were later switched to be programmed differently (for the
> later platforms corresponding nodes use qos.ap_owned, which prevents
> those resources from being programmed.
>
> In preparation for conversion of the MSM8974 driver (which doesn't have
> QoS code yet) to the main icc-rpm set of helpers, let the driver declare
> that those -ENXIO errors must be ignored (for now). Later, when the QoS
> programming is sorted out (and more interconnects are added to the DT),
> this quirk might be removed.

For reference, these are the errors I see on fairphone-fp2

[    5.534806] qcom_icc_rpm_smd_send mas 8 error -6
[    5.535010] qcom_icc_rpm_smd_send mas 8 error -6
[    5.535218] qcom_icc_rpm_smd_send slv 16 error -6
[    5.535445] qcom_icc_rpm_smd_send slv 16 error -6
[    5.535754] qcom_icc_rpm_smd_send mas 2 error -6
[    5.535909] qcom_icc_rpm_smd_send mas 2 error -6
[    5.543665] /soc/display-subsystem@fd900000/dsi@fd922800: Fixed dependency cycle(s) with /soc/display-subsystem@fd900000/dsi@fd922800/panel@0
[    5.544067] /soc/display-subsystem@fd900000/dsi@fd922800/panel@0: Fixed dependency cycle(s) with /soc/display-subsystem@fd900000/dsi@fd922800
[    5.548238] [drm] using 192m VRAM carveout
[    5.560881] msm_mdp fd900100.display-controller: [drm:msm_drm_bind [msm]] VRAM: 70100000->7c100000
[    5.561454] msm_mdp fd900100.display-controller: bound fd922800.dsi (ops dsi_ops [msm])
[    5.563093] adreno fdb00000.gpu: supply vdd not found, using dummy regulator
[    5.563624] adreno fdb00000.gpu: supply vddcx not found, using dummy regulator
[    5.563756] adreno fdb00000.gpu: [drm:msm_devfreq_init [msm]] *ERROR* Couldn't initialize GPU devfreq
[    5.564152] msm_mdp fd900100.display-controller: [drm:msm_gpu_init [msm]] 3.3.0.2: no IOMMU, fallback to VRAM carveout!
[    5.565468] msm_mdp fd900100.display-controller: [drm:a3xx_gpu_init [msm]] *ERROR* No memory protection without IOMMU
[    5.566035] qcom_icc_rpm_smd_send mas 6 error -6
[    5.566235] qcom_icc_rpm_smd_send mas 6 error -6
[    5.566443] qcom_icc_rpm_smd_send slv 16 error -6
[    5.566654] qcom_icc_rpm_smd_send slv 16 error -6
[    5.566863] qcom_icc_rpm_smd_send mas 2 error -6
[    5.567072] qcom_icc_rpm_smd_send mas 2 error -6
[    5.567573] qcom_icc_rpm_smd_send mas 55 error -6
[    5.567869] qcom_icc_rpm_smd_send mas 55 error -6
[    5.568078] qcom_icc_rpm_smd_send mas 56 error -6
[    5.568287] qcom_icc_rpm_smd_send slv 79 error -6
[    5.568495] qcom_icc_rpm_smd_send mas 56 error -6
[    5.568707] qcom_icc_rpm_smd_send slv 79 error -6
[    5.569199] qcom_icc_rpm_smd_send mas 56 error -6
[    5.569486] qcom_icc_rpm_smd_send slv 79 error -6
[    5.569695] qcom_icc_rpm_smd_send mas 56 error -6
[    5.569905] qcom_icc_rpm_smd_send slv 79 error -6
[    5.570292] qcom_icc_rpm_smd_send slv 78 error -6
[    5.570722] qcom_icc_rpm_smd_send slv 78 error -6
[    5.571147] qcom_icc_rpm_smd_send slv 78 error -6
[    5.571575] qcom_icc_rpm_smd_send slv 78 error -6
[    5.572034] msm_mdp fd900100.display-controller: bound fdb00000.gpu (ops a3xx_ops [msm])
[    5.572543] msm_mdp fd900100.display-controller: [drm:mdp5_kms_init [msm]] MDP5 version v1.2
[    5.600817] msm_mdp fd900100.display-controller: [drm] no IOMMU, fallback to phys contig buffers for scanout
[    5.600893] msm_mdp fd900100.display-controller: [drm:mdp5_kms_init [msm]] Skipping eDP interface 0
[    5.601365] msm_mdp fd900100.display-controller: fall back to the other CTL category for INTF 1!
[    5.607380] [drm] Initialized msm 1.12.0 for fd900100.display-controller on minor 0

Regards
Luca

>
> Reviewed-by: Brian Masney <bmasney@redhat.com>
> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> ---
>  drivers/interconnect/qcom/icc-rpm.c | 17 ++++++++++-------
>  drivers/interconnect/qcom/icc-rpm.h |  3 +++
>  2 files changed, 13 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c
> index aec2f84cd56f..23a1d116e79a 100644
> --- a/drivers/interconnect/qcom/icc-rpm.c
> +++ b/drivers/interconnect/qcom/icc-rpm.c
> @@ -204,7 +204,7 @@ static int qcom_icc_qos_set(struct icc_node *node)
>  	}
>  }
>  
> -static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw)
> +static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw, bool ignore_enxio)
>  {
>  	int ret, rpm_ctx = 0;
>  	u64 bw_bps;
> @@ -222,8 +222,9 @@ static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw)
>  						    bw_bps);
>  			if (ret) {
>  				pr_err("qcom_icc_rpm_smd_send mas %d error %d\n",
> -				qn->mas_rpm_id, ret);
> -				return ret;
> +				       qn->mas_rpm_id, ret);
> +				if (ret != -ENXIO || !ignore_enxio)
> +					return ret;
>  			}
>  		}
>  
> @@ -234,8 +235,9 @@ static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw)
>  						    bw_bps);
>  			if (ret) {
>  				pr_err("qcom_icc_rpm_smd_send slv %d error %d\n",
> -				qn->slv_rpm_id, ret);
> -				return ret;
> +				       qn->slv_rpm_id, ret);
> +				if (ret != -ENXIO || !ignore_enxio)
> +					return ret;
>  			}
>  		}
>  	}
> @@ -361,12 +363,12 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
>  	active_rate = agg_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE];
>  	sleep_rate = agg_clk_rate[QCOM_SMD_RPM_SLEEP_STATE];
>  
> -	ret = qcom_icc_rpm_set(src_qn, src_qn->sum_avg);
> +	ret = qcom_icc_rpm_set(src_qn, src_qn->sum_avg, qp->ignore_enxio);
>  	if (ret)
>  		return ret;
>  
>  	if (dst_qn) {
> -		ret = qcom_icc_rpm_set(dst_qn, dst_qn->sum_avg);
> +		ret = qcom_icc_rpm_set(dst_qn, dst_qn->sum_avg, qp->ignore_enxio);
>  		if (ret)
>  			return ret;
>  	}
> @@ -509,6 +511,7 @@ int qnoc_probe(struct platform_device *pdev)
>  	for (i = 0; i < cd_num; i++)
>  		qp->intf_clks[i].id = cds[i];
>  
> +	qp->ignore_enxio = desc->ignore_enxio;
>  	qp->keep_alive = desc->keep_alive;
>  	qp->type = desc->type;
>  	qp->qos_offset = desc->qos_offset;
> diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h
> index ad554c63967b..7d1cb2efa9ee 100644
> --- a/drivers/interconnect/qcom/icc-rpm.h
> +++ b/drivers/interconnect/qcom/icc-rpm.h
> @@ -51,6 +51,7 @@ struct rpm_clk_resource {
>   * @bus_clk: a pointer to a HLOS-owned bus clock
>   * @intf_clks: a clk_bulk_data array of interface clocks
>   * @keep_alive: whether to always keep a minimum vote on the bus clocks
> + * @ignore_enxio: whether to ignore ENXIO errors (for MSM8974)
>   */
>  struct qcom_icc_provider {
>  	struct icc_provider provider;
> @@ -65,6 +66,7 @@ struct qcom_icc_provider {
>  	struct clk *bus_clk;
>  	struct clk_bulk_data *intf_clks;
>  	bool keep_alive;
> +	bool ignore_enxio;
>  };
>  
>  /**
> @@ -136,6 +138,7 @@ struct qcom_icc_desc {
>  	u16 ab_coeff;
>  	u16 ib_coeff;
>  	int (*get_bw)(struct icc_node *node, u32 *avg, u32 *peak);
> +	bool ignore_enxio;
>  };
>  
>  /* Valid for all bus types */


^ permalink raw reply

* Re: [PATCH v8 3/3] PCI: Add support for PCIe WAKE# interrupt
From: Krishna Chaitanya Chundru @ 2026-03-24 12:03 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: Rafael J. Wysocki, Len Brown, Pavel Machek, Greg Kroah-Hartman,
	Danilo Krummrich, Bjorn Helgaas, Bartosz Golaszewski,
	Linus Walleij, Bartosz Golaszewski, Rob Herring, Saravana Kannan,
	linux-pm, linux-kernel, linux-pci, linux-gpio, quic_vbadigan,
	sherry.sun, driver-core, devicetree
In-Reply-To: <4n5heks4oymfz75wiajyc5zuzzulmwyfj2couudbi7gi67h2rk@smpnmyhdjkns>



On 3/17/2026 12:56 PM, Manivannan Sadhasivam wrote:
> On Fri, Mar 13, 2026 at 12:38:42PM +0530, Krishna Chaitanya Chundru wrote:
>> According to the PCI Express specification (PCIe r7.0, Section 5.3.3.2),
>> two link wakeup mechanisms are defined: Beacon and WAKE#. Beacon is a
>> hardware-only mechanism and is invisible to software (PCIe r7.0,
>> Section 4.2.7.8.1). This change adds support for the WAKE# mechanism in
>> the PCI core.
>>
>> According to the PCIe specification, multiple WAKE# signals can exist in
>> a system or each component in the hierarchy could share a single WAKE#
>> signal. In configurations involving a PCIe switch, each downstream port
>> (DSP) of the switch may be connected to a separate WAKE# line, allowing
>> each endpoint to signal WAKE# independently. From figure 5.4 in sec
>> 5.3.3.2, WAKE# can also be terminated at the switch itself. To support
>> this, the WAKE# should be described in the device tree node of the
>> endpoint/bridge. If all endpoints share a single WAKE# line, then each
>> endpoint node should describe the same WAKE# signal or a single WAKE# in
>> the Root Port node.
>>
>> In pci_device_add(), PCI framework will search for the WAKE# in device
>> node, If not found, it searches in its upstream port only if upstream port
>> is Root Port. Once found, register for the wake IRQ in shared mode, as the
>> WAKE# may be shared among multiple endpoints.
>>
>> dev_pm_set_dedicated_shared_wake_irq() associates a wakeup IRQ with a
>> device and requests it, but the PM core keeps the IRQ disabled by default.
>> The IRQ is enabled only when the device is permitted to wake the system,
>> i.e. during system suspend and after runtime suspend, and only when device
>> wakeup is enabled.
>>
>> When the wake IRQ fires, the wakeirq handler invokes pm_runtime_resume() to
>> bring the device back to an active power state, such as transitioning from
>> D3cold to D0. Once the device is active and the link is usable, the
>> endpoint may generate a PME, which is then handled by the PCI core through
>> PME polling or the PCIe PME service driver to complete the wakeup of the
>> endpoint.
>>
>> WAKE# is added in dts schema and merged based on below links.
>>
>> Link: https://lore.kernel.org/all/20250515090517.3506772-1-krishna.chundru@oss.qualcomm.com/
>> Link: https://github.com/devicetree-org/dt-schema/pull/170
>> Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
>> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
>> ---
>>   drivers/pci/of.c       | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>   drivers/pci/pci.c      | 10 +++++++
>>   drivers/pci/pci.h      |  2 ++
>>   drivers/pci/probe.c    |  2 ++
>>   drivers/pci/remove.c   |  1 +
>>   include/linux/of_pci.h |  4 +++
>>   include/linux/pci.h    |  2 ++
>>   7 files changed, 95 insertions(+)
>>
>> diff --git a/drivers/pci/of.c b/drivers/pci/of.c
>> index 9f8eb5df279ed28db7a3b2fd29c65da9975c2efa..b7199d3598b31b62245716c178a5a73565efc89e 100644
>> --- a/drivers/pci/of.c
>> +++ b/drivers/pci/of.c
>> @@ -7,6 +7,7 @@
>>   #define pr_fmt(fmt)	"PCI: OF: " fmt
>>   
>>   #include <linux/cleanup.h>
>> +#include <linux/gpio/consumer.h>
>>   #include <linux/irqdomain.h>
>>   #include <linux/kernel.h>
>>   #include <linux/pci.h>
>> @@ -15,6 +16,7 @@
>>   #include <linux/of_address.h>
>>   #include <linux/of_pci.h>
>>   #include <linux/platform_device.h>
>> +#include <linux/pm_wakeirq.h>
>>   #include "pci.h"
>>   
>>   #ifdef CONFIG_PCI
>> @@ -586,6 +588,78 @@ int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin)
>>   	return irq_create_of_mapping(&oirq);
>>   }
>>   EXPORT_SYMBOL_GPL(of_irq_parse_and_map_pci);
>> +
>> +static void pci_configure_wake_irq(struct pci_dev *pdev, struct gpio_desc *wake)
>> +{
>> +	int ret, wake_irq;
>> +
>> +	wake_irq = gpiod_to_irq(wake);
>> +	if (wake_irq < 0) {
>> +		pci_err(pdev, "Failed to get wake irq: %d\n", wake_irq);
>> +		return;
>> +	}
>> +
>> +	device_init_wakeup(&pdev->dev, true);
> Just set wakeup only if dev_pm_set_dedicated_shared_wake_irq() succeeds.
ack.
>> +
>> +	/*
>> +	 * dev_pm_set_dedicated_shared_wake_irq() associates a wakeup IRQ with the
>> +	 * device and requests it, but the PM core keeps it disabled by default.
>> +	 * The IRQ is enabled only when the device is allowed to wake the system
>> +	 * (during system suspend and after runtime suspend), and only if device
>> +	 * wakeup is enabled.
>> +	 *
>> +	 * When the wake IRQ fires, the wakeirq handler invokes pm_runtime_resume()
>> +	 * to bring the device back to an active power state (e.g. from D3cold to D0).
>> +	 * Once the device is active and the link is usable, the endpoint may signal
>> +	 * a PME, which is then handled by the PCI core (either via PME polling or the
>> +	 * PCIe PME service driver) to wakeup particular endpoint.
>> +	 */
>> +	ret = dev_pm_set_dedicated_shared_wake_irq(&pdev->dev, wake_irq,
>> +						   IRQ_TYPE_EDGE_FALLING);
> Isn't WAKE# a level triggered signal?
Ack. I will change IRQ_TYPE_EDGE_FALLING to IRQ_TYPE_LEVEL_LOW.
>> +	if (ret < 0) {
>> +		pci_err(pdev, "Failed to set wake IRQ: %d\n", ret);
> s/wake/WAKE#
ack.

- Krishna Chaitanya.
> - Mani
>


^ permalink raw reply

* Re: [RFC PATCH 1/2] thermal/cpufreq_cooling: remove unused cpu_idx in get_load()
From: Xuewen Yan @ 2026-03-24 12:03 UTC (permalink / raw)
  To: Lukasz Luba
  Cc: Viresh Kumar, Xuewen Yan, rui.zhang, rafael, linux-pm,
	amit.kachhap, daniel.lezcano, linux-kernel, ke.wang, di.shen,
	jeson.gao
In-Reply-To: <35d472ac-8a58-44c5-a0b1-5e1de8ac6cfc@arm.com>

On Tue, Mar 24, 2026 at 6:45 PM Lukasz Luba <lukasz.luba@arm.com> wrote:
>
>
>
> On 3/24/26 02:20, Xuewen Yan wrote:
> > On Mon, Mar 23, 2026 at 9:25 PM Lukasz Luba <lukasz.luba@arm.com> wrote:
> >>
> >>
> >>
> >> On 3/23/26 11:06, Viresh Kumar wrote:
> >>> On 23-03-26, 10:52, Lukasz Luba wrote:
> >>>>> How is that okay ? What am I missing ?
> >>>
> >>> I was missing !SMP :)
> >>>
> >>>> Right, there is a mix of two things.
> >>>> The 'i' left but should be removed as well, since
> >>>> this is !SMP code with only 1 cpu and i=0.
> >
> > That's also why we sent out patch 1/2; after all, it is always 0 on
> > !SMP systems.
> >
> >>>>
> >>>> The whole split which has been made for getting
> >>>> the load or utilization from CPU(s) needs to be
> >>>> cleaned. The compiled code looks different since
> >>>> it knows there is non-SMP config used.
> >>>
> >>> Right, we are allocating that for num_cpus (which should be 1 CPU
> >>> anyway). The entire thing must be cleaned.
> >>>
> >>>> Do you want to clean that or I should do this?
> >>>
> >>> It would be helpful if you can do it :)
> >>>
> >>
> >> OK, I will. Thanks for your involvement Viresh!
> >>
> >> Xuewen please wait with your v2, I will send
> >> a redesign of this left code today.
> >
> > Okay, and Qais's point is also worth considering: do we actually need
> > sched_cpu_util()?
> > The way I see it, generally speaking, the request_power derived from
> > idle_time might be higher than what we get from sched_cpu_util().
> > Take this scenario as an example:
> > Consider a CPU running at the lowest frequency with 50% idle time,
> > versus one running at the highest frequency with the same 50% idle
> > time.
> > In this case, using idle_time yields the same load value for both.
> > However, sched_cpu_util() would report a lower load when the CPU
> > frequency is low. This results in a smaller request_power...
>
> Right, there are 2 things to consider:
> 1. what is the utilization when the CPU still have idle time, e.g.
>     this 50% that you mentioned
> 2. what is the utilization when there is no idle time and CPU
>     is fully busy (and starts throttling due to heat)
>
> In this thermal fwk we are mostly in the 2nd case. In that case the
> utilization on CPU's runqueue goes to 1024 no mater the CPU's frequency.

Haha, indeed. When we debug IPA, we also keep the CPU constantly
running with basically no idle time.
In this scenario, we tested using both sched_cpu_util() and idle_time,
and for thermal control purposes, there was basically no difference
(likely because the load was at 100%).
Maybe we can cook up a test case where the CPU is overheating despite
having some idle time? That way we can compare how the two interfaces
perform.

> We know which highest frequency was allowed to run and we pick the power
> value from EM for it. That's why the estimation is not that bad (apart
> from power variation for different flavors of workloads: heavy SIMD vs.
> normal integer/load).
>
> In 1st case scenario we might underestimate the power, but that
> is not the thermal stress situation anyway, so the max OPP is
> still allowed.
>
> So far it is hard to find the best power model to use and robust CPU
> load mechanisms. Adding more complexity and creating some
> over-engineered code in the kernel to maintain might not have sense.
> The thermal solutions are solved in the Firmware nowadays since the
> kernel won't react that fast for some rapid changes.
>
> We have to balance the complexity here.
> Let's improve the situation a bit. It would be very much appreciated if
> you could share information if those changes help your platform
> (some older boards might not show any benefit with the new code).
>
Understood. We appreciate the balance between complexity and accuracy.
We could test these changes on our platforms and let you know if we
see any improvements in thermal stability or power estimation. Expect
an update from us in a few days.

Thanks!
---

> Regards,
> Lukasz
>

^ permalink raw reply

* Re: [PATCH v6 3/4] cpufreq: Set policy->min and max as real QoS constraints
From: Pierre Gondois @ 2026-03-24 11:40 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: linux-kernel, Jie Zhan, Lifeng Zheng, Ionela Voinescu,
	Sumit Gupta, Huang Rui, Gautham R. Shenoy, Mario Limonciello,
	Perry Yuan, Rafael J. Wysocki, Srinivas Pandruvada, Len Brown,
	Saravana Kannan, linux-pm
In-Reply-To: <ukxdxr3xv7x6pqnwwafxgvhvfh5n7w67y3zwunhnk7b45tz3t6@d6ehl4vqac6t>


On 3/20/26 11:14, Viresh Kumar wrote:
> On 17-03-26, 11:17, Pierre Gondois wrote:
>> cpufreq_set_policy() will ultimately override the policy min/max
>> values written in the .init() callback through:
>> cpufreq_policy_online()
>> \-cpufreq_init_policy()
>>    \-cpufreq_set_policy()
>>      \-/* Set policy->min/max */
>> Thus the policy min/max values provided are only temporary.
> I am not comfortable with this patch to be honest. policy->min/max are used at
> so many places that it is really difficult to make sure if this patch will break
> something or not.
>
> For example:
>
> cpufreq_set_policy()
>   cpufreq_driver->verify()
>    cpufreq_frequency_table_verify()
>
> This uses min/max before it is set by the path you mentioned.
>
> I would suggest dropping this change, or most of it and doing only what is
> really required for this series.
>
Being able to set the min/max_freq_req from the driver might be
something that is needed, cf:
https://lore.kernel.org/lkml/20260213100633.15413-1-zhangpengjie2@huawei.com/

It would also allow to have a common way to set policy->min/max values
as they are set to the the cpuinfo.min/max_freq.

On the other hand I agree that I didn't test all the possible paths
for this change, so this is a bit audacious.


What about adding the following to have the values set for all drivers:

diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c

index 70814c567243b..3a1e5f58a301f 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1530,6 +1530,9 @@ static int cpufreq_policy_online(struct 
cpufreq_policy *policy,
                                 CPUFREQ_CREATE_POLICY, policy);
         }

+       policy->max = policy->cpuinfo.max_freq;
+       policy->min = policy->cpuinfo.min_freq;
+
         if (cpufreq_driver->get && has_target()) {
                 policy->cur = cpufreq_driver->get(policy->cpu);
                 if (!policy->cur) {


^ permalink raw reply related

* Re: [PATCH v6 1/4] cpufreq: Remove per-CPU QoS constraint
From: Pierre Gondois @ 2026-03-24 11:39 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Lifeng Zheng, linux-kernel, Jie Zhan, Ionela Voinescu,
	Sumit Gupta, Huang Rui, Gautham R. Shenoy, Mario Limonciello,
	Perry Yuan, Rafael J. Wysocki, Srinivas Pandruvada, Len Brown,
	Saravana Kannan, linux-pm
In-Reply-To: <5ulxqpnblsbxhcg5m2rfscrvkokd43wwg5sihml4oytcsswh43@lqdwn5tqopf3>

Hello Viresh,

On 3/20/26 10:18, Viresh Kumar wrote:
> On 19-03-26, 10:30, Pierre Gondois wrote:
>> On 3/18/26 12:13, Viresh Kumar wrote:
>>> On 17-03-26, 11:17, Pierre Gondois wrote:
>>>> policy->max_freq_req represents the maximum allowed frequency as
>>>> requested by the policyX/scaling_max_freq sysfs file. This request
>>>> applies to all CPUs of the policy. It is not possible to request
>>>> a per-CPU maximum frequency.
>>>>
>>>> Thus, the interaction between the policy boost and scaling_max_freq
>>>> settings should be handled by adding a boost specific QoS constraint.
>>>> This will be handled in the following patches.
>>> I don't think the above is required anymore. This patch is removing stale code
>>> now which isn't useful anymore. It has nothing to do with a boost specific QOS
>>> constraint.
>> Yes ok
>>> And it would be better to know for sure why this isn't required anymore and
>>> which patch exactly fixed this issue.
>>>
>> On a kernel based on 1608f0230510~, and replicating the
>> process described in the commit message of
>>
>> commit 1608f0230510 ("cpufreq: Fix re-boost issue after hotplugging
>> a CPU")
>>
>> I could not see any issue regarding the values of:
>>
>> - policy1/cpuinfo_max_freq
>> - policy1/scaling_max_freq
> The commit message (of 1608f0230510) is confusing. The issue was discussed
> properly in the following thread.
>
> https://lore.kernel.org/all/20250120082723.am7rxujmdvzz4eky@vireshk-i7/
>
> The problem is that policy->max and policy->cpuinfo_max_freq are incorrect after
> the sequence mentioned in the commit, while max_freq_req is correct.

I experimented a bit more and it seems the following happens:

1. boost all CPUs: echo 1 > /sys/devices/system/cpu/cpufreq/boost
2. offline one CPU: echo 0 > /sys/devices/system/cpu/cpuX/online
3. deboost all CPUs: echo 0 > /sys/devices/system/cpu/cpufreq/boost

cpufreq_boost_trigger_state()
\-for_each_active_policy()
   \-cpufreq_driver->set_boost()
doesn't act on the policy where there are no more online CPUs,
so the max/cpuinfo.max/max_freq_req is left to the actual
boost freq.

4. online CPUX: echo 1 > /sys/devices/system/cpu/cpuX/online

cpufreq_online()
\-cpufreq_driver->init()
   \-cppc_cpufreq_cpu_init()
There:
- policy->max
- policy->cpuinfo.max_freq
are set to the maximal non-boost freq., which is the correct value.

However, max_freq_req is left to the boosted frequency, so this
is effectively an incorrect state.
Also in cpufreq_set_policy(), policy->max is set to:
   min(max_freq_req, cpuinfo.max_freq)
(cf. verify() cb), so the incorrect state of max_freq_req is not
visible.

5. boost all CPUs again: echo 1 > /sys/devices/system/cpu/cpufreq/boost

As the max_freq_req value and the new boost value are equal,
cpufreq_notifier_max() won't be called, which means that if the
CPU needed to raise its freq., it won't be notified until another
event trigger a re-evaluation of the freq. selection.

To observe that, I had to:
- use the performance governor to be sure to select the max. available
   freq.
- observe scaling_cur_freq to see the last requested freq. for the CPU

So IMO the issue was actually fixed by:
dd016f379ebc ("cpufreq: Introduce a more generic way to set default
per-policy boost flag")
which sets the correct max_freq_req value when putting back an
inactive policy.


>
> I think another commit has fixed that (incorrectly and unintentionally):
> commit 6db0f533d320 ("cpufreq: preserve freq_table_sorted across suspend/hibernate")
>
> @@ -1421,9 +1421,12 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy,
>                   * If there is a problem with its frequency table, take it
>                   * offline and drop it.
>                   */
> -               ret = cpufreq_table_validate_and_sort(policy);
> -               if (ret)
> -                       goto out_offline_policy;
> +               if (policy->freq_table_sorted != CPUFREQ_TABLE_SORTED_ASCENDING &&
> +                   policy->freq_table_sorted != CPUFREQ_TABLE_SORTED_DESCENDING) {
> +                       ret = cpufreq_table_validate_and_sort(policy);
> +                       if (ret)
> +                               goto out_offline_policy;
> +               }
>
> This skipped calling cpufreq_table_validate_and_sort() completely on online and
> so max/cpuinfo_max_freq, max_freq_req are all in sync.
>
> That change should be fixed with:
>
> diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
> index 277884d91913..1f794524a1d9 100644
> --- a/drivers/cpufreq/cpufreq.c
> +++ b/drivers/cpufreq/cpufreq.c
> @@ -1427,12 +1427,9 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy,
>                   * If there is a problem with its frequency table, take it
>                   * offline and drop it.
>                   */
> -               if (policy->freq_table_sorted != CPUFREQ_TABLE_SORTED_ASCENDING &&
> -                   policy->freq_table_sorted != CPUFREQ_TABLE_SORTED_DESCENDING) {
> -                       ret = cpufreq_table_validate_and_sort(policy);
> -                       if (ret)
> -                               goto out_offline_policy;
> -               }
> +               ret = cpufreq_table_validate_and_sort(policy);
> +               if (ret)
> +                       goto out_offline_policy;
>
>                  /* related_cpus should at least include policy->cpus. */
>                  cpumask_copy(policy->related_cpus, policy->cpus);
> diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c
> index 7f251daf03ce..5b364d8da4f9 100644
> --- a/drivers/cpufreq/freq_table.c
> +++ b/drivers/cpufreq/freq_table.c
> @@ -360,6 +360,10 @@ int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy)
>          if (policy_has_boost_freq(policy))
>                  policy->boost_supported = true;
>
> +       if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING ||
> +           policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_DESCENDING)
> +               return 0;
> +
>          return set_freq_table_sorted(policy);
>   }
>

^ permalink raw reply

* [PATCH v5 11/11] power: supply: bq257xx: Add support for BQ25792
From: Alexey Charkov @ 2026-03-24 11:38 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Chris Morgan, Liam Girdwood, Mark Brown, Sebastian Reichel
  Cc: devicetree, linux-kernel, Sebastian Reichel, linux-pm,
	Alexey Charkov
In-Reply-To: <20260324-bq25792-v5-0-0a2eb58cf11d@flipper.net>

Add support for TI BQ25792 integrated battery charger and buck-boost
converter.

It shares high-level logic of operation with the already supported
BQ25703A, but has a different register map, bit definitions and some of
the lower-level hardware states.

Tested-by: Chris Morgan <macromorgan@hotmail.com>
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
 drivers/power/supply/bq257xx_charger.c | 482 ++++++++++++++++++++++++++++++++-
 1 file changed, 481 insertions(+), 1 deletion(-)

diff --git a/drivers/power/supply/bq257xx_charger.c b/drivers/power/supply/bq257xx_charger.c
index 9c082865e745..b9a41fe6f00e 100644
--- a/drivers/power/supply/bq257xx_charger.c
+++ b/drivers/power/supply/bq257xx_charger.c
@@ -5,6 +5,7 @@
  */
 
 #include <linux/bitfield.h>
+#include <linux/byteorder/generic.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/mfd/bq257xx.h>
@@ -88,6 +89,53 @@ struct bq257xx_chg {
 	u32 vsys_min;
 };
 
+/**
+ * bq25792_read16() - Read a 16-bit value from device register
+ * @pdata: driver platform data
+ * @reg: register address to read from
+ * @val: pointer to store the register value
+ *
+ * Read a 16-bit big-endian value from the BQ25792 device via regmap
+ * and convert to CPU byte order.
+ *
+ * Return: Returns 0 on success or error on failure to read.
+ */
+static int bq25792_read16(struct bq257xx_chg *pdata, unsigned int reg, u16 *val)
+{
+	__be16 regval;
+	int ret;
+
+	ret = regmap_raw_read(pdata->bq->regmap, reg, &regval, sizeof(regval));
+	if (ret)
+		return ret;
+
+	*val = be16_to_cpu(regval);
+	return 0;
+}
+
+/**
+ * bq25792_write16() - Write a 16-bit value to device register
+ * @pdata: driver platform data
+ * @reg: register address to write to
+ * @val: 16-bit value to write in CPU byte order
+ *
+ * Convert the value to big-endian and write a 16-bit value to the
+ * BQ25792 device via regmap.
+ *
+ * Return: Returns 0 on success or error on failure to write.
+ */
+static int bq25792_write16(struct bq257xx_chg *pdata, unsigned int reg, u16 val)
+{
+	__be16 regval = cpu_to_be16(val);
+	int ret;
+
+	ret = regmap_raw_write(pdata->bq->regmap, reg, &regval, sizeof(regval));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 /**
  * bq25703_get_state() - Get the current state of the device
  * @pdata: driver platform data
@@ -119,6 +167,43 @@ static int bq25703_get_state(struct bq257xx_chg *pdata)
 	return 0;
 }
 
+/**
+ * bq25792_get_state() - Get the current state of the device
+ * @pdata: driver platform data
+ *
+ * Get the current state of the BQ25792 charger by reading status
+ * registers. Updates the online, charging, overvoltage, and fault
+ * status fields in the driver data structure.
+ *
+ * Return: Returns 0 on success or error on failure to read device.
+ */
+static int bq25792_get_state(struct bq257xx_chg *pdata)
+{
+	unsigned int reg;
+	int ret;
+
+	ret = regmap_read(pdata->bq->regmap, BQ25792_REG1B_CHARGER_STATUS_0, &reg);
+	if (ret)
+		return ret;
+
+	pdata->online = reg & BQ25792_REG1B_PG_STAT;
+
+	ret = regmap_read(pdata->bq->regmap, BQ25792_REG1C_CHARGER_STATUS_1, &reg);
+	if (ret)
+		return ret;
+
+	pdata->charging = reg & BQ25792_REG1C_CHG_STAT_MASK;
+
+	ret = regmap_read(pdata->bq->regmap, BQ25792_REG20_FAULT_STATUS_0, &reg);
+	if (ret)
+		return ret;
+
+	pdata->overvoltage = reg & BQ25792_REG20_OVERVOLTAGE_MASK;
+	pdata->oc_fault = reg & BQ25792_REG20_OVERCURRENT_MASK;
+
+	return 0;
+}
+
 /**
  * bq25703_get_min_vsys() - Get the minimum system voltage
  * @pdata: driver platform data
@@ -142,6 +227,31 @@ static int bq25703_get_min_vsys(struct bq257xx_chg *pdata, int *intval)
 	return ret;
 }
 
+/**
+ * bq25792_get_min_vsys() - Get the minimum system voltage
+ * @pdata: driver platform data
+ * @intval: pointer to store the minimum voltage value
+ *
+ * Read the current minimum system voltage setting from the device
+ * and return it in microvolts.
+ *
+ * Return: Returns 0 on success or error on failure to read.
+ */
+static int bq25792_get_min_vsys(struct bq257xx_chg *pdata, int *intval)
+{
+	unsigned int reg;
+	int ret;
+
+	ret = regmap_read(pdata->bq->regmap, BQ25792_REG00_MIN_SYS_VOLTAGE, &reg);
+	if (ret)
+		return ret;
+
+	reg = FIELD_GET(BQ25792_REG00_VSYSMIN_MASK, reg);
+	*intval = (reg * BQ25792_MINVSYS_STEP_UV) + BQ25792_MINVSYS_MIN_UV;
+
+	return ret;
+}
+
 /**
  * bq25703_set_min_vsys() - Set the minimum system voltage
  * @pdata: driver platform data
@@ -166,6 +276,29 @@ static int bq25703_set_min_vsys(struct bq257xx_chg *pdata, int vsys)
 			    reg);
 }
 
+/**
+ * bq25792_set_min_vsys() - Set the minimum system voltage
+ * @pdata: driver platform data
+ * @vsys: voltage value to set in uV
+ *
+ * Set the minimum system voltage by clamping the requested value
+ * between device limits and writing to the appropriate register.
+ *
+ * Return: Returns 0 on success or error on failure to write.
+ */
+static int bq25792_set_min_vsys(struct bq257xx_chg *pdata, int vsys)
+{
+	unsigned int reg;
+	int vsys_min = pdata->vsys_min;
+
+	vsys = clamp(vsys, vsys_min, BQ25792_MINVSYS_MAX_UV);
+	reg = ((vsys - BQ25792_MINVSYS_MIN_UV) / BQ25792_MINVSYS_STEP_UV);
+	reg = FIELD_PREP(BQ25792_REG00_VSYSMIN_MASK, reg);
+
+	return regmap_write(pdata->bq->regmap,
+			    BQ25792_REG00_MIN_SYS_VOLTAGE, reg);
+}
+
 /**
  * bq25703_get_cur() - Get the reported current from the battery
  * @pdata: driver platform data
@@ -195,6 +328,30 @@ static int bq25703_get_cur(struct bq257xx_chg *pdata, int *intval)
 	return ret;
 }
 
+/**
+ * bq25792_get_cur() - Get the reported current from the battery
+ * @pdata: driver platform data
+ * @intval: pointer to store the battery current value
+ *
+ * Read the current ADC value from the device representing the battery
+ * charge or discharge current and return it in microamps.
+ *
+ * Return: Returns 0 on success or error on failure to read.
+ */
+static int bq25792_get_cur(struct bq257xx_chg *pdata, int *intval)
+{
+	u16 reg;
+	int ret;
+
+	ret = bq25792_read16(pdata, BQ25792_REG33_IBAT_ADC, &reg);
+	if (ret < 0)
+		return ret;
+
+	*intval = (s16)reg * BQ25792_ADCIBAT_STEP_UA;
+
+	return ret;
+}
+
 /**
  * bq25703_get_ichg_cur() - Get the maximum reported charge current
  * @pdata: driver platform data
@@ -218,6 +375,30 @@ static int bq25703_get_ichg_cur(struct bq257xx_chg *pdata, int *intval)
 	return ret;
 }
 
+/**
+ * bq25792_get_ichg_cur() - Get the maximum reported charge current
+ * @pdata: driver platform data
+ * @intval: pointer to store the maximum charge current value
+ *
+ * Read the programmed maximum charge current limit from the device.
+ *
+ * Return: Returns 0 on success or error on failure to read value.
+ */
+static int bq25792_get_ichg_cur(struct bq257xx_chg *pdata, int *intval)
+{
+	u16 reg;
+	int ret;
+
+	ret = bq25792_read16(pdata, BQ25792_REG03_CHARGE_CURRENT_LIMIT, &reg);
+	if (ret)
+		return ret;
+
+	*intval = FIELD_GET(BQ25792_REG03_ICHG_MASK, reg) *
+		  BQ25792_ICHG_STEP_UA;
+
+	return ret;
+}
+
 /**
  * bq25703_set_ichg_cur() - Set the maximum charge current
  * @pdata: driver platform data
@@ -242,6 +423,28 @@ static int bq25703_set_ichg_cur(struct bq257xx_chg *pdata, int ichg)
 			    reg);
 }
 
+/**
+ * bq25792_set_ichg_cur() - Set the maximum charge current
+ * @pdata: driver platform data
+ * @ichg: current value to set in uA
+ *
+ * Set the maximum charge current by clamping the requested value
+ * between device limits and writing to the appropriate register.
+ *
+ * Return: Returns 0 on success or error on failure to write.
+ */
+static int bq25792_set_ichg_cur(struct bq257xx_chg *pdata, int ichg)
+{
+	int ichg_max = pdata->ichg_max;
+	u16 reg;
+
+	ichg = clamp(ichg, BQ25792_ICHG_MIN_UA, ichg_max);
+	reg = FIELD_PREP(BQ25792_REG03_ICHG_MASK,
+			 (ichg / BQ25792_ICHG_STEP_UA));
+
+	return bq25792_write16(pdata, BQ25792_REG03_CHARGE_CURRENT_LIMIT, reg);
+}
+
 /**
  * bq25703_get_chrg_volt() - Get the maximum set charge voltage
  * @pdata: driver platform data
@@ -265,6 +468,30 @@ static int bq25703_get_chrg_volt(struct bq257xx_chg *pdata, int *intval)
 	return ret;
 }
 
+/**
+ * bq25792_get_chrg_volt() - Get the maximum set charge voltage
+ * @pdata: driver platform data
+ * @intval: pointer to store the maximum charge voltage value
+ *
+ * Read the current charge voltage limit from the device.
+ *
+ * Return: Returns 0 on success or error on failure to read value.
+ */
+static int bq25792_get_chrg_volt(struct bq257xx_chg *pdata, int *intval)
+{
+	u16 reg;
+	int ret;
+
+	ret = bq25792_read16(pdata, BQ25792_REG01_CHARGE_VOLTAGE_LIMIT, &reg);
+	if (ret)
+		return ret;
+
+	*intval = FIELD_GET(BQ25792_REG01_VREG_MASK, reg) *
+		  BQ25792_VBATREG_STEP_UV;
+
+	return ret;
+}
+
 /**
  * bq25703_set_chrg_volt() - Set the maximum charge voltage
  * @pdata: driver platform data
@@ -291,6 +518,29 @@ static int bq25703_set_chrg_volt(struct bq257xx_chg *pdata, int vbat)
 			    reg);
 }
 
+/**
+ * bq25792_set_chrg_volt() - Set the maximum charge voltage
+ * @pdata: driver platform data
+ * @vbat: voltage value to set in uV
+ *
+ * Set the maximum charge voltage by clamping the requested value
+ * between device limits and writing to the appropriate register.
+ *
+ * Return: Returns 0 on success or error on failure to write.
+ */
+static int bq25792_set_chrg_volt(struct bq257xx_chg *pdata, int vbat)
+{
+	int vbat_max = pdata->vbat_max;
+	u16 reg;
+
+	vbat = clamp(vbat, BQ25792_VBATREG_MIN_UV, vbat_max);
+
+	reg = FIELD_PREP(BQ25792_REG01_VREG_MASK,
+			 (vbat / BQ25792_VBATREG_STEP_UV));
+
+	return bq25792_write16(pdata, BQ25792_REG01_CHARGE_VOLTAGE_LIMIT, reg);
+}
+
 /**
  * bq25703_get_iindpm() - Get the maximum set input current
  * @pdata: driver platform data
@@ -319,6 +569,30 @@ static int bq25703_get_iindpm(struct bq257xx_chg *pdata, int *intval)
 	return ret;
 }
 
+/**
+ * bq25792_get_iindpm() - Get the maximum set input current
+ * @pdata: driver platform data
+ * @intval: pointer to store the maximum input current value
+ *
+ * Read the current input current limit from the device.
+ *
+ * Return: Returns 0 on success or error on failure to read value.
+ */
+static int bq25792_get_iindpm(struct bq257xx_chg *pdata, int *intval)
+{
+	u16 reg;
+	int ret;
+
+	ret = bq25792_read16(pdata, BQ25792_REG06_INPUT_CURRENT_LIMIT, &reg);
+	if (ret)
+		return ret;
+
+	reg = FIELD_GET(BQ25792_REG06_IINDPM_MASK, reg);
+	*intval = reg * BQ25792_IINDPM_STEP_UA;
+
+	return ret;
+}
+
 /**
  * bq25703_set_iindpm() - Set the maximum input current
  * @pdata: driver platform data
@@ -344,6 +618,29 @@ static int bq25703_set_iindpm(struct bq257xx_chg *pdata, int iindpm)
 			    FIELD_PREP(BQ25703_IINDPM_MASK, reg));
 }
 
+/**
+ * bq25792_set_iindpm() - Set the maximum input current
+ * @pdata: driver platform data
+ * @iindpm: current value in uA
+ *
+ * Set the maximum input current by clamping the requested value
+ * between device limits and writing to the appropriate register.
+ *
+ * Return: Returns 0 on success or error on failure to write.
+ */
+static int bq25792_set_iindpm(struct bq257xx_chg *pdata, int iindpm)
+{
+	u16 reg;
+	int iindpm_max = pdata->iindpm_max;
+
+	iindpm = clamp(iindpm, BQ25792_IINDPM_MIN_UA, iindpm_max);
+
+	reg = iindpm / BQ25792_IINDPM_STEP_UA;
+
+	return bq25792_write16(pdata, BQ25792_REG06_INPUT_CURRENT_LIMIT,
+			       FIELD_PREP(BQ25792_REG06_IINDPM_MASK, reg));
+}
+
 /**
  * bq25703_get_vbat() - Get the reported voltage from the battery
  * @pdata: driver platform data
@@ -368,6 +665,30 @@ static int bq25703_get_vbat(struct bq257xx_chg *pdata, int *intval)
 	return ret;
 }
 
+/**
+ * bq25792_get_vbat() - Get the reported voltage from the battery
+ * @pdata: driver platform data
+ * @intval: pointer to store the battery voltage value
+ *
+ * Read the current ADC value representing the battery voltage
+ * and return it in microvolts.
+ *
+ * Return: Returns 0 on success or error on failure to read value.
+ */
+static int bq25792_get_vbat(struct bq257xx_chg *pdata, int *intval)
+{
+	u16 reg;
+	int ret;
+
+	ret = bq25792_read16(pdata, BQ25792_REG3B_VBAT_ADC, &reg);
+	if (ret)
+		return ret;
+
+	*intval = reg * BQ25792_ADCVSYSVBAT_STEP_UV;
+
+	return ret;
+}
+
 /**
  * bq25703_hw_init() - Set all the required registers to init the charger
  * @pdata: driver platform data
@@ -434,6 +755,62 @@ static int bq25703_hw_init(struct bq257xx_chg *pdata)
 	return ret;
 }
 
+/**
+ * bq25792_hw_init() - Initialize BQ25792 hardware
+ * @pdata: driver platform data
+ *
+ * Initialize the BQ25792 by disabling the watchdog, enabling discharge
+ * current sensing with 5A limit, and configuring input current regulation.
+ * Set the charge current, charge voltage, minimum system voltage, and
+ * input current limit from platform data. Enable and configure the ADC
+ * to measure all available channels.
+ *
+ * Return: Returns 0 on success or error code on error.
+ */
+static int bq25792_hw_init(struct bq257xx_chg *pdata)
+{
+	struct regmap *regmap = pdata->bq->regmap;
+	int ret = 0;
+
+	/* Disable watchdog (TODO: make it work instead) */
+	regmap_write(regmap, BQ25792_REG10_CHARGER_CONTROL_1, 0);
+
+	/*
+	 * Enable battery discharge current sensing, 5A discharge current
+	 * limit, input current regulation and ship FET functions
+	 */
+	regmap_write(regmap, BQ25792_REG14_CHARGER_CONTROL_5,
+		     BQ25792_REG14_SFET_PRESENT |
+		     BQ25792_REG14_EN_IBAT |
+		     BQ25792_IBAT_5A |
+		     BQ25792_REG14_EN_IINDPM);
+
+	ret = pdata->chip->bq257xx_set_ichg(pdata, pdata->ichg_max);
+	if (ret)
+		return ret;
+
+	ret = pdata->chip->bq257xx_set_vbatreg(pdata, pdata->vbat_max);
+	if (ret)
+		return ret;
+
+	ret = bq25792_set_min_vsys(pdata, pdata->vsys_min);
+	if (ret)
+		return ret;
+
+	ret = pdata->chip->bq257xx_set_iindpm(pdata, pdata->iindpm_max);
+	if (ret)
+		return ret;
+
+	/* Enable the ADC. */
+	regmap_write(regmap, BQ25792_REG2E_ADC_CONTROL, BQ25792_REG2E_ADC_EN);
+
+	/* Clear per-channel ADC disable bits - enable all channels */
+	regmap_write(regmap, BQ25792_REG2F_ADC_FUNCTION_DISABLE_0, 0);
+	regmap_write(regmap, BQ25792_REG30_ADC_FUNCTION_DISABLE_1, 0);
+
+	return ret;
+}
+
 /**
  * bq25703_hw_shutdown() - Set registers for shutdown
  * @pdata: driver platform data
@@ -446,6 +823,30 @@ static void bq25703_hw_shutdown(struct bq257xx_chg *pdata)
 			   BQ25703_EN_LWPWR, BQ25703_EN_LWPWR);
 }
 
+/**
+ * bq25792_hw_shutdown() - Shutdown BQ25792 hardware
+ * @pdata: driver platform data
+ *
+ * Perform hardware shutdown for the BQ25792. Currently a no-op
+ * as the device does not require special shutdown configuration.
+ */
+static void bq25792_hw_shutdown(struct bq257xx_chg *pdata)
+{
+	/* Nothing to do here */
+}
+
+/**
+ * bq257xx_set_charger_property() - Set a power supply property
+ * @psy: power supply device
+ * @prop: power supply property to set
+ * @val: value to set for the property
+ *
+ * Handle requests to set power supply properties such as input current
+ * limit, constant charge voltage, and constant charge current. Routes
+ * the request to the chip-specific implementation.
+ *
+ * Return: Returns 0 on success or -EINVAL if property is not supported.
+ */
 static int bq257xx_set_charger_property(struct power_supply *psy,
 		enum power_supply_property prop,
 		const union power_supply_propval *val)
@@ -469,6 +870,19 @@ static int bq257xx_set_charger_property(struct power_supply *psy,
 	return -EINVAL;
 }
 
+/**
+ * bq257xx_get_charger_property() - Get a power supply property
+ * @psy: power supply device
+ * @psp: power supply property to get
+ * @val: pointer to store the property value
+ *
+ * Handle requests to get power supply properties, including status,
+ * health, manufacturer, online state, and various voltage/current
+ * measurements. Reads current device state and routes chip-specific
+ * property requests to appropriate handlers.
+ *
+ * Return: Returns 0 on success or -EINVAL if property is not supported.
+ */
 static int bq257xx_get_charger_property(struct power_supply *psy,
 				enum power_supply_property psp,
 				union power_supply_propval *val)
@@ -550,6 +964,17 @@ static enum power_supply_property bq257xx_power_supply_props[] = {
 	POWER_SUPPLY_PROP_USB_TYPE,
 };
 
+/**
+ * bq257xx_property_is_writeable() - Check if a property is writeable
+ * @psy: power supply device
+ * @prop: power supply property to check
+ *
+ * Determines which power supply properties can be written to. Only
+ * charge current limit, charge voltage limit, and input current
+ * limit are writeable.
+ *
+ * Return: Returns 1 if property is writeable, 0 otherwise.
+ */
 static int bq257xx_property_is_writeable(struct power_supply *psy,
 					 enum power_supply_property prop)
 {
@@ -622,6 +1047,17 @@ static void bq257xx_external_power_changed(struct power_supply *psy)
 	power_supply_changed(psy);
 }
 
+/**
+ * bq257xx_irq_handler_thread() - Handle charger interrupt
+ * @irq: interrupt number
+ * @private: pointer to driver private data
+ *
+ * Thread handler for charger interrupts. Triggers re-evaluation of
+ * external power status and updates power supply state in response
+ * to charger events.
+ *
+ * Return: Returns IRQ_HANDLED if interrupt was processed.
+ */
 static irqreturn_t bq257xx_irq_handler_thread(int irq, void *private)
 {
 	struct bq257xx_chg *pdata = private;
@@ -662,6 +1098,22 @@ static const struct bq257xx_chip_info bq25703_chip_info = {
 		.bq257xx_get_min_vsys = &bq25703_get_min_vsys,
 };
 
+static const struct bq257xx_chip_info bq25792_chip_info = {
+		.default_iindpm_uA = BQ25792_IINDPM_DEFAULT_UA,
+		.bq257xx_hw_init = &bq25792_hw_init,
+		.bq257xx_hw_shutdown = &bq25792_hw_shutdown,
+		.bq257xx_get_state = &bq25792_get_state,
+		.bq257xx_get_ichg = &bq25792_get_ichg_cur,
+		.bq257xx_set_ichg = &bq25792_set_ichg_cur,
+		.bq257xx_get_vbatreg = &bq25792_get_chrg_volt,
+		.bq257xx_set_vbatreg = &bq25792_set_chrg_volt,
+		.bq257xx_get_iindpm = &bq25792_get_iindpm,
+		.bq257xx_set_iindpm = &bq25792_set_iindpm,
+		.bq257xx_get_cur = &bq25792_get_cur,
+		.bq257xx_get_vbat = &bq25792_get_vbat,
+		.bq257xx_get_min_vsys = &bq25792_get_min_vsys,
+};
+
 /**
  * bq257xx_parse_dt() - Parse the device tree for required properties
  * @pdata: driver platform data
@@ -707,6 +1159,17 @@ static int bq257xx_parse_dt(struct bq257xx_chg *pdata,
 	return 0;
 }
 
+/**
+ * bq257xx_charger_probe() - Probe routine for charger platform device
+ * @pdev: platform device
+ *
+ * Probe the charger device, allocate driver data structure, select the
+ * appropriate chip-specific function pointers, register the power supply,
+ * parse device tree properties for battery limits, initialize hardware,
+ * and set up the interrupt handler if available.
+ *
+ * Return: Returns 0 on success or error code on failure.
+ */
 static int bq257xx_charger_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -722,7 +1185,17 @@ static int bq257xx_charger_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	pdata->bq = bq;
-	pdata->chip = &bq25703_chip_info;
+
+	switch (bq->type) {
+	case BQ25703A:
+		pdata->chip = &bq25703_chip_info;
+		break;
+	case BQ25792:
+		pdata->chip = &bq25792_chip_info;
+		break;
+	default:
+		return dev_err_probe(dev, -EINVAL, "Unknown chip type\n");
+	}
 
 	platform_set_drvdata(pdev, pdata);
 
@@ -760,6 +1233,13 @@ static int bq257xx_charger_probe(struct platform_device *pdev)
 	return ret;
 }
 
+/**
+ * bq257xx_charger_shutdown() - Shutdown routine for charger platform device
+ * @pdev: platform device
+ *
+ * Called during system shutdown to perform charger cleanup, including
+ * disabling watchdog timers or other chip-specific shutdown procedures.
+ */
 static void bq257xx_charger_shutdown(struct platform_device *pdev)
 {
 	struct bq257xx_chg *pdata = platform_get_drvdata(pdev);

-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 10/11] regulator: bq257xx: Add support for BQ25792
From: Alexey Charkov @ 2026-03-24 11:38 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Chris Morgan, Liam Girdwood, Mark Brown, Sebastian Reichel
  Cc: devicetree, linux-kernel, Sebastian Reichel, linux-pm,
	Alexey Charkov
In-Reply-To: <20260324-bq25792-v5-0-0a2eb58cf11d@flipper.net>

Add support for TI BQ25792, an integrated battery charger and buck/boost
regulator. This enables VBUS output from the charger's boost converter
for use in USB OTG applications, supporting 2.8-22V output at up to 3.32A
with 10mV and 40mA resolution.

Acked-by: Mark Brown <broonie@kernel.org>
Tested-by: Chris Morgan <macromorgan@hotmail.com>
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
 drivers/regulator/bq257xx-regulator.c | 98 ++++++++++++++++++++++++++++++++++-
 1 file changed, 97 insertions(+), 1 deletion(-)

diff --git a/drivers/regulator/bq257xx-regulator.c b/drivers/regulator/bq257xx-regulator.c
index 0bb58ab4b8d4..f197162382cc 100644
--- a/drivers/regulator/bq257xx-regulator.c
+++ b/drivers/regulator/bq257xx-regulator.c
@@ -31,6 +31,32 @@ static int bq25703_vbus_get_cur_limit(struct regulator_dev *rdev)
 	return FIELD_GET(BQ25703_OTG_CUR_MASK, reg) * BQ25703_OTG_CUR_STEP_UA;
 }
 
+static int bq25792_vbus_get_cur_limit(struct regulator_dev *rdev)
+{
+	struct regmap *regmap = rdev_get_regmap(rdev);
+	int ret;
+	unsigned int reg;
+
+	ret = regmap_read(regmap, BQ25792_REG0D_IOTG_REGULATION, &reg);
+	if (ret)
+		return ret;
+	return FIELD_GET(BQ25792_REG0D_IOTG_MASK, reg) * BQ25792_OTG_CUR_STEP_UA;
+}
+
+static int bq25792_vbus_get_voltage_sel(struct regulator_dev *rdev)
+{
+	struct regmap *regmap = rdev_get_regmap(rdev);
+	__be16 reg;
+	int ret;
+
+	ret = regmap_raw_read(regmap, BQ25792_REG0B_VOTG_REGULATION,
+			      &reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	return FIELD_GET(BQ25792_REG0B_VOTG_MASK, be16_to_cpu(reg));
+}
+
 /*
  * Check if the minimum current and maximum current requested are
  * sane values, then set the register accordingly.
@@ -54,6 +80,37 @@ static int bq25703_vbus_set_cur_limit(struct regulator_dev *rdev,
 			    FIELD_PREP(BQ25703_OTG_CUR_MASK, reg));
 }
 
+static int bq25792_vbus_set_cur_limit(struct regulator_dev *rdev,
+				      int min_uA, int max_uA)
+{
+	struct regmap *regmap = rdev_get_regmap(rdev);
+	unsigned int reg;
+
+	if ((min_uA > BQ25792_OTG_CUR_MAX_UA) ||
+	    (max_uA < BQ25792_OTG_CUR_MIN_UA))
+		return -EINVAL;
+
+	reg = (max_uA / BQ25792_OTG_CUR_STEP_UA);
+
+	/* Catch rounding errors since our step is 40000uA. */
+	if ((reg * BQ25792_OTG_CUR_STEP_UA) < min_uA)
+		return -EINVAL;
+
+	return regmap_write(regmap, BQ25792_REG0D_IOTG_REGULATION,
+			    FIELD_PREP(BQ25792_REG0D_IOTG_MASK, reg));
+}
+
+static int bq25792_vbus_set_voltage_sel(struct regulator_dev *rdev,
+					unsigned int sel)
+{
+	struct regmap *regmap = rdev_get_regmap(rdev);
+	__be16 reg;
+
+	reg = cpu_to_be16(FIELD_PREP(BQ25792_REG0B_VOTG_MASK, sel));
+	return regmap_raw_write(regmap, BQ25792_REG0B_VOTG_REGULATION,
+				&reg, sizeof(reg));
+}
+
 static int bq25703_vbus_enable(struct regulator_dev *rdev)
 {
 	struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev);
@@ -101,6 +158,34 @@ static const struct regulator_desc bq25703_vbus_desc = {
 	.vsel_mask = BQ25703_OTG_VOLT_MASK,
 };
 
+static const struct regulator_ops bq25792_vbus_ops = {
+	/* No GPIO for enabling the OTG regulator */
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+	.list_voltage = regulator_list_voltage_linear,
+	.get_voltage_sel = bq25792_vbus_get_voltage_sel,
+	.set_voltage_sel = bq25792_vbus_set_voltage_sel,
+	.get_current_limit = bq25792_vbus_get_cur_limit,
+	.set_current_limit = bq25792_vbus_set_cur_limit,
+};
+
+static const struct regulator_desc bq25792_vbus_desc = {
+	.name = "vbus",
+	.of_match = of_match_ptr("vbus"),
+	.regulators_node = of_match_ptr("regulators"),
+	.type = REGULATOR_VOLTAGE,
+	.owner = THIS_MODULE,
+	.ops = &bq25792_vbus_ops,
+	.min_uV = BQ25792_OTG_VOLT_MIN_UV,
+	.uV_step = BQ25792_OTG_VOLT_STEP_UV,
+	.n_voltages = BQ25792_OTG_VOLT_NUM_VOLT,
+	.enable_mask = BQ25792_REG12_EN_OTG,
+	.enable_reg = BQ25792_REG12_CHARGER_CONTROL_3,
+	.enable_val = BQ25792_REG12_EN_OTG,
+	.disable_val = 0,
+};
+
 /* Get optional GPIO for OTG regulator enable. */
 static void bq257xx_reg_dt_parse_gpio(struct platform_device *pdev)
 {
@@ -141,6 +226,7 @@ static void bq257xx_reg_dt_parse_gpio(struct platform_device *pdev)
 static int bq257xx_regulator_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
+	struct bq257xx_device *bq = dev_get_drvdata(pdev->dev.parent);
 	struct bq257xx_reg_data *pdata;
 	struct device_node *np = dev->of_node;
 	struct regulator_config cfg = {};
@@ -153,7 +239,17 @@ static int bq257xx_regulator_probe(struct platform_device *pdev)
 	if (!pdata)
 		return -ENOMEM;
 
-	pdata->desc = bq25703_vbus_desc;
+	switch (bq->type) {
+	case BQ25703A:
+		pdata->desc = bq25703_vbus_desc;
+		break;
+	case BQ25792:
+		pdata->desc = bq25792_vbus_desc;
+		break;
+	default:
+		return dev_err_probe(&pdev->dev, -EINVAL,
+				     "Unsupported device type\n");
+	}
 
 	platform_set_drvdata(pdev, pdata);
 	bq257xx_reg_dt_parse_gpio(pdev);

-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 09/11] mfd: bq257xx: Add BQ25792 support
From: Alexey Charkov @ 2026-03-24 11:38 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Chris Morgan, Liam Girdwood, Mark Brown, Sebastian Reichel
  Cc: devicetree, linux-kernel, Sebastian Reichel, linux-pm,
	Alexey Charkov
In-Reply-To: <20260324-bq25792-v5-0-0a2eb58cf11d@flipper.net>

Add register definitions and a new 'type' enum to be passed via MFD
private data to support the BQ25792, which is a newer variant of the
BQ257xx family.

BQ25792 shares similar logic of operation with the already supported
BQ25703A but has a completely different register map and different
electrical constraints.

Tested-by: Chris Morgan <macromorgan@hotmail.com>
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
 drivers/mfd/bq257xx.c       |  54 +++++-
 include/linux/mfd/bq257xx.h | 412 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 463 insertions(+), 3 deletions(-)

diff --git a/drivers/mfd/bq257xx.c b/drivers/mfd/bq257xx.c
index e9d49dac0a16..31654925afa5 100644
--- a/drivers/mfd/bq257xx.c
+++ b/drivers/mfd/bq257xx.c
@@ -39,6 +39,39 @@ static const struct regmap_config bq25703_regmap_config = {
 	.val_format_endian = REGMAP_ENDIAN_LITTLE,
 };
 
+static const struct regmap_range bq25792_writeable_reg_ranges[] = {
+	regmap_reg_range(BQ25792_REG00_MIN_SYS_VOLTAGE,
+			 BQ25792_REG18_NTC_CONTROL_1),
+	regmap_reg_range(BQ25792_REG28_CHARGER_MASK_0,
+			 BQ25792_REG30_ADC_FUNCTION_DISABLE_1),
+};
+
+static const struct regmap_access_table bq25792_writeable_regs = {
+	.yes_ranges = bq25792_writeable_reg_ranges,
+	.n_yes_ranges = ARRAY_SIZE(bq25792_writeable_reg_ranges),
+};
+
+static const struct regmap_range bq25792_volatile_reg_ranges[] = {
+	regmap_reg_range(BQ25792_REG19_ICO_CURRENT_LIMIT,
+			 BQ25792_REG27_FAULT_FLAG_1),
+	regmap_reg_range(BQ25792_REG31_IBUS_ADC,
+			 BQ25792_REG47_DPDM_DRIVER),
+};
+
+static const struct regmap_access_table bq25792_volatile_regs = {
+	.yes_ranges = bq25792_volatile_reg_ranges,
+	.n_yes_ranges = ARRAY_SIZE(bq25792_volatile_reg_ranges),
+};
+
+static const struct regmap_config bq25792_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = BQ25792_REG48_PART_INFORMATION,
+	.cache_type = REGCACHE_MAPLE,
+	.wr_table = &bq25792_writeable_regs,
+	.volatile_table = &bq25792_volatile_regs,
+};
+
 static const struct mfd_cell cells[] = {
 	MFD_CELL_NAME("bq257xx-regulator"),
 	MFD_CELL_NAME("bq257xx-charger"),
@@ -46,6 +79,7 @@ static const struct mfd_cell cells[] = {
 
 static int bq257xx_probe(struct i2c_client *client)
 {
+	const struct regmap_config *rcfg;
 	struct bq257xx_device *ddata;
 	int ret;
 
@@ -53,9 +87,21 @@ static int bq257xx_probe(struct i2c_client *client)
 	if (!ddata)
 		return -ENOMEM;
 
+	ddata->type = (uintptr_t)i2c_get_match_data(client);
 	ddata->client = client;
 
-	ddata->regmap = devm_regmap_init_i2c(client, &bq25703_regmap_config);
+	switch (ddata->type) {
+	case BQ25703A:
+		rcfg = &bq25703_regmap_config;
+		break;
+	case BQ25792:
+		rcfg = &bq25792_regmap_config;
+		break;
+	default:
+		return dev_err_probe(&client->dev, -EINVAL, "Unsupported device type\n");
+	}
+
+	ddata->regmap = devm_regmap_init_i2c(client, rcfg);
 	if (IS_ERR(ddata->regmap)) {
 		return dev_err_probe(&client->dev, PTR_ERR(ddata->regmap),
 				     "Failed to allocate register map\n");
@@ -73,13 +119,15 @@ static int bq257xx_probe(struct i2c_client *client)
 }
 
 static const struct i2c_device_id bq257xx_i2c_ids[] = {
-	{ "bq25703a" },
+	{ "bq25703a", BQ25703A },
+	{ "bq25792", BQ25792 },
 	{}
 };
 MODULE_DEVICE_TABLE(i2c, bq257xx_i2c_ids);
 
 static const struct of_device_id bq257xx_of_match[] = {
-	{ .compatible = "ti,bq25703a" },
+	{ .compatible = "ti,bq25703a", .data = (void *)BQ25703A },
+	{ .compatible = "ti,bq25792", .data = (void *)BQ25792 },
 	{}
 };
 MODULE_DEVICE_TABLE(of, bq257xx_of_match);
diff --git a/include/linux/mfd/bq257xx.h b/include/linux/mfd/bq257xx.h
index 1d6ddc7fb09f..4ec72eb920f2 100644
--- a/include/linux/mfd/bq257xx.h
+++ b/include/linux/mfd/bq257xx.h
@@ -98,7 +98,419 @@
 
 #define BQ25703_EN_OTG_MASK			BIT(12)
 
+#define BQ25792_REG00_MIN_SYS_VOLTAGE		0x00
+#define BQ25792_REG01_CHARGE_VOLTAGE_LIMIT	0x01
+#define BQ25792_REG03_CHARGE_CURRENT_LIMIT	0x03
+#define BQ25792_REG05_INPUT_VOLTAGE_LIMIT	0x05
+#define BQ25792_REG06_INPUT_CURRENT_LIMIT	0x06
+#define BQ25792_REG08_PRECHARGE_CONTROL		0x08
+#define BQ25792_REG09_TERMINATION_CONTROL	0x09
+#define BQ25792_REG0A_RECHARGE_CONTROL		0x0a
+#define BQ25792_REG0B_VOTG_REGULATION		0x0b
+#define BQ25792_REG0D_IOTG_REGULATION		0x0d
+#define BQ25792_REG0E_TIMER_CONTROL		0x0e
+#define BQ25792_REG0F_CHARGER_CONTROL_0		0x0f
+#define BQ25792_REG10_CHARGER_CONTROL_1		0x10
+#define BQ25792_REG11_CHARGER_CONTROL_2		0x11
+#define BQ25792_REG12_CHARGER_CONTROL_3		0x12
+#define BQ25792_REG13_CHARGER_CONTROL_4		0x13
+#define BQ25792_REG14_CHARGER_CONTROL_5		0x14
+/* REG15 reserved */
+#define BQ25792_REG16_TEMPERATURE_CONTROL	0x16
+#define BQ25792_REG17_NTC_CONTROL_0		0x17
+#define BQ25792_REG18_NTC_CONTROL_1		0x18
+#define BQ25792_REG19_ICO_CURRENT_LIMIT		0x19
+#define BQ25792_REG1B_CHARGER_STATUS_0		0x1b
+#define BQ25792_REG1C_CHARGER_STATUS_1		0x1c
+#define BQ25792_REG1D_CHARGER_STATUS_2		0x1d
+#define BQ25792_REG1E_CHARGER_STATUS_3		0x1e
+#define BQ25792_REG1F_CHARGER_STATUS_4		0x1f
+#define BQ25792_REG20_FAULT_STATUS_0		0x20
+#define BQ25792_REG21_FAULT_STATUS_1		0x21
+#define BQ25792_REG22_CHARGER_FLAG_0		0x22
+#define BQ25792_REG23_CHARGER_FLAG_1		0x23
+#define BQ25792_REG24_CHARGER_FLAG_2		0x24
+#define BQ25792_REG25_CHARGER_FLAG_3		0x25
+#define BQ25792_REG26_FAULT_FLAG_0		0x26
+#define BQ25792_REG27_FAULT_FLAG_1		0x27
+#define BQ25792_REG28_CHARGER_MASK_0		0x28
+#define BQ25792_REG29_CHARGER_MASK_1		0x29
+#define BQ25792_REG2A_CHARGER_MASK_2		0x2a
+#define BQ25792_REG2B_CHARGER_MASK_3		0x2b
+#define BQ25792_REG2C_FAULT_MASK_0		0x2c
+#define BQ25792_REG2D_FAULT_MASK_1		0x2d
+#define BQ25792_REG2E_ADC_CONTROL		0x2e
+#define BQ25792_REG2F_ADC_FUNCTION_DISABLE_0	0x2f
+#define BQ25792_REG30_ADC_FUNCTION_DISABLE_1	0x30
+#define BQ25792_REG31_IBUS_ADC			0x31
+#define BQ25792_REG33_IBAT_ADC			0x33
+#define BQ25792_REG35_VBUS_ADC			0x35
+#define BQ25792_REG37_VAC1_ADC			0x37
+#define BQ25792_REG39_VAC2_ADC			0x39
+#define BQ25792_REG3B_VBAT_ADC			0x3b
+#define BQ25792_REG3D_VSYS_ADC			0x3d
+#define BQ25792_REG3F_TS_ADC			0x3f
+#define BQ25792_REG41_TDIE_ADC			0x41
+#define BQ25792_REG43_DP_ADC			0x43
+#define BQ25792_REG45_DM_ADC			0x45
+#define BQ25792_REG47_DPDM_DRIVER		0x47
+#define BQ25792_REG48_PART_INFORMATION		0x48
+
+/* Minimal System Voltage */
+#define BQ25792_REG00_VSYSMIN_MASK		GENMASK(5, 0)
+
+#define BQ25792_MINVSYS_MIN_UV			2500000
+#define BQ25792_MINVSYS_STEP_UV			250000
+#define BQ25792_MINVSYS_MAX_UV			16000000
+
+/* Charge Voltage Limit */
+#define BQ25792_REG01_VREG_MASK			GENMASK(10, 0)
+
+#define BQ25792_VBATREG_MIN_UV			3000000
+#define BQ25792_VBATREG_STEP_UV			10000
+#define BQ25792_VBATREG_MAX_UV			18800000
+
+/* Charge Current Limit */
+#define BQ25792_REG03_ICHG_MASK			GENMASK(8, 0)
+
+#define BQ25792_ICHG_MIN_UA			50000
+#define BQ25792_ICHG_STEP_UA			10000
+#define BQ25792_ICHG_MAX_UA			5000000
+
+/* Input Voltage Limit */
+#define BQ25792_REG05_VINDPM_MASK		GENMASK(7, 0)
+
+/* Input Current Limit */
+#define BQ25792_REG06_IINDPM_MASK		GENMASK(8, 0)
+#define BQ25792_IINDPM_DEFAULT_UA		3000000
+#define BQ25792_IINDPM_STEP_UA			10000
+#define BQ25792_IINDPM_MIN_UA			100000
+#define BQ25792_IINDPM_MAX_UA			3300000
+
+/* Precharge Control */
+#define BQ25792_REG08_VBAT_LOWV_MASK		GENMASK(7, 6)
+#define BQ25792_REG08_IPRECHG_MASK		GENMASK(5, 0)
+
+/* Termination Control */
+#define BQ25792_REG09_REG_RST			BIT(6)
+#define BQ25792_REG09_ITERM_MASK		GENMASK(4, 0)
+
+/* Re-charge Control */
+#define BQ25792_REG0A_CELL_MASK			GENMASK(7, 6)
+#define BQ25792_REG0A_TRECHG_MASK		GENMASK(5, 4)
+#define BQ25792_REG0A_VRECHG_MASK		GENMASK(3, 0)
+
+/* VOTG regulation */
+#define BQ25792_REG0B_VOTG_MASK			GENMASK(10, 0)
+
+#define BQ25792_OTG_VOLT_MIN_UV			2800000
+#define BQ25792_OTG_VOLT_STEP_UV		10000
+#define BQ25792_OTG_VOLT_MAX_UV			22000000
+#define BQ25792_OTG_VOLT_NUM_VOLT		((BQ25792_OTG_VOLT_MAX_UV \
+						  - BQ25792_OTG_VOLT_MIN_UV) \
+						  / BQ25792_OTG_VOLT_STEP_UV + 1)
+
+/* IOTG regulation */
+#define BQ25792_REG0D_PRECHG_TMR		BIT(7)
+#define BQ25792_REG0D_IOTG_MASK			GENMASK(6, 0)
+
+#define BQ25792_OTG_CUR_MIN_UA		120000
+#define BQ25792_OTG_CUR_STEP_UA		40000
+#define BQ25792_OTG_CUR_MAX_UA		3320000
+
+/* Timer Control */
+#define BQ25792_REG0E_TOPOFF_TMR_MASK		GENMASK(7, 6)
+#define BQ25792_REG0E_EN_TRICHG_TMR		BIT(5)
+#define BQ25792_REG0E_EN_PRECHG_TMR		BIT(4)
+#define BQ25792_REG0E_EN_CHG_TMR		BIT(3)
+#define BQ25792_REG0E_CHG_TMR_MASK		GENMASK(2, 1)
+#define BQ25792_REG0E_TMR2X_EN			BIT(0)
+
+/* Charger Control 0 */
+#define BQ25792_REG0F_EN_AUTO_IBATDIS		BIT(7)
+#define BQ25792_REG0F_FORCE_IBATDIS		BIT(6)
+#define BQ25792_REG0F_EN_CHG			BIT(5)
+#define BQ25792_REG0F_EN_ICO			BIT(4)
+#define BQ25792_REG0F_FORCE_ICO			BIT(3)
+#define BQ25792_REG0F_EN_HIZ			BIT(2)
+#define BQ25792_REG0F_EN_TERM			BIT(1)
+/* bit0 reserved */
+
+/* Charger Control 1 */
+#define BQ25792_REG10_VAC_OVP_MASK		GENMASK(5, 4)
+#define BQ25792_REG10_WD_RST			BIT(3)
+#define BQ25792_REG10_WATCHDOG_MASK		GENMASK(2, 0)
+
+/* Charger Control 2 */
+#define BQ25792_REG11_FORCE_INDET		BIT(7)
+#define BQ25792_REG11_AUTO_INDET_EN		BIT(6)
+#define BQ25792_REG11_EN_12V			BIT(5)
+#define BQ25792_REG11_EN_9V			BIT(4)
+#define BQ25792_REG11_HVDCP_EN			BIT(3)
+#define BQ25792_REG11_SDRV_CTRL_MASK		GENMASK(2, 1)
+#define BQ25792_REG11_SDRV_DLY			BIT(0)
+
+/* Charger Control 3 */
+#define BQ25792_REG12_DIS_ACDRV			BIT(7)
+#define BQ25792_REG12_EN_OTG			BIT(6)
+#define BQ25792_REG12_PFM_OTG_DIS		BIT(5)
+#define BQ25792_REG12_PFM_FWD_DIS		BIT(4)
+#define BQ25792_REG12_WKUP_DLY			BIT(3)
+#define BQ25792_REG12_DIS_LDO			BIT(2)
+#define BQ25792_REG12_DIS_OTG_OOA		BIT(1)
+#define BQ25792_REG12_DIS_FWD_OOA		BIT(0)
+
+/* Charger Control 4 */
+#define BQ25792_REG13_EN_ACDRV2			BIT(7)
+#define BQ25792_REG13_EN_ACDRV1			BIT(6)
+#define BQ25792_REG13_PWM_FREQ			BIT(5)
+#define BQ25792_REG13_DIS_STAT			BIT(4)
+#define BQ25792_REG13_DIS_VSYS_SHORT		BIT(3)
+#define BQ25792_REG13_DIS_VOTG_UVP		BIT(2)
+#define BQ25792_REG13_FORCE_VINDPM_DET		BIT(1)
+#define BQ25792_REG13_EN_IBUS_OCP		BIT(0)
+
+/* Charger Control 5 */
+#define BQ25792_REG14_SFET_PRESENT		BIT(7)
+/* bit6 reserved */
+#define BQ25792_REG14_EN_IBAT			BIT(5)
+#define BQ25792_REG14_IBAT_REG_MASK		GENMASK(4, 3)
+#define BQ25792_REG14_EN_IINDPM			BIT(2)
+#define BQ25792_REG14_EN_EXTILIM		BIT(1)
+#define BQ25792_REG14_EN_BATOC			BIT(0)
+
+#define BQ25792_IBAT_3A				FIELD_PREP(BQ25792_REG14_IBAT_REG_MASK, 0)
+#define BQ25792_IBAT_4A				FIELD_PREP(BQ25792_REG14_IBAT_REG_MASK, 1)
+#define BQ25792_IBAT_5A				FIELD_PREP(BQ25792_REG14_IBAT_REG_MASK, 2)
+#define BQ25792_IBAT_UNLIM			FIELD_PREP(BQ25792_REG14_IBAT_REG_MASK, 3)
+
+/* Temperature Control */
+#define BQ25792_REG16_TREG_MASK			GENMASK(7, 6)
+#define BQ25792_REG16_TSHUT_MASK		GENMASK(5, 4)
+#define BQ25792_REG16_VBUS_PD_EN		BIT(3)
+#define BQ25792_REG16_VAC1_PD_EN		BIT(2)
+#define BQ25792_REG16_VAC2_PD_EN		BIT(1)
+
+/* NTC Control 0 */
+#define BQ25792_REG17_JEITA_VSET_MASK		GENMASK(7, 5)
+#define BQ25792_REG17_JEITA_ISETH_MASK		GENMASK(4, 3)
+#define BQ25792_REG17_JEITA_ISETC_MASK		GENMASK(2, 1)
+
+/* NTC Control 1 */
+#define BQ25792_REG18_TS_COOL_MASK		GENMASK(7, 6)
+#define BQ25792_REG18_TS_WARM_MASK		GENMASK(5, 4)
+#define BQ25792_REG18_BHOT_MASK			GENMASK(3, 2)
+#define BQ25792_REG18_BCOLD			BIT(1)
+#define BQ25792_REG18_TS_IGNORE			BIT(0)
+
+/* ICO Current Limit */
+#define BQ25792_REG19_ICO_ILIM_MASK		GENMASK(8, 0)
+
+/* Charger Status 0 */
+#define BQ25792_REG1B_IINDPM_STAT		BIT(7)
+#define BQ25792_REG1B_VINDPM_STAT		BIT(6)
+#define BQ25792_REG1B_WD_STAT			BIT(5)
+#define BQ25792_REG1B_POORSRC_STAT		BIT(4)
+#define BQ25792_REG1B_PG_STAT			BIT(3)
+#define BQ25792_REG1B_AC2_PRESENT_STAT		BIT(2)
+#define BQ25792_REG1B_AC1_PRESENT_STAT		BIT(1)
+#define BQ25792_REG1B_VBUS_PRESENT_STAT		BIT(0)
+
+/* Charger Status 1 */
+#define BQ25792_REG1C_CHG_STAT_MASK		GENMASK(7, 5)
+#define BQ25792_REG1C_VBUS_STAT_MASK		GENMASK(4, 1)
+#define BQ25792_REG1C_BC12_DONE_STAT		BIT(0)
+
+/* Charger Status 2 */
+#define BQ25792_REG1D_ICO_STAT_MASK		GENMASK(7, 6)
+#define BQ25792_REG1D_TREG_STAT			BIT(2)
+#define BQ25792_REG1D_DPDM_STAT			BIT(1)
+#define BQ25792_REG1D_VBAT_PRESENT_STAT		BIT(0)
+
+/* Charger Status 3 */
+#define BQ25792_REG1E_ACRB2_STAT		BIT(7)
+#define BQ25792_REG1E_ACRB1_STAT		BIT(6)
+#define BQ25792_REG1E_ADC_DONE_STAT		BIT(5)
+#define BQ25792_REG1E_VSYS_STAT			BIT(4)
+#define BQ25792_REG1E_CHG_TMR_STAT		BIT(3)
+#define BQ25792_REG1E_TRICHG_TMR_STAT		BIT(2)
+#define BQ25792_REG1E_PRECHG_TMR_STAT		BIT(1)
+
+/* Charger Status 4 */
+#define BQ25792_REG1F_VBATOTG_LOW_STAT		BIT(4)
+#define BQ25792_REG1F_TS_COLD_STAT		BIT(3)
+#define BQ25792_REG1F_TS_COOL_STAT		BIT(2)
+#define BQ25792_REG1F_TS_WARM_STAT		BIT(1)
+#define BQ25792_REG1F_TS_HOT_STAT		BIT(0)
+
+/* FAULT Status 0 */
+#define BQ25792_REG20_IBAT_REG_STAT		BIT(7)
+#define BQ25792_REG20_VBUS_OVP_STAT		BIT(6)
+#define BQ25792_REG20_VBAT_OVP_STAT		BIT(5)
+#define BQ25792_REG20_IBUS_OCP_STAT		BIT(4)
+#define BQ25792_REG20_IBAT_OCP_STAT		BIT(3)
+#define BQ25792_REG20_CONV_OCP_STAT		BIT(2)
+#define BQ25792_REG20_VAC2_OVP_STAT		BIT(1)
+#define BQ25792_REG20_VAC1_OVP_STAT		BIT(0)
+
+#define BQ25792_REG20_OVERVOLTAGE_MASK		(BQ25792_REG20_VBAT_OVP_STAT | \
+						 BQ25792_REG20_VAC2_OVP_STAT | \
+						 BQ25792_REG20_VAC1_OVP_STAT)
+#define BQ25792_REG20_OVERCURRENT_MASK		(BQ25792_REG20_IBAT_OCP_STAT | \
+						 BQ25792_REG20_CONV_OCP_STAT)
+
+/* FAULT Status 1 */
+#define BQ25792_REG21_VSYS_SHORT_STAT		BIT(7)
+#define BQ25792_REG21_VSYS_OVP_STAT		BIT(6)
+#define BQ25792_REG21_OTG_OVP_STAT		BIT(5)
+#define BQ25792_REG21_OTG_UVP_STAT		BIT(4)
+#define BQ25792_REG21_TSHUT_STAT		BIT(2)
+
+
+/* Charger Flag 0 */
+#define BQ25792_REG22_IINDPM_FLAG		BIT(7)
+#define BQ25792_REG22_VINDPM_FLAG		BIT(6)
+#define BQ25792_REG22_WD_FLAG			BIT(5)
+#define BQ25792_REG22_POORSRC_FLAG		BIT(4)
+#define BQ25792_REG22_PG_FLAG			BIT(3)
+#define BQ25792_REG22_AC2_PRESENT_FLAG		BIT(2)
+#define BQ25792_REG22_AC1_PRESENT_FLAG		BIT(1)
+#define BQ25792_REG22_VBUS_PRESENT_FLAG		BIT(0)
+
+/* Charger Flag 1 */
+#define BQ25792_REG23_CHG_FLAG			BIT(7)
+#define BQ25792_REG23_ICO_FLAG			BIT(6)
+#define BQ25792_REG23_VBUS_FLAG			BIT(4)
+#define BQ25792_REG23_TREG_FLAG			BIT(2)
+#define BQ25792_REG23_VBAT_PRESENT_FLAG		BIT(1)
+#define BQ25792_REG23_BC12_DONE_FLAG		BIT(0)
+
+/* Charger Flag 2 */
+#define BQ25792_REG24_DPDM_DONE_FLAG		BIT(6)
+#define BQ25792_REG24_ADC_DONE_FLAG		BIT(5)
+#define BQ25792_REG24_VSYS_FLAG			BIT(4)
+#define BQ25792_REG24_CHG_TMR_FLAG		BIT(3)
+#define BQ25792_REG24_TRICHG_TMR_FLAG		BIT(2)
+#define BQ25792_REG24_PRECHG_TMR_FLAG		BIT(1)
+#define BQ25792_REG24_TOPOFF_TMR_FLAG		BIT(0)
+
+/* Charger Flag 3 */
+#define BQ25792_REG25_VBATOTG_LOW_FLAG		BIT(4)
+#define BQ25792_REG25_TS_COLD_FLAG		BIT(3)
+#define BQ25792_REG25_TS_COOL_FLAG		BIT(2)
+#define BQ25792_REG25_TS_WARM_FLAG		BIT(1)
+#define BQ25792_REG25_TS_HOT_FLAG		BIT(0)
+
+/* FAULT Flag 0 */
+#define BQ25792_REG26_IBAT_REG_FLAG		BIT(7)
+#define BQ25792_REG26_VBUS_OVP_FLAG		BIT(6)
+#define BQ25792_REG26_VBAT_OVP_FLAG		BIT(5)
+#define BQ25792_REG26_IBUS_OCP_FLAG		BIT(4)
+#define BQ25792_REG26_IBAT_OCP_FLAG		BIT(3)
+#define BQ25792_REG26_CONV_OCP_FLAG		BIT(2)
+#define BQ25792_REG26_VAC2_OVP_FLAG		BIT(1)
+#define BQ25792_REG26_VAC1_OVP_FLAG		BIT(0)
+
+/* FAULT Flag 1 */
+#define BQ25792_REG27_VSYS_SHORT_FLAG		BIT(7)
+#define BQ25792_REG27_VSYS_OVP_FLAG		BIT(6)
+#define BQ25792_REG27_OTG_OVP_FLAG		BIT(5)
+#define BQ25792_REG27_OTG_UVP_FLAG		BIT(4)
+#define BQ25792_REG27_TSHUT_FLAG		BIT(2)
+
+/* Charger Mask 0 */
+#define BQ25792_REG28_IINDPM_MASK		BIT(7)
+#define BQ25792_REG28_VINDPM_MASK		BIT(6)
+#define BQ25792_REG28_WD_MASK			BIT(5)
+#define BQ25792_REG28_POORSRC_MASK		BIT(4)
+#define BQ25792_REG28_PG_MASK			BIT(3)
+#define BQ25792_REG28_AC2_PRESENT_MASK		BIT(2)
+#define BQ25792_REG28_AC1_PRESENT_MASK		BIT(1)
+#define BQ25792_REG28_VBUS_PRESENT_MASK		BIT(0)
+
+/* Charger Mask 1 */
+#define BQ25792_REG29_CHG_MASK			BIT(7)
+#define BQ25792_REG29_ICO_MASK			BIT(6)
+#define BQ25792_REG29_VBUS_MASK			BIT(4)
+#define BQ25792_REG29_TREG_MASK			BIT(2)
+#define BQ25792_REG29_VBAT_PRESENT_MASK		BIT(1)
+#define BQ25792_REG29_BC12_DONE_MASK		BIT(0)
+
+/* Charger Mask 2 */
+#define BQ25792_REG2A_DPDM_DONE_MASK		BIT(6)
+#define BQ25792_REG2A_ADC_DONE_MASK		BIT(5)
+#define BQ25792_REG2A_VSYS_MASK			BIT(4)
+#define BQ25792_REG2A_CHG_TMR_MASK		BIT(3)
+#define BQ25792_REG2A_TRICHG_TMR_MASK		BIT(2)
+#define BQ25792_REG2A_PRECHG_TMR_MASK		BIT(1)
+#define BQ25792_REG2A_TOPOFF_TMR_MASK		BIT(0)
+
+/* Charger Mask 3 */
+#define BQ25792_REG2B_VBATOTG_LOW_MASK		BIT(4)
+#define BQ25792_REG2B_TS_COLD_MASK		BIT(3)
+#define BQ25792_REG2B_TS_COOL_MASK		BIT(2)
+#define BQ25792_REG2B_TS_WARM_MASK		BIT(1)
+#define BQ25792_REG2B_TS_HOT_MASK		BIT(0)
+
+/* FAULT Mask 0 */
+#define BQ25792_REG2C_IBAT_REG_MASK		BIT(7)
+#define BQ25792_REG2C_VBUS_OVP_MASK		BIT(6)
+#define BQ25792_REG2C_VBAT_OVP_MASK		BIT(5)
+#define BQ25792_REG2C_IBUS_OCP_MASK		BIT(4)
+#define BQ25792_REG2C_IBAT_OCP_MASK		BIT(3)
+#define BQ25792_REG2C_CONV_OCP_MASK		BIT(2)
+#define BQ25792_REG2C_VAC2_OVP_MASK		BIT(1)
+#define BQ25792_REG2C_VAC1_OVP_MASK		BIT(0)
+
+/* FAULT Mask 1 */
+#define BQ25792_REG2D_VSYS_SHORT_MASK		BIT(7)
+#define BQ25792_REG2D_VSYS_OVP_MASK		BIT(6)
+#define BQ25792_REG2D_OTG_OVP_MASK		BIT(5)
+#define BQ25792_REG2D_OTG_UVP_MASK		BIT(4)
+#define BQ25792_REG2D_TSHUT_MASK		BIT(2)
+
+/* ADC Control */
+#define BQ25792_REG2E_ADC_EN			BIT(7)
+#define BQ25792_REG2E_ADC_RATE			BIT(6)
+#define BQ25792_REG2E_ADC_SAMPLE_MASK		GENMASK(5, 4)
+#define BQ25792_REG2E_ADC_AVG			BIT(3)
+#define BQ25792_REG2E_ADC_AVG_INIT		BIT(2)
+
+/* ADC Function Disable 0 */
+#define BQ25792_REG2F_IBUS_ADC_DIS		BIT(7)
+#define BQ25792_REG2F_IBAT_ADC_DIS		BIT(6)
+#define BQ25792_REG2F_VBUS_ADC_DIS		BIT(5)
+#define BQ25792_REG2F_VBAT_ADC_DIS		BIT(4)
+#define BQ25792_REG2F_VSYS_ADC_DIS		BIT(3)
+#define BQ25792_REG2F_TS_ADC_DIS		BIT(2)
+#define BQ25792_REG2F_TDIE_ADC_DIS		BIT(1)
+
+/* ADC Function Disable 1 */
+#define BQ25792_REG30_DP_ADC_DIS		BIT(7)
+#define BQ25792_REG30_DM_ADC_DIS		BIT(6)
+#define BQ25792_REG30_VAC2_ADC_DIS		BIT(5)
+#define BQ25792_REG30_VAC1_ADC_DIS		BIT(4)
+
+/* 0x31-0x45: ADC result registers (16-bit, RO): single full-width field */
+
+#define BQ25792_ADCVSYSVBAT_STEP_UV		1000
+#define BQ25792_ADCIBAT_STEP_UA			1000
+
+/* DPDM Driver */
+#define BQ25792_REG47_DPLUS_DAC_MASK		GENMASK(7, 5)
+#define BQ25792_REG47_DMINUS_DAC_MASK		GENMASK(4, 2)
+
+/* Part Information */
+#define BQ25792_REG48_PN_MASK			GENMASK(5, 3)
+#define BQ25792_REG48_DEV_REV_MASK		GENMASK(2, 0)
+
+enum bq257xx_type {
+	BQ25703A = 1,
+	BQ25792,
+};
+
 struct bq257xx_device {
 	struct i2c_client *client;
 	struct regmap *regmap;
+	enum bq257xx_type type;
 };

-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 08/11] power: supply: bq257xx: Add fields for 'charging' and 'overvoltage' states
From: Alexey Charkov @ 2026-03-24 11:38 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Chris Morgan, Liam Girdwood, Mark Brown, Sebastian Reichel
  Cc: devicetree, linux-kernel, Sebastian Reichel, linux-pm,
	Alexey Charkov
In-Reply-To: <20260324-bq25792-v5-0-0a2eb58cf11d@flipper.net>

The driver currently reports the 'charging' and 'overvoltage' states based
on a logical expression in the get_charger_property() wrapper function.
This doesn't scale well to other chip variants, which may have a different
number and type of hardware reported conditions which fall into these
broad power supply states.

Move the logic for determining 'charging' and 'overvoltage' states into
chip-specific accessors, which can be overridden by each variant as
needed.

This helps keep the get_charger_property() wrapper function chip-agnostic
while allowing for new chip variants to be added bringing their own logic.

Tested-by: Chris Morgan <macromorgan@hotmail.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
 drivers/power/supply/bq257xx_charger.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/power/supply/bq257xx_charger.c b/drivers/power/supply/bq257xx_charger.c
index 0765673728e4..9c082865e745 100644
--- a/drivers/power/supply/bq257xx_charger.c
+++ b/drivers/power/supply/bq257xx_charger.c
@@ -54,8 +54,10 @@ struct bq257xx_chip_info {
  * @bq: parent MFD device
  * @charger: power supply device
  * @online: charger input is present
+ * @charging: charger is actively charging the battery
  * @fast_charge: charger is in fast charge mode
  * @pre_charge: charger is in pre-charge mode
+ * @overvoltage: overvoltage fault detected
  * @ov_fault: charger reports over voltage fault
  * @batoc_fault: charger reports battery over current fault
  * @oc_fault: charger reports over current fault
@@ -71,8 +73,10 @@ struct bq257xx_chg {
 	struct bq257xx_device *bq;
 	struct power_supply *charger;
 	bool online;
+	bool charging;
 	bool fast_charge;
 	bool pre_charge;
+	bool overvoltage;
 	bool ov_fault;
 	bool batoc_fault;
 	bool oc_fault;
@@ -106,8 +110,10 @@ static int bq25703_get_state(struct bq257xx_chg *pdata)
 	pdata->online = reg & BQ25703_STS_AC_STAT;
 	pdata->fast_charge = reg & BQ25703_STS_IN_FCHRG;
 	pdata->pre_charge = reg & BQ25703_STS_IN_PCHRG;
+	pdata->charging = pdata->fast_charge || pdata->pre_charge;
 	pdata->ov_fault = reg & BQ25703_STS_FAULT_ACOV;
 	pdata->batoc_fault = reg & BQ25703_STS_FAULT_BATOC;
+	pdata->overvoltage = pdata->ov_fault || pdata->batoc_fault;
 	pdata->oc_fault = reg & BQ25703_STS_FAULT_ACOC;
 
 	return 0;
@@ -478,14 +484,14 @@ static int bq257xx_get_charger_property(struct power_supply *psy,
 	case POWER_SUPPLY_PROP_STATUS:
 		if (!pdata->online)
 			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-		else if (pdata->fast_charge || pdata->pre_charge)
+		else if (pdata->charging)
 			val->intval = POWER_SUPPLY_STATUS_CHARGING;
 		else
 			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
 		break;
 
 	case POWER_SUPPLY_PROP_HEALTH:
-		if (pdata->ov_fault || pdata->batoc_fault)
+		if (pdata->overvoltage)
 			val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
 		else if (pdata->oc_fault)
 			val->intval = POWER_SUPPLY_HEALTH_OVERCURRENT;

-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 07/11] power: supply: bq257xx: Consistently use indirect get/set helpers
From: Alexey Charkov @ 2026-03-24 11:38 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Chris Morgan, Liam Girdwood, Mark Brown, Sebastian Reichel
  Cc: devicetree, linux-kernel, Sebastian Reichel, linux-pm,
	Alexey Charkov
In-Reply-To: <20260324-bq25792-v5-0-0a2eb58cf11d@flipper.net>

Move the remaining get/set helper functions to indirect calls via the
per-chip bq257xx_chip_info struct.

This improves the consistency of the code and prepares the driver to
support multiple chip variants with different register layouts and bit
definitions.

Tested-by: Chris Morgan <macromorgan@hotmail.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
 drivers/power/supply/bq257xx_charger.c | 30 ++++++++++++++++++++++++------
 1 file changed, 24 insertions(+), 6 deletions(-)

diff --git a/drivers/power/supply/bq257xx_charger.c b/drivers/power/supply/bq257xx_charger.c
index 39718223c3f9..0765673728e4 100644
--- a/drivers/power/supply/bq257xx_charger.c
+++ b/drivers/power/supply/bq257xx_charger.c
@@ -22,18 +22,30 @@ struct bq257xx_chg;
  * @bq257xx_hw_init: init function for hw
  * @bq257xx_hw_shutdown: shutdown function for hw
  * @bq257xx_get_state: get and update state of hardware
+ * @bq257xx_get_ichg: get maximum charge current (in uA)
  * @bq257xx_set_ichg: set maximum charge current (in uA)
+ * @bq257xx_get_vbatreg: get maximum charge voltage (in uV)
  * @bq257xx_set_vbatreg: set maximum charge voltage (in uV)
+ * @bq257xx_get_iindpm: get maximum input current (in uA)
  * @bq257xx_set_iindpm: set maximum input current (in uA)
+ * @bq257xx_get_cur: get battery current from ADC (in uA)
+ * @bq257xx_get_vbat: get battery voltage from ADC (in uV)
+ * @bq257xx_get_min_vsys: get minimum system voltage (in uV)
  */
 struct bq257xx_chip_info {
 	int default_iindpm_uA;
 	int (*bq257xx_hw_init)(struct bq257xx_chg *pdata);
 	void (*bq257xx_hw_shutdown)(struct bq257xx_chg *pdata);
 	int (*bq257xx_get_state)(struct bq257xx_chg *pdata);
+	int (*bq257xx_get_ichg)(struct bq257xx_chg *pdata, int *intval);
 	int (*bq257xx_set_ichg)(struct bq257xx_chg *pdata, int ichg);
+	int (*bq257xx_get_vbatreg)(struct bq257xx_chg *pdata, int *intval);
 	int (*bq257xx_set_vbatreg)(struct bq257xx_chg *pdata, int vbatreg);
+	int (*bq257xx_get_iindpm)(struct bq257xx_chg *pdata, int *intval);
 	int (*bq257xx_set_iindpm)(struct bq257xx_chg *pdata, int iindpm);
+	int (*bq257xx_get_cur)(struct bq257xx_chg *pdata, int *intval);
+	int (*bq257xx_get_vbat)(struct bq257xx_chg *pdata, int *intval);
+	int (*bq257xx_get_min_vsys)(struct bq257xx_chg *pdata, int *intval);
 };
 
 /**
@@ -490,22 +502,22 @@ static int bq257xx_get_charger_property(struct power_supply *psy,
 		break;
 
 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
-		return bq25703_get_iindpm(pdata, &val->intval);
+		return pdata->chip->bq257xx_get_iindpm(pdata, &val->intval);
 
 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
-		return bq25703_get_chrg_volt(pdata, &val->intval);
+		return pdata->chip->bq257xx_get_vbatreg(pdata, &val->intval);
 
 	case POWER_SUPPLY_PROP_CURRENT_NOW:
-		return bq25703_get_cur(pdata, &val->intval);
+		return pdata->chip->bq257xx_get_cur(pdata, &val->intval);
 
 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-		return bq25703_get_vbat(pdata, &val->intval);
+		return pdata->chip->bq257xx_get_vbat(pdata, &val->intval);
 
 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
-		return bq25703_get_ichg_cur(pdata, &val->intval);
+		return pdata->chip->bq257xx_get_ichg(pdata, &val->intval);
 
 	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
-		return bq25703_get_min_vsys(pdata, &val->intval);
+		return pdata->chip->bq257xx_get_min_vsys(pdata, &val->intval);
 
 	case POWER_SUPPLY_PROP_USB_TYPE:
 		val->intval = pdata->usb_type;
@@ -633,9 +645,15 @@ static const struct bq257xx_chip_info bq25703_chip_info = {
 		.bq257xx_hw_init = &bq25703_hw_init,
 		.bq257xx_hw_shutdown = &bq25703_hw_shutdown,
 		.bq257xx_get_state = &bq25703_get_state,
+		.bq257xx_get_ichg = &bq25703_get_ichg_cur,
 		.bq257xx_set_ichg = &bq25703_set_ichg_cur,
+		.bq257xx_get_vbatreg = &bq25703_get_chrg_volt,
 		.bq257xx_set_vbatreg = &bq25703_set_chrg_volt,
+		.bq257xx_get_iindpm = &bq25703_get_iindpm,
 		.bq257xx_set_iindpm = &bq25703_set_iindpm,
+		.bq257xx_get_cur = &bq25703_get_cur,
+		.bq257xx_get_vbat = &bq25703_get_vbat,
+		.bq257xx_get_min_vsys = &bq25703_get_min_vsys,
 };
 
 /**

-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 06/11] power: supply: bq257xx: Make the default current limit a per-chip attribute
From: Alexey Charkov @ 2026-03-24 11:38 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Chris Morgan, Liam Girdwood, Mark Brown, Sebastian Reichel
  Cc: devicetree, linux-kernel, Sebastian Reichel, linux-pm,
	Alexey Charkov
In-Reply-To: <20260324-bq25792-v5-0-0a2eb58cf11d@flipper.net>

Add a field for the default current limit to the bq257xx_info structure and
use it instead of the hardcoded value in the probe function.

This prepares the driver for allowing different electrical constraints for
different chip variants.

Tested-by: Chris Morgan <macromorgan@hotmail.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
 drivers/power/supply/bq257xx_charger.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/power/supply/bq257xx_charger.c b/drivers/power/supply/bq257xx_charger.c
index 7ca4ae610902..39718223c3f9 100644
--- a/drivers/power/supply/bq257xx_charger.c
+++ b/drivers/power/supply/bq257xx_charger.c
@@ -18,6 +18,7 @@ struct bq257xx_chg;
 
 /**
  * struct bq257xx_chip_info - chip specific routines
+ * @default_iindpm_uA: default input current limit in microamps
  * @bq257xx_hw_init: init function for hw
  * @bq257xx_hw_shutdown: shutdown function for hw
  * @bq257xx_get_state: get and update state of hardware
@@ -26,6 +27,7 @@ struct bq257xx_chg;
  * @bq257xx_set_iindpm: set maximum input current (in uA)
  */
 struct bq257xx_chip_info {
+	int default_iindpm_uA;
 	int (*bq257xx_hw_init)(struct bq257xx_chg *pdata);
 	void (*bq257xx_hw_shutdown)(struct bq257xx_chg *pdata);
 	int (*bq257xx_get_state)(struct bq257xx_chg *pdata);
@@ -627,6 +629,7 @@ static const struct power_supply_desc bq257xx_power_supply_desc = {
 };
 
 static const struct bq257xx_chip_info bq25703_chip_info = {
+		.default_iindpm_uA = BQ25703_IINDPM_DEFAULT_UA,
 		.bq257xx_hw_init = &bq25703_hw_init,
 		.bq257xx_hw_shutdown = &bq25703_hw_shutdown,
 		.bq257xx_get_state = &bq25703_get_state,
@@ -675,7 +678,7 @@ static int bq257xx_parse_dt(struct bq257xx_chg *pdata,
 				       "input-current-limit-microamp",
 				       &pdata->iindpm_max);
 	if (ret)
-		pdata->iindpm_max = BQ25703_IINDPM_DEFAULT_UA;
+		pdata->iindpm_max = pdata->chip->default_iindpm_uA;
 
 	return 0;
 }

-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 05/11] power: supply: bq257xx: Fix VSYSMIN clamping logic
From: Alexey Charkov @ 2026-03-24 11:38 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Chris Morgan, Liam Girdwood, Mark Brown, Sebastian Reichel
  Cc: devicetree, linux-kernel, Sebastian Reichel, linux-pm,
	Alexey Charkov, stable
In-Reply-To: <20260324-bq25792-v5-0-0a2eb58cf11d@flipper.net>

The minimal system voltage (VSYSMIN) is meant to protect the battery from
dangerous over-discharge. When the device tree provides a value for the
minimum design voltage of the battery, the user should not be allowed to
set a lower VSYSMIN, as that would defeat the purpose of this protection.

Flip the clamping logic when setting VSYSMIN to ensure that battery design
voltage is respected.

Cc: stable@vger.kernel.org
Fixes: 1cc017b7f9c7 ("power: supply: bq257xx: Add support for BQ257XX charger")
Tested-by: Chris Morgan <macromorgan@hotmail.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
 drivers/power/supply/bq257xx_charger.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/power/supply/bq257xx_charger.c b/drivers/power/supply/bq257xx_charger.c
index 02c7d8b61e82..7ca4ae610902 100644
--- a/drivers/power/supply/bq257xx_charger.c
+++ b/drivers/power/supply/bq257xx_charger.c
@@ -128,9 +128,8 @@ static int bq25703_get_min_vsys(struct bq257xx_chg *pdata, int *intval)
  * @vsys: voltage value to set in uV.
  *
  * This function takes a requested minimum system voltage value, clamps
- * it between the minimum supported value by the charger and a user
- * defined minimum system value, and then writes the value to the
- * appropriate register.
+ * it between the user defined minimum system value and the maximum supported
+ * value by the charger, and then writes the value to the appropriate register.
  *
  * Return: Returns 0 on success or error if an error occurs.
  */
@@ -139,7 +138,7 @@ static int bq25703_set_min_vsys(struct bq257xx_chg *pdata, int vsys)
 	unsigned int reg;
 	int vsys_min = pdata->vsys_min;
 
-	vsys = clamp(vsys, BQ25703_MINVSYS_MIN_UV, vsys_min);
+	vsys = clamp(vsys, vsys_min, BQ25703_MINVSYS_MAX_UV);
 	reg = ((vsys - BQ25703_MINVSYS_MIN_UV) / BQ25703_MINVSYS_STEP_UV);
 	reg = FIELD_PREP(BQ25703_MINVSYS_MASK, reg);
 

-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 04/11] regulator: bq257xx: Make OTG enable GPIO really optional
From: Alexey Charkov @ 2026-03-24 11:38 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Chris Morgan, Liam Girdwood, Mark Brown, Sebastian Reichel
  Cc: devicetree, linux-kernel, Sebastian Reichel, linux-pm,
	Alexey Charkov
In-Reply-To: <20260324-bq25792-v5-0-0a2eb58cf11d@flipper.net>

The bindings describe the OTG enable GPIO as optional, but the driver
gets upset if it's not provided in the device tree.

Make the driver accept the absence of the GPIO, and just use register
writes to handle OTG mode in that case, skipping the error message for
-ENOENT.

Acked-by: Mark Brown <broonie@kernel.org>
Tested-by: Chris Morgan <macromorgan@hotmail.com>
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
 drivers/regulator/bq257xx-regulator.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/regulator/bq257xx-regulator.c b/drivers/regulator/bq257xx-regulator.c
index 674dae6109e9..0bb58ab4b8d4 100644
--- a/drivers/regulator/bq257xx-regulator.c
+++ b/drivers/regulator/bq257xx-regulator.c
@@ -126,6 +126,12 @@ static void bq257xx_reg_dt_parse_gpio(struct platform_device *pdev)
 	of_node_put(subchild);
 
 	if (IS_ERR(pdata->otg_en_gpio)) {
+		if (PTR_ERR(pdata->otg_en_gpio) == -ENOENT) {
+			/* No GPIO, will only use register writes for OTG */
+			pdata->otg_en_gpio = NULL;
+			return;
+		}
+
 		dev_err(&pdev->dev, "Error getting enable gpio: %ld\n",
 			PTR_ERR(pdata->otg_en_gpio));
 		return;

-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 03/11] regulator: bq257xx: Drop the regulator_dev from the driver data
From: Alexey Charkov @ 2026-03-24 11:38 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Chris Morgan, Liam Girdwood, Mark Brown, Sebastian Reichel
  Cc: devicetree, linux-kernel, Sebastian Reichel, linux-pm,
	Alexey Charkov
In-Reply-To: <20260324-bq25792-v5-0-0a2eb58cf11d@flipper.net>

The field was not used anywhere in the driver, so just drop it. This helps
further slim down the platform data structure.

Acked-by: Mark Brown <broonie@kernel.org>
Tested-by: Chris Morgan <macromorgan@hotmail.com>
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
 drivers/regulator/bq257xx-regulator.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/regulator/bq257xx-regulator.c b/drivers/regulator/bq257xx-regulator.c
index 812938e565e3..674dae6109e9 100644
--- a/drivers/regulator/bq257xx-regulator.c
+++ b/drivers/regulator/bq257xx-regulator.c
@@ -15,7 +15,6 @@
 #include <linux/regulator/of_regulator.h>
 
 struct bq257xx_reg_data {
-	struct regulator_dev *bq257xx_reg;
 	struct gpio_desc *otg_en_gpio;
 	struct regulator_desc desc;
 };
@@ -139,6 +138,7 @@ static int bq257xx_regulator_probe(struct platform_device *pdev)
 	struct bq257xx_reg_data *pdata;
 	struct device_node *np = dev->of_node;
 	struct regulator_config cfg = {};
+	struct regulator_dev *rdev;
 
 	pdev->dev.of_node = pdev->dev.parent->of_node;
 	pdev->dev.of_node_reused = true;
@@ -159,9 +159,9 @@ static int bq257xx_regulator_probe(struct platform_device *pdev)
 	if (!cfg.regmap)
 		return -ENODEV;
 
-	pdata->bq257xx_reg = devm_regulator_register(dev, &pdata->desc, &cfg);
-	if (IS_ERR(pdata->bq257xx_reg)) {
-		return dev_err_probe(&pdev->dev, PTR_ERR(pdata->bq257xx_reg),
+	rdev = devm_regulator_register(dev, &pdata->desc, &cfg);
+	if (IS_ERR(rdev)) {
+		return dev_err_probe(&pdev->dev, PTR_ERR(rdev),
 				     "error registering bq257xx regulator");
 	}
 

-- 
2.52.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox