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);