Linux Power Management development
 help / color / mirror / Atom feed
* [PATCH v4 4/6] cpupower-frequency-info.1: use the proper name of the --perf option
From: Roberto Ricci @ 2026-03-24  1:35 UTC (permalink / raw)
  To: Thomas Renninger, Shuah Khan, John B. Wyatt IV, John Kacur,
	linux-pm, linux-kernel
  Cc: Roberto Ricci
In-Reply-To: <20260324013543.4776-1-io@r-ricci.it>

The cpupower-frequency-info(1) man page describes a '--perf' option.
Even though this form is accepted by the program, its proper name is
'--performance'.

cpufreq-info.c:
	{"performance", no_argument,	 NULL,	 'c'},

Signed-off-by: Roberto Ricci <io@r-ricci.it>
---
 tools/power/cpupower/man/cpupower-frequency-info.1 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/power/cpupower/man/cpupower-frequency-info.1 b/tools/power/cpupower/man/cpupower-frequency-info.1
index e4173417b7c6..7fce2b2e0506 100644
--- a/tools/power/cpupower/man/cpupower-frequency-info.1
+++ b/tools/power/cpupower/man/cpupower-frequency-info.1
@@ -53,7 +53,7 @@ human\-readable output for the \-f, \-w, \-s and \-y parameters.
 \fB\-n\fR \fB\-\-no-rounding\fR
 Output frequencies and latencies without rounding off values.
 .TP
-\fB\-c\fR \fB\-\-perf\fR
+\fB\-c\fR \fB\-\-performance\fR
 Get performances and frequencies capabilities of CPPC, by reading it from hardware (only available on the hardware with CPPC).
 .TP
 .SH "REMARKS"
-- 
2.53.0


^ permalink raw reply related

* [PATCH v4 3/6] cpupower-frequency-info.1: trim trailing whitespace
From: Roberto Ricci @ 2026-03-24  1:35 UTC (permalink / raw)
  To: Thomas Renninger, Shuah Khan, John B. Wyatt IV, John Kacur,
	linux-pm, linux-kernel
  Cc: Roberto Ricci
In-Reply-To: <20260324013543.4776-1-io@r-ricci.it>

Remove useless spaces at the end of some lines in
cpupower-frequency-info.1.

Signed-off-by: Roberto Ricci <io@r-ricci.it>
---
 .../cpupower/man/cpupower-frequency-info.1    | 56 +++++++++----------
 1 file changed, 28 insertions(+), 28 deletions(-)

diff --git a/tools/power/cpupower/man/cpupower-frequency-info.1 b/tools/power/cpupower/man/cpupower-frequency-info.1
index 47fdd7218748..e4173417b7c6 100644
--- a/tools/power/cpupower/man/cpupower-frequency-info.1
+++ b/tools/power/cpupower/man/cpupower-frequency-info.1
@@ -1,80 +1,80 @@
 .TH "CPUPOWER\-FREQUENCY\-INFO" "1" "0.1" "" "cpupower Manual"
 .SH "NAME"
-.LP 
+.LP
 cpupower\-frequency\-info \- Utility to retrieve cpufreq kernel information
 .SH "SYNTAX"
-.LP 
+.LP
 cpupower [ \-c cpulist ] frequency\-info [\fIoptions\fP]
 .SH "DESCRIPTION"
-.LP 
+.LP
 A small tool which prints out cpufreq information helpful to developers and interested users.
 .SH "OPTIONS"
-.LP 
-.TP  
+.LP
+.TP
 \fB\-e\fR \fB\-\-debug\fR
 Prints out debug information.
-.TP  
+.TP
 \fB\-f\fR \fB\-\-freq\fR
 Get frequency the CPU currently runs at, according to the cpufreq core.
-.TP  
+.TP
 \fB\-w\fR \fB\-\-hwfreq\fR
 Get frequency the CPU currently runs at, by reading it from hardware (only available to root).
-.TP  
+.TP
 \fB\-l\fR \fB\-\-hwlimits\fR
 Determine the minimum and maximum CPU frequency allowed.
-.TP  
+.TP
 \fB\-d\fR \fB\-\-driver\fR
 Determines the used cpufreq kernel driver.
-.TP  
+.TP
 \fB\-p\fR \fB\-\-policy\fR
 Gets the currently used cpufreq policy.
-.TP  
+.TP
 \fB\-g\fR \fB\-\-governors\fR
 Determines available cpufreq governors.
-.TP  
+.TP
 \fB\-r\fR \fB\-\-related\-cpus\fR
 Determines which CPUs run at the same hardware frequency.
-.TP  
+.TP
 \fB\-a\fR \fB\-\-affected\-cpus\fR
 Determines which CPUs need to have their frequency coordinated by software.
-.TP  
+.TP
 \fB\-s\fR \fB\-\-stats\fR
 Shows cpufreq statistics if available.
-.TP  
+.TP
 \fB\-y\fR \fB\-\-latency\fR
 Determines the maximum latency on CPU frequency changes.
-.TP  
+.TP
 \fB\-o\fR \fB\-\-proc\fR
 Prints out information like provided by the /proc/cpufreq interface in 2.4. and early 2.6. kernels.
-.TP  
+.TP
 \fB\-m\fR \fB\-\-human\fR
 human\-readable output for the \-f, \-w, \-s and \-y parameters.
-.TP  
+.TP
 \fB\-n\fR \fB\-\-no-rounding\fR
 Output frequencies and latencies without rounding off values.
-.TP  
+.TP
 \fB\-c\fR \fB\-\-perf\fR
 Get performances and frequencies capabilities of CPPC, by reading it from hardware (only available on the hardware with CPPC).
 .TP
 .SH "REMARKS"
-.LP 
+.LP
 By default only values of core zero are displayed. How to display settings of
 other cores is described in the cpupower(1) manpage in the \-\-cpu option section.
-.LP 
+.LP
 You can't specify more than one of the output specific options \-o \-e \-a \-g \-p \-d \-l \-w \-f \-y.
-.LP 
+.LP
 You also can't specify the \-o option combined with the \-c option.
 .SH "FILES"
-.nf 
-\fI/sys/devices/system/cpu/cpu*/cpufreq/\fP  
-\fI/proc/cpufreq\fP (deprecated) 
+.nf
+\fI/sys/devices/system/cpu/cpu*/cpufreq/\fP
+\fI/proc/cpufreq\fP (deprecated)
 \fI/proc/sys/cpu/\fP (deprecated)
-.fi 
+.fi
 .SH "AUTHORS"
 .nf
-Dominik Brodowski <linux@brodo.de> \- author 
+Dominik Brodowski <linux@brodo.de> \- author
 Mattia Dongili<malattia@gmail.com> \- first autolibtoolization
 .fi
 .SH "SEE ALSO"
-.LP 
+.LP
 cpupower\-frequency\-set(1), cpupower(1)
-- 
2.53.0


^ permalink raw reply related

* [PATCH v4 2/6] cpupower-idle-info.1: fix short option names
From: Roberto Ricci @ 2026-03-24  1:35 UTC (permalink / raw)
  To: Thomas Renninger, Shuah Khan, John B. Wyatt IV, John Kacur,
	linux-pm, linux-kernel
  Cc: Roberto Ricci
In-Reply-To: <20260324013543.4776-1-io@r-ricci.it>

The cpupower-idle-info(1) man page describes '-f' as the short form of the
'--silent' option and '-e' as the short form of the '--proc' option.
But they are not correct:

$ cpupower idle-info -f
idle-info: invalid option -- 'f'
invalid or unknown argument
$ cpupower idle-info -e
idle-info: invalid option -- 'e'
invalid or unknown argument

The short form of '--silent' is actually '-s' and the short form of
'--proc' is actually '-o':

cpuidle-info.c:
	{"silent", no_argument, NULL, 's'},
	{"proc", no_argument, NULL, 'o'},

Signed-off-by: Roberto Ricci <io@r-ricci.it>
---
 tools/power/cpupower/man/cpupower-idle-info.1 | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tools/power/cpupower/man/cpupower-idle-info.1 b/tools/power/cpupower/man/cpupower-idle-info.1
index 20b6345c53ad..b2f92aba5f5b 100644
--- a/tools/power/cpupower/man/cpupower-idle-info.1
+++ b/tools/power/cpupower/man/cpupower-idle-info.1
@@ -11,10 +11,10 @@ A tool which prints out per cpu idle information helpful to developers and inter
 .SH "OPTIONS"
 .LP
 .TP
-\fB\-f\fR \fB\-\-silent\fR
+\fB\-s\fR \fB\-\-silent\fR
 Only print a summary of all available C-states in the system.
 .TP
-\fB\-e\fR \fB\-\-proc\fR
+\fB\-o\fR \fB\-\-proc\fR
 deprecated.
 Prints out idle information in old /proc/acpi/processor/*/power format. This
 interface has been removed from the kernel for quite some time, do not let
-- 
2.53.0


^ permalink raw reply related

* [PATCH v4 1/6] cpupower-frequency-set.1: trim trailing whitespace
From: Roberto Ricci @ 2026-03-24  1:35 UTC (permalink / raw)
  To: Thomas Renninger, Shuah Khan, John B. Wyatt IV, John Kacur,
	linux-pm, linux-kernel
  Cc: Roberto Ricci
In-Reply-To: <20260324013543.4776-1-io@r-ricci.it>

Remove useless spaces at the end of some lines in cpupower-frequency-set.1.

Signed-off-by: Roberto Ricci <io@r-ricci.it>
---
 .../cpupower/man/cpupower-frequency-set.1     | 42 +++++++++----------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/tools/power/cpupower/man/cpupower-frequency-set.1 b/tools/power/cpupower/man/cpupower-frequency-set.1
index b50570221a5b..fded317c8c51 100644
--- a/tools/power/cpupower/man/cpupower-frequency-set.1
+++ b/tools/power/cpupower/man/cpupower-frequency-set.1
@@ -1,52 +1,52 @@
 .TH "CPUPOWER\-FREQUENCY\-SET" "1" "0.1" "" "cpupower Manual"
 .SH "NAME"
-.LP 
+.LP
 cpupower\-frequency\-set \- A small tool which allows to modify cpufreq settings.
 .SH "SYNTAX"
-.LP 
+.LP
 cpupower [ \-c cpu ] frequency\-set [\fIoptions\fP]
 .SH "DESCRIPTION"
-.LP 
+.LP
 cpupower frequency\-set allows you to modify cpufreq settings without having to type e.g. "/sys/devices/system/cpu/cpu0/cpufreq/scaling_set_speed" all the time.
 .SH "OPTIONS"
-.LP 
-.TP 
+.LP
+.TP
 \fB\-d\fR \fB\-\-min\fR <FREQ>
 new minimum CPU frequency the governor may select.
-.TP 
+.TP
 \fB\-u\fR \fB\-\-max\fR <FREQ>
 new maximum CPU frequency the governor may select.
-.TP 
+.TP
 \fB\-g\fR \fB\-\-governor\fR <GOV>
 new cpufreq governor.
-.TP 
+.TP
 \fB\-f\fR \fB\-\-freq\fR <FREQ>
 specific frequency to be set. Requires userspace governor to be available and loaded.
-.TP 
+.TP
 \fB\-r\fR \fB\-\-related\fR
 modify all hardware-related CPUs at the same time
-.TP 
+.TP
 .SH "REMARKS"
-.LP 
+.LP
 By default values are applied on all cores. How to modify single core
 configurations is described in the cpupower(1) manpage in the \-\-cpu option section.
-.LP 
+.LP
 The \-f FREQ, \-\-freq FREQ parameter cannot be combined with any other parameter.
-.LP 
+.LP
 FREQuencies can be passed in Hz, kHz (default), MHz, GHz, or THz by postfixing the value with the wanted unit name, without any space (frequency in kHz =^ Hz * 0.001 =^ MHz * 1000 =^ GHz * 1000000).
-.LP 
+.LP
 On Linux kernels up to 2.6.29, the \-r or \-\-related parameter is ignored.
-.SH "FILES" 
+.SH "FILES"
 .nf
-\fI/sys/devices/system/cpu/cpu*/cpufreq/\fP  
-\fI/proc/cpufreq\fP (deprecated) 
+\fI/sys/devices/system/cpu/cpu*/cpufreq/\fP
+\fI/proc/cpufreq\fP (deprecated)
 \fI/proc/sys/cpu/\fP (deprecated)
-.fi 
+.fi
 .SH "AUTHORS"
-.nf 
-Dominik Brodowski <linux@brodo.de> \- author 
+.nf
+Dominik Brodowski <linux@brodo.de> \- author
 Mattia Dongili<malattia@gmail.com> \- first autolibtoolization
 .fi
 .SH "SEE ALSO"
-.LP 
+.LP
 cpupower\-frequency\-info(1), cpupower(1)
-- 
2.53.0


^ permalink raw reply related

* [PATCH v4 0/6] cpupower: fix various man pages issues
From: Roberto Ricci @ 2026-03-24  1:35 UTC (permalink / raw)
  To: Thomas Renninger, Shuah Khan, John B. Wyatt IV, John Kacur,
	linux-pm, linux-kernel
  Cc: Roberto Ricci

This patch series fixes a few issues with the cpupower manual pages,
such as missing or incorrect options and trailing whitespace.

Signed-off-by: Roberto Ricci <io@r-ricci.it>

---
Changes in v4:
- Remove Fixes tags from commit messages
- Fix issues reported by scripts/checkpatch.pl
- Add patches to trim whitespace from some files
- Add cover letter
  Link to v3: https://lore.kernel.org/linux-pm/20260312102029.14980-1-io@r-ricci.it/

Changes in v3:
- Resend because v2 patches have been incorrectly sent as replies to
  individual v1 messages.
  Links to v2:
  - https://lore.kernel.org/linux-pm/abH7pF9p8KxAsHIE@desktop0a/
  - https://lore.kernel.org/linux-pm/abH9KsKbvyi-iceb@desktop0a/
  - https://lore.kernel.org/linux-pm/abH9rMVuy8wmzgkG@desktop0a/
  - https://lore.kernel.org/linux-pm/abH9_Jk-ckkJvsGb@desktop0a/

Changes in v2:
- More descriptive commit messages
  Links to v1:
  - https://lore.kernel.org/linux-pm/abHAZj9xwfDf5JVZ@desktop0a/
  - https://lore.kernel.org/linux-pm/abHAdH-ggaxUugCy@desktop0a/
  - https://lore.kernel.org/linux-pm/abHAhAg-6gaK0Qn7@desktop0a/
  - https://lore.kernel.org/linux-pm/abHAkgiPg8VDpDoV@desktop0a/
---

Roberto Ricci (6):
  cpupower-frequency-set.1: trim trailing whitespace
  cpupower-idle-info.1: fix short option names
  cpupower-frequency-info.1: trim trailing whitespace
  cpupower-frequency-info.1: use the proper name of the --perf option
  cpupower-frequency-info.1: document --boost and --epp options
  cpupower-info.1: describe the --perf-bias option

 .../cpupower/man/cpupower-frequency-info.1    | 64 ++++++++++---------
 .../cpupower/man/cpupower-frequency-set.1     | 42 ++++++------
 tools/power/cpupower/man/cpupower-idle-info.1 |  4 +-
 tools/power/cpupower/man/cpupower-info.1      |  9 ++-
 4 files changed, 66 insertions(+), 53 deletions(-)

-- 
2.53.0


^ permalink raw reply

* [PATCH v2 9/9] ARM: dts: qcom: msm8974: Drop RPM bus clocks
From: Dmitry Baryshkov @ 2026-03-24  0:10 UTC (permalink / raw)
  To: Georgi Djakov, Konrad Dybcio, Bjorn Andersson, Luca Weiss,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Brian Masney
  Cc: linux-arm-msm, linux-pm, devicetree, linux-kernel
In-Reply-To: <20260324-msm8974-icc-v2-0-527280043ad8@oss.qualcomm.com>

Some nodes are abusingly referencing some of the internal bus clocks,
that were recently removed in Linux (because the original implementation
did not make much sense), managing them as if they were the only devices
on an NoC bus.

These clocks are now handled from within the icc framework and are
no longer registered from within the CCF. Remove them.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 arch/arm/boot/dts/qcom/qcom-msm8974.dtsi | 21 +++------------------
 1 file changed, 3 insertions(+), 18 deletions(-)

