* [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions
@ 2026-07-24 7:18 Pragnesh Papaniya
2026-07-24 7:18 ` [PATCH RFC 01/10] firmware: arm_scmi: Add SCMI QCOM Generic Extension Protocol documentation Pragnesh Papaniya
` (10 more replies)
0 siblings, 11 replies; 20+ messages in thread
From: Pragnesh Papaniya @ 2026-07-24 7:18 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park,
Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter,
Bjorn Andersson, Konrad Dybcio
Cc: Pragnesh Papaniya, Rajendra Nayak, Pankaj Patil, linux-arm-msm,
linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm,
linux-tegra, Amir Vajid, Ramakrishna Gottimukkula, Jia Yang
System Control Management Interface (SCMI) Qualcomm Generic Extension Protocol
consists of a small set of generic SET/GET/START/STOP commands, which is
used to turn on/off and configure Qualcomm SoC specific algorithms that run
on the SCP. Each algorithm is identified through an algorithm string and has
an immutable list of param_ids. All supported algorithms (currently just MEMLAT)
have their own dedicated section and are listed after the generic commands.
This series introduces the devfreq SCMI client driver that uses the MEMLAT
algorithm string hosted on the QCOM SCMI Generic Extension Protocol to detect
memory latency workloads and control frequency/level of the various memory
buses (DDR/LLCC/DDR_QOS). DDR/LLCC/DDR_QOS are modelled as devfreq devices
using the remote devfreq governor. This provides basic insight into device
operation via trans_stat and lets userspace further tweak the parameters of
the remote governor.
trans_stat data for DDR/LLCC/DDR_QOS is now available with this series:
From : To
315000000 479000000 545000000 725000000 840000000 959000000 1090000000 1211000000 time(ms)
315000000: 0 3 6 6 6 7 0 30 143956
479000000: 2 0 7 1 1 1 0 3 356
545000000: 7 6 0 5 5 0 0 10 1200
725000000: 3 0 5 0 6 1 0 6 2172
840000000: 8 2 3 2 0 4 0 12 1188
959000000: 3 0 1 2 2 0 0 13 272
1090000000: 0 0 0 0 0 0 0 0 0
1211000000: 35 4 11 5 11 8 0 0 21684
Total transition : 253
MEMLAT: Memory Latency algorithm
The MEMLAT algorithm (0x4d454d4c4154, ASCII "MEMLAT") scales the DDR, LLCC and
DDR_QOS buses in response to memory-latency-bound workloads. It runs on the CPUCP:
every sampling window it reads the per-CPU AMU counters, derives its statistics
(instructions-per-miss, back-end stall, write-back ratio), maps that to a target
level and votes for it directly on the DDR/LLCC/DDR_QOS interconnect. The kernel
never issues a frequency request in that loop. The 6-byte value is treated as a
64-bit algorithm string and split into two uint32 fields on the wire: algo_low
carries its lower 32 bits and algo_high its upper 32 bits.
With a distinct need to have the memory buses scaling done in SCP in response to
memory-latency-bound workloads, none of the existing SCMI solutions could be used
as-is. MPAM does not apply either: it is not enabled on all of the affected parts
(e.g. Hamoa). The role split between SCP and client driver is described next.
MEMLAT client driver pseudo-code:
probe():
SET_COMMON_EV_MAP # AMU events (all groups)
for each memory group (DDR / LLCC / DDR_QOS):
SET_MEM_GROUP # bind group to interconnect
SET_GRP_EV_MAP # per-group AMU events
for each monitor in the group:
SET_MONITOR # topology, cpumask, name
IPM_CEIL / BE_STALL_FLOOR # per-monitor tuneables
MON_FREQ_MAP # cpufreq -> memfreq map
SET_MIN_FREQ / SET_MAX_FREQ # clamps
SAMPLE_MS # sampling period
SET_EFFECTIVE_FREQ_METHOD # cpu-freq derivation method
START_TIMER # CPUCP now scales autonomously
devfreq poll (twice per CPUCP sample period):
GET_CUR_FREQ # read voted freq, per monitor
remove():
STOP_TIMER
SCP pseudo-code:
every sample_ms:
for each CPU:
sample AMU counters (instructions, cycles, cache-misses, stalls)
derive per-CPU IPM, back-end-stall % and write-back ratio
for each configured memory group (DDR / LLCC / DDR_QOS):
for each monitor in the group:
best_freq = 0
for each CPU in the monitor's cpumask:
if CPU is memory-bound (IPM/stall/write-back vs. the
monitor's configured thresholds):
freq = CPU's cpufreq, optionally scaled toward the ceiling
best_freq = max(best_freq, freq)
monitor.target_freq = monitor's cpufreq->memfreq map(best_freq)
group_vote = max(target_freq of every monitor in the group)
if group_vote changed since the last sample:
vote group_vote onto the group's interconnect path
GET_CUR_FREQ returns a monitor's last target_freq
START_TIMER / STOP_TIMER: resume / suspend the loop above
Thanks in advance for taking time to review the series.
Changes in v8:
- Add memlat support for Kaanapali.
- Express the cpufreq/memfreq map in MHz on both sides, matching the
firmware wire format, dropping the kHz conversion and the DDR_QOS
special case [Bjorn].
- Build the on-wire messages directly from the static configuration
instead of deep-copying it into heap mirror structs [Bjorn].
- Anchor each memory bus's OPP table and devfreq device on a lightweight
faux_device instead of a platform_device [Bjorn]; register the devfreq
device non-devm with matching teardown on our own error paths, fixing a
device/devfreq leak and use-after-free on unwind [Sashiko].
- Switch param_ids to #defines (fixed wire tokens) and keep hw_type as
the only enum; unit-suffix frequencies, add a memlat_set_param()
helper and rename message locals for clarity [Bjorn].
- Bound the vendor-protocol GET response copy against the caller
buffer and skip the payload copy for zero-length transfers
[Sashiko]; add an algorithm-string allowlist so the firmware never
sees an unrecognised string [Sudeep].
- Rewrite the driver Kconfig help and commit message around the
CPUCP/kernel role split, and expand the MEMLAT documentation with
the motivation for a vendor protocol and a call flow [Bjorn, Sudeep].
- Fold scmi-qcom-memlat-cfg.h into the driver .c, laid out as
definitions / per-SoC configuration / logic and driver boilerplate;
add kernel-doc on the per-monitor tuneables (be_stall_floor, ipm_ceil,
etc.) and reword the devfreq polling-interval comment to explain why
half the CPUCP sample period is correct here [Bjorn].
- Link to v7: https://lore.kernel.org/lkml/20260610-rfc_v7_scmi_memlat-v7-0-f3f68c608f25@oss.qualcomm.com/
Changes in v7:
- Minor bug fixes and documentation improvements based on review feedback.
- Significantly expand the QCOM Generic Vendor Protocol documentation
with a dedicated MEMLAT section covering all param_ids, payloads and
return values [Sudeep, Cristian].
- Move per-SoC event IDs and hardware type identifiers from the driver
into the platform configuration header.
- Poll devfreq at twice the CPUCP sampling rate so every firmware voting
cycle is reliably observed [Lukasz].
- Add a devicetree binding schema for the Qualcomm SCMI vendor
protocol@80 node.
- Link to v6: https://lore.kernel.org/r/20260507062237.78051-1-sibi.sankar@oss.qualcomm.com
Changes in v6:
- Combining vendor protocol and the client and moving it
back into RFC mode.
- Introduce target_freq attr flag and TRACK_REMOTE devfreq
governor flag.
- Add basic remote devfreq governor to give users data like
transtat [Dmitry]
- Drop the current design that relies on manual parsing of
device tree data and move those into SoC specific structs
- Add Glymur/Mahua/Hamoa/Purwa support in the same series.
- Link to v5: https://lore.kernel.org/lkml/20241115011515.1313447-1-quic_sibis@quicinc.com/
Changes in v5:
- Splitting the series into vendor protocol and memlat client.
- Also the move the memlat client implementation back to RFC
due to multiple opens.
- Use common xfer helper to avoid code duplication [Dmitry]
- Update enum documentation without duplicate enum info [Dmitry]
- Update the protol attributes doc with more information. [Cristian]
- Link to v4: https://lore.kernel.org/lkml/20241007061023.1978380-1-quic_sibis@quicinc.com/
Changes in v4:
- Restructure the bindings to mimic IMX [Christian]
- Add documentation for the qcom generic vendor protocol [Christian/Sudeep]
- Pick up Rb tag and fixup/re-order elements of the vendor ops [Christian]
- Follow naming convention and folder structure used by IMX
- Add missing enum in the vendor protocol and fix documentation [Konrad]
- Add missing enum in the scmi memlat driver and fix documentation [Konrad]
- Add checks for max memory and monitor [Shivnandan]
- Fix typo from START_TIMER -> STOP_TIMER [Shivnandan]
- Make populate_physical_mask func to void [Shivnandan]
- Remove unecessary zero set [Shivnandan]
- Use __free(device node) in init_cpufreq-memfreqmap [Christian/Konrad]
- Use sdev->dev.of_node directly [Christian]
- use return dev_err_probe in multiple places [Christian]
- Link to v3: https://lore.kernel.org/lkml/20240702191440.2161623-1-quic_sibis@quicinc.com/
Changes in v3:
- Drop container dvfs memlat container node. [Rob]
- Move scmi-memlat.yaml to protocol level given that a lot of vendors might end up
- using the same protocol number. [Rob]
- Replace qcom,cpulist with the standard "cpus" property. [Rob]
- Fix up compute-type/ipm-ceil required. [Rob]
- Make driver changes to the accommodate bindings changes. [Rob]
- Minor fixups in subjects/coverletter.
- Minor style fixes in dts.
- Link to v2: https://lore.kernel.org/lkml/20240612183031.219906-1-quic_sibis@quicinc.com/
Changes in v2:
- Add missing bindings for the protocol. [Konrad/Dmitry]
- Use alternate bindings. [Dmitry/Konrad]
- Rebase on top of Cristian's "SCMI multiple vendor protocol support" series. [Cristian]
- Add more documentation wherever possible. [Sudeep]
- Replace pr_err/info with it's dev equivalents.
- Mixed tabs and initialization cleanups in the memlat driver. [Konrad]
- Commit message update for the memlat driver. [Dmitry]
- Cleanups/Fixes suggested for the client driver. [Dmitry/Konrad/Cristian]
- Use opp-tables instead of memfreq-tbl. [Dmitry/Konrad]
- Detect physical cpu to deal with variants with reduced cpu count.
- Add support for DDR_QOS mem_type.
- Link to v1: https://lore.kernel.org/lkml/20240117173458.2312669-1-quic_sibis@quicinc.com/
Signed-off-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com>
---
Jia Yang (1):
arm64: dts: qcom: kaanapali: Enable LLCC/DDR/DDR_QOS DVFS
Pragnesh Papaniya (2):
firmware: arm_scmi: Add SCMI QCOM Generic Extension Protocol documentation
dt-bindings: firmware: arm,scmi: Add Qualcomm Generic Extension Protocol
Sibi Sankar (7):
firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions
PM / devfreq: Add new target_freq attribute flag for governors
PM / devfreq: Add new track_remote flag for governors
PM / devfreq: Add a governor for tracking remote device frequencies
PM / devfreq: Introduce the QCOM SCMI Memlat devfreq driver
arm64: dts: qcom: glymur: Enable LLCC/DDR/DDR_QOS DVFS
arm64: dts: qcom: hamoa: Enable LLCC/DDR/DDR_QOS DVFS
Documentation/ABI/testing/sysfs-class-devfreq | 8 +
.../devicetree/bindings/firmware/arm,scmi.yaml | 1 +
.../bindings/firmware/qcom,generic-scmi.yaml | 27 +
MAINTAINERS | 18 +
arch/arm64/boot/dts/qcom/glymur.dtsi | 41 +
arch/arm64/boot/dts/qcom/hamoa.dtsi | 4 +
arch/arm64/boot/dts/qcom/kaanapali.dtsi | 41 +
drivers/devfreq/Kconfig | 23 +
drivers/devfreq/Makefile | 2 +
drivers/devfreq/devfreq.c | 27 +
drivers/devfreq/governor_passive.c | 1 +
drivers/devfreq/governor_performance.c | 1 +
drivers/devfreq/governor_powersave.c | 1 +
drivers/devfreq/governor_remote.c | 73 ++
drivers/devfreq/governor_simpleondemand.c | 1 +
drivers/devfreq/governor_userspace.c | 1 +
drivers/devfreq/hisi_uncore_freq.c | 1 +
drivers/devfreq/scmi-qcom-memlat-devfreq.c | 1331 ++++++++++++++++++++
drivers/devfreq/tegra30-devfreq.c | 3 +-
drivers/firmware/arm_scmi/Kconfig | 1 +
drivers/firmware/arm_scmi/Makefile | 1 +
drivers/firmware/arm_scmi/vendors/qcom/Kconfig | 15 +
drivers/firmware/arm_scmi/vendors/qcom/Makefile | 2 +
.../arm_scmi/vendors/qcom/qcom-generic-ext.c | 183 +++
.../arm_scmi/vendors/qcom/qcom_generic.rst | 954 ++++++++++++++
include/linux/devfreq-governor.h | 8 +
include/linux/devfreq.h | 1 +
include/linux/scmi_qcom_protocol.h | 37 +
28 files changed, 2806 insertions(+), 1 deletion(-)
---
base-commit: 9eebf259d5352b87080d67758f483583d9e763d7
change-id: 20260724-rfc_v8_scmi_memlat-bc57a7472637
Best regards,
--
Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 20+ messages in thread* [PATCH RFC 01/10] firmware: arm_scmi: Add SCMI QCOM Generic Extension Protocol documentation 2026-07-24 7:18 [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Pragnesh Papaniya @ 2026-07-24 7:18 ` Pragnesh Papaniya 2026-07-24 7:27 ` sashiko-bot 2026-07-24 9:13 ` Sudeep Holla 2026-07-24 7:18 ` [PATCH RFC 02/10] dt-bindings: firmware: arm,scmi: Add Qualcomm Generic Extension Protocol Pragnesh Papaniya ` (9 subsequent siblings) 10 siblings, 2 replies; 20+ messages in thread From: Pragnesh Papaniya @ 2026-07-24 7:18 UTC (permalink / raw) To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Bjorn Andersson, Konrad Dybcio Cc: Pragnesh Papaniya, Rajendra Nayak, Pankaj Patil, linux-arm-msm, linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm, linux-tegra Add System Control Management Interface (SCMI) Qualcomm Generic Extension Protocol documentation. It consists of a small set of generic SET/GET/ START/STOP commands, which is used to turn on/off and configure Qualcomm SoC specific algorithms that run on the SCP. It currently only supports MEMLAT (memory latency governor) algorithm. The immutable pairing of the MEMLAT algorithm string with the supported param_ids associated with it are documented here. Co-developed-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com> Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com> Signed-off-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> --- .../arm_scmi/vendors/qcom/qcom_generic.rst | 954 +++++++++++++++++++++ 1 file changed, 954 insertions(+) diff --git a/drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst b/drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst new file mode 100644 index 000000000000..42e327d53841 --- /dev/null +++ b/drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst @@ -0,0 +1,954 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: <isonum.txt> + +================================================================================== +System Control and Management Interface (SCMI) Qualcomm Generic Extension Protocol +================================================================================== + +:Copyright: |copy| Qualcomm Technologies, Inc. and/or its subsidiaries. + +:Authors: + - Sibi Sankar <sibi.sankar@oss.qualcomm.com> + - Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> + +System Control and Management Interface Qualcomm Generic Extension Vendor Protocol +================================================================================== + +System Control Management Interface (SCMI) Qualcomm Generic Extension Protocol +consists of a small set of generic SET/GET/START/STOP commands, which is used to +turn on/off and configure Qualcomm SoC specific algorithms that run on the SCP. +Each algorithm is identified through an algorithm string and has an immutable list +of param_ids. All supported algorithms (currently just MEMLAT) have their own +dedicated section and are listed after the generic commands. + +Commands: +_________ + +PROTOCOL_VERSION +~~~~~~~~~~~~~~~~ + +message_id: 0x0 +protocol_id: 0x80 + ++---------------+--------------------------------------------------------------+ +|Return values | ++---------------+--------------------------------------------------------------+ +|Name |Description | ++---------------+--------------------------------------------------------------+ +|int32 status |See ARM SCMI Specification for status code definitions. | ++---------------+--------------------------------------------------------------+ +|uint32 version |For this revision of the specification, this value must be | +| |0x10000. | ++---------------+--------------------------------------------------------------+ + +PROTOCOL_ATTRIBUTES +~~~~~~~~~~~~~~~~~~~ + +message_id: 0x1 +protocol_id: 0x80 + ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |See ARM SCMI Specification for status code definitions. | ++------------------+-----------------------------------------------------------+ +|uint32 attributes |Bits[31:16] Reserved, must be set to 0. | +| |Bits[15:8] Number of agents in the system. Must match the | +| |value reported by the standard BASE protocol's | +| |PROTOCOL_ATTRIBUTES response. | +| |Bits[7:0] Number of algorithmic strings supported by the | +| |system. Only "MEMLAT" is currently supported hence it | +| |returns 1. | ++------------------+-----------------------------------------------------------+ + +PROTOCOL_MESSAGE_ATTRIBUTES +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +message_id: 0x2 +protocol_id: 0x80 + ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |See ARM SCMI Specification for status code definitions. | ++------------------+-----------------------------------------------------------+ +|uint32 attributes |For all message IDs the parameter has a value of 0. | ++------------------+-----------------------------------------------------------+ + +QCOM_SCMI_SET_PARAM +~~~~~~~~~~~~~~~~~~~ + +message_id: 0x10 +protocol_id: 0x80 + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 ext_id |Reserved, must be zero. | ++------------------+-----------------------------------------------------------+ +|uint32 algo_low |Lower 32-bit value of the algorithm string. | ++------------------+-----------------------------------------------------------+ +|uint32 algo_high |Upper 32-bit value of the algorithm string. | ++------------------+-----------------------------------------------------------+ +|uint32 param_id |Serves as the token message id for the algorithm string | +| |and is used to set various parameters supported by it. | ++------------------+-----------------------------------------------------------+ +|uint32 buf[] |Serves as the payload for the specified param_id and | +| |algorithm string pair. The payload size depends on the | +| |(algorithm string, param_id) pair; see the per-algorithm | +| |sections below. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: if the param_id and buf[] is parsed successfully | +| |by the chosen algorithm string. | +| |NOT_SUPPORTED: if the algorithm string does not have any | +| |matches. | +| |INVALID_PARAMETERS: if the param_id and the buf[] passed | +| |is rejected by the algorithm string. | ++------------------+-----------------------------------------------------------+ + +QCOM_SCMI_GET_PARAM +~~~~~~~~~~~~~~~~~~~ + +message_id: 0x11 +protocol_id: 0x80 + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 ext_id |Reserved, must be zero. | ++------------------+-----------------------------------------------------------+ +|uint32 algo_low |Lower 32-bit value of the algorithm string. | ++------------------+-----------------------------------------------------------+ +|uint32 algo_high |Upper 32-bit value of the algorithm string. | ++------------------+-----------------------------------------------------------+ +|uint32 param_id |Serves as the token message id for the algorithm string. | ++------------------+-----------------------------------------------------------+ +|uint32 buf[] |Serves as the payload and store of value for the specified | +| |param_id and algorithm string pair. The payload size | +| |depends on the (algorithm string, param_id) pair; see the | +| |per-algorithm sections below. The response payload is | +| |returned in the same buffer, overwriting the request | +| |contents on success. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: if the param_id and buf[] is parsed successfully | +| |by the chosen algorithm string and the result is copied | +| |into buf[]. | +| |NOT_SUPPORTED: if the algorithm string does not have any | +| |matches. | +| |INVALID_PARAMETERS: if the param_id and the buf[] passed | +| |is rejected by the algorithm string. | ++------------------+-----------------------------------------------------------+ +|uint32 buf[] |Holds the payload of the result of the query, returned in | +| |the same buffer used to send the request. Size depends on | +| |the (algorithm string, param_id) pair. | ++------------------+-----------------------------------------------------------+ + +QCOM_SCMI_START_ACTIVITY +~~~~~~~~~~~~~~~~~~~~~~~~ + +message_id: 0x12 +protocol_id: 0x80 + +The activity to be started is defined by the algorithm string; see the +per-algorithm sections (e.g. MEMLAT_START_TIMER) for valid param_ids. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 ext_id |Reserved, must be zero. | ++------------------+-----------------------------------------------------------+ +|uint32 algo_low |Lower 32-bit value of the algorithm string. | ++------------------+-----------------------------------------------------------+ +|uint32 algo_high |Upper 32-bit value of the algorithm string. | ++------------------+-----------------------------------------------------------+ +|uint32 param_id |Serves as the token message id for the algorithm string | +| |and is generally used to start the activity performed by | +| |the algorithm string. | ++------------------+-----------------------------------------------------------+ +|uint32 buf[] |Serves as the payload for the specified param_id and | +| |algorithm string pair. The payload size depends on the | +| |(algorithm string, param_id) pair; see the per-algorithm | +| |sections below. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: if the activity performed by the algorithm string | +| |starts successfully, or if it was already running. | +| |NOT_SUPPORTED: if the algorithm string does not have any | +| |matches. | ++------------------+-----------------------------------------------------------+ + +QCOM_SCMI_STOP_ACTIVITY +~~~~~~~~~~~~~~~~~~~~~~~ + +message_id: 0x13 +protocol_id: 0x80 + +The activity to be stopped is defined by the algorithm string; see the +per-algorithm sections (e.g. MEMLAT_STOP_TIMER) for valid param_ids. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 ext_id |Reserved, must be zero. | ++------------------+-----------------------------------------------------------+ +|uint32 algo_low |Lower 32-bit value of the algorithm string. | ++------------------+-----------------------------------------------------------+ +|uint32 algo_high |Upper 32-bit value of the algorithm string. | ++------------------+-----------------------------------------------------------+ +|uint32 param_id |Serves as the token message id for the algorithm string | +| |and is generally used to stop the activity performed by | +| |the algorithm string. | ++------------------+-----------------------------------------------------------+ +|uint32 buf[] |Serves as the payload for the specified param_id and | +| |algorithm string pair. The payload size depends on the | +| |(algorithm string, param_id) pair; see the per-algorithm | +| |sections below. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: if the activity performed by the algorithm string | +| |stops successfully, or if it was not running. | +| |NOT_SUPPORTED: if the algorithm string does not have any | +| |matches. | ++------------------+-----------------------------------------------------------+ + +MEMLAT: Memory Latency algorithm +________________________________ + +The MEMLAT algorithm (0x4d454d4c4154, ASCII "MEMLAT") scales the DDR, LLCC and +DDR_QOS buses in response to memory-latency-bound workloads. It runs on the CPUCP: +every sampling window it reads the per-CPU AMU counters, derives its statistics +(instructions-per-miss, back-end stall, write-back ratio), maps that to a target +level and votes for it directly on the DDR/LLCC/DDR_QOS interconnect. The kernel +never issues a frequency request in that loop. The 6-byte value is treated as a +64-bit algorithm string and split into two uint32 fields on the wire: algo_low +carries its lower 32 bits and algo_high its upper 32 bits. + +With a distinct need to have the memory buses scaling done in SCP in response to +memory-latency-bound workloads, none of the existing SCMI solutions could be used +as-is. MPAM does not apply either: it is not enabled on all of the affected parts +(e.g. Hamoa). The role split between SCP and client driver is described next. + +MEMLAT client driver pseudo-code: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + probe(): + SET_COMMON_EV_MAP # AMU events (all groups) + for each memory group (DDR / LLCC / DDR_QOS): + SET_MEM_GROUP # bind group to interconnect + SET_GRP_EV_MAP # per-group AMU events + for each monitor in the group: + SET_MONITOR # topology, cpumask, name + IPM_CEIL / BE_STALL_FLOOR # per-monitor tuneables + MON_FREQ_MAP # cpufreq -> memfreq map + SET_MIN_FREQ / SET_MAX_FREQ # clamps + SAMPLE_MS # sampling period + SET_EFFECTIVE_FREQ_METHOD # cpu-freq derivation method + START_TIMER # CPUCP now scales autonomously + + devfreq poll (twice per CPUCP sample period): + GET_CUR_FREQ # read voted freq, per monitor + + remove(): + STOP_TIMER + +SCP pseudo-code: +~~~~~~~~~~~~~~~~ + +.. code-block:: text + + every sample_ms: + for each CPU: + sample AMU counters (instructions, cycles, cache-misses, stalls) + derive per-CPU IPM, back-end-stall % and write-back ratio + + for each configured memory group (DDR / LLCC / DDR_QOS): + for each monitor in the group: + best_freq = 0 + for each CPU in the monitor's cpumask: + if CPU is memory-bound (IPM/stall/write-back vs. the + monitor's configured thresholds): + freq = CPU's cpufreq, optionally scaled toward the ceiling + best_freq = max(best_freq, freq) + monitor.target_freq = monitor's cpufreq->memfreq map(best_freq) + + group_vote = max(target_freq of every monitor in the group) + if group_vote changed since the last sample: + vote group_vote onto the group's interconnect path + + GET_CUR_FREQ returns a monitor's last target_freq + START_TIMER / STOP_TIMER: resume / suspend the loop above + +MEMLAT: Supported memory buses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The hw_type field carried in most payloads identifies the memory group: + ++----------+--------------------------------------------------------------+ +|hw_type |Group | ++----------+--------------------------------------------------------------+ +|0 |DDR | ++----------+--------------------------------------------------------------+ +|1 |LLCC | ++----------+--------------------------------------------------------------+ +|2 |DDR_QOS_COMPUTE | ++----------+--------------------------------------------------------------+ +|3 |DDR_QOS_MOBILE | ++----------+--------------------------------------------------------------+ + +All multi-byte fields below are little-endian. mon_idx selects a monitor +within the group (0-based, less than the firmware-supported maximum). All +SET commands return the SCMI status word; on success it carries SUCCESS, on +lookup failure INVALID_PARAMETERS, and on an unknown param_id NOT_SUPPORTED. + +Frequency units are not uniform across commands (kHz, MHz or a raw 0/1 +DDR_QOS level, depending on the param_id); each command documents its own +units below. + +MEMLAT_SET_MEM_GROUP +~~~~~~~~~~~~~~~~~~~~ + +param_id: 0x10 (16) +command: QCOM_SCMI_SET_PARAM + +Allocates a new memory group on the firmware side and binds it to the +underlying interconnect path (DDR / LLCC / DDR_QOS). + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 cpumask |Bitmask of HW CPU IDs that contribute counters to this | +| |group (limited to 32 CPUs). | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier (0 = DDR, 1 = LLCC, | +| |2 = DDR_QOS_COMPUTE, 3 = DDR_QOS_MOBILE). | ++------------------+-----------------------------------------------------------+ +|uint32 mon_type |Reserved for SET_MEM_GROUP (set to 0; populated only on | +| |SET_MONITOR). | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Reserved for SET_MEM_GROUP (set to 0). | ++------------------+-----------------------------------------------------------+ +|char mon_name[20] |Reserved for SET_MEM_GROUP (zero-filled). | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: group allocated. | +| |BUSY: no free memory group slot (the firmware-supported | +| |maximum number of groups is already configured). | ++------------------+-----------------------------------------------------------+ + +MEMLAT_SET_MONITOR +~~~~~~~~~~~~~~~~~~ + +param_id: 0x11 (17) +command: QCOM_SCMI_SET_PARAM + +Adds a monitor (a CPU subset that votes within an already-configured +group). + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 cpumask |Bitmask of HW CPU IDs assigned to this monitor (must be a | +| |subset of the group's cpumask; limited to 32 CPUs). | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier the monitor belongs to. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_type |0 = IPM-based latency monitor, 1 = compute (passive | +| |governor tracking cpufreq map irrespective of IPM) monitor | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Index of the monitor within the group. | ++------------------+-----------------------------------------------------------+ +|char mon_name[20] |Human-readable monitor name (NUL-terminated, used in | +| |firmware log lines). | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: monitor created. | +| |NOT_FOUND: hw_type does not match any configured group, or | +| |the firmware-supported maximum number of monitors already | +| |exist for the group. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_SET_COMMON_EV_MAP +~~~~~~~~~~~~~~~~~~~~~~~~ + +param_id: 0x12 (18) +command: QCOM_SCMI_SET_PARAM + +Configures the common counter IDs (instructions, cycles, stall, etc.) +that the firmware reads on every sample for every CPU. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 num_evs |Number of valid entries in cid[]. | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Set to 0xFF (sentinel for the common-events case). | ++------------------+-----------------------------------------------------------+ +|uint8 cid[] |Array of CPUCP counter IDs indexed by INST/CYC/CONST_CYC/ | +| |FE_STALL/BE_STALL. 0xFF marks an unused slot. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if num_evs exceeds the | +| |firmware-supported maximum. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_SET_GRP_EV_MAP +~~~~~~~~~~~~~~~~~~~~~ + +param_id: 0x13 (19) +command: QCOM_SCMI_SET_PARAM + +Configures the per-group event IDs (cache miss / writeback / access) +used by the IPM and write-back computations for the selected hw_type. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 num_evs |Number of valid entries in cid[]. | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint8 cid[] |Array of CPUCP counter IDs indexed by MISS/WB/ACC. 0xFF | +| |marks an unused slot. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if hw_type is unknown or | +| |num_evs exceeds the firmware-supported maximum. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_ADAPTIVE_LOW_FREQ +~~~~~~~~~~~~~~~~~~~~~~~~ + +param_id: 0x14 (20) +command: QCOM_SCMI_SET_PARAM + +Sets the adaptive low-frequency floor for a group. When the algorithm's +voted frequency falls below this floor the firmware clamps it up to the +adaptive floor instead. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Ignored (group-scoped parameter). | ++------------------+-----------------------------------------------------------+ +|uint32 val |Adaptive low frequency in kHz (converted to MHz by the | +| |firmware). | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if hw_type does not match a | +| |configured group. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_ADAPTIVE_HIGH_FREQ +~~~~~~~~~~~~~~~~~~~~~~~~~ + +param_id: 0x15 (21) +command: QCOM_SCMI_SET_PARAM + +Sets the adaptive high-frequency floor for a group. Units and behaviour +mirror MEMLAT_ADAPTIVE_LOW_FREQ. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Ignored (group-scoped parameter). | ++------------------+-----------------------------------------------------------+ +|uint32 val |Adaptive high frequency in kHz (converted to MHz by the | +| |firmware). | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if hw_type does not match a | +| |configured group. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_GET_ADAPTIVE_CUR_FREQ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +param_id: 0x16 (22) +command: QCOM_SCMI_GET_PARAM + +Reads the group's current adaptive frequency. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if hw_type does not match a | +| |configured group. | ++------------------+-----------------------------------------------------------+ +|uint32 cur_freq |Current adaptive frequency in kHz. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_IPM_CEIL +~~~~~~~~~~~~~~~ + +param_id: 0x17 (23) +command: QCOM_SCMI_SET_PARAM + +Sets the IPM (Instructions-Per-Miss) ceiling for a monitor. CPUs whose +IPM falls at or below this ceiling are considered memory-bound and +contribute their cpufreq into the monitor's voting pool. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Monitor index within the group. | ++------------------+-----------------------------------------------------------+ +|uint32 val |IPM ceiling (instructions per cache miss). | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if (hw_type, mon_idx) does | +| |not match a registered monitor. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_FE_STALL_FLOOR +~~~~~~~~~~~~~~~~~~~~~ + +param_id: 0x18 (24) +command: QCOM_SCMI_SET_PARAM + +Sets the front-end stall floor (in percent) for a monitor. Analogous to +MEMLAT_BE_STALL_FLOOR but for front-end stalls. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Monitor index within the group. | ++------------------+-----------------------------------------------------------+ +|uint32 val |Front-end stall floor in percent (0..100). | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if (hw_type, mon_idx) does | +| |not match a registered monitor. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_BE_STALL_FLOOR +~~~~~~~~~~~~~~~~~~~~~ + +param_id: 0x19 (25) +command: QCOM_SCMI_SET_PARAM + +Sets the back-end stall floor (in percent) for a monitor. CPUs whose +back-end stall percentage is at or above this floor are eligible to have +their cpufreq scaled up by the freq-scale knobs below. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Monitor index within the group. | ++------------------+-----------------------------------------------------------+ +|uint32 val |Back-end stall floor in percent (0..100). | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if (hw_type, mon_idx) does | +| |not match a registered monitor. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_WB_PCT +~~~~~~~~~~~~~ + +param_id: 0x1A (26) +command: QCOM_SCMI_SET_PARAM + +Sets the write-back ratio threshold (in percent) for a monitor. A CPU's +write-back ratio must be at or above this value for the monitor to treat +it as memory-bound. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Monitor index within the group. | ++------------------+-----------------------------------------------------------+ +|uint32 val |Write-back ratio threshold in percent. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if (hw_type, mon_idx) does | +| |not match a registered monitor. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_IPM_FILTER +~~~~~~~~~~~~~~~~~ + +param_id: 0x1B (27) +command: QCOM_SCMI_SET_PARAM + +Sets the IPM filter for a monitor. A CPU's IPM must be at or below this +value (in addition to the write-back check) for the monitor to select it. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Monitor index within the group. | ++------------------+-----------------------------------------------------------+ +|uint32 val |IPM filter (instructions per cache miss). | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if (hw_type, mon_idx) does | +| |not match a registered monitor. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_FREQ_SCALE_PCT +~~~~~~~~~~~~~~~~~~~~~ + +param_id: 0x1C (28) +command: QCOM_SCMI_SET_PARAM + +Sets the frequency-scale percentage for a monitor. When non-zero, a +memory-bound CPU's effective frequency is boosted proportionally to how +far its IPM is below the ceiling, bounded by the scale floor/ceiling +below. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Monitor index within the group. | ++------------------+-----------------------------------------------------------+ +|uint32 val |Frequency-scale factor in percent. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if (hw_type, mon_idx) does | +| |not match a registered monitor. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_FREQ_SCALE_CEIL_MHZ +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +param_id: 0x1D (29) +command: QCOM_SCMI_SET_PARAM + +Sets the upper cpufreq bound (in MHz) for the freq-scale boost above. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Monitor index within the group. | ++------------------+-----------------------------------------------------------+ +|uint32 val |Frequency-scale ceiling in MHz. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if (hw_type, mon_idx) does | +| |not match a registered monitor. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_FREQ_SCALE_FLOOR_MHZ +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +param_id: 0x1E (30) +command: QCOM_SCMI_SET_PARAM + +Sets the lower cpufreq bound (in MHz) for the freq-scale boost above. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Monitor index within the group. | ++------------------+-----------------------------------------------------------+ +|uint32 val |Frequency-scale floor in MHz. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if (hw_type, mon_idx) does | +| |not match a registered monitor. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_SAMPLE_MS +~~~~~~~~~~~~~~~~ + +param_id: 0x1F (31) +command: QCOM_SCMI_SET_PARAM + +Sets the sampling period (in milliseconds) used by the firmware update +loop. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 sample_ms |Sampling period in milliseconds. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_MON_FREQ_MAP +~~~~~~~~~~~~~~~~~~~ + +param_id: 0x20 (32) +command: QCOM_SCMI_SET_PARAM + +Programs the cpufreq to memfreq voting table for a single monitor. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Monitor index within the group. | ++------------------+-----------------------------------------------------------+ +|uint32 nr_rows |Number of valid rows in tbl[] (must not exceed the | +| |firmware-supported maximum). | ++------------------+-----------------------------------------------------------+ +|struct { |Per-row mapping. cpu_freq_mhz is the cpufreq trigger in | +|u16 cpu_freq_mhz; |MHz; mem_freq_mhz is the resulting memfreq vote (MHz for | +|u16 mem_freq_mhz; |DDR/LLCC, a level index 0/1 for DDR_QOS). | +|} tbl[] | | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if (hw_type, mon_idx) is | +| |unknown / OUT_OF_RANGE if nr_rows exceeds the | +| |firmware-supported maximum. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_SET_MIN_FREQ +~~~~~~~~~~~~~~~~~~~ + +param_id: 0x21 (33) +command: QCOM_SCMI_SET_PARAM + +Clamps a monitor's vote to a minimum value. Units are kHz for DDR/LLCC and +raw level index for DDR_QOS. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Monitor index within the group. | ++------------------+-----------------------------------------------------------+ +|uint32 val |Minimum frequency: kHz for DDR/LLCC, level for DDR_QOS. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if (hw_type, mon_idx) does | +| |not match a registered monitor. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_SET_MAX_FREQ +~~~~~~~~~~~~~~~~~~~ + +param_id: 0x22 (34) +command: QCOM_SCMI_SET_PARAM + +Clamps a monitor's vote to a maximum value. Units identical to +MEMLAT_SET_MIN_FREQ. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Monitor index within the group. | ++------------------+-----------------------------------------------------------+ +|uint32 val |Maximum frequency: kHz for DDR/LLCC, level for DDR_QOS. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if (hw_type, mon_idx) does | +| |not match a registered monitor. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_GET_CUR_FREQ +~~~~~~~~~~~~~~~~~~~ + +param_id: 0x23 (35) +command: QCOM_SCMI_GET_PARAM + +Reads the current target frequency that the firmware is voting for the +selected (hw_type, mon_idx) tuple. The response payload is returned in +the same buffer used to send the request, overwriting it on success. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 hw_type |Memory group identifier. | ++------------------+-----------------------------------------------------------+ +|uint32 mon_idx |Monitor index within the group. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if (hw_type, mon_idx) does | +| |not match a registered monitor. | ++------------------+-----------------------------------------------------------+ +|uint32 cur_freq |Current target frequency in kHz for DDR/LLCC; raw level | +| |(0/1) for DDR_QOS. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_START_TIMER +~~~~~~~~~~~~~~~~~~ + +param_id: 0x24 (36) +command: QCOM_SCMI_START_ACTIVITY + +Starts the firmware sampling and voting loop at the configured +sample_ms interval. Has no payload beyond the QCOM_SCMI_START_ACTIVITY +header. + ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: timer started (or was already running). | +| |GENERIC_ERROR: events not yet initialized | +| |(MEMLAT_SET_GRP_EV_MAP not called for any group). | +| |NOT_SUPPORTED: invalid param_id under START_ACTIVITY. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_STOP_TIMER +~~~~~~~~~~~~~~~~~ + +param_id: 0x25 (37) +command: QCOM_SCMI_STOP_ACTIVITY + +Suspends the firmware sampling and voting loop. Has no payload beyond +the QCOM_SCMI_STOP_ACTIVITY header. The configured monitors and freq +maps are retained, so a subsequent MEMLAT_START_TIMER resumes voting +without re-programming. + ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: timer stopped (or was already stopped). | +| |NOT_SUPPORTED: invalid param_id under STOP_ACTIVITY. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_GET_TIMESTAMP +~~~~~~~~~~~~~~~~~~~~ + +param_id: 0x26 (38) +command: QCOM_SCMI_GET_PARAM + +Reads the firmware timestamp (nanoseconds). + ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS. | ++------------------+-----------------------------------------------------------+ +|uint64 tstamp |Firmware timestamp in nanoseconds. | ++------------------+-----------------------------------------------------------+ + +MEMLAT_SET_EFFECTIVE_FREQ_METHOD +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +param_id: 0x27 (39) +command: QCOM_SCMI_SET_PARAM + +Selects the algorithm used to derive the per-CPU effective frequency +from the cycle counters. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 method |0: const-cycles method (CPU cycles / const-cycles, scaled | +| |by the cluster's max frequency). | +| |1: legacy method (CPU cycles / sampling-window time). | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS / INVALID_PARAMETERS if method is not 0 or 1. | ++------------------+-----------------------------------------------------------+ -- 2.34.1 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH RFC 01/10] firmware: arm_scmi: Add SCMI QCOM Generic Extension Protocol documentation 2026-07-24 7:18 ` [PATCH RFC 01/10] firmware: arm_scmi: Add SCMI QCOM Generic Extension Protocol documentation Pragnesh Papaniya @ 2026-07-24 7:27 ` sashiko-bot 2026-07-24 9:13 ` Sudeep Holla 1 sibling, 0 replies; 20+ messages in thread From: sashiko-bot @ 2026-07-24 7:27 UTC (permalink / raw) To: Pragnesh Papaniya; +Cc: devicetree, robh, conor+dt Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: - [Medium] The representation of the MEMLAT algorithm string as a 64-bit integer introduces a byte-ordering flaw that will result in a scrambled string over the wire. - [Medium] The generic protocol acts as an opaque multiplexer (ioctl-like tunnel), bypassing standard SCMI protocol architecture and discovery. - [Medium] The 32-bit `cpumask` field restricts the protocol to 32 CPUs and ambiguously refers to "HW CPU IDs". - [Medium] Using `u16` for `cpu_freq_mhz` and `mem_freq_mhz` truncates frequency values to integer megahertz, causing precision loss and protocol unit inconsistency. -- --- Patch [1]: [PATCH RFC 01/10] firmware: arm_scmi: Add SCMI QCOM Generic Extension Protocol documentation --- commit a6be594dc6c7f4a121d53c66abe50771e714527d Author: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> firmware: arm_scmi: Add SCMI QCOM Generic Extension Protocol documentation This commit adds documentation for the System Control Management Interface (SCMI) Qualcomm Generic Extension Protocol. It describes generic commands to turn on and configure SoC specific algorithms on the SCP, including the MEMLAT memory latency governor. > diff --git a/drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst b/drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst > new file mode 100644 > index 0000000000000..42e327d538414 > --- /dev/null > +++ b/drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst > @@ -0,0 +1,954 @@ [ ... ] > +System Control Management Interface (SCMI) Qualcomm Generic Extension Protocol > +consists of a small set of generic SET/GET/START/STOP commands, which is used to > +turn on/off and configure Qualcomm SoC specific algorithms that run on the SCP. > +Each algorithm is identified through an algorithm string and has an immutable list > +of param_ids. [Severity: Medium] Does this multiplexer design bypass the standard SCMI protocol architecture? By routing distinct algorithms through a single generic SCMI protocol ID (0x80) and dispatching them via algo_low/algo_high tokens, the design circumvents standard SCMI protocol layout. Standard SCMI expects distinct features to occupy their own vendor protocol IDs and utilize standard protocol discovery. [ ... ] > +MEMLAT: Memory Latency algorithm > +________________________________ > + > +The MEMLAT algorithm (0x4d454d4c4154, ASCII "MEMLAT") scales the DDR, LLCC and > +DDR_QOS buses in response to memory-latency-bound workloads. It runs on the CPUCP: [ ... ] > +The 6-byte value is treated as a > +64-bit algorithm string and split into two uint32 fields on the wire: algo_low > +carries its lower 32 bits and algo_high its upper 32 bits. [Severity: Medium] Will this 64-bit integer representation result in a scrambled string over a little-endian SCMI transport? The document defines the algorithm string as a 64-bit integer 0x4d454d4c4154, which is MEMLAT in big-endian hex. Splitting this into algo_low (0x4d4c4154) and algo_high (0x00004d45) means a little-endian transport will send algo_low as 54 41 4C 4D (T, A, L, M) and algo_high as 45 4D 00 00 (E, M, \0, \0). This results in the string TALMEM in memory, rather than MEMLAT. [ ... ] > +MEMLAT_SET_MEM_GROUP > +~~~~~~~~~~~~~~~~~~~~ > + > +param_id: 0x10 (16) > +command: QCOM_SCMI_SET_PARAM > + > +Allocates a new memory group on the firmware side and binds it to the > +underlying interconnect path (DDR / LLCC / DDR_QOS). > + > ++------------------+-----------------------------------------------------------+ > +|Parameters | > ++------------------+-----------------------------------------------------------+ > +|Name |Description | > ++------------------+-----------------------------------------------------------+ > +|uint32 cpumask |Bitmask of HW CPU IDs that contribute counters to this | > +| |group (limited to 32 CPUs). | > ++------------------+-----------------------------------------------------------+ [Severity: Medium] Are HW CPU IDs guaranteed to map to a contiguous 32-bit bitmask? If HW CPU IDs refers to ARM MPIDR values, those IDs are typically sparse and would not fit in a 32-bit bitmask. If it refers to SCMI logical CPU indices, could this be clarified? Additionally, does hardcoding this bitmask to 32 bits limit the protocol on future hardware with more than 32 CPUs? [ ... ] > +MEMLAT_MON_FREQ_MAP > +~~~~~~~~~~~~~~~~~~~ > + > +param_id: 0x20 (32) > +command: QCOM_SCMI_SET_PARAM > + > +Programs the cpufreq to memfreq voting table for a single monitor. > + > ++------------------+-----------------------------------------------------------+ > +|Parameters | > ++------------------+-----------------------------------------------------------+ > +|Name |Description | > ++------------------+-----------------------------------------------------------+ [ ... ] > ++------------------+-----------------------------------------------------------+ > +|struct { |Per-row mapping. cpu_freq_mhz is the cpufreq trigger in | > +|u16 cpu_freq_mhz; |MHz; mem_freq_mhz is the resulting memfreq vote (MHz for | > +|u16 mem_freq_mhz; |DDR/LLCC, a level index 0/1 for DDR_QOS). | > +|} tbl[] | | > ++------------------+-----------------------------------------------------------+ [Severity: Medium] Does using u16 values in megahertz cause precision loss for frequencies that are not perfectly divisible into integer MHz? Other commands in this protocol, such as MEMLAT_SET_MIN_FREQ and MEMLAT_GET_CUR_FREQ, use kHz. Could this struct also use kHz to maintain unit consistency and avoid fractional megahertz truncation? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260724-rfc_v8_scmi_memlat-v1-0-cb732bcff1f4@oss.qualcomm.com?part=1 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH RFC 01/10] firmware: arm_scmi: Add SCMI QCOM Generic Extension Protocol documentation 2026-07-24 7:18 ` [PATCH RFC 01/10] firmware: arm_scmi: Add SCMI QCOM Generic Extension Protocol documentation Pragnesh Papaniya 2026-07-24 7:27 ` sashiko-bot @ 2026-07-24 9:13 ` Sudeep Holla 1 sibling, 0 replies; 20+ messages in thread From: Sudeep Holla @ 2026-07-24 9:13 UTC (permalink / raw) To: Pragnesh Papaniya Cc: Cristian Marussi, Sudeep Holla, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Bjorn Andersson, Konrad Dybcio, Rajendra Nayak, Pankaj Patil, linux-arm-msm, linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm, linux-tegra On Fri, Jul 24, 2026 at 12:48:06PM +0530, Pragnesh Papaniya wrote: > Add System Control Management Interface (SCMI) Qualcomm Generic Extension > Protocol documentation. It consists of a small set of generic SET/GET/ > START/STOP commands, which is used to turn on/off and configure Qualcomm > SoC specific algorithms that run on the SCP. > > It currently only supports MEMLAT (memory latency governor) algorithm. > The immutable pairing of the MEMLAT algorithm string with the supported > param_ids associated with it are documented here. > > Co-developed-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com> > Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com> > Signed-off-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> > --- > .../arm_scmi/vendors/qcom/qcom_generic.rst | 954 +++++++++++++++++++++ > 1 file changed, 954 insertions(+) > > diff --git a/drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst b/drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst > new file mode 100644 > index 000000000000..42e327d53841 > --- /dev/null > +++ b/drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst > @@ -0,0 +1,954 @@ > +.. SPDX-License-Identifier: GPL-2.0 > +.. include:: <isonum.txt> > + > +================================================================================== > +System Control and Management Interface (SCMI) Qualcomm Generic Extension Protocol > +================================================================================== > + > +:Copyright: |copy| Qualcomm Technologies, Inc. and/or its subsidiaries. > + > +:Authors: > + - Sibi Sankar <sibi.sankar@oss.qualcomm.com> > + - Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> > + > +System Control and Management Interface Qualcomm Generic Extension Vendor Protocol > +================================================================================== > + > +System Control Management Interface (SCMI) Qualcomm Generic Extension Protocol > +consists of a small set of generic SET/GET/START/STOP commands, which is used to > +turn on/off and configure Qualcomm SoC specific algorithms that run on the SCP. > +Each algorithm is identified through an algorithm string and has an immutable list > +of param_ids. All supported algorithms (currently just MEMLAT) have their own > +dedicated section and are listed after the generic commands. > + This multiplexer 'N' random algorithn into one single custom SCMI protocol ID (0x80) seems to go against the general SCMI design principle and this seems like a deliberated attempt to circumvent the standard SCMI protocol template. Standard SCMI expects distinct features to occupy their own vendor protocol IDs and utilize standard protocol discovery. I have asked details of algorithm to be listed here atleast few times now and has been constantly ignored. So don't complain if I start ignoring these patches. > +message_id: 0x1 > +protocol_id: 0x80 > + > ++------------------+-----------------------------------------------------------+ > +|Return values | > ++------------------+-----------------------------------------------------------+ > +|Name |Description | > ++------------------+-----------------------------------------------------------+ > +|int32 status |See ARM SCMI Specification for status code definitions. | > ++------------------+-----------------------------------------------------------+ > +|uint32 attributes |Bits[31:16] Reserved, must be set to 0. | > +| |Bits[15:8] Number of agents in the system. Must match the | > +| |value reported by the standard BASE protocol's | > +| |PROTOCOL_ATTRIBUTES response. | Please drop the above, duplication is always recipe for problems. > +| |Bits[7:0] Number of algorithmic strings supported by the | > +| |system. Only "MEMLAT" is currently supported hence it | > +| |returns 1. | > ++------------------+-----------------------------------------------------------+ OK, so this is the only algorithm. > + > +PROTOCOL_MESSAGE_ATTRIBUTES > +~~~~~~~~~~~~~~~~~~~~~~~~~~~ > + > +message_id: 0x2 > +protocol_id: 0x80 > + > ++------------------+-----------------------------------------------------------+ > +|Return values | > ++------------------+-----------------------------------------------------------+ > +|Name |Description | > ++------------------+-----------------------------------------------------------+ > +|int32 status |See ARM SCMI Specification for status code definitions. | > ++------------------+-----------------------------------------------------------+ > +|uint32 attributes |For all message IDs the parameter has a value of 0. | > ++------------------+-----------------------------------------------------------+ > + > +QCOM_SCMI_SET_PARAM > +~~~~~~~~~~~~~~~~~~~ > + > +message_id: 0x10 > +protocol_id: 0x80 > + > ++------------------+-----------------------------------------------------------+ > +|Parameters | > ++------------------+-----------------------------------------------------------+ > +|Name |Description | > ++------------------+-----------------------------------------------------------+ > +|uint32 ext_id |Reserved, must be zero. | > ++------------------+-----------------------------------------------------------+ > +|uint32 algo_low |Lower 32-bit value of the algorithm string. | > ++------------------+-----------------------------------------------------------+ > +|uint32 algo_high |Upper 32-bit value of the algorithm string. | > ++------------------+-----------------------------------------------------------+ > +|uint32 param_id |Serves as the token message id for the algorithm string | > +| |and is used to set various parameters supported by it. | This is too open and can soon become ambiguous. More description on what exactly this token message id is a must. This is not like normal kernel function to keep it this ambiguous, it is a firmware interface which is comparable to the user ABI. > ++------------------+-----------------------------------------------------------+ > +|uint32 buf[] |Serves as the payload for the specified param_id and | > +| |algorithm string pair. The payload size depends on the | > +| |(algorithm string, param_id) pair; see the per-algorithm | > +| |sections below. | Ditto. > ++------------------+-----------------------------------------------------------+ > +|Return values | > ++------------------+-----------------------------------------------------------+ > +|Name |Description | > ++------------------+-----------------------------------------------------------+ > +|int32 status |SUCCESS: if the param_id and buf[] is parsed successfully | > +| |by the chosen algorithm string. | > +| |NOT_SUPPORTED: if the algorithm string does not have any | > +| |matches. | > +| |INVALID_PARAMETERS: if the param_id and the buf[] passed | > +| |is rejected by the algorithm string. | > ++------------------+-----------------------------------------------------------+ > + > +QCOM_SCMI_GET_PARAM > +~~~~~~~~~~~~~~~~~~~ > + > +message_id: 0x11 > +protocol_id: 0x80 > + > ++------------------+-----------------------------------------------------------+ > +|Parameters | > ++------------------+-----------------------------------------------------------+ > +|Name |Description | > ++------------------+-----------------------------------------------------------+ > +|uint32 ext_id |Reserved, must be zero. | > ++------------------+-----------------------------------------------------------+ > +|uint32 algo_low |Lower 32-bit value of the algorithm string. | > ++------------------+-----------------------------------------------------------+ > +|uint32 algo_high |Upper 32-bit value of the algorithm string. | At this point I start to think what is the point of exchanging the whole 8 byte string back and forth instead of simple ID. > ++------------------+-----------------------------------------------------------+ > +|uint32 param_id |Serves as the token message id for the algorithm string. | > ++------------------+-----------------------------------------------------------+ > +|uint32 buf[] |Serves as the payload and store of value for the specified | > +| |param_id and algorithm string pair. The payload size | > +| |depends on the (algorithm string, param_id) pair; see the | > +| |per-algorithm sections below. The response payload is | > +| |returned in the same buffer, overwriting the request | > +| |contents on success. | Ditto as above(too ambiguous, gives no clue on what that is) > ++------------------+-----------------------------------------------------------+ > +|Return values | > ++------------------+-----------------------------------------------------------+ > +|Name |Description | > ++------------------+-----------------------------------------------------------+ > +|int32 status |SUCCESS: if the param_id and buf[] is parsed successfully | > +| |by the chosen algorithm string and the result is copied | > +| |into buf[]. | > +| |NOT_SUPPORTED: if the algorithm string does not have any | > +| |matches. | > +| |INVALID_PARAMETERS: if the param_id and the buf[] passed | > +| |is rejected by the algorithm string. | > ++------------------+-----------------------------------------------------------+ > +|uint32 buf[] |Holds the payload of the result of the query, returned in | > +| |the same buffer used to send the request. Size depends on | > +| |the (algorithm string, param_id) pair. | > ++------------------+-----------------------------------------------------------+ > + > +QCOM_SCMI_START_ACTIVITY > +~~~~~~~~~~~~~~~~~~~~~~~~ > + > +message_id: 0x12 > +protocol_id: 0x80 > + > +The activity to be started is defined by the algorithm string; see the > +per-algorithm sections (e.g. MEMLAT_START_TIMER) for valid param_ids. > + > ++------------------+-----------------------------------------------------------+ > +|Parameters | > ++------------------+-----------------------------------------------------------+ > +|Name |Description | > ++------------------+-----------------------------------------------------------+ > +|uint32 ext_id |Reserved, must be zero. | > ++------------------+-----------------------------------------------------------+ > +|uint32 algo_low |Lower 32-bit value of the algorithm string. | > ++------------------+-----------------------------------------------------------+ > +|uint32 algo_high |Upper 32-bit value of the algorithm string. | > ++------------------+-----------------------------------------------------------+ > +|uint32 param_id |Serves as the token message id for the algorithm string | > +| |and is generally used to start the activity performed by | > +| |the algorithm string. | > ++------------------+-----------------------------------------------------------+ > +|uint32 buf[] |Serves as the payload for the specified param_id and | > +| |algorithm string pair. The payload size depends on the | > +| |(algorithm string, param_id) pair; see the per-algorithm | > +| |sections below. | Ditto > ++------------------+-----------------------------------------------------------+ > +|Return values | > ++------------------+-----------------------------------------------------------+ > +|Name |Description | > ++------------------+-----------------------------------------------------------+ > +|int32 status |SUCCESS: if the activity performed by the algorithm string | > +| |starts successfully, or if it was already running. | > +| |NOT_SUPPORTED: if the algorithm string does not have any | > +| |matches. | > ++------------------+-----------------------------------------------------------+ > + > +QCOM_SCMI_STOP_ACTIVITY > +~~~~~~~~~~~~~~~~~~~~~~~ > + > +message_id: 0x13 > +protocol_id: 0x80 > + > +The activity to be stopped is defined by the algorithm string; see the > +per-algorithm sections (e.g. MEMLAT_STOP_TIMER) for valid param_ids. > + > ++------------------+-----------------------------------------------------------+ > +|Parameters | > ++------------------+-----------------------------------------------------------+ > +|Name |Description | > ++------------------+-----------------------------------------------------------+ > +|uint32 ext_id |Reserved, must be zero. | > ++------------------+-----------------------------------------------------------+ > +|uint32 algo_low |Lower 32-bit value of the algorithm string. | > ++------------------+-----------------------------------------------------------+ > +|uint32 algo_high |Upper 32-bit value of the algorithm string. | > ++------------------+-----------------------------------------------------------+ > +|uint32 param_id |Serves as the token message id for the algorithm string | > +| |and is generally used to stop the activity performed by | > +| |the algorithm string. | > ++------------------+-----------------------------------------------------------+ > +|uint32 buf[] |Serves as the payload for the specified param_id and | > +| |algorithm string pair. The payload size depends on the | > +| |(algorithm string, param_id) pair; see the per-algorithm | > +| |sections below. | Ditto > ++------------------+-----------------------------------------------------------+ > +|Return values | > ++------------------+-----------------------------------------------------------+ > +|Name |Description | > ++------------------+-----------------------------------------------------------+ > +|int32 status |SUCCESS: if the activity performed by the algorithm string | > +| |stops successfully, or if it was not running. | > +| |NOT_SUPPORTED: if the algorithm string does not have any | > +| |matches. | > ++------------------+-----------------------------------------------------------+ > + > +MEMLAT: Memory Latency algorithm > +________________________________ > + > +The MEMLAT algorithm (0x4d454d4c4154, ASCII "MEMLAT") scales the DDR, LLCC and > +DDR_QOS buses in response to memory-latency-bound workloads. It runs on the CPUCP: > +every sampling window it reads the per-CPU AMU counters, derives its statistics > +(instructions-per-miss, back-end stall, write-back ratio), maps that to a target > +level and votes for it directly on the DDR/LLCC/DDR_QOS interconnect. The kernel > +never issues a frequency request in that loop. The 6-byte value is treated as a > +64-bit algorithm string and split into two uint32 fields on the wire: algo_low > +carries its lower 32 bits and algo_high its upper 32 bits. > + > +With a distinct need to have the memory buses scaling done in SCP in response to > +memory-latency-bound workloads, none of the existing SCMI solutions could be used > +as-is. MPAM does not apply either: it is not enabled on all of the affected parts > +(e.g. Hamoa). The role split between SCP and client driver is described next. > + > +MEMLAT client driver pseudo-code: > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > + > +.. code-block:: text > + > + probe(): > + SET_COMMON_EV_MAP # AMU events (all groups) > + for each memory group (DDR / LLCC / DDR_QOS): > + SET_MEM_GROUP # bind group to interconnect > + SET_GRP_EV_MAP # per-group AMU events > + for each monitor in the group: > + SET_MONITOR # topology, cpumask, name > + IPM_CEIL / BE_STALL_FLOOR # per-monitor tuneables > + MON_FREQ_MAP # cpufreq -> memfreq map > + SET_MIN_FREQ / SET_MAX_FREQ # clamps > + SAMPLE_MS # sampling period > + SET_EFFECTIVE_FREQ_METHOD # cpu-freq derivation method > + START_TIMER # CPUCP now scales autonomously > + > + devfreq poll (twice per CPUCP sample period): > + GET_CUR_FREQ # read voted freq, per monitor > + > + remove(): > + STOP_TIMER > + > +SCP pseudo-code: > +~~~~~~~~~~~~~~~~ > + > +.. code-block:: text > + > + every sample_ms: > + for each CPU: > + sample AMU counters (instructions, cycles, cache-misses, stalls) > + derive per-CPU IPM, back-end-stall % and write-back ratio > + > + for each configured memory group (DDR / LLCC / DDR_QOS): > + for each monitor in the group: > + best_freq = 0 > + for each CPU in the monitor's cpumask: > + if CPU is memory-bound (IPM/stall/write-back vs. the > + monitor's configured thresholds): > + freq = CPU's cpufreq, optionally scaled toward the ceiling > + best_freq = max(best_freq, freq) > + monitor.target_freq = monitor's cpufreq->memfreq map(best_freq) > + > + group_vote = max(target_freq of every monitor in the group) > + if group_vote changed since the last sample: > + vote group_vote onto the group's interconnect path > + > + GET_CUR_FREQ returns a monitor's last target_freq > + START_TIMER / STOP_TIMER: resume / suspend the loop above > + > +MEMLAT: Supported memory buses > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > + > +The hw_type field carried in most payloads identifies the memory group: > + > ++----------+--------------------------------------------------------------+ > +|hw_type |Group | > ++----------+--------------------------------------------------------------+ > +|0 |DDR | > ++----------+--------------------------------------------------------------+ > +|1 |LLCC | > ++----------+--------------------------------------------------------------+ > +|2 |DDR_QOS_COMPUTE | > ++----------+--------------------------------------------------------------+ > +|3 |DDR_QOS_MOBILE | > ++----------+--------------------------------------------------------------+ > + > +All multi-byte fields below are little-endian. mon_idx selects a monitor > +within the group (0-based, less than the firmware-supported maximum). All > +SET commands return the SCMI status word; on success it carries SUCCESS, on > +lookup failure INVALID_PARAMETERS, and on an unknown param_id NOT_SUPPORTED. > + > +Frequency units are not uniform across commands (kHz, MHz or a raw 0/1 > +DDR_QOS level, depending on the param_id); each command documents its own > +units below. > + > +MEMLAT_SET_MEM_GROUP > +~~~~~~~~~~~~~~~~~~~~ > + > +param_id: 0x10 (16) Ah so, these are param id for the above commands I mentioned as too ambiguous ? If so, at this point I think it is better to make custom vendor protocol ID 0x80 for MEMLAT and make all these part of it. I think my comment of multiplexing is a bad idea seem to be confirmed here. I will skip the rest as it makes no sense to review it any further. -- Regards, Sudeep ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH RFC 02/10] dt-bindings: firmware: arm,scmi: Add Qualcomm Generic Extension Protocol 2026-07-24 7:18 [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Pragnesh Papaniya 2026-07-24 7:18 ` [PATCH RFC 01/10] firmware: arm_scmi: Add SCMI QCOM Generic Extension Protocol documentation Pragnesh Papaniya @ 2026-07-24 7:18 ` Pragnesh Papaniya 2026-07-24 7:28 ` sashiko-bot 2026-07-24 7:18 ` [PATCH RFC 03/10] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions Pragnesh Papaniya ` (8 subsequent siblings) 10 siblings, 1 reply; 20+ messages in thread From: Pragnesh Papaniya @ 2026-07-24 7:18 UTC (permalink / raw) To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Bjorn Andersson, Konrad Dybcio Cc: Pragnesh Papaniya, Rajendra Nayak, Pankaj Patil, linux-arm-msm, linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm, linux-tegra Add the binding schema for the SCMI Qualcomm Generic Extension Protocol which helps support Bus DVFS on Glymur/Mahua/Hamoa/Purwa/Kaanapali SoCs. Co-developed-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com> Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com> Signed-off-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> --- .../devicetree/bindings/firmware/arm,scmi.yaml | 1 + .../bindings/firmware/qcom,generic-scmi.yaml | 27 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml index 6fc44105f1c3..85f06a6a7452 100644 --- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml +++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml @@ -24,6 +24,7 @@ description: | anyOf: - $ref: /schemas/firmware/nxp,imx95-scmi.yaml + - $ref: /schemas/firmware/qcom,generic-scmi.yaml properties: $nodename: diff --git a/Documentation/devicetree/bindings/firmware/qcom,generic-scmi.yaml b/Documentation/devicetree/bindings/firmware/qcom,generic-scmi.yaml new file mode 100644 index 000000000000..a50e5e8fdccc --- /dev/null +++ b/Documentation/devicetree/bindings/firmware/qcom,generic-scmi.yaml @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/firmware/qcom,generic-scmi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: System Control and Management Interface (SCMI) Qualcomm Generic Extension Protocol + +maintainers: + - Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> + - Sibi Sankar <sibi.sankar@oss.qualcomm.com> + +properties: + protocol@80: + description: + SCMI Qualcomm Generic Extension Protocol exposes Qualcomm SoC specific + algorithms such as Memory Latency (MEMLAT) bound DDR/LLCC/DDR-QOS bus DVFS. + The algorithm runs on the SCP, it can be turned on/off and be configured + through a pairing of algorithm strings with an immutable list param_ids. + $ref: '/schemas/firmware/arm,scmi.yaml#/$defs/protocol-node' + unevaluatedProperties: false + + properties: + reg: + const: 0x80 + +additionalProperties: true -- 2.34.1 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH RFC 02/10] dt-bindings: firmware: arm,scmi: Add Qualcomm Generic Extension Protocol 2026-07-24 7:18 ` [PATCH RFC 02/10] dt-bindings: firmware: arm,scmi: Add Qualcomm Generic Extension Protocol Pragnesh Papaniya @ 2026-07-24 7:28 ` sashiko-bot 0 siblings, 0 replies; 20+ messages in thread From: sashiko-bot @ 2026-07-24 7:28 UTC (permalink / raw) To: Pragnesh Papaniya; +Cc: conor+dt, devicetree, robh Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] Adding `qcom,generic-scmi.yaml` to the `anyOf` block in `arm,scmi.yaml` creates a schema validation bypass that silently masks errors in vendor-specific protocols. -- --- Patch [2]: [PATCH RFC 02/10] dt-bindings: firmware: arm,scmi: Add Qualcomm Generic Extension Protocol --- commit ad9e4b588cb16e4e6566d7094e38434deace551a Author: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> dt-bindings: firmware: arm,scmi: Add Qualcomm Generic Extension Protocol This commit adds the device tree binding schema for the SCMI Qualcomm Generic Extension Protocol. It supports Bus DVFS on Glymur, Mahua, Hamoa, Purwa, and Kaanapali SoCs. > diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml > index 6fc44105f1c37..85f06a6a74520 100644 > --- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml > +++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml > @@ -24,6 +24,7 @@ description: | > > anyOf: > - $ref: /schemas/firmware/nxp,imx95-scmi.yaml > + - $ref: /schemas/firmware/qcom,generic-scmi.yaml [Severity: High] Does adding this schema to the anyOf block create a schema validation bypass for vendor-specific protocols? If a device tree defines an NXP-specific protocol with invalid properties, it will correctly fail the nxp,imx95-scmi.yaml schema. However, won't it then be evaluated against qcom,generic-scmi.yaml? > diff --git a/Documentation/devicetree/bindings/firmware/qcom,generic-scmi.yaml b/Documentation/devicetree/bindings/firmware/qcom,generic-scmi.yaml > new file mode 100644 > index 0000000000000..a50e5e8fdcccc > --- /dev/null > +++ b/Documentation/devicetree/bindings/firmware/qcom,generic-scmi.yaml [ ... ] > + properties: > + reg: > + const: 0x80 > + > +additionalProperties: true Because the QCOM schema specifies additionalProperties: true at its top level and does not define the NXP protocol nodes, it seems it would accept the node and mark its properties as evaluated. Since anyOf requires only one branch to pass, does this cause the overall validation to succeed and silently mask errors in other vendor protocols? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260724-rfc_v8_scmi_memlat-v1-0-cb732bcff1f4@oss.qualcomm.com?part=2 ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH RFC 03/10] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions 2026-07-24 7:18 [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Pragnesh Papaniya 2026-07-24 7:18 ` [PATCH RFC 01/10] firmware: arm_scmi: Add SCMI QCOM Generic Extension Protocol documentation Pragnesh Papaniya 2026-07-24 7:18 ` [PATCH RFC 02/10] dt-bindings: firmware: arm,scmi: Add Qualcomm Generic Extension Protocol Pragnesh Papaniya @ 2026-07-24 7:18 ` Pragnesh Papaniya 2026-07-24 7:30 ` sashiko-bot 2026-07-24 7:18 ` [PATCH RFC 04/10] PM / devfreq: Add new target_freq attribute flag for governors Pragnesh Papaniya ` (7 subsequent siblings) 10 siblings, 1 reply; 20+ messages in thread From: Pragnesh Papaniya @ 2026-07-24 7:18 UTC (permalink / raw) To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Bjorn Andersson, Konrad Dybcio Cc: Pragnesh Papaniya, Rajendra Nayak, Pankaj Patil, linux-arm-msm, linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm, linux-tegra, Amir Vajid, Ramakrishna Gottimukkula From: Sibi Sankar <sibi.sankar@oss.qualcomm.com> The System Control Management Interface (SCMI) Qualcomm Generic Extension Protocol consists of a small set of generic SET/GET/ START/STOP commands, which is used to turn on/off and configure Qualcomm SoC specific algorithms that run on the SCP. Co-developed-by: Amir Vajid <amir.vajid@oss.qualcomm.com> Signed-off-by: Amir Vajid <amir.vajid@oss.qualcomm.com> Co-developed-by: Ramakrishna Gottimukkula <ramakrishna.gottimukkula@oss.qualcomm.com> Signed-off-by: Ramakrishna Gottimukkula <ramakrishna.gottimukkula@oss.qualcomm.com> Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com> Co-developed-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> Signed-off-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> --- MAINTAINERS | 10 ++ drivers/firmware/arm_scmi/Kconfig | 1 + drivers/firmware/arm_scmi/Makefile | 1 + drivers/firmware/arm_scmi/vendors/qcom/Kconfig | 15 ++ drivers/firmware/arm_scmi/vendors/qcom/Makefile | 2 + .../arm_scmi/vendors/qcom/qcom-generic-ext.c | 183 +++++++++++++++++++++ include/linux/scmi_qcom_protocol.h | 37 +++++ 7 files changed, 249 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index cc3c01eecfa2..265eb99bdc7a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26487,6 +26487,16 @@ S: Maintained F: Documentation/devicetree/bindings/firmware/nxp,*scmi.yaml F: drivers/firmware/arm_scmi/vendors/imx/ +SYSTEM CONTROL MANAGEMENT INTERFACE (SCMI) Qualcomm Generic Extension Protocol driver +M: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> +M: Sibi Sankar <sibi.sankar@oss.qualcomm.com> +L: arm-scmi@vger.kernel.org +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/firmware/qcom,generic-scmi.yaml +F: drivers/firmware/arm_scmi/vendors/qcom/ +F: include/linux/scmi_qcom_protocol.h + SYSTEM RESET/SHUTDOWN DRIVERS M: Sebastian Reichel <sre@kernel.org> L: linux-pm@vger.kernel.org diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig index 783c24a20e29..3918aa2e7140 100644 --- a/drivers/firmware/arm_scmi/Kconfig +++ b/drivers/firmware/arm_scmi/Kconfig @@ -84,6 +84,7 @@ config ARM_SCMI_QUIRKS source "drivers/firmware/arm_scmi/transports/Kconfig" source "drivers/firmware/arm_scmi/vendors/imx/Kconfig" +source "drivers/firmware/arm_scmi/vendors/qcom/Kconfig" endif #ARM_SCMI_PROTOCOL diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index 780cd62b2f78..5a0e003c2477 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -13,6 +13,7 @@ scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transport-y) obj-$(CONFIG_ARM_SCMI_PROTOCOL) += transports/ obj-$(CONFIG_ARM_SCMI_PROTOCOL) += vendors/imx/ +obj-$(CONFIG_ARM_SCMI_PROTOCOL) += vendors/qcom/ obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o diff --git a/drivers/firmware/arm_scmi/vendors/qcom/Kconfig b/drivers/firmware/arm_scmi/vendors/qcom/Kconfig new file mode 100644 index 000000000000..8aa0efd6a03d --- /dev/null +++ b/drivers/firmware/arm_scmi/vendors/qcom/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +menu "ARM SCMI QCOM Vendor Protocols" + +config QCOM_SCMI_GENERIC_EXT + tristate "Qualcomm Technologies, Inc. SCMI Generic Vendor Protocol" + depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF) + help + The QCOM SCMI vendor protocol provides a generic way of exposing + a number of Qualcomm SoC specific features (like memory bus scaling) + through a mixture of pre-determined algorithm strings and param_id + pairs hosted on the SCMI controller. + + This driver defines/documents the message IDs used for this + communication and also exposes the operations used by the clients. +endmenu diff --git a/drivers/firmware/arm_scmi/vendors/qcom/Makefile b/drivers/firmware/arm_scmi/vendors/qcom/Makefile new file mode 100644 index 000000000000..6b98fabbebb8 --- /dev/null +++ b/drivers/firmware/arm_scmi/vendors/qcom/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_QCOM_SCMI_GENERIC_EXT) += qcom-generic-ext.o diff --git a/drivers/firmware/arm_scmi/vendors/qcom/qcom-generic-ext.c b/drivers/firmware/arm_scmi/vendors/qcom/qcom-generic-ext.c new file mode 100644 index 000000000000..7ca920c8da05 --- /dev/null +++ b/drivers/firmware/arm_scmi/vendors/qcom/qcom-generic-ext.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/module.h> +#include <linux/scmi_qcom_protocol.h> +#include <linux/string.h> +#include <linux/stringify.h> +#include <linux/types.h> + +#include "../../common.h" + +/* + * This protocol is intended as a generic way of exposing a number of Qualcomm + * SoC specific features through a mixture of pre-determined algorithm string + * and param_id pairs hosted on the SCMI controller. + * + * The QCOM SCMI Vendor Protocol has the protocol id as 0x80 and vendor id set + * to Qualcomm and the supported version is set to 0x10000. The PROTOCOL_VERSION + * command returns version 1.0. + */ + +/** + * enum qcom_generic_ext_protocol_cmd - vendor specific commands supported by SCMI Qualcomm + * generic vendor protocol. + * + * @QCOM_SCMI_SET_PARAM: is used to set the parameter of a specific algo_str hosted on + * QCOM SCMI Vendor Protocol. The tx len depends on the algo_str used. + * @QCOM_SCMI_GET_PARAM: is used to get parameter information of a specific algo_str + * hosted on QCOM SCMI Vendor Protocol. The tx and rx len depends + * on the algo_str used. + * @QCOM_SCMI_START_ACTIVITY: is used to start the activity performed by the algo_str. + * @QCOM_SCMI_STOP_ACTIVITY: is used to stop a pre-existing activity performed by the algo_str. + */ +enum qcom_generic_ext_protocol_cmd { + QCOM_SCMI_SET_PARAM = 0x10, + QCOM_SCMI_GET_PARAM = 0x11, + QCOM_SCMI_START_ACTIVITY = 0x12, + QCOM_SCMI_STOP_ACTIVITY = 0x13, +}; + +/** + * struct qcom_scmi_msg - represents the various parameters to be populated + * for using the QCOM SCMI Vendor Protocol + * + * @ext_id: reserved, must be zero + * @algo_low: lower 32 bits of the algo_str + * @algo_high: upper 32 bits of the algo_str + * @param_id: serves as token message id to the specific algo_str + * @buf: serves as the payload to the specified param_id and algo_str pair + */ +struct qcom_scmi_msg { + __le32 ext_id; + __le32 algo_low; + __le32 algo_high; + __le32 param_id; + __le32 buf[]; +}; + +/* + * The firmware only implements a handful of algorithm strings. Keep an + * allowlist so a client cannot push an unsupported (or garbage) string to + * the firmware; extend it as new algorithms are added. + */ +static const u64 qcom_scmi_algo_str[] = { + 0x4d454d4c4154ULL, /* "MEMLAT" */ +}; + +static bool qcom_scmi_algo_str_valid(u64 algo_str) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(qcom_scmi_algo_str); i++) + if (algo_str == qcom_scmi_algo_str[i]) + return true; + + return false; +} + +static int qcom_scmi_common_xfer(const struct scmi_protocol_handle *ph, + enum qcom_generic_ext_protocol_cmd cmd_id, void *buf, + size_t buf_len, u64 algo_str, u32 param_id, size_t rx_size) +{ + struct scmi_xfer *t; + struct qcom_scmi_msg *msg; + int ret; + + if (!qcom_scmi_algo_str_valid(algo_str)) + return -EINVAL; + + /* Reject calls where rx_size exceeds buf_len. */ + if (rx_size > buf_len) + return -EINVAL; + + ret = ph->xops->xfer_get_init(ph, cmd_id, buf_len + sizeof(*msg), rx_size, &t); + if (ret) + return ret; + + msg = t->tx.buf; + msg->ext_id = 0; + msg->algo_low = cpu_to_le32(lower_32_bits(algo_str)); + msg->algo_high = cpu_to_le32(upper_32_bits(algo_str)); + msg->param_id = cpu_to_le32(param_id); + if (buf_len) + memcpy(msg->buf, buf, buf_len); + + ret = ph->xops->do_xfer(ph, t); + if (!ret && rx_size) { + /* + * The response is returned into the caller's @buf, replacing + * the tx payload. Bound the copy to the caller's buffer since + * t->rx.len is reported by the firmware, and require at least + * the expected rx_size so the caller never reads stale tx data. + */ + if (t->rx.len < rx_size || t->rx.len > buf_len) + ret = -EPROTO; + else + memcpy(buf, t->rx.buf, rx_size); + } + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int qcom_scmi_set_param(const struct scmi_protocol_handle *ph, void *buf, size_t buf_len, + u64 algo_str, u32 param_id) +{ + return qcom_scmi_common_xfer(ph, QCOM_SCMI_SET_PARAM, buf, buf_len, algo_str, + param_id, 0); +} + +static int qcom_scmi_get_param(const struct scmi_protocol_handle *ph, void *buf, size_t buf_len, + u64 algo_str, u32 param_id, size_t rx_size) +{ + return qcom_scmi_common_xfer(ph, QCOM_SCMI_GET_PARAM, buf, buf_len, algo_str, + param_id, rx_size); +} + +static int qcom_scmi_start_activity(const struct scmi_protocol_handle *ph, void *buf, + size_t buf_len, u64 algo_str, u32 param_id) +{ + return qcom_scmi_common_xfer(ph, QCOM_SCMI_START_ACTIVITY, buf, buf_len, algo_str, + param_id, 0); +} + +static int qcom_scmi_stop_activity(const struct scmi_protocol_handle *ph, void *buf, + size_t buf_len, u64 algo_str, u32 param_id) +{ + return qcom_scmi_common_xfer(ph, QCOM_SCMI_STOP_ACTIVITY, buf, buf_len, algo_str, + param_id, 0); +} + +static const struct qcom_generic_ext_ops qcom_proto_ops = { + .set_param = qcom_scmi_set_param, + .get_param = qcom_scmi_get_param, + .start_activity = qcom_scmi_start_activity, + .stop_activity = qcom_scmi_stop_activity, +}; + +static int qcom_generic_ext_protocol_init(const struct scmi_protocol_handle *ph) +{ + dev_dbg(ph->dev, "QCOM Generic Vendor Version %d.%d\n", + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); + + return 0; +} + +static const struct scmi_protocol qcom_generic_ext = { + .id = SCMI_PROTOCOL_QCOM_GENERIC, + .owner = THIS_MODULE, + .instance_init = &qcom_generic_ext_protocol_init, + .ops = &qcom_proto_ops, + .vendor_id = "Qualcomm", + .supported_version = 0x10000, +}; +module_scmi_protocol(qcom_generic_ext); + +MODULE_ALIAS("scmi-protocol-" __stringify(SCMI_PROTOCOL_QCOM_GENERIC) "-Qualcomm"); +MODULE_AUTHOR("Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com>"); +MODULE_AUTHOR("Sibi Sankar <sibi.sankar@oss.qualcomm.com>"); +MODULE_DESCRIPTION("QCOM SCMI Generic Vendor Protocol"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/scmi_qcom_protocol.h b/include/linux/scmi_qcom_protocol.h new file mode 100644 index 000000000000..41632ee7bbc7 --- /dev/null +++ b/include/linux/scmi_qcom_protocol.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * SCMI Message Protocol driver QCOM extension header + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _LINUX_SCMI_QCOM_PROTOCOL_H +#define _LINUX_SCMI_QCOM_PROTOCOL_H + +#include <linux/types.h> + +#define SCMI_PROTOCOL_QCOM_GENERIC 0x80 + +struct scmi_protocol_handle; + +/** + * struct qcom_generic_ext_ops - represents the various operations provided + * by QCOM Generic Vendor Protocol + * + * @set_param: set parameter specified by param_id and algo_str pair. + * @get_param: retrieve parameter specified by param_id and algo_str pair. + * @start_activity: initiate a specific activity defined by algo_str. + * @stop_activity: halt previously initiated activity defined by algo_str. + */ +struct qcom_generic_ext_ops { + int (*set_param)(const struct scmi_protocol_handle *ph, void *buf, size_t buf_len, + u64 algo_str, u32 param_id); + int (*get_param)(const struct scmi_protocol_handle *ph, void *buf, size_t buf_len, + u64 algo_str, u32 param_id, size_t rx_size); + int (*start_activity)(const struct scmi_protocol_handle *ph, void *buf, size_t buf_len, + u64 algo_str, u32 param_id); + int (*stop_activity)(const struct scmi_protocol_handle *ph, void *buf, size_t buf_len, + u64 algo_str, u32 param_id); +}; + +#endif /* _LINUX_SCMI_QCOM_PROTOCOL_H */ -- 2.34.1 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH RFC 03/10] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions 2026-07-24 7:18 ` [PATCH RFC 03/10] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions Pragnesh Papaniya @ 2026-07-24 7:30 ` sashiko-bot 0 siblings, 0 replies; 20+ messages in thread From: sashiko-bot @ 2026-07-24 7:30 UTC (permalink / raw) To: Pragnesh Papaniya; +Cc: robh, devicetree, conor+dt Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] The API tightly couples TX payload size and RX buffer capacity into a single `buf_len` parameter, breaking commands where RX size exceeds TX size and leaking uninitialized kernel memory. - [High] An integer overflow in the transmit size calculation bypasses SCMI core bounds checks, leading to an out-of-bounds write. - [Medium] The 64-bit constant representing the "MEMLAT" algorithm string is incorrectly split and endian-swapped, resulting in the mangled string "TALMEM\0\0" being laid out in memory. -- --- Patch [3]: [PATCH RFC 03/10] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions --- commit 9dcfeae2ae27c1a2917b7de276eae7604cac163d Author: Sibi Sankar <sibi.sankar@oss.qualcomm.com> firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions The System Control Management Interface (SCMI) Qualcomm Generic Extension Protocol consists of a small set of generic SET/GET/ START/STOP commands, which is used to turn on/off and configure Qualcomm SoC specific algorithms that run on the SCP. > diff --git a/drivers/firmware/arm_scmi/vendors/qcom/qcom-generic-ext.c b/drivers/firmware/arm_scmi/vendors/qcom/qcom-generic-ext.c > new file mode 100644 > index 0000000000000..7ca920c8da059 > --- /dev/null > +++ b/drivers/firmware/arm_scmi/vendors/qcom/qcom-generic-ext.c > @@ -0,0 +1,183 @@ [ ... ] > +/* > + * The firmware only implements a handful of algorithm strings. Keep an > + * allowlist so a client cannot push an unsupported (or garbage) string to > + * the firmware; extend it as new algorithms are added. > + */ > +static const u64 qcom_scmi_algo_str[] = { > + 0x4d454d4c4154ULL, /* "MEMLAT" */ > +}; [ ... ] > +static int qcom_scmi_common_xfer(const struct scmi_protocol_handle *ph, > + enum qcom_generic_ext_protocol_cmd cmd_id, void *buf, > + size_t buf_len, u64 algo_str, u32 param_id, size_t rx_size) > +{ > + struct scmi_xfer *t; > + struct qcom_scmi_msg *msg; > + int ret; > + > + if (!qcom_scmi_algo_str_valid(algo_str)) > + return -EINVAL; > + > + /* Reject calls where rx_size exceeds buf_len. */ > + if (rx_size > buf_len) > + return -EINVAL; > + > + ret = ph->xops->xfer_get_init(ph, cmd_id, buf_len + sizeof(*msg), rx_size, &t); [Severity: High] Can buf_len + sizeof(*msg) overflow? If a caller passes a very large buf_len, could the addition wrap around to a small positive integer? This would bypass the max_msg_size bounds check in xfer_get_init(), leading to an out-of-bounds write in the subsequent memcpy() call. > + if (ret) > + return ret; > + > + msg = t->tx.buf; > + msg->ext_id = 0; > + msg->algo_low = cpu_to_le32(lower_32_bits(algo_str)); > + msg->algo_high = cpu_to_le32(upper_32_bits(algo_str)); [Severity: Medium] Does the firmware expect this string as an 8-byte character array? The constant 0x4d454d4c4154ULL represents "MEMLAT" in big-endian hex. When this is split into algo_low and algo_high and passed through cpu_to_le32(), the resulting little-endian memory layout becomes 0x54 0x41 0x4C 0x4D ("TALM") followed by 0x45 0x4D 0x00 0x00 ("EM\0\0"). Will the firmware correctly reconstruct the 64-bit integer, or might it reject the mangled string "TALMEM\0\0" if it reads it as a character array? > + msg->param_id = cpu_to_le32(param_id); > + if (buf_len) > + memcpy(msg->buf, buf, buf_len); [Severity: High] If a caller uses qcom_scmi_get_param() expecting an RX payload but has no TX payload, doesn't buf_len need to be at least rx_size to pass the validation check at the top of this function? If buf_len is artificially inflated to satisfy rx_size <= buf_len, won't this memcpy() read buf_len bytes from the caller's buffer into the TX payload, potentially leaking uninitialized kernel memory to the firmware? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260724-rfc_v8_scmi_memlat-v1-0-cb732bcff1f4@oss.qualcomm.com?part=3 ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH RFC 04/10] PM / devfreq: Add new target_freq attribute flag for governors 2026-07-24 7:18 [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Pragnesh Papaniya ` (2 preceding siblings ...) 2026-07-24 7:18 ` [PATCH RFC 03/10] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions Pragnesh Papaniya @ 2026-07-24 7:18 ` Pragnesh Papaniya 2026-07-24 7:35 ` sashiko-bot 2026-07-24 7:18 ` [PATCH RFC 05/10] PM / devfreq: Add new track_remote " Pragnesh Papaniya ` (6 subsequent siblings) 10 siblings, 1 reply; 20+ messages in thread From: Pragnesh Papaniya @ 2026-07-24 7:18 UTC (permalink / raw) To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Bjorn Andersson, Konrad Dybcio Cc: Pragnesh Papaniya, Rajendra Nayak, Pankaj Patil, linux-arm-msm, linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm, linux-tegra From: Sibi Sankar <sibi.sankar@oss.qualcomm.com> The target_freq sysfs attribute exposes a governor's predicted next target frequency. Not every devfreq governor has a meaningful value to report there; some merely observe an externally-driven device. Add a DEVFREQ_GOV_ATTR_TARGET_FREQ attribute flag that governors with a meaningful target frequency must opt in to. Gate the existing target_freq sysfs read on the flag and return -EINVAL when the active governor does not advertise it. Tag all in-tree governors so visible behaviour stays unchanged for in-tree users. Out-of-tree governors that drive frequency updates and want to keep target_freq readable need to set the new flag. Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com> Co-developed-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> Signed-off-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> --- Documentation/ABI/testing/sysfs-class-devfreq | 8 ++++++++ drivers/devfreq/devfreq.c | 6 ++++++ drivers/devfreq/governor_passive.c | 1 + drivers/devfreq/governor_performance.c | 1 + drivers/devfreq/governor_powersave.c | 1 + drivers/devfreq/governor_simpleondemand.c | 1 + drivers/devfreq/governor_userspace.c | 1 + drivers/devfreq/hisi_uncore_freq.c | 1 + drivers/devfreq/tegra30-devfreq.c | 3 ++- include/linux/devfreq-governor.h | 3 +++ 10 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index df8ba88b9f6a..5be9940a5853 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -37,6 +37,14 @@ Description: The /sys/class/devfreq/.../target_freq shows the next governor predicted target frequency of the corresponding devfreq object. + Reading this attribute returns -EINVAL when the active + governor does not advertise DEVFREQ_GOV_ATTR_TARGET_FREQ. + All in-tree governors that drive frequency transitions tag + this attribute, so existing in-tree behaviour is unchanged. + Out-of-tree governors that previously relied on the + unconditional read of df->previous_freq must opt in by + setting DEVFREQ_GOV_ATTR_TARGET_FREQ in their attrs field. + What: /sys/class/devfreq/.../trans_stat Date: October 2012 Contact: MyungJoo Ham <myungjoo.ham@samsung.com> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index f08fc6966eae..2f27a239e34a 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -1530,6 +1530,12 @@ static ssize_t target_freq_show(struct device *dev, { struct devfreq *df = to_devfreq(dev); + guard(mutex)(&devfreq_list_lock); + + if (!df->profile || !df->governor || + !IS_SUPPORTED_ATTR(df->governor->attrs, TARGET_FREQ)) + return -EINVAL; + return sprintf(buf, "%lu\n", df->previous_freq); } static DEVICE_ATTR_RO(target_freq); diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c index d7feecd900f1..b75e4bbee4b1 100644 --- a/drivers/devfreq/governor_passive.c +++ b/drivers/devfreq/governor_passive.c @@ -448,6 +448,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq, static struct devfreq_governor devfreq_passive = { .name = DEVFREQ_GOV_PASSIVE, + .attrs = DEVFREQ_GOV_ATTR_TARGET_FREQ, .flags = DEVFREQ_GOV_FLAG_IMMUTABLE, .get_target_freq = devfreq_passive_get_target_freq, .event_handler = devfreq_passive_event_handler, diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c index fdb22bf512cf..b9ec587f582c 100644 --- a/drivers/devfreq/governor_performance.c +++ b/drivers/devfreq/governor_performance.c @@ -37,6 +37,7 @@ static int devfreq_performance_handler(struct devfreq *devfreq, static struct devfreq_governor devfreq_performance = { .name = DEVFREQ_GOV_PERFORMANCE, + .attrs = DEVFREQ_GOV_ATTR_TARGET_FREQ, .get_target_freq = devfreq_performance_func, .event_handler = devfreq_performance_handler, }; diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c index ee2d6ec8a512..69eab1d0a7fc 100644 --- a/drivers/devfreq/governor_powersave.c +++ b/drivers/devfreq/governor_powersave.c @@ -37,6 +37,7 @@ static int devfreq_powersave_handler(struct devfreq *devfreq, static struct devfreq_governor devfreq_powersave = { .name = DEVFREQ_GOV_POWERSAVE, + .attrs = DEVFREQ_GOV_ATTR_TARGET_FREQ, .get_target_freq = devfreq_powersave_func, .event_handler = devfreq_powersave_handler, }; diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index ac9c5e9e51a4..65ff9d912ef9 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -118,6 +118,7 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq, static struct devfreq_governor devfreq_simple_ondemand = { .name = DEVFREQ_GOV_SIMPLE_ONDEMAND, .attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL + | DEVFREQ_GOV_ATTR_TARGET_FREQ | DEVFREQ_GOV_ATTR_TIMER, .get_target_freq = devfreq_simple_ondemand_func, .event_handler = devfreq_simple_ondemand_handler, diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c index 3906ebedbae8..d1b765a7b8e5 100644 --- a/drivers/devfreq/governor_userspace.c +++ b/drivers/devfreq/governor_userspace.c @@ -135,6 +135,7 @@ static int devfreq_userspace_handler(struct devfreq *devfreq, static struct devfreq_governor devfreq_userspace = { .name = DEVFREQ_GOV_USERSPACE, + .attrs = DEVFREQ_GOV_ATTR_TARGET_FREQ, .get_target_freq = devfreq_userspace_func, .event_handler = devfreq_userspace_handler, }; diff --git a/drivers/devfreq/hisi_uncore_freq.c b/drivers/devfreq/hisi_uncore_freq.c index e1f64b723082..5a2d7e011ad1 100644 --- a/drivers/devfreq/hisi_uncore_freq.c +++ b/drivers/devfreq/hisi_uncore_freq.c @@ -398,6 +398,7 @@ static struct devfreq_governor hisi_platform_governor = { * Set interrupt_driven to skip the devfreq monitor mechanism, though * this governor is not interrupt-driven. */ + .attrs = DEVFREQ_GOV_ATTR_TARGET_FREQ, .flags = DEVFREQ_GOV_FLAG_IRQ_DRIVEN, .get_target_freq = hisi_platform_gov_func, .event_handler = hisi_platform_gov_handler, diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c index 401aac6a9f07..fcb278c4a74c 100644 --- a/drivers/devfreq/tegra30-devfreq.c +++ b/drivers/devfreq/tegra30-devfreq.c @@ -776,7 +776,8 @@ static int tegra_governor_event_handler(struct devfreq *devfreq, static struct devfreq_governor tegra_devfreq_governor = { .name = "tegra_actmon", - .attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL, + .attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL + | DEVFREQ_GOV_ATTR_TARGET_FREQ, .flags = DEVFREQ_GOV_FLAG_IMMUTABLE | DEVFREQ_GOV_FLAG_IRQ_DRIVEN, .get_target_freq = tegra_governor_get_target, diff --git a/include/linux/devfreq-governor.h b/include/linux/devfreq-governor.h index dfdd0160a29f..2853f571dfdf 100644 --- a/include/linux/devfreq-governor.h +++ b/include/linux/devfreq-governor.h @@ -43,9 +43,12 @@ * : Indicate polling_interval sysfs attribute * - DEVFREQ_GOV_ATTR_TIMER * : Indicate timer sysfs attribute + * - DEVFREQ_GOV_ATTR_TARGET_FREQ + * : Indicate the target freq sysfs attribute */ #define DEVFREQ_GOV_ATTR_POLLING_INTERVAL BIT(0) #define DEVFREQ_GOV_ATTR_TIMER BIT(1) +#define DEVFREQ_GOV_ATTR_TARGET_FREQ BIT(2) /** * struct devfreq_governor - Devfreq policy governor -- 2.34.1 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH RFC 04/10] PM / devfreq: Add new target_freq attribute flag for governors 2026-07-24 7:18 ` [PATCH RFC 04/10] PM / devfreq: Add new target_freq attribute flag for governors Pragnesh Papaniya @ 2026-07-24 7:35 ` sashiko-bot 0 siblings, 0 replies; 20+ messages in thread From: sashiko-bot @ 2026-07-24 7:35 UTC (permalink / raw) To: Pragnesh Papaniya; +Cc: devicetree, robh, conor+dt Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: Pre-existing issues: - [High] An ABBA deadlock between kernfs active references and `devfreq_list_lock` can occur when a sysfs attribute in `gov_attr_group` is accessed concurrently with a governor switch. - [High] Reading the `governor` sysfs attribute can cause a Use-After-Free due to a missing lock. -- --- Patch [4]: [PATCH RFC 04/10] PM / devfreq: Add new target_freq attribute flag for governors --- commit 270fe8b349c3594b09be3beb4f4386f10ac713aa Author: Sibi Sankar <sibi.sankar@oss.qualcomm.com> PM / devfreq: Add new target_freq attribute flag for governors This commit adds a new DEVFREQ_GOV_ATTR_TARGET_FREQ flag for devfreq governors. It gates the existing target_freq sysfs read on this flag, returning -EINVAL if the active governor does not advertise it. > diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c > index f08fc6966eaee..2f27a239e34a6 100644 > --- a/drivers/devfreq/devfreq.c > +++ b/drivers/devfreq/devfreq.c > @@ -1530,6 +1530,12 @@ static ssize_t target_freq_show(struct device *dev, > { > struct devfreq *df = to_devfreq(dev); > > + guard(mutex)(&devfreq_list_lock); > + [Severity: High] This isn't a bug introduced by this patch, but since we are modifying the devfreq sysfs locking here, could there be an ABBA deadlock between kernfs active references and devfreq_list_lock involving the governor attributes? If a userspace process writes to the governor sysfs file, governor_store() takes devfreq_list_lock and calls sysfs_update_group(): drivers/devfreq/devfreq.c:governor_store() { ... ret = sysfs_update_group(&df->dev.kobj, &gov_attr_group); ... } This causes create_files() to unconditionally remove the group's attributes (like timer) via kernfs_remove_by_name(), which waits for all active references to drain: fs/sysfs/group.c:create_files() { ... kernfs_remove_by_name(parent, (*attr)->name); ... } Concurrently, if another userspace process reads the timer sysfs file: drivers/devfreq/devfreq.c:timer_show() { ... guard(mutex)(&devfreq_list_lock); ... } The sysfs core holds the active reference to the timer node and calls timer_show(), which blocks trying to acquire devfreq_list_lock, completing the ABBA deadlock. [Severity: High] This is a pre-existing issue, but while we are adding devfreq_list_lock here to safely access the governor, does governor_show() have a Use-After-Free risk because it lacks this lock? If governor_show() accesses df->governor without holding devfreq_list_lock: drivers/devfreq/devfreq.c:governor_show() { ... return sprintf(buf, "%s\n", df->governor->name); } Concurrently, if a governor module is unloaded, devfreq_remove_governor() acquires devfreq_list_lock, stops the governor, and clears df->governor. After it returns, the module memory is freed. If governor_show() evaluates df->governor as non-NULL before it's cleared, gets preempted, and then resumes after the module is freed, could the dereference of df->governor->name access freed memory? > + if (!df->profile || !df->governor || > + !IS_SUPPORTED_ATTR(df->governor->attrs, TARGET_FREQ)) > + return -EINVAL; > + > return sprintf(buf, "%lu\n", df->previous_freq); > } [ ... ] -- Sashiko AI review · https://sashiko.dev/#/patchset/20260724-rfc_v8_scmi_memlat-v1-0-cb732bcff1f4@oss.qualcomm.com?part=4 ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH RFC 05/10] PM / devfreq: Add new track_remote flag for governors 2026-07-24 7:18 [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Pragnesh Papaniya ` (3 preceding siblings ...) 2026-07-24 7:18 ` [PATCH RFC 04/10] PM / devfreq: Add new target_freq attribute flag for governors Pragnesh Papaniya @ 2026-07-24 7:18 ` Pragnesh Papaniya 2026-07-24 7:31 ` sashiko-bot 2026-07-24 7:18 ` [PATCH RFC 06/10] PM / devfreq: Add a governor for tracking remote device frequencies Pragnesh Papaniya ` (5 subsequent siblings) 10 siblings, 1 reply; 20+ messages in thread From: Pragnesh Papaniya @ 2026-07-24 7:18 UTC (permalink / raw) To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Bjorn Andersson, Konrad Dybcio Cc: Pragnesh Papaniya, Rajendra Nayak, Pankaj Patil, linux-arm-msm, linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm, linux-tegra From: Sibi Sankar <sibi.sankar@oss.qualcomm.com> Some devfreq governors need to track frequency changes performed on remote devices rather than driving the frequency updates themselves. In such cases, the device's frequency is already updated by an external entity and devfreq only needs to keep its transition statistics and notifier subscribers in sync. Add a new DEVFREQ_GOV_FLAG_TRACK_REMOTE governor flag. When set, devfreq_set_target() skips the profile->target() update sequence but still emits the DEVFREQ_PRECHANGE/DEVFREQ_POSTCHANGE notification pair and the devfreq_frequency trace event, so transition-notifier subscribers (passive governor, devfreq cooling, etc.) and tracing observe the remote frequency change exactly as they would on the normal path, and the transition statistics are refreshed. Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com> Co-developed-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> Signed-off-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> --- drivers/devfreq/devfreq.c | 21 +++++++++++++++++++++ include/linux/devfreq-governor.h | 5 +++++ 2 files changed, 26 insertions(+) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 2f27a239e34a..71c6ced9e4e1 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -348,6 +348,26 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, unsigned long cur_freq; int err = 0; + /* + * When a remote agent (e.g. firmware) owns the frequency, skip the + * local profile->target() call. Still emit the PRECHANGE/POSTCHANGE + * pair and the trace event so that transition-notifier subscribers + * (passive governor, devfreq cooling, etc.) and tracing observe the + * frequency change exactly as they would on the normal path. + */ + if (devfreq->governor && + IS_SUPPORTED_FLAG(devfreq->governor->flags, TRACK_REMOTE)) { + freqs.old = devfreq->previous_freq; + freqs.new = new_freq; + devfreq_notify_transition(devfreq, &freqs, DEVFREQ_PRECHANGE); + + if (trace_devfreq_frequency_enabled() && new_freq != freqs.old) + trace_devfreq_frequency(devfreq, new_freq, freqs.old); + + devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); + goto update_status; + } + if (devfreq->profile->get_cur_freq) devfreq->profile->get_cur_freq(devfreq->dev.parent, &cur_freq); else @@ -375,6 +395,7 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, freqs.new = new_freq; devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); +update_status: if (devfreq_update_status(devfreq, new_freq)) dev_warn(&devfreq->dev, "Couldn't update frequency transition information.\n"); diff --git a/include/linux/devfreq-governor.h b/include/linux/devfreq-governor.h index 2853f571dfdf..e4f7b6cb07bc 100644 --- a/include/linux/devfreq-governor.h +++ b/include/linux/devfreq-governor.h @@ -33,9 +33,14 @@ * : This governor is never changeable to other governors. * - DEVFREQ_GOV_FLAG_IRQ_DRIVEN * : The devfreq won't schedule the work for this governor. + * - DEVFREQ_GOV_FLAG_TRACK_REMOTE + * : The governor only tracks frequency changes performed by a remote + * agent (e.g. firmware); devfreq skips the local profile->target() + * call and just keeps its statistics and notifiers in sync. */ #define DEVFREQ_GOV_FLAG_IMMUTABLE BIT(0) #define DEVFREQ_GOV_FLAG_IRQ_DRIVEN BIT(1) +#define DEVFREQ_GOV_FLAG_TRACK_REMOTE BIT(2) /* * Definition of governor attribute flags except for common sysfs attributes -- 2.34.1 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH RFC 05/10] PM / devfreq: Add new track_remote flag for governors 2026-07-24 7:18 ` [PATCH RFC 05/10] PM / devfreq: Add new track_remote " Pragnesh Papaniya @ 2026-07-24 7:31 ` sashiko-bot 0 siblings, 0 replies; 20+ messages in thread From: sashiko-bot @ 2026-07-24 7:31 UTC (permalink / raw) To: Pragnesh Papaniya; +Cc: robh, conor+dt, devicetree Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] Skipping `profile->target()` for `TRACK_REMOTE` bypasses OPP frequency rounding, causing state desynchronization and permanently failing `devfreq_update_status()` on QoS-clamped frequencies. -- --- Patch [5]: [PATCH RFC 05/10] PM / devfreq: Add new track_remote flag for governors --- commit 3b4a905187566fcb4b261840b1355a57916fec6b Author: Sibi Sankar <sibi.sankar@oss.qualcomm.com> PM / devfreq: Add new track_remote flag for governors Some devfreq governors need to track frequency changes performed on remote devices rather than driving the frequency updates themselves. Add a new DEVFREQ_GOV_FLAG_TRACK_REMOTE governor flag to skip the local target() call and just keep its transition statistics and notifiers in sync. > diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c > index 2f27a239e34a6..71c6ced9e4e1d 100644 > --- a/drivers/devfreq/devfreq.c > +++ b/drivers/devfreq/devfreq.c > @@ -348,6 +348,26 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, > unsigned long cur_freq; > int err = 0; > > + /* > + * When a remote agent (e.g. firmware) owns the frequency, skip the > + * local profile->target() call. Still emit the PRECHANGE/POSTCHANGE > + * pair and the trace event so that transition-notifier subscribers > + * (passive governor, devfreq cooling, etc.) and tracing observe the > + * frequency change exactly as they would on the normal path. > + */ > + if (devfreq->governor && > + IS_SUPPORTED_FLAG(devfreq->governor->flags, TRACK_REMOTE)) { > + freqs.old = devfreq->previous_freq; > + freqs.new = new_freq; > + devfreq_notify_transition(devfreq, &freqs, DEVFREQ_PRECHANGE); > + > + if (trace_devfreq_frequency_enabled() && new_freq != freqs.old) > + trace_devfreq_frequency(devfreq, new_freq, freqs.old); > + > + devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); > + goto update_status; [Severity: High] Does skipping profile->target() here cause issues with unrounded frequencies? When a frequency update occurs, devfreq_update_target() can clamp the requested frequency to a PM QoS limit (e.g., min_freq) that might not be an exact valid OPP frequency. By skipping profile->target() (which normally rounds the frequency to a valid OPP), this unrounded frequency is passed directly to devfreq_update_status() via the goto. Inside devfreq_update_status(), devfreq_get_freq_level() requires an exact match in the freq_table. If the unrounded frequency is not found, it fails and returns < 0, preventing stats updates and leaving previous_freq as an invalid OPP. > + } > + > if (devfreq->profile->get_cur_freq) > devfreq->profile->get_cur_freq(devfreq->dev.parent, &cur_freq); > else -- Sashiko AI review · https://sashiko.dev/#/patchset/20260724-rfc_v8_scmi_memlat-v1-0-cb732bcff1f4@oss.qualcomm.com?part=5 ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH RFC 06/10] PM / devfreq: Add a governor for tracking remote device frequencies 2026-07-24 7:18 [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Pragnesh Papaniya ` (4 preceding siblings ...) 2026-07-24 7:18 ` [PATCH RFC 05/10] PM / devfreq: Add new track_remote " Pragnesh Papaniya @ 2026-07-24 7:18 ` Pragnesh Papaniya 2026-07-24 7:33 ` sashiko-bot 2026-07-24 7:18 ` [PATCH RFC 07/10] PM / devfreq: Introduce the QCOM SCMI Memlat devfreq driver Pragnesh Papaniya ` (4 subsequent siblings) 10 siblings, 1 reply; 20+ messages in thread From: Pragnesh Papaniya @ 2026-07-24 7:18 UTC (permalink / raw) To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Bjorn Andersson, Konrad Dybcio Cc: Pragnesh Papaniya, Rajendra Nayak, Pankaj Patil, linux-arm-msm, linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm, linux-tegra From: Sibi Sankar <sibi.sankar@oss.qualcomm.com> On SoCs where the governor and the mechanism to control the frequency for devices like caches is hosted on the System Control Processor (SCP), there exists a need to track the frequency changes in a reliable way and provide ways to tweak parameters on the remote governor. Add a new "remote" devfreq governor that uses the track_remote flag to expose the remote device's frequency to userspace via trans_stat. The governor deliberately does not advertise the DEVFREQ_GOV_ATTR_TARGET_FREQ attribute since the kernel cannot set the frequency on a remote-managed device; reads of the target_freq sysfs node therefore return -EINVAL. Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com> Signed-off-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> --- drivers/devfreq/Kconfig | 8 +++++ drivers/devfreq/Makefile | 1 + drivers/devfreq/governor_remote.c | 73 +++++++++++++++++++++++++++++++++++++++ include/linux/devfreq.h | 1 + 4 files changed, 83 insertions(+) diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index c999c4a1e567..2caa87554914 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -56,6 +56,14 @@ config DEVFREQ_GOV_POWERSAVE the DEVFREQ framework returns the lowest frequency available at any time. +config DEVFREQ_GOV_REMOTE + tristate "Remote" + help + A simple governor to track the frequency of devices whose + dvfs control lies outside the kernel. This governor acts + as an observer and provides for ways to track frequency and + set/get information related to the remote dvfs device. + config DEVFREQ_GOV_USERSPACE tristate "Userspace" help diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 404179d79a9d..cde57c8cda76 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_PM_DEVFREQ_EVENT) += devfreq-event.o obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) += governor_simpleondemand.o obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o +obj-$(CONFIG_DEVFREQ_GOV_REMOTE) += governor_remote.o obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o diff --git a/drivers/devfreq/governor_remote.c b/drivers/devfreq/governor_remote.c new file mode 100644 index 000000000000..df3819757e56 --- /dev/null +++ b/drivers/devfreq/governor_remote.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/devfreq.h> +#include <linux/devfreq-governor.h> + +static int devfreq_remote_track_func(struct devfreq *devfreq, unsigned long *freq) +{ + if (!devfreq->profile->get_cur_freq) + return -ENXIO; + + return devfreq->profile->get_cur_freq(devfreq->dev.parent, freq); +} + +static int devfreq_remote_track_handler(struct devfreq *devfreq, unsigned int event, void *data) +{ + switch (event) { + case DEVFREQ_GOV_START: + devfreq_monitor_start(devfreq); + break; + + case DEVFREQ_GOV_STOP: + devfreq_monitor_stop(devfreq); + break; + + case DEVFREQ_GOV_UPDATE_INTERVAL: + devfreq_update_interval(devfreq, (unsigned int *)data); + break; + + case DEVFREQ_GOV_SUSPEND: + devfreq_monitor_suspend(devfreq); + break; + + case DEVFREQ_GOV_RESUME: + devfreq_monitor_resume(devfreq); + break; + } + + return 0; +} + +static struct devfreq_governor devfreq_remote_track = { + .name = DEVFREQ_GOV_REMOTE, + .attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL + | DEVFREQ_GOV_ATTR_TIMER, + .flags = DEVFREQ_GOV_FLAG_IMMUTABLE + | DEVFREQ_GOV_FLAG_TRACK_REMOTE, + .get_target_freq = devfreq_remote_track_func, + .event_handler = devfreq_remote_track_handler, +}; + +static int __init devfreq_remote_track_init(void) +{ + return devfreq_add_governor(&devfreq_remote_track); +} +subsys_initcall(devfreq_remote_track_init); + +static void __exit devfreq_remote_track_exit(void) +{ + int ret; + + ret = devfreq_remove_governor(&devfreq_remote_track); + if (ret) + pr_err("%s: failed to remove governor %d\n", __func__, ret); +} +module_exit(devfreq_remote_track_exit); + +MODULE_DESCRIPTION("DEVFREQ Remote Tracking governor"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index dc1075dc3446..4d50cf230950 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -21,6 +21,7 @@ #define DEVFREQ_GOV_POWERSAVE "powersave" #define DEVFREQ_GOV_USERSPACE "userspace" #define DEVFREQ_GOV_PASSIVE "passive" +#define DEVFREQ_GOV_REMOTE "remote" /* DEVFREQ notifier interface */ #define DEVFREQ_TRANSITION_NOTIFIER (0) -- 2.34.1 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH RFC 06/10] PM / devfreq: Add a governor for tracking remote device frequencies 2026-07-24 7:18 ` [PATCH RFC 06/10] PM / devfreq: Add a governor for tracking remote device frequencies Pragnesh Papaniya @ 2026-07-24 7:33 ` sashiko-bot 0 siblings, 0 replies; 20+ messages in thread From: sashiko-bot @ 2026-07-24 7:33 UTC (permalink / raw) To: Pragnesh Papaniya; +Cc: conor+dt, devicetree, robh Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: New issues: - [High] The remote tracking governor corrupts observed frequencies and causes continuous log spam due to core PM QoS clamping and exact OPP matching. - [Medium] Missing validation of `get_cur_freq` during `DEVFREQ_GOV_START` causes infinite error log flooding. Pre-existing issues: - [High] Bypassing the IMMUTABLE governor restriction allows a local attacker (root) to trigger a NULL pointer dereference by switching a TRACK_REMOTE device to a mutable governor. -- --- Patch [6]: [PATCH RFC 06/10] PM / devfreq: Add a governor for tracking remote device frequencies --- commit dc71eb5d4b37ce705f4a10bb03bda903dcd75d0b Author: Sibi Sankar <sibi.sankar@oss.qualcomm.com> PM / devfreq: Add a governor for tracking remote device frequencies This commit adds a new "remote" devfreq governor that uses the track_remote flag to expose a remote device's frequency to userspace via trans_stat. The governor avoids advertising DEVFREQ_GOV_ATTR_TARGET_FREQ and acts strictly as an observer. > diff --git a/drivers/devfreq/governor_remote.c b/drivers/devfreq/governor_remote.c > new file mode 100644 > index 0000000000000..df3819757e56f > --- /dev/null > +++ b/drivers/devfreq/governor_remote.c > @@ -0,0 +1,73 @@ [ ... ] > +static int devfreq_remote_track_handler(struct devfreq *devfreq, unsigned int event, void *data) > +{ > + switch (event) { > + case DEVFREQ_GOV_START: > + devfreq_monitor_start(devfreq); [Severity: Medium] Should the governor validate the presence of get_cur_freq before starting the monitor? If a device profile is missing the get_cur_freq callback, devfreq_remote_track_func() returns -ENXIO. This error is intercepted by the core devfreq_monitor() function, which logs 'dvfs failed with (-6) error' and reschedules the delayed work, leading to an infinite flood in the system logs. Could we check for get_cur_freq during governor initialization or before calling devfreq_monitor_start() to fail gracefully? > + break; [ ... ] > +static struct devfreq_governor devfreq_remote_track = { > + .name = DEVFREQ_GOV_REMOTE, > + .attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL > + | DEVFREQ_GOV_ATTR_TIMER, > + .flags = DEVFREQ_GOV_FLAG_IMMUTABLE [Severity: High] This is a pre-existing issue, but I wanted to point out a potential vulnerability when using DEVFREQ_GOV_FLAG_IMMUTABLE. If the governor_remote module is unloaded, devfreq_remove_governor() stops it and sets df->governor to NULL. A privileged user could then write a new mutable governor name (like simpleondemand) to the device's governor sysfs node. In the core governor_store() function, the IMMUTABLE security check is entirely bypassed if !df->governor is true: drivers/devfreq/devfreq.c:governor_store() { if (!df->governor) goto start_new_governor; if (df->governor == governor) { ... } else if (IS_SUPPORTED_FLAG(df->governor->flags, IMMUTABLE) ... } When the new mutable governor runs devfreq_set_target() without the TRACK_REMOTE flag, it will blindly call devfreq->profile->target(). Since TRACK_REMOTE devices intentionally omit the .target callback, this leads to a NULL pointer dereference. > + | DEVFREQ_GOV_FLAG_TRACK_REMOTE, [Severity: High] Does the use of DEVFREQ_GOV_FLAG_TRACK_REMOTE interact correctly with PM QoS clamping? The devfreq_remote_track_func() provides the raw observed frequency. However, the core devfreq_update_target() applies local PM QoS constraints to this tracked frequency. Because DEVFREQ_GOV_FLAG_TRACK_REMOTE bypasses the driver's .target callback, the frequency is never rounded to a valid OPP: drivers/devfreq/devfreq.c:devfreq_set_target() { if (devfreq->governor && IS_SUPPORTED_FLAG(devfreq->governor->flags, TRACK_REMOTE)) { freqs.old = devfreq->previous_freq; freqs.new = new_freq; ... goto update_status; } } When this clamped, unrounded frequency is passed directly to devfreq_update_status(), it will fail to find an exact match in the freq_table and return -EINVAL. This causes a continuous log spam of 'Couldn't update frequency transition information' on every polling interval and breaks trans_stat metrics. > + .get_target_freq = devfreq_remote_track_func, > + .event_handler = devfreq_remote_track_handler, > +}; -- Sashiko AI review · https://sashiko.dev/#/patchset/20260724-rfc_v8_scmi_memlat-v1-0-cb732bcff1f4@oss.qualcomm.com?part=6 ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH RFC 07/10] PM / devfreq: Introduce the QCOM SCMI Memlat devfreq driver 2026-07-24 7:18 [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Pragnesh Papaniya ` (5 preceding siblings ...) 2026-07-24 7:18 ` [PATCH RFC 06/10] PM / devfreq: Add a governor for tracking remote device frequencies Pragnesh Papaniya @ 2026-07-24 7:18 ` Pragnesh Papaniya 2026-07-24 7:18 ` [PATCH RFC 08/10] arm64: dts: qcom: glymur: Enable LLCC/DDR/DDR_QOS DVFS Pragnesh Papaniya ` (3 subsequent siblings) 10 siblings, 0 replies; 20+ messages in thread From: Pragnesh Papaniya @ 2026-07-24 7:18 UTC (permalink / raw) To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Bjorn Andersson, Konrad Dybcio Cc: Pragnesh Papaniya, Rajendra Nayak, Pankaj Patil, linux-arm-msm, linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm, linux-tegra, Amir Vajid, Ramakrishna Gottimukkula From: Sibi Sankar <sibi.sankar@oss.qualcomm.com> On Qualcomm Glymur, Mahua, X1E/X1P (Hamoa/Purwa) and Kaanapali SoCs, the memory-latency (memlat) governor runs entirely on the CPU Control Processor (CPUCP). Every sampling window the CPUCP reads the per-CPU PMU counters, derives its statistics (instructions-per-miss, back-end stall), maps them to a target operating point for the DDR, LLCC and DDR_QOS buses and votes it directly to the interconnect. The kernel is never in that runtime decision loop; the CPUCP algorithm and its controls are reached through the QCOM SCMI Generic Extension Protocol, addressed via the "MEMLAT" algorithm string. Introduce a devfreq SCMI client driver whose role is split in two. At probe it configures the CPUCP algorithm once (event maps, per-monitor tuneables, the cpufreq-to-memfreq maps and min/max clamps) and starts it; this half is required, as without it the CPUCP has no per-SoC data to run the algorithm. Thereafter the driver only reads back the current operating point. Each bus is modelled as a devfreq device and uses the remote devfreq governor precisely for this "observe a frequency someone else drives" case: it exposes the read-back through trans_stat and lets userspace retune the remote governor's parameters. Co-developed-by: Amir Vajid <amir.vajid@oss.qualcomm.com> Signed-off-by: Amir Vajid <amir.vajid@oss.qualcomm.com> Co-developed-by: Ramakrishna Gottimukkula <ramakrishna.gottimukkula@oss.qualcomm.com> Signed-off-by: Ramakrishna Gottimukkula <ramakrishna.gottimukkula@oss.qualcomm.com> Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com> Co-developed-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> Signed-off-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> --- MAINTAINERS | 8 + drivers/devfreq/Kconfig | 15 + drivers/devfreq/Makefile | 1 + drivers/devfreq/scmi-qcom-memlat-devfreq.c | 1331 ++++++++++++++++++++++++++++ 4 files changed, 1355 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 265eb99bdc7a..a58e2219d5df 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22495,6 +22495,14 @@ F: Documentation/networking/device_drivers/cellular/qualcomm/rmnet.rst F: drivers/net/ethernet/qualcomm/rmnet/ F: include/linux/if_rmnet.h +QUALCOMM SCMI MEMLAT DEVFREQ DRIVER +M: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> +M: Sibi Sankar <sibi.sankar@oss.qualcomm.com> +L: linux-arm-msm@vger.kernel.org +L: linux-pm@vger.kernel.org +S: Maintained +F: drivers/devfreq/scmi-qcom-memlat-devfreq.c + QUALCOMM TEE (QCOMTEE) DRIVER M: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com> L: linux-arm-msm@vger.kernel.org diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 2caa87554914..fdf6484fc72c 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -169,6 +169,21 @@ config ARM_SUN8I_A33_MBUS_DEVFREQ This adds the DEVFREQ driver for the MBUS controller in some Allwinner sun8i (A33 through H3) and sun50i (A64 and H5) SoCs. +config SCMI_QCOM_MEMLAT_DEVFREQ + tristate "Qualcomm SCMI memory latency (memlat) devfreq driver" + depends on QCOM_SCMI_GENERIC_EXT || COMPILE_TEST + select DEVFREQ_GOV_REMOTE + help + Enable memory-bus (DDR/LLCC/DDR_QOS) scaling on Qualcomm Glymur, + X1E/X1P-class and Kaanapali SoCs. The scaling algorithm itself runs + autonomously on the CPU Control Processor (CPUCP); this driver only + configures and starts it at boot and then exposes each bus as a + devfreq device so its operating point is visible via trans_stat and + tunable from userspace. + + Nothing else needs to be enabled for the scaling to take effect. Say Y + or M here on the affected Qualcomm SoCs; otherwise say N. + source "drivers/devfreq/event/Kconfig" endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index cde57c8cda76..b11f94e2f485 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_ARM_MEDIATEK_CCI_DEVFREQ) += mtk-cci-devfreq.o obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_SUN8I_A33_MBUS_DEVFREQ) += sun8i-a33-mbus.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o +obj-$(CONFIG_SCMI_QCOM_MEMLAT_DEVFREQ) += scmi-qcom-memlat-devfreq.o # DEVFREQ Event Drivers obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/ diff --git a/drivers/devfreq/scmi-qcom-memlat-devfreq.c b/drivers/devfreq/scmi-qcom-memlat-devfreq.c new file mode 100644 index 000000000000..9ca85b2f4c1a --- /dev/null +++ b/drivers/devfreq/scmi-qcom-memlat-devfreq.c @@ -0,0 +1,1331 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/cpu.h> +#include <linux/devfreq.h> +#include <linux/device/faux.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pm_opp.h> +#include <linux/scmi_protocol.h> +#include <linux/scmi_qcom_protocol.h> +#include <linux/units.h> + +/* CPUCP AMU event indices programmed into the common/group event maps. */ +#define MEMLAT_EV_CPU_CYCLES 0 +#define MEMLAT_EV_CNT_CYCLES 1 +#define MEMLAT_EV_INST_RETIRED 2 +#define MEMLAT_EV_STALL_BACKEND_MEM 3 +#define MEMLAT_EV_L2_D_RFILL 5 + +/* Sentinel for an event slot that is not wired to any AMU counter. */ +#define MEMLAT_INVALID_IDX 0xff + +/* hw_type value that targets every group (common events are not group-scoped). */ +#define MEMLAT_HW_TYPE_ALL 0xff + +#define MEMLAT_ALGO_STR 0x4d454d4c4154ULL /* "MEMLAT" */ + +/** + * struct scmi_qcom_map_table - one row of a monitor's cpufreq->memfreq map + * @cpu_freq_mhz: CPU frequency threshold, in MHz. When the monitor's busiest + * CPU is at or above this frequency, the firmware votes the + * paired memory frequency. + * @mem_freq_mhz: memory frequency to vote, in MHz (a 0/1 level for DDR_QOS). + */ +struct scmi_qcom_map_table { + unsigned int cpu_freq_mhz; + unsigned int mem_freq_mhz; +}; + +struct scmi_qcom_opp_data { + u64 freq; +}; + +struct scmi_qcom_memory_range { + unsigned int min_freq_khz; + unsigned int max_freq_khz; +}; + +enum common_ev_idx { + INST_IDX, + CYC_IDX, + CONST_CYC_IDX, + FE_STALL_IDX, + BE_STALL_IDX, + NUM_COMMON_EVS +}; + +enum grp_ev_idx { + MISS_IDX, + WB_IDX, + ACC_IDX, + NUM_GRP_EVS +}; + +/* + * hw_type identifiers for the memory buses. These are a firmware-defined + * encoding carried in the node/scalar/map/ev messages; the driver only + * mirrors them here. + */ +enum scmi_qcom_memlat_hw_type { + MEMLAT_HW_DDR = 0, + MEMLAT_HW_LLCC = 1, + MEMLAT_HW_DDR_QOS_COMPUTE = 2, + MEMLAT_HW_DDR_QOS_MOBILE = 3, +}; + +/** + * struct scmi_qcom_monitor_cfg - per-monitor firmware tuneables + * @table: cpufreq->memfreq voting map for this monitor. + * @name: monitor name, forwarded to the firmware and used in its log lines. + * @be_stall_floor: back-end-stall gating threshold, in percent. Each sample + * window the firmware computes a per-CPU back-end-stall + * percentage (stall cycles / total cycles); a CPU only + * contributes its frequency vote to this monitor when that + * percentage is at or above @be_stall_floor. A value of 1 + * therefore means "any stall counts", i.e. the CPU is + * effectively always eligible. + * @cpu_mask: bitmask of CPUs whose counters this monitor aggregates. + * @ipm_ceil: instructions-per-miss ceiling. A CPU is treated as + * memory-latency-bound (and eligible to vote) only when its IPM is + * at or below this ceiling; a monitor with @ipm_ceil == 0 is a + * "compute" monitor and scales passively with cpufreq. + * @table_len: number of valid rows in @table. + */ +struct scmi_qcom_monitor_cfg { + const struct scmi_qcom_map_table *table; + const char *name; + u32 be_stall_floor; + u32 cpu_mask; + u32 ipm_ceil; + u32 table_len; +}; + +struct scmi_qcom_memory_cfg { + const struct scmi_qcom_monitor_cfg *monitor_cfg; + const struct scmi_qcom_opp_data *mem_table; + struct scmi_qcom_memory_range memory_range; + const u32 *grp_ev; + const char *name; + enum scmi_qcom_memlat_hw_type memory_type; + u32 monitor_cnt; + u32 num_opps; +}; + +struct scmi_qcom_memlat_cfg_data { + const struct scmi_qcom_memory_cfg *memory_cfg; + const u32 *common_ev; + bool cpucp_legacy_freq_method; + u32 cpucp_sample_ms; + u32 memory_cnt; +}; + +#define MEMLAT_MAX_NAME_LEN 20 +#define MEMLAT_MAX_MAP_ENTRIES 10 + +/* + * param_ids of the "MEMLAT" algo_str hosted by the Qualcomm Generic Vendor + * Protocol. MEMLAT detects memory-latency-bound workloads and scales the + * memory buses accordingly; these are the fixed wire-protocol tokens that + * the firmware matches. + * + * MEMLAT_SET_MEM_GROUP: initializes the frequency/level scaling functions for the memory bus. + * MEMLAT_SET_MONITOR: configures the monitor to work on a specific memory bus. + * MEMLAT_SET_COMMON_EV_MAP: set up common counters used to monitor the cpu frequency. + * MEMLAT_SET_GRP_EV_MAP: set up any specific counters used to monitor the memory bus. + * MEMLAT_ADAPTIVE_LOW_FREQ: set the adaptive low frequency floor of the memory bus. + * MEMLAT_ADAPTIVE_HIGH_FREQ: set the adaptive high frequency floor of the memory bus. + * MEMLAT_GET_ADAPTIVE_CUR_FREQ: query the current adaptive frequency of the memory bus. + * MEMLAT_IPM_CEIL: set the IPM (Instruction Per Misses) ceiling per monitor. + * MEMLAT_FE_STALL_FLOOR: set the front-end stall floor per monitor. + * MEMLAT_BE_STALL_FLOOR: set the back-end stall floor per monitor. + * MEMLAT_WB_PCT: set the write-back percentage threshold per monitor. + * MEMLAT_IPM_FILTER: set the IPM filter used to reject non-latency samples per monitor. + * MEMLAT_FREQ_SCALE_PCT: set the frequency scaling percentage per monitor. + * MEMLAT_FREQ_SCALE_CEIL_MHZ: set the cpu frequency ceiling for scaling per monitor. + * MEMLAT_FREQ_SCALE_FLOOR_MHZ: set the cpu frequency floor for scaling per monitor. + * MEMLAT_SAMPLE_MS: set the sampling period for all the monitors. + * MEMLAT_MON_FREQ_MAP: setup the cpufreq to memfreq map. + * MEMLAT_SET_MIN_FREQ: set the min frequency of the memory bus. + * MEMLAT_SET_MAX_FREQ: set the max frequency of the memory bus. + * MEMLAT_GET_CUR_FREQ: query the current frequency/level of the memory bus. + * MEMLAT_START_TIMER: start all the monitors with the requested sampling period. + * MEMLAT_STOP_TIMER: stop all the running monitors. + * MEMLAT_GET_TIMESTAMP: query the firmware timestamp. + * MEMLAT_SET_EFFECTIVE_FREQ_METHOD: set the method used to determine cpu frequency. + */ +#define MEMLAT_SET_MEM_GROUP 16 +#define MEMLAT_SET_MONITOR 17 +#define MEMLAT_SET_COMMON_EV_MAP 18 +#define MEMLAT_SET_GRP_EV_MAP 19 +#define MEMLAT_ADAPTIVE_LOW_FREQ 20 +#define MEMLAT_ADAPTIVE_HIGH_FREQ 21 +#define MEMLAT_GET_ADAPTIVE_CUR_FREQ 22 +#define MEMLAT_IPM_CEIL 23 +#define MEMLAT_FE_STALL_FLOOR 24 +#define MEMLAT_BE_STALL_FLOOR 25 +#define MEMLAT_WB_PCT 26 +#define MEMLAT_IPM_FILTER 27 +#define MEMLAT_FREQ_SCALE_PCT 28 +#define MEMLAT_FREQ_SCALE_CEIL_MHZ 29 +#define MEMLAT_FREQ_SCALE_FLOOR_MHZ 30 +#define MEMLAT_SAMPLE_MS 31 +#define MEMLAT_MON_FREQ_MAP 32 +#define MEMLAT_SET_MIN_FREQ 33 +#define MEMLAT_SET_MAX_FREQ 34 +#define MEMLAT_GET_CUR_FREQ 35 +#define MEMLAT_START_TIMER 36 +#define MEMLAT_STOP_TIMER 37 +#define MEMLAT_GET_TIMESTAMP 38 +#define MEMLAT_SET_EFFECTIVE_FREQ_METHOD 39 + +struct cpucp_map_table { + __le16 cpu_freq_mhz; + __le16 mem_freq_mhz; +}; + +struct map_param_msg { + __le32 hw_type; + __le32 mon_idx; + __le32 nr_rows; + struct cpucp_map_table tbl[MEMLAT_MAX_MAP_ENTRIES]; +}; + +struct node_msg { + __le32 cpumask; + __le32 hw_type; + __le32 mon_type; + __le32 mon_idx; + char mon_name[MEMLAT_MAX_NAME_LEN]; +}; + +struct scalar_param_msg { + __le32 hw_type; + __le32 mon_idx; + __le32 val; +}; + +struct ev_map_msg { + __le32 num_evs; + __le32 hw_type; + __le32 cid[NUM_COMMON_EVS]; +}; + +/* + * MEMLAT_GET_CUR_FREQ reuses the request buffer for its response, so express + * the in/out aliasing as a union rather than reinterpreting the bytes. + */ +union cur_freq_msg { + struct scalar_param_msg req; + __le32 resp; +}; + +struct scmi_qcom_memory_info { + const struct scmi_qcom_memory_cfg *cfg; + struct devfreq_dev_profile profile; + struct devfreq *devfreq; + struct faux_device *fdev; + struct scmi_protocol_handle *ph; + const struct qcom_generic_ext_ops *ops; +}; + +struct scmi_qcom_memlat_info { + struct scmi_protocol_handle *ph; + const struct qcom_generic_ext_ops *ops; + const struct scmi_qcom_memlat_cfg_data *cfg_data; + struct scmi_qcom_memory_info **memory; + u32 memory_cnt; +}; + +/* + * ========================================================================== + * Per-SoC configuration + * ========================================================================== + */ + +static const u32 glymur_common_ev[NUM_COMMON_EVS] = { + [INST_IDX] = MEMLAT_EV_INST_RETIRED, + [CYC_IDX] = MEMLAT_EV_CPU_CYCLES, + [CONST_CYC_IDX] = MEMLAT_EV_CNT_CYCLES, + [FE_STALL_IDX] = MEMLAT_INVALID_IDX, + [BE_STALL_IDX] = MEMLAT_EV_STALL_BACKEND_MEM, +}; + +static const u32 glymur_ddr_grp_ev[NUM_GRP_EVS] = { + [MISS_IDX] = MEMLAT_EV_L2_D_RFILL, + [WB_IDX] = MEMLAT_INVALID_IDX, + [ACC_IDX] = MEMLAT_INVALID_IDX, +}; + +static const u32 glymur_llcc_grp_ev[NUM_GRP_EVS] = { + [MISS_IDX] = MEMLAT_EV_L2_D_RFILL, + [WB_IDX] = MEMLAT_INVALID_IDX, + [ACC_IDX] = MEMLAT_INVALID_IDX, +}; + +static const u32 glymur_ddr_qos_grp_ev[NUM_GRP_EVS] = { + [MISS_IDX] = MEMLAT_EV_L2_D_RFILL, + [WB_IDX] = MEMLAT_INVALID_IDX, + [ACC_IDX] = MEMLAT_INVALID_IDX, +}; + +static const u32 hamoa_common_ev[NUM_COMMON_EVS] = { + [INST_IDX] = MEMLAT_EV_INST_RETIRED, + [CYC_IDX] = MEMLAT_EV_CPU_CYCLES, + [CONST_CYC_IDX] = MEMLAT_EV_CNT_CYCLES, + [FE_STALL_IDX] = MEMLAT_INVALID_IDX, + [BE_STALL_IDX] = MEMLAT_EV_STALL_BACKEND_MEM, +}; + +static const u32 hamoa_ddr_grp_ev[NUM_GRP_EVS] = { + [MISS_IDX] = MEMLAT_EV_L2_D_RFILL, + [WB_IDX] = MEMLAT_INVALID_IDX, + [ACC_IDX] = MEMLAT_INVALID_IDX, +}; + +static const u32 hamoa_llcc_grp_ev[NUM_GRP_EVS] = { + [MISS_IDX] = MEMLAT_EV_L2_D_RFILL, + [WB_IDX] = MEMLAT_INVALID_IDX, + [ACC_IDX] = MEMLAT_INVALID_IDX, +}; + +static const u32 hamoa_ddr_qos_grp_ev[NUM_GRP_EVS] = { + [MISS_IDX] = MEMLAT_EV_L2_D_RFILL, + [WB_IDX] = MEMLAT_INVALID_IDX, + [ACC_IDX] = MEMLAT_INVALID_IDX, +}; + +static const u32 kaanapali_common_ev[NUM_COMMON_EVS] = { + [INST_IDX] = MEMLAT_EV_INST_RETIRED, + [CYC_IDX] = MEMLAT_EV_CPU_CYCLES, + [CONST_CYC_IDX] = MEMLAT_INVALID_IDX, + [FE_STALL_IDX] = MEMLAT_INVALID_IDX, + [BE_STALL_IDX] = MEMLAT_INVALID_IDX, +}; + +static const u32 kaanapali_ddr_grp_ev[NUM_GRP_EVS] = { + [MISS_IDX] = MEMLAT_EV_L2_D_RFILL, + [WB_IDX] = MEMLAT_INVALID_IDX, + [ACC_IDX] = MEMLAT_INVALID_IDX, +}; + +static const u32 kaanapali_llcc_grp_ev[NUM_GRP_EVS] = { + [MISS_IDX] = MEMLAT_EV_L2_D_RFILL, + [WB_IDX] = MEMLAT_INVALID_IDX, + [ACC_IDX] = MEMLAT_INVALID_IDX, +}; + +static const u32 kaanapali_ddr_qos_grp_ev[NUM_GRP_EVS] = { + [MISS_IDX] = MEMLAT_EV_L2_D_RFILL, + [WB_IDX] = MEMLAT_INVALID_IDX, + [ACC_IDX] = MEMLAT_INVALID_IDX, +}; + +static const struct scmi_qcom_opp_data glymur_llcc_table[] = { + { .freq = 315000000 }, + { .freq = 479000000 }, + { .freq = 545000000 }, + { .freq = 725000000 }, + { .freq = 840000000 }, + { .freq = 959000000 }, + { .freq = 1090000000 }, + { .freq = 1211000000 }, +}; + +static const struct scmi_qcom_opp_data hamoa_llcc_table[] = { + { .freq = 300000000 }, + { .freq = 466000000 }, + { .freq = 600000000 }, + { .freq = 806000000 }, + { .freq = 933000000 }, + { .freq = 1066000000 }, +}; + +static const struct scmi_qcom_opp_data kaanapali_llcc_table[] = { + { .freq = 282000000 }, + { .freq = 350000000 }, + { .freq = 533000000 }, + { .freq = 605600000 }, + { .freq = 806000000 }, + { .freq = 933000000 }, + { .freq = 1066000000 }, + { .freq = 1211000000 }, + { .freq = 1350000000 }, +}; + +static const struct scmi_qcom_opp_data glymur_ddr_table[] = { + { .freq = 200000000 }, + { .freq = 547000000 }, + { .freq = 1353000000 }, + { .freq = 1555000000 }, + { .freq = 1708000000 }, + { .freq = 2092000000 }, + { .freq = 2736000000 }, + { .freq = 3187000000 }, + { .freq = 3686000000 }, + { .freq = 4224000000 }, + { .freq = 4761000000 }, +}; + +static const struct scmi_qcom_opp_data hamoa_ddr_table[] = { + { .freq = 200000000 }, + { .freq = 547000000 }, + { .freq = 768000000 }, + { .freq = 1555000000 }, + { .freq = 1708000000 }, + { .freq = 2092000000 }, + { .freq = 2736000000 }, + { .freq = 3187000000 }, + { .freq = 3686000000 }, + { .freq = 4224000000 }, +}; + +static const struct scmi_qcom_opp_data kaanapali_ddr_table[] = { + { .freq = 547000000 }, + { .freq = 1353000000 }, + { .freq = 1555000000 }, + { .freq = 1708000000 }, + { .freq = 2092000000 }, + { .freq = 2736000000 }, + { .freq = 3187000000 }, + { .freq = 3686000000 }, + { .freq = 4224000000 }, + { .freq = 4780000000 }, + { .freq = 5333000000 }, +}; + +/* + * DDR_QOS is a boolean bus: the firmware reports level 0 (nominal) or 1 + * (boost) rather than a frequency. dev_pm_opp_add_dynamic() rejects a zero + * frequency, so the two OPP entries below use 1 and 100 purely as distinct, + * non-zero devfreq keys for the two levels; the values are not real + * frequencies. scmi_qcom_devfreq_get_cur_freq() maps the reported level back + * to the matching OPP frequency. + */ +static const struct scmi_qcom_opp_data glymur_ddr_qos_table[] = { + { .freq = 1 }, + { .freq = 100 }, +}; + +static const struct scmi_qcom_memory_cfg glymur_memory_cfg[] = { + { + .memory_type = MEMLAT_HW_DDR, + .name = "ddr", + .mem_table = glymur_ddr_table, + .num_opps = ARRAY_SIZE(glymur_ddr_table), + .grp_ev = glymur_ddr_grp_ev, + .monitor_cnt = 4, + .memory_range = { .min_freq_khz = 547000, .max_freq_khz = 4761000 }, + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) { + { + .name = "mon_0", + .cpu_mask = 0x3f, + .ipm_ceil = 60000000, + .be_stall_floor = 1, + .table_len = 8, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 960, .mem_freq_mhz = 547 }, + { .cpu_freq_mhz = 1133, .mem_freq_mhz = 1353 }, + { .cpu_freq_mhz = 1594, .mem_freq_mhz = 1555 }, + { .cpu_freq_mhz = 1920, .mem_freq_mhz = 1708 }, + { .cpu_freq_mhz = 2228, .mem_freq_mhz = 2736 }, + { .cpu_freq_mhz = 2362, .mem_freq_mhz = 3187 }, + { .cpu_freq_mhz = 2650, .mem_freq_mhz = 3686 }, + { .cpu_freq_mhz = 2938, .mem_freq_mhz = 4761 }, + } + }, + { + .name = "mon_1", + .cpu_mask = 0xfc0, + .ipm_ceil = 60000000, + .be_stall_floor = 1, + .table_len = 8, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 356, .mem_freq_mhz = 547 }, + { .cpu_freq_mhz = 1018, .mem_freq_mhz = 1353 }, + { .cpu_freq_mhz = 1536, .mem_freq_mhz = 1555 }, + { .cpu_freq_mhz = 1748, .mem_freq_mhz = 1708 }, + { .cpu_freq_mhz = 2324, .mem_freq_mhz = 2736 }, + { .cpu_freq_mhz = 2496, .mem_freq_mhz = 3187 }, + { .cpu_freq_mhz = 2900, .mem_freq_mhz = 3686 }, + { .cpu_freq_mhz = 3514, .mem_freq_mhz = 4761 }, + } + }, + { + .name = "mon_2", + .cpu_mask = 0x3f000, + .ipm_ceil = 60000000, + .be_stall_floor = 1, + .table_len = 8, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 356, .mem_freq_mhz = 547 }, + { .cpu_freq_mhz = 1018, .mem_freq_mhz = 1353 }, + { .cpu_freq_mhz = 1536, .mem_freq_mhz = 1555 }, + { .cpu_freq_mhz = 1748, .mem_freq_mhz = 1708 }, + { .cpu_freq_mhz = 2324, .mem_freq_mhz = 2736 }, + { .cpu_freq_mhz = 2496, .mem_freq_mhz = 3187 }, + { .cpu_freq_mhz = 2900, .mem_freq_mhz = 3686 }, + { .cpu_freq_mhz = 3514, .mem_freq_mhz = 4761 }, + } + }, + { + .name = "mon_3", + .cpu_mask = 0x3ffff, + .table_len = 4, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 2823, .mem_freq_mhz = 547 }, + { .cpu_freq_mhz = 3034, .mem_freq_mhz = 1555 }, + { .cpu_freq_mhz = 3226, .mem_freq_mhz = 1708 }, + { .cpu_freq_mhz = 5012, .mem_freq_mhz = 2092 }, + } + }, + }, + }, + { + .memory_type = MEMLAT_HW_LLCC, + .name = "llcc", + .mem_table = glymur_llcc_table, + .num_opps = ARRAY_SIZE(glymur_llcc_table), + .grp_ev = glymur_llcc_grp_ev, + .monitor_cnt = 3, + .memory_range = { .min_freq_khz = 315000, .max_freq_khz = 1211000 }, + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) { + { + .name = "mon_0", + .cpu_mask = 0x3f, + .ipm_ceil = 60000000, + .be_stall_floor = 1, + .table_len = 7, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 960, .mem_freq_mhz = 315 }, + { .cpu_freq_mhz = 1113, .mem_freq_mhz = 479 }, + { .cpu_freq_mhz = 1594, .mem_freq_mhz = 545 }, + { .cpu_freq_mhz = 1920, .mem_freq_mhz = 725 }, + { .cpu_freq_mhz = 2362, .mem_freq_mhz = 840 }, + { .cpu_freq_mhz = 2650, .mem_freq_mhz = 959 }, + { .cpu_freq_mhz = 2938, .mem_freq_mhz = 1211 }, + } + }, + { + .name = "mon_1", + .cpu_mask = 0xfc0, + .ipm_ceil = 60000000, + .be_stall_floor = 1, + .table_len = 7, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 356, .mem_freq_mhz = 315 }, + { .cpu_freq_mhz = 1018, .mem_freq_mhz = 479 }, + { .cpu_freq_mhz = 1536, .mem_freq_mhz = 545 }, + { .cpu_freq_mhz = 1748, .mem_freq_mhz = 725 }, + { .cpu_freq_mhz = 2496, .mem_freq_mhz = 840 }, + { .cpu_freq_mhz = 2900, .mem_freq_mhz = 959 }, + { .cpu_freq_mhz = 3514, .mem_freq_mhz = 1211 }, + } + }, + { + .name = "mon_2", + .cpu_mask = 0x3f000, + .ipm_ceil = 60000000, + .be_stall_floor = 1, + .table_len = 7, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 356, .mem_freq_mhz = 315 }, + { .cpu_freq_mhz = 1018, .mem_freq_mhz = 479 }, + { .cpu_freq_mhz = 1536, .mem_freq_mhz = 545 }, + { .cpu_freq_mhz = 1748, .mem_freq_mhz = 725 }, + { .cpu_freq_mhz = 2496, .mem_freq_mhz = 840 }, + { .cpu_freq_mhz = 2900, .mem_freq_mhz = 959 }, + { .cpu_freq_mhz = 3514, .mem_freq_mhz = 1211 }, + } + }, + }, + }, + { + .memory_type = MEMLAT_HW_DDR_QOS_COMPUTE, + .name = "ddr-qos", + .monitor_cnt = 3, + .mem_table = glymur_ddr_qos_table, + .num_opps = ARRAY_SIZE(glymur_ddr_qos_table), + .grp_ev = glymur_ddr_qos_grp_ev, + .memory_range = { .min_freq_khz = 0, .max_freq_khz = 1 }, + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) { + { + .name = "mon_0", + .cpu_mask = 0x3f, + .ipm_ceil = 80000000, + .be_stall_floor = 1, + .table_len = 2, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 2362, .mem_freq_mhz = 0 }, + { .cpu_freq_mhz = 2938, .mem_freq_mhz = 1 }, + } + }, + { + .name = "mon_1", + .cpu_mask = 0xfc0, + .ipm_ceil = 80000000, + .be_stall_floor = 1, + .table_len = 2, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 2496, .mem_freq_mhz = 0 }, + { .cpu_freq_mhz = 3514, .mem_freq_mhz = 1 }, + } + }, + { + .name = "mon_2", + .cpu_mask = 0x3f000, + .ipm_ceil = 80000000, + .be_stall_floor = 1, + .table_len = 2, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 2496, .mem_freq_mhz = 0 }, + { .cpu_freq_mhz = 3514, .mem_freq_mhz = 1 }, + } + }, + }, + }, +}; + +static const struct scmi_qcom_memory_cfg hamoa_memory_cfg[] = { + { + .memory_type = MEMLAT_HW_DDR, + .name = "ddr", + .mem_table = hamoa_ddr_table, + .num_opps = ARRAY_SIZE(hamoa_ddr_table), + .grp_ev = hamoa_ddr_grp_ev, + .monitor_cnt = 4, + .memory_range = { .min_freq_khz = 200000, .max_freq_khz = 4224000 }, + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) { + { + .name = "mon_0", + .cpu_mask = 0xf, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 6, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 999, .mem_freq_mhz = 547 }, + { .cpu_freq_mhz = 1440, .mem_freq_mhz = 768 }, + { .cpu_freq_mhz = 1671, .mem_freq_mhz = 1555 }, + { .cpu_freq_mhz = 2189, .mem_freq_mhz = 2092 }, + { .cpu_freq_mhz = 2516, .mem_freq_mhz = 3187 }, + { .cpu_freq_mhz = 3860, .mem_freq_mhz = 4224 }, + } + }, + { + .name = "mon_1", + .cpu_mask = 0xf0, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 6, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 999, .mem_freq_mhz = 547 }, + { .cpu_freq_mhz = 1440, .mem_freq_mhz = 768 }, + { .cpu_freq_mhz = 1671, .mem_freq_mhz = 1555 }, + { .cpu_freq_mhz = 2189, .mem_freq_mhz = 2092 }, + { .cpu_freq_mhz = 2516, .mem_freq_mhz = 3187 }, + { .cpu_freq_mhz = 3860, .mem_freq_mhz = 4224 }, + } + }, + { + .name = "mon_2", + .cpu_mask = 0xf00, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 6, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 999, .mem_freq_mhz = 547 }, + { .cpu_freq_mhz = 1440, .mem_freq_mhz = 768 }, + { .cpu_freq_mhz = 1671, .mem_freq_mhz = 1555 }, + { .cpu_freq_mhz = 2189, .mem_freq_mhz = 2092 }, + { .cpu_freq_mhz = 2516, .mem_freq_mhz = 3187 }, + { .cpu_freq_mhz = 3860, .mem_freq_mhz = 4224 }, + } + }, + { + .name = "mon_3", + .cpu_mask = 0xfff, + .table_len = 4, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 1440, .mem_freq_mhz = 547 }, + { .cpu_freq_mhz = 2189, .mem_freq_mhz = 768 }, + { .cpu_freq_mhz = 2516, .mem_freq_mhz = 1555 }, + { .cpu_freq_mhz = 3860, .mem_freq_mhz = 2092 }, + } + }, + }, + }, + { + .memory_type = MEMLAT_HW_LLCC, + .name = "llcc", + .mem_table = hamoa_llcc_table, + .num_opps = ARRAY_SIZE(hamoa_llcc_table), + .grp_ev = hamoa_llcc_grp_ev, + .monitor_cnt = 3, + .memory_range = { .min_freq_khz = 300000, .max_freq_khz = 1066000 }, + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) { + { + .name = "mon_0", + .cpu_mask = 0xf, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 6, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 999, .mem_freq_mhz = 300 }, + { .cpu_freq_mhz = 1440, .mem_freq_mhz = 466 }, + { .cpu_freq_mhz = 1671, .mem_freq_mhz = 600 }, + { .cpu_freq_mhz = 2189, .mem_freq_mhz = 806 }, + { .cpu_freq_mhz = 2516, .mem_freq_mhz = 933 }, + { .cpu_freq_mhz = 3860, .mem_freq_mhz = 1066 }, + } + }, + { + .name = "mon_1", + .cpu_mask = 0xf0, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 6, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 999, .mem_freq_mhz = 300 }, + { .cpu_freq_mhz = 1440, .mem_freq_mhz = 466 }, + { .cpu_freq_mhz = 1671, .mem_freq_mhz = 600 }, + { .cpu_freq_mhz = 2189, .mem_freq_mhz = 806 }, + { .cpu_freq_mhz = 2516, .mem_freq_mhz = 933 }, + { .cpu_freq_mhz = 3860, .mem_freq_mhz = 1066 }, + } + }, + { + .name = "mon_2", + .cpu_mask = 0xf00, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 6, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 999, .mem_freq_mhz = 300 }, + { .cpu_freq_mhz = 1440, .mem_freq_mhz = 466 }, + { .cpu_freq_mhz = 1671, .mem_freq_mhz = 600 }, + { .cpu_freq_mhz = 2189, .mem_freq_mhz = 806 }, + { .cpu_freq_mhz = 2516, .mem_freq_mhz = 933 }, + { .cpu_freq_mhz = 3860, .mem_freq_mhz = 1066 }, + } + }, + }, + }, + { + .memory_type = MEMLAT_HW_DDR_QOS_COMPUTE, + .name = "ddr-qos", + .monitor_cnt = 3, + .mem_table = glymur_ddr_qos_table, + .num_opps = ARRAY_SIZE(glymur_ddr_qos_table), + .grp_ev = hamoa_ddr_qos_grp_ev, + .memory_range = { .min_freq_khz = 0, .max_freq_khz = 1 }, + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) { + { + .name = "mon_0", + .cpu_mask = 0xf, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 2, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 2189, .mem_freq_mhz = 0 }, + { .cpu_freq_mhz = 3860, .mem_freq_mhz = 1 }, + } + }, + { + .name = "mon_1", + .cpu_mask = 0xf0, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 2, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 2189, .mem_freq_mhz = 0 }, + { .cpu_freq_mhz = 3860, .mem_freq_mhz = 1 }, + } + }, + { + .name = "mon_2", + .cpu_mask = 0xf00, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 2, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 2189, .mem_freq_mhz = 0 }, + { .cpu_freq_mhz = 3860, .mem_freq_mhz = 1 }, + } + }, + }, + }, +}; + +static const struct scmi_qcom_memory_cfg kaanapali_memory_cfg[] = { + { + .memory_type = MEMLAT_HW_DDR, + .name = "ddr", + .mem_table = kaanapali_ddr_table, + .num_opps = ARRAY_SIZE(kaanapali_ddr_table), + .grp_ev = kaanapali_ddr_grp_ev, + .monitor_cnt = 4, + .memory_range = { .min_freq_khz = 547000, .max_freq_khz = 5333000 }, + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) { + { + .name = "mon_0", + .cpu_mask = 0x3f, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 6, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 787, .mem_freq_mhz = 547 }, + { .cpu_freq_mhz = 960, .mem_freq_mhz = 1353 }, + { .cpu_freq_mhz = 1200, .mem_freq_mhz = 1555 }, + { .cpu_freq_mhz = 1536, .mem_freq_mhz = 2092 }, + { .cpu_freq_mhz = 2534, .mem_freq_mhz = 3187 }, + { .cpu_freq_mhz = 3552, .mem_freq_mhz = 3686 }, + } + }, + { + .name = "mon_1", + .cpu_mask = 0xc0, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 8, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 614, .mem_freq_mhz = 547 }, + { .cpu_freq_mhz = 864, .mem_freq_mhz = 1353 }, + { .cpu_freq_mhz = 1382, .mem_freq_mhz = 2092 }, + { .cpu_freq_mhz = 2131, .mem_freq_mhz = 3187 }, + { .cpu_freq_mhz = 2726, .mem_freq_mhz = 3686 }, + { .cpu_freq_mhz = 3513, .mem_freq_mhz = 4224 }, + { .cpu_freq_mhz = 3916, .mem_freq_mhz = 4780 }, + { .cpu_freq_mhz = 4185, .mem_freq_mhz = 5333 }, + } + }, + { + .name = "mon_2", + .cpu_mask = 0xff, + .table_len = 2, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 2035, .mem_freq_mhz = 547 }, + { .cpu_freq_mhz = 4185, .mem_freq_mhz = 2092 }, + } + }, + { + .name = "mon_3", + .cpu_mask = 0xc0, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 2, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 2726, .mem_freq_mhz = 547 }, + { .cpu_freq_mhz = 4185, .mem_freq_mhz = 4224 }, + } + }, + }, + }, + { + .memory_type = MEMLAT_HW_LLCC, + .name = "llcc", + .mem_table = kaanapali_llcc_table, + .num_opps = ARRAY_SIZE(kaanapali_llcc_table), + .grp_ev = kaanapali_llcc_grp_ev, + .monitor_cnt = 3, + .memory_range = { .min_freq_khz = 282000, .max_freq_khz = 1350000 }, + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) { + { + .name = "mon_0", + .cpu_mask = 0x3f, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 5, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 787, .mem_freq_mhz = 282 }, + { .cpu_freq_mhz = 960, .mem_freq_mhz = 350 }, + { .cpu_freq_mhz = 1536, .mem_freq_mhz = 533 }, + { .cpu_freq_mhz = 2534, .mem_freq_mhz = 806 }, + { .cpu_freq_mhz = 3552, .mem_freq_mhz = 933 }, + } + }, + { + .name = "mon_1", + .cpu_mask = 0xc0, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 8, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 614, .mem_freq_mhz = 282 }, + { .cpu_freq_mhz = 864, .mem_freq_mhz = 350 }, + { .cpu_freq_mhz = 1382, .mem_freq_mhz = 533 }, + { .cpu_freq_mhz = 2131, .mem_freq_mhz = 806 }, + { .cpu_freq_mhz = 2726, .mem_freq_mhz = 933 }, + { .cpu_freq_mhz = 3513, .mem_freq_mhz = 1066 }, + { .cpu_freq_mhz = 3916, .mem_freq_mhz = 1211 }, + { .cpu_freq_mhz = 4185, .mem_freq_mhz = 1350 }, + } + }, + { + .name = "mon_2", + .cpu_mask = 0xff, + .table_len = 2, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 2035, .mem_freq_mhz = 282 }, + { .cpu_freq_mhz = 4185, .mem_freq_mhz = 533 }, + } + }, + }, + }, + { + .memory_type = MEMLAT_HW_DDR_QOS_MOBILE, + .name = "ddr-qos", + .monitor_cnt = 4, + .mem_table = glymur_ddr_qos_table, + .num_opps = ARRAY_SIZE(glymur_ddr_qos_table), + .grp_ev = kaanapali_ddr_qos_grp_ev, + .memory_range = { .min_freq_khz = 0, .max_freq_khz = 1 }, + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) { + { + .name = "mon_0", + .cpu_mask = 0xff, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 2, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 2035, .mem_freq_mhz = 0 }, + { .cpu_freq_mhz = 4185, .mem_freq_mhz = 1 }, + } + }, + { + .name = "mon_1", + .cpu_mask = 0xc0, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 2, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 1574, .mem_freq_mhz = 0 }, + { .cpu_freq_mhz = 4185, .mem_freq_mhz = 1 }, + } + }, + { + .name = "mon_2", + .cpu_mask = 0xc0, + .ipm_ceil = 20000000, + .be_stall_floor = 1, + .table_len = 2, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 2131, .mem_freq_mhz = 0 }, + { .cpu_freq_mhz = 4185, .mem_freq_mhz = 1 }, + } + }, + { + .name = "mon_3", + .cpu_mask = 0xc0, + .table_len = 2, + .table = (const struct scmi_qcom_map_table[]) { + { .cpu_freq_mhz = 3513, .mem_freq_mhz = 0 }, + { .cpu_freq_mhz = 4185, .mem_freq_mhz = 1 }, + } + }, + }, + }, +}; + +static const struct scmi_qcom_memlat_cfg_data glymur_memlat_data = { + .memory_cfg = glymur_memory_cfg, + .common_ev = glymur_common_ev, + .cpucp_legacy_freq_method = true, + .cpucp_sample_ms = 4, + .memory_cnt = ARRAY_SIZE(glymur_memory_cfg), +}; + +static const struct scmi_qcom_memlat_cfg_data hamoa_memlat_data = { + .memory_cfg = hamoa_memory_cfg, + .common_ev = hamoa_common_ev, + .cpucp_legacy_freq_method = true, + .cpucp_sample_ms = 4, + .memory_cnt = ARRAY_SIZE(hamoa_memory_cfg), +}; + +static const struct scmi_qcom_memlat_cfg_data kaanapali_memlat_data = { + .memory_cfg = kaanapali_memory_cfg, + .common_ev = kaanapali_common_ev, + .cpucp_legacy_freq_method = true, + .cpucp_sample_ms = 4, + .memory_cnt = ARRAY_SIZE(kaanapali_memory_cfg), +}; + +static const struct of_device_id scmi_qcom_memlat_configs[] = { + { .compatible = "qcom,glymur", .data = &glymur_memlat_data }, + { .compatible = "qcom,mahua", .data = &glymur_memlat_data }, + { .compatible = "qcom,x1e80100", .data = &hamoa_memlat_data }, + { .compatible = "qcom,x1p42100", .data = &hamoa_memlat_data }, + { .compatible = "qcom,kaanapali", .data = &kaanapali_memlat_data }, + { } +}; + +static int memlat_set_param(struct scmi_qcom_memlat_info *info, void *msg, + size_t size, u32 param_id) +{ + return info->ops->set_param(info->ph, msg, size, MEMLAT_ALGO_STR, param_id); +} + +static int configure_cpucp_common_events(struct scmi_qcom_memlat_info *info) +{ + struct ev_map_msg ev = {}; + int i; + + ev.num_evs = cpu_to_le32(NUM_COMMON_EVS); + ev.hw_type = cpu_to_le32(MEMLAT_HW_TYPE_ALL); + for (i = 0; i < NUM_COMMON_EVS; i++) + ev.cid[i] = cpu_to_le32(info->cfg_data->common_ev[i]); + + return memlat_set_param(info, &ev, sizeof(ev), MEMLAT_SET_COMMON_EV_MAP); +} + +static int configure_cpucp_grp(struct device *dev, struct scmi_qcom_memlat_info *info, + int memory_index) +{ + const struct scmi_qcom_memory_cfg *cfg = &info->cfg_data->memory_cfg[memory_index]; + struct ev_map_msg ev = {}; + struct node_msg node = {}; + int ret, i; + + node.cpumask = cpu_to_le32(*cpumask_bits(cpu_possible_mask)); + node.hw_type = cpu_to_le32(cfg->memory_type); + + /* mon_type/mon_idx are don't-care for SET_MEM_GROUP; leave them zero. */ + ret = memlat_set_param(info, &node, sizeof(node), MEMLAT_SET_MEM_GROUP); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to configure mem type %d\n", + cfg->memory_type); + + ev.num_evs = cpu_to_le32(NUM_GRP_EVS); + ev.hw_type = cpu_to_le32(cfg->memory_type); + for (i = 0; i < NUM_GRP_EVS; i++) + ev.cid[i] = cpu_to_le32(cfg->grp_ev[i]); + + ret = memlat_set_param(info, &ev, sizeof(ev), MEMLAT_SET_GRP_EV_MAP); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to configure event map for mem type %d\n", + cfg->memory_type); + + return 0; +} + +static int configure_cpucp_mon(struct device *dev, struct scmi_qcom_memlat_info *info, + int memory_index, int monitor_index) +{ + const struct scmi_qcom_memory_cfg *cfg = &info->cfg_data->memory_cfg[memory_index]; + const struct scmi_qcom_monitor_cfg *mon = &cfg->monitor_cfg[monitor_index]; + struct scalar_param_msg scalar = {}; + struct map_param_msg map = {}; + struct node_msg node = {}; + int ret, i; + + if (mon->table_len > MEMLAT_MAX_MAP_ENTRIES) + return -EINVAL; + + node.cpumask = cpu_to_le32(mon->cpu_mask); + node.hw_type = cpu_to_le32(cfg->memory_type); + + /* The compute monitor passively scales with cpufreq irrespective of IPM ratio. */ + node.mon_type = cpu_to_le32(!mon->ipm_ceil); + node.mon_idx = cpu_to_le32(monitor_index); + strscpy(node.mon_name, mon->name, sizeof(node.mon_name)); + ret = memlat_set_param(info, &node, sizeof(node), MEMLAT_SET_MONITOR); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to configure monitor %s\n", mon->name); + + scalar.hw_type = cpu_to_le32(cfg->memory_type); + scalar.mon_idx = cpu_to_le32(monitor_index); + scalar.val = cpu_to_le32(mon->ipm_ceil); + ret = memlat_set_param(info, &scalar, sizeof(scalar), MEMLAT_IPM_CEIL); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to set ipm ceil for %s\n", mon->name); + + scalar.val = cpu_to_le32(mon->be_stall_floor); + ret = memlat_set_param(info, &scalar, sizeof(scalar), MEMLAT_BE_STALL_FLOOR); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to set be_stall_floor for %s\n", mon->name); + + map.hw_type = cpu_to_le32(cfg->memory_type); + map.mon_idx = cpu_to_le32(monitor_index); + map.nr_rows = cpu_to_le32(mon->table_len); + for (i = 0; i < mon->table_len; i++) { + map.tbl[i].cpu_freq_mhz = cpu_to_le16(mon->table[i].cpu_freq_mhz); + map.tbl[i].mem_freq_mhz = cpu_to_le16(mon->table[i].mem_freq_mhz); + } + ret = memlat_set_param(info, &map, sizeof(map), MEMLAT_MON_FREQ_MAP); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to configure freq_map for %s\n", mon->name); + + scalar.val = cpu_to_le32(cfg->memory_range.min_freq_khz); + ret = memlat_set_param(info, &scalar, sizeof(scalar), MEMLAT_SET_MIN_FREQ); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to set min_freq for %s\n", mon->name); + + scalar.val = cpu_to_le32(cfg->memory_range.max_freq_khz); + ret = memlat_set_param(info, &scalar, sizeof(scalar), MEMLAT_SET_MAX_FREQ); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to set max_freq for %s\n", mon->name); + + return 0; +} + +static int scmi_qcom_devfreq_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct scmi_qcom_memory_info *memory = dev_get_drvdata(dev); + const struct qcom_generic_ext_ops *ops = memory->ops; + const struct scmi_qcom_memory_cfg *cfg = memory->cfg; + u32 max_freq_khz = 0; + int ret, i; + + /* The bus's voted frequency is the max reported across all its monitors. */ + for (i = 0; i < cfg->monitor_cnt; i++) { + union cur_freq_msg msg = {}; + + msg.req.hw_type = cpu_to_le32(cfg->memory_type); + msg.req.mon_idx = cpu_to_le32(i); + + ret = ops->get_param(memory->ph, &msg, sizeof(msg.req), MEMLAT_ALGO_STR, + MEMLAT_GET_CUR_FREQ, sizeof(msg.resp)); + if (ret < 0) { + /* One monitor failing shouldn't abort the whole read-back. */ + dev_warn_once(dev, "failed to get current frequency for %s\n", + cfg->monitor_cfg[i].name); + continue; + } + + max_freq_khz = max(max_freq_khz, le32_to_cpu(msg.resp)); + } + + /* + * DDR/LLCC report a frequency in kHz (converted to Hz for the OPP + * table); DDR_QOS reports a 0/1 level mapped to its synthetic OPP keys. + */ + if (cfg->memory_range.max_freq_khz > 1) + *freq = (unsigned long)((u64)max_freq_khz * HZ_PER_KHZ); + else + *freq = max_freq_khz ? 100 : 1; + + return 0; +} + +static void scmi_qcom_memlat_teardown(struct scmi_qcom_memlat_info *info) +{ + for (int i = 0; i < info->memory_cnt; i++) { + struct scmi_qcom_memory_info *memory = info->memory[i]; + + if (!memory || !memory->fdev) + continue; + + if (memory->devfreq) + devfreq_remove_device(memory->devfreq); + dev_pm_opp_remove_all_dynamic(&memory->fdev->dev); + faux_device_destroy(memory->fdev); + } +} + +static int scmi_qcom_memlat_configure_events(struct scmi_device *sdev, + struct scmi_qcom_memlat_info *info) +{ + const struct qcom_generic_ext_ops *ops = info->ops; + struct scmi_protocol_handle *ph = info->ph; + __le32 sample_ms, freq_method; + int i, j, ret; + + ret = configure_cpucp_common_events(info); + if (ret < 0) + return dev_err_probe(&sdev->dev, ret, "failed to configure common events\n"); + + for (i = 0; i < info->memory_cnt; i++) { + ret = configure_cpucp_grp(&sdev->dev, info, i); + if (ret < 0) + return ret; + + for (j = 0; j < info->memory[i]->cfg->monitor_cnt; j++) { + ret = configure_cpucp_mon(&sdev->dev, info, i, j); + if (ret < 0) + return ret; + } + } + + sample_ms = cpu_to_le32(info->cfg_data->cpucp_sample_ms); + ret = memlat_set_param(info, &sample_ms, sizeof(sample_ms), MEMLAT_SAMPLE_MS); + if (ret < 0) + return dev_err_probe(&sdev->dev, ret, "failed to set sample_ms\n"); + + freq_method = cpu_to_le32(info->cfg_data->cpucp_legacy_freq_method); + ret = memlat_set_param(info, &freq_method, sizeof(freq_method), + MEMLAT_SET_EFFECTIVE_FREQ_METHOD); + if (ret < 0) + return dev_err_probe(&sdev->dev, ret, + "failed to set effective frequency calc method\n"); + + /* Start sampling and voting timer */ + ret = ops->start_activity(ph, NULL, 0, MEMLAT_ALGO_STR, MEMLAT_START_TIMER); + if (ret < 0) + return dev_err_probe(&sdev->dev, ret, "failed to start memory group timer\n"); + + for (i = 0; i < info->memory_cnt; i++) { + struct scmi_qcom_memory_info *memory = info->memory[i]; + const struct scmi_qcom_memory_range *range = &memory->cfg->memory_range; + struct devfreq_dev_profile *profile = &memory->profile; + + /* + * CPUCP re-evaluates and re-votes at most once per cpucp_sample_ms, + * so a new operating point can appear only that often. Polling at half + * that period guarantees each distinct vote is observed in trans_stat + * before it can change again. + */ + profile->polling_ms = max(1U, info->cfg_data->cpucp_sample_ms / 2); + profile->get_cur_freq = scmi_qcom_devfreq_get_cur_freq; + profile->initial_freq = range->max_freq_khz > 1 ? + (unsigned long)((u64)range->min_freq_khz * HZ_PER_KHZ) : + range->min_freq_khz; + + faux_device_set_drvdata(memory->fdev, memory); + + memory->devfreq = devfreq_add_device(&memory->fdev->dev, profile, + DEVFREQ_GOV_REMOTE, NULL); + if (IS_ERR(memory->devfreq)) { + ret = PTR_ERR(memory->devfreq); + memory->devfreq = NULL; + ops->stop_activity(ph, NULL, 0, MEMLAT_ALGO_STR, MEMLAT_STOP_TIMER); + return dev_err_probe(&sdev->dev, ret, "failed to add devfreq device\n"); + } + } + + return 0; +} + +static int scmi_qcom_memlat_setup(struct scmi_device *sdev, struct scmi_qcom_memlat_info *info) +{ + const struct scmi_qcom_memlat_cfg_data *cfg_data; + int ret, i, j; + + cfg_data = of_machine_get_match_data(scmi_qcom_memlat_configs); + if (!cfg_data) { + dev_dbg(&sdev->dev, "no memlat config data for this platform\n"); + return -ENODEV; + } + + info->cfg_data = cfg_data; + info->memory = devm_kcalloc(&sdev->dev, cfg_data->memory_cnt, + sizeof(*info->memory), GFP_KERNEL); + if (!info->memory) + return -ENOMEM; + + for (i = 0; i < cfg_data->memory_cnt; i++) { + const struct scmi_qcom_memory_cfg *memory_cfg = &cfg_data->memory_cfg[i]; + struct scmi_qcom_memory_info *memory; + + memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL); + if (!memory) { + ret = -ENOMEM; + goto err_teardown; + } + + memory->cfg = memory_cfg; + memory->ops = info->ops; + memory->ph = info->ph; + info->memory[i] = memory; + + memory->fdev = faux_device_create(memory_cfg->name, &sdev->dev, NULL); + if (!memory->fdev) { + ret = dev_err_probe(&sdev->dev, -ENODEV, + "failed to create faux device\n"); + goto err_teardown; + } + info->memory_cnt = i + 1; + + for (j = 0; j < memory_cfg->num_opps; j++) { + struct dev_pm_opp_data data = { + .freq = memory_cfg->mem_table[j].freq, + }; + + ret = dev_pm_opp_add_dynamic(&memory->fdev->dev, &data); + if (ret) { + dev_err_probe(&sdev->dev, ret, "failed to add OPP\n"); + goto err_teardown; + } + } + } + + return 0; + +err_teardown: + scmi_qcom_memlat_teardown(info); + return ret; +} + +static int scmi_qcom_devfreq_memlat_probe(struct scmi_device *sdev) +{ + const struct scmi_handle *handle = sdev->handle; + const struct qcom_generic_ext_ops *ops; + struct scmi_qcom_memlat_info *info; + struct scmi_protocol_handle *ph; + int ret; + + if (!handle) + return -ENODEV; + + info = devm_kzalloc(&sdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_QCOM_GENERIC, &ph); + if (IS_ERR(ops)) + return PTR_ERR(ops); + + info->ops = ops; + info->ph = ph; + + ret = scmi_qcom_memlat_setup(sdev, info); + if (ret) + return ret; + + ret = scmi_qcom_memlat_configure_events(sdev, info); + if (ret) { + scmi_qcom_memlat_teardown(info); + return ret; + } + + dev_set_drvdata(&sdev->dev, info); + + return 0; +} + +static void scmi_qcom_devfreq_memlat_remove(struct scmi_device *sdev) +{ + struct scmi_qcom_memlat_info *info = dev_get_drvdata(&sdev->dev); + int ret; + + ret = info->ops->stop_activity(info->ph, NULL, 0, MEMLAT_ALGO_STR, MEMLAT_STOP_TIMER); + if (ret < 0) + dev_err(&sdev->dev, "failed to stop memory group timer\n"); + + scmi_qcom_memlat_teardown(info); +} + +static const struct scmi_device_id scmi_id_table[] = { + { SCMI_PROTOCOL_QCOM_GENERIC, "qcom-generic-ext" }, + { }, +}; +MODULE_DEVICE_TABLE(scmi, scmi_id_table); + +static struct scmi_driver scmi_qcom_devfreq_memlat_driver = { + .name = "scmi-qcom-devfreq-memlat", + .probe = scmi_qcom_devfreq_memlat_probe, + .remove = scmi_qcom_devfreq_memlat_remove, + .id_table = scmi_id_table, +}; +module_scmi_driver(scmi_qcom_devfreq_memlat_driver); + +MODULE_AUTHOR("Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com>"); +MODULE_AUTHOR("Sibi Sankar <sibi.sankar@oss.qualcomm.com>"); +MODULE_DESCRIPTION("Qualcomm SCMI memlat devfreq driver"); +MODULE_LICENSE("GPL"); -- 2.34.1 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH RFC 08/10] arm64: dts: qcom: glymur: Enable LLCC/DDR/DDR_QOS DVFS 2026-07-24 7:18 [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Pragnesh Papaniya ` (6 preceding siblings ...) 2026-07-24 7:18 ` [PATCH RFC 07/10] PM / devfreq: Introduce the QCOM SCMI Memlat devfreq driver Pragnesh Papaniya @ 2026-07-24 7:18 ` Pragnesh Papaniya 2026-07-24 7:18 ` [PATCH RFC 09/10] arm64: dts: qcom: hamoa: " Pragnesh Papaniya ` (2 subsequent siblings) 10 siblings, 0 replies; 20+ messages in thread From: Pragnesh Papaniya @ 2026-07-24 7:18 UTC (permalink / raw) To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Bjorn Andersson, Konrad Dybcio Cc: Pragnesh Papaniya, Rajendra Nayak, Pankaj Patil, linux-arm-msm, linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm, linux-tegra From: Sibi Sankar <sibi.sankar@oss.qualcomm.com> On Qualcomm Glymur SoCs, the memlat governor and the mechanism for controlling the LLCC and DDR/DDR_QOS frequencies run on the CPU Control Processor (CPUCP). Add the CPUCP mailbox and SCMI nodes required for the QCOM SCMI Generic Extension protocol to probe and get functional bus DVFS on Glymur/Mahua SoCs. Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com> Signed-off-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> --- arch/arm64/boot/dts/qcom/glymur.dtsi | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi index f32af66a3f1c..32ac471e6727 100644 --- a/arch/arm64/boot/dts/qcom/glymur.dtsi +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi @@ -420,6 +420,20 @@ scmi_perf: protocol@13 { #power-domain-cells = <1>; }; }; + + cpucp_scmi: scmi-1 { + compatible = "arm,scmi"; + mboxes = <&cpucp_mbox 0>, <&cpucp_mbox 2>; + mbox-names = "tx", "rx"; + shmem = <&cpucp_scp_lpri0>, <&cpucp_scp_lpri1>; + + #address-cells = <1>; + #size-cells = <0>; + + scmi_vendor: protocol@80 { + reg = <0x80>; + }; + }; }; clk_virt: interconnect-0 { @@ -7658,6 +7672,13 @@ pdp0_mbox: mailbox@17610000 { #mbox-cells = <1>; }; + cpucp_mbox: mailbox@17620000 { + compatible = "qcom,glymur-cpucp-mbox", "qcom,x1e80100-cpucp-mbox"; + reg = <0x0 0x17620000 0 0x8000>, <0 0x18830000 0 0x8000>; + interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>; + #mbox-cells = <1>; + }; + timer@17810000 { compatible = "arm,armv7-timer-mem"; reg = <0x0 0x17810000 0x0 0x1000>; @@ -7842,6 +7863,26 @@ rpmhpd_opp_turbo_l1: opp-416 { }; }; + cpucp_sram: sram@18b4e000 { + compatible = "mmio-sram"; + reg = <0x0 0x18b4e000 0x0 0x400>; + + #address-cells = <1>; + #size-cells = <1>; + + ranges = <0x0 0x0 0x18b4e000 0x400>; + + cpucp_scp_lpri0: scp-sram-section@0 { + compatible = "arm,scmi-shmem"; + reg = <0x0 0x200>; + }; + + cpucp_scp_lpri1: scp-sram-section@200 { + compatible = "arm,scmi-shmem"; + reg = <0x200 0x200>; + }; + }; + nsi_noc: interconnect@1d600000 { compatible = "qcom,glymur-nsinoc"; reg = <0x0 0x1d600000 0x0 0x14080>; -- 2.34.1 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH RFC 09/10] arm64: dts: qcom: hamoa: Enable LLCC/DDR/DDR_QOS DVFS 2026-07-24 7:18 [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Pragnesh Papaniya ` (7 preceding siblings ...) 2026-07-24 7:18 ` [PATCH RFC 08/10] arm64: dts: qcom: glymur: Enable LLCC/DDR/DDR_QOS DVFS Pragnesh Papaniya @ 2026-07-24 7:18 ` Pragnesh Papaniya 2026-07-24 7:18 ` [PATCH RFC 10/10] arm64: dts: qcom: kaanapali: " Pragnesh Papaniya 2026-07-24 8:40 ` [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sudeep Holla 10 siblings, 0 replies; 20+ messages in thread From: Pragnesh Papaniya @ 2026-07-24 7:18 UTC (permalink / raw) To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Bjorn Andersson, Konrad Dybcio Cc: Pragnesh Papaniya, Rajendra Nayak, Pankaj Patil, linux-arm-msm, linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm, linux-tegra From: Sibi Sankar <sibi.sankar@oss.qualcomm.com> On Qualcomm Hamoa SoCs, the memlat governor and the mechanism for controlling the LLCC and DDR/DDR_QOS frequencies run on the CPU Control Processor (CPUCP) and are exposed via the QCOM SCMI Generic Extension protocol. Add the SCMI vendor protocol node required for the QCOM SCMI Generic Extension protocol to probe and get functional bus DVFS on Hamoa/Purwa SoCs. Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com> Signed-off-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> --- arch/arm64/boot/dts/qcom/hamoa.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/hamoa.dtsi b/arch/arm64/boot/dts/qcom/hamoa.dtsi index 09527dcf9576..e9e75c4c4da6 100644 --- a/arch/arm64/boot/dts/qcom/hamoa.dtsi +++ b/arch/arm64/boot/dts/qcom/hamoa.dtsi @@ -340,6 +340,10 @@ scmi_dvfs: protocol@13 { reg = <0x13>; #power-domain-cells = <1>; }; + + scmi_vendor: protocol@80 { + reg = <0x80>; + }; }; }; -- 2.34.1 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH RFC 10/10] arm64: dts: qcom: kaanapali: Enable LLCC/DDR/DDR_QOS DVFS 2026-07-24 7:18 [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Pragnesh Papaniya ` (8 preceding siblings ...) 2026-07-24 7:18 ` [PATCH RFC 09/10] arm64: dts: qcom: hamoa: " Pragnesh Papaniya @ 2026-07-24 7:18 ` Pragnesh Papaniya 2026-07-24 8:40 ` [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sudeep Holla 10 siblings, 0 replies; 20+ messages in thread From: Pragnesh Papaniya @ 2026-07-24 7:18 UTC (permalink / raw) To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Bjorn Andersson, Konrad Dybcio Cc: Pragnesh Papaniya, Rajendra Nayak, Pankaj Patil, linux-arm-msm, linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm, linux-tegra, Jia Yang From: Jia Yang <jia.yang@oss.qualcomm.com> On Qualcomm Kaanapali SoCs, the memlat governor and the mechanism for controlling the LLCC and DDR/DDR_QOS frequencies run on the CPU Control Processor (CPUCP) and are exposed via the QCOM SCMI Generic Extension protocol. Add the CPUCP mailbox, the SCMI shared-memory regions and the CPUCP SCMI instance (scmi-1) with the Qualcomm vendor protocol@80 required for the QCOM SCMI Generic Extension protocol to probe and get functional bus DVFS on Kaanapali. Signed-off-by: Jia Yang <jia.yang@oss.qualcomm.com> Signed-off-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com> --- arch/arm64/boot/dts/qcom/kaanapali.dtsi | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/kaanapali.dtsi b/arch/arm64/boot/dts/qcom/kaanapali.dtsi index 74699bc3eadb..594fac6d27bc 100644 --- a/arch/arm64/boot/dts/qcom/kaanapali.dtsi +++ b/arch/arm64/boot/dts/qcom/kaanapali.dtsi @@ -240,6 +240,20 @@ scmi_perf: protocol@13 { #power-domain-cells = <1>; }; }; + + cpucp_scmi: scmi-1 { + compatible = "arm,scmi"; + mboxes = <&cpucp_mbox 0>, <&cpucp_mbox 2>; + mbox-names = "tx", "rx"; + shmem = <&cpucp_scp_lpri0>, <&cpucp_scp_lpri1>; + + #address-cells = <1>; + #size-cells = <0>; + + scmi_vendor: protocol@80 { + reg = <0x80>; + }; + }; }; clk_virt: interconnect-0 { @@ -5756,6 +5770,13 @@ pdp0_mbox: mailbox@17610000 { #mbox-cells = <1>; }; + cpucp_mbox: mailbox@17620000 { + compatible = "qcom,kaanapali-cpucp-mbox", "qcom,x1e80100-cpucp-mbox"; + reg = <0x0 0x17620000 0x0 0x8000>, <0x0 0x18830000 0x0 0x8000>; + interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>; + #mbox-cells = <1>; + }; + timer@17810000 { compatible = "arm,armv7-timer-mem"; reg = <0x0 0x17810000 0x0 0x1000>; @@ -5965,6 +5986,26 @@ rpmhpd_opp_super_turbo_no_cpr: opp-480 { }; }; + cpucp_sram: sram@1d838000 { + compatible = "mmio-sram"; + reg = <0x0 0x1d838000 0x0 0x400>; + + #address-cells = <1>; + #size-cells = <1>; + + ranges = <0x0 0x0 0x1d838000 0x400>; + + cpucp_scp_lpri0: scp-sram-section@0 { + compatible = "arm,scmi-shmem"; + reg = <0x0 0x200>; + }; + + cpucp_scp_lpri1: scp-sram-section@200 { + compatible = "arm,scmi-shmem"; + reg = <0x200 0x200>; + }; + }; + nsp_noc: interconnect@260c0000 { compatible = "qcom,kaanapali-nsp-noc"; reg = <0x0 0x260c0000 0x0 0x21280>; -- 2.34.1 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions 2026-07-24 7:18 [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Pragnesh Papaniya ` (9 preceding siblings ...) 2026-07-24 7:18 ` [PATCH RFC 10/10] arm64: dts: qcom: kaanapali: " Pragnesh Papaniya @ 2026-07-24 8:40 ` Sudeep Holla 2026-07-24 9:02 ` Pragnesh Papaniya 10 siblings, 1 reply; 20+ messages in thread From: Sudeep Holla @ 2026-07-24 8:40 UTC (permalink / raw) To: Pragnesh Papaniya Cc: Cristian Marussi, Sudeep Holla, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Bjorn Andersson, Konrad Dybcio, Rajendra Nayak, Pankaj Patil, linux-arm-msm, linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm, linux-tegra, Amir Vajid, Ramakrishna Gottimukkula, Jia Yang On Fri, Jul 24, 2026 at 12:48:05PM +0530, Pragnesh Papaniya wrote: [...] > > Changes in v8: Yet the patches are labelled as if they are RFC and/or v1, so it gets really hard to track. -- Regards, Sudeep ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions 2026-07-24 8:40 ` [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sudeep Holla @ 2026-07-24 9:02 ` Pragnesh Papaniya 0 siblings, 0 replies; 20+ messages in thread From: Pragnesh Papaniya @ 2026-07-24 9:02 UTC (permalink / raw) To: Sudeep Holla Cc: Cristian Marussi, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Bjorn Andersson, Konrad Dybcio, Rajendra Nayak, Pankaj Patil, linux-arm-msm, linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm, linux-tegra, Amir Vajid, Ramakrishna Gottimukkula, Jia Yang On 24-Jul-26 2:10 PM, Sudeep Holla wrote: > On Fri, Jul 24, 2026 at 12:48:05PM +0530, Pragnesh Papaniya wrote: > > [...] > >> >> Changes in v8: > > Yet the patches are labelled as if they are RFC and/or v1, so it gets really > hard to track. It was a miss. Won't happen again. Thanks, pragnesh > ^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-07-24 9:13 UTC | newest] Thread overview: 20+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-24 7:18 [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Pragnesh Papaniya 2026-07-24 7:18 ` [PATCH RFC 01/10] firmware: arm_scmi: Add SCMI QCOM Generic Extension Protocol documentation Pragnesh Papaniya 2026-07-24 7:27 ` sashiko-bot 2026-07-24 9:13 ` Sudeep Holla 2026-07-24 7:18 ` [PATCH RFC 02/10] dt-bindings: firmware: arm,scmi: Add Qualcomm Generic Extension Protocol Pragnesh Papaniya 2026-07-24 7:28 ` sashiko-bot 2026-07-24 7:18 ` [PATCH RFC 03/10] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions Pragnesh Papaniya 2026-07-24 7:30 ` sashiko-bot 2026-07-24 7:18 ` [PATCH RFC 04/10] PM / devfreq: Add new target_freq attribute flag for governors Pragnesh Papaniya 2026-07-24 7:35 ` sashiko-bot 2026-07-24 7:18 ` [PATCH RFC 05/10] PM / devfreq: Add new track_remote " Pragnesh Papaniya 2026-07-24 7:31 ` sashiko-bot 2026-07-24 7:18 ` [PATCH RFC 06/10] PM / devfreq: Add a governor for tracking remote device frequencies Pragnesh Papaniya 2026-07-24 7:33 ` sashiko-bot 2026-07-24 7:18 ` [PATCH RFC 07/10] PM / devfreq: Introduce the QCOM SCMI Memlat devfreq driver Pragnesh Papaniya 2026-07-24 7:18 ` [PATCH RFC 08/10] arm64: dts: qcom: glymur: Enable LLCC/DDR/DDR_QOS DVFS Pragnesh Papaniya 2026-07-24 7:18 ` [PATCH RFC 09/10] arm64: dts: qcom: hamoa: " Pragnesh Papaniya 2026-07-24 7:18 ` [PATCH RFC 10/10] arm64: dts: qcom: kaanapali: " Pragnesh Papaniya 2026-07-24 8:40 ` [PATCH RFC 00/10] firmware: arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sudeep Holla 2026-07-24 9:02 ` Pragnesh Papaniya
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.