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

Commit 48a235fb authored by Minghao Zhang's avatar Minghao Zhang Committed by Gerrit - the friendly Code Review server
Browse files

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: default avatarMinghao Zhang <minghao@codeaurora.org>
parent 3c9afc13
Loading
Loading
Loading
Loading
+224 −40
Original line number Diff line number Diff line
@@ -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 *)

#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 subsystem_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,23 +225,32 @@ 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;
	if (cmd != DDR_STATS_IOCTL) {
		ret = subsystem_sleep_stats(drvdata, temp, pid, smem_item);
		if (ret < 0)
			goto out_free;
	}

	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
@@ -162,7 +264,12 @@ static long stats_data_ioctl(struct file *file, unsigned int cmd,
					- temp->last_entered_at);
		}

	ret = copy_to_user((void __user *)arg, temp, sizeof(struct subsystem_stats));
		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));
	}

	kfree(temp);
	mutex_unlock(&sleep_stats_mutex);
@@ -178,14 +285,21 @@ static long stats_data_ioctl(struct file *file, unsigned int cmd,

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},
	{},
};