Skip to content

Commit

Permalink
avr32: Power Management support ("standby" and "mem" modes)
Browse files Browse the repository at this point in the history
Implement Standby support. In this mode, we'll suspend all drivers,
put the SDRAM in self-refresh mode and switch off the HSB bus
("frozen" mode.)

Implement Suspend-to-mem support. In this mode, we suspend all
drivers, put the SDRAM into self-refresh mode and switch off all
internal clocks except the 32 kHz oscillator ("stop" mode.)

The lowest-level suspend code runs from a small portion of SRAM
allocated at startup time. This gets rid of a small potential race
with the SDRAM where we might try to enter self-refresh mode in the
middle of an icache burst. We also relocate all interrupt and
exception handlers to SRAM during the small window when we enter and
exit the low-power modes.

We don't need to do any special tricks to start and stop the PLL. The
main clock is automatically gated by hardware until the PLL is stable.

Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
  • Loading branch information
Haavard Skinnemoen authored and Haavard Skinnemoen committed Jul 2, 2008
1 parent aa8e87c commit 02a00cf
Show file tree
Hide file tree
Showing 8 changed files with 496 additions and 1 deletion.
5 changes: 5 additions & 0 deletions arch/avr32/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ endmenu

menu "Power management options"

source "kernel/power/Kconfig"

config ARCH_SUSPEND_POSSIBLE
def_bool y

menu "CPU Frequency scaling"

source "drivers/cpufreq/Kconfig"
Expand Down
5 changes: 5 additions & 0 deletions arch/avr32/mach-at32ap/Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
obj-y += pdc.o clock.o intc.o extint.o pio.o hsmc.o
obj-$(CONFIG_CPU_AT32AP700X) += at32ap700x.o pm-at32ap700x.o
obj-$(CONFIG_CPU_FREQ_AT32AP) += cpufreq.o
obj-$(CONFIG_PM) += pm.o

ifeq ($(CONFIG_PM_DEBUG),y)
CFLAGS_pm.o += -DDEBUG
endif
54 changes: 53 additions & 1 deletion arch/avr32/mach-at32ap/intc.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ struct intc {
void __iomem *regs;
struct irq_chip chip;
struct sys_device sysdev;
#ifdef CONFIG_PM
unsigned long suspend_ipr;
unsigned long saved_ipr[64];
#endif
};

extern struct platform_device at32_intc0_device;
Expand Down Expand Up @@ -138,8 +142,56 @@ void __init init_IRQ(void)
panic("Interrupt controller initialization failed!\n");
}

#ifdef CONFIG_PM
void intc_set_suspend_handler(unsigned long offset)
{
intc0.suspend_ipr = offset;
}

static int intc_suspend(struct sys_device *sdev, pm_message_t state)
{
struct intc *intc = container_of(sdev, struct intc, sysdev);
int i;

if (unlikely(!irqs_disabled())) {
pr_err("intc_suspend: called with interrupts enabled\n");
return -EINVAL;
}

if (unlikely(!intc->suspend_ipr)) {
pr_err("intc_suspend: suspend_ipr not initialized\n");
return -EINVAL;
}

for (i = 0; i < 64; i++) {
intc->saved_ipr[i] = intc_readl(intc, INTPR0 + 4 * i);
intc_writel(intc, INTPR0 + 4 * i, intc->suspend_ipr);
}

return 0;
}

static int intc_resume(struct sys_device *sdev)
{
struct intc *intc = container_of(sdev, struct intc, sysdev);
int i;

WARN_ON(!irqs_disabled());

for (i = 0; i < 64; i++)
intc_writel(intc, INTPR0 + 4 * i, intc->saved_ipr[i]);

return 0;
}
#else
#define intc_suspend NULL
#define intc_resume NULL
#endif

static struct sysdev_class intc_class = {
.name = "intc",
.name = "intc",
.suspend = intc_suspend,
.resume = intc_resume,
};

static int __init intc_init_sysdev(void)
Expand Down
108 changes: 108 additions & 0 deletions arch/avr32/mach-at32ap/pm-at32ap700x.S
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
#include <asm/thread_info.h>
#include <asm/arch/pm.h>

