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.

at v6.16-rc6 297 lines 7.6 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (c) 2011-2021, The Linux Foundation. All rights reserved. 4 */ 5 6#include <linux/debugfs.h> 7#include <linux/device.h> 8#include <linux/io.h> 9#include <linux/module.h> 10#include <linux/of.h> 11#include <linux/platform_device.h> 12#include <linux/seq_file.h> 13 14#include <linux/soc/qcom/smem.h> 15#include <clocksource/arm_arch_timer.h> 16 17#define RPM_DYNAMIC_ADDR 0x14 18#define RPM_DYNAMIC_ADDR_MASK 0xFFFF 19 20#define STAT_TYPE_OFFSET 0x0 21#define COUNT_OFFSET 0x4 22#define LAST_ENTERED_AT_OFFSET 0x8 23#define LAST_EXITED_AT_OFFSET 0x10 24#define ACCUMULATED_OFFSET 0x18 25#define CLIENT_VOTES_OFFSET 0x20 26 27struct subsystem_data { 28 const char *name; 29 u32 smem_item; 30 u32 pid; 31}; 32 33static const struct subsystem_data subsystems[] = { 34 { "modem", 605, 1 }, 35 { "wpss", 605, 13 }, 36 { "adsp", 606, 2 }, 37 { "cdsp", 607, 5 }, 38 { "cdsp1", 607, 12 }, 39 { "gpdsp0", 607, 17 }, 40 { "gpdsp1", 607, 18 }, 41 { "slpi", 608, 3 }, 42 { "gpu", 609, 0 }, 43 { "display", 610, 0 }, 44 { "adsp_island", 613, 2 }, 45 { "slpi_island", 613, 3 }, 46 { "apss", 631, QCOM_SMEM_HOST_ANY }, 47}; 48 49struct stats_config { 50 size_t stats_offset; 51 size_t num_records; 52 bool appended_stats_avail; 53 bool dynamic_offset; 54 bool subsystem_stats_in_smem; 55}; 56 57struct stats_data { 58 bool appended_stats_avail; 59 void __iomem *base; 60}; 61 62struct sleep_stats { 63 u32 stat_type; 64 u32 count; 65 u64 last_entered_at; 66 u64 last_exited_at; 67 u64 accumulated; 68}; 69 70struct appended_stats { 71 u32 client_votes; 72 u32 reserved[3]; 73}; 74 75static void qcom_print_stats(struct seq_file *s, const struct sleep_stats *stat) 76{ 77 u64 accumulated = stat->accumulated; 78 /* 79 * If a subsystem is in sleep when reading the sleep stats adjust 80 * the accumulated sleep duration to show actual sleep time. 81 */ 82 if (stat->last_entered_at > stat->last_exited_at) 83 accumulated += arch_timer_read_counter() - stat->last_entered_at; 84 85 seq_printf(s, "Count: %u\n", stat->count); 86 seq_printf(s, "Last Entered At: %llu\n", stat->last_entered_at); 87 seq_printf(s, "Last Exited At: %llu\n", stat->last_exited_at); 88 seq_printf(s, "Accumulated Duration: %llu\n", accumulated); 89} 90 91static int qcom_subsystem_sleep_stats_show(struct seq_file *s, void *unused) 92{ 93 struct subsystem_data *subsystem = s->private; 94 struct sleep_stats *stat; 95 96 /* Items are allocated lazily, so lookup pointer each time */ 97 stat = qcom_smem_get(subsystem->pid, subsystem->smem_item, NULL); 98 if (IS_ERR(stat)) 99 return 0; 100 101 qcom_print_stats(s, stat); 102 103 return 0; 104} 105 106static int qcom_soc_sleep_stats_show(struct seq_file *s, void *unused) 107{ 108 struct stats_data *d = s->private; 109 void __iomem *reg = d->base; 110 struct sleep_stats stat; 111 112 memcpy_fromio(&stat, reg, sizeof(stat)); 113 qcom_print_stats(s, &stat); 114 115 if (d->appended_stats_avail) { 116 struct appended_stats votes; 117 118 memcpy_fromio(&votes, reg + CLIENT_VOTES_OFFSET, sizeof(votes)); 119 seq_printf(s, "Client Votes: %#x\n", votes.client_votes); 120 } 121 122 return 0; 123} 124 125DEFINE_SHOW_ATTRIBUTE(qcom_soc_sleep_stats); 126DEFINE_SHOW_ATTRIBUTE(qcom_subsystem_sleep_stats); 127 128static void qcom_create_soc_sleep_stat_files(struct dentry *root, void __iomem *reg, 129 struct stats_data *d, 130 const struct stats_config *config) 131{ 132 char stat_type[sizeof(u32) + 1] = {0}; 133 size_t stats_offset = config->stats_offset; 134 u32 offset = 0, type; 135 int i, j; 136 137 /* 138 * On RPM targets, stats offset location is dynamic and changes from target 139 * to target and sometimes from build to build for same target. 140 * 141 * In such cases the dynamic address is present at 0x14 offset from base 142 * address in devicetree. The last 16bits indicates the stats_offset. 143 */ 144 if (config->dynamic_offset) { 145 stats_offset = readl(reg + RPM_DYNAMIC_ADDR); 146 stats_offset &= RPM_DYNAMIC_ADDR_MASK; 147 } 148 149 for (i = 0; i < config->num_records; i++) { 150 d[i].base = reg + offset + stats_offset; 151 152 /* 153 * Read the low power mode name and create debugfs file for it. 154 * The names read could be of below, 155 * (may change depending on low power mode supported). 156 * For rpmh-sleep-stats: "aosd", "cxsd" and "ddr". 157 * For rpm-sleep-stats: "vmin" and "vlow". 158 */ 159 type = readl(d[i].base); 160 for (j = 0; j < sizeof(u32); j++) { 161 stat_type[j] = type & 0xff; 162 type = type >> 8; 163 } 164 strim(stat_type); 165 debugfs_create_file(stat_type, 0400, root, &d[i], 166 &qcom_soc_sleep_stats_fops); 167 168 offset += sizeof(struct sleep_stats); 169 if (d[i].appended_stats_avail) 170 offset += sizeof(struct appended_stats); 171 } 172} 173 174static void qcom_create_subsystem_stat_files(struct dentry *root, 175 const struct stats_config *config) 176{ 177 int i; 178 179 if (!config->subsystem_stats_in_smem) 180 return; 181 182 for (i = 0; i < ARRAY_SIZE(subsystems); i++) 183 debugfs_create_file(subsystems[i].name, 0400, root, (void *)&subsystems[i], 184 &qcom_subsystem_sleep_stats_fops); 185} 186 187static int qcom_stats_probe(struct platform_device *pdev) 188{ 189 void __iomem *reg; 190 struct dentry *root; 191 const struct stats_config *config; 192 struct stats_data *d; 193 int i; 194 195 config = device_get_match_data(&pdev->dev); 196 if (!config) 197 return -ENODEV; 198 199 reg = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); 200 if (IS_ERR(reg)) 201 return -ENOMEM; 202 203 d = devm_kcalloc(&pdev->dev, config->num_records, 204 sizeof(*d), GFP_KERNEL); 205 if (!d) 206 return -ENOMEM; 207 208 for (i = 0; i < config->num_records; i++) 209 d[i].appended_stats_avail = config->appended_stats_avail; 210 211 root = debugfs_create_dir("qcom_stats", NULL); 212 213 qcom_create_subsystem_stat_files(root, config); 214 qcom_create_soc_sleep_stat_files(root, reg, d, config); 215 216 platform_set_drvdata(pdev, root); 217 218 device_set_pm_not_required(&pdev->dev); 219 220 return 0; 221} 222 223static void qcom_stats_remove(struct platform_device *pdev) 224{ 225 struct dentry *root = platform_get_drvdata(pdev); 226 227 debugfs_remove_recursive(root); 228} 229 230static const struct stats_config rpm_data = { 231 .stats_offset = 0, 232 .num_records = 2, 233 .appended_stats_avail = true, 234 .dynamic_offset = true, 235 .subsystem_stats_in_smem = false, 236}; 237 238/* Older RPM firmwares have the stats at a fixed offset instead */ 239static const struct stats_config rpm_data_dba0 = { 240 .stats_offset = 0xdba0, 241 .num_records = 2, 242 .appended_stats_avail = true, 243 .dynamic_offset = false, 244 .subsystem_stats_in_smem = false, 245}; 246 247static const struct stats_config rpmh_data_sdm845 = { 248 .stats_offset = 0x48, 249 .num_records = 2, 250 .appended_stats_avail = false, 251 .dynamic_offset = false, 252 .subsystem_stats_in_smem = true, 253}; 254 255static const struct stats_config rpmh_data = { 256 .stats_offset = 0x48, 257 .num_records = 3, 258 .appended_stats_avail = false, 259 .dynamic_offset = false, 260 .subsystem_stats_in_smem = true, 261}; 262 263static const struct of_device_id qcom_stats_table[] = { 264 { .compatible = "qcom,apq8084-rpm-stats", .data = &rpm_data_dba0 }, 265 { .compatible = "qcom,msm8226-rpm-stats", .data = &rpm_data_dba0 }, 266 { .compatible = "qcom,msm8916-rpm-stats", .data = &rpm_data_dba0 }, 267 { .compatible = "qcom,msm8974-rpm-stats", .data = &rpm_data_dba0 }, 268 { .compatible = "qcom,rpm-stats", .data = &rpm_data }, 269 { .compatible = "qcom,rpmh-stats", .data = &rpmh_data }, 270 { .compatible = "qcom,sdm845-rpmh-stats", .data = &rpmh_data_sdm845 }, 271 { } 272}; 273MODULE_DEVICE_TABLE(of, qcom_stats_table); 274 275static struct platform_driver qcom_stats = { 276 .probe = qcom_stats_probe, 277 .remove = qcom_stats_remove, 278 .driver = { 279 .name = "qcom_stats", 280 .of_match_table = qcom_stats_table, 281 }, 282}; 283 284static int __init qcom_stats_init(void) 285{ 286 return platform_driver_register(&qcom_stats); 287} 288late_initcall(qcom_stats_init); 289 290static void __exit qcom_stats_exit(void) 291{ 292 platform_driver_unregister(&qcom_stats); 293} 294module_exit(qcom_stats_exit) 295 296MODULE_DESCRIPTION("Qualcomm Technologies, Inc. (QTI) Stats driver"); 297MODULE_LICENSE("GPL v2");