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

Commit 2598c908 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "rpmh_master_stat: Add support for application processor master stats"

parents b480776e 24c5f03e
Loading
Loading
Loading
Loading
+15 −2
Original line number Diff line number Diff line
@@ -4,15 +4,28 @@ Differet Subsystems maintains master data in SMEM.
It tells about the individual masters information at any given
time like "system sleep counts", "system sleep last entered at"
and "system sleep accumulated duration" etc. These stats can be
show to the user using the debugfs interface of the kernel.
displayed using the sysfs interface.
To achieve this, device tree node has been added.

Additionally, RPMH master stats also maintains application processor's
master stats. It uses profiling units to calculate power down and power
up stats.

The required properties for rpmh-master-stats are:

- compatible: "qcom,rpmh-master-stats".
- compatible:
	Usage: required
	Value type: <string>
	Definition: Should be "qcom,rpmh-master-stats-v1".

- reg:
	Usage: required
	Value type: <prop-encoded-array>
	Definition: Specifies physical address of start of profiling unit.

Example:

qcom,rpmh-master-stats {
	compatible = "qcom,rpmh-master-stats";
	reg = <0xb221200 0x60>;
};
+102 −46
Original line number Diff line number Diff line
/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2017-2018, 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
@@ -13,21 +13,25 @@

#define pr_fmt(fmt) "%s: " fmt, KBUILD_MODNAME

#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/uaccess.h>
#include <soc/qcom/smem.h>
#include "rpmh_master_stat.h"

#define UNIT_DIST 0x14
#define REG_VALID 0x0
#define REG_DATA_LO 0x4
#define REG_DATA_HI 0x8

#define GET_ADDR(REG, UNIT_NO) (REG + (UNIT_DIST * UNIT_NO))

enum master_smem_id {
	MPSS = 605,
@@ -48,6 +52,14 @@ enum master_pid {
	PID_DISPLAY = PID_APSS,
};

enum profile_data {
	POWER_DOWN_START,
	POWER_UP_END,
	POWER_DOWN_END,
	POWER_UP_START,
	NUM_UNIT,
};

struct msm_rpmh_master_data {
	char *master_name;
	enum master_smem_id smem_id;
@@ -66,16 +78,24 @@ static const struct msm_rpmh_master_data rpmh_masters[] = {
struct msm_rpmh_master_stats {
	uint32_t version_id;
	uint32_t counts;
	uint64_t last_entered_at;
	uint64_t last_exited_at;
	uint64_t last_entered;
	uint64_t last_exited;
	uint64_t accumulated_duration;
};

struct msm_rpmh_profile_unit {
	uint64_t value;
	uint64_t valid;
};

struct rpmh_master_stats_prv_data {
	struct kobj_attribute ka;
	struct kobject *kobj;
};

static struct msm_rpmh_master_stats apss_master_stats;
static void __iomem *rpmh_unit_base;

static DEFINE_MUTEX(rpmh_stats_mutex);

static ssize_t msm_rpmh_master_stats_print_data(char *prvbuf, ssize_t length,
@@ -88,7 +108,7 @@ static ssize_t msm_rpmh_master_stats_print_data(char *prvbuf, ssize_t length,
			"\tSleep Last Exited At:0x%llx\n"
			"\tSleep Accumulated Duration:0x%llx\n\n",
			name, record->version_id, record->counts,
			record->last_entered_at, record->last_exited_at,
			record->last_entered, record->last_exited,
			record->accumulated_duration);
}

@@ -100,13 +120,16 @@ static ssize_t msm_rpmh_master_stats_show(struct kobject *kobj,
	unsigned int size = 0;
	struct msm_rpmh_master_stats *record = NULL;

	/*
	 * Read SMEM data written by masters
	 */

	mutex_lock(&rpmh_stats_mutex);

	for (i = 0, length = 0; i < ARRAY_SIZE(rpmh_masters); i++) {
	/* First Read APSS master stats */

	length = msm_rpmh_master_stats_print_data(buf, PAGE_SIZE,
						&apss_master_stats, "APSS");

	/* Read SMEM data written by other masters */

	for (i = 0; i < ARRAY_SIZE(rpmh_masters); i++) {
		record = (struct msm_rpmh_master_stats *) smem_get_entry(
					rpmh_masters[i].smem_id, &size,
					rpmh_masters[i].pid, 0);
@@ -122,30 +145,66 @@ static ssize_t msm_rpmh_master_stats_show(struct kobject *kobj,
	return length;
}

static inline void msm_rpmh_apss_master_stats_update(
				struct msm_rpmh_profile_unit *profile_unit)
{
	apss_master_stats.counts++;
	apss_master_stats.last_entered = profile_unit[POWER_DOWN_END].value;
	apss_master_stats.last_exited = profile_unit[POWER_UP_START].value;
	apss_master_stats.accumulated_duration +=
					(apss_master_stats.last_exited
					- apss_master_stats.last_entered);
}

void msm_rpmh_master_stats_update(void)
{
	int i;
	struct msm_rpmh_profile_unit profile_unit[NUM_UNIT];

	if (!rpmh_unit_base)
		return;

	for (i = POWER_DOWN_END; i < NUM_UNIT; i++) {
		profile_unit[i].valid = readl_relaxed(rpmh_unit_base +
						GET_ADDR(REG_VALID, i));

		/*
		 * Do not update APSS stats if valid bit is not set.
		 * It means APSS did not execute cx-off sequence.
		 * This can be due to fall through at some point.
		 */

		if (!(profile_unit[i].valid & BIT(REG_VALID)))
			return;

		profile_unit[i].value = readl_relaxed(rpmh_unit_base +
						GET_ADDR(REG_DATA_LO, i));
		profile_unit[i].value |= ((uint64_t)
					readl_relaxed(rpmh_unit_base +
					GET_ADDR(REG_DATA_HI, i)) << 32);
	}
	msm_rpmh_apss_master_stats_update(profile_unit);
}
EXPORT_SYMBOL(msm_rpmh_master_stats_update);

static int msm_rpmh_master_stats_probe(struct platform_device *pdev)
{
	struct rpmh_master_stats_prv_data *prvdata = NULL;
	struct kobject *rpmh_master_stats_kobj = NULL;
	int ret = 0;
	int ret = -ENOMEM;

	if (!pdev)
		return -EINVAL;

	prvdata = kzalloc(sizeof(struct rpmh_master_stats_prv_data),
							GFP_KERNEL);
	if (!prvdata) {
		ret = -ENOMEM;
		goto fail;
	}
	prvdata = devm_kzalloc(&pdev->dev, sizeof(*prvdata), GFP_KERNEL);
	if (!prvdata)
		return ret;

	rpmh_master_stats_kobj = kobject_create_and_add(
					"rpmh_stats",
					power_kobj);
	if (!rpmh_master_stats_kobj) {
		ret = -ENOMEM;
		kfree(prvdata);
		goto fail;
	}
	if (!rpmh_master_stats_kobj)
		return ret;

	prvdata->kobj = rpmh_master_stats_kobj;

@@ -158,14 +217,24 @@ static int msm_rpmh_master_stats_probe(struct platform_device *pdev)
	ret = sysfs_create_file(prvdata->kobj, &prvdata->ka.attr);
	if (ret) {
		pr_err("sysfs_create_file failed\n");
		kobject_put(prvdata->kobj);
		kfree(prvdata);
		goto fail;
		goto fail_sysfs;
	}

	rpmh_unit_base = of_iomap(pdev->dev.of_node, 0);
	if (!rpmh_unit_base) {
		pr_err("Failed to get rpmh_unit_base\n");
		ret = -ENOMEM;
		goto fail_iomap;
	}

	apss_master_stats.version_id = 0x1;
	platform_set_drvdata(pdev, prvdata);
	return ret;

fail:
fail_iomap:
	sysfs_remove_file(prvdata->kobj, &prvdata->ka.attr);
fail_sysfs:
	kobject_put(prvdata->kobj);
	return ret;
}

@@ -181,14 +250,14 @@ static int msm_rpmh_master_stats_remove(struct platform_device *pdev)

	sysfs_remove_file(prvdata->kobj, &prvdata->ka.attr);
	kobject_put(prvdata->kobj);
	kfree(prvdata);
	platform_set_drvdata(pdev, NULL);
	iounmap(rpmh_unit_base);

	return 0;
}

static const struct of_device_id rpmh_master_table[] = {
	{.compatible = "qcom,rpmh-master-stats"},
	{.compatible = "qcom,rpmh-master-stats-v1"},
	{},
};

@@ -197,24 +266,11 @@ static struct platform_driver msm_rpmh_master_stats_driver = {
	.remove = msm_rpmh_master_stats_remove,
	.driver = {
		.name = "msm_rpmh_master_stats",
		.owner = THIS_MODULE,
		.of_match_table = rpmh_master_table,
	},
};

static int __init msm_rpmh_master_stats_init(void)
{
	return platform_driver_register(&msm_rpmh_master_stats_driver);
}

static void __exit msm_rpmh_master_stats_exit(void)
{
	platform_driver_unregister(&msm_rpmh_master_stats_driver);
}

module_init(msm_rpmh_master_stats_init);
module_exit(msm_rpmh_master_stats_exit);

module_platform_driver(msm_rpmh_master_stats_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MSM RPMH Master Statistics driver");
MODULE_ALIAS("platform:msm_rpmh_master_stat_log");
+23 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2018 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.
 *
 */

#if defined(CONFIG_QTI_RPMH_API) && defined(CONFIG_QTI_RPM_STATS_LOG)

void msm_rpmh_master_stats_update(void);

#else

static inline void msm_rpmh_master_stats_update(void) {}

#endif
+3 −1
Original line number Diff line number Diff line
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2018, 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
@@ -19,6 +19,7 @@
#include <soc/qcom/system_pm.h>

#include <clocksource/arm_arch_timer.h>
#include "rpmh_master_stat.h"

#define PDC_TIME_VALID_SHIFT	31
#define PDC_TIME_UPPER_MASK	0xFFFFFF
@@ -76,6 +77,7 @@ EXPORT_SYMBOL(system_sleep_enter);
 */
void system_sleep_exit(void)
{
	msm_rpmh_master_stats_update();
}
EXPORT_SYMBOL(system_sleep_exit);