Hi!

How about something like the following.
Changes:
- lots of code moved from asm to C
- add compatible "mpc5200" to lite5200x soc (already is this
  way on efika). And change Efika's soc device_type to "soc".
- add wakeup supported with RTC (1 to 24*60-1 minutes)
- each board now configures it's wakeup mode and possibly
  board suspend and resume functions it needs to call (USB on lite)


This code survived > 70k suspend/resume cycles :-)


Comments?



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.


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

---
 arch/powerpc/boot/dts/lite5200.dts          |    1 
 arch/powerpc/boot/dts/lite5200b.dts         |    1 
 arch/powerpc/kernel/prom_init.c             |    2 
 arch/powerpc/platforms/52xx/Makefile        |    2 
 arch/powerpc/platforms/52xx/efika.c         |    8 
 arch/powerpc/platforms/52xx/lite5200.c      |   28 +++
 arch/powerpc/platforms/52xx/mpc52xx_pm.c    |  230 ++++++++++++++++++++++++++++
 arch/powerpc/platforms/52xx/mpc52xx_sleep.S |  161 +++++++++++++++++++
 include/asm-powerpc/mpc52xx.h               |   63 +++++++
 9 files changed, 495 insertions(+), 1 deletion(-)

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,230 @@
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <asm/time.h>
+#include <asm/cacheflush.h>
+#include <asm/mpc52xx.h>
+#include "bestcomm.h"
+
+#undef DEBUG /* define for 1s wakeups */
+extern void mpc52xx_deep_sleep(void *sram, void *, struct mpc52xx_cdm *, 
struct mpc52xx_intr *);
+
+static void __iomem *mbar;
+static void __iomem *sdram;
+static struct mpc52xx_cdm __iomem *cdm;
+static struct mpc52xx_intr __iomem *intr;
+static struct mpc52xx_rtc __iomem *rtc;
+static struct mpc52xx_gpio_wkup __iomem *gpiow;
+
+struct mpc52xx_wakeup mpc52xx_wakeup;
+
+static int mpc52xx_pm_valid(suspend_state_t state)
+{
+       switch (state) {
+       case PM_SUSPEND_STANDBY:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static int mpc52xx_set_wakeup_gpio(u8 pin, u8 level)
+{
+       u16 tmp;
+
+       /* 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/high level creates wakeup interrupt */
+       tmp = in_be16(&gpiow->wkup_itype);
+       tmp &= ~(0x3 << (pin * 2));
+       tmp |= (!level + 1) << (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));
+
+       return 0;
+}
+
+static int mpc52xx_set_wakeup_rtc(int delay)
+{
+#ifndef DEBUG
+       u8 hour;
+       u8 minute;
+
+       if (delay < 0)
+               return -EINVAL;
+       else if (delay == 0)
+               return 0;
+
+       hour = in_8(&rtc->hour);
+       minute = in_8(&rtc->minute);
+
+       hour += delay / 60;
+       hour %= 24;
+       minute += delay % 60 + 1;
+       if (minute >= 60) {
+               minute -= 60;
+               hour++;
+       }
+
+       out_8(&rtc->alm_hour_set, hour);
+       out_8(&rtc->alm_min_set, minute);
+       out_8(&rtc->alm_enable, 1);
+#else
+       u8 tmp = in_8(&rtc->int_enable);
+       out_8(&rtc->int_enable, (tmp & ~0x8) | 0x1); /* every second */
+#endif
+
+       return 0;
+}
+
+int mpc52xx_pm_prepare(suspend_state_t state)
+{
+       if (state != PM_SUSPEND_STANDBY)
+               return -EINVAL;
+
+       /* map the whole register space */
+       mbar = mpc52xx_find_and_map("mpc5200");
+       if (!mbar) {
+               printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, 
__LINE__);
+               return -ENOSYS;
+       }
+       /* these offsets are from mpc5200 users manual */
+       sdram = mbar + 0x100;
+       cdm = mbar + 0x200;
+       intr = mbar + 0x500;
+       rtc = mbar + 0x800;
+       gpiow = mbar + 0xc00;
+
+
+#ifdef DEBUG
+       mpc52xx_wakeup.mask |= WAKEUP_RTC;
+#endif
+       if (mpc52xx_wakeup.mask == 0) {
+               printk(KERN_ALERT "%s: %i don't know how to wake up the 
board\n",
+                               __func__, __LINE__);
+               goto out_unmap;
+       }
+       if (mpc52xx_wakeup.mask & WAKEUP_GPIO) {
+               if (mpc52xx_set_wakeup_gpio(mpc52xx_wakeup.pin, 
mpc52xx_wakeup.level))
+                       goto out_unmap;
+       }
+       if (mpc52xx_wakeup.mask & WAKEUP_RTC) {
+               if (mpc52xx_set_wakeup_rtc(mpc52xx_wakeup.delay))
+                       goto out_unmap;
+       }
+       if (mpc52xx_wakeup.mask & WAKEUP_MSCAN) {
+               /* TODO */
+       }
+
+       /* call board suspend code, if applicable */
+       if (mpc52xx_wakeup.board_suspend_prepare)
+               mpc52xx_wakeup.board_suspend_prepare(mbar);
+
+       return 0;
+
+ out_unmap:
+       iounmap(mbar);
+       return -ENOSYS;
+}
+
+extern void mpc52xx_ds_sram(void);
+extern const long mpc52xx_ds_sram_size;
+extern void mpc52xx_ds_cached(void);
+extern const long mpc52xx_ds_cached_size;
+
+static char saved_sram[0x4000];
+
+int mpc52xx_pm_enter(suspend_state_t state)
+{
+       u32 clk_enables;
+       u32 msr, hid0;
+       void __iomem * irq_0x500 = (void *)CONFIG_KERNEL_START + 0x500;
+       unsigned long irq_0x500_stop = (unsigned long)irq_0x500 + 
mpc52xx_ds_cached_size;
+       char saved_0x500[mpc52xx_ds_cached_size];
+
+       /* don't let DEC expire any time soon */
+       mtspr(SPRN_DEC, 0x7fffffff);
+
+       /* save SRAM */
+       memcpy(saved_sram, sdma.sram, sdma.sram_size);
+
+       /* copy low level suspend code to sram */
+       memcpy(sdma.sram, mpc52xx_ds_sram, mpc52xx_ds_sram_size);
+
+       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 and bestcomm (SRAM) clocks */
+       clk_enables = in_be32(&cdm->clk_enables);
+       out_be32(&cdm->clk_enables, clk_enables & 0x00088000);
+
+       /* disable power management */
+       msr = mfmsr();
+       mtmsr(msr & ~MSR_POW);
+
+       /* enable sleep mode, disable others */
+       hid0 = mfspr(SPRN_HID0);
+       mtspr(SPRN_HID0, (hid0 & ~(HID0_DOZE | HID0_NAP | HID0_DPM)) | 
HID0_SLEEP);
+
+       /* save original, copy our irq handler, flush from dcache and 
invalidate icache */
+       memcpy(saved_0x500, irq_0x500, mpc52xx_ds_cached_size);
+       memcpy(irq_0x500, mpc52xx_ds_cached, mpc52xx_ds_cached_size);
+       flush_icache_range((unsigned long)irq_0x500, irq_0x500_stop);
+
+       /* call low-level sleep code */
+       mpc52xx_deep_sleep(sdma.sram, sdram, cdm, intr);
+
+       /* restore original irq handler */
+       memcpy(irq_0x500, saved_0x500, mpc52xx_ds_cached_size);
+       flush_icache_range((unsigned long)irq_0x500, irq_0x500_stop);
+
+       /* restore old power mode */
+       mtmsr(msr & ~MSR_POW);
+       mtspr(SPRN_HID0, hid0);
+       mtmsr(msr);
+
+       out_be32(&cdm->clk_enables, clk_enables);
+       out_8(&cdm->ccs_sleep_enable, 0);
+       out_8(&cdm->osc_sleep_enable, 0);
+
+       /* restore SRAM */
+       memcpy(sdma.sram, saved_sram, sdma.sram_size);
+
+       /* restart jiffies */
+       wakeup_decrementer();
+
+       return 0;
+}
+
+static int mpc52xx_pm_finish(suspend_state_t state)
+{
+       /* call board resume code */
+       if (mpc52xx_wakeup.board_resume_finish)
+               mpc52xx_wakeup.board_resume_finish(mbar);
+
+       iounmap(mbar);
+
+       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,
+};
+
+int __init mpc52xx_pm_init(void)
+{
+       pm_set_ops(&mpc52xx_pm_ops);
+       return 0;
+}
Index: grant.git/arch/powerpc/platforms/52xx/mpc52xx_sleep.S
===================================================================
--- /dev/null
+++ grant.git/arch/powerpc/platforms/52xx/mpc52xx_sleep.S
@@ -0,0 +1,161 @@
+#include <asm/reg.h>
+#include <asm/ppc_asm.h>
+#include <asm/processor.h>
+
+
+#undef DEBUG /* doesn't halt the cpu. useful for bdi2000 debugging  */
+
+/* mpc5200b puts sdram automatically in self-refresh, previous versions don't 
*/
+#define SELF_REFRESH
+
+.text
+       .globl mpc52xx_deep_sleep
+mpc52xx_deep_sleep: /* args r3-r6: SRAM, SDRAM regs, CDM regs, INTR regs */
+
+       /* enable interrupts */
+       mfmsr   r7
+       ori     r7, r7, 0x8000 /* EE */
+       mtmsr   r7
+       sync; isync;
+
+       li      r10, 0 /* flag that irq handler sets */
+
+       /* enable tmr7 (or any other) interrupt */
+       lwz     r8, 0x14(r6) /* intr->main_mask */
+       ori     r8, r8, 0x1
+       xori    r8, r8, 0x1
+       stw     r8, 0x14(r6)
+       sync
+
+       /* emulate tmr7 interrupt */
+       li      r8, 0x1
+       stw     r8, 0x40(r6) /* intr->main_emulate */
+       sync
+
+       /* wait for it to happen */
+1:
+       cmpi    cr0, r10, 1
+       bne     cr0, 1b
+
+       /* lock icache */
+       mfspr   r10, SPRN_HID0
+       ori     r10, r10, 0x2000
+       sync; isync;
+       mtspr   SPRN_HID0, r10
+       sync; isync;
+
+
+       mflr    r9 /* save LR */
+
+       /* jump to sram */
+       mtlr    r3
+       blrl
+
+       mtlr    r9 /* restore LR */
+
+       /* unlock icache */
+       mfspr   r10, SPRN_HID0
+       ori     r10, r10, 0x2000
+       xori    r10, r10, 0x2000
+       sync; isync;
+       mtspr   SPRN_HID0, r10
+       sync; isync;
+
+
+       /* return to C code */
+       blr
+
+
+_GLOBAL(mpc52xx_ds_sram)
+mpc52xx_ds_sram:
+#ifdef SELF_REFRESH
+       lwz     r8, 0x4(r4)     /* sdram->ctrl */
+
+       oris    r8, r8, 0x8000 /* mode_en */
+       stw     r8, 0x4(r4)
+       sync
+
+       ori     r8, r8, 0x0002 /* soft_pre */
+       stw     r8, 0x4(r4)
+       sync
+       xori    r8, r8, 0x0002
+
+       xoris   r8, r8, 0x8000 /* mode_en */
+       stw     r8, 0x4(r4)
+       sync
+
+       oris    r8, r8, 0x5000
+       xoris   r8, r8, 0x4000 /* ref_en !cke */
+       stw     r8, 0x4(r4)
+       sync
+
+       /* disable SDRAM clock */
+       lwz     r8, 0x14(r5) /* cdm->clkenable */
+       ori     r8, r8, 0x0008
+       xori    r8, r8, 0x0008
+       stw     r8, 0x14(r5)
+       sync
+#endif
+
+#ifndef DEBUG
+       /* 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     r8, 0x14(r5)
+       ori     r8, r8, 0x0008
+       stw     r8, 0x14(r5)
+       sync
+
+       /* get ram out of self-refresh */
+       lwz     r8, 0x4(r4)
+       oris    r8, r8, 0x5000 /* cke ref_en */
+       stw     r8, 0x4(r4)
+       sync
+#endif
+
+       blr
+_GLOBAL(mpc52xx_ds_sram_size)
+mpc52xx_ds_sram_size:
+       .long $-mpc52xx_ds_sram
+
+
+/* ### interrupt handler for wakeup from deep-sleep ### */
+_GLOBAL(mpc52xx_ds_cached)
+mpc52xx_ds_cached:
+       mtspr   SPRN_SPRG0, r7
+       mtspr   SPRN_SPRG1, r8
+
+       /* disable emulated interrupt */
+       mfspr   r7, 311 /* MBAR */
+       addi    r7, r7, 0x540   /* intr->main_emul */
+       li      r8, 0
+       stw     r8, 0(r7)
+       sync
+       dcbf    0, r7
+
+       /* acknowledge wakeup, so CCS releases power pown */
+       mfspr   r7, 311 /* MBAR */
+       addi    r7, r7, 0x524   /* intr->enc_status */
+       lwz     r8, 0(r7)
+       ori     r8, r8, 0x0400
+       stw     r8, 0(r7)
+       sync
+       dcbf    0, r7
+
+       /* flag - we handled the interrupt */
+       li      r10, 1
+
+       mfspr   r8, SPRN_SPRG1
+       mfspr   r7, SPRN_SPRG0
+
+       rfi
+_GLOBAL(mpc52xx_ds_cached_size)
+mpc52xx_ds_cached_size:
+       .long $-mpc52xx_ds_cached
Index: grant.git/arch/powerpc/platforms/52xx/efika.c
===================================================================
--- grant.git.orig/arch/powerpc/platforms/52xx/efika.c
+++ grant.git/arch/powerpc/platforms/52xx/efika.c
@@ -200,6 +200,14 @@ static void __init efika_setup_arch(void
 
        efika_pcisetup();
 
+#ifdef CONFIG_PM
+       mpc52xx_wakeup.mask = WAKEUP_GPIO | WAKEUP_RTC;
+       mpc52xx_wakeup.pin = 4;         /* GPIO_WKUP_4 (GPIO_PSC6_0 - IRDA_RX) 
*/
+       mpc52xx_wakeup.level = 1;       /* wakeup on high level */
+       /* IOW. to wake it up, short pins 1 and 3 on IRDA connector */
+       mpc52xx_pm_init();
+#endif
+
        if (ppc_md.progress)
                ppc_md.progress("Linux/PPC " UTS_RELEASE " running on Efika 
;-)\n", 0x0);
 }
