]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
soc: qcom: qcom_stats: Add support to read DDR statistic
authorMaulik Shah <maulik.shah@oss.qualcomm.com>
Wed, 11 Jun 2025 06:03:45 +0000 (11:33 +0530)
committerBjorn Andersson <andersson@kernel.org>
Tue, 17 Jun 2025 14:12:08 +0000 (09:12 -0500)
DDR statistic provide different DDR LPM and DDR frequency statistic.
Add support to read from MSGRAM and display via debugfs.

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20250611-ddr_stats_-v5-1-24b16dd67c9c@oss.qualcomm.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
drivers/soc/qcom/qcom_stats.c

index 5de99cf59b9fbe32c0580e371c3cc362dfabb895..33fd2a1574464768bd07289e743fbb79ba415e84 100644 (file)
@@ -1,8 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2011-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2025, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
+#include <linux/bitfield.h>
 #include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/io.h>
 #define ACCUMULATED_OFFSET     0x18
 #define CLIENT_VOTES_OFFSET    0x20
 
+#define DDR_STATS_MAGIC_KEY            0xA1157A75
+#define DDR_STATS_MAX_NUM_MODES                20
+#define DDR_STATS_MAGIC_KEY_ADDR       0x0
+#define DDR_STATS_NUM_MODES_ADDR       0x4
+#define DDR_STATS_ENTRY_START_ADDR     0x8
+
+#define DDR_STATS_CP_IDX(data)         FIELD_GET(GENMASK(4, 0), data)
+#define DDR_STATS_LPM_NAME(data)       FIELD_GET(GENMASK(7, 0), data)
+#define DDR_STATS_TYPE(data)           FIELD_GET(GENMASK(15, 8), data)
+#define DDR_STATS_FREQ(data)           FIELD_GET(GENMASK(31, 16), data)
+
 struct subsystem_data {
        const char *name;
        u32 smem_item;
@@ -48,12 +61,19 @@ static const struct subsystem_data subsystems[] = {
 
 struct stats_config {
        size_t stats_offset;
+       size_t ddr_stats_offset;
        size_t num_records;
        bool appended_stats_avail;
        bool dynamic_offset;
        bool subsystem_stats_in_smem;
 };
 
+struct ddr_stats_entry {
+       u32 name;
+       u32 count;
+       u64 duration;
+};
+
 struct stats_data {
        bool appended_stats_avail;
        void __iomem *base;
@@ -122,8 +142,85 @@ static int qcom_soc_sleep_stats_show(struct seq_file *s, void *unused)
        return 0;
 }
 
+static void qcom_ddr_stats_print(struct seq_file *s, struct ddr_stats_entry *data)
+{
+       u32 cp_idx;
+
+       /*
+        * DDR statistic have two different types of details encoded.
+        * (1) DDR LPM Stats
+        * (2) DDR Frequency Stats
+        *
+        * The name field have details like which type of DDR stat (bits 8:15)
+        * along with other details as explained below
+        *
+        * In case of DDR LPM stat, name field will be encoded as,
+        * Bits  -  Meaning
+        * 0:7   -  DDR LPM name, can be of 0xd4, 0xd3, 0x11 and 0xd0.
+        * 8:15  -  0x0 (indicates its a LPM stat)
+        * 16:31 -  Unused
+        *
+        * In case of DDR FREQ stats, name field will be encoded as,
+        * Bits  -  Meaning
+        * 0:4   -  DDR Clock plan index (CP IDX)
+        * 5:7   -  Unused
+        * 8:15  -  0x1 (indicates its Freq stat)
+        * 16:31 -  Frequency value in Mhz
+        */
+       switch (DDR_STATS_TYPE(data->name)) {
+       case 0:
+               seq_printf(s, "DDR LPM Stat Name:0x%lx\tcount:%u\tDuration (ticks):%llu\n",
+                          DDR_STATS_LPM_NAME(data->name), data->count, data->duration);
+               break;
+       case 1:
+               if (!data->count || !DDR_STATS_FREQ(data->name))
+                       return;
+
+               cp_idx = DDR_STATS_CP_IDX(data->name);
+               seq_printf(s, "DDR Freq %luMhz:\tCP IDX:%u\tcount:%u\tDuration (ticks):%llu\n",
+                          DDR_STATS_FREQ(data->name), cp_idx, data->count, data->duration);
+               break;
+       }
+}
+
+static int qcom_ddr_stats_show(struct seq_file *s, void *d)
+{
+       struct ddr_stats_entry data[DDR_STATS_MAX_NUM_MODES];
+       void __iomem *reg = (void __iomem *)s->private;
+       u32 entry_count;
+       int i;
+
+       entry_count = readl_relaxed(reg + DDR_STATS_NUM_MODES_ADDR);
+       if (entry_count > DDR_STATS_MAX_NUM_MODES)
+               return -EINVAL;
+
+       reg += DDR_STATS_ENTRY_START_ADDR;
+       memcpy_fromio(data, reg, sizeof(struct ddr_stats_entry) * entry_count);
+
+       for (i = 0; i < entry_count; i++)
+               qcom_ddr_stats_print(s, &data[i]);
+
+       return 0;
+}
+
 DEFINE_SHOW_ATTRIBUTE(qcom_soc_sleep_stats);
 DEFINE_SHOW_ATTRIBUTE(qcom_subsystem_sleep_stats);
+DEFINE_SHOW_ATTRIBUTE(qcom_ddr_stats);
+
+static void qcom_create_ddr_stat_files(struct dentry *root, void __iomem *reg,
+                                      const struct stats_config *config)
+{
+       u32 key;
+
+       if (!config->ddr_stats_offset)
+               return;
+
+       key = readl_relaxed(reg + config->ddr_stats_offset + DDR_STATS_MAGIC_KEY_ADDR);
+       if (key == DDR_STATS_MAGIC_KEY)
+               debugfs_create_file("ddr_stats", 0400, root,
+                                   (__force void *)reg + config->ddr_stats_offset,
+                                   &qcom_ddr_stats_fops);
+}
 
 static void qcom_create_soc_sleep_stat_files(struct dentry *root, void __iomem *reg,
                                             struct stats_data *d,
@@ -212,6 +309,7 @@ static int qcom_stats_probe(struct platform_device *pdev)
 
        qcom_create_subsystem_stat_files(root, config);
        qcom_create_soc_sleep_stat_files(root, reg, d, config);
+       qcom_create_ddr_stat_files(root, reg, config);
 
        platform_set_drvdata(pdev, root);
 
@@ -254,6 +352,7 @@ static const struct stats_config rpmh_data_sdm845 = {
 
 static const struct stats_config rpmh_data = {
        .stats_offset = 0x48,
+       .ddr_stats_offset = 0xb8,
        .num_records = 3,
        .appended_stats_avail = false,
        .dynamic_offset = false,