2002 lines
49 KiB
C
2002 lines
49 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (c) 2019 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/cpu.h>
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/string.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/cpumask.h>
|
|
#include <linux/tick.h>
|
|
|
|
#include <linux/seq_file.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/kallsyms.h>
|
|
|
|
//#include <linux/irqchip/mtk-gic.h>
|
|
#include <linux/irqchip/mtk-gic-extend.h>
|
|
#include <asm/system_misc.h>
|
|
#include <mt-plat/sync_write.h>
|
|
#include <mtk_gpt.h>
|
|
#include <mtk_spm.h>
|
|
#include <mtk_spm_dpidle.h>
|
|
#include <mtk_spm_idle.h>
|
|
#ifdef CONFIG_THERMAL
|
|
#include <mach/mtk_thermal.h>
|
|
#endif
|
|
#include <mtk_idle.h>
|
|
#include <mtk_idle_internal.h>
|
|
#include <mtk_idle_profile.h>
|
|
#include <mtk_spm_reg.h>
|
|
#include <mtk_spm_misc.h>
|
|
#include <mtk_spm_resource_req.h>
|
|
#include <mtk_spm_resource_req_internal.h>
|
|
|
|
#ifdef CONFIG_MTK_DCS
|
|
#include <mt-plat/mtk_meminfo.h>
|
|
#endif
|
|
|
|
#include <linux/uaccess.h>
|
|
|
|
#if defined(CONFIG_MTK_PMIC) || defined(CONFIG_MTK_PMIC_NEW_ARCH)
|
|
#include <mt-plat/upmu_common.h>
|
|
#include <include/pmic.h>
|
|
#endif
|
|
|
|
#if !defined(SPM_K414_EARLY_PORTING)
|
|
#include "mtk_mcdi_governor.h"
|
|
#endif
|
|
|
|
#include <mt-plat/mtk_boot.h>
|
|
|
|
#include <mtk_lp_sysfs.h>
|
|
#include <mtk_lp_kernfs.h>
|
|
#include "mtk_idle_sysfs.h"
|
|
|
|
#define IDLE_GPT GPT4
|
|
|
|
#define IDLE_TAG "[name:spm&]Power/swap "
|
|
#define idle_err(fmt, args...) printk_deferred(IDLE_TAG fmt, ##args)
|
|
#define idle_warn(fmt, args...) printk_deferred(IDLE_TAG fmt, ##args)
|
|
#define idle_info(fmt, args...) printk_deferred(IDLE_TAG fmt, ##args)
|
|
#define idle_ver(fmt, args...) printk_deferred(IDLE_TAG fmt, ##args)
|
|
#define idle_dbg(fmt, args...) printk_deferred(IDLE_TAG fmt, ##args)
|
|
|
|
#define log2buf(p, s, fmt, args...) \
|
|
(p += scnprintf(p, sizeof(s) - strlen(s), fmt, ##args))
|
|
|
|
#define USING_STD_TIMER_OPS
|
|
|
|
void go_to_wfi(void)
|
|
{
|
|
isb();
|
|
mb(); /* memory barrier */
|
|
__asm__ __volatile__("wfi" : : : "memory");
|
|
}
|
|
|
|
#if 1
|
|
void __attribute__((weak)) mtkTTimer_start_timer(void)
|
|
{
|
|
|
|
}
|
|
|
|
void __attribute__((weak)) mtkTTimer_cancel_timer(void)
|
|
{
|
|
|
|
}
|
|
|
|
bool __attribute__((weak)) spm_get_sodi3_en(void)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool __attribute__((weak)) spm_get_sodi_en(void)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int __attribute__((weak)) hps_del_timer(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int __attribute__((weak)) hps_restart_timer(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
unsigned int __attribute__((weak))
|
|
spm_go_to_dpidle(u32 spm_flags, u32 spm_data,
|
|
u32 log_cond, u32 operation_cond)
|
|
{
|
|
go_to_wfi();
|
|
|
|
return WR_NONE;
|
|
}
|
|
|
|
unsigned int __attribute__((weak))
|
|
spm_go_to_sodi3(u32 spm_flags, u32 spm_data, u32 sodi_flags)
|
|
{
|
|
go_to_wfi();
|
|
|
|
return WR_NONE;
|
|
}
|
|
|
|
unsigned int __attribute__((weak))
|
|
spm_go_to_sodi(u32 spm_flags, u32 spm_data, u32 sodi_flags)
|
|
{
|
|
go_to_wfi();
|
|
|
|
return WR_NONE;
|
|
}
|
|
|
|
int __attribute__((weak)) is_teei_ready(void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
unsigned long __attribute__((weak)) localtimer_get_counter(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int __attribute__((weak)) localtimer_set_next_event(unsigned long evt)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
uint32_t __attribute__((weak)) clk_buf_bblpm_enter_cond(void)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
void __attribute__((weak)) idle_refcnt_inc(void)
|
|
{
|
|
}
|
|
|
|
void __attribute__((weak)) idle_refcnt_dec(void)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
#define IDLE_VCORE_CHECK_FOR_LP_MODE 0
|
|
#define IDLE_VCORE_FORCE_LP_MODE 1
|
|
#define IDLE_VCORE_FORCE_NORMAL_MODE 2
|
|
#define IDLE_VCORE_BYPASS_CHECK_FOR_LP_MODE 3
|
|
|
|
static int idle_force_vcore_lp_mode = IDLE_VCORE_CHECK_FOR_LP_MODE;
|
|
static bool idle_by_pass_secure_cg;
|
|
static unsigned int idle_block_mask[NR_TYPES][NF_CG_STA_RECORD];
|
|
static bool clkmux_cond[NR_TYPES];
|
|
static bool vcore_cond[NR_TYPES];
|
|
static unsigned int clkmux_block_mask[NR_TYPES][NF_CLK_CFG];
|
|
static unsigned int clkmux_addr[NF_CLK_CFG];
|
|
|
|
/* DeepIdle */
|
|
static unsigned int dpidle_time_criteria = 26000; /* 2ms */
|
|
static unsigned long dpidle_cnt[NR_CPUS] = {0};
|
|
static unsigned long dpidle_f26m_cnt[NR_CPUS] = {0};
|
|
static unsigned long dpidle_block_cnt[NR_REASONS] = {0};
|
|
static bool dpidle_by_pass_cg;
|
|
bool dpidle_by_pass_pg;
|
|
static bool dpidle_gs_dump_req;
|
|
static unsigned int dpidle_gs_dump_delay_ms = 10000; /* 10 sec */
|
|
static unsigned int dpidle_gs_dump_req_ts;
|
|
static unsigned int dpidle_dump_log = DEEPIDLE_LOG_REDUCED;
|
|
static unsigned int dpidle_run_once;
|
|
|
|
/* SODI3 */
|
|
|
|
static unsigned int soidle3_pll_block_mask[NR_PLLS] = {0x0};
|
|
static unsigned long soidle3_cnt[NR_CPUS] = {0};
|
|
static unsigned long soidle3_block_cnt[NR_REASONS] = {0};
|
|
|
|
static bool soidle3_by_pass_cg;
|
|
static bool soidle3_by_pass_pll;
|
|
static bool soidle3_by_pass_en;
|
|
static bool soidle3_by_pass_disp_pwm;
|
|
static u32 sodi3_flags = SODI_FLAG_REDUCE_LOG;
|
|
static int sodi3_by_uptime_count;
|
|
|
|
static bool is_sodi3_enter;
|
|
|
|
/* SODI */
|
|
static unsigned long soidle_cnt[NR_CPUS] = {0};
|
|
static unsigned long soidle_block_cnt[NR_REASONS] = {0};
|
|
static bool soidle_by_pass_cg;
|
|
bool soidle_by_pass_pg;
|
|
static bool soidle_by_pass_en;
|
|
static u32 sodi_flags = SODI_FLAG_REDUCE_LOG;
|
|
static int sodi_by_uptime_count;
|
|
|
|
/* Regular Idle */
|
|
static unsigned long rgidle_cnt[NR_CPUS] = {0};
|
|
|
|
/* idle_notifier */
|
|
static RAW_NOTIFIER_HEAD(mtk_idle_notifier);
|
|
|
|
int mtk_idle_notifier_register(struct notifier_block *n)
|
|
{
|
|
int ret = 0;
|
|
int index = 0;
|
|
#ifdef CONFIG_KALLSYMS
|
|
char namebuf[128] = {0};
|
|
const char *symname = NULL;
|
|
|
|
symname = kallsyms_lookup((unsigned long)n->notifier_call,
|
|
NULL, NULL, NULL, namebuf);
|
|
if (symname) {
|
|
printk_deferred("[name:spm&][mt_idle_ntf] <%02d>%08lx (%s)\n",
|
|
index++, (unsigned long)n->notifier_call, symname);
|
|
} else {
|
|
printk_deferred("[name:spm&][mt_idle_ntf] <%02d>%08lx\n",
|
|
index++, (unsigned long)n->notifier_call);
|
|
}
|
|
#else
|
|
printk_deferred("[name:spm&][mt_idle_ntf] <%02d>%08lx\n",
|
|
index++, (unsigned long)n->notifier_call);
|
|
#endif
|
|
|
|
ret = raw_notifier_chain_register(&mtk_idle_notifier, n);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mtk_idle_notifier_register);
|
|
|
|
void mtk_idle_notifier_unregister(struct notifier_block *n)
|
|
{
|
|
raw_notifier_chain_unregister(&mtk_idle_notifier, n);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mtk_idle_notifier_unregister);
|
|
|
|
void mtk_idle_notifier_call_chain(unsigned long val)
|
|
{
|
|
raw_notifier_call_chain(&mtk_idle_notifier, val, NULL);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mtk_idle_notifier_call_chain);
|
|
|
|
/*
|
|
* Check clkmux condition
|
|
* 1. Only in deepidle/SODI3
|
|
* 2. After mtk_idle_notifier_call_chain()
|
|
* - To ensure other subsystem controls its clkmux well
|
|
*/
|
|
static unsigned int check_and_update_vcore_lp_mode_cond(int type)
|
|
{
|
|
unsigned int op_cond = 0;
|
|
|
|
if (idle_force_vcore_lp_mode == IDLE_VCORE_BYPASS_CHECK_FOR_LP_MODE)
|
|
goto END;
|
|
|
|
memset(clkmux_block_mask[type], 0, NF_CLK_CFG * sizeof(unsigned int));
|
|
|
|
clkmux_cond[type] = mtk_idle_check_clkmux(type, clkmux_block_mask);
|
|
vcore_cond[type] = mtk_idle_check_vcore_cond();
|
|
|
|
switch (idle_force_vcore_lp_mode) {
|
|
case IDLE_VCORE_CHECK_FOR_LP_MODE:
|
|
/* by clkmux check */
|
|
op_cond |= (clkmux_cond[type] ? DEEPIDLE_OPT_VCORE_LP_MODE : 0);
|
|
op_cond |= (vcore_cond[type] ? DEEPIDLE_OPT_VCORE_LOW_VOLT : 0);
|
|
break;
|
|
case IDLE_VCORE_FORCE_LP_MODE:
|
|
/* enter LP mode */
|
|
op_cond |= (DEEPIDLE_OPT_VCORE_LP_MODE |
|
|
DEEPIDLE_OPT_VCORE_LOW_VOLT);
|
|
break;
|
|
case IDLE_VCORE_FORCE_NORMAL_MODE:
|
|
/* no enter LP mode */
|
|
op_cond &= ~(DEEPIDLE_OPT_VCORE_LP_MODE |
|
|
DEEPIDLE_OPT_VCORE_LOW_VOLT);
|
|
break;
|
|
default:
|
|
op_cond = 0;
|
|
break;
|
|
}
|
|
|
|
END:
|
|
return op_cond;
|
|
}
|
|
|
|
#ifndef USING_STD_TIMER_OPS
|
|
/* Workaround of static analysis defect*/
|
|
int idle_gpt_get_cnt(unsigned int id, unsigned int *ptr)
|
|
{
|
|
unsigned int val[2] = {0};
|
|
int ret = 0;
|
|
|
|
ret = gpt_get_cnt(id, val);
|
|
*ptr = val[0];
|
|
|
|
return ret;
|
|
}
|
|
|
|
int idle_gpt_get_cmp(unsigned int id, unsigned int *ptr)
|
|
{
|
|
unsigned int val[2] = {0};
|
|
int ret = 0;
|
|
|
|
ret = gpt_get_cmp(id, val);
|
|
*ptr = val[0];
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
static bool next_timer_criteria_check(unsigned int timer_criteria)
|
|
{
|
|
unsigned int timer_left = 0;
|
|
bool ret = true;
|
|
|
|
#ifdef USING_STD_TIMER_OPS
|
|
struct timespec t;
|
|
|
|
t = ktime_to_timespec(tick_nohz_get_sleep_length());
|
|
timer_left = t.tv_sec * USEC_PER_SEC + t.tv_nsec / NSEC_PER_USEC;
|
|
|
|
if (timer_left < timer_criteria)
|
|
ret = false;
|
|
#else
|
|
#ifdef CONFIG_SMP
|
|
timer_left = localtimer_get_counter();
|
|
|
|
if ((int)timer_left < timer_criteria ||
|
|
((int)timer_left) < 0)
|
|
ret = false;
|
|
#else
|
|
unsigned int timer_cmp = 0;
|
|
|
|
gpt_get_cnt(GPT1, &timer_left);
|
|
gpt_get_cmp(GPT1, &timer_cmp);
|
|
|
|
if ((timer_cmp - timer_left) < timer_criteria)
|
|
ret = false;
|
|
#endif
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static void timer_setting_before_wfi(bool f26m_off)
|
|
{
|
|
#ifndef USING_STD_TIMER_OPS
|
|
#ifdef CONFIG_SMP
|
|
unsigned int timer_left = 0;
|
|
|
|
timer_left = localtimer_get_counter();
|
|
|
|
if ((int)timer_left <= 0)
|
|
/* Trigger idle_gpt Timeout imediately */
|
|
gpt_set_cmp(IDLE_GPT, 1);
|
|
else {
|
|
if (f26m_off)
|
|
gpt_set_cmp(IDLE_GPT, div_u64(timer_left, 406.25));
|
|
else
|
|
gpt_set_cmp(IDLE_GPT, timer_left);
|
|
}
|
|
|
|
if (f26m_off)
|
|
gpt_set_clk(IDLE_GPT, GPT_CLK_SRC_RTC, GPT_CLK_DIV_1);
|
|
|
|
start_gpt(IDLE_GPT);
|
|
#else
|
|
gpt_get_cnt(GPT1, &timer_left);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
static void timer_setting_after_wfi(bool f26m_off)
|
|
{
|
|
#ifndef USING_STD_TIMER_OPS
|
|
#ifdef CONFIG_SMP
|
|
if (gpt_check_and_ack_irq(IDLE_GPT)) {
|
|
localtimer_set_next_event(1);
|
|
if (f26m_off)
|
|
gpt_set_clk(IDLE_GPT, GPT_CLK_SRC_SYS, GPT_CLK_DIV_1);
|
|
} else {
|
|
/* waked up by other wakeup source */
|
|
unsigned int cnt, cmp;
|
|
|
|
idle_gpt_get_cnt(IDLE_GPT, &cnt);
|
|
idle_gpt_get_cmp(IDLE_GPT, &cmp);
|
|
if (unlikely(cmp < cnt)) {
|
|
idle_err("[%s]GPT%d: counter = %10u, compare = %10u\n",
|
|
__func__, IDLE_GPT + 1, cnt, cmp);
|
|
/* BUG(); */
|
|
}
|
|
|
|
if (f26m_off) {
|
|
localtimer_set_next_event((cmp - cnt) * 1625 / 4);
|
|
gpt_set_clk(IDLE_GPT, GPT_CLK_SRC_SYS, GPT_CLK_DIV_1);
|
|
} else
|
|
localtimer_set_next_event(cmp - cnt);
|
|
|
|
stop_gpt(IDLE_GPT);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
#if !defined(CONFIG_FPGA_EARLY_PORTING)
|
|
static bool mtk_idle_cpu_criteria(void)
|
|
{
|
|
/* single core check will be checked mcdi driver for acao case */
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
static unsigned int idle_ufs_lock;
|
|
static DEFINE_SPINLOCK(idle_ufs_spin_lock);
|
|
|
|
void idle_lock_by_ufs(unsigned int lock)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&idle_ufs_spin_lock, flags);
|
|
idle_ufs_lock = lock;
|
|
spin_unlock_irqrestore(&idle_ufs_spin_lock, flags);
|
|
}
|
|
|
|
/*
|
|
* Enable/Disable idle condition mask
|
|
*/
|
|
|
|
static DEFINE_SPINLOCK(idle_condition_mask_spin_lock);
|
|
|
|
static void enable_idle_by_mask(int idle_type, int grp, unsigned int mask)
|
|
{
|
|
unsigned long flags;
|
|
|
|
if (!((idle_type >= 0 && idle_type < NR_TYPES) &&
|
|
(grp >= 0 && grp < NR_GRPS)))
|
|
return;
|
|
spin_lock_irqsave(&idle_condition_mask_spin_lock, flags);
|
|
idle_condition_mask[idle_type][grp] &= ~mask;
|
|
spin_unlock_irqrestore(&idle_condition_mask_spin_lock, flags);
|
|
}
|
|
|
|
static void disable_idle_by_mask(int idle_type, int grp, unsigned int mask)
|
|
{
|
|
unsigned long flags;
|
|
|
|
if (!((idle_type >= 0 && idle_type < NR_TYPES) &&
|
|
(grp >= 0 && grp < NR_GRPS)))
|
|
return;
|
|
spin_lock_irqsave(&idle_condition_mask_spin_lock, flags);
|
|
idle_condition_mask[idle_type][grp] |= mask;
|
|
spin_unlock_irqrestore(&idle_condition_mask_spin_lock, flags);
|
|
}
|
|
|
|
static void enable_idle_by_bit(int idle_type, int id)
|
|
{
|
|
int grp = id / 32;
|
|
unsigned int mask = 1U << (id % 32);
|
|
|
|
if (!((idle_type >= 0 && idle_type < NR_TYPES) &&
|
|
(grp >= 0 && grp < NR_GRPS)))
|
|
return;
|
|
enable_idle_by_mask(idle_type, grp, mask);
|
|
|
|
if (idle_type == IDLE_TYPE_SO)
|
|
enable_idle_by_mask(IDLE_TYPE_SO3, grp, mask);
|
|
}
|
|
|
|
static void disable_idle_by_bit(int idle_type, int id)
|
|
{
|
|
int grp = id / 32;
|
|
unsigned int mask = 1U << (id % 32);
|
|
|
|
if (!((idle_type >= 0 && idle_type < NR_TYPES) &&
|
|
(grp >= 0 && grp < NR_GRPS)))
|
|
return;
|
|
|
|
disable_idle_by_mask(idle_type, grp, mask);
|
|
|
|
if (idle_type == IDLE_TYPE_SO)
|
|
disable_idle_by_mask(IDLE_TYPE_SO3, grp, mask);
|
|
}
|
|
|
|
/*
|
|
* SODI3 part
|
|
*/
|
|
static bool soidle3_can_enter(int cpu, int reason)
|
|
{
|
|
/* check previous common criterion */
|
|
if (reason == BY_CLK) {
|
|
if (soidle3_by_pass_cg == 0) {
|
|
if (idle_block_mask[IDLE_TYPE_SO3][NR_GRPS])
|
|
goto out;
|
|
}
|
|
reason = NR_REASONS;
|
|
} else if (reason < NR_REASONS)
|
|
goto out;
|
|
|
|
#if !defined(CONFIG_FPGA_EARLY_PORTING)
|
|
if (soidle3_by_pass_en == 0) {
|
|
if ((spm_get_sodi_en() == 0) || (spm_get_sodi3_en() == 0)) {
|
|
/* if SODI is disabled, SODI3 is also disabled */
|
|
reason = BY_DIS;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (soidle3_by_pass_disp_pwm == 0) {
|
|
if (!mtk_idle_disp_is_pwm_rosc()) {
|
|
reason = BY_PWM;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (soidle3_by_pass_pll == 0) {
|
|
if (!mtk_idle_check_pll(soidle3_pll_condition_mask,
|
|
soidle3_pll_block_mask)) {
|
|
reason = BY_PLL;
|
|
goto out;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* do not enter sodi3 at first 30 seconds */
|
|
if (sodi3_by_uptime_count != -1) {
|
|
struct timespec uptime;
|
|
unsigned long val;
|
|
|
|
get_monotonic_boottime(&uptime);
|
|
val = (unsigned long)uptime.tv_sec;
|
|
if (val <= 30) {
|
|
sodi3_by_uptime_count++;
|
|
reason = BY_BOOT;
|
|
goto out;
|
|
} else {
|
|
#if !defined(CONFIG_MACH_MT6739)
|
|
idle_warn("SODI3: blocking by uptime, count = %d\n",
|
|
sodi3_by_uptime_count);
|
|
#endif
|
|
sodi3_by_uptime_count = -1;
|
|
}
|
|
}
|
|
|
|
/* Check Secure CGs - after other SODI3 criteria PASS */
|
|
if (!idle_by_pass_secure_cg) {
|
|
if (!mtk_idle_check_secure_cg(idle_block_mask)) {
|
|
reason = BY_CLK;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
return mtk_idle_select_state(IDLE_TYPE_SO3, reason);
|
|
}
|
|
|
|
void soidle3_before_wfi(int cpu)
|
|
{
|
|
timer_setting_before_wfi(true);
|
|
}
|
|
|
|
void soidle3_after_wfi(int cpu)
|
|
{
|
|
timer_setting_after_wfi(true);
|
|
|
|
soidle3_cnt[cpu]++;
|
|
}
|
|
|
|
/*
|
|
* SODI part
|
|
*/
|
|
static bool soidle_can_enter(int cpu, int reason)
|
|
{
|
|
/* check previous common criterion */
|
|
if (reason == BY_CLK) {
|
|
if (soidle_by_pass_cg == 0) {
|
|
if (idle_block_mask[IDLE_TYPE_SO][NR_GRPS])
|
|
goto out;
|
|
}
|
|
reason = NR_REASONS;
|
|
} else if (reason < NR_REASONS)
|
|
goto out;
|
|
|
|
#if !defined(CONFIG_FPGA_EARLY_PORTING)
|
|
if (soidle_by_pass_en == 0) {
|
|
if (spm_get_sodi_en() == 0) {
|
|
reason = BY_DIS;
|
|
goto out;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* do not enter sodi at first 20 seconds */
|
|
if (sodi_by_uptime_count != -1) {
|
|
struct timespec uptime;
|
|
unsigned long val;
|
|
|
|
get_monotonic_boottime(&uptime);
|
|
val = (unsigned long)uptime.tv_sec;
|
|
if (val <= 20) {
|
|
sodi_by_uptime_count++;
|
|
reason = BY_BOOT;
|
|
goto out;
|
|
} else {
|
|
#if !defined(CONFIG_MACH_MT6739)
|
|
idle_warn("SODI: blocking by uptime, count = %d\n",
|
|
sodi_by_uptime_count);
|
|
#endif
|
|
sodi_by_uptime_count = -1;
|
|
}
|
|
}
|
|
|
|
/* Check Secure CGs - after other SODI criteria PASS */
|
|
if (!idle_by_pass_secure_cg) {
|
|
if (!mtk_idle_check_secure_cg(idle_block_mask)) {
|
|
reason = BY_CLK;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
return mtk_idle_select_state(IDLE_TYPE_SO, reason);
|
|
}
|
|
|
|
void soidle_before_wfi(int cpu)
|
|
{
|
|
timer_setting_before_wfi(false);
|
|
}
|
|
|
|
void soidle_after_wfi(int cpu)
|
|
{
|
|
timer_setting_after_wfi(false);
|
|
|
|
soidle_cnt[cpu]++;
|
|
}
|
|
|
|
/*
|
|
* deep idle part
|
|
*/
|
|
static bool dpidle_can_enter(int cpu, int reason)
|
|
{
|
|
dpidle_profile_time(DPIDLE_PROFILE_CAN_ENTER_START);
|
|
|
|
/* check previous common criterion */
|
|
if (reason == BY_CLK) {
|
|
if (dpidle_by_pass_cg == 0) {
|
|
if (idle_block_mask[IDLE_TYPE_DP][NR_GRPS])
|
|
goto out;
|
|
}
|
|
} else if (reason < NR_REASONS)
|
|
goto out;
|
|
|
|
reason = NR_REASONS;
|
|
|
|
/* Check Secure CGs - after other deepidle criteria PASS */
|
|
if (!idle_by_pass_secure_cg) {
|
|
if (!mtk_idle_check_secure_cg(idle_block_mask)) {
|
|
reason = BY_CLK;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
dpidle_profile_time(DPIDLE_PROFILE_CAN_ENTER_END);
|
|
|
|
return mtk_idle_select_state(IDLE_TYPE_DP, reason);
|
|
}
|
|
|
|
/*
|
|
* regular idle part
|
|
*/
|
|
static bool rgidle_can_enter(int cpu, int reason)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static void rgidle_before_wfi(int cpu)
|
|
{
|
|
|
|
}
|
|
|
|
static void rgidle_after_wfi(int cpu)
|
|
{
|
|
rgidle_cnt[cpu]++;
|
|
}
|
|
|
|
static noinline void go_to_rgidle(int cpu)
|
|
{
|
|
rgidle_before_wfi(cpu);
|
|
|
|
go_to_wfi();
|
|
|
|
rgidle_after_wfi(cpu);
|
|
}
|
|
|
|
/*
|
|
* idle task flow part
|
|
*/
|
|
|
|
/*
|
|
* xxidle_handler return 1 if enter and exit the low power state
|
|
*/
|
|
#if defined(CONFIG_MACH_MT6763)
|
|
|
|
static u32 slp_spm_SODI3_flags = {
|
|
SPM_FLAG_DIS_INFRA_PDN |
|
|
SPM_FLAG_DIS_VCORE_DVS |
|
|
SPM_FLAG_DIS_VCORE_DFS |
|
|
SPM_FLAG_DIS_ATF_ABORT |
|
|
SPM_FLAG_KEEP_CSYSPWRUPACK_HIGH |
|
|
#if !defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT)
|
|
SPM_FLAG_DIS_SSPM_SRAM_SLEEP |
|
|
#endif
|
|
SPM_FLAG_SODI_OPTION |
|
|
SPM_FLAG_ENABLE_SODI3
|
|
};
|
|
|
|
static u32 slp_spm_SODI_flags = {
|
|
SPM_FLAG_DIS_INFRA_PDN |
|
|
SPM_FLAG_DIS_VCORE_DVS |
|
|
SPM_FLAG_DIS_VCORE_DFS |
|
|
SPM_FLAG_DIS_ATF_ABORT |
|
|
SPM_FLAG_KEEP_CSYSPWRUPACK_HIGH |
|
|
#if !defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT)
|
|
SPM_FLAG_DIS_SSPM_SRAM_SLEEP |
|
|
#endif
|
|
SPM_FLAG_SODI_OPTION
|
|
};
|
|
|
|
u32 slp_spm_deepidle_flags = {
|
|
/* SPM_FLAG_DIS_CPU_PDN | */
|
|
SPM_FLAG_DIS_INFRA_PDN |
|
|
/* SPM_FLAG_DIS_DDRPHY_PDN | */
|
|
SPM_FLAG_DIS_VCORE_DVS |
|
|
SPM_FLAG_DIS_VCORE_DFS |
|
|
SPM_FLAG_DIS_ATF_ABORT |
|
|
SPM_FLAG_KEEP_CSYSPWRUPACK_HIGH |
|
|
#if !defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT)
|
|
SPM_FLAG_DIS_SSPM_SRAM_SLEEP |
|
|
#endif
|
|
SPM_FLAG_DEEPIDLE_OPTION
|
|
};
|
|
|
|
static u32 slp_spm_SODI3_flags1 = {
|
|
0
|
|
};
|
|
|
|
static u32 slp_spm_SODI_flags1 = {
|
|
0
|
|
};
|
|
|
|
static u32 slp_spm_deepidle_flags1 = {
|
|
0
|
|
};
|
|
|
|
#elif defined(CONFIG_MACH_MT6739)
|
|
|
|
static u32 slp_spm_SODI3_flags = {
|
|
/* SPM_FLAG_DIS_CPU_PDN | */
|
|
/* SPM_FLAG_DIS_VPROC_VSRAM_DVS | */
|
|
SPM_FLAG_DIS_ATF_ABORT |
|
|
SPM_FLAG_DIS_VCORE_DVS |
|
|
SPM_FLAG_DIS_VCORE_DFS |
|
|
SPM_FLAG_KEEP_CSYSPWRUPACK_HIGH |
|
|
SPM_FLAG_ENABLE_SODI2P5
|
|
};
|
|
|
|
static u32 slp_spm_SODI_flags = {
|
|
/* SPM_FLAG_DIS_CPU_PDN | */
|
|
/* SPM_FLAG_DIS_VPROC_VSRAM_DVS | */
|
|
SPM_FLAG_DIS_ATF_ABORT |
|
|
SPM_FLAG_DIS_VCORE_DVS |
|
|
SPM_FLAG_DIS_VCORE_DFS |
|
|
SPM_FLAG_KEEP_CSYSPWRUPACK_HIGH |
|
|
SPM_FLAG_ENABLE_SODI2P5
|
|
};
|
|
|
|
u32 slp_spm_deepidle_flags = {
|
|
/* SPM_FLAG_DIS_CPU_PDN | */
|
|
/* SPM_FLAG_DIS_VPROC_VSRAM_DVS | */
|
|
SPM_FLAG_DIS_ATF_ABORT |
|
|
SPM_FLAG_DIS_VCORE_DVS |
|
|
SPM_FLAG_DIS_VCORE_DFS |
|
|
SPM_FLAG_KEEP_CSYSPWRUPACK_HIGH
|
|
};
|
|
|
|
static u32 slp_spm_SODI3_flags1 = {
|
|
0
|
|
};
|
|
|
|
static u32 slp_spm_SODI_flags1 = {
|
|
0
|
|
};
|
|
|
|
static u32 slp_spm_deepidle_flags1 = {
|
|
0
|
|
};
|
|
|
|
|
|
#elif defined(CONFIG_MACH_MT6771)
|
|
|
|
static u32 slp_spm_SODI3_flags = {
|
|
SPM_FLAG_DIS_INFRA_PDN |
|
|
SPM_FLAG_DIS_VCORE_DVS |
|
|
SPM_FLAG_DIS_VCORE_DFS |
|
|
SPM_FLAG_DIS_ATF_ABORT |
|
|
SPM_FLAG_KEEP_CSYSPWRUPACK_HIGH |
|
|
/* SPM_FLAG_DIS_VPROC_VSRAM_DVS | */
|
|
#if !defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT)
|
|
SPM_FLAG_DIS_SSPM_SRAM_SLEEP |
|
|
#endif
|
|
SPM_FLAG_SODI_OPTION |
|
|
SPM_FLAG_ENABLE_SODI3
|
|
};
|
|
|
|
static u32 slp_spm_SODI_flags = {
|
|
SPM_FLAG_DIS_INFRA_PDN |
|
|
SPM_FLAG_DIS_VCORE_DVS |
|
|
SPM_FLAG_DIS_VCORE_DFS |
|
|
SPM_FLAG_DIS_ATF_ABORT |
|
|
SPM_FLAG_KEEP_CSYSPWRUPACK_HIGH |
|
|
/* SPM_FLAG_DIS_VPROC_VSRAM_DVS | */
|
|
#if !defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT)
|
|
SPM_FLAG_DIS_SSPM_SRAM_SLEEP |
|
|
#endif
|
|
SPM_FLAG_SODI_OPTION
|
|
};
|
|
|
|
u32 slp_spm_deepidle_flags = {
|
|
/* SPM_FLAG_DIS_CPU_PDN | */
|
|
SPM_FLAG_DIS_INFRA_PDN |
|
|
/* SPM_FLAG_DIS_DDRPHY_PDN | */
|
|
SPM_FLAG_DIS_VCORE_DVS |
|
|
SPM_FLAG_DIS_VCORE_DFS |
|
|
SPM_FLAG_DIS_ATF_ABORT |
|
|
SPM_FLAG_KEEP_CSYSPWRUPACK_HIGH |
|
|
/* SPM_FLAG_DIS_VPROC_VSRAM_DVS | */
|
|
#if !defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT)
|
|
SPM_FLAG_DIS_SSPM_SRAM_SLEEP |
|
|
#endif
|
|
SPM_FLAG_DEEPIDLE_OPTION
|
|
};
|
|
|
|
static u32 slp_spm_SODI3_flags1 = {
|
|
0
|
|
};
|
|
|
|
static u32 slp_spm_SODI_flags1 = {
|
|
0
|
|
};
|
|
|
|
static u32 slp_spm_deepidle_flags1 = {
|
|
0
|
|
};
|
|
|
|
#endif
|
|
|
|
unsigned int ufs_cb_before_xxidle(void)
|
|
{
|
|
unsigned int op_cond = 0;
|
|
bool bblpm_check = false;
|
|
|
|
#if defined(CONFIG_MTK_UFS_SUPPORT)
|
|
bool ufs_in_hibernate = false;
|
|
int boot_type;
|
|
|
|
boot_type = get_boot_type();
|
|
if (boot_type == BOOTDEV_UFS) {
|
|
ufs_in_hibernate = !ufs_mtk_deepidle_hibern8_check();
|
|
op_cond = ufs_in_hibernate ? DEEPIDLE_OPT_XO_UFS_ON_OFF : 0;
|
|
}
|
|
#endif
|
|
|
|
bblpm_check = !clk_buf_bblpm_enter_cond();
|
|
op_cond |= bblpm_check ? DEEPIDLE_OPT_CLKBUF_BBLPM : 0;
|
|
|
|
return op_cond;
|
|
}
|
|
|
|
void ufs_cb_after_xxidle(void)
|
|
{
|
|
#if defined(CONFIG_MTK_UFS_SUPPORT)
|
|
int boot_type;
|
|
|
|
boot_type = get_boot_type();
|
|
if (boot_type == BOOTDEV_UFS)
|
|
ufs_mtk_deepidle_leave();
|
|
#endif
|
|
}
|
|
|
|
unsigned int soidle_pre_handler(void)
|
|
{
|
|
unsigned int op_cond = 0;
|
|
|
|
op_cond = ufs_cb_before_xxidle();
|
|
|
|
|
|
#ifdef CONFIG_THERMAL
|
|
/* cancel thermal hrtimer for power saving */
|
|
mtkTTimer_cancel_timer();
|
|
#endif
|
|
|
|
if (is_sodi3_enter) {
|
|
op_cond |=
|
|
check_and_update_vcore_lp_mode_cond(IDLE_TYPE_SO3);
|
|
}
|
|
|
|
return op_cond;
|
|
}
|
|
|
|
void soidle_post_handler(void)
|
|
{
|
|
|
|
#ifdef CONFIG_THERMAL
|
|
/* restart thermal hrtimer for update temp info */
|
|
mtkTTimer_start_timer();
|
|
#endif
|
|
|
|
ufs_cb_after_xxidle();
|
|
}
|
|
|
|
static unsigned int dpidle_pre_process(int cpu)
|
|
{
|
|
unsigned int op_cond = 0;
|
|
|
|
dpidle_profile_time(DPIDLE_PROFILE_ENTER_UFS_CB_BEFORE_XXIDLE_START);
|
|
|
|
op_cond = ufs_cb_before_xxidle();
|
|
|
|
dpidle_profile_time(DPIDLE_PROFILE_ENTER_UFS_CB_BEFORE_XXIDLE_END);
|
|
|
|
mtk_idle_notifier_call_chain(NOTIFY_DPIDLE_ENTER);
|
|
|
|
dpidle_profile_time(DPIDLE_PROFILE_IDLE_NOTIFIER_END);
|
|
|
|
#if !defined(CONFIG_FPGA_EARLY_PORTING)
|
|
#ifdef CONFIG_THERMAL
|
|
/* cancel thermal hrtimer for power saving */
|
|
mtkTTimer_cancel_timer();
|
|
#endif
|
|
#endif
|
|
|
|
timer_setting_before_wfi(false);
|
|
|
|
dpidle_profile_time(DPIDLE_PROFILE_TIMER_DEL_END);
|
|
|
|
op_cond |= check_and_update_vcore_lp_mode_cond(IDLE_TYPE_DP);
|
|
|
|
return op_cond;
|
|
}
|
|
|
|
static void dpidle_post_process(int cpu)
|
|
{
|
|
dpidle_profile_time(DPIDLE_PROFILE_TIMER_RESTORE_START);
|
|
|
|
#if !defined(CONFIG_FPGA_EARLY_PORTING)
|
|
timer_setting_after_wfi(false);
|
|
|
|
#ifdef CONFIG_THERMAL
|
|
/* restart thermal hrtimer for update temp info */
|
|
mtkTTimer_start_timer();
|
|
#endif
|
|
#endif
|
|
|
|
dpidle_profile_time(DPIDLE_PROFILE_TIMER_RESTORE_END);
|
|
|
|
mtk_idle_notifier_call_chain(NOTIFY_DPIDLE_LEAVE);
|
|
|
|
dpidle_profile_time(DPIDLE_PROFILE_UFS_CB_AFTER_XXIDLE_START);
|
|
|
|
ufs_cb_after_xxidle();
|
|
|
|
dpidle_profile_time(DPIDLE_PROFILE_UFS_CB_AFTER_XXIDLE_END);
|
|
|
|
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
|
|
spm_dpidle_notify_sspm_after_wfi_async_wait();
|
|
#endif
|
|
|
|
dpidle_profile_time(
|
|
DPIDLE_PROFILE_NOTIFY_SSPM_AFTER_WFI_ASYNC_WAIT_END);
|
|
|
|
dpidle_cnt[cpu]++;
|
|
}
|
|
|
|
static bool (*idle_can_enter[NR_TYPES])(int cpu, int reason) = {
|
|
dpidle_can_enter,
|
|
soidle3_can_enter,
|
|
soidle_can_enter,
|
|
rgidle_can_enter,
|
|
};
|
|
|
|
/* Mapping idle_switch to CPUidle C States */
|
|
static int idle_stat_mapping_table[NR_TYPES] = {
|
|
CPUIDLE_STATE_DP,
|
|
CPUIDLE_STATE_SO3,
|
|
CPUIDLE_STATE_SO,
|
|
CPUIDLE_STATE_RG
|
|
};
|
|
|
|
int mtk_idle_select(int cpu)
|
|
{
|
|
int i = NR_TYPES - 1;
|
|
int reason = NR_REASONS;
|
|
#if defined(CONFIG_MTK_UFS_SUPPORT)
|
|
unsigned long flags = 0;
|
|
unsigned int ufs_locked;
|
|
int boot_type;
|
|
#endif
|
|
#ifdef CONFIG_MTK_DCS
|
|
int ch = 0, ret = -1;
|
|
enum dcs_status dcs_status;
|
|
bool dcs_lock_get = false;
|
|
#endif
|
|
|
|
profile_so_start(PIDX_SELECT_TO_ENTER);
|
|
profile_so3_start(PIDX_SELECT_TO_ENTER);
|
|
dpidle_profile_time(DPIDLE_PROFILE_IDLE_SELECT_START);
|
|
|
|
#if !defined(CONFIG_FPGA_EARLY_PORTING)
|
|
/* check if firmware loaded or not */
|
|
if (!spm_load_firmware_status()) {
|
|
reason = BY_FRM;
|
|
goto get_idle_idx;
|
|
}
|
|
#endif
|
|
|
|
#if !defined(CONFIG_FPGA_EARLY_PORTING)
|
|
#ifdef CONFIG_SMP
|
|
/* check cpu status */
|
|
if (!mtk_idle_cpu_criteria()) {
|
|
reason = BY_CPU;
|
|
goto get_idle_idx;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
if (spm_get_resource_usage() == SPM_RESOURCE_ALL) {
|
|
reason = BY_SRR;
|
|
goto get_idle_idx;
|
|
}
|
|
|
|
#if defined(CONFIG_MTK_UFS_SUPPORT)
|
|
boot_type = get_boot_type();
|
|
if (boot_type == BOOTDEV_UFS) {
|
|
spin_lock_irqsave(&idle_ufs_spin_lock, flags);
|
|
ufs_locked = idle_ufs_lock;
|
|
spin_unlock_irqrestore(&idle_ufs_spin_lock, flags);
|
|
|
|
if (ufs_locked) {
|
|
reason = BY_UFS;
|
|
goto get_idle_idx;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* teei ready */
|
|
#if !defined(CONFIG_FPGA_EARLY_PORTING)
|
|
#ifdef CONFIG_MICROTRUST_TEE_SUPPORT
|
|
if (!is_teei_ready()) {
|
|
reason = BY_TEE;
|
|
goto get_idle_idx;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(CONFIG_FPGA_EARLY_PORTING)
|
|
/* cg check */
|
|
memset(idle_block_mask, 0,
|
|
NR_TYPES * NF_CG_STA_RECORD * sizeof(unsigned int));
|
|
if (!mtk_idle_check_cg(idle_block_mask)) {
|
|
reason = BY_CLK;
|
|
goto get_idle_idx;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_MTK_DCS
|
|
/* check if DCS channel switching */
|
|
ret = dcs_get_dcs_status_trylock(&ch, &dcs_status);
|
|
if (ret) {
|
|
reason = BY_DCS;
|
|
goto get_idle_idx;
|
|
}
|
|
|
|
dcs_lock_get = true;
|
|
|
|
#endif
|
|
|
|
get_idle_idx:
|
|
/* check if criteria check fail in common part */
|
|
for (i = 0; i < NR_TYPES; i++) {
|
|
if (idle_switch[i]) {
|
|
/* call each idle scenario check functions */
|
|
if (idle_can_enter[i](cpu, reason))
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Prevent potential out-of-bounds vulnerability */
|
|
i = (i >= NR_TYPES) ? NR_TYPES : i;
|
|
|
|
#ifdef CONFIG_MTK_DCS
|
|
if (dcs_lock_get)
|
|
dcs_get_dcs_status_unlock();
|
|
#endif
|
|
|
|
dpidle_profile_time(DPIDLE_PROFILE_IDLE_SELECT_END);
|
|
|
|
return i;
|
|
}
|
|
|
|
int mtk_idle_select_base_on_menu_gov(int cpu, int menu_select_state)
|
|
{
|
|
int i;
|
|
int state = CPUIDLE_STATE_RG;
|
|
|
|
if (menu_select_state < 0)
|
|
return menu_select_state;
|
|
|
|
mtk_idle_dump_cnt_in_interval();
|
|
|
|
i = mtk_idle_select(cpu);
|
|
|
|
/* residency requirement of ALL C state is satisfied */
|
|
if (menu_select_state == CPUIDLE_STATE_SO3) {
|
|
state = idle_stat_mapping_table[i];
|
|
/* SODI3.0 residency requirement does NOT satisfied */
|
|
} else if (menu_select_state >= CPUIDLE_STATE_SO &&
|
|
menu_select_state <= CPUIDLE_STATE_DP) {
|
|
if (i == IDLE_TYPE_SO3)
|
|
i = idle_switch[IDLE_TYPE_SO] ?
|
|
IDLE_TYPE_SO : IDLE_TYPE_RG;
|
|
|
|
state = idle_stat_mapping_table[i];
|
|
/* DPIDLE, SODI3.0, and SODI residency requirement does NOT satisfied */
|
|
} else {
|
|
state = CPUIDLE_STATE_RG;
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
int dpidle_enter(int cpu)
|
|
{
|
|
int ret = CPUIDLE_STATE_DP;
|
|
u32 operation_cond = 0;
|
|
|
|
/* don't check lock dependency */
|
|
lockdep_off();
|
|
|
|
dpidle_profile_time(DPIDLE_PROFILE_ENTER);
|
|
|
|
mtk_idle_ratio_calc_start(IDLE_TYPE_DP, cpu);
|
|
|
|
operation_cond |= dpidle_pre_process(cpu);
|
|
|
|
if (dpidle_gs_dump_req) {
|
|
unsigned int current_ts = idle_get_current_time_ms();
|
|
|
|
if ((current_ts - dpidle_gs_dump_req_ts) >=
|
|
dpidle_gs_dump_delay_ms) {
|
|
#if !defined(CONFIG_MACH_MT6739)
|
|
idle_warn("dpidle dump LP golden\n");
|
|
#endif
|
|
dpidle_gs_dump_req = 0;
|
|
operation_cond |= DEEPIDLE_OPT_DUMP_LP_GOLDEN;
|
|
}
|
|
}
|
|
|
|
spm_go_to_dpidle(slp_spm_deepidle_flags,
|
|
slp_spm_deepidle_flags1,
|
|
dpidle_dump_log, operation_cond);
|
|
|
|
dpidle_post_process(cpu);
|
|
|
|
mtk_idle_ratio_calc_stop(IDLE_TYPE_DP, cpu);
|
|
|
|
dpidle_profile_time(DPIDLE_PROFILE_LEAVE);
|
|
dpidle_show_profile_time();
|
|
|
|
lockdep_on();
|
|
|
|
/* For test */
|
|
if (dpidle_run_once)
|
|
idle_switch[IDLE_TYPE_DP] = 0;
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(dpidle_enter);
|
|
|
|
int soidle3_enter(int cpu)
|
|
{
|
|
int ret = CPUIDLE_STATE_SO3;
|
|
unsigned long long soidle3_time = 0;
|
|
static unsigned long long soidle3_residency;
|
|
|
|
is_sodi3_enter = true;
|
|
|
|
/* don't check lock dependency */
|
|
lockdep_off();
|
|
|
|
profile_so3_end(PIDX_SELECT_TO_ENTER);
|
|
|
|
profile_so3_start(PIDX_ENTER_TOTAL);
|
|
|
|
if (sodi3_flags & SODI_FLAG_RESIDENCY)
|
|
soidle3_time = idle_get_current_time_ms();
|
|
|
|
mtk_idle_ratio_calc_start(IDLE_TYPE_SO3, cpu);
|
|
|
|
profile_so3_start(PIDX_IDLE_NOTIFY_ENTER);
|
|
mtk_idle_notifier_call_chain(NOTIFY_SOIDLE3_ENTER);
|
|
profile_so3_end(PIDX_IDLE_NOTIFY_ENTER);
|
|
|
|
#ifdef DEFAULT_MMP_ENABLE
|
|
mmprofile_log_ex(sodi_mmp_get_events()->sodi_enable,
|
|
MMPROFILE_FLAG_START, 0, 0);
|
|
#endif /* DEFAULT_MMP_ENABLE */
|
|
|
|
spm_go_to_sodi3(slp_spm_SODI3_flags, slp_spm_SODI3_flags1, sodi3_flags);
|
|
|
|
/* Clear SODI_FLAG_DUMP_LP_GS in sodi3_flags */
|
|
sodi3_flags &= (~SODI_FLAG_DUMP_LP_GS);
|
|
|
|
#ifdef DEFAULT_MMP_ENABLE
|
|
mmprofile_log_ex(sodi_mmp_get_events()->sodi_enable,
|
|
MMPROFILE_FLAG_END, 0, spm_read(SPM_PASR_DPD_3));
|
|
#endif /* DEFAULT_MMP_ENABLE */
|
|
|
|
profile_so3_start(PIDX_IDLE_NOTIFY_LEAVE);
|
|
mtk_idle_notifier_call_chain(NOTIFY_SOIDLE3_LEAVE);
|
|
profile_so3_end(PIDX_IDLE_NOTIFY_LEAVE);
|
|
|
|
mtk_idle_ratio_calc_stop(IDLE_TYPE_SO3, cpu);
|
|
|
|
if (sodi3_flags & SODI_FLAG_RESIDENCY) {
|
|
soidle3_residency += idle_get_current_time_ms() - soidle3_time;
|
|
idle_dbg("SO3: soidle3_residency = %llu\n", soidle3_residency);
|
|
}
|
|
|
|
profile_so3_end(PIDX_LEAVE_TOTAL);
|
|
|
|
/* dump latency profiling result */
|
|
profile_so3_dump();
|
|
|
|
lockdep_on();
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(soidle3_enter);
|
|
|
|
int soidle_enter(int cpu)
|
|
{
|
|
int ret = CPUIDLE_STATE_SO;
|
|
unsigned long long soidle_time = 0;
|
|
static unsigned long long soidle_residency;
|
|
|
|
is_sodi3_enter = false;
|
|
|
|
/* don't check lock dependency */
|
|
lockdep_off();
|
|
|
|
profile_so_end(PIDX_SELECT_TO_ENTER);
|
|
|
|
profile_so_start(PIDX_ENTER_TOTAL);
|
|
|
|
if (sodi_flags & SODI_FLAG_RESIDENCY)
|
|
soidle_time = idle_get_current_time_ms();
|
|
|
|
mtk_idle_ratio_calc_start(IDLE_TYPE_SO, cpu);
|
|
|
|
profile_so_start(PIDX_IDLE_NOTIFY_ENTER);
|
|
mtk_idle_notifier_call_chain(NOTIFY_SOIDLE_ENTER);
|
|
profile_so_end(PIDX_IDLE_NOTIFY_ENTER);
|
|
|
|
#ifdef DEFAULT_MMP_ENABLE
|
|
mmprofile_log_ex(sodi_mmp_get_events()->sodi_enable,
|
|
MMPROFILE_FLAG_START, 0, 0);
|
|
#endif /* DEFAULT_MMP_ENABLE */
|
|
|
|
spm_go_to_sodi(slp_spm_SODI_flags, slp_spm_SODI_flags1, sodi_flags);
|
|
|
|
/* Clear SODI_FLAG_DUMP_LP_GS in sodi_flags */
|
|
sodi_flags &= (~SODI_FLAG_DUMP_LP_GS);
|
|
|
|
#ifdef DEFAULT_MMP_ENABLE
|
|
mmprofile_log_ex(sodi_mmp_get_events()->sodi_enable,
|
|
MMPROFILE_FLAG_END, 0, spm_read(SPM_PASR_DPD_3));
|
|
#endif /* DEFAULT_MMP_ENABLE */
|
|
|
|
profile_so_start(PIDX_IDLE_NOTIFY_LEAVE);
|
|
mtk_idle_notifier_call_chain(NOTIFY_SOIDLE_LEAVE);
|
|
profile_so_end(PIDX_IDLE_NOTIFY_LEAVE);
|
|
|
|
mtk_idle_ratio_calc_stop(IDLE_TYPE_SO, cpu);
|
|
|
|
if (sodi_flags & SODI_FLAG_RESIDENCY) {
|
|
soidle_residency += idle_get_current_time_ms() - soidle_time;
|
|
idle_dbg("SO: soidle_residency = %llu\n", soidle_residency);
|
|
}
|
|
|
|
profile_so_end(PIDX_LEAVE_TOTAL);
|
|
|
|
/* dump latency profiling result */
|
|
profile_so_dump();
|
|
|
|
lockdep_on();
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(soidle_enter);
|
|
|
|
int rgidle_enter(int cpu)
|
|
{
|
|
int ret = CPUIDLE_STATE_RG;
|
|
|
|
mtk_idle_dump_cnt_in_interval();
|
|
#if !defined(SPM_K414_EARLY_PORTING)
|
|
mcdi_heart_beat_log_dump();
|
|
#endif
|
|
remove_cpu_from_prefer_schedule_domain(cpu);
|
|
|
|
mtk_idle_ratio_calc_start(IDLE_TYPE_RG, cpu);
|
|
|
|
idle_refcnt_inc();
|
|
|
|
go_to_rgidle(cpu);
|
|
|
|
idle_refcnt_dec();
|
|
|
|
mtk_idle_ratio_calc_stop(IDLE_TYPE_RG, cpu);
|
|
|
|
add_cpu_to_prefer_schedule_domain(cpu);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(rgidle_enter);
|
|
|
|
/*
|
|
* debugfs
|
|
*/
|
|
#define SYSFS_ENTRY "/sys/kernel/mtk_lpm/cpuidle"
|
|
|
|
/* idle_state */
|
|
static ssize_t idle_state_read(char *ToUserBuf, size_t sz_t, void *priv)
|
|
{
|
|
int i;
|
|
char *p = ToUserBuf;
|
|
size_t sz = sz_t;
|
|
struct mtk_idle_twam *cur_twam;
|
|
|
|
cur_twam = mtk_idle_get_twam();
|
|
|
|
#undef mt_idle_log
|
|
#define mt_idle_log(fmt, args...) \
|
|
do { \
|
|
int l = scnprintf(p, sz, fmt, ##args); \
|
|
p += l; \
|
|
sz -= l; \
|
|
} while (0)
|
|
|
|
mt_idle_log("********** idle state dump **********\n");
|
|
|
|
for (i = 0; i < nr_cpu_ids; i++) {
|
|
mt_idle_log("dpidle_cnt[%d]=%lu, dpidle_26m[%d]=%lu, ",
|
|
i, dpidle_cnt[i], i, dpidle_f26m_cnt[i]);
|
|
mt_idle_log(
|
|
"soidle3_cnt[%d]=%lu, soidle_cnt[%d]=%lu, rgidle_cnt[%d]=%lu\n",
|
|
i, soidle3_cnt[i], i, soidle_cnt[i], i, rgidle_cnt[i]);
|
|
}
|
|
|
|
mt_idle_log("\n********** variables dump **********\n");
|
|
for (i = 0; i < NR_TYPES; i++)
|
|
mt_idle_log("%s_switch=%d, ",
|
|
mtk_get_idle_name(i), idle_switch[i]);
|
|
|
|
mt_idle_log("\n");
|
|
mt_idle_log("idle_ratio_en = %u\n", mtk_idle_get_ratio_status());
|
|
mt_idle_log("idle_latency_en = %d\n",
|
|
mtk_idle_latency_profile_is_on() ? 1 : 0);
|
|
if (cur_twam != NULL) {
|
|
mt_idle_log("twam_handler:%s (clk:%s)\n",
|
|
(cur_twam->running) ?
|
|
"on":"off",
|
|
(cur_twam->speed_mode) ?
|
|
"speed":"normal");
|
|
}
|
|
mt_idle_log("bypass_secure_cg = %u\n", idle_by_pass_secure_cg);
|
|
mt_idle_log("force VCORE lp mode = %u\n", idle_force_vcore_lp_mode);
|
|
|
|
mt_idle_log("\n********** idle command help **********\n");
|
|
mt_idle_log("status help: cat %s/idle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("switch on/off: echo switch mask > %s/idle_state\n",
|
|
SYSFS_ENTRY);
|
|
mt_idle_log("idle ratio profile: ");
|
|
mt_idle_log("echo ratio 1/0 > %s/idle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("idle latency profile: ");
|
|
mt_idle_log("echo latency 1/0 > %s/idle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("soidle3 help: cat %s/soidle3_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("soidle help: cat %s/soidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("dpidle help: cat %s/dpidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("rgidle help: cat %s/rgidle_state\n", SYSFS_ENTRY);
|
|
|
|
return p - ToUserBuf;
|
|
}
|
|
|
|
static ssize_t idle_state_write(char *FromUserBuf, size_t sz, void *priv)
|
|
{
|
|
char cmd[128];
|
|
int param, idx;
|
|
struct mtk_idle_twam *cur_twam;
|
|
|
|
if (sscanf(FromUserBuf, "%127s %x", cmd, ¶m) == 2) {
|
|
if (!strcmp(cmd, "switch")) {
|
|
for (idx = 0; idx < NR_TYPES; idx++)
|
|
idle_switch[idx] =
|
|
(param & (1U << idx)) ? 1 : 0;
|
|
} else if (!strcmp(cmd, "ratio")) {
|
|
if (param == 1)
|
|
mtk_idle_enable_ratio_calc();
|
|
else
|
|
mtk_idle_disable_ratio_calc();
|
|
} else if (!strcmp(cmd, "latency")) {
|
|
mtk_idle_latency_profile_enable(param ? true : false);
|
|
} else if (!strcmp(cmd, "spmtwam_clk")) {
|
|
cur_twam = mtk_idle_get_twam();
|
|
if (cur_twam != NULL)
|
|
cur_twam->speed_mode = param;
|
|
} else if (!strcmp(cmd, "spmtwam_sel")) {
|
|
cur_twam = mtk_idle_get_twam();
|
|
if (cur_twam != NULL)
|
|
cur_twam->sel = param;
|
|
} else if (!strcmp(cmd, "spmtwam")) {
|
|
#if !defined(CONFIG_FPGA_EARLY_PORTING)
|
|
idle_dbg("spmtwam_event = %d\n", param);
|
|
if (param >= 0)
|
|
mtk_idle_twam_enable((u32)param);
|
|
else
|
|
mtk_idle_twam_disable();
|
|
#endif
|
|
} else if (!strcmp(cmd, "bypass_secure_cg")) {
|
|
idle_by_pass_secure_cg = param;
|
|
} else if (!strcmp(cmd, "force_vcore_lp_mode")) {
|
|
idle_force_vcore_lp_mode = param;
|
|
}
|
|
return sz;
|
|
} else if ((!kstrtoint(FromUserBuf, 10, ¶m)) == 1) {
|
|
return sz;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static const struct mtk_lp_sysfs_op idle_state_fops = {
|
|
.fs_read = idle_state_read,
|
|
.fs_write = idle_state_write,
|
|
};
|
|
|
|
/* dpidle_state */
|
|
static ssize_t dpidle_state_read(char *ToUserBuf, size_t sz_t, void *priv)
|
|
{
|
|
int i, k;
|
|
char *p = ToUserBuf;
|
|
size_t sz = sz_t;
|
|
|
|
#undef mt_idle_log
|
|
#define mt_idle_log(fmt, args...) \
|
|
do { \
|
|
int l = scnprintf(p, sz, fmt, ##args); \
|
|
p += l; \
|
|
sz -= l; \
|
|
} while (0)
|
|
|
|
mt_idle_log("*********** deep idle state ************\n");
|
|
mt_idle_log("dpidle_time_criteria=%u\n", dpidle_time_criteria);
|
|
|
|
for (i = 0; i < NR_REASONS; i++)
|
|
mt_idle_log("[%d]dpidle_block_cnt[%s]=%lu\n",
|
|
i, mtk_get_reason_name(i), dpidle_block_cnt[i]);
|
|
mt_idle_log("\n");
|
|
|
|
for (i = 0; i < NR_GRPS; i++) {
|
|
mt_idle_log("[%02d]dpidle_condition_mask[%-10s]=0x%08x\t\t",
|
|
i, mtk_get_cg_group_name(i),
|
|
idle_condition_mask[IDLE_TYPE_DP][i]);
|
|
mt_idle_log("dpidle_block_mask[%-10s]=0x%08x\n",
|
|
mtk_get_cg_group_name(i),
|
|
idle_block_mask[IDLE_TYPE_DP][i]);
|
|
}
|
|
|
|
mt_idle_log("dpidle pg_stat=0x%08x\n",
|
|
idle_block_mask[IDLE_TYPE_DP][NR_GRPS + 1]);
|
|
|
|
mt_idle_log("dpidle_blocking_stat=\n");
|
|
|
|
for (i = 0; i < NR_GRPS; i++) {
|
|
mt_idle_log("[%-10s] - ", mtk_get_cg_group_name(i));
|
|
|
|
for (k = 0; k < 32; k++) {
|
|
if (dpidle_blocking_stat[i][k] != 0)
|
|
mt_idle_log("%-2d: %d, ",
|
|
k, dpidle_blocking_stat[i][k]);
|
|
dpidle_blocking_stat[i][k] = 0;
|
|
}
|
|
mt_idle_log("\n");
|
|
}
|
|
|
|
mt_idle_log("dpidle_clkmux_cond = %d\n", clkmux_cond[IDLE_TYPE_DP]);
|
|
mt_idle_log("dpidle_vcore_cond = %d\n", vcore_cond[IDLE_TYPE_DP]);
|
|
for (i = 0; i < NF_CLK_CFG; i++)
|
|
mt_idle_log("[%02d]block_cond(0x%08x)=0x%08x\n",
|
|
i, clkmux_addr[i],
|
|
clkmux_block_mask[IDLE_TYPE_DP][i]);
|
|
|
|
mt_idle_log("dpidle_by_pass_cg=%u\n", dpidle_by_pass_cg);
|
|
mt_idle_log("dpidle_by_pass_pg=%u\n", dpidle_by_pass_pg);
|
|
mt_idle_log("dpidle_dump_log = %u\n", dpidle_dump_log);
|
|
mt_idle_log("([0]: Reduced, [1]: Full, [2]: resource_usage\n");
|
|
|
|
mt_idle_log("\n*********** dpidle command help ************\n");
|
|
mt_idle_log("dpidle help: cat %s/dpidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("switch on/off: ");
|
|
mt_idle_log("echo [dpidle] 1/0 > %s/dpidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("cpupdn on/off: ");
|
|
mt_idle_log("echo cpupdn 1/0 > %s/dpidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("en_dp_by_bit: ");
|
|
mt_idle_log("echo enable id > %s/dpidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("dis_dp_by_bit: ");
|
|
mt_idle_log("echo disable id > %s/dpidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("modify tm_cri: ");
|
|
mt_idle_log("echo time value(dec) > %s/dpidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("bypass cg: ");
|
|
mt_idle_log("echo bypass 1/0 > %s/dpidle_state\n", SYSFS_ENTRY);
|
|
|
|
return p - ToUserBuf;
|
|
}
|
|
|
|
static ssize_t dpidle_state_write(char *FromUserBuf, size_t sz, void *priv)
|
|
{
|
|
char cmd[128];
|
|
int param;
|
|
|
|
if (sscanf(FromUserBuf, "%127s %d", cmd, ¶m) == 2) {
|
|
if (!strcmp(cmd, "dpidle"))
|
|
idle_switch[IDLE_TYPE_DP] = param;
|
|
else if (!strcmp(cmd, "enable"))
|
|
enable_idle_by_bit(IDLE_TYPE_DP, param);
|
|
else if (!strcmp(cmd, "disable"))
|
|
disable_idle_by_bit(IDLE_TYPE_DP, param);
|
|
else if (!strcmp(cmd, "once"))
|
|
dpidle_run_once = param;
|
|
else if (!strcmp(cmd, "time"))
|
|
dpidle_time_criteria = param;
|
|
else if (!strcmp(cmd, "bypass"))
|
|
dpidle_by_pass_cg = param;
|
|
else if (!strcmp(cmd, "bypass_pg")) {
|
|
dpidle_by_pass_pg = param;
|
|
idle_warn("bypass_pg = %d\n", dpidle_by_pass_pg);
|
|
} else if (!strcmp(cmd, "golden")) {
|
|
dpidle_gs_dump_req = param;
|
|
|
|
if (dpidle_gs_dump_req)
|
|
dpidle_gs_dump_req_ts =
|
|
idle_get_current_time_ms();
|
|
} else if (!strcmp(cmd, "golden_delay_ms")) {
|
|
dpidle_gs_dump_delay_ms = (param >= 0) ? param : 0;
|
|
} else if (!strcmp(cmd, "profile_sampling"))
|
|
dpidle_set_profile_sampling(param);
|
|
else if (!strcmp(cmd, "dump_profile_result"))
|
|
(param) ? dpidle_show_profile_result() : 0;
|
|
else if (!strcmp(cmd, "log"))
|
|
dpidle_dump_log = param;
|
|
|
|
return sz;
|
|
} else if ((!kstrtoint(FromUserBuf, 10, ¶m)) == 1) {
|
|
idle_switch[IDLE_TYPE_DP] = param;
|
|
|
|
return sz;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static const struct mtk_lp_sysfs_op dpidle_state_fops = {
|
|
.fs_read = dpidle_state_read,
|
|
.fs_write = dpidle_state_write,
|
|
};
|
|
|
|
/* soidle3_state */
|
|
static ssize_t soidle3_state_read(char *ToUserBuf, size_t sz_t, void *priv)
|
|
{
|
|
int i;
|
|
char *p = ToUserBuf;
|
|
size_t sz = sz_t;
|
|
|
|
#undef mt_idle_log
|
|
#define mt_idle_log(fmt, args...) \
|
|
do { \
|
|
int l = scnprintf(p, sz, fmt, ##args); \
|
|
p += l; \
|
|
sz -= l; \
|
|
} while (0)
|
|
|
|
|
|
mt_idle_log("*********** soidle3 state ************\n");
|
|
|
|
for (i = 0; i < NR_REASONS; i++)
|
|
mt_idle_log("[%d]soidle3_block_cnt[%s]=%lu\n",
|
|
i, mtk_get_reason_name(i), soidle3_block_cnt[i]);
|
|
mt_idle_log("\n");
|
|
|
|
for (i = 0; i < NR_PLLS; i++) {
|
|
mt_idle_log(
|
|
"[%02d]soidle3_pll_condition_mask[%-8s]=0x%08x\t\t",
|
|
i, mtk_get_pll_group_name(i),
|
|
soidle3_pll_condition_mask[i]);
|
|
mt_idle_log("soidle3_pll_block_mask[%-8s]=0x%08x\n",
|
|
mtk_get_pll_group_name(i),
|
|
soidle3_pll_block_mask[i]);
|
|
}
|
|
mt_idle_log("\n");
|
|
|
|
for (i = 0; i < NR_GRPS; i++) {
|
|
mt_idle_log("[%02d]soidle3_condition_mask[%-10s]=0x%08x\t\t",
|
|
i, mtk_get_cg_group_name(i),
|
|
idle_condition_mask[IDLE_TYPE_SO3][i]);
|
|
mt_idle_log("soidle3_block_mask[%-10s]=0x%08x\n",
|
|
mtk_get_cg_group_name(i),
|
|
idle_block_mask[IDLE_TYPE_SO3][i]);
|
|
}
|
|
|
|
mt_idle_log("soidle3 pg_stat=0x%08x\n",
|
|
idle_block_mask[IDLE_TYPE_SO3][NR_GRPS + 1]);
|
|
|
|
mt_idle_log("sodi3_clkmux_cond = %d\n", clkmux_cond[IDLE_TYPE_SO3]);
|
|
mt_idle_log("sodi3_vcore_cond = %d\n", vcore_cond[IDLE_TYPE_SO3]);
|
|
for (i = 0; i < NF_CLK_CFG; i++)
|
|
mt_idle_log("[%02d]block_cond(0x%08x)=0x%08x\n",
|
|
i, clkmux_addr[i],
|
|
clkmux_block_mask[IDLE_TYPE_SO3][i]);
|
|
|
|
mt_idle_log("soidle3_bypass_pll=%u\n", soidle3_by_pass_pll);
|
|
mt_idle_log("soidle3_bypass_cg=%u\n", soidle3_by_pass_cg);
|
|
mt_idle_log("soidle3_bypass_en=%u\n", soidle3_by_pass_en);
|
|
mt_idle_log("soidle3_by_pass_disp_pwm=%u\n", soidle3_by_pass_disp_pwm);
|
|
mt_idle_log("sodi3_flags=0x%x\n", sodi3_flags);
|
|
|
|
mt_idle_log("\n*********** soidle3 command help ************\n");
|
|
mt_idle_log("soidle3 help: cat %s/soidle3_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("switch on/off: ");
|
|
mt_idle_log("echo [soidle3] 1/0 > %s/soidle3_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("en_dp_by_bit: ");
|
|
mt_idle_log("echo enable id > %s/soidle3_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("dis_dp_by_bit: ");
|
|
mt_idle_log("echo disable id > %s/soidle3_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("bypass pll: ");
|
|
mt_idle_log("echo bypass_pll 1/0 > %s/soidle3_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("bypass cg: ");
|
|
mt_idle_log("echo bypass 1/0 > %s/soidle3_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("bypass en: ");
|
|
mt_idle_log("echo bypass_en 1/0 > %s/soidle3_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("sodi3 flags: ");
|
|
mt_idle_log("echo sodi3_flags value > %s/soidle3_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("\t[0] reduce log, [1] residency, [2] resource usage\n");
|
|
|
|
return p - ToUserBuf;
|
|
}
|
|
|
|
static ssize_t soidle3_state_write(char *FromUserBuf, size_t sz, void *priv)
|
|
{
|
|
char cmd[128];
|
|
int param;
|
|
|
|
if (sscanf(FromUserBuf, "%127s %d", cmd, ¶m) == 2) {
|
|
if (!strcmp(cmd, "soidle3"))
|
|
idle_switch[IDLE_TYPE_SO3] = param;
|
|
else if (!strcmp(cmd, "enable"))
|
|
enable_idle_by_bit(IDLE_TYPE_SO3, param);
|
|
else if (!strcmp(cmd, "disable"))
|
|
disable_idle_by_bit(IDLE_TYPE_SO3, param);
|
|
else if (!strcmp(cmd, "bypass_pll")) {
|
|
soidle3_by_pass_pll = param;
|
|
idle_dbg("bypass_pll = %d\n", soidle3_by_pass_pll);
|
|
} else if (!strcmp(cmd, "bypass")) {
|
|
soidle3_by_pass_cg = param;
|
|
idle_dbg("bypass = %d\n", soidle3_by_pass_cg);
|
|
} else if (!strcmp(cmd, "bypass_en")) {
|
|
soidle3_by_pass_en = param;
|
|
idle_dbg("bypass_en = %d\n", soidle3_by_pass_en);
|
|
} else if (!strcmp(cmd, "bypass_pwm")) {
|
|
soidle3_by_pass_disp_pwm = param;
|
|
idle_dbg("bypass_pwm = %d\n", soidle3_by_pass_disp_pwm);
|
|
} else if (!strcmp(cmd, "sodi3_flags")) {
|
|
sodi3_flags = param;
|
|
idle_dbg("sodi3_flags = 0x%x\n", sodi3_flags);
|
|
}
|
|
return sz;
|
|
} else if ((!kstrtoint(FromUserBuf, 10, ¶m)) == 1) {
|
|
idle_switch[IDLE_TYPE_SO3] = param;
|
|
return sz;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static const struct mtk_lp_sysfs_op soidle3_state_fops = {
|
|
.fs_read = soidle3_state_read,
|
|
.fs_write = soidle3_state_write,
|
|
};
|
|
|
|
|
|
/* soidle_state */
|
|
static ssize_t soidle_state_read(char *ToUserBuf, size_t sz_t, void *priv)
|
|
{
|
|
int i;
|
|
char *p = ToUserBuf;
|
|
size_t sz = sz_t;
|
|
|
|
#undef mt_idle_log
|
|
#define mt_idle_log(fmt, args...) \
|
|
do { \
|
|
int l = scnprintf(p, sz, fmt, ##args); \
|
|
p += l; \
|
|
sz -= l; \
|
|
} while (0)
|
|
|
|
mt_idle_log("*********** soidle state ************\n");
|
|
for (i = 0; i < NR_REASONS; i++)
|
|
mt_idle_log("[%d]soidle_block_cnt[%s]=%lu\n",
|
|
i, mtk_get_reason_name(i),
|
|
soidle_block_cnt[i]);
|
|
mt_idle_log("\n");
|
|
|
|
for (i = 0; i < NR_GRPS; i++) {
|
|
mt_idle_log("[%02d]soidle_condition_mask[%-10s]=0x%08x\t\t",
|
|
i, mtk_get_cg_group_name(i),
|
|
idle_condition_mask[IDLE_TYPE_SO][i]);
|
|
mt_idle_log("soidle_block_mask[%-10s]=0x%08x\n",
|
|
mtk_get_cg_group_name(i),
|
|
idle_block_mask[IDLE_TYPE_SO][i]);
|
|
}
|
|
|
|
mt_idle_log("soidle pg_stat=0x%08x\n",
|
|
idle_block_mask[IDLE_TYPE_SO][NR_GRPS + 1]);
|
|
|
|
mt_idle_log("soidle_bypass_cg=%u\n", soidle_by_pass_cg);
|
|
mt_idle_log("soidle_by_pass_pg=%u\n", soidle_by_pass_pg);
|
|
mt_idle_log("soidle_bypass_en=%u\n", soidle_by_pass_en);
|
|
mt_idle_log("sodi_flags=0x%x\n", sodi_flags);
|
|
|
|
mt_idle_log("\n*********** soidle command help ************\n");
|
|
mt_idle_log("soidle help: ");
|
|
mt_idle_log("cat %s/soidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("switch on/off: ");
|
|
mt_idle_log("echo [soidle] 1/0 > %s/soidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("en_dp_by_bit: ");
|
|
mt_idle_log("echo enable id > %s/soidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("dis_dp_by_bit: ");
|
|
mt_idle_log("echo disable id > %s/soidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("bypass cg: ");
|
|
mt_idle_log("echo bypass 1/0 > %s/soidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("bypass en: ");
|
|
mt_idle_log("echo bypass_en 1/0 > %s/soidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("sodi flags: ");
|
|
mt_idle_log("echo sodi_flags value > %s/soidle_state\n", SYSFS_ENTRY);
|
|
mt_idle_log("\t[0] reduce log, [1] residency, [2] resource usage\n");
|
|
|
|
return p - ToUserBuf;
|
|
}
|
|
|
|
|
|
static ssize_t soidle_state_write(char *FromUserBuf, size_t sz, void *priv)
|
|
{
|
|
char cmd[128];
|
|
int param;
|
|
|
|
if (sscanf(FromUserBuf, "%127s %d", cmd, ¶m) == 2) {
|
|
if (!strcmp(cmd, "soidle"))
|
|
idle_switch[IDLE_TYPE_SO] = param;
|
|
else if (!strcmp(cmd, "enable"))
|
|
enable_idle_by_bit(IDLE_TYPE_SO, param);
|
|
else if (!strcmp(cmd, "disable"))
|
|
disable_idle_by_bit(IDLE_TYPE_SO, param);
|
|
else if (!strcmp(cmd, "bypass")) {
|
|
soidle_by_pass_cg = param;
|
|
idle_dbg("bypass = %d\n", soidle_by_pass_cg);
|
|
} else if (!strcmp(cmd, "bypass_pg")) {
|
|
soidle_by_pass_pg = param;
|
|
idle_warn("bypass_pg = %d\n", soidle_by_pass_pg);
|
|
} else if (!strcmp(cmd, "bypass_en")) {
|
|
soidle_by_pass_en = param;
|
|
idle_dbg("bypass_en = %d\n", soidle_by_pass_en);
|
|
} else if (!strcmp(cmd, "sodi_flags")) {
|
|
sodi_flags = param;
|
|
idle_dbg("sodi_flags = 0x%x\n", sodi_flags);
|
|
}
|
|
return sz;
|
|
} else if ((!kstrtoint(FromUserBuf, 10, ¶m)) == 1) {
|
|
idle_switch[IDLE_TYPE_SO] = param;
|
|
return sz;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static const struct mtk_lp_sysfs_op soidle_state_fops = {
|
|
.fs_read = soidle_state_read,
|
|
.fs_write = soidle_state_write,
|
|
};
|
|
|
|
static int mtk_cpuidle_debugfs_init(void)
|
|
{
|
|
struct mtk_lp_sysfs_handle *pParent = NULL;
|
|
struct mtk_lp_sysfs_handle entry_cpuidle;
|
|
|
|
/* create /sys/kernel/mtk_lpm as root */
|
|
mtk_idle_sysfs_root_entry_create();
|
|
|
|
/* create /sys/kernel/mtk_lpm/cpuidle */
|
|
if (mtk_idle_sysfs_entry_root_get(&pParent) == 0) {
|
|
mtk_lp_sysfs_entry_func_create("cpuidle",
|
|
0444, pParent, &entry_cpuidle);
|
|
mtk_lp_sysfs_entry_func_node_add("idle_state",
|
|
0644, &idle_state_fops, &entry_cpuidle, NULL);
|
|
mtk_lp_sysfs_entry_func_node_add("soidle_state",
|
|
0644, &soidle_state_fops, &entry_cpuidle, NULL);
|
|
mtk_lp_sysfs_entry_func_node_add("dpidle_state",
|
|
0644, &dpidle_state_fops, &entry_cpuidle, NULL);
|
|
mtk_lp_sysfs_entry_func_node_add("soidle3_state",
|
|
0644, &soidle3_state_fops, &entry_cpuidle, NULL);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_MACH_MT6763)
|
|
void mtk_spm_dump_debug_info(void)
|
|
{
|
|
printk_deferred("[name:spm&]SPM_POWER_ON_VAL0 0x%08x\n",
|
|
spm_read(SPM_POWER_ON_VAL0));
|
|
printk_deferred("[name:spm&]SPM_POWER_ON_VAL1 0x%08x\n",
|
|
spm_read(SPM_POWER_ON_VAL1));
|
|
printk_deferred("[name:spm&]PCM_PWR_IO_EN 0x%08x\n",
|
|
spm_read(PCM_PWR_IO_EN));
|
|
printk_deferred("[name:spm&]PCM_REG0_DATA 0x%08x\n",
|
|
spm_read(PCM_REG0_DATA));
|
|
printk_deferred("[name:spm&]PCM_REG7_DATA 0x%08x\n",
|
|
spm_read(PCM_REG7_DATA));
|
|
printk_deferred("[name:spm&]PCM_REG12_DATA 0x%08x\n",
|
|
spm_read(PCM_REG12_DATA));
|
|
printk_deferred("[name:spm&]PCM_REG13_DATA 0x%08x\n",
|
|
spm_read(PCM_REG13_DATA));
|
|
printk_deferred("[name:spm&]PCM_REG15_DATA 0x%08x\n",
|
|
spm_read(PCM_REG15_DATA));
|
|
printk_deferred("[name:spm&]SPM_MAS_PAUSE_MASK_B 0x%08x\n",
|
|
spm_read(SPM_MAS_PAUSE_MASK_B));
|
|
printk_deferred("[name:spm&]SPM_MAS_PAUSE2_MASK_B 0x%08x\n",
|
|
spm_read(SPM_MAS_PAUSE2_MASK_B));
|
|
printk_deferred("[name:spm&]SPM_SW_FLAG 0x%08x\n",
|
|
spm_read(SPM_SW_FLAG));
|
|
printk_deferred("[name:spm&]SPM_DEBUG_FLAG 0x%08x\n",
|
|
spm_read(SPM_SW_DEBUG));
|
|
printk_deferred("[name:spm&]SPM_PC_TRACE_G0 0x%08x\n",
|
|
spm_read(SPM_PC_TRACE_G0));
|
|
printk_deferred("[name:spm&]SPM_PC_TRACE_G1 0x%08x\n",
|
|
spm_read(SPM_PC_TRACE_G1));
|
|
printk_deferred("[name:spm&]SPM_PC_TRACE_G2 0x%08x\n",
|
|
spm_read(SPM_PC_TRACE_G2));
|
|
printk_deferred("[name:spm&]SPM_PC_TRACE_G3 0x%08x\n",
|
|
spm_read(SPM_PC_TRACE_G3));
|
|
printk_deferred("[name:spm&]SPM_PC_TRACE_G4 0x%08x\n",
|
|
spm_read(SPM_PC_TRACE_G4));
|
|
printk_deferred("[name:spm&]SPM_PC_TRACE_G5 0x%08x\n",
|
|
spm_read(SPM_PC_TRACE_G5));
|
|
printk_deferred("[name:spm&]SPM_PC_TRACE_G6 0x%08x\n",
|
|
spm_read(SPM_PC_TRACE_G6));
|
|
printk_deferred("[name:spm&]SPM_PC_TRACE_G7 0x%08x\n",
|
|
spm_read(SPM_PC_TRACE_G7));
|
|
printk_deferred("[name:spm&]DCHA_GATING_LATCH_0 0x%08x\n",
|
|
spm_read(DCHA_GATING_LATCH_0));
|
|
printk_deferred("[name:spm&]DCHA_GATING_LATCH_5 0x%08x\n",
|
|
spm_read(DCHA_GATING_LATCH_5));
|
|
printk_deferred("[name:spm&]DCHB_GATING_LATCH_0 0x%08x\n",
|
|
spm_read(DCHB_GATING_LATCH_0));
|
|
printk_deferred("[name:spm&]DCHB_GATING_LATCH_5 0x%08x\n",
|
|
spm_read(DCHB_GATING_LATCH_5));
|
|
}
|
|
#endif
|
|
|
|
void mtk_idle_gpt_init(void)
|
|
{
|
|
#ifndef USING_STD_TIMER_OPS
|
|
int err = 0;
|
|
|
|
err = request_gpt(IDLE_GPT, GPT_ONE_SHOT,
|
|
GPT_CLK_SRC_SYS, GPT_CLK_DIV_1,
|
|
0, NULL, GPT_NOAUTOEN);
|
|
if (err)
|
|
idle_warn("[%s] fail to request GPT %d\n",
|
|
__func__, IDLE_GPT + 1);
|
|
#endif
|
|
}
|
|
|
|
static void mtk_idle_profile_init(void)
|
|
{
|
|
mtk_idle_twam_init();
|
|
mtk_idle_block_setting(IDLE_TYPE_DP, dpidle_cnt,
|
|
dpidle_block_cnt,
|
|
idle_block_mask[IDLE_TYPE_DP]);
|
|
mtk_idle_block_setting(IDLE_TYPE_SO3, soidle3_cnt,
|
|
soidle3_block_cnt,
|
|
idle_block_mask[IDLE_TYPE_SO3]);
|
|
mtk_idle_block_setting(IDLE_TYPE_SO, soidle_cnt,
|
|
soidle_block_cnt,
|
|
idle_block_mask[IDLE_TYPE_SO]);
|
|
mtk_idle_block_setting(IDLE_TYPE_RG, rgidle_cnt, NULL, NULL);
|
|
}
|
|
|
|
void mtk_idle_set_clkmux_addr(void)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < NF_CLK_CFG; i++)
|
|
clkmux_addr[i] = 0x10000040 + i * 0x10;
|
|
}
|
|
|
|
void __init mtk_cpuidle_framework_init(void)
|
|
{
|
|
#if !defined(CONFIG_MACH_MT6739)
|
|
idle_ver("[%s]entry!!\n", __func__);
|
|
#endif
|
|
|
|
#if defined(CONFIG_MACH_MT6763)
|
|
#ifdef CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES
|
|
/* Note: disable sodi/dpidle in mduse projects.
|
|
* k63v1_64_mduse
|
|
* k63v1_64_op01_mduse
|
|
* k63v1_64_op02_lwtg_mduse
|
|
* k63v1_64_op09_lwcg_mduse
|
|
*/
|
|
if (strstr(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES,
|
|
"_mduse") != NULL) {
|
|
|
|
idle_switch[IDLE_TYPE_DP] = 0;
|
|
idle_switch[IDLE_TYPE_SO3] = 0;
|
|
idle_switch[IDLE_TYPE_SO] = 0;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(CONFIG_MACH_MT6739)
|
|
#if defined(CONFIG_MTK_PMIC) || defined(CONFIG_MTK_PMIC_NEW_ARCH)
|
|
if (PMIC_LP_CHIP_VER() == 1) {
|
|
/* Note: For E1, disable Vproc/Vproc_sram DVS for SO/SO3/DP. */
|
|
slp_spm_SODI3_flags |= SPM_FLAG_DIS_VPROC_VSRAM_DVS;
|
|
slp_spm_SODI_flags |= SPM_FLAG_DIS_VPROC_VSRAM_DVS;
|
|
slp_spm_deepidle_flags |= SPM_FLAG_DIS_VPROC_VSRAM_DVS;
|
|
/* Note: For E1, disable Vcore_sram adjustment */
|
|
slp_spm_SODI3_flags |= SPM_FLAG_DISABLE_VCORE_SRAM_ADJ;
|
|
slp_spm_SODI_flags |= SPM_FLAG_DISABLE_VCORE_SRAM_ADJ;
|
|
slp_spm_deepidle_flags |= SPM_FLAG_DISABLE_VCORE_SRAM_ADJ;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
iomap_init();
|
|
mtk_cpuidle_debugfs_init();
|
|
|
|
mtk_idle_gpt_init();
|
|
|
|
dpidle_by_pass_pg = false;
|
|
|
|
mtk_idle_set_clkmux_addr();
|
|
mtk_idle_profile_init();
|
|
}
|
|
EXPORT_SYMBOL(mtk_cpuidle_framework_init);
|
|
|