[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250429-ddr_stats_-v1-1-4fc818aab7bb@oss.qualcomm.com>
Date: Tue, 29 Apr 2025 09:22:33 +0530
From: Maulik Shah <maulik.shah@....qualcomm.com>
To: Bjorn Andersson <andersson@...nel.org>,
Konrad Dybcio <konradybcio@...nel.org>, Rob Herring <robh@...nel.org>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>
Cc: linux-arm-msm@...r.kernel.org, linux-kernel@...r.kernel.org,
devicetree@...r.kernel.org,
Marijn Suijten <marijn.suijten@...ainline.org>,
Doug Anderson <dianders@...omium.org>,
Maulik Shah <maulik.shah@....qualcomm.com>
Subject: [PATCH 1/3] soc: qcom: qcom_stats: Add support to read DDR
statistic
DDR statistic provide different DDR LPM and DDR frequency statistic.
Add support to read from MSGRAM and display via debugfs.
Signed-off-by: Maulik Shah <maulik.shah@....qualcomm.com>
---
drivers/soc/qcom/qcom_stats.c | 99 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 99 insertions(+)
diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c
index 5de99cf59b9fbe32c0580e371c3cc362dfabb895..ee11fb0919742454d40442112787c087ba8f6598 100644
--- a/drivers/soc/qcom/qcom_stats.c
+++ b/drivers/soc/qcom/qcom_stats.c
@@ -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>
@@ -24,6 +26,17 @@
#define ACCUMULATED_OFFSET 0x18
#define CLIENT_VOTES_OFFSET 0x20
+#define DDR_STATS_MAGIC_KEY 0xA1157A75
+#define DDR_STATS_MAX_NUM_MODES 0x14
+#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, name;
+
+ /*
+ * 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
+ */
+ name = DDR_STATS_TYPE(data->name);
+ if (name == 0x0) {
+ name = DDR_STATS_LPM_NAME(data->name);
+ seq_printf(s, "DDR LPM Stat Name:0x%x\tcount:%u\tDuration (ticks):%llu\n",
+ name, data->count, data->duration);
+ } else if (name == 0x1) {
+ name = DDR_STATS_FREQ(data->name);
+ if (!name || !data->count)
+ return;
+
+ cp_idx = DDR_STATS_CP_IDX(data->name);
+ seq_printf(s, "DDR Freq %uMhz:\tCP IDX:%u\tcount:%u\tDuration (ticks):%llu\n",
+ name, cp_idx, data->count, data->duration);
+ }
+}
+
+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 0;
+
+ 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,
--
2.34.1
Powered by blists - more mailing lists