aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/52xx/lite5200_sleep.S
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/52xx/lite5200_sleep.S')
-rw-r--r--arch/powerpc/platforms/52xx/lite5200_sleep.S412
1 files changed, 412 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/52xx/lite5200_sleep.S b/arch/powerpc/platforms/52xx/lite5200_sleep.S
new file mode 100644
index 000000000000..08ab6fefcf7a
--- /dev/null
+++ b/arch/powerpc/platforms/52xx/lite5200_sleep.S
@@ -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