* [PATCH] OLPC: Add XO-1 suspend/resume support
@ 2010-10-19 22:01 Daniel Drake
2010-10-19 22:06 ` Randy Dunlap
2010-10-19 22:11 ` H. Peter Anvin
0 siblings, 2 replies; 8+ messages in thread
From: Daniel Drake @ 2010-10-19 22:01 UTC (permalink / raw)
To: tglx, mingo, hpa, x86; +Cc: dilinger, linux-kernel
Add code needed for basic suspend/resume of the XO-1 laptop.
swsusp_pg_dir needs to be exposed as it is used by the assembly
code run in the wakeup path.
Signed-off-by: Daniel Drake <dsd@laptop.org>
---
arch/x86/include/asm/olpc.h | 5 +-
arch/x86/kernel/Makefile | 2 +-
arch/x86/kernel/olpc-xo1-wakeup.S | 132 +++++++++++++++++++++++++++++++++++++
arch/x86/kernel/olpc-xo1.c | 79 ++++++++++++++++++++++
arch/x86/mm/init_32.c | 6 +-
5 files changed, 219 insertions(+), 5 deletions(-)
create mode 100644 arch/x86/kernel/olpc-xo1-wakeup.S
diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h
index 101229b..54dfe92 100644
--- a/arch/x86/include/asm/olpc.h
+++ b/arch/x86/include/asm/olpc.h
@@ -88,7 +88,10 @@ extern int olpc_ec_mask_unset(uint8_t bits);
/* EC commands */
-#define EC_FIRMWARE_REV 0x08
+#define EC_FIRMWARE_REV 0x08
+#define EC_WAKE_UP_WLAN 0x24
+#define EC_SET_SCI_INHIBIT 0x32
+#define EC_SET_SCI_INHIBIT_RELEASE 0x34
/* SCI source values */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 4983b61..1b23334 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -106,7 +106,7 @@ obj-$(CONFIG_SCx200) += scx200.o
scx200-y += scx200_32.o
obj-$(CONFIG_OLPC) += olpc.o
-obj-$(CONFIG_OLPC_XO1) += olpc-xo1.o
+obj-$(CONFIG_OLPC_XO1) += olpc-xo1.o olpc-xo1-wakeup.o
obj-$(CONFIG_OLPC_OPENFIRMWARE) += olpc_ofw.o
obj-$(CONFIG_X86_MRST) += mrst.o
diff --git a/arch/x86/kernel/olpc-xo1-wakeup.S b/arch/x86/kernel/olpc-xo1-wakeup.S
new file mode 100644
index 0000000..d335074
--- /dev/null
+++ b/arch/x86/kernel/olpc-xo1-wakeup.S
@@ -0,0 +1,132 @@
+.text
+#include <linux/linkage.h>
+#include <asm/segment.h>
+#include <asm/page.h>
+
+ .macro writepost,value
+ movb $0x34, %al
+ outb %al, $0x70
+ movb $\value, %al
+ outb %al, $0x71
+ .endm
+
+ALIGN
+ .align 4096
+
+wakeup_start:
+# jmp wakeup_start
+
+ cli
+ cld
+
+ # Clear any dangerous flags
+
+ pushl $0
+ popfl
+
+ writepost 0x31
+
+ # Set up %cr3
+ movl $swsusp_pg_dir - __PAGE_OFFSET, %eax
+ movl %eax, %cr3
+
+ movl saved_cr4, %eax
+ movl %eax, %cr4
+
+ movl saved_cr0, %eax
+ movl %eax, %cr0
+
+ jmp 1f
+1:
+ ljmpl $__KERNEL_CS,$wakeup_return
+
+
+.org 0x1000
+
+wakeup_return:
+ movw $__KERNEL_DS, %ax
+ movw %ax, %ss
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+
+ lgdt saved_gdt
+ lidt saved_idt
+ lldt saved_ldt
+ ljmp $(__KERNEL_CS),$1f
+1:
+ movl %cr3, %eax
+ movl %eax, %cr3
+ wbinvd
+
+ # Go back to the return point
+ jmp ret_point
+
+save_registers:
+ sgdt saved_gdt
+ sidt saved_idt
+ sldt saved_ldt
+
+ pushl %edx
+ movl %cr4, %edx
+ movl %edx, saved_cr4
+
+ movl %cr0, %edx
+ movl %edx, saved_cr0
+
+ popl %edx
+
+ movl %ebx, saved_context_ebx
+ movl %ebp, saved_context_ebp
+ movl %esi, saved_context_esi
+ movl %edi, saved_context_edi
+
+ pushfl
+ popl saved_context_eflags
+
+ ret
+
+
+restore_registers:
+ movl saved_context_ebp, %ebp
+ movl saved_context_ebx, %ebx
+ movl saved_context_esi, %esi
+ movl saved_context_edi, %edi
+
+ pushl saved_context_eflags
+ popfl
+
+ ret
+
+
+ENTRY(do_olpc_suspend_lowlevel)
+ call save_processor_state
+ call save_registers
+
+ # This is the stack context we want to remember
+ movl %esp, saved_context_esp
+
+ pushl $3
+ call olpc_xo1_do_sleep
+
+ jmp wakeup_start
+ .p2align 4,,7
+ret_point:
+ movl saved_context_esp, %esp
+
+ writepost 0x32
+
+ call restore_registers
+ call restore_processor_state
+ ret
+
+.data
+ALIGN
+
+saved_gdt: .long 0,0
+saved_idt: .long 0,0
+saved_ldt: .long 0
+saved_cr4: .long 0
+saved_cr0: .long 0
+
diff --git a/arch/x86/kernel/olpc-xo1.c b/arch/x86/kernel/olpc-xo1.c
index f5442c0..9a06081 100644
--- a/arch/x86/kernel/olpc-xo1.c
+++ b/arch/x86/kernel/olpc-xo1.c
@@ -16,6 +16,7 @@
#include <linux/pci_ids.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
+#include <linux/suspend.h>
#include <asm/io.h>
#include <asm/olpc.h>
@@ -33,12 +34,83 @@
#define PM_SSC 0x54
/* PM registers (ACPI block) */
+#define PM1_STS 0x00
#define PM1_CNT 0x08
#define PM_GPE0_STS 0x18
+#define CS5536_PM_PWRBTN (1 << 8)
+
+extern void do_olpc_suspend_lowlevel(void);
+
static unsigned long acpi_base;
static unsigned long pms_base;
+static struct {
+ unsigned long address;
+ unsigned short segment;
+} ofw_bios_entry = { 0xF0000 + PAGE_OFFSET, __KERNEL_CS };
+
+static int xo1_power_state_enter(suspend_state_t pm_state)
+{
+ int r;
+
+ /* Only STR is supported */
+ if (pm_state != PM_SUSPEND_MEM)
+ return -EINVAL;
+
+ r = olpc_ec_cmd(EC_SET_SCI_INHIBIT, NULL, 0, NULL, 0);
+ if (r)
+ return r;
+
+ /* Save CPU state */
+ do_olpc_suspend_lowlevel();
+
+ /* Resume path starts here */
+
+ /* Tell the EC to stop inhibiting SCIs */
+ olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0);
+
+ /*
+ * Tell the wireless module to restart USB communication.
+ * Must be done twice.
+ */
+ olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
+ olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
+
+ return 0;
+}
+
+asmlinkage int olpc_xo1_do_sleep(u8 sleep_state)
+{
+ void *pgd_addr = __va(read_cr3());
+ printk(KERN_ERR "xo1_do_sleep!\n"); /* this needs to remain here so
+ * that gcc doesn't optimize
+ * away our __va! */
+
+ /* Enable wakeup through power button */
+ outl((CS5536_PM_PWRBTN << 16) | 0xFFFF, acpi_base + PM1_STS);
+
+ __asm__ __volatile__("movl %0,%%eax" : : "r" (pgd_addr));
+ __asm__("call *(%%edi); cld"
+ : : "D" (&ofw_bios_entry));
+ __asm__ __volatile__("movb $0x34, %al\n\t"
+ "outb %al, $0x70\n\t"
+ "movb $0x30, %al\n\t"
+ "outb %al, $0x71\n\t");
+ return 0;
+}
+
+static int xo1_power_state_valid(suspend_state_t pm_state)
+{
+ /* suspend-to-RAM only */
+ return pm_state == PM_SUSPEND_MEM;
+}
+
+static struct platform_suspend_ops xo1_suspend_ops = {
+ .valid = xo1_power_state_valid,
+ .enter = xo1_power_state_enter,
+};
+
static void xo1_power_off(void)
{
printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
@@ -101,6 +173,13 @@ static int __devinit olpc_xo1_probe(struct platform_device *pdev)
if (r)
return r;
+ /*
+ * Take a reference on ourself to prevent module unloading. We can't
+ * safely unload after changing the suspend handlers.
+ */
+ __module_get(THIS_MODULE);
+
+ suspend_set_ops(&xo1_suspend_ops);
pm_power_off = xo1_power_off;
printk(KERN_INFO "OLPC XO-1 support registered\n");
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c
index 43bbc29..dbdab67 100644
--- a/arch/x86/mm/init_32.c
+++ b/arch/x86/mm/init_32.c
@@ -549,7 +549,7 @@ static void __init pagetable_init(void)
permanent_kmaps_init(pgd_base);
}
-#ifdef CONFIG_ACPI_SLEEP
+#if defined(CONFIG_ACPI_SLEEP) || defined(CONFIG_OLPC_XO1)
/*
* ACPI suspend needs this for resume, because things like the intel-agp
* driver might have split up a kernel 4MB mapping.
@@ -561,11 +561,11 @@ static inline void save_pg_dir(void)
{
copy_page(swsusp_pg_dir, swapper_pg_dir);
}
-#else /* !CONFIG_ACPI_SLEEP */
+#else /* !CONFIG_ACPI_SLEEP && !CONFIG_OLPC_XO1 */
static inline void save_pg_dir(void)
{
}
-#endif /* !CONFIG_ACPI_SLEEP */
+#endif /* !CONFIG_ACPI_SLEEP && !CONFIG_OLPC_XO1 */
void zap_low_mappings(bool early)
{
--
1.7.2.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] OLPC: Add XO-1 suspend/resume support
2010-10-19 22:01 [PATCH] OLPC: Add XO-1 suspend/resume support Daniel Drake
@ 2010-10-19 22:06 ` Randy Dunlap
2010-10-19 22:11 ` H. Peter Anvin
1 sibling, 0 replies; 8+ messages in thread
From: Randy Dunlap @ 2010-10-19 22:06 UTC (permalink / raw)
To: Daniel Drake; +Cc: tglx, mingo, hpa, x86, dilinger, linux-kernel
On Tue, 19 Oct 2010 23:01:58 +0100 (BST) Daniel Drake wrote:
> Add code needed for basic suspend/resume of the XO-1 laptop.
>
> swsusp_pg_dir needs to be exposed as it is used by the assembly
> code run in the wakeup path.
>
> Signed-off-by: Daniel Drake <dsd@laptop.org>
> ---
> arch/x86/include/asm/olpc.h | 5 +-
> arch/x86/kernel/Makefile | 2 +-
> arch/x86/kernel/olpc-xo1-wakeup.S | 132 +++++++++++++++++++++++++++++++++++++
> arch/x86/kernel/olpc-xo1.c | 79 ++++++++++++++++++++++
> arch/x86/mm/init_32.c | 6 +-
> 5 files changed, 219 insertions(+), 5 deletions(-)
> create mode 100644 arch/x86/kernel/olpc-xo1-wakeup.S
>
> diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h
> index 101229b..54dfe92 100644
> --- a/arch/x86/include/asm/olpc.h
> +++ b/arch/x86/include/asm/olpc.h
> @@ -88,7 +88,10 @@ extern int olpc_ec_mask_unset(uint8_t bits);
>
> /* EC commands */
>
> -#define EC_FIRMWARE_REV 0x08
> +#define EC_FIRMWARE_REV 0x08
> +#define EC_WAKE_UP_WLAN 0x24
> +#define EC_SET_SCI_INHIBIT 0x32
> +#define EC_SET_SCI_INHIBIT_RELEASE 0x34
>
> /* SCI source values */
>
> diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
> index 4983b61..1b23334 100644
> --- a/arch/x86/kernel/Makefile
> +++ b/arch/x86/kernel/Makefile
> @@ -106,7 +106,7 @@ obj-$(CONFIG_SCx200) += scx200.o
> scx200-y += scx200_32.o
>
> obj-$(CONFIG_OLPC) += olpc.o
> -obj-$(CONFIG_OLPC_XO1) += olpc-xo1.o
> +obj-$(CONFIG_OLPC_XO1) += olpc-xo1.o olpc-xo1-wakeup.o
> obj-$(CONFIG_OLPC_OPENFIRMWARE) += olpc_ofw.o
> obj-$(CONFIG_X86_MRST) += mrst.o
>
Hi,
Does this build OK when CONFIG_PM is not enabled?
or is that config not possible?
> diff --git a/arch/x86/kernel/olpc-xo1-wakeup.S b/arch/x86/kernel/olpc-xo1-wakeup.S
> new file mode 100644
> index 0000000..d335074
> --- /dev/null
> +++ b/arch/x86/kernel/olpc-xo1-wakeup.S
> @@ -0,0 +1,132 @@
> +.text
> +#include <linux/linkage.h>
> +#include <asm/segment.h>
> +#include <asm/page.h>
> +
> + .macro writepost,value
> + movb $0x34, %al
> + outb %al, $0x70
> + movb $\value, %al
> + outb %al, $0x71
> + .endm
> +
> +ALIGN
> + .align 4096
> +
> +wakeup_start:
> +# jmp wakeup_start
> +
> + cli
> + cld
> +
> + # Clear any dangerous flags
> +
> + pushl $0
> + popfl
> +
> + writepost 0x31
> +
> + # Set up %cr3
> + movl $swsusp_pg_dir - __PAGE_OFFSET, %eax
> + movl %eax, %cr3
> +
> + movl saved_cr4, %eax
> + movl %eax, %cr4
> +
> + movl saved_cr0, %eax
> + movl %eax, %cr0
> +
> + jmp 1f
> +1:
> + ljmpl $__KERNEL_CS,$wakeup_return
> +
> +
> +.org 0x1000
> +
> +wakeup_return:
> + movw $__KERNEL_DS, %ax
> + movw %ax, %ss
> + movw %ax, %ds
> + movw %ax, %es
> + movw %ax, %fs
> + movw %ax, %gs
> +
> + lgdt saved_gdt
> + lidt saved_idt
> + lldt saved_ldt
> + ljmp $(__KERNEL_CS),$1f
> +1:
> + movl %cr3, %eax
> + movl %eax, %cr3
> + wbinvd
> +
> + # Go back to the return point
> + jmp ret_point
> +
> +save_registers:
> + sgdt saved_gdt
> + sidt saved_idt
> + sldt saved_ldt
> +
> + pushl %edx
> + movl %cr4, %edx
> + movl %edx, saved_cr4
> +
> + movl %cr0, %edx
> + movl %edx, saved_cr0
> +
> + popl %edx
> +
> + movl %ebx, saved_context_ebx
> + movl %ebp, saved_context_ebp
> + movl %esi, saved_context_esi
> + movl %edi, saved_context_edi
> +
> + pushfl
> + popl saved_context_eflags
> +
> + ret
> +
> +
> +restore_registers:
> + movl saved_context_ebp, %ebp
> + movl saved_context_ebx, %ebx
> + movl saved_context_esi, %esi
> + movl saved_context_edi, %edi
> +
> + pushl saved_context_eflags
> + popfl
> +
> + ret
> +
> +
> +ENTRY(do_olpc_suspend_lowlevel)
> + call save_processor_state
> + call save_registers
> +
> + # This is the stack context we want to remember
> + movl %esp, saved_context_esp
> +
> + pushl $3
> + call olpc_xo1_do_sleep
> +
> + jmp wakeup_start
> + .p2align 4,,7
> +ret_point:
> + movl saved_context_esp, %esp
> +
> + writepost 0x32
> +
> + call restore_registers
> + call restore_processor_state
> + ret
> +
> +.data
> +ALIGN
> +
> +saved_gdt: .long 0,0
> +saved_idt: .long 0,0
> +saved_ldt: .long 0
> +saved_cr4: .long 0
> +saved_cr0: .long 0
> +
> diff --git a/arch/x86/kernel/olpc-xo1.c b/arch/x86/kernel/olpc-xo1.c
> index f5442c0..9a06081 100644
> --- a/arch/x86/kernel/olpc-xo1.c
> +++ b/arch/x86/kernel/olpc-xo1.c
> @@ -16,6 +16,7 @@
> #include <linux/pci_ids.h>
> #include <linux/platform_device.h>
> #include <linux/pm.h>
> +#include <linux/suspend.h>
>
> #include <asm/io.h>
> #include <asm/olpc.h>
> @@ -33,12 +34,83 @@
> #define PM_SSC 0x54
>
> /* PM registers (ACPI block) */
> +#define PM1_STS 0x00
> #define PM1_CNT 0x08
> #define PM_GPE0_STS 0x18
>
> +#define CS5536_PM_PWRBTN (1 << 8)
> +
> +extern void do_olpc_suspend_lowlevel(void);
> +
> static unsigned long acpi_base;
> static unsigned long pms_base;
>
> +static struct {
> + unsigned long address;
> + unsigned short segment;
> +} ofw_bios_entry = { 0xF0000 + PAGE_OFFSET, __KERNEL_CS };
> +
> +static int xo1_power_state_enter(suspend_state_t pm_state)
> +{
> + int r;
> +
> + /* Only STR is supported */
> + if (pm_state != PM_SUSPEND_MEM)
> + return -EINVAL;
> +
> + r = olpc_ec_cmd(EC_SET_SCI_INHIBIT, NULL, 0, NULL, 0);
> + if (r)
> + return r;
> +
> + /* Save CPU state */
> + do_olpc_suspend_lowlevel();
> +
> + /* Resume path starts here */
> +
> + /* Tell the EC to stop inhibiting SCIs */
> + olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0);
> +
> + /*
> + * Tell the wireless module to restart USB communication.
> + * Must be done twice.
> + */
> + olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
> + olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
> +
> + return 0;
> +}
> +
> +asmlinkage int olpc_xo1_do_sleep(u8 sleep_state)
> +{
> + void *pgd_addr = __va(read_cr3());
> + printk(KERN_ERR "xo1_do_sleep!\n"); /* this needs to remain here so
> + * that gcc doesn't optimize
> + * away our __va! */
> +
> + /* Enable wakeup through power button */
> + outl((CS5536_PM_PWRBTN << 16) | 0xFFFF, acpi_base + PM1_STS);
> +
> + __asm__ __volatile__("movl %0,%%eax" : : "r" (pgd_addr));
> + __asm__("call *(%%edi); cld"
> + : : "D" (&ofw_bios_entry));
> + __asm__ __volatile__("movb $0x34, %al\n\t"
> + "outb %al, $0x70\n\t"
> + "movb $0x30, %al\n\t"
> + "outb %al, $0x71\n\t");
> + return 0;
> +}
> +
> +static int xo1_power_state_valid(suspend_state_t pm_state)
> +{
> + /* suspend-to-RAM only */
> + return pm_state == PM_SUSPEND_MEM;
> +}
> +
> +static struct platform_suspend_ops xo1_suspend_ops = {
> + .valid = xo1_power_state_valid,
> + .enter = xo1_power_state_enter,
> +};
> +
> static void xo1_power_off(void)
> {
> printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
> @@ -101,6 +173,13 @@ static int __devinit olpc_xo1_probe(struct platform_device *pdev)
> if (r)
> return r;
>
> + /*
> + * Take a reference on ourself to prevent module unloading. We can't
> + * safely unload after changing the suspend handlers.
> + */
> + __module_get(THIS_MODULE);
> +
> + suspend_set_ops(&xo1_suspend_ops);
> pm_power_off = xo1_power_off;
>
> printk(KERN_INFO "OLPC XO-1 support registered\n");
> diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c
> index 43bbc29..dbdab67 100644
> --- a/arch/x86/mm/init_32.c
> +++ b/arch/x86/mm/init_32.c
> @@ -549,7 +549,7 @@ static void __init pagetable_init(void)
> permanent_kmaps_init(pgd_base);
> }
>
> -#ifdef CONFIG_ACPI_SLEEP
> +#if defined(CONFIG_ACPI_SLEEP) || defined(CONFIG_OLPC_XO1)
> /*
> * ACPI suspend needs this for resume, because things like the intel-agp
> * driver might have split up a kernel 4MB mapping.
> @@ -561,11 +561,11 @@ static inline void save_pg_dir(void)
> {
> copy_page(swsusp_pg_dir, swapper_pg_dir);
> }
> -#else /* !CONFIG_ACPI_SLEEP */
> +#else /* !CONFIG_ACPI_SLEEP && !CONFIG_OLPC_XO1 */
> static inline void save_pg_dir(void)
> {
> }
> -#endif /* !CONFIG_ACPI_SLEEP */
> +#endif /* !CONFIG_ACPI_SLEEP && !CONFIG_OLPC_XO1 */
>
> void zap_low_mappings(bool early)
> {
> --
---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] OLPC: Add XO-1 suspend/resume support
2010-10-19 22:01 [PATCH] OLPC: Add XO-1 suspend/resume support Daniel Drake
2010-10-19 22:06 ` Randy Dunlap
@ 2010-10-19 22:11 ` H. Peter Anvin
2010-10-20 5:47 ` Borislav Petkov
1 sibling, 1 reply; 8+ messages in thread
From: H. Peter Anvin @ 2010-10-19 22:11 UTC (permalink / raw)
To: Daniel Drake
Cc: tglx, mingo, x86, dilinger, linux-kernel, Borislav Petkov,
Roedel, Joerg
On 10/19/2010 03:01 PM, Daniel Drake wrote:
> Add code needed for basic suspend/resume of the XO-1 laptop.
>
> swsusp_pg_dir needs to be exposed as it is used by the assembly
> code run in the wakeup path.
>
Okay... this is Yet Another Reason why we need to unify all the bloody
trampoline page tables.
What it comes down to is that there are several users which need a 1:1
mapped pagetable and a chunk of memory < 1 MiB, and they should all be
combined and linked together, and share a single pagetable set that
sticks around.
Borislav has already been doing some of this work:
[PATCH] x86-32, mm: Add an initial page table for core bootstrapping
I thought that patch was already in -tip, but it looks like it's not...
Borislav/Joerg... was there a newer patch or did we just miss the final
version? I remember we talked about this at some length, but it looks
like I dropped the ball (I was on vacation when the above message was
posted.)
-hpa
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] OLPC: Add XO-1 suspend/resume support
2010-10-19 22:11 ` H. Peter Anvin
@ 2010-10-20 5:47 ` Borislav Petkov
2010-10-20 5:58 ` H. Peter Anvin
0 siblings, 1 reply; 8+ messages in thread
From: Borislav Petkov @ 2010-10-20 5:47 UTC (permalink / raw)
To: H. Peter Anvin
Cc: Daniel Drake, tglx@linutronix.de, mingo@redhat.com,
x86@kernel.org, dilinger@queued.net, linux-kernel@vger.kernel.org,
Borislav Petkov, Roedel, Joerg
From: "H. Peter Anvin" <hpa@zytor.com>
Date: Tue, Oct 19, 2010 at 06:11:13PM -0400
> On 10/19/2010 03:01 PM, Daniel Drake wrote:
> > Add code needed for basic suspend/resume of the XO-1 laptop.
> >
> > swsusp_pg_dir needs to be exposed as it is used by the assembly
> > code run in the wakeup path.
> >
>
> Okay... this is Yet Another Reason why we need to unify all the bloody
> trampoline page tables.
>
> What it comes down to is that there are several users which need a 1:1
> mapped pagetable and a chunk of memory < 1 MiB, and they should all be
> combined and linked together, and share a single pagetable set that
> sticks around.
>
> Borislav has already been doing some of this work:
>
> [PATCH] x86-32, mm: Add an initial page table for core bootstrapping
>
> I thought that patch was already in -tip, but it looks like it's not...
> Borislav/Joerg... was there a newer patch or did we just miss the final
> version? I remember we talked about this at some length, but it looks
> like I dropped the ball (I was on vacation when the above message was
> posted.)
The final version with fixed ACPI sleep is this one:
http://www.gossamer-threads.com/lists/linux/kernel/1270589
I ran it for a couple of weeks and it didn't show any regressions. If
you want to get it into the merge window, I can get you an updated
version against a -tip branch of your liking ASAP.
--
Regards/Gruss,
Boris.
Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
General Managers: Alberto Bozzo, Andrew Bowd
Registration: Dornach, Gemeinde Aschheim, Landkreis Muenchen
Registergericht Muenchen, HRB Nr. 43632
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] OLPC: Add XO-1 suspend/resume support
2010-10-20 5:47 ` Borislav Petkov
@ 2010-10-20 5:58 ` H. Peter Anvin
2010-10-20 7:05 ` [PATCH -tip/master] x86-32, mm: Add an initial page table for core bootstrapping Borislav Petkov
0 siblings, 1 reply; 8+ messages in thread
From: H. Peter Anvin @ 2010-10-20 5:58 UTC (permalink / raw)
To: Borislav Petkov
Cc: Daniel Drake, tglx@linutronix.de, mingo@redhat.com,
x86@kernel.org, dilinger@queued.net, linux-kernel@vger.kernel.org,
Roedel, Joerg
On 10/19/2010 10:47 PM, Borislav Petkov wrote:
>
> The final version with fixed ACPI sleep is this one:
> http://www.gossamer-threads.com/lists/linux/kernel/1270589
>
> I ran it for a couple of weeks and it didn't show any regressions. If
> you want to get it into the merge window, I can get you an updated
> version against a -tip branch of your liking ASAP.
>
If you could prep something against tip:master we *might* be able to get
it in... not sure yet (and it partly depends on when Linus decides to
push the button, of course.)
-hpa
--
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel. I don't speak on their behalf.
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH -tip/master] x86-32, mm: Add an initial page table for core bootstrapping
2010-10-20 5:58 ` H. Peter Anvin
@ 2010-10-20 7:05 ` Borislav Petkov
2010-10-20 21:28 ` [tip:x86/trampoline] " tip-bot for Borislav Petkov
0 siblings, 1 reply; 8+ messages in thread
From: Borislav Petkov @ 2010-10-20 7:05 UTC (permalink / raw)
To: H. Peter Anvin
Cc: Borislav Petkov, Daniel Drake, tglx@linutronix.de,
mingo@redhat.com, x86@kernel.org, dilinger@queued.net,
linux-kernel@vger.kernel.org, Roedel, Joerg
From: "H. Peter Anvin" <hpa@zytor.com>
Date: Tue, Oct 19, 2010 at 10:58:30PM -0700
> If you could prep something against tip:master we *might* be able to get
> it in... not sure yet (and it partly depends on when Linus decides to
> push the button, of course.)
Here you go, suspend-to-{ram,disk} works, will run it in the next couple
of days just in case.
--
From: Borislav Petkov <bp@alien8.de>
Date: Sat, 28 Aug 2010 15:58:33 +0200
Subject: [PATCH -tip/master] x86-32, mm: Add an initial page table for core bootstrapping
This patch adds an initial page table with low mappings used exclusively
for booting APs/resuming after ACPI suspend/machine restart. After this,
there's no need to add low mappings to swapper_pg_dir and zap them later
or create own swsusp PGD page solely for ACPI sleep needs - we have
initial_page_table for that.
[ v1.1 Fix suspend-to-ram ]
Signed-off-by: Borislav Petkov <bp@alien8.de>
---
arch/x86/include/asm/pgtable_32.h | 2 +-
arch/x86/include/asm/tlbflush.h | 2 -
arch/x86/include/asm/trampoline.h | 3 --
arch/x86/kernel/acpi/sleep.c | 7 ++++-
arch/x86/kernel/head32.c | 1 +
arch/x86/kernel/head_32.S | 55 +++++++++++++++++--------------------
arch/x86/kernel/reboot.c | 10 +-----
arch/x86/kernel/setup.c | 18 +++++++++++-
arch/x86/kernel/smpboot.c | 16 +++--------
arch/x86/kernel/trampoline.c | 16 -----------
arch/x86/mm/init_32.c | 45 ------------------------------
11 files changed, 56 insertions(+), 119 deletions(-)
diff --git a/arch/x86/include/asm/pgtable_32.h b/arch/x86/include/asm/pgtable_32.h
index f686f49..8abde9e 100644
--- a/arch/x86/include/asm/pgtable_32.h
+++ b/arch/x86/include/asm/pgtable_32.h
@@ -26,7 +26,7 @@ struct mm_struct;
struct vm_area_struct;
extern pgd_t swapper_pg_dir[1024];
-extern pgd_t trampoline_pg_dir[1024];
+extern pgd_t initial_page_table[1024];
static inline void pgtable_cache_init(void) { }
static inline void check_pgt_cache(void) { }
diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
index 7f3eba0..169be89 100644
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -172,6 +172,4 @@ static inline void flush_tlb_kernel_range(unsigned long start,
flush_tlb_all();
}
-extern void zap_low_mappings(bool early);
-
#endif /* _ASM_X86_TLBFLUSH_H */
diff --git a/arch/x86/include/asm/trampoline.h b/arch/x86/include/asm/trampoline.h
index 4dde797..f4500fb 100644
--- a/arch/x86/include/asm/trampoline.h
+++ b/arch/x86/include/asm/trampoline.h
@@ -13,16 +13,13 @@ extern unsigned char *trampoline_base;
extern unsigned long init_rsp;
extern unsigned long initial_code;
-extern unsigned long initial_page_table;
extern unsigned long initial_gs;
#define TRAMPOLINE_SIZE roundup(trampoline_end - trampoline_data, PAGE_SIZE)
extern unsigned long setup_trampoline(void);
-extern void __init setup_trampoline_page_table(void);
extern void __init reserve_trampoline_memory(void);
#else
-static inline void setup_trampoline_page_table(void) {}
static inline void reserve_trampoline_memory(void) {}
#endif /* CONFIG_X86_TRAMPOLINE */
diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c
index e125207..74a8478 100644
--- a/arch/x86/kernel/acpi/sleep.c
+++ b/arch/x86/kernel/acpi/sleep.c
@@ -13,6 +13,11 @@
#include <asm/segment.h>
#include <asm/desc.h>
+#ifdef CONFIG_X86_32
+#include <asm/pgtable.h>
+#include <asm/pgtable_32.h>
+#endif
+
#include "realmode/wakeup.h"
#include "sleep.h"
@@ -91,7 +96,7 @@ int acpi_save_state_mem(void)
#ifndef CONFIG_64BIT
header->pmode_entry = (u32)&wakeup_pmode_return;
- header->pmode_cr3 = (u32)(swsusp_pg_dir - __PAGE_OFFSET);
+ header->pmode_cr3 = (u32)__pa(&initial_page_table);
saved_magic = 0x12345678;
#else /* CONFIG_64BIT */
header->trampoline_segment = setup_trampoline() >> 4;
diff --git a/arch/x86/kernel/head32.c b/arch/x86/kernel/head32.c
index 9a6ca23..7633101 100644
--- a/arch/x86/kernel/head32.c
+++ b/arch/x86/kernel/head32.c
@@ -18,6 +18,7 @@
#include <asm/apic.h>
#include <asm/io_apic.h>
#include <asm/bios_ebda.h>
+#include <asm/tlbflush.h>
static void __init i386_default_early_setup(void)
{
diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S
index fa8c1b8..bcece91 100644
--- a/arch/x86/kernel/head_32.S
+++ b/arch/x86/kernel/head_32.S
@@ -183,13 +183,12 @@ default_entry:
#ifdef CONFIG_X86_PAE
/*
- * In PAE mode swapper_pg_dir is statically defined to contain enough
- * entries to cover the VMSPLIT option (that is the top 1, 2 or 3
- * entries). The identity mapping is handled by pointing two PGD
- * entries to the first kernel PMD.
+ * In PAE mode initial_page_table is statically defined to contain
+ * enough entries to cover the VMSPLIT option (that is the top 1, 2 or 3
+ * entries). The identity mapping is handled by pointing two PGD entries
+ * to the first kernel PMD.
*
- * Note the upper half of each PMD or PTE are always zero at
- * this stage.
+ * Note the upper half of each PMD or PTE are always zero at this stage.
*/
#define KPMDS (((-__PAGE_OFFSET) >> 30) & 3) /* Number of kernel PMDs */
@@ -197,7 +196,7 @@ default_entry:
xorl %ebx,%ebx /* %ebx is kept at zero */
movl $pa(__brk_base), %edi
- movl $pa(swapper_pg_pmd), %edx
+ movl $pa(initial_pg_pmd), %edx
movl $PTE_IDENT_ATTR, %eax
10:
leal PDE_IDENT_ATTR(%edi),%ecx /* Create PMD entry */
@@ -226,14 +225,14 @@ default_entry:
movl %eax, pa(max_pfn_mapped)
/* Do early initialization of the fixmap area */
- movl $pa(swapper_pg_fixmap)+PDE_IDENT_ATTR,%eax
- movl %eax,pa(swapper_pg_pmd+0x1000*KPMDS-8)
+ movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eax
+ movl %eax,pa(initial_pg_pmd+0x1000*KPMDS-8)
#else /* Not PAE */
page_pde_offset = (__PAGE_OFFSET >> 20);
movl $pa(__brk_base), %edi
- movl $pa(swapper_pg_dir), %edx
+ movl $pa(initial_page_table), %edx
movl $PTE_IDENT_ATTR, %eax
10:
leal PDE_IDENT_ATTR(%edi),%ecx /* Create PDE entry */
@@ -257,8 +256,8 @@ page_pde_offset = (__PAGE_OFFSET >> 20);
movl %eax, pa(max_pfn_mapped)
/* Do early initialization of the fixmap area */
- movl $pa(swapper_pg_fixmap)+PDE_IDENT_ATTR,%eax
- movl %eax,pa(swapper_pg_dir+0xffc)
+ movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eax
+ movl %eax,pa(initial_page_table+0xffc)
#endif
jmp 3f
/*
@@ -334,7 +333,7 @@ ENTRY(startup_32_smp)
/*
* Enable paging
*/
- movl pa(initial_page_table), %eax
+ movl $pa(initial_page_table), %eax
movl %eax,%cr3 /* set the page table pointer.. */
movl %cr0,%eax
orl $X86_CR0_PG,%eax
@@ -614,8 +613,6 @@ ignore_int:
.align 4
ENTRY(initial_code)
.long i386_start_kernel
-ENTRY(initial_page_table)
- .long pa(swapper_pg_dir)
/*
* BSS section
@@ -623,20 +620,18 @@ ENTRY(initial_page_table)
__PAGE_ALIGNED_BSS
.align PAGE_SIZE_asm
#ifdef CONFIG_X86_PAE
-swapper_pg_pmd:
+initial_pg_pmd:
.fill 1024*KPMDS,4,0
#else
-ENTRY(swapper_pg_dir)
+ENTRY(initial_page_table)
.fill 1024,4,0
#endif
-swapper_pg_fixmap:
+initial_pg_fixmap:
.fill 1024,4,0
-#ifdef CONFIG_X86_TRAMPOLINE
-ENTRY(trampoline_pg_dir)
- .fill 1024,4,0
-#endif
ENTRY(empty_zero_page)
.fill 4096,1,0
+ENTRY(swapper_pg_dir)
+ .fill 1024,4,0
/*
* This starts the data section.
@@ -645,20 +640,20 @@ ENTRY(empty_zero_page)
__PAGE_ALIGNED_DATA
/* Page-aligned for the benefit of paravirt? */
.align PAGE_SIZE_asm
-ENTRY(swapper_pg_dir)
- .long pa(swapper_pg_pmd+PGD_IDENT_ATTR),0 /* low identity map */
+ENTRY(initial_page_table)
+ .long pa(initial_pg_pmd+PGD_IDENT_ATTR),0 /* low identity map */
# if KPMDS == 3
- .long pa(swapper_pg_pmd+PGD_IDENT_ATTR),0
- .long pa(swapper_pg_pmd+PGD_IDENT_ATTR+0x1000),0
- .long pa(swapper_pg_pmd+PGD_IDENT_ATTR+0x2000),0
+ .long pa(initial_pg_pmd+PGD_IDENT_ATTR),0
+ .long pa(initial_pg_pmd+PGD_IDENT_ATTR+0x1000),0
+ .long pa(initial_pg_pmd+PGD_IDENT_ATTR+0x2000),0
# elif KPMDS == 2
.long 0,0
- .long pa(swapper_pg_pmd+PGD_IDENT_ATTR),0
- .long pa(swapper_pg_pmd+PGD_IDENT_ATTR+0x1000),0
+ .long pa(initial_pg_pmd+PGD_IDENT_ATTR),0
+ .long pa(initial_pg_pmd+PGD_IDENT_ATTR+0x1000),0
# elif KPMDS == 1
.long 0,0
.long 0,0
- .long pa(swapper_pg_pmd+PGD_IDENT_ATTR),0
+ .long pa(initial_pg_pmd+PGD_IDENT_ATTR),0
# else
# error "Kernel PMDs should be 1, 2 or 3"
# endif
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index 7a4cf14..f7f53dc 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -371,16 +371,10 @@ void machine_real_restart(const unsigned char *code, int length)
CMOS_WRITE(0x00, 0x8f);
spin_unlock(&rtc_lock);
- /* Remap the kernel at virtual address zero, as well as offset zero
- from the kernel segment. This assumes the kernel segment starts at
- virtual address PAGE_OFFSET. */
- memcpy(swapper_pg_dir, swapper_pg_dir + KERNEL_PGD_BOUNDARY,
- sizeof(swapper_pg_dir [0]) * KERNEL_PGD_PTRS);
-
/*
- * Use `swapper_pg_dir' as our page directory.
+ * Switch back to the initial page table.
*/
- load_cr3(swapper_pg_dir);
+ load_cr3(initial_page_table);
/* Write 0x1234 to absolute memory location 0x472. The BIOS reads
this on booting to tell it to "Bypass memory test (also warm
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 44e208e..3df995f 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -700,6 +700,17 @@ void __init setup_arch(char **cmdline_p)
#ifdef CONFIG_X86_32
memcpy(&boot_cpu_data, &new_cpu_data, sizeof(new_cpu_data));
visws_early_detect();
+
+ /*
+ * copy kernel address range established so far and switch
+ * to the proper swapper page table
+ */
+ clone_pgd_range(swapper_pg_dir + KERNEL_PGD_BOUNDARY,
+ initial_page_table + KERNEL_PGD_BOUNDARY,
+ KERNEL_PGD_PTRS);
+
+ load_cr3(swapper_pg_dir);
+ __flush_tlb_all();
#else
printk(KERN_INFO "Command line: %s\n", boot_command_line);
#endif
@@ -985,7 +996,12 @@ void __init setup_arch(char **cmdline_p)
paging_init();
x86_init.paging.pagetable_setup_done(swapper_pg_dir);
- setup_trampoline_page_table();
+#ifdef CONFIG_X86_32
+ /* sync back kernel address range */
+ clone_pgd_range(initial_page_table + KERNEL_PGD_BOUNDARY,
+ swapper_pg_dir + KERNEL_PGD_BOUNDARY,
+ KERNEL_PGD_PTRS);
+#endif
tboot_probe();
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index dfb5089..6af1185 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -299,22 +299,16 @@ notrace static void __cpuinit start_secondary(void *unused)
* fragile that we want to limit the things done here to the
* most necessary things.
*/
+ cpu_init();
+ preempt_disable();
+ smp_callin();
#ifdef CONFIG_X86_32
- /*
- * Switch away from the trampoline page-table
- *
- * Do this before cpu_init() because it needs to access per-cpu
- * data which may not be mapped in the trampoline page-table.
- */
+ /* switch away from the initial page table */
load_cr3(swapper_pg_dir);
__flush_tlb_all();
#endif
- cpu_init();
- preempt_disable();
- smp_callin();
-
/* otherwise gcc will move up smp_processor_id before the cpu_init */
barrier();
/*
@@ -785,7 +779,6 @@ do_rest:
#ifdef CONFIG_X86_32
/* Stack for startup_32 can be just as for start_secondary onwards */
irq_ctx_init(cpu);
- initial_page_table = __pa(&trampoline_pg_dir);
#else
clear_tsk_thread_flag(c_idle.idle, TIF_FORK);
initial_gs = per_cpu_offset(cpu);
@@ -934,7 +927,6 @@ int __cpuinit native_cpu_up(unsigned int cpu)
per_cpu(cpu_state, cpu) = CPU_UP_PREPARE;
err = do_boot_cpu(apicid, cpu);
-
if (err) {
pr_debug("do_boot_cpu failed %d\n", err);
return -EIO;
diff --git a/arch/x86/kernel/trampoline.c b/arch/x86/kernel/trampoline.c
index 4c3da56..a375616 100644
--- a/arch/x86/kernel/trampoline.c
+++ b/arch/x86/kernel/trampoline.c
@@ -38,19 +38,3 @@ unsigned long __trampinit setup_trampoline(void)
memcpy(trampoline_base, trampoline_data, TRAMPOLINE_SIZE);
return virt_to_phys(trampoline_base);
}
-
-void __init setup_trampoline_page_table(void)
-{
-#ifdef CONFIG_X86_32
- /* Copy kernel address range */
- clone_pgd_range(trampoline_pg_dir + KERNEL_PGD_BOUNDARY,
- swapper_pg_dir + KERNEL_PGD_BOUNDARY,
- KERNEL_PGD_PTRS);
-
- /* Initialize low mappings */
- clone_pgd_range(trampoline_pg_dir,
- swapper_pg_dir + KERNEL_PGD_BOUNDARY,
- min_t(unsigned long, KERNEL_PGD_PTRS,
- KERNEL_PGD_BOUNDARY));
-#endif
-}
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c
index 5d0a671..0e969f9 100644
--- a/arch/x86/mm/init_32.c
+++ b/arch/x86/mm/init_32.c
@@ -528,48 +528,6 @@ static void __init pagetable_init(void)
permanent_kmaps_init(pgd_base);
}
-#ifdef CONFIG_ACPI_SLEEP
-/*
- * ACPI suspend needs this for resume, because things like the intel-agp
- * driver might have split up a kernel 4MB mapping.
- */
-char swsusp_pg_dir[PAGE_SIZE]
- __attribute__ ((aligned(PAGE_SIZE)));
-
-static inline void save_pg_dir(void)
-{
- copy_page(swsusp_pg_dir, swapper_pg_dir);
-}
-#else /* !CONFIG_ACPI_SLEEP */
-static inline void save_pg_dir(void)
-{
-}
-#endif /* !CONFIG_ACPI_SLEEP */
-
-void zap_low_mappings(bool early)
-{
- int i;
-
- /*
- * Zap initial low-memory mappings.
- *
- * Note that "pgd_clear()" doesn't do it for
- * us, because pgd_clear() is a no-op on i386.
- */
- for (i = 0; i < KERNEL_PGD_BOUNDARY; i++) {
-#ifdef CONFIG_X86_PAE
- set_pgd(swapper_pg_dir+i, __pgd(1 + __pa(empty_zero_page)));
-#else
- set_pgd(swapper_pg_dir+i, __pgd(0));
-#endif
- }
-
- if (early)
- __flush_tlb();
- else
- flush_tlb_all();
-}
-
pteval_t __supported_pte_mask __read_mostly = ~(_PAGE_NX | _PAGE_GLOBAL | _PAGE_IOMAP);
EXPORT_SYMBOL_GPL(__supported_pte_mask);
@@ -882,9 +840,6 @@ void __init mem_init(void)
if (boot_cpu_data.wp_works_ok < 0)
test_wp_bit();
-
- save_pg_dir();
- zap_low_mappings(true);
}
#ifdef CONFIG_MEMORY_HOTPLUG
--
1.7.2.3
--
Regards/Gruss,
Boris.
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [tip:x86/trampoline] x86-32, mm: Add an initial page table for core bootstrapping
2010-10-20 7:05 ` [PATCH -tip/master] x86-32, mm: Add an initial page table for core bootstrapping Borislav Petkov
@ 2010-10-20 21:28 ` tip-bot for Borislav Petkov
0 siblings, 0 replies; 8+ messages in thread
From: tip-bot for Borislav Petkov @ 2010-10-20 21:28 UTC (permalink / raw)
To: linux-tip-commits; +Cc: linux-kernel, hpa, mingo, tglx, hpa, bp
Commit-ID: b40827fa7268fda8a62490728a61c2856f33830b
Gitweb: http://git.kernel.org/tip/b40827fa7268fda8a62490728a61c2856f33830b
Author: Borislav Petkov <bp@alien8.de>
AuthorDate: Sat, 28 Aug 2010 15:58:33 +0200
Committer: H. Peter Anvin <hpa@linux.intel.com>
CommitDate: Wed, 20 Oct 2010 14:23:55 -0700
x86-32, mm: Add an initial page table for core bootstrapping
This patch adds an initial page table with low mappings used exclusively
for booting APs/resuming after ACPI suspend/machine restart. After this,
there's no need to add low mappings to swapper_pg_dir and zap them later
or create own swsusp PGD page solely for ACPI sleep needs - we have
initial_page_table for that.
Signed-off-by: Borislav Petkov <bp@alien8.de>
LKML-Reference: <20101020070526.GA9588@liondog.tnic>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
---
arch/x86/include/asm/pgtable_32.h | 2 +-
arch/x86/include/asm/tlbflush.h | 2 -
arch/x86/include/asm/trampoline.h | 3 --
arch/x86/kernel/acpi/sleep.c | 7 ++++-
arch/x86/kernel/head32.c | 1 +
arch/x86/kernel/head_32.S | 55 +++++++++++++++++--------------------
arch/x86/kernel/reboot.c | 10 +-----
arch/x86/kernel/setup.c | 18 +++++++++++-
arch/x86/kernel/smpboot.c | 16 +++--------
arch/x86/kernel/trampoline.c | 16 -----------
arch/x86/mm/init_32.c | 45 ------------------------------
11 files changed, 56 insertions(+), 119 deletions(-)
diff --git a/arch/x86/include/asm/pgtable_32.h b/arch/x86/include/asm/pgtable_32.h
index f686f49..8abde9e 100644
--- a/arch/x86/include/asm/pgtable_32.h
+++ b/arch/x86/include/asm/pgtable_32.h
@@ -26,7 +26,7 @@ struct mm_struct;
struct vm_area_struct;
extern pgd_t swapper_pg_dir[1024];
-extern pgd_t trampoline_pg_dir[1024];
+extern pgd_t initial_page_table[1024];
static inline void pgtable_cache_init(void) { }
static inline void check_pgt_cache(void) { }
diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
index 7f3eba0..169be89 100644
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -172,6 +172,4 @@ static inline void flush_tlb_kernel_range(unsigned long start,
flush_tlb_all();
}
-extern void zap_low_mappings(bool early);
-
#endif /* _ASM_X86_TLBFLUSH_H */
diff --git a/arch/x86/include/asm/trampoline.h b/arch/x86/include/asm/trampoline.h
index 4dde797..f4500fb 100644
--- a/arch/x86/include/asm/trampoline.h
+++ b/arch/x86/include/asm/trampoline.h
@@ -13,16 +13,13 @@ extern unsigned char *trampoline_base;
extern unsigned long init_rsp;
extern unsigned long initial_code;
-extern unsigned long initial_page_table;
extern unsigned long initial_gs;
#define TRAMPOLINE_SIZE roundup(trampoline_end - trampoline_data, PAGE_SIZE)
extern unsigned long setup_trampoline(void);
-extern void __init setup_trampoline_page_table(void);
extern void __init reserve_trampoline_memory(void);
#else
-static inline void setup_trampoline_page_table(void) {}
static inline void reserve_trampoline_memory(void) {}
#endif /* CONFIG_X86_TRAMPOLINE */
diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c
index 33cec15..b35e1ab 100644
--- a/arch/x86/kernel/acpi/sleep.c
+++ b/arch/x86/kernel/acpi/sleep.c
@@ -12,6 +12,11 @@
#include <asm/segment.h>
#include <asm/desc.h>
+#ifdef CONFIG_X86_32
+#include <asm/pgtable.h>
+#include <asm/pgtable_32.h>
+#endif
+
#include "realmode/wakeup.h"
#include "sleep.h"
@@ -90,7 +95,7 @@ int acpi_save_state_mem(void)
#ifndef CONFIG_64BIT
header->pmode_entry = (u32)&wakeup_pmode_return;
- header->pmode_cr3 = (u32)(swsusp_pg_dir - __PAGE_OFFSET);
+ header->pmode_cr3 = (u32)__pa(&initial_page_table);
saved_magic = 0x12345678;
#else /* CONFIG_64BIT */
header->trampoline_segment = setup_trampoline() >> 4;
diff --git a/arch/x86/kernel/head32.c b/arch/x86/kernel/head32.c
index 784360c..8b9c201 100644
--- a/arch/x86/kernel/head32.c
+++ b/arch/x86/kernel/head32.c
@@ -17,6 +17,7 @@
#include <asm/apic.h>
#include <asm/io_apic.h>
#include <asm/bios_ebda.h>
+#include <asm/tlbflush.h>
static void __init i386_default_early_setup(void)
{
diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S
index fa8c1b8..bcece91 100644
--- a/arch/x86/kernel/head_32.S
+++ b/arch/x86/kernel/head_32.S
@@ -183,13 +183,12 @@ default_entry:
#ifdef CONFIG_X86_PAE
/*
- * In PAE mode swapper_pg_dir is statically defined to contain enough
- * entries to cover the VMSPLIT option (that is the top 1, 2 or 3
- * entries). The identity mapping is handled by pointing two PGD
- * entries to the first kernel PMD.
+ * In PAE mode initial_page_table is statically defined to contain
+ * enough entries to cover the VMSPLIT option (that is the top 1, 2 or 3
+ * entries). The identity mapping is handled by pointing two PGD entries
+ * to the first kernel PMD.
*
- * Note the upper half of each PMD or PTE are always zero at
- * this stage.
+ * Note the upper half of each PMD or PTE are always zero at this stage.
*/
#define KPMDS (((-__PAGE_OFFSET) >> 30) & 3) /* Number of kernel PMDs */
@@ -197,7 +196,7 @@ default_entry:
xorl %ebx,%ebx /* %ebx is kept at zero */
movl $pa(__brk_base), %edi
- movl $pa(swapper_pg_pmd), %edx
+ movl $pa(initial_pg_pmd), %edx
movl $PTE_IDENT_ATTR, %eax
10:
leal PDE_IDENT_ATTR(%edi),%ecx /* Create PMD entry */
@@ -226,14 +225,14 @@ default_entry:
movl %eax, pa(max_pfn_mapped)
/* Do early initialization of the fixmap area */
- movl $pa(swapper_pg_fixmap)+PDE_IDENT_ATTR,%eax
- movl %eax,pa(swapper_pg_pmd+0x1000*KPMDS-8)
+ movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eax
+ movl %eax,pa(initial_pg_pmd+0x1000*KPMDS-8)
#else /* Not PAE */
page_pde_offset = (__PAGE_OFFSET >> 20);
movl $pa(__brk_base), %edi
- movl $pa(swapper_pg_dir), %edx
+ movl $pa(initial_page_table), %edx
movl $PTE_IDENT_ATTR, %eax
10:
leal PDE_IDENT_ATTR(%edi),%ecx /* Create PDE entry */
@@ -257,8 +256,8 @@ page_pde_offset = (__PAGE_OFFSET >> 20);
movl %eax, pa(max_pfn_mapped)
/* Do early initialization of the fixmap area */
- movl $pa(swapper_pg_fixmap)+PDE_IDENT_ATTR,%eax
- movl %eax,pa(swapper_pg_dir+0xffc)
+ movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eax
+ movl %eax,pa(initial_page_table+0xffc)
#endif
jmp 3f
/*
@@ -334,7 +333,7 @@ ENTRY(startup_32_smp)
/*
* Enable paging
*/
- movl pa(initial_page_table), %eax
+ movl $pa(initial_page_table), %eax
movl %eax,%cr3 /* set the page table pointer.. */
movl %cr0,%eax
orl $X86_CR0_PG,%eax
@@ -614,8 +613,6 @@ ignore_int:
.align 4
ENTRY(initial_code)
.long i386_start_kernel
-ENTRY(initial_page_table)
- .long pa(swapper_pg_dir)
/*
* BSS section
@@ -623,20 +620,18 @@ ENTRY(initial_page_table)
__PAGE_ALIGNED_BSS
.align PAGE_SIZE_asm
#ifdef CONFIG_X86_PAE
-swapper_pg_pmd:
+initial_pg_pmd:
.fill 1024*KPMDS,4,0
#else
-ENTRY(swapper_pg_dir)
+ENTRY(initial_page_table)
.fill 1024,4,0
#endif
-swapper_pg_fixmap:
+initial_pg_fixmap:
.fill 1024,4,0
-#ifdef CONFIG_X86_TRAMPOLINE
-ENTRY(trampoline_pg_dir)
- .fill 1024,4,0
-#endif
ENTRY(empty_zero_page)
.fill 4096,1,0
+ENTRY(swapper_pg_dir)
+ .fill 1024,4,0
/*
* This starts the data section.
@@ -645,20 +640,20 @@ ENTRY(empty_zero_page)
__PAGE_ALIGNED_DATA
/* Page-aligned for the benefit of paravirt? */
.align PAGE_SIZE_asm
-ENTRY(swapper_pg_dir)
- .long pa(swapper_pg_pmd+PGD_IDENT_ATTR),0 /* low identity map */
+ENTRY(initial_page_table)
+ .long pa(initial_pg_pmd+PGD_IDENT_ATTR),0 /* low identity map */
# if KPMDS == 3
- .long pa(swapper_pg_pmd+PGD_IDENT_ATTR),0
- .long pa(swapper_pg_pmd+PGD_IDENT_ATTR+0x1000),0
- .long pa(swapper_pg_pmd+PGD_IDENT_ATTR+0x2000),0
+ .long pa(initial_pg_pmd+PGD_IDENT_ATTR),0
+ .long pa(initial_pg_pmd+PGD_IDENT_ATTR+0x1000),0
+ .long pa(initial_pg_pmd+PGD_IDENT_ATTR+0x2000),0
# elif KPMDS == 2
.long 0,0
- .long pa(swapper_pg_pmd+PGD_IDENT_ATTR),0
- .long pa(swapper_pg_pmd+PGD_IDENT_ATTR+0x1000),0
+ .long pa(initial_pg_pmd+PGD_IDENT_ATTR),0
+ .long pa(initial_pg_pmd+PGD_IDENT_ATTR+0x1000),0
# elif KPMDS == 1
.long 0,0
.long 0,0
- .long pa(swapper_pg_pmd+PGD_IDENT_ATTR),0
+ .long pa(initial_pg_pmd+PGD_IDENT_ATTR),0
# else
# error "Kernel PMDs should be 1, 2 or 3"
# endif
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index 7a4cf14..f7f53dc 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -371,16 +371,10 @@ void machine_real_restart(const unsigned char *code, int length)
CMOS_WRITE(0x00, 0x8f);
spin_unlock(&rtc_lock);
- /* Remap the kernel at virtual address zero, as well as offset zero
- from the kernel segment. This assumes the kernel segment starts at
- virtual address PAGE_OFFSET. */
- memcpy(swapper_pg_dir, swapper_pg_dir + KERNEL_PGD_BOUNDARY,
- sizeof(swapper_pg_dir [0]) * KERNEL_PGD_PTRS);
-
/*
- * Use `swapper_pg_dir' as our page directory.
+ * Switch back to the initial page table.
*/
- load_cr3(swapper_pg_dir);
+ load_cr3(initial_page_table);
/* Write 0x1234 to absolute memory location 0x472. The BIOS reads
this on booting to tell it to "Bypass memory test (also warm
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 322b24f..af6cf2b 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -728,6 +728,17 @@ void __init setup_arch(char **cmdline_p)
#ifdef CONFIG_X86_32
memcpy(&boot_cpu_data, &new_cpu_data, sizeof(new_cpu_data));
visws_early_detect();
+
+ /*
+ * copy kernel address range established so far and switch
+ * to the proper swapper page table
+ */
+ clone_pgd_range(swapper_pg_dir + KERNEL_PGD_BOUNDARY,
+ initial_page_table + KERNEL_PGD_BOUNDARY,
+ KERNEL_PGD_PTRS);
+
+ load_cr3(swapper_pg_dir);
+ __flush_tlb_all();
#else
printk(KERN_INFO "Command line: %s\n", boot_command_line);
#endif
@@ -1009,7 +1020,12 @@ void __init setup_arch(char **cmdline_p)
paging_init();
x86_init.paging.pagetable_setup_done(swapper_pg_dir);
- setup_trampoline_page_table();
+#ifdef CONFIG_X86_32
+ /* sync back kernel address range */
+ clone_pgd_range(initial_page_table + KERNEL_PGD_BOUNDARY,
+ swapper_pg_dir + KERNEL_PGD_BOUNDARY,
+ KERNEL_PGD_PTRS);
+#endif
tboot_probe();
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 63a1a55..e63bb51 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -298,22 +298,16 @@ notrace static void __cpuinit start_secondary(void *unused)
* fragile that we want to limit the things done here to the
* most necessary things.
*/
+ cpu_init();
+ preempt_disable();
+ smp_callin();
#ifdef CONFIG_X86_32
- /*
- * Switch away from the trampoline page-table
- *
- * Do this before cpu_init() because it needs to access per-cpu
- * data which may not be mapped in the trampoline page-table.
- */
+ /* switch away from the initial page table */
load_cr3(swapper_pg_dir);
__flush_tlb_all();
#endif
- cpu_init();
- preempt_disable();
- smp_callin();
-
/* otherwise gcc will move up smp_processor_id before the cpu_init */
barrier();
/*
@@ -772,7 +766,6 @@ do_rest:
#ifdef CONFIG_X86_32
/* Stack for startup_32 can be just as for start_secondary onwards */
irq_ctx_init(cpu);
- initial_page_table = __pa(&trampoline_pg_dir);
#else
clear_tsk_thread_flag(c_idle.idle, TIF_FORK);
initial_gs = per_cpu_offset(cpu);
@@ -921,7 +914,6 @@ int __cpuinit native_cpu_up(unsigned int cpu)
per_cpu(cpu_state, cpu) = CPU_UP_PREPARE;
err = do_boot_cpu(apicid, cpu);
-
if (err) {
pr_debug("do_boot_cpu failed %d\n", err);
return -EIO;
diff --git a/arch/x86/kernel/trampoline.c b/arch/x86/kernel/trampoline.c
index e2a5952..f1488a3 100644
--- a/arch/x86/kernel/trampoline.c
+++ b/arch/x86/kernel/trampoline.c
@@ -38,19 +38,3 @@ unsigned long __trampinit setup_trampoline(void)
memcpy(trampoline_base, trampoline_data, TRAMPOLINE_SIZE);
return virt_to_phys(trampoline_base);
}
-
-void __init setup_trampoline_page_table(void)
-{
-#ifdef CONFIG_X86_32
- /* Copy kernel address range */
- clone_pgd_range(trampoline_pg_dir + KERNEL_PGD_BOUNDARY,
- swapper_pg_dir + KERNEL_PGD_BOUNDARY,
- KERNEL_PGD_PTRS);
-
- /* Initialize low mappings */
- clone_pgd_range(trampoline_pg_dir,
- swapper_pg_dir + KERNEL_PGD_BOUNDARY,
- min_t(unsigned long, KERNEL_PGD_PTRS,
- KERNEL_PGD_BOUNDARY));
-#endif
-}
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c
index 558f2d3..1aeac2d 100644
--- a/arch/x86/mm/init_32.c
+++ b/arch/x86/mm/init_32.c
@@ -548,48 +548,6 @@ static void __init pagetable_init(void)
permanent_kmaps_init(pgd_base);
}
-#ifdef CONFIG_ACPI_SLEEP
-/*
- * ACPI suspend needs this for resume, because things like the intel-agp
- * driver might have split up a kernel 4MB mapping.
- */
-char swsusp_pg_dir[PAGE_SIZE]
- __attribute__ ((aligned(PAGE_SIZE)));
-
-static inline void save_pg_dir(void)
-{
- copy_page(swsusp_pg_dir, swapper_pg_dir);
-}
-#else /* !CONFIG_ACPI_SLEEP */
-static inline void save_pg_dir(void)
-{
-}
-#endif /* !CONFIG_ACPI_SLEEP */
-
-void zap_low_mappings(bool early)
-{
- int i;
-
- /*
- * Zap initial low-memory mappings.
- *
- * Note that "pgd_clear()" doesn't do it for
- * us, because pgd_clear() is a no-op on i386.
- */
- for (i = 0; i < KERNEL_PGD_BOUNDARY; i++) {
-#ifdef CONFIG_X86_PAE
- set_pgd(swapper_pg_dir+i, __pgd(1 + __pa(empty_zero_page)));
-#else
- set_pgd(swapper_pg_dir+i, __pgd(0));
-#endif
- }
-
- if (early)
- __flush_tlb();
- else
- flush_tlb_all();
-}
-
pteval_t __supported_pte_mask __read_mostly = ~(_PAGE_NX | _PAGE_GLOBAL | _PAGE_IOMAP);
EXPORT_SYMBOL_GPL(__supported_pte_mask);
@@ -958,9 +916,6 @@ void __init mem_init(void)
if (boot_cpu_data.wp_works_ok < 0)
test_wp_bit();
-
- save_pg_dir();
- zap_low_mappings(true);
}
#ifdef CONFIG_MEMORY_HOTPLUG
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH] OLPC: Add XO-1 suspend/resume support
@ 2010-11-11 15:02 Daniel Drake
0 siblings, 0 replies; 8+ messages in thread
From: Daniel Drake @ 2010-11-11 15:02 UTC (permalink / raw)
To: tglx, mingo, hpa, x86; +Cc: linux-kernel
Add code needed for basic suspend/resume of the XO-1 laptop.
Signed-off-by: Daniel Drake <dsd@laptop.org>
---
arch/x86/Kconfig | 2 +-
arch/x86/include/asm/olpc.h | 9 ++-
arch/x86/platform/olpc/Makefile | 2 +-
arch/x86/platform/olpc/olpc-xo1-wakeup.S | 132 ++++++++++++++++++++++++++++++
arch/x86/platform/olpc/olpc-xo1.c | 79 ++++++++++++++++++
5 files changed, 219 insertions(+), 5 deletions(-)
create mode 100644 arch/x86/platform/olpc/olpc-xo1-wakeup.S
v2: add dependency on CONFIG_PM_SLEEP (thanks Randy), avoid requirement
on hacking swsusp_pg_dir by switching to initial_page_table
v3: rebase to fix conflict in olpc.h with the now-merged XO-1 rfkill driver
Resending after 2 weeks of no feedback.
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index e832768..a27b0dc 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2041,7 +2041,7 @@ config OLPC
config OLPC_XO1
tristate "OLPC XO-1 support"
- depends on OLPC && PCI
+ depends on OLPC && PCI && PM_SLEEP
---help---
Add support for non-essential features of the OLPC XO-1 laptop.
diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h
index 42a978c..5bf0805 100644
--- a/arch/x86/include/asm/olpc.h
+++ b/arch/x86/include/asm/olpc.h
@@ -88,9 +88,12 @@ extern int olpc_ec_mask_unset(uint8_t bits);
/* EC commands */
-#define EC_FIRMWARE_REV 0x08
-#define EC_WLAN_ENTER_RESET 0x35
-#define EC_WLAN_LEAVE_RESET 0x25
+#define EC_FIRMWARE_REV 0x08
+#define EC_WAKE_UP_WLAN 0x24
+#define EC_WLAN_LEAVE_RESET 0x25
+#define EC_SET_SCI_INHIBIT 0x32
+#define EC_SET_SCI_INHIBIT_RELEASE 0x34
+#define EC_WLAN_ENTER_RESET 0x35
/* SCI source values */
diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile
index c31b8fc..a1e7f02 100644
--- a/arch/x86/platform/olpc/Makefile
+++ b/arch/x86/platform/olpc/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_OLPC) += olpc.o
-obj-$(CONFIG_OLPC_XO1) += olpc-xo1.o
+obj-$(CONFIG_OLPC_XO1) += olpc-xo1.o olpc-xo1-wakeup.o
obj-$(CONFIG_OLPC_OPENFIRMWARE) += olpc_ofw.o
diff --git a/arch/x86/platform/olpc/olpc-xo1-wakeup.S b/arch/x86/platform/olpc/olpc-xo1-wakeup.S
new file mode 100644
index 0000000..d36e5f4
--- /dev/null
+++ b/arch/x86/platform/olpc/olpc-xo1-wakeup.S
@@ -0,0 +1,132 @@
+.text
+#include <linux/linkage.h>
+#include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/pgtable_32.h>
+
+ .macro writepost,value
+ movb $0x34, %al
+ outb %al, $0x70
+ movb $\value, %al
+ outb %al, $0x71
+ .endm
+
+ALIGN
+ .align 4096
+
+wakeup_start:
+# jmp wakeup_start
+
+ cli
+ cld
+
+ # Clear any dangerous flags
+
+ pushl $0
+ popfl
+
+ writepost 0x31
+
+ # Set up %cr3
+ movl $initial_page_table - __PAGE_OFFSET, %eax
+ movl %eax, %cr3
+
+ movl saved_cr4, %eax
+ movl %eax, %cr4
+
+ movl saved_cr0, %eax
+ movl %eax, %cr0
+
+ jmp 1f
+1:
+ ljmpl $__KERNEL_CS,$wakeup_return
+
+
+.org 0x1000
+
+wakeup_return:
+ movw $__KERNEL_DS, %ax
+ movw %ax, %ss
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+
+ lgdt saved_gdt
+ lidt saved_idt
+ lldt saved_ldt
+ ljmp $(__KERNEL_CS),$1f
+1:
+ movl %cr3, %eax
+ movl %eax, %cr3
+ wbinvd
+
+ # Go back to the return point
+ jmp ret_point
+
+save_registers:
+ sgdt saved_gdt
+ sidt saved_idt
+ sldt saved_ldt
+
+ pushl %edx
+ movl %cr4, %edx
+ movl %edx, saved_cr4
+
+ movl %cr0, %edx
+ movl %edx, saved_cr0
+
+ popl %edx
+
+ movl %ebx, saved_context_ebx
+ movl %ebp, saved_context_ebp
+ movl %esi, saved_context_esi
+ movl %edi, saved_context_edi
+
+ pushfl
+ popl saved_context_eflags
+
+ ret
+
+
+restore_registers:
+ movl saved_context_ebp, %ebp
+ movl saved_context_ebx, %ebx
+ movl saved_context_esi, %esi
+ movl saved_context_edi, %edi
+
+ pushl saved_context_eflags
+ popfl
+
+ ret
+
+
+ENTRY(do_olpc_suspend_lowlevel)
+ call save_processor_state
+ call save_registers
+
+ # This is the stack context we want to remember
+ movl %esp, saved_context_esp
+
+ pushl $3
+ call olpc_xo1_do_sleep
+
+ jmp wakeup_start
+ .p2align 4,,7
+ret_point:
+ movl saved_context_esp, %esp
+
+ writepost 0x32
+
+ call restore_registers
+ call restore_processor_state
+ ret
+
+.data
+ALIGN
+
+saved_gdt: .long 0,0
+saved_idt: .long 0,0
+saved_ldt: .long 0
+saved_cr4: .long 0
+saved_cr0: .long 0
diff --git a/arch/x86/platform/olpc/olpc-xo1.c b/arch/x86/platform/olpc/olpc-xo1.c
index f5442c0..9a06081 100644
--- a/arch/x86/platform/olpc/olpc-xo1.c
+++ b/arch/x86/platform/olpc/olpc-xo1.c
@@ -16,6 +16,7 @@
#include <linux/pci_ids.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
+#include <linux/suspend.h>
#include <asm/io.h>
#include <asm/olpc.h>
@@ -33,12 +34,83 @@
#define PM_SSC 0x54
/* PM registers (ACPI block) */
+#define PM1_STS 0x00
#define PM1_CNT 0x08
#define PM_GPE0_STS 0x18
+#define CS5536_PM_PWRBTN (1 << 8)
+
+extern void do_olpc_suspend_lowlevel(void);
+
static unsigned long acpi_base;
static unsigned long pms_base;
+static struct {
+ unsigned long address;
+ unsigned short segment;
+} ofw_bios_entry = { 0xF0000 + PAGE_OFFSET, __KERNEL_CS };
+
+static int xo1_power_state_enter(suspend_state_t pm_state)
+{
+ int r;
+
+ /* Only STR is supported */
+ if (pm_state != PM_SUSPEND_MEM)
+ return -EINVAL;
+
+ r = olpc_ec_cmd(EC_SET_SCI_INHIBIT, NULL, 0, NULL, 0);
+ if (r)
+ return r;
+
+ /* Save CPU state */
+ do_olpc_suspend_lowlevel();
+
+ /* Resume path starts here */
+
+ /* Tell the EC to stop inhibiting SCIs */
+ olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0);
+
+ /*
+ * Tell the wireless module to restart USB communication.
+ * Must be done twice.
+ */
+ olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
+ olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
+
+ return 0;
+}
+
+asmlinkage int olpc_xo1_do_sleep(u8 sleep_state)
+{
+ void *pgd_addr = __va(read_cr3());
+ printk(KERN_ERR "xo1_do_sleep!\n"); /* this needs to remain here so
+ * that gcc doesn't optimize
+ * away our __va! */
+
+ /* Enable wakeup through power button */
+ outl((CS5536_PM_PWRBTN << 16) | 0xFFFF, acpi_base + PM1_STS);
+
+ __asm__ __volatile__("movl %0,%%eax" : : "r" (pgd_addr));
+ __asm__("call *(%%edi); cld"
+ : : "D" (&ofw_bios_entry));
+ __asm__ __volatile__("movb $0x34, %al\n\t"
+ "outb %al, $0x70\n\t"
+ "movb $0x30, %al\n\t"
+ "outb %al, $0x71\n\t");
+ return 0;
+}
+
+static int xo1_power_state_valid(suspend_state_t pm_state)
+{
+ /* suspend-to-RAM only */
+ return pm_state == PM_SUSPEND_MEM;
+}
+
+static struct platform_suspend_ops xo1_suspend_ops = {
+ .valid = xo1_power_state_valid,
+ .enter = xo1_power_state_enter,
+};
+
static void xo1_power_off(void)
{
printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
@@ -101,6 +173,13 @@ static int __devinit olpc_xo1_probe(struct platform_device *pdev)
if (r)
return r;
+ /*
+ * Take a reference on ourself to prevent module unloading. We can't
+ * safely unload after changing the suspend handlers.
+ */
+ __module_get(THIS_MODULE);
+
+ suspend_set_ops(&xo1_suspend_ops);
pm_power_off = xo1_power_off;
printk(KERN_INFO "OLPC XO-1 support registered\n");
--
1.7.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2010-11-11 15:02 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-19 22:01 [PATCH] OLPC: Add XO-1 suspend/resume support Daniel Drake
2010-10-19 22:06 ` Randy Dunlap
2010-10-19 22:11 ` H. Peter Anvin
2010-10-20 5:47 ` Borislav Petkov
2010-10-20 5:58 ` H. Peter Anvin
2010-10-20 7:05 ` [PATCH -tip/master] x86-32, mm: Add an initial page table for core bootstrapping Borislav Petkov
2010-10-20 21:28 ` [tip:x86/trampoline] " tip-bot for Borislav Petkov
-- strict thread matches above, loose matches on Subject: below --
2010-11-11 15:02 [PATCH] OLPC: Add XO-1 suspend/resume support Daniel Drake
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.