| /* |
| * linux/arch/arm/mach-mmp/pm.c |
| * |
| * Author: Fan Wu <fwu@marvell.com> |
| * Copyright: (C) 2012 Marvell International Ltd. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| */ |
| |
| #include <asm/cputype.h> |
| #include <linux/irq.h> |
| #include <linux/interrupt.h> |
| #include <asm/mcpm.h> |
| #include <linux/suspend.h> |
| #include <asm/suspend.h> |
| #include <soc/asr/asr18xx_lowpower.h> |
| #include <linux/irqchip/arm-gic.h> |
| #include <linux/irqchip/mmp.h> |
| #include <linux/cpu_pm.h> |
| #include "irqs.h" |
| #include "pm.h" |
| #include <soc/asr/mmp_cpuidle.h> |
| |
| struct platform_suspend *mmp_suspend; |
| u32 detect_wakeup_status = 0; |
| EXPORT_SYMBOL(detect_wakeup_status); |
| |
| #if !defined(CONFIG_CPU_ASR18XX) |
| static int notrace mcpm_powerdown_finisher(unsigned long arg) |
| { |
| u32 mpidr = read_cpuid_mpidr(); |
| u32 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); |
| u32 this_cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); |
| |
| mcpm_set_entry_vector(cpu, this_cluster, cpu_resume); |
| |
| asr_set_target_lpm_state(cpu, this_cluster, *((int *)arg), (int *)arg); |
| |
| mcpm_cpu_suspend(); |
| return 1; |
| } |
| #endif |
| |
| #if defined(CONFIG_CPU_ASR18XX) |
| extern void asr18xx_pm_power_down(void); |
| extern void asr18xx_pm_powered_up(void); |
| extern void asr18xx_dcstat(unsigned long addr, unsigned int flag); |
| extern int asr18xx_pm_check_constraint(void); |
| static int notrace mmp_suspend_finish(unsigned long val) |
| { |
| asr18xx_pm_power_down(); |
| return 1; |
| } |
| #endif |
| |
| static int mmp_pm_enter(suspend_state_t state) |
| { |
| unsigned int real_idx = mmp_suspend->suspend_state; |
| |
| if (mmp_suspend->ops->pre_suspend_check) { |
| if (mmp_suspend->ops->pre_suspend_check()) |
| return -EAGAIN; |
| } |
| #if !defined(CONFIG_CPU_ASR18XX) |
| cpu_suspend((unsigned long)&real_idx, mcpm_powerdown_finisher); |
| #else |
| cpu_suspend((unsigned long)&real_idx, mmp_suspend_finish); |
| #endif |
| if (mmp_suspend->ops->post_chk_wakeup) |
| detect_wakeup_status = mmp_suspend->ops->post_chk_wakeup(); |
| |
| if (mmp_suspend->ops->post_clr_wakeup) |
| mmp_suspend->ops->post_clr_wakeup(detect_wakeup_status); |
| |
| if (real_idx != mmp_suspend->suspend_state) |
| pr_info("WARNING!!! " |
| "Suspend Didn't enter the Expected Low power mode\n"); |
| #if !defined(CONFIG_CPU_ASR18XX) |
| mcpm_cpu_powered_up(); |
| #else |
| asr18xx_pm_powered_up(); |
| #endif |
| return 0; |
| } |
| |
| static int mmp_pm_valid(suspend_state_t state) |
| { |
| return ((state == PM_SUSPEND_STANDBY) || (state == PM_SUSPEND_MEM)); |
| } |
| |
| extern void asr_clear_wakeup_event_idx(void); |
| /* Called after devices suspend, before noirq devices suspend */ |
| static int mmp_pm_prepare(void) |
| { |
| #ifdef CONFIG_CPU_ASR18XX |
| asr18xx_dcstat(0, 0); |
| #endif |
| asr_clear_wakeup_event_idx(); |
| return 0; |
| } |
| |
| /* Called after noirq devices resume, before devices resume */ |
| static void mmp_pm_finish(void) |
| { |
| } |
| |
| /* Called after Non-boot CPUs have been Hotplug in if necessary */ |
| static void mmp_pm_wake(void) |
| { |
| } |
| |
| static const struct platform_suspend_ops mmp_pm_ops = { |
| .valid = mmp_pm_valid, |
| .prepare = mmp_pm_prepare, |
| .enter = mmp_pm_enter, |
| .finish = mmp_pm_finish, |
| .wake = mmp_pm_wake, |
| }; |
| |
| int mmp_set_wake(struct irq_data *data, unsigned int on) |
| { |
| int irq = (int)data->hwirq; |
| struct irq_desc *desc = irq_to_desc(data->irq); |
| |
| if (!mmp_suspend->ops->set_wake) { |
| WARN_ON("Platform set_wake function is not Existed!!"); |
| return -EINVAL; |
| } |
| if (!desc) { |
| pr_err("irq_desc is NULL\n"); |
| return -EINVAL; |
| } |
| if (on) { |
| if (desc->action) |
| desc->action->flags |= IRQF_NO_SUSPEND; |
| } else { |
| if (desc->action) |
| desc->action->flags &= ~IRQF_NO_SUSPEND; |
| } |
| |
| mmp_suspend->ops->set_wake(irq, on); |
| |
| return 0; |
| } |
| |
| int __init mmp_platform_suspend_register(struct platform_suspend *plt_suspend) |
| { |
| if (mmp_suspend) |
| return -EBUSY; |
| |
| mmp_suspend = plt_suspend; |
| |
| suspend_set_ops(&mmp_pm_ops); |
| #ifndef CONFIG_CPU_ASR18XX |
| gic_arch_extn.irq_set_wake = mmp_set_wake; |
| #else |
| icu_irq_chip.irq_set_wake = mmp_set_wake; |
| #endif |
| if (mmp_suspend->ops->plt_suspend_init) |
| mmp_suspend->ops->plt_suspend_init(); |
| |
| return 0; |
| } |