Initial commit: Linux kernel 4.19 for Unplugged OS

This commit is contained in:
ziv committed 2025-10-06 13:52:42 +00:00
commit d30235b04c
80519 files changed
+35800681

No files matched your search

@@ -0,0 +1,8 @@
obj-$(CONFIG_MTK_LPM_DBG_COMMON) += common/
obj-$(CONFIG_MTK_LPM_MT6781) += mt6781/
obj-$(CONFIG_MTK_LPM_MT6885) += mt6885/
obj-$(CONFIG_MTK_LPM_MT6877) += mt6877/
obj-$(CONFIG_MTK_LPM_MT6779) += mt6779/
obj-$(CONFIG_MTK_LPM_MT6833) += mt6833/
obj-$(CONFIG_MTK_LPM_MT6853) += mt6853/
obj-$(CONFIG_MTK_LPM_MT6873) += mt6873/
@@ -0,0 +1,12 @@
MTK_LPM_GS_DUMP_VERSION = $(strip $(subst ",, $(CONFIG_MTK_LPM_GS_DUMP_VERSION)))
ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/include/power_gs_v1
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/inc/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/mtk_lpm_fs/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/modules/include/
obj-$(CONFIG_MTK_LPM_DBG_COMMON) += mtk_dbg_fs_common.o
obj-$(CONFIG_MTK_LPM_DBG_COMMON) += mtk_lpm_timer.o
obj-$(CONFIG_MTK_LPM_GS_DUMP_SUPPORT) += mtk_lpm_pwr_gs.o
obj-$(CONFIG_MTK_LPM_GS_DUMP_SUPPORT) += gs/${MTK_LPM_GS_DUMP_VERSION}/mtk_lpm_pwr_gs_common.o
obj-$(CONFIG_MTK_LPM_GS_DUMP_SUPPORT) += gs/${MTK_LPM_GS_DUMP_VERSION}/mtk_lpm_pwr_gs_dump_clk.o
obj-$(CONFIG_MTK_LPM_GS_DUMP_SUPPORT) += gs/${MTK_LPM_GS_DUMP_VERSION}/mtk_lpm_pwr_gs_dump_pmic.o
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <gs/mtk_lpm_pwr_gs.h>
#include <gs/v1/mtk_lpm_pwr_gs_internal.h>
int mtk_lpm_pwr_gs_common_init(void)
{
mtk_lpm_gs_dump_pmic_init();
mtk_lpm_gs_dump_dcm_init();
return 0;
}
void mtk_lpm_pwr_gs_common_deinit(void)
{
mtk_lpm_gs_dump_pmic_deinit();
mtk_lpm_gs_dump_dcm_deinit();
}
@@ -0,0 +1,254 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <gs/mtk_lpm_pwr_gs.h>
#include <gs/v1/mtk_power_gs.h>
#include <mt-plat/upmu_common.h>
#define MTK_LPM_GS_CMP_SEARCH (1u << 0)
struct mtk_lpm_gs_dcm_info_inst {
struct mtk_lpm_gs_clk_info *info;
};
static struct base_remap br;
#define DEBUG_BUF_SIZE 2048
static char buf[DEBUG_BUF_SIZE] = { 0 };
static struct mtk_lpm_gs_dcm_info_inst mtk_lpm_gs_clks;
void __iomem *_get_virt_base_from_table(unsigned int pa)
{
unsigned int k;
void __iomem *io_base = 0;
if (br.table) {
for (k = 0; k < br.table_pos; k++)
if (br.table[k].pa == pa)
return (io_base = br.table[k].va);
} else
pr_info("Power_gs: cannot find virtual address\n");
return io_base;
}
/* Check phys addr is existed in table or not */
bool _is_exist_in_phys_to_virt_table(unsigned int pa)
{
unsigned int k;
if (br.table)
for (k = 0; k < br.table_pos; k++)
if (br.table[k].pa == pa)
return true;
return false;
}
unsigned int mt_power_gs_base_remap_init(char *scenario, char *pmic_name,
const unsigned int *pmic_gs, unsigned int pmic_gs_len)
{
unsigned int i, base;
struct phys_to_virt_table *table;
if (!br.table)
br.table = kmalloc(sizeof(struct phys_to_virt_table) *
REMAP_SIZE_MASK + 1, GFP_KERNEL);
if (br.table) {
table = br.table;
for (i = 0; i < pmic_gs_len; i += 3) {
base = (pmic_gs[i] & (~(unsigned long)REMAP_SIZE_MASK));
if (!_is_exist_in_phys_to_virt_table(base)) {
table[br.table_pos].pa = base;
table[br.table_pos].va =
ioremap_nocache(base,
REMAP_SIZE_MASK + 1);
if (!table[br.table_pos].va)
pr_info("ioremap_nocache(0x%x, 0x%x)\n",
base, REMAP_SIZE_MASK + 1);
if (br.table_pos < br.table_size)
br.table_pos++;
else {
pr_info("base_remap in maximum size\n");
return 0;
}
}
}
}
return 0;
}
static u16 gs_pmic_read(u16 reg)
{
u32 reg_val = 0;
#if defined(CONFIG_MTK_PMIC_NEW_ARCH)
u32 ret = 0;
ret = pmic_read_interface_nolock(reg, &reg_val, 0xFFFF, 0x0);
#endif
return (u16)reg_val;
}
static bool _is_pmic_addr(unsigned int addr)
{
return (addr >> 16) ? 0 : 1;
}
unsigned int _golden_read_reg(unsigned int addr)
{
unsigned int reg_val;
unsigned int base = addr & (~(unsigned long)REMAP_SIZE_MASK);
unsigned int offset = addr & (unsigned long)REMAP_SIZE_MASK;
void __iomem *io_base;
if (_is_pmic_addr(addr))
reg_val = gs_pmic_read(addr);
else {
#if 0
io_base = ioremap_nocache(base, REMAP_SIZE_MASK+1);
reg_val = ioread32(io_base + offset);
iounmap(io_base);
#else
io_base = _get_virt_base_from_table(base);
if (io_base)
reg_val = ioread32(io_base + offset);
else
reg_val = 0;
#endif
}
return reg_val;
}
#define PER_LINE_TO_PRINT 8
static void mt_power_gs_clk_compare(char *pmic_name,
struct mtk_lpm_gs_clk_user *user)
{
unsigned int i, k, val0, val1, val2, diff, dump_cnt = 0;
char *p;
/* dump diff mode */
p = buf;
p += snprintf(p, sizeof(buf), "\n");
p += snprintf(p, sizeof(buf) - (p - buf),
"Scenario - %s - Addr - Value - Mask - Golden - Wrong Bit\n",
pmic_name);
for (i = 0; i < user->array_sz; i += 3) {
val0 = _golden_read_reg(user->array[i]);
val1 = val0 & user->array[i + 1];
val2 = user->array[i + 2] & user->array[i + 1];
if (val1 != val2) {
dump_cnt++;
p += snprintf(p, sizeof(buf) - (p - buf),
"%s - %s - 0x%08x - 0x%08x - 0x%08x - 0x%08x -",
user->name, pmic_name, user->array[i], val0,
user->array[i + 1], user->array[i + 2]);
for (k = 0, diff = val1 ^ val2; diff != 0; k++,
diff >>= 1) {
if ((diff % 2) != 0)
p += snprintf(p,
sizeof(buf) - (p - buf),
" %d", k);
}
p += snprintf(p, sizeof(buf) - (p - buf), "\n");
if (dump_cnt &&
((dump_cnt % PER_LINE_TO_PRINT) == 0)) {
pr_notice("%s", buf);
p = buf;
p += snprintf(p, sizeof(buf), "\n");
}
}
}
if (dump_cnt % PER_LINE_TO_PRINT)
pr_notice("%s", buf);
}
int mtk_lpm_gs_clk_cmp_init(void *data)
{
int i = 0;
struct mtk_lpm_gs_clk_info *_info;
struct mtk_lpm_gs_clk *const *_dcm;
if (!data)
return -EINVAL;
_info = (struct mtk_lpm_gs_clk_info *)data;
for (i = 0, _dcm = _info->dcm; *_dcm ; i++, _dcm++) {
if (_info->attach) {
int ret = 0;
ret = _info->attach((*_dcm));
if (!ret) {
int idx = 0;
for (idx = 0; idx < MTK_LPM_PWR_GS_TYPE_MAX;
++idx) {
mt_power_gs_base_remap_init(
(*_dcm)->user[idx].name,
(*_dcm)->name,
(*_dcm)->user[idx].array,
(*_dcm)->user[idx].array_sz);
}
}
}
}
mtk_lpm_gs_clks.info = _info;
return 0;
}
int __mtk_lpm_gs_clk_cmp(unsigned int flag, unsigned int type, int user)
{
int i = 0, is_found;
struct mtk_lpm_gs_clk_info *_info = mtk_lpm_gs_clks.info;
struct mtk_lpm_gs_clk *const *_dcm;
for (i = 0, _dcm = _info->dcm; *_dcm ; i++, _dcm++) {
is_found = (flag & MTK_LPM_GS_CMP_SEARCH) ? 0 : 1;
if (!is_found && ((*_dcm)->type == type))
is_found = 1;
if (is_found)
mt_power_gs_clk_compare((*_dcm)->name,
&((*_dcm)->user[user]));
}
return 0;
}
int mtk_lpm_gs_clk_cmp(int user)
{
return __mtk_lpm_gs_clk_cmp(0, 0, user);
}
int mtk_lpm_gs_clk_cmp_by_type(int user, unsigned int type)
{
return __mtk_lpm_gs_clk_cmp(MTK_LPM_GS_CMP_SEARCH, type, user);
}
struct mtk_lpm_gs_cmp mtk_lpm_gs_cmp_clk = {
.cmp_init = mtk_lpm_gs_clk_cmp_init,
.cmp = mtk_lpm_gs_clk_cmp,
.cmp_by_type = mtk_lpm_gs_clk_cmp_by_type,
};
int mtk_lpm_gs_dump_dcm_init(void)
{
mtk_lpm_pwr_gs_compare_register(MTK_LPM_GS_CMP_CLK, &mtk_lpm_gs_cmp_clk);
return 0;
}
void mtk_lpm_gs_dump_dcm_deinit(void)
{
mtk_lpm_pwr_gs_compare_unregister(MTK_LPM_GS_CMP_CLK);
}
@@ -0,0 +1,189 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <gs/mtk_lpm_pwr_gs.h>
#include <gs/v1/mtk_power_gs.h>
#define PER_LINE_TO_PRINT 8
#define DEBUG_BUF_SIZE 2048
static char buf[DEBUG_BUF_SIZE] = { 0 };
static int slp_chk_golden_diff_mode = 1;
struct mtk_lpm_gs_pmic_info_inst {
unsigned int count_pmic;
struct mtk_lpm_gs_pmic_info *info;
};
static struct mtk_lpm_gs_pmic_info_inst mtk_lpm_pmics;
void generic_dump_pmic(const char *pmic_name, int pmic_num,
struct mtk_lpm_gs_pmic_user *user,
struct regmap *regmap)
{
unsigned int i, k, val0 = 0, val1, val2, diff, dump_cnt = 0, ret;
char *p;
if (!pmic_name || !user)
goto ERROR;
if (IS_ERR(regmap)) {
pr_info("%s regmap get failed\n", __func__);
goto ERROR;
}
/* dump diff mode */
if (slp_chk_golden_diff_mode) {
p = buf;
p += snprintf(p, sizeof(buf), "\n");
p += snprintf(p, sizeof(buf) - (p - buf),
"Scenario - %s - Addr - Value - Mask - Golden - Wrong Bit\n",
pmic_name);
for (i = 0; i < user->array_sz; i += 3) {
ret = regmap_read(regmap, user->array[i], &val0);
if (ret) {
pr_info("read pmic-%d 0x%x error\n",
pmic_num, user->array[i]);
goto ERROR;
}
val1 = val0 & user->array[i + 1];
val2 = user->array[i + 2] & user->array[i + 1];
if (val1 != val2) {
dump_cnt++;
p += snprintf(p, sizeof(buf) - (p - buf),
"%s - %s - 0x%08x - 0x%08x- 0x%08x - 0x%08x -",
user->name, pmic_name, user->array[i], val0,
user->array[i + 1], user->array[i + 2]);
for (k = 0, diff = val1 ^ val2; diff != 0; k++,
diff >>= 1) {
if ((diff % 2) != 0)
p += snprintf(p,
sizeof(buf) - (p - buf),
" %d", k);
}
p += snprintf(p, sizeof(buf) - (p - buf), "\n");
if (dump_cnt &&
((dump_cnt % PER_LINE_TO_PRINT) == 0)) {
pr_info("%s", buf);
p = buf;
p += snprintf(p, sizeof(buf), "\n");
}
}
}
if (dump_cnt % PER_LINE_TO_PRINT)
pr_info("%s", buf);
/* dump raw data mode */
} else {
p = buf;
p += snprintf(p, sizeof(buf), "\n");
p += snprintf(p, sizeof(buf) - (p - buf),
"Scenario - PMIC - Addr - Value\n");
for (i = 0; i < user->array_sz; i += 3) {
ret = regmap_read(regmap, user->array[i], &val0);
if (ret) {
pr_info("read pmic-%d 0x%x error\n",
pmic_num, user->array[i]);
goto ERROR;
}
dump_cnt++;
p += snprintf(p, sizeof(buf) - (p - buf),
"%s - %s-%d - 0x%08x - 0x%08x\n",
user->name, pmic_name, pmic_num,
user->array[i], val0);
if (dump_cnt && ((dump_cnt % PER_LINE_TO_PRINT) == 0)) {
pr_info("%s", buf);
p = buf;
p += snprintf(p, sizeof(buf), "\n");
}
}
if (dump_cnt % PER_LINE_TO_PRINT)
pr_info("%s", buf);
}
ERROR:
return;
}
int mtk_lpm_gs_pmic_cmp_init(void *data)
{
int i = 0;
struct mtk_lpm_gs_pmic_info *_info;
struct mtk_lpm_gs_pmic *const *_pmic;
if (!data)
return -EINVAL;
_info = (struct mtk_lpm_gs_pmic_info *)data;
for (i = 0, _pmic = _info->pmic; *_pmic ; i++, _pmic++) {
if (_info->attach)
_info->attach((*_pmic));
}
mtk_lpm_pmics.count_pmic = i;
mtk_lpm_pmics.info = _info;
return 0;
}
int mtk_lpm_gs_pmic_cmp(int user)
{
int i = 0;
struct mtk_lpm_gs_pmic_info *_info = mtk_lpm_pmics.info;
struct mtk_lpm_gs_pmic *const *_pmic;
struct regulator *regulator;
struct regmap *regmap;
for (i = 0, _pmic = _info->pmic; *_pmic ; i++, _pmic++) {
regulator = regulator_get_optional(NULL, (*_pmic)->regulator);
if (IS_ERR(regulator)) {
pr_info("%s regulator get failed\n", __func__);
continue;
}
regmap = regulator_get_regmap(regulator);
if ((regmap == NULL) || IS_ERR(regmap)) {
pr_info("%s regmap get failed\n", __func__);
continue;
}
generic_dump_pmic((*_pmic)->pwr_domain, i,
&((*_pmic)->user[user]), regmap);
}
return 0;
}
struct mtk_lpm_gs_cmp mtk_lpm_gs_cmp_pmic = {
.cmp_init = mtk_lpm_gs_pmic_cmp_init,
.cmp = mtk_lpm_gs_pmic_cmp,
};
int mtk_lpm_gs_dump_pmic_init(void)
{
mtk_lpm_pwr_gs_compare_register(MTK_LPM_GS_CMP_PMIC,
&mtk_lpm_gs_cmp_pmic);
return 0;
}
void mtk_lpm_gs_dump_pmic_deinit(void)
{
mtk_lpm_pwr_gs_compare_unregister(MTK_LPM_GS_CMP_PMIC);
}
@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef _MTK_LPM_PWR_GS_INITERNAL_H__
#define _MTK_LPM_PWR_GS_INITERNAL_H__
int mtk_lpm_gs_dump_pmic_init(void);
void mtk_lpm_gs_dump_pmic_deinit(void);
int mtk_lpm_gs_dump_dcm_init(void);
void mtk_lpm_gs_dump_dcm_deinit(void);
#endif
@@ -0,0 +1,245 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/console.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/syscore_ops.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mtk_resource_constraint_v1.h>
#include <mtk_suspend_sysfs.h>
#include <mtk_spm_sysfs.h>
#define MTK_DGB_SUSP_NODE "/sys/kernel/debug/suspend/suspend_state"
#undef mtk_dbg_log
#define mtk_dbg_log(fmt, args...) \
do { \
int l = scnprintf(p, sz, fmt, ##args); \
p += l; \
sz -= l; \
} while (0)
static struct wakeup_source *mtk_suspend_lock;
/* debugfs for blocking syscore callback */
static int spm_syscore_block_suspend(void) { return -EINVAL; }
static void spm_syscore_block_resume(void) {}
static struct syscore_ops spm_block_syscore_ops = {
.suspend = spm_syscore_block_suspend,
.resume = spm_syscore_block_resume,
};
/* debugfs for debug in syscore callback */
static int spm_syscore_dbg_suspend(void)
{
return 0;
}
static void spm_syscore_dbg_resume(void) {}
static struct syscore_ops spm_dbg_syscore_ops = {
.suspend = spm_syscore_dbg_suspend,
.resume = spm_syscore_dbg_resume,
};
static ssize_t mtk_dbg_suspend_state_read(char *ToUser, size_t sz, void *priv)
{
char *p = ToUser;
if (!ToUser)
return -EINVAL;
mtk_dbg_log("mtk_suspend command help\n");
mtk_dbg_log("kernel suspend disable/enable:\n");
mtk_dbg_log("echo kernel_suspend 0/1 > %s\n", MTK_DGB_SUSP_NODE);
mtk_dbg_log("mtk_suspend disable/enable:\n");
mtk_dbg_log("echo mtk_suspend 0/1 > %s\n", MTK_DGB_SUSP_NODE);
return p - ToUser;
}
static ssize_t mtk_dbg_suspend_state_write(char *FromUser,
size_t sz, void *priv)
{
char cmd[128];
int param;
if (!FromUser)
return -EINVAL;
if (sscanf(FromUser, "%127s %d", cmd, &param) == 2) {
if (!strcmp(cmd, "kernel_suspend")) {
/* 0:require wakelock */
if (!param)
__pm_stay_awake(mtk_suspend_lock);
/* 1:release wakelock */
else
__pm_relax(mtk_suspend_lock);
} else if (!strcmp(cmd, "mtk_suspend")) {
/* 0:block in syscore */
if (!param)
register_syscore_ops(&spm_block_syscore_ops);
/* 1:unblock in syscore */
else
unregister_syscore_ops(&spm_block_syscore_ops);
}
return sz;
}
return -EINVAL;
}
static const struct mtk_lp_sysfs_op mtk_dbg_suspend_state_fops = {
.fs_read = mtk_dbg_suspend_state_read,
.fs_write = mtk_dbg_suspend_state_write,
};
static ssize_t mtk_dbg_get_spm_sleep_count(char *ToUserBuf,
size_t sz, void *priv)
{
int bLen;
bLen = snprintf(ToUserBuf, sz, "%lu\n",
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_SUSPEND_DBG_CTRL,
MT_LPM_SMC_ACT_GET,
DBG_CTRL_COUNT, 0));
return (bLen > sz) ? sz : bLen;
}
static const struct mtk_lp_sysfs_op mtk_dbg_spm_sleep_count_fops = {
.fs_read = mtk_dbg_get_spm_sleep_count,
};
static void mtk_dbg_suspend_fs_init(void)
{
mtk_suspend_sysfs_root_entry_create();
mtk_suspend_sysfs_entry_node_add("suspend_state"
, 0444, &mtk_dbg_suspend_state_fops, NULL);
mtk_suspend_sysfs_entry_node_add("spm_sleep_count"
, 0444, &mtk_dbg_spm_sleep_count_fops, NULL);
}
static ssize_t mtk_dbg_get_spm_last_wakeup_src(char *ToUserBuf,
size_t sz, void *priv)
{
int bLen = snprintf(ToUserBuf, sz, "0x%lx\n",
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_FS,
MT_LPM_SMC_ACT_GET,
WAKE_STA_R12, 0));
return (bLen > sz) ? sz : bLen;
}
static const struct mtk_lp_sysfs_op mtk_dbg_spm_last_wakesrc_fops = {
.fs_read = mtk_dbg_get_spm_last_wakeup_src,
};
static ssize_t mtk_dbg_get_spm_last_debug_flag(char *ToUserBuf,
size_t sz, void *priv)
{
int bLen = snprintf(ToUserBuf, sz, "0x%lx\n",
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_FS,
MT_LPM_SMC_ACT_GET,
WAKE_STA_DEBUG_FLAG, 0));
return (bLen > sz) ? sz : bLen;
}
static const struct mtk_lp_sysfs_op mtk_dbg_spm_last_debugflag_fops = {
.fs_read = mtk_dbg_get_spm_last_debug_flag,
};
static ssize_t mtk_dbg_get_spmfw_version(char *ToUserBuf,
size_t sz, void *priv)
{
int index = 0;
const char *version;
char *p = ToUserBuf;
struct device_node *node =
of_find_compatible_node(NULL, NULL, "mediatek,sleep");
if (node == NULL) {
mtk_dbg_log("No Found mediatek,mediatek,sleep\n");
goto return_size;
}
while (!of_property_read_string_index(node,
"spmfw_version", index, &version)) {
mtk_dbg_log("%d: %s\n", index, version);
index++;
}
mtk_dbg_log("spmfw index: %lu\n",
mtk_lpm_smc_spm(MT_SPM_SMC_UID_FW_TYPE,
MT_LPM_SMC_ACT_GET, 0, 0));
mtk_dbg_log("spmfw ready: %d\n",
(mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RC_SWITCH,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_DRAM, -1)
& MT_SPM_RC_VALID_FW) ? 1 : 0);
if (node)
of_node_put(node);
return_size:
return p - ToUserBuf;
}
static const struct mtk_lp_sysfs_op mtk_dbg_spm_spmfw_ver_fops = {
.fs_read = mtk_dbg_get_spmfw_version,
};
static void mtk_dbg_spm_fs_init(void)
{
mtk_spm_sysfs_root_entry_create();
mtk_spm_sysfs_entry_node_add("spm_last_wakeup_src", 0444
, &mtk_dbg_spm_last_wakesrc_fops, NULL);
mtk_spm_sysfs_entry_node_add("spm_last_debug_flag", 0444
, &mtk_dbg_spm_last_debugflag_fops, NULL);
mtk_spm_sysfs_entry_node_add("spmfw_version", 0444
, &mtk_dbg_spm_spmfw_ver_fops, NULL);
}
static bool mtk_system_console_suspend;
void mtk_dbg_common_fs_exit(void)
{
/* restore suspend console */
console_suspend_enabled = mtk_system_console_suspend;
/* wakeup source deinit */
wakeup_source_unregister(mtk_suspend_lock);
/* remove syscore callback */
unregister_syscore_ops(&spm_block_syscore_ops);
unregister_syscore_ops(&spm_dbg_syscore_ops);
}
int mtk_dbg_common_fs_init(void)
{
/* wakeup source init for suspend enable and disable */
mtk_suspend_lock = wakeup_source_register(NULL, "mtk_suspend_wakelock");
if (!mtk_suspend_lock) {
pr_info("%s %d: init wakeup source fail!", __func__, __LINE__);
return -1;
}
/* backup and disable suspend console (enable log print) */
mtk_system_console_suspend = console_suspend_enabled;
console_suspend_enabled = false;
mtk_dbg_suspend_fs_init();
mtk_dbg_spm_fs_init();
register_syscore_ops(&spm_dbg_syscore_ops);
pr_info("%s %d: finish", __func__, __LINE__);
return 0;
}
@@ -0,0 +1,100 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <gs/mtk_lpm_pwr_gs.h>
static DEFINE_SPINLOCK(mtk_lpm_gs_locker);
static struct mtk_lpm_gs_cmp *mtk_lpm_gs_cmps[MTK_LPM_GS_CMP_MAX];
int mtk_lpm_pwr_gs_compare_register(int comparer, struct mtk_lpm_gs_cmp *cmp)
{
if ((comparer < 0) || (comparer >= MTK_LPM_GS_CMP_MAX)
|| !cmp || mtk_lpm_gs_cmps[comparer])
return -EINVAL;
spin_lock(&mtk_lpm_gs_locker);
mtk_lpm_gs_cmps[comparer] = cmp;
if (mtk_lpm_gs_cmps[comparer]->init)
mtk_lpm_gs_cmps[comparer]->init();
spin_unlock(&mtk_lpm_gs_locker);
return 0;
}
void mtk_lpm_pwr_gs_compare_unregister(int comparer)
{
if ((comparer < 0) || (comparer >= MTK_LPM_GS_CMP_MAX))
return;
spin_lock(&mtk_lpm_gs_locker);
if (mtk_lpm_gs_cmps[comparer] && mtk_lpm_gs_cmps[comparer]->deinit)
mtk_lpm_gs_cmps[comparer]->deinit();
mtk_lpm_gs_cmps[comparer] = NULL;
spin_unlock(&mtk_lpm_gs_locker);
}
int mtk_lpm_pwr_gs_compare(int comparer, int user)
{
int ret = 0;
if ((comparer < 0) || (comparer >= MTK_LPM_GS_CMP_MAX)
|| !mtk_lpm_gs_cmps[comparer])
return -EINVAL;
if (mtk_lpm_gs_cmps[comparer]->cmp) {
spin_lock(&mtk_lpm_gs_locker);
mtk_lpm_gs_cmps[comparer]->cmp(user);
spin_unlock(&mtk_lpm_gs_locker);
} else
ret = -EACCES;
return ret;
}
int mtk_lpm_pwr_gs_compare_by_type(int comparer, int user,
unsigned int type)
{
int ret = 0;
if ((comparer < 0) || (comparer >= MTK_LPM_GS_CMP_MAX)
|| !mtk_lpm_gs_cmps[comparer])
return -EINVAL;
if ((user < 0) || (user >= MTK_LPM_GS_CMP_MAX))
return -EINVAL;
spin_lock(&mtk_lpm_gs_locker);
if (mtk_lpm_gs_cmps[comparer]->cmp_by_type)
mtk_lpm_gs_cmps[comparer]->cmp_by_type(user, type);
else if (mtk_lpm_gs_cmps[comparer]->cmp)
mtk_lpm_gs_cmps[comparer]->cmp(user);
else
ret = -EACCES;
spin_unlock(&mtk_lpm_gs_locker);
return ret;
}
int mtk_lpm_pwr_gs_compare_init(int comparer, void *info)
{
int ret = 0;
if ((comparer < 0) || (comparer >= MTK_LPM_GS_CMP_MAX)
|| !mtk_lpm_gs_cmps[comparer])
return -EINVAL;
if (mtk_lpm_gs_cmps[comparer]->cmp_init) {
spin_lock(&mtk_lpm_gs_locker);
mtk_lpm_gs_cmps[comparer]->cmp_init(info);
spin_unlock(&mtk_lpm_gs_locker);
} else
ret = -EACCES;
return ret;
}
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <mtk_lpm_timer.h>
#define MTK_LPM_TIMER_GET_INTERVAL(ms) msecs_to_jiffies(ms)
#define MTK_LPM_TIMER_GET_TIME(jif) jiffies_to_msecs(jif)
#define MTK_LPM_TIMER_NEXT_INTERVAL(interval) (jiffies + interval)
static void mtk_lpm_timer_tm_func(struct timer_list *t)
{
int ret = -EINVAL;
struct mt_lpm_timer *timer = from_timer(timer, t, tm);
if (timer && timer->timeout) {
unsigned long dur = jiffies - timer->cur;
ret = timer->timeout(MTK_LPM_TIMER_GET_TIME(dur),
timer->priv);
}
if (ret)
return;
if (timer->type == MTK_LPM_TIMER_REPEAT) {
mod_timer(&timer->tm,
MTK_LPM_TIMER_NEXT_INTERVAL(timer->interval));
timer->cur = jiffies;
}
}
static int __is_mtk_lpm_timer_running(struct mt_lpm_timer *timer)
{
if (!timer)
return -EINVAL;
return timer_pending(&timer->tm);
}
int mtk_lpm_timer_init(struct mt_lpm_timer *timer, int type)
{
if (!timer)
return -EINVAL;
timer_setup(&timer->tm, mtk_lpm_timer_tm_func,
TIMER_DEFERRABLE);
timer->interval = 0;
timer->type = type;
return 0;
}
void mtk_lpm_timer_stop(struct mt_lpm_timer *timer)
{
if (!timer)
return;
del_timer_sync(&timer->tm);
}
int mtk_lpm_timer_start(struct mt_lpm_timer *timer)
{
if (!timer || (timer->interval == 0))
return -EINVAL;
if (!__is_mtk_lpm_timer_running(timer)) {
mod_timer(&timer->tm,
MTK_LPM_TIMER_NEXT_INTERVAL(timer->interval));
timer->cur = jiffies;
}
return 0;
}
int mtk_lpm_timer_interval_update(struct mt_lpm_timer *timer,
unsigned long long ms)
{
if (!timer)
return -EINVAL;
timer->interval = MTK_LPM_TIMER_GET_INTERVAL(ms);
mod_timer_pending(&timer->tm,
MTK_LPM_TIMER_NEXT_INTERVAL(timer->interval));
return 0;
}
int mtk_lpm_timer_is_running(struct mt_lpm_timer *timer)
{
return __is_mtk_lpm_timer_running(timer);
}
unsigned long mtk_lpm_timer_interval(struct mt_lpm_timer *timer)
{
if (!timer)
return 0;
return MTK_LPM_TIMER_GET_TIME(timer->interval);
}
@@ -0,0 +1,32 @@
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/inc/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/mtk_lpm_fs/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/modules/include/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/modules/include/mt6779/
BUILD_MTK_LPM_DBG_MODUDLE_NAME=mtk-lpm-dbg-$(strip $(subst ",, $(CONFIG_MTK_LPM_MODULE_NAME)))
obj-$(CONFIG_MTK_LPM_MT6779) += ${BUILD_MTK_LPM_DBG_MODUDLE_NAME}.o
ifeq ($(CONFIG_MTK_LPM_DBG_COMMON),$(filter $(CONFIG_MTK_LPM_DBG_COMMON),m y))
ccflags-y += -DMTK_LPM_DBG_COMMON
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += ../common/mtk_dbg_fs_common.o
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += ../common/mtk_lpm_timer.o
endif
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += mt6779_dbg.o
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += mt6779_logger.o
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += mt6779_dbg_fs.o
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += mt6779_dbg_idle_fs.o
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += mt6779_dbg_spm_fs.o
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += mtk_cpupm_dbg.o
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += mtk_cpuidle_status.o
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += mtk_cpuidle_cpc.o
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += mtk_idle_procfs.o
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += mtk_idle_procfs_state.o
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += mtk_idle_procfs_cpc.o
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += mtk_idle_procfs_profile.o
${BUILD_MTK_LPM_DBG_MODUDLE_NAME}-y += mtk_idle_procfs_control.o
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mt6779_dbg_fs_common.h>
static int __init mt6779_dbg_init(void)
{
#ifdef MTK_LPM_DBG_COMMON
mtk_dbg_common_fs_init();
#endif
mtk_cpupm_dbg_init();
mt6779_dbg_fs_init();
return 0;
}
static void __exit mt6779_dbg_exit(void)
{
#ifdef MTK_LPM_DBG_COMMON
mtk_dbg_common_fs_exit();
#endif
mt6779_dbg_fs_exit();
mtk_cpupm_dbg_exit();
}
module_init(mt6779_dbg_init);
module_exit(mt6779_dbg_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MT6779 Low Power debug KO");
MODULE_AUTHOR("MediaTek Inc.");
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mt6779_dbg_fs_common.h>
void mt6779_dbg_fs_exit(void)
{
mt6779_dbg_idle_fs_deinit();
mt6779_dbg_spm_fs_deinit();
}
int mt6779_dbg_fs_init(void)
{
mt6779_logger_init();
mt6779_dbg_idle_fs_init();
mt6779_dbg_spm_fs_init();
pr_info("%s %d: finish", __func__, __LINE__);
return 0;
}
@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MT6779_DBG_FS_COMMON_H__
#define __MT6779_DBG_FS_COMMON_H__
/* Determine for user name handle */
#define MT_LP_RQ_USER_NAME_LEN (4)
#define MT_LP_RQ_USER_CHAR_U (8)
#define MT_LP_RQ_USER_CHAR_MASK (0xFF)
/* Determine for resource usage id */
#define MT_LP_RQ_ID_ALL_USAGE (-1)
int mt6779_dbg_idle_fs_init(void);
int mt6779_dbg_idle_fs_deinit(void);
int mt6779_dbg_spm_fs_init(void);
int mt6779_dbg_spm_fs_deinit(void);
int mt6779_logger_init(void);
int mt6779_dbg_fs_init(void);
void mt6779_dbg_fs_exit(void);
int mtk_cpupm_dbg_init(void);
void mtk_cpupm_dbg_exit(void);
#endif /* __MTK_LP_PLAT_FS_H__ */
@@ -0,0 +1,290 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/console.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mtk_resource_constraint_v1.h>
#include <mtk_lpm_sysfs.h>
#include <mtk_suspend_sysfs.h>
#include <mtk_spm_sysfs.h>
#include <mt6779_pwr_ctrl.h>
#include <mt6779_dbg_fs_common.h>
#include <mt6779_cond.h>
#include <mt6779_spm_comm.h>
#define MT_LP_BUS26M_NODE "/sys/kernel/debug/cpuidle/IdleBus26m_state"
#define MT_LP_SYSPLL_NODE "/sys/kernel/debug/cpuidle/IdleSyspll_state"
#define MT_LP_DRAM_NODE "/sys/kernel/debug/cpuidle/IdleDram_state"
#undef mtk_idle_log
#define mtk_idle_log(fmt, args...) \
do { \
int l = scnprintf(p, sz, fmt, ##args); \
p += l; \
sz -= l; \
} while (0)
static char *mt6779_spm_cond_cg_str[PLAT_SPM_COND_MAX] = {
[PLAT_SPM_COND_MTCMOS_0] = "MTCMOS_0",
[PLAT_SPM_COND_MTCMOS_1] = "MTCMOS_1",
[PLAT_SPM_COND_CG_INFRA_0] = "INFRA_0",
[PLAT_SPM_COND_CG_INFRA_1] = "INFRA_1",
[PLAT_SPM_COND_CG_INFRA_2] = "INFRA_2",
[PLAT_SPM_COND_CG_INFRA_3] = "INFRA_3",
[PLAT_SPM_COND_CG_MMSYS_0] = "MMSYS_0",
[PLAT_SPM_COND_CG_MMSYS_1] = "MMSYS_1",
};
static char *mt6779_spm_cond_pll_str[PLAT_SPM_COND_PLL_MAX] = {
[PLAT_SPM_COND_UNIVPLL] = "UNIVPLL",
[PLAT_SPM_COND_MFGPLL] = "MFGPLL",
[PLAT_SPM_COND_MSDCPLL] = "MSDCPLL",
[PLAT_SPM_COND_TVPLL] = "TVPLL",
[PLAT_SPM_COND_MMPLL] = "MMPLL",
};
static ssize_t mt6779_idle_dram_read(char *ToUserBuf,
size_t sz, void *priv)
{
char *p = ToUserBuf;
uint32_t block, b;
int i;
block = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_COND_BLOCK,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_DRAM, 0);
mtk_idle_log("enable=%lu. count=%lu, is_cond_check = %lu\n\n",
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RC_SWITCH,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_DRAM, 0)
& MT_SPM_RC_VALID_SW,
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RC_CNT,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_DRAM, 0),
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_COND_CHECK,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_DRAM, 0));
mtk_idle_log("is_cond_block = 0x%08x\n", block);
for (i = 0, b = block >> SPM_COND_BLOCKED_CG_IDX;
i < PLAT_SPM_COND_MAX; i++)
mtk_idle_log("%8s = 0x%08lx\n", mt6779_spm_cond_cg_str[i],
((b >> i) & 0x1) ?
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_BLOCK_DETAIL,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_DRAM, i) : 0);
for (i = 0, b = block >> SPM_COND_BLOCKED_PLL_IDX;
i < PLAT_SPM_COND_PLL_MAX; i++)
mtk_idle_log("%8s = %d\n", mt6779_spm_cond_pll_str[i],
((b >> i) & 0x1));
mtk_idle_log("*************** idle command help ****************\n");
mtk_idle_log("echo cond_check 0/1 > %s\n", MT_LP_DRAM_NODE);
return p - ToUserBuf;
}
static ssize_t mt6779_idle_dram_write(char *FromUserBuf,
size_t sz, void *priv)
{
char cmd[128];
int parm;
if (sscanf(FromUserBuf, "%127s %x", cmd, &parm) == 2) {
if (!strcmp(cmd, "cond_check")) {
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_COND_CHECK,
MT_LPM_SMC_ACT_SET,
MT_RM_CONSTRAINT_ID_DRAM, !!parm);
}
return sz;
} else if ((!kstrtoint(FromUserBuf, 10, &parm)) == 1) {
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RC_SWITCH,
MT_LPM_SMC_ACT_SET,
MT_RM_CONSTRAINT_ID_DRAM, !!parm);
return sz;
}
return -EINVAL;
}
static const struct mtk_lp_sysfs_op mt6779_idle_dram_fops = {
.fs_read = mt6779_idle_dram_read,
.fs_write = mt6779_idle_dram_write,
};
static ssize_t mt6779_idle_syspll_read(char *ToUserBuf,
size_t sz, void *priv)
{
char *p = ToUserBuf;
uint32_t block, b;
int i;
block = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_COND_BLOCK,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_SYSPLL, 0);
mtk_idle_log("enable=%lu. count=%lu, is_cond_check = %lu\n\n",
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RC_SWITCH,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_SYSPLL, 0)
& MT_SPM_RC_VALID_SW,
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RC_CNT,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_SYSPLL, 0),
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_COND_CHECK,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_SYSPLL, 0));
mtk_idle_log("is_cond_block = 0x%08x\n", block);
for (i = 0, b = block >> SPM_COND_BLOCKED_CG_IDX;
i < PLAT_SPM_COND_MAX; i++)
mtk_idle_log("%8s = 0x%08lx\n", mt6779_spm_cond_cg_str[i],
((b >> i) & 0x1) ?
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_BLOCK_DETAIL,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_SYSPLL, i) : 0);
for (i = 0, b = block >> SPM_COND_BLOCKED_PLL_IDX;
i < PLAT_SPM_COND_PLL_MAX; i++)
mtk_idle_log("%8s = %d\n", mt6779_spm_cond_pll_str[i],
((b >> i) & 0x1));
mtk_idle_log("*************** idle command help ****************\n");
mtk_idle_log("echo cond_check 0/1 > %s\n", MT_LP_SYSPLL_NODE);
return p - ToUserBuf;
}
static ssize_t mt6779_idle_syspll_write(char *FromUserBuf,
size_t sz, void *priv)
{
char cmd[128];
int parm;
if (sscanf(FromUserBuf, "%127s %x", cmd, &parm) == 2) {
if (!strcmp(cmd, "cond_check")) {
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_COND_CHECK,
MT_LPM_SMC_ACT_SET,
MT_RM_CONSTRAINT_ID_SYSPLL, !!parm);
}
return sz;
} else if ((!kstrtoint(FromUserBuf, 10, &parm)) == 1) {
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RC_SWITCH,
MT_LPM_SMC_ACT_SET,
MT_RM_CONSTRAINT_ID_SYSPLL, !!parm);
return sz;
}
return -EINVAL;
}
static const struct mtk_lp_sysfs_op mt6779_idle_syspll_fops = {
.fs_read = mt6779_idle_syspll_read,
.fs_write = mt6779_idle_syspll_write,
};
static ssize_t mt6779_idle_bus26m_read(char *ToUserBuf,
size_t sz, void *priv)
{
char *p = ToUserBuf;
uint32_t block, b;
int i;
block = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_COND_BLOCK,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_BUS26M, 0);
mtk_idle_log("enable=%lu. count=%lu, is_cond_check = %lu\n\n",
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RC_SWITCH,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_BUS26M, 0)
& MT_SPM_RC_VALID_SW,
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RC_CNT,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_BUS26M, 0),
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_COND_CHECK,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_BUS26M, 0));
mtk_idle_log("is_cond_block = 0x%08X\n", block);
for (i = 0, b = block >> SPM_COND_BLOCKED_CG_IDX;
i < PLAT_SPM_COND_MAX; i++)
mtk_idle_log("%8s = 0x%08lX\n", mt6779_spm_cond_cg_str[i],
((b >> i) & 0x1) ?
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_BLOCK_DETAIL,
MT_LPM_SMC_ACT_GET,
MT_RM_CONSTRAINT_ID_BUS26M, i) : 0);
for (i = 0, b = block >> SPM_COND_BLOCKED_PLL_IDX;
i < PLAT_SPM_COND_PLL_MAX; i++)
mtk_idle_log("%8s = %d\n", mt6779_spm_cond_pll_str[i],
((b >> i) & 0x1));
mtk_idle_log("*************** idle command help ****************\n");
mtk_idle_log("echo cond_check 0/1 > %s\n", MT_LP_BUS26M_NODE);
return p - ToUserBuf;
}
static ssize_t mt6779_idle_bus26m_write(char *FromUserBuf,
size_t sz, void *priv)
{
char cmd[128];
int parm;
if (sscanf(FromUserBuf, "%127s %x", cmd, &parm) == 2) {
if (!strcmp(cmd, "cond_check")) {
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_COND_CHECK,
MT_LPM_SMC_ACT_SET,
MT_RM_CONSTRAINT_ID_BUS26M, !!parm);
}
return sz;
} else if ((!kstrtoint(FromUserBuf, 10, &parm)) == 1) {
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RC_SWITCH,
MT_LPM_SMC_ACT_SET,
MT_RM_CONSTRAINT_ID_BUS26M, !!parm);
return sz;
}
return -EINVAL;
}
static const struct mtk_lp_sysfs_op mt6779_idle_bus26m_fops = {
.fs_read = mt6779_idle_bus26m_read,
.fs_write = mt6779_idle_bus26m_write,
};
int mt6779_dbg_idle_fs_init(void)
{
/* enable resource constraint condition block latch */
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_BLOCK_LATCH,
MT_LPM_SMC_ACT_SET, 0, 0);
mtk_lpm_sysfs_root_entry_create();
mtk_lpm_sysfs_entry_node_add("IdleDram_state"
, 0644, &mt6779_idle_dram_fops, NULL);
mtk_lpm_sysfs_entry_node_add("IdleSyspll_state"
, 0644, &mt6779_idle_syspll_fops, NULL);
mtk_lpm_sysfs_entry_node_add("IdleBus26m_state"
, 0644, &mt6779_idle_bus26m_fops, NULL);
return 0;
}
int mt6779_dbg_idle_fs_deinit(void)
{
/* disable resource contraint condition block latch */
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_BLOCK_LATCH,
MT_LPM_SMC_ACT_CLR, 0, 0);
return 0;
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,321 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <aee.h>
#include <mtk_lpm.h>
#include <mt6779_spm_comm.h>
#include <mt6779_spm_reg.h>
#include <mt6779_pwr_ctrl.h>
#include <mt6779_pcm_def.h>
#include <mtk_dbg_common_v1.h>
#define BYPASS_LOG
#ifndef BYPASS_LOG
static struct mt6779_spm_wake_status mt6779_wake;
#endif
void __iomem *mt6779_spm_base;
const char *mt6779_wakesrc_str[32] = {
[0] = " R12_PCM_TIMER_EVENT",
[1] = " R12_SPM_TWAM_IRQ_B",
[2] = " R12_KP_IRQ_B",
[3] = " R12_APWDT_EVENT_B",
[4] = " R12_APXGPT1_EVENT_B",
[5] = " R12_CONN2AP_SPM_WAKEUP_B",
[6] = " R12_EINT_EVENT_B",
[7] = " R12_CONN_WDT_IRQ_B",
[8] = " R12_CCIF0_EVENT_B",
[9] = " R12_LOWBATTERY_IRQ_B",
[10] = " R12_SC_SSPM2SPM_WAKEUP",
[11] = " R12_SC_SCP2SPM_WAKEUP",
[12] = " R12_SC_ADSP2SPM_WAKEUP",
[13] = " R12_PCM_WDT_EVENT_B",
[14] = " R12_USBX_CDSC_B",
[15] = " R12_USBX_POWERDWN_B",
[16] = " R12_SYS_TIMER_EVENT_B",
[17] = " R12_EINT_EVENT_SECURE_B",
[18] = " R12_CCIF1_EVENT_B",
[19] = " R12_UART0_IRQ_B",
[20] = " R12_AFE_IRQ_MCU_B",
[21] = " R12_THERMAL_CTRL_EVENT_B",
[22] = " R12_SYS_CIRQ_IRQ_B",
[23] = " R12_MD2AP_PEER_WAKEUP_EVENT",
[24] = " R12_CSYSPWREQ_B",
[25] = " R12_MD1_WDT_B",
[26] = " R12_AP2AP_PEER_WAKEUP_EVENT",
[27] = " R12_SEJ_EVENT_B",
[28] = " R12_SPM_CPU_WAKEUP_EVENT",
[29] = " R12_CPU_IRQOUT",
[30] = " R12_CPU_WFI",
[31] = " R12_MCUSYS_IDLE_TO_EMI_ALL",
};
#define plat_mmio_read(offset) __raw_readl(mt6779_spm_base + offset)
int mt6779_get_wakeup_status(struct mt6779_spm_wake_status *wakesta)
{
if (!wakesta || !mt6779_spm_base)
return -EINVAL;
wakesta->r12 = plat_mmio_read(SPM_SW_RSV_0);
wakesta->r12_ext = plat_mmio_read(SPM_WAKEUP_STA);
wakesta->raw_sta = plat_mmio_read(SPM_WAKEUP_STA);
wakesta->raw_ext_sta = plat_mmio_read(SPM_WAKEUP_EXT_STA);
wakesta->md32pcm_wakeup_sta = plat_mmio_read(MD32PCM_WAKEUP_STA);
wakesta->md32pcm_event_sta = plat_mmio_read(MD32PCM_EVENT_STA);
wakesta->src_req = plat_mmio_read(SPM_SRC_REQ);
/* backup of SPM_WAKEUP_MISC */
wakesta->wake_misc = plat_mmio_read(SPM_SW_RSV_5);
/* get sleep time */
/* backup of PCM_TIMER_OUT */
wakesta->timer_out = plat_mmio_read(SPM_SW_RSV_6);
/* get other SYS and co-clock status */
wakesta->r13 = plat_mmio_read(PCM_REG13_DATA);
wakesta->idle_sta = plat_mmio_read(SUBSYS_IDLE_STA);
wakesta->req_sta0 = plat_mmio_read(SRC_REQ_STA_0);
wakesta->req_sta1 = plat_mmio_read(SRC_REQ_STA_1);
wakesta->req_sta2 = plat_mmio_read(SRC_REQ_STA_2);
wakesta->req_sta3 = plat_mmio_read(SRC_REQ_STA_3);
wakesta->req_sta4 = plat_mmio_read(SRC_REQ_STA_4);
/* get debug flag for PCM execution check */
wakesta->debug_flag = plat_mmio_read(PCM_WDT_LATCH_SPARE_0);
wakesta->debug_flag1 = plat_mmio_read(PCM_WDT_LATCH_SPARE_1);
/* get backup SW flag status */
wakesta->b_sw_flag0 = plat_mmio_read(SPM_SW_RSV_7);
wakesta->b_sw_flag1 = plat_mmio_read(SPM_SW_RSV_8);
/* get ISR status */
wakesta->isr = plat_mmio_read(SPM_IRQ_STA);
/* get SW flag status */
wakesta->sw_flag0 = plat_mmio_read(SPM_SW_FLAG_0);
wakesta->sw_flag1 = plat_mmio_read(SPM_SW_FLAG_1);
/* get CLK SETTLE */
wakesta->clk_settle = plat_mmio_read(SPM_CLK_SETTLE);
/* check abort */
wakesta->is_abort = wakesta->debug_flag & DEBUG_ABORT_MASK;
wakesta->is_abort |= wakesta->debug_flag1 & DEBUG_ABORT_MASK_1;
return 0;
}
#ifdef BYPASS_LOG
int mt6779_show_log_message(int type, const char *prefix, void *data)
{
return 0;
}
#else
int mt6779_show_log_message(int type, const char *prefix, void *data)
{
#define LOG_BUF_SIZE 256
#define LOG_BUF_OUT_SZ 768
int i;
char buf[LOG_BUF_SIZE] = { 0 };
char log_buf[LOG_BUF_OUT_SZ] = { 0 };
char *local_ptr;
int log_size = 0;
unsigned int wr = WR_UNKNOWN;
const char *scenario = prefix ?: "UNKNOWN";
mt6779_get_wakeup_status(&mt6779_wake);
/* Disable rcu lock checking */
rcu_irq_enter_irqson();
if (mt6779_wake.is_abort != 0) {
/* add size check for vcoredvfs */
aee_sram_printk("SPM ABORT (%s), r13 = 0x%x, ",
scenario, mt6779_wake.r13);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"[SPM] ABORT (%s), r13 = 0x%x, ",
scenario, mt6779_wake.r13);
aee_sram_printk(" debug_flag = 0x%x 0x%x\n",
mt6779_wake.debug_flag, mt6779_wake.debug_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" debug_flag = 0x%x 0x%x\n",
mt6779_wake.debug_flag, mt6779_wake.debug_flag1);
aee_sram_printk(" sw_flag = 0x%x 0x%x\n",
mt6779_wake.sw_flag0, mt6779_wake.sw_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" sw_flag = 0x%x 0x%x\n",
mt6779_wake.sw_flag0, mt6779_wake.sw_flag1);
aee_sram_printk(" b_sw_flag = 0x%x 0x%x\n",
mt6779_wake.b_sw_flag0, mt6779_wake.b_sw_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" b_sw_flag = 0x%x 0x%x\n",
mt6779_wake.b_sw_flag0, mt6779_wake.b_sw_flag1);
wr = WR_ABORT;
}
if (mt6779_wake.r12 & R12_PCM_TIMER_EVENT) {
if (mt6779_wake.wake_misc & WAKE_MISC_PCM_TIMER_EVENT) {
local_ptr = " PCM_TIMER";
if ((strlen(buf) + strlen(local_ptr)) < LOG_BUF_SIZE)
strncat(buf, local_ptr, strlen(local_ptr));
wr = WR_PCM_TIMER;
}
}
if (mt6779_wake.r12 & R12_SPM_TWAM_IRQ_B) {
if (mt6779_wake.wake_misc & WAKE_MISC_DVFSRC_IRQ) {
local_ptr = " DVFSRC";
if ((strlen(buf) + strlen(local_ptr)) < LOG_BUF_SIZE)
strncat(buf, local_ptr, strlen(local_ptr));
wr = WR_DVFSRC;
}
if (mt6779_wake.wake_misc & WAKE_MISC_TWAM_IRQ_B) {
local_ptr = " TWAM";
if ((strlen(buf) + strlen(local_ptr)) < LOG_BUF_SIZE)
strncat(buf, local_ptr, strlen(local_ptr));
wr = WR_TWAM;
}
if (mt6779_wake.wake_misc & WAKE_MISC_PMSR_IRQ_B_SET0) {
local_ptr = " PMSR";
if ((strlen(buf) + strlen(local_ptr)) < LOG_BUF_SIZE)
strncat(buf, local_ptr, strlen(local_ptr));
wr = WR_PMSR;
}
if (mt6779_wake.wake_misc & WAKE_MISC_PMSR_IRQ_B_SET1) {
local_ptr = " PMSR";
if ((strlen(buf) + strlen(local_ptr)) < LOG_BUF_SIZE)
strncat(buf, local_ptr, strlen(local_ptr));
wr = WR_PMSR;
}
if (mt6779_wake.wake_misc & WAKE_MISC_SPM_ACK_CHK_WAKEUP_0) {
local_ptr = " SPM_ACK_CHK";
if ((strlen(buf) + strlen(local_ptr)) < LOG_BUF_SIZE)
strncat(buf, local_ptr, strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (mt6779_wake.wake_misc & WAKE_MISC_SPM_ACK_CHK_WAKEUP_1) {
local_ptr = " SPM_ACK_CHK";
if ((strlen(buf) + strlen(local_ptr)) < LOG_BUF_SIZE)
strncat(buf, local_ptr, strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (mt6779_wake.wake_misc & WAKE_MISC_SPM_ACK_CHK_WAKEUP_2) {
local_ptr = " SPM_ACK_CHK";
if ((strlen(buf) + strlen(local_ptr)) < LOG_BUF_SIZE)
strncat(buf, local_ptr, strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (mt6779_wake.wake_misc & WAKE_MISC_SPM_ACK_CHK_WAKEUP_3) {
local_ptr = " SPM_ACK_CHK";
if ((strlen(buf) + strlen(local_ptr)) < LOG_BUF_SIZE)
strncat(buf, local_ptr, strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (mt6779_wake.wake_misc & WAKE_MISC_SPM_ACK_CHK_WAKEUP_ALL) {
local_ptr = " SPM_ACK_CHK";
if ((strlen(buf) + strlen(local_ptr)) < LOG_BUF_SIZE)
strncat(buf, local_ptr, strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
}
for (i = 1; i < 32; i++) {
if (mt6779_wake.r12 & (1U << i)) {
if ((strlen(buf) + strlen(mt6779_wakesrc_str[i])) <
LOG_BUF_SIZE)
strncat(buf, mt6779_wakesrc_str[i],
strlen(mt6779_wakesrc_str[i]));
wr = WR_WAKE_SRC;
}
}
WARN_ON(strlen(buf) >= LOG_BUF_SIZE);
log_size += scnprintf(log_buf + log_size, LOG_BUF_OUT_SZ - log_size,
"%s wake up by %s, timer_out = %u, r13 = 0x%x, debug_flag = 0x%x 0x%x, ",
scenario, buf, mt6779_wake.timer_out, mt6779_wake.r13,
mt6779_wake.debug_flag, mt6779_wake.debug_flag1);
log_size += scnprintf(log_buf + log_size, LOG_BUF_OUT_SZ - log_size,
"r12 = 0x%x, r12_ext = 0x%x, raw_sta = 0x%x 0x%x 0x%x, idle_sta = 0x%x, ",
mt6779_wake.r12, mt6779_wake.r12_ext, mt6779_wake.raw_sta,
mt6779_wake.md32pcm_wakeup_sta, mt6779_wake.md32pcm_event_sta,
mt6779_wake.idle_sta);
log_size += scnprintf(log_buf + log_size, LOG_BUF_OUT_SZ - log_size,
" req_sta = 0x%x 0x%x 0x%x 0x%x 0x%x, isr = 0x%x, ",
mt6779_wake.req_sta0, mt6779_wake.req_sta1,
mt6779_wake.req_sta2, mt6779_wake.req_sta3,
mt6779_wake.req_sta4, mt6779_wake.isr);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"raw_ext_sta = 0x%x, wake_misc = 0x%x, pcm_flag = 0x%x 0x%x 0x%x 0x%x, req = 0x%x, ",
mt6779_wake.raw_ext_sta,
mt6779_wake.wake_misc,
mt6779_wake.sw_flag0,
mt6779_wake.sw_flag1, mt6779_wake.b_sw_flag0,
mt6779_wake.b_sw_flag0,
mt6779_wake.src_req);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" clk_settle = 0x%x, ", mt6779_wake.clk_settle);
WARN_ON(log_size >= LOG_BUF_OUT_SZ);
pr_info("[name:spm&][SPM] %s", log_buf);
/* Eable rcu lock checking */
rcu_irq_exit_irqson();
return wr;
}
#endif
struct mtk_lpm_issuer mt6779_issuer = {
.log = mt6779_show_log_message,
};
int mt6779_logger_init(void)
{
struct device_node *node = NULL;
node = of_find_compatible_node(NULL, NULL, "mediatek,spm");
if (node) {
mt6779_spm_base = of_iomap(node, 0);
of_node_put(node);
}
if (mt6779_spm_base)
mtk_lp_issuer_register(&mt6779_issuer);
else
pr_info("[name:mtk_lpm][P] - Don't register the issue by error! (%s:%d)\n",
__func__, __LINE__);
return 0;
}
EXPORT_SYMBOL(mt6779_logger_init);
@@ -0,0 +1,296 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/pm_qos.h>
#include <mtk_lpm.h>
#include <mtk_lp_plat_reg.h>
#include "mtk_cpupm_dbg.h"
#include "mtk_cpuidle_cpc.h"
static DEFINE_SPINLOCK(cpc_prof_spin_lock);
/* profile latecny */
#define PROF_DEV_NAME_SIZE 36
#define get_core_type_index(cpu) ((cpu > 3) ? CPU_TYPE_B : CPU_TYPE_L)
struct mtk_cpc_lat_data {
char name[PROF_DEV_NAME_SIZE];
unsigned int on_sum;
unsigned int on_max;
unsigned int off_sum;
unsigned int off_max;
unsigned int on_cnt;
unsigned int off_cnt;
};
struct mtk_cpc_device {
union {
struct mtk_cpc_lat_data p[DEV_TYPE_NUM];
struct {
struct mtk_cpc_lat_data cpu[NF_CPU_TYPE];
struct mtk_cpc_lat_data cluster;
struct mtk_cpc_lat_data mcusys;
};
};
bool prof_en;
};
struct mtk_cpc_device cpc;
static void mtk_cpc_clr_lat(void)
{
unsigned int size;
unsigned int ofs;
int i;
ofs = sizeof(cpc.p[0].name);
size = sizeof(struct mtk_cpc_lat_data) - ofs;
for (i = 0; i < DEV_TYPE_NUM; i++)
memset((char *)&cpc.p[i] + ofs, 0, size);
}
static void mtk_cpc_cal_lat(void)
{
}
static void mtk_cpc_get_cpu_lat(int cpu, unsigned int *on, unsigned int *off)
{
unsigned int lat;
lat = mtk_cpupm_mcusys_read(CPC_CPU_LATENCY(cpu));
*on = (lat >> 16) & 0xFFFF;
*off = lat & 0xFFFF;
}
static void mtk_cpc_get_cpusys_lat(unsigned int *on, unsigned int *off)
{
unsigned int lat_on;
unsigned int lat_off;
lat_on = mtk_cpupm_mcusys_read(CPC_CLUSTER_ON_LATENCY);
lat_off = mtk_cpupm_mcusys_read(CPC_CLUSTER_OFF_LATENCY);
*on = lat_on & 0xFFFF;
*off = lat_off & 0xFFFF;
}
static void mtk_cpc_get_mcusys_lat(unsigned int *on, unsigned int *off)
{
unsigned int lat;
lat = mtk_cpupm_mcusys_read(CPC_MCUSYS_LATENCY);
*on = (lat >> 16) & 0xFFFF;
*off = lat & 0xFFFF;
}
#define __mtk_cpc_record_lat(sum, max, lat) \
do { \
if (lat > max) \
max = lat; \
(sum) += (lat); \
} while (0)
static void mtk_cpc_on_record_lat(struct mtk_cpc_lat_data *lat,
unsigned int ticks)
{
unsigned long flags;
if (ticks == 0)
return;
spin_lock_irqsave(&cpc_prof_spin_lock, flags);
__mtk_cpc_record_lat(lat->on_sum, lat->on_max, ticks);
lat->on_cnt++;
spin_unlock_irqrestore(&cpc_prof_spin_lock, flags);
}
static void mtk_cpc_off_record_lat(struct mtk_cpc_lat_data *lat,
unsigned int ticks)
{
unsigned long flags;
if (ticks == 0)
return;
spin_lock_irqsave(&cpc_prof_spin_lock, flags);
__mtk_cpc_record_lat(lat->off_sum, lat->off_max, ticks);
lat->off_cnt++;
spin_unlock_irqrestore(&cpc_prof_spin_lock, flags);
}
static bool mtk_cpc_did_cluster_pwr_off(void)
{
static unsigned int last_cnt;
unsigned int cnt = mtk_cpupm_mcusys_read(CPC_DORMANT_COUNTER);
/**
* Cluster off count
* bit[0:15] : memory retention
* bit[16:31] : memory off
*/
if ((cnt & 0x7FFF) == 0)
cnt = ((cnt >> 16) & 0x7FFF);
else
cnt = cnt & 0x7FFF;
cnt += mtk_cpupm_syssram_read(SYSRAM_CPC_CPUSYS_CNT_BACKUP);
if (last_cnt == cnt)
return false;
last_cnt = cnt;
return true;
}
static bool mtk_cpc_did_mcusys_pwr_off(void)
{
static unsigned int last_cnt;
unsigned int cnt = mtk_cpupm_syssram_read(SYSRAM_CPC_MCUSYS_CNT_BACKUP);
cnt += mtk_cpupm_syssram_read(SYSRAM_MCUSYS_CNT);
if (last_cnt == cnt)
return false;
last_cnt = cnt;
return true;
}
static void mtk_cpc_save_latency(int cpu)
{
unsigned int on, off;
struct mtk_cpc_lat_data *lat;
lat = &cpc.cpu[get_core_type_index(cpu)];
mtk_cpc_get_cpu_lat(cpu, &on, &off);
mtk_cpc_on_record_lat(lat, on);
mtk_cpc_off_record_lat(lat, off);
if (mtk_cpc_did_mcusys_pwr_off()) {
lat = &cpc.mcusys;
mtk_cpc_get_mcusys_lat(&on, &off);
mtk_cpc_on_record_lat(lat, on);
mtk_cpc_off_record_lat(lat, off);
} else if (!mtk_cpc_did_cluster_pwr_off()) {
return;
}
lat = &cpc.cluster;
mtk_cpc_get_cpusys_lat(&on, &off);
mtk_cpc_on_record_lat(lat, on);
mtk_cpc_off_record_lat(lat, off);
}
void mtk_cpc_prof_lat_dump(struct seq_file *m)
{
struct mtk_cpc_lat_data *lat;
int i;
for (i = 0; i < DEV_TYPE_NUM; i++) {
lat = &cpc.p[i];
seq_printf(m, "%s\n", lat->name);
if (lat->off_cnt) {
seq_printf(m, "\toff : avg = %2dus, max = %3dus, cnt = %d\n",
cpc_tick_to_us(lat->off_sum / lat->off_cnt),
cpc_tick_to_us(lat->off_max),
lat->off_cnt);
} else {
seq_puts(m, "\toff : None\n");
}
if (lat->on_cnt) {
seq_printf(m, "\ton : avg = %2dus, max = %3dus, cnt = %d\n",
cpc_tick_to_us(lat->on_sum / lat->on_cnt),
cpc_tick_to_us(lat->on_max),
lat->on_cnt);
} else {
seq_puts(m, "\ton : None\n");
}
}
}
void mtk_cpc_prof_start(void)
{
mtk_cpupm_block();
cpc_prof_en();
mtk_cpc_clr_lat();
cpc.prof_en = true;
mtk_cpupm_allow();
}
void mtk_cpc_prof_stop(void)
{
mtk_cpupm_block();
cpc.prof_en = false;
mtk_cpc_cal_lat();
cpc_prof_dis();
mtk_cpupm_allow();
}
int mtk_cpc_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct mtk_lpm_nb_data *nb_data = (struct mtk_lpm_nb_data *)data;
if (!cpc.prof_en)
return NOTIFY_OK;
if (action & MTK_LPM_NB_BEFORE_REFLECT)
mtk_cpc_save_latency(nb_data->cpu);
return NOTIFY_OK;
}
struct notifier_block mtk_cpc_nb = {
.notifier_call = mtk_cpc_notify,
};
int __init mtk_cpc_init(void)
{
cpc.prof_en = false;
snprintf(cpc.cpu[CPU_TYPE_L].name, PROF_DEV_NAME_SIZE, "cpu_L");
snprintf(cpc.cpu[CPU_TYPE_B].name, PROF_DEV_NAME_SIZE, "cpu_B");
snprintf(cpc.cluster.name, PROF_DEV_NAME_SIZE, "cluster");
snprintf(cpc.mcusys.name, PROF_DEV_NAME_SIZE, "mcusys");
mtk_lpm_notifier_register(&mtk_cpc_nb);
return 0;
}
void __exit mtk_cpc_exit(void)
{
mtk_lpm_notifier_unregister(&mtk_cpc_nb);
}
@@ -0,0 +1,95 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_CPUIDLE_CPC_H__
#define __MTK_CPUIDLE_CPC_H__
#include <linux/seq_file.h>
#include <mtk_lpm_module.h>
enum {
CPU_TYPE_L,
CPU_TYPE_B,
NF_CPU_TYPE
};
enum {
CPC_SMC_EVENT_DUMP_TRACE_DATA,
CPC_SMC_EVENT_GIC_DPG_SET,
CPC_SMC_EVENT_CPC_CONFIG,
CPC_SMC_EVENT_READ_CONFIG,
NF_CPC_SMC_EVENT
};
enum {
CPC_SMC_CONFIG_PROF,
CPC_SMC_CONFIG_AUTO_OFF,
CPC_SMC_CONFIG_AUTO_OFF_THRES,
CPC_SMC_CONFIG_CNT_CLR,
NF_CPC_SMC_CONFIG
};
/**
* number of profile type :
* - (number of cpu type) + cluster + mcusys
*/
#define DEV_TYPE_NUM (NF_CPU_TYPE + 2)
/* smc */
#define mtk_cpc_smc(act, arg1, arg2)\
mtk_lpm_smc_cpu_pm(CPC_COMMAND, act, arg1, arg2)
/* configuration */
#define cpc_prof_en()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_PROF, 1)
#define cpc_prof_dis()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_PROF, 0)
#define cpc_auto_off_en()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF, 1)
#define cpc_auto_off_dis()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF, 0)
#define cpc_set_auto_off_thres(us)\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF_THRES, us)
#define cpc_cluster_cnt_clr()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_CNT_CLR, 0)
/* status */
#define cpc_get_prof_sta()\
mtk_cpc_smc(CPC_SMC_EVENT_READ_CONFIG\
, CPC_SMC_CONFIG_PROF, 0)
#define cpc_get_auto_off_sta()\
mtk_cpc_smc(CPC_SMC_EVENT_READ_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF, 0)
#define cpc_get_auto_off_thres()\
mtk_cpc_smc(CPC_SMC_EVENT_READ_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF_THRES, 0)
#define cpc_tick_to_us(val) ((val) / 13)
void mtk_cpc_prof_start(void);
void mtk_cpc_prof_stop(void);
void mtk_cpc_prof_lat_dump(struct seq_file *m);
int __init mtk_cpc_init(void);
void __exit mtk_cpc_exit(void);
#endif /* __MTK_CPUIDLE_CPC_H__ */
@@ -0,0 +1,612 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/cpuidle.h>
#include <linux/sched/clock.h>
#include <linux/cpu.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/pm_qos.h>
#include <linux/tick.h>
#include <linux/timer.h>
#include <mboot_params.h>
#include <mtk_lpm.h>
#include <mtk_lp_plat_reg.h>
#include "mtk_cpupm_dbg.h"
#include "mtk_cpuidle_status.h"
#include "mtk_cpuidle_cpc.h"
#define DUMP_INTERVAL sec_to_ns(5)
static u64 last_dump_ns;
/* stress test */
static unsigned int timer_interval = 10 * 1000;
static struct task_struct *stress_tsk[NR_CPUS];
/* CPU off state : core, cluster and mcusys */
#define CPU_OFF_MAX_LV (3)
/* all core idle profiling */
struct all_cpu_idle_data {
unsigned long long last_ns;
unsigned long long dur_ns;
unsigned int cnt;
unsigned int bp; /* basis points : 0.01% */
};
struct all_cpu_idle {
unsigned long long start_us;
struct all_cpu_idle_data lv[CPU_OFF_MAX_LV];
};
static struct all_cpu_idle all_core_off;
/* idle ratio profiling */
struct mtk_cpuidle_ratio {
unsigned long long start_us;
unsigned long long idle_time_ns[CPUIDLE_STATE_MAX];
unsigned int bp[CPUIDLE_STATE_MAX];
};
/* idle latency profiling */
struct mtk_cpuidle_prof {
unsigned int cnt;
unsigned int max;
union {
u64 avg;
u64 sum;
};
};
/* idle information */
struct mtk_cpuidle_info {
u64 enter_time_ns;
int idle_index;
int cnt[CPUIDLE_STATE_MAX];
};
/* mtk cpu idle status */
struct mtk_cpuidle_device {
struct mtk_cpuidle_ratio ratio;
struct mtk_cpuidle_info info;
struct mtk_cpuidle_prof prof;
struct hrtimer timer;
int cpu;
int state_count;
bool tmr_running;
};
static DEFINE_PER_CPU(struct mtk_cpuidle_device, mtk_cpuidle_dev);
/* mtk cpu idle configuration */
struct mtk_cpuidle_control {
bool tmr_en;
bool prof_en;
bool log_en;
bool stress_en;
};
static struct mtk_cpuidle_control mtk_cpuidle_ctrl;
static int mtk_cpuidle_stress_task(void *arg)
{
while (mtk_cpuidle_ctrl.stress_en)
usleep_range(timer_interval - 10, timer_interval + 10);
return 0;
}
static void mtk_cpuidle_stress_start(void)
{
int i;
char name[20] = {0};
int ret = 0;
if (mtk_cpuidle_ctrl.stress_en)
return;
mtk_cpuidle_ctrl.stress_en = true;
for_each_online_cpu(i) {
ret = scnprintf(name, sizeof(name), "mtk_cpupm_stress_%d", i);
stress_tsk[i] =
kthread_create(mtk_cpuidle_stress_task, NULL, name);
if (!IS_ERR(stress_tsk[i])) {
kthread_bind(stress_tsk[i], i);
wake_up_process(stress_tsk[i]);
}
}
}
static void mtk_cpuidle_stress_stop(void)
{
mtk_cpuidle_ctrl.stress_en = false;
msleep(20);
}
void mtk_cpuidle_set_stress_test(bool en)
{
if (en)
mtk_cpuidle_stress_start();
else
mtk_cpuidle_stress_stop();
}
bool mtk_cpuidle_get_stress_status(void)
{
return mtk_cpuidle_ctrl.stress_en;
}
void mtk_cpuidle_set_stress_time(unsigned int val)
{
timer_interval = clamp_val(val, 100, 20000);
}
unsigned int mtk_cpuidle_get_stress_time(void)
{
return timer_interval;
}
void mtk_cpuidle_ctrl_timer_en(bool enable)
{
mtk_cpupm_block();
mtk_cpuidle_ctrl.tmr_en = enable;
mtk_cpupm_allow();
}
bool mtk_cpuidle_ctrl_timer_sta_get(void)
{
return mtk_cpuidle_ctrl.tmr_en;
}
void mtk_cpuidle_ctrl_log_en(bool enable)
{
mtk_cpupm_block();
mtk_cpuidle_ctrl.log_en = enable;
mtk_cpupm_allow();
}
bool mtk_cpuidle_ctrl_log_sta_get(void)
{
return mtk_cpuidle_ctrl.log_en;
}
static void _mtk_cpuidle_prof_ratio_start(void *data)
{
int i, cpu;
struct mtk_cpuidle_device *mtk_idle;
cpu = get_cpu();
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
put_cpu();
if (unlikely(!mtk_idle))
return;
mtk_idle->ratio.start_us = div64_u64(sched_clock(), 1000);
for (i = 0; i < mtk_idle->state_count; i++)
mtk_idle->ratio.idle_time_ns[i] = 0;
}
void mtk_cpuidle_prof_ratio_start(void)
{
int i;
if (mtk_cpuidle_ctrl.prof_en)
return;
mtk_cpupm_block();
for (i = 0; i < CPU_OFF_MAX_LV; i++) {
all_core_off.lv[i].dur_ns = 0;
all_core_off.lv[i].cnt = 0;
}
all_core_off.start_us = div64_u64(sched_clock(), 1000);
on_each_cpu(_mtk_cpuidle_prof_ratio_start, NULL, 1);
mtk_cpuidle_ctrl.prof_en = true;
mtk_cpupm_allow();
}
static void _mtk_cpuidle_prof_ratio_stop(void *stop_time)
{
int i, cpu;
uint64_t dur_us;
struct mtk_cpuidle_device *mtk_idle;
cpu = get_cpu();
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
put_cpu();
if (unlikely(!mtk_idle))
return;
dur_us = div64_u64(sched_clock(), 1000) -
mtk_idle->ratio.start_us;
for (i = 0; i < mtk_idle->state_count; i++)
mtk_idle->ratio.bp[i] = (unsigned int)div64_u64(
mtk_idle->ratio.idle_time_ns[i] * 10,
dur_us);
}
void mtk_cpuidle_prof_ratio_stop(void)
{
int i;
uint64_t time_us;
if (!mtk_cpuidle_ctrl.prof_en)
return;
mtk_cpupm_block();
on_each_cpu(_mtk_cpuidle_prof_ratio_stop, NULL, 1);
mtk_cpuidle_ctrl.prof_en = false;
time_us = div64_u64(sched_clock(), 1000) - all_core_off.start_us;
for (i = 0; i < CPU_OFF_MAX_LV; i++)
all_core_off.lv[i].bp = (unsigned int)div64_u64(
all_core_off.lv[i].dur_ns * 10, time_us);
mtk_cpupm_allow();
}
void mtk_cpuidle_prof_ratio_dump(struct seq_file *m)
{
int i, cpu;
struct mtk_cpuidle_device *mtk_idle;
seq_printf(m, "%6s%12s%12s%12s\n",
"",
"cpu_off",
"cluster_off",
"mcusys_off");
for_each_possible_cpu(cpu) {
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
if (!mtk_idle)
continue;
seq_printf(m, "cpu%d: ", cpu);
for (i = 1; i < mtk_idle->state_count; i++) {
seq_printf(m, "%5s%3u.%02u%%",
"",
mtk_idle->ratio.bp[i] / 100,
mtk_idle->ratio.bp[i] % 100);
}
seq_puts(m, "\n");
}
seq_puts(m, "All core off ratio :\n");
for (i = 0; i < CPU_OFF_MAX_LV; i++)
seq_printf(m, "\t C_%d = %3u.%02u%%\n",
i + 1,
all_core_off.lv[i].bp / 100,
all_core_off.lv[i].bp % 100);
}
void mtk_cpuidle_state_enable(bool en)
{
struct cpuidle_driver *drv = NULL;
int i, cpu;
mtk_cpupm_block();
for_each_possible_cpu(cpu) {
//drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
for (i = drv->state_count - 1; i > 0; i--)
mtk_cpuidle_set_param(drv, i, IDLE_PARAM_EN, en);
}
mtk_cpupm_allow();
}
static bool mtk_cpuidle_need_dump(unsigned int idx)
{
u64 curr_ns;
bool dump = false;
if (!mtk_cpuidle_ctrl.log_en || !idx)
return false;
curr_ns = sched_clock();
if (curr_ns - last_dump_ns > DUMP_INTERVAL) {
last_dump_ns = curr_ns;
dump = true;
}
return dump;
}
static unsigned int mtk_cpuidle_get_cluster_off_cnt(void)
{
unsigned int cnt = mtk_cpupm_mcusys_read(CPC_DORMANT_COUNTER);
/**
* Cluster off count
* bit[0:15] : memory retention
* bit[16:31] : memory off
*/
if ((cnt & 0x7FFF) == 0)
cnt = ((cnt >> 16) & 0x7FFF);
else
cnt = cnt & 0x7FFF;
cnt += mtk_cpupm_syssram_read(SYSRAM_CPC_CPUSYS_CNT_BACKUP);
cpc_cluster_cnt_clr();
mtk_cpupm_syssram_write(SYSRAM_CPC_CPUSYS_CNT_BACKUP, 0);
mtk_cpupm_syssram_write(SYSRAM_CPUSYS_CNT,
mtk_cpupm_syssram_read(SYSRAM_CPUSYS_CNT) + cnt);
return cnt;
}
static unsigned int mtk_cpuidle_get_mcusys_off_cnt(void)
{
unsigned int cnt = mtk_cpupm_syssram_read(SYSRAM_CPC_MCUSYS_CNT_BACKUP);
mtk_cpupm_syssram_write(SYSRAM_CPC_MCUSYS_CNT_BACKUP, 0);
mtk_cpupm_syssram_write(SYSRAM_MCUSYS_CNT,
mtk_cpupm_syssram_read(SYSRAM_MCUSYS_CNT) + cnt);
return cnt;
}
static void mtk_cpuidle_dump_info(void)
{
struct mtk_cpuidle_device *mtk_idle;
int cpu, idx, ofs;
unsigned int avail_cpu_mask = 0;
for_each_possible_cpu(cpu) {
if (cpu_online(cpu))
avail_cpu_mask |= (1 << cpu);
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
if (!mtk_idle)
continue;
ofs = SYSRAM_RECENT_CPU_CNT(cpu);
mtk_cpupm_syssram_write(ofs, 0);
for (idx = 1; idx < mtk_idle->state_count ; idx++) {
mtk_cpupm_syssram_write(ofs, mtk_cpupm_syssram_read(ofs)
+ mtk_idle->info.cnt[idx]);
mtk_idle->info.cnt[idx] = 0;
}
}
mtk_cpupm_syssram_write(SYSRAM_RECENT_CPUSYS_CNT,
mtk_cpuidle_get_cluster_off_cnt());
mtk_cpupm_syssram_write(SYSRAM_RECENT_MCUSYS_CNT,
mtk_cpuidle_get_mcusys_off_cnt());
mtk_cpupm_syssram_write(SYSRAM_CPU_ONLINE, avail_cpu_mask);
mtk_cpupm_syssram_write(SYSRAM_RECENT_CNT_TS_H,
(unsigned int)((last_dump_ns >> 32) & 0xFFFFFFFF));
mtk_cpupm_syssram_write(SYSRAM_RECENT_CNT_TS_L,
(unsigned int)(last_dump_ns & 0xFFFFFFFF));
}
static enum hrtimer_restart mtk_cpuidle_hrtimer_func(struct hrtimer *timer)
{
return HRTIMER_NORESTART;
}
static void mtk_cpuidle_set_timer(struct mtk_cpuidle_device *mtk_idle)
{
int index;
int limit_sleep_us;
struct cpuidle_driver *drv;
if (!mtk_cpuidle_ctrl.tmr_en)
return;
index = mtk_idle->info.idle_index;
/* Only support WFI/CPU_OFF state */
if (index > 1)
return;
drv = cpuidle_get_driver();
if (unlikely(!drv))
return;
if (index + 1 >= mtk_idle->state_count)
//|| !tick_nohz_tick_stopped())
return;
limit_sleep_us = 4 * max_t(unsigned int,
get_residency(drv, index),
get_residency(drv, index + 1));
if (index != 0)
tick_broadcast_exit();
//RCU_NONIDLE(hrtimer_start(&mtk_idle->timer,
// ns_to_ktime(limit_sleep_us * NSEC_PER_USEC),
// HRTIMER_MODE_REL_PINNED));
mtk_idle->tmr_running = true;
if (index != 0)
tick_broadcast_enter();
}
static void mtk_cpuidle_cancel_timer(struct mtk_cpuidle_device *mtk_idle)
{
if (!mtk_cpuidle_ctrl.tmr_en)
return;
if (mtk_idle->tmr_running) {
mtk_idle->tmr_running = true;
//RCU_NONIDLE(hrtimer_try_to_cancel(&mtk_idle->timer));
}
}
static void mtk_cpuidle_set_last_off_ts(int idx)
{
int i;
uint64_t curr_ns = sched_clock();
if (!idx)
return;
if (idx > CPU_OFF_MAX_LV)
idx = CPU_OFF_MAX_LV;
for (i = 0; i < idx; i++) {
all_core_off.lv[i].cnt++;
if (all_core_off.lv[i].cnt == num_online_cpus())
all_core_off.lv[i].last_ns = curr_ns;
}
}
static void mtk_cpuidle_save_idle_time(int idx)
{
int i;
uint64_t curr_ns = sched_clock();
if (!idx)
return;
if (idx > CPU_OFF_MAX_LV)
idx = CPU_OFF_MAX_LV;
for (i = 0; i < idx; i++) {
if (all_core_off.lv[i].cnt == num_online_cpus())
all_core_off.lv[i].dur_ns += (curr_ns
- all_core_off.lv[i].last_ns);
all_core_off.lv[i].cnt--;
}
}
static int mtk_cpuidle_status_update(struct notifier_block *nb,
unsigned long action, void *data)
{
struct mtk_cpuidle_device *mtk_idle;
struct mtk_lpm_nb_data *nb_data = (struct mtk_lpm_nb_data *)data;
if (action & MTK_LPM_NB_BEFORE_REFLECT) {
/* prevent race conditions by mtk_lp_mod_locker */
if (mtk_cpuidle_ctrl.prof_en)
mtk_cpuidle_save_idle_time(nb_data->index);
if (mtk_cpuidle_need_dump(nb_data->index))
mtk_cpuidle_dump_info();
} else if (action & MTK_LPM_NB_RESUME) {
//aee_rr_rec_mcdi_val(nb_data->cpu,
// (nb_data->index << 16) | 0x0);
mtk_idle = &per_cpu(mtk_cpuidle_dev, nb_data->cpu);
mtk_idle->info.idle_index = -1;
mtk_idle->info.cnt[nb_data->index]++;
if (mtk_cpuidle_ctrl.prof_en)
mtk_idle->ratio.idle_time_ns[nb_data->index] +=
(sched_clock() - mtk_idle->info.enter_time_ns);
mtk_cpuidle_cancel_timer(mtk_idle);
} else if (action & MTK_LPM_NB_AFTER_PROMPT) {
/* prevent race conditions by mtk_lp_mod_locker */
if (mtk_cpuidle_ctrl.prof_en)
mtk_cpuidle_set_last_off_ts(nb_data->index);
} else if (action & MTK_LPM_NB_PREPARE) {
mtk_idle = &per_cpu(mtk_cpuidle_dev, nb_data->cpu);
mtk_idle->info.idle_index = nb_data->index;
mtk_idle->info.enter_time_ns = sched_clock();
mtk_cpuidle_set_timer(mtk_idle);
//aee_rr_rec_mcdi_val(nb_data->cpu,
// (nb_data->index << 16) | 0xff);
}
return NOTIFY_OK;
}
struct notifier_block mtk_cpuidle_status_nb = {
.notifier_call = mtk_cpuidle_status_update,
};
static void mtk_cpuidle_init_per_cpu(void *info)
{
struct mtk_cpuidle_device *mtk_idle;
int cpu;
cpu = get_cpu();
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
put_cpu();
hrtimer_init(&mtk_idle->timer,
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
mtk_idle->cpu = cpu;
mtk_idle->timer.function = mtk_cpuidle_hrtimer_func;
mtk_idle->state_count = mtk_cpupm_get_idle_state_count(cpu);
}
int __init mtk_cpuidle_status_init(void)
{
mtk_cpuidle_ctrl.tmr_en = true;
mtk_cpuidle_ctrl.prof_en = false;
mtk_cpuidle_ctrl.log_en = true;
mtk_cpuidle_ctrl.stress_en = false;
mtk_cpupm_block();
on_each_cpu(mtk_cpuidle_init_per_cpu, NULL, 0);
mtk_cpupm_allow();
mtk_lpm_notifier_register(&mtk_cpuidle_status_nb);
return 0;
}
void __exit mtk_cpuidle_status_exit(void)
{
mtk_lpm_notifier_unregister(&mtk_cpuidle_status_nb);
}
@@ -0,0 +1,69 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_CPUIDLE_STATUS_H__
#define __MTK_CPUIDLE_STATUS_H__
#include <linux/seq_file.h>
#define sec_to_ns(v) ((v) * 1000 * 1000 * 1000ULL)
enum {
IDLE_PARAM_LAT,
IDLE_PARAM_RES,
IDLE_PARAM_EN,
NF_IDLE_PARAM
};
#define get_residency(drv, idx)\
((drv)->states[idx].target_residency)
#define get_latency(drv, idx)\
((drv)->states[idx].exit_latency)
#define get_disabled(drv, idx)\
((drv)->states[idx].disabled)
#define mtk_cpuidle_get_param(drv, state, param) \
({ \
int val = -1; \
if (param == IDLE_PARAM_LAT) \
val = (int)get_latency(drv, state); \
else if (param == IDLE_PARAM_RES) \
val = (int)get_residency(drv, state); \
else if (param == IDLE_PARAM_EN) \
val = get_disabled(drv, state) ? 0 : 1 ; \
val; \
})
#define mtk_cpuidle_set_param(drv, state, param, val) \
do { \
if (param == IDLE_PARAM_LAT) \
get_latency(drv, state) = val; \
else if (param == IDLE_PARAM_RES) \
get_residency(drv, state) = val; \
else if (param == IDLE_PARAM_EN) \
get_disabled(drv, state) = !val ; \
} while (0)
void mtk_cpuidle_set_stress_test(bool en);
bool mtk_cpuidle_get_stress_status(void);
void mtk_cpuidle_set_stress_time(unsigned int val);
unsigned int mtk_cpuidle_get_stress_time(void);
void mtk_cpuidle_prof_ratio_start(void);
void mtk_cpuidle_prof_ratio_stop(void);
void mtk_cpuidle_prof_ratio_dump(struct seq_file *m);
void mtk_cpuidle_ctrl_timer_en(bool enable);
bool mtk_cpuidle_ctrl_timer_sta_get(void);
void mtk_cpuidle_ctrl_log_en(bool enable);
bool mtk_cpuidle_ctrl_log_sta_get(void);
void mtk_cpuidle_state_enable(bool en);
int __init mtk_cpuidle_status_init(void);
void __exit mtk_cpuidle_status_exit(void);
#endif /* __MTK_CPUIDLE_STATUS_H__ */
@@ -0,0 +1,152 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cpuidle.h>
#include <linux/pm_qos.h>
#include "mtk_cpuidle_status.h"
#include "mtk_cpuidle_cpc.h"
#include "mtk_idle_procfs.h"
static void __iomem *cpupm_mcusys_base_addr;
static void __iomem *cpupm_syssram_base_addr;
static struct pm_qos_request cpuidle_dbg_qos_req;
int mtk_cpupm_mcusys_write(int ofs, unsigned int val)
{
if (!cpupm_mcusys_base_addr)
return -EADDRNOTAVAIL;
if ((ofs % 4) != 0)
return -EINVAL;
__raw_writel(val, cpupm_mcusys_base_addr + ofs);
mb(); /* make sure register access in order */
return 0;
}
unsigned int mtk_cpupm_mcusys_read(int ofs)
{
if (!cpupm_mcusys_base_addr)
return 0;
if ((ofs % 4) != 0)
return 0;
return __raw_readl(cpupm_mcusys_base_addr + ofs);
}
int mtk_cpupm_syssram_write(int ofs, unsigned int val)
{
if (!cpupm_syssram_base_addr)
return -EADDRNOTAVAIL;
if ((ofs % 4) != 0)
return -EINVAL;
__raw_writel(val, cpupm_syssram_base_addr + ofs);
mb(); /* make sure register access in order */
return 0;
}
unsigned int mtk_cpupm_syssram_read(int ofs)
{
if (!cpupm_syssram_base_addr)
return 0;
if ((ofs % 4) != 0)
return 0;
return __raw_readl(cpupm_syssram_base_addr + ofs);
}
void mtk_cpupm_block(void)
{
pm_qos_update_request(&cpuidle_dbg_qos_req, 2);
}
void mtk_cpupm_allow(void)
{
pm_qos_update_request(&cpuidle_dbg_qos_req, PM_QOS_DEFAULT_VALUE);
}
int mtk_cpupm_get_idle_state_count(int cpu)
{
struct device_node *state_node, *cpu_node;
int i, state_count, start_idx = 1;
cpu_node = of_cpu_device_node_get(cpu);
if (!cpu_node)
return start_idx;
for (i = 0; ; i++) {
state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
if (!state_node)
break;
of_node_put(state_node);
}
of_node_put(cpu_node);
state_count = i + start_idx;
if (state_count > CPUIDLE_STATE_MAX)
state_count = CPUIDLE_STATE_MAX;
return state_count;
}
static void __init mtk_cpupm_node_init(void)
{
struct device_node *node = NULL;
cpupm_mcusys_base_addr = NULL;
cpupm_syssram_base_addr = NULL;
node = of_find_compatible_node(NULL, NULL,
"mediatek,mcusys-ctrl");
if (node) {
cpupm_mcusys_base_addr = of_iomap(node, 0);
of_node_put(node);
}
node = of_find_compatible_node(NULL, NULL,
"mediatek,cpupm-sysram");
if (node) {
cpupm_syssram_base_addr = of_iomap(node, 0);
of_node_put(node);
}
}
int mtk_cpupm_dbg_init(void)
{
pm_qos_add_request(&cpuidle_dbg_qos_req,
PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
mtk_cpupm_node_init();
mtk_cpuidle_status_init();
mtk_cpc_init();
mtk_idle_procfs_init();
return 0;
}
void mtk_cpupm_dbg_exit(void)
{
mtk_idle_procfs_exit();
mtk_cpuidle_status_exit();
mtk_cpc_exit();
pm_qos_remove_request(&cpuidle_dbg_qos_req);
}
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_CPUPM_DBG_H__
#define __MTK_CPUPM_DBG_H__
int mtk_cpupm_mcusys_write(int ofs, unsigned int val);
unsigned int mtk_cpupm_mcusys_read(int ofs);
int mtk_cpupm_syssram_write(int ofs, unsigned int val);
unsigned int mtk_cpupm_syssram_read(int ofs);
void mtk_cpupm_block(void);
void mtk_cpupm_allow(void);
int mtk_cpupm_get_idle_state_count(int cpu);
#endif /* __MTK_CPUPM_DBG_H__ */
@@ -0,0 +1,130 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include <mtk_lp_plat_reg.h>
#include "mtk_cpupm_dbg.h"
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
#define MTK_IDLE_PROC_FS_NAME "cpuidle"
static int idle_proc_info_show(struct seq_file *m, void *v)
{
int cpu;
unsigned long long ts, rem;
ts = mtk_cpupm_syssram_read(SYSRAM_RECENT_CNT_TS_H);
ts = (ts << 32) | mtk_cpupm_syssram_read(SYSRAM_RECENT_CNT_TS_L);
ts = div64_u64_rem(ts, 1000 * 1000 * 1000, &rem);
seq_puts(m, "\n========= Power off count =========\n");
seq_puts(m, "\n-- Within 5 seconds before dumped --\n");
seq_printf(m, "\nLast dump timestamp = %llu.%09llu\n",
ts, rem);
seq_printf(m, "%8s", "cpu:");
for_each_possible_cpu(cpu)
seq_printf(m, " %d", mtk_cpupm_syssram_read(
SYSRAM_RECENT_CPU_CNT(cpu)));
seq_puts(m, "\n");
seq_printf(m, "%8s %d\n",
"cluster:",
mtk_cpupm_syssram_read(SYSRAM_RECENT_CPUSYS_CNT));
seq_printf(m, "%8s %d\n",
"mcusys:",
mtk_cpupm_syssram_read(SYSRAM_RECENT_MCUSYS_CNT));
seq_printf(m, "%8s 0x%x\n",
"online:",
mtk_cpupm_syssram_read(SYSRAM_CPU_ONLINE));
seq_puts(m, "\n---- Total ----\n");
seq_printf(m, "%8s %d\n",
"cluster:",
mtk_cpupm_syssram_read(SYSRAM_CPUSYS_CNT)
+ mtk_cpupm_syssram_read(SYSRAM_RECENT_CPUSYS_CNT));
seq_printf(m, "%8s %d\n",
"mcusys:",
mtk_cpupm_syssram_read(SYSRAM_MCUSYS_CNT)
+ mtk_cpupm_syssram_read(SYSRAM_RECENT_MCUSYS_CNT));
return 0;
}
static ssize_t idle_proc_info_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
return count;
}
static int idle_proc_enable_show(struct seq_file *m, void *v)
{
seq_printf(m, "%s()\n", __func__);
return 0;
}
static ssize_t idle_proc_enable_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_state_enable(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
PROC_FOPS(info);
PROC_FOPS(enable);
int __init mtk_idle_procfs_init(void)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(info),
PROC_ENTRY(enable)
};
dir = proc_mkdir(MTK_IDLE_PROC_FS_NAME, NULL);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return -ENOMEM;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
mtk_idle_procfs_state_dir_init(dir);
mtk_idle_procfs_cpc_dir_init(dir);
mtk_idle_procfs_profile_dir_init(dir);
mtk_idle_procfs_control_dir_init(dir);
return 0;
}
void __exit mtk_idle_procfs_exit(void)
{
remove_proc_subtree(MTK_IDLE_PROC_FS_NAME, NULL);
}
@@ -0,0 +1,75 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_IDLE_PROCFS__
#define __MTK_IDLE_PROCFS__
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
struct idle_proc_entry {
const char *name;
const struct file_operations *fops;
};
#define PROC_FOPS(name) \
static int idle_proc_ ## name ## _open(struct inode *inode, struct file *file) \
{ \
return single_open(file, idle_proc_ ## name ## _show, PDE_DATA(inode));\
} \
static const struct file_operations idle_proc_ ## name ## _fops = { \
.owner = THIS_MODULE, \
.open = idle_proc_ ## name ## _open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
.write = idle_proc_ ## name ## _write, \
}
#define PROC_ENTRY(name) {__stringify(name), &idle_proc_ ## name ## _fops}
#define PROC_CREATE_NODE(dir, entry) \
do { \
if (!proc_create(entry.name, 0644, dir, entry.fops)) \
pr_notice("%s(), create procfs cpuidle node %s failed\n", \
__func__, entry.name); \
} while (0)
#define mtk_idle_procfs_alloc_from_user(buf, userbuf, count) \
do { \
buf = (char *)__get_free_page(GFP_USER); \
\
if (!buf) \
break; \
\
if (count >= PAGE_SIZE \
|| copy_from_user(buf, userbuf, count)) { \
free_page((unsigned long)buf); \
buf = NULL; \
break; \
} \
\
buf[count] = '\0'; \
\
} while (0)
#define mtk_idle_procfs_free(buf) \
do { \
if (!buf) \
break; \
\
free_page((unsigned long)buf); \
} while (0)
void __init mtk_idle_procfs_cpc_dir_init(struct proc_dir_entry *parent);
void __init mtk_idle_procfs_state_dir_init(struct proc_dir_entry *parent);
void __init mtk_idle_procfs_profile_dir_init(struct proc_dir_entry *parent);
void __init mtk_idle_procfs_control_dir_init(struct proc_dir_entry *parent);
int __init mtk_idle_procfs_init(void);
void __exit mtk_idle_procfs_exit(void);
#endif /* __MTK_IDLE_PROCFS__ */
@@ -0,0 +1,315 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include <mtk_lp_plat_apmcu_mbox.h>
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
static int idle_proc_log_show(struct seq_file *m, void *v)
{
seq_printf(m, "CPU idle log : %s\n",
mtk_cpuidle_ctrl_log_sta_get() ?
"Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/control/log\n");
return 0;
}
static ssize_t idle_proc_log_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_ctrl_log_en(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_timer_show(struct seq_file *m, void *v)
{
seq_printf(m, "Limit sleep timer : %s\n",
mtk_cpuidle_ctrl_timer_sta_get() ?
"Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/control/timer\n");
return 0;
}
static ssize_t idle_proc_timer_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_ctrl_timer_en(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_stress_show(struct seq_file *m, void *v)
{
seq_printf(m, "CPU idle stress : %s\n",
mtk_cpuidle_get_stress_status() ?
"Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/control/stress\n");
return 0;
}
static ssize_t idle_proc_stress_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_set_stress_test(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_stress_time_show(struct seq_file *m, void *v)
{
seq_printf(m, "CPU idle stress interval time : %u\n",
mtk_cpuidle_get_stress_time());
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [us] > /proc/cpuidle/control/stress_time\n");
return 0;
}
static ssize_t idle_proc_stress_time_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int interval_us;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &interval_us) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_set_stress_time(interval_us);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_buck_mode_show(struct seq_file *m, void *v)
{
int mode;
struct mode_param {
int id;
char *str;
};
const struct mode_param table[] = {
{MCUPM_BUCK_NORMAL_MODE, "on"},
{MCUPM_BUCK_LP_MODE, "low power"},
{MCUPM_BUCK_OFF_MODE, "off"},
{NF_MCUPM_BUCK_MODE, "unknown"},
};
mode = mtk_get_mcupm_buck_mode();
if (mode < 0 || mode > NF_MCUPM_BUCK_MODE)
mode = NF_MCUPM_BUCK_MODE;
seq_printf(m, "Vproc/Vproc_sram buck mode : %s\n",
table[mode].str);
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [mode] > /proc/cpuidle/control/buck_mode\n");
seq_puts(m, "mode:\n");
for (mode = 0; mode < NF_MCUPM_BUCK_MODE; mode++)
seq_printf(m, "\t%d: %s\n",
table[mode].id,
table[mode].str);
return 0;
}
static ssize_t idle_proc_buck_mode_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int mode;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &mode) != 0) {
ret = -EINVAL;
goto free;
}
if (mode < NF_MCUPM_BUCK_MODE)
mtk_set_mcupm_buck_mode(mode);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_armpll_mode_show(struct seq_file *m, void *v)
{
int mode;
struct mode_param {
int id;
char *str;
};
const struct mode_param table[] = {
{MCUPM_ARMPLL_ON, "on"},
{MCUPM_ARMPLL_GATING, "gating"},
{MCUPM_ARMPLL_OFF, "off"},
{NF_MCUPM_ARMPLL_MODE, "unknown"},
};
mode = mtk_get_mcupm_pll_mode();
if (mode < 0 || mode > NF_MCUPM_ARMPLL_MODE)
mode = NF_MCUPM_ARMPLL_MODE;
seq_printf(m, "armpll mode : %s\n",
table[mode].str);
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [mode] > /proc/cpuidle/control/armpll_mode\n");
seq_puts(m, "mode:\n");
for (mode = 0; mode < NF_MCUPM_ARMPLL_MODE; mode++)
seq_printf(m, "\t%d: %s\n",
table[mode].id,
table[mode].str);
return 0;
}
static ssize_t idle_proc_armpll_mode_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int mode;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &mode) != 0) {
ret = -EINVAL;
goto free;
}
if (mode < NF_MCUPM_ARMPLL_MODE)
mtk_set_mcupm_pll_mode(mode);
free:
mtk_idle_procfs_free(buf);
return ret;
}
PROC_FOPS(log);
PROC_FOPS(timer);
PROC_FOPS(stress);
PROC_FOPS(stress_time);
PROC_FOPS(buck_mode);
PROC_FOPS(armpll_mode);
void __init mtk_idle_procfs_control_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(log),
PROC_ENTRY(timer),
PROC_ENTRY(stress),
PROC_ENTRY(stress_time),
PROC_ENTRY(buck_mode),
PROC_ENTRY(armpll_mode)
};
dir = proc_mkdir("control", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include "mtk_cpuidle_cpc.h"
#include "mtk_idle_procfs.h"
static int idle_proc_auto_off_show(struct seq_file *m, void *v)
{
seq_printf(m, "auto_off mode : %s\n",
cpc_get_auto_off_sta() ? "Enabled" : "Disabled");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/cpc/auto_off\n");
return 0;
}
static ssize_t idle_proc_auto_off_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
if (enabled)
cpc_auto_off_en();
else
cpc_auto_off_dis();
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_auto_off_thres_show(struct seq_file *m, void *v)
{
seq_printf(m, "auto_off threshold = %u us\n",
(unsigned int)cpc_get_auto_off_thres());
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [us] > /proc/cpuidle/cpc/auto_off_thres\n");
return 0;
}
static ssize_t idle_proc_auto_off_thres_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int threshold_us;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &threshold_us) != 0) {
ret = -EINVAL;
goto free;
}
cpc_set_auto_off_thres(threshold_us);
free:
mtk_idle_procfs_free(buf);
return ret;
}
PROC_FOPS(auto_off);
PROC_FOPS(auto_off_thres);
void __init mtk_idle_procfs_cpc_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(auto_off),
PROC_ENTRY(auto_off_thres)
};
dir = proc_mkdir("cpc", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include "mtk_cpuidle_cpc.h"
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
static int idle_proc_kernel_show(struct seq_file *m, void *v)
{
seq_printf(m, "%s\n", __func__);
return 0;
}
static ssize_t idle_proc_kernel_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
/* Start/Stop kernel profile */
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_target_state_show(struct seq_file *m, void *v)
{
seq_printf(m, "%s\n", __func__);
return 0;
}
static ssize_t idle_proc_target_state_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
int state;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtoint(buf, 10, &state) != 0) {
ret = -EINVAL;
goto free;
}
/* Set target state */
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_ratio_show(struct seq_file *m, void *v)
{
mtk_cpuidle_prof_ratio_dump(m);
return 0;
}
static ssize_t idle_proc_ratio_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
if (enabled)
mtk_cpuidle_prof_ratio_start();
else
mtk_cpuidle_prof_ratio_stop();
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_cpc_show(struct seq_file *m, void *v)
{
mtk_cpc_prof_lat_dump(m);
return 0;
}
static ssize_t idle_proc_cpc_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
if (enabled)
mtk_cpc_prof_start();
else
mtk_cpc_prof_stop();
free:
mtk_idle_procfs_free(buf);
return ret;
}
PROC_FOPS(kernel);
PROC_FOPS(target_state);
PROC_FOPS(ratio);
PROC_FOPS(cpc);
void __init mtk_idle_procfs_profile_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(kernel),
PROC_ENTRY(target_state),
PROC_ENTRY(ratio),
PROC_ENTRY(cpc)
};
dir = proc_mkdir("profile", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,218 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/cpuidle.h>
#include <linux/seq_file.h>
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
#define ALL_LITTLR_CPU_ID (10)
#define ALL_BIG_CPU_ID (20)
#define ALL_CPU_ID (100)
#define CPU_L_MASK (0x3f)
#define CPU_B_MASK (0xc0)
#define ALL_CPU_MASK (0xff)
static void idle_proc_state_uasge_print(struct seq_file *m, char *node_name)
{
seq_puts(m, "\n======== Command Usage ========\n");
seq_printf(m, "%s > /proc/cpuidle/state/%s\n",
"echo [cpu_id] [state_index] [val(dec)]",
node_name);
seq_puts(m, "\t cpu_id: 0~7 -> cpu number\n");
seq_printf(m, "\t %3d -> all little CPU\n", ALL_LITTLR_CPU_ID);
seq_printf(m, "\t %3d -> all big CPU\n", ALL_BIG_CPU_ID);
seq_printf(m, "\t %3d -> all CPU\n", ALL_CPU_ID);
seq_puts(m, "\t state_index: >0 (index 0 can't be modified)\n");
seq_puts(m, "\n");
}
static void idle_proc_state_param_show(struct seq_file *m, int param)
{
struct cpuidle_driver *drv = NULL;
int i, cpu, desc = 0;
for_each_possible_cpu(cpu) {
//drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
if (desc == 0) {
seq_printf(m, "%-12s:", "state_index");
for (i = 0; i < drv->state_count; i++)
seq_printf(m, "%7d ", i);
seq_puts(m, "\n");
desc = 1;
}
seq_printf(m, "%11s%d:", "cpu", cpu);
for (i = 0; i < drv->state_count; i++)
seq_printf(m, "%7d ",
mtk_cpuidle_get_param(drv, i, param));
seq_puts(m, "\n");
}
}
static int idle_proc_state_param_setting(char *cmd, int param)
{
struct cpuidle_driver *drv = NULL;
char *args;
unsigned int cpu_mask, state_idx, val;
int cpu, i;
struct cmd_param {
unsigned int id;
unsigned int mask;
};
const struct cmd_param table[] = {
{ALL_LITTLR_CPU_ID, CPU_L_MASK},
{ALL_BIG_CPU_ID, CPU_B_MASK},
{ALL_CPU_ID, ALL_CPU_MASK},
};
args = strsep(&cmd, " ");
if (!args || kstrtoint(args, 10, &cpu) != 0)
return -EINVAL;
args = strsep(&cmd, " ");
if (!args || kstrtouint(args, 10, &state_idx) != 0)
return -EINVAL;
args = strsep(&cmd, " ");
if (!args || kstrtouint(args, 10, &val) != 0)
return -EINVAL;
if (!state_idx)
return -EINVAL;
cpu_mask = 0;
if (cpu >= 0 && cpu < nr_cpu_ids) {
cpu_mask = (1 << cpu);
} else {
for (i = 0; i < ARRAY_SIZE(table); i++) {
if (cpu == table[i].id) {
cpu_mask = table[i].mask;
break;
}
}
}
for_each_possible_cpu(cpu) {
if ((cpu_mask & (1 << cpu)) == 0)
continue;
//drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
mtk_cpuidle_set_param(drv, state_idx, param, val);
}
return 0;
}
static int idle_proc_latency_show(struct seq_file *m, void *v)
{
seq_puts(m, "==== CPU idle state: Exit latency ====\n");
idle_proc_state_param_show(m, IDLE_PARAM_LAT);
idle_proc_state_uasge_print(m, "latency");
return 0;
}
static ssize_t idle_proc_latency_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
idle_proc_state_param_setting(buf, IDLE_PARAM_LAT);
mtk_idle_procfs_free(buf);
return count;
}
static int idle_proc_residency_show(struct seq_file *m, void *v)
{
seq_puts(m, "==== CPU idle state: Target residency ====\n");
idle_proc_state_param_show(m, IDLE_PARAM_RES);
idle_proc_state_uasge_print(m, "residency");
return 0;
}
static ssize_t idle_proc_residency_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
idle_proc_state_param_setting(buf, IDLE_PARAM_RES);
mtk_idle_procfs_free(buf);
return count;
}
static int idle_proc_enabled_show(struct seq_file *m, void *v)
{
seq_puts(m, "==== CPU idle state: Enabled ====\n");
idle_proc_state_param_show(m, IDLE_PARAM_EN);
idle_proc_state_uasge_print(m, "enabled");
return 0;
}
static ssize_t idle_proc_enabled_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
idle_proc_state_param_setting(buf, IDLE_PARAM_EN);
mtk_idle_procfs_free(buf);
return count;
}
PROC_FOPS(latency);
PROC_FOPS(residency);
PROC_FOPS(enabled);
void __init mtk_idle_procfs_state_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(latency),
PROC_ENTRY(residency),
PROC_ENTRY(enabled)
};
dir = proc_mkdir("state", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,30 @@
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/inc/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/mtk_lpm_fs/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/inc/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/modules/include/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/modules/include/mt6781/
obj-y += mt6781_dbg.o
obj-y += mt6781_dbg_lpm_fs.o
obj-y += mt6781_dbg_lpm_rc_fs.o
obj-y += mt6781_dbg_spm_fs.o
obj-y += mt6781_logger.o
obj-y += mt6781_lpm_trace_event.o
obj-y += mt6781_cpupm_dbg.o
mt6781_cpupm_dbg-y += mtk_cpupm_dbg.o
mt6781_cpupm_dbg-y += mtk_cpuidle_status.o
mt6781_cpupm_dbg-y += mtk_cpuidle_cpc.o
mt6781_cpupm_dbg-y += mtk_idle_procfs.o
mt6781_cpupm_dbg-y += mtk_idle_procfs_state.o
mt6781_cpupm_dbg-y += mtk_idle_procfs_cpc.o
mt6781_cpupm_dbg-y += mtk_idle_procfs_profile.o
mt6781_cpupm_dbg-y += mtk_idle_procfs_control.o
obj-$(CONFIG_MTK_LPM_GS_DUMP_SUPPORT) += mt6781_power_gs_compare.o
obj-$(CONFIG_MTK_LPM_GS_DUMP_SUPPORT) += mtk_power_gs_6366_array.o
obj-$(CONFIG_MTK_LPM_GS_DUMP_SUPPORT) += mtk_dcm_array.o
obj-$(CONFIG_MTK_LPM_GS_DUMP_SUPPORT) += mtk_cg_array.o
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <gs/mtk_lpm_pwr_gs.h>
#include <mt6781_dbg_fs_common.h>
#include <mt6781_power_gs_compare.h>
static void __exit mt6781_dbg_exit(void)
{
#ifdef MTK_LPM_DBG_COMMON
mtk_dbg_common_fs_exit();
#endif
mt6781_dbg_lpm_fs_deinit();
mt6781_dbg_spm_fs_deinit();
mt6781_dbg_lpm_deinit();
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
mt6781_power_gs_deinit();
#endif
}
static int __init mt6781_dbg_init(void)
{
#ifdef MTK_LPM_DBG_COMMON
mtk_dbg_common_fs_init();
#endif
mt6781_dbg_lpm_init();
mt6781_dbg_lpm_fs_init();
mt6781_dbg_spm_fs_init();
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
mt6781_power_gs_init();
#endif
pr_info("%s %d: finish", __func__, __LINE__);
return 0;
}
module_init(mt6781_dbg_init);
module_exit(mt6781_dbg_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MT6781 Low Power FileSystem");
MODULE_AUTHOR("MediaTek Inc.");
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#ifndef __MT6781_DBG_FS_COMMON_H__
#define __MT6781_DBG_FS_COMMON_H__
/* Determine for user name handle */
#define MT_LP_RQ_USER_NAME_LEN (4)
#define MT_LP_RQ_USER_CHAR_U (8)
#define MT_LP_RQ_USER_CHAR_MASK (0xFF)
/* Determine for resource usage id */
#define MT_LP_RQ_ID_ALL_USAGE (-1)
int mt6781_dbg_lpm_init(void);
int mt6781_dbg_lpm_deinit(void);
int mt6781_dbg_lpm_fs_init(void);
int mt6781_dbg_lpm_fs_deinit(void);
int mt6781_dbg_spm_fs_init(void);
int mt6781_dbg_spm_fs_deinit(void);
#endif /* __MTK_LP_PLAT_FS_H__ */
@@ -0,0 +1,233 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/device.h>
#include <linux/of_device.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mtk_resource_constraint_v1.h>
#include <mt6781_pwr_ctrl.h>
#include <mt6781_dbg_fs_common.h>
#include <mt6781_cond.h>
#include <mt6781_spm_comm.h>
#include <mtk_lpm_sysfs.h>
#include <mtk_lp_sysfs.h>
#include <mtk_lpm_platform.h>
#define MT6781_GENERIC_TRACE_NODE_INIT(_n, _r, _w) ({\
_n.op.fs_read = _r;\
_n.op.fs_write = _w;\
_n.op.priv = &_n; })
#define mt6781_dbg_lpm_trace_log(buf, sz, len, fmt, args...) ({\
if (len < sz)\
len += scnprintf(buf + len, sz - len,\
fmt, ##args); })
/* Common section */
#define MT6781_DGB_TRACE_COMMON_BASE (0x0)
#define MT6781_DGB_TRACE_COMMON_SZ (0x20)
#define TRACE_DBG_LAST_RC_INFO_CPU_SHIFT (28)
#define TRACE_DBG_LAST_RC_INFO_CPU_MASK (0xf)
#define TRACE_DBG_LAST_RC_INFO_RCID_SHIFT (0)
#define TRACE_DBG_LAST_RC_INFO_RCID_MASK (0xffff)
#define TRACE_DBG_LAST_RC_INFO_CPU(info)\
((info >> TRACE_DBG_LAST_RC_INFO_CPU_SHIFT)\
& TRACE_DBG_LAST_RC_INFO_CPU_MASK)
#define TRACE_DBG_LAST_RC_INFO_RC_ID(info)\
((info >> TRACE_DBG_LAST_RC_INFO_RCID_SHIFT)\
& TRACE_DBG_LAST_RC_INFO_RCID_MASK)
/* Suspend section */
#define MT6781_DGB_TRACE_SUSPEND_BASE \
(MT6781_DGB_TRACE_COMMON_BASE + MT6781_DGB_TRACE_COMMON_SZ)
#define MT6781_DGB_TRACE_SUSPEND_SZ (0xe0)
/* Low power section */
#define MT6781_DGB_TRACE_LP_BASE \
(MT6781_DGB_TRACE_SUSPEND_BASE + MT6781_DGB_TRACE_SUSPEND_SZ)
#define MT6781_DGB_TRACE_LP_SZ (0x200)
struct MT6781_LPM_TRACE_NODE {
struct mtk_lp_sysfs_handle handle;
struct mtk_lp_sysfs_op op;
};
struct mtk_lp_sysfs_handle mt6781_entry_lpm_trace;
struct MT6781_LPM_TRACE_NODE mt6781_lpm_trace_node_comm;
struct MT6781_LPM_TRACE_NODE mt6781_lpm_trace_node_lp;
struct MT6781_LPM_TRACE_NODE mt6781_lpm_trace_node_suspend;
struct MT6781_TRACE_COMM {
unsigned int magic;
unsigned int common_fp;
unsigned int rc_timestamp_h;
unsigned int rc_timestamp_l;
unsigned int rc_info;
unsigned int rc_fp;
unsigned int rc_valid;
};
static ssize_t mt6781_dbg_lpm_trace_suspend_show(char *ToUserBuf,
size_t sz, void *priv)
{
struct MTK_LPM_PLAT_TRACE trace;
unsigned int dump[4], offset = 0;
size_t len = 0;
int ret = 0;
ret = mtk_lpm_platform_trace_get(MT_LPM_PLAT_TRACE_SYSRAM, &trace);
if (ret || !trace.read)
return -ENOENT;
while (((offset + sizeof(dump)) < MT6781_DGB_TRACE_SUSPEND_SZ)) {
dump[0] = dump[1] = dump[2] = dump[3] = 0;
trace.read((MT6781_DGB_TRACE_SUSPEND_BASE + offset),
dump, sizeof(dump));
mt6781_dbg_lpm_trace_log(ToUserBuf, sz, len,
"%03xh: %08x %08x %08x %08x\n", offset,
dump[0], dump[1], dump[2], dump[3]);
offset += sizeof(dump);
}
return len;
}
static ssize_t mt6781_dbg_lpm_trace_lp_show(char *ToUserBuf,
size_t sz, void *priv)
{
struct MTK_LPM_PLAT_TRACE trace;
unsigned int dump[4], offset = 0;
size_t len = 0;
int ret = 0;
ret = mtk_lpm_platform_trace_get(MT_LPM_PLAT_TRACE_SYSRAM, &trace);
if (ret || !trace.read)
return -ENOENT;
while (((offset + sizeof(dump)) <= MT6781_DGB_TRACE_LP_SZ)) {
dump[0] = dump[1] = dump[2] = dump[3] = 0;
trace.read((MT6781_DGB_TRACE_LP_BASE + offset),
dump, sizeof(dump));
mt6781_dbg_lpm_trace_log(ToUserBuf, sz, len,
"%03xh: %08x %08x %08x %08x\n", offset,
dump[0], dump[1], dump[2], dump[3]);
offset += sizeof(dump);
}
return len;
}
static ssize_t mt6781_dbg_lpm_trace_comm_show(char *ToUserBuf,
size_t sz, void *priv)
{
struct MTK_LPM_PLAT_TRACE trace;
struct MT6781_TRACE_COMM *data = NULL;
size_t len = 0, rSz = 0;
int ret = 0;
ret = mtk_lpm_platform_trace_get(MT_LPM_PLAT_TRACE_SYSRAM, &trace);
if (ret || !trace.read)
return -ENOENT;
data = kzalloc(sizeof(struct MT6781_TRACE_COMM),
GFP_KERNEL);
if (!data)
return -ENOMEM;
rSz = (sizeof(struct MT6781_TRACE_COMM) > MT6781_DGB_TRACE_COMMON_SZ) ?
MT6781_DGB_TRACE_COMMON_SZ : sizeof(struct MT6781_TRACE_COMM);
len = trace.read(MT6781_DGB_TRACE_COMMON_BASE, data, rSz);
if (len > 0) {
unsigned long long timestamp;
len = 0;
timestamp = data->rc_timestamp_h;
timestamp = (timestamp << 32) | data->rc_timestamp_l;
mt6781_dbg_lpm_trace_log(ToUserBuf, sz, len,
"magic: 0x%x\n", data->magic);
mt6781_dbg_lpm_trace_log(ToUserBuf, sz, len,
"common_footfprint: 0x%x\n", data->common_fp);
mt6781_dbg_lpm_trace_log(ToUserBuf, sz, len,
"timestamp: %llu.%03llu\n",
timestamp/1000000000,
timestamp%1000000000);
mt6781_dbg_lpm_trace_log(ToUserBuf, sz, len,
"last constriant\n - cpu:%d\n - rc_id:%d\n - valid:0x%x\n",
TRACE_DBG_LAST_RC_INFO_CPU(data->rc_info),
TRACE_DBG_LAST_RC_INFO_RC_ID(data->rc_info),
data->rc_valid);
}
kfree(data);
return sz;
}
int mt6781_dbg_lpm_init(void)
{
mtk_lpm_sysfs_root_entry_create();
mtk_lpm_sysfs_sub_entry_add("trace", 0644, NULL,
&mt6781_entry_lpm_trace);
MT6781_GENERIC_TRACE_NODE_INIT(mt6781_lpm_trace_node_comm,
mt6781_dbg_lpm_trace_comm_show,
NULL);
mtk_lpm_sysfs_sub_entry_node_add("common", 0400,
&mt6781_lpm_trace_node_comm.op,
&mt6781_entry_lpm_trace,
&mt6781_lpm_trace_node_comm.handle);
MT6781_GENERIC_TRACE_NODE_INIT(mt6781_lpm_trace_node_lp,
mt6781_dbg_lpm_trace_lp_show,
NULL);
mtk_lpm_sysfs_sub_entry_node_add("lp", 0400,
&mt6781_lpm_trace_node_lp.op,
&mt6781_entry_lpm_trace,
&mt6781_lpm_trace_node_lp.handle);
MT6781_GENERIC_TRACE_NODE_INIT(mt6781_lpm_trace_node_suspend,
mt6781_dbg_lpm_trace_suspend_show,
NULL);
mtk_lpm_sysfs_sub_entry_node_add("suspend", 0400,
&mt6781_lpm_trace_node_suspend.op,
&mt6781_entry_lpm_trace,
&mt6781_lpm_trace_node_suspend.handle);
return 0;
}
int mt6781_dbg_lpm_deinit(void)
{
mtk_lpm_sysfs_entry_node_remove(&mt6781_lpm_trace_node_suspend.handle);
mtk_lpm_sysfs_entry_node_remove(&mt6781_lpm_trace_node_lp.handle);
mtk_lpm_sysfs_entry_node_remove(&mt6781_lpm_trace_node_comm.handle);
mtk_lpm_sysfs_entry_node_remove(&mt6781_entry_lpm_trace);
return 0;
}
@@ -0,0 +1,637 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/console.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/spinlock.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mtk_resource_constraint_v1.h>
#include <mt6781_pwr_ctrl.h>
#include <mt6781_dbg_fs_common.h>
#include <mt6781_cond.h>
#include <mt6781_spm_comm.h>
#include <mtk_lpm_sysfs.h>
#include <mtk_lp_sysfs.h>
#include <mtk_lpm_timer.h>
#define SPM_RC_UPDATE_COND_ID_MASK 0xffff
#define SPM_RC_UPDATE_COND_RC_ID_MASK 0xffff
#define SPM_RC_UPDATE_COND_RC_ID_SHIFT (16)
#define SPM_RC_UPDATE_COND_CTRL_ID(rc, cond)\
(((rc & SPM_RC_UPDATE_COND_RC_ID_MASK)\
<< SPM_RC_UPDATE_COND_RC_ID_SHIFT)\
| (cond & SPM_RC_UPDATE_COND_ID_MASK))
#define MT6781_LPM_RC_RATIO_DEFAULT 10000
#define mt6781_rc_log(buf, sz, len, fmt, args...) ({\
if (len < sz)\
len += scnprintf(buf + len, sz - len,\
fmt, ##args); })
#define MT6781_DBG_SMC(_id, _act, _rc, _param) ({\
(unsigned long)mtk_lpm_smc_spm_dbg(_id, _act, _rc, _param); })
enum MT6781_RC_RATIO_TYPE {
MT6781_RC_RATIO_UNKNOWN,
MT6781_RC_RATIO_ENABLE,
MT6781_RC_RATIO_DISABLE,
MT6781_RC_RATIO_ENABLE_INFO,
MT6781_RC_RATIO_INFO,
};
enum MT6781_RC_NODE_TYPE {
MT6781_RC_NODE_STATE,
MT6781_RC_NODE_RC_ENABLE,
MT6781_RC_NODE_RC_STATE_SIMPLE,
MT6781_RC_NODE_RC_STATE,
MT6781_RC_NODE_COND_ENABLE,
MT6781_RC_NODE_COND_STATE,
MT6781_RC_NODE_COND_SET,
MT6781_RC_NODE_COND_CLR,
MT6781_RC_NODE_RATIO_ENABLE,
MT6781_RC_NODE_RATIO_INTERVAL,
MT6781_RC_NODE_VALID_BBLPM,
MT6781_RC_NODE_VALID_TRACE,
MT6781_RC_NODE_VALID_TIMESTAMP,
MT6781_RC_NODE_MAX
};
struct MT6781_RC_NODE {
const char *name;
int rc_id;
int type;
struct mtk_lp_sysfs_handle handle;
struct mtk_lp_sysfs_op op;
};
struct MT6781_RC_ENTERY {
const char *name;
struct mtk_lp_sysfs_handle handle;
};
struct MT6781_RC_COND_HANDLES {
struct MT6781_RC_ENTERY root;
struct MT6781_RC_NODE hSet;
struct MT6781_RC_NODE hClr;
struct MT6781_RC_NODE hState;
struct MT6781_RC_NODE hEnable;
};
struct MT6781_RC_VALID_HANDLES {
struct MT6781_RC_ENTERY root;
struct MT6781_RC_NODE hBblpm;
struct MT6781_RC_NODE hTrace;
struct MT6781_RC_NODE hTimestamp;
};
struct MT6781_RC_RATIO_HANDLES {
struct MT6781_RC_ENTERY root;
struct MT6781_RC_NODE hInterval;
struct MT6781_RC_NODE hEnable;
};
struct MT6781_RC_HANDLE_BASIC {
struct MT6781_RC_ENTERY root;
struct MT6781_RC_NODE hEnable;
struct MT6781_RC_NODE hState;
};
struct MT6781_RC_HANDLE {
struct MT6781_RC_HANDLE_BASIC basic;
struct MT6781_RC_COND_HANDLES hCond;
struct MT6781_RC_VALID_HANDLES valid;
};
#define MT6781_CONSTRAINT_GENERIC_OP(op, _priv) ({\
op.fs_read = mt6781_generic_rc_read;\
op.fs_write = mt6781_generic_rc_write;\
op.priv = _priv; })
#define MT6781_GENERIC_RC_NODE_INIT(_n, _name, _id, _type) ({\
_n.name = _name;\
_n.type = _type;\
_n.rc_id = _id;\
MT6781_CONSTRAINT_GENERIC_OP(_n.op, &_n); })
struct mtk_lp_sysfs_handle mt6781_entry_rc;
struct MT6781_RC_HANDLE rc_dram;
struct MT6781_RC_HANDLE rc_syspll;
struct MT6781_RC_HANDLE rc_bus26m;
struct MT6781_RC_HANDLE_BASIC rc_cpu_buckldo;
struct MT6781_RC_RATIO_HANDLES rc_Ratio;
struct MT6781_RC_NODE rc_state;
struct mt_lpm_timer rc_ratio_timer;
static char *mt6781_spm_cond_cg_str[PLAT_SPM_COND_MAX] = {
[PLAT_SPM_COND_MTCMOS_0] = "MTCMOS_0",
[PLAT_SPM_COND_CG_INFRA_0] = "INFRA_0",
[PLAT_SPM_COND_CG_INFRA_1] = "INFRA_1",
[PLAT_SPM_COND_CG_INFRA_2] = "INFRA_2",
[PLAT_SPM_COND_CG_INFRA_3] = "INFRA_3",
[PLAT_SPM_COND_CG_INFRA_4] = "INFRA_4",
[PLAT_SPM_COND_CG_INFRA_5] = "INFRA_5",
[PLAT_SPM_COND_CG_MMSYS_0] = "MMSYS_0",
[PLAT_SPM_COND_CG_MMSYS_1] = "MMSYS_1",
[PLAT_SPM_COND_CG_MMSYS_2] = "MMSYS_2",
[PLAT_SPM_COND_CG_MMSYS_3] = "MMSYS_3",
};
static char *mt6781_spm_cond_pll_str[PLAT_SPM_COND_PLL_MAX] = {
[PLAT_SPM_COND_UNIVPLL] = "UNIVPLL",
[PLAT_SPM_COND_MFGPLL] = "MFGPLL",
[PLAT_SPM_COND_MSDCPLL] = "MSDCPLL",
[PLAT_SPM_COND_TVPLL] = "TVPLL",
[PLAT_SPM_COND_MMPLL] = "MMPLL",
};
int mt6781_lpm_rc_cond_ctrl(int rc_id, unsigned int act,
unsigned int cond_id, unsigned int value)
{
unsigned int cond_ctrl_id;
int res = 0;
cond_ctrl_id = SPM_RC_UPDATE_COND_CTRL_ID(rc_id, cond_id);
if (cond_id < PLAT_SPM_COND_MAX)
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_COND_CTRL,
act, cond_ctrl_id, value);
else if ((cond_id - PLAT_SPM_COND_MAX) < PLAT_SPM_COND_PLL_MAX)
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_COND_CTRL,
act, cond_ctrl_id, !!value);
else
pr_info("[%s:%d] - unknown cond id = %u\n",
__func__, __LINE__, cond_id);
return res;
}
int mt6781_lpm_rc_ratio_timer_func(unsigned long long dur, void *priv)
{
/* spm tick to ms: tick/32 (ms) */
#define RC_RESIDENCY_COVERT(x) (x>>5)
#define RC_COVERT_RATIO(dur, x)\
((RC_RESIDENCY_COVERT(x) * 100)/dur)
unsigned long long rc_dram_t;
unsigned long long rc_syspll_t;
unsigned long long rc_bus26m_t;
rc_dram_t = (unsigned long long)
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_INFO,
(MT_LPM_SMC_ACT_GET | MT_LPM_SMC_ACT_CLR),
MT_RM_CONSTRAINT_ID_DRAM, 0);
rc_syspll_t = (unsigned long long)
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_INFO,
(MT_LPM_SMC_ACT_GET | MT_LPM_SMC_ACT_CLR),
MT_RM_CONSTRAINT_ID_SYSPLL, 0);
rc_bus26m_t = (unsigned long long)
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_INFO,
(MT_LPM_SMC_ACT_GET | MT_LPM_SMC_ACT_CLR),
MT_RM_CONSTRAINT_ID_BUS26M, 0);
pr_info("[name:spm&][RC] ratio, duration_ms:%llu, dram:%llu%%, syspll:%llu%%, bus26m:%llu%%\n",
dur, RC_COVERT_RATIO(dur, rc_dram_t),
RC_COVERT_RATIO(dur, rc_syspll_t),
RC_COVERT_RATIO(dur, rc_bus26m_t));
return 0;
}
static ssize_t mt6781_lpm_rc_block_info(int rc_id,
char *ToUserBuf,
size_t sz)
{
uint32_t block, b;
int i;
ssize_t len = 0;
block = (uint32_t)
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_COND_BLOCK,
MT_LPM_SMC_ACT_GET, rc_id, 0);
b = (uint32_t)
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_COND_CHECK,
MT_LPM_SMC_ACT_GET, rc_id, 0);
mt6781_rc_log(ToUserBuf, sz, len,
"blocked=%u, blocked_cond=0x%08x\n",
b, block);
for (i = 0, b = block >> SPM_COND_BLOCKED_CG_IDX;
i < PLAT_SPM_COND_MAX; i++)
mt6781_rc_log(ToUserBuf, sz, len,
"[%2d] %8s=0x%08lx\n", i,
mt6781_spm_cond_cg_str[i],
((b >> i) & 0x1) ?
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_BLOCK_DETAIL,
MT_LPM_SMC_ACT_GET, rc_id, i) : 0);
for (i = 0, b = block >> SPM_COND_BLOCKED_PLL_IDX;
i < PLAT_SPM_COND_PLL_MAX; i++)
mt6781_rc_log(ToUserBuf, sz, len,
"[%2d] %8s=%d\n",
(i + PLAT_SPM_COND_MAX),
mt6781_spm_cond_pll_str[i],
((b >> i) & 0x1));
return len;
}
static ssize_t mt6781_rc_state(int rc_id, char *ToUserBuf, size_t sz)
{
ssize_t len = 0;
unsigned long gpio200_datain = 0;
if (rc_id < 0)
return 0;
if (rc_id == MT_RM_CONSTRAINT_ID_BUS26M ||
rc_id == MT_RM_CONSTRAINT_ID_SYSPLL) {
gpio200_datain = MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_GPIO200_DATAIN,
MT_LPM_SMC_ACT_GET, 0, 0);
mt6781_rc_log(ToUserBuf, sz, len,
"enable=%lu(gpio200=%lu), count=%lu, rc-id=%d\n",
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_SWITCH,
MT_LPM_SMC_ACT_GET, rc_id, 0)
& MT_SPM_RC_VALID_SW,
gpio200_datain,
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_CNT,
MT_LPM_SMC_ACT_GET, rc_id, 0),
rc_id);
} else {
mt6781_rc_log(ToUserBuf, sz, len,
"enable=%lu, count=%lu, rc-id=%d\n",
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_SWITCH,
MT_LPM_SMC_ACT_GET, rc_id, 0)
& MT_SPM_RC_VALID_SW,
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_CNT,
MT_LPM_SMC_ACT_GET, rc_id, 0),
rc_id);
}
return len;
}
static ssize_t mt6781_generic_rc_read(char *ToUserBuf,
size_t sz, void *priv)
{
ssize_t len = 0;
struct MT6781_RC_NODE *node = (struct MT6781_RC_NODE *)priv;
if (!node)
return -EINVAL;
switch (node->type) {
case MT6781_RC_NODE_STATE:
mt6781_rc_log(ToUserBuf, sz, len, "count:%lu\n",
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_IDLE_CNT,
MT_LPM_SMC_ACT_GET, DBG_CTRL_COUNT, 0));
break;
case MT6781_RC_NODE_RC_STATE_SIMPLE:
len += mt6781_rc_state(node->rc_id, ToUserBuf, sz);
break;
case MT6781_RC_NODE_RC_STATE:
len += mt6781_rc_state(node->rc_id, ToUserBuf, sz);
len += mt6781_lpm_rc_block_info(node->rc_id,
ToUserBuf + len, sz - len);
break;
case MT6781_RC_NODE_RC_ENABLE:
mt6781_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_SWITCH,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6781_RC_NODE_COND_ENABLE:
mt6781_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_COND_CHECK,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6781_RC_NODE_COND_STATE:
len += mt6781_lpm_rc_block_info(node->rc_id, ToUserBuf, sz);
break;
case MT6781_RC_NODE_RATIO_ENABLE:
mt6781_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_CTRL,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6781_RC_NODE_RATIO_INTERVAL:
mt6781_rc_log(ToUserBuf, sz, len, "%lu\n",
mtk_lpm_timer_interval(&rc_ratio_timer));
break;
case MT6781_RC_NODE_VALID_BBLPM:
mt6781_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_BBLPM,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6781_RC_NODE_VALID_TRACE:
mt6781_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6781_RC_NODE_VALID_TIMESTAMP:
mt6781_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE_TIME,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
}
return len;
}
static ssize_t mt6781_generic_rc_write(char *FromUserBuf,
size_t sz, void *priv)
{
struct MT6781_RC_NODE *node = (struct MT6781_RC_NODE *)priv;
if (!node)
return -EINVAL;
if ((node->type == MT6781_RC_NODE_RC_ENABLE) ||
(node->type == MT6781_RC_NODE_COND_ENABLE) ||
(node->type == MT6781_RC_NODE_VALID_BBLPM) ||
(node->type == MT6781_RC_NODE_VALID_TRACE) ||
(node->type == MT6781_RC_NODE_VALID_TIMESTAMP)) {
unsigned int parm;
int cmd;
if ((!kstrtoint(FromUserBuf, 10, &parm)) == 1) {
cmd = (node->type == MT6781_RC_NODE_RC_ENABLE) ?
MT_SPM_DBG_SMC_UID_RC_SWITCH :
(node->type == MT6781_RC_NODE_COND_ENABLE) ?
MT_SPM_DBG_SMC_UID_COND_CHECK :
(node->type == MT6781_RC_NODE_VALID_BBLPM) ?
MT_SPM_DBG_SMC_UID_RC_BBLPM :
(node->type == MT6781_RC_NODE_VALID_TRACE) ?
MT_SPM_DBG_SMC_UID_RC_TRACE :
(node->type == MT6781_RC_NODE_VALID_TIMESTAMP) ?
MT_SPM_DBG_SMC_UID_RC_TRACE_TIME : -1;
if (cmd < 0)
return -EINVAL;
if (!!parm)
parm = MT_LPM_SMC_ACT_SET;
else
parm = MT_LPM_SMC_ACT_CLR;
MT6781_DBG_SMC(cmd, parm, node->rc_id, 0);
}
} else if ((node->type == MT6781_RC_NODE_COND_SET)
|| (node->type == MT6781_RC_NODE_COND_CLR)) {
unsigned int parm1, parm2, act;
if (sscanf(FromUserBuf, "%u %x", &parm1, &parm2) == 2) {
act = (node->type == MT6781_RC_NODE_COND_SET) ?
MT_LPM_SMC_ACT_SET :
(node->type == MT6781_RC_NODE_COND_CLR) ?
MT_LPM_SMC_ACT_CLR : 0;
if (act != 0)
mt6781_lpm_rc_cond_ctrl(node->rc_id, act,
parm1, parm2);
}
} else if ((node->type == MT6781_RC_NODE_RATIO_ENABLE)
|| (node->type == MT6781_RC_NODE_RATIO_INTERVAL)) {
unsigned int parm;
if ((!kstrtoint(FromUserBuf, 10, &parm)) == 1) {
if (node->type == MT6781_RC_NODE_RATIO_ENABLE) {
parm = (!!parm) ? MT_LPM_SMC_ACT_SET :
MT_LPM_SMC_ACT_CLR;
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_CTRL,
parm, MT_RM_CONSTRAINT_ID_DRAM, 0);
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_CTRL,
parm, MT_RM_CONSTRAINT_ID_SYSPLL, 0);
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_CTRL,
parm, MT_RM_CONSTRAINT_ID_BUS26M, 0);
if (parm == MT_LPM_SMC_ACT_SET)
mtk_lpm_timer_start(&rc_ratio_timer);
else
mtk_lpm_timer_stop(&rc_ratio_timer);
} else if (node->type == MT6781_RC_NODE_RATIO_INTERVAL)
mtk_lpm_timer_interval_update(&rc_ratio_timer,
(unsigned long long)parm);
}
}
return sz;
}
static int mt6781_lpm_rc_node_add(struct MT6781_RC_NODE *n,
int mode, struct MT6781_RC_ENTERY *p)
{
return mtk_lpm_sysfs_sub_entry_node_add(n->name, mode,
&n->op, &p->handle, &n->handle);
}
static int mt6781_lpm_rc_entry_add(struct MT6781_RC_ENTERY *n,
int mode, struct MT6781_RC_ENTERY *p)
{
return mtk_lpm_sysfs_sub_entry_add(n->name, mode,
&p->handle, &n->handle);
}
static int mt6781_lpm_rc_valid_node_add(int rc_id,
struct MT6781_RC_ENTERY *parent,
struct MT6781_RC_VALID_HANDLES *valid)
{
int bRet = 0;
if (!valid || !parent)
return -EINVAL;
valid->root.name = "valid";
bRet = mt6781_lpm_rc_entry_add(&valid->root, 0644, parent);
if (!bRet) {
MT6781_GENERIC_RC_NODE_INIT(valid->hBblpm, "bblpm",
rc_id, MT6781_RC_NODE_VALID_BBLPM);
mt6781_lpm_rc_node_add(&valid->hBblpm, 0200, &valid->root);
MT6781_GENERIC_RC_NODE_INIT(valid->hTrace, "trace",
rc_id, MT6781_RC_NODE_VALID_TRACE);
mt6781_lpm_rc_node_add(&valid->hTrace, 0200, &valid->root);
MT6781_GENERIC_RC_NODE_INIT(valid->hTimestamp, "timestamp",
rc_id, MT6781_RC_NODE_VALID_TIMESTAMP);
mt6781_lpm_rc_node_add(&valid->hTimestamp, 0200, &valid->root);
}
return bRet;
}
static int mt6781_lpm_rc_ratio_entry(struct mtk_lp_sysfs_handle *parent,
struct MT6781_RC_RATIO_HANDLES *ratio)
{
int bRet = 0;
if (!ratio || !parent)
return -EINVAL;
ratio->root.name = "ratio";
bRet = mtk_lpm_sysfs_sub_entry_add(ratio->root.name, 0644,
parent, &ratio->root.handle);
if (!bRet) {
MT6781_GENERIC_RC_NODE_INIT(ratio->hEnable, "enable",
0, MT6781_RC_NODE_RATIO_ENABLE);
mt6781_lpm_rc_node_add(&ratio->hEnable, 0200, &ratio->root);
MT6781_GENERIC_RC_NODE_INIT(ratio->hInterval, "interval",
0, MT6781_RC_NODE_RATIO_INTERVAL);
mt6781_lpm_rc_node_add(&ratio->hInterval, 0200, &ratio->root);
}
return 0;
}
static int mt6781_lpm_rc_cond_node_add(int rc_id,
struct MT6781_RC_ENTERY *parent,
struct MT6781_RC_COND_HANDLES *cond)
{
int bRet = 0;
if (!cond || !parent)
return -EINVAL;
cond->root.name = "cond";
bRet = mt6781_lpm_rc_entry_add(&cond->root, 0644, parent);
if (!bRet) {
MT6781_GENERIC_RC_NODE_INIT(cond->hSet, "set",
rc_id, MT6781_RC_NODE_COND_SET);
mt6781_lpm_rc_node_add(&cond->hSet, 0200, &cond->root);
MT6781_GENERIC_RC_NODE_INIT(cond->hClr, "clr",
rc_id, MT6781_RC_NODE_COND_CLR);
mt6781_lpm_rc_node_add(&cond->hClr, 0200, &cond->root);
MT6781_GENERIC_RC_NODE_INIT(cond->hEnable, "enable",
rc_id, MT6781_RC_NODE_COND_ENABLE);
mt6781_lpm_rc_node_add(&cond->hEnable, 0644, &cond->root);
MT6781_GENERIC_RC_NODE_INIT(cond->hState, "state",
rc_id, MT6781_RC_NODE_COND_STATE);
mt6781_lpm_rc_node_add(&cond->hState, 0444, &cond->root);
}
return bRet;
}
static int mt6781_lpm_rc_entry_nodes_basic(int IsSimple,
const char *name, int rc_id,
struct mtk_lp_sysfs_handle *parent,
struct MT6781_RC_HANDLE_BASIC *rc)
{
int bRet = 0;
if (!parent || !rc)
return -EINVAL;
rc->root.name = name;
bRet = mtk_lpm_sysfs_sub_entry_add(rc->root.name, 0644,
parent, &rc->root.handle);
if (bRet)
return -EINVAL;
MT6781_GENERIC_RC_NODE_INIT(rc->hState, "state", rc_id,
(IsSimple) ? MT6781_RC_NODE_RC_STATE_SIMPLE
: MT6781_RC_NODE_RC_STATE);
mt6781_lpm_rc_node_add(&rc->hState, 0444, &rc->root);
MT6781_GENERIC_RC_NODE_INIT(rc->hEnable, "enable", rc_id,
MT6781_RC_NODE_RC_ENABLE);
mt6781_lpm_rc_node_add(&rc->hEnable, 0644, &rc->root);
return bRet;
}
int mt6781_lpm_rc_entry_nodes(const char *name, int rc_id,
struct mtk_lp_sysfs_handle *parent,
struct MT6781_RC_HANDLE *rc)
{
int bRet = 0;
if (!parent || !rc)
return -EINVAL;
memset(rc, 0, sizeof(struct MT6781_RC_HANDLE));
bRet = mt6781_lpm_rc_entry_nodes_basic(0, name, rc_id,
parent, &rc->basic);
bRet = mt6781_lpm_rc_cond_node_add(rc_id, &rc->basic.root,
&rc->hCond);
bRet = mt6781_lpm_rc_valid_node_add(rc_id, &rc->basic.root,
&rc->valid);
return 0;
}
int mt6781_dbg_lpm_fs_init(void)
{
/* enable resource constraint condition block latch */
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_BLOCK_LATCH,
MT_LPM_SMC_ACT_SET, 0, 0);
mtk_lpm_sysfs_root_entry_create();
mtk_lpm_sysfs_sub_entry_add("rc", 0644, NULL,
&mt6781_entry_rc);
MT6781_GENERIC_RC_NODE_INIT(rc_state, "state",
0, MT6781_RC_NODE_STATE);
mtk_lpm_sysfs_sub_entry_node_add(rc_state.name, 0444,
&rc_state.op, &mt6781_entry_rc,
&rc_state.handle);
mt6781_lpm_rc_ratio_entry(&mt6781_entry_rc, &rc_Ratio);
mt6781_lpm_rc_entry_nodes_basic(1, "cpu-buck-ldo",
MT_RM_CONSTRAINT_ID_CPU_BUCK_LDO,
&mt6781_entry_rc, &rc_cpu_buckldo);
mt6781_lpm_rc_entry_nodes("dram", MT_RM_CONSTRAINT_ID_DRAM,
&mt6781_entry_rc, &rc_dram);
mt6781_lpm_rc_entry_nodes("syspll", MT_RM_CONSTRAINT_ID_SYSPLL,
&mt6781_entry_rc, &rc_syspll);
mt6781_lpm_rc_entry_nodes("bus26m", MT_RM_CONSTRAINT_ID_BUS26M,
&mt6781_entry_rc, &rc_bus26m);
rc_ratio_timer.timeout = mt6781_lpm_rc_ratio_timer_func;
mtk_lpm_timer_init(&rc_ratio_timer, MTK_LPM_TIMER_REPEAT);
mtk_lpm_timer_interval_update(&rc_ratio_timer,
MT6781_LPM_RC_RATIO_DEFAULT);
/* enable constraint tracing */
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_SET, MT_RM_CONSTRAINT_ID_BUS26M, 0);
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_SET, MT_RM_CONSTRAINT_ID_SYSPLL, 0);
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_SET, MT_RM_CONSTRAINT_ID_DRAM, 0);
return 0;
}
int mt6781_dbg_lpm_fs_deinit(void)
{
/* disable resource contraint condition block latch */
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_BLOCK_LATCH,
MT_LPM_SMC_ACT_CLR, 0, 0);
/* disable constraint tracing */
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_CLR, MT_RM_CONSTRAINT_ID_BUS26M, 0);
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_CLR, MT_RM_CONSTRAINT_ID_SYSPLL, 0);
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_CLR, MT_RM_CONSTRAINT_ID_DRAM, 0);
return 0;
}
@@ -0,0 +1,459 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/spinlock.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mtk_idle_sysfs.h>
#include <mtk_suspend_sysfs.h>
#include <mtk_spm_sysfs.h>
#include <mt6781_pwr_ctrl.h>
#include <mt6781_dbg_fs_common.h>
#include <mt6781_cond.h>
#include <mt6781_spm_comm.h>
/* Determine for node route */
#define MT_LP_RQ_NODE "/sys/kernel/debug/spm/spm_resource_req"
#define DEFINE_ATTR_RO(_name) \
static struct kobj_attribute _name##_attr = { \
.attr = { \
.name = #_name, \
.mode = 0444, \
}, \
.show = _name##_show, \
}
#define DEFINE_ATTR_RW(_name) \
static struct kobj_attribute _name##_attr = { \
.attr = { \
.name = #_name, \
.mode = 0644, \
}, \
.show = _name##_show, \
.store = _name##_store, \
}
#define __ATTR_OF(_name) (&_name##_attr.attr)
#undef mtk_dbg_spm_log
#define mtk_dbg_spm_log(fmt, args...) \
do { \
int l = scnprintf(p, sz, fmt, ##args); \
p += l; \
sz -= l; \
} while (0)
static char *mt6781_pwr_ctrl_str[PW_MAX_COUNT] = {
[PW_PCM_FLAGS] = "pcm_flags",
[PW_PCM_FLAGS_CUST] = "pcm_flags_cust",
[PW_PCM_FLAGS_CUST_SET] = "pcm_flags_cust_set",
[PW_PCM_FLAGS_CUST_CLR] = "pcm_flags_cust_clr",
[PW_PCM_FLAGS1] = "pcm_flags1",
[PW_PCM_FLAGS1_CUST] = "pcm_flags1_cust",
[PW_PCM_FLAGS1_CUST_SET] = "pcm_flags1_cust_set",
[PW_PCM_FLAGS1_CUST_CLR] = "pcm_flags1_cust_clr",
[PW_TIMER_VAL] = "timer_val",
[PW_TIMER_VAL_CUST] = "timer_val_cust",
[PW_TIMER_VAL_RAMP_EN] = "timer_val_ramp_en",
[PW_TIMER_VAL_RAMP_EN_SEC] = "timer_val_ramp_en_sec",
[PW_WAKE_SRC] = "wake_src",
[PW_WAKE_SRC_CUST] = "wake_src_cust",
[PW_WAKELOCK_TIMER_VAL] = "wakelock_timer_val",
[PW_WDT_DISABLE] = "wdt_disable",
/* SPM_AP_STANDBY_CON */
[PW_REG_WFI_OP] = "reg_wfi_op",
[PW_REG_WFI_TYPE] = "reg_wfi_type",
[PW_REG_MP0_CPUTOP_IDLE_MASK] = "reg_mp0_cputop_idle_mask",
[PW_REG_MP1_CPUTOP_IDLE_MASK] = "reg_mp1_cputop_idle_mask",
[PW_REG_MCUSYS_IDLE_MASK] = "reg_mcusys_idle_mask",
[PW_REG_MD_APSRC_1_SEL] = "reg_md_apsrc_1_sel",
[PW_REG_MD_APSRC_0_SEL] = "reg_md_apsrc_0_sel",
[PW_REG_CONN_APSRC_SEL] = "reg_conn_apsrc_sel",
/* SPM_SRC6_MASK */
[PW_REG_CCIF_EVENT_INFRA_REQ_MASK_B] =
"reg_ccif_event_infra_req_mask_b",
[PW_REG_CCIF_EVENT_APSRC_REQ_MASK_B] =
"reg_ccif_event_apsrc_req_mask_b",
[PW_REG_WAKEUP_EVENT_SENS] =
"reg_wakeup_event_sens",
/* SPM_SRC_REQ */
[PW_REG_SPM_APSRC_REQ] = "reg_spm_apsrc_req",
[PW_REG_SPM_F26M_REQ] = "reg_spm_f26m_req",
[PW_REG_SPM_INFRA_REQ] = "reg_spm_infra_req",
[PW_REG_SPM_VRF18_REQ] = "reg_spm_vrf18_req",
[PW_REG_SPM_DDREN_REQ] = "reg_spm_ddren_req",
[PW_REG_SPM_DVFS_REQ] = "reg_spm_dvfs_req",
[PW_REG_SPM_SW_MAILBOX_REQ] = "reg_spm_sw_mailbox_req",
[PW_REG_SPM_SSPM_MAILBOX_REQ] = "reg_spm_sspm_mailbox_req",
[PW_REG_SPM_ADSP_MAILBOX_REQ] = "reg_spm_adsp_mailbox_req",
[PW_REG_SPM_SCP_MAILBOX_REQ] = "reg_spm_scp_mailbox_req",
/* SPM_SRC_MASK */
[PW_REG_MD_0_SRCCLKENA_MASK_B] = "reg_md_0_srcclkena_mask_b",
[PW_REG_MD_0_INFRA_REQ_MASK_B] = "reg_md_0_infra_req_mask_b",
[PW_REG_MD_0_APSRC_REQ_MASK_B] = "reg_md_0_apsrc_req_mask_b",
[PW_REG_MD_0_VRF18_REQ_MASK_B] = "reg_md_0_vrf18_req_mask_b",
[PW_REG_MD_0_DDREN_REQ_MASK_B] = "reg_md_0_ddren_req_mask_b",
[PW_REG_MD_1_SRCCLKENA_MASK_B] = "reg_md_1_srcclkena_mask_b",
[PW_REG_MD_1_INFRA_REQ_MASK_B] = "reg_md_1_infra_req_mask_b",
[PW_REG_MD_1_APSRC_REQ_MASK_B] = "reg_md_1_apsrc_req_mask_b",
[PW_REG_MD_1_VRF18_REQ_MASK_B] = "reg_md_1_vrf18_req_mask_b",
[PW_REG_MD_1_DDREN_REQ_MASK_B] = "reg_md_1_ddren_req_mask_b",
[PW_REG_CONN_SRCCLKENA_MASK_B] = "reg_conn_srcclkena_mask_b",
[PW_REG_CONN_SRCCLKENB_MASK_B] = "reg_conn_srcclkenb_mask_b",
[PW_REG_CONN_INFRA_REQ_MASK_B] = "reg_conn_infra_req_mask_b",
[PW_REG_CONN_APSRC_REQ_MASK_B] = "reg_conn_apsrc_req_mask_b",
[PW_REG_CONN_VRF18_REQ_MASK_B] = "reg_conn_vrf18_req_mask_b",
[PW_REG_CONN_DDREN_REQ_MASK_B] = "reg_conn_ddren_req_mask_b",
[PW_REG_CONN_VFE28_MASK_B] = "reg_conn_vfe28_mask_b",
[PW_REG_SRCCLKENI_SRCCLKENA_MASK_B] = "reg_srcclkeni_srcclkena_mask_b",
[PW_REG_SRCCLKENI_INFRA_REQ_MASK_B] = "reg_srcclkeni_infra_req_mask_b",
[PW_REG_INFRASYS_APSRC_REQ_MASK_B] = "reg_infrasys_apsrc_req_mask_b",
[PW_REG_INFRASYS_DDREN_REQ_MASK_B] = "reg_infrasys_ddren_req_mask_b",
[PW_REG_SSPM_SRCCLKENA_MASK_B] = "reg_sspm_srcclkena_mask_b",
[PW_REG_SSPM_INFRA_REQ_MASK_B] = "reg_sspm_infra_req_mask_b",
[PW_REG_SSPM_APSRC_REQ_MASK_B] = "reg_sspm_apsrc_req_mask_b",
[PW_REG_SSPM_VRF18_REQ_MASK_B] = "reg_sspm_vrf18_req_mask_b",
[PW_REG_SSPM_DDREN_REQ_MASK_B] = "reg_sspm_ddren_req_mask_b",
/* SPM_SRC2_MASK */
[PW_REG_SCP_SRCCLKENA_MASK_B] = "reg_scp_srcclkena_mask_b",
[PW_REG_SCP_INFRA_REQ_MASK_B] = "reg_scp_infra_req_mask_b",
[PW_REG_SCP_APSRC_REQ_MASK_B] = "reg_scp_apsrc_req_mask_b",
[PW_REG_SCP_VRF18_REQ_MASK_B] = "reg_scp_vrf18_req_mask_b",
[PW_REG_SCP_DDREN_REQ_MASK_B] = "reg_scp_ddren_req_mask_b",
[PW_REG_AUDIO_DSP_SRCCLKENA_MASK_B] = "reg_audio_dsp_srcclkena_mask_b",
[PW_REG_AUDIO_DSP_INFRA_REQ_MASK_B] = "reg_audio_dsp_infra_req_mask_b",
[PW_REG_AUDIO_DSP_APSRC_REQ_MASK_B] = "reg_audio_dsp_apsrc_req_mask_b",
[PW_REG_AUDIO_DSP_VRF18_REQ_MASK_B] = "reg_audio_dsp_vrf18_req_mask_b",
[PW_REG_AUDIO_DSP_DDREN_REQ_MASK_B] = "reg_audio_dsp_ddren_req_mask_b",
[PW_REG_UFS_SRCCLKENA_MASK_B] = "reg_ufs_srcclkena_mask_b",
[PW_REG_UFS_INFRA_REQ_MASK_B] = "reg_ufs_infra_req_mask_b",
[PW_REG_UFS_APSRC_REQ_MASK_B] = "reg_ufs_apsrc_req_mask_b",
[PW_REG_UFS_VRF18_REQ_MASK_B] = "reg_ufs_vrf18_req_mask_b",
[PW_REG_UFS_DDREN_REQ_MASK_B] = "reg_ufs_ddren_req_mask_b",
[PW_REG_DISP0_APSRC_REQ_MASK_B] = "reg_disp0_apsrc_req_mask_b",
[PW_REG_DISP0_DDREN_REQ_MASK_B] = "reg_disp0_ddren_req_mask_b",
[PW_REG_DISP1_APSRC_REQ_MASK_B] = "reg_disp1_apsrc_req_mask_b",
[PW_REG_DISP1_DDREN_REQ_MASK_B] = "reg_disp1_ddren_req_mask_b",
[PW_REG_GCE_INFRA_REQ_MASK_B] = "reg_gce_infra_req_mask_b",
[PW_REG_GCE_APSRC_REQ_MASK_B] = "reg_gce_apsrc_req_mask_b",
[PW_REG_GCE_VRF18_REQ_MASK_B] = "reg_gce_vrf18_req_mask_b",
[PW_REG_GCE_DDREN_REQ_MASK_B] = "reg_gce_ddren_req_mask_b",
[PW_REG_APU_SRCCLKENA_MASK_B] = "reg_apu_srcclkena_mask_b",
[PW_REG_APU_INFRA_REQ_MASK_B] = "reg_apu_infra_req_mask_b",
[PW_REG_APU_APSRC_REQ_MASK_B] = "reg_apu_apsrc_req_mask_b",
[PW_REG_APU_VRF18_REQ_MASK_B] = "reg_apu_vrf18_req_mask_b",
[PW_REG_APU_DDREN_REQ_MASK_B] = "reg_apu_ddren_req_mask_b",
[PW_REG_CG_CHECK_SRCCLKENA_MASK_B] = "reg_cg_check_srcclkena_mask_b",
[PW_REG_CG_CHECK_APSRC_REQ_MASK_B] = "reg_cg_check_apsrc_req_mask_b",
[PW_REG_CG_CHECK_VRF18_REQ_MASK_B] = "reg_cg_check_vrf18_req_mask_b",
[PW_REG_CG_CHECK_DDREN_REQ_MASK_B] = "reg_cg_check_ddren_req_mask_b",
/* SPM_SRC3_MASK */
[PW_REG_DVFSRC_EVENT_TRIGGER_MASK_B] =
"reg_dvfsrc_event_trigger_mask_b",
[PW_REG_SW2SPM_WAKEUP_MASK_B] = "reg_sw2spm_wakeup_mask_b",
[PW_REG_ADSP2SPM_WAKEUP_MASK_B] = "reg_adsp2spm_wakeup_mask_b",
[PW_REG_SSPM2SPM_WAKEUP_MASK_B] = "reg_sspm2spm_wakeup_mask_b",
[PW_REG_SCP2SPM_WAKEUP_MASK_B] = "reg_scp2spm_wakeup_mask_b",
[PW_REG_CSYSPWRUP_ACK_MASK] = "reg_csyspwrup_ack_mask",
[PW_REG_SPM_RESERVED_SRCCLKENA_MASK_B] =
"reg_spm_reserved_srcclkena_mask_b",
[PW_REG_SPM_RESERVED_INFRA_REQ_MASK_B] =
"reg_spm_reserved_infra_req_mask_b",
[PW_REG_SPM_RESERVED_APSRC_REQ_MASK_B] =
"reg_spm_reserved_apsrc_req_mask_b",
[PW_REG_SPM_RESERVED_VRF18_REQ_MASK_B] =
"reg_spm_reserved_vrf18_req_mask_b",
[PW_REG_SPM_RESERVED_DDREN_REQ_MASK_B] =
"reg_spm_reserved_ddren_req_mask_b",
[PW_REG_MCUPM_SRCCLKENA_MASK_B] = "reg_mcupm_srcclkena_mask_b",
[PW_REG_MCUPM_INFRA_REQ_MASK_B] = "reg_mcupm_infra_req_mask_b",
[PW_REG_MCUPM_APSRC_REQ_MASK_B] = "reg_mcupm_apsrc_req_mask_b",
[PW_REG_MCUPM_VRF18_REQ_MASK_B] = "reg_mcupm_vrf18_req_mask_b",
[PW_REG_MCUPM_DDREN_REQ_MASK_B] = "reg_mcupm_ddren_req_mask_b",
[PW_REG_MSDC0_SRCCLKENA_MASK_B] = "reg_msdc0_srcclkena_mask_b",
[PW_REG_MSDC0_INFRA_REQ_MASK_B] = "reg_msdc0_infra_req_mask_b",
[PW_REG_MSDC0_APSRC_REQ_MASK_B] = "reg_msdc0_apsrc_req_mask_b",
[PW_REG_MSDC0_VRF18_REQ_MASK_B] = "reg_msdc0_vrf18_req_mask_b",
[PW_REG_MSDC0_DDREN_REQ_MASK_B] = "reg_msdc0_ddren_req_mask_b",
[PW_REG_MSDC1_SRCCLKENA_MASK_B] = "reg_msdc1_srcclkena_mask_b",
[PW_REG_MSDC1_INFRA_REQ_MASK_B] = "reg_msdc1_infra_req_mask_b",
[PW_REG_MSDC1_APSRC_REQ_MASK_B] = "reg_msdc1_apsrc_req_mask_b",
[PW_REG_MSDC1_VRF18_REQ_MASK_B] = "reg_msdc1_vrf18_req_mask_b",
[PW_REG_MSDC1_DDREN_REQ_MASK_B] = "reg_msdc1_ddren_req_mask_b",
/* SPM_SRC4_MASK */
[PW_REG_CCIF_EVENT_SRCCLKENA_MASK_B] =
"reg_ccif_event_srcclkena_mask_b",
[PW_REG_BAK_PSRI_SRCCLKENA_MASK_B] = "reg_bak_psri_srcclkena_mask_b",
[PW_REG_BAK_PSRI_INFRA_REQ_MASK_B] = "reg_bak_psri_infra_req_mask_b",
[PW_REG_BAK_PSRI_APSRC_REQ_MASK_B] = "reg_bak_psri_apsrc_req_mask_b",
[PW_REG_BAK_PSRI_VRF18_REQ_MASK_B] = "reg_bak_psri_vrf18_req_mask_b",
[PW_REG_BAK_PSRI_DDREN_REQ_MASK_B] = "reg_bak_psri_ddren_req_mask_b",
[PW_REG_DRAMC_MD32_INFRA_REQ_MASK_B] =
"reg_dramc_md32_infra_req_mask_b",
[PW_REG_DRAMC_MD32_VRF18_REQ_MASK_B] =
"reg_dramc_md32_vrf18_req_mask_b",
[PW_REG_CONN_SRCCLKENB2PWRAP_MASK_B] =
"reg_conn_srcclkenb2pwrap_mask_b",
[PW_REG_DRAMC_MD32_APSRC_REQ_MASK_B] =
"reg_dramc_md32_apsrc_req_mask_b",
/* SPM_SRC5_MASK */
[PW_REG_MCUSYS_MERGE_APSRC_REQ_MASK_B] =
"reg_mcusys_merge_apsrc_req_mask_b",
[PW_REG_MCUSYS_MERGE_DDREN_REQ_MASK_B] =
"reg_mcusys_merge_ddren_req_mask_b",
[PW_REG_AFE_SRCCLKENA_MASK_B] = "reg_afe_srcclkena_mask_b",
[PW_REG_AFE_INFRA_REQ_MASK_B] = "reg_afe_infra_req_mask_b",
[PW_REG_AFE_APSRC_REQ_MASK_B] = "reg_afe_apsrc_req_mask_b",
[PW_REG_AFE_VRF18_REQ_MASK_B] = "reg_afe_vrf18_req_mask_b",
[PW_REG_AFE_DDREN_REQ_MASK_B] = "reg_afe_ddren_req_mask_b",
[PW_REG_MSDC2_SRCCLKENA_MASK_B] = "reg_msdc2_srcclkena_mask_b",
[PW_REG_MSDC2_INFRA_REQ_MASK_B] = "reg_msdc2_infra_req_mask_b",
[PW_REG_MSDC2_APSRC_REQ_MASK_B] = "reg_msdc2_apsrc_req_mask_b",
[PW_REG_MSDC2_VRF18_REQ_MASK_B] = "reg_msdc2_vrf18_req_mask_b",
[PW_REG_MSDC2_DDREN_REQ_MASK_B] = "reg_msdc2_ddren_req_mask_b",
/* SPM_WAKEUP_EVENT_MASK */
[PW_REG_WAKEUP_EVENT_MASK] = "reg_wakeup_event_mask",
/* SPM_WAKEUP_EVENT_EXT_MASK */
[PW_REG_EXT_WAKEUP_EVENT_MASK] = "reg_ext_wakeup_event_mask",
/* SPM_SRC7_MASK */
[PW_REG_PCIE_SRCCLKENA_MASK_B] = "reg_pcie_srcclkena_mask_b",
[PW_REG_PCIE_INFRA_REQ_MASK_B] = "reg_pcie_infra_req_mask_b",
[PW_REG_PCIE_APSRC_REQ_MASK_B] = "reg_pcie_apsrc_req_mask_b",
[PW_REG_PCIE_VRF18_REQ_MASK_B] = "reg_pcie_vrf18_req_mask_b",
[PW_REG_PCIE_DDREN_REQ_MASK_B] = "reg_pcie_ddren_req_mask_b",
[PW_REG_DPMAIF_SRCCLKENA_MASK_B] = "reg_dpmaif_srcclkena_mask_b",
[PW_REG_DPMAIF_INFRA_REQ_MASK_B] = "reg_dpmaif_infra_req_mask_b",
[PW_REG_DPMAIF_APSRC_REQ_MASK_B] = "reg_dpmaif_apsrc_req_mask_b",
[PW_REG_DPMAIF_VRF18_REQ_MASK_B] = "reg_dpmaif_vrf18_req_mask_b",
[PW_REG_DPMAIF_DDREN_REQ_MASK_B] = "reg_dpmaif_ddren_req_mask_b",
};
/**************************************
* xxx_ctrl_show Function
**************************************/
/* code gen by spm_pwr_ctrl_atf.pl, need struct pwr_ctrl */
static ssize_t mt6781_show_pwr_ctrl(int id, char *buf, size_t buf_sz)
{
char *p = buf;
size_t mSize = 0;
int i;
for (i = 0; i < PW_MAX_COUNT; i++) {
mSize += scnprintf(p + mSize, buf_sz - mSize,
"%s = 0x%zx\n",
mt6781_pwr_ctrl_str[i],
mtk_lpm_smc_spm_dbg(id, MT_LPM_SMC_ACT_GET, i, 0));
}
WARN_ON(buf_sz - mSize <= 0);
return mSize;
}
/**************************************
* xxx_ctrl_store Function
**************************************/
/* code gen by spm_pwr_ctrl_atf.pl, need struct pwr_ctrl */
static ssize_t mt6781_store_pwr_ctrl(int id, const char *buf, size_t count)
{
u32 val;
char cmd[64];
int i;
if (sscanf(buf, "%63s %x", cmd, &val) != 2)
return -EPERM;
pr_info("[SPM] pwr_ctrl: cmd = %s, val = 0x%x\n", cmd, val);
for (i = 0 ; i < PW_MAX_COUNT; i++) {
if (!strcmp(cmd, mt6781_pwr_ctrl_str[i])) {
mtk_lpm_smc_spm_dbg(id, MT_LPM_SMC_ACT_SET, i, val);
break;
}
}
return count;
}
static ssize_t suspend_ctrl_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return mt6781_show_pwr_ctrl(MT_SPM_DBG_SMC_UID_SUSPEND_PWR_CTRL,
buf, get_mtk_lp_kernfs_bufsz_max());
}
static ssize_t idle_ctrl_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return mt6781_show_pwr_ctrl(MT_SPM_DBG_SMC_UID_IDLE_PWR_CTRL,
buf, get_mtk_lp_kernfs_bufsz_max());
}
static ssize_t suspend_ctrl_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
return mt6781_store_pwr_ctrl(MT_SPM_DBG_SMC_UID_SUSPEND_PWR_CTRL,
buf, count);
}
static ssize_t idle_ctrl_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
return mt6781_store_pwr_ctrl(MT_SPM_DBG_SMC_UID_IDLE_PWR_CTRL,
buf, count);
}
/**************************************
* Init Function
**************************************/
DEFINE_ATTR_RW(suspend_ctrl);
DEFINE_ATTR_RW(idle_ctrl);
static struct attribute *pwrctrl_attrs[] = {
/* for spm_lp_scen.pwrctrl */
__ATTR_OF(suspend_ctrl),
__ATTR_OF(idle_ctrl),
/* must */
NULL,
};
static struct attribute_group pwrctrl_attr_group = {
.name = "spm",
.attrs = pwrctrl_attrs,
};
static char *mt6781_spm_resource_str[MT_SPM_RES_MAX] = {
[MT_SPM_RES_XO_FPM] = "XO_FPM",
[MT_SPM_RES_CK_26M] = "CK_26M",
[MT_SPM_RES_INFRA] = "INFRA",
[MT_SPM_RES_SYSPLL] = "SYSPLL",
[MT_SPM_RES_DRAM_S0] = "DRAM_S0",
[MT_SPM_RES_DRAM_S1] = "DRAM_S1",
};
static ssize_t mt6781_spm_res_rq_read(char *ToUserBuf, size_t sz, void *priv)
{
char *p = ToUserBuf;
int i, s, u;
unsigned int unum, uvalid, uname_i, uname_t;
unsigned int rnum, rusage, per_usage;
char uname[MT_LP_RQ_USER_NAME_LEN+1];
mtk_dbg_spm_log("resource_num=%d, user_num=%d, user_valid=0x%x\n",
rnum = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_NUM,
MT_LPM_SMC_ACT_GET, 0, 0),
unum = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_NUM,
MT_LPM_SMC_ACT_GET, 0, 0),
uvalid = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_VALID,
MT_LPM_SMC_ACT_GET, 0, 0));
rusage = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USAGE,
MT_LPM_SMC_ACT_GET,
MT_LP_RQ_ID_ALL_USAGE, 0);
mtk_dbg_spm_log("\n");
mtk_dbg_spm_log("user [bit][valid]:\n");
for (i = 0; i < unum; i++) {
uname_i = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_NAME,
MT_LPM_SMC_ACT_GET, i, 0);
for (s = 0, u = 0; s < MT_LP_RQ_USER_NAME_LEN;
s++, u += MT_LP_RQ_USER_CHAR_U) {
uname_t = ((uname_i >> u) & MT_LP_RQ_USER_CHAR_MASK);
uname[s] = (uname_t) ? (char)uname_t : ' ';
}
uname[s] = '\0';
mtk_dbg_spm_log("%4s [%3d][%3s]\n", uname, i,
((1<<i) & uvalid) ? "yes" : "no");
}
mtk_dbg_spm_log("\n");
if (rnum != MT_SPM_RES_MAX) {
mtk_dbg_spm_log("Platform resource amount mismatch\n");
rnum = (rnum > MT_SPM_RES_MAX) ? MT_SPM_RES_MAX : rnum;
}
mtk_dbg_spm_log("resource [bit][user_usage][blocking]:\n");
for (i = 0; i < rnum; i++) {
mtk_dbg_spm_log("%8s [%3d][0x%08x][%3s]\n",
mt6781_spm_resource_str[i], i,
(per_usage =
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USAGE,
MT_LPM_SMC_ACT_GET, i, 0)),
((1<<i) & rusage) ? "yes" : "no"
);
}
mtk_dbg_spm_log("\n");
mtk_dbg_spm_log("resource request command help:\n");
mtk_dbg_spm_log("echo enable ${user_bit} > %s\n", MT_LP_RQ_NODE);
mtk_dbg_spm_log("echo bypass ${user_bit} > %s\n", MT_LP_RQ_NODE);
mtk_dbg_spm_log("echo request ${resource_bit} > %s\n", MT_LP_RQ_NODE);
mtk_dbg_spm_log("echo release > %s\n", MT_LP_RQ_NODE);
return p - ToUserBuf;
}
static ssize_t mt6781_spm_res_rq_write(char *FromUserBuf, size_t sz, void *priv)
{
char cmd[128];
int parm;
if (sscanf(FromUserBuf, "%127s %x", cmd, &parm) == 2) {
if (!strcmp(cmd, "bypass"))
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_VALID,
MT_LPM_SMC_ACT_SET,
parm, 0);
else if (!strcmp(cmd, "enable"))
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_VALID,
MT_LPM_SMC_ACT_SET,
parm, 1);
else if (!strcmp(cmd, "request"))
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_REQ,
MT_LPM_SMC_ACT_SET,
0, parm);
return sz;
} else if (sscanf(FromUserBuf, "%127s", cmd) == 1) {
if (!strcmp(cmd, "release"))
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_REQ,
MT_LPM_SMC_ACT_CLR,
0, 0);
return sz;
} else if ((!kstrtoint(FromUserBuf, 10, &parm)) == 1) {
return sz;
}
return -EINVAL;
}
static const struct mtk_lp_sysfs_op mt6781_spm_res_rq_fops = {
.fs_read = mt6781_spm_res_rq_read,
.fs_write = mt6781_spm_res_rq_write,
};
int mt6781_dbg_spm_fs_init(void)
{
int r;
mtk_spm_sysfs_root_entry_create();
mtk_spm_sysfs_entry_node_add("spm_resource_req", 0444
, &mt6781_spm_res_rq_fops, NULL);
/* create /sys/power/spm/xxx */
r = mtk_spm_sysfs_power_create_group(&pwrctrl_attr_group);
if (r)
pr_info("[SPM] FAILED TO CREATE /sys/power/spm (%d)\n", r);
return r;
}
int mt6781_dbg_spm_fs_deinit(void)
{
return 0;
}
@@ -0,0 +1,936 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/rtc.h>
#include <linux/wakeup_reason.h>
#include <linux/syscore_ops.h>
#include <mt6781_cond.h>
#include <mtk_lpm_module.h>
#include <aee.h>
#include <mtk_lpm.h>
#include <mt6781_spm_comm.h>
#include <mt6781_spm_reg.h>
#include <mt6781_pwr_ctrl.h>
#include <mt6781_pcm_def.h>
#include <mtk_dbg_common_v1.h>
#include <mt-plat/mtk_ccci_common.h>
#include <mtk_lpm_call.h>
#include <mtk_lpm_call_type.h>
#include <mtk_lpm_timer.h>
#include <mtk_lpm_sysfs.h>
#include <gs/v1/mtk_power_gs.h>
#define MT6781_LOG_MONITOR_STATE_NAME "mcusysoff"
#define MT6781_LOG_DEFAULT_MS 5000
#define PCM_32K_TICKS_PER_SEC (32768)
#define PCM_TICK_TO_SEC(TICK) (TICK / PCM_32K_TICKS_PER_SEC)
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
struct MT6886_LOGGER_NODE mt6781_log_gs_idle;
struct MTK_LPM_GS_IDLE_INFO {
unsigned short limit;
unsigned short limit_set;
unsigned int dump_type;
};
struct MTK_LPM_GS_IDLE_INFO mt6781_log_gs_info;
struct cpumask mtk_lpm_gs_idle_cpumask;
#define IS_MTK_LPM_GS_UPDATE(x) \
((cpumask_weight(&mtk_lpm_gs_idle_cpumask)\
== num_online_cpus()) &&\
(x.limit != x.limit_set))
#endif
static struct mt6781_spm_wake_status mt6781_wake;
void __iomem *mt6781_spm_base;
struct mt6781_log_helper {
short cur;
short prev;
struct mt6781_spm_wake_status *wakesrc;
};
struct mt6781_log_helper mt6781_logger_help = {
.wakesrc = &mt6781_wake,
.cur = 0,
.prev = 0,
};
static char *mt6781_spm_cond_cg_str[PLAT_SPM_COND_MAX] = {
[PLAT_SPM_COND_MTCMOS_0] = "MTCMOS_0",
[PLAT_SPM_COND_CG_INFRA_0] = "INFRA_0",
[PLAT_SPM_COND_CG_INFRA_1] = "INFRA_1",
[PLAT_SPM_COND_CG_INFRA_2] = "INFRA_2",
[PLAT_SPM_COND_CG_INFRA_3] = "INFRA_3",
[PLAT_SPM_COND_CG_INFRA_4] = "INFRA_4",
[PLAT_SPM_COND_CG_INFRA_5] = "INFRA_5",
[PLAT_SPM_COND_CG_MMSYS_0] = "MMSYS_0",
[PLAT_SPM_COND_CG_MMSYS_1] = "MMSYS_1",
[PLAT_SPM_COND_CG_MMSYS_2] = "MMSYS_2",
[PLAT_SPM_COND_CG_MMSYS_3] = "MMSYS_3",
};
const char *mt6781_wakesrc_str[32] = {
[0] = " R12_PCM_TIMER",
[1] = " R12_TWAM_IRQ_B",
[2] = " R12_KP_IRQ_B",
[3] = " R12_APWDT_EVENT_B",
[4] = " R12_APXGPT1_EVENT_B",
[5] = " R12_CONN2AP_SPM_WAKEUP_B",
[6] = " R12_EINT_EVENT_B",
[7] = " R12_CONN_WDT_IRQ_B",
[8] = " R12_CCIF0_EVENT_B",
[9] = " R12_LOWBATTERY_IRQ_B",
[10] = " R12_SSPM2SPM_WAKEUP_B",
[11] = " R12_SCP2SPM_WAKEUP_B",
[12] = " R12_RESERVED_BIT_12",
[13] = " R12_PCM_WDT_WAKEUP_B",
[14] = " R12_USB_CDSC_B",
[15] = " R12_USB_POWERDWN_B",
[16] = " R12_SYS_TIMER_EVENT_B",
[17] = " R12_EINT_EVENT_SECURE_B",
[18] = " R12_CCIF1_EVENT_B",
[19] = " R12_UART0_IRQ_B",
[20] = " R12_AFE_IRQ_MCU_B",
[21] = " R12_THERM_CTRL_EVENT_B",
[22] = " R12_SYS_CIRQ_IRQ_B",
[23] = " R12_MD2AP_PEER_EVENT_B",
[24] = " R12_CSYSPWREQ_B",
[25] = " R12_MD1_WDT_B",
[26] = " R12_CLDMA_EVENT_B_B",
[27] = " R12_SEJ_EVENT_B",
[28] = " R12_REG_CPU_WAKEUP",
[29] = " R12_CPU_IRQOUT",
[30] = " R12_CPU_WFI",
[31] = " R12_MCUSYS_IDLE",
};
struct spm_wakesrc_irq_list mt6781_spm_wakesrc_irqs[] = {
/* mtk-kpd */
{ WAKE_SRC_STA1_KP_IRQ_B, "mediatek,kp", 0, 0},
/* mt_wdt */
{ WAKE_SRC_STA1_APWDT_EVENT_B, "mediatek,toprgu", 0, 0},
/* bt_cvsd_int */
{ WAKE_SRC_STA1_CONN2AP_SPM_WAKEUP_B, "mediatek,mtk-btcvsd-snd", 0, 0},
/* wf_hif_int */
{ WAKE_SRC_STA1_CONN2AP_SPM_WAKEUP_B, "mediatek,wifi", 0, 0},
/* conn2ap_btif_wakeup_out */
{ WAKE_SRC_STA1_CONN2AP_SPM_WAKEUP_B, "mediatek,mt6781-consys", 0, 0},
/* conn2ap_sw_irq */
{ WAKE_SRC_STA1_CONN2AP_SPM_WAKEUP_B, "mediatek,mt6781-consys", 2, 0},
/* CCIF_AP_DATA */
{ WAKE_SRC_STA1_CCIF0_EVENT_B, "mediatek,ap_ccif0", 0, 0},
/* SCP IPC0 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 0, 0},
/* SCP IPC1 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 1, 0},
/* MBOX_ISR0 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 2, 0},
/* MBOX_ISR1 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 3, 0},
/* MBOX_ISR2 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 4, 0},
/* MBOX_ISR3 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 5, 0},
/* MBOX_ISR4 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 6, 0},
/* ADSP_A_AUD */
{ WAKE_SRC_STA1_SC_ADSP2SPM_WAKEUP_B, "mediatek,adsp_core_0", 2, 0},
/* ADSP_B_AUD */
{ WAKE_SRC_STA1_SC_ADSP2SPM_WAKEUP_B, "mediatek,adsp_core_1", 2, 0},
/* CCIF0_AP */
{ WAKE_SRC_STA1_MD1_WDT_B, "mediatek,mddriver", 2, 0},
/* DPMAIF_AP */
{ WAKE_SRC_STA1_AP2AP_PEER_WAKEUPEVENT_B, "mediatek,dpmaif", 0, 0},
};
#define plat_mmio_read(offset) __raw_readl(mt6781_spm_base + offset)
u64 ap_pd_count;
u64 ap_slp_duration;
u64 spm_26M_off_count;
u64 spm_26M_off_duration;
u32 before_ap_slp_duration;
#define IRQ_NUMBER \
(sizeof(mt6781_spm_wakesrc_irqs)/sizeof(struct spm_wakesrc_irq_list))
static void mt6781_get_spm_wakesrc_irq(void)
{
int i;
struct device_node *node = NULL;
for (i = 0; i < IRQ_NUMBER; i++) {
if (mt6781_spm_wakesrc_irqs[i].name == NULL)
continue;
node = of_find_compatible_node(NULL, NULL,
mt6781_spm_wakesrc_irqs[i].name);
if (!node) {
pr_info("[name:spm&][SPM] find '%s' node failed\n",
mt6781_spm_wakesrc_irqs[i].name);
continue;
}
mt6781_spm_wakesrc_irqs[i].irq_no =
irq_of_parse_and_map(node,
mt6781_spm_wakesrc_irqs[i].order);
if (!mt6781_spm_wakesrc_irqs[i].irq_no) {
pr_info("[name:spm&][SPM] get '%s' failed\n",
mt6781_spm_wakesrc_irqs[i].name);
}
}
}
struct mt6781_logger_timer {
struct mt_lpm_timer tm;
unsigned int fired;
};
struct mt6781_logger_fired_info {
unsigned int fired;
int state_index;
};
static struct mt6781_logger_timer mt6781_log_timer;
static struct mt6781_logger_fired_info mt6781_logger_fired;
int mt6781_get_wakeup_status(struct mt6781_log_helper *help)
{
if (!help->wakesrc || !mt6781_spm_base)
return -EINVAL;
help->wakesrc->r12 = plat_mmio_read(SPM_BK_WAKE_EVENT);
help->wakesrc->r12_ext = plat_mmio_read(SPM_WAKEUP_STA);
help->wakesrc->raw_sta = plat_mmio_read(SPM_WAKEUP_STA);
help->wakesrc->raw_ext_sta = plat_mmio_read(SPM_WAKEUP_EXT_STA);
help->wakesrc->md32pcm_wakeup_sta = plat_mmio_read(MD32PCM_WAKEUP_STA);
help->wakesrc->md32pcm_event_sta = plat_mmio_read(MD32PCM_EVENT_STA);
help->wakesrc->src_req = plat_mmio_read(SPM_SRC_REQ);
/* backup of SPM_WAKEUP_MISC */
help->wakesrc->wake_misc = plat_mmio_read(SPM_BK_WAKE_MISC);
/* get sleep time */
/* backup of PCM_TIMER_OUT */
help->wakesrc->timer_out = plat_mmio_read(SPM_BK_PCM_TIMER);
/* get other SYS and co-clock status */
help->wakesrc->r13 = plat_mmio_read(PCM_REG13_DATA);
help->wakesrc->idle_sta = plat_mmio_read(SUBSYS_IDLE_STA);
help->wakesrc->req_sta0 = plat_mmio_read(SRC_REQ_STA_0);
help->wakesrc->req_sta1 = plat_mmio_read(SRC_REQ_STA_1);
help->wakesrc->req_sta2 = plat_mmio_read(SRC_REQ_STA_2);
help->wakesrc->req_sta3 = plat_mmio_read(SRC_REQ_STA_3);
help->wakesrc->req_sta4 = plat_mmio_read(SRC_REQ_STA_4);
/* get HW CG check status */
help->wakesrc->cg_check_sta = plat_mmio_read(SPM_CG_CHECK_STA);
/* get debug flag for PCM execution check */
help->wakesrc->debug_flag = plat_mmio_read(PCM_WDT_LATCH_SPARE_0);
help->wakesrc->debug_flag1 = plat_mmio_read(PCM_WDT_LATCH_SPARE_1);
/* get backup SW flag status */
help->wakesrc->b_sw_flag0 = plat_mmio_read(SPM_SW_RSV_7);
help->wakesrc->b_sw_flag1 = plat_mmio_read(SPM_SW_RSV_8);
help->wakesrc->rt_req_sta0 = plat_mmio_read(SPM_SW_RSV_2);
help->wakesrc->rt_req_sta1 = plat_mmio_read(SPM_SW_RSV_3);
help->wakesrc->rt_req_sta2 = plat_mmio_read(SPM_SW_RSV_4);
help->wakesrc->rt_req_sta3 = plat_mmio_read(SPM_SW_RSV_5);
help->wakesrc->rt_req_sta4 = plat_mmio_read(SPM_SW_RSV_6);
/* get ISR status */
help->wakesrc->isr = plat_mmio_read(SPM_IRQ_STA);
/* get SW flag status */
help->wakesrc->sw_flag0 = plat_mmio_read(SPM_SW_FLAG_0);
help->wakesrc->sw_flag1 = plat_mmio_read(SPM_SW_FLAG_1);
/* get CLK SETTLE */
help->wakesrc->clk_settle = plat_mmio_read(SPM_CLK_SETTLE);
/* check abort */
help->wakesrc->is_abort =
help->wakesrc->debug_flag & DEBUG_ABORT_MASK;
help->wakesrc->is_abort |=
help->wakesrc->debug_flag1 & DEBUG_ABORT_MASK_1;
help->cur += 1;
return 0;
}
static void mt6781_save_sleep_info(void)
{
#define AVOID_OVERFLOW (0xFFFFFFFF00000000)
u32 off_26M_duration;
u32 slp_duration;
slp_duration = plat_mmio_read(SPM_BK_PCM_TIMER);
if (slp_duration == before_ap_slp_duration)
return;
/* Save ap off counter and duration */
if (ap_pd_count >= AVOID_OVERFLOW)
ap_pd_count = 0;
else
ap_pd_count++;
if (ap_slp_duration >= AVOID_OVERFLOW)
ap_slp_duration = 0;
else {
ap_slp_duration = ap_slp_duration + slp_duration;
before_ap_slp_duration = slp_duration;
}
/* Save 26M's off counter and duration */
if (spm_26M_off_duration >= AVOID_OVERFLOW)
spm_26M_off_duration = 0;
else {
off_26M_duration = plat_mmio_read(SPM_BK_VTCXO_DUR);
if (off_26M_duration == 0)
return;
spm_26M_off_duration = spm_26M_off_duration +
off_26M_duration;
}
if (spm_26M_off_count >= AVOID_OVERFLOW)
spm_26M_off_count = 0;
else
spm_26M_off_count = (plat_mmio_read(SPM_VTCXO_EVENT_COUNT_STA)
& 0xffff)
+ spm_26M_off_count;
}
static void mt6781_suspend_show_detailed_wakeup_reason
(struct mt6781_spm_wake_status *wakesta)
{
int i;
unsigned int irq_no;
#if !defined(CONFIG_FPGA_EARLY_PORTING)
#ifdef CONFIG_MTK_CCCI_DEVICES
exec_ccci_kern_func_by_md_id(0, ID_DUMP_MD_SLEEP_MODE,
NULL, 0);
#endif
#ifdef CONFIG_MTK_ECCCI_DRIVER
if (wakesta->r12 & R12_CLDMA_EVENT_B)
exec_ccci_kern_func_by_md_id(0, ID_GET_MD_WAKEUP_SRC,
NULL, 0);
if (wakesta->r12 & R12_MD2AP_PEER_EVENT_B)
exec_ccci_kern_func_by_md_id(0, ID_GET_MD_WAKEUP_SRC,
NULL, 0);
if (wakesta->r12 & R12_CCIF0_EVENT_B)
exec_ccci_kern_func_by_md_id(0, ID_GET_MD_WAKEUP_SRC,
NULL, 0);
if (wakesta->r12 & R12_CCIF1_EVENT_B)
exec_ccci_kern_func_by_md_id(2, ID_GET_MD_WAKEUP_SRC,
NULL, 0);
#endif
#endif
/* Check if pending irq happen and log them */
for (i = 0; i < IRQ_NUMBER; i++) {
if (mt6781_spm_wakesrc_irqs[i].name == NULL ||
!mt6781_spm_wakesrc_irqs[i].irq_no)
continue;
if (mt6781_spm_wakesrc_irqs[i].wakesrc & wakesta->r12) {
irq_no = mt6781_spm_wakesrc_irqs[i].irq_no;
if (mt_irq_get_pending(irq_no))
log_irq_wakeup_reason(irq_no);
}
}
}
#define MT6781_DBG_SMC(_id, _act, _rc, _param) ({\
(u32) mtk_lpm_smc_spm_dbg(_id, _act, _rc, _param); })
static void dump_lp_cond(void)
{
int i;
u32 blkcg;
for (i = 1 ; i < PLAT_SPM_COND_MAX ; i++) {
blkcg = MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_BLOCK_DETAIL, MT_LPM_SMC_ACT_GET, 0, i);
if (blkcg != 0)
printk_deferred("suspend warning: CG: %6s = 0x%08lx\n"
, mt6781_spm_cond_cg_str[i], blkcg);
}
}
static void mt6781_suspend_spm_rsc_req_check
(struct mt6781_spm_wake_status *wakesta)
{
#define LOG_BUF_SIZE 256
#define IS_BLOCKED_OVER_TIMES 10
#undef AVOID_OVERFLOW
#define AVOID_OVERFLOW (0xF0000000)
static u32 is_blocked_cnt;
char log_buf[LOG_BUF_SIZE] = { 0 };
int log_size = 0;
u32 is_no_blocked = 0;
u32 req_sta_0, req_sta_1, req_sta_4;
u32 src_req;
if (is_blocked_cnt >= AVOID_OVERFLOW)
is_blocked_cnt = 0;
/* Check if ever enter deepest System LPM */
is_no_blocked = wakesta->debug_flag & 0x2;
/* Check if System LPM ever is blocked over 10 times */
if (!is_no_blocked)
is_blocked_cnt++;
else
is_blocked_cnt = 0;
if (is_blocked_cnt < IS_BLOCKED_OVER_TIMES)
return;
/* Show who is blocking system LPM */
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size,
"suspend warning:(OneShot) System LPM is blocked by ");
req_sta_0 = plat_mmio_read(SRC_REQ_STA_0);
if (req_sta_0 & 0xFFF)
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "md ");
if (req_sta_0 & (0x3F << 12))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "conn ");
if (req_sta_0 & (0x7 << 18))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "nfc ");
if (req_sta_0 & (0xF << 26))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "disp ");
req_sta_1 = plat_mmio_read(SRC_REQ_STA_1);
if (req_sta_1 & 0x1F)
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "scp ");
if (req_sta_1 & (0x1F << 5))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "adsp ");
if (req_sta_1 & (0x1F << 10))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "ufs ");
if (req_sta_1 & (0xF << 15))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "gce ");
if (req_sta_1 & (0x3FF << 21))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "msdc ");
req_sta_4 = plat_mmio_read(SRC_REQ_STA_4);
if (req_sta_4 & 0x1F)
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "apu ");
src_req = plat_mmio_read(SPM_SRC_REQ);
if (src_req & 0x9B) {
dump_lp_cond();
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "spm ");
}
WARN_ON(strlen(log_buf) >= LOG_BUF_SIZE);
printk_deferred("[name:spm&][SPM] %s", log_buf);
}
static int mt6781_show_message(struct mt6781_spm_wake_status *wakesrc, int type,
const char *prefix, void *data)
{
#undef LOG_BUF_SIZE
#define LOG_BUF_SIZE 256
#define LOG_BUF_OUT_SZ 768
#define IS_WAKE_MISC(x) (wakesrc->wake_misc & x)
#define IS_LOGBUF(ptr, newstr) \
((strlen(ptr) + strlen(newstr)) < LOG_BUF_SIZE)
int i;
unsigned int spm_26M_off_pct = 0;
char buf[LOG_BUF_SIZE] = { 0 };
char log_buf[LOG_BUF_OUT_SZ] = { 0 };
char *local_ptr = NULL;
int log_size = 0;
unsigned int wr = WR_UNKNOWN;
const char *scenario = prefix ?: "UNKNOWN";
/* Disable rcu lock checking */
if (type == MT_LPM_ISSUER_SUSPEND)
rcu_irq_enter_irqson();
if (wakesrc->is_abort != 0) {
/* add size check for vcoredvfs */
aee_sram_printk("SPM ABORT (%s), r13 = 0x%x, ",
scenario, wakesrc->r13);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"[SPM] ABORT (%s), r13 = 0x%x, ",
scenario, wakesrc->r13);
aee_sram_printk(" debug_flag = 0x%x 0x%x",
wakesrc->debug_flag, wakesrc->debug_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" debug_flag = 0x%x 0x%x",
wakesrc->debug_flag, wakesrc->debug_flag1);
aee_sram_printk(" sw_flag = 0x%x 0x%x",
wakesrc->sw_flag0, wakesrc->sw_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" sw_flag = 0x%x 0x%x",
wakesrc->sw_flag0, wakesrc->sw_flag1);
aee_sram_printk(" b_sw_flag = 0x%x 0x%x",
wakesrc->b_sw_flag0, wakesrc->b_sw_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" b_sw_flag = 0x%x 0x%x",
wakesrc->b_sw_flag0, wakesrc->b_sw_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"gpio200 = %u\n", wakesrc->gpio200_datain);
wr = WR_ABORT;
} else {
if (wakesrc->r12 & R12_PCM_TIMER) {
if (wakesrc->wake_misc & WAKE_MISC_PCM_TIMER_EVENT) {
local_ptr = " PCM_TIMER";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_PCM_TIMER;
}
}
if (wakesrc->r12 & R12_TWAM_IRQ_B) {
if (IS_WAKE_MISC(WAKE_MISC_DVFSRC_IRQ)) {
local_ptr = " DVFSRC";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_DVFSRC;
}
if (IS_WAKE_MISC(WAKE_MISC_TWAM_IRQ_B)) {
local_ptr = " TWAM";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_TWAM;
}
if (IS_WAKE_MISC(WAKE_MISC_PMSR_IRQ_B_SET0)) {
local_ptr = " PMSR";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_PMSR;
}
if (IS_WAKE_MISC(WAKE_MISC_PMSR_IRQ_B_SET1)) {
local_ptr = " PMSR";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_PMSR;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_0)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_1)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_2)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_3)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_ALL)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
}
for (i = 1; i < 32; i++) {
if (wakesrc->r12 & (1U << i)) {
if (IS_LOGBUF(buf, mt6781_wakesrc_str[i]))
strncat(buf, mt6781_wakesrc_str[i],
strlen(mt6781_wakesrc_str[i]));
wr = WR_WAKE_SRC;
}
}
WARN_ON(strlen(buf) >= LOG_BUF_SIZE);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"%s wake up by %s, timer_out = %u, r13 = 0x%x, debug_flag = 0x%x 0x%x, ",
scenario, buf, wakesrc->timer_out, wakesrc->r13,
wakesrc->debug_flag, wakesrc->debug_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"r12 = 0x%x, r12_ext = 0x%x, raw_sta = 0x%x 0x%x 0x%x, idle_sta = 0x%x, ",
wakesrc->r12, wakesrc->r12_ext,
wakesrc->raw_sta,
wakesrc->md32pcm_wakeup_sta,
wakesrc->md32pcm_event_sta,
wakesrc->idle_sta);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"req_sta = 0x%x 0x%x 0x%x 0x%x 0x%x, cg_check_sta =0x%x, isr = 0x%x, rt_req_sta0 = 0x%x rt_req_sta1 = 0x%x rt_req_sta2 = 0x%x rt_req_sta3 = 0x%x dram_sw_con_3 = 0x%x, ",
wakesrc->req_sta0, wakesrc->req_sta1,
wakesrc->req_sta2, wakesrc->req_sta3,
wakesrc->req_sta4, wakesrc->cg_check_sta,
wakesrc->isr, wakesrc->rt_req_sta0,
wakesrc->rt_req_sta1, wakesrc->rt_req_sta2,
wakesrc->rt_req_sta3, wakesrc->rt_req_sta4);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"raw_ext_sta = 0x%x, wake_misc = 0x%x, pcm_flag = 0x%x 0x%x 0x%x 0x%x, req = 0x%x, ",
wakesrc->raw_ext_sta,
wakesrc->wake_misc,
wakesrc->sw_flag0,
wakesrc->sw_flag1, wakesrc->b_sw_flag0,
wakesrc->b_sw_flag0,
wakesrc->src_req);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" clk_settle = 0x%x, ", wakesrc->clk_settle);
if (type == MT_LPM_ISSUER_SUSPEND) {
/* calculate 26M off percentage in suspend period */
if (wakesrc->timer_out != 0) {
spm_26M_off_pct =
(100 * plat_mmio_read(SPM_BK_VTCXO_DUR))
/ wakesrc->timer_out;
}
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"wlk_cntcv_l = 0x%x, wlk_cntcv_h = 0x%x, 26M_off_pct = %d, ",
plat_mmio_read(SYS_TIMER_VALUE_L),
plat_mmio_read(SYS_TIMER_VALUE_H),
spm_26M_off_pct);
}
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"gpio200 = %u\n", wakesrc->gpio200_datain);
}
WARN_ON(log_size >= LOG_BUF_OUT_SZ);
if (type == MT_LPM_ISSUER_SUSPEND) {
printk_deferred("[name:spm&][SPM] %s", log_buf);
mt6781_suspend_show_detailed_wakeup_reason(wakesrc);
mt6781_suspend_spm_rsc_req_check(wakesrc);
printk_deferred("[name:spm&][SPM] Suspended for %d.%03d seconds",
PCM_TICK_TO_SEC(wakesrc->timer_out),
PCM_TICK_TO_SEC((wakesrc->timer_out %
PCM_32K_TICKS_PER_SEC)
* 1000));
/* Eable rcu lock checking */
rcu_irq_exit_irqson();
} else
pr_info("[name:spm&][SPM] %s", log_buf);
return wr;
}
int mt6781_issuer_func(int type, const char *prefix, void *data)
{
mt6781_get_wakeup_status(&mt6781_logger_help);
return mt6781_show_message(mt6781_logger_help.wakesrc,
type, prefix, data);
}
struct mtk_lpm_issuer mt6781_issuer = {
.log = mt6781_issuer_func,
};
static int mt6781_idle_save_sleep_info_nb_func(struct notifier_block *nb,
unsigned long action, void *data)
{
struct mtk_lpm_nb_data *nb_data = (struct mtk_lpm_nb_data *)data;
if (nb_data && (action == MTK_LPM_NB_BEFORE_REFLECT))
mt6781_save_sleep_info();
return NOTIFY_OK;
}
struct notifier_block mt6781_idle_save_sleep_info_nb = {
.notifier_call = mt6781_idle_save_sleep_info_nb_func,
};
static void mt6781_suspend_save_sleep_info_func(void)
{
mt6781_save_sleep_info();
}
static struct syscore_ops mt6781_suspend_save_sleep_info_syscore_ops = {
.resume = mt6781_suspend_save_sleep_info_func,
};
static int mt6781_log_timer_func(unsigned long long dur, void *priv)
{
struct mt6781_logger_timer *timer =
(struct mt6781_logger_timer *)priv;
struct mt6781_logger_fired_info *info = &mt6781_logger_fired;
if (timer->fired != info->fired) {
/* if the wake src had beed update before
* then won't do wake src update
*/
if (mt6781_logger_help.prev == mt6781_logger_help.cur)
mt6781_get_wakeup_status(&mt6781_logger_help);
mt6781_show_message(mt6781_logger_help.wakesrc,
MT_LPM_ISSUER_CPUIDLE,
"MCUSYSOFF", NULL);
mt6781_logger_help.prev = mt6781_logger_help.cur;
} else
pr_info("[name:spm&][SPM] MCUSYSOFF Didn't enter low power scenario\n");
timer->fired = info->fired;
return 0;
}
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
struct mtk_lpm_gs_idleinfo {
unsigned int type[2];
};
#endif
static int mt6781_logger_nb_func(struct notifier_block *nb,
unsigned long action, void *data)
{
struct mtk_lpm_nb_data *nb_data = (struct mtk_lpm_nb_data *)data;
struct mt6781_logger_fired_info *info = &mt6781_logger_fired;
if (nb_data && (action == MTK_LPM_NB_BEFORE_REFLECT)
&& (nb_data->index == info->state_index)) {
info->fired++;
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
cpumask_clear_cpu(nb_data->cpu, &mtk_lpm_gs_idle_cpumask);
#endif
}
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
if (nb_data && (action == MTK_LPM_NB_AFTER_PROMPT)
&& (nb_data->index == info->state_index)) {
cpumask_set_cpu(nb_data->cpu, &mtk_lpm_gs_idle_cpumask);
if (IS_MTK_LPM_GS_UPDATE(mt6781_log_gs_info)) {
struct mtk_lpm_callee_simple *callee = NULL;
struct mtk_lpm_data val;
val.d.u32 = mt6781_log_gs_info.dump_type;
if (!mtk_lpm_callee_get(MTK_LPM_CALLEE_PWR_GS, &callee))
callee->set(MTK_LPM_PWR_GS_TYPE_VCORELP_26M, &val);
mt6781_log_gs_info.limit =
mt6781_log_gs_info.limit_set;
}
}
#endif
return NOTIFY_OK;
}
struct notifier_block mt6781_logger_nb = {
.notifier_call = mt6781_logger_nb_func,
};
static ssize_t mt6781_logger_debugfs_read(char *ToUserBuf,
size_t sz, void *priv)
{
char *p = ToUserBuf;
int len;
if (priv == ((void *)&mt6781_log_timer)) {
len = scnprintf(p, sz, "%lu\n",
mtk_lpm_timer_interval(&mt6781_log_timer.tm));
p += len;
}
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
else if (priv == ((void *)&mt6781_log_gs_info)) {
len = scnprintf(p, sz, "golden_type: %u\n",
mt6781_log_gs_info.dump_type);
}
#endif
return (p - ToUserBuf);
}
static ssize_t mt6781_logger_debugfs_write(char *FromUserBuf,
size_t sz, void *priv)
{
if (priv == ((void *)&mt6781_log_timer)) {
unsigned int val = 0;
if (!kstrtouint(FromUserBuf, 10, &val)) {
if (val == 0)
mtk_lpm_timer_stop(&mt6781_log_timer.tm);
else
mtk_lpm_timer_interval_update(
&mt6781_log_timer.tm, val);
}
}
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
else if (priv == ((void *)&mt6781_log_gs_info)) {
char cmd[64];
unsigned int param;
memset(cmd, 0, sizeof(cmd));
if (sscanf(FromUserBuf, "%20s %u", cmd, &param) == 2) {
if (!strcmp(cmd, "golden_dump")) {
if (param)
mt6781_log_gs_info.limit_set += 1;
} else if (!strcmp(cmd, "golden_type"))
mt6781_log_gs_info.dump_type = param;
}
}
#endif
return sz;
}
struct MT6886_LOGGER_NODE {
struct mtk_lp_sysfs_handle handle;
struct mtk_lp_sysfs_op op;
};
#define MT6781_LOGGER_NODE_INIT(_n, _priv) ({\
_n.op.fs_read = mt6781_logger_debugfs_read;\
_n.op.fs_write = mt6781_logger_debugfs_write;\
_n.op.priv = _priv; })\
struct mtk_lp_sysfs_handle mt6781_log_tm_node;
struct MT6886_LOGGER_NODE mt6781_log_tm_interval;
int mt6781_logger_timer_debugfs_init(void)
{
mtk_lpm_sysfs_sub_entry_add("logger", 0644,
NULL, &mt6781_log_tm_node);
MT6781_LOGGER_NODE_INIT(mt6781_log_tm_interval,
&mt6781_log_timer);
mtk_lpm_sysfs_sub_entry_node_add("interval", 0644,
&mt6781_log_tm_interval.op,
&mt6781_log_tm_node,
&mt6781_log_tm_interval.handle);
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
MT6781_LOGGER_NODE_INIT(mt6781_log_gs_idle,
&mt6781_log_gs_info);
mtk_lpm_sysfs_sub_entry_node_add("gs", 0644,
&mt6781_log_gs_idle.op,
&mt6781_log_tm_node,
&mt6781_log_gs_idle.handle);
#endif
return 0;
}
int __init mt6781_logger_init(void)
{
struct device_node *node = NULL;
struct cpuidle_driver *drv = NULL;
struct cpuidle_device *dev = NULL;
node = of_find_compatible_node(NULL, NULL, "mediatek,sleep");
if (node) {
mt6781_spm_base = of_iomap(node, 0);
of_node_put(node);
}
if (mt6781_spm_base)
mtk_lp_issuer_register(&mt6781_issuer);
else
pr_info("[name:mtk_lpm][P] - Don't register the issue by error! (%s:%d)\n",
__func__, __LINE__);
mt6781_get_spm_wakesrc_irq();
mtk_lpm_sysfs_root_entry_create();
mt6781_logger_timer_debugfs_init();
dev = cpuidle_get_device();
drv = cpuidle_get_cpu_driver(dev);
mt6781_logger_fired.state_index = -1;
mt6781_logger_help.wakesrc->gpio200_datain =
MT6781_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_GPIO200_DATAIN,
MT_LPM_SMC_ACT_GET, 0, 0);
if (drv) {
int idx;
for (idx = 0; idx < drv->state_count; ++idx) {
if (!strcmp(MT6781_LOG_MONITOR_STATE_NAME,
drv->states[idx].name)) {
mt6781_logger_fired.state_index = idx;
break;
}
}
}
mtk_lpm_notifier_register(&mt6781_logger_nb);
mtk_lpm_notifier_register(&mt6781_idle_save_sleep_info_nb);
mt6781_log_timer.tm.timeout = mt6781_log_timer_func;
mt6781_log_timer.tm.priv = &mt6781_log_timer;
mtk_lpm_timer_init(&mt6781_log_timer.tm, MTK_LPM_TIMER_REPEAT);
mtk_lpm_timer_interval_update(&mt6781_log_timer.tm,
MT6781_LOG_DEFAULT_MS);
mtk_lpm_timer_start(&mt6781_log_timer.tm);
register_syscore_ops(&mt6781_suspend_save_sleep_info_syscore_ops);
return 0;
}
late_initcall_sync(mt6781_logger_init);
@@ -0,0 +1,148 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <mtk_lpm_trace_event/mtk_lpm_trace_event.h>
#include <mtk_spm_sysfs.h>
#include <mt6781_spm_reg.h>
#define plat_mmio_read(offset) __raw_readl(spm_base + offset)
void __iomem *spm_base;
struct timer_list spm_resource_req_timer;
u32 spm_resource_req_timer_is_enabled;
u32 spm_resource_req_timer_ms;
/* static void spm_resource_req_timer_fn(unsigned long data)
{
u32 req_sta_0, req_sta_1, req_sta_4;
u32 src_req;
u32 md = 0, conn = 0, scp = 0, adsp = 0;
u32 ufs = 0, msdc = 0, disp = 0, apu = 0;
u32 spm = 0;
req_sta_0 = plat_mmio_read(SRC_REQ_STA_0);
if (req_sta_0 & 0xFFF)
md = 1;
if (req_sta_0 & (0x3F << 12))
conn = 1;
if (req_sta_0 & (0xF << 26))
disp = 1;
req_sta_1 = plat_mmio_read(SRC_REQ_STA_1);
if (req_sta_1 & 0x1F)
scp = 1;
if (req_sta_1 & (0x1F << 5))
adsp = 1;
if (req_sta_1 & (0x1F << 10))
ufs = 1;
if (req_sta_1 & (0x3FF << 21))
msdc = 1;
req_sta_4 = plat_mmio_read(SRC_REQ_STA_4);
if (req_sta_4 & 0x1F)
apu = 1;
src_req = plat_mmio_read(SPM_SRC_REQ);
if (src_req & 0x19B)
spm = 1;
trace_SPM__resource_req_0(
md, conn,
scp, adsp,
ufs, msdc,
disp, apu,
spm);
spm_resource_req_timer.expires = jiffies +
msecs_to_jiffies(spm_resource_req_timer_ms);
add_timer(&spm_resource_req_timer);
}
static void spm_resource_req_timer_en(u32 enable, u32 timer_ms)
{
if (enable) {
if (spm_resource_req_timer.function == NULL) {
init_timer(&spm_resource_req_timer);
spm_resource_req_timer.function =
spm_resource_req_timer_fn;
spm_resource_req_timer.data = 0;
spm_resource_req_timer_is_enabled = false;
}
if (spm_resource_req_timer_is_enabled)
return;
spm_resource_req_timer_ms = timer_ms;
spm_resource_req_timer.expires = jiffies +
msecs_to_jiffies(spm_resource_req_timer_ms);
add_timer(&spm_resource_req_timer);
spm_resource_req_timer_is_enabled = true;
} else if (spm_resource_req_timer_is_enabled) {
del_timer(&spm_resource_req_timer);
spm_resource_req_timer_is_enabled = false;
}
}
*/
ssize_t get_spm_resource_req_timer_enable(char *ToUserBuf
, size_t sz, void *priv)
{
int bLen = snprintf(ToUserBuf, sz
, "spm resource request timer is enabled: %d\n",
spm_resource_req_timer_is_enabled);
return (bLen > sz) ? sz : bLen;
}
ssize_t set_spm_resource_req_timer_enable(char *ToUserBuf
, size_t sz, void *priv)
{
u32 is_enable;
u32 timer_ms;
if (!ToUserBuf)
return -EINVAL;
if (sscanf(ToUserBuf, "%d %d", &is_enable, &timer_ms) == 2) {
//spm_resource_req_timer_en(is_enable, timer_ms);
return sz;
}
return -EINVAL;
}
static const struct mtk_lp_sysfs_op spm_resource_req_timer_enable_fops = {
.fs_read = get_spm_resource_req_timer_enable,
.fs_write = set_spm_resource_req_timer_enable,
};
bool spm_is_md1_sleep(void)
{
return !(plat_mmio_read(SRC_REQ_STA_0) & 0x3F);
}
EXPORT_SYMBOL(spm_is_md1_sleep);
int __init mt6781_lpm_trace_init(void)
{
struct device_node *node = NULL;
node = of_find_compatible_node(NULL, NULL, "mediatek,sleep");
if (node) {
spm_base = of_iomap(node, 0);
of_node_put(node);
}
mtk_spm_sysfs_root_entry_create();
mtk_spm_sysfs_entry_node_add("spm_dump_res_req_enable", 0444
, &spm_resource_req_timer_enable_fops, NULL);
return 0;
}
late_initcall_sync(mt6781_lpm_trace_init);
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include "mtk_power_gs_array.h"
#include <mtk_lpm_call.h>
#include <mtk_lpm_call_type.h>
#include <gs/mtk_lpm_pwr_gs.h>
#include <gs/v1/mtk_power_gs.h>
int mt6781_pwr_gs_set(unsigned int type, const struct mtk_lpm_data *val)
{
int ret = 0;
if (val->d.u32 & GS_PMIC)
ret = mtk_lpm_pwr_gs_compare(MTK_LPM_GS_CMP_PMIC, type);
if (ret)
return ret;
#ifdef MTK_LPM_GS_PLAT_CLK_DUMP_SUPPORT
if (val->d.v_u32 & GS_DCM)
ret = mtk_lpm_pwr_gs_compare_by_type(
MTK_LPM_GS_CMP_CLK, type, GS_DCM);
if (ret)
return ret;
if (val->d.v_u32 & GS_CG)
ret = mtk_lpm_pwr_gs_compare_by_type(
MTK_LPM_GS_CMP_CLK, type, GS_CG);
#endif
return ret;
}
struct mtk_lpm_callee mt6781_pwr_gs_callee = {
.uid = MTK_LPM_CALLEE_PWR_GS,
.i.simple = {
.set = mt6781_pwr_gs_set,
},
};
#ifdef MTK_LPM_GS_PLAT_CLK_DUMP_SUPPORT
struct mtk_lpm_gs_clk mt6781_clk_cg = {
.type = GS_CG,
.name = "CG",
};
struct mtk_lpm_gs_clk mt6781_clk_dcm = {
.type = GS_DCM,
.name = "DCM",
};
struct mtk_lpm_gs_clk *mt6781_clks[] = {
&mt6781_clk_dcm,
&mt6781_clk_cg,
NULL,
};
int mt6781_power_gs_clk_user_attach(struct mtk_lpm_gs_clk *p)
{
/* Set compare golden setting for scenario */
if (!strcmp(p->name, "CG")) {
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].name = "suspend";
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].array =
AP_CG_Golden_Setting_tcl_gs_suspend;
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].array_sz =
AP_CG_Golden_Setting_tcl_gs_suspend_len;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].name = "sodi3";
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].array =
AP_CG_Golden_Setting_tcl_gs_sodi;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].array_sz =
AP_CG_Golden_Setting_tcl_gs_sodi_len;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].name = "dpidle";
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].array =
AP_CG_Golden_Setting_tcl_gs_dpidle;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].array_sz =
AP_CG_Golden_Setting_tcl_gs_dpidle_len;
} else if (!strcmp(p->name, "DCM")) {
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].name = "suspend";
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].array =
AP_DCM_Golden_Setting_tcl_gs_suspend;
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].array_sz =
AP_DCM_Golden_Setting_tcl_gs_suspend_len;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].name = "sodi3";
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].array =
AP_DCM_Golden_Setting_tcl_gs_sodi;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].array_sz =
AP_DCM_Golden_Setting_tcl_gs_sodi_len;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].name = "dpidle";
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].array =
AP_DCM_Golden_Setting_tcl_gs_dpidle;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].array_sz =
AP_DCM_Golden_Setting_tcl_gs_dpidle_len;
}
return 0;
}
struct mtk_lpm_gs_clk_info mt6781_clk_infos = {
.attach = mt6781_power_gs_clk_user_attach,
.dcm = mt6781_clks,
};
#endif
/* PMIC */
struct mtk_lpm_gs_pmic mt6781_pmic6366 = {
.type = GS_PMIC,
.regulator = "vcore",
.pwr_domain = "6366",
};
struct mtk_lpm_gs_pmic *mt6781_pmic[] = {
&mt6781_pmic6366,
NULL,
};
int mt6781_power_gs_pmic_user_attach(struct mtk_lpm_gs_pmic *p)
{
if (!p || !p->regulator)
return -EINVAL;
/* Set compare golden setting for scenario */
if (!strcmp(p->regulator, "vcore")) {
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].name = "suspend";
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].array =
AP_PMIC_REG_6366_gs_suspend_32kless;
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].array_sz =
AP_PMIC_REG_6366_gs_suspend_32kless_len;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].name = "sodi3";
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].array =
AP_PMIC_REG_6366_gs_sodi3p0_32kless;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].array_sz =
AP_PMIC_REG_6366_gs_sodi3p0_32kless_len;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].name = "dpidle";
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].array =
AP_PMIC_REG_6366_gs_deepidle___lp_mp3_32kless;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].array_sz =
AP_PMIC_REG_6366_gs_deepidle___lp_mp3_32kless_len;
} else
return -EINVAL;
return 0;
}
struct mtk_lpm_gs_pmic_info mt6781_pmic_infos = {
.pmic = mt6781_pmic,
.attach = mt6781_power_gs_pmic_user_attach,
};
int mt6781_power_gs_init(void)
{
mtk_lpm_callee_registry(&mt6781_pwr_gs_callee);
/* initial gs compare method */
mtk_lpm_pwr_gs_common_init();
/* initial gs pmic information */
mtk_lpm_pwr_gs_compare_init(MTK_LPM_GS_CMP_PMIC, &mt6781_pmic_infos);
#ifdef MTK_LPM_GS_PLAT_CLK_DUMP_SUPPORT
/* initial gs dcm information */
mtk_lpm_pwr_gs_compare_init(MTK_LPM_GS_CMP_CLK, &mt6781_clk_infos);
#endif
return 0;
}
int mt6781_power_gs_deinit(void)
{
mtk_lpm_pwr_gs_common_deinit();
return 0;
}
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#ifndef __MT6781_PWR_GS_COMPARE_H__
#define __MT6781_PWR_GS_COMPARE_H__
int mt6781_power_gs_init(void);
int mt6781_power_gs_deinit(void);
#endif
@@ -0,0 +1,268 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
/* This file is generated by GenLP_setting.pl v1.5.7 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
const unsigned int AP_CG_Golden_Setting_tcl_gs_dpidle_data[] = {
/* Address Mask Golden Setting Value */
0x0D0A002C, 0x00000001, 0x00000001,/* DBGBUSCLK_EN */
0x0D0A0030, 0x00000001, 0x00000001,/* DBGSYSCLK_EN */
0x0D0A0070, 0x00000001, 0x00000001,/* ATB_CON */
0x10000000, 0x00000700, 0x00000000,/* CLK_MODE */
0x10000040, 0x80808000, 0x80808000,/* CLK_CFG_0 */
0x10000050, 0x80808080, 0x80808080,/* CLK_CFG_1 */
0x10000060, 0x80808080, 0x80808080,/* CLK_CFG_2 */
0x10000070, 0x80808080, 0x80808080,/* CLK_CFG_3 */
0x10000080, 0x80808080, 0x80808080,/* CLK_CFG_4 */
0x10000090, 0x80808080, 0x80800080,/* CLK_CFG_5 */
0x100000A0, 0x80800080, 0x80000080,/* CLK_CFG_6 */
0x100000B0, 0x80808080, 0x80808080,/* CLK_CFG_7 */
0x100000C0, 0x80808080, 0x80808080,/* CLK_CFG_8 */
0x100000D0, 0x80808080, 0x80808080,/* CLK_CFG_9 */
0x100000E0, 0x00808080, 0x00008080,/* CLK_CFG_10 */
0x100000EC, 0x80808080, 0x80808080,/* CLK_CFG_11 */
0x10000130, 0x00000080, 0x00000080,/* CLK_CFG_20 */
0x10000140, 0x00003030, 0x00003000,/* CLK_MISC_CFG_0 */
0x10000320, 0x000000FC, 0x000000FC,/* CLK_AUDDIV_0 */
0x10000328, 0x00010000, 0x00010000,/* CLK_AUDDIV_2 */
0x10001080, 0x99FFFF7F, 0x99FFFF60,/* MODULE_SW_CG_0_SET */
0x10001084, 0x99FFFF7F, 0x0000001F,/* MODULE_SW_CG_0_CLR */
0x10001088, 0xBF9C7F9E, 0x3B140B1E,/* MODULE_SW_CG_1_SET */
0x1000108C, 0xBF9C7F9E, 0x84887480,/* MODULE_SW_CG_1_CLR */
0x100010A4, 0xFFFCBFDD, 0xFFFCBFDD,/* MODULE_SW_CG_2_SET */
0x100010A8, 0xFFFCBFDD, 0x00000000,/* MODULE_SW_CG_2_CLR */
0x100010C0, 0x0FFF87DB, 0x0CC387DB,/* MODULE_SW_CG_3_SET */
0x100010C4, 0x0FFF87DB, 0x033C0000,/* MODULE_SW_CG_3_CLR */
0x10001200, 0x80000000, 0x00000000,/* INFRA_TOPAXI_SI0_CTL */
0x1000320C, 0x80000000, 0x00000000,/* PERIAXI_SI0_CTL */
0x1000C008, 0x1FFE0000, 0x1FFE0000,/* AP_PLL_CON2 */
0x1000C00C, 0x7FFFFFFF, 0x7FFD5550,/* AP_PLL_CON3 */
0x1000C010, 0x0003F50F, 0x0002A405,/* AP_PLL_CON4 */
0x1000C020, 0x00036BF0, 0x000022A0,/* AP_PLL_CON8 */
0x1000C200, 0x01000001, 0x01000001,/* ARMPLL_LL_CON0 */
0x1000C20C, 0x00000003, 0x00000001,/* ARMPLL_LL_PWR_CON0 */
0x1000C210, 0x01000001, 0x01000001,/* ARMPLL_BL_CON0 */
0x1000C21C, 0x00000003, 0x00000001,/* ARMPLL_BL_PWR_CON0 */
0x1000C220, 0x01000001, 0x01000001,/* ARMPLL_BB_CON0 */
0x1000C22C, 0x00000003, 0x00000001,/* ARMPLL_BB_PWR_CON0 */
0x1000C230, 0x01000001, 0x01000001,/* MAINPLL_CON0 */
0x1000C23C, 0x00000003, 0x00000001,/* MAINPLL_PWR_CON0 */
0x1000C240, 0x00000001, 0x00000000,/* UNIVPLL_CON0 */
0x1000C24C, 0x00000003, 0x00000002,/* UNIVPLL_PWR_CON0 */
0x1000C250, 0x00000001, 0x00000000,/* MFGPLL_CON0 */
0x1000C25C, 0x00000003, 0x00000002,/* MFGPLL_PWR_CON0 */
0x1000C260, 0x00000001, 0x00000000,/* MSDCPLL_CON0 */
0x1000C26C, 0x00000003, 0x00000002,/* MSDCPLL_PWR_CON0 */
0x1000C270, 0x00000001, 0x00000000,/* TVDPLL_CON0 */
0x1000C27C, 0x00000003, 0x00000002,/* TVDPLL_PWR_CON0 */
0x1000C280, 0x00000001, 0x00000000,/* MMPLL_CON0 */
0x1000C28C, 0x00000003, 0x00000002,/* MMPLL_PWR_CON0 */
0x1000C290, 0x00000001, 0x00000000,/* MPLL_CON0 */
0x1000C29C, 0x00000003, 0x00000002,/* MPLL_PWR_CON0 */
0x1000C2A0, 0x01000001, 0x01000001,/* CCIPLL_CON0 */
0x1000C2AC, 0x00000003, 0x00000001,/* CCIPLL_PWR_CON0 */
0x1000C2B0, 0x00000001, 0x00000000,/* ADSPPLL_CON0 */
0x1000C2BC, 0x00000003, 0x00000002,/* ADSPPLL_PWR_CON0 */
0x1000C2C0, 0x00000001, 0x00000000,/* APLL1_CON0 */
0x1000C2D0, 0x00000003, 0x00000002,/* APLL1_PWR_CON0 */
0x1000C2D4, 0x00000001, 0x00000000,/* APLL2_CON0 */
0x1000C2E4, 0x00000003, 0x00000002,/* APLL2_PWR_CON0 */
0x1000C2EC, 0x00000001, 0x00000000,/* APUPLL_CON0 */
0x1000C2F8, 0x00000003, 0x00000002,/* APUPLL_PWR_CON0 */
0x1000D0F0, 0x00000001, 0x00000001,/* TIMER_CTRL */
0x10012000, 0x00000001, 0x00000000,/* DVFSRC_BASIC_CONTROL */
0x10219834, 0x00000003, 0x00000000,/* EMI_THRO_PRD0 */
0x10219838, 0x00FF0000, 0x00000000,/* EMI_THRO_PRD1 */
0x10219858, 0x00000800, 0x00000000,/* EMI_THRO_CTRL1 */
0x1021D468, 0x000000FF, 0x00000022,/* CG_EN */
0x1022C0F0, 0xFFFF0000, 0xFFFF0000,/* GCE_CTL_INT0 */
0x10443000, 0x001FEBFE, 0x00004000,/* SSPM_CK_EN */
0x10443010, 0x00000001, 0x00000000,/* SSPM_UART_CTRL */
0x10601000, 0x0000003B, 0x00000000,/* ADSP_CK_EN */
0x10601010, 0x00000001, 0x00000000,/* ADSP_UART_CTRL */
0x1100B0E4, 0x00000001, 0x00000000,/* LVTSCLKEN_0 */
0x1100B1E4, 0x00000001, 0x00000000,/* LVTSCLKEN_1 */
0x1100B2E4, 0x00000001, 0x00000000,/* LVTSCLKEN_2 */
0x1100BF00, 0x80070000, 0x00000000,/* RST; BX; ACC; RAND */
0x11017E00, 0x00000001, 0x00000001,/* AP_CLOCK_CG_RO */
0x11210000, 0x1F1C0304, 0x1F1C0304,/* AUDIO_TOP_CON0 */
0x11210004, 0xF033F0F0, 0xF033F0F0,/* AUDIO_TOP_CON1 */
0x11210008, 0x01800000, 0x01800000,/* AUDIO_TOP_CON2 */
0x112300B4, 0x00400000, 0x00000000,/* PATCH_BIT1 */
0x11270144, 0x00000001, 0x00000000,/* UFS_ADDR_XOUFS_ST */
0x11C9004C, 0x80000000, 0x00000000,/* MSDC_TOP_TEST_CFG2 */
0x11CA0380, 0x00000001, 0x00000000,/* U2PHYBC12C */
0x11CD004C, 0x80000000, 0x00000000,/* MSDC_TOP_TEST_CFG2 */
0x13FBF004, 0x00000001, 0x00000001,/* MFG_CG_SET */
0x13FBF008, 0x00000001, 0x00000000,/* MFG_CG_CLR */
0x14000100, 0xFFFFFFFF, 0xFFFFFFFF,/* MMSYS_CG_CON0 */
0x14000110, 0xFFFFFFFF, 0xFFFFFFFF,/* MMSYS_CG_CON1 */
0x140001A0, 0xFFFFFFFF, 0xFFFFFFFF,/* MMSYS_CG_CON2 */
0x15020000, 0x00001007, 0x00001007,/* IMG_CG_CON */
0x15820000, 0x000011C3, 0x000011C3,/* IMG_CG_CON */
0x1602F000, 0x00000111, 0x00000000,/* VDEC_CKEN_SET */
0x1602F004, 0x00000001, 0x00000001,/* VDEC_CKEN_CLR */
0x1602F008, 0x00000001, 0x00000000,/* LARB_CKEN_CON_SET */
0x1602F00C, 0x00000001, 0x00000001,/* LARB_CKEN_CON_CLR */
0x1602F190, 0x00000001, 0x00000000,/* MINI_MDP_CFG_0 */
0x1602F200, 0x00000111, 0x00000000,/* LAT_CKEN_SET */
0x1602F204, 0x00000001, 0x00000001,/* LAT_CKEN_CLR */
0x17000000, 0x10000111, 0x00000000,/* VENCSYS_CG_CON */
0x17000004, 0x10000111, 0x00000000,/* VENCSYS_CG_SET */
0x17000008, 0x10000111, 0x10000111,/* VENCSYS_CG_CLR */
0x1A000000, 0x000FFDCF, 0x000FFDCF,/* CAMSYS_CG_CON */
0x1A04F000, 0x00000007, 0x00000007,/* CAMSYS_CG_CON */
0x1A06F000, 0x00000007, 0x00000007,/* CAMSYS_CG_CON */
0x1B000100, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON0 */
0x1B000110, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON1 */
0x1B000120, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON2 */
0x1B000130, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON3 */
0x1B000140, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON4 */
0x1C000000, 0x0000017F, 0x0000017F /* IMG_CG_CON */
};
const unsigned int *AP_CG_Golden_Setting_tcl_gs_dpidle =
AP_CG_Golden_Setting_tcl_gs_dpidle_data;
unsigned int AP_CG_Golden_Setting_tcl_gs_dpidle_len = 342;
const unsigned int AP_CG_Golden_Setting_tcl_gs_suspend_data[] = {
/* Address Mask Golden Setting Value */
0x0D0A002C, 0x00000001, 0x00000000,/* DBGBUSCLK_EN */
0x0D0A0030, 0x00000001, 0x00000000,/* DBGSYSCLK_EN */
0x0D0A0070, 0x00000001, 0x00000000,/* ATB_CON */
0x10000000, 0x00000700, 0x00000000,/* CLK_MODE */
0x10000040, 0x80808000, 0x80808000,/* CLK_CFG_0 */
0x10000050, 0x80808080, 0x80808080,/* CLK_CFG_1 */
0x10000060, 0x80808080, 0x80808080,/* CLK_CFG_2 */
0x10000070, 0x80808080, 0x80808080,/* CLK_CFG_3 */
0x10000080, 0x80808080, 0x80808080,/* CLK_CFG_4 */
0x10000090, 0x80808080, 0x80808080,/* CLK_CFG_5 */
0x100000A0, 0x80800080, 0x80000080,/* CLK_CFG_6 */
0x100000B0, 0x80808080, 0x80808080,/* CLK_CFG_7 */
0x100000C0, 0x80808080, 0x80808080,/* CLK_CFG_8 */
0x100000D0, 0x80808080, 0x80808080,/* CLK_CFG_9 */
0x100000E0, 0x00808080, 0x00808080,/* CLK_CFG_10 */
0x100000EC, 0x80808080, 0x80808080,/* CLK_CFG_11 */
0x10000130, 0x00000080, 0x00000000,/* CLK_CFG_20 */
0x10000140, 0x00003030, 0x00003000,/* CLK_MISC_CFG_0 */
0x10000320, 0x000000FC, 0x000000FC,/* CLK_AUDDIV_0 */
0x10000328, 0x00010000, 0x00010000,/* CLK_AUDDIV_2 */
0x10001080, 0x99FFFF7F, 0x99FFFF60,/* MODULE_SW_CG_0_SET */
0x10001084, 0x99FFFF7F, 0x0000001F,/* MODULE_SW_CG_0_CLR */
0x10001088, 0xBF9C7F9E, 0x3B140B1E,/* MODULE_SW_CG_1_SET */
0x1000108C, 0xBF9C7F9E, 0x84887480,/* MODULE_SW_CG_1_CLR */
0x100010A4, 0xFFFCBFDD, 0xFFFCBFDD,/* MODULE_SW_CG_2_SET */
0x100010A8, 0xFFFCBFDD, 0x00000000,/* MODULE_SW_CG_2_CLR */
0x100010C0, 0x0FFF87DB, 0x0CC387DB,/* MODULE_SW_CG_3_SET */
0x100010C4, 0x0FFF87DB, 0x033C0000,/* MODULE_SW_CG_3_CLR */
0x10001200, 0x80000000, 0x00000000,/* INFRA_TOPAXI_SI0_CTL */
0x1000320C, 0x80000000, 0x00000000,/* PERIAXI_SI0_CTL */
0x1000C008, 0x1FFE0000, 0x1FFE0000,/* AP_PLL_CON2 */
0x1000C00C, 0x7FFFFFFF, 0x7FFD5550,/* AP_PLL_CON3 */
0x1000C010, 0x0003F50F, 0x0002A405,/* AP_PLL_CON4 */
0x1000C020, 0x00036BF0, 0x000020A0,/* AP_PLL_CON8 */
0x1000C200, 0x01000001, 0x01000001,/* ARMPLL_LL_CON0 */
0x1000C20C, 0x00000003, 0x00000001,/* ARMPLL_LL_PWR_CON0 */
0x1000C210, 0x01000001, 0x01000001,/* ARMPLL_BL_CON0 */
0x1000C21C, 0x00000003, 0x00000001,/* ARMPLL_BL_PWR_CON0 */
0x1000C220, 0x01000001, 0x01000001,/* ARMPLL_BB_CON0 */
0x1000C22C, 0x00000003, 0x00000001,/* ARMPLL_BB_PWR_CON0 */
0x1000C230, 0x01000001, 0x01000001,/* MAINPLL_CON0 */
0x1000C23C, 0x00000003, 0x00000001,/* MAINPLL_PWR_CON0 */
0x1000C240, 0x00000001, 0x00000000,/* UNIVPLL_CON0 */
0x1000C24C, 0x00000003, 0x00000002,/* UNIVPLL_PWR_CON0 */
0x1000C250, 0x00000001, 0x00000000,/* MFGPLL_CON0 */
0x1000C25C, 0x00000003, 0x00000002,/* MFGPLL_PWR_CON0 */
0x1000C260, 0x00000001, 0x00000000,/* MSDCPLL_CON0 */
0x1000C26C, 0x00000003, 0x00000002,/* MSDCPLL_PWR_CON0 */
0x1000C270, 0x00000001, 0x00000000,/* TVDPLL_CON0 */
0x1000C27C, 0x00000003, 0x00000002,/* TVDPLL_PWR_CON0 */
0x1000C280, 0x00000001, 0x00000000,/* MMPLL_CON0 */
0x1000C28C, 0x00000003, 0x00000002,/* MMPLL_PWR_CON0 */
0x1000C290, 0x00000001, 0x00000000,/* MPLL_CON0 */
0x1000C29C, 0x00000003, 0x00000002,/* MPLL_PWR_CON0 */
0x1000C2A0, 0x01000001, 0x01000001,/* CCIPLL_CON0 */
0x1000C2AC, 0x00000003, 0x00000001,/* CCIPLL_PWR_CON0 */
0x1000C2B0, 0x00000001, 0x00000000,/* ADSPPLL_CON0 */
0x1000C2BC, 0x00000003, 0x00000002,/* ADSPPLL_PWR_CON0 */
0x1000C2C0, 0x00000001, 0x00000000,/* APLL1_CON0 */
0x1000C2D0, 0x00000003, 0x00000002,/* APLL1_PWR_CON0 */
0x1000C2D4, 0x00000001, 0x00000000,/* APLL2_CON0 */
0x1000C2E4, 0x00000003, 0x00000002,/* APLL2_PWR_CON0 */
0x1000C2EC, 0x00000001, 0x00000000,/* APUPLL_CON0 */
0x1000C2F8, 0x00000003, 0x00000002,/* APUPLL_PWR_CON0 */
0x1000D0F0, 0x00000001, 0x00000001,/* TIMER_CTRL */
0x10012000, 0x00000001, 0x00000000,/* DVFSRC_BASIC_CONTROL */
0x10219834, 0x00000003, 0x00000000,/* EMI_THRO_PRD0 */
0x10219838, 0x00FF0000, 0x00000000,/* EMI_THRO_PRD1 */
0x10219858, 0x00000800, 0x00000000,/* EMI_THRO_CTRL1 */
0x1021D468, 0x000000FF, 0x00000022,/* CG_EN */
0x1022C0F0, 0xFFFF0000, 0xFFFF0000,/* GCE_CTL_INT0 */
0x10443000, 0x001FEBFE, 0x00004000,/* SSPM_CK_EN */
0x10443010, 0x00000001, 0x00000000,/* SSPM_UART_CTRL */
0x10601000, 0x0000003B, 0x00000000,/* ADSP_CK_EN */
0x10601010, 0x00000001, 0x00000000,/* ADSP_UART_CTRL */
0x1100B0E4, 0x00000001, 0x00000000,/* LVTSCLKEN_0 */
0x1100B1E4, 0x00000001, 0x00000000,/* LVTSCLKEN_1 */
0x1100B2E4, 0x00000001, 0x00000000,/* LVTSCLKEN_2 */
0x1100BF00, 0x80070000, 0x00000000,/* RST; BX; ACC; RAND */
0x11017E00, 0x00000001, 0x00000001,/* AP_CLOCK_CG_RO */
0x11210000, 0x1F1C0304, 0x1F1C0304,/* AUDIO_TOP_CON0 */
0x11210004, 0xF033F0F0, 0xF033F0F0,/* AUDIO_TOP_CON1 */
0x11210008, 0x01800000, 0x01800000,/* AUDIO_TOP_CON2 */
0x112300B4, 0x00400000, 0x00000000,/* PATCH_BIT1 */
0x11270144, 0x00000001, 0x00000000,/* UFS_ADDR_XOUFS_ST */
0x11C9004C, 0x80000000, 0x00000000,/* MSDC_TOP_TEST_CFG2 */
0x11CA0380, 0x00000001, 0x00000000,/* U2PHYBC12C */
0x11CD004C, 0x80000000, 0x00000000,/* MSDC_TOP_TEST_CFG2 */
0x13FBF004, 0x00000001, 0x00000001,/* MFG_CG_SET */
0x13FBF008, 0x00000001, 0x00000000,/* MFG_CG_CLR */
0x14000100, 0xFFFFFFFF, 0xFFFFFFFF,/* MMSYS_CG_CON0 */
0x14000110, 0xFFFFFFFF, 0xFFFFFFFF,/* MMSYS_CG_CON1 */
0x140001A0, 0xFFFFFFFF, 0xFFFFFFFF,/* MMSYS_CG_CON2 */
0x14012000, 0x00000101, 0x00000000,/* DISP_DSC_CON */
0x14012400, 0x00000101, 0x00000000,/* DISP_DSC1_CON */
0x15020000, 0x00001007, 0x00001007,/* IMG_CG_CON */
0x15820000, 0x000011C3, 0x000011C3,/* IMG_CG_CON */
0x1602F000, 0x00000111, 0x00000000,/* VDEC_CKEN_SET */
0x1602F004, 0x00000001, 0x00000001,/* VDEC_CKEN_CLR */
0x1602F008, 0x00000001, 0x00000000,/* LARB_CKEN_CON_SET */
0x1602F00C, 0x00000001, 0x00000001,/* LARB_CKEN_CON_CLR */
0x1602F190, 0x00000001, 0x00000000,/* MINI_MDP_CFG_0 */
0x1602F200, 0x00000111, 0x00000000,/* LAT_CKEN_SET */
0x1602F204, 0x00000001, 0x00000001,/* LAT_CKEN_CLR */
0x17000000, 0x10000111, 0x00000000,/* VENCSYS_CG_CON */
0x17000004, 0x10000111, 0x00000000,/* VENCSYS_CG_SET */
0x17000008, 0x10000111, 0x10000111,/* VENCSYS_CG_CLR */
0x1A000000, 0x000FFDCF, 0x000FFDCF,/* CAMSYS_CG_CON */
0x1A04F000, 0x00000007, 0x00000007,/* CAMSYS_CG_CON */
0x1A06F000, 0x00000007, 0x00000007,/* CAMSYS_CG_CON */
0x1B000100, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON0 */
0x1B000110, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON1 */
0x1B000120, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON2 */
0x1B000130, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON3 */
0x1B000140, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON4 */
0x1C000000, 0x0000017F, 0x0000017F /* IMG_CG_CON */
};
const unsigned int *AP_CG_Golden_Setting_tcl_gs_suspend =
AP_CG_Golden_Setting_tcl_gs_suspend_data;
unsigned int AP_CG_Golden_Setting_tcl_gs_suspend_len = 348;
/*NO CODEGEN FOR SODI */
const unsigned int AP_CG_Golden_Setting_tcl_gs_sodi_data[] = {
/* Address Mask Golden Setting Value */
};
const unsigned int *AP_CG_Golden_Setting_tcl_gs_sodi;
unsigned int AP_CG_Golden_Setting_tcl_gs_sodi_len;
@@ -0,0 +1,274 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/pm_qos.h>
#include <mtk_lpm.h>
#include <mtk_lp_plat_reg.h>
#include "mtk_cpupm_dbg.h"
#include "mtk_cpuidle_cpc.h"
static DEFINE_SPINLOCK(cpc_prof_spin_lock);
/* profile latecny */
#define PROF_DEV_NAME_SIZE 36
#define get_core_type_index(cpu) ((cpu > 3) ? CPU_TYPE_B : CPU_TYPE_L)
struct mtk_cpc_lat_data {
char name[PROF_DEV_NAME_SIZE];
unsigned int on_sum;
unsigned int on_max;
unsigned int off_sum;
unsigned int off_max;
unsigned int on_cnt;
unsigned int off_cnt;
};
struct mtk_cpc_device {
union {
struct mtk_cpc_lat_data p[DEV_TYPE_NUM];
struct {
struct mtk_cpc_lat_data cpu[NF_CPU_TYPE];
struct mtk_cpc_lat_data cluster;
struct mtk_cpc_lat_data mcusys;
};
};
bool prof_en;
};
struct mtk_cpc_device cpc;
static void mtk_cpc_clr_lat(void)
{
unsigned int size;
unsigned int ofs;
int i;
ofs = sizeof(cpc.p[0].name);
size = sizeof(struct mtk_cpc_lat_data) - ofs;
for (i = 0; i < DEV_TYPE_NUM; i++)
memset((char *)&cpc.p[i] + ofs, 0, size);
}
static void mtk_cpc_cal_lat(void)
{
}
#define __mtk_cpc_record_lat(sum, max, lat) \
do { \
if (lat > max) \
max = lat; \
(sum) += (lat); \
} while (0)
static void mtk_cpc_record_lat(struct mtk_cpc_lat_data *lat,
unsigned int on_ticks, unsigned int off_ticks)
{
unsigned long flags;
if ((on_ticks == 0) || (off_ticks == 0))
return;
spin_lock_irqsave(&cpc_prof_spin_lock, flags);
__mtk_cpc_record_lat(lat->on_sum, lat->on_max, on_ticks);
lat->on_cnt++;
__mtk_cpc_record_lat(lat->off_sum, lat->off_max, off_ticks);
lat->off_cnt++;
spin_unlock_irqrestore(&cpc_prof_spin_lock, flags);
}
static void mtk_cpc_save_cpu_lat(int cpu)
{
unsigned int lat, on, off;
lat = mtk_cpupm_mcusys_read(CPC_CPU_LATENCY(cpu));
on = (lat >> 16) & 0xFFFF;
off = lat & 0xFFFF;
mtk_cpc_record_lat(&cpc.cpu[get_core_type_index(cpu)],
on, off);
}
static void mtk_cpc_save_cpusys_lat(void)
{
unsigned int lat_on, lat_off, on, off;
lat_on = mtk_cpupm_mcusys_read(CPC_CLUSTER_ON_LATENCY);
lat_off = mtk_cpupm_mcusys_read(CPC_CLUSTER_OFF_LATENCY);
on = lat_on & 0xFFFF;
off = lat_off & 0xFFFF;
mtk_cpc_record_lat(&cpc.cluster, on, off);
}
static void mtk_cpc_save_mcusys_lat(void)
{
unsigned int lat, on, off;
lat = mtk_cpupm_mcusys_read(CPC_MCUSYS_LATENCY);
on = (lat >> 16) & 0xFFFF;
off = lat & 0xFFFF;
mtk_cpc_record_lat(&cpc.mcusys, on, off);
}
static bool mtk_cpc_did_cluster_pwr_off(void)
{
static unsigned int last_cnt;
unsigned int cnt = mtk_cpupm_mcusys_read(CPC_DORMANT_COUNTER);
/**
* Cluster off count
* bit[0:15] : memory retention
* bit[16:31] : memory off
*/
if ((cnt & 0x7FFF) == 0)
cnt = ((cnt >> 16) & 0x7FFF);
else
cnt = cnt & 0x7FFF;
cnt += mtk_cpupm_syssram_read(SYSRAM_CPC_CPUSYS_CNT_BACKUP);
if (last_cnt == cnt)
return false;
last_cnt = cnt;
return true;
}
static bool mtk_cpc_did_mcusys_pwr_off(void)
{
static unsigned int last_cnt;
unsigned int cnt = mtk_cpupm_syssram_read(SYSRAM_MCUPM_MCUSYS_COUNTER);
cnt += mtk_cpupm_syssram_read(SYSRAM_MCUSYS_CNT);
if (last_cnt == cnt)
return false;
last_cnt = cnt;
return true;
}
static void mtk_cpc_save_latency(int cpu)
{
mtk_cpc_save_cpu_lat(cpu);
if (mtk_cpc_did_mcusys_pwr_off())
mtk_cpc_save_mcusys_lat();
else if (!mtk_cpc_did_cluster_pwr_off())
return;
mtk_cpc_save_cpusys_lat();
}
void mtk_cpc_prof_lat_dump(struct seq_file *m)
{
struct mtk_cpc_lat_data *lat;
int i;
for (i = 0; i < DEV_TYPE_NUM; i++) {
lat = &cpc.p[i];
seq_printf(m, "%s\n", lat->name);
if (lat->off_cnt) {
seq_printf(m, "\toff : avg = %2dus, max = %3dus, cnt = %d\n",
cpc_tick_to_us(lat->off_sum / lat->off_cnt),
cpc_tick_to_us(lat->off_max),
lat->off_cnt);
} else {
seq_puts(m, "\toff : None\n");
}
if (lat->on_cnt) {
seq_printf(m, "\ton : avg = %2dus, max = %3dus, cnt = %d\n",
cpc_tick_to_us(lat->on_sum / lat->on_cnt),
cpc_tick_to_us(lat->on_max),
lat->on_cnt);
} else {
seq_puts(m, "\ton : None\n");
}
}
}
void mtk_cpc_prof_start(void)
{
mtk_cpupm_block();
cpc_prof_en();
mtk_cpc_clr_lat();
cpc.prof_en = true;
mtk_cpupm_allow();
}
void mtk_cpc_prof_stop(void)
{
mtk_cpupm_block();
cpc.prof_en = false;
mtk_cpc_cal_lat();
cpc_prof_dis();
mtk_cpupm_allow();
}
int mtk_cpc_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct mtk_lpm_nb_data *nb_data = (struct mtk_lpm_nb_data *)data;
if (!cpc.prof_en)
return NOTIFY_OK;
if (action & MTK_LPM_NB_BEFORE_REFLECT)
mtk_cpc_save_latency(nb_data->cpu);
return NOTIFY_OK;
}
struct notifier_block mtk_cpc_nb = {
.notifier_call = mtk_cpc_notify,
};
int __init mtk_cpc_init(void)
{
int cx0, cx1, cx2, cx3;
cpc.prof_en = false;
cx0 = snprintf(cpc.cpu[CPU_TYPE_L].name, PROF_DEV_NAME_SIZE, "cpu_L");
cx1 = snprintf(cpc.cpu[CPU_TYPE_B].name, PROF_DEV_NAME_SIZE, "cpu_B");
cx2 = snprintf(cpc.cluster.name, PROF_DEV_NAME_SIZE, "cluster");
cx3 = snprintf(cpc.mcusys.name, PROF_DEV_NAME_SIZE, "mcusys");
if (cx0 < 0 || cx1 < 0 || cx2 < 0 || cx3 < 0)
return -1;
mtk_lpm_notifier_register(&mtk_cpc_nb);
return 0;
}
void __exit mtk_cpc_exit(void)
{
mtk_lpm_notifier_unregister(&mtk_cpc_nb);
}
@@ -0,0 +1,100 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#ifndef __MTK_CPUIDLE_CPC_H__
#define __MTK_CPUIDLE_CPC_H__
#include <linux/seq_file.h>
#include <mtk_lpm_module.h>
enum {
CPU_TYPE_L,
CPU_TYPE_B,
NF_CPU_TYPE
};
enum {
CPC_SMC_EVENT_DUMP_TRACE_DATA,
CPC_SMC_EVENT_GIC_DPG_SET,
CPC_SMC_EVENT_CPC_CONFIG,
CPC_SMC_EVENT_READ_CONFIG,
NF_CPC_SMC_EVENT
};
enum {
CPC_SMC_CONFIG_PROF,
CPC_SMC_CONFIG_AUTO_OFF,
CPC_SMC_CONFIG_AUTO_OFF_THRES,
CPC_SMC_CONFIG_CNT_CLR,
CPC_SMC_CONFIG_TIME_SYNC,
NF_CPC_SMC_CONFIG
};
/**
* number of profile type :
* - (number of cpu type) + cluster + mcusys
*/
#define DEV_TYPE_NUM (NF_CPU_TYPE + 2)
/* smc */
#define mtk_cpc_smc(act, arg1, arg2)\
mtk_lpm_smc_cpu_pm(CPC_COMMAND, act, arg1, arg2)
/* configuration */
#define cpc_prof_en()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_PROF, 1)
#define cpc_prof_dis()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_PROF, 0)
#define cpc_auto_off_en()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF, 1)
#define cpc_auto_off_dis()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF, 0)
#define cpc_set_auto_off_thres(us)\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF_THRES, us)
#define cpc_cluster_cnt_clr()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_CNT_CLR, 0)
#define cpc_time_sync()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_TIME_SYNC, 0)
/* status */
#define cpc_get_prof_sta()\
mtk_cpc_smc(CPC_SMC_EVENT_READ_CONFIG\
, CPC_SMC_CONFIG_PROF, 0)
#define cpc_get_auto_off_sta()\
mtk_cpc_smc(CPC_SMC_EVENT_READ_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF, 0)
#define cpc_get_auto_off_thres()\
mtk_cpc_smc(CPC_SMC_EVENT_READ_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF_THRES, 0)
#define cpc_tick_to_us(val) ((val) / 13)
void mtk_cpc_prof_start(void);
void mtk_cpc_prof_stop(void);
void mtk_cpc_prof_lat_dump(struct seq_file *m);
int __init mtk_cpc_init(void);
void __exit mtk_cpc_exit(void);
#endif /* __MTK_CPUIDLE_CPC_H__ */
@@ -0,0 +1,662 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/cpuidle.h>
#include <linux/sched/clock.h>
#include <linux/cpu.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/pm_qos.h>
#include <linux/tick.h>
#include <linux/timer.h>
//#include <mtk_ram_console.h>
#include <mtk_lpm.h>
#include <mtk_lp_plat_reg.h>
#include <mt6781_spm_comm.h>
#include "mtk_cpupm_dbg.h"
#include "mtk_cpuidle_status.h"
#include "mtk_cpuidle_cpc.h"
#define DUMP_INTERVAL sec_to_ns(5)
static u64 last_dump_ns;
static unsigned long long mtk_lpm_last_cpuidle_dis;
/* stress test */
static unsigned int timer_interval = 10 * 1000;
static struct task_struct *stress_tsk[NR_CPUS];
/* CPU off state : core, cluster and mcusys */
#define CPU_OFF_MAX_LV (3)
/* all core idle profiling */
struct all_cpu_idle_data {
unsigned long long last_ns;
unsigned long long dur_ns;
unsigned int cnt;
unsigned int bp; /* basis points : 0.01% */
};
struct all_cpu_idle {
unsigned long long start_us;
struct all_cpu_idle_data lv[CPU_OFF_MAX_LV];
};
static struct all_cpu_idle all_core_off;
/* idle ratio profiling */
struct mtk_cpuidle_ratio {
unsigned long long start_us;
unsigned long long idle_time_ns[CPUIDLE_STATE_MAX];
unsigned int bp[CPUIDLE_STATE_MAX];
};
/* idle latency profiling */
struct mtk_cpuidle_prof {
unsigned int cnt;
unsigned int max;
union {
u64 avg;
u64 sum;
};
};
/* idle information */
struct mtk_cpuidle_info {
u64 enter_time_ns;
int idle_index;
int cnt[CPUIDLE_STATE_MAX];
};
/* mtk cpu idle status */
struct mtk_cpuidle_device {
struct mtk_cpuidle_ratio ratio;
struct mtk_cpuidle_info info;
struct mtk_cpuidle_prof prof;
struct hrtimer timer;
int cpu;
int state_count;
bool tmr_running;
};
static DEFINE_PER_CPU(struct mtk_cpuidle_device, mtk_cpuidle_dev);
/* mtk cpu idle configuration */
struct mtk_cpuidle_control {
bool tmr_en;
bool prof_en;
bool log_en;
bool stress_en;
};
static struct mtk_cpuidle_control mtk_cpuidle_ctrl;
static int mtk_cpuidle_stress_task(void *arg)
{
while (mtk_cpuidle_ctrl.stress_en)
usleep_range(timer_interval - 10, timer_interval + 10);
return 0;
}
static void mtk_cpuidle_stress_start(void)
{
int i;
char name[20] = {0};
int ret = 0;
if (mtk_cpuidle_ctrl.stress_en)
return;
mtk_cpuidle_ctrl.stress_en = true;
for_each_online_cpu(i) {
ret = scnprintf(name, sizeof(name), "mtk_cpupm_stress_%d", i);
stress_tsk[i] =
kthread_create(mtk_cpuidle_stress_task, NULL, name);
if (!IS_ERR(stress_tsk[i])) {
kthread_bind(stress_tsk[i], i);
wake_up_process(stress_tsk[i]);
}
}
}
static void mtk_cpuidle_stress_stop(void)
{
mtk_cpuidle_ctrl.stress_en = false;
msleep(20);
}
void mtk_cpuidle_set_stress_test(bool en)
{
if (en)
mtk_cpuidle_stress_start();
else
mtk_cpuidle_stress_stop();
}
bool mtk_cpuidle_get_stress_status(void)
{
return mtk_cpuidle_ctrl.stress_en;
}
void mtk_cpuidle_set_stress_time(unsigned int val)
{
timer_interval = clamp_val(val, 100, 20000);
}
unsigned int mtk_cpuidle_get_stress_time(void)
{
return timer_interval;
}
void mtk_cpuidle_ctrl_timer_en(bool enable)
{
mtk_cpupm_block();
mtk_cpuidle_ctrl.tmr_en = enable;
mtk_cpupm_allow();
}
bool mtk_cpuidle_ctrl_timer_sta_get(void)
{
return mtk_cpuidle_ctrl.tmr_en;
}
void mtk_cpuidle_ctrl_log_en(bool enable)
{
mtk_cpupm_block();
mtk_cpuidle_ctrl.log_en = enable;
mtk_cpupm_allow();
}
bool mtk_cpuidle_ctrl_log_sta_get(void)
{
return mtk_cpuidle_ctrl.log_en;
}
static void _mtk_cpuidle_prof_ratio_start(void *data)
{
int i, cpu;
struct mtk_cpuidle_device *mtk_idle;
cpu = get_cpu();
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
put_cpu();
if (unlikely(!mtk_idle))
return;
mtk_idle->ratio.start_us = div64_u64(sched_clock(), 1000);
for (i = 0; i < mtk_idle->state_count; i++)
mtk_idle->ratio.idle_time_ns[i] = 0;
}
void mtk_cpuidle_prof_ratio_start(void)
{
int i;
if (mtk_cpuidle_ctrl.prof_en)
return;
mtk_cpupm_block();
for (i = 0; i < CPU_OFF_MAX_LV; i++) {
all_core_off.lv[i].dur_ns = 0;
all_core_off.lv[i].cnt = 0;
}
all_core_off.start_us = div64_u64(sched_clock(), 1000);
on_each_cpu(_mtk_cpuidle_prof_ratio_start, NULL, 1);
mtk_cpuidle_ctrl.prof_en = true;
mtk_cpupm_allow();
}
static void _mtk_cpuidle_prof_ratio_stop(void *stop_time)
{
int i, cpu;
uint64_t dur_us;
struct mtk_cpuidle_device *mtk_idle;
cpu = get_cpu();
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
put_cpu();
if (unlikely(!mtk_idle))
return;
dur_us = div64_u64(sched_clock(), 1000) -
mtk_idle->ratio.start_us;
for (i = 0; i < mtk_idle->state_count; i++)
mtk_idle->ratio.bp[i] = (unsigned int)div64_u64(
mtk_idle->ratio.idle_time_ns[i] * 10,
dur_us);
}
void mtk_cpuidle_prof_ratio_stop(void)
{
int i;
uint64_t time_us;
if (!mtk_cpuidle_ctrl.prof_en)
return;
mtk_cpupm_block();
on_each_cpu(_mtk_cpuidle_prof_ratio_stop, NULL, 1);
mtk_cpuidle_ctrl.prof_en = false;
time_us = div64_u64(sched_clock(), 1000) - all_core_off.start_us;
for (i = 0; i < CPU_OFF_MAX_LV; i++)
all_core_off.lv[i].bp = (unsigned int)div64_u64(
all_core_off.lv[i].dur_ns * 10, time_us);
mtk_cpupm_allow();
}
void mtk_cpuidle_prof_ratio_dump(struct seq_file *m)
{
int i, cpu;
struct mtk_cpuidle_device *mtk_idle;
seq_printf(m, "%6s%12s%12s%12s\n",
"",
"cpu_off",
"cluster_off",
"mcusys_off");
for_each_possible_cpu(cpu) {
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
if (!mtk_idle)
continue;
seq_printf(m, "cpu%d: ", cpu);
for (i = 1; i < mtk_idle->state_count; i++) {
seq_printf(m, "%5s%3u.%02u%%",
"",
mtk_idle->ratio.bp[i] / 100,
mtk_idle->ratio.bp[i] % 100);
}
seq_puts(m, "\n");
}
seq_puts(m, "All core off ratio :\n");
for (i = 0; i < CPU_OFF_MAX_LV; i++)
seq_printf(m, "\t C_%d = %3u.%02u%%\n",
i + 1,
all_core_off.lv[i].bp / 100,
all_core_off.lv[i].bp % 100);
}
void mtk_cpuidle_state_enable(bool en)
{
struct cpuidle_driver *drv;
int i, cpu;
int suspend_type = mtk_lpm_suspend_type_get();
mtk_cpupm_block();
for_each_possible_cpu(cpu) {
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
for (i = drv->state_count - 1; i > 0; i--) {
if ((suspend_type == MTK_LPM_SUSPEND_S2IDLE) &&
!strcmp(drv->states[i].name, S2IDLE_STATE_NAME))
continue;
mtk_cpuidle_set_param(drv, i, IDLE_PARAM_EN, en);
}
}
if (!en)
mtk_lpm_last_cpuidle_dis = sched_clock();
mtk_cpupm_allow();
}
void mtk_s2idle_state_enable(bool en)
{
struct cpuidle_driver *drv;
int i, cpu;
int suspend_type = mtk_lpm_suspend_type_get();
mtk_cpupm_block();
for_each_possible_cpu(cpu) {
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
i = drv->state_count - 1;
if ((suspend_type == MTK_LPM_SUSPEND_S2IDLE) &&
!strcmp(drv->states[i].name, S2IDLE_STATE_NAME)) {
mtk_cpuidle_set_param(drv, i, IDLE_PARAM_EN, en);
}
}
if (!en)
mtk_lpm_last_cpuidle_dis = sched_clock();
mtk_cpupm_allow();
}
unsigned long long mtk_cpuidle_state_last_dis_ms(void)
{
return (mtk_lpm_last_cpuidle_dis / 1000000);
}
static bool mtk_cpuidle_need_dump(unsigned int idx)
{
u64 curr_ns;
bool dump = false;
if (!mtk_cpuidle_ctrl.log_en || !idx)
return false;
curr_ns = sched_clock();
if (curr_ns - last_dump_ns > DUMP_INTERVAL) {
last_dump_ns = curr_ns;
dump = true;
}
return dump;
}
static unsigned int mtk_cpuidle_get_cluster_off_cnt(void)
{
unsigned int cnt = mtk_cpupm_mcusys_read(CPC_DORMANT_COUNTER);
/**
* Cluster off count
* bit[0:15] : memory retention
* bit[16:31] : memory off
*/
if ((cnt & 0x7FFF) == 0)
cnt = ((cnt >> 16) & 0x7FFF);
else
cnt = cnt & 0x7FFF;
cnt += mtk_cpupm_syssram_read(SYSRAM_CPC_CPUSYS_CNT_BACKUP);
cpc_cluster_cnt_clr();
mtk_cpupm_syssram_write(SYSRAM_CPC_CPUSYS_CNT_BACKUP, 0);
/**
* Add mcusys off count because CPC cluster counter will be cleared
* when mcusys power off.
*/
cnt += mtk_cpupm_syssram_read(SYSRAM_MCUPM_MCUSYS_COUNTER);
mtk_cpupm_syssram_write(SYSRAM_CPUSYS_CNT,
mtk_cpupm_syssram_read(SYSRAM_CPUSYS_CNT) + cnt);
return cnt;
}
static unsigned int mtk_cpuidle_get_mcusys_off_cnt(void)
{
unsigned int cnt = mtk_cpupm_syssram_read(SYSRAM_MCUPM_MCUSYS_COUNTER);
mtk_cpupm_syssram_write(SYSRAM_MCUPM_MCUSYS_COUNTER, 0);
mtk_cpupm_syssram_write(SYSRAM_MCUSYS_CNT,
mtk_cpupm_syssram_read(SYSRAM_MCUSYS_CNT) + cnt);
return cnt;
}
static void mtk_cpuidle_dump_info(void)
{
struct mtk_cpuidle_device *mtk_idle;
int cpu, idx, ofs;
unsigned int avail_cpu_mask = 0;
for_each_possible_cpu(cpu) {
if (cpu_online(cpu))
avail_cpu_mask |= (1 << cpu);
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
if (!mtk_idle)
continue;
ofs = SYSRAM_RECENT_CPU_CNT(cpu);
mtk_cpupm_syssram_write(ofs, 0);
for (idx = 1; idx < mtk_idle->state_count ; idx++) {
mtk_cpupm_syssram_write(ofs, mtk_cpupm_syssram_read(ofs)
+ mtk_idle->info.cnt[idx]);
mtk_idle->info.cnt[idx] = 0;
}
}
/* Should calculate cluster off count before mcusys counter reset */
mtk_cpupm_syssram_write(SYSRAM_RECENT_CPUSYS_CNT,
mtk_cpuidle_get_cluster_off_cnt());
mtk_cpupm_syssram_write(SYSRAM_RECENT_MCUSYS_CNT,
mtk_cpuidle_get_mcusys_off_cnt());
mtk_cpupm_syssram_write(SYSRAM_CPU_ONLINE, avail_cpu_mask);
mtk_cpupm_syssram_write(SYSRAM_RECENT_CNT_TS_H,
(unsigned int)((last_dump_ns >> 32) & 0xFFFFFFFF));
mtk_cpupm_syssram_write(SYSRAM_RECENT_CNT_TS_L,
(unsigned int)(last_dump_ns & 0xFFFFFFFF));
}
static enum hrtimer_restart mtk_cpuidle_hrtimer_func(struct hrtimer *timer)
{
return HRTIMER_NORESTART;
}
static void mtk_cpuidle_set_timer(struct mtk_cpuidle_device *mtk_idle)
{
int index;
int limit_sleep_us;
struct cpuidle_driver *drv;
if (!mtk_cpuidle_ctrl.tmr_en)
return;
index = mtk_idle->info.idle_index;
/* Only support WFI/CPU_OFF state */
if (index < 0 || index > 1)
return;
drv = cpuidle_get_driver();
if (unlikely(!drv))
return;
if (index + 1 >= mtk_idle->state_count
|| !tick_nohz_tick_stopped())
return;
limit_sleep_us = 4 * max_t(unsigned int,
get_residency(drv, index),
get_residency(drv, index + 1));
if (index != 0)
tick_broadcast_exit();
RCU_NONIDLE(hrtimer_start(&mtk_idle->timer,
ns_to_ktime(limit_sleep_us * NSEC_PER_USEC),
HRTIMER_MODE_REL_PINNED));
mtk_idle->tmr_running = true;
if (index != 0)
tick_broadcast_enter();
}
static void mtk_cpuidle_cancel_timer(struct mtk_cpuidle_device *mtk_idle)
{
if (!mtk_cpuidle_ctrl.tmr_en)
return;
if (mtk_idle->tmr_running) {
mtk_idle->tmr_running = true;
RCU_NONIDLE(hrtimer_try_to_cancel(&mtk_idle->timer));
}
}
static void mtk_cpuidle_set_last_off_ts(int idx)
{
int i;
uint64_t curr_ns = sched_clock();
if (!idx)
return;
if (idx > CPU_OFF_MAX_LV)
idx = CPU_OFF_MAX_LV;
for (i = 0; i < idx; i++) {
all_core_off.lv[i].cnt++;
if (all_core_off.lv[i].cnt == num_online_cpus())
all_core_off.lv[i].last_ns = curr_ns;
}
}
static void mtk_cpuidle_save_idle_time(int idx)
{
int i;
uint64_t curr_ns = sched_clock();
if (!idx)
return;
if (idx > CPU_OFF_MAX_LV)
idx = CPU_OFF_MAX_LV;
for (i = 0; i < idx; i++) {
if (all_core_off.lv[i].cnt == num_online_cpus())
all_core_off.lv[i].dur_ns += (curr_ns
- all_core_off.lv[i].last_ns);
all_core_off.lv[i].cnt--;
}
}
static int mtk_cpuidle_status_update(struct notifier_block *nb,
unsigned long action, void *data)
{
struct mtk_cpuidle_device *mtk_idle;
struct mtk_lpm_nb_data *nb_data = (struct mtk_lpm_nb_data *)data;
if (action & MTK_LPM_NB_BEFORE_REFLECT) {
/* prevent race conditions by mtk_lp_mod_locker */
if (mtk_cpuidle_ctrl.prof_en)
mtk_cpuidle_save_idle_time(nb_data->index);
if (mtk_cpuidle_need_dump(nb_data->index))
mtk_cpuidle_dump_info();
} else if (action & MTK_LPM_NB_RESUME) {
//aee_rr_rec_mcdi_val(nb_data->cpu,(nb_data->index << 16) | 0x0);
mtk_idle = &per_cpu(mtk_cpuidle_dev, nb_data->cpu);
mtk_idle->info.idle_index = -1;
mtk_idle->info.cnt[nb_data->index]++;
if (mtk_cpuidle_ctrl.prof_en)
mtk_idle->ratio.idle_time_ns[nb_data->index] +=
(sched_clock() - mtk_idle->info.enter_time_ns);
mtk_cpuidle_cancel_timer(mtk_idle);
} else if (action & MTK_LPM_NB_AFTER_PROMPT) {
/* prevent race conditions by mtk_lp_mod_locker */
if (mtk_cpuidle_ctrl.prof_en)
mtk_cpuidle_set_last_off_ts(nb_data->index);
} else if (action & MTK_LPM_NB_PREPARE) {
mtk_idle = &per_cpu(mtk_cpuidle_dev, nb_data->cpu);
mtk_idle->info.idle_index = nb_data->index;
mtk_idle->info.enter_time_ns = sched_clock();
mtk_cpuidle_set_timer(mtk_idle);
//aee_rr_rec_mcdi_val(nb_data->cpu,(nb_data->index << 16) | 0xff);
}
return NOTIFY_OK;
}
struct notifier_block mtk_cpuidle_status_nb = {
.notifier_call = mtk_cpuidle_status_update,
};
static void mtk_cpuidle_init_per_cpu(void *info)
{
struct mtk_cpuidle_device *mtk_idle;
int cpu;
cpu = get_cpu();
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
put_cpu();
hrtimer_init(&mtk_idle->timer,
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
mtk_idle->cpu = cpu;
mtk_idle->timer.function = mtk_cpuidle_hrtimer_func;
mtk_idle->state_count = mtk_cpupm_get_idle_state_count(cpu);
}
int __init mtk_cpuidle_status_init(void)
{
mtk_cpuidle_ctrl.tmr_en = true;
mtk_cpuidle_ctrl.prof_en = false;
mtk_cpuidle_ctrl.log_en = true;
mtk_cpuidle_ctrl.stress_en = false;
mtk_cpupm_block();
on_each_cpu(mtk_cpuidle_init_per_cpu, NULL, 0);
mtk_cpupm_allow();
mtk_lpm_notifier_register(&mtk_cpuidle_status_nb);
return 0;
}
void __exit mtk_cpuidle_status_exit(void)
{
mtk_lpm_notifier_unregister(&mtk_cpuidle_status_nb);
}
@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#ifndef __MTK_CPUIDLE_STATUS_H__
#define __MTK_CPUIDLE_STATUS_H__
#include <linux/seq_file.h>
#define sec_to_ns(v) ((v) * 1000 * 1000 * 1000ULL)
#define S2IDLE_STATE_NAME "s2idle"
enum {
IDLE_PARAM_LAT,
IDLE_PARAM_RES,
IDLE_PARAM_EN,
NF_IDLE_PARAM
};
#define get_residency(drv, idx)\
((drv)->states[idx].target_residency)
#define get_latency(drv, idx)\
((drv)->states[idx].exit_latency)
#define get_disabled(drv, idx)\
((drv)->states[idx].disabled)
#define mtk_cpuidle_get_param(drv, state, param) \
({ \
int val = -1; \
if (param == IDLE_PARAM_LAT) \
val = (int)get_latency(drv, state); \
else if (param == IDLE_PARAM_RES) \
val = (int)get_residency(drv, state); \
else if (param == IDLE_PARAM_EN) \
val = get_disabled(drv, state) ? 0 : 1 ; \
val; \
})
#define mtk_cpuidle_set_param(drv, state, param, val) \
do { \
if (param == IDLE_PARAM_LAT) \
get_latency(drv, state) = val; \
else if (param == IDLE_PARAM_RES) \
get_residency(drv, state) = val; \
else if (param == IDLE_PARAM_EN) \
get_disabled(drv, state) = !val ; \
} while (0)
void mtk_cpuidle_set_stress_test(bool en);
bool mtk_cpuidle_get_stress_status(void);
void mtk_cpuidle_set_stress_time(unsigned int val);
unsigned int mtk_cpuidle_get_stress_time(void);
void mtk_cpuidle_prof_ratio_start(void);
void mtk_cpuidle_prof_ratio_stop(void);
void mtk_cpuidle_prof_ratio_dump(struct seq_file *m);
void mtk_cpuidle_ctrl_timer_en(bool enable);
bool mtk_cpuidle_ctrl_timer_sta_get(void);
void mtk_cpuidle_ctrl_log_en(bool enable);
bool mtk_cpuidle_ctrl_log_sta_get(void);
void mtk_cpuidle_state_enable(bool en);
void mtk_s2idle_state_enable(bool en);
int __init mtk_cpuidle_status_init(void);
void __exit mtk_cpuidle_status_exit(void);
unsigned long long mtk_cpuidle_state_last_dis_ms(void);
#endif /* __MTK_CPUIDLE_STATUS_H__ */
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cpuidle.h>
#include <linux/pm_qos.h>
#include "mtk_cpuidle_status.h"
#include "mtk_cpuidle_cpc.h"
#include "mtk_idle_procfs.h"
static void __iomem *cpupm_mcusys_base_addr;
static void __iomem *cpupm_syssram_base_addr;
static struct pm_qos_request cpuidle_dbg_qos_req;
int mtk_cpupm_mcusys_write(int ofs, unsigned int val)
{
if (!cpupm_mcusys_base_addr)
return -EADDRNOTAVAIL;
if ((ofs % 4) != 0)
return -EINVAL;
__raw_writel(val, cpupm_mcusys_base_addr + ofs);
mb(); /* make sure register access in order */
return 0;
}
unsigned int mtk_cpupm_mcusys_read(int ofs)
{
if (!cpupm_mcusys_base_addr)
return 0;
if ((ofs % 4) != 0)
return 0;
return __raw_readl(cpupm_mcusys_base_addr + ofs);
}
int mtk_cpupm_syssram_write(int ofs, unsigned int val)
{
if (!cpupm_syssram_base_addr)
return -EADDRNOTAVAIL;
if ((ofs % 4) != 0)
return -EINVAL;
__raw_writel(val, cpupm_syssram_base_addr + ofs);
mb(); /* make sure register access in order */
return 0;
}
unsigned int mtk_cpupm_syssram_read(int ofs)
{
if (!cpupm_syssram_base_addr)
return 0;
if ((ofs % 4) != 0)
return 0;
return __raw_readl(cpupm_syssram_base_addr + ofs);
}
void mtk_cpupm_block(void)
{
pm_qos_update_request(&cpuidle_dbg_qos_req, 2);
}
void mtk_cpupm_allow(void)
{
pm_qos_update_request(&cpuidle_dbg_qos_req, PM_QOS_DEFAULT_VALUE);
}
int mtk_cpupm_get_idle_state_count(int cpu)
{
struct device_node *state_node, *cpu_node;
int i, state_count, start_idx = 1;
cpu_node = of_cpu_device_node_get(cpu);
if (!cpu_node)
return start_idx;
for (i = 0; ; i++) {
state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
if (!state_node)
break;
of_node_put(state_node);
}
of_node_put(cpu_node);
state_count = i + start_idx;
if (state_count > CPUIDLE_STATE_MAX)
state_count = CPUIDLE_STATE_MAX;
return state_count;
}
static void __init mtk_cpupm_node_init(void)
{
struct device_node *node = NULL;
cpupm_mcusys_base_addr = NULL;
cpupm_syssram_base_addr = NULL;
node = of_find_compatible_node(NULL, NULL,
"mediatek,mcusys-ctrl");
if (node) {
cpupm_mcusys_base_addr = of_iomap(node, 0);
of_node_put(node);
}
node = of_find_compatible_node(NULL, NULL,
"mediatek,cpupm-sysram");
if (node) {
cpupm_syssram_base_addr = of_iomap(node, 0);
of_node_put(node);
}
}
static int __init mtk_cpupm_dbg_init(void)
{
pm_qos_add_request(&cpuidle_dbg_qos_req,
PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
mtk_cpupm_node_init();
mtk_cpuidle_status_init();
mtk_cpc_init();
mtk_idle_procfs_init();
cpc_time_sync();
return 0;
}
static void __exit mtk_cpupm_dbg_exit(void)
{
mtk_idle_procfs_exit();
mtk_cpuidle_status_exit();
mtk_cpc_exit();
pm_qos_remove_request(&cpuidle_dbg_qos_req);
}
module_init(mtk_cpupm_dbg_init);
module_exit(mtk_cpupm_dbg_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MediaTek CPU Idle Debug FileSystem");
MODULE_AUTHOR("MediaTek Inc.");
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#ifndef __MTK_CPUPM_DBG_H__
#define __MTK_CPUPM_DBG_H__
int mtk_cpupm_mcusys_write(int ofs, unsigned int val);
unsigned int mtk_cpupm_mcusys_read(int ofs);
int mtk_cpupm_syssram_write(int ofs, unsigned int val);
unsigned int mtk_cpupm_syssram_read(int ofs);
void mtk_cpupm_block(void);
void mtk_cpupm_allow(void);
int mtk_cpupm_get_idle_state_count(int cpu);
#endif /* __MTK_CPUPM_DBG_H__ */
@@ -0,0 +1,270 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
/* This file is generated by GenLP_setting.pl v1.5.7 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
const unsigned int AP_DCM_Golden_Setting_tcl_gs_dpidle_data[] = {
/* Address Mask Golden Setting Value */
0x0C53A2A0, 0x03000800, 0x03000800,/* CPU_PLLDIV_CFG0 */
0x0C53A2A4, 0x03000800, 0x03000800,/* CPU_PLLDIV_CFG1 */
0x0C53A2E0, 0x83000800, 0x83000800,/* BUS_PLLDIV_CFG */
0x0C53A418, 0x00000001, 0x00000000,/* MCSI_CFG2 */
0x0C53A440, 0x0000FFFF, 0x0000FFFF,/* MCSIC_DCM0 */
0x0C53A510, 0x00270000, 0x00270000,/* MP_ADB_DCM_CFG4 */
0x0C53A518, 0x0000003F, 0x0000003F,/* MP_MISC_DCM_CFG0 */
0x0C53A5C0, 0x00070100, 0x00070100,/* MCUSYS_DCM_CFG0 */
0x0C53A900, 0x00000005, 0x00000005,/* EMI_WFIFO */
0x0C53C880, 0x0001000F, 0x0001000F,/* MP0_DCM_CFG0 */
0x0C53C89C, 0x00000011, 0x00000011,/* MP0_DCM_CFG7 */
0x0D0A007C, 0x00000002, 0x00000000,/* dbg_mode */
0x10001070, 0x40F07C03, 0x40F00003,/* INFRA_BUS_DCM_CTRL */
0x10001074, 0x30107C03, 0x30100003,/* PERI_BUS_DCM_CTRL */
0x10001078, 0xBFFF01C1, 0x0BE00180,/* MEM_DCM_CTRL */
0x100010A0, 0x0000000F, 0x00000000,/* P2P_RX_CLK_ON */
0x1000A000, 0x80000000, 0x80000000,/* SEJ_CON */
0x1000D1EC, 0x007FFFFF, 0x00000000,/* DCM_EN */
0x10015008, 0x00000001, 0x00000001,/* DCM_CTRL */
0x10015010, 0xFFFFFFFF, 0xFFFFFFFF,/* INF_CK_DCM_EN */
0x10015014, 0x0000007F, 0x0000007F,/* OTHER_CK_DCM_EN */
0x1001A208, 0x0000FFFF, 0x0000FFFF,/* DXCC_NEW_HWDCM_CFG */
0x10219060, 0xFF000000, 0x00000000,/* EMI_CONM */
0x10219068, 0xFF000000, 0x00000000,/* EMI_CONN */
0x10219830, 0x00002000, 0x00000000,/* EMI_THRO_CTRL0 */
0x10220038, 0xC4000007, 0xC0000007,/* DRAMC_PD_CTRL */
0x1022003C, 0x80000000, 0x80000000,/* CLKAR */
0x10225008, 0xFF000000, 0x00000000,/* CHN_EMI_CONB */
0x10228284, 0x000BFF00, 0x00000000,/* MISC_CG_CTRL0 */
0x1022828C, 0x07E000C0, 0x01000000,/* MISC_CG_CTRL2 */
0x102282A4, 0x00000003, 0x00000000,/* MISC_CTRL2 */
0x1022C0F0, 0x0000FFFF, 0x0000FFFF,/* GCE_CTL_INT0 */
0x10230038, 0xC4000007, 0xC0000007,/* DRAMC_PD_CTRL */
0x1023003C, 0x80000000, 0x80000000,/* CLKAR */
0x10238284, 0x000BFF00, 0x00000000,/* MISC_CG_CTRL0 */
0x1023828C, 0x07E000C0, 0x01000000,/* MISC_CG_CTRL2 */
0x102382A4, 0x00000003, 0x00000000,/* MISC_CTRL2 */
0x10443004, 0x00000100, 0x00000100,/* SSPM_MCLK_DIV */
0x10443008, 0x001FFFFF, 0x001FBFFF,/* SSPM_DCM_CTRL */
0x105C5F88, 0x00000007, 0x00000007,/* I3C0_CHN_HW_CG_EN */
0x105C6F88, 0x00000007, 0x00000007,/* I2C1_CHN_HW_CG_EN */
0x11008F88, 0x00000007, 0x00000007,/* I2C1_CHN_HW_CG_EN */
0x1100DF88, 0x00000007, 0x00000007,/* I2C6_CHN_HW_CG_EN */
0x1100FF88, 0x00000007, 0x00000007,/* I2C3_CHN_HW_CG_EN */
0x11016F88, 0x00000007, 0x00000007,/* I2C5_CHN_HW_CG_EN */
0x11200700, 0x000F0000, 0x00000000,/* RESREG */
0x11210000, 0x60000000, 0x60000000,/* AUDIO_TOP_CON0 */
0x112300B4, 0xFFA00000, 0x00000000,/* PATCH_BIT1 */
0x112400B4, 0xFFA00000, 0x00000000,/* PATCH_BIT1 */
0x13FBF010, 0x0003FF7F, 0x0000C03F,/* MFG_DCM_CON_0 */
0x13FBF024, 0x00000001, 0x00000001,/* MFG_ASYNC_CON_1 */
0x13FBF0B0, 0x00000700, 0x00000000,/* MFG_GLOBAL_CON */
0x13FBF0B4, 0x00000010, 0x00000010,/* MFG_QCHANNEL_CON */
0x14000120, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS0 */
0x14000130, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS1 */
0x14000140, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS0 */
0x14000150, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS1 */
0x140001B0, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS2 */
0x140001C0, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS2 */
0x14002300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x14003014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x14004014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1400E1F0, 0xFFFFFFFF, 0x00000000,/* DISP_POSTMASK_FUNC_DCM0 */
0x1400E1F4, 0xFFFFFFFF, 0x00000000,/* DISP_POSTMASK_FUNC_DCM1 */
0x14016050, 0x007FFFFF, 0x00000000,/* MMU_DCM_DIS */
0x1401B300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1401C300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1401E300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1502E014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1502F300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1582E014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1602E014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1602F018, 0x00000001, 0x00000000,/* VDEC_DCM_CON */
0x1602F218, 0x00000001, 0x00000000,/* LAT_DCM_CON */
0x17010014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x17030300, 0x00000001, 0x00000000,/* JPGENC_DCM_CTRL */
0x1A001014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1A002014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1A00C300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1A00D300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1A00F014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1A010014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1B000150, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS0 */
0x1B000160, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS1 */
0x1B000170, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS2 */
0x1B000180, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS3 */
0x1B000190, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS4 */
0x1B0001A0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS0 */
0x1B0001B0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS1 */
0x1B0001C0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS2 */
0x1B0001D0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS3 */
0x1B0001E0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS4 */
0x1B002014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1C00E300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1C00F014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1C10F014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
};
const unsigned int *AP_DCM_Golden_Setting_tcl_gs_dpidle =
AP_DCM_Golden_Setting_tcl_gs_dpidle_data;
unsigned int AP_DCM_Golden_Setting_tcl_gs_dpidle_len = 288;
const unsigned int AP_DCM_Golden_Setting_tcl_gs_suspend_data[] = {
/* Address Mask Golden Setting Value */
0x0C53A2A0, 0x03000800, 0x03000800,/* CPU_PLLDIV_CFG0 */
0x0C53A2A4, 0x03000800, 0x03000800,/* CPU_PLLDIV_CFG1 */
0x0C53A2E0, 0x83000800, 0x83000800,/* BUS_PLLDIV_CFG */
0x0C53A418, 0x00000001, 0x00000000,/* MCSI_CFG2 */
0x0C53A440, 0x0000FFFF, 0x0000FFFF,/* MCSIC_DCM0 */
0x0C53A510, 0x00270000, 0x00270000,/* MP_ADB_DCM_CFG4 */
0x0C53A518, 0x0000003F, 0x0000003F,/* MP_MISC_DCM_CFG0 */
0x0C53A5C0, 0x00070100, 0x00070100,/* MCUSYS_DCM_CFG0 */
0x0C53A900, 0x00000005, 0x00000005,/* EMI_WFIFO */
0x0C53C880, 0x0001000F, 0x0001000F,/* MP0_DCM_CFG0 */
0x0C53C89C, 0x00000011, 0x00000011,/* MP0_DCM_CFG7 */
0x0D0A007C, 0x00000002, 0x00000000,/* dbg_mode */
0x10001070, 0x40F07C03, 0x40F00003,/* INFRA_BUS_DCM_CTRL */
0x10001074, 0x30107C03, 0x30100003,/* PERI_BUS_DCM_CTRL */
0x10001078, 0xBFFF01C1, 0x0BE00180,/* MEM_DCM_CTRL */
0x100010A0, 0x0000000F, 0x00000000,/* P2P_RX_CLK_ON */
0x1000A000, 0x80000000, 0x80000000,/* SEJ_CON */
0x1000D1EC, 0x007FFFFF, 0x00000000,/* DCM_EN */
0x10015008, 0x00000001, 0x00000001,/* DCM_CTRL */
0x10015010, 0xFFFFFFFF, 0xFFFFFFFF,/* INF_CK_DCM_EN */
0x10015014, 0x0000007F, 0x0000007F,/* OTHER_CK_DCM_EN */
0x1001A208, 0x0000FFFF, 0x0000FFFF,/* DXCC_NEW_HWDCM_CFG */
0x10219060, 0xFF000000, 0x00000000,/* EMI_CONM */
0x10219068, 0xFF000000, 0x00000000,/* EMI_CONN */
0x10219830, 0x00002000, 0x00000000,/* EMI_THRO_CTRL0 */
0x10220038, 0xC4000007, 0xC0000007,/* DRAMC_PD_CTRL */
0x1022003C, 0x80000000, 0x80000000,/* CLKAR */
0x10225008, 0xFF000000, 0x00000000,/* CHN_EMI_CONB */
0x10228284, 0x000BFF00, 0x00000000,/* MISC_CG_CTRL0 */
0x1022828C, 0x07E000C0, 0x01000000,/* MISC_CG_CTRL2 */
0x102282A4, 0x00000003, 0x00000000,/* MISC_CTRL2 */
0x1022C0F0, 0x0000FFFF, 0x0000FFFF,/* GCE_CTL_INT0 */
0x10230038, 0xC4000007, 0xC0000007,/* DRAMC_PD_CTRL */
0x1023003C, 0x80000000, 0x80000000,/* CLKAR */
0x10238284, 0x000BFF00, 0x00000000,/* MISC_CG_CTRL0 */
0x1023828C, 0x07E000C0, 0x01000000,/* MISC_CG_CTRL2 */
0x102382A4, 0x00000003, 0x00000000,/* MISC_CTRL2 */
0x10443004, 0x00000100, 0x00000100,/* SSPM_MCLK_DIV */
0x10443008, 0x001FFFFF, 0x001FBFFF,/* SSPM_DCM_CTRL */
0x105C5F88, 0x00000007, 0x00000007,/* I3C0_CHN_HW_CG_EN */
0x105C6F88, 0x00000007, 0x00000007,/* I2C1_CHN_HW_CG_EN */
0x11008F88, 0x00000007, 0x00000007,/* I2C1_CHN_HW_CG_EN */
0x1100DF88, 0x00000007, 0x00000007,/* I2C6_CHN_HW_CG_EN */
0x1100FF88, 0x00000007, 0x00000007,/* I2C3_CHN_HW_CG_EN */
0x11016F88, 0x00000007, 0x00000007,/* I2C5_CHN_HW_CG_EN */
0x11200700, 0x000F0000, 0x00000000,/* RESREG */
0x11210000, 0x60000000, 0x60000000,/* AUDIO_TOP_CON0 */
0x112300B4, 0xFFA00000, 0x00000000,/* PATCH_BIT1 */
0x112400B4, 0xFFA00000, 0x00000000,/* PATCH_BIT1 */
0x13FBF010, 0x0003FF7F, 0x0000C03F,/* MFG_DCM_CON_0 */
0x13FBF024, 0x00000001, 0x00000001,/* MFG_ASYNC_CON_1 */
0x13FBF0B0, 0x00000700, 0x00000000,/* MFG_GLOBAL_CON */
0x13FBF0B4, 0x00000010, 0x00000010,/* MFG_QCHANNEL_CON */
0x14000120, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS0 */
0x14000130, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS1 */
0x14000140, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS0 */
0x14000150, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS1 */
0x140001B0, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS2 */
0x140001C0, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS2 */
0x14002300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x14003014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x14004014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1400E1F0, 0xFFFFFFFF, 0x00000000,/* DISP_POSTMASK_FUNC_DCM0 */
0x1400E1F4, 0xFFFFFFFF, 0x00000000,/* DISP_POSTMASK_FUNC_DCM1 */
0x14016050, 0x007FFFFF, 0x00000000,/* MMU_DCM_DIS */
0x1401B300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1401C300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1401E300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1502E014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1502F300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1582E014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1602E014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1602F018, 0x00000001, 0x00000000,/* VDEC_DCM_CON */
0x1602F218, 0x00000001, 0x00000000,/* LAT_DCM_CON */
0x17010014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x17030300, 0x00000001, 0x00000000,/* JPGENC_DCM_CTRL */
0x1A001014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1A002014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1A00C300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1A00D300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1A00F014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1A010014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1B000150, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS0 */
0x1B000160, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS1 */
0x1B000170, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS2 */
0x1B000180, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS3 */
0x1B000190, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS4 */
0x1B0001A0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS0 */
0x1B0001B0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS1 */
0x1B0001C0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS2 */
0x1B0001D0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS3 */
0x1B0001E0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS4 */
0x1B002014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1C00E300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1C00F014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1C10F014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
};
const unsigned int *AP_DCM_Golden_Setting_tcl_gs_suspend =
AP_DCM_Golden_Setting_tcl_gs_suspend_data;
unsigned int AP_DCM_Golden_Setting_tcl_gs_suspend_len = 288;
const unsigned int AP_DCM_Golden_Setting_tcl_gs_sodi_data[] = {
/* Address Mask Golden Setting Value */
0x0D0A007C, 0x00000002, 0x00000000,/* dbg_mode */
0x1000D1EC, 0x007FFFFF, 0x00000000,/* DCM_EN */
0x10015008, 0x00000001, 0x00000001,/* DCM_CTRL */
0x10015010, 0xFFFFFFFF, 0xFFFFFFFF,/* INF_CK_DCM_EN */
0x10015014, 0x0000007F, 0x0000007F,/* OTHER_CK_DCM_EN */
0x1001A208, 0x0000FFFF, 0x0000FFFF,/* DXCC_NEW_HWDCM_CFG */
0x10219060, 0xFF000000, 0x00000000,/* EMI_CONM */
0x10219068, 0xFF000000, 0x00000000,/* EMI_CONN */
0x10219830, 0x00002000, 0x00000000,/* EMI_THRO_CTRL0 */
0x10225008, 0xFF000000, 0x00000000,/* CHN_EMI_CONB */
0x10228284, 0x000BFF00, 0x00000000,/* MISC_CG_CTRL0 */
0x1022828C, 0x07E000C0, 0x01000000,/* MISC_CG_CTRL2 */
0x102282A4, 0x00000003, 0x00000000,/* MISC_CTRL2 */
0x10238284, 0x000BFF00, 0x00000000,/* MISC_CG_CTRL0 */
0x1023828C, 0x07E000C0, 0x01000000,/* MISC_CG_CTRL2 */
0x102382A4, 0x00000003, 0x00000000,/* MISC_CTRL2 */
0x112300B4, 0xFFA00000, 0x00000000,/* PATCH_BIT1 */
0x112400B4, 0xFFA00000, 0x00000000,/* PATCH_BIT1 */
0x14000120, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS0 */
0x14000130, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS1 */
0x14000140, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS0 */
0x14000150, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS1 */
0x140001B0, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS2 */
0x140001C0, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS2 */
0x1400E1F0, 0xFFFFFFFF, 0x00000000,/* DISP_POSTMASK_FUNC_DCM0 */
0x1400E1F4, 0xFFFFFFFF, 0x00000000,/* DISP_POSTMASK_FUNC_DCM1 */
0x14016050, 0x007FFFFF, 0x00000000,/* MMU_DCM_DIS */
0x1602F018, 0x00000001, 0x00000000,/* VDEC_DCM_CON */
0x1602F218, 0x00000001, 0x00000000,/* LAT_DCM_CON */
0x17030300, 0x00000001, 0x00000000,/* JPGENC_DCM_CTRL */
0x1B000150, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS0 */
0x1B000160, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS1 */
0x1B000170, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS2 */
0x1B000180, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS3 */
0x1B000190, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS4 */
0x1B0001A0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS0 */
0x1B0001B0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS1 */
0x1B0001C0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS2 */
0x1B0001D0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS3 */
0x1B0001E0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS4 */
};
const unsigned int *AP_DCM_Golden_Setting_tcl_gs_sodi =
AP_DCM_Golden_Setting_tcl_gs_sodi_data;
unsigned int AP_DCM_Golden_Setting_tcl_gs_sodi_len = 120;
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include <linux/cpuidle.h>
#include <mtk_lp_plat_reg.h>
#include <mtk_lpm.h>
#include <mt6781_spm_comm.h>
#include "mtk_cpupm_dbg.h"
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
#define MTK_IDLE_PROC_FS_NAME "cpuidle"
static int idle_proc_info_show(struct seq_file *m, void *v)
{
int cpu;
unsigned long long ts, rem;
ts = mtk_cpupm_syssram_read(SYSRAM_RECENT_CNT_TS_H);
ts = (ts << 32) | mtk_cpupm_syssram_read(SYSRAM_RECENT_CNT_TS_L);
ts = div64_u64_rem(ts, 1000 * 1000 * 1000, &rem);
seq_puts(m, "\n========= Power off count =========\n");
seq_puts(m, "\n-- Within 5 seconds before dumped --\n");
seq_printf(m, "\nLast dump timestamp = %llu.%09llu\n",
ts, rem);
seq_printf(m, "%8s", "cpu:");
for_each_possible_cpu(cpu)
seq_printf(m, " %d", mtk_cpupm_syssram_read(
SYSRAM_RECENT_CPU_CNT(cpu)));
seq_puts(m, "\n");
seq_printf(m, "%8s %d\n",
"cluster:",
mtk_cpupm_syssram_read(SYSRAM_RECENT_CPUSYS_CNT));
seq_printf(m, "%8s %d\n",
"mcusys:",
mtk_cpupm_syssram_read(SYSRAM_RECENT_MCUSYS_CNT));
seq_printf(m, "%8s 0x%x\n",
"online:",
mtk_cpupm_syssram_read(SYSRAM_CPU_ONLINE));
seq_puts(m, "\n---- Total ----\n");
seq_printf(m, "%8s %d\n",
"cluster:",
mtk_cpupm_syssram_read(SYSRAM_CPUSYS_CNT)
+ mtk_cpupm_syssram_read(SYSRAM_RECENT_CPUSYS_CNT));
seq_printf(m, "%8s %d\n",
"mcusys:",
mtk_cpupm_syssram_read(SYSRAM_MCUSYS_CNT)
+ mtk_cpupm_syssram_read(SYSRAM_RECENT_MCUSYS_CNT));
return 0;
}
static ssize_t idle_proc_info_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
return count;
}
static int idle_proc_enable_show(struct seq_file *m, void *v)
{
struct cpuidle_driver *drv;
int i, cpu, en = 0;
int suspend_type = mtk_lpm_suspend_type_get();
for_each_possible_cpu(cpu) {
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
for (i = 1; i < drv->state_count; i++) {
if ((suspend_type == MTK_LPM_SUSPEND_S2IDLE) &&
!strcmp(drv->states[i].name, S2IDLE_STATE_NAME))
continue;
en += mtk_cpuidle_get_param(drv, i, IDLE_PARAM_EN);
}
}
if (en == 0) {
seq_printf(m, "MCDI: Disable, %llu ms\n",
mtk_cpuidle_state_last_dis_ms());
} else {
seq_puts(m, "MCDI: Enable\n");
seq_puts(m,
"(cat /proc/cpuidle/state/enabled for more detail)\n");
}
return 0;
}
static ssize_t idle_proc_enable_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_state_enable(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
PROC_FOPS(info);
PROC_FOPS(enable);
int __init mtk_idle_procfs_init(void)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(info),
PROC_ENTRY(enable)
};
dir = proc_mkdir(MTK_IDLE_PROC_FS_NAME, NULL);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return -ENOMEM;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
mtk_idle_procfs_state_dir_init(dir);
mtk_idle_procfs_cpc_dir_init(dir);
mtk_idle_procfs_profile_dir_init(dir);
mtk_idle_procfs_control_dir_init(dir);
return 0;
}
void __exit mtk_idle_procfs_exit(void)
{
remove_proc_subtree(MTK_IDLE_PROC_FS_NAME, NULL);
}
@@ -0,0 +1,75 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#ifndef __MTK_IDLE_PROCFS__
#define __MTK_IDLE_PROCFS__
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
struct idle_proc_entry {
const char *name;
const struct file_operations *fops;
};
#define PROC_FOPS(name) \
static int idle_proc_ ## name ## _open(struct inode *inode, struct file *file) \
{ \
return single_open(file, idle_proc_ ## name ## _show, PDE_DATA(inode));\
} \
static const struct file_operations idle_proc_ ## name ## _fops = { \
.owner = THIS_MODULE, \
.open = idle_proc_ ## name ## _open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
.write = idle_proc_ ## name ## _write, \
}
#define PROC_ENTRY(name) {__stringify(name), &idle_proc_ ## name ## _fops}
#define PROC_CREATE_NODE(dir, entry) \
do { \
if (!proc_create(entry.name, 0644, dir, entry.fops)) \
pr_notice("%s(), create procfs cpuidle node %s failed\n", \
__func__, entry.name); \
} while (0)
#define mtk_idle_procfs_alloc_from_user(buf, userbuf, count) \
do { \
buf = (char *)__get_free_page(GFP_USER); \
\
if (!buf) \
break; \
\
if (count >= PAGE_SIZE \
|| copy_from_user(buf, userbuf, count)) { \
free_page((unsigned long)buf); \
buf = NULL; \
break; \
} \
\
buf[count] = '\0'; \
\
} while (0)
#define mtk_idle_procfs_free(buf) \
do { \
if (!buf) \
break; \
\
free_page((unsigned long)buf); \
} while (0)
void __init mtk_idle_procfs_cpc_dir_init(struct proc_dir_entry *parent);
void __init mtk_idle_procfs_state_dir_init(struct proc_dir_entry *parent);
void __init mtk_idle_procfs_profile_dir_init(struct proc_dir_entry *parent);
void __init mtk_idle_procfs_control_dir_init(struct proc_dir_entry *parent);
int __init mtk_idle_procfs_init(void);
void __exit mtk_idle_procfs_exit(void);
#endif /* __MTK_IDLE_PROCFS__ */
@@ -0,0 +1,337 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include <mtk_lp_plat_apmcu_mbox.h>
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
static int idle_proc_log_show(struct seq_file *m, void *v)
{
seq_printf(m, "CPU idle log : %s\n",
mtk_cpuidle_ctrl_log_sta_get() ?
"Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/control/log\n");
return 0;
}
static ssize_t idle_proc_log_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_ctrl_log_en(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_timer_show(struct seq_file *m, void *v)
{
seq_printf(m, "Limit sleep timer : %s\n",
mtk_cpuidle_ctrl_timer_sta_get() ?
"Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/control/timer\n");
return 0;
}
static ssize_t idle_proc_timer_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_ctrl_timer_en(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_stress_show(struct seq_file *m, void *v)
{
seq_printf(m, "CPU idle stress : %s\n",
mtk_cpuidle_get_stress_status() ?
"Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/control/stress\n");
return 0;
}
static ssize_t idle_proc_stress_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_set_stress_test(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_stress_time_show(struct seq_file *m, void *v)
{
seq_printf(m, "CPU idle stress interval time : %u\n",
mtk_cpuidle_get_stress_time());
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [us] > /proc/cpuidle/control/stress_time\n");
return 0;
}
static ssize_t idle_proc_stress_time_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int interval_us = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &interval_us) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_set_stress_time(interval_us);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_buck_mode_show(struct seq_file *m, void *v)
{
int mode;
struct mode_param {
int id;
char *str;
};
const struct mode_param table[] = {
{MCUPM_BUCK_NORMAL_MODE, "on"},
{MCUPM_BUCK_LP_MODE, "low power"},
{MCUPM_BUCK_OFF_MODE, "off"},
{NF_MCUPM_BUCK_MODE, "unknown"},
};
mode = mtk_get_lp_apmcu_buck_mode();
if (mode < 0 || mode > NF_MCUPM_BUCK_MODE)
mode = NF_MCUPM_BUCK_MODE;
seq_printf(m, "Vproc/Vproc_sram buck mode : %s\n",
table[mode].str);
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [mode] > /proc/cpuidle/control/buck_mode\n");
seq_puts(m, "mode:\n");
for (mode = 0; mode < NF_MCUPM_BUCK_MODE; mode++)
seq_printf(m, "\t%d: %s\n",
table[mode].id,
table[mode].str);
return 0;
}
static ssize_t idle_proc_buck_mode_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int mode = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &mode) != 0) {
ret = -EINVAL;
goto free;
}
if (mode < NF_MCUPM_BUCK_MODE)
mtk_set_lp_apmcu_buck_mode(mode);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_armpll_mode_show(struct seq_file *m, void *v)
{
int mode;
struct mode_param {
int id;
char *str;
};
const struct mode_param table[] = {
{MCUPM_ARMPLL_ON, "on"},
{MCUPM_ARMPLL_GATING, "gating"},
{MCUPM_ARMPLL_OFF, "off"},
{NF_MCUPM_ARMPLL_MODE, "unknown"},
};
mode = mtk_get_lp_apmcu_pll_mode();
if (mode < 0 || mode > NF_MCUPM_ARMPLL_MODE)
mode = NF_MCUPM_ARMPLL_MODE;
seq_printf(m, "armpll mode : %s\n",
table[mode].str);
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [mode] > /proc/cpuidle/control/armpll_mode\n");
seq_puts(m, "mode:\n");
for (mode = 0; mode < NF_MCUPM_ARMPLL_MODE; mode++)
seq_printf(m, "\t%d: %s\n",
table[mode].id,
table[mode].str);
return 0;
}
static ssize_t idle_proc_armpll_mode_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int mode = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &mode) != 0) {
ret = -EINVAL;
goto free;
}
if (mode < NF_MCUPM_ARMPLL_MODE)
mtk_set_lp_apmcu_pll_mode(mode);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_notify_cm_show(struct seq_file *m, void *v)
{
bool notify;
notify = mtk_lp_apmcu_cm_is_notified();
seq_printf(m, "mcupm cm mgr : %s\n",
notify ? "Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "Read Only : Not support dynamic control\n");
return 0;
}
static ssize_t idle_proc_notify_cm_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
return count;
}
PROC_FOPS(log);
PROC_FOPS(timer);
PROC_FOPS(stress);
PROC_FOPS(stress_time);
PROC_FOPS(buck_mode);
PROC_FOPS(armpll_mode);
PROC_FOPS(notify_cm);
void __init mtk_idle_procfs_control_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(log),
PROC_ENTRY(timer),
PROC_ENTRY(stress),
PROC_ENTRY(stress_time),
PROC_ENTRY(buck_mode),
PROC_ENTRY(armpll_mode),
PROC_ENTRY(notify_cm)
};
dir = proc_mkdir("control", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include "mtk_cpuidle_cpc.h"
#include "mtk_idle_procfs.h"
static int idle_proc_auto_off_show(struct seq_file *m, void *v)
{
seq_printf(m, "auto_off mode : %s\n",
cpc_get_auto_off_sta() ? "Enabled" : "Disabled");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/cpc/auto_off\n");
return 0;
}
static ssize_t idle_proc_auto_off_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
if (enabled)
cpc_auto_off_en();
else
cpc_auto_off_dis();
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_auto_off_thres_show(struct seq_file *m, void *v)
{
seq_printf(m, "auto_off threshold = %u us\n",
(unsigned int)cpc_get_auto_off_thres());
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [us] > /proc/cpuidle/cpc/auto_off_thres\n");
return 0;
}
static ssize_t idle_proc_auto_off_thres_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int threshold_us = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &threshold_us) != 0) {
ret = -EINVAL;
goto free;
}
cpc_set_auto_off_thres(threshold_us);
free:
mtk_idle_procfs_free(buf);
return ret;
}
PROC_FOPS(auto_off);
PROC_FOPS(auto_off_thres);
void __init mtk_idle_procfs_cpc_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(auto_off),
PROC_ENTRY(auto_off_thres)
};
dir = proc_mkdir("cpc", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include "mtk_cpuidle_cpc.h"
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
static int idle_proc_kernel_show(struct seq_file *m, void *v)
{
seq_printf(m, "%s\n", __func__);
return 0;
}
static ssize_t idle_proc_kernel_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
/* Start/Stop kernel profile */
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_target_state_show(struct seq_file *m, void *v)
{
seq_printf(m, "%s\n", __func__);
return 0;
}
static ssize_t idle_proc_target_state_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
int state = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtoint(buf, 10, &state) != 0) {
ret = -EINVAL;
goto free;
}
/* Set target state */
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_ratio_show(struct seq_file *m, void *v)
{
mtk_cpuidle_prof_ratio_dump(m);
return 0;
}
static ssize_t idle_proc_ratio_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
if (enabled)
mtk_cpuidle_prof_ratio_start();
else
mtk_cpuidle_prof_ratio_stop();
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_cpc_show(struct seq_file *m, void *v)
{
mtk_cpc_prof_lat_dump(m);
return 0;
}
static ssize_t idle_proc_cpc_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
if (enabled)
mtk_cpc_prof_start();
else
mtk_cpc_prof_stop();
free:
mtk_idle_procfs_free(buf);
return ret;
}
PROC_FOPS(kernel);
PROC_FOPS(target_state);
PROC_FOPS(ratio);
PROC_FOPS(cpc);
void __init mtk_idle_procfs_profile_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(kernel),
PROC_ENTRY(target_state),
PROC_ENTRY(ratio),
PROC_ENTRY(cpc)
};
dir = proc_mkdir("profile", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,218 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/cpuidle.h>
#include <linux/seq_file.h>
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
#define ALL_LITTLR_CPU_ID (10)
#define ALL_BIG_CPU_ID (20)
#define ALL_CPU_ID (100)
#define CPU_L_MASK (0x0f)
#define CPU_B_MASK (0xf0)
#define ALL_CPU_MASK (0xff)
static void idle_proc_state_uasge_print(struct seq_file *m, char *node_name)
{
seq_puts(m, "\n======== Command Usage ========\n");
seq_printf(m, "%s > /proc/cpuidle/state/%s\n",
"echo [cpu_id] [state_index] [val(dec)]",
node_name);
seq_puts(m, "\t cpu_id: 0~7 -> cpu number\n");
seq_printf(m, "\t %3d -> all little CPU\n", ALL_LITTLR_CPU_ID);
seq_printf(m, "\t %3d -> all big CPU\n", ALL_BIG_CPU_ID);
seq_printf(m, "\t %3d -> all CPU\n", ALL_CPU_ID);
seq_puts(m, "\t state_index: >0 (index 0 can't be modified)\n");
seq_puts(m, "\n");
}
static void idle_proc_state_param_show(struct seq_file *m, int param)
{
struct cpuidle_driver *drv;
int i, cpu, desc = 0;
for_each_possible_cpu(cpu) {
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
if (desc == 0) {
seq_printf(m, "%-12s:", "state_index");
for (i = 0; i < drv->state_count; i++)
seq_printf(m, "%7d ", i);
seq_puts(m, "\n");
desc = 1;
}
seq_printf(m, "%11s%d:", "cpu", cpu);
for (i = 0; i < drv->state_count; i++)
seq_printf(m, "%7d ",
mtk_cpuidle_get_param(drv, i, param));
seq_puts(m, "\n");
}
}
static int idle_proc_state_param_setting(char *cmd, int param)
{
struct cpuidle_driver *drv;
char *args;
unsigned int cpu_mask, state_idx = 0, val = 0;
int cpu = 0, i;
struct cmd_param {
unsigned int id;
unsigned int mask;
};
const struct cmd_param table[] = {
{ALL_LITTLR_CPU_ID, CPU_L_MASK},
{ALL_BIG_CPU_ID, CPU_B_MASK},
{ALL_CPU_ID, ALL_CPU_MASK},
};
args = strsep(&cmd, " ");
if (!args || kstrtoint(args, 10, &cpu) != 0)
return -EINVAL;
args = strsep(&cmd, " ");
if (!args || kstrtouint(args, 10, &state_idx) != 0)
return -EINVAL;
args = strsep(&cmd, " ");
if (!args || kstrtouint(args, 10, &val) != 0)
return -EINVAL;
if (!state_idx)
return -EINVAL;
cpu_mask = 0;
if (cpu >= 0 && cpu < nr_cpu_ids) {
cpu_mask = (1 << cpu);
} else {
for (i = 0; i < ARRAY_SIZE(table); i++) {
if (cpu == table[i].id) {
cpu_mask = table[i].mask;
break;
}
}
}
for_each_possible_cpu(cpu) {
if ((cpu_mask & (1 << cpu)) == 0)
continue;
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
mtk_cpuidle_set_param(drv, state_idx, param, val);
}
return 0;
}
static int idle_proc_latency_show(struct seq_file *m, void *v)
{
seq_puts(m, "==== CPU idle state: Exit latency ====\n");
idle_proc_state_param_show(m, IDLE_PARAM_LAT);
idle_proc_state_uasge_print(m, "latency");
return 0;
}
static ssize_t idle_proc_latency_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
idle_proc_state_param_setting(buf, IDLE_PARAM_LAT);
mtk_idle_procfs_free(buf);
return count;
}
static int idle_proc_residency_show(struct seq_file *m, void *v)
{
seq_puts(m, "==== CPU idle state: Target residency ====\n");
idle_proc_state_param_show(m, IDLE_PARAM_RES);
idle_proc_state_uasge_print(m, "residency");
return 0;
}
static ssize_t idle_proc_residency_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
idle_proc_state_param_setting(buf, IDLE_PARAM_RES);
mtk_idle_procfs_free(buf);
return count;
}
static int idle_proc_enabled_show(struct seq_file *m, void *v)
{
seq_puts(m, "==== CPU idle state: Enabled ====\n");
idle_proc_state_param_show(m, IDLE_PARAM_EN);
idle_proc_state_uasge_print(m, "enabled");
return 0;
}
static ssize_t idle_proc_enabled_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
idle_proc_state_param_setting(buf, IDLE_PARAM_EN);
mtk_idle_procfs_free(buf);
return count;
}
PROC_FOPS(latency);
PROC_FOPS(residency);
PROC_FOPS(enabled);
void __init mtk_idle_procfs_state_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(latency),
PROC_ENTRY(residency),
PROC_ENTRY(enabled)
};
dir = proc_mkdir("state", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,437 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
const unsigned int AP_PMIC_REG_6366_gs_suspend_32kless_data[] = {
/* Address Mask Golden Setting Value */
0x1E, 0xA, 0xA,/* TOP_CON */
0x22, 0x1F00, 0x1F00,/* TEST_CON0 */
0x2E, 0x1, 0x1,/* SMT_CON0 */
0x10C, 0x18, 0x18,/* TOP_CKPDN_CON0 */
0x112, 0x4, 0x4,/* TOP_CKPDN_CON1 */
0x118, 0x8, 0x8,/* TOP_CKSEL_CON0 */
0x11E, 0xF00, 0x0,/* TOP_CKSEL_CON1 */
0x134, 0xABF0, 0x3B0,/* TOP_CLK_CON0 */
0x408, 0x1, 0x1,/* RG_SPI_CON0 */
0x51A, 0xFF, 0x7F,/* SCK_TOP_CKPDN_CON0 */
0x788, 0x3FF8, 0x6D8,/* DCXO_CW00 */
0x790, 0x1, 0x0,/* DCXO_CW02 */
0x7A2, 0x3F00, 0x1B00,/* DCXO_CW11 */
0x7AC, 0x1000, 0x1000,/* DCXO_CW14 */
0x7B0, 0x3CFF, 0x1455,/* DCXO_CW16 */
0xC0C, 0x5FF, 0x1E4,/* BM_TOP_CKPDN_CON0 */
0xC1E, 0x3, 0x3,/* BM_TOP_CKHWEN_CON0 */
0xD08, 0x301, 0x301,/* FGADC_CON0 */
0xD0E, 0x30F, 0x308,/* FGADC_CON3 */
0xD44, 0xFFFF, 0x1F,/* FGADC_SON_CON0 */
0xD46, 0x3FF, 0x14,/* FGADC_SON_CON1 */
0xD48, 0x3FF, 0xFF,/* FGADC_SON_CON2 */
0xD4A, 0xFF, 0x3,/* FGADC_SON_CON3 */
0xD4C, 0xFFFF, 0x1F,/* FGADC_SOFF_CON0 */
0xD4E, 0x3FF, 0x14,/* FGADC_SOFF_CON1 */
0xD50, 0x3FF, 0xFF,/* FGADC_SOFF_CON2 */
0xD96, 0x1FFF, 0x400,/* FGADC_GAIN_CON0 */
0xF2C, 0x1, 0x0,/* BIF_CON18 */
0xF8C, 0x1FFF, 0xAAA,/* HK_TOP_CLK_CON0 */
0xF8E, 0x1, 0x1,/* HK_TOP_CLK_CON1 */
0x1188, 0xE03F, 0x14,/* AUXADC_CON0 */
0x11B4, 0x100, 0x0,/* AUXADC_CON20 */
0x123A, 0x8000, 0x8000,/* AUXADC_MDRT_0 */
0x1248, 0x3F9, 0x51,/* AUXADC_NAG_0 */
0x1260, 0x154, 0x0,/* AUXADC_DCM_CON */
0x130C, 0x1F, 0x0,/* BUCK_TOP_CLK_CON0 */
0x1312, 0xF, 0xF,/* BUCK_TOP_CLK_HWEN_CON0 */
0x1334, 0x100, 0x0,/* BUCK_TOP_K_CON0 */
0x1388, 0x3, 0x0,/* BUCK_VPROC11_CON0 */
0x1390, 0x2, 0x0,/* BUCK_VPROC11_OP_EN */
0x1408, 0x3, 0x0,/* BUCK_VPROC12_CON0 */
0x1410, 0x2, 0x0,/* BUCK_VPROC12_OP_EN */
0x1488, 0x3, 0x1,/* BUCK_VCORE_CON0 */
0x1490, 0x3, 0x3,/* BUCK_VCORE_OP_EN */
0x1496, 0x2, 0x2,/* BUCK_VCORE_OP_CFG */
0x1508, 0x3, 0x0,/* BUCK_VGPU_CON0 */
0x1510, 0x2, 0x0,/* BUCK_VGPU_OP_EN */
0x1588, 0x3, 0x1,/* BUCK_VMODEM_CON0 */
0x1590, 0x3, 0x3,/* BUCK_VMODEM_OP_EN */
0x1596, 0x2, 0x2,/* BUCK_VMODEM_OP_CFG */
0x1608, 0x1, 0x1,/* BUCK_VDRAM1_CON0 */
0x1610, 0x2, 0x2,/* BUCK_VDRAM1_OP_EN */
0x1616, 0x2, 0x2,/* BUCK_VDRAM1_OP_CFG */
0x1688, 0x1, 0x1,/* BUCK_VS1_CON0 */
0x1690, 0x3, 0x3,/* BUCK_VS1_OP_EN */
0x1696, 0x2, 0x2,/* BUCK_VS1_OP_CFG */
0x1708, 0x1, 0x1,/* BUCK_VS2_CON0 */
0x1710, 0x2, 0x2,/* BUCK_VS2_OP_EN */
0x1716, 0x2, 0x2,/* BUCK_VS2_OP_CFG */
0x1788, 0x1, 0x0,/* BUCK_VPA_CON0 */
0x1A0E, 0x3, 0x3,/* TOP_TOP_CKHWEN_CON0 */
0x1A10, 0x1, 0x1,/* LDO_TOP_CLK_DCM_CON0 */
0x1A12, 0x1, 0x0,/* LDO_TOP_CLK_VFE28_CON0 */
0x1A14, 0x1, 0x0,/* LDO_TOP_CLK_VXO22_CON0 */
0x1A16, 0x1, 0x0,/* LDO_TOP_CLK_VRF18_CON0 */
0x1A18, 0x1, 0x0,/* LDO_TOP_CLK_VRF12_CON0 */
0x1A1A, 0x1, 0x0,/* LDO_TOP_CLK_VEFUSE_CON0 */
0x1A1C, 0x1, 0x0,/* LDO_TOP_CLK_VCN33_CON0 */
0x1A1E, 0x1, 0x0,/* LDO_TOP_CLK_VCN28_CON0 */
0x1A20, 0x1, 0x0,/* LDO_TOP_CLK_VCN18_CON0 */
0x1A22, 0x1, 0x0,/* LDO_TOP_CLK_VM18_CON0 */
0x1A24, 0x1, 0x0,/* LDO_TOP_CLK_VMDDR_CON0 */
0x1A26, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_CORE_CON0 */
0x1A2C, 0x1, 0x0,/* LDO_TOP_CLK_VA12_CON0 */
0x1A2E, 0x1, 0x0,/* LDO_TOP_CLK_VAUX18_CON0 */
0x1A30, 0x1, 0x0,/* LDO_TOP_CLK_VAUD28_CON0 */
0x1A32, 0x1, 0x0,/* LDO_TOP_CLK_VIO28_CON0 */
0x1A34, 0x1, 0x0,/* LDO_TOP_CLK_VIO18_CON0 */
0x1A36, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_PROC11_CON0 */
0x1A38, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_PROC12_CON0 */
0x1A3A, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_OTHERS_CON0 */
0x1A3C, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_GPU_CON0 */
0x1A3E, 0x1, 0x0,/* LDO_TOP_CLK_VDRAM2_CON0 */
0x1A40, 0x1, 0x0,/* LDO_TOP_CLK_VMC_CON0 */
0x1A42, 0x1, 0x0,/* LDO_TOP_CLK_VMCH_CON0 */
0x1A44, 0x1, 0x0,/* LDO_TOP_CLK_VEMC_CON0 */
0x1A46, 0x1, 0x0,/* LDO_TOP_CLK_VSIM1_CON0 */
0x1A48, 0x1, 0x0,/* LDO_TOP_CLK_VSIM2_CON0 */
0x1A4A, 0x1, 0x0,/* LDO_TOP_CLK_VIBR_CON0 */
0x1A4C, 0x1, 0x0,/* LDO_TOP_CLK_VUSB_CON0 */
0x1A4E, 0x1, 0x0,/* LDO_TOP_CLK_VBIF28_CON0 */
0x1A8A, 0x2, 0x2,/* LDO_VXO22_OP_EN */
0x1A90, 0x2, 0x2,/* LDO_VXO22_OP_CFG */
0x1A9E, 0x2, 0x2,/* LDO_VA12_OP_EN */
0x1AA4, 0x2, 0x2,/* LDO_VA12_OP_CFG */
0x1AB2, 0x2, 0x2,/* LDO_VAUX18_OP_EN */
0x1AB8, 0x2, 0x2,/* LDO_VAUX18_OP_CFG */
0x1AC6, 0x2, 0x2,/* LDO_VAUD28_OP_EN */
0x1ACC, 0x2, 0x2,/* LDO_VAUD28_OP_CFG */
0x1AD8, 0x1, 0x1,/* LDO_VIO28_CON0 */
0x1AEC, 0x1, 0x1,/* LDO_VIO18_CON0 */
0x1B0A, 0x2, 0x2,/* LDO_VDRAM2_OP_EN */
0x1B10, 0x2, 0x2,/* LDO_VDRAM2_OP_CFG */
0x1B1C, 0x1, 0x0,/* LDO_VEMC_CON0 */
0x1B32, 0x2, 0x2,/* LDO_VUSB_OP_EN */
0x1B38, 0x2, 0x2,/* LDO_VUSB_OP_CFG */
0x1B46, 0x1, 0x0,/* LDO_VSRAM_PROC11_CON0 */
0x1B88, 0x1, 0x0,/* LDO_VSRAM_PROC12_CON0 */
0x1BA6, 0x1, 0x1,/* LDO_VSRAM_OTHERS_CON0 */
0x1BC8, 0x1, 0x0,/* LDO_VSRAM_GPU_CON0 */
0x1C0A, 0x4, 0x4,/* LDO_VFE28_OP_EN */
0x1C10, 0x4, 0x0,/* LDO_VFE28_OP_CFG */
0x1C1E, 0x4, 0x4,/* LDO_VRF18_OP_EN */
0x1C24, 0x4, 0x0,/* LDO_VRF18_OP_CFG */
0x1C32, 0x4, 0x4,/* LDO_VRF12_OP_EN */
0x1C38, 0x4, 0x0,/* LDO_VRF12_OP_CFG */
0x1C44, 0x1, 0x0,/* LDO_VEFUSE_CON0 */
0x1C58, 0x1, 0x0,/* LDO_VCN18_CON0 */
0x1C6C, 0x1, 0x1,/* LDO_VM18_CON0 */
0x1C8A, 0x2, 0x2,/* LDO_VMDDR_OP_EN */
0x1C90, 0x2, 0x2,/* LDO_VMDDR_OP_CFG */
0x1CA4, 0x2, 0x2,/* LDO_VSRAM_CORE_OP_EN */
0x1CAA, 0x2, 0x2,/* LDO_VSRAM_CORE_OP_CFG */
0x1CC4, 0x1, 0x0,/* LDO_VMC_CON0 */
0x1CD8, 0x1, 0x0,/* LDO_VMCH_CON0 */
0x1D08, 0x1, 0x0,/* LDO_VIBR_CON0 */
0x1D1C, 0x1, 0x0,/* LDO_VCN33_CON0_0 */
0x1D2A, 0x1, 0x0,/* LDO_VCN33_CON0_1 */
0x1D48, 0x1, 0x0,/* LDO_VSIM1_CON0 */
0x1D5C, 0x1, 0x0,/* LDO_VSIM2_CON0 */
0x1D88, 0x1, 0x0,/* LDO_VCN28_CON0 */
0x1D9C, 0x2, 0x2,/* VRTC28_CON0 */
0x1DA0, 0x2, 0x2,/* LDO_VBIF28_OP_EN */
0x1DA6, 0x2, 0x0,/* LDO_VBIF28_OP_CFG */
};
const unsigned int *AP_PMIC_REG_6366_gs_suspend_32kless =
AP_PMIC_REG_6366_gs_suspend_32kless_data;
unsigned int AP_PMIC_REG_6366_gs_suspend_32kless_len = 402;
const unsigned int AP_PMIC_REG_6366_gs_sodi3p0_32kless_data[] = {
/* Address Mask Golden Setting Value */
0x1E, 0xA, 0xA,/* TOP_CON */
0x22, 0x1F00, 0x1F00,/* TEST_CON0 */
0x2E, 0x1, 0x1,/* SMT_CON0 */
0x10C, 0x18, 0x18,/* TOP_CKPDN_CON0 */
0x112, 0x4, 0x4,/* TOP_CKPDN_CON1 */
0x118, 0x8, 0x8,/* TOP_CKSEL_CON0 */
0x11E, 0xF00, 0x0,/* TOP_CKSEL_CON1 */
0x134, 0xABF0, 0x3B0,/* TOP_CLK_CON0 */
0x408, 0x1, 0x1,/* RG_SPI_CON0 */
0x51A, 0xFF, 0x7F,/* SCK_TOP_CKPDN_CON0 */
0x788, 0x3FF8, 0x6D8,/* DCXO_CW00 */
0x790, 0x1, 0x0,/* DCXO_CW02 */
0x7A2, 0x3F00, 0x1B00,/* DCXO_CW11 */
0x7AC, 0x1000, 0x1000,/* DCXO_CW14 */
0x7B0, 0x3CFF, 0x1455,/* DCXO_CW16 */
0xC0C, 0x5FF, 0x1E4,/* BM_TOP_CKPDN_CON0 */
0xC1E, 0x3, 0x3,/* BM_TOP_CKHWEN_CON0 */
0xD08, 0x301, 0x301,/* FGADC_CON0 */
0xD0E, 0x30F, 0x308,/* FGADC_CON3 */
0xD44, 0xFFFF, 0x1F,/* FGADC_SON_CON0 */
0xD46, 0x3FF, 0x14,/* FGADC_SON_CON1 */
0xD48, 0x3FF, 0xFF,/* FGADC_SON_CON2 */
0xD4A, 0xFF, 0x3,/* FGADC_SON_CON3 */
0xD4C, 0xFFFF, 0x1F,/* FGADC_SOFF_CON0 */
0xD4E, 0x3FF, 0x14,/* FGADC_SOFF_CON1 */
0xD50, 0x3FF, 0xFF,/* FGADC_SOFF_CON2 */
0xD96, 0x1FFF, 0x400,/* FGADC_GAIN_CON0 */
0xF2C, 0x1, 0x0,/* BIF_CON18 */
0xF8C, 0x1FFF, 0xAAA,/* HK_TOP_CLK_CON0 */
0xF8E, 0x1, 0x1,/* HK_TOP_CLK_CON1 */
0x1188, 0xE03F, 0x14,/* AUXADC_CON0 */
0x11B4, 0x100, 0x0,/* AUXADC_CON20 */
0x123A, 0x8000, 0x8000,/* AUXADC_MDRT_0 */
0x1248, 0x3F9, 0x51,/* AUXADC_NAG_0 */
0x1260, 0x154, 0x0,/* AUXADC_DCM_CON */
0x130C, 0x1F, 0x0,/* BUCK_TOP_CLK_CON0 */
0x1312, 0xF, 0xF,/* BUCK_TOP_CLK_HWEN_CON0 */
0x1334, 0x100, 0x0,/* BUCK_TOP_K_CON0 */
0x1388, 0x3, 0x0,/* BUCK_VPROC11_CON0 */
0x1390, 0x2, 0x0,/* BUCK_VPROC11_OP_EN */
0x1408, 0x3, 0x0,/* BUCK_VPROC12_CON0 */
0x1410, 0x2, 0x0,/* BUCK_VPROC12_OP_EN */
0x1488, 0x3, 0x1,/* BUCK_VCORE_CON0 */
0x1490, 0x3, 0x3,/* BUCK_VCORE_OP_EN */
0x1496, 0x2, 0x2,/* BUCK_VCORE_OP_CFG */
0x1508, 0x3, 0x0,/* BUCK_VGPU_CON0 */
0x1510, 0x2, 0x0,/* BUCK_VGPU_OP_EN */
0x1588, 0x3, 0x1,/* BUCK_VMODEM_CON0 */
0x1590, 0x3, 0x3,/* BUCK_VMODEM_OP_EN */
0x1596, 0x2, 0x2,/* BUCK_VMODEM_OP_CFG */
0x1608, 0x1, 0x1,/* BUCK_VDRAM1_CON0 */
0x1610, 0x2, 0x2,/* BUCK_VDRAM1_OP_EN */
0x1616, 0x2, 0x2,/* BUCK_VDRAM1_OP_CFG */
0x1688, 0x1, 0x1,/* BUCK_VS1_CON0 */
0x1690, 0x3, 0x3,/* BUCK_VS1_OP_EN */
0x1696, 0x2, 0x2,/* BUCK_VS1_OP_CFG */
0x1708, 0x1, 0x1,/* BUCK_VS2_CON0 */
0x1710, 0x2, 0x2,/* BUCK_VS2_OP_EN */
0x1716, 0x2, 0x2,/* BUCK_VS2_OP_CFG */
0x1788, 0x1, 0x0,/* BUCK_VPA_CON0 */
0x1A0E, 0x3, 0x3,/* TOP_TOP_CKHWEN_CON0 */
0x1A10, 0x1, 0x1,/* LDO_TOP_CLK_DCM_CON0 */
0x1A12, 0x1, 0x0,/* LDO_TOP_CLK_VFE28_CON0 */
0x1A14, 0x1, 0x0,/* LDO_TOP_CLK_VXO22_CON0 */
0x1A16, 0x1, 0x0,/* LDO_TOP_CLK_VRF18_CON0 */
0x1A18, 0x1, 0x0,/* LDO_TOP_CLK_VRF12_CON0 */
0x1A1A, 0x1, 0x0,/* LDO_TOP_CLK_VEFUSE_CON0 */
0x1A1C, 0x1, 0x0,/* LDO_TOP_CLK_VCN33_CON0 */
0x1A1E, 0x1, 0x0,/* LDO_TOP_CLK_VCN28_CON0 */
0x1A20, 0x1, 0x0,/* LDO_TOP_CLK_VCN18_CON0 */
0x1A22, 0x1, 0x0,/* LDO_TOP_CLK_VM18_CON0 */
0x1A24, 0x1, 0x0,/* LDO_TOP_CLK_VMDDR_CON0 */
0x1A26, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_CORE_CON0 */
0x1A2C, 0x1, 0x0,/* LDO_TOP_CLK_VA12_CON0 */
0x1A2E, 0x1, 0x0,/* LDO_TOP_CLK_VAUX18_CON0 */
0x1A30, 0x1, 0x0,/* LDO_TOP_CLK_VAUD28_CON0 */
0x1A32, 0x1, 0x0,/* LDO_TOP_CLK_VIO28_CON0 */
0x1A34, 0x1, 0x0,/* LDO_TOP_CLK_VIO18_CON0 */
0x1A36, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_PROC11_CON0 */
0x1A38, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_PROC12_CON0 */
0x1A3A, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_OTHERS_CON0 */
0x1A3C, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_GPU_CON0 */
0x1A3E, 0x1, 0x0,/* LDO_TOP_CLK_VDRAM2_CON0 */
0x1A40, 0x1, 0x0,/* LDO_TOP_CLK_VMC_CON0 */
0x1A42, 0x1, 0x0,/* LDO_TOP_CLK_VMCH_CON0 */
0x1A44, 0x1, 0x0,/* LDO_TOP_CLK_VEMC_CON0 */
0x1A46, 0x1, 0x0,/* LDO_TOP_CLK_VSIM1_CON0 */
0x1A48, 0x1, 0x0,/* LDO_TOP_CLK_VSIM2_CON0 */
0x1A4A, 0x1, 0x0,/* LDO_TOP_CLK_VIBR_CON0 */
0x1A4C, 0x1, 0x0,/* LDO_TOP_CLK_VUSB_CON0 */
0x1A4E, 0x1, 0x0,/* LDO_TOP_CLK_VBIF28_CON0 */
0x1A8A, 0x2, 0x2,/* LDO_VXO22_OP_EN */
0x1A90, 0x2, 0x2,/* LDO_VXO22_OP_CFG */
0x1A9E, 0x2, 0x2,/* LDO_VA12_OP_EN */
0x1AA4, 0x2, 0x2,/* LDO_VA12_OP_CFG */
0x1AB2, 0x2, 0x2,/* LDO_VAUX18_OP_EN */
0x1AB8, 0x2, 0x2,/* LDO_VAUX18_OP_CFG */
0x1AC6, 0x2, 0x2,/* LDO_VAUD28_OP_EN */
0x1ACC, 0x2, 0x2,/* LDO_VAUD28_OP_CFG */
0x1AD8, 0x1, 0x1,/* LDO_VIO28_CON0 */
0x1AEC, 0x1, 0x1,/* LDO_VIO18_CON0 */
0x1B0A, 0x2, 0x2,/* LDO_VDRAM2_OP_EN */
0x1B10, 0x2, 0x2,/* LDO_VDRAM2_OP_CFG */
0x1B1C, 0x1, 0x1,/* LDO_VEMC_CON0 */
0x1B32, 0x2, 0x2,/* LDO_VUSB_OP_EN */
0x1B38, 0x2, 0x2,/* LDO_VUSB_OP_CFG */
0x1B46, 0x1, 0x0,/* LDO_VSRAM_PROC11_CON0 */
0x1B88, 0x1, 0x0,/* LDO_VSRAM_PROC12_CON0 */
0x1BA6, 0x1, 0x1,/* LDO_VSRAM_OTHERS_CON0 */
0x1BC8, 0x1, 0x0,/* LDO_VSRAM_GPU_CON0 */
0x1C0A, 0x4, 0x4,/* LDO_VFE28_OP_EN */
0x1C10, 0x4, 0x0,/* LDO_VFE28_OP_CFG */
0x1C1E, 0x4, 0x4,/* LDO_VRF18_OP_EN */
0x1C24, 0x4, 0x0,/* LDO_VRF18_OP_CFG */
0x1C32, 0x4, 0x4,/* LDO_VRF12_OP_EN */
0x1C38, 0x4, 0x0,/* LDO_VRF12_OP_CFG */
0x1C44, 0x1, 0x0,/* LDO_VEFUSE_CON0 */
0x1C58, 0x1, 0x0,/* LDO_VCN18_CON0 */
0x1C6C, 0x1, 0x1,/* LDO_VM18_CON0 */
0x1C8A, 0x2, 0x2,/* LDO_VMDDR_OP_EN */
0x1C90, 0x2, 0x2,/* LDO_VMDDR_OP_CFG */
0x1CA4, 0x2, 0x2,/* LDO_VSRAM_CORE_OP_EN */
0x1CAA, 0x2, 0x2,/* LDO_VSRAM_CORE_OP_CFG */
0x1CC4, 0x1, 0x0,/* LDO_VMC_CON0 */
0x1CD8, 0x1, 0x0,/* LDO_VMCH_CON0 */
0x1D08, 0x1, 0x0,/* LDO_VIBR_CON0 */
0x1D1C, 0x1, 0x0,/* LDO_VCN33_CON0_0 */
0x1D2A, 0x1, 0x0,/* LDO_VCN33_CON0_1 */
0x1D48, 0x1, 0x0,/* LDO_VSIM1_CON0 */
0x1D5C, 0x1, 0x0,/* LDO_VSIM2_CON0 */
0x1D88, 0x1, 0x0,/* LDO_VCN28_CON0 */
0x1D9C, 0x2, 0x2,/* VRTC28_CON0 */
0x1DA0, 0x2, 0x2,/* LDO_VBIF28_OP_EN */
0x1DA6, 0x2, 0x0,/* LDO_VBIF28_OP_CFG */
};
const unsigned int *AP_PMIC_REG_6366_gs_sodi3p0_32kless =
AP_PMIC_REG_6366_gs_sodi3p0_32kless_data;
unsigned int AP_PMIC_REG_6366_gs_sodi3p0_32kless_len = 402;
const unsigned int AP_PMIC_REG_6366_gs_deepidle___lp_mp3_32kless_data[] = {
/* Address Mask Golden Setting Value */
0x1E, 0xA, 0xA,/* TOP_CON */
0x22, 0x1F00, 0x1F00,/* TEST_CON0 */
0x2E, 0x1, 0x1,/* SMT_CON0 */
0x10C, 0x18, 0x18,/* TOP_CKPDN_CON0 */
0x112, 0x4, 0x4,/* TOP_CKPDN_CON1 */
0x118, 0x8, 0x8,/* TOP_CKSEL_CON0 */
0x11E, 0xF00, 0x0,/* TOP_CKSEL_CON1 */
0x134, 0xABF0, 0x3B0,/* TOP_CLK_CON0 */
0x408, 0x1, 0x1,/* RG_SPI_CON0 */
0x51A, 0xFF, 0x7F,/* SCK_TOP_CKPDN_CON0 */
0x788, 0x3FF8, 0x16D8,/* DCXO_CW00 */
0x790, 0x1, 0x0,/* DCXO_CW02 */
0x7A2, 0x3F00, 0x1B00,/* DCXO_CW11 */
0x7AC, 0x1000, 0x1000,/* DCXO_CW14 */
0x7B0, 0x3CFF, 0x1455,/* DCXO_CW16 */
0xC0C, 0x5FF, 0x1E4,/* BM_TOP_CKPDN_CON0 */
0xC1E, 0x3, 0x3,/* BM_TOP_CKHWEN_CON0 */
0xD08, 0x301, 0x301,/* FGADC_CON0 */
0xD0E, 0x30F, 0x308,/* FGADC_CON3 */
0xD44, 0xFFFF, 0x1F,/* FGADC_SON_CON0 */
0xD46, 0x3FF, 0x14,/* FGADC_SON_CON1 */
0xD48, 0x3FF, 0xFF,/* FGADC_SON_CON2 */
0xD4A, 0xFF, 0x3,/* FGADC_SON_CON3 */
0xD4C, 0xFFFF, 0x1F,/* FGADC_SOFF_CON0 */
0xD4E, 0x3FF, 0x14,/* FGADC_SOFF_CON1 */
0xD50, 0x3FF, 0xFF,/* FGADC_SOFF_CON2 */
0xD96, 0x1FFF, 0x400,/* FGADC_GAIN_CON0 */
0xF2C, 0x1, 0x0,/* BIF_CON18 */
0xF8C, 0x1FFF, 0xAAA,/* HK_TOP_CLK_CON0 */
0xF8E, 0x1, 0x1,/* HK_TOP_CLK_CON1 */
0x1188, 0xE03F, 0x14,/* AUXADC_CON0 */
0x11B4, 0x100, 0x0,/* AUXADC_CON20 */
0x123A, 0x8000, 0x8000,/* AUXADC_MDRT_0 */
0x1248, 0x3F9, 0x51,/* AUXADC_NAG_0 */
0x1260, 0x154, 0x0,/* AUXADC_DCM_CON */
0x130C, 0x1F, 0x0,/* BUCK_TOP_CLK_CON0 */
0x1312, 0xF, 0xF,/* BUCK_TOP_CLK_HWEN_CON0 */
0x1334, 0x100, 0x0,/* BUCK_TOP_K_CON0 */
0x1388, 0x3, 0x0,/* BUCK_VPROC11_CON0 */
0x1390, 0x8, 0x0,/* BUCK_VPROC11_OP_EN */
0x1408, 0x3, 0x0,/* BUCK_VPROC12_CON0 */
0x1410, 0x8, 0x0,/* BUCK_VPROC12_OP_EN */
0x1488, 0x3, 0x1,/* BUCK_VCORE_CON0 */
0x1490, 0x9, 0x9,/* BUCK_VCORE_OP_EN */
0x1496, 0x8, 0x8,/* BUCK_VCORE_OP_CFG */
0x1508, 0x3, 0x0,/* BUCK_VGPU_CON0 */
0x1510, 0x8, 0x0,/* BUCK_VGPU_OP_EN */
0x1588, 0x3, 0x1,/* BUCK_VMODEM_CON0 */
0x1590, 0x9, 0x1,/* BUCK_VMODEM_OP_EN */
0x1596, 0x8, 0x8,/* BUCK_VMODEM_OP_CFG */
0x1608, 0x1, 0x1,/* BUCK_VDRAM1_CON0 */
0x1610, 0x8, 0x8,/* BUCK_VDRAM1_OP_EN */
0x1616, 0x8, 0x8,/* BUCK_VDRAM1_OP_CFG */
0x1688, 0x1, 0x1,/* BUCK_VS1_CON0 */
0x1690, 0x9, 0x1,/* BUCK_VS1_OP_EN */
0x1696, 0x8, 0x8,/* BUCK_VS1_OP_CFG */
0x1708, 0x1, 0x1,/* BUCK_VS2_CON0 */
0x1710, 0x8, 0x8,/* BUCK_VS2_OP_EN */
0x1716, 0x8, 0x8,/* BUCK_VS2_OP_CFG */
0x1788, 0x1, 0x0,/* BUCK_VPA_CON0 */
0x1A0E, 0x3, 0x3,/* TOP_TOP_CKHWEN_CON0 */
0x1A10, 0x1, 0x1,/* LDO_TOP_CLK_DCM_CON0 */
0x1A12, 0x1, 0x0,/* LDO_TOP_CLK_VFE28_CON0 */
0x1A14, 0x1, 0x0,/* LDO_TOP_CLK_VXO22_CON0 */
0x1A16, 0x1, 0x0,/* LDO_TOP_CLK_VRF18_CON0 */
0x1A18, 0x1, 0x0,/* LDO_TOP_CLK_VRF12_CON0 */
0x1A1A, 0x1, 0x0,/* LDO_TOP_CLK_VEFUSE_CON0 */
0x1A1C, 0x1, 0x0,/* LDO_TOP_CLK_VCN33_CON0 */
0x1A1E, 0x1, 0x0,/* LDO_TOP_CLK_VCN28_CON0 */
0x1A20, 0x1, 0x0,/* LDO_TOP_CLK_VCN18_CON0 */
0x1A22, 0x1, 0x0,/* LDO_TOP_CLK_VM18_CON0 */
0x1A24, 0x1, 0x0,/* LDO_TOP_CLK_VMDDR_CON0 */
0x1A26, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_CORE_CON0 */
0x1A2C, 0x1, 0x0,/* LDO_TOP_CLK_VA12_CON0 */
0x1A2E, 0x1, 0x0,/* LDO_TOP_CLK_VAUX18_CON0 */
0x1A30, 0x1, 0x0,/* LDO_TOP_CLK_VAUD28_CON0 */
0x1A32, 0x1, 0x0,/* LDO_TOP_CLK_VIO28_CON0 */
0x1A34, 0x1, 0x0,/* LDO_TOP_CLK_VIO18_CON0 */
0x1A36, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_PROC11_CON0 */
0x1A38, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_PROC12_CON0 */
0x1A3A, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_OTHERS_CON0 */
0x1A3C, 0x1, 0x0,/* LDO_TOP_CLK_VSRAM_GPU_CON0 */
0x1A3E, 0x1, 0x0,/* LDO_TOP_CLK_VDRAM2_CON0 */
0x1A40, 0x1, 0x0,/* LDO_TOP_CLK_VMC_CON0 */
0x1A42, 0x1, 0x0,/* LDO_TOP_CLK_VMCH_CON0 */
0x1A44, 0x1, 0x0,/* LDO_TOP_CLK_VEMC_CON0 */
0x1A46, 0x1, 0x0,/* LDO_TOP_CLK_VSIM1_CON0 */
0x1A48, 0x1, 0x0,/* LDO_TOP_CLK_VSIM2_CON0 */
0x1A4A, 0x1, 0x0,/* LDO_TOP_CLK_VIBR_CON0 */
0x1A4C, 0x1, 0x0,/* LDO_TOP_CLK_VUSB_CON0 */
0x1A4E, 0x1, 0x0,/* LDO_TOP_CLK_VBIF28_CON0 */
0x1A8A, 0x8, 0x8,/* LDO_VXO22_OP_EN */
0x1A90, 0x8, 0x8,/* LDO_VXO22_OP_CFG */
0x1A9E, 0x8, 0x8,/* LDO_VA12_OP_EN */
0x1AA4, 0x8, 0x8,/* LDO_VA12_OP_CFG */
0x1AB2, 0x8, 0x8,/* LDO_VAUX18_OP_EN */
0x1AB8, 0x8, 0x8,/* LDO_VAUX18_OP_CFG */
0x1AC4, 0x1, 0x1,/* LDO_VAUD28_CON0 */
0x1AD8, 0x1, 0x1,/* LDO_VIO28_CON0 */
0x1AEC, 0x1, 0x1,/* LDO_VIO18_CON0 */
0x1B0A, 0x8, 0x8,/* LDO_VDRAM2_OP_EN */
0x1B10, 0x8, 0x8,/* LDO_VDRAM2_OP_CFG */
0x1B1C, 0x1, 0x1,/* LDO_VEMC_CON0 */
0x1B32, 0x8, 0x8,/* LDO_VUSB_OP_EN */
0x1B38, 0x8, 0x8,/* LDO_VUSB_OP_CFG */
0x1B46, 0x1, 0x0,/* LDO_VSRAM_PROC11_CON0 */
0x1B88, 0x1, 0x0,/* LDO_VSRAM_PROC12_CON0 */
0x1BA6, 0x1, 0x1,/* LDO_VSRAM_OTHERS_CON0 */
0x1BC8, 0x1, 0x0,/* LDO_VSRAM_GPU_CON0 */
0x1C0A, 0x4, 0x4,/* LDO_VFE28_OP_EN */
0x1C10, 0x4, 0x0,/* LDO_VFE28_OP_CFG */
0x1C1E, 0x4, 0x4,/* LDO_VRF18_OP_EN */
0x1C24, 0x4, 0x0,/* LDO_VRF18_OP_CFG */
0x1C32, 0x4, 0x4,/* LDO_VRF12_OP_EN */
0x1C38, 0x4, 0x0,/* LDO_VRF12_OP_CFG */
0x1C44, 0x1, 0x0,/* LDO_VEFUSE_CON0 */
0x1C58, 0x1, 0x0,/* LDO_VCN18_CON0 */
0x1C6C, 0x1, 0x1,/* LDO_VM18_CON0 */
0x1C8A, 0x8, 0x8,/* LDO_VMDDR_OP_EN */
0x1C90, 0x8, 0x8,/* LDO_VMDDR_OP_CFG */
0x1CA4, 0x8, 0x8,/* LDO_VSRAM_CORE_OP_EN */
0x1CAA, 0x8, 0x8,/* LDO_VSRAM_CORE_OP_CFG */
0x1CC4, 0x1, 0x0,/* LDO_VMC_CON0 */
0x1CD8, 0x1, 0x0,/* LDO_VMCH_CON0 */
0x1D08, 0x1, 0x0,/* LDO_VIBR_CON0 */
0x1D1C, 0x1, 0x0,/* LDO_VCN33_CON0_0 */
0x1D2A, 0x1, 0x0,/* LDO_VCN33_CON0_1 */
0x1D48, 0x1, 0x0,/* LDO_VSIM1_CON0 */
0x1D5C, 0x1, 0x0,/* LDO_VSIM2_CON0 */
0x1D88, 0x1, 0x0,/* LDO_VCN28_CON0 */
0x1D9C, 0x2, 0x2,/* VRTC28_CON0 */
0x1DA0, 0x8, 0x8,/* LDO_VBIF28_OP_EN */
0x1DA6, 0x8, 0x0,/* LDO_VBIF28_OP_CFG */
};
const unsigned int *AP_PMIC_REG_6366_gs_deepidle___lp_mp3_32kless =
AP_PMIC_REG_6366_gs_deepidle___lp_mp3_32kless_data;
unsigned int AP_PMIC_REG_6366_gs_deepidle___lp_mp3_32kless_len = 399;
@@ -0,0 +1,31 @@
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/inc/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/mtk_lpm_fs/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/inc/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/modules/include/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/modules/include/mt6833/
obj-y += mt6833_dbg.o
obj-y += mt6833_dbg_lpm_fs.o
obj-y += mt6833_dbg_lpm_rc_fs.o
obj-y += mt6833_dbg_spm_fs.o
obj-y += mt6833_logger.o
obj-y += mt6833_lpm_trace_event.o
obj-y += mt6833_cpupm_dbg.o
mt6833_cpupm_dbg-y += mtk_cpupm_dbg.o
mt6833_cpupm_dbg-y += mtk_cpuidle_status.o
mt6833_cpupm_dbg-y += mtk_cpuidle_cpc.o
mt6833_cpupm_dbg-y += mtk_idle_procfs.o
mt6833_cpupm_dbg-y += mtk_idle_procfs_state.o
mt6833_cpupm_dbg-y += mtk_idle_procfs_cpc.o
mt6833_cpupm_dbg-y += mtk_idle_procfs_profile.o
mt6833_cpupm_dbg-y += mtk_idle_procfs_control.o
obj-$(CONFIG_MTK_LPM_GS_DUMP_SUPPORT) += mt6833_power_gs_compare.o
obj-$(CONFIG_MTK_LPM_GS_DUMP_SUPPORT) += mtk_power_gs_6315_array.o
obj-$(CONFIG_MTK_LPM_GS_DUMP_SUPPORT) += mtk_power_gs_6365_array.o
obj-$(CONFIG_MTK_LPM_GS_DUMP_SUPPORT) += mtk_dcm_array.o
obj-$(CONFIG_MTK_LPM_GS_DUMP_SUPPORT) += mtk_cg_array.o
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <gs/mtk_lpm_pwr_gs.h>
#include <mt6833_dbg_fs_common.h>
#include <mt6833_power_gs_compare.h>
static void __exit mt6833_dbg_exit(void)
{
mt6833_dbg_lpm_fs_deinit();
mt6833_dbg_spm_fs_deinit();
mt6833_dbg_lpm_deinit();
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
mt6833_power_gs_deinit();
#endif
mtk_dbg_common_fs_exit();
}
static int __init mt6833_dbg_init(void)
{
mt6833_dbg_lpm_init();
mt6833_dbg_lpm_fs_init();
mt6833_dbg_spm_fs_init();
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
mt6833_power_gs_init();
#endif
mtk_dbg_common_fs_init();
pr_info("%s %d: finish", __func__, __LINE__);
return 0;
}
module_init(mt6833_dbg_init);
module_exit(mt6833_dbg_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MT6833 Low Power FileSystem");
MODULE_AUTHOR("MediaTek Inc.");
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mt6833_dbg_fs_common.h>
static void __exit mt6833_dbg_fs_exit(void)
{
mt6833_dbg_lpm_fs_deinit();
mt6833_dbg_spm_fs_deinit();
mt6833_dbg_lpm_deinit();
}
static int __init mt6833_dbg_fs_init(void)
{
mt6833_dbg_lpm_init();
mt6833_dbg_lpm_fs_init();
mt6833_dbg_spm_fs_init();
pr_info("%s %d: finish", __func__, __LINE__);
return 0;
}
module_init(mt6833_dbg_fs_init);
module_exit(mt6833_dbg_fs_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MT6833 Low Power FileSystem");
MODULE_AUTHOR("MediaTek Inc.");
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MT6833_DBG_FS_COMMON_H__
#define __MT6833_DBG_FS_COMMON_H__
/* Determine for user name handle */
#define MT_LP_RQ_USER_NAME_LEN (4)
#define MT_LP_RQ_USER_CHAR_U (8)
#define MT_LP_RQ_USER_CHAR_MASK (0xFF)
/* Determine for resource usage id */
#define MT_LP_RQ_ID_ALL_USAGE (-1)
int mt6833_dbg_lpm_init(void);
int mt6833_dbg_lpm_deinit(void);
int mt6833_dbg_lpm_fs_init(void);
int mt6833_dbg_lpm_fs_deinit(void);
int mt6833_dbg_spm_fs_init(void);
int mt6833_dbg_spm_fs_deinit(void);
#endif /* __MTK_LP_PLAT_FS_H__ */
@@ -0,0 +1,241 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/device.h>
#include <linux/of_device.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mtk_resource_constraint_v1.h>
#include <mt6833_pwr_ctrl.h>
#include <mt6833_dbg_fs_common.h>
#include <mt6833_cond.h>
#include <mt6833_spm_comm.h>
#include <mtk_lpm_sysfs.h>
#include <mtk_lp_sysfs.h>
#include <mtk_lpm_platform.h>
#define MT6833_GENERIC_TRACE_NODE_INIT(_n, _r, _w) ({\
_n.op.fs_read = _r;\
_n.op.fs_write = _w;\
_n.op.priv = &_n; })
#define mt6833_dbg_lpm_trace_log(buf, sz, len, fmt, args...) ({\
if (len < sz)\
len += scnprintf(buf + len, sz - len,\
fmt, ##args); })
/* Common section */
#define MT6833_DGB_TRACE_COMMON_BASE (0x0)
#define MT6833_DGB_TRACE_COMMON_SZ (0x20)
#define TRACE_DBG_LAST_RC_INFO_CPU_SHIFT (28)
#define TRACE_DBG_LAST_RC_INFO_CPU_MASK (0xf)
#define TRACE_DBG_LAST_RC_INFO_RCID_SHIFT (0)
#define TRACE_DBG_LAST_RC_INFO_RCID_MASK (0xffff)
#define TRACE_DBG_LAST_RC_INFO_CPU(info)\
((info >> TRACE_DBG_LAST_RC_INFO_CPU_SHIFT)\
& TRACE_DBG_LAST_RC_INFO_CPU_MASK)
#define TRACE_DBG_LAST_RC_INFO_RC_ID(info)\
((info >> TRACE_DBG_LAST_RC_INFO_RCID_SHIFT)\
& TRACE_DBG_LAST_RC_INFO_RCID_MASK)
/* Suspend section */
#define MT6833_DGB_TRACE_SUSPEND_BASE \
(MT6833_DGB_TRACE_COMMON_BASE + MT6833_DGB_TRACE_COMMON_SZ)
#define MT6833_DGB_TRACE_SUSPEND_SZ (0xe0)
/* Low power section */
#define MT6833_DGB_TRACE_LP_BASE \
(MT6833_DGB_TRACE_SUSPEND_BASE + MT6833_DGB_TRACE_SUSPEND_SZ)
#define MT6833_DGB_TRACE_LP_SZ (0x200)
struct MT6833_LPM_TRACE_NODE {
struct mtk_lp_sysfs_handle handle;
struct mtk_lp_sysfs_op op;
};
struct mtk_lp_sysfs_handle mt6833_entry_lpm_trace;
struct MT6833_LPM_TRACE_NODE mt6833_lpm_trace_node_comm;
struct MT6833_LPM_TRACE_NODE mt6833_lpm_trace_node_lp;
struct MT6833_LPM_TRACE_NODE mt6833_lpm_trace_node_suspend;
struct MT6833_TRACE_COMM {
unsigned int magic;
unsigned int common_fp;
unsigned int rc_timestamp_h;
unsigned int rc_timestamp_l;
unsigned int rc_info;
unsigned int rc_fp;
unsigned int rc_valid;
};
static ssize_t mt6833_dbg_lpm_trace_suspend_show(char *ToUserBuf,
size_t sz, void *priv)
{
struct MTK_LPM_PLAT_TRACE trace;
unsigned int dump[4], offset = 0;
size_t len = 0;
int ret = 0;
ret = mtk_lpm_platform_trace_get(MT_LPM_PLAT_TRACE_SYSRAM, &trace);
if (ret || !trace.read)
return -ENOENT;
while (((offset + sizeof(dump)) < MT6833_DGB_TRACE_SUSPEND_SZ)) {
dump[0] = dump[1] = dump[2] = dump[3] = 0;
trace.read((MT6833_DGB_TRACE_SUSPEND_BASE + offset),
dump, sizeof(dump));
mt6833_dbg_lpm_trace_log(ToUserBuf, sz, len,
"%03xh: %08x %08x %08x %08x\n", offset,
dump[0], dump[1], dump[2], dump[3]);
offset += sizeof(dump);
}
return len;
}
static ssize_t mt6833_dbg_lpm_trace_lp_show(char *ToUserBuf,
size_t sz, void *priv)
{
struct MTK_LPM_PLAT_TRACE trace;
unsigned int dump[4], offset = 0;
size_t len = 0;
int ret = 0;
ret = mtk_lpm_platform_trace_get(MT_LPM_PLAT_TRACE_SYSRAM, &trace);
if (ret || !trace.read)
return -ENOENT;
while (((offset + sizeof(dump)) <= MT6833_DGB_TRACE_LP_SZ)) {
dump[0] = dump[1] = dump[2] = dump[3] = 0;
trace.read((MT6833_DGB_TRACE_LP_BASE + offset),
dump, sizeof(dump));
mt6833_dbg_lpm_trace_log(ToUserBuf, sz, len,
"%03xh: %08x %08x %08x %08x\n", offset,
dump[0], dump[1], dump[2], dump[3]);
offset += sizeof(dump);
}
return len;
}
static ssize_t mt6833_dbg_lpm_trace_comm_show(char *ToUserBuf,
size_t sz, void *priv)
{
struct MTK_LPM_PLAT_TRACE trace;
struct MT6833_TRACE_COMM *data = NULL;
size_t len = 0, rSz = 0;
int ret = 0;
ret = mtk_lpm_platform_trace_get(MT_LPM_PLAT_TRACE_SYSRAM, &trace);
if (ret || !trace.read)
return -ENOENT;
data = kzalloc(sizeof(struct MT6833_TRACE_COMM),
GFP_KERNEL);
if (!data)
return -ENOMEM;
rSz = (sizeof(struct MT6833_TRACE_COMM) > MT6833_DGB_TRACE_COMMON_SZ) ?
MT6833_DGB_TRACE_COMMON_SZ : sizeof(struct MT6833_TRACE_COMM);
len = trace.read(MT6833_DGB_TRACE_COMMON_BASE, data, rSz);
if (len > 0) {
unsigned long long timestamp;
len = 0;
timestamp = data->rc_timestamp_h;
timestamp = (timestamp << 32) | data->rc_timestamp_l;
mt6833_dbg_lpm_trace_log(ToUserBuf, sz, len,
"magic: 0x%x\n", data->magic);
mt6833_dbg_lpm_trace_log(ToUserBuf, sz, len,
"common_footfprint: 0x%x\n", data->common_fp);
#if BITS_PER_LONG == 32
/* mt6833_dbg_lpm_trace_log(ToUserBuf, sz, len,
"timestamp: %llu.%03llu\n",
div_u64(timestamp, 1000000000),
timestamp%1000000000);
*/
#else
mt6833_dbg_lpm_trace_log(ToUserBuf, sz, len,
"timestamp: %llu.%03llu\n",
timestamp/1000000000,
timestamp%1000000000);
#endif
mt6833_dbg_lpm_trace_log(ToUserBuf, sz, len,
"last constriant\n - cpu:%d\n - rc_id:%d\n - valid:0x%x\n",
TRACE_DBG_LAST_RC_INFO_CPU(data->rc_info),
TRACE_DBG_LAST_RC_INFO_RC_ID(data->rc_info),
data->rc_valid);
}
kfree(data);
return sz;
}
int mt6833_dbg_lpm_init(void)
{
mtk_lpm_sysfs_root_entry_create();
mtk_lpm_sysfs_sub_entry_add("trace", 0644, NULL,
&mt6833_entry_lpm_trace);
MT6833_GENERIC_TRACE_NODE_INIT(mt6833_lpm_trace_node_comm,
mt6833_dbg_lpm_trace_comm_show,
NULL);
mtk_lpm_sysfs_sub_entry_node_add("common", 0400,
&mt6833_lpm_trace_node_comm.op,
&mt6833_entry_lpm_trace,
&mt6833_lpm_trace_node_comm.handle);
MT6833_GENERIC_TRACE_NODE_INIT(mt6833_lpm_trace_node_lp,
mt6833_dbg_lpm_trace_lp_show,
NULL);
mtk_lpm_sysfs_sub_entry_node_add("lp", 0400,
&mt6833_lpm_trace_node_lp.op,
&mt6833_entry_lpm_trace,
&mt6833_lpm_trace_node_lp.handle);
MT6833_GENERIC_TRACE_NODE_INIT(mt6833_lpm_trace_node_suspend,
mt6833_dbg_lpm_trace_suspend_show,
NULL);
mtk_lpm_sysfs_sub_entry_node_add("suspend", 0400,
&mt6833_lpm_trace_node_suspend.op,
&mt6833_entry_lpm_trace,
&mt6833_lpm_trace_node_suspend.handle);
return 0;
}
int mt6833_dbg_lpm_deinit(void)
{
mtk_lpm_sysfs_entry_node_remove(&mt6833_lpm_trace_node_suspend.handle);
mtk_lpm_sysfs_entry_node_remove(&mt6833_lpm_trace_node_lp.handle);
mtk_lpm_sysfs_entry_node_remove(&mt6833_lpm_trace_node_comm.handle);
mtk_lpm_sysfs_entry_node_remove(&mt6833_entry_lpm_trace);
return 0;
}
@@ -0,0 +1,624 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/console.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/spinlock.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mtk_resource_constraint_v1.h>
#include <mt6833_pwr_ctrl.h>
#include <mt6833_dbg_fs_common.h>
#include <mt6833_cond.h>
#include <mt6833_spm_comm.h>
#include <mtk_lpm_sysfs.h>
#include <mtk_lp_sysfs.h>
#include <mtk_lpm_timer.h>
#define SPM_RC_UPDATE_COND_ID_MASK 0xffff
#define SPM_RC_UPDATE_COND_RC_ID_MASK 0xffff
#define SPM_RC_UPDATE_COND_RC_ID_SHIFT (16)
#define SPM_RC_UPDATE_COND_CTRL_ID(rc, cond)\
(((rc & SPM_RC_UPDATE_COND_RC_ID_MASK)\
<< SPM_RC_UPDATE_COND_RC_ID_SHIFT)\
| (cond & SPM_RC_UPDATE_COND_ID_MASK))
#define MT6833_LPM_RC_RATIO_DEFAULT 10000
#define mt6833_rc_log(buf, sz, len, fmt, args...) ({\
if (len < sz)\
len += scnprintf(buf + len, sz - len,\
fmt, ##args); })
#define MT6833_DBG_SMC(_id, _act, _rc, _param) ({\
(unsigned long)mtk_lpm_smc_spm_dbg(_id, _act, _rc, _param); })
enum MT6833_RC_RATIO_TYPE {
MT6833_RC_RATIO_UNKNOWN,
MT6833_RC_RATIO_ENABLE,
MT6833_RC_RATIO_DISABLE,
MT6833_RC_RATIO_ENABLE_INFO,
MT6833_RC_RATIO_INFO,
};
enum MT6833_RC_NODE_TYPE {
MT6833_RC_NODE_STATE,
MT6833_RC_NODE_RC_ENABLE,
MT6833_RC_NODE_RC_STATE_SIMPLE,
MT6833_RC_NODE_RC_STATE,
MT6833_RC_NODE_COND_ENABLE,
MT6833_RC_NODE_COND_STATE,
MT6833_RC_NODE_COND_SET,
MT6833_RC_NODE_COND_CLR,
MT6833_RC_NODE_RATIO_ENABLE,
MT6833_RC_NODE_RATIO_INTERVAL,
MT6833_RC_NODE_VALID_BBLPM,
MT6833_RC_NODE_VALID_TRACE,
MT6833_RC_NODE_VALID_TIMESTAMP,
MT6833_RC_NODE_MAX
};
struct MT6833_RC_NODE {
const char *name;
int rc_id;
int type;
struct mtk_lp_sysfs_handle handle;
struct mtk_lp_sysfs_op op;
};
struct MT6833_RC_ENTERY {
const char *name;
struct mtk_lp_sysfs_handle handle;
};
struct MT6833_RC_COND_HANDLES {
struct MT6833_RC_ENTERY root;
struct MT6833_RC_NODE hSet;
struct MT6833_RC_NODE hClr;
struct MT6833_RC_NODE hState;
struct MT6833_RC_NODE hEnable;
};
struct MT6833_RC_VALID_HANDLES {
struct MT6833_RC_ENTERY root;
struct MT6833_RC_NODE hBblpm;
struct MT6833_RC_NODE hTrace;
struct MT6833_RC_NODE hTimestamp;
};
struct MT6833_RC_RATIO_HANDLES {
struct MT6833_RC_ENTERY root;
struct MT6833_RC_NODE hInterval;
struct MT6833_RC_NODE hEnable;
};
struct MT6833_RC_HANDLE_BASIC {
struct MT6833_RC_ENTERY root;
struct MT6833_RC_NODE hEnable;
struct MT6833_RC_NODE hState;
};
struct MT6833_RC_HANDLE {
struct MT6833_RC_HANDLE_BASIC basic;
struct MT6833_RC_COND_HANDLES hCond;
struct MT6833_RC_VALID_HANDLES valid;
};
#define MT6833_CONSTRAINT_GENERIC_OP(op, _priv) ({\
op.fs_read = mt6833_generic_rc_read;\
op.fs_write = mt6833_generic_rc_write;\
op.priv = _priv; })
#define MT6833_GENERIC_RC_NODE_INIT(_n, _name, _id, _type) ({\
_n.name = _name;\
_n.type = _type;\
_n.rc_id = _id;\
MT6833_CONSTRAINT_GENERIC_OP(_n.op, &_n); })
struct mtk_lp_sysfs_handle mt6833_entry_rc;
struct MT6833_RC_HANDLE rc_dram;
struct MT6833_RC_HANDLE rc_syspll;
struct MT6833_RC_HANDLE rc_bus26m;
struct MT6833_RC_HANDLE_BASIC rc_cpu_buckldo;
struct MT6833_RC_RATIO_HANDLES rc_Ratio;
struct MT6833_RC_NODE rc_state;
struct mt_lpm_timer rc_ratio_timer;
static char *mt6833_spm_cond_cg_str[PLAT_SPM_COND_MAX] = {
[PLAT_SPM_COND_MTCMOS_0] = "MTCMOS_0",
[PLAT_SPM_COND_CG_INFRA_0] = "INFRA_0",
[PLAT_SPM_COND_CG_INFRA_1] = "INFRA_1",
[PLAT_SPM_COND_CG_INFRA_2] = "INFRA_2",
[PLAT_SPM_COND_CG_INFRA_3] = "INFRA_3",
[PLAT_SPM_COND_CG_INFRA_4] = "INFRA_4",
[PLAT_SPM_COND_CG_INFRA_5] = "INFRA_5",
[PLAT_SPM_COND_CG_MMSYS_0] = "MMSYS_0",
[PLAT_SPM_COND_CG_MMSYS_1] = "MMSYS_1",
[PLAT_SPM_COND_CG_MMSYS_2] = "MMSYS_2",
[PLAT_SPM_COND_CG_MMSYS_3] = "MMSYS_3",
};
static char *mt6833_spm_cond_pll_str[PLAT_SPM_COND_PLL_MAX] = {
[PLAT_SPM_COND_UNIVPLL] = "UNIVPLL",
[PLAT_SPM_COND_MFGPLL] = "MFGPLL",
[PLAT_SPM_COND_MSDCPLL] = "MSDCPLL",
[PLAT_SPM_COND_TVPLL] = "TVPLL",
[PLAT_SPM_COND_MMPLL] = "MMPLL",
};
int mt6833_lpm_rc_cond_ctrl(int rc_id, unsigned int act,
unsigned int cond_id, unsigned int value)
{
unsigned int cond_ctrl_id;
int res = 0;
cond_ctrl_id = SPM_RC_UPDATE_COND_CTRL_ID(rc_id, cond_id);
if (cond_id < PLAT_SPM_COND_MAX)
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_COND_CTRL,
act, cond_ctrl_id, value);
else if ((cond_id - PLAT_SPM_COND_MAX) < PLAT_SPM_COND_PLL_MAX)
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_COND_CTRL,
act, cond_ctrl_id, !!value);
else
pr_info("[%s:%d] - unknown cond id = %u\n",
__func__, __LINE__, cond_id);
return res;
}
int mt6833_lpm_rc_ratio_timer_func(unsigned long long dur, void *priv)
{
/* spm tick to ms: tick/32 (ms) */
#define RC_RESIDENCY_COVERT(x) (x>>5)
#if BITS_PER_LONG == 32
#define RC_COVERT_RATIO(dur, x)\
(div_u64((RC_RESIDENCY_COVERT(x) * 100), dur))
#else
#define RC_COVERT_RATIO(dur, x)\
((RC_RESIDENCY_COVERT(x) * 100)/dur)
#endif
unsigned long long rc_dram_t;
unsigned long long rc_syspll_t;
unsigned long long rc_bus26m_t;
rc_dram_t = (unsigned long long)
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_INFO,
(MT_LPM_SMC_ACT_GET | MT_LPM_SMC_ACT_CLR),
MT_RM_CONSTRAINT_ID_DRAM, 0);
rc_syspll_t = (unsigned long long)
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_INFO,
(MT_LPM_SMC_ACT_GET | MT_LPM_SMC_ACT_CLR),
MT_RM_CONSTRAINT_ID_SYSPLL, 0);
rc_bus26m_t = (unsigned long long)
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_INFO,
(MT_LPM_SMC_ACT_GET | MT_LPM_SMC_ACT_CLR),
MT_RM_CONSTRAINT_ID_BUS26M, 0);
pr_info("[name:spm&][RC] ratio, duration_ms:%llu, dram:%llu%%, syspll:%llu%%, bus26m:%llu%%\n",
dur, RC_COVERT_RATIO(dur, rc_dram_t),
RC_COVERT_RATIO(dur, rc_syspll_t),
RC_COVERT_RATIO(dur, rc_bus26m_t));
return 0;
}
static ssize_t mt6833_lpm_rc_block_info(int rc_id,
char *ToUserBuf,
size_t sz)
{
uint32_t block, b;
int i;
ssize_t len = 0;
block = (uint32_t)
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_COND_BLOCK,
MT_LPM_SMC_ACT_GET, rc_id, 0);
b = (uint32_t)
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_COND_CHECK,
MT_LPM_SMC_ACT_GET, rc_id, 0);
mt6833_rc_log(ToUserBuf, sz, len,
"blocked=%u, blocked_cond=0x%08x\n",
b, block);
for (i = 0, b = block >> SPM_COND_BLOCKED_CG_IDX;
i < PLAT_SPM_COND_MAX; i++)
mt6833_rc_log(ToUserBuf, sz, len,
"[%2d] %8s=0x%08lx\n", i,
mt6833_spm_cond_cg_str[i],
((b >> i) & 0x1) ?
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_BLOCK_DETAIL,
MT_LPM_SMC_ACT_GET, rc_id, i) : 0);
for (i = 0, b = block >> SPM_COND_BLOCKED_PLL_IDX;
i < PLAT_SPM_COND_PLL_MAX; i++)
mt6833_rc_log(ToUserBuf, sz, len,
"[%2d] %8s=%d\n",
(i + PLAT_SPM_COND_MAX),
mt6833_spm_cond_pll_str[i],
((b >> i) & 0x1));
return len;
}
static ssize_t mt6833_rc_state(int rc_id, char *ToUserBuf, size_t sz)
{
ssize_t len = 0;
if (rc_id < 0)
return 0;
mt6833_rc_log(ToUserBuf, sz, len,
"enable=%lu, count=%lu, rc-id=%d\n",
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_SWITCH,
MT_LPM_SMC_ACT_GET, rc_id, 0)
& MT_SPM_RC_VALID_SW,
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_CNT,
MT_LPM_SMC_ACT_GET, rc_id, 0),
rc_id);
return len;
}
static ssize_t mt6833_generic_rc_read(char *ToUserBuf,
size_t sz, void *priv)
{
ssize_t len = 0;
struct MT6833_RC_NODE *node = (struct MT6833_RC_NODE *)priv;
if (!node)
return -EINVAL;
switch (node->type) {
case MT6833_RC_NODE_STATE:
mt6833_rc_log(ToUserBuf, sz, len, "count:%lu\n",
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_IDLE_CNT,
MT_LPM_SMC_ACT_GET, DBG_CTRL_COUNT, 0));
break;
case MT6833_RC_NODE_RC_STATE_SIMPLE:
len += mt6833_rc_state(node->rc_id, ToUserBuf, sz);
break;
case MT6833_RC_NODE_RC_STATE:
len += mt6833_rc_state(node->rc_id, ToUserBuf, sz);
len += mt6833_lpm_rc_block_info(node->rc_id,
ToUserBuf + len, sz - len);
break;
case MT6833_RC_NODE_RC_ENABLE:
mt6833_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_SWITCH,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6833_RC_NODE_COND_ENABLE:
mt6833_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_COND_CHECK,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6833_RC_NODE_COND_STATE:
len += mt6833_lpm_rc_block_info(node->rc_id, ToUserBuf, sz);
break;
case MT6833_RC_NODE_RATIO_ENABLE:
mt6833_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_CTRL,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6833_RC_NODE_RATIO_INTERVAL:
mt6833_rc_log(ToUserBuf, sz, len, "%lu\n",
mtk_lpm_timer_interval(&rc_ratio_timer));
break;
case MT6833_RC_NODE_VALID_BBLPM:
mt6833_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_BBLPM,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6833_RC_NODE_VALID_TRACE:
mt6833_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6833_RC_NODE_VALID_TIMESTAMP:
mt6833_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE_TIME,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
}
return len;
}
static ssize_t mt6833_generic_rc_write(char *FromUserBuf,
size_t sz, void *priv)
{
struct MT6833_RC_NODE *node = (struct MT6833_RC_NODE *)priv;
if (!node)
return -EINVAL;
if ((node->type == MT6833_RC_NODE_RC_ENABLE) ||
(node->type == MT6833_RC_NODE_COND_ENABLE) ||
(node->type == MT6833_RC_NODE_VALID_BBLPM) ||
(node->type == MT6833_RC_NODE_VALID_TRACE) ||
(node->type == MT6833_RC_NODE_VALID_TIMESTAMP)) {
unsigned int parm;
int cmd;
if ((!kstrtoint(FromUserBuf, 10, &parm)) == 1) {
cmd = (node->type == MT6833_RC_NODE_RC_ENABLE) ?
MT_SPM_DBG_SMC_UID_RC_SWITCH :
(node->type == MT6833_RC_NODE_COND_ENABLE) ?
MT_SPM_DBG_SMC_UID_COND_CHECK :
(node->type == MT6833_RC_NODE_VALID_BBLPM) ?
MT_SPM_DBG_SMC_UID_RC_BBLPM :
(node->type == MT6833_RC_NODE_VALID_TRACE) ?
MT_SPM_DBG_SMC_UID_RC_TRACE :
(node->type == MT6833_RC_NODE_VALID_TIMESTAMP) ?
MT_SPM_DBG_SMC_UID_RC_TRACE_TIME : -1;
if (cmd < 0)
return -EINVAL;
if (!!parm)
parm = MT_LPM_SMC_ACT_SET;
else
parm = MT_LPM_SMC_ACT_CLR;
MT6833_DBG_SMC(cmd, parm, node->rc_id, 0);
}
} else if ((node->type == MT6833_RC_NODE_COND_SET)
|| (node->type == MT6833_RC_NODE_COND_CLR)) {
unsigned int parm1, parm2, act;
if (sscanf(FromUserBuf, "%u %x", &parm1, &parm2) == 2) {
act = (node->type == MT6833_RC_NODE_COND_SET) ?
MT_LPM_SMC_ACT_SET :
(node->type == MT6833_RC_NODE_COND_CLR) ?
MT_LPM_SMC_ACT_CLR : 0;
if (act != 0)
mt6833_lpm_rc_cond_ctrl(node->rc_id, act,
parm1, parm2);
}
} else if ((node->type == MT6833_RC_NODE_RATIO_ENABLE)
|| (node->type == MT6833_RC_NODE_RATIO_INTERVAL)) {
unsigned int parm;
if ((!kstrtoint(FromUserBuf, 10, &parm)) == 1) {
if (node->type == MT6833_RC_NODE_RATIO_ENABLE) {
parm = (!!parm) ? MT_LPM_SMC_ACT_SET :
MT_LPM_SMC_ACT_CLR;
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_CTRL,
parm, MT_RM_CONSTRAINT_ID_DRAM, 0);
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_CTRL,
parm, MT_RM_CONSTRAINT_ID_SYSPLL, 0);
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_CTRL,
parm, MT_RM_CONSTRAINT_ID_BUS26M, 0);
if (parm == MT_LPM_SMC_ACT_SET)
mtk_lpm_timer_start(&rc_ratio_timer);
else
mtk_lpm_timer_stop(&rc_ratio_timer);
} else if (node->type == MT6833_RC_NODE_RATIO_INTERVAL)
mtk_lpm_timer_interval_update(&rc_ratio_timer,
(unsigned long long)parm);
}
}
return sz;
}
static int mt6833_lpm_rc_node_add(struct MT6833_RC_NODE *n,
int mode, struct MT6833_RC_ENTERY *p)
{
return mtk_lpm_sysfs_sub_entry_node_add(n->name, mode,
&n->op, &p->handle, &n->handle);
}
static int mt6833_lpm_rc_entry_add(struct MT6833_RC_ENTERY *n,
int mode, struct MT6833_RC_ENTERY *p)
{
return mtk_lpm_sysfs_sub_entry_add(n->name, mode,
&p->handle, &n->handle);
}
static int mt6833_lpm_rc_valid_node_add(int rc_id,
struct MT6833_RC_ENTERY *parent,
struct MT6833_RC_VALID_HANDLES *valid)
{
int bRet = 0;
if (!valid || !parent)
return -EINVAL;
valid->root.name = "valid";
bRet = mt6833_lpm_rc_entry_add(&valid->root, 0644, parent);
if (!bRet) {
MT6833_GENERIC_RC_NODE_INIT(valid->hBblpm, "bblpm",
rc_id, MT6833_RC_NODE_VALID_BBLPM);
mt6833_lpm_rc_node_add(&valid->hBblpm, 0200, &valid->root);
MT6833_GENERIC_RC_NODE_INIT(valid->hTrace, "trace",
rc_id, MT6833_RC_NODE_VALID_TRACE);
mt6833_lpm_rc_node_add(&valid->hTrace, 0200, &valid->root);
MT6833_GENERIC_RC_NODE_INIT(valid->hTimestamp, "timestamp",
rc_id, MT6833_RC_NODE_VALID_TIMESTAMP);
mt6833_lpm_rc_node_add(&valid->hTimestamp, 0200, &valid->root);
}
return bRet;
}
static int mt6833_lpm_rc_ratio_entry(struct mtk_lp_sysfs_handle *parent,
struct MT6833_RC_RATIO_HANDLES *ratio)
{
int bRet = 0;
if (!ratio || !parent)
return -EINVAL;
ratio->root.name = "ratio";
bRet = mtk_lpm_sysfs_sub_entry_add(ratio->root.name, 0644,
parent, &ratio->root.handle);
if (!bRet) {
MT6833_GENERIC_RC_NODE_INIT(ratio->hEnable, "enable",
0, MT6833_RC_NODE_RATIO_ENABLE);
mt6833_lpm_rc_node_add(&ratio->hEnable, 0200, &ratio->root);
MT6833_GENERIC_RC_NODE_INIT(ratio->hInterval, "interval",
0, MT6833_RC_NODE_RATIO_INTERVAL);
mt6833_lpm_rc_node_add(&ratio->hInterval, 0200, &ratio->root);
}
return 0;
}
static int mt6833_lpm_rc_cond_node_add(int rc_id,
struct MT6833_RC_ENTERY *parent,
struct MT6833_RC_COND_HANDLES *cond)
{
int bRet = 0;
if (!cond || !parent)
return -EINVAL;
cond->root.name = "cond";
bRet = mt6833_lpm_rc_entry_add(&cond->root, 0644, parent);
if (!bRet) {
MT6833_GENERIC_RC_NODE_INIT(cond->hSet, "set",
rc_id, MT6833_RC_NODE_COND_SET);
mt6833_lpm_rc_node_add(&cond->hSet, 0200, &cond->root);
MT6833_GENERIC_RC_NODE_INIT(cond->hClr, "clr",
rc_id, MT6833_RC_NODE_COND_CLR);
mt6833_lpm_rc_node_add(&cond->hClr, 0200, &cond->root);
MT6833_GENERIC_RC_NODE_INIT(cond->hEnable, "enable",
rc_id, MT6833_RC_NODE_COND_ENABLE);
mt6833_lpm_rc_node_add(&cond->hEnable, 0644, &cond->root);
MT6833_GENERIC_RC_NODE_INIT(cond->hState, "state",
rc_id, MT6833_RC_NODE_COND_STATE);
mt6833_lpm_rc_node_add(&cond->hState, 0444, &cond->root);
}
return bRet;
}
static int mt6833_lpm_rc_entry_nodes_basic(int IsSimple,
const char *name, int rc_id,
struct mtk_lp_sysfs_handle *parent,
struct MT6833_RC_HANDLE_BASIC *rc)
{
int bRet = 0;
if (!parent || !rc)
return -EINVAL;
rc->root.name = name;
bRet = mtk_lpm_sysfs_sub_entry_add(rc->root.name, 0644,
parent, &rc->root.handle);
if (bRet)
return -EINVAL;
MT6833_GENERIC_RC_NODE_INIT(rc->hState, "state", rc_id,
(IsSimple) ? MT6833_RC_NODE_RC_STATE_SIMPLE
: MT6833_RC_NODE_RC_STATE);
mt6833_lpm_rc_node_add(&rc->hState, 0444, &rc->root);
MT6833_GENERIC_RC_NODE_INIT(rc->hEnable, "enable", rc_id,
MT6833_RC_NODE_RC_ENABLE);
mt6833_lpm_rc_node_add(&rc->hEnable, 0644, &rc->root);
return bRet;
}
int mt6833_lpm_rc_entry_nodes(const char *name, int rc_id,
struct mtk_lp_sysfs_handle *parent,
struct MT6833_RC_HANDLE *rc)
{
int bRet = 0;
if (!parent || !rc)
return -EINVAL;
memset(rc, 0, sizeof(struct MT6833_RC_HANDLE));
bRet = mt6833_lpm_rc_entry_nodes_basic(0, name, rc_id,
parent, &rc->basic);
bRet = mt6833_lpm_rc_cond_node_add(rc_id, &rc->basic.root,
&rc->hCond);
bRet = mt6833_lpm_rc_valid_node_add(rc_id, &rc->basic.root,
&rc->valid);
return 0;
}
int mt6833_dbg_lpm_fs_init(void)
{
/* enable resource constraint condition block latch */
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_BLOCK_LATCH,
MT_LPM_SMC_ACT_SET, 0, 0);
mtk_lpm_sysfs_root_entry_create();
mtk_lpm_sysfs_sub_entry_add("rc", 0644, NULL,
&mt6833_entry_rc);
MT6833_GENERIC_RC_NODE_INIT(rc_state, "state",
0, MT6833_RC_NODE_STATE);
mtk_lpm_sysfs_sub_entry_node_add(rc_state.name, 0444,
&rc_state.op, &mt6833_entry_rc,
&rc_state.handle);
mt6833_lpm_rc_ratio_entry(&mt6833_entry_rc, &rc_Ratio);
mt6833_lpm_rc_entry_nodes_basic(1, "cpu-buck-ldo",
MT_RM_CONSTRAINT_ID_CPU_BUCK_LDO,
&mt6833_entry_rc, &rc_cpu_buckldo);
mt6833_lpm_rc_entry_nodes("dram", MT_RM_CONSTRAINT_ID_DRAM,
&mt6833_entry_rc, &rc_dram);
mt6833_lpm_rc_entry_nodes("syspll", MT_RM_CONSTRAINT_ID_SYSPLL,
&mt6833_entry_rc, &rc_syspll);
mt6833_lpm_rc_entry_nodes("bus26m", MT_RM_CONSTRAINT_ID_BUS26M,
&mt6833_entry_rc, &rc_bus26m);
rc_ratio_timer.timeout = mt6833_lpm_rc_ratio_timer_func;
mtk_lpm_timer_init(&rc_ratio_timer, MTK_LPM_TIMER_REPEAT);
mtk_lpm_timer_interval_update(&rc_ratio_timer,
MT6833_LPM_RC_RATIO_DEFAULT);
/* enable constraint tracing */
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_SET, MT_RM_CONSTRAINT_ID_BUS26M, 0);
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_SET, MT_RM_CONSTRAINT_ID_SYSPLL, 0);
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_SET, MT_RM_CONSTRAINT_ID_DRAM, 0);
return 0;
}
int mt6833_dbg_lpm_fs_deinit(void)
{
/* disable resource contraint condition block latch */
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_BLOCK_LATCH,
MT_LPM_SMC_ACT_CLR, 0, 0);
/* disable constraint tracing */
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_CLR, MT_RM_CONSTRAINT_ID_BUS26M, 0);
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_CLR, MT_RM_CONSTRAINT_ID_SYSPLL, 0);
MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_CLR, MT_RM_CONSTRAINT_ID_DRAM, 0);
return 0;
}
@@ -0,0 +1,469 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/spinlock.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mtk_idle_sysfs.h>
#include <mtk_suspend_sysfs.h>
#include <mtk_spm_sysfs.h>
#include <mt6833_pwr_ctrl.h>
#include <mt6833_dbg_fs_common.h>
#include <mt6833_cond.h>
#include <mt6833_spm_comm.h>
/* Determine for node route */
#define MT_LP_RQ_NODE "/sys/kernel/debug/spm/spm_resource_req"
#define DEFINE_ATTR_RO(_name) \
static struct kobj_attribute _name##_attr = { \
.attr = { \
.name = #_name, \
.mode = 0444, \
}, \
.show = _name##_show, \
}
#define DEFINE_ATTR_RW(_name) \
static struct kobj_attribute _name##_attr = { \
.attr = { \
.name = #_name, \
.mode = 0644, \
}, \
.show = _name##_show, \
.store = _name##_store, \
}
#define __ATTR_OF(_name) (&_name##_attr.attr)
#undef mtk_dbg_spm_log
#define mtk_dbg_spm_log(fmt, args...) \
do { \
int l = scnprintf(p, sz, fmt, ##args); \
p += l; \
sz -= l; \
} while (0)
static char *mt6833_pwr_ctrl_str[PW_MAX_COUNT] = {
[PW_PCM_FLAGS] = "pcm_flags",
[PW_PCM_FLAGS_CUST] = "pcm_flags_cust",
[PW_PCM_FLAGS_CUST_SET] = "pcm_flags_cust_set",
[PW_PCM_FLAGS_CUST_CLR] = "pcm_flags_cust_clr",
[PW_PCM_FLAGS1] = "pcm_flags1",
[PW_PCM_FLAGS1_CUST] = "pcm_flags1_cust",
[PW_PCM_FLAGS1_CUST_SET] = "pcm_flags1_cust_set",
[PW_PCM_FLAGS1_CUST_CLR] = "pcm_flags1_cust_clr",
[PW_TIMER_VAL] = "timer_val",
[PW_TIMER_VAL_CUST] = "timer_val_cust",
[PW_TIMER_VAL_RAMP_EN] = "timer_val_ramp_en",
[PW_TIMER_VAL_RAMP_EN_SEC] = "timer_val_ramp_en_sec",
[PW_WAKE_SRC] = "wake_src",
[PW_WAKE_SRC_CUST] = "wake_src_cust",
[PW_WAKELOCK_TIMER_VAL] = "wakelock_timer_val",
[PW_WDT_DISABLE] = "wdt_disable",
/* SPM_CLK_CON */
[PW_REG_SRCCLKEN0_CTL] = "reg_srcclken0_ctl",
[PW_REG_SRCCLKEN1_CTL] = "reg_srcclken1_ctl",
[PW_REG_SPM_LOCK_INFRA_DCM] = "reg_spm_lock_infra_dcm",
[PW_REG_SRCCLKEN_MASK] = "reg_srcclken_mask",
[PW_REG_MD1_C32RM_EN] = "reg_md1_c32rm_en",
[PW_REG_MD2_C32RM_EN] = "reg_md2_c32rm_en",
[PW_REG_CLKSQ0_SEL_CTRL] = "reg_clksq0_sel_ctrl",
[PW_REG_CLKSQ1_SEL_CTRL] = "reg_clksq1_sel_ctrl",
[PW_REG_SRCCLKEN0_EN] = "reg_srcclken0_en",
[PW_REG_SRCCLKEN1_EN] = "reg_srcclken1_en",
[PW_REG_SYSCLK0_SRC_MASK_B] = "reg_sysclk0_src_mask_b",
[PW_REG_SYSCLK1_SRC_MASK_B] = "reg_sysclk1_src_mask_b",
/* SPM_AP_STANDBY_CON */
[PW_REG_WFI_OP] = "reg_wfi_op",
[PW_REG_WFI_TYPE] = "reg_wfi_type",
[PW_REG_MP0_CPUTOP_IDLE_MASK] = "reg_mp0_cputop_idle_mask",
[PW_REG_MP1_CPUTOP_IDLE_MASK] = "reg_mp1_cputop_idle_mask",
[PW_REG_MCUSYS_IDLE_MASK] = "reg_mcusys_idle_mask",
[PW_REG_MD_APSRC_1_SEL] = "reg_md_apsrc_1_sel",
[PW_REG_MD_APSRC_0_SEL] = "reg_md_apsrc_0_sel",
[PW_REG_CONN_APSRC_SEL] = "reg_conn_apsrc_sel",
/* SPM_SRC6_MASK */
[PW_REG_CCIF_EVENT_INFRA_REQ_MASK_B] =
"reg_ccif_event_infra_req_mask_b",
[PW_REG_CCIF_EVENT_APSRC_REQ_MASK_B] =
"reg_ccif_event_apsrc_req_mask_b",
/* SPM_SRC_REQ */
[PW_REG_SPM_APSRC_REQ] = "reg_spm_apsrc_req",
[PW_REG_SPM_F26M_REQ] = "reg_spm_f26m_req",
[PW_REG_SPM_INFRA_REQ] = "reg_spm_infra_req",
[PW_REG_SPM_VRF18_REQ] = "reg_spm_vrf18_req",
[PW_REG_SPM_DDREN_REQ] = "reg_spm_ddren_req",
[PW_REG_SPM_DVFS_REQ] = "reg_spm_dvfs_req",
[PW_REG_SPM_SW_MAILBOX_REQ] = "reg_spm_sw_mailbox_req",
[PW_REG_SPM_SSPM_MAILBOX_REQ] = "reg_spm_sspm_mailbox_req",
[PW_REG_SPM_ADSP_MAILBOX_REQ] = "reg_spm_adsp_mailbox_req",
[PW_REG_SPM_SCP_MAILBOX_REQ] = "reg_spm_scp_mailbox_req",
/* SPM_SRC_MASK */
[PW_REG_MD_0_SRCCLKENA_MASK_B] = "reg_md_0_srcclkena_mask_b",
[PW_REG_MD_0_INFRA_REQ_MASK_B] = "reg_md_0_infra_req_mask_b",
[PW_REG_MD_0_APSRC_REQ_MASK_B] = "reg_md_0_apsrc_req_mask_b",
[PW_REG_MD_0_VRF18_REQ_MASK_B] = "reg_md_0_vrf18_req_mask_b",
[PW_REG_MD_0_DDREN_REQ_MASK_B] = "reg_md_0_ddren_req_mask_b",
[PW_REG_MD_1_SRCCLKENA_MASK_B] = "reg_md_1_srcclkena_mask_b",
[PW_REG_MD_1_INFRA_REQ_MASK_B] = "reg_md_1_infra_req_mask_b",
[PW_REG_MD_1_APSRC_REQ_MASK_B] = "reg_md_1_apsrc_req_mask_b",
[PW_REG_MD_1_VRF18_REQ_MASK_B] = "reg_md_1_vrf18_req_mask_b",
[PW_REG_MD_1_DDREN_REQ_MASK_B] = "reg_md_1_ddren_req_mask_b",
[PW_REG_CONN_SRCCLKENA_MASK_B] = "reg_conn_srcclkena_mask_b",
[PW_REG_CONN_SRCCLKENB_MASK_B] = "reg_conn_srcclkenb_mask_b",
[PW_REG_CONN_INFRA_REQ_MASK_B] = "reg_conn_infra_req_mask_b",
[PW_REG_CONN_APSRC_REQ_MASK_B] = "reg_conn_apsrc_req_mask_b",
[PW_REG_CONN_VRF18_REQ_MASK_B] = "reg_conn_vrf18_req_mask_b",
[PW_REG_CONN_DDREN_REQ_MASK_B] = "reg_conn_ddren_req_mask_b",
[PW_REG_CONN_VFE28_MASK_B] = "reg_conn_vfe28_mask_b",
[PW_REG_SRCCLKENI_SRCCLKENA_MASK_B] = "reg_srcclkeni_srcclkena_mask_b",
[PW_REG_SRCCLKENI_INFRA_REQ_MASK_B] = "reg_srcclkeni_infra_req_mask_b",
[PW_REG_INFRASYS_APSRC_REQ_MASK_B] = "reg_infrasys_apsrc_req_mask_b",
[PW_REG_INFRASYS_DDREN_REQ_MASK_B] = "reg_infrasys_ddren_req_mask_b",
[PW_REG_SSPM_SRCCLKENA_MASK_B] = "reg_sspm_srcclkena_mask_b",
[PW_REG_SSPM_INFRA_REQ_MASK_B] = "reg_sspm_infra_req_mask_b",
[PW_REG_SSPM_APSRC_REQ_MASK_B] = "reg_sspm_apsrc_req_mask_b",
[PW_REG_SSPM_VRF18_REQ_MASK_B] = "reg_sspm_vrf18_req_mask_b",
[PW_REG_SSPM_DDREN_REQ_MASK_B] = "reg_sspm_ddren_req_mask_b",
/* SPM_SRC2_MASK */
[PW_REG_SCP_SRCCLKENA_MASK_B] = "reg_scp_srcclkena_mask_b",
[PW_REG_SCP_INFRA_REQ_MASK_B] = "reg_scp_infra_req_mask_b",
[PW_REG_SCP_APSRC_REQ_MASK_B] = "reg_scp_apsrc_req_mask_b",
[PW_REG_SCP_VRF18_REQ_MASK_B] = "reg_scp_vrf18_req_mask_b",
[PW_REG_SCP_DDREN_REQ_MASK_B] = "reg_scp_ddren_req_mask_b",
[PW_REG_AUDIO_DSP_SRCCLKENA_MASK_B] = "reg_audio_dsp_srcclkena_mask_b",
[PW_REG_AUDIO_DSP_INFRA_REQ_MASK_B] = "reg_audio_dsp_infra_req_mask_b",
[PW_REG_AUDIO_DSP_APSRC_REQ_MASK_B] = "reg_audio_dsp_apsrc_req_mask_b",
[PW_REG_AUDIO_DSP_VRF18_REQ_MASK_B] = "reg_audio_dsp_vrf18_req_mask_b",
[PW_REG_AUDIO_DSP_DDREN_REQ_MASK_B] = "reg_audio_dsp_ddren_req_mask_b",
[PW_REG_UFS_SRCCLKENA_MASK_B] = "reg_ufs_srcclkena_mask_b",
[PW_REG_UFS_INFRA_REQ_MASK_B] = "reg_ufs_infra_req_mask_b",
[PW_REG_UFS_APSRC_REQ_MASK_B] = "reg_ufs_apsrc_req_mask_b",
[PW_REG_UFS_VRF18_REQ_MASK_B] = "reg_ufs_vrf18_req_mask_b",
[PW_REG_UFS_DDREN_REQ_MASK_B] = "reg_ufs_ddren_req_mask_b",
[PW_REG_DISP0_APSRC_REQ_MASK_B] = "reg_disp0_apsrc_req_mask_b",
[PW_REG_DISP0_DDREN_REQ_MASK_B] = "reg_disp0_ddren_req_mask_b",
[PW_REG_DISP1_APSRC_REQ_MASK_B] = "reg_disp1_apsrc_req_mask_b",
[PW_REG_DISP1_DDREN_REQ_MASK_B] = "reg_disp1_ddren_req_mask_b",
[PW_REG_GCE_INFRA_REQ_MASK_B] = "reg_gce_infra_req_mask_b",
[PW_REG_GCE_APSRC_REQ_MASK_B] = "reg_gce_apsrc_req_mask_b",
[PW_REG_GCE_VRF18_REQ_MASK_B] = "reg_gce_vrf18_req_mask_b",
[PW_REG_GCE_DDREN_REQ_MASK_B] = "reg_gce_ddren_req_mask_b",
[PW_REG_APU_SRCCLKENA_MASK_B] = "reg_apu_srcclkena_mask_b",
[PW_REG_APU_INFRA_REQ_MASK_B] = "reg_apu_infra_req_mask_b",
[PW_REG_APU_APSRC_REQ_MASK_B] = "reg_apu_apsrc_req_mask_b",
[PW_REG_APU_VRF18_REQ_MASK_B] = "reg_apu_vrf18_req_mask_b",
[PW_REG_APU_DDREN_REQ_MASK_B] = "reg_apu_ddren_req_mask_b",
[PW_REG_CG_CHECK_SRCCLKENA_MASK_B] = "reg_cg_check_srcclkena_mask_b",
[PW_REG_CG_CHECK_APSRC_REQ_MASK_B] = "reg_cg_check_apsrc_req_mask_b",
[PW_REG_CG_CHECK_VRF18_REQ_MASK_B] = "reg_cg_check_vrf18_req_mask_b",
[PW_REG_CG_CHECK_DDREN_REQ_MASK_B] = "reg_cg_check_ddren_req_mask_b",
/* SPM_SRC3_MASK */
[PW_REG_DVFSRC_EVENT_TRIGGER_MASK_B] =
"reg_dvfsrc_event_trigger_mask_b",
[PW_REG_SW2SPM_WAKEUP_MASK_B] = "reg_sw2spm_wakeup_mask_b",
[PW_REG_ADSP2SPM_WAKEUP_MASK_B] = "reg_adsp2spm_wakeup_mask_b",
[PW_REG_SSPM2SPM_WAKEUP_MASK_B] = "reg_sspm2spm_wakeup_mask_b",
[PW_REG_SCP2SPM_WAKEUP_MASK_B] = "reg_scp2spm_wakeup_mask_b",
[PW_REG_CSYSPWRUP_ACK_MASK] = "reg_csyspwrup_ack_mask",
[PW_REG_SPM_RESERVED_SRCCLKENA_MASK_B] =
"reg_spm_reserved_srcclkena_mask_b",
[PW_REG_SPM_RESERVED_INFRA_REQ_MASK_B] =
"reg_spm_reserved_infra_req_mask_b",
[PW_REG_SPM_RESERVED_APSRC_REQ_MASK_B] =
"reg_spm_reserved_apsrc_req_mask_b",
[PW_REG_SPM_RESERVED_VRF18_REQ_MASK_B] =
"reg_spm_reserved_vrf18_req_mask_b",
[PW_REG_SPM_RESERVED_DDREN_REQ_MASK_B] =
"reg_spm_reserved_ddren_req_mask_b",
[PW_REG_MCUPM_SRCCLKENA_MASK_B] = "reg_mcupm_srcclkena_mask_b",
[PW_REG_MCUPM_INFRA_REQ_MASK_B] = "reg_mcupm_infra_req_mask_b",
[PW_REG_MCUPM_APSRC_REQ_MASK_B] = "reg_mcupm_apsrc_req_mask_b",
[PW_REG_MCUPM_VRF18_REQ_MASK_B] = "reg_mcupm_vrf18_req_mask_b",
[PW_REG_MCUPM_DDREN_REQ_MASK_B] = "reg_mcupm_ddren_req_mask_b",
[PW_REG_MSDC0_SRCCLKENA_MASK_B] = "reg_msdc0_srcclkena_mask_b",
[PW_REG_MSDC0_INFRA_REQ_MASK_B] = "reg_msdc0_infra_req_mask_b",
[PW_REG_MSDC0_APSRC_REQ_MASK_B] = "reg_msdc0_apsrc_req_mask_b",
[PW_REG_MSDC0_VRF18_REQ_MASK_B] = "reg_msdc0_vrf18_req_mask_b",
[PW_REG_MSDC0_DDREN_REQ_MASK_B] = "reg_msdc0_ddren_req_mask_b",
[PW_REG_MSDC1_SRCCLKENA_MASK_B] = "reg_msdc1_srcclkena_mask_b",
[PW_REG_MSDC1_INFRA_REQ_MASK_B] = "reg_msdc1_infra_req_mask_b",
[PW_REG_MSDC1_APSRC_REQ_MASK_B] = "reg_msdc1_apsrc_req_mask_b",
[PW_REG_MSDC1_VRF18_REQ_MASK_B] = "reg_msdc1_vrf18_req_mask_b",
[PW_REG_MSDC1_DDREN_REQ_MASK_B] = "reg_msdc1_ddren_req_mask_b",
/* SPM_SRC4_MASK */
[PW_REG_CCIF_EVENT_SRCCLKENA_MASK_B] =
"reg_ccif_event_srcclkena_mask_b",
[PW_REG_BAK_PSRI_SRCCLKENA_MASK_B] = "reg_bak_psri_srcclkena_mask_b",
[PW_REG_BAK_PSRI_INFRA_REQ_MASK_B] = "reg_bak_psri_infra_req_mask_b",
[PW_REG_BAK_PSRI_APSRC_REQ_MASK_B] = "reg_bak_psri_apsrc_req_mask_b",
[PW_REG_BAK_PSRI_VRF18_REQ_MASK_B] = "reg_bak_psri_vrf18_req_mask_b",
[PW_REG_BAK_PSRI_DDREN_REQ_MASK_B] = "reg_bak_psri_ddren_req_mask_b",
[PW_REG_DRAMC_MD32_INFRA_REQ_MASK_B] =
"reg_dramc_md32_infra_req_mask_b",
[PW_REG_DRAMC_MD32_VRF18_REQ_MASK_B] =
"reg_dramc_md32_vrf18_req_mask_b",
[PW_REG_CONN_SRCCLKENB2PWRAP_MASK_B] =
"reg_conn_srcclkenb2pwrap_mask_b",
[PW_REG_DRAMC_MD32_APSRC_REQ_MASK_B] =
"reg_dramc_md32_apsrc_req_mask_b",
/* SPM_SRC5_MASK */
[PW_REG_MCUSYS_MERGE_APSRC_REQ_MASK_B] =
"reg_mcusys_merge_apsrc_req_mask_b",
[PW_REG_MCUSYS_MERGE_DDREN_REQ_MASK_B] =
"reg_mcusys_merge_ddren_req_mask_b",
[PW_REG_AFE_SRCCLKENA_MASK_B] = "reg_afe_srcclkena_mask_b",
[PW_REG_AFE_INFRA_REQ_MASK_B] = "reg_afe_infra_req_mask_b",
[PW_REG_AFE_APSRC_REQ_MASK_B] = "reg_afe_apsrc_req_mask_b",
[PW_REG_AFE_VRF18_REQ_MASK_B] = "reg_afe_vrf18_req_mask_b",
[PW_REG_AFE_DDREN_REQ_MASK_B] = "reg_afe_ddren_req_mask_b",
[PW_REG_MSDC2_SRCCLKENA_MASK_B] = "reg_msdc2_srcclkena_mask_b",
[PW_REG_MSDC2_INFRA_REQ_MASK_B] = "reg_msdc2_infra_req_mask_b",
[PW_REG_MSDC2_APSRC_REQ_MASK_B] = "reg_msdc2_apsrc_req_mask_b",
[PW_REG_MSDC2_VRF18_REQ_MASK_B] = "reg_msdc2_vrf18_req_mask_b",
[PW_REG_MSDC2_DDREN_REQ_MASK_B] = "reg_msdc2_ddren_req_mask_b",
/* SPM_SRC7_MASK */
[PW_REG_PCIE_SRCCLKENA_MASK_B] = "reg_pcie_srcclkena_mask_b",
[PW_REG_PCIE_INFRA_REQ_MASK_B] = "reg_pcie_infra_req_mask_b",
[PW_REG_PCIE_APSRC_REQ_MASK_B] = "reg_pcie_apsrc_req_mask_b",
[PW_REG_PCIE_VRF18_REQ_MASK_B] = "reg_pcie_vrf18_req_mask_b",
[PW_REG_PCIE_DDREN_REQ_MASK_B] = "reg_pcie_ddren_req_mask_b",
[PW_REG_DPMAIF_SRCCLKENA_MASK_B] = "reg_dpmaif_srcclkena_mask_b",
[PW_REG_DPMAIF_INFRA_REQ_MASK_B] = "reg_dpmaif_infra_req_mask_b",
[PW_REG_DPMAIF_APSRC_REQ_MASK_B] = "reg_dpmaif_apsrc_req_mask_b",
[PW_REG_DPMAIF_VRF18_REQ_MASK_B] = "reg_dpmaif_vrf18_req_mask_b",
[PW_REG_DPMAIF_DDREN_REQ_MASK_B] = "reg_dpmaif_ddren_req_mask_b",
/* SPM_WAKEUP_EVENT_MASK */
[PW_REG_WAKEUP_EVENT_MASK] = "reg_wakeup_event_mask",
/* SPM_WAKEUP_EVENT_EXT_MASK */
[PW_REG_EXT_WAKEUP_EVENT_MASK] = "reg_ext_wakeup_event_mask",
};
/**************************************
* xxx_ctrl_show Function
**************************************/
/* code gen by spm_pwr_ctrl_atf.pl, need struct pwr_ctrl */
static ssize_t mt6833_show_pwr_ctrl(int id, char *buf, size_t buf_sz)
{
char *p = buf;
size_t mSize = 0;
int i;
for (i = 0; i < PW_MAX_COUNT; i++) {
mSize += scnprintf(p + mSize, buf_sz - mSize,
"%s = 0x%zx\n",
mt6833_pwr_ctrl_str[i],
mtk_lpm_smc_spm_dbg(id, MT_LPM_SMC_ACT_GET, i, 0));
}
WARN_ON(buf_sz - mSize <= 0);
return mSize;
}
/**************************************
* xxx_ctrl_store Function
**************************************/
/* code gen by spm_pwr_ctrl_atf.pl, need struct pwr_ctrl */
static ssize_t mt6833_store_pwr_ctrl(int id, const char *buf, size_t count)
{
u32 val;
char cmd[64];
int i;
if (sscanf(buf, "%63s %x", cmd, &val) != 2)
return -EPERM;
pr_info("[SPM] pwr_ctrl: cmd = %s, val = 0x%x\n", cmd, val);
for (i = 0 ; i < PW_MAX_COUNT; i++) {
if (!strcmp(cmd, mt6833_pwr_ctrl_str[i])) {
mtk_lpm_smc_spm_dbg(id, MT_LPM_SMC_ACT_SET, i, val);
break;
}
}
return count;
}
static ssize_t suspend_ctrl_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return mt6833_show_pwr_ctrl(MT_SPM_DBG_SMC_UID_SUSPEND_PWR_CTRL,
buf, get_mtk_lp_kernfs_bufsz_max());
}
static ssize_t idle_ctrl_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return mt6833_show_pwr_ctrl(MT_SPM_DBG_SMC_UID_IDLE_PWR_CTRL,
buf, get_mtk_lp_kernfs_bufsz_max());
}
static ssize_t suspend_ctrl_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
return mt6833_store_pwr_ctrl(MT_SPM_DBG_SMC_UID_SUSPEND_PWR_CTRL,
buf, count);
}
static ssize_t idle_ctrl_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
return mt6833_store_pwr_ctrl(MT_SPM_DBG_SMC_UID_IDLE_PWR_CTRL,
buf, count);
}
/**************************************
* Init Function
**************************************/
DEFINE_ATTR_RW(suspend_ctrl);
DEFINE_ATTR_RW(idle_ctrl);
static struct attribute *pwrctrl_attrs[] = {
/* for spm_lp_scen.pwrctrl */
__ATTR_OF(suspend_ctrl),
__ATTR_OF(idle_ctrl),
/* must */
NULL,
};
static struct attribute_group pwrctrl_attr_group = {
.name = "spm",
.attrs = pwrctrl_attrs,
};
static char *mt6833_spm_resource_str[MT_SPM_RES_MAX] = {
[MT_SPM_RES_XO_FPM] = "XO_FPM",
[MT_SPM_RES_CK_26M] = "CK_26M",
[MT_SPM_RES_INFRA] = "INFRA",
[MT_SPM_RES_SYSPLL] = "SYSPLL",
[MT_SPM_RES_DRAM_S0] = "DRAM_S0",
[MT_SPM_RES_DRAM_S1] = "DRAM_S1",
};
static ssize_t mt6833_spm_res_rq_read(char *ToUserBuf, size_t sz, void *priv)
{
char *p = ToUserBuf;
int i, s, u;
unsigned int unum, uvalid, uname_i, uname_t;
unsigned int rnum, rusage, per_usage;
char uname[MT_LP_RQ_USER_NAME_LEN+1];
mtk_dbg_spm_log("resource_num=%d, user_num=%d, user_valid=0x%x\n",
rnum = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_NUM,
MT_LPM_SMC_ACT_GET, 0, 0),
unum = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_NUM,
MT_LPM_SMC_ACT_GET, 0, 0),
uvalid = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_VALID,
MT_LPM_SMC_ACT_GET, 0, 0));
rusage = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USAGE,
MT_LPM_SMC_ACT_GET,
MT_LP_RQ_ID_ALL_USAGE, 0);
mtk_dbg_spm_log("\n");
mtk_dbg_spm_log("user [bit][valid]:\n");
for (i = 0; i < unum; i++) {
uname_i = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_NAME,
MT_LPM_SMC_ACT_GET, i, 0);
for (s = 0, u = 0; s < MT_LP_RQ_USER_NAME_LEN;
s++, u += MT_LP_RQ_USER_CHAR_U) {
uname_t = ((uname_i >> u) & MT_LP_RQ_USER_CHAR_MASK);
uname[s] = (uname_t) ? (char)uname_t : ' ';
}
uname[s] = '\0';
mtk_dbg_spm_log("%4s [%3d][%3s]\n", uname, i,
((1<<i) & uvalid) ? "yes" : "no");
}
mtk_dbg_spm_log("\n");
if (rnum != MT_SPM_RES_MAX) {
mtk_dbg_spm_log("Platform resource amount mismatch\n");
rnum = (rnum > MT_SPM_RES_MAX) ? MT_SPM_RES_MAX : rnum;
}
mtk_dbg_spm_log("resource [bit][user_usage][blocking]:\n");
for (i = 0; i < rnum; i++) {
mtk_dbg_spm_log("%8s [%3d][0x%08x][%3s]\n",
mt6833_spm_resource_str[i], i,
(per_usage =
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USAGE,
MT_LPM_SMC_ACT_GET, i, 0)),
((1<<i) & rusage) ? "yes" : "no"
);
}
mtk_dbg_spm_log("\n");
mtk_dbg_spm_log("resource request command help:\n");
mtk_dbg_spm_log("echo enable ${user_bit} > %s\n", MT_LP_RQ_NODE);
mtk_dbg_spm_log("echo bypass ${user_bit} > %s\n", MT_LP_RQ_NODE);
mtk_dbg_spm_log("echo request ${resource_bit} > %s\n", MT_LP_RQ_NODE);
mtk_dbg_spm_log("echo release > %s\n", MT_LP_RQ_NODE);
return p - ToUserBuf;
}
static ssize_t mt6833_spm_res_rq_write(char *FromUserBuf, size_t sz, void *priv)
{
char cmd[128];
int parm;
if (sscanf(FromUserBuf, "%127s %x", cmd, &parm) == 2) {
if (!strcmp(cmd, "bypass"))
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_VALID,
MT_LPM_SMC_ACT_SET,
parm, 0);
else if (!strcmp(cmd, "enable"))
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_VALID,
MT_LPM_SMC_ACT_SET,
parm, 1);
else if (!strcmp(cmd, "request"))
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_REQ,
MT_LPM_SMC_ACT_SET,
0, parm);
return sz;
} else if (sscanf(FromUserBuf, "%127s", cmd) == 1) {
if (!strcmp(cmd, "release"))
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_REQ,
MT_LPM_SMC_ACT_CLR,
0, 0);
return sz;
} else if ((!kstrtoint(FromUserBuf, 10, &parm)) == 1) {
return sz;
}
return -EINVAL;
}
static const struct mtk_lp_sysfs_op mt6833_spm_res_rq_fops = {
.fs_read = mt6833_spm_res_rq_read,
.fs_write = mt6833_spm_res_rq_write,
};
int mt6833_dbg_spm_fs_init(void)
{
int r;
mtk_spm_sysfs_root_entry_create();
mtk_spm_sysfs_entry_node_add("spm_resource_req", 0444
, &mt6833_spm_res_rq_fops, NULL);
/* create /sys/power/spm/xxx */
r = mtk_spm_sysfs_power_create_group(&pwrctrl_attr_group);
if (r)
pr_info("[SPM] FAILED TO CREATE /sys/power/spm (%d)\n", r);
return r;
}
int mt6833_dbg_spm_fs_deinit(void)
{
return 0;
}
@@ -0,0 +1,926 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/rtc.h>
#include <linux/wakeup_reason.h>
#include <linux/syscore_ops.h>
#include <mt6833_cond.h>
#include <mtk_lpm_module.h>
#include <aee.h>
#include <mtk_lpm.h>
#include <mt6833_spm_comm.h>
#include <mt6833_spm_reg.h>
#include <mt6833_pwr_ctrl.h>
#include <mt6833_pcm_def.h>
#include <mtk_dbg_common_v1.h>
#include <mt-plat/mtk_ccci_common.h>
#include <mtk_lpm_call.h>
#include <mtk_lpm_call_type.h>
#include <mtk_lpm_timer.h>
#include <mtk_lpm_sysfs.h>
#include <gs/v1/mtk_power_gs.h>
#define MT6833_LOG_MONITOR_STATE_NAME "mcusysoff"
#define MT6833_LOG_DEFAULT_MS 5000
#define PCM_32K_TICKS_PER_SEC (32768)
#define PCM_TICK_TO_SEC(TICK) (TICK / PCM_32K_TICKS_PER_SEC)
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
struct MT6886_LOGGER_NODE mt6833_log_gs_idle;
struct MTK_LPM_GS_IDLE_INFO {
unsigned short limit;
unsigned short limit_set;
unsigned int dump_type;
};
struct MTK_LPM_GS_IDLE_INFO mt6833_log_gs_info;
struct cpumask mtk_lpm_gs_idle_cpumask;
#define IS_MTK_LPM_GS_UPDATE(x) \
((cpumask_weight(&mtk_lpm_gs_idle_cpumask)\
== num_online_cpus()) &&\
(x.limit != x.limit_set))
#endif
static struct mt6833_spm_wake_status mt6833_wake;
void __iomem *mt6833_spm_base;
struct mt6833_log_helper {
short cur;
short prev;
struct mt6833_spm_wake_status *wakesrc;
};
struct mt6833_log_helper mt6833_logger_help = {
.wakesrc = &mt6833_wake,
.cur = 0,
.prev = 0,
};
static char *mt6833_spm_cond_cg_str[PLAT_SPM_COND_MAX] = {
[PLAT_SPM_COND_MTCMOS_0] = "MTCMOS_0",
[PLAT_SPM_COND_CG_INFRA_0] = "INFRA_0",
[PLAT_SPM_COND_CG_INFRA_1] = "INFRA_1",
[PLAT_SPM_COND_CG_INFRA_2] = "INFRA_2",
[PLAT_SPM_COND_CG_INFRA_3] = "INFRA_3",
[PLAT_SPM_COND_CG_INFRA_4] = "INFRA_4",
[PLAT_SPM_COND_CG_INFRA_5] = "INFRA_5",
[PLAT_SPM_COND_CG_MMSYS_0] = "MMSYS_0",
[PLAT_SPM_COND_CG_MMSYS_1] = "MMSYS_1",
[PLAT_SPM_COND_CG_MMSYS_2] = "MMSYS_2",
};
const char *mt6833_wakesrc_str[32] = {
[0] = " R12_PCM_TIMER",
[1] = " R12_RESERVED_DEBUG_B",
[2] = " R12_KP_IRQ_B",
[3] = " R12_APWDT_EVENT_B",
[4] = " R12_APXGPT1_EVENT_B",
[5] = " R12_CONN2AP_SPM_WAKEUP_B",
[6] = " R12_EINT_EVENT_B",
[7] = " R12_CONN_WDT_IRQ_B",
[8] = " R12_CCIF0_EVENT_B",
[9] = " R12_LOWBATTERY_IRQ_B",
[10] = " R12_SC_SSPM2SPM_WAKEUP_B",
[11] = " R12_SC_SCP2SPM_WAKEUP_B",
[12] = " R12_RESERVED_BIT_12",
[13] = " R12_PCM_WDT_WAKEUP_B",
[14] = " R12_USB_CDSC_B",
[15] = " R12_USB_POWERDWN_B",
[16] = " R12_SYS_TIMER_EVENT_B",
[17] = " R12_EINT_EVENT_SECURE_B",
[18] = " R12_CCIF1_EVENT_B",
[19] = " R12_UART0_IRQ_B",
[20] = " R12_AFE_IRQ_MCU_B",
[21] = " R12_THERM_CTRL_EVENT_B",
[22] = " R12_SYS_CIRQ_IRQ_B",
[23] = " R12_MD2AP_PEER_EVENT_B",
[24] = " R12_CSYSPWREQ_B",
[25] = " R12_MD1_WDT_B",
[26] = " R12_AP2AP_PEER_WAKEUPEVENT_B",
[27] = " R12_SEJ_EVENT_B",
[28] = " R12_SPM_CPU_WAKEUPEVENT_B",
[29] = " R12_RESERVED_BIT_29",
[30] = " R12_RESERVED_BIT_30",
[31] = " R12_RESERVED_BIT_31",
};
struct spm_wakesrc_irq_list mt6833_spm_wakesrc_irqs[] = {
/* mtk-kpd */
{ WAKE_SRC_STA1_KP_IRQ_B, "mediatek,kp", 0, 0},
/* mt_wdt */
{ WAKE_SRC_STA1_APWDT_EVENT_B, "mediatek,toprgu", 0, 0},
/* bt_cvsd_int */
{ WAKE_SRC_STA1_CONN2AP_SPM_WAKEUP_B, "mediatek,mtk-btcvsd-snd", 0, 0},
/* wf_hif_int */
{ WAKE_SRC_STA1_CONN2AP_SPM_WAKEUP_B, "mediatek,wifi", 0, 0},
/* conn2ap_btif_wakeup_out */
{ WAKE_SRC_STA1_CONN2AP_SPM_WAKEUP_B, "mediatek,mt6833-consys", 0, 0},
/* conn2ap_sw_irq */
{ WAKE_SRC_STA1_CONN2AP_SPM_WAKEUP_B, "mediatek,mt6833-consys", 2, 0},
/* CCIF_AP_DATA */
{ WAKE_SRC_STA1_CCIF0_EVENT_B, "mediatek,ap_ccif0", 0, 0},
/* SCP IPC0 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 0, 0},
/* SCP IPC1 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 1, 0},
/* MBOX_ISR0 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 2, 0},
/* MBOX_ISR1 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 3, 0},
/* MBOX_ISR2 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 4, 0},
/* MBOX_ISR3 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 5, 0},
/* MBOX_ISR4 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 6, 0},
/* ADSP_A_AUD */
{ WAKE_SRC_STA1_SC_ADSP2SPM_WAKEUP_B, "mediatek,adsp_core_0", 2, 0},
/* ADSP_B_AUD */
{ WAKE_SRC_STA1_SC_ADSP2SPM_WAKEUP_B, "mediatek,adsp_core_1", 2, 0},
/* CCIF0_AP */
{ WAKE_SRC_STA1_MD1_WDT_B, "mediatek,mddriver", 2, 0},
/* DPMAIF_AP */
{ WAKE_SRC_STA1_AP2AP_PEER_WAKEUPEVENT_B, "mediatek,dpmaif", 0, 0},
};
#define plat_mmio_read(offset) __raw_readl(mt6833_spm_base + offset)
u64 ap_pd_count;
u64 ap_slp_duration;
u64 spm_26M_off_count;
u64 spm_26M_off_duration;
u32 before_ap_slp_duration;
#define IRQ_NUMBER \
(sizeof(mt6833_spm_wakesrc_irqs)/sizeof(struct spm_wakesrc_irq_list))
static void mt6833_get_spm_wakesrc_irq(void)
{
int i;
struct device_node *node = NULL;
for (i = 0; i < IRQ_NUMBER; i++) {
if (mt6833_spm_wakesrc_irqs[i].name == NULL)
continue;
node = of_find_compatible_node(NULL, NULL,
mt6833_spm_wakesrc_irqs[i].name);
if (!node) {
pr_info("[name:spm&][SPM] find '%s' node failed\n",
mt6833_spm_wakesrc_irqs[i].name);
continue;
}
mt6833_spm_wakesrc_irqs[i].irq_no =
irq_of_parse_and_map(node,
mt6833_spm_wakesrc_irqs[i].order);
if (!mt6833_spm_wakesrc_irqs[i].irq_no) {
pr_info("[name:spm&][SPM] get '%s' failed\n",
mt6833_spm_wakesrc_irqs[i].name);
}
}
}
struct mt6833_logger_timer {
struct mt_lpm_timer tm;
unsigned int fired;
};
struct mt6833_logger_fired_info {
unsigned int fired;
int state_index;
};
static struct mt6833_logger_timer mt6833_log_timer;
static struct mt6833_logger_fired_info mt6833_logger_fired;
int mt6833_get_wakeup_status(struct mt6833_log_helper *help)
{
if (!help->wakesrc || !mt6833_spm_base)
return -EINVAL;
help->wakesrc->r12 = plat_mmio_read(SPM_BK_WAKE_EVENT);
help->wakesrc->r12_ext = plat_mmio_read(SPM_WAKEUP_STA);
help->wakesrc->raw_sta = plat_mmio_read(SPM_WAKEUP_STA);
help->wakesrc->raw_ext_sta = plat_mmio_read(SPM_WAKEUP_EXT_STA);
help->wakesrc->md32pcm_wakeup_sta = plat_mmio_read(MD32PCM_WAKEUP_STA);
help->wakesrc->md32pcm_event_sta = plat_mmio_read(MD32PCM_EVENT_STA);
help->wakesrc->src_req = plat_mmio_read(SPM_SRC_REQ);
/* backup of SPM_WAKEUP_MISC */
help->wakesrc->wake_misc = plat_mmio_read(SPM_BK_WAKE_MISC);
/* get sleep time */
/* backup of PCM_TIMER_OUT */
help->wakesrc->timer_out = plat_mmio_read(SPM_BK_PCM_TIMER);
/* get other SYS and co-clock status */
help->wakesrc->r13 = plat_mmio_read(PCM_REG13_DATA);
help->wakesrc->idle_sta = plat_mmio_read(SUBSYS_IDLE_STA);
help->wakesrc->req_sta0 = plat_mmio_read(SRC_REQ_STA_0);
help->wakesrc->req_sta1 = plat_mmio_read(SRC_REQ_STA_1);
help->wakesrc->req_sta2 = plat_mmio_read(SRC_REQ_STA_2);
help->wakesrc->req_sta3 = plat_mmio_read(SRC_REQ_STA_3);
help->wakesrc->req_sta4 = plat_mmio_read(SRC_REQ_STA_4);
/* get HW CG check status */
help->wakesrc->cg_check_sta = plat_mmio_read(SPM_CG_CHECK_STA);
/* get debug flag for PCM execution check */
help->wakesrc->debug_flag = plat_mmio_read(PCM_WDT_LATCH_SPARE_0);
help->wakesrc->debug_flag1 = plat_mmio_read(PCM_WDT_LATCH_SPARE_1);
/* get backup SW flag status */
help->wakesrc->b_sw_flag0 = plat_mmio_read(SPM_SW_RSV_7);
help->wakesrc->b_sw_flag1 = plat_mmio_read(SPM_SW_RSV_8);
help->wakesrc->rt_req_sta0 = plat_mmio_read(SPM_SW_RSV_2);
help->wakesrc->rt_req_sta1 = plat_mmio_read(SPM_SW_RSV_3);
help->wakesrc->rt_req_sta2 = plat_mmio_read(SPM_SW_RSV_4);
help->wakesrc->rt_req_sta3 = plat_mmio_read(SPM_SW_RSV_5);
help->wakesrc->rt_req_sta4 = plat_mmio_read(SPM_SW_RSV_6);
/* get ISR status */
help->wakesrc->isr = plat_mmio_read(SPM_IRQ_STA);
/* get SW flag status */
help->wakesrc->sw_flag0 = plat_mmio_read(SPM_SW_FLAG_0);
help->wakesrc->sw_flag1 = plat_mmio_read(SPM_SW_FLAG_1);
/* get CLK SETTLE */
help->wakesrc->clk_settle = plat_mmio_read(SPM_CLK_SETTLE);
/* check abort */
help->wakesrc->is_abort =
help->wakesrc->debug_flag & DEBUG_ABORT_MASK;
help->wakesrc->is_abort |=
help->wakesrc->debug_flag1 & DEBUG_ABORT_MASK_1;
help->cur += 1;
return 0;
}
static void mt6833_save_sleep_info(void)
{
#define AVOID_OVERFLOW (0xFFFFFFFF00000000)
u32 off_26M_duration;
u32 slp_duration;
slp_duration = plat_mmio_read(SPM_BK_PCM_TIMER);
if (slp_duration == before_ap_slp_duration)
return;
/* Save ap off counter and duration */
if (ap_pd_count >= AVOID_OVERFLOW)
ap_pd_count = 0;
else
ap_pd_count++;
if (ap_slp_duration >= AVOID_OVERFLOW)
ap_slp_duration = 0;
else {
ap_slp_duration = ap_slp_duration + slp_duration;
before_ap_slp_duration = slp_duration;
}
/* Save 26M's off counter and duration */
if (spm_26M_off_duration >= AVOID_OVERFLOW)
spm_26M_off_duration = 0;
else {
off_26M_duration = plat_mmio_read(SPM_BK_VTCXO_DUR);
if (off_26M_duration == 0)
return;
spm_26M_off_duration = spm_26M_off_duration +
off_26M_duration;
}
if (spm_26M_off_count >= AVOID_OVERFLOW)
spm_26M_off_count = 0;
else
spm_26M_off_count = (plat_mmio_read(SPM_VTCXO_EVENT_COUNT_STA)
& 0xffff)
+ spm_26M_off_count;
}
static void mt6833_suspend_show_detailed_wakeup_reason
(struct mt6833_spm_wake_status *wakesta)
{
int i;
unsigned int irq_no;
#if !defined(CONFIG_FPGA_EARLY_PORTING)
#ifdef CONFIG_MTK_CCCI_DEVICES
exec_ccci_kern_func_by_md_id(0, ID_DUMP_MD_SLEEP_MODE,
NULL, 0);
#endif
#ifdef CONFIG_MTK_ECCCI_DRIVER
if (wakesta->r12 & R12_CLDMA_EVENT_B)
exec_ccci_kern_func_by_md_id(0, ID_GET_MD_WAKEUP_SRC,
NULL, 0);
if (wakesta->r12 & R12_MD2AP_PEER_EVENT_B)
exec_ccci_kern_func_by_md_id(0, ID_GET_MD_WAKEUP_SRC,
NULL, 0);
if (wakesta->r12 & R12_CCIF0_EVENT_B)
exec_ccci_kern_func_by_md_id(0, ID_GET_MD_WAKEUP_SRC,
NULL, 0);
if (wakesta->r12 & R12_CCIF1_EVENT_B)
exec_ccci_kern_func_by_md_id(2, ID_GET_MD_WAKEUP_SRC,
NULL, 0);
#endif
#endif
/* Check if pending irq happen and log them */
for (i = 0; i < IRQ_NUMBER; i++) {
if (mt6833_spm_wakesrc_irqs[i].name == NULL ||
!mt6833_spm_wakesrc_irqs[i].irq_no)
continue;
if (mt6833_spm_wakesrc_irqs[i].wakesrc & wakesta->r12) {
irq_no = mt6833_spm_wakesrc_irqs[i].irq_no;
if (mt_irq_get_pending(irq_no))
log_irq_wakeup_reason(irq_no);
}
}
}
static void dump_lp_cond(void)
{
#define MT6833_DBG_SMC(_id, _act, _rc, _param) ({\
(u32) mtk_lpm_smc_spm_dbg(_id, _act, _rc, _param); })
int i;
u32 blkcg;
for (i = 1 ; i < PLAT_SPM_COND_MAX ; i++) {
blkcg = MT6833_DBG_SMC(MT_SPM_DBG_SMC_UID_BLOCK_DETAIL, MT_LPM_SMC_ACT_GET, 0, i);
if (blkcg != 0)
printk_deferred("suspend warning: CG: %6s = 0x%08lx\n"
, mt6833_spm_cond_cg_str[i], blkcg);
}
}
static void mt6833_suspend_spm_rsc_req_check
(struct mt6833_spm_wake_status *wakesta)
{
#define LOG_BUF_SIZE 256
#define IS_BLOCKED_OVER_TIMES 10
#undef AVOID_OVERFLOW
#define AVOID_OVERFLOW (0xF0000000)
static u32 is_blocked_cnt;
char log_buf[LOG_BUF_SIZE] = { 0 };
int log_size = 0;
u32 is_no_blocked = 0;
u32 req_sta_0, req_sta_1, req_sta_4;
u32 src_req;
if (is_blocked_cnt >= AVOID_OVERFLOW)
is_blocked_cnt = 0;
/* Check if ever enter deepest System LPM */
is_no_blocked = wakesta->debug_flag & 0x2;
/* Check if System LPM ever is blocked over 10 times */
if (!is_no_blocked)
is_blocked_cnt++;
else
is_blocked_cnt = 0;
if (is_blocked_cnt < IS_BLOCKED_OVER_TIMES)
return;
/* Show who is blocking system LPM */
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size,
"suspend warning:(OneShot) System LPM is blocked by ");
req_sta_0 = plat_mmio_read(SRC_REQ_STA_0);
if (req_sta_0 & 0xFFF)
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "md ");
if (req_sta_0 & (0x3F << 12))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "conn ");
if (req_sta_0 & (0x7 << 18))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "nfc ");
if (req_sta_0 & (0xF << 26))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "disp ");
req_sta_1 = plat_mmio_read(SRC_REQ_STA_1);
if (req_sta_1 & 0x1F)
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "scp ");
if (req_sta_1 & (0x1F << 5))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "adsp ");
if (req_sta_1 & (0x1F << 10))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "ufs ");
if (req_sta_1 & (0xF << 15))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "gce ");
if (req_sta_1 & (0x3FF << 21))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "msdc ");
req_sta_4 = plat_mmio_read(SRC_REQ_STA_4);
if (req_sta_4 & 0x1F)
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "apu ");
src_req = plat_mmio_read(SPM_SRC_REQ);
if (src_req & 0x9B) {
dump_lp_cond();
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "spm ");
}
WARN_ON(strlen(log_buf) >= LOG_BUF_SIZE);
printk_deferred("[name:spm&][SPM] %s", log_buf);
}
static int mt6833_show_message(struct mt6833_spm_wake_status *wakesrc, int type,
const char *prefix, void *data)
{
#undef LOG_BUF_SIZE
#define LOG_BUF_SIZE 256
#define LOG_BUF_OUT_SZ 768
#define IS_WAKE_MISC(x) (wakesrc->wake_misc & x)
#define IS_LOGBUF(ptr, newstr) \
((strlen(ptr) + strlen(newstr)) < LOG_BUF_SIZE)
int i;
unsigned int spm_26M_off_pct = 0;
char buf[LOG_BUF_SIZE] = { 0 };
char log_buf[LOG_BUF_OUT_SZ] = { 0 };
char *local_ptr = NULL;
int log_size = 0;
unsigned int wr = WR_UNKNOWN;
const char *scenario = prefix ?: "UNKNOWN";
/* Disable rcu lock checking */
if (type == MT_LPM_ISSUER_SUSPEND)
rcu_irq_enter_irqson();
if (wakesrc->is_abort != 0) {
/* add size check for vcoredvfs */
aee_sram_printk("SPM ABORT (%s), r13 = 0x%x, ",
scenario, wakesrc->r13);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"[SPM] ABORT (%s), r13 = 0x%x, ",
scenario, wakesrc->r13);
aee_sram_printk(" debug_flag = 0x%x 0x%x",
wakesrc->debug_flag, wakesrc->debug_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" debug_flag = 0x%x 0x%x",
wakesrc->debug_flag, wakesrc->debug_flag1);
aee_sram_printk(" sw_flag = 0x%x 0x%x",
wakesrc->sw_flag0, wakesrc->sw_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" sw_flag = 0x%x 0x%x\n",
wakesrc->sw_flag0, wakesrc->sw_flag1);
aee_sram_printk(" b_sw_flag = 0x%x 0x%x",
wakesrc->b_sw_flag0, wakesrc->b_sw_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" b_sw_flag = 0x%x 0x%x",
wakesrc->b_sw_flag0, wakesrc->b_sw_flag1);
wr = WR_ABORT;
} else {
if (wakesrc->r12 & R12_PCM_TIMER) {
if (wakesrc->wake_misc & WAKE_MISC_PCM_TIMER_EVENT) {
local_ptr = " PCM_TIMER";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_PCM_TIMER;
}
}
if (wakesrc->r12 & R12_TWAM_IRQ_B) {
if (IS_WAKE_MISC(WAKE_MISC_DVFSRC_IRQ)) {
local_ptr = " DVFSRC";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_DVFSRC;
}
if (IS_WAKE_MISC(WAKE_MISC_TWAM_IRQ_B)) {
local_ptr = " TWAM";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_TWAM;
}
if (IS_WAKE_MISC(WAKE_MISC_PMSR_IRQ_B_SET0)) {
local_ptr = " PMSR";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_PMSR;
}
if (IS_WAKE_MISC(WAKE_MISC_PMSR_IRQ_B_SET1)) {
local_ptr = " PMSR";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_PMSR;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_0)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_1)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_2)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_3)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_ALL)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
}
for (i = 1; i < 32; i++) {
if (wakesrc->r12 & (1U << i)) {
if (IS_LOGBUF(buf, mt6833_wakesrc_str[i]))
strncat(buf, mt6833_wakesrc_str[i],
strlen(mt6833_wakesrc_str[i]));
wr = WR_WAKE_SRC;
}
}
WARN_ON(strlen(buf) >= LOG_BUF_SIZE);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"%s wake up by %s, timer_out = %u, r13 = 0x%x, debug_flag = 0x%x 0x%x, ",
scenario, buf, wakesrc->timer_out, wakesrc->r13,
wakesrc->debug_flag, wakesrc->debug_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"r12 = 0x%x, r12_ext = 0x%x, raw_sta = 0x%x 0x%x 0x%x, idle_sta = 0x%x, ",
wakesrc->r12, wakesrc->r12_ext,
wakesrc->raw_sta,
wakesrc->md32pcm_wakeup_sta,
wakesrc->md32pcm_event_sta,
wakesrc->idle_sta);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"req_sta = 0x%x 0x%x 0x%x 0x%x 0x%x, cg_check_sta =0x%x, isr = 0x%x, rt_req_sta0 = 0x%x rt_req_sta1 = 0x%x rt_req_sta2 = 0x%x rt_req_sta3 = 0x%x dram_sw_con_3 = 0x%x, ",
wakesrc->req_sta0, wakesrc->req_sta1,
wakesrc->req_sta2, wakesrc->req_sta3,
wakesrc->req_sta4, wakesrc->cg_check_sta,
wakesrc->isr, wakesrc->rt_req_sta0,
wakesrc->rt_req_sta1, wakesrc->rt_req_sta2,
wakesrc->rt_req_sta3, wakesrc->rt_req_sta4);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"raw_ext_sta = 0x%x, wake_misc = 0x%x, pcm_flag = 0x%x 0x%x 0x%x 0x%x, req = 0x%x, ",
wakesrc->raw_ext_sta,
wakesrc->wake_misc,
wakesrc->sw_flag0,
wakesrc->sw_flag1, wakesrc->b_sw_flag0,
wakesrc->b_sw_flag0,
wakesrc->src_req);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" clk_settle = 0x%x, ", wakesrc->clk_settle);
if (type == MT_LPM_ISSUER_SUSPEND) {
/* calculate 26M off percentage in suspend period */
if (wakesrc->timer_out != 0) {
spm_26M_off_pct =
(100 * plat_mmio_read(SPM_BK_VTCXO_DUR))
/ wakesrc->timer_out;
}
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"wlk_cntcv_l = 0x%x, wlk_cntcv_h = 0x%x, 26M_off_pct = %d\n",
plat_mmio_read(SYS_TIMER_VALUE_L),
plat_mmio_read(SYS_TIMER_VALUE_H),
spm_26M_off_pct);
}
}
WARN_ON(log_size >= LOG_BUF_OUT_SZ);
if (type == MT_LPM_ISSUER_SUSPEND) {
printk_deferred("[name:spm&][SPM] %s", log_buf);
mt6833_suspend_show_detailed_wakeup_reason(wakesrc);
mt6833_suspend_spm_rsc_req_check(wakesrc);
printk_deferred("[name:spm&][SPM] Suspended for %d.%03d seconds",
PCM_TICK_TO_SEC(wakesrc->timer_out),
PCM_TICK_TO_SEC((wakesrc->timer_out %
PCM_32K_TICKS_PER_SEC)
* 1000));
/* Eable rcu lock checking */
rcu_irq_exit_irqson();
} else
pr_info("[name:spm&][SPM] %s", log_buf);
return wr;
}
int mt6833_issuer_func(int type, const char *prefix, void *data)
{
mt6833_get_wakeup_status(&mt6833_logger_help);
return mt6833_show_message(mt6833_logger_help.wakesrc,
type, prefix, data);
}
struct mtk_lpm_issuer mt6833_issuer = {
.log = mt6833_issuer_func,
};
static int mt6833_idle_save_sleep_info_nb_func(struct notifier_block *nb,
unsigned long action, void *data)
{
struct mtk_lpm_nb_data *nb_data = (struct mtk_lpm_nb_data *)data;
if (nb_data && (action == MTK_LPM_NB_BEFORE_REFLECT))
mt6833_save_sleep_info();
return NOTIFY_OK;
}
struct notifier_block mt6833_idle_save_sleep_info_nb = {
.notifier_call = mt6833_idle_save_sleep_info_nb_func,
};
static void mt6833_suspend_save_sleep_info_func(void)
{
mt6833_save_sleep_info();
}
static struct syscore_ops mt6833_suspend_save_sleep_info_syscore_ops = {
.resume = mt6833_suspend_save_sleep_info_func,
};
static int mt6833_log_timer_func(unsigned long long dur, void *priv)
{
struct mt6833_logger_timer *timer =
(struct mt6833_logger_timer *)priv;
struct mt6833_logger_fired_info *info = &mt6833_logger_fired;
if (timer->fired != info->fired) {
/* if the wake src had beed update before
* then won't do wake src update
*/
if (mt6833_logger_help.prev == mt6833_logger_help.cur)
mt6833_get_wakeup_status(&mt6833_logger_help);
mt6833_show_message(mt6833_logger_help.wakesrc,
MT_LPM_ISSUER_CPUIDLE,
"MCUSYSOFF", NULL);
mt6833_logger_help.prev = mt6833_logger_help.cur;
} else
pr_info("[name:spm&][SPM] MCUSYSOFF Didn't enter low power scenario\n");
timer->fired = info->fired;
return 0;
}
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
struct mtk_lpm_gs_idleinfo {
unsigned int type[2];
};
#endif
static int mt6833_logger_nb_func(struct notifier_block *nb,
unsigned long action, void *data)
{
struct mtk_lpm_nb_data *nb_data = (struct mtk_lpm_nb_data *)data;
struct mt6833_logger_fired_info *info = &mt6833_logger_fired;
if (nb_data && (action == MTK_LPM_NB_BEFORE_REFLECT)
&& (nb_data->index == info->state_index)) {
info->fired++;
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
cpumask_clear_cpu(nb_data->cpu, &mtk_lpm_gs_idle_cpumask);
#endif
}
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
if (nb_data && (action == MTK_LPM_NB_AFTER_PROMPT)
&& (nb_data->index == info->state_index)) {
cpumask_set_cpu(nb_data->cpu, &mtk_lpm_gs_idle_cpumask);
if (IS_MTK_LPM_GS_UPDATE(mt6833_log_gs_info)) {
struct mtk_lpm_callee_simple *callee = NULL;
struct mtk_lpm_data val;
val.d.u32 = mt6833_log_gs_info.dump_type;
if (!mtk_lpm_callee_get(MTK_LPM_CALLEE_PWR_GS, &callee))
callee->set(MTK_LPM_PWR_GS_TYPE_VCORELP_26M, &val);
mt6833_log_gs_info.limit =
mt6833_log_gs_info.limit_set;
}
}
#endif
return NOTIFY_OK;
}
struct notifier_block mt6833_logger_nb = {
.notifier_call = mt6833_logger_nb_func,
};
static ssize_t mt6833_logger_debugfs_read(char *ToUserBuf,
size_t sz, void *priv)
{
char *p = ToUserBuf;
int len;
if (priv == ((void *)&mt6833_log_timer)) {
len = scnprintf(p, sz, "%lu\n",
mtk_lpm_timer_interval(&mt6833_log_timer.tm));
p += len;
}
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
else if (priv == ((void *)&mt6833_log_gs_info)) {
len = scnprintf(p, sz, "golden_type: %u\n",
mt6833_log_gs_info.dump_type);
p += len;
}
#endif
return (p - ToUserBuf);
}
static ssize_t mt6833_logger_debugfs_write(char *FromUserBuf,
size_t sz, void *priv)
{
if (priv == ((void *)&mt6833_log_timer)) {
unsigned int val = 0;
if (!kstrtouint(FromUserBuf, 10, &val)) {
if (val == 0)
mtk_lpm_timer_stop(&mt6833_log_timer.tm);
else
mtk_lpm_timer_interval_update(
&mt6833_log_timer.tm, val);
}
}
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
else if (priv == ((void *)&mt6833_log_gs_info)) {
char cmd[64];
unsigned int param;
memset(cmd, 0, sizeof(cmd));
if (sscanf(FromUserBuf, "%20s %u", cmd, &param) == 2) {
if (!strcmp(cmd, "golden_dump")) {
if (param)
mt6833_log_gs_info.limit_set += 1;
} else if (!strcmp(cmd, "golden_type"))
mt6833_log_gs_info.dump_type = param;
}
}
#endif
return sz;
}
struct MT6886_LOGGER_NODE {
struct mtk_lp_sysfs_handle handle;
struct mtk_lp_sysfs_op op;
};
#define MT6833_LOGGER_NODE_INIT(_n, _priv) ({\
_n.op.fs_read = mt6833_logger_debugfs_read;\
_n.op.fs_write = mt6833_logger_debugfs_write;\
_n.op.priv = _priv; })\
struct mtk_lp_sysfs_handle mt6833_log_tm_node;
struct MT6886_LOGGER_NODE mt6833_log_tm_interval;
int mt6833_logger_timer_debugfs_init(void)
{
mtk_lpm_sysfs_sub_entry_add("logger", 0644,
NULL, &mt6833_log_tm_node);
MT6833_LOGGER_NODE_INIT(mt6833_log_tm_interval,
&mt6833_log_timer);
mtk_lpm_sysfs_sub_entry_node_add("interval", 0644,
&mt6833_log_tm_interval.op,
&mt6833_log_tm_node,
&mt6833_log_tm_interval.handle);
#ifdef CONFIG_MTK_LPM_GS_DUMP_SUPPORT
MT6833_LOGGER_NODE_INIT(mt6833_log_gs_idle,
&mt6833_log_gs_info);
mtk_lpm_sysfs_sub_entry_node_add("gs", 0644,
&mt6833_log_gs_idle.op,
&mt6833_log_tm_node,
&mt6833_log_gs_idle.handle);
#endif
return 0;
}
int __init mt6833_logger_init(void)
{
struct device_node *node = NULL;
struct cpuidle_driver *drv = NULL;
struct cpuidle_device *dev = NULL;
node = of_find_compatible_node(NULL, NULL, "mediatek,sleep");
if (node) {
mt6833_spm_base = of_iomap(node, 0);
of_node_put(node);
}
if (mt6833_spm_base)
mtk_lp_issuer_register(&mt6833_issuer);
else
pr_info("[name:mtk_lpm][P] - Don't register the issue by error! (%s:%d)\n",
__func__, __LINE__);
mt6833_get_spm_wakesrc_irq();
mtk_lpm_sysfs_root_entry_create();
mt6833_logger_timer_debugfs_init();
preempt_disable();
dev = cpuidle_get_device();
drv = cpuidle_get_cpu_driver(dev);
preempt_enable();
mt6833_logger_fired.state_index = -1;
if (drv) {
int idx;
for (idx = 0; idx < drv->state_count; ++idx) {
if (!strcmp(MT6833_LOG_MONITOR_STATE_NAME,
drv->states[idx].name)) {
mt6833_logger_fired.state_index = idx;
break;
}
}
}
mtk_lpm_notifier_register(&mt6833_logger_nb);
mtk_lpm_notifier_register(&mt6833_idle_save_sleep_info_nb);
mt6833_log_timer.tm.timeout = mt6833_log_timer_func;
mt6833_log_timer.tm.priv = &mt6833_log_timer;
mtk_lpm_timer_init(&mt6833_log_timer.tm, MTK_LPM_TIMER_REPEAT);
mtk_lpm_timer_interval_update(&mt6833_log_timer.tm,
MT6833_LOG_DEFAULT_MS);
mtk_lpm_timer_start(&mt6833_log_timer.tm);
register_syscore_ops(&mt6833_suspend_save_sleep_info_syscore_ops);
return 0;
}
late_initcall_sync(mt6833_logger_init);
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <mtk_lpm_trace_event/mtk_lpm_trace_event.h>
#include <mtk_spm_sysfs.h>
#include <mt6833_spm_reg.h>
#define plat_mmio_read(offset) __raw_readl(spm_base + offset)
void __iomem *spm_base;
struct timer_list spm_resource_req_timer;
u32 spm_resource_req_timer_is_enabled;
u32 spm_resource_req_timer_ms;
/*
static void spm_resource_req_timer_fn(unsigned long data)
{
u32 req_sta_0, req_sta_1, req_sta_4;
u32 src_req;
u32 md = 0, conn = 0, scp = 0, adsp = 0;
u32 ufs = 0, msdc = 0, disp = 0, apu = 0;
u32 spm = 0;
req_sta_0 = plat_mmio_read(SRC_REQ_STA_0);
if (req_sta_0 & 0xFFF)
md = 1;
if (req_sta_0 & (0x3F << 12))
conn = 1;
if (req_sta_0 & (0xF << 26))
disp = 1;
req_sta_1 = plat_mmio_read(SRC_REQ_STA_1);
if (req_sta_1 & 0x1F)
scp = 1;
if (req_sta_1 & (0x1F << 5))
adsp = 1;
if (req_sta_1 & (0x1F << 10))
ufs = 1;
if (req_sta_1 & (0x3FF << 21))
msdc = 1;
req_sta_4 = plat_mmio_read(SRC_REQ_STA_4);
if (req_sta_4 & 0x1F)
apu = 1;
src_req = plat_mmio_read(SPM_SRC_REQ);
if (src_req & 0x19B)
spm = 1;
trace_SPM__resource_req_0(
md, conn,
scp, adsp,
ufs, msdc,
disp, apu,
spm);
spm_resource_req_timer.expires = jiffies +
msecs_to_jiffies(spm_resource_req_timer_ms);
add_timer(&spm_resource_req_timer);
}*/
/* static void spm_resource_req_timer_en(u32 enable, u32 timer_ms)
{
if (enable) {
if (spm_resource_req_timer.function == NULL) {
init_timer(&spm_resource_req_timer);
spm_resource_req_timer.function =
spm_resource_req_timer_fn;
spm_resource_req_timer.data = 0;
spm_resource_req_timer_is_enabled = false;
}
if (spm_resource_req_timer_is_enabled)
return;
spm_resource_req_timer_ms = timer_ms;
spm_resource_req_timer.expires = jiffies +
msecs_to_jiffies(spm_resource_req_timer_ms);
add_timer(&spm_resource_req_timer);
spm_resource_req_timer_is_enabled = true;
} else if (spm_resource_req_timer_is_enabled) {
del_timer(&spm_resource_req_timer);
spm_resource_req_timer_is_enabled = false;
}
} */
ssize_t get_spm_resource_req_timer_enable(char *ToUserBuf
, size_t sz, void *priv)
{
int bLen = snprintf(ToUserBuf, sz
, "spm resource request timer is enabled: %d\n",
spm_resource_req_timer_is_enabled);
return (bLen > sz) ? sz : bLen;
}
ssize_t set_spm_resource_req_timer_enable(char *ToUserBuf
, size_t sz, void *priv)
{
u32 is_enable;
u32 timer_ms;
if (!ToUserBuf)
return -EINVAL;
if (sscanf(ToUserBuf, "%d %d", &is_enable, &timer_ms) == 2) {
/* spm_resource_req_timer_en(is_enable, timer_ms); */
return sz;
}
return -EINVAL;
}
static const struct mtk_lp_sysfs_op spm_resource_req_timer_enable_fops = {
.fs_read = get_spm_resource_req_timer_enable,
.fs_write = set_spm_resource_req_timer_enable,
};
bool spm_is_md1_sleep(void)
{
return !(plat_mmio_read(SRC_REQ_STA_0) & 0x3F);
}
EXPORT_SYMBOL(spm_is_md1_sleep);
int __init mt6833_lpm_trace_init(void)
{
struct device_node *node = NULL;
node = of_find_compatible_node(NULL, NULL, "mediatek,sleep");
if (node) {
spm_base = of_iomap(node, 0);
of_node_put(node);
}
mtk_spm_sysfs_root_entry_create();
mtk_spm_sysfs_entry_node_add("spm_dump_res_req_enable", 0444
, &spm_resource_req_timer_enable_fops, NULL);
return 0;
}
late_initcall_sync(mt6833_lpm_trace_init);
@@ -0,0 +1,204 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include "mtk_power_gs_array.h"
#include <mtk_lpm_call.h>
#include <mtk_lpm_call_type.h>
#include <gs/mtk_lpm_pwr_gs.h>
#include <gs/v1/mtk_power_gs.h>
int mt6833_pwr_gs_set(unsigned int type, const struct mtk_lpm_data *val)
{
int ret = 0;
if (val->d.u32 & GS_PMIC)
ret = mtk_lpm_pwr_gs_compare(MTK_LPM_GS_CMP_PMIC, type);
if (ret)
return ret;
#ifdef MTK_LPM_GS_PLAT_CLK_DUMP_SUPPORT
if (val->d.v_u32 & GS_DCM)
ret = mtk_lpm_pwr_gs_compare_by_type(
MTK_LPM_GS_CMP_CLK, type, GS_DCM);
if (ret)
return ret;
if (val->d.v_u32 & GS_CG)
ret = mtk_lpm_pwr_gs_compare_by_type(
MTK_LPM_GS_CMP_CLK, type, GS_CG);
#endif
return ret;
}
struct mtk_lpm_callee mt6833_pwr_gs_callee = {
.uid = MTK_LPM_CALLEE_PWR_GS,
.i.simple = {
.set = mt6833_pwr_gs_set,
},
};
#ifdef MTK_LPM_GS_PLAT_CLK_DUMP_SUPPORT
struct mtk_lpm_gs_clk mt6833_clk_cg = {
.type = GS_CG,
.name = "CG",
};
struct mtk_lpm_gs_clk mt6833_clk_dcm = {
.type = GS_DCM,
.name = "DCM",
};
struct mtk_lpm_gs_clk *mt6833_clks[] = {
&mt6833_clk_dcm,
&mt6833_clk_cg,
NULL,
};
int mt6833_power_gs_clk_user_attach(struct mtk_lpm_gs_clk *p)
{
/* Set compare golden setting for scenario */
if (!strcmp(p->name, "CG")) {
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].name = "suspend";
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].array =
AP_CG_Golden_Setting_tcl_gs_suspend;
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].array_sz =
AP_CG_Golden_Setting_tcl_gs_suspend_len;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].name = "sodi3";
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].array =
AP_CG_Golden_Setting_tcl_gs_sodi;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].array_sz =
AP_CG_Golden_Setting_tcl_gs_sodi_len;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].name = "dpidle";
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].array =
AP_CG_Golden_Setting_tcl_gs_dpidle;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].array_sz =
AP_CG_Golden_Setting_tcl_gs_dpidle_len;
} else if (!strcmp(p->name, "DCM")) {
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].name = "suspend";
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].array =
AP_DCM_Golden_Setting_tcl_gs_suspend;
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].array_sz =
AP_DCM_Golden_Setting_tcl_gs_suspend_len;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].name = "sodi3";
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].array =
AP_DCM_Golden_Setting_tcl_gs_sodi;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].array_sz =
AP_DCM_Golden_Setting_tcl_gs_sodi_len;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].name = "dpidle";
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].array =
AP_DCM_Golden_Setting_tcl_gs_dpidle;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].array_sz =
AP_DCM_Golden_Setting_tcl_gs_dpidle_len;
}
return 0;
}
struct mtk_lpm_gs_clk_info mt6833_clk_infos = {
.attach = mt6833_power_gs_clk_user_attach,
.dcm = mt6833_clks,
};
#endif
/* PMIC */
struct mtk_lpm_gs_pmic mt6833_pmic6365 = {
.type = GS_PMIC,
.regulator = "vcore",
.pwr_domain = "6365",
};
struct mtk_lpm_gs_pmic mt6833_pmic6315 = {
.type = GS_PMIC,
.regulator = "3_vbuck1",
.pwr_domain = "6315",
};
struct mtk_lpm_gs_pmic *mt6833_pmic[] = {
&mt6833_pmic6365,
&mt6833_pmic6315,
NULL,
};
int mt6833_power_gs_pmic_user_attach(struct mtk_lpm_gs_pmic *p)
{
if (!p || !p->regulator)
return -EINVAL;
/* Set compare golden setting for scenario */
if (!strcmp(p->regulator, "vcore")) {
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].name = "suspend";
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].array =
AP_PMIC_REG_6365_gs_suspend_32kless;
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].array_sz =
AP_PMIC_REG_6365_gs_suspend_32kless_len;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].name = "sodi3";
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].array =
AP_PMIC_REG_6365_gs_sodi3p0_32kless;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].array_sz =
AP_PMIC_REG_6365_gs_sodi3p0_32kless_len;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].name = "dpidle";
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].array =
AP_PMIC_REG_6365_gs_deepidle___lp_mp3_32kless;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].array_sz =
AP_PMIC_REG_6365_gs_deepidle___lp_mp3_32kless_len;
} else if (!strcmp(p->regulator, "3_vbuck1")) {
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].name = "suspend";
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].array =
AP_PMIC_REG_MT6315_gs_suspend_32kless;
p->user[MTK_LPM_PWR_GS_TYPE_SUSPEND].array_sz =
AP_PMIC_REG_MT6315_gs_suspend_32kless_len;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].name = "sodi3";
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].array =
AP_PMIC_REG_MT6315_gs_sodi3p0_32kless;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP_26M].array_sz =
AP_PMIC_REG_MT6315_gs_sodi3p0_32kless_len;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].name = "dpidle";
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].array =
AP_PMIC_REG_MT6315_gs_deepidle___lp_mp3_32kless;
p->user[MTK_LPM_PWR_GS_TYPE_VCORELP].array_sz =
AP_PMIC_REG_MT6315_gs_deepidle___lp_mp3_32kless_len;
} else
return -EINVAL;
return 0;
}
struct mtk_lpm_gs_pmic_info mt6833_pmic_infos = {
.pmic = mt6833_pmic,
.attach = mt6833_power_gs_pmic_user_attach,
};
int mt6833_power_gs_init(void)
{
mtk_lpm_callee_registry(&mt6833_pwr_gs_callee);
/* initial gs compare method */
mtk_lpm_pwr_gs_common_init();
/* initial gs pmic information */
mtk_lpm_pwr_gs_compare_init(MTK_LPM_GS_CMP_PMIC, &mt6833_pmic_infos);
#ifdef MTK_LPM_GS_PLAT_CLK_DUMP_SUPPORT
/* initial gs dcm information */
mtk_lpm_pwr_gs_compare_init(MTK_LPM_GS_CMP_CLK, &mt6833_clk_infos);
#endif
return 0;
}
int mt6833_power_gs_deinit(void)
{
mtk_lpm_pwr_gs_common_deinit();
return 0;
}
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MT6833_PWR_GS_COMPARE_H__
#define __MT6833_PWR_GS_COMPARE_H__
int mt6833_power_gs_init(void);
int mt6833_power_gs_deinit(void);
#endif
@@ -0,0 +1,309 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
/* This file is generated by GenLP_setting.pl v1.5.7 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
const unsigned int AP_CG_Golden_Setting_tcl_gs_dpidle_data[] = {
/* Address Mask Golden Setting Value */
0x0C53C8A0, 0x00000002, 0x00000002,/* MP0_DCM_CFG8 */
0x0D01A070, 0x00000001, 0x00000001,/* ATB_CON */
0x0D0A002C, 0x00000001, 0x00000001,/* DBGBUSCLK_EN */
0x0D0A0030, 0x00000001, 0x00000001,/* DBGSYSCLK_EN */
0x0D0A0070, 0x00000001, 0x00000001,/* ATB_CON */
0x10000000, 0x00000F00, 0x00000000,/* CLK_MODE */
0x10000010, 0x80808080, 0x00800000,/* CLK_CFG_0 */
0x10000020, 0x80808080, 0x80808080,/* CLK_CFG_1 */
0x10000030, 0x80808080, 0x80808080,/* CLK_CFG_2 */
0x10000050, 0x80800000, 0x80800000,/* CLK_CFG_4 */
0x10000060, 0x80808080, 0x80808080,/* CLK_CFG_5 */
0x10000070, 0x80808080, 0x80808080,/* CLK_CFG_6 */
0x10000080, 0x80808080, 0x80808080,/* CLK_CFG_7 */
0x10000090, 0x80808080, 0x00800000,/* CLK_CFG_8 */
0x100000A0, 0x80808080, 0x80808080,/* CLK_CFG_9 */
0x100000B0, 0x80808080, 0x80808080,/* CLK_CFG_10 */
0x100000C0, 0x80808080, 0x80808080,/* CLK_CFG_11 */
0x100000D0, 0x80808080, 0x80808080,/* CLK_CFG_12 */
0x100000E0, 0x80008080, 0x80008080,/* CLK_CFG_13 */
0x10000100, 0x00800000, 0x00000000,/* CLK_CFG_15 */
0x10000110, 0x00008000, 0x00008000,/* CLK_CFG_16 */
0x10000140, 0x00003030, 0x00003000,/* CLK_MISC_CFG_0 */
0x10000320, 0x000007FF, 0x000007FF,/* CLK_AUDDIV_0 */
0x10001070, 0x00000004, 0x00000000,/* INFRA_BUS_DCM_CTRL */
0x10001074, 0x00000004, 0x00000000,/* PERI_BUS_DCM_CTRL */
0x10001080, 0x9BEFFFFF, 0x9BEFFFF0,/* MODULE_SW_CG_0_SET */
0x10001084, 0x9BEFFFFF, 0x00000000,/* MODULE_SW_CG_0_CLR */
0x10001088, 0xFF9FFFD6, 0x7F9FBB56,/* MODULE_SW_CG_1_SET */
0x1000108C, 0xFF9FFFD7, 0x00000000,/* MODULE_SW_CG_1_CLR */
0x100010A4, 0xFFFFFEFF, 0xFFFFFEFF,/* MODULE_SW_CG_2_SET */
0x100010A8, 0xFFFFFEFF, 0x00000000,/* MODULE_SW_CG_2_CLR */
0x100010C0, 0xFFFFCFFF, 0xFFFFCFFF,/* MODULE_SW_CG_3_SET */
0x100010C4, 0xFFFFCFFF, 0x00300000,/* MODULE_SW_CG_3_CLR */
0x100010E0, 0xFFFF007F, 0xFFFF007F,/* MODULE_SW_CG_4_SET */
0x100010E4, 0xFFFF007F, 0x01000000,/* MODULE_SW_CG_4_CLR */
0x1000320C, 0x80000000, 0x00000000,/* PERIAXI_SI0_CTL */
0x10006B00, 0x0100001E, 0x01000000,/* MD32PCM_CK_EN */
0x10006B04, 0x0000001E, 0x00000000,/* MD32PCM_CKCTRL_FORCE_CK_ON */
0x1000C014, 0x00030B78, 0x00000020,/* AP_PLL_CON5 */
0x1000C200, 0x00000FFF, 0x00000FA0,/* AP_PLLGP1_CON0 */
0x1000C208, 0x00000001, 0x00000001,/* ARMPLL_LL_CON0 */
0x1000C218, 0x00000001, 0x00000001,/* ARMPLL_BL0_CON0 */
0x1000C228, 0x00000001, 0x00000001,/* ARMPLL_BL1_CON0 */
0x1000C238, 0x00000001, 0x00000001,/* ARMPLL_BL2_CON0 */
0x1000C248, 0x00000001, 0x00000001,/* ARMPLL_BL3_CON0 */
0x1000C258, 0x00000001, 0x00000001,/* CCIPLL_CON0 */
0x1000C268, 0x00000001, 0x00000000,/* MFGPLL_CON0 */
0x1000C300, 0xC60003FF, 0x06000184,/* AP_PLLGP2_CON0 */
0x1000C304, 0x0FFA0000, 0x0FFA0000,/* AP_PLLGP2_CON1 */
0x1000C308, 0xFF000001, 0xFC000000,/* UNIVPLL_CON0 */
0x1000C318, 0x00000001, 0x00000000,/* APLL1_CON0 */
0x1000C32C, 0x00000001, 0x00000000,/* APLL2_CON0 */
0x1000C340, 0x7F000001, 0x7C000001,/* MAINPLL_CON0 */
0x1000C350, 0x00000001, 0x00000000,/* MSDCPLL_CON0 */
0x1000C360, 0xFF000001, 0x00000000,/* MMPLL_CON0 */
0x1000C370, 0xFF000001, 0x00000000,/* ADSPPLL_CON0 */
0x1000C380, 0x00000001, 0x00000000,/* TVDPLL_CON0 */
0x1000C390, 0x00000001, 0x00000001,/* MPLL_CON0 */
0x1000C3A0, 0xFF000001, 0x00000000,/* APUPLL_CON0 */
0x1000C3B4, 0xFF000001, 0x00000000,/* NPUPLL_CON0 */
0x10012000, 0x00000001, 0x00000000,/* DVFSRC_BASIC_CONTROL */
0x1020EB00, 0x00000007, 0x00000000,/* BUS_MON_CKEN */
0x1021C210, 0x0000000F, 0x00000000,/* BUS_MEM_MON_CKEN */
0x102280F0, 0x00010000, 0x00010000,/* GCE_CTL_INT0 */
0x1022D468, 0x0000003F, 0x00000000,/* CG_EN */
0x10801000, 0x00000CFB, 0x00000000,/* ADSP_CK_EN */
0x10801010, 0x00000001, 0x00000000,/* ADSP_UART_CTRL */
0x10943000, 0x00001000, 0x00000000,/* SSPM_CK_EN */
0x10943004, 0x00000100, 0x00000100,/* SSPM_MCLK_DIV */
0x10943010, 0x00000001, 0x00000000,/* SSPM_UART_CTRL */
0x1094703C, 0x00000002, 0x00000002,/* LPIF_DFD_DBUG_0 */
0x11007E00, 0x00000003, 0x00000003,/* AP_CLOCK_CG_CEN_STA */
0x11007E04, 0x00000003, 0x00000000,/* AP_CLOCK_CG_CEN_CLR */
0x11007E08, 0x00000003, 0x00000003,/* AP_CLOCK_CG_CEN_SET */
0x1100B0E4, 0x00000001, 0x00000000,/* LVTSCLKEN_0 */
0x1100B1E4, 0x00000001, 0x00000000,/* LVTSCLKEN_1 */
0x1100B2E4, 0x00000001, 0x00000000,/* LVTSCLKEN_2 */
0x1100B3E4, 0x00000001, 0x00000000,/* LVTSCLKEN_3 */
0x1100BF00, 0x800F0000, 0x00000000,/* */
0x112300B4, 0x00400000, 0x00000000,/* PATCH_BIT1 */
0x11CB1E00, 0x00000001, 0x00000001,/* AP_CLOCK_CG_EST_STA */
0x11CB1E04, 0x00000001, 0x00000001,/* AP_CLOCK_CG_EST_CLR */
0x11CB1E08, 0x00000001, 0x00000001,/* AP_CLOCK_CG_EST_SET */
0x11D02E00, 0x00000003, 0x00000003,/* AP_CLOCK_CG_SOU_STA */
0x11D02E04, 0x00000003, 0x00000000,/* AP_CLOCK_CG_SOU_CLR */
0x11D02E08, 0x00000003, 0x00000003,/* AP_CLOCK_CG_SOU_SET */
0x11D23E00, 0x00000007, 0x00000007,/* AP_CLOCK_CG_WEST_SOU_STA */
0x11D23E04, 0x00000007, 0x00000000,/* AP_CLOCK_CG_WEST_SOU_CLR */
0x11D23E08, 0x00000007, 0x00000007,/* AP_CLOCK_CG_WEST_SOU_SET */
0x11E03E00, 0x00000007, 0x00000007,/* AP_CLOCK_CG_WST_STA */
0x11E03E04, 0x00000007, 0x00000000,/* AP_CLOCK_CG_WST_CLR */
0x11E03E08, 0x00000007, 0x00000007,/* AP_CLOCK_CG_WST_SET */
0x11E40380, 0x00000001, 0x00000000,/* U2PHYBC12C */
0x11F01E00, 0x00000001, 0x00000001,/* AP_CLOCK_CG_NOR_STA */
0x11F01E04, 0x00000001, 0x00000001,/* AP_CLOCK_CG_NOR_CLR */
0x11F01E08, 0x00000001, 0x00000001,/* AP_CLOCK_CG_NOR_SET */
0x13FBF004, 0x00000001, 0x00000001,/* MFG_CG_SET */
0x13FBF008, 0x00000001, 0x00000000,/* MFG_CG_CLR */
0x14000100, 0xFFFFFFFF, 0xFFFFFFFF,/* MMSYS_CG_CON0 */
0x14000110, 0xFFFFFFFF, 0xFFFFFFFF,/* MMSYS_CG_CON1 */
0x140001A0, 0xFFFFFFFF, 0xFFFFFFFF,/* MMSYS_CG_CON2 */
0x15020000, 0x00001007, 0x00001007,/* IMG_CG_CON */
0x15820000, 0x000011C3, 0x000011C3,/* IMG_CG_CON */
0x1602F000, 0x00000001, 0x00000000,/* VDEC_CKEN_SET */
0x1602F004, 0x00000001, 0x00000001,/* VDEC_CKEN_CLR */
0x1602F008, 0x00000001, 0x00000000,/* LARB_CKEN_CON_SET */
0x1602F00C, 0x00000001, 0x00000001,/* LARB_CKEN_CON_CLR */
0x1602F200, 0x00000001, 0x00000000,/* LAT_CKEN_SET */
0x1602F204, 0x00000001, 0x00000001,/* LAT_CKEN_CLR */
0x17000000, 0x10000111, 0x00000000,/* VENCSYS_CG_CON */
0x17000004, 0x10000111, 0x00000000,/* VENCSYS_CG_SET */
0x17000008, 0x10000111, 0x10000111,/* VENCSYS_CG_CLR */
0x1A000000, 0x000FFDCF, 0x000FFDCF,/* CAMSYS_CG_CON */
0x1A04F000, 0x00000007, 0x00000007,/* CAMSYS_CG_CON */
0x1A06F000, 0x00000007, 0x00000007,/* CAMSYS_CG_CON */
0x1B000000, 0x0000017F, 0x0000017F,/* IMG_CG_CON */
0x1F000100, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON0 */
0x1F000110, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON1 */
0x1F000120, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON2 */
0x1F000130, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON3 */
0x1F000140, 0xFFFFFFFF, 0xFFFFFFFF /* MDPSYS_CG_CON4 */
};
const unsigned int *AP_CG_Golden_Setting_tcl_gs_dpidle =
AP_CG_Golden_Setting_tcl_gs_dpidle_data;
unsigned int AP_CG_Golden_Setting_tcl_gs_dpidle_len = 363;
const unsigned int AP_CG_Golden_Setting_tcl_gs_suspend_data[] = {
/* Address Mask Golden Setting Value */
0x0C53C8A0, 0x00000002, 0x00000002,/* MP0_DCM_CFG8 */
0x0D01A070, 0x00000001, 0x00000001,/* ATB_CON */
0x0D0A002C, 0x00000001, 0x00000000,/* DBGBUSCLK_EN */
0x0D0A0030, 0x00000001, 0x00000000,/* DBGSYSCLK_EN */
0x0D0A0070, 0x00000001, 0x00000000,/* ATB_CON */
0x10000000, 0x00000F00, 0x00000000,/* CLK_MODE */
0x10000010, 0x80808080, 0x00800000,/* CLK_CFG_0 */
0x10000020, 0x80808080, 0x80808080,/* CLK_CFG_1 */
0x10000030, 0x80808080, 0x80808080,/* CLK_CFG_2 */
0x10000050, 0x80800000, 0x80800000,/* CLK_CFG_4 */
0x10000060, 0x80808080, 0x80808080,/* CLK_CFG_5 */
0x10000070, 0x80808080, 0x80808080,/* CLK_CFG_6 */
0x10000080, 0x80808080, 0x80808080,/* CLK_CFG_7 */
0x10000090, 0x80808080, 0x00800080,/* CLK_CFG_8 */
0x100000A0, 0x80808080, 0x80808080,/* CLK_CFG_9 */
0x100000B0, 0x80808080, 0x80808080,/* CLK_CFG_10 */
0x100000C0, 0x80808080, 0x80808080,/* CLK_CFG_11 */
0x100000D0, 0x80808080, 0x80808080,/* CLK_CFG_12 */
0x100000E0, 0x80008080, 0x80008080,/* CLK_CFG_13 */
0x10000100, 0x00800000, 0x00000000,/* CLK_CFG_15 */
0x10000110, 0x00008000, 0x00008000,/* CLK_CFG_16 */
0x10000140, 0x00003030, 0x00003000,/* CLK_MISC_CFG_0 */
0x10000320, 0x000007FF, 0x000007FF,/* CLK_AUDDIV_0 */
0x10001070, 0x00000004, 0x00000000,/* INFRA_BUS_DCM_CTRL */
0x10001074, 0x00000004, 0x00000000,/* PERI_BUS_DCM_CTRL */
0x10001080, 0x9BEFFFFF, 0x9BEFFFF0,/* MODULE_SW_CG_0_SET */
0x10001084, 0x9BEFFFFF, 0x00000000,/* MODULE_SW_CG_0_CLR */
0x10001088, 0xFF9FFFD6, 0x7F9FBB56,/* MODULE_SW_CG_1_SET */
0x1000108C, 0xFF9FFFD7, 0x00000000,/* MODULE_SW_CG_1_CLR */
0x100010A4, 0xFFFFFEFF, 0xFFFFFEFF,/* MODULE_SW_CG_2_SET */
0x100010A8, 0xFFFFFEFF, 0x00000000,/* MODULE_SW_CG_2_CLR */
0x100010C0, 0xFFFFCFFF, 0xFFFFCFFF,/* MODULE_SW_CG_3_SET */
0x100010C4, 0xFFFFCFFF, 0x00300000,/* MODULE_SW_CG_3_CLR */
0x100010E0, 0xFFFF007F, 0xFFFF007F,/* MODULE_SW_CG_4_SET */
0x100010E4, 0xFFFF007F, 0x01000000,/* MODULE_SW_CG_4_CLR */
0x1000320C, 0x80000000, 0x00000000,/* PERIAXI_SI0_CTL */
0x10006B00, 0x0100001E, 0x01000000,/* MD32PCM_CK_EN */
0x10006B04, 0x0000001E, 0x00000000,/* MD32PCM_CKCTRL_FORCE_CK_ON */
0x1000C014, 0x00030B78, 0x00000020,/* AP_PLL_CON5 */
0x1000C200, 0x00000FFF, 0x00000FA0,/* AP_PLLGP1_CON0 */
0x1000C208, 0x00000001, 0x00000001,/* ARMPLL_LL_CON0 */
0x1000C218, 0x00000001, 0x00000001,/* ARMPLL_BL0_CON0 */
0x1000C228, 0x00000001, 0x00000001,/* ARMPLL_BL1_CON0 */
0x1000C238, 0x00000001, 0x00000001,/* ARMPLL_BL2_CON0 */
0x1000C248, 0x00000001, 0x00000001,/* ARMPLL_BL3_CON0 */
0x1000C258, 0x00000001, 0x00000001,/* CCIPLL_CON0 */
0x1000C268, 0x00000001, 0x00000000,/* MFGPLL_CON0 */
0x1000C300, 0xC60003FF, 0x06000184,/* AP_PLLGP2_CON0 */
0x1000C304, 0x0FFA0000, 0x0FFA0000,/* AP_PLLGP2_CON1 */
0x1000C308, 0xFF000001, 0xFC000000,/* UNIVPLL_CON0 */
0x1000C318, 0x00000001, 0x00000000,/* APLL1_CON0 */
0x1000C32C, 0x00000001, 0x00000000,/* APLL2_CON0 */
0x1000C340, 0x7F000001, 0x7C000001,/* MAINPLL_CON0 */
0x1000C350, 0x00000001, 0x00000000,/* MSDCPLL_CON0 */
0x1000C360, 0xFF000001, 0x00000000,/* MMPLL_CON0 */
0x1000C370, 0xFF000001, 0x00000000,/* ADSPPLL_CON0 */
0x1000C380, 0x00000001, 0x00000000,/* TVDPLL_CON0 */
0x1000C390, 0x00000001, 0x00000001,/* MPLL_CON0 */
0x1000C3A0, 0xFF000001, 0x00000000,/* APUPLL_CON0 */
0x1000C3B4, 0xFF000001, 0x00000000,/* NPUPLL_CON0 */
0x10012000, 0x00000001, 0x00000000,/* DVFSRC_BASIC_CONTROL */
0x1020EB00, 0x00000007, 0x00000000,/* BUS_MON_CKEN */
0x1021C210, 0x0000000F, 0x00000000,/* BUS_MEM_MON_CKEN */
0x102280F0, 0x00010000, 0x00010000,/* GCE_CTL_INT0 */
0x1022D468, 0x0000003F, 0x00000000,/* CG_EN */
0x10801000, 0x00000CFB, 0x00000000,/* ADSP_CK_EN */
0x10801010, 0x00000001, 0x00000000,/* ADSP_UART_CTRL */
0x10943000, 0x00001000, 0x00000000,/* SSPM_CK_EN */
0x10943004, 0x00000100, 0x00000100,/* SSPM_MCLK_DIV */
0x10943010, 0x00000001, 0x00000000,/* SSPM_UART_CTRL */
0x1094703C, 0x00000002, 0x00000002,/* LPIF_DFD_DBUG_0 */
0x11007E00, 0x00000003, 0x00000003,/* AP_CLOCK_CG_CEN_STA */
0x11007E04, 0x00000003, 0x00000000,/* AP_CLOCK_CG_CEN_CLR */
0x11007E08, 0x00000003, 0x00000003,/* AP_CLOCK_CG_CEN_SET */
0x1100B0E4, 0x00000001, 0x00000000,/* LVTSCLKEN_0 */
0x1100B1E4, 0x00000001, 0x00000000,/* LVTSCLKEN_1 */
0x1100B2E4, 0x00000001, 0x00000000,/* LVTSCLKEN_2 */
0x1100B3E4, 0x00000001, 0x00000000,/* LVTSCLKEN_3 */
0x1100BF00, 0x800F0000, 0x00000000,/* */
0x112300B4, 0x00400000, 0x00000000,/* PATCH_BIT1 */
0x11CB1E00, 0x00000001, 0x00000001,/* AP_CLOCK_CG_EST_STA */
0x11CB1E04, 0x00000001, 0x00000001,/* AP_CLOCK_CG_EST_CLR */
0x11CB1E08, 0x00000001, 0x00000001,/* AP_CLOCK_CG_EST_SET */
0x11D02E00, 0x00000003, 0x00000003,/* AP_CLOCK_CG_SOU_STA */
0x11D02E04, 0x00000003, 0x00000000,/* AP_CLOCK_CG_SOU_CLR */
0x11D02E08, 0x00000003, 0x00000003,/* AP_CLOCK_CG_SOU_SET */
0x11D23E00, 0x00000007, 0x00000007,/* AP_CLOCK_CG_WEST_SOU_STA */
0x11D23E04, 0x00000007, 0x00000000,/* AP_CLOCK_CG_WEST_SOU_CLR */
0x11D23E08, 0x00000007, 0x00000007,/* AP_CLOCK_CG_WEST_SOU_SET */
0x11E03E00, 0x00000007, 0x00000007,/* AP_CLOCK_CG_WST_STA */
0x11E03E04, 0x00000007, 0x00000000,/* AP_CLOCK_CG_WST_CLR */
0x11E03E08, 0x00000007, 0x00000007,/* AP_CLOCK_CG_WST_SET */
0x11E40380, 0x00000001, 0x00000000,/* U2PHYBC12C */
0x11F01E00, 0x00000001, 0x00000001,/* AP_CLOCK_CG_NOR_STA */
0x11F01E04, 0x00000001, 0x00000001,/* AP_CLOCK_CG_NOR_CLR */
0x11F01E08, 0x00000001, 0x00000001,/* AP_CLOCK_CG_NOR_SET */
0x13FBF004, 0x00000001, 0x00000001,/* MFG_CG_SET */
0x13FBF008, 0x00000001, 0x00000000,/* MFG_CG_CLR */
0x14000100, 0xFFFFFFFF, 0xFFFFFFFF,/* MMSYS_CG_CON0 */
0x14000110, 0xFFFFFFFF, 0xFFFFFFFF,/* MMSYS_CG_CON1 */
0x140001A0, 0xFFFFFFFF, 0xFFFFFFFF,/* MMSYS_CG_CON2 */
0x15020000, 0x00001007, 0x00001007,/* IMG_CG_CON */
0x15820000, 0x000011C3, 0x000011C3,/* IMG_CG_CON */
0x1602F000, 0x00000001, 0x00000000,/* VDEC_CKEN_SET */
0x1602F004, 0x00000001, 0x00000001,/* VDEC_CKEN_CLR */
0x1602F008, 0x00000001, 0x00000000,/* LARB_CKEN_CON_SET */
0x1602F00C, 0x00000001, 0x00000001,/* LARB_CKEN_CON_CLR */
0x1602F200, 0x00000001, 0x00000000,/* LAT_CKEN_SET */
0x1602F204, 0x00000001, 0x00000001,/* LAT_CKEN_CLR */
0x17000000, 0x10000111, 0x00000000,/* VENCSYS_CG_CON */
0x17000004, 0x10000111, 0x00000000,/* VENCSYS_CG_SET */
0x17000008, 0x10000111, 0x10000111,/* VENCSYS_CG_CLR */
0x1A000000, 0x000FFDCF, 0x000FFDCF,/* CAMSYS_CG_CON */
0x1A04F000, 0x00000007, 0x00000007,/* CAMSYS_CG_CON */
0x1A06F000, 0x00000007, 0x00000007,/* CAMSYS_CG_CON */
0x1B000000, 0x0000017F, 0x0000017F,/* IMG_CG_CON */
0x1F000100, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON0 */
0x1F000110, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON1 */
0x1F000120, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON2 */
0x1F000130, 0xFFFFFFFF, 0xFFFFFFFF,/* MDPSYS_CG_CON3 */
0x1F000140, 0xFFFFFFFF, 0xFFFFFFFF /* MDPSYS_CG_CON4 */
};
const unsigned int *AP_CG_Golden_Setting_tcl_gs_suspend =
AP_CG_Golden_Setting_tcl_gs_suspend_data;
unsigned int AP_CG_Golden_Setting_tcl_gs_suspend_len = 363;
const unsigned int AP_CG_Golden_Setting_tcl_gs_sodi_data[] = {
/* Address Mask Golden Setting Value */
0x0D01A070, 0x00000001, 0x00000001,/* ATB_CON */
0x0D0A002C, 0x00000001, 0x00000001,/* DBGBUSCLK_EN */
0x0D0A0030, 0x00000001, 0x00000001,/* DBGSYSCLK_EN */
0x0D0A0070, 0x00000001, 0x00000001,/* ATB_CON */
0x10012000, 0x00000001, 0x00000001,/* DVFSRC_BASIC_CONTROL */
0x1022D468, 0x0000003F, 0x00000000,/* CG_EN */
0x112300B4, 0x00400000, 0x00400000,/* PATCH_BIT1 */
0x14000100, 0xFFFFFFFF, 0x00000000,/* MMSYS_CG_CON0 */
0x14000110, 0xFFFFFFFF, 0x00000000,/* MMSYS_CG_CON1 */
0x140001A0, 0xFFFFFFFF, 0x00000000,/* MMSYS_CG_CON2 */
0x15020000, 0x00001007, 0x00001007,/* IMG_CG_CON */
0x15820000, 0x000011C3, 0x000011C3,/* IMG_CG_CON */
0x1602F000, 0x00000001, 0x00000001,/* VDEC_CKEN_SET */
0x1602F004, 0x00000001, 0x00000000,/* VDEC_CKEN_CLR */
0x1602F008, 0x00000001, 0x00000001,/* LARB_CKEN_CON_SET */
0x1602F00C, 0x00000001, 0x00000000,/* LARB_CKEN_CON_CLR */
0x1602F200, 0x00000001, 0x00000001,/* LAT_CKEN_SET */
0x1602F204, 0x00000001, 0x00000000,/* LAT_CKEN_CLR */
0x17000000, 0x10000111, 0x00000000,/* VENCSYS_CG_CON */
0x17000004, 0x10000111, 0x00000000,/* VENCSYS_CG_SET */
0x17000008, 0x10000111, 0x10000111,/* VENCSYS_CG_CLR */
0x1A000000, 0x000FFDCF, 0x000FFDCF,/* CAMSYS_CG_CON */
0x1A04F000, 0x00000007, 0x00000007,/* CAMSYS_CG_CON */
0x1A06F000, 0x00000007, 0x00000007,/* CAMSYS_CG_CON */
0x1B000000, 0x0000017F, 0x0000017F,/* IMG_CG_CON */
0x1F000100, 0xFFFFFFFF, 0x00000000,/* MDPSYS_CG_CON0 */
0x1F000110, 0xFFFFFFFF, 0x00000000,/* MDPSYS_CG_CON1 */
0x1F000120, 0xFFFFFFFF, 0x00000000,/* MDPSYS_CG_CON2 */
0x1F000130, 0xFFFFFFFF, 0x00000000,/* MDPSYS_CG_CON3 */
0x1F000140, 0xFFFFFFFF, 0x00000000 /* MDPSYS_CG_CON4 */
};
const unsigned int *AP_CG_Golden_Setting_tcl_gs_sodi =
AP_CG_Golden_Setting_tcl_gs_sodi_data;
unsigned int AP_CG_Golden_Setting_tcl_gs_sodi_len = 90;
@@ -0,0 +1,279 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/pm_qos.h>
#include <mtk_lpm.h>
#include <mtk_lp_plat_reg.h>
#include "mtk_cpupm_dbg.h"
#include "mtk_cpuidle_cpc.h"
static DEFINE_SPINLOCK(cpc_prof_spin_lock);
/* profile latecny */
#define PROF_DEV_NAME_SIZE 36
#define get_core_type_index(cpu) ((cpu > 3) ? CPU_TYPE_B : CPU_TYPE_L)
struct mtk_cpc_lat_data {
char name[PROF_DEV_NAME_SIZE];
unsigned int on_sum;
unsigned int on_max;
unsigned int off_sum;
unsigned int off_max;
unsigned int on_cnt;
unsigned int off_cnt;
};
struct mtk_cpc_device {
union {
struct mtk_cpc_lat_data p[DEV_TYPE_NUM];
struct {
struct mtk_cpc_lat_data cpu[NF_CPU_TYPE];
struct mtk_cpc_lat_data cluster;
struct mtk_cpc_lat_data mcusys;
};
};
bool prof_en;
};
struct mtk_cpc_device cpc;
static void mtk_cpc_clr_lat(void)
{
unsigned int size;
unsigned int ofs;
int i;
ofs = sizeof(cpc.p[0].name);
size = sizeof(struct mtk_cpc_lat_data) - ofs;
for (i = 0; i < DEV_TYPE_NUM; i++)
memset((char *)&cpc.p[i] + ofs, 0, size);
}
static void mtk_cpc_cal_lat(void)
{
}
#define __mtk_cpc_record_lat(sum, max, lat) \
do { \
if (lat > max) \
max = lat; \
(sum) += (lat); \
} while (0)
static void mtk_cpc_record_lat(struct mtk_cpc_lat_data *lat,
unsigned int on_ticks, unsigned int off_ticks)
{
unsigned long flags;
if ((on_ticks == 0) || (off_ticks == 0))
return;
spin_lock_irqsave(&cpc_prof_spin_lock, flags);
__mtk_cpc_record_lat(lat->on_sum, lat->on_max, on_ticks);
lat->on_cnt++;
__mtk_cpc_record_lat(lat->off_sum, lat->off_max, off_ticks);
lat->off_cnt++;
spin_unlock_irqrestore(&cpc_prof_spin_lock, flags);
}
static void mtk_cpc_save_cpu_lat(int cpu)
{
unsigned int lat, on, off;
lat = mtk_cpupm_mcusys_read(CPC_CPU_LATENCY(cpu));
on = (lat >> 16) & 0xFFFF;
off = lat & 0xFFFF;
mtk_cpc_record_lat(&cpc.cpu[get_core_type_index(cpu)],
on, off);
}
static void mtk_cpc_save_cpusys_lat(void)
{
unsigned int lat_on, lat_off, on, off;
lat_on = mtk_cpupm_mcusys_read(CPC_CLUSTER_ON_LATENCY);
lat_off = mtk_cpupm_mcusys_read(CPC_CLUSTER_OFF_LATENCY);
on = lat_on & 0xFFFF;
off = lat_off & 0xFFFF;
mtk_cpc_record_lat(&cpc.cluster, on, off);
}
static void mtk_cpc_save_mcusys_lat(void)
{
unsigned int lat, on, off;
lat = mtk_cpupm_mcusys_read(CPC_MCUSYS_LATENCY);
on = (lat >> 16) & 0xFFFF;
off = lat & 0xFFFF;
mtk_cpc_record_lat(&cpc.mcusys, on, off);
}
static bool mtk_cpc_did_cluster_pwr_off(void)
{
static unsigned int last_cnt;
unsigned int cnt = mtk_cpupm_mcusys_read(CPC_DORMANT_COUNTER);
/**
* Cluster off count
* bit[0:15] : memory retention
* bit[16:31] : memory off
*/
if ((cnt & 0x7FFF) == 0)
cnt = ((cnt >> 16) & 0x7FFF);
else
cnt = cnt & 0x7FFF;
cnt += mtk_cpupm_syssram_read(SYSRAM_CPC_CPUSYS_CNT_BACKUP);
if (last_cnt == cnt)
return false;
last_cnt = cnt;
return true;
}
static bool mtk_cpc_did_mcusys_pwr_off(void)
{
static unsigned int last_cnt;
unsigned int cnt = mtk_cpupm_syssram_read(SYSRAM_MCUPM_MCUSYS_COUNTER);
cnt += mtk_cpupm_syssram_read(SYSRAM_MCUSYS_CNT);
if (last_cnt == cnt)
return false;
last_cnt = cnt;
return true;
}
static void mtk_cpc_save_latency(int cpu)
{
mtk_cpc_save_cpu_lat(cpu);
if (mtk_cpc_did_mcusys_pwr_off())
mtk_cpc_save_mcusys_lat();
else if (!mtk_cpc_did_cluster_pwr_off())
return;
mtk_cpc_save_cpusys_lat();
}
void mtk_cpc_prof_lat_dump(struct seq_file *m)
{
struct mtk_cpc_lat_data *lat;
int i;
for (i = 0; i < DEV_TYPE_NUM; i++) {
lat = &cpc.p[i];
seq_printf(m, "%s\n", lat->name);
if (lat->off_cnt) {
seq_printf(m, "\toff : avg = %2dus, max = %3dus, cnt = %d\n",
cpc_tick_to_us(lat->off_sum / lat->off_cnt),
cpc_tick_to_us(lat->off_max),
lat->off_cnt);
} else {
seq_puts(m, "\toff : None\n");
}
if (lat->on_cnt) {
seq_printf(m, "\ton : avg = %2dus, max = %3dus, cnt = %d\n",
cpc_tick_to_us(lat->on_sum / lat->on_cnt),
cpc_tick_to_us(lat->on_max),
lat->on_cnt);
} else {
seq_puts(m, "\ton : None\n");
}
}
}
void mtk_cpc_prof_start(void)
{
mtk_cpupm_block();
cpc_prof_en();
mtk_cpc_clr_lat();
cpc.prof_en = true;
mtk_cpupm_allow();
}
void mtk_cpc_prof_stop(void)
{
mtk_cpupm_block();
cpc.prof_en = false;
mtk_cpc_cal_lat();
cpc_prof_dis();
mtk_cpupm_allow();
}
int mtk_cpc_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct mtk_lpm_nb_data *nb_data = (struct mtk_lpm_nb_data *)data;
if (!cpc.prof_en)
return NOTIFY_OK;
if (action & MTK_LPM_NB_BEFORE_REFLECT)
mtk_cpc_save_latency(nb_data->cpu);
return NOTIFY_OK;
}
struct notifier_block mtk_cpc_nb = {
.notifier_call = mtk_cpc_notify,
};
int __init mtk_cpc_init(void)
{
int ret;
cpc.prof_en = false;
ret = snprintf(cpc.cpu[CPU_TYPE_L].name, PROF_DEV_NAME_SIZE, "cpu_L");
if (ret < 0 || ret >= PROF_DEV_NAME_SIZE)
pr_info("LPM cpc debug name assign fail");
ret = snprintf(cpc.cpu[CPU_TYPE_B].name, PROF_DEV_NAME_SIZE, "cpu_B");
if (ret < 0 || ret >= PROF_DEV_NAME_SIZE)
pr_info("LPM cpc debug name assign fail");
ret = snprintf(cpc.cluster.name, PROF_DEV_NAME_SIZE, "cluster");
if (ret < 0 || ret >= PROF_DEV_NAME_SIZE)
pr_info("LPM cpc debug name assign fail");
ret = snprintf(cpc.mcusys.name, PROF_DEV_NAME_SIZE, "mcusys");
if (ret < 0 || ret >= PROF_DEV_NAME_SIZE)
pr_info("LPM cpc debug name assign fail");
mtk_lpm_notifier_register(&mtk_cpc_nb);
return 0;
}
void __exit mtk_cpc_exit(void)
{
mtk_lpm_notifier_unregister(&mtk_cpc_nb);
}
@@ -0,0 +1,100 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_CPUIDLE_CPC_H__
#define __MTK_CPUIDLE_CPC_H__
#include <linux/seq_file.h>
#include <mtk_lpm_module.h>
enum {
CPU_TYPE_L,
CPU_TYPE_B,
NF_CPU_TYPE
};
enum {
CPC_SMC_EVENT_DUMP_TRACE_DATA,
CPC_SMC_EVENT_GIC_DPG_SET,
CPC_SMC_EVENT_CPC_CONFIG,
CPC_SMC_EVENT_READ_CONFIG,
NF_CPC_SMC_EVENT
};
enum {
CPC_SMC_CONFIG_PROF,
CPC_SMC_CONFIG_AUTO_OFF,
CPC_SMC_CONFIG_AUTO_OFF_THRES,
CPC_SMC_CONFIG_CNT_CLR,
CPC_SMC_CONFIG_TIME_SYNC,
NF_CPC_SMC_CONFIG
};
/**
* number of profile type :
* - (number of cpu type) + cluster + mcusys
*/
#define DEV_TYPE_NUM (NF_CPU_TYPE + 2)
/* smc */
#define mtk_cpc_smc(act, arg1, arg2)\
mtk_lpm_smc_cpu_pm(CPC_COMMAND, act, arg1, arg2)
/* configuration */
#define cpc_prof_en()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_PROF, 1)
#define cpc_prof_dis()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_PROF, 0)
#define cpc_auto_off_en()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF, 1)
#define cpc_auto_off_dis()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF, 0)
#define cpc_set_auto_off_thres(us)\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF_THRES, us)
#define cpc_cluster_cnt_clr()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_CNT_CLR, 0)
#define cpc_time_sync()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_TIME_SYNC, 0)
/* status */
#define cpc_get_prof_sta()\
mtk_cpc_smc(CPC_SMC_EVENT_READ_CONFIG\
, CPC_SMC_CONFIG_PROF, 0)
#define cpc_get_auto_off_sta()\
mtk_cpc_smc(CPC_SMC_EVENT_READ_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF, 0)
#define cpc_get_auto_off_thres()\
mtk_cpc_smc(CPC_SMC_EVENT_READ_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF_THRES, 0)
#define cpc_tick_to_us(val) ((val) / 13)
void mtk_cpc_prof_start(void);
void mtk_cpc_prof_stop(void);
void mtk_cpc_prof_lat_dump(struct seq_file *m);
int __init mtk_cpc_init(void);
void __exit mtk_cpc_exit(void);
#endif /* __MTK_CPUIDLE_CPC_H__ */
@@ -0,0 +1,668 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/cpuidle.h>
#include <linux/sched/clock.h>
#include <linux/cpu.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/pm_qos.h>
#include <linux/tick.h>
#include <linux/timer.h>
//#include <mtk_ram_console.h>
#include <mtk_lpm.h>
#include <mtk_lp_plat_reg.h>
#include <mt6833_spm_comm.h>
#include "mtk_cpupm_dbg.h"
#include "mtk_cpuidle_status.h"
#include "mtk_cpuidle_cpc.h"
#define DUMP_INTERVAL sec_to_ns(5)
static u64 last_dump_ns;
static unsigned long long mtk_lpm_last_cpuidle_dis;
/* stress test */
static unsigned int timer_interval = 10 * 1000;
static struct task_struct *stress_tsk[NR_CPUS];
/* CPU off state : core, cluster and mcusys */
#define CPU_OFF_MAX_LV (3)
/* all core idle profiling */
struct all_cpu_idle_data {
unsigned long long last_ns;
unsigned long long dur_ns;
unsigned int cnt;
unsigned int bp; /* basis points : 0.01% */
};
struct all_cpu_idle {
unsigned long long start_us;
struct all_cpu_idle_data lv[CPU_OFF_MAX_LV];
};
static struct all_cpu_idle all_core_off;
/* idle ratio profiling */
struct mtk_cpuidle_ratio {
unsigned long long start_us;
unsigned long long idle_time_ns[CPUIDLE_STATE_MAX];
unsigned int bp[CPUIDLE_STATE_MAX];
};
/* idle latency profiling */
struct mtk_cpuidle_prof {
unsigned int cnt;
unsigned int max;
union {
u64 avg;
u64 sum;
};
};
/* idle information */
struct mtk_cpuidle_info {
u64 enter_time_ns;
int idle_index;
int cnt[CPUIDLE_STATE_MAX];
};
/* mtk cpu idle status */
struct mtk_cpuidle_device {
struct mtk_cpuidle_ratio ratio;
struct mtk_cpuidle_info info;
struct mtk_cpuidle_prof prof;
struct hrtimer timer;
int cpu;
int state_count;
bool tmr_running;
};
static DEFINE_PER_CPU(struct mtk_cpuidle_device, mtk_cpuidle_dev);
/* mtk cpu idle configuration */
struct mtk_cpuidle_control {
bool tmr_en;
bool prof_en;
bool log_en;
bool stress_en;
};
static struct mtk_cpuidle_control mtk_cpuidle_ctrl;
static int mtk_cpuidle_stress_task(void *arg)
{
while (mtk_cpuidle_ctrl.stress_en)
usleep_range(timer_interval - 10, timer_interval + 10);
return 0;
}
static void mtk_cpuidle_stress_start(void)
{
int i;
char name[20] = {0};
int ret = 0;
if (mtk_cpuidle_ctrl.stress_en)
return;
mtk_cpuidle_ctrl.stress_en = true;
for_each_online_cpu(i) {
ret = scnprintf(name, sizeof(name), "mtk_cpupm_stress_%d", i);
stress_tsk[i] =
kthread_create(mtk_cpuidle_stress_task, NULL, name);
if (!IS_ERR(stress_tsk[i])) {
kthread_bind(stress_tsk[i], i);
wake_up_process(stress_tsk[i]);
}
}
}
static void mtk_cpuidle_stress_stop(void)
{
mtk_cpuidle_ctrl.stress_en = false;
msleep(20);
}
void mtk_cpuidle_set_stress_test(bool en)
{
if (en)
mtk_cpuidle_stress_start();
else
mtk_cpuidle_stress_stop();
}
bool mtk_cpuidle_get_stress_status(void)
{
return mtk_cpuidle_ctrl.stress_en;
}
void mtk_cpuidle_set_stress_time(unsigned int val)
{
timer_interval = clamp_val(val, 100, 20000);
}
unsigned int mtk_cpuidle_get_stress_time(void)
{
return timer_interval;
}
void mtk_cpuidle_ctrl_timer_en(bool enable)
{
mtk_cpupm_block();
mtk_cpuidle_ctrl.tmr_en = enable;
mtk_cpupm_allow();
}
bool mtk_cpuidle_ctrl_timer_sta_get(void)
{
return mtk_cpuidle_ctrl.tmr_en;
}
void mtk_cpuidle_ctrl_log_en(bool enable)
{
mtk_cpupm_block();
mtk_cpuidle_ctrl.log_en = enable;
mtk_cpupm_allow();
}
bool mtk_cpuidle_ctrl_log_sta_get(void)
{
return mtk_cpuidle_ctrl.log_en;
}
static void _mtk_cpuidle_prof_ratio_start(void *data)
{
int i, cpu;
struct mtk_cpuidle_device *mtk_idle;
cpu = get_cpu();
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
put_cpu();
if (unlikely(!mtk_idle))
return;
mtk_idle->ratio.start_us = div64_u64(sched_clock(), 1000);
for (i = 0; i < mtk_idle->state_count; i++)
mtk_idle->ratio.idle_time_ns[i] = 0;
}
void mtk_cpuidle_prof_ratio_start(void)
{
int i;
if (mtk_cpuidle_ctrl.prof_en)
return;
mtk_cpupm_block();
for (i = 0; i < CPU_OFF_MAX_LV; i++) {
all_core_off.lv[i].dur_ns = 0;
all_core_off.lv[i].cnt = 0;
}
all_core_off.start_us = div64_u64(sched_clock(), 1000);
on_each_cpu(_mtk_cpuidle_prof_ratio_start, NULL, 1);
mtk_cpuidle_ctrl.prof_en = true;
mtk_cpupm_allow();
}
static void _mtk_cpuidle_prof_ratio_stop(void *stop_time)
{
int i, cpu;
uint64_t dur_us;
struct mtk_cpuidle_device *mtk_idle;
cpu = get_cpu();
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
put_cpu();
if (unlikely(!mtk_idle))
return;
dur_us = div64_u64(sched_clock(), 1000) -
mtk_idle->ratio.start_us;
for (i = 0; i < mtk_idle->state_count; i++)
mtk_idle->ratio.bp[i] = (unsigned int)div64_u64(
mtk_idle->ratio.idle_time_ns[i] * 10,
dur_us);
}
void mtk_cpuidle_prof_ratio_stop(void)
{
int i;
uint64_t time_us;
if (!mtk_cpuidle_ctrl.prof_en)
return;
mtk_cpupm_block();
on_each_cpu(_mtk_cpuidle_prof_ratio_stop, NULL, 1);
mtk_cpuidle_ctrl.prof_en = false;
time_us = div64_u64(sched_clock(), 1000) - all_core_off.start_us;
for (i = 0; i < CPU_OFF_MAX_LV; i++)
all_core_off.lv[i].bp = (unsigned int)div64_u64(
all_core_off.lv[i].dur_ns * 10, time_us);
mtk_cpupm_allow();
}
void mtk_cpuidle_prof_ratio_dump(struct seq_file *m)
{
int i, cpu;
struct mtk_cpuidle_device *mtk_idle;
seq_printf(m, "%6s%12s%12s%12s\n",
"",
"cpu_off",
"cluster_off",
"mcusys_off");
for_each_possible_cpu(cpu) {
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
if (!mtk_idle)
continue;
seq_printf(m, "cpu%d: ", cpu);
for (i = 1; i < mtk_idle->state_count; i++) {
seq_printf(m, "%5s%3u.%02u%%",
"",
mtk_idle->ratio.bp[i] / 100,
mtk_idle->ratio.bp[i] % 100);
}
seq_puts(m, "\n");
}
seq_puts(m, "All core off ratio :\n");
for (i = 0; i < CPU_OFF_MAX_LV; i++)
seq_printf(m, "\t C_%d = %3u.%02u%%\n",
i + 1,
all_core_off.lv[i].bp / 100,
all_core_off.lv[i].bp % 100);
}
void mtk_cpuidle_state_enable(bool en)
{
struct cpuidle_driver *drv;
int i, cpu;
int suspend_type = mtk_lpm_suspend_type_get();
mtk_cpupm_block();
for_each_possible_cpu(cpu) {
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
for (i = drv->state_count - 1; i > 0; i--) {
if ((suspend_type == MTK_LPM_SUSPEND_S2IDLE) &&
!strcmp(drv->states[i].name, S2IDLE_STATE_NAME))
continue;
mtk_cpuidle_set_param(drv, i, IDLE_PARAM_EN, en);
}
}
if (!en)
mtk_lpm_last_cpuidle_dis = sched_clock();
mtk_cpupm_allow();
}
void mtk_s2idle_state_enable(bool en)
{
struct cpuidle_driver *drv;
int i, cpu;
int suspend_type = mtk_lpm_suspend_type_get();
mtk_cpupm_block();
for_each_possible_cpu(cpu) {
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
i = drv->state_count - 1;
if ((suspend_type == MTK_LPM_SUSPEND_S2IDLE) &&
!strcmp(drv->states[i].name, S2IDLE_STATE_NAME)) {
mtk_cpuidle_set_param(drv, i, IDLE_PARAM_EN, en);
}
}
if (!en)
mtk_lpm_last_cpuidle_dis = sched_clock();
mtk_cpupm_allow();
}
unsigned long long mtk_cpuidle_state_last_dis_ms(void)
{
#if BITS_PER_LONG == 32
return div_u64(mtk_lpm_last_cpuidle_dis, 1000000);
#else
return (mtk_lpm_last_cpuidle_dis / 1000000);
#endif
}
static bool mtk_cpuidle_need_dump(unsigned int idx)
{
u64 curr_ns;
bool dump = false;
if (!mtk_cpuidle_ctrl.log_en || !idx)
return false;
curr_ns = sched_clock();
if (curr_ns - last_dump_ns > DUMP_INTERVAL) {
last_dump_ns = curr_ns;
dump = true;
}
return dump;
}
static unsigned int mtk_cpuidle_get_cluster_off_cnt(void)
{
unsigned int cnt = mtk_cpupm_mcusys_read(CPC_DORMANT_COUNTER);
/**
* Cluster off count
* bit[0:15] : memory retention
* bit[16:31] : memory off
*/
if ((cnt & 0x7FFF) == 0)
cnt = ((cnt >> 16) & 0x7FFF);
else
cnt = cnt & 0x7FFF;
cnt += mtk_cpupm_syssram_read(SYSRAM_CPC_CPUSYS_CNT_BACKUP);
cpc_cluster_cnt_clr();
mtk_cpupm_syssram_write(SYSRAM_CPC_CPUSYS_CNT_BACKUP, 0);
/**
* Add mcusys off count because CPC cluster counter will be cleared
* when mcusys power off.
*/
cnt += mtk_cpupm_syssram_read(SYSRAM_MCUPM_MCUSYS_COUNTER);
mtk_cpupm_syssram_write(SYSRAM_CPUSYS_CNT,
mtk_cpupm_syssram_read(SYSRAM_CPUSYS_CNT) + cnt);
return cnt;
}
static unsigned int mtk_cpuidle_get_mcusys_off_cnt(void)
{
unsigned int cnt = mtk_cpupm_syssram_read(SYSRAM_MCUPM_MCUSYS_COUNTER);
mtk_cpupm_syssram_write(SYSRAM_MCUPM_MCUSYS_COUNTER, 0);
mtk_cpupm_syssram_write(SYSRAM_MCUSYS_CNT,
mtk_cpupm_syssram_read(SYSRAM_MCUSYS_CNT) + cnt);
return cnt;
}
static void mtk_cpuidle_dump_info(void)
{
struct mtk_cpuidle_device *mtk_idle;
int cpu, idx, ofs;
unsigned int avail_cpu_mask = 0;
for_each_possible_cpu(cpu) {
if (cpu_online(cpu))
avail_cpu_mask |= (1 << cpu);
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
if (!mtk_idle)
continue;
ofs = SYSRAM_RECENT_CPU_CNT(cpu);
mtk_cpupm_syssram_write(ofs, 0);
for (idx = 1; idx < mtk_idle->state_count ; idx++) {
mtk_cpupm_syssram_write(ofs, mtk_cpupm_syssram_read(ofs)
+ mtk_idle->info.cnt[idx]);
mtk_idle->info.cnt[idx] = 0;
}
}
/* Should calculate cluster off count before mcusys counter reset */
mtk_cpupm_syssram_write(SYSRAM_RECENT_CPUSYS_CNT,
mtk_cpuidle_get_cluster_off_cnt());
mtk_cpupm_syssram_write(SYSRAM_RECENT_MCUSYS_CNT,
mtk_cpuidle_get_mcusys_off_cnt());
mtk_cpupm_syssram_write(SYSRAM_CPU_ONLINE, avail_cpu_mask);
mtk_cpupm_syssram_write(SYSRAM_RECENT_CNT_TS_H,
(unsigned int)((last_dump_ns >> 32) & 0xFFFFFFFF));
mtk_cpupm_syssram_write(SYSRAM_RECENT_CNT_TS_L,
(unsigned int)(last_dump_ns & 0xFFFFFFFF));
}
static enum hrtimer_restart mtk_cpuidle_hrtimer_func(struct hrtimer *timer)
{
return HRTIMER_NORESTART;
}
static void mtk_cpuidle_set_timer(struct mtk_cpuidle_device *mtk_idle)
{
int index;
int limit_sleep_us;
struct cpuidle_driver *drv;
if (!mtk_cpuidle_ctrl.tmr_en)
return;
index = mtk_idle->info.idle_index;
/* Only support WFI/CPU_OFF state */
if (index > 1)
return;
drv = cpuidle_get_driver();
if (unlikely(!drv))
return;
if (index + 1 >= mtk_idle->state_count
|| !tick_nohz_tick_stopped())
return;
limit_sleep_us = 4 * max_t(unsigned int,
get_residency(drv, index),
get_residency(drv, index + 1));
if (index != 0)
tick_broadcast_exit();
RCU_NONIDLE(hrtimer_start(&mtk_idle->timer,
ns_to_ktime(limit_sleep_us * NSEC_PER_USEC),
HRTIMER_MODE_REL_PINNED));
mtk_idle->tmr_running = true;
if (index != 0)
tick_broadcast_enter();
}
static void mtk_cpuidle_cancel_timer(struct mtk_cpuidle_device *mtk_idle)
{
if (!mtk_cpuidle_ctrl.tmr_en)
return;
if (mtk_idle->tmr_running) {
mtk_idle->tmr_running = true;
RCU_NONIDLE(hrtimer_try_to_cancel(&mtk_idle->timer));
}
}
static void mtk_cpuidle_set_last_off_ts(int idx)
{
int i;
uint64_t curr_ns = sched_clock();
if (!idx)
return;
if (idx > CPU_OFF_MAX_LV)
idx = CPU_OFF_MAX_LV;
for (i = 0; i < idx; i++) {
all_core_off.lv[i].cnt++;
if (all_core_off.lv[i].cnt == num_online_cpus())
all_core_off.lv[i].last_ns = curr_ns;
}
}
static void mtk_cpuidle_save_idle_time(int idx)
{
int i;
uint64_t curr_ns = sched_clock();
if (!idx)
return;
if (idx > CPU_OFF_MAX_LV)
idx = CPU_OFF_MAX_LV;
for (i = 0; i < idx; i++) {
if (all_core_off.lv[i].cnt == num_online_cpus())
all_core_off.lv[i].dur_ns += (curr_ns
- all_core_off.lv[i].last_ns);
all_core_off.lv[i].cnt--;
}
}
static int mtk_cpuidle_status_update(struct notifier_block *nb,
unsigned long action, void *data)
{
struct mtk_cpuidle_device *mtk_idle;
struct mtk_lpm_nb_data *nb_data = (struct mtk_lpm_nb_data *)data;
if (action & MTK_LPM_NB_BEFORE_REFLECT) {
/* prevent race conditions by mtk_lp_mod_locker */
if (mtk_cpuidle_ctrl.prof_en)
mtk_cpuidle_save_idle_time(nb_data->index);
if (mtk_cpuidle_need_dump(nb_data->index))
mtk_cpuidle_dump_info();
} else if (action & MTK_LPM_NB_RESUME) {
/* aee_rr_rec_mcdi_val(nb_data->cpu,
(nb_data->index << 16) | 0x0); */
mtk_idle = &per_cpu(mtk_cpuidle_dev, nb_data->cpu);
mtk_idle->info.idle_index = -1;
mtk_idle->info.cnt[nb_data->index]++;
if (mtk_cpuidle_ctrl.prof_en)
mtk_idle->ratio.idle_time_ns[nb_data->index] +=
(sched_clock() - mtk_idle->info.enter_time_ns);
mtk_cpuidle_cancel_timer(mtk_idle);
} else if (action & MTK_LPM_NB_AFTER_PROMPT) {
/* prevent race conditions by mtk_lp_mod_locker */
if (mtk_cpuidle_ctrl.prof_en)
mtk_cpuidle_set_last_off_ts(nb_data->index);
} else if (action & MTK_LPM_NB_PREPARE) {
mtk_idle = &per_cpu(mtk_cpuidle_dev, nb_data->cpu);
mtk_idle->info.idle_index = nb_data->index;
mtk_idle->info.enter_time_ns = sched_clock();
mtk_cpuidle_set_timer(mtk_idle);
/* aee_rr_rec_mcdi_val(nb_data->cpu,
(nb_data->index << 16) | 0xff); */
}
return NOTIFY_OK;
}
struct notifier_block mtk_cpuidle_status_nb = {
.notifier_call = mtk_cpuidle_status_update,
};
static void mtk_cpuidle_init_per_cpu(void *info)
{
struct mtk_cpuidle_device *mtk_idle;
int cpu;
cpu = get_cpu();
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
put_cpu();
hrtimer_init(&mtk_idle->timer,
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
mtk_idle->cpu = cpu;
mtk_idle->timer.function = mtk_cpuidle_hrtimer_func;
mtk_idle->state_count = mtk_cpupm_get_idle_state_count(cpu);
}
int __init mtk_cpuidle_status_init(void)
{
mtk_cpuidle_ctrl.tmr_en = true;
mtk_cpuidle_ctrl.prof_en = false;
mtk_cpuidle_ctrl.log_en = true;
mtk_cpuidle_ctrl.stress_en = false;
mtk_cpupm_block();
on_each_cpu(mtk_cpuidle_init_per_cpu, NULL, 0);
mtk_cpupm_allow();
mtk_lpm_notifier_register(&mtk_cpuidle_status_nb);
return 0;
}
void __exit mtk_cpuidle_status_exit(void)
{
mtk_lpm_notifier_unregister(&mtk_cpuidle_status_nb);
}
@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_CPUIDLE_STATUS_H__
#define __MTK_CPUIDLE_STATUS_H__
#include <linux/seq_file.h>
#define sec_to_ns(v) ((v) * 1000 * 1000 * 1000ULL)
#define S2IDLE_STATE_NAME "s2idle"
enum {
IDLE_PARAM_LAT,
IDLE_PARAM_RES,
IDLE_PARAM_EN,
NF_IDLE_PARAM
};
#define get_residency(drv, idx)\
((drv)->states[idx].target_residency)
#define get_latency(drv, idx)\
((drv)->states[idx].exit_latency)
#define get_disabled(drv, idx)\
((drv)->states[idx].disabled)
#define mtk_cpuidle_get_param(drv, state, param) \
({ \
int val = -1; \
if (param == IDLE_PARAM_LAT) \
val = (int)get_latency(drv, state); \
else if (param == IDLE_PARAM_RES) \
val = (int)get_residency(drv, state); \
else if (param == IDLE_PARAM_EN) \
val = get_disabled(drv, state) ? 0 : 1 ; \
val; \
})
#define mtk_cpuidle_set_param(drv, state, param, val) \
do { \
if (param == IDLE_PARAM_LAT) \
get_latency(drv, state) = val; \
else if (param == IDLE_PARAM_RES) \
get_residency(drv, state) = val; \
else if (param == IDLE_PARAM_EN) \
get_disabled(drv, state) = !val ; \
} while (0)
void mtk_cpuidle_set_stress_test(bool en);
bool mtk_cpuidle_get_stress_status(void);
void mtk_cpuidle_set_stress_time(unsigned int val);
unsigned int mtk_cpuidle_get_stress_time(void);
void mtk_cpuidle_prof_ratio_start(void);
void mtk_cpuidle_prof_ratio_stop(void);
void mtk_cpuidle_prof_ratio_dump(struct seq_file *m);
void mtk_cpuidle_ctrl_timer_en(bool enable);
bool mtk_cpuidle_ctrl_timer_sta_get(void);
void mtk_cpuidle_ctrl_log_en(bool enable);
bool mtk_cpuidle_ctrl_log_sta_get(void);
void mtk_cpuidle_state_enable(bool en);
void mtk_s2idle_state_enable(bool en);
int __init mtk_cpuidle_status_init(void);
void __exit mtk_cpuidle_status_exit(void);
unsigned long long mtk_cpuidle_state_last_dis_ms(void);
#endif /* __MTK_CPUIDLE_STATUS_H__ */
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cpuidle.h>
#include <linux/pm_qos.h>
#include "mtk_cpuidle_status.h"
#include "mtk_cpuidle_cpc.h"
#include "mtk_idle_procfs.h"
static void __iomem *cpupm_mcusys_base_addr;
static void __iomem *cpupm_syssram_base_addr;
static struct pm_qos_request cpuidle_dbg_qos_req;
int mtk_cpupm_mcusys_write(int ofs, unsigned int val)
{
if (!cpupm_mcusys_base_addr)
return -EADDRNOTAVAIL;
if ((ofs % 4) != 0)
return -EINVAL;
__raw_writel(val, cpupm_mcusys_base_addr + ofs);
mb(); /* make sure register access in order */
return 0;
}
unsigned int mtk_cpupm_mcusys_read(int ofs)
{
if (!cpupm_mcusys_base_addr)
return 0;
if ((ofs % 4) != 0)
return 0;
return __raw_readl(cpupm_mcusys_base_addr + ofs);
}
int mtk_cpupm_syssram_write(int ofs, unsigned int val)
{
if (!cpupm_syssram_base_addr)
return -EADDRNOTAVAIL;
if ((ofs % 4) != 0)
return -EINVAL;
__raw_writel(val, cpupm_syssram_base_addr + ofs);
mb(); /* make sure register access in order */
return 0;
}
unsigned int mtk_cpupm_syssram_read(int ofs)
{
if (!cpupm_syssram_base_addr)
return 0;
if ((ofs % 4) != 0)
return 0;
return __raw_readl(cpupm_syssram_base_addr + ofs);
}
void mtk_cpupm_block(void)
{
pm_qos_update_request(&cpuidle_dbg_qos_req, 2);
}
void mtk_cpupm_allow(void)
{
pm_qos_update_request(&cpuidle_dbg_qos_req, PM_QOS_DEFAULT_VALUE);
}
int mtk_cpupm_get_idle_state_count(int cpu)
{
struct device_node *state_node, *cpu_node;
int i, state_count, start_idx = 1;
cpu_node = of_cpu_device_node_get(cpu);
if (!cpu_node)
return start_idx;
for (i = 0; ; i++) {
state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
if (!state_node)
break;
of_node_put(state_node);
}
of_node_put(cpu_node);
state_count = i + start_idx;
if (state_count > CPUIDLE_STATE_MAX)
state_count = CPUIDLE_STATE_MAX;
return state_count;
}
static void __init mtk_cpupm_node_init(void)
{
struct device_node *node = NULL;
cpupm_mcusys_base_addr = NULL;
cpupm_syssram_base_addr = NULL;
node = of_find_compatible_node(NULL, NULL,
"mediatek,mcusys-ctrl");
if (node) {
cpupm_mcusys_base_addr = of_iomap(node, 0);
of_node_put(node);
}
node = of_find_compatible_node(NULL, NULL,
"mediatek,cpupm-sysram");
if (node) {
cpupm_syssram_base_addr = of_iomap(node, 0);
of_node_put(node);
}
}
static int __init mtk_cpupm_dbg_init(void)
{
pm_qos_add_request(&cpuidle_dbg_qos_req,
PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
mtk_cpupm_node_init();
mtk_cpuidle_status_init();
mtk_cpc_init();
mtk_idle_procfs_init();
cpc_time_sync();
return 0;
}
static void __exit mtk_cpupm_dbg_exit(void)
{
mtk_idle_procfs_exit();
mtk_cpuidle_status_exit();
mtk_cpc_exit();
pm_qos_remove_request(&cpuidle_dbg_qos_req);
}
module_init(mtk_cpupm_dbg_init);
module_exit(mtk_cpupm_dbg_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MediaTek CPU Idle Debug FileSystem");
MODULE_AUTHOR("MediaTek Inc.");
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_CPUPM_DBG_H__
#define __MTK_CPUPM_DBG_H__
int mtk_cpupm_mcusys_write(int ofs, unsigned int val);
unsigned int mtk_cpupm_mcusys_read(int ofs);
int mtk_cpupm_syssram_write(int ofs, unsigned int val);
unsigned int mtk_cpupm_syssram_read(int ofs);
void mtk_cpupm_block(void);
void mtk_cpupm_allow(void);
int mtk_cpupm_get_idle_state_count(int cpu);
#endif /* __MTK_CPUPM_DBG_H__ */
@@ -0,0 +1,252 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
/* This file is generated by GenLP_setting.pl v1.5.7 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
const unsigned int AP_DCM_Golden_Setting_tcl_gs_dpidle_data[] = {
/* Address Mask Golden Setting Value */
0x0C530230, 0x00000001, 0x00000000,/* CPU_STALL_DCM_CTRL */
0x0C530A30, 0x00000001, 0x00000000,/* CPU_STALL_DCM_CTRL */
0x0C531230, 0x00000001, 0x00000000,/* CPU_STALL_DCM_CTRL */
0x0C531A30, 0x00000001, 0x00000000,/* CPU_STALL_DCM_CTRL */
0x0C532230, 0x00000001, 0x00000000,/* CPU_STALL_DCM_CTRL */
0x0C532A30, 0x00000001, 0x00000000,/* CPU_STALL_DCM_CTRL */
0x0C533230, 0x00000003, 0x00000000,/* STALL_DCM_CONF */
0x0C533A30, 0x00000003, 0x00000000,/* STALL_DCM_CONF */
0x0C53A2E0, 0x80000000, 0x80000000,/* BUS_PLLDIV_CFG */
0x0C53A440, 0x0000FFFF, 0x0000FFFF,/* MCSIC_DCM0 */
0x0C53A500, 0x00020000, 0x00020000,/* MP_ADB_DCM_CFG0 */
0x0C53A510, 0x00278000, 0x00278000,/* MP_ADB_DCM_CFG4 */
0x0C53A518, 0x0000003A, 0x0000003A,/* MP_MISC_DCM_CFG0 */
0x0C53A5C0, 0x00078100, 0x00078100,/* MCUSYS_DCM_CFG0 */
0x0C53A900, 0x0000000F, 0x0000000F,/* EMI_WFIFO */
0x0C53C880, 0x0001000F, 0x0001000F,/* MP0_DCM_CFG0 */
0x0C53C89C, 0x00000011, 0x00000011,/* MP0_DCM_CFG7 */
0x0D0A007C, 0x00000002, 0x00000000,/* dbg_mode */
0x10001070, 0xC0D07FFB, 0xC0D00603,/* INFRA_BUS_DCM_CTRL */
0x10001074, 0xA03FFFFB, 0xA03F83E3,/* PERI_BUS_DCM_CTRL */
0x100010A0, 0x0000000F, 0x00000000,/* P2P_RX_CLK_ON */
0x100010A4, 0x00000100, 0x00000100,/* MODULE_SW_CG_2_SET */
0x100010A8, 0x00000100, 0x00000000,/* MODULE_SW_CG_2_CLR */
0x10001A30, 0x0007F000, 0x00030000,/* INFRA_AXIMEM_IDLE_BIT_EN_0 */
0x10002028, 0x0000007E, 0x0000007E,/* INFRA_EMI_DCM_CFG0 */
0x1000202C, 0xFFFFFFFF, 0x40388000,/* INFRA_EMI_DCM_CFG1 */
0x10002034, 0xFFFFFFFF, 0x000000FF,/* INFRA_EMI_DCM_CFG3 */
0x10002038, 0xFFFFFFFF, 0x00000007,/* TOP_CK_ANCHOR_CFG */
0x1000A000, 0x80000000, 0x80000000,/* SEJ_CON */
0x1001A208, 0x0000FFFF, 0x0000FFFF,/* DXCC_NEW_HWDCM_CFG */
0x10022034, 0x10000003, 0x10000003,/* VDNR_DCM_TOP_INFRA_PAR_BUS_u_INFRA_PAR_BUS_CTRL_0 */
0x10022038, 0x040003E0, 0x04000000,/* VDNR_DCM_TOP_INFRA_PAR_BUS_u_INFRA_PAR_BUS_CTRL_1 */
0x1002203C, 0x000003E0, 0x00000000,/* VDNR_DCM_TOP_INFRA_PAR_BUS_u_INFRA_PAR_BUS_CTRL_2 */
0x10022058, 0x00400000, 0x00400000,/* VDNR_DCM_TOP_INFRA_PAR_BUS_u_INFRA_PAR_BUS_CTRL_9 */
0x1002205C, 0x00001000, 0x00001000,/* VDNR_DCM_TOP_INFRA_PAR_BUS_u_INFRA_PAR_BUS_CTRL_10 */
0x102280F0, 0x00000007, 0x00000007,/* GCE_CTL_INT0 */
0x10309300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1030A300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1030B300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1030C300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x10728F88, 0x00000007, 0x00000007,/* I3C0_CHN_HW_CG_EN */
0x10729F88, 0x00000007, 0x00000007,/* I2C1_CHN_HW_CG_EN */
0x10943008, 0x40000FFF, 0x40000FFF,/* SSPM_DCM_CTRL */
0x11200700, 0x000F0000, 0x00000000,/* RESREG */
0x112300B4, 0xFFA00000, 0x00000000,/* PATCH_BIT1 */
0x11C10480, 0x00000007, 0x00000007,/* DCM_ON */
0x11FA0014, 0x08000000, 0x00000000,/* MP_GLB_DIG_14 */
0x13FBF010, 0x0003FF7F, 0x0000C03F,/* MFG_DCM_CON_0 */
0x13FBF024, 0x00000001, 0x00000001,/* MFG_ASYNC_CON_1 */
0x13FBF0B0, 0x00000700, 0x00000000,/* MFG_GLOBAL_CON */
0x13FBF0B4, 0x00000010, 0x00000010,/* MFG_QCHANNEL_CON */
0x14000120, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS0 */
0x14000130, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS1 */
0x14000140, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS0 */
0x14000150, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS1 */
0x140001B0, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS2 */
0x140001C0, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS2 */
0x14002300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x14003014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x14004014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1400E1F0, 0xFFFFFFFF, 0x00000000,/* DISP_POSTMASK_FUNC_DCM0 */
0x1400E1F4, 0xFFFFFFFF, 0x00000000,/* DISP_POSTMASK_FUNC_DCM1 */
0x14016050, 0x007FFFFF, 0x00000000,/* MMU_DCM_DIS */
0x1401B300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1401C300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1401E300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1502E014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1502F300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1582E014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1602E014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1602F018, 0x00000001, 0x00000000,/* VDEC_DCM_CON */
0x1602F218, 0x00000001, 0x00000000,/* LAT_DCM_CON */
0x17010014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x17030300, 0x00000001, 0x00000000,/* JPGENC_DCM_CTRL */
0x1A001014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1A002014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1A00C300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1A00D300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1A00F014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1A010014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1B00E300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1B00F014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1B10F014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1F000150, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS0 */
0x1F000160, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS1 */
0x1F000170, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS2 */
0x1F000180, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS3 */
0x1F000190, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS4 */
0x1F0001A0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS0 */
0x1F0001B0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS1 */
0x1F0001C0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS2 */
0x1F0001D0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS3 */
0x1F0001E0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS4 */
0x1F002014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
};
const unsigned int *AP_DCM_Golden_Setting_tcl_gs_dpidle =
AP_DCM_Golden_Setting_tcl_gs_dpidle_data;
unsigned int AP_DCM_Golden_Setting_tcl_gs_dpidle_len = 282;
const unsigned int AP_DCM_Golden_Setting_tcl_gs_suspend_data[] = {
/* Address Mask Golden Setting Value */
0x0C530230, 0x00000001, 0x00000000,/* CPU_STALL_DCM_CTRL */
0x0C530A30, 0x00000001, 0x00000000,/* CPU_STALL_DCM_CTRL */
0x0C531230, 0x00000001, 0x00000000,/* CPU_STALL_DCM_CTRL */
0x0C531A30, 0x00000001, 0x00000000,/* CPU_STALL_DCM_CTRL */
0x0C532230, 0x00000001, 0x00000000,/* CPU_STALL_DCM_CTRL */
0x0C532A30, 0x00000001, 0x00000000,/* CPU_STALL_DCM_CTRL */
0x0C533230, 0x00000003, 0x00000000,/* STALL_DCM_CONF */
0x0C533A30, 0x00000003, 0x00000000,/* STALL_DCM_CONF */
0x0C53A2E0, 0x80000000, 0x80000000,/* BUS_PLLDIV_CFG */
0x0C53A440, 0x0000FFFF, 0x0000FFFF,/* MCSIC_DCM0 */
0x0C53A500, 0x00020000, 0x00020000,/* MP_ADB_DCM_CFG0 */
0x0C53A510, 0x00278000, 0x00278000,/* MP_ADB_DCM_CFG4 */
0x0C53A518, 0x0000003A, 0x0000003A,/* MP_MISC_DCM_CFG0 */
0x0C53A5C0, 0x00078100, 0x00078100,/* MCUSYS_DCM_CFG0 */
0x0C53A900, 0x0000000F, 0x0000000F,/* EMI_WFIFO */
0x0C53C880, 0x0001000F, 0x0001000F,/* MP0_DCM_CFG0 */
0x0C53C89C, 0x00000011, 0x00000011,/* MP0_DCM_CFG7 */
0x0D0A007C, 0x00000002, 0x00000000,/* dbg_mode */
0x10001070, 0xC0D07FFB, 0xC0D00603,/* INFRA_BUS_DCM_CTRL */
0x10001074, 0xA03FFFFB, 0xA03F83E3,/* PERI_BUS_DCM_CTRL */
0x100010A0, 0x0000000F, 0x00000000,/* P2P_RX_CLK_ON */
0x100010A4, 0x00000100, 0x00000100,/* MODULE_SW_CG_2_SET */
0x100010A8, 0x00000100, 0x00000000,/* MODULE_SW_CG_2_CLR */
0x10001A30, 0x0007F000, 0x00030000,/* INFRA_AXIMEM_IDLE_BIT_EN_0 */
0x10002028, 0x0000007E, 0x0000007E,/* INFRA_EMI_DCM_CFG0 */
0x1000202C, 0xFFFFFFFF, 0x40388000,/* INFRA_EMI_DCM_CFG1 */
0x10002034, 0xFFFFFFFF, 0x000000FF,/* INFRA_EMI_DCM_CFG3 */
0x10002038, 0xFFFFFFFF, 0x00000007,/* TOP_CK_ANCHOR_CFG */
0x1000A000, 0x80000000, 0x80000000,/* SEJ_CON */
0x1001A208, 0x0000FFFF, 0x0000FFFF,/* DXCC_NEW_HWDCM_CFG */
0x10022034, 0x10000003, 0x10000003,/* VDNR_DCM_TOP_INFRA_PAR_BUS_u_INFRA_PAR_BUS_CTRL_0 */
0x10022038, 0x040003E0, 0x04000000,/* VDNR_DCM_TOP_INFRA_PAR_BUS_u_INFRA_PAR_BUS_CTRL_1 */
0x1002203C, 0x000003E0, 0x00000000,/* VDNR_DCM_TOP_INFRA_PAR_BUS_u_INFRA_PAR_BUS_CTRL_2 */
0x10022058, 0x00400000, 0x00400000,/* VDNR_DCM_TOP_INFRA_PAR_BUS_u_INFRA_PAR_BUS_CTRL_9 */
0x1002205C, 0x00001000, 0x00001000,/* VDNR_DCM_TOP_INFRA_PAR_BUS_u_INFRA_PAR_BUS_CTRL_10 */
0x102280F0, 0x00000007, 0x00000007,/* GCE_CTL_INT0 */
0x10309300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1030A300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1030B300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1030C300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x10728F88, 0x00000007, 0x00000007,/* I3C0_CHN_HW_CG_EN */
0x10729F88, 0x00000007, 0x00000007,/* I2C1_CHN_HW_CG_EN */
0x10943008, 0x40000FFF, 0x40000FFF,/* SSPM_DCM_CTRL */
0x11200700, 0x000F0000, 0x00000000,/* RESREG */
0x112300B4, 0xFFA00000, 0x00000000,/* PATCH_BIT1 */
0x11C10480, 0x00000007, 0x00000007,/* DCM_ON */
0x11FA0014, 0x08000000, 0x00000000,/* MP_GLB_DIG_14 */
0x13FBF010, 0x0003FF7F, 0x0000C03F,/* MFG_DCM_CON_0 */
0x13FBF024, 0x00000001, 0x00000001,/* MFG_ASYNC_CON_1 */
0x13FBF0B0, 0x00000700, 0x00000000,/* MFG_GLOBAL_CON */
0x13FBF0B4, 0x00000010, 0x00000010,/* MFG_QCHANNEL_CON */
0x14000120, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS0 */
0x14000130, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS1 */
0x14000140, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS0 */
0x14000150, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS1 */
0x140001B0, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS2 */
0x140001C0, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS2 */
0x14002300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x14003014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x14004014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1400E1F0, 0xFFFFFFFF, 0x00000000,/* DISP_POSTMASK_FUNC_DCM0 */
0x1400E1F4, 0xFFFFFFFF, 0x00000000,/* DISP_POSTMASK_FUNC_DCM1 */
0x14016050, 0x007FFFFF, 0x00000000,/* MMU_DCM_DIS */
0x1401B300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1401C300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1401E300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1502E014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1502F300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1582E014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1602E014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1602F018, 0x00000001, 0x00000000,/* VDEC_DCM_CON */
0x1602F218, 0x00000001, 0x00000000,/* LAT_DCM_CON */
0x17010014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x17030300, 0x00000001, 0x00000000,/* JPGENC_DCM_CTRL */
0x1A001014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1A002014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1A00C300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1A00D300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1A00F014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1A010014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1B00E300, 0x000000FE, 0x00000000,/* SMI_DCM */
0x1B00F014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1B10F014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
0x1F000150, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS0 */
0x1F000160, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS1 */
0x1F000170, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS2 */
0x1F000180, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS3 */
0x1F000190, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS4 */
0x1F0001A0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS0 */
0x1F0001B0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS1 */
0x1F0001C0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS2 */
0x1F0001D0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS3 */
0x1F0001E0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS4 */
0x1F002014, 0x0000FFF0, 0x0000FFF0,/* SMI_LARB_CON_SET */
};
const unsigned int *AP_DCM_Golden_Setting_tcl_gs_suspend =
AP_DCM_Golden_Setting_tcl_gs_suspend_data;
unsigned int AP_DCM_Golden_Setting_tcl_gs_suspend_len = 282;
const unsigned int AP_DCM_Golden_Setting_tcl_gs_sodi_data[] = {
/* Address Mask Golden Setting Value */
0x0D0A007C, 0x00000002, 0x00000000,/* dbg_mode */
0x1001A208, 0x0000FFFF, 0x0000FFFF,/* DXCC_NEW_HWDCM_CFG */
0x10943008, 0x40000FFF, 0x40000FFF,/* SSPM_DCM_CTRL */
0x112300B4, 0xFFA00000, 0x00000000,/* PATCH_BIT1 */
0x11C10480, 0x00000007, 0x00000007,/* DCM_ON */
0x14000120, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS0 */
0x14000130, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS1 */
0x14000140, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS0 */
0x14000150, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS1 */
0x140001B0, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_1ST_DIS2 */
0x140001C0, 0xFFFFFFFF, 0x00000000,/* MMSYS_HW_DCM_2ND_DIS2 */
0x1400E1F0, 0xFFFFFFFF, 0x00000000,/* DISP_POSTMASK_FUNC_DCM0 */
0x1400E1F4, 0xFFFFFFFF, 0x00000000,/* DISP_POSTMASK_FUNC_DCM1 */
0x14016050, 0x007FFFFF, 0x00000000,/* MMU_DCM_DIS */
0x1602F018, 0x00000001, 0x00000000,/* VDEC_DCM_CON */
0x1602F218, 0x00000001, 0x00000000,/* LAT_DCM_CON */
0x17030300, 0x00000001, 0x00000000,/* JPGENC_DCM_CTRL */
0x1F000150, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS0 */
0x1F000160, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS1 */
0x1F000170, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS2 */
0x1F000180, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS3 */
0x1F000190, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_1ST_DIS4 */
0x1F0001A0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS0 */
0x1F0001B0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS1 */
0x1F0001C0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS2 */
0x1F0001D0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS3 */
0x1F0001E0, 0xFFFFFFFF, 0x00000000,/* MDPSYS_HW_DCM_2ND_DIS4 */
};
const unsigned int *AP_DCM_Golden_Setting_tcl_gs_sodi =
AP_DCM_Golden_Setting_tcl_gs_sodi_data;
unsigned int AP_DCM_Golden_Setting_tcl_gs_sodi_len = 90;
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include <linux/cpuidle.h>
#include <mtk_lp_plat_reg.h>
#include <mtk_lpm.h>
#include <mt6833_spm_comm.h>
#include "mtk_cpupm_dbg.h"
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
#define MTK_IDLE_PROC_FS_NAME "cpuidle"
static int idle_proc_info_show(struct seq_file *m, void *v)
{
int cpu;
unsigned long long ts, rem;
ts = mtk_cpupm_syssram_read(SYSRAM_RECENT_CNT_TS_H);
ts = (ts << 32) | mtk_cpupm_syssram_read(SYSRAM_RECENT_CNT_TS_L);
ts = div64_u64_rem(ts, 1000 * 1000 * 1000, &rem);
seq_puts(m, "\n========= Power off count =========\n");
seq_puts(m, "\n-- Within 5 seconds before dumped --\n");
seq_printf(m, "\nLast dump timestamp = %llu.%09llu\n",
ts, rem);
seq_printf(m, "%8s", "cpu:");
for_each_possible_cpu(cpu)
seq_printf(m, " %d", mtk_cpupm_syssram_read(
SYSRAM_RECENT_CPU_CNT(cpu)));
seq_puts(m, "\n");
seq_printf(m, "%8s %d\n",
"cluster:",
mtk_cpupm_syssram_read(SYSRAM_RECENT_CPUSYS_CNT));
seq_printf(m, "%8s %d\n",
"mcusys:",
mtk_cpupm_syssram_read(SYSRAM_RECENT_MCUSYS_CNT));
seq_printf(m, "%8s 0x%x\n",
"online:",
mtk_cpupm_syssram_read(SYSRAM_CPU_ONLINE));
seq_puts(m, "\n---- Total ----\n");
seq_printf(m, "%8s %d\n",
"cluster:",
mtk_cpupm_syssram_read(SYSRAM_CPUSYS_CNT)
+ mtk_cpupm_syssram_read(SYSRAM_RECENT_CPUSYS_CNT));
seq_printf(m, "%8s %d\n",
"mcusys:",
mtk_cpupm_syssram_read(SYSRAM_MCUSYS_CNT)
+ mtk_cpupm_syssram_read(SYSRAM_RECENT_MCUSYS_CNT));
return 0;
}
static ssize_t idle_proc_info_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
return count;
}
static int idle_proc_enable_show(struct seq_file *m, void *v)
{
struct cpuidle_driver *drv;
int i, cpu, en = 0;
int suspend_type = mtk_lpm_suspend_type_get();
for_each_possible_cpu(cpu) {
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
for (i = 1; i < drv->state_count; i++) {
if ((suspend_type == MTK_LPM_SUSPEND_S2IDLE) &&
!strcmp(drv->states[i].name, S2IDLE_STATE_NAME))
continue;
en += mtk_cpuidle_get_param(drv, i, IDLE_PARAM_EN);
}
}
if (en == 0) {
seq_printf(m, "MCDI: Disable, %llu ms\n",
mtk_cpuidle_state_last_dis_ms());
} else {
seq_puts(m, "MCDI: Enable\n");
seq_puts(m,
"(cat /proc/cpuidle/state/enabled for more detail)\n");
}
return 0;
}
static ssize_t idle_proc_enable_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_state_enable(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
PROC_FOPS(info);
PROC_FOPS(enable);
int __init mtk_idle_procfs_init(void)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(info),
PROC_ENTRY(enable)
};
dir = proc_mkdir(MTK_IDLE_PROC_FS_NAME, NULL);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return -ENOMEM;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
mtk_idle_procfs_state_dir_init(dir);
mtk_idle_procfs_cpc_dir_init(dir);
mtk_idle_procfs_profile_dir_init(dir);
mtk_idle_procfs_control_dir_init(dir);
return 0;
}
void __exit mtk_idle_procfs_exit(void)
{
remove_proc_subtree(MTK_IDLE_PROC_FS_NAME, NULL);
}
@@ -0,0 +1,75 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_IDLE_PROCFS__
#define __MTK_IDLE_PROCFS__
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
struct idle_proc_entry {
const char *name;
const struct file_operations *fops;
};
#define PROC_FOPS(name) \
static int idle_proc_ ## name ## _open(struct inode *inode, struct file *file) \
{ \
return single_open(file, idle_proc_ ## name ## _show, PDE_DATA(inode));\
} \
static const struct file_operations idle_proc_ ## name ## _fops = { \
.owner = THIS_MODULE, \
.open = idle_proc_ ## name ## _open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
.write = idle_proc_ ## name ## _write, \
}
#define PROC_ENTRY(name) {__stringify(name), &idle_proc_ ## name ## _fops}
#define PROC_CREATE_NODE(dir, entry) \
do { \
if (!proc_create(entry.name, 0644, dir, entry.fops)) \
pr_notice("%s(), create procfs cpuidle node %s failed\n", \
__func__, entry.name); \
} while (0)
#define mtk_idle_procfs_alloc_from_user(buf, userbuf, count) \
do { \
buf = (char *)__get_free_page(GFP_USER); \
\
if (!buf) \
break; \
\
if (count >= PAGE_SIZE \
|| copy_from_user(buf, userbuf, count)) { \
free_page((unsigned long)buf); \
buf = NULL; \
break; \
} \
\
buf[count] = '\0'; \
\
} while (0)
#define mtk_idle_procfs_free(buf) \
do { \
if (!buf) \
break; \
\
free_page((unsigned long)buf); \
} while (0)
void __init mtk_idle_procfs_cpc_dir_init(struct proc_dir_entry *parent);
void __init mtk_idle_procfs_state_dir_init(struct proc_dir_entry *parent);
void __init mtk_idle_procfs_profile_dir_init(struct proc_dir_entry *parent);
void __init mtk_idle_procfs_control_dir_init(struct proc_dir_entry *parent);
int __init mtk_idle_procfs_init(void);
void __exit mtk_idle_procfs_exit(void);
#endif /* __MTK_IDLE_PROCFS__ */
@@ -0,0 +1,337 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include <mtk_lp_plat_apmcu_mbox.h>
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
static int idle_proc_log_show(struct seq_file *m, void *v)
{
seq_printf(m, "CPU idle log : %s\n",
mtk_cpuidle_ctrl_log_sta_get() ?
"Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/control/log\n");
return 0;
}
static ssize_t idle_proc_log_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_ctrl_log_en(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_timer_show(struct seq_file *m, void *v)
{
seq_printf(m, "Limit sleep timer : %s\n",
mtk_cpuidle_ctrl_timer_sta_get() ?
"Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/control/timer\n");
return 0;
}
static ssize_t idle_proc_timer_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_ctrl_timer_en(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_stress_show(struct seq_file *m, void *v)
{
seq_printf(m, "CPU idle stress : %s\n",
mtk_cpuidle_get_stress_status() ?
"Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/control/stress\n");
return 0;
}
static ssize_t idle_proc_stress_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_set_stress_test(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_stress_time_show(struct seq_file *m, void *v)
{
seq_printf(m, "CPU idle stress interval time : %u\n",
mtk_cpuidle_get_stress_time());
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [us] > /proc/cpuidle/control/stress_time\n");
return 0;
}
static ssize_t idle_proc_stress_time_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int interval_us = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &interval_us) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_set_stress_time(interval_us);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_buck_mode_show(struct seq_file *m, void *v)
{
int mode;
struct mode_param {
int id;
char *str;
};
const struct mode_param table[] = {
{MCUPM_BUCK_NORMAL_MODE, "on"},
{MCUPM_BUCK_LP_MODE, "low power"},
{MCUPM_BUCK_OFF_MODE, "off"},
{NF_MCUPM_BUCK_MODE, "unknown"},
};
mode = mtk_get_mcupm_buck_mode();
if (mode < 0 || mode > NF_MCUPM_BUCK_MODE)
mode = NF_MCUPM_BUCK_MODE;
seq_printf(m, "Vproc/Vproc_sram buck mode : %s\n",
table[mode].str);
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [mode] > /proc/cpuidle/control/buck_mode\n");
seq_puts(m, "mode:\n");
for (mode = 0; mode < NF_MCUPM_BUCK_MODE; mode++)
seq_printf(m, "\t%d: %s\n",
table[mode].id,
table[mode].str);
return 0;
}
static ssize_t idle_proc_buck_mode_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int mode = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &mode) != 0) {
ret = -EINVAL;
goto free;
}
if (mode < NF_MCUPM_BUCK_MODE)
mtk_set_mcupm_buck_mode(mode);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_armpll_mode_show(struct seq_file *m, void *v)
{
int mode;
struct mode_param {
int id;
char *str;
};
const struct mode_param table[] = {
{MCUPM_ARMPLL_ON, "on"},
{MCUPM_ARMPLL_GATING, "gating"},
{MCUPM_ARMPLL_OFF, "off"},
{NF_MCUPM_ARMPLL_MODE, "unknown"},
};
mode = mtk_get_mcupm_pll_mode();
if (mode < 0 || mode > NF_MCUPM_ARMPLL_MODE)
mode = NF_MCUPM_ARMPLL_MODE;
seq_printf(m, "armpll mode : %s\n",
table[mode].str);
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [mode] > /proc/cpuidle/control/armpll_mode\n");
seq_puts(m, "mode:\n");
for (mode = 0; mode < NF_MCUPM_ARMPLL_MODE; mode++)
seq_printf(m, "\t%d: %s\n",
table[mode].id,
table[mode].str);
return 0;
}
static ssize_t idle_proc_armpll_mode_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int mode = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &mode) != 0) {
ret = -EINVAL;
goto free;
}
if (mode < NF_MCUPM_ARMPLL_MODE)
mtk_set_mcupm_pll_mode(mode);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_notify_cm_show(struct seq_file *m, void *v)
{
bool notify;
notify = mtk_mcupm_cm_is_notified();
seq_printf(m, "mcupm cm mgr : %s\n",
notify ? "Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "Read Only : Not support dynamic control\n");
return 0;
}
static ssize_t idle_proc_notify_cm_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
return count;
}
PROC_FOPS(log);
PROC_FOPS(timer);
PROC_FOPS(stress);
PROC_FOPS(stress_time);
PROC_FOPS(buck_mode);
PROC_FOPS(armpll_mode);
PROC_FOPS(notify_cm);
void __init mtk_idle_procfs_control_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(log),
PROC_ENTRY(timer),
PROC_ENTRY(stress),
PROC_ENTRY(stress_time),
PROC_ENTRY(buck_mode),
PROC_ENTRY(armpll_mode),
PROC_ENTRY(notify_cm)
};
dir = proc_mkdir("control", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include "mtk_cpuidle_cpc.h"
#include "mtk_idle_procfs.h"
static int idle_proc_auto_off_show(struct seq_file *m, void *v)
{
seq_printf(m, "auto_off mode : %s\n",
cpc_get_auto_off_sta() ? "Enabled" : "Disabled");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/cpc/auto_off\n");
return 0;
}
static ssize_t idle_proc_auto_off_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
if (enabled)
cpc_auto_off_en();
else
cpc_auto_off_dis();
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_auto_off_thres_show(struct seq_file *m, void *v)
{
seq_printf(m, "auto_off threshold = %u us\n",
(unsigned int)cpc_get_auto_off_thres());
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [us] > /proc/cpuidle/cpc/auto_off_thres\n");
return 0;
}
static ssize_t idle_proc_auto_off_thres_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int threshold_us = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &threshold_us) != 0) {
ret = -EINVAL;
goto free;
}
cpc_set_auto_off_thres(threshold_us);
free:
mtk_idle_procfs_free(buf);
return ret;
}
PROC_FOPS(auto_off);
PROC_FOPS(auto_off_thres);
void __init mtk_idle_procfs_cpc_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(auto_off),
PROC_ENTRY(auto_off_thres)
};
dir = proc_mkdir("cpc", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include "mtk_cpuidle_cpc.h"
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
static int idle_proc_kernel_show(struct seq_file *m, void *v)
{
seq_printf(m, "%s\n", __func__);
return 0;
}
static ssize_t idle_proc_kernel_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
/* Start/Stop kernel profile */
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_target_state_show(struct seq_file *m, void *v)
{
seq_printf(m, "%s\n", __func__);
return 0;
}
static ssize_t idle_proc_target_state_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
int state = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtoint(buf, 10, &state) != 0) {
ret = -EINVAL;
goto free;
}
/* Set target state */
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_ratio_show(struct seq_file *m, void *v)
{
mtk_cpuidle_prof_ratio_dump(m);
return 0;
}
static ssize_t idle_proc_ratio_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
if (enabled)
mtk_cpuidle_prof_ratio_start();
else
mtk_cpuidle_prof_ratio_stop();
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_cpc_show(struct seq_file *m, void *v)
{
mtk_cpc_prof_lat_dump(m);
return 0;
}
static ssize_t idle_proc_cpc_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
if (enabled)
mtk_cpc_prof_start();
else
mtk_cpc_prof_stop();
free:
mtk_idle_procfs_free(buf);
return ret;
}
PROC_FOPS(kernel);
PROC_FOPS(target_state);
PROC_FOPS(ratio);
PROC_FOPS(cpc);
void __init mtk_idle_procfs_profile_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(kernel),
PROC_ENTRY(target_state),
PROC_ENTRY(ratio),
PROC_ENTRY(cpc)
};
dir = proc_mkdir("profile", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,218 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/cpuidle.h>
#include <linux/seq_file.h>
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
#define ALL_LITTLR_CPU_ID (10)
#define ALL_BIG_CPU_ID (20)
#define ALL_CPU_ID (100)
#define CPU_L_MASK (0x0f)
#define CPU_B_MASK (0xf0)
#define ALL_CPU_MASK (0xff)
static void idle_proc_state_uasge_print(struct seq_file *m, char *node_name)
{
seq_puts(m, "\n======== Command Usage ========\n");
seq_printf(m, "%s > /proc/cpuidle/state/%s\n",
"echo [cpu_id] [state_index] [val(dec)]",
node_name);
seq_puts(m, "\t cpu_id: 0~7 -> cpu number\n");
seq_printf(m, "\t %3d -> all little CPU\n", ALL_LITTLR_CPU_ID);
seq_printf(m, "\t %3d -> all big CPU\n", ALL_BIG_CPU_ID);
seq_printf(m, "\t %3d -> all CPU\n", ALL_CPU_ID);
seq_puts(m, "\t state_index: >0 (index 0 can't be modified)\n");
seq_puts(m, "\n");
}
static void idle_proc_state_param_show(struct seq_file *m, int param)
{
struct cpuidle_driver *drv;
int i, cpu, desc = 0;
for_each_possible_cpu(cpu) {
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
if (desc == 0) {
seq_printf(m, "%-12s:", "state_index");
for (i = 0; i < drv->state_count; i++)
seq_printf(m, "%7d ", i);
seq_puts(m, "\n");
desc = 1;
}
seq_printf(m, "%11s%d:", "cpu", cpu);
for (i = 0; i < drv->state_count; i++)
seq_printf(m, "%7d ",
mtk_cpuidle_get_param(drv, i, param));
seq_puts(m, "\n");
}
}
static int idle_proc_state_param_setting(char *cmd, int param)
{
struct cpuidle_driver *drv;
char *args;
unsigned int cpu_mask, state_idx = 0, val = 0;
int cpu = 0, i;
struct cmd_param {
unsigned int id;
unsigned int mask;
};
const struct cmd_param table[] = {
{ALL_LITTLR_CPU_ID, CPU_L_MASK},
{ALL_BIG_CPU_ID, CPU_B_MASK},
{ALL_CPU_ID, ALL_CPU_MASK},
};
args = strsep(&cmd, " ");
if (!args || kstrtoint(args, 10, &cpu) != 0)
return -EINVAL;
args = strsep(&cmd, " ");
if (!args || kstrtouint(args, 10, &state_idx) != 0)
return -EINVAL;
args = strsep(&cmd, " ");
if (!args || kstrtouint(args, 10, &val) != 0)
return -EINVAL;
if (!state_idx)
return -EINVAL;
cpu_mask = 0;
if (cpu >= 0 && cpu < nr_cpu_ids) {
cpu_mask = (1 << cpu);
} else {
for (i = 0; i < ARRAY_SIZE(table); i++) {
if (cpu == table[i].id) {
cpu_mask = table[i].mask;
break;
}
}
}
for_each_possible_cpu(cpu) {
if ((cpu_mask & (1 << cpu)) == 0)
continue;
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
mtk_cpuidle_set_param(drv, state_idx, param, val);
}
return 0;
}
static int idle_proc_latency_show(struct seq_file *m, void *v)
{
seq_puts(m, "==== CPU idle state: Exit latency ====\n");
idle_proc_state_param_show(m, IDLE_PARAM_LAT);
idle_proc_state_uasge_print(m, "latency");
return 0;
}
static ssize_t idle_proc_latency_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
idle_proc_state_param_setting(buf, IDLE_PARAM_LAT);
mtk_idle_procfs_free(buf);
return count;
}
static int idle_proc_residency_show(struct seq_file *m, void *v)
{
seq_puts(m, "==== CPU idle state: Target residency ====\n");
idle_proc_state_param_show(m, IDLE_PARAM_RES);
idle_proc_state_uasge_print(m, "residency");
return 0;
}
static ssize_t idle_proc_residency_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
idle_proc_state_param_setting(buf, IDLE_PARAM_RES);
mtk_idle_procfs_free(buf);
return count;
}
static int idle_proc_enabled_show(struct seq_file *m, void *v)
{
seq_puts(m, "==== CPU idle state: Enabled ====\n");
idle_proc_state_param_show(m, IDLE_PARAM_EN);
idle_proc_state_uasge_print(m, "enabled");
return 0;
}
static ssize_t idle_proc_enabled_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
idle_proc_state_param_setting(buf, IDLE_PARAM_EN);
mtk_idle_procfs_free(buf);
return count;
}
PROC_FOPS(latency);
PROC_FOPS(residency);
PROC_FOPS(enabled);
void __init mtk_idle_procfs_state_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(latency),
PROC_ENTRY(residency),
PROC_ENTRY(enabled)
};
dir = proc_mkdir("state", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,103 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
/* This file is generated by GenLP_setting.pl v1.5.7 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
const unsigned int AP_PMIC_REG_MT6315_gs_suspend_32kless_data[] = {
/* Address Mask Golden Setting Value */
0x13, 0x2, 0x2,/* TOP_CON */
0x15, 0x1F, 0x1F,/* TEST_CON0 */
0x22, 0x10, 0x10,/* SMT_CON0 */
0x10B, 0x3, 0x3,/* TOP_CKPDN_CON0 */
0x196, 0x4, 0x4,/* VDIG18_CON0 */
0x38B, 0x4, 0x0,/* PLT_CON0 */
0x140B, 0x7, 0x0,/* BUCK_TOP_CLK_CON0 */
0x140E, 0xF, 0xF,/* BUCK_TOP_CLK_HWEN_CON0 */
0x1440, 0xF, 0xD,/* BUCK_TOP_CON0 */
0x1443, 0xF, 0x0,/* BUCK_TOP_CON1 */
0x148D, 0x1, 0x1,/* BUCK_VBUCK1_OP_EN_0 */
0x1493, 0x1, 0x1,/* BUCK_VBUCK1_OP_CFG_0 */
0x1496, 0x1, 0x1,/* BUCK_VBUCK1_OP_MODE_0 */
0x150D, 0x1, 0x0,/* BUCK_VBUCK2_OP_EN_0 */
0x1513, 0x1, 0x1,/* BUCK_VBUCK2_OP_CFG_0 */
0x1516, 0x1, 0x1,/* BUCK_VBUCK2_OP_MODE_0 */
0x158D, 0x1, 0x1,/* BUCK_VBUCK3_OP_EN_0 */
0x1593, 0x1, 0x1,/* BUCK_VBUCK3_OP_CFG_0 */
0x1596, 0x1, 0x1,/* BUCK_VBUCK3_OP_MODE_0 */
0x160D, 0x1, 0x1,/* BUCK_VBUCK4_OP_EN_0 */
0x1613, 0x1, 0x1,/* BUCK_VBUCK4_OP_CFG_0 */
0x1616, 0x1, 0x1 /* BUCK_VBUCK4_OP_MODE_0 */
};
const unsigned int *AP_PMIC_REG_MT6315_gs_suspend_32kless =
AP_PMIC_REG_MT6315_gs_suspend_32kless_data;
unsigned int AP_PMIC_REG_MT6315_gs_suspend_32kless_len = 66;
const unsigned int AP_PMIC_REG_MT6315_gs_deepidle___lp_mp3_32kless_data[] = {
/* Address Mask Golden Setting Value */
0x13, 0x2, 0x0,/* TOP_CON */
0x15, 0x1F, 0x1F,/* TEST_CON0 */
0x22, 0x10, 0x10,/* SMT_CON0 */
0x10B, 0x3, 0x3,/* TOP_CKPDN_CON0 */
0x196, 0x4, 0x4,/* VDIG18_CON0 */
0x38B, 0x4, 0x4,/* PLT_CON0 */
0x140B, 0x7, 0x0,/* BUCK_TOP_CLK_CON0 */
0x140E, 0xF, 0xF,/* BUCK_TOP_CLK_HWEN_CON0 */
0x1440, 0xF, 0xD,/* BUCK_TOP_CON0 */
0x1443, 0xF, 0x0,/* BUCK_TOP_CON1 */
0x148D, 0x1, 0x1,/* BUCK_VBUCK1_OP_EN_0 */
0x1493, 0x1, 0x1,/* BUCK_VBUCK1_OP_CFG_0 */
0x1496, 0x1, 0x1,/* BUCK_VBUCK1_OP_MODE_0 */
0x150D, 0x1, 0x0,/* BUCK_VBUCK2_OP_EN_0 */
0x1513, 0x1, 0x1,/* BUCK_VBUCK2_OP_CFG_0 */
0x1516, 0x1, 0x1,/* BUCK_VBUCK2_OP_MODE_0 */
0x158D, 0x1, 0x1,/* BUCK_VBUCK3_OP_EN_0 */
0x1593, 0x1, 0x1,/* BUCK_VBUCK3_OP_CFG_0 */
0x1596, 0x1, 0x1,/* BUCK_VBUCK3_OP_MODE_0 */
0x160D, 0x1, 0x1,/* BUCK_VBUCK4_OP_EN_0 */
0x1613, 0x1, 0x1,/* BUCK_VBUCK4_OP_CFG_0 */
0x1616, 0x1, 0x1 /* BUCK_VBUCK4_OP_MODE_0 */
};
const unsigned int *AP_PMIC_REG_MT6315_gs_deepidle___lp_mp3_32kless =
AP_PMIC_REG_MT6315_gs_deepidle___lp_mp3_32kless_data;
unsigned int AP_PMIC_REG_MT6315_gs_deepidle___lp_mp3_32kless_len = 66;
const unsigned int AP_PMIC_REG_MT6315_gs_sodi3p0_32kless_data[] = {
/* Address Mask Golden Setting Value */
0x13, 0x2, 0x2,/* TOP_CON */
0x15, 0x1F, 0x1F,/* TEST_CON0 */
0x22, 0x10, 0x10,/* SMT_CON0 */
0x10B, 0x3, 0x3,/* TOP_CKPDN_CON0 */
0x196, 0x4, 0x4,/* VDIG18_CON0 */
0x38B, 0x4, 0x0,/* PLT_CON0 */
0x140B, 0x7, 0x0,/* BUCK_TOP_CLK_CON0 */
0x140E, 0xF, 0xF,/* BUCK_TOP_CLK_HWEN_CON0 */
0x1440, 0xF, 0xD,/* BUCK_TOP_CON0 */
0x1443, 0xF, 0x0,/* BUCK_TOP_CON1 */
0x148D, 0x1, 0x1,/* BUCK_VBUCK1_OP_EN_0 */
0x1493, 0x1, 0x1,/* BUCK_VBUCK1_OP_CFG_0 */
0x1496, 0x1, 0x1,/* BUCK_VBUCK1_OP_MODE_0 */
0x150D, 0x1, 0x0,/* BUCK_VBUCK2_OP_EN_0 */
0x1513, 0x1, 0x1,/* BUCK_VBUCK2_OP_CFG_0 */
0x1516, 0x1, 0x1,/* BUCK_VBUCK2_OP_MODE_0 */
0x158D, 0x1, 0x1,/* BUCK_VBUCK3_OP_EN_0 */
0x1593, 0x1, 0x1,/* BUCK_VBUCK3_OP_CFG_0 */
0x1596, 0x1, 0x1,/* BUCK_VBUCK3_OP_MODE_0 */
0x160D, 0x1, 0x1,/* BUCK_VBUCK4_OP_EN_0 */
0x1613, 0x1, 0x1,/* BUCK_VBUCK4_OP_CFG_0 */
0x1616, 0x1, 0x1 /* BUCK_VBUCK4_OP_MODE_0 */
};
const unsigned int *AP_PMIC_REG_MT6315_gs_sodi3p0_32kless =
AP_PMIC_REG_MT6315_gs_sodi3p0_32kless_data;
unsigned int AP_PMIC_REG_MT6315_gs_sodi3p0_32kless_len = 66;
@@ -0,0 +1,482 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
const unsigned int AP_PMIC_REG_6365_gs_suspend_32kless_data[] = {
/* Address Mask Golden Setting Value */
0x20, 0xA, 0xA,/* TOP_CON */
0x24, 0x1F00, 0x1F00,/* TEST_CON0 */
0x30, 0x1, 0x1,/* SMT_CON0 */
0x10C, 0x10, 0x10,/* TOP_CKPDN_CON0 */
0x112, 0x4, 0x4,/* TOP_CKPDN_CON1 */
0x118, 0x8, 0x8,/* TOP_CKSEL_CON0 */
0x11E, 0x300, 0x0,/* TOP_CKSEL_CON1 */
0x408, 0x3, 0x1,/* RG_SPI_CON0 */
0x514, 0xFF, 0x7F,/* SCK_TOP_CKPDN_CON0 */
0x788, 0x3FF8, 0x6D8,/* DCXO_CW00 */
0x790, 0x1, 0x0,/* DCXO_CW02 */
0x7A8, 0x1000, 0x1000,/* DCXO_CW12 */
0xC0C, 0x5FD, 0x1E4,/* BM_TOP_CKPDN_CON0 */
0xC1E, 0x3, 0x3,/* BM_TOP_CKHWEN_CON0 */
0xC2C, 0x1, 0x1,/* BM_TOP_RST_CON1 */
0xD08, 0x301, 0x301,/* FGADC_CON0 */
0xD0E, 0x70F, 0x708,/* FGADC_CON3 */
0xD3E, 0xFFFF, 0x1F,/* FGADC_SON_CON0 */
0xD40, 0x3FF, 0x14,/* FGADC_SON_CON1 */
0xD42, 0x3FF, 0xFF,/* FGADC_SON_CON2 */
0xD44, 0xFFFF, 0x1F,/* FGADC_SOFF_CON0 */
0xD46, 0x3FF, 0x14,/* FGADC_SOFF_CON1 */
0xD48, 0x3FF, 0xFF,/* FGADC_SOFF_CON2 */
0xF2C, 0x1, 0x0,/* BIF_CON18 */
0xF8C, 0x3FF, 0x115,/* HK_TOP_CLK_CON0 */
0x1188, 0xE03F, 0x14,/* AUXADC_CON0 */
0x1198, 0x3FF, 0x13,/* AUXADC_CON6 */
0x119E, 0x7000, 0x6000,/* AUXADC_CON9 */
0x11BE, 0x19, 0x11,/* AUXADC_NAG_0 */
0x11D4, 0x2, 0x0,/* AUXADC_NAG_11 */
0x1212, 0x2, 0x0,/* AUXADC_IMP5 */
0x1224, 0x2, 0x0,/* AUXADC_LBAT8 */
0x1226, 0x1, 0x1,/* AUXADC_BAT_TEMP_0 */
0x122A, 0x3, 0x2,/* AUXADC_BAT_TEMP_2 */
0x122E, 0x2000, 0x2000,/* AUXADC_BAT_TEMP_4 */
0x1238, 0x2, 0x0,/* AUXADC_BAT_TEMP_9 */
0x123A, 0x1, 0x1,/* AUXADC_LBAT2_0 */
0x123C, 0x3, 0x2,/* AUXADC_LBAT2_1 */
0x1240, 0x2000, 0x2000,/* AUXADC_LBAT2_3 */
0x124A, 0x2, 0x0,/* AUXADC_LBAT2_8 */
0x125C, 0x2, 0x0,/* AUXADC_THR8 */
0x125E, 0x8003, 0x8002,/* AUXADC_MDRT_0 */
0x1262, 0x4, 0x4,/* AUXADC_MDRT_2 */
0x140C, 0x7, 0x0,/* BUCK_TOP_CLK_CON0 */
0x1412, 0x8, 0x8,/* BUCK_TOP_CLK_HWEN_CON0 */
0x1494, 0x7, 0x2,/* BUCK_VPU_OP_EN */
0x149A, 0x7, 0x7,/* BUCK_VPU_OP_CFG */
0x14A0, 0x7, 0x7,/* BUCK_VPU_OP_MODE */
0x1514, 0x7, 0x1,/* BUCK_VCORE_OP_EN */
0x151A, 0x7, 0x1,/* BUCK_VCORE_OP_CFG */
0x1520, 0x7, 0x7,/* BUCK_VCORE_OP_MODE */
0x1588, 0x1, 0x0,/* BUCK_VGPU11_CON0 */
0x1594, 0x7, 0x0,/* BUCK_VGPU11_OP_EN */
0x159A, 0x7, 0x0,/* BUCK_VGPU11_OP_CFG */
0x1608, 0x1, 0x0,/* BUCK_VGPU12_CON0 */
0x1614, 0x5, 0x0,/* BUCK_VGPU12_OP_EN */
0x1688, 0x1, 0x1,/* BUCK_VMODEM_CON0 */
0x1694, 0x7, 0x2,/* BUCK_VMODEM_OP_EN */
0x169A, 0x2, 0x0,/* BUCK_VMODEM_OP_CFG */
0x16A0, 0x2, 0x2,/* BUCK_VMODEM_OP_MODE */
0x1708, 0x1, 0x0,/* BUCK_VPROC1_CON0 */
0x1714, 0x5, 0x0,/* BUCK_VPROC1_OP_EN */
0x1788, 0x3, 0x0,/* BUCK_VPROC2_CON0 */
0x1794, 0x7, 0x0,/* BUCK_VPROC2_OP_EN */
0x179A, 0x2, 0x0,/* BUCK_VPROC2_OP_CFG */
0x17A0, 0x2, 0x2,/* BUCK_VPROC2_OP_MODE */
0x1808, 0x1, 0x1,/* BUCK_VS1_CON0 */
0x1814, 0x5, 0x5,/* BUCK_VS1_OP_EN */
0x181A, 0x5, 0x5,/* BUCK_VS1_OP_CFG */
0x1888, 0x1, 0x1,/* BUCK_VS2_CON0 */
0x1894, 0x5, 0x5,/* BUCK_VS2_OP_EN */
0x189A, 0x5, 0x5,/* BUCK_VS2_OP_CFG */
0x1908, 0x1, 0x0,/* BUCK_VPA_CON0 */
0x1B0E, 0x3, 0x3,/* TOP_TOP_CKHWEN_CON0 */
0x1B10, 0x1, 0x1,/* LDO_TOP_CLK_DCM_CON0 */
0x1B34, 0x2, 0x2,/* VRTC28_CON */
0x1B8A, 0x8000, 0x0,/* LDO_VFE28_CON1 */
0x1B8E, 0x2, 0x2,/* LDO_VFE28_OP_EN */
0x1B9C, 0x8000, 0x0,/* LDO_VXO22_CON1 */
0x1BA0, 0x1, 0x1,/* LDO_VXO22_OP_EN */
0x1BA6, 0x1, 0x1,/* LDO_VXO22_OP_CFG */
0x1BAE, 0x8000, 0x0,/* LDO_VRF18_CON1 */
0x1BB2, 0x2, 0x2,/* LDO_VRF18_OP_EN */
0x1BC0, 0x8000, 0x0,/* LDO_VRF12_CON1 */
0x1BC4, 0x1, 0x1,/* LDO_VRF12_OP_EN */
0x1BCA, 0x1, 0x1,/* LDO_VRF12_OP_CFG */
0x1BD0, 0x1, 0x0,/* LDO_VEFUSE_CON0 */
0x1BD2, 0x8000, 0x0,/* LDO_VEFUSE_CON1 */
0x1BE2, 0x1, 0x0,/* LDO_VCN33_1_CON0 */
0x1BE4, 0x8000, 0x0,/* LDO_VCN33_1_CON1 */
0x1BF4, 0x8000, 0x0,/* LDO_VCN33_1_MULTI_SW */
0x1C08, 0x1, 0x0,/* LDO_VCN33_2_CON0 */
0x1C0A, 0x8000, 0x0,/* LDO_VCN33_2_CON1 */
0x1C1A, 0x8000, 0x0,/* LDO_VCN33_2_MULTI_SW */
0x1C1C, 0x1, 0x0,/* LDO_VCN13_CON0 */
0x1C1E, 0x8000, 0x0,/* LDO_VCN13_CON1 */
0x1C2E, 0x1, 0x0,/* LDO_VCN18_CON0 */
0x1C30, 0x8000, 0x0,/* LDO_VCN18_CON1 */
0x1C42, 0x8000, 0x0,/* LDO_VA09_CON1 */
0x1C46, 0x2, 0x2,/* LDO_VA09_OP_EN */
0x1C4C, 0x2, 0x2,/* LDO_VA09_OP_CFG */
0x1C52, 0x1, 0x0,/* LDO_VCAMIO_CON0 */
0x1C54, 0x8000, 0x0,/* LDO_VCAMIO_CON1 */
0x1C66, 0x8000, 0x0,/* LDO_VA12_CON1 */
0x1C6A, 0x1, 0x1,/* LDO_VA12_OP_EN */
0x1C70, 0x1, 0x1,/* LDO_VA12_OP_CFG */
0x1C8A, 0x8000, 0x0,/* LDO_VAUX18_CON1 */
0x1C8E, 0x1, 0x1,/* LDO_VAUX18_OP_EN */
0x1C94, 0x1, 0x1,/* LDO_VAUX18_OP_CFG */
0x1C9A, 0x1, 0x0,/* LDO_VAUD18_CON0 */
0x1C9C, 0x8000, 0x0,/* LDO_VAUD18_CON1 */
0x1CAE, 0x8004, 0x0,/* LDO_VIO18_CON1 */
0x1CB2, 0x1, 0x1,/* LDO_VIO18_OP_EN */
0x1CB8, 0x1, 0x1,/* LDO_VIO18_OP_CFG */
0x1CBE, 0x1, 0x0,/* LDO_VEMC_CON0 */
0x1CC0, 0x8000, 0x0,/* LDO_VEMC_CON1 */
0x1CD0, 0x1, 0x0,/* LDO_VSIM1_CON0 */
0x1CD2, 0x8000, 0x0,/* LDO_VSIM1_CON1 */
0x1CE2, 0x1, 0x0,/* LDO_VSIM2_CON0 */
0x1CE4, 0x8000, 0x0,/* LDO_VSIM2_CON1 */
0x1D0A, 0x8000, 0x0,/* LDO_VUSB_CON1 */
0x1D0E, 0x1, 0x1,/* LDO_VUSB_OP_EN */
0x1D14, 0x1, 0x1,/* LDO_VUSB_OP_CFG */
0x1D1C, 0x1, 0x0,/* LDO_VRFCK_CON0 */
0x1D1E, 0x8000, 0x0,/* LDO_VRFCK_CON1 */
0x1D22, 0x4000, 0x4000,/* LDO_VRFCK_OP_EN */
0x1D2E, 0x1, 0x0,/* LDO_VBBCK_CON0 */
0x1D30, 0x8000, 0x0,/* LDO_VBBCK_CON1 */
0x1D34, 0x4000, 0x4000,/* LDO_VBBCK_OP_EN */
0x1D42, 0x8000, 0x0,/* LDO_VBIF28_CON1 */
0x1D46, 0x1, 0x1,/* LDO_VBIF28_OP_EN */
0x1D52, 0x1, 0x0,/* LDO_VIBR_CON0 */
0x1D54, 0x8000, 0x0,/* LDO_VIBR_CON1 */
0x1D64, 0x1, 0x1,/* LDO_VIO28_CON0 */
0x1D66, 0x8000, 0x0,/* LDO_VIO28_CON1 */
0x1D88, 0x1, 0x1,/* LDO_VM18_CON0 */
0x1D8A, 0x8000, 0x0,/* LDO_VM18_CON1 */
0x1D9C, 0x8000, 0x0,/* LDO_VUFS_CON1 */
0x1DA0, 0x1, 0x1,/* LDO_VUFS_OP_EN */
0x1DA6, 0x1, 0x1,/* LDO_VUFS_OP_CFG */
0x1E88, 0x1, 0x0,/* LDO_VSRAM_PROC1_CON0 */
0x1E8A, 0x8000, 0x0,/* LDO_VSRAM_PROC1_CON1 */
0x1EA8, 0x1, 0x0,/* LDO_VSRAM_PROC2_CON0 */
0x1EAA, 0x8000, 0x0,/* LDO_VSRAM_PROC2_CON1 */
0x1F08, 0x1, 0x1,/* LDO_VSRAM_OTHERS_CON0 */
0x1F0A, 0x8000, 0x0,/* LDO_VSRAM_OTHERS_CON1 */
0x1F16, 0x7, 0x1,/* LDO_VSRAM_OTHERS_OP_EN */
0x1F1C, 0x7, 0x1,/* LDO_VSRAM_OTHERS_OP_CFG */
0x1F2E, 0x1, 0x0,/* LDO_VSRAM_MD_CON0 */
0x1F30, 0x8000, 0x0,/* LDO_VSRAM_MD_CON1 */
};
const unsigned int *AP_PMIC_REG_6365_gs_suspend_32kless =
AP_PMIC_REG_6365_gs_suspend_32kless_data;
unsigned int AP_PMIC_REG_6365_gs_suspend_32kless_len = 450;
const unsigned int AP_PMIC_REG_6365_gs_sodi3p0_32kless_data[] = {
/* Address Mask Golden Setting Value */
0x20, 0xA, 0xA,/* TOP_CON */
0x24, 0x1F00, 0x1F00,/* TEST_CON0 */
0x30, 0x1, 0x1,/* SMT_CON0 */
0x10C, 0x10, 0x10,/* TOP_CKPDN_CON0 */
0x112, 0x4, 0x4,/* TOP_CKPDN_CON1 */
0x118, 0x8, 0x8,/* TOP_CKSEL_CON0 */
0x11E, 0x300, 0x0,/* TOP_CKSEL_CON1 */
0x408, 0x3, 0x1,/* RG_SPI_CON0 */
0x514, 0xFF, 0x7F,/* SCK_TOP_CKPDN_CON0 */
0x788, 0x3FF8, 0x6D8,/* DCXO_CW00 */
0x790, 0x1, 0x0,/* DCXO_CW02 */
0x7A8, 0x1000, 0x1000,/* DCXO_CW12 */
0xC0C, 0x5FD, 0x1E4,/* BM_TOP_CKPDN_CON0 */
0xC1E, 0x3, 0x3,/* BM_TOP_CKHWEN_CON0 */
0xC2C, 0x1, 0x1,/* BM_TOP_RST_CON1 */
0xD08, 0x301, 0x301,/* FGADC_CON0 */
0xD0E, 0x70F, 0x708,/* FGADC_CON3 */
0xD3E, 0xFFFF, 0x1F,/* FGADC_SON_CON0 */
0xD40, 0x3FF, 0x14,/* FGADC_SON_CON1 */
0xD42, 0x3FF, 0xFF,/* FGADC_SON_CON2 */
0xD44, 0xFFFF, 0x1F,/* FGADC_SOFF_CON0 */
0xD46, 0x3FF, 0x14,/* FGADC_SOFF_CON1 */
0xD48, 0x3FF, 0xFF,/* FGADC_SOFF_CON2 */
0xF2C, 0x1, 0x0,/* BIF_CON18 */
0xF8C, 0x3FF, 0x115,/* HK_TOP_CLK_CON0 */
0x1188, 0xE03F, 0x14,/* AUXADC_CON0 */
0x1198, 0x3FF, 0x13,/* AUXADC_CON6 */
0x119E, 0x7000, 0x6000,/* AUXADC_CON9 */
0x11BE, 0x19, 0x11,/* AUXADC_NAG_0 */
0x11D4, 0x2, 0x0,/* AUXADC_NAG_11 */
0x1212, 0x2, 0x0,/* AUXADC_IMP5 */
0x1224, 0x2, 0x0,/* AUXADC_LBAT8 */
0x1226, 0x1, 0x1,/* AUXADC_BAT_TEMP_0 */
0x122A, 0x3, 0x2,/* AUXADC_BAT_TEMP_2 */
0x122E, 0x2000, 0x2000,/* AUXADC_BAT_TEMP_4 */
0x1238, 0x2, 0x0,/* AUXADC_BAT_TEMP_9 */
0x123A, 0x1, 0x1,/* AUXADC_LBAT2_0 */
0x123C, 0x3, 0x2,/* AUXADC_LBAT2_1 */
0x1240, 0x2000, 0x2000,/* AUXADC_LBAT2_3 */
0x124A, 0x2, 0x0,/* AUXADC_LBAT2_8 */
0x125C, 0x2, 0x0,/* AUXADC_THR8 */
0x125E, 0x8003, 0x8002,/* AUXADC_MDRT_0 */
0x1262, 0x4, 0x4,/* AUXADC_MDRT_2 */
0x140C, 0x7, 0x0,/* BUCK_TOP_CLK_CON0 */
0x1412, 0x8, 0x8,/* BUCK_TOP_CLK_HWEN_CON0 */
0x1494, 0x7, 0x2,/* BUCK_VPU_OP_EN */
0x149A, 0x7, 0x7,/* BUCK_VPU_OP_CFG */
0x14A0, 0x7, 0x7,/* BUCK_VPU_OP_MODE */
0x1514, 0x7, 0x1,/* BUCK_VCORE_OP_EN */
0x151A, 0x7, 0x1,/* BUCK_VCORE_OP_CFG */
0x1520, 0x7, 0x7,/* BUCK_VCORE_OP_MODE */
0x1588, 0x1, 0x0,/* BUCK_VGPU11_CON0 */
0x1594, 0x7, 0x0,/* BUCK_VGPU11_OP_EN */
0x159A, 0x7, 0x0,/* BUCK_VGPU11_OP_CFG */
0x1608, 0x1, 0x0,/* BUCK_VGPU12_CON0 */
0x1614, 0x5, 0x0,/* BUCK_VGPU12_OP_EN */
0x1688, 0x1, 0x1,/* BUCK_VMODEM_CON0 */
0x1694, 0x7, 0x2,/* BUCK_VMODEM_OP_EN */
0x169A, 0x2, 0x0,/* BUCK_VMODEM_OP_CFG */
0x16A0, 0x2, 0x2,/* BUCK_VMODEM_OP_MODE */
0x1708, 0x1, 0x0,/* BUCK_VPROC1_CON0 */
0x1714, 0x5, 0x0,/* BUCK_VPROC1_OP_EN */
0x1788, 0x3, 0x0,/* BUCK_VPROC2_CON0 */
0x1794, 0x7, 0x0,/* BUCK_VPROC2_OP_EN */
0x179A, 0x2, 0x0,/* BUCK_VPROC2_OP_CFG */
0x17A0, 0x2, 0x2,/* BUCK_VPROC2_OP_MODE */
0x1808, 0x1, 0x1,/* BUCK_VS1_CON0 */
0x1814, 0x5, 0x5,/* BUCK_VS1_OP_EN */
0x181A, 0x5, 0x5,/* BUCK_VS1_OP_CFG */
0x1888, 0x1, 0x1,/* BUCK_VS2_CON0 */
0x1894, 0x5, 0x5,/* BUCK_VS2_OP_EN */
0x189A, 0x5, 0x5,/* BUCK_VS2_OP_CFG */
0x1908, 0x1, 0x0,/* BUCK_VPA_CON0 */
0x1B0E, 0x3, 0x3,/* TOP_TOP_CKHWEN_CON0 */
0x1B10, 0x1, 0x1,/* LDO_TOP_CLK_DCM_CON0 */
0x1B34, 0x2, 0x2,/* VRTC28_CON */
0x1B8A, 0x8000, 0x0,/* LDO_VFE28_CON1 */
0x1B8E, 0x2, 0x2,/* LDO_VFE28_OP_EN */
0x1B9C, 0x8000, 0x0,/* LDO_VXO22_CON1 */
0x1BA0, 0x1, 0x1,/* LDO_VXO22_OP_EN */
0x1BA6, 0x1, 0x1,/* LDO_VXO22_OP_CFG */
0x1BAE, 0x8000, 0x0,/* LDO_VRF18_CON1 */
0x1BB2, 0x2, 0x2,/* LDO_VRF18_OP_EN */
0x1BC0, 0x8000, 0x0,/* LDO_VRF12_CON1 */
0x1BC4, 0x1, 0x1,/* LDO_VRF12_OP_EN */
0x1BCA, 0x1, 0x1,/* LDO_VRF12_OP_CFG */
0x1BD0, 0x1, 0x0,/* LDO_VEFUSE_CON0 */
0x1BD2, 0x8000, 0x0,/* LDO_VEFUSE_CON1 */
0x1BE2, 0x1, 0x1,/* LDO_VCN33_1_CON0 */
0x1BE4, 0x8000, 0x0,/* LDO_VCN33_1_CON1 */
0x1BF4, 0x8000, 0x8000,/* LDO_VCN33_1_MULTI_SW */
0x1C08, 0x1, 0x0,/* LDO_VCN33_2_CON0 */
0x1C0A, 0x8000, 0x0,/* LDO_VCN33_2_CON1 */
0x1C1A, 0x8000, 0x0,/* LDO_VCN33_2_MULTI_SW */
0x1C1C, 0x1, 0x0,/* LDO_VCN13_CON0 */
0x1C1E, 0x8000, 0x0,/* LDO_VCN13_CON1 */
0x1C2E, 0x1, 0x0,/* LDO_VCN18_CON0 */
0x1C30, 0x8000, 0x0,/* LDO_VCN18_CON1 */
0x1C42, 0x8000, 0x0,/* LDO_VA09_CON1 */
0x1C46, 0x2, 0x2,/* LDO_VA09_OP_EN */
0x1C4C, 0x2, 0x2,/* LDO_VA09_OP_CFG */
0x1C52, 0x1, 0x0,/* LDO_VCAMIO_CON0 */
0x1C54, 0x8000, 0x0,/* LDO_VCAMIO_CON1 */
0x1C66, 0x8000, 0x0,/* LDO_VA12_CON1 */
0x1C6A, 0x1, 0x1,/* LDO_VA12_OP_EN */
0x1C70, 0x1, 0x1,/* LDO_VA12_OP_CFG */
0x1C8A, 0x8000, 0x0,/* LDO_VAUX18_CON1 */
0x1C8E, 0x1, 0x1,/* LDO_VAUX18_OP_EN */
0x1C94, 0x1, 0x1,/* LDO_VAUX18_OP_CFG */
0x1C9A, 0x1, 0x0,/* LDO_VAUD18_CON0 */
0x1C9C, 0x8000, 0x0,/* LDO_VAUD18_CON1 */
0x1CAC, 0x1, 0x1,/* LDO_VIO18_CON0 */
0x1CAE, 0x8004, 0x0,/* LDO_VIO18_CON1 */
0x1CBE, 0x1, 0x1,/* LDO_VEMC_CON0 */
0x1CC0, 0x8000, 0x0,/* LDO_VEMC_CON1 */
0x1CD0, 0x1, 0x0,/* LDO_VSIM1_CON0 */
0x1CD2, 0x8000, 0x0,/* LDO_VSIM1_CON1 */
0x1CE2, 0x1, 0x0,/* LDO_VSIM2_CON0 */
0x1CE4, 0x8000, 0x0,/* LDO_VSIM2_CON1 */
0x1D0A, 0x8000, 0x0,/* LDO_VUSB_CON1 */
0x1D0E, 0x1, 0x1,/* LDO_VUSB_OP_EN */
0x1D14, 0x1, 0x1,/* LDO_VUSB_OP_CFG */
0x1D1C, 0x1, 0x0,/* LDO_VRFCK_CON0 */
0x1D1E, 0x8000, 0x0,/* LDO_VRFCK_CON1 */
0x1D22, 0x4000, 0x4000,/* LDO_VRFCK_OP_EN */
0x1D2E, 0x1, 0x0,/* LDO_VBBCK_CON0 */
0x1D30, 0x8000, 0x0,/* LDO_VBBCK_CON1 */
0x1D34, 0x4000, 0x4000,/* LDO_VBBCK_OP_EN */
0x1D42, 0x8000, 0x0,/* LDO_VBIF28_CON1 */
0x1D46, 0x1, 0x1,/* LDO_VBIF28_OP_EN */
0x1D52, 0x1, 0x0,/* LDO_VIBR_CON0 */
0x1D54, 0x8000, 0x0,/* LDO_VIBR_CON1 */
0x1D64, 0x1, 0x1,/* LDO_VIO28_CON0 */
0x1D66, 0x8000, 0x0,/* LDO_VIO28_CON1 */
0x1D88, 0x1, 0x1,/* LDO_VM18_CON0 */
0x1D8A, 0x8000, 0x0,/* LDO_VM18_CON1 */
0x1D9A, 0x1, 0x1,/* LDO_VUFS_CON0 */
0x1D9C, 0x8000, 0x0,/* LDO_VUFS_CON1 */
0x1E88, 0x1, 0x0,/* LDO_VSRAM_PROC1_CON0 */
0x1E8A, 0x8000, 0x0,/* LDO_VSRAM_PROC1_CON1 */
0x1EA8, 0x1, 0x0,/* LDO_VSRAM_PROC2_CON0 */
0x1EAA, 0x8000, 0x0,/* LDO_VSRAM_PROC2_CON1 */
0x1F08, 0x1, 0x1,/* LDO_VSRAM_OTHERS_CON0 */
0x1F0A, 0x8000, 0x0,/* LDO_VSRAM_OTHERS_CON1 */
0x1F16, 0x7, 0x1,/* LDO_VSRAM_OTHERS_OP_EN */
0x1F1C, 0x7, 0x1,/* LDO_VSRAM_OTHERS_OP_CFG */
0x1F2E, 0x1, 0x0,/* LDO_VSRAM_MD_CON0 */
0x1F30, 0x8000, 0x0,/* LDO_VSRAM_MD_CON1 */
};
const unsigned int *AP_PMIC_REG_6365_gs_sodi3p0_32kless =
AP_PMIC_REG_6365_gs_sodi3p0_32kless_data;
unsigned int AP_PMIC_REG_6365_gs_sodi3p0_32kless_len = 444;
const unsigned int AP_PMIC_REG_6365_gs_deepidle___lp_mp3_32kless_data[] = {
/* Address Mask Golden Setting Value */
0x20, 0xA, 0xA,/* TOP_CON */
0x24, 0x1F00, 0x1F00,/* TEST_CON0 */
0x30, 0x1, 0x1,/* SMT_CON0 */
0x10C, 0x10, 0x10,/* TOP_CKPDN_CON0 */
0x112, 0x4, 0x4,/* TOP_CKPDN_CON1 */
0x118, 0x8, 0x8,/* TOP_CKSEL_CON0 */
0x11E, 0x300, 0x0,/* TOP_CKSEL_CON1 */
0x408, 0x3, 0x1,/* RG_SPI_CON0 */
0x514, 0xFF, 0x7F,/* SCK_TOP_CKPDN_CON0 */
0x788, 0x3FF8, 0x16D8,/* DCXO_CW00 */
0x790, 0x1, 0x0,/* DCXO_CW02 */
0x7A8, 0x1000, 0x1000,/* DCXO_CW12 */
0xC0C, 0x5FD, 0x1E4,/* BM_TOP_CKPDN_CON0 */
0xC1E, 0x3, 0x3,/* BM_TOP_CKHWEN_CON0 */
0xC2C, 0x1, 0x1,/* BM_TOP_RST_CON1 */
0xD08, 0x301, 0x301,/* FGADC_CON0 */
0xD0E, 0x70F, 0x708,/* FGADC_CON3 */
0xD3E, 0xFFFF, 0x1F,/* FGADC_SON_CON0 */
0xD40, 0x3FF, 0x14,/* FGADC_SON_CON1 */
0xD42, 0x3FF, 0xFF,/* FGADC_SON_CON2 */
0xD44, 0xFFFF, 0x1F,/* FGADC_SOFF_CON0 */
0xD46, 0x3FF, 0x14,/* FGADC_SOFF_CON1 */
0xD48, 0x3FF, 0xFF,/* FGADC_SOFF_CON2 */
0xF2C, 0x1, 0x0,/* BIF_CON18 */
0xF8C, 0x3FF, 0x115,/* HK_TOP_CLK_CON0 */
0x1188, 0xE03F, 0x14,/* AUXADC_CON0 */
0x1198, 0x3FF, 0x13,/* AUXADC_CON6 */
0x119E, 0x7000, 0x6000,/* AUXADC_CON9 */
0x11BE, 0x19, 0x11,/* AUXADC_NAG_0 */
0x11D4, 0x2, 0x0,/* AUXADC_NAG_11 */
0x1212, 0x2, 0x0,/* AUXADC_IMP5 */
0x1224, 0x2, 0x0,/* AUXADC_LBAT8 */
0x1226, 0x1, 0x1,/* AUXADC_BAT_TEMP_0 */
0x122A, 0x3, 0x2,/* AUXADC_BAT_TEMP_2 */
0x122E, 0x2000, 0x2000,/* AUXADC_BAT_TEMP_4 */
0x1238, 0x2, 0x0,/* AUXADC_BAT_TEMP_9 */
0x123A, 0x1, 0x1,/* AUXADC_LBAT2_0 */
0x123C, 0x3, 0x2,/* AUXADC_LBAT2_1 */
0x1240, 0x2000, 0x2000,/* AUXADC_LBAT2_3 */
0x124A, 0x2, 0x0,/* AUXADC_LBAT2_8 */
0x125C, 0x2, 0x0,/* AUXADC_THR8 */
0x125E, 0x8003, 0x8002,/* AUXADC_MDRT_0 */
0x1262, 0x4, 0x4,/* AUXADC_MDRT_2 */
0x140C, 0x7, 0x0,/* BUCK_TOP_CLK_CON0 */
0x1412, 0x8, 0x8,/* BUCK_TOP_CLK_HWEN_CON0 */
0x1494, 0x7, 0x2,/* BUCK_VPU_OP_EN */
0x149A, 0x7, 0x7,/* BUCK_VPU_OP_CFG */
0x14A0, 0x7, 0x7,/* BUCK_VPU_OP_MODE */
0x1514, 0x7, 0x4,/* BUCK_VCORE_OP_EN */
0x151A, 0x7, 0x4,/* BUCK_VCORE_OP_CFG */
0x1520, 0x7, 0x7,/* BUCK_VCORE_OP_MODE */
0x1588, 0x1, 0x0,/* BUCK_VGPU11_CON0 */
0x1594, 0x7, 0x0,/* BUCK_VGPU11_OP_EN */
0x159A, 0x7, 0x0,/* BUCK_VGPU11_OP_CFG */
0x1608, 0x1, 0x0,/* BUCK_VGPU12_CON0 */
0x1614, 0x5, 0x0,/* BUCK_VGPU12_OP_EN */
0x1688, 0x1, 0x1,/* BUCK_VMODEM_CON0 */
0x1694, 0x7, 0x2,/* BUCK_VMODEM_OP_EN */
0x169A, 0x2, 0x0,/* BUCK_VMODEM_OP_CFG */
0x16A0, 0x2, 0x2,/* BUCK_VMODEM_OP_MODE */
0x1708, 0x1, 0x0,/* BUCK_VPROC1_CON0 */
0x1714, 0x5, 0x0,/* BUCK_VPROC1_OP_EN */
0x1788, 0x3, 0x0,/* BUCK_VPROC2_CON0 */
0x1794, 0x7, 0x0,/* BUCK_VPROC2_OP_EN */
0x179A, 0x2, 0x0,/* BUCK_VPROC2_OP_CFG */
0x17A0, 0x2, 0x2,/* BUCK_VPROC2_OP_MODE */
0x1808, 0x1, 0x1,/* BUCK_VS1_CON0 */
0x1814, 0x5, 0x5,/* BUCK_VS1_OP_EN */
0x181A, 0x5, 0x5,/* BUCK_VS1_OP_CFG */
0x1888, 0x1, 0x1,/* BUCK_VS2_CON0 */
0x1894, 0x5, 0x5,/* BUCK_VS2_OP_EN */
0x189A, 0x5, 0x5,/* BUCK_VS2_OP_CFG */
0x1908, 0x1, 0x0,/* BUCK_VPA_CON0 */
0x1B0E, 0x3, 0x3,/* TOP_TOP_CKHWEN_CON0 */
0x1B10, 0x1, 0x1,/* LDO_TOP_CLK_DCM_CON0 */
0x1B34, 0x2, 0x2,/* VRTC28_CON */
0x1B8A, 0x8000, 0x0,/* LDO_VFE28_CON1 */
0x1B8E, 0x2, 0x2,/* LDO_VFE28_OP_EN */
0x1B9C, 0x8000, 0x0,/* LDO_VXO22_CON1 */
0x1BA0, 0x4, 0x4,/* LDO_VXO22_OP_EN */
0x1BA6, 0x4, 0x4,/* LDO_VXO22_OP_CFG */
0x1BAE, 0x8000, 0x0,/* LDO_VRF18_CON1 */
0x1BB2, 0x2, 0x2,/* LDO_VRF18_OP_EN */
0x1BC0, 0x8000, 0x0,/* LDO_VRF12_CON1 */
0x1BC4, 0x4, 0x4,/* LDO_VRF12_OP_EN */
0x1BCA, 0x4, 0x4,/* LDO_VRF12_OP_CFG */
0x1BD0, 0x1, 0x0,/* LDO_VEFUSE_CON0 */
0x1BD2, 0x8000, 0x0,/* LDO_VEFUSE_CON1 */
0x1BE2, 0x1, 0x0,/* LDO_VCN33_1_CON0 */
0x1BE4, 0x8000, 0x0,/* LDO_VCN33_1_CON1 */
0x1BF4, 0x8000, 0x0,/* LDO_VCN33_1_MULTI_SW */
0x1C08, 0x1, 0x0,/* LDO_VCN33_2_CON0 */
0x1C0A, 0x8000, 0x0,/* LDO_VCN33_2_CON1 */
0x1C1A, 0x8000, 0x0,/* LDO_VCN33_2_MULTI_SW */
0x1C1C, 0x1, 0x0,/* LDO_VCN13_CON0 */
0x1C1E, 0x8000, 0x0,/* LDO_VCN13_CON1 */
0x1C2E, 0x1, 0x0,/* LDO_VCN18_CON0 */
0x1C30, 0x8000, 0x0,/* LDO_VCN18_CON1 */
0x1C42, 0x8000, 0x0,/* LDO_VA09_CON1 */
0x1C46, 0x2, 0x2,/* LDO_VA09_OP_EN */
0x1C4C, 0x2, 0x2,/* LDO_VA09_OP_CFG */
0x1C52, 0x1, 0x0,/* LDO_VCAMIO_CON0 */
0x1C54, 0x8000, 0x0,/* LDO_VCAMIO_CON1 */
0x1C66, 0x8000, 0x0,/* LDO_VA12_CON1 */
0x1C6A, 0x4, 0x4,/* LDO_VA12_OP_EN */
0x1C70, 0x4, 0x4,/* LDO_VA12_OP_CFG */
0x1C8A, 0x8000, 0x0,/* LDO_VAUX18_CON1 */
0x1C8E, 0x4, 0x4,/* LDO_VAUX18_OP_EN */
0x1C94, 0x4, 0x4,/* LDO_VAUX18_OP_CFG */
0x1C9A, 0x1, 0x0,/* LDO_VAUD18_CON0 */
0x1C9C, 0x8000, 0x0,/* LDO_VAUD18_CON1 */
0x1CAC, 0x1, 0x1,/* LDO_VIO18_CON0 */
0x1CAE, 0x8004, 0x0,/* LDO_VIO18_CON1 */
0x1CBE, 0x1, 0x1,/* LDO_VEMC_CON0 */
0x1CC0, 0x8000, 0x0,/* LDO_VEMC_CON1 */
0x1CD0, 0x1, 0x0,/* LDO_VSIM1_CON0 */
0x1CD2, 0x8000, 0x0,/* LDO_VSIM1_CON1 */
0x1CE2, 0x1, 0x0,/* LDO_VSIM2_CON0 */
0x1CE4, 0x8000, 0x0,/* LDO_VSIM2_CON1 */
0x1D0A, 0x8000, 0x0,/* LDO_VUSB_CON1 */
0x1D0E, 0x4, 0x4,/* LDO_VUSB_OP_EN */
0x1D14, 0x4, 0x4,/* LDO_VUSB_OP_CFG */
0x1D1C, 0x1, 0x0,/* LDO_VRFCK_CON0 */
0x1D1E, 0x8000, 0x0,/* LDO_VRFCK_CON1 */
0x1D22, 0x4000, 0x4000,/* LDO_VRFCK_OP_EN */
0x1D2E, 0x1, 0x1,/* LDO_VBBCK_CON0 */
0x1D30, 0x8000, 0x0,/* LDO_VBBCK_CON1 */
0x1D34, 0x4000, 0x4000,/* LDO_VBBCK_OP_EN */
0x1D42, 0x8000, 0x0,/* LDO_VBIF28_CON1 */
0x1D46, 0x4, 0x4,/* LDO_VBIF28_OP_EN */
0x1D52, 0x1, 0x0,/* LDO_VIBR_CON0 */
0x1D54, 0x8000, 0x0,/* LDO_VIBR_CON1 */
0x1D64, 0x1, 0x1,/* LDO_VIO28_CON0 */
0x1D66, 0x8000, 0x0,/* LDO_VIO28_CON1 */
0x1D88, 0x1, 0x1,/* LDO_VM18_CON0 */
0x1D8A, 0x8000, 0x0,/* LDO_VM18_CON1 */
0x1D9A, 0x1, 0x1,/* LDO_VUFS_CON0 */
0x1D9C, 0x8000, 0x0,/* LDO_VUFS_CON1 */
0x1E88, 0x1, 0x0,/* LDO_VSRAM_PROC1_CON0 */
0x1E8A, 0x8000, 0x0,/* LDO_VSRAM_PROC1_CON1 */
0x1EA8, 0x1, 0x0,/* LDO_VSRAM_PROC2_CON0 */
0x1EAA, 0x8000, 0x0,/* LDO_VSRAM_PROC2_CON1 */
0x1F08, 0x1, 0x1,/* LDO_VSRAM_OTHERS_CON0 */
0x1F0A, 0x8000, 0x0,/* LDO_VSRAM_OTHERS_CON1 */
0x1F16, 0x7, 0x4,/* LDO_VSRAM_OTHERS_OP_EN */
0x1F1C, 0x7, 0x4,/* LDO_VSRAM_OTHERS_OP_CFG */
0x1F2E, 0x1, 0x0,/* LDO_VSRAM_MD_CON0 */
0x1F30, 0x8000, 0x0,/* LDO_VSRAM_MD_CON1 */
};
const unsigned int *AP_PMIC_REG_6365_gs_deepidle___lp_mp3_32kless =
AP_PMIC_REG_6365_gs_deepidle___lp_mp3_32kless_data;
unsigned int AP_PMIC_REG_6365_gs_deepidle___lp_mp3_32kless_len = 444;
@@ -0,0 +1,26 @@
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/include/power_gs_v1
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/inc/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/mtk_lpm_fs/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/inc/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/modules/include/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/lpm/modules/include/mt6853/
obj-y += mt6853_dbg_fs.o
obj-y += mt6853_dbg_lpm_fs.o
obj-y += mt6853_dbg_lpm_rc_fs.o
obj-y += mt6853_dbg_spm_fs.o
obj-y += mt6853_logger.o
obj-y += mt6853_lpm_trace_event.o
obj-y += mt6853_cpupm_dbg.o
mt6853_cpupm_dbg-y += mtk_cpupm_dbg.o
mt6853_cpupm_dbg-y += mtk_cpuidle_status.o
mt6853_cpupm_dbg-y += mtk_cpuidle_cpc.o
mt6853_cpupm_dbg-y += mtk_idle_procfs.o
mt6853_cpupm_dbg-y += mtk_idle_procfs_state.o
mt6853_cpupm_dbg-y += mtk_idle_procfs_cpc.o
mt6853_cpupm_dbg-y += mtk_idle_procfs_profile.o
mt6853_cpupm_dbg-y += mtk_idle_procfs_control.o
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mt6853_dbg_fs_common.h>
static void __exit mt6853_dbg_fs_exit(void)
{
mt6853_dbg_lpm_fs_deinit();
mt6853_dbg_spm_fs_deinit();
mt6853_dbg_lpm_deinit();
mtk_dbg_common_fs_exit();
}
static int __init mt6853_dbg_fs_init(void)
{
mt6853_dbg_lpm_init();
mt6853_dbg_lpm_fs_init();
mt6853_dbg_spm_fs_init();
mtk_dbg_common_fs_init();
pr_info("%s %d: finish", __func__, __LINE__);
return 0;
}
module_init(mt6853_dbg_fs_init);
module_exit(mt6853_dbg_fs_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MT6853 Low Power FileSystem");
MODULE_AUTHOR("MediaTek Inc.");
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MT6853_DBG_FS_COMMON_H__
#define __MT6853_DBG_FS_COMMON_H__
/* Determine for user name handle */
#define MT_LP_RQ_USER_NAME_LEN (4)
#define MT_LP_RQ_USER_CHAR_U (8)
#define MT_LP_RQ_USER_CHAR_MASK (0xFF)
/* Determine for resource usage id */
#define MT_LP_RQ_ID_ALL_USAGE (-1)
int mt6853_dbg_lpm_init(void);
int mt6853_dbg_lpm_deinit(void);
int mt6853_dbg_lpm_fs_init(void);
int mt6853_dbg_lpm_fs_deinit(void);
int mt6853_dbg_spm_fs_init(void);
int mt6853_dbg_spm_fs_deinit(void);
#endif /* __MTK_LP_PLAT_FS_H__ */
@@ -0,0 +1,233 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/device.h>
#include <linux/of_device.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mtk_resource_constraint_v1.h>
#include <mt6853_pwr_ctrl.h>
#include <mt6853_dbg_fs_common.h>
#include <mt6853_cond.h>
#include <mt6853_spm_comm.h>
#include <mtk_lpm_sysfs.h>
#include <mtk_lp_sysfs.h>
#include <mtk_lpm_platform.h>
#define MT6853_GENERIC_TRACE_NODE_INIT(_n, _r, _w) ({\
_n.op.fs_read = _r;\
_n.op.fs_write = _w;\
_n.op.priv = &_n; })
#define mt6853_dbg_lpm_trace_log(buf, sz, len, fmt, args...) ({\
if (len < sz)\
len += scnprintf(buf + len, sz - len,\
fmt, ##args); })
/* Common section */
#define MT6853_DGB_TRACE_COMMON_BASE (0x0)
#define MT6853_DGB_TRACE_COMMON_SZ (0x20)
#define TRACE_DBG_LAST_RC_INFO_CPU_SHIFT (28)
#define TRACE_DBG_LAST_RC_INFO_CPU_MASK (0xf)
#define TRACE_DBG_LAST_RC_INFO_RCID_SHIFT (0)
#define TRACE_DBG_LAST_RC_INFO_RCID_MASK (0xffff)
#define TRACE_DBG_LAST_RC_INFO_CPU(info)\
((info >> TRACE_DBG_LAST_RC_INFO_CPU_SHIFT)\
& TRACE_DBG_LAST_RC_INFO_CPU_MASK)
#define TRACE_DBG_LAST_RC_INFO_RC_ID(info)\
((info >> TRACE_DBG_LAST_RC_INFO_RCID_SHIFT)\
& TRACE_DBG_LAST_RC_INFO_RCID_MASK)
/* Suspend section */
#define MT6853_DGB_TRACE_SUSPEND_BASE \
(MT6853_DGB_TRACE_COMMON_BASE + MT6853_DGB_TRACE_COMMON_SZ)
#define MT6853_DGB_TRACE_SUSPEND_SZ (0xe0)
/* Low power section */
#define MT6853_DGB_TRACE_LP_BASE \
(MT6853_DGB_TRACE_SUSPEND_BASE + MT6853_DGB_TRACE_SUSPEND_SZ)
#define MT6853_DGB_TRACE_LP_SZ (0x200)
struct MT6853_LPM_TRACE_NODE {
struct mtk_lp_sysfs_handle handle;
struct mtk_lp_sysfs_op op;
};
struct mtk_lp_sysfs_handle mt6853_entry_lpm_trace;
struct MT6853_LPM_TRACE_NODE mt6853_lpm_trace_node_comm;
struct MT6853_LPM_TRACE_NODE mt6853_lpm_trace_node_lp;
struct MT6853_LPM_TRACE_NODE mt6853_lpm_trace_node_suspend;
struct MT6853_TRACE_COMM {
unsigned int magic;
unsigned int common_fp;
unsigned int rc_timestamp_h;
unsigned int rc_timestamp_l;
unsigned int rc_info;
unsigned int rc_fp;
unsigned int rc_valid;
};
static ssize_t mt6853_dbg_lpm_trace_suspend_show(char *ToUserBuf,
size_t sz, void *priv)
{
struct MTK_LPM_PLAT_TRACE trace;
unsigned int dump[4], offset = 0;
size_t len = 0;
int ret = 0;
ret = mtk_lpm_platform_trace_get(MT_LPM_PLAT_TRACE_SYSRAM, &trace);
if (ret || !trace.read)
return -ENOENT;
while (((offset + sizeof(dump)) < MT6853_DGB_TRACE_SUSPEND_SZ)) {
dump[0] = dump[1] = dump[2] = dump[3] = 0;
trace.read((MT6853_DGB_TRACE_SUSPEND_BASE + offset),
dump, sizeof(dump));
mt6853_dbg_lpm_trace_log(ToUserBuf, sz, len,
"%03xh: %08x %08x %08x %08x\n", offset,
dump[0], dump[1], dump[2], dump[3]);
offset += sizeof(dump);
}
return len;
}
static ssize_t mt6853_dbg_lpm_trace_lp_show(char *ToUserBuf,
size_t sz, void *priv)
{
struct MTK_LPM_PLAT_TRACE trace;
unsigned int dump[4], offset = 0;
size_t len = 0;
int ret = 0;
ret = mtk_lpm_platform_trace_get(MT_LPM_PLAT_TRACE_SYSRAM, &trace);
if (ret || !trace.read)
return -ENOENT;
while (((offset + sizeof(dump)) <= MT6853_DGB_TRACE_LP_SZ)) {
dump[0] = dump[1] = dump[2] = dump[3] = 0;
trace.read((MT6853_DGB_TRACE_LP_BASE + offset),
dump, sizeof(dump));
mt6853_dbg_lpm_trace_log(ToUserBuf, sz, len,
"%03xh: %08x %08x %08x %08x\n", offset,
dump[0], dump[1], dump[2], dump[3]);
offset += sizeof(dump);
}
return len;
}
static ssize_t mt6853_dbg_lpm_trace_comm_show(char *ToUserBuf,
size_t sz, void *priv)
{
struct MTK_LPM_PLAT_TRACE trace;
struct MT6853_TRACE_COMM *data = NULL;
size_t len = 0, rSz = 0;
int ret = 0;
ret = mtk_lpm_platform_trace_get(MT_LPM_PLAT_TRACE_SYSRAM, &trace);
if (ret || !trace.read)
return -ENOENT;
data = kzalloc(sizeof(struct MT6853_TRACE_COMM),
GFP_KERNEL);
if (!data)
return -ENOMEM;
rSz = (sizeof(struct MT6853_TRACE_COMM) > MT6853_DGB_TRACE_COMMON_SZ) ?
MT6853_DGB_TRACE_COMMON_SZ : sizeof(struct MT6853_TRACE_COMM);
len = trace.read(MT6853_DGB_TRACE_COMMON_BASE, data, rSz);
if (len > 0) {
unsigned long long timestamp;
len = 0;
timestamp = data->rc_timestamp_h;
timestamp = (timestamp << 32) | data->rc_timestamp_l;
mt6853_dbg_lpm_trace_log(ToUserBuf, sz, len,
"magic: 0x%x\n", data->magic);
mt6853_dbg_lpm_trace_log(ToUserBuf, sz, len,
"common_footfprint: 0x%x\n", data->common_fp);
mt6853_dbg_lpm_trace_log(ToUserBuf, sz, len,
"timestamp: %llu.%03llu\n",
timestamp/1000000000,
timestamp%1000000000);
mt6853_dbg_lpm_trace_log(ToUserBuf, sz, len,
"last constriant\n - cpu:%d\n - rc_id:%d\n - valid:0x%x\n",
TRACE_DBG_LAST_RC_INFO_CPU(data->rc_info),
TRACE_DBG_LAST_RC_INFO_RC_ID(data->rc_info),
data->rc_valid);
}
kfree(data);
return sz;
}
int mt6853_dbg_lpm_init(void)
{
mtk_lpm_sysfs_root_entry_create();
mtk_lpm_sysfs_sub_entry_add("trace", 0644, NULL,
&mt6853_entry_lpm_trace);
MT6853_GENERIC_TRACE_NODE_INIT(mt6853_lpm_trace_node_comm,
mt6853_dbg_lpm_trace_comm_show,
NULL);
mtk_lpm_sysfs_sub_entry_node_add("common", 0400,
&mt6853_lpm_trace_node_comm.op,
&mt6853_entry_lpm_trace,
&mt6853_lpm_trace_node_comm.handle);
MT6853_GENERIC_TRACE_NODE_INIT(mt6853_lpm_trace_node_lp,
mt6853_dbg_lpm_trace_lp_show,
NULL);
mtk_lpm_sysfs_sub_entry_node_add("lp", 0400,
&mt6853_lpm_trace_node_lp.op,
&mt6853_entry_lpm_trace,
&mt6853_lpm_trace_node_lp.handle);
MT6853_GENERIC_TRACE_NODE_INIT(mt6853_lpm_trace_node_suspend,
mt6853_dbg_lpm_trace_suspend_show,
NULL);
mtk_lpm_sysfs_sub_entry_node_add("suspend", 0400,
&mt6853_lpm_trace_node_suspend.op,
&mt6853_entry_lpm_trace,
&mt6853_lpm_trace_node_suspend.handle);
return 0;
}
int mt6853_dbg_lpm_deinit(void)
{
mtk_lpm_sysfs_entry_node_remove(&mt6853_lpm_trace_node_suspend.handle);
mtk_lpm_sysfs_entry_node_remove(&mt6853_lpm_trace_node_lp.handle);
mtk_lpm_sysfs_entry_node_remove(&mt6853_lpm_trace_node_comm.handle);
mtk_lpm_sysfs_entry_node_remove(&mt6853_entry_lpm_trace);
return 0;
}
@@ -0,0 +1,619 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/console.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/spinlock.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mtk_resource_constraint_v1.h>
#include <mt6853_pwr_ctrl.h>
#include <mt6853_dbg_fs_common.h>
#include <mt6853_cond.h>
#include <mt6853_spm_comm.h>
#include <mtk_lpm_sysfs.h>
#include <mtk_lp_sysfs.h>
#include <mtk_lpm_timer.h>
#define SPM_RC_UPDATE_COND_ID_MASK 0xffff
#define SPM_RC_UPDATE_COND_RC_ID_MASK 0xffff
#define SPM_RC_UPDATE_COND_RC_ID_SHIFT (16)
#define SPM_RC_UPDATE_COND_CTRL_ID(rc, cond)\
(((rc & SPM_RC_UPDATE_COND_RC_ID_MASK)\
<< SPM_RC_UPDATE_COND_RC_ID_SHIFT)\
| (cond & SPM_RC_UPDATE_COND_ID_MASK))
#define MT6853_LPM_RC_RATIO_DEFAULT 10000
#define mt6853_rc_log(buf, sz, len, fmt, args...) ({\
if (len < sz)\
len += scnprintf(buf + len, sz - len,\
fmt, ##args); })
#define MT6853_DBG_SMC(_id, _act, _rc, _param) ({\
(unsigned long)mtk_lpm_smc_spm_dbg(_id, _act, _rc, _param); })
enum MT6853_RC_RATIO_TYPE {
MT6853_RC_RATIO_UNKNOWN,
MT6853_RC_RATIO_ENABLE,
MT6853_RC_RATIO_DISABLE,
MT6853_RC_RATIO_ENABLE_INFO,
MT6853_RC_RATIO_INFO,
};
enum MT6853_RC_NODE_TYPE {
MT6853_RC_NODE_STATE,
MT6853_RC_NODE_RC_ENABLE,
MT6853_RC_NODE_RC_STATE_SIMPLE,
MT6853_RC_NODE_RC_STATE,
MT6853_RC_NODE_COND_ENABLE,
MT6853_RC_NODE_COND_STATE,
MT6853_RC_NODE_COND_SET,
MT6853_RC_NODE_COND_CLR,
MT6853_RC_NODE_RATIO_ENABLE,
MT6853_RC_NODE_RATIO_INTERVAL,
MT6853_RC_NODE_VALID_BBLPM,
MT6853_RC_NODE_VALID_TRACE,
MT6853_RC_NODE_VALID_TIMESTAMP,
MT6853_RC_NODE_MAX
};
struct MT6853_RC_NODE {
const char *name;
int rc_id;
int type;
struct mtk_lp_sysfs_handle handle;
struct mtk_lp_sysfs_op op;
};
struct MT6853_RC_ENTERY {
const char *name;
struct mtk_lp_sysfs_handle handle;
};
struct MT6853_RC_COND_HANDLES {
struct MT6853_RC_ENTERY root;
struct MT6853_RC_NODE hSet;
struct MT6853_RC_NODE hClr;
struct MT6853_RC_NODE hState;
struct MT6853_RC_NODE hEnable;
};
struct MT6853_RC_VALID_HANDLES {
struct MT6853_RC_ENTERY root;
struct MT6853_RC_NODE hBblpm;
struct MT6853_RC_NODE hTrace;
struct MT6853_RC_NODE hTimestamp;
};
struct MT6853_RC_RATIO_HANDLES {
struct MT6853_RC_ENTERY root;
struct MT6853_RC_NODE hInterval;
struct MT6853_RC_NODE hEnable;
};
struct MT6853_RC_HANDLE_BASIC {
struct MT6853_RC_ENTERY root;
struct MT6853_RC_NODE hEnable;
struct MT6853_RC_NODE hState;
};
struct MT6853_RC_HANDLE {
struct MT6853_RC_HANDLE_BASIC basic;
struct MT6853_RC_COND_HANDLES hCond;
struct MT6853_RC_VALID_HANDLES valid;
};
#define MT6853_CONSTRAINT_GENERIC_OP(op, _priv) ({\
op.fs_read = mt6853_generic_rc_read;\
op.fs_write = mt6853_generic_rc_write;\
op.priv = _priv; })
#define MT6853_GENERIC_RC_NODE_INIT(_n, _name, _id, _type) ({\
_n.name = _name;\
_n.type = _type;\
_n.rc_id = _id;\
MT6853_CONSTRAINT_GENERIC_OP(_n.op, &_n); })
struct mtk_lp_sysfs_handle mt6853_entry_rc;
struct MT6853_RC_HANDLE rc_dram;
struct MT6853_RC_HANDLE rc_syspll;
struct MT6853_RC_HANDLE rc_bus26m;
struct MT6853_RC_HANDLE_BASIC rc_cpu_buckldo;
struct MT6853_RC_RATIO_HANDLES rc_Ratio;
struct MT6853_RC_NODE rc_state;
struct mt_lpm_timer rc_ratio_timer;
static char *mt6853_spm_cond_cg_str[PLAT_SPM_COND_MAX] = {
[PLAT_SPM_COND_MTCMOS_0] = "MTCMOS_0",
[PLAT_SPM_COND_CG_INFRA_0] = "INFRA_0",
[PLAT_SPM_COND_CG_INFRA_1] = "INFRA_1",
[PLAT_SPM_COND_CG_INFRA_2] = "INFRA_2",
[PLAT_SPM_COND_CG_INFRA_3] = "INFRA_3",
[PLAT_SPM_COND_CG_INFRA_4] = "INFRA_4",
[PLAT_SPM_COND_CG_INFRA_5] = "INFRA_5",
[PLAT_SPM_COND_CG_MMSYS_0] = "MMSYS_0",
[PLAT_SPM_COND_CG_MMSYS_1] = "MMSYS_1",
[PLAT_SPM_COND_CG_MMSYS_2] = "MMSYS_2",
[PLAT_SPM_COND_CG_MMSYS_3] = "MMSYS_3",
};
static char *mt6853_spm_cond_pll_str[PLAT_SPM_COND_PLL_MAX] = {
[PLAT_SPM_COND_UNIVPLL] = "UNIVPLL",
[PLAT_SPM_COND_MFGPLL] = "MFGPLL",
[PLAT_SPM_COND_MSDCPLL] = "MSDCPLL",
[PLAT_SPM_COND_TVPLL] = "TVPLL",
[PLAT_SPM_COND_MMPLL] = "MMPLL",
};
int mt6853_lpm_rc_cond_ctrl(int rc_id, unsigned int act,
unsigned int cond_id, unsigned int value)
{
unsigned int cond_ctrl_id;
int res = 0;
cond_ctrl_id = SPM_RC_UPDATE_COND_CTRL_ID(rc_id, cond_id);
if (cond_id < PLAT_SPM_COND_MAX)
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_COND_CTRL,
act, cond_ctrl_id, value);
else if ((cond_id - PLAT_SPM_COND_MAX) < PLAT_SPM_COND_PLL_MAX)
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_COND_CTRL,
act, cond_ctrl_id, !!value);
else
pr_info("[%s:%d] - unknown cond id = %u\n",
__func__, __LINE__, cond_id);
return res;
}
int mt6853_lpm_rc_ratio_timer_func(unsigned long long dur, void *priv)
{
/* spm tick to ms: tick/32 (ms) */
#define RC_RESIDENCY_COVERT(x) (x>>5)
#define RC_COVERT_RATIO(dur, x)\
((RC_RESIDENCY_COVERT(x) * 100)/dur)
unsigned long long rc_dram_t;
unsigned long long rc_syspll_t;
unsigned long long rc_bus26m_t;
rc_dram_t = (unsigned long long)
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_INFO,
(MT_LPM_SMC_ACT_GET | MT_LPM_SMC_ACT_CLR),
MT_RM_CONSTRAINT_ID_DRAM, 0);
rc_syspll_t = (unsigned long long)
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_INFO,
(MT_LPM_SMC_ACT_GET | MT_LPM_SMC_ACT_CLR),
MT_RM_CONSTRAINT_ID_SYSPLL, 0);
rc_bus26m_t = (unsigned long long)
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_INFO,
(MT_LPM_SMC_ACT_GET | MT_LPM_SMC_ACT_CLR),
MT_RM_CONSTRAINT_ID_BUS26M, 0);
pr_info("[name:spm&][RC] ratio, duration_ms:%llu, dram:%llu%%, syspll:%llu%%, bus26m:%llu%%\n",
dur, RC_COVERT_RATIO(dur, rc_dram_t),
RC_COVERT_RATIO(dur, rc_syspll_t),
RC_COVERT_RATIO(dur, rc_bus26m_t));
return 0;
}
static ssize_t mt6853_lpm_rc_block_info(int rc_id,
char *ToUserBuf,
size_t sz)
{
uint32_t block, b;
int i;
ssize_t len = 0;
block = (uint32_t)
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_COND_BLOCK,
MT_LPM_SMC_ACT_GET, rc_id, 0);
b = (uint32_t)
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_COND_CHECK,
MT_LPM_SMC_ACT_GET, rc_id, 0);
mt6853_rc_log(ToUserBuf, sz, len,
"blocked=%u, blocked_cond=0x%08x\n",
b, block);
for (i = 0, b = block >> SPM_COND_BLOCKED_CG_IDX;
i < PLAT_SPM_COND_MAX; i++)
mt6853_rc_log(ToUserBuf, sz, len,
"[%2d] %8s=0x%08lx\n", i,
mt6853_spm_cond_cg_str[i],
((b >> i) & 0x1) ?
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_BLOCK_DETAIL,
MT_LPM_SMC_ACT_GET, rc_id, i) : 0);
for (i = 0, b = block >> SPM_COND_BLOCKED_PLL_IDX;
i < PLAT_SPM_COND_PLL_MAX; i++)
mt6853_rc_log(ToUserBuf, sz, len,
"[%2d] %8s=%d\n",
(i + PLAT_SPM_COND_MAX),
mt6853_spm_cond_pll_str[i],
((b >> i) & 0x1));
return len;
}
static ssize_t mt6853_rc_state(int rc_id, char *ToUserBuf, size_t sz)
{
ssize_t len = 0;
if (rc_id < 0)
return 0;
mt6853_rc_log(ToUserBuf, sz, len,
"enable=%lu, count=%lu, rc-id=%d\n",
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_SWITCH,
MT_LPM_SMC_ACT_GET, rc_id, 0)
& MT_SPM_RC_VALID_SW,
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_CNT,
MT_LPM_SMC_ACT_GET, rc_id, 0),
rc_id);
return len;
}
static ssize_t mt6853_generic_rc_read(char *ToUserBuf,
size_t sz, void *priv)
{
ssize_t len = 0;
struct MT6853_RC_NODE *node = (struct MT6853_RC_NODE *)priv;
if (!node)
return -EINVAL;
switch (node->type) {
case MT6853_RC_NODE_STATE:
mt6853_rc_log(ToUserBuf, sz, len, "count:%lu\n",
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_IDLE_CNT,
MT_LPM_SMC_ACT_GET, DBG_CTRL_COUNT, 0));
break;
case MT6853_RC_NODE_RC_STATE_SIMPLE:
len += mt6853_rc_state(node->rc_id, ToUserBuf, sz);
break;
case MT6853_RC_NODE_RC_STATE:
len += mt6853_rc_state(node->rc_id, ToUserBuf, sz);
len += mt6853_lpm_rc_block_info(node->rc_id,
ToUserBuf + len, sz - len);
break;
case MT6853_RC_NODE_RC_ENABLE:
mt6853_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_SWITCH,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6853_RC_NODE_COND_ENABLE:
mt6853_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_COND_CHECK,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6853_RC_NODE_COND_STATE:
len += mt6853_lpm_rc_block_info(node->rc_id, ToUserBuf, sz);
break;
case MT6853_RC_NODE_RATIO_ENABLE:
mt6853_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_CTRL,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6853_RC_NODE_RATIO_INTERVAL:
mt6853_rc_log(ToUserBuf, sz, len, "%lu\n",
mtk_lpm_timer_interval(&rc_ratio_timer));
break;
case MT6853_RC_NODE_VALID_BBLPM:
mt6853_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_BBLPM,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6853_RC_NODE_VALID_TRACE:
mt6853_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
case MT6853_RC_NODE_VALID_TIMESTAMP:
mt6853_rc_log(ToUserBuf, sz, len, "%lu\n",
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE_TIME,
MT_LPM_SMC_ACT_GET, node->rc_id, 0));
break;
}
return len;
}
static ssize_t mt6853_generic_rc_write(char *FromUserBuf,
size_t sz, void *priv)
{
struct MT6853_RC_NODE *node = (struct MT6853_RC_NODE *)priv;
if (!node)
return -EINVAL;
if ((node->type == MT6853_RC_NODE_RC_ENABLE) ||
(node->type == MT6853_RC_NODE_COND_ENABLE) ||
(node->type == MT6853_RC_NODE_VALID_BBLPM) ||
(node->type == MT6853_RC_NODE_VALID_TRACE) ||
(node->type == MT6853_RC_NODE_VALID_TIMESTAMP)) {
unsigned int parm;
int cmd;
if ((!kstrtoint(FromUserBuf, 10, &parm)) == 1) {
cmd = (node->type == MT6853_RC_NODE_RC_ENABLE) ?
MT_SPM_DBG_SMC_UID_RC_SWITCH :
(node->type == MT6853_RC_NODE_COND_ENABLE) ?
MT_SPM_DBG_SMC_UID_COND_CHECK :
(node->type == MT6853_RC_NODE_VALID_BBLPM) ?
MT_SPM_DBG_SMC_UID_RC_BBLPM :
(node->type == MT6853_RC_NODE_VALID_TRACE) ?
MT_SPM_DBG_SMC_UID_RC_TRACE :
(node->type == MT6853_RC_NODE_VALID_TIMESTAMP) ?
MT_SPM_DBG_SMC_UID_RC_TRACE_TIME : -1;
if (cmd < 0)
return -EINVAL;
if (!!parm)
parm = MT_LPM_SMC_ACT_SET;
else
parm = MT_LPM_SMC_ACT_CLR;
MT6853_DBG_SMC(cmd, parm, node->rc_id, 0);
}
} else if ((node->type == MT6853_RC_NODE_COND_SET)
|| (node->type == MT6853_RC_NODE_COND_CLR)) {
unsigned int parm1, parm2, act;
if (sscanf(FromUserBuf, "%u %x", &parm1, &parm2) == 2) {
act = (node->type == MT6853_RC_NODE_COND_SET) ?
MT_LPM_SMC_ACT_SET :
(node->type == MT6853_RC_NODE_COND_CLR) ?
MT_LPM_SMC_ACT_CLR : 0;
if (act != 0)
mt6853_lpm_rc_cond_ctrl(node->rc_id, act,
parm1, parm2);
}
} else if ((node->type == MT6853_RC_NODE_RATIO_ENABLE)
|| (node->type == MT6853_RC_NODE_RATIO_INTERVAL)) {
unsigned int parm;
if ((!kstrtoint(FromUserBuf, 10, &parm)) == 1) {
if (node->type == MT6853_RC_NODE_RATIO_ENABLE) {
parm = (!!parm) ? MT_LPM_SMC_ACT_SET :
MT_LPM_SMC_ACT_CLR;
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_CTRL,
parm, MT_RM_CONSTRAINT_ID_DRAM, 0);
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_CTRL,
parm, MT_RM_CONSTRAINT_ID_SYSPLL, 0);
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_RES_CTRL,
parm, MT_RM_CONSTRAINT_ID_BUS26M, 0);
if (parm == MT_LPM_SMC_ACT_SET)
mtk_lpm_timer_start(&rc_ratio_timer);
else
mtk_lpm_timer_stop(&rc_ratio_timer);
} else if (node->type == MT6853_RC_NODE_RATIO_INTERVAL)
mtk_lpm_timer_interval_update(&rc_ratio_timer,
(unsigned long long)parm);
}
}
return sz;
}
static int mt6853_lpm_rc_node_add(struct MT6853_RC_NODE *n,
int mode, struct MT6853_RC_ENTERY *p)
{
return mtk_lpm_sysfs_sub_entry_node_add(n->name, mode,
&n->op, &p->handle, &n->handle);
}
static int mt6853_lpm_rc_entry_add(struct MT6853_RC_ENTERY *n,
int mode, struct MT6853_RC_ENTERY *p)
{
return mtk_lpm_sysfs_sub_entry_add(n->name, mode,
&p->handle, &n->handle);
}
static int mt6853_lpm_rc_valid_node_add(int rc_id,
struct MT6853_RC_ENTERY *parent,
struct MT6853_RC_VALID_HANDLES *valid)
{
int bRet = 0;
if (!valid || !parent)
return -EINVAL;
valid->root.name = "valid";
bRet = mt6853_lpm_rc_entry_add(&valid->root, 0644, parent);
if (!bRet) {
MT6853_GENERIC_RC_NODE_INIT(valid->hBblpm, "bblpm",
rc_id, MT6853_RC_NODE_VALID_BBLPM);
mt6853_lpm_rc_node_add(&valid->hBblpm, 0200, &valid->root);
MT6853_GENERIC_RC_NODE_INIT(valid->hTrace, "trace",
rc_id, MT6853_RC_NODE_VALID_TRACE);
mt6853_lpm_rc_node_add(&valid->hTrace, 0200, &valid->root);
MT6853_GENERIC_RC_NODE_INIT(valid->hTimestamp, "timestamp",
rc_id, MT6853_RC_NODE_VALID_TIMESTAMP);
mt6853_lpm_rc_node_add(&valid->hTimestamp, 0200, &valid->root);
}
return bRet;
}
static int mt6853_lpm_rc_ratio_entry(struct mtk_lp_sysfs_handle *parent,
struct MT6853_RC_RATIO_HANDLES *ratio)
{
int bRet = 0;
if (!ratio || !parent)
return -EINVAL;
ratio->root.name = "ratio";
bRet = mtk_lpm_sysfs_sub_entry_add(ratio->root.name, 0644,
parent, &ratio->root.handle);
if (!bRet) {
MT6853_GENERIC_RC_NODE_INIT(ratio->hEnable, "enable",
0, MT6853_RC_NODE_RATIO_ENABLE);
mt6853_lpm_rc_node_add(&ratio->hEnable, 0200, &ratio->root);
MT6853_GENERIC_RC_NODE_INIT(ratio->hInterval, "interval",
0, MT6853_RC_NODE_RATIO_INTERVAL);
mt6853_lpm_rc_node_add(&ratio->hInterval, 0200, &ratio->root);
}
return 0;
}
static int mt6853_lpm_rc_cond_node_add(int rc_id,
struct MT6853_RC_ENTERY *parent,
struct MT6853_RC_COND_HANDLES *cond)
{
int bRet = 0;
if (!cond || !parent)
return -EINVAL;
cond->root.name = "cond";
bRet = mt6853_lpm_rc_entry_add(&cond->root, 0644, parent);
if (!bRet) {
MT6853_GENERIC_RC_NODE_INIT(cond->hSet, "set",
rc_id, MT6853_RC_NODE_COND_SET);
mt6853_lpm_rc_node_add(&cond->hSet, 0200, &cond->root);
MT6853_GENERIC_RC_NODE_INIT(cond->hClr, "clr",
rc_id, MT6853_RC_NODE_COND_CLR);
mt6853_lpm_rc_node_add(&cond->hClr, 0200, &cond->root);
MT6853_GENERIC_RC_NODE_INIT(cond->hEnable, "enable",
rc_id, MT6853_RC_NODE_COND_ENABLE);
mt6853_lpm_rc_node_add(&cond->hEnable, 0644, &cond->root);
MT6853_GENERIC_RC_NODE_INIT(cond->hState, "state",
rc_id, MT6853_RC_NODE_COND_STATE);
mt6853_lpm_rc_node_add(&cond->hState, 0444, &cond->root);
}
return bRet;
}
static int mt6853_lpm_rc_entry_nodes_basic(int IsSimple,
const char *name, int rc_id,
struct mtk_lp_sysfs_handle *parent,
struct MT6853_RC_HANDLE_BASIC *rc)
{
int bRet = 0;
if (!parent || !rc)
return -EINVAL;
rc->root.name = name;
bRet = mtk_lpm_sysfs_sub_entry_add(rc->root.name, 0644,
parent, &rc->root.handle);
if (bRet)
return -EINVAL;
MT6853_GENERIC_RC_NODE_INIT(rc->hState, "state", rc_id,
(IsSimple) ? MT6853_RC_NODE_RC_STATE_SIMPLE
: MT6853_RC_NODE_RC_STATE);
mt6853_lpm_rc_node_add(&rc->hState, 0444, &rc->root);
MT6853_GENERIC_RC_NODE_INIT(rc->hEnable, "enable", rc_id,
MT6853_RC_NODE_RC_ENABLE);
mt6853_lpm_rc_node_add(&rc->hEnable, 0644, &rc->root);
return bRet;
}
int mt6853_lpm_rc_entry_nodes(const char *name, int rc_id,
struct mtk_lp_sysfs_handle *parent,
struct MT6853_RC_HANDLE *rc)
{
int bRet = 0;
if (!parent || !rc)
return -EINVAL;
memset(rc, 0, sizeof(struct MT6853_RC_HANDLE));
bRet = mt6853_lpm_rc_entry_nodes_basic(0, name, rc_id,
parent, &rc->basic);
bRet = mt6853_lpm_rc_cond_node_add(rc_id, &rc->basic.root,
&rc->hCond);
bRet = mt6853_lpm_rc_valid_node_add(rc_id, &rc->basic.root,
&rc->valid);
return 0;
}
int mt6853_dbg_lpm_fs_init(void)
{
/* enable resource constraint condition block latch */
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_BLOCK_LATCH,
MT_LPM_SMC_ACT_SET, 0, 0);
mtk_lpm_sysfs_root_entry_create();
mtk_lpm_sysfs_sub_entry_add("rc", 0644, NULL,
&mt6853_entry_rc);
MT6853_GENERIC_RC_NODE_INIT(rc_state, "state",
0, MT6853_RC_NODE_STATE);
mtk_lpm_sysfs_sub_entry_node_add(rc_state.name, 0444,
&rc_state.op, &mt6853_entry_rc,
&rc_state.handle);
mt6853_lpm_rc_ratio_entry(&mt6853_entry_rc, &rc_Ratio);
mt6853_lpm_rc_entry_nodes_basic(1, "cpu-buck-ldo",
MT_RM_CONSTRAINT_ID_CPU_BUCK_LDO,
&mt6853_entry_rc, &rc_cpu_buckldo);
mt6853_lpm_rc_entry_nodes("dram", MT_RM_CONSTRAINT_ID_DRAM,
&mt6853_entry_rc, &rc_dram);
mt6853_lpm_rc_entry_nodes("syspll", MT_RM_CONSTRAINT_ID_SYSPLL,
&mt6853_entry_rc, &rc_syspll);
mt6853_lpm_rc_entry_nodes("bus26m", MT_RM_CONSTRAINT_ID_BUS26M,
&mt6853_entry_rc, &rc_bus26m);
rc_ratio_timer.timeout = mt6853_lpm_rc_ratio_timer_func;
mtk_lpm_timer_init(&rc_ratio_timer, MTK_LPM_TIMER_REPEAT);
mtk_lpm_timer_interval_update(&rc_ratio_timer,
MT6853_LPM_RC_RATIO_DEFAULT);
/* enable constraint tracing */
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_SET, MT_RM_CONSTRAINT_ID_BUS26M, 0);
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_SET, MT_RM_CONSTRAINT_ID_SYSPLL, 0);
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_SET, MT_RM_CONSTRAINT_ID_DRAM, 0);
return 0;
}
int mt6853_dbg_lpm_fs_deinit(void)
{
/* disable resource contraint condition block latch */
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_BLOCK_LATCH,
MT_LPM_SMC_ACT_CLR, 0, 0);
/* disable constraint tracing */
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_CLR, MT_RM_CONSTRAINT_ID_BUS26M, 0);
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_CLR, MT_RM_CONSTRAINT_ID_SYSPLL, 0);
MT6853_DBG_SMC(MT_SPM_DBG_SMC_UID_RC_TRACE,
MT_LPM_SMC_ACT_CLR, MT_RM_CONSTRAINT_ID_DRAM, 0);
return 0;
}
@@ -0,0 +1,469 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/spinlock.h>
#include <mtk_dbg_common_v1.h>
#include <mtk_lpm_module.h>
#include <mtk_idle_sysfs.h>
#include <mtk_suspend_sysfs.h>
#include <mtk_spm_sysfs.h>
#include <mt6853_pwr_ctrl.h>
#include <mt6853_dbg_fs_common.h>
#include <mt6853_cond.h>
#include <mt6853_spm_comm.h>
/* Determine for node route */
#define MT_LP_RQ_NODE "/sys/kernel/debug/spm/spm_resource_req"
#define DEFINE_ATTR_RO(_name) \
static struct kobj_attribute _name##_attr = { \
.attr = { \
.name = #_name, \
.mode = 0444, \
}, \
.show = _name##_show, \
}
#define DEFINE_ATTR_RW(_name) \
static struct kobj_attribute _name##_attr = { \
.attr = { \
.name = #_name, \
.mode = 0644, \
}, \
.show = _name##_show, \
.store = _name##_store, \
}
#define __ATTR_OF(_name) (&_name##_attr.attr)
#undef mtk_dbg_spm_log
#define mtk_dbg_spm_log(fmt, args...) \
do { \
int l = scnprintf(p, sz, fmt, ##args); \
p += l; \
sz -= l; \
} while (0)
static char *mt6853_pwr_ctrl_str[PW_MAX_COUNT] = {
[PW_PCM_FLAGS] = "pcm_flags",
[PW_PCM_FLAGS_CUST] = "pcm_flags_cust",
[PW_PCM_FLAGS_CUST_SET] = "pcm_flags_cust_set",
[PW_PCM_FLAGS_CUST_CLR] = "pcm_flags_cust_clr",
[PW_PCM_FLAGS1] = "pcm_flags1",
[PW_PCM_FLAGS1_CUST] = "pcm_flags1_cust",
[PW_PCM_FLAGS1_CUST_SET] = "pcm_flags1_cust_set",
[PW_PCM_FLAGS1_CUST_CLR] = "pcm_flags1_cust_clr",
[PW_TIMER_VAL] = "timer_val",
[PW_TIMER_VAL_CUST] = "timer_val_cust",
[PW_TIMER_VAL_RAMP_EN] = "timer_val_ramp_en",
[PW_TIMER_VAL_RAMP_EN_SEC] = "timer_val_ramp_en_sec",
[PW_WAKE_SRC] = "wake_src",
[PW_WAKE_SRC_CUST] = "wake_src_cust",
[PW_WAKELOCK_TIMER_VAL] = "wakelock_timer_val",
[PW_WDT_DISABLE] = "wdt_disable",
/* SPM_CLK_CON */
[PW_REG_SRCCLKEN0_CTL] = "reg_srcclken0_ctl",
[PW_REG_SRCCLKEN1_CTL] = "reg_srcclken1_ctl",
[PW_REG_SPM_LOCK_INFRA_DCM] = "reg_spm_lock_infra_dcm",
[PW_REG_SRCCLKEN_MASK] = "reg_srcclken_mask",
[PW_REG_MD1_C32RM_EN] = "reg_md1_c32rm_en",
[PW_REG_MD2_C32RM_EN] = "reg_md2_c32rm_en",
[PW_REG_CLKSQ0_SEL_CTRL] = "reg_clksq0_sel_ctrl",
[PW_REG_CLKSQ1_SEL_CTRL] = "reg_clksq1_sel_ctrl",
[PW_REG_SRCCLKEN0_EN] = "reg_srcclken0_en",
[PW_REG_SRCCLKEN1_EN] = "reg_srcclken1_en",
[PW_REG_SYSCLK0_SRC_MASK_B] = "reg_sysclk0_src_mask_b",
[PW_REG_SYSCLK1_SRC_MASK_B] = "reg_sysclk1_src_mask_b",
/* SPM_AP_STANDBY_CON */
[PW_REG_WFI_OP] = "reg_wfi_op",
[PW_REG_WFI_TYPE] = "reg_wfi_type",
[PW_REG_MP0_CPUTOP_IDLE_MASK] = "reg_mp0_cputop_idle_mask",
[PW_REG_MP1_CPUTOP_IDLE_MASK] = "reg_mp1_cputop_idle_mask",
[PW_REG_MCUSYS_IDLE_MASK] = "reg_mcusys_idle_mask",
[PW_REG_MD_APSRC_1_SEL] = "reg_md_apsrc_1_sel",
[PW_REG_MD_APSRC_0_SEL] = "reg_md_apsrc_0_sel",
[PW_REG_CONN_APSRC_SEL] = "reg_conn_apsrc_sel",
/* SPM_SRC6_MASK */
[PW_REG_CCIF_EVENT_INFRA_REQ_MASK_B] =
"reg_ccif_event_infra_req_mask_b",
[PW_REG_CCIF_EVENT_APSRC_REQ_MASK_B] =
"reg_ccif_event_apsrc_req_mask_b",
/* SPM_SRC_REQ */
[PW_REG_SPM_APSRC_REQ] = "reg_spm_apsrc_req",
[PW_REG_SPM_F26M_REQ] = "reg_spm_f26m_req",
[PW_REG_SPM_INFRA_REQ] = "reg_spm_infra_req",
[PW_REG_SPM_VRF18_REQ] = "reg_spm_vrf18_req",
[PW_REG_SPM_DDREN_REQ] = "reg_spm_ddren_req",
[PW_REG_SPM_DVFS_REQ] = "reg_spm_dvfs_req",
[PW_REG_SPM_SW_MAILBOX_REQ] = "reg_spm_sw_mailbox_req",
[PW_REG_SPM_SSPM_MAILBOX_REQ] = "reg_spm_sspm_mailbox_req",
[PW_REG_SPM_ADSP_MAILBOX_REQ] = "reg_spm_adsp_mailbox_req",
[PW_REG_SPM_SCP_MAILBOX_REQ] = "reg_spm_scp_mailbox_req",
/* SPM_SRC_MASK */
[PW_REG_MD_0_SRCCLKENA_MASK_B] = "reg_md_0_srcclkena_mask_b",
[PW_REG_MD_0_INFRA_REQ_MASK_B] = "reg_md_0_infra_req_mask_b",
[PW_REG_MD_0_APSRC_REQ_MASK_B] = "reg_md_0_apsrc_req_mask_b",
[PW_REG_MD_0_VRF18_REQ_MASK_B] = "reg_md_0_vrf18_req_mask_b",
[PW_REG_MD_0_DDREN_REQ_MASK_B] = "reg_md_0_ddren_req_mask_b",
[PW_REG_MD_1_SRCCLKENA_MASK_B] = "reg_md_1_srcclkena_mask_b",
[PW_REG_MD_1_INFRA_REQ_MASK_B] = "reg_md_1_infra_req_mask_b",
[PW_REG_MD_1_APSRC_REQ_MASK_B] = "reg_md_1_apsrc_req_mask_b",
[PW_REG_MD_1_VRF18_REQ_MASK_B] = "reg_md_1_vrf18_req_mask_b",
[PW_REG_MD_1_DDREN_REQ_MASK_B] = "reg_md_1_ddren_req_mask_b",
[PW_REG_CONN_SRCCLKENA_MASK_B] = "reg_conn_srcclkena_mask_b",
[PW_REG_CONN_SRCCLKENB_MASK_B] = "reg_conn_srcclkenb_mask_b",
[PW_REG_CONN_INFRA_REQ_MASK_B] = "reg_conn_infra_req_mask_b",
[PW_REG_CONN_APSRC_REQ_MASK_B] = "reg_conn_apsrc_req_mask_b",
[PW_REG_CONN_VRF18_REQ_MASK_B] = "reg_conn_vrf18_req_mask_b",
[PW_REG_CONN_DDREN_REQ_MASK_B] = "reg_conn_ddren_req_mask_b",
[PW_REG_CONN_VFE28_MASK_B] = "reg_conn_vfe28_mask_b",
[PW_REG_SRCCLKENI_SRCCLKENA_MASK_B] = "reg_srcclkeni_srcclkena_mask_b",
[PW_REG_SRCCLKENI_INFRA_REQ_MASK_B] = "reg_srcclkeni_infra_req_mask_b",
[PW_REG_INFRASYS_APSRC_REQ_MASK_B] = "reg_infrasys_apsrc_req_mask_b",
[PW_REG_INFRASYS_DDREN_REQ_MASK_B] = "reg_infrasys_ddren_req_mask_b",
[PW_REG_SSPM_SRCCLKENA_MASK_B] = "reg_sspm_srcclkena_mask_b",
[PW_REG_SSPM_INFRA_REQ_MASK_B] = "reg_sspm_infra_req_mask_b",
[PW_REG_SSPM_APSRC_REQ_MASK_B] = "reg_sspm_apsrc_req_mask_b",
[PW_REG_SSPM_VRF18_REQ_MASK_B] = "reg_sspm_vrf18_req_mask_b",
[PW_REG_SSPM_DDREN_REQ_MASK_B] = "reg_sspm_ddren_req_mask_b",
/* SPM_SRC2_MASK */
[PW_REG_SCP_SRCCLKENA_MASK_B] = "reg_scp_srcclkena_mask_b",
[PW_REG_SCP_INFRA_REQ_MASK_B] = "reg_scp_infra_req_mask_b",
[PW_REG_SCP_APSRC_REQ_MASK_B] = "reg_scp_apsrc_req_mask_b",
[PW_REG_SCP_VRF18_REQ_MASK_B] = "reg_scp_vrf18_req_mask_b",
[PW_REG_SCP_DDREN_REQ_MASK_B] = "reg_scp_ddren_req_mask_b",
[PW_REG_AUDIO_DSP_SRCCLKENA_MASK_B] = "reg_audio_dsp_srcclkena_mask_b",
[PW_REG_AUDIO_DSP_INFRA_REQ_MASK_B] = "reg_audio_dsp_infra_req_mask_b",
[PW_REG_AUDIO_DSP_APSRC_REQ_MASK_B] = "reg_audio_dsp_apsrc_req_mask_b",
[PW_REG_AUDIO_DSP_VRF18_REQ_MASK_B] = "reg_audio_dsp_vrf18_req_mask_b",
[PW_REG_AUDIO_DSP_DDREN_REQ_MASK_B] = "reg_audio_dsp_ddren_req_mask_b",
[PW_REG_UFS_SRCCLKENA_MASK_B] = "reg_ufs_srcclkena_mask_b",
[PW_REG_UFS_INFRA_REQ_MASK_B] = "reg_ufs_infra_req_mask_b",
[PW_REG_UFS_APSRC_REQ_MASK_B] = "reg_ufs_apsrc_req_mask_b",
[PW_REG_UFS_VRF18_REQ_MASK_B] = "reg_ufs_vrf18_req_mask_b",
[PW_REG_UFS_DDREN_REQ_MASK_B] = "reg_ufs_ddren_req_mask_b",
[PW_REG_DISP0_APSRC_REQ_MASK_B] = "reg_disp0_apsrc_req_mask_b",
[PW_REG_DISP0_DDREN_REQ_MASK_B] = "reg_disp0_ddren_req_mask_b",
[PW_REG_DISP1_APSRC_REQ_MASK_B] = "reg_disp1_apsrc_req_mask_b",
[PW_REG_DISP1_DDREN_REQ_MASK_B] = "reg_disp1_ddren_req_mask_b",
[PW_REG_GCE_INFRA_REQ_MASK_B] = "reg_gce_infra_req_mask_b",
[PW_REG_GCE_APSRC_REQ_MASK_B] = "reg_gce_apsrc_req_mask_b",
[PW_REG_GCE_VRF18_REQ_MASK_B] = "reg_gce_vrf18_req_mask_b",
[PW_REG_GCE_DDREN_REQ_MASK_B] = "reg_gce_ddren_req_mask_b",
[PW_REG_APU_SRCCLKENA_MASK_B] = "reg_apu_srcclkena_mask_b",
[PW_REG_APU_INFRA_REQ_MASK_B] = "reg_apu_infra_req_mask_b",
[PW_REG_APU_APSRC_REQ_MASK_B] = "reg_apu_apsrc_req_mask_b",
[PW_REG_APU_VRF18_REQ_MASK_B] = "reg_apu_vrf18_req_mask_b",
[PW_REG_APU_DDREN_REQ_MASK_B] = "reg_apu_ddren_req_mask_b",
[PW_REG_CG_CHECK_SRCCLKENA_MASK_B] = "reg_cg_check_srcclkena_mask_b",
[PW_REG_CG_CHECK_APSRC_REQ_MASK_B] = "reg_cg_check_apsrc_req_mask_b",
[PW_REG_CG_CHECK_VRF18_REQ_MASK_B] = "reg_cg_check_vrf18_req_mask_b",
[PW_REG_CG_CHECK_DDREN_REQ_MASK_B] = "reg_cg_check_ddren_req_mask_b",
/* SPM_SRC3_MASK */
[PW_REG_DVFSRC_EVENT_TRIGGER_MASK_B] =
"reg_dvfsrc_event_trigger_mask_b",
[PW_REG_SW2SPM_WAKEUP_MASK_B] = "reg_sw2spm_wakeup_mask_b",
[PW_REG_ADSP2SPM_WAKEUP_MASK_B] = "reg_adsp2spm_wakeup_mask_b",
[PW_REG_SSPM2SPM_WAKEUP_MASK_B] = "reg_sspm2spm_wakeup_mask_b",
[PW_REG_SCP2SPM_WAKEUP_MASK_B] = "reg_scp2spm_wakeup_mask_b",
[PW_REG_CSYSPWRUP_ACK_MASK] = "reg_csyspwrup_ack_mask",
[PW_REG_SPM_RESERVED_SRCCLKENA_MASK_B] =
"reg_spm_reserved_srcclkena_mask_b",
[PW_REG_SPM_RESERVED_INFRA_REQ_MASK_B] =
"reg_spm_reserved_infra_req_mask_b",
[PW_REG_SPM_RESERVED_APSRC_REQ_MASK_B] =
"reg_spm_reserved_apsrc_req_mask_b",
[PW_REG_SPM_RESERVED_VRF18_REQ_MASK_B] =
"reg_spm_reserved_vrf18_req_mask_b",
[PW_REG_SPM_RESERVED_DDREN_REQ_MASK_B] =
"reg_spm_reserved_ddren_req_mask_b",
[PW_REG_MCUPM_SRCCLKENA_MASK_B] = "reg_mcupm_srcclkena_mask_b",
[PW_REG_MCUPM_INFRA_REQ_MASK_B] = "reg_mcupm_infra_req_mask_b",
[PW_REG_MCUPM_APSRC_REQ_MASK_B] = "reg_mcupm_apsrc_req_mask_b",
[PW_REG_MCUPM_VRF18_REQ_MASK_B] = "reg_mcupm_vrf18_req_mask_b",
[PW_REG_MCUPM_DDREN_REQ_MASK_B] = "reg_mcupm_ddren_req_mask_b",
[PW_REG_MSDC0_SRCCLKENA_MASK_B] = "reg_msdc0_srcclkena_mask_b",
[PW_REG_MSDC0_INFRA_REQ_MASK_B] = "reg_msdc0_infra_req_mask_b",
[PW_REG_MSDC0_APSRC_REQ_MASK_B] = "reg_msdc0_apsrc_req_mask_b",
[PW_REG_MSDC0_VRF18_REQ_MASK_B] = "reg_msdc0_vrf18_req_mask_b",
[PW_REG_MSDC0_DDREN_REQ_MASK_B] = "reg_msdc0_ddren_req_mask_b",
[PW_REG_MSDC1_SRCCLKENA_MASK_B] = "reg_msdc1_srcclkena_mask_b",
[PW_REG_MSDC1_INFRA_REQ_MASK_B] = "reg_msdc1_infra_req_mask_b",
[PW_REG_MSDC1_APSRC_REQ_MASK_B] = "reg_msdc1_apsrc_req_mask_b",
[PW_REG_MSDC1_VRF18_REQ_MASK_B] = "reg_msdc1_vrf18_req_mask_b",
[PW_REG_MSDC1_DDREN_REQ_MASK_B] = "reg_msdc1_ddren_req_mask_b",
/* SPM_SRC4_MASK */
[PW_REG_CCIF_EVENT_SRCCLKENA_MASK_B] =
"reg_ccif_event_srcclkena_mask_b",
[PW_REG_BAK_PSRI_SRCCLKENA_MASK_B] = "reg_bak_psri_srcclkena_mask_b",
[PW_REG_BAK_PSRI_INFRA_REQ_MASK_B] = "reg_bak_psri_infra_req_mask_b",
[PW_REG_BAK_PSRI_APSRC_REQ_MASK_B] = "reg_bak_psri_apsrc_req_mask_b",
[PW_REG_BAK_PSRI_VRF18_REQ_MASK_B] = "reg_bak_psri_vrf18_req_mask_b",
[PW_REG_BAK_PSRI_DDREN_REQ_MASK_B] = "reg_bak_psri_ddren_req_mask_b",
[PW_REG_DRAMC_MD32_INFRA_REQ_MASK_B] =
"reg_dramc_md32_infra_req_mask_b",
[PW_REG_DRAMC_MD32_VRF18_REQ_MASK_B] =
"reg_dramc_md32_vrf18_req_mask_b",
[PW_REG_CONN_SRCCLKENB2PWRAP_MASK_B] =
"reg_conn_srcclkenb2pwrap_mask_b",
[PW_REG_DRAMC_MD32_APSRC_REQ_MASK_B] =
"reg_dramc_md32_apsrc_req_mask_b",
/* SPM_SRC5_MASK */
[PW_REG_MCUSYS_MERGE_APSRC_REQ_MASK_B] =
"reg_mcusys_merge_apsrc_req_mask_b",
[PW_REG_MCUSYS_MERGE_DDREN_REQ_MASK_B] =
"reg_mcusys_merge_ddren_req_mask_b",
[PW_REG_AFE_SRCCLKENA_MASK_B] = "reg_afe_srcclkena_mask_b",
[PW_REG_AFE_INFRA_REQ_MASK_B] = "reg_afe_infra_req_mask_b",
[PW_REG_AFE_APSRC_REQ_MASK_B] = "reg_afe_apsrc_req_mask_b",
[PW_REG_AFE_VRF18_REQ_MASK_B] = "reg_afe_vrf18_req_mask_b",
[PW_REG_AFE_DDREN_REQ_MASK_B] = "reg_afe_ddren_req_mask_b",
[PW_REG_MSDC2_SRCCLKENA_MASK_B] = "reg_msdc2_srcclkena_mask_b",
[PW_REG_MSDC2_INFRA_REQ_MASK_B] = "reg_msdc2_infra_req_mask_b",
[PW_REG_MSDC2_APSRC_REQ_MASK_B] = "reg_msdc2_apsrc_req_mask_b",
[PW_REG_MSDC2_VRF18_REQ_MASK_B] = "reg_msdc2_vrf18_req_mask_b",
[PW_REG_MSDC2_DDREN_REQ_MASK_B] = "reg_msdc2_ddren_req_mask_b",
/* SPM_SRC7_MASK */
[PW_REG_PCIE_SRCCLKENA_MASK_B] = "reg_pcie_srcclkena_mask_b",
[PW_REG_PCIE_INFRA_REQ_MASK_B] = "reg_pcie_infra_req_mask_b",
[PW_REG_PCIE_APSRC_REQ_MASK_B] = "reg_pcie_apsrc_req_mask_b",
[PW_REG_PCIE_VRF18_REQ_MASK_B] = "reg_pcie_vrf18_req_mask_b",
[PW_REG_PCIE_DDREN_REQ_MASK_B] = "reg_pcie_ddren_req_mask_b",
[PW_REG_DPMAIF_SRCCLKENA_MASK_B] = "reg_dpmaif_srcclkena_mask_b",
[PW_REG_DPMAIF_INFRA_REQ_MASK_B] = "reg_dpmaif_infra_req_mask_b",
[PW_REG_DPMAIF_APSRC_REQ_MASK_B] = "reg_dpmaif_apsrc_req_mask_b",
[PW_REG_DPMAIF_VRF18_REQ_MASK_B] = "reg_dpmaif_vrf18_req_mask_b",
[PW_REG_DPMAIF_DDREN_REQ_MASK_B] = "reg_dpmaif_ddren_req_mask_b",
/* SPM_WAKEUP_EVENT_MASK */
[PW_REG_WAKEUP_EVENT_MASK] = "reg_wakeup_event_mask",
/* SPM_WAKEUP_EVENT_EXT_MASK */
[PW_REG_EXT_WAKEUP_EVENT_MASK] = "reg_ext_wakeup_event_mask",
};
/**************************************
* xxx_ctrl_show Function
**************************************/
/* code gen by spm_pwr_ctrl_atf.pl, need struct pwr_ctrl */
static ssize_t mt6853_show_pwr_ctrl(int id, char *buf, size_t buf_sz)
{
char *p = buf;
size_t mSize = 0;
int i;
for (i = 0; i < PW_MAX_COUNT; i++) {
mSize += scnprintf(p + mSize, buf_sz - mSize,
"%s = 0x%zx\n",
mt6853_pwr_ctrl_str[i],
mtk_lpm_smc_spm_dbg(id, MT_LPM_SMC_ACT_GET, i, 0));
}
WARN_ON(buf_sz - mSize <= 0);
return mSize;
}
/**************************************
* xxx_ctrl_store Function
**************************************/
/* code gen by spm_pwr_ctrl_atf.pl, need struct pwr_ctrl */
static ssize_t mt6853_store_pwr_ctrl(int id, const char *buf, size_t count)
{
u32 val;
char cmd[64];
int i;
if (sscanf(buf, "%63s %x", cmd, &val) != 2)
return -EPERM;
pr_info("[SPM] pwr_ctrl: cmd = %s, val = 0x%x\n", cmd, val);
for (i = 0 ; i < PW_MAX_COUNT; i++) {
if (!strcmp(cmd, mt6853_pwr_ctrl_str[i])) {
mtk_lpm_smc_spm_dbg(id, MT_LPM_SMC_ACT_SET, i, val);
break;
}
}
return count;
}
static ssize_t suspend_ctrl_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return mt6853_show_pwr_ctrl(MT_SPM_DBG_SMC_UID_SUSPEND_PWR_CTRL,
buf, get_mtk_lp_kernfs_bufsz_max());
}
static ssize_t idle_ctrl_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return mt6853_show_pwr_ctrl(MT_SPM_DBG_SMC_UID_IDLE_PWR_CTRL,
buf, get_mtk_lp_kernfs_bufsz_max());
}
static ssize_t suspend_ctrl_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
return mt6853_store_pwr_ctrl(MT_SPM_DBG_SMC_UID_SUSPEND_PWR_CTRL,
buf, count);
}
static ssize_t idle_ctrl_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
return mt6853_store_pwr_ctrl(MT_SPM_DBG_SMC_UID_IDLE_PWR_CTRL,
buf, count);
}
/**************************************
* Init Function
**************************************/
DEFINE_ATTR_RW(suspend_ctrl);
DEFINE_ATTR_RW(idle_ctrl);
static struct attribute *pwrctrl_attrs[] = {
/* for spm_lp_scen.pwrctrl */
__ATTR_OF(suspend_ctrl),
__ATTR_OF(idle_ctrl),
/* must */
NULL,
};
static struct attribute_group pwrctrl_attr_group = {
.name = "spm",
.attrs = pwrctrl_attrs,
};
static char *mt6853_spm_resource_str[MT_SPM_RES_MAX] = {
[MT_SPM_RES_XO_FPM] = "XO_FPM",
[MT_SPM_RES_CK_26M] = "CK_26M",
[MT_SPM_RES_INFRA] = "INFRA",
[MT_SPM_RES_SYSPLL] = "SYSPLL",
[MT_SPM_RES_DRAM_S0] = "DRAM_S0",
[MT_SPM_RES_DRAM_S1] = "DRAM_S1",
};
static ssize_t mt6853_spm_res_rq_read(char *ToUserBuf, size_t sz, void *priv)
{
char *p = ToUserBuf;
int i, s, u;
unsigned int unum, uvalid, uname_i, uname_t;
unsigned int rnum, rusage, per_usage;
char uname[MT_LP_RQ_USER_NAME_LEN+1];
mtk_dbg_spm_log("resource_num=%d, user_num=%d, user_valid=0x%x\n",
rnum = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_NUM,
MT_LPM_SMC_ACT_GET, 0, 0),
unum = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_NUM,
MT_LPM_SMC_ACT_GET, 0, 0),
uvalid = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_VALID,
MT_LPM_SMC_ACT_GET, 0, 0));
rusage = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USAGE,
MT_LPM_SMC_ACT_GET,
MT_LP_RQ_ID_ALL_USAGE, 0);
mtk_dbg_spm_log("\n");
mtk_dbg_spm_log("user [bit][valid]:\n");
for (i = 0; i < unum; i++) {
uname_i = mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_NAME,
MT_LPM_SMC_ACT_GET, i, 0);
for (s = 0, u = 0; s < MT_LP_RQ_USER_NAME_LEN;
s++, u += MT_LP_RQ_USER_CHAR_U) {
uname_t = ((uname_i >> u) & MT_LP_RQ_USER_CHAR_MASK);
uname[s] = (uname_t) ? (char)uname_t : ' ';
}
uname[s] = '\0';
mtk_dbg_spm_log("%4s [%3d][%3s]\n", uname, i,
((1<<i) & uvalid) ? "yes" : "no");
}
mtk_dbg_spm_log("\n");
if (rnum != MT_SPM_RES_MAX) {
mtk_dbg_spm_log("Platform resource amount mismatch\n");
rnum = (rnum > MT_SPM_RES_MAX) ? MT_SPM_RES_MAX : rnum;
}
mtk_dbg_spm_log("resource [bit][user_usage][blocking]:\n");
for (i = 0; i < rnum; i++) {
mtk_dbg_spm_log("%8s [%3d][0x%08x][%3s]\n",
mt6853_spm_resource_str[i], i,
(per_usage =
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USAGE,
MT_LPM_SMC_ACT_GET, i, 0)),
((1<<i) & rusage) ? "yes" : "no"
);
}
mtk_dbg_spm_log("\n");
mtk_dbg_spm_log("resource request command help:\n");
mtk_dbg_spm_log("echo enable ${user_bit} > %s\n", MT_LP_RQ_NODE);
mtk_dbg_spm_log("echo bypass ${user_bit} > %s\n", MT_LP_RQ_NODE);
mtk_dbg_spm_log("echo request ${resource_bit} > %s\n", MT_LP_RQ_NODE);
mtk_dbg_spm_log("echo release > %s\n", MT_LP_RQ_NODE);
return p - ToUserBuf;
}
static ssize_t mt6853_spm_res_rq_write(char *FromUserBuf, size_t sz, void *priv)
{
char cmd[128];
int parm;
if (sscanf(FromUserBuf, "%127s %x", cmd, &parm) == 2) {
if (!strcmp(cmd, "bypass"))
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_VALID,
MT_LPM_SMC_ACT_SET,
parm, 0);
else if (!strcmp(cmd, "enable"))
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_USER_VALID,
MT_LPM_SMC_ACT_SET,
parm, 1);
else if (!strcmp(cmd, "request"))
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_REQ,
MT_LPM_SMC_ACT_SET,
0, parm);
return sz;
} else if (sscanf(FromUserBuf, "%127s", cmd) == 1) {
if (!strcmp(cmd, "release"))
mtk_lpm_smc_spm_dbg(MT_SPM_DBG_SMC_UID_RES_REQ,
MT_LPM_SMC_ACT_CLR,
0, 0);
return sz;
} else if ((!kstrtoint(FromUserBuf, 10, &parm)) == 1) {
return sz;
}
return -EINVAL;
}
static const struct mtk_lp_sysfs_op mt6853_spm_res_rq_fops = {
.fs_read = mt6853_spm_res_rq_read,
.fs_write = mt6853_spm_res_rq_write,
};
int mt6853_dbg_spm_fs_init(void)
{
int r;
mtk_spm_sysfs_root_entry_create();
mtk_spm_sysfs_entry_node_add("spm_resource_req", 0444
, &mt6853_spm_res_rq_fops, NULL);
/* create /sys/power/spm/xxx */
r = mtk_spm_sysfs_power_create_group(&pwrctrl_attr_group);
if (r)
pr_info("[SPM] FAILED TO CREATE /sys/power/spm (%d)\n", r);
return r;
}
int mt6853_dbg_spm_fs_deinit(void)
{
return 0;
}
@@ -0,0 +1,847 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/rtc.h>
#include <linux/wakeup_reason.h>
#include <linux/syscore_ops.h>
#include <mt6853_cond.h>
#include <mtk_lpm_module.h>
#include <aee.h>
#include <mtk_lpm.h>
#include <mt6853_spm_comm.h>
#include <mt6853_spm_reg.h>
#include <mt6853_pwr_ctrl.h>
#include <mt6853_pcm_def.h>
#include <mtk_dbg_common_v1.h>
#include <mt-plat/mtk_ccci_common.h>
#include <mtk_lpm_timer.h>
#include <mtk_lpm_sysfs.h>
#define MT6853_LOG_MONITOR_STATE_NAME "mcusysoff"
#define MT6853_LOG_DEFAULT_MS 5000
#define PCM_32K_TICKS_PER_SEC (32768)
#define PCM_TICK_TO_SEC(TICK) (TICK / PCM_32K_TICKS_PER_SEC)
static struct mt6853_spm_wake_status mt6853_wake;
void __iomem *mt6853_spm_base;
struct mt6853_log_helper {
short cur;
short prev;
struct mt6853_spm_wake_status *wakesrc;
};
struct mt6853_log_helper mt6853_logger_help = {
.wakesrc = &mt6853_wake,
.cur = 0,
.prev = 0,
};
static char *mt6853_spm_cond_cg_str[PLAT_SPM_COND_MAX] = {
[PLAT_SPM_COND_MTCMOS_0] = "MTCMOS_0",
[PLAT_SPM_COND_CG_INFRA_0] = "INFRA_0",
[PLAT_SPM_COND_CG_INFRA_1] = "INFRA_1",
[PLAT_SPM_COND_CG_INFRA_2] = "INFRA_2",
[PLAT_SPM_COND_CG_INFRA_3] = "INFRA_3",
[PLAT_SPM_COND_CG_INFRA_4] = "INFRA_4",
[PLAT_SPM_COND_CG_INFRA_5] = "INFRA_5",
[PLAT_SPM_COND_CG_MMSYS_0] = "MMSYS_0",
[PLAT_SPM_COND_CG_MMSYS_1] = "MMSYS_1",
[PLAT_SPM_COND_CG_MMSYS_2] = "MMSYS_2",
};
const char *mt6853_wakesrc_str[32] = {
[0] = " R12_PCM_TIMER",
[1] = " R12_RESERVED_DEBUG_B",
[2] = " R12_KP_IRQ_B",
[3] = " R12_APWDT_EVENT_B",
[4] = " R12_APXGPT1_EVENT_B",
[5] = " R12_CONN2AP_SPM_WAKEUP_B",
[6] = " R12_EINT_EVENT_B",
[7] = " R12_CONN_WDT_IRQ_B",
[8] = " R12_CCIF0_EVENT_B",
[9] = " R12_LOWBATTERY_IRQ_B",
[10] = " R12_SC_SSPM2SPM_WAKEUP_B",
[11] = " R12_SC_SCP2SPM_WAKEUP_B",
[12] = " R12_SC_ADSP2SPM_WAKEUP_B",
[13] = " R12_PCM_WDT_WAKEUP_B",
[14] = " R12_USB_CDSC_B",
[15] = " R12_USB_POWERDWN_B",
[16] = " R12_SYS_TIMER_EVENT_B",
[17] = " R12_EINT_EVENT_SECURE_B",
[18] = " R12_CCIF1_EVENT_B",
[19] = " R12_UART0_IRQ_B",
[20] = " R12_AFE_IRQ_MCU_B",
[21] = " R12_THERM_CTRL_EVENT_B",
[22] = " R12_SYS_CIRQ_IRQ_B",
[23] = " R12_MD2AP_PEER_EVENT_B",
[24] = " R12_CSYSPWREQ_B",
[25] = " R12_MD1_WDT_B",
[26] = " R12_AP2AP_PEER_WAKEUPEVENT_B",
[27] = " R12_SEJ_EVENT_B",
[28] = " R12_SPM_CPU_WAKEUPEVENT_B",
[29] = " R12_APUSYS",
[30] = " R12_PCIE_BRIDGE_IRQ",
[31] = " R12_PCIE_IRQ",
};
struct spm_wakesrc_irq_list mt6853_spm_wakesrc_irqs[] = {
/* mtk-kpd */
{ WAKE_SRC_STA1_KP_IRQ_B, "mediatek,kp", 0, 0},
/* mt_wdt */
{ WAKE_SRC_STA1_APWDT_EVENT_B, "mediatek,toprgu", 0, 0},
/* bt_cvsd_int */
{ WAKE_SRC_STA1_CONN2AP_SPM_WAKEUP_B, "mediatek,mtk-btcvsd-snd", 0, 0},
/* wf_hif_int */
{ WAKE_SRC_STA1_CONN2AP_SPM_WAKEUP_B, "mediatek,wifi", 0, 0},
/* conn2ap_btif_wakeup_out */
{ WAKE_SRC_STA1_CONN2AP_SPM_WAKEUP_B, "mediatek,mt6853-consys", 0, 0},
/* conn2ap_sw_irq */
{ WAKE_SRC_STA1_CONN2AP_SPM_WAKEUP_B, "mediatek,mt6853-consys", 2, 0},
/* CCIF_AP_DATA */
{ WAKE_SRC_STA1_CCIF0_EVENT_B, "mediatek,ap_ccif0", 0, 0},
/* SCP IPC0 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 0, 0},
/* SCP IPC1 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 1, 0},
/* MBOX_ISR0 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 2, 0},
/* MBOX_ISR1 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 3, 0},
/* MBOX_ISR2 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 4, 0},
/* MBOX_ISR3 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 5, 0},
/* MBOX_ISR4 */
{ WAKE_SRC_STA1_SC_SCP2SPM_WAKEUP_B, "mediatek,scp", 6, 0},
/* ADSP_A_AUD */
{ WAKE_SRC_STA1_SC_ADSP2SPM_WAKEUP_B, "mediatek,adsp_core_0", 2, 0},
/* ADSP_B_AUD */
{ WAKE_SRC_STA1_SC_ADSP2SPM_WAKEUP_B, "mediatek,adsp_core_1", 2, 0},
/* CCIF0_AP */
{ WAKE_SRC_STA1_MD1_WDT_B, "mediatek,mddriver", 2, 0},
/* DPMAIF_AP */
{ WAKE_SRC_STA1_AP2AP_PEER_WAKEUPEVENT_B, "mediatek,dpmaif", 0, 0},
};
#define plat_mmio_read(offset) __raw_readl(mt6853_spm_base + offset)
u64 ap_pd_count;
u64 ap_slp_duration;
u64 spm_26M_off_count;
u64 spm_26M_off_duration;
u32 before_ap_slp_duration;
#define IRQ_NUMBER \
(sizeof(mt6853_spm_wakesrc_irqs)/sizeof(struct spm_wakesrc_irq_list))
static void mt6853_get_spm_wakesrc_irq(void)
{
int i;
struct device_node *node = NULL;
for (i = 0; i < IRQ_NUMBER; i++) {
if (mt6853_spm_wakesrc_irqs[i].name == NULL)
continue;
node = of_find_compatible_node(NULL, NULL,
mt6853_spm_wakesrc_irqs[i].name);
if (!node) {
pr_info("[name:spm&][SPM] find '%s' node failed\n",
mt6853_spm_wakesrc_irqs[i].name);
continue;
}
mt6853_spm_wakesrc_irqs[i].irq_no =
irq_of_parse_and_map(node,
mt6853_spm_wakesrc_irqs[i].order);
if (!mt6853_spm_wakesrc_irqs[i].irq_no) {
pr_info("[name:spm&][SPM] get '%s' failed\n",
mt6853_spm_wakesrc_irqs[i].name);
}
}
}
struct mt6853_logger_timer {
struct mt_lpm_timer tm;
unsigned int fired;
};
struct mt6853_logger_fired_info {
unsigned int fired;
int state_index;
};
static struct mt6853_logger_timer mt6853_log_timer;
static struct mt6853_logger_fired_info mt6853_logger_fired;
int mt6853_get_wakeup_status(struct mt6853_log_helper *help)
{
if (!help->wakesrc || !mt6853_spm_base)
return -EINVAL;
help->wakesrc->r12 = plat_mmio_read(SPM_BK_WAKE_EVENT);
help->wakesrc->r12_ext = plat_mmio_read(SPM_WAKEUP_STA);
help->wakesrc->raw_sta = plat_mmio_read(SPM_WAKEUP_STA);
help->wakesrc->raw_ext_sta = plat_mmio_read(SPM_WAKEUP_EXT_STA);
help->wakesrc->md32pcm_wakeup_sta = plat_mmio_read(MD32PCM_WAKEUP_STA);
help->wakesrc->md32pcm_event_sta = plat_mmio_read(MD32PCM_EVENT_STA);
help->wakesrc->src_req = plat_mmio_read(SPM_SRC_REQ);
/* backup of SPM_WAKEUP_MISC */
help->wakesrc->wake_misc = plat_mmio_read(SPM_BK_WAKE_MISC);
/* get sleep time */
/* backup of PCM_TIMER_OUT */
help->wakesrc->timer_out = plat_mmio_read(SPM_BK_PCM_TIMER);
/* get other SYS and co-clock status */
help->wakesrc->r13 = plat_mmio_read(PCM_REG13_DATA);
help->wakesrc->idle_sta = plat_mmio_read(SUBSYS_IDLE_STA);
help->wakesrc->req_sta0 = plat_mmio_read(SRC_REQ_STA_0);
help->wakesrc->req_sta1 = plat_mmio_read(SRC_REQ_STA_1);
help->wakesrc->req_sta2 = plat_mmio_read(SRC_REQ_STA_2);
help->wakesrc->req_sta3 = plat_mmio_read(SRC_REQ_STA_3);
help->wakesrc->req_sta4 = plat_mmio_read(SRC_REQ_STA_4);
/* get HW CG check status */
help->wakesrc->cg_check_sta = plat_mmio_read(SPM_CG_CHECK_STA);
/* get debug flag for PCM execution check */
help->wakesrc->debug_flag = plat_mmio_read(PCM_WDT_LATCH_SPARE_0);
help->wakesrc->debug_flag1 = plat_mmio_read(PCM_WDT_LATCH_SPARE_1);
/* get backup SW flag status */
help->wakesrc->b_sw_flag0 = plat_mmio_read(SPM_SW_RSV_7);
help->wakesrc->b_sw_flag1 = plat_mmio_read(SPM_SW_RSV_8);
help->wakesrc->rt_req_sta0 = plat_mmio_read(SPM_SW_RSV_2);
help->wakesrc->rt_req_sta1 = plat_mmio_read(SPM_SW_RSV_3);
help->wakesrc->rt_req_sta2 = plat_mmio_read(SPM_SW_RSV_4);
help->wakesrc->rt_req_sta3 = plat_mmio_read(SPM_SW_RSV_5);
help->wakesrc->rt_req_sta4 = plat_mmio_read(SPM_SW_RSV_6);
/* get ISR status */
help->wakesrc->isr = plat_mmio_read(SPM_IRQ_STA);
/* get SW flag status */
help->wakesrc->sw_flag0 = plat_mmio_read(SPM_SW_FLAG_0);
help->wakesrc->sw_flag1 = plat_mmio_read(SPM_SW_FLAG_1);
/* get CLK SETTLE */
help->wakesrc->clk_settle = plat_mmio_read(SPM_CLK_SETTLE);
/* check abort */
help->wakesrc->is_abort =
help->wakesrc->debug_flag & DEBUG_ABORT_MASK;
help->wakesrc->is_abort |=
help->wakesrc->debug_flag1 & DEBUG_ABORT_MASK_1;
help->cur += 1;
return 0;
}
static void mt6853_save_sleep_info(void)
{
#define AVOID_OVERFLOW (0xFFFFFFFF00000000)
u32 off_26M_duration;
u32 slp_duration;
slp_duration = plat_mmio_read(SPM_BK_PCM_TIMER);
if (slp_duration == before_ap_slp_duration)
return;
/* Save ap off counter and duration */
if (ap_pd_count >= AVOID_OVERFLOW)
ap_pd_count = 0;
else
ap_pd_count++;
if (ap_slp_duration >= AVOID_OVERFLOW)
ap_slp_duration = 0;
else {
ap_slp_duration = ap_slp_duration + slp_duration;
before_ap_slp_duration = slp_duration;
}
/* Save 26M's off counter and duration */
if (spm_26M_off_duration >= AVOID_OVERFLOW)
spm_26M_off_duration = 0;
else {
off_26M_duration = plat_mmio_read(SPM_BK_VTCXO_DUR);
if (off_26M_duration == 0)
return;
spm_26M_off_duration = spm_26M_off_duration +
off_26M_duration;
}
if (spm_26M_off_count >= AVOID_OVERFLOW)
spm_26M_off_count = 0;
else
spm_26M_off_count = (plat_mmio_read(SPM_VTCXO_EVENT_COUNT_STA)
& 0xffff)
+ spm_26M_off_count;
}
static void mt6853_suspend_show_detailed_wakeup_reason
(struct mt6853_spm_wake_status *wakesta)
{
int i;
unsigned int irq_no;
#if !defined(CONFIG_FPGA_EARLY_PORTING)
#ifdef CONFIG_MTK_CCCI_DEVICES
exec_ccci_kern_func_by_md_id(0, ID_DUMP_MD_SLEEP_MODE,
NULL, 0);
#endif
#ifdef CONFIG_MTK_ECCCI_DRIVER
if (wakesta->r12 & R12_CLDMA_EVENT_B)
exec_ccci_kern_func_by_md_id(0, ID_GET_MD_WAKEUP_SRC,
NULL, 0);
if (wakesta->r12 & R12_MD2AP_PEER_EVENT_B)
exec_ccci_kern_func_by_md_id(0, ID_GET_MD_WAKEUP_SRC,
NULL, 0);
if (wakesta->r12 & R12_CCIF0_EVENT_B)
exec_ccci_kern_func_by_md_id(0, ID_GET_MD_WAKEUP_SRC,
NULL, 0);
if (wakesta->r12 & R12_CCIF1_EVENT_B)
exec_ccci_kern_func_by_md_id(2, ID_GET_MD_WAKEUP_SRC,
NULL, 0);
#endif
#endif
/* Check if pending irq happen and log them */
for (i = 0; i < IRQ_NUMBER; i++) {
if (mt6853_spm_wakesrc_irqs[i].name == NULL ||
!mt6853_spm_wakesrc_irqs[i].irq_no)
continue;
if (mt6853_spm_wakesrc_irqs[i].wakesrc & wakesta->r12) {
irq_no = mt6853_spm_wakesrc_irqs[i].irq_no;
if (mt_irq_get_pending(irq_no))
log_irq_wakeup_reason(irq_no);
}
}
}
static void dump_lp_cond(void)
{
#define mt6853_DBG_SMC(_id, _act, _rc, _param) ({\
(u32) mtk_lpm_smc_spm_dbg(_id, _act, _rc, _param); })
int i;
u32 blkcg;
for (i = 1 ; i < PLAT_SPM_COND_MAX ; i++) {
blkcg = mt6853_DBG_SMC(MT_SPM_DBG_SMC_UID_BLOCK_DETAIL, MT_LPM_SMC_ACT_GET, 0, i);
if (blkcg != 0)
printk_deferred("suspend warning: CG: %6s = 0x%08lx\n"
, mt6853_spm_cond_cg_str[i], blkcg);
}
}
static void mt6853_suspend_spm_rsc_req_check
(struct mt6853_spm_wake_status *wakesta)
{
#define LOG_BUF_SIZE 256
#define IS_BLOCKED_OVER_TIMES 10
#undef AVOID_OVERFLOW
#define AVOID_OVERFLOW (0xF0000000)
static u32 is_blocked_cnt;
char log_buf[LOG_BUF_SIZE] = { 0 };
int log_size = 0;
u32 is_no_blocked = 0;
u32 req_sta_0, req_sta_1, req_sta_4;
u32 src_req;
if (is_blocked_cnt >= AVOID_OVERFLOW)
is_blocked_cnt = 0;
/* Check if ever enter deepest System LPM */
is_no_blocked = wakesta->debug_flag & 0x2;
/* Check if System LPM ever is blocked over 10 times */
if (!is_no_blocked)
is_blocked_cnt++;
else
is_blocked_cnt = 0;
if (is_blocked_cnt < IS_BLOCKED_OVER_TIMES)
return;
/* Show who is blocking system LPM */
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size,
"suspend warning:(OneShot) System LPM is blocked by ");
req_sta_0 = plat_mmio_read(SRC_REQ_STA_0);
if (req_sta_0 & 0xFFF)
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "md ");
if (req_sta_0 & (0x3F << 12))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "conn ");
if (req_sta_0 & (0x7 << 18))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "nfc ");
if (req_sta_0 & (0xF << 26))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "disp ");
req_sta_1 = plat_mmio_read(SRC_REQ_STA_1);
if (req_sta_1 & 0x1F)
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "scp ");
if (req_sta_1 & (0x1F << 5))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "adsp ");
if (req_sta_1 & (0x1F << 10))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "ufs ");
if (req_sta_1 & (0xF << 15))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "gce ");
if (req_sta_1 & (0x3FF << 21))
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "msdc ");
req_sta_4 = plat_mmio_read(SRC_REQ_STA_4);
if (req_sta_4 & 0x1F)
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "apu ");
src_req = plat_mmio_read(SPM_SRC_REQ);
if (src_req & 0x9B) {
dump_lp_cond();
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "spm ");
}
WARN_ON(strlen(log_buf) >= LOG_BUF_SIZE);
printk_deferred("[name:spm&][SPM] %s", log_buf);
}
static int mt6853_show_message(struct mt6853_spm_wake_status *wakesrc, int type,
const char *prefix, void *data)
{
#undef LOG_BUF_SIZE
#define LOG_BUF_SIZE 256
#define LOG_BUF_OUT_SZ 768
#define IS_WAKE_MISC(x) (wakesrc->wake_misc & x)
#define IS_LOGBUF(ptr, newstr) \
((strlen(ptr) + strlen(newstr)) < LOG_BUF_SIZE)
int i;
unsigned int spm_26M_off_pct = 0;
char buf[LOG_BUF_SIZE] = { 0 };
char log_buf[LOG_BUF_OUT_SZ] = { 0 };
char *local_ptr = NULL;
int log_size = 0;
unsigned int wr = WR_UNKNOWN;
const char *scenario = prefix ?: "UNKNOWN";
/* Disable rcu lock checking */
if (type == MT_LPM_ISSUER_SUSPEND)
rcu_irq_enter_irqson();
if (wakesrc->is_abort != 0) {
/* add size check for vcoredvfs */
aee_sram_printk("SPM ABORT (%s), r13 = 0x%x, ",
scenario, wakesrc->r13);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"[SPM] ABORT (%s), r13 = 0x%x, ",
scenario, wakesrc->r13);
aee_sram_printk(" debug_flag = 0x%x 0x%x",
wakesrc->debug_flag, wakesrc->debug_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" debug_flag = 0x%x 0x%x",
wakesrc->debug_flag, wakesrc->debug_flag1);
aee_sram_printk(" sw_flag = 0x%x 0x%x",
wakesrc->sw_flag0, wakesrc->sw_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" sw_flag = 0x%x 0x%x\n",
wakesrc->sw_flag0, wakesrc->sw_flag1);
aee_sram_printk(" b_sw_flag = 0x%x 0x%x",
wakesrc->b_sw_flag0, wakesrc->b_sw_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" b_sw_flag = 0x%x 0x%x",
wakesrc->b_sw_flag0, wakesrc->b_sw_flag1);
wr = WR_ABORT;
} else {
if (wakesrc->r12 & R12_PCM_TIMER) {
if (wakesrc->wake_misc & WAKE_MISC_PCM_TIMER_EVENT) {
local_ptr = " PCM_TIMER";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_PCM_TIMER;
}
}
if (wakesrc->r12 & R12_TWAM_IRQ_B) {
if (IS_WAKE_MISC(WAKE_MISC_DVFSRC_IRQ)) {
local_ptr = " DVFSRC";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_DVFSRC;
}
if (IS_WAKE_MISC(WAKE_MISC_TWAM_IRQ_B)) {
local_ptr = " TWAM";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_TWAM;
}
if (IS_WAKE_MISC(WAKE_MISC_PMSR_IRQ_B_SET0)) {
local_ptr = " PMSR";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_PMSR;
}
if (IS_WAKE_MISC(WAKE_MISC_PMSR_IRQ_B_SET1)) {
local_ptr = " PMSR";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_PMSR;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_0)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_1)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_2)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_3)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
if (IS_WAKE_MISC(WAKE_MISC_SPM_ACK_CHK_WAKEUP_ALL)) {
local_ptr = " SPM_ACK_CHK";
if (IS_LOGBUF(buf, local_ptr))
strncat(buf, local_ptr,
strlen(local_ptr));
wr = WR_SPM_ACK_CHK;
}
}
for (i = 1; i < 32; i++) {
if (wakesrc->r12 & (1U << i)) {
if (IS_LOGBUF(buf, mt6853_wakesrc_str[i]))
strncat(buf, mt6853_wakesrc_str[i],
strlen(mt6853_wakesrc_str[i]));
wr = WR_WAKE_SRC;
}
}
WARN_ON(strlen(buf) >= LOG_BUF_SIZE);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"%s wake up by %s, timer_out = %u, r13 = 0x%x, debug_flag = 0x%x 0x%x, ",
scenario, buf, wakesrc->timer_out, wakesrc->r13,
wakesrc->debug_flag, wakesrc->debug_flag1);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"r12 = 0x%x, r12_ext = 0x%x, raw_sta = 0x%x 0x%x 0x%x, idle_sta = 0x%x, ",
wakesrc->r12, wakesrc->r12_ext,
wakesrc->raw_sta,
wakesrc->md32pcm_wakeup_sta,
wakesrc->md32pcm_event_sta,
wakesrc->idle_sta);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"req_sta = 0x%x 0x%x 0x%x 0x%x 0x%x, cg_check_sta =0x%x, isr = 0x%x, rt_req_sta0 = 0x%x rt_req_sta1 = 0x%x rt_req_sta2 = 0x%x rt_req_sta3 = 0x%x dram_sw_con_3 = 0x%x, ",
wakesrc->req_sta0, wakesrc->req_sta1,
wakesrc->req_sta2, wakesrc->req_sta3,
wakesrc->req_sta4, wakesrc->cg_check_sta,
wakesrc->isr, wakesrc->rt_req_sta0,
wakesrc->rt_req_sta1, wakesrc->rt_req_sta2,
wakesrc->rt_req_sta3, wakesrc->rt_req_sta4);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"raw_ext_sta = 0x%x, wake_misc = 0x%x, pcm_flag = 0x%x 0x%x 0x%x 0x%x, req = 0x%x, ",
wakesrc->raw_ext_sta,
wakesrc->wake_misc,
wakesrc->sw_flag0,
wakesrc->sw_flag1, wakesrc->b_sw_flag0,
wakesrc->b_sw_flag0,
wakesrc->src_req);
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
" clk_settle = 0x%x, ", wakesrc->clk_settle);
if (type == MT_LPM_ISSUER_SUSPEND) {
/* calculate 26M off percentage in suspend period */
if (wakesrc->timer_out != 0) {
spm_26M_off_pct =
(100 * plat_mmio_read(SPM_BK_VTCXO_DUR))
/ wakesrc->timer_out;
}
log_size += scnprintf(log_buf + log_size,
LOG_BUF_OUT_SZ - log_size,
"wlk_cntcv_l = 0x%x, wlk_cntcv_h = 0x%x, 26M_off_pct = %d\n",
plat_mmio_read(SYS_TIMER_VALUE_L),
plat_mmio_read(SYS_TIMER_VALUE_H),
spm_26M_off_pct);
}
}
WARN_ON(log_size >= LOG_BUF_OUT_SZ);
if (type == MT_LPM_ISSUER_SUSPEND) {
printk_deferred("[name:spm&][SPM] %s", log_buf);
mt6853_suspend_show_detailed_wakeup_reason(wakesrc);
mt6853_suspend_spm_rsc_req_check(wakesrc);
printk_deferred("[name:spm&][SPM] Suspended for %d.%03d seconds",
PCM_TICK_TO_SEC(wakesrc->timer_out),
PCM_TICK_TO_SEC((wakesrc->timer_out %
PCM_32K_TICKS_PER_SEC)
* 1000));
/* Eable rcu lock checking */
rcu_irq_exit_irqson();
} else
pr_info("[name:spm&][SPM] %s", log_buf);
return wr;
}
int mt6853_issuer_func(int type, const char *prefix, void *data)
{
mt6853_get_wakeup_status(&mt6853_logger_help);
return mt6853_show_message(mt6853_logger_help.wakesrc,
type, prefix, data);
}
struct mtk_lpm_issuer mt6853_issuer = {
.log = mt6853_issuer_func,
};
static int mt6853_idle_save_sleep_info_nb_func(struct notifier_block *nb,
unsigned long action, void *data)
{
struct mtk_lpm_nb_data *nb_data = (struct mtk_lpm_nb_data *)data;
if (nb_data && (action == MTK_LPM_NB_BEFORE_REFLECT))
mt6853_save_sleep_info();
return NOTIFY_OK;
}
struct notifier_block mt6853_idle_save_sleep_info_nb = {
.notifier_call = mt6853_idle_save_sleep_info_nb_func,
};
static void mt6853_suspend_save_sleep_info_func(void)
{
mt6853_save_sleep_info();
}
static struct syscore_ops mt6853_suspend_save_sleep_info_syscore_ops = {
.resume = mt6853_suspend_save_sleep_info_func,
};
static int mt6853_log_timer_func(unsigned long long dur, void *priv)
{
struct mt6853_logger_timer *timer =
(struct mt6853_logger_timer *)priv;
struct mt6853_logger_fired_info *info = &mt6853_logger_fired;
if (timer->fired != info->fired) {
/* if the wake src had beed update before
* then won't do wake src update
*/
if (mt6853_logger_help.prev == mt6853_logger_help.cur)
mt6853_get_wakeup_status(&mt6853_logger_help);
mt6853_show_message(mt6853_logger_help.wakesrc,
MT_LPM_ISSUER_CPUIDLE,
"MCUSYSOFF", NULL);
mt6853_logger_help.prev = mt6853_logger_help.cur;
} else
pr_info("[name:spm&][SPM] MCUSYSOFF Didn't enter low power scenario\n");
timer->fired = info->fired;
return 0;
}
static int mt6853_logger_nb_func(struct notifier_block *nb,
unsigned long action, void *data)
{
struct mtk_lpm_nb_data *nb_data = (struct mtk_lpm_nb_data *)data;
struct mt6853_logger_fired_info *info = &mt6853_logger_fired;
if (nb_data && (action == MTK_LPM_NB_BEFORE_REFLECT)
&& (nb_data->index == info->state_index))
info->fired++;
return NOTIFY_OK;
}
struct notifier_block mt6853_logger_nb = {
.notifier_call = mt6853_logger_nb_func,
};
static ssize_t mt6853_logger_debugfs_read(char *ToUserBuf,
size_t sz, void *priv)
{
char *p = ToUserBuf;
int len;
if (priv == ((void *)&mt6853_log_timer)) {
len = scnprintf(p, sz, "%lu\n",
mtk_lpm_timer_interval(&mt6853_log_timer.tm));
p += len;
}
return (p - ToUserBuf);
}
static ssize_t mt6853_logger_debugfs_write(char *FromUserBuf,
size_t sz, void *priv)
{
if (priv == ((void *)&mt6853_log_timer)) {
unsigned int val = 0;
if (!kstrtouint(FromUserBuf, 10, &val)) {
if (val == 0)
mtk_lpm_timer_stop(&mt6853_log_timer.tm);
else
mtk_lpm_timer_interval_update(
&mt6853_log_timer.tm, val);
}
}
return sz;
}
struct MT6886_LOGGER_NODE {
struct mtk_lp_sysfs_handle handle;
struct mtk_lp_sysfs_op op;
};
#define MT6853_LOGGER_NODE_INIT(_n, _priv) ({\
_n.op.fs_read = mt6853_logger_debugfs_read;\
_n.op.fs_write = mt6853_logger_debugfs_write;\
_n.op.priv = _priv; })\
struct mtk_lp_sysfs_handle mt6853_log_tm_node;
struct MT6886_LOGGER_NODE mt6853_log_tm_interval;
int mt6853_logger_timer_debugfs_init(void)
{
mtk_lpm_sysfs_sub_entry_add("logger", 0644,
NULL, &mt6853_log_tm_node);
MT6853_LOGGER_NODE_INIT(mt6853_log_tm_interval,
&mt6853_log_timer);
mtk_lpm_sysfs_sub_entry_node_add("interval", 0644,
&mt6853_log_tm_interval.op,
&mt6853_log_tm_node,
&mt6853_log_tm_interval.handle);
return 0;
}
int __init mt6853_logger_init(void)
{
struct device_node *node = NULL;
struct cpuidle_driver *drv = NULL;
struct cpuidle_device *dev = NULL;
node = of_find_compatible_node(NULL, NULL, "mediatek,sleep");
if (node) {
mt6853_spm_base = of_iomap(node, 0);
of_node_put(node);
}
if (mt6853_spm_base)
mtk_lp_issuer_register(&mt6853_issuer);
else
pr_info("[name:mtk_lpm][P] - Don't register the issue by error! (%s:%d)\n",
__func__, __LINE__);
mt6853_get_spm_wakesrc_irq();
mtk_lpm_sysfs_root_entry_create();
mt6853_logger_timer_debugfs_init();
preempt_disable();
dev = cpuidle_get_device();
drv = cpuidle_get_cpu_driver(dev);
preempt_enable();
mt6853_logger_fired.state_index = -1;
if (drv) {
int idx;
for (idx = 0; idx < drv->state_count; ++idx) {
if (!strcmp(MT6853_LOG_MONITOR_STATE_NAME,
drv->states[idx].name)) {
mt6853_logger_fired.state_index = idx;
break;
}
}
}
mtk_lpm_notifier_register(&mt6853_logger_nb);
mtk_lpm_notifier_register(&mt6853_idle_save_sleep_info_nb);
mt6853_log_timer.tm.timeout = mt6853_log_timer_func;
mt6853_log_timer.tm.priv = &mt6853_log_timer;
mtk_lpm_timer_init(&mt6853_log_timer.tm, MTK_LPM_TIMER_REPEAT);
mtk_lpm_timer_interval_update(&mt6853_log_timer.tm,
MT6853_LOG_DEFAULT_MS);
mtk_lpm_timer_start(&mt6853_log_timer.tm);
register_syscore_ops(&mt6853_suspend_save_sleep_info_syscore_ops);
return 0;
}
late_initcall_sync(mt6853_logger_init);
@@ -0,0 +1,141 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <mtk_lpm_trace_event/mtk_lpm_trace_event.h>
#include <mtk_spm_sysfs.h>
#include <mt6853_spm_reg.h>
#define plat_mmio_read(offset) __raw_readl(spm_base + offset)
void __iomem *spm_base;
struct timer_list spm_resource_req_timer;
u32 spm_resource_req_timer_is_enabled;
u32 spm_resource_req_timer_ms;
static void spm_resource_req_timer_fn(struct timer_list *data)
{
u32 req_sta_0, req_sta_1, req_sta_4;
u32 src_req;
u32 md = 0, conn = 0, scp = 0, adsp = 0;
u32 ufs = 0, msdc = 0, disp = 0, apu = 0;
u32 spm = 0;
req_sta_0 = plat_mmio_read(SRC_REQ_STA_0);
if (req_sta_0 & 0xFFF)
md = 1;
if (req_sta_0 & (0x3F << 12))
conn = 1;
if (req_sta_0 & (0xF << 26))
disp = 1;
req_sta_1 = plat_mmio_read(SRC_REQ_STA_1);
if (req_sta_1 & 0x1F)
scp = 1;
if (req_sta_1 & (0x1F << 5))
adsp = 1;
if (req_sta_1 & (0x1F << 10))
ufs = 1;
if (req_sta_1 & (0x3FF << 21))
msdc = 1;
req_sta_4 = plat_mmio_read(SRC_REQ_STA_4);
if (req_sta_4 & 0x1F)
apu = 1;
src_req = plat_mmio_read(SPM_SRC_REQ);
if (src_req & 0x19B)
spm = 1;
spm_resource_req_timer.expires = jiffies +
msecs_to_jiffies(spm_resource_req_timer_ms);
add_timer(&spm_resource_req_timer);
}
static void spm_resource_req_timer_en(u32 enable, u32 timer_ms)
{
if (enable) {
if (spm_resource_req_timer_is_enabled)
return;
timer_setup(&spm_resource_req_timer,
spm_resource_req_timer_fn, 0);
spm_resource_req_timer_ms = timer_ms;
spm_resource_req_timer.expires = jiffies +
msecs_to_jiffies(spm_resource_req_timer_ms);
add_timer(&spm_resource_req_timer);
spm_resource_req_timer_is_enabled = true;
} else if (spm_resource_req_timer_is_enabled) {
del_timer(&spm_resource_req_timer);
spm_resource_req_timer_is_enabled = false;
}
}
ssize_t get_spm_resource_req_timer_enable(char *ToUserBuf
, size_t sz, void *priv)
{
int bLen = snprintf(ToUserBuf, sz
, "spm resource request timer is enabled: %d\n",
spm_resource_req_timer_is_enabled);
return (bLen > sz) ? sz : bLen;
}
ssize_t set_spm_resource_req_timer_enable(char *ToUserBuf
, size_t sz, void *priv)
{
u32 is_enable;
u32 timer_ms;
if (!ToUserBuf)
return -EINVAL;
if (sscanf(ToUserBuf, "%d %d", &is_enable, &timer_ms) == 2) {
spm_resource_req_timer_en(is_enable, timer_ms);
return sz;
}
if (kstrtouint(ToUserBuf, 10, &is_enable) == 0) {
if (is_enable == 0) {
spm_resource_req_timer_en(is_enable, 0);
return sz;
}
}
return -EINVAL;
}
static const struct mtk_lp_sysfs_op spm_resource_req_timer_enable_fops = {
.fs_read = get_spm_resource_req_timer_enable,
.fs_write = set_spm_resource_req_timer_enable,
};
bool spm_is_md1_sleep(void)
{
return !(plat_mmio_read(SRC_REQ_STA_0) & 0x3F);
}
EXPORT_SYMBOL(spm_is_md1_sleep);
int __init mt6853_lpm_trace_init(void)
{
struct device_node *node = NULL;
node = of_find_compatible_node(NULL, NULL, "mediatek,sleep");
if (node) {
spm_base = of_iomap(node, 0);
of_node_put(node);
}
mtk_spm_sysfs_root_entry_create();
mtk_spm_sysfs_entry_node_add("spm_dump_res_req_enable", 0444
, &spm_resource_req_timer_enable_fops, NULL);
return 0;
}
late_initcall_sync(mt6853_lpm_trace_init);
@@ -0,0 +1,270 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/pm_qos.h>
#include <mtk_lpm.h>
#include <mtk_lp_plat_reg.h>
#include "mtk_cpupm_dbg.h"
#include "mtk_cpuidle_cpc.h"
static DEFINE_SPINLOCK(cpc_prof_spin_lock);
/* profile latecny */
#define PROF_DEV_NAME_SIZE 36
#define get_core_type_index(cpu) ((cpu > 3) ? CPU_TYPE_B : CPU_TYPE_L)
struct mtk_cpc_lat_data {
char name[PROF_DEV_NAME_SIZE];
unsigned int on_sum;
unsigned int on_max;
unsigned int off_sum;
unsigned int off_max;
unsigned int on_cnt;
unsigned int off_cnt;
};
struct mtk_cpc_device {
union {
struct mtk_cpc_lat_data p[DEV_TYPE_NUM];
struct {
struct mtk_cpc_lat_data cpu[NF_CPU_TYPE];
struct mtk_cpc_lat_data cluster;
struct mtk_cpc_lat_data mcusys;
};
};
bool prof_en;
};
struct mtk_cpc_device cpc;
static void mtk_cpc_clr_lat(void)
{
unsigned int size;
unsigned int ofs;
int i;
ofs = sizeof(cpc.p[0].name);
size = sizeof(struct mtk_cpc_lat_data) - ofs;
for (i = 0; i < DEV_TYPE_NUM; i++)
memset((char *)&cpc.p[i] + ofs, 0, size);
}
static void mtk_cpc_cal_lat(void)
{
}
#define __mtk_cpc_record_lat(sum, max, lat) \
do { \
if (lat > max) \
max = lat; \
(sum) += (lat); \
} while (0)
static void mtk_cpc_record_lat(struct mtk_cpc_lat_data *lat,
unsigned int on_ticks, unsigned int off_ticks)
{
unsigned long flags;
if ((on_ticks == 0) || (off_ticks == 0))
return;
spin_lock_irqsave(&cpc_prof_spin_lock, flags);
__mtk_cpc_record_lat(lat->on_sum, lat->on_max, on_ticks);
lat->on_cnt++;
__mtk_cpc_record_lat(lat->off_sum, lat->off_max, off_ticks);
lat->off_cnt++;
spin_unlock_irqrestore(&cpc_prof_spin_lock, flags);
}
static void mtk_cpc_save_cpu_lat(int cpu)
{
unsigned int lat, on, off;
lat = mtk_cpupm_mcusys_read(CPC_CPU_LATENCY(cpu));
on = (lat >> 16) & 0xFFFF;
off = lat & 0xFFFF;
mtk_cpc_record_lat(&cpc.cpu[get_core_type_index(cpu)],
on, off);
}
static void mtk_cpc_save_cpusys_lat(void)
{
unsigned int lat_on, lat_off, on, off;
lat_on = mtk_cpupm_mcusys_read(CPC_CLUSTER_ON_LATENCY);
lat_off = mtk_cpupm_mcusys_read(CPC_CLUSTER_OFF_LATENCY);
on = lat_on & 0xFFFF;
off = lat_off & 0xFFFF;
mtk_cpc_record_lat(&cpc.cluster, on, off);
}
static void mtk_cpc_save_mcusys_lat(void)
{
unsigned int lat, on, off;
lat = mtk_cpupm_mcusys_read(CPC_MCUSYS_LATENCY);
on = (lat >> 16) & 0xFFFF;
off = lat & 0xFFFF;
mtk_cpc_record_lat(&cpc.mcusys, on, off);
}
static bool mtk_cpc_did_cluster_pwr_off(void)
{
static unsigned int last_cnt;
unsigned int cnt = mtk_cpupm_mcusys_read(CPC_DORMANT_COUNTER);
/**
* Cluster off count
* bit[0:15] : memory retention
* bit[16:31] : memory off
*/
if ((cnt & 0x7FFF) == 0)
cnt = ((cnt >> 16) & 0x7FFF);
else
cnt = cnt & 0x7FFF;
cnt += mtk_cpupm_syssram_read(SYSRAM_CPC_CPUSYS_CNT_BACKUP);
if (last_cnt == cnt)
return false;
last_cnt = cnt;
return true;
}
static bool mtk_cpc_did_mcusys_pwr_off(void)
{
static unsigned int last_cnt;
unsigned int cnt = mtk_cpupm_syssram_read(SYSRAM_MCUPM_MCUSYS_COUNTER);
cnt += mtk_cpupm_syssram_read(SYSRAM_MCUSYS_CNT);
if (last_cnt == cnt)
return false;
last_cnt = cnt;
return true;
}
static void mtk_cpc_save_latency(int cpu)
{
mtk_cpc_save_cpu_lat(cpu);
if (mtk_cpc_did_mcusys_pwr_off())
mtk_cpc_save_mcusys_lat();
else if (!mtk_cpc_did_cluster_pwr_off())
return;
mtk_cpc_save_cpusys_lat();
}
void mtk_cpc_prof_lat_dump(struct seq_file *m)
{
struct mtk_cpc_lat_data *lat;
int i;
for (i = 0; i < DEV_TYPE_NUM; i++) {
lat = &cpc.p[i];
seq_printf(m, "%s\n", lat->name);
if (lat->off_cnt) {
seq_printf(m, "\toff : avg = %2dus, max = %3dus, cnt = %d\n",
cpc_tick_to_us(lat->off_sum / lat->off_cnt),
cpc_tick_to_us(lat->off_max),
lat->off_cnt);
} else {
seq_puts(m, "\toff : None\n");
}
if (lat->on_cnt) {
seq_printf(m, "\ton : avg = %2dus, max = %3dus, cnt = %d\n",
cpc_tick_to_us(lat->on_sum / lat->on_cnt),
cpc_tick_to_us(lat->on_max),
lat->on_cnt);
} else {
seq_puts(m, "\ton : None\n");
}
}
}
void mtk_cpc_prof_start(void)
{
mtk_cpupm_block();
cpc_prof_en();
mtk_cpc_clr_lat();
cpc.prof_en = true;
mtk_cpupm_allow();
}
void mtk_cpc_prof_stop(void)
{
mtk_cpupm_block();
cpc.prof_en = false;
mtk_cpc_cal_lat();
cpc_prof_dis();
mtk_cpupm_allow();
}
int mtk_cpc_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct mtk_lpm_nb_data *nb_data = (struct mtk_lpm_nb_data *)data;
if (!cpc.prof_en)
return NOTIFY_OK;
if (action & MTK_LPM_NB_BEFORE_REFLECT)
mtk_cpc_save_latency(nb_data->cpu);
return NOTIFY_OK;
}
struct notifier_block mtk_cpc_nb = {
.notifier_call = mtk_cpc_notify,
};
int __init mtk_cpc_init(void)
{
cpc.prof_en = false;
snprintf(cpc.cpu[CPU_TYPE_L].name, PROF_DEV_NAME_SIZE, "cpu_L");
snprintf(cpc.cpu[CPU_TYPE_B].name, PROF_DEV_NAME_SIZE, "cpu_B");
snprintf(cpc.cluster.name, PROF_DEV_NAME_SIZE, "cluster");
snprintf(cpc.mcusys.name, PROF_DEV_NAME_SIZE, "mcusys");
mtk_lpm_notifier_register(&mtk_cpc_nb);
return 0;
}
void __exit mtk_cpc_exit(void)
{
mtk_lpm_notifier_unregister(&mtk_cpc_nb);
}
@@ -0,0 +1,100 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_CPUIDLE_CPC_H__
#define __MTK_CPUIDLE_CPC_H__
#include <linux/seq_file.h>
#include <mtk_lpm_module.h>
enum {
CPU_TYPE_L,
CPU_TYPE_B,
NF_CPU_TYPE
};
enum {
CPC_SMC_EVENT_DUMP_TRACE_DATA,
CPC_SMC_EVENT_GIC_DPG_SET,
CPC_SMC_EVENT_CPC_CONFIG,
CPC_SMC_EVENT_READ_CONFIG,
NF_CPC_SMC_EVENT
};
enum {
CPC_SMC_CONFIG_PROF,
CPC_SMC_CONFIG_AUTO_OFF,
CPC_SMC_CONFIG_AUTO_OFF_THRES,
CPC_SMC_CONFIG_CNT_CLR,
CPC_SMC_CONFIG_TIME_SYNC,
NF_CPC_SMC_CONFIG
};
/**
* number of profile type :
* - (number of cpu type) + cluster + mcusys
*/
#define DEV_TYPE_NUM (NF_CPU_TYPE + 2)
/* smc */
#define mtk_cpc_smc(act, arg1, arg2)\
mtk_lpm_smc_cpu_pm(CPC_COMMAND, act, arg1, arg2)
/* configuration */
#define cpc_prof_en()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_PROF, 1)
#define cpc_prof_dis()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_PROF, 0)
#define cpc_auto_off_en()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF, 1)
#define cpc_auto_off_dis()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF, 0)
#define cpc_set_auto_off_thres(us)\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF_THRES, us)
#define cpc_cluster_cnt_clr()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_CNT_CLR, 0)
#define cpc_time_sync()\
mtk_cpc_smc(CPC_SMC_EVENT_CPC_CONFIG\
, CPC_SMC_CONFIG_TIME_SYNC, 0)
/* status */
#define cpc_get_prof_sta()\
mtk_cpc_smc(CPC_SMC_EVENT_READ_CONFIG\
, CPC_SMC_CONFIG_PROF, 0)
#define cpc_get_auto_off_sta()\
mtk_cpc_smc(CPC_SMC_EVENT_READ_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF, 0)
#define cpc_get_auto_off_thres()\
mtk_cpc_smc(CPC_SMC_EVENT_READ_CONFIG\
, CPC_SMC_CONFIG_AUTO_OFF_THRES, 0)
#define cpc_tick_to_us(val) ((val) / 13)
void mtk_cpc_prof_start(void);
void mtk_cpc_prof_stop(void);
void mtk_cpc_prof_lat_dump(struct seq_file *m);
int __init mtk_cpc_init(void);
void __exit mtk_cpc_exit(void);
#endif /* __MTK_CPUIDLE_CPC_H__ */
@@ -0,0 +1,665 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/cpuidle.h>
#include <linux/sched/clock.h>
#include <linux/cpu.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/pm_qos.h>
#include <linux/tick.h>
#include <linux/timer.h>
#include <mboot_params.h>
//#include <mtk_ram_console.h>
#include <mt6853_spm_comm.h>
#include <mtk_lpm.h>
#include <mtk_lp_plat_reg.h>
#include "mtk_cpupm_dbg.h"
#include "mtk_cpuidle_status.h"
#include "mtk_cpuidle_cpc.h"
#define DUMP_INTERVAL sec_to_ns(5)
static u64 last_dump_ns;
static unsigned long long mtk_lpm_last_cpuidle_dis;
/* stress test */
static unsigned int timer_interval = 10 * 1000;
static struct task_struct *stress_tsk[NR_CPUS];
/* CPU off state : core, cluster and mcusys */
#define CPU_OFF_MAX_LV (3)
/* all core idle profiling */
struct all_cpu_idle_data {
unsigned long long last_ns;
unsigned long long dur_ns;
unsigned int cnt;
unsigned int bp; /* basis points : 0.01% */
};
struct all_cpu_idle {
unsigned long long start_us;
struct all_cpu_idle_data lv[CPU_OFF_MAX_LV];
};
static struct all_cpu_idle all_core_off;
/* idle ratio profiling */
struct mtk_cpuidle_ratio {
unsigned long long start_us;
unsigned long long idle_time_ns[CPUIDLE_STATE_MAX];
unsigned int bp[CPUIDLE_STATE_MAX];
};
/* idle latency profiling */
struct mtk_cpuidle_prof {
unsigned int cnt;
unsigned int max;
union {
u64 avg;
u64 sum;
};
};
/* idle information */
struct mtk_cpuidle_info {
u64 enter_time_ns;
int idle_index;
int cnt[CPUIDLE_STATE_MAX];
};
/* mtk cpu idle status */
struct mtk_cpuidle_device {
struct mtk_cpuidle_ratio ratio;
struct mtk_cpuidle_info info;
struct mtk_cpuidle_prof prof;
struct hrtimer timer;
int cpu;
int state_count;
bool tmr_running;
};
static DEFINE_PER_CPU(struct mtk_cpuidle_device, mtk_cpuidle_dev);
/* mtk cpu idle configuration */
struct mtk_cpuidle_control {
bool tmr_en;
bool prof_en;
bool log_en;
bool stress_en;
};
static struct mtk_cpuidle_control mtk_cpuidle_ctrl;
static int mtk_cpuidle_stress_task(void *arg)
{
while (mtk_cpuidle_ctrl.stress_en)
usleep_range(timer_interval - 10, timer_interval + 10);
return 0;
}
static void mtk_cpuidle_stress_start(void)
{
int i;
char name[20] = {0};
int ret = 0;
if (mtk_cpuidle_ctrl.stress_en)
return;
mtk_cpuidle_ctrl.stress_en = true;
for_each_online_cpu(i) {
ret = scnprintf(name, sizeof(name), "mtk_cpupm_stress_%d", i);
stress_tsk[i] =
kthread_create(mtk_cpuidle_stress_task, NULL, name);
if (!IS_ERR(stress_tsk[i])) {
kthread_bind(stress_tsk[i], i);
wake_up_process(stress_tsk[i]);
}
}
}
static void mtk_cpuidle_stress_stop(void)
{
mtk_cpuidle_ctrl.stress_en = false;
msleep(20);
}
void mtk_cpuidle_set_stress_test(bool en)
{
if (en)
mtk_cpuidle_stress_start();
else
mtk_cpuidle_stress_stop();
}
bool mtk_cpuidle_get_stress_status(void)
{
return mtk_cpuidle_ctrl.stress_en;
}
void mtk_cpuidle_set_stress_time(unsigned int val)
{
timer_interval = clamp_val(val, 100, 20000);
}
unsigned int mtk_cpuidle_get_stress_time(void)
{
return timer_interval;
}
void mtk_cpuidle_ctrl_timer_en(bool enable)
{
mtk_cpupm_block();
mtk_cpuidle_ctrl.tmr_en = enable;
mtk_cpupm_allow();
}
bool mtk_cpuidle_ctrl_timer_sta_get(void)
{
return mtk_cpuidle_ctrl.tmr_en;
}
void mtk_cpuidle_ctrl_log_en(bool enable)
{
mtk_cpupm_block();
mtk_cpuidle_ctrl.log_en = enable;
mtk_cpupm_allow();
}
bool mtk_cpuidle_ctrl_log_sta_get(void)
{
return mtk_cpuidle_ctrl.log_en;
}
static void _mtk_cpuidle_prof_ratio_start(void *data)
{
int i, cpu;
struct mtk_cpuidle_device *mtk_idle;
cpu = get_cpu();
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
put_cpu();
if (unlikely(!mtk_idle))
return;
mtk_idle->ratio.start_us = div64_u64(sched_clock(), 1000);
for (i = 0; i < mtk_idle->state_count; i++)
mtk_idle->ratio.idle_time_ns[i] = 0;
}
void mtk_cpuidle_prof_ratio_start(void)
{
int i;
if (mtk_cpuidle_ctrl.prof_en)
return;
mtk_cpupm_block();
for (i = 0; i < CPU_OFF_MAX_LV; i++) {
all_core_off.lv[i].dur_ns = 0;
all_core_off.lv[i].cnt = 0;
}
all_core_off.start_us = div64_u64(sched_clock(), 1000);
on_each_cpu(_mtk_cpuidle_prof_ratio_start, NULL, 1);
mtk_cpuidle_ctrl.prof_en = true;
mtk_cpupm_allow();
}
static void _mtk_cpuidle_prof_ratio_stop(void *stop_time)
{
int i, cpu;
uint64_t dur_us;
struct mtk_cpuidle_device *mtk_idle;
cpu = get_cpu();
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
put_cpu();
if (unlikely(!mtk_idle))
return;
dur_us = div64_u64(sched_clock(), 1000) -
mtk_idle->ratio.start_us;
for (i = 0; i < mtk_idle->state_count; i++)
mtk_idle->ratio.bp[i] = (unsigned int)div64_u64(
mtk_idle->ratio.idle_time_ns[i] * 10,
dur_us);
}
void mtk_cpuidle_prof_ratio_stop(void)
{
int i;
uint64_t time_us;
if (!mtk_cpuidle_ctrl.prof_en)
return;
mtk_cpupm_block();
on_each_cpu(_mtk_cpuidle_prof_ratio_stop, NULL, 1);
mtk_cpuidle_ctrl.prof_en = false;
time_us = div64_u64(sched_clock(), 1000) - all_core_off.start_us;
for (i = 0; i < CPU_OFF_MAX_LV; i++)
all_core_off.lv[i].bp = (unsigned int)div64_u64(
all_core_off.lv[i].dur_ns * 10, time_us);
mtk_cpupm_allow();
}
void mtk_cpuidle_prof_ratio_dump(struct seq_file *m)
{
int i, cpu;
struct mtk_cpuidle_device *mtk_idle;
seq_printf(m, "%6s%12s%12s%12s\n",
"",
"cpu_off",
"cluster_off",
"mcusys_off");
for_each_possible_cpu(cpu) {
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
if (!mtk_idle)
continue;
seq_printf(m, "cpu%d: ", cpu);
for (i = 1; i < mtk_idle->state_count; i++) {
seq_printf(m, "%5s%3u.%02u%%",
"",
mtk_idle->ratio.bp[i] / 100,
mtk_idle->ratio.bp[i] % 100);
}
seq_puts(m, "\n");
}
seq_puts(m, "All core off ratio :\n");
for (i = 0; i < CPU_OFF_MAX_LV; i++)
seq_printf(m, "\t C_%d = %3u.%02u%%\n",
i + 1,
all_core_off.lv[i].bp / 100,
all_core_off.lv[i].bp % 100);
}
void mtk_cpuidle_state_enable(bool en)
{
struct cpuidle_driver *drv;
int i, cpu;
int suspend_type = mtk_lpm_suspend_type_get();
mtk_cpupm_block();
for_each_possible_cpu(cpu) {
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
for (i = drv->state_count - 1; i > 0; i--) {
if ((suspend_type == MTK_LPM_SUSPEND_S2IDLE) &&
!strcmp(drv->states[i].name, S2IDLE_STATE_NAME))
continue;
mtk_cpuidle_set_param(drv, i, IDLE_PARAM_EN, en);
}
}
if (!en)
mtk_lpm_last_cpuidle_dis = sched_clock();
mtk_cpupm_allow();
}
void mtk_s2idle_state_enable(bool en)
{
struct cpuidle_driver *drv;
int i, cpu;
int suspend_type = mtk_lpm_suspend_type_get();
mtk_cpupm_block();
for_each_possible_cpu(cpu) {
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
i = drv->state_count - 1;
if ((suspend_type == MTK_LPM_SUSPEND_S2IDLE) &&
!strcmp(drv->states[i].name, S2IDLE_STATE_NAME)) {
mtk_cpuidle_set_param(drv, i, IDLE_PARAM_EN, en);
}
}
if (!en)
mtk_lpm_last_cpuidle_dis = sched_clock();
mtk_cpupm_allow();
}
unsigned long long mtk_cpuidle_state_last_dis_ms(void)
{
return (mtk_lpm_last_cpuidle_dis / 1000000);
}
static bool mtk_cpuidle_need_dump(unsigned int idx)
{
u64 curr_ns;
bool dump = false;
if (!mtk_cpuidle_ctrl.log_en || !idx)
return false;
curr_ns = sched_clock();
if (curr_ns - last_dump_ns > DUMP_INTERVAL) {
last_dump_ns = curr_ns;
dump = true;
}
return dump;
}
static unsigned int mtk_cpuidle_get_cluster_off_cnt(void)
{
unsigned int cnt = mtk_cpupm_mcusys_read(CPC_DORMANT_COUNTER);
/**
* Cluster off count
* bit[0:15] : memory retention
* bit[16:31] : memory off
*/
if ((cnt & 0x7FFF) == 0)
cnt = ((cnt >> 16) & 0x7FFF);
else
cnt = cnt & 0x7FFF;
cnt += mtk_cpupm_syssram_read(SYSRAM_CPC_CPUSYS_CNT_BACKUP);
cpc_cluster_cnt_clr();
mtk_cpupm_syssram_write(SYSRAM_CPC_CPUSYS_CNT_BACKUP, 0);
/**
* Add mcusys off count because CPC cluster counter will be cleared
* when mcusys power off.
*/
cnt += mtk_cpupm_syssram_read(SYSRAM_MCUPM_MCUSYS_COUNTER);
mtk_cpupm_syssram_write(SYSRAM_CPUSYS_CNT,
mtk_cpupm_syssram_read(SYSRAM_CPUSYS_CNT) + cnt);
return cnt;
}
static unsigned int mtk_cpuidle_get_mcusys_off_cnt(void)
{
unsigned int cnt = mtk_cpupm_syssram_read(SYSRAM_MCUPM_MCUSYS_COUNTER);
mtk_cpupm_syssram_write(SYSRAM_MCUPM_MCUSYS_COUNTER, 0);
mtk_cpupm_syssram_write(SYSRAM_MCUSYS_CNT,
mtk_cpupm_syssram_read(SYSRAM_MCUSYS_CNT) + cnt);
return cnt;
}
static void mtk_cpuidle_dump_info(void)
{
struct mtk_cpuidle_device *mtk_idle;
int cpu, idx, ofs;
unsigned int avail_cpu_mask = 0;
for_each_possible_cpu(cpu) {
if (cpu_online(cpu))
avail_cpu_mask |= (1 << cpu);
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
if (!mtk_idle)
continue;
ofs = SYSRAM_RECENT_CPU_CNT(cpu);
mtk_cpupm_syssram_write(ofs, 0);
for (idx = 1; idx < mtk_idle->state_count ; idx++) {
mtk_cpupm_syssram_write(ofs, mtk_cpupm_syssram_read(ofs)
+ mtk_idle->info.cnt[idx]);
mtk_idle->info.cnt[idx] = 0;
}
}
/* Should calculate cluster off count before mcusys counter reset */
mtk_cpupm_syssram_write(SYSRAM_RECENT_CPUSYS_CNT,
mtk_cpuidle_get_cluster_off_cnt());
mtk_cpupm_syssram_write(SYSRAM_RECENT_MCUSYS_CNT,
mtk_cpuidle_get_mcusys_off_cnt());
mtk_cpupm_syssram_write(SYSRAM_CPU_ONLINE, avail_cpu_mask);
mtk_cpupm_syssram_write(SYSRAM_RECENT_CNT_TS_H,
(unsigned int)((last_dump_ns >> 32) & 0xFFFFFFFF));
mtk_cpupm_syssram_write(SYSRAM_RECENT_CNT_TS_L,
(unsigned int)(last_dump_ns & 0xFFFFFFFF));
}
static enum hrtimer_restart mtk_cpuidle_hrtimer_func(struct hrtimer *timer)
{
return HRTIMER_NORESTART;
}
static void mtk_cpuidle_set_timer(struct mtk_cpuidle_device *mtk_idle)
{
int index;
int limit_sleep_us;
struct cpuidle_driver *drv;
if (!mtk_cpuidle_ctrl.tmr_en)
return;
index = mtk_idle->info.idle_index;
/* Only support WFI/CPU_OFF state */
if (index > 1)
return;
drv = cpuidle_get_driver();
if (unlikely(!drv))
return;
if (index + 1 >= mtk_idle->state_count
|| !tick_nohz_tick_stopped())
return;
limit_sleep_us = 4 * max_t(unsigned int,
get_residency(drv, index),
get_residency(drv, index + 1));
if (index != 0)
tick_broadcast_exit();
RCU_NONIDLE(hrtimer_start(&mtk_idle->timer,
ns_to_ktime(limit_sleep_us * NSEC_PER_USEC),
HRTIMER_MODE_REL_PINNED));
mtk_idle->tmr_running = true;
if (index != 0)
tick_broadcast_enter();
}
static void mtk_cpuidle_cancel_timer(struct mtk_cpuidle_device *mtk_idle)
{
if (!mtk_cpuidle_ctrl.tmr_en)
return;
if (mtk_idle->tmr_running) {
mtk_idle->tmr_running = true;
RCU_NONIDLE(hrtimer_try_to_cancel(&mtk_idle->timer));
}
}
static void mtk_cpuidle_set_last_off_ts(int idx)
{
int i;
uint64_t curr_ns = sched_clock();
if (!idx)
return;
if (idx > CPU_OFF_MAX_LV)
idx = CPU_OFF_MAX_LV;
for (i = 0; i < idx; i++) {
all_core_off.lv[i].cnt++;
if (all_core_off.lv[i].cnt == num_online_cpus())
all_core_off.lv[i].last_ns = curr_ns;
}
}
static void mtk_cpuidle_save_idle_time(int idx)
{
int i;
uint64_t curr_ns = sched_clock();
if (!idx)
return;
if (idx > CPU_OFF_MAX_LV)
idx = CPU_OFF_MAX_LV;
for (i = 0; i < idx; i++) {
if (all_core_off.lv[i].cnt == num_online_cpus())
all_core_off.lv[i].dur_ns += (curr_ns
- all_core_off.lv[i].last_ns);
all_core_off.lv[i].cnt--;
}
}
static int mtk_cpuidle_status_update(struct notifier_block *nb,
unsigned long action, void *data)
{
struct mtk_cpuidle_device *mtk_idle;
struct mtk_lpm_nb_data *nb_data = (struct mtk_lpm_nb_data *)data;
if (action & MTK_LPM_NB_BEFORE_REFLECT) {
/* prevent race conditions by mtk_lp_mod_locker */
if (mtk_cpuidle_ctrl.prof_en)
mtk_cpuidle_save_idle_time(nb_data->index);
if (mtk_cpuidle_need_dump(nb_data->index))
mtk_cpuidle_dump_info();
} else if (action & MTK_LPM_NB_RESUME) {
aee_rr_rec_mcdi_val(nb_data->cpu,
(nb_data->index << 16) | 0x0);
mtk_idle = &per_cpu(mtk_cpuidle_dev, nb_data->cpu);
mtk_idle->info.idle_index = -1;
mtk_idle->info.cnt[nb_data->index]++;
if (mtk_cpuidle_ctrl.prof_en)
mtk_idle->ratio.idle_time_ns[nb_data->index] +=
(sched_clock() - mtk_idle->info.enter_time_ns);
mtk_cpuidle_cancel_timer(mtk_idle);
} else if (action & MTK_LPM_NB_AFTER_PROMPT) {
/* prevent race conditions by mtk_lp_mod_locker */
if (mtk_cpuidle_ctrl.prof_en)
mtk_cpuidle_set_last_off_ts(nb_data->index);
} else if (action & MTK_LPM_NB_PREPARE) {
mtk_idle = &per_cpu(mtk_cpuidle_dev, nb_data->cpu);
mtk_idle->info.idle_index = nb_data->index;
mtk_idle->info.enter_time_ns = sched_clock();
mtk_cpuidle_set_timer(mtk_idle);
aee_rr_rec_mcdi_val(nb_data->cpu,
(nb_data->index << 16) | 0xff);
}
return NOTIFY_OK;
}
struct notifier_block mtk_cpuidle_status_nb = {
.notifier_call = mtk_cpuidle_status_update,
};
static void mtk_cpuidle_init_per_cpu(void *info)
{
struct mtk_cpuidle_device *mtk_idle;
int cpu;
cpu = get_cpu();
mtk_idle = &per_cpu(mtk_cpuidle_dev, cpu);
put_cpu();
hrtimer_init(&mtk_idle->timer,
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
mtk_idle->cpu = cpu;
mtk_idle->timer.function = mtk_cpuidle_hrtimer_func;
mtk_idle->state_count = mtk_cpupm_get_idle_state_count(cpu);
}
int __init mtk_cpuidle_status_init(void)
{
mtk_cpuidle_ctrl.tmr_en = true;
mtk_cpuidle_ctrl.prof_en = false;
mtk_cpuidle_ctrl.log_en = true;
mtk_cpuidle_ctrl.stress_en = false;
mtk_cpupm_block();
on_each_cpu(mtk_cpuidle_init_per_cpu, NULL, 0);
mtk_cpupm_allow();
mtk_lpm_notifier_register(&mtk_cpuidle_status_nb);
return 0;
}
void __exit mtk_cpuidle_status_exit(void)
{
mtk_lpm_notifier_unregister(&mtk_cpuidle_status_nb);
}
@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_CPUIDLE_STATUS_H__
#define __MTK_CPUIDLE_STATUS_H__
#include <linux/seq_file.h>
#define sec_to_ns(v) ((v) * 1000 * 1000 * 1000ULL)
#define S2IDLE_STATE_NAME "s2idle"
enum {
IDLE_PARAM_LAT,
IDLE_PARAM_RES,
IDLE_PARAM_EN,
NF_IDLE_PARAM
};
#define get_residency(drv, idx)\
((drv)->states[idx].target_residency)
#define get_latency(drv, idx)\
((drv)->states[idx].exit_latency)
#define get_disabled(drv, idx)\
((drv)->states[idx].disabled)
#define mtk_cpuidle_get_param(drv, state, param) \
({ \
int val = -1; \
if (param == IDLE_PARAM_LAT) \
val = (int)get_latency(drv, state); \
else if (param == IDLE_PARAM_RES) \
val = (int)get_residency(drv, state); \
else if (param == IDLE_PARAM_EN) \
val = get_disabled(drv, state) ? 0 : 1 ; \
val; \
})
#define mtk_cpuidle_set_param(drv, state, param, val) \
do { \
if (param == IDLE_PARAM_LAT) \
get_latency(drv, state) = val; \
else if (param == IDLE_PARAM_RES) \
get_residency(drv, state) = val; \
else if (param == IDLE_PARAM_EN) \
get_disabled(drv, state) = !val ; \
} while (0)
void mtk_cpuidle_set_stress_test(bool en);
bool mtk_cpuidle_get_stress_status(void);
void mtk_cpuidle_set_stress_time(unsigned int val);
unsigned int mtk_cpuidle_get_stress_time(void);
void mtk_cpuidle_prof_ratio_start(void);
void mtk_cpuidle_prof_ratio_stop(void);
void mtk_cpuidle_prof_ratio_dump(struct seq_file *m);
void mtk_cpuidle_ctrl_timer_en(bool enable);
bool mtk_cpuidle_ctrl_timer_sta_get(void);
void mtk_cpuidle_ctrl_log_en(bool enable);
bool mtk_cpuidle_ctrl_log_sta_get(void);
void mtk_cpuidle_state_enable(bool en);
void mtk_s2idle_state_enable(bool en);
int __init mtk_cpuidle_status_init(void);
void __exit mtk_cpuidle_status_exit(void);
unsigned long long mtk_cpuidle_state_last_dis_ms(void);
#endif /* __MTK_CPUIDLE_STATUS_H__ */
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cpuidle.h>
#include <linux/pm_qos.h>
#include "mtk_cpuidle_status.h"
#include "mtk_cpuidle_cpc.h"
#include "mtk_idle_procfs.h"
static void __iomem *cpupm_mcusys_base_addr;
static void __iomem *cpupm_syssram_base_addr;
static struct pm_qos_request cpuidle_dbg_qos_req;
int mtk_cpupm_mcusys_write(int ofs, unsigned int val)
{
if (!cpupm_mcusys_base_addr)
return -EADDRNOTAVAIL;
if ((ofs % 4) != 0)
return -EINVAL;
__raw_writel(val, cpupm_mcusys_base_addr + ofs);
mb(); /* make sure register access in order */
return 0;
}
unsigned int mtk_cpupm_mcusys_read(int ofs)
{
if (!cpupm_mcusys_base_addr)
return 0;
if ((ofs % 4) != 0)
return 0;
return __raw_readl(cpupm_mcusys_base_addr + ofs);
}
int mtk_cpupm_syssram_write(int ofs, unsigned int val)
{
if (!cpupm_syssram_base_addr)
return -EADDRNOTAVAIL;
if ((ofs % 4) != 0)
return -EINVAL;
__raw_writel(val, cpupm_syssram_base_addr + ofs);
mb(); /* make sure register access in order */
return 0;
}
unsigned int mtk_cpupm_syssram_read(int ofs)
{
if (!cpupm_syssram_base_addr)
return 0;
if ((ofs % 4) != 0)
return 0;
return __raw_readl(cpupm_syssram_base_addr + ofs);
}
void mtk_cpupm_block(void)
{
pm_qos_update_request(&cpuidle_dbg_qos_req, 2);
}
void mtk_cpupm_allow(void)
{
pm_qos_update_request(&cpuidle_dbg_qos_req, PM_QOS_DEFAULT_VALUE);
}
int mtk_cpupm_get_idle_state_count(int cpu)
{
struct device_node *state_node, *cpu_node;
int i, state_count, start_idx = 1;
cpu_node = of_cpu_device_node_get(cpu);
if (!cpu_node)
return start_idx;
for (i = 0; ; i++) {
state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
if (!state_node)
break;
of_node_put(state_node);
}
of_node_put(cpu_node);
state_count = i + start_idx;
if (state_count > CPUIDLE_STATE_MAX)
state_count = CPUIDLE_STATE_MAX;
return state_count;
}
static void __init mtk_cpupm_node_init(void)
{
struct device_node *node = NULL;
cpupm_mcusys_base_addr = NULL;
cpupm_syssram_base_addr = NULL;
node = of_find_compatible_node(NULL, NULL,
"mediatek,mcusys-ctrl");
if (node) {
cpupm_mcusys_base_addr = of_iomap(node, 0);
of_node_put(node);
}
node = of_find_compatible_node(NULL, NULL,
"mediatek,cpupm-sysram");
if (node) {
cpupm_syssram_base_addr = of_iomap(node, 0);
of_node_put(node);
}
}
static int __init mtk_cpupm_dbg_init(void)
{
pm_qos_add_request(&cpuidle_dbg_qos_req,
PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
mtk_cpupm_node_init();
mtk_cpuidle_status_init();
mtk_cpc_init();
mtk_idle_procfs_init();
cpc_time_sync();
return 0;
}
static void __exit mtk_cpupm_dbg_exit(void)
{
mtk_idle_procfs_exit();
mtk_cpuidle_status_exit();
mtk_cpc_exit();
pm_qos_remove_request(&cpuidle_dbg_qos_req);
}
module_init(mtk_cpupm_dbg_init);
module_exit(mtk_cpupm_dbg_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MediaTek CPU Idle Debug FileSystem");
MODULE_AUTHOR("MediaTek Inc.");
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_CPUPM_DBG_H__
#define __MTK_CPUPM_DBG_H__
int mtk_cpupm_mcusys_write(int ofs, unsigned int val);
unsigned int mtk_cpupm_mcusys_read(int ofs);
int mtk_cpupm_syssram_write(int ofs, unsigned int val);
unsigned int mtk_cpupm_syssram_read(int ofs);
void mtk_cpupm_block(void);
void mtk_cpupm_allow(void);
int mtk_cpupm_get_idle_state_count(int cpu);
#endif /* __MTK_CPUPM_DBG_H__ */
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include <linux/cpuidle.h>
#include <mtk_lp_plat_reg.h>
#include <mtk_lpm.h>
#include <mt6853_spm_comm.h>
#include "mtk_cpupm_dbg.h"
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
#define MTK_IDLE_PROC_FS_NAME "cpuidle"
static int idle_proc_info_show(struct seq_file *m, void *v)
{
int cpu;
unsigned long long ts, rem;
ts = mtk_cpupm_syssram_read(SYSRAM_RECENT_CNT_TS_H);
ts = (ts << 32) | mtk_cpupm_syssram_read(SYSRAM_RECENT_CNT_TS_L);
ts = div64_u64_rem(ts, 1000 * 1000 * 1000, &rem);
seq_puts(m, "\n========= Power off count =========\n");
seq_puts(m, "\n-- Within 5 seconds before dumped --\n");
seq_printf(m, "\nLast dump timestamp = %llu.%09llu\n",
ts, rem);
seq_printf(m, "%8s", "cpu:");
for_each_possible_cpu(cpu)
seq_printf(m, " %d", mtk_cpupm_syssram_read(
SYSRAM_RECENT_CPU_CNT(cpu)));
seq_puts(m, "\n");
seq_printf(m, "%8s %d\n",
"cluster:",
mtk_cpupm_syssram_read(SYSRAM_RECENT_CPUSYS_CNT));
seq_printf(m, "%8s %d\n",
"mcusys:",
mtk_cpupm_syssram_read(SYSRAM_RECENT_MCUSYS_CNT));
seq_printf(m, "%8s 0x%x\n",
"online:",
mtk_cpupm_syssram_read(SYSRAM_CPU_ONLINE));
seq_puts(m, "\n---- Total ----\n");
seq_printf(m, "%8s %d\n",
"cluster:",
mtk_cpupm_syssram_read(SYSRAM_CPUSYS_CNT)
+ mtk_cpupm_syssram_read(SYSRAM_RECENT_CPUSYS_CNT));
seq_printf(m, "%8s %d\n",
"mcusys:",
mtk_cpupm_syssram_read(SYSRAM_MCUSYS_CNT)
+ mtk_cpupm_syssram_read(SYSRAM_RECENT_MCUSYS_CNT));
return 0;
}
static ssize_t idle_proc_info_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
return count;
}
static int idle_proc_enable_show(struct seq_file *m, void *v)
{
struct cpuidle_driver *drv;
int i, cpu, en = 0;
int suspend_type = mtk_lpm_suspend_type_get();
for_each_possible_cpu(cpu) {
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
for (i = 1; i < drv->state_count; i++) {
if ((suspend_type == MTK_LPM_SUSPEND_S2IDLE) &&
!strcmp(drv->states[i].name, S2IDLE_STATE_NAME))
continue;
en += mtk_cpuidle_get_param(drv, i, IDLE_PARAM_EN);
}
}
if (en == 0) {
seq_printf(m, "MCDI: Disable, %llu ms\n",
mtk_cpuidle_state_last_dis_ms());
} else {
seq_puts(m, "MCDI: Enable\n");
seq_puts(m,
"(cat /proc/cpuidle/state/enabled for more detail)\n");
}
return 0;
}
static ssize_t idle_proc_enable_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_state_enable(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
PROC_FOPS(info);
PROC_FOPS(enable);
int __init mtk_idle_procfs_init(void)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(info),
PROC_ENTRY(enable)
};
dir = proc_mkdir(MTK_IDLE_PROC_FS_NAME, NULL);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return -ENOMEM;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
mtk_idle_procfs_state_dir_init(dir);
mtk_idle_procfs_cpc_dir_init(dir);
mtk_idle_procfs_profile_dir_init(dir);
mtk_idle_procfs_control_dir_init(dir);
return 0;
}
void __exit mtk_idle_procfs_exit(void)
{
remove_proc_subtree(MTK_IDLE_PROC_FS_NAME, NULL);
}
@@ -0,0 +1,75 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_IDLE_PROCFS__
#define __MTK_IDLE_PROCFS__
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
struct idle_proc_entry {
const char *name;
const struct file_operations *fops;
};
#define PROC_FOPS(name) \
static int idle_proc_ ## name ## _open(struct inode *inode, struct file *file) \
{ \
return single_open(file, idle_proc_ ## name ## _show, PDE_DATA(inode));\
} \
static const struct file_operations idle_proc_ ## name ## _fops = { \
.owner = THIS_MODULE, \
.open = idle_proc_ ## name ## _open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
.write = idle_proc_ ## name ## _write, \
}
#define PROC_ENTRY(name) {__stringify(name), &idle_proc_ ## name ## _fops}
#define PROC_CREATE_NODE(dir, entry) \
do { \
if (!proc_create(entry.name, 0644, dir, entry.fops)) \
pr_notice("%s(), create procfs cpuidle node %s failed\n", \
__func__, entry.name); \
} while (0)
#define mtk_idle_procfs_alloc_from_user(buf, userbuf, count) \
do { \
buf = (char *)__get_free_page(GFP_USER); \
\
if (!buf) \
break; \
\
if (count >= PAGE_SIZE \
|| copy_from_user(buf, userbuf, count)) { \
free_page((unsigned long)buf); \
buf = NULL; \
break; \
} \
\
buf[count] = '\0'; \
\
} while (0)
#define mtk_idle_procfs_free(buf) \
do { \
if (!buf) \
break; \
\
free_page((unsigned long)buf); \
} while (0)
void __init mtk_idle_procfs_cpc_dir_init(struct proc_dir_entry *parent);
void __init mtk_idle_procfs_state_dir_init(struct proc_dir_entry *parent);
void __init mtk_idle_procfs_profile_dir_init(struct proc_dir_entry *parent);
void __init mtk_idle_procfs_control_dir_init(struct proc_dir_entry *parent);
int __init mtk_idle_procfs_init(void);
void __exit mtk_idle_procfs_exit(void);
#endif /* __MTK_IDLE_PROCFS__ */
@@ -0,0 +1,337 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include <mtk_lp_plat_apmcu_mbox.h>
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
static int idle_proc_log_show(struct seq_file *m, void *v)
{
seq_printf(m, "CPU idle log : %s\n",
mtk_cpuidle_ctrl_log_sta_get() ?
"Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/control/log\n");
return 0;
}
static ssize_t idle_proc_log_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_ctrl_log_en(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_timer_show(struct seq_file *m, void *v)
{
seq_printf(m, "Limit sleep timer : %s\n",
mtk_cpuidle_ctrl_timer_sta_get() ?
"Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/control/timer\n");
return 0;
}
static ssize_t idle_proc_timer_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_ctrl_timer_en(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_stress_show(struct seq_file *m, void *v)
{
seq_printf(m, "CPU idle stress : %s\n",
mtk_cpuidle_get_stress_status() ?
"Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/control/stress\n");
return 0;
}
static ssize_t idle_proc_stress_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_set_stress_test(!!enabled);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_stress_time_show(struct seq_file *m, void *v)
{
seq_printf(m, "CPU idle stress interval time : %u\n",
mtk_cpuidle_get_stress_time());
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [us] > /proc/cpuidle/control/stress_time\n");
return 0;
}
static ssize_t idle_proc_stress_time_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int interval_us = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &interval_us) != 0) {
ret = -EINVAL;
goto free;
}
mtk_cpuidle_set_stress_time(interval_us);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_buck_mode_show(struct seq_file *m, void *v)
{
int mode;
struct mode_param {
int id;
char *str;
};
const struct mode_param table[] = {
{MCUPM_BUCK_NORMAL_MODE, "on"},
{MCUPM_BUCK_LP_MODE, "low power"},
{MCUPM_BUCK_OFF_MODE, "off"},
{NF_MCUPM_BUCK_MODE, "unknown"},
};
mode = mtk_get_mcupm_buck_mode();
if (mode < 0 || mode > NF_MCUPM_BUCK_MODE)
mode = NF_MCUPM_BUCK_MODE;
seq_printf(m, "Vproc/Vproc_sram buck mode : %s\n",
table[mode].str);
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [mode] > /proc/cpuidle/control/buck_mode\n");
seq_puts(m, "mode:\n");
for (mode = 0; mode < NF_MCUPM_BUCK_MODE; mode++)
seq_printf(m, "\t%d: %s\n",
table[mode].id,
table[mode].str);
return 0;
}
static ssize_t idle_proc_buck_mode_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int mode = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &mode) != 0) {
ret = -EINVAL;
goto free;
}
if (mode < NF_MCUPM_BUCK_MODE)
mtk_set_mcupm_buck_mode(mode);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_armpll_mode_show(struct seq_file *m, void *v)
{
int mode;
struct mode_param {
int id;
char *str;
};
const struct mode_param table[] = {
{MCUPM_ARMPLL_ON, "on"},
{MCUPM_ARMPLL_GATING, "gating"},
{MCUPM_ARMPLL_OFF, "off"},
{NF_MCUPM_ARMPLL_MODE, "unknown"},
};
mode = mtk_get_mcupm_pll_mode();
if (mode < 0 || mode > NF_MCUPM_ARMPLL_MODE)
mode = NF_MCUPM_ARMPLL_MODE;
seq_printf(m, "armpll mode : %s\n",
table[mode].str);
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [mode] > /proc/cpuidle/control/armpll_mode\n");
seq_puts(m, "mode:\n");
for (mode = 0; mode < NF_MCUPM_ARMPLL_MODE; mode++)
seq_printf(m, "\t%d: %s\n",
table[mode].id,
table[mode].str);
return 0;
}
static ssize_t idle_proc_armpll_mode_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int mode = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &mode) != 0) {
ret = -EINVAL;
goto free;
}
if (mode < NF_MCUPM_ARMPLL_MODE)
mtk_set_mcupm_pll_mode(mode);
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_notify_cm_show(struct seq_file *m, void *v)
{
bool notify;
notify = mtk_mcupm_cm_is_notified();
seq_printf(m, "mcupm cm mgr : %s\n",
notify ? "Enable" : "Disable");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "Read Only : Not support dynamic control\n");
return 0;
}
static ssize_t idle_proc_notify_cm_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
return count;
}
PROC_FOPS(log);
PROC_FOPS(timer);
PROC_FOPS(stress);
PROC_FOPS(stress_time);
PROC_FOPS(buck_mode);
PROC_FOPS(armpll_mode);
PROC_FOPS(notify_cm);
void __init mtk_idle_procfs_control_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(log),
PROC_ENTRY(timer),
PROC_ENTRY(stress),
PROC_ENTRY(stress_time),
PROC_ENTRY(buck_mode),
PROC_ENTRY(armpll_mode),
PROC_ENTRY(notify_cm)
};
dir = proc_mkdir("control", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include "mtk_cpuidle_cpc.h"
#include "mtk_idle_procfs.h"
static int idle_proc_auto_off_show(struct seq_file *m, void *v)
{
seq_printf(m, "auto_off mode : %s\n",
cpc_get_auto_off_sta() ? "Enabled" : "Disabled");
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [0|1] > /proc/cpuidle/cpc/auto_off\n");
return 0;
}
static ssize_t idle_proc_auto_off_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
if (enabled)
cpc_auto_off_en();
else
cpc_auto_off_dis();
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_auto_off_thres_show(struct seq_file *m, void *v)
{
seq_printf(m, "auto_off threshold = %u us\n",
(unsigned int)cpc_get_auto_off_thres());
seq_puts(m, "\n======== Command Usage ========\n");
seq_puts(m, "echo [us] > /proc/cpuidle/cpc/auto_off_thres\n");
return 0;
}
static ssize_t idle_proc_auto_off_thres_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int threshold_us = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &threshold_us) != 0) {
ret = -EINVAL;
goto free;
}
cpc_set_auto_off_thres(threshold_us);
free:
mtk_idle_procfs_free(buf);
return ret;
}
PROC_FOPS(auto_off);
PROC_FOPS(auto_off_thres);
void __init mtk_idle_procfs_cpc_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(auto_off),
PROC_ENTRY(auto_off_thres)
};
dir = proc_mkdir("cpc", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,173 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/seq_file.h>
#include "mtk_cpuidle_cpc.h"
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
#include "mt6853_spm_comm.h"
static int idle_proc_kernel_show(struct seq_file *m, void *v)
{
seq_printf(m, "%s\n", __func__);
return 0;
}
static ssize_t idle_proc_kernel_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
/* Start/Stop kernel profile */
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_target_state_show(struct seq_file *m, void *v)
{
seq_printf(m, "%s\n", __func__);
return 0;
}
static ssize_t idle_proc_target_state_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
int state = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtoint(buf, 10, &state) != 0) {
ret = -EINVAL;
goto free;
}
/* Set target state */
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_ratio_show(struct seq_file *m, void *v)
{
mtk_cpuidle_prof_ratio_dump(m);
return 0;
}
static ssize_t idle_proc_ratio_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
if (enabled)
mtk_cpuidle_prof_ratio_start();
else
mtk_cpuidle_prof_ratio_stop();
free:
mtk_idle_procfs_free(buf);
return ret;
}
static int idle_proc_cpc_show(struct seq_file *m, void *v)
{
mtk_cpc_prof_lat_dump(m);
return 0;
}
static ssize_t idle_proc_cpc_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
ssize_t ret = count;
unsigned int enabled = 0;
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
if (kstrtouint(buf, 10, &enabled) != 0) {
ret = -EINVAL;
goto free;
}
if (enabled)
mtk_cpc_prof_start();
else
mtk_cpc_prof_stop();
free:
mtk_idle_procfs_free(buf);
return ret;
}
PROC_FOPS(kernel);
PROC_FOPS(target_state);
PROC_FOPS(ratio);
PROC_FOPS(cpc);
void __init mtk_idle_procfs_profile_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(kernel),
PROC_ENTRY(target_state),
PROC_ENTRY(ratio),
PROC_ENTRY(cpc)
};
dir = proc_mkdir("profile", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
@@ -0,0 +1,218 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/cpuidle.h>
#include <linux/seq_file.h>
#include "mtk_cpuidle_status.h"
#include "mtk_idle_procfs.h"
#define ALL_LITTLR_CPU_ID (10)
#define ALL_BIG_CPU_ID (20)
#define ALL_CPU_ID (100)
#define CPU_L_MASK (0x0f)
#define CPU_B_MASK (0xf0)
#define ALL_CPU_MASK (0xff)
static void idle_proc_state_uasge_print(struct seq_file *m, char *node_name)
{
seq_puts(m, "\n======== Command Usage ========\n");
seq_printf(m, "%s > /proc/cpuidle/state/%s\n",
"echo [cpu_id] [state_index] [val(dec)]",
node_name);
seq_puts(m, "\t cpu_id: 0~7 -> cpu number\n");
seq_printf(m, "\t %3d -> all little CPU\n", ALL_LITTLR_CPU_ID);
seq_printf(m, "\t %3d -> all big CPU\n", ALL_BIG_CPU_ID);
seq_printf(m, "\t %3d -> all CPU\n", ALL_CPU_ID);
seq_puts(m, "\t state_index: >0 (index 0 can't be modified)\n");
seq_puts(m, "\n");
}
static void idle_proc_state_param_show(struct seq_file *m, int param)
{
struct cpuidle_driver *drv;
int i, cpu, desc = 0;
for_each_possible_cpu(cpu) {
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
if (desc == 0) {
seq_printf(m, "%-12s:", "state_index");
for (i = 0; i < drv->state_count; i++)
seq_printf(m, "%7d ", i);
seq_puts(m, "\n");
desc = 1;
}
seq_printf(m, "%11s%d:", "cpu", cpu);
for (i = 0; i < drv->state_count; i++)
seq_printf(m, "%7d ",
mtk_cpuidle_get_param(drv, i, param));
seq_puts(m, "\n");
}
}
static int idle_proc_state_param_setting(char *cmd, int param)
{
struct cpuidle_driver *drv;
char *args;
unsigned int cpu_mask, state_idx = 0, val = 0;
int cpu = 0, i;
struct cmd_param {
unsigned int id;
unsigned int mask;
};
const struct cmd_param table[] = {
{ALL_LITTLR_CPU_ID, CPU_L_MASK},
{ALL_BIG_CPU_ID, CPU_B_MASK},
{ALL_CPU_ID, ALL_CPU_MASK},
};
args = strsep(&cmd, " ");
if (!args || kstrtoint(args, 10, &cpu) != 0)
return -EINVAL;
args = strsep(&cmd, " ");
if (!args || kstrtouint(args, 10, &state_idx) != 0)
return -EINVAL;
args = strsep(&cmd, " ");
if (!args || kstrtouint(args, 10, &val) != 0)
return -EINVAL;
if (!state_idx)
return -EINVAL;
cpu_mask = 0;
if (cpu >= 0 && cpu < nr_cpu_ids) {
cpu_mask = (1 << cpu);
} else {
for (i = 0; i < ARRAY_SIZE(table); i++) {
if (cpu == table[i].id) {
cpu_mask = table[i].mask;
break;
}
}
}
for_each_possible_cpu(cpu) {
if ((cpu_mask & (1 << cpu)) == 0)
continue;
drv = cpuidle_get_cpu_driver(per_cpu(cpuidle_devices, cpu));
if (!drv)
continue;
mtk_cpuidle_set_param(drv, state_idx, param, val);
}
return 0;
}
static int idle_proc_latency_show(struct seq_file *m, void *v)
{
seq_puts(m, "==== CPU idle state: Exit latency ====\n");
idle_proc_state_param_show(m, IDLE_PARAM_LAT);
idle_proc_state_uasge_print(m, "latency");
return 0;
}
static ssize_t idle_proc_latency_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
idle_proc_state_param_setting(buf, IDLE_PARAM_LAT);
mtk_idle_procfs_free(buf);
return count;
}
static int idle_proc_residency_show(struct seq_file *m, void *v)
{
seq_puts(m, "==== CPU idle state: Target residency ====\n");
idle_proc_state_param_show(m, IDLE_PARAM_RES);
idle_proc_state_uasge_print(m, "residency");
return 0;
}
static ssize_t idle_proc_residency_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
idle_proc_state_param_setting(buf, IDLE_PARAM_RES);
mtk_idle_procfs_free(buf);
return count;
}
static int idle_proc_enabled_show(struct seq_file *m, void *v)
{
seq_puts(m, "==== CPU idle state: Enabled ====\n");
idle_proc_state_param_show(m, IDLE_PARAM_EN);
idle_proc_state_uasge_print(m, "enabled");
return 0;
}
static ssize_t idle_proc_enabled_write(struct file *filp,
const char __user *userbuf, size_t count, loff_t *f_pos)
{
char *buf;
mtk_idle_procfs_alloc_from_user(buf, userbuf, count);
if (!buf)
return -EINVAL;
idle_proc_state_param_setting(buf, IDLE_PARAM_EN);
mtk_idle_procfs_free(buf);
return count;
}
PROC_FOPS(latency);
PROC_FOPS(residency);
PROC_FOPS(enabled);
void __init mtk_idle_procfs_state_dir_init(struct proc_dir_entry *parent)
{
int i;
struct proc_dir_entry *dir = NULL;
const struct idle_proc_entry entries[] = {
PROC_ENTRY(latency),
PROC_ENTRY(residency),
PROC_ENTRY(enabled)
};
dir = proc_mkdir("state", parent);
if (!dir) {
pr_notice("fail to create procfs @ %s()\n", __func__);
return;
}
for (i = 0; i < ARRAY_SIZE(entries); i++)
PROC_CREATE_NODE(dir, entries[i]);
}
Loaded 100 of 80519 files, more files were not shown because too many files have changed in this diff. Show more