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 0000000000000000000000000000000000000000..02396745dd5ea332a3e18e4200727d4211b597ef --- /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 a8a965e0a6d8835211b510424956e36fd7eb8ac0..3e1789fe8ff8451b353410aeca05c40bdd1c5840 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, }, };