sh: Add MMU and Cache handling sleep mode code
authorMagnus Damm <damm@opensource.se>
Fri, 30 Oct 2009 04:24:23 +0000 (04:24 +0000)
committerPaul Mundt <lethal@linux-sh.org>
Fri, 30 Oct 2009 05:37:56 +0000 (14:37 +0900)
Add MMU and cache handling functionality to the SuperH Mobile
sleep code. The MMU and cache registers are saved and restored.
The MMU is disabled and the cache is flushed and disabled before
entering sleep modes if the SUSP_SH_MMU flag is set. This flag
should be set in the case of R-standby and most likely for future
U-standby support as well.

Signed-off-by: Magnus Damm <damm@opensource.se>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
arch/sh/include/asm/suspend.h
arch/sh/kernel/asm-offsets.c
arch/sh/kernel/cpu/shmobile/pm.c
arch/sh/kernel/cpu/shmobile/sleep.S

index 8eddf236fb85a9fa4e92fb4474f280b5313f0ac9..702025d960a0b6dfa57c772c20eb40e1ffb9a983 100644 (file)
@@ -38,6 +38,20 @@ void sh_mobile_register_self_refresh(unsigned long flags,
 /* register structure for address/data information */
 struct sh_sleep_regs {
        unsigned long stbcr;
+
+       /* MMU */
+       unsigned long pteh;
+       unsigned long ptel;
+       unsigned long ttb;
+       unsigned long tea;
+       unsigned long mmucr;
+       unsigned long ptea;
+       unsigned long pascr;
+       unsigned long irmcr;
+
+       /* Cache */
+       unsigned long ccr;
+       unsigned long ramcr;
 };
 
 /* data area for low-level sleep code */
@@ -72,5 +86,6 @@ extern unsigned long sh_mobile_sleep_supported;
 #define SUSP_SH_RSTANDBY       (1 << 2) /* SH-Mobile R-standby mode */
 #define SUSP_SH_USTANDBY       (1 << 3) /* SH-Mobile U-standby mode */
 #define SUSP_SH_SF             (1 << 4) /* Enable self-refresh */
+#define SUSP_SH_MMU            (1 << 5) /* Save/restore MMU and cache */
 
 #endif /* _ASM_SH_SUSPEND_H */
index 9bdeff962e1c878e8deaabff6bbaa60fd84bfc30..6026b0f849a1bf367081d3e14bc4b01a1d6fe116 100644 (file)
@@ -44,5 +44,15 @@ int main(void)
        DEFINE(SH_SLEEP_BASE_ADDR, offsetof(struct sh_sleep_data, addr));
        DEFINE(SH_SLEEP_BASE_DATA, offsetof(struct sh_sleep_data, data));
        DEFINE(SH_SLEEP_REG_STBCR, offsetof(struct sh_sleep_regs, stbcr));
+       DEFINE(SH_SLEEP_REG_PTEH, offsetof(struct sh_sleep_regs, pteh));
+       DEFINE(SH_SLEEP_REG_PTEL, offsetof(struct sh_sleep_regs, ptel));
+       DEFINE(SH_SLEEP_REG_TTB, offsetof(struct sh_sleep_regs, ttb));
+       DEFINE(SH_SLEEP_REG_TEA, offsetof(struct sh_sleep_regs, tea));
+       DEFINE(SH_SLEEP_REG_MMUCR, offsetof(struct sh_sleep_regs, mmucr));
+       DEFINE(SH_SLEEP_REG_PTEA, offsetof(struct sh_sleep_regs, ptea));
+       DEFINE(SH_SLEEP_REG_PASCR, offsetof(struct sh_sleep_regs, pascr));
+       DEFINE(SH_SLEEP_REG_IRMCR, offsetof(struct sh_sleep_regs, irmcr));
+       DEFINE(SH_SLEEP_REG_CCR, offsetof(struct sh_sleep_regs, ccr));
+       DEFINE(SH_SLEEP_REG_RAMCR, offsetof(struct sh_sleep_regs, ramcr));
        return 0;
 }
