Messages in this thread | | | Date | Tue, 19 Oct 2010 15:06:24 -0700 | From | Randy Dunlap <> | Subject | Re: [PATCH] OLPC: Add XO-1 suspend/resume support |
| |
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 ***
| |