diff options
| author | Magnus Damm <damm@igel.co.jp> | 2009-03-06 04:47:02 -0500 |
|---|---|---|
| committer | Paul Mundt <lethal@linux-sh.org> | 2009-03-09 23:55:40 -0400 |
| commit | 2ef7f0dab6b3d171b6aff00a47077385ae3155b5 (patch) | |
| tree | 6066996e7dfc65b02470ae4c1c96c6c7c70965a6 | |
| parent | edab56f4c90f45ba4e61d06d3fc9658da4e94bde (diff) | |
sh: hibernation support
Add Suspend-to-disk / swsusp / CONFIG_HIBERNATION support
to the SuperH architecture.
To suspend, use "swapon /dev/sda2; echo disk > /sys/power/state"
To resume, pass "resume=/dev/sda2" on the kernel command line.
The patch "pm: rework includes, remove arch ifdefs V2" is
needed to allow the generic swsusp code to build properly.
Hibernation is not enabled with this patch though, a patch
setting ARCH_HIBERNATION_POSSIBLE will be submitted later.
Signed-off-by: Magnus Damm <damm@igel.co.jp>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
| -rw-r--r-- | arch/sh/include/asm/sections.h | 1 | ||||
| -rw-r--r-- | arch/sh/include/asm/suspend.h | 13 | ||||
| -rw-r--r-- | arch/sh/kernel/Makefile_32 | 1 | ||||
| -rw-r--r-- | arch/sh/kernel/asm-offsets.c | 8 | ||||
| -rw-r--r-- | arch/sh/kernel/cpu/sh3/Makefile | 2 | ||||
| -rw-r--r-- | arch/sh/kernel/cpu/sh3/entry.S | 22 | ||||
| -rw-r--r-- | arch/sh/kernel/cpu/sh3/swsusp.S | 147 | ||||
| -rw-r--r-- | arch/sh/kernel/cpu/sh4/Makefile | 1 | ||||
| -rw-r--r-- | arch/sh/kernel/swsusp.c | 38 |
9 files changed, 228 insertions, 5 deletions
diff --git a/arch/sh/include/asm/sections.h b/arch/sh/include/asm/sections.h index 8f8f4ad400df..01a4076a3719 100644 --- a/arch/sh/include/asm/sections.h +++ b/arch/sh/include/asm/sections.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <asm-generic/sections.h> | 4 | #include <asm-generic/sections.h> |
| 5 | 5 | ||
| 6 | extern void __nosave_begin, __nosave_end; | ||
| 6 | extern long __machvec_start, __machvec_end; | 7 | extern long __machvec_start, __machvec_end; |
| 7 | extern char __uncached_start, __uncached_end; | 8 | extern char __uncached_start, __uncached_end; |
| 8 | extern char _ebss[]; | 9 | extern char _ebss[]; |
diff --git a/arch/sh/include/asm/suspend.h b/arch/sh/include/asm/suspend.h new file mode 100644 index 000000000000..5d6d8aba5682 --- /dev/null +++ b/arch/sh/include/asm/suspend.h | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | #ifndef _ASM_SH_SUSPEND_H | ||
| 2 | #define _ASM_SH_SUSPEND_H | ||
| 3 | |||
| 4 | static inline int arch_prepare_suspend(void) { return 0; } | ||
| 5 | |||
| 6 | #include <asm/ptrace.h> | ||
| 7 | |||
| 8 | struct swsusp_arch_regs { | ||
| 9 | struct pt_regs user_regs; | ||
| 10 | unsigned long bank1_regs[8]; | ||
| 11 | }; | ||
| 12 | |||
| 13 | #endif /* _ASM_SH_SUSPEND_H */ | ||
diff --git a/arch/sh/kernel/Makefile_32 b/arch/sh/kernel/Makefile_32 index 2e1b86e16ab5..82a3a150c00d 100644 --- a/arch/sh/kernel/Makefile_32 +++ b/arch/sh/kernel/Makefile_32 | |||
| @@ -30,5 +30,6 @@ obj-$(CONFIG_KPROBES) += kprobes.o | |||
| 30 | obj-$(CONFIG_GENERIC_GPIO) += gpio.o | 30 | obj-$(CONFIG_GENERIC_GPIO) += gpio.o |
| 31 | obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o | 31 | obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o |
| 32 | obj-$(CONFIG_DUMP_CODE) += disassemble.o | 32 | obj-$(CONFIG_DUMP_CODE) += disassemble.o |
| 33 | obj-$(CONFIG_HIBERNATION) += swsusp.o | ||
| 33 | 34 | ||
| 34 | EXTRA_CFLAGS += -Werror | 35 | EXTRA_CFLAGS += -Werror |
diff --git a/arch/sh/kernel/asm-offsets.c b/arch/sh/kernel/asm-offsets.c index 57cf0e0680f3..99aceb28ee24 100644 --- a/arch/sh/kernel/asm-offsets.c +++ b/arch/sh/kernel/asm-offsets.c | |||
| @@ -12,8 +12,10 @@ | |||
| 12 | #include <linux/types.h> | 12 | #include <linux/types.h> |
| 13 | #include <linux/mm.h> | 13 | #include <linux/mm.h> |
| 14 | #include <linux/kbuild.h> | 14 | #include <linux/kbuild.h> |
| 15 | #include <linux/suspend.h> | ||
| 15 | 16 | ||
| 16 | #include <asm/thread_info.h> | 17 | #include <asm/thread_info.h> |
| 18 | #include <asm/suspend.h> | ||
| 17 | 19 | ||
| 18 | int main(void) | 20 | int main(void) |
| 19 | { | 21 | { |
| @@ -25,5 +27,11 @@ int main(void) | |||
| 25 | DEFINE(TI_PRE_COUNT, offsetof(struct thread_info, preempt_count)); | 27 | DEFINE(TI_PRE_COUNT, offsetof(struct thread_info, preempt_count)); |
| 26 | DEFINE(TI_RESTART_BLOCK,offsetof(struct thread_info, restart_block)); | 28 | DEFINE(TI_RESTART_BLOCK,offsetof(struct thread_info, restart_block)); |
| 27 | 29 | ||
| 30 | #ifdef CONFIG_HIBERNATION | ||
| 31 | DEFINE(PBE_ADDRESS, offsetof(struct pbe, address)); | ||
| 32 | DEFINE(PBE_ORIG_ADDRESS, offsetof(struct pbe, orig_address)); | ||
| 33 | DEFINE(PBE_NEXT, offsetof(struct pbe, next)); | ||
| 34 | DEFINE(SWSUSP_ARCH_REGS_SIZE, sizeof(struct swsusp_arch_regs)); | ||
| 35 | #endif | ||
| 28 | return 0; | 36 | return 0; |
| 29 | } | 37 | } |
diff --git a/arch/sh/kernel/cpu/sh3/Makefile b/arch/sh/kernel/cpu/sh3/Makefile index e07c69e16d9b..ecab274141a8 100644 --- a/arch/sh/kernel/cpu/sh3/Makefile +++ b/arch/sh/kernel/cpu/sh3/Makefile | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | obj-y := ex.o probe.o entry.o setup-sh3.o | 5 | obj-y := ex.o probe.o entry.o setup-sh3.o |
| 6 | 6 | ||
| 7 | obj-$(CONFIG_HIBERNATION) += swsusp.o | ||
| 8 | |||
| 7 | # CPU subtype setup | 9 | # CPU subtype setup |
| 8 | obj-$(CONFIG_CPU_SUBTYPE_SH7705) += setup-sh7705.o | 10 | obj-$(CONFIG_CPU_SUBTYPE_SH7705) += setup-sh7705.o |
| 9 | obj-$(CONFIG_CPU_SUBTYPE_SH7706) += setup-sh770x.o | 11 | obj-$(CONFIG_CPU_SUBTYPE_SH7706) += setup-sh770x.o |
diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S index c4829d6dee51..fba6ac20bb17 100644 --- a/arch/sh/kernel/cpu/sh3/entry.S +++ b/arch/sh/kernel/cpu/sh3/entry.S | |||
| @@ -216,7 +216,7 @@ ENTRY(sh_bios_handler) | |||
| 216 | ! r9 trashed | 216 | ! r9 trashed |
| 217 | ! BL=0 on entry, on exit BL=1 (depending on r8). | 217 | ! BL=0 on entry, on exit BL=1 (depending on r8). |
| 218 | 218 | ||
| 219 | restore_regs: | 219 | ENTRY(restore_regs) |
| 220 | mov.l @r15+, r0 | 220 | mov.l @r15+, r0 |
| 221 | mov.l @r15+, r1 | 221 | mov.l @r15+, r1 |
| 222 | mov.l @r15+, r2 | 222 | mov.l @r15+, r2 |
| @@ -362,8 +362,10 @@ general_exception: | |||
| 362 | nop | 362 | nop |
| 363 | 363 | ||
| 364 | ! Save registers / Switch to bank 0 | 364 | ! Save registers / Switch to bank 0 |
| 365 | mov.l k4, k2 ! keep vector in k2 | ||
| 366 | mov.l 1f, k4 ! SR bits to clear in k4 | ||
| 365 | bsr save_regs ! needs original pr value in k3 | 367 | bsr save_regs ! needs original pr value in k3 |
| 366 | mov k4, k2 ! keep vector in k2 | 368 | nop |
| 367 | 369 | ||
| 368 | bra handle_exception_special | 370 | bra handle_exception_special |
| 369 | nop | 371 | nop |
| @@ -471,6 +473,7 @@ handle_exception: | |||
| 471 | 473 | ||
| 472 | ! Save registers / Switch to bank 0 | 474 | ! Save registers / Switch to bank 0 |
| 473 | mov.l 5f, k2 ! vector register address | 475 | mov.l 5f, k2 ! vector register address |
| 476 | mov.l 1f, k4 ! SR bits to clear in k4 | ||
| 474 | bsr save_regs ! needs original pr value in k3 | 477 | bsr save_regs ! needs original pr value in k3 |
| 475 | mov.l @k2, k2 ! read out vector and keep in k2 | 478 | mov.l @k2, k2 ! read out vector and keep in k2 |
| 476 | 479 | ||
| @@ -495,10 +498,10 @@ handle_exception_special: | |||
| 495 | ! k0 contains original stack pointer* | 498 | ! k0 contains original stack pointer* |
| 496 | ! k1 trashed | 499 | ! k1 trashed |
| 497 | ! k3 passes original pr* | 500 | ! k3 passes original pr* |
| 498 | ! k4 trashed | 501 | ! k4 passes SR bitmask |
| 499 | ! BL=1 on entry, on exit BL=0. | 502 | ! BL=1 on entry, on exit BL=0. |
| 500 | 503 | ||
| 501 | save_regs: | 504 | ENTRY(save_regs) |
| 502 | mov #-1, r1 | 505 | mov #-1, r1 |
| 503 | mov.l k1, @-r15 ! set TRA (default: -1) | 506 | mov.l k1, @-r15 ! set TRA (default: -1) |
| 504 | sts.l macl, @-r15 | 507 | sts.l macl, @-r15 |
| @@ -518,8 +521,16 @@ save_regs: | |||
| 518 | mov.l r8, @-r15 | 521 | mov.l r8, @-r15 |
| 519 | 522 | ||
| 520 | mov.l 0f, k3 ! SR bits to set in k3 | 523 | mov.l 0f, k3 ! SR bits to set in k3 |
| 521 | mov.l 1f, k4 ! SR bits to clear in k4 | ||
| 522 | 524 | ||
| 525 | ! fall-through | ||
| 526 | |||
| 527 | ! save_low_regs() | ||
| 528 | ! - modify SR for bank switch | ||
| 529 | ! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack | ||
| 530 | ! k3 passes bits to set in SR | ||
| 531 | ! k4 passes bits to clear in SR | ||
| 532 | |||
| 533 | ENTRY(save_low_regs) | ||
| 523 | stc sr, r8 | 534 | stc sr, r8 |
| 524 | or k3, r8 | 535 | or k3, r8 |
| 525 | and k4, r8 | 536 | and k4, r8 |
| @@ -565,6 +576,7 @@ ENTRY(handle_interrupt) | |||
| 565 | PREF(k0) | 576 | PREF(k0) |
| 566 | 577 | ||
| 567 | ! Save registers / Switch to bank 0 | 578 | ! Save registers / Switch to bank 0 |
| 579 | mov.l 1f, k4 ! SR bits to clear in k4 | ||
| 568 | bsr save_regs ! needs original pr value in k3 | 580 | bsr save_regs ! needs original pr value in k3 |
| 569 | mov #-1, k2 ! default vector kept in k2 | 581 | mov #-1, k2 ! default vector kept in k2 |
| 570 | 582 | ||
diff --git a/arch/sh/kernel/cpu/sh3/swsusp.S b/arch/sh/kernel/cpu/sh3/swsusp.S new file mode 100644 index 000000000000..01145426a2b8 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/swsusp.S | |||
| @@ -0,0 +1,147 @@ | |||
| 1 | /* | ||
| 2 | * arch/sh/kernel/cpu/sh3/swsusp.S | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Magnus Damm | ||
| 5 | * | ||
| 6 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 7 | * License. See the file "COPYING" in the main directory of this archive | ||
| 8 | * for more details. | ||
| 9 | */ | ||
| 10 | #include <linux/sys.h> | ||
| 11 | #include <linux/errno.h> | ||
| 12 | #include <linux/linkage.h> | ||
| 13 | #include <asm/asm-offsets.h> | ||
| 14 | #include <asm/page.h> | ||
| 15 | |||
| 16 | #define k0 r0 | ||
| 17 | #define k1 r1 | ||
| 18 | #define k2 r2 | ||
| 19 | #define k3 r3 | ||
| 20 | #define k4 r4 | ||
| 21 | |||
| 22 | ! swsusp_arch_resume() | ||
| 23 | ! - copy restore_pblist pages | ||
| 24 | ! - restore registers from swsusp_arch_regs_cpu0 | ||
| 25 | |||
| 26 | ENTRY(swsusp_arch_resume) | ||
| 27 | mov.l 1f, r15 | ||
| 28 | mov.l 2f, r4 | ||
| 29 | mov.l @r4, r4 | ||
| 30 | |||
| 31 | swsusp_copy_loop: | ||
| 32 | mov r4, r0 | ||
| 33 | cmp/eq #0, r0 | ||
| 34 | bt swsusp_restore_regs | ||
| 35 | |||
| 36 | mov.l @(PBE_ADDRESS, r4), r2 | ||
| 37 | mov.l @(PBE_ORIG_ADDRESS, r4), r5 | ||
| 38 | |||
| 39 | mov #(PAGE_SIZE >> 10), r3 | ||
| 40 | shll8 r3 | ||
| 41 | shlr2 r3 /* PAGE_SIZE / 16 */ | ||
| 42 | swsusp_copy_page: | ||
| 43 | dt r3 | ||
| 44 | mov.l @r2+,r1 /* 16n+0 */ | ||
| 45 | mov.l r1,@r5 | ||
| 46 | add #4,r5 | ||
| 47 | mov.l @r2+,r1 /* 16n+4 */ | ||
| 48 | mov.l r1,@r5 | ||
| 49 | add #4,r5 | ||
| 50 | mov.l @r2+,r1 /* 16n+8 */ | ||
| 51 | mov.l r1,@r5 | ||
| 52 | add #4,r5 | ||
| 53 | mov.l @r2+,r1 /* 16n+12 */ | ||
| 54 | mov.l r1,@r5 | ||
| 55 | bf/s swsusp_copy_page | ||
| 56 | add #4,r5 | ||
| 57 | |||
| 58 | bra swsusp_copy_loop | ||
| 59 | mov.l @(PBE_NEXT, r4), r4 | ||
| 60 | |||
| 61 | swsusp_restore_regs: | ||
| 62 | ! BL=0: R7->R0 is bank0 | ||
| 63 | mov.l 3f, r8 | ||
| 64 | mov.l 4f, r5 | ||
| 65 | jsr @r5 | ||
| 66 | nop | ||
| 67 | |||
| 68 | ! BL=1: R7->R0 is bank1 | ||
| 69 | lds k2, pr | ||
| 70 | ldc k3, ssr | ||
| 71 | |||
| 72 | mov.l @r15+, r0 | ||
| 73 | mov.l @r15+, r1 | ||
| 74 | mov.l @r15+, r2 | ||
| 75 | mov.l @r15+, r3 | ||
| 76 | mov.l @r15+, r4 | ||
| 77 | mov.l @r15+, r5 | ||
| 78 | mov.l @r15+, r6 | ||
| 79 | mov.l @r15+, r7 | ||
| 80 | |||
| 81 | rte | ||
| 82 | nop | ||
| 83 | ! BL=0: R7->R0 is bank0 | ||
| 84 | |||
| 85 | .align 2 | ||
| 86 | 1: .long swsusp_arch_regs_cpu0 | ||
| 87 | 2: .long restore_pblist | ||
| 88 | 3: .long 0x20000000 ! RB=1 | ||
| 89 | 4: .long restore_regs | ||
| 90 | |||
| 91 | ! swsusp_arch_suspend() | ||
| 92 | ! - prepare pc for resume, return from function without swsusp_save on resume | ||
| 93 | ! - save registers in swsusp_arch_regs_cpu0 | ||
| 94 | ! - call swsusp_save write suspend image | ||
| 95 | |||
| 96 | ENTRY(swsusp_arch_suspend) | ||
| 97 | sts pr, r0 ! save pr in r0 | ||
| 98 | mov r15, r2 ! save sp in r2 | ||
| 99 | mov r8, r5 ! save r8 in r5 | ||
| 100 | stc sr, r1 | ||
| 101 | ldc r1, ssr ! save sr in ssr | ||
| 102 | mov.l 1f, r1 | ||
| 103 | ldc r1, spc ! setup pc value for resuming | ||
| 104 | mov.l 5f, r15 ! use swsusp_arch_regs_cpu0 as stack | ||
| 105 | mov.l 6f, r3 | ||
| 106 | add r3, r15 ! save from top of structure | ||
| 107 | |||
| 108 | ! BL=0: R7->R0 is bank0 | ||
| 109 | mov.l 2f, r3 ! get new SR value for bank1 | ||
| 110 | mov #0, r4 | ||
| 111 | mov.l 7f, r1 | ||
| 112 | jsr @r1 ! switch to bank1 and save bank1 r7->r0 | ||
| 113 | not r4, r4 | ||
| 114 | |||
| 115 | ! BL=1: R7->R0 is bank1 | ||
| 116 | stc r2_bank, k0 ! fetch old sp from r2_bank0 | ||
| 117 | mov.l 3f, k4 ! SR bits to clear in k4 | ||
| 118 | mov.l 8f, k1 | ||
| 119 | jsr @k1 ! switch to bank0 and save all regs | ||
| 120 | stc r0_bank, k3 ! fetch old pr from r0_bank0 | ||
| 121 | |||
| 122 | ! BL=0: R7->R0 is bank0 | ||
| 123 | mov r2, r15 ! restore old sp | ||
| 124 | mov r5, r8 ! restore old r8 | ||
| 125 | stc ssr, r1 | ||
| 126 | ldc r1, sr ! restore old sr | ||
| 127 | lds r0, pr ! restore old pr | ||
| 128 | mov.l 4f, r0 | ||
| 129 | jmp @r0 | ||
| 130 | nop | ||
| 131 | |||
| 132 | swsusp_call_save: | ||
| 133 | mov r2, r15 ! restore old sp | ||
| 134 | mov r5, r8 ! restore old r8 | ||
| 135 | lds r0, pr ! restore old pr | ||
| 136 | rts | ||
| 137 | mov #0, r0 | ||
| 138 | |||
| 139 | .align 2 | ||
| 140 | 1: .long swsusp_call_save | ||
| 141 | 2: .long 0x20000000 ! RB=1 | ||
| 142 | 3: .long 0xdfffffff ! RB=0 | ||
| 143 | 4: .long swsusp_save | ||
| 144 | 5: .long swsusp_arch_regs_cpu0 | ||
| 145 | 6: .long SWSUSP_ARCH_REGS_SIZE | ||
| 146 | 7: .long save_low_regs | ||
| 147 | 8: .long save_regs | ||
diff --git a/arch/sh/kernel/cpu/sh4/Makefile b/arch/sh/kernel/cpu/sh4/Makefile index d608557c7a3f..203b18347b83 100644 --- a/arch/sh/kernel/cpu/sh4/Makefile +++ b/arch/sh/kernel/cpu/sh4/Makefile | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | obj-y := probe.o common.o | 5 | obj-y := probe.o common.o |
| 6 | common-y += $(addprefix ../sh3/, entry.o ex.o) | 6 | common-y += $(addprefix ../sh3/, entry.o ex.o) |
| 7 | 7 | ||
| 8 | obj-$(CONFIG_HIBERNATION) += $(addprefix ../sh3/, swsusp.o) | ||
| 8 | obj-$(CONFIG_SH_FPU) += fpu.o softfloat.o | 9 | obj-$(CONFIG_SH_FPU) += fpu.o softfloat.o |
| 9 | obj-$(CONFIG_SH_STORE_QUEUES) += sq.o | 10 | obj-$(CONFIG_SH_STORE_QUEUES) += sq.o |
| 10 | 11 | ||
diff --git a/arch/sh/kernel/swsusp.c b/arch/sh/kernel/swsusp.c new file mode 100644 index 000000000000..12b64a0f2f01 --- /dev/null +++ b/arch/sh/kernel/swsusp.c | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* | ||
| 2 | * swsusp.c - SuperH hibernation support | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Magnus Damm | ||
| 5 | * | ||
| 6 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 7 | * License. See the file "COPYING" in the main directory of this archive | ||
| 8 | * for more details. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/mm.h> | ||
| 12 | #include <linux/sched.h> | ||
| 13 | #include <linux/suspend.h> | ||
| 14 | #include <asm/suspend.h> | ||
| 15 | #include <asm/sections.h> | ||
| 16 | #include <asm/tlbflush.h> | ||
| 17 | #include <asm/page.h> | ||
| 18 | #include <asm/fpu.h> | ||
| 19 | |||
| 20 | struct swsusp_arch_regs swsusp_arch_regs_cpu0; | ||
| 21 | |||
| 22 | int pfn_is_nosave(unsigned long pfn) | ||
| 23 | { | ||
| 24 | unsigned long begin_pfn = __pa(&__nosave_begin) >> PAGE_SHIFT; | ||
| 25 | unsigned long end_pfn = PAGE_ALIGN(__pa(&__nosave_end)) >> PAGE_SHIFT; | ||
| 26 | |||
| 27 | return (pfn >= begin_pfn) && (pfn < end_pfn); | ||
| 28 | } | ||
| 29 | |||
| 30 | void save_processor_state(void) | ||
| 31 | { | ||
| 32 | init_fpu(current); | ||
| 33 | } | ||
| 34 | |||
| 35 | void restore_processor_state(void) | ||
| 36 | { | ||
| 37 | local_flush_tlb_all(); | ||
| 38 | } | ||
