dragonheart_kernel_oneplus_.../drivers/soc/qcom/rpmh_master_stat.c

306 lines
7.8 KiB
C

/* Copyright (c) 2017-2020, 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.
*
*/
#define pr_fmt(fmt) "%s: " fmt, KBUILD_MODNAME
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/uaccess.h>
#include <linux/soc/qcom/smem.h>
#include <asm/arch_timer.h>
#include <linux/kobject.h>
#include <linux/sysfs.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,
ADSP,
CDSP,
SLPI,
GPU,
DISPLAY,
};
enum master_pid {
PID_APSS = 0,
PID_MPSS = 1,
PID_ADSP = 2,
PID_SLPI = 3,
PID_CDSP = 5,
PID_GPU = PID_APSS,
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;
enum master_pid pid;
};
static const struct msm_rpmh_master_data rpmh_masters[] = {
{"MPSS", MPSS, PID_MPSS},
{"ADSP", ADSP, PID_ADSP},
{"CDSP", CDSP, PID_CDSP},
{"SLPI", SLPI, PID_SLPI},
{"GPU", GPU, PID_GPU},
{"DISPLAY", DISPLAY, PID_DISPLAY},
};
struct msm_rpmh_master_stats {
uint32_t version_id;
uint32_t counts;
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 void msm_rpmh_master_stats_print_data(struct seq_file *s,
struct msm_rpmh_master_stats *record,
const char *name)
{
uint64_t temp_accumulated_duration = record->accumulated_duration;
/*
* If a master 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 (record->last_entered > record->last_exited)
temp_accumulated_duration +=
(arch_counter_get_cntvct()
- record->last_entered);
seq_printf(s, "%s\n\tVersion:0x%x\n"
"\tSleep Count:0x%x\n"
"\tSleep Last Entered At:0x%llx\n"
"\tSleep Last Exited At:0x%llx\n"
"\tSleep Accumulated Duration:0x%llx\n\n",
name, record->version_id,
record->counts, record->last_entered,
record->last_exited, temp_accumulated_duration);
}
static int rpmh_master_stats_show(struct seq_file *s, void *data)
{
int i = 0;
size_t size = 0;
struct msm_rpmh_master_stats *record = NULL;
mutex_lock(&rpmh_stats_mutex);
/* First Read APSS master stats */
msm_rpmh_master_stats_print_data(s, &apss_master_stats,
"APSS");
/*
* Read SMEM data written by masters
*/
for (i = 0; i < ARRAY_SIZE(rpmh_masters); i++) {
record = (struct msm_rpmh_master_stats *) qcom_smem_get(
rpmh_masters[i].pid,
rpmh_masters[i].smem_id, &size);
if (!IS_ERR_OR_NULL(record))
msm_rpmh_master_stats_print_data(s, record,
rpmh_masters[i].master_name);
}
mutex_unlock(&rpmh_stats_mutex);
return 0;
}
int rpmh_master_stats_open(struct inode *inode, struct file *file)
{
return single_open(file, rpmh_master_stats_show, inode->i_private);
}
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);
ssize_t master_stats_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int i = 0;
size_t size = 0;
struct msm_rpmh_master_stats *record = NULL;
char stats_buf[1024];
uint64_t temp_accumulated_duration = 0;
mutex_lock(&rpmh_stats_mutex);
/* First Read APSS master stats */
temp_accumulated_duration = apss_master_stats.accumulated_duration;
if (apss_master_stats.last_entered > apss_master_stats.last_exited)
temp_accumulated_duration +=
(arch_counter_get_cntvct()
- apss_master_stats.last_entered);
snprintf(stats_buf, sizeof(stats_buf),
"APSS\n\tVersion:0x%x\n"
"\tSleep Count:0x%x\n"
"\tSleep Last Entered At:0x%llx\n"
"\tSleep Last Exited At:0x%llx\n"
"\tSleep Accumulated Duration:0x%llx\n\n",
apss_master_stats.version_id,
apss_master_stats.counts, apss_master_stats.last_entered,
apss_master_stats.last_exited, temp_accumulated_duration);
/*
* Read SMEM data written by masters
*/
for (i = 0; i < ARRAY_SIZE(rpmh_masters); i++) {
record = (struct msm_rpmh_master_stats *) qcom_smem_get(
rpmh_masters[i].pid,
rpmh_masters[i].smem_id, &size);
if (!IS_ERR_OR_NULL(record)) {
temp_accumulated_duration = record->accumulated_duration;
if (record->last_entered > record->last_exited)
temp_accumulated_duration +=
(arch_counter_get_cntvct()
- record->last_entered);
snprintf(stats_buf + strlen(stats_buf), sizeof(stats_buf),
"%s\n\tVersion:0x%x\n"
"\tSleep Count:0x%x\n"
"\tSleep Last Entered At:0x%llx\n"
"\tSleep Last Exited At:0x%llx\n"
"\tSleep Accumulated Duration:0x%llx\n\n",
rpmh_masters[i].master_name, record->version_id,
record->counts, record->last_entered,
record->last_exited, temp_accumulated_duration);
}
}
mutex_unlock(&rpmh_stats_mutex);
return snprintf(buf, sizeof(stats_buf), "%s", stats_buf);
}
static int msm_rpmh_master_stats_probe(struct platform_device *pdev)
{
rpmh_unit_base = of_iomap(pdev->dev.of_node, 0);
if (!rpmh_unit_base) {
pr_err("Failed to get rpmh_unit_base\n");
return -ENOMEM;
}
apss_master_stats.version_id = 0x1;
return 0;
}
static int msm_rpmh_master_stats_remove(struct platform_device *pdev)
{
iounmap(rpmh_unit_base);
return 0;
}
static const struct of_device_id rpmh_master_table[] = {
{.compatible = "qcom,rpmh-master-stats-v1"},
{},
};
static struct platform_driver msm_rpmh_master_stats_driver = {
.probe = msm_rpmh_master_stats_probe,
.remove = msm_rpmh_master_stats_remove,
.driver = {
.name = "msm_rpmh_master_stats",
.of_match_table = rpmh_master_table,
},
};
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");