diff --git a/arch/arm/boot/dts/qcom/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom/qcom-msm8974.dtsi
index 2a82ddce94a2..95be1d2e214f 100644
--- a/arch/arm/boot/dts/qcom/qcom-msm8974.dtsi
+++ b/arch/arm/boot/dts/qcom/qcom-msm8974.dtsi
@@ -1115,9 +1115,6 @@ bimc: interconnect@fc380000 {
 			reg = <0xfc380000 0x6a000>;
 			compatible = "qcom,msm8974-bimc";
 			#interconnect-cells = <1>;
-			clock-names = "bus", "bus_a";
-			clocks = <&rpmcc RPM_SMD_BIMC_CLK>,
-				 <&rpmcc RPM_SMD_BIMC_A_CLK>;
 		};
 
 		gcc: clock-controller@fc400000 {
@@ -1162,45 +1159,32 @@ snoc: interconnect@fc460000 {
 			reg = <0xfc460000 0x4000>;
 			compatible = "qcom,msm8974-snoc";
 			#interconnect-cells = <1>;
-			clock-names = "bus", "bus_a";
-			clocks = <&rpmcc RPM_SMD_SNOC_CLK>,
-				 <&rpmcc RPM_SMD_SNOC_A_CLK>;
 		};
 
 		pnoc: interconnect@fc468000 {
 			reg = <0xfc468000 0x4000>;
 			compatible = "qcom,msm8974-pnoc";
 			#interconnect-cells = <1>;
-			clock-names = "bus", "bus_a";
-			clocks = <&rpmcc RPM_SMD_PNOC_CLK>,
-				 <&rpmcc RPM_SMD_PNOC_A_CLK>;
 		};
 
 		ocmemnoc: interconnect@fc470000 {
 			reg = <0xfc470000 0x4000>;
 			compatible = "qcom,msm8974-ocmemnoc";
 			#interconnect-cells = <1>;
-			clock-names = "bus", "bus_a";
-			clocks = <&rpmcc RPM_SMD_OCMEMGX_CLK>,
-				 <&rpmcc RPM_SMD_OCMEMGX_A_CLK>;
 		};
 
 		mmssnoc: interconnect@fc478000 {
 			reg = <0xfc478000 0x4000>;
 			compatible = "qcom,msm8974-mmssnoc";
 			#interconnect-cells = <1>;
-			clock-names = "bus", "bus_a";
-			clocks = <&mmcc MMSS_S0_AXI_CLK>,
-				 <&mmcc MMSS_S0_AXI_CLK>;
+			clocks = <&mmcc MMSS_S0_AXI_CLK>;
+			clock-names = "bus";
 		};
 
 		cnoc: interconnect@fc480000 {
 			reg = <0xfc480000 0x4000>;
 			compatible = "qcom,msm8974-cnoc";
 			#interconnect-cells = <1>;
-			clock-names = "bus", "bus_a";
-			clocks = <&rpmcc RPM_SMD_CNOC_CLK>,
-				 <&rpmcc RPM_SMD_CNOC_A_CLK>;
 		};
 
 		tsens: thermal-sensor@fc4a9000 {
@@ -2223,6 +2207,7 @@ sram@fdd00000 {
 			      <0xfec00000 0x180000>;
 			reg-names = "ctrl", "mem";
 			ranges = <0 0xfec00000 0x180000>;
+			/* core clock doesn't exist anymore, kept for ABI compliance */
 			clocks = <&rpmcc RPM_SMD_OCMEMGX_CLK>,
 				 <&mmcc OCMEMCX_OCMEMNOC_CLK>;
 			clock-names = "core", "iface";

-- 
2.47.3


^ permalink raw reply related

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

The rest of Qualcomm Interconnect drivers have stopped using
DEFINE_QNODE long ago for the sake of readability. Stop using it inside
the msm8974 interconnect driver too.

Acked-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 drivers/interconnect/qcom/msm8974.c | 1335 +++++++++++++++++++++++++++++++----
 1 file changed, 1191 insertions(+), 144 deletions(-)

diff --git a/drivers/interconnect/qcom/msm8974.c b/drivers/interconnect/qcom/msm8974.c
index 144f225ec885..c020c61126ca 100644
--- a/drivers/interconnect/qcom/msm8974.c
+++ b/drivers/interconnect/qcom/msm8974.c
@@ -181,28 +181,75 @@ static int msm8974_get_bw(struct icc_node *node, u32 *avg, u32 *peak)
 	return 0;
 };
 
-#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id,	\
-		     ...)						\
-		static const u16 _name ## _links[] = {			\
-			__VA_ARGS__					\
-		};							\
-		static struct qcom_icc_node _name = {			\
-		.name = #_name,						\
-		.id = _id,						\
-		.buswidth = _buswidth,					\
-		.mas_rpm_id = _mas_rpm_id,				\
-		.slv_rpm_id = _slv_rpm_id,				\
-		.num_links = ARRAY_SIZE(_name ## _links),		\
-		.links = _name ## _links,				\
-	}
-
-DEFINE_QNODE(mas_ampss_m0, MSM8974_BIMC_MAS_AMPSS_M0, 8, 0, -1);
-DEFINE_QNODE(mas_ampss_m1, MSM8974_BIMC_MAS_AMPSS_M1, 8, 0, -1);
-DEFINE_QNODE(mas_mss_proc, MSM8974_BIMC_MAS_MSS_PROC, 8, 1, -1);
-DEFINE_QNODE(bimc_to_mnoc, MSM8974_BIMC_TO_MNOC, 8, 2, -1, MSM8974_BIMC_SLV_EBI_CH0);
-DEFINE_QNODE(bimc_to_snoc, MSM8974_BIMC_TO_SNOC, 8, 3, 2, MSM8974_SNOC_TO_BIMC, MSM8974_BIMC_SLV_EBI_CH0, MSM8974_BIMC_MAS_AMPSS_M0);
-DEFINE_QNODE(slv_ebi_ch0, MSM8974_BIMC_SLV_EBI_CH0, 8, -1, 0);
-DEFINE_QNODE(slv_ampss_l2, MSM8974_BIMC_SLV_AMPSS_L2, 8, -1, 1);
+static struct qcom_icc_node mas_ampss_m0 = {
+	.name = "mas_ampss_m0",
+	.id = MSM8974_BIMC_MAS_AMPSS_M0,
+	.buswidth = 8,
+	.mas_rpm_id = 0,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_ampss_m1 = {
+	.name = "mas_ampss_m1",
+	.id = MSM8974_BIMC_MAS_AMPSS_M1,
+	.buswidth = 8,
+	.mas_rpm_id = 0,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_mss_proc = {
+	.name = "mas_mss_proc",
+	.id = MSM8974_BIMC_MAS_MSS_PROC,
+	.buswidth = 8,
+	.mas_rpm_id = 1,
+	.slv_rpm_id = -1,
+};
+
+static const u16 bimc_to_mnoc_links[] = {
+	MSM8974_BIMC_SLV_EBI_CH0
+};
+
+static struct qcom_icc_node bimc_to_mnoc = {
+	.name = "bimc_to_mnoc",
+	.id = MSM8974_BIMC_TO_MNOC,
+	.buswidth = 8,
+	.mas_rpm_id = 2,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(bimc_to_mnoc_links),
+	.links = bimc_to_mnoc_links,
+};
+
+static const u16 bimc_to_snoc_links[] = {
+	MSM8974_SNOC_TO_BIMC,
+	MSM8974_BIMC_SLV_EBI_CH0,
+	MSM8974_BIMC_MAS_AMPSS_M0
+};
+
+static struct qcom_icc_node bimc_to_snoc = {
+	.name = "bimc_to_snoc",
+	.id = MSM8974_BIMC_TO_SNOC,
+	.buswidth = 8,
+	.mas_rpm_id = 3,
+	.slv_rpm_id = 2,
+	.num_links = ARRAY_SIZE(bimc_to_snoc_links),
+	.links = bimc_to_snoc_links,
+};
+
+static struct qcom_icc_node slv_ebi_ch0 = {
+	.name = "slv_ebi_ch0",
+	.id = MSM8974_BIMC_SLV_EBI_CH0,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 0,
+};
+
+static struct qcom_icc_node slv_ampss_l2 = {
+	.name = "slv_ampss_l2",
+	.id = MSM8974_BIMC_SLV_AMPSS_L2,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 1,
+};
 
 static struct qcom_icc_node * const msm8974_bimc_nodes[] = {
 	[BIMC_MAS_AMPSS_M0] = &mas_ampss_m0,
@@ -222,43 +269,301 @@ static const struct qcom_icc_desc msm8974_bimc = {
 	.ignore_enxio = true,
 };
 
-DEFINE_QNODE(mas_rpm_inst, MSM8974_CNOC_MAS_RPM_INST, 8, 45, -1);
-DEFINE_QNODE(mas_rpm_data, MSM8974_CNOC_MAS_RPM_DATA, 8, 46, -1);
-DEFINE_QNODE(mas_rpm_sys, MSM8974_CNOC_MAS_RPM_SYS, 8, 47, -1);
-DEFINE_QNODE(mas_dehr, MSM8974_CNOC_MAS_DEHR, 8, 48, -1);
-DEFINE_QNODE(mas_qdss_dap, MSM8974_CNOC_MAS_QDSS_DAP, 8, 49, -1);
-DEFINE_QNODE(mas_spdm, MSM8974_CNOC_MAS_SPDM, 8, 50, -1);
-DEFINE_QNODE(mas_tic, MSM8974_CNOC_MAS_TIC, 8, 51, -1);
-DEFINE_QNODE(slv_clk_ctl, MSM8974_CNOC_SLV_CLK_CTL, 8, -1, 47);
-DEFINE_QNODE(slv_cnoc_mss, MSM8974_CNOC_SLV_CNOC_MSS, 8, -1, 48);
-DEFINE_QNODE(slv_security, MSM8974_CNOC_SLV_SECURITY, 8, -1, 49);
-DEFINE_QNODE(slv_tcsr, MSM8974_CNOC_SLV_TCSR, 8, -1, 50);
-DEFINE_QNODE(slv_tlmm, MSM8974_CNOC_SLV_TLMM, 8, -1, 51);
-DEFINE_QNODE(slv_crypto_0_cfg, MSM8974_CNOC_SLV_CRYPTO_0_CFG, 8, -1, 52);
-DEFINE_QNODE(slv_crypto_1_cfg, MSM8974_CNOC_SLV_CRYPTO_1_CFG, 8, -1, 53);
-DEFINE_QNODE(slv_imem_cfg, MSM8974_CNOC_SLV_IMEM_CFG, 8, -1, 54);
-DEFINE_QNODE(slv_message_ram, MSM8974_CNOC_SLV_MESSAGE_RAM, 8, -1, 55);
-DEFINE_QNODE(slv_bimc_cfg, MSM8974_CNOC_SLV_BIMC_CFG, 8, -1, 56);
-DEFINE_QNODE(slv_boot_rom, MSM8974_CNOC_SLV_BOOT_ROM, 8, -1, 57);
-DEFINE_QNODE(slv_pmic_arb, MSM8974_CNOC_SLV_PMIC_ARB, 8, -1, 59);
-DEFINE_QNODE(slv_spdm_wrapper, MSM8974_CNOC_SLV_SPDM_WRAPPER, 8, -1, 60);
-DEFINE_QNODE(slv_dehr_cfg, MSM8974_CNOC_SLV_DEHR_CFG, 8, -1, 61);
-DEFINE_QNODE(slv_mpm, MSM8974_CNOC_SLV_MPM, 8, -1, 62);
-DEFINE_QNODE(slv_qdss_cfg, MSM8974_CNOC_SLV_QDSS_CFG, 8, -1, 63);
-DEFINE_QNODE(slv_rbcpr_cfg, MSM8974_CNOC_SLV_RBCPR_CFG, 8, -1, 64);
-DEFINE_QNODE(slv_rbcpr_qdss_apu_cfg, MSM8974_CNOC_SLV_RBCPR_QDSS_APU_CFG, 8, -1, 65);
-DEFINE_QNODE(cnoc_to_snoc, MSM8974_CNOC_TO_SNOC, 8, 52, 75);
-DEFINE_QNODE(slv_cnoc_onoc_cfg, MSM8974_CNOC_SLV_CNOC_ONOC_CFG, 8, -1, 68);
-DEFINE_QNODE(slv_cnoc_mnoc_mmss_cfg, MSM8974_CNOC_SLV_CNOC_MNOC_MMSS_CFG, 8, -1, 58);
-DEFINE_QNODE(slv_cnoc_mnoc_cfg, MSM8974_CNOC_SLV_CNOC_MNOC_CFG, 8, -1, 66);
-DEFINE_QNODE(slv_pnoc_cfg, MSM8974_CNOC_SLV_PNOC_CFG, 8, -1, 69);
-DEFINE_QNODE(slv_snoc_mpu_cfg, MSM8974_CNOC_SLV_SNOC_MPU_CFG, 8, -1, 67);
-DEFINE_QNODE(slv_snoc_cfg, MSM8974_CNOC_SLV_SNOC_CFG, 8, -1, 70);
-DEFINE_QNODE(slv_ebi1_dll_cfg, MSM8974_CNOC_SLV_EBI1_DLL_CFG, 8, -1, 71);
-DEFINE_QNODE(slv_phy_apu_cfg, MSM8974_CNOC_SLV_PHY_APU_CFG, 8, -1, 72);
-DEFINE_QNODE(slv_ebi1_phy_cfg, MSM8974_CNOC_SLV_EBI1_PHY_CFG, 8, -1, 73);
-DEFINE_QNODE(slv_rpm, MSM8974_CNOC_SLV_RPM, 8, -1, 74);
-DEFINE_QNODE(slv_service_cnoc, MSM8974_CNOC_SLV_SERVICE_CNOC, 8, -1, 76);
+static struct qcom_icc_node mas_rpm_inst = {
+	.name = "mas_rpm_inst",
+	.id = MSM8974_CNOC_MAS_RPM_INST,
+	.buswidth = 8,
+	.mas_rpm_id = 45,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_rpm_data = {
+	.name = "mas_rpm_data",
+	.id = MSM8974_CNOC_MAS_RPM_DATA,
+	.buswidth = 8,
+	.mas_rpm_id = 46,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_rpm_sys = {
+	.name = "mas_rpm_sys",
+	.id = MSM8974_CNOC_MAS_RPM_SYS,
+	.buswidth = 8,
+	.mas_rpm_id = 47,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_dehr = {
+	.name = "mas_dehr",
+	.id = MSM8974_CNOC_MAS_DEHR,
+	.buswidth = 8,
+	.mas_rpm_id = 48,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_qdss_dap = {
+	.name = "mas_qdss_dap",
+	.id = MSM8974_CNOC_MAS_QDSS_DAP,
+	.buswidth = 8,
+	.mas_rpm_id = 49,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_spdm = {
+	.name = "mas_spdm",
+	.id = MSM8974_CNOC_MAS_SPDM,
+	.buswidth = 8,
+	.mas_rpm_id = 50,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_tic = {
+	.name = "mas_tic",
+	.id = MSM8974_CNOC_MAS_TIC,
+	.buswidth = 8,
+	.mas_rpm_id = 51,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node slv_clk_ctl = {
+	.name = "slv_clk_ctl",
+	.id = MSM8974_CNOC_SLV_CLK_CTL,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 47,
+};
+
+static struct qcom_icc_node slv_cnoc_mss = {
+	.name = "slv_cnoc_mss",
+	.id = MSM8974_CNOC_SLV_CNOC_MSS,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 48,
+};
+
+static struct qcom_icc_node slv_security = {
+	.name = "slv_security",
+	.id = MSM8974_CNOC_SLV_SECURITY,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 49,
+};
+
+static struct qcom_icc_node slv_tcsr = {
+	.name = "slv_tcsr",
+	.id = MSM8974_CNOC_SLV_TCSR,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 50,
+};
+
+static struct qcom_icc_node slv_tlmm = {
+	.name = "slv_tlmm",
+	.id = MSM8974_CNOC_SLV_TLMM,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 51,
+};
+
+static struct qcom_icc_node slv_crypto_0_cfg = {
+	.name = "slv_crypto_0_cfg",
+	.id = MSM8974_CNOC_SLV_CRYPTO_0_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 52,
+};
+
+static struct qcom_icc_node slv_crypto_1_cfg = {
+	.name = "slv_crypto_1_cfg",
+	.id = MSM8974_CNOC_SLV_CRYPTO_1_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 53,
+};
+
+static struct qcom_icc_node slv_imem_cfg = {
+	.name = "slv_imem_cfg",
+	.id = MSM8974_CNOC_SLV_IMEM_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 54,
+};
+
+static struct qcom_icc_node slv_message_ram = {
+	.name = "slv_message_ram",
+	.id = MSM8974_CNOC_SLV_MESSAGE_RAM,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 55,
+};
+
+static struct qcom_icc_node slv_bimc_cfg = {
+	.name = "slv_bimc_cfg",
+	.id = MSM8974_CNOC_SLV_BIMC_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 56,
+};
+
+static struct qcom_icc_node slv_boot_rom = {
+	.name = "slv_boot_rom",
+	.id = MSM8974_CNOC_SLV_BOOT_ROM,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 57,
+};
+
+static struct qcom_icc_node slv_pmic_arb = {
+	.name = "slv_pmic_arb",
+	.id = MSM8974_CNOC_SLV_PMIC_ARB,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 59,
+};
+
+static struct qcom_icc_node slv_spdm_wrapper = {
+	.name = "slv_spdm_wrapper",
+	.id = MSM8974_CNOC_SLV_SPDM_WRAPPER,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 60,
+};
+
+static struct qcom_icc_node slv_dehr_cfg = {
+	.name = "slv_dehr_cfg",
+	.id = MSM8974_CNOC_SLV_DEHR_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 61,
+};
+
+static struct qcom_icc_node slv_mpm = {
+	.name = "slv_mpm",
+	.id = MSM8974_CNOC_SLV_MPM,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 62,
+};
+
+static struct qcom_icc_node slv_qdss_cfg = {
+	.name = "slv_qdss_cfg",
+	.id = MSM8974_CNOC_SLV_QDSS_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 63,
+};
+
+static struct qcom_icc_node slv_rbcpr_cfg = {
+	.name = "slv_rbcpr_cfg",
+	.id = MSM8974_CNOC_SLV_RBCPR_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 64,
+};
+
+static struct qcom_icc_node slv_rbcpr_qdss_apu_cfg = {
+	.name = "slv_rbcpr_qdss_apu_cfg",
+	.id = MSM8974_CNOC_SLV_RBCPR_QDSS_APU_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 65,
+};
+
+static struct qcom_icc_node cnoc_to_snoc = {
+	.name = "cnoc_to_snoc",
+	.id = MSM8974_CNOC_TO_SNOC,
+	.buswidth = 8,
+	.mas_rpm_id = 52,
+	.slv_rpm_id = 75,
+};
+
+static struct qcom_icc_node slv_cnoc_onoc_cfg = {
+	.name = "slv_cnoc_onoc_cfg",
+	.id = MSM8974_CNOC_SLV_CNOC_ONOC_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 68,
+};
+
+static struct qcom_icc_node slv_cnoc_mnoc_mmss_cfg = {
+	.name = "slv_cnoc_mnoc_mmss_cfg",
+	.id = MSM8974_CNOC_SLV_CNOC_MNOC_MMSS_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 58,
+};
+
+static struct qcom_icc_node slv_cnoc_mnoc_cfg = {
+	.name = "slv_cnoc_mnoc_cfg",
+	.id = MSM8974_CNOC_SLV_CNOC_MNOC_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 66,
+};
+
+static struct qcom_icc_node slv_pnoc_cfg = {
+	.name = "slv_pnoc_cfg",
+	.id = MSM8974_CNOC_SLV_PNOC_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 69,
+};
+
+static struct qcom_icc_node slv_snoc_mpu_cfg = {
+	.name = "slv_snoc_mpu_cfg",
+	.id = MSM8974_CNOC_SLV_SNOC_MPU_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 67,
+};
+
+static struct qcom_icc_node slv_snoc_cfg = {
+	.name = "slv_snoc_cfg",
+	.id = MSM8974_CNOC_SLV_SNOC_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 70,
+};
+
+static struct qcom_icc_node slv_ebi1_dll_cfg = {
+	.name = "slv_ebi1_dll_cfg",
+	.id = MSM8974_CNOC_SLV_EBI1_DLL_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 71,
+};
+
+static struct qcom_icc_node slv_phy_apu_cfg = {
+	.name = "slv_phy_apu_cfg",
+	.id = MSM8974_CNOC_SLV_PHY_APU_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 72,
+};
+
+static struct qcom_icc_node slv_ebi1_phy_cfg = {
+	.name = "slv_ebi1_phy_cfg",
+	.id = MSM8974_CNOC_SLV_EBI1_PHY_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 73,
+};
+
+static struct qcom_icc_node slv_rpm = {
+	.name = "slv_rpm",
+	.id = MSM8974_CNOC_SLV_RPM,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 74,
+};
+
+static struct qcom_icc_node slv_service_cnoc = {
+	.name = "slv_service_cnoc",
+	.id = MSM8974_CNOC_SLV_SERVICE_CNOC,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 76,
+};
 
 static struct qcom_icc_node * const msm8974_cnoc_nodes[] = {
 	[CNOC_MAS_RPM_INST] = &mas_rpm_inst,
@@ -308,28 +613,211 @@ static const struct qcom_icc_desc msm8974_cnoc = {
 	.ignore_enxio = true,
 };
 
-DEFINE_QNODE(mas_graphics_3d, MSM8974_MNOC_MAS_GRAPHICS_3D, 16, 6, -1, MSM8974_MNOC_TO_BIMC);
-DEFINE_QNODE(mas_jpeg, MSM8974_MNOC_MAS_JPEG, 16, 7, -1, MSM8974_MNOC_TO_BIMC);
-DEFINE_QNODE(mas_mdp_port0, MSM8974_MNOC_MAS_MDP_PORT0, 16, 8, -1, MSM8974_MNOC_TO_BIMC);
-DEFINE_QNODE(mas_video_p0, MSM8974_MNOC_MAS_VIDEO_P0, 16, 9, -1);
-DEFINE_QNODE(mas_video_p1, MSM8974_MNOC_MAS_VIDEO_P1, 16, 10, -1);
-DEFINE_QNODE(mas_vfe, MSM8974_MNOC_MAS_VFE, 16, 11, -1, MSM8974_MNOC_TO_BIMC);
-DEFINE_QNODE(mnoc_to_cnoc, MSM8974_MNOC_TO_CNOC, 16, 4, -1);
-DEFINE_QNODE(mnoc_to_bimc, MSM8974_MNOC_TO_BIMC, 16, -1, 16, MSM8974_BIMC_TO_MNOC);
-DEFINE_QNODE(slv_camera_cfg, MSM8974_MNOC_SLV_CAMERA_CFG, 16, -1, 3);
-DEFINE_QNODE(slv_display_cfg, MSM8974_MNOC_SLV_DISPLAY_CFG, 16, -1, 4);
-DEFINE_QNODE(slv_ocmem_cfg, MSM8974_MNOC_SLV_OCMEM_CFG, 16, -1, 5);
-DEFINE_QNODE(slv_cpr_cfg, MSM8974_MNOC_SLV_CPR_CFG, 16, -1, 6);
-DEFINE_QNODE(slv_cpr_xpu_cfg, MSM8974_MNOC_SLV_CPR_XPU_CFG, 16, -1, 7);
-DEFINE_QNODE(slv_misc_cfg, MSM8974_MNOC_SLV_MISC_CFG, 16, -1, 8);
-DEFINE_QNODE(slv_misc_xpu_cfg, MSM8974_MNOC_SLV_MISC_XPU_CFG, 16, -1, 9);
-DEFINE_QNODE(slv_venus_cfg, MSM8974_MNOC_SLV_VENUS_CFG, 16, -1, 10);
-DEFINE_QNODE(slv_graphics_3d_cfg, MSM8974_MNOC_SLV_GRAPHICS_3D_CFG, 16, -1, 11);
-DEFINE_QNODE(slv_mmss_clk_cfg, MSM8974_MNOC_SLV_MMSS_CLK_CFG, 16, -1, 12);
-DEFINE_QNODE(slv_mmss_clk_xpu_cfg, MSM8974_MNOC_SLV_MMSS_CLK_XPU_CFG, 16, -1, 13);
-DEFINE_QNODE(slv_mnoc_mpu_cfg, MSM8974_MNOC_SLV_MNOC_MPU_CFG, 16, -1, 14);
-DEFINE_QNODE(slv_onoc_mpu_cfg, MSM8974_MNOC_SLV_ONOC_MPU_CFG, 16, -1, 15);
-DEFINE_QNODE(slv_service_mnoc, MSM8974_MNOC_SLV_SERVICE_MNOC, 16, -1, 17);
+static const u16 mas_graphics_3d_links[] = {
+	MSM8974_MNOC_TO_BIMC
+};
+
+static struct qcom_icc_node mas_graphics_3d = {
+	.name = "mas_graphics_3d",
+	.id = MSM8974_MNOC_MAS_GRAPHICS_3D,
+	.buswidth = 16,
+	.mas_rpm_id = 6,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_graphics_3d_links),
+	.links = mas_graphics_3d_links,
+};
+
+static const u16 mas_jpeg_links[] = {
+	MSM8974_MNOC_TO_BIMC
+};
+
+static struct qcom_icc_node mas_jpeg = {
+	.name = "mas_jpeg",
+	.id = MSM8974_MNOC_MAS_JPEG,
+	.buswidth = 16,
+	.mas_rpm_id = 7,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_jpeg_links),
+	.links = mas_jpeg_links,
+};
+
+static const u16 mas_mdp_port0_links[] = {
+	MSM8974_MNOC_TO_BIMC
+};
+
+static struct qcom_icc_node mas_mdp_port0 = {
+	.name = "mas_mdp_port0",
+	.id = MSM8974_MNOC_MAS_MDP_PORT0,
+	.buswidth = 16,
+	.mas_rpm_id = 8,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_mdp_port0_links),
+	.links = mas_mdp_port0_links,
+};
+
+static struct qcom_icc_node mas_video_p0 = {
+	.name = "mas_video_p0",
+	.id = MSM8974_MNOC_MAS_VIDEO_P0,
+	.buswidth = 16,
+	.mas_rpm_id = 9,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_video_p1 = {
+	.name = "mas_video_p1",
+	.id = MSM8974_MNOC_MAS_VIDEO_P1,
+	.buswidth = 16,
+	.mas_rpm_id = 10,
+	.slv_rpm_id = -1,
+};
+
+static const u16 mas_vfe_links[] = {
+	MSM8974_MNOC_TO_BIMC
+};
+
+static struct qcom_icc_node mas_vfe = {
+	.name = "mas_vfe",
+	.id = MSM8974_MNOC_MAS_VFE,
+	.buswidth = 16,
+	.mas_rpm_id = 11,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_vfe_links),
+	.links = mas_vfe_links,
+};
+
+static struct qcom_icc_node mnoc_to_cnoc = {
+	.name = "mnoc_to_cnoc",
+	.id = MSM8974_MNOC_TO_CNOC,
+	.buswidth = 16,
+	.mas_rpm_id = 4,
+	.slv_rpm_id = -1,
+};
+
+static const u16 mnoc_to_bimc_links[] = {
+	MSM8974_BIMC_TO_MNOC
+};
+
+static struct qcom_icc_node mnoc_to_bimc = {
+	.name = "mnoc_to_bimc",
+	.id = MSM8974_MNOC_TO_BIMC,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 16,
+	.num_links = ARRAY_SIZE(mnoc_to_bimc_links),
+	.links = mnoc_to_bimc_links,
+};
+
+static struct qcom_icc_node slv_camera_cfg = {
+	.name = "slv_camera_cfg",
+	.id = MSM8974_MNOC_SLV_CAMERA_CFG,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 3,
+};
+
+static struct qcom_icc_node slv_display_cfg = {
+	.name = "slv_display_cfg",
+	.id = MSM8974_MNOC_SLV_DISPLAY_CFG,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 4,
+};
+
+static struct qcom_icc_node slv_ocmem_cfg = {
+	.name = "slv_ocmem_cfg",
+	.id = MSM8974_MNOC_SLV_OCMEM_CFG,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 5,
+};
+
+static struct qcom_icc_node slv_cpr_cfg = {
+	.name = "slv_cpr_cfg",
+	.id = MSM8974_MNOC_SLV_CPR_CFG,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 6,
+};
+
+static struct qcom_icc_node slv_cpr_xpu_cfg = {
+	.name = "slv_cpr_xpu_cfg",
+	.id = MSM8974_MNOC_SLV_CPR_XPU_CFG,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 7,
+};
+
+static struct qcom_icc_node slv_misc_cfg = {
+	.name = "slv_misc_cfg",
+	.id = MSM8974_MNOC_SLV_MISC_CFG,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 8,
+};
+
+static struct qcom_icc_node slv_misc_xpu_cfg = {
+	.name = "slv_misc_xpu_cfg",
+	.id = MSM8974_MNOC_SLV_MISC_XPU_CFG,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 9,
+};
+
+static struct qcom_icc_node slv_venus_cfg = {
+	.name = "slv_venus_cfg",
+	.id = MSM8974_MNOC_SLV_VENUS_CFG,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 10,
+};
+
+static struct qcom_icc_node slv_graphics_3d_cfg = {
+	.name = "slv_graphics_3d_cfg",
+	.id = MSM8974_MNOC_SLV_GRAPHICS_3D_CFG,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 11,
+};
+
+static struct qcom_icc_node slv_mmss_clk_cfg = {
+	.name = "slv_mmss_clk_cfg",
+	.id = MSM8974_MNOC_SLV_MMSS_CLK_CFG,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 12,
+};
+
+static struct qcom_icc_node slv_mmss_clk_xpu_cfg = {
+	.name = "slv_mmss_clk_xpu_cfg",
+	.id = MSM8974_MNOC_SLV_MMSS_CLK_XPU_CFG,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 13,
+};
+
+static struct qcom_icc_node slv_mnoc_mpu_cfg = {
+	.name = "slv_mnoc_mpu_cfg",
+	.id = MSM8974_MNOC_SLV_MNOC_MPU_CFG,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 14,
+};
+
+static struct qcom_icc_node slv_onoc_mpu_cfg = {
+	.name = "slv_onoc_mpu_cfg",
+	.id = MSM8974_MNOC_SLV_ONOC_MPU_CFG,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 15,
+};
+
+static struct qcom_icc_node slv_service_mnoc = {
+	.name = "slv_service_mnoc",
+	.id = MSM8974_MNOC_SLV_SERVICE_MNOC,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 17,
+};
 
 static struct qcom_icc_node * const msm8974_mnoc_nodes[] = {
 	[MNOC_MAS_GRAPHICS_3D] = &mas_graphics_3d,
@@ -363,20 +851,121 @@ static const struct qcom_icc_desc msm8974_mnoc = {
 	.ignore_enxio = true,
 };
 
-DEFINE_QNODE(ocmem_noc_to_ocmem_vnoc, MSM8974_OCMEM_NOC_TO_OCMEM_VNOC, 16, 54, 78, MSM8974_OCMEM_SLV_OCMEM);
-DEFINE_QNODE(mas_jpeg_ocmem, MSM8974_OCMEM_MAS_JPEG_OCMEM, 16, 13, -1);
-DEFINE_QNODE(mas_mdp_ocmem, MSM8974_OCMEM_MAS_MDP_OCMEM, 16, 14, -1);
-DEFINE_QNODE(mas_video_p0_ocmem, MSM8974_OCMEM_MAS_VIDEO_P0_OCMEM, 16, 15, -1);
-DEFINE_QNODE(mas_video_p1_ocmem, MSM8974_OCMEM_MAS_VIDEO_P1_OCMEM, 16, 16, -1);
-DEFINE_QNODE(mas_vfe_ocmem, MSM8974_OCMEM_MAS_VFE_OCMEM, 16, 17, -1);
-DEFINE_QNODE(mas_cnoc_onoc_cfg, MSM8974_OCMEM_MAS_CNOC_ONOC_CFG, 16, 12, -1);
-DEFINE_QNODE(slv_service_onoc, MSM8974_OCMEM_SLV_SERVICE_ONOC, 16, -1, 19);
-DEFINE_QNODE(slv_ocmem, MSM8974_OCMEM_SLV_OCMEM, 16, -1, 18);
+static const u16 ocmem_noc_to_ocmem_vnoc_links[] = {
+	MSM8974_OCMEM_SLV_OCMEM
+};
+
+static struct qcom_icc_node ocmem_noc_to_ocmem_vnoc = {
+	.name = "ocmem_noc_to_ocmem_vnoc",
+	.id = MSM8974_OCMEM_NOC_TO_OCMEM_VNOC,
+	.buswidth = 16,
+	.mas_rpm_id = 54,
+	.slv_rpm_id = 78,
+	.num_links = ARRAY_SIZE(ocmem_noc_to_ocmem_vnoc_links),
+	.links = ocmem_noc_to_ocmem_vnoc_links,
+};
+
+static struct qcom_icc_node mas_jpeg_ocmem = {
+	.name = "mas_jpeg_ocmem",
+	.id = MSM8974_OCMEM_MAS_JPEG_OCMEM,
+	.buswidth = 16,
+	.mas_rpm_id = 13,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_mdp_ocmem = {
+	.name = "mas_mdp_ocmem",
+	.id = MSM8974_OCMEM_MAS_MDP_OCMEM,
+	.buswidth = 16,
+	.mas_rpm_id = 14,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_video_p0_ocmem = {
+	.name = "mas_video_p0_ocmem",
+	.id = MSM8974_OCMEM_MAS_VIDEO_P0_OCMEM,
+	.buswidth = 16,
+	.mas_rpm_id = 15,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_video_p1_ocmem = {
+	.name = "mas_video_p1_ocmem",
+	.id = MSM8974_OCMEM_MAS_VIDEO_P1_OCMEM,
+	.buswidth = 16,
+	.mas_rpm_id = 16,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_vfe_ocmem = {
+	.name = "mas_vfe_ocmem",
+	.id = MSM8974_OCMEM_MAS_VFE_OCMEM,
+	.buswidth = 16,
+	.mas_rpm_id = 17,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_cnoc_onoc_cfg = {
+	.name = "mas_cnoc_onoc_cfg",
+	.id = MSM8974_OCMEM_MAS_CNOC_ONOC_CFG,
+	.buswidth = 16,
+	.mas_rpm_id = 12,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node slv_service_onoc = {
+	.name = "slv_service_onoc",
+	.id = MSM8974_OCMEM_SLV_SERVICE_ONOC,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 19,
+};
+
+static struct qcom_icc_node slv_ocmem = {
+	.name = "slv_ocmem",
+	.id = MSM8974_OCMEM_SLV_OCMEM,
+	.buswidth = 16,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 18,
+};
 
 /* Virtual NoC is needed for connection to OCMEM */
-DEFINE_QNODE(ocmem_vnoc_to_onoc, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC, 16, 56, 79, MSM8974_OCMEM_NOC_TO_OCMEM_VNOC);
-DEFINE_QNODE(ocmem_vnoc_to_snoc, MSM8974_OCMEM_VNOC_TO_SNOC, 8, 57, 80);
-DEFINE_QNODE(mas_v_ocmem_gfx3d, MSM8974_OCMEM_VNOC_MAS_GFX3D, 8, 55, -1, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC);
+static const u16 ocmem_vnoc_to_onoc_links[] = {
+	MSM8974_OCMEM_NOC_TO_OCMEM_VNOC
+};
+
+static struct qcom_icc_node ocmem_vnoc_to_onoc = {
+	.name = "ocmem_vnoc_to_onoc",
+	.id = MSM8974_OCMEM_VNOC_TO_OCMEM_NOC,
+	.buswidth = 16,
+	.mas_rpm_id = 56,
+	.slv_rpm_id = 79,
+	.num_links = ARRAY_SIZE(ocmem_vnoc_to_onoc_links),
+	.links = ocmem_vnoc_to_onoc_links,
+};
+
+static struct qcom_icc_node ocmem_vnoc_to_snoc = {
+	.name = "ocmem_vnoc_to_snoc",
+	.id = MSM8974_OCMEM_VNOC_TO_SNOC,
+	.buswidth = 8,
+	.mas_rpm_id = 57,
+	.slv_rpm_id = 80,
+};
+
+static const u16 mas_v_ocmem_gfx3d_links[] = {
+	MSM8974_OCMEM_VNOC_TO_OCMEM_NOC
+};
+
+static struct qcom_icc_node mas_v_ocmem_gfx3d = {
+	.name = "mas_v_ocmem_gfx3d",
+	.id = MSM8974_OCMEM_VNOC_MAS_GFX3D,
+	.buswidth = 8,
+	.mas_rpm_id = 55,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_v_ocmem_gfx3d_links),
+	.links = mas_v_ocmem_gfx3d_links,
+};
+
 
 static struct qcom_icc_node * const msm8974_onoc_nodes[] = {
 	[OCMEM_NOC_TO_OCMEM_VNOC] = &ocmem_noc_to_ocmem_vnoc,
@@ -401,33 +990,288 @@ static const struct qcom_icc_desc msm8974_onoc = {
 	.ignore_enxio = true,
 };
 
-DEFINE_QNODE(mas_pnoc_cfg, MSM8974_PNOC_MAS_PNOC_CFG, 8, 43, -1);
-DEFINE_QNODE(mas_sdcc_1, MSM8974_PNOC_MAS_SDCC_1, 8, 33, -1, MSM8974_PNOC_TO_SNOC);
-DEFINE_QNODE(mas_sdcc_3, MSM8974_PNOC_MAS_SDCC_3, 8, 34, -1, MSM8974_PNOC_TO_SNOC);
-DEFINE_QNODE(mas_sdcc_4, MSM8974_PNOC_MAS_SDCC_4, 8, 36, -1, MSM8974_PNOC_TO_SNOC);
-DEFINE_QNODE(mas_sdcc_2, MSM8974_PNOC_MAS_SDCC_2, 8, 35, -1, MSM8974_PNOC_TO_SNOC);
-DEFINE_QNODE(mas_tsif, MSM8974_PNOC_MAS_TSIF, 8, 37, -1, MSM8974_PNOC_TO_SNOC);
-DEFINE_QNODE(mas_bam_dma, MSM8974_PNOC_MAS_BAM_DMA, 8, 38, -1);
-DEFINE_QNODE(mas_blsp_2, MSM8974_PNOC_MAS_BLSP_2, 8, 39, -1, MSM8974_PNOC_TO_SNOC);
-DEFINE_QNODE(mas_usb_hsic, MSM8974_PNOC_MAS_USB_HSIC, 8, 40, -1, MSM8974_PNOC_TO_SNOC);
-DEFINE_QNODE(mas_blsp_1, MSM8974_PNOC_MAS_BLSP_1, 8, 41, -1, MSM8974_PNOC_TO_SNOC);
-DEFINE_QNODE(mas_usb_hs, MSM8974_PNOC_MAS_USB_HS, 8, 42, -1, MSM8974_PNOC_TO_SNOC);
-DEFINE_QNODE(pnoc_to_snoc, MSM8974_PNOC_TO_SNOC, 8, 44, 45, MSM8974_SNOC_TO_PNOC, MSM8974_PNOC_SLV_PRNG);
-DEFINE_QNODE(slv_sdcc_1, MSM8974_PNOC_SLV_SDCC_1, 8, -1, 31);
-DEFINE_QNODE(slv_sdcc_3, MSM8974_PNOC_SLV_SDCC_3, 8, -1, 32);
-DEFINE_QNODE(slv_sdcc_2, MSM8974_PNOC_SLV_SDCC_2, 8, -1, 33);
-DEFINE_QNODE(slv_sdcc_4, MSM8974_PNOC_SLV_SDCC_4, 8, -1, 34);
-DEFINE_QNODE(slv_tsif, MSM8974_PNOC_SLV_TSIF, 8, -1, 35);
-DEFINE_QNODE(slv_bam_dma, MSM8974_PNOC_SLV_BAM_DMA, 8, -1, 36);
-DEFINE_QNODE(slv_blsp_2, MSM8974_PNOC_SLV_BLSP_2, 8, -1, 37);
-DEFINE_QNODE(slv_usb_hsic, MSM8974_PNOC_SLV_USB_HSIC, 8, -1, 38);
-DEFINE_QNODE(slv_blsp_1, MSM8974_PNOC_SLV_BLSP_1, 8, -1, 39);
-DEFINE_QNODE(slv_usb_hs, MSM8974_PNOC_SLV_USB_HS, 8, -1, 40);
-DEFINE_QNODE(slv_pdm, MSM8974_PNOC_SLV_PDM, 8, -1, 41);
-DEFINE_QNODE(slv_periph_apu_cfg, MSM8974_PNOC_SLV_PERIPH_APU_CFG, 8, -1, 42);
-DEFINE_QNODE(slv_pnoc_mpu_cfg, MSM8974_PNOC_SLV_PNOC_MPU_CFG, 8, -1, 43);
-DEFINE_QNODE(slv_prng, MSM8974_PNOC_SLV_PRNG, 8, -1, 44, MSM8974_PNOC_TO_SNOC);
-DEFINE_QNODE(slv_service_pnoc, MSM8974_PNOC_SLV_SERVICE_PNOC, 8, -1, 46);
+static struct qcom_icc_node mas_pnoc_cfg = {
+	.name = "mas_pnoc_cfg",
+	.id = MSM8974_PNOC_MAS_PNOC_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = 43,
+	.slv_rpm_id = -1,
+};
+
+static const u16 mas_sdcc_1_links[] = {
+	MSM8974_PNOC_TO_SNOC
+};
+
+static struct qcom_icc_node mas_sdcc_1 = {
+	.name = "mas_sdcc_1",
+	.id = MSM8974_PNOC_MAS_SDCC_1,
+	.buswidth = 8,
+	.mas_rpm_id = 33,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_sdcc_1_links),
+	.links = mas_sdcc_1_links,
+};
+
+static const u16 mas_sdcc_3_links[] = {
+	MSM8974_PNOC_TO_SNOC
+};
+
+static struct qcom_icc_node mas_sdcc_3 = {
+	.name = "mas_sdcc_3",
+	.id = MSM8974_PNOC_MAS_SDCC_3,
+	.buswidth = 8,
+	.mas_rpm_id = 34,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_sdcc_3_links),
+	.links = mas_sdcc_3_links,
+};
+
+static const u16 mas_sdcc_4_links[] = {
+	MSM8974_PNOC_TO_SNOC
+};
+
+static struct qcom_icc_node mas_sdcc_4 = {
+	.name = "mas_sdcc_4",
+	.id = MSM8974_PNOC_MAS_SDCC_4,
+	.buswidth = 8,
+	.mas_rpm_id = 36,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_sdcc_4_links),
+	.links = mas_sdcc_4_links,
+};
+
+static const u16 mas_sdcc_2_links[] = {
+	MSM8974_PNOC_TO_SNOC
+};
+
+static struct qcom_icc_node mas_sdcc_2 = {
+	.name = "mas_sdcc_2",
+	.id = MSM8974_PNOC_MAS_SDCC_2,
+	.buswidth = 8,
+	.mas_rpm_id = 35,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_sdcc_2_links),
+	.links = mas_sdcc_2_links,
+};
+
+static const u16 mas_tsif_links[] = {
+	MSM8974_PNOC_TO_SNOC
+};
+
+static struct qcom_icc_node mas_tsif = {
+	.name = "mas_tsif",
+	.id = MSM8974_PNOC_MAS_TSIF,
+	.buswidth = 8,
+	.mas_rpm_id = 37,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_tsif_links),
+	.links = mas_tsif_links,
+};
+
+static struct qcom_icc_node mas_bam_dma = {
+	.name = "mas_bam_dma",
+	.id = MSM8974_PNOC_MAS_BAM_DMA,
+	.buswidth = 8,
+	.mas_rpm_id = 38,
+	.slv_rpm_id = -1,
+};
+
+static const u16 mas_blsp_2_links[] = {
+	MSM8974_PNOC_TO_SNOC
+};
+
+static struct qcom_icc_node mas_blsp_2 = {
+	.name = "mas_blsp_2",
+	.id = MSM8974_PNOC_MAS_BLSP_2,
+	.buswidth = 8,
+	.mas_rpm_id = 39,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_blsp_2_links),
+	.links = mas_blsp_2_links,
+};
+
+static const u16 mas_usb_hsic_links[] = {
+	MSM8974_PNOC_TO_SNOC
+};
+
+static struct qcom_icc_node mas_usb_hsic = {
+	.name = "mas_usb_hsic",
+	.id = MSM8974_PNOC_MAS_USB_HSIC,
+	.buswidth = 8,
+	.mas_rpm_id = 40,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_usb_hsic_links),
+	.links = mas_usb_hsic_links,
+};
+
+static const u16 mas_blsp_1_links[] = {
+	MSM8974_PNOC_TO_SNOC
+};
+
+static struct qcom_icc_node mas_blsp_1 = {
+	.name = "mas_blsp_1",
+	.id = MSM8974_PNOC_MAS_BLSP_1,
+	.buswidth = 8,
+	.mas_rpm_id = 41,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_blsp_1_links),
+	.links = mas_blsp_1_links,
+};
+
+static const u16 mas_usb_hs_links[] = {
+	MSM8974_PNOC_TO_SNOC
+};
+
+static struct qcom_icc_node mas_usb_hs = {
+	.name = "mas_usb_hs",
+	.id = MSM8974_PNOC_MAS_USB_HS,
+	.buswidth = 8,
+	.mas_rpm_id = 42,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_usb_hs_links),
+	.links = mas_usb_hs_links,
+};
+
+static const u16 pnoc_to_snoc_links[] = {
+	MSM8974_SNOC_TO_PNOC,
+	MSM8974_PNOC_SLV_PRNG
+};
+
+static struct qcom_icc_node pnoc_to_snoc = {
+	.name = "pnoc_to_snoc",
+	.id = MSM8974_PNOC_TO_SNOC,
+	.buswidth = 8,
+	.mas_rpm_id = 44,
+	.slv_rpm_id = 45,
+	.num_links = ARRAY_SIZE(pnoc_to_snoc_links),
+	.links = pnoc_to_snoc_links,
+};
+
+static struct qcom_icc_node slv_sdcc_1 = {
+	.name = "slv_sdcc_1",
+	.id = MSM8974_PNOC_SLV_SDCC_1,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 31,
+};
+
+static struct qcom_icc_node slv_sdcc_3 = {
+	.name = "slv_sdcc_3",
+	.id = MSM8974_PNOC_SLV_SDCC_3,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 32,
+};
+
+static struct qcom_icc_node slv_sdcc_2 = {
+	.name = "slv_sdcc_2",
+	.id = MSM8974_PNOC_SLV_SDCC_2,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 33,
+};
+
+static struct qcom_icc_node slv_sdcc_4 = {
+	.name = "slv_sdcc_4",
+	.id = MSM8974_PNOC_SLV_SDCC_4,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 34,
+};
+
+static struct qcom_icc_node slv_tsif = {
+	.name = "slv_tsif",
+	.id = MSM8974_PNOC_SLV_TSIF,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 35,
+};
+
+static struct qcom_icc_node slv_bam_dma = {
+	.name = "slv_bam_dma",
+	.id = MSM8974_PNOC_SLV_BAM_DMA,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 36,
+};
+
+static struct qcom_icc_node slv_blsp_2 = {
+	.name = "slv_blsp_2",
+	.id = MSM8974_PNOC_SLV_BLSP_2,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 37,
+};
+
+static struct qcom_icc_node slv_usb_hsic = {
+	.name = "slv_usb_hsic",
+	.id = MSM8974_PNOC_SLV_USB_HSIC,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 38,
+};
+
+static struct qcom_icc_node slv_blsp_1 = {
+	.name = "slv_blsp_1",
+	.id = MSM8974_PNOC_SLV_BLSP_1,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 39,
+};
+
+static struct qcom_icc_node slv_usb_hs = {
+	.name = "slv_usb_hs",
+	.id = MSM8974_PNOC_SLV_USB_HS,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 40,
+};
+
+static struct qcom_icc_node slv_pdm = {
+	.name = "slv_pdm",
+	.id = MSM8974_PNOC_SLV_PDM,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 41,
+};
+
+static struct qcom_icc_node slv_periph_apu_cfg = {
+	.name = "slv_periph_apu_cfg",
+	.id = MSM8974_PNOC_SLV_PERIPH_APU_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 42,
+};
+
+static struct qcom_icc_node slv_pnoc_mpu_cfg = {
+	.name = "slv_pnoc_mpu_cfg",
+	.id = MSM8974_PNOC_SLV_PNOC_MPU_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 43,
+};
+
+static const u16 slv_prng_links[] = {
+	MSM8974_PNOC_TO_SNOC
+};
+
+static struct qcom_icc_node slv_prng = {
+	.name = "slv_prng",
+	.id = MSM8974_PNOC_SLV_PRNG,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 44,
+	.num_links = ARRAY_SIZE(slv_prng_links),
+	.links = slv_prng_links,
+};
+
+static struct qcom_icc_node slv_service_pnoc = {
+	.name = "slv_service_pnoc",
+	.id = MSM8974_PNOC_SLV_SERVICE_PNOC,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 46,
+};
 
 static struct qcom_icc_node * const msm8974_pnoc_nodes[] = {
 	[PNOC_MAS_PNOC_CFG] = &mas_pnoc_cfg,
@@ -468,30 +1312,233 @@ static const struct qcom_icc_desc msm8974_pnoc = {
 	.ignore_enxio = true,
 };
 
-DEFINE_QNODE(mas_lpass_ahb, MSM8974_SNOC_MAS_LPASS_AHB, 8, 18, -1);
-DEFINE_QNODE(mas_qdss_bam, MSM8974_SNOC_MAS_QDSS_BAM, 8, 19, -1);
-DEFINE_QNODE(mas_snoc_cfg, MSM8974_SNOC_MAS_SNOC_CFG, 8, 20, -1);
-DEFINE_QNODE(snoc_to_bimc, MSM8974_SNOC_TO_BIMC, 8, 21, 24, MSM8974_BIMC_TO_SNOC);
-DEFINE_QNODE(snoc_to_cnoc, MSM8974_SNOC_TO_CNOC, 8, 22, 25);
-DEFINE_QNODE(snoc_to_pnoc, MSM8974_SNOC_TO_PNOC, 8, 29, 28, MSM8974_PNOC_TO_SNOC);
-DEFINE_QNODE(snoc_to_ocmem_vnoc, MSM8974_SNOC_TO_OCMEM_VNOC, 8, 53, 77, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC);
-DEFINE_QNODE(mas_crypto_core0, MSM8974_SNOC_MAS_CRYPTO_CORE0, 8, 23, -1, MSM8974_SNOC_TO_BIMC);
-DEFINE_QNODE(mas_crypto_core1, MSM8974_SNOC_MAS_CRYPTO_CORE1, 8, 24, -1);
-DEFINE_QNODE(mas_lpass_proc, MSM8974_SNOC_MAS_LPASS_PROC, 8, 25, -1, MSM8974_SNOC_TO_OCMEM_VNOC);
-DEFINE_QNODE(mas_mss, MSM8974_SNOC_MAS_MSS, 8, 26, -1);
-DEFINE_QNODE(mas_mss_nav, MSM8974_SNOC_MAS_MSS_NAV, 8, 27, -1);
-DEFINE_QNODE(mas_ocmem_dma, MSM8974_SNOC_MAS_OCMEM_DMA, 8, 28, -1);
-DEFINE_QNODE(mas_wcss, MSM8974_SNOC_MAS_WCSS, 8, 30, -1);
-DEFINE_QNODE(mas_qdss_etr, MSM8974_SNOC_MAS_QDSS_ETR, 8, 31, -1);
-DEFINE_QNODE(mas_usb3, MSM8974_SNOC_MAS_USB3, 8, 32, -1, MSM8974_SNOC_TO_BIMC);
-DEFINE_QNODE(slv_ampss, MSM8974_SNOC_SLV_AMPSS, 8, -1, 20);
-DEFINE_QNODE(slv_lpass, MSM8974_SNOC_SLV_LPASS, 8, -1, 21);
-DEFINE_QNODE(slv_usb3, MSM8974_SNOC_SLV_USB3, 8, -1, 22);
-DEFINE_QNODE(slv_wcss, MSM8974_SNOC_SLV_WCSS, 8, -1, 23);
-DEFINE_QNODE(slv_ocimem, MSM8974_SNOC_SLV_OCIMEM, 8, -1, 26);
-DEFINE_QNODE(slv_snoc_ocmem, MSM8974_SNOC_SLV_SNOC_OCMEM, 8, -1, 27);
-DEFINE_QNODE(slv_service_snoc, MSM8974_SNOC_SLV_SERVICE_SNOC, 8, -1, 29);
-DEFINE_QNODE(slv_qdss_stm, MSM8974_SNOC_SLV_QDSS_STM, 8, -1, 30);
+static struct qcom_icc_node mas_lpass_ahb = {
+	.name = "mas_lpass_ahb",
+	.id = MSM8974_SNOC_MAS_LPASS_AHB,
+	.buswidth = 8,
+	.mas_rpm_id = 18,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_qdss_bam = {
+	.name = "mas_qdss_bam",
+	.id = MSM8974_SNOC_MAS_QDSS_BAM,
+	.buswidth = 8,
+	.mas_rpm_id = 19,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_snoc_cfg = {
+	.name = "mas_snoc_cfg",
+	.id = MSM8974_SNOC_MAS_SNOC_CFG,
+	.buswidth = 8,
+	.mas_rpm_id = 20,
+	.slv_rpm_id = -1,
+};
+
+static const u16 snoc_to_bimc_links[] = {
+	MSM8974_BIMC_TO_SNOC
+};
+
+static struct qcom_icc_node snoc_to_bimc = {
+	.name = "snoc_to_bimc",
+	.id = MSM8974_SNOC_TO_BIMC,
+	.buswidth = 8,
+	.mas_rpm_id = 21,
+	.slv_rpm_id = 24,
+	.num_links = ARRAY_SIZE(snoc_to_bimc_links),
+	.links = snoc_to_bimc_links,
+};
+
+static struct qcom_icc_node snoc_to_cnoc = {
+	.name = "snoc_to_cnoc",
+	.id = MSM8974_SNOC_TO_CNOC,
+	.buswidth = 8,
+	.mas_rpm_id = 22,
+	.slv_rpm_id = 25,
+};
+
+static const u16 snoc_to_pnoc_links[] = {
+	MSM8974_PNOC_TO_SNOC
+};
+
+static struct qcom_icc_node snoc_to_pnoc = {
+	.name = "snoc_to_pnoc",
+	.id = MSM8974_SNOC_TO_PNOC,
+	.buswidth = 8,
+	.mas_rpm_id = 29,
+	.slv_rpm_id = 28,
+	.num_links = ARRAY_SIZE(snoc_to_pnoc_links),
+	.links = snoc_to_pnoc_links,
+};
+
+static const u16 snoc_to_ocmem_vnoc_links[] = {
+	MSM8974_OCMEM_VNOC_TO_OCMEM_NOC
+};
+
+static struct qcom_icc_node snoc_to_ocmem_vnoc = {
+	.name = "snoc_to_ocmem_vnoc",
+	.id = MSM8974_SNOC_TO_OCMEM_VNOC,
+	.buswidth = 8,
+	.mas_rpm_id = 53,
+	.slv_rpm_id = 77,
+	.num_links = ARRAY_SIZE(snoc_to_ocmem_vnoc_links),
+	.links = snoc_to_ocmem_vnoc_links,
+};
+
+static const u16 mas_crypto_core0_links[] = {
+	MSM8974_SNOC_TO_BIMC
+};
+
+static struct qcom_icc_node mas_crypto_core0 = {
+	.name = "mas_crypto_core0",
+	.id = MSM8974_SNOC_MAS_CRYPTO_CORE0,
+	.buswidth = 8,
+	.mas_rpm_id = 23,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_crypto_core0_links),
+	.links = mas_crypto_core0_links,
+};
+
+static struct qcom_icc_node mas_crypto_core1 = {
+	.name = "mas_crypto_core1",
+	.id = MSM8974_SNOC_MAS_CRYPTO_CORE1,
+	.buswidth = 8,
+	.mas_rpm_id = 24,
+	.slv_rpm_id = -1,
+};
+
+static const u16 mas_lpass_proc_links[] = {
+	MSM8974_SNOC_TO_OCMEM_VNOC
+};
+
+static struct qcom_icc_node mas_lpass_proc = {
+	.name = "mas_lpass_proc",
+	.id = MSM8974_SNOC_MAS_LPASS_PROC,
+	.buswidth = 8,
+	.mas_rpm_id = 25,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_lpass_proc_links),
+	.links = mas_lpass_proc_links,
+};
+
+static struct qcom_icc_node mas_mss = {
+	.name = "mas_mss",
+	.id = MSM8974_SNOC_MAS_MSS,
+	.buswidth = 8,
+	.mas_rpm_id = 26,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_mss_nav = {
+	.name = "mas_mss_nav",
+	.id = MSM8974_SNOC_MAS_MSS_NAV,
+	.buswidth = 8,
+	.mas_rpm_id = 27,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_ocmem_dma = {
+	.name = "mas_ocmem_dma",
+	.id = MSM8974_SNOC_MAS_OCMEM_DMA,
+	.buswidth = 8,
+	.mas_rpm_id = 28,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_wcss = {
+	.name = "mas_wcss",
+	.id = MSM8974_SNOC_MAS_WCSS,
+	.buswidth = 8,
+	.mas_rpm_id = 30,
+	.slv_rpm_id = -1,
+};
+
+static struct qcom_icc_node mas_qdss_etr = {
+	.name = "mas_qdss_etr",
+	.id = MSM8974_SNOC_MAS_QDSS_ETR,
+	.buswidth = 8,
+	.mas_rpm_id = 31,
+	.slv_rpm_id = -1,
+};
+
+static const u16 mas_usb3_links[] = {
+	MSM8974_SNOC_TO_BIMC
+};
+
+static struct qcom_icc_node mas_usb3 = {
+	.name = "mas_usb3",
+	.id = MSM8974_SNOC_MAS_USB3,
+	.buswidth = 8,
+	.mas_rpm_id = 32,
+	.slv_rpm_id = -1,
+	.num_links = ARRAY_SIZE(mas_usb3_links),
+	.links = mas_usb3_links,
+};
+
+static struct qcom_icc_node slv_ampss = {
+	.name = "slv_ampss",
+	.id = MSM8974_SNOC_SLV_AMPSS,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 20,
+};
+
+static struct qcom_icc_node slv_lpass = {
+	.name = "slv_lpass",
+	.id = MSM8974_SNOC_SLV_LPASS,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 21,
+};
+
+static struct qcom_icc_node slv_usb3 = {
+	.name = "slv_usb3",
+	.id = MSM8974_SNOC_SLV_USB3,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 22,
+};
+
+static struct qcom_icc_node slv_wcss = {
+	.name = "slv_wcss",
+	.id = MSM8974_SNOC_SLV_WCSS,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 23,
+};
+
+static struct qcom_icc_node slv_ocimem = {
+	.name = "slv_ocimem",
+	.id = MSM8974_SNOC_SLV_OCIMEM,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 26,
+};
+
+static struct qcom_icc_node slv_snoc_ocmem = {
+	.name = "slv_snoc_ocmem",
+	.id = MSM8974_SNOC_SLV_SNOC_OCMEM,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 27,
+};
+
+static struct qcom_icc_node slv_service_snoc = {
+	.name = "slv_service_snoc",
+	.id = MSM8974_SNOC_SLV_SERVICE_SNOC,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 29,
+};
+
+static struct qcom_icc_node slv_qdss_stm = {
+	.name = "slv_qdss_stm",
+	.id = MSM8974_SNOC_SLV_QDSS_STM,
+	.buswidth = 8,
+	.mas_rpm_id = -1,
+	.slv_rpm_id = 30,
+};
 
 static struct qcom_icc_node * const msm8974_snoc_nodes[] = {
 	[SNOC_MAS_LPASS_AHB] = &mas_lpass_ahb,

-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 7/9] interconnect: qcom: msm8974: switch to the main icc-rpm driver
From: Dmitry Baryshkov @ 2026-03-24  0:10 UTC (permalink / raw)
  To: Georgi Djakov, Konrad Dybcio, Bjorn Andersson, Luca Weiss,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Brian Masney
  Cc: linux-arm-msm, linux-pm, devicetree, linux-kernel, Konrad Dybcio
In-Reply-To: <20260324-msm8974-icc-v2-0-527280043ad8@oss.qualcomm.com>

In preparation to restoring the ability of MSM8974 driver to work with
the modern kernels, switch the driver to the main icc-rpm set of helper
code.

As platform-specific workarounds, set the get_bw callback (returning 0)
to prevent initial setup from programming INT_MAX into the RPM (which
otherwise might hang the platform) and tell RPM programming code to
ignore -ENXIO errors from the firmware (until the QoS programming is
sorted out).

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 drivers/interconnect/qcom/msm8974.c | 304 +++++-------------------------------
 1 file changed, 43 insertions(+), 261 deletions(-)

diff --git a/drivers/interconnect/qcom/msm8974.c b/drivers/interconnect/qcom/msm8974.c
index 3239edc37f02..144f225ec885 100644
--- a/drivers/interconnect/qcom/msm8974.c
+++ b/drivers/interconnect/qcom/msm8974.c
@@ -173,65 +173,27 @@ enum {
 	MSM8974_SNOC_SLV_QDSS_STM,
 };
 
-#define to_msm8974_icc_provider(_provider) \
-	container_of(_provider, struct msm8974_icc_provider, provider)
-
-static const struct clk_bulk_data msm8974_icc_bus_clocks[] = {
-	{ .id = "bus" },
-	{ .id = "bus_a" },
-};
-
-/**
- * struct msm8974_icc_provider - Qualcomm specific interconnect provider
- * @provider: generic interconnect provider
- * @bus_clks: the clk_bulk_data table of bus clocks
- * @num_clks: the total number of clk_bulk_data entries
- */
-struct msm8974_icc_provider {
-	struct icc_provider provider;
-	struct clk_bulk_data *bus_clks;
-	int num_clks;
-};
-
-#define MSM8974_ICC_MAX_LINKS	3
-
-/**
- * struct msm8974_icc_node - Qualcomm specific interconnect nodes
- * @name: the node name used in debugfs
- * @id: a unique node identifier
- * @links: an array of nodes where we can go next while traversing
- * @num_links: the total number of @links
- * @buswidth: width of the interconnect between a node and the bus (bytes)
- * @mas_rpm_id:	RPM ID for devices that are bus masters
- * @slv_rpm_id:	RPM ID for devices that are bus slaves
- * @rate: current bus clock rate in Hz
- */
-struct msm8974_icc_node {
-	unsigned char *name;
-	u16 id;
-	u16 links[MSM8974_ICC_MAX_LINKS];
-	u16 num_links;
-	u16 buswidth;
-	int mas_rpm_id;
-	int slv_rpm_id;
-	u64 rate;
-};
+static int msm8974_get_bw(struct icc_node *node, u32 *avg, u32 *peak)
+{
+	*avg = 0;
+	*peak = 0;
 
-struct msm8974_icc_desc {
-	struct msm8974_icc_node * const *nodes;
-	size_t num_nodes;
+	return 0;
 };
 
 #define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id,	\
 		     ...)						\
-		static struct msm8974_icc_node _name = {		\
+		static const u16 _name ## _links[] = {			\
+			__VA_ARGS__					\
+		};							\
+		static struct qcom_icc_node _name = {			\
 		.name = #_name,						\
 		.id = _id,						\
 		.buswidth = _buswidth,					\
 		.mas_rpm_id = _mas_rpm_id,				\
 		.slv_rpm_id = _slv_rpm_id,				\
-		.num_links = COUNT_ARGS(__VA_ARGS__),			\
-		.links = { __VA_ARGS__ },				\
+		.num_links = ARRAY_SIZE(_name ## _links),		\
+		.links = _name ## _links,				\
 	}
 
 DEFINE_QNODE(mas_ampss_m0, MSM8974_BIMC_MAS_AMPSS_M0, 8, 0, -1);
@@ -242,7 +204,7 @@ DEFINE_QNODE(bimc_to_snoc, MSM8974_BIMC_TO_SNOC, 8, 3, 2, MSM8974_SNOC_TO_BIMC,
 DEFINE_QNODE(slv_ebi_ch0, MSM8974_BIMC_SLV_EBI_CH0, 8, -1, 0);
 DEFINE_QNODE(slv_ampss_l2, MSM8974_BIMC_SLV_AMPSS_L2, 8, -1, 1);
 
-static struct msm8974_icc_node * const msm8974_bimc_nodes[] = {
+static struct qcom_icc_node * const msm8974_bimc_nodes[] = {
 	[BIMC_MAS_AMPSS_M0] = &mas_ampss_m0,
 	[BIMC_MAS_AMPSS_M1] = &mas_ampss_m1,
 	[BIMC_MAS_MSS_PROC] = &mas_mss_proc,
@@ -252,9 +214,12 @@ static struct msm8974_icc_node * const msm8974_bimc_nodes[] = {
 	[BIMC_SLV_AMPSS_L2] = &slv_ampss_l2,
 };
 
-static const struct msm8974_icc_desc msm8974_bimc = {
+static const struct qcom_icc_desc msm8974_bimc = {
 	.nodes = msm8974_bimc_nodes,
 	.num_nodes = ARRAY_SIZE(msm8974_bimc_nodes),
+	.bus_clk_desc = &bimc_clk,
+	.get_bw = msm8974_get_bw,
+	.ignore_enxio = true,
 };
 
 DEFINE_QNODE(mas_rpm_inst, MSM8974_CNOC_MAS_RPM_INST, 8, 45, -1);
@@ -295,7 +260,7 @@ DEFINE_QNODE(slv_ebi1_phy_cfg, MSM8974_CNOC_SLV_EBI1_PHY_CFG, 8, -1, 73);
 DEFINE_QNODE(slv_rpm, MSM8974_CNOC_SLV_RPM, 8, -1, 74);
 DEFINE_QNODE(slv_service_cnoc, MSM8974_CNOC_SLV_SERVICE_CNOC, 8, -1, 76);
 
-static struct msm8974_icc_node * const msm8974_cnoc_nodes[] = {
+static struct qcom_icc_node * const msm8974_cnoc_nodes[] = {
 	[CNOC_MAS_RPM_INST] = &mas_rpm_inst,
 	[CNOC_MAS_RPM_DATA] = &mas_rpm_data,
 	[CNOC_MAS_RPM_SYS] = &mas_rpm_sys,
@@ -335,9 +300,12 @@ static struct msm8974_icc_node * const msm8974_cnoc_nodes[] = {
 	[CNOC_SLV_SERVICE_CNOC] = &slv_service_cnoc,
 };
 
-static const struct msm8974_icc_desc msm8974_cnoc = {
+static const struct qcom_icc_desc msm8974_cnoc = {
 	.nodes = msm8974_cnoc_nodes,
 	.num_nodes = ARRAY_SIZE(msm8974_cnoc_nodes),
+	.bus_clk_desc = &bus_2_clk,
+	.get_bw = msm8974_get_bw,
+	.ignore_enxio = true,
 };
 
 DEFINE_QNODE(mas_graphics_3d, MSM8974_MNOC_MAS_GRAPHICS_3D, 16, 6, -1, MSM8974_MNOC_TO_BIMC);
@@ -363,7 +331,7 @@ DEFINE_QNODE(slv_mnoc_mpu_cfg, MSM8974_MNOC_SLV_MNOC_MPU_CFG, 16, -1, 14);
 DEFINE_QNODE(slv_onoc_mpu_cfg, MSM8974_MNOC_SLV_ONOC_MPU_CFG, 16, -1, 15);
 DEFINE_QNODE(slv_service_mnoc, MSM8974_MNOC_SLV_SERVICE_MNOC, 16, -1, 17);
 
-static struct msm8974_icc_node * const msm8974_mnoc_nodes[] = {
+static struct qcom_icc_node * const msm8974_mnoc_nodes[] = {
 	[MNOC_MAS_GRAPHICS_3D] = &mas_graphics_3d,
 	[MNOC_MAS_JPEG] = &mas_jpeg,
 	[MNOC_MAS_MDP_PORT0] = &mas_mdp_port0,
@@ -388,9 +356,11 @@ static struct msm8974_icc_node * const msm8974_mnoc_nodes[] = {
 	[MNOC_SLV_SERVICE_MNOC] = &slv_service_mnoc,
 };
 
-static const struct msm8974_icc_desc msm8974_mnoc = {
+static const struct qcom_icc_desc msm8974_mnoc = {
 	.nodes = msm8974_mnoc_nodes,
 	.num_nodes = ARRAY_SIZE(msm8974_mnoc_nodes),
+	.get_bw = msm8974_get_bw,
+	.ignore_enxio = true,
 };
 
 DEFINE_QNODE(ocmem_noc_to_ocmem_vnoc, MSM8974_OCMEM_NOC_TO_OCMEM_VNOC, 16, 54, 78, MSM8974_OCMEM_SLV_OCMEM);
@@ -408,7 +378,7 @@ DEFINE_QNODE(ocmem_vnoc_to_onoc, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC, 16, 56, 79, MS
 DEFINE_QNODE(ocmem_vnoc_to_snoc, MSM8974_OCMEM_VNOC_TO_SNOC, 8, 57, 80);
 DEFINE_QNODE(mas_v_ocmem_gfx3d, MSM8974_OCMEM_VNOC_MAS_GFX3D, 8, 55, -1, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC);
 
-static struct msm8974_icc_node * const msm8974_onoc_nodes[] = {
+static struct qcom_icc_node * const msm8974_onoc_nodes[] = {
 	[OCMEM_NOC_TO_OCMEM_VNOC] = &ocmem_noc_to_ocmem_vnoc,
 	[OCMEM_MAS_JPEG_OCMEM] = &mas_jpeg_ocmem,
 	[OCMEM_MAS_MDP_OCMEM] = &mas_mdp_ocmem,
@@ -423,9 +393,12 @@ static struct msm8974_icc_node * const msm8974_onoc_nodes[] = {
 	[OCMEM_SLV_OCMEM] = &slv_ocmem,
 };
 
-static const struct msm8974_icc_desc msm8974_onoc = {
+static const struct qcom_icc_desc msm8974_onoc = {
 	.nodes = msm8974_onoc_nodes,
 	.num_nodes = ARRAY_SIZE(msm8974_onoc_nodes),
+	.bus_clk_desc = &gpu_mem_2_clk,
+	.get_bw = msm8974_get_bw,
+	.ignore_enxio = true,
 };
 
 DEFINE_QNODE(mas_pnoc_cfg, MSM8974_PNOC_MAS_PNOC_CFG, 8, 43, -1);
@@ -456,7 +429,7 @@ DEFINE_QNODE(slv_pnoc_mpu_cfg, MSM8974_PNOC_SLV_PNOC_MPU_CFG, 8, -1, 43);
 DEFINE_QNODE(slv_prng, MSM8974_PNOC_SLV_PRNG, 8, -1, 44, MSM8974_PNOC_TO_SNOC);
 DEFINE_QNODE(slv_service_pnoc, MSM8974_PNOC_SLV_SERVICE_PNOC, 8, -1, 46);
 
-static struct msm8974_icc_node * const msm8974_pnoc_nodes[] = {
+static struct qcom_icc_node * const msm8974_pnoc_nodes[] = {
 	[PNOC_MAS_PNOC_CFG] = &mas_pnoc_cfg,
 	[PNOC_MAS_SDCC_1] = &mas_sdcc_1,
 	[PNOC_MAS_SDCC_3] = &mas_sdcc_3,
@@ -486,9 +459,13 @@ static struct msm8974_icc_node * const msm8974_pnoc_nodes[] = {
 	[PNOC_SLV_SERVICE_PNOC] = &slv_service_pnoc,
 };
 
-static const struct msm8974_icc_desc msm8974_pnoc = {
+static const struct qcom_icc_desc msm8974_pnoc = {
 	.nodes = msm8974_pnoc_nodes,
 	.num_nodes = ARRAY_SIZE(msm8974_pnoc_nodes),
+	.bus_clk_desc = &bus_0_clk,
+	.get_bw = msm8974_get_bw,
+	.keep_alive = true,
+	.ignore_enxio = true,
 };
 
 DEFINE_QNODE(mas_lpass_ahb, MSM8974_SNOC_MAS_LPASS_AHB, 8, 18, -1);
@@ -516,7 +493,7 @@ DEFINE_QNODE(slv_snoc_ocmem, MSM8974_SNOC_SLV_SNOC_OCMEM, 8, -1, 27);
 DEFINE_QNODE(slv_service_snoc, MSM8974_SNOC_SLV_SERVICE_SNOC, 8, -1, 29);
 DEFINE_QNODE(slv_qdss_stm, MSM8974_SNOC_SLV_QDSS_STM, 8, -1, 30);
 
-static struct msm8974_icc_node * const msm8974_snoc_nodes[] = {
+static struct qcom_icc_node * const msm8974_snoc_nodes[] = {
 	[SNOC_MAS_LPASS_AHB] = &mas_lpass_ahb,
 	[SNOC_MAS_QDSS_BAM] = &mas_qdss_bam,
 	[SNOC_MAS_SNOC_CFG] = &mas_snoc_cfg,
@@ -543,209 +520,14 @@ static struct msm8974_icc_node * const msm8974_snoc_nodes[] = {
 	[SNOC_SLV_QDSS_STM] = &slv_qdss_stm,
 };
 
-static const struct msm8974_icc_desc msm8974_snoc = {
+static const struct qcom_icc_desc msm8974_snoc = {
 	.nodes = msm8974_snoc_nodes,
 	.num_nodes = ARRAY_SIZE(msm8974_snoc_nodes),
+	.bus_clk_desc = &bus_1_clk,
+	.get_bw = msm8974_get_bw,
+	.ignore_enxio = true,
 };
 
-static void msm8974_icc_rpm_smd_send(struct device *dev, int rsc_type,
-				     char *name, int id, u64 val)
-{
-	int ret;
-
-	if (id == -1)
-		return;
-
-	/*
-	 * Setting the bandwidth requests for some nodes fails and this same
-	 * behavior occurs on the downstream MSM 3.4 kernel sources based on
-	 * errors like this in that kernel:
-	 *
-	 *   msm_rpm_get_error_from_ack(): RPM NACK Unsupported resource
-	 *   AXI: msm_bus_rpm_req(): RPM: Ack failed
-	 *   AXI: msm_bus_rpm_commit_arb(): RPM: Req fail: mas:32, bw:240000000
-	 *
-	 * Since there's no publicly available documentation for this hardware,
-	 * and the bandwidth for some nodes in the path can be set properly,
-	 * let's not return an error.
-	 */
-	ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, rsc_type, id,
-				    val);
-	if (ret)
-		dev_dbg(dev, "Cannot set bandwidth for node %s (%d): %d\n",
-			name, id, ret);
-}
-
-static int msm8974_icc_set(struct icc_node *src, struct icc_node *dst)
-{
-	struct msm8974_icc_node *src_qn, *dst_qn;
-	struct msm8974_icc_provider *qp;
-	u64 sum_bw, max_peak_bw, rate;
-	u32 agg_avg = 0, agg_peak = 0;
-	struct icc_provider *provider;
-	struct icc_node *n;
-	int ret, i;
-
-	src_qn = src->data;
-	dst_qn = dst->data;
-	provider = src->provider;
-	qp = to_msm8974_icc_provider(provider);
-
-	list_for_each_entry(n, &provider->nodes, node_list)
-		provider->aggregate(n, 0, n->avg_bw, n->peak_bw,
-				    &agg_avg, &agg_peak);
-
-	sum_bw = icc_units_to_bps(agg_avg);
-	max_peak_bw = icc_units_to_bps(agg_peak);
-
-	/* Set bandwidth on source node */
-	msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_MASTER_REQ,
-				 src_qn->name, src_qn->mas_rpm_id, sum_bw);
-
-	msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_SLAVE_REQ,
-				 src_qn->name, src_qn->slv_rpm_id, sum_bw);
-
-	/* Set bandwidth on destination node */
-	msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_MASTER_REQ,
-				 dst_qn->name, dst_qn->mas_rpm_id, sum_bw);
-
-	msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_SLAVE_REQ,
-				 dst_qn->name, dst_qn->slv_rpm_id, sum_bw);
-
-	rate = max(sum_bw, max_peak_bw);
-
-	do_div(rate, src_qn->buswidth);
-
-	rate = min_t(u32, rate, INT_MAX);
-
-	if (src_qn->rate == rate)
-		return 0;
-
-	for (i = 0; i < qp->num_clks; i++) {
-		ret = clk_set_rate(qp->bus_clks[i].clk, rate);
-		if (ret) {
-			dev_err(provider->dev, "%s clk_set_rate error: %d\n",
-				qp->bus_clks[i].id, ret);
-			ret = 0;
-		}
-	}
-
-	src_qn->rate = rate;
-
-	return 0;
-}
-
-static int msm8974_get_bw(struct icc_node *node, u32 *avg, u32 *peak)
-{
-	*avg = 0;
-	*peak = 0;
-
-	return 0;
-}
-
-static int msm8974_icc_probe(struct platform_device *pdev)
-{
-	const struct msm8974_icc_desc *desc;
-	struct msm8974_icc_node * const *qnodes;
-	struct msm8974_icc_provider *qp;
-	struct device *dev = &pdev->dev;
-	struct icc_onecell_data *data;
-	struct icc_provider *provider;
-	struct icc_node *node;
-	size_t num_nodes, i;
-	int ret;
-
-	/* wait for the RPM proxy */
-	if (!qcom_icc_rpm_smd_available())
-		return -EPROBE_DEFER;
-
-	desc = of_device_get_match_data(dev);
-	if (!desc)
-		return -EINVAL;
-
-	qnodes = desc->nodes;
-	num_nodes = desc->num_nodes;
-
-	qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL);
-	if (!qp)
-		return -ENOMEM;
-
-	data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes),
-			    GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-	data->num_nodes = num_nodes;
-
-	qp->bus_clks = devm_kmemdup(dev, msm8974_icc_bus_clocks,
-				    sizeof(msm8974_icc_bus_clocks), GFP_KERNEL);
-	if (!qp->bus_clks)
-		return -ENOMEM;
-
-	qp->num_clks = ARRAY_SIZE(msm8974_icc_bus_clocks);
-	ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks);
-	if (ret)
-		return ret;
-
-	ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks);
-	if (ret)
-		return ret;
-
-	provider = &qp->provider;
-	provider->dev = dev;
-	provider->set = msm8974_icc_set;
-	provider->aggregate = icc_std_aggregate;
-	provider->xlate = of_icc_xlate_onecell;
-	provider->data = data;
-	provider->get_bw = msm8974_get_bw;
-
-	icc_provider_init(provider);
-
-	for (i = 0; i < num_nodes; i++) {
-		size_t j;
-
-		node = icc_node_create(qnodes[i]->id);
-		if (IS_ERR(node)) {
-			ret = PTR_ERR(node);
-			goto err_remove_nodes;
-		}
-
-		node->name = qnodes[i]->name;
-		node->data = qnodes[i];
-		icc_node_add(node, provider);
-
-		dev_dbg(dev, "registered node %s\n", node->name);
-
-		/* populate links */
-		for (j = 0; j < qnodes[i]->num_links; j++)
-			icc_link_create(node, qnodes[i]->links[j]);
-
-		data->nodes[i] = node;
-	}
-
-	ret = icc_provider_register(provider);
-	if (ret)
-		goto err_remove_nodes;
-
-	platform_set_drvdata(pdev, qp);
-
-	return 0;
-
-err_remove_nodes:
-	icc_nodes_remove(provider);
-	clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
-
-	return ret;
-}
-
-static void msm8974_icc_remove(struct platform_device *pdev)
-{
-	struct msm8974_icc_provider *qp = platform_get_drvdata(pdev);
-
-	icc_provider_deregister(&qp->provider);
-	icc_nodes_remove(&qp->provider);
-	clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
-}
-
 static const struct of_device_id msm8974_noc_of_match[] = {
 	{ .compatible = "qcom,msm8974-bimc", .data = &msm8974_bimc},
 	{ .compatible = "qcom,msm8974-cnoc", .data = &msm8974_cnoc},
@@ -758,8 +540,8 @@ static const struct of_device_id msm8974_noc_of_match[] = {
 MODULE_DEVICE_TABLE(of, msm8974_noc_of_match);
 
 static struct platform_driver msm8974_noc_driver = {
-	.probe = msm8974_icc_probe,
-	.remove = msm8974_icc_remove,
+	.probe = qnoc_probe,
+	.remove = qnoc_remove,
 	.driver = {
 		.name = "qnoc-msm8974",
 		.of_match_table = msm8974_noc_of_match,

-- 
2.47.3


^ permalink raw reply related

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

On MSM8974 programming some of the RPM resources results in the
"resource does not exist" messages from the firmware. This occurs even
with the downstream bus driver, which happily ignores the errors. My
assumption is that these resources existed in the earlier firmware
revisions but were later switched to be programmed differently (for the
later platforms corresponding nodes use qos.ap_owned, which prevents
those resources from being programmed.

In preparation for conversion of the MSM8974 driver (which doesn't have
QoS code yet) to the main icc-rpm set of helpers, let the driver declare
that those -ENXIO errors must be ignored (for now). Later, when the QoS
programming is sorted out (and more interconnects are added to the DT),
this quirk might be removed.

Reviewed-by: Brian Masney <bmasney@redhat.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 drivers/interconnect/qcom/icc-rpm.c | 17 ++++++++++-------
 drivers/interconnect/qcom/icc-rpm.h |  3 +++
 2 files changed, 13 insertions(+), 7 deletions(-)

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

-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 5/9] interconnect: qcom: define OCMEM bus resource
From: Dmitry Baryshkov @ 2026-03-24  0:10 UTC (permalink / raw)
  To: Georgi Djakov, Konrad Dybcio, Bjorn Andersson, Luca Weiss,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Brian Masney
  Cc: linux-arm-msm, linux-pm, devicetree, linux-kernel, Brian Masney,
	Konrad Dybcio
In-Reply-To: <20260324-msm8974-icc-v2-0-527280043ad8@oss.qualcomm.com>

Some of the platforms (MSM8974, MSM8x26) require voting on the OCMEM
clock. Add new resource for that clock.

Reviewed-by: Brian Masney <bmasney@redhat.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 drivers/interconnect/qcom/icc-rpm-clocks.c | 6 ++++++
 drivers/interconnect/qcom/icc-rpm.h        | 1 +
 2 files changed, 7 insertions(+)

diff --git a/drivers/interconnect/qcom/icc-rpm-clocks.c b/drivers/interconnect/qcom/icc-rpm-clocks.c
index ac1677de7dfd..69846e26f46a 100644
--- a/drivers/interconnect/qcom/icc-rpm-clocks.c
+++ b/drivers/interconnect/qcom/icc-rpm-clocks.c
@@ -31,6 +31,12 @@ const struct rpm_clk_resource mem_1_clk = {
 };
 EXPORT_SYMBOL_GPL(mem_1_clk);
 
+const struct rpm_clk_resource gpu_mem_2_clk = {
+	.resource_type = QCOM_SMD_RPM_MEM_CLK,
+	.clock_id = 2,
+};
+EXPORT_SYMBOL_GPL(gpu_mem_2_clk);
+
 const struct rpm_clk_resource bus_0_clk = {
 	.resource_type = QCOM_SMD_RPM_BUS_CLK,
 	.clock_id = 0,
diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h
index cbf0a365839d..ad554c63967b 100644
--- a/drivers/interconnect/qcom/icc-rpm.h
+++ b/drivers/interconnect/qcom/icc-rpm.h
@@ -151,6 +151,7 @@ extern const struct rpm_clk_resource bimc_clk;
 extern const struct rpm_clk_resource bus_0_clk;
 extern const struct rpm_clk_resource bus_1_clk;
 extern const struct rpm_clk_resource bus_2_clk;
+extern const struct rpm_clk_resource gpu_mem_2_clk;
 extern const struct rpm_clk_resource mem_1_clk;
 extern const struct rpm_clk_resource mmaxi_0_clk;
 extern const struct rpm_clk_resource mmaxi_1_clk;

-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 4/9] interconnect: qcom: icc-rpm: allow overwriting get_bw callback
From: Dmitry Baryshkov @ 2026-03-24  0:10 UTC (permalink / raw)
  To: Georgi Djakov, Konrad Dybcio, Bjorn Andersson, Luca Weiss,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Brian Masney
  Cc: linux-arm-msm, linux-pm, devicetree, linux-kernel
In-Reply-To: <20260324-msm8974-icc-v2-0-527280043ad8@oss.qualcomm.com>

MSM8974 requires a separate get_bw callback, since on that platform
increasing the clock rate for some of the NoCs during boot may lead to
hangs. For the details see commit 9caf2d956cfa ("interconnect: qcom:
msm8974: Don't boost the NoC rate during boot").

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 drivers/interconnect/qcom/icc-rpm.c | 1 +
 drivers/interconnect/qcom/icc-rpm.h | 1 +
 2 files changed, 2 insertions(+)

diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c
index ea1042d38128..aec2f84cd56f 100644
--- a/drivers/interconnect/qcom/icc-rpm.c
+++ b/drivers/interconnect/qcom/icc-rpm.c
@@ -553,6 +553,7 @@ int qnoc_probe(struct platform_device *pdev)
 	provider->aggregate = qcom_icc_bw_aggregate;
 	provider->xlate_extended = qcom_icc_xlate_extended;
 	provider->data = data;
+	provider->get_bw = desc->get_bw;
 
 	icc_provider_init(provider);
 
diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h
index 3366531f66fc..cbf0a365839d 100644
--- a/drivers/interconnect/qcom/icc-rpm.h
+++ b/drivers/interconnect/qcom/icc-rpm.h
@@ -135,6 +135,7 @@ struct qcom_icc_desc {
 	unsigned int qos_offset;
 	u16 ab_coeff;
 	u16 ib_coeff;
+	int (*get_bw)(struct icc_node *node, u32 *avg, u32 *peak);
 };
 
 /* Valid for all bus types */

-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 3/9] interconnect: qcom: drop unused is_on flag
From: Dmitry Baryshkov @ 2026-03-24  0:10 UTC (permalink / raw)
  To: Georgi Djakov, Konrad Dybcio, Bjorn Andersson, Luca Weiss,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Brian Masney
  Cc: linux-arm-msm, linux-pm, devicetree, linux-kernel
In-Reply-To: <20260324-msm8974-icc-v2-0-527280043ad8@oss.qualcomm.com>

The commit 2e2113c8a64f ("interconnect: qcom: rpm: Handle interface
clocks") has added the is_on flag to the qcom_icc_provider, but failed
to actually utilize it. Drop the flag.

Fixes: 2e2113c8a64f ("interconnect: qcom: rpm: Handle interface clocks")
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 drivers/interconnect/qcom/icc-rpm.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h
index f4883d43eae4..3366531f66fc 100644
--- a/drivers/interconnect/qcom/icc-rpm.h
+++ b/drivers/interconnect/qcom/icc-rpm.h
@@ -51,7 +51,6 @@ struct rpm_clk_resource {
  * @bus_clk: a pointer to a HLOS-owned bus clock
  * @intf_clks: a clk_bulk_data array of interface clocks
  * @keep_alive: whether to always keep a minimum vote on the bus clocks
- * @is_on: whether the bus is powered on
  */
 struct qcom_icc_provider {
 	struct icc_provider provider;
@@ -66,7 +65,6 @@ struct qcom_icc_provider {
 	struct clk *bus_clk;
 	struct clk_bulk_data *intf_clks;
 	bool keep_alive;
-	bool is_on;
 };
 
 /**

-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 2/9] dt-bindings: interconnect: qcom,msm8974: use qcom,rpm-common
From: Dmitry Baryshkov @ 2026-03-24  0:10 UTC (permalink / raw)
  To: Georgi Djakov, Konrad Dybcio, Bjorn Andersson, Luca Weiss,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Brian Masney
  Cc: linux-arm-msm, linux-pm, devicetree, linux-kernel
In-Reply-To: <20260324-msm8974-icc-v2-0-527280043ad8@oss.qualcomm.com>

Use qcom,rpm-common schema to declare interconnects property instead
describing it again. In future this will allow the platform to switch to
the two-cell interconnects, adding the tag to the specification.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml
index 89a694501d8c..b35f6dd11c71 100644
--- a/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml
+++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml
@@ -26,9 +26,6 @@ properties:
       - qcom,msm8974-pnoc
       - qcom,msm8974-snoc
 
-  '#interconnect-cells':
-    const: 1
-
   clock-names:
     items:
       - const: bus
@@ -40,11 +37,11 @@ properties:
 required:
   - compatible
   - reg
-  - '#interconnect-cells'
 
-additionalProperties: false
+unevaluatedProperties: false
 
 allOf:
+  - $ref: qcom,rpm-common.yaml#
   - if:
       properties:
         compatible:

-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 1/9] dt-bindings: interconnect: qcom,msm8974: drop bus clocks
From: Dmitry Baryshkov @ 2026-03-24  0:10 UTC (permalink / raw)
  To: Georgi Djakov, Konrad Dybcio, Bjorn Andersson, Luca Weiss,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Brian Masney
  Cc: linux-arm-msm, linux-pm, devicetree, linux-kernel
In-Reply-To: <20260324-msm8974-icc-v2-0-527280043ad8@oss.qualcomm.com>

Remove the wrong internal RPM bus clock representation that we've been
carrying for years. They are an internal part of the interconnect
fabric. They are not exported by any device and are not supposed to be
used.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 .../bindings/interconnect/qcom,msm8974.yaml         | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml
index 95ce25ce1f7d..89a694501d8c 100644
--- a/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml
+++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml
@@ -32,22 +32,32 @@ properties:
   clock-names:
     items:
       - const: bus
-      - const: bus_a
 
   clocks:
     items:
       - description: Bus Clock
-      - description: Bus A Clock
 
 required:
   - compatible
   - reg
   - '#interconnect-cells'
-  - clock-names
-  - clocks
 
 additionalProperties: false
 
+allOf:
+  - if:
+      properties:
+        compatible:
+          const: qcom,msm8974-mmssnoc
+    then:
+      required:
+        - clocks
+        - clock-names
+    else:
+      properties:
+        clocks: false
+        clock-names: false
+
 examples:
   - |
     #include <dt-bindings/clock/qcom,rpmcc.h>
@@ -56,7 +66,4 @@ examples:
         reg = <0xfc380000 0x6a000>;
         compatible = "qcom,msm8974-bimc";
         #interconnect-cells = <1>;
-        clock-names = "bus", "bus_a";
-        clocks = <&rpmcc RPM_SMD_BIMC_CLK>,
-                 <&rpmcc RPM_SMD_BIMC_A_CLK>;
     };

-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 0/9] interconnect: qcom: let MSM8974 interconnect work again
From: Dmitry Baryshkov @ 2026-03-24  0:10 UTC (permalink / raw)
  To: Georgi Djakov, Konrad Dybcio, Bjorn Andersson, Luca Weiss,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Brian Masney
  Cc: linux-arm-msm, linux-pm, devicetree, linux-kernel, Brian Masney,
	Konrad Dybcio

Commit d6edc31f3a68 ("clk: qcom: smd-rpm: Separate out interconnect bus
clocks") moved control over several RPM resources from the clk-smd-rpm
driver to the icc-rpm.c interconnect helpers. Most of the platforms were
fixed before that commit or shortly after. However the MSM8974 was left
as a foster child in broken state. Fix the loose ends and reenable
interconnects on that platform.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
Changes in v2:
- Swapped order of clocks and clock-names properties (Konrad)
- Corrected the ocmem comment regarding core clock (Konrad)
- Link to v1: https://lore.kernel.org/r/20260323-msm8974-icc-v1-0-7892b8d5f2ea@oss.qualcomm.com

---
Dmitry Baryshkov (9):
      dt-bindings: interconnect: qcom,msm8974: drop bus clocks
      dt-bindings: interconnect: qcom,msm8974: use qcom,rpm-common
      interconnect: qcom: drop unused is_on flag
      interconnect: qcom: icc-rpm: allow overwriting get_bw callback
      interconnect: qcom: define OCMEM bus resource
      interconnect: qcom: let platforms declare their bugginess
      interconnect: qcom: msm8974: switch to the main icc-rpm driver
      interconnect: qcom: msm8974: expand DEFINE_QNODE macros
      ARM: dts: qcom: msm8974: Drop RPM bus clocks

 .../bindings/interconnect/qcom,msm8974.yaml        |   28 +-
 arch/arm/boot/dts/qcom/qcom-msm8974.dtsi           |   21 +-
 drivers/interconnect/qcom/icc-rpm-clocks.c         |    6 +
 drivers/interconnect/qcom/icc-rpm.c                |   18 +-
 drivers/interconnect/qcom/icc-rpm.h                |    7 +-
 drivers/interconnect/qcom/msm8974.c                | 1637 +++++++++++++++-----
 6 files changed, 1274 insertions(+), 443 deletions(-)
---
base-commit: 8e5a478b6d6a5bb0a3d52147862b15e4d826af19
change-id: 20260322-msm8974-icc-0ac4c28e139a

Best regards,
--  
With best wishes
Dmitry


^ permalink raw reply

* Re: [RFC] power/hibernate: TPM2 image encryption to allow hibernation under lockdown
From: Matthew Garrett @ 2026-03-23 23:39 UTC (permalink / raw)
  To: Jhon Taylor Forbes Cañizares
  Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org,
	keyrings@vger.kernel.org, jarkko@kernel.org, ebiggers@kernel.org,
	rafael@kernel.org
In-Reply-To: <IA1PR14MB62243E515C24AE8BF40E36BCB14BA@IA1PR14MB6224.namprd14.prod.outlook.com>

Hey Jhon! Thank you very much for the patch - I haven't fully reviewed 
it yet, but I think the configurable PCR approach introduces a risk that 
I'd need to think about closely before saying that this is going to 
work.

This patchset is actually a complicated one for LLM assistance, because 
there's a bunch of security assumptions that aren't very well documented 
in the code. I think what would be helpful (both for any attempt at 
further LLM work and also just for ensuring that the design is clear and 
everyone agrees it'll work) would be for me to write a clear design 
requirements document and share that. I was planning on doing that in 
the near future and working on a new set of patches based on that.

I'm not currently using LLMs to write kernel code, but I'm interested to 
see what the impact of having a well-defined problem statement is on the 
output here. If you're interested, I'd very much like to have you try 
this experiment again once that design is agreed on and we'll see how 
close the implementations look? It feels like an interesting experiment.

On Mon, Mar 23, 2026 at 04:57:17AM +0000, Jhon Taylor Forbes Cañizares wrote:
> ________________________________
> Hi,
> My name is Jhon Taylor Forbes Cañizares, I'm a 16-year-old technology enthusiast from Curaçao. I'm not a professional developer — I'm someone who loves technology, enjoys experimenting with Linux, and likes everything to work just right.
> I should mention that English is not my language — my native language is Spanish. I used a translator and Claude's help to write this email, so I apologize in advance if something doesn't read naturally.
> I'm also naturally introverted and honestly quite nervous about sending this. I was doubtful for a while about whether it was appropriate for someone like me to propose something like this. But I decided to go ahead because I genuinely care about this problem and wanted to try to contribute something, even if small.
> For a long time, I wanted hibernation working alongside lockdown=confidentiality on my own system. While researching why it's blocked, I found Matthew Garrett's v5 patch series and read through the upstream review feedback explaining why it stalled. I had the idea of trying to address those review concerns and create an improved proposal — and I asked Claude (Anthropic's AI assistant) to help me understand the kernel internals, explain how a solution could work, and write the actual code based on my direction.
> I want to be fully transparent: the idea and the goal were mine, but Claude did the technical heavy lifting of analyzing the feedback, designing the approach, and writing the implementation. I'm not a developer and I couldn't have written this alone.
> The proposal attempts to address each piece of review feedback from v5:
> 
>   *   Jarkko Sakkinen: Replaced generic tpm_pcr_reset() with a restricted snapenc_pcr_reset() only callable from hibernate context
>   *   Eric Biggers: TPM sessions now use TPM2_SE_HMAC with parameter encryption — key material is encrypted on the TPM bus
>   *   James Bottomley: PCR index is now configurable via hibernate_tpm_pcr=N cmdline and verified to be zero at boot before use
> 
> It also adds a Rust abstraction layer using the type state pattern to enforce correct PCR state transitions at compile time, and RAII session handling to guarantee TPM session cleanup on all error paths.
> I know you've probably thought about all of this before, and I know this proposal likely has gaps — some TPM API calls may need adjustment to match current kernel interfaces. I'm not presenting this as a finished patch. I just wanted to contribute something to a problem I care about, and I hope it can at least be useful as a starting point or a conversation.
> Attached:
> 
>   *   snapenc.c — main C implementation
>   *   snapenc.h — interface header
>   *   hibernate_pcr.rs — Rust abstraction layer
>   *   Kconfig.patch — new config option
>   *   0001-hibernate-tpm2-encryption.patch — diff against current kernel
> 
> Any feedback is very welcome.
> Signed-off-by: Jhon Taylor Forbes Cañizares hermanojhonforbes@hotmail.com
> 

> From: [v6 revision]
> Subject: [PATCH v6 1/4] power/hibernate: Add TPM2 image encryption to allow
>          coexistence with lockdown=confidentiality
> 
> This patch series implements hibernate image encryption using TPM2 PCR
> sealing, addressing the core reason lockdown=confidentiality blocks
> hibernation: the kernel cannot trust the image on disk hasn't been tampered.
> 
> With this series:
>  - The hibernate image is encrypted with AES-256-GCM
>  - The key is sealed to TPM2 PCR23 (or configurable via cmdline)
>  - Resume fails if the image has been tampered (GCM tag mismatch)
>  - Resume fails if PCR23 state doesn't match (TPM policy failure)
>  - lockdown=confidentiality no longer needs to block hibernation when
>    CONFIG_HIBERNATION_ENCRYPTION=y
> 
> Changes from v5 (addressing upstream review feedback):
>  - [Jarkko Sakkinen] Replaced generic tpm_pcr_reset() API with a
>    restricted snapenc_pcr_reset() only callable from snapenc.c
>  - [Eric Biggers] TPM sessions now use TPM2_SE_HMAC with parameter
>    encryption instead of plain TPM2_SE_POLICY — key material is
>    encrypted on the TPM bus
>  - [James Bottomley] PCR index is now configurable via kernel cmdline
>    (hibernate_tpm_pcr=N, default 23) and verified to be zero at boot
>    before use; if not zero, a clear error is emitted
>  - Added Rust abstraction layer (rust/kernel/tpm/hibernate_pcr.rs) using
>    typestate pattern to enforce correct PCR state transitions at
>    compile time
>  - Added RAII TpmHmacSession in Rust to guarantee session flush on all
>    error paths
> 
> ---
> 
> diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
> --- a/security/lockdown/lockdown.c
> +++ b/security/lockdown/lockdown.c
> @@ -36,7 +36,12 @@ static const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
>  int security_lock_kernel_down(const char *where, enum lockdown_reason what)
>  {
> -	if (kernel_locked_down >= what) {
> +	/*
> +	 * Allow hibernation if image encryption is active.
> +	 * The image is encrypted with AES-256-GCM and the key is sealed
> +	 * to TPM2 PCR23, providing equivalent security guarantees.
> +	 */
> +	if (what == LOCKDOWN_HIBERNATE &&
> +	    IS_ENABLED(CONFIG_HIBERNATION_ENCRYPTION) &&
> +	    snapenc_is_active()) {
> +		return 0;
> +	}
> +
> +	if (kernel_locked_down >= what) {
>  		pr_notice("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
>  			  where, lockdown_reasons[what]);
>  		return -EPERM;
> 
> ---
> 
> diff --git a/kernel/power/Makefile b/kernel/power/Makefile
> --- a/kernel/power/Makefile
> +++ b/kernel/power/Makefile
> @@ -16,3 +16,4 @@ obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
> +obj-$(CONFIG_HIBERNATION_ENCRYPTION)	+= snapenc.o
> 
> ---
> 
> diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
> --- a/kernel/power/hibernate.c
> +++ b/kernel/power/hibernate.c
> @@ -702,6 +702,12 @@ int hibernate(void)
> +#ifdef CONFIG_HIBERNATION_ENCRYPTION
> +	error = snapenc_hibernate_begin();
> +	if (error) {
> +		pr_err("hibernate: failed to initialize image encryption: %d\n",
> +		       error);
> +		goto Unlock;
> +	}
> +#endif
> +
>  	error = freeze_processes();
> 
> @@ -750,6 +756,10 @@ int hibernate(void)
> +#ifdef CONFIG_HIBERNATION_ENCRYPTION
> +	snapenc_hibernate_end();
> +#endif
> 
>  Resume_devices:
> 
> ---
> 
> diff --git a/kernel/power/swap.c b/kernel/power/swap.c
> --- a/kernel/power/swap.c
> +++ b/kernel/power/swap.c
> @@ -501,6 +501,15 @@ static int save_image(struct swap_map_handle *handle,
> +#ifdef CONFIG_HIBERNATION_ENCRYPTION
> +		/* Encrypt chunk before writing to swap */
> +		ret = snapenc_encrypt_chunk(hibernate_enc_ctx,
> +					    buffer, ret,
> +					    encrypted_buf, chunk_idx++);
> +		if (ret < 0)
> +			break;
> +		/* Write encrypted_buf instead of buffer */
> +#endif
> 
> @@ -601,6 +610,15 @@ static int load_image(struct swap_map_handle *handle,
> +#ifdef CONFIG_HIBERNATION_ENCRYPTION
> +		/* Decrypt chunk after reading from swap */
> +		ret = snapenc_decrypt_chunk(hibernate_enc_ctx,
> +					    buffer, ret,
> +					    decrypted_buf, chunk_idx++);
> +		if (ret < 0) {
> +			pr_crit("hibernate: image authentication failed — "
> +				"aborting resume\n");
> +			break;
> +		}
> +#endif

> // SPDX-License-Identifier: GPL-2.0-only
> //
> // rust/kernel/tpm/hibernate_pcr.rs
> //
> // Rust abstraction over the C snapenc PCR operations.
> //
> // The core hibernate encryption logic lives in C (snapenc.c) because
> // the TPM and crypto subsystems don't yet have full Rust bindings.
> // This module provides a safe Rust wrapper for the parts that can be
> // expressed in Rust today: the PCR state machine and policy builder.
> //
> // Design principles:
> //  - Ownership types enforce the correct PCR state transitions at
> //    compile time — you cannot call seal() without first extending,
> //    and you cannot extend twice without resetting in between.
> //  - The TPM session is an RAII type: dropping it always calls
> //    tpm2_end_auth_session(), preventing session leaks.
> //  - All key material is wrapped in ZeroOnDrop to ensure it's zeroed
> //    when it goes out of scope, even on error paths.
> 
> use kernel::prelude::*;
> use kernel::tpm::{Chip, PcrIndex, TpmAlgorithm};
> use core::ops::Drop;
> 
> /// SHA-256 digest size in bytes.
> const SHA256_SIZE: usize = 32;
> 
> /// AES-256-GCM key size in bytes.
> const KEY_SIZE: usize = 32;
> 
> /// The well-known magic value extended into PCR before sealing.
> /// SHA-256("linux-hibernate-v1") — same value as in snapenc.c.
> const HIBERNATE_EXTEND_VALUE: [u8; SHA256_SIZE] = [
>     0x7a, 0x3f, 0x8c, 0x12, 0xe4, 0x56, 0xb9, 0x01,
>     0xcd, 0x78, 0x2e, 0x5a, 0x90, 0x11, 0xf3, 0x44,
>     0x6b, 0x82, 0x7d, 0x3e, 0x19, 0xc5, 0xa0, 0xfb,
>     0x55, 0x24, 0x8e, 0x71, 0x92, 0xdd, 0x4c, 0x0a,
> ];
> 
> // ---------------------------------------------------------------------------
> //  ZeroOnDrop: key material that is zeroed when dropped
> // ---------------------------------------------------------------------------
> 
> /// A buffer containing sensitive key material.
> /// Guaranteed to be zeroed on drop, even if the owner panics.
> pub struct ZeroOnDrop<const N: usize> {
>     data: [u8; N],
> }
> 
> impl<const N: usize> ZeroOnDrop<N> {
>     /// Create a new zeroed buffer.
>     pub fn new() -> Self {
>         Self { data: [0u8; N] }
>     }
> 
>     /// Access the underlying bytes.
>     pub fn as_bytes(&self) -> &[u8] {
>         &self.data
>     }
> 
>     /// Access mutable bytes for filling.
>     pub fn as_bytes_mut(&mut self) -> &mut [u8] {
>         &mut self.data
>     }
> }
> 
> impl<const N: usize> Drop for ZeroOnDrop<N> {
>     fn drop(&mut self) {
>         // Volatile write to prevent the compiler from optimizing this out.
>         for byte in self.data.iter_mut() {
>             // SAFETY: We own this memory and are writing valid u8 values.
>             unsafe { core::ptr::write_volatile(byte, 0u8) };
>         }
>     }
> }
> 
> // ---------------------------------------------------------------------------
> //  PCR State Machine
> //  Typestate pattern: the PCR's current state is encoded in the type,
> //  so invalid transitions are caught at compile time.
> // ---------------------------------------------------------------------------
> 
> /// Marker: PCR is at its baseline zero value.
> pub struct PcrZero;
> 
> /// Marker: PCR has been extended with the hibernate magic value.
> pub struct PcrExtended;
> 
> /// Represents ownership of the hibernate PCR in a particular state.
> ///
> /// The typestate parameter `S` encodes whether the PCR is currently
> /// zero (`PcrZero`) or extended (`PcrExtended`). This prevents:
> ///  - Calling `extend()` twice without a `reset()` in between
> ///  - Calling `reset()` when already zero
> ///  - Calling `seal()` before `extend()`
> pub struct HibernatePcr<S> {
>     chip: Chip,
>     pcr_index: PcrIndex,
>     _state: core::marker::PhantomData<S>,
> }
> 
> impl HibernatePcr<PcrZero> {
>     /// Acquire the hibernate PCR. Verifies it is zero before proceeding.
>     ///
>     /// Returns `Err(EBUSY)` if the PCR is already in use by another agent.
>     pub fn acquire(chip: Chip, pcr_index: PcrIndex) -> Result<Self> {
>         let digest = chip.pcr_read(pcr_index, TpmAlgorithm::Sha256)?;
> 
>         if digest.iter().any(|&b| b != 0) {
>             pr_err!(
>                 "snapenc: PCR{} is not zero — already in use. \
>                  Use hibernate_tpm_pcr=N to select a free PCR.\n",
>                 u32::from(pcr_index)
>             );
>             return Err(EBUSY);
>         }
> 
>         Ok(Self {
>             chip,
>             pcr_index,
>             _state: core::marker::PhantomData,
>         })
>     }
> 
>     /// Extend the PCR with the hibernate magic value.
>     ///
>     /// Consumes `self` (PcrZero) and returns `HibernatePcr<PcrExtended>`.
>     /// You cannot call this again until you reset.
>     pub fn extend(self) -> Result<HibernatePcr<PcrExtended>> {
>         self.chip.pcr_extend(
>             self.pcr_index,
>             TpmAlgorithm::Sha256,
>             &HIBERNATE_EXTEND_VALUE,
>         )?;
> 
>         Ok(HibernatePcr {
>             chip: self.chip,
>             pcr_index: self.pcr_index,
>             _state: core::marker::PhantomData,
>         })
>     }
> }
> 
> impl HibernatePcr<PcrExtended> {
>     /// Reset the PCR back to zero.
>     ///
>     /// Consumes `self` (PcrExtended) and returns `HibernatePcr<PcrZero>`.
>     pub fn reset(self) -> Result<HibernatePcr<PcrZero>> {
>         self.chip.pcr_reset(self.pcr_index)?;
> 
>         Ok(HibernatePcr {
>             chip: self.chip,
>             pcr_index: self.pcr_index,
>             _state: core::marker::PhantomData,
>         })
>     }
> 
>     /// Seal a key to the TPM, bound to the current PCR state.
>     ///
>     /// Only callable when the PCR is in the extended state.
>     /// Returns a `SealedKey` that can be stored alongside the image.
>     pub fn seal_key(&self, key: &ZeroOnDrop<KEY_SIZE>) -> Result<SealedKey> {
>         // Open an HMAC session with parameter encryption.
>         // This is the critical change vs v5: HMAC session (not just policy)
>         // means the key material is encrypted on the TPM bus.
>         let session = TpmHmacSession::begin(&self.chip, TpmAlgorithm::Sha256)?;
> 
>         // Bind the session to our PCR policy
>         session.policy_pcr(self.pcr_index)?;
> 
>         // Create the sealed object
>         let blob = self.chip.seal(key.as_bytes(), &session)?;
> 
>         Ok(SealedKey {
>             blob,
>             pcr_index: self.pcr_index,
>         })
>     }
> 
>     /// Unseal a previously sealed key from the TPM.
>     ///
>     /// Only callable when the PCR is in the extended state.
>     /// Returns `Err(EACCES)` if the PCR policy does not match.
>     pub fn unseal_key(&self, sealed: &SealedKey) -> Result<ZeroOnDrop<KEY_SIZE>> {
>         let session = TpmHmacSession::begin(&self.chip, TpmAlgorithm::Sha256)?;
>         session.policy_pcr(self.pcr_index)?;
> 
>         let mut key = ZeroOnDrop::<KEY_SIZE>::new();
>         self.chip
>             .unseal(&sealed.blob, &session, key.as_bytes_mut())
>             .map_err(|e| {
>                 if e == EPERM {
>                     // TPM returned policy failure — translate to EACCES
>                     pr_crit!(
>                         "snapenc: TPM REFUSED to release key. \
>                          PCR{} policy mismatch. Resume aborted.\n",
>                         u32::from(self.pcr_index)
>                     );
>                     EACCES
>                 } else {
>                     e
>                 }
>             })?;
> 
>         Ok(key)
>     }
> }
> 
> // ---------------------------------------------------------------------------
> //  RAII TPM HMAC Session
> // ---------------------------------------------------------------------------
> 
> /// An active TPM2 HMAC session with parameter encryption.
> ///
> /// The session is automatically flushed on drop — no session leaks.
> /// This is an improvement over the C code where early returns could
> /// theoretically skip the flush if error handling wasn't careful.
> pub struct TpmHmacSession<'a> {
>     chip: &'a Chip,
>     handle: u32,
> }
> 
> impl<'a> TpmHmacSession<'a> {
>     /// Start a new HMAC session with parameter encryption enabled.
>     ///
>     /// `TPM2_SE_HMAC` + parameter encryption = authenticated and
>     /// encrypted communication between kernel and TPM.
>     pub fn begin(chip: &'a Chip, hash_alg: TpmAlgorithm) -> Result<Self> {
>         let handle = chip.start_auth_session_hmac(hash_alg)?;
>         Ok(Self { chip, handle })
>     }
> 
>     /// Add a PCR policy binding to this session.
>     pub fn policy_pcr(&self, pcr_index: PcrIndex) -> Result<()> {
>         self.chip.policy_pcr(self.handle, pcr_index)
>     }
> }
> 
> impl<'a> Drop for TpmHmacSession<'a> {
>     fn drop(&mut self) {
>         // Always flush the session, even on error paths.
>         // Ignoring the return value here is intentional — we're in drop().
>         let _ = self.chip.flush_context(self.handle);
>     }
> }
> 
> // ---------------------------------------------------------------------------
> //  SealedKey: opaque blob stored in the hibernate image header
> // ---------------------------------------------------------------------------
> 
> /// A key sealed to the TPM, bound to a PCR policy.
> ///
> /// This is stored in the hibernate image header alongside the
> /// encrypted image data. On resume, it's passed to `unseal_key()`.
> pub struct SealedKey {
>     /// The TPM2B_PRIVATE + TPM2B_PUBLIC blobs.
>     pub blob: Vec<u8>,
>     /// Which PCR the key is bound to.
>     pub pcr_index: PcrIndex,
> }
> 
> // ---------------------------------------------------------------------------
> //  High-level hibernate / resume orchestration
> // ---------------------------------------------------------------------------
> 
> /// Orchestrate the full hibernate key-sealing sequence.
> ///
> /// Returns the sealed key to be stored in the image header,
> /// and resets PCR23 to zero before returning.
> ///
> /// ```
> /// PCR23: 0 → extend → [seal key] → reset → 0
> ///                          ↓
> ///                     SealedKey (stored in image header)
> /// ```
> pub fn hibernate_seal(chip: Chip, pcr_index: PcrIndex,
>                       key: &ZeroOnDrop<KEY_SIZE>) -> Result<SealedKey> {
>     // Acquire PCR (verifies it's zero)
>     let pcr = HibernatePcr::<PcrZero>::acquire(chip, pcr_index)?;
> 
>     // Extend → seal
>     let pcr = pcr.extend()?;
>     let sealed = pcr.seal_key(key)?;
> 
>     // Reset — typestate ensures we can only reset from Extended state
>     let _pcr_zero = pcr.reset()?;
>     // _pcr_zero is dropped here, PCR is back to zero
> 
>     Ok(sealed)
> }
> 
> /// Orchestrate the full resume key-unsealing sequence.
> ///
> /// Returns the plaintext key (as ZeroOnDrop) so the caller can
> /// re-initialize AES-GCM for decryption.
> ///
> /// ```
> /// PCR23: 0 → extend → [unseal key] → reset → 0
> ///                           ↓
> ///                      ZeroOnDrop<KEY_SIZE>
> /// ```
> pub fn resume_unseal(chip: Chip, pcr_index: PcrIndex,
>                      sealed: &SealedKey) -> Result<ZeroOnDrop<KEY_SIZE>> {
>     let pcr = HibernatePcr::<PcrZero>::acquire(chip, pcr_index)?;
>     let pcr = pcr.extend()?;
> 
>     // If this fails (EACCES), the PCR policy didn't match.
>     // We still need to reset PCR23 before returning the error.
>     let key_result = pcr.unseal_key(sealed);
> 
>     // Reset regardless of whether unseal succeeded
>     let _ = pcr.reset();
> 
>     key_result
> }

> # Hibernate + Lockdown: TPM2 Image Encryption (v6)
> 
> ## Problema que resuelve
> 
> `lockdown=confidentiality` bloquea la hibernación porque el kernel no puede
> confiar en que la imagen en disco no ha sido manipulada. Un atacante con
> acceso físico podría reemplazar la imagen con código malicioso, y el kernel
> la ejecutaría al hacer resume.
> 
> Este patch resuelve eso cifrando y autenticando la imagen con AES-256-GCM,
> usando una clave sellada al TPM2 via PCR23.
> 
> ## Cómo funciona
> 
> ### Al hibernar:
> ```
> 1. Verificar que PCR23 == 0 (si no, error claro)
> 2. Extender PCR23 con valor conocido H("linux-hibernate-v1")
> 3. Generar clave AES-256 aleatoria
> 4. Sellar la clave al TPM (política: PCR23 debe == H("linux-hibernate-v1"))
>    → sesión HMAC autenticada, clave cifrada en el bus TPM
> 5. Resetear PCR23 a 0
> 6. Cifrar imagen chunk por chunk con AES-256-GCM
>    → cada chunk tiene IV único y AAD con índice (evita reordenamiento)
>    → tag GCM de 16 bytes appended a cada chunk
> ```
> 
> ### Al resumir:
> ```
> 1. Extender PCR23 con el mismo valor
> 2. Pedir al TPM que libere la clave
>    → si PCR23 no coincide: TPM rechaza, resume abortado (EACCES)
> 3. Descifrar y verificar cada chunk
>    → si tag GCM falla: imagen manipulada, resume abortado (EBADMSG)
> 4. Resetear PCR23 a 0
> 5. Lockdown satisfecho ✓
> ```
> 
> ## Archivos del patch
> 
> ```
> kernel/power/snapenc.c          — Implementación principal (C)
> kernel/power/snapenc.h          — Header / interface
> kernel/power/Kconfig.patch      — Nueva opción CONFIG_HIBERNATION_ENCRYPTION
> rust/kernel/tpm/hibernate_pcr.rs — Capa Rust con typestate + RAII
> 0001-hibernate-tpm2-encryption.patch — Diff contra kernel actual
> ```
> 
> ## Cambios vs v5 (respuestas a críticas upstream)
> 
> | Crítica | Autor | Solución en v6 |
> |---------|-------|----------------|
> | `tpm_pcr_reset()` API demasiado genérica y peligrosa | Jarkko Sakkinen | Reemplazada por `snapenc_pcr_reset()` restringida solo a este contexto |
> | Sesiones TPM sin cifrado de parámetros | Eric Biggers | Ahora usa `TPM2_SE_HMAC` + `TPM2_ENC_PARAM_YES` — clave cifrada en el bus |
> | PCR23 podría estar en uso por otro agente | James Bottomley | Verificación al boot + parámetro `hibernate_tpm_pcr=N` configurable |
> | Sin mantenedor activo | (organizacional) | — |
> 
> ## Innovaciones del v6 no presentes en v5
> 
> ### 1. Rust typestate para transiciones de PCR
> El código Rust en `hibernate_pcr.rs` usa el patrón typestate:
> ```rust
> // NO compila — no puedes sellar sin antes extender:
> let pcr = HibernatePcr::<PcrZero>::acquire(chip, index)?;
> pcr.seal_key(&key)  // ERROR DE COMPILACIÓN ✓
> 
> // Correcto:
> let pcr = pcr.extend()?;     // PcrZero → PcrExtended
> let sealed = pcr.seal_key(&key)?;
> let _ = pcr.reset()?;        // PcrExtended → PcrZero
> ```
> Las transiciones incorrectas de PCR son imposibles en tiempo de compilación.
> 
> ### 2. RAII TpmHmacSession
> ```rust
> // La sesión se flushea SIEMPRE al salir del scope, incluso en error paths.
> // En v5 (C) era posible saltarse el flush con returns tempranos.
> let session = TpmHmacSession::begin(&chip, TpmAlgorithm::Sha256)?;
> // ... si hay error aquí, drop() cierra la sesión automáticamente
> ```
> 
> ### 3. ZeroOnDrop para material de claves
> ```rust
> let key = ZeroOnDrop::<32>::new();
> // Al salir del scope: memoria zeroizada con volatile write
> // El compilador no puede optimizar el zeroing
> ```
> 
> ### 4. AAD con índice de chunk
> Cada chunk tiene su índice como Additional Authenticated Data:
> ```c
> put_unaligned_be64(chunk_idx, aad);
> // Evita ataques de reordenamiento: chunk 5 no puede usarse como chunk 3
> ```
> 
> ## Cómo enviar upstream
> 
> 1. Dirigir a: `linux-kernel@vger.kernel.org`
> 2. CC obligatorio:
>    - `linux-pm@vger.kernel.org` (power management)
>    - `keyrings@vger.kernel.org` (TPM/trusted keys)
>    - `jarkko@kernel.org` (TPM subsystem maintainer)
>    - `ebiggers@kernel.org` (crypto subsystem)
>    - `mjg59@srcf.ucam.org` (autor original del concepto)
> 3. Subject format: `[PATCH v6 N/4] power/hibernate: ...`
> 
> ## Parámetros de kernel
> 
> ```
> hibernate_tpm_pcr=N   Qué PCR usar (default: 23, válido: 16-23)
> ```
> 
> ## Dependencias de configuración
> 
> ```
> CONFIG_HIBERNATION=y
> CONFIG_HIBERNATION_ENCRYPTION=y
> CONFIG_TCG_TPM=y
> CONFIG_TCG_TPM2=y
> CONFIG_CRYPTO_AES=y
> CONFIG_CRYPTO_GCM=y
> CONFIG_CRYPTO_SHA256=y
> CONFIG_TRUSTED_KEYS=y
> ```

> // SPDX-License-Identifier: GPL-2.0-only
> /*
>  * kernel/power/snapenc.c
>  *
>  * Hibernate image encryption and authentication using TPM2 + PCR23.
>  *
>  * This module allows hibernation to coexist with kernel lockdown by:
>  *  1. Sealing an AES-256-GCM key to TPM2 PCR23 at hibernate time
>  *  2. Encrypting and authenticating the hibernate image with that key
>  *  3. Unsealing and verifying on resume — if PCR23 doesn't match,
>  *     the TPM refuses to release the key and resume is aborted.
>  *
>  * This addresses the concern raised by lockdown=confidentiality that
>  * an attacker could replace the hibernate image on disk. With this
>  * code, tampering with the image on disk causes authentication failure
>  * (GCM tag mismatch) and tampering with PCR23 causes TPM policy failure.
>  *
>  * Based on the design proposed by Matthew Garrett and Evan Green.
>  * Revised (v6) to address upstream review concerns:
>  *   - Restricted PCR reset API (not generic)
>  *   - HMAC-authenticated TPM sessions (not plain policy sessions)
>  *   - Configurable PCR via kernel cmdline (hibernate_tpm_pcr=N)
>  *   - Boot-time verification that the chosen PCR is zero
>  *
>  * Authors:
>  *   Matthew Garrett <mjg59@srcf.ucam.org> (original concept)
>  *   Evan Green <evgreen@chromium.org> (v5 implementation)
>  *   [v6 revision addressing upstream review feedback]
>  */
> 
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/init.h>
> #include <linux/slab.h>
> #include <linux/random.h>
> #include <linux/crypto.h>
> #include <linux/scatterlist.h>
> #include <crypto/aead.h>
> #include <crypto/gcm.h>
> #include <crypto/hash.h>
> #include <linux/tpm.h>
> #include <linux/suspend.h>
> #include <linux/security.h>
> #include "power.h"
> #include "snapenc.h"
> 
> /* Default PCR to use for hibernate sealing. Can be overridden via cmdline. */
> #define HIBERNATE_PCR_DEFAULT   23
> #define HIBERNATE_PCR_MIN       16   /* PCRs 0-15 are platform-managed */
> #define HIBERNATE_PCR_MAX       23
> 
> /*
>  * Magic value extended into the PCR before sealing.
>  * Must be a stable, well-known value — not secret.
>  * SHA-256("linux-hibernate-v1")
>  */
> static const u8 hibernate_pcr_extend_value[SHA256_DIGEST_SIZE] = {
> 	0x7a, 0x3f, 0x8c, 0x12, 0xe4, 0x56, 0xb9, 0x01,
> 	0xcd, 0x78, 0x2e, 0x5a, 0x90, 0x11, 0xf3, 0x44,
> 	0x6b, 0x82, 0x7d, 0x3e, 0x19, 0xc5, 0xa0, 0xfb,
> 	0x55, 0x24, 0x8e, 0x71, 0x92, 0xdd, 0x4c, 0x0a,
> };
> 
> /* AES-256-GCM parameters */
> #define SNAPENC_KEY_SIZE    32  /* 256 bits */
> #define SNAPENC_IV_SIZE     12  /* 96 bits — GCM standard */
> #define SNAPENC_TAG_SIZE    16  /* 128 bits — GCM authentication tag */
> #define SNAPENC_CHUNK_SIZE  (1 << 20)  /* 1 MiB per chunk */
> 
> /* Configurable PCR number via kernel cmdline: hibernate_tpm_pcr=N */
> static int hibernate_tpm_pcr = HIBERNATE_PCR_DEFAULT;
> 
> static int __init hibernate_tpm_pcr_setup(char *str)
> {
> 	int pcr;
> 
> 	if (kstrtoint(str, 10, &pcr))
> 		return -EINVAL;
> 
> 	if (pcr < HIBERNATE_PCR_MIN || pcr > HIBERNATE_PCR_MAX) {
> 		pr_err("snapenc: hibernate_tpm_pcr=%d out of range [%d-%d], "
> 		       "using default %d\n",
> 		       pcr, HIBERNATE_PCR_MIN, HIBERNATE_PCR_MAX,
> 		       HIBERNATE_PCR_DEFAULT);
> 		return -ERANGE;
> 	}
> 
> 	hibernate_tpm_pcr = pcr;
> 	pr_info("snapenc: using PCR%d for hibernate sealing\n", hibernate_tpm_pcr);
> 	return 0;
> }
> early_param("hibernate_tpm_pcr", hibernate_tpm_pcr_setup);
> 
> /* ------------------------------------------------------------------ */
> /*  TPM helpers — PCR operations restricted to hibernate context only  */
> /* ------------------------------------------------------------------ */
> 
> /**
>  * snapenc_pcr_is_zero - Verify that our chosen PCR is zero at boot.
>  *
>  * Called once during module init. If the PCR is not zero, another agent
>  * (firmware, security software) is using it. We refuse to proceed rather
>  * than silently clobbering it.
>  *
>  * Returns 0 if PCR is zero, -EBUSY if in use, negative on error.
>  */
> static int snapenc_pcr_is_zero(struct tpm_chip *chip)
> {
> 	u8 digest[SHA256_DIGEST_SIZE];
> 	int rc;
> 	bool all_zero = true;
> 	int i;
> 
> 	rc = tpm_pcr_read(chip, hibernate_tpm_pcr,
> 			  (struct tpm_digest *)digest);
> 	if (rc) {
> 		pr_err("snapenc: failed to read PCR%d: %d\n",
> 		       hibernate_tpm_pcr, rc);
> 		return rc;
> 	}
> 
> 	for (i = 0; i < SHA256_DIGEST_SIZE; i++) {
> 		if (digest[i] != 0) {
> 			all_zero = false;
> 			break;
> 		}
> 	}
> 
> 	if (!all_zero) {
> 		pr_err("snapenc: PCR%d is not zero — already in use by another "
> 		       "agent. Use hibernate_tpm_pcr=N to select a free PCR.\n",
> 		       hibernate_tpm_pcr);
> 		return -EBUSY;
> 	}
> 
> 	return 0;
> }
> 
> /**
>  * snapenc_pcr_extend - Extend PCR with the well-known hibernate magic value.
>  *
>  * This is NOT a generic PCR extend — it only accepts our fixed magic value
>  * and only operates on hibernate_tpm_pcr. It cannot be called from outside
>  * this file.
>  *
>  * Returns 0 on success, negative on error.
>  */
> static int snapenc_pcr_extend(struct tpm_chip *chip)
> {
> 	struct tpm_digest digest;
> 
> 	digest.alg_id = TPM_ALG_SHA256;
> 	memcpy(digest.digest, hibernate_pcr_extend_value, SHA256_DIGEST_SIZE);
> 
> 	return tpm_pcr_extend(chip, hibernate_tpm_pcr, &digest);
> }
> 
> /**
>  * snapenc_pcr_reset - Reset our hibernate PCR back to zero.
>  *
>  * Uses TPM2_PCR_Reset. Only valid for PCRs 16-23 (resettable range).
>  * Restricted to hibernate context — cannot be called from userspace or
>  * arbitrary kernel code.
>  *
>  * This replaces the generic tpm_pcr_reset() proposed in v5 which reviewers
>  * (Jarkko Sakkinen) considered too dangerous as a general API.
>  *
>  * Returns 0 on success, negative on error.
>  */
> static int snapenc_pcr_reset(struct tpm_chip *chip)
> {
> 	/* Compile-time assertion: only resettable PCRs */
> 	BUILD_BUG_ON(HIBERNATE_PCR_DEFAULT < 16);
> 
> 	return tpm2_pcr_reset(chip, hibernate_tpm_pcr);
> }
> 
> /* ------------------------------------------------------------------ */
> /*  TPM2 key sealing / unsealing with HMAC-authenticated sessions      */
> /* ------------------------------------------------------------------ */
> 
> /**
>  * struct snapenc_sealed_key - A key sealed to the TPM with a PCR policy.
>  *
>  * @blob:       The TPM2B_PRIVATE blob returned by TPM2_Create
>  * @blob_len:   Length of @blob
>  * @pub:        The TPM2B_PUBLIC blob
>  * @pub_len:    Length of @pub
>  * @pcr_index:  Which PCR was used in the policy
>  * @pcr_digest: Expected PCR value at time of sealing
>  */
> struct snapenc_sealed_key {
> 	u8  blob[512];
> 	u32 blob_len;
> 	u8  pub[512];
> 	u32 pub_len;
> 	u32 pcr_index;
> 	u8  pcr_digest[SHA256_DIGEST_SIZE];
> };
> 
> /**
>  * snapenc_seal_key - Seal a key to the TPM, bound to current PCR state.
>  *
>  * Uses TPM2_Create with a PCR policy. The session is an HMAC session
>  * (TPM2_SE_HMAC) with parameter encryption enabled, so that the key
>  * material never travels in plaintext between kernel and TPM.
>  *
>  * This addresses Eric Biggers' review concern about session security in v5.
>  *
>  * @chip:       TPM chip to use
>  * @key:        Plaintext key to seal (SNAPENC_KEY_SIZE bytes)
>  * @sealed:     Output: sealed key blob
>  *
>  * Returns 0 on success, negative on error.
>  */
> static int snapenc_seal_key(struct tpm_chip *chip,
> 			    const u8 *key,
> 			    struct snapenc_sealed_key *sealed)
> {
> 	struct tpm2_auth *auth;
> 	int rc;
> 
> 	/*
> 	 * Start an HMAC session with parameter encryption.
> 	 * TPM2_SE_HMAC ensures the session itself is authenticated;
> 	 * TPM2_ENC_PARAM_YES ensures key material is encrypted in transit.
> 	 *
> 	 * This is the key difference from v5 which used TPM2_SE_POLICY
> 	 * without parameter encryption, leaving the key visible on the
> 	 * TPM bus during the Create command.
> 	 */
> 	auth = tpm2_start_auth_session(chip,
> 				       TPM2_SE_HMAC,
> 				       TPM2_ALG_SHA256,
> 				       TPM2_ENC_PARAM_YES);
> 	if (IS_ERR(auth)) {
> 		pr_err("snapenc: failed to start auth session: %ld\n",
> 		       PTR_ERR(auth));
> 		return PTR_ERR(auth);
> 	}
> 
> 	/* Build PCR policy: seal to current value of hibernate_tpm_pcr */
> 	rc = tpm2_policy_pcr(auth, hibernate_tpm_pcr);
> 	if (rc) {
> 		pr_err("snapenc: failed to build PCR policy: %d\n", rc);
> 		goto out_flush_session;
> 	}
> 
> 	/* Create the sealed object */
> 	rc = tpm2_seal_trusted(chip, key, SNAPENC_KEY_SIZE,
> 			       auth,
> 			       sealed->blob, &sealed->blob_len,
> 			       sealed->pub,  &sealed->pub_len);
> 	if (rc) {
> 		pr_err("snapenc: TPM2_Create (seal) failed: %d\n", rc);
> 		goto out_flush_session;
> 	}
> 
> 	sealed->pcr_index = hibernate_tpm_pcr;
> 	memcpy(sealed->pcr_digest, hibernate_pcr_extend_value,
> 	       SHA256_DIGEST_SIZE);
> 
> out_flush_session:
> 	tpm2_end_auth_session(auth);
> 	return rc;
> }
> 
> /**
>  * snapenc_unseal_key - Unseal a previously sealed key from the TPM.
>  *
>  * The TPM will refuse to release the key if the current value of
>  * hibernate_tpm_pcr does not match the policy recorded at seal time.
>  * This is the core security guarantee: a tampered image cannot be
>  * resumed because the PCR state won't match.
>  *
>  * @chip:       TPM chip to use
>  * @sealed:     Sealed key blob (from snapenc_seal_key)
>  * @key:        Output: plaintext key (SNAPENC_KEY_SIZE bytes)
>  *
>  * Returns 0 on success, -EACCES if PCR policy fails, negative on error.
>  */
> static int snapenc_unseal_key(struct tpm_chip *chip,
> 			      const struct snapenc_sealed_key *sealed,
> 			      u8 *key)
> {
> 	struct tpm2_auth *auth;
> 	u32 key_len = SNAPENC_KEY_SIZE;
> 	int rc;
> 
> 	/* Same session type as seal: HMAC + parameter encryption */
> 	auth = tpm2_start_auth_session(chip,
> 				       TPM2_SE_HMAC,
> 				       TPM2_ALG_SHA256,
> 				       TPM2_ENC_PARAM_YES);
> 	if (IS_ERR(auth)) {
> 		pr_err("snapenc: failed to start auth session for unseal: %ld\n",
> 		       PTR_ERR(auth));
> 		return PTR_ERR(auth);
> 	}
> 
> 	rc = tpm2_policy_pcr(auth, sealed->pcr_index);
> 	if (rc) {
> 		pr_err("snapenc: PCR policy rebuild failed: %d\n", rc);
> 		goto out;
> 	}
> 
> 	rc = tpm2_unseal_trusted(chip,
> 				 sealed->blob, sealed->blob_len,
> 				 sealed->pub,  sealed->pub_len,
> 				 auth,
> 				 key, &key_len);
> 	if (rc) {
> 		/*
> 		 * TPM returns TPM2_RC_POLICY_FAIL when PCR doesn't match.
> 		 * Translate to -EACCES so callers can give a clear error.
> 		 */
> 		if (rc == TPM2_RC_POLICY_FAIL || rc == TPM2_RC_AUTH_FAIL)
> 			rc = -EACCES;
> 		pr_err("snapenc: TPM2_Unseal failed (PCR mismatch?): %d\n", rc);
> 	}
> 
> out:
> 	tpm2_end_auth_session(auth);
> 	return rc;
> }
> 
> /* ------------------------------------------------------------------ */
> /*  AES-256-GCM image encryption / decryption                         */
> /* ------------------------------------------------------------------ */
> 
> /**
>  * struct snapenc_ctx - Per-hibernate encryption context.
>  *
>  * Allocated at hibernate time, freed after resume or on error.
>  */
> struct snapenc_ctx {
> 	/* AES-256-GCM transform */
> 	struct crypto_aead      *tfm;
> 
> 	/* Random key, sealed to TPM at hibernate time */
> 	u8                       key[SNAPENC_KEY_SIZE];
> 	struct snapenc_sealed_key sealed;
> 
> 	/* Per-chunk IV: 96-bit, incremented for each chunk */
> 	u8                       iv[SNAPENC_IV_SIZE];
> 
> 	/* Temporary buffer for one chunk */
> 	u8                      *chunk_buf;
> 	size_t                   chunk_buf_size;
> 
> 	/* Accumulated SHA-256 over all encrypted chunks (extra integrity) */
> 	struct shash_desc       *image_hash;
> 	u8                       image_digest[SHA256_DIGEST_SIZE];
> };
> 
> static struct snapenc_ctx *hibernate_enc_ctx;
> 
> /**
>  * snapenc_ctx_alloc - Allocate and initialize an encryption context.
>  *
>  * Generates a fresh random key, sets up the AEAD transform, and
>  * prepares the per-image SHA-256 accumulator.
>  *
>  * Returns a new context on success, ERR_PTR on failure.
>  */
> static struct snapenc_ctx *snapenc_ctx_alloc(void)
> {
> 	struct snapenc_ctx *ctx;
> 	int rc;
> 
> 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> 	if (!ctx)
> 		return ERR_PTR(-ENOMEM);
> 
> 	/* Allocate chunk buffer */
> 	ctx->chunk_buf_size = SNAPENC_CHUNK_SIZE + SNAPENC_TAG_SIZE;
> 	ctx->chunk_buf = vmalloc(ctx->chunk_buf_size);
> 	if (!ctx->chunk_buf) {
> 		rc = -ENOMEM;
> 		goto err_free_ctx;
> 	}
> 
> 	/* Set up AES-256-GCM transform */
> 	ctx->tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
> 	if (IS_ERR(ctx->tfm)) {
> 		rc = PTR_ERR(ctx->tfm);
> 		pr_err("snapenc: failed to alloc AES-GCM: %d\n", rc);
> 		goto err_free_buf;
> 	}
> 
> 	rc = crypto_aead_setauthsize(ctx->tfm, SNAPENC_TAG_SIZE);
> 	if (rc) {
> 		pr_err("snapenc: failed to set tag size: %d\n", rc);
> 		goto err_free_tfm;
> 	}
> 
> 	/* Generate fresh random key for this hibernate image */
> 	get_random_bytes(ctx->key, SNAPENC_KEY_SIZE);
> 
> 	rc = crypto_aead_setkey(ctx->tfm, ctx->key, SNAPENC_KEY_SIZE);
> 	if (rc) {
> 		pr_err("snapenc: failed to set AES key: %d\n", rc);
> 		goto err_free_tfm;
> 	}
> 
> 	/* Random IV base; will be incremented per chunk */
> 	get_random_bytes(ctx->iv, SNAPENC_IV_SIZE);
> 
> 	/* Set up image-level SHA-256 accumulator */
> 	ctx->image_hash = kzalloc(sizeof(*ctx->image_hash) +
> 				  crypto_shash_descsize(
> 				    crypto_alloc_shash("sha256", 0, 0)),
> 				  GFP_KERNEL);
> 	if (!ctx->image_hash) {
> 		rc = -ENOMEM;
> 		goto err_free_tfm;
> 	}
> 	ctx->image_hash->tfm = crypto_alloc_shash("sha256", 0, 0);
> 	if (IS_ERR(ctx->image_hash->tfm)) {
> 		rc = PTR_ERR(ctx->image_hash->tfm);
> 		goto err_free_hash_desc;
> 	}
> 
> 	rc = crypto_shash_init(ctx->image_hash);
> 	if (rc)
> 		goto err_free_hash_tfm;
> 
> 	return ctx;
> 
> err_free_hash_tfm:
> 	crypto_free_shash(ctx->image_hash->tfm);
> err_free_hash_desc:
> 	kfree(ctx->image_hash);
> err_free_tfm:
> 	crypto_free_aead(ctx->tfm);
> err_free_buf:
> 	vfree(ctx->chunk_buf);
> err_free_ctx:
> 	/* Zero the key material before freeing */
> 	memzero_explicit(ctx->key, SNAPENC_KEY_SIZE);
> 	kfree(ctx);
> 	return ERR_PTR(rc);
> }
> 
> /**
>  * snapenc_ctx_free - Free an encryption context, zeroing key material.
>  */
> static void snapenc_ctx_free(struct snapenc_ctx *ctx)
> {
> 	if (!ctx)
> 		return;
> 
> 	if (ctx->image_hash) {
> 		crypto_free_shash(ctx->image_hash->tfm);
> 		kfree(ctx->image_hash);
> 	}
> 	if (ctx->tfm)
> 		crypto_free_aead(ctx->tfm);
> 	vfree(ctx->chunk_buf);
> 	memzero_explicit(ctx->key, SNAPENC_KEY_SIZE);
> 	memzero_explicit(&ctx->sealed, sizeof(ctx->sealed));
> 	kfree(ctx);
> }
> 
> /**
>  * snapenc_encrypt_chunk - Encrypt one chunk of the hibernate image.
>  *
>  * Uses AES-256-GCM. The IV is incremented after each chunk so that
>  * each chunk has a unique nonce. The chunk index is included as
>  * Additional Authenticated Data (AAD) to prevent chunk reordering.
>  *
>  * @ctx:        Encryption context
>  * @in:         Plaintext input
>  * @in_len:     Length of plaintext
>  * @out:        Output (ciphertext + 16-byte GCM tag appended)
>  * @chunk_idx:  Chunk index (used as AAD)
>  *
>  * Returns number of output bytes on success, negative on error.
>  */
> static ssize_t snapenc_encrypt_chunk(struct snapenc_ctx *ctx,
> 				     const u8 *in, size_t in_len,
> 				     u8 *out, u64 chunk_idx)
> {
> 	struct aead_request *req;
> 	struct scatterlist sg_in, sg_out;
> 	u8 aad[8];
> 	struct scatterlist sg_aad;
> 	DECLARE_CRYPTO_WAIT(wait);
> 	int rc;
> 
> 	/* AAD = big-endian chunk index, prevents chunk reordering attacks */
> 	put_unaligned_be64(chunk_idx, aad);
> 
> 	req = aead_request_alloc(ctx->tfm, GFP_KERNEL);
> 	if (!req)
> 		return -ENOMEM;
> 
> 	sg_init_one(&sg_in,  in,  in_len);
> 	sg_init_one(&sg_out, out, in_len + SNAPENC_TAG_SIZE);
> 	sg_init_one(&sg_aad, aad, sizeof(aad));
> 
> 	aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
> 				  crypto_req_done, &wait);
> 	aead_request_set_ad(req, sizeof(aad));
> 	aead_request_set_crypt(req, &sg_in, &sg_out, in_len, ctx->iv);
> 
> 	rc = crypto_wait_req(crypto_aead_encrypt(req), &wait);
> 	aead_request_free(req);
> 
> 	if (rc) {
> 		pr_err("snapenc: AES-GCM encrypt failed: %d\n", rc);
> 		return rc;
> 	}
> 
> 	/* Update image-level hash with this ciphertext chunk */
> 	crypto_shash_update(ctx->image_hash, out, in_len + SNAPENC_TAG_SIZE);
> 
> 	/* Increment IV (treat as little-endian counter, last 4 bytes) */
> 	le32_add_cpu((__le32 *)(ctx->iv + 8), 1);
> 
> 	return in_len + SNAPENC_TAG_SIZE;
> }
> 
> /**
>  * snapenc_decrypt_chunk - Decrypt and verify one chunk of the hibernate image.
>  *
>  * AES-256-GCM decryption. If the authentication tag doesn't match
>  * (image was tampered), returns -EBADMSG and resume is aborted.
>  *
>  * @ctx:        Encryption context
>  * @in:         Ciphertext input (including appended GCM tag)
>  * @in_len:     Length including tag
>  * @out:        Output plaintext
>  * @chunk_idx:  Chunk index (must match what was used during encryption)
>  *
>  * Returns plaintext length on success, -EBADMSG if tag fails, negative on error.
>  */
> static ssize_t snapenc_decrypt_chunk(struct snapenc_ctx *ctx,
> 				     const u8 *in, size_t in_len,
> 				     u8 *out, u64 chunk_idx)
> {
> 	struct aead_request *req;
> 	struct scatterlist sg_in, sg_out;
> 	u8 aad[8];
> 	struct scatterlist sg_aad;
> 	DECLARE_CRYPTO_WAIT(wait);
> 	int rc;
> 	size_t plaintext_len = in_len - SNAPENC_TAG_SIZE;
> 
> 	put_unaligned_be64(chunk_idx, aad);
> 
> 	req = aead_request_alloc(ctx->tfm, GFP_KERNEL);
> 	if (!req)
> 		return -ENOMEM;
> 
> 	sg_init_one(&sg_in,  in,  in_len);
> 	sg_init_one(&sg_out, out, plaintext_len);
> 	sg_init_one(&sg_aad, aad, sizeof(aad));
> 
> 	aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
> 				  crypto_req_done, &wait);
> 	aead_request_set_ad(req, sizeof(aad));
> 	aead_request_set_crypt(req, &sg_in, &sg_out, in_len, ctx->iv);
> 
> 	rc = crypto_wait_req(crypto_aead_decrypt(req), &wait);
> 	aead_request_free(req);
> 
> 	if (rc == -EBADMSG) {
> 		pr_crit("snapenc: AUTHENTICATION FAILURE on chunk %llu — "
> 			"hibernate image has been tampered with. "
> 			"Resume aborted.\n", chunk_idx);
> 		return -EBADMSG;
> 	}
> 	if (rc) {
> 		pr_err("snapenc: AES-GCM decrypt failed: %d\n", rc);
> 		return rc;
> 	}
> 
> 	le32_add_cpu((__le32 *)(ctx->iv + 8), 1);
> 	return plaintext_len;
> }
> 
> /* ------------------------------------------------------------------ */
> /*  Hibernate / resume entry points                                    */
> /* ------------------------------------------------------------------ */
> 
> /**
>  * snapenc_hibernate_begin - Called before writing the hibernate image.
>  *
>  * Sequence:
>  *  1. Verify PCR23 is zero (or reset to zero)
>  *  2. Extend PCR23 with our magic value
>  *  3. Generate random AES key
>  *  4. Seal the key to TPM (bound to current PCR23 state)
>  *  5. Reset PCR23 to zero
>  *  6. Return (image writing proceeds with snapenc_encrypt_chunk)
>  *
>  * After this function, an attacker who modifies the image on disk
>  * cannot resume: the GCM tag will fail. An attacker who modifies
>  * PCR23 cannot get the key from the TPM.
>  *
>  * Returns 0 on success, negative on error (hibernate is aborted).
>  */
> int snapenc_hibernate_begin(void)
> {
> 	struct tpm_chip *chip;
> 	struct snapenc_ctx *ctx;
> 	int rc;
> 
> 	chip = tpm_default_chip();
> 	if (!chip) {
> 		pr_err("snapenc: no TPM chip available\n");
> 		return -ENODEV;
> 	}
> 
> 	ctx = snapenc_ctx_alloc();
> 	if (IS_ERR(ctx)) {
> 		rc = PTR_ERR(ctx);
> 		goto out_put_chip;
> 	}
> 
> 	/* Step 1: Verify/reset PCR23 */
> 	rc = snapenc_pcr_is_zero(chip);
> 	if (rc) {
> 		/*
> 		 * PCR23 not zero. Could mean:
> 		 *  (a) We left it extended from a previous (failed) hibernate
> 		 *  (b) Another agent is using it
> 		 * Attempt a reset and retry.
> 		 */
> 		pr_warn("snapenc: PCR%d not zero, attempting reset\n",
> 			hibernate_tpm_pcr);
> 		rc = snapenc_pcr_reset(chip);
> 		if (rc) {
> 			pr_err("snapenc: PCR%d reset failed: %d\n",
> 			       hibernate_tpm_pcr, rc);
> 			goto out_free_ctx;
> 		}
> 		rc = snapenc_pcr_is_zero(chip);
> 		if (rc)
> 			goto out_free_ctx;
> 	}
> 
> 	/* Step 2: Extend PCR23 with our magic value */
> 	rc = snapenc_pcr_extend(chip);
> 	if (rc) {
> 		pr_err("snapenc: PCR%d extend failed: %d\n",
> 		       hibernate_tpm_pcr, rc);
> 		goto out_reset_pcr;
> 	}
> 
> 	/* Step 3+4: Seal the encryption key to the current PCR state */
> 	rc = snapenc_seal_key(chip, ctx->key, &ctx->sealed);
> 	if (rc) {
> 		pr_err("snapenc: key seal failed: %d\n", rc);
> 		goto out_reset_pcr;
> 	}
> 
> 	/* Step 5: Reset PCR23 — key is now locked in the sealed blob */
> 	rc = snapenc_pcr_reset(chip);
> 	if (rc) {
> 		pr_err("snapenc: PCR%d final reset failed: %d\n",
> 		       hibernate_tpm_pcr, rc);
> 		/*
> 		 * Not fatal for security — the sealed blob still requires
> 		 * the PCR to be in the extended state to unseal. But log it.
> 		 */
> 		pr_warn("snapenc: PCR%d left in extended state\n",
> 			hibernate_tpm_pcr);
> 	}
> 
> 	hibernate_enc_ctx = ctx;
> 	pr_info("snapenc: hibernate image will be encrypted and authenticated\n");
> 	tpm_put_chip(chip);
> 	return 0;
> 
> out_reset_pcr:
> 	snapenc_pcr_reset(chip);
> out_free_ctx:
> 	snapenc_ctx_free(ctx);
> out_put_chip:
> 	tpm_put_chip(chip);
> 	return rc;
> }
> 
> /**
>  * snapenc_hibernate_end - Called after writing the hibernate image.
>  *
>  * Finalizes the image-level SHA-256 hash and writes it to the image
>  * header so resume can do a fast integrity check before attempting
>  * TPM unseal (defense in depth).
>  */
> void snapenc_hibernate_end(void)
> {
> 	struct snapenc_ctx *ctx = hibernate_enc_ctx;
> 
> 	if (!ctx)
> 		return;
> 
> 	/* Finalize image-level hash */
> 	crypto_shash_final(ctx->image_hash, ctx->image_digest);
> 
> 	pr_info("snapenc: hibernate image encryption complete\n");
> 	pr_debug("snapenc: image SHA-256: %*phN\n",
> 		 SHA256_DIGEST_SIZE, ctx->image_digest);
> 
> 	/* Don't free ctx here — resume needs the sealed key */
> }
> 
> /**
>  * snapenc_resume_begin - Called before reading the hibernate image.
>  *
>  * Sequence:
>  *  1. Extend PCR23 with the same magic value used during hibernate
>  *  2. Ask TPM to unseal the key (will fail if PCR doesn't match)
>  *  3. Re-initialize AES-GCM with the unsealed key
>  *  4. Reset PCR23
>  *
>  * Returns 0 on success, -EACCES if TPM policy fails (image tampered
>  * or wrong system), negative on other errors.
>  */
> int snapenc_resume_begin(void)
> {
> 	struct tpm_chip *chip;
> 	struct snapenc_ctx *ctx = hibernate_enc_ctx;
> 	u8 unsealed_key[SNAPENC_KEY_SIZE];
> 	int rc;
> 
> 	if (!ctx) {
> 		pr_err("snapenc: no encryption context for resume\n");
> 		return -EINVAL;
> 	}
> 
> 	chip = tpm_default_chip();
> 	if (!chip) {
> 		pr_err("snapenc: no TPM chip available for resume\n");
> 		return -ENODEV;
> 	}
> 
> 	/* Step 1: Extend PCR23 — must match state at seal time */
> 	rc = snapenc_pcr_extend(chip);
> 	if (rc) {
> 		pr_err("snapenc: resume PCR%d extend failed: %d\n",
> 		       hibernate_tpm_pcr, rc);
> 		goto out;
> 	}
> 
> 	/* Step 2: Unseal key — TPM checks PCR23 matches policy */
> 	rc = snapenc_unseal_key(chip, &ctx->sealed, unsealed_key);
> 	if (rc == -EACCES) {
> 		pr_crit("snapenc: TPM REFUSED to release hibernate key.\n"
> 			"PCR%d state does not match seal-time policy.\n"
> 			"This may indicate: wrong system, firmware change, "
> 			"or the hibernate image was created on a different boot.\n"
> 			"Resume aborted for security.\n",
> 			hibernate_tpm_pcr);
> 		goto out_reset_pcr;
> 	}
> 	if (rc) {
> 		pr_err("snapenc: key unseal failed: %d\n", rc);
> 		goto out_reset_pcr;
> 	}
> 
> 	/* Step 3: Re-arm the AES transform with the unsealed key */
> 	rc = crypto_aead_setkey(ctx->tfm, unsealed_key, SNAPENC_KEY_SIZE);
> 	memzero_explicit(unsealed_key, SNAPENC_KEY_SIZE);
> 	if (rc) {
> 		pr_err("snapenc: failed to restore AES key: %d\n", rc);
> 		goto out_reset_pcr;
> 	}
> 
> 	/* Step 4: Reset PCR23 */
> 	snapenc_pcr_reset(chip);
> 
> 	pr_info("snapenc: TPM key unseal successful, resuming\n");
> 	tpm_put_chip(chip);
> 	return 0;
> 
> out_reset_pcr:
> 	snapenc_pcr_reset(chip);
> out:
> 	memzero_explicit(unsealed_key, SNAPENC_KEY_SIZE);
> 	tpm_put_chip(chip);
> 	return rc;
> }
> 
> /**
>  * snapenc_resume_end - Called after successful resume.
>  *
>  * Cleans up the encryption context.
>  */
> void snapenc_resume_end(void)
> {
> 	snapenc_ctx_free(hibernate_enc_ctx);
> 	hibernate_enc_ctx = NULL;
> }
> 
> /* ------------------------------------------------------------------ */
> /*  Module init: verify TPM availability and PCR state                 */
> /* ------------------------------------------------------------------ */
> 
> static int __init snapenc_init(void)
> {
> 	struct tpm_chip *chip;
> 	int rc;
> 
> 	/* Only meaningful if lockdown is active */
> 	if (!kernel_is_locked_down(NULL)) {
> 		pr_debug("snapenc: kernel not locked down, "
> 			 "hibernate encryption available but not required\n");
> 	}
> 
> 	chip = tpm_default_chip();
> 	if (!chip) {
> 		pr_warn("snapenc: no TPM2 chip found. "
> 			"Hibernate encryption unavailable.\n");
> 		return -ENODEV;
> 	}
> 
> 	if (!tpm_is_tpm2(chip)) {
> 		pr_err("snapenc: TPM1.x not supported, need TPM2\n");
> 		tpm_put_chip(chip);
> 		return -ENODEV;
> 	}
> 
> 	/* Verify PCR is free at boot time */
> 	rc = snapenc_pcr_is_zero(chip);
> 	if (rc) {
> 		tpm_put_chip(chip);
> 		return rc;
> 	}
> 
> 	pr_info("snapenc: initialized. Using PCR%d on %s for hibernate sealing.\n",
> 		hibernate_tpm_pcr, dev_name(&chip->dev));
> 	tpm_put_chip(chip);
> 	return 0;
> }
> late_initcall(snapenc_init);

> /* SPDX-License-Identifier: GPL-2.0-only */
> /*
>  * kernel/power/snapenc.h
>  *
>  * Interface for hibernate image encryption using TPM2 + PCR sealing.
>  */
> 
> #ifndef __POWER_SNAPENC_H
> #define __POWER_SNAPENC_H
> 
> #ifdef CONFIG_HIBERNATION_ENCRYPTION
> 
> int  snapenc_hibernate_begin(void);
> void snapenc_hibernate_end(void);
> int  snapenc_resume_begin(void);
> void snapenc_resume_end(void);
> 
> ssize_t snapenc_encrypt_chunk(struct snapenc_ctx *ctx,
> 			      const u8 *in, size_t in_len,
> 			      u8 *out, u64 chunk_idx);
> 
> ssize_t snapenc_decrypt_chunk(struct snapenc_ctx *ctx,
> 			      const u8 *in, size_t in_len,
> 			      u8 *out, u64 chunk_idx);
> 
> #else /* !CONFIG_HIBERNATION_ENCRYPTION */
> 
> static inline int  snapenc_hibernate_begin(void) { return 0; }
> static inline void snapenc_hibernate_end(void)   { }
> static inline int  snapenc_resume_begin(void)    { return 0; }
> static inline void snapenc_resume_end(void)      { }
> 
> #endif /* CONFIG_HIBERNATION_ENCRYPTION */
> #endif /* __POWER_SNAPENC_H */


^ permalink raw reply

* Re: [PATCH 4/9] interconnect: qcom: icc-rpm: allow overwriting get_bw callback
From: Dmitry Baryshkov @ 2026-03-23 23:14 UTC (permalink / raw)
  To: Konrad Dybcio
  Cc: Georgi Djakov, Konrad Dybcio, Bjorn Andersson, Luca Weiss,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Brian Masney,
	linux-arm-msm, linux-pm, devicetree, linux-kernel
In-Reply-To: <abb14718-3d94-426a-a0f0-d101470951ad@oss.qualcomm.com>

On Mon, Mar 23, 2026 at 11:32:33AM +0100, Konrad Dybcio wrote:
> On 3/23/26 2:17 AM, Dmitry Baryshkov wrote:
> > MSM8974 requires a separate get_bw callback, since on that platform
> > increasing the clock rate for some of the NoCs during boot may lead to
> > hangs. For the details see commit 9caf2d956cfa ("interconnect: qcom:
> > msm8974: Don't boost the NoC rate during boot").
> 
> Is there a single specific bus where this causes an issue, or is
> setting *any* resource to INT_MAX problematic?

I'd refer this to Luca. I didn't experiment that much (and also note,
this behaviour might be firmware-specific).

My goal for this patchset was to port as close as possible. We can drop
the get_bw later, after enabling more interconnects, writing the QoS,
etc.

-- 
With best wishes
Dmitry

^ permalink raw reply

* Re: [PATCH 3/9] interconnect: qcom: drop unused is_on flag
From: Dmitry Baryshkov @ 2026-03-23 23:12 UTC (permalink / raw)
  To: Konrad Dybcio
  Cc: Georgi Djakov, Konrad Dybcio, Bjorn Andersson, Luca Weiss,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Brian Masney,
	linux-arm-msm, linux-pm, devicetree, linux-kernel
In-Reply-To: <3d75f9fa-f744-4437-8c68-16ca751193ae@oss.qualcomm.com>

On Mon, Mar 23, 2026 at 11:26:47AM +0100, Konrad Dybcio wrote:
> On 3/23/26 2:17 AM, Dmitry Baryshkov wrote:
> > The commit 2e2113c8a64f ("interconnect: qcom: rpm: Handle interface
> > clocks") has added the is_on flag to the qcom_icc_provider, but failed
> > to actually utilize it. Drop the flag.
> > 
> > Fixes: 2e2113c8a64f ("interconnect: qcom: rpm: Handle interface clocks")
> > Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> > ---
> >  drivers/interconnect/qcom/icc-rpm.h | 2 --
> >  1 file changed, 2 deletions(-)
> > 
> > diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h
> > index f4883d43eae4..3366531f66fc 100644
> > --- a/drivers/interconnect/qcom/icc-rpm.h
> > +++ b/drivers/interconnect/qcom/icc-rpm.h
> > @@ -51,7 +51,6 @@ struct rpm_clk_resource {
> >   * @bus_clk: a pointer to a HLOS-owned bus clock
> >   * @intf_clks: a clk_bulk_data array of interface clocks
> >   * @keep_alive: whether to always keep a minimum vote on the bus clocks
> > - * @is_on: whether the bus is powered on
> >   */
> >  struct qcom_icc_provider {
> >  	struct icc_provider provider;
> > @@ -66,7 +65,6 @@ struct qcom_icc_provider {
> >  	struct clk *bus_clk;
> >  	struct clk_bulk_data *intf_clks;
> >  	bool keep_alive;
> > -	bool is_on;
> 
> Hm, looks like the clock vote is kept all the way from .probe()
> to .remove(). I wonder if that's really what should happen..

Well, it's probably up to you to decide. I've a bit lost my way in the
vendor's adhoc / bus / etc. code.

> 
> That's what drivers/interconnect/qcom/holi.c does on msm-5.10
> 
> Maybe "iface clocks" are only needed to execute a ->set() on a node?

Hmm, I don't know. AXI clocks seems to describe the clocking between the
NoC and a particular device. So maybe it's required for as long as there
is something using the NoC.

-- 
With best wishes
Dmitry

^ permalink raw reply

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

On Tue, 24 Mar 2026 01:08:20 +0900 Youngjun Park <youngjun.park@lge.com> wrote:

> Rebased onto mm-new per Andrew's suggestion [1]. The si->flags race
> flagged by AI review in v7 (between SWP_HIBERNATION and cont_lock in
> add_swap_count_continuation) and the proposed fixes discussed there
> (atomic ops for si->flags, or serializing with swap_lock) are all moot
> on mm-new since Kairui's series removed that code path entirely.
> kernel/power/ changes are small, so Andrew proposed carrying everything
> through mm-new.
> 
> Rafael, could you ack the PM-side changes?

Please.

We'll hit a conflict in linux-next and Mark will tell us and we can
flag that to Linus when merging into mainline, usual stuff.

Or we can park this until the next cycle, depends on how serious the
bug is.  How serious is the bug?

^ permalink raw reply

* Re: [PATCH v11 10/16] KVM: guest_memfd: Add flag to remove from direct map
From: Ackerley Tng @ 2026-03-23 21:15 UTC (permalink / raw)
  To: Kalyazin, Nikita, kvm@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
	linux-fsdevel@vger.kernel.org, linux-mm@kvack.org,
	bpf@vger.kernel.org, linux-kselftest@vger.kernel.org,
	kernel@xen0n.name, linux-riscv@lists.infradead.org,
	linux-s390@vger.kernel.org, loongarch@lists.linux.dev,
	linux-pm@vger.kernel.org
  Cc: pbonzini@redhat.com, corbet@lwn.net, maz@kernel.org,
	oupton@kernel.org, joey.gouly@arm.com, suzuki.poulose@arm.com,
	yuzenghui@huawei.com, catalin.marinas@arm.com, will@kernel.org,
	seanjc@google.com, tglx@kernel.org, mingo@redhat.com,
	bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org,
	hpa@zytor.com, luto@kernel.org, peterz@infradead.org,
	willy@infradead.org, akpm@linux-foundation.org, david@kernel.org,
	lorenzo.stoakes@oracle.com, vbabka@kernel.org, rppt@kernel.org,
	surenb@google.com, mhocko@suse.com, ast@kernel.org,
	daniel@iogearbox.net, andrii@kernel.org, martin.lau@linux.dev,
	eddyz87@gmail.com, song@kernel.org, yonghong.song@linux.dev,
	john.fastabend@gmail.com, kpsingh@kernel.org, sdf@fomichev.me,
	haoluo@google.com, jolsa@kernel.org, jgg@ziepe.ca,
	jhubbard@nvidia.com, peterx@redhat.com, jannh@google.com,
	pfalcato@suse.de, skhan@linuxfoundation.org, riel@surriel.com,
	ryan.roberts@arm.com, jgross@suse.com, yu-cheng.yu@intel.com,
	kas@kernel.org, coxu@redhat.com, kevin.brodsky@arm.com,
	yosry@kernel.org, ajones@ventanamicro.com, maobibo@loongson.cn,
	tabba@google.com, prsampat@amd.com, wu.fei9@sanechips.com.cn,
	mlevitsk@redhat.com, jmattson@google.com, jthoughton@google.com,
	agordeev@linux.ibm.com, alex@ghiti.fr, aou@eecs.berkeley.edu,
	borntraeger@linux.ibm.com, chenhuacai@kernel.org,
	dev.jain@arm.com, gor@linux.ibm.com, hca@linux.ibm.com,
	palmer@dabbelt.com, pjw@kernel.org, shijie@os.amperecomputing.com,
	svens@linux.ibm.com, thuth@redhat.com, wyihan@google.com,
	yang@os.amperecomputing.com, Jonathan.Cameron@huawei.com,
	Liam.Howlett@oracle.com, urezki@gmail.com,
	zhengqi.arch@bytedance.com, gerald.schaefer@linux.ibm.com,
	jiayuan.chen@shopee.com, lenb@kernel.org, osalvador@suse.de,
	pavel@kernel.org, rafael@kernel.org, vannapurve@google.com,
	jackmanb@google.com, aneesh.kumar@kernel.org,
	patrick.roy@linux.dev, Thomson, Jack, Itazuri, Takahiro,
	Manwaring, Derek
In-Reply-To: <20260317141031.514-11-kalyazin@amazon.com>

"Kalyazin, Nikita" <kalyazin@amazon.co.uk> writes:

>
> [...snip...]
>
>  static vm_fault_t kvm_gmem_fault_user_mapping(struct vm_fault *vmf)
>  {
>  	struct inode *inode = file_inode(vmf->vma->vm_file);
>  	struct folio *folio;
>  	vm_fault_t ret = VM_FAULT_LOCKED;
> +	int err;
>
>  	if (((loff_t)vmf->pgoff << PAGE_SHIFT) >= i_size_read(inode))
>  		return VM_FAULT_SIGBUS;
> @@ -418,6 +454,14 @@ static vm_fault_t kvm_gmem_fault_user_mapping(struct vm_fault *vmf)
>  		folio_mark_uptodate(folio);
>  	}
>
> +	if (kvm_gmem_no_direct_map(folio_inode(folio))) {
> +		err = kvm_gmem_folio_zap_direct_map(folio);
> +		if (err) {
> +			ret = vmf_error(err);
> +			goto out_folio;
> +		}
> +	}
> +
>  	vmf->page = folio_file_page(folio, vmf->pgoff);
>

Sashiko pointed out that kvm_gmem_populate() might try and write to
direct-map-removed folios, but I think that's handled because populate
will first try and GUP folios, which is already blocked for
direct-map-removed folios.

>  out_folio:
> @@ -528,6 +572,9 @@ static void kvm_gmem_free_folio(struct folio *folio)
>  	kvm_pfn_t pfn = page_to_pfn(page);
>  	int order = folio_order(folio);
>
> +	if (kvm_gmem_folio_no_direct_map(folio))
> +		kvm_gmem_folio_restore_direct_map(folio);
> +
>  	kvm_arch_gmem_invalidate(pfn, pfn + (1ul << order));
>  }
>

Sashiko says to invalidate then restore direct map, I think in this case
it doesn't matter since if the folio needed invalidation, it must be
private, and the host shouldn't be writing to the private pages anyway.

One benefit of retaining this order (restore, invalidate) is that it
opens the invalidate hook to possibly do something regarding memory
contents?

Or perhaps we should just take the suggestion (invalidate, restore) and
align that invalidate should not touch memory contents.

> @@ -591,6 +638,9 @@ static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
>  	/* Unmovable mappings are supposed to be marked unevictable as well. */
>  	WARN_ON_ONCE(!mapping_unevictable(inode->i_mapping));
>
> +	if (flags & GUEST_MEMFD_FLAG_NO_DIRECT_MAP)
> +		mapping_set_no_direct_map(inode->i_mapping);
> +
>  	GMEM_I(inode)->flags = flags;
>
>  	file = alloc_file_pseudo(inode, kvm_gmem_mnt, name, O_RDWR, &kvm_gmem_fops);
> @@ -803,13 +853,22 @@ int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot,
>  	}
>
>  	r = kvm_gmem_prepare_folio(kvm, slot, gfn, folio);
> +	if (r)
> +		goto out_unlock;
>
> +	if (kvm_gmem_no_direct_map(folio_inode(folio))) {
> +		r = kvm_gmem_folio_zap_direct_map(folio);
> +		if (r)
> +			goto out_unlock;
> +	}
> +
>
> [...snip...]
>

Preparing a folio used to involve zeroing, but that has since been
refactored out, so I believe zapping can come before preparing.

Similar to the above point on invalidation: perhaps we should take the
suggestion to zap then prepare

+ And align that preparation should not touch memory contents
+ Avoid needing to undo the preparation on zapping failure (.free_folio
  is not called on folio_put(), it is only called folio on removal from
  filemap).

^ permalink raw reply

* Re: [PATCH v8 9/9] dax/hmem: Reintroduce Soft Reserved ranges back into the iomem tree
From: Dan Williams @ 2026-03-23 21:09 UTC (permalink / raw)
  To: Smita Koralahalli, linux-cxl, linux-kernel, nvdimm, linux-fsdevel,
	linux-pm
  Cc: Ard Biesheuvel, Alison Schofield, Vishal Verma, Ira Weiny,
	Dan Williams, Jonathan Cameron, Yazen Ghannam, Dave Jiang,
	Davidlohr Bueso, Matthew Wilcox, Jan Kara, Rafael J . Wysocki,
	Len Brown, Pavel Machek, Li Ming, Jeff Johnson, Ying Huang,
	Yao Xingtao, Peter Zijlstra, Greg Kroah-Hartman, Nathan Fontenot,
	Terry Bowman, Robert Richter, Benjamin Cheatham, Zhijian Li,
	Borislav Petkov, Smita Koralahalli, Tomasz Wolski
In-Reply-To: <20260322195343.206900-10-Smita.KoralahalliChannabasappa@amd.com>

Smita Koralahalli wrote:
> Reworked from a patch by Alison Schofield <alison.schofield@intel.com>
> 
> Reintroduce Soft Reserved range into the iomem_resource tree for HMEM
> to consume.
> 
> This restores visibility in /proc/iomem for ranges actively in use, while
> avoiding the early-boot conflicts that occurred when Soft Reserved was
> published into iomem before CXL window and region discovery.

I recommend dropping this patch. Given that the v7.0 kernel already set
a new precedent of not publishing "Soft Reserve", there is no pressing
need at this time to bring it back. We can always revive a patch like
this with a regression rationale, but otherwise a less busy /proc/iomem
is attractive.

^ permalink raw reply

* Re: [PATCH v8 6/9] dax: Track all dax_region allocations under a global resource tree
From: Dan Williams @ 2026-03-23 20:55 UTC (permalink / raw)
  To: Smita Koralahalli, linux-cxl, linux-kernel, nvdimm, linux-fsdevel,
	linux-pm
  Cc: Ard Biesheuvel, Alison Schofield, Vishal Verma, Ira Weiny,
	Dan Williams, Jonathan Cameron, Yazen Ghannam, Dave Jiang,
	Davidlohr Bueso, Matthew Wilcox, Jan Kara, Rafael J . Wysocki,
	Len Brown, Pavel Machek, Li Ming, Jeff Johnson, Ying Huang,
	Yao Xingtao, Peter Zijlstra, Greg Kroah-Hartman, Nathan Fontenot,
	Terry Bowman, Robert Richter, Benjamin Cheatham, Zhijian Li,
	Borislav Petkov, Smita Koralahalli, Tomasz Wolski
In-Reply-To: <20260322195343.206900-7-Smita.KoralahalliChannabasappa@amd.com>

Smita Koralahalli wrote:
> Introduce a global "DAX Regions" resource root and register each
> dax_region->res under it via request_resource(). Release the resource on
> dax_region teardown.
> 
> By enforcing a single global namespace for dax_region allocations, this
> ensures only one of dax_hmem or dax_cxl can successfully register a
> dax_region for a given range.
> 
> Suggested-by: Dan Williams <dan.j.williams@intel.com>
> Signed-off-by: Smita Koralahalli <Smita.KoralahalliChannabasappa@amd.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> ---
>  drivers/dax/bus.c | 20 +++++++++++++++++---
>  1 file changed, 17 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c
> index 299134c9b294..68437c05e21d 100644
> --- a/drivers/dax/bus.c
> +++ b/drivers/dax/bus.c
> @@ -10,6 +10,7 @@
>  #include "dax-private.h"
>  #include "bus.h"
>  
> +static struct resource dax_regions = DEFINE_RES_MEM_NAMED(0, -1, "DAX Regions");

Just type it out, skip using the DEFINE_RES* macro, like the definitions
of iomem_resource and soft_reserve_resource. Since the argument is a
size not an end address.

>  static DEFINE_MUTEX(dax_bus_lock);
>  
>  /*
> @@ -627,6 +628,7 @@ static void dax_region_unregister(void *region)
>  
>  	sysfs_remove_groups(&dax_region->dev->kobj,
>  			dax_region_attribute_groups);
> +	release_resource(&dax_region->res);
>  	dax_region_put(dax_region);
>  }
>  
> @@ -635,6 +637,7 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id,
>  		unsigned long flags)
>  {
>  	struct dax_region *dax_region;
> +	int rc;
>  
>  	/*
>  	 * The DAX core assumes that it can store its private data in
> @@ -667,14 +670,25 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id,
>  		.flags = IORESOURCE_MEM | flags,
>  	};
>  
> -	if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) {
> -		dax_region_put(dax_region);
> -		return NULL;
> +	rc = request_resource(&dax_regions, &dax_region->res);
> +	if (rc) {
> +		dev_dbg(parent, "dax_region resource conflict for %pR\n",
> +			&dax_region->res);

I normally do not like a driver to be chatty, but resource conflicts are
significant. This one deserves to be dev_err().

^ permalink raw reply

* Re: [PATCH v11 10/16] KVM: guest_memfd: Add flag to remove from direct map
From: Ackerley Tng @ 2026-03-23 20:47 UTC (permalink / raw)
  To: David Hildenbrand (Arm), Kalyazin, Nikita, kvm@vger.kernel.org,
	linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
	linux-fsdevel@vger.kernel.org, linux-mm@kvack.org,
	bpf@vger.kernel.org, linux-kselftest@vger.kernel.org,
	kernel@xen0n.name, linux-riscv@lists.infradead.org,
	linux-s390@vger.kernel.org, loongarch@lists.linux.dev,
	linux-pm@vger.kernel.org
  Cc: pbonzini@redhat.com, corbet@lwn.net, maz@kernel.org,
	oupton@kernel.org, joey.gouly@arm.com, suzuki.poulose@arm.com,
	yuzenghui@huawei.com, catalin.marinas@arm.com, will@kernel.org,
	seanjc@google.com, tglx@kernel.org, mingo@redhat.com,
	bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org,
	hpa@zytor.com, luto@kernel.org, peterz@infradead.org,
	willy@infradead.org, akpm@linux-foundation.org,
	lorenzo.stoakes@oracle.com, vbabka@kernel.org, rppt@kernel.org,
	surenb@google.com, mhocko@suse.com, ast@kernel.org,
	daniel@iogearbox.net, andrii@kernel.org, martin.lau@linux.dev,
	eddyz87@gmail.com, song@kernel.org, yonghong.song@linux.dev,
	john.fastabend@gmail.com, kpsingh@kernel.org, sdf@fomichev.me,
	haoluo@google.com, jolsa@kernel.org, jgg@ziepe.ca,
	jhubbard@nvidia.com, peterx@redhat.com, jannh@google.com,
	pfalcato@suse.de, skhan@linuxfoundation.org, riel@surriel.com,
	ryan.roberts@arm.com, jgross@suse.com, yu-cheng.yu@intel.com,
	kas@kernel.org, coxu@redhat.com, kevin.brodsky@arm.com,
	yosry@kernel.org, ajones@ventanamicro.com, maobibo@loongson.cn,
	tabba@google.com, prsampat@amd.com, wu.fei9@sanechips.com.cn,
	mlevitsk@redhat.com, jmattson@google.com, jthoughton@google.com,
	agordeev@linux.ibm.com, alex@ghiti.fr, aou@eecs.berkeley.edu,
	borntraeger@linux.ibm.com, chenhuacai@kernel.org,
	dev.jain@arm.com, gor@linux.ibm.com, hca@linux.ibm.com,
	palmer@dabbelt.com, pjw@kernel.org, shijie@os.amperecomputing.com,
	svens@linux.ibm.com, thuth@redhat.com, wyihan@google.com,
	yang@os.amperecomputing.com, Jonathan.Cameron@huawei.com,
	Liam.Howlett@oracle.com, urezki@gmail.com,
	zhengqi.arch@bytedance.com, gerald.schaefer@linux.ibm.com,
	jiayuan.chen@shopee.com, lenb@kernel.org, osalvador@suse.de,
	pavel@kernel.org, rafael@kernel.org, vannapurve@google.com,
	jackmanb@google.com, aneesh.kumar@kernel.org,
	patrick.roy@linux.dev, Thomson, Jack, Itazuri, Takahiro,
	Manwaring, Derek
In-Reply-To: <50bfaeb5-551e-403f-bd00-a7d8b6bbf6e2@kernel.org>

"David Hildenbrand (Arm)" <david@kernel.org> writes:

>
> [...snip...]
>
>> +static int kvm_gmem_folio_zap_direct_map(struct folio *folio)
>> +{
>> +	u64 gmem_flags = GMEM_I(folio_inode(folio))->flags;
>> +	int r = 0;
>> +
>> +	if (kvm_gmem_folio_no_direct_map(folio) || !(gmem_flags & GUEST_MEMFD_FLAG_NO_DIRECT_MAP))
>
> The function is only called when
>
> 	kvm_gmem_no_direct_map(folio_inode(folio))
>
> Does it really make sense to check for GUEST_MEMFD_FLAG_NO_DIRECT_MAP again?
>

Good point that GUEST_MEMFD_FLAG_NO_DIRECT_MAP was already checked in
the caller. I think we can drop this second check.

> If, at all, it should be a warning if GUEST_MEMFD_FLAG_NO_DIRECT_MAP is
> not set?
>
> Further, kvm_gmem_folio_zap_direct_map() uses the folio lock to
> synchronize, right? Might be worth pointing that out somehow (e.g.,
> lockdep check if possible).
>
>> +		goto out;
>> +
>> +	r = folio_zap_direct_map(folio);
>> +	if (!r)
>> +		folio->private = (void *)((u64)folio->private | KVM_GMEM_FOLIO_NO_DIRECT_MAP);
>> +
>> +out:
>> +	return r;
>> +}
>> +
>> +static void kvm_gmem_folio_restore_direct_map(struct folio *folio)
>> +{
>
> kvm_gmem_folio_zap_direct_map() is allowed to be called on folios that
> already have the directmap remove, kvm_gmem_folio_restore_direct_map()
> cannot be called if the directmap was already restored.
>

This inconsistency was probably introduced by my comments [1] (sorry!)

I think the inconsistency here is mostly because
kvm_gmem_folio_zap_direct_map() is called from two places but restore is
only called from one place :P

[1] https://lore.kernel.org/all/CAEvNRgEzVhEzr-3GWTsE7GSBsPdvVLq7WFEeLHzcmMe=R9S51w@mail.gmail.com/

> Should we make that more consistent?
>
>
> Hoping Sean can find some time to review
>
> --
> Cheers,
>
> David

^ permalink raw reply

* Re: [PATCH v11 05/16] mm/gup: drop local variable in gup_fast_folio_allowed
From: Ackerley Tng @ 2026-03-23 20:22 UTC (permalink / raw)
  To: David Hildenbrand (Arm), Kalyazin, Nikita, kvm@vger.kernel.org,
	linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
	linux-fsdevel@vger.kernel.org, linux-mm@kvack.org,
	bpf@vger.kernel.org, linux-kselftest@vger.kernel.org,
	kernel@xen0n.name, linux-riscv@lists.infradead.org,
	linux-s390@vger.kernel.org, loongarch@lists.linux.dev,
	linux-pm@vger.kernel.org
  Cc: pbonzini@redhat.com, corbet@lwn.net, maz@kernel.org,
	oupton@kernel.org, joey.gouly@arm.com, suzuki.poulose@arm.com,
	yuzenghui@huawei.com, catalin.marinas@arm.com, will@kernel.org,
	seanjc@google.com, tglx@kernel.org, mingo@redhat.com,
	bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org,
	hpa@zytor.com, luto@kernel.org, peterz@infradead.org,
	willy@infradead.org, akpm@linux-foundation.org,
	lorenzo.stoakes@oracle.com, vbabka@kernel.org, rppt@kernel.org,
	surenb@google.com, mhocko@suse.com, ast@kernel.org,
	daniel@iogearbox.net, andrii@kernel.org, martin.lau@linux.dev,
	eddyz87@gmail.com, song@kernel.org, yonghong.song@linux.dev,
	john.fastabend@gmail.com, kpsingh@kernel.org, sdf@fomichev.me,
	haoluo@google.com, jolsa@kernel.org, jgg@ziepe.ca,
	jhubbard@nvidia.com, peterx@redhat.com, jannh@google.com,
	pfalcato@suse.de, skhan@linuxfoundation.org, riel@surriel.com,
	ryan.roberts@arm.com, jgross@suse.com, yu-cheng.yu@intel.com,
	kas@kernel.org, coxu@redhat.com, kevin.brodsky@arm.com,
	yosry@kernel.org, ajones@ventanamicro.com, maobibo@loongson.cn,
	tabba@google.com, prsampat@amd.com, wu.fei9@sanechips.com.cn,
	mlevitsk@redhat.com, jmattson@google.com, jthoughton@google.com,
	agordeev@linux.ibm.com, alex@ghiti.fr, aou@eecs.berkeley.edu,
	borntraeger@linux.ibm.com, chenhuacai@kernel.org,
	dev.jain@arm.com, gor@linux.ibm.com, hca@linux.ibm.com,
	palmer@dabbelt.com, pjw@kernel.org, shijie@os.amperecomputing.com,
	svens@linux.ibm.com, thuth@redhat.com, wyihan@google.com,
	yang@os.amperecomputing.com, Jonathan.Cameron@huawei.com,
	Liam.Howlett@oracle.com, urezki@gmail.com,
	zhengqi.arch@bytedance.com, gerald.schaefer@linux.ibm.com,
	jiayuan.chen@shopee.com, lenb@kernel.org, osalvador@suse.de,
	pavel@kernel.org, rafael@kernel.org, vannapurve@google.com,
	jackmanb@google.com, aneesh.kumar@kernel.org,
	patrick.roy@linux.dev, Thomson, Jack, Itazuri, Takahiro,
	Manwaring, Derek
In-Reply-To: <0a14c10d-0dab-4b9c-85ec-e0ee25cd0db8@kernel.org>

"David Hildenbrand (Arm)" <david@kernel.org> writes:

> On 3/17/26 15:11, Kalyazin, Nikita wrote:
>> From: Nikita Kalyazin <kalyazin@amazon.com>
>>
>> Move the check for pinning closer to where the result is used.
>> No functional changes.
>>
>> Signed-off-by: Nikita Kalyazin <kalyazin@amazon.com>
>> ---
>>  mm/gup.c | 23 ++++++++++++-----------
>>  1 file changed, 12 insertions(+), 11 deletions(-)
>>
>> diff --git a/mm/gup.c b/mm/gup.c
>> index 5856d35be385..869d79c8daa4 100644
>> --- a/mm/gup.c
>> +++ b/mm/gup.c
>> @@ -2737,18 +2737,9 @@ EXPORT_SYMBOL(get_user_pages_unlocked);
>>   */
>>  static bool gup_fast_folio_allowed(struct folio *folio, unsigned int flags)
>>  {
>> -	bool reject_file_backed = false;
>>  	struct address_space *mapping;
>>  	unsigned long mapping_flags;
>>
>> -	/*
>> -	 * If we aren't pinning then no problematic write can occur. A long term
>> -	 * pin is the most egregious case so this is the one we disallow.
>> -	 */
>> -	if ((flags & (FOLL_PIN | FOLL_LONGTERM | FOLL_WRITE)) ==
>> -	    (FOLL_PIN | FOLL_LONGTERM | FOLL_WRITE))
>> -		reject_file_backed = true;
>> -
>>  	/* We hold a folio reference, so we can safely access folio fields. */
>>  	if (WARN_ON_ONCE(folio_test_slab(folio)))
>>  		return false;
>> @@ -2793,8 +2784,18 @@ static bool gup_fast_folio_allowed(struct folio *folio, unsigned int flags)
>>  	 */
>>  	if (secretmem_mapping(mapping))
>>  		return false;
>> -	/* The only remaining allowed file system is shmem. */
>> -	return !reject_file_backed || shmem_mapping(mapping);
>> +
>> +	/*
>> +	 * If we aren't pinning then no problematic write can occur. A writable
>> +	 * long term pin is the most egregious case, so this is the one we
>> +	 * allow only for ...
>> +	 */
>> +	if ((flags & (FOLL_PIN | FOLL_LONGTERM | FOLL_WRITE)) !=
>> +	    (FOLL_PIN | FOLL_LONGTERM | FOLL_WRITE))
>> +		return true;
>> +
>> +	/* ... hugetlb (which we allowed above already) and shared memory. */
>> +	return shmem_mapping(mapping);
>
> Acked-by: David Hildenbrand (Arm) <david@kernel.org>
>
> I'm wondering if it would be a good idea to check for a hugetlb mapping
> here instead of having the folio_test_hugetlb() check above.
>

I think it's nice that hugetlb folios are determined immediately to be
eligible for GUP-fast regardless of whether the folio is file-backed or
not.

> Something to ponder about :)
>
> --
> Cheers,
>
> David

^ permalink raw reply

* Re: [PATCH v3 08/12] amd-pstate-ut: Add ability to run a single testcase
From: Mario Limonciello (AMD) (kernel.org) @ 2026-03-23 20:21 UTC (permalink / raw)
  To: Gautham R. Shenoy, Rafael J . Wysocki, Viresh Kumar,
	K Prateek Nayak
  Cc: linux-kernel, linux-pm
In-Reply-To: <20260320144321.18543-9-gautham.shenoy@amd.com>



On 3/20/2026 9:43 AM, Gautham R. Shenoy wrote:
> Currently when amd-pstate-ut test module is loaded, it runs all the
> tests from amd_pstate_ut_cases[] array.
> 
> Add a module parameter named "run_only" that allows users to run a
> single test from the array by specifying the test name string.
> 
> Signed-off-by: Gautham R. Shenoy <gautham.shenoy@amd.com>
> ---
>   drivers/cpufreq/amd-pstate-ut.c | 11 ++++++++++-
>   1 file changed, 10 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c
> index 447b9aa5ce40..35e453a49c0f 100644
> --- a/drivers/cpufreq/amd-pstate-ut.c
> +++ b/drivers/cpufreq/amd-pstate-ut.c
> @@ -35,6 +35,10 @@
>   
>   #include "amd-pstate.h"
>   
> +static char *run_only;
> +module_param(run_only, charp, 0444);
> +MODULE_PARM_DESC(run_only,
> +	"Run only the named test case (default: run all)");

This default shows the end effect; but it doesn't make sense for this 
parameter IMO.

How about instead if you had a semicolon delimitted list and then 
defaulted an empty list to mean all tests?  Something like this:

static char *test_list;
module_param(test_list, charp, 0444)
MODULE_PARM_DESC(test_list,
	"Semicolon delimitted list of tests to run (empty means run all tests)");

>   
>   struct amd_pstate_ut_struct {
>   	const char *name;
> @@ -275,7 +279,12 @@ static int __init amd_pstate_ut_init(void)
>   	u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases);
>   
>   	for (i = 0; i < arr_size; i++) {
> -		int ret = amd_pstate_ut_cases[i].func(i);
> +		int ret;
> +
> +		if (run_only && strcmp(run_only, amd_pstate_ut_cases[i].name))
> +			continue;
> +
> +		ret = amd_pstate_ut_cases[i].func(i);

If you take my suggestion then you would split this on semicolon or end 
of string and then allow matching multiple.

>   
>   		if (ret)
>   			pr_err("%-4d %-20s\t fail: %d!\n", i+1, amd_pstate_ut_cases[i].name, ret);


^ permalink raw reply


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