/*
* OMAP3 Power Management Routines
*
* Copyright (C) 2006-2008 Nokia Corporation
* Tony Lindgren <tony@atomide.com>
* Jouni Hogander
*
* Copyright (C) 2007 Texas Instruments, Inc.
* Rajendra Nayak <rnayak@ti.com>
*
* Copyright (C) 2005 Texas Instruments, Inc.
* Richard Woodruff <r-woodruff2@ti.com>
*
* Based on pm.c for omap1
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/pm.h>
#include <linux/suspend.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/console.h>
#include <plat/sram.h>
#include <plat/clockdomain.h>
#include <plat/powerdomain.h>
#include <plat/serial.h>
#include <plat/sdrc.h>
#include <plat/prcm.h>
#include <plat/gpmc.h>
#include <plat/dma.h>
#include <asm/tlbflush.h>
#include "cm.h"
#include "cm-regbits-34xx.h"
#include "prm-regbits-34xx.h"
#include "prm.h"
#include "pm.h"
#include "sdrc.h"
#include "control.h"
/* Scratchpad offsets */
#define OMAP343X_TABLE_ADDRESS_OFFSET 0xc4
#define OMAP343X_TABLE_VALUE_OFFSET 0xc0
#define OMAP343X_CONTROL_REG_VALUE_OFFSET 0xc8
struct power_state {
struct powerdomain *pwrdm;
u32 next_state;
#ifdef CONFIG_SUSPEND
u32 saved_state;
#endif
struct list_head node;
};
static LIST_HEAD(pwrst_list);
static void (*_omap_sram_idle)(u32 *addr, int save_state);
static int (*_omap_save_secure_sram)(u32 *addr);
static struct powerdomain *mpu_pwrdm, *neon_pwrdm;
static struct powerdomain *core_pwrdm, *per_pwrdm;
static struct powerdomain *cam_pwrdm;
static inline void omap3_per_save_context(void)
{
omap_gpio_save_context();
}
static inline void omap3_per_restore_context(void)
{
omap_gpio_restore_context();
}
static void omap3_enable_io_chain(void)
{
int timeout = 0;
if (omap_rev() >= OMAP3430_REV_ES3_1) {
prm_set_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, WKUP_MOD,
PM_WKEN);
/* Do a readback to assure write has been done */
prm_read_mod_reg(WKUP_MOD, PM_WKEN);
while (!(prm_read_mod_reg(WKUP_MOD, PM_WKEN) &
OMAP3430_ST_IO_CHAIN_MASK)) {
timeout++;
if (timeout > 1000) {
printk(KERN_ERR "Wake up daisy chain "
"activation failed.\n");
return;
}
prm_set_mod_reg_bits(OMAP3430_ST_IO_CHAIN_MASK,
WKUP_MOD, PM_WKEN);
}
}
}
static void omap3_disable_io_chain(void)
{
if (omap_rev() >= OMAP3430_REV_ES3_1)
prm_clear_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, WKUP_MOD,
PM_WKEN);
}
static void omap3_core_save_context(void)
{
u32 control_padconf_off;
/* Save the padconf registers */
control_padconf_off = omap_ctrl_readl(OMAP343X_CONTROL_PADCONF_OFF);
control_padconf_off |= START_PADCONF_SAVE;
omap_ctrl_writel(control_padconf_off, OMAP343X_CONTROL_PADCONF_OFF);
/* wait for the save to complete */
while (!(omap_ctrl_readl(OMAP343X_CONTROL_GENERAL_PURPOSE_STATUS)
& PADCONF_SAVE_DONE))
udelay(1);
/*
* Force write last pad into memory, as this can fail in some
* cases according to erratas 1.157, 1.185
*/
omap_ctrl_writel(omap_ctrl_readl(OMAP343X_PADCONF_ETK_D14),
OMAP343X_CONTROL_MEM_WKUP + 0x2a0);
/* Save the Interrupt controller context */
omap_intc_save_context();
/* Save the GPMC context */
omap3_gpmc_save_context();
/* Save the system control module context, padconf already save above*/
omap3_control_save_context();
omap_dma_global_context_save();
}
static void omap3_core_restore_context(void)
{
/* Restore the control module context, padconf restored by h/w */
omap3_control_restore_context();
/* Restore the GPMC context */
omap3_gpmc_restore_context();
/* Restore the interrupt controller context */
omap_intc_restore_context();
omap_dma_global_context_restore();
}
/*
* FIXME: This function should be called before entering off-mode after
* OMAP3 secure services have been accessed. Currently it is only called
* once during boot sequence, but this works as we are not using secure
* services.
*/
static void omap3_save_secure_ram_context(u32 target_mpu_state)
{
u32 ret;
if (omap_type() != OMAP2_DEVICE_TYPE_GP) {
/*
* MPU next state must be set to POWER_ON temporarily,
* otherwise the WFI executed inside the ROM code
* will hang the system.
*/
pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_ON);
ret = _omap_save_secure_sram((u32 *)
__pa(omap3_secure_ram_storage));
pwrdm_set_next_pwrst(mpu_pwrdm, target_mpu_state);
/* Following is for error tracking, it should not happen */
if (ret) {
printk(KERN_ERR "save_secure_sram() returns %08x\n",
ret);
while (1)
;
}
}
}
/*
* PRCM Interrupt Handler Helper Function
*
* The purpose of this function is to clear any wake-up events latched
* in the PRCM PM_WKST_x registers. It is possible that a wake-up event
* may occur whilst attempting to c