diff options
author | Domen Puncer <domen.puncer@telargo.com> | 2007-07-17 16:32:31 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-09-19 01:25:34 -0400 |
commit | ee983079ce04641523b23b8ed02cc3503632351e (patch) | |
tree | 3c6b975e47fb38ee51cf3d57e8f5abb1a7699f66 /arch/powerpc/platforms/52xx/lite5200_pm.c | |
parent | 104f0cc2dcf7ce0ca7da041177233747d6aa0136 (diff) |
[POWERPC] MPC5200 low power mode
Low-power mode implementation for Lite5200b.
Some I/O registers are also saved here.
A recent U-Boot that supports this (lite5200b_PM_config) is needed.
Signed-off-by: Domen Puncer <domen.puncer@telargo.com>
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/platforms/52xx/lite5200_pm.c')
-rw-r--r-- | arch/powerpc/platforms/52xx/lite5200_pm.c | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/52xx/lite5200_pm.c b/arch/powerpc/platforms/52xx/lite5200_pm.c new file mode 100644 index 000000000000..f26afcd41757 --- /dev/null +++ b/arch/powerpc/platforms/52xx/lite5200_pm.c | |||
@@ -0,0 +1,213 @@ | |||
1 | #include <linux/init.h> | ||
2 | #include <linux/pm.h> | ||
3 | #include <asm/io.h> | ||
4 | #include <asm/time.h> | ||
5 | #include <asm/mpc52xx.h> | ||
6 | #include "mpc52xx_pic.h" | ||
7 | |||
8 | /* defined in lite5200_sleep.S and only used here */ | ||
9 | extern void lite5200_low_power(void __iomem *sram, void __iomem *mbar); | ||
10 | |||
11 | static struct mpc52xx_cdm __iomem *cdm; | ||
12 | static struct mpc52xx_intr __iomem *pic; | ||
13 | static struct mpc52xx_sdma __iomem *bes; | ||
14 | static struct mpc52xx_xlb __iomem *xlb; | ||
15 | static struct mpc52xx_gpio __iomem *gps; | ||
16 | static struct mpc52xx_gpio_wkup __iomem *gpw; | ||
17 | static void __iomem *sram; | ||
18 | static const int sram_size = 0x4000; /* 16 kBytes */ | ||
19 | static void __iomem *mbar; | ||
20 | |||
21 | static int lite5200_pm_valid(suspend_state_t state) | ||
22 | { | ||
23 | switch (state) { | ||
24 | case PM_SUSPEND_STANDBY: | ||
25 | case PM_SUSPEND_MEM: | ||
26 | return 1; | ||
27 | default: | ||
28 | return 0; | ||
29 | } | ||
30 | } | ||
31 | |||
32 | static int lite5200_pm_prepare(suspend_state_t state) | ||
33 | { | ||
34 | /* deep sleep? let mpc52xx code handle that */ | ||
35 | if (state == PM_SUSPEND_STANDBY) | ||
36 | return mpc52xx_pm_prepare(state); | ||
37 | |||
38 | if (state != PM_SUSPEND_MEM) | ||
39 | return -EINVAL; | ||
40 | |||
41 | /* map registers */ | ||
42 | mbar = mpc52xx_find_and_map("mpc5200"); | ||
43 | if (!mbar) { | ||
44 | printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__); | ||
45 | return -ENOSYS; | ||
46 | } | ||
47 | |||
48 | cdm = mbar + 0x200; | ||
49 | pic = mbar + 0x500; | ||
50 | gps = mbar + 0xb00; | ||
51 | gpw = mbar + 0xc00; | ||
52 | bes = mbar + 0x1200; | ||
53 | xlb = mbar + 0x1f00; | ||
54 | sram = mbar + 0x8000; | ||
55 | |||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | /* save and restore registers not bound to any real devices */ | ||
60 | static struct mpc52xx_cdm scdm; | ||
61 | static struct mpc52xx_intr spic; | ||
62 | static struct mpc52xx_sdma sbes; | ||
63 | static struct mpc52xx_xlb sxlb; | ||
64 | static struct mpc52xx_gpio sgps; | ||
65 | static struct mpc52xx_gpio_wkup sgpw; | ||
66 | |||
67 | static void lite5200_save_regs(void) | ||
68 | { | ||
69 | _memcpy_fromio(&spic, pic, sizeof(*pic)); | ||
70 | _memcpy_fromio(&sbes, bes, sizeof(*bes)); | ||
71 | _memcpy_fromio(&scdm, cdm, sizeof(*cdm)); | ||
72 | _memcpy_fromio(&sxlb, xlb, sizeof(*xlb)); | ||
73 | _memcpy_fromio(&sgps, gps, sizeof(*gps)); | ||
74 | _memcpy_fromio(&sgpw, gpw, sizeof(*gpw)); | ||
75 | |||
76 | _memcpy_fromio(saved_sram, sram, sram_size); | ||
77 | } | ||
78 | |||
79 | static void lite5200_restore_regs(void) | ||
80 | { | ||
81 | int i; | ||
82 | _memcpy_toio(sram, saved_sram, sram_size); | ||
83 | |||
84 | |||
85 | /* | ||
86 | * GPIOs. Interrupt Master Enable has higher address then other | ||
87 | * registers, so just memcpy is ok. | ||
88 | */ | ||
89 | _memcpy_toio(gpw, &sgpw, sizeof(*gpw)); | ||
90 | _memcpy_toio(gps, &sgps, sizeof(*gps)); | ||
91 | |||
92 | |||
93 | /* XLB Arbitrer */ | ||
94 | out_be32(&xlb->snoop_window, sxlb.snoop_window); | ||
95 | out_be32(&xlb->master_priority, sxlb.master_priority); | ||
96 | out_be32(&xlb->master_pri_enable, sxlb.master_pri_enable); | ||
97 | |||
98 | /* enable */ | ||
99 | out_be32(&xlb->int_enable, sxlb.int_enable); | ||
100 | out_be32(&xlb->config, sxlb.config); | ||
101 | |||
102 | |||
103 | /* CDM - Clock Distribution Module */ | ||
104 | out_8(&cdm->ipb_clk_sel, scdm.ipb_clk_sel); | ||
105 | out_8(&cdm->pci_clk_sel, scdm.pci_clk_sel); | ||
106 | |||
107 | out_8(&cdm->ext_48mhz_en, scdm.ext_48mhz_en); | ||
108 | out_8(&cdm->fd_enable, scdm.fd_enable); | ||
109 | out_be16(&cdm->fd_counters, scdm.fd_counters); | ||
110 | |||
111 | out_be32(&cdm->clk_enables, scdm.clk_enables); | ||
112 | |||
113 | out_8(&cdm->osc_disable, scdm.osc_disable); | ||
114 | |||
115 | out_be16(&cdm->mclken_div_psc1, scdm.mclken_div_psc1); | ||
116 | out_be16(&cdm->mclken_div_psc2, scdm.mclken_div_psc2); | ||
117 | out_be16(&cdm->mclken_div_psc3, scdm.mclken_div_psc3); | ||
118 | out_be16(&cdm->mclken_div_psc6, scdm.mclken_div_psc6); | ||
119 | |||
120 | |||
121 | /* BESTCOMM */ | ||
122 | out_be32(&bes->taskBar, sbes.taskBar); | ||
123 | out_be32(&bes->currentPointer, sbes.currentPointer); | ||
124 | out_be32(&bes->endPointer, sbes.endPointer); | ||
125 | out_be32(&bes->variablePointer, sbes.variablePointer); | ||
126 | |||
127 | out_8(&bes->IntVect1, sbes.IntVect1); | ||
128 | out_8(&bes->IntVect2, sbes.IntVect2); | ||
129 | out_be16(&bes->PtdCntrl, sbes.PtdCntrl); | ||
130 | |||
131 | for (i=0; i<32; i++) | ||
132 | out_8(&bes->ipr[i], sbes.ipr[i]); | ||
133 | |||
134 | out_be32(&bes->cReqSelect, sbes.cReqSelect); | ||
135 | out_be32(&bes->task_size0, sbes.task_size0); | ||
136 | out_be32(&bes->task_size1, sbes.task_size1); | ||
137 | out_be32(&bes->MDEDebug, sbes.MDEDebug); | ||
138 | out_be32(&bes->ADSDebug, sbes.ADSDebug); | ||
139 | out_be32(&bes->Value1, sbes.Value1); | ||
140 | out_be32(&bes->Value2, sbes.Value2); | ||
141 | out_be32(&bes->Control, sbes.Control); | ||
142 | out_be32(&bes->Status, sbes.Status); | ||
143 | out_be32(&bes->PTDDebug, sbes.PTDDebug); | ||
144 | |||
145 | /* restore tasks */ | ||
146 | for (i=0; i<16; i++) | ||
147 | out_be16(&bes->tcr[i], sbes.tcr[i]); | ||
148 | |||
149 | /* enable interrupts */ | ||
150 | out_be32(&bes->IntPend, sbes.IntPend); | ||
151 | out_be32(&bes->IntMask, sbes.IntMask); | ||
152 | |||
153 | |||
154 | /* PIC */ | ||
155 | out_be32(&pic->per_pri1, spic.per_pri1); | ||
156 | out_be32(&pic->per_pri2, spic.per_pri2); | ||
157 | out_be32(&pic->per_pri3, spic.per_pri3); | ||
158 | |||
159 | out_be32(&pic->main_pri1, spic.main_pri1); | ||
160 | out_be32(&pic->main_pri2, spic.main_pri2); | ||
161 | |||
162 | out_be32(&pic->enc_status, spic.enc_status); | ||
163 | |||
164 | /* unmask and enable interrupts */ | ||
165 | out_be32(&pic->per_mask, spic.per_mask); | ||
166 | out_be32(&pic->main_mask, spic.main_mask); | ||
167 | out_be32(&pic->ctrl, spic.ctrl); | ||
168 | } | ||
169 | |||
170 | static int lite5200_pm_enter(suspend_state_t state) | ||
171 | { | ||
172 | /* deep sleep? let mpc52xx code handle that */ | ||
173 | if (state == PM_SUSPEND_STANDBY) { | ||
174 | return mpc52xx_pm_enter(state); | ||
175 | } | ||
176 | |||
177 | lite5200_save_regs(); | ||
178 | |||
179 | /* effectively save FP regs */ | ||
180 | enable_kernel_fp(); | ||
181 | |||
182 | lite5200_low_power(sram, mbar); | ||
183 | |||
184 | lite5200_restore_regs(); | ||
185 | |||
186 | /* restart jiffies */ | ||
187 | wakeup_decrementer(); | ||
188 | |||
189 | iounmap(mbar); | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static int lite5200_pm_finish(suspend_state_t state) | ||
194 | { | ||
195 | /* deep sleep? let mpc52xx code handle that */ | ||
196 | if (state == PM_SUSPEND_STANDBY) { | ||
197 | return mpc52xx_pm_finish(state); | ||
198 | } | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static struct pm_ops lite5200_pm_ops = { | ||
203 | .valid = lite5200_pm_valid, | ||
204 | .prepare = lite5200_pm_prepare, | ||
205 | .enter = lite5200_pm_enter, | ||
206 | .finish = lite5200_pm_finish, | ||
207 | }; | ||
208 | |||
209 | int __init lite5200_pm_init(void) | ||
210 | { | ||
211 | pm_set_ops(&lite5200_pm_ops); | ||
212 | return 0; | ||
213 | } | ||