index a94dc480f0c1371efd77cb299da987c8bf5bd60c..ca642f39e2e3ab85f39014b1016a3cb1a4dc6cf2 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/suspend.h>
 #include <asm/suspend.h>
 #include <asm/uaccess.h>
+#include <asm/cacheflush.h>
 
 /*
  * Notifier lists for pre/post sleep notification
@@ -54,6 +55,10 @@ void sh_mobile_call_standby(unsigned long mode)
        atomic_notifier_call_chain(&sh_mobile_pre_sleep_notifier_list,
                                   mode, NULL);
 
+       /* flush the caches if MMU flag is set */
+       if (mode & SUSP_SH_MMU)
+               flush_cache_all();
+
        /* Let assembly snippet in on-chip memory handle the rest */
        standby_onchip_mem(mode, ILRAM_BASE);
 
@@ -81,6 +86,16 @@ void sh_mobile_register_self_refresh(unsigned long flags,
        /* part 0: data area */
        sdp = onchip_mem;
        sdp->addr.stbcr = 0xa4150020; /* STBCR */
+       sdp->addr.pteh = 0xff000000; /* PTEH */
+       sdp->addr.ptel = 0xff000004; /* PTEL */
+       sdp->addr.ttb = 0xff000008; /* TTB */
+       sdp->addr.tea = 0xff00000c; /* TEA */
+       sdp->addr.mmucr = 0xff000010; /* MMUCR */
+       sdp->addr.ptea = 0xff000034; /* PTEA */
+       sdp->addr.pascr = 0xff000070; /* PASCR */
+       sdp->addr.irmcr = 0xff000078; /* IRMCR */
+       sdp->addr.ccr = 0xff00001c; /* CCR */
+       sdp->addr.ramcr = 0xff000074; /* RAMCR */
        vp = sdp + 1;
 
        /* part 1: common code to enter sleep mode */
index d3221d9b88be25ebe86965e9d6879327da64c4a3..e620bf397af5a3d8f479577c7dcd86aaa6565bd6 100644 (file)
@@ -52,6 +52,57 @@ ENTRY(sh_mobile_sleep_enter_start)
        bsr     save_register
         mov    #SH_SLEEP_REG_STBCR, r0
 
+       /* save mmu and cache context if needed */
+       mov.l   @(SH_SLEEP_MODE, r5), r0
+       tst     #SUSP_SH_MMU, r0
+       bt      skip_mmu_save_disable
+
+       /* save mmu state */
+       bsr     save_register
+        mov    #SH_SLEEP_REG_PTEH, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_PTEL, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_TTB, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_TEA, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_MMUCR, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_PTEA, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_PASCR, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_IRMCR, r0
+
+       /* invalidate TLBs and disable the MMU */
+       bsr     get_register
+        mov    #SH_SLEEP_REG_MMUCR, r0
+       mov     #4, r1
+       mov.l   r1, @r0
+       icbi    @r0
+
+       /* save cache registers and disable caches */
+       bsr     save_register
+        mov    #SH_SLEEP_REG_CCR, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_RAMCR, r0
+
+       bsr     get_register
+        mov    #SH_SLEEP_REG_CCR, r0
+       mov     #0, r1
+       mov.l   r1, @r0
+       icbi    @r0
+
+skip_mmu_save_disable:
        /* call self-refresh entering code if needed */
        mov.l   @(SH_SLEEP_MODE, r5), r0
        tst     #SUSP_SH_SF, r0
@@ -166,6 +217,47 @@ ENTRY(sh_mobile_sleep_resume_start)
         nop
 
 skip_restore_sf:
+       /* restore mmu and cache state if needed */
+       mov.l   @(SH_SLEEP_MODE, r5), r0
+       tst     #SUSP_SH_MMU, r0
+       bt      skip_restore_mmu
+
+       /* restore mmu state */
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_PTEH, r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_PTEL, r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_TTB, r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_TEA, r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_PTEA, r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_PASCR, r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_IRMCR, r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_MMUCR, r0
+       icbi    @r0
+
+       /* restore cache settings */
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_RAMCR, r0
+       icbi    @r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_CCR, r0
+       icbi    @r0
+
+skip_restore_mmu:
        rte
         nop