aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorHaavard Skinnemoen <hskinnemoen@atmel.com>2008-02-24 07:51:38 -0500
committerHaavard Skinnemoen <haavard.skinnemoen@atmel.com>2008-07-02 05:05:01 -0400
commit02a00cf672a37292c31bbdde191712bfa40a4f1d (patch)
tree5588f7cd600056861fe1313abe7b47b9c051eab6 /arch
parentaa8e87ca619a3d1944874e85d74fda90607c73b9 (diff)
avr32: Power Management support ("standby" and "mem" modes)
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>
Diffstat (limited to 'arch')
-rw-r--r--arch/avr32/Kconfig5
-rw-r--r--arch/avr32/mach-at32ap/Makefile5
-rw-r--r--arch/avr32/mach-at32ap/intc.c54
-rw-r--r--arch/avr32/mach-at32ap/pm-at32ap700x.S108
-rw-r--r--arch/avr32/mach-at32ap/pm.c245
-rw-r--r--arch/avr32/mach-at32ap/sdramc.h76
6 files changed, 492 insertions, 1 deletions
diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig
index e8ee5fa017b6..45d63c986015 100644
--- a/arch/avr32/Kconfig
+++ b/arch/avr32/Kconfig
@@ -205,6 +205,11 @@ endmenu
205 205
206menu "Power management options" 206menu "Power management options"
207 207
208source "kernel/power/Kconfig"
209
210config ARCH_SUSPEND_POSSIBLE
211 def_bool y
212
208menu "CPU Frequency scaling" 213menu "CPU Frequency scaling"
209 214
210source "drivers/cpufreq/Kconfig" 215source "drivers/cpufreq/Kconfig"
diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile
index cb44182d3868..d5018e2eed25 100644
--- a/arch/avr32/mach-at32ap/Makefile
+++ b/arch/avr32/mach-at32ap/Makefile
@@ -1,3 +1,8 @@
1obj-y += pdc.o clock.o intc.o extint.o pio.o hsmc.o 1obj-y += pdc.o clock.o intc.o extint.o pio.o hsmc.o
2obj-$(CONFIG_CPU_AT32AP700X) += at32ap700x.o pm-at32ap700x.o 2obj-$(CONFIG_CPU_AT32AP700X) += at32ap700x.o pm-at32ap700x.o
3obj-$(CONFIG_CPU_FREQ_AT32AP) += cpufreq.o 3obj-$(CONFIG_CPU_FREQ_AT32AP) += cpufreq.o
4obj-$(CONFIG_PM) += pm.o
5
6ifeq ($(CONFIG_PM_DEBUG),y)
7CFLAGS_pm.o += -DDEBUG
8endif
diff --git a/arch/avr32/mach-at32ap/intc.c b/arch/avr32/mach-at32ap/intc.c
index 644a3fb8c164..994c4545e2b7 100644
--- a/arch/avr32/mach-at32ap/intc.c
+++ b/arch/avr32/mach-at32ap/intc.c
@@ -22,6 +22,10 @@ struct intc {
22 void __iomem *regs; 22 void __iomem *regs;
23 struct irq_chip chip; 23 struct irq_chip chip;
24 struct sys_device sysdev; 24 struct sys_device sysdev;
25#ifdef CONFIG_PM
26 unsigned long suspend_ipr;
27 unsigned long saved_ipr[64];
28#endif
25}; 29};
26 30
27extern struct platform_device at32_intc0_device; 31extern struct platform_device at32_intc0_device;
@@ -138,8 +142,56 @@ fail:
138 panic("Interrupt controller initialization failed!\n"); 142 panic("Interrupt controller initialization failed!\n");
139} 143}
140 144
145#ifdef CONFIG_PM
146void intc_set_suspend_handler(unsigned long offset)
147{
148 intc0.suspend_ipr = offset;
149}
150
151static int intc_suspend(struct sys_device *sdev, pm_message_t state)
152{
153 struct intc *intc = container_of(sdev, struct intc, sysdev);
154 int i;
155
156 if (unlikely(!irqs_disabled())) {
157 pr_err("intc_suspend: called with interrupts enabled\n");
158 return -EINVAL;
159 }
160
161 if (unlikely(!intc->suspend_ipr)) {
162 pr_err("intc_suspend: suspend_ipr not initialized\n");
163 return -EINVAL;
164 }
165
166 for (i = 0; i < 64; i++) {
167 intc->saved_ipr[i] = intc_readl(intc, INTPR0 + 4 * i);
168 intc_writel(intc, INTPR0 + 4 * i, intc->suspend_ipr);
169 }
170
171 return 0;
172}
173
174static int intc_resume(struct sys_device *sdev)
175{
176 struct intc *intc = container_of(sdev, struct intc, sysdev);
177 int i;
178
179 WARN_ON(!irqs_disabled());
180
181 for (i = 0; i < 64; i++)
182 intc_writel(intc, INTPR0 + 4 * i, intc->saved_ipr[i]);
183
184 return 0;
185}
186#else
187#define intc_suspend NULL
188#define intc_resume NULL
189#endif
190
141static struct sysdev_class intc_class = { 191static struct sysdev_class intc_class = {
142 .name = "intc", 192 .name = "intc",
193 .suspend = intc_suspend,
194 .resume = intc_resume,
143}; 195};
144 196
145static int __init intc_init_sysdev(void) 197static int __init intc_init_sysdev(void)
diff --git a/arch/avr32/mach-at32ap/pm-at32ap700x.S b/arch/avr32/mach-at32ap/pm-at32ap700x.S
index 949e2485e278..0a53ad314ff4 100644
--- a/arch/avr32/mach-at32ap/pm-at32ap700x.S
+++ b/arch/avr32/mach-at32ap/pm-at32ap700x.S
@@ -12,6 +12,12 @@
12#include <asm/thread_info.h> 12#include <asm/thread_info.h>
13#include <asm/arch/pm.h> 13#include <asm/arch/pm.h>
14 14
15#include "pm.h"
16#include "sdramc.h"
17
18/* Same as 0xfff00000 but fits in a 21 bit signed immediate */
19#define PM_BASE -0x100000
20
15 .section .bss, "wa", @nobits 21 .section .bss, "wa", @nobits
16 .global disable_idle_sleep 22 .global disable_idle_sleep
17 .type disable_idle_sleep, @object 23 .type disable_idle_sleep, @object
@@ -64,3 +70,105 @@ cpu_idle_skip_sleep:
64 unmask_interrupts 70 unmask_interrupts
65 retal r12 71 retal r12
66 .size cpu_idle_skip_sleep, . - cpu_idle_skip_sleep 72 .size cpu_idle_skip_sleep, . - cpu_idle_skip_sleep
73
74#ifdef CONFIG_PM
75 .section .init.text, "ax", @progbits
76
77 .global pm_exception
78 .type pm_exception, @function
79pm_exception:
80 /*
81 * Exceptions are masked when we switch to this handler, so
82 * we'll only get "unrecoverable" exceptions (offset 0.)
83 */
84 sub r12, pc, . - .Lpanic_msg
85 lddpc pc, .Lpanic_addr
86
87 .align 2
88.Lpanic_addr:
89 .long panic
90.Lpanic_msg:
91 .asciz "Unrecoverable exception during suspend\n"
92 .size pm_exception, . - pm_exception
93
94 .global pm_irq0
95 .type pm_irq0, @function
96pm_irq0:
97 /* Disable interrupts and return after the sleep instruction */
98 mfsr r9, SYSREG_RSR_INT0
99 mtsr SYSREG_RAR_INT0, r8
100 sbr r9, SYSREG_GM_OFFSET
101 mtsr SYSREG_RSR_INT0, r9
102 rete
103
104 /*
105 * void cpu_enter_standby(unsigned long sdramc_base)
106 *
107 * Enter PM_SUSPEND_STANDBY mode. At this point, all drivers
108 * are suspended and interrupts are disabled. Interrupts
109 * marked as 'wakeup' event sources may still come along and
110 * get us out of here.
111 *
112 * The SDRAM will be put into self-refresh mode (which does
113 * not require a clock from the CPU), and the CPU will be put
114 * into "frozen" mode (HSB bus stopped). The SDRAM controller
115 * will automatically bring the SDRAM into normal mode on the
116 * first access, and the power manager will automatically
117 * start the HSB and CPU clocks upon a wakeup event.
118 *
119 * This code uses the same "skip sleep" technique as above.
120 * It is very important that we jump directly to
121 * cpu_after_sleep after the sleep instruction since that's
122 * where we'll end up if the interrupt handler decides that we
123 * need to skip the sleep instruction.
124 */
125 .global pm_standby
126 .type pm_standby, @function
127pm_standby:
128 /*
129 * interrupts are already masked at this point, and EVBA
130 * points to pm_exception above.
131 */
132 ld.w r10, r12[SDRAMC_LPR]
133 sub r8, pc, . - 1f /* return address for irq handler */
134 mov r11, SDRAMC_LPR_LPCB_SELF_RFR
135 bfins r10, r11, 0, 2 /* LPCB <- self Refresh */
136 sync 0 /* flush write buffer */
137 st.w r12[SDRAMC_LPR], r11 /* put SDRAM in self-refresh mode */
138 ld.w r11, r12[SDRAMC_LPR]
139 unmask_interrupts
140 sleep CPU_SLEEP_FROZEN
1411: mask_interrupts
142 retal r12
143 .size pm_standby, . - pm_standby
144
145 .global pm_suspend_to_ram
146 .type pm_suspend_to_ram, @function
147pm_suspend_to_ram:
148 /*
149 * interrupts are already masked at this point, and EVBA
150 * points to pm_exception above.
151 */
152 mov r11, 0
153 cache r11[2], 8 /* clean all dcache lines */
154 sync 0 /* flush write buffer */
155 ld.w r10, r12[SDRAMC_LPR]
156 sub r8, pc, . - 1f /* return address for irq handler */
157 mov r11, SDRAMC_LPR_LPCB_SELF_RFR
158 bfins r10, r11, 0, 2 /* LPCB <- self refresh */
159 st.w r12[SDRAMC_LPR], r10 /* put SDRAM in self-refresh mode */
160 ld.w r11, r12[SDRAMC_LPR]
161
162 unmask_interrupts
163 sleep CPU_SLEEP_STOP
1641: mask_interrupts
165
166 retal r12
167 .size pm_suspend_to_ram, . - pm_suspend_to_ram
168
169 .global pm_sram_end
170 .type pm_sram_end, @function
171pm_sram_end:
172 .size pm_sram_end, 0
173
174#endif /* CONFIG_PM */
diff --git a/arch/avr32/mach-at32ap/pm.c b/arch/avr32/mach-at32ap/pm.c
new file mode 100644
index 000000000000..0b764320135d
--- /dev/null
+++ b/arch/avr32/mach-at32ap/pm.c
@@ -0,0 +1,245 @@
1/*
2 * AVR32 AP Power Management
3 *
4 * Copyright (C) 2008 Atmel Corporation
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
9 */
10#include <linux/io.h>
11#include <linux/suspend.h>
12#include <linux/vmalloc.h>
13
14#include <asm/cacheflush.h>
15#include <asm/sysreg.h>
16
17#include <asm/arch/pm.h>
18#include <asm/arch/sram.h>
19
20/* FIXME: This is only valid for AP7000 */
21#define SDRAMC_BASE 0xfff03800
22
23#include "sdramc.h"
24
25#define SRAM_PAGE_FLAGS (SYSREG_BIT(TLBELO_D) | SYSREG_BF(SZ, 1) \
26 | SYSREG_BF(AP, 3) | SYSREG_BIT(G))
27
28
29static unsigned long pm_sram_start;
30static size_t pm_sram_size;
31static struct vm_struct *pm_sram_area;
32
33static void (*avr32_pm_enter_standby)(unsigned long sdramc_base);
34static void (*avr32_pm_enter_str)(unsigned long sdramc_base);
35
36/*
37 * Must be called with interrupts disabled. Exceptions will be masked
38 * on return (i.e. all exceptions will be "unrecoverable".)
39 */
40static void *avr32_pm_map_sram(void)
41{
42 unsigned long vaddr;
43 unsigned long page_addr;
44 u32 tlbehi;
45 u32 mmucr;
46
47 vaddr = (unsigned long)pm_sram_area->addr;
48 page_addr = pm_sram_start & PAGE_MASK;
49
50 /*
51 * Mask exceptions and grab the first TLB entry. We won't be
52 * needing it while sleeping.
53 */
54 asm volatile("ssrf %0" : : "i"(SYSREG_EM_OFFSET) : "memory");
55
56 mmucr = sysreg_read(MMUCR);
57 tlbehi = sysreg_read(TLBEHI);
58 sysreg_write(MMUCR, SYSREG_BFINS(DRP, 0, mmucr));
59
60 tlbehi = SYSREG_BF(ASID, SYSREG_BFEXT(ASID, tlbehi));
61 tlbehi |= vaddr & PAGE_MASK;
62 tlbehi |= SYSREG_BIT(TLBEHI_V);
63
64 sysreg_write(TLBELO, page_addr | SRAM_PAGE_FLAGS);
65 sysreg_write(TLBEHI, tlbehi);
66 __builtin_tlbw();
67
68 return (void *)(vaddr + pm_sram_start - page_addr);
69}
70
71/*
72 * Must be called with interrupts disabled. Exceptions will be
73 * unmasked on return.
74 */
75static void avr32_pm_unmap_sram(void)
76{
77 u32 mmucr;
78 u32 tlbehi;
79 u32 tlbarlo;
80
81 /* Going to update TLB entry at index 0 */
82 mmucr = sysreg_read(MMUCR);
83 tlbehi = sysreg_read(TLBEHI);
84 sysreg_write(MMUCR, SYSREG_BFINS(DRP, 0, mmucr));
85
86 /* Clear the "valid" bit */
87 tlbehi = SYSREG_BF(ASID, SYSREG_BFEXT(ASID, tlbehi));
88 sysreg_write(TLBEHI, tlbehi);
89
90 /* Mark it as "not accessed" */
91 tlbarlo = sysreg_read(TLBARLO);
92 sysreg_write(TLBARLO, tlbarlo | 0x80000000U);
93
94 /* Update the TLB */
95 __builtin_tlbw();
96
97 /* Unmask exceptions */
98 asm volatile("csrf %0" : : "i"(SYSREG_EM_OFFSET) : "memory");
99}
100
101static int avr32_pm_valid_state(suspend_state_t state)
102{
103 switch (state) {
104 case PM_SUSPEND_ON:
105 case PM_SUSPEND_STANDBY:
106 case PM_SUSPEND_MEM:
107 return 1;
108
109 default:
110 return 0;
111 }
112}
113
114static int avr32_pm_enter(suspend_state_t state)
115{
116 u32 lpr_saved;
117 u32 evba_saved;
118 void *sram;
119
120 switch (state) {
121 case PM_SUSPEND_STANDBY:
122 sram = avr32_pm_map_sram();
123
124 /* Switch to in-sram exception handlers */
125 evba_saved = sysreg_read(EVBA);
126 sysreg_write(EVBA, (unsigned long)sram);
127
128 /*
129 * Save the LPR register so that we can re-enable
130 * SDRAM Low Power mode on resume.
131 */
132 lpr_saved = sdramc_readl(LPR);
133 pr_debug("%s: Entering standby...\n", __func__);
134 avr32_pm_enter_standby(SDRAMC_BASE);
135 sdramc_writel(LPR, lpr_saved);
136
137 /* Switch back to regular exception handlers */
138 sysreg_write(EVBA, evba_saved);
139
140 avr32_pm_unmap_sram();
141 break;
142
143 case PM_SUSPEND_MEM:
144 sram = avr32_pm_map_sram();
145
146 /* Switch to in-sram exception handlers */
147 evba_saved = sysreg_read(EVBA);
148 sysreg_write(EVBA, (unsigned long)sram);
149
150 /*
151 * Save the LPR register so that we can re-enable
152 * SDRAM Low Power mode on resume.
153 */
154 lpr_saved = sdramc_readl(LPR);
155 pr_debug("%s: Entering suspend-to-ram...\n", __func__);
156 avr32_pm_enter_str(SDRAMC_BASE);
157 sdramc_writel(LPR, lpr_saved);
158
159 /* Switch back to regular exception handlers */
160 sysreg_write(EVBA, evba_saved);
161
162 avr32_pm_unmap_sram();
163 break;
164
165 case PM_SUSPEND_ON:
166 pr_debug("%s: Entering idle...\n", __func__);
167 cpu_enter_idle();
168 break;
169
170 default:
171 pr_debug("%s: Invalid suspend state %d\n", __func__, state);
172 goto out;
173 }
174
175 pr_debug("%s: wakeup\n", __func__);
176
177out:
178 return 0;
179}
180
181static struct platform_suspend_ops avr32_pm_ops = {
182 .valid = avr32_pm_valid_state,
183 .enter = avr32_pm_enter,
184};
185
186static unsigned long avr32_pm_offset(void *symbol)
187{
188 extern u8 pm_exception[];
189
190 return (unsigned long)symbol - (unsigned long)pm_exception;
191}
192
193static int __init avr32_pm_init(void)
194{
195 extern u8 pm_exception[];
196 extern u8 pm_irq0[];
197 extern u8 pm_standby[];
198 extern u8 pm_suspend_to_ram[];
199 extern u8 pm_sram_end[];
200 void *dst;
201
202 /*
203 * To keep things simple, we depend on not needing more than a
204 * single page.
205 */
206 pm_sram_size = avr32_pm_offset(pm_sram_end);
207 if (pm_sram_size > PAGE_SIZE)
208 goto err;
209
210 pm_sram_start = sram_alloc(pm_sram_size);
211 if (!pm_sram_start)
212 goto err_alloc_sram;
213
214 /* Grab a virtual area we can use later on. */
215 pm_sram_area = get_vm_area(pm_sram_size, VM_IOREMAP);
216 if (!pm_sram_area)
217 goto err_vm_area;
218 pm_sram_area->phys_addr = pm_sram_start;
219
220 local_irq_disable();
221 dst = avr32_pm_map_sram();
222 memcpy(dst, pm_exception, pm_sram_size);
223 flush_dcache_region(dst, pm_sram_size);
224 invalidate_icache_region(dst, pm_sram_size);
225 avr32_pm_unmap_sram();
226 local_irq_enable();
227
228 avr32_pm_enter_standby = dst + avr32_pm_offset(pm_standby);
229 avr32_pm_enter_str = dst + avr32_pm_offset(pm_suspend_to_ram);
230 intc_set_suspend_handler(avr32_pm_offset(pm_irq0));
231
232 suspend_set_ops(&avr32_pm_ops);
233
234 printk("AVR32 AP Power Management enabled\n");
235
236 return 0;
237
238err_vm_area:
239 sram_free(pm_sram_start, pm_sram_size);
240err_alloc_sram:
241err:
242 pr_err("AVR32 Power Management initialization failed\n");
243 return -ENOMEM;
244}
245arch_initcall(avr32_pm_init);
diff --git a/arch/avr32/mach-at32ap/sdramc.h b/arch/avr32/mach-at32ap/sdramc.h
new file mode 100644
index 000000000000..66eeaed49073
--- /dev/null
+++ b/arch/avr32/mach-at32ap/sdramc.h
@@ -0,0 +1,76 @@
1/*
2 * Register definitions for the AT32AP SDRAM Controller
3 *
4 * Copyright (C) 2008 Atmel Corporation
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
9 */
10
11/* Register offsets */
12#define SDRAMC_MR 0x0000
13#define SDRAMC_TR 0x0004
14#define SDRAMC_CR 0x0008
15#define SDRAMC_HSR 0x000c
16#define SDRAMC_LPR 0x0010
17#define SDRAMC_IER 0x0014
18#define SDRAMC_IDR 0x0018
19#define SDRAMC_IMR 0x001c
20#define SDRAMC_ISR 0x0020
21#define SDRAMC_MDR 0x0024
22
23/* MR - Mode Register */
24#define SDRAMC_MR_MODE_NORMAL ( 0 << 0)
25#define SDRAMC_MR_MODE_NOP ( 1 << 0)
26#define SDRAMC_MR_MODE_BANKS_PRECHARGE ( 2 << 0)
27#define SDRAMC_MR_MODE_LOAD_MODE ( 3 << 0)
28#define SDRAMC_MR_MODE_AUTO_REFRESH ( 4 << 0)
29#define SDRAMC_MR_MODE_EXT_LOAD_MODE ( 5 << 0)
30#define SDRAMC_MR_MODE_POWER_DOWN ( 6 << 0)
31
32/* CR - Configuration Register */
33#define SDRAMC_CR_NC_8_BITS ( 0 << 0)
34#define SDRAMC_CR_NC_9_BITS ( 1 << 0)
35#define SDRAMC_CR_NC_10_BITS ( 2 << 0)
36#define SDRAMC_CR_NC_11_BITS ( 3 << 0)
37#define SDRAMC_CR_NR_11_BITS ( 0 << 2)
38#define SDRAMC_CR_NR_12_BITS ( 1 << 2)
39#define SDRAMC_CR_NR_13_BITS ( 2 << 2)
40#define SDRAMC_CR_NB_2_BANKS ( 0 << 4)
41#define SDRAMC_CR_NB_4_BANKS ( 1 << 4)
42#define SDRAMC_CR_CAS(x) ((x) << 5)
43#define SDRAMC_CR_DBW_32_BITS ( 0 << 7)
44#define SDRAMC_CR_DBW_16_BITS ( 1 << 7)
45#define SDRAMC_CR_TWR(x) ((x) << 8)
46#define SDRAMC_CR_TRC(x) ((x) << 12)
47#define SDRAMC_CR_TRP(x) ((x) << 16)
48#define SDRAMC_CR_TRCD(x) ((x) << 20)
49#define SDRAMC_CR_TRAS(x) ((x) << 24)
50#define SDRAMC_CR_TXSR(x) ((x) << 28)
51
52/* HSR - High Speed Register */
53#define SDRAMC_HSR_DA ( 1 << 0)
54
55/* LPR - Low Power Register */
56#define SDRAMC_LPR_LPCB_INHIBIT ( 0 << 0)
57#define SDRAMC_LPR_LPCB_SELF_RFR ( 1 << 0)
58#define SDRAMC_LPR_LPCB_PDOWN ( 2 << 0)
59#define SDRAMC_LPR_LPCB_DEEP_PDOWN ( 3 << 0)
60#define SDRAMC_LPR_PASR(x) ((x) << 4)
61#define SDRAMC_LPR_TCSR(x) ((x) << 8)
62#define SDRAMC_LPR_DS(x) ((x) << 10)
63#define SDRAMC_LPR_TIMEOUT(x) ((x) << 12)
64
65/* IER/IDR/IMR/ISR - Interrupt Enable/Disable/Mask/Status Register */
66#define SDRAMC_ISR_RES ( 1 << 0)
67
68/* MDR - Memory Device Register */
69#define SDRAMC_MDR_MD_SDRAM ( 0 << 0)
70#define SDRAMC_MDR_MD_LOW_PWR_SDRAM ( 1 << 0)
71
72/* Register access macros */
73#define sdramc_readl(reg) \
74 __raw_readl((void __iomem __force *)SDRAMC_BASE + SDRAMC_##reg)
75#define sdramc_writel(reg, value) \
76 __raw_writel(value, (void __iomem __force *)SDRAMC_BASE + SDRAMC_##reg)