unplugged-kernel/drivers/misc/mediatek/base/power/spm/mt6768/mtk_spm.c

363 lines
9.0 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/pm_wakeup.h>
#include <linux/debugfs.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/suspend.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <mtk_cpuidle.h> /* mtk_cpuidle_init */
#include <mtk_sleep.h> /* slp_module_init */
#include <mt-plat/aee.h> /* aee_xxx */
#include <mtk_spm_irq.h>
#include <mtk_spm_internal.h>
#include <mtk_sspm.h>
#include <mtk_idle_fs/mtk_idle_sysfs.h>
DEFINE_SPINLOCK(__spm_lock);
/* Note: implemented in mtk_spm_dram.c */
int __attribute__ ((weak)) spm_get_spmfw_idx(void)
{
aee_sram_printk("NO %s !!!\n", __func__);
pr_info("[SPM] NO %s !!!\n", __func__);
return 1;
}
/* Note: implemented in mtk_spm_irq.c */
int __attribute__ ((weak)) mtk_spm_irq_register(unsigned int spmirq0)
{
aee_sram_printk("NO %s !!!\n", __func__);
pr_info("[SPM] NO %s !!!\n", __func__);
return 0;
}
/* Note: implemented in mtk_cpuidle.c */
int __attribute__ ((weak)) mtk_cpuidle_init(void) { return -EOPNOTSUPP; }
/* Note: implemented in mtk_spm_dram.c */
void __attribute__((weak)) spm_do_dram_config_check(void)
{
aee_sram_printk("NO %s !!!\n", __func__);
pr_info("[SPM] NO %s !!!\n", __func__);
}
/* Note: implemented in mtk_spm_fs.c */
int __attribute__((weak)) spm_fs_init(void)
{
aee_sram_printk("NO %s !!!\n", __func__);
pr_info("[SPM] NO %s !!!\n", __func__);
return 0;
}
/* Note: implemented in mtk_spm_utils.c */
ssize_t __attribute__((weak)) get_spm_last_wakeup_src(
char *ToUserBuf, size_t sz, void *priv) { return 0; }
/* Note: implemented in mtk_spm_utils.c */
ssize_t __attribute__((weak)) get_spm_sleep_count(
char *ToUserBuf, size_t sz, void *priv) { return 0; }
/* Note: implemented in mtk_spm_utils.c */
ssize_t __attribute__((weak)) get_spm_last_debug_flag(
char *ToUserBuf, size_t sz, void *priv) { return 0; }
/* Note: implemented in mtk_spm_utils.c */
ssize_t __attribute__((weak)) get_spmfw_version(
char *ToUserBuf, size_t sz, void *priv) { return 0; }
void __iomem *spm_base;
void __iomem *sleep_reg_md_base;
static struct platform_device *pspmdev;
static struct wakeup_source *spm_wakelock;
/* FIXME: should not used externally !!! */
void *mt_spm_base_get(void)
{
return spm_base;
}
EXPORT_SYMBOL(mt_spm_base_get);
void spm_pm_stay_awake(int sec)
{
__pm_wakeup_event(spm_wakelock, HZ * sec);
}
static void spm_register_init(unsigned int *spm_irq_0_ptr)
{
struct device_node *node;
unsigned int spmirq0;
node = of_find_compatible_node(NULL, NULL, "mediatek,sleep");
if (!node)
pr_info("[SPM] find sleep node failed\n");
spm_base = of_iomap(node, 0);
if (!spm_base)
pr_info("[SPM] base spm_base failed\n");
spmirq0 = irq_of_parse_and_map(node, 0);
if (!spmirq0)
pr_info("[SPM] get spm_irq_0 failed\n");
*spm_irq_0_ptr = spmirq0;
node = of_find_compatible_node(NULL, NULL, "mediatek,sleep_reg_md");
if (!node)
pr_info("[SPM] find sleep_reg_md node failed\n");
sleep_reg_md_base = of_iomap(node, 0);
if (!sleep_reg_md_base)
pr_info("[SPM] base sleep_reg_md_base failed\n");
pr_info("[SPM] spm_base = %p, sleep_reg_md_base = %p, spm_irq_0 = %d\n",
spm_base, sleep_reg_md_base, spmirq0);
}
static ssize_t show_debug_log(
struct device *dev, struct device_attribute *attr,
char *buf)
{
char *p = buf;
p += sprintf(p, "for test\n");
return p - buf;
}
static ssize_t store_debug_log(
struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
return size;
}
static DEVICE_ATTR(debug_log, 0664, show_debug_log, store_debug_log);
static int spm_probe(struct platform_device *pdev)
{
int ret;
ret = device_create_file(&(pdev->dev), &dev_attr_debug_log);
return 0;
}
static int spm_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id spm_of_ids[] = {
{.compatible = "mediatek,SLEEP",},
{}
};
static struct platform_driver spm_dev_drv = {
.probe = spm_probe,
.remove = spm_remove,
.driver = {
.name = "spm",
.owner = THIS_MODULE,
.of_match_table = spm_of_ids,
},
};
#if !defined(CONFIG_FPGA_EARLY_PORTING)
#ifdef CONFIG_PM
static int spm_pm_event(struct notifier_block *notifier, unsigned long pm_event,
void *unused)
{
struct timespec ts;
struct rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec, &tm);
#if 0 /* Avoid race condition between Suspend sync and Idle async IPI cmd */
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
struct spm_data spm_d;
int ret;
unsigned long flags;
#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
#endif
switch (pm_event) {
case PM_HIBERNATION_PREPARE:
return NOTIFY_DONE;
case PM_RESTORE_PREPARE:
return NOTIFY_DONE;
case PM_POST_HIBERNATION:
return NOTIFY_DONE;
case PM_SUSPEND_PREPARE:
#if 0 /* Avoid race condition between Suspend sync and Idle async IPI cmd */
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
spin_lock_irqsave(&__spm_lock, flags);
ret = spm_to_sspm_command(SPM_SUSPEND_PREPARE, &spm_d);
spin_unlock_irqrestore(&__spm_lock, flags);
if (ret < 0) {
printk_deferred("[name:spm&]#@# %s(%d) PM_SUSPEND_PREPARE return %d!!!\n",
__func__, __LINE__, ret);
return NOTIFY_BAD;
}
#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
#endif
printk_deferred(
"[name:spm&][SPM] PM: suspend entry %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
return NOTIFY_DONE;
case PM_POST_SUSPEND:
#if 0 /* Avoid race condition between Suspend sync and Idle async IPI cmd */
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
spin_lock_irqsave(&__spm_lock, flags);
ret = spm_to_sspm_command(SPM_POST_SUSPEND, &spm_d);
spin_unlock_irqrestore(&__spm_lock, flags);
if (ret < 0) {
printk_deferred("[name:spm&]#@# %s(%d) PM_POST_SUSPEND return %d!!!\n",
__func__, __LINE__, ret);
return NOTIFY_BAD;
}
#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
#endif
printk_deferred(
"[name:spm&][SPM] PM: suspend exit %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
return NOTIFY_DONE;
}
return NOTIFY_OK;
}
static struct notifier_block spm_pm_notifier_func = {
.notifier_call = spm_pm_event,
.priority = 0,
};
#endif /* CONFIG_PM */
#endif /* CONFIG_FPGA_EARLY_PORTING */
static const struct mtk_idle_sysfs_op spm_last_wakeup_src_fops = {
.fs_read = get_spm_last_wakeup_src,
};
static const struct mtk_idle_sysfs_op spm_sleep_count_fops = {
.fs_read = get_spm_sleep_count,
};
static const struct mtk_idle_sysfs_op spm_last_debug_flag_fops = {
.fs_read = get_spm_last_debug_flag,
};
static const struct mtk_idle_sysfs_op spm_spmfw_version_fops = {
.fs_read = get_spmfw_version,
};
static int spm_module_init(void)
{
unsigned int spm_irq_0 = 0;
int r = 0;
int ret = -1;
struct mtk_idle_sysfs_handle pParent2ND;
struct mtk_idle_sysfs_handle *pParent = NULL;
spm_wakelock = wakeup_source_register(NULL, "spm");
spm_register_init(&spm_irq_0);
/* implemented in mtk_spm_irq.c */
if (mtk_spm_irq_register(spm_irq_0) != 0)
r = -EPERM;
#if defined(CONFIG_PM)
if (spm_fs_init() != 0)
r = -EPERM;
#endif
/* implemented in mtk_spm_dram.c */
spm_do_dram_config_check();
ret = platform_driver_register(&spm_dev_drv);
if (ret) {
pr_debug("fail to register platform driver\n");
return ret;
}
pspmdev = platform_device_register_simple("spm", -1, NULL, 0);
if (IS_ERR(pspmdev)) {
pr_debug("Failed to register platform device.\n");
return -EINVAL;
}
mtk_idle_sysfs_entry_create();
if (mtk_idle_sysfs_entry_root_get(&pParent) == 0) {
mtk_idle_sysfs_entry_func_create("spm", 0444
, pParent, &pParent2ND);
mtk_idle_sysfs_entry_func_node_add("spm_sleep_count", 0444
, &spm_sleep_count_fops, &pParent2ND, NULL);
mtk_idle_sysfs_entry_func_node_add("spm_last_wakeup_src", 0444
, &spm_last_wakeup_src_fops, &pParent2ND, NULL);
mtk_idle_sysfs_entry_func_node_add("spm_last_debug_flag", 0444
, &spm_last_debug_flag_fops, &pParent2ND, NULL);
mtk_idle_sysfs_entry_func_node_add("spmfw_version", 0444
, &spm_spmfw_version_fops, &pParent2ND, NULL);
}
#if !defined(CONFIG_FPGA_EARLY_PORTING)
#ifdef CONFIG_PM
ret = register_pm_notifier(&spm_pm_notifier_func);
if (ret) {
pr_debug("Failed to register PM notifier.\n");
return ret;
}
#endif /* CONFIG_PM */
#endif /* CONFIG_FPGA_EARLY_PORTING */
SMC_CALL(ARGS, SPM_ARGS_SPMFW_IDX, spm_get_spmfw_idx(), 0);
return 0;
}
int mtk_spm_init(void)
{
int ret;
mtk_cpuidle_init();
ret = spm_module_init();
return ret;
}
bool mtk_spm_base_ready(void)
{
return spm_base != 0;
}
unsigned int mtk_spm_read_register(int register_index)
{
if (register_index == SPM_PWRSTA)
return spm_read(PWR_STATUS);
else if (register_index == SPM_MD1_PWR_CON)
return spm_read(MD1_PWR_CON);
else if (register_index == SPM_REG13)
return spm_read(PCM_REG13_DATA);
else if (register_index == SPM_SPARE_ACK_MASK)
return spm_read(SPARE_ACK_MASK);
else
return 0;
}
MODULE_DESCRIPTION("SPM Driver v0.1");