Skip to content
Snippets Groups Projects
Commit fff168b0 authored by Naveen Ramaraj's avatar Naveen Ramaraj Committed by Thierry Strudel
Browse files

msm: rpm_master_stats: Add DT support for rpm master stat driver


RPM master stats driver is used to show each master stats by
reading the RPM message RAM. These stats shows the useful
information of each subsystems like "active cores", "number of
shoutdowns" and "wakeup reason" etc. Current driver do not have
the DT support and it can't be used for few targets. Hence
implementing the DT support and make the driver more generic to
support different family of chipsets.

Cherry-Picked-From: codeaurora.org msm-3.4 commit: cd513b84
CRs-Fixed: 517589
Change-Id: If6695939c6da5e0620f91f21a8b377be12ccb444
Signed-off-by: default avatarMurali Nalajala <mnalajal@codeaurora.org>
Signed-off-by: default avatarNaveen Ramaraj <nramaraj@codeaurora.org>
parent 9635f396
No related branches found
No related tags found
No related merge requests found
* 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>;
};
......@@ -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));
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);
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 = 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)
......@@ -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,
},
};
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment