kernel_samsung_a34x-permissive/drivers/soc/mediatek/mtk-pm-qos.c
2024-04-28 15:51:13 +02:00

969 lines
31 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/pm_qos.h>
#include <linux/soc/mediatek/mtk-pm-qos.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/export.h>
#define CREATE_TRACE_POINTS
#include <mtk-pm-qos-trace.h>
struct mtk_pm_qos_object {
struct pm_qos_constraints *constraints;
struct mutex qos_lock;
struct list_head req_list;
char *name;
};
static DEFINE_SPINLOCK(mtk_pm_qos_lock);
static struct mtk_pm_qos_object null_mtk_pm_qos;
static BLOCKING_NOTIFIER_HEAD(memory_bandwidth_notifier);
static struct pm_qos_constraints memory_bw_constraints = {
.list = PLIST_HEAD_INIT(memory_bw_constraints.list),
.target_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.type = PM_QOS_SUM,
.notifiers = &memory_bandwidth_notifier,
};
static struct mtk_pm_qos_object memory_bandwidth_pm_qos = {
.constraints = &memory_bw_constraints,
.req_list = LIST_HEAD_INIT(memory_bandwidth_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(memory_bandwidth_pm_qos.qos_lock),
.name = "memory_bandwidth",
};
static BLOCKING_NOTIFIER_HEAD(memory_ext_bandwidth_notifier);
static struct pm_qos_constraints memory_ext_bw_constraints = {
.list = PLIST_HEAD_INIT(memory_ext_bw_constraints.list),
.target_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.type = PM_QOS_SUM,
.notifiers = &memory_ext_bandwidth_notifier,
};
static struct mtk_pm_qos_object memory_ext_bandwidth_pm_qos = {
.constraints = &memory_ext_bw_constraints,
.req_list = LIST_HEAD_INIT(memory_ext_bandwidth_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(memory_ext_bandwidth_pm_qos.qos_lock),
.name = "memory_ext_bandwidth",
};
static BLOCKING_NOTIFIER_HEAD(ddr_opp_notifier);
static struct pm_qos_constraints ddr_opp_constraints = {
.list = PLIST_HEAD_INIT(ddr_opp_constraints.list),
.target_value = MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE,
.type = PM_QOS_MIN,
.notifiers = &ddr_opp_notifier,
};
static struct mtk_pm_qos_object ddr_opp_pm_qos = {
.constraints = &ddr_opp_constraints,
.req_list = LIST_HEAD_INIT(ddr_opp_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(ddr_opp_pm_qos.qos_lock),
.name = "ddr_opp",
};
static BLOCKING_NOTIFIER_HEAD(vcore_opp_notifier);
static struct pm_qos_constraints vcore_opp_constraints = {
.list = PLIST_HEAD_INIT(vcore_opp_constraints.list),
.target_value = MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE,
.type = PM_QOS_MIN,
.notifiers = &vcore_opp_notifier,
};
static struct mtk_pm_qos_object vcore_opp_pm_qos = {
.constraints = &vcore_opp_constraints,
.req_list = LIST_HEAD_INIT(vcore_opp_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(vcore_opp_pm_qos.qos_lock),
.name = "vcore_opp",
};
static BLOCKING_NOTIFIER_HEAD(scp_vcore_req_notifier);
static struct pm_qos_constraints scp_vcore_req_constraints = {
.list = PLIST_HEAD_INIT(scp_vcore_req_constraints.list),
.target_value = MTK_PM_QOS_SCP_VCORE_REQUEST_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_SCP_VCORE_REQUEST_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_SCP_VCORE_REQUEST_DEFAULT_VALUE,
.type = PM_QOS_MIN,
.notifiers = &scp_vcore_req_notifier,
};
static struct mtk_pm_qos_object scp_vcore_req_pm_qos = {
.constraints = &scp_vcore_req_constraints,
.req_list = LIST_HEAD_INIT(scp_vcore_req_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(scp_vcore_req_pm_qos.qos_lock),
.name = "scp_vcore_req",
};
static BLOCKING_NOTIFIER_HEAD(hrt_bandwidth_notifier);
static struct pm_qos_constraints hrt_bw_constraints = {
.list = PLIST_HEAD_INIT(hrt_bw_constraints.list),
.target_value = MTK_PM_QOS_HRT_BANDWIDTH_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_HRT_BANDWIDTH_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_HRT_BANDWIDTH_DEFAULT_VALUE,
.type = PM_QOS_SUM,
.notifiers = &hrt_bandwidth_notifier,
};
static struct mtk_pm_qos_object hrt_bandwidth_pm_qos = {
.constraints = &hrt_bw_constraints,
.req_list = LIST_HEAD_INIT(hrt_bandwidth_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(hrt_bandwidth_pm_qos.qos_lock),
.name = "hrt_bandwidth",
};
static BLOCKING_NOTIFIER_HEAD(disp_freq_notifier);
static struct pm_qos_constraints disp_freq_constraints = {
.list = PLIST_HEAD_INIT(disp_freq_constraints.list),
.target_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.default_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.no_constraint_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.type = PM_QOS_MAX,
.notifiers = &disp_freq_notifier,
};
static struct mtk_pm_qos_object disp_freq_pm_qos = {
.constraints = &disp_freq_constraints,
.req_list = LIST_HEAD_INIT(disp_freq_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(disp_freq_pm_qos.qos_lock),
.name = "disp_freq",
};
static BLOCKING_NOTIFIER_HEAD(mdp_freq_notifier);
static struct pm_qos_constraints mdp_freq_constraints = {
.list = PLIST_HEAD_INIT(mdp_freq_constraints.list),
.target_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.default_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.no_constraint_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.type = PM_QOS_MAX,
.notifiers = &mdp_freq_notifier,
};
static struct mtk_pm_qos_object mdp_freq_pm_qos = {
.constraints = &mdp_freq_constraints,
.req_list = LIST_HEAD_INIT(mdp_freq_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(mdp_freq_pm_qos.qos_lock),
.name = "mdp_freq",
};
static BLOCKING_NOTIFIER_HEAD(vdec_freq_notifier);
static struct pm_qos_constraints vdec_freq_constraints = {
.list = PLIST_HEAD_INIT(vdec_freq_constraints.list),
.target_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.default_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.no_constraint_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.type = PM_QOS_MAX,
.notifiers = &vdec_freq_notifier,
};
static struct mtk_pm_qos_object vdec_freq_pm_qos = {
.constraints = &vdec_freq_constraints,
.req_list = LIST_HEAD_INIT(vdec_freq_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(vdec_freq_pm_qos.qos_lock),
.name = "vdec_freq",
};
static BLOCKING_NOTIFIER_HEAD(venc_freq_notifier);
static struct pm_qos_constraints venc_freq_constraints = {
.list = PLIST_HEAD_INIT(venc_freq_constraints.list),
.target_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.default_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.no_constraint_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.type = PM_QOS_MAX,
.notifiers = &venc_freq_notifier,
};
static struct mtk_pm_qos_object venc_freq_pm_qos = {
.constraints = &venc_freq_constraints,
.req_list = LIST_HEAD_INIT(venc_freq_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(venc_freq_pm_qos.qos_lock),
.name = "venc_freq",
};
static BLOCKING_NOTIFIER_HEAD(img_freq_notifier);
static struct pm_qos_constraints img_freq_constraints = {
.list = PLIST_HEAD_INIT(img_freq_constraints.list),
.target_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.default_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.no_constraint_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.type = PM_QOS_MAX,
.notifiers = &img_freq_notifier,
};
static struct mtk_pm_qos_object img_freq_pm_qos = {
.constraints = &img_freq_constraints,
.req_list = LIST_HEAD_INIT(img_freq_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(img_freq_pm_qos.qos_lock),
.name = "img_freq",
};
static BLOCKING_NOTIFIER_HEAD(cam_freq_notifier);
static struct pm_qos_constraints cam_freq_constraints = {
.list = PLIST_HEAD_INIT(cam_freq_constraints.list),
.target_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.default_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.no_constraint_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.type = PM_QOS_MAX,
.notifiers = &cam_freq_notifier,
};
static struct mtk_pm_qos_object cam_freq_pm_qos = {
.constraints = &cam_freq_constraints,
.req_list = LIST_HEAD_INIT(cam_freq_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(cam_freq_pm_qos.qos_lock),
.name = "cam_freq",
};
static BLOCKING_NOTIFIER_HEAD(dpe_freq_notifier);
static struct pm_qos_constraints dpe_freq_constraints = {
.list = PLIST_HEAD_INIT(dpe_freq_constraints.list),
.target_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.default_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.no_constraint_value = PM_QOS_MM_FREQ_DEFAULT_VALUE,
.type = PM_QOS_MAX,
.notifiers = &dpe_freq_notifier,
};
static struct mtk_pm_qos_object dpe_freq_pm_qos = {
.constraints = &dpe_freq_constraints,
.req_list = LIST_HEAD_INIT(dpe_freq_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(dpe_freq_pm_qos.qos_lock),
.name = "dpe_freq",
};
static BLOCKING_NOTIFIER_HEAD(mm0_bandwidth_limiter_notifier);
static struct pm_qos_constraints mm0_bw_limiter_constraints = {
.list = PLIST_HEAD_INIT(mm0_bw_limiter_constraints.list),
.target_value = PM_QOS_MM_BANDWIDTH_LIMITER_DEFAULT_VALUE,
.default_value = PM_QOS_MM_BANDWIDTH_LIMITER_DEFAULT_VALUE,
.no_constraint_value = PM_QOS_MM_BANDWIDTH_LIMITER_DEFAULT_VALUE,
.type = PM_QOS_SUM,
.notifiers = &mm0_bandwidth_limiter_notifier,
};
static struct mtk_pm_qos_object mm0_bandwidth_limiter = {
.constraints = &mm0_bw_limiter_constraints,
.req_list = LIST_HEAD_INIT(mm0_bandwidth_limiter.req_list),
.qos_lock = __MUTEX_INITIALIZER(mm0_bandwidth_limiter.qos_lock),
.name = "mm0_bandwidth_limiter",
};
static BLOCKING_NOTIFIER_HEAD(mm1_bandwidth_limiter_notifier);
static struct pm_qos_constraints mm1_bw_limiter_constraints = {
.target_value = PM_QOS_MM_BANDWIDTH_LIMITER_DEFAULT_VALUE,
.default_value = PM_QOS_MM_BANDWIDTH_LIMITER_DEFAULT_VALUE,
.no_constraint_value = PM_QOS_MM_BANDWIDTH_LIMITER_DEFAULT_VALUE,
.type = PM_QOS_SUM,
.notifiers = &mm1_bandwidth_limiter_notifier,
};
static struct mtk_pm_qos_object mm1_bandwidth_limiter = {
.constraints = &mm1_bw_limiter_constraints,
.req_list = LIST_HEAD_INIT(mm1_bandwidth_limiter.req_list),
.qos_lock = __MUTEX_INITIALIZER(mm1_bandwidth_limiter.qos_lock),
.name = "mm1_bandwidth_limiter",
};
static BLOCKING_NOTIFIER_HEAD(vvpu_opp_notifier);
static struct pm_qos_constraints vvpu_opp_constraints = {
.list = PLIST_HEAD_INIT(vvpu_opp_constraints.list),
.target_value = MTK_PM_QOS_VVPU_OPP_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_VVPU_OPP_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_VVPU_OPP_DEFAULT_VALUE,
.type = PM_QOS_MIN,
.notifiers = &vvpu_opp_notifier,
};
static struct mtk_pm_qos_object vvpu_opp_pm_qos = {
.constraints = &vvpu_opp_constraints,
.req_list = LIST_HEAD_INIT(vvpu_opp_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(vvpu_opp_pm_qos.qos_lock),
.name = "vvpu_opp",
};
static BLOCKING_NOTIFIER_HEAD(vmdla_opp_notifier);
static struct pm_qos_constraints vmdla_opp_constraints = {
.list = PLIST_HEAD_INIT(vmdla_opp_constraints.list),
.target_value = MTK_PM_QOS_VMDLA_OPP_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_VMDLA_OPP_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_VMDLA_OPP_DEFAULT_VALUE,
.type = PM_QOS_MIN,
.notifiers = &vmdla_opp_notifier,
};
static struct mtk_pm_qos_object vmdla_opp_pm_qos = {
.constraints = &vmdla_opp_constraints,
.req_list = LIST_HEAD_INIT(vmdla_opp_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(vmdla_opp_pm_qos.qos_lock),
.name = "vmdla_opp",
};
static BLOCKING_NOTIFIER_HEAD(cpu_memory_bandwidth_notifier);
static struct pm_qos_constraints cpu_memory_bw_constraints = {
.list = PLIST_HEAD_INIT(cpu_memory_bw_constraints.list),
.target_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.type = PM_QOS_SUM,
.notifiers = &cpu_memory_bandwidth_notifier,
};
static struct mtk_pm_qos_object cpu_memory_bandwidth_pm_qos = {
.constraints = &cpu_memory_bw_constraints,
.req_list = LIST_HEAD_INIT(cpu_memory_bandwidth_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(cpu_memory_bandwidth_pm_qos.qos_lock),
.name = "cpu_memory_bandwidth",
};
static BLOCKING_NOTIFIER_HEAD(gpu_memory_bandwidth_notifier);
static struct pm_qos_constraints gpu_memory_bw_constraints = {
.list = PLIST_HEAD_INIT(gpu_memory_bw_constraints.list),
.target_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.type = PM_QOS_SUM,
.notifiers = &gpu_memory_bandwidth_notifier,
};
static struct mtk_pm_qos_object gpu_memory_bandwidth_pm_qos = {
.constraints = &gpu_memory_bw_constraints,
.req_list = LIST_HEAD_INIT(gpu_memory_bandwidth_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(gpu_memory_bandwidth_pm_qos.qos_lock),
.name = "gpu_memory_bandwidth",
};
static BLOCKING_NOTIFIER_HEAD(other_memory_bandwidth_notifier);
static struct pm_qos_constraints other_memory_bw_constraints = {
.list = PLIST_HEAD_INIT(other_memory_bw_constraints.list),
.target_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.type = PM_QOS_SUM,
.notifiers = &other_memory_bandwidth_notifier,
};
static struct mtk_pm_qos_object other_memory_bandwidth_pm_qos = {
.constraints = &other_memory_bw_constraints,
.req_list = LIST_HEAD_INIT(other_memory_bandwidth_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(other_memory_bandwidth_pm_qos.qos_lock),
.name = "other_memory_bandwidth",
};
static BLOCKING_NOTIFIER_HEAD(power_model_ddr_req_notifier);
static struct pm_qos_constraints power_model_ddr_req_constraints = {
.list = PLIST_HEAD_INIT(power_model_ddr_req_constraints.list),
.target_value = MTK_PM_QOS_POWER_MODEL_DDR_REQUEST_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_POWER_MODEL_DDR_REQUEST_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_POWER_MODEL_DDR_REQUEST_DEFAULT_VALUE,
.type = PM_QOS_SUM,
.notifiers = &power_model_ddr_req_notifier,
};
static struct mtk_pm_qos_object power_model_ddr_req_pm_qos = {
.constraints = &power_model_ddr_req_constraints,
.req_list = LIST_HEAD_INIT(power_model_ddr_req_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(power_model_ddr_req_pm_qos.qos_lock),
.name = "power_model_ddr_req",
};
static BLOCKING_NOTIFIER_HEAD(power_model_vcore_req_notifier);
static struct pm_qos_constraints power_model_vcore_req_constraints = {
.list = PLIST_HEAD_INIT(power_model_vcore_req_constraints.list),
.target_value = MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST_DEFAULT_VALUE,
.type = PM_QOS_SUM,
.notifiers = &power_model_vcore_req_notifier,
};
static struct mtk_pm_qos_object power_model_vcore_req_pm_qos = {
.constraints = &power_model_vcore_req_constraints,
.req_list = LIST_HEAD_INIT(power_model_vcore_req_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(power_model_vcore_req_pm_qos.qos_lock),
.name = "power_model_vcore_req",
};
static BLOCKING_NOTIFIER_HEAD(vcore_dvfs_force_opp_notifier);
static struct pm_qos_constraints vcore_dvfs_force_opp_constraints = {
.list = PLIST_HEAD_INIT(vcore_dvfs_force_opp_constraints.list),
.target_value = MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE,
.type = PM_QOS_SUM,
.notifiers = &vcore_dvfs_force_opp_notifier,
};
static struct mtk_pm_qos_object vcore_dvfs_force_opp_pm_qos = {
.constraints = &vcore_dvfs_force_opp_constraints,
.req_list = LIST_HEAD_INIT(vcore_dvfs_force_opp_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(vcore_dvfs_force_opp_pm_qos.qos_lock),
.name = "vcore_dvfs_force_opp",
};
static BLOCKING_NOTIFIER_HEAD(memory_bandwidth_test_notifier);
static struct pm_qos_constraints memory_bw_test_constraints = {
.list = PLIST_HEAD_INIT(memory_bw_test_constraints.list),
.target_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.default_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.no_constraint_value = MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
.type = PM_QOS_SUM,
.notifiers = &memory_bandwidth_test_notifier,
};
static struct mtk_pm_qos_object memory_bandwidth_test_pm_qos = {
.constraints = &memory_bw_test_constraints,
.req_list = LIST_HEAD_INIT(memory_bandwidth_test_pm_qos.req_list),
.qos_lock = __MUTEX_INITIALIZER(memory_bandwidth_test_pm_qos.qos_lock),
.name = "memory_bandwidth_tst",
};
static struct mtk_pm_qos_object *mtk_pm_qos_array[] = {
[MTK_PM_QOS_RESERVED] = &null_mtk_pm_qos,
[MTK_PM_QOS_MEMORY_BANDWIDTH] = &memory_bandwidth_pm_qos,
[MTK_PM_QOS_MEMORY_EXT_BANDWIDTH] = &memory_ext_bandwidth_pm_qos,
[MTK_PM_QOS_HRT_BANDWIDTH] = &hrt_bandwidth_pm_qos,
[MTK_PM_QOS_DDR_OPP] = &ddr_opp_pm_qos,
[MTK_PM_QOS_VCORE_OPP] = &vcore_opp_pm_qos,
[MTK_PM_QOS_SCP_VCORE_REQUEST] = &scp_vcore_req_pm_qos,
[MTK_PM_QOS_VVPU_OPP] = &vvpu_opp_pm_qos,
[MTK_PM_QOS_VMDLA_OPP] = &vmdla_opp_pm_qos,
[PM_QOS_DISP_FREQ] = &disp_freq_pm_qos,
[PM_QOS_MDP_FREQ] = &mdp_freq_pm_qos,
[PM_QOS_VDEC_FREQ] = &vdec_freq_pm_qos,
[PM_QOS_VENC_FREQ] = &venc_freq_pm_qos,
[PM_QOS_IMG_FREQ] = &img_freq_pm_qos,
[PM_QOS_CAM_FREQ] = &cam_freq_pm_qos,
[PM_QOS_DPE_FREQ] = &dpe_freq_pm_qos,
[PM_QOS_MM0_BANDWIDTH_LIMITER] = &mm0_bandwidth_limiter,
[PM_QOS_MM1_BANDWIDTH_LIMITER] = &mm1_bandwidth_limiter,
[MTK_PM_QOS_CPU_MEMORY_BANDWIDTH] = &cpu_memory_bandwidth_pm_qos,
[MTK_PM_QOS_GPU_MEMORY_BANDWIDTH] = &gpu_memory_bandwidth_pm_qos,
[MTK_PM_QOS_OTHER_MEMORY_BANDWIDTH] = &other_memory_bandwidth_pm_qos,
[MTK_PM_QOS_POWER_MODEL_DDR_REQUEST] = &power_model_ddr_req_pm_qos,
[MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST] = &power_model_vcore_req_pm_qos,
[MTK_PM_QOS_VCORE_DVFS_FORCE_OPP] = &vcore_dvfs_force_opp_pm_qos,
[MTK_PM_QOS_MEMORY_BANDWIDTH_TEST] = &memory_bandwidth_test_pm_qos,
};
/* unlocked internal variant */
static inline int mtk_pm_qos_get_value(struct pm_qos_constraints *c)
{
struct plist_node *node;
int total_value = 0;
if (plist_head_empty(&c->list))
return c->no_constraint_value;
switch (c->type) {
case PM_QOS_MIN:
return plist_first(&c->list)->prio;
case PM_QOS_MAX:
return plist_last(&c->list)->prio;
case PM_QOS_SUM:
plist_for_each(node, &c->list)
total_value += node->prio;
return total_value;
default:
/* runtime check for not using enum */
WARN_ON(1);
return PM_QOS_DEFAULT_VALUE;
}
}
void mtk_pm_qos_trace_dbg_show_request(int pm_qos_class)
{
struct pm_qos_constraints *c;
struct mtk_pm_qos_request *req;
unsigned long flags;
if (pm_qos_class < MTK_PM_QOS_RESERVED
|| pm_qos_class >= MTK_PM_QOS_NUM_CLASSES)
return;
c = mtk_pm_qos_array[pm_qos_class]->constraints;
spin_lock_irqsave(&mtk_pm_qos_lock, flags);
plist_for_each_entry(req, &c->list, node) {
trace_mtk_pm_qos_update_request(req->pm_qos_class,
req->node.prio, req->owner);
}
spin_unlock_irqrestore(&mtk_pm_qos_lock, flags);
}
EXPORT_SYMBOL_GPL(mtk_pm_qos_trace_dbg_show_request);
static int mtk_pm_qos_dbg_show_requests(struct seq_file *s, void *unused)
{
struct mtk_pm_qos_object *qos = (struct mtk_pm_qos_object *)s->private;
struct pm_qos_constraints *c;
struct mtk_pm_qos_request *req;
char *type;
unsigned long flags;
int tot_reqs = 0;
int active_reqs = 0;
if (IS_ERR_OR_NULL(qos)) {
pr_err("%s: bad qos param!\n", __func__);
return -EINVAL;
}
c = qos->constraints;
if (IS_ERR_OR_NULL(c)) {
pr_err("%s: Bad constraints on qos?\n", __func__);
return -EINVAL;
}
/* Lock to ensure we have a snapshot */
spin_lock_irqsave(&mtk_pm_qos_lock, flags);
if (plist_head_empty(&c->list)) {
seq_puts(s, "Empty!\n");
goto out;
}
switch (c->type) {
case PM_QOS_MIN:
type = "Minimum";
break;
case PM_QOS_MAX:
type = "Maximum";
break;
case PM_QOS_SUM:
type = "Sum";
break;
default:
type = "Unknown";
}
plist_for_each_entry(req, &c->list, node) {
char *state = "Default";
if ((req->node).prio != c->default_value) {
active_reqs++;
state = "Active";
}
tot_reqs++;
seq_printf(s, "[%s] : %d: %s\n", req->owner,
(req->node).prio, state);
}
seq_printf(s, "Type=%s, Value=%d, Requests: active=%d / total=%d\n",
type, mtk_pm_qos_get_value(c), active_reqs, tot_reqs);
out:
spin_unlock_irqrestore(&mtk_pm_qos_lock, flags);
return 0;
}
static int mtk_pm_qos_dbg_open(struct inode *inode, struct file *file)
{
return single_open(file, mtk_pm_qos_dbg_show_requests,
PDE_DATA(inode));
}
static const struct file_operations mtk_pm_qos_proc_fops = {
.owner = THIS_MODULE,
.open = mtk_pm_qos_dbg_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* plist_add - add @node to @head
*
* @node: &struct plist_node pointer
* @head: &struct plist_head pointer
*/
static void mtk_pm_qos_plist_add(struct plist_node *node,
struct plist_head *head)
{
struct plist_node *first, *iter, *prev = NULL;
struct list_head *node_next = &head->node_list;
WARN_ON(!plist_node_empty(node));
WARN_ON(!list_empty(&node->prio_list));
if (plist_head_empty(head))
goto ins_node;
first = iter = plist_first(head);
do {
if (node->prio < iter->prio) {
node_next = &iter->node_list;
break;
}
prev = iter;
iter = list_entry(iter->prio_list.next,
struct plist_node, prio_list);
} while (iter != first);
if (!prev || prev->prio != node->prio)
list_add_tail(&node->prio_list, &iter->prio_list);
ins_node:
list_add_tail(&node->node_list, node_next);
}
/**
* plist_del - Remove a @node from plist.
*
* @node: &struct plist_node pointer - entry to be removed
* @head: &struct plist_head pointer - list head
*/
static void mtk_pm_qos_plist_del(struct plist_node *node,
struct plist_head *head)
{
if (!list_empty(&node->prio_list)) {
if (node->node_list.next != &head->node_list) {
struct plist_node *next;
next = list_entry(node->node_list.next,
struct plist_node, node_list);
/* add the next plist_node into prio_list */
if (list_empty(&next->prio_list))
list_add(&next->prio_list, &node->prio_list);
}
list_del_init(&node->prio_list);
}
list_del_init(&node->node_list);
}
int mtk_pm_qos_request_active(struct mtk_pm_qos_request *req)
{
return req->pm_qos_class != 0;
}
EXPORT_SYMBOL_GPL(mtk_pm_qos_request_active);
static void mtk_pm_qos_update_target_req_list(struct mtk_pm_qos_object *c,
struct mtk_pm_qos_request *req, enum pm_qos_req_action action)
{
unsigned long flags;
spin_lock_irqsave(&mtk_pm_qos_lock, flags);
switch (action) {
case PM_QOS_REMOVE_REQ:
list_del(&req->list_node);
break;
case PM_QOS_UPDATE_REQ:
/*
* to change the list, we atomically remove, reinit
* with new value and add, then see if the extremal
* changed
*/
list_del(&req->list_node);
case PM_QOS_ADD_REQ:
list_add(&req->list_node, &c->req_list);
break;
default:
/* no action */
break;
}
spin_unlock_irqrestore(&mtk_pm_qos_lock, flags);
}
static inline void mtk_pm_qos_set_value(struct pm_qos_constraints *c, s32 value)
{
c->target_value = value;
}
/**
* mtk_pm_qos_request - returns current system wide qos expectation
* @pm_qos_class: identification of which qos value is requested
*
* This function returns the current target value.
*/
int mtk_pm_qos_request(unsigned int pm_qos_class)
{
if (pm_qos_class >= MTK_PM_QOS_NUM_CLASSES) {
pr_err("%s: unknown class =%d\n", __func__, pm_qos_class);
return PM_QOS_DEFAULT_VALUE;
}
return mtk_pm_qos_array[pm_qos_class]->constraints->target_value;
}
EXPORT_SYMBOL_GPL(mtk_pm_qos_request);
/**
* pm_qos_update_target - manages the constraints list and calls the notifiers
* if needed
* @c: constraints data struct
* @node: request to add to the list, to update or to remove
* @action: action to take on the constraints list
* @value: value of the request to add or update
*
* This function returns 1 if the aggregated constraint value has changed, 0
* otherwise.
*/
static int __mtk_pm_qos_update_target(struct pm_qos_constraints *c,
struct plist_node *node, enum pm_qos_req_action action, int value)
{
unsigned long flags;
int prev_value, curr_value, new_value;
int ret;
spin_lock_irqsave(&mtk_pm_qos_lock, flags);
prev_value = mtk_pm_qos_get_value(c);
if (value == PM_QOS_DEFAULT_VALUE)
new_value = c->default_value;
else
new_value = value;
switch (action) {
case PM_QOS_REMOVE_REQ:
mtk_pm_qos_plist_del(node, &c->list);
break;
case PM_QOS_UPDATE_REQ:
/*
* to change the list, we atomically remove, reinit
* with new value and add, then see if the extremal
* changed
*/
mtk_pm_qos_plist_del(node, &c->list);
/* fall through */
case PM_QOS_ADD_REQ:
plist_node_init(node, new_value);
mtk_pm_qos_plist_add(node, &c->list);
break;
default:
/* no action */
break;
}
curr_value = mtk_pm_qos_get_value(c);
mtk_pm_qos_set_value(c, curr_value);
spin_unlock_irqrestore(&mtk_pm_qos_lock, flags);
if (prev_value != curr_value) {
ret = 1;
if (c->notifiers)
blocking_notifier_call_chain(c->notifiers,
(unsigned long)curr_value,
NULL);
} else {
ret = 0;
}
return ret;
}
static void mtk_pm_qos_update_target(struct mtk_pm_qos_object *obj,
struct plist_node *node, enum pm_qos_req_action action, int value)
{
mutex_lock(&obj->qos_lock);
__mtk_pm_qos_update_target(obj->constraints, node, action, value);
mutex_unlock(&obj->qos_lock);
}
/**
* mtk_pm_qos_add_request - inserts new qos request into the list
* @req: pointer to a preallocated handle
* @pm_qos_class: identifies which list of qos request to use
* @value: defines the qos request
*
* This function inserts a new entry in the pm_qos_class list of requested qos
* performance characteristics. It recomputes the aggregate QoS expectations
* for the pm_qos_class of parameters and initializes the pm_qos_request
* handle. Caller needs to save this handle for later use in updates and
* removal.
*/
void mtk_pm_qos_add_request(struct mtk_pm_qos_request *req,
unsigned int pm_qos_class, s32 value)
{
char owner[20];
int n;
if (!req) /*guard against callers passing in null */
return;
if (pm_qos_class >= MTK_PM_QOS_NUM_CLASSES) {
pr_err("%s: unknown class =%d\n", __func__, pm_qos_class);
return;
}
n = snprintf(owner, sizeof(owner) - 1, "%pS",
__builtin_return_address(0));
if (n < 0)
strncpy(owner, "unknown", sizeof(owner) - 1);
if (mtk_pm_qos_request_active(req)) {
pr_err("%s: called for already added request\n", __func__);
return;
}
/* name of mtk_pm_qos reqester */
strncpy(req->owner, owner, sizeof(req->owner) - 1);
req->pm_qos_class = pm_qos_class;
trace_mtk_pm_qos_add_request(pm_qos_class, value, req->owner);
mtk_pm_qos_update_target(
mtk_pm_qos_array[pm_qos_class],
&req->node, PM_QOS_ADD_REQ, value);
mtk_pm_qos_update_target_req_list(
mtk_pm_qos_array[req->pm_qos_class],
req, PM_QOS_ADD_REQ);
}
EXPORT_SYMBOL_GPL(mtk_pm_qos_add_request);
/**
* mtk_pm_qos_update_request - modifies an existing qos request
* @req : handle to list element holding a mtk_pm_qos request to use
* @value: defines the qos request
*
* Updates an existing qos request for the pm_qos_class of parameters along
* with updating the target pm_qos_class value.
*
* Attempts are made to make this code callable on hot code paths.
*/
void mtk_pm_qos_update_request(struct mtk_pm_qos_request *req,
s32 new_value)
{
if (!req) /*guard against callers passing in null */
return;
if (!mtk_pm_qos_request_active(req)) {
pr_err("%s: called for unknown object\n", __func__);
return;
}
trace_mtk_pm_qos_update_request(req->pm_qos_class,
new_value, req->owner);
if (new_value != req->node.prio) {
mtk_pm_qos_update_target(
mtk_pm_qos_array[req->pm_qos_class],
&req->node, PM_QOS_UPDATE_REQ, new_value);
mtk_pm_qos_update_target_req_list(
mtk_pm_qos_array[req->pm_qos_class],
req, PM_QOS_UPDATE_REQ);
}
}
EXPORT_SYMBOL_GPL(mtk_pm_qos_update_request);
/**
* mtk_pm_qos_remove_request - modifies an existing qos request
* @req: handle to request list element
*
* Will remove pm qos request from the list of constraints and
* recompute the current target value for the pm_qos_class. Call this
* on slow code paths.
*/
void mtk_pm_qos_remove_request(struct mtk_pm_qos_request *req)
{
if (!req) /*guard against callers passing in null */
return;
/* silent return to keep pcm code cleaner */
if (!mtk_pm_qos_request_active(req)) {
pr_err("%s: called for unknown object\n", __func__);
return;
}
trace_mtk_pm_qos_remove_request(req->pm_qos_class, PM_QOS_DEFAULT_VALUE,
req->owner);
mtk_pm_qos_update_target(
mtk_pm_qos_array[req->pm_qos_class],
&req->node, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
mtk_pm_qos_update_target_req_list(
mtk_pm_qos_array[req->pm_qos_class],
req, PM_QOS_REMOVE_REQ);
memset(req, 0, sizeof(*req));
}
EXPORT_SYMBOL_GPL(mtk_pm_qos_remove_request);
/**
* mtk_pm_qos_add_notifier - sets notification entry for changes to target value
* @pm_qos_class: identifies which qos target changes should be notified.
* @notifier: notifier block managed by caller.
*
* will register the notifier into a notification chain that gets called
* upon changes to the pm_qos_class target value.
*/
int mtk_pm_qos_add_notifier(unsigned int pm_qos_class,
struct notifier_block *notifier)
{
int retval;
if (pm_qos_class >= MTK_PM_QOS_NUM_CLASSES) {
pr_err("%s: unknown class =%d\n", __func__, pm_qos_class);
return -EINVAL;
}
retval = blocking_notifier_chain_register(
mtk_pm_qos_array[pm_qos_class]->constraints->notifiers,
notifier);
return retval;
}
EXPORT_SYMBOL_GPL(mtk_pm_qos_add_notifier);
/**
* mtk_pm_qos_remove_notifier - deletes notification entry from chain.
* @pm_qos_class: identifies which qos target changes are notified.
* @notifier: notifier block to be removed.
*
* will remove the notifier from the notification chain that gets called
* upon changes to the pm_qos_class target value.
*/
int mtk_pm_qos_remove_notifier(unsigned int pm_qos_class,
struct notifier_block *notifier)
{
int retval;
if (pm_qos_class >= MTK_PM_QOS_NUM_CLASSES) {
pr_err("%s: unknown class =%d\n", __func__, pm_qos_class);
return -EINVAL;
}
retval = blocking_notifier_chain_unregister(
mtk_pm_qos_array[pm_qos_class]->constraints->notifiers,
notifier);
return retval;
}
EXPORT_SYMBOL_GPL(mtk_pm_qos_remove_notifier);
/* User space interface to PM QoS classes via misc devices */
static int register_mtk_pm_qos_procfs(struct mtk_pm_qos_object *qos,
struct proc_dir_entry *d)
{
proc_create_data(qos->name, 0444, d,
&mtk_pm_qos_proc_fops, (void *)qos);
return 0;
}
static int __init mtk_pm_qos_power_init(void)
{
int i;
struct proc_dir_entry *proc_root = NULL;
proc_root = proc_mkdir("mtk_pm_qos", NULL);
if (!proc_root)
return -1;
for (i = MTK_PM_QOS_MEMORY_BANDWIDTH; i < MTK_PM_QOS_NUM_CLASSES; i++)
register_mtk_pm_qos_procfs(mtk_pm_qos_array[i], proc_root);
return 0;
}
late_initcall(mtk_pm_qos_power_init);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MediaTek PMQoS driver");