Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | /* * arch/sh/kernel/cpu/shmobile/pm.c * * Power management support code for SuperH Mobile * * Copyright (C) 2009 Magnus Damm * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/io.h> #include <linux/suspend.h> #include <asm/suspend.h> #include <asm/uaccess.h> #include <asm/cacheflush.h> #include <asm/bl_bit.h> /* * Notifier lists for pre/post sleep notification */ ATOMIC_NOTIFIER_HEAD(sh_mobile_pre_sleep_notifier_list); ATOMIC_NOTIFIER_HEAD(sh_mobile_post_sleep_notifier_list); /* * Sleep modes available on SuperH Mobile: * * Sleep mode is just plain "sleep" instruction * Sleep Self-Refresh mode is above plus RAM put in Self-Refresh * Standby Self-Refresh mode is above plus stopped clocks */ #define SUSP_MODE_SLEEP (SUSP_SH_SLEEP) #define SUSP_MODE_SLEEP_SF (SUSP_SH_SLEEP | SUSP_SH_SF) #define SUSP_MODE_STANDBY_SF (SUSP_SH_STANDBY | SUSP_SH_SF) #define SUSP_MODE_RSTANDBY_SF \ (SUSP_SH_RSTANDBY | SUSP_SH_MMU | SUSP_SH_REGS | SUSP_SH_SF) /* * U-standby mode is unsupported since it needs bootloader hacks */ #ifdef CONFIG_CPU_SUBTYPE_SH7724 #define RAM_BASE 0xfd800000 /* RSMEM */ #else #define RAM_BASE 0xe5200000 /* ILRAM */ #endif void sh_mobile_call_standby(unsigned long mode) { void *onchip_mem = (void *)RAM_BASE; struct sh_sleep_data *sdp = onchip_mem; void (*standby_onchip_mem)(unsigned long, unsigned long); /* code located directly after data structure */ standby_onchip_mem = (void *)(sdp + 1); 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, RAM_BASE); atomic_notifier_call_chain(&sh_mobile_post_sleep_notifier_list, mode, NULL); } extern char sh_mobile_sleep_enter_start; extern char sh_mobile_sleep_enter_end; extern char sh_mobile_sleep_resume_start; extern char sh_mobile_sleep_resume_end; unsigned long sh_mobile_sleep_supported = SUSP_SH_SLEEP; void sh_mobile_register_self_refresh(unsigned long flags, void *pre_start, void *pre_end, void *post_start, void *post_end) { void *onchip_mem = (void *)RAM_BASE; void *vp; struct sh_sleep_data *sdp; int n; /* part 0: data area */ sdp = onchip_mem; sdp->addr.stbcr = 0xa4150020; /* STBCR */ sdp->addr.bar = 0xa4150040; /* BAR */ 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 */ n = &sh_mobile_sleep_enter_end - &sh_mobile_sleep_enter_start; memcpy(vp, &sh_mobile_sleep_enter_start, n); vp += roundup(n, 4); /* part 2: board specific code to enter self-refresh mode */ n = pre_end - pre_start; memcpy(vp, pre_start, n); sdp->sf_pre = (unsigned long)vp; vp += roundup(n, 4); /* part 3: board specific code to resume from self-refresh mode */ n = post_end - post_start; memcpy(vp, post_start, n); sdp->sf_post = (unsigned long)vp; vp += roundup(n, 4); /* part 4: common code to resume from sleep mode */ WARN_ON(vp > (onchip_mem + 0x600)); vp = onchip_mem + 0x600; /* located at interrupt vector */ n = &sh_mobile_sleep_resume_end - &sh_mobile_sleep_resume_start; memcpy(vp, &sh_mobile_sleep_resume_start, n); sdp->resume = (unsigned long)vp; sh_mobile_sleep_supported |= flags; } static int sh_pm_enter(suspend_state_t state) { if (!(sh_mobile_sleep_supported & SUSP_MODE_STANDBY_SF)) return -ENXIO; local_irq_disable(); set_bl_bit(); sh_mobile_call_standby(SUSP_MODE_STANDBY_SF); local_irq_disable(); clear_bl_bit(); return 0; } static const struct platform_suspend_ops sh_pm_ops = { .enter = sh_pm_enter, .valid = suspend_valid_only_mem, }; static int __init sh_pm_init(void) { suspend_set_ops(&sh_pm_ops); sh_mobile_setup_cpuidle(); return 0; } late_initcall(sh_pm_init); |