aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDomen Puncer <domen.puncer@telargo.com>2007-05-06 11:38:52 -0400
committerPaul Mackerras <paulus@samba.org>2007-05-07 06:31:15 -0400
commit2e1ee1f76684c5d4dd8e5a08cbf22d57f88769ed (patch)
tree0c69d52b8fb2cf82f257944d182ce1f8973ab9f9
parenta3481197783c187707090504062488862768260a (diff)
[POWERPC] mpc52xx suspend to deep-sleep
Implement deep-sleep on MPC52xx. SDRAM is put into self-refresh with help of SRAM code (alternatives would be code in FLASH, I-cache). Interrupt code must also not be in SDRAM, so put it in I-cache. MPC52xx core is static, so contents will remain intact even with clocks turned off. Signed-off-by: Domen Puncer <domen.puncer@telargo.com> Acked-by: Grant Likely <grant.likely@secretlab.ca> Signed-off-by: Sylvain Munaut <tnt@246tNt.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r--arch/powerpc/platforms/52xx/Makefile2
-rw-r--r--arch/powerpc/platforms/52xx/efika.c15
-rw-r--r--arch/powerpc/platforms/52xx/lite5200.c28
-rw-r--r--arch/powerpc/platforms/52xx/mpc52xx_pm.c191
-rw-r--r--arch/powerpc/platforms/52xx/mpc52xx_sleep.S154
-rw-r--r--include/asm-powerpc/mpc52xx.h11
6 files changed, 401 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/52xx/Makefile b/arch/powerpc/platforms/52xx/Makefile
index 07cdbcacf156..b91e39c84d46 100644
--- a/arch/powerpc/platforms/52xx/Makefile
+++ b/arch/powerpc/platforms/52xx/Makefile
@@ -8,3 +8,5 @@ endif
8 8
9obj-$(CONFIG_PPC_EFIKA) += efika.o 9obj-$(CONFIG_PPC_EFIKA) += efika.o
10obj-$(CONFIG_PPC_LITE5200) += lite5200.o 10obj-$(CONFIG_PPC_LITE5200) += lite5200.o
11
12obj-$(CONFIG_PM) += mpc52xx_sleep.o mpc52xx_pm.o
diff --git a/arch/powerpc/platforms/52xx/efika.c b/arch/powerpc/platforms/52xx/efika.c
index a6bba97314eb..f591a9fc19b9 100644
--- a/arch/powerpc/platforms/52xx/efika.c
+++ b/arch/powerpc/platforms/52xx/efika.c
@@ -184,6 +184,16 @@ static void efika_show_cpuinfo(struct seq_file *m)
184 of_node_put(root); 184 of_node_put(root);
185} 185}
186 186
187#ifdef CONFIG_PM
188static void efika_suspend_prepare(void __iomem *mbar)
189{
190 u8 pin = 4; /* GPIO_WKUP_4 (GPIO_PSC6_0 - IRDA_RX) */
191 u8 level = 1; /* wakeup on high level */
192 /* IOW. to wake it up, short pins 1 and 3 on IRDA connector */
193 mpc52xx_set_wakeup_gpio(pin, level);
194}
195#endif
196
187static void __init efika_setup_arch(void) 197static void __init efika_setup_arch(void)
188{ 198{
189 rtas_initialize(); 199 rtas_initialize();
@@ -199,6 +209,11 @@ static void __init efika_setup_arch(void)
199 209
200 efika_pcisetup(); 210 efika_pcisetup();
201 211
212#ifdef CONFIG_PM
213 mpc52xx_suspend.board_suspend_prepare = efika_suspend_prepare;
214 mpc52xx_pm_init();
215#endif
216
202 if (ppc_md.progress) 217 if (ppc_md.progress)
203 ppc_md.progress("Linux/PPC " UTS_RELEASE " running on Efika ;-)\n", 0x0); 218 ppc_md.progress("Linux/PPC " UTS_RELEASE " running on Efika ;-)\n", 0x0);
204} 219}
diff --git a/arch/powerpc/platforms/52xx/lite5200.c b/arch/powerpc/platforms/52xx/lite5200.c
index 8e2646ac417b..1cfc00dfb99a 100644
--- a/arch/powerpc/platforms/52xx/lite5200.c
+++ b/arch/powerpc/platforms/52xx/lite5200.c
@@ -85,6 +85,28 @@ error:
85 iounmap(gpio); 85 iounmap(gpio);
86} 86}
87 87
88#ifdef CONFIG_PM
89static u32 descr_a;
90static void lite5200_suspend_prepare(void __iomem *mbar)
91{
92 u8 pin = 1; /* GPIO_WKUP_1 (GPIO_PSC2_4) */
93 u8 level = 0; /* wakeup on low level */
94 mpc52xx_set_wakeup_gpio(pin, level);
95
96 /*
97 * power down usb port
98 * this needs to be called before of-ohci suspend code
99 */
100 descr_a = in_be32(mbar + 0x1048);
101 out_be32(mbar + 0x1048, (descr_a & ~0x200) | 0x100);
102}
103
104static void lite5200_resume_finish(void __iomem *mbar)
105{
106 out_be32(mbar + 0x1048, descr_a);
107}
108#endif
109
88static void __init lite5200_setup_arch(void) 110static void __init lite5200_setup_arch(void)
89{ 111{
90 struct device_node *np; 112 struct device_node *np;
@@ -107,6 +129,12 @@ static void __init lite5200_setup_arch(void)
107 mpc52xx_setup_cpu(); /* Generic */ 129 mpc52xx_setup_cpu(); /* Generic */
108 lite5200_setup_cpu(); /* Platorm specific */ 130 lite5200_setup_cpu(); /* Platorm specific */
109 131
132#ifdef CONFIG_PM
133 mpc52xx_suspend.board_suspend_prepare = lite5200_suspend_prepare;
134 mpc52xx_suspend.board_resume_finish = lite5200_resume_finish;
135 mpc52xx_pm_init();
136#endif
137
110#ifdef CONFIG_PCI 138#ifdef CONFIG_PCI
111 np = of_find_node_by_type(NULL, "pci"); 139 np = of_find_node_by_type(NULL, "pci");
112 if (np) { 140 if (np) {
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pm.c b/arch/powerpc/platforms/52xx/mpc52xx_pm.c
new file mode 100644
index 000000000000..fd40044d16cd
--- /dev/null
+++ b/arch/powerpc/platforms/52xx/mpc52xx_pm.c
@@ -0,0 +1,191 @@
1#include <linux/init.h>
2#include <linux/pm.h>
3#include <linux/io.h>
4#include <asm/time.h>
5#include <asm/cacheflush.h>
6#include <asm/mpc52xx.h>
7
8#include "mpc52xx_pic.h"
9
10
11/* these are defined in mpc52xx_sleep.S, and only used here */
12extern void mpc52xx_deep_sleep(void *sram, void *sdram_regs,
13 struct mpc52xx_cdm *, struct mpc52xx_intr *);
14extern void mpc52xx_ds_sram(void);
15extern const long mpc52xx_ds_sram_size;
16extern void mpc52xx_ds_cached(void);
17extern const long mpc52xx_ds_cached_size;
18
19static void __iomem *mbar;
20static void __iomem *sdram;
21static struct mpc52xx_cdm __iomem *cdm;
22static struct mpc52xx_intr __iomem *intr;
23static struct mpc52xx_gpio_wkup __iomem *gpiow;
24static void *sram;
25static int sram_size;
26
27struct mpc52xx_suspend mpc52xx_suspend;
28
29static int mpc52xx_pm_valid(suspend_state_t state)
30{
31 switch (state) {
32 case PM_SUSPEND_STANDBY:
33 return 1;
34 default:
35 return 0;
36 }
37}
38
39int mpc52xx_set_wakeup_gpio(u8 pin, u8 level)
40{
41 u16 tmp;
42
43 /* enable gpio */
44 out_8(&gpiow->wkup_gpioe, in_8(&gpiow->wkup_gpioe) | (1 << pin));
45 /* set as input */
46 out_8(&gpiow->wkup_ddr, in_8(&gpiow->wkup_ddr) & ~(1 << pin));
47 /* enable deep sleep interrupt */
48 out_8(&gpiow->wkup_inten, in_8(&gpiow->wkup_inten) | (1 << pin));
49 /* low/high level creates wakeup interrupt */
50 tmp = in_be16(&gpiow->wkup_itype);
51 tmp &= ~(0x3 << (pin * 2));
52 tmp |= (!level + 1) << (pin * 2);
53 out_be16(&gpiow->wkup_itype, tmp);
54 /* master enable */
55 out_8(&gpiow->wkup_maste, 1);
56
57 return 0;
58}
59
60int mpc52xx_pm_prepare(suspend_state_t state)
61{
62 if (state != PM_SUSPEND_STANDBY)
63 return -EINVAL;
64
65 /* map the whole register space */
66 mbar = mpc52xx_find_and_map("mpc5200");
67 if (!mbar) {
68 printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__);
69 return -ENOSYS;
70 }
71 /* these offsets are from mpc5200 users manual */
72 sdram = mbar + 0x100;
73 cdm = mbar + 0x200;
74 intr = mbar + 0x500;
75 gpiow = mbar + 0xc00;
76 sram = mbar + 0x8000; /* Those will be handled by the */
77 sram_size = 0x4000; /* bestcomm driver soon */
78
79 /* call board suspend code, if applicable */
80 if (mpc52xx_suspend.board_suspend_prepare)
81 mpc52xx_suspend.board_suspend_prepare(mbar);
82 else {
83 printk(KERN_ALERT "%s: %i don't know how to wake up the board\n",
84 __func__, __LINE__);
85 goto out_unmap;
86 }
87
88 return 0;
89
90 out_unmap:
91 iounmap(mbar);
92 return -ENOSYS;
93}
94
95
96char saved_sram[0x4000];
97
98int mpc52xx_pm_enter(suspend_state_t state)
99{
100 u32 clk_enables;
101 u32 msr, hid0;
102 u32 intr_main_mask;
103 void __iomem * irq_0x500 = (void *)CONFIG_KERNEL_START + 0x500;
104 unsigned long irq_0x500_stop = (unsigned long)irq_0x500 + mpc52xx_ds_cached_size;
105 char saved_0x500[mpc52xx_ds_cached_size];
106
107 /* disable all interrupts in PIC */
108 intr_main_mask = in_be32(&intr->main_mask);
109 out_be32(&intr->main_mask, intr_main_mask | 0x1ffff);
110
111 /* don't let DEC expire any time soon */
112 mtspr(SPRN_DEC, 0x7fffffff);
113
114 /* save SRAM */
115 memcpy(saved_sram, sram, sram_size);
116
117 /* copy low level suspend code to sram */
118 memcpy(sram, mpc52xx_ds_sram, mpc52xx_ds_sram_size);
119
120 out_8(&cdm->ccs_sleep_enable, 1);
121 out_8(&cdm->osc_sleep_enable, 1);
122 out_8(&cdm->ccs_qreq_test, 1);
123
124 /* disable all but SDRAM and bestcomm (SRAM) clocks */
125 clk_enables = in_be32(&cdm->clk_enables);
126 out_be32(&cdm->clk_enables, clk_enables & 0x00088000);
127
128 /* disable power management */
129 msr = mfmsr();
130 mtmsr(msr & ~MSR_POW);
131
132 /* enable sleep mode, disable others */
133 hid0 = mfspr(SPRN_HID0);
134 mtspr(SPRN_HID0, (hid0 & ~(HID0_DOZE | HID0_NAP | HID0_DPM)) | HID0_SLEEP);
135
136 /* save original, copy our irq handler, flush from dcache and invalidate icache */
137 memcpy(saved_0x500, irq_0x500, mpc52xx_ds_cached_size);
138 memcpy(irq_0x500, mpc52xx_ds_cached, mpc52xx_ds_cached_size);
139 flush_icache_range((unsigned long)irq_0x500, irq_0x500_stop);
140
141 /* call low-level sleep code */
142 mpc52xx_deep_sleep(sram, sdram, cdm, intr);
143
144 /* restore original irq handler */
145 memcpy(irq_0x500, saved_0x500, mpc52xx_ds_cached_size);
146 flush_icache_range((unsigned long)irq_0x500, irq_0x500_stop);
147
148 /* restore old power mode */
149 mtmsr(msr & ~MSR_POW);
150 mtspr(SPRN_HID0, hid0);
151 mtmsr(msr);
152
153 out_be32(&cdm->clk_enables, clk_enables);
154 out_8(&cdm->ccs_sleep_enable, 0);
155 out_8(&cdm->osc_sleep_enable, 0);
156
157 /* restore SRAM */
158 memcpy(sram, saved_sram, sram_size);
159
160 /* restart jiffies */
161 wakeup_decrementer();
162
163 /* reenable interrupts in PIC */
164 out_be32(&intr->main_mask, intr_main_mask);
165
166 return 0;
167}
168
169int mpc52xx_pm_finish(suspend_state_t state)
170{
171 /* call board resume code */
172 if (mpc52xx_suspend.board_resume_finish)
173 mpc52xx_suspend.board_resume_finish(mbar);
174
175 iounmap(mbar);
176
177 return 0;
178}
179
180static struct pm_ops mpc52xx_pm_ops = {
181 .valid = mpc52xx_pm_valid,
182 .prepare = mpc52xx_pm_prepare,
183 .enter = mpc52xx_pm_enter,
184 .finish = mpc52xx_pm_finish,
185};
186
187int __init mpc52xx_pm_init(void)
188{
189 pm_set_ops(&mpc52xx_pm_ops);
190 return 0;
191}
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_sleep.S b/arch/powerpc/platforms/52xx/mpc52xx_sleep.S
new file mode 100644
index 000000000000..4dc170b0ae18
--- /dev/null
+++ b/arch/powerpc/platforms/52xx/mpc52xx_sleep.S
@@ -0,0 +1,154 @@
1#include <asm/reg.h>
2#include <asm/ppc_asm.h>
3#include <asm/processor.h>
4
5
6.text
7
8_GLOBAL(mpc52xx_deep_sleep)
9mpc52xx_deep_sleep: /* args r3-r6: SRAM, SDRAM regs, CDM regs, INTR regs */
10
11 /* enable interrupts */
12 mfmsr r7
13 ori r7, r7, 0x8000 /* EE */
14 mtmsr r7
15 sync; isync;
16
17 li r10, 0 /* flag that irq handler sets */
18
19 /* enable tmr7 (or any other) interrupt */
20 lwz r8, 0x14(r6) /* intr->main_mask */
21 ori r8, r8, 0x1
22 xori r8, r8, 0x1
23 stw r8, 0x14(r6)
24 sync
25
26 /* emulate tmr7 interrupt */
27 li r8, 0x1
28 stw r8, 0x40(r6) /* intr->main_emulate */
29 sync
30
31 /* wait for it to happen */
321:
33 cmpi cr0, r10, 1
34 bne cr0, 1b
35
36 /* lock icache */
37 mfspr r10, SPRN_HID0
38 ori r10, r10, 0x2000
39 sync; isync;
40 mtspr SPRN_HID0, r10
41 sync; isync;
42
43
44 mflr r9 /* save LR */
45
46 /* jump to sram */
47 mtlr r3
48 blrl
49
50 mtlr r9 /* restore LR */
51
52 /* unlock icache */
53 mfspr r10, SPRN_HID0
54 ori r10, r10, 0x2000
55 xori r10, r10, 0x2000
56 sync; isync;
57 mtspr SPRN_HID0, r10
58 sync; isync;
59
60
61 /* return to C code */
62 blr
63
64
65_GLOBAL(mpc52xx_ds_sram)
66mpc52xx_ds_sram:
67 /* put SDRAM into self-refresh */
68 lwz r8, 0x4(r4) /* sdram->ctrl */
69
70 oris r8, r8, 0x8000 /* mode_en */
71 stw r8, 0x4(r4)
72 sync
73
74 ori r8, r8, 0x0002 /* soft_pre */
75 stw r8, 0x4(r4)
76 sync
77 xori r8, r8, 0x0002
78
79 xoris r8, r8, 0x8000 /* !mode_en */
80 stw r8, 0x4(r4)
81 sync
82
83 oris r8, r8, 0x5000
84 xoris r8, r8, 0x4000 /* ref_en !cke */
85 stw r8, 0x4(r4)
86 sync
87
88 /* disable SDRAM clock */
89 lwz r8, 0x14(r5) /* cdm->clkenable */
90 ori r8, r8, 0x0008
91 xori r8, r8, 0x0008
92 stw r8, 0x14(r5)
93 sync
94
95
96 /* put mpc5200 to sleep */
97 mfmsr r10
98 oris r10, r10, 0x0004 /* POW = 1 */
99 sync; isync;
100 mtmsr r10
101 sync; isync;
102
103
104 /* enable clock */
105 lwz r8, 0x14(r5)
106 ori r8, r8, 0x0008
107 stw r8, 0x14(r5)
108 sync
109
110 /* get ram out of self-refresh */
111 lwz r8, 0x4(r4)
112 oris r8, r8, 0x5000 /* cke ref_en */
113 stw r8, 0x4(r4)
114 sync
115
116 blr
117_GLOBAL(mpc52xx_ds_sram_size)
118mpc52xx_ds_sram_size:
119 .long $-mpc52xx_ds_sram
120
121
122/* ### interrupt handler for wakeup from deep-sleep ### */
123_GLOBAL(mpc52xx_ds_cached)
124mpc52xx_ds_cached:
125 mtspr SPRN_SPRG0, r7
126 mtspr SPRN_SPRG1, r8
127
128 /* disable emulated interrupt */
129 mfspr r7, 311 /* MBAR */
130 addi r7, r7, 0x540 /* intr->main_emul */
131 li r8, 0
132 stw r8, 0(r7)
133 sync
134 dcbf 0, r7
135
136 /* acknowledge wakeup, so CCS releases power pown */
137 mfspr r7, 311 /* MBAR */
138 addi r7, r7, 0x524 /* intr->enc_status */
139 lwz r8, 0(r7)
140 ori r8, r8, 0x0400
141 stw r8, 0(r7)
142 sync
143 dcbf 0, r7
144
145 /* flag - we handled the interrupt */
146 li r10, 1
147
148 mfspr r8, SPRN_SPRG1
149 mfspr r7, SPRN_SPRG0
150
151 rfi
152_GLOBAL(mpc52xx_ds_cached_size)
153mpc52xx_ds_cached_size:
154 .long $-mpc52xx_ds_cached
diff --git a/include/asm-powerpc/mpc52xx.h b/include/asm-powerpc/mpc52xx.h
index 7afd5bf94528..c4631f6dd4f9 100644
--- a/include/asm-powerpc/mpc52xx.h
+++ b/include/asm-powerpc/mpc52xx.h
@@ -253,5 +253,16 @@ extern int __init mpc52xx_add_bridge(struct device_node *node);
253 253
254#endif /* __ASSEMBLY__ */ 254#endif /* __ASSEMBLY__ */
255 255
256#ifdef CONFIG_PM
257struct mpc52xx_suspend {
258 void (*board_suspend_prepare)(void __iomem *mbar);
259 void (*board_resume_finish)(void __iomem *mbar);
260};
261
262extern struct mpc52xx_suspend mpc52xx_suspend;
263extern int __init mpc52xx_pm_init(void);
264extern int mpc52xx_set_wakeup_gpio(u8 pin, u8 level);
265#endif /* CONFIG_PM */
266
256#endif /* __ASM_POWERPC_MPC52xx_H__ */ 267#endif /* __ASM_POWERPC_MPC52xx_H__ */
257 268