2010-11-18 20:28:48

by Daniel Drake

[permalink] [raw]
Subject: [PATCH v4 resend] OLPC: Add XO-1 suspend/resume support

Add code needed for basic suspend/resume of the XO-1 laptop.

Signed-off-by: Daniel Drake <[email protected]>
---
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


I've submitted this 5 times over the last few weeks, am I being too
impatient/persistant? Code is too horrible to even warrant review comments?

http://marc.info/?l=linux-kernel&m=128752574203087&w=2
http://marc.info/?l=linux-kernel&m=128758786712015&w=2
http://marc.info/?l=linux-kernel&m=128818888428639&w=2
http://marc.info/?l=linux-kernel&m=128836342103914&w=2
http://marc.info/?l=linux-kernel&m=128948775532145&w=2

Feedback after the first submission was quickly acted upon and I haven't been
able to get a peep since.




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

v4: update for arch/x86/platform/olpc code move

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


2010-11-26 09:43:48

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [PATCH v4 resend] OLPC: Add XO-1 suspend/resume support

On Thu, 18 Nov 2010, Daniel Drake wrote:
> Feedback after the first submission was quickly acted upon and I haven't been
> able to get a peep since.

Sorry, fell off the radar.

> +
> +wakeup_start:
> +# jmp wakeup_start

Debug leftover ?

> +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! */
> +

This looks suspect along with the __volatile__ magic below. Care to
explain, what you fear that gcc is optimizing away ?

> + /* 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");

Looks good otherwise.

Thanks,

tglx

2010-11-26 19:29:39

by Andres Salomon

[permalink] [raw]
Subject: Re: [PATCH v4 resend] OLPC: Add XO-1 suspend/resume support

On Fri, 26 Nov 2010 16:28:51 +0000
Daniel Drake <[email protected]> wrote:

[...]
> Re. the printk thing, this was your doing:
> http://dev.laptop.org/git/olpc-2.6/commit/?h=stable&id=b4334e9e1444761f19806f9066c6b38ce23079bf
[...]
> ---------- Forwarded message ----------
> From: Thomas Gleixner <[email protected]>
> Date: 26 November 2010 09:43
> Subject: Re: [PATCH v4 resend] OLPC: Add XO-1 suspend/resume support
> To: Daniel Drake <[email protected]>
> Cc: [email protected], [email protected]
>
>
> On Thu, 18 Nov 2010, Daniel Drake wrote:
> > Feedback after the first submission was quickly acted upon and I
> > haven't been able to get a peep since.
>
> Sorry, fell off the radar.
>
> > +
> > +wakeup_start:
> > +# ? ?jmp wakeup_start
>
> ?Debug leftover ?


Leftovers from the original patch; certainly should be removed.


>
> > +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! */
> > +
>
> ?This looks suspect along with the __volatile__ magic below. Care to
> ?explain, what you fear that gcc is optimizing away ?
>


I'm working from memory here; in the version of gcc we were using
at the time (stock FC5 or FC6), setting pgd_addr immediately followed
by the asm below resulted in gcc optimizing away the __va(). The asm
turned into simply:

movl %cr3, %eax

With no mention of PAGE_OFFSET. Messing with 'volatile' didn't help.
Adding the printk did, as it causes %eax to get clobbered, forcing
temporary storage of %cr3 into a separate register. A lot has changed
since then (compilers, this code, as well as the definition of
read_cr3()), so I highly suspect that it's no longer needed.


> > + ? ? /* 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");
>
> Looks good otherwise.
>
> Thanks,
>
> ? ? ? ?tglx

2010-12-03 00:58:16

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH v4 resend] OLPC: Add XO-1 suspend/resume support

On Thu, 18 Nov 2010 20:28:37 +0000 (GMT)
Daniel Drake <[email protected]> wrote:

> Add code needed for basic suspend/resume of the XO-1 laptop.
>
> Signed-off-by: Daniel Drake <[email protected]>
> ---
> 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 ++++++++++++++++++

i386 allmodconfig:

make[3]: *** No rule to make target `arch/x86/platform/olpc/olpc-xo1-wakeup.c',

I "fixed" that locally by making olpc depend on x86_64.

2010-12-03 02:10:40

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH v4 resend] OLPC: Add XO-1 suspend/resume support

On 12/02/2010 04:57 PM, Andrew Morton wrote:
> On Thu, 18 Nov 2010 20:28:37 +0000 (GMT)
> Daniel Drake <[email protected]> wrote:
>
>> Add code needed for basic suspend/resume of the XO-1 laptop.
>>
>> Signed-off-by: Daniel Drake <[email protected]>
>> ---
>> 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 ++++++++++++++++++
>
> i386 allmodconfig:
>
> make[3]: *** No rule to make target `arch/x86/platform/olpc/olpc-xo1-wakeup.c',
>
> I "fixed" that locally by making olpc depend on x86_64.

... which is of course completely busted since olpc is 32 bit.

-hpa

--
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel. I don't speak on their behalf.