diff options
Diffstat (limited to 'arch/blackfin/mach-common/pm.c')
-rw-r--r-- | arch/blackfin/mach-common/pm.c | 225 |
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 | |||
64 | void bfin_pm_suspend_standby_enter(void) | 66 | void 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 | ||
98 | int 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 | |||
112 | int 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 | ||
126 | static 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 | |||
163 | static 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 | |||
179 | static 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 | |||
191 | static 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 | |||
203 | static 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 | |||
215 | int 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 | */ |
102 | static int bfin_pm_valid(suspend_state_t state) | 293 | static 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 | } |