diff -urN corig/arch/ppc64/Kconfig c/arch/ppc64/Kconfig --- corig/arch/ppc64/Kconfig 2005-07-14 16:55:40.000000000 -0500 +++ c/arch/ppc64/Kconfig 2005-07-14 15:55:52.000000000 -0500 @@ -339,6 +339,12 @@ source "drivers/pci/Kconfig" +menu "Power management options" + +source kernel/power/Kconfig + +endmenu + config HOTPLUG_CPU bool "Support for hot-pluggable CPUs" depends on SMP && EXPERIMENTAL && (PPC_PSERIES || PPC_PMAC) diff -urN corig/arch/ppc64/kernel/Makefile c/arch/ppc64/kernel/Makefile --- corig/arch/ppc64/kernel/Makefile 2005-07-14 16:55:40.000000000 -0500 +++ c/arch/ppc64/kernel/Makefile 2005-07-14 15:55:52.000000000 -0500 @@ -62,6 +62,7 @@ obj-$(CONFIG_PPC_MAPLE) += smp-tbsync.o endif +obj-$(CONFIG_SUSPEND2) += suspend2.o obj-$(CONFIG_ALTIVEC) += vecemu.o vector.o obj-$(CONFIG_KPROBES) += kprobes.o diff -urN corig/arch/ppc64/kernel/rtasd.c c/arch/ppc64/kernel/rtasd.c --- corig/arch/ppc64/kernel/rtasd.c 2005-07-14 16:55:41.000000000 -0500 +++ c/arch/ppc64/kernel/rtasd.c 2005-07-14 16:11:46.000000000 -0500 @@ -413,6 +413,8 @@ /* Drop hotplug lock, and sleep for the specified delay */ unlock_cpu_hotplug(); set_current_state(TASK_INTERRUPTIBLE); + try_to_freeze(); + schedule_timeout(delay); lock_cpu_hotplug(); diff -urN corig/arch/ppc64/kernel/setup.c c/arch/ppc64/kernel/setup.c --- corig/arch/ppc64/kernel/setup.c 2005-07-14 16:55:41.000000000 -0500 +++ c/arch/ppc64/kernel/setup.c 2005-07-14 15:55:52.000000000 -0500 @@ -700,6 +700,8 @@ EXPORT_SYMBOL(machine_halt); +void (*pm_power_off)(void) = machine_power_off; + unsigned long ppc_proc_freq; unsigned long ppc_tb_freq; diff -urN corig/arch/ppc64/kernel/signal.c c/arch/ppc64/kernel/signal.c --- corig/arch/ppc64/kernel/signal.c 2005-07-14 16:55:41.000000000 -0500 +++ c/arch/ppc64/kernel/signal.c 2005-07-14 16:09:16.000000000 -0500 @@ -534,6 +534,20 @@ int signr; struct k_sigaction ka; + if (try_to_freeze()) { + signr = 0; + if (!signal_pending(current)) + goto no_signal; + } + + if (freezing(current)) { + try_to_freeze(); + signr = 0; + recalc_sigpending(); + if (!signal_pending(current)) + goto no_signal; + } + /* * If the current thread is 32 bit - invoke the * 32 bit signal handling code @@ -552,6 +566,7 @@ return handle_signal(signr, &ka, &info, oldset, regs); } +no_signal: if (TRAP(regs) == 0x0C00) { /* System Call! */ if ((int)regs->result == -ERESTARTNOHAND || (int)regs->result == -ERESTARTSYS || diff -urN corig/arch/ppc64/kernel/suspend2.c c/arch/ppc64/kernel/suspend2.c --- corig/arch/ppc64/kernel/suspend2.c 1969-12-31 18:00:00.000000000 -0600 +++ c/arch/ppc64/kernel/suspend2.c 2005-07-14 20:01:33.000000000 -0500 @@ -0,0 +1,164 @@ +/* + * Written by Santiago Leon (santil@us.ibm.com) IBM Corp. + * based on ppc implementation by Hu Gang (hugang@soulinfo.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include + +#ifdef CONFIG_SMP +struct saved_context suspend2_saved_contexts[NR_CPUS]; +#endif +extern atomic_t suspend_cpu_counter __nosavedata; + +inline void __save_processor_state(struct saved_context *s) +{ + asm volatile ("std 1,%0" : "=m" (s->sp)); + asm volatile ("std 2,%0" : "=m" (s->r2)); + asm volatile ("std 12,%0" : "=m" (s->r[0])); + asm volatile ("std 13,%0" : "=m" (s->r[1])); + asm volatile ("std 14,%0" : "=m" (s->r[2])); + asm volatile ("std 15,%0" : "=m" (s->r[3])); + asm volatile ("std 16,%0" : "=m" (s->r[4])); + asm volatile ("std 17,%0" : "=m" (s->r[5])); + asm volatile ("std 18,%0" : "=m" (s->r[6])); + asm volatile ("std 19,%0" : "=m" (s->r[7])); + asm volatile ("std 20,%0" : "=m" (s->r[8])); + asm volatile ("std 21,%0" : "=m" (s->r[9])); + asm volatile ("std 22,%0" : "=m" (s->r[10])); + asm volatile ("std 23,%0" : "=m" (s->r[11])); + asm volatile ("std 24,%0" : "=m" (s->r[12])); + asm volatile ("std 25,%0" : "=m" (s->r[13])); + asm volatile ("std 26,%0" : "=m" (s->r[14])); + asm volatile ("std 27,%0" : "=m" (s->r[15])); + asm volatile ("std 28,%0" : "=m" (s->r[16])); + asm volatile ("std 29,%0" : "=m" (s->r[17])); + asm volatile ("std 30,%0" : "=m" (s->r[18])); + asm volatile ("std 31,%0" : "=m" (s->r[19])); + + asm volatile ("mftb 4; std 4,%0": "=m" (s->tb)); + + /* Save SPRGs */ + asm volatile ("mfsprg 4,0; std 4,%0 " : "=m" (s->sprg[0])); + asm volatile ("mfsprg 4,1; std 4,%0 " : "=m" (s->sprg[1])); + asm volatile ("mfsprg 4,2; std 4,%0 " : "=m" (s->sprg[2])); + asm volatile ("mfsprg 4,3; std 4,%0 " : "=m" (s->sprg[3])); + + /* Save MSR & SDR1 */ + asm volatile ("mfmsr 4; std 4,%0" : "=m" (s->msr)); + asm volatile ("mfsdr1 4; std 4,%0": "=m" (s->sdr1)); +} + +inline void __restore_processor_state(struct saved_context *s) +{ + /* Restore MSR and SDR1 */ + asm volatile ("ld 4,%0; mtsdr1 4" : "=m" (s->sdr1)); + asm volatile ("ld 4,%0; mtmsr 4" : "=m" (s->msr)); + + /* Restore SPRGs */ + asm volatile ("ld 4,%0; mtsprg 0,4": "=m" (s->sprg[0])); + asm volatile ("ld 4,%0; mtsprg 1,4": "=m" (s->sprg[1])); + asm volatile ("ld 4,%0; mtsprg 2,4": "=m" (s->sprg[2])); + asm volatile ("ld 4,%0; mtsprg 3,4": "=m" (s->sprg[3])); + + /* Restore TB */ + asm volatile ("li 3,0; mttbl 3; \n" + "lwz 3,%0\n; lwz 4,%1\n" + "mttbu 3; mttbl 4" : + "=m" (s->tb[0]), + "=m" (s->tb[1]) : : "r3"); + + /* Restore the callee-saved registers and return */ + asm volatile ("ld 12,%0" : "=m" (s->r[0])); + asm volatile ("ld 13,%0" : "=m" (s->r[1])); + asm volatile ("ld 14,%0" : "=m" (s->r[2])); + asm volatile ("ld 15,%0" : "=m" (s->r[3])); + asm volatile ("ld 16,%0" : "=m" (s->r[4])); + asm volatile ("ld 17,%0" : "=m" (s->r[5])); + asm volatile ("ld 18,%0" : "=m" (s->r[6])); + asm volatile ("ld 19,%0" : "=m" (s->r[7])); + asm volatile ("ld 20,%0" : "=m" (s->r[8])); + asm volatile ("ld 21,%0" : "=m" (s->r[9])); + asm volatile ("ld 22,%0" : "=m" (s->r[10])); + asm volatile ("ld 23,%0" : "=m" (s->r[11])); + asm volatile ("ld 24,%0" : "=m" (s->r[12])); + asm volatile ("ld 25,%0" : "=m" (s->r[13])); + asm volatile ("ld 26,%0" : "=m" (s->r[14])); + asm volatile ("ld 27,%0" : "=m" (s->r[15])); + asm volatile ("ld 28,%0" : "=m" (s->r[16])); + asm volatile ("ld 29,%0" : "=m" (s->r[17])); + asm volatile ("ld 30,%0" : "=m" (s->r[18])); + asm volatile ("ld 31,%0" : "=m" (s->r[19])); + + asm volatile ("ld 2,%0" : "=m" (s->r2)); + asm volatile ("ld 1,%0" : "=m" (s->sp)); +} + + + +#ifdef CONFIG_SMP + +/* + * Save and restore processor state for secondary processors. + * IRQs (and therefore preemption) are already disabled + * when we enter here (IPI). + */ +void __smp_suspend_lowlevel(void * data) +{ + + if (test_suspend_state(SUSPEND_NOW_RESUMING)) { + BUG_ON(!irqs_disabled()); + atomic_inc(&suspend_cpu_counter); + /* Only image copied back while we spin in this loop. Our + * task info should not be looked at while this is happening + * (which smp_processor_id() will do( */ + while (test_suspend_state(SUSPEND_FREEZE_SMP)) { + cpu_relax(); + barrier(); + } + + while (atomic_read(&suspend_cpu_counter) + != _smp_processor_id()) { + cpu_relax(); + barrier(); + } + + __restore_processor_state(suspend2_saved_contexts + + _smp_processor_id()); + local_flush_tlb(); + atomic_dec(&suspend_cpu_counter); + } else { /* suspending */ + BUG_ON(!irqs_disabled()); + /* + *Save context and go back to idling. + * Note that we cannot leave the processor + * here. It must be able to receive IPIs if + * the LZF compression driver (eg) does a + * vfree after compressing the kernel etc + */ + while (test_suspend_state(SUSPEND_FREEZE_SMP) && + (atomic_read(&suspend_cpu_counter) + != (_smp_processor_id() - 1))) { + cpu_relax(); + barrier(); + } + __save_processor_state(suspend2_saved_contexts + + _smp_processor_id()); + atomic_inc(&suspend_cpu_counter); + /* Now spin until the atomic copy of the kernel is made. */ + while (test_suspend_state(SUSPEND_FREEZE_SMP)) { + cpu_relax(); + barrier(); + } + atomic_dec(&suspend_cpu_counter); + } +} + +#endif diff -urN corig/arch/ppc64/kernel/vmlinux.lds.S c/arch/ppc64/kernel/vmlinux.lds.S --- corig/arch/ppc64/kernel/vmlinux.lds.S 2005-07-14 16:55:41.000000000 -0500 +++ c/arch/ppc64/kernel/vmlinux.lds.S 2005-07-14 15:55:52.000000000 -0500 @@ -101,6 +101,13 @@ /* Read/write sections */ + + . = ALIGN(4096); + __nosave_begin = .; + .data_nosave : { *(.data.nosave) } + . = ALIGN(4096); + __nosave_end = .; + . = ALIGN(16384); /* The initial task and kernel stack */ .data.init_task : { diff -urN corig/arch/ppc64/mm/init.c c/arch/ppc64/mm/init.c --- corig/arch/ppc64/mm/init.c 2005-07-14 16:55:41.000000000 -0500 +++ c/arch/ppc64/mm/init.c 2005-07-14 15:55:52.000000000 -0500 @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -461,6 +462,7 @@ addr = (unsigned long)__init_begin; for (; addr < (unsigned long)__init_end; addr += PAGE_SIZE) { ClearPageReserved(virt_to_page(addr)); + ClearPageNosave(virt_to_page(addr)); set_page_count(virt_to_page(addr), 1); free_page(addr); totalram_pages++; @@ -476,6 +478,7 @@ printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10); for (; start < end; start += PAGE_SIZE) { ClearPageReserved(virt_to_page(start)); + ClearPageNosave(virt_to_page(start)); set_page_count(virt_to_page(start), 1); free_page(start); totalram_pages++; @@ -730,8 +733,13 @@ for_each_pgdat(pgdat) { for (i = 0; i < pgdat->node_spanned_pages; i++) { page = pgdat->node_mem_map + i; + void* addr = pfn_to_kaddr(page_to_pfn(page)); if (PageReserved(page)) reservedpages++; + if (addr >= (void *)&__nosave_begin + && addr < (void *)&__nosave_end) + SetPageNosave(virt_to_page(addr)); + } } diff -urN corig/include/asm-ppc64/suspend2.h c/include/asm-ppc64/suspend2.h --- corig/include/asm-ppc64/suspend2.h 1969-12-31 18:00:00.000000000 -0600 +++ c/include/asm-ppc64/suspend2.h 2005-07-14 20:01:54.000000000 -0500 @@ -0,0 +1,91 @@ +/* + * Written by Santiago Leon (santil@us.ibm.com) IBM Corp. + * based on ppc implementation by Hu Gang (hugang@soulinfo.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +/* image of the saved processor states */ +struct saved_context { + u32 cr; + u64 lr, sp, r2; + u64 r[20]; /* r12 - r31 */ + u64 sprg[4]; + u64 msr, sdr1; + u32 tb[2]; +}; + +inline void __save_processor_state(struct saved_context *s); +inline void __restore_processor_state(struct saved_context *s); + +static struct saved_context suspend_saved_context; +static unsigned long new_stack_page; +extern atomic_t suspend_cpu_counter __nosavedata; + +static inline void suspend2_save_processor_context(void) +{ + __save_processor_state(&suspend_saved_context); +} + +static inline void suspend2_restore_processor_context(void) +{ + __restore_processor_state(&suspend_saved_context); + + enable_kernel_fp(); +} + +static inline void suspend2_pre_copy(void) +{ +} + +static inline void suspend2_post_copy(void) +{ +} + +static inline void suspend2_pre_copyback(void) +{ +} + +static inline void suspend2_post_copyback(void) +{ + /* Get other CPUs to restore their contexts and flush their tlbs. */ + clear_suspend_state(SUSPEND_FREEZE_SMP); + + do { + cpu_relax(); + barrier(); + } while (atomic_read(&suspend_cpu_counter)); + +} + +static inline void suspend2_flush_caches(void) +{ +} + + +static inline void move_stack_to_nonconflicing_area(void) +{ + unsigned long old_stack, src; + + new_stack_page = + suspend2_get_nonconflicting_pages(get_order(THREAD_SIZE)); + + BUG_ON(!new_stack_page); + + /* geting stack address */ + asm volatile ("std %%r1, %0" : "=m" (old_stack)); + + src = old_stack & (~(THREAD_SIZE - 1)); + + /* Copy stack */ + memcpy((void*)new_stack_page, (void*)src, THREAD_SIZE); + + new_stack_page += (old_stack - src); + + /* switch to new stack */ + asm volatile ("ld %%r1, %0" : "=m" (new_stack_page)); + +} diff -urN corig/include/asm-ppc64/tlbflush.h c/include/asm-ppc64/tlbflush.h --- corig/include/asm-ppc64/tlbflush.h 2005-07-14 16:55:59.000000000 -0500 +++ c/include/asm-ppc64/tlbflush.h 2005-07-14 15:55:52.000000000 -0500 @@ -39,6 +39,7 @@ put_cpu_var(ppc64_tlb_batch); } +#define local_flush_tlb() flush_tlb_pending() #define flush_tlb_mm(mm) flush_tlb_pending() #define flush_tlb_page(vma, addr) flush_tlb_pending() #define flush_tlb_page_nohash(vma, addr) do { } while (0) diff -urN corig/kernel/power/driver_model.c c/kernel/power/driver_model.c --- corig/kernel/power/driver_model.c 2005-07-14 17:02:22.000000000 -0500 +++ c/kernel/power/driver_model.c 2005-07-14 16:13:20.000000000 -0500 @@ -9,6 +9,7 @@ */ #include +#include #include "driver_model.h" #include "power_off.h"