diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2017-02-15 17:00:56 -0800 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-02-15 17:00:56 -0800 |
commit | 33ba0cc09ded50adea28c8b4214696c1d4927a82 (patch) | |
tree | 30db991ef1542c73eaa955f78325657cc798b32c /drivers | |
parent | 18e54e2239b3a5eaacbd653ed5867d74e4066dc0 (diff) | |
parent | 70e9765c9307c27142eef65a70a34e22abfa36cb (diff) |
Merge "rpm-rail-stats: Add support to read RPM rail stats"
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/soc/qcom/Makefile | 2 | ||||
-rw-r--r-- | drivers/soc/qcom/rpm_rail_stats.c | 329 |
2 files changed, 330 insertions, 1 deletions
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 623e389aff1f..2605107e2dbd 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -95,7 +95,7 @@ obj-$(CONFIG_QCOM_REMOTEQDSS) += remoteqdss.o obj-$(CONFIG_MSM_SERVICE_LOCATOR) += service-locator.o obj-$(CONFIG_MSM_QBT1000) += qbt1000.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 system_stats.o +obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o rpm_master_stat.o rpm_rail_stats.o system_stats.o obj-$(CONFIG_MSM_RPM_LOG) += rpm_log.o obj-$(CONFIG_QSEE_IPC_IRQ_BRIDGE) += qsee_ipc_irq_bridge.o obj-$(CONFIG_WCD_DSP_GLINK) += wcd-dsp-glink.o diff --git a/drivers/soc/qcom/rpm_rail_stats.c b/drivers/soc/qcom/rpm_rail_stats.c new file mode 100644 index 000000000000..d80a9fc5146a --- /dev/null +++ b/drivers/soc/qcom/rpm_rail_stats.c @@ -0,0 +1,329 @@ +/* Copyright (c) 2015,2017, 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/of.h> +#include <linux/uaccess.h> + +#include "rpm_stats.h" + +#define RPM_RAIL_BUF_LEN 600 + +#define SNPRINTF(buf, size, format, ...) \ +{ \ + if (size > 0) { \ + int ret; \ + ret = snprintf(buf, size, format, ## __VA_ARGS__); \ + if (ret > size) { \ + buf += size; \ + size = 0; \ + } else { \ + buf += ret; \ + size -= ret; \ + } \ + } \ +} + +#define NAMELEN (sizeof(uint32_t)+1) + +struct msm_rpm_rail_stats_platform_data { + phys_addr_t phys_addr_base; + u32 phys_size; +}; + +struct msm_rpm_rail_corner { + uint64_t time; + uint32_t corner; + uint32_t reserved; +}; + +struct msm_rpm_rail_type { + uint32_t rail; + uint32_t num_corners; + uint32_t current_corner; + uint32_t last_entered; +}; + +struct msm_rpm_rail_stats { + uint32_t num_rails; + uint32_t reserved; +}; + +struct msm_rpm_rail_stats_private_data { + void __iomem *reg_base; + u32 len; + char buf[RPM_RAIL_BUF_LEN]; + struct msm_rpm_rail_stats_platform_data *platform_data; +}; + +int msm_rpm_rail_stats_file_close(struct inode *inode, struct file *file) +{ + struct msm_rpm_rail_stats_private_data *private = file->private_data; + + if (private->reg_base) + iounmap(private->reg_base); + kfree(file->private_data); + + return 0; +} + +static int msm_rpm_rail_corner_copy(void __iomem **base, char **buf, + int count) +{ + struct msm_rpm_rail_corner rc; + char corner[NAMELEN]; + + memset(&rc, 0, sizeof(rc)); + memcpy_fromio(&rc, *base, sizeof(rc)); + + corner[NAMELEN - 1] = '\0'; + memcpy(corner, &rc.corner, NAMELEN - 1); + SNPRINTF(*buf, count, "\t\tcorner:%-5s time:%-16llu\n", + corner, rc.time); + + *base += sizeof(rc); + + return count; +} + +static int msm_rpm_rail_type_copy(void __iomem **base, char **buf, int count) +{ + struct msm_rpm_rail_type rt; + char rail[NAMELEN]; + int i; + + memset(&rt, 0, sizeof(rt)); + memcpy_fromio(&rt, *base, sizeof(rt)); + + rail[NAMELEN - 1] = '\0'; + memcpy(rail, &rt.rail, NAMELEN - 1); + SNPRINTF(*buf, count, + "\trail:%-2s num_corners:%-2u current_corner:%-2u last_entered:%-8u\n", + rail, rt.num_corners, rt.current_corner, rt.last_entered); + + *base += sizeof(rt); + + for (i = 0; i < rt.num_corners; i++) + count = msm_rpm_rail_corner_copy(base, buf, count); + + return count; +} + +static int msm_rpm_rail_stats_copy( + struct msm_rpm_rail_stats_private_data *prvdata) +{ + struct msm_rpm_rail_stats rs; + void __iomem *base = prvdata->reg_base; + char *buf = prvdata->buf; + int count = RPM_RAIL_BUF_LEN; + int i; + + memset(&rs, 0, sizeof(rs)); + memcpy_fromio(&rs, base, sizeof(rs)); + + SNPRINTF(buf, count, "Number of Rails:%u\n", rs.num_rails); + + base = prvdata->reg_base + sizeof(rs); + + for (i = 0; i < rs.num_rails; i++) + count = msm_rpm_rail_type_copy(&base, &buf, count); + + return RPM_RAIL_BUF_LEN - count; +} + +static ssize_t msm_rpm_rail_stats_file_read(struct file *file, + char __user *bufu, size_t count, loff_t *ppos) +{ + struct msm_rpm_rail_stats_private_data *prvdata = + file->private_data; + struct msm_rpm_rail_stats_platform_data *pdata; + + if (!prvdata) + return -EINVAL; + + if (!prvdata->platform_data) + return -EINVAL; + + if (!bufu || count == 0) + return -EINVAL; + + pdata = prvdata->platform_data; + + if (*ppos <= pdata->phys_size) { + prvdata->len = msm_rpm_rail_stats_copy(prvdata); + *ppos = 0; + } + + return simple_read_from_buffer(bufu, count, ppos, + prvdata->buf, prvdata->len); +} + +static int msm_rpm_rail_stats_file_open(struct inode *inode, + struct file *file) +{ + struct msm_rpm_rail_stats_private_data *prvdata; + struct msm_rpm_rail_stats_platform_data *pdata = inode->i_private; + + file->private_data = + kzalloc(sizeof(struct msm_rpm_rail_stats_private_data), + GFP_KERNEL); + + if (!file->private_data) + return -ENOMEM; + 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); + return -EBUSY; + } + + prvdata->len = 0; + prvdata->platform_data = pdata; + return 0; +} + + +static const struct file_operations msm_rpm_rail_stats_fops = { + .owner = THIS_MODULE, + .open = msm_rpm_rail_stats_file_open, + .read = msm_rpm_rail_stats_file_read, + .release = msm_rpm_rail_stats_file_close, + .llseek = no_llseek, +}; + +static int msm_rpm_rail_stats_probe(struct platform_device *pdev) +{ + struct dentry *dent; + struct msm_rpm_rail_stats_platform_data *pdata; + struct resource *res; + struct resource *offset; + struct device_node *node; + uint32_t offset_addr; + void __iomem *phys_ptr; + + if (!pdev) + return -EINVAL; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "phys_addr_base"); + if (!res) + return -EINVAL; + + offset = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "offset_addr"); + if (!offset) + return -EINVAL; + + phys_ptr = ioremap_nocache(offset->start, SZ_4); + if (!phys_ptr) { + dev_err(&pdev->dev, "%s: Failed to ioremap address.\n", + __func__); + return -ENODEV; + } + offset_addr = readl_relaxed(phys_ptr); + iounmap(phys_ptr); + + if (!offset_addr) { + dev_err(&pdev->dev, "%s: RPM Rail Stats not available: Exit\n", + __func__); + return 0; + } + + node = pdev->dev.of_node; + + if (pdev->dev.platform_data) { + pdata = pdev->dev.platform_data; + if (!pdata) + return -ENOMEM; + } else if (node) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + } else { + dev_err(&pdev->dev, "%s: pdata is not available: Exit\n", + __func__); + return 0; + } + + pdata->phys_addr_base = res->start + offset_addr; + pdata->phys_size = resource_size(res); + + dent = debugfs_create_file("rpm_rail_stats", S_IRUGO, NULL, + pdata, &msm_rpm_rail_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_rail_stats_remove(struct platform_device *pdev) +{ + struct dentry *dent = platform_get_drvdata(pdev); + + debugfs_remove(dent); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id rpm_rail_table[] = { + {.compatible = "qcom,rpm-rail-stats"}, + {}, +}; + +static struct platform_driver msm_rpm_rail_stats_driver = { + .probe = msm_rpm_rail_stats_probe, + .remove = msm_rpm_rail_stats_remove, + .driver = { + .name = "msm_rpm_rail_stats", + .owner = THIS_MODULE, + .of_match_table = rpm_rail_table, + }, +}; + +static int __init msm_rpm_rail_stats_init(void) +{ + return platform_driver_register(&msm_rpm_rail_stats_driver); +} + +static void __exit msm_rpm_rail_stats_exit(void) +{ + platform_driver_unregister(&msm_rpm_rail_stats_driver); +} + +module_init(msm_rpm_rail_stats_init); +module_exit(msm_rpm_rail_stats_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM RPM rail Statistics driver"); +MODULE_ALIAS("platform:msm_rail_stat_log"); |