Linux Power Management development
 help / color / mirror / Atom feed
* [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
@ 2026-05-07  6:22 Sibi Sankar
  2026-05-07  6:22 ` [RFC V6 1/8] firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation Sibi Sankar
                   ` (8 more replies)
  0 siblings, 9 replies; 13+ messages in thread
From: Sibi Sankar @ 2026-05-07  6:22 UTC (permalink / raw)
  To: cristian.marussi, sudeep.holla, cw00.choi, kyungmin.park,
	myungjoo.ham, konradybcio, andersson
  Cc: linux-arm-msm, dri-devel, devicetree, linux-kernel, arm-scmi,
	linux-tegra, linux-pm, dmitry.baryshkov, jonathanh,
	thierry.reding, digetx, conor+dt, krzk+dt, robh

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. On Qualcomm Glymur and Hamoa SoCs, the memlat governor and the
mechanism to control the various caches and ram is hosted on the CPU Control
Processor (CPUCP) and the method to tweak and start the governor is exposed
through the QCOM SCMI Generic Extension Protocol.

This series introduces the devfreq scmi client driver that uses the memlat
algorithm string hosted on QCOM SCMI Generic Extension Protocol to detect
memory latency workloads and control frequency/level of the various memory
buses (DDR/LLCC/DDR_QOS). The DDR/LLCC/DDR_QOS are modelled as devfreq
devices, with the governor set to remote devfreq governor. This serves as
a way to get a basic insight into the device operation through trans_stat
and provides for ways to further tweak the parameters of the remote
governor.

Transtat data for DDR/LLCC/DDR_QOS is now available in this series:

#cat llcc/trans_stat 
From  :   To
315000000 479000000 545000000 725000000 840000000 95900000010900000001211000000   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

QCOM SCMI Generic Vendor protocol background:
It was found that a lot of the vendor protocol used internally was
for debug/internal development purposes that would either be super
SoC specific or had to be disabled because of some features being
fused out during production. This lead to a large number of vendor
protocol numbers being quickly consumed and were never released
either. Using a generic vendor protocol with functionality abstracted
behind algorithm strings gave us the flexibility of allowing such
functionality exist during initial development/debugging while
still being able to expose functionality like memlat once they have
matured enough. The param-ids are certainly expected to act as ABI
for algorithms strings like MEMLAT.

Thanks in advance for taking time to review the series.

Changes in v5:
 - 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.

 Changes in v4:
 - 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]

Changes in v3:
 - 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]

Changes in v2:
 - 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.

Changes in v1:
 - 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.

Sibi Sankar (8):
  firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation
  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 device
  arm64: dts: qcom: glymur: Enable LLCC/DDR/DDR_QOS dvfs
  arm64: dts: qcom: hamoa: Enable LLCC/DDR/DDR_QOS dvfs

 arch/arm64/boot/dts/qcom/glymur.dtsi          |  42 ++
 arch/arm64/boot/dts/qcom/hamoa.dtsi           |   4 +
 drivers/devfreq/Kconfig                       |  21 +
 drivers/devfreq/Makefile                      |   2 +
 drivers/devfreq/devfreq.c                     |   9 +
 drivers/devfreq/governor_passive.c            |   1 +
 drivers/devfreq/governor_performance.c        |   1 +
 drivers/devfreq/governor_powersave.c          |   1 +
 drivers/devfreq/governor_remote.c             |  80 +++
 drivers/devfreq/governor_simpleondemand.c     |   1 +
 drivers/devfreq/governor_userspace.c          |   1 +
 drivers/devfreq/hisi_uncore_freq.c            |   1 +
 drivers/devfreq/scmi-qcom-memlat-cfg.h        | 473 ++++++++++++++
 drivers/devfreq/scmi-qcom-memlat-devfreq.c    | 582 ++++++++++++++++++
 drivers/devfreq/tegra30-devfreq.c             |   3 +-
 drivers/firmware/arm_scmi/Kconfig             |   1 +
 drivers/firmware/arm_scmi/Makefile            |   1 +
 .../firmware/arm_scmi/vendors/qcom/Kconfig    |  15 +
 .../firmware/arm_scmi/vendors/qcom/Makefile   |   2 +
 .../arm_scmi/vendors/qcom/qcom-generic-ext.c  | 135 ++++
 .../arm_scmi/vendors/qcom/qcom_generic.rst    | 211 +++++++
 include/linux/devfreq-governor.h              |   4 +
 include/linux/devfreq.h                       |   1 +
 include/linux/scmi_qcom_protocol.h            |  37 ++
 24 files changed, 1628 insertions(+), 1 deletion(-)
 create mode 100644 drivers/devfreq/governor_remote.c
 create mode 100644 drivers/devfreq/scmi-qcom-memlat-cfg.h
 create mode 100644 drivers/devfreq/scmi-qcom-memlat-devfreq.c
 create mode 100644 drivers/firmware/arm_scmi/vendors/qcom/Kconfig
 create mode 100644 drivers/firmware/arm_scmi/vendors/qcom/Makefile
 create mode 100644 drivers/firmware/arm_scmi/vendors/qcom/qcom-generic-ext.c
 create mode 100644 drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst
 create mode 100644 include/linux/scmi_qcom_protocol.h


