Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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.

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>

authored by

Maulik Shah and committed by
Bjorn Andersson
33301e5b 47e339ca

+99
+99
drivers/soc/qcom/qcom_stats.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 3 * Copyright (c) 2011-2021, The Linux Foundation. All rights reserved. 4 + * Copyright (c) 2022-2025, Qualcomm Innovation Center, Inc. All rights reserved. 4 5 */ 5 6 7 + #include <linux/bitfield.h> 6 8 #include <linux/debugfs.h> 7 9 #include <linux/device.h> 8 10 #include <linux/io.h> ··· 25 23 #define LAST_EXITED_AT_OFFSET 0x10 26 24 #define ACCUMULATED_OFFSET 0x18 27 25 #define CLIENT_VOTES_OFFSET 0x20 26 + 27 + #define DDR_STATS_MAGIC_KEY 0xA1157A75 28 + #define DDR_STATS_MAX_NUM_MODES 20 29 + #define DDR_STATS_MAGIC_KEY_ADDR 0x0 30 + #define DDR_STATS_NUM_MODES_ADDR 0x4 31 + #define DDR_STATS_ENTRY_START_ADDR 0x8 32 + 33 + #define DDR_STATS_CP_IDX(data) FIELD_GET(GENMASK(4, 0), data) 34 + #define DDR_STATS_LPM_NAME(data) FIELD_GET(GENMASK(7, 0), data) 35 + #define DDR_STATS_TYPE(data) FIELD_GET(GENMASK(15, 8), data) 36 + #define DDR_STATS_FREQ(data) FIELD_GET(GENMASK(31, 16), data) 28 37 29 38 struct subsystem_data { 30 39 const char *name; ··· 61 48 62 49 struct stats_config { 63 50 size_t stats_offset; 51 + size_t ddr_stats_offset; 64 52 size_t num_records; 65 53 bool appended_stats_avail; 66 54 bool dynamic_offset; 67 55 bool subsystem_stats_in_smem; 56 + }; 57 + 58 + struct ddr_stats_entry { 59 + u32 name; 60 + u32 count; 61 + u64 duration; 68 62 }; 69 63 70 64 struct stats_data { ··· 142 122 return 0; 143 123 } 144 124 125 + static void qcom_ddr_stats_print(struct seq_file *s, struct ddr_stats_entry *data) 126 + { 127 + u32 cp_idx; 128 + 129 + /* 130 + * DDR statistic have two different types of details encoded. 131 + * (1) DDR LPM Stats 132 + * (2) DDR Frequency Stats 133 + * 134 + * The name field have details like which type of DDR stat (bits 8:15) 135 + * along with other details as explained below 136 + * 137 + * In case of DDR LPM stat, name field will be encoded as, 138 + * Bits - Meaning 139 + * 0:7 - DDR LPM name, can be of 0xd4, 0xd3, 0x11 and 0xd0. 140 + * 8:15 - 0x0 (indicates its a LPM stat) 141 + * 16:31 - Unused 142 + * 143 + * In case of DDR FREQ stats, name field will be encoded as, 144 + * Bits - Meaning 145 + * 0:4 - DDR Clock plan index (CP IDX) 146 + * 5:7 - Unused 147 + * 8:15 - 0x1 (indicates its Freq stat) 148 + * 16:31 - Frequency value in Mhz 149 + */ 150 + switch (DDR_STATS_TYPE(data->name)) { 151 + case 0: 152 + seq_printf(s, "DDR LPM Stat Name:0x%lx\tcount:%u\tDuration (ticks):%llu\n", 153 + DDR_STATS_LPM_NAME(data->name), data->count, data->duration); 154 + break; 155 + case 1: 156 + if (!data->count || !DDR_STATS_FREQ(data->name)) 157 + return; 158 + 159 + cp_idx = DDR_STATS_CP_IDX(data->name); 160 + seq_printf(s, "DDR Freq %luMhz:\tCP IDX:%u\tcount:%u\tDuration (ticks):%llu\n", 161 + DDR_STATS_FREQ(data->name), cp_idx, data->count, data->duration); 162 + break; 163 + } 164 + } 165 + 166 + static int qcom_ddr_stats_show(struct seq_file *s, void *d) 167 + { 168 + struct ddr_stats_entry data[DDR_STATS_MAX_NUM_MODES]; 169 + void __iomem *reg = (void __iomem *)s->private; 170 + u32 entry_count; 171 + int i; 172 + 173 + entry_count = readl_relaxed(reg + DDR_STATS_NUM_MODES_ADDR); 174 + if (entry_count > DDR_STATS_MAX_NUM_MODES) 175 + return -EINVAL; 176 + 177 + reg += DDR_STATS_ENTRY_START_ADDR; 178 + memcpy_fromio(data, reg, sizeof(struct ddr_stats_entry) * entry_count); 179 + 180 + for (i = 0; i < entry_count; i++) 181 + qcom_ddr_stats_print(s, &data[i]); 182 + 183 + return 0; 184 + } 185 + 145 186 DEFINE_SHOW_ATTRIBUTE(qcom_soc_sleep_stats); 146 187 DEFINE_SHOW_ATTRIBUTE(qcom_subsystem_sleep_stats); 188 + DEFINE_SHOW_ATTRIBUTE(qcom_ddr_stats); 189 + 190 + static void qcom_create_ddr_stat_files(struct dentry *root, void __iomem *reg, 191 + const struct stats_config *config) 192 + { 193 + u32 key; 194 + 195 + if (!config->ddr_stats_offset) 196 + return; 197 + 198 + key = readl_relaxed(reg + config->ddr_stats_offset + DDR_STATS_MAGIC_KEY_ADDR); 199 + if (key == DDR_STATS_MAGIC_KEY) 200 + debugfs_create_file("ddr_stats", 0400, root, 201 + (__force void *)reg + config->ddr_stats_offset, 202 + &qcom_ddr_stats_fops); 203 + } 147 204 148 205 static void qcom_create_soc_sleep_stat_files(struct dentry *root, void __iomem *reg, 149 206 struct stats_data *d, ··· 309 212 310 213 qcom_create_subsystem_stat_files(root, config); 311 214 qcom_create_soc_sleep_stat_files(root, reg, d, config); 215 + qcom_create_ddr_stat_files(root, reg, config); 312 216 313 217 platform_set_drvdata(pdev, root); 314 218 ··· 352 254 353 255 static const struct stats_config rpmh_data = { 354 256 .stats_offset = 0x48, 257 + .ddr_stats_offset = 0xb8, 355 258 .num_records = 3, 356 259 .appended_stats_avail = false, 357 260 .dynamic_offset = false,