#include <linux/list.h>
#include <linux/err.h>
#include <linux/gpio.h>
+#include <linux/clk.h>
#include <plat/sram.h>
#include <plat/clockdomain.h>
#include <plat/prcm.h>
#include <plat/gpmc.h>
#include <plat/dma.h>
+#include <plat/dmtimer.h>
#include <asm/tlbflush.h>
#include "prm.h"
#include "pm.h"
+#include "sdrc.h"
+
+#define SDRC_POWER_AUTOCOUNT_SHIFT 8
+#define SDRC_POWER_AUTOCOUNT_MASK (0xffff << SDRC_POWER_AUTOCOUNT_SHIFT)
+#define SDRC_POWER_CLKCTRL_SHIFT 4
+#define SDRC_POWER_CLKCTRL_MASK (0x3 << SDRC_POWER_CLKCTRL_SHIFT)
+#define SDRC_SELF_REFRESH_ON_AUTOCOUNT (0x2 << SDRC_POWER_CLKCTRL_SHIFT)
/* Scratchpad offsets */
#define OMAP343X_TABLE_ADDRESS_OFFSET 0x31
#define OMAP343X_TABLE_VALUE_OFFSET 0x30
#define OMAP343X_CONTROL_REG_VALUE_OFFSET 0x32
+u32 enable_off_mode;
+u32 sleep_while_idle;
+u32 wakeup_timer_seconds;
+
struct power_state {
struct powerdomain *pwrdm;
u32 next_state;
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) {
- /* Disable dma irq before calling secure rom code API */
- omap_dma_disable_irq(0);
- omap_dma_disable_irq(1);
/*
* MPU next state must be set to POWER_ON temporarily,
* otherwise the WFI executed inside the ROM code
int per_next_state = PWRDM_POWER_ON;
int core_next_state = PWRDM_POWER_ON;
int core_prev_state, per_prev_state;
+ u32 sdrc_pwr = 0;
+ int per_state_modified = 0;
if (!_omap_sram_idle)
return;
if (pwrdm_read_pwrst(neon_pwrdm) == PWRDM_POWER_ON)
set_pwrdm_state(neon_pwrdm, mpu_next_state);
- /* CORE & PER */
+ /* PER */
+ per_next_state = pwrdm_read_next_pwrst(per_pwrdm);
core_next_state = pwrdm_read_next_pwrst(core_pwrdm);
- if (core_next_state < PWRDM_POWER_ON) {
+ if (per_next_state < PWRDM_POWER_ON) {
+ omap_uart_prepare_idle(2);
omap2_gpio_prepare_for_retention();
- omap_uart_prepare_idle(0);
- omap_uart_prepare_idle(1);
- /* PER changes only with core */
- per_next_state = pwrdm_read_next_pwrst(per_pwrdm);
- if (per_next_state < PWRDM_POWER_ON) {
- omap_uart_prepare_idle(2);
- if (per_next_state == PWRDM_POWER_OFF)
+ if (per_next_state == PWRDM_POWER_OFF) {
+ if (core_next_state == PWRDM_POWER_ON) {
+ per_next_state = PWRDM_POWER_RET;
+ pwrdm_set_next_pwrst(per_pwrdm, per_next_state);
+ per_state_modified = 1;
+ } else
omap3_per_save_context();
}
+ }
+
+ /* CORE */
+ if (core_next_state < PWRDM_POWER_ON) {
+ omap_uart_prepare_idle(0);
+ omap_uart_prepare_idle(1);
if (core_next_state == PWRDM_POWER_OFF) {
omap3_core_save_context();
omap3_prcm_save_context();
- omap3_save_secure_ram_context(mpu_next_state);
}
/* Enable IO-PAD wakeup */
prm_set_mod_reg_bits(OMAP3430_EN_IO, WKUP_MOD, PM_WKEN);
}
+ /*
+ * Force SDRAM controller to self-refresh mode after timeout on
+ * autocount. This is needed on ES3.0 to avoid SDRAM controller
+ * hang-ups.
+ */
+ if (omap_rev() >= OMAP3430_REV_ES3_0 &&
+ omap_type() != OMAP2_DEVICE_TYPE_GP &&
+ core_next_state == PWRDM_POWER_OFF) {
+ sdrc_pwr = sdrc_read_reg(SDRC_POWER);
+ sdrc_write_reg((sdrc_pwr &
+ ~(SDRC_POWER_AUTOCOUNT_MASK|SDRC_POWER_CLKCTRL_MASK)) |
+ (1 << SDRC_POWER_AUTOCOUNT_SHIFT) |
+ SDRC_SELF_REFRESH_ON_AUTOCOUNT, SDRC_POWER);
+ }
+
/*
* omap3_arm_context is the location where ARM registers
* get saved. The restore path then reads from this
_omap_sram_idle(omap3_arm_context, save_state);
cpu_init();
+ /* Restore normal SDRAM settings */
+ if (omap_rev() >= OMAP3430_REV_ES3_0 &&
+ omap_type() != OMAP2_DEVICE_TYPE_GP &&
+ core_next_state == PWRDM_POWER_OFF)
+ sdrc_write_reg(sdrc_pwr, SDRC_POWER);
+
/* Restore table entry modified during MMU restoration */
if (pwrdm_read_prev_pwrst(mpu_pwrdm) == PWRDM_POWER_OFF)
restore_table_entry();
+ /* CORE */
if (core_next_state < PWRDM_POWER_ON) {
- if (per_next_state < PWRDM_POWER_ON)
- omap_uart_resume_idle(2);
- omap_uart_resume_idle(1);
- omap_uart_resume_idle(0);
-
- /* Disable IO-PAD wakeup */
- prm_clear_mod_reg_bits(OMAP3430_EN_IO, WKUP_MOD, PM_WKEN);
core_prev_state = pwrdm_read_prev_pwrst(core_pwrdm);
if (core_prev_state == PWRDM_POWER_OFF) {
omap3_core_restore_context();
omap3_prcm_restore_context();
omap3_sram_restore_context();
+ omap2_sms_restore_context();
}
- if (per_next_state < PWRDM_POWER_ON) {
- per_prev_state =
- pwrdm_read_prev_pwrst(per_pwrdm);
- if (per_prev_state == PWRDM_POWER_OFF)
- omap3_per_restore_context();
- }
+ omap_uart_resume_idle(0);
+ omap_uart_resume_idle(1);
+ if (core_next_state == PWRDM_POWER_OFF)
+ prm_clear_mod_reg_bits(OMAP3430_AUTO_OFF,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_VOLTCTRL_OFFSET);
+ }
+
+ /* PER */
+ if (per_next_state < PWRDM_POWER_ON) {
+ per_prev_state = pwrdm_read_prev_pwrst(per_pwrdm);
+ if (per_prev_state == PWRDM_POWER_OFF)
+ omap3_per_restore_context();
omap2_gpio_resume_after_retention();
+ omap_uart_resume_idle(2);
+ if (per_state_modified)
+ pwrdm_set_next_pwrst(per_pwrdm, PWRDM_POWER_OFF);
}
+ /* Disable IO-PAD wakeup */
+ if (core_next_state < PWRDM_POWER_ON)
+ prm_clear_mod_reg_bits(OMAP3430_EN_IO, WKUP_MOD, PM_WKEN);
+
pwrdm_post_transition();
}
static int omap3_can_sleep(void)
{
+ if (!sleep_while_idle)
+ return 0;
if (!omap_uart_can_sleep())
return 0;
if (omap3_fclks_active())
#ifdef CONFIG_SUSPEND
static suspend_state_t suspend_state;
+static void omap2_pm_wakeup_on_timer(u32 seconds)
+{
+ u32 tick_rate, cycles;
+
+ if (!seconds)
+ return;
+
+ tick_rate = clk_get_rate(omap_dm_timer_get_fclk(gptimer_wakeup));
+ cycles = tick_rate * seconds;
+ omap_dm_timer_stop(gptimer_wakeup);
+ omap_dm_timer_set_load_start(gptimer_wakeup, 0, 0xffffffff - cycles);
+
+ pr_info("PM: Resume timer in %d secs (%d ticks at %d ticks/sec.)\n",
+ seconds, cycles, tick_rate);
+}
+
static int omap3_pm_prepare(void)
{
disable_hlt();
struct power_state *pwrst;
int state, ret = 0;
+ if (wakeup_timer_seconds)
+ omap2_pm_wakeup_on_timer(wakeup_timer_seconds);
+
/* Read current next_pwrsts */
list_for_each_entry(pwrst, &pwrst_list, node)
pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
omap3_d2d_idle();
}
+void omap3_pm_off_mode_enable(int enable)
+{
+ struct power_state *pwrst;
+ u32 state;
+
+ if (enable)
+ state = PWRDM_POWER_OFF;
+ else
+ state = PWRDM_POWER_RET;
+
+ list_for_each_entry(pwrst, &pwrst_list, node) {
+ pwrst->next_state = state;
+ set_pwrdm_state(pwrst->pwrdm, state);
+ }
+}
+
int omap3_pm_get_suspend_state(struct powerdomain *pwrdm)
{
struct power_state *pwrst;
if (!omap3_secure_ram_storage)
printk(KERN_ERR "Memory allocation failed when"
"allocating for secure sram context\n");
+
+ local_irq_disable();
+ local_fiq_disable();
+
+ omap_dma_global_context_save();
+ omap3_save_secure_ram_context(PWRDM_POWER_ON);
+ omap_dma_global_context_restore();
+
+ local_irq_enable();
+ local_fiq_enable();
}
- omap3_save_scratchpad_contents();
+ omap3_save_scratchpad_contents();
err1:
return ret;
err2: