Magnus Damm | 7759491 | 2009-03-13 15:23:04 +0000 | [diff] [blame] | 1 | /* |
Magnus Damm | 7426394 | 2009-07-03 10:28:00 +0000 | [diff] [blame] | 2 | * arch/sh/kernel/cpu/shmobile/pm.c |
Magnus Damm | 7759491 | 2009-03-13 15:23:04 +0000 | [diff] [blame] | 3 | * |
| 4 | * Power management support code for SuperH Mobile |
| 5 | * |
| 6 | * Copyright (C) 2009 Magnus Damm |
| 7 | * |
| 8 | * This file is subject to the terms and conditions of the GNU General Public |
| 9 | * License. See the file "COPYING" in the main directory of this archive |
| 10 | * for more details. |
| 11 | */ |
| 12 | #include <linux/init.h> |
| 13 | #include <linux/kernel.h> |
| 14 | #include <linux/io.h> |
| 15 | #include <linux/suspend.h> |
| 16 | #include <asm/suspend.h> |
| 17 | #include <asm/uaccess.h> |
| 18 | |
| 19 | /* |
Magnus Damm | 49f4264 | 2009-10-29 10:51:48 +0000 | [diff] [blame] | 20 | * Notifier lists for pre/post sleep notification |
| 21 | */ |
| 22 | ATOMIC_NOTIFIER_HEAD(sh_mobile_pre_sleep_notifier_list); |
| 23 | ATOMIC_NOTIFIER_HEAD(sh_mobile_post_sleep_notifier_list); |
| 24 | |
| 25 | /* |
Magnus Damm | 7759491 | 2009-03-13 15:23:04 +0000 | [diff] [blame] | 26 | * Sleep modes available on SuperH Mobile: |
| 27 | * |
| 28 | * Sleep mode is just plain "sleep" instruction |
| 29 | * Sleep Self-Refresh mode is above plus RAM put in Self-Refresh |
| 30 | * Standby Self-Refresh mode is above plus stopped clocks |
| 31 | */ |
| 32 | #define SUSP_MODE_SLEEP (SUSP_SH_SLEEP) |
| 33 | #define SUSP_MODE_SLEEP_SF (SUSP_SH_SLEEP | SUSP_SH_SF) |
| 34 | #define SUSP_MODE_STANDBY_SF (SUSP_SH_STANDBY | SUSP_SH_SF) |
| 35 | |
| 36 | /* |
| 37 | * The following modes are not there yet: |
| 38 | * |
| 39 | * R-standby mode is unsupported, but will be added in the future |
| 40 | * U-standby mode is low priority since it needs bootloader hacks |
Magnus Damm | 7759491 | 2009-03-13 15:23:04 +0000 | [diff] [blame] | 41 | */ |
| 42 | |
Magnus Damm | 7426394 | 2009-07-03 10:28:00 +0000 | [diff] [blame] | 43 | #define ILRAM_BASE 0xe5200000 |
| 44 | |
Magnus Damm | 7759491 | 2009-03-13 15:23:04 +0000 | [diff] [blame] | 45 | extern const unsigned char sh_mobile_standby[]; |
| 46 | extern const unsigned int sh_mobile_standby_size; |
| 47 | |
Magnus Damm | 7426394 | 2009-07-03 10:28:00 +0000 | [diff] [blame] | 48 | void sh_mobile_call_standby(unsigned long mode) |
Magnus Damm | 7759491 | 2009-03-13 15:23:04 +0000 | [diff] [blame] | 49 | { |
Magnus Damm | 7426394 | 2009-07-03 10:28:00 +0000 | [diff] [blame] | 50 | void *onchip_mem = (void *)ILRAM_BASE; |
Magnus Damm | 309214a | 2009-08-17 09:27:29 +0000 | [diff] [blame] | 51 | void (*standby_onchip_mem)(unsigned long, unsigned long) = onchip_mem; |
Magnus Damm | 7759491 | 2009-03-13 15:23:04 +0000 | [diff] [blame] | 52 | |
Magnus Damm | 49f4264 | 2009-10-29 10:51:48 +0000 | [diff] [blame] | 53 | atomic_notifier_call_chain(&sh_mobile_pre_sleep_notifier_list, |
| 54 | mode, NULL); |
| 55 | |
Magnus Damm | 7759491 | 2009-03-13 15:23:04 +0000 | [diff] [blame] | 56 | /* Let assembly snippet in on-chip memory handle the rest */ |
Magnus Damm | 309214a | 2009-08-17 09:27:29 +0000 | [diff] [blame] | 57 | standby_onchip_mem(mode, ILRAM_BASE); |
Magnus Damm | 49f4264 | 2009-10-29 10:51:48 +0000 | [diff] [blame] | 58 | |
| 59 | atomic_notifier_call_chain(&sh_mobile_post_sleep_notifier_list, |
| 60 | mode, NULL); |
Magnus Damm | 7759491 | 2009-03-13 15:23:04 +0000 | [diff] [blame] | 61 | } |
| 62 | |
Magnus Damm | 159f8cd | 2009-10-29 10:52:06 +0000 | [diff] [blame^] | 63 | void sh_mobile_register_self_refresh(unsigned long flags, |
| 64 | void *pre_start, void *pre_end, |
| 65 | void *post_start, void *post_end) |
| 66 | { |
| 67 | } |
| 68 | |
Magnus Damm | 7759491 | 2009-03-13 15:23:04 +0000 | [diff] [blame] | 69 | static int sh_pm_enter(suspend_state_t state) |
| 70 | { |
| 71 | local_irq_disable(); |
| 72 | set_bl_bit(); |
| 73 | sh_mobile_call_standby(SUSP_MODE_STANDBY_SF); |
| 74 | local_irq_disable(); |
| 75 | clear_bl_bit(); |
| 76 | return 0; |
| 77 | } |
| 78 | |
| 79 | static struct platform_suspend_ops sh_pm_ops = { |
| 80 | .enter = sh_pm_enter, |
| 81 | .valid = suspend_valid_only_mem, |
| 82 | }; |
| 83 | |
| 84 | static int __init sh_pm_init(void) |
| 85 | { |
Magnus Damm | 7426394 | 2009-07-03 10:28:00 +0000 | [diff] [blame] | 86 | void *onchip_mem = (void *)ILRAM_BASE; |
| 87 | |
| 88 | /* Copy the assembly snippet to the otherwise ununsed ILRAM */ |
| 89 | memcpy(onchip_mem, sh_mobile_standby, sh_mobile_standby_size); |
| 90 | wmb(); |
| 91 | ctrl_barrier(); |
| 92 | |
Magnus Damm | 7759491 | 2009-03-13 15:23:04 +0000 | [diff] [blame] | 93 | suspend_set_ops(&sh_pm_ops); |
Magnus Damm | 7426394 | 2009-07-03 10:28:00 +0000 | [diff] [blame] | 94 | sh_mobile_setup_cpuidle(); |
Magnus Damm | 7759491 | 2009-03-13 15:23:04 +0000 | [diff] [blame] | 95 | return 0; |
| 96 | } |
| 97 | |
| 98 | late_initcall(sh_pm_init); |