mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 05:39:54 -04:00
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:
parent
3c9afc139b
commit
48a235fb41
1 changed files with 224 additions and 40 deletions
|
|
@ -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},
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue