Loading Documentation/devicetree/bindings/arm/msm/npa-dump.txt 0 → 100644 +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>; }; drivers/soc/qcom/Kconfig +6 −0 Original line number Diff line number Diff line Loading @@ -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 drivers/soc/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading drivers/soc/qcom/npa-dump.c 0 → 100644 +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"); Loading
Documentation/devicetree/bindings/arm/msm/npa-dump.txt 0 → 100644 +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>; };
drivers/soc/qcom/Kconfig +6 −0 Original line number Diff line number Diff line Loading @@ -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
drivers/soc/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
drivers/soc/qcom/npa-dump.c 0 → 100644 +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");