From fff168b028c767ac0497ae2450bb4043fc546b38 Mon Sep 17 00:00:00 2001 From: Naveen Ramaraj <nramaraj@codeaurora.org> Date: Tue, 18 Aug 2015 15:10:14 -0700 Subject: [PATCH] msm: rpm_master_stats: Add DT support for rpm master stat driver RPM master stats driver is used to show each master stats by reading the RPM message RAM. These stats shows the useful information of each subsystems like "active cores", "number of shoutdowns" and "wakeup reason" etc. Current driver do not have the DT support and it can't be used for few targets. Hence implementing the DT support and make the driver more generic to support different family of chipsets. Cherry-Picked-From: codeaurora.org msm-3.4 commit: cd513b84 CRs-Fixed: 517589 Change-Id: If6695939c6da5e0620f91f21a8b377be12ccb444 Signed-off-by: Murali Nalajala <mnalajal@codeaurora.org> Signed-off-by: Naveen Ramaraj <nramaraj@codeaurora.org> --- .../bindings/arm/msm/rpm-master-stats.txt | 29 ++ arch/arm/mach-msm/rpm_master_stat.c | 304 ++++++++++++++---- 2 files changed, 276 insertions(+), 57 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/msm/rpm-master-stats.txt diff --git a/Documentation/devicetree/bindings/arm/msm/rpm-master-stats.txt b/Documentation/devicetree/bindings/arm/msm/rpm-master-stats.txt new file mode 100644 index 000000000000..02396745dd5e --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/rpm-master-stats.txt @@ -0,0 +1,29 @@ +* RPM Master Stats + +RPM maintains each master data in RPM message RAM at a specific +offset. It tells about the individual masters information at +any given time like "number of active cores in sub system", +"number of shutdowns" and "wakeup reason for SS" etc. These stats +can be show to the user using the debugfs interface of the kernel. +To achieve this device tree node has been added and it will hold +the address of the RPM RAM from where master stats are read. +Added version number to distinguish the type of data structure +being read from the RAM for different targets. + +The required properties for rpm-master-stats are: + +- compatible: "qcom,rpm-master-stats". +- reg: The address on the RPM RAM from where stats are read. +- qcom,masters: Each master name. +- qcom,master-offset: Offset required to access each master stats area. +- qcom,master-stats-version: Version number. + +Example: + +qcom,rpm-stats@fc428150 { + compatible = "qcom,rpm-stats"; + reg = <0xfc428150 0x1000>; + qcom,masters = "APSS", "MPSS", "LPSS", "PRONTO"; + qcom,master-offset = <2560>; + qcom,master-stats-version = <2>; +}; diff --git a/arch/arm/mach-msm/rpm_master_stat.c b/arch/arm/mach-msm/rpm_master_stat.c index a8a965e0a6d8..3e1789fe8ff8 100644 --- a/arch/arm/mach-msm/rpm_master_stat.c +++ b/arch/arm/mach-msm/rpm_master_stat.c @@ -22,39 +22,57 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/mm.h> +#include <linux/of.h> #include <asm/uaccess.h> #include <mach/msm_iomap.h> #include "rpm_stats.h" -#define MSG_RAM_SIZE_PER_MASTER 32 - -enum { - NUMSHUTDOWNS, - ACTIVECORES, - MASTER_ID_MAX, -}; - -static char *msm_rpm_master_stats_id_labels[MASTER_ID_MAX] = { - [NUMSHUTDOWNS] = "num_shutdowns", - [ACTIVECORES] = "active_cores", -}; +#define RPM_MASTERS_BUF_LEN 400 + +#define SNPRINTF(buf, size, format, ...) \ + do { \ + if (size > 0) { \ + int ret; \ + ret = snprintf(buf, size, format, ## __VA_ARGS__); \ + if (ret > size) { \ + buf += size; \ + size = 0; \ + } else { \ + 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)) struct msm_rpm_master_stats { - unsigned long numshutdowns; - unsigned long active_cores; + 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; }; struct msm_rpm_master_stats_private_data { void __iomem *reg_base; u32 len; char **master_names; - u32 nomasters; - char buf[256]; + 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, +int msm_rpm_master_stats_file_close(struct inode *inode, struct file *file) { struct msm_rpm_master_stats_private_data *private = file->private_data; @@ -67,53 +85,138 @@ static int msm_rpm_master_stats_file_close(struct inode *inode, } static int msm_rpm_master_copy_stats( - struct msm_rpm_master_stats_private_data *pdata) + struct msm_rpm_master_stats_private_data *prvdata) { struct msm_rpm_master_stats record; - static int nomasters; - int count; + struct msm_rpm_master_stats_platform_data *pdata; + static int master_cnt; + int count, j = 0; + char *buf; static DEFINE_MUTEX(msm_rpm_master_stats_mutex); - int j = 0; mutex_lock(&msm_rpm_master_stats_mutex); - /* - * iterrate possible nomasters times. - * 8960, 8064 have 5 masters. - * 8930 has 4 masters. - * 9x15 has 3 masters. - */ - if (nomasters > pdata->nomasters - 1) { - nomasters = 0; + + /* Iterate possible number of masters */ + if (master_cnt > prvdata->num_masters - 1) { + master_cnt = 0; mutex_unlock(&msm_rpm_master_stats_mutex); return 0; } - record.numshutdowns = readl_relaxed(pdata->reg_base + - (nomasters * MSG_RAM_SIZE_PER_MASTER)); - record.active_cores = readl_relaxed(pdata->reg_base + - (nomasters * MSG_RAM_SIZE_PER_MASTER + 4)); - - count = snprintf(pdata->buf, sizeof(pdata->buf), - "%s\n\t%s:%lu\n\t%s:%lu\n", - pdata->master_names[nomasters], - msm_rpm_master_stats_id_labels[0], - record.numshutdowns, - msm_rpm_master_stats_id_labels[1], - record.active_cores); + 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 = readll_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 = readll_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 = readll_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 = readll_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.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.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); + } - j = find_first_bit(&record.active_cores, BITS_PER_LONG); + j = find_first_bit((unsigned long *)&record.active_cores, + BITS_PER_LONG); while (j < BITS_PER_LONG) { - count += snprintf(pdata->buf + count, - sizeof(pdata->buf) - count, - "\t\tcore%d\n", j); - j = find_next_bit(&record.active_cores, + SNPRINTF(buf, count, "\t\tcore%d\n", j); + j = find_next_bit((unsigned long *)&record.active_cores, BITS_PER_LONG, j + 1); } - - nomasters++; + master_cnt++; mutex_unlock(&msm_rpm_master_stats_mutex); - return count; + return RPM_MASTERS_BUF_LEN - count; } static int msm_rpm_master_stats_file_read(struct file *file, char __user *bufu, @@ -151,7 +254,7 @@ static int msm_rpm_master_stats_file_open(struct inode *inode, pdata = inode->i_private; file->private_data = - kmalloc(sizeof(struct msm_rpm_master_stats_private_data), + kzalloc(sizeof(struct msm_rpm_master_stats_private_data), GFP_KERNEL); if (!file->private_data) @@ -159,7 +262,7 @@ static int msm_rpm_master_stats_file_open(struct inode *inode, prvdata = file->private_data; prvdata->reg_base = ioremap(pdata->phys_addr_base, - pdata->phys_size); + pdata->phys_size); if (!prvdata->reg_base) { kfree(file->private_data); prvdata = NULL; @@ -170,7 +273,7 @@ static int msm_rpm_master_stats_file_open(struct inode *inode, } prvdata->len = 0; - prvdata->nomasters = pdata->num_masters; + prvdata->num_masters = pdata->num_masters; prvdata->master_names = pdata->masters; prvdata->platform_data = pdata; return 0; @@ -184,27 +287,108 @@ static const struct file_operations msm_rpm_master_stats_fops = { .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) { + dev_err(dev, "could not allocate memory for platform data\n"); + 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) { + dev_err(dev, "%s:Failed to allocated memory\n", __func__); + 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_kzalloc(dev, sizeof(char) * + strlen(master_name) + 1, GFP_KERNEL); + if (!pdata->masters[i]) { + dev_err(dev, "%s:Failed to get memory\n", __func__); + goto err; + } + strlcpy(pdata->masters[i], master_name, + strlen(master_name) + 1); + } + return pdata; +err: + return NULL; +} + static int __devinit msm_rpm_master_stats_probe(struct platform_device *pdev) { struct dentry *dent; struct msm_rpm_master_stats_platform_data *pdata; - struct resource *res; + struct resource *res = NULL; - pdata = pdev->dev.platform_data; - if (!pdata) + if (!pdev) return -EINVAL; + if (pdev->dev.of_node) + pdata = msm_rpm_master_populate_pdata(&pdev->dev); + else + pdata = pdev->dev.platform_data; + + if (!pdata) { + dev_err(&pdev->dev, "%s: Unable to get pdata\n", __func__); + 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", + __func__); + return -ENXIO; + } + pdata->phys_addr_base = res->start; pdata->phys_size = resource_size(res); dent = debugfs_create_file("rpm_master_stats", S_IRUGO, NULL, - pdev->dev.platform_data, &msm_rpm_master_stats_fops); + pdata, &msm_rpm_master_stats_fops); if (!dent) { - pr_err("%s: ERROR debugfs_create_file failed\n", __func__); + dev_err(&pdev->dev, "%s: ERROR debugfs_create_file failed\n", + __func__); return -ENOMEM; } + platform_set_drvdata(pdev, dent); return 0; } @@ -219,12 +403,18 @@ static int __devexit msm_rpm_master_stats_remove(struct platform_device *pdev) return 0; } +static 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 = __devexit_p(msm_rpm_master_stats_remove), .driver = { .name = "msm_rpm_master_stats", .owner = THIS_MODULE, + .of_match_table = rpm_master_table, }, }; -- GitLab