From: Domen Puncer <domen.puncer@telargo•com>
To: linuxppc-embedded@ozlabs•org
Subject: [PATCH 5/5] lite5200b suspend: low-power mode
Date: Thu, 15 Mar 2007 11:44:47 +0100 [thread overview]
Message-ID: <20070315104447.GG22215@moe.telargo.com> (raw)
In-Reply-To: <20070315103959.GA22215@moe.telargo.com>
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 <domen.puncer@telargo•com>
---
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, 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
+ /* 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, 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
+
+ // 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, 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 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, 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;
+
+ // 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, 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
+#ifdef DONT_DEBUG
+ subis r4, r4, CONFIG_KERNEL_START@h
+#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,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
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
next prev parent reply other threads:[~2007-03-15 10:44 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-03-15 10:39 [PATCH 0/5 v2] MPC5200 and Lite5200b low power modes Domen Puncer
2007-03-15 10:41 ` [PATCH 1/5] mpc52xx suspend: UART Domen Puncer
2007-03-15 10:41 ` [PATCH 2/5] mpc52xx suspend: FEC (ethernet) Domen Puncer
2007-03-15 13:35 ` Grant Likely
2007-03-15 10:42 ` [PATCH 3/5] mpc52xx suspend: USB Domen Puncer
2007-03-15 13:24 ` Grant Likely
2007-03-15 14:37 ` Wrong board info for ML403 Leonid
2007-03-16 8:15 ` Andrei Konovalov
2007-03-22 7:44 ` [PATCH 3/5 v2] mpc52xx suspend: USB Domen Puncer
2007-03-23 11:56 ` Sylvain Munaut
2007-03-23 16:00 ` Grant Likely
2007-03-15 10:43 ` [PATCH 4/5] mpc52xx suspend: deep-sleep Domen Puncer
2007-03-23 15:58 ` Grant Likely
2007-04-04 7:37 ` Domen Puncer
2007-04-16 5:40 ` Grant Likely
2007-04-17 7:05 ` Domen Puncer
2007-04-17 7:10 ` Grant Likely
2007-03-15 10:44 ` [PATCH] icecube/lite5200b: wakeup from low-power support Domen Puncer
2007-03-26 16:08 ` Grant Likely
2007-04-03 8:46 ` Domen Puncer
2007-04-16 4:45 ` Grant Likely
2007-04-16 6:25 ` Domen Puncer
2007-03-31 17:20 ` Rafal Jaworowski
2007-03-31 18:38 ` Domen Puncer
2007-03-15 10:44 ` Domen Puncer [this message]
2007-03-15 14:09 ` [PATCH 5/5] lite5200b suspend: low-power mode Grant Likely
2007-03-15 16:36 ` Domen Puncer
2007-03-22 7:41 ` Domen Puncer
2007-03-26 13:23 ` Domen Puncer
2007-03-26 15:54 ` Grant Likely
2007-04-17 7:11 ` Domen Puncer
2007-04-17 7:25 ` Grant Likely
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20070315104447.GG22215@moe.telargo.com \
--to=domen.puncer@telargo$(echo .)com \
--cc=linuxppc-embedded@ozlabs$(echo .)org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox