blob: 609cde70125ae5a906118abdee47ff17d92d87c0 [file] [log] [blame]
#include <linux/cpuidle.h>
#include <linux/cpu_pm.h>
#include <linux/clk/dvfs-dvc.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/edge_wakeup_mmp.h>
#include <linux/slab.h>
#include <soc/asr/regs-addr.h>
#include <linux/cputype.h>
#include <asm/cpuidle.h>
#include <asm/mach/map.h>
#include <asm/mcpm.h>
#include <soc/asr/mmp_cpuidle.h>
#ifdef CONFIG_CPU_ASR1901
#include <soc/asr/asr1901_lowpower.h>
#else
#include <soc/asr/asr18xx_lowpower.h>
#endif
#include "addr-map.h"
#include <linux/arm-smccc.h>
#include <linux/of_address.h>
#include <linux/asr_tee_sip.h>
extern int __init asr_platform_power_register(struct platform_idle *idle);
#ifdef CONFIG_CPU_ASR1901
#define ASR_CPUIDLE_FLAG CPUIDLE_FLAG_NONE
#else
#define ASR_CPUIDLE_FLAG CPUIDLE_FLAG_TIMER_STOP
#endif
static struct cpuidle_state asr_modes[] = {
[0] = {
.exit_latency = 18,
.target_residency = 36,
/*
* Use CPUIDLE_FLAG_TIMER_STOP flag to let the cpuidle
* framework handle the CLOCK_EVENT_NOTIFY_BROADCAST_
* ENTER/EXIT when entering idle states.
*/
.flags = /* CPUIDLE_FLAG_TIME_VALID */0,
.name = "C1",
.desc = "C1: Core internal clock gated",
.enter = arm_cpuidle_simple_enter,
},
[1] = {
.exit_latency = 350,
.target_residency = 700,
.flags = ASR_CPUIDLE_FLAG,
.name = "C2",
.desc = "C2: Core power down",
},
[2] = {
.exit_latency = 500,
.target_residency = 1000,
.flags = ASR_CPUIDLE_FLAG,
.name = "D1p",
.desc = "D1p: AP idle state",
},
[3] = {
.exit_latency = 600,
.target_residency = 1200,
.flags = ASR_CPUIDLE_FLAG,
.name = "D1",
.desc = "D1: Chip idle state",
},
};
static int need_restore_pad_wakeup;
#ifdef CONFIG_CPU_ASR1901
#define EDGE_WAKEUP_GIC 39 /* GIC interrupt num for edge wakeup */
extern void extern_gic_mask_unmask_irq(int irq_num, bool mask);
static void edge_wakeup_gic_enable(void)
{
extern_gic_mask_unmask_irq(EDGE_WAKEUP_GIC, 0);
}
static void edge_wakeup_gic_disable(void)
{
extern_gic_mask_unmask_irq(EDGE_WAKEUP_GIC, 1);
}
#endif
void asr_set_pmu(u32 cpu, u32 power_mode)
{
(void)cpu;
/* TODO: move to TOS next step */
if(power_mode > POWER_MODE_CORE_POWERDOWN) {
edge_wakeup_mfp_enable();
#ifdef CONFIG_CPU_ASR1901
edge_wakeup_gic_enable();
#endif
need_restore_pad_wakeup = 1;
}
}
void asr_clr_pmu(u32 cpu)
{
/* TODO: move to TOS next step */
if(need_restore_pad_wakeup) {
edge_wakeup_mfp_disable();
#ifdef CONFIG_CPU_ASR1901
edge_wakeup_gic_disable();
#endif
need_restore_pad_wakeup = 0;
}
}
static struct platform_power_ops asr_power_ops = {
.set_pmu = asr_set_pmu,
.clr_pmu = asr_clr_pmu,
.save_wakeup = NULL,
.restore_wakeup = NULL,
.power_up_setup = ca7_power_up_setup,
.cpu_power_status = NULL,
};
static struct platform_idle asr_idle = {
.cpudown_state = POWER_MODE_CORE_POWERDOWN,
.wakeup_state = POWER_MODE_SYS_SLEEP,
.hotplug_state = POWER_MODE_UDR,
.l2_flush_state = POWER_MODE_UDR,
.ops = &asr_power_ops,
.states = asr_modes,
.state_count = ARRAY_SIZE(asr_modes),
};
static int __init asr_lowpower_init(void)
{
asr_platform_power_register(&asr_idle);
#ifdef CONFIG_ARM_ERRATA_802022
{
u32 mp_idle_cfg;
int i;
for_each_possible_cpu(i) {
mp_idle_cfg = __raw_readl(APMU_MP_IDLE_CFG[i]);
mp_idle_cfg |= (PMUA_DIS_MP_SLP);
__raw_writel(mp_idle_cfg, APMU_MP_IDLE_CFG[i]);
}
}
#endif
return 0;
}
early_initcall(asr_lowpower_init);