2 * AVR32 AP Power Management
4 * Copyright (C) 2008 Atmel Corporation
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.
11 #include <linux/suspend.h>
12 #include <linux/vmalloc.h>
14 #include <asm/cacheflush.h>
15 #include <asm/sysreg.h>
18 #include <mach/sram.h>
20 /* FIXME: This is only valid for AP7000 */
21 #define SDRAMC_BASE 0xfff03800
25 #define SRAM_PAGE_FLAGS (SYSREG_BIT(TLBELO_D) | SYSREG_BF(SZ, 1) \
26 | SYSREG_BF(AP, 3) | SYSREG_BIT(G))
29 static unsigned long pm_sram_start
;
30 static size_t pm_sram_size
;
31 static struct vm_struct
*pm_sram_area
;
33 static void (*avr32_pm_enter_standby
)(unsigned long sdramc_base
);
34 static void (*avr32_pm_enter_str
)(unsigned long sdramc_base
);
37 * Must be called with interrupts disabled. Exceptions will be masked
38 * on return (i.e. all exceptions will be "unrecoverable".)
40 static void *avr32_pm_map_sram(void)
43 unsigned long page_addr
;
47 vaddr
= (unsigned long)pm_sram_area
->addr
;
48 page_addr
= pm_sram_start
& PAGE_MASK
;
51 * Mask exceptions and grab the first TLB entry. We won't be
52 * needing it while sleeping.
54 asm volatile("ssrf %0" : : "i"(SYSREG_EM_OFFSET
) : "memory");
56 mmucr
= sysreg_read(MMUCR
);
57 tlbehi
= sysreg_read(TLBEHI
);
58 sysreg_write(MMUCR
, SYSREG_BFINS(DRP
, 0, mmucr
));
60 tlbehi
= SYSREG_BF(ASID
, SYSREG_BFEXT(ASID
, tlbehi
));
61 tlbehi
|= vaddr
& PAGE_MASK
;
62 tlbehi
|= SYSREG_BIT(TLBEHI_V
);
64 sysreg_write(TLBELO
, page_addr
| SRAM_PAGE_FLAGS
);
65 sysreg_write(TLBEHI
, tlbehi
);
68 return (void *)(vaddr
+ pm_sram_start
- page_addr
);
72 * Must be called with interrupts disabled. Exceptions will be
75 static void avr32_pm_unmap_sram(void)
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
));
86 /* Clear the "valid" bit */
87 tlbehi
= SYSREG_BF(ASID
, SYSREG_BFEXT(ASID
, tlbehi
));
88 sysreg_write(TLBEHI
, tlbehi
);
90 /* Mark it as "not accessed" */
91 tlbarlo
= sysreg_read(TLBARLO
);
92 sysreg_write(TLBARLO
, tlbarlo
| 0x80000000U
);
97 /* Unmask exceptions */
98 asm volatile("csrf %0" : : "i"(SYSREG_EM_OFFSET
) : "memory");
101 static int avr32_pm_valid_state(suspend_state_t state
)
105 case PM_SUSPEND_STANDBY
:
114 static int avr32_pm_enter(suspend_state_t state
)
121 case PM_SUSPEND_STANDBY
:
122 sram
= avr32_pm_map_sram();
124 /* Switch to in-sram exception handlers */
125 evba_saved
= sysreg_read(EVBA
);
126 sysreg_write(EVBA
, (unsigned long)sram
);
129 * Save the LPR register so that we can re-enable
130 * SDRAM Low Power mode on resume.
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
);
137 /* Switch back to regular exception handlers */
138 sysreg_write(EVBA
, evba_saved
);
140 avr32_pm_unmap_sram();
144 sram
= avr32_pm_map_sram();
146 /* Switch to in-sram exception handlers */
147 evba_saved
= sysreg_read(EVBA
);
148 sysreg_write(EVBA
, (unsigned long)sram
);
151 * Save the LPR register so that we can re-enable
152 * SDRAM Low Power mode on resume.
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
);
159 /* Switch back to regular exception handlers */
160 sysreg_write(EVBA
, evba_saved
);
162 avr32_pm_unmap_sram();
166 pr_debug("%s: Entering idle...\n", __func__
);
171 pr_debug("%s: Invalid suspend state %d\n", __func__
, state
);
175 pr_debug("%s: wakeup\n", __func__
);
181 static struct platform_suspend_ops avr32_pm_ops
= {
182 .valid
= avr32_pm_valid_state
,
183 .enter
= avr32_pm_enter
,
186 static unsigned long avr32_pm_offset(void *symbol
)
188 extern u8 pm_exception
[];
190 return (unsigned long)symbol
- (unsigned long)pm_exception
;
193 static int __init
avr32_pm_init(void)
195 extern u8 pm_exception
[];
197 extern u8 pm_standby
[];
198 extern u8 pm_suspend_to_ram
[];
199 extern u8 pm_sram_end
[];
203 * To keep things simple, we depend on not needing more than a
206 pm_sram_size
= avr32_pm_offset(pm_sram_end
);
207 if (pm_sram_size
> PAGE_SIZE
)
210 pm_sram_start
= sram_alloc(pm_sram_size
);
214 /* Grab a virtual area we can use later on. */
215 pm_sram_area
= get_vm_area(pm_sram_size
, VM_IOREMAP
);
218 pm_sram_area
->phys_addr
= pm_sram_start
;
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();
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
));
232 suspend_set_ops(&avr32_pm_ops
);
234 printk("AVR32 AP Power Management enabled\n");
239 sram_free(pm_sram_start
, pm_sram_size
);
242 pr_err("AVR32 Power Management initialization failed\n");
245 arch_initcall(avr32_pm_init
);