devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
@ 2024-10-07  6:10 Sibi Sankar
  2024-10-07  6:10 ` [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension Sibi Sankar
                   ` (6 more replies)
  0 siblings, 7 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-10-07  6:10 UTC (permalink / raw)
  To: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt
  Cc: linux-kernel, linux-arm-msm, devicetree, linux-arm-kernel,
	quic_rgottimu, quic_kshivnan, quic_sibis, conor+dt, arm-scmi

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. Introduce a client driver that
uses the memlat 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).

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.

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]

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.

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 (5):
  dt-bindings: firmware: Document bindings for QCOM SCMI Generic
    Extension
  firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation
  firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions
  soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  arm64: dts: qcom: x1e80100: Enable LLCC/DDR/DDR_QOS dvfs

 .../bindings/firmware/arm,scmi.yaml           |   1 +
 .../bindings/firmware/qcom,scmi-memlat.yaml   | 246 ++++++++
 arch/arm64/boot/dts/qcom/x1e80100.dtsi        | 138 +++++
 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  | 184 ++++++
 .../arm_scmi/vendors/qcom/qcom_generic.rst    | 210 +++++++
 drivers/soc/qcom/Kconfig                      |  12 +
 drivers/soc/qcom/Makefile                     |   1 +
 drivers/soc/qcom/qcom_scmi_memlat_client.c    | 569 ++++++++++++++++++
 .../dt-bindings/firmware/qcom,scmi-memlat.h   |  22 +
 include/linux/scmi_qcom_protocol.h            |  39 ++
 14 files changed, 1441 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
 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 drivers/soc/qcom/qcom_scmi_memlat_client.c
 create mode 100644 include/dt-bindings/firmware/qcom,scmi-memlat.h
 create mode 100644 include/linux/scmi_qcom_protocol.h

-- 
2.34.1


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

* [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-10-07  6:10 [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
@ 2024-10-07  6:10 ` Sibi Sankar
  2024-10-07 18:06   ` Dmitry Baryshkov
                     ` (4 more replies)
  2024-10-07  6:10 ` [PATCH V4 2/5] firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation Sibi Sankar
                   ` (5 subsequent siblings)
  6 siblings, 5 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-10-07  6:10 UTC (permalink / raw)
  To: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt
  Cc: linux-kernel, linux-arm-msm, devicetree, linux-arm-kernel,
	quic_rgottimu, quic_kshivnan, quic_sibis, conor+dt, arm-scmi

Document the various memory buses that can be monitored and scaled by
the memory latency governor hosted by the QCOM SCMI Generic Extension
Protocol v1.0.

Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com>
---

v3:
* Restructure the bindings to mimic IMX [Christian]

 .../bindings/firmware/arm,scmi.yaml           |   1 +
 .../bindings/firmware/qcom,scmi-memlat.yaml   | 246 ++++++++++++++++++
 .../dt-bindings/firmware/qcom,scmi-memlat.h   |  22 ++
 3 files changed, 269 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
 create mode 100644 include/dt-bindings/firmware/qcom,scmi-memlat.h

diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
index 54d7d11bfed4..1d405f429168 100644
--- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
+++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
@@ -24,6 +24,7 @@ description: |
 
 anyOf:
   - $ref: /schemas/firmware/nxp,imx95-scmi.yaml
+  - $ref: /schemas/firmware/qcom,scmi-memlat.yaml
 
 properties:
   $nodename:
diff --git a/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml b/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
new file mode 100644
index 000000000000..0e8ea6dacd6a
--- /dev/null
+++ b/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
@@ -0,0 +1,246 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/firmware/qcom,scmi-memlat.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm SCMI Memory Bus nodes
+
+maintainers:
+  - Sibi Sankar <quic_sibis@quicinc.com>
+
+description:
+  This binding describes the various memory buses that can be monitored and scaled
+  by memory latency governor running on the CPU Control Processor (SCMI controller).
+
+properties:
+  protocol@80:
+    $ref: '/schemas/firmware/arm,scmi.yaml#/$defs/protocol-node'
+    unevaluatedProperties: false
+
+    properties:
+      reg:
+        const: 0x80
+
+    patternProperties:
+      '^memory-[0-9]$':
+        type: object
+        unevaluatedProperties: false
+        description:
+          The list of all memory buses that can be monitored and scaled by the
+          memory latency governor running on the SCMI controller.
+
+        properties:
+          qcom,memory-type:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            enum: [0, 1, 2]
+            description: |
+              Memory Bus Identifier
+              0 = QCOM_MEM_TYPE_DDR
+              1 = QCOM_MEM_TYPE_LLCC
+              2 = QCOM_MEM_TYPE_DDR_QOS
+
+          freq-table-hz:
+            items:
+              items:
+                - description: Minimum frequency of the memory bus in Hz
+                - description: Maximum frequency of the memory bus in Hz
+
+        patternProperties:
+          '^monitor-[0-9]$':
+            type: object
+            unevaluatedProperties: false
+            description:
+              The list of all monitors detecting the memory latency bound workloads using
+              various counters.
+
+            properties:
+              qcom,compute-type:
+                description:
+                  Monitors of type compute perform bus dvfs based on a rudimentary CPU
+                  frequency to memory frequency map.
+                type: boolean
+
+              qcom,ipm-ceil:
+                $ref: /schemas/types.yaml#/definitions/uint32
+                description:
+                  Monitors having this property perform bus dvfs based on the same
+                  rudimentary table but the scaling is performed only if the calculated
+                  IPM (Instruction Per Misses) exceeds the given ceiling.
+
+              cpus:
+                $ref: /schemas/types.yaml#/definitions/phandle-array
+                description:
+                  Should be a list of phandles to CPU nodes (as described in
+                  Documentation/devicetree/bindings/arm/cpus.yaml).
+
+              operating-points-v2: true
+              opp-table:
+                type: object
+
+            required:
+              - cpus
+              - operating-points-v2
+
+            oneOf:
+              - required: [ 'qcom,compute-type' ]
+              - required: [ 'qcom,ipm-ceil' ]
+
+        required:
+          - qcom,memory-type
+          - freq-table-hz
+
+additionalProperties: true
+
+examples:
+  - |
+    #include <dt-bindings/firmware/qcom,scmi-memlat.h>
+
+    firmware {
+        scmi {
+            compatible = "arm,scmi";
+            mboxes = <&cpucp_mbox 0>, <&cpucp_mbox 2>;
+            mbox-names = "tx", "rx";
+            shmem = <&cpu_scp_lpri0>, <&cpu_scp_lpri1>;
+
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            protocol@80 {
+                reg = <0x80>;
+
+                memory-0 {
+                    qcom,memory-type = <QCOM_MEM_TYPE_DDR>;
+                    freq-table-hz = /bits/ 64 <200000000 4224000000>;
+
+                    monitor-0 {
+                        qcom,ipm-ceil = <20000000>;
+                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
+                                &CPU8 &CPU9 &CPU10 &CPU11>;
+                        operating-points-v2 = <&memory0_monitor0_opp_table>;
+
+                        memory0_monitor0_opp_table: opp-table {
+                            compatible = "operating-points-v2";
+
+                            opp-999000000 {
+                                opp-hz = /bits/ 64 <999000000 547000000>;
+                            };
+
+                            opp-1440000000 {
+                                opp-hz = /bits/ 64 <1440000000 768000000>;
+                            };
+
+                            opp-1671000000 {
+                                opp-hz = /bits/ 64 <1671000000 1555000000>;
+                            };
+
+                            opp-2189000000 {
+                                opp-hz = /bits/ 64 <2189000000 2092000000>;
+                            };
+
+                            opp-2516000000 {
+                                opp-hz = /bits/ 64 <2516000000 3187000000>;
+                            };
+
+                            opp-3860000000 {
+                                opp-hz = /bits/ 64 <3860000000 4224000000>;
+                            };
+                        };
+                    };
+
+                    monitor-1 {
+                        qcom,compute-type;
+                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
+                                &CPU8 &CPU9 &CPU10 &CPU11>;
+                        operating-points-v2 = <&memory0_monitor1_opp_table>;
+
+                        memory0_monitor1_opp_table: opp-table {
+                            compatible = "operating-points-v2";
+
+                            opp-1440000000 {
+                                    opp-hz = /bits/ 64 <1440000000 200000000>;
+                            };
+
+                            opp-2189000000 {
+                                    opp-hz = /bits/ 64 <2189000000 768000000>;
+                            };
+
+                            opp-2516000000 {
+                                    opp-hz = /bits/ 64 <2516000000 1555000000>;
+                            };
+
+                            opp-3860000000 {
+                                    opp-hz = /bits/ 64 <3860000000 4224000000>;
+                            };
+                        };
+                    };
+                };
+
+                memory-1 {
+                    qcom,memory-type = <QCOM_MEM_TYPE_LLCC>;
+                    freq-table-hz = /bits/ 64 <300000000 1067000000>;
+
+                    monitor-0 {
+                        qcom,ipm-ceil = <20000000>;
+                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
+                                &CPU8 &CPU9 &CPU10 &CPU11>;
+                        operating-points-v2 = <&memory1_monitor0_opp_table>;
+
+                        memory1_monitor0_opp_table: opp-table {
+                            compatible = "operating-points-v2";
+
+                            opp-999000000 {
+                                opp-hz = /bits/ 64 <999000000 300000000>;
+                            };
+
+                            opp-1440000000 {
+                                opp-hz = /bits/ 64 <1440000000 466000000>;
+                            };
+
+                            opp-1671000000 {
+                                opp-hz = /bits/ 64 <1671000000 600000000>;
+                            };
+
+                            opp-2189000000 {
+                                opp-hz = /bits/ 64 <2189000000 806000000>;
+                            };
+
+                            opp-2516000000 {
+                                opp-hz = /bits/ 64 <2516000000 933000000>;
+                            };
+
+                            opp-3860000000 {
+                                opp-hz = /bits/ 64 <3860000000 1066000000>;
+                            };
+                        };
+                    };
+                };
+
+                memory-2 {
+                    qcom,memory-type = <QCOM_MEM_TYPE_DDR_QOS>;
+                    freq-table-hz = /bits/ 64 <QCOM_DDR_LEVEL_AUTO QCOM_DDR_LEVEL_PERF>;
+
+                    monitor-0 {
+                        qcom,ipm-ceil = <20000000>;
+                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
+                                &CPU8 &CPU9 &CPU10 &CPU11>;
+                        operating-points-v2 = <&memory2_monitor0_opp_table>;
+
+                        memory2_monitor0_opp_table: opp-table {
+                            compatible = "operating-points-v2";
+
+                            opp-2189000000 {
+                                opp-hz = /bits/ 64 <2189000000>;
+                                opp-level = <QCOM_DDR_LEVEL_AUTO>;
+                            };
+
+                            opp-3860000000 {
+                                opp-hz = /bits/ 64 <3860000000>;
+                                opp-level = <QCOM_DDR_LEVEL_PERF>;
+                            };
+                        };
+                    };
+                };
+            };
+        };
+    };
diff --git a/include/dt-bindings/firmware/qcom,scmi-memlat.h b/include/dt-bindings/firmware/qcom,scmi-memlat.h
new file mode 100644
index 000000000000..7ae8d8d5623b
--- /dev/null
+++ b/include/dt-bindings/firmware/qcom,scmi-memlat.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#ifndef __DT_BINDINGS_QCOM_SCMI_VENDOR_H
+#define __DT_BINDINGS_QCOM_SCMI_VENDOR
+
+/* Memory IDs */
+#define QCOM_MEM_TYPE_DDR	0x0
+#define QCOM_MEM_TYPE_LLCC	0x1
+#define QCOM_MEM_TYPE_DDR_QOS	0x2
+
+/*
+ * QCOM_MEM_TYPE_DDR_QOS supports the following states.
+ *
+ * %QCOM_DDR_LEVEL_AUTO:	DDR operates with LPM enabled
+ * %QCOM_DDR_LEVEL_PERF:	DDR operates with LPM disabled
+ */
+#define QCOM_DDR_LEVEL_AUTO	0x0
+#define QCOM_DDR_LEVEL_PERF	0x1
+
+#endif /* __DT_BINDINGS_QCOM_SCMI_VENDOR_H */
-- 
2.34.1


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

* [PATCH V4 2/5] firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation
  2024-10-07  6:10 [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
  2024-10-07  6:10 ` [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension Sibi Sankar
@ 2024-10-07  6:10 ` Sibi Sankar
  2024-10-22 10:22   ` Cristian Marussi
  2024-10-07  6:10 ` [PATCH V4 3/5] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions Sibi Sankar
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 64+ messages in thread
From: Sibi Sankar @ 2024-10-07  6:10 UTC (permalink / raw)
  To: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt
  Cc: linux-kernel, linux-arm-msm, devicetree, linux-arm-kernel,
	quic_rgottimu, quic_kshivnan, quic_sibis, conor+dt, arm-scmi

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

Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com>
---
 .../arm_scmi/vendors/qcom/qcom_generic.rst    | 210 ++++++++++++++++++
 1 file changed, 210 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..1ee6dabaac23
--- /dev/null
+++ b/drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst
@@ -0,0 +1,210 @@
+.. 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[8] Set to 1.                                          |
+|                  |Bits[0] Set to 1.                                          |
++------------------+-----------------------------------------------------------+
+
+PROTOCOL_MESSAGE_ATTRIBUTES
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+message_id: 0x2
+protocol_id: 0x80
+
++---------------+--------------------------------------------------------------+
+|Return values                                                                 |
++------------------+-----------------------------------------------------------+
+|Name              |Description                                                |
++------------------+-----------------------------------------------------------+
+|int32 status      |See ARM SCMI Specification for status code definitions.    |
++------------------+-----------------------------------------------------------+
+|uint32 attributes |For all message 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] 64+ messages in thread

* [PATCH V4 3/5] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions
  2024-10-07  6:10 [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
  2024-10-07  6:10 ` [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension Sibi Sankar
  2024-10-07  6:10 ` [PATCH V4 2/5] firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation Sibi Sankar
@ 2024-10-07  6:10 ` Sibi Sankar
  2024-10-07 18:13   ` Dmitry Baryshkov
  2024-10-07  6:10 ` [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor Sibi Sankar
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 64+ messages in thread
From: Sibi Sankar @ 2024-10-07  6:10 UTC (permalink / raw)
  To: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt
  Cc: linux-kernel, linux-arm-msm, devicetree, linux-arm-kernel,
	quic_rgottimu, quic_kshivnan, quic_sibis, conor+dt, arm-scmi,
	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 <quic_sibis@quicinc.com>
Reviewed-by: Cristian Marussi <cristian.marussi@arm.com>
---

v3:
* 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]

 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  | 184 ++++++++++++++++++
 include/linux/scmi_qcom_protocol.h            |  39 ++++
 6 files changed, 242 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 dabd874641d0..73128442d97b 100644
--- a/drivers/firmware/arm_scmi/Kconfig
+++ b/drivers/firmware/arm_scmi/Kconfig
@@ -71,6 +71,7 @@ config ARM_SCMI_DEBUG_COUNTERS
 
 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 9ac81adff567..58cf4d656cbb 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -12,6 +12,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..f7cd949161df
--- /dev/null
+++ b/drivers/firmware/arm_scmi/vendors/qcom/qcom-generic-ext.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024, 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 implementation version set to 0x20000. The PROTOCOL_VERSION command
+ * returns version 1.0.
+ *
+ * @QCOM_SCMI_SET_PARAM: message_id: 0x10 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: message_id: 0x11 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: message_id: 0x12 is used to start the activity performed by
+ *                            the algo_str.
+ * @QCOM_SCMI_STOP_ACTIVITY: message_id: 0x13 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_set_param(const struct scmi_protocol_handle *ph, void *buf, size_t buf_len,
+			       u64 algo_str, u32 param_id)
+{
+	struct scmi_xfer *t;
+	struct qcom_scmi_msg *msg;
+	int ret;
+
+	ret = ph->xops->xfer_get_init(ph, QCOM_SCMI_SET_PARAM, buf_len + sizeof(*msg), 0, &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, t->tx.len - sizeof(*msg));
+
+	ret = ph->xops->do_xfer(ph, t);
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+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)
+{
+	struct scmi_xfer *t;
+	struct qcom_scmi_msg *msg;
+	int ret;
+
+	ret = ph->xops->xfer_get_init(ph, QCOM_SCMI_GET_PARAM, 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, t->tx.len - sizeof(*msg));
+
+	ret = ph->xops->do_xfer(ph, t);
+	memcpy(buf, t->rx.buf, t->rx.len);
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static int qcom_scmi_start_activity(const struct scmi_protocol_handle *ph, void *buf,
+				    size_t buf_len, u64 algo_str, u32 param_id)
+{
+	struct scmi_xfer *t;
+	struct qcom_scmi_msg *msg;
+	int ret;
+
+	ret = ph->xops->xfer_get_init(ph, QCOM_SCMI_START_ACTIVITY, buf_len + sizeof(*msg), 0, &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, t->tx.len - sizeof(*msg));
+
+	ret = ph->xops->do_xfer(ph, t);
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static int qcom_scmi_stop_activity(const struct scmi_protocol_handle *ph, void *buf,
+				   size_t buf_len, u64 algo_str, u32 param_id)
+{
+	struct scmi_xfer *t;
+	struct qcom_scmi_msg *msg;
+	int ret;
+
+	ret = ph->xops->xfer_get_init(ph, QCOM_SCMI_STOP_ACTIVITY, buf_len + sizeof(*msg), 0, &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, t->tx.len - sizeof(*msg));
+
+	ret = ph->xops->do_xfer(ph, t);
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+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)
+{
+	u32 version;
+
+	ph->xops->version_get(ph, &version);
+
+	dev_info(ph->dev, "QCOM Generic Vendor Version %d.%d\n",
+		 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(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",
+	.impl_ver = 0x20000,
+};
+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..8f82c42e566d
--- /dev/null
+++ b/include/linux/scmi_qcom_protocol.h
@@ -0,0 +1,39 @@
+/* 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/bitfield.h>
+#include <linux/device.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] 64+ messages in thread

* [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-10-07  6:10 [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
                   ` (2 preceding siblings ...)
  2024-10-07  6:10 ` [PATCH V4 3/5] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions Sibi Sankar
@ 2024-10-07  6:10 ` Sibi Sankar
  2024-10-07 17:57   ` Dmitry Baryshkov
                     ` (3 more replies)
  2024-10-07  6:10 ` [PATCH V4 5/5] arm64: dts: qcom: x1e80100: Enable LLCC/DDR/DDR_QOS dvfs Sibi Sankar
                   ` (2 subsequent siblings)
  6 siblings, 4 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-10-07  6:10 UTC (permalink / raw)
  To: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt
  Cc: linux-kernel, linux-arm-msm, devicetree, linux-arm-kernel,
	quic_rgottimu, quic_kshivnan, quic_sibis, conor+dt, arm-scmi,
	Amir Vajid

Introduce a 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).

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 <quic_sibis@quicinc.com>
---

v3:
* 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]

 drivers/soc/qcom/Kconfig                   |  12 +
 drivers/soc/qcom/Makefile                  |   1 +
 drivers/soc/qcom/qcom_scmi_memlat_client.c | 569 +++++++++++++++++++++
 3 files changed, 582 insertions(+)
 create mode 100644 drivers/soc/qcom/qcom_scmi_memlat_client.c

diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 74b9121240f8..1b6dd40d69ea 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -295,4 +295,16 @@ config QCOM_PBS
 	  This module provides the APIs to the client drivers that wants to send the
 	  PBS trigger event to the PBS RAM.
 
+config QCOM_SCMI_MEMLAT_CLIENT
+	tristate "Qualcomm Technologies Inc. SCMI client driver"
+	depends on QCOM_SCMI_GENERIC_EXT || COMPILE_TEST
+	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.
+
 endmenu
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index acbca2ab5cc2..28549bb141bc 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_QCOM_APR) += apr.o
 obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o
 obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) +=	kryo-l2-accessors.o
 obj-$(CONFIG_QCOM_ICC_BWMON)	+= icc-bwmon.o
+obj-$(CONFIG_QCOM_SCMI_MEMLAT_CLIENT)	+= qcom_scmi_memlat_client.o
 qcom_ice-objs			+= ice.o
 obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE)	+= qcom_ice.o
 obj-$(CONFIG_QCOM_PBS) +=	qcom-pbs.o
diff --git a/drivers/soc/qcom/qcom_scmi_memlat_client.c b/drivers/soc/qcom/qcom_scmi_memlat_client.c
new file mode 100644
index 000000000000..05198bf1f7ec
--- /dev/null
+++ b/drivers/soc/qcom/qcom_scmi_memlat_client.c
@@ -0,0 +1,569 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/cpu.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/scmi_protocol.h>
+#include <linux/scmi_qcom_protocol.h>
+#include <linux/units.h>
+#include <dt-bindings/firmware/qcom,scmi-memlat.h>
+
+#define MEMLAT_ALGO_STR				0x4D454D4C4154 /* MEMLAT */
+#define INVALID_IDX				0xff
+#define MAX_MEMORY_TYPES			3
+#define MAX_MONITOR_CNT				4
+#define MAX_NAME_LEN				20
+#define MAX_MAP_ENTRIES				7
+#define CPUCP_DEFAULT_SAMPLING_PERIOD_MS	4
+#define CPUCP_DEFAULT_FREQ_METHOD		1
+
+/**
+ * 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_SAMPLE_MS = 31,
+	MEMLAT_MON_FREQ_MAP,
+	MEMLAT_SET_MIN_FREQ,
+	MEMLAT_SET_MAX_FREQ,
+	MEMLAT_START_TIMER = 36,
+	MEMLAT_STOP_TIMER,
+	MEMLAT_SET_EFFECTIVE_FREQ_METHOD = 39,
+};
+
+struct map_table {
+	u16 v1;
+	u16 v2;
+};
+
+struct map_param_msg {
+	u32 hw_type;
+	u32 mon_idx;
+	u32 nr_rows;
+	struct 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
+};
+
+#define EV_CPU_CYCLES		0
+#define EV_INST_RETIRED		2
+#define EV_L2_D_RFILL		5
+
+struct ev_map_msg {
+	u32 num_evs;
+	u32 hw_type;
+	u32 cid[NUM_COMMON_EVS];
+};
+
+struct cpufreq_memfreq_map {
+	unsigned int cpufreq_mhz;
+	unsigned int memfreq_khz;
+};
+
+struct scmi_monitor_info {
+	struct cpufreq_memfreq_map *freq_map;
+	char mon_name[MAX_NAME_LEN];
+	u32 mon_idx;
+	u32 mon_type;
+	u32 ipm_ceil;
+	u32 mask;
+	u32 freq_map_len;
+};
+
+struct scmi_memory_info {
+	struct scmi_monitor_info *monitor[MAX_MONITOR_CNT];
+	u32 hw_type;
+	int monitor_cnt;
+	u32 min_freq;
+	u32 max_freq;
+};
+
+struct scmi_memlat_info {
+	struct scmi_protocol_handle *ph;
+	const struct qcom_generic_ext_ops *ops;
+	struct scmi_memory_info *memory[MAX_MEMORY_TYPES];
+	u32 cluster_info[NR_CPUS];
+	int memory_cnt;
+};
+
+static int populate_cluster_info(u32 *cluster_info)
+{
+	char name[MAX_NAME_LEN];
+	int i = 0;
+
+	struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
+	if (!cn)
+		return -ENODEV;
+
+	struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
+	if (!map)
+		return -ENODEV;
+
+	do {
+		snprintf(name, sizeof(name), "cluster%d", i);
+		struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
+		if (!c)
+			break;
+
+		*(cluster_info + i) = of_get_child_count(c);
+		i++;
+	} while (1);
+
+	return 0;
+}
+
+static void populate_physical_mask(struct device_node *np, u32 *mask, u32 *cluster_info)
+{
+	struct device_node *dev_phandle __free(device_node);
+	int cpu, i = 0, physical_id;
+
+	do {
+		dev_phandle = of_parse_phandle(np, "cpus", i++);
+		cpu = of_cpu_node_to_id(dev_phandle);
+		if (cpu != -ENODEV) {
+			physical_id = topology_core_id(cpu);
+			for (int j = 0; j < topology_cluster_id(cpu); j++)
+				physical_id += *(cluster_info + j);
+			*mask |= BIT(physical_id);
+		}
+	} while (dev_phandle);
+}
+
+static struct cpufreq_memfreq_map *init_cpufreq_memfreq_map(struct device *dev,
+							    struct scmi_memory_info *memory,
+							    struct device_node *of_node,
+							    u32 *cnt)
+{
+	struct device_node *tbl_np __free(device_node), *opp_np __free(device_node);
+	struct cpufreq_memfreq_map *tbl;
+	int ret, i = 0;
+	u32 level, len;
+	u64 rate;
+
+	tbl_np = of_parse_phandle(of_node, "operating-points-v2", 0);
+	if (!tbl_np)
+		return ERR_PTR(-ENODEV);
+
+	len = min(of_get_available_child_count(tbl_np), MAX_MAP_ENTRIES);
+	if (len == 0)
+		return ERR_PTR(-ENODEV);
+
+	tbl = devm_kzalloc(dev, (len + 1) * sizeof(struct cpufreq_memfreq_map),
+			   GFP_KERNEL);
+	if (!tbl)
+		return ERR_PTR(-ENOMEM);
+
+	for_each_available_child_of_node(tbl_np, opp_np) {
+		ret = of_property_read_u64_index(opp_np, "opp-hz", 0, &rate);
+		if (ret < 0)
+			return ERR_PTR(ret);
+
+		tbl[i].cpufreq_mhz = rate / HZ_PER_MHZ;
+
+		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
+			ret = of_property_read_u64_index(opp_np, "opp-hz", 1, &rate);
+			if (ret < 0)
+				return ERR_PTR(ret);
+
+			tbl[i].memfreq_khz = rate / HZ_PER_KHZ;
+		} else {
+			ret = of_property_read_u32(opp_np, "opp-level", &level);
+			if (ret < 0)
+				return ERR_PTR(ret);
+
+			tbl[i].memfreq_khz = level;
+		}
+
+		dev_dbg(dev, "Entry%d CPU:%u, Mem:%u\n", i, tbl[i].cpufreq_mhz, tbl[i].memfreq_khz);
+		i++;
+	}
+	*cnt = len;
+
+	return tbl;
+}
+
+static int process_scmi_memlat_of_node(struct scmi_device *sdev, struct scmi_memlat_info *info)
+{
+	struct scmi_monitor_info *monitor;
+	struct scmi_memory_info *memory;
+	char name[MAX_NAME_LEN];
+	u64 memfreq[2];
+	int ret;
+
+	ret = populate_cluster_info(info->cluster_info);
+	if (ret < 0) {
+		dev_err_probe(&sdev->dev, ret, "failed to populate cluster info\n");
+		goto err;
+	}
+
+	of_node_get(sdev->dev.of_node);
+	do {
+		snprintf(name, sizeof(name), "memory-%d", info->memory_cnt);
+		struct device_node *memory_np __free(device_node) =
+			of_find_node_by_name(sdev->dev.of_node, name);
+
+		if (!memory_np)
+			break;
+
+		if (info->memory_cnt >= MAX_MEMORY_TYPES)
+			return dev_err_probe(&sdev->dev, -EINVAL,
+					     "failed to parse unsupported memory type\n");
+
+		memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
+		if (!memory) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		ret = of_property_read_u32(memory_np, "qcom,memory-type", &memory->hw_type);
+		if (ret) {
+			dev_err_probe(&sdev->dev, ret, "failed to read memory type\n");
+			goto err;
+		}
+
+		ret = of_property_read_u64_array(memory_np, "freq-table-hz", memfreq, 2);
+		if (ret && (ret != -EINVAL)) {
+			dev_err_probe(&sdev->dev, ret, "failed to read min/max freq\n");
+			goto err;
+		}
+
+		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
+			memory->min_freq = memfreq[0] / HZ_PER_KHZ;
+			memory->max_freq = memfreq[1] / HZ_PER_KHZ;
+		} else {
+			memory->min_freq = memfreq[0];
+			memory->max_freq = memfreq[1];
+		}
+		info->memory[info->memory_cnt++] = memory;
+
+		do {
+			snprintf(name, sizeof(name), "monitor-%d", memory->monitor_cnt);
+			struct device_node *monitor_np __free(device_node) =
+				of_get_child_by_name(memory_np, name);
+
+			if (!monitor_np)
+				break;
+
+			if (memory->monitor_cnt >= MAX_MONITOR_CNT)
+				return dev_err_probe(&sdev->dev, -EINVAL,
+						     "failed to parse unsupported monitor\n");
+
+			monitor = devm_kzalloc(&sdev->dev, sizeof(*monitor), GFP_KERNEL);
+			if (!monitor) {
+				ret = -ENOMEM;
+				goto err;
+			}
+
+			monitor->mon_type = of_property_read_bool(monitor_np, "qcom,compute-type");
+			if (!monitor->mon_type) {
+				ret = of_property_read_u32(monitor_np, "qcom,ipm-ceil",
+							   &monitor->ipm_ceil);
+				if (ret) {
+					dev_err_probe(&sdev->dev, ret,
+						      "failed to read IPM ceiling\n");
+					goto err;
+				}
+			}
+
+			/*
+			 * Variants of the SoC having reduced number of cpus operate
+			 * with the same number of logical cpus but the physical
+			 * cpu disabled will differ between parts. Calculate the
+			 * physical cpu number using cluster information instead.
+			 */
+			populate_physical_mask(monitor_np, &monitor->mask, info->cluster_info);
+
+			monitor->freq_map = init_cpufreq_memfreq_map(&sdev->dev, memory, monitor_np,
+								     &monitor->freq_map_len);
+			if (IS_ERR(monitor->freq_map)) {
+				dev_err_probe(&sdev->dev, PTR_ERR(monitor->freq_map),
+					      "failed to populate cpufreq-memfreq map\n");
+				goto err;
+			}
+
+			strscpy(monitor->mon_name, name, sizeof(monitor->mon_name));
+			monitor->mon_idx = memory->monitor_cnt;
+
+			memory->monitor[memory->monitor_cnt++] = monitor;
+		} while (1);
+
+		if (!memory->monitor_cnt) {
+			ret = -EINVAL;
+			dev_err_probe(&sdev->dev, ret, "failed to find monitor nodes\n");
+			goto err;
+		}
+	} while (1);
+
+	if (!info->memory_cnt) {
+		ret = -EINVAL;
+		dev_err_probe(&sdev->dev, ret, "failed to find memory nodes\n");
+	}
+
+err:
+	of_node_put(sdev->dev.of_node);
+
+	return ret;
+}
+
+static int configure_cpucp_common_events(struct scmi_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.hw_type = INVALID_IDX;
+	msg.cid[INST_IDX] = EV_INST_RETIRED;
+	msg.cid[CYC_IDX] = EV_CPU_CYCLES;
+	msg.cid[CONST_CYC_IDX] = INVALID_IDX;
+	msg.cid[FE_STALL_IDX] = INVALID_IDX;
+	msg.cid[BE_STALL_IDX] = INVALID_IDX;
+
+	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_memlat_info *info, int memory_index)
+{
+	const struct qcom_generic_ext_ops *ops = info->ops;
+	struct scmi_memory_info *memory = info->memory[memory_index];
+	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_memlat_info *info,
+			       int memory_index, int monitor_index)
+{
+	const struct qcom_generic_ext_ops *ops = info->ops;
+	struct scmi_memory_info *memory = info->memory[memory_index];
+	struct scmi_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->mon_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->mon_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->mon_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;
+		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->mon_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->mon_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->mon_name);
+
+	return ret;
+}
+
+static int cpucp_memlat_init(struct scmi_device *sdev)
+{
+	const struct scmi_handle *handle = sdev->handle;
+	const struct qcom_generic_ext_ops *ops;
+	struct scmi_protocol_handle *ph;
+	struct scmi_memlat_info *info;
+	u32 cpucp_freq_method = CPUCP_DEFAULT_FREQ_METHOD;
+	u32 cpucp_sample_ms = CPUCP_DEFAULT_SAMPLING_PERIOD_MS;
+	int ret, i, j;
+
+	if (!handle)
+		return -ENODEV;
+
+	ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_QCOM_GENERIC, &ph);
+	if (IS_ERR(ops))
+		return PTR_ERR(ops);
+
+	info = devm_kzalloc(&sdev->dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	ret = process_scmi_memlat_of_node(sdev, info);
+	if (ret)
+		return ret;
+
+	info->ph = ph;
+	info->ops = ops;
+
+	/* 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, &cpucp_sample_ms, sizeof(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, &cpucp_freq_method, sizeof(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");
+
+	return ret;
+}
+
+static int scmi_client_probe(struct scmi_device *sdev)
+{
+	return cpucp_memlat_init(sdev);
+}
+
+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 qcom_scmi_client_drv = {
+	.name		= "scmi-qcom-generic-ext-memlat",
+	.probe		= scmi_client_probe,
+	.id_table	= scmi_id_table,
+};
+module_scmi_driver(qcom_scmi_client_drv);
+
+MODULE_DESCRIPTION("QTI SCMI client driver");
+MODULE_LICENSE("GPL");
-- 
2.34.1


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

* [PATCH V4 5/5] arm64: dts: qcom: x1e80100: Enable LLCC/DDR/DDR_QOS dvfs
  2024-10-07  6:10 [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
                   ` (3 preceding siblings ...)
  2024-10-07  6:10 ` [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor Sibi Sankar
@ 2024-10-07  6:10 ` Sibi Sankar
  2024-10-08  6:52 ` [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Krzysztof Kozlowski
  2024-11-06 12:55 ` Johan Hovold
  6 siblings, 0 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-10-07  6:10 UTC (permalink / raw)
  To: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt
  Cc: linux-kernel, linux-arm-msm, devicetree, linux-arm-kernel,
	quic_rgottimu, quic_kshivnan, quic_sibis, conor+dt, arm-scmi

Enable LLCC/DDR/DDR_QOS bus scaling through the QCOM SCMI Generic
Extension protocol.

Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com>
---
 arch/arm64/boot/dts/qcom/x1e80100.dtsi | 138 +++++++++++++++++++++++++
 1 file changed, 138 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/x1e80100.dtsi b/arch/arm64/boot/dts/qcom/x1e80100.dtsi
index aec3ceb502f6..24ebccd8c124 100644
--- a/arch/arm64/boot/dts/qcom/x1e80100.dtsi
+++ b/arch/arm64/boot/dts/qcom/x1e80100.dtsi
@@ -10,6 +10,7 @@
 #include <dt-bindings/clock/qcom,x1e80100-gpucc.h>
 #include <dt-bindings/clock/qcom,x1e80100-tcsr.h>
 #include <dt-bindings/dma/qcom-gpi.h>
+#include <dt-bindings/firmware/qcom,scmi-memlat.h>
 #include <dt-bindings/interconnect/qcom,icc.h>
 #include <dt-bindings/interconnect/qcom,x1e80100-rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
@@ -325,6 +326,143 @@ scmi_dvfs: protocol@13 {
 				reg = <0x13>;
 				#power-domain-cells = <1>;
 			};
+
+			scmi_vendor: protocol@80 {
+				reg = <0x80>;
+
+				memory-0 {
+					qcom,memory-type = <QCOM_MEM_TYPE_DDR>;
+					freq-table-hz = /bits/ 64 <200000000 4224000000>;
+
+					monitor-0 {
+						cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6
+							&CPU7 &CPU8 &CPU9 &CPU10 &CPU11>;
+						qcom,ipm-ceil = <20000000>;
+						operating-points-v2 = <&memory0_monitor0_opp_table>;
+
+						memory0_monitor0_opp_table: opp-table {
+							compatible = "operating-points-v2";
+
+							opp-999000000 {
+								opp-hz = /bits/ 64 <999000000 547000000>;
+							};
+
+							opp-1440000000 {
+								opp-hz = /bits/ 64 <1440000000 768000000>;
+							};
+
+							opp-1671000000 {
+								opp-hz = /bits/ 64 <1671000000 1555000000>;
+							};
+
+							opp-2189000000 {
+								opp-hz = /bits/ 64 <2189000000 2092000000>;
+							};
+
+							opp-2516000000 {
+								opp-hz = /bits/ 64 <2516000000 3187000000>;
+							};
+
+							opp-3860000000 {
+								opp-hz = /bits/ 64 <3860000000 4224000000>;
+							};
+						};
+					};
+
+					monitor-1 {
+						cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6
+							&CPU7 &CPU8 &CPU9 &CPU10 &CPU11>;
+						operating-points-v2 = <&memory0_monitor1_opp_table>;
+						qcom,compute-type;
+
+						memory0_monitor1_opp_table: opp-table {
+							compatible = "operating-points-v2";
+
+							opp-1440000000 {
+								opp-hz = /bits/ 64 <1440000000 200000000>;
+							};
+
+							opp-2189000000 {
+								opp-hz = /bits/ 64 <2189000000 768000000>;
+							};
+
+							opp-2516000000 {
+								opp-hz = /bits/ 64 <2516000000 1555000000>;
+							};
+
+							opp-3860000000 {
+								opp-hz = /bits/ 64 <3860000000 4224000000>;
+							};
+						};
+					};
+				};
+
+				memory-1 {
+					qcom,memory-type = <QCOM_MEM_TYPE_LLCC>;
+					freq-table-hz = /bits/ 64 <300000000 1067000000>;
+
+					monitor-0 {
+						cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6
+							&CPU7 &CPU8 &CPU9 &CPU10 &CPU11>;
+						qcom,ipm-ceil = <20000000>;
+						operating-points-v2 = <&memory1_monitor0_opp_table>;
+
+						memory1_monitor0_opp_table: opp-table {
+							compatible = "operating-points-v2";
+
+							opp-999000000 {
+								opp-hz = /bits/ 64 <999000000 300000000>;
+							};
+
+							opp-1440000000 {
+								opp-hz = /bits/ 64 <1440000000 466000000>;
+							};
+
+							opp-1671000000 {
+								opp-hz = /bits/ 64 <1671000000 600000000>;
+							};
+
+							opp-2189000000 {
+								opp-hz = /bits/ 64 <2189000000 806000000>;
+							};
+
+							opp-2516000000 {
+								opp-hz = /bits/ 64 <2516000000 933000000>;
+							};
+
+							opp-3860000000 {
+								opp-hz = /bits/ 64 <3860000000 1066000000>;
+							};
+						};
+					};
+				};
+
+				memory-2 {
+					qcom,memory-type = <QCOM_MEM_TYPE_DDR_QOS>;
+					freq-table-hz = /bits/ 64 <QCOM_DDR_LEVEL_AUTO QCOM_DDR_LEVEL_PERF>;
+
+					monitor-0 {
+						qcom,ipm-ceil = <20000000>;
+						cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6
+							&CPU7 &CPU8 &CPU9 &CPU10 &CPU11>;
+						operating-points-v2 = <&memory2_monitor0_opp_table>;
+
+						memory2_monitor0_opp_table: opp-table {
+							compatible = "operating-points-v2";
+
+							opp-2189000000 {
+								opp-hz = /bits/ 64 <2189000000>;
+								opp-level = <QCOM_DDR_LEVEL_AUTO>;
+							};
+
+							opp-3860000000 {
+								opp-hz = /bits/ 64 <3860000000>;
+								opp-level = <QCOM_DDR_LEVEL_PERF>;
+							};
+						};
+					};
+				};
+			};
 		};
 	};
 
-- 
2.34.1


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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-10-07  6:10 ` [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor Sibi Sankar
@ 2024-10-07 17:57   ` Dmitry Baryshkov
  2024-10-22  8:18     ` Sibi Sankar
  2024-10-10 12:18   ` Jonathan Cameron
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 64+ messages in thread
From: Dmitry Baryshkov @ 2024-10-07 17:57 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid

On Mon, Oct 07, 2024 at 11:40:22AM GMT, Sibi Sankar wrote:
> Introduce a 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).

This sounds like a devfreq implementation. Please provide a reason why
it doesn't use existing API (even if to export the information to the
userspace).

> 
> 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 <quic_sibis@quicinc.com>
> ---
> 
> v3:
> * 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]
> 
>  drivers/soc/qcom/Kconfig                   |  12 +
>  drivers/soc/qcom/Makefile                  |   1 +
>  drivers/soc/qcom/qcom_scmi_memlat_client.c | 569 +++++++++++++++++++++
>  3 files changed, 582 insertions(+)
>  create mode 100644 drivers/soc/qcom/qcom_scmi_memlat_client.c
> 
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index 74b9121240f8..1b6dd40d69ea 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -295,4 +295,16 @@ config QCOM_PBS
>  	  This module provides the APIs to the client drivers that wants to send the
>  	  PBS trigger event to the PBS RAM.
>  
> +config QCOM_SCMI_MEMLAT_CLIENT
> +	tristate "Qualcomm Technologies Inc. SCMI client driver"
> +	depends on QCOM_SCMI_GENERIC_EXT || COMPILE_TEST
> +	help
> +	  This driver uses the MEMLAT (memory latency) algorithm string
> +	  hosted on QCOM SCMI Vendor Protocol to detect memory latency

How can it use the string to detect workloads? Most likely you mean something like "uses memlat extensions".
Also s/QCOM/Qualcomm/ in the help text.

> +	  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.
> +
>  endmenu
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index acbca2ab5cc2..28549bb141bc 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -36,6 +36,7 @@ obj-$(CONFIG_QCOM_APR) += apr.o
>  obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o
>  obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) +=	kryo-l2-accessors.o
>  obj-$(CONFIG_QCOM_ICC_BWMON)	+= icc-bwmon.o
> +obj-$(CONFIG_QCOM_SCMI_MEMLAT_CLIENT)	+= qcom_scmi_memlat_client.o
>  qcom_ice-objs			+= ice.o
>  obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE)	+= qcom_ice.o
>  obj-$(CONFIG_QCOM_PBS) +=	qcom-pbs.o
> diff --git a/drivers/soc/qcom/qcom_scmi_memlat_client.c b/drivers/soc/qcom/qcom_scmi_memlat_client.c
> new file mode 100644
> index 000000000000..05198bf1f7ec
> --- /dev/null
> +++ b/drivers/soc/qcom/qcom_scmi_memlat_client.c
> @@ -0,0 +1,569 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#include <linux/cpu.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/scmi_protocol.h>
> +#include <linux/scmi_qcom_protocol.h>
> +#include <linux/units.h>
> +#include <dt-bindings/firmware/qcom,scmi-memlat.h>
> +
> +#define MEMLAT_ALGO_STR				0x4D454D4C4154 /* MEMLAT */
> +#define INVALID_IDX				0xff
> +#define MAX_MEMORY_TYPES			3
> +#define MAX_MONITOR_CNT				4
> +#define MAX_NAME_LEN				20
> +#define MAX_MAP_ENTRIES				7
> +#define CPUCP_DEFAULT_SAMPLING_PERIOD_MS	4
> +#define CPUCP_DEFAULT_FREQ_METHOD		1
> +
> +/**
> + * 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_SAMPLE_MS = 31,
> +	MEMLAT_MON_FREQ_MAP,
> +	MEMLAT_SET_MIN_FREQ,
> +	MEMLAT_SET_MAX_FREQ,
> +	MEMLAT_START_TIMER = 36,
> +	MEMLAT_STOP_TIMER,
> +	MEMLAT_SET_EFFECTIVE_FREQ_METHOD = 39,
> +};
> +
> +struct map_table {
> +	u16 v1;
> +	u16 v2;

Huh? Why can't it be cpufreq and memfreq with some suffix?

> +};
> +
> +struct map_param_msg {
> +	u32 hw_type;
> +	u32 mon_idx;
> +	u32 nr_rows;
> +	struct 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
> +};
> +
> +#define EV_CPU_CYCLES		0
> +#define EV_INST_RETIRED		2
> +#define EV_L2_D_RFILL		5
> +
> +struct ev_map_msg {
> +	u32 num_evs;
> +	u32 hw_type;
> +	u32 cid[NUM_COMMON_EVS];
> +};
> +
> +struct cpufreq_memfreq_map {
> +	unsigned int cpufreq_mhz;
> +	unsigned int memfreq_khz;
> +};
> +
> +struct scmi_monitor_info {
> +	struct cpufreq_memfreq_map *freq_map;
> +	char mon_name[MAX_NAME_LEN];
> +	u32 mon_idx;
> +	u32 mon_type;
> +	u32 ipm_ceil;
> +	u32 mask;
> +	u32 freq_map_len;
> +};
> +
> +struct scmi_memory_info {
> +	struct scmi_monitor_info *monitor[MAX_MONITOR_CNT];
> +	u32 hw_type;
> +	int monitor_cnt;
> +	u32 min_freq;
> +	u32 max_freq;
> +};
> +
> +struct scmi_memlat_info {
> +	struct scmi_protocol_handle *ph;
> +	const struct qcom_generic_ext_ops *ops;
> +	struct scmi_memory_info *memory[MAX_MEMORY_TYPES];
> +	u32 cluster_info[NR_CPUS];
> +	int memory_cnt;
> +};
> +
> +static int populate_cluster_info(u32 *cluster_info)
> +{
> +	char name[MAX_NAME_LEN];
> +	int i = 0;
> +
> +	struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
> +	if (!cn)
> +		return -ENODEV;
> +
> +	struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
> +	if (!map)
> +		return -ENODEV;
> +
> +	do {
> +		snprintf(name, sizeof(name), "cluster%d", i);
> +		struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
> +		if (!c)
> +			break;
> +
> +		*(cluster_info + i) = of_get_child_count(c);
> +		i++;
> +	} while (1);

Can you use existing API from drivers/base/arch_topology.c? If not, can
it be extended to support your usecase?

> +
> +	return 0;
> +}
> +
> +static void populate_physical_mask(struct device_node *np, u32 *mask, u32 *cluster_info)
> +{
> +	struct device_node *dev_phandle __free(device_node);
> +	int cpu, i = 0, physical_id;
> +
> +	do {
> +		dev_phandle = of_parse_phandle(np, "cpus", i++);
> +		cpu = of_cpu_node_to_id(dev_phandle);
> +		if (cpu != -ENODEV) {
> +			physical_id = topology_core_id(cpu);
> +			for (int j = 0; j < topology_cluster_id(cpu); j++)
> +				physical_id += *(cluster_info + j);
> +			*mask |= BIT(physical_id);
> +		}
> +	} while (dev_phandle);
> +}
> +
> +static struct cpufreq_memfreq_map *init_cpufreq_memfreq_map(struct device *dev,
> +							    struct scmi_memory_info *memory,
> +							    struct device_node *of_node,
> +							    u32 *cnt)
> +{
> +	struct device_node *tbl_np __free(device_node), *opp_np __free(device_node);
> +	struct cpufreq_memfreq_map *tbl;
> +	int ret, i = 0;
> +	u32 level, len;
> +	u64 rate;
> +
> +	tbl_np = of_parse_phandle(of_node, "operating-points-v2", 0);

Please use existing API to parse OPP tables or document a reason why it
can't be used.

> +	if (!tbl_np)
> +		return ERR_PTR(-ENODEV);
> +
> +	len = min(of_get_available_child_count(tbl_np), MAX_MAP_ENTRIES);
> +	if (len == 0)
> +		return ERR_PTR(-ENODEV);
> +
> +	tbl = devm_kzalloc(dev, (len + 1) * sizeof(struct cpufreq_memfreq_map),
> +			   GFP_KERNEL);
> +	if (!tbl)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for_each_available_child_of_node(tbl_np, opp_np) {
> +		ret = of_property_read_u64_index(opp_np, "opp-hz", 0, &rate);
> +		if (ret < 0)
> +			return ERR_PTR(ret);
> +
> +		tbl[i].cpufreq_mhz = rate / HZ_PER_MHZ;
> +
> +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
> +			ret = of_property_read_u64_index(opp_np, "opp-hz", 1, &rate);
> +			if (ret < 0)
> +				return ERR_PTR(ret);
> +
> +			tbl[i].memfreq_khz = rate / HZ_PER_KHZ;
> +		} else {
> +			ret = of_property_read_u32(opp_np, "opp-level", &level);
> +			if (ret < 0)
> +				return ERR_PTR(ret);
> +
> +			tbl[i].memfreq_khz = level;
> +		}
> +
> +		dev_dbg(dev, "Entry%d CPU:%u, Mem:%u\n", i, tbl[i].cpufreq_mhz, tbl[i].memfreq_khz);
> +		i++;
> +	}
> +	*cnt = len;
> +
> +	return tbl;
> +}
> +
> +static int process_scmi_memlat_of_node(struct scmi_device *sdev, struct scmi_memlat_info *info)
> +{
> +	struct scmi_monitor_info *monitor;
> +	struct scmi_memory_info *memory;
> +	char name[MAX_NAME_LEN];
> +	u64 memfreq[2];
> +	int ret;
> +
> +	ret = populate_cluster_info(info->cluster_info);
> +	if (ret < 0) {
> +		dev_err_probe(&sdev->dev, ret, "failed to populate cluster info\n");
> +		goto err;
> +	}
> +
> +	of_node_get(sdev->dev.of_node);
> +	do {
> +		snprintf(name, sizeof(name), "memory-%d", info->memory_cnt);
> +		struct device_node *memory_np __free(device_node) =
> +			of_find_node_by_name(sdev->dev.of_node, name);
> +
> +		if (!memory_np)
> +			break;
> +
> +		if (info->memory_cnt >= MAX_MEMORY_TYPES)
> +			return dev_err_probe(&sdev->dev, -EINVAL,
> +					     "failed to parse unsupported memory type\n");
> +
> +		memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
> +		if (!memory) {
> +			ret = -ENOMEM;
> +			goto err;
> +		}
> +
> +		ret = of_property_read_u32(memory_np, "qcom,memory-type", &memory->hw_type);
> +		if (ret) {
> +			dev_err_probe(&sdev->dev, ret, "failed to read memory type\n");
> +			goto err;
> +		}
> +
> +		ret = of_property_read_u64_array(memory_np, "freq-table-hz", memfreq, 2);
> +		if (ret && (ret != -EINVAL)) {
> +			dev_err_probe(&sdev->dev, ret, "failed to read min/max freq\n");
> +			goto err;
> +		}

Can we get this information from the OPP table instead?

> +
> +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
> +			memory->min_freq = memfreq[0] / HZ_PER_KHZ;
> +			memory->max_freq = memfreq[1] / HZ_PER_KHZ;
> +		} else {
> +			memory->min_freq = memfreq[0];
> +			memory->max_freq = memfreq[1];

Why? At least invert the logic here, please. The DDR_QOS is a special
case, not all other kinds of memory.

> +		}
> +		info->memory[info->memory_cnt++] = memory;
> +
> +		do {
> +			snprintf(name, sizeof(name), "monitor-%d", memory->monitor_cnt);
> +			struct device_node *monitor_np __free(device_node) =
> +				of_get_child_by_name(memory_np, name);
> +
> +			if (!monitor_np)
> +				break;
> +
> +			if (memory->monitor_cnt >= MAX_MONITOR_CNT)

Why do you need to limit it? Is it a protocol limitation or an
artificial driver limitation? Can monitors be allocated dynamically?

> +				return dev_err_probe(&sdev->dev, -EINVAL,
> +						     "failed to parse unsupported monitor\n");
> +
> +			monitor = devm_kzalloc(&sdev->dev, sizeof(*monitor), GFP_KERNEL);
> +			if (!monitor) {
> +				ret = -ENOMEM;
> +				goto err;
> +			}
> +
> +			monitor->mon_type = of_property_read_bool(monitor_np, "qcom,compute-type");
> +			if (!monitor->mon_type) {
> +				ret = of_property_read_u32(monitor_np, "qcom,ipm-ceil",
> +							   &monitor->ipm_ceil);
> +				if (ret) {
> +					dev_err_probe(&sdev->dev, ret,
> +						      "failed to read IPM ceiling\n");
> +					goto err;
> +				}
> +			}
> +
> +			/*
> +			 * Variants of the SoC having reduced number of cpus operate
> +			 * with the same number of logical cpus but the physical
> +			 * cpu disabled will differ between parts. Calculate the
> +			 * physical cpu number using cluster information instead.
> +			 */
> +			populate_physical_mask(monitor_np, &monitor->mask, info->cluster_info);
> +
> +			monitor->freq_map = init_cpufreq_memfreq_map(&sdev->dev, memory, monitor_np,
> +								     &monitor->freq_map_len);
> +			if (IS_ERR(monitor->freq_map)) {
> +				dev_err_probe(&sdev->dev, PTR_ERR(monitor->freq_map),
> +					      "failed to populate cpufreq-memfreq map\n");
> +				goto err;
> +			}
> +
> +			strscpy(monitor->mon_name, name, sizeof(monitor->mon_name));
> +			monitor->mon_idx = memory->monitor_cnt;
> +
> +			memory->monitor[memory->monitor_cnt++] = monitor;
> +		} while (1);
> +
> +		if (!memory->monitor_cnt) {
> +			ret = -EINVAL;
> +			dev_err_probe(&sdev->dev, ret, "failed to find monitor nodes\n");
> +			goto err;
> +		}
> +	} while (1);
> +
> +	if (!info->memory_cnt) {
> +		ret = -EINVAL;
> +		dev_err_probe(&sdev->dev, ret, "failed to find memory nodes\n");
> +	}
> +
> +err:
> +	of_node_put(sdev->dev.of_node);
> +
> +	return ret;
> +}
> +
> +static int configure_cpucp_common_events(struct scmi_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.hw_type = INVALID_IDX;
> +	msg.cid[INST_IDX] = EV_INST_RETIRED;
> +	msg.cid[CYC_IDX] = EV_CPU_CYCLES;
> +	msg.cid[CONST_CYC_IDX] = INVALID_IDX;
> +	msg.cid[FE_STALL_IDX] = INVALID_IDX;
> +	msg.cid[BE_STALL_IDX] = INVALID_IDX;
> +
> +	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_memlat_info *info, int memory_index)
> +{
> +	const struct qcom_generic_ext_ops *ops = info->ops;
> +	struct scmi_memory_info *memory = info->memory[memory_index];
> +	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_memlat_info *info,
> +			       int memory_index, int monitor_index)
> +{
> +	const struct qcom_generic_ext_ops *ops = info->ops;
> +	struct scmi_memory_info *memory = info->memory[memory_index];
> +	struct scmi_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->mon_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->mon_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->mon_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;
> +		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->mon_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->mon_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->mon_name);
> +
> +	return ret;
> +}
> +
> +static int cpucp_memlat_init(struct scmi_device *sdev)
> +{
> +	const struct scmi_handle *handle = sdev->handle;
> +	const struct qcom_generic_ext_ops *ops;
> +	struct scmi_protocol_handle *ph;
> +	struct scmi_memlat_info *info;
> +	u32 cpucp_freq_method = CPUCP_DEFAULT_FREQ_METHOD;
> +	u32 cpucp_sample_ms = CPUCP_DEFAULT_SAMPLING_PERIOD_MS;
> +	int ret, i, j;
> +
> +	if (!handle)
> +		return -ENODEV;
> +
> +	ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_QCOM_GENERIC, &ph);
> +	if (IS_ERR(ops))
> +		return PTR_ERR(ops);
> +
> +	info = devm_kzalloc(&sdev->dev, sizeof(*info), GFP_KERNEL);
> +	if (!info)
> +		return -ENOMEM;
> +
> +	ret = process_scmi_memlat_of_node(sdev, info);
> +	if (ret)
> +		return ret;
> +
> +	info->ph = ph;
> +	info->ops = ops;
> +
> +	/* 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, &cpucp_sample_ms, sizeof(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, &cpucp_freq_method, sizeof(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");
> +
> +	return ret;
> +}
> +
> +static int scmi_client_probe(struct scmi_device *sdev)
> +{
> +	return cpucp_memlat_init(sdev);

Inline it here, please.

> +}
> +
> +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 qcom_scmi_client_drv = {
> +	.name		= "scmi-qcom-generic-ext-memlat",
> +	.probe		= scmi_client_probe,
> +	.id_table	= scmi_id_table,
> +};
> +module_scmi_driver(qcom_scmi_client_drv);
> +
> +MODULE_DESCRIPTION("QTI SCMI client driver");
> +MODULE_LICENSE("GPL");
> -- 
> 2.34.1
> 

-- 
With best wishes
Dmitry

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

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-10-07  6:10 ` [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension Sibi Sankar
@ 2024-10-07 18:06   ` Dmitry Baryshkov
  2024-10-22  7:13     ` Sibi Sankar
  2024-10-08  6:47   ` Krzysztof Kozlowski
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 64+ messages in thread
From: Dmitry Baryshkov @ 2024-10-07 18:06 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi

On Mon, Oct 07, 2024 at 11:40:19AM GMT, Sibi Sankar wrote:
> Document the various memory buses that can be monitored and scaled by
> the memory latency governor hosted by the QCOM SCMI Generic Extension
> Protocol v1.0.
> 
> Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com>
> ---
> 
> v3:
> * Restructure the bindings to mimic IMX [Christian]
> 
>  .../bindings/firmware/arm,scmi.yaml           |   1 +
>  .../bindings/firmware/qcom,scmi-memlat.yaml   | 246 ++++++++++++++++++
>  .../dt-bindings/firmware/qcom,scmi-memlat.h   |  22 ++
>  3 files changed, 269 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
>  create mode 100644 include/dt-bindings/firmware/qcom,scmi-memlat.h
> 
> diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
> index 54d7d11bfed4..1d405f429168 100644
> --- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
> +++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
> @@ -24,6 +24,7 @@ description: |
>  
>  anyOf:
>    - $ref: /schemas/firmware/nxp,imx95-scmi.yaml
> +  - $ref: /schemas/firmware/qcom,scmi-memlat.yaml
>  
>  properties:
>    $nodename:
> diff --git a/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml b/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
> new file mode 100644
> index 000000000000..0e8ea6dacd6a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
> @@ -0,0 +1,246 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/firmware/qcom,scmi-memlat.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Qualcomm SCMI Memory Bus nodes
> +
> +maintainers:
> +  - Sibi Sankar <quic_sibis@quicinc.com>
> +
> +description:
> +  This binding describes the various memory buses that can be monitored and scaled
> +  by memory latency governor running on the CPU Control Processor (SCMI controller).
> +
> +properties:
> +  protocol@80:
> +    $ref: '/schemas/firmware/arm,scmi.yaml#/$defs/protocol-node'
> +    unevaluatedProperties: false
> +
> +    properties:
> +      reg:
> +        const: 0x80
> +
> +    patternProperties:
> +      '^memory-[0-9]$':
> +        type: object
> +        unevaluatedProperties: false
> +        description:
> +          The list of all memory buses that can be monitored and scaled by the
> +          memory latency governor running on the SCMI controller.
> +
> +        properties:
> +          qcom,memory-type:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            enum: [0, 1, 2]
> +            description: |
> +              Memory Bus Identifier
> +              0 = QCOM_MEM_TYPE_DDR
> +              1 = QCOM_MEM_TYPE_LLCC
> +              2 = QCOM_MEM_TYPE_DDR_QOS

I'm sorry if this has been discussed and frowned upon, but can you
squash memory type into device node?

protocol@80 {
	ddr {
	};

	llcc {
	};

	ddr-qos {
	};
};

> +
> +          freq-table-hz:
> +            items:
> +              items:
> +                - description: Minimum frequency of the memory bus in Hz
> +                - description: Maximum frequency of the memory bus in Hz

Does it make sense for the DDR-QOS type? Can we hardcode those values
and drop freq-table-hz from the DDR-QOS node?

Also, can we drop this completely by adding one extra OPP entry with the
minimum memory bus frequency?

> +
> +        patternProperties:
> +          '^monitor-[0-9]$':
> +            type: object
> +            unevaluatedProperties: false
> +            description:
> +              The list of all monitors detecting the memory latency bound workloads using
> +              various counters.
> +
> +            properties:
> +              qcom,compute-type:
> +                description:
> +                  Monitors of type compute perform bus dvfs based on a rudimentary CPU
> +                  frequency to memory frequency map.
> +                type: boolean

This seems to be redundant. If there is no qcom,ipm-ceil property, then
it's qcom,compute-type, isn't it?

> +
> +              qcom,ipm-ceil:
> +                $ref: /schemas/types.yaml#/definitions/uint32
> +                description:
> +                  Monitors having this property perform bus dvfs based on the same
> +                  rudimentary table but the scaling is performed only if the calculated
> +                  IPM (Instruction Per Misses) exceeds the given ceiling.
> +
> +              cpus:
> +                $ref: /schemas/types.yaml#/definitions/phandle-array
> +                description:
> +                  Should be a list of phandles to CPU nodes (as described in
> +                  Documentation/devicetree/bindings/arm/cpus.yaml).

Which CPU nodes? I see that the examples list all CPUs here. Do we
really need them?

> +
> +              operating-points-v2: true
> +              opp-table:
> +                type: object
> +
> +            required:
> +              - cpus
> +              - operating-points-v2
> +
> +            oneOf:
> +              - required: [ 'qcom,compute-type' ]
> +              - required: [ 'qcom,ipm-ceil' ]
> +
> +        required:
> +          - qcom,memory-type
> +          - freq-table-hz
> +
> +additionalProperties: true
> +
> +examples:
> +  - |
> +    #include <dt-bindings/firmware/qcom,scmi-memlat.h>
> +
> +    firmware {
> +        scmi {
> +            compatible = "arm,scmi";
> +            mboxes = <&cpucp_mbox 0>, <&cpucp_mbox 2>;
> +            mbox-names = "tx", "rx";
> +            shmem = <&cpu_scp_lpri0>, <&cpu_scp_lpri1>;
> +
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +
> +            protocol@80 {
> +                reg = <0x80>;
> +
> +                memory-0 {
> +                    qcom,memory-type = <QCOM_MEM_TYPE_DDR>;
> +                    freq-table-hz = /bits/ 64 <200000000 4224000000>;
> +
> +                    monitor-0 {

Hmm. Can we say that each memory type can have at most one IPM and one
compute aka "passive" memlat monitor? Does it make sense to use them as
node names and drop the extra monitor-M names?

> +                        qcom,ipm-ceil = <20000000>;
> +                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
> +                                &CPU8 &CPU9 &CPU10 &CPU11>;

Are CPU lists different between monitors? Can they be different? Can
they be different between different memory types?

> +                        operating-points-v2 = <&memory0_monitor0_opp_table>;
> +
> +                        memory0_monitor0_opp_table: opp-table {

sensible names are better:

ddr_ipm_opp_table: opp-table {
};

> +                            compatible = "operating-points-v2";
> +
> +                            opp-999000000 {
> +                                opp-hz = /bits/ 64 <999000000 547000000>;
> +                            };
> +
> +                            opp-1440000000 {
> +                                opp-hz = /bits/ 64 <1440000000 768000000>;
> +                            };
> +
> +                            opp-1671000000 {
> +                                opp-hz = /bits/ 64 <1671000000 1555000000>;
> +                            };
> +
> +                            opp-2189000000 {
> +                                opp-hz = /bits/ 64 <2189000000 2092000000>;
> +                            };
> +
> +                            opp-2516000000 {
> +                                opp-hz = /bits/ 64 <2516000000 3187000000>;
> +                            };
> +
> +                            opp-3860000000 {
> +                                opp-hz = /bits/ 64 <3860000000 4224000000>;
> +                            };
> +                        };
> +                    };
> +
> +                    monitor-1 {
> +                        qcom,compute-type;
> +                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
> +                                &CPU8 &CPU9 &CPU10 &CPU11>;
> +                        operating-points-v2 = <&memory0_monitor1_opp_table>;
> +
> +                        memory0_monitor1_opp_table: opp-table {
> +                            compatible = "operating-points-v2";
> +
> +                            opp-1440000000 {
> +                                    opp-hz = /bits/ 64 <1440000000 200000000>;
> +                            };
> +
> +                            opp-2189000000 {
> +                                    opp-hz = /bits/ 64 <2189000000 768000000>;
> +                            };
> +
> +                            opp-2516000000 {
> +                                    opp-hz = /bits/ 64 <2516000000 1555000000>;
> +                            };
> +
> +                            opp-3860000000 {
> +                                    opp-hz = /bits/ 64 <3860000000 4224000000>;
> +                            };
> +                        };
> +                    };
> +                };
> +
> +                memory-1 {
> +                    qcom,memory-type = <QCOM_MEM_TYPE_LLCC>;
> +                    freq-table-hz = /bits/ 64 <300000000 1067000000>;
> +
> +                    monitor-0 {
> +                        qcom,ipm-ceil = <20000000>;
> +                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
> +                                &CPU8 &CPU9 &CPU10 &CPU11>;
> +                        operating-points-v2 = <&memory1_monitor0_opp_table>;
> +
> +                        memory1_monitor0_opp_table: opp-table {
> +                            compatible = "operating-points-v2";
> +
> +                            opp-999000000 {
> +                                opp-hz = /bits/ 64 <999000000 300000000>;
> +                            };
> +
> +                            opp-1440000000 {
> +                                opp-hz = /bits/ 64 <1440000000 466000000>;
> +                            };
> +
> +                            opp-1671000000 {
> +                                opp-hz = /bits/ 64 <1671000000 600000000>;
> +                            };
> +
> +                            opp-2189000000 {
> +                                opp-hz = /bits/ 64 <2189000000 806000000>;
> +                            };
> +
> +                            opp-2516000000 {
> +                                opp-hz = /bits/ 64 <2516000000 933000000>;
> +                            };
> +
> +                            opp-3860000000 {
> +                                opp-hz = /bits/ 64 <3860000000 1066000000>;
> +                            };
> +                        };
> +                    };
> +                };
> +
> +                memory-2 {
> +                    qcom,memory-type = <QCOM_MEM_TYPE_DDR_QOS>;
> +                    freq-table-hz = /bits/ 64 <QCOM_DDR_LEVEL_AUTO QCOM_DDR_LEVEL_PERF>;

This is definitely not 'frequency of the memory bys in Hz'

> +
> +                    monitor-0 {
> +                        qcom,ipm-ceil = <20000000>;
> +                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
> +                                &CPU8 &CPU9 &CPU10 &CPU11>;
> +                        operating-points-v2 = <&memory2_monitor0_opp_table>;
> +
> +                        memory2_monitor0_opp_table: opp-table {
> +                            compatible = "operating-points-v2";
> +
> +                            opp-2189000000 {
> +                                opp-hz = /bits/ 64 <2189000000>;
> +                                opp-level = <QCOM_DDR_LEVEL_AUTO>;
> +                            };
> +
> +                            opp-3860000000 {
> +                                opp-hz = /bits/ 64 <3860000000>;
> +                                opp-level = <QCOM_DDR_LEVEL_PERF>;
> +                            };
> +                        };
> +                    };
> +                };
> +            };
> +        };
> +    };
> diff --git a/include/dt-bindings/firmware/qcom,scmi-memlat.h b/include/dt-bindings/firmware/qcom,scmi-memlat.h
> new file mode 100644
> index 000000000000..7ae8d8d5623b
> --- /dev/null
> +++ b/include/dt-bindings/firmware/qcom,scmi-memlat.h
> @@ -0,0 +1,22 @@
> +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
> +/*
> + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +#ifndef __DT_BINDINGS_QCOM_SCMI_VENDOR_H
> +#define __DT_BINDINGS_QCOM_SCMI_VENDOR
> +
> +/* Memory IDs */
> +#define QCOM_MEM_TYPE_DDR	0x0
> +#define QCOM_MEM_TYPE_LLCC	0x1
> +#define QCOM_MEM_TYPE_DDR_QOS	0x2
> +
> +/*
> + * QCOM_MEM_TYPE_DDR_QOS supports the following states.
> + *
> + * %QCOM_DDR_LEVEL_AUTO:	DDR operates with LPM enabled
> + * %QCOM_DDR_LEVEL_PERF:	DDR operates with LPM disabled
> + */
> +#define QCOM_DDR_LEVEL_AUTO	0x0
> +#define QCOM_DDR_LEVEL_PERF	0x1
> +
> +#endif /* __DT_BINDINGS_QCOM_SCMI_VENDOR_H */
> -- 
> 2.34.1
> 

-- 
With best wishes
Dmitry

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

* Re: [PATCH V4 3/5] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions
  2024-10-07  6:10 ` [PATCH V4 3/5] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions Sibi Sankar
@ 2024-10-07 18:13   ` Dmitry Baryshkov
  2024-10-22  7:18     ` Sibi Sankar
  0 siblings, 1 reply; 64+ messages in thread
From: Dmitry Baryshkov @ 2024-10-07 18:13 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid

On Mon, Oct 07, 2024 at 11:40:21AM GMT, Sibi Sankar wrote:
> 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 <quic_sibis@quicinc.com>
> Reviewed-by: Cristian Marussi <cristian.marussi@arm.com>
> ---
> 
> v3:
> * 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]
> 
>  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  | 184 ++++++++++++++++++
>  include/linux/scmi_qcom_protocol.h            |  39 ++++
>  6 files changed, 242 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 dabd874641d0..73128442d97b 100644
> --- a/drivers/firmware/arm_scmi/Kconfig
> +++ b/drivers/firmware/arm_scmi/Kconfig
> @@ -71,6 +71,7 @@ config ARM_SCMI_DEBUG_COUNTERS
>  
>  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 9ac81adff567..58cf4d656cbb 100644
> --- a/drivers/firmware/arm_scmi/Makefile
> +++ b/drivers/firmware/arm_scmi/Makefile
> @@ -12,6 +12,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..f7cd949161df
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/vendors/qcom/qcom-generic-ext.c
> @@ -0,0 +1,184 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2024, 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 implementation version set to 0x20000. The PROTOCOL_VERSION command
> + * returns version 1.0.
> + *
> + * @QCOM_SCMI_SET_PARAM: message_id: 0x10 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: message_id: 0x11 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: message_id: 0x12 is used to start the activity performed by
> + *                            the algo_str.
> + * @QCOM_SCMI_STOP_ACTIVITY: message_id: 0x13 is used to stop a pre-existing activity
> + *                           performed by the algo_str.

Drop message_id's from the definitions. They duplicate enum values.

> + */
> +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_set_param(const struct scmi_protocol_handle *ph, void *buf, size_t buf_len,
> +			       u64 algo_str, u32 param_id)
> +{
> +	struct scmi_xfer *t;
> +	struct qcom_scmi_msg *msg;
> +	int ret;
> +
> +	ret = ph->xops->xfer_get_init(ph, QCOM_SCMI_SET_PARAM, buf_len + sizeof(*msg), 0, &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, t->tx.len - sizeof(*msg));
> +
> +	ret = ph->xops->do_xfer(ph, t);
> +	ph->xops->xfer_put(ph, t);
> +
> +	return ret;
> +}
> +
> +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)
> +{
> +	struct scmi_xfer *t;
> +	struct qcom_scmi_msg *msg;
> +	int ret;
> +
> +	ret = ph->xops->xfer_get_init(ph, QCOM_SCMI_GET_PARAM, 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, t->tx.len - sizeof(*msg));

Isn't it buf_len?

> +
> +	ret = ph->xops->do_xfer(ph, t);
> +	memcpy(buf, t->rx.buf, t->rx.len);
> +	ph->xops->xfer_put(ph, t);
> +
> +	return ret;
> +}
> +
> +static int qcom_scmi_start_activity(const struct scmi_protocol_handle *ph, void *buf,
> +				    size_t buf_len, u64 algo_str, u32 param_id)
> +{
> +	struct scmi_xfer *t;
> +	struct qcom_scmi_msg *msg;
> +	int ret;
> +
> +	ret = ph->xops->xfer_get_init(ph, QCOM_SCMI_START_ACTIVITY, buf_len + sizeof(*msg), 0, &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, t->tx.len - sizeof(*msg));

Isn't it buf_len?

> +
> +	ret = ph->xops->do_xfer(ph, t);
> +	ph->xops->xfer_put(ph, t);
> +
> +	return ret;
> +}
> +
> +static int qcom_scmi_stop_activity(const struct scmi_protocol_handle *ph, void *buf,
> +				   size_t buf_len, u64 algo_str, u32 param_id)
> +{
> +	struct scmi_xfer *t;
> +	struct qcom_scmi_msg *msg;
> +	int ret;
> +
> +	ret = ph->xops->xfer_get_init(ph, QCOM_SCMI_STOP_ACTIVITY, buf_len + sizeof(*msg), 0, &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, t->tx.len - sizeof(*msg));

Isn't it buf_len?

> +
> +	ret = ph->xops->do_xfer(ph, t);
> +	ph->xops->xfer_put(ph, t);

Could you please extract a common helper that handles xfer for you?
Seeing the same code 4 times is 3 times too much in my opinion.

> +
> +	return ret;
> +}
> +
> +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)
> +{
> +	u32 version;
> +
> +	ph->xops->version_get(ph, &version);
> +
> +	dev_info(ph->dev, "QCOM Generic Vendor Version %d.%d\n",
> +		 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));

dev_dbg, please.

> +
> +	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",
> +	.impl_ver = 0x20000,
> +};
> +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..8f82c42e566d
> --- /dev/null
> +++ b/include/linux/scmi_qcom_protocol.h
> @@ -0,0 +1,39 @@
> +/* 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/bitfield.h>
> +#include <linux/device.h>

It doesn't look like you need those two headers.

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

-- 
With best wishes
Dmitry

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

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-10-07  6:10 ` [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension Sibi Sankar
  2024-10-07 18:06   ` Dmitry Baryshkov
@ 2024-10-08  6:47   ` Krzysztof Kozlowski
  2024-10-08  6:49   ` Krzysztof Kozlowski
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 64+ messages in thread
From: Krzysztof Kozlowski @ 2024-10-08  6:47 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi

On Mon, Oct 07, 2024 at 11:40:19AM +0530, Sibi Sankar wrote:
> Document the various memory buses that can be monitored and scaled by
> the memory latency governor hosted by the QCOM SCMI Generic Extension
> Protocol v1.0.
> 
> Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com>
> ---
> 
> v3:
> * Restructure the bindings to mimic IMX [Christian]
> 
>  .../bindings/firmware/arm,scmi.yaml           |   1 +
>  .../bindings/firmware/qcom,scmi-memlat.yaml   | 246 ++++++++++++++++++
>  .../dt-bindings/firmware/qcom,scmi-memlat.h   |  22 ++
>  3 files changed, 269 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
>  create mode 100644 include/dt-bindings/firmware/qcom,scmi-memlat.h
> 
> diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
> index 54d7d11bfed4..1d405f429168 100644
> --- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
> +++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
> @@ -24,6 +24,7 @@ description: |
>  
>  anyOf:
>    - $ref: /schemas/firmware/nxp,imx95-scmi.yaml
> +  - $ref: /schemas/firmware/qcom,scmi-memlat.yaml
>  
>  properties:
>    $nodename:
> diff --git a/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml b/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
> new file mode 100644
> index 000000000000..0e8ea6dacd6a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
> @@ -0,0 +1,246 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/firmware/qcom,scmi-memlat.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Qualcomm SCMI Memory Bus nodes
> +
> +maintainers:
> +  - Sibi Sankar <quic_sibis@quicinc.com>
> +
> +description:
> +  This binding describes the various memory buses that can be monitored and scaled

Drop "This binding" and describe the hardware/firmware, not binding.

Also, not wrapped according to Linux coding style.


> +  by memory latency governor running on the CPU Control Processor (SCMI controller).
> +
> +properties:
> +  protocol@80:
> +    $ref: '/schemas/firmware/arm,scmi.yaml#/$defs/protocol-node'
> +    unevaluatedProperties: false
> +
> +    properties:
> +      reg:
> +        const: 0x80
> +
> +    patternProperties:
> +      '^memory-[0-9]$':
> +        type: object
> +        unevaluatedProperties: false
> +        description:
> +          The list of all memory buses that can be monitored and scaled by the
> +          memory latency governor running on the SCMI controller.
> +
> +        properties:
> +          qcom,memory-type:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            enum: [0, 1, 2]
> +            description: |
> +              Memory Bus Identifier
> +              0 = QCOM_MEM_TYPE_DDR
> +              1 = QCOM_MEM_TYPE_LLCC
> +              2 = QCOM_MEM_TYPE_DDR_QOS

You need to describe these. Why "Quality of service" should be a separate
bus?

To me it looks like you are re-defining interconnects.

> +
> +          freq-table-hz:
> +            items:
> +              items:

" - items:"
no?

> +                - description: Minimum frequency of the memory bus in Hz
> +                - description: Maximum frequency of the memory bus in Hz
> +
> +        patternProperties:
> +          '^monitor-[0-9]$':
> +            type: object
> +            unevaluatedProperties: false
> +            description:
> +              The list of all monitors detecting the memory latency bound workloads using
> +              various counters.
> +
> +            properties:
> +              qcom,compute-type:
> +                description:
> +                  Monitors of type compute perform bus dvfs based on a rudimentary CPU
> +                  frequency to memory frequency map.

I don't understand why you need to describe what type of monitor the
SCMI has. Commit msg is pretty vague, so does not help me, either.

> +                type: boolean
> +
> +              qcom,ipm-ceil:
> +                $ref: /schemas/types.yaml#/definitions/uint32
> +                description:
> +                  Monitors having this property perform bus dvfs based on the same
> +                  rudimentary table but the scaling is performed only if the calculated
> +                  IPM (Instruction Per Misses) exceeds the given ceiling.
> +
> +              cpus:
> +                $ref: /schemas/types.yaml#/definitions/phandle-array
> +                description:
> +                  Should be a list of phandles to CPU nodes (as described in
> +                  Documentation/devicetree/bindings/arm/cpus.yaml).
> +
> +              operating-points-v2: true
> +              opp-table:
> +                type: object
> +
> +            required:
> +              - cpus
> +              - operating-points-v2
> +
> +            oneOf:
> +              - required: [ 'qcom,compute-type' ]
> +              - required: [ 'qcom,ipm-ceil' ]
> +
> +        required:
> +          - qcom,memory-type
> +          - freq-table-hz
> +
> +additionalProperties: true
> +
> +examples:
> +  - |
> +    #include <dt-bindings/firmware/qcom,scmi-memlat.h>
> +
> +    firmware {
> +        scmi {
> +            compatible = "arm,scmi";
> +            mboxes = <&cpucp_mbox 0>, <&cpucp_mbox 2>;
> +            mbox-names = "tx", "rx";
> +            shmem = <&cpu_scp_lpri0>, <&cpu_scp_lpri1>;
> +
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +
> +            protocol@80 {
> +                reg = <0x80>;
> +
> +                memory-0 {
> +                    qcom,memory-type = <QCOM_MEM_TYPE_DDR>;
> +                    freq-table-hz = /bits/ 64 <200000000 4224000000>;
> +
> +                    monitor-0 {
> +                        qcom,ipm-ceil = <20000000>;
> +                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
> +                                &CPU8 &CPU9 &CPU10 &CPU11>;

Labels are lowercase.

> +                        operating-points-v2 = <&memory0_monitor0_opp_table>;
> +
> +                        memory0_monitor0_opp_table: opp-table {
> +                            compatible = "operating-points-v2";
> +
> +                            opp-999000000 {
> +                                opp-hz = /bits/ 64 <999000000 547000000>;
> +                            };
> +
> +                            opp-1440000000 {
> +                                opp-hz = /bits/ 64 <1440000000 768000000>;
> +                            };
> +
> +                            opp-1671000000 {
> +                                opp-hz = /bits/ 64 <1671000000 1555000000>;
> +                            };
> +
> +                            opp-2189000000 {
> +                                opp-hz = /bits/ 64 <2189000000 2092000000>;
> +                            };
> +
> +                            opp-2516000000 {
> +                                opp-hz = /bits/ 64 <2516000000 3187000000>;
> +                            };
> +
> +                            opp-3860000000 {
> +                                opp-hz = /bits/ 64 <3860000000 4224000000>;
> +                            };
> +                        };
> +                    };
> +
> +                    monitor-1 {
> +                        qcom,compute-type;
> +                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
> +                                &CPU8 &CPU9 &CPU10 &CPU11>;
> +                        operating-points-v2 = <&memory0_monitor1_opp_table>;
> +
> +                        memory0_monitor1_opp_table: opp-table {
> +                            compatible = "operating-points-v2";
> +
> +                            opp-1440000000 {
> +                                    opp-hz = /bits/ 64 <1440000000 200000000>;
> +                            };
> +
> +                            opp-2189000000 {
> +                                    opp-hz = /bits/ 64 <2189000000 768000000>;
> +                            };
> +
> +                            opp-2516000000 {
> +                                    opp-hz = /bits/ 64 <2516000000 1555000000>;
> +                            };
> +
> +                            opp-3860000000 {
> +                                    opp-hz = /bits/ 64 <3860000000 4224000000>;
> +                            };
> +                        };
> +                    };
> +                };
> +
> +                memory-1 {
> +                    qcom,memory-type = <QCOM_MEM_TYPE_LLCC>;
> +                    freq-table-hz = /bits/ 64 <300000000 1067000000>;
> +
> +                    monitor-0 {
> +                        qcom,ipm-ceil = <20000000>;
> +                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
> +                                &CPU8 &CPU9 &CPU10 &CPU11>;
> +                        operating-points-v2 = <&memory1_monitor0_opp_table>;
> +
> +                        memory1_monitor0_opp_table: opp-table {
> +                            compatible = "operating-points-v2";
> +
> +                            opp-999000000 {
> +                                opp-hz = /bits/ 64 <999000000 300000000>;
> +                            };
> +
> +                            opp-1440000000 {
> +                                opp-hz = /bits/ 64 <1440000000 466000000>;
> +                            };
> +
> +                            opp-1671000000 {
> +                                opp-hz = /bits/ 64 <1671000000 600000000>;
> +                            };
> +
> +                            opp-2189000000 {
> +                                opp-hz = /bits/ 64 <2189000000 806000000>;
> +                            };
> +
> +                            opp-2516000000 {
> +                                opp-hz = /bits/ 64 <2516000000 933000000>;
> +                            };
> +
> +                            opp-3860000000 {
> +                                opp-hz = /bits/ 64 <3860000000 1066000000>;
> +                            };
> +                        };
> +                    };
> +                };
> +
> +                memory-2 {
> +                    qcom,memory-type = <QCOM_MEM_TYPE_DDR_QOS>;
> +                    freq-table-hz = /bits/ 64 <QCOM_DDR_LEVEL_AUTO QCOM_DDR_LEVEL_PERF>;
> +
> +                    monitor-0 {
> +                        qcom,ipm-ceil = <20000000>;
> +                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
> +                                &CPU8 &CPU9 &CPU10 &CPU11>;
> +                        operating-points-v2 = <&memory2_monitor0_opp_table>;
> +
> +                        memory2_monitor0_opp_table: opp-table {
> +                            compatible = "operating-points-v2";
> +
> +                            opp-2189000000 {
> +                                opp-hz = /bits/ 64 <2189000000>;
> +                                opp-level = <QCOM_DDR_LEVEL_AUTO>;
> +                            };
> +
> +                            opp-3860000000 {
> +                                opp-hz = /bits/ 64 <3860000000>;
> +                                opp-level = <QCOM_DDR_LEVEL_PERF>;
> +                            };
> +                        };
> +                    };
> +                };
> +            };
> +        };
> +    };
> diff --git a/include/dt-bindings/firmware/qcom,scmi-memlat.h b/include/dt-bindings/firmware/qcom,scmi-memlat.h
> new file mode 100644
> index 000000000000..7ae8d8d5623b
> --- /dev/null
> +++ b/include/dt-bindings/firmware/qcom,scmi-memlat.h
> @@ -0,0 +1,22 @@
> +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
> +/*
> + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +#ifndef __DT_BINDINGS_QCOM_SCMI_VENDOR_H
> +#define __DT_BINDINGS_QCOM_SCMI_VENDOR
> +
> +/* Memory IDs */
> +#define QCOM_MEM_TYPE_DDR	0x0
> +#define QCOM_MEM_TYPE_LLCC	0x1
> +#define QCOM_MEM_TYPE_DDR_QOS	0x2

Use decimal.

> +
> +/*
> + * QCOM_MEM_TYPE_DDR_QOS supports the following states.
> + *
> + * %QCOM_DDR_LEVEL_AUTO:	DDR operates with LPM enabled
> + * %QCOM_DDR_LEVEL_PERF:	DDR operates with LPM disabled

What is "%"?

> + */
> +#define QCOM_DDR_LEVEL_AUTO	0x0
> +#define QCOM_DDR_LEVEL_PERF	0x1

Decimal.

> +
> +#endif /* __DT_BINDINGS_QCOM_SCMI_VENDOR_H */
> -- 
> 2.34.1
> 

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

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-10-07  6:10 ` [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension Sibi Sankar
  2024-10-07 18:06   ` Dmitry Baryshkov
  2024-10-08  6:47   ` Krzysztof Kozlowski
@ 2024-10-08  6:49   ` Krzysztof Kozlowski
  2024-10-08 12:10     ` Dmitry Baryshkov
  2024-11-06 22:18   ` Jeffrey Hugo
  2024-12-05 15:27   ` Sudeep Holla
  4 siblings, 1 reply; 64+ messages in thread
From: Krzysztof Kozlowski @ 2024-10-08  6:49 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi

On Mon, Oct 07, 2024 at 11:40:19AM +0530, Sibi Sankar wrote:
> +/*
> + * QCOM_MEM_TYPE_DDR_QOS supports the following states.
> + *
> + * %QCOM_DDR_LEVEL_AUTO:	DDR operates with LPM enabled
> + * %QCOM_DDR_LEVEL_PERF:	DDR operates with LPM disabled
> + */
> +#define QCOM_DDR_LEVEL_AUTO	0x0
> +#define QCOM_DDR_LEVEL_PERF	0x1

I could not find any driver using these. Can you point me to usage in
the drivers?

Best regards,
Krzysztof


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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-10-07  6:10 [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
                   ` (4 preceding siblings ...)
  2024-10-07  6:10 ` [PATCH V4 5/5] arm64: dts: qcom: x1e80100: Enable LLCC/DDR/DDR_QOS dvfs Sibi Sankar
@ 2024-10-08  6:52 ` Krzysztof Kozlowski
  2024-10-22  8:24   ` Sibi Sankar
  2024-11-06 12:55 ` Johan Hovold
  6 siblings, 1 reply; 64+ messages in thread
From: Krzysztof Kozlowski @ 2024-10-08  6:52 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi

On Mon, Oct 07, 2024 at 11:40:18AM +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. Introduce a client driver that
> uses the memlat 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).

None of your patches are wrapped according to Linux coding style which
makes reviewing more difficult than it should be. And before you answer
with checkpatch, checkpatch is not a coding style.

Best regards,
Krzysztof


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

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-10-08  6:49   ` Krzysztof Kozlowski
@ 2024-10-08 12:10     ` Dmitry Baryshkov
  2024-10-08 12:11       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 64+ messages in thread
From: Dmitry Baryshkov @ 2024-10-08 12:10 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Sibi Sankar, sudeep.holla, cristian.marussi, andersson,
	konrad.dybcio, robh+dt, krzysztof.kozlowski+dt, linux-kernel,
	linux-arm-msm, devicetree, linux-arm-kernel, quic_rgottimu,
	quic_kshivnan, conor+dt, arm-scmi

On Tue, Oct 08, 2024 at 08:49:27AM GMT, Krzysztof Kozlowski wrote:
> On Mon, Oct 07, 2024 at 11:40:19AM +0530, Sibi Sankar wrote:
> > +/*
> > + * QCOM_MEM_TYPE_DDR_QOS supports the following states.
> > + *
> > + * %QCOM_DDR_LEVEL_AUTO:	DDR operates with LPM enabled
> > + * %QCOM_DDR_LEVEL_PERF:	DDR operates with LPM disabled
> > + */
> > +#define QCOM_DDR_LEVEL_AUTO	0x0
> > +#define QCOM_DDR_LEVEL_PERF	0x1
> 
> I could not find any driver using these. Can you point me to usage in
> the drivers?

It's well hidden. These are the raw values used for DDR_QOS memory.

> 
> Best regards,
> Krzysztof
> 

-- 
With best wishes
Dmitry

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

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-10-08 12:10     ` Dmitry Baryshkov
@ 2024-10-08 12:11       ` Krzysztof Kozlowski
  2024-10-22  7:25         ` Sibi Sankar
  0 siblings, 1 reply; 64+ messages in thread
From: Krzysztof Kozlowski @ 2024-10-08 12:11 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Sibi Sankar, sudeep.holla, cristian.marussi, andersson,
	konrad.dybcio, robh+dt, krzysztof.kozlowski+dt, linux-kernel,
	linux-arm-msm, devicetree, linux-arm-kernel, quic_rgottimu,
	quic_kshivnan, conor+dt, arm-scmi

On 08/10/2024 14:10, Dmitry Baryshkov wrote:
> On Tue, Oct 08, 2024 at 08:49:27AM GMT, Krzysztof Kozlowski wrote:
>> On Mon, Oct 07, 2024 at 11:40:19AM +0530, Sibi Sankar wrote:
>>> +/*
>>> + * QCOM_MEM_TYPE_DDR_QOS supports the following states.
>>> + *
>>> + * %QCOM_DDR_LEVEL_AUTO:	DDR operates with LPM enabled
>>> + * %QCOM_DDR_LEVEL_PERF:	DDR operates with LPM disabled
>>> + */
>>> +#define QCOM_DDR_LEVEL_AUTO	0x0
>>> +#define QCOM_DDR_LEVEL_PERF	0x1
>>
>> I could not find any driver using these. Can you point me to usage in
>> the drivers?
> 
> It's well hidden. These are the raw values used for DDR_QOS memory.

So not a binding? Then these should be dropped.

Best regards,
Krzysztof


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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-10-07  6:10 ` [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor Sibi Sankar
  2024-10-07 17:57   ` Dmitry Baryshkov
@ 2024-10-10 12:18   ` Jonathan Cameron
  2024-10-22  7:31     ` Sibi Sankar
  2024-10-22 12:00   ` Cristian Marussi
  2024-11-29  9:57   ` Shivnandan Kumar
  3 siblings, 1 reply; 64+ messages in thread
From: Jonathan Cameron @ 2024-10-10 12:18 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid

On Mon, 7 Oct 2024 11:40:22 +0530
Sibi Sankar <quic_sibis@quicinc.com> wrote:

> Introduce a 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).
> 
> 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 <quic_sibis@quicinc.com>
I was curious. A few comments from a quick read through.

Jonathan

> diff --git a/drivers/soc/qcom/qcom_scmi_memlat_client.c b/drivers/soc/qcom/qcom_scmi_memlat_client.c
> new file mode 100644
> index 000000000000..05198bf1f7ec
> --- /dev/null
> +++ b/drivers/soc/qcom/qcom_scmi_memlat_client.c

> +static int populate_cluster_info(u32 *cluster_info)
> +{
> +	char name[MAX_NAME_LEN];
> +	int i = 0;
> +
> +	struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
> +	if (!cn)
> +		return -ENODEV;
> +
> +	struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
> +	if (!map)
> +		return -ENODEV;
> +
> +	do {
while(1) {
}
> +		snprintf(name, sizeof(name), "cluster%d", i);
> +		struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
> +		if (!c)
> +			break;
> +
> +		*(cluster_info + i) = of_get_child_count(c);
> +		i++;
> +	} while (1);
> +
> +	return 0;
> +}
> +
tic struct cpufreq_memfreq_map *init_cpufreq_memfreq_map(struct device *dev,
> +							    struct scmi_memory_info *memory,
> +							    struct device_node *of_node,
> +							    u32 *cnt)
> +{
> +	struct device_node *tbl_np __free(device_node), *opp_np __free(device_node);
> +	struct cpufreq_memfreq_map *tbl;
> +	int ret, i = 0;
> +	u32 level, len;
> +	u64 rate;
> +
> +	tbl_np = of_parse_phandle(of_node, "operating-points-v2", 0);
> +	if (!tbl_np)

This will call the free on the uninitialzed opp_np above.
Note this sort of path is why the constructor and destructor should always
be together in the code.

> +		return ERR_PTR(-ENODEV);
> +
> +	len = min(of_get_available_child_count(tbl_np), MAX_MAP_ENTRIES);
> +	if (len == 0)
> +		return ERR_PTR(-ENODEV);
> +
> +	tbl = devm_kzalloc(dev, (len + 1) * sizeof(struct cpufreq_memfreq_map),
> +			   GFP_KERNEL);
> +	if (!tbl)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for_each_available_child_of_node(tbl_np, opp_np) {

Why not scoped variant which will also solve the lifetime issue above.

> +		ret = of_property_read_u64_index(opp_np, "opp-hz", 0, &rate);
> +		if (ret < 0)
> +			return ERR_PTR(ret);
> +
> +		tbl[i].cpufreq_mhz = rate / HZ_PER_MHZ;
> +
> +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
> +			ret = of_property_read_u64_index(opp_np, "opp-hz", 1, &rate);
> +			if (ret < 0)
> +				return ERR_PTR(ret);
> +
> +			tbl[i].memfreq_khz = rate / HZ_PER_KHZ;
> +		} else {
> +			ret = of_property_read_u32(opp_np, "opp-level", &level);
> +			if (ret < 0)
> +				return ERR_PTR(ret);
> +
> +			tbl[i].memfreq_khz = level;
> +		}
> +
> +		dev_dbg(dev, "Entry%d CPU:%u, Mem:%u\n", i, tbl[i].cpufreq_mhz, tbl[i].memfreq_khz);
> +		i++;
> +	}
> +	*cnt = len;
> +
> +	return tbl;
> +}
> +
> +static int process_scmi_memlat_of_node(struct scmi_device *sdev, struct scmi_memlat_info *info)
> +{
> +	struct scmi_monitor_info *monitor;
> +	struct scmi_memory_info *memory;
> +	char name[MAX_NAME_LEN];
> +	u64 memfreq[2];
> +	int ret;
> +
> +	ret = populate_cluster_info(info->cluster_info);
> +	if (ret < 0) {
> +		dev_err_probe(&sdev->dev, ret, "failed to populate cluster info\n");
> +		goto err;
putting a node you never got?
		return dev_err_probe()


> +	}
> +
> +	of_node_get(sdev->dev.of_node);
Maybe use __free(device_node) here so you can do early returns on error.
Will need a local variable for the return of of_node_get, but that would
be nice anyway to simplify some parameters belwo.

> +	do {
Might as well do while(1) {
}
> +		snprintf(name, sizeof(name), "memory-%d", info->memory_cnt);
> +		struct device_node *memory_np __free(device_node) =
> +			of_find_node_by_name(sdev->dev.of_node, name);
> +
> +		if (!memory_np)
> +			break;
> +
> +		if (info->memory_cnt >= MAX_MEMORY_TYPES)
> +			return dev_err_probe(&sdev->dev, -EINVAL,
> +					     "failed to parse unsupported memory type\n");
> +
> +		memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
> +		if (!memory) {
> +			ret = -ENOMEM;
> +			goto err;
> +		}
> +
> +		ret = of_property_read_u32(memory_np, "qcom,memory-type", &memory->hw_type);
> +		if (ret) {
> +			dev_err_probe(&sdev->dev, ret, "failed to read memory type\n");
> +			goto err;
> +		}
> +
> +		ret = of_property_read_u64_array(memory_np, "freq-table-hz", memfreq, 2);
> +		if (ret && (ret != -EINVAL)) {
> +			dev_err_probe(&sdev->dev, ret, "failed to read min/max freq\n");
> +			goto err;
> +		}
> +
> +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
> +			memory->min_freq = memfreq[0] / HZ_PER_KHZ;
> +			memory->max_freq = memfreq[1] / HZ_PER_KHZ;
> +		} else {
> +			memory->min_freq = memfreq[0];
> +			memory->max_freq = memfreq[1];
> +		}
> +		info->memory[info->memory_cnt++] = memory;
> +
> +		do {
> +			snprintf(name, sizeof(name), "monitor-%d", memory->monitor_cnt);
> +			struct device_node *monitor_np __free(device_node) =
> +				of_get_child_by_name(memory_np, name);
> +
> +			if (!monitor_np)
> +				break;
> +
> +			if (memory->monitor_cnt >= MAX_MONITOR_CNT)
> +				return dev_err_probe(&sdev->dev, -EINVAL,
> +						     "failed to parse unsupported monitor\n");
> +
> +			monitor = devm_kzalloc(&sdev->dev, sizeof(*monitor), GFP_KERNEL);
> +			if (!monitor) {
> +				ret = -ENOMEM;
> +				goto err;
> +			}
> +
> +			monitor->mon_type = of_property_read_bool(monitor_np, "qcom,compute-type");
> +			if (!monitor->mon_type) {
> +				ret = of_property_read_u32(monitor_np, "qcom,ipm-ceil",
> +							   &monitor->ipm_ceil);
> +				if (ret) {
> +					dev_err_probe(&sdev->dev, ret,
> +						      "failed to read IPM ceiling\n");
> +					goto err;
> +				}
> +			}
> +
> +			/*
> +			 * Variants of the SoC having reduced number of cpus operate
> +			 * with the same number of logical cpus but the physical
> +			 * cpu disabled will differ between parts. Calculate the
> +			 * physical cpu number using cluster information instead.
> +			 */
> +			populate_physical_mask(monitor_np, &monitor->mask, info->cluster_info);
> +
> +			monitor->freq_map = init_cpufreq_memfreq_map(&sdev->dev, memory, monitor_np,
> +								     &monitor->freq_map_len);
> +			if (IS_ERR(monitor->freq_map)) {
> +				dev_err_probe(&sdev->dev, PTR_ERR(monitor->freq_map),
> +					      "failed to populate cpufreq-memfreq map\n");
> +				goto err;
> +			}
> +
> +			strscpy(monitor->mon_name, name, sizeof(monitor->mon_name));
> +			monitor->mon_idx = memory->monitor_cnt;
> +
> +			memory->monitor[memory->monitor_cnt++] = monitor;
> +		} while (1);
> +
> +		if (!memory->monitor_cnt) {
> +			ret = -EINVAL;
> +			dev_err_probe(&sdev->dev, ret, "failed to find monitor nodes\n");
> +			goto err;
> +		}
> +	} while (1);
> +
> +	if (!info->memory_cnt) {
> +		ret = -EINVAL;
> +		dev_err_probe(&sdev->dev, ret, "failed to find memory nodes\n");
> +	}
> +
> +err:
> +	of_node_put(sdev->dev.of_node);
> +
> +	return ret;
> +}


> +
> +static int cpucp_memlat_init(struct scmi_device *sdev)
> +{
> +	const struct scmi_handle *handle = sdev->handle;
> +	const struct qcom_generic_ext_ops *ops;
> +	struct scmi_protocol_handle *ph;
> +	struct scmi_memlat_info *info;
> +	u32 cpucp_freq_method = CPUCP_DEFAULT_FREQ_METHOD;
> +	u32 cpucp_sample_ms = CPUCP_DEFAULT_SAMPLING_PERIOD_MS;
> +	int ret, i, j;
> +
> +	if (!handle)
> +		return -ENODEV;
> +
> +	ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_QCOM_GENERIC, &ph);
> +	if (IS_ERR(ops))
> +		return PTR_ERR(ops);
> +
> +	info = devm_kzalloc(&sdev->dev, sizeof(*info), GFP_KERNEL);

I'd add a local variable

	struct device *dev = &sdev->dev;
given how many uses of this you have in this function.

> +	if (!info)
> +		return -ENOMEM;
> +
> +	ret = process_scmi_memlat_of_node(sdev, info);
> +	if (ret)
> +		return ret;
> +
> +	info->ph = ph;
> +	info->ops = ops;
> +
> +	/* Configure common events ids */
As below.
> +	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 */
As below.
> +		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 */

I'd argue this and the above comment are clear from the function names
so add no benefit, but not that important if you want to keep them anyway.
Reasoning is that if a comment isn't providing more information it
is an opportunity for bit rot in the longer run and bloats the code.
Keep them for where they add more value.

> +			ret = configure_cpucp_mon(&sdev->dev, info, i, j);
> +			if (ret < 0)
> +				return ret;
> +		}
> +	}
...

> +}
> +
> +static int scmi_client_probe(struct scmi_device *sdev)
> +{
> +	return cpucp_memlat_init(sdev);
What is benefit of this wrapper? I'd just use cpucp_memlat_init as the probe
function.

> +}
> +
> +static const struct scmi_device_id scmi_id_table[] = {

Probably name this in a fashion related to the driver given
maybe we'll have a namespace clash in future with such
a generic name.

> +	{ SCMI_PROTOCOL_QCOM_GENERIC, "qcom-generic-ext" },
> +	{ },
No point in comma after a 'NULL' terminator like this.

> +};
> +MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> +
> +static struct scmi_driver qcom_scmi_client_drv = {
> +	.name		= "scmi-qcom-generic-ext-memlat",
> +	.probe		= scmi_client_probe,
> +	.id_table	= scmi_id_table,
> +};
> +module_scmi_driver(qcom_scmi_client_drv);
> +
> +MODULE_DESCRIPTION("QTI SCMI client driver");
> +MODULE_LICENSE("GPL");


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

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-10-07 18:06   ` Dmitry Baryshkov
@ 2024-10-22  7:13     ` Sibi Sankar
  2024-10-24 19:54       ` Dmitry Baryshkov
  0 siblings, 1 reply; 64+ messages in thread
From: Sibi Sankar @ 2024-10-22  7:13 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi



On 10/7/24 23:36, Dmitry Baryshkov wrote:
> On Mon, Oct 07, 2024 at 11:40:19AM GMT, Sibi Sankar wrote:
>> Document the various memory buses that can be monitored and scaled by
>> the memory latency governor hosted by the QCOM SCMI Generic Extension
>> Protocol v1.0.
>>
>> Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com>

Hey Dmitry,

Thanks for taking time to review the series!

>> ---
>>
>> v3:
>> * Restructure the bindings to mimic IMX [Christian]
>>
>>   .../bindings/firmware/arm,scmi.yaml           |   1 +
>>   .../bindings/firmware/qcom,scmi-memlat.yaml   | 246 ++++++++++++++++++
>>   .../dt-bindings/firmware/qcom,scmi-memlat.h   |  22 ++
>>   3 files changed, 269 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
>>   create mode 100644 include/dt-bindings/firmware/qcom,scmi-memlat.h
>>
>> diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
>> index 54d7d11bfed4..1d405f429168 100644
>> --- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
>> +++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
>> @@ -24,6 +24,7 @@ description: |
>>   
>>   anyOf:
>>     - $ref: /schemas/firmware/nxp,imx95-scmi.yaml
>> +  - $ref: /schemas/firmware/qcom,scmi-memlat.yaml
>>   
>>   properties:
>>     $nodename:
>> diff --git a/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml b/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
>> new file mode 100644
>> index 000000000000..0e8ea6dacd6a
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
>> @@ -0,0 +1,246 @@
>> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/firmware/qcom,scmi-memlat.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Qualcomm SCMI Memory Bus nodes
>> +
>> +maintainers:
>> +  - Sibi Sankar <quic_sibis@quicinc.com>
>> +
>> +description:
>> +  This binding describes the various memory buses that can be monitored and scaled
>> +  by memory latency governor running on the CPU Control Processor (SCMI controller).
>> +
>> +properties:
>> +  protocol@80:
>> +    $ref: '/schemas/firmware/arm,scmi.yaml#/$defs/protocol-node'
>> +    unevaluatedProperties: false
>> +
>> +    properties:
>> +      reg:
>> +        const: 0x80
>> +
>> +    patternProperties:
>> +      '^memory-[0-9]$':
>> +        type: object
>> +        unevaluatedProperties: false
>> +        description:
>> +          The list of all memory buses that can be monitored and scaled by the
>> +          memory latency governor running on the SCMI controller.
>> +
>> +        properties:
>> +          qcom,memory-type:
>> +            $ref: /schemas/types.yaml#/definitions/uint32
>> +            enum: [0, 1, 2]
>> +            description: |
>> +              Memory Bus Identifier
>> +              0 = QCOM_MEM_TYPE_DDR
>> +              1 = QCOM_MEM_TYPE_LLCC
>> +              2 = QCOM_MEM_TYPE_DDR_QOS
> 
> I'm sorry if this has been discussed and frowned upon, but can you
> squash memory type into device node?

I don't think anyone had any strong opinions on how the
nodes is to be named. We went with a generic node name since
it could accomodate multiple instances of llcc or ddr in the
future. We didn't want it be named ddr-0/ddr-1 and so on. So
I'll continue to stick with the current naming unless you have
a strong reason other than readability.

> 
> protocol@80 {
> 	ddr {
> 	};
> 
> 	llcc {
> 	};
> 
> 	ddr-qos {
> 	};
> };
> 
>> +
>> +          freq-table-hz:
>> +            items:
>> +              items:
>> +                - description: Minimum frequency of the memory bus in Hz
>> +                - description: Maximum frequency of the memory bus in Hz
> 
> Does it make sense for the DDR-QOS type? Can we hardcode those values
> and drop freq-table-hz from the DDR-QOS node?
> 
> Also, can we drop this completely by adding one extra OPP entry with the
> minimum memory bus frequency?

the map table doesn't necessarily list all the supported
frequencies. It was made that way so that the table is flexible
enough that it doesn't have to be changed a lot across platforms.
Hence a need for a separate property to list min/max freq.

> 
>> +
>> +        patternProperties:
>> +          '^monitor-[0-9]$':
>> +            type: object
>> +            unevaluatedProperties: false
>> +            description:
>> +              The list of all monitors detecting the memory latency bound workloads using
>> +              various counters.
>> +
>> +            properties:
>> +              qcom,compute-type:
>> +                description:
>> +                  Monitors of type compute perform bus dvfs based on a rudimentary CPU
>> +                  frequency to memory frequency map.
>> +                type: boolean
> 
> This seems to be redundant. If there is no qcom,ipm-ceil property, then
> it's qcom,compute-type, isn't it?

ack

> 
>> +
>> +              qcom,ipm-ceil:
>> +                $ref: /schemas/types.yaml#/definitions/uint32
>> +                description:
>> +                  Monitors having this property perform bus dvfs based on the same
>> +                  rudimentary table but the scaling is performed only if the calculated
>> +                  IPM (Instruction Per Misses) exceeds the given ceiling.
>> +
>> +              cpus:
>> +                $ref: /schemas/types.yaml#/definitions/phandle-array
>> +                description:
>> +                  Should be a list of phandles to CPU nodes (as described in
>> +                  Documentation/devicetree/bindings/arm/cpus.yaml).
> 
> Which CPU nodes? I see that the examples list all CPUs here. Do we
> really need them?

This observation is only valid for X1E where all the cpus have
identical freq charecteristics. Even with this case we need to
list them to handle cases where cpus gets disabled by the bootloader
on lower cored X1E parts i.e. we use this to figure out the actual
physical mask.

> 
>> +
>> +              operating-points-v2: true
>> +              opp-table:
>> +                type: object
>> +
>> +            required:
>> +              - cpus
>> +              - operating-points-v2
>> +
>> +            oneOf:
>> +              - required: [ 'qcom,compute-type' ]
>> +              - required: [ 'qcom,ipm-ceil' ]
>> +
>> +        required:
>> +          - qcom,memory-type
>> +          - freq-table-hz
>> +
>> +additionalProperties: true
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/firmware/qcom,scmi-memlat.h>
>> +
>> +    firmware {
>> +        scmi {
>> +            compatible = "arm,scmi";
>> +            mboxes = <&cpucp_mbox 0>, <&cpucp_mbox 2>;
>> +            mbox-names = "tx", "rx";
>> +            shmem = <&cpu_scp_lpri0>, <&cpu_scp_lpri1>;
>> +
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +
>> +            protocol@80 {
>> +                reg = <0x80>;
>> +
>> +                memory-0 {
>> +                    qcom,memory-type = <QCOM_MEM_TYPE_DDR>;
>> +                    freq-table-hz = /bits/ 64 <200000000 4224000000>;
>> +
>> +                    monitor-0 {
> 
> Hmm. Can we say that each memory type can have at most one IPM and one
> compute aka "passive" memlat monitor? Does it make sense to use them as
> node names and drop the extra monitor-M names?

Again this observation is valid only for X1E where the cpu freq
across cpu's are identical across clusters and is not true for
other mobile SoCs. So each memory can have more than 2 monitors
i.e. atleast one active/passibe monitor for each cluster.

> 
>> +                        qcom,ipm-ceil = <20000000>;
>> +                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
>> +                                &CPU8 &CPU9 &CPU10 &CPU11>;
> 
> Are CPU lists different between monitors? Can they be different? Can
> they be different between different memory types?

same explanation.

> 
>> +                        operating-points-v2 = <&memory0_monitor0_opp_table>;
>> +
>> +                        memory0_monitor0_opp_table: opp-table {
> 
> sensible names are better:

I think I just picked these names up from a cpufreq table upstream.

> 
> ddr_ipm_opp_table: opp-table {
> };
> 
>> +                            compatible = "operating-points-v2";
>> +
>> +                            opp-999000000 {
>> +                                opp-hz = /bits/ 64 <999000000 547000000>;
>> +                            };
>> +
>> +                            opp-1440000000 {
>> +                                opp-hz = /bits/ 64 <1440000000 768000000>;
>> +                            };
>> +
>> +                            opp-1671000000 {
>> +                                opp-hz = /bits/ 64 <1671000000 1555000000>;
>> +                            };
>> +
>> +                            opp-2189000000 {
>> +                                opp-hz = /bits/ 64 <2189000000 2092000000>;
>> +                            };
>> +
>> +                            opp-2516000000 {
>> +                                opp-hz = /bits/ 64 <2516000000 3187000000>;
>> +                            };
>> +
>> +                            opp-3860000000 {
>> +                                opp-hz = /bits/ 64 <3860000000 4224000000>;
>> +                            };
>> +                        };
>> +                    };
>> +
>> +                    monitor-1 {
>> +                        qcom,compute-type;
>> +                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
>> +                                &CPU8 &CPU9 &CPU10 &CPU11>;
>> +                        operating-points-v2 = <&memory0_monitor1_opp_table>;
>> +
>> +                        memory0_monitor1_opp_table: opp-table {
>> +                            compatible = "operating-points-v2";
>> +
>> +                            opp-1440000000 {
>> +                                    opp-hz = /bits/ 64 <1440000000 200000000>;
>> +                            };
>> +
>> +                            opp-2189000000 {
>> +                                    opp-hz = /bits/ 64 <2189000000 768000000>;
>> +                            };
>> +
>> +                            opp-2516000000 {
>> +                                    opp-hz = /bits/ 64 <2516000000 1555000000>;
>> +                            };
>> +
>> +                            opp-3860000000 {
>> +                                    opp-hz = /bits/ 64 <3860000000 4224000000>;
>> +                            };
>> +                        };
>> +                    };
>> +                };
>> +
>> +                memory-1 {
>> +                    qcom,memory-type = <QCOM_MEM_TYPE_LLCC>;
>> +                    freq-table-hz = /bits/ 64 <300000000 1067000000>;
>> +
>> +                    monitor-0 {
>> +                        qcom,ipm-ceil = <20000000>;
>> +                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
>> +                                &CPU8 &CPU9 &CPU10 &CPU11>;
>> +                        operating-points-v2 = <&memory1_monitor0_opp_table>;
>> +
>> +                        memory1_monitor0_opp_table: opp-table {
>> +                            compatible = "operating-points-v2";
>> +
>> +                            opp-999000000 {
>> +                                opp-hz = /bits/ 64 <999000000 300000000>;
>> +                            };
>> +
>> +                            opp-1440000000 {
>> +                                opp-hz = /bits/ 64 <1440000000 466000000>;
>> +                            };
>> +
>> +                            opp-1671000000 {
>> +                                opp-hz = /bits/ 64 <1671000000 600000000>;
>> +                            };
>> +
>> +                            opp-2189000000 {
>> +                                opp-hz = /bits/ 64 <2189000000 806000000>;
>> +                            };
>> +
>> +                            opp-2516000000 {
>> +                                opp-hz = /bits/ 64 <2516000000 933000000>;
>> +                            };
>> +
>> +                            opp-3860000000 {
>> +                                opp-hz = /bits/ 64 <3860000000 1066000000>;
>> +                            };
>> +                        };
>> +                    };
>> +                };
>> +
>> +                memory-2 {
>> +                    qcom,memory-type = <QCOM_MEM_TYPE_DDR_QOS>;
>> +                    freq-table-hz = /bits/ 64 <QCOM_DDR_LEVEL_AUTO QCOM_DDR_LEVEL_PERF>;
> 
> This is definitely not 'frequency of the memory bys in Hz'

ack, will change it.

-Sibi

> 
>> +
>> +                    monitor-0 {
>> +                        qcom,ipm-ceil = <20000000>;
>> +                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
>> +                                &CPU8 &CPU9 &CPU10 &CPU11>;
>> +                        operating-points-v2 = <&memory2_monitor0_opp_table>;
>> +
>> +                        memory2_monitor0_opp_table: opp-table {
>> +                            compatible = "operating-points-v2";
>> +
>> +                            opp-2189000000 {
>> +                                opp-hz = /bits/ 64 <2189000000>;
>> +                                opp-level = <QCOM_DDR_LEVEL_AUTO>;
>> +                            };
>> +
>> +                            opp-3860000000 {
>> +                                opp-hz = /bits/ 64 <3860000000>;
>> +                                opp-level = <QCOM_DDR_LEVEL_PERF>;
>> +                            };
>> +                        };
>> +                    };
>> +                };
>> +            };
>> +        };
>> +    };
>> diff --git a/include/dt-bindings/firmware/qcom,scmi-memlat.h b/include/dt-bindings/firmware/qcom,scmi-memlat.h
>> new file mode 100644
>> index 000000000000..7ae8d8d5623b
>> --- /dev/null
>> +++ b/include/dt-bindings/firmware/qcom,scmi-memlat.h
>> @@ -0,0 +1,22 @@
>> +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
>> +/*
>> + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
>> + */
>> +#ifndef __DT_BINDINGS_QCOM_SCMI_VENDOR_H
>> +#define __DT_BINDINGS_QCOM_SCMI_VENDOR
>> +
>> +/* Memory IDs */
>> +#define QCOM_MEM_TYPE_DDR	0x0
>> +#define QCOM_MEM_TYPE_LLCC	0x1
>> +#define QCOM_MEM_TYPE_DDR_QOS	0x2
>> +
>> +/*
>> + * QCOM_MEM_TYPE_DDR_QOS supports the following states.
>> + *
>> + * %QCOM_DDR_LEVEL_AUTO:	DDR operates with LPM enabled
>> + * %QCOM_DDR_LEVEL_PERF:	DDR operates with LPM disabled
>> + */
>> +#define QCOM_DDR_LEVEL_AUTO	0x0
>> +#define QCOM_DDR_LEVEL_PERF	0x1
>> +
>> +#endif /* __DT_BINDINGS_QCOM_SCMI_VENDOR_H */
>> -- 
>> 2.34.1
>>
> 

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

* Re: [PATCH V4 3/5] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions
  2024-10-07 18:13   ` Dmitry Baryshkov
@ 2024-10-22  7:18     ` Sibi Sankar
  0 siblings, 0 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-10-22  7:18 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid



On 10/7/24 23:43, Dmitry Baryshkov wrote:
> On Mon, Oct 07, 2024 at 11:40:21AM GMT, Sibi Sankar wrote:
>> 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 <quic_sibis@quicinc.com>
>> Reviewed-by: Cristian Marussi <cristian.marussi@arm.com>

Thanks again for spending time to review the series!

>> ---
>>
>> v3:
>> * 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]
>>
>>   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  | 184 ++++++++++++++++++
>>   include/linux/scmi_qcom_protocol.h            |  39 ++++
>>   6 files changed, 242 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 dabd874641d0..73128442d97b 100644
>> --- a/drivers/firmware/arm_scmi/Kconfig
>> +++ b/drivers/firmware/arm_scmi/Kconfig
>> @@ -71,6 +71,7 @@ config ARM_SCMI_DEBUG_COUNTERS
>>   
>>   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 9ac81adff567..58cf4d656cbb 100644
>> --- a/drivers/firmware/arm_scmi/Makefile
>> +++ b/drivers/firmware/arm_scmi/Makefile
>> @@ -12,6 +12,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..f7cd949161df
>> --- /dev/null
>> +++ b/drivers/firmware/arm_scmi/vendors/qcom/qcom-generic-ext.c
>> @@ -0,0 +1,184 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2024, 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 implementation version set to 0x20000. The PROTOCOL_VERSION command
>> + * returns version 1.0.
>> + *
>> + * @QCOM_SCMI_SET_PARAM: message_id: 0x10 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: message_id: 0x11 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: message_id: 0x12 is used to start the activity performed by
>> + *                            the algo_str.
>> + * @QCOM_SCMI_STOP_ACTIVITY: message_id: 0x13 is used to stop a pre-existing activity
>> + *                           performed by the algo_str.
> 
> Drop message_id's from the definitions. They duplicate enum values.

ack

> 
>> + */
>> +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_set_param(const struct scmi_protocol_handle *ph, void *buf, size_t buf_len,
>> +			       u64 algo_str, u32 param_id)
>> +{
>> +	struct scmi_xfer *t;
>> +	struct qcom_scmi_msg *msg;
>> +	int ret;
>> +
>> +	ret = ph->xops->xfer_get_init(ph, QCOM_SCMI_SET_PARAM, buf_len + sizeof(*msg), 0, &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, t->tx.len - sizeof(*msg));
>> +
>> +	ret = ph->xops->do_xfer(ph, t);
>> +	ph->xops->xfer_put(ph, t);
>> +
>> +	return ret;
>> +}
>> +
>> +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)
>> +{
>> +	struct scmi_xfer *t;
>> +	struct qcom_scmi_msg *msg;
>> +	int ret;
>> +
>> +	ret = ph->xops->xfer_get_init(ph, QCOM_SCMI_GET_PARAM, 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, t->tx.len - sizeof(*msg));
> 
> Isn't it buf_len?

> 
>> +
>> +	ret = ph->xops->do_xfer(ph, t);
>> +	memcpy(buf, t->rx.buf, t->rx.len);
>> +	ph->xops->xfer_put(ph, t);
>> +
>> +	return ret;
>> +}
>> +
>> +static int qcom_scmi_start_activity(const struct scmi_protocol_handle *ph, void *buf,
>> +				    size_t buf_len, u64 algo_str, u32 param_id)
>> +{
>> +	struct scmi_xfer *t;
>> +	struct qcom_scmi_msg *msg;
>> +	int ret;
>> +
>> +	ret = ph->xops->xfer_get_init(ph, QCOM_SCMI_START_ACTIVITY, buf_len + sizeof(*msg), 0, &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, t->tx.len - sizeof(*msg));
> 
> Isn't it buf_len?
> 
>> +
>> +	ret = ph->xops->do_xfer(ph, t);
>> +	ph->xops->xfer_put(ph, t);
>> +
>> +	return ret;
>> +}
>> +
>> +static int qcom_scmi_stop_activity(const struct scmi_protocol_handle *ph, void *buf,
>> +				   size_t buf_len, u64 algo_str, u32 param_id)
>> +{
>> +	struct scmi_xfer *t;
>> +	struct qcom_scmi_msg *msg;
>> +	int ret;
>> +
>> +	ret = ph->xops->xfer_get_init(ph, QCOM_SCMI_STOP_ACTIVITY, buf_len + sizeof(*msg), 0, &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, t->tx.len - sizeof(*msg));
> 
> Isn't it buf_len?

ack

> 
>> +
>> +	ret = ph->xops->do_xfer(ph, t);
>> +	ph->xops->xfer_put(ph, t);
> 
> Could you please extract a common helper that handles xfer for you?
> Seeing the same code 4 times is 3 times too much in my opinion.

Yup, you have been asking for this multiple times already.
Will get it addressed in the next re-spin.

> 
>> +
>> +	return ret;
>> +}
>> +
>> +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)
>> +{
>> +	u32 version;
>> +
>> +	ph->xops->version_get(ph, &version);
>> +
>> +	dev_info(ph->dev, "QCOM Generic Vendor Version %d.%d\n",
>> +		 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
> 
> dev_dbg, please.

Few protocols had them marked as info. Sure, I'll convert it to dbg
instead.

> 
>> +
>> +	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",
>> +	.impl_ver = 0x20000,
>> +};
>> +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..8f82c42e566d
>> --- /dev/null
>> +++ b/include/linux/scmi_qcom_protocol.h
>> @@ -0,0 +1,39 @@
>> +/* 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/bitfield.h>
>> +#include <linux/device.h>
> 
> It doesn't look like you need those two headers.

Thanks for catching this.

-Sibi

> 
>> +#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	[flat|nested] 64+ messages in thread

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-10-08 12:11       ` Krzysztof Kozlowski
@ 2024-10-22  7:25         ` Sibi Sankar
  2024-10-24 13:29           ` Krzysztof Kozlowski
  2024-10-24 19:48           ` Dmitry Baryshkov
  0 siblings, 2 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-10-22  7:25 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Dmitry Baryshkov
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi



On 10/8/24 17:41, Krzysztof Kozlowski wrote:
> On 08/10/2024 14:10, Dmitry Baryshkov wrote:
>> On Tue, Oct 08, 2024 at 08:49:27AM GMT, Krzysztof Kozlowski wrote:
>>> On Mon, Oct 07, 2024 at 11:40:19AM +0530, Sibi Sankar wrote:
>>>> +/*
>>>> + * QCOM_MEM_TYPE_DDR_QOS supports the following states.
>>>> + *
>>>> + * %QCOM_DDR_LEVEL_AUTO:	DDR operates with LPM enabled
>>>> + * %QCOM_DDR_LEVEL_PERF:	DDR operates with LPM disabled
>>>> + */
>>>> +#define QCOM_DDR_LEVEL_AUTO	0x0
>>>> +#define QCOM_DDR_LEVEL_PERF	0x1
>>>
>>> I could not find any driver using these. Can you point me to usage in
>>> the drivers?
>>
>> It's well hidden. These are the raw values used for DDR_QOS memory.
> 
> So not a binding? Then these should be dropped.

I am not sure why the term "well hidden" was even considered :(
The driver just reads them from dt and passes them along. If you
want the dt to list magic numbers 0/1 instead I can do that as well.

-Sibi

> 
> Best regards,
> Krzysztof
> 

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-10-10 12:18   ` Jonathan Cameron
@ 2024-10-22  7:31     ` Sibi Sankar
  0 siblings, 0 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-10-22  7:31 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid



On 10/10/24 17:48, Jonathan Cameron wrote:
> On Mon, 7 Oct 2024 11:40:22 +0530
> Sibi Sankar <quic_sibis@quicinc.com> wrote:
> 
>> Introduce a 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).
>>
>> 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 <quic_sibis@quicinc.com>
> I was curious. A few comments from a quick read through.
> 
> Jonathan

Hey Jonathan,

Thanks for taking time to review the series!

Will get them all addressed in the next re-spin.

-Sibi

> 
>> diff --git a/drivers/soc/qcom/qcom_scmi_memlat_client.c b/drivers/soc/qcom/qcom_scmi_memlat_client.c
>> new file mode 100644
>> index 000000000000..05198bf1f7ec
>> --- /dev/null
>> +++ b/drivers/soc/qcom/qcom_scmi_memlat_client.c
> 
>> +static int populate_cluster_info(u32 *cluster_info)
>> +{
>> +	char name[MAX_NAME_LEN];
>> +	int i = 0;
>> +
>> +	struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
>> +	if (!cn)
>> +		return -ENODEV;
>> +
>> +	struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
>> +	if (!map)
>> +		return -ENODEV;
>> +
>> +	do {
> while(1) {
> } >> +		snprintf(name, sizeof(name), "cluster%d", i);
>> +		struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
>> +		if (!c)
>> +			break;
>> +
>> +		*(cluster_info + i) = of_get_child_count(c);
>> +		i++;
>> +	} while (1);
>> +
>> +	return 0;
>> +}
>> +
> tic struct cpufreq_memfreq_map *init_cpufreq_memfreq_map(struct device *dev,
>> +							    struct scmi_memory_info *memory,
>> +							    struct device_node *of_node,
>> +							    u32 *cnt)
>> +{
>> +	struct device_node *tbl_np __free(device_node), *opp_np __free(device_node);
>> +	struct cpufreq_memfreq_map *tbl;
>> +	int ret, i = 0;
>> +	u32 level, len;
>> +	u64 rate;
>> +
>> +	tbl_np = of_parse_phandle(of_node, "operating-points-v2", 0);
>> +	if (!tbl_np)
> 
> This will call the free on the uninitialzed opp_np above.
> Note this sort of path is why the constructor and destructor should always
> be together in the code.
> 
>> +		return ERR_PTR(-ENODEV);
>> +
>> +	len = min(of_get_available_child_count(tbl_np), MAX_MAP_ENTRIES);
>> +	if (len == 0)
>> +		return ERR_PTR(-ENODEV);
>> +
>> +	tbl = devm_kzalloc(dev, (len + 1) * sizeof(struct cpufreq_memfreq_map),
>> +			   GFP_KERNEL);
>> +	if (!tbl)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	for_each_available_child_of_node(tbl_np, opp_np) {
> 
> Why not scoped variant which will also solve the lifetime issue above.
> 
>> +		ret = of_property_read_u64_index(opp_np, "opp-hz", 0, &rate);
>> +		if (ret < 0)
>> +			return ERR_PTR(ret);
>> +
>> +		tbl[i].cpufreq_mhz = rate / HZ_PER_MHZ;
>> +
>> +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
>> +			ret = of_property_read_u64_index(opp_np, "opp-hz", 1, &rate);
>> +			if (ret < 0)
>> +				return ERR_PTR(ret);
>> +
>> +			tbl[i].memfreq_khz = rate / HZ_PER_KHZ;
>> +		} else {
>> +			ret = of_property_read_u32(opp_np, "opp-level", &level);
>> +			if (ret < 0)
>> +				return ERR_PTR(ret);
>> +
>> +			tbl[i].memfreq_khz = level;
>> +		}
>> +
>> +		dev_dbg(dev, "Entry%d CPU:%u, Mem:%u\n", i, tbl[i].cpufreq_mhz, tbl[i].memfreq_khz);
>> +		i++;
>> +	}
>> +	*cnt = len;
>> +
>> +	return tbl;
>> +}
>> +
>> +static int process_scmi_memlat_of_node(struct scmi_device *sdev, struct scmi_memlat_info *info)
>> +{
>> +	struct scmi_monitor_info *monitor;
>> +	struct scmi_memory_info *memory;
>> +	char name[MAX_NAME_LEN];
>> +	u64 memfreq[2];
>> +	int ret;
>> +
>> +	ret = populate_cluster_info(info->cluster_info);
>> +	if (ret < 0) {
>> +		dev_err_probe(&sdev->dev, ret, "failed to populate cluster info\n");
>> +		goto err;
> putting a node you never got?
> 		return dev_err_probe()
> 
> 
>> +	}
>> +
>> +	of_node_get(sdev->dev.of_node);
> Maybe use __free(device_node) here so you can do early returns on error.
> Will need a local variable for the return of of_node_get, but that would
> be nice anyway to simplify some parameters belwo.
> 
>> +	do {
> Might as well do while(1) {
> }
>> +		snprintf(name, sizeof(name), "memory-%d", info->memory_cnt);
>> +		struct device_node *memory_np __free(device_node) =
>> +			of_find_node_by_name(sdev->dev.of_node, name);
>> +
>> +		if (!memory_np)
>> +			break;
>> +
>> +		if (info->memory_cnt >= MAX_MEMORY_TYPES)
>> +			return dev_err_probe(&sdev->dev, -EINVAL,
>> +					     "failed to parse unsupported memory type\n");
>> +
>> +		memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
>> +		if (!memory) {
>> +			ret = -ENOMEM;
>> +			goto err;
>> +		}
>> +
>> +		ret = of_property_read_u32(memory_np, "qcom,memory-type", &memory->hw_type);
>> +		if (ret) {
>> +			dev_err_probe(&sdev->dev, ret, "failed to read memory type\n");
>> +			goto err;
>> +		}
>> +
>> +		ret = of_property_read_u64_array(memory_np, "freq-table-hz", memfreq, 2);
>> +		if (ret && (ret != -EINVAL)) {
>> +			dev_err_probe(&sdev->dev, ret, "failed to read min/max freq\n");
>> +			goto err;
>> +		}
>> +
>> +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
>> +			memory->min_freq = memfreq[0] / HZ_PER_KHZ;
>> +			memory->max_freq = memfreq[1] / HZ_PER_KHZ;
>> +		} else {
>> +			memory->min_freq = memfreq[0];
>> +			memory->max_freq = memfreq[1];
>> +		}
>> +		info->memory[info->memory_cnt++] = memory;
>> +
>> +		do {
>> +			snprintf(name, sizeof(name), "monitor-%d", memory->monitor_cnt);
>> +			struct device_node *monitor_np __free(device_node) =
>> +				of_get_child_by_name(memory_np, name);
>> +
>> +			if (!monitor_np)
>> +				break;
>> +
>> +			if (memory->monitor_cnt >= MAX_MONITOR_CNT)
>> +				return dev_err_probe(&sdev->dev, -EINVAL,
>> +						     "failed to parse unsupported monitor\n");
>> +
>> +			monitor = devm_kzalloc(&sdev->dev, sizeof(*monitor), GFP_KERNEL);
>> +			if (!monitor) {
>> +				ret = -ENOMEM;
>> +				goto err;
>> +			}
>> +
>> +			monitor->mon_type = of_property_read_bool(monitor_np, "qcom,compute-type");
>> +			if (!monitor->mon_type) {
>> +				ret = of_property_read_u32(monitor_np, "qcom,ipm-ceil",
>> +							   &monitor->ipm_ceil);
>> +				if (ret) {
>> +					dev_err_probe(&sdev->dev, ret,
>> +						      "failed to read IPM ceiling\n");
>> +					goto err;
>> +				}
>> +			}
>> +
>> +			/*
>> +			 * Variants of the SoC having reduced number of cpus operate
>> +			 * with the same number of logical cpus but the physical
>> +			 * cpu disabled will differ between parts. Calculate the
>> +			 * physical cpu number using cluster information instead.
>> +			 */
>> +			populate_physical_mask(monitor_np, &monitor->mask, info->cluster_info);
>> +
>> +			monitor->freq_map = init_cpufreq_memfreq_map(&sdev->dev, memory, monitor_np,
>> +								     &monitor->freq_map_len);
>> +			if (IS_ERR(monitor->freq_map)) {
>> +				dev_err_probe(&sdev->dev, PTR_ERR(monitor->freq_map),
>> +					      "failed to populate cpufreq-memfreq map\n");
>> +				goto err;
>> +			}
>> +
>> +			strscpy(monitor->mon_name, name, sizeof(monitor->mon_name));
>> +			monitor->mon_idx = memory->monitor_cnt;
>> +
>> +			memory->monitor[memory->monitor_cnt++] = monitor;
>> +		} while (1);
>> +
>> +		if (!memory->monitor_cnt) {
>> +			ret = -EINVAL;
>> +			dev_err_probe(&sdev->dev, ret, "failed to find monitor nodes\n");
>> +			goto err;
>> +		}
>> +	} while (1);
>> +
>> +	if (!info->memory_cnt) {
>> +		ret = -EINVAL;
>> +		dev_err_probe(&sdev->dev, ret, "failed to find memory nodes\n");
>> +	}
>> +
>> +err:
>> +	of_node_put(sdev->dev.of_node);
>> +
>> +	return ret;
>> +}
> 
> 
>> +
>> +static int cpucp_memlat_init(struct scmi_device *sdev)
>> +{
>> +	const struct scmi_handle *handle = sdev->handle;
>> +	const struct qcom_generic_ext_ops *ops;
>> +	struct scmi_protocol_handle *ph;
>> +	struct scmi_memlat_info *info;
>> +	u32 cpucp_freq_method = CPUCP_DEFAULT_FREQ_METHOD;
>> +	u32 cpucp_sample_ms = CPUCP_DEFAULT_SAMPLING_PERIOD_MS;
>> +	int ret, i, j;
>> +
>> +	if (!handle)
>> +		return -ENODEV;
>> +
>> +	ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_QCOM_GENERIC, &ph);
>> +	if (IS_ERR(ops))
>> +		return PTR_ERR(ops);
>> +
>> +	info = devm_kzalloc(&sdev->dev, sizeof(*info), GFP_KERNEL);
> 
> I'd add a local variable
> 
> 	struct device *dev = &sdev->dev;
> given how many uses of this you have in this function.
> 
>> +	if (!info)
>> +		return -ENOMEM;
>> +
>> +	ret = process_scmi_memlat_of_node(sdev, info);
>> +	if (ret)
>> +		return ret;
>> +
>> +	info->ph = ph;
>> +	info->ops = ops;
>> +
>> +	/* Configure common events ids */
> As below.
>> +	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 */
> As below.
>> +		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 */
> 
> I'd argue this and the above comment are clear from the function names
> so add no benefit, but not that important if you want to keep them anyway.
> Reasoning is that if a comment isn't providing more information it
> is an opportunity for bit rot in the longer run and bloats the code.
> Keep them for where they add more value.
> 
>> +			ret = configure_cpucp_mon(&sdev->dev, info, i, j);
>> +			if (ret < 0)
>> +				return ret;
>> +		}
>> +	}
> ...
> 
>> +}
>> +
>> +static int scmi_client_probe(struct scmi_device *sdev)
>> +{
>> +	return cpucp_memlat_init(sdev);
> What is benefit of this wrapper? I'd just use cpucp_memlat_init as the probe
> function.
> 
>> +}
>> +
>> +static const struct scmi_device_id scmi_id_table[] = {
> 
> Probably name this in a fashion related to the driver given
> maybe we'll have a namespace clash in future with such
> a generic name.
> 
>> +	{ SCMI_PROTOCOL_QCOM_GENERIC, "qcom-generic-ext" },
>> +	{ },
> No point in comma after a 'NULL' terminator like this.
> 
>> +};
>> +MODULE_DEVICE_TABLE(scmi, scmi_id_table);
>> +
>> +static struct scmi_driver qcom_scmi_client_drv = {
>> +	.name		= "scmi-qcom-generic-ext-memlat",
>> +	.probe		= scmi_client_probe,
>> +	.id_table	= scmi_id_table,
>> +};
>> +module_scmi_driver(qcom_scmi_client_drv);
>> +
>> +MODULE_DESCRIPTION("QTI SCMI client driver");
>> +MODULE_LICENSE("GPL");
> 

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-10-07 17:57   ` Dmitry Baryshkov
@ 2024-10-22  8:18     ` Sibi Sankar
  2024-10-26 18:16       ` Dmitry Baryshkov
  2024-10-28  8:30       ` Cristian Marussi
  0 siblings, 2 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-10-22  8:18 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid



On 10/7/24 23:27, Dmitry Baryshkov wrote:
> On Mon, Oct 07, 2024 at 11:40:22AM GMT, Sibi Sankar wrote:
>> Introduce a 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).
> 
> This sounds like a devfreq implementation. Please provide a reason why
> it doesn't use existing API (even if to export the information to the
> userspace).

IIRC, you asked the same question when the RFC version of it
was posted and I replied to it back then.

https://lore.kernel.org/lkml/0adaa065-3883-ebfe-8259-05ebdbd821eb@quicinc.com/

The series does not yet export any information to userspace yet
and when it does get added in the future, it would have no way
of populating governor node with the current way devfreq framework
is structured. Since this series is all about just enabling basic
support, I guess what you ask can be accomodated when we do start
exporting this info to userspace.

> 
>>
>> 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 <quic_sibis@quicinc.com>
>> ---
>>
>> v3:
>> * 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]
>>
>>   drivers/soc/qcom/Kconfig                   |  12 +
>>   drivers/soc/qcom/Makefile                  |   1 +
>>   drivers/soc/qcom/qcom_scmi_memlat_client.c | 569 +++++++++++++++++++++
>>   3 files changed, 582 insertions(+)
>>   create mode 100644 drivers/soc/qcom/qcom_scmi_memlat_client.c
>>
>> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
>> index 74b9121240f8..1b6dd40d69ea 100644
>> --- a/drivers/soc/qcom/Kconfig
>> +++ b/drivers/soc/qcom/Kconfig
>> @@ -295,4 +295,16 @@ config QCOM_PBS
>>   	  This module provides the APIs to the client drivers that wants to send the
>>   	  PBS trigger event to the PBS RAM.
>>   
>> +config QCOM_SCMI_MEMLAT_CLIENT
>> +	tristate "Qualcomm Technologies Inc. SCMI client driver"
>> +	depends on QCOM_SCMI_GENERIC_EXT || COMPILE_TEST
>> +	help
>> +	  This driver uses the MEMLAT (memory latency) algorithm string
>> +	  hosted on QCOM SCMI Vendor Protocol to detect memory latency
> 
> How can it use the string to detect workloads? Most likely you mean something like "uses memlat extensions".
> Also s/QCOM/Qualcomm/ in the help text.

The generic vendor protocol extension works by associating algorithms to
strings. But like you said it can be re-worded to avoid confusion.

> 
>> +	  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.
>> +
>>   endmenu
>> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
>> index acbca2ab5cc2..28549bb141bc 100644
>> --- a/drivers/soc/qcom/Makefile
>> +++ b/drivers/soc/qcom/Makefile
>> @@ -36,6 +36,7 @@ obj-$(CONFIG_QCOM_APR) += apr.o
>>   obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o
>>   obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) +=	kryo-l2-accessors.o
>>   obj-$(CONFIG_QCOM_ICC_BWMON)	+= icc-bwmon.o
>> +obj-$(CONFIG_QCOM_SCMI_MEMLAT_CLIENT)	+= qcom_scmi_memlat_client.o
>>   qcom_ice-objs			+= ice.o
>>   obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE)	+= qcom_ice.o
>>   obj-$(CONFIG_QCOM_PBS) +=	qcom-pbs.o
>> diff --git a/drivers/soc/qcom/qcom_scmi_memlat_client.c b/drivers/soc/qcom/qcom_scmi_memlat_client.c
>> new file mode 100644
>> index 000000000000..05198bf1f7ec
>> --- /dev/null
>> +++ b/drivers/soc/qcom/qcom_scmi_memlat_client.c
>> @@ -0,0 +1,569 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
>> + */
>> +
>> +#include <linux/cpu.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/scmi_protocol.h>
>> +#include <linux/scmi_qcom_protocol.h>
>> +#include <linux/units.h>
>> +#include <dt-bindings/firmware/qcom,scmi-memlat.h>
>> +
>> +#define MEMLAT_ALGO_STR				0x4D454D4C4154 /* MEMLAT */
>> +#define INVALID_IDX				0xff
>> +#define MAX_MEMORY_TYPES			3
>> +#define MAX_MONITOR_CNT				4
>> +#define MAX_NAME_LEN				20
>> +#define MAX_MAP_ENTRIES				7
>> +#define CPUCP_DEFAULT_SAMPLING_PERIOD_MS	4
>> +#define CPUCP_DEFAULT_FREQ_METHOD		1
>> +
>> +/**
>> + * 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_SAMPLE_MS = 31,
>> +	MEMLAT_MON_FREQ_MAP,
>> +	MEMLAT_SET_MIN_FREQ,
>> +	MEMLAT_SET_MAX_FREQ,
>> +	MEMLAT_START_TIMER = 36,
>> +	MEMLAT_STOP_TIMER,
>> +	MEMLAT_SET_EFFECTIVE_FREQ_METHOD = 39,
>> +};
>> +
>> +struct map_table {
>> +	u16 v1;
>> +	u16 v2;
> 
> Huh? Why can't it be cpufreq and memfreq with some suffix?

ack

> 
>> +};
>> +
>> +struct map_param_msg {
>> +	u32 hw_type;
>> +	u32 mon_idx;
>> +	u32 nr_rows;
>> +	struct 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
>> +};
>> +
>> +#define EV_CPU_CYCLES		0
>> +#define EV_INST_RETIRED		2
>> +#define EV_L2_D_RFILL		5
>> +
>> +struct ev_map_msg {
>> +	u32 num_evs;
>> +	u32 hw_type;
>> +	u32 cid[NUM_COMMON_EVS];
>> +};
>> +
>> +struct cpufreq_memfreq_map {
>> +	unsigned int cpufreq_mhz;
>> +	unsigned int memfreq_khz;
>> +};
>> +
>> +struct scmi_monitor_info {
>> +	struct cpufreq_memfreq_map *freq_map;
>> +	char mon_name[MAX_NAME_LEN];
>> +	u32 mon_idx;
>> +	u32 mon_type;
>> +	u32 ipm_ceil;
>> +	u32 mask;
>> +	u32 freq_map_len;
>> +};
>> +
>> +struct scmi_memory_info {
>> +	struct scmi_monitor_info *monitor[MAX_MONITOR_CNT];
>> +	u32 hw_type;
>> +	int monitor_cnt;
>> +	u32 min_freq;
>> +	u32 max_freq;
>> +};
>> +
>> +struct scmi_memlat_info {
>> +	struct scmi_protocol_handle *ph;
>> +	const struct qcom_generic_ext_ops *ops;
>> +	struct scmi_memory_info *memory[MAX_MEMORY_TYPES];
>> +	u32 cluster_info[NR_CPUS];
>> +	int memory_cnt;
>> +};
>> +
>> +static int populate_cluster_info(u32 *cluster_info)
>> +{
>> +	char name[MAX_NAME_LEN];
>> +	int i = 0;
>> +
>> +	struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
>> +	if (!cn)
>> +		return -ENODEV;
>> +
>> +	struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
>> +	if (!map)
>> +		return -ENODEV;
>> +
>> +	do {
>> +		snprintf(name, sizeof(name), "cluster%d", i);
>> +		struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
>> +		if (!c)
>> +			break;
>> +
>> +		*(cluster_info + i) = of_get_child_count(c);
>> +		i++;
>> +	} while (1);
> 
> Can you use existing API from drivers/base/arch_topology.c? If not, can
> it be extended to support your usecase?

ack. But I'm pretty sure it's going to take a while for reaching such
an agreement so I'll drop this feature during the next re-spin.

> 
>> +
>> +	return 0;
>> +}
>> +
>> +static void populate_physical_mask(struct device_node *np, u32 *mask, u32 *cluster_info)
>> +{
>> +	struct device_node *dev_phandle __free(device_node);
>> +	int cpu, i = 0, physical_id;
>> +
>> +	do {
>> +		dev_phandle = of_parse_phandle(np, "cpus", i++);
>> +		cpu = of_cpu_node_to_id(dev_phandle);
>> +		if (cpu != -ENODEV) {
>> +			physical_id = topology_core_id(cpu);
>> +			for (int j = 0; j < topology_cluster_id(cpu); j++)
>> +				physical_id += *(cluster_info + j);
>> +			*mask |= BIT(physical_id);
>> +		}
>> +	} while (dev_phandle);
>> +}
>> +
>> +static struct cpufreq_memfreq_map *init_cpufreq_memfreq_map(struct device *dev,
>> +							    struct scmi_memory_info *memory,
>> +							    struct device_node *of_node,
>> +							    u32 *cnt)
>> +{
>> +	struct device_node *tbl_np __free(device_node), *opp_np __free(device_node);
>> +	struct cpufreq_memfreq_map *tbl;
>> +	int ret, i = 0;
>> +	u32 level, len;
>> +	u64 rate;
>> +
>> +	tbl_np = of_parse_phandle(of_node, "operating-points-v2", 0);
> 
> Please use existing API to parse OPP tables or document a reason why it
> can't be used.

Thanks, I had them documented as opens in the coverletter. Dropped them
since no one had any comments on it during V3. Will add them as comments
to this driver instead.

https://lore.kernel.org/lkml/20240702191440.2161623-1-quic_sibis@quicinc.com/

re-copying things again:
opp-tables are used but they don't get to be added to the scmi device
(thus we rely on a lot of manual parsing) because the memlat client 
driver doesn't vote on these resources clocks/interconnects/power-domain
from the kernel and some of the resources aren't modeled in the first
place like DDR_QOS.


> 
>> +	if (!tbl_np)
>> +		return ERR_PTR(-ENODEV);
>> +
>> +	len = min(of_get_available_child_count(tbl_np), MAX_MAP_ENTRIES);
>> +	if (len == 0)
>> +		return ERR_PTR(-ENODEV);
>> +
>> +	tbl = devm_kzalloc(dev, (len + 1) * sizeof(struct cpufreq_memfreq_map),
>> +			   GFP_KERNEL);
>> +	if (!tbl)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	for_each_available_child_of_node(tbl_np, opp_np) {
>> +		ret = of_property_read_u64_index(opp_np, "opp-hz", 0, &rate);
>> +		if (ret < 0)
>> +			return ERR_PTR(ret);
>> +
>> +		tbl[i].cpufreq_mhz = rate / HZ_PER_MHZ;
>> +
>> +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
>> +			ret = of_property_read_u64_index(opp_np, "opp-hz", 1, &rate);
>> +			if (ret < 0)
>> +				return ERR_PTR(ret);
>> +
>> +			tbl[i].memfreq_khz = rate / HZ_PER_KHZ;
>> +		} else {
>> +			ret = of_property_read_u32(opp_np, "opp-level", &level);
>> +			if (ret < 0)
>> +				return ERR_PTR(ret);
>> +
>> +			tbl[i].memfreq_khz = level;
>> +		}
>> +
>> +		dev_dbg(dev, "Entry%d CPU:%u, Mem:%u\n", i, tbl[i].cpufreq_mhz, tbl[i].memfreq_khz);
>> +		i++;
>> +	}
>> +	*cnt = len;
>> +
>> +	return tbl;
>> +}
>> +
>> +static int process_scmi_memlat_of_node(struct scmi_device *sdev, struct scmi_memlat_info *info)
>> +{
>> +	struct scmi_monitor_info *monitor;
>> +	struct scmi_memory_info *memory;
>> +	char name[MAX_NAME_LEN];
>> +	u64 memfreq[2];
>> +	int ret;
>> +
>> +	ret = populate_cluster_info(info->cluster_info);
>> +	if (ret < 0) {
>> +		dev_err_probe(&sdev->dev, ret, "failed to populate cluster info\n");
>> +		goto err;
>> +	}
>> +
>> +	of_node_get(sdev->dev.of_node);
>> +	do {
>> +		snprintf(name, sizeof(name), "memory-%d", info->memory_cnt);
>> +		struct device_node *memory_np __free(device_node) =
>> +			of_find_node_by_name(sdev->dev.of_node, name);
>> +
>> +		if (!memory_np)
>> +			break;
>> +
>> +		if (info->memory_cnt >= MAX_MEMORY_TYPES)
>> +			return dev_err_probe(&sdev->dev, -EINVAL,
>> +					     "failed to parse unsupported memory type\n");
>> +
>> +		memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
>> +		if (!memory) {
>> +			ret = -ENOMEM;
>> +			goto err;
>> +		}
>> +
>> +		ret = of_property_read_u32(memory_np, "qcom,memory-type", &memory->hw_type);
>> +		if (ret) {
>> +			dev_err_probe(&sdev->dev, ret, "failed to read memory type\n");
>> +			goto err;
>> +		}
>> +
>> +		ret = of_property_read_u64_array(memory_np, "freq-table-hz", memfreq, 2);
>> +		if (ret && (ret != -EINVAL)) {
>> +			dev_err_probe(&sdev->dev, ret, "failed to read min/max freq\n");
>> +			goto err;
>> +		}
> 
> Can we get this information from the OPP table instead?

we don't list all the available ddr/llcc freqs in the opp-table
so that we can keep the table constant across platforms.

> 
>> +
>> +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
>> +			memory->min_freq = memfreq[0] / HZ_PER_KHZ;
>> +			memory->max_freq = memfreq[1] / HZ_PER_KHZ;
>> +		} else {
>> +			memory->min_freq = memfreq[0];
>> +			memory->max_freq = memfreq[1];
> 
> Why? At least invert the logic here, please. The DDR_QOS is a special
> case, not all other kinds of memory.

ack
> 
>> +		}
>> +		info->memory[info->memory_cnt++] = memory;
>> +
>> +		do {
>> +			snprintf(name, sizeof(name), "monitor-%d", memory->monitor_cnt);
>> +			struct device_node *monitor_np __free(device_node) =
>> +				of_get_child_by_name(memory_np, name);
>> +
>> +			if (!monitor_np)
>> +				break;
>> +
>> +			if (memory->monitor_cnt >= MAX_MONITOR_CNT)
> 
> Why do you need to limit it? Is it a protocol limitation or an
> artificial driver limitation? Can monitors be allocated dynamically?

Yeah, they are limited to a max of 5 in firmware.

> 
>> +				return dev_err_probe(&sdev->dev, -EINVAL,
>> +						     "failed to parse unsupported monitor\n");
>> +
>> +			monitor = devm_kzalloc(&sdev->dev, sizeof(*monitor), GFP_KERNEL);
>> +			if (!monitor) {
>> +				ret = -ENOMEM;
>> +				goto err;
>> +			}
>> +
>> +			monitor->mon_type = of_property_read_bool(monitor_np, "qcom,compute-type");
>> +			if (!monitor->mon_type) {
>> +				ret = of_property_read_u32(monitor_np, "qcom,ipm-ceil",
>> +							   &monitor->ipm_ceil);
>> +				if (ret) {
>> +					dev_err_probe(&sdev->dev, ret,
>> +						      "failed to read IPM ceiling\n");
>> +					goto err;
>> +				}
>> +			}
>> +
>> +			/*
>> +			 * Variants of the SoC having reduced number of cpus operate
>> +			 * with the same number of logical cpus but the physical
>> +			 * cpu disabled will differ between parts. Calculate the
>> +			 * physical cpu number using cluster information instead.
>> +			 */
>> +			populate_physical_mask(monitor_np, &monitor->mask, info->cluster_info);
>> +
>> +			monitor->freq_map = init_cpufreq_memfreq_map(&sdev->dev, memory, monitor_np,
>> +								     &monitor->freq_map_len);
>> +			if (IS_ERR(monitor->freq_map)) {
>> +				dev_err_probe(&sdev->dev, PTR_ERR(monitor->freq_map),
>> +					      "failed to populate cpufreq-memfreq map\n");
>> +				goto err;
>> +			}
>> +
>> +			strscpy(monitor->mon_name, name, sizeof(monitor->mon_name));
>> +			monitor->mon_idx = memory->monitor_cnt;
>> +
>> +			memory->monitor[memory->monitor_cnt++] = monitor;
>> +		} while (1);
>> +
>> +		if (!memory->monitor_cnt) {
>> +			ret = -EINVAL;
>> +			dev_err_probe(&sdev->dev, ret, "failed to find monitor nodes\n");
>> +			goto err;
>> +		}
>> +	} while (1);
>> +
>> +	if (!info->memory_cnt) {
>> +		ret = -EINVAL;
>> +		dev_err_probe(&sdev->dev, ret, "failed to find memory nodes\n");
>> +	}
>> +
>> +err:
>> +	of_node_put(sdev->dev.of_node);
>> +
>> +	return ret;
>> +}
>> +
>> +static int configure_cpucp_common_events(struct scmi_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.hw_type = INVALID_IDX;
>> +	msg.cid[INST_IDX] = EV_INST_RETIRED;
>> +	msg.cid[CYC_IDX] = EV_CPU_CYCLES;
>> +	msg.cid[CONST_CYC_IDX] = INVALID_IDX;
>> +	msg.cid[FE_STALL_IDX] = INVALID_IDX;
>> +	msg.cid[BE_STALL_IDX] = INVALID_IDX;
>> +
>> +	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_memlat_info *info, int memory_index)
>> +{
>> +	const struct qcom_generic_ext_ops *ops = info->ops;
>> +	struct scmi_memory_info *memory = info->memory[memory_index];
>> +	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_memlat_info *info,
>> +			       int memory_index, int monitor_index)
>> +{
>> +	const struct qcom_generic_ext_ops *ops = info->ops;
>> +	struct scmi_memory_info *memory = info->memory[memory_index];
>> +	struct scmi_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->mon_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->mon_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->mon_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;
>> +		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->mon_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->mon_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->mon_name);
>> +
>> +	return ret;
>> +}
>> +
>> +static int cpucp_memlat_init(struct scmi_device *sdev)
>> +{
>> +	const struct scmi_handle *handle = sdev->handle;
>> +	const struct qcom_generic_ext_ops *ops;
>> +	struct scmi_protocol_handle *ph;
>> +	struct scmi_memlat_info *info;
>> +	u32 cpucp_freq_method = CPUCP_DEFAULT_FREQ_METHOD;
>> +	u32 cpucp_sample_ms = CPUCP_DEFAULT_SAMPLING_PERIOD_MS;
>> +	int ret, i, j;
>> +
>> +	if (!handle)
>> +		return -ENODEV;
>> +
>> +	ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_QCOM_GENERIC, &ph);
>> +	if (IS_ERR(ops))
>> +		return PTR_ERR(ops);
>> +
>> +	info = devm_kzalloc(&sdev->dev, sizeof(*info), GFP_KERNEL);
>> +	if (!info)
>> +		return -ENOMEM;
>> +
>> +	ret = process_scmi_memlat_of_node(sdev, info);
>> +	if (ret)
>> +		return ret;
>> +
>> +	info->ph = ph;
>> +	info->ops = ops;
>> +
>> +	/* 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, &cpucp_sample_ms, sizeof(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, &cpucp_freq_method, sizeof(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");
>> +
>> +	return ret;
>> +}
>> +
>> +static int scmi_client_probe(struct scmi_device *sdev)
>> +{
>> +	return cpucp_memlat_init(sdev);
> 
> Inline it here, please.

ack.

-Sibi

> 
>> +}
>> +
>> +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 qcom_scmi_client_drv = {
>> +	.name		= "scmi-qcom-generic-ext-memlat",
>> +	.probe		= scmi_client_probe,
>> +	.id_table	= scmi_id_table,
>> +};
>> +module_scmi_driver(qcom_scmi_client_drv);
>> +
>> +MODULE_DESCRIPTION("QTI SCMI client driver");
>> +MODULE_LICENSE("GPL");
>> -- 
>> 2.34.1
>>
> 

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-10-08  6:52 ` [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Krzysztof Kozlowski
@ 2024-10-22  8:24   ` Sibi Sankar
  0 siblings, 0 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-10-22  8:24 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi



On 10/8/24 12:22, Krzysztof Kozlowski wrote:
> On Mon, Oct 07, 2024 at 11:40:18AM +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. Introduce a client driver that
>> uses the memlat 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).
> 
> None of your patches are wrapped according to Linux coding style which
> makes reviewing more difficult than it should be. And before you answer
> with checkpatch, checkpatch is not a coding style.

I can see that you've been a reviewer of this series from the very
initial version. That would imply you had a chance to shape/guide the
series to whatever shape you prefer. Yet you choose not to do so and
make a blanket statement now that it's close to merge in v4 :/

-Sibi

> 
> Best regards,
> Krzysztof
> 

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

* Re: [PATCH V4 2/5] firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation
  2024-10-07  6:10 ` [PATCH V4 2/5] firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation Sibi Sankar
@ 2024-10-22 10:22   ` Cristian Marussi
  2024-11-14  4:32     ` Sibi Sankar
  0 siblings, 1 reply; 64+ messages in thread
From: Cristian Marussi @ 2024-10-22 10:22 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi

On Mon, Oct 07, 2024 at 11:40:20AM +0530, Sibi Sankar wrote:
> Add QCOM System Control Management Interface (SCMI) Generic Vendor
> Extensions Protocol documentation.
> 

Hi Sibi,

a few remarks down below.

> Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com>
> ---
>  .../arm_scmi/vendors/qcom/qcom_generic.rst    | 210 ++++++++++++++++++
>  1 file changed, 210 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..1ee6dabaac23
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst
> @@ -0,0 +1,210 @@
> +.. 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[8] Set to 1.                                          |
> +|                  |Bits[0] Set to 1.                                          |
> ++------------------+-----------------------------------------------------------+

Mmmm, this does not explain so much what are those bits and what values
they can indeed assume :P ...

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

And what abot the size of this payload ? .. so you are relying on the
fact that the transport will add the total message length at that layer
and so the server can determine where the valid payload ends...

...it means the server will have some expectations about the payload length
based on the param_id and will check against the received transport-advertised
message-length, am I right ?

...BUT what if you end up with multiple versions of this protocol in the future,
with varying payload lengths for the same param_id...REMEMEBER that the server
cannot know which version of a protocol the client is running (while the client
can see what the server runs) UNLESS you implement also NEGOTIATE_PROTOCOL_VERSION
for this protocol...

...so without an explicit length nor the NEGOTIATE_PROTOCOL_VERSION you wont be
able in the future, server-side, to be sure if you are assumnig the right payload
length for the right version that the client is speaking...so at the end you
wont be able to support multiple versions of the protocol even if the Kernel
can support all of those versions...do you see what I mean ?

I think that would be advisable to implement NEGOTIATE_PROTOCOL_VERSION
if you dont want to carry an explicit size in the message for this payload...

...or am I missing something ?

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

Similarly, no payload length means you will have to code some builtin
check to verify the length of the message that you have received against
the specific version that the server is running...this is NOT so
problematic here as in the _SET above since the client/agent DOES know which
protocol version the server is running...

...it is a bit odd, but indeed similar to other variable sized SCMI messages in
standard protocols that sports optional fields in the reply, for which, similarly
you have to check the version of the protocol to desume the size of the message
based on the presence or not of some fields...

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

Same consideration as above...being a SET-like operation with a variable
sized field in the request AND no explicit payload length, you will have
to derive the size from the message length BUT since you doint even have
implemented NEGOTIATE_PROTOCOL_VERSION in the future any kind of check
will become impossibe server side if you will have multiple protocols
with varying sizes for buf depending on the protocol version

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

Same.

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

Thanks,
Cristian

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-10-07  6:10 ` [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor Sibi Sankar
  2024-10-07 17:57   ` Dmitry Baryshkov
  2024-10-10 12:18   ` Jonathan Cameron
@ 2024-10-22 12:00   ` Cristian Marussi
  2024-11-29  9:57   ` Shivnandan Kumar
  3 siblings, 0 replies; 64+ messages in thread
From: Cristian Marussi @ 2024-10-22 12:00 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid

On Mon, Oct 07, 2024 at 11:40:22AM +0530, Sibi Sankar wrote:
> Introduce a 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).
> 

Hi,

a few small remarks, down below.

> 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 <quic_sibis@quicinc.com>
> ---

[snip]

> +static int populate_cluster_info(u32 *cluster_info)
> +{
> +	char name[MAX_NAME_LEN];
> +	int i = 0;
> +
> +	struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
> +	if (!cn)
> +		return -ENODEV;

Not sure if this is some new coding style accepted for the new cleanup.h
fancy stuff (sincere question/doubt...so please take this with a grain of salt),
BUT, if not, you should consider grouping this definition/initialization to
the start of the block whose scope they are in...like:


	struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
	struct device_node *map __free(device_node) = NULL;
	char name[MAX_NAME_LEN];
	int i = 0;

	if (!cn)
		return -ENODEV;

	map = of_get_child_by_name(cn, "cpu-map");
	if (!map)
		return -ENODEV;

> +
> +	struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
> +	if (!map)
> +		return -ENODEV;
> +

As said...

> +	do {
> +		snprintf(name, sizeof(name), "cluster%d", i);
> +		struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
> +		if (!c)
> +			break;
> +
> +		*(cluster_info + i) = of_get_child_count(c);
> +		i++;
> +	} while (1);
> +
> +	return 0;
> +}
> +
> +static void populate_physical_mask(struct device_node *np, u32 *mask, u32 *cluster_info)
> +{
> +	struct device_node *dev_phandle __free(device_node);

...so this cleanups on return....

> +	int cpu, i = 0, physical_id;
> +
> +	do {
> +		dev_phandle = of_parse_phandle(np, "cpus", i++);

BUT wont this be needed to be of_put, between calls to of_parse_phandle
inside this loop ? ... so cannot this be done like

	int cpu, i = 0, physical_id;

	while (1) {
		struct device_node *dev_phandle __free(device_node) = of_parse_phandle(np, "cpus", i++);
	
		if (!dev_phandle)
			break;

		cpu = of_cpu_node_to_id(dev_phandle);
		if (cpu != -ENODEV) {
			....
	}

...not even build tested ... ah... :P


> +		cpu = of_cpu_node_to_id(dev_phandle);
> +		if (cpu != -ENODEV) {
> +			physical_id = topology_core_id(cpu);
> +			for (int j = 0; j < topology_cluster_id(cpu); j++)
> +				physical_id += *(cluster_info + j);
> +			*mask |= BIT(physical_id);
> +		}
> +	} while (dev_phandle);
> +}
> +
> +static struct cpufreq_memfreq_map *init_cpufreq_memfreq_map(struct device *dev,
> +							    struct scmi_memory_info *memory,
> +							    struct device_node *of_node,
> +							    u32 *cnt)
> +{
> +	struct device_node *tbl_np __free(device_node), *opp_np __free(device_node);
> +	struct cpufreq_memfreq_map *tbl;
> +	int ret, i = 0;
> +	u32 level, len;
> +	u64 rate;
> +
> +	tbl_np = of_parse_phandle(of_node, "operating-points-v2", 0);
> +	if (!tbl_np)
> +		return ERR_PTR(-ENODEV);
> +
> +	len = min(of_get_available_child_count(tbl_np), MAX_MAP_ENTRIES);
> +	if (len == 0)
> +		return ERR_PTR(-ENODEV);
> +
> +	tbl = devm_kzalloc(dev, (len + 1) * sizeof(struct cpufreq_memfreq_map),
> +			   GFP_KERNEL);
> +	if (!tbl)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for_each_available_child_of_node(tbl_np, opp_np) {

This seems to lack a of+node_put at the end but possibly the scoped
version  for_each_available_child_of_node_scoped() will do it for you...

> +		ret = of_property_read_u64_index(opp_np, "opp-hz", 0, &rate);
> +		if (ret < 0)
> +			return ERR_PTR(ret);
> +
> +		tbl[i].cpufreq_mhz = rate / HZ_PER_MHZ;
> +
> +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
> +			ret = of_property_read_u64_index(opp_np, "opp-hz", 1, &rate);
> +			if (ret < 0)
> +				return ERR_PTR(ret);
> +
> +			tbl[i].memfreq_khz = rate / HZ_PER_KHZ;
> +		} else {
> +			ret = of_property_read_u32(opp_np, "opp-level", &level);
> +			if (ret < 0)
> +				return ERR_PTR(ret);
> +
> +			tbl[i].memfreq_khz = level;
> +		}
> +
> +		dev_dbg(dev, "Entry%d CPU:%u, Mem:%u\n", i, tbl[i].cpufreq_mhz, tbl[i].memfreq_khz);
> +		i++;
> +	}
> +	*cnt = len;
> +
> +	return tbl;
> +}
> +
> +static int process_scmi_memlat_of_node(struct scmi_device *sdev, struct scmi_memlat_info *info)
> +{
> +	struct scmi_monitor_info *monitor;
> +	struct scmi_memory_info *memory;
> +	char name[MAX_NAME_LEN];
> +	u64 memfreq[2];
> +	int ret;
> +
> +	ret = populate_cluster_info(info->cluster_info);
> +	if (ret < 0) {
> +		dev_err_probe(&sdev->dev, ret, "failed to populate cluster info\n");
> +		goto err;
> +	}
> +
> +	of_node_get(sdev->dev.of_node);

cant you use cleanup.h magic also for this and get rid of a few gotos down below ?
...this function seems the ideal case fot that...

> +	do {
> +		snprintf(name, sizeof(name), "memory-%d", info->memory_cnt);
> +		struct device_node *memory_np __free(device_node) =
> +			of_find_node_by_name(sdev->dev.of_node, name);
> +
> +		if (!memory_np)
> +			break;
> +
> +		if (info->memory_cnt >= MAX_MEMORY_TYPES)

Shouldn't the MAX_MEMORY_TYPES something discoverable at runtime through
some command of your vendor protocol ? for better future scalability I
mean...maybe I am overthinking... 

> +			return dev_err_probe(&sdev->dev, -EINVAL,
> +					     "failed to parse unsupported memory type\n");
> +
> +		memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
> +		if (!memory) {
> +			ret = -ENOMEM;
> +			goto err;
> +		}
> +

Thanks,
Cristian

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

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-10-22  7:25         ` Sibi Sankar
@ 2024-10-24 13:29           ` Krzysztof Kozlowski
  2024-10-24 19:46             ` Dmitry Baryshkov
  2024-10-24 19:48           ` Dmitry Baryshkov
  1 sibling, 1 reply; 64+ messages in thread
From: Krzysztof Kozlowski @ 2024-10-24 13:29 UTC (permalink / raw)
  To: Sibi Sankar, Dmitry Baryshkov
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi

On 22/10/2024 09:25, Sibi Sankar wrote:
> 
> 
> On 10/8/24 17:41, Krzysztof Kozlowski wrote:
>> On 08/10/2024 14:10, Dmitry Baryshkov wrote:
>>> On Tue, Oct 08, 2024 at 08:49:27AM GMT, Krzysztof Kozlowski wrote:
>>>> On Mon, Oct 07, 2024 at 11:40:19AM +0530, Sibi Sankar wrote:
>>>>> +/*
>>>>> + * QCOM_MEM_TYPE_DDR_QOS supports the following states.
>>>>> + *
>>>>> + * %QCOM_DDR_LEVEL_AUTO:	DDR operates with LPM enabled
>>>>> + * %QCOM_DDR_LEVEL_PERF:	DDR operates with LPM disabled
>>>>> + */
>>>>> +#define QCOM_DDR_LEVEL_AUTO	0x0
>>>>> +#define QCOM_DDR_LEVEL_PERF	0x1
>>>>
>>>> I could not find any driver using these. Can you point me to usage in
>>>> the drivers?
>>>
>>> It's well hidden. These are the raw values used for DDR_QOS memory.
>>
>> So not a binding? Then these should be dropped.
> 
> I am not sure why the term "well hidden" was even considered :(
> The driver just reads them from dt and passes them along. If you
> want the dt to list magic numbers 0/1 instead I can do that as well.
> 

If these are used by FW, then it's fine, although please document it
clearly in comment.

Best regards,
Krzysztof


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

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-10-24 13:29           ` Krzysztof Kozlowski
@ 2024-10-24 19:46             ` Dmitry Baryshkov
  0 siblings, 0 replies; 64+ messages in thread
From: Dmitry Baryshkov @ 2024-10-24 19:46 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Sibi Sankar, sudeep.holla, cristian.marussi, andersson,
	konrad.dybcio, robh+dt, krzysztof.kozlowski+dt, linux-kernel,
	linux-arm-msm, devicetree, linux-arm-kernel, quic_rgottimu,
	quic_kshivnan, conor+dt, arm-scmi

On Thu, Oct 24, 2024 at 03:29:05PM +0200, Krzysztof Kozlowski wrote:
> On 22/10/2024 09:25, Sibi Sankar wrote:
> > 
> > 
> > On 10/8/24 17:41, Krzysztof Kozlowski wrote:
> >> On 08/10/2024 14:10, Dmitry Baryshkov wrote:
> >>> On Tue, Oct 08, 2024 at 08:49:27AM GMT, Krzysztof Kozlowski wrote:
> >>>> On Mon, Oct 07, 2024 at 11:40:19AM +0530, Sibi Sankar wrote:
> >>>>> +/*
> >>>>> + * QCOM_MEM_TYPE_DDR_QOS supports the following states.
> >>>>> + *
> >>>>> + * %QCOM_DDR_LEVEL_AUTO:	DDR operates with LPM enabled
> >>>>> + * %QCOM_DDR_LEVEL_PERF:	DDR operates with LPM disabled
> >>>>> + */
> >>>>> +#define QCOM_DDR_LEVEL_AUTO	0x0
> >>>>> +#define QCOM_DDR_LEVEL_PERF	0x1
> >>>>
> >>>> I could not find any driver using these. Can you point me to usage in
> >>>> the drivers?
> >>>
> >>> It's well hidden. These are the raw values used for DDR_QOS memory.
> >>
> >> So not a binding? Then these should be dropped.
> > 
> > I am not sure why the term "well hidden" was even considered :(
> > The driver just reads them from dt and passes them along. If you
> > want the dt to list magic numbers 0/1 instead I can do that as well.
> > 
> 
> If these are used by FW, then it's fine, although please document it
> clearly in comment.

If they are used by FW but they are common between all platforms, it
should go to the driver instead of being stuffed into DT.


-- 
With best wishes
Dmitry

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

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-10-22  7:25         ` Sibi Sankar
  2024-10-24 13:29           ` Krzysztof Kozlowski
@ 2024-10-24 19:48           ` Dmitry Baryshkov
  1 sibling, 0 replies; 64+ messages in thread
From: Dmitry Baryshkov @ 2024-10-24 19:48 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: Krzysztof Kozlowski, sudeep.holla, cristian.marussi, andersson,
	konrad.dybcio, robh+dt, krzysztof.kozlowski+dt, linux-kernel,
	linux-arm-msm, devicetree, linux-arm-kernel, quic_rgottimu,
	quic_kshivnan, conor+dt, arm-scmi

On Tue, Oct 22, 2024 at 12:55:19PM +0530, Sibi Sankar wrote:
> 
> 
> On 10/8/24 17:41, Krzysztof Kozlowski wrote:
> > On 08/10/2024 14:10, Dmitry Baryshkov wrote:
> > > On Tue, Oct 08, 2024 at 08:49:27AM GMT, Krzysztof Kozlowski wrote:
> > > > On Mon, Oct 07, 2024 at 11:40:19AM +0530, Sibi Sankar wrote:
> > > > > +/*
> > > > > + * QCOM_MEM_TYPE_DDR_QOS supports the following states.
> > > > > + *
> > > > > + * %QCOM_DDR_LEVEL_AUTO:	DDR operates with LPM enabled
> > > > > + * %QCOM_DDR_LEVEL_PERF:	DDR operates with LPM disabled
> > > > > + */
> > > > > +#define QCOM_DDR_LEVEL_AUTO	0x0
> > > > > +#define QCOM_DDR_LEVEL_PERF	0x1
> > > > 
> > > > I could not find any driver using these. Can you point me to usage in
> > > > the drivers?
> > > 
> > > It's well hidden. These are the raw values used for DDR_QOS memory.
> > 
> > So not a binding? Then these should be dropped.
> 
> I am not sure why the term "well hidden" was even considered :(
> The driver just reads them from dt and passes them along. If you
> want the dt to list magic numbers 0/1 instead I can do that as well.

Well, it is well hidden, because e.g. Krzysztof could not find them
being used.

No, nobody asks for the magic numbers. Please drop them from DT and move
to the driver instead. And this is one additional bonus point for the
non-generic node names. ddr-qos is _different_ from all other "memory"
types. It doesn't have min/max values.

-- 
With best wishes
Dmitry

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

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-10-22  7:13     ` Sibi Sankar
@ 2024-10-24 19:54       ` Dmitry Baryshkov
  0 siblings, 0 replies; 64+ messages in thread
From: Dmitry Baryshkov @ 2024-10-24 19:54 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi

On Tue, Oct 22, 2024 at 12:43:09PM +0530, Sibi Sankar wrote:
> 
> 
> On 10/7/24 23:36, Dmitry Baryshkov wrote:
> > On Mon, Oct 07, 2024 at 11:40:19AM GMT, Sibi Sankar wrote:
> > > Document the various memory buses that can be monitored and scaled by
> > > the memory latency governor hosted by the QCOM SCMI Generic Extension
> > > Protocol v1.0.
> > > 
> > > Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com>
> 
> Hey Dmitry,
> 
> Thanks for taking time to review the series!
> 
> > > ---
> > > 
> > > v3:
> > > * Restructure the bindings to mimic IMX [Christian]
> > > 
> > >   .../bindings/firmware/arm,scmi.yaml           |   1 +
> > >   .../bindings/firmware/qcom,scmi-memlat.yaml   | 246 ++++++++++++++++++
> > >   .../dt-bindings/firmware/qcom,scmi-memlat.h   |  22 ++
> > >   3 files changed, 269 insertions(+)
> > >   create mode 100644 Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
> > >   create mode 100644 include/dt-bindings/firmware/qcom,scmi-memlat.h
> > > 
> > > diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
> > > index 54d7d11bfed4..1d405f429168 100644
> > > --- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
> > > +++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
> > > @@ -24,6 +24,7 @@ description: |
> > >   anyOf:
> > >     - $ref: /schemas/firmware/nxp,imx95-scmi.yaml
> > > +  - $ref: /schemas/firmware/qcom,scmi-memlat.yaml
> > >   properties:
> > >     $nodename:
> > > diff --git a/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml b/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
> > > new file mode 100644
> > > index 000000000000..0e8ea6dacd6a
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
> > > @@ -0,0 +1,246 @@
> > > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/firmware/qcom,scmi-memlat.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Qualcomm SCMI Memory Bus nodes
> > > +
> > > +maintainers:
> > > +  - Sibi Sankar <quic_sibis@quicinc.com>
> > > +
> > > +description:
> > > +  This binding describes the various memory buses that can be monitored and scaled
> > > +  by memory latency governor running on the CPU Control Processor (SCMI controller).
> > > +
> > > +properties:
> > > +  protocol@80:
> > > +    $ref: '/schemas/firmware/arm,scmi.yaml#/$defs/protocol-node'
> > > +    unevaluatedProperties: false
> > > +
> > > +    properties:
> > > +      reg:
> > > +        const: 0x80
> > > +
> > > +    patternProperties:
> > > +      '^memory-[0-9]$':
> > > +        type: object
> > > +        unevaluatedProperties: false
> > > +        description:
> > > +          The list of all memory buses that can be monitored and scaled by the
> > > +          memory latency governor running on the SCMI controller.
> > > +
> > > +        properties:
> > > +          qcom,memory-type:
> > > +            $ref: /schemas/types.yaml#/definitions/uint32
> > > +            enum: [0, 1, 2]
> > > +            description: |
> > > +              Memory Bus Identifier
> > > +              0 = QCOM_MEM_TYPE_DDR
> > > +              1 = QCOM_MEM_TYPE_LLCC
> > > +              2 = QCOM_MEM_TYPE_DDR_QOS
> > 
> > I'm sorry if this has been discussed and frowned upon, but can you
> > squash memory type into device node?
> 
> I don't think anyone had any strong opinions on how the
> nodes is to be named. We went with a generic node name since
> it could accomodate multiple instances of llcc or ddr in the
> future. We didn't want it be named ddr-0/ddr-1 and so on. So
> I'll continue to stick with the current naming unless you have
> a strong reason other than readability.

As I wrote in the other email, the memory types are not equal. They have
different properties, etc. Having non-generic names allows describing
that in schema.

Last, but not least, please consider how reserved memory nodes are
handled nowadays: they have non-generic names, each one describing the
purpose / kind.

> > protocol@80 {
> > 	ddr {
> > 	};
> > 
> > 	llcc {
> > 	};
> > 
> > 	ddr-qos {
> > 	};
> > };
> > 
> > > +
> > > +          freq-table-hz:
> > > +            items:
> > > +              items:
> > > +                - description: Minimum frequency of the memory bus in Hz
> > > +                - description: Maximum frequency of the memory bus in Hz
> > 
> > Does it make sense for the DDR-QOS type? Can we hardcode those values
> > and drop freq-table-hz from the DDR-QOS node?
> > 
> > Also, can we drop this completely by adding one extra OPP entry with the
> > minimum memory bus frequency?
> 
> the map table doesn't necessarily list all the supported
> frequencies. It was made that way so that the table is flexible
> enough that it doesn't have to be changed a lot across platforms.
> Hence a need for a separate property to list min/max freq.

Please use opp-supported-hw or other similar techniques to describe
supported frequencies.

> 
> > 
> > > +
> > > +        patternProperties:
> > > +          '^monitor-[0-9]$':
> > > +            type: object
> > > +            unevaluatedProperties: false
> > > +            description:
> > > +              The list of all monitors detecting the memory latency bound workloads using
> > > +              various counters.
> > > +
> > > +            properties:
> > > +              qcom,compute-type:
> > > +                description:
> > > +                  Monitors of type compute perform bus dvfs based on a rudimentary CPU
> > > +                  frequency to memory frequency map.
> > > +                type: boolean
> > 
> > This seems to be redundant. If there is no qcom,ipm-ceil property, then
> > it's qcom,compute-type, isn't it?
> 
> ack
> 
> > 
> > > +
> > > +              qcom,ipm-ceil:
> > > +                $ref: /schemas/types.yaml#/definitions/uint32
> > > +                description:
> > > +                  Monitors having this property perform bus dvfs based on the same
> > > +                  rudimentary table but the scaling is performed only if the calculated
> > > +                  IPM (Instruction Per Misses) exceeds the given ceiling.
> > > +
> > > +              cpus:
> > > +                $ref: /schemas/types.yaml#/definitions/phandle-array
> > > +                description:
> > > +                  Should be a list of phandles to CPU nodes (as described in
> > > +                  Documentation/devicetree/bindings/arm/cpus.yaml).
> > 
> > Which CPU nodes? I see that the examples list all CPUs here. Do we
> > really need them?
> 
> This observation is only valid for X1E where all the cpus have
> identical freq charecteristics. Even with this case we need to
> list them to handle cases where cpus gets disabled by the bootloader
> on lower cored X1E parts i.e. we use this to figure out the actual
> physical mask.

This should be a part of the description.

BTW, why do you need to remove bootloader-removed cores? Can you simply
ignore non-existing CPUs instead?

> 
> > 
> > > +
> > > +              operating-points-v2: true
> > > +              opp-table:
> > > +                type: object
> > > +
> > > +            required:
> > > +              - cpus
> > > +              - operating-points-v2
> > > +
> > > +            oneOf:
> > > +              - required: [ 'qcom,compute-type' ]
> > > +              - required: [ 'qcom,ipm-ceil' ]
> > > +
> > > +        required:
> > > +          - qcom,memory-type
> > > +          - freq-table-hz
> > > +
> > > +additionalProperties: true
> > > +
> > > +examples:
> > > +  - |
> > > +    #include <dt-bindings/firmware/qcom,scmi-memlat.h>
> > > +
> > > +    firmware {
> > > +        scmi {
> > > +            compatible = "arm,scmi";
> > > +            mboxes = <&cpucp_mbox 0>, <&cpucp_mbox 2>;
> > > +            mbox-names = "tx", "rx";
> > > +            shmem = <&cpu_scp_lpri0>, <&cpu_scp_lpri1>;
> > > +
> > > +            #address-cells = <1>;
> > > +            #size-cells = <0>;
> > > +
> > > +            protocol@80 {
> > > +                reg = <0x80>;
> > > +
> > > +                memory-0 {
> > > +                    qcom,memory-type = <QCOM_MEM_TYPE_DDR>;
> > > +                    freq-table-hz = /bits/ 64 <200000000 4224000000>;
> > > +
> > > +                    monitor-0 {
> > 
> > Hmm. Can we say that each memory type can have at most one IPM and one
> > compute aka "passive" memlat monitor? Does it make sense to use them as
> > node names and drop the extra monitor-M names?
> 
> Again this observation is valid only for X1E where the cpu freq
> across cpu's are identical across clusters and is not true for
> other mobile SoCs. So each memory can have more than 2 monitors
> i.e. atleast one active/passibe monitor for each cluster.

Description or commit message, please.

> 
> > 
> > > +                        qcom,ipm-ceil = <20000000>;
> > > +                        cpus = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6 &CPU7
> > > +                                &CPU8 &CPU9 &CPU10 &CPU11>;
> > 
> > Are CPU lists different between monitors? Can they be different? Can
> > they be different between different memory types?
> 
> same explanation.
> 
> > 
> > > +                        operating-points-v2 = <&memory0_monitor0_opp_table>;
> > > +
> > > +                        memory0_monitor0_opp_table: opp-table {
> > 
> > sensible names are better:
> 
> I think I just picked these names up from a cpufreq table upstream.

Doesn't mean that you can't be better than that :-D

> 
> > 
> > ddr_ipm_opp_table: opp-table {
> > };
> > 

-- 
With best wishes
Dmitry

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-10-22  8:18     ` Sibi Sankar
@ 2024-10-26 18:16       ` Dmitry Baryshkov
  2024-11-14  4:13         ` Sibi Sankar
       [not found]         ` <CGME20241114041419epcas1p3b52bb9795ffd9efa568bb106ba268e02@epcms1p5>
  2024-10-28  8:30       ` Cristian Marussi
  1 sibling, 2 replies; 64+ messages in thread
From: Dmitry Baryshkov @ 2024-10-26 18:16 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid

On Tue, Oct 22, 2024 at 01:48:25PM +0530, Sibi Sankar wrote:
> 
> 
> On 10/7/24 23:27, Dmitry Baryshkov wrote:
> > On Mon, Oct 07, 2024 at 11:40:22AM GMT, Sibi Sankar wrote:
> > > Introduce a 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).
> > 
> > This sounds like a devfreq implementation. Please provide a reason why
> > it doesn't use existing API (even if to export the information to the
> > userspace).
> 
> IIRC, you asked the same question when the RFC version of it
> was posted and I replied to it back then.
> 
> https://lore.kernel.org/lkml/0adaa065-3883-ebfe-8259-05ebdbd821eb@quicinc.com/
> 
> The series does not yet export any information to userspace yet
> and when it does get added in the future, it would have no way
> of populating governor node with the current way devfreq framework
> is structured. Since this series is all about just enabling basic
> support, I guess what you ask can be accomodated when we do start
> exporting this info to userspace.

Please consider using devfreq to export basic information (min / max /
cur_freq). This way the user can find current limitations and the
current vote for the system. Then, when there is more information to
export, it can be added to the sysfs or to debugfs using device-specific
ABI. But the core (devfreq) will still be present.

> > > 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 <quic_sibis@quicinc.com>
> > > ---
> > > 
> > > v3:
> > > * 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]
> > > 
> > >   drivers/soc/qcom/Kconfig                   |  12 +
> > >   drivers/soc/qcom/Makefile                  |   1 +
> > >   drivers/soc/qcom/qcom_scmi_memlat_client.c | 569 +++++++++++++++++++++
> > >   3 files changed, 582 insertions(+)
> > >   create mode 100644 drivers/soc/qcom/qcom_scmi_memlat_client.c
> > > 
> > > diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> > > index 74b9121240f8..1b6dd40d69ea 100644
> > > --- a/drivers/soc/qcom/Kconfig
> > > +++ b/drivers/soc/qcom/Kconfig
> > > @@ -295,4 +295,16 @@ config QCOM_PBS
> > >   	  This module provides the APIs to the client drivers that wants to send the
> > >   	  PBS trigger event to the PBS RAM.
> > > +config QCOM_SCMI_MEMLAT_CLIENT
> > > +	tristate "Qualcomm Technologies Inc. SCMI client driver"
> > > +	depends on QCOM_SCMI_GENERIC_EXT || COMPILE_TEST
> > > +	help
> > > +	  This driver uses the MEMLAT (memory latency) algorithm string
> > > +	  hosted on QCOM SCMI Vendor Protocol to detect memory latency
> > 
> > How can it use the string to detect workloads? Most likely you mean something like "uses memlat extensions".
> > Also s/QCOM/Qualcomm/ in the help text.
> 
> The generic vendor protocol extension works by associating algorithms to
> strings. But like you said it can be re-worded to avoid confusion.

SGTM

> 
> > 
> > > +	  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.
> > > +
> > >   endmenu
> > > diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> > > index acbca2ab5cc2..28549bb141bc 100644
> > > --- a/drivers/soc/qcom/Makefile
> > > +++ b/drivers/soc/qcom/Makefile
> > > @@ -36,6 +36,7 @@ obj-$(CONFIG_QCOM_APR) += apr.o
> > >   obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o
> > >   obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) +=	kryo-l2-accessors.o
> > >   obj-$(CONFIG_QCOM_ICC_BWMON)	+= icc-bwmon.o
> > > +obj-$(CONFIG_QCOM_SCMI_MEMLAT_CLIENT)	+= qcom_scmi_memlat_client.o
> > >   qcom_ice-objs			+= ice.o
> > >   obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE)	+= qcom_ice.o
> > >   obj-$(CONFIG_QCOM_PBS) +=	qcom-pbs.o
> > > diff --git a/drivers/soc/qcom/qcom_scmi_memlat_client.c b/drivers/soc/qcom/qcom_scmi_memlat_client.c
> > > new file mode 100644
> > > index 000000000000..05198bf1f7ec
> > > --- /dev/null
> > > +++ b/drivers/soc/qcom/qcom_scmi_memlat_client.c
> > > @@ -0,0 +1,569 @@
> > > +// SPDX-License-Identifier: GPL-2.0-only
> > > +/*
> > > + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
> > > + */
> > > +
> > > +#include <linux/cpu.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/scmi_protocol.h>
> > > +#include <linux/scmi_qcom_protocol.h>
> > > +#include <linux/units.h>
> > > +#include <dt-bindings/firmware/qcom,scmi-memlat.h>
> > > +
> > > +#define MEMLAT_ALGO_STR				0x4D454D4C4154 /* MEMLAT */
> > > +#define INVALID_IDX				0xff
> > > +#define MAX_MEMORY_TYPES			3
> > > +#define MAX_MONITOR_CNT				4
> > > +#define MAX_NAME_LEN				20
> > > +#define MAX_MAP_ENTRIES				7
> > > +#define CPUCP_DEFAULT_SAMPLING_PERIOD_MS	4
> > > +#define CPUCP_DEFAULT_FREQ_METHOD		1
> > > +
> > > +/**
> > > + * 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_SAMPLE_MS = 31,
> > > +	MEMLAT_MON_FREQ_MAP,
> > > +	MEMLAT_SET_MIN_FREQ,
> > > +	MEMLAT_SET_MAX_FREQ,
> > > +	MEMLAT_START_TIMER = 36,
> > > +	MEMLAT_STOP_TIMER,
> > > +	MEMLAT_SET_EFFECTIVE_FREQ_METHOD = 39,
> > > +};
> > > +
> > > +struct map_table {
> > > +	u16 v1;
> > > +	u16 v2;
> > 
> > Huh? Why can't it be cpufreq and memfreq with some suffix?
> 
> ack
> 
> > 
> > > +};
> > > +
> > > +struct map_param_msg {
> > > +	u32 hw_type;
> > > +	u32 mon_idx;
> > > +	u32 nr_rows;
> > > +	struct 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
> > > +};
> > > +
> > > +#define EV_CPU_CYCLES		0
> > > +#define EV_INST_RETIRED		2
> > > +#define EV_L2_D_RFILL		5
> > > +
> > > +struct ev_map_msg {
> > > +	u32 num_evs;
> > > +	u32 hw_type;
> > > +	u32 cid[NUM_COMMON_EVS];
> > > +};
> > > +
> > > +struct cpufreq_memfreq_map {
> > > +	unsigned int cpufreq_mhz;
> > > +	unsigned int memfreq_khz;
> > > +};
> > > +
> > > +struct scmi_monitor_info {
> > > +	struct cpufreq_memfreq_map *freq_map;
> > > +	char mon_name[MAX_NAME_LEN];
> > > +	u32 mon_idx;
> > > +	u32 mon_type;
> > > +	u32 ipm_ceil;
> > > +	u32 mask;
> > > +	u32 freq_map_len;
> > > +};
> > > +
> > > +struct scmi_memory_info {
> > > +	struct scmi_monitor_info *monitor[MAX_MONITOR_CNT];
> > > +	u32 hw_type;
> > > +	int monitor_cnt;
> > > +	u32 min_freq;
> > > +	u32 max_freq;
> > > +};
> > > +
> > > +struct scmi_memlat_info {
> > > +	struct scmi_protocol_handle *ph;
> > > +	const struct qcom_generic_ext_ops *ops;
> > > +	struct scmi_memory_info *memory[MAX_MEMORY_TYPES];
> > > +	u32 cluster_info[NR_CPUS];
> > > +	int memory_cnt;
> > > +};
> > > +
> > > +static int populate_cluster_info(u32 *cluster_info)
> > > +{
> > > +	char name[MAX_NAME_LEN];
> > > +	int i = 0;
> > > +
> > > +	struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
> > > +	if (!cn)
> > > +		return -ENODEV;
> > > +
> > > +	struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
> > > +	if (!map)
> > > +		return -ENODEV;
> > > +
> > > +	do {
> > > +		snprintf(name, sizeof(name), "cluster%d", i);
> > > +		struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
> > > +		if (!c)
> > > +			break;
> > > +
> > > +		*(cluster_info + i) = of_get_child_count(c);
> > > +		i++;
> > > +	} while (1);
> > 
> > Can you use existing API from drivers/base/arch_topology.c? If not, can
> > it be extended to support your usecase?
> 
> ack. But I'm pretty sure it's going to take a while for reaching such
> an agreement so I'll drop this feature during the next re-spin.

Why? What kind of API do you actually need? The arch_topology.c simply
exports a table of struct cpu_topology. Is it somehow different from
what you are parsing manually?

> 
> > 
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static void populate_physical_mask(struct device_node *np, u32 *mask, u32 *cluster_info)
> > > +{
> > > +	struct device_node *dev_phandle __free(device_node);
> > > +	int cpu, i = 0, physical_id;
> > > +
> > > +	do {
> > > +		dev_phandle = of_parse_phandle(np, "cpus", i++);
> > > +		cpu = of_cpu_node_to_id(dev_phandle);
> > > +		if (cpu != -ENODEV) {
> > > +			physical_id = topology_core_id(cpu);
> > > +			for (int j = 0; j < topology_cluster_id(cpu); j++)
> > > +				physical_id += *(cluster_info + j);
> > > +			*mask |= BIT(physical_id);
> > > +		}
> > > +	} while (dev_phandle);
> > > +}
> > > +
> > > +static struct cpufreq_memfreq_map *init_cpufreq_memfreq_map(struct device *dev,
> > > +							    struct scmi_memory_info *memory,
> > > +							    struct device_node *of_node,
> > > +							    u32 *cnt)
> > > +{
> > > +	struct device_node *tbl_np __free(device_node), *opp_np __free(device_node);
> > > +	struct cpufreq_memfreq_map *tbl;
> > > +	int ret, i = 0;
> > > +	u32 level, len;
> > > +	u64 rate;
> > > +
> > > +	tbl_np = of_parse_phandle(of_node, "operating-points-v2", 0);
> > 
> > Please use existing API to parse OPP tables or document a reason why it
> > can't be used.
> 
> Thanks, I had them documented as opens in the coverletter. Dropped them
> since no one had any comments on it during V3. Will add them as comments
> to this driver instead.
> 
> https://lore.kernel.org/lkml/20240702191440.2161623-1-quic_sibis@quicinc.com/
> 
> re-copying things again:
> opp-tables are used but they don't get to be added to the scmi device
> (thus we rely on a lot of manual parsing) because the memlat client driver
> doesn't vote on these resources clocks/interconnects/power-domain
> from the kernel and some of the resources aren't modeled in the first
> place like DDR_QOS.

As discussed offline, please consider extending the OPP to be able to
get the struct opp_table for the particular phandle. Another option
might be to change the memlat driver by having a separate device for
each monitor. This way you can use existing API to parse OPP tables and
to get necessary data from those tables.

> 
> 
> > 
> > > +	if (!tbl_np)
> > > +		return ERR_PTR(-ENODEV);
> > > +
> > > +	len = min(of_get_available_child_count(tbl_np), MAX_MAP_ENTRIES);
> > > +	if (len == 0)
> > > +		return ERR_PTR(-ENODEV);
> > > +
> > > +	tbl = devm_kzalloc(dev, (len + 1) * sizeof(struct cpufreq_memfreq_map),
> > > +			   GFP_KERNEL);
> > > +	if (!tbl)
> > > +		return ERR_PTR(-ENOMEM);
> > > +
> > > +	for_each_available_child_of_node(tbl_np, opp_np) {
> > > +		ret = of_property_read_u64_index(opp_np, "opp-hz", 0, &rate);
> > > +		if (ret < 0)
> > > +			return ERR_PTR(ret);
> > > +
> > > +		tbl[i].cpufreq_mhz = rate / HZ_PER_MHZ;
> > > +
> > > +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
> > > +			ret = of_property_read_u64_index(opp_np, "opp-hz", 1, &rate);
> > > +			if (ret < 0)
> > > +				return ERR_PTR(ret);
> > > +
> > > +			tbl[i].memfreq_khz = rate / HZ_PER_KHZ;
> > > +		} else {
> > > +			ret = of_property_read_u32(opp_np, "opp-level", &level);
> > > +			if (ret < 0)
> > > +				return ERR_PTR(ret);
> > > +
> > > +			tbl[i].memfreq_khz = level;
> > > +		}
> > > +
> > > +		dev_dbg(dev, "Entry%d CPU:%u, Mem:%u\n", i, tbl[i].cpufreq_mhz, tbl[i].memfreq_khz);
> > > +		i++;
> > > +	}
> > > +	*cnt = len;
> > > +
> > > +	return tbl;
> > > +}
> > > +
> > > +static int process_scmi_memlat_of_node(struct scmi_device *sdev, struct scmi_memlat_info *info)
> > > +{
> > > +	struct scmi_monitor_info *monitor;
> > > +	struct scmi_memory_info *memory;
> > > +	char name[MAX_NAME_LEN];
> > > +	u64 memfreq[2];
> > > +	int ret;
> > > +
> > > +	ret = populate_cluster_info(info->cluster_info);
> > > +	if (ret < 0) {
> > > +		dev_err_probe(&sdev->dev, ret, "failed to populate cluster info\n");
> > > +		goto err;
> > > +	}
> > > +
> > > +	of_node_get(sdev->dev.of_node);
> > > +	do {
> > > +		snprintf(name, sizeof(name), "memory-%d", info->memory_cnt);
> > > +		struct device_node *memory_np __free(device_node) =
> > > +			of_find_node_by_name(sdev->dev.of_node, name);
> > > +
> > > +		if (!memory_np)
> > > +			break;
> > > +
> > > +		if (info->memory_cnt >= MAX_MEMORY_TYPES)
> > > +			return dev_err_probe(&sdev->dev, -EINVAL,
> > > +					     "failed to parse unsupported memory type\n");
> > > +
> > > +		memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
> > > +		if (!memory) {
> > > +			ret = -ENOMEM;
> > > +			goto err;
> > > +		}
> > > +
> > > +		ret = of_property_read_u32(memory_np, "qcom,memory-type", &memory->hw_type);
> > > +		if (ret) {
> > > +			dev_err_probe(&sdev->dev, ret, "failed to read memory type\n");
> > > +			goto err;
> > > +		}
> > > +
> > > +		ret = of_property_read_u64_array(memory_np, "freq-table-hz", memfreq, 2);
> > > +		if (ret && (ret != -EINVAL)) {
> > > +			dev_err_probe(&sdev->dev, ret, "failed to read min/max freq\n");
> > > +			goto err;
> > > +		}
> > 
> > Can we get this information from the OPP table instead?
> 
> we don't list all the available ddr/llcc freqs in the opp-table
> so that we can keep the table constant across platforms.

NO. Use opp-supported-hw to limit data to a particular platform. There
is no reason to keep min/max out of the OPP table.

> 
> > 
> > > +
> > > +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
> > > +			memory->min_freq = memfreq[0] / HZ_PER_KHZ;
> > > +			memory->max_freq = memfreq[1] / HZ_PER_KHZ;
> > > +		} else {
> > > +			memory->min_freq = memfreq[0];
> > > +			memory->max_freq = memfreq[1];
> > 
> > Why? At least invert the logic here, please. The DDR_QOS is a special
> > case, not all other kinds of memory.
> 
> ack
> > 
> > > +		}
> > > +		info->memory[info->memory_cnt++] = memory;
> > > +
> > > +		do {
> > > +			snprintf(name, sizeof(name), "monitor-%d", memory->monitor_cnt);
> > > +			struct device_node *monitor_np __free(device_node) =
> > > +				of_get_child_by_name(memory_np, name);
> > > +
> > > +			if (!monitor_np)
> > > +				break;
> > > +
> > > +			if (memory->monitor_cnt >= MAX_MONITOR_CNT)
> > 
> > Why do you need to limit it? Is it a protocol limitation or an
> > artificial driver limitation? Can monitors be allocated dynamically?
> 
> Yeah, they are limited to a max of 5 in firmware.

Comment in the source code.

> > 
> > > +				return dev_err_probe(&sdev->dev, -EINVAL,
> > > +						     "failed to parse unsupported monitor\n");
> > > +
> > > +			monitor = devm_kzalloc(&sdev->dev, sizeof(*monitor), GFP_KERNEL);
> > > +			if (!monitor) {
> > > +				ret = -ENOMEM;
> > > +				goto err;
> > > +			}
> > > +
> > > +			monitor->mon_type = of_property_read_bool(monitor_np, "qcom,compute-type");
> > > +			if (!monitor->mon_type) {
> > > +				ret = of_property_read_u32(monitor_np, "qcom,ipm-ceil",
> > > +							   &monitor->ipm_ceil);
> > > +				if (ret) {
> > > +					dev_err_probe(&sdev->dev, ret,
> > > +						      "failed to read IPM ceiling\n");
> > > +					goto err;
> > > +				}
> > > +			}
> > > +
> > > +			/*
> > > +			 * Variants of the SoC having reduced number of cpus operate
> > > +			 * with the same number of logical cpus but the physical
> > > +			 * cpu disabled will differ between parts. Calculate the
> > > +			 * physical cpu number using cluster information instead.
> > > +			 */
> > > +			populate_physical_mask(monitor_np, &monitor->mask, info->cluster_info);
> > > +
> > > +			monitor->freq_map = init_cpufreq_memfreq_map(&sdev->dev, memory, monitor_np,
> > > +								     &monitor->freq_map_len);
> > > +			if (IS_ERR(monitor->freq_map)) {
> > > +				dev_err_probe(&sdev->dev, PTR_ERR(monitor->freq_map),
> > > +					      "failed to populate cpufreq-memfreq map\n");
> > > +				goto err;
> > > +			}
> > > +
> > > +			strscpy(monitor->mon_name, name, sizeof(monitor->mon_name));
> > > +			monitor->mon_idx = memory->monitor_cnt;
> > > +
> > > +			memory->monitor[memory->monitor_cnt++] = monitor;
> > > +		} while (1);
> > > +
> > > +		if (!memory->monitor_cnt) {
> > > +			ret = -EINVAL;
> > > +			dev_err_probe(&sdev->dev, ret, "failed to find monitor nodes\n");
> > > +			goto err;
> > > +		}
> > > +	} while (1);
> > > +
> > > +	if (!info->memory_cnt) {
> > > +		ret = -EINVAL;
> > > +		dev_err_probe(&sdev->dev, ret, "failed to find memory nodes\n");
> > > +	}
> > > +
> > > +err:
> > > +	of_node_put(sdev->dev.of_node);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int configure_cpucp_common_events(struct scmi_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.hw_type = INVALID_IDX;
> > > +	msg.cid[INST_IDX] = EV_INST_RETIRED;
> > > +	msg.cid[CYC_IDX] = EV_CPU_CYCLES;
> > > +	msg.cid[CONST_CYC_IDX] = INVALID_IDX;
> > > +	msg.cid[FE_STALL_IDX] = INVALID_IDX;
> > > +	msg.cid[BE_STALL_IDX] = INVALID_IDX;
> > > +
> > > +	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_memlat_info *info, int memory_index)
> > > +{
> > > +	const struct qcom_generic_ext_ops *ops = info->ops;
> > > +	struct scmi_memory_info *memory = info->memory[memory_index];
> > > +	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_memlat_info *info,
> > > +			       int memory_index, int monitor_index)
> > > +{
> > > +	const struct qcom_generic_ext_ops *ops = info->ops;
> > > +	struct scmi_memory_info *memory = info->memory[memory_index];
> > > +	struct scmi_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->mon_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->mon_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->mon_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;
> > > +		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->mon_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->mon_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->mon_name);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int cpucp_memlat_init(struct scmi_device *sdev)
> > > +{
> > > +	const struct scmi_handle *handle = sdev->handle;
> > > +	const struct qcom_generic_ext_ops *ops;
> > > +	struct scmi_protocol_handle *ph;
> > > +	struct scmi_memlat_info *info;
> > > +	u32 cpucp_freq_method = CPUCP_DEFAULT_FREQ_METHOD;
> > > +	u32 cpucp_sample_ms = CPUCP_DEFAULT_SAMPLING_PERIOD_MS;
> > > +	int ret, i, j;
> > > +
> > > +	if (!handle)
> > > +		return -ENODEV;
> > > +
> > > +	ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_QCOM_GENERIC, &ph);
> > > +	if (IS_ERR(ops))
> > > +		return PTR_ERR(ops);
> > > +
> > > +	info = devm_kzalloc(&sdev->dev, sizeof(*info), GFP_KERNEL);
> > > +	if (!info)
> > > +		return -ENOMEM;
> > > +
> > > +	ret = process_scmi_memlat_of_node(sdev, info);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	info->ph = ph;
> > > +	info->ops = ops;
> > > +
> > > +	/* 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, &cpucp_sample_ms, sizeof(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, &cpucp_freq_method, sizeof(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");
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int scmi_client_probe(struct scmi_device *sdev)
> > > +{
> > > +	return cpucp_memlat_init(sdev);
> > 
> > Inline it here, please.
> 
> ack.
> 
> -Sibi
> 
> > 
> > > +}
> > > +
> > > +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 qcom_scmi_client_drv = {
> > > +	.name		= "scmi-qcom-generic-ext-memlat",
> > > +	.probe		= scmi_client_probe,
> > > +	.id_table	= scmi_id_table,
> > > +};
> > > +module_scmi_driver(qcom_scmi_client_drv);
> > > +
> > > +MODULE_DESCRIPTION("QTI SCMI client driver");
> > > +MODULE_LICENSE("GPL");
> > > -- 
> > > 2.34.1
> > > 
> > 

-- 
With best wishes
Dmitry

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-10-22  8:18     ` Sibi Sankar
  2024-10-26 18:16       ` Dmitry Baryshkov
@ 2024-10-28  8:30       ` Cristian Marussi
  1 sibling, 0 replies; 64+ messages in thread
From: Cristian Marussi @ 2024-10-28  8:30 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: Dmitry Baryshkov, sudeep.holla, cristian.marussi, andersson,
	konrad.dybcio, robh+dt, krzysztof.kozlowski+dt, linux-kernel,
	linux-arm-msm, devicetree, linux-arm-kernel, quic_rgottimu,
	quic_kshivnan, conor+dt, arm-scmi, Amir Vajid

On Tue, Oct 22, 2024 at 01:48:25PM +0530, Sibi Sankar wrote:
> 
> 

Hi Sibi,

one more thing down below...

> On 10/7/24 23:27, Dmitry Baryshkov wrote:
> > On Mon, Oct 07, 2024 at 11:40:22AM GMT, Sibi Sankar wrote:
> > > Introduce a 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).

[snip]

> > > +		}
> > > +		info->memory[info->memory_cnt++] = memory;
> > > +
> > > +		do {
> > > +			snprintf(name, sizeof(name), "monitor-%d", memory->monitor_cnt);
> > > +			struct device_node *monitor_np __free(device_node) =
> > > +				of_get_child_by_name(memory_np, name);
> > > +
> > > +			if (!monitor_np)
> > > +				break;
> > > +
> > > +			if (memory->monitor_cnt >= MAX_MONITOR_CNT)
> > 
> > Why do you need to limit it? Is it a protocol limitation or an
> > artificial driver limitation? Can monitors be allocated dynamically?
> 
> Yeah, they are limited to a max of 5 in firmware.

Similarly as I already commented elsewhere, in an SCMI-based platform
this is something you should be able to discover at run-time with a
query command of some kind (or using some bits of the existing cmds)
....so that you dont have this dependency on constants defined and
built into the platform fw....what if your fw evolves to allow more
monitors ?

Thanks,
Cristian

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-10-07  6:10 [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
                   ` (5 preceding siblings ...)
  2024-10-08  6:52 ` [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Krzysztof Kozlowski
@ 2024-11-06 12:55 ` Johan Hovold
  2024-11-06 20:03   ` Cristian Marussi
  6 siblings, 1 reply; 64+ messages in thread
From: Johan Hovold @ 2024-11-06 12:55 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi

On Mon, Oct 07, 2024 at 11:40:18AM +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. Introduce a client driver that
> uses the memlat 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).
> 
> 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.

I wanted to give this series a quick spin on the x1e80100 CRD, but ran
into some issues.

First, I expected the drivers to be loaded automatically when built as
modules, but that did not happen so something appears to be missing.

Second, after loading the protocol and client drivers manually (in that
order, shouldn't the client driver pull in the protocol?), I got:

	scmi_module: Loaded SCMI Vendor Protocol 0x80 - Qualcomm  20000
	arm-scmi arm-scmi.0.auto: QCOM Generic Vendor Version 1.0
	scmi-qcom-generic-ext-memlat scmi_dev.5: error -EOPNOTSUPP: failed to configure common events
	scmi-qcom-generic-ext-memlat scmi_dev.5: probe with driver scmi-qcom-generic-ext-memlat failed with error -95

which seems to suggest that the firmware on my CRD does not support this
feature. Is that the way this should be interpreted? And does that mean
that non of the commercial laptops supports this either?

Johan

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-11-06 12:55 ` Johan Hovold
@ 2024-11-06 20:03   ` Cristian Marussi
  2024-11-08 15:14     ` Johan Hovold
  0 siblings, 1 reply; 64+ messages in thread
From: Cristian Marussi @ 2024-11-06 20:03 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Sibi Sankar, sudeep.holla, cristian.marussi, andersson,
	konrad.dybcio, robh+dt, krzysztof.kozlowski+dt, linux-kernel,
	linux-arm-msm, devicetree, linux-arm-kernel, quic_rgottimu,
	quic_kshivnan, conor+dt, arm-scmi

On Wed, Nov 06, 2024 at 01:55:33PM +0100, Johan Hovold wrote:
> On Mon, Oct 07, 2024 at 11:40:18AM +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. Introduce a client driver that
> > uses the memlat 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).
> > 
> > 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.
> 
> I wanted to give this series a quick spin on the x1e80100 CRD, but ran
> into some issues.
> 
> First, I expected the drivers to be loaded automatically when built as
> modules, but that did not happen so something appears to be missing.
> 

Hi Johan,

so the SCMI stack is fully modularizable as of this release, i.e.

 - SCMI core (scmi-core + scmi-module)
 - SCMI transports (scmi_transport_{mailbox,virtio,smc,optee}
 - optional SCMI Vendor protos
 - Std and Vendor SCMI Drivers on top of protos

....on the other side the SCMI standard protocols are still embedded
in scmi-module (or builtin) as of now...

Even though, module usage is tracked by the core AND when an SCMI Vendor
driver tries to use protocol_X, it causes protocol_X to be initialized
(calling its protocol_init), there is NO auto-loading for SCMI Vendor
Protocols when bult as LKM...because there were really no ask till now
and this stuff is in general needed so very early dburing boot...so the
usecase of all these LKM modules is just debug/test as in your case
(or in mine frequently)....

...and I am NOT saying with this that is necessarily right or must be
stay like this...just explaining how it is now....

....anyway...it is mostly trivial to add vendor/protocols autoloading
transparently...today I was experimenting with a patch that triggers
autoloading based on a generic common alias pattern in the form
'scmi-protocol-0x<NN>' (with NN the specific protocol ID of course)
that triggers the loading as soon as the SCMI Vendor driver tries to
access the protocol during its probe...

....I will post it for the next cycle once it is clean.
(unless I am missing something else that you want to add...)

> Second, after loading the protocol and client drivers manually (in that
> order, shouldn't the client driver pull in the protocol?), I got:
> 
> 	scmi_module: Loaded SCMI Vendor Protocol 0x80 - Qualcomm  20000
> 	arm-scmi arm-scmi.0.auto: QCOM Generic Vendor Version 1.0
> 	scmi-qcom-generic-ext-memlat scmi_dev.5: error -EOPNOTSUPP: failed to configure common events
> 	scmi-qcom-generic-ext-memlat scmi_dev.5: probe with driver scmi-qcom-generic-ext-memlat failed with error -95
> 
> which seems to suggest that the firmware on my CRD does not support this
> feature. Is that the way this should be interpreted? And does that mean
> that non of the commercial laptops supports this either?

This seems like FW rejecting the command, maybe just only for the specific
Linux OSPM agent since it is not allowed to ask for that specific setup,
and only Sibi can shed a light here :D

...but this Vendor protocol, if I am not mistaken, AFAIU, uses a bunch
of "algo strings" coming from tokens it picks from DT and use thsoe as
params for the set_param() VendorProtocol ops...cannot be that a bad/missing
DT value could cause the FW to reject the command due to the params ?
(even if the command is supported)...

...just a guess ah... I have no real knowledge of this venndor proto...


Thanks,
Cristian


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

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-10-07  6:10 ` [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension Sibi Sankar
                     ` (2 preceding siblings ...)
  2024-10-08  6:49   ` Krzysztof Kozlowski
@ 2024-11-06 22:18   ` Jeffrey Hugo
  2024-11-14  4:17     ` Sibi Sankar
  2024-12-05 15:27   ` Sudeep Holla
  4 siblings, 1 reply; 64+ messages in thread
From: Jeffrey Hugo @ 2024-11-06 22:18 UTC (permalink / raw)
  To: Sibi Sankar, sudeep.holla, cristian.marussi, andersson,
	konrad.dybcio, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-kernel, linux-arm-msm, devicetree, linux-arm-kernel,
	quic_rgottimu, quic_kshivnan, conor+dt, arm-scmi

On 10/7/2024 12:10 AM, Sibi Sankar wrote:
> diff --git a/include/dt-bindings/firmware/qcom,scmi-memlat.h b/include/dt-bindings/firmware/qcom,scmi-memlat.h
> new file mode 100644
> index 000000000000..7ae8d8d5623b
> --- /dev/null
> +++ b/include/dt-bindings/firmware/qcom,scmi-memlat.h
> @@ -0,0 +1,22 @@
> +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
> +/*
> + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +#ifndef __DT_BINDINGS_QCOM_SCMI_VENDOR_H
> +#define __DT_BINDINGS_QCOM_SCMI_VENDOR

The #define does not match the #ifndef (missing "_H")

> +
> +/* Memory IDs */
> +#define QCOM_MEM_TYPE_DDR	0x0
> +#define QCOM_MEM_TYPE_LLCC	0x1
> +#define QCOM_MEM_TYPE_DDR_QOS	0x2
> +
> +/*
> + * QCOM_MEM_TYPE_DDR_QOS supports the following states.
> + *
> + * %QCOM_DDR_LEVEL_AUTO:	DDR operates with LPM enabled
> + * %QCOM_DDR_LEVEL_PERF:	DDR operates with LPM disabled
> + */
> +#define QCOM_DDR_LEVEL_AUTO	0x0
> +#define QCOM_DDR_LEVEL_PERF	0x1
> +
> +#endif /* __DT_BINDINGS_QCOM_SCMI_VENDOR_H */


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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-11-06 20:03   ` Cristian Marussi
@ 2024-11-08 15:14     ` Johan Hovold
  2024-11-14  4:22       ` Sibi Sankar
  0 siblings, 1 reply; 64+ messages in thread
From: Johan Hovold @ 2024-11-08 15:14 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: Sibi Sankar, sudeep.holla, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi

On Wed, Nov 06, 2024 at 08:03:30PM +0000, Cristian Marussi wrote:
> On Wed, Nov 06, 2024 at 01:55:33PM +0100, Johan Hovold wrote:

> > First, I expected the drivers to be loaded automatically when built as
> > modules, but that did not happen so something appears to be missing.

> Even though, module usage is tracked by the core AND when an SCMI Vendor
> driver tries to use protocol_X, it causes protocol_X to be initialized
> (calling its protocol_init), there is NO auto-loading for SCMI Vendor
> Protocols when bult as LKM...because there were really no ask till now
> and this stuff is in general needed so very early dburing boot...so the
> usecase of all these LKM modules is just debug/test as in your case
> (or in mine frequently)....
> 
> ...and I am NOT saying with this that is necessarily right or must be
> stay like this...just explaining how it is now....

Ok, thanks for clarifying.

> ....anyway...it is mostly trivial to add vendor/protocols autoloading
> transparently...today I was experimenting with a patch that triggers
> autoloading based on a generic common alias pattern in the form
> 'scmi-protocol-0x<NN>' (with NN the specific protocol ID of course)
> that triggers the loading as soon as the SCMI Vendor driver tries to
> access the protocol during its probe...
> 
> ....I will post it for the next cycle once it is clean.
> (unless I am missing something else that you want to add...)

Sounds like that would solve the issue. I was just expecting the memlat
client driver and its protocol dependency to be loaded automatically
when built as modules on machines that can use them (e.g. as we don't
want to have every vendor protocol driver built into distro kernels,
etc).

> > Second, after loading the protocol and client drivers manually (in that
> > order, shouldn't the client driver pull in the protocol?), I got:
> > 
> > 	scmi_module: Loaded SCMI Vendor Protocol 0x80 - Qualcomm  20000
> > 	arm-scmi arm-scmi.0.auto: QCOM Generic Vendor Version 1.0
> > 	scmi-qcom-generic-ext-memlat scmi_dev.5: error -EOPNOTSUPP: failed to configure common events
> > 	scmi-qcom-generic-ext-memlat scmi_dev.5: probe with driver scmi-qcom-generic-ext-memlat failed with error -95
> > 
> > which seems to suggest that the firmware on my CRD does not support this
> > feature. Is that the way this should be interpreted? And does that mean
> > that non of the commercial laptops supports this either?
> 
> This seems like FW rejecting the command, maybe just only for the specific
> Linux OSPM agent since it is not allowed to ask for that specific setup,
> and only Sibi can shed a light here :D
> 
> ...but this Vendor protocol, if I am not mistaken, AFAIU, uses a bunch
> of "algo strings" coming from tokens it picks from DT and use thsoe as
> params for the set_param() VendorProtocol ops...cannot be that a bad/missing
> DT value could cause the FW to reject the command due to the params ?
> (even if the command is supported)...
> 
> ...just a guess ah... I have no real knowledge of this venndor proto...

Yeah, hopefully Sibi can shed some light on this. I'm using the DT
patch (5/5) from this series, which according to the commit message is
supposed to enable bus scaling on the x1e80100 platform. So I guess
something is missing in my firmware.

Johan

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-10-26 18:16       ` Dmitry Baryshkov
@ 2024-11-14  4:13         ` Sibi Sankar
  2024-11-14 12:32           ` Dmitry Baryshkov
       [not found]         ` <CGME20241114041419epcas1p3b52bb9795ffd9efa568bb106ba268e02@epcms1p5>
  1 sibling, 1 reply; 64+ messages in thread
From: Sibi Sankar @ 2024-11-14  4:13 UTC (permalink / raw)
  To: Dmitry Baryshkov, myungjoo.ham, Kyungmin.park, cw00.choi,
	Viresh Kumar
  Cc: sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid



On 10/26/24 23:46, Dmitry Baryshkov wrote:
> On Tue, Oct 22, 2024 at 01:48:25PM +0530, Sibi Sankar wrote:
>>
>>
>> On 10/7/24 23:27, Dmitry Baryshkov wrote:
>>> On Mon, Oct 07, 2024 at 11:40:22AM GMT, Sibi Sankar wrote:
>>>> Introduce a 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).
>>>
>>> This sounds like a devfreq implementation. Please provide a reason why
>>> it doesn't use existing API (even if to export the information to the
>>> userspace).
>>
>> IIRC, you asked the same question when the RFC version of it
>> was posted and I replied to it back then.
>>
>> https://lore.kernel.org/lkml/0adaa065-3883-ebfe-8259-05ebdbd821eb@quicinc.com/
>>
>> The series does not yet export any information to userspace yet
>> and when it does get added in the future, it would have no way
>> of populating governor node with the current way devfreq framework
>> is structured. Since this series is all about just enabling basic
>> support, I guess what you ask can be accomodated when we do start
>> exporting this info to userspace.

Hey Dmitry,

Thanks for taking time to review the series.

+ Devfreq maintainers to comment (I thought you already added
them by name)


Hey MyungJoo/Kyungmin/Chanwoo,

Can you weigh in here? Does it make sense to add a new
class of devfreq devices that don't have governors
associated with them just for them to export a few
essential data to userspace? In this scenario the
scaling algorithm is in a SCP and we just start
them from the kernel. We do have ways to get the
current frequency of various buses but does this
warrant adding a new class of governor less devices?

-Sibi

> 
> Please consider using devfreq to export basic information (min / max /
> cur_freq). This way the user can find current limitations and the
> current vote for the system. Then, when there is more information to
> export, it can be added to the sysfs or to debugfs using device-specific
> ABI. But the core (devfreq) will still be present.
> 
>>>> 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 <quic_sibis@quicinc.com>
>>>> ---
>>>>
>>>> v3:
>>>> * 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]
>>>>
>>>>    drivers/soc/qcom/Kconfig                   |  12 +
>>>>    drivers/soc/qcom/Makefile                  |   1 +
>>>>    drivers/soc/qcom/qcom_scmi_memlat_client.c | 569 +++++++++++++++++++++
>>>>    3 files changed, 582 insertions(+)
>>>>    create mode 100644 drivers/soc/qcom/qcom_scmi_memlat_client.c
>>>>
>>>> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
>>>> index 74b9121240f8..1b6dd40d69ea 100644
>>>> --- a/drivers/soc/qcom/Kconfig
>>>> +++ b/drivers/soc/qcom/Kconfig
>>>> @@ -295,4 +295,16 @@ config QCOM_PBS
>>>>    	  This module provides the APIs to the client drivers that wants to send the
>>>>    	  PBS trigger event to the PBS RAM.
>>>> +config QCOM_SCMI_MEMLAT_CLIENT
>>>> +	tristate "Qualcomm Technologies Inc. SCMI client driver"
>>>> +	depends on QCOM_SCMI_GENERIC_EXT || COMPILE_TEST
>>>> +	help
>>>> +	  This driver uses the MEMLAT (memory latency) algorithm string
>>>> +	  hosted on QCOM SCMI Vendor Protocol to detect memory latency
>>>
>>> How can it use the string to detect workloads? Most likely you mean something like "uses memlat extensions".
>>> Also s/QCOM/Qualcomm/ in the help text.
>>
>> The generic vendor protocol extension works by associating algorithms to
>> strings. But like you said it can be re-worded to avoid confusion.
> 
> SGTM
> 
>>
>>>
>>>> +	  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.
>>>> +
>>>>    endmenu
>>>> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
>>>> index acbca2ab5cc2..28549bb141bc 100644
>>>> --- a/drivers/soc/qcom/Makefile
>>>> +++ b/drivers/soc/qcom/Makefile
>>>> @@ -36,6 +36,7 @@ obj-$(CONFIG_QCOM_APR) += apr.o
>>>>    obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o
>>>>    obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) +=	kryo-l2-accessors.o
>>>>    obj-$(CONFIG_QCOM_ICC_BWMON)	+= icc-bwmon.o
>>>> +obj-$(CONFIG_QCOM_SCMI_MEMLAT_CLIENT)	+= qcom_scmi_memlat_client.o
>>>>    qcom_ice-objs			+= ice.o
>>>>    obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE)	+= qcom_ice.o
>>>>    obj-$(CONFIG_QCOM_PBS) +=	qcom-pbs.o
>>>> diff --git a/drivers/soc/qcom/qcom_scmi_memlat_client.c b/drivers/soc/qcom/qcom_scmi_memlat_client.c
>>>> new file mode 100644
>>>> index 000000000000..05198bf1f7ec
>>>> --- /dev/null
>>>> +++ b/drivers/soc/qcom/qcom_scmi_memlat_client.c
>>>> @@ -0,0 +1,569 @@
>>>> +// SPDX-License-Identifier: GPL-2.0-only
>>>> +/*
>>>> + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
>>>> + */
>>>> +
>>>> +#include <linux/cpu.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/scmi_protocol.h>
>>>> +#include <linux/scmi_qcom_protocol.h>
>>>> +#include <linux/units.h>
>>>> +#include <dt-bindings/firmware/qcom,scmi-memlat.h>
>>>> +
>>>> +#define MEMLAT_ALGO_STR				0x4D454D4C4154 /* MEMLAT */
>>>> +#define INVALID_IDX				0xff
>>>> +#define MAX_MEMORY_TYPES			3
>>>> +#define MAX_MONITOR_CNT				4
>>>> +#define MAX_NAME_LEN				20
>>>> +#define MAX_MAP_ENTRIES				7
>>>> +#define CPUCP_DEFAULT_SAMPLING_PERIOD_MS	4
>>>> +#define CPUCP_DEFAULT_FREQ_METHOD		1
>>>> +
>>>> +/**
>>>> + * 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_SAMPLE_MS = 31,
>>>> +	MEMLAT_MON_FREQ_MAP,
>>>> +	MEMLAT_SET_MIN_FREQ,
>>>> +	MEMLAT_SET_MAX_FREQ,
>>>> +	MEMLAT_START_TIMER = 36,
>>>> +	MEMLAT_STOP_TIMER,
>>>> +	MEMLAT_SET_EFFECTIVE_FREQ_METHOD = 39,
>>>> +};
>>>> +
>>>> +struct map_table {
>>>> +	u16 v1;
>>>> +	u16 v2;
>>>
>>> Huh? Why can't it be cpufreq and memfreq with some suffix?
>>
>> ack
>>
>>>
>>>> +};
>>>> +
>>>> +struct map_param_msg {
>>>> +	u32 hw_type;
>>>> +	u32 mon_idx;
>>>> +	u32 nr_rows;
>>>> +	struct 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
>>>> +};
>>>> +
>>>> +#define EV_CPU_CYCLES		0
>>>> +#define EV_INST_RETIRED		2
>>>> +#define EV_L2_D_RFILL		5
>>>> +
>>>> +struct ev_map_msg {
>>>> +	u32 num_evs;
>>>> +	u32 hw_type;
>>>> +	u32 cid[NUM_COMMON_EVS];
>>>> +};
>>>> +
>>>> +struct cpufreq_memfreq_map {
>>>> +	unsigned int cpufreq_mhz;
>>>> +	unsigned int memfreq_khz;
>>>> +};
>>>> +
>>>> +struct scmi_monitor_info {
>>>> +	struct cpufreq_memfreq_map *freq_map;
>>>> +	char mon_name[MAX_NAME_LEN];
>>>> +	u32 mon_idx;
>>>> +	u32 mon_type;
>>>> +	u32 ipm_ceil;
>>>> +	u32 mask;
>>>> +	u32 freq_map_len;
>>>> +};
>>>> +
>>>> +struct scmi_memory_info {
>>>> +	struct scmi_monitor_info *monitor[MAX_MONITOR_CNT];
>>>> +	u32 hw_type;
>>>> +	int monitor_cnt;
>>>> +	u32 min_freq;
>>>> +	u32 max_freq;
>>>> +};
>>>> +
>>>> +struct scmi_memlat_info {
>>>> +	struct scmi_protocol_handle *ph;
>>>> +	const struct qcom_generic_ext_ops *ops;
>>>> +	struct scmi_memory_info *memory[MAX_MEMORY_TYPES];
>>>> +	u32 cluster_info[NR_CPUS];
>>>> +	int memory_cnt;
>>>> +};
>>>> +
>>>> +static int populate_cluster_info(u32 *cluster_info)
>>>> +{
>>>> +	char name[MAX_NAME_LEN];
>>>> +	int i = 0;
>>>> +
>>>> +	struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
>>>> +	if (!cn)
>>>> +		return -ENODEV;
>>>> +
>>>> +	struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
>>>> +	if (!map)
>>>> +		return -ENODEV;
>>>> +
>>>> +	do {
>>>> +		snprintf(name, sizeof(name), "cluster%d", i);
>>>> +		struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
>>>> +		if (!c)
>>>> +			break;
>>>> +
>>>> +		*(cluster_info + i) = of_get_child_count(c);
>>>> +		i++;
>>>> +	} while (1);
>>>
>>> Can you use existing API from drivers/base/arch_topology.c? If not, can
>>> it be extended to support your usecase?
>>
>> ack. But I'm pretty sure it's going to take a while for reaching such
>> an agreement so I'll drop this feature during the next re-spin.
> 
> Why? What kind of API do you actually need? The arch_topology.c simply
> exports a table of struct cpu_topology. Is it somehow different from
> what you are parsing manually?

yup, we had to figure out the physical id of the cpu
since cpus can be disabled by the bootloader using
status = "failed" property and we have to pass this
onto the cpucp memlat algorithm.

> 
>>
>>>
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static void populate_physical_mask(struct device_node *np, u32 *mask, u32 *cluster_info)
>>>> +{
>>>> +	struct device_node *dev_phandle __free(device_node);
>>>> +	int cpu, i = 0, physical_id;
>>>> +
>>>> +	do {
>>>> +		dev_phandle = of_parse_phandle(np, "cpus", i++);
>>>> +		cpu = of_cpu_node_to_id(dev_phandle);
>>>> +		if (cpu != -ENODEV) {
>>>> +			physical_id = topology_core_id(cpu);
>>>> +			for (int j = 0; j < topology_cluster_id(cpu); j++)
>>>> +				physical_id += *(cluster_info + j);
>>>> +			*mask |= BIT(physical_id);
>>>> +		}
>>>> +	} while (dev_phandle);
>>>> +}
>>>> +
>>>> +static struct cpufreq_memfreq_map *init_cpufreq_memfreq_map(struct device *dev,
>>>> +							    struct scmi_memory_info *memory,
>>>> +							    struct device_node *of_node,
>>>> +							    u32 *cnt)
>>>> +{
>>>> +	struct device_node *tbl_np __free(device_node), *opp_np __free(device_node);
>>>> +	struct cpufreq_memfreq_map *tbl;
>>>> +	int ret, i = 0;
>>>> +	u32 level, len;
>>>> +	u64 rate;
>>>> +
>>>> +	tbl_np = of_parse_phandle(of_node, "operating-points-v2", 0);
>>>
>>> Please use existing API to parse OPP tables or document a reason why it
>>> can't be used.
>>
>> Thanks, I had them documented as opens in the coverletter. Dropped them
>> since no one had any comments on it during V3. Will add them as comments
>> to this driver instead.
>>
>> https://lore.kernel.org/lkml/20240702191440.2161623-1-quic_sibis@quicinc.com/
>>
>> re-copying things again:
>> opp-tables are used but they don't get to be added to the scmi device
>> (thus we rely on a lot of manual parsing) because the memlat client driver
>> doesn't vote on these resources clocks/interconnects/power-domain
>> from the kernel and some of the resources aren't modeled in the first
>> place like DDR_QOS.
> 
> As discussed offline, please consider extending the OPP to be able to
> get the struct opp_table for the particular phandle. Another option
> might be to change the memlat driver by having a separate device for
> each monitor. This way you can use existing API to parse OPP tables and
> to get necessary data from those tables.

+ Viresh

Spoke with Viresh offline and he had stricter requirements
than what you proposed. He definitely wanted the opp-tables
to be assoiciated with devices at the very least and have
all opp parsing logic within the opp-framework. Given that
we have to model all these dummy devices just to add the
tables I'll re-check the feasibility of movign the tables
into the driver itself. Will move the patch series back
into RFC and re-post just the vendor protocol since that's
close to merge

-Sibi

> 
>>
>>
>>>
>>>> +	if (!tbl_np)
>>>> +		return ERR_PTR(-ENODEV);
>>>> +
>>>> +	len = min(of_get_available_child_count(tbl_np), MAX_MAP_ENTRIES);
>>>> +	if (len == 0)
>>>> +		return ERR_PTR(-ENODEV);
>>>> +
>>>> +	tbl = devm_kzalloc(dev, (len + 1) * sizeof(struct cpufreq_memfreq_map),
>>>> +			   GFP_KERNEL);
>>>> +	if (!tbl)
>>>> +		return ERR_PTR(-ENOMEM);
>>>> +
>>>> +	for_each_available_child_of_node(tbl_np, opp_np) {
>>>> +		ret = of_property_read_u64_index(opp_np, "opp-hz", 0, &rate);
>>>> +		if (ret < 0)
>>>> +			return ERR_PTR(ret);
>>>> +
>>>> +		tbl[i].cpufreq_mhz = rate / HZ_PER_MHZ;
>>>> +
>>>> +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
>>>> +			ret = of_property_read_u64_index(opp_np, "opp-hz", 1, &rate);
>>>> +			if (ret < 0)
>>>> +				return ERR_PTR(ret);
>>>> +
>>>> +			tbl[i].memfreq_khz = rate / HZ_PER_KHZ;
>>>> +		} else {
>>>> +			ret = of_property_read_u32(opp_np, "opp-level", &level);
>>>> +			if (ret < 0)
>>>> +				return ERR_PTR(ret);
>>>> +
>>>> +			tbl[i].memfreq_khz = level;
>>>> +		}
>>>> +
>>>> +		dev_dbg(dev, "Entry%d CPU:%u, Mem:%u\n", i, tbl[i].cpufreq_mhz, tbl[i].memfreq_khz);
>>>> +		i++;
>>>> +	}
>>>> +	*cnt = len;
>>>> +
>>>> +	return tbl;
>>>> +}
>>>> +
>>>> +static int process_scmi_memlat_of_node(struct scmi_device *sdev, struct scmi_memlat_info *info)
>>>> +{
>>>> +	struct scmi_monitor_info *monitor;
>>>> +	struct scmi_memory_info *memory;
>>>> +	char name[MAX_NAME_LEN];
>>>> +	u64 memfreq[2];
>>>> +	int ret;
>>>> +
>>>> +	ret = populate_cluster_info(info->cluster_info);
>>>> +	if (ret < 0) {
>>>> +		dev_err_probe(&sdev->dev, ret, "failed to populate cluster info\n");
>>>> +		goto err;
>>>> +	}
>>>> +
>>>> +	of_node_get(sdev->dev.of_node);
>>>> +	do {
>>>> +		snprintf(name, sizeof(name), "memory-%d", info->memory_cnt);
>>>> +		struct device_node *memory_np __free(device_node) =
>>>> +			of_find_node_by_name(sdev->dev.of_node, name);
>>>> +
>>>> +		if (!memory_np)
>>>> +			break;
>>>> +
>>>> +		if (info->memory_cnt >= MAX_MEMORY_TYPES)
>>>> +			return dev_err_probe(&sdev->dev, -EINVAL,
>>>> +					     "failed to parse unsupported memory type\n");
>>>> +
>>>> +		memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
>>>> +		if (!memory) {
>>>> +			ret = -ENOMEM;
>>>> +			goto err;
>>>> +		}
>>>> +
>>>> +		ret = of_property_read_u32(memory_np, "qcom,memory-type", &memory->hw_type);
>>>> +		if (ret) {
>>>> +			dev_err_probe(&sdev->dev, ret, "failed to read memory type\n");
>>>> +			goto err;
>>>> +		}
>>>> +
>>>> +		ret = of_property_read_u64_array(memory_np, "freq-table-hz", memfreq, 2);
>>>> +		if (ret && (ret != -EINVAL)) {
>>>> +			dev_err_probe(&sdev->dev, ret, "failed to read min/max freq\n");
>>>> +			goto err;
>>>> +		}
>>>
>>> Can we get this information from the OPP table instead?
>>
>> we don't list all the available ddr/llcc freqs in the opp-table
>> so that we can keep the table constant across platforms.
> 
> NO. Use opp-supported-hw to limit data to a particular platform. There
> is no reason to keep min/max out of the OPP table.

if we are movign the opp-tables into driver data for the reasons
described above, this can probably stay?

> 
>>
>>>
>>>> +
>>>> +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
>>>> +			memory->min_freq = memfreq[0] / HZ_PER_KHZ;
>>>> +			memory->max_freq = memfreq[1] / HZ_PER_KHZ;
>>>> +		} else {
>>>> +			memory->min_freq = memfreq[0];
>>>> +			memory->max_freq = memfreq[1];
>>>
>>> Why? At least invert the logic here, please. The DDR_QOS is a special
>>> case, not all other kinds of memory.
>>
>> ack
>>>
>>>> +		}
>>>> +		info->memory[info->memory_cnt++] = memory;
>>>> +
>>>> +		do {
>>>> +			snprintf(name, sizeof(name), "monitor-%d", memory->monitor_cnt);
>>>> +			struct device_node *monitor_np __free(device_node) =
>>>> +				of_get_child_by_name(memory_np, name);
>>>> +
>>>> +			if (!monitor_np)
>>>> +				break;
>>>> +
>>>> +			if (memory->monitor_cnt >= MAX_MONITOR_CNT)
>>>
>>> Why do you need to limit it? Is it a protocol limitation or an
>>> artificial driver limitation? Can monitors be allocated dynamically?
>>
>> Yeah, they are limited to a max of 5 in firmware.
> 
> Comment in the source code.

ack

> 
>>>
>>>> +				return dev_err_probe(&sdev->dev, -EINVAL,
>>>> +						     "failed to parse unsupported monitor\n");
>>>> +
>>>> +			monitor = devm_kzalloc(&sdev->dev, sizeof(*monitor), GFP_KERNEL);
>>>> +			if (!monitor) {
>>>> +				ret = -ENOMEM;
>>>> +				goto err;
>>>> +			}
>>>> +
>>>> +			monitor->mon_type = of_property_read_bool(monitor_np, "qcom,compute-type");
>>>> +			if (!monitor->mon_type) {
>>>> +				ret = of_property_read_u32(monitor_np, "qcom,ipm-ceil",
>>>> +							   &monitor->ipm_ceil);
>>>> +				if (ret) {
>>>> +					dev_err_probe(&sdev->dev, ret,
>>>> +						      "failed to read IPM ceiling\n");
>>>> +					goto err;
>>>> +				}
>>>> +			}
>>>> +
>>>> +			/*
>>>> +			 * Variants of the SoC having reduced number of cpus operate
>>>> +			 * with the same number of logical cpus but the physical
>>>> +			 * cpu disabled will differ between parts. Calculate the
>>>> +			 * physical cpu number using cluster information instead.
>>>> +			 */
>>>> +			populate_physical_mask(monitor_np, &monitor->mask, info->cluster_info);
>>>> +
>>>> +			monitor->freq_map = init_cpufreq_memfreq_map(&sdev->dev, memory, monitor_np,
>>>> +								     &monitor->freq_map_len);
>>>> +			if (IS_ERR(monitor->freq_map)) {
>>>> +				dev_err_probe(&sdev->dev, PTR_ERR(monitor->freq_map),
>>>> +					      "failed to populate cpufreq-memfreq map\n");
>>>> +				goto err;
>>>> +			}
>>>> +
>>>> +			strscpy(monitor->mon_name, name, sizeof(monitor->mon_name));
>>>> +			monitor->mon_idx = memory->monitor_cnt;
>>>> +
>>>> +			memory->monitor[memory->monitor_cnt++] = monitor;
>>>> +		} while (1);
>>>> +
>>>> +		if (!memory->monitor_cnt) {
>>>> +			ret = -EINVAL;
>>>> +			dev_err_probe(&sdev->dev, ret, "failed to find monitor nodes\n");
>>>> +			goto err;
>>>> +		}
>>>> +	} while (1);
>>>> +
>>>> +	if (!info->memory_cnt) {
>>>> +		ret = -EINVAL;
>>>> +		dev_err_probe(&sdev->dev, ret, "failed to find memory nodes\n");
>>>> +	}
>>>> +
>>>> +err:
>>>> +	of_node_put(sdev->dev.of_node);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static int configure_cpucp_common_events(struct scmi_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.hw_type = INVALID_IDX;
>>>> +	msg.cid[INST_IDX] = EV_INST_RETIRED;
>>>> +	msg.cid[CYC_IDX] = EV_CPU_CYCLES;
>>>> +	msg.cid[CONST_CYC_IDX] = INVALID_IDX;
>>>> +	msg.cid[FE_STALL_IDX] = INVALID_IDX;
>>>> +	msg.cid[BE_STALL_IDX] = INVALID_IDX;
>>>> +
>>>> +	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_memlat_info *info, int memory_index)
>>>> +{
>>>> +	const struct qcom_generic_ext_ops *ops = info->ops;
>>>> +	struct scmi_memory_info *memory = info->memory[memory_index];
>>>> +	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_memlat_info *info,
>>>> +			       int memory_index, int monitor_index)
>>>> +{
>>>> +	const struct qcom_generic_ext_ops *ops = info->ops;
>>>> +	struct scmi_memory_info *memory = info->memory[memory_index];
>>>> +	struct scmi_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->mon_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->mon_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->mon_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;
>>>> +		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->mon_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->mon_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->mon_name);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static int cpucp_memlat_init(struct scmi_device *sdev)
>>>> +{
>>>> +	const struct scmi_handle *handle = sdev->handle;
>>>> +	const struct qcom_generic_ext_ops *ops;
>>>> +	struct scmi_protocol_handle *ph;
>>>> +	struct scmi_memlat_info *info;
>>>> +	u32 cpucp_freq_method = CPUCP_DEFAULT_FREQ_METHOD;
>>>> +	u32 cpucp_sample_ms = CPUCP_DEFAULT_SAMPLING_PERIOD_MS;
>>>> +	int ret, i, j;
>>>> +
>>>> +	if (!handle)
>>>> +		return -ENODEV;
>>>> +
>>>> +	ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_QCOM_GENERIC, &ph);
>>>> +	if (IS_ERR(ops))
>>>> +		return PTR_ERR(ops);
>>>> +
>>>> +	info = devm_kzalloc(&sdev->dev, sizeof(*info), GFP_KERNEL);
>>>> +	if (!info)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	ret = process_scmi_memlat_of_node(sdev, info);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	info->ph = ph;
>>>> +	info->ops = ops;
>>>> +
>>>> +	/* 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, &cpucp_sample_ms, sizeof(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, &cpucp_freq_method, sizeof(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");
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static int scmi_client_probe(struct scmi_device *sdev)
>>>> +{
>>>> +	return cpucp_memlat_init(sdev);
>>>
>>> Inline it here, please.
>>
>> ack.
>>
>> -Sibi
>>
>>>
>>>> +}
>>>> +
>>>> +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 qcom_scmi_client_drv = {
>>>> +	.name		= "scmi-qcom-generic-ext-memlat",
>>>> +	.probe		= scmi_client_probe,
>>>> +	.id_table	= scmi_id_table,
>>>> +};
>>>> +module_scmi_driver(qcom_scmi_client_drv);
>>>> +
>>>> +MODULE_DESCRIPTION("QTI SCMI client driver");
>>>> +MODULE_LICENSE("GPL");
>>>> -- 
>>>> 2.34.1
>>>>
>>>
> 

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

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-11-06 22:18   ` Jeffrey Hugo
@ 2024-11-14  4:17     ` Sibi Sankar
  0 siblings, 0 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-11-14  4:17 UTC (permalink / raw)
  To: Jeffrey Hugo, sudeep.holla, cristian.marussi, andersson,
	konrad.dybcio, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-kernel, linux-arm-msm, devicetree, linux-arm-kernel,
	quic_rgottimu, quic_kshivnan, conor+dt, arm-scmi



On 11/7/24 03:48, Jeffrey Hugo wrote:
> On 10/7/2024 12:10 AM, Sibi Sankar wrote:
>> diff --git a/include/dt-bindings/firmware/qcom,scmi-memlat.h 
>> b/include/dt-bindings/firmware/qcom,scmi-memlat.h
>> new file mode 100644
>> index 000000000000..7ae8d8d5623b
>> --- /dev/null
>> +++ b/include/dt-bindings/firmware/qcom,scmi-memlat.h
>> @@ -0,0 +1,22 @@
>> +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
>> +/*
>> + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights 
>> reserved.
>> + */
>> +#ifndef __DT_BINDINGS_QCOM_SCMI_VENDOR_H
>> +#define __DT_BINDINGS_QCOM_SCMI_VENDOR
> 
> The #define does not match the #ifndef (missing "_H")

Thanks for catching this. Will fix it in the next re-spin.

-Sibi

> 
>> +
>> +/* Memory IDs */
>> +#define QCOM_MEM_TYPE_DDR    0x0
>> +#define QCOM_MEM_TYPE_LLCC    0x1
>> +#define QCOM_MEM_TYPE_DDR_QOS    0x2
>> +
>> +/*
>> + * QCOM_MEM_TYPE_DDR_QOS supports the following states.
>> + *
>> + * %QCOM_DDR_LEVEL_AUTO:    DDR operates with LPM enabled
>> + * %QCOM_DDR_LEVEL_PERF:    DDR operates with LPM disabled
>> + */
>> +#define QCOM_DDR_LEVEL_AUTO    0x0
>> +#define QCOM_DDR_LEVEL_PERF    0x1
>> +
>> +#endif /* __DT_BINDINGS_QCOM_SCMI_VENDOR_H */
> 

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-11-08 15:14     ` Johan Hovold
@ 2024-11-14  4:22       ` Sibi Sankar
  2024-11-22  8:37         ` Johan Hovold
  0 siblings, 1 reply; 64+ messages in thread
From: Sibi Sankar @ 2024-11-14  4:22 UTC (permalink / raw)
  To: Johan Hovold, Cristian Marussi
  Cc: sudeep.holla, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi



On 11/8/24 20:44, Johan Hovold wrote:
> On Wed, Nov 06, 2024 at 08:03:30PM +0000, Cristian Marussi wrote:
>> On Wed, Nov 06, 2024 at 01:55:33PM +0100, Johan Hovold wrote:
> 
>>> First, I expected the drivers to be loaded automatically when built as
>>> modules, but that did not happen so something appears to be missing.
> 
>> Even though, module usage is tracked by the core AND when an SCMI Vendor
>> driver tries to use protocol_X, it causes protocol_X to be initialized
>> (calling its protocol_init), there is NO auto-loading for SCMI Vendor
>> Protocols when bult as LKM...because there were really no ask till now
>> and this stuff is in general needed so very early dburing boot...so the
>> usecase of all these LKM modules is just debug/test as in your case
>> (or in mine frequently)....
>>
>> ...and I am NOT saying with this that is necessarily right or must be
>> stay like this...just explaining how it is now....
> 
> Ok, thanks for clarifying.
> 
>> ....anyway...it is mostly trivial to add vendor/protocols autoloading
>> transparently...today I was experimenting with a patch that triggers
>> autoloading based on a generic common alias pattern in the form
>> 'scmi-protocol-0x<NN>' (with NN the specific protocol ID of course)
>> that triggers the loading as soon as the SCMI Vendor driver tries to
>> access the protocol during its probe...
>>
>> ....I will post it for the next cycle once it is clean.
>> (unless I am missing something else that you want to add...)
> 
> Sounds like that would solve the issue. I was just expecting the memlat
> client driver and its protocol dependency to be loaded automatically
> when built as modules on machines that can use them (e.g. as we don't
> want to have every vendor protocol driver built into distro kernels,
> etc).
> 
>>> Second, after loading the protocol and client drivers manually (in that
>>> order, shouldn't the client driver pull in the protocol?), I got:
>>>
>>> 	scmi_module: Loaded SCMI Vendor Protocol 0x80 - Qualcomm  20000
>>> 	arm-scmi arm-scmi.0.auto: QCOM Generic Vendor Version 1.0
>>> 	scmi-qcom-generic-ext-memlat scmi_dev.5: error -EOPNOTSUPP: failed to configure common events
>>> 	scmi-qcom-generic-ext-memlat scmi_dev.5: probe with driver scmi-qcom-generic-ext-memlat failed with error -95
>>>
>>> which seems to suggest that the firmware on my CRD does not support this
>>> feature. Is that the way this should be interpreted? And does that mean
>>> that non of the commercial laptops supports this either?
>>
>> This seems like FW rejecting the command, maybe just only for the specific
>> Linux OSPM agent since it is not allowed to ask for that specific setup,
>> and only Sibi can shed a light here :D
>>
>> ...but this Vendor protocol, if I am not mistaken, AFAIU, uses a bunch
>> of "algo strings" coming from tokens it picks from DT and use thsoe as
>> params for the set_param() VendorProtocol ops...cannot be that a bad/missing
>> DT value could cause the FW to reject the command due to the params ?
>> (even if the command is supported)...
>>
>> ...just a guess ah... I have no real knowledge of this venndor proto...
> 
> Yeah, hopefully Sibi can shed some light on this. I'm using the DT
> patch (5/5) from this series, which according to the commit message is
> supposed to enable bus scaling on the x1e80100 platform. So I guess
> something is missing in my firmware.

Nah, it's probably just because of the algo string used.
The past few series used caps MEMLAT string instead of
memlat to pass the tuneables, looks like all the laptops
havn't really switched to it yet. Will revert back to
using to lower case memlat so that all devices are
supported. Thanks for trying the series out!

-Sibi

> 
> Johan

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

* Re: [PATCH V4 2/5] firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation
  2024-10-22 10:22   ` Cristian Marussi
@ 2024-11-14  4:32     ` Sibi Sankar
  0 siblings, 0 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-11-14  4:32 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: sudeep.holla, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi



On 10/22/24 15:52, Cristian Marussi wrote:
> On Mon, Oct 07, 2024 at 11:40:20AM +0530, Sibi Sankar wrote:
>> Add QCOM System Control Management Interface (SCMI) Generic Vendor
>> Extensions Protocol documentation.
>>
> 
> Hi Sibi,
> 
> a few remarks down below.
> 
>> Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com>
>> ---
>>   .../arm_scmi/vendors/qcom/qcom_generic.rst    | 210 ++++++++++++++++++
>>   1 file changed, 210 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..1ee6dabaac23
>> --- /dev/null
>> +++ b/drivers/firmware/arm_scmi/vendors/qcom/qcom_generic.rst
>> @@ -0,0 +1,210 @@
>> +.. 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[8] Set to 1.                                          |
>> +|                  |Bits[0] Set to 1.                                          |
>> ++------------------+-----------------------------------------------------------+
> 
> Mmmm, this does not explain so much what are those bits and what values
> they can indeed assume :P ...

lol, after a lot of rooting around figured out they
return the number of vendor protocols available in
the system :/

Not really sure if it's adding any useful info.

> 
>> +
>> +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.                                     |
>> ++------------------+-----------------------------------------------------------+
> 
> And what abot the size of this payload ? .. so you are relying on the
> fact that the transport will add the total message length at that layer
> and so the server can determine where the valid payload ends...
> 
> ...it means the server will have some expectations about the payload length
> based on the param_id and will check against the received transport-advertised
> message-length, am I right ?
> 
> ...BUT what if you end up with multiple versions of this protocol in the future,
> with varying payload lengths for the same param_id...REMEMEBER that the server
> cannot know which version of a protocol the client is running (while the client
> can see what the server runs) UNLESS you implement also NEGOTIATE_PROTOCOL_VERSION
> for this protocol...
> 
> ...so without an explicit length nor the NEGOTIATE_PROTOCOL_VERSION you wont be
> able in the future, server-side, to be sure if you are assumnig the right payload
> length for the right version that the client is speaking...so at the end you
> wont be able to support multiple versions of the protocol even if the Kernel
> can support all of those versions...do you see what I mean ?
> 
> I think that would be advisable to implement NEGOTIATE_PROTOCOL_VERSION
> if you dont want to carry an explicit size in the message for this payload...
> 
> ...or am I missing something ?

ack makes sense but we planned to make sure that the sub-strings
used maintain abi for all their messages but like you said having
way to negotiate protocol version will be nice to have. Will make
sure something along the lines get implemented with the next major
version upgrade.

-Sibi

> 
>> +|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.                       |
>> ++------------------+-----------------------------------------------------------+
> 
> Similarly, no payload length means you will have to code some builtin
> check to verify the length of the message that you have received against
> the specific version that the server is running...this is NOT so
> problematic here as in the _SET above since the client/agent DOES know which
> protocol version the server is running...
> 
> ...it is a bit odd, but indeed similar to other variable sized SCMI messages in
> standard protocols that sports optional fields in the reply, for which, similarly
> you have to check the version of the protocol to desume the size of the message
> based on the presence or not of some fields...
> 
>> +
>> +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.             |
>> ++------------------+-----------------------------------------------------------+
>> +
> 
> Same consideration as above...being a SET-like operation with a variable
> sized field in the request AND no explicit payload length, you will have
> to derive the size from the message length BUT since you doint even have
> implemented NEGOTIATE_PROTOCOL_VERSION in the future any kind of check
> will become impossibe server side if you will have multiple protocols
> with varying sizes for buf depending on the protocol version
> 
>> +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.                                     |
>> ++------------------+-----------------------------------------------------------+
> 
> Same.
> 
>> +|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.                  |
>> ++------------------+-----------------------------------------------------------+
> 
> Thanks,
> Cristian

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-11-14  4:13         ` Sibi Sankar
@ 2024-11-14 12:32           ` Dmitry Baryshkov
  2024-12-05 10:52             ` Sibi Sankar
  0 siblings, 1 reply; 64+ messages in thread
From: Dmitry Baryshkov @ 2024-11-14 12:32 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: myungjoo.ham, Kyungmin.park, cw00.choi, Viresh Kumar,
	sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid

On Thu, Nov 14, 2024 at 09:43:53AM +0530, Sibi Sankar wrote:
> 
> 
> On 10/26/24 23:46, Dmitry Baryshkov wrote:
> > On Tue, Oct 22, 2024 at 01:48:25PM +0530, Sibi Sankar wrote:
> > > 
> > > 
> > > On 10/7/24 23:27, Dmitry Baryshkov wrote:
> > > > On Mon, Oct 07, 2024 at 11:40:22AM GMT, Sibi Sankar wrote:

> > > > 
> > > > > +};
> > > > > +
> > > > > +struct map_param_msg {
> > > > > +	u32 hw_type;
> > > > > +	u32 mon_idx;
> > > > > +	u32 nr_rows;
> > > > > +	struct 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
> > > > > +};
> > > > > +
> > > > > +#define EV_CPU_CYCLES		0
> > > > > +#define EV_INST_RETIRED		2
> > > > > +#define EV_L2_D_RFILL		5
> > > > > +
> > > > > +struct ev_map_msg {
> > > > > +	u32 num_evs;
> > > > > +	u32 hw_type;
> > > > > +	u32 cid[NUM_COMMON_EVS];
> > > > > +};
> > > > > +
> > > > > +struct cpufreq_memfreq_map {
> > > > > +	unsigned int cpufreq_mhz;
> > > > > +	unsigned int memfreq_khz;
> > > > > +};
> > > > > +
> > > > > +struct scmi_monitor_info {
> > > > > +	struct cpufreq_memfreq_map *freq_map;
> > > > > +	char mon_name[MAX_NAME_LEN];
> > > > > +	u32 mon_idx;
> > > > > +	u32 mon_type;
> > > > > +	u32 ipm_ceil;
> > > > > +	u32 mask;
> > > > > +	u32 freq_map_len;
> > > > > +};
> > > > > +
> > > > > +struct scmi_memory_info {
> > > > > +	struct scmi_monitor_info *monitor[MAX_MONITOR_CNT];
> > > > > +	u32 hw_type;
> > > > > +	int monitor_cnt;
> > > > > +	u32 min_freq;
> > > > > +	u32 max_freq;
> > > > > +};
> > > > > +
> > > > > +struct scmi_memlat_info {
> > > > > +	struct scmi_protocol_handle *ph;
> > > > > +	const struct qcom_generic_ext_ops *ops;
> > > > > +	struct scmi_memory_info *memory[MAX_MEMORY_TYPES];
> > > > > +	u32 cluster_info[NR_CPUS];
> > > > > +	int memory_cnt;
> > > > > +};
> > > > > +
> > > > > +static int populate_cluster_info(u32 *cluster_info)
> > > > > +{
> > > > > +	char name[MAX_NAME_LEN];
> > > > > +	int i = 0;
> > > > > +
> > > > > +	struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
> > > > > +	if (!cn)
> > > > > +		return -ENODEV;
> > > > > +
> > > > > +	struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
> > > > > +	if (!map)
> > > > > +		return -ENODEV;
> > > > > +
> > > > > +	do {
> > > > > +		snprintf(name, sizeof(name), "cluster%d", i);
> > > > > +		struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
> > > > > +		if (!c)
> > > > > +			break;
> > > > > +
> > > > > +		*(cluster_info + i) = of_get_child_count(c);
> > > > > +		i++;
> > > > > +	} while (1);
> > > > 
> > > > Can you use existing API from drivers/base/arch_topology.c? If not, can
> > > > it be extended to support your usecase?
> > > 
> > > ack. But I'm pretty sure it's going to take a while for reaching such
> > > an agreement so I'll drop this feature during the next re-spin.
> > 
> > Why? What kind of API do you actually need? The arch_topology.c simply
> > exports a table of struct cpu_topology. Is it somehow different from
> > what you are parsing manually?
> 
> yup, we had to figure out the physical id of the cpu
> since cpus can be disabled by the bootloader using
> status = "failed" property and we have to pass this
> onto the cpucp memlat algorithm.

Isn't it equal to the index in the cpu_topology table?

> 
> > 
> > > 
> > > > 
> > > > > +
> > > > > +	return 0;
> > > > > +}
> > > > > +
> > > > > +static void populate_physical_mask(struct device_node *np, u32 *mask, u32 *cluster_info)
> > > > > +{
> > > > > +	struct device_node *dev_phandle __free(device_node);
> > > > > +	int cpu, i = 0, physical_id;
> > > > > +
> > > > > +	do {
> > > > > +		dev_phandle = of_parse_phandle(np, "cpus", i++);
> > > > > +		cpu = of_cpu_node_to_id(dev_phandle);
> > > > > +		if (cpu != -ENODEV) {
> > > > > +			physical_id = topology_core_id(cpu);
> > > > > +			for (int j = 0; j < topology_cluster_id(cpu); j++)
> > > > > +				physical_id += *(cluster_info + j);
> > > > > +			*mask |= BIT(physical_id);
> > > > > +		}
> > > > > +	} while (dev_phandle);
> > > > > +}
> > > > > +
> > > > > +static struct cpufreq_memfreq_map *init_cpufreq_memfreq_map(struct device *dev,
> > > > > +							    struct scmi_memory_info *memory,
> > > > > +							    struct device_node *of_node,
> > > > > +							    u32 *cnt)
> > > > > +{
> > > > > +	struct device_node *tbl_np __free(device_node), *opp_np __free(device_node);
> > > > > +	struct cpufreq_memfreq_map *tbl;
> > > > > +	int ret, i = 0;
> > > > > +	u32 level, len;
> > > > > +	u64 rate;
> > > > > +
> > > > > +	tbl_np = of_parse_phandle(of_node, "operating-points-v2", 0);
> > > > 
> > > > Please use existing API to parse OPP tables or document a reason why it
> > > > can't be used.
> > > 
> > > Thanks, I had them documented as opens in the coverletter. Dropped them
> > > since no one had any comments on it during V3. Will add them as comments
> > > to this driver instead.
> > > 
> > > https://lore.kernel.org/lkml/20240702191440.2161623-1-quic_sibis@quicinc.com/
> > > 
> > > re-copying things again:
> > > opp-tables are used but they don't get to be added to the scmi device
> > > (thus we rely on a lot of manual parsing) because the memlat client driver
> > > doesn't vote on these resources clocks/interconnects/power-domain
> > > from the kernel and some of the resources aren't modeled in the first
> > > place like DDR_QOS.
> > 
> > As discussed offline, please consider extending the OPP to be able to
> > get the struct opp_table for the particular phandle. Another option
> > might be to change the memlat driver by having a separate device for
> > each monitor. This way you can use existing API to parse OPP tables and
> > to get necessary data from those tables.
> 
> + Viresh
> 
> Spoke with Viresh offline and he had stricter requirements
> than what you proposed. He definitely wanted the opp-tables
> to be assoiciated with devices at the very least and have
> all opp parsing logic within the opp-framework. Given that
> we have to model all these dummy devices just to add the
> tables I'll re-check the feasibility of movign the tables
> into the driver itself. Will move the patch series back
> into RFC and re-post just the vendor protocol since that's
> close to merge

I don't think it's sensible to move the tables to the driver. Instead
adding a device per monitor sounds like a better idea.

> > > > > +	if (!tbl_np)
> > > > > +		return ERR_PTR(-ENODEV);
> > > > > +
> > > > > +	len = min(of_get_available_child_count(tbl_np), MAX_MAP_ENTRIES);
> > > > > +	if (len == 0)
> > > > > +		return ERR_PTR(-ENODEV);
> > > > > +
> > > > > +	tbl = devm_kzalloc(dev, (len + 1) * sizeof(struct cpufreq_memfreq_map),
> > > > > +			   GFP_KERNEL);
> > > > > +	if (!tbl)
> > > > > +		return ERR_PTR(-ENOMEM);
> > > > > +
> > > > > +	for_each_available_child_of_node(tbl_np, opp_np) {
> > > > > +		ret = of_property_read_u64_index(opp_np, "opp-hz", 0, &rate);
> > > > > +		if (ret < 0)
> > > > > +			return ERR_PTR(ret);
> > > > > +
> > > > > +		tbl[i].cpufreq_mhz = rate / HZ_PER_MHZ;
> > > > > +
> > > > > +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
> > > > > +			ret = of_property_read_u64_index(opp_np, "opp-hz", 1, &rate);
> > > > > +			if (ret < 0)
> > > > > +				return ERR_PTR(ret);
> > > > > +
> > > > > +			tbl[i].memfreq_khz = rate / HZ_PER_KHZ;
> > > > > +		} else {
> > > > > +			ret = of_property_read_u32(opp_np, "opp-level", &level);
> > > > > +			if (ret < 0)
> > > > > +				return ERR_PTR(ret);
> > > > > +
> > > > > +			tbl[i].memfreq_khz = level;
> > > > > +		}
> > > > > +
> > > > > +		dev_dbg(dev, "Entry%d CPU:%u, Mem:%u\n", i, tbl[i].cpufreq_mhz, tbl[i].memfreq_khz);
> > > > > +		i++;
> > > > > +	}
> > > > > +	*cnt = len;
> > > > > +
> > > > > +	return tbl;
> > > > > +}
> > > > > +
> > > > > +static int process_scmi_memlat_of_node(struct scmi_device *sdev, struct scmi_memlat_info *info)
> > > > > +{
> > > > > +	struct scmi_monitor_info *monitor;
> > > > > +	struct scmi_memory_info *memory;
> > > > > +	char name[MAX_NAME_LEN];
> > > > > +	u64 memfreq[2];
> > > > > +	int ret;
> > > > > +
> > > > > +	ret = populate_cluster_info(info->cluster_info);
> > > > > +	if (ret < 0) {
> > > > > +		dev_err_probe(&sdev->dev, ret, "failed to populate cluster info\n");
> > > > > +		goto err;
> > > > > +	}
> > > > > +
> > > > > +	of_node_get(sdev->dev.of_node);
> > > > > +	do {
> > > > > +		snprintf(name, sizeof(name), "memory-%d", info->memory_cnt);
> > > > > +		struct device_node *memory_np __free(device_node) =
> > > > > +			of_find_node_by_name(sdev->dev.of_node, name);
> > > > > +
> > > > > +		if (!memory_np)
> > > > > +			break;
> > > > > +
> > > > > +		if (info->memory_cnt >= MAX_MEMORY_TYPES)
> > > > > +			return dev_err_probe(&sdev->dev, -EINVAL,
> > > > > +					     "failed to parse unsupported memory type\n");
> > > > > +
> > > > > +		memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
> > > > > +		if (!memory) {
> > > > > +			ret = -ENOMEM;
> > > > > +			goto err;
> > > > > +		}
> > > > > +
> > > > > +		ret = of_property_read_u32(memory_np, "qcom,memory-type", &memory->hw_type);
> > > > > +		if (ret) {
> > > > > +			dev_err_probe(&sdev->dev, ret, "failed to read memory type\n");
> > > > > +			goto err;
> > > > > +		}
> > > > > +
> > > > > +		ret = of_property_read_u64_array(memory_np, "freq-table-hz", memfreq, 2);
> > > > > +		if (ret && (ret != -EINVAL)) {
> > > > > +			dev_err_probe(&sdev->dev, ret, "failed to read min/max freq\n");
> > > > > +			goto err;
> > > > > +		}
> > > > 
> > > > Can we get this information from the OPP table instead?
> > > 
> > > we don't list all the available ddr/llcc freqs in the opp-table
> > > so that we can keep the table constant across platforms.
> > 
> > NO. Use opp-supported-hw to limit data to a particular platform. There
> > is no reason to keep min/max out of the OPP table.
> 
> if we are movign the opp-tables into driver data for the reasons
> described above, this can probably stay?

No. They duplicate the information that can be a part of the tables. It
doesn't matter if the tables are in the driver or in DT.


-- 
With best wishes
Dmitry

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

* RE: Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
       [not found]         ` <CGME20241114041419epcas1p3b52bb9795ffd9efa568bb106ba268e02@epcms1p5>
@ 2024-11-15  0:38           ` MyungJoo Ham
  2024-12-05 10:17             ` Sibi Sankar
  0 siblings, 1 reply; 64+ messages in thread
From: MyungJoo Ham @ 2024-11-15  0:38 UTC (permalink / raw)
  To: Sibi Sankar, Dmitry Baryshkov, Kyungmin Park, Chanwoo Choi,
	Viresh Kumar
  Cc: sudeep.holla@arm.com, cristian.marussi@arm.com,
	andersson@kernel.org, konrad.dybcio@linaro.org,
	robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org,
	linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
	devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	quic_rgottimu@quicinc.com, quic_kshivnan@quicinc.com,
	conor+dt@kernel.org, arm-scmi@vger.kernel.org, Amir Vajid

>
>Hey Dmitry,
>
>Thanks for taking time to review the series.
>
>+ Devfreq maintainers to comment (I thought you already added
>them by name)
>
>
>Hey MyungJoo/Kyungmin/Chanwoo,
>
>Can you weigh in here? Does it make sense to add a new
>class of devfreq devices that don't have governors
>associated with them just for them to export a few
>essential data to userspace? In this scenario the
>scaling algorithm is in a SCP and we just start
>them from the kernel. We do have ways to get the
>current frequency of various buses but does this
>warrant adding a new class of governor less devices?
>
>-Sibi

If voltage/frequency is controlled by SCP
(it's an SoC's internal hardware IP, right?),
it's good to have a userspace governer
with the driver not accepting updates from userspace.

E.g., Let "target" callback not update the frequency value,
 or let "target" callback always return an error with
 a dev_err message that you don't accept frequency changes
 from userspace.

Cheers,
MyungJoo.

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-11-14  4:22       ` Sibi Sankar
@ 2024-11-22  8:37         ` Johan Hovold
  2024-12-05 10:56           ` Sibi Sankar
  2024-12-05 17:01           ` Sudeep Holla
  0 siblings, 2 replies; 64+ messages in thread
From: Johan Hovold @ 2024-11-22  8:37 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: Cristian Marussi, sudeep.holla, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi

On Thu, Nov 14, 2024 at 09:52:12AM +0530, Sibi Sankar wrote:
> On 11/8/24 20:44, Johan Hovold wrote:

> >> On Wed, Nov 06, 2024 at 01:55:33PM +0100, Johan Hovold wrote:

> >>> Second, after loading the protocol and client drivers manually (in that
> >>> order, shouldn't the client driver pull in the protocol?), I got:
> >>>
> >>> 	scmi_module: Loaded SCMI Vendor Protocol 0x80 - Qualcomm  20000
> >>> 	arm-scmi arm-scmi.0.auto: QCOM Generic Vendor Version 1.0
> >>> 	scmi-qcom-generic-ext-memlat scmi_dev.5: error -EOPNOTSUPP: failed to configure common events
> >>> 	scmi-qcom-generic-ext-memlat scmi_dev.5: probe with driver scmi-qcom-generic-ext-memlat failed with error -95
> >>>
> >>> which seems to suggest that the firmware on my CRD does not support this
> >>> feature. Is that the way this should be interpreted? And does that mean
> >>> that non of the commercial laptops supports this either?

> > Yeah, hopefully Sibi can shed some light on this. I'm using the DT
> > patch (5/5) from this series, which according to the commit message is
> > supposed to enable bus scaling on the x1e80100 platform. So I guess
> > something is missing in my firmware.
> 
> Nah, it's probably just because of the algo string used.
> The past few series used caps MEMLAT string instead of
> memlat to pass the tuneables, looks like all the laptops
> havn't really switched to it yet. Will revert back to
> using to lower case memlat so that all devices are
> supported. Thanks for trying the series out!

I have a Lenovo ThinkPad T14s set up now so I gave this series a spin
there too, and there I do *not* see the above mentioned -EOPNOSUPP error
and the memlat driver probes successfully.

On the other hand, this series seems to have no effect on a kernel
compilation benchmark. Is that expected?

And does this mean that you should stick with the uppercase "MEMLAT"
string after all? The firmware on my CRD is not the latest one, but I am
using the latest available firmware for the T14s.

Johan

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-10-07  6:10 ` [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor Sibi Sankar
                     ` (2 preceding siblings ...)
  2024-10-22 12:00   ` Cristian Marussi
@ 2024-11-29  9:57   ` Shivnandan Kumar
  2024-12-05 11:03     ` Sibi Sankar
  3 siblings, 1 reply; 64+ messages in thread
From: Shivnandan Kumar @ 2024-11-29  9:57 UTC (permalink / raw)
  To: Sibi Sankar, sudeep.holla, cristian.marussi, andersson,
	konrad.dybcio, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-kernel, linux-arm-msm, devicetree, linux-arm-kernel,
	quic_rgottimu, conor+dt, arm-scmi, Amir Vajid



On 10/7/2024 11:40 AM, Sibi Sankar wrote:
> Introduce a 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).
> 
> 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 <quic_sibis@quicinc.com>
> ---
> 
> v3:
> * 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]
> 
>   drivers/soc/qcom/Kconfig                   |  12 +
>   drivers/soc/qcom/Makefile                  |   1 +
>   drivers/soc/qcom/qcom_scmi_memlat_client.c | 569 +++++++++++++++++++++
>   3 files changed, 582 insertions(+)
>   create mode 100644 drivers/soc/qcom/qcom_scmi_memlat_client.c
> 
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index 74b9121240f8..1b6dd40d69ea 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -295,4 +295,16 @@ config QCOM_PBS
>   	  This module provides the APIs to the client drivers that wants to send the
>   	  PBS trigger event to the PBS RAM.
>   
> +config QCOM_SCMI_MEMLAT_CLIENT
> +	tristate "Qualcomm Technologies Inc. SCMI client driver"
> +	depends on QCOM_SCMI_GENERIC_EXT || COMPILE_TEST
> +	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.
> +
>   endmenu
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index acbca2ab5cc2..28549bb141bc 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -36,6 +36,7 @@ obj-$(CONFIG_QCOM_APR) += apr.o
>   obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o
>   obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) +=	kryo-l2-accessors.o
>   obj-$(CONFIG_QCOM_ICC_BWMON)	+= icc-bwmon.o
> +obj-$(CONFIG_QCOM_SCMI_MEMLAT_CLIENT)	+= qcom_scmi_memlat_client.o
>   qcom_ice-objs			+= ice.o
>   obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE)	+= qcom_ice.o
>   obj-$(CONFIG_QCOM_PBS) +=	qcom-pbs.o
> diff --git a/drivers/soc/qcom/qcom_scmi_memlat_client.c b/drivers/soc/qcom/qcom_scmi_memlat_client.c
> new file mode 100644
> index 000000000000..05198bf1f7ec
> --- /dev/null
> +++ b/drivers/soc/qcom/qcom_scmi_memlat_client.c
> @@ -0,0 +1,569 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#include <linux/cpu.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/scmi_protocol.h>
> +#include <linux/scmi_qcom_protocol.h>
> +#include <linux/units.h>
> +#include <dt-bindings/firmware/qcom,scmi-memlat.h>
> +
> +#define MEMLAT_ALGO_STR				0x4D454D4C4154 /* MEMLAT */
> +#define INVALID_IDX				0xff
> +#define MAX_MEMORY_TYPES			3
> +#define MAX_MONITOR_CNT				4
> +#define MAX_NAME_LEN				20
> +#define MAX_MAP_ENTRIES				7
> +#define CPUCP_DEFAULT_SAMPLING_PERIOD_MS	4
> +#define CPUCP_DEFAULT_FREQ_METHOD		1
> +
> +/**
> + * 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_SAMPLE_MS = 31,
> +	MEMLAT_MON_FREQ_MAP,
> +	MEMLAT_SET_MIN_FREQ,
> +	MEMLAT_SET_MAX_FREQ,
> +	MEMLAT_START_TIMER = 36,
> +	MEMLAT_STOP_TIMER,
> +	MEMLAT_SET_EFFECTIVE_FREQ_METHOD = 39,
> +};
> +
> +struct map_table {
> +	u16 v1;
> +	u16 v2;
> +};
> +
> +struct map_param_msg {
> +	u32 hw_type;
> +	u32 mon_idx;
> +	u32 nr_rows;
> +	struct 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
> +};
> +
> +#define EV_CPU_CYCLES		0
> +#define EV_INST_RETIRED		2
> +#define EV_L2_D_RFILL		5
> +
> +struct ev_map_msg {
> +	u32 num_evs;
> +	u32 hw_type;
> +	u32 cid[NUM_COMMON_EVS];
> +};
> +
> +struct cpufreq_memfreq_map {
> +	unsigned int cpufreq_mhz;
> +	unsigned int memfreq_khz;
> +};
> +
> +struct scmi_monitor_info {
> +	struct cpufreq_memfreq_map *freq_map;
> +	char mon_name[MAX_NAME_LEN];
> +	u32 mon_idx;
> +	u32 mon_type;
> +	u32 ipm_ceil;
> +	u32 mask;
> +	u32 freq_map_len;
> +};
> +
> +struct scmi_memory_info {
> +	struct scmi_monitor_info *monitor[MAX_MONITOR_CNT];
> +	u32 hw_type;
> +	int monitor_cnt;
> +	u32 min_freq;
> +	u32 max_freq;
> +};
> +
> +struct scmi_memlat_info {
> +	struct scmi_protocol_handle *ph;
> +	const struct qcom_generic_ext_ops *ops;
> +	struct scmi_memory_info *memory[MAX_MEMORY_TYPES];
> +	u32 cluster_info[NR_CPUS];
> +	int memory_cnt;
> +};
> +
> +static int populate_cluster_info(u32 *cluster_info)
> +{
> +	char name[MAX_NAME_LEN];
> +	int i = 0;
> +
> +	struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
> +	if (!cn)
> +		return -ENODEV;
> +
> +	struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
> +	if (!map)
> +		return -ENODEV;
> +
> +	do {
> +		snprintf(name, sizeof(name), "cluster%d", i);
> +		struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
> +		if (!c)
> +			break;
> +
> +		*(cluster_info + i) = of_get_child_count(c);
> +		i++;
> +	} while (1);
> +
> +	return 0;
> +}
> +
> +static void populate_physical_mask(struct device_node *np, u32 *mask, u32 *cluster_info)
> +{
> +	struct device_node *dev_phandle __free(device_node);
> +	int cpu, i = 0, physical_id;
> +
> +	do {
> +		dev_phandle = of_parse_phandle(np, "cpus", i++);
> +		cpu = of_cpu_node_to_id(dev_phandle);
> +		if (cpu != -ENODEV) {
> +			physical_id = topology_core_id(cpu);
> +			for (int j = 0; j < topology_cluster_id(cpu); j++)
> +				physical_id += *(cluster_info + j);
> +			*mask |= BIT(physical_id);
> +		}
> +	} while (dev_phandle);
> +}
> +
> +static struct cpufreq_memfreq_map *init_cpufreq_memfreq_map(struct device *dev,
> +							    struct scmi_memory_info *memory,
> +							    struct device_node *of_node,
> +							    u32 *cnt)
> +{
> +	struct device_node *tbl_np __free(device_node), *opp_np __free(device_node);
> +	struct cpufreq_memfreq_map *tbl;
> +	int ret, i = 0;
> +	u32 level, len;
> +	u64 rate;
> +
> +	tbl_np = of_parse_phandle(of_node, "operating-points-v2", 0);
> +	if (!tbl_np)
> +		return ERR_PTR(-ENODEV);
> +
> +	len = min(of_get_available_child_count(tbl_np), MAX_MAP_ENTRIES);
> +	if (len == 0)
> +		return ERR_PTR(-ENODEV);
> +
> +	tbl = devm_kzalloc(dev, (len + 1) * sizeof(struct cpufreq_memfreq_map),
> +			   GFP_KERNEL);
> +	if (!tbl)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for_each_available_child_of_node(tbl_np, opp_np) {
> +		ret = of_property_read_u64_index(opp_np, "opp-hz", 0, &rate);
> +		if (ret < 0)
> +			return ERR_PTR(ret);
> +
> +		tbl[i].cpufreq_mhz = rate / HZ_PER_MHZ;
> +
> +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
> +			ret = of_property_read_u64_index(opp_np, "opp-hz", 1, &rate);
> +			if (ret < 0)
> +				return ERR_PTR(ret);
> +
> +			tbl[i].memfreq_khz = rate / HZ_PER_KHZ;
> +		} else {
> +			ret = of_property_read_u32(opp_np, "opp-level", &level);
> +			if (ret < 0)
> +				return ERR_PTR(ret);
> +
> +			tbl[i].memfreq_khz = level;
> +		}
> +
> +		dev_dbg(dev, "Entry%d CPU:%u, Mem:%u\n", i, tbl[i].cpufreq_mhz, tbl[i].memfreq_khz);
> +		i++;
> +	}
> +	*cnt = len;
> +
> +	return tbl;
> +}
> +
> +static int process_scmi_memlat_of_node(struct scmi_device *sdev, struct scmi_memlat_info *info)
> +{
> +	struct scmi_monitor_info *monitor;
> +	struct scmi_memory_info *memory;
> +	char name[MAX_NAME_LEN];
> +	u64 memfreq[2];
> +	int ret;
> +
> +	ret = populate_cluster_info(info->cluster_info);
> +	if (ret < 0) {
> +		dev_err_probe(&sdev->dev, ret, "failed to populate cluster info\n");
> +		goto err;
> +	}
> +
> +	of_node_get(sdev->dev.of_node);
> +	do {
> +		snprintf(name, sizeof(name), "memory-%d", info->memory_cnt);
> +		struct device_node *memory_np __free(device_node) =
> +			of_find_node_by_name(sdev->dev.of_node, name);
> +
> +		if (!memory_np)
> +			break;
> +
> +		if (info->memory_cnt >= MAX_MEMORY_TYPES)
> +			return dev_err_probe(&sdev->dev, -EINVAL,
> +					     "failed to parse unsupported memory type\n");
> +
> +		memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
> +		if (!memory) {
> +			ret = -ENOMEM;
> +			goto err;
> +		}
> +
> +		ret = of_property_read_u32(memory_np, "qcom,memory-type", &memory->hw_type);
> +		if (ret) {
> +			dev_err_probe(&sdev->dev, ret, "failed to read memory type\n");
> +			goto err;
> +		}
> +
> +		ret = of_property_read_u64_array(memory_np, "freq-table-hz", memfreq, 2);
> +		if (ret && (ret != -EINVAL)) {
> +			dev_err_probe(&sdev->dev, ret, "failed to read min/max freq\n");
> +			goto err;
> +		}
> +
> +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
> +			memory->min_freq = memfreq[0] / HZ_PER_KHZ;
> +			memory->max_freq = memfreq[1] / HZ_PER_KHZ;
> +		} else {
> +			memory->min_freq = memfreq[0];
> +			memory->max_freq = memfreq[1];
> +		}
> +		info->memory[info->memory_cnt++] = memory;
> +
> +		do {
> +			snprintf(name, sizeof(name), "monitor-%d", memory->monitor_cnt);
> +			struct device_node *monitor_np __free(device_node) =
> +				of_get_child_by_name(memory_np, name);
> +
> +			if (!monitor_np)
> +				break;
> +
> +			if (memory->monitor_cnt >= MAX_MONITOR_CNT)
> +				return dev_err_probe(&sdev->dev, -EINVAL,
> +						     "failed to parse unsupported monitor\n");
> +
> +			monitor = devm_kzalloc(&sdev->dev, sizeof(*monitor), GFP_KERNEL);
> +			if (!monitor) {
> +				ret = -ENOMEM;
> +				goto err;
> +			}
> +
> +			monitor->mon_type = of_property_read_bool(monitor_np, "qcom,compute-type");
> +			if (!monitor->mon_type) {
> +				ret = of_property_read_u32(monitor_np, "qcom,ipm-ceil",
> +							   &monitor->ipm_ceil);
> +				if (ret) {
> +					dev_err_probe(&sdev->dev, ret,
> +						      "failed to read IPM ceiling\n");
> +					goto err;
> +				}
> +			}
> +
> +			/*
> +			 * Variants of the SoC having reduced number of cpus operate
> +			 * with the same number of logical cpus but the physical
> +			 * cpu disabled will differ between parts. Calculate the
> +			 * physical cpu number using cluster information instead.
> +			 */
> +			populate_physical_mask(monitor_np, &monitor->mask, info->cluster_info);
> +
> +			monitor->freq_map = init_cpufreq_memfreq_map(&sdev->dev, memory, monitor_np,
> +								     &monitor->freq_map_len);
> +			if (IS_ERR(monitor->freq_map)) {
> +				dev_err_probe(&sdev->dev, PTR_ERR(monitor->freq_map),
> +					      "failed to populate cpufreq-memfreq map\n");
> +				goto err;
> +			}
> +
> +			strscpy(monitor->mon_name, name, sizeof(monitor->mon_name));
> +			monitor->mon_idx = memory->monitor_cnt;
> +
> +			memory->monitor[memory->monitor_cnt++] = monitor;
> +		} while (1);
> +
> +		if (!memory->monitor_cnt) {
> +			ret = -EINVAL;
> +			dev_err_probe(&sdev->dev, ret, "failed to find monitor nodes\n");
> +			goto err;
> +		}
> +	} while (1);
> +
> +	if (!info->memory_cnt) {
> +		ret = -EINVAL;
> +		dev_err_probe(&sdev->dev, ret, "failed to find memory nodes\n");
> +	}
> +
> +err:
> +	of_node_put(sdev->dev.of_node);
> +
> +	return ret;
> +}
> +
> +static int configure_cpucp_common_events(struct scmi_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.hw_type = INVALID_IDX;
> +	msg.cid[INST_IDX] = EV_INST_RETIRED;
> +	msg.cid[CYC_IDX] = EV_CPU_CYCLES;
> +	msg.cid[CONST_CYC_IDX] = INVALID_IDX;
> +	msg.cid[FE_STALL_IDX] = INVALID_IDX;
> +	msg.cid[BE_STALL_IDX] = INVALID_IDX;
> +
> +	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_memlat_info *info, int memory_index)
> +{
> +	const struct qcom_generic_ext_ops *ops = info->ops;
> +	struct scmi_memory_info *memory = info->memory[memory_index];
> +	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_memlat_info *info,
> +			       int memory_index, int monitor_index)
> +{
> +	const struct qcom_generic_ext_ops *ops = info->ops;
> +	struct scmi_memory_info *memory = info->memory[memory_index];
> +	struct scmi_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->mon_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->mon_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->mon_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;
> +		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->mon_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->mon_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->mon_name);
> +
> +	return ret;
> +}
> +
> +static int cpucp_memlat_init(struct scmi_device *sdev)
> +{
> +	const struct scmi_handle *handle = sdev->handle;
> +	const struct qcom_generic_ext_ops *ops;
> +	struct scmi_protocol_handle *ph;
> +	struct scmi_memlat_info *info;
> +	u32 cpucp_freq_method = CPUCP_DEFAULT_FREQ_METHOD;
> +	u32 cpucp_sample_ms = CPUCP_DEFAULT_SAMPLING_PERIOD_MS;
> +	int ret, i, j;
> +
> +	if (!handle)
> +		return -ENODEV;
> +
> +	ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_QCOM_GENERIC, &ph);
> +	if (IS_ERR(ops))
> +		return PTR_ERR(ops);
> +
> +	info = devm_kzalloc(&sdev->dev, sizeof(*info), GFP_KERNEL);
> +	if (!info)
> +		return -ENOMEM;
> +
> +	ret = process_scmi_memlat_of_node(sdev, info);
> +	if (ret)
> +		return ret;
> +
> +	info->ph = ph;
> +	info->ops = ops;
> +
> +	/* 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, &cpucp_sample_ms, sizeof(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, &cpucp_freq_method, sizeof(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");
> +

Hi Sibi,
Since the MEMLAT_SET_EFFECTIVE_FREQ_METHOD command is not supported in 
the legacy CPUCP firmware, it should be kept optional. This way, if the 
legacy firmware is used, the driver will not return an error when the 
CPUCP firmware returns -EOPNOTSUPP.

Thanks,
Shivnandan

> +	/* 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");
> +
> +	return ret;
> +}
> +
> +static int scmi_client_probe(struct scmi_device *sdev)
> +{
> +	return cpucp_memlat_init(sdev);
> +}
> +
> +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 qcom_scmi_client_drv = {
> +	.name		= "scmi-qcom-generic-ext-memlat",
> +	.probe		= scmi_client_probe,
> +	.id_table	= scmi_id_table,
> +};
> +module_scmi_driver(qcom_scmi_client_drv);
> +
> +MODULE_DESCRIPTION("QTI SCMI client driver");
> +MODULE_LICENSE("GPL");

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-11-15  0:38           ` MyungJoo Ham
@ 2024-12-05 10:17             ` Sibi Sankar
  0 siblings, 0 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-12-05 10:17 UTC (permalink / raw)
  To: myungjoo.ham, Dmitry Baryshkov, Kyungmin Park, Chanwoo Choi,
	Viresh Kumar
  Cc: sudeep.holla@arm.com, cristian.marussi@arm.com,
	andersson@kernel.org, konrad.dybcio@linaro.org,
	robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org,
	linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
	devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	quic_rgottimu@quicinc.com, quic_kshivnan@quicinc.com,
	conor+dt@kernel.org, arm-scmi@vger.kernel.org, Amir Vajid



On 11/15/24 06:08, MyungJoo Ham wrote:
>>
>> Hey Dmitry,
>>
>> Thanks for taking time to review the series.
>>
>> + Devfreq maintainers to comment (I thought you already added
>> them by name)
>>
>>
>> Hey MyungJoo/Kyungmin/Chanwoo,
>>
>> Can you weigh in here? Does it make sense to add a new
>> class of devfreq devices that don't have governors
>> associated with them just for them to export a few
>> essential data to userspace? In this scenario the
>> scaling algorithm is in a SCP and we just start
>> them from the kernel. We do have ways to get the
>> current frequency of various buses but does this
>> warrant adding a new class of governor less devices?
>>
>> -Sibi
> 
> If voltage/frequency is controlled by SCP
> (it's an SoC's internal hardware IP, right?),
> it's good to have a userspace governer
> with the driver not accepting updates from userspace.
> 
> E.g., Let "target" callback not update the frequency value,
>   or let "target" callback always return an error with
>   a dev_err message that you don't accept frequency changes
>   from userspace.

Thanks for the input. Will implement something similar to
what you suggested for the next re-spin.

-Sibi

> 
> Cheers,
> MyungJoo.

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-11-14 12:32           ` Dmitry Baryshkov
@ 2024-12-05 10:52             ` Sibi Sankar
  2024-12-05 11:30               ` Dmitry Baryshkov
  0 siblings, 1 reply; 64+ messages in thread
From: Sibi Sankar @ 2024-12-05 10:52 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: myungjoo.ham, Kyungmin.park, cw00.choi, Viresh Kumar,
	sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid



On 11/14/24 18:02, Dmitry Baryshkov wrote:
> On Thu, Nov 14, 2024 at 09:43:53AM +0530, Sibi Sankar wrote:
>>
>>
>> On 10/26/24 23:46, Dmitry Baryshkov wrote:
>>> On Tue, Oct 22, 2024 at 01:48:25PM +0530, Sibi Sankar wrote:
>>>>
>>>>
>>>> On 10/7/24 23:27, Dmitry Baryshkov wrote:
>>>>> On Mon, Oct 07, 2024 at 11:40:22AM GMT, Sibi Sankar wrote:
> 
>>>>>
>>>>>> +};
>>>>>> +
>>>>>> +struct map_param_msg {
>>>>>> +	u32 hw_type;
>>>>>> +	u32 mon_idx;
>>>>>> +	u32 nr_rows;
>>>>>> +	struct 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
>>>>>> +};
>>>>>> +
>>>>>> +#define EV_CPU_CYCLES		0
>>>>>> +#define EV_INST_RETIRED		2
>>>>>> +#define EV_L2_D_RFILL		5
>>>>>> +
>>>>>> +struct ev_map_msg {
>>>>>> +	u32 num_evs;
>>>>>> +	u32 hw_type;
>>>>>> +	u32 cid[NUM_COMMON_EVS];
>>>>>> +};
>>>>>> +
>>>>>> +struct cpufreq_memfreq_map {
>>>>>> +	unsigned int cpufreq_mhz;
>>>>>> +	unsigned int memfreq_khz;
>>>>>> +};
>>>>>> +
>>>>>> +struct scmi_monitor_info {
>>>>>> +	struct cpufreq_memfreq_map *freq_map;
>>>>>> +	char mon_name[MAX_NAME_LEN];
>>>>>> +	u32 mon_idx;
>>>>>> +	u32 mon_type;
>>>>>> +	u32 ipm_ceil;
>>>>>> +	u32 mask;
>>>>>> +	u32 freq_map_len;
>>>>>> +};
>>>>>> +
>>>>>> +struct scmi_memory_info {
>>>>>> +	struct scmi_monitor_info *monitor[MAX_MONITOR_CNT];
>>>>>> +	u32 hw_type;
>>>>>> +	int monitor_cnt;
>>>>>> +	u32 min_freq;
>>>>>> +	u32 max_freq;
>>>>>> +};
>>>>>> +
>>>>>> +struct scmi_memlat_info {
>>>>>> +	struct scmi_protocol_handle *ph;
>>>>>> +	const struct qcom_generic_ext_ops *ops;
>>>>>> +	struct scmi_memory_info *memory[MAX_MEMORY_TYPES];
>>>>>> +	u32 cluster_info[NR_CPUS];
>>>>>> +	int memory_cnt;
>>>>>> +};
>>>>>> +
>>>>>> +static int populate_cluster_info(u32 *cluster_info)
>>>>>> +{
>>>>>> +	char name[MAX_NAME_LEN];
>>>>>> +	int i = 0;
>>>>>> +
>>>>>> +	struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
>>>>>> +	if (!cn)
>>>>>> +		return -ENODEV;
>>>>>> +
>>>>>> +	struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
>>>>>> +	if (!map)
>>>>>> +		return -ENODEV;
>>>>>> +
>>>>>> +	do {
>>>>>> +		snprintf(name, sizeof(name), "cluster%d", i);
>>>>>> +		struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
>>>>>> +		if (!c)
>>>>>> +			break;
>>>>>> +
>>>>>> +		*(cluster_info + i) = of_get_child_count(c);
>>>>>> +		i++;
>>>>>> +	} while (1);
>>>>>
>>>>> Can you use existing API from drivers/base/arch_topology.c? If not, can
>>>>> it be extended to support your usecase?
>>>>
>>>> ack. But I'm pretty sure it's going to take a while for reaching such
>>>> an agreement so I'll drop this feature during the next re-spin.
>>>
>>> Why? What kind of API do you actually need? The arch_topology.c simply
>>> exports a table of struct cpu_topology. Is it somehow different from
>>> what you are parsing manually?
>>
>> yup, we had to figure out the physical id of the cpu
>> since cpus can be disabled by the bootloader using
>> status = "failed" property and we have to pass this
>> onto the cpucp memlat algorithm.
> 
> Isn't it equal to the index in the cpu_topology table?

from what I see cpu_topology indexes remain unpopulated
for cpus that are disabled since get_cpu_for_node
ignores those?

> 
>>
>>>
>>>>
>>>>>
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static void populate_physical_mask(struct device_node *np, u32 *mask, u32 *cluster_info)
>>>>>> +{
>>>>>> +	struct device_node *dev_phandle __free(device_node);
>>>>>> +	int cpu, i = 0, physical_id;
>>>>>> +
>>>>>> +	do {
>>>>>> +		dev_phandle = of_parse_phandle(np, "cpus", i++);
>>>>>> +		cpu = of_cpu_node_to_id(dev_phandle);
>>>>>> +		if (cpu != -ENODEV) {
>>>>>> +			physical_id = topology_core_id(cpu);
>>>>>> +			for (int j = 0; j < topology_cluster_id(cpu); j++)
>>>>>> +				physical_id += *(cluster_info + j);
>>>>>> +			*mask |= BIT(physical_id);
>>>>>> +		}
>>>>>> +	} while (dev_phandle);
>>>>>> +}
>>>>>> +
>>>>>> +static struct cpufreq_memfreq_map *init_cpufreq_memfreq_map(struct device *dev,
>>>>>> +							    struct scmi_memory_info *memory,
>>>>>> +							    struct device_node *of_node,
>>>>>> +							    u32 *cnt)
>>>>>> +{
>>>>>> +	struct device_node *tbl_np __free(device_node), *opp_np __free(device_node);
>>>>>> +	struct cpufreq_memfreq_map *tbl;
>>>>>> +	int ret, i = 0;
>>>>>> +	u32 level, len;
>>>>>> +	u64 rate;
>>>>>> +
>>>>>> +	tbl_np = of_parse_phandle(of_node, "operating-points-v2", 0);
>>>>>
>>>>> Please use existing API to parse OPP tables or document a reason why it
>>>>> can't be used.
>>>>
>>>> Thanks, I had them documented as opens in the coverletter. Dropped them
>>>> since no one had any comments on it during V3. Will add them as comments
>>>> to this driver instead.
>>>>
>>>> https://lore.kernel.org/lkml/20240702191440.2161623-1-quic_sibis@quicinc.com/
>>>>
>>>> re-copying things again:
>>>> opp-tables are used but they don't get to be added to the scmi device
>>>> (thus we rely on a lot of manual parsing) because the memlat client driver
>>>> doesn't vote on these resources clocks/interconnects/power-domain
>>>> from the kernel and some of the resources aren't modeled in the first
>>>> place like DDR_QOS.
>>>
>>> As discussed offline, please consider extending the OPP to be able to
>>> get the struct opp_table for the particular phandle. Another option
>>> might be to change the memlat driver by having a separate device for
>>> each monitor. This way you can use existing API to parse OPP tables and
>>> to get necessary data from those tables.
>>
>> + Viresh
>>
>> Spoke with Viresh offline and he had stricter requirements
>> than what you proposed. He definitely wanted the opp-tables
>> to be assoiciated with devices at the very least and have
>> all opp parsing logic within the opp-framework. Given that
>> we have to model all these dummy devices just to add the
>> tables I'll re-check the feasibility of movign the tables
>> into the driver itself. Will move the patch series back
>> into RFC and re-post just the vendor protocol since that's
>> close to merge
> 
> I don't think it's sensible to move the tables to the driver. Instead
> adding a device per monitor sounds like a better idea.

yeah, I would like to keep this in dt as well. But in order
to be able to do that through the opp core we would need
to put in a clock and interconnect paths so that the framework
is able to add the table to the device. I should be able
to list the scmi perf domain as the clock phandle but inorder
to be able to convert the kbps values for the interconnect I
would need to store the bus width and so on which is currently
abstracted by the interconnect framework. Also in the future
we may have to model dummy devices just to get the table parsed
if the devices aren't modelled and controlled in the kernel.
All of these seems to indicate that having the tables in the
driver is a better alternative, lol.

-Sibi

> 
>>>>>> +	if (!tbl_np)
>>>>>> +		return ERR_PTR(-ENODEV);
>>>>>> +
>>>>>> +	len = min(of_get_available_child_count(tbl_np), MAX_MAP_ENTRIES);
>>>>>> +	if (len == 0)
>>>>>> +		return ERR_PTR(-ENODEV);
>>>>>> +
>>>>>> +	tbl = devm_kzalloc(dev, (len + 1) * sizeof(struct cpufreq_memfreq_map),
>>>>>> +			   GFP_KERNEL);
>>>>>> +	if (!tbl)
>>>>>> +		return ERR_PTR(-ENOMEM);
>>>>>> +
>>>>>> +	for_each_available_child_of_node(tbl_np, opp_np) {
>>>>>> +		ret = of_property_read_u64_index(opp_np, "opp-hz", 0, &rate);
>>>>>> +		if (ret < 0)
>>>>>> +			return ERR_PTR(ret);
>>>>>> +
>>>>>> +		tbl[i].cpufreq_mhz = rate / HZ_PER_MHZ;
>>>>>> +
>>>>>> +		if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
>>>>>> +			ret = of_property_read_u64_index(opp_np, "opp-hz", 1, &rate);
>>>>>> +			if (ret < 0)
>>>>>> +				return ERR_PTR(ret);
>>>>>> +
>>>>>> +			tbl[i].memfreq_khz = rate / HZ_PER_KHZ;
>>>>>> +		} else {
>>>>>> +			ret = of_property_read_u32(opp_np, "opp-level", &level);
>>>>>> +			if (ret < 0)
>>>>>> +				return ERR_PTR(ret);
>>>>>> +
>>>>>> +			tbl[i].memfreq_khz = level;
>>>>>> +		}
>>>>>> +
>>>>>> +		dev_dbg(dev, "Entry%d CPU:%u, Mem:%u\n", i, tbl[i].cpufreq_mhz, tbl[i].memfreq_khz);
>>>>>> +		i++;
>>>>>> +	}
>>>>>> +	*cnt = len;
>>>>>> +
>>>>>> +	return tbl;
>>>>>> +}
>>>>>> +
>>>>>> +static int process_scmi_memlat_of_node(struct scmi_device *sdev, struct scmi_memlat_info *info)
>>>>>> +{
>>>>>> +	struct scmi_monitor_info *monitor;
>>>>>> +	struct scmi_memory_info *memory;
>>>>>> +	char name[MAX_NAME_LEN];
>>>>>> +	u64 memfreq[2];
>>>>>> +	int ret;
>>>>>> +
>>>>>> +	ret = populate_cluster_info(info->cluster_info);
>>>>>> +	if (ret < 0) {
>>>>>> +		dev_err_probe(&sdev->dev, ret, "failed to populate cluster info\n");
>>>>>> +		goto err;
>>>>>> +	}
>>>>>> +
>>>>>> +	of_node_get(sdev->dev.of_node);
>>>>>> +	do {
>>>>>> +		snprintf(name, sizeof(name), "memory-%d", info->memory_cnt);
>>>>>> +		struct device_node *memory_np __free(device_node) =
>>>>>> +			of_find_node_by_name(sdev->dev.of_node, name);
>>>>>> +
>>>>>> +		if (!memory_np)
>>>>>> +			break;
>>>>>> +
>>>>>> +		if (info->memory_cnt >= MAX_MEMORY_TYPES)
>>>>>> +			return dev_err_probe(&sdev->dev, -EINVAL,
>>>>>> +					     "failed to parse unsupported memory type\n");
>>>>>> +
>>>>>> +		memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
>>>>>> +		if (!memory) {
>>>>>> +			ret = -ENOMEM;
>>>>>> +			goto err;
>>>>>> +		}
>>>>>> +
>>>>>> +		ret = of_property_read_u32(memory_np, "qcom,memory-type", &memory->hw_type);
>>>>>> +		if (ret) {
>>>>>> +			dev_err_probe(&sdev->dev, ret, "failed to read memory type\n");
>>>>>> +			goto err;
>>>>>> +		}
>>>>>> +
>>>>>> +		ret = of_property_read_u64_array(memory_np, "freq-table-hz", memfreq, 2);
>>>>>> +		if (ret && (ret != -EINVAL)) {
>>>>>> +			dev_err_probe(&sdev->dev, ret, "failed to read min/max freq\n");
>>>>>> +			goto err;
>>>>>> +		}
>>>>>
>>>>> Can we get this information from the OPP table instead?
>>>>
>>>> we don't list all the available ddr/llcc freqs in the opp-table
>>>> so that we can keep the table constant across platforms.
>>>
>>> NO. Use opp-supported-hw to limit data to a particular platform. There
>>> is no reason to keep min/max out of the OPP table.
>>
>> if we are movign the opp-tables into driver data for the reasons
>> described above, this can probably stay?
> 
> No. They duplicate the information that can be a part of the tables. It
> doesn't matter if the tables are in the driver or in DT.
> 
> 

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-11-22  8:37         ` Johan Hovold
@ 2024-12-05 10:56           ` Sibi Sankar
  2024-12-05 15:52             ` Johan Hovold
  2024-12-05 17:01           ` Sudeep Holla
  1 sibling, 1 reply; 64+ messages in thread
From: Sibi Sankar @ 2024-12-05 10:56 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Cristian Marussi, sudeep.holla, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi



On 11/22/24 14:07, Johan Hovold wrote:
> On Thu, Nov 14, 2024 at 09:52:12AM +0530, Sibi Sankar wrote:
>> On 11/8/24 20:44, Johan Hovold wrote:
> 
>>>> On Wed, Nov 06, 2024 at 01:55:33PM +0100, Johan Hovold wrote:
> 
>>>>> Second, after loading the protocol and client drivers manually (in that
>>>>> order, shouldn't the client driver pull in the protocol?), I got:
>>>>>
>>>>> 	scmi_module: Loaded SCMI Vendor Protocol 0x80 - Qualcomm  20000
>>>>> 	arm-scmi arm-scmi.0.auto: QCOM Generic Vendor Version 1.0
>>>>> 	scmi-qcom-generic-ext-memlat scmi_dev.5: error -EOPNOTSUPP: failed to configure common events
>>>>> 	scmi-qcom-generic-ext-memlat scmi_dev.5: probe with driver scmi-qcom-generic-ext-memlat failed with error -95
>>>>>
>>>>> which seems to suggest that the firmware on my CRD does not support this
>>>>> feature. Is that the way this should be interpreted? And does that mean
>>>>> that non of the commercial laptops supports this either?
> 
>>> Yeah, hopefully Sibi can shed some light on this. I'm using the DT
>>> patch (5/5) from this series, which according to the commit message is
>>> supposed to enable bus scaling on the x1e80100 platform. So I guess
>>> something is missing in my firmware.
>>
>> Nah, it's probably just because of the algo string used.
>> The past few series used caps MEMLAT string instead of
>> memlat to pass the tuneables, looks like all the laptops
>> havn't really switched to it yet. Will revert back to
>> using to lower case memlat so that all devices are
>> supported. Thanks for trying the series out!
> 
> I have a Lenovo ThinkPad T14s set up now so I gave this series a spin
> there too, and there I do *not* see the above mentioned -EOPNOSUPP error
> and the memlat driver probes successfully.
> 
> On the other hand, this series seems to have no effect on a kernel
> compilation benchmark. Is that expected?

I can have a look at your tree. But memlat in general
depends on the cpu frequency when your benchmarks max
the cpu's the ddr/llcc are scaled accordingly by it.

> 
> And does this mean that you should stick with the uppercase "MEMLAT"
> string after all? The firmware on my CRD is not the latest one, but I am
> using the latest available firmware for the T14s.

We should stick with "memlat" if we run into a device in the
wild that doesn't support "MEMLAT"

-Sibi

> 
> Johan
> 

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-11-29  9:57   ` Shivnandan Kumar
@ 2024-12-05 11:03     ` Sibi Sankar
  2024-12-05 12:39       ` Cristian Marussi
  0 siblings, 1 reply; 64+ messages in thread
From: Sibi Sankar @ 2024-12-05 11:03 UTC (permalink / raw)
  To: Shivnandan Kumar, sudeep.holla, cristian.marussi, andersson,
	konrad.dybcio, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-kernel, linux-arm-msm, devicetree, linux-arm-kernel,
	quic_rgottimu, conor+dt, arm-scmi, Amir Vajid



On 11/29/24 15:27, Shivnandan Kumar wrote:
> 
> 
> On 10/7/2024 11:40 AM, Sibi Sankar wrote:
>> Introduce a 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).
>>
>> 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 <quic_sibis@quicinc.com>
>> ---
>>
>> v3:
>> * 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]
>>
>>   drivers/soc/qcom/Kconfig                   |  12 +
>>   drivers/soc/qcom/Makefile                  |   1 +
>>   drivers/soc/qcom/qcom_scmi_memlat_client.c | 569 +++++++++++++++++++++
>>   3 files changed, 582 insertions(+)
>>   create mode 100644 drivers/soc/qcom/qcom_scmi_memlat_client.c
>>
>> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
>> index 74b9121240f8..1b6dd40d69ea 100644
>> --- a/drivers/soc/qcom/Kconfig
>> +++ b/drivers/soc/qcom/Kconfig
>> @@ -295,4 +295,16 @@ config QCOM_PBS
>>         This module provides the APIs to the client drivers that wants 
>> to send the
>>         PBS trigger event to the PBS RAM.
>> +config QCOM_SCMI_MEMLAT_CLIENT
>> +    tristate "Qualcomm Technologies Inc. SCMI client driver"
>> +    depends on QCOM_SCMI_GENERIC_EXT || COMPILE_TEST
>> +    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.
>> +
>>   endmenu
>> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
>> index acbca2ab5cc2..28549bb141bc 100644
>> --- a/drivers/soc/qcom/Makefile
>> +++ b/drivers/soc/qcom/Makefile
>> @@ -36,6 +36,7 @@ obj-$(CONFIG_QCOM_APR) += apr.o
>>   obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o
>>   obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) +=    kryo-l2-accessors.o
>>   obj-$(CONFIG_QCOM_ICC_BWMON)    += icc-bwmon.o
>> +obj-$(CONFIG_QCOM_SCMI_MEMLAT_CLIENT)    += qcom_scmi_memlat_client.o
>>   qcom_ice-objs            += ice.o
>>   obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE)    += qcom_ice.o
>>   obj-$(CONFIG_QCOM_PBS) +=    qcom-pbs.o
>> diff --git a/drivers/soc/qcom/qcom_scmi_memlat_client.c 
>> b/drivers/soc/qcom/qcom_scmi_memlat_client.c
>> new file mode 100644
>> index 000000000000..05198bf1f7ec
>> --- /dev/null
>> +++ b/drivers/soc/qcom/qcom_scmi_memlat_client.c
>> @@ -0,0 +1,569 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights 
>> reserved.
>> + */
>> +
>> +#include <linux/cpu.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/scmi_protocol.h>
>> +#include <linux/scmi_qcom_protocol.h>
>> +#include <linux/units.h>
>> +#include <dt-bindings/firmware/qcom,scmi-memlat.h>
>> +
>> +#define MEMLAT_ALGO_STR                0x4D454D4C4154 /* MEMLAT */
>> +#define INVALID_IDX                0xff
>> +#define MAX_MEMORY_TYPES            3
>> +#define MAX_MONITOR_CNT                4
>> +#define MAX_NAME_LEN                20
>> +#define MAX_MAP_ENTRIES                7
>> +#define CPUCP_DEFAULT_SAMPLING_PERIOD_MS    4
>> +#define CPUCP_DEFAULT_FREQ_METHOD        1
>> +
>> +/**
>> + * 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_SAMPLE_MS = 31,
>> +    MEMLAT_MON_FREQ_MAP,
>> +    MEMLAT_SET_MIN_FREQ,
>> +    MEMLAT_SET_MAX_FREQ,
>> +    MEMLAT_START_TIMER = 36,
>> +    MEMLAT_STOP_TIMER,
>> +    MEMLAT_SET_EFFECTIVE_FREQ_METHOD = 39,
>> +};
>> +
>> +struct map_table {
>> +    u16 v1;
>> +    u16 v2;
>> +};
>> +
>> +struct map_param_msg {
>> +    u32 hw_type;
>> +    u32 mon_idx;
>> +    u32 nr_rows;
>> +    struct 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
>> +};
>> +
>> +#define EV_CPU_CYCLES        0
>> +#define EV_INST_RETIRED        2
>> +#define EV_L2_D_RFILL        5
>> +
>> +struct ev_map_msg {
>> +    u32 num_evs;
>> +    u32 hw_type;
>> +    u32 cid[NUM_COMMON_EVS];
>> +};
>> +
>> +struct cpufreq_memfreq_map {
>> +    unsigned int cpufreq_mhz;
>> +    unsigned int memfreq_khz;
>> +};
>> +
>> +struct scmi_monitor_info {
>> +    struct cpufreq_memfreq_map *freq_map;
>> +    char mon_name[MAX_NAME_LEN];
>> +    u32 mon_idx;
>> +    u32 mon_type;
>> +    u32 ipm_ceil;
>> +    u32 mask;
>> +    u32 freq_map_len;
>> +};
>> +
>> +struct scmi_memory_info {
>> +    struct scmi_monitor_info *monitor[MAX_MONITOR_CNT];
>> +    u32 hw_type;
>> +    int monitor_cnt;
>> +    u32 min_freq;
>> +    u32 max_freq;
>> +};
>> +
>> +struct scmi_memlat_info {
>> +    struct scmi_protocol_handle *ph;
>> +    const struct qcom_generic_ext_ops *ops;
>> +    struct scmi_memory_info *memory[MAX_MEMORY_TYPES];
>> +    u32 cluster_info[NR_CPUS];
>> +    int memory_cnt;
>> +};
>> +
>> +static int populate_cluster_info(u32 *cluster_info)
>> +{
>> +    char name[MAX_NAME_LEN];
>> +    int i = 0;
>> +
>> +    struct device_node *cn __free(device_node) = 
>> of_find_node_by_path("/cpus");
>> +    if (!cn)
>> +        return -ENODEV;
>> +
>> +    struct device_node *map __free(device_node) = 
>> of_get_child_by_name(cn, "cpu-map");
>> +    if (!map)
>> +        return -ENODEV;
>> +
>> +    do {
>> +        snprintf(name, sizeof(name), "cluster%d", i);
>> +        struct device_node *c __free(device_node) = 
>> of_get_child_by_name(map, name);
>> +        if (!c)
>> +            break;
>> +
>> +        *(cluster_info + i) = of_get_child_count(c);
>> +        i++;
>> +    } while (1);
>> +
>> +    return 0;
>> +}
>> +
>> +static void populate_physical_mask(struct device_node *np, u32 *mask, 
>> u32 *cluster_info)
>> +{
>> +    struct device_node *dev_phandle __free(device_node);
>> +    int cpu, i = 0, physical_id;
>> +
>> +    do {
>> +        dev_phandle = of_parse_phandle(np, "cpus", i++);
>> +        cpu = of_cpu_node_to_id(dev_phandle);
>> +        if (cpu != -ENODEV) {
>> +            physical_id = topology_core_id(cpu);
>> +            for (int j = 0; j < topology_cluster_id(cpu); j++)
>> +                physical_id += *(cluster_info + j);
>> +            *mask |= BIT(physical_id);
>> +        }
>> +    } while (dev_phandle);
>> +}
>> +
>> +static struct cpufreq_memfreq_map *init_cpufreq_memfreq_map(struct 
>> device *dev,
>> +                                struct scmi_memory_info *memory,
>> +                                struct device_node *of_node,
>> +                                u32 *cnt)
>> +{
>> +    struct device_node *tbl_np __free(device_node), *opp_np 
>> __free(device_node);
>> +    struct cpufreq_memfreq_map *tbl;
>> +    int ret, i = 0;
>> +    u32 level, len;
>> +    u64 rate;
>> +
>> +    tbl_np = of_parse_phandle(of_node, "operating-points-v2", 0);
>> +    if (!tbl_np)
>> +        return ERR_PTR(-ENODEV);
>> +
>> +    len = min(of_get_available_child_count(tbl_np), MAX_MAP_ENTRIES);
>> +    if (len == 0)
>> +        return ERR_PTR(-ENODEV);
>> +
>> +    tbl = devm_kzalloc(dev, (len + 1) * sizeof(struct 
>> cpufreq_memfreq_map),
>> +               GFP_KERNEL);
>> +    if (!tbl)
>> +        return ERR_PTR(-ENOMEM);
>> +
>> +    for_each_available_child_of_node(tbl_np, opp_np) {
>> +        ret = of_property_read_u64_index(opp_np, "opp-hz", 0, &rate);
>> +        if (ret < 0)
>> +            return ERR_PTR(ret);
>> +
>> +        tbl[i].cpufreq_mhz = rate / HZ_PER_MHZ;
>> +
>> +        if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
>> +            ret = of_property_read_u64_index(opp_np, "opp-hz", 1, 
>> &rate);
>> +            if (ret < 0)
>> +                return ERR_PTR(ret);
>> +
>> +            tbl[i].memfreq_khz = rate / HZ_PER_KHZ;
>> +        } else {
>> +            ret = of_property_read_u32(opp_np, "opp-level", &level);
>> +            if (ret < 0)
>> +                return ERR_PTR(ret);
>> +
>> +            tbl[i].memfreq_khz = level;
>> +        }
>> +
>> +        dev_dbg(dev, "Entry%d CPU:%u, Mem:%u\n", i, 
>> tbl[i].cpufreq_mhz, tbl[i].memfreq_khz);
>> +        i++;
>> +    }
>> +    *cnt = len;
>> +
>> +    return tbl;
>> +}
>> +
>> +static int process_scmi_memlat_of_node(struct scmi_device *sdev, 
>> struct scmi_memlat_info *info)
>> +{
>> +    struct scmi_monitor_info *monitor;
>> +    struct scmi_memory_info *memory;
>> +    char name[MAX_NAME_LEN];
>> +    u64 memfreq[2];
>> +    int ret;
>> +
>> +    ret = populate_cluster_info(info->cluster_info);
>> +    if (ret < 0) {
>> +        dev_err_probe(&sdev->dev, ret, "failed to populate cluster 
>> info\n");
>> +        goto err;
>> +    }
>> +
>> +    of_node_get(sdev->dev.of_node);
>> +    do {
>> +        snprintf(name, sizeof(name), "memory-%d", info->memory_cnt);
>> +        struct device_node *memory_np __free(device_node) =
>> +            of_find_node_by_name(sdev->dev.of_node, name);
>> +
>> +        if (!memory_np)
>> +            break;
>> +
>> +        if (info->memory_cnt >= MAX_MEMORY_TYPES)
>> +            return dev_err_probe(&sdev->dev, -EINVAL,
>> +                         "failed to parse unsupported memory type\n");
>> +
>> +        memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
>> +        if (!memory) {
>> +            ret = -ENOMEM;
>> +            goto err;
>> +        }
>> +
>> +        ret = of_property_read_u32(memory_np, "qcom,memory-type", 
>> &memory->hw_type);
>> +        if (ret) {
>> +            dev_err_probe(&sdev->dev, ret, "failed to read memory 
>> type\n");
>> +            goto err;
>> +        }
>> +
>> +        ret = of_property_read_u64_array(memory_np, "freq-table-hz", 
>> memfreq, 2);
>> +        if (ret && (ret != -EINVAL)) {
>> +            dev_err_probe(&sdev->dev, ret, "failed to read min/max 
>> freq\n");
>> +            goto err;
>> +        }
>> +
>> +        if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
>> +            memory->min_freq = memfreq[0] / HZ_PER_KHZ;
>> +            memory->max_freq = memfreq[1] / HZ_PER_KHZ;
>> +        } else {
>> +            memory->min_freq = memfreq[0];
>> +            memory->max_freq = memfreq[1];
>> +        }
>> +        info->memory[info->memory_cnt++] = memory;
>> +
>> +        do {
>> +            snprintf(name, sizeof(name), "monitor-%d", 
>> memory->monitor_cnt);
>> +            struct device_node *monitor_np __free(device_node) =
>> +                of_get_child_by_name(memory_np, name);
>> +
>> +            if (!monitor_np)
>> +                break;
>> +
>> +            if (memory->monitor_cnt >= MAX_MONITOR_CNT)
>> +                return dev_err_probe(&sdev->dev, -EINVAL,
>> +                             "failed to parse unsupported monitor\n");
>> +
>> +            monitor = devm_kzalloc(&sdev->dev, sizeof(*monitor), 
>> GFP_KERNEL);
>> +            if (!monitor) {
>> +                ret = -ENOMEM;
>> +                goto err;
>> +            }
>> +
>> +            monitor->mon_type = of_property_read_bool(monitor_np, 
>> "qcom,compute-type");
>> +            if (!monitor->mon_type) {
>> +                ret = of_property_read_u32(monitor_np, "qcom,ipm-ceil",
>> +                               &monitor->ipm_ceil);
>> +                if (ret) {
>> +                    dev_err_probe(&sdev->dev, ret,
>> +                              "failed to read IPM ceiling\n");
>> +                    goto err;
>> +                }
>> +            }
>> +
>> +            /*
>> +             * Variants of the SoC having reduced number of cpus operate
>> +             * with the same number of logical cpus but the physical
>> +             * cpu disabled will differ between parts. Calculate the
>> +             * physical cpu number using cluster information instead.
>> +             */
>> +            populate_physical_mask(monitor_np, &monitor->mask, 
>> info->cluster_info);
>> +
>> +            monitor->freq_map = init_cpufreq_memfreq_map(&sdev->dev, 
>> memory, monitor_np,
>> +                                     &monitor->freq_map_len);
>> +            if (IS_ERR(monitor->freq_map)) {
>> +                dev_err_probe(&sdev->dev, PTR_ERR(monitor->freq_map),
>> +                          "failed to populate cpufreq-memfreq map\n");
>> +                goto err;
>> +            }
>> +
>> +            strscpy(monitor->mon_name, name, sizeof(monitor->mon_name));
>> +            monitor->mon_idx = memory->monitor_cnt;
>> +
>> +            memory->monitor[memory->monitor_cnt++] = monitor;
>> +        } while (1);
>> +
>> +        if (!memory->monitor_cnt) {
>> +            ret = -EINVAL;
>> +            dev_err_probe(&sdev->dev, ret, "failed to find monitor 
>> nodes\n");
>> +            goto err;
>> +        }
>> +    } while (1);
>> +
>> +    if (!info->memory_cnt) {
>> +        ret = -EINVAL;
>> +        dev_err_probe(&sdev->dev, ret, "failed to find memory nodes\n");
>> +    }
>> +
>> +err:
>> +    of_node_put(sdev->dev.of_node);
>> +
>> +    return ret;
>> +}
>> +
>> +static int configure_cpucp_common_events(struct scmi_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.hw_type = INVALID_IDX;
>> +    msg.cid[INST_IDX] = EV_INST_RETIRED;
>> +    msg.cid[CYC_IDX] = EV_CPU_CYCLES;
>> +    msg.cid[CONST_CYC_IDX] = INVALID_IDX;
>> +    msg.cid[FE_STALL_IDX] = INVALID_IDX;
>> +    msg.cid[BE_STALL_IDX] = INVALID_IDX;
>> +
>> +    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_memlat_info *info, int memory_index)
>> +{
>> +    const struct qcom_generic_ext_ops *ops = info->ops;
>> +    struct scmi_memory_info *memory = info->memory[memory_index];
>> +    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_memlat_info *info,
>> +                   int memory_index, int monitor_index)
>> +{
>> +    const struct qcom_generic_ext_ops *ops = info->ops;
>> +    struct scmi_memory_info *memory = info->memory[memory_index];
>> +    struct scmi_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->mon_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->mon_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->mon_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;
>> +        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->mon_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->mon_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->mon_name);
>> +
>> +    return ret;
>> +}
>> +
>> +static int cpucp_memlat_init(struct scmi_device *sdev)
>> +{
>> +    const struct scmi_handle *handle = sdev->handle;
>> +    const struct qcom_generic_ext_ops *ops;
>> +    struct scmi_protocol_handle *ph;
>> +    struct scmi_memlat_info *info;
>> +    u32 cpucp_freq_method = CPUCP_DEFAULT_FREQ_METHOD;
>> +    u32 cpucp_sample_ms = CPUCP_DEFAULT_SAMPLING_PERIOD_MS;
>> +    int ret, i, j;
>> +
>> +    if (!handle)
>> +        return -ENODEV;
>> +
>> +    ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_QCOM_GENERIC, 
>> &ph);
>> +    if (IS_ERR(ops))
>> +        return PTR_ERR(ops);
>> +
>> +    info = devm_kzalloc(&sdev->dev, sizeof(*info), GFP_KERNEL);
>> +    if (!info)
>> +        return -ENOMEM;
>> +
>> +    ret = process_scmi_memlat_of_node(sdev, info);
>> +    if (ret)
>> +        return ret;
>> +
>> +    info->ph = ph;
>> +    info->ops = ops;
>> +
>> +    /* 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, &cpucp_sample_ms, 
>> sizeof(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, &cpucp_freq_method, 
>> sizeof(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");
>> +
> 
> Hi Sibi,

Hey Shiv,

Thanks for taking time to review the series!

> Since the MEMLAT_SET_EFFECTIVE_FREQ_METHOD command is not supported in 
> the legacy CPUCP firmware, it should be kept optional. This way, if the 
> legacy firmware is used, the driver will not return an error when the 
> CPUCP firmware returns -EOPNOTSUPP.

The vendor protocol with the current major/minor version is
expected to work as is on x1e platforms. What legacy firmware
are you referring to? All future SoCs that plan to adhere to
it are expected to maintain this abi and can decide to make
use of alternate mechanisms to calculating frequency based
on additional dt properties set.

-Sibi

> 
> Thanks,
> Shivnandan
> 
>> +    /* 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");
>> +
>> +    return ret;
>> +}
>> +
>> +static int scmi_client_probe(struct scmi_device *sdev)
>> +{
>> +    return cpucp_memlat_init(sdev);
>> +}
>> +
>> +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 qcom_scmi_client_drv = {
>> +    .name        = "scmi-qcom-generic-ext-memlat",
>> +    .probe        = scmi_client_probe,
>> +    .id_table    = scmi_id_table,
>> +};
>> +module_scmi_driver(qcom_scmi_client_drv);
>> +
>> +MODULE_DESCRIPTION("QTI SCMI client driver");
>> +MODULE_LICENSE("GPL");

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-12-05 10:52             ` Sibi Sankar
@ 2024-12-05 11:30               ` Dmitry Baryshkov
  2024-12-17 10:16                 ` Sibi Sankar
  0 siblings, 1 reply; 64+ messages in thread
From: Dmitry Baryshkov @ 2024-12-05 11:30 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: myungjoo.ham, Kyungmin.park, cw00.choi, Viresh Kumar,
	sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid

On Thu, 5 Dec 2024 at 12:53, Sibi Sankar <quic_sibis@quicinc.com> wrote:
>
>
>
> On 11/14/24 18:02, Dmitry Baryshkov wrote:
> > On Thu, Nov 14, 2024 at 09:43:53AM +0530, Sibi Sankar wrote:
> >>
> >>
> >> On 10/26/24 23:46, Dmitry Baryshkov wrote:
> >>> On Tue, Oct 22, 2024 at 01:48:25PM +0530, Sibi Sankar wrote:
> >>>>
> >>>>
> >>>> On 10/7/24 23:27, Dmitry Baryshkov wrote:
> >>>>> On Mon, Oct 07, 2024 at 11:40:22AM GMT, Sibi Sankar wrote:
> >
> >>>>>
> >>>>>> +};
> >>>>>> +
> >>>>>> +struct map_param_msg {
> >>>>>> +        u32 hw_type;
> >>>>>> +        u32 mon_idx;
> >>>>>> +        u32 nr_rows;
> >>>>>> +        struct 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
> >>>>>> +};
> >>>>>> +
> >>>>>> +#define EV_CPU_CYCLES           0
> >>>>>> +#define EV_INST_RETIRED         2
> >>>>>> +#define EV_L2_D_RFILL           5
> >>>>>> +
> >>>>>> +struct ev_map_msg {
> >>>>>> +        u32 num_evs;
> >>>>>> +        u32 hw_type;
> >>>>>> +        u32 cid[NUM_COMMON_EVS];
> >>>>>> +};
> >>>>>> +
> >>>>>> +struct cpufreq_memfreq_map {
> >>>>>> +        unsigned int cpufreq_mhz;
> >>>>>> +        unsigned int memfreq_khz;
> >>>>>> +};
> >>>>>> +
> >>>>>> +struct scmi_monitor_info {
> >>>>>> +        struct cpufreq_memfreq_map *freq_map;
> >>>>>> +        char mon_name[MAX_NAME_LEN];
> >>>>>> +        u32 mon_idx;
> >>>>>> +        u32 mon_type;
> >>>>>> +        u32 ipm_ceil;
> >>>>>> +        u32 mask;
> >>>>>> +        u32 freq_map_len;
> >>>>>> +};
> >>>>>> +
> >>>>>> +struct scmi_memory_info {
> >>>>>> +        struct scmi_monitor_info *monitor[MAX_MONITOR_CNT];
> >>>>>> +        u32 hw_type;
> >>>>>> +        int monitor_cnt;
> >>>>>> +        u32 min_freq;
> >>>>>> +        u32 max_freq;
> >>>>>> +};
> >>>>>> +
> >>>>>> +struct scmi_memlat_info {
> >>>>>> +        struct scmi_protocol_handle *ph;
> >>>>>> +        const struct qcom_generic_ext_ops *ops;
> >>>>>> +        struct scmi_memory_info *memory[MAX_MEMORY_TYPES];
> >>>>>> +        u32 cluster_info[NR_CPUS];
> >>>>>> +        int memory_cnt;
> >>>>>> +};
> >>>>>> +
> >>>>>> +static int populate_cluster_info(u32 *cluster_info)
> >>>>>> +{
> >>>>>> +        char name[MAX_NAME_LEN];
> >>>>>> +        int i = 0;
> >>>>>> +
> >>>>>> +        struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
> >>>>>> +        if (!cn)
> >>>>>> +                return -ENODEV;
> >>>>>> +
> >>>>>> +        struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
> >>>>>> +        if (!map)
> >>>>>> +                return -ENODEV;
> >>>>>> +
> >>>>>> +        do {
> >>>>>> +                snprintf(name, sizeof(name), "cluster%d", i);
> >>>>>> +                struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
> >>>>>> +                if (!c)
> >>>>>> +                        break;
> >>>>>> +
> >>>>>> +                *(cluster_info + i) = of_get_child_count(c);
> >>>>>> +                i++;
> >>>>>> +        } while (1);
> >>>>>
> >>>>> Can you use existing API from drivers/base/arch_topology.c? If not, can
> >>>>> it be extended to support your usecase?
> >>>>
> >>>> ack. But I'm pretty sure it's going to take a while for reaching such
> >>>> an agreement so I'll drop this feature during the next re-spin.
> >>>
> >>> Why? What kind of API do you actually need? The arch_topology.c simply
> >>> exports a table of struct cpu_topology. Is it somehow different from
> >>> what you are parsing manually?
> >>
> >> yup, we had to figure out the physical id of the cpu
> >> since cpus can be disabled by the bootloader using
> >> status = "failed" property and we have to pass this
> >> onto the cpucp memlat algorithm.
> >
> > Isn't it equal to the index in the cpu_topology table?
>
> from what I see cpu_topology indexes remain unpopulated
> for cpus that are disabled since get_cpu_for_node
> ignores those?

Why do you need cpu_topology for disabled aka non-existing CPU devices?

>
> >
> >>
> >>>
> >>>>
> >>>>>
> >>>>>> +
> >>>>>> +        return 0;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static void populate_physical_mask(struct device_node *np, u32 *mask, u32 *cluster_info)
> >>>>>> +{
> >>>>>> +        struct device_node *dev_phandle __free(device_node);
> >>>>>> +        int cpu, i = 0, physical_id;
> >>>>>> +
> >>>>>> +        do {
> >>>>>> +                dev_phandle = of_parse_phandle(np, "cpus", i++);
> >>>>>> +                cpu = of_cpu_node_to_id(dev_phandle);
> >>>>>> +                if (cpu != -ENODEV) {
> >>>>>> +                        physical_id = topology_core_id(cpu);
> >>>>>> +                        for (int j = 0; j < topology_cluster_id(cpu); j++)
> >>>>>> +                                physical_id += *(cluster_info + j);
> >>>>>> +                        *mask |= BIT(physical_id);
> >>>>>> +                }
> >>>>>> +        } while (dev_phandle);
> >>>>>> +}
> >>>>>> +
> >>>>>> +static struct cpufreq_memfreq_map *init_cpufreq_memfreq_map(struct device *dev,
> >>>>>> +                                                            struct scmi_memory_info *memory,
> >>>>>> +                                                            struct device_node *of_node,
> >>>>>> +                                                            u32 *cnt)
> >>>>>> +{
> >>>>>> +        struct device_node *tbl_np __free(device_node), *opp_np __free(device_node);
> >>>>>> +        struct cpufreq_memfreq_map *tbl;
> >>>>>> +        int ret, i = 0;
> >>>>>> +        u32 level, len;
> >>>>>> +        u64 rate;
> >>>>>> +
> >>>>>> +        tbl_np = of_parse_phandle(of_node, "operating-points-v2", 0);
> >>>>>
> >>>>> Please use existing API to parse OPP tables or document a reason why it
> >>>>> can't be used.
> >>>>
> >>>> Thanks, I had them documented as opens in the coverletter. Dropped them
> >>>> since no one had any comments on it during V3. Will add them as comments
> >>>> to this driver instead.
> >>>>
> >>>> https://lore.kernel.org/lkml/20240702191440.2161623-1-quic_sibis@quicinc.com/
> >>>>
> >>>> re-copying things again:
> >>>> opp-tables are used but they don't get to be added to the scmi device
> >>>> (thus we rely on a lot of manual parsing) because the memlat client driver
> >>>> doesn't vote on these resources clocks/interconnects/power-domain
> >>>> from the kernel and some of the resources aren't modeled in the first
> >>>> place like DDR_QOS.
> >>>
> >>> As discussed offline, please consider extending the OPP to be able to
> >>> get the struct opp_table for the particular phandle. Another option
> >>> might be to change the memlat driver by having a separate device for
> >>> each monitor. This way you can use existing API to parse OPP tables and
> >>> to get necessary data from those tables.
> >>
> >> + Viresh
> >>
> >> Spoke with Viresh offline and he had stricter requirements
> >> than what you proposed. He definitely wanted the opp-tables
> >> to be assoiciated with devices at the very least and have
> >> all opp parsing logic within the opp-framework. Given that
> >> we have to model all these dummy devices just to add the
> >> tables I'll re-check the feasibility of movign the tables
> >> into the driver itself. Will move the patch series back
> >> into RFC and re-post just the vendor protocol since that's
> >> close to merge
> >
> > I don't think it's sensible to move the tables to the driver. Instead
> > adding a device per monitor sounds like a better idea.
>
> yeah, I would like to keep this in dt as well. But in order
> to be able to do that through the opp core we would need
> to put in a clock and interconnect paths so that the framework
> is able to add the table to the device.
> I should be able
> to list the scmi perf domain as the clock phandle but inorder
> to be able to convert the kbps values for the interconnect I
> would need to store the bus width and so on which is currently
> abstracted by the interconnect framework. Also in the future
> we may have to model dummy devices just to get the table parsed
> if the devices aren't modelled and controlled in the kernel.
> All of these seems to indicate that having the tables in the
> driver is a better alternative, lol.

Or fix the OPP API. I don't think that having monitor tables for the
SCMI-based platforms in the driver code is going to scale.

>
> -Sibi
>
> >
> >>>>>> +        if (!tbl_np)
> >>>>>> +                return ERR_PTR(-ENODEV);
> >>>>>> +
> >>>>>> +        len = min(of_get_available_child_count(tbl_np), MAX_MAP_ENTRIES);
> >>>>>> +        if (len == 0)
> >>>>>> +                return ERR_PTR(-ENODEV);
> >>>>>> +
> >>>>>> +        tbl = devm_kzalloc(dev, (len + 1) * sizeof(struct cpufreq_memfreq_map),
> >>>>>> +                           GFP_KERNEL);
> >>>>>> +        if (!tbl)
> >>>>>> +                return ERR_PTR(-ENOMEM);
> >>>>>> +
> >>>>>> +        for_each_available_child_of_node(tbl_np, opp_np) {
> >>>>>> +                ret = of_property_read_u64_index(opp_np, "opp-hz", 0, &rate);
> >>>>>> +                if (ret < 0)
> >>>>>> +                        return ERR_PTR(ret);
> >>>>>> +
> >>>>>> +                tbl[i].cpufreq_mhz = rate / HZ_PER_MHZ;
> >>>>>> +
> >>>>>> +                if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
> >>>>>> +                        ret = of_property_read_u64_index(opp_np, "opp-hz", 1, &rate);
> >>>>>> +                        if (ret < 0)
> >>>>>> +                                return ERR_PTR(ret);
> >>>>>> +
> >>>>>> +                        tbl[i].memfreq_khz = rate / HZ_PER_KHZ;
> >>>>>> +                } else {
> >>>>>> +                        ret = of_property_read_u32(opp_np, "opp-level", &level);
> >>>>>> +                        if (ret < 0)
> >>>>>> +                                return ERR_PTR(ret);
> >>>>>> +
> >>>>>> +                        tbl[i].memfreq_khz = level;
> >>>>>> +                }
> >>>>>> +
> >>>>>> +                dev_dbg(dev, "Entry%d CPU:%u, Mem:%u\n", i, tbl[i].cpufreq_mhz, tbl[i].memfreq_khz);
> >>>>>> +                i++;
> >>>>>> +        }
> >>>>>> +        *cnt = len;
> >>>>>> +
> >>>>>> +        return tbl;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int process_scmi_memlat_of_node(struct scmi_device *sdev, struct scmi_memlat_info *info)
> >>>>>> +{
> >>>>>> +        struct scmi_monitor_info *monitor;
> >>>>>> +        struct scmi_memory_info *memory;
> >>>>>> +        char name[MAX_NAME_LEN];
> >>>>>> +        u64 memfreq[2];
> >>>>>> +        int ret;
> >>>>>> +
> >>>>>> +        ret = populate_cluster_info(info->cluster_info);
> >>>>>> +        if (ret < 0) {
> >>>>>> +                dev_err_probe(&sdev->dev, ret, "failed to populate cluster info\n");
> >>>>>> +                goto err;
> >>>>>> +        }
> >>>>>> +
> >>>>>> +        of_node_get(sdev->dev.of_node);
> >>>>>> +        do {
> >>>>>> +                snprintf(name, sizeof(name), "memory-%d", info->memory_cnt);
> >>>>>> +                struct device_node *memory_np __free(device_node) =
> >>>>>> +                        of_find_node_by_name(sdev->dev.of_node, name);
> >>>>>> +
> >>>>>> +                if (!memory_np)
> >>>>>> +                        break;
> >>>>>> +
> >>>>>> +                if (info->memory_cnt >= MAX_MEMORY_TYPES)
> >>>>>> +                        return dev_err_probe(&sdev->dev, -EINVAL,
> >>>>>> +                                             "failed to parse unsupported memory type\n");
> >>>>>> +
> >>>>>> +                memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
> >>>>>> +                if (!memory) {
> >>>>>> +                        ret = -ENOMEM;
> >>>>>> +                        goto err;
> >>>>>> +                }
> >>>>>> +
> >>>>>> +                ret = of_property_read_u32(memory_np, "qcom,memory-type", &memory->hw_type);
> >>>>>> +                if (ret) {
> >>>>>> +                        dev_err_probe(&sdev->dev, ret, "failed to read memory type\n");
> >>>>>> +                        goto err;
> >>>>>> +                }
> >>>>>> +
> >>>>>> +                ret = of_property_read_u64_array(memory_np, "freq-table-hz", memfreq, 2);
> >>>>>> +                if (ret && (ret != -EINVAL)) {
> >>>>>> +                        dev_err_probe(&sdev->dev, ret, "failed to read min/max freq\n");
> >>>>>> +                        goto err;
> >>>>>> +                }
> >>>>>
> >>>>> Can we get this information from the OPP table instead?
> >>>>
> >>>> we don't list all the available ddr/llcc freqs in the opp-table
> >>>> so that we can keep the table constant across platforms.
> >>>
> >>> NO. Use opp-supported-hw to limit data to a particular platform. There
> >>> is no reason to keep min/max out of the OPP table.
> >>
> >> if we are movign the opp-tables into driver data for the reasons
> >> described above, this can probably stay?
> >
> > No. They duplicate the information that can be a part of the tables. It
> > doesn't matter if the tables are in the driver or in DT.
> >
> >



-- 
With best wishes
Dmitry

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-12-05 11:03     ` Sibi Sankar
@ 2024-12-05 12:39       ` Cristian Marussi
  2024-12-23 13:57         ` Sibi Sankar
  0 siblings, 1 reply; 64+ messages in thread
From: Cristian Marussi @ 2024-12-05 12:39 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: Shivnandan Kumar, sudeep.holla, cristian.marussi, andersson,
	konrad.dybcio, robh+dt, krzysztof.kozlowski+dt, linux-kernel,
	linux-arm-msm, devicetree, linux-arm-kernel, quic_rgottimu,
	conor+dt, arm-scmi, Amir Vajid

On Thu, Dec 05, 2024 at 04:33:05PM +0530, Sibi Sankar wrote:
> 
> 
> On 11/29/24 15:27, Shivnandan Kumar wrote:
> > 
> >

Hi Sibi,

some rants down below :P
 
> > On 10/7/2024 11:40 AM, Sibi Sankar wrote:
> > > Introduce a 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).
> > > 

[snip]

> > > +    /* Set the effective cpu frequency calculation method */
> > > +    ret = ops->set_param(ph, &cpucp_freq_method,
> > > sizeof(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");
> > > +
> > 
> > Hi Sibi,
> 
> Hey Shiv,
> 
> Thanks for taking time to review the series!
> 
> > Since the MEMLAT_SET_EFFECTIVE_FREQ_METHOD command is not supported in
> > the legacy CPUCP firmware, it should be kept optional. This way, if the
> > legacy firmware is used, the driver will not return an error when the
> > CPUCP firmware returns -EOPNOTSUPP.
> 
> The vendor protocol with the current major/minor version is
> expected to work as is on x1e platforms. What legacy firmware
> are you referring to? All future SoCs that plan to adhere to
> it are expected to maintain this abi and can decide to make
> use of alternate mechanisms to calculating frequency based
> on additional dt properties set.
> 

Normally in the SCMI world you could leverage protocol versioning and
the standard PROTOCOL_MESSAGE_ATTRIBUTES(0x2) command to let the agent
investigate if the SCMI server it is speaking to implements or NOT a
specific command and which specific version of that command is understood
(with possibly varying size and fields)...

...BUT since your vendor protocol is 'Generic' and, as it stands, it
basically piggybacks any kind of binary payload (i.e. subcommands of
some kind of subprotocols of yours) into the same 4 get/set/start/stop
'Generic' ops, without even specifying the transmitted/received payload
sizes into the message itself....all of the possible SCMI versioning
autodiscovery and backward-compatibility capabilities happily go out of
the window because:

- your versioning refers to the generic protocol and you cannot possibly
  describe all the possible future subcommands (opaque payloads) variations
  and/or subcommands addition/removals purely on the major/minor version, AND
  even if you did that, NONE of such future variations will be documented
  anywhere since you are hiding all of this inside a bunch of binary blobs

- you dont even specify the payload sizes of the tx/rx 'Generic' payload
  subcommands so it becomes even difficult for both the server and the
  client to safely handle your 'Generic' subcommand message payloads

- you cannot issue a PROTOCOL_MESSAGE_ATTRIBUTE() to see if a specific
  subcommand is supported, because your subcommand is NOT really a protocol
  command but it is just one of the payloads of one of the 'Generic' protocol:
  you commmands are only set/get/start/stop (maybe some sort of hack
  could be doen around these...bit all will be even more flaky...)

- you dont implement NEGOTIATE_PROTOCOL_VERSION and so you cannot even
  check if the SCMI server that you are speaking to will agree to
  downgrade and 'speak' your Kernel SCMI agent (possibly older) 'Generic'
  protocol version

All of this basically defeats all of the SCMI general capabilities
around versioning and auto-discovery when it comes to your 'Generic' vendor
protocol, with the end result that you will have to be EXTREMELY confident
to perfectly match and keep in sync at all times your SCMI Server(FW) and
Client(Kernel) sides, without any possibility of coping with a such a mismatch
on the field by using some of the fallback/downgrade mechanism that you
threw out of the window...
(...and sorry but looking at the above xchange about 'legacy firmware' I am
 a bit skeptic about this as of now :D)

...as I said initially when reviewing this series, you can do whatever
you want within your Vendor protocol, but abusing the SCMI Vendor extensions
capabilities in such a way could end up bringing to the table more cons
(the above) than pros (some supposed 'simplification')

Thanks,
Cristian

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

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-10-07  6:10 ` [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension Sibi Sankar
                     ` (3 preceding siblings ...)
  2024-11-06 22:18   ` Jeffrey Hugo
@ 2024-12-05 15:27   ` Sudeep Holla
  2024-12-17 11:45     ` Sibi Sankar
  4 siblings, 1 reply; 64+ messages in thread
From: Sudeep Holla @ 2024-12-05 15:27 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: cristian.marussi, andersson, Sudeep Holla, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi

On Mon, Oct 07, 2024 at 11:40:19AM +0530, Sibi Sankar wrote:
> Document the various memory buses that can be monitored and scaled by
> the memory latency governor hosted by the QCOM SCMI Generic Extension
> Protocol v1.0.
> 
> Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com>
> ---
> 
> v3:
> * Restructure the bindings to mimic IMX [Christian]
> 
>  .../bindings/firmware/arm,scmi.yaml           |   1 +
>  .../bindings/firmware/qcom,scmi-memlat.yaml   | 246 ++++++++++++++++++
>  .../dt-bindings/firmware/qcom,scmi-memlat.h   |  22 ++
>  3 files changed, 269 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
>  create mode 100644 include/dt-bindings/firmware/qcom,scmi-memlat.h
> 
> diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
> index 54d7d11bfed4..1d405f429168 100644
> --- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
> +++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
> @@ -24,6 +24,7 @@ description: |
>  
>  anyOf:
>    - $ref: /schemas/firmware/nxp,imx95-scmi.yaml
> +  - $ref: /schemas/firmware/qcom,scmi-memlat.yaml
>  
>  properties:
>    $nodename:
> diff --git a/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml b/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
> new file mode 100644
> index 000000000000..0e8ea6dacd6a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
> @@ -0,0 +1,246 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/firmware/qcom,scmi-memlat.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Qualcomm SCMI Memory Bus nodes
> +
> +maintainers:
> +  - Sibi Sankar <quic_sibis@quicinc.com>
> +
> +description:
> +  This binding describes the various memory buses that can be monitored and scaled
> +  by memory latency governor running on the CPU Control Processor (SCMI controller).
> +
> +properties:
> +  protocol@80:
> +    $ref: '/schemas/firmware/arm,scmi.yaml#/$defs/protocol-node'
> +    unevaluatedProperties: false
> +
> +    properties:
> +      reg:
> +        const: 0x80
> +
> +    patternProperties:
> +      '^memory-[0-9]$':
> +        type: object
> +        unevaluatedProperties: false
> +        description:
> +          The list of all memory buses that can be monitored and scaled by the
> +          memory latency governor running on the SCMI controller.
> +
> +        properties:
> +          qcom,memory-type:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            enum: [0, 1, 2]
> +            description: |
> +              Memory Bus Identifier
> +              0 = QCOM_MEM_TYPE_DDR
> +              1 = QCOM_MEM_TYPE_LLCC
> +              2 = QCOM_MEM_TYPE_DDR_QOS
> +
> +          freq-table-hz:
> +            items:
> +              items:
> +                - description: Minimum frequency of the memory bus in Hz
> +                - description: Maximum frequency of the memory bus in Hz
> +
> +        patternProperties:
> +          '^monitor-[0-9]$':
> +            type: object
> +            unevaluatedProperties: false
> +            description:
> +              The list of all monitors detecting the memory latency bound workloads using
> +              various counters.
> +
> +            properties:
> +              qcom,compute-type:
> +                description:
> +                  Monitors of type compute perform bus dvfs based on a rudimentary CPU
> +                  frequency to memory frequency map.
> +                type: boolean
> +
> +              qcom,ipm-ceil:
> +                $ref: /schemas/types.yaml#/definitions/uint32
> +                description:
> +                  Monitors having this property perform bus dvfs based on the same
> +                  rudimentary table but the scaling is performed only if the calculated
> +                  IPM (Instruction Per Misses) exceeds the given ceiling.
> +
> +              cpus:
> +                $ref: /schemas/types.yaml#/definitions/phandle-array
> +                description:
> +                  Should be a list of phandles to CPU nodes (as described in
> +                  Documentation/devicetree/bindings/arm/cpus.yaml).

And what exactly this list of cpu phandles represent here ?

> +
> +              operating-points-v2: true

Can you elaborate why the protocol can't add a command to fetch this from
the firmware to avoid any possible mismatch between the DT and firmware.

> +              opp-table:
> +                type: object
> +
> +            required:
> +              - cpus
> +              - operating-points-v2
> +
> +            oneOf:
> +              - required: [ 'qcom,compute-type' ]
> +              - required: [ 'qcom,ipm-ceil' ]
> +
> +        required:
> +          - qcom,memory-type
> +          - freq-table-hz
> +
> +additionalProperties: true
> +
> +examples:
> +  - |
> +    #include <dt-bindings/firmware/qcom,scmi-memlat.h>
> +
> +    firmware {
> +        scmi {
> +            compatible = "arm,scmi";
> +            mboxes = <&cpucp_mbox 0>, <&cpucp_mbox 2>;
> +            mbox-names = "tx", "rx";
> +            shmem = <&cpu_scp_lpri0>, <&cpu_scp_lpri1>;
> +
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +
> +            protocol@80 {
> +                reg = <0x80>;
> +
> +                memory-0 {

I am not sure if it is just me, but I find this binding hard to understand.
Hence I thought I will look and the example and get better understanding
instead.

So these monitors are numbered ? If there were any meaningful name it would
have been slightly better but irrespective of that I find it hard as why
the communication with firmware is not based on index and why they are not
part of the bindings though I see indices used in the driver.

> +                    qcom,memory-type = <QCOM_MEM_TYPE_DDR>;

Also I see that the type can be easily derived from the index, care to
elaborate why the firmware expects it to be sent, if not why is that
information needed here in the DT ?

-- 
Regards,
Sudeep

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-12-05 10:56           ` Sibi Sankar
@ 2024-12-05 15:52             ` Johan Hovold
  2024-12-17 11:49               ` Sibi Sankar
  0 siblings, 1 reply; 64+ messages in thread
From: Johan Hovold @ 2024-12-05 15:52 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: Cristian Marussi, sudeep.holla, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi

On Thu, Dec 05, 2024 at 04:26:55PM +0530, Sibi Sankar wrote:
> On 11/22/24 14:07, Johan Hovold wrote:

> > I have a Lenovo ThinkPad T14s set up now so I gave this series a spin
> > there too, and there I do *not* see the above mentioned -EOPNOSUPP error
> > and the memlat driver probes successfully.
> > 
> > On the other hand, this series seems to have no effect on a kernel
> > compilation benchmark. Is that expected?
> 
> I can have a look at your tree. But memlat in general
> depends on the cpu frequency when your benchmarks max
> the cpu's the ddr/llcc are scaled accordingly by it.

A kernel compilation should max out the CPU frequency on all cores.

> > And does this mean that you should stick with the uppercase "MEMLAT"
> > string after all? The firmware on my CRD is not the latest one, but I am
> > using the latest available firmware for the T14s.
> 
> We should stick with "memlat" if we run into a device in the
> wild that doesn't support "MEMLAT"

Ok. So the updated firmware supports both strings?

Johan

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-11-22  8:37         ` Johan Hovold
  2024-12-05 10:56           ` Sibi Sankar
@ 2024-12-05 17:01           ` Sudeep Holla
  2024-12-17 12:25             ` Sibi Sankar
  1 sibling, 1 reply; 64+ messages in thread
From: Sudeep Holla @ 2024-12-05 17:01 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Sibi Sankar, Sudeep Holla, Cristian Marussi, andersson,
	konrad.dybcio, robh+dt, krzysztof.kozlowski+dt, linux-kernel,
	linux-arm-msm, devicetree, linux-arm-kernel, quic_rgottimu,
	quic_kshivnan, conor+dt, arm-scmi

On Fri, Nov 22, 2024 at 09:37:47AM +0100, Johan Hovold wrote:
> On Thu, Nov 14, 2024 at 09:52:12AM +0530, Sibi Sankar wrote:
> > On 11/8/24 20:44, Johan Hovold wrote:
>
> > >> On Wed, Nov 06, 2024 at 01:55:33PM +0100, Johan Hovold wrote:
>
> > >>> Second, after loading the protocol and client drivers manually (in that
> > >>> order, shouldn't the client driver pull in the protocol?), I got:
> > >>>
> > >>> 	scmi_module: Loaded SCMI Vendor Protocol 0x80 - Qualcomm  20000
> > >>> 	arm-scmi arm-scmi.0.auto: QCOM Generic Vendor Version 1.0
> > >>> 	scmi-qcom-generic-ext-memlat scmi_dev.5: error -EOPNOTSUPP: failed to configure common events
> > >>> 	scmi-qcom-generic-ext-memlat scmi_dev.5: probe with driver scmi-qcom-generic-ext-memlat failed with error -95
> > >>>
> > >>> which seems to suggest that the firmware on my CRD does not support this
> > >>> feature. Is that the way this should be interpreted? And does that mean
> > >>> that non of the commercial laptops supports this either?
>
> > > Yeah, hopefully Sibi can shed some light on this. I'm using the DT
> > > patch (5/5) from this series, which according to the commit message is
> > > supposed to enable bus scaling on the x1e80100 platform. So I guess
> > > something is missing in my firmware.
> >
> > Nah, it's probably just because of the algo string used.
> > The past few series used caps MEMLAT string instead of
> > memlat to pass the tuneables, looks like all the laptops
> > havn't really switched to it yet. Will revert back to
> > using to lower case memlat so that all devices are
> > supported. Thanks for trying the series out!
>
> I have a Lenovo ThinkPad T14s set up now so I gave this series a spin
> there too, and there I do *not* see the above mentioned -EOPNOSUPP error
> and the memlat driver probes successfully.
>
> On the other hand, this series seems to have no effect on a kernel
> compilation benchmark. Is that expected?
>

Hijacking this thread to rant about state of firmware implementation on
this platform that gives me zero confidence in merging any of these without
examining each of the interface details in depth and at lengths.

Also I see the standard protocol like PERF seem to have so many issues which
adds to my no confidence. I can't comment on that thread for specific reasons.

I will briefly mention my suspicion here. This Lenovo ThinkPad T14s being
primarily targeting other OS using ACPI might have just implemented what is
required for ACPI CPPC which conveniently doesn't have to discover lot of
fastchannel details since they are supplied in the tables straight away.
But that also would mean it could be not fully compliant to SCMI spec.

Either we need to run some compliance test suite(which again may need more
work as it is unfortunately make platform specific - referring to [1])
or use the raw interface in the kernel and throw /dev/random at it and see
how well it can cope up.

--
Regards,
Sudeep

[1] https://gitlab.arm.com/tests/scmi-tests (Not so great and needs platform
     specific vectors to check compliance)

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-12-05 11:30               ` Dmitry Baryshkov
@ 2024-12-17 10:16                 ` Sibi Sankar
  2024-12-17 10:46                   ` Dmitry Baryshkov
  0 siblings, 1 reply; 64+ messages in thread
From: Sibi Sankar @ 2024-12-17 10:16 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: myungjoo.ham, Kyungmin.park, cw00.choi, Viresh Kumar,
	sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid



On 12/5/24 17:00, Dmitry Baryshkov wrote:
> On Thu, 5 Dec 2024 at 12:53, Sibi Sankar <quic_sibis@quicinc.com> wrote:
>>
>>
>>
>> On 11/14/24 18:02, Dmitry Baryshkov wrote:
>>> On Thu, Nov 14, 2024 at 09:43:53AM +0530, Sibi Sankar wrote:
>>>>
>>>>
>>>> On 10/26/24 23:46, Dmitry Baryshkov wrote:
>>>>> On Tue, Oct 22, 2024 at 01:48:25PM +0530, Sibi Sankar wrote:
>>>>>>
>>>>>>
>>>>>> On 10/7/24 23:27, Dmitry Baryshkov wrote:
>>>>>>> On Mon, Oct 07, 2024 at 11:40:22AM GMT, Sibi Sankar wrote:
>>>
>>>>>>>
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +struct map_param_msg {
>>>>>>>> +        u32 hw_type;
>>>>>>>> +        u32 mon_idx;
>>>>>>>> +        u32 nr_rows;
>>>>>>>> +        struct 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
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +#define EV_CPU_CYCLES           0
>>>>>>>> +#define EV_INST_RETIRED         2
>>>>>>>> +#define EV_L2_D_RFILL           5
>>>>>>>> +
>>>>>>>> +struct ev_map_msg {
>>>>>>>> +        u32 num_evs;
>>>>>>>> +        u32 hw_type;
>>>>>>>> +        u32 cid[NUM_COMMON_EVS];
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +struct cpufreq_memfreq_map {
>>>>>>>> +        unsigned int cpufreq_mhz;
>>>>>>>> +        unsigned int memfreq_khz;
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +struct scmi_monitor_info {
>>>>>>>> +        struct cpufreq_memfreq_map *freq_map;
>>>>>>>> +        char mon_name[MAX_NAME_LEN];
>>>>>>>> +        u32 mon_idx;
>>>>>>>> +        u32 mon_type;
>>>>>>>> +        u32 ipm_ceil;
>>>>>>>> +        u32 mask;
>>>>>>>> +        u32 freq_map_len;
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +struct scmi_memory_info {
>>>>>>>> +        struct scmi_monitor_info *monitor[MAX_MONITOR_CNT];
>>>>>>>> +        u32 hw_type;
>>>>>>>> +        int monitor_cnt;
>>>>>>>> +        u32 min_freq;
>>>>>>>> +        u32 max_freq;
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +struct scmi_memlat_info {
>>>>>>>> +        struct scmi_protocol_handle *ph;
>>>>>>>> +        const struct qcom_generic_ext_ops *ops;
>>>>>>>> +        struct scmi_memory_info *memory[MAX_MEMORY_TYPES];
>>>>>>>> +        u32 cluster_info[NR_CPUS];
>>>>>>>> +        int memory_cnt;
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +static int populate_cluster_info(u32 *cluster_info)
>>>>>>>> +{
>>>>>>>> +        char name[MAX_NAME_LEN];
>>>>>>>> +        int i = 0;
>>>>>>>> +
>>>>>>>> +        struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
>>>>>>>> +        if (!cn)
>>>>>>>> +                return -ENODEV;
>>>>>>>> +
>>>>>>>> +        struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
>>>>>>>> +        if (!map)
>>>>>>>> +                return -ENODEV;
>>>>>>>> +
>>>>>>>> +        do {
>>>>>>>> +                snprintf(name, sizeof(name), "cluster%d", i);
>>>>>>>> +                struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
>>>>>>>> +                if (!c)
>>>>>>>> +                        break;
>>>>>>>> +
>>>>>>>> +                *(cluster_info + i) = of_get_child_count(c);
>>>>>>>> +                i++;
>>>>>>>> +        } while (1);
>>>>>>>
>>>>>>> Can you use existing API from drivers/base/arch_topology.c? If not, can
>>>>>>> it be extended to support your usecase?
>>>>>>
>>>>>> ack. But I'm pretty sure it's going to take a while for reaching such
>>>>>> an agreement so I'll drop this feature during the next re-spin.
>>>>>
>>>>> Why? What kind of API do you actually need? The arch_topology.c simply
>>>>> exports a table of struct cpu_topology. Is it somehow different from
>>>>> what you are parsing manually?
>>>>
>>>> yup, we had to figure out the physical id of the cpu
>>>> since cpus can be disabled by the bootloader using
>>>> status = "failed" property and we have to pass this
>>>> onto the cpucp memlat algorithm.
>>>
>>> Isn't it equal to the index in the cpu_topology table?
>>
>> from what I see cpu_topology indexes remain unpopulated
>> for cpus that are disabled since get_cpu_for_node
>> ignores those?
> 
> Why do you need cpu_topology for disabled aka non-existing CPU devices?

sorry was out sick couldn't back earlier. We need the know
what cpus are disbled to pass on the correct mask of cpus
enabled to the SCP.

> 
>>
>>>
>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>> +
>>>>>>>> +        return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void populate_physical_mask(struct device_node *np, u32 *mask, u32 *cluster_info)
>>>>>>>> +{
>>>>>>>> +        struct device_node *dev_phandle __free(device_node);
>>>>>>>> +        int cpu, i = 0, physical_id;
>>>>>>>> +
>>>>>>>> +        do {
>>>>>>>> +                dev_phandle = of_parse_phandle(np, "cpus", i++);
>>>>>>>> +                cpu = of_cpu_node_to_id(dev_phandle);
>>>>>>>> +                if (cpu != -ENODEV) {
>>>>>>>> +                        physical_id = topology_core_id(cpu);
>>>>>>>> +                        for (int j = 0; j < topology_cluster_id(cpu); j++)
>>>>>>>> +                                physical_id += *(cluster_info + j);
>>>>>>>> +                        *mask |= BIT(physical_id);
>>>>>>>> +                }
>>>>>>>> +        } while (dev_phandle);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static struct cpufreq_memfreq_map *init_cpufreq_memfreq_map(struct device *dev,
>>>>>>>> +                                                            struct scmi_memory_info *memory,
>>>>>>>> +                                                            struct device_node *of_node,
>>>>>>>> +                                                            u32 *cnt)
>>>>>>>> +{
>>>>>>>> +        struct device_node *tbl_np __free(device_node), *opp_np __free(device_node);
>>>>>>>> +        struct cpufreq_memfreq_map *tbl;
>>>>>>>> +        int ret, i = 0;
>>>>>>>> +        u32 level, len;
>>>>>>>> +        u64 rate;
>>>>>>>> +
>>>>>>>> +        tbl_np = of_parse_phandle(of_node, "operating-points-v2", 0);
>>>>>>>
>>>>>>> Please use existing API to parse OPP tables or document a reason why it
>>>>>>> can't be used.
>>>>>>
>>>>>> Thanks, I had them documented as opens in the coverletter. Dropped them
>>>>>> since no one had any comments on it during V3. Will add them as comments
>>>>>> to this driver instead.
>>>>>>
>>>>>> https://lore.kernel.org/lkml/20240702191440.2161623-1-quic_sibis@quicinc.com/
>>>>>>
>>>>>> re-copying things again:
>>>>>> opp-tables are used but they don't get to be added to the scmi device
>>>>>> (thus we rely on a lot of manual parsing) because the memlat client driver
>>>>>> doesn't vote on these resources clocks/interconnects/power-domain
>>>>>> from the kernel and some of the resources aren't modeled in the first
>>>>>> place like DDR_QOS.
>>>>>
>>>>> As discussed offline, please consider extending the OPP to be able to
>>>>> get the struct opp_table for the particular phandle. Another option
>>>>> might be to change the memlat driver by having a separate device for
>>>>> each monitor. This way you can use existing API to parse OPP tables and
>>>>> to get necessary data from those tables.
>>>>
>>>> + Viresh
>>>>
>>>> Spoke with Viresh offline and he had stricter requirements
>>>> than what you proposed. He definitely wanted the opp-tables
>>>> to be assoiciated with devices at the very least and have
>>>> all opp parsing logic within the opp-framework. Given that
>>>> we have to model all these dummy devices just to add the
>>>> tables I'll re-check the feasibility of movign the tables
>>>> into the driver itself. Will move the patch series back
>>>> into RFC and re-post just the vendor protocol since that's
>>>> close to merge
>>>
>>> I don't think it's sensible to move the tables to the driver. Instead
>>> adding a device per monitor sounds like a better idea.
>>
>> yeah, I would like to keep this in dt as well. But in order
>> to be able to do that through the opp core we would need
>> to put in a clock and interconnect paths so that the framework
>> is able to add the table to the device.
>> I should be able
>> to list the scmi perf domain as the clock phandle but inorder
>> to be able to convert the kbps values for the interconnect I
>> would need to store the bus width and so on which is currently
>> abstracted by the interconnect framework. Also in the future
>> we may have to model dummy devices just to get the table parsed
>> if the devices aren't modelled and controlled in the kernel.
>> All of these seems to indicate that having the tables in the
>> driver is a better alternative, lol.
> 
> Or fix the OPP API. I don't think that having monitor tables for the
> SCMI-based platforms in the driver code is going to scale.

There is nothing to fix in the OPP API, looking at number
of clocks, interconnects and regulators to allocate
memory and associate a opp-table to a device is already
the right thing to do. So to adhere to this we would
need to list dummy clocks, interconnects even when it
isn't modelled or controlled by the kernel. Sure I'll
continue to have it in dt in the next re-spin and take
this series back in RFC.

-Sibi

> 
>>
>> -Sibi
>>
>>>
>>>>>>>> +        if (!tbl_np)
>>>>>>>> +                return ERR_PTR(-ENODEV);
>>>>>>>> +
>>>>>>>> +        len = min(of_get_available_child_count(tbl_np), MAX_MAP_ENTRIES);
>>>>>>>> +        if (len == 0)
>>>>>>>> +                return ERR_PTR(-ENODEV);
>>>>>>>> +
>>>>>>>> +        tbl = devm_kzalloc(dev, (len + 1) * sizeof(struct cpufreq_memfreq_map),
>>>>>>>> +                           GFP_KERNEL);
>>>>>>>> +        if (!tbl)
>>>>>>>> +                return ERR_PTR(-ENOMEM);
>>>>>>>> +
>>>>>>>> +        for_each_available_child_of_node(tbl_np, opp_np) {
>>>>>>>> +                ret = of_property_read_u64_index(opp_np, "opp-hz", 0, &rate);
>>>>>>>> +                if (ret < 0)
>>>>>>>> +                        return ERR_PTR(ret);
>>>>>>>> +
>>>>>>>> +                tbl[i].cpufreq_mhz = rate / HZ_PER_MHZ;
>>>>>>>> +
>>>>>>>> +                if (memory->hw_type != QCOM_MEM_TYPE_DDR_QOS) {
>>>>>>>> +                        ret = of_property_read_u64_index(opp_np, "opp-hz", 1, &rate);
>>>>>>>> +                        if (ret < 0)
>>>>>>>> +                                return ERR_PTR(ret);
>>>>>>>> +
>>>>>>>> +                        tbl[i].memfreq_khz = rate / HZ_PER_KHZ;
>>>>>>>> +                } else {
>>>>>>>> +                        ret = of_property_read_u32(opp_np, "opp-level", &level);
>>>>>>>> +                        if (ret < 0)
>>>>>>>> +                                return ERR_PTR(ret);
>>>>>>>> +
>>>>>>>> +                        tbl[i].memfreq_khz = level;
>>>>>>>> +                }
>>>>>>>> +
>>>>>>>> +                dev_dbg(dev, "Entry%d CPU:%u, Mem:%u\n", i, tbl[i].cpufreq_mhz, tbl[i].memfreq_khz);
>>>>>>>> +                i++;
>>>>>>>> +        }
>>>>>>>> +        *cnt = len;
>>>>>>>> +
>>>>>>>> +        return tbl;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static int process_scmi_memlat_of_node(struct scmi_device *sdev, struct scmi_memlat_info *info)
>>>>>>>> +{
>>>>>>>> +        struct scmi_monitor_info *monitor;
>>>>>>>> +        struct scmi_memory_info *memory;
>>>>>>>> +        char name[MAX_NAME_LEN];
>>>>>>>> +        u64 memfreq[2];
>>>>>>>> +        int ret;
>>>>>>>> +
>>>>>>>> +        ret = populate_cluster_info(info->cluster_info);
>>>>>>>> +        if (ret < 0) {
>>>>>>>> +                dev_err_probe(&sdev->dev, ret, "failed to populate cluster info\n");
>>>>>>>> +                goto err;
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +        of_node_get(sdev->dev.of_node);
>>>>>>>> +        do {
>>>>>>>> +                snprintf(name, sizeof(name), "memory-%d", info->memory_cnt);
>>>>>>>> +                struct device_node *memory_np __free(device_node) =
>>>>>>>> +                        of_find_node_by_name(sdev->dev.of_node, name);
>>>>>>>> +
>>>>>>>> +                if (!memory_np)
>>>>>>>> +                        break;
>>>>>>>> +
>>>>>>>> +                if (info->memory_cnt >= MAX_MEMORY_TYPES)
>>>>>>>> +                        return dev_err_probe(&sdev->dev, -EINVAL,
>>>>>>>> +                                             "failed to parse unsupported memory type\n");
>>>>>>>> +
>>>>>>>> +                memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
>>>>>>>> +                if (!memory) {
>>>>>>>> +                        ret = -ENOMEM;
>>>>>>>> +                        goto err;
>>>>>>>> +                }
>>>>>>>> +
>>>>>>>> +                ret = of_property_read_u32(memory_np, "qcom,memory-type", &memory->hw_type);
>>>>>>>> +                if (ret) {
>>>>>>>> +                        dev_err_probe(&sdev->dev, ret, "failed to read memory type\n");
>>>>>>>> +                        goto err;
>>>>>>>> +                }
>>>>>>>> +
>>>>>>>> +                ret = of_property_read_u64_array(memory_np, "freq-table-hz", memfreq, 2);
>>>>>>>> +                if (ret && (ret != -EINVAL)) {
>>>>>>>> +                        dev_err_probe(&sdev->dev, ret, "failed to read min/max freq\n");
>>>>>>>> +                        goto err;
>>>>>>>> +                }
>>>>>>>
>>>>>>> Can we get this information from the OPP table instead?
>>>>>>
>>>>>> we don't list all the available ddr/llcc freqs in the opp-table
>>>>>> so that we can keep the table constant across platforms.
>>>>>
>>>>> NO. Use opp-supported-hw to limit data to a particular platform. There
>>>>> is no reason to keep min/max out of the OPP table.
>>>>
>>>> if we are movign the opp-tables into driver data for the reasons
>>>> described above, this can probably stay?
>>>
>>> No. They duplicate the information that can be a part of the tables. It
>>> doesn't matter if the tables are in the driver or in DT.
>>>
>>>
> 
> 
> 

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-12-17 10:16                 ` Sibi Sankar
@ 2024-12-17 10:46                   ` Dmitry Baryshkov
  2024-12-17 11:05                     ` Sibi Sankar
  0 siblings, 1 reply; 64+ messages in thread
From: Dmitry Baryshkov @ 2024-12-17 10:46 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: myungjoo.ham, Kyungmin.park, cw00.choi, Viresh Kumar,
	sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid

On Tue, Dec 17, 2024 at 03:46:24PM +0530, Sibi Sankar wrote:
> 
> 
> On 12/5/24 17:00, Dmitry Baryshkov wrote:
> > On Thu, 5 Dec 2024 at 12:53, Sibi Sankar <quic_sibis@quicinc.com> wrote:
> > > 
> > > 
> > > 
> > > On 11/14/24 18:02, Dmitry Baryshkov wrote:
> > > > On Thu, Nov 14, 2024 at 09:43:53AM +0530, Sibi Sankar wrote:
> > > > > 
> > > > > 
> > > > > On 10/26/24 23:46, Dmitry Baryshkov wrote:
> > > > > > On Tue, Oct 22, 2024 at 01:48:25PM +0530, Sibi Sankar wrote:
> > > > > > > 
> > > > > > > 
> > > > > > > On 10/7/24 23:27, Dmitry Baryshkov wrote:
> > > > > > > > On Mon, Oct 07, 2024 at 11:40:22AM GMT, Sibi Sankar wrote:
> > > > 
> > > > > > > > 
> > > > > > > > > +};
> > > > > > > > > +
> > > > > > > > > +struct map_param_msg {
> > > > > > > > > +        u32 hw_type;
> > > > > > > > > +        u32 mon_idx;
> > > > > > > > > +        u32 nr_rows;
> > > > > > > > > +        struct 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
> > > > > > > > > +};
> > > > > > > > > +
> > > > > > > > > +#define EV_CPU_CYCLES           0
> > > > > > > > > +#define EV_INST_RETIRED         2
> > > > > > > > > +#define EV_L2_D_RFILL           5
> > > > > > > > > +
> > > > > > > > > +struct ev_map_msg {
> > > > > > > > > +        u32 num_evs;
> > > > > > > > > +        u32 hw_type;
> > > > > > > > > +        u32 cid[NUM_COMMON_EVS];
> > > > > > > > > +};
> > > > > > > > > +
> > > > > > > > > +struct cpufreq_memfreq_map {
> > > > > > > > > +        unsigned int cpufreq_mhz;
> > > > > > > > > +        unsigned int memfreq_khz;
> > > > > > > > > +};
> > > > > > > > > +
> > > > > > > > > +struct scmi_monitor_info {
> > > > > > > > > +        struct cpufreq_memfreq_map *freq_map;
> > > > > > > > > +        char mon_name[MAX_NAME_LEN];
> > > > > > > > > +        u32 mon_idx;
> > > > > > > > > +        u32 mon_type;
> > > > > > > > > +        u32 ipm_ceil;
> > > > > > > > > +        u32 mask;
> > > > > > > > > +        u32 freq_map_len;
> > > > > > > > > +};
> > > > > > > > > +
> > > > > > > > > +struct scmi_memory_info {
> > > > > > > > > +        struct scmi_monitor_info *monitor[MAX_MONITOR_CNT];
> > > > > > > > > +        u32 hw_type;
> > > > > > > > > +        int monitor_cnt;
> > > > > > > > > +        u32 min_freq;
> > > > > > > > > +        u32 max_freq;
> > > > > > > > > +};
> > > > > > > > > +
> > > > > > > > > +struct scmi_memlat_info {
> > > > > > > > > +        struct scmi_protocol_handle *ph;
> > > > > > > > > +        const struct qcom_generic_ext_ops *ops;
> > > > > > > > > +        struct scmi_memory_info *memory[MAX_MEMORY_TYPES];
> > > > > > > > > +        u32 cluster_info[NR_CPUS];
> > > > > > > > > +        int memory_cnt;
> > > > > > > > > +};
> > > > > > > > > +
> > > > > > > > > +static int populate_cluster_info(u32 *cluster_info)
> > > > > > > > > +{
> > > > > > > > > +        char name[MAX_NAME_LEN];
> > > > > > > > > +        int i = 0;
> > > > > > > > > +
> > > > > > > > > +        struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
> > > > > > > > > +        if (!cn)
> > > > > > > > > +                return -ENODEV;
> > > > > > > > > +
> > > > > > > > > +        struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
> > > > > > > > > +        if (!map)
> > > > > > > > > +                return -ENODEV;
> > > > > > > > > +
> > > > > > > > > +        do {
> > > > > > > > > +                snprintf(name, sizeof(name), "cluster%d", i);
> > > > > > > > > +                struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
> > > > > > > > > +                if (!c)
> > > > > > > > > +                        break;
> > > > > > > > > +
> > > > > > > > > +                *(cluster_info + i) = of_get_child_count(c);
> > > > > > > > > +                i++;
> > > > > > > > > +        } while (1);
> > > > > > > > 
> > > > > > > > Can you use existing API from drivers/base/arch_topology.c? If not, can
> > > > > > > > it be extended to support your usecase?
> > > > > > > 
> > > > > > > ack. But I'm pretty sure it's going to take a while for reaching such
> > > > > > > an agreement so I'll drop this feature during the next re-spin.
> > > > > > 
> > > > > > Why? What kind of API do you actually need? The arch_topology.c simply
> > > > > > exports a table of struct cpu_topology. Is it somehow different from
> > > > > > what you are parsing manually?
> > > > > 
> > > > > yup, we had to figure out the physical id of the cpu
> > > > > since cpus can be disabled by the bootloader using
> > > > > status = "failed" property and we have to pass this
> > > > > onto the cpucp memlat algorithm.
> > > > 
> > > > Isn't it equal to the index in the cpu_topology table?
> > > 
> > > from what I see cpu_topology indexes remain unpopulated
> > > for cpus that are disabled since get_cpu_for_node
> > > ignores those?
> > 
> > Why do you need cpu_topology for disabled aka non-existing CPU devices?
> 
> sorry was out sick couldn't back earlier. We need the know
> what cpus are disbled to pass on the correct mask of cpus
> enabled to the SCP.

Yes. So isn't it enough to know only the enabled CPUs?


-- 
With best wishes
Dmitry

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-12-17 10:46                   ` Dmitry Baryshkov
@ 2024-12-17 11:05                     ` Sibi Sankar
  2024-12-17 12:10                       ` Dmitry Baryshkov
  0 siblings, 1 reply; 64+ messages in thread
From: Sibi Sankar @ 2024-12-17 11:05 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: myungjoo.ham, Kyungmin.park, cw00.choi, Viresh Kumar,
	sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid



On 12/17/24 16:16, Dmitry Baryshkov wrote:
> On Tue, Dec 17, 2024 at 03:46:24PM +0530, Sibi Sankar wrote:
>>
>>
>> On 12/5/24 17:00, Dmitry Baryshkov wrote:
>>> On Thu, 5 Dec 2024 at 12:53, Sibi Sankar <quic_sibis@quicinc.com> wrote:
>>>>
>>>>
>>>>
>>>> On 11/14/24 18:02, Dmitry Baryshkov wrote:
>>>>> On Thu, Nov 14, 2024 at 09:43:53AM +0530, Sibi Sankar wrote:
>>>>>>
>>>>>>
>>>>>> On 10/26/24 23:46, Dmitry Baryshkov wrote:
>>>>>>> On Tue, Oct 22, 2024 at 01:48:25PM +0530, Sibi Sankar wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> On 10/7/24 23:27, Dmitry Baryshkov wrote:
>>>>>>>>> On Mon, Oct 07, 2024 at 11:40:22AM GMT, Sibi Sankar wrote:
>>>>>
>>>>>>>>>
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +struct map_param_msg {
>>>>>>>>>> +        u32 hw_type;
>>>>>>>>>> +        u32 mon_idx;
>>>>>>>>>> +        u32 nr_rows;
>>>>>>>>>> +        struct 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
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +#define EV_CPU_CYCLES           0
>>>>>>>>>> +#define EV_INST_RETIRED         2
>>>>>>>>>> +#define EV_L2_D_RFILL           5
>>>>>>>>>> +
>>>>>>>>>> +struct ev_map_msg {
>>>>>>>>>> +        u32 num_evs;
>>>>>>>>>> +        u32 hw_type;
>>>>>>>>>> +        u32 cid[NUM_COMMON_EVS];
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +struct cpufreq_memfreq_map {
>>>>>>>>>> +        unsigned int cpufreq_mhz;
>>>>>>>>>> +        unsigned int memfreq_khz;
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +struct scmi_monitor_info {
>>>>>>>>>> +        struct cpufreq_memfreq_map *freq_map;
>>>>>>>>>> +        char mon_name[MAX_NAME_LEN];
>>>>>>>>>> +        u32 mon_idx;
>>>>>>>>>> +        u32 mon_type;
>>>>>>>>>> +        u32 ipm_ceil;
>>>>>>>>>> +        u32 mask;
>>>>>>>>>> +        u32 freq_map_len;
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +struct scmi_memory_info {
>>>>>>>>>> +        struct scmi_monitor_info *monitor[MAX_MONITOR_CNT];
>>>>>>>>>> +        u32 hw_type;
>>>>>>>>>> +        int monitor_cnt;
>>>>>>>>>> +        u32 min_freq;
>>>>>>>>>> +        u32 max_freq;
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +struct scmi_memlat_info {
>>>>>>>>>> +        struct scmi_protocol_handle *ph;
>>>>>>>>>> +        const struct qcom_generic_ext_ops *ops;
>>>>>>>>>> +        struct scmi_memory_info *memory[MAX_MEMORY_TYPES];
>>>>>>>>>> +        u32 cluster_info[NR_CPUS];
>>>>>>>>>> +        int memory_cnt;
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +static int populate_cluster_info(u32 *cluster_info)
>>>>>>>>>> +{
>>>>>>>>>> +        char name[MAX_NAME_LEN];
>>>>>>>>>> +        int i = 0;
>>>>>>>>>> +
>>>>>>>>>> +        struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
>>>>>>>>>> +        if (!cn)
>>>>>>>>>> +                return -ENODEV;
>>>>>>>>>> +
>>>>>>>>>> +        struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
>>>>>>>>>> +        if (!map)
>>>>>>>>>> +                return -ENODEV;
>>>>>>>>>> +
>>>>>>>>>> +        do {
>>>>>>>>>> +                snprintf(name, sizeof(name), "cluster%d", i);
>>>>>>>>>> +                struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
>>>>>>>>>> +                if (!c)
>>>>>>>>>> +                        break;
>>>>>>>>>> +
>>>>>>>>>> +                *(cluster_info + i) = of_get_child_count(c);
>>>>>>>>>> +                i++;
>>>>>>>>>> +        } while (1);
>>>>>>>>>
>>>>>>>>> Can you use existing API from drivers/base/arch_topology.c? If not, can
>>>>>>>>> it be extended to support your usecase?
>>>>>>>>
>>>>>>>> ack. But I'm pretty sure it's going to take a while for reaching such
>>>>>>>> an agreement so I'll drop this feature during the next re-spin.
>>>>>>>
>>>>>>> Why? What kind of API do you actually need? The arch_topology.c simply
>>>>>>> exports a table of struct cpu_topology. Is it somehow different from
>>>>>>> what you are parsing manually?
>>>>>>
>>>>>> yup, we had to figure out the physical id of the cpu
>>>>>> since cpus can be disabled by the bootloader using
>>>>>> status = "failed" property and we have to pass this
>>>>>> onto the cpucp memlat algorithm.
>>>>>
>>>>> Isn't it equal to the index in the cpu_topology table?
>>>>
>>>> from what I see cpu_topology indexes remain unpopulated
>>>> for cpus that are disabled since get_cpu_for_node
>>>> ignores those?
>>>
>>> Why do you need cpu_topology for disabled aka non-existing CPU devices?
>>
>> sorry was out sick couldn't back earlier. We need the know
>> what cpus are disbled to pass on the correct mask of cpus
>> enabled to the SCP.
> 
> Yes. So isn't it enough to know only the enabled CPUs?

yes just knowing the physical index of the enabled cpus
should be enough.

> 
> 

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

* Re: [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension
  2024-12-05 15:27   ` Sudeep Holla
@ 2024-12-17 11:45     ` Sibi Sankar
  0 siblings, 0 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-12-17 11:45 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi



On 12/5/24 20:57, Sudeep Holla wrote:
> On Mon, Oct 07, 2024 at 11:40:19AM +0530, Sibi Sankar wrote:
>> Document the various memory buses that can be monitored and scaled by
>> the memory latency governor hosted by the QCOM SCMI Generic Extension
>> Protocol v1.0.
>>
>> Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com>
>> ---
>>
>> v3:
>> * Restructure the bindings to mimic IMX [Christian]
>>
>>   .../bindings/firmware/arm,scmi.yaml           |   1 +
>>   .../bindings/firmware/qcom,scmi-memlat.yaml   | 246 ++++++++++++++++++
>>   .../dt-bindings/firmware/qcom,scmi-memlat.h   |  22 ++
>>   3 files changed, 269 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
>>   create mode 100644 include/dt-bindings/firmware/qcom,scmi-memlat.h
>>
>> diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
>> index 54d7d11bfed4..1d405f429168 100644
>> --- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
>> +++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
>> @@ -24,6 +24,7 @@ description: |
>>   
>>   anyOf:
>>     - $ref: /schemas/firmware/nxp,imx95-scmi.yaml
>> +  - $ref: /schemas/firmware/qcom,scmi-memlat.yaml
>>   
>>   properties:
>>     $nodename:
>> diff --git a/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml b/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
>> new file mode 100644
>> index 000000000000..0e8ea6dacd6a
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/firmware/qcom,scmi-memlat.yaml
>> @@ -0,0 +1,246 @@
>> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/firmware/qcom,scmi-memlat.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Qualcomm SCMI Memory Bus nodes
>> +
>> +maintainers:
>> +  - Sibi Sankar <quic_sibis@quicinc.com>
>> +
>> +description:
>> +  This binding describes the various memory buses that can be monitored and scaled
>> +  by memory latency governor running on the CPU Control Processor (SCMI controller).
>> +
>> +properties:
>> +  protocol@80:
>> +    $ref: '/schemas/firmware/arm,scmi.yaml#/$defs/protocol-node'
>> +    unevaluatedProperties: false
>> +
>> +    properties:
>> +      reg:
>> +        const: 0x80
>> +
>> +    patternProperties:
>> +      '^memory-[0-9]$':
>> +        type: object
>> +        unevaluatedProperties: false
>> +        description:
>> +          The list of all memory buses that can be monitored and scaled by the
>> +          memory latency governor running on the SCMI controller.
>> +
>> +        properties:
>> +          qcom,memory-type:
>> +            $ref: /schemas/types.yaml#/definitions/uint32
>> +            enum: [0, 1, 2]
>> +            description: |
>> +              Memory Bus Identifier
>> +              0 = QCOM_MEM_TYPE_DDR
>> +              1 = QCOM_MEM_TYPE_LLCC
>> +              2 = QCOM_MEM_TYPE_DDR_QOS
>> +
>> +          freq-table-hz:
>> +            items:
>> +              items:
>> +                - description: Minimum frequency of the memory bus in Hz
>> +                - description: Maximum frequency of the memory bus in Hz
>> +
>> +        patternProperties:
>> +          '^monitor-[0-9]$':
>> +            type: object
>> +            unevaluatedProperties: false
>> +            description:
>> +              The list of all monitors detecting the memory latency bound workloads using
>> +              various counters.
>> +
>> +            properties:
>> +              qcom,compute-type:
>> +                description:
>> +                  Monitors of type compute perform bus dvfs based on a rudimentary CPU
>> +                  frequency to memory frequency map.
>> +                type: boolean
>> +
>> +              qcom,ipm-ceil:
>> +                $ref: /schemas/types.yaml#/definitions/uint32
>> +                description:
>> +                  Monitors having this property perform bus dvfs based on the same
>> +                  rudimentary table but the scaling is performed only if the calculated
>> +                  IPM (Instruction Per Misses) exceeds the given ceiling.
>> +
>> +              cpus:
>> +                $ref: /schemas/types.yaml#/definitions/phandle-array
>> +                description:
>> +                  Should be a list of phandles to CPU nodes (as described in
>> +                  Documentation/devicetree/bindings/arm/cpus.yaml).
> 
> And what exactly this list of cpu phandles represent here ?
> 
>> +
>> +              operating-points-v2: true
> 
> Can you elaborate why the protocol can't add a command to fetch this from
> the firmware to avoid any possible mismatch between the DT and firmware.
> 
>> +              opp-table:
>> +                type: object
>> +
>> +            required:
>> +              - cpus
>> +              - operating-points-v2
>> +
>> +            oneOf:
>> +              - required: [ 'qcom,compute-type' ]
>> +              - required: [ 'qcom,ipm-ceil' ]
>> +
>> +        required:
>> +          - qcom,memory-type
>> +          - freq-table-hz
>> +
>> +additionalProperties: true
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/firmware/qcom,scmi-memlat.h>
>> +
>> +    firmware {
>> +        scmi {
>> +            compatible = "arm,scmi";
>> +            mboxes = <&cpucp_mbox 0>, <&cpucp_mbox 2>;
>> +            mbox-names = "tx", "rx";
>> +            shmem = <&cpu_scp_lpri0>, <&cpu_scp_lpri1>;
>> +
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +
>> +            protocol@80 {
>> +                reg = <0x80>;
>> +
>> +                memory-0 {


Hey Sudeep,

Thanks for taking time to review the series :)

> 
> I am not sure if it is just me, but I find this binding hard to understand.
> Hence I thought I will look and the example and get better understanding
> instead.
> 
> So these monitors are numbered ? If there were any meaningful name it would

A memory type can have a variable number of monitors. The
monitors take in a table and memory type. They track the freq
of a group of cpus and put in requests to scale the memory
according to the table. The naming is just arbitrary here
and we can choose whatever that makes the most sense.


> have been slightly better but irrespective of that I find it hard as why
> the communication with firmware is not based on index and why they are not
> part of the bindings though I see indices used in the driver.
> 
>> +                    qcom,memory-type = <QCOM_MEM_TYPE_DDR>;
> 
> Also I see that the type can be easily derived from the index, care to
> elaborate why the firmware expects it to be sent, if not why is that
> information needed here in the DT ?

The firmware doesn't have any information on the memory available
for it to scale and relies of the linux telling it everything in
order to function.


-Sibi


> 

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-12-05 15:52             ` Johan Hovold
@ 2024-12-17 11:49               ` Sibi Sankar
  2024-12-19 10:37                 ` Johan Hovold
  0 siblings, 1 reply; 64+ messages in thread
From: Sibi Sankar @ 2024-12-17 11:49 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Cristian Marussi, sudeep.holla, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi



On 12/5/24 21:22, Johan Hovold wrote:
> On Thu, Dec 05, 2024 at 04:26:55PM +0530, Sibi Sankar wrote:
>> On 11/22/24 14:07, Johan Hovold wrote:
> 
>>> I have a Lenovo ThinkPad T14s set up now so I gave this series a spin
>>> there too, and there I do *not* see the above mentioned -EOPNOSUPP error
>>> and the memlat driver probes successfully.
>>>
>>> On the other hand, this series seems to have no effect on a kernel
>>> compilation benchmark. Is that expected?
>>
>> I can have a look at your tree. But memlat in general
>> depends on the cpu frequency when your benchmarks max
>> the cpu's the ddr/llcc are scaled accordingly by it.
> 
> A kernel compilation should max out the CPU frequency on all cores.
> 
>>> And does this mean that you should stick with the uppercase "MEMLAT"
>>> string after all? The firmware on my CRD is not the latest one, but I am
>>> using the latest available firmware for the T14s.
>>
>> We should stick with "memlat" if we run into a device in the
>> wild that doesn't support "MEMLAT"
> 
> Ok. So the updated firmware supports both strings?


Sry for the delay, was out sick. Yes the updated firmware supports both
strings.

-Sibi

> 
> Johan

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-12-17 11:05                     ` Sibi Sankar
@ 2024-12-17 12:10                       ` Dmitry Baryshkov
  0 siblings, 0 replies; 64+ messages in thread
From: Dmitry Baryshkov @ 2024-12-17 12:10 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: myungjoo.ham, Kyungmin.park, cw00.choi, Viresh Kumar,
	sudeep.holla, cristian.marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Amir Vajid

On Tue, Dec 17, 2024 at 04:35:15PM +0530, Sibi Sankar wrote:
> 
> 
> On 12/17/24 16:16, Dmitry Baryshkov wrote:
> > On Tue, Dec 17, 2024 at 03:46:24PM +0530, Sibi Sankar wrote:
> > > 
> > > 
> > > On 12/5/24 17:00, Dmitry Baryshkov wrote:
> > > > On Thu, 5 Dec 2024 at 12:53, Sibi Sankar <quic_sibis@quicinc.com> wrote:
> > > > > 
> > > > > 
> > > > > 
> > > > > On 11/14/24 18:02, Dmitry Baryshkov wrote:
> > > > > > On Thu, Nov 14, 2024 at 09:43:53AM +0530, Sibi Sankar wrote:
> > > > > > > 
> > > > > > > 
> > > > > > > On 10/26/24 23:46, Dmitry Baryshkov wrote:
> > > > > > > > On Tue, Oct 22, 2024 at 01:48:25PM +0530, Sibi Sankar wrote:
> > > > > > > > > 
> > > > > > > > > 
> > > > > > > > > On 10/7/24 23:27, Dmitry Baryshkov wrote:
> > > > > > > > > > On Mon, Oct 07, 2024 at 11:40:22AM GMT, Sibi Sankar wrote:
> > > > > > 
> > > > > > > > > > 
> > > > > > > > > > > +};
> > > > > > > > > > > +
> > > > > > > > > > > +struct map_param_msg {
> > > > > > > > > > > +        u32 hw_type;
> > > > > > > > > > > +        u32 mon_idx;
> > > > > > > > > > > +        u32 nr_rows;
> > > > > > > > > > > +        struct 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
> > > > > > > > > > > +};
> > > > > > > > > > > +
> > > > > > > > > > > +#define EV_CPU_CYCLES           0
> > > > > > > > > > > +#define EV_INST_RETIRED         2
> > > > > > > > > > > +#define EV_L2_D_RFILL           5
> > > > > > > > > > > +
> > > > > > > > > > > +struct ev_map_msg {
> > > > > > > > > > > +        u32 num_evs;
> > > > > > > > > > > +        u32 hw_type;
> > > > > > > > > > > +        u32 cid[NUM_COMMON_EVS];
> > > > > > > > > > > +};
> > > > > > > > > > > +
> > > > > > > > > > > +struct cpufreq_memfreq_map {
> > > > > > > > > > > +        unsigned int cpufreq_mhz;
> > > > > > > > > > > +        unsigned int memfreq_khz;
> > > > > > > > > > > +};
> > > > > > > > > > > +
> > > > > > > > > > > +struct scmi_monitor_info {
> > > > > > > > > > > +        struct cpufreq_memfreq_map *freq_map;
> > > > > > > > > > > +        char mon_name[MAX_NAME_LEN];
> > > > > > > > > > > +        u32 mon_idx;
> > > > > > > > > > > +        u32 mon_type;
> > > > > > > > > > > +        u32 ipm_ceil;
> > > > > > > > > > > +        u32 mask;
> > > > > > > > > > > +        u32 freq_map_len;
> > > > > > > > > > > +};
> > > > > > > > > > > +
> > > > > > > > > > > +struct scmi_memory_info {
> > > > > > > > > > > +        struct scmi_monitor_info *monitor[MAX_MONITOR_CNT];
> > > > > > > > > > > +        u32 hw_type;
> > > > > > > > > > > +        int monitor_cnt;
> > > > > > > > > > > +        u32 min_freq;
> > > > > > > > > > > +        u32 max_freq;
> > > > > > > > > > > +};
> > > > > > > > > > > +
> > > > > > > > > > > +struct scmi_memlat_info {
> > > > > > > > > > > +        struct scmi_protocol_handle *ph;
> > > > > > > > > > > +        const struct qcom_generic_ext_ops *ops;
> > > > > > > > > > > +        struct scmi_memory_info *memory[MAX_MEMORY_TYPES];
> > > > > > > > > > > +        u32 cluster_info[NR_CPUS];
> > > > > > > > > > > +        int memory_cnt;
> > > > > > > > > > > +};
> > > > > > > > > > > +
> > > > > > > > > > > +static int populate_cluster_info(u32 *cluster_info)
> > > > > > > > > > > +{
> > > > > > > > > > > +        char name[MAX_NAME_LEN];
> > > > > > > > > > > +        int i = 0;
> > > > > > > > > > > +
> > > > > > > > > > > +        struct device_node *cn __free(device_node) = of_find_node_by_path("/cpus");
> > > > > > > > > > > +        if (!cn)
> > > > > > > > > > > +                return -ENODEV;
> > > > > > > > > > > +
> > > > > > > > > > > +        struct device_node *map __free(device_node) = of_get_child_by_name(cn, "cpu-map");
> > > > > > > > > > > +        if (!map)
> > > > > > > > > > > +                return -ENODEV;
> > > > > > > > > > > +
> > > > > > > > > > > +        do {
> > > > > > > > > > > +                snprintf(name, sizeof(name), "cluster%d", i);
> > > > > > > > > > > +                struct device_node *c __free(device_node) = of_get_child_by_name(map, name);
> > > > > > > > > > > +                if (!c)
> > > > > > > > > > > +                        break;
> > > > > > > > > > > +
> > > > > > > > > > > +                *(cluster_info + i) = of_get_child_count(c);
> > > > > > > > > > > +                i++;
> > > > > > > > > > > +        } while (1);
> > > > > > > > > > 
> > > > > > > > > > Can you use existing API from drivers/base/arch_topology.c? If not, can
> > > > > > > > > > it be extended to support your usecase?
> > > > > > > > > 
> > > > > > > > > ack. But I'm pretty sure it's going to take a while for reaching such
> > > > > > > > > an agreement so I'll drop this feature during the next re-spin.
> > > > > > > > 
> > > > > > > > Why? What kind of API do you actually need? The arch_topology.c simply
> > > > > > > > exports a table of struct cpu_topology. Is it somehow different from
> > > > > > > > what you are parsing manually?
> > > > > > > 
> > > > > > > yup, we had to figure out the physical id of the cpu
> > > > > > > since cpus can be disabled by the bootloader using
> > > > > > > status = "failed" property and we have to pass this
> > > > > > > onto the cpucp memlat algorithm.
> > > > > > 
> > > > > > Isn't it equal to the index in the cpu_topology table?
> > > > > 
> > > > > from what I see cpu_topology indexes remain unpopulated
> > > > > for cpus that are disabled since get_cpu_for_node
> > > > > ignores those?
> > > > 
> > > > Why do you need cpu_topology for disabled aka non-existing CPU devices?
> > > 
> > > sorry was out sick couldn't back earlier. We need the know
> > > what cpus are disbled to pass on the correct mask of cpus
> > > enabled to the SCP.
> > 
> > Yes. So isn't it enough to know only the enabled CPUs?
> 
> yes just knowing the physical index of the enabled cpus
> should be enough.

Then exiting cpu_topology is enough for your case, isn't it?

-- 
With best wishes
Dmitry

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-12-05 17:01           ` Sudeep Holla
@ 2024-12-17 12:25             ` Sibi Sankar
  2024-12-17 14:45               ` Cristian Marussi
  2024-12-17 17:59               ` Sudeep Holla
  0 siblings, 2 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-12-17 12:25 UTC (permalink / raw)
  To: Sudeep Holla, Johan Hovold
  Cc: Cristian Marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi



On 12/5/24 22:31, Sudeep Holla wrote:
> On Fri, Nov 22, 2024 at 09:37:47AM +0100, Johan Hovold wrote:
>> On Thu, Nov 14, 2024 at 09:52:12AM +0530, Sibi Sankar wrote:
>>> On 11/8/24 20:44, Johan Hovold wrote:
>>
>>>>> On Wed, Nov 06, 2024 at 01:55:33PM +0100, Johan Hovold wrote:
>>
>>>>>> Second, after loading the protocol and client drivers manually (in that
>>>>>> order, shouldn't the client driver pull in the protocol?), I got:
>>>>>>
>>>>>> 	scmi_module: Loaded SCMI Vendor Protocol 0x80 - Qualcomm  20000
>>>>>> 	arm-scmi arm-scmi.0.auto: QCOM Generic Vendor Version 1.0
>>>>>> 	scmi-qcom-generic-ext-memlat scmi_dev.5: error -EOPNOTSUPP: failed to configure common events
>>>>>> 	scmi-qcom-generic-ext-memlat scmi_dev.5: probe with driver scmi-qcom-generic-ext-memlat failed with error -95
>>>>>>
>>>>>> which seems to suggest that the firmware on my CRD does not support this
>>>>>> feature. Is that the way this should be interpreted? And does that mean
>>>>>> that non of the commercial laptops supports this either?
>>
>>>> Yeah, hopefully Sibi can shed some light on this. I'm using the DT
>>>> patch (5/5) from this series, which according to the commit message is
>>>> supposed to enable bus scaling on the x1e80100 platform. So I guess
>>>> something is missing in my firmware.
>>>
>>> Nah, it's probably just because of the algo string used.
>>> The past few series used caps MEMLAT string instead of
>>> memlat to pass the tuneables, looks like all the laptops
>>> havn't really switched to it yet. Will revert back to
>>> using to lower case memlat so that all devices are
>>> supported. Thanks for trying the series out!
>>
>> I have a Lenovo ThinkPad T14s set up now so I gave this series a spin
>> there too, and there I do *not* see the above mentioned -EOPNOSUPP error
>> and the memlat driver probes successfully.
>>
>> On the other hand, this series seems to have no effect on a kernel
>> compilation benchmark. Is that expected?
>>
> 
> Hijacking this thread to rant about state of firmware implementation on
> this platform that gives me zero confidence in merging any of these without
> examining each of the interface details in depth and at lengths.
> 

Hey Sudeep,

Thanks for taking time to review the series.

> Also I see the standard protocol like PERF seem to have so many issues which
> adds to my no confidence. I can't comment on that thread for specific reasons.

^^ is largely untrue, a lot of finger pointing and a gross
misrepresentation of reality :/

The only major problem that X1E perf protocol has is a firmware
crash in the LEVEL_GET regular message implementation. This
pretty much went unnoticed because of messaging in perf implementation
in kernel. Given the fastchannel implementation isn't mandatory
according to spec, the kernel clearly says it switches to
regular messaging when it clearly doesn't do that and uses
stale data structures instead. This ensured that level get regular
messaging never got tested.

We pretty much have been good upstream citizens, finding bugs and
sending fixes wherever we can. We clearly don't deserve such a hostile
stance.

> 
> I will briefly mention my suspicion here. This Lenovo ThinkPad T14s being
> primarily targeting other OS using ACPI might have just implemented what is
> required for ACPI CPPC which conveniently doesn't have to discover lot of
> fastchannel details since they are supplied in the tables straight away.
> But that also would mean it could be not fully compliant to SCMI spec.

Not fully compliant to the spec? I am pretty sure this series would
have been shot down completely and NAKd on the list by you if that
was the case lol.

-Sibi

> 
> Either we need to run some compliance test suite(which again may need more
> work as it is unfortunately make platform specific - referring to [1])
> or use the raw interface in the kernel and throw /dev/random at it and see
> how well it can cope up.
> 
> --
> Regards,
> Sudeep
> 
> [1] https://gitlab.arm.com/tests/scmi-tests (Not so great and needs platform
>       specific vectors to check compliance)

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-12-17 12:25             ` Sibi Sankar
@ 2024-12-17 14:45               ` Cristian Marussi
  2024-12-23 14:09                 ` Sibi Sankar
  2024-12-17 17:59               ` Sudeep Holla
  1 sibling, 1 reply; 64+ messages in thread
From: Cristian Marussi @ 2024-12-17 14:45 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: Sudeep Holla, Johan Hovold, Cristian Marussi, andersson,
	konrad.dybcio, robh+dt, krzysztof.kozlowski+dt, linux-kernel,
	linux-arm-msm, devicetree, linux-arm-kernel, quic_rgottimu,
	quic_kshivnan, conor+dt, arm-scmi

On Tue, Dec 17, 2024 at 05:55:35PM +0530, Sibi Sankar wrote:
> 
> 
> On 12/5/24 22:31, Sudeep Holla wrote:
> > On Fri, Nov 22, 2024 at 09:37:47AM +0100, Johan Hovold wrote:
> > > On Thu, Nov 14, 2024 at 09:52:12AM +0530, Sibi Sankar wrote:
> > > > On 11/8/24 20:44, Johan Hovold wrote:
> > > 
> > > > > > On Wed, Nov 06, 2024 at 01:55:33PM +0100, Johan Hovold wrote:
> > > 
> > > > > > > Second, after loading the protocol and client drivers manually (in that
> > > > > > > order, shouldn't the client driver pull in the protocol?), I got:
> > > > > > > 
> > > > > > > 	scmi_module: Loaded SCMI Vendor Protocol 0x80 - Qualcomm  20000
> > > > > > > 	arm-scmi arm-scmi.0.auto: QCOM Generic Vendor Version 1.0
> > > > > > > 	scmi-qcom-generic-ext-memlat scmi_dev.5: error -EOPNOTSUPP: failed to configure common events
> > > > > > > 	scmi-qcom-generic-ext-memlat scmi_dev.5: probe with driver scmi-qcom-generic-ext-memlat failed with error -95
> > > > > > > 
> > > > > > > which seems to suggest that the firmware on my CRD does not support this
> > > > > > > feature. Is that the way this should be interpreted? And does that mean
> > > > > > > that non of the commercial laptops supports this either?
> > > 
> > > > > Yeah, hopefully Sibi can shed some light on this. I'm using the DT
> > > > > patch (5/5) from this series, which according to the commit message is
> > > > > supposed to enable bus scaling on the x1e80100 platform. So I guess
> > > > > something is missing in my firmware.
> > > > 
> > > > Nah, it's probably just because of the algo string used.
> > > > The past few series used caps MEMLAT string instead of
> > > > memlat to pass the tuneables, looks like all the laptops
> > > > havn't really switched to it yet. Will revert back to
> > > > using to lower case memlat so that all devices are
> > > > supported. Thanks for trying the series out!
> > > 
> > > I have a Lenovo ThinkPad T14s set up now so I gave this series a spin
> > > there too, and there I do *not* see the above mentioned -EOPNOSUPP error
> > > and the memlat driver probes successfully.
> > > 
> > > On the other hand, this series seems to have no effect on a kernel
> > > compilation benchmark. Is that expected?
> > > 
> > 
> > Hijacking this thread to rant about state of firmware implementation on
> > this platform that gives me zero confidence in merging any of these without
> > examining each of the interface details in depth and at lengths.
> > 
> 

Hi Sibi,

> Hey Sudeep,
> 
> Thanks for taking time to review the series.
> 
> > Also I see the standard protocol like PERF seem to have so many issues which
> > adds to my no confidence. I can't comment on that thread for specific reasons.
> 
> ^^ is largely untrue, a lot of finger pointing and a gross
> misrepresentation of reality :/
> 
> The only major problem that X1E perf protocol has is a firmware
> crash in the LEVEL_GET regular message implementation. This
> pretty much went unnoticed because of messaging in perf implementation
> in kernel. Given the fastchannel implementation isn't mandatory
> according to spec, the kernel clearly says it switches to
> regular messaging when it clearly doesn't do that and uses
> stale data structures instead. This ensured that level get regular
> messaging never got tested.

You claimed this a couple of times here and on IRC, but sincerely,
looking at the fastchannel implementation in SCMI core and Perf, I could
not track down where this could have happened in the recent code
(i.e. with or without your recent, welcomed, patches...)

When FC initialization fails and bailout it says:
	
	"Failed to get FC for protocol %X [MSG_ID:%u / RES_ID:%u] - ret:%d. Using regular messaging."

... and it clears any gathered address for that FC, so that in __scmi_perf_level_get()
you end up skipping the FC machinery and use messaging

	if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].get_addr) {
		...
	}

	return scmi_perf_msg_level_get(ph, dom->id, level, poll);

Now this is done ONLY for the FC that specifically failed
initialization, i.e. identified by the tuple PROTO_ID/MSG_ID/RES_ID
(as stated in the noisy message above where MSG_ID is specified) NOT for
all Fastchannel, so you can have an FC successfully initialized only on
the GET but failing in the SET, so only the GET FC will be used.

I dont really understand how the Kernel was misbehaving and using
instead stale data, neither, if this was the case, I can see where this
issue would have been fixed.

To be clear, I am not really interested in throwing an argument here, but
I sincerely dont see where the alleged problem was and how was fixed (kernel
side), so I fear it could be still there, hidden maybe by a change in the
platform fw.

Apologies if I missed something along the history of this..

Thanks,
Cristian

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-12-17 12:25             ` Sibi Sankar
  2024-12-17 14:45               ` Cristian Marussi
@ 2024-12-17 17:59               ` Sudeep Holla
  2024-12-23 14:14                 ` Sibi Sankar
  1 sibling, 1 reply; 64+ messages in thread
From: Sudeep Holla @ 2024-12-17 17:59 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: Johan Hovold, Sudeep Holla, Cristian Marussi, andersson,
	konrad.dybcio, robh+dt, krzysztof.kozlowski+dt, linux-kernel,
	linux-arm-msm, devicetree, linux-arm-kernel, quic_rgottimu,
	quic_kshivnan, conor+dt, arm-scmi

On Tue, Dec 17, 2024 at 05:55:35PM +0530, Sibi Sankar wrote:
> 
> 
> On 12/5/24 22:31, Sudeep Holla wrote:
> > On Fri, Nov 22, 2024 at 09:37:47AM +0100, Johan Hovold wrote:
> > > On Thu, Nov 14, 2024 at 09:52:12AM +0530, Sibi Sankar wrote:
> > > > On 11/8/24 20:44, Johan Hovold wrote:
> > > 
> > > > > > On Wed, Nov 06, 2024 at 01:55:33PM +0100, Johan Hovold wrote:
> > > 
> > > > > > > Second, after loading the protocol and client drivers manually (in that
> > > > > > > order, shouldn't the client driver pull in the protocol?), I got:
> > > > > > > 
> > > > > > > 	scmi_module: Loaded SCMI Vendor Protocol 0x80 - Qualcomm  20000
> > > > > > > 	arm-scmi arm-scmi.0.auto: QCOM Generic Vendor Version 1.0
> > > > > > > 	scmi-qcom-generic-ext-memlat scmi_dev.5: error -EOPNOTSUPP: failed to configure common events
> > > > > > > 	scmi-qcom-generic-ext-memlat scmi_dev.5: probe with driver scmi-qcom-generic-ext-memlat failed with error -95
> > > > > > > 
> > > > > > > which seems to suggest that the firmware on my CRD does not support this
> > > > > > > feature. Is that the way this should be interpreted? And does that mean
> > > > > > > that non of the commercial laptops supports this either?
> > > 
> > > > > Yeah, hopefully Sibi can shed some light on this. I'm using the DT
> > > > > patch (5/5) from this series, which according to the commit message is
> > > > > supposed to enable bus scaling on the x1e80100 platform. So I guess
> > > > > something is missing in my firmware.
> > > > 
> > > > Nah, it's probably just because of the algo string used.
> > > > The past few series used caps MEMLAT string instead of
> > > > memlat to pass the tuneables, looks like all the laptops
> > > > havn't really switched to it yet. Will revert back to
> > > > using to lower case memlat so that all devices are
> > > > supported. Thanks for trying the series out!
> > > 
> > > I have a Lenovo ThinkPad T14s set up now so I gave this series a spin
> > > there too, and there I do *not* see the above mentioned -EOPNOSUPP error
> > > and the memlat driver probes successfully.
> > > 
> > > On the other hand, this series seems to have no effect on a kernel
> > > compilation benchmark. Is that expected?
> > > 
> > 
> > Hijacking this thread to rant about state of firmware implementation on
> > this platform that gives me zero confidence in merging any of these without
> > examining each of the interface details in depth and at lengths.
> > 
> 
> Hey Sudeep,
> 
> Thanks for taking time to review the series.
> 
> > Also I see the standard protocol like PERF seem to have so many issues which
> > adds to my no confidence. I can't comment on that thread for specific reasons.
> 
> ^^ is largely untrue, a lot of finger pointing and a gross
> misrepresentation of reality :/
>

Sorry if I was not clear, I just said I don't have confidence yet and if
the firmware is stable, then it is just the impression I have arrived based
on the discussions.

> crash in the LEVEL_GET regular message implementation. This
> pretty much went unnoticed because of messaging in perf implementation
> in kernel.

OK, is there any scope to improve in your opinion ? Please suggest and
discuss or post a patch to have separate discussion.

> Given the fastchannel implementation isn't mandatory
> according to spec, the kernel clearly says it switches to
> regular messaging when it clearly doesn't do that and uses
> stale data structures instead.

Interesting, it sounds like a bug. Please provide details or patch to
fix the bug. That would probably fix it on whatever platform we are
concerned here.

> This ensured that level get regular messaging never got tested.
>

You seem to point at this bug several time now, we need to get it fixed,
but we need to understand it first if you want us to fix it or as mentioned
before you can as well post the patch.

> We pretty much have been good upstream citizens, finding bugs and
> sending fixes wherever we can. We clearly don't deserve such a hostile
> stance.
>

Not sure what made you think we are hostile towards your contributions.
We just need a maintainable solution merged upstream and we are working
towards the same. The documents written as part of this series is not
there yet to help me understand the protocol yet. I have asked questions
and answer to those can be made part of the next version to improve it
IMO.

> > I will briefly mention my suspicion here. This Lenovo ThinkPad T14s being
> > primarily targeting other OS using ACPI might have just implemented what is
> > required for ACPI CPPC which conveniently doesn't have to discover lot of
> > fastchannel details since they are supplied in the tables straight away.
> > But that also would mean it could be not fully compliant to SCMI spec.
>
> Not fully compliant to the spec? I am pretty sure this series would
> have been shot down completely and NAKd on the list by you if that
> was the case lol.
>

Honestly I am still trying to make any sense out of this vendor protocols.
The documents produced as part of this series doesn't help me understand
the same and that is my main feedback so far on this thread. I haven't
looked at the code yet so I can't comment on the same as I first need
to understand the vendor protocol document/specification.

-- 
Regards,
Sudeep

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-12-17 11:49               ` Sibi Sankar
@ 2024-12-19 10:37                 ` Johan Hovold
  2024-12-23 14:00                   ` Sibi Sankar
  0 siblings, 1 reply; 64+ messages in thread
From: Johan Hovold @ 2024-12-19 10:37 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: Cristian Marussi, sudeep.holla, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Ettore Chimenti

On Tue, Dec 17, 2024 at 05:19:25PM +0530, Sibi Sankar wrote:
> On 12/5/24 21:22, Johan Hovold wrote:
> > On Thu, Dec 05, 2024 at 04:26:55PM +0530, Sibi Sankar wrote:
> >> On 11/22/24 14:07, Johan Hovold wrote:
> > 
> >>> I have a Lenovo ThinkPad T14s set up now so I gave this series a spin
> >>> there too, and there I do *not* see the above mentioned -EOPNOSUPP error
> >>> and the memlat driver probes successfully.
> >>>
> >>> On the other hand, this series seems to have no effect on a kernel
> >>> compilation benchmark. Is that expected?
> >>
> >> I can have a look at your tree. But memlat in general
> >> depends on the cpu frequency when your benchmarks max
> >> the cpu's the ddr/llcc are scaled accordingly by it.
> > 
> > A kernel compilation should max out the CPU frequency on all cores.

Answering my own question here; bwmon should scale the buses for
benchmarks like kernel compilations so I guess the non-existing impact
of memlat is expected here.

Ettore helped me run some further benchmarks, including cachebench, but
also saw no positive (or negative) effect with this series running on an
X1E CRD (with recent firmware).

Do you have any suggestions of benchmarks to run where the effect of
memlat should show up? What have you been using for testing?

I did measure a possibly slightly higher (idle) power consumption with
memlat, but I guess that is also expected given the intended more
aggressive ramping of the bus clocks.

These are the branches (and configs; johan_defconfig) we've used for
testing:

	https://github.com/jhovold/linux/tree/wip/x1e80100-6.13-rc3
	https://github.com/jhovold/linux/tree/wip/x1e80100-6.13-rc3-memlat

> >>> And does this mean that you should stick with the uppercase "MEMLAT"
> >>> string after all? The firmware on my CRD is not the latest one, but I am
> >>> using the latest available firmware for the T14s.
> >>
> >> We should stick with "memlat" if we run into a device in the
> >> wild that doesn't support "MEMLAT"
> > 
> > Ok. So the updated firmware supports both strings?
> 
> Sry for the delay, was out sick. Yes the updated firmware supports both
> strings.

No worries, hope you're feeling better.

I noticed that the firmware on the T14s indeed accepts both strings.

Johan

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

* Re: [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor
  2024-12-05 12:39       ` Cristian Marussi
@ 2024-12-23 13:57         ` Sibi Sankar
  0 siblings, 0 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-12-23 13:57 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: Shivnandan Kumar, sudeep.holla, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, conor+dt, arm-scmi, Amir Vajid



On 12/5/24 18:09, Cristian Marussi wrote:
> On Thu, Dec 05, 2024 at 04:33:05PM +0530, Sibi Sankar wrote:
>>
>>
>> On 11/29/24 15:27, Shivnandan Kumar wrote:
>>>
>>>
> 
> Hi Sibi,
> 
> some rants down below :P

Hey Cristian,

Thanks for taking time to put out your thoughts!

>   
>>> On 10/7/2024 11:40 AM, Sibi Sankar wrote:
>>>> Introduce a 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).
>>>>
> 
> [snip]
> 
>>>> +    /* Set the effective cpu frequency calculation method */
>>>> +    ret = ops->set_param(ph, &cpucp_freq_method,
>>>> sizeof(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");
>>>> +
>>>
>>> Hi Sibi,
>>
>> Hey Shiv,
>>
>> Thanks for taking time to review the series!
>>
>>> Since the MEMLAT_SET_EFFECTIVE_FREQ_METHOD command is not supported in
>>> the legacy CPUCP firmware, it should be kept optional. This way, if the
>>> legacy firmware is used, the driver will not return an error when the
>>> CPUCP firmware returns -EOPNOTSUPP.
>>
>> The vendor protocol with the current major/minor version is
>> expected to work as is on x1e platforms. What legacy firmware
>> are you referring to? All future SoCs that plan to adhere to
>> it are expected to maintain this abi and can decide to make
>> use of alternate mechanisms to calculating frequency based
>> on additional dt properties set.
>>
> 
> Normally in the SCMI world you could leverage protocol versioning and
> the standard PROTOCOL_MESSAGE_ATTRIBUTES(0x2) command to let the agent
> investigate if the SCMI server it is speaking to implements or NOT a
> specific command and which specific version of that command is understood
> (with possibly varying size and fields)...
> 
> ...BUT since your vendor protocol is 'Generic' and, as it stands, it
> basically piggybacks any kind of binary payload (i.e. subcommands of
> some kind of subprotocols of yours) into the same 4 get/set/start/stop
> 'Generic' ops, without even specifying the transmitted/received payload
> sizes into the message itself....all of the possible SCMI versioning
> autodiscovery and backward-compatibility capabilities happily go out of
> the window because:
> 
> - your versioning refers to the generic protocol and you cannot possibly
>    describe all the possible future subcommands (opaque payloads) variations
>    and/or subcommands addition/removals purely on the major/minor version, AND
>    even if you did that, NONE of such future variations will be documented
>    anywhere since you are hiding all of this inside a bunch of binary blobs
> 
> - you dont even specify the payload sizes of the tx/rx 'Generic' payload
>    subcommands so it becomes even difficult for both the server and the
>    client to safely handle your 'Generic' subcommand message payloads
> 
> - you cannot issue a PROTOCOL_MESSAGE_ATTRIBUTE() to see if a specific
>    subcommand is supported, because your subcommand is NOT really a protocol
>    command but it is just one of the payloads of one of the 'Generic' protocol:
>    you commmands are only set/get/start/stop (maybe some sort of hack
>    could be doen around these...bit all will be even more flaky...)
> 
> - you dont implement NEGOTIATE_PROTOCOL_VERSION and so you cannot even
>    check if the SCMI server that you are speaking to will agree to
>    downgrade and 'speak' your Kernel SCMI agent (possibly older) 'Generic'
>    protocol version
> 
> All of this basically defeats all of the SCMI general capabilities
> around versioning and auto-discovery when it comes to your 'Generic' vendor
> protocol, with the end result that you will have to be EXTREMELY confident
> to perfectly match and keep in sync at all times your SCMI Server(FW) and
> Client(Kernel) sides, without any possibility of coping with a such a mismatch
> on the field by using some of the fallback/downgrade mechanism that you
> threw out of the window...

Even though we listed some of the background behind the generic
you have to understand it was done in a vacuum where QCOM might
have assumed that the entire vendor ID space was to be shared
across all vendors.

But your suggestions for adding another messages like NEGOTIATE
PROTOCOL_VERSION in a future major version upgrade would help
solve some of the problems you are listing out here.

Since there are devices that are out in the wild already running this
firmware, the first version of the generic vendor protocol is limited in
what it can accommodate. But like we already said we are open to changes
that will help review/maintain this for future SoCs without breakage.

> (...and sorry but looking at the above xchange about 'legacy firmware' I am
>   a bit skeptic about this as of now :D)

Some people within just used the wrong terminology here. There
are no "legacy" firmware since this is the first version of it
landing upstream and future versions have the options to implement
additional messages and ensure they do things in a way that is
easily reviewable/maintainable.

-Sibi

> 
> ...as I said initially when reviewing this series, you can do whatever
> you want within your Vendor protocol, but abusing the SCMI Vendor extensions
> capabilities in such a way could end up bringing to the table more cons
> (the above) than pros (some supposed 'simplification')
> 
> Thanks,
> Cristian

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-12-19 10:37                 ` Johan Hovold
@ 2024-12-23 14:00                   ` Sibi Sankar
  0 siblings, 0 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-12-23 14:00 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Cristian Marussi, sudeep.holla, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi, Ettore Chimenti



On 12/19/24 16:07, Johan Hovold wrote:
> On Tue, Dec 17, 2024 at 05:19:25PM +0530, Sibi Sankar wrote:
>> On 12/5/24 21:22, Johan Hovold wrote:
>>> On Thu, Dec 05, 2024 at 04:26:55PM +0530, Sibi Sankar wrote:
>>>> On 11/22/24 14:07, Johan Hovold wrote:
>>>
>>>>> I have a Lenovo ThinkPad T14s set up now so I gave this series a spin
>>>>> there too, and there I do *not* see the above mentioned -EOPNOSUPP error
>>>>> and the memlat driver probes successfully.
>>>>>
>>>>> On the other hand, this series seems to have no effect on a kernel
>>>>> compilation benchmark. Is that expected?
>>>>
>>>> I can have a look at your tree. But memlat in general
>>>> depends on the cpu frequency when your benchmarks max
>>>> the cpu's the ddr/llcc are scaled accordingly by it.
>>>
>>> A kernel compilation should max out the CPU frequency on all cores.
> 
> Answering my own question here; bwmon should scale the buses for
> benchmarks like kernel compilations so I guess the non-existing impact
> of memlat is expected here.

you would see impact only in cases where you would benefit from
having ddr and llcc at a higher frequency i.e. latency workloads.
I usually run geekbench with and we are expected to see a big
difference with and without it.

> 
> Ettore helped me run some further benchmarks, including cachebench, but
> also saw no positive (or negative) effect with this series running on an
> X1E CRD (with recent firmware).
> 
> Do you have any suggestions of benchmarks to run where the effect of
> memlat should show up? What have you been using for testing?
> 
> I did measure a possibly slightly higher (idle) power consumption with
> memlat, but I guess that is also expected given the intended more
> aggressive ramping of the bus clocks.
> 
> These are the branches (and configs; johan_defconfig) we've used for
> testing:
> 
> 	https://github.com/jhovold/linux/tree/wip/x1e80100-6.13-rc3
> 	https://github.com/jhovold/linux/tree/wip/x1e80100-6.13-rc3-memlat

Thanks, we'll get this sorted out.

> 
>>>>> And does this mean that you should stick with the uppercase "MEMLAT"
>>>>> string after all? The firmware on my CRD is not the latest one, but I am
>>>>> using the latest available firmware for the T14s.
>>>>
>>>> We should stick with "memlat" if we run into a device in the
>>>> wild that doesn't support "MEMLAT"
>>>
>>> Ok. So the updated firmware supports both strings?
>>
>> Sry for the delay, was out sick. Yes the updated firmware supports both
>> strings.
> 
> No worries, hope you're feeling better.
> 
> I noticed that the firmware on the T14s indeed accepts both strings.
> 
> Johan

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-12-17 14:45               ` Cristian Marussi
@ 2024-12-23 14:09                 ` Sibi Sankar
  0 siblings, 0 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-12-23 14:09 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: Sudeep Holla, Johan Hovold, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi



On 12/17/24 20:15, Cristian Marussi wrote:
> On Tue, Dec 17, 2024 at 05:55:35PM +0530, Sibi Sankar wrote:
>>
>>
>> On 12/5/24 22:31, Sudeep Holla wrote:
>>> On Fri, Nov 22, 2024 at 09:37:47AM +0100, Johan Hovold wrote:
>>>> On Thu, Nov 14, 2024 at 09:52:12AM +0530, Sibi Sankar wrote:
>>>>> On 11/8/24 20:44, Johan Hovold wrote:
>>>>
>>>>>>> On Wed, Nov 06, 2024 at 01:55:33PM +0100, Johan Hovold wrote:
>>>>
>>>>>>>> Second, after loading the protocol and client drivers manually (in that
>>>>>>>> order, shouldn't the client driver pull in the protocol?), I got:
>>>>>>>>
>>>>>>>> 	scmi_module: Loaded SCMI Vendor Protocol 0x80 - Qualcomm  20000
>>>>>>>> 	arm-scmi arm-scmi.0.auto: QCOM Generic Vendor Version 1.0
>>>>>>>> 	scmi-qcom-generic-ext-memlat scmi_dev.5: error -EOPNOTSUPP: failed to configure common events
>>>>>>>> 	scmi-qcom-generic-ext-memlat scmi_dev.5: probe with driver scmi-qcom-generic-ext-memlat failed with error -95
>>>>>>>>
>>>>>>>> which seems to suggest that the firmware on my CRD does not support this
>>>>>>>> feature. Is that the way this should be interpreted? And does that mean
>>>>>>>> that non of the commercial laptops supports this either?
>>>>
>>>>>> Yeah, hopefully Sibi can shed some light on this. I'm using the DT
>>>>>> patch (5/5) from this series, which according to the commit message is
>>>>>> supposed to enable bus scaling on the x1e80100 platform. So I guess
>>>>>> something is missing in my firmware.
>>>>>
>>>>> Nah, it's probably just because of the algo string used.
>>>>> The past few series used caps MEMLAT string instead of
>>>>> memlat to pass the tuneables, looks like all the laptops
>>>>> havn't really switched to it yet. Will revert back to
>>>>> using to lower case memlat so that all devices are
>>>>> supported. Thanks for trying the series out!
>>>>
>>>> I have a Lenovo ThinkPad T14s set up now so I gave this series a spin
>>>> there too, and there I do *not* see the above mentioned -EOPNOSUPP error
>>>> and the memlat driver probes successfully.
>>>>
>>>> On the other hand, this series seems to have no effect on a kernel
>>>> compilation benchmark. Is that expected?
>>>>
>>>
>>> Hijacking this thread to rant about state of firmware implementation on
>>> this platform that gives me zero confidence in merging any of these without
>>> examining each of the interface details in depth and at lengths.
>>>
>>
> 
> Hi Sibi,
> 
>> Hey Sudeep,
>>
>> Thanks for taking time to review the series.
>>
>>> Also I see the standard protocol like PERF seem to have so many issues which
>>> adds to my no confidence. I can't comment on that thread for specific reasons.
>>
>> ^^ is largely untrue, a lot of finger pointing and a gross
>> misrepresentation of reality :/
>>
>> The only major problem that X1E perf protocol has is a firmware
>> crash in the LEVEL_GET regular message implementation. This
>> pretty much went unnoticed because of messaging in perf implementation
>> in kernel. Given the fastchannel implementation isn't mandatory
>> according to spec, the kernel clearly says it switches to
>> regular messaging when it clearly doesn't do that and uses
>> stale data structures instead. This ensured that level get regular
>> messaging never got tested.
> 
> You claimed this a couple of times here and on IRC, but sincerely,
> looking at the fastchannel implementation in SCMI core and Perf, I could
> not track down where this could have happened in the recent code
> (i.e. with or without your recent, welcomed, patches...)
> 
> When FC initialization fails and bailout it says:
> 	
> 	"Failed to get FC for protocol %X [MSG_ID:%u / RES_ID:%u] - ret:%d. Using regular messaging."
> 
> ... and it clears any gathered address for that FC, so that in __scmi_perf_level_get()
> you end up skipping the FC machinery and use messaging
> 
> 	if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].get_addr) {
> 		...
> 	}
> 
> 	return scmi_perf_msg_level_get(ph, dom->id, level, poll);
> 
> Now this is done ONLY for the FC that specifically failed
> initialization, i.e. identified by the tuple PROTO_ID/MSG_ID/RES_ID
> (as stated in the noisy message above where MSG_ID is specified) NOT for
> all Fastchannel, so you can have an FC successfully initialized only on
> the GET but failing in the SET, so only the GET FC will be used.
> 
> I dont really understand how the Kernel was misbehaving and using
> instead stale data, neither, if this was the case, I can see where this
> issue would have been fixed.
> 
> To be clear, I am not really interested in throwing an argument here, but
> I sincerely dont see where the alleged problem was and how was fixed (kernel
> side), so I fear it could be still there, hidden maybe by a change in the
> platform fw.
> 
> Apologies if I missed something along the history of this..

lol, this is pretty embarrassing :|, It's just like you said
looks like this fw supports get_level fastchannel but fails
to say it supports it. This was the reason behind get_level
regular message for being never tested and being buggy and
had nothing to do the kernel messaging or being buggy.
My bad :(, sry again.

-Sibi

> 
> Thanks,
> Cristian

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

* Re: [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions
  2024-12-17 17:59               ` Sudeep Holla
@ 2024-12-23 14:14                 ` Sibi Sankar
  0 siblings, 0 replies; 64+ messages in thread
From: Sibi Sankar @ 2024-12-23 14:14 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Johan Hovold, Cristian Marussi, andersson, konrad.dybcio, robh+dt,
	krzysztof.kozlowski+dt, linux-kernel, linux-arm-msm, devicetree,
	linux-arm-kernel, quic_rgottimu, quic_kshivnan, conor+dt,
	arm-scmi



On 12/17/24 23:29, Sudeep Holla wrote:
> On Tue, Dec 17, 2024 at 05:55:35PM +0530, Sibi Sankar wrote:
>>
>>
>> On 12/5/24 22:31, Sudeep Holla wrote:
>>> On Fri, Nov 22, 2024 at 09:37:47AM +0100, Johan Hovold wrote:
>>>> On Thu, Nov 14, 2024 at 09:52:12AM +0530, Sibi Sankar wrote:
>>>>> On 11/8/24 20:44, Johan Hovold wrote:
>>>>
>>>>>>> On Wed, Nov 06, 2024 at 01:55:33PM +0100, Johan Hovold wrote:
>>>>
>>>>>>>> Second, after loading the protocol and client drivers manually (in that
>>>>>>>> order, shouldn't the client driver pull in the protocol?), I got:
>>>>>>>>
>>>>>>>> 	scmi_module: Loaded SCMI Vendor Protocol 0x80 - Qualcomm  20000
>>>>>>>> 	arm-scmi arm-scmi.0.auto: QCOM Generic Vendor Version 1.0
>>>>>>>> 	scmi-qcom-generic-ext-memlat scmi_dev.5: error -EOPNOTSUPP: failed to configure common events
>>>>>>>> 	scmi-qcom-generic-ext-memlat scmi_dev.5: probe with driver scmi-qcom-generic-ext-memlat failed with error -95
>>>>>>>>
>>>>>>>> which seems to suggest that the firmware on my CRD does not support this
>>>>>>>> feature. Is that the way this should be interpreted? And does that mean
>>>>>>>> that non of the commercial laptops supports this either?
>>>>
>>>>>> Yeah, hopefully Sibi can shed some light on this. I'm using the DT
>>>>>> patch (5/5) from this series, which according to the commit message is
>>>>>> supposed to enable bus scaling on the x1e80100 platform. So I guess
>>>>>> something is missing in my firmware.
>>>>>
>>>>> Nah, it's probably just because of the algo string used.
>>>>> The past few series used caps MEMLAT string instead of
>>>>> memlat to pass the tuneables, looks like all the laptops
>>>>> havn't really switched to it yet. Will revert back to
>>>>> using to lower case memlat so that all devices are
>>>>> supported. Thanks for trying the series out!
>>>>
>>>> I have a Lenovo ThinkPad T14s set up now so I gave this series a spin
>>>> there too, and there I do *not* see the above mentioned -EOPNOSUPP error
>>>> and the memlat driver probes successfully.
>>>>
>>>> On the other hand, this series seems to have no effect on a kernel
>>>> compilation benchmark. Is that expected?
>>>>
>>>
>>> Hijacking this thread to rant about state of firmware implementation on
>>> this platform that gives me zero confidence in merging any of these without
>>> examining each of the interface details in depth and at lengths.
>>>
>>
>> Hey Sudeep,
>>
>> Thanks for taking time to review the series.
>>
>>> Also I see the standard protocol like PERF seem to have so many issues which
>>> adds to my no confidence. I can't comment on that thread for specific reasons.
>>
>> ^^ is largely untrue, a lot of finger pointing and a gross
>> misrepresentation of reality :/
>>
> 
> Sorry if I was not clear, I just said I don't have confidence yet and if
> the firmware is stable, then it is just the impression I have arrived based
> on the discussions.

It's like you said the SCMI PERF protocol isn't used in Windows
but they do vendor protocol for bus scaling i.e. the memlat
algostring hosted on the generic vendor protocol. So those
bits are expected to be pretty stable.

> 
>> crash in the LEVEL_GET regular message implementation. This
>> pretty much went unnoticed because of messaging in perf implementation
>> in kernel.
> 
> OK, is there any scope to improve in your opinion ? Please suggest and
> discuss or post a patch to have separate discussion.
> 
>> Given the fastchannel implementation isn't mandatory
>> according to spec, the kernel clearly says it switches to
>> regular messaging when it clearly doesn't do that and uses
>> stale data structures instead.
> 
> Interesting, it sounds like a bug. Please provide details or patch to
> fix the bug. That would probably fix it on whatever platform we are
> concerned here.

sry, It was just a misunderstanding. Please ignore.

> 
>> This ensured that level get regular messaging never got tested.
>>
> 
> You seem to point at this bug several time now, we need to get it fixed,
> but we need to understand it first if you want us to fix it or as mentioned
> before you can as well post the patch.
> 
>> We pretty much have been good upstream citizens, finding bugs and
>> sending fixes wherever we can. We clearly don't deserve such a hostile
>> stance.
>>
> 
> Not sure what made you think we are hostile towards your contributions.
> We just need a maintainable solution merged upstream and we are working
> towards the same. The documents written as part of this series is not
> there yet to help me understand the protocol yet. I have asked questions
> and answer to those can be made part of the next version to improve it
> IMO.

Ack and we would ensure those get implemented to ensure the
protocol remains easily reviewable and maintainable.

-Sibi

> 
>>> I will briefly mention my suspicion here. This Lenovo ThinkPad T14s being
>>> primarily targeting other OS using ACPI might have just implemented what is
>>> required for ACPI CPPC which conveniently doesn't have to discover lot of
>>> fastchannel details since they are supplied in the tables straight away.
>>> But that also would mean it could be not fully compliant to SCMI spec.
>>
>> Not fully compliant to the spec? I am pretty sure this series would
>> have been shot down completely and NAKd on the list by you if that
>> was the case lol.
>>
> 
> Honestly I am still trying to make any sense out of this vendor protocols.
> The documents produced as part of this series doesn't help me understand
> the same and that is my main feedback so far on this thread. I haven't
> looked at the code yet so I can't comment on the same as I first need
> to understand the vendor protocol document/specification.
> 

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

end of thread, other threads:[~2024-12-23 14:50 UTC | newest]

Thread overview: 64+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-07  6:10 [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Sibi Sankar
2024-10-07  6:10 ` [PATCH V4 1/5] dt-bindings: firmware: Document bindings for QCOM SCMI Generic Extension Sibi Sankar
2024-10-07 18:06   ` Dmitry Baryshkov
2024-10-22  7:13     ` Sibi Sankar
2024-10-24 19:54       ` Dmitry Baryshkov
2024-10-08  6:47   ` Krzysztof Kozlowski
2024-10-08  6:49   ` Krzysztof Kozlowski
2024-10-08 12:10     ` Dmitry Baryshkov
2024-10-08 12:11       ` Krzysztof Kozlowski
2024-10-22  7:25         ` Sibi Sankar
2024-10-24 13:29           ` Krzysztof Kozlowski
2024-10-24 19:46             ` Dmitry Baryshkov
2024-10-24 19:48           ` Dmitry Baryshkov
2024-11-06 22:18   ` Jeffrey Hugo
2024-11-14  4:17     ` Sibi Sankar
2024-12-05 15:27   ` Sudeep Holla
2024-12-17 11:45     ` Sibi Sankar
2024-10-07  6:10 ` [PATCH V4 2/5] firmware: arm_scmi: Add QCOM Generic Vendor Protocol documentation Sibi Sankar
2024-10-22 10:22   ` Cristian Marussi
2024-11-14  4:32     ` Sibi Sankar
2024-10-07  6:10 ` [PATCH V4 3/5] firmware: arm_scmi: vendors: Add QCOM SCMI Generic Extensions Sibi Sankar
2024-10-07 18:13   ` Dmitry Baryshkov
2024-10-22  7:18     ` Sibi Sankar
2024-10-07  6:10 ` [PATCH V4 4/5] soc: qcom: Introduce SCMI based Memlat (Memory Latency) governor Sibi Sankar
2024-10-07 17:57   ` Dmitry Baryshkov
2024-10-22  8:18     ` Sibi Sankar
2024-10-26 18:16       ` Dmitry Baryshkov
2024-11-14  4:13         ` Sibi Sankar
2024-11-14 12:32           ` Dmitry Baryshkov
2024-12-05 10:52             ` Sibi Sankar
2024-12-05 11:30               ` Dmitry Baryshkov
2024-12-17 10:16                 ` Sibi Sankar
2024-12-17 10:46                   ` Dmitry Baryshkov
2024-12-17 11:05                     ` Sibi Sankar
2024-12-17 12:10                       ` Dmitry Baryshkov
     [not found]         ` <CGME20241114041419epcas1p3b52bb9795ffd9efa568bb106ba268e02@epcms1p5>
2024-11-15  0:38           ` MyungJoo Ham
2024-12-05 10:17             ` Sibi Sankar
2024-10-28  8:30       ` Cristian Marussi
2024-10-10 12:18   ` Jonathan Cameron
2024-10-22  7:31     ` Sibi Sankar
2024-10-22 12:00   ` Cristian Marussi
2024-11-29  9:57   ` Shivnandan Kumar
2024-12-05 11:03     ` Sibi Sankar
2024-12-05 12:39       ` Cristian Marussi
2024-12-23 13:57         ` Sibi Sankar
2024-10-07  6:10 ` [PATCH V4 5/5] arm64: dts: qcom: x1e80100: Enable LLCC/DDR/DDR_QOS dvfs Sibi Sankar
2024-10-08  6:52 ` [PATCH V4 0/5] arm_scmi: vendors: Qualcomm Generic Vendor Extensions Krzysztof Kozlowski
2024-10-22  8:24   ` Sibi Sankar
2024-11-06 12:55 ` Johan Hovold
2024-11-06 20:03   ` Cristian Marussi
2024-11-08 15:14     ` Johan Hovold
2024-11-14  4:22       ` Sibi Sankar
2024-11-22  8:37         ` Johan Hovold
2024-12-05 10:56           ` Sibi Sankar
2024-12-05 15:52             ` Johan Hovold
2024-12-17 11:49               ` Sibi Sankar
2024-12-19 10:37                 ` Johan Hovold
2024-12-23 14:00                   ` Sibi Sankar
2024-12-05 17:01           ` Sudeep Holla
2024-12-17 12:25             ` Sibi Sankar
2024-12-17 14:45               ` Cristian Marussi
2024-12-23 14:09                 ` Sibi Sankar
2024-12-17 17:59               ` Sudeep Holla
2024-12-23 14:14                 ` Sibi Sankar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).