base-commit: 9d0d467c3572e93c5faa2e5906a8bbcd70b24efd
-- 
2.34.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [RFC V6 1/8] firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation
  2026-05-07  6:22 [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
@ 2026-05-07  6:22 ` Sibi Sankar
  2026-05-07 12:36   ` Sudeep Holla
  2026-05-07  6:22 ` [RFC V6 2/8] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions Sibi Sankar
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 13+ messages in thread
From: Sibi Sankar @ 2026-05-07  6:22 UTC (permalink / raw)
  To: cristian.marussi, sudeep.holla, cw00.choi, kyungmin.park,
	myungjoo.ham, konradybcio, andersson
  Cc: linux-arm-msm, dri-devel, devicetree, linux-kernel, arm-scmi,
	linux-tegra, linux-pm, dmitry.baryshkov, jonathanh,
	thierry.reding, digetx, conor+dt, krzk+dt, robh

Add QCOM System Control Management Interface (SCMI) Generic Vendor
Extensions Protocol documentation.

Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com>
---

@Sudeep/@Christian, Just moving the series back in RFC mode to get feedback
on the devfreq part of the series. Will add a lot more documentation in the
next re-spin, so definitely not ignoring your earlier comments :).

 .../arm_scmi/vendors/qcom/qcom_generic.rst    | 211 ++++++++++++++++++
 1 file changed, 211 insertions(+)
 create mode 100644 drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst

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..141bc932e30f
--- /dev/null
+++ b/drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst
@@ -0,0 +1,211 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+===============================================================================
+QCOM System Control and Management Interface(SCMI) Vendor Protocols Extension
+===============================================================================
+
+:Copyright: |copy| 2024, Qualcomm Innovation Center, Inc. All rights reserved.
+
+:Author: Sibi Sankar <quic_sibis@quicinc.com>
+
+SCMI_GENERIC: System Control and Management Interface QCOM Generic Vendor Protocol
+==================================================================================
+
+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. It implements an interface compliant
+with the Arm SCMI Specification with additional vendor specific commands as
+detailed below.
+
+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 to 0.                        |
+|                  |Bits[15:8] Number of agents in the system                  |
+|                  |Bits[7:0] Number of vendor protocols in the system         |
++------------------+-----------------------------------------------------------+
+
+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 id's 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.                                     |
++------------------+-----------------------------------------------------------+
+|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.                        |
++------------------+-----------------------------------------------------------+
+|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.                       |
++------------------+-----------------------------------------------------------+
+
+QCOM_SCMI_START_ACTIVITY
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+message_id: 0x12
+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 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.                                     |
++------------------+-----------------------------------------------------------+
+|Return values                                                                 |
++------------------+-----------------------------------------------------------+
+|Name              |Description                                                |
++------------------+-----------------------------------------------------------+
+|int32 status      |SUCCESS: if the activity performed by the algorithm string |
+|                  |starts successfully.                                       |
+|                  |NOT_SUPPORTED: if the algorithm string does not have any.  |
+|                  |matches or if the activity is already running.             |
++------------------+-----------------------------------------------------------+
+
+QCOM_SCMI_STOP_ACTIVITY
+~~~~~~~~~~~~~~~~~~~~~~~
+
+message_id: 0x13
+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 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.                                     |
++------------------+-----------------------------------------------------------+
+|Return values                                                                 |
++------------------+-----------------------------------------------------------+
+|Name              |Description                                                |
++------------------+-----------------------------------------------------------+
+|int32 status      |SUCCESS: if the activity performed by the algorithm string |
+|                  |stops successfully.                                        |
+|                  |NOT_SUPPORTED: if the algorithm string does not have any   |
+|                  |matches or if the activity isn't running.                  |
++------------------+-----------------------------------------------------------+
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [RFC V6 2/8] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions
  2026-05-07  6:22 [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
  2026-05-07  6:22 ` [RFC V6 1/8] firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation Sibi Sankar
@ 2026-05-07  6:22 ` Sibi Sankar
  2026-05-07  6:22 ` [RFC V6 3/8] PM / devfreq: Add new target_freq attribute flag for governors Sibi Sankar
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Sibi Sankar @ 2026-05-07  6:22 UTC (permalink / raw)
  To: cristian.marussi, sudeep.holla, cw00.choi, kyungmin.park,
	myungjoo.ham, konradybcio, andersson
  Cc: linux-arm-msm, dri-devel, devicetree, linux-kernel, arm-scmi,
	linux-tegra, linux-pm, dmitry.baryshkov, jonathanh,
	thierry.reding, digetx, conor+dt, krzk+dt, robh, Shivnandan Kumar,
	Ramakrishna Gottimukkula, Amir Vajid

The QCOM SCMI Generic Extensions 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.

Co-developed-by: Shivnandan Kumar <quic_kshivnan@quicinc.com>
Signed-off-by: Shivnandan Kumar <quic_kshivnan@quicinc.com>
Co-developed-by: Ramakrishna Gottimukkula <quic_rgottimu@quicinc.com>
Signed-off-by: Ramakrishna Gottimukkula <quic_rgottimu@quicinc.com>
Co-developed-by: Amir Vajid <avajid@quicinc.com>
Signed-off-by: Amir Vajid <avajid@quicinc.com>
Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com>
---

@Sudeep/@Christian, Just moving the series back in RFC mode to get feedback
on the devfreq part of the series. Will add a lot more documentation in the
next re-spin, so definitely not ignoring your earlier comments :).

 drivers/firmware/arm_scmi/Kconfig             |   1 +
 drivers/firmware/arm_scmi/Makefile            |   1 +
 .../firmware/arm_scmi/vendors/qcom/Kconfig    |  15 ++
 .../firmware/arm_scmi/vendors/qcom/Makefile   |   2 +
 .../arm_scmi/vendors/qcom/qcom-generic-ext.c  | 135 ++++++++++++++++++
 include/linux/scmi_qcom_protocol.h            |  37 +++++
 6 files changed, 191 insertions(+)
 create mode 100644 drivers/firmware/arm_scmi/vendors/qcom/Kconfig
 create mode 100644 drivers/firmware/arm_scmi/vendors/qcom/Makefile
 create mode 100644 drivers/firmware/arm_scmi/vendors/qcom/qcom-generic-ext.c
 create mode 100644 include/linux/scmi_qcom_protocol.h

diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig
index e3fb36825978..a52f4d1b8b2c 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..5dd9e8a6b75f
--- /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. Qcom SCMI vendor Protocol"
+	depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
+	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 ID's 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..4f9eba8ff4bd
--- /dev/null
+++ b/drivers/firmware/arm_scmi/vendors/qcom/qcom-generic-ext.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/scmi_qcom_protocol.h>
+
+#include "../../common.h"
+
+/**
+ * enum qcom_generic_ext_protocol_cmd - vendor specific commands supported by SCMI Qualcomm
+ *                                      generic vendor protocol.
+ *
+ * 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.
+ *
+ * @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[];
+};
+
+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;
+
+	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->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);
+	memcpy(msg->buf, buf, buf_len);
+
+	ret = ph->xops->do_xfer(ph, t);
+	if (!ret && rx_size)
+		memcpy(buf, t->rx.buf, t->rx.len);
+	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 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_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..465b2522ca29
--- /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) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#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] 13+ messages in thread

* [RFC V6 3/8] PM / devfreq: Add new target_freq attribute flag for governors
  2026-05-07  6:22 [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
  2026-05-07  6:22 ` [RFC V6 1/8] firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation Sibi Sankar
  2026-05-07  6:22 ` [RFC V6 2/8] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions Sibi Sankar
@ 2026-05-07  6:22 ` Sibi Sankar
  2026-05-07  6:22 ` [RFC V6 4/8] PM / devfreq: Add new track_remote " Sibi Sankar
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Sibi Sankar @ 2026-05-07  6:22 UTC (permalink / raw)
  To: cristian.marussi, sudeep.holla, cw00.choi, kyungmin.park,
	myungjoo.ham, konradybcio, andersson
  Cc: linux-arm-msm, dri-devel, devicetree, linux-kernel, arm-scmi,
	linux-tegra, linux-pm, dmitry.baryshkov, jonathanh,
	thierry.reding, digetx, conor+dt, krzk+dt, robh

Introduce the target_freq attribute flag as a pre-cursor to adding support
for devfreq governors who do not explicitly allow for a method to get/set
target frequency.

Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com>
---
 drivers/devfreq/devfreq.c                 | 4 ++++
 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 +++
 9 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index e5d3f9cf94dc..85e937e321e8 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -1526,6 +1526,10 @@ static ssize_t target_freq_show(struct device *dev,
 {
 	struct devfreq *df = to_devfreq(dev);
 
+	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 4d00d813c8ac..0800116e3334 100644
--- a/drivers/devfreq/hisi_uncore_freq.c
+++ b/drivers/devfreq/hisi_uncore_freq.c
@@ -399,6 +399,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] 13+ messages in thread

* [RFC V6 4/8] PM / devfreq: Add new track_remote flag for governors
  2026-05-07  6:22 [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
                   ` (2 preceding siblings ...)
  2026-05-07  6:22 ` [RFC V6 3/8] PM / devfreq: Add new target_freq attribute flag for governors Sibi Sankar
@ 2026-05-07  6:22 ` Sibi Sankar
  2026-05-07  6:22 ` [RFC V6 5/8] PM / devfreq: Add a governor for tracking remote device frequencies Sibi Sankar
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Sibi Sankar @ 2026-05-07  6:22 UTC (permalink / raw)
  To: cristian.marussi, sudeep.holla, cw00.choi, kyungmin.park,
	myungjoo.ham, konradybcio, andersson
  Cc: linux-arm-msm, dri-devel, devicetree, linux-kernel, arm-scmi,
	linux-tegra, linux-pm, dmitry.baryshkov, jonathanh,
	thierry.reding, digetx, conor+dt, krzk+dt, robh

Add a new track_remote flag for devfreq governors as a pre-curor to
adding the devfreq governors that is responsible for tracking
frequency changes on remote devices.

This new governor flag provides for a way track the remote device's
frequency and keep the trans_stat data updated and skip the frequency
update sequency for the device.

Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com>
---
 drivers/devfreq/devfreq.c        | 5 +++++
 include/linux/devfreq-governor.h | 1 +
 2 files changed, 6 insertions(+)

diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 85e937e321e8..e0272078912b 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -348,6 +348,10 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq,
 	unsigned long cur_freq;
 	int err = 0;
 
+	/* Frequency already updated, update trans-stat info */
+	if (IS_SUPPORTED_FLAG(devfreq->governor->flags, TRACK_REMOTE))
+		goto update_status;
+
 	if (devfreq->profile->get_cur_freq)
 		devfreq->profile->get_cur_freq(devfreq->dev.parent, &cur_freq);
 	else
@@ -375,6 +379,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..83aa82c4a9b6 100644
--- a/include/linux/devfreq-governor.h
+++ b/include/linux/devfreq-governor.h
@@ -36,6 +36,7 @@
  */
 #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] 13+ messages in thread

