diff options
| author | Mahesh Sivasubramanian <msivasub@codeaurora.org> | 2016-04-25 16:49:31 -0600 |
|---|---|---|
| committer | Kyle Yan <kyan@codeaurora.org> | 2016-06-07 11:55:46 -0700 |
| commit | 1d321eb86c2ff3ad61fb56fe7d7653af581da993 (patch) | |
| tree | a29f01fd3ac9db0d6830c9d7a4137387423cace8 | |
| parent | 5b4b23763df6b15a5c1c90e8ce00ec425ace80b6 (diff) | |
drivers: soc: qcom: system_stats: Support for System stats
RPM stats and RPM master stats provide the similar information via
different interfaces. Combine them into a single driver to provide a
unified information.
Change-Id: If5a5ef6e080ab6d75139472c5204eb6d5e9c6614
Signed-off-by: Mahesh Sivasubramanian <msivasub@codeaurora.org>
| -rw-r--r-- | Documentation/devicetree/bindings/arm/msm/system_stats.txt | 93 | ||||
| -rw-r--r-- | drivers/soc/qcom/Makefile | 2 | ||||
| -rw-r--r-- | drivers/soc/qcom/system_stats.c | 425 |
3 files changed, 519 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/system_stats.txt b/Documentation/devicetree/bindings/arm/msm/system_stats.txt new file mode 100644 index 000000000000..39961ca534a5 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/system_stats.txt @@ -0,0 +1,93 @@ +* System sleep stats + +Resource Power manager maintains information about system sleep and the time +each master spent in respective sleep modes. These were previously exported +through two modules, one for system and another for the respective master +votes. The two modules are now combined into a system stats drivers to provide +better visibility into system sleep modes. + +Main node properties + +- compatible + Usage: Required + Value type: <string> + Definition: must be "qcom,system-stats". + +- qcom,rpm-msg-ram + Usage: Required + Value type: <phandle> + Definition: phandle to RPM's message ram registers. + +- qcom,rpm-code-ram + Usage: Required + Value type: <phandle> + Definition: phandle to RPM's code ram registers. + +- qcom,masters: + Usage: Required + Value type: <string list> + +Definition of memory DT phandles that system stats module is dependent on. + +qcom,rpm-msg-ram: + +The required phandle pointed to by qcom,rpm-msg-ram are: +Node properties: +- compatible: + Usage: Required + Value Type: <string> + Definition: must be "qcom,rpm-msg-ram" + +- reg: + Usage: Required + Value Type: <prop-encoded-array> + Definition: Addresses and sizes for RPM address as visible to Apps and + Stats address location. + +- reg-names: + Usage: Required + Value Type: <stringlist> + Definition: Address names. Must be "phys_addr_base" or "msg-ram-base". + Must be specified in the same order as the + corresponding addresses in the reg property. + +qcom,rpm-code-ram: +The required phandle pointed to by qcom,rpm-msg-ram are: + +Node properties: +- compatible: + Usage: Required + Value Type: <string> + Definition: must be "qcom,rpm-code-ram". +- reg: + Usage: Required + Value Type: <prop-encoded-array> + Definition: Address and size for RPM code address. + +- reg-names: + Usage: Required + Value Type: <stringlist> + Definition: Address names. Must be "msg-ram-base" + +Example: + + rpm_code_ram: rpm-memory@0x68000 { + compatible = "qcom,rpm-code-ram"; + reg = <0x68000 0x5000>; + reg-name = "msg-ram-base"; + }; + + rpm_msg_ram: memory@0x200000 { + compatible = "qcom,rpm-msg-ram"; + reg = <0x200000 0x1000>, + <0x290000 0x1000>; + reg-names = "phys_addr_base", + "code-ram-base"; + }; + + qcom,system-stats@68140 { + compatible = "qcom,system-stats"; + qcom,rpm-msg-ram = <&rpm_msg_ram>; + qcom,rpm-code-ram = <&rpm_code_ram>; + qcom,masters = "APSS", "MPSS", "ADSP", "SLPI"; + }; diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 027e52439301..70d0a2dca5bf 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -90,5 +90,5 @@ obj-$(CONFIG_MSM_RTB) += msm_rtb-hotplug.o obj-$(CONFIG_QCOM_REMOTEQDSS) += remoteqdss.o obj-$(CONFIG_MSM_SERVICE_LOCATOR) += service-locator.o obj-$(CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG) += rpm_rbcpr_stats_v2.o -obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o rpm_master_stat.o +obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o rpm_master_stat.o system_stats.o obj-$(CONFIG_MSM_RPM_LOG) += rpm_log.o diff --git a/drivers/soc/qcom/system_stats.c b/drivers/soc/qcom/system_stats.c new file mode 100644 index 000000000000..476d2f6dca27 --- /dev/null +++ b/drivers/soc/qcom/system_stats.c @@ -0,0 +1,425 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#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/types.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/uaccess.h> +#include <asm/arch_timer.h> + +#define SCLK_HZ 32768 +#define MSM_ARCH_TIMER_FREQ 19200000 +#define NUM_STATS_RECORD 2 +#define STATS_OFFSET 0x14 +#define HEAP_OFFSET 0x1c +#define MASTER_START_ADDR_OFFSET 0x150 +#define MASTER_OFFSET_ADDRESS 0x1000 + +struct rpm_system_stats { + void __iomem *rpm_stats_addr; + void __iomem *rpm_master_addr; + int num_masters; + char **master; +} ss; + +struct msm_rpmstats_record { + char name[32]; + uint32_t id; + uint32_t val; +}; + +struct msm_rpm_stats_data { + uint32_t stat_type; + uint32_t count; + uint64_t last_entered_at; + uint64_t last_exited_at; + uint64_t accumulated; + uint32_t client_votes; + uint32_t reserved[3]; +}; + +struct rpm_master_stats_data { + 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; +}; + +static inline uint32_t msm_rpmstats_read_long_register(void __iomem *regbase, + int index, int offset) +{ + return readl_relaxed(regbase + offset + + index * sizeof(struct msm_rpm_stats_data)); +} + +static inline uint64_t msm_rpmstats_read_quad_register(void __iomem *regbase, + int index, int offset) +{ + uint64_t dst; + + memcpy_fromio(&dst, + regbase + offset + index * sizeof(struct msm_rpm_stats_data), + 8); + return dst; +} + +static inline unsigned long msm_rpmstats_read_register(void __iomem *regbase, + int index, int offset) +{ + return readl_relaxed(regbase + index * 12 + (offset + 1) * 4); +} + +static inline uint64_t get_time_in_msec(u64 counter) +{ + do_div(counter, MSM_ARCH_TIMER_FREQ); + counter *= MSEC_PER_SEC; + return counter; +} + +static inline uint64_t get_time_in_sec(u64 counter) +{ + do_div(counter, MSM_ARCH_TIMER_FREQ); + return counter; +} + +static void rpm_stats_copy_data(struct msm_rpm_stats_data *data, int idx) +{ + void __iomem *reg = ss.rpm_stats_addr; + + data->stat_type = msm_rpmstats_read_long_register(reg, idx, + offsetof(struct msm_rpm_stats_data, stat_type)); + + data->count = msm_rpmstats_read_long_register(reg, idx, + offsetof(struct msm_rpm_stats_data, count)); + data->last_entered_at = msm_rpmstats_read_quad_register(reg, + idx, offsetof(struct msm_rpm_stats_data, + last_entered_at)); + data->last_exited_at = msm_rpmstats_read_quad_register(reg, + idx, offsetof(struct msm_rpm_stats_data, + last_exited_at)); + + data->accumulated = msm_rpmstats_read_quad_register(reg, + idx, offsetof(struct msm_rpm_stats_data, + accumulated)); + data->client_votes = msm_rpmstats_read_long_register(reg, + idx, offsetof(struct msm_rpm_stats_data, + client_votes)); +} + +static int rpm_stats_write_buf(struct seq_file *m) +{ + struct msm_rpm_stats_data rs; + int i; + + for (i = 0; i < NUM_STATS_RECORD; i++) { + char stat_type[5] = {0}; + uint64_t time; + + rpm_stats_copy_data(&rs, i); + memcpy(stat_type, &rs.stat_type, sizeof(uint32_t)); + seq_printf(m, "RPM Mode:%s\n", stat_type); + seq_printf(m, "\tcount:%d\n", rs.count); + + time = rs.last_exited_at - rs.last_entered_at; + time = get_time_in_msec(time); + seq_printf(m, "\ttime in last mode(msec):%llu\n", time); + + time = arch_counter_get_cntpct() - rs.last_exited_at; + time = get_time_in_sec(time); + seq_printf(m, "\ttime since last mode(sec):%llu\n", time); + + time = get_time_in_msec(rs.accumulated); + seq_printf(m, "\tactual last sleep(msec):%llu\n", time); + + seq_printf(m, "\tclient votes: %#010x\n\n", rs.client_votes); + } + + return 0; +} + +static void master_stats_copy_data(struct rpm_master_stats_data *data, int idx) +{ + data->shutdown_req = readq_relaxed(ss.rpm_master_addr + + idx * MASTER_OFFSET_ADDRESS + + offsetof(struct rpm_master_stats_data, shutdown_req)); + + data->wakeup_ind = readq_relaxed(ss.rpm_master_addr + + (idx * MASTER_OFFSET_ADDRESS + + offsetof(struct rpm_master_stats_data, wakeup_ind))); + + data->bringup_req = readq_relaxed(ss.rpm_master_addr + + (idx * MASTER_OFFSET_ADDRESS + + offsetof(struct rpm_master_stats_data, bringup_req))); + + data->bringup_ack = readq_relaxed(ss.rpm_master_addr + + (idx * MASTER_OFFSET_ADDRESS + + offsetof(struct rpm_master_stats_data, bringup_ack))); + + data->xo_last_entered_at = readq_relaxed(ss.rpm_master_addr + + (idx * MASTER_OFFSET_ADDRESS + + offsetof(struct rpm_master_stats_data, + xo_last_entered_at))); + + data->xo_last_exited_at = readq_relaxed(ss.rpm_master_addr + + (idx * MASTER_OFFSET_ADDRESS + + offsetof(struct rpm_master_stats_data, + xo_last_exited_at))); + + data->xo_accumulated_duration = + readq_relaxed(ss.rpm_master_addr + + (idx * MASTER_OFFSET_ADDRESS + + offsetof(struct rpm_master_stats_data, + xo_accumulated_duration))); + + data->last_sleep_transition_duration = + readl_relaxed(ss.rpm_master_addr + + (idx * MASTER_OFFSET_ADDRESS + + offsetof(struct rpm_master_stats_data, + last_sleep_transition_duration))); + + data->last_wake_transition_duration = + readl_relaxed(ss.rpm_master_addr + + (idx * MASTER_OFFSET_ADDRESS + + offsetof(struct rpm_master_stats_data, + last_wake_transition_duration))); + + data->xo_count = + readl_relaxed(ss.rpm_master_addr + + (idx * MASTER_OFFSET_ADDRESS + + offsetof(struct rpm_master_stats_data, + xo_count))); + + data->wakeup_reason = readl_relaxed(ss.rpm_master_addr + + (idx * MASTER_OFFSET_ADDRESS + + offsetof(struct rpm_master_stats_data, + wakeup_reason))); + + data->numshutdowns = readl_relaxed(ss.rpm_master_addr + + (idx * MASTER_OFFSET_ADDRESS + + offsetof(struct rpm_master_stats_data, numshutdowns))); + + data->active_cores = readl_relaxed(ss.rpm_master_addr + + (idx * MASTER_OFFSET_ADDRESS) + + offsetof(struct rpm_master_stats_data, active_cores)); + +} + +static int master_stats_write_buf(struct seq_file *m) +{ + struct rpm_master_stats_data ms; + int i; + + for (i = 0; i < ss.num_masters; i++) { + + master_stats_copy_data(&ms, i); + + seq_printf(m, "%s\n", ss.master[i]); + seq_printf(m, "\tShutdown Request:0x%llX\n", ms.shutdown_req); + seq_printf(m, "\tWakeup interrupt:0x%llX\n", ms.wakeup_ind); + seq_printf(m, "\tBringup Req::0x%llX\n", ms.bringup_req); + seq_printf(m, "\tBringup Ack:0x%llX\n", ms.bringup_ack); + seq_printf(m, "\tLast XO Entry:0x%llX\n", + ms.xo_last_entered_at); + seq_printf(m, "\tLast XO Exit:0x%llX\n", ms.xo_last_exited_at); + seq_printf(m, "\tAccumulated XO duration:0x%llX\n", + ms.xo_accumulated_duration); + seq_printf(m, "\tLast sleep transition duration:0x%x\n", + ms.last_sleep_transition_duration); + seq_printf(m, "\tLast wake transition duration:0x%x\n", + ms.last_wake_transition_duration); + seq_printf(m, "\tXO Count:0x%x\n", ms.xo_count); + seq_printf(m, "\tWakeup Reason:0x%s\n", + ms.wakeup_reason ? "Sched" : "Rude"); + seq_printf(m, "\tNum Shutdowns:0x%x\n", ms.numshutdowns); + seq_printf(m, "\tActive Cores:0x%x\n", ms.active_cores); + } + return 0; +} + +static int ss_file_show(struct seq_file *m, void *v) +{ + int ret = 0; + + ret = rpm_stats_write_buf(m); + if (ret) + return ret; + + return master_stats_write_buf(m); +} + +static int ss_file_open(struct inode *inode, struct file *file) +{ + return single_open(file, ss_file_show, inode->i_private); +} + +static const struct file_operations ss_file_ops = { + .owner = THIS_MODULE, + .open = ss_file_open, + .read = seq_read, + .llseek = no_llseek, + .release = single_release, +}; + +static int msm_rpmstats_probe(struct platform_device *pdev) +{ + struct dentry *dent = NULL; + struct device_node *node = NULL; + uint32_t offset = 0; + void __iomem *offset_addr = NULL; + struct resource res; + int i, ret = 0; + + if (!pdev) + return -EINVAL; + + node = of_parse_phandle(pdev->dev.of_node, "qcom,rpm-msg-ram", 0); + if (!node) + return -EINVAL; + + ret = of_address_to_resource(node, 1, &res); + if (ret) + return ret; + + offset_addr = ioremap_nocache(res.start + STATS_OFFSET, + resource_size(&res)); + if (!offset_addr) + return -ENOMEM; + + offset = readl_relaxed(offset_addr); + iounmap(offset_addr); + + ret = of_address_to_resource(node, 0, &res); + if (ret) + return ret; + + ss.rpm_stats_addr = devm_ioremap_nocache(&pdev->dev, + res.start + offset, + resource_size(&res)); + + if (!ss.rpm_stats_addr) + return -ENOMEM; + + node = of_parse_phandle(pdev->dev.of_node, "qcom,rpm-code-ram", 0); + if (!node) + return -EINVAL; + + ret = of_address_to_resource(node, 0, &res); + if (ret) + return ret; + + ss.rpm_master_addr = devm_ioremap_nocache( + &pdev->dev, res.start + MASTER_START_ADDR_OFFSET, + resource_size(&res)); + + if (!ss.rpm_master_addr) + return -ENOMEM; + + ss.num_masters = of_property_count_strings(pdev->dev.of_node, + "qcom,masters"); + if (ss.num_masters < 0) { + dev_err(&pdev->dev, "Failed to get number of masters =%d\n", + ss.num_masters); + return -EINVAL; + } + + ss.master = devm_kzalloc(&pdev->dev, + sizeof(char *) * ss.num_masters, GFP_KERNEL); + + if (!ss.master) { + dev_err(&pdev->dev, "%s:Failed to allocated memory\n", + __func__); + return -ENOMEM; + } + + /* + * Read master names from DT + */ + for (i = 0; i < ss.num_masters; i++) { + const char *master_name; + + of_property_read_string_index(pdev->dev.of_node, + "qcom,masters", + i, &master_name); + ss.master[i] = devm_kzalloc(&pdev->dev, + sizeof(char) * strlen(master_name) + 1, + GFP_KERNEL); + if (!ss.master[i]) { + pr_err("%s:Failed to get memory\n", __func__); + return -ENOMEM; + } + strlcpy(ss.master[i], master_name, + strlen(master_name) + 1); + } + + dent = debugfs_create_file("system_stats", S_IRUGO, NULL, + &ss, &ss_file_ops); + + if (!dent) { + pr_err("%s: ERROR rpm_stats debugfs_create_file fail\n", + __func__); + return -ENOMEM; + } + + platform_set_drvdata(pdev, dent); + return 0; +} + +static int msm_rpmstats_remove(struct platform_device *pdev) +{ + struct dentry *dent; + + dent = platform_get_drvdata(pdev); + debugfs_remove(dent); + return 0; +} + +static const struct of_device_id rpm_stats_table[] = { + {.compatible = "qcom,system-stats"}, + {}, +}; + +static struct platform_driver msm_system_stats_driver = { + .probe = msm_rpmstats_probe, + .remove = msm_rpmstats_remove, + .driver = { + .name = "msm_stat", + .owner = THIS_MODULE, + .of_match_table = rpm_stats_table, + }, +}; + +module_platform_driver(msm_system_stats_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM Statistics driver"); +MODULE_ALIAS("platform:stat_log"); |
