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

Commit 3d9b8c14 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "qcom: npa-dump: Support reading NPA Dump from RPM"

parents 1b13992a 02ebeb20
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
* NPA Dump

RPM maintains the shared resources using the Node Power Architecture (NPA)
framework. The framework aggregates requests from different clients for a
resource and determines the final value. The NPA framework also provides an
option to log a snapshot of the resource active values and the client
requests. RPM provides a command to initiate and log this snapshot in the
shared ram

The required properties for npa-dump are:

- compatible: "qcom,npa-dump".
- reg: The address on RPM RAM from where the start address of log to be read.
       The second tuple would be the start of the RPM address and the size of
       the log.

Example:

	qcom,npa-dump@0xfc190020 {
		compatible = "qcom,npa-dump";
		reg = <0xfc190020 0x4>, <0xfc000000 0x1c00>;
	};
+6 −0
Original line number Diff line number Diff line
@@ -601,6 +601,12 @@ config MSM_PERFORMANCE
	  It ensures that no more than a user specified number of CPUs stay
	  online at any given point in time.

config QCOM_NPA_DUMP
	bool "Read NPA dump from the RPM memory"
	depends on DEBUG_FS
	help
	  Read the NPA dump from RPM memory through debugfs.

source "drivers/soc/qcom/memshare/Kconfig"

endif # ARCH_MSM
+1 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ endif
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_LOG) += rpm_log.o
obj-$(CONFIG_QCOM_NPA_DUMP) += npa-dump.o
obj-$(CONFIG_MSM_JTAG) += jtag-fuse.o jtag.o
obj-$(CONFIG_MSM_JTAG_MM) +=  jtag-fuse.o jtag-mm.o
obj-$(CONFIG_MSM_JTAGV8) += jtag-fuse.o jtagv8.o jtagv8-mm.o
+213 −0
Original line number Diff line number Diff line
/* Copyright (c) 2014, 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/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>

#include <soc/qcom/rpm-smd.h>

#define RESOURCE_NAME_MAX	64
#define MISC_RESOURCE		0x6373696D /* = misc */

struct npa_ver_header {
	u32 ver;
	u32 timestamp[2];
	u32 size;
};

struct npa_res {
	u8 units[4];
	u32 active_max;
	u32 active_state;
	u32 request_state;
};

struct npa_res_client {
	char name[4];
	char type[4];
	u32 request_state;
};

static u8 rpm_data[8];

static struct msm_rpm_kvp kvp = {
	.key = 0x706D7564, /*  = dump */
	.data = &rpm_data[0],
	.length = sizeof(rpm_data),
};

static int npa_file_read(struct seq_file *m, void *unused)
{
	int ret;
	struct npa_ver_header ver;
	struct npa_res res;
	struct npa_res_client client;
	u8 *pos = m->private;
	char name[RESOURCE_NAME_MAX];
	char *p;
	int i;
	u64 ts;

	if (!pos || !*pos)
		return -EINVAL;

	ret = msm_rpm_send_message(MSM_RPM_CTX_ACTIVE_SET,
					MISC_RESOURCE, 0, &kvp, 1);
	if (ret)
		return ret;

	/**
	 * | Version (uint32) | Timestamp (uint64) | NPA Dump Size (uint32) |
	 * | Resource Name (char[variable]) | Unit (char[4]) |
	 *      Active Max (uint32) | Active State (uint32) |
	 *      Request State (uint32) |
	 *  | Client Name (char[4]) | Type (char[4]) | Request State (uint32) |
	 *  | Client Name (char[4]) | Type (char[4]) | Request State (uint32) |
	 *   ...
	 *  | Client Name (char[4]) | Type (char[4]) | Request State (uint32) |
	 *  | NULL (uint32) |
	 */

	/* Header */
	memcpy_fromio(&ver, pos, sizeof(ver));
	ts = ver.timestamp[0] | ver.timestamp[1] << sizeof(u32);
	seq_printf(m, "Version = %u Timestamp = 0x%llx Size = %u\n",
			ver.ver, ts, ver.size);
	pos += sizeof(ver);

	while (*pos) {
		i = 0;
		p = pos;
		memset(name, 0, RESOURCE_NAME_MAX);
		/**
		 * Read the resource name (null terminated),
		 * aligned on a 4 byte boundary.
		 */
		do {
			name[i] = readb_relaxed(p++);
			if (i % 4 == 0)
				pos += 4;
			if (!name[i])
				break;
			i++;
		} while (1);

		memcpy_fromio(&res, pos, sizeof(res));
		seq_printf(m,
			"Resource Name: %s Unit: %.4s Active Max: %u Active State: %u Request State: %u\n",
			name, res.units, res.active_max,
			res.active_state, res.request_state);
		pos += sizeof(res);

		/* Read the clients */
		while (*pos) {
			memcpy_fromio(&client, pos, sizeof(client));
			seq_printf(m,
				"\tClient Name: %.4s Type: %.4s Request State: %u\n",
				client.name, client.type, client.request_state);
			pos += sizeof(client);
		}

		/* Skip the NULL terminator for the resource */
		pos += sizeof(u32);
	}

	return ret;
}

static int npa_file_open(struct inode *inode, struct file *file)
{
	if (!inode->i_private)
		return -ENODEV;

	return single_open(file, npa_file_read, inode->i_private);
}

static const struct file_operations npa_fops = {
	.open		= npa_file_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

static int npa_dump_probe(struct platform_device *pdev)
{
	struct resource *res;
	void __iomem *npa_base, *rpm_base;
	struct dentry *dent;
	int ret;

	/* Get the location of the NPA log's start address offset */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	rpm_base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(rpm_base))
		return PTR_ERR(rpm_base);

	/* Offset the log's start address from the RPM phys address */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
	npa_base = devm_ioremap(&pdev->dev,
				res->start + readl_relaxed(rpm_base),
				resource_size(res));
	if (IS_ERR(npa_base))
		return PTR_ERR(npa_base);

	dent = debugfs_create_file("npa-dump", S_IRUGO, NULL,
					npa_base, &npa_fops);
	if (!dent) {
		pr_err("%s: error debugfs_create_file failed\n", __func__);
		return -ENOMEM;
	}

	platform_set_drvdata(pdev, dent);

	return ret;
}

static int npa_dump_remove(struct platform_device *pdev)
{
	struct dentry *dent;

	dent = platform_get_drvdata(pdev);
	debugfs_remove(dent);

	return 0;
}

static const struct of_device_id npa_dump_table[] = {
	{.compatible = "qcom,npa-dump"},
	{},
};

static struct platform_driver npa_dump_driver = {
	.probe  = npa_dump_probe,
	.remove = npa_dump_remove,
	.driver = {
		.name = "npa-dump",
		.of_match_table = npa_dump_table,
	},
};
module_platform_driver(npa_dump_driver);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("RPM NPA Dump driver");
MODULE_ALIAS("platform:npa-dump");