Devicetree
 help / color / mirror / Atom feed
* [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

* [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

* [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

* [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

* [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

* [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

* [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 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 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

* 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

* 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

* 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

* 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

* 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

* 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

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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox