Commit | Line | Data |
---|---|---|
3c0e1947 KW |
1 | /* |
2 | * arch/arm/mach-lpc32xx/pm.c | |
3 | * | |
4 | * Original authors: Vitaly Wool, Dmitry Chigirev <source@mvista.com> | |
5 | * Modified by Kevin Wells <kevin.wells@nxp.com> | |
6 | * | |
7 | * 2005 (c) MontaVista Software, Inc. This file is licensed under | |
8 | * the terms of the GNU General Public License version 2. This program | |
9 | * is licensed "as is" without any warranty of any kind, whether express | |
10 | * or implied. | |
11 | */ | |
12 | ||
13 | /* | |
14 | * LPC32XX CPU and system power management | |
15 | * | |
16 | * The LCP32XX has three CPU modes for controlling system power: run, | |
17 | * direct-run, and halt modes. When switching between halt and run modes, | |
18 | * the CPU transistions through direct-run mode. For Linux, direct-run | |
19 | * mode is not used in normal operation. Halt mode is used when the | |
20 | * system is fully suspended. | |
21 | * | |
22 | * Run mode: | |
23 | * The ARM CPU clock (HCLK_PLL), HCLK bus clock, and PCLK bus clocks are | |
24 | * derived from the HCLK PLL. The HCLK and PCLK bus rates are divided from | |
25 | * the HCLK_PLL rate. Linux runs in this mode. | |
26 | * | |
27 | * Direct-run mode: | |
28 | * The ARM CPU clock, HCLK bus clock, and PCLK bus clocks are driven from | |
29 | * SYSCLK. SYSCLK is usually around 13MHz, but may vary based on SYSCLK | |
30 | * source or the frequency of the main oscillator. In this mode, the | |
31 | * HCLK_PLL can be safely enabled, changed, or disabled. | |
32 | * | |
33 | * Halt mode: | |
34 | * SYSCLK is gated off and the CPU and system clocks are halted. | |
35 | * Peripherals based on the 32KHz oscillator clock (ie, RTC, touch, | |
36 | * key scanner, etc.) still operate if enabled. In this state, an enabled | |
37 | * system event (ie, GPIO state change, RTC match, key press, etc.) will | |
38 | * wake the system up back into direct-run mode. | |
39 | * | |
40 | * DRAM refresh | |
41 | * DRAM clocking and refresh are slightly different for systems with DDR | |
42 | * DRAM or regular SDRAM devices. If SDRAM is used in the system, the | |
43 | * SDRAM will still be accessible in direct-run mode. In DDR based systems, | |
44 | * a transistion to direct-run mode will stop all DDR accesses (no clocks). | |
45 | * Because of this, the code to switch power modes and the code to enter | |
46 | * and exit DRAM self-refresh modes must not be executed in DRAM. A small | |
47 | * section of IRAM is used instead for this. | |
48 | * | |
49 | * Suspend is handled with the following logic: | |
50 | * Backup a small area of IRAM used for the suspend code | |
51 | * Copy suspend code to IRAM | |
52 | * Transfer control to code in IRAM | |
53 | * Places DRAMs in self-refresh mode | |
54 | * Enter direct-run mode | |
55 | * Save state of HCLK_PLL PLL | |
56 | * Disable HCLK_PLL PLL | |
57 | * Enter halt mode - CPU and buses will stop | |
58 | * System enters direct-run mode when an enabled event occurs | |
59 | * HCLK PLL state is restored | |
60 | * Run mode is entered | |
61 | * DRAMS are placed back into normal mode | |
62 | * Code execution returns from IRAM | |
63 | * IRAM code are used for suspend is restored | |
64 | * Suspend mode is exited | |
65 | */ | |
66 | ||
67 | #include <linux/suspend.h> | |
68 | #include <linux/io.h> | |
69 | #include <linux/slab.h> | |
70 | ||
71 | #include <asm/cacheflush.h> | |
72 | ||
73 | #include <mach/hardware.h> | |
74 | #include <mach/platform.h> | |
75 | #include "common.h" | |
76 | #include "clock.h" | |
77 | ||
78 | #define TEMP_IRAM_AREA IO_ADDRESS(LPC32XX_IRAM_BASE) | |
79 | ||
80 | /* | |
81 | * Both STANDBY and MEM suspend states are handled the same with no | |
82 | * loss of CPU or memory state | |
83 | */ | |
84 | static int lpc32xx_pm_enter(suspend_state_t state) | |
85 | { | |
86 | int (*lpc32xx_suspend_ptr) (void); | |
87 | void *iram_swap_area; | |
88 | ||
89 | /* Allocate some space for temporary IRAM storage */ | |
90 | iram_swap_area = kmalloc(lpc32xx_sys_suspend_sz, GFP_KERNEL); | |
91 | if (!iram_swap_area) { | |
92 | printk(KERN_ERR | |
93 | "PM Suspend: cannot allocate memory to save portion " | |
94 | "of SRAM\n"); | |
95 | return -ENOMEM; | |
96 | } | |
97 | ||
98 | /* Backup a small area of IRAM used for the suspend code */ | |
99 | memcpy(iram_swap_area, (void *) TEMP_IRAM_AREA, | |
100 | lpc32xx_sys_suspend_sz); | |
101 | ||
102 | /* | |
103 | * Copy code to suspend system into IRAM. The suspend code | |
104 | * needs to run from IRAM as DRAM may no longer be available | |
105 | * when the PLL is stopped. | |
106 | */ | |
107 | memcpy((void *) TEMP_IRAM_AREA, &lpc32xx_sys_suspend, | |
108 | lpc32xx_sys_suspend_sz); | |
109 | flush_icache_range((unsigned long)TEMP_IRAM_AREA, | |
110 | (unsigned long)(TEMP_IRAM_AREA) + lpc32xx_sys_suspend_sz); | |
111 | ||
112 | /* Transfer to suspend code in IRAM */ | |
113 | lpc32xx_suspend_ptr = (void *) TEMP_IRAM_AREA; | |
114 | flush_cache_all(); | |
115 | (void) lpc32xx_suspend_ptr(); | |
116 | ||
117 | /* Restore original IRAM contents */ | |
118 | memcpy((void *) TEMP_IRAM_AREA, iram_swap_area, | |
119 | lpc32xx_sys_suspend_sz); | |
120 | ||
121 | kfree(iram_swap_area); | |
122 | ||
123 | return 0; | |
124 | } | |
125 | ||
126 | static struct platform_suspend_ops lpc32xx_pm_ops = { | |
127 | .valid = suspend_valid_only_mem, | |
128 | .enter = lpc32xx_pm_enter, | |
129 | }; | |
130 | ||
131 | #define EMC_DYN_MEM_CTRL_OFS 0x20 | |
132 | #define EMC_SRMMC (1 << 3) | |
133 | #define EMC_CTRL_REG io_p2v(LPC32XX_EMC_BASE + EMC_DYN_MEM_CTRL_OFS) | |
134 | static int __init lpc32xx_pm_init(void) | |
135 | { | |
136 | /* | |
137 | * Setup SDRAM self-refresh clock to automatically disable o | |
138 | * start of self-refresh. This only needs to be done once. | |
139 | */ | |
140 | __raw_writel(__raw_readl(EMC_CTRL_REG) | EMC_SRMMC, EMC_CTRL_REG); | |
141 | ||
142 | suspend_set_ops(&lpc32xx_pm_ops); | |
143 | ||
144 | return 0; | |
145 | } | |
146 | arch_initcall(lpc32xx_pm_init); |