Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e339a99c authored by Karthik Parsha's avatar Karthik Parsha
Browse files

rpm-rail-stats: Add support to read RPM rail stats



Shared rails such as CX, MX are controlled by the RPM. RPM aggregates
the votes to these rails and sets the rails at a corner voltage to
satisfy the needs of all the masters.

Add support to read and print the CX, MX rail-stats from shared memory.

cat /d/rpm_rail_stats

Number of Rails:2
        rail:mx num_corners:4  current_corner:2  last_entered:1019693304
                corner:ret   time:0
                corner:svs   time:0
                corner:nom   time:1012700395189
                corner:turb  time:1015543861827
        rail:cx num_corners:5  current_corner:1  last_entered:1019698990
                corner:ret   time:0
                corner:svs2  time:8544797816954
                corner:svs   time:14534859650042
                corner:nom   time:8934117151715
                corner:turb  time:1015543586519

Change-Id: I730c4accc1bf4c152ef95f9ce9343a58022c2630
Signed-off-by: default avatarKarthik Parsha <kparsha@codeaurora.org>
parent f779b1c4
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
* RPM Rail Stats

RPM maintains rail and corner data in the RPM RAM. A device tree node
will hold the address of the RPM RAM from where rail stats are read.

RPM maintains free heap space availability in the RPM RAM. RPM free heap
space value is required for debugging purpose.

The required properties for rpm-rail-stats are:

- compatible: "qcom,rpm-rail-stats"
- reg: The address on the RPM RAM from where stats are read.

	Second register specifies the offset of the rpm rail
	stats start address pointer.

- reg-names: Name of the register holding address.

Example:

qcom,rpm-stats@200000 {
		compatible = "qcom,rpm-stats";
		reg = <0x200000 0x100>,
			<0x290000 0x4>;
		reg-names = "phys_addr_base",
				"offset_addr"
};
+1 −1
Original line number Diff line number Diff line
@@ -46,7 +46,7 @@ obj-y += qdsp6v2/
obj-$(CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG) += rpm_rbcpr_stats_v2.o
obj-$(CONFIG_MEM_SHARE_QMI_SERVICE)		+= memshare/
obj-$(CONFIG_CP_ACCESS64) += cpaccess64.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 rpm_rail_stats.o
obj-$(CONFIG_MSM_RPM_LOG) += rpm_log.o
obj-$(CONFIG_MSM_JTAG) += jtag-fuse.o jtag.o
obj-$(CONFIG_MSM_JTAG_MM) +=  jtag-fuse.o jtag-mm.o
+322 −0
Original line number Diff line number Diff line
/* Copyright (c) 2015, 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, ...) \
	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 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) {
		pr_err("%s: Failed to ioremap address: %x\n",
				__func__, offset_addr);
		return -ENODEV;
	}
	offset_addr = readl_relaxed(phys_ptr);
	iounmap(phys_ptr);

	if (!offset_addr) {
		pr_err("%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;
	else if (node)
		pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);

	if (!pdata)
		return -ENOMEM;

	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 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");