Index: grant.git/arch/powerpc/platforms/52xx/lite5200.c
===================================================================
--- grant.git.orig/arch/powerpc/platforms/52xx/lite5200.c
+++ grant.git/arch/powerpc/platforms/52xx/lite5200.c
@@ -85,6 +85,24 @@ error:
        iounmap(gpio);
 }
 
+#ifdef CONFIG_PM
+static u32 descr_a;
+static void lite5200_suspend_prepare(void __iomem *mbar)
+{
+       /*
+        * power down usb port
+        * this needs to be called before of-ohci suspend code
+        */
+       descr_a = in_be32(mbar + 0x1048);
+       out_be32(mbar + 0x1048, (descr_a & ~0x200) | 0x100);
+}
+
+static void lite5200_resume_finish(void __iomem *mbar)
+{
+       out_be32(mbar + 0x1048, descr_a);
+}
+#endif
+
 static void __init lite5200_setup_arch(void)
 {
        struct device_node *np;
@@ -107,6 +125,16 @@ static void __init lite5200_setup_arch(v
        mpc52xx_setup_cpu();    /* Generic */
        lite5200_setup_cpu();   /* Platorm specific */
 
+#ifdef CONFIG_PM
+       mpc52xx_wakeup.mask = WAKEUP_GPIO | WAKEUP_RTC;
+       mpc52xx_wakeup.pin = 1;         /* GPIO_WKUP_1 (GPIO_PSC2_4) */
+       mpc52xx_wakeup.level = 0;       /* wakeup on low level */
+       /* mpc52xx_wakeup.delay = 10;    wake after 10 minutes (RTC) */
+       mpc52xx_wakeup.board_suspend_prepare = lite5200_suspend_prepare;
+       mpc52xx_wakeup.board_resume_finish = lite5200_resume_finish;
+       mpc52xx_pm_init();
+#endif
+
 #ifdef CONFIG_PCI
        np = of_find_node_by_type(np, "pci");
        if (np)
Index: grant.git/include/asm-powerpc/mpc52xx.h
===================================================================
--- grant.git.orig/include/asm-powerpc/mpc52xx.h
+++ grant.git/include/asm-powerpc/mpc52xx.h
@@ -232,6 +232,51 @@ struct mpc52xx_cdm {
        u16 mclken_div_psc6;    /* CDM + 0x36  reg13 byte2,3 */
 };
 
+/* RTC */
+struct mpc52xx_rtc {
+       u8      set_time;       /* RTC + 0x00 */
+       u8      hour_set;       /* RTC + 0x01 */
+       u8      minute_set;     /* RTC + 0x02 */
+       u8      second_set;     /* RTC + 0x03 */
+
+       u8      set_date;       /* RTC + 0x04 */
+       u8      month_set;      /* RTC + 0x05 */
+       u8      weekday_set;    /* RTC + 0x06 */
+       u8      date_set;       /* RTC + 0x07 */
+
+       u8      write_sw;       /* RTC + 0x08 */
+       u8      sw_set;         /* RTC + 0x09 */
+       u16     year_set;       /* RTC + 0x0a */
+
+       u8      alm_enable;     /* RTC + 0x0c */
+       u8      alm_hour_set;   /* RTC + 0x0d */
+       u8      alm_min_set;    /* RTC + 0x0e */
+       u8      int_enable;     /* RTC + 0x0f */
+
+       u8      reserved1;
+       u8      hour;           /* RTC + 0x11 */
+       u8      minute;         /* RTC + 0x12 */
+       u8      second;         /* RTC + 0x13 */
+
+       u8      month;          /* RTC + 0x14 */
+       u8      wday_mday;      /* RTC + 0x15 */
+       u16     year;           /* RTC + 0x16 */
+
+       u8      int_alm;        /* RTC + 0x18 */
+       u8      int_sw;         /* RTC + 0x19 */
+       u8      alm_status;     /* RTC + 0x1a */
+       u8      sw_minute;      /* RTC + 0x1b */
+
+       u8      bus_error_1;    /* RTC + 0x1c */
+       u8      int_day;        /* RTC + 0x1d */
+       u8      int_min;        /* RTC + 0x1e */
+       u8      int_sec;        /* RTC + 0x1f */
+
+       u8      pterm;          /* RTC + 0x20 */
+       u8      eterm;          /* RTC + 0x21 */
+       u16     reserved2;
+};
+
 #endif /* __ASSEMBLY__ */
 
 
@@ -253,5 +298,23 @@ extern int __init mpc52xx_add_bridge(str
 
 #endif /* __ASSEMBLY__ */
 
+#ifdef CONFIG_PM
+#define WAKEUP_GPIO    (1<<1)
+#define WAKEUP_RTC     (2<<1)
+#define WAKEUP_MSCAN   (3<<1)
+struct mpc52xx_wakeup {
+       u8 mask;
+       u8 pin;         /* which wakeup GPIO */
+       u8 level;       /* transition to high or to low level */
+       u16 delay;      /* after how many minutes (RTC) */
+
+       void (*board_suspend_prepare)(void __iomem *mbar);
+       void (*board_resume_finish)(void __iomem *mbar);
+};
+
+extern struct mpc52xx_wakeup mpc52xx_wakeup;
+int __init mpc52xx_pm_init(void);
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_POWERPC_MPC52xx_H__ */
 
Index: grant.git/arch/powerpc/boot/dts/lite5200.dts
===================================================================
--- grant.git.orig/arch/powerpc/boot/dts/lite5200.dts
+++ grant.git/arch/powerpc/boot/dts/lite5200.dts
@@ -49,6 +49,7 @@
 
        [EMAIL PROTECTED] {
                model = "fsl,mpc5200";
+               compatible = "mpc5200";
                revision = ""                   // from bootloader
                #interrupt-cells = <3>;
                device_type = "soc";
Index: grant.git/arch/powerpc/boot/dts/lite5200b.dts
===================================================================
--- grant.git.orig/arch/powerpc/boot/dts/lite5200b.dts
+++ grant.git/arch/powerpc/boot/dts/lite5200b.dts
@@ -49,6 +49,7 @@
 
        [EMAIL PROTECTED] {
                model = "fsl,mpc5200b";
+               compatible = "mpc5200";
                revision = "";                  // from bootloader
                #interrupt-cells = <3>;
                device_type = "soc";
Index: grant.git/arch/powerpc/kernel/prom_init.c
===================================================================
--- grant.git.orig/arch/powerpc/kernel/prom_init.c
+++ grant.git/arch/powerpc/kernel/prom_init.c
@@ -2142,7 +2142,7 @@ static void __init fixup_device_tree_efi
                                     3,12,0, 3,13,0, 3,14,0, 3,15,0 };
        struct subst_entry efika_subst_table[] = {
                { "/",                  "device_type",  prop_cstr("efika") },
-               { "/builtin",           "compatible",   prop_cstr("soc") },
+               { "/builtin",           "device_type",  prop_cstr("soc") },
                { "/builtin/ata",       "compatible",   
prop_cstr("mpc5200b-ata\0mpc5200-ata"), },
                { "/builtin/bestcomm",  "compatible",   
prop_cstr("mpc5200b-bestcomm\0mpc5200-bestcomm") },
                { "/builtin/bestcomm",  "interrupts",   prop_bcomm_irq, 
sizeof(prop_bcomm_irq) },
_______________________________________________
Linuxppc-embedded mailing list
[email protected]
https://ozlabs.org/mailman/listinfo/linuxppc-embedded

Reply via email to