| #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); |