]>
Commit | Line | Data |
---|---|---|
ee983079 | 1 | #include <linux/init.h> |
e6c5eb95 | 2 | #include <linux/suspend.h> |
ee983079 DP |
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 | ||
e6c5eb95 RW |
21 | static suspend_state_t lite5200_pm_target_state; |
22 | ||
ee983079 DP |
23 | static int lite5200_pm_valid(suspend_state_t state) |
24 | { | |
25 | switch (state) { | |
26 | case PM_SUSPEND_STANDBY: | |
27 | case PM_SUSPEND_MEM: | |
28 | return 1; | |
29 | default: | |
30 | return 0; | |
31 | } | |
32 | } | |
33 | ||
e6c5eb95 RW |
34 | static int lite5200_pm_set_target(suspend_state_t state) |
35 | { | |
36 | if (lite5200_pm_valid(state)) { | |
37 | lite5200_pm_target_state = state; | |
38 | return 0; | |
39 | } | |
40 | return -EINVAL; | |
41 | } | |
42 | ||
43 | static int lite5200_pm_prepare(void) | |
ee983079 | 44 | { |
75ca399e GL |
45 | struct device_node *np; |
46 | ||
ee983079 | 47 | /* deep sleep? let mpc52xx code handle that */ |
e6c5eb95 RW |
48 | if (lite5200_pm_target_state == PM_SUSPEND_STANDBY) |
49 | return mpc52xx_pm_prepare(); | |
ee983079 | 50 | |
e6c5eb95 | 51 | if (lite5200_pm_target_state != PM_SUSPEND_MEM) |
ee983079 DP |
52 | return -EINVAL; |
53 | ||
54 | /* map registers */ | |
75ca399e GL |
55 | np = of_find_compatible_node(NULL, NULL, "mpc5200"); |
56 | mbar = of_iomap(np, 0); | |
57 | of_node_put(np); | |
ee983079 DP |
58 | if (!mbar) { |
59 | printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__); | |
60 | return -ENOSYS; | |
61 | } | |
62 | ||
63 | cdm = mbar + 0x200; | |
64 | pic = mbar + 0x500; | |
65 | gps = mbar + 0xb00; | |
66 | gpw = mbar + 0xc00; | |
67 | bes = mbar + 0x1200; | |
68 | xlb = mbar + 0x1f00; | |
69 | sram = mbar + 0x8000; | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
74 | /* save and restore registers not bound to any real devices */ | |
75 | static struct mpc52xx_cdm scdm; | |
76 | static struct mpc52xx_intr spic; | |
77 | static struct mpc52xx_sdma sbes; | |
78 | static struct mpc52xx_xlb sxlb; | |
79 | static struct mpc52xx_gpio sgps; | |
80 | static struct mpc52xx_gpio_wkup sgpw; | |
81 | ||
82 | static void lite5200_save_regs(void) | |
83 | { | |
84 | _memcpy_fromio(&spic, pic, sizeof(*pic)); | |
85 | _memcpy_fromio(&sbes, bes, sizeof(*bes)); | |
86 | _memcpy_fromio(&scdm, cdm, sizeof(*cdm)); | |
87 | _memcpy_fromio(&sxlb, xlb, sizeof(*xlb)); | |
88 | _memcpy_fromio(&sgps, gps, sizeof(*gps)); | |
89 | _memcpy_fromio(&sgpw, gpw, sizeof(*gpw)); | |
90 | ||
91 | _memcpy_fromio(saved_sram, sram, sram_size); | |
92 | } | |
93 | ||
94 | static void lite5200_restore_regs(void) | |
95 | { | |
96 | int i; | |
97 | _memcpy_toio(sram, saved_sram, sram_size); | |
98 | ||
99 | ||
100 | /* | |
101 | * GPIOs. Interrupt Master Enable has higher address then other | |
102 | * registers, so just memcpy is ok. | |
103 | */ | |
104 | _memcpy_toio(gpw, &sgpw, sizeof(*gpw)); | |
105 | _memcpy_toio(gps, &sgps, sizeof(*gps)); | |
106 | ||
107 | ||
108 | /* XLB Arbitrer */ | |
109 | out_be32(&xlb->snoop_window, sxlb.snoop_window); | |
110 | out_be32(&xlb->master_priority, sxlb.master_priority); | |
111 | out_be32(&xlb->master_pri_enable, sxlb.master_pri_enable); | |
112 | ||
113 | /* enable */ | |
114 | out_be32(&xlb->int_enable, sxlb.int_enable); | |
115 | out_be32(&xlb->config, sxlb.config); | |
116 | ||
117 | ||
118 | /* CDM - Clock Distribution Module */ | |
119 | out_8(&cdm->ipb_clk_sel, scdm.ipb_clk_sel); | |
120 | out_8(&cdm->pci_clk_sel, scdm.pci_clk_sel); | |
121 | ||
122 | out_8(&cdm->ext_48mhz_en, scdm.ext_48mhz_en); | |
123 | out_8(&cdm->fd_enable, scdm.fd_enable); | |
124 | out_be16(&cdm->fd_counters, scdm.fd_counters); | |
125 | ||
126 | out_be32(&cdm->clk_enables, scdm.clk_enables); | |
127 | ||
128 | out_8(&cdm->osc_disable, scdm.osc_disable); | |
129 | ||
130 | out_be16(&cdm->mclken_div_psc1, scdm.mclken_div_psc1); | |
131 | out_be16(&cdm->mclken_div_psc2, scdm.mclken_div_psc2); | |
132 | out_be16(&cdm->mclken_div_psc3, scdm.mclken_div_psc3); | |
133 | out_be16(&cdm->mclken_div_psc6, scdm.mclken_div_psc6); | |
134 | ||
135 | ||
136 | /* BESTCOMM */ | |
137 | out_be32(&bes->taskBar, sbes.taskBar); | |
138 | out_be32(&bes->currentPointer, sbes.currentPointer); | |
139 | out_be32(&bes->endPointer, sbes.endPointer); | |
140 | out_be32(&bes->variablePointer, sbes.variablePointer); | |
141 | ||
142 | out_8(&bes->IntVect1, sbes.IntVect1); | |
143 | out_8(&bes->IntVect2, sbes.IntVect2); | |
144 | out_be16(&bes->PtdCntrl, sbes.PtdCntrl); | |
145 | ||
146 | for (i=0; i<32; i++) | |
147 | out_8(&bes->ipr[i], sbes.ipr[i]); | |
148 | ||
149 | out_be32(&bes->cReqSelect, sbes.cReqSelect); | |
150 | out_be32(&bes->task_size0, sbes.task_size0); | |
151 | out_be32(&bes->task_size1, sbes.task_size1); | |
152 | out_be32(&bes->MDEDebug, sbes.MDEDebug); | |
153 | out_be32(&bes->ADSDebug, sbes.ADSDebug); | |
154 | out_be32(&bes->Value1, sbes.Value1); | |
155 | out_be32(&bes->Value2, sbes.Value2); | |
156 | out_be32(&bes->Control, sbes.Control); | |
157 | out_be32(&bes->Status, sbes.Status); | |
158 | out_be32(&bes->PTDDebug, sbes.PTDDebug); | |
159 | ||
160 | /* restore tasks */ | |
161 | for (i=0; i<16; i++) | |
162 | out_be16(&bes->tcr[i], sbes.tcr[i]); | |
163 | ||
164 | /* enable interrupts */ | |
165 | out_be32(&bes->IntPend, sbes.IntPend); | |
166 | out_be32(&bes->IntMask, sbes.IntMask); | |
167 | ||
168 | ||
169 | /* PIC */ | |
170 | out_be32(&pic->per_pri1, spic.per_pri1); | |
171 | out_be32(&pic->per_pri2, spic.per_pri2); | |
172 | out_be32(&pic->per_pri3, spic.per_pri3); | |
173 | ||
174 | out_be32(&pic->main_pri1, spic.main_pri1); | |
175 | out_be32(&pic->main_pri2, spic.main_pri2); | |
176 | ||
177 | out_be32(&pic->enc_status, spic.enc_status); | |
178 | ||
179 | /* unmask and enable interrupts */ | |
180 | out_be32(&pic->per_mask, spic.per_mask); | |
181 | out_be32(&pic->main_mask, spic.main_mask); | |
182 | out_be32(&pic->ctrl, spic.ctrl); | |
183 | } | |
184 | ||
185 | static int lite5200_pm_enter(suspend_state_t state) | |
186 | { | |
187 | /* deep sleep? let mpc52xx code handle that */ | |
188 | if (state == PM_SUSPEND_STANDBY) { | |
189 | return mpc52xx_pm_enter(state); | |
190 | } | |
191 | ||
192 | lite5200_save_regs(); | |
193 | ||
194 | /* effectively save FP regs */ | |
195 | enable_kernel_fp(); | |
196 | ||
197 | lite5200_low_power(sram, mbar); | |
198 | ||
199 | lite5200_restore_regs(); | |
200 | ||
201 | /* restart jiffies */ | |
202 | wakeup_decrementer(); | |
203 | ||
204 | iounmap(mbar); | |
205 | return 0; | |
206 | } | |
207 | ||
e6c5eb95 | 208 | static void lite5200_pm_finish(void) |
ee983079 DP |
209 | { |
210 | /* deep sleep? let mpc52xx code handle that */ | |
e6c5eb95 RW |
211 | if (lite5200_pm_target_state == PM_SUSPEND_STANDBY) |
212 | mpc52xx_pm_finish(); | |
ee983079 DP |
213 | } |
214 | ||
e6c5eb95 | 215 | static struct platform_suspend_ops lite5200_pm_ops = { |
ee983079 | 216 | .valid = lite5200_pm_valid, |
e6c5eb95 | 217 | .set_target = lite5200_pm_set_target, |
ee983079 DP |
218 | .prepare = lite5200_pm_prepare, |
219 | .enter = lite5200_pm_enter, | |
220 | .finish = lite5200_pm_finish, | |
221 | }; | |
222 | ||
223 | int __init lite5200_pm_init(void) | |
224 | { | |
e6c5eb95 | 225 | suspend_set_ops(&lite5200_pm_ops); |
ee983079 DP |
226 | return 0; |
227 | } |