Low-power mode implementation for Lite5200b.
Some I/O registers are also saved here.

A patch to U-Boot that wakes up SDRAM, and transfers control
to address saved at physical 0x0 is needed.


Signed-off-by: Domen Puncer <[EMAIL PROTECTED]>

---
 arch/powerpc/platforms/52xx/Makefile         |    3 
 arch/powerpc/platforms/52xx/lite5200_pm.c    |  125 ++++++++
 arch/powerpc/platforms/52xx/lite5200_sleep.S |  419 +++++++++++++++++++++++++++
 3 files changed, 547 insertions(+)

Index: grant.git/arch/powerpc/platforms/52xx/lite5200_pm.c
===================================================================
--- /dev/null
+++ grant.git/arch/powerpc/platforms/52xx/lite5200_pm.c
@@ -0,0 +1,125 @@
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <asm/io.h>
+#include <asm/mpc52xx.h>
+#include "mpc52xx_pic.h"
+#include "bestcomm.h"
+
+extern void lite5200_low_power(void *sram, void *mbar);
+extern int mpc52xx_pm_enter(suspend_state_t);
+extern int mpc52xx_pm_prepare(suspend_state_t);
+
+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 = ioremap_nocache(0xf0000000, 0x8000);
+       if (!mbar) {
+               printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, 
__LINE__);
+               return -ENOSYS;
+       }
+
+       return 0;
+}
+
+/* save and restore registers not bound to any real devices */
+static struct mpc52xx_cdm __iomem *cdm;
+static struct mpc52xx_cdm scdm;
+static struct mpc52xx_intr __iomem *pic;
+static struct mpc52xx_intr spic;
+static struct mpc52xx_sdma __iomem *bes;
+static struct mpc52xx_sdma sbes;
+static struct mpc52xx_xlb __iomem *xlb;
+static struct mpc52xx_xlb sxlb;
+static struct mpc52xx_gpio __iomem *gps;
+static struct mpc52xx_gpio sgps;
+static struct mpc52xx_gpio_wkup __iomem *gpw;
+static struct mpc52xx_gpio_wkup sgpw;
+extern char saved_sram[0x4000];
+
+static void lite5200_save_regs(void)
+{
+       _memcpy_fromio(&sbes, bes, sizeof(*bes));
+       _memcpy_fromio(&spic, pic, sizeof(*pic));
+       _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(saved_sram, sdma.sram, sdma.sram_size);
+}
+
+static void lite5200_restore_regs(void)
+{
+       memcpy(sdma.sram, saved_sram, sdma.sram_size);
+
+       _memcpy_toio(gpw, &sgpw, sizeof(*gpw));
+       _memcpy_toio(gps, &sgps, sizeof(*gps));
+       _memcpy_toio(xlb, &sxlb, sizeof(*xlb));
+       _memcpy_toio(cdm, &scdm, sizeof(*cdm));
+       _memcpy_toio(pic, &spic, sizeof(*pic));
+       _memcpy_toio(bes, &sbes, sizeof(*bes));
+}
+
+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);
+       }
+
+       cdm = mbar + 0x200;
+       pic = mbar + 0x500;
+       gps = mbar + 0xb00;
+       gpw = mbar + 0xc00;
+       bes = mbar + 0x1200;
+       xlb = mbar + 0x1f00;
+       lite5200_save_regs();
+
+       lite5200_low_power(sdma.sram, mbar);
+
+       lite5200_restore_regs();
+
+       iounmap(mbar);
+       return 0;
+}
+
+static int lite5200_pm_finish(suspend_state_t 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,
+};
+
+static int __init lite5200_pm_init(void)
+{
+       pm_set_ops(&lite5200_pm_ops);
+       return 0;
+}
+
+arch_initcall(lite5200_pm_init);
Index: grant.git/arch/powerpc/platforms/52xx/lite5200_sleep.S
===================================================================
--- /dev/null
+++ grant.git/arch/powerpc/platforms/52xx/lite5200_sleep.S
@@ -0,0 +1,419 @@
+#include <asm/reg.h>
+#include <asm/ppc_asm.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+
+
+#define SDRAM_MODE     0x100
+#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_ODE      0xc04
+#define GPIOW_DDR      0xc08
+#define GPIOW_DVO      0xc0c
+#define GPIOW_INTEN    0xc10
+
+#define CDM_CE         0x214
+#define CDM_SDRAM      (1<<3)
+
+
+// about 2000 cpu cycles for one sdram cycle here
+// just increase, to be on the safe side?
+#define TCK    5000
+
+
+#define DONT_DEBUG 1
+
+// 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;
+
+// XXX it uses cca. 10 mA less if registers are saved in .text. WTF
+// is this still true?
+//     .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, [EMAIL PROTECTED]
+       lis     r4, [EMAIL PROTECTED]
+       ori     r4, r4, [EMAIL PROTECTED]
+       sub     r4, r4, r3
+       stw     r4, 0(r3)
+
+
+       // save stuff BDI overwrites
+       /* save 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, [EMAIL PROTECTED]
+       ori     r4, r4, [EMAIL PROTECTED]
+       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, [EMAIL PROTECTED]
+       ori     r3, r3, [EMAIL PROTECTED]
+1:
+       lwz     r5, 0(r3)
+       stw     r5, 0(r4)
+       addi    r3, r3, 4
+       addi    r4, r4, 4
+       bdnz    1b
+
+       // 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;
+
+#if DONT_DEBUG
+       // jump to sram
+       mtlr    r7
+       blrl
+       // doesn't return
+#else
+       // debugging
+       b       lite5200_wakeup
+#endif
+
+
+sram_code:
+       // self refresh
+       lwz     r4, SDRAM_CTRL(r8)
+
+       // send NOP (precharge)
+       oris    r4, r4, [EMAIL PROTECTED]       // 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, [EMAIL PROTECTED]       // !mode_en
+       stw     r4, SDRAM_CTRL(r8)
+       sync
+
+       // delay for one sdram cycle (for NOP to finish)
+       li      r5, TCK
+       mtctr   r5
+1:     bdnz-   1b
+
+       // 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 for two sdram cycles (after !CKE there should be two cycles)
+       li      r5, 2*TCK
+       mtctr   r5
+1:     bdnz-   1b
+
+       // disable clock
+       lwz     r4, CDM_CE(r8)
+       ori     r4, r4, CDM_SDRAM
+       xori    r4, r4, CDM_SDRAM
+       stw     r4, CDM_CE(r8)
+       sync
+
+       // delay for two sdram cycles
+       li      r5, 2*TCK
+       mtctr   r5
+1:     bdnz-   1b
+
+
+       // 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
+
+       // delay
+       // 2000 cycles is cca 12 uS, 10uS should be enough
+       li      r4, 2000
+       mtctr   r4
+1:
+       bdnz-   1b
+
+       // turn off
+       li      r4, 0
+       stb     r4, GPIOW_DVO(r8)       // output low
+       sync
+
+       // wait until we're offline
+1:
+       b       1b
+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, [EMAIL PROTECTED]
+       ori     r10, r10, [EMAIL PROTECTED]
+       mtspr   SPRN_SRR0, r10
+       sync
+       rfi
+mmu_on:
+       // kernel offset (r4 is still set from restore_registers)
+       addis   r4, r4, [EMAIL PROTECTED]
+
+
+       // restore MSR
+       lwz     r10, (4*0x1b)(r4)
+       mtmsr   r10
+       sync; isync;
+
+       // setup DEC somewhere in the 1/HZ range
+       // if you don't do this, timer interrupt will trigger a few
+       //      seconds later, and that is not good.
+       lis     r3, 0x10
+       mtdec   r3
+
+       // 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, [EMAIL PROTECTED]
+       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, [EMAIL PROTECTED]
+       ori     r4, r4, [EMAIL PROTECTED]
+#ifdef DONT_DEBUG
+       subis   r4, r4, [EMAIL PROTECTED]
+#endif
+
+       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.
+ */
+        .globl flush_data_cache
+flush_data_cache:
+       lis     r3,[EMAIL PROTECTED]
+       ori     r3,r3,[EMAIL PROTECTED]
+       li      r4,NUM_CACHE_LINES
+       mtctr   r4
+1:
+       lwz     r4,0(r3)
+       addi    r3,r3,L1_CACHE_BYTES    /* Next line, please */
+       bdnz    1b
+       blr
Index: grant.git/arch/powerpc/platforms/52xx/Makefile
===================================================================
--- grant.git.orig/arch/powerpc/platforms/52xx/Makefile
+++ grant.git/arch/powerpc/platforms/52xx/Makefile
@@ -12,3 +12,6 @@ obj-$(CONFIG_PPC_EFIKA)               += efika.o
 obj-$(CONFIG_PPC_LITE5200)     += lite5200.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
_______________________________________________
Linuxppc-embedded mailing list
Linuxppc-embedded@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-embedded

Reply via email to