aboutsummaryrefslogtreecommitdiffstats
path: root/arch/blackfin/mach-common/pm.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/blackfin/mach-common/pm.c')
-rw-r--r--arch/blackfin/mach-common/pm.c225
1 files changed, 216 insertions, 9 deletions
diff --git a/arch/blackfin/mach-common/pm.c b/arch/blackfin/mach-common/pm.c
index 0be805ca423f..4fe6a2366b13 100644
--- a/arch/blackfin/mach-common/pm.c
+++ b/arch/blackfin/mach-common/pm.c
@@ -38,8 +38,9 @@
38#include <linux/io.h> 38#include <linux/io.h>
39#include <linux/irq.h> 39#include <linux/irq.h>
40 40
41#include <asm/dpmc.h>
42#include <asm/gpio.h> 41#include <asm/gpio.h>
42#include <asm/dma.h>
43#include <asm/dpmc.h>
43 44
44#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_H 45#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_H
45#define WAKEUP_TYPE PM_WAKE_HIGH 46#define WAKEUP_TYPE PM_WAKE_HIGH
@@ -61,16 +62,17 @@
61#define WAKEUP_TYPE PM_WAKE_BOTH_EDGES 62#define WAKEUP_TYPE PM_WAKE_BOTH_EDGES
62#endif 63#endif
63 64
65
64void bfin_pm_suspend_standby_enter(void) 66void bfin_pm_suspend_standby_enter(void)
65{ 67{
68 unsigned long flags;
69
66#ifdef CONFIG_PM_WAKEUP_BY_GPIO 70#ifdef CONFIG_PM_WAKEUP_BY_GPIO
67 gpio_pm_wakeup_request(CONFIG_PM_WAKEUP_GPIO_NUMBER, WAKEUP_TYPE); 71 gpio_pm_wakeup_request(CONFIG_PM_WAKEUP_GPIO_NUMBER, WAKEUP_TYPE);
68#endif 72#endif
69 73
70 u32 flags;
71
72 local_irq_save(flags); 74 local_irq_save(flags);
73 bfin_pm_setup(); 75 bfin_pm_standby_setup();
74 76
75#ifdef CONFIG_PM_BFIN_SLEEP_DEEPER 77#ifdef CONFIG_PM_BFIN_SLEEP_DEEPER
76 sleep_deeper(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); 78 sleep_deeper(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]);
@@ -78,7 +80,7 @@ void bfin_pm_suspend_standby_enter(void)
78 sleep_mode(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); 80 sleep_mode(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]);
79#endif 81#endif
80 82
81 bfin_pm_restore(); 83 bfin_pm_standby_restore();
82 84
83#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) 85#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561)
84 bfin_write_SIC_IWR0(IWR_ENABLE_ALL); 86 bfin_write_SIC_IWR0(IWR_ENABLE_ALL);
@@ -93,6 +95,195 @@ void bfin_pm_suspend_standby_enter(void)
93 local_irq_restore(flags); 95 local_irq_restore(flags);
94} 96}
95 97
98int bf53x_suspend_l1_mem(unsigned char *memptr)
99{
100 dma_memcpy(memptr, (const void *) L1_CODE_START, L1_CODE_LENGTH);
101 dma_memcpy(memptr + L1_CODE_LENGTH, (const void *) L1_DATA_A_START,
102 L1_DATA_A_LENGTH);
103 dma_memcpy(memptr + L1_CODE_LENGTH + L1_DATA_A_LENGTH,
104 (const void *) L1_DATA_B_START, L1_DATA_B_LENGTH);
105 memcpy(memptr + L1_CODE_LENGTH + L1_DATA_A_LENGTH +
106 L1_DATA_B_LENGTH, (const void *) L1_SCRATCH_START,
107 L1_SCRATCH_LENGTH);
108
109 return 0;
110}
111
112int bf53x_resume_l1_mem(unsigned char *memptr)
113{
114 dma_memcpy((void *) L1_CODE_START, memptr, L1_CODE_LENGTH);
115 dma_memcpy((void *) L1_DATA_A_START, memptr + L1_CODE_LENGTH,
116 L1_DATA_A_LENGTH);
117 dma_memcpy((void *) L1_DATA_B_START, memptr + L1_CODE_LENGTH +
118 L1_DATA_A_LENGTH, L1_DATA_B_LENGTH);
119 memcpy((void *) L1_SCRATCH_START, memptr + L1_CODE_LENGTH +
120 L1_DATA_A_LENGTH + L1_DATA_B_LENGTH, L1_SCRATCH_LENGTH);
121
122 return 0;
123}
124
125#ifdef CONFIG_BFIN_WB
126static void flushinv_all_dcache(void)
127{
128 u32 way, bank, subbank, set;
129 u32 status, addr;
130 u32 dmem_ctl = bfin_read_DMEM_CONTROL();
131
132 for (bank = 0; bank < 2; ++bank) {
133 if (!(dmem_ctl & (1 << (DMC1_P - bank))))
134 continue;
135
136 for (way = 0; way < 2; ++way)
137 for (subbank = 0; subbank < 4; ++subbank)
138 for (set = 0; set < 64; ++set) {
139
140 bfin_write_DTEST_COMMAND(
141 way << 26 |
142 bank << 23 |
143 subbank << 16 |
144 set << 5
145 );
146 CSYNC();
147 status = bfin_read_DTEST_DATA0();
148
149 /* only worry about valid/dirty entries */
150 if ((status & 0x3) != 0x3)
151 continue;
152
153 /* construct the address using the tag */
154 addr = (status & 0xFFFFC800) | (subbank << 12) | (set << 5);
155
156 /* flush it */
157 __asm__ __volatile__("FLUSHINV[%0];" : : "a"(addr));
158 }
159 }
160}
161#endif
162
163static inline void dcache_disable(void)
164{
165#ifdef CONFIG_BFIN_DCACHE
166 unsigned long ctrl;
167
168#ifdef CONFIG_BFIN_WB
169 flushinv_all_dcache();
170#endif
171 SSYNC();
172 ctrl = bfin_read_DMEM_CONTROL();
173 ctrl &= ~ENDCPLB;
174 bfin_write_DMEM_CONTROL(ctrl);
175 SSYNC();
176#endif
177}
178
179static inline void dcache_enable(void)
180{
181#ifdef CONFIG_BFIN_DCACHE
182 unsigned long ctrl;
183 SSYNC();
184 ctrl = bfin_read_DMEM_CONTROL();
185 ctrl |= ENDCPLB;
186 bfin_write_DMEM_CONTROL(ctrl);
187 SSYNC();
188#endif
189}
190
191static inline void icache_disable(void)
192{
193#ifdef CONFIG_BFIN_ICACHE
194 unsigned long ctrl;
195 SSYNC();
196 ctrl = bfin_read_IMEM_CONTROL();
197 ctrl &= ~ENICPLB;
198 bfin_write_IMEM_CONTROL(ctrl);
199 SSYNC();
200#endif
201}
202
203static inline void icache_enable(void)
204{
205#ifdef CONFIG_BFIN_ICACHE
206 unsigned long ctrl;
207 SSYNC();
208 ctrl = bfin_read_IMEM_CONTROL();
209 ctrl |= ENICPLB;
210 bfin_write_IMEM_CONTROL(ctrl);
211 SSYNC();
212#endif
213}
214
215int bfin_pm_suspend_mem_enter(void)
216{
217 unsigned long flags;
218 int wakeup, ret;
219
220 unsigned char *memptr = kmalloc(L1_CODE_LENGTH + L1_DATA_A_LENGTH
221 + L1_DATA_B_LENGTH + L1_SCRATCH_LENGTH,
222 GFP_KERNEL);
223
224 if (memptr == NULL) {
225 panic("bf53x_suspend_l1_mem malloc failed");
226 return -ENOMEM;
227 }
228
229 wakeup = bfin_read_VR_CTL() & ~FREQ;
230 wakeup |= SCKELOW;
231
232 /* FIXME: merge this somehow with set_irq_wake */
233#ifdef CONFIG_PM_BFIN_WAKE_RTC
234 wakeup |= WAKE;
235#endif
236#ifdef CONFIG_PM_BFIN_WAKE_PH6
237 wakeup |= PHYWE;
238#endif
239#ifdef CONFIG_PM_BFIN_WAKE_CAN
240 wakeup |= CANWE;
241#endif
242#ifdef CONFIG_PM_BFIN_WAKE_GP
243 wakeup |= GPWE;
244#endif
245#ifdef CONFIG_PM_BFIN_WAKE_USB
246 wakeup |= USBWE;
247#endif
248#ifdef CONFIG_PM_BFIN_WAKE_KEYPAD
249 wakeup |= KPADWE;
250#endif
251#ifdef CONFIG_PM_BFIN_WAKE_ROTARY
252 wakeup |= ROTWE;
253#endif
254
255 local_irq_save(flags);
256
257 ret = blackfin_dma_suspend();
258
259 if (ret) {
260 local_irq_restore(flags);
261 kfree(memptr);
262 return ret;
263 }
264
265 bfin_gpio_pm_hibernate_suspend();
266
267 dcache_disable();
268 icache_disable();
269 bf53x_suspend_l1_mem(memptr);
270
271 do_hibernate(wakeup); /* Goodbye */
272
273 bf53x_resume_l1_mem(memptr);
274
275 icache_enable();
276 dcache_enable();
277
278 bfin_gpio_pm_hibernate_restore();
279 blackfin_dma_resume();
280
281 local_irq_restore(flags);
282 kfree(memptr);
283
284 return 0;
285}
286
96/* 287/*
97 * bfin_pm_valid - Tell the PM core that we only support the standby sleep 288 * bfin_pm_valid - Tell the PM core that we only support the standby sleep
98 * state 289 * state
@@ -101,7 +292,24 @@ void bfin_pm_suspend_standby_enter(void)
101 */ 292 */
102static int bfin_pm_valid(suspend_state_t state) 293static int bfin_pm_valid(suspend_state_t state)
103{ 294{
104 return (state == PM_SUSPEND_STANDBY); 295 return (state == PM_SUSPEND_STANDBY
296#ifndef BF533_FAMILY
297 /*
298 * On BF533/2/1:
299 * If we enter Hibernate the SCKE Pin is driven Low,
300 * so that the SDRAM enters Self Refresh Mode.
301 * However when the reset sequence that follows hibernate
302 * state is executed, SCKE is driven High, taking the
303 * SDRAM out of Self Refresh.
304 *
305 * If you reconfigure and access the SDRAM "very quickly",
306 * you are likely to avoid errors, otherwise the SDRAM
307 * start losing its contents.
308 * An external HW workaround is possible using logic gates.
309 */
310 || state == PM_SUSPEND_MEM
311#endif
312 );
105} 313}
106 314
107/* 315/*
@@ -115,10 +323,9 @@ static int bfin_pm_enter(suspend_state_t state)
115 case PM_SUSPEND_STANDBY: 323 case PM_SUSPEND_STANDBY:
116 bfin_pm_suspend_standby_enter(); 324 bfin_pm_suspend_standby_enter();
117 break; 325 break;
118
119 case PM_SUSPEND_MEM: 326 case PM_SUSPEND_MEM:
120 return -ENOTSUPP; 327 bfin_pm_suspend_mem_enter();
121 328 break;
122 default: 329 default:
123 return -EINVAL; 330 return -EINVAL;
124 } 331 }