aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/83xx
diff options
context:
space:
mode:
authorScott Wood <scottwood@freescale.com>2007-10-09 13:37:13 -0400
committerKumar Gala <galak@kernel.crashing.org>2008-07-16 18:57:30 -0400
commitd49747bdfb2ddebea24d1580da55b79d093d48a9 (patch)
treecb2ae6ea03bab0ff7901c9997ef50131bba6b511 /arch/powerpc/platforms/83xx
parent7e72063c9aaeb618815589cd4d57f26186e6fcad (diff)
powerpc/mpc83xx: Power Management support
Basic PM support for 83xx. Standby is implemented as sleep. Suspend-to-RAM is implemented as "deep sleep" (with the processor turned off) on 831x. Signed-off-by: Scott Wood <scottwood@freescale.com> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/platforms/83xx')
-rw-r--r--arch/powerpc/platforms/83xx/Makefile1
-rw-r--r--arch/powerpc/platforms/83xx/suspend-asm.S533
-rw-r--r--arch/powerpc/platforms/83xx/suspend.c388
3 files changed, 922 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/83xx/Makefile b/arch/powerpc/platforms/83xx/Makefile
index f331fd7dd836..32c7ad13911c 100644
--- a/arch/powerpc/platforms/83xx/Makefile
+++ b/arch/powerpc/platforms/83xx/Makefile
@@ -3,6 +3,7 @@
3# 3#
4obj-y := misc.o usb.o 4obj-y := misc.o usb.o
5obj-$(CONFIG_PCI) += pci.o 5obj-$(CONFIG_PCI) += pci.o
6obj-$(CONFIG_SUSPEND) += suspend.o suspend-asm.o
6obj-$(CONFIG_MPC831x_RDB) += mpc831x_rdb.o 7obj-$(CONFIG_MPC831x_RDB) += mpc831x_rdb.o
7obj-$(CONFIG_MPC832x_RDB) += mpc832x_rdb.o 8obj-$(CONFIG_MPC832x_RDB) += mpc832x_rdb.o
8obj-$(CONFIG_MPC834x_MDS) += mpc834x_mds.o 9obj-$(CONFIG_MPC834x_MDS) += mpc834x_mds.o
diff --git a/arch/powerpc/platforms/83xx/suspend-asm.S b/arch/powerpc/platforms/83xx/suspend-asm.S
new file mode 100644
index 000000000000..1930543c98d3
--- /dev/null
+++ b/arch/powerpc/platforms/83xx/suspend-asm.S
@@ -0,0 +1,533 @@
1/*
2 * Enter and leave deep sleep state on MPC83xx
3 *
4 * Copyright (c) 2006-2008 Freescale Semiconductor, Inc.
5 * Author: Scott Wood <scottwood@freescale.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published
9 * by the Free Software Foundation.
10 */
11
12#include <asm/page.h>
13#include <asm/ppc_asm.h>
14#include <asm/reg.h>
15#include <asm/asm-offsets.h>
16
17#define SS_MEMSAVE 0x00 /* First 8 bytes of RAM */
18#define SS_HID 0x08 /* 3 HIDs */
19#define SS_IABR 0x14 /* 2 IABRs */
20#define SS_IBCR 0x1c
21#define SS_DABR 0x20 /* 2 DABRs */
22#define SS_DBCR 0x28
23#define SS_SP 0x2c
24#define SS_SR 0x30 /* 16 segment registers */
25#define SS_R2 0x70
26#define SS_MSR 0x74
27#define SS_SDR1 0x78
28#define SS_LR 0x7c
29#define SS_SPRG 0x80 /* 4 SPRGs */
30#define SS_DBAT 0x90 /* 8 DBATs */
31#define SS_IBAT 0xd0 /* 8 IBATs */
32#define SS_TB 0x110
33#define SS_CR 0x118
34#define SS_GPREG 0x11c /* r12-r31 */
35#define STATE_SAVE_SIZE 0x16c
36
37 .section .data
38 .align 5
39
40mpc83xx_sleep_save_area:
41 .space STATE_SAVE_SIZE
42immrbase:
43 .long 0
44
45 .section .text
46 .align 5
47
48 /* r3 = physical address of IMMR */
49_GLOBAL(mpc83xx_enter_deep_sleep)
50 lis r4, immrbase@ha
51 stw r3, immrbase@l(r4)
52
53 /* The first 2 words of memory are used to communicate with the
54 * bootloader, to tell it how to resume.
55 *
56 * The first word is the magic number 0xf5153ae5, and the second
57 * is the pointer to mpc83xx_deep_resume.
58 *
59 * The original content of these two words is saved in SS_MEMSAVE.
60 */
61
62 lis r3, mpc83xx_sleep_save_area@h
63 ori r3, r3, mpc83xx_sleep_save_area@l
64
65 lis r4, KERNELBASE@h
66 lwz r5, 0(r4)
67 lwz r6, 4(r4)
68
69 stw r5, SS_MEMSAVE+0(r3)
70 stw r6, SS_MEMSAVE+4(r3)
71
72 mfspr r5, SPRN_HID0
73 mfspr r6, SPRN_HID1
74 mfspr r7, SPRN_HID2
75
76 stw r5, SS_HID+0(r3)
77 stw r6, SS_HID+4(r3)
78 stw r7, SS_HID+8(r3)
79
80 mfspr r4, SPRN_IABR
81 mfspr r5, SPRN_IABR2
82 mfspr r6, SPRN_IBCR
83 mfspr r7, SPRN_DABR
84 mfspr r8, SPRN_DABR2
85 mfspr r9, SPRN_DBCR
86
87 stw r4, SS_IABR+0(r3)
88 stw r5, SS_IABR+4(r3)
89 stw r6, SS_IBCR(r3)
90 stw r7, SS_DABR+0(r3)
91 stw r8, SS_DABR+4(r3)
92 stw r9, SS_DBCR(r3)
93
94 mfspr r4, SPRN_SPRG0
95 mfspr r5, SPRN_SPRG1
96 mfspr r6, SPRN_SPRG2
97 mfspr r7, SPRN_SPRG3
98 mfsdr1 r8
99
100 stw r4, SS_SPRG+0(r3)
101 stw r5, SS_SPRG+4(r3)
102 stw r6, SS_SPRG+8(r3)
103 stw r7, SS_SPRG+12(r3)
104 stw r8, SS_SDR1(r3)
105
106 mfspr r4, SPRN_DBAT0U
107 mfspr r5, SPRN_DBAT0L
108 mfspr r6, SPRN_DBAT1U
109 mfspr r7, SPRN_DBAT1L
110
111 stw r4, SS_DBAT+0x00(r3)
112 stw r5, SS_DBAT+0x04(r3)
113 stw r6, SS_DBAT+0x08(r3)
114 stw r7, SS_DBAT+0x0c(r3)
115
116 mfspr r4, SPRN_DBAT2U
117 mfspr r5, SPRN_DBAT2L
118 mfspr r6, SPRN_DBAT3U
119 mfspr r7, SPRN_DBAT3L
120
121 stw r4, SS_DBAT+0x10(r3)
122 stw r5, SS_DBAT+0x14(r3)
123 stw r6, SS_DBAT+0x18(r3)
124 stw r7, SS_DBAT+0x1c(r3)
125
126 mfspr r4, SPRN_DBAT4U
127 mfspr r5, SPRN_DBAT4L
128 mfspr r6, SPRN_DBAT5U
129 mfspr r7, SPRN_DBAT5L
130
131 stw r4, SS_DBAT+0x20(r3)
132 stw r5, SS_DBAT+0x24(r3)
133 stw r6, SS_DBAT+0x28(r3)
134 stw r7, SS_DBAT+0x2c(r3)
135
136 mfspr r4, SPRN_DBAT6U
137 mfspr r5, SPRN_DBAT6L
138 mfspr r6, SPRN_DBAT7U
139 mfspr r7, SPRN_DBAT7L
140
141 stw r4, SS_DBAT+0x30(r3)
142 stw r5, SS_DBAT+0x34(r3)
143 stw r6, SS_DBAT+0x38(r3)
144 stw r7, SS_DBAT+0x3c(r3)
145
146 mfspr r4, SPRN_IBAT0U
147 mfspr r5, SPRN_IBAT0L
148 mfspr r6, SPRN_IBAT1U
149 mfspr r7, SPRN_IBAT1L
150
151 stw r4, SS_IBAT+0x00(r3)
152 stw r5, SS_IBAT+0x04(r3)
153 stw r6, SS_IBAT+0x08(r3)
154 stw r7, SS_IBAT+0x0c(r3)
155
156 mfspr r4, SPRN_IBAT2U
157 mfspr r5, SPRN_IBAT2L
158 mfspr r6, SPRN_IBAT3U
159 mfspr r7, SPRN_IBAT3L
160
161 stw r4, SS_IBAT+0x10(r3)
162 stw r5, SS_IBAT+0x14(r3)
163 stw r6, SS_IBAT+0x18(r3)
164 stw r7, SS_IBAT+0x1c(r3)
165
166 mfspr r4, SPRN_IBAT4U
167 mfspr r5, SPRN_IBAT4L
168 mfspr r6, SPRN_IBAT5U
169 mfspr r7, SPRN_IBAT5L
170
171 stw r4, SS_IBAT+0x20(r3)
172 stw r5, SS_IBAT+0x24(r3)
173 stw r6, SS_IBAT+0x28(r3)
174 stw r7, SS_IBAT+0x2c(r3)
175
176 mfspr r4, SPRN_IBAT6U
177 mfspr r5, SPRN_IBAT6L
178 mfspr r6, SPRN_IBAT7U
179 mfspr r7, SPRN_IBAT7L
180
181 stw r4, SS_IBAT+0x30(r3)
182 stw r5, SS_IBAT+0x34(r3)
183 stw r6, SS_IBAT+0x38(r3)
184 stw r7, SS_IBAT+0x3c(r3)
185
186 mfmsr r4
187 mflr r5
188 mfcr r6
189
190 stw r4, SS_MSR(r3)
191 stw r5, SS_LR(r3)
192 stw r6, SS_CR(r3)
193 stw r1, SS_SP(r3)
194 stw r2, SS_R2(r3)
195
1961: mftbu r4
197 mftb r5
198 mftbu r6
199 cmpw r4, r6
200 bne 1b
201
202 stw r4, SS_TB+0(r3)
203 stw r5, SS_TB+4(r3)
204
205 stmw r12, SS_GPREG(r3)
206
207 li r4, 0
208 addi r6, r3, SS_SR-4
2091: mfsrin r5, r4
210 stwu r5, 4(r6)
211 addis r4, r4, 0x1000
212 cmpwi r4, 0
213 bne 1b
214
215 /* Disable machine checks and critical exceptions */
216 mfmsr r4
217 rlwinm r4, r4, 0, ~MSR_CE
218 rlwinm r4, r4, 0, ~MSR_ME
219 mtmsr r4
220 isync
221
222#define TMP_VIRT_IMMR 0xf0000000
223#define DEFAULT_IMMR_VALUE 0xff400000
224#define IMMRBAR_BASE 0x0000
225
226 lis r4, immrbase@ha
227 lwz r4, immrbase@l(r4)
228
229 /* Use DBAT0 to address the current IMMR space */
230
231 ori r4, r4, 0x002a
232 mtspr SPRN_DBAT0L, r4
233 lis r8, TMP_VIRT_IMMR@h
234 ori r4, r8, 0x001e /* 1 MByte accessable from Kernel Space only */
235 mtspr SPRN_DBAT0U, r4
236 isync
237
238 /* Use DBAT1 to address the original IMMR space */
239
240 lis r4, DEFAULT_IMMR_VALUE@h
241 ori r4, r4, 0x002a
242 mtspr SPRN_DBAT1L, r4
243 lis r9, (TMP_VIRT_IMMR + 0x01000000)@h
244 ori r4, r9, 0x001e /* 1 MByte accessable from Kernel Space only */
245 mtspr SPRN_DBAT1U, r4
246 isync
247
248 /* Use DBAT2 to address the beginning of RAM. This isn't done
249 * using the normal virtual mapping, because with page debugging
250 * enabled it will be read-only.
251 */
252
253 li r4, 0x0002
254 mtspr SPRN_DBAT2L, r4
255 lis r4, KERNELBASE@h
256 ori r4, r4, 0x001e /* 1 MByte accessable from Kernel Space only */
257 mtspr SPRN_DBAT2U, r4
258 isync
259
260 /* Flush the cache with our BAT, as there will be TLB misses
261 * otherwise if page debugging is enabled, and these misses
262 * will disturb the PLRU algorithm.
263 */
264
265 bl __flush_disable_L1
266
267 /* Keep the i-cache enabled, so the hack below for low-boot
268 * flash will work.
269 */
270 mfspr r3, SPRN_HID0
271 ori r3, r3, HID0_ICE
272 mtspr SPRN_HID0, r3
273 isync
274
275 lis r6, 0xf515
276 ori r6, r6, 0x3ae5
277
278 lis r7, mpc83xx_deep_resume@h
279 ori r7, r7, mpc83xx_deep_resume@l
280 tophys(r7, r7)
281
282 lis r5, KERNELBASE@h
283 stw r6, 0(r5)
284 stw r7, 4(r5)
285
286 /* Reset BARs */
287
288 li r4, 0
289 stw r4, 0x0024(r8)
290 stw r4, 0x002c(r8)
291 stw r4, 0x0034(r8)
292 stw r4, 0x003c(r8)
293 stw r4, 0x0064(r8)
294 stw r4, 0x006c(r8)
295
296 /* Rev 1 of the 8313 has problems with wakeup events that are
297 * pending during the transition to deep sleep state (such as if
298 * the PCI host sets the state to D3 and then D0 in rapid
299 * succession). This check shrinks the race window somewhat.
300 *
301 * See erratum PCI23, though the problem is not limited
302 * to PCI.
303 */
304
305 lwz r3, 0x0b04(r8)
306 andi. r3, r3, 1
307 bne- mpc83xx_deep_resume
308
309 /* Move IMMR back to the default location, following the
310 * procedure specified in the MPC8313 manual.
311 */
312 lwz r4, IMMRBAR_BASE(r8)
313 isync
314 lis r4, DEFAULT_IMMR_VALUE@h
315 stw r4, IMMRBAR_BASE(r8)
316 lis r4, KERNELBASE@h
317 lwz r4, 0(r4)
318 isync
319 lwz r4, IMMRBAR_BASE(r9)
320 mr r8, r9
321 isync
322
323 /* Check the Reset Configuration Word to see whether flash needs
324 * to be mapped at a low address or a high address.
325 */
326
327 lwz r4, 0x0904(r8)
328 andis. r4, r4, 0x0400
329 li r4, 0
330 beq boot_low
331 lis r4, 0xff80
332boot_low:
333 stw r4, 0x0020(r8)
334 lis r7, 0x8000
335 ori r7, r7, 0x0016
336
337 mfspr r5, SPRN_HID0
338 rlwinm r5, r5, 0, ~(HID0_DOZE | HID0_NAP)
339 oris r5, r5, HID0_SLEEP@h
340 mtspr SPRN_HID0, r5
341 isync
342
343 mfmsr r5
344 oris r5, r5, MSR_POW@h
345
346 /* Enable the flash mapping at the appropriate address. This
347 * mapping will override the RAM mapping if booting low, so there's
348 * no need to disable the latter. This must be done inside the same
349 * cache line as setting MSR_POW, so that no instruction fetches
350 * from RAM happen after the flash mapping is turned on.
351 */
352
353 .align 5
354 stw r7, 0x0024(r8)
355 sync
356 isync
357 mtmsr r5
358 isync
3591: b 1b
360
361mpc83xx_deep_resume:
362 lis r4, 1f@h
363 ori r4, r4, 1f@l
364 tophys(r4, r4)
365 mtsrr0 r4
366
367 mfmsr r4
368 rlwinm r4, r4, 0, ~(MSR_IR | MSR_DR)
369 mtsrr1 r4
370
371 rfi
372
3731: tlbia
374 bl __inval_enable_L1
375
376 lis r3, mpc83xx_sleep_save_area@h
377 ori r3, r3, mpc83xx_sleep_save_area@l
378 tophys(r3, r3)
379
380 lwz r5, SS_MEMSAVE+0(r3)
381 lwz r6, SS_MEMSAVE+4(r3)
382
383 stw r5, 0(0)
384 stw r6, 4(0)
385
386 lwz r5, SS_HID+0(r3)
387 lwz r6, SS_HID+4(r3)
388 lwz r7, SS_HID+8(r3)
389
390 mtspr SPRN_HID0, r5
391 mtspr SPRN_HID1, r6
392 mtspr SPRN_HID2, r7
393
394 lwz r4, SS_IABR+0(r3)
395 lwz r5, SS_IABR+4(r3)
396 lwz r6, SS_IBCR(r3)
397 lwz r7, SS_DABR+0(r3)
398 lwz r8, SS_DABR+4(r3)
399 lwz r9, SS_DBCR(r3)
400
401 mtspr SPRN_IABR, r4
402 mtspr SPRN_IABR2, r5
403 mtspr SPRN_IBCR, r6
404 mtspr SPRN_DABR, r7
405 mtspr SPRN_DABR2, r8
406 mtspr SPRN_DBCR, r9
407
408 li r4, 0
409 addi r6, r3, SS_SR-4
4101: lwzu r5, 4(r6)
411 mtsrin r5, r4
412 addis r4, r4, 0x1000
413 cmpwi r4, 0
414 bne 1b
415
416 lwz r4, SS_DBAT+0x00(r3)
417 lwz r5, SS_DBAT+0x04(r3)
418 lwz r6, SS_DBAT+0x08(r3)
419 lwz r7, SS_DBAT+0x0c(r3)
420
421 mtspr SPRN_DBAT0U, r4
422 mtspr SPRN_DBAT0L, r5
423 mtspr SPRN_DBAT1U, r6
424 mtspr SPRN_DBAT1L, r7
425
426 lwz r4, SS_DBAT+0x10(r3)
427 lwz r5, SS_DBAT+0x14(r3)
428 lwz r6, SS_DBAT+0x18(r3)
429 lwz r7, SS_DBAT+0x1c(r3)
430
431 mtspr SPRN_DBAT2U, r4
432 mtspr SPRN_DBAT2L, r5
433 mtspr SPRN_DBAT3U, r6
434 mtspr SPRN_DBAT3L, r7
435
436 lwz r4, SS_DBAT+0x20(r3)
437 lwz r5, SS_DBAT+0x24(r3)
438 lwz r6, SS_DBAT+0x28(r3)
439 lwz r7, SS_DBAT+0x2c(r3)
440
441 mtspr SPRN_DBAT4U, r4
442 mtspr SPRN_DBAT4L, r5
443 mtspr SPRN_DBAT5U, r6
444 mtspr SPRN_DBAT5L, r7
445
446 lwz r4, SS_DBAT+0x30(r3)
447 lwz r5, SS_DBAT+0x34(r3)
448 lwz r6, SS_DBAT+0x38(r3)
449 lwz r7, SS_DBAT+0x3c(r3)
450
451 mtspr SPRN_DBAT6U, r4
452 mtspr SPRN_DBAT6L, r5
453 mtspr SPRN_DBAT7U, r6
454 mtspr SPRN_DBAT7L, r7
455
456 lwz r4, SS_IBAT+0x00(r3)
457 lwz r5, SS_IBAT+0x04(r3)
458 lwz r6, SS_IBAT+0x08(r3)
459 lwz r7, SS_IBAT+0x0c(r3)
460
461 mtspr SPRN_IBAT0U, r4
462 mtspr SPRN_IBAT0L, r5
463 mtspr SPRN_IBAT1U, r6
464 mtspr SPRN_IBAT1L, r7
465
466 lwz r4, SS_IBAT+0x10(r3)
467 lwz r5, SS_IBAT+0x14(r3)
468 lwz r6, SS_IBAT+0x18(r3)
469 lwz r7, SS_IBAT+0x1c(r3)
470
471 mtspr SPRN_IBAT2U, r4
472 mtspr SPRN_IBAT2L, r5
473 mtspr SPRN_IBAT3U, r6
474 mtspr SPRN_IBAT3L, r7
475
476 lwz r4, SS_IBAT+0x20(r3)
477 lwz r5, SS_IBAT+0x24(r3)
478 lwz r6, SS_IBAT+0x28(r3)
479 lwz r7, SS_IBAT+0x2c(r3)
480
481 mtspr SPRN_IBAT4U, r4
482 mtspr SPRN_IBAT4L, r5
483 mtspr SPRN_IBAT5U, r6
484 mtspr SPRN_IBAT5L, r7
485
486 lwz r4, SS_IBAT+0x30(r3)
487 lwz r5, SS_IBAT+0x34(r3)
488 lwz r6, SS_IBAT+0x38(r3)
489 lwz r7, SS_IBAT+0x3c(r3)
490
491 mtspr SPRN_IBAT6U, r4
492 mtspr SPRN_IBAT6L, r5
493 mtspr SPRN_IBAT7U, r6
494 mtspr SPRN_IBAT7L, r7
495
496 lwz r4, SS_SPRG+0(r3)
497 lwz r5, SS_SPRG+4(r3)
498 lwz r6, SS_SPRG+8(r3)
499 lwz r7, SS_SPRG+12(r3)
500 lwz r8, SS_SDR1(r3)
501
502 mtspr SPRN_SPRG0, r4
503 mtspr SPRN_SPRG1, r5
504 mtspr SPRN_SPRG2, r6
505 mtspr SPRN_SPRG3, r7
506 mtsdr1 r8
507
508 lwz r4, SS_MSR(r3)
509 lwz r5, SS_LR(r3)
510 lwz r6, SS_CR(r3)
511 lwz r1, SS_SP(r3)
512 lwz r2, SS_R2(r3)
513
514 mtsrr1 r4
515 mtsrr0 r5
516 mtcr r6
517
518 li r4, 0
519 mtspr SPRN_TBWL, r4
520
521 lwz r4, SS_TB+0(r3)
522 lwz r5, SS_TB+4(r3)
523
524 mtspr SPRN_TBWU, r4
525 mtspr SPRN_TBWL, r5
526
527 lmw r12, SS_GPREG(r3)
528
529 /* Kick decrementer */
530 li r0, 1
531 mtdec r0
532
533 rfi
diff --git a/arch/powerpc/platforms/83xx/suspend.c b/arch/powerpc/platforms/83xx/suspend.c
new file mode 100644
index 000000000000..08e65fc8b98c
--- /dev/null
+++ b/arch/powerpc/platforms/83xx/suspend.c
@@ -0,0 +1,388 @@
1/*
2 * MPC83xx suspend support
3 *
4 * Author: Scott Wood <scottwood@freescale.com>
5 *
6 * Copyright (c) 2006-2007 Freescale Semiconductor, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as published
10 * by the Free Software Foundation.
11 */
12
13#include <linux/init.h>
14#include <linux/pm.h>
15#include <linux/types.h>
16#include <linux/ioport.h>
17#include <linux/interrupt.h>
18#include <linux/wait.h>
19#include <linux/kthread.h>
20#include <linux/freezer.h>
21#include <linux/suspend.h>
22#include <linux/fsl_devices.h>
23#include <linux/of_platform.h>
24
25#include <asm/reg.h>
26#include <asm/io.h>
27#include <asm/time.h>
28#include <asm/mpc6xx.h>
29
30#include <sysdev/fsl_soc.h>
31
32#define PMCCR1_NEXT_STATE 0x0C /* Next state for power management */
33#define PMCCR1_NEXT_STATE_SHIFT 2
34#define PMCCR1_CURR_STATE 0x03 /* Current state for power management*/
35#define IMMR_RCW_OFFSET 0x900
36#define RCW_PCI_HOST 0x80000000
37
38void mpc83xx_enter_deep_sleep(phys_addr_t immrbase);
39
40struct mpc83xx_pmc {
41 u32 config;
42#define PMCCR_DLPEN 2 /* DDR SDRAM low power enable */
43#define PMCCR_SLPEN 1 /* System low power enable */
44
45 u32 event;
46 u32 mask;
47/* All but PMCI are deep-sleep only */
48#define PMCER_GPIO 0x100
49#define PMCER_PCI 0x080
50#define PMCER_USB 0x040
51#define PMCER_ETSEC1 0x020
52#define PMCER_ETSEC2 0x010
53#define PMCER_TIMER 0x008
54#define PMCER_INT1 0x004
55#define PMCER_INT2 0x002
56#define PMCER_PMCI 0x001
57#define PMCER_ALL 0x1FF
58
59 /* deep-sleep only */
60 u32 config1;
61#define PMCCR1_USE_STATE 0x80000000
62#define PMCCR1_PME_EN 0x00000080
63#define PMCCR1_ASSERT_PME 0x00000040
64#define PMCCR1_POWER_OFF 0x00000020
65
66 /* deep-sleep only */
67 u32 config2;
68};
69
70struct mpc83xx_rcw {
71 u32 rcwlr;
72 u32 rcwhr;
73};
74
75struct mpc83xx_clock {
76 u32 spmr;
77 u32 occr;
78 u32 sccr;
79};
80
81struct pmc_type {
82 int has_deep_sleep;
83};
84
85static struct of_device *pmc_dev;
86static int has_deep_sleep, deep_sleeping;
87static int pmc_irq;
88static struct mpc83xx_pmc __iomem *pmc_regs;
89static struct mpc83xx_clock __iomem *clock_regs;
90static int is_pci_agent, wake_from_pci;
91static phys_addr_t immrbase;
92static int pci_pm_state;
93static DECLARE_WAIT_QUEUE_HEAD(agent_wq);
94
95int fsl_deep_sleep(void)
96{
97 return deep_sleeping;
98}
99
100static int mpc83xx_change_state(void)
101{
102 u32 curr_state;
103 u32 reg_cfg1 = in_be32(&pmc_regs->config1);
104
105 if (is_pci_agent) {
106 pci_pm_state = (reg_cfg1 & PMCCR1_NEXT_STATE) >>
107 PMCCR1_NEXT_STATE_SHIFT;
108 curr_state = reg_cfg1 & PMCCR1_CURR_STATE;
109
110 if (curr_state != pci_pm_state) {
111 reg_cfg1 &= ~PMCCR1_CURR_STATE;
112 reg_cfg1 |= pci_pm_state;
113 out_be32(&pmc_regs->config1, reg_cfg1);
114
115 wake_up(&agent_wq);
116 return 1;
117 }
118 }
119
120 return 0;
121}
122
123static irqreturn_t pmc_irq_handler(int irq, void *dev_id)
124{
125 u32 event = in_be32(&pmc_regs->event);
126 int ret = IRQ_NONE;
127
128 if (mpc83xx_change_state())
129 ret = IRQ_HANDLED;
130
131 if (event) {
132 out_be32(&pmc_regs->event, event);
133 ret = IRQ_HANDLED;
134 }
135
136 return ret;
137}
138
139static int mpc83xx_suspend_enter(suspend_state_t state)
140{
141 int ret = -EAGAIN;
142
143 /* Don't go to sleep if there's a race where pci_pm_state changes
144 * between the agent thread checking it and the PM code disabling
145 * interrupts.
146 */
147 if (wake_from_pci) {
148 if (pci_pm_state != (deep_sleeping ? 3 : 2))
149 goto out;
150
151 out_be32(&pmc_regs->config1,
152 in_be32(&pmc_regs->config1) | PMCCR1_PME_EN);
153 }
154
155 /* Put the system into low-power mode and the RAM
156 * into self-refresh mode once the core goes to
157 * sleep.
158 */
159
160 out_be32(&pmc_regs->config, PMCCR_SLPEN | PMCCR_DLPEN);
161
162 /* If it has deep sleep (i.e. it's an 831x or compatible),
163 * disable power to the core upon entering sleep mode. This will
164 * require going through the boot firmware upon a wakeup event.
165 */
166
167 if (deep_sleeping) {
168 out_be32(&pmc_regs->mask, PMCER_ALL);
169
170 out_be32(&pmc_regs->config1,
171 in_be32(&pmc_regs->config1) | PMCCR1_POWER_OFF);
172
173 enable_kernel_fp();
174
175 mpc83xx_enter_deep_sleep(immrbase);
176
177 out_be32(&pmc_regs->config1,
178 in_be32(&pmc_regs->config1) & ~PMCCR1_POWER_OFF);
179
180 out_be32(&pmc_regs->mask, PMCER_PMCI);
181 } else {
182 out_be32(&pmc_regs->mask, PMCER_PMCI);
183
184 mpc6xx_enter_standby();
185 }
186
187 ret = 0;
188
189out:
190 out_be32(&pmc_regs->config1,
191 in_be32(&pmc_regs->config1) & ~PMCCR1_PME_EN);
192
193 return ret;
194}
195
196static void mpc83xx_suspend_finish(void)
197{
198 deep_sleeping = 0;
199}
200
201static int mpc83xx_suspend_valid(suspend_state_t state)
202{
203 return state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM;
204}
205
206static int mpc83xx_suspend_begin(suspend_state_t state)
207{
208 switch (state) {
209 case PM_SUSPEND_STANDBY:
210 deep_sleeping = 0;
211 return 0;
212
213 case PM_SUSPEND_MEM:
214 if (has_deep_sleep)
215 deep_sleeping = 1;
216
217 return 0;
218
219 default:
220 return -EINVAL;
221 }
222}
223
224static int agent_thread_fn(void *data)
225{
226 while (1) {
227 wait_event_interruptible(agent_wq, pci_pm_state >= 2);
228 try_to_freeze();
229
230 if (signal_pending(current) || pci_pm_state < 2)
231 continue;
232
233 /* With a preemptible kernel (or SMP), this could race with
234 * a userspace-driven suspend request. It's probably best
235 * to avoid mixing the two with such a configuration (or
236 * else fix it by adding a mutex to state_store that we can
237 * synchronize with).
238 */
239
240 wake_from_pci = 1;
241
242 pm_suspend(pci_pm_state == 3 ? PM_SUSPEND_MEM :
243 PM_SUSPEND_STANDBY);
244
245 wake_from_pci = 0;
246 }
247
248 return 0;
249}
250
251static void mpc83xx_set_agent(void)
252{
253 out_be32(&pmc_regs->config1, PMCCR1_USE_STATE);
254 out_be32(&pmc_regs->mask, PMCER_PMCI);
255
256 kthread_run(agent_thread_fn, NULL, "PCI power mgt");
257}
258
259static int mpc83xx_is_pci_agent(void)
260{
261 struct mpc83xx_rcw __iomem *rcw_regs;
262 int ret;
263
264 rcw_regs = ioremap(get_immrbase() + IMMR_RCW_OFFSET,
265 sizeof(struct mpc83xx_rcw));
266
267 if (!rcw_regs)
268 return -ENOMEM;
269
270 ret = !(in_be32(&rcw_regs->rcwhr) & RCW_PCI_HOST);
271
272 iounmap(rcw_regs);
273 return ret;
274}
275
276static struct platform_suspend_ops mpc83xx_suspend_ops = {
277 .valid = mpc83xx_suspend_valid,
278 .begin = mpc83xx_suspend_begin,
279 .enter = mpc83xx_suspend_enter,
280 .finish = mpc83xx_suspend_finish,
281};
282
283static int pmc_probe(struct of_device *ofdev,
284 const struct of_device_id *match)
285{
286 struct device_node *np = ofdev->node;
287 struct resource res;
288 struct pmc_type *type = match->data;
289 int ret = 0;
290
291 if (!of_device_is_available(np))
292 return -ENODEV;
293
294 has_deep_sleep = type->has_deep_sleep;
295 immrbase = get_immrbase();
296 pmc_dev = ofdev;
297
298 is_pci_agent = mpc83xx_is_pci_agent();
299 if (is_pci_agent < 0)
300 return is_pci_agent;
301
302 ret = of_address_to_resource(np, 0, &res);
303 if (ret)
304 return -ENODEV;
305
306 pmc_irq = irq_of_parse_and_map(np, 0);
307 if (pmc_irq != NO_IRQ) {
308 ret = request_irq(pmc_irq, pmc_irq_handler, IRQF_SHARED,
309 "pmc", ofdev);
310
311 if (ret)
312 return -EBUSY;
313 }
314
315 pmc_regs = ioremap(res.start, sizeof(struct mpc83xx_pmc));
316
317 if (!pmc_regs) {
318 ret = -ENOMEM;
319 goto out;
320 }
321
322 ret = of_address_to_resource(np, 1, &res);
323 if (ret) {
324 ret = -ENODEV;
325 goto out_pmc;
326 }
327
328 clock_regs = ioremap(res.start, sizeof(struct mpc83xx_pmc));
329
330 if (!clock_regs) {
331 ret = -ENOMEM;
332 goto out_pmc;
333 }
334
335 if (is_pci_agent)
336 mpc83xx_set_agent();
337
338 suspend_set_ops(&mpc83xx_suspend_ops);
339 return 0;
340
341out_pmc:
342 iounmap(pmc_regs);
343out:
344 if (pmc_irq != NO_IRQ)
345 free_irq(pmc_irq, ofdev);
346
347 return ret;
348}
349
350static int pmc_remove(struct of_device *ofdev)
351{
352 return -EPERM;
353};
354
355static struct pmc_type pmc_types[] = {
356 {
357 .has_deep_sleep = 1,
358 },
359 {
360 .has_deep_sleep = 0,
361 }
362};
363
364static struct of_device_id pmc_match[] = {
365 {
366 .compatible = "fsl,mpc8313-pmc",
367 .data = &pmc_types[0],
368 },
369 {
370 .compatible = "fsl,mpc8349-pmc",
371 .data = &pmc_types[1],
372 },
373 {}
374};
375
376static struct of_platform_driver pmc_driver = {
377 .name = "mpc83xx-pmc",
378 .match_table = pmc_match,
379 .probe = pmc_probe,
380 .remove = pmc_remove
381};
382
383static int pmc_init(void)
384{
385 return of_register_platform_driver(&pmc_driver);
386}
387
388module_init(pmc_init);