[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>
This commit is contained in:
Родитель
104f0cc2dc
Коммит
ee983079ce
|
@ -10,3 +10,6 @@ obj-$(CONFIG_PPC_EFIKA) += efika.o
|
||||||
obj-$(CONFIG_PPC_LITE5200) += lite5200.o
|
obj-$(CONFIG_PPC_LITE5200) += lite5200.o
|
||||||
|
|
||||||
obj-$(CONFIG_PM) += mpc52xx_sleep.o mpc52xx_pm.o
|
obj-$(CONFIG_PM) += mpc52xx_sleep.o mpc52xx_pm.o
|
||||||
|
ifeq ($(CONFIG_PPC_LITE5200),y)
|
||||||
|
obj-$(CONFIG_PM) += lite5200_sleep.o lite5200_pm.o
|
||||||
|
endif
|
||||||
|
|
|
@ -85,7 +85,6 @@ error:
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static u32 descr_a;
|
|
||||||
static void lite5200_suspend_prepare(void __iomem *mbar)
|
static void lite5200_suspend_prepare(void __iomem *mbar)
|
||||||
{
|
{
|
||||||
u8 pin = 1; /* GPIO_WKUP_1 (GPIO_PSC2_4) */
|
u8 pin = 1; /* GPIO_WKUP_1 (GPIO_PSC2_4) */
|
||||||
|
@ -96,13 +95,18 @@ static void lite5200_suspend_prepare(void __iomem *mbar)
|
||||||
* power down usb port
|
* power down usb port
|
||||||
* this needs to be called before of-ohci suspend code
|
* this needs to be called before of-ohci suspend code
|
||||||
*/
|
*/
|
||||||
descr_a = in_be32(mbar + 0x1048);
|
|
||||||
out_be32(mbar + 0x1048, (descr_a & ~0x200) | 0x100);
|
/* set ports to "power switched" and "powered at the same time"
|
||||||
|
* USB Rh descriptor A: NPS = 0, PSM = 0 */
|
||||||
|
out_be32(mbar + 0x1048, in_be32(mbar + 0x1048) & ~0x300);
|
||||||
|
/* USB Rh status: LPS = 1 - turn off power */
|
||||||
|
out_be32(mbar + 0x1050, 0x00000001);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lite5200_resume_finish(void __iomem *mbar)
|
static void lite5200_resume_finish(void __iomem *mbar)
|
||||||
{
|
{
|
||||||
out_be32(mbar + 0x1048, descr_a);
|
/* USB Rh status: LPSC = 1 - turn on power */
|
||||||
|
out_be32(mbar + 0x1050, 0x00010000);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -122,7 +126,7 @@ static void __init lite5200_setup_arch(void)
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
mpc52xx_suspend.board_suspend_prepare = lite5200_suspend_prepare;
|
mpc52xx_suspend.board_suspend_prepare = lite5200_suspend_prepare;
|
||||||
mpc52xx_suspend.board_resume_finish = lite5200_resume_finish;
|
mpc52xx_suspend.board_resume_finish = lite5200_resume_finish;
|
||||||
mpc52xx_pm_init();
|
lite5200_pm_init();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PCI
|
#ifdef CONFIG_PCI
|
||||||
|
|
|
@ -0,0 +1,213 @@
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/pm.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/time.h>
|
||||||
|
#include <asm/mpc52xx.h>
|
||||||
|
#include "mpc52xx_pic.h"
|
||||||
|
|
||||||
|
/* defined in lite5200_sleep.S and only used here */
|
||||||
|
extern void lite5200_low_power(void __iomem *sram, void __iomem *mbar);
|
||||||
|
|
||||||
|
static struct mpc52xx_cdm __iomem *cdm;
|
||||||
|
static struct mpc52xx_intr __iomem *pic;
|
||||||
|
static struct mpc52xx_sdma __iomem *bes;
|
||||||
|
static struct mpc52xx_xlb __iomem *xlb;
|
||||||
|
static struct mpc52xx_gpio __iomem *gps;
|
||||||
|
static struct mpc52xx_gpio_wkup __iomem *gpw;
|
||||||
|
static void __iomem *sram;
|
||||||
|
static const int sram_size = 0x4000; /* 16 kBytes */
|
||||||
|
static void __iomem *mbar;
|
||||||
|
|
||||||
|
static int lite5200_pm_valid(suspend_state_t state)
|
||||||
|
{
|
||||||
|
switch (state) {
|
||||||
|
case PM_SUSPEND_STANDBY:
|
||||||
|
case PM_SUSPEND_MEM:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lite5200_pm_prepare(suspend_state_t state)
|
||||||
|
{
|
||||||
|
/* deep sleep? let mpc52xx code handle that */
|
||||||
|
if (state == PM_SUSPEND_STANDBY)
|
||||||
|
return mpc52xx_pm_prepare(state);
|
||||||
|
|
||||||
|
if (state != PM_SUSPEND_MEM)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* map registers */
|
||||||
|
mbar = mpc52xx_find_and_map("mpc5200");
|
||||||
|
if (!mbar) {
|
||||||
|
printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__);
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
cdm = mbar + 0x200;
|
||||||
|
pic = mbar + 0x500;
|
||||||
|
gps = mbar + 0xb00;
|
||||||
|
gpw = mbar + 0xc00;
|
||||||
|
bes = mbar + 0x1200;
|
||||||
|
xlb = mbar + 0x1f00;
|
||||||
|
sram = mbar + 0x8000;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save and restore registers not bound to any real devices */
|
||||||
|
static struct mpc52xx_cdm scdm;
|
||||||
|
static struct mpc52xx_intr spic;
|
||||||
|
static struct mpc52xx_sdma sbes;
|
||||||
|
static struct mpc52xx_xlb sxlb;
|
||||||
|
static struct mpc52xx_gpio sgps;
|
||||||
|
static struct mpc52xx_gpio_wkup sgpw;
|
||||||
|
|
||||||
|
static void lite5200_save_regs(void)
|
||||||
|
{
|
||||||
|
_memcpy_fromio(&spic, pic, sizeof(*pic));
|
||||||
|
_memcpy_fromio(&sbes, bes, sizeof(*bes));
|
||||||
|
_memcpy_fromio(&scdm, cdm, sizeof(*cdm));
|
||||||
|
_memcpy_fromio(&sxlb, xlb, sizeof(*xlb));
|
||||||
|
_memcpy_fromio(&sgps, gps, sizeof(*gps));
|
||||||
|
_memcpy_fromio(&sgpw, gpw, sizeof(*gpw));
|
||||||
|
|
||||||
|
_memcpy_fromio(saved_sram, sram, sram_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lite5200_restore_regs(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
_memcpy_toio(sram, saved_sram, sram_size);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GPIOs. Interrupt Master Enable has higher address then other
|
||||||
|
* registers, so just memcpy is ok.
|
||||||
|
*/
|
||||||
|
_memcpy_toio(gpw, &sgpw, sizeof(*gpw));
|
||||||
|
_memcpy_toio(gps, &sgps, sizeof(*gps));
|
||||||
|
|
||||||
|
|
||||||
|
/* XLB Arbitrer */
|
||||||
|
out_be32(&xlb->snoop_window, sxlb.snoop_window);
|
||||||
|
out_be32(&xlb->master_priority, sxlb.master_priority);
|
||||||
|
out_be32(&xlb->master_pri_enable, sxlb.master_pri_enable);
|
||||||
|
|
||||||
|
/* enable */
|
||||||
|
out_be32(&xlb->int_enable, sxlb.int_enable);
|
||||||
|
out_be32(&xlb->config, sxlb.config);
|
||||||
|
|
||||||
|
|
||||||
|
/* CDM - Clock Distribution Module */
|
||||||
|
out_8(&cdm->ipb_clk_sel, scdm.ipb_clk_sel);
|
||||||
|
out_8(&cdm->pci_clk_sel, scdm.pci_clk_sel);
|
||||||
|
|
||||||
|
out_8(&cdm->ext_48mhz_en, scdm.ext_48mhz_en);
|
||||||
|
out_8(&cdm->fd_enable, scdm.fd_enable);
|
||||||
|
out_be16(&cdm->fd_counters, scdm.fd_counters);
|
||||||
|
|
||||||
|
out_be32(&cdm->clk_enables, scdm.clk_enables);
|
||||||
|
|
||||||
|
out_8(&cdm->osc_disable, scdm.osc_disable);
|
||||||
|
|
||||||
|
out_be16(&cdm->mclken_div_psc1, scdm.mclken_div_psc1);
|
||||||
|
out_be16(&cdm->mclken_div_psc2, scdm.mclken_div_psc2);
|
||||||
|
out_be16(&cdm->mclken_div_psc3, scdm.mclken_div_psc3);
|
||||||
|
out_be16(&cdm->mclken_div_psc6, scdm.mclken_div_psc6);
|
||||||
|
|
||||||
|
|
||||||
|
/* BESTCOMM */
|
||||||
|
out_be32(&bes->taskBar, sbes.taskBar);
|
||||||
|
out_be32(&bes->currentPointer, sbes.currentPointer);
|
||||||
|
out_be32(&bes->endPointer, sbes.endPointer);
|
||||||
|
out_be32(&bes->variablePointer, sbes.variablePointer);
|
||||||
|
|
||||||
|
out_8(&bes->IntVect1, sbes.IntVect1);
|
||||||
|
out_8(&bes->IntVect2, sbes.IntVect2);
|
||||||
|
out_be16(&bes->PtdCntrl, sbes.PtdCntrl);
|
||||||
|
|
||||||
|
for (i=0; i<32; i++)
|
||||||
|
out_8(&bes->ipr[i], sbes.ipr[i]);
|
||||||
|
|
||||||
|
out_be32(&bes->cReqSelect, sbes.cReqSelect);
|
||||||
|
out_be32(&bes->task_size0, sbes.task_size0);
|
||||||
|
out_be32(&bes->task_size1, sbes.task_size1);
|
||||||
|
out_be32(&bes->MDEDebug, sbes.MDEDebug);
|
||||||
|
out_be32(&bes->ADSDebug, sbes.ADSDebug);
|
||||||
|
out_be32(&bes->Value1, sbes.Value1);
|
||||||
|
out_be32(&bes->Value2, sbes.Value2);
|
||||||
|
out_be32(&bes->Control, sbes.Control);
|
||||||
|
out_be32(&bes->Status, sbes.Status);
|
||||||
|
out_be32(&bes->PTDDebug, sbes.PTDDebug);
|
||||||
|
|
||||||
|
/* restore tasks */
|
||||||
|
for (i=0; i<16; i++)
|
||||||
|
out_be16(&bes->tcr[i], sbes.tcr[i]);
|
||||||
|
|
||||||
|
/* enable interrupts */
|
||||||
|
out_be32(&bes->IntPend, sbes.IntPend);
|
||||||
|
out_be32(&bes->IntMask, sbes.IntMask);
|
||||||
|
|
||||||
|
|
||||||
|
/* PIC */
|
||||||
|
out_be32(&pic->per_pri1, spic.per_pri1);
|
||||||
|
out_be32(&pic->per_pri2, spic.per_pri2);
|
||||||
|
out_be32(&pic->per_pri3, spic.per_pri3);
|
||||||
|
|
||||||
|
out_be32(&pic->main_pri1, spic.main_pri1);
|
||||||
|
out_be32(&pic->main_pri2, spic.main_pri2);
|
||||||
|
|
||||||
|
out_be32(&pic->enc_status, spic.enc_status);
|
||||||
|
|
||||||
|
/* unmask and enable interrupts */
|
||||||
|
out_be32(&pic->per_mask, spic.per_mask);
|
||||||
|
out_be32(&pic->main_mask, spic.main_mask);
|
||||||
|
out_be32(&pic->ctrl, spic.ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lite5200_pm_enter(suspend_state_t state)
|
||||||
|
{
|
||||||
|
/* deep sleep? let mpc52xx code handle that */
|
||||||
|
if (state == PM_SUSPEND_STANDBY) {
|
||||||
|
return mpc52xx_pm_enter(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
lite5200_save_regs();
|
||||||
|
|
||||||
|
/* effectively save FP regs */
|
||||||
|
enable_kernel_fp();
|
||||||
|
|
||||||
|
lite5200_low_power(sram, mbar);
|
||||||
|
|
||||||
|
lite5200_restore_regs();
|
||||||
|
|
||||||
|
/* restart jiffies */
|
||||||
|
wakeup_decrementer();
|
||||||
|
|
||||||
|
iounmap(mbar);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lite5200_pm_finish(suspend_state_t state)
|
||||||
|
{
|
||||||
|
/* deep sleep? let mpc52xx code handle that */
|
||||||
|
if (state == PM_SUSPEND_STANDBY) {
|
||||||
|
return mpc52xx_pm_finish(state);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pm_ops lite5200_pm_ops = {
|
||||||
|
.valid = lite5200_pm_valid,
|
||||||
|
.prepare = lite5200_pm_prepare,
|
||||||
|
.enter = lite5200_pm_enter,
|
||||||
|
.finish = lite5200_pm_finish,
|
||||||
|
};
|
||||||
|
|
||||||
|
int __init lite5200_pm_init(void)
|
||||||
|
{
|
||||||
|
pm_set_ops(&lite5200_pm_ops);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,412 @@
|
||||||
|
#include <asm/reg.h>
|
||||||
|
#include <asm/ppc_asm.h>
|
||||||
|
#include <asm/processor.h>
|
||||||
|
#include <asm/cache.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define SDRAM_CTRL 0x104
|
||||||
|
#define SC_MODE_EN (1<<31)
|
||||||
|
#define SC_CKE (1<<30)
|
||||||
|
#define SC_REF_EN (1<<28)
|
||||||
|
#define SC_SOFT_PRE (1<<1)
|
||||||
|
|
||||||
|
#define GPIOW_GPIOE 0xc00
|
||||||
|
#define GPIOW_DDR 0xc08
|
||||||
|
#define GPIOW_DVO 0xc0c
|
||||||
|
|
||||||
|
#define CDM_CE 0x214
|
||||||
|
#define CDM_SDRAM (1<<3)
|
||||||
|
|
||||||
|
|
||||||
|
/* helpers... beware: r10 and r4 are overwritten */
|
||||||
|
#define SAVE_SPRN(reg, addr) \
|
||||||
|
mfspr r10, SPRN_##reg; \
|
||||||
|
stw r10, ((addr)*4)(r4);
|
||||||
|
|
||||||
|
#define LOAD_SPRN(reg, addr) \
|
||||||
|
lwz r10, ((addr)*4)(r4); \
|
||||||
|
mtspr SPRN_##reg, r10; \
|
||||||
|
sync; \
|
||||||
|
isync;
|
||||||
|
|
||||||
|
|
||||||
|
.data
|
||||||
|
registers:
|
||||||
|
.space 0x5c*4
|
||||||
|
.text
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
/* low-power mode with help of M68HLC908QT1 */
|
||||||
|
|
||||||
|
.globl lite5200_low_power
|
||||||
|
lite5200_low_power:
|
||||||
|
|
||||||
|
mr r7, r3 /* save SRAM va */
|
||||||
|
mr r8, r4 /* save MBAR va */
|
||||||
|
|
||||||
|
/* setup wakeup address for u-boot at physical location 0x0 */
|
||||||
|
lis r3, CONFIG_KERNEL_START@h
|
||||||
|
lis r4, lite5200_wakeup@h
|
||||||
|
ori r4, r4, lite5200_wakeup@l
|
||||||
|
sub r4, r4, r3
|
||||||
|
stw r4, 0(r3)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* save stuff BDI overwrites
|
||||||
|
* 0xf0 (0xe0->0x100 gets overwritten when BDI connected;
|
||||||
|
* even when CONFIG_BDI* is disabled and MMU XLAT commented; heisenbug?))
|
||||||
|
* WARNING: self-refresh doesn't seem to work when BDI2000 is connected,
|
||||||
|
* possibly because BDI sets SDRAM registers before wakeup code does
|
||||||
|
*/
|
||||||
|
lis r4, registers@h
|
||||||
|
ori r4, r4, registers@l
|
||||||
|
lwz r10, 0xf0(r3)
|
||||||
|
stw r10, (0x1d*4)(r4)
|
||||||
|
|
||||||
|
/* save registers to r4 [destroys r10] */
|
||||||
|
SAVE_SPRN(LR, 0x1c)
|
||||||
|
bl save_regs
|
||||||
|
|
||||||
|
/* flush caches [destroys r3, r4] */
|
||||||
|
bl flush_data_cache
|
||||||
|
|
||||||
|
|
||||||
|
/* copy code to sram */
|
||||||
|
mr r4, r7
|
||||||
|
li r3, (sram_code_end - sram_code)/4
|
||||||
|
mtctr r3
|
||||||
|
lis r3, sram_code@h
|
||||||
|
ori r3, r3, sram_code@l
|
||||||
|
1:
|
||||||
|
lwz r5, 0(r3)
|
||||||
|
stw r5, 0(r4)
|
||||||
|
addi r3, r3, 4
|
||||||
|
addi r4, r4, 4
|
||||||
|
bdnz 1b
|
||||||
|
|
||||||
|
/* get tb_ticks_per_usec */
|
||||||
|
lis r3, tb_ticks_per_usec@h
|
||||||
|
lwz r11, tb_ticks_per_usec@l(r3)
|
||||||
|
|
||||||
|
/* disable I and D caches */
|
||||||
|
mfspr r3, SPRN_HID0
|
||||||
|
ori r3, r3, HID0_ICE | HID0_DCE
|
||||||
|
xori r3, r3, HID0_ICE | HID0_DCE
|
||||||
|
sync; isync;
|
||||||
|
mtspr SPRN_HID0, r3
|
||||||
|
sync; isync;
|
||||||
|
|
||||||
|
/* jump to sram */
|
||||||
|
mtlr r7
|
||||||
|
blrl
|
||||||
|
/* doesn't return */
|
||||||
|
|
||||||
|
|
||||||
|
sram_code:
|
||||||
|
/* self refresh */
|
||||||
|
lwz r4, SDRAM_CTRL(r8)
|
||||||
|
|
||||||
|
/* send NOP (precharge) */
|
||||||
|
oris r4, r4, SC_MODE_EN@h /* mode_en */
|
||||||
|
stw r4, SDRAM_CTRL(r8)
|
||||||
|
sync
|
||||||
|
|
||||||
|
ori r4, r4, SC_SOFT_PRE /* soft_pre */
|
||||||
|
stw r4, SDRAM_CTRL(r8)
|
||||||
|
sync
|
||||||
|
xori r4, r4, SC_SOFT_PRE
|
||||||
|
|
||||||
|
xoris r4, r4, SC_MODE_EN@h /* !mode_en */
|
||||||
|
stw r4, SDRAM_CTRL(r8)
|
||||||
|
sync
|
||||||
|
|
||||||
|
/* delay (for NOP to finish) */
|
||||||
|
li r12, 1
|
||||||
|
bl udelay
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mode_en must not be set when enabling self-refresh
|
||||||
|
* send AR with CKE low (self-refresh)
|
||||||
|
*/
|
||||||
|
oris r4, r4, (SC_REF_EN | SC_CKE)@h
|
||||||
|
xoris r4, r4, (SC_CKE)@h /* ref_en !cke */
|
||||||
|
stw r4, SDRAM_CTRL(r8)
|
||||||
|
sync
|
||||||
|
|
||||||
|
/* delay (after !CKE there should be two cycles) */
|
||||||
|
li r12, 1
|
||||||
|
bl udelay
|
||||||
|
|
||||||
|
/* disable clock */
|
||||||
|
lwz r4, CDM_CE(r8)
|
||||||
|
ori r4, r4, CDM_SDRAM
|
||||||
|
xori r4, r4, CDM_SDRAM
|
||||||
|
stw r4, CDM_CE(r8)
|
||||||
|
sync
|
||||||
|
|
||||||
|
/* delay a bit */
|
||||||
|
li r12, 1
|
||||||
|
bl udelay
|
||||||
|
|
||||||
|
|
||||||
|
/* turn off with QT chip */
|
||||||
|
li r4, 0x02
|
||||||
|
stb r4, GPIOW_GPIOE(r8) /* enable gpio_wkup1 */
|
||||||
|
sync
|
||||||
|
|
||||||
|
stb r4, GPIOW_DVO(r8) /* "output" high */
|
||||||
|
sync
|
||||||
|
stb r4, GPIOW_DDR(r8) /* output */
|
||||||
|
sync
|
||||||
|
stb r4, GPIOW_DVO(r8) /* output high */
|
||||||
|
sync
|
||||||
|
|
||||||
|
/* 10uS delay */
|
||||||
|
li r12, 10
|
||||||
|
bl udelay
|
||||||
|
|
||||||
|
/* turn off */
|
||||||
|
li r4, 0
|
||||||
|
stb r4, GPIOW_DVO(r8) /* output low */
|
||||||
|
sync
|
||||||
|
|
||||||
|
/* wait until we're offline */
|
||||||
|
1:
|
||||||
|
b 1b
|
||||||
|
|
||||||
|
|
||||||
|
/* local udelay in sram is needed */
|
||||||
|
udelay: /* r11 - tb_ticks_per_usec, r12 - usecs, overwrites r13 */
|
||||||
|
mullw r12, r12, r11
|
||||||
|
mftb r13 /* start */
|
||||||
|
addi r12, r13, r12 /* end */
|
||||||
|
1:
|
||||||
|
mftb r13 /* current */
|
||||||
|
cmp cr0, r13, r12
|
||||||
|
blt 1b
|
||||||
|
blr
|
||||||
|
|
||||||
|
sram_code_end:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* uboot jumps here on resume */
|
||||||
|
lite5200_wakeup:
|
||||||
|
bl restore_regs
|
||||||
|
|
||||||
|
|
||||||
|
/* HIDs, MSR */
|
||||||
|
LOAD_SPRN(HID1, 0x19)
|
||||||
|
LOAD_SPRN(HID2, 0x1a)
|
||||||
|
|
||||||
|
|
||||||
|
/* address translation is tricky (see turn_on_mmu) */
|
||||||
|
mfmsr r10
|
||||||
|
ori r10, r10, MSR_DR | MSR_IR
|
||||||
|
|
||||||
|
|
||||||
|
mtspr SPRN_SRR1, r10
|
||||||
|
lis r10, mmu_on@h
|
||||||
|
ori r10, r10, mmu_on@l
|
||||||
|
mtspr SPRN_SRR0, r10
|
||||||
|
sync
|
||||||
|
rfi
|
||||||
|
mmu_on:
|
||||||
|
/* kernel offset (r4 is still set from restore_registers) */
|
||||||
|
addis r4, r4, CONFIG_KERNEL_START@h
|
||||||
|
|
||||||
|
|
||||||
|
/* restore MSR */
|
||||||
|
lwz r10, (4*0x1b)(r4)
|
||||||
|
mtmsr r10
|
||||||
|
sync; isync;
|
||||||
|
|
||||||
|
/* invalidate caches */
|
||||||
|
mfspr r10, SPRN_HID0
|
||||||
|
ori r5, r10, HID0_ICFI | HID0_DCI
|
||||||
|
mtspr SPRN_HID0, r5 /* invalidate caches */
|
||||||
|
sync; isync;
|
||||||
|
mtspr SPRN_HID0, r10
|
||||||
|
sync; isync;
|
||||||
|
|
||||||
|
/* enable caches */
|
||||||
|
lwz r10, (4*0x18)(r4)
|
||||||
|
mtspr SPRN_HID0, r10 /* restore (enable caches, DPM) */
|
||||||
|
/* ^ this has to be after address translation set in MSR */
|
||||||
|
sync
|
||||||
|
isync
|
||||||
|
|
||||||
|
|
||||||
|
/* restore 0xf0 (BDI2000) */
|
||||||
|
lis r3, CONFIG_KERNEL_START@h
|
||||||
|
lwz r10, (0x1d*4)(r4)
|
||||||
|
stw r10, 0xf0(r3)
|
||||||
|
|
||||||
|
LOAD_SPRN(LR, 0x1c)
|
||||||
|
|
||||||
|
|
||||||
|
blr
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
/* boring code: helpers */
|
||||||
|
|
||||||
|
/* save registers */
|
||||||
|
#define SAVE_BAT(n, addr) \
|
||||||
|
SAVE_SPRN(DBAT##n##L, addr); \
|
||||||
|
SAVE_SPRN(DBAT##n##U, addr+1); \
|
||||||
|
SAVE_SPRN(IBAT##n##L, addr+2); \
|
||||||
|
SAVE_SPRN(IBAT##n##U, addr+3);
|
||||||
|
|
||||||
|
#define SAVE_SR(n, addr) \
|
||||||
|
mfsr r10, n; \
|
||||||
|
stw r10, ((addr)*4)(r4);
|
||||||
|
|
||||||
|
#define SAVE_4SR(n, addr) \
|
||||||
|
SAVE_SR(n, addr); \
|
||||||
|
SAVE_SR(n+1, addr+1); \
|
||||||
|
SAVE_SR(n+2, addr+2); \
|
||||||
|
SAVE_SR(n+3, addr+3);
|
||||||
|
|
||||||
|
save_regs:
|
||||||
|
stw r0, 0(r4)
|
||||||
|
stw r1, 0x4(r4)
|
||||||
|
stw r2, 0x8(r4)
|
||||||
|
stmw r11, 0xc(r4) /* 0xc -> 0x5f, (0x18*4-1) */
|
||||||
|
|
||||||
|
SAVE_SPRN(HID0, 0x18)
|
||||||
|
SAVE_SPRN(HID1, 0x19)
|
||||||
|
SAVE_SPRN(HID2, 0x1a)
|
||||||
|
mfmsr r10
|
||||||
|
stw r10, (4*0x1b)(r4)
|
||||||
|
/*SAVE_SPRN(LR, 0x1c) have to save it before the call */
|
||||||
|
/* 0x1d reserved by 0xf0 */
|
||||||
|
SAVE_SPRN(RPA, 0x1e)
|
||||||
|
SAVE_SPRN(SDR1, 0x1f)
|
||||||
|
|
||||||
|
/* save MMU regs */
|
||||||
|
SAVE_BAT(0, 0x20)
|
||||||
|
SAVE_BAT(1, 0x24)
|
||||||
|
SAVE_BAT(2, 0x28)
|
||||||
|
SAVE_BAT(3, 0x2c)
|
||||||
|
SAVE_BAT(4, 0x30)
|
||||||
|
SAVE_BAT(5, 0x34)
|
||||||
|
SAVE_BAT(6, 0x38)
|
||||||
|
SAVE_BAT(7, 0x3c)
|
||||||
|
|
||||||
|
SAVE_4SR(0, 0x40)
|
||||||
|
SAVE_4SR(4, 0x44)
|
||||||
|
SAVE_4SR(8, 0x48)
|
||||||
|
SAVE_4SR(12, 0x4c)
|
||||||
|
|
||||||
|
SAVE_SPRN(SPRG0, 0x50)
|
||||||
|
SAVE_SPRN(SPRG1, 0x51)
|
||||||
|
SAVE_SPRN(SPRG2, 0x52)
|
||||||
|
SAVE_SPRN(SPRG3, 0x53)
|
||||||
|
SAVE_SPRN(SPRG4, 0x54)
|
||||||
|
SAVE_SPRN(SPRG5, 0x55)
|
||||||
|
SAVE_SPRN(SPRG6, 0x56)
|
||||||
|
SAVE_SPRN(SPRG7, 0x57)
|
||||||
|
|
||||||
|
SAVE_SPRN(IABR, 0x58)
|
||||||
|
SAVE_SPRN(DABR, 0x59)
|
||||||
|
SAVE_SPRN(TBRL, 0x5a)
|
||||||
|
SAVE_SPRN(TBRU, 0x5b)
|
||||||
|
|
||||||
|
blr
|
||||||
|
|
||||||
|
|
||||||
|
/* restore registers */
|
||||||
|
#define LOAD_BAT(n, addr) \
|
||||||
|
LOAD_SPRN(DBAT##n##L, addr); \
|
||||||
|
LOAD_SPRN(DBAT##n##U, addr+1); \
|
||||||
|
LOAD_SPRN(IBAT##n##L, addr+2); \
|
||||||
|
LOAD_SPRN(IBAT##n##U, addr+3);
|
||||||
|
|
||||||
|
#define LOAD_SR(n, addr) \
|
||||||
|
lwz r10, ((addr)*4)(r4); \
|
||||||
|
mtsr n, r10;
|
||||||
|
|
||||||
|
#define LOAD_4SR(n, addr) \
|
||||||
|
LOAD_SR(n, addr); \
|
||||||
|
LOAD_SR(n+1, addr+1); \
|
||||||
|
LOAD_SR(n+2, addr+2); \
|
||||||
|
LOAD_SR(n+3, addr+3);
|
||||||
|
|
||||||
|
restore_regs:
|
||||||
|
lis r4, registers@h
|
||||||
|
ori r4, r4, registers@l
|
||||||
|
|
||||||
|
/* MMU is not up yet */
|
||||||
|
subis r4, r4, CONFIG_KERNEL_START@h
|
||||||
|
|
||||||
|
lwz r0, 0(r4)
|
||||||
|
lwz r1, 0x4(r4)
|
||||||
|
lwz r2, 0x8(r4)
|
||||||
|
lmw r11, 0xc(r4)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* these are a bit tricky
|
||||||
|
*
|
||||||
|
* 0x18 - HID0
|
||||||
|
* 0x19 - HID1
|
||||||
|
* 0x1a - HID2
|
||||||
|
* 0x1b - MSR
|
||||||
|
* 0x1c - LR
|
||||||
|
* 0x1d - reserved by 0xf0 (BDI2000)
|
||||||
|
*/
|
||||||
|
LOAD_SPRN(RPA, 0x1e);
|
||||||
|
LOAD_SPRN(SDR1, 0x1f);
|
||||||
|
|
||||||
|
/* restore MMU regs */
|
||||||
|
LOAD_BAT(0, 0x20)
|
||||||
|
LOAD_BAT(1, 0x24)
|
||||||
|
LOAD_BAT(2, 0x28)
|
||||||
|
LOAD_BAT(3, 0x2c)
|
||||||
|
LOAD_BAT(4, 0x30)
|
||||||
|
LOAD_BAT(5, 0x34)
|
||||||
|
LOAD_BAT(6, 0x38)
|
||||||
|
LOAD_BAT(7, 0x3c)
|
||||||
|
|
||||||
|
LOAD_4SR(0, 0x40)
|
||||||
|
LOAD_4SR(4, 0x44)
|
||||||
|
LOAD_4SR(8, 0x48)
|
||||||
|
LOAD_4SR(12, 0x4c)
|
||||||
|
|
||||||
|
/* rest of regs */
|
||||||
|
LOAD_SPRN(SPRG0, 0x50);
|
||||||
|
LOAD_SPRN(SPRG1, 0x51);
|
||||||
|
LOAD_SPRN(SPRG2, 0x52);
|
||||||
|
LOAD_SPRN(SPRG3, 0x53);
|
||||||
|
LOAD_SPRN(SPRG4, 0x54);
|
||||||
|
LOAD_SPRN(SPRG5, 0x55);
|
||||||
|
LOAD_SPRN(SPRG6, 0x56);
|
||||||
|
LOAD_SPRN(SPRG7, 0x57);
|
||||||
|
|
||||||
|
LOAD_SPRN(IABR, 0x58);
|
||||||
|
LOAD_SPRN(DABR, 0x59);
|
||||||
|
LOAD_SPRN(TBWL, 0x5a); /* these two have separate R/W regs */
|
||||||
|
LOAD_SPRN(TBWU, 0x5b);
|
||||||
|
|
||||||
|
blr
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* cache flushing code. copied from arch/ppc/boot/util.S */
|
||||||
|
#define NUM_CACHE_LINES (128*8)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flush data cache
|
||||||
|
* Do this by just reading lots of stuff into the cache.
|
||||||
|
*/
|
||||||
|
flush_data_cache:
|
||||||
|
lis r3,CONFIG_KERNEL_START@h
|
||||||
|
ori r3,r3,CONFIG_KERNEL_START@l
|
||||||
|
li r4,NUM_CACHE_LINES
|
||||||
|
mtctr r4
|
||||||
|
1:
|
||||||
|
lwz r4,0(r3)
|
||||||
|
addi r3,r3,L1_CACHE_BYTES /* Next line, please */
|
||||||
|
bdnz 1b
|
||||||
|
blr
|
|
@ -262,6 +262,16 @@ struct mpc52xx_suspend {
|
||||||
extern struct mpc52xx_suspend mpc52xx_suspend;
|
extern struct mpc52xx_suspend mpc52xx_suspend;
|
||||||
extern int __init mpc52xx_pm_init(void);
|
extern int __init mpc52xx_pm_init(void);
|
||||||
extern int mpc52xx_set_wakeup_gpio(u8 pin, u8 level);
|
extern int mpc52xx_set_wakeup_gpio(u8 pin, u8 level);
|
||||||
|
|
||||||
|
#ifdef CONFIG_PPC_LITE5200
|
||||||
|
extern int __init lite5200_pm_init(void);
|
||||||
|
|
||||||
|
/* lite5200 calls mpc5200 suspend functions, so here they are */
|
||||||
|
extern int mpc52xx_pm_prepare(suspend_state_t);
|
||||||
|
extern int mpc52xx_pm_enter(suspend_state_t);
|
||||||
|
extern int mpc52xx_pm_finish(suspend_state_t);
|
||||||
|
extern char saved_sram[0x4000]; /* reuse buffer from mpc52xx suspend */
|
||||||
|
#endif
|
||||||
#endif /* CONFIG_PM */
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
#endif /* __ASM_POWERPC_MPC52xx_H__ */
|
#endif /* __ASM_POWERPC_MPC52xx_H__ */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче