123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
- */
- #include <linux/debugfs.h>
- #include <linux/delay.h>
- #include <linux/errno.h>
- #include <linux/init.h>
- #include <linux/io.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/sched.h>
- #include <linux/slab.h>
- #include <linux/types.h>
- #include <linux/mm.h>
- #include <linux/of.h>
- #include <linux/uaccess.h>
- #define RPM_MASTERS_BUF_LEN 400
- #define SNPRINTF(buf, size, format, ...) \
- do { \
- if (size > 0) { \
- int ret; \
- ret = scnprintf(buf, size, format, ## __VA_ARGS__); \
- buf += ret; \
- size -= ret; \
- } \
- } while (0)
- #define GET_MASTER_NAME(a, prvdata) \
- ((a >= prvdata->num_masters) ? "Invalid Master Name" : \
- prvdata->master_names[a])
- #define GET_FIELD(a) ((strnstr(#a, ".", 80) + 1))
- #ifdef CONFIG_ARM
- #undef readq_relaxed
- #define readq_relaxed(a) ({ \
- u64 val = readl_relaxed((a) + 4); \
- val <<= 32; \
- val |= readl_relaxed((a)); \
- val; \
- })
- #endif
- struct msm_rpm_master_stats_platform_data {
- phys_addr_t phys_addr_base;
- u32 phys_size;
- char **masters;
- /*
- * RPM maintains PC stats for each master in MSG RAM,
- * it allocates 256 bytes for this use.
- * No of masters differs for different targets.
- * Based on the number of masters, linux rpm stat
- * driver reads (32 * num_masters) bytes to display
- * master stats.
- */
- s32 num_masters;
- u32 master_offset;
- u32 version;
- };
- static DEFINE_MUTEX(msm_rpm_master_stats_mutex);
- struct msm_rpm_master_stats {
- uint32_t active_cores;
- uint32_t numshutdowns;
- uint64_t shutdown_req;
- uint64_t wakeup_ind;
- uint64_t bringup_req;
- uint64_t bringup_ack;
- uint32_t wakeup_reason; /* 0 = rude wakeup, 1 = scheduled wakeup */
- uint32_t last_sleep_transition_duration;
- uint32_t last_wake_transition_duration;
- uint32_t xo_count;
- uint64_t xo_last_entered_at;
- uint64_t xo_last_exited_at;
- uint64_t xo_accumulated_duration;
- };
- struct msm_rpm_master_stats_private_data {
- void __iomem *reg_base;
- u32 len;
- char **master_names;
- u32 num_masters;
- char buf[RPM_MASTERS_BUF_LEN];
- struct msm_rpm_master_stats_platform_data *platform_data;
- };
- static int msm_rpm_master_stats_file_close(struct inode *inode,
- struct file *file)
- {
- struct msm_rpm_master_stats_private_data *private = file->private_data;
- mutex_lock(&msm_rpm_master_stats_mutex);
- if (private->reg_base)
- iounmap(private->reg_base);
- kfree(file->private_data);
- mutex_unlock(&msm_rpm_master_stats_mutex);
- return 0;
- }
- static int msm_rpm_master_copy_stats(
- struct msm_rpm_master_stats_private_data *prvdata)
- {
- struct msm_rpm_master_stats record;
- struct msm_rpm_master_stats_platform_data *pdata;
- static int master_cnt;
- int count, j = 0;
- char *buf;
- unsigned long active_cores;
- /* Iterate possible number of masters */
- if (master_cnt > prvdata->num_masters - 1) {
- master_cnt = 0;
- return 0;
- }
- pdata = prvdata->platform_data;
- count = RPM_MASTERS_BUF_LEN;
- buf = prvdata->buf;
- if (prvdata->platform_data->version == 2) {
- SNPRINTF(buf, count, "%s\n",
- GET_MASTER_NAME(master_cnt, prvdata));
- record.shutdown_req = readq_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset +
- offsetof(struct msm_rpm_master_stats, shutdown_req)));
- SNPRINTF(buf, count, "\t%s:0x%llX\n",
- GET_FIELD(record.shutdown_req),
- record.shutdown_req);
- record.wakeup_ind = readq_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset +
- offsetof(struct msm_rpm_master_stats, wakeup_ind)));
- SNPRINTF(buf, count, "\t%s:0x%llX\n",
- GET_FIELD(record.wakeup_ind),
- record.wakeup_ind);
- record.bringup_req = readq_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset +
- offsetof(struct msm_rpm_master_stats, bringup_req)));
- SNPRINTF(buf, count, "\t%s:0x%llX\n",
- GET_FIELD(record.bringup_req),
- record.bringup_req);
- record.bringup_ack = readq_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset +
- offsetof(struct msm_rpm_master_stats, bringup_ack)));
- SNPRINTF(buf, count, "\t%s:0x%llX\n",
- GET_FIELD(record.bringup_ack),
- record.bringup_ack);
- record.xo_last_entered_at = readq_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset +
- offsetof(struct msm_rpm_master_stats,
- xo_last_entered_at)));
- SNPRINTF(buf, count, "\t%s:0x%llX\n",
- GET_FIELD(record.xo_last_entered_at),
- record.xo_last_entered_at);
- record.xo_last_exited_at = readq_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset +
- offsetof(struct msm_rpm_master_stats,
- xo_last_exited_at)));
- SNPRINTF(buf, count, "\t%s:0x%llX\n",
- GET_FIELD(record.xo_last_exited_at),
- record.xo_last_exited_at);
- record.xo_accumulated_duration =
- readq_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset +
- offsetof(struct msm_rpm_master_stats,
- xo_accumulated_duration)));
- SNPRINTF(buf, count, "\t%s:0x%llX\n",
- GET_FIELD(record.xo_accumulated_duration),
- record.xo_accumulated_duration);
- record.last_sleep_transition_duration =
- readl_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset +
- offsetof(struct msm_rpm_master_stats,
- last_sleep_transition_duration)));
- SNPRINTF(buf, count, "\t%s:0x%x\n",
- GET_FIELD(record.last_sleep_transition_duration),
- record.last_sleep_transition_duration);
- record.last_wake_transition_duration =
- readl_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset +
- offsetof(struct msm_rpm_master_stats,
- last_wake_transition_duration)));
- SNPRINTF(buf, count, "\t%s:0x%x\n",
- GET_FIELD(record.last_wake_transition_duration),
- record.last_wake_transition_duration);
- record.xo_count =
- readl_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset +
- offsetof(struct msm_rpm_master_stats,
- xo_count)));
- SNPRINTF(buf, count, "\t%s:0x%x\n",
- GET_FIELD(record.xo_count),
- record.xo_count);
- record.wakeup_reason = readl_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset +
- offsetof(struct msm_rpm_master_stats,
- wakeup_reason)));
- SNPRINTF(buf, count, "\t%s:0x%x\n",
- GET_FIELD(record.wakeup_reason),
- record.wakeup_reason);
- record.numshutdowns = readl_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset +
- offsetof(struct msm_rpm_master_stats, numshutdowns)));
- SNPRINTF(buf, count, "\t%s:0x%x\n",
- GET_FIELD(record.numshutdowns),
- record.numshutdowns);
- record.active_cores = readl_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset) +
- offsetof(struct msm_rpm_master_stats, active_cores));
- SNPRINTF(buf, count, "\t%s:0x%x\n",
- GET_FIELD(record.active_cores),
- record.active_cores);
- } else {
- SNPRINTF(buf, count, "%s\n",
- GET_MASTER_NAME(master_cnt, prvdata));
- record.numshutdowns = readl_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset) + 0x0);
- SNPRINTF(buf, count, "\t%s:0x%0x\n",
- GET_FIELD(record.numshutdowns),
- record.numshutdowns);
- record.active_cores = readl_relaxed(prvdata->reg_base +
- (master_cnt * pdata->master_offset) + 0x4);
- SNPRINTF(buf, count, "\t%s:0x%0x\n",
- GET_FIELD(record.active_cores),
- record.active_cores);
- }
- active_cores = record.active_cores;
- j = find_first_bit(&active_cores, BITS_PER_LONG);
- while (j < (BITS_PER_LONG - 1)) {
- SNPRINTF(buf, count, "\t\tcore%d\n", j);
- j = find_next_bit((const unsigned long *)&active_cores,
- BITS_PER_LONG, j + 1);
- }
- if (j == (BITS_PER_LONG - 1))
- SNPRINTF(buf, count, "\t\tcore%d\n", j);
- master_cnt++;
- return RPM_MASTERS_BUF_LEN - count;
- }
- static ssize_t msm_rpm_master_stats_file_read(struct file *file,
- char __user *bufu, size_t count, loff_t *ppos)
- {
- struct msm_rpm_master_stats_private_data *prvdata;
- struct msm_rpm_master_stats_platform_data *pdata;
- ssize_t ret = -EINVAL;
- mutex_lock(&msm_rpm_master_stats_mutex);
- prvdata = file->private_data;
- if (!prvdata)
- goto exit;
- pdata = prvdata->platform_data;
- if (!pdata)
- goto exit;
- if (!bufu || count == 0)
- goto exit;
- if (*ppos <= pdata->phys_size) {
- prvdata->len = msm_rpm_master_copy_stats(prvdata);
- *ppos = 0;
- }
- ret = simple_read_from_buffer(bufu, count, ppos,
- prvdata->buf, prvdata->len);
- exit:
- mutex_unlock(&msm_rpm_master_stats_mutex);
- return ret;
- }
- static int msm_rpm_master_stats_file_open(struct inode *inode,
- struct file *file)
- {
- struct msm_rpm_master_stats_private_data *prvdata;
- struct msm_rpm_master_stats_platform_data *pdata;
- int ret = 0;
- mutex_lock(&msm_rpm_master_stats_mutex);
- pdata = inode->i_private;
- file->private_data =
- kzalloc(sizeof(struct msm_rpm_master_stats_private_data),
- GFP_KERNEL);
- if (!file->private_data) {
- ret = -ENOMEM;
- goto exit;
- }
- prvdata = file->private_data;
- prvdata->reg_base = ioremap(pdata->phys_addr_base,
- pdata->phys_size);
- if (!prvdata->reg_base) {
- kfree(file->private_data);
- prvdata = NULL;
- pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n",
- __func__, &pdata->phys_addr_base,
- pdata->phys_size);
- ret = -ENOMEM;
- goto exit;
- }
- prvdata->len = 0;
- prvdata->num_masters = pdata->num_masters;
- prvdata->master_names = pdata->masters;
- prvdata->platform_data = pdata;
- exit:
- mutex_unlock(&msm_rpm_master_stats_mutex);
- return ret;
- }
- static const struct file_operations msm_rpm_master_stats_fops = {
- .owner = THIS_MODULE,
- .open = msm_rpm_master_stats_file_open,
- .read = msm_rpm_master_stats_file_read,
- .release = msm_rpm_master_stats_file_close,
- .llseek = no_llseek,
- };
- static struct msm_rpm_master_stats_platform_data
- *msm_rpm_master_populate_pdata(struct device *dev)
- {
- struct msm_rpm_master_stats_platform_data *pdata;
- struct device_node *node = dev->of_node;
- int rc = 0, i;
- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- goto err;
- rc = of_property_read_u32(node, "qcom,master-stats-version",
- &pdata->version);
- if (rc) {
- dev_err(dev, "master-stats-version missing rc=%d\n", rc);
- goto err;
- }
- rc = of_property_read_u32(node, "qcom,master-offset",
- &pdata->master_offset);
- if (rc) {
- dev_err(dev, "master-offset missing rc=%d\n", rc);
- goto err;
- }
- pdata->num_masters = of_property_count_strings(node, "qcom,masters");
- if (pdata->num_masters < 0) {
- dev_err(dev, "Failed to get number of masters =%d\n",
- pdata->num_masters);
- goto err;
- }
- pdata->masters = devm_kzalloc(dev, sizeof(char *) * pdata->num_masters,
- GFP_KERNEL);
- if (!pdata->masters)
- goto err;
- /*
- * Read master names from DT
- */
- for (i = 0; i < pdata->num_masters; i++) {
- const char *master_name;
- of_property_read_string_index(node, "qcom,masters",
- i, &master_name);
- pdata->masters[i] = devm_kstrdup(dev, master_name, GFP_KERNEL);
- if (!pdata->masters[i])
- goto err;
- }
- return pdata;
- err:
- return NULL;
- }
- static int msm_rpm_master_stats_probe(struct platform_device *pdev)
- {
- struct dentry *dent;
- struct msm_rpm_master_stats_platform_data *pdata;
- struct resource *res = NULL;
- pdata = msm_rpm_master_populate_pdata(&pdev->dev);
- if (!pdata)
- return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev,
- "%s: Failed to get IO resource from platform device\n",
- __func__);
- return -ENXIO;
- }
- pdata->phys_addr_base = res->start;
- pdata->phys_size = resource_size(res);
- dent = debugfs_create_file("rpm_master_stats", 0444, NULL,
- pdata, &msm_rpm_master_stats_fops);
- if (!dent) {
- dev_err(&pdev->dev, "%s: ERROR debugfs_create_file failed\n",
- __func__);
- return -ENOMEM;
- }
- platform_set_drvdata(pdev, dent);
- return 0;
- }
- static int msm_rpm_master_stats_remove(struct platform_device *pdev)
- {
- struct dentry *dent;
- dent = platform_get_drvdata(pdev);
- debugfs_remove(dent);
- platform_set_drvdata(pdev, NULL);
- return 0;
- }
- static const struct of_device_id rpm_master_table[] = {
- {.compatible = "qcom,rpm-master-stats"},
- {},
- };
- static struct platform_driver msm_rpm_master_stats_driver = {
- .probe = msm_rpm_master_stats_probe,
- .remove = msm_rpm_master_stats_remove,
- .driver = {
- .name = "msm_rpm_master_stats",
- .of_match_table = rpm_master_table,
- },
- };
- static int __init msm_rpm_master_stats_init(void)
- {
- return platform_driver_register(&msm_rpm_master_stats_driver);
- }
- static void __exit msm_rpm_master_stats_exit(void)
- {
- platform_driver_unregister(&msm_rpm_master_stats_driver);
- }
- module_init(msm_rpm_master_stats_init);
- module_exit(msm_rpm_master_stats_exit);
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("MSM RPM Master Statistics driver");
- MODULE_ALIAS("platform:msm_master_stat_log");
|