soc: qcom: subsystem_sleep_stats: Add support soc sleep info

This change enhances subsystem_sleep_stats driver to give soc sleep
information including aosd, cxsd, ddr and ddr_stats.

Change-Id: I7be161158d5dc9b5d06a940cc69cf883d73fd803
Signed-off-by: Minghao Zhang <minghao@codeaurora.org>
This commit is contained in:
Minghao Zhang 2021-07-16 21:59:47 +08:00 • committed by Gerrit - the friendly Code Review server
commit 48a235fb41

View file

@ -5,6 +5,7 @@
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/ioctl.h>
#include <linux/module.h>
#include <linux/of.h>
@ -17,29 +18,50 @@
#define STATS_MAX_MINOR 1
#define STATS_DEVICE_NAME "stats"
#define SUBSYSTEM_STATS_MAGIC_NUM (0x9d)
#define SUBSYSTEM_STATS_OTHERS_NUM (-2)
#define DDR_STATS_MAGIC_KEY 0xA1157A75
#define DDR_STATS_MAX_NUM_MODES 0x14
#define DDR_STATS_MAGIC_KEY_ADDR 0x0
#define DDR_STATS_NUM_MODES_ADDR 0x4
#define DDR_STATS_NAME_ADDR 0x0
#define DDR_STATS_COUNT_ADDR 0x4
#define DDR_STATS_DURATION_ADDR 0x8
#define APSS_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 0, \
struct subsystem_stats *)
struct sleep_stats *)
#define MODEM_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 1, \
struct subsystem_stats *)
struct sleep_stats *)
#define WPSS_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 2, \
struct subsystem_stats *)
struct sleep_stats *)
#define ADSP_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 3, \
struct subsystem_stats *)
struct sleep_stats *)
#define ADSP_ISLAND_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 4, \
struct subsystem_stats *)
struct sleep_stats *)
#define CDSP_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 5, \
struct subsystem_stats *)
struct sleep_stats *)
#define SLPI_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 6, \
struct subsystem_stats *)
struct sleep_stats *)
#define GPU_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 7, \
struct subsystem_stats *)
struct sleep_stats *)
#define DISPLAY_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 8, \
struct subsystem_stats *)
struct sleep_stats *)
#define SLPI_ISLAND_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 9, \
struct subsystem_stats *)
struct sleep_stats *)
struct subsystem_stats {
#define AOSD_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 10, \
struct sleep_stats *)
#define CXSD_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 11, \
struct sleep_stats *)
#define DDR_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 12, \
struct sleep_stats *)
#define DDR_STATS_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 13, \
struct sleep_stats *)
struct sleep_stats {
u32 version;
u32 count;
u64 last_entered_at;
@ -48,6 +70,10 @@ struct subsystem_stats {
};
enum subsystem_smem_id {
AOSD = 0,
CXSD = 1,
DDR = 2,
DDR_STATS = 3,
MPSS = 605,
ADSP,
CDSP,
@ -67,6 +93,13 @@ enum subsystem_pid {
PID_WPSS = 13,
PID_GPU = PID_APSS,
PID_DISPLAY = PID_APSS,
PID_OTHERS = -2,
};
struct stats_config {
unsigned int offset_addr;
unsigned int ddr_offset_addr;
unsigned int num_records;
};
struct sleep_stats_data {
@ -74,20 +107,80 @@ struct sleep_stats_data {
struct class *stats_class;
struct device *stats_device;
struct cdev stats_cdev;
const struct stats_config **config;
void __iomem *reg_base;
void __iomem *ddr_reg;
void __iomem **reg;
u32 ddr_key;
u32 ddr_entry_count;
};
static DEFINE_MUTEX(sleep_stats_mutex);
static int stats_data_open(struct inode *inode, struct file *file)
{
struct sleep_stats_data *drvdata = NULL;
if (!inode || !inode->i_cdev || !file)
return -EINVAL;
drvdata = container_of(inode->i_cdev, struct sleep_stats_data, stats_cdev);
file->private_data = drvdata;
return 0;
}
static void ddr_stats_sleep_stat(struct sleep_stats_data *stats_data, struct sleep_stats *ddr_stats)
{
void __iomem *reg;
int i;
reg = stats_data->ddr_reg + DDR_STATS_NUM_MODES_ADDR + 0x4;
for (i = 0; i < stats_data->ddr_entry_count; i++) {
(ddr_stats + i)->version = readl_relaxed(reg + DDR_STATS_NAME_ADDR);
(ddr_stats + i)->count = readl_relaxed(reg + DDR_STATS_COUNT_ADDR);
(ddr_stats + i)->last_entered_at = 0xDEADDEAD;
(ddr_stats + i)->last_exited_at = 0xDEADDEAD;
(ddr_stats + i)->accumulated = readq_relaxed(reg + DDR_STATS_DURATION_ADDR);
reg += sizeof(struct sleep_stats) - 2 * sizeof(u64);
}
}
static int subsystem_sleep_stats(struct sleep_stats_data *stats_data, struct sleep_stats *stats,
unsigned int pid, unsigned int idx)
{
struct sleep_stats *subsystem_stats_data;
if (pid == SUBSYSTEM_STATS_OTHERS_NUM)
memcpy_fromio(stats, stats_data->reg[idx], sizeof(*stats));
else {
subsystem_stats_data = qcom_smem_get(pid, idx, NULL);
if (IS_ERR(subsystem_stats_data))
return -ENODEV;
stats->version = subsystem_stats_data->version;
stats->count = subsystem_stats_data->count;
stats->last_entered_at = subsystem_stats_data->last_entered_at;
stats->last_exited_at = subsystem_stats_data->last_exited_at;
stats->accumulated = subsystem_stats_data->accumulated;
}
return 0;
}
static long stats_data_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct subsystem_stats *temp, *subsystem_stats_data;
struct sleep_stats_data *drvdata = file->private_data;
struct sleep_stats *temp;
int ret = -ENOMEM;
unsigned int pid, smem_item;
mutex_lock(&sleep_stats_mutex);
temp = kzalloc(sizeof(struct subsystem_stats), GFP_KERNEL);
if (cmd != DDR_STATS_IOCTL)
temp = kzalloc(sizeof(struct sleep_stats), GFP_KERNEL);
else
temp = kcalloc(DDR_STATS_MAX_NUM_MODES, sizeof(struct sleep_stats), GFP_KERNEL);
if (!temp)
goto out_unlock;
@ -132,38 +225,52 @@ static long stats_data_ioctl(struct file *file, unsigned int cmd,
pid = PID_SLPI;
smem_item = SLPI_ISLAND;
break;
case AOSD_IOCTL:
pid = PID_OTHERS;
smem_item = AOSD;
break;
case CXSD_IOCTL:
pid = PID_OTHERS;
smem_item = CXSD;
break;
case DDR_IOCTL:
pid = PID_OTHERS;
smem_item = DDR;
break;
case DDR_STATS_IOCTL:
pid = PID_OTHERS;
smem_item = DDR_STATS;
break;
default:
pr_err("Incorrect command error\n");
ret = -EINVAL;
goto out_free;
}
subsystem_stats_data = qcom_smem_get(pid, smem_item, NULL);
if (IS_ERR(subsystem_stats_data)) {
ret = -ENODEV;
goto out_free;
if (cmd != DDR_STATS_IOCTL) {
ret = subsystem_sleep_stats(drvdata, temp, pid, smem_item);
if (ret < 0)
goto out_free;
/*
* If a subsystem is in sleep when reading the sleep stats from SMEM
* adjust the accumulated sleep duration to show actual sleep time.
* This ensures that the displayed stats are real when used for
* the purpose of computing battery utilization.
*/
if (temp->last_entered_at > temp->last_exited_at) {
temp->accumulated +=
(__arch_counter_get_cntvct()
- temp->last_entered_at);
}
ret = copy_to_user((void __user *)arg, temp, sizeof(struct sleep_stats));
} else {
ddr_stats_sleep_stat(drvdata, temp);
ret = copy_to_user((void __user *)arg, temp,
DDR_STATS_MAX_NUM_MODES * sizeof(struct sleep_stats));
}
temp->version = subsystem_stats_data->version;
temp->count = subsystem_stats_data->count;
temp->last_entered_at = subsystem_stats_data->last_entered_at;
temp->last_exited_at = subsystem_stats_data->last_exited_at;
temp->accumulated = subsystem_stats_data->accumulated;
/*
* If a subsystem is in sleep when reading the sleep stats from SMEM
* adjust the accumulated sleep duration to show actual sleep time.
* This ensures that the displayed stats are real when used for
* the purpose of computing battery utilization.
*/
if (temp->last_entered_at > temp->last_exited_at) {
temp->accumulated +=
(__arch_counter_get_cntvct()
- temp->last_entered_at);
}
ret = copy_to_user((void __user *)arg, temp, sizeof(struct subsystem_stats));
kfree(temp);
mutex_unlock(&sleep_stats_mutex);
@ -178,14 +285,21 @@ out_unlock:
static const struct file_operations stats_data_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.open = stats_data_open,
.unlocked_ioctl = stats_data_ioctl,
};
static int subsystem_stats_probe(struct platform_device *pdev)
{
struct sleep_stats_data *stats_data;
const struct stats_config *config;
struct resource *res;
void __iomem *offset_addr;
phys_addr_t stats_base;
resource_size_t stats_size;
int ret = -ENOMEM;
int i;
u32 offset;
stats_data = devm_kzalloc(&pdev->dev, sizeof(struct sleep_stats_data), GFP_KERNEL);
if (!stats_data)
@ -215,6 +329,70 @@ static int subsystem_stats_probe(struct platform_device *pdev)
goto fail_device_create;
}
config = device_get_match_data(&pdev->dev);
if (!config) {
ret = -ENODEV;
goto fail_device_create;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = PTR_ERR(res);
goto fail_device_create;
}
offset_addr = devm_ioremap(&pdev->dev, res->start + config->offset_addr, sizeof(u32));
if (IS_ERR(offset_addr)) {
ret = PTR_ERR(offset_addr);
goto fail_device_create;
}
stats_base = res->start | readl_relaxed(offset_addr);
stats_size = resource_size(res);
stats_data->reg_base = devm_ioremap(&pdev->dev, stats_base, stats_size);
if (!stats_data->reg_base) {
ret = -ENOMEM;
goto fail_device_create;
}
stats_data->config = devm_kcalloc(&pdev->dev, config->num_records,
sizeof(struct stats_config *), GFP_KERNEL);
stats_data->reg = devm_kcalloc(&pdev->dev, config->num_records, sizeof(void __iomem *),
GFP_KERNEL);
for (i = 0; i < config->num_records; i++) {
stats_data->config[i] = config;
offset = (i * sizeof(struct sleep_stats));
stats_data->reg[i] = stats_data->reg_base + offset;
}
offset_addr = devm_ioremap(&pdev->dev, res->start + config->ddr_offset_addr, sizeof(u32));
if (IS_ERR(offset_addr)) {
ret = PTR_ERR(offset_addr);
goto fail_device_create;
}
stats_base = res->start | readl_relaxed(offset_addr);
stats_data->ddr_reg = devm_ioremap(&pdev->dev, stats_base, stats_size);
if (!stats_data->ddr_reg) {
ret = -ENOMEM;
goto fail_device_create;
}
stats_data->ddr_key = readl_relaxed(stats_data->ddr_reg + DDR_STATS_MAGIC_KEY_ADDR);
if (stats_data->ddr_key != DDR_STATS_MAGIC_KEY) {
ret = -EINVAL;
goto fail_device_create;
}
stats_data->ddr_entry_count = readl_relaxed(stats_data->ddr_reg + DDR_STATS_NUM_MODES_ADDR);
if (stats_data->ddr_entry_count > DDR_STATS_MAX_NUM_MODES) {
ret = -EINVAL;
goto fail_device_create;
}
platform_set_drvdata(pdev, stats_data);
return 0;
@ -244,8 +422,14 @@ static int subsystem_stats_remove(struct platform_device *pdev)
return 0;
}
static const struct stats_config rpmh_data = {
.offset_addr = 0x4,
.ddr_offset_addr = 0x1c,
.num_records = 3,
};
static const struct of_device_id subsystem_stats_table[] = {
{.compatible = "qcom,subsystem-sleep-stats"},
{ .compatible = "qcom,subsystem-sleep-stats", .data = &rpmh_data},
{},
};