1 #include <linux/init.h>
2 #include <linux/suspend.h>
5 #include <asm/mpc52xx.h>
7 /* defined in lite5200_sleep.S and only used here */
8 extern void lite5200_low_power(void __iomem
*sram
, void __iomem
*mbar
);
10 static struct mpc52xx_cdm __iomem
*cdm
;
11 static struct mpc52xx_intr __iomem
*pic
;
12 static struct mpc52xx_sdma __iomem
*bes
;
13 static struct mpc52xx_xlb __iomem
*xlb
;
14 static struct mpc52xx_gpio __iomem
*gps
;
15 static struct mpc52xx_gpio_wkup __iomem
*gpw
;
16 static void __iomem
*pci
;
17 static void __iomem
*sram
;
18 static const int sram_size
= 0x4000; /* 16 kBytes */
19 static void __iomem
*mbar
;
21 static suspend_state_t lite5200_pm_target_state
;
23 static int lite5200_pm_valid(suspend_state_t state
)
26 case PM_SUSPEND_STANDBY
:
34 static int lite5200_pm_begin(suspend_state_t state
)
36 if (lite5200_pm_valid(state
)) {
37 lite5200_pm_target_state
= state
;
43 static int lite5200_pm_prepare(void)
45 struct device_node
*np
;
46 const struct of_device_id immr_ids
[] = {
47 { .compatible
= "fsl,mpc5200-immr", },
48 { .compatible
= "fsl,mpc5200b-immr", },
49 { .type
= "soc", .compatible
= "mpc5200", }, /* lite5200 */
50 { .type
= "builtin", .compatible
= "mpc5200", }, /* efika */
56 /* deep sleep? let mpc52xx code handle that */
57 if (lite5200_pm_target_state
== PM_SUSPEND_STANDBY
)
58 return mpc52xx_pm_prepare();
60 if (lite5200_pm_target_state
!= PM_SUSPEND_MEM
)
64 np
= of_find_matching_node(NULL
, immr_ids
);
65 regaddr_p
= of_get_address(np
, 0, NULL
, NULL
);
67 regaddr64
= of_translate_address(np
, regaddr_p
);
70 mbar
= ioremap((u32
) regaddr64
, 0xC000);
72 printk(KERN_ERR
"%s:%i Error mapping registers\n", __func__
, __LINE__
);
88 /* save and restore registers not bound to any real devices */
89 static struct mpc52xx_cdm scdm
;
90 static struct mpc52xx_intr spic
;
91 static struct mpc52xx_sdma sbes
;
92 static struct mpc52xx_xlb sxlb
;
93 static struct mpc52xx_gpio sgps
;
94 static struct mpc52xx_gpio_wkup sgpw
;
95 static char spci
[0x200];
97 static void lite5200_save_regs(void)
99 _memcpy_fromio(&spic
, pic
, sizeof(*pic
));
100 _memcpy_fromio(&sbes
, bes
, sizeof(*bes
));
101 _memcpy_fromio(&scdm
, cdm
, sizeof(*cdm
));
102 _memcpy_fromio(&sxlb
, xlb
, sizeof(*xlb
));
103 _memcpy_fromio(&sgps
, gps
, sizeof(*gps
));
104 _memcpy_fromio(&sgpw
, gpw
, sizeof(*gpw
));
105 _memcpy_fromio(spci
, pci
, 0x200);
107 _memcpy_fromio(saved_sram
, sram
, sram_size
);
110 static void lite5200_restore_regs(void)
113 _memcpy_toio(sram
, saved_sram
, sram_size
);
115 /* PCI Configuration */
116 _memcpy_toio(pci
, spci
, 0x200);
119 * GPIOs. Interrupt Master Enable has higher address then other
120 * registers, so just memcpy is ok.
122 _memcpy_toio(gpw
, &sgpw
, sizeof(*gpw
));
123 _memcpy_toio(gps
, &sgps
, sizeof(*gps
));
127 out_be32(&xlb
->snoop_window
, sxlb
.snoop_window
);
128 out_be32(&xlb
->master_priority
, sxlb
.master_priority
);
129 out_be32(&xlb
->master_pri_enable
, sxlb
.master_pri_enable
);
132 out_be32(&xlb
->int_enable
, sxlb
.int_enable
);
133 out_be32(&xlb
->config
, sxlb
.config
);
136 /* CDM - Clock Distribution Module */
137 out_8(&cdm
->ipb_clk_sel
, scdm
.ipb_clk_sel
);
138 out_8(&cdm
->pci_clk_sel
, scdm
.pci_clk_sel
);
140 out_8(&cdm
->ext_48mhz_en
, scdm
.ext_48mhz_en
);
141 out_8(&cdm
->fd_enable
, scdm
.fd_enable
);
142 out_be16(&cdm
->fd_counters
, scdm
.fd_counters
);
144 out_be32(&cdm
->clk_enables
, scdm
.clk_enables
);
146 out_8(&cdm
->osc_disable
, scdm
.osc_disable
);
148 out_be16(&cdm
->mclken_div_psc1
, scdm
.mclken_div_psc1
);
149 out_be16(&cdm
->mclken_div_psc2
, scdm
.mclken_div_psc2
);
150 out_be16(&cdm
->mclken_div_psc3
, scdm
.mclken_div_psc3
);
151 out_be16(&cdm
->mclken_div_psc6
, scdm
.mclken_div_psc6
);
155 out_be32(&bes
->taskBar
, sbes
.taskBar
);
156 out_be32(&bes
->currentPointer
, sbes
.currentPointer
);
157 out_be32(&bes
->endPointer
, sbes
.endPointer
);
158 out_be32(&bes
->variablePointer
, sbes
.variablePointer
);
160 out_8(&bes
->IntVect1
, sbes
.IntVect1
);
161 out_8(&bes
->IntVect2
, sbes
.IntVect2
);
162 out_be16(&bes
->PtdCntrl
, sbes
.PtdCntrl
);
165 out_8(&bes
->ipr
[i
], sbes
.ipr
[i
]);
167 out_be32(&bes
->cReqSelect
, sbes
.cReqSelect
);
168 out_be32(&bes
->task_size0
, sbes
.task_size0
);
169 out_be32(&bes
->task_size1
, sbes
.task_size1
);
170 out_be32(&bes
->MDEDebug
, sbes
.MDEDebug
);
171 out_be32(&bes
->ADSDebug
, sbes
.ADSDebug
);
172 out_be32(&bes
->Value1
, sbes
.Value1
);
173 out_be32(&bes
->Value2
, sbes
.Value2
);
174 out_be32(&bes
->Control
, sbes
.Control
);
175 out_be32(&bes
->Status
, sbes
.Status
);
176 out_be32(&bes
->PTDDebug
, sbes
.PTDDebug
);
180 out_be16(&bes
->tcr
[i
], sbes
.tcr
[i
]);
182 /* enable interrupts */
183 out_be32(&bes
->IntPend
, sbes
.IntPend
);
184 out_be32(&bes
->IntMask
, sbes
.IntMask
);
188 out_be32(&pic
->per_pri1
, spic
.per_pri1
);
189 out_be32(&pic
->per_pri2
, spic
.per_pri2
);
190 out_be32(&pic
->per_pri3
, spic
.per_pri3
);
192 out_be32(&pic
->main_pri1
, spic
.main_pri1
);
193 out_be32(&pic
->main_pri2
, spic
.main_pri2
);
195 out_be32(&pic
->enc_status
, spic
.enc_status
);
197 /* unmask and enable interrupts */
198 out_be32(&pic
->per_mask
, spic
.per_mask
);
199 out_be32(&pic
->main_mask
, spic
.main_mask
);
200 out_be32(&pic
->ctrl
, spic
.ctrl
);
203 static int lite5200_pm_enter(suspend_state_t state
)
205 /* deep sleep? let mpc52xx code handle that */
206 if (state
== PM_SUSPEND_STANDBY
) {
207 return mpc52xx_pm_enter(state
);
210 lite5200_save_regs();
212 /* effectively save FP regs */
215 lite5200_low_power(sram
, mbar
);
217 lite5200_restore_regs();
223 static void lite5200_pm_finish(void)
225 /* deep sleep? let mpc52xx code handle that */
226 if (lite5200_pm_target_state
== PM_SUSPEND_STANDBY
)
230 static void lite5200_pm_end(void)
232 lite5200_pm_target_state
= PM_SUSPEND_ON
;
235 static struct platform_suspend_ops lite5200_pm_ops
= {
236 .valid
= lite5200_pm_valid
,
237 .begin
= lite5200_pm_begin
,
238 .prepare
= lite5200_pm_prepare
,
239 .enter
= lite5200_pm_enter
,
240 .finish
= lite5200_pm_finish
,
241 .end
= lite5200_pm_end
,
244 int __init
lite5200_pm_init(void)
246 suspend_set_ops(&lite5200_pm_ops
);