From: Domen Puncer <domen.puncer@telargo.com>
To: linuxppc-embedded@ozlabs.org
Subject: [PATCH 5/7] mpc52xx suspend: deep-sleep
Date: Thu, 1 Mar 2007 08:56:44 +0100 [thread overview]
Message-ID: <20070301075644.GE17184@moe.telargo.com> (raw)
In-Reply-To: <20070301075323.GP4397@moe.telargo.com>
Implement deep-sleep on MPC52xx.
SDRAM is put into self-refresh with help of SRAM code
(alternatives would be code in FLASH, I-cache).
Interrupt code must also not be in SDRAM, so put it
in I-cache.
MPC52xx core is static, so contents will remain intact even
with clocks turned off.
There seems to be a race with decrementer interrupt (uncomment
#define TESTING, and execute `echo standby > /sys/power/state`
couple thousands of times to reproduce it). :-(
Signed-off-by: Domen Puncer <domen.puncer@telargo.com>
Index: grant.git/arch/powerpc/platforms/52xx/Makefile
===================================================================
--- grant.git.orig/arch/powerpc/platforms/52xx/Makefile
+++ grant.git/arch/powerpc/platforms/52xx/Makefile
@@ -10,3 +10,5 @@ endif
obj-$(CONFIG_PPC_EFIKA) += efika.o
obj-$(CONFIG_PPC_LITE5200) += lite5200.o
+
+obj-$(CONFIG_PM) += mpc52xx_sleep.o mpc52xx_pm.o
Index: grant.git/arch/powerpc/platforms/52xx/mpc52xx_pm.c
===================================================================
--- /dev/null
+++ grant.git/arch/powerpc/platforms/52xx/mpc52xx_pm.c
@@ -0,0 +1,123 @@
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <asm/mpc52xx.h>
+#include "bestcomm.h"
+#include "mpc52xx_pic.h"
+
+extern void mpc52xx_deep_sleep(void *, void *);
+
+static int mpc52xx_pm_valid(suspend_state_t state)
+{
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int mpc52xx_pm_prepare(suspend_state_t state)
+{
+ return 0;
+}
+
+/* you will want to change this, to match your board gpios, rtc or whatever */
+static void mpc52xx_set_wakeup_mode(void)
+{
+ struct mpc52xx_gpio_wkup __iomem *gpiow;
+ struct mpc52xx_intr __iomem *intr;
+ int pin = 1; /* GPIO_WKUP_1 (GPIO_PSC2_4) */
+ u16 tmp;
+
+ gpiow = mpc52xx_find_and_map("mpc5200-gpio-wkup");
+ intr = mpc52xx_find_and_map("mpc5200-pic");
+ if (!gpiow || !intr) {
+ printk(KERN_ERR "%s: couldn't map io space\n", __func__);
+ goto out;
+ }
+
+ /* enable gpio */
+ out_8(&gpiow->wkup_gpioe, in_8(&gpiow->wkup_gpioe) | (1 << pin));
+ /* set as input */
+ out_8(&gpiow->wkup_ddr, in_8(&gpiow->wkup_ddr) & ~(1 << pin));
+ /* enable deep sleep interrupt */
+ out_8(&gpiow->wkup_inten, in_8(&gpiow->wkup_inten) | (1 << pin));
+ /* low level creates wakeup interrupt */
+ tmp = in_be16(&gpiow->wkup_itype);
+ tmp &= 2 << (pin * 2);
+ tmp |= 2 << (pin * 2);
+ out_be16(&gpiow->wkup_itype, tmp);
+ /* master enable */
+ out_8(&gpiow->wkup_maste, 1);
+
+ /* enable wakeup gpio interrupt in PIC */
+ out_be32(&intr->main_mask, in_be32(&intr->main_mask) & ~(1 << 8));
+ out:
+ iounmap(gpiow);
+ iounmap(intr);
+}
+
+int mpc52xx_pm_enter(suspend_state_t state)
+{
+ int err = 0;
+ void __iomem *mbar;
+ struct mpc52xx_cdm __iomem *cdm;
+ u32 clk_enables;
+
+ if (state != PM_SUSPEND_STANDBY)
+ return 0;
+
+ mpc52xx_set_wakeup_mode();
+
+ /* is there a nicer way? */
+ mbar = ioremap_nocache(0xf0000000, 0x8000);
+ cdm = mpc52xx_find_and_map("mpc5200-cdm");
+ if (!mbar || !cdm) {
+ printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__);
+ err = -ENOSYS;
+ goto out;
+ }
+
+ mpc52xx_sdma_suspend();
+
+ out_8(&cdm->ccs_sleep_enable, 1);
+ out_8(&cdm->osc_sleep_enable, 1);
+ out_8(&cdm->ccs_qreq_test, 1);
+
+ /* disable all but SDRAM, bestcomm (SRAM) and timer clocks */
+ clk_enables = in_be32(&cdm->clk_enables);
+ out_be32(&cdm->clk_enables, clk_enables & 0x00088002);
+
+ mpc52xx_deep_sleep(sdma.sram, mbar);
+
+ out_be32(&cdm->clk_enables, clk_enables);
+ out_8(&cdm->ccs_sleep_enable, 0);
+ out_8(&cdm->osc_sleep_enable, 0);
+
+ mpc52xx_sdma_resume();
+
+ iounmap(mbar);
+ out:
+ return err;
+}
+
+static int mpc52xx_pm_finish(suspend_state_t state)
+{
+ return 0;
+}
+
+static struct pm_ops mpc52xx_pm_ops = {
+ .valid = mpc52xx_pm_valid,
+ .prepare = mpc52xx_pm_prepare,
+ .enter = mpc52xx_pm_enter,
+ .finish = mpc52xx_pm_finish,
+};
+
+static int __init mpc52xx_pm_init(void)
+{
+ pm_set_ops(&mpc52xx_pm_ops);
+ return 0;
+}
+
+arch_initcall(mpc52xx_pm_init);
Index: grant.git/arch/powerpc/platforms/52xx/mpc52xx_sleep.S
===================================================================
--- /dev/null
+++ grant.git/arch/powerpc/platforms/52xx/mpc52xx_sleep.S
@@ -0,0 +1,277 @@
+#include <asm/reg.h>
+#include <asm/ppc_asm.h>
+#include <asm/processor.h>
+
+
+// Tck is cca. 2000 cpu cycles here
+#define TCK 2000
+
+#define TMR0_ENABLE 0x600
+#define TMR0_INPUT 0x604
+
+#define SDRAM_CTRL 0x104
+
+#define CDM_CE 0x214
+#define CDM_CCSCR 0x21c
+
+#define INTR_MAIN_MASK 0x514
+#define INTR_ENC_STAT 0x524
+
+
+//#define TESTING
+
+// mpc5200b puts sdram automatically in self-refresh, previous versions don't
+#define SELF_REFRESH
+
+ .globl mpc52xx_deep_sleep
+mpc52xx_deep_sleep:
+
+ mr r7, r3 // SRAM va
+ mr r8, r4 // MBAR va
+ mflr r9
+
+ // we don't want DEC expiring anytime soon, but not very late either
+ lis r4, 0x1
+ mtspr SPRN_DEC, r4
+
+
+ // setup power mode bits
+ mfmsr r11
+ mr r10, r11
+ oris r10, r10, 0x0004
+ xoris r10, r10, 0x0004 // POW = 0
+ sync; isync;
+ mtmsr r10
+ sync; isync;
+
+ mfspr r12, SPRN_HID0
+ mr r10, r12
+ oris r10, r10, 0x00f0
+ xoris r10, r10, 0x00d0 // disable all power modes but sleep
+ sync; isync;
+ mtspr SPRN_HID0, r10
+ sync; isync;
+
+ // copy code to sram
+ mr r4, r7
+ subi r4, r4, 4
+ li r3, (sram_code_end-sram_code)/4
+ mtctr r3
+ lis r3, (sram_code-4)@h
+ ori r3, r3, (sram_code-4)@l
+1:
+ lwzu r5, 4(r3)
+ stwu r5, 4(r4)
+ bdnz 1b
+
+
+ // save original irq handler, and write a new one
+ lis r3, (orig_0x500-4)@h
+ ori r3, r3, (orig_0x500-4)@l
+ li r4, (cached_code_end - cached_code)/4
+ mtctr r4
+ lis r4, CONFIG_KERNEL_START@h
+ ori r4, r4, 0x500
+ lis r10, (cached_code-4)@h
+ ori r10, r10, (cached_code-4)@l
+1:
+ lwz r5, 0(r4)
+ stwu r5, 4(r3)
+ lwzu r5, 4(r10)
+ stw r5, 0(r4)
+
+ dcbf 0, r4
+ icbi 0, r4
+ addi r4, r4, 4
+
+ bdnz- 1b
+
+
+ // enable tmr0 interrupt
+ lwz r4, INTR_MAIN_MASK(r8)
+ ori r4, r4, 0x0080
+ xori r4, r4, 0x0080
+ stw r4, INTR_MAIN_MASK(r8)
+ sync
+
+ li r5, 0 // flag that irq handler sets
+
+ // enable interrupts
+ mfmsr r3
+ ori r3, r3, 0x8000 // EE
+ mtmsr r3
+ sync; isync;
+
+ // trigger tmr interrupt to cache the code
+ lis r4, 0x100
+ ori r4, r4, 0x1
+ stw r4, TMR0_INPUT(r8)
+ sync
+ li r4, 0x1104
+ stw r4, TMR0_ENABLE(r8)
+ sync
+
+1:
+ cmpi cr0, r5, 1
+ bne cr0, 1b
+
+ // lock icache
+ mfspr r10, SPRN_HID0
+ ori r10, r10, 0x2000
+ sync; isync;
+ mtspr SPRN_HID0, r10
+ sync; isync;
+
+ // jump to sram
+ mtlr r7
+ blrl
+
+
+ // unlock icache
+ mfspr r10, SPRN_HID0
+ ori r10, r10, 0x2000
+ xori r10, r10, 0x2000
+ sync; isync;
+ mtspr SPRN_HID0, r10
+ sync; isync;
+
+
+ // restore former power mode (and re-disable interrupts)
+ mfmsr r10
+ oris r10, r10, 0x0004
+ xoris r10, r10, 0x0004 // POW = 0
+ sync; isync;
+ mtmsr r10
+ sync; isync;
+
+ mtspr SPRN_HID0, r12
+ sync; isync;
+
+ mtmsr r11
+ sync; isync;
+
+ // restore original irq handler
+ lis r3, (orig_0x500-4)@h
+ ori r3, r3, (orig_0x500-4)@l
+ li r4, (cached_code_end - cached_code)/4
+ mtctr r4
+ lis r4, CONFIG_KERNEL_START@h
+ ori r4, r4, 0x500
+1:
+ lwzu r5, 4(r3)
+ stw r5, 0(r4)
+
+ dcbf 0, r4
+ icbi 0, r4
+ addi r4, r4, 4
+
+ bdnz- 1b
+
+
+ mtlr r9
+ blr
+
+
+sram_code:
+ // self refresh
+#ifdef SELF_REFRESH
+ lwz r4, SDRAM_CTRL(r8)
+
+ oris r4, r4, 0x8000 //mode_en
+ stw r4, SDRAM_CTRL(r8)
+ sync
+
+ ori r4, r4, 0x0002 // soft_pre
+ stw r4, SDRAM_CTRL(r8)
+ sync
+ xori r4, r4, 0x0002
+
+ xoris r4, r4, 0x8000 //mode_en
+ stw r4, SDRAM_CTRL(r8)
+ sync
+
+ // delay one sdram cycle
+ li r5, TCK
+ mtctr r5
+1:
+ bdnz- 1b
+
+ oris r4, r4, 0x5000
+ xoris r4, r4, 0x4000 // ref_en !cke
+ stw r4, SDRAM_CTRL(r8)
+ sync
+
+ // delay for 2 sdram cycles
+ li r4, 2*TCK
+ mtctr r4
+1:
+ bdnz- 1b
+
+ // disable clock
+ lwz r4, CDM_CE(r8)
+ ori r4, r4, 0x0008
+ xori r4, r4, 0x0008
+ stw r4, CDM_CE(r8)
+ sync
+#endif
+
+#ifndef TESTING
+ // put it to sleep
+ mfmsr r10
+ oris r10, r10, 0x0004 // POW = 1
+ sync; isync;
+ mtmsr r10
+ sync; isync;
+#endif
+
+#ifdef SELF_REFRESH
+ // enable clock
+ lwz r4, CDM_CE(r8)
+ ori r4, r4, 0x0008
+ stw r4, CDM_CE(r8)
+ sync
+
+ // get ram out of self-refresh
+ lwz r4, SDRAM_CTRL(r8)
+ oris r4, r4, 0x5000 // cke ref_en
+ stw r4, SDRAM_CTRL(r8)
+ sync
+
+ li r4, 2*TCK
+ mtctr r4
+1:
+ bdnz- 1b
+#endif
+
+ blr
+sram_code_end:
+
+
+// ### interrupt handler for wakeup from deep-sleep ###
+cached_code:
+ // disable timer
+ mfspr r3, 311 // MBAR
+ addi r3, r3, TMR0_ENABLE
+ li r4, 0
+ stw r4, 0(r3)
+ sync
+ dcbf 0, r3
+
+ // acknowledge wakeup, so CCS releases power pown
+ mfspr r3, 311 // MBAR
+ addi r3, r3, INTR_ENC_STAT
+ lwz r4, 0(r3)
+ ori r4, r4, 0x0400
+ stw r4, 0(r3)
+ sync
+ dcbf 0, r3
+
+ // flag that we handled an interrupt
+ li r5, 1
+
+ rfi
+cached_code_end:
+
+
+orig_0x500:
+ .space (cached_code_end - cached_code)
next prev parent reply other threads:[~2007-03-01 7:56 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-03-01 7:53 [PATCH 0/7] MPC5200 and Lite5200b low power modes Domen Puncer
2007-03-01 7:54 ` [PATCH 1/7] mpc52xx suspend: bestcomm Domen Puncer
2007-03-01 7:55 ` [PATCH 2/7] mpc52xx suspend: UART Domen Puncer
2007-03-01 7:55 ` [PATCH 3/7] mpc52xx suspend: FEC (ethernet) Domen Puncer
2007-03-01 7:56 ` [PATCH 4/7] mpc52xx suspend: USB Domen Puncer
2007-03-01 7:56 ` Domen Puncer [this message]
2007-03-01 7:57 ` [PATCH 6/7] lite5200b suspend: PIC Domen Puncer
2007-03-01 7:59 ` [u-boot patch] support lite5200b wakeup in u-boot Domen Puncer
2007-03-01 8:49 ` Stefan Roese
2007-03-01 8:00 ` [PATCH 7/7] lite5200b suspend: low-power mode Domen Puncer
2007-03-02 18:57 ` Scott Wood
2007-03-03 7:15 ` Domen Puncer
2007-03-01 14:25 ` [PATCH 0/7] MPC5200 and Lite5200b low power modes Grant Likely
2007-03-01 14:51 ` New Bestcomm/FEC patches (was: Re: [PATCH 0/7] MPC5200 and Lite5200b low power modes) Bartlomiej Sieka
2007-03-02 7:31 ` Domen Puncer
2007-03-02 21:35 ` [PATCH 0/7] MPC5200 and Lite5200b low power modes Sylvain Munaut
2007-03-03 7:33 ` Domen Puncer
2007-03-03 19:58 ` Endianness versus too many byte swaps?? Charles Krinke
2007-03-05 10:53 ` [PATCH 0/7] MPC5200 and Lite5200b low power modes Domen Puncer
2007-03-05 10:58 ` Sylvain Munaut
2007-03-05 20:21 ` Domen Puncer
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=20070301075644.GE17184@moe.telargo.com \
--to=domen.puncer@telargo.com \
--cc=linuxppc-embedded@ozlabs.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.