Linux Power Management development
 help / color / mirror / Atom feed
* Re: [PATCH v5 00/21] Virtual Swap Space
From: YoungJun Park @ 2026-03-25 18:53 UTC (permalink / raw)
  To: Nhat Pham
  Cc: Kairui Song, Liam.Howlett, akpm, apopple, axelrasmussen, baohua,
	baolin.wang, bhe, byungchul, cgroups, chengming.zhou, chrisl,
	corbet, david, dev.jain, gourry, hannes, hughd, jannh,
	joshua.hahnjy, 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, roman.gushchin, rppt, ryan.roberts, shakeel.butt,
	shikemeng, surenb, tglx, vbabka, weixugc, ying.huang, yosry.ahmed,
	yuanchu, zhengqi.arch, ziy, kernel-team, riel
In-Reply-To: <CAKEwX=PBjMVfMvKkNfqbgiw7o10NFyZBSB62ODzsqogv-WDYKQ@mail.gmail.com>

On Mon, Mar 23, 2026 at 11:32:57AM -0400, Nhat Pham wrote:

> Interesting. Normally "lots of zero-filled page" is a very beneficial
> case for vswap. You don't need a swapfile, or any zram/zswap metadata
> overhead - it's a native swap backend. If production workload has this
> many zero-filled pages, I think the numbers of vswap would be much
> less alarming - perhaps even matching memory overhead because you
> don't need to maintain a zram entry metadata (it's at least 2 words
> per zram entry right?), while there's no reverse map overhead induced
> (so it's 24 bytes on both side), and no need to do zram-side locking
> :)
> 
> So I was surprised to see that it's not working out very well here. I
> checked the implementation of memhog - let me know if this is wrong
> place to look:
> 
> https://man7.org/linux/man-pages/man8/memhog.8.html
> https://github.com/numactl/numactl/blob/master/memhog.c#L52
> 
> I think this is what happened here: memhog was populating the memory
> 0xff, which triggers the full overhead of a swapfile-backed swap entry
> because even though it's "same-filled" it's not zero-filled! I was
> following Usama's observation - "less than 1% of the same-filled pages
> were non-zero" - and so I only handled the zero-filled case here:
> 
> https://lore.kernel.org/all/20240530102126.357438-1-usamaarif642@gmail.com/
> 
> This sounds a bit artificial IMHO - as Usama pointed out above, I
> think most samefilled pages are zero pages, in real production
> workloads. However, if you think there are real use cases with a lot
> of non-zero samefilled pages, please let me know I can fix this real
> quick. We can support this in vswap with zero extra metadata overhead
> - change the VSWAP_ZERO swap entry type to VSWAP_SAME_FILLED, then use
> the backend field to store that value. I can send you a patch if
> you're interested.

This brings back memories -- I'm pretty sure we talked about
exactly this at LPC. Our custom swap device already handles both
zero-filled and same-filled pages on its own, so what we really
wanted was a way to tell the swap layer "just skip the detection
and let it through."
 
I looked at two approaches back then but never submitted either:
 
  - A per-swap_info flag to opt out of zero/same-filled handling.
    But this felt wrong from vswap's perspective -- if even one
    device opts out of the zeromap, the model gets messy.
 
  - Revisiting Usama's patch 2 approach.
    Sounded good in theory, but as you said,
    it's not as simple to verify in practice. And it is more clean design
    swapout time zero check as I see. So,  I gave up on it.
 
Seeing this come up again is actually kind of nice :)
 
One thought -- maybe a compile-time CONFIG or a boot param to
control the scope? e.g. zero-only, same-filled, or disabled.
That way vendors like us just turn it off, and setups like
Kairui's can opt into broader detection. Just an idea though --
open to other approaches if you have something in mind.
 
Thanks,
Youngjun Park

^ permalink raw reply

* Re: [PATCH v5 00/21] Virtual Swap Space
From: YoungJun Park @ 2026-03-25 18:36 UTC (permalink / raw)
  To: Nhat Pham
  Cc: kasong, Liam.Howlett, akpm, apopple, axelrasmussen, baohua,
	baolin.wang, bhe, byungchul, cgroups, chengming.zhou, chrisl,
	corbet, david, dev.jain, gourry, hannes, hughd, jannh,
	joshua.hahnjy, 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, roman.gushchin, rppt, ryan.roberts, shakeel.butt,
	shikemeng, surenb, tglx, vbabka, weixugc, ying.huang, yosry.ahmed,
	yuanchu, zhengqi.arch, ziy, kernel-team, riel
In-Reply-To: <20260320192735.748051-1-nphamcs@gmail.com>

On Fri, Mar 20, 2026 at 12:27:14PM -0700, Nhat Pham wrote:
> 
> This patch series is based on 6.19. There are a couple more
> swap-related changes in mainline that I would need to coordinate
> with, but I still want to send this out as an update for the
> regressions reported by Kairui Song in [15]. It's probably easier
> to just build this thing rather than dig through that series of
> emails to get the fix patch :)

Hi Nhat,

I wanted to fully understand the patches before asking questions,
but reviewing everything takes time, and I didn't want to miss the
timing. So let me share some thoughts and ask about your direction. 

These are the perspectives I'm coming from:

Pros:
- The architecture is very clean.
- Zero entries currently consume swap space, which can prevent
  actual swap usage in some cases.
- It resolves zswap's dependency on swap device size.
- And so on.

Cons:
- An additional virtual allocation step is introduced per every swap.
- not easy to merge (change swap infrastructure totally?)

To address the cons, I think if we can demonstrate that the
benefits always outweigh the costs, it could fully replace the
existing mechanism. However, if this can be applied selectively,
we get only the pros without the cons.

1. Modularization

You removed CONFIG_* and went with a unified approach. I recall
you were also considering a module-based structure at some point.
What are your thoughts on that direction?

If we take that approach, we could extend the recent swap ops
patchset (https://lore.kernel.org/linux-mm/20260302104016.163542-1-bhe@redhat.com/)
as follows:
- Make vswap a swap module
- Have cluster allocation functions reside in swapops
- Enable vswap through swapon

I think this could result in a similar structure. An additional
benefit would be that it enables various configurations:

- vswap + regular swap together
- vswap only
- And other combinations

And merge is not that hard. it is not the total change of swap infra structure.

But, swapoff fastness might disappear? it is not that critical as I think.

2. Flash-friendly swap integration (for my use case)

I've been thinking about the flash-friendly swap concept that
I mentioned before and recently proposed:
(https://lore.kernel.org/linux-mm/aZW0voL4MmnMQlaR@yjaykim-PowerEdge-T330/)

One of its core functions requires buffering RAM-swapped pages
and writing them sequentially at an appropriate time -- not
immediately, but in proper block-sized units, sequentially.

This means allocated offsets must essentially be virtual, and
physical offsets need to be managed separately at the actual
write time.

If we integrate this into the current vswap, we would either
need vswap itself to handle the sequential writes (bypassing
the physical device and receiving pages directly), or swapon
a swap device and have vswap obtain physical offsets from it.
But since those offsets cannot be used directly (due to
buffering and sequential write requirements), they become
virtual too, resulting in:

  virtual -> virtual -> physical

This triple indirection is not ideal.

However, if the modularization from point 1 is achieved and
vswap acts as a swap device itself, then we can cleanly
establish a:

  virtual -> physical

relationship within it.

I noticed you seem to be exploring collaboration with Kairui
as well. I'm curious whether you have a compromise direction
in mind, or if you plan to stick with the current approach.

P.S. I definitely want to review the vswap code in detail
when I get the time. great work and code.

Thanks,
Youngjun Park

^ permalink raw reply

* [PATCH 3/3] arm64/sched: Enable CPPC-based asympacking
From: Christian Loehle @ 2026-03-25 18:13 UTC (permalink / raw)
  To: arighi
  Cc: peterz, vincent.guittot, dietmar.eggemann, valentin.schneider,
	mingo, rostedt, segall, mgorman, catalin.marinas, will,
	sudeep.holla, rafael, linux-pm, linux-kernel, juri.lelli, kobak,
	fabecassis, Christian Loehle
In-Reply-To: <20260325181314.3875909-1-christian.loehle@arm.com>

To handle minor capacity differences (<5%) use asym-packing to steer
tasks towards higher performance CPUs, replacing capacity-aware
scheduling in those cases and skip setting SD_ASYM_CPUCAPACITY.
This is implemented by using highest_perf values as priorities to steer
towards.
highest_perf-based asympacking is a global ordering that is applied
at all levels of the hierarchy for now.

Signed-off-by: Christian Loehle <christian.loehle@arm.com>
---
 arch/arm64/include/asm/topology.h |  6 ++++++
 arch/arm64/kernel/topology.c      | 34 +++++++++++++++++++++++++++++++
 kernel/sched/topology.c           | 26 ++++++++++++++++-------
 3 files changed, 59 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h
index b9eaf4ad7085..e0b039e1a5bb 100644
--- a/arch/arm64/include/asm/topology.h
+++ b/arch/arm64/include/asm/topology.h
@@ -39,6 +39,12 @@ void update_freq_counters_refs(void);
 #undef arch_cpu_is_threaded
 #define arch_cpu_is_threaded() (read_cpuid_mpidr() & MPIDR_MT_BITMASK)
 
+#undef arch_asym_cpu_priority
+#define arch_asym_cpu_priority arm64_arch_asym_cpu_priority
+#define arch_sched_asym_flags arm64_arch_sched_asym_flags
+extern int arm64_arch_asym_cpu_priority(int cpu);
+extern int arm64_arch_sched_asym_flags(void);
+
 #include <asm-generic/topology.h>
 
 #endif /* _ASM_ARM_TOPOLOGY_H */
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index b32f13358fbb..4e3582d44a26 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -19,6 +19,7 @@
 #include <linux/init.h>
 #include <linux/percpu.h>
 #include <linux/sched/isolation.h>
+#include <linux/sched/topology.h>
 #include <linux/xarray.h>
 
 #include <asm/cpu.h>
@@ -373,6 +374,26 @@ core_initcall(init_amu_fie);
 #ifdef CONFIG_ACPI_CPPC_LIB
 #include <acpi/cppc_acpi.h>
 
+static bool __read_mostly sched_cppc_asym_active;
+DEFINE_PER_CPU_READ_MOSTLY(int, sched_cppc_priority);
+
+int arm64_arch_asym_cpu_priority(int cpu)
+{
+	if (!READ_ONCE(sched_cppc_asym_active))
+		return -cpu;
+	return per_cpu(sched_cppc_priority, cpu);
+}
+
+int arm64_arch_sched_asym_flags(void)
+{
+	return READ_ONCE(sched_cppc_asym_active) ? SD_ASYM_PACKING : 0;
+}
+
+void arch_topology_init_cppc_asym(void)
+{
+	WRITE_ONCE(sched_cppc_asym_active, topology_init_cppc_asym_packing(&sched_cppc_priority));
+}
+
 static void cpu_read_corecnt(void *val)
 {
 	/*
@@ -473,4 +494,17 @@ int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
 {
 	return -EOPNOTSUPP;
 }
+
+#else
+int arm64_arch_asym_cpu_priority(int cpu)
+{
+	return -cpu;
+}
+
+int arm64_arch_sched_asym_flags(void)
+{
+	return 0;
+}
+
+void arch_topology_init_cppc_asym(void) { }
 #endif /* CONFIG_ACPI_CPPC_LIB */
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index b0c590dfdb01..758b8796b62d 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -1396,6 +1396,8 @@ asym_cpu_capacity_classify(const struct cpumask *sd_span,
 			   const struct cpumask *cpu_map)
 {
 	struct asym_cap_data *entry;
+	unsigned long max_cap = 0, min_cap = ULONG_MAX;
+	bool has_cap = false;
 	int count = 0, miss = 0;
 
 	/*
@@ -1405,9 +1407,12 @@ asym_cpu_capacity_classify(const struct cpumask *sd_span,
 	 * skip those.
 	 */
 	list_for_each_entry(entry, &asym_cap_list, link) {
-		if (cpumask_intersects(sd_span, cpu_capacity_span(entry)))
+		if (cpumask_intersects(sd_span, cpu_capacity_span(entry))) {
 			++count;
-		else if (cpumask_intersects(cpu_map, cpu_capacity_span(entry)))
+			max_cap = max(max_cap, entry->capacity);
+			min_cap = min(min_cap, entry->capacity);
+			has_cap = true;
+		} else if (cpumask_intersects(cpu_map, cpu_capacity_span(entry)))
 			++miss;
 	}
 
@@ -1419,10 +1424,12 @@ asym_cpu_capacity_classify(const struct cpumask *sd_span,
 	/* Some of the available CPU capacity values have not been detected */
 	if (miss)
 		return SD_ASYM_CPUCAPACITY;
+	/* When asym packing is active, ignore small capacity differences. */
+	if (arch_sched_asym_flags() && has_cap && !capacity_greater(max_cap, min_cap))
+		return 0;
 
 	/* Full asymmetry */
 	return SD_ASYM_CPUCAPACITY | SD_ASYM_CPUCAPACITY_FULL;
-
 }
 
 static void free_asym_cap_entry(struct rcu_head *head)
@@ -1753,7 +1760,7 @@ static inline int topology_arch_sched_asym_flags(void)
 #ifdef CONFIG_SCHED_SMT
 int cpu_smt_flags(void)
 {
-	return SD_SHARE_CPUCAPACITY | SD_SHARE_LLC;
+	return SD_SHARE_CPUCAPACITY | SD_SHARE_LLC | arch_sched_asym_flags();
 }
 
 const struct cpumask *tl_smt_mask(struct sched_domain_topology_level *tl, int cpu)
@@ -1765,7 +1772,7 @@ const struct cpumask *tl_smt_mask(struct sched_domain_topology_level *tl, int cp
 #ifdef CONFIG_SCHED_CLUSTER
 int cpu_cluster_flags(void)
 {
-	return SD_CLUSTER | SD_SHARE_LLC;
+	return SD_CLUSTER | SD_SHARE_LLC | arch_sched_asym_flags();
 }
 
 const struct cpumask *tl_cls_mask(struct sched_domain_topology_level *tl, int cpu)
@@ -1777,7 +1784,7 @@ const struct cpumask *tl_cls_mask(struct sched_domain_topology_level *tl, int cp
 #ifdef CONFIG_SCHED_MC
 int cpu_core_flags(void)
 {
-	return SD_SHARE_LLC;
+	return SD_SHARE_LLC | arch_sched_asym_flags();
 }
 
 const struct cpumask *tl_mc_mask(struct sched_domain_topology_level *tl, int cpu)
@@ -1791,6 +1798,11 @@ const struct cpumask *tl_pkg_mask(struct sched_domain_topology_level *tl, int cp
 	return cpu_node_mask(cpu);
 }
 
+static int cpu_pkg_flags(void)
+{
+	return arch_sched_asym_flags();
+}
+
 /*
  * Topology list, bottom-up.
  */
@@ -1806,7 +1818,7 @@ static struct sched_domain_topology_level default_topology[] = {
 #ifdef CONFIG_SCHED_MC
 	SDTL_INIT(tl_mc_mask, cpu_core_flags, MC),
 #endif
-	SDTL_INIT(tl_pkg_mask, NULL, PKG),
+	SDTL_INIT(tl_pkg_mask, cpu_pkg_flags, PKG),
 	{ NULL, },
 };
 
-- 
2.34.1


^ permalink raw reply related

* [PATCH 2/3] arch_topology: Export CPPC-based asympacking prios
From: Christian Loehle @ 2026-03-25 18:13 UTC (permalink / raw)
  To: arighi
  Cc: peterz, vincent.guittot, dietmar.eggemann, valentin.schneider,
	mingo, rostedt, segall, mgorman, catalin.marinas, will,
	sudeep.holla, rafael, linux-pm, linux-kernel, juri.lelli, kobak,
	fabecassis, Christian Loehle
In-Reply-To: <20260325181314.3875909-1-christian.loehle@arm.com>

Determine asymmetric packing priorities by reading CPPC's
highest_perf so it can be read by arch code later to enable
asympacking if the difference between the minimum and
maximum highest_perf is small (<5%).

Signed-off-by: Christian Loehle <christian.loehle@arm.com>
---
 drivers/base/arch_topology.c | 40 ++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
index 8c5e47c28d9a..c80c782e5eb2 100644
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -321,8 +321,47 @@ void __weak freq_inv_set_max_ratio(int cpu, u64 max_rate)
 {
 }
 
+void __weak arch_topology_init_cppc_asym(void)
+{
+}
+
 #ifdef CONFIG_ACPI_CPPC_LIB
 #include <acpi/cppc_acpi.h>
+#include <linux/limits.h>
+
+/**
+ * topology_init_cppc_asym_packing() - Detect CPPC-based asymmetric packing
+ * @priority_var: Per-CPU variable to store CPU priorities
+ *
+ * Query CPPC highest_perf for all CPUs and determine if asymmetric packing
+ * should be enabled based on minor performance differences (~5% threshold).
+ *
+ * Return: true if asympacking should be enabled, false otherwise
+ */
+bool topology_init_cppc_asym_packing(int __percpu *priority_var)
+{
+	struct cppc_perf_caps perf_caps;
+	u32 max_perf = 0, min_perf = U32_MAX;
+	int cpu;
+
+	if (!acpi_cpc_valid())
+		return false;
+
+	for_each_possible_cpu(cpu) {
+		if (cppc_get_perf_caps(cpu, &perf_caps))
+			return false;
+		if (perf_caps.highest_perf < perf_caps.nominal_perf ||
+		    perf_caps.highest_perf < perf_caps.lowest_perf)
+			return false;
+
+		*per_cpu_ptr(priority_var, cpu) = perf_caps.highest_perf;
+		max_perf = max(max_perf, perf_caps.highest_perf);
+		min_perf = min(min_perf, perf_caps.highest_perf);
+	}
+
+	return (max_perf != min_perf) && !capacity_greater(max_perf, min_perf);
+}
+EXPORT_SYMBOL_GPL(topology_init_cppc_asym_packing);
 
 static inline void topology_init_cpu_capacity_cppc(void)
 {
@@ -369,6 +408,7 @@ static inline void topology_init_cpu_capacity_cppc(void)
 			cpu, topology_get_cpu_scale(cpu));
 	}
 
+	arch_topology_init_cppc_asym();
 	schedule_work(&update_topology_flags_work);
 	pr_debug("cpu_capacity: cpu_capacity initialization done\n");
 
-- 
2.34.1


^ permalink raw reply related

* [PATCH 1/3] sched/topology: Introduce arch hooks for asympacking
From: Christian Loehle @ 2026-03-25 18:13 UTC (permalink / raw)
  To: arighi
  Cc: peterz, vincent.guittot, dietmar.eggemann, valentin.schneider,
	mingo, rostedt, segall, mgorman, catalin.marinas, will,
	sudeep.holla, rafael, linux-pm, linux-kernel, juri.lelli, kobak,
	fabecassis, Christian Loehle
In-Reply-To: <20260325181314.3875909-1-christian.loehle@arm.com>

Prepare for arch-specifc asympacking logic.

No functional impact intended.

Signed-off-by: Christian Loehle <christian.loehle@arm.com>
---
 include/linux/arch_topology.h  | 24 ++++++++++++++++++++++++
 include/linux/sched/topology.h |  9 +++++++++
 kernel/sched/fair.c            | 16 ----------------
 kernel/sched/topology.c        |  8 ++++++++
 4 files changed, 41 insertions(+), 16 deletions(-)

diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h
index ebd7f8935f96..3ab571b287ef 100644
--- a/include/linux/arch_topology.h
+++ b/include/linux/arch_topology.h
@@ -94,6 +94,11 @@ void remove_cpu_topology(unsigned int cpuid);
 void reset_cpu_topology(void);
 int parse_acpi_topology(void);
 void freq_inv_set_max_ratio(int cpu, u64 max_rate);
+void arch_topology_init_cppc_asym(void);
+
+#ifdef CONFIG_ACPI_CPPC_LIB
+bool topology_init_cppc_asym_packing(int __percpu *priority_var);
+#endif
 
 /*
  * Architectures like ARM64 don't have reliable architectural way to get SMT
@@ -105,10 +110,29 @@ static inline bool topology_core_has_smt(int cpu)
 	return cpu_topology[cpu].thread_id != -1;
 }
 
+#ifdef CONFIG_ARM64
+#undef arch_sched_asym_flags
+#define arch_sched_asym_flags arm64_arch_sched_asym_flags
+int arm64_arch_asym_cpu_priority(int cpu);
+int arm64_arch_sched_asym_flags(void);
+#endif
+
 #else
 
 static inline bool topology_core_has_smt(int cpu) { return false; }
 
 #endif /* CONFIG_GENERIC_ARCH_TOPOLOGY */
 
+/*
+ * Architectures may override this to provide a custom CPU priority for
+ * asymmetric packing.
+ */
+#ifndef arch_asym_cpu_priority
+#define arch_asym_cpu_priority topology_arch_asym_cpu_priority
+static inline int topology_arch_asym_cpu_priority(int cpu)
+{
+	return -cpu;
+}
+#endif
+
 #endif /* _LINUX_ARCH_TOPOLOGY_H_ */
diff --git a/include/linux/sched/topology.h b/include/linux/sched/topology.h
index 45c0022b91ce..48cfa89df0fc 100644
--- a/include/linux/sched/topology.h
+++ b/include/linux/sched/topology.h
@@ -50,6 +50,15 @@ extern const struct cpumask *tl_mc_mask(struct sched_domain_topology_level *tl,
 extern const struct cpumask *tl_pkg_mask(struct sched_domain_topology_level *tl, int cpu);
 
 extern int arch_asym_cpu_priority(int cpu);
+extern int arch_sched_asym_flags(void);
+
+/*
+ * The margin used when comparing CPU capacities.
+ * is 'cap1' noticeably greater than 'cap2'
+ *
+ * (default: ~5%)
+ */
+#define capacity_greater(cap1, cap2) ((cap1) * 1024 > (cap2) * 1078)
 
 struct sched_domain_attr {
 	int relax_domain_level;
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index bf948db905ed..c5f8aa3ad535 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -88,14 +88,6 @@ static int __init setup_sched_thermal_decay_shift(char *str)
 }
 __setup("sched_thermal_decay_shift=", setup_sched_thermal_decay_shift);
 
-/*
- * For asym packing, by default the lower numbered CPU has higher priority.
- */
-int __weak arch_asym_cpu_priority(int cpu)
-{
-	return -cpu;
-}
-
 /*
  * The margin used when comparing utilization with CPU capacity.
  *
@@ -103,14 +95,6 @@ int __weak arch_asym_cpu_priority(int cpu)
  */
 #define fits_capacity(cap, max)	((cap) * 1280 < (max) * 1024)
 
-/*
- * The margin used when comparing CPU capacities.
- * is 'cap1' noticeably greater than 'cap2'
- *
- * (default: ~5%)
- */
-#define capacity_greater(cap1, cap2) ((cap1) * 1024 > (cap2) * 1078)
-
 #ifdef CONFIG_CFS_BANDWIDTH
 /*
  * Amount of runtime to allocate from global (tg) to local (per-cfs_rq) pool
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index 32dcddaead82..b0c590dfdb01 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -1742,6 +1742,14 @@ sd_init(struct sched_domain_topology_level *tl,
 	return sd;
 }
 
+#ifndef arch_sched_asym_flags
+#define arch_sched_asym_flags topology_arch_sched_asym_flags
+static inline int topology_arch_sched_asym_flags(void)
+{
+	return 0;
+}
+#endif
+
 #ifdef CONFIG_SCHED_SMT
 int cpu_smt_flags(void)
 {
-- 
2.34.1


^ permalink raw reply related

* [RFC][RFT][PATCH 0/3] arm64: Enable asympacking for minor CPPC asymmetry
From: Christian Loehle @ 2026-03-25 18:13 UTC (permalink / raw)
  To: arighi
  Cc: peterz, vincent.guittot, dietmar.eggemann, valentin.schneider,
	mingo, rostedt, segall, mgorman, catalin.marinas, will,
	sudeep.holla, rafael, linux-pm, linux-kernel, juri.lelli, kobak,
	fabecassis, Christian Loehle

The scheduler currently handles CPU performance asymmetry via either:

- SD_ASYM_PACKING: simple priority-based task placement (x86 ITMT)
- SD_ASYM_CPUCAPACITY: capacity-aware scheduling

On arm64, capacity-aware scheduling is used for any detected capacity
differences.

Some systems expose small per-CPU performance differences via CPPC
highest_perf (e.g. due to chip binning), resulting in slightly different
capacities (<~5%). These differences are sufficient to trigger
SD_ASYM_CPUCAPACITY, even though the system is otherwise effectively
symmetric.

For such small deltas, capacity-aware scheduling is unnecessarily
complex. A simpler priority-based approach, similar to x86 ITMT, is
sufficient.

This series introduces support for using asymmetric packing in that case:

- derive per-CPU priorities from CPPC highest_perf
- detect when CPUs differ but not enough to form distinct capacity classes
- suppress SD_ASYM_CPUCAPACITY for such domains
- enable SD_ASYM_PACKING and use CPPC-based priority ordering instead

The asympacking flag is exposed at all topology levels; domains with
equal priorities are unaffected, while domains spanning CPUs with
different priorities can honor the ordering.

RFC:
I'm not entirely sure if this is the best way to implement this.
Currently this is baked into CPPC and arm64, while neither are strictly
necessary, we could also use cpu_capacity directly to derive the
ordering and enable this for non-CPPC and/or non-arm64.
RFT:
Andrea, please give this a try. This should perform better in particular
for single-threaded workloads and workloads that do not utilize all
cores (all the time anyway).
Capacity-aware scheduling wakeup works very different to the SMP path
used now, some workloads will benefit, some regress, it would be nice
to get some test results for these.
We already discussed DCPerf MediaWiki seems to benefit from
capacity-aware scheduling wakeup behavior, but others (most?) should
benefit from this series.

I don't know if we can also be clever about ordering amongst SMT siblings.
That would be dependent on the uarch and I don't have a platform to
experiment with this though, so consider this series orthogonal to the
idle-core SMT considerations.
On platforms with SMT though asympacking makes a lot more sense than
capacity-aware scheduling, because arguing about capacity without
considering utilization of the sibling(s) (and the resulting potential
'stolen' capacity we perceive) isn't theoretically sound.

Christian Loehle (3):
  sched/topology: Introduce arch hooks for asympacking
  arch_topology: Export CPPC-based asympacking prios
  arm64/sched: Enable CPPC-based asympacking

 arch/arm64/include/asm/topology.h |  6 +++++
 arch/arm64/kernel/topology.c      | 34 ++++++++++++++++++++++++++
 drivers/base/arch_topology.c      | 40 +++++++++++++++++++++++++++++++
 include/linux/arch_topology.h     | 24 +++++++++++++++++++
 include/linux/sched/topology.h    |  9 +++++++
 kernel/sched/fair.c               | 16 -------------
 kernel/sched/topology.c           | 34 ++++++++++++++++++++------
 7 files changed, 140 insertions(+), 23 deletions(-)

-- 
2.34.1


^ permalink raw reply

* [rafael-pm:bleeding-edge] BUILD SUCCESS 877556758cf5718848fe4ff01c93109f51ce715b
From: kernel test robot @ 2026-03-25 17:37 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: linux-acpi, linux-pm

tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git bleeding-edge
branch HEAD: 877556758cf5718848fe4ff01c93109f51ce715b  Merge branch 'experimental/acpi-driver-conversion' into bleeding-edge

elapsed time: 1258m

configs tested: 153
configs skipped: 10

The following configs have been built successfully.
More configs may be tested in the coming days.

tested configs:
alpha                             allnoconfig    gcc-15.2.0
alpha                            allyesconfig    gcc-15.2.0
alpha                               defconfig    gcc-15.2.0
arc                              allmodconfig    gcc-15.2.0
arc                               allnoconfig    gcc-15.2.0
arc                              allyesconfig    gcc-15.2.0
arc                                 defconfig    gcc-15.2.0
arc                   randconfig-001-20260325    gcc-15.2.0
arc                   randconfig-002-20260325    gcc-8.5.0
arm                               allnoconfig    clang-23
arm                              allyesconfig    gcc-15.2.0
arm                                 defconfig    clang-23
arm                            mmp2_defconfig    gcc-15.2.0
arm                   randconfig-001-20260325    gcc-8.5.0
arm                   randconfig-002-20260325    clang-23
arm                   randconfig-003-20260325    clang-23
arm                   randconfig-004-20260325    clang-17
arm64                             allnoconfig    gcc-15.2.0
arm64                               defconfig    gcc-15.2.0
arm64                 randconfig-001-20260325    clang-23
arm64                 randconfig-002-20260325    gcc-11.5.0
arm64                 randconfig-003-20260325    gcc-8.5.0
arm64                 randconfig-004-20260325    clang-23
csky                              allnoconfig    gcc-15.2.0
csky                                defconfig    gcc-15.2.0
csky                  randconfig-001-20260325    gcc-10.5.0
csky                  randconfig-002-20260325    gcc-12.5.0
hexagon                          allmodconfig    clang-17
hexagon                           allnoconfig    clang-23
hexagon                             defconfig    clang-23
hexagon               randconfig-001-20260325    clang-19
hexagon               randconfig-002-20260325    clang-23
i386                              allnoconfig    gcc-14
i386                             allyesconfig    gcc-14
i386        buildonly-randconfig-001-20260325    gcc-14
i386        buildonly-randconfig-002-20260325    gcc-14
i386        buildonly-randconfig-003-20260325    clang-20
i386        buildonly-randconfig-004-20260325    clang-20
i386        buildonly-randconfig-005-20260325    clang-20
i386        buildonly-randconfig-006-20260325    clang-20
i386                                defconfig    clang-20
i386                  randconfig-001-20260325    clang-20
i386                  randconfig-002-20260325    clang-20
i386                  randconfig-003-20260325    gcc-14
i386                  randconfig-004-20260325    clang-20
i386                  randconfig-005-20260325    gcc-14
i386                  randconfig-006-20260325    clang-20
i386                  randconfig-007-20260325    clang-20
i386                  randconfig-011-20260325    gcc-14
i386                  randconfig-012-20260325    gcc-14
i386                  randconfig-013-20260325    gcc-14
i386                  randconfig-014-20260325    gcc-14
i386                  randconfig-015-20260325    clang-20
i386                  randconfig-016-20260325    clang-20
i386                  randconfig-017-20260325    gcc-14
loongarch                        allmodconfig    clang-19
loongarch                         allnoconfig    clang-23
loongarch                           defconfig    clang-19
loongarch             randconfig-001-20260325    clang-23
loongarch             randconfig-002-20260325    clang-23
m68k                             allmodconfig    gcc-15.2.0
m68k                              allnoconfig    gcc-15.2.0
m68k                             allyesconfig    gcc-15.2.0
m68k                                defconfig    gcc-15.2.0
microblaze                        allnoconfig    gcc-15.2.0
microblaze                       allyesconfig    gcc-15.2.0
microblaze                          defconfig    gcc-15.2.0
mips                              allnoconfig    gcc-15.2.0
mips                             allyesconfig    gcc-15.2.0
nios2                            allmodconfig    gcc-11.5.0
nios2                             allnoconfig    gcc-11.5.0
nios2                               defconfig    gcc-11.5.0
nios2                 randconfig-001-20260325    gcc-8.5.0
nios2                 randconfig-002-20260325    gcc-11.5.0
openrisc                         allmodconfig    gcc-15.2.0
openrisc                          allnoconfig    gcc-15.2.0
openrisc                            defconfig    gcc-15.2.0
parisc                            allnoconfig    gcc-15.2.0
parisc                           allyesconfig    gcc-15.2.0
parisc                              defconfig    gcc-15.2.0
parisc                randconfig-001-20260325    gcc-8.5.0
parisc                randconfig-002-20260325    gcc-14.3.0
parisc64                            defconfig    gcc-15.2.0
powerpc                           allnoconfig    gcc-15.2.0
powerpc               randconfig-001-20260325    clang-23
powerpc               randconfig-002-20260325    clang-23
powerpc64             randconfig-001-20260325    clang-23
powerpc64             randconfig-002-20260325    clang-23
riscv                            allmodconfig    clang-23
riscv                             allnoconfig    gcc-15.2.0
riscv                            allyesconfig    clang-16
riscv                               defconfig    clang-23
riscv                 randconfig-001-20260325    clang-20
riscv                 randconfig-002-20260325    gcc-14.3.0
s390                             allmodconfig    clang-18
s390                              allnoconfig    clang-23
s390                                defconfig    clang-23
s390                  randconfig-001-20260325    clang-23
s390                  randconfig-002-20260325    gcc-8.5.0
sh                               allmodconfig    gcc-15.2.0
sh                                allnoconfig    gcc-15.2.0
sh                               allyesconfig    gcc-15.2.0
sh                                  defconfig    gcc-15.2.0
sh                    randconfig-001-20260325    gcc-15.2.0
sh                    randconfig-002-20260325    gcc-15.2.0
sparc                             allnoconfig    gcc-15.2.0
sparc                               defconfig    gcc-15.2.0
sparc                 randconfig-001-20260325    gcc-8.5.0
sparc                 randconfig-002-20260325    gcc-14.3.0
sparc64                          allmodconfig    clang-23
sparc64                             defconfig    clang-20
sparc64               randconfig-001-20260325    clang-23
sparc64               randconfig-002-20260325    clang-23
um                               allmodconfig    clang-19
um                                allnoconfig    clang-23
um                               allyesconfig    gcc-14
um                                  defconfig    clang-23
um                             i386_defconfig    gcc-14
um                    randconfig-001-20260325    gcc-13
um                    randconfig-002-20260325    gcc-14
um                           x86_64_defconfig    clang-23
x86_64                           allmodconfig    clang-20
x86_64                            allnoconfig    clang-20
x86_64                           allyesconfig    clang-20
x86_64      buildonly-randconfig-001-20260325    gcc-14
x86_64      buildonly-randconfig-002-20260325    gcc-14
x86_64      buildonly-randconfig-003-20260325    gcc-14
x86_64      buildonly-randconfig-004-20260325    gcc-14
x86_64      buildonly-randconfig-005-20260325    gcc-14
x86_64      buildonly-randconfig-006-20260325    clang-20
x86_64                              defconfig    gcc-14
x86_64                randconfig-001-20260325    gcc-14
x86_64                randconfig-002-20260325    gcc-14
x86_64                randconfig-003-20260325    clang-20
x86_64                randconfig-004-20260325    clang-20
x86_64                randconfig-005-20260325    gcc-14
x86_64                randconfig-006-20260325    gcc-12
x86_64                randconfig-011-20260325    clang-20
x86_64                randconfig-012-20260325    gcc-13
x86_64                randconfig-013-20260325    gcc-14
x86_64                randconfig-014-20260325    gcc-14
x86_64                randconfig-015-20260325    clang-20
x86_64                randconfig-016-20260325    gcc-13
x86_64                randconfig-071-20260325    clang-20
x86_64                randconfig-072-20260325    gcc-14
x86_64                randconfig-073-20260325    clang-20
x86_64                randconfig-074-20260325    gcc-14
x86_64                randconfig-075-20260325    gcc-14
x86_64                randconfig-076-20260325    gcc-14
x86_64                          rhel-9.4-rust    clang-20
xtensa                            allnoconfig    gcc-15.2.0
xtensa                randconfig-001-20260325    gcc-13.4.0
xtensa                randconfig-002-20260325    gcc-12.5.0

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* Re: [PATCH v6 3/4] cpufreq: Set policy->min and max as real QoS constraints
From: Pierre Gondois @ 2026-03-25 16:54 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: <ir3mvkz7bbzwtsydrfdhnkkkseaj6fclzgiw3soxb673xvhczk@fukk5ad7jrtq>


On 3/25/26 07:49, Viresh Kumar wrote:
> On 24-03-26, 12:40, Pierre Gondois wrote:
>> 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) {
> I am okay with a separate series doing all these cleanups. We can set some sort
> of rule on what is expected from the driver here and what is left from the core.
> For example driver only sets cpuinfo.max/min and core takes care of rest. This
> series would cleanup all the drivers, etc.
>
Ok,

Is the current state of this patch + the above modification acceptable ?


^ permalink raw reply

* [PATCH v7 2/2] cpufreq: Add boost_freq_req QoS request
From: Pierre Gondois @ 2026-03-25 16:52 UTC (permalink / raw)
  To: linux-kernel
  Cc: Lifeng Zheng, Pierre Gondois, Huang Rui, Gautham R. Shenoy,
	Mario Limonciello, Perry Yuan, Rafael J. Wysocki, Viresh Kumar,
	linux-pm
In-Reply-To: <20260325165255.386576-1-pierre.gondois@arm.com>

The Power Management Quality of Service (PM QoS) allows to
aggregate constraints from multiple entities. It is currently
used to manage the min/max frequency of a given policy.

Frequency constraints can come for instance from:
- Thermal framework: acpi_thermal_cpufreq_init()
- Firmware: _PPC objects: acpi_processor_ppc_init()
- User: by setting policyX/scaling_[min|max]_freq
The minimum of the max frequency constraints is used to compute
the resulting maximum allowed frequency.

When enabling boost frequencies, the same frequency request object
(policy->max_freq_req) as to handle requests from users is used.
As a result, when setting:
- scaling_max_freq
- boost
The last sysfs file used overwrites the request from the other
sysfs file.

To avoid this, create a per-policy boost_freq_req to save the boost
constraints instead of overwriting the last scaling_max_freq
constraint.

policy_set_boost() calls the cpufreq set_boost callback.
Update the newly added boost_freq_req request from there:
- whenever boost is toggled
- to cover all possible paths

In the existing .set_boost() callbacks:
- Don't update policy->max as this is done through the qos notifier
  cpufreq_notifier_max() which calls cpufreq_set_policy().
- Remove freq_qos_update_request() calls as the qos request is now
  done in policy_set_boost() and updates the new boost_freq_req

$ ## Init state
scaling_max_freq:1000000
cpuinfo_max_freq:1000000

$ echo 700000 > scaling_max_freq
scaling_max_freq:700000
cpuinfo_max_freq:1000000

$ echo 1 > ../boost
scaling_max_freq:1200000
cpuinfo_max_freq:1200000

$ echo 800000 > scaling_max_freq
scaling_max_freq:800000
cpuinfo_max_freq:1200000

$ ## Final step:
$ ## Without the patches:
$ echo 0 > ../boost
scaling_max_freq:1000000
cpuinfo_max_freq:1000000

$ ## With the patches:
$ echo 0 > ../boost
scaling_max_freq:800000
cpuinfo_max_freq:1000000

Note:
cpufreq_frequency_table_cpuinfo() updates policy->min
and max from:
A.
cpufreq_boost_set_sw()
\-cpufreq_frequency_table_cpuinfo()
B.
cpufreq_policy_online()
\-cpufreq_table_validate_and_sort()
  \-cpufreq_frequency_table_cpuinfo()
Keep these updates as some drivers expect policy->min and
max to be set through B.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
 drivers/cpufreq/amd-pstate.c   |  2 --
 drivers/cpufreq/cppc_cpufreq.c | 10 ++-----
 drivers/cpufreq/cpufreq.c      | 51 ++++++++++++++++++++++++++--------
 include/linux/cpufreq.h        |  1 +
 4 files changed, 43 insertions(+), 21 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 5aa9fcd80cf51..d0675d6a19fe1 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -769,8 +769,6 @@ static int amd_pstate_cpu_boost_update(struct cpufreq_policy *policy, bool on)
 	else if (policy->cpuinfo.max_freq > nominal_freq)
 		policy->cpuinfo.max_freq = nominal_freq;
 
-	policy->max = policy->cpuinfo.max_freq;
-
 	if (cppc_state == AMD_PSTATE_PASSIVE) {
 		ret = freq_qos_update_request(&cpudata->req[1], policy->cpuinfo.max_freq);
 		if (ret < 0)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 011f35cb47b94..f4f574fbe547b 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -807,17 +807,11 @@ static int cppc_cpufreq_set_boost(struct cpufreq_policy *policy, int state)
 {
 	struct cppc_cpudata *cpu_data = policy->driver_data;
 	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
-	int ret;
 
 	if (state)
-		policy->max = cppc_perf_to_khz(caps, caps->highest_perf);
+		policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, caps->highest_perf);
 	else
-		policy->max = cppc_perf_to_khz(caps, caps->nominal_perf);
-	policy->cpuinfo.max_freq = policy->max;
-
-	ret = freq_qos_update_request(policy->max_freq_req, policy->max);
-	if (ret < 0)
-		return ret;
+		policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, caps->nominal_perf);
 
 	return 0;
 }
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 5757f12633d16..947ed87cf8d76 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -609,10 +609,19 @@ static int policy_set_boost(struct cpufreq_policy *policy, bool enable)
 	policy->boost_enabled = enable;
 
 	ret = cpufreq_driver->set_boost(policy, enable);
-	if (ret)
+	if (ret) {
 		policy->boost_enabled = !policy->boost_enabled;
+		return ret;
+	}
 
-	return ret;
+	ret = freq_qos_update_request(policy->boost_freq_req, policy->cpuinfo.max_freq);
+	if (ret < 0) {
+		policy->boost_enabled = !policy->boost_enabled;
+		cpufreq_driver->set_boost(policy, policy->boost_enabled);
+		return ret;
+	}
+
+	return 0;
 }
 
 static ssize_t store_local_boost(struct cpufreq_policy *policy,
@@ -1377,6 +1386,8 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
 	}
 
 	freq_qos_remove_request(policy->min_freq_req);
+	if (policy->boost_freq_req)
+		freq_qos_remove_request(policy->boost_freq_req);
 	kfree(policy->min_freq_req);
 
 	cpufreq_policy_put_kobj(policy);
@@ -1445,18 +1456,42 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy,
 	cpumask_and(policy->cpus, policy->cpus, cpu_online_mask);
 
 	if (new_policy) {
+		unsigned int req_nr;
+
 		for_each_cpu(j, policy->related_cpus) {
 			per_cpu(cpufreq_cpu_data, j) = policy;
 			add_cpu_dev_symlink(policy, j, get_cpu_device(j));
 		}
 
-		policy->min_freq_req = kzalloc(2 * sizeof(*policy->min_freq_req),
+		req_nr = policy->boost_supported ? 3 : 2;
+		policy->min_freq_req = kzalloc(req_nr * sizeof(*policy->min_freq_req),
 					       GFP_KERNEL);
 		if (!policy->min_freq_req) {
 			ret = -ENOMEM;
 			goto out_destroy_policy;
 		}
 
+		if (policy->boost_supported) {
+			policy->boost_freq_req = policy->min_freq_req + 2;
+
+			/*
+			 * If boost is supported,
+			 * init the constraint with cpuinfo.max_freq.
+			 */
+			ret = freq_qos_add_request(&policy->constraints,
+						   policy->boost_freq_req,
+						   FREQ_QOS_MAX,
+						   policy->cpuinfo.max_freq);
+			if (ret < 0) {
+				/*
+				 * So we don't call freq_qos_remove_request() for an
+				 * uninitialized request.
+				 */
+				policy->boost_freq_req = NULL;
+				goto out_destroy_policy;
+			}
+		}
+
 		ret = freq_qos_add_request(&policy->constraints,
 					   policy->min_freq_req, FREQ_QOS_MIN,
 					   FREQ_QOS_MIN_DEFAULT_VALUE);
@@ -2788,16 +2823,10 @@ int cpufreq_boost_set_sw(struct cpufreq_policy *policy, int state)
 		return -ENXIO;
 
 	ret = cpufreq_frequency_table_cpuinfo(policy);
-	if (ret) {
+	if (ret)
 		pr_err("%s: Policy frequency update failed\n", __func__);
-		return ret;
-	}
-
-	ret = freq_qos_update_request(policy->max_freq_req, policy->max);
-	if (ret < 0)
-		return ret;
 
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(cpufreq_boost_set_sw);
 
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index cc894fc389710..89157e367eefa 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -81,6 +81,7 @@ struct cpufreq_policy {
 	struct freq_constraints	constraints;
 	struct freq_qos_request	*min_freq_req;
 	struct freq_qos_request	*max_freq_req;
+	struct freq_qos_request *boost_freq_req;
 
 	struct cpufreq_frequency_table	*freq_table;
 	enum cpufreq_table_sorting freq_table_sorted;
-- 
2.43.0


^ permalink raw reply related

* [PATCH v7 1/2] cpufreq: Remove per-CPU QoS constraint
From: Pierre Gondois @ 2026-03-25 16:52 UTC (permalink / raw)
  To: linux-kernel
  Cc: Lifeng Zheng, Pierre Gondois, Huang Rui, Gautham R. Shenoy,
	Mario Limonciello, Perry Yuan, Rafael J. Wysocki, Viresh Kumar,
	linux-pm
In-Reply-To: <20260325165255.386576-1-pierre.gondois@arm.com>

policy->max_freq_req QoS constraint represents the maximal allowed
frequency than can be requested. It is set by:
- writing to policyX/scaling_max sysfs file
- toggling the cpufreq/boost sysfs file

Upon calling freq_qos_update_request(), a successful update
of the max_freq_req value triggers cpufreq_notifier_max(),
followed by cpufreq_set_policy() which update the requested
frequency for the policy.
If the new max_freq_req value is not different from the
original value, no frequency update is triggered.

In a specific sequence of toggling:
- cpufreq/boost sysfs file
- CPU hot-plugging
a CPU could end up with boost enabled but running at the
maximal non-boost frequency, cpufreq_notifier_max() not being
triggered. The following fixed that:
commit 1608f0230510 ("cpufreq: Fix re-boost issue after hotplugging
a CPU")

The following:
commit dd016f379ebc ("cpufreq: Introduce a more generic way to
set default per-policy boost flag")
also fixed the issue by correctly setting the max_freq_req
constraint of a policy that is re-activated. This makes the
first fix unnecessary.

As the original issue is fixed by another method,
this patch reverts:
commit 1608f0230510 ("cpufreq: Fix re-boost issue after hotplugging
a CPU")

Reviewed-by: Lifeng Zheng <zhenglifeng1@huawei.com>
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
 drivers/cpufreq/cpufreq.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 277884d91913c..5757f12633d16 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1487,10 +1487,6 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy,
 
 		blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
 				CPUFREQ_CREATE_POLICY, policy);
-	} else {
-		ret = freq_qos_update_request(policy->max_freq_req, policy->max);
-		if (ret < 0)
-			goto out_destroy_policy;
 	}
 
 	if (cpufreq_driver->get && has_target()) {
-- 
2.43.0


^ permalink raw reply related

* [PATCH v7 0/2] cpufreq: Introduce boost frequency QoS
From: Pierre Gondois @ 2026-03-25 16:52 UTC (permalink / raw)
  To: linux-kernel
  Cc: Lifeng Zheng, Pierre Gondois, Huang Rui, Gautham R. Shenoy,
	Mario Limonciello, Perry Yuan, Rafael J. Wysocki, Viresh Kumar,
	linux-pm

The Power Management Quality of Service (PM QoS) allows to
aggregate constraints from multiple entities. It is currently
used to manage the min/max frequency of a given policy.

Frequency constraints can come from:
- Thermal framework: acpi_thermal_cpufreq_init()
- Firmware: _PPC objects: acpi_processor_ppc_init()
- User: by setting policyX/scaling_[min|max]_freq
The minimum of the max frequency constraints is used to compute
the resulting maximum allowed frequency.

When enabling boost frequencies, the same frequency request object
(policy->max_freq_req) as to handle requests from users is used.
As a result, when setting:
- scaling_max_freq
- boost
The last sysfs file used overwrites the request from the other
sysfs file.

To avoid this:
1. Create a per-policy boost_freq_req to save the boost
constraints instead of overwriting the last scaling_max_freq
constraint.

2. policy_set_boost() calls the cpufreq set_boost callback.
Update the newly added boost_freq_req request from there:
- whenever boost is toggled
- to cover all possible paths

3. In the existing set_boost() callbacks:
- Don't update policy->max as this is done through the qos notifier
  cpufreq_notifier_max() which calls cpufreq_set_policy().
- Remove freq_qos_update_request() calls as the qos request is now
  done in policy_set_boost() and updates the new boost_freq_req

---

E.g.:
On a Juno with available frequencies: 600.000, 1.000.000
Boost frequencies: 1.200.000
Using the cppc-cpufreq driver.

---
Without the patches:
# ## Init state
scaling_max_freq:1000000
cpuinfo_max_freq:1000000

# echo 700000 > scaling_max_freq
scaling_max_freq:700000
cpuinfo_max_freq:1000000

# echo 1 > ../boost
scaling_max_freq:1200000
cpuinfo_max_freq:1200000

# echo 800000 > scaling_max_freq
scaling_max_freq:800000
cpuinfo_max_freq:1200000

# echo 0 > ../boost
scaling_max_freq:1000000
cpuinfo_max_freq:1000000

---
With the patches:
# ## Init
scaling_max_freq:1000000
cpuinfo_max_freq:1000000

# echo 700000 > scaling_max_freq
scaling_max_freq:700000
cpuinfo_max_freq:1000000

# echo 1 > ../boost
scaling_max_freq:700000
cpuinfo_max_freq:1200000

# echo 800000 > scaling_max_freq
scaling_max_freq:800000
cpuinfo_max_freq:1200000

# echo 0 > ../boost
scaling_max_freq:800000
cpuinfo_max_freq:1000000

With the patches, the maximum scaling frequency requested is
conserved even though boosting is enabled/disabled.

---

Note:
It seems that there is a confusion in the cpufreq framework between:
- the min/max frequency requested by the user
- the min/max frequency constraint applied when selecting a frequency.

E.g:
A.
$ echo XXX > scaling_max_freq
updates the max_freq_req QoS request.

B.
$ cat scaling_max_freq
shows the content of policy->max, which is the not representing
the value of the max_freq_req QoS request.

C.
Whenever policy->max is accessed in the cpufreq framework,
the aggregation of all the requests on the maximum frequency should
be used instead.

cpufreq_set_policy() aggregates min/max constraints and
writes the resulting value in policy->min/max. These values
are then used in the cpufreq drivers.

Creating a clear distinction would be doable but quite invasive.
This patchset focuses on handling the boost frequency QoS request
first and should not change the current behaviour of policy->min
and max.

---

v1: https://lore.kernel.org/all/20251204101344.192678-1-pierre.gondois@arm.com/#t
v2: https://lore.kernel.org/all/20251208105933.1369125-1-pierre.gondois@arm.com/#t
Changes:
- Fixed error path
- Integrated [PATCH 1/4] Revert "cpufreq: Fix re-boost issue after hotplugging a CPU"
  to another patch
v3:
Changes:
- Fixed error path
- Extracted the revert of:
  "cpufreq: Fix re-boost issue after hotplugging a CPU"
  for clarity purpose
- Set cpuinfo.max_freq as a max_freq_req QoS constraint by default
New patches:
- "cpufreq: Allow decreasing cpuinfo.max_freq"
- "cpufreq: Set policy->min and max as QoS constraints"
v4:
- Correct reported issues
v5:
- Corrections
v6:
- Folded patches:
  - cpufreq: Centralize boost freq QoS requests
  - cpufreq: Update .set_boost() callbacks to rely on boost_freq_req
  inside:
  - cpufreq: Add boost_freq_req QoS request
- Simplified allocation handling of boost_freq_req
- Removed unnecessary bits
v7:
- Removed the following patches to submit them separately
  - cpufreq: Set policy->min and max as real QoS constraints
  - cpufreq/freq_table: Allow decreasing cpuinfo.max_freq
- Fixed blocking_notifier_call_chain() call order when removing
  a policy.
- Updated the commit message of:
  - cpufreq: Remove per-CPU QoS constraint

Pierre Gondois (2):
  cpufreq: Remove per-CPU QoS constraint
  cpufreq: Add boost_freq_req QoS request

 drivers/cpufreq/amd-pstate.c   |  2 --
 drivers/cpufreq/cppc_cpufreq.c | 10 ++-----
 drivers/cpufreq/cpufreq.c      | 55 ++++++++++++++++++++++++----------
 include/linux/cpufreq.h        |  1 +
 4 files changed, 43 insertions(+), 25 deletions(-)

--
2.43.0

^ permalink raw reply

* Re: [PATCH 7/7] soc: renesas: Convert to of_machine_get_match()
From: Geert Uytterhoeven @ 2026-03-25 16:35 UTC (permalink / raw)
  To: Rob Herring
  Cc: Bartosz Golaszewski, Saravana Kannan, Rafael J . Wysocki,
	Viresh Kumar, Ilia Lin, Bjorn Andersson, Konrad Dybcio,
	Magnus Damm, devicetree, linux-pm, linux-arm-msm,
	linux-renesas-soc, linux-kernel
In-Reply-To: <20260313215912.GA3415767-robh@kernel.org>

Hi Rob,

On Fri, 13 Mar 2026 at 22:59, Rob Herring <robh@kernel.org> wrote:
> On Mon, Mar 02, 2026 at 05:29:11PM +0100, Geert Uytterhoeven wrote:
> > Use the of_machine_get_match() helper to avoid accessing of_root
> > directly, which is planned to become private.
> >
> > Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> > ---
> > This is an alternative solution to "[PATCH v2 8/9] soc: renesas: don't
> > access of_root directly"
> > https://lore.kernel.org/20260223-soc-of-root-v2-8-b45da45903c8@oss.qualcomm.com
>
> Greg applied this, so you'll have to respin on top of that. Next cycle I
> guess. Unless you get him to revert it.

That was my impression, too, but apparently he skipped that patch.
So you can still apply this patch, too.

> I'm applying the rest of the series.

Thanks!

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH v10 00/12] barrier: Add smp_cond_load_{relaxed,acquire}_timeout()
From: Catalin Marinas @ 2026-03-25 16:32 UTC (permalink / raw)
  To: David Laight
  Cc: Ankur Arora, Andrew Morton, linux-kernel, linux-arch,
	linux-arm-kernel, linux-pm, bpf, arnd, will, peterz, mark.rutland,
	harisokn, cl, ast, rafael, daniel.lezcano, memxor, zhenglifeng1,
	xueshuai, rdunlap, joao.m.martins, boris.ostrovsky, konrad.wilk
In-Reply-To: <20260325154210.79a621df@pumpkin>

On Wed, Mar 25, 2026 at 03:42:10PM +0000, David Laight wrote:
> On Wed, 25 Mar 2026 13:53:50 +0000
> Catalin Marinas <catalin.marinas@arm.com> wrote:
> 
> > On Tue, Mar 17, 2026 at 09:17:05AM +0000, David Laight wrote:
> > > On Mon, 16 Mar 2026 23:53:22 -0700
> > > Ankur Arora <ankur.a.arora@oracle.com> wrote:  
> > > > David Laight <david.laight.linux@gmail.com> writes:  
> > > > > On arm64 I think you could use explicit sev and wfe - but that will wake all
> > > > > 'sleeping' cpu; and you may not want the 'thundering herd'.    
> > > > 
> > > > Wouldn't we still have the same narrow window where the CPU disregards the IPI?  
> > > 
> > > You need a 'sevl' in the interrupt exit path.  
> > 
> > No need to, see the rule below in
> > https://developer.arm.com/documentation/ddi0487/maa/2983-beijhbbd:
> > 
> > R_XRZRK
> >   The Event Register for a PE is set by any of the following:
> >   [...]
> >   - An exception return.
> > 
> 
> It is a shame the pages for the SEV and WFE instructions don't mention that.
> And the copy I found doesn't have working hyperlinks to any other sections.
> (Not even references to related instructions...)

The latest architecture spec (M.a.a) has working hyperlinks.

> You do need to at least comment that the "msr s0_3_c1_c0_0, %[ecycles]" is
> actually WFET.
> Is that using an absolute cycle count?

Yes, compared to CNTVCT.

> If so does it work if the time has already passed?

Yes, it exits immediately. These counters are not going to wrap in our
(or device's) lifetime.

> If it is absolute do you need to recalculate it every time around the loop?

No but you do need to read CNTVCT, that's what __delay_cycles() does (it
does not wait).

> __delay_cycles() contains guard(preempt_notrace()). I haven't looked what
> that does but is it needed here since preemption is disabled?

The guard was added recently by commit e5cb94ba5f96 ("arm64: Fix
sampling the "stable" virtual counter in preemptible section"). It's
needed for the udelay() case but probably not for Ankur's series. Maybe
we can move the guard in the caller?

> Looking at the code I think the "sevl; wfe" pair should be higher up.

Yes, I replied to your other message. We could move it higher indeed,
before the condition check, but I can't get my head around the ordering.
Can need_resched() check be speculated before the WFE? I need to think
some more.

> I also wonder how long it takes the cpu to leave any low power state.
> We definitely found that was an issue on some x86 cpu and had to both
> disable the lowest low power state and completely rework some wakeup
> code that really wanted a 'thundering herd' rather than the very gentle
> 'bring each cpu out of low power one at a time' that cv_broadcast()
> gave it.

WFE is a very shallow power state where all hardware state is retained.
We have an even stream broadcast to all CPUs regularly already (10KHz)
and I haven't heard people complaining about power degradation. If a CPU
is a WFI state or even deeper into firmware (following a PSCI call), an
exclusive monitor event won't wake it up. It's only for those cores
waiting in WFE.

-- 
Catalin

^ permalink raw reply

* Re: [PATCH] thermal: core: fix use-after-free due to init/cancel delayed_work race
From: Rafael J. Wysocki @ 2026-03-25 16:24 UTC (permalink / raw)
  To: Mauricio Faria de Oliveira
  Cc: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
	linux-pm, linux-kernel, kernel-dev, syzbot+3b3852c6031d0f30dfaf
In-Reply-To: <a61c19043dcf579e46f7d4f124013e60@igalia.com>

On Wed, Mar 25, 2026 at 4:13 PM Mauricio Faria de Oliveira
<mfo@igalia.com> wrote:
>
> On 2026-03-25 11:28, Mauricio Faria de Oliveira wrote:
> > On 2026-03-25 11:17, Mauricio Faria de Oliveira wrote:
> >> Thanks for looking into this.
> >>
> >> On 2026-03-25 09:47, Rafael J. Wysocki wrote:
> >>> I can see the one between thermal_zone_device_unregister() and
> >>> thermal_zone_device_resume(), but that can be addressed by adding a
> >>> TZ_STATE_FLAG_EXIT check to the latter AFAICS.
> >>
> >
> > Please disregard this paragraph; I incorrectly read/wrote _resume()
> > as thermal_zone_pm_complete() discussed above. The rest should be
> > right. I'll review this and get back shortly.
> >
> >> In the example describe above and detailed below, apparently that
> >> is not sufficient, if I'm not missing anything. See, if _resume()
> >> is reached with thermal_list_lock held, thermal_zone_device_exit()
> >> is waiting for thermal_list_lock before setting TZ_STATE_FLAG_EXIT,
> >> thus a check for it in _resume() would find it clear yet.
>
> Ok, similarly:
>
> Say, thermal_pm_notify() -> thermal_pm_notify_complete() ->
> thermal_zone_pm_complete()
> run before thermal_zone_device_unregister() is called;
> thermal_zone_device_resume()
> starts, and by now thermal_zone_device_unregister() is called.
>
> If thermal_zone_device_resume() wins the race over thermal_zone_exit()
> for guard(thermal_zone(tz) (tz->lock), it sees TZ_STATE_FLAG_EXIT clear;
> note its callees (eg, thermal_zone_device_init()) run with tz->lock
> held,
> so they see it clear as well.
>
> So, thermal_zone_device_init() calls INIT_DELAYED_WORK(), everything
> returns, tz->lock is released and the thermal_zone_device_unregister()
> -> thermal_zone_exit() path can continue to run.
>
> Only now thermal_zone_exit() sets TZ_STATE_FLAG_EXIT (too late),
> returns.
> cancel_delayed_work_sync() does not wait for
> thermal_zone_device_resume()
> due to INIT_DELAYED_WORK() in thermal_zone_device_init(); and kfree(tz).
>
> Then, thermal_zone_device_resume() accesses tz and hits use-after-free.
>
> Hope this clarifies. Please let me know your thoughts. Thanks!

Thanks for the analysis, it sounds accurate.

I'd say that thermal_zone_device_unregister() needs to flush the
workqueue before calling cancel_delayed_work_sync() to get rid of the
stuff that may be running out of it that hasn't seen the changes made
by thermal_zone_exit().

This should take care of all of the existing races because if anything
is running out of the workqueue when thermal_zone_device_unregister()
runs, it will be waited for after calling thermal_zone_exit() and any
leftover stuff will be caught by cancel_delayed_work_sync().

Of course, it's better to switch over to using a dedicated workqueue
in the thermal core for that.

^ permalink raw reply

* Re: [PATCH v10 00/12] barrier: Add smp_cond_load_{relaxed,acquire}_timeout()
From: Catalin Marinas @ 2026-03-25 15:55 UTC (permalink / raw)
  To: David Laight
  Cc: Ankur Arora, Andrew Morton, linux-kernel, linux-arch,
	linux-arm-kernel, linux-pm, bpf, arnd, will, peterz, mark.rutland,
	harisokn, cl, ast, rafael, daniel.lezcano, memxor, zhenglifeng1,
	xueshuai, rdunlap, joao.m.martins, boris.ostrovsky, konrad.wilk
In-Reply-To: <20260316233712.7cbfac27@pumpkin>

On Mon, Mar 16, 2026 at 11:37:12PM +0000, David Laight wrote:
> On Mon, 16 Mar 2026 15:08:07 -0700
> Ankur Arora <ankur.a.arora@oracle.com> wrote:
> > However, as David Laight pointed out in this thread
> > (https://lore.kernel.org/lkml/20260214113122.70627a8b@pumpkin/)
> > that this would be fine so long as the polling is on memory, but would
> > need some work to handle MMIO.
> 
> I'm not sure the current code works with MMIO on arm64.

It won't but also passing an MMIO pointer to smp_cond_load() is wrong in
general. You'd need a new API that takes an __iomem pointer.

> I was looking at the osq_lock() code, it uses smp_cond_load() with 'expr'
> being 'VAL || need_resched()' expecting to get woken by the IPI associated
> with the preemption being requested.
> But the arm64 code relies on 'wfe' being woken when the memory write
> 'breaks' the 'ldx' for the monitored location.
> That will only work for cached addresses.

Even worse, depending on the hardware, you may even get a data abort
when attempting LDXR on Device memory.

> For osq_lock(), while an IPI will wake it up, there is also a small timing
> window where the IPI can happen before the ldx and so not actually wake up it.
> This is true whenever 'expr' is non-trivial.

Hmm, I thought this is fine because of the implicit SEVL on exception
return but the arm64 __cmpwait_relaxed() does a SEVL+WFE which clears
any prior event, it can wait in theory forever when the event stream is
disabled.

Expanding smp_cond_load_relaxed() into asm, we have something like:

	LDR	X0, [PTR]
	condition check for VAL || need_resched() with branch out
	SEVL
	WFE
	LDXR	X1, [PTR]
	EOR	X1, X1, X0
	CBNZ	out
	WFE
  out:

If the condition is updated to become true (need_resched()) after the
condition check but before the first WFE while *PTR remains unchanged,
the IPI won't do anything. Maybe we should revert 1cfc63b5ae60 ("arm64:
cmpwait: Clear event register before arming exclusive monitor"). Not
great but probably better than reverting f5bfdc8e3947 ("locking/osq: Use
optimized spinning loop for arm64")).

Using SEV instead of IPI would have the same problem.

-- 
Catalin

^ permalink raw reply

* Re: [PATCH v10 00/12] barrier: Add smp_cond_load_{relaxed,acquire}_timeout()
From: David Laight @ 2026-03-25 15:42 UTC (permalink / raw)
  To: Catalin Marinas
  Cc: Ankur Arora, Andrew Morton, linux-kernel, linux-arch,
	linux-arm-kernel, linux-pm, bpf, arnd, will, peterz, mark.rutland,
	harisokn, cl, ast, rafael, daniel.lezcano, memxor, zhenglifeng1,
	xueshuai, rdunlap, joao.m.martins, boris.ostrovsky, konrad.wilk
In-Reply-To: <acPo7l_pd5rnHpSX@arm.com>

On Wed, 25 Mar 2026 13:53:50 +0000
Catalin Marinas <catalin.marinas@arm.com> wrote:

> On Tue, Mar 17, 2026 at 09:17:05AM +0000, David Laight wrote:
> > On Mon, 16 Mar 2026 23:53:22 -0700
> > Ankur Arora <ankur.a.arora@oracle.com> wrote:  
> > > David Laight <david.laight.linux@gmail.com> writes:  
> > > > On arm64 I think you could use explicit sev and wfe - but that will wake all
> > > > 'sleeping' cpu; and you may not want the 'thundering herd'.    
> > > 
> > > Wouldn't we still have the same narrow window where the CPU disregards the IPI?  
> > 
> > You need a 'sevl' in the interrupt exit path.  
> 
> No need to, see the rule below in
> https://developer.arm.com/documentation/ddi0487/maa/2983-beijhbbd:
> 
> R_XRZRK
>   The Event Register for a PE is set by any of the following:
>   [...]
>   - An exception return.
> 

It is a shame the pages for the SEV and WFE instructions don't mention that.
And the copy I found doesn't have working hyperlinks to any other sections.
(Not even references to related instructions...)

You do need to at least comment that the "msr s0_3_c1_c0_0, %[ecycles]" is
actually WFET.
Is that using an absolute cycle count?
If so does it work if the time has already passed?
If it is absolute do you need to recalculate it every time around the loop?
__delay_cycles() contains guard(preempt_notrace()). I haven't looked what
that does but is it needed here since preemption is disabled?

Looking at the code I think the "sevl; wfe" pair should be higher up.
If they were before the evaluation of the condition then an IPI that set
need_resched() just after it was tested would cause a wakeup.
Clearly that won't help if the condition does anything that executes 'wfe'
and won't sleep if it sets the event - but I suspect they are unlikely.

I also wonder how long it takes the cpu to leave any low power state.
We definitely found that was an issue on some x86 cpu and had to both
disable the lowest low power state and completely rework some wakeup
code that really wanted a 'thundering herd' rather than the very gentle
'bring each cpu out of low power one at a time' that cv_broadcast()
gave it.

	David


^ permalink raw reply

* Re: [PATCH] cpuidle: Deny idle entry when CPU already have IPI interrupt pending
From: Maulik Shah (mkshah) @ 2026-03-25 15:34 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Rafael J. Wysocki, Daniel Lezcano, Christian Loehle, linux-pm,
	linux-kernel, linux-arm-msm
In-Reply-To: <CAPDyKFqJX3rN0GY1j3-RDXGc0jCcMTG=OmWzi9+2y7CXJOvY3A@mail.gmail.com>



On 3/24/2026 9:16 PM, Ulf Hansson wrote:
> 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.

Yes, addressing in v2.

> 
> 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.

Yes, i did play moving this and "stats" do change with differences in next idle state selection.

below is high level scenario happening when the IPI deny change is placed inside target_state->enter()
or any place which will update the rejected count.

Using a menu governor,

 entered_state = target_state->enter(dev, drv, index);

 - entered_state will be negative error when IPI is pending.

 - This makes rejected count increment (which is okay) but it also makes dev->last_residency_ns = 0;

 - For next idle entry, menu governor will log this last interval as failed,
   via menu_select() -> menu_update_intervals(data, UINT_MAX);
   this is because menu_reflect() is not invoked for failed entry.

 - This makes 1 of last 8 intervals invalid, which don't get accounted from get_typical_interval(),
   hence divisor value in this API is never reaching 8, the goto again, loop inside get_typical_interval()
   will get aborted "faster". This interval logging have good influence on next 8 idle entries until
   it will get replaced with meaningful value.

 - In ideal case get_typical_interval() would go from divisor value reaching 8, 7 and then 6 and once its down to 6,
   it gets aborted if not predicted yet, so maximum 3 trials, but with any interval invalid
   it finishes faster, often without prediction.

 - sometimes IPI bailouts may happen frequently, in such cases we have 3 to 4 intervals as invalid in history,
   effectively making get_typical_interval() not predicting since divisor value in first loop would be less than 6.

 - Not predicting via get_typical_interval() can make deeper idle selection more often by only going with
   adjusted sleep lengths.

In summary, 

The benefits seen by aborting the idle entry with IPI pending getting nullified when it is logged
as "rejected" due to next idle entries can go deeper.

If we ignore setting dev->last_residency_ns = 0 for rejected cases or make menu_select() ignore the
last rejected iteration to update in intervals history, this would improve the chances of get_typical_interval()
to predict a meaningful sleep length (in a separate change).

For this change, IMO its better to bail out early without logging, since CPU did not make entry to idle driver yet.

Thanks,
Maulik

> 
>> +
>>         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

* Re: [PATCH] thermal: core: fix use-after-free due to init/cancel delayed_work race
From: Mauricio Faria de Oliveira @ 2026-03-25 15:13 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Daniel Lezcano, Zhang Rui, Lukasz Luba, linux-pm, linux-kernel,
	kernel-dev, syzbot+3b3852c6031d0f30dfaf
In-Reply-To: <52d861b9a215150424ae4d49b4e2c90b@igalia.com>

On 2026-03-25 11:28, Mauricio Faria de Oliveira wrote:
> On 2026-03-25 11:17, Mauricio Faria de Oliveira wrote:
>> Thanks for looking into this.
>> 
>> On 2026-03-25 09:47, Rafael J. Wysocki wrote:
>>> I can see the one between thermal_zone_device_unregister() and
>>> thermal_zone_device_resume(), but that can be addressed by adding a
>>> TZ_STATE_FLAG_EXIT check to the latter AFAICS.
>>
> 
> Please disregard this paragraph; I incorrectly read/wrote _resume()
> as thermal_zone_pm_complete() discussed above. The rest should be
> right. I'll review this and get back shortly.
>
>> In the example describe above and detailed below, apparently that
>> is not sufficient, if I'm not missing anything. See, if _resume()
>> is reached with thermal_list_lock held, thermal_zone_device_exit()
>> is waiting for thermal_list_lock before setting TZ_STATE_FLAG_EXIT,
>> thus a check for it in _resume() would find it clear yet.

Ok, similarly: 

Say, thermal_pm_notify() -> thermal_pm_notify_complete() ->
thermal_zone_pm_complete() 
run before thermal_zone_device_unregister() is called;
thermal_zone_device_resume()
starts, and by now thermal_zone_device_unregister() is called.

If thermal_zone_device_resume() wins the race over thermal_zone_exit()
for guard(thermal_zone(tz) (tz->lock), it sees TZ_STATE_FLAG_EXIT clear;
note its callees (eg, thermal_zone_device_init()) run with tz->lock
held,
so they see it clear as well.

So, thermal_zone_device_init() calls INIT_DELAYED_WORK(), everything
returns, tz->lock is released and the thermal_zone_device_unregister()
-> thermal_zone_exit() path can continue to run.

Only now thermal_zone_exit() sets TZ_STATE_FLAG_EXIT (too late),
returns.
cancel_delayed_work_sync() does not wait for
thermal_zone_device_resume()
due to INIT_DELAYED_WORK() in thermal_zone_device_init(); and kfree(tz).

Then, thermal_zone_device_resume() accesses tz and hits use-after-free.

Hope this clarifies. Please let me know your thoughts. Thanks!

-- 
Mauricio

^ permalink raw reply

* Re: [PATCH RESEND v8 3/6] mfd: max77759: add register bitmasks and modify irq configs for charger
From: Lee Jones @ 2026-03-25 15:10 UTC (permalink / raw)
  To: Amit Sunil Dhamne via B4 Relay
  Cc: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	André Draszik, Greg Kroah-Hartman, Badhri Jagan Sridharan,
	Heikki Krogerus, Peter Griffin, Tudor Ambarus, Alim Akhtar,
	Mark Brown, Matti Vaittinen, Andrew Morton, linux-kernel,
	linux-pm, devicetree, linux-usb, linux-arm-kernel,
	linux-samsung-soc, RD Babiera, Kyle Tso, Amit Sunil Dhamne
In-Reply-To: <20260314-max77759-charger-v8-3-226ca5f8c7d2@google.com>

On Sat, 14 Mar 2026, Amit Sunil Dhamne via B4 Relay wrote:

> From: Amit Sunil Dhamne <amitsd@google.com>
> 
> Add register bitmasks for charger function.
> 
> In addition split the charger IRQs further such that each bit represents
> an IRQ downstream of charger regmap irq chip. In addition populate the
> ack_base to offload irq ack to the regmap irq chip framework.
> 
> Signed-off-by: Amit Sunil Dhamne <amitsd@google.com>
> Reviewed-by: André Draszik <andre.draszik@linaro.org>
> ---
>  drivers/mfd/max77759.c       |  91 ++++++++++++++++++++--
>  include/linux/mfd/max77759.h | 176 ++++++++++++++++++++++++++++++++++++-------
>  2 files changed, 230 insertions(+), 37 deletions(-)
> 
> diff --git a/drivers/mfd/max77759.c b/drivers/mfd/max77759.c
> index a7efe233ec8c..288746f675b8 100644
> --- a/drivers/mfd/max77759.c
> +++ b/drivers/mfd/max77759.c
> @@ -201,8 +201,24 @@ static const struct regmap_config max77759_regmap_config_charger = {
>   *         - SYSUVLO_INT
>   *         - FSHIP_NOT_RD
>   *     - CHGR_INT: charger
> - *       - CHG_INT
> - *       - CHG_INT2
> + *       - INT1
> + *         - AICL
> + *         - CHGIN
> + *         - WCIN
> + *         - CHG
> + *         - BAT
> + *         - INLIM
> + *         - THM2
> + *         - BYP
> + *       - INT2
> + *         - INSEL
> + *         - SYS_UVLO1
> + *         - SYS_UVLO2
> + *         - BAT_OILO
> + *         - CHG_STA_CC
> + *         - CHG_STA_CV
> + *         - CHG_STA_TO
> + *         - CHG_STA_DONE
>   */
>  enum {
>  	MAX77759_INT_MAXQ,
> @@ -256,8 +286,38 @@ static const struct regmap_irq max77759_topsys_irqs[] = {
>  };
>  
>  static const struct regmap_irq max77759_chgr_irqs[] = {
> -	REGMAP_IRQ_REG(MAX77759_CHARGER_INT_1, 0, GENMASK(7, 0)),
> -	REGMAP_IRQ_REG(MAX77759_CHARGER_INT_2, 1, GENMASK(7, 0)),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT1_AICL, 0,
> +		       MAX77759_CHGR_REG_CHG_INT_AICL),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT1_CHGIN, 0,
> +		       MAX77759_CHGR_REG_CHG_INT_CHGIN),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT1_WCIN, 0,
> +		       MAX77759_CHGR_REG_CHG_INT_WCIN),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT1_CHG, 0,
> +		       MAX77759_CHGR_REG_CHG_INT_CHG),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT1_BAT, 0,
> +		       MAX77759_CHGR_REG_CHG_INT_BAT),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT1_INLIM, 0,
> +		       MAX77759_CHGR_REG_CHG_INT_INLIM),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT1_THM2, 0,
> +		       MAX77759_CHGR_REG_CHG_INT_THM2),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT1_BYP, 0,
> +		       MAX77759_CHGR_REG_CHG_INT_BYP),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT2_INSEL, 1,
> +		       MAX77759_CHGR_REG_CHG_INT2_INSEL),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT2_SYS_UVLO1, 1,
> +		       MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO1),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT2_SYS_UVLO2, 1,
> +		       MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO2),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT2_BAT_OILO, 1,
> +		       MAX77759_CHGR_REG_CHG_INT2_BAT_OILO),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_CC, 1,
> +		       MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CC),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_CV, 1,
> +		       MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CV),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_TO, 1,
> +		       MAX77759_CHGR_REG_CHG_INT2_CHG_STA_TO),
> +	REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_DONE, 1,
> +		       MAX77759_CHGR_REG_CHG_INT2_CHG_STA_DONE),
>  };
>  
>  static const struct regmap_irq_chip max77759_pmic_irq_chip = {
> @@ -302,6 +362,7 @@ static const struct regmap_irq_chip max77759_chrg_irq_chip = {

Minor nit: The new code in this patch consistently uses "chgr" as the prefix
for charger-related names. To improve consistency, how about we rename this
struct to `max77759_chgr_irq_chip`?

>  	.domain_suffix = "CHGR",
>  	.status_base = MAX77759_CHGR_REG_CHG_INT,
>  	.mask_base = MAX77759_CHGR_REG_CHG_INT_MASK,
> +	.ack_base = MAX77759_CHGR_REG_CHG_INT,
>  	.num_regs = 2,
>  	.irqs = max77759_chgr_irqs,
>  	.num_irqs = ARRAY_SIZE(max77759_chgr_irqs),
> @@ -325,8 +386,22 @@ static const struct resource max77759_gpio_resources[] = {
>  };
>  
>  static const struct resource max77759_charger_resources[] = {
> -	DEFINE_RES_IRQ_NAMED(MAX77759_CHARGER_INT_1, "INT1"),
> -	DEFINE_RES_IRQ_NAMED(MAX77759_CHARGER_INT_2, "INT2"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_AICL,         "AICL"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_CHGIN,        "CHGIN"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_WCIN,         "WCIN"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_CHG,          "CHG"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_BAT,          "BAT"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_INLIM,        "INLIM"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_THM2,         "THM2"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_BYP,          "BYP"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_INSEL,        "INSEL"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_SYS_UVLO1,    "SYS_UVLO1"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_SYS_UVLO2,    "SYS_UVLO2"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_BAT_OILO,     "BAT_OILO"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_CC,   "CHG_STA_CC"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_CV,   "CHG_STA_CV"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_TO,   "CHG_STA_TO"),
> +	DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_DONE, "CHG_STA_DONE"),
>  };
>  
>  static const struct mfd_cell max77759_cells[] = {
> diff --git a/include/linux/mfd/max77759.h b/include/linux/mfd/max77759.h
> index c6face34e385..fd5aea21ab2e 100644
> --- a/include/linux/mfd/max77759.h
> +++ b/include/linux/mfd/max77759.h
> @@ -59,35 +59,65 @@
>  #define MAX77759_MAXQ_REG_AP_DATAIN0            0xb1
>  #define MAX77759_MAXQ_REG_UIC_SWRST             0xe0
>  
> -#define MAX77759_CHGR_REG_CHG_INT               0xb0
> -#define MAX77759_CHGR_REG_CHG_INT2              0xb1
> -#define MAX77759_CHGR_REG_CHG_INT_MASK          0xb2
> -#define MAX77759_CHGR_REG_CHG_INT2_MASK         0xb3
> -#define MAX77759_CHGR_REG_CHG_INT_OK            0xb4
> -#define MAX77759_CHGR_REG_CHG_DETAILS_00        0xb5
> -#define MAX77759_CHGR_REG_CHG_DETAILS_01        0xb6
> -#define MAX77759_CHGR_REG_CHG_DETAILS_02        0xb7
> -#define MAX77759_CHGR_REG_CHG_DETAILS_03        0xb8
> -#define MAX77759_CHGR_REG_CHG_CNFG_00           0xb9
> -#define MAX77759_CHGR_REG_CHG_CNFG_01           0xba
> -#define MAX77759_CHGR_REG_CHG_CNFG_02           0xbb
> -#define MAX77759_CHGR_REG_CHG_CNFG_03           0xbc
> -#define MAX77759_CHGR_REG_CHG_CNFG_04           0xbd
> -#define MAX77759_CHGR_REG_CHG_CNFG_05           0xbe
> -#define MAX77759_CHGR_REG_CHG_CNFG_06           0xbf
> -#define MAX77759_CHGR_REG_CHG_CNFG_07           0xc0
> -#define MAX77759_CHGR_REG_CHG_CNFG_08           0xc1
> -#define MAX77759_CHGR_REG_CHG_CNFG_09           0xc2
> -#define MAX77759_CHGR_REG_CHG_CNFG_10           0xc3
> -#define MAX77759_CHGR_REG_CHG_CNFG_11           0xc4
> -#define MAX77759_CHGR_REG_CHG_CNFG_12           0xc5
> -#define MAX77759_CHGR_REG_CHG_CNFG_13           0xc6
> -#define MAX77759_CHGR_REG_CHG_CNFG_14           0xc7
> -#define MAX77759_CHGR_REG_CHG_CNFG_15           0xc8
> -#define MAX77759_CHGR_REG_CHG_CNFG_16           0xc9
> -#define MAX77759_CHGR_REG_CHG_CNFG_17           0xca
> -#define MAX77759_CHGR_REG_CHG_CNFG_18           0xcb
> -#define MAX77759_CHGR_REG_CHG_CNFG_19           0xcc
> +#define MAX77759_CHGR_REG_CHG_INT                      0xb0
> +#define   MAX77759_CHGR_REG_CHG_INT_AICL               BIT(7)
> +#define   MAX77759_CHGR_REG_CHG_INT_CHGIN              BIT(6)
> +#define   MAX77759_CHGR_REG_CHG_INT_WCIN               BIT(5)
> +#define   MAX77759_CHGR_REG_CHG_INT_CHG                BIT(4)
> +#define   MAX77759_CHGR_REG_CHG_INT_BAT                BIT(3)
> +#define   MAX77759_CHGR_REG_CHG_INT_INLIM              BIT(2)
> +#define   MAX77759_CHGR_REG_CHG_INT_THM2               BIT(1)
> +#define   MAX77759_CHGR_REG_CHG_INT_BYP                BIT(0)
> +#define MAX77759_CHGR_REG_CHG_INT2                     0xb1
> +#define   MAX77759_CHGR_REG_CHG_INT2_INSEL             BIT(7)
> +#define   MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO1         BIT(6)
> +#define   MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO2         BIT(5)
> +#define   MAX77759_CHGR_REG_CHG_INT2_BAT_OILO          BIT(4)
> +#define   MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CC        BIT(3)
> +#define   MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CV        BIT(2)
> +#define   MAX77759_CHGR_REG_CHG_INT2_CHG_STA_TO        BIT(1)
> +#define   MAX77759_CHGR_REG_CHG_INT2_CHG_STA_DONE      BIT(0)
> +#define MAX77759_CHGR_REG_CHG_INT_MASK                 0xb2
> +#define MAX77759_CHGR_REG_CHG_INT2_MASK                0xb3
> +#define MAX77759_CHGR_REG_CHG_INT_OK                   0xb4
> +#define MAX77759_CHGR_REG_CHG_DETAILS_00               0xb5
> +#define   MAX77759_CHGR_REG_CHG_DETAILS_00_CHGIN_DTLS  GENMASK(6, 5)
> +#define MAX77759_CHGR_REG_CHG_DETAILS_01               0xb6
> +#define   MAX77759_CHGR_REG_CHG_DETAILS_01_BAT_DTLS    GENMASK(6, 4)
> +#define   MAX77759_CHGR_REG_CHG_DETAILS_01_CHG_DTLS    GENMASK(3, 0)
> +#define MAX77759_CHGR_REG_CHG_DETAILS_02               0xb7
> +#define   MAX77759_CHGR_REG_CHG_DETAILS_02_CHGIN_STS   BIT(5)
> +#define MAX77759_CHGR_REG_CHG_DETAILS_03               0xb8
> +#define MAX77759_CHGR_REG_CHG_CNFG_00                  0xb9
> +#define   MAX77759_CHGR_REG_CHG_CNFG_00_MODE           GENMASK(3, 0)
> +#define MAX77759_CHGR_REG_CHG_CNFG_01                  0xba
> +#define MAX77759_CHGR_REG_CHG_CNFG_02                  0xbb
> +#define   MAX77759_CHGR_REG_CHG_CNFG_02_CHGCC          GENMASK(5, 0)
> +#define MAX77759_CHGR_REG_CHG_CNFG_03                  0xbc
> +#define MAX77759_CHGR_REG_CHG_CNFG_04                  0xbd
> +#define   MAX77759_CHGR_REG_CHG_CNFG_04_CHG_CV_PRM     GENMASK(5, 0)
> +#define MAX77759_CHGR_REG_CHG_CNFG_05                  0xbe
> +#define MAX77759_CHGR_REG_CHG_CNFG_06                  0xbf
> +#define   MAX77759_CHGR_REG_CHG_CNFG_06_CHGPROT        GENMASK(3, 2)
> +#define MAX77759_CHGR_REG_CHG_CNFG_07                  0xc0
> +#define MAX77759_CHGR_REG_CHG_CNFG_08                  0xc1
> +#define MAX77759_CHGR_REG_CHG_CNFG_09                  0xc2
> +#define   MAX77759_CHGR_REG_CHG_CNFG_09_CHGIN_ILIM     GENMASK(6, 0)
> +#define MAX77759_CHGR_REG_CHG_CNFG_10                  0xc3
> +#define MAX77759_CHGR_REG_CHG_CNFG_11                  0xc4
> +#define MAX77759_CHGR_REG_CHG_CNFG_12                  0xc5
> +/* Wireless Charging input channel select */
> +#define   MAX77759_CHGR_REG_CHG_CNFG_12_WCINSEL        BIT(6)
> +/* CHGIN/USB input channel select */
> +#define   MAX77759_CHGR_REG_CHG_CNFG_12_CHGINSEL       BIT(5)
> +#define MAX77759_CHGR_REG_CHG_CNFG_13                  0xc6
> +#define MAX77759_CHGR_REG_CHG_CNFG_14                  0xc7
> +#define MAX77759_CHGR_REG_CHG_CNFG_15                  0xc8
> +#define MAX77759_CHGR_REG_CHG_CNFG_16                  0xc9
> +#define MAX77759_CHGR_REG_CHG_CNFG_17                  0xca
> +#define MAX77759_CHGR_REG_CHG_CNFG_18                  0xcb
> +#define   MAX77759_CHGR_REG_CHG_CNFG_18_WDTEN          BIT(0)
> +#define MAX77759_CHGR_REG_CHG_CNFG_19                  0xcc
>  
>  /* MaxQ opcodes for max77759_maxq_command() */
>  #define MAX77759_MAXQ_OPCODE_MAXLENGTH (MAX77759_MAXQ_REG_AP_DATAOUT32 - \
> @@ -101,6 +131,94 @@
>  #define MAX77759_MAXQ_OPCODE_USER_SPACE_READ     0x81
>  #define MAX77759_MAXQ_OPCODE_USER_SPACE_WRITE    0x82
>  
> +/*
> + * Charger Input Status
> + * @MAX77759_CHGR_CHGIN_DTLS_VBUS_UNDERVOLTAGE:
> + *     Charger input voltage (Vchgin) < Under Voltage Threshold (Vuvlo)
> + * @MAX77759_CHGR_CHGIN_DTLS_VBUS_MARGINAL_VOLTAGE: Vchgin > Vuvlo and
> + *     Vchgin < (Battery Voltage (Vbatt) + system voltage (Vsys))
> + * @MAX77759_CHGR_CHGIN_DTLS_VBUS_OVERVOLTAGE:
> + *     Vchgin > Over Voltage threshold (Vovlo)
> + * @MAX77759_CHGR_CHGIN_DTLS_VBUS_VALID:
> + *     Vchgin > Vuvlo, Vchgin < Vovlo and Vchgin > (Vsys + Vbatt)
> + */
> +enum max77759_chgr_chgin_dtls_status {
> +	MAX77759_CHGR_CHGIN_DTLS_VBUS_UNDERVOLTAGE,
> +	MAX77759_CHGR_CHGIN_DTLS_VBUS_MARGINAL_VOLTAGE,
> +	MAX77759_CHGR_CHGIN_DTLS_VBUS_OVERVOLTAGE,
> +	MAX77759_CHGR_CHGIN_DTLS_VBUS_VALID,
> +};
> +
> +/*
> + * Battery Details
> + * @MAX77759_CHGR_BAT_DTLS_NO_BATT_CHG_SUSP:
> + *     No battery and the charger suspended
> + * @MAX77759_CHGR_BAT_DTLS_DEAD_BATTERY: Vbatt < Vtrickle
> + * @MAX77759_CHGR_BAT_DTLS_BAT_CHG_TIMER_FAULT:
> + *     Charging suspended due to timer fault
> + * @MAX77759_CHGR_BAT_DTLS_BAT_OKAY:
> + *     Battery okay and Vbatt > Min Sys Voltage (Vsysmin)
> + * @MAX77759_CHGR_BAT_DTLS_BAT_UNDERVOLTAGE:
> + *     Battery is okay. Vtrickle < Vbatt < Vsysmin
> + * @MAX77759_CHGR_BAT_DTLS_BAT_OVERVOLTAGE:
> + *     Battery voltage > Overvoltage threshold
> + * @MAX77759_CHGR_BAT_DTLS_BAT_OVERCURRENT:
> + *     Battery current exceeds overcurrent threshold
> + * @MAX77759_CHGR_BAT_DTLS_BAT_ONLY_MODE:
> + *     Battery only mode and battery level not available
> + */
> +enum max77759_chgr_bat_dtls_states {
> +	MAX77759_CHGR_BAT_DTLS_NO_BATT_CHG_SUSP,
> +	MAX77759_CHGR_BAT_DTLS_DEAD_BATTERY,
> +	MAX77759_CHGR_BAT_DTLS_BAT_CHG_TIMER_FAULT,
> +	MAX77759_CHGR_BAT_DTLS_BAT_OKAY,
> +	MAX77759_CHGR_BAT_DTLS_BAT_UNDERVOLTAGE,
> +	MAX77759_CHGR_BAT_DTLS_BAT_OVERVOLTAGE,
> +	MAX77759_CHGR_BAT_DTLS_BAT_OVERCURRENT,
> +	MAX77759_CHGR_BAT_DTLS_BAT_ONLY_MODE,
> +};
> +
> +/*
> + * Charger Details
> + * @MAX77759_CHGR_CHG_DTLS_PREQUAL: Charger in prequalification mode
> + * @MAX77759_CHGR_CHG_DTLS_CC:      Charger in fast charge const curr mode
> + * @MAX77759_CHGR_CHG_DTLS_CV:      Charger in fast charge const voltage mode
> + * @MAX77759_CHGR_CHG_DTLS_TO:      Charger is in top off mode
> + * @MAX77759_CHGR_CHG_DTLS_DONE:    Charger is done
> + * @MAX77759_CHGR_CHG_DTLS_RSVD_1:  Reserved
> + * @MAX77759_CHGR_CHG_DTLS_TIMER_FAULT:   Charger is in timer fault mode
> + * @MAX77759_CHGR_CHG_DTLS_SUSP_BATT_THM:
> + *     Charger is suspended as bettery removal detected

Typo here, s/bettery/battery/.

> + * @MAX77759_CHGR_CHG_DTLS_OFF:
> + *     Charger is off. Input invalid or charger disabled
> + * @MAX77759_CHGR_CHG_DTLS_RSVD_2:  Reserved
> + * @MAX77759_CHGR_CHG_DTLS_RSVD_3:  Reserved
> + * @MAX77759_CHGR_CHG_DTLS_OFF_WDOG_TIMER:
> + *     Charger is off as watchdog timer expired
> + * @MAX77759_CHGR_CHG_DTLS_SUSP_JEITA:    Charger is in JEITA control mode
> + */
> +enum max77759_chgr_chg_dtls_states {

Just a small style suggestion, could you please align the descriptions in this
kerneldoc block? It improves readability. Using a consistent number of spaces
or tabs after the colon helps.

Feel free to use up to 100-chars if it improves readability.

> +	MAX77759_CHGR_CHG_DTLS_PREQUAL,
> +	MAX77759_CHGR_CHG_DTLS_CC,
> +	MAX77759_CHGR_CHG_DTLS_CV,
> +	MAX77759_CHGR_CHG_DTLS_TO,
> +	MAX77759_CHGR_CHG_DTLS_DONE,
> +	MAX77759_CHGR_CHG_DTLS_RSVD_1,
> +	MAX77759_CHGR_CHG_DTLS_TIMER_FAULT,
> +	MAX77759_CHGR_CHG_DTLS_SUSP_BATT_THM,
> +	MAX77759_CHGR_CHG_DTLS_OFF,
> +	MAX77759_CHGR_CHG_DTLS_RSVD_2,
> +	MAX77759_CHGR_CHG_DTLS_RSVD_3,
> +	MAX77759_CHGR_CHG_DTLS_OFF_WDOG_TIMER,
> +	MAX77759_CHGR_CHG_DTLS_SUSP_JEITA,
> +};
> +
> +enum max77759_chgr_mode {
> +	MAX77759_CHGR_MODE_OFF,
> +	MAX77759_CHGR_MODE_CHG_BUCK_ON = 0x5,
> +	MAX77759_CHGR_MODE_OTG_BOOST_ON = 0xA,
> +};
> +
>  /**
>   * struct max77759 - core max77759 internal data structure
>   *

-- 
Lee Jones [李琼斯]

^ permalink raw reply

* Re: [PATCH v3 09/13] leds: flash: add support for Samsung S2M series PMIC flash LED device
From: Lee Jones @ 2026-03-25 14:58 UTC (permalink / raw)
  To: Kaustabh Chakraborty
  Cc: Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
	Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
	Jonathan Corbet, Shuah Khan, Nam Tran, linux-leds, devicetree,
	linux-kernel, linux-pm, linux-samsung-soc, linux-rtc, linux-doc
In-Reply-To: <DH1XVOS6IIOE.HGIH6JQRHNAM@disroot.org>

On Sat, 14 Mar 2026, Kaustabh Chakraborty wrote:

> On 2026-03-10 11:38 +00:00, Lee Jones wrote:
> > On Wed, 25 Feb 2026, Kaustabh Chakraborty wrote:
> >
> >> Add support for flash LEDs found in certain Samsung S2M series PMICs.
> >> The device has two channels for LEDs, typically for the back and front
> >> cameras in mobile devices. Both channels can be independently
> >> controlled, and can be operated in torch or flash modes.
> >> 
> >> The driver includes initial support for the S2MU005 PMIC flash LEDs.
> >> 
> >> Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
> >> ---
> >>  drivers/leds/flash/Kconfig          |  12 +
> >>  drivers/leds/flash/Makefile         |   1 +
> >>  drivers/leds/flash/leds-s2m-flash.c | 429 ++++++++++++++++++++++++++++++++++++
> >>  3 files changed, 442 insertions(+)
> >> 
> >> diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig
> >> index 5e08102a67841..be62e05277429 100644
> >> --- a/drivers/leds/flash/Kconfig
> >> +++ b/drivers/leds/flash/Kconfig
> >> @@ -114,6 +114,18 @@ config LEDS_RT8515
> >>  	  To compile this driver as a module, choose M here: the module
> >>  	  will be called leds-rt8515.
> >>  
> >> +config LEDS_S2M_FLASH
> >> +	tristate "Samsung S2M series PMICs flash/torch LED support"
> >> +	depends on LEDS_CLASS
> >> +	depends on MFD_SEC_CORE
> >> +	depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
> >> +	select REGMAP_IRQ
> >> +	help
> >> +	  This option enables support for the flash/torch LEDs found in
> >> +	  certain Samsung S2M series PMICs, such as the S2MU005. It has
> >> +	  a LED channel dedicated for every physical LED. The LEDs can
> >> +	  be controlled in flash and torch modes.
> >> +
> >>  config LEDS_SGM3140
> >>  	tristate "LED support for the SGM3140"
> >>  	depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
> >> diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile
> >> index 712fb737a428e..44e6c1b4beb37 100644
> >> --- a/drivers/leds/flash/Makefile
> >> +++ b/drivers/leds/flash/Makefile
> >> @@ -10,6 +10,7 @@ obj-$(CONFIG_LEDS_MAX77693)	+= leds-max77693.o
> >>  obj-$(CONFIG_LEDS_QCOM_FLASH)	+= leds-qcom-flash.o
> >>  obj-$(CONFIG_LEDS_RT4505)	+= leds-rt4505.o
> >>  obj-$(CONFIG_LEDS_RT8515)	+= leds-rt8515.o
> >> +obj-$(CONFIG_LEDS_S2M_FLASH)	+= leds-s2m-flash.o
> >>  obj-$(CONFIG_LEDS_SGM3140)	+= leds-sgm3140.o
> >>  obj-$(CONFIG_LEDS_SY7802)	+= leds-sy7802.o
> >>  obj-$(CONFIG_LEDS_TPS6131X)	+= leds-tps6131x.o

[...]

> >> +static int s2mu005_fled_torch_brightness_set(struct led_classdev *cdev,
> >> +					     enum led_brightness value)
> >> +{
> >> +	struct s2m_fled *priv = to_led_priv(to_cdev_flash(cdev));
> >> +	struct regmap *regmap = priv->regmap;
> >> +	int ret;
> >> +
> >> +	mutex_lock(&priv->lock);
> >> +
> >> +	if (value == LED_OFF) {
> >
> > These defines are deprecated.
> >
> > From include/linux/leds.h:
> >
> > /* This is obsolete/useless. We now support variable maximum brightness. */
> > enum led_brightness {
> >         LED_OFF         = 0,
> >         LED_ON          = 1,
> >         LED_HALF        = 127,
> >         LED_FULL        = 255,
> > };
> >
> 
> Let me know what am I supposed to use then. The
> brightness_set_blocking() function is defined as such:
> 
> 	int (*brightness_set_blocking)(struct led_classdev *led_cdev,
> 				       enum led_brightness brightness);
> 
> Which has enum led_brightness as one of its params.
> 
> Do I just ignore the 'obsolete' param for now and replace ` == LED_OFF`
> with a logical NOT?

I'm pretty sure most places just treat this as a u8 these days.

-- 
Lee Jones [李琼斯]

^ permalink raw reply

* [PATCH v6 27/27] misc: lan966x_pci: Add drivers needed to support SFPs in Kconfig help
From: Herve Codina @ 2026-03-25 14:35 UTC (permalink / raw)
  To: Andrew Lunn, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Kalle Niemi, Matti Vaittinen,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	Michael Turquette, Stephen Boyd, Andi Shyti, Wolfram Sang,
	Peter Rosin, Arnd Bergmann, Herve Codina, Saravana Kannan,
	Bjorn Helgaas, Charles Keepax, Richard Fitzgerald, David Rhodes,
	Linus Walleij, Ulf Hansson, Mark Brown, Len Brown,
	Andy Shevchenko, Daniel Scally, Heikki Krogerus, Sakari Ailus,
	Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
	Vishal Verma, Ira Weiny, Dan Williams, Shawn Guo
  Cc: Wolfram Sang, linux-kernel, driver-core, imx, linux-arm-kernel,
	linux-clk, linux-i2c, devicetree, linux-pci, linux-sound, patches,
	linux-gpio, linux-pm, linux-spi, linux-acpi, linux-cxl,
	Allan Nielsen, Horatiu Vultur, Steen Hegelund, Luca Ceresoli,
	Thomas Petazzoni
In-Reply-To: <20260325143555.451852-1-herve.codina@bootlin.com>

Recently, new device-tree nodes were added in the overlay to add support
for SFPs on LAN966x PCI device.

The LAN966X Kconfig help section mentions drivers related to devices
added based on the overlay description.

Add drivers related to devices described by those new nodes in the
already existing driver list.

Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
 drivers/misc/Kconfig | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 9c285a7c88ba..69825dc0f85e 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -635,13 +635,18 @@ config MCHP_LAN966X_PCI
 	  Even if this driver does not depend on those other drivers, in order
 	  to have a fully functional board, the following drivers are needed:
 	    - fixed-clock (COMMON_CLK)
+	    - i2c-mux-pinctrl (I2C_MUX_PINCTRL)
 	    - lan966x-cpu-syscon (MFD_SYSCON)
+	    - lan966x-gck (COMMON_CLK_LAN966X)
 	    - lan966x-miim (MDIO_MSCC_MIIM)
 	    - lan966x-oic (LAN966X_OIC)
 	    - lan966x-pinctrl (PINCTRL_OCELOT)
 	    - lan966x-serdes (PHY_LAN966X_SERDES)
 	    - lan966x-switch (LAN966X_SWITCH)
 	    - lan966x-switch-reset (RESET_MCHP_SPARX5)
+	    - sam9x60-i2c (I2C_AT91)
+	    - sama5d2-flexcom (MFD_ATMEL_FLEXCOM)
+	    - sfp (SFP)
 
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
-- 
2.53.0


^ permalink raw reply related

* [PATCH v6 26/27] misc: lan966x_pci: Sort the drivers list in Kconfig help
From: Herve Codina @ 2026-03-25 14:35 UTC (permalink / raw)
  To: Andrew Lunn, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Kalle Niemi, Matti Vaittinen,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	Michael Turquette, Stephen Boyd, Andi Shyti, Wolfram Sang,
	Peter Rosin, Arnd Bergmann, Herve Codina, Saravana Kannan,
	Bjorn Helgaas, Charles Keepax, Richard Fitzgerald, David Rhodes,
	Linus Walleij, Ulf Hansson, Mark Brown, Len Brown,
	Andy Shevchenko, Daniel Scally, Heikki Krogerus, Sakari Ailus,
	Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
	Vishal Verma, Ira Weiny, Dan Williams, Shawn Guo
  Cc: Wolfram Sang, linux-kernel, driver-core, imx, linux-arm-kernel,
	linux-clk, linux-i2c, devicetree, linux-pci, linux-sound, patches,
	linux-gpio, linux-pm, linux-spi, linux-acpi, linux-cxl,
	Allan Nielsen, Horatiu Vultur, Steen Hegelund, Luca Ceresoli,
	Thomas Petazzoni
In-Reply-To: <20260325143555.451852-1-herve.codina@bootlin.com>

The LAN966X Kconfig help section mentions drivers related to
devices.

Sort this list alphabetically.

Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
 drivers/misc/Kconfig | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 5cc79d1517af..9c285a7c88ba 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -635,13 +635,13 @@ config MCHP_LAN966X_PCI
 	  Even if this driver does not depend on those other drivers, in order
 	  to have a fully functional board, the following drivers are needed:
 	    - fixed-clock (COMMON_CLK)
-	    - lan966x-oic (LAN966X_OIC)
 	    - lan966x-cpu-syscon (MFD_SYSCON)
-	    - lan966x-switch-reset (RESET_MCHP_SPARX5)
+	    - lan966x-miim (MDIO_MSCC_MIIM)
+	    - lan966x-oic (LAN966X_OIC)
 	    - lan966x-pinctrl (PINCTRL_OCELOT)
 	    - lan966x-serdes (PHY_LAN966X_SERDES)
-	    - lan966x-miim (MDIO_MSCC_MIIM)
 	    - lan966x-switch (LAN966X_SWITCH)
+	    - lan966x-switch-reset (RESET_MCHP_SPARX5)
 
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
-- 
2.53.0


^ permalink raw reply related

* [PATCH v6 25/27] misc: lan966x_pci: Add dtsi/dtso nodes in order to support SFPs
From: Herve Codina @ 2026-03-25 14:35 UTC (permalink / raw)
  To: Andrew Lunn, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Kalle Niemi, Matti Vaittinen,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	Michael Turquette, Stephen Boyd, Andi Shyti, Wolfram Sang,
	Peter Rosin, Arnd Bergmann, Herve Codina, Saravana Kannan,
	Bjorn Helgaas, Charles Keepax, Richard Fitzgerald, David Rhodes,
	Linus Walleij, Ulf Hansson, Mark Brown, Len Brown,
	Andy Shevchenko, Daniel Scally, Heikki Krogerus, Sakari Ailus,
	Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
	Vishal Verma, Ira Weiny, Dan Williams, Shawn Guo
  Cc: Wolfram Sang, linux-kernel, driver-core, imx, linux-arm-kernel,
	linux-clk, linux-i2c, devicetree, linux-pci, linux-sound, patches,
	linux-gpio, linux-pm, linux-spi, linux-acpi, linux-cxl,
	Allan Nielsen, Horatiu Vultur, Steen Hegelund, Luca Ceresoli,
	Thomas Petazzoni
In-Reply-To: <20260325143555.451852-1-herve.codina@bootlin.com>

Add device-tree nodes needed to support SFPs.
Those nodes are:
 - the clock controller
 - the i2c controller
 - the i2c mux
 - the SFPs themselves and their related ports in the switch

Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
 drivers/misc/lan966x_evb_lan9662_nic.dtso | 96 +++++++++++++++++++++++
 drivers/misc/lan966x_pci.dtsi             | 42 ++++++++++
 2 files changed, 138 insertions(+)

diff --git a/drivers/misc/lan966x_evb_lan9662_nic.dtso b/drivers/misc/lan966x_evb_lan9662_nic.dtso
index 3ad50abee72d..20e1fe4f78bf 100644
--- a/drivers/misc/lan966x_evb_lan9662_nic.dtso
+++ b/drivers/misc/lan966x_evb_lan9662_nic.dtso
@@ -4,6 +4,7 @@
  */
 
 #include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/mfd/atmel-flexcom.h>
 #include <dt-bindings/phy/phy-lan966x-serdes.h>
 
 /dts-v1/;
@@ -27,15 +28,94 @@ __overlay__ {
 			#size-cells = <2>;
 
 			#include "lan966x_pci.dtsi"
+
+			i2c0_emux: i2c0-emux {
+				compatible = "i2c-mux-pinctrl";
+				#address-cells = <1>;
+				#size-cells = <0>;
+				i2c-parent = <&i2c0>;
+				pinctrl-names = "i2c102", "i2c103", "idle";
+				pinctrl-0 = <&i2cmux_0>;
+				pinctrl-1 = <&i2cmux_1>;
+				pinctrl-2 = <&i2cmux_pins>;
+
+				i2c102: i2c@0 {
+					reg = <0>;
+					#address-cells = <1>;
+					#size-cells = <0>;
+				};
+
+				i2c103: i2c@1 {
+					reg = <1>;
+					#address-cells = <1>;
+					#size-cells = <0>;
+				};
+			};
+
+			sfp2: sfp2 {
+				compatible = "sff,sfp";
+				i2c-bus = <&i2c102>;
+				tx-disable-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
+				los-gpios = <&gpio 25 GPIO_ACTIVE_HIGH>;
+				mod-def0-gpios = <&gpio 18 GPIO_ACTIVE_LOW>;
+				tx-fault-gpios = <&gpio 2 GPIO_ACTIVE_HIGH>;
+			};
+
+			sfp3: sfp3 {
+				compatible = "sff,sfp";
+				i2c-bus = <&i2c103>;
+				tx-disable-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
+				los-gpios = <&gpio 26 GPIO_ACTIVE_HIGH>;
+				mod-def0-gpios = <&gpio 19 GPIO_ACTIVE_LOW>;
+				tx-fault-gpios = <&gpio 3 GPIO_ACTIVE_HIGH>;
+			};
 		};
 	};
 };
 
+&flx0 {
+	atmel,flexcom-mode = <ATMEL_FLEXCOM_MODE_TWI>;
+	status = "okay";
+};
+
+&i2c0 {
+	pinctrl-0 = <&fc0_a_pins>;
+	pinctrl-names = "default";
+	i2c-analog-filter;
+	i2c-digital-filter;
+	i2c-digital-filter-width-ns = <35>;
+	status = "okay";
+};
+
 &gpio {
 	tod_pins: tod_pins {
 		pins = "GPIO_36";
 		function = "ptpsync_1";
 	};
+
+	fc0_a_pins: fcb4-i2c-pins {
+		/* RXD, TXD */
+		pins = "GPIO_9", "GPIO_10";
+		function = "fc0_a";
+	};
+
+	i2cmux_pins: i2cmux-pins {
+		pins = "GPIO_76", "GPIO_77";
+		function = "twi_slc_gate";
+		output-low;
+	};
+
+	i2cmux_0: i2cmux-0 {
+		pins = "GPIO_76";
+		function = "twi_slc_gate";
+		output-high;
+	};
+
+	i2cmux_1: i2cmux-1 {
+		pins = "GPIO_77";
+		function = "twi_slc_gate";
+		output-high;
+	};
 };
 
 &lan966x_phy0 {
@@ -64,6 +144,22 @@ &port1 {
 	status = "okay";
 };
 
+&port2 {
+	phy-mode = "sgmii";
+	phys = <&serdes 2 SERDES6G(0)>;
+	sfp = <&sfp2>;
+	managed = "in-band-status";
+	status = "okay";
+};
+
+&port3 {
+	phy-mode = "sgmii";
+	phys = <&serdes 3 SERDES6G(1)>;
+	sfp = <&sfp3>;
+	managed = "in-band-status";
+	status = "okay";
+};
+
 &switch {
 	pinctrl-names = "default";
 	pinctrl-0 = <&tod_pins>;
diff --git a/drivers/misc/lan966x_pci.dtsi b/drivers/misc/lan966x_pci.dtsi
index 170298084fa5..d5c2056e4e5c 100644
--- a/drivers/misc/lan966x_pci.dtsi
+++ b/drivers/misc/lan966x_pci.dtsi
@@ -3,6 +3,7 @@
  * Copyright (C) 2025 Microchip UNG
  */
 
+#include <dt-bindings/clock/microchip,lan966x.h>
 #include <dt-bindings/interrupt-controller/irq.h>
 
 cpu_clk: clock-600000000 {
@@ -61,6 +62,39 @@ port1: port@1 {
 				reg = <1>;
 				status = "disabled";
 			};
+
+			port2: port@2 {
+				reg = <2>;
+				status = "disabled";
+			};
+
+			port3: port@3 {
+				reg = <3>;
+				status = "disabled";
+			};
+		};
+	};
+
+	flx0: flexcom@e0040000 {
+		compatible = "atmel,sama5d2-flexcom";
+		reg = <0xe0040000 0x100>;
+		clocks = <&clks GCK_ID_FLEXCOM0>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x0 0xe0040000 0x800>;
+		status = "disabled";
+
+		i2c0: i2c@600 {
+			compatible = "microchip,sam9x60-i2c";
+			reg = <0x600 0x200>;
+			interrupt-parent = <&oic>;
+			interrupts = <48 IRQ_TYPE_LEVEL_HIGH>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			clocks = <&clks GCK_ID_FLEXCOM0>;
+			assigned-clocks = <&clks GCK_ID_FLEXCOM0>;
+			assigned-clock-rates = <20000000>;
+			status = "disabled";
 		};
 	};
 
@@ -69,6 +103,14 @@ cpu_ctrl: syscon@e00c0000 {
 		reg = <0xe00c0000 0xa8>;
 	};
 
+	clks: clock-controller@e00c00a8 {
+		compatible = "microchip,lan966x-gck";
+		#clock-cells = <1>;
+		clocks = <&cpu_clk>, <&ddr_clk>, <&sys_clk>;
+		clock-names = "cpu", "ddr", "sys";
+		reg = <0xe00c00a8 0x38>, <0xe00c02cc 0x4>;
+	};
+
 	oic: oic@e00c0120 {
 		compatible = "microchip,lan966x-oic";
 		#interrupt-cells = <2>;
-- 
2.53.0


^ permalink raw reply related

* [PATCH v6 24/27] misc: lan966x_pci: Introduce board specific data
From: Herve Codina @ 2026-03-25 14:35 UTC (permalink / raw)
  To: Andrew Lunn, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Kalle Niemi, Matti Vaittinen,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	Michael Turquette, Stephen Boyd, Andi Shyti, Wolfram Sang,
	Peter Rosin, Arnd Bergmann, Herve Codina, Saravana Kannan,
	Bjorn Helgaas, Charles Keepax, Richard Fitzgerald, David Rhodes,
	Linus Walleij, Ulf Hansson, Mark Brown, Len Brown,
	Andy Shevchenko, Daniel Scally, Heikki Krogerus, Sakari Ailus,
	Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
	Vishal Verma, Ira Weiny, Dan Williams, Shawn Guo
  Cc: Wolfram Sang, linux-kernel, driver-core, imx, linux-arm-kernel,
	linux-clk, linux-i2c, devicetree, linux-pci, linux-sound, patches,
	linux-gpio, linux-pm, linux-spi, linux-acpi, linux-cxl,
	Allan Nielsen, Horatiu Vultur, Steen Hegelund, Luca Ceresoli,
	Thomas Petazzoni
In-Reply-To: <20260325143555.451852-1-herve.codina@bootlin.com>

Only one device-tree overlay (lan966x_evb_lan9662_nic.dtbo) is handled
and this overlay is directly referenced in lan966x_pci_load_overlay().

This avoid to use the code for an other board.

In order to be more generic and to allow support for other boards (PCI
Vendor/Device IDs), introduce the lan966x_pci_info structure and attach
it to PCI Vendor/Device IDs handled by the driver.

This structure contains information related to the PCI board such as
information related to the dtbo describing the board we have to load.

Signed-off-by: Herve Codina <herve.codina@bootlin.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
 drivers/misc/lan966x_pci.c | 30 ++++++++++++++++++++++--------
 1 file changed, 22 insertions(+), 8 deletions(-)

diff --git a/drivers/misc/lan966x_pci.c b/drivers/misc/lan966x_pci.c
index e6d1fce0b116..041e92f924c4 100644
--- a/drivers/misc/lan966x_pci.c
+++ b/drivers/misc/lan966x_pci.c
@@ -18,10 +18,6 @@
 #include <linux/pci_ids.h>
 #include <linux/slab.h>
 
-/* Embedded dtbo symbols created by cmd_wrap_S_dtb in scripts/Makefile.lib */
-extern char __dtbo_lan966x_evb_lan9662_nic_begin[];
-extern char __dtbo_lan966x_evb_lan9662_nic_end[];
-
 struct pci_dev_intr_ctrl {
 	struct pci_dev *pci_dev;
 	struct irq_domain *irq_domain;
@@ -118,17 +114,23 @@ static int devm_pci_dev_create_intr_ctrl(struct pci_dev *pdev)
 	return devm_add_action_or_reset(&pdev->dev, devm_pci_dev_remove_intr_ctrl, intr_ctrl);
 }
 
+struct lan966x_pci_info {
+	void *dtbo_begin;
+	void *dtbo_end;
+};
+
 struct lan966x_pci {
 	struct device *dev;
 	int ovcs_id;
+	const struct lan966x_pci_info *info;
 };
 
 static int lan966x_pci_load_overlay(struct lan966x_pci *data)
 {
-	u32 dtbo_size = __dtbo_lan966x_evb_lan9662_nic_end - __dtbo_lan966x_evb_lan9662_nic_begin;
-	void *dtbo_start = __dtbo_lan966x_evb_lan9662_nic_begin;
+	const struct lan966x_pci_info *info = data->info;
 
-	return of_overlay_fdt_apply(dtbo_start, dtbo_size, &data->ovcs_id, dev_of_node(data->dev));
+	return of_overlay_fdt_apply(info->dtbo_begin, info->dtbo_end - info->dtbo_begin,
+				    &data->ovcs_id, dev_of_node(data->dev));
 }
 
 static void lan966x_pci_unload_overlay(struct lan966x_pci *data)
@@ -169,6 +171,9 @@ static int lan966x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
 
 	pci_set_drvdata(pdev, data);
 	data->dev = dev;
+	data->info = (const struct lan966x_pci_info *)id->driver_data;
+	if (!data->info)
+		return -EINVAL;
 
 	ret = lan966x_pci_load_overlay(data);
 	if (ret)
@@ -196,8 +201,17 @@ static void lan966x_pci_remove(struct pci_dev *pdev)
 	lan966x_pci_unload_overlay(data);
 }
 
+/* Embedded dtbo symbols created by cmd_wrap_S_dtb in scripts/Makefile.lib */
+extern char __dtbo_lan966x_evb_lan9662_nic_begin[];
+extern char __dtbo_lan966x_evb_lan9662_nic_end[];
+
+static struct lan966x_pci_info evb_lan9662_nic_info = {
+	.dtbo_begin = __dtbo_lan966x_evb_lan9662_nic_begin,
+	.dtbo_end = __dtbo_lan966x_evb_lan9662_nic_end,
+};
+
 static struct pci_device_id lan966x_pci_ids[] = {
-	{ PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_LAN9662) },
+	{ PCI_DEVICE_DATA(EFAR, LAN9662, &evb_lan9662_nic_info) },
 	{ }
 };
 MODULE_DEVICE_TABLE(pci, lan966x_pci_ids);
-- 
2.53.0


^ permalink raw reply related

* [PATCH v6 23/27] PCI: Add Microchip LAN9662 PCI Device ID
From: Herve Codina @ 2026-03-25 14:35 UTC (permalink / raw)
  To: Andrew Lunn, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Kalle Niemi, Matti Vaittinen,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	Michael Turquette, Stephen Boyd, Andi Shyti, Wolfram Sang,
	Peter Rosin, Arnd Bergmann, Herve Codina, Saravana Kannan,
	Bjorn Helgaas, Charles Keepax, Richard Fitzgerald, David Rhodes,
	Linus Walleij, Ulf Hansson, Mark Brown, Len Brown,
	Andy Shevchenko, Daniel Scally, Heikki Krogerus, Sakari Ailus,
	Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
	Vishal Verma, Ira Weiny, Dan Williams, Shawn Guo
  Cc: Wolfram Sang, linux-kernel, driver-core, imx, linux-arm-kernel,
	linux-clk, linux-i2c, devicetree, linux-pci, linux-sound, patches,
	linux-gpio, linux-pm, linux-spi, linux-acpi, linux-cxl,
	Allan Nielsen, Horatiu Vultur, Steen Hegelund, Luca Ceresoli,
	Thomas Petazzoni
In-Reply-To: <20260325143555.451852-1-herve.codina@bootlin.com>

Existing code uses the 0x9660 value (LAN9662 PCI Device ID) in several
places.

Avoid this direct use of the 0x9660 value replacing it by defined PCI
Device ID.

Signed-off-by: Herve Codina <herve.codina@bootlin.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
---
 drivers/misc/lan966x_pci.c | 2 +-
 drivers/pci/quirks.c       | 2 +-
 include/linux/pci_ids.h    | 1 +
 3 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/misc/lan966x_pci.c b/drivers/misc/lan966x_pci.c
index bbd87c89663d..e6d1fce0b116 100644
--- a/drivers/misc/lan966x_pci.c
+++ b/drivers/misc/lan966x_pci.c
@@ -197,7 +197,7 @@ static void lan966x_pci_remove(struct pci_dev *pdev)
 }
 
 static struct pci_device_id lan966x_pci_ids[] = {
-	{ PCI_DEVICE(PCI_VENDOR_ID_EFAR, 0x9660) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_LAN9662) },
 	{ }
 };
 MODULE_DEVICE_TABLE(pci, lan966x_pci_ids);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index a63c24d3901d..cdb81f90f91f 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -6358,7 +6358,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0xa76e, dpc_log_size);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_XILINX, 0x5020, of_pci_make_dev_node);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_XILINX, 0x5021, of_pci_make_dev_node);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_REDHAT, 0x0005, of_pci_make_dev_node);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_EFAR, 0x9660, of_pci_make_dev_node);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_LAN9662, of_pci_make_dev_node);
 
 /*
  * Devices known to require a longer delay before first config space access
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 406abf629be2..766684176a51 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -934,6 +934,7 @@
 #define PCI_VENDOR_ID_EFAR		0x1055
 #define PCI_DEVICE_ID_EFAR_SLC90E66_1	0x9130
 #define PCI_DEVICE_ID_EFAR_SLC90E66_3	0x9463
+#define PCI_DEVICE_ID_EFAR_LAN9662	0x9660
 
 #define PCI_VENDOR_ID_MOTOROLA		0x1057
 #define PCI_DEVICE_ID_MOTOROLA_MPC105	0x0001
-- 
2.53.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