From a402311df4e32c25b58f2452b85819027d25c0d7 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Thu, 19 May 2022 11:05:33 +0530 Subject: [PATCH] soc: qcom: Add support to read ddr_stats This change adds changes to read ddr statistic which can be read with below command. cat sys/kernel/debug/qcom_stats/ddr_stats the output for this will be printed as, LPM 1: Name:0xd4 count:0 Duration (ticks):0 (~0%) LPM 2: Name:0xd3 count:0 Duration (ticks):0 (~0%) LPM 3: Name:0x11 count:0 Duration (ticks):0 (~0%) LPM 4: Name:0xd0 count:0 Duration (ticks):0 (~0%) Freq 1555Mhz: CP IDX:8 count:2 Duration (ticks):9959909 (~3%) Freq 1708Mhz: CP IDX:9 count:3 Duration (ticks):248315666 (~96%). Change-Id: I3c3eb3dc651d04c0fff2ca24078ea22787e505b7 Signed-off-by: Tushar Nimkar Signed-off-by: Maulik Shah --- drivers/soc/qcom/qcom_stats.c | 100 ++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c index d6bfd1bbdc2a..78661b62a215 100644 --- a/drivers/soc/qcom/qcom_stats.c +++ b/drivers/soc/qcom/qcom_stats.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2011-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -24,6 +25,15 @@ #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_NAME_ADDR 0x0 +#define DDR_STATS_COUNT_ADDR 0x4 +#define DDR_STATS_DURATION_ADDR 0x8 + struct subsystem_data { const char *name; u32 smem_item; @@ -44,12 +54,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 { + uint32_t name; + uint32_t count; + uint64_t duration; +}; + struct stats_data { bool appended_stats_avail; void __iomem *base; @@ -118,8 +135,89 @@ static int qcom_soc_sleep_stats_show(struct seq_file *s, void *unused) return 0; } +static void print_ddr_stats(struct seq_file *s, int *count, + struct ddr_stats_entry *data, u64 accumulated_duration) +{ + + u32 cp_idx = 0; + u32 name, duration; + + if (accumulated_duration) + duration = (data->duration * 100) / accumulated_duration; + + name = (data->name >> 8) & 0xFF; + if (name == 0x0) { + name = (data->name) & 0xFF; + *count = *count + 1; + seq_printf(s, + "LPM %d:\tName:0x%x\tcount:%u\tDuration (ticks):%ld (~%d%%)\n", + *count, name, data->count, data->duration, duration); + } else if (name == 0x1) { + cp_idx = data->name & 0x1F; + name = data->name >> 16; + + if (!name || !data->count) + return; + + seq_printf(s, + "Freq %dMhz:\tCP IDX:%u\tcount:%u\tDuration (ticks):%ld (~%d%%)\n", + name, cp_idx, data->count, data->duration, duration); + } +} + +static int ddr_stats_show(struct seq_file *s, void *d) +{ + struct ddr_stats_entry data[DDR_STATS_MAX_NUM_MODES]; + void __iomem *reg = s->private; + u32 entry_count; + u64 accumulated_duration = 0; + int i, lpm_count = 0; + + entry_count = readl_relaxed(reg + DDR_STATS_NUM_MODES_ADDR); + if (entry_count > DDR_STATS_MAX_NUM_MODES) { + pr_err("Invalid entry count\n"); + return 0; + } + + reg += DDR_STATS_NUM_MODES_ADDR + 0x4; + + for (i = 0; i < entry_count; i++) { + data[i].count = readl_relaxed(reg + DDR_STATS_COUNT_ADDR); + + data[i].name = readl_relaxed(reg + DDR_STATS_NAME_ADDR); + + data[i].duration = readq_relaxed(reg + DDR_STATS_DURATION_ADDR); + + accumulated_duration += data[i].duration; + reg += sizeof(struct ddr_stats_entry); + } + + for (i = 0; i < entry_count; i++) + print_ddr_stats(s, &lpm_count, &data[i], accumulated_duration); + + return 0; +} + DEFINE_SHOW_ATTRIBUTE(qcom_soc_sleep_stats); DEFINE_SHOW_ATTRIBUTE(qcom_subsystem_sleep_stats); +DEFINE_SHOW_ATTRIBUTE(ddr_stats); + +static void qcom_create_ddr_stat_files(struct dentry *root, void __iomem *reg, + struct stats_data *d, + const struct stats_config *config) +{ + size_t stats_offset; + u32 key; + + if (!config->ddr_stats_offset) + return; + + stats_offset = config->ddr_stats_offset; + + key = readl_relaxed(reg + stats_offset + DDR_STATS_MAGIC_KEY_ADDR); + if (key == DDR_STATS_MAGIC_KEY) + debugfs_create_file("ddr_stats", 0400, root, reg + stats_offset, &ddr_stats_fops); +} static void qcom_create_soc_sleep_stat_files(struct dentry *root, void __iomem *reg, struct stats_data *d, @@ -214,6 +312,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, d, config); platform_set_drvdata(pdev, root); @@ -248,6 +347,7 @@ static const struct stats_config rpm_data_dba0 = { static const struct stats_config rpmh_data = { .stats_offset = 0x48, + .ddr_stats_offset = 0xb8, .num_records = 3, .appended_stats_avail = false, .dynamic_offset = false,