blob: 985deb5d68edad3f2e3aec842c73dd7e84137ac7 [file] [log] [blame]
/*
* arch/arm/plat-ambarella/generic/pm.c
* Power Management Routines
* Author: Anthony Ginger <hfjiang@ambarella.com>
*
* Copyright (C) 2004-2009, Ambarella, Inc.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/suspend.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/cpu.h>
#include <linux/power_supply.h>
#include <asm/cacheflush.h>
#include <asm/io.h>
#include <asm/system.h>
#include <mach/board.h>
#include <mach/hardware.h>
#include <mach/init.h>
#include <mach/system.h>
#include <hal/hal.h>
#include <linux/mfd/tps6586x.h>
/* ==========================================================================*/
#ifdef MODULE_PARAM_PREFIX
#undef MODULE_PARAM_PREFIX
#endif
#define MODULE_PARAM_PREFIX "ambarella_config."
/* ==========================================================================*/
static int pm_debug_enable_timer_irq = 0;
module_param(pm_debug_enable_timer_irq, int, 0644);
static int pm_check_power_supply = 1;
module_param(pm_check_power_supply, int, 0644);
static int pm_force_power_on = 0;
module_param(pm_force_power_on, int, 0644);
/* ==========================================================================*/
void ambarella_power_off(void)
{
#ifdef CONFIG_PMIC_AMBARELLA_TPS6586X
if(ambarella_board_generic.board_rev == 'D'){
tps6586_powerdown();
}
#endif
if (ambarella_board_generic.power_control.gpio_id >= 0) {
ambarella_set_gpio_output(
&ambarella_board_generic.power_control, 0);
} else {
rct_power_down();
}
}
void ambarella_power_off_prepare(void)
{
}
/* ==========================================================================*/
static int ambarella_pm_notify(unsigned long val)
{
int retval = 0;
retval = notifier_to_errno(ambarella_set_event(val, NULL));
if (retval)
pr_debug("%s: 0x%08lx fail(%d)\n",__func__, val, retval);
return retval;
}
static int ambarella_pm_notify_raw(unsigned long val)
{
int retval = 0;
retval = notifier_to_errno(ambarella_set_raw_event(val, NULL));
if (retval)
pr_debug("%s: 0x%08lx fail(%d)\n",__func__, val, retval);
return retval;
}
static int ambarella_pm_pre(unsigned long *irqflag, u32 bsuspend, u32 tm_level)
{
int retval = 0;
if (irqflag)
local_irq_save(*irqflag);
if (bsuspend) {
ambarella_adc_suspend(0);
ambarella_timer_suspend(tm_level);
ambarella_irq_suspend(0);
ambarella_gpio_suspend(0);
ambarella_pll_suspend(0);
}
retval = ambarella_pm_notify_raw(AMBA_EVENT_PRE_PM);
return retval;
}
static int ambarella_pm_post(unsigned long *irqflag, u32 bresume, u32 tm_level)
{
int retval = 0;
retval = ambarella_pm_notify_raw(AMBA_EVENT_POST_PM);
if (bresume) {
ambarella_pll_resume(0);
ambarella_gpio_resume(0);
ambarella_irq_resume(0);
ambarella_timer_resume(tm_level);
ambarella_adc_resume(0);
}
if (irqflag)
local_irq_restore(*irqflag);
return retval;
}
static int ambarella_pm_check(suspend_state_t state)
{
int retval = 0;
retval = ambarella_pm_notify_raw(AMBA_EVENT_CHECK_PM);
if (retval)
goto ambarella_pm_check_exit;
retval = ambarella_pm_notify(AMBA_EVENT_CHECK_PM);
if (retval)
goto ambarella_pm_check_exit;
ambarella_pm_check_exit:
return retval;
}
/* ==========================================================================*/
static int ambarella_pm_enter_standby(void)
{
int retval = 0;
struct irq_desc *pm_desc = NULL;
struct irq_chip *pm_chip = NULL;
unsigned long flags;
if (ambarella_pm_pre(&flags, 1, 1))
BUG();
if (pm_debug_enable_timer_irq) {
pm_desc = irq_to_desc(TIMER1_IRQ);
if (pm_desc)
pm_chip = get_irq_desc_chip(pm_desc);
if (pm_chip && pm_chip->irq_shutdown)
pm_chip->irq_shutdown(&pm_desc->irq_data);
amba_clrbitsl(TIMER_CTR_REG, TIMER_CTR_EN1);
amba_writel(TIMER1_STATUS_REG, 0x800000);
amba_writel(TIMER1_RELOAD_REG, 0x800000);
amba_writel(TIMER1_MATCH1_REG, 0x0);
amba_writel(TIMER1_MATCH2_REG, 0x0);
amba_setbitsl(TIMER_CTR_REG, TIMER_CTR_OF1);
amba_clrbitsl(TIMER_CTR_REG, TIMER_CTR_CSL1);
}
if (pm_debug_enable_timer_irq) {
if (pm_chip && pm_chip->irq_startup)
pm_chip->irq_startup(&pm_desc->irq_data);
amba_setbitsl(TIMER_CTR_REG, TIMER_CTR_EN1);
}
cpu_do_idle();
if (pm_debug_enable_timer_irq) {
amba_clrbitsl(TIMER_CTR_REG, TIMER_CTR_EN1);
if (pm_chip && pm_chip->irq_shutdown)
pm_chip->irq_shutdown(&pm_desc->irq_data);
}
if (ambarella_pm_post(&flags, 1, 1))
BUG();
return retval;
}
static int ambarella_pm_enter_mem(void)
{
int retval = 0;
unsigned long flags;
#if defined(CONFIG_AMBARELLA_SUPPORT_BAPI)
struct ambarella_bapi_reboot_info_s reboot_info;
#endif
if (ambarella_pm_pre(&flags, 1, 1))
BUG();
#if defined(CONFIG_AMBARELLA_SUPPORT_BAPI)
reboot_info.magic = DEFAULT_BAPI_REBOOT_MAGIC;
reboot_info.mode = AMBARELLA_BAPI_CMD_REBOOT_SELFREFERESH;
retval = ambarella_bapi_cmd(AMBARELLA_BAPI_CMD_SET_REBOOT_INFO,
&reboot_info);
if (retval)
goto ambarella_pm_enter_mem_exit_bapi;
retval = ambarella_bapi_cmd(AMBARELLA_BAPI_CMD_AOSS_SAVE, NULL);
ambarella_pm_enter_mem_exit_bapi:
#endif
if (ambarella_pm_post(&flags, 1, 1))
BUG();
return retval;
}
static int ambarella_pm_suspend_valid(suspend_state_t state)
{
int retval = 0;
int valid = 0;
#if defined(CONFIG_AMBARELLA_SUPPORT_BAPI)
int mode;
#endif
retval = ambarella_pm_check(state);
if (retval)
goto ambarella_pm_valid_exit;
switch (state) {
case PM_SUSPEND_ON:
valid = 1;
break;
case PM_SUSPEND_STANDBY:
valid = 1;
break;
case PM_SUSPEND_MEM:
#if defined(CONFIG_AMBARELLA_SUPPORT_BAPI)
mode = AMBARELLA_BAPI_REBOOT_SELFREFERESH;
if (ambarella_bapi_cmd(AMBARELLA_BAPI_CMD_CHECK_REBOOT,
&mode) == 1) {
valid = 1;
}
#endif
if (pm_check_power_supply &&
(power_supply_is_system_supplied() > 0))
valid = 0;
if (pm_force_power_on)
valid = 0;
break;
default:
break;
}
ambarella_pm_valid_exit:
pr_debug("%s: state[%d]=%d\n", __func__, state, valid);
return valid;
}
static int ambarella_pm_suspend_begin(suspend_state_t state)
{
int retval = 0;
switch (state) {
case PM_SUSPEND_STANDBY:
case PM_SUSPEND_MEM:
retval = ambarella_pm_notify(AMBA_EVENT_PRE_PM);
break;
default:
break;
}
return retval;
}
static int ambarella_pm_suspend_enter(suspend_state_t state)
{
int retval = 0;
switch (state) {
case PM_SUSPEND_ON:
break;
case PM_SUSPEND_STANDBY:
retval = ambarella_pm_enter_standby();
break;
case PM_SUSPEND_MEM:
retval = ambarella_pm_enter_mem();
break;
default:
break;
}
return retval;
}
static void ambarella_pm_suspend_end(void)
{
ambarella_pm_notify(AMBA_EVENT_POST_PM);
}
static struct platform_suspend_ops ambarella_pm_suspend_ops = {
.valid = ambarella_pm_suspend_valid,
.begin = ambarella_pm_suspend_begin,
.enter = ambarella_pm_suspend_enter,
.end = ambarella_pm_suspend_end,
};
/* ==========================================================================*/
static int ambarella_pm_hibernation_begin(void)
{
int retval = -1;
#if defined(CONFIG_AMBARELLA_SUPPORT_BAPI)
int mode;
mode = AMBARELLA_BAPI_REBOOT_HIBERNATE;
if (ambarella_bapi_cmd(AMBARELLA_BAPI_CMD_CHECK_REBOOT, &mode) != 1) {
goto ambarella_pm_hibernation_begin_exit;
}
#endif
retval = ambarella_pm_notify(AMBA_EVENT_PRE_PM);
#if defined(CONFIG_AMBARELLA_SUPPORT_BAPI)
ambarella_pm_hibernation_begin_exit:
#endif
return retval;
}
static void ambarella_pm_hibernation_end(void)
{
ambarella_pm_notify(AMBA_EVENT_POST_PM);
}
static int ambarella_pm_hibernation_pre_snapshot(void)
{
int retval = 0;
retval = ambarella_pm_pre(NULL, 1, 0);
return retval;
}
static void ambarella_pm_hibernation_finish(void)
{
}
static int ambarella_pm_hibernation_prepare(void)
{
int retval = 0;
return retval;
}
static int ambarella_pm_hibernation_enter(void)
{
int retval = 0;
ambarella_power_off();
return retval;
}
static void ambarella_pm_hibernation_leave(void)
{
ambarella_pm_post(NULL, 1, 0);
}
static int ambarella_pm_hibernation_pre_restore(void)
{
int retval = 0;
return retval;
}
static void ambarella_pm_hibernation_restore_cleanup(void)
{
}
static void ambarella_pm_hibernation_restore_recover(void)
{
}
static struct platform_hibernation_ops ambarella_pm_hibernation_ops = {
.begin = ambarella_pm_hibernation_begin,
.end = ambarella_pm_hibernation_end,
.pre_snapshot = ambarella_pm_hibernation_pre_snapshot,
.finish = ambarella_pm_hibernation_finish,
.prepare = ambarella_pm_hibernation_prepare,
.enter = ambarella_pm_hibernation_enter,
.leave = ambarella_pm_hibernation_leave,
.pre_restore = ambarella_pm_hibernation_pre_restore,
.restore_cleanup = ambarella_pm_hibernation_restore_cleanup,
.recover = ambarella_pm_hibernation_restore_recover,
};
/* ==========================================================================*/
int __init ambarella_init_pm(void)
{
pm_power_off = ambarella_power_off;
pm_power_off_prepare = ambarella_power_off_prepare;
suspend_set_ops(&ambarella_pm_suspend_ops);
hibernation_set_ops(&ambarella_pm_hibernation_ops);
return 0;
}