* [RFC V6 5/8] PM / devfreq: Add a governor for tracking remote device frequencies
  2026-05-07  6:22 [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
                   ` (3 preceding siblings ...)
  2026-05-07  6:22 ` [RFC V6 4/8] PM / devfreq: Add new track_remote " Sibi Sankar
@ 2026-05-07  6:22 ` Sibi Sankar
  2026-05-07  6:22 ` [RFC V6 6/8] PM / devfreq: Introduce the QCOM SCMI Memlat devfreq device Sibi Sankar
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Sibi Sankar @ 2026-05-07  6:22 UTC (permalink / raw)
  To: cristian.marussi, sudeep.holla, cw00.choi, kyungmin.park,
	myungjoo.ham, konradybcio, andersson
  Cc: linux-arm-msm, dri-devel, devicetree, linux-kernel, arm-scmi,
	linux-tegra, linux-pm, dmitry.baryshkov, jonathanh,
	thierry.reding, digetx, conor+dt, krzk+dt, robh

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 track the frequency changes in a reliable way and provide
ways to tweaking parameters on the remote governor.

To address this introduce the new remote devfreq governor that provides for
a way to track the frequency changes on remote devices. It uses the newly
introduced target_freq attribute flag and track_remote flag to achieve
this.

Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com>
---
 drivers/devfreq/Kconfig           |  8 ++++
 drivers/devfreq/Makefile          |  1 +
 drivers/devfreq/governor_remote.c | 80 +++++++++++++++++++++++++++++++
 include/linux/devfreq.h           |  1 +
 4 files changed, 90 insertions(+)
 create mode 100644 drivers/devfreq/governor_remote.c

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..6bff3cdaf1e5
--- /dev/null
+++ b/drivers/devfreq/governor_remote.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#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)
+{
+	unsigned long cur_freq = 0;
+
+	if (devfreq->profile->get_cur_freq)
+		devfreq->profile->get_cur_freq(devfreq->dev.parent, &cur_freq);
+
+	*freq = cur_freq;
+
+	return 0;
+}
+
+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;
+
+	default:
+		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 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] 13+ messages in thread

* [RFC V6 6/8] PM / devfreq: Introduce the QCOM SCMI Memlat devfreq device
  2026-05-07  6:22 [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
                   ` (4 preceding siblings ...)
  2026-05-07  6:22 ` [RFC V6 5/8] PM / devfreq: Add a governor for tracking remote device frequencies Sibi Sankar
@ 2026-05-07  6:22 ` Sibi Sankar
  2026-05-07  6:22 ` [RFC V6 7/8] arm64: dts: qcom: glymur: Enable LLCC/DDR/DDR_QOS dvfs Sibi Sankar
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Sibi Sankar @ 2026-05-07  6:22 UTC (permalink / raw)
  To: cristian.marussi, sudeep.holla, cw00.choi, kyungmin.park,
	myungjoo.ham, konradybcio, andersson
  Cc: linux-arm-msm, dri-devel, devicetree, linux-kernel, arm-scmi,
	linux-tegra, linux-pm, dmitry.baryshkov, jonathanh,
	thierry.reding, digetx, conor+dt, krzk+dt, robh, Shivnandan Kumar,
	Ramakrishna Gottimukkula, Amir Vajid

On Qualcomm Glymur and Hamoa SoCs, the memlat governor and the mechanism to
control the various caches and ram is hosted on the CPU Control Processor
(CPUCP) and the method to tweak and start the governor is exposed through
the QCOM SCMI Generic Extension Protocol.

Introduce the devfreq scmi client driver that uses the memlat algorithm
string hosted on QCOM SCMI Generic Extension Protocol to detect memory
latency workloads and control frequency/level of the various memory buses
(DDR/LLCC/DDR_QOS). The DDR/LLCC/DDR_QOS are modelled as devfreq devices,
with the governor set to remote devfreq governor. This serves as a way
to get a basic insight into the device operation through trans_stat and
provides for ways to further tweak the parameters of the remote governor.

Co-developed-by: Shivnandan Kumar <quic_kshivnan@quicinc.com>
Signed-off-by: Shivnandan Kumar <quic_kshivnan@quicinc.com>
Co-developed-by: Ramakrishna Gottimukkula <quic_rgottimu@quicinc.com>
Signed-off-by: Ramakrishna Gottimukkula <quic_rgottimu@quicinc.com>
Co-developed-by: Amir Vajid <avajid@quicinc.com>
Signed-off-by: Amir Vajid <avajid@quicinc.com>
Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com>
---
 drivers/devfreq/Kconfig                    |  13 +
 drivers/devfreq/Makefile                   |   1 +
 drivers/devfreq/scmi-qcom-memlat-cfg.h     | 473 +++++++++++++++++
 drivers/devfreq/scmi-qcom-memlat-devfreq.c | 582 +++++++++++++++++++++
 4 files changed, 1069 insertions(+)
 create mode 100644 drivers/devfreq/scmi-qcom-memlat-cfg.h
 create mode 100644 drivers/devfreq/scmi-qcom-memlat-devfreq.c

diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index 2caa87554914..98b5a50d3189 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -169,6 +169,19 @@ 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 Technologies Inc. SCMI client driver"
+	depends on QCOM_SCMI_GENERIC_EXT || COMPILE_TEST
+	select DEVFREQ_GOV_REMOTE
+	help
+	  This driver uses the MEMLAT (memory latency) algorithm string
+	  hosted on QCOM SCMI Vendor Protocol to detect memory latency
+	  workloads and control frequency/level of the various memory
+	  buses (DDR/LLCC/DDR_QOS).
+
+	  This driver defines/documents the parameter IDs used while configuring
+	  the memory buses.
+
 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-cfg.h b/drivers/devfreq/scmi-qcom-memlat-cfg.h
new file mode 100644
index 000000000000..e56899489db4
--- /dev/null
+++ b/drivers/devfreq/scmi-qcom-memlat-cfg.h
@@ -0,0 +1,473 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __DRIVERS_DEVFREQ_SCMI_QCOM_MEMLAT_CONFIG_H__
+#define __DRIVERS_DEVFREQ_SCMI_QCOM_MEMLAT_CONFIG_H__
+
+/*
+ * Memlat Effective Frequency Calculation Method
+ * CPUCP_EFFECTIVE_FREQ_METHOD_0 - Uses CPU Cycles and CONST Cycles to calculate
+ * CPUCP_EFFECTIVE_FREQ_METHOD_1 - Uses CPU Cycles and time period
+ */
+#define CPUCP_EFFECTIVE_FREQ_CALC_METHOD_0	0
+#define CPUCP_EFFECTIVE_FREQ_CALC_METHOD_1	1
+
+#define EV_CPU_CYCLES		0
+#define EV_CNT_CYCLES		1
+#define EV_INST_RETIRED		2
+#define EV_STALL_BACKEND_MEM	3
+#define EV_L2_D_RFILL		5
+#define INVALID_IDX		0xff
+
+#define MEMLAT_ALGO_STR		0x4D454D4C4154 /* MEMLAT */
+
+struct scmi_qcom_map_table {
+	unsigned int cpu_freq;
+	unsigned int mem_freq;
+};
+
+struct scmi_qcom_opp_data {
+	unsigned long freq;
+	unsigned int level;
+};
+
+struct scmi_qcom_memory_range {
+	unsigned int min_freq;
+	unsigned int max_freq;
+};
+
+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;
+	int 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 char *name;
+	u32 memory_type;
+	int monitor_cnt;
+	int num_opps;
+};
+
+struct scmi_qcom_memlat_cfg_data {
+	const struct scmi_qcom_memory_cfg *memory_cfg;
+	u32 cpucp_freq_method;
+	u32 cpucp_sample_ms;
+	int memory_cnt;
+};
+
+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 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 glymur_ddr_qos_table[] = {
+	{ .freq = 1, .level = 0 },
+	{ .freq = 100, .level = 1 },
+};
+
+static const struct scmi_qcom_memory_cfg glymur_memory_cfg[] = {
+	{
+		.memory_type = 0,
+		.name = "ddr",
+		.mem_table = glymur_ddr_table,
+		.num_opps = ARRAY_SIZE(glymur_ddr_table),
+		.monitor_cnt = 4,
+		.memory_range = { .min_freq = 547000, .max_freq = 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 = 960, .mem_freq = 547000 },
+					{ .cpu_freq = 1133, .mem_freq = 1353000 },
+					{ .cpu_freq = 1594, .mem_freq = 1555000 },
+					{ .cpu_freq = 1920, .mem_freq = 1708000 },
+					{ .cpu_freq = 2228, .mem_freq = 2736000 },
+					{ .cpu_freq = 2362, .mem_freq = 3187000 },
+					{ .cpu_freq = 2650, .mem_freq = 3686000 },
+					{ .cpu_freq = 2938, .mem_freq = 4761000 },
+				}
+			},
+			{
+				.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 = 356, .mem_freq = 547000 },
+					{ .cpu_freq = 1018, .mem_freq = 1353000 },
+					{ .cpu_freq = 1536, .mem_freq = 1555000 },
+					{ .cpu_freq = 1748, .mem_freq = 1708800 },
+					{ .cpu_freq = 2324, .mem_freq = 2736000 },
+					{ .cpu_freq = 2496, .mem_freq = 3187000 },
+					{ .cpu_freq = 2900, .mem_freq = 3686000 },
+					{ .cpu_freq = 3514, .mem_freq = 4761000 },
+				}
+			},
+			{
+				.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 = 356, .mem_freq = 547000 },
+					{ .cpu_freq = 1018, .mem_freq = 1353000 },
+					{ .cpu_freq = 1536, .mem_freq = 1555000 },
+					{ .cpu_freq = 1748, .mem_freq = 1708800 },
+					{ .cpu_freq = 2324, .mem_freq = 2736000 },
+					{ .cpu_freq = 2496, .mem_freq = 3187000 },
+					{ .cpu_freq = 2900, .mem_freq = 3686000 },
+					{ .cpu_freq = 3514, .mem_freq = 4761000 },
+				}
+			},
+			{
+				.name = "mon_3",
+				.cpu_mask = 0x3ffff,
+				.table_len = 4,
+				.table = (const struct scmi_qcom_map_table[]) {
+					{ .cpu_freq = 2823, .mem_freq = 547000 },
+					{ .cpu_freq = 3034, .mem_freq = 1555000 },
+					{ .cpu_freq = 3226, .mem_freq = 1708000 },
+					{ .cpu_freq = 5012, .mem_freq = 2092000 },
+				}
+			},
+		},
+	},
+	{
+		.memory_type = 1,
+		.name = "llcc",
+		.mem_table = glymur_llcc_table,
+		.num_opps = ARRAY_SIZE(glymur_llcc_table),
+		.monitor_cnt = 3,
+		.memory_range = { .min_freq = 315000, .max_freq = 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 = 960, .mem_freq = 315000 },
+					{ .cpu_freq = 1113, .mem_freq = 479000 },
+					{ .cpu_freq = 1594, .mem_freq = 545000 },
+					{ .cpu_freq = 1920, .mem_freq = 725000 },
+					{ .cpu_freq = 2362, .mem_freq = 840000 },
+					{ .cpu_freq = 2650, .mem_freq = 959000 },
+					{ .cpu_freq = 2938, .mem_freq = 1211000 },
+				}
+			},
+			{
+				.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 = 356, .mem_freq = 315000 },
+					{ .cpu_freq = 1018, .mem_freq = 479000 },
+					{ .cpu_freq = 1536, .mem_freq = 545000 },
+					{ .cpu_freq = 1748, .mem_freq = 725000 },
+					{ .cpu_freq = 2496, .mem_freq = 840000 },
+					{ .cpu_freq = 2900, .mem_freq = 959000 },
+					{ .cpu_freq = 3514, .mem_freq = 1211000 },
+				}
+			},
+			{
+				.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 = 356, .mem_freq = 315000 },
+					{ .cpu_freq = 1018, .mem_freq = 479000 },
+					{ .cpu_freq = 1536, .mem_freq = 545000 },
+					{ .cpu_freq = 1748, .mem_freq = 725000 },
+					{ .cpu_freq = 2496, .mem_freq = 840000 },
+					{ .cpu_freq = 2900, .mem_freq = 959000 },
+					{ .cpu_freq = 3514, .mem_freq = 1211000 },
+				}
+			},
+		},
+	},
+	{
+		.memory_type = 2,
+		.name = "ddr-qos",
+		.monitor_cnt = 3,
+		.mem_table = glymur_ddr_qos_table,
+		.num_opps = ARRAY_SIZE(glymur_ddr_qos_table),
+		.memory_range = { .min_freq = 0, .max_freq = 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 = 2362, .mem_freq = 0 },
+					{ .cpu_freq = 2938, .mem_freq = 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 = 2496, .mem_freq = 0 },
+					{ .cpu_freq = 3514, .mem_freq = 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 = 2496, .mem_freq = 0 },
+					{ .cpu_freq = 3514, .mem_freq = 1 },
+				}
+			},
+		},
+	},
+};
+
+static const struct scmi_qcom_memory_cfg hamoa_memory_cfg[] = {
+	{
+		.memory_type = 0,
+		.name = "ddr",
+		.mem_table = hamoa_ddr_table,
+		.num_opps = ARRAY_SIZE(hamoa_ddr_table),
+		.monitor_cnt = 4,
+		.memory_range = { .min_freq = 200000, .max_freq = 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 = 999, .mem_freq = 547000 },
+					{ .cpu_freq = 1440, .mem_freq = 768000 },
+					{ .cpu_freq = 1671, .mem_freq = 1555000 },
+					{ .cpu_freq = 2189, .mem_freq = 2092000 },
+					{ .cpu_freq = 2516, .mem_freq = 3187000 },
+					{ .cpu_freq = 3860, .mem_freq = 4224000 },
+				}
+			},
+			{
+				.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 = 999, .mem_freq = 547000 },
+					{ .cpu_freq = 1440, .mem_freq = 768000 },
+					{ .cpu_freq = 1671, .mem_freq = 1555000 },
+					{ .cpu_freq = 2189, .mem_freq = 2092000 },
+					{ .cpu_freq = 2516, .mem_freq = 3187000 },
+					{ .cpu_freq = 3860, .mem_freq = 4224000 },
+				}
+			},
+			{
+				.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 = 999, .mem_freq = 547000 },
+					{ .cpu_freq = 1440, .mem_freq = 768000 },
+					{ .cpu_freq = 1671, .mem_freq = 1555000 },
+					{ .cpu_freq = 2189, .mem_freq = 2092000 },
+					{ .cpu_freq = 2516, .mem_freq = 3187000 },
+					{ .cpu_freq = 3860, .mem_freq = 4224000 },
+				}
+			},
+			{
+				.name = "mon_3",
+				.cpu_mask = 0xfff,
+				.table_len = 4,
+				.table = (const struct scmi_qcom_map_table[]) {
+					{ .cpu_freq = 1440, .mem_freq = 547000 },
+					{ .cpu_freq = 2189, .mem_freq = 768000 },
+					{ .cpu_freq = 2516, .mem_freq = 1555000 },
+					{ .cpu_freq = 3860, .mem_freq = 2092000 },
+				}
+			},
+		},
+	},
+	{
+		.memory_type = 1,
+		.name = "llcc",
+		.mem_table = hamoa_llcc_table,
+		.num_opps = ARRAY_SIZE(hamoa_llcc_table),
+		.monitor_cnt = 3,
+		.memory_range = { .min_freq = 300000, .max_freq = 1067000},
+		.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 = 999, .mem_freq = 300000 },
+					{ .cpu_freq = 1440, .mem_freq = 466000 },
+					{ .cpu_freq = 1671, .mem_freq = 600000 },
+					{ .cpu_freq = 2189, .mem_freq = 806000 },
+					{ .cpu_freq = 2516, .mem_freq = 933000 },
+					{ .cpu_freq = 3860, .mem_freq = 1066000 },
+				}
+			},
+			{
+				.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 = 999, .mem_freq = 300000 },
+					{ .cpu_freq = 1440, .mem_freq = 466000 },
+					{ .cpu_freq = 1671, .mem_freq = 600000 },
+					{ .cpu_freq = 2189, .mem_freq = 806000 },
+					{ .cpu_freq = 2516, .mem_freq = 933000 },
+					{ .cpu_freq = 3860, .mem_freq = 1066000 },
+				}
+			},
+			{
+				.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 = 999, .mem_freq = 300000 },
+					{ .cpu_freq = 1440, .mem_freq = 466000 },
+					{ .cpu_freq = 1671, .mem_freq = 600000 },
+					{ .cpu_freq = 2189, .mem_freq = 806000 },
+					{ .cpu_freq = 2516, .mem_freq = 933000 },
+					{ .cpu_freq = 3860, .mem_freq = 1066000 },
+				}
+			},
+		},
+	},
+	{
+		.memory_type = 2,
+		.name = "ddr-qos",
+		.monitor_cnt = 3,
+		.mem_table = glymur_ddr_qos_table,
+		.num_opps = ARRAY_SIZE(glymur_ddr_qos_table),
+		.memory_range = { .min_freq = 0, .max_freq = 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 = 2189, .mem_freq = 0 },
+					{ .cpu_freq = 3860, .mem_freq = 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 = 2189, .mem_freq = 0 },
+					{ .cpu_freq = 3860, .mem_freq = 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 = 2189, .mem_freq = 0 },
+					{ .cpu_freq = 3860, .mem_freq = 1 },
+				}
+			},
+		},
+	},
+};
+
+static const struct scmi_qcom_memlat_cfg_data glymur_memlat_data = {
+	.memory_cfg = glymur_memory_cfg,
+	.cpucp_freq_method = CPUCP_EFFECTIVE_FREQ_CALC_METHOD_1,
+	.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,
+	.cpucp_freq_method = CPUCP_EFFECTIVE_FREQ_CALC_METHOD_1,
+	.cpucp_sample_ms = 4,
+	.memory_cnt = ARRAY_SIZE(hamoa_memory_cfg),
+};
+
+#endif
diff --git a/drivers/devfreq/scmi-qcom-memlat-devfreq.c b/drivers/devfreq/scmi-qcom-memlat-devfreq.c
new file mode 100644
index 000000000000..c75bfb16bb2b
--- /dev/null
+++ b/drivers/devfreq/scmi-qcom-memlat-devfreq.c
@@ -0,0 +1,582 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/cpu.h>
+#include <linux/devfreq.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/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/scmi_protocol.h>
+#include <linux/scmi_qcom_protocol.h>
+#include <linux/units.h>
+
+#define MAX_MEMORY_TYPES			4
+#define MAX_MONITOR_CNT				5
+#define MAX_NAME_LEN				20
+#define MAX_MAP_ENTRIES				10
+
+#include "scmi-qcom-memlat-cfg.h"
+
+/**
+ * enum scmi_memlat_protocol_cmd - parameter_ids supported by the "MEMLAT" algo_str hosted
+ *                                 by the Qualcomm Generic Vendor Protocol on the SCMI controller.
+ *
+ * MEMLAT (Memory Latency) monitors the counters to detect memory latency bound workloads
+ * and scales the frequency/levels of the memory buses accordingly.
+ *
+ * @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_IPM_CEIL: set the IPM (Instruction Per Misses) ceiling 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 max frequency of the memory bus.
+ * @MEMLAT_SET_MAX_FREQ: set the min frequency 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_SET_EFFECTIVE_FREQ_METHOD: set the method used to determine cpu frequency.
+ */
+enum scmi_memlat_protocol_cmd {
+	MEMLAT_SET_MEM_GROUP = 16,
+	MEMLAT_SET_MONITOR,
+	MEMLAT_SET_COMMON_EV_MAP,
+	MEMLAT_SET_GRP_EV_MAP,
+	MEMLAT_IPM_CEIL = 23,
+	MEMLAT_BE_STALL_FLOOR = 25,
+	MEMLAT_SAMPLE_MS = 31,
+	MEMLAT_MON_FREQ_MAP,
+	MEMLAT_SET_MIN_FREQ,
+	MEMLAT_SET_MAX_FREQ,
+	MEMLAT_GET_CUR_FREQ,
+	MEMLAT_START_TIMER = 36,
+	MEMLAT_STOP_TIMER,
+	MEMLAT_SET_EFFECTIVE_FREQ_METHOD = 39,
+};
+
+struct cpucp_map_table {
+	u16 v1;
+	u16 v2;
+};
+
+struct map_param_msg {
+	u32 hw_type;
+	u32 mon_idx;
+	u32 nr_rows;
+	struct cpucp_map_table tbl[MAX_MAP_ENTRIES];
+} __packed;
+
+struct node_msg {
+	u32 cpumask;
+	u32 hw_type;
+	u32 mon_type;
+	u32 mon_idx;
+	char mon_name[MAX_NAME_LEN];
+};
+
+struct scalar_param_msg {
+	u32 hw_type;
+	u32 mon_idx;
+	u32 val;
+};
+
+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
+};
+
+struct ev_map_msg {
+	u32 num_evs;
+	u32 hw_type;
+	u32 cid[NUM_COMMON_EVS];
+};
+
+struct scmi_qcom_memlat_map {
+	unsigned int cpufreq_mhz;
+	unsigned int memfreq_khz;
+};
+
+struct scmi_qcom_monitor_info {
+	struct scmi_qcom_memlat_map *freq_map;
+	char name[MAX_NAME_LEN];
+	u32 mon_idx;
+	u32 mon_type;
+	u32 ipm_ceil;
+	u32 be_stall_floor;
+	u32 mask;
+	u32 freq_map_len;
+};
+
+struct scmi_qcom_memory_info {
+	struct scmi_qcom_monitor_info *monitor[MAX_MONITOR_CNT];
+	u32 hw_type;
+	int monitor_cnt;
+	u32 min_freq;
+	u32 max_freq;
+	struct devfreq_dev_profile profile;
+	struct devfreq *devfreq;
+	struct platform_device *pdev;
+	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;
+	struct scmi_qcom_memory_info *memory[MAX_MEMORY_TYPES];
+	u32 cpucp_freq_method;
+	u32 cpucp_sample_ms;
+	int memory_cnt;
+};
+
+static int configure_cpucp_common_events(struct scmi_qcom_memlat_info *info)
+{
+	const struct qcom_generic_ext_ops *ops = info->ops;
+	u8 ev_map[NUM_COMMON_EVS];
+	struct ev_map_msg msg;
+
+	memset(ev_map, 0xFF, NUM_COMMON_EVS);
+
+	msg.num_evs = NUM_COMMON_EVS;
+	msg.cid[INST_IDX] = EV_INST_RETIRED;
+	msg.cid[CYC_IDX] = EV_CPU_CYCLES;
+	msg.cid[CONST_CYC_IDX] = EV_CNT_CYCLES;
+	msg.cid[FE_STALL_IDX] = INVALID_IDX;
+	msg.cid[BE_STALL_IDX] = EV_STALL_BACKEND_MEM;
+
+	return ops->set_param(info->ph, &msg, sizeof(msg), MEMLAT_ALGO_STR,
+			      MEMLAT_SET_COMMON_EV_MAP);
+}
+
+static int configure_cpucp_grp(struct device *dev, struct scmi_qcom_memlat_info *info,
+			       int memory_index)
+{
+	struct scmi_qcom_memory_info *memory = info->memory[memory_index];
+	const struct qcom_generic_ext_ops *ops = info->ops;
+	struct ev_map_msg ev_msg;
+	u8 ev_map[NUM_GRP_EVS];
+	struct node_msg msg;
+	int ret;
+
+	msg.cpumask = 0;
+	msg.hw_type = memory->hw_type;
+	msg.mon_type = 0;
+	msg.mon_idx = 0;
+	ret = ops->set_param(info->ph, &msg, sizeof(msg), MEMLAT_ALGO_STR, MEMLAT_SET_MEM_GROUP);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "failed to configure mem type %d\n",
+				     memory->hw_type);
+
+	memset(ev_map, 0xFF, NUM_GRP_EVS);
+	ev_msg.num_evs = NUM_GRP_EVS;
+	ev_msg.hw_type = memory->hw_type;
+	ev_msg.cid[MISS_IDX] = EV_L2_D_RFILL;
+	ev_msg.cid[WB_IDX] = INVALID_IDX;
+	ev_msg.cid[ACC_IDX] = INVALID_IDX;
+	ret = ops->set_param(info->ph, &ev_msg, sizeof(ev_msg), MEMLAT_ALGO_STR,
+			     MEMLAT_SET_GRP_EV_MAP);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "failed to configure event map for mem type %d\n",
+				     memory->hw_type);
+
+	return ret;
+}
+
+static int configure_cpucp_mon(struct device *dev, struct scmi_qcom_memlat_info *info,
+			       int memory_index, int monitor_index)
+{
+	const struct qcom_generic_ext_ops *ops = info->ops;
+	struct scmi_qcom_memory_info *memory = info->memory[memory_index];
+	struct scmi_qcom_monitor_info *monitor = memory->monitor[monitor_index];
+	struct scalar_param_msg scalar_msg;
+	struct map_param_msg map_msg;
+	struct node_msg msg;
+	int ret;
+	int i;
+
+	msg.cpumask = monitor->mask;
+	msg.hw_type = memory->hw_type;
+	msg.mon_type = monitor->mon_type;
+	msg.mon_idx = monitor->mon_idx;
+	strscpy(msg.mon_name, monitor->name, sizeof(msg.mon_name));
+	ret = ops->set_param(info->ph, &msg, sizeof(msg), MEMLAT_ALGO_STR, MEMLAT_SET_MONITOR);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "failed to configure monitor %s\n",
+				     monitor->name);
+
+	scalar_msg.hw_type = memory->hw_type;
+	scalar_msg.mon_idx = monitor->mon_idx;
+	scalar_msg.val = monitor->ipm_ceil;
+	ret = ops->set_param(info->ph, &scalar_msg, sizeof(scalar_msg), MEMLAT_ALGO_STR,
+			     MEMLAT_IPM_CEIL);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "failed to set ipm ceil for %s\n",
+				     monitor->name);
+
+	scalar_msg.hw_type = memory->hw_type;
+	scalar_msg.mon_idx = monitor->mon_idx;
+	scalar_msg.val = monitor->be_stall_floor;
+	ret = ops->set_param(info->ph, &scalar_msg, sizeof(scalar_msg), MEMLAT_ALGO_STR,
+			     MEMLAT_BE_STALL_FLOOR);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "failed to set ipm ceil for %s\n", monitor->name);
+
+	map_msg.hw_type = memory->hw_type;
+	map_msg.mon_idx = monitor->mon_idx;
+	map_msg.nr_rows = monitor->freq_map_len;
+	for (i = 0; i < monitor->freq_map_len; i++) {
+		map_msg.tbl[i].v1 = monitor->freq_map[i].cpufreq_mhz;
+
+		if (monitor->freq_map[i].memfreq_khz > 1)
+			map_msg.tbl[i].v2 = monitor->freq_map[i].memfreq_khz / 1000;
+		else
+			map_msg.tbl[i].v2 = monitor->freq_map[i].memfreq_khz;
+	}
+	ret = ops->set_param(info->ph, &map_msg, sizeof(map_msg), MEMLAT_ALGO_STR,
+			     MEMLAT_MON_FREQ_MAP);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "failed to configure freq_map for %s\n",
+				     monitor->name);
+
+	scalar_msg.hw_type = memory->hw_type;
+	scalar_msg.mon_idx = monitor->mon_idx;
+	scalar_msg.val = memory->min_freq;
+	ret = ops->set_param(info->ph, &scalar_msg, sizeof(scalar_msg), MEMLAT_ALGO_STR,
+			     MEMLAT_SET_MIN_FREQ);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "failed to set min_freq for %s\n",
+				     monitor->name);
+
+	scalar_msg.hw_type = memory->hw_type;
+	scalar_msg.mon_idx = monitor->mon_idx;
+	scalar_msg.val = memory->max_freq;
+	ret = ops->set_param(info->ph, &scalar_msg, sizeof(scalar_msg), MEMLAT_ALGO_STR,
+			     MEMLAT_SET_MAX_FREQ);
+	if (ret < 0)
+		dev_err_probe(dev, ret, "failed to set max_freq for %s\n", monitor->name);
+
+	return ret;
+}
+
+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;
+	struct scalar_param_msg scalar_msg;
+	int ret;
+
+	ops = memory->ops;
+
+	scalar_msg.hw_type = memory->hw_type;
+	scalar_msg.mon_idx = 0;
+	u32 cur_freq;
+
+	ret = ops->get_param(memory->ph, &scalar_msg, sizeof(scalar_msg), MEMLAT_ALGO_STR,
+			     MEMLAT_GET_CUR_FREQ, sizeof(cur_freq));
+	if (ret < 0) {
+		pr_err("failed to get mon current frequency\n");
+		return ret;
+	}
+
+	memcpy(&cur_freq, (void *)&scalar_msg, sizeof(cur_freq));
+
+	if (memory->hw_type == 2)
+		*freq = le32_to_cpu(cur_freq) ? 100 : 1;
+	else
+		*freq = le32_to_cpu(cur_freq) * 1000UL;
+
+	return 0;
+}
+
+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;
+	int i, j, ret;
+
+	/* Configure common events ids */
+	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++) {
+		/* Configure per group parameters */
+		ret = configure_cpucp_grp(&sdev->dev, info, i);
+		if (ret < 0)
+			return ret;
+
+		for (j = 0; j < info->memory[i]->monitor_cnt; j++) {
+			/* Configure per monitor parameters */
+			ret = configure_cpucp_mon(&sdev->dev, info, i, j);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	/* Set loop sampling time */
+	ret = ops->set_param(ph, &info->cpucp_sample_ms, sizeof(info->cpucp_sample_ms),
+			     MEMLAT_ALGO_STR, MEMLAT_SAMPLE_MS);
+	if (ret < 0)
+		return dev_err_probe(&sdev->dev, ret, "failed to set sample_ms\n");
+
+	/* Set the effective cpu frequency calculation method */
+	ret = ops->set_param(ph, &info->cpucp_freq_method, sizeof(info->cpucp_freq_method),
+			     MEMLAT_ALGO_STR, 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)
+		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];
+		struct platform_device *pdev = memory->pdev;
+		struct devfreq_dev_profile *profile = &memory->profile;
+
+		profile->polling_ms = info->cpucp_sample_ms;
+		profile->get_cur_freq = scmi_qcom_devfreq_get_cur_freq;
+		profile->initial_freq = memory->min_freq > 1 ?
+					(memory->min_freq * 1000UL) : memory->min_freq;
+
+		memory->ops = info->ops;
+		memory->ph = info->ph;
+
+		platform_set_drvdata(pdev, memory);
+
+		memory->devfreq = devm_devfreq_add_device(&pdev->dev, profile,
+							  DEVFREQ_GOV_REMOTE, NULL);
+		if (IS_ERR(memory->devfreq)) {
+			dev_err(&sdev->dev, "failed to add devfreq device\n");
+			/* Start sampling and voting timer */
+			ret = ops->start_activity(ph, NULL, 0, MEMLAT_ALGO_STR, MEMLAT_STOP_TIMER);
+			if (ret < 0)
+				dev_err_probe(&sdev->dev, ret,
+					      "failed to stop memory group timer\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static struct scmi_qcom_memlat_map *scmi_qcom_parse_memlat_map(struct device *dev,
+							       const struct scmi_qcom_monitor_cfg *mon_cfg)
+{
+	struct scmi_qcom_memlat_map *map_table;
+	const struct scmi_qcom_map_table *table;
+
+	map_table = devm_kzalloc(dev, MAX_MAP_ENTRIES * sizeof(struct scmi_qcom_memlat_map),
+				 GFP_KERNEL);
+	if (!map_table)
+		return ERR_PTR(-ENOMEM);
+
+	for (int i = 0; i < mon_cfg->table_len; i++) {
+		table = &mon_cfg->table[i];
+
+		map_table[i].cpufreq_mhz = table->cpu_freq;
+		map_table[i].memfreq_khz = table->mem_freq;
+	}
+
+	return map_table;
+}
+
+static const struct of_device_id scmi_qcom_memlat_configs[] __maybe_unused = {
+	{ .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},
+	{ }
+};
+
+static int scmi_qcom_memlat_parse_cfg(struct scmi_device *sdev, struct scmi_qcom_memlat_info *info)
+{
+	const struct scmi_qcom_memlat_cfg_data *cfg_data;
+	struct scmi_qcom_monitor_info *monitor;
+	struct scmi_qcom_memory_info *memory;
+	int ret, i, j;
+
+	cfg_data = of_machine_get_match_data(scmi_qcom_memlat_configs);
+	if (!cfg_data) {
+		return dev_err_probe(&sdev->dev, PTR_ERR(cfg_data),
+				     "Couldn't find config data for this platform\n");
+	}
+
+	for (i = 0; i < cfg_data->memory_cnt; i++) {
+		const struct scmi_qcom_memory_cfg *memory_cfg = &cfg_data->memory_cfg[i];
+		struct platform_device_info pdevinfo = { 0 };
+
+		pdevinfo.parent = &sdev->dev;
+		pdevinfo.name = memory_cfg->name;
+		pdevinfo.id = PLATFORM_DEVID_NONE;
+
+		memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
+		if (!memory) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		memory->ops = info->ops;
+		memory->ph = info->ph;
+		memory->hw_type = memory_cfg->memory_type;
+		memory->monitor_cnt = memory_cfg->monitor_cnt;
+		memory->min_freq = memory_cfg->memory_range.min_freq;
+		memory->max_freq = memory_cfg->memory_range.max_freq;
+
+		memory->pdev = platform_device_register_full(&pdevinfo);
+		if (IS_ERR(memory->pdev)) {
+			dev_err_probe(&sdev->dev, PTR_ERR(memory->pdev),
+				      "failed to register platform device\n");
+			ret = PTR_ERR(memory->pdev);
+			goto out;
+		}
+
+		info->memory[i] = memory;
+
+		for (j = 0; j < memory_cfg->num_opps; j++) {
+			const struct scmi_qcom_opp_data *table = &memory_cfg->mem_table[j];
+			struct platform_device *pdev = memory->pdev;
+			struct dev_pm_opp_data data;
+
+			data.freq = table->freq;
+			data.level = table->level;
+
+			ret = dev_pm_opp_add_dynamic(&pdev->dev, &data);
+			if (ret) {
+				dev_err_probe(&sdev->dev, ret, "failed to add OPP\n");
+				dev_pm_opp_remove_all_dynamic(&pdev->dev);
+				goto out;
+			}
+		}
+
+		for (j = 0; j < memory_cfg->monitor_cnt; j++) {
+			const struct scmi_qcom_monitor_cfg *monitor_cfg = &memory_cfg->monitor_cfg[j];
+
+			monitor = devm_kzalloc(&sdev->dev, sizeof(*monitor), GFP_KERNEL);
+			if (!monitor)
+				return -ENOMEM;
+
+			monitor->ipm_ceil = monitor_cfg->ipm_ceil;
+			monitor->mon_type = monitor->ipm_ceil ? 0 : 1;
+			monitor->be_stall_floor = monitor_cfg->be_stall_floor;
+			monitor->mask = monitor_cfg->cpu_mask;
+			monitor->freq_map_len = monitor_cfg->table_len;
+
+			monitor->freq_map = scmi_qcom_parse_memlat_map(&sdev->dev, monitor_cfg);
+			if (IS_ERR(monitor->freq_map)) {
+				dev_err_probe(&sdev->dev, PTR_ERR(monitor->freq_map),
+					      "failed to populate cpufreq-memfreq map\n");
+				ret = -EINVAL;
+				goto out;
+			}
+
+			strscpy(monitor->name, monitor_cfg->name, sizeof(monitor->name));
+			monitor->mon_idx = j;
+			memory->monitor[j] = monitor;
+		}
+	}
+
+	info->cpucp_freq_method = cfg_data->cpucp_freq_method;
+	info->cpucp_sample_ms = cfg_data->cpucp_sample_ms;
+	info->memory_cnt = cfg_data->memory_cnt;
+
+	return 0;
+
+out:
+	for (i = 0; i < cfg_data->memory_cnt; i++) {
+		if (IS_ERR_OR_NULL(info->memory[i]))
+			break;
+
+		memory = info->memory[i];
+		if (!IS_ERR(memory->pdev))
+			platform_device_unregister(memory->pdev);
+	}
+
+	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_parse_cfg(sdev, info);
+	if (ret)
+		return ret;
+
+	ret = scmi_qcom_memlat_configure_events(sdev, info);
+	if (ret)
+		return ret;
+
+	dev_set_drvdata(&sdev->dev, info);
+
+	return ret;
+}
+
+static void scmi_qcom_devfreq_memlat_remove(struct scmi_device *sdev)
+{
+	struct scmi_qcom_memlat_info *info = dev_get_drvdata(&sdev->dev);
+
+	for (int i = 0; i < info->memory_cnt; i++) {
+		struct scmi_qcom_memory_info *memory = info->memory[i];
+
+		if (!IS_ERR(memory->pdev))
+			platform_device_unregister(memory->pdev);
+	}
+}
+
+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("Sibi Sankar <sibi.sankar@oss.qualcomm.com>");
+MODULE_DESCRIPTION("SCMI QCOM DEVFREQ MEMLAT driver");
+MODULE_LICENSE("GPL");
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [RFC V6 7/8] arm64: dts: qcom: glymur: Enable LLCC/DDR/DDR_QOS dvfs
  2026-05-07  6:22 [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
                   ` (5 preceding siblings ...)
  2026-05-07  6:22 ` [RFC V6 6/8] PM / devfreq: Introduce the QCOM SCMI Memlat devfreq device Sibi Sankar
@ 2026-05-07  6:22 ` Sibi Sankar
  2026-05-07  6:22 ` [RFC V6 8/8] arm64: dts: qcom: hamoa: " Sibi Sankar
  2026-05-07  9:10 ` [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Dmitry Baryshkov
  8 siblings, 0 replies; 13+ messages in thread
From: Sibi Sankar @ 2026-05-07  6:22 UTC (permalink / raw)
  To: cristian.marussi, sudeep.holla, cw00.choi, kyungmin.park,
	myungjoo.ham, konradybcio, andersson
  Cc: linux-arm-msm, dri-devel, devicetree, linux-kernel, arm-scmi,
	linux-tegra, linux-pm, dmitry.baryshkov, jonathanh,
	thierry.reding, digetx, conor+dt, krzk+dt, robh

On Qualcomm Glymur SoCs, the memlat governor and the mechanism
to control the LLCC and DDR/DDR_QOS is hosted on the CPU Control
Processor (CPUCP). Enable the nodes required to get QCOM SCMI Generic
Extension protocol to probe on Glymur and Mahua SoCs.

Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com>
---
 arch/arm64/boot/dts/qcom/glymur.dtsi | 42 ++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi
index f23cf81ddb77..6409350ad9d7 100644
--- a/arch/arm64/boot/dts/qcom/glymur.dtsi
+++ b/arch/arm64/boot/dts/qcom/glymur.dtsi
@@ -349,6 +349,21 @@ scm: scm {
 					 &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
 		};
 
+		cpucp_scmi {
+			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>;
+			};
+		};
+
 		scmi {
 			compatible = "arm,scmi";
 			mboxes = <&pdp0_mbox 0>, <&pdp0_mbox 1>;
@@ -5675,6 +5690,13 @@ pdp0_mbox: mailbox@17610000 {
 			#mbox-cells = <1>;
 		};
 
+		cpucp_mbox: mailbox@17620000 {
+			compatible = "qcom,glymur-cpucp-mbox", "qcom,x1e80100-cpucp-mbox";
+			reg = <0 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>;
@@ -5859,6 +5881,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] 13+ messages in thread

* [RFC V6 8/8] arm64: dts: qcom: hamoa: Enable LLCC/DDR/DDR_QOS dvfs
  2026-05-07  6:22 [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
                   ` (6 preceding siblings ...)
  2026-05-07  6:22 ` [RFC V6 7/8] arm64: dts: qcom: glymur: Enable LLCC/DDR/DDR_QOS dvfs Sibi Sankar
@ 2026-05-07  6:22 ` Sibi Sankar
  2026-05-07  9:10 ` [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Dmitry Baryshkov
  8 siblings, 0 replies; 13+ messages in thread
From: Sibi Sankar @ 2026-05-07  6:22 UTC (permalink / raw)
  To: cristian.marussi, sudeep.holla, cw00.choi, kyungmin.park,
	myungjoo.ham, konradybcio, andersson
  Cc: linux-arm-msm, dri-devel, devicetree, linux-kernel, arm-scmi,
	linux-tegra, linux-pm, dmitry.baryshkov, jonathanh,
	thierry.reding, digetx, conor+dt, krzk+dt, robh

On Qualcomm Hamoa SoCs, the memlat governor and the mechanism
to control the LLCC and DDR/DDR_QOS is hosted on the CPU Control
Processor (CPUCP). Enable the nodes required to get QCOM SCMI
Generic Extension protocol to probe on Hamoa and Purwa SoCs.

Signed-off-by: Sibi Sankar <sibi.sankar@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 051dee076416..a2d5c9db984d 100644
--- a/arch/arm64/boot/dts/qcom/hamoa.dtsi
+++ b/arch/arm64/boot/dts/qcom/hamoa.dtsi
@@ -338,6 +338,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] 13+ messages in thread

* Re: [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2026-05-07  6:22 [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
                   ` (7 preceding siblings ...)
  2026-05-07  6:22 ` [RFC V6 8/8] arm64: dts: qcom: hamoa: " Sibi Sankar
@ 2026-05-07  9:10 ` Dmitry Baryshkov
  2026-05-07  9:58   ` Sibi Sankar
  8 siblings, 1 reply; 13+ messages in thread
From: Dmitry Baryshkov @ 2026-05-07  9:10 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: cristian.marussi, sudeep.holla, cw00.choi, kyungmin.park,
	myungjoo.ham, konradybcio, andersson, linux-arm-msm, dri-devel,
	devicetree, linux-kernel, arm-scmi, linux-tegra, linux-pm,
	jonathanh, thierry.reding, digetx, conor+dt, krzk+dt, robh

On Thu, May 07, 2026 at 11:52:29AM +0530, Sibi Sankar wrote:
> 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. On Qualcomm Glymur and Hamoa SoCs, the memlat governor and the
> mechanism to control the various caches and ram is hosted on the CPU Control
> Processor (CPUCP) and the method to tweak and start the governor is exposed
> through the QCOM SCMI Generic Extension Protocol.


Could you please clarify, does this apply to the common commercial
Glymur and Hamoa laptops?

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2026-05-07  9:10 ` [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Dmitry Baryshkov
@ 2026-05-07  9:58   ` Sibi Sankar
  2026-05-07 11:10     ` Dmitry Baryshkov
  0 siblings, 1 reply; 13+ messages in thread
From: Sibi Sankar @ 2026-05-07  9:58 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: cristian.marussi, sudeep.holla, cw00.choi, kyungmin.park,
	myungjoo.ham, konradybcio, andersson, linux-arm-msm, dri-devel,
	devicetree, linux-kernel, arm-scmi, linux-tegra, linux-pm,
	jonathanh, thierry.reding, digetx, conor+dt, krzk+dt, robh


On 5/7/2026 2:40 PM, Dmitry Baryshkov wrote:
> On Thu, May 07, 2026 at 11:52:29AM +0530, Sibi Sankar wrote:
>> 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. On Qualcomm Glymur and Hamoa SoCs, the memlat governor and the
>> mechanism to control the various caches and ram is hosted on the CPU Control
>> Processor (CPUCP) and the method to tweak and start the governor is exposed
>> through the QCOM SCMI Generic Extension Protocol.
>
> Could you please clarify, does this apply to the common commercial
> Glymur and Hamoa laptops?

Yes, they do apply to common commercial Glymur/Hamoa Laptops
as is. This is the same solution used on the windows side of things
as well. There can be certain cases like Johan has reported earlier
where certain oems are stuck with on older version of CPUCP
which requires a the memlat string to be sent out in lower case
we should be able to handle those as well with overriding those
by using driver data and specific compatibles.

-Sibi

>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2026-05-07  9:58   ` Sibi Sankar
@ 2026-05-07 11:10     ` Dmitry Baryshkov
  0 siblings, 0 replies; 13+ messages in thread
From: Dmitry Baryshkov @ 2026-05-07 11:10 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: cristian.marussi, sudeep.holla, cw00.choi, kyungmin.park,
	myungjoo.ham, konradybcio, andersson, linux-arm-msm, dri-devel,
	devicetree, linux-kernel, arm-scmi, linux-tegra, linux-pm,
	jonathanh, thierry.reding, digetx, conor+dt, krzk+dt, robh

On Thu, May 07, 2026 at 03:28:21PM +0530, Sibi Sankar wrote:
> 
> On 5/7/2026 2:40 PM, Dmitry Baryshkov wrote:
> > On Thu, May 07, 2026 at 11:52:29AM +0530, Sibi Sankar wrote:
> > > 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. On Qualcomm Glymur and Hamoa SoCs, the memlat governor and the
> > > mechanism to control the various caches and ram is hosted on the CPU Control
> > > Processor (CPUCP) and the method to tweak and start the governor is exposed
> > > through the QCOM SCMI Generic Extension Protocol.
> > 
> > Could you please clarify, does this apply to the common commercial
> > Glymur and Hamoa laptops?
> 
> Yes, they do apply to common commercial Glymur/Hamoa Laptops
> as is. This is the same solution used on the windows side of things
> as well. There can be certain cases like Johan has reported earlier
> where certain oems are stuck with on older version of CPUCP
> which requires a the memlat string to be sent out in lower case
> we should be able to handle those as well with overriding those
> by using driver data and specific compatibles.

Ack, thanks for the confirmation.

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC V6 1/8] firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation
  2026-05-07  6:22 ` [RFC V6 1/8] firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation Sibi Sankar
@ 2026-05-07 12:36   ` Sudeep Holla
  0 siblings, 0 replies; 13+ messages in thread
From: Sudeep Holla @ 2026-05-07 12:36 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: cristian.marussi, cw00.choi, Sudeep Holla, kyungmin.park,
	myungjoo.ham, konradybcio, andersson, linux-arm-msm, dri-devel,
	devicetree, linux-kernel, arm-scmi, linux-tegra, linux-pm,
	dmitry.baryshkov, jonathanh, thierry.reding, digetx, conor+dt,
	krzk+dt, robh

On Thu, May 07, 2026 at 11:52:30AM +0530, Sibi Sankar wrote:
> Add QCOM System Control Management Interface (SCMI) Generic Vendor
> Extensions Protocol documentation.
> 
> Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com>
> ---
> 
> @Sudeep/@Christian, Just moving the series back in RFC mode to get feedback
> on the devfreq part of the series. Will add a lot more documentation in the
> next re-spin, so definitely not ignoring your earlier comments :).
> 

Thanks for letting us know. We must settle the specification first before
moving on to any implementation details. I will therefore skip reviewing this
version, as requested.

-- 
Regards,
Sudeep

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-05-07 12:36 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-07  6:22 [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
2026-05-07  6:22 ` [RFC V6 1/8] firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation Sibi Sankar
2026-05-07 12:36   ` Sudeep Holla
2026-05-07  6:22 ` [RFC V6 2/8] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions Sibi Sankar
2026-05-07  6:22 ` [RFC V6 3/8] PM / devfreq: Add new target_freq attribute flag for governors Sibi Sankar
2026-05-07  6:22 ` [RFC V6 4/8] PM / devfreq: Add new track_remote " Sibi Sankar
2026-05-07  6:22 ` [RFC V6 5/8] PM / devfreq: Add a governor for tracking remote device frequencies Sibi Sankar
2026-05-07  6:22 ` [RFC V6 6/8] PM / devfreq: Introduce the QCOM SCMI Memlat devfreq device Sibi Sankar
2026-05-07  6:22 ` [RFC V6 7/8] arm64: dts: qcom: glymur: Enable LLCC/DDR/DDR_QOS dvfs Sibi Sankar
2026-05-07  6:22 ` [RFC V6 8/8] arm64: dts: qcom: hamoa: " Sibi Sankar
2026-05-07  9:10 ` [RFC V6 0/8] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Dmitry Baryshkov
2026-05-07  9:58   ` Sibi Sankar
2026-05-07 11:10     ` Dmitry Baryshkov

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