#include "pm.h"
#include "sdramc.h"

/* Same as 0xfff00000 but fits in a 21 bit signed immediate */
#define PM_BASE -0x100000

.section .bss, "wa", @nobits
.global disable_idle_sleep
.type disable_idle_sleep, @object
Expand Down Expand Up @@ -64,3 +70,105 @@ cpu_idle_skip_sleep:
unmask_interrupts
retal r12
.size cpu_idle_skip_sleep, . - cpu_idle_skip_sleep

#ifdef CONFIG_PM
.section .init.text, "ax", @progbits

.global pm_exception
.type pm_exception, @function
pm_exception:
/*
* Exceptions are masked when we switch to this handler, so
* we'll only get "unrecoverable" exceptions (offset 0.)
*/
sub r12, pc, . - .Lpanic_msg
lddpc pc, .Lpanic_addr

.align 2
.Lpanic_addr:
.long panic
.Lpanic_msg:
.asciz "Unrecoverable exception during suspend\n"
.size pm_exception, . - pm_exception

.global pm_irq0
.type pm_irq0, @function
pm_irq0:
/* Disable interrupts and return after the sleep instruction */
mfsr r9, SYSREG_RSR_INT0
mtsr SYSREG_RAR_INT0, r8
sbr r9, SYSREG_GM_OFFSET
mtsr SYSREG_RSR_INT0, r9
rete

/*
* void cpu_enter_standby(unsigned long sdramc_base)
*
* Enter PM_SUSPEND_STANDBY mode. At this point, all drivers
* are suspended and interrupts are disabled. Interrupts
* marked as 'wakeup' event sources may still come along and
* get us out of here.
*
* The SDRAM will be put into self-refresh mode (which does
* not require a clock from the CPU), and the CPU will be put
* into "frozen" mode (HSB bus stopped). The SDRAM controller
* will automatically bring the SDRAM into normal mode on the
* first access, and the power manager will automatically
* start the HSB and CPU clocks upon a wakeup event.
*
* This code uses the same "skip sleep" technique as above.
* It is very important that we jump directly to
* cpu_after_sleep after the sleep instruction since that's
* where we'll end up if the interrupt handler decides that we
* need to skip the sleep instruction.
*/
.global pm_standby
.type pm_standby, @function
pm_standby:
/*
* interrupts are already masked at this point, and EVBA
* points to pm_exception above.
*/
ld.w r10, r12[SDRAMC_LPR]
sub r8, pc, . - 1f /* return address for irq handler */
mov r11, SDRAMC_LPR_LPCB_SELF_RFR
bfins r10, r11, 0, 2 /* LPCB <- self Refresh */
sync 0 /* flush write buffer */
st.w r12[SDRAMC_LPR], r11 /* put SDRAM in self-refresh mode */
ld.w r11, r12[SDRAMC_LPR]
unmask_interrupts
sleep CPU_SLEEP_FROZEN
1: mask_interrupts
retal r12
.size pm_standby, . - pm_standby

.global pm_suspend_to_ram
.type pm_suspend_to_ram, @function
pm_suspend_to_ram:
/*
* interrupts are already masked at this point, and EVBA
* points to pm_exception above.
*/
mov r11, 0
cache r11[2], 8 /* clean all dcache lines */
sync 0 /* flush write buffer */
ld.w r10, r12[SDRAMC_LPR]
sub r8, pc, . - 1f /* return address for irq handler */
mov r11, SDRAMC_LPR_LPCB_SELF_RFR
bfins r10, r11, 0, 2 /* LPCB <- self refresh */
st.w r12[SDRAMC_LPR], r10 /* put SDRAM in self-refresh mode */
ld.w r11, r12[SDRAMC_LPR]

unmask_interrupts
sleep CPU_SLEEP_STOP
1: mask_interrupts

retal r12
.size pm_suspend_to_ram, . - pm_suspend_to_ram

.global pm_sram_end
.type pm_sram_end, @function
pm_sram_end:
.size pm_sram_end, 0

#endif /* CONFIG_PM */
Loading

0 comments on commit 02a00cf

Please sign in to comment.