11// SPDX-License-Identifier: GPL-2.0-only
22/*
33 * Copyright (c) 2011-2021, The Linux Foundation. All rights reserved.
4+ * Copyright (c) 2022-2025, Qualcomm Innovation Center, Inc. All rights reserved.
45 */
56
7+ #include <linux/bitfield.h>
68#include <linux/debugfs.h>
79#include <linux/device.h>
810#include <linux/io.h>
2426#define ACCUMULATED_OFFSET 0x18
2527#define CLIENT_VOTES_OFFSET 0x20
2628
29+ #define DDR_STATS_MAGIC_KEY 0xA1157A75
30+ #define DDR_STATS_MAX_NUM_MODES 20
31+ #define DDR_STATS_MAGIC_KEY_ADDR 0x0
32+ #define DDR_STATS_NUM_MODES_ADDR 0x4
33+ #define DDR_STATS_ENTRY_START_ADDR 0x8
34+
35+ #define DDR_STATS_CP_IDX (data ) FIELD_GET(GENMASK(4, 0), data)
36+ #define DDR_STATS_LPM_NAME (data ) FIELD_GET(GENMASK(7, 0), data)
37+ #define DDR_STATS_TYPE (data ) FIELD_GET(GENMASK(15, 8), data)
38+ #define DDR_STATS_FREQ (data ) FIELD_GET(GENMASK(31, 16), data)
39+
2740struct subsystem_data {
2841 const char * name ;
2942 u32 smem_item ;
@@ -48,12 +61,19 @@ static const struct subsystem_data subsystems[] = {
4861
4962struct stats_config {
5063 size_t stats_offset ;
64+ size_t ddr_stats_offset ;
5165 size_t num_records ;
5266 bool appended_stats_avail ;
5367 bool dynamic_offset ;
5468 bool subsystem_stats_in_smem ;
5569};
5670
71+ struct ddr_stats_entry {
72+ u32 name ;
73+ u32 count ;
74+ u64 duration ;
75+ };
76+
5777struct stats_data {
5878 bool appended_stats_avail ;
5979 void __iomem * base ;
@@ -122,8 +142,85 @@ static int qcom_soc_sleep_stats_show(struct seq_file *s, void *unused)
122142 return 0 ;
123143}
124144
145+ static void qcom_ddr_stats_print (struct seq_file * s , struct ddr_stats_entry * data )
146+ {
147+ u32 cp_idx ;
148+
149+ /*
150+ * DDR statistic have two different types of details encoded.
151+ * (1) DDR LPM Stats
152+ * (2) DDR Frequency Stats
153+ *
154+ * The name field have details like which type of DDR stat (bits 8:15)
155+ * along with other details as explained below
156+ *
157+ * In case of DDR LPM stat, name field will be encoded as,
158+ * Bits - Meaning
159+ * 0:7 - DDR LPM name, can be of 0xd4, 0xd3, 0x11 and 0xd0.
160+ * 8:15 - 0x0 (indicates its a LPM stat)
161+ * 16:31 - Unused
162+ *
163+ * In case of DDR FREQ stats, name field will be encoded as,
164+ * Bits - Meaning
165+ * 0:4 - DDR Clock plan index (CP IDX)
166+ * 5:7 - Unused
167+ * 8:15 - 0x1 (indicates its Freq stat)
168+ * 16:31 - Frequency value in Mhz
169+ */
170+ switch (DDR_STATS_TYPE (data -> name )) {
171+ case 0 :
172+ seq_printf (s , "DDR LPM Stat Name:0x%lx\tcount:%u\tDuration (ticks):%llu\n" ,
173+ DDR_STATS_LPM_NAME (data -> name ), data -> count , data -> duration );
174+ break ;
175+ case 1 :
176+ if (!data -> count || !DDR_STATS_FREQ (data -> name ))
177+ return ;
178+
179+ cp_idx = DDR_STATS_CP_IDX (data -> name );
180+ seq_printf (s , "DDR Freq %luMhz:\tCP IDX:%u\tcount:%u\tDuration (ticks):%llu\n" ,
181+ DDR_STATS_FREQ (data -> name ), cp_idx , data -> count , data -> duration );
182+ break ;
183+ }
184+ }
185+
186+ static int qcom_ddr_stats_show (struct seq_file * s , void * d )
187+ {
188+ struct ddr_stats_entry data [DDR_STATS_MAX_NUM_MODES ];
189+ void __iomem * reg = (void __iomem * )s -> private ;
190+ u32 entry_count ;
191+ int i ;
192+
193+ entry_count = readl_relaxed (reg + DDR_STATS_NUM_MODES_ADDR );
194+ if (entry_count > DDR_STATS_MAX_NUM_MODES )
195+ return - EINVAL ;
196+
197+ reg += DDR_STATS_ENTRY_START_ADDR ;
198+ memcpy_fromio (data , reg , sizeof (struct ddr_stats_entry ) * entry_count );
199+
200+ for (i = 0 ; i < entry_count ; i ++ )
201+ qcom_ddr_stats_print (s , & data [i ]);
202+
203+ return 0 ;
204+ }
205+
125206DEFINE_SHOW_ATTRIBUTE (qcom_soc_sleep_stats );
126207DEFINE_SHOW_ATTRIBUTE (qcom_subsystem_sleep_stats );
208+ DEFINE_SHOW_ATTRIBUTE (qcom_ddr_stats );
209+
210+ static void qcom_create_ddr_stat_files (struct dentry * root , void __iomem * reg ,
211+ const struct stats_config * config )
212+ {
213+ u32 key ;
214+
215+ if (!config -> ddr_stats_offset )
216+ return ;
217+
218+ key = readl_relaxed (reg + config -> ddr_stats_offset + DDR_STATS_MAGIC_KEY_ADDR );
219+ if (key == DDR_STATS_MAGIC_KEY )
220+ debugfs_create_file ("ddr_stats" , 0400 , root ,
221+ (__force void * )reg + config -> ddr_stats_offset ,
222+ & qcom_ddr_stats_fops );
223+ }
127224
128225static void qcom_create_soc_sleep_stat_files (struct dentry * root , void __iomem * reg ,
129226 struct stats_data * d ,
@@ -212,6 +309,7 @@ static int qcom_stats_probe(struct platform_device *pdev)
212309
213310 qcom_create_subsystem_stat_files (root , config );
214311 qcom_create_soc_sleep_stat_files (root , reg , d , config );
312+ qcom_create_ddr_stat_files (root , reg , config );
215313
216314 platform_set_drvdata (pdev , root );
217315
@@ -254,6 +352,7 @@ static const struct stats_config rpmh_data_sdm845 = {
254352
255353static const struct stats_config rpmh_data = {
256354 .stats_offset = 0x48 ,
355+ .ddr_stats_offset = 0xb8 ,
257356 .num_records = 3 ,
258357 .appended_stats_avail = false,
259358 .dynamic_offset = false,
0 commit comments