2011-04-30 12:54:10

by Daniel Drake

[permalink] [raw]
Subject: [PATCH 0/11] OLPC Power Management

Hi,

These patches implement various power management features on the XO-1
and XO-1.5 laptops.

Review comments very welcome, I'll do my best to act upon them quickly.

Daniel


2011-04-30 12:29:02

by Daniel Drake

[permalink] [raw]
Subject: [PATCH 02/11] x86, olpc: Move CS5536-related constants to cs5535.h

Move these definitions into the relevant header file.
This was requested in the review of the upcoming XO-1 suspend/resume code.

Signed-off-by: Daniel Drake <[email protected]>
---
arch/x86/platform/olpc/olpc-xo1.c | 26 ++++++++------------------
include/linux/cs5535.h | 21 +++++++++++++++++++++
2 files changed, 29 insertions(+), 18 deletions(-)

diff --git a/arch/x86/platform/olpc/olpc-xo1.c b/arch/x86/platform/olpc/olpc-xo1.c
index ab81fb2..a63e948 100644
--- a/arch/x86/platform/olpc/olpc-xo1.c
+++ b/arch/x86/platform/olpc/olpc-xo1.c
@@ -12,6 +12,7 @@
* (at your option) any later version.
*/

+#include <linux/cs5535.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
@@ -22,17 +23,6 @@

#define DRV_NAME "olpc-xo1"

-/* PMC registers (PMS block) */
-#define PM_SCLK 0x10
-#define PM_IN_SLPCTL 0x20
-#define PM_WKXD 0x34
-#define PM_WKD 0x30
-#define PM_SSC 0x54
-
-/* PM registers (ACPI block) */
-#define PM1_CNT 0x08
-#define PM_GPE0_STS 0x18
-
static unsigned long acpi_base;
static unsigned long pms_base;

@@ -41,17 +31,17 @@ static void xo1_power_off(void)
printk(KERN_INFO "OLPC XO-1 power off sequence...\n");

/* Enable all of these controls with 0 delay */
- outl(0x40000000, pms_base + PM_SCLK);
- outl(0x40000000, pms_base + PM_IN_SLPCTL);
- outl(0x40000000, pms_base + PM_WKXD);
- outl(0x40000000, pms_base + PM_WKD);
+ outl(0x40000000, pms_base + CS5536_PM_SCLK);
+ outl(0x40000000, pms_base + CS5536_PM_IN_SLPCTL);
+ outl(0x40000000, pms_base + CS5536_PM_WKXD);
+ outl(0x40000000, pms_base + CS5536_PM_WKD);

/* Clear status bits (possibly unnecessary) */
- outl(0x0002ffff, pms_base + PM_SSC);
- outl(0xffffffff, acpi_base + PM_GPE0_STS);
+ outl(0x0002ffff, pms_base + CS5536_PM_SSC);
+ outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);

/* Write SLP_EN bit to start the machinery */
- outl(0x00002000, acpi_base + PM1_CNT);
+ outl(0x00002000, acpi_base + CS5536_PM1_CNT);
}

static int __devinit olpc_xo1_probe(struct platform_device *pdev)
diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h
index 6fe2114..e46b8b0 100644
--- a/include/linux/cs5535.h
+++ b/include/linux/cs5535.h
@@ -49,6 +49,27 @@
#define LBAR_ACPI_SIZE 0x40
#define LBAR_PMS_SIZE 0x80

+/*
+ * PMC registers (PMS block)
+ * It is only safe to access these registers as dword accesses.
+ * See CS5536 Specification Update erratas 17 & 18
+ */
+#define CS5536_PM_SCLK 0x10
+#define CS5536_PM_IN_SLPCTL 0x20
+#define CS5536_PM_WKXD 0x34
+#define CS5536_PM_WKD 0x30
+#define CS5536_PM_SSC 0x54
+
+/*
+ * PM registers (ACPI block)
+ * It is only safe to access these registers as dword accesses.
+ * See CS5536 Specification Update erratas 17 & 18
+ */
+#define CS5536_PM1_STS 0x00
+#define CS5536_PM1_EN 0x02
+#define CS5536_PM1_CNT 0x08
+#define CS5536_PM_GPE0_STS 0x18
+
/* VSA2 magic values */
#define VSA_VRC_INDEX 0xAC1C
#define VSA_VRC_DATA 0xAC1E
--
1.7.4.4

2011-04-30 12:29:33

by Daniel Drake

[permalink] [raw]
Subject: [PATCH 04/11] x86, olpc: Add XO-1 suspend/resume support

Add code needed for basic suspend/resume of the XO-1 laptop.
Based on earlier work by Jordan Crouse, Andres Salomon, and others.

This patch incorporates all earlier feedback from Thomas Gleixner. To
clarify a certain point (now more obvious in the code itself):
On resume, OpenFirmware returns execution to Linux in protected mode
with a kernel-compatible GDT already set up. The changes and
simplifications suggested have all been included.

Signed-off-by: Daniel Drake <[email protected]>
---
arch/x86/Kconfig | 4 +-
arch/x86/include/asm/olpc.h | 15 +++-
arch/x86/platform/olpc/Makefile | 2 +-
arch/x86/platform/olpc/olpc-xo1-pm.c | 92 +++++++++++++++++++++++++
arch/x86/platform/olpc/xo1-wakeup.S | 124 ++++++++++++++++++++++++++++++++++
include/linux/cs5535.h | 3 +
6 files changed, 234 insertions(+), 6 deletions(-)
create mode 100644 arch/x86/platform/olpc/xo1-wakeup.S

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 3db9ca5..ec9cf25 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2068,9 +2068,9 @@ config OLPC

config OLPC_XO1_PM
bool "OLPC XO-1 Power Management"
- depends on OLPC && MFD_CS5535
+ depends on OLPC && MFD_CS5535 && PM_SLEEP
---help---
- Add support for poweroff of the OLPC XO-1 laptop.
+ Add support for poweroff and suspend of the OLPC XO-1 laptop.

endif # X86_32

diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h
index 5ca6801..10ea595 100644
--- a/arch/x86/include/asm/olpc.h
+++ b/arch/x86/include/asm/olpc.h
@@ -76,6 +76,12 @@ static inline int olpc_has_dcon(void)

#endif

+#ifdef CONFIG_OLPC_XO1_PM
+extern void do_olpc_suspend_lowlevel(void);
+extern void olpc_xo1_pm_wakeup_set(u16 value);
+extern void olpc_xo1_pm_wakeup_clear(u16 value);
+#endif
+
extern int pci_olpc_init(void);

/* EC related functions */
@@ -88,9 +94,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 cd25038..1ae7bed 100644
--- a/arch/x86/platform/olpc/Makefile
+++ b/arch/x86/platform/olpc/Makefile
@@ -1,2 +1,2 @@
obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o
-obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o
+obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o xo1-wakeup.o
diff --git a/arch/x86/platform/olpc/olpc-xo1-pm.c b/arch/x86/platform/olpc/olpc-xo1-pm.c
index a2a59d3..6f3855a 100644
--- a/arch/x86/platform/olpc/olpc-xo1-pm.c
+++ b/arch/x86/platform/olpc/olpc-xo1-pm.c
@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/mfd/core.h>
+#include <linux/suspend.h>

#include <asm/io.h>
#include <asm/olpc.h>
@@ -25,6 +26,85 @@
static unsigned long acpi_base;
static unsigned long pms_base;

+static u16 wakeup_mask = CS5536_PM_PWRBTN;
+
+static struct {
+ unsigned long address;
+ unsigned short segment;
+} ofw_bios_entry = { 0xF0000 + PAGE_OFFSET, __KERNEL_CS };
+
+/* Set bits in the wakeup mask */
+void olpc_xo1_pm_wakeup_set(u16 value)
+{
+ wakeup_mask |= value;
+}
+EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_set);
+
+/* Clear bits in the wakeup mask */
+void olpc_xo1_pm_wakeup_clear(u16 value)
+{
+ wakeup_mask &= ~value;
+}
+EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_clear);
+
+static int xo1_power_state_enter(suspend_state_t pm_state)
+{
+ unsigned long saved_sci_mask;
+ 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 SCI mask (this gets lost since PM1_EN is used as a mask for
+ * wakeup events, which is not necessarily the same event set)
+ */
+ saved_sci_mask = inl(acpi_base + CS5536_PM1_STS);
+ saved_sci_mask &= 0xffff0000;
+
+ /* Save CPU state */
+ do_olpc_suspend_lowlevel();
+
+ /* Resume path starts here */
+
+ /* Restore SCI mask (using dword access to CS5536_PM1_EN) */
+ outl(saved_sci_mask, acpi_base + CS5536_PM1_STS);
+
+ /* 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 xo1_do_sleep(u8 sleep_state)
+{
+ void *pgd_addr = __va(read_cr3());
+
+ /* Program wakeup mask (using dword access to CS5536_PM1_EN) */
+ outl(wakeup_mask << 16, acpi_base + CS5536_PM1_STS);
+
+ __asm__("movl %0,%%eax" : : "r" (pgd_addr));
+ __asm__("call *(%%edi); cld"
+ : : "D" (&ofw_bios_entry));
+ __asm__("movb $0x34, %al\n\t"
+ "outb %al, $0x70\n\t"
+ "movb $0x30, %al\n\t"
+ "outb %al, $0x71\n\t");
+ return 0;
+}
+
static void xo1_power_off(void)
{
printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
@@ -43,6 +123,17 @@ static void xo1_power_off(void)
outl(0x00002000, acpi_base + CS5536_PM1_CNT);
}

+static int xo1_power_state_valid(suspend_state_t pm_state)
+{
+ /* suspend-to-RAM only */
+ return pm_state == PM_SUSPEND_MEM;
+}
+
+static const struct platform_suspend_ops xo1_suspend_ops = {
+ .valid = xo1_power_state_valid,
+ .enter = xo1_power_state_enter,
+};
+
static int __devinit xo1_pm_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -68,6 +159,7 @@ static int __devinit xo1_pm_probe(struct platform_device *pdev)

/* If we have both addresses, we can override the poweroff hook */
if (pms_base && acpi_base) {
+ 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/platform/olpc/xo1-wakeup.S b/arch/x86/platform/olpc/xo1-wakeup.S
new file mode 100644
index 0000000..948deb2
--- /dev/null
+++ b/arch/x86/platform/olpc/xo1-wakeup.S
@@ -0,0 +1,124 @@
+.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
+
+wakeup_start:
+ # OFW lands us here, running in protected mode, with a
+ # kernel-compatible GDT already setup.
+
+ # 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
+
+ # Control registers were modified, pipeline resync is needed
+ jmp 1f
+1:
+
+ 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 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
+saved_gdt: .long 0,0
+saved_idt: .long 0,0
+saved_ldt: .long 0
+saved_cr4: .long 0
+saved_cr0: .long 0
+saved_context_esp: .long 0
+saved_context_edi: .long 0
+saved_context_esi: .long 0
+saved_context_ebx: .long 0
+saved_context_ebp: .long 0
+saved_context_eflags: .long 0
diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h
index e46b8b0..2facf16 100644
--- a/include/linux/cs5535.h
+++ b/include/linux/cs5535.h
@@ -70,6 +70,9 @@
#define CS5536_PM1_CNT 0x08
#define CS5536_PM_GPE0_STS 0x18

+/* CS5536_PM1_EN bits */
+#define CS5536_PM_PWRBTN (1 << 8)
+
/* VSA2 magic values */
#define VSA_VRC_INDEX 0xAC1C
#define VSA_VRC_DATA 0xAC1E
--
1.7.4.4

2011-04-30 12:29:38

by Daniel Drake

[permalink] [raw]
Subject: [PATCH 08/11] x86, olpc-xo1-sci: Add lid switch functionality

Configure the XO-1's lid switch GPIO to trigger an SCI interrupt,
and correctly expose this input device which can be used as a wakeup
source.

Signed-off-by: Daniel Drake <[email protected]>
---
arch/x86/Kconfig | 1 +
arch/x86/platform/olpc/olpc-xo1-sci.c | 207 ++++++++++++++++++++++++++++++++-
include/linux/cs5535.h | 1 +
3 files changed, 208 insertions(+), 1 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 0e62cd5..c40cd82 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2080,6 +2080,7 @@ config OLPC_XO1_SCI
- EC-driven system wakeups
- Power button
- Ebook switch
+ - Lid switch

endif # X86_32

diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c
index 9de2a00..176e4ae 100644
--- a/arch/x86/platform/olpc/olpc-xo1-sci.c
+++ b/arch/x86/platform/olpc/olpc-xo1-sci.c
@@ -32,9 +32,26 @@
static unsigned long acpi_base;
static struct input_dev *power_button_idev;
static struct input_dev *ebook_switch_idev;
+static struct input_dev *lid_switch_idev;

static int sci_irq;

+static bool lid_open;
+static bool lid_inverted;
+static int lid_wake_mode;
+
+enum lid_wake_modes {
+ LID_WAKE_ALWAYS,
+ LID_WAKE_OPEN,
+ LID_WAKE_CLOSE,
+};
+
+static const char * const lid_wake_mode_names[] = {
+ [LID_WAKE_ALWAYS] = "always",
+ [LID_WAKE_OPEN] = "open",
+ [LID_WAKE_CLOSE] = "close",
+};
+
/* Report current ebook switch state through input layer */
static void send_ebook_state(void)
{
@@ -49,6 +66,70 @@ static void send_ebook_state(void)
input_sync(ebook_switch_idev);
}

+static void flip_lid_inverter(void)
+{
+ /* gpio is high; invert so we'll get l->h event interrupt */
+ if (lid_inverted)
+ cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_INPUT_INVERT);
+ else
+ cs5535_gpio_set(OLPC_GPIO_LID, GPIO_INPUT_INVERT);
+ lid_inverted = !lid_inverted;
+}
+
+static void detect_lid_state(void)
+{
+ /*
+ * the edge detector hookup on the gpio inputs on the geode is
+ * odd, to say the least. See http://dev.laptop.org/ticket/5703
+ * for details, but in a nutshell: we don't use the edge
+ * detectors. instead, we make use of an anomoly: with the both
+ * edge detectors turned off, we still get an edge event on a
+ * positive edge transition. to take advantage of this, we use the
+ * front-end inverter to ensure that that's the edge we're always
+ * going to see next.
+ */
+
+ int state;
+
+ state = cs5535_gpio_isset(OLPC_GPIO_LID, GPIO_READ_BACK);
+ lid_open = !state ^ !lid_inverted; /* x ^^ y */
+ if (!state)
+ return;
+
+ flip_lid_inverter();
+}
+
+/* Report current lid switch state through input layer */
+static void send_lid_state(void)
+{
+ input_report_switch(lid_switch_idev, SW_LID, !lid_open);
+ input_sync(lid_switch_idev);
+}
+
+static ssize_t lid_wake_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const char *mode = lid_wake_mode_names[lid_wake_mode];
+ return sprintf(buf, "%s\n", mode);
+}
+static ssize_t lid_wake_mode_set(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(lid_wake_mode_names); i++) {
+ const char *mode = lid_wake_mode_names[i];
+ if (strlen(mode) != count || strncasecmp(mode, buf, count))
+ continue;
+
+ lid_wake_mode = i;
+ return count;
+ }
+ return -EINVAL;
+}
+static DEVICE_ATTR(lid_wake_mode, S_IWUSR | S_IRUGO, lid_wake_mode_show,
+ lid_wake_mode_set);
+
/*
* Process all items in the EC's SCI queue.
*
@@ -111,6 +192,11 @@ static irqreturn_t xo1_sci_intr(int irq, void *dev_id)
schedule_work(&sci_work);
}

+ cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS);
+ cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS);
+ detect_lid_state();
+ send_lid_state();
+
return IRQ_HANDLED;
}

@@ -126,11 +212,32 @@ static int xo1_sci_suspend(struct platform_device *pdev, pm_message_t state)
else
olpc_ec_wakeup_clear(EC_SCI_SRC_EBOOK);

+ if (!device_may_wakeup(&lid_switch_idev->dev)) {
+ cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
+ } else if (( lid_open && lid_wake_mode == LID_WAKE_OPEN) ||
+ (!lid_open && lid_wake_mode == LID_WAKE_CLOSE)) {
+ flip_lid_inverter();
+
+ /* we may have just caused an event */
+ cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS);
+ cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS);
+
+ cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
+ }
+
return 0;
}

static int xo1_sci_resume(struct platform_device *pdev)
{
+ /*
+ * We don't know what may have happened while we were asleep.
+ * Reestablish our lid setup so we're sure to catch all transitions.
+ */
+ detect_lid_state();
+ send_lid_state();
+ cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
+
/* Enable all EC events */
olpc_ec_mask_write(EC_SCI_SRC_ALL);
return 0;
@@ -208,6 +315,43 @@ static void free_ec_sci(void)
gpio_free(OLPC_GPIO_ECSCI);
}

+static int __devinit setup_lid_events(void)
+{
+ int r;
+
+ r = gpio_request(OLPC_GPIO_LID, "OLPC-LID");
+ if (r)
+ return r;
+
+ gpio_direction_input(OLPC_GPIO_LID);
+
+ cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_INPUT_INVERT);
+ lid_inverted = 0;
+
+ /* Clear edge detection and event enable for now */
+ cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
+ cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_EN);
+ cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_EN);
+ cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS);
+ cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS);
+
+ /* Set the LID to cause an PME event on group 6 */
+ cs5535_gpio_setup_event(OLPC_GPIO_LID, 6, 1);
+
+ /* Set PME group 6 to fire the SCI interrupt */
+ cs5535_gpio_set_irq(6, sci_irq);
+
+ /* Enable the event */
+ cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
+
+ return 0;
+}
+
+static void free_lid_events(void)
+{
+ gpio_free(OLPC_GPIO_LID);
+}
+
static int __devinit setup_power_button(struct platform_device *pdev)
{
int r;
@@ -270,6 +414,50 @@ static void free_ebook_switch(void)
input_free_device(ebook_switch_idev);
}

+static int __devinit setup_lid_switch(struct platform_device *pdev)
+{
+ int r;
+
+ lid_switch_idev = input_allocate_device();
+ if (!lid_switch_idev)
+ return -ENOMEM;
+
+ lid_switch_idev->name = "Lid Switch";
+ lid_switch_idev->phys = DRV_NAME "/input2";
+ set_bit(EV_SW, lid_switch_idev->evbit);
+ set_bit(SW_LID, lid_switch_idev->swbit);
+
+ lid_switch_idev->dev.parent = &pdev->dev;
+ device_set_wakeup_capable(&lid_switch_idev->dev, true);
+
+ r = input_register_device(lid_switch_idev);
+ if (r) {
+ dev_err(&pdev->dev, "failed to register lid switch: %d\n", r);
+ goto err_register;
+ }
+
+ r = device_create_file(&lid_switch_idev->dev, &dev_attr_lid_wake_mode);
+ if (r) {
+ dev_err(&pdev->dev, "failed to create wake mode attr: %d\n", r);
+ goto err_create_attr;
+ }
+
+ return 0;
+
+err_create_attr:
+ input_unregister_device(lid_switch_idev);
+err_register:
+ input_free_device(lid_switch_idev);
+ return r;
+}
+
+static void free_lid_switch(void)
+{
+ device_remove_file(&lid_switch_idev->dev, &dev_attr_lid_wake_mode);
+ input_unregister_device(lid_switch_idev);
+ input_free_device(lid_switch_idev);
+}
+
static int __devinit xo1_sci_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -298,12 +486,21 @@ static int __devinit xo1_sci_probe(struct platform_device *pdev)
if (r)
goto err_ebook;

+ r = setup_lid_switch(pdev);
+ if (r)
+ goto err_lid;
+
+ r = setup_lid_events();
+ if (r)
+ goto err_lidevt;
+
r = setup_ec_sci();
if (r)
goto err_ecsci;

/* Enable PME generation for EC-generated events */
- outl(CS5536_GPIOM7_PME_EN, acpi_base + CS5536_PM_GPE0_EN);
+ outl(CS5536_GPIOM6_PME_EN | CS5536_GPIOM7_PME_EN,
+ acpi_base + CS5536_PM_GPE0_EN);

/* Clear pending events */
outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
@@ -311,6 +508,8 @@ static int __devinit xo1_sci_probe(struct platform_device *pdev)

/* Initial sync */
send_ebook_state();
+ detect_lid_state();
+ send_lid_state();

r = setup_sci_interrupt(pdev);
if (r)
@@ -324,6 +523,10 @@ static int __devinit xo1_sci_probe(struct platform_device *pdev)
err_sci:
free_ec_sci();
err_ecsci:
+ free_lid_events();
+err_lidevt:
+ free_lid_switch();
+err_lid:
free_ebook_switch();
err_ebook:
free_power_button();
@@ -336,6 +539,8 @@ static int __devexit xo1_sci_remove(struct platform_device *pdev)
free_irq(sci_irq, pdev);
cancel_work_sync(&sci_work);
free_ec_sci();
+ free_lid_events();
+ free_lid_switch();
free_ebook_switch();
free_power_button();
acpi_base = 0;
diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h
index 95c16ac..f2b4a2f 100644
--- a/include/linux/cs5535.h
+++ b/include/linux/cs5535.h
@@ -88,6 +88,7 @@

/* CS5536_PM_GPE0_EN bits */
#define CS5536_GPIOM7_PME_EN (1 << 31)
+#define CS5536_GPIOM6_PME_EN (1 << 30)

/* VSA2 magic values */
#define VSA_VRC_INDEX 0xAC1C
--
1.7.4.4

2011-04-30 12:29:40

by Daniel Drake

[permalink] [raw]
Subject: [PATCH 09/11] x86, olpc-xo1-sci: Propagate power supply/battery events

EC events indicate change in AC power connectivity, battery state of
charge, battery error, battery presence, etc. Send notifications to
the power supply subsystem when changes are detected.

Signed-off-by: Daniel Drake <[email protected]>
---
arch/x86/Kconfig | 4 ++-
arch/x86/platform/olpc/olpc-xo1-sci.c | 37 +++++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+), 1 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index c40cd82..583e1c5 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2074,13 +2074,15 @@ config OLPC_XO1_PM

config OLPC_XO1_SCI
bool "OLPC XO-1 SCI extras"
- depends on OLPC && OLPC_XO1_PM
+ depends on OLPC && OLPC_XO1_PM && POWER_SUPPLY
---help---
Add support for SCI-based features of the OLPC XO-1 laptop:
- EC-driven system wakeups
- Power button
- Ebook switch
- Lid switch
+ - AC adapter status updates
+ - Battery status updates

endif # X86_32

diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c
index 176e4ae..1cd800e 100644
--- a/arch/x86/platform/olpc/olpc-xo1-sci.c
+++ b/arch/x86/platform/olpc/olpc-xo1-sci.c
@@ -19,6 +19,7 @@
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/mfd/core.h>
+#include <linux/power_supply.h>
#include <linux/suspend.h>
#include <linux/workqueue.h>

@@ -52,6 +53,26 @@ static const char * const lid_wake_mode_names[] = {
[LID_WAKE_CLOSE] = "close",
};

+static void battery_status_changed(void)
+{
+ struct power_supply *psy = power_supply_get_by_name("olpc-battery");
+
+ if (psy) {
+ power_supply_changed(psy);
+ put_device(psy->dev);
+ }
+}
+
+static void ac_status_changed(void)
+{
+ struct power_supply *psy = power_supply_get_by_name("olpc-ac");
+
+ if (psy) {
+ power_supply_changed(psy);
+ put_device(psy->dev);
+ }
+}
+
/* Report current ebook switch state through input layer */
static void send_ebook_state(void)
{
@@ -151,6 +172,18 @@ static void process_sci_queue(bool propagate_events)

pr_debug(PFX "SCI 0x%x received\n", data);

+ switch (data) {
+ case EC_SCI_SRC_BATERR:
+ case EC_SCI_SRC_BATSOC:
+ case EC_SCI_SRC_BATTERY:
+ case EC_SCI_SRC_BATCRIT:
+ battery_status_changed();
+ break;
+ case EC_SCI_SRC_ACPWR:
+ ac_status_changed();
+ break;
+ }
+
if (data == EC_SCI_SRC_EBOOK && propagate_events)
send_ebook_state();
} while (data);
@@ -240,6 +273,10 @@ static int xo1_sci_resume(struct platform_device *pdev)

/* Enable all EC events */
olpc_ec_mask_write(EC_SCI_SRC_ALL);
+
+ /* Power/battery status might have changed too */
+ battery_status_changed();
+ ac_status_changed();
return 0;
}

--
1.7.4.4

2011-04-30 12:29:30

by Daniel Drake

[permalink] [raw]
Subject: [PATCH 03/11] x86, olpc: rename olpc-xo1 to olpc-xo1-pm

Based on earlier review comments, we'll no longer try to stick all of
our XO-1 goodies in a single driver. We'll split it into a power management
driver, and an EC/SCI driver.

As a first step, rename olpc-xo1 to olpc-xo1-pm, and make it builtin
instead of modular.
---
arch/x86/Kconfig | 6 +-
arch/x86/platform/olpc/Makefile | 2 +-
arch/x86/platform/olpc/olpc-xo1-pm.c | 123 ++++++++++++++++++++++++++++++
arch/x86/platform/olpc/olpc-xo1.c | 136 ----------------------------------
4 files changed, 127 insertions(+), 140 deletions(-)
create mode 100644 arch/x86/platform/olpc/olpc-xo1-pm.c
delete mode 100644 arch/x86/platform/olpc/olpc-xo1.c

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 0945ed8..3db9ca5 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2066,11 +2066,11 @@ config OLPC
Add support for detecting the unique features of the OLPC
XO hardware.

-config OLPC_XO1
- tristate "OLPC XO-1 support"
+config OLPC_XO1_PM
+ bool "OLPC XO-1 Power Management"
depends on OLPC && MFD_CS5535
---help---
- Add support for non-essential features of the OLPC XO-1 laptop.
+ Add support for poweroff of the OLPC XO-1 laptop.

endif # X86_32

diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile
index 81c5e21..cd25038 100644
--- a/arch/x86/platform/olpc/Makefile
+++ b/arch/x86/platform/olpc/Makefile
@@ -1,2 +1,2 @@
obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o
-obj-$(CONFIG_OLPC_XO1) += olpc-xo1.o
+obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o
diff --git a/arch/x86/platform/olpc/olpc-xo1-pm.c b/arch/x86/platform/olpc/olpc-xo1-pm.c
new file mode 100644
index 0000000..a2a59d3
--- /dev/null
+++ b/arch/x86/platform/olpc/olpc-xo1-pm.c
@@ -0,0 +1,123 @@
+/*
+ * Support for power management features of the OLPC XO-1 laptop
+ *
+ * Copyright (C) 2010 Andres Salomon <[email protected]>
+ * Copyright (C) 2010 One Laptop per Child
+ * Copyright (C) 2006 Red Hat, Inc.
+ * Copyright (C) 2006 Advanced Micro Devices, Inc.
+ *
+ * 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 <linux/cs5535.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/mfd/core.h>
+
+#include <asm/io.h>
+#include <asm/olpc.h>
+
+#define DRV_NAME "olpc-xo1-pm"
+
+static unsigned long acpi_base;
+static unsigned long pms_base;
+
+static void xo1_power_off(void)
+{
+ printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
+
+ /* Enable all of these controls with 0 delay */
+ outl(0x40000000, pms_base + CS5536_PM_SCLK);
+ outl(0x40000000, pms_base + CS5536_PM_IN_SLPCTL);
+ outl(0x40000000, pms_base + CS5536_PM_WKXD);
+ outl(0x40000000, pms_base + CS5536_PM_WKD);
+
+ /* Clear status bits (possibly unnecessary) */
+ outl(0x0002ffff, pms_base + CS5536_PM_SSC);
+ outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
+
+ /* Write SLP_EN bit to start the machinery */
+ outl(0x00002000, acpi_base + CS5536_PM1_CNT);
+}
+
+static int __devinit xo1_pm_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int err;
+
+ /* don't run on non-XOs */
+ if (!machine_is_olpc())
+ return -ENODEV;
+
+ err = mfd_cell_enable(pdev);
+ if (err)
+ return err;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "can't fetch device resource info\n");
+ return -EIO;
+ }
+ if (strcmp(pdev->name, "cs5535-pms") == 0)
+ pms_base = res->start;
+ else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
+ acpi_base = res->start;
+
+ /* If we have both addresses, we can override the poweroff hook */
+ if (pms_base && acpi_base) {
+ pm_power_off = xo1_power_off;
+ printk(KERN_INFO "OLPC XO-1 support registered\n");
+ }
+
+ return 0;
+}
+
+static int __devexit xo1_pm_remove(struct platform_device *pdev)
+{
+ mfd_cell_disable(pdev);
+
+ if (strcmp(pdev->name, "cs5535-pms") == 0)
+ pms_base = 0;
+ else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
+ acpi_base = 0;
+
+ pm_power_off = NULL;
+ return 0;
+}
+
+static struct platform_driver cs5535_pms_driver = {
+ .driver = {
+ .name = "cs5535-pms",
+ .owner = THIS_MODULE,
+ },
+ .probe = xo1_pm_probe,
+ .remove = __devexit_p(xo1_pm_remove),
+};
+
+static struct platform_driver cs5535_acpi_driver = {
+ .driver = {
+ .name = "olpc-xo1-pm-acpi",
+ .owner = THIS_MODULE,
+ },
+ .probe = xo1_pm_probe,
+ .remove = __devexit_p(xo1_pm_remove),
+};
+
+static int __init xo1_pm_init(void)
+{
+ int r;
+
+ r = platform_driver_register(&cs5535_pms_driver);
+ if (r)
+ return r;
+
+ r = platform_driver_register(&cs5535_acpi_driver);
+ if (r)
+ platform_driver_unregister(&cs5535_pms_driver);
+
+ return r;
+}
+arch_initcall(xo1_pm_init);
diff --git a/arch/x86/platform/olpc/olpc-xo1.c b/arch/x86/platform/olpc/olpc-xo1.c
deleted file mode 100644
index a63e948..0000000
--- a/arch/x86/platform/olpc/olpc-xo1.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Support for features of the OLPC XO-1 laptop
- *
- * Copyright (C) 2010 Andres Salomon <[email protected]>
- * Copyright (C) 2010 One Laptop per Child
- * Copyright (C) 2006 Red Hat, Inc.
- * Copyright (C) 2006 Advanced Micro Devices, Inc.
- *
- * 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 <linux/cs5535.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pm.h>
-#include <linux/mfd/core.h>
-
-#include <asm/io.h>
-#include <asm/olpc.h>
-
-#define DRV_NAME "olpc-xo1"
-
-static unsigned long acpi_base;
-static unsigned long pms_base;
-
-static void xo1_power_off(void)
-{
- printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
-
- /* Enable all of these controls with 0 delay */
- outl(0x40000000, pms_base + CS5536_PM_SCLK);
- outl(0x40000000, pms_base + CS5536_PM_IN_SLPCTL);
- outl(0x40000000, pms_base + CS5536_PM_WKXD);
- outl(0x40000000, pms_base + CS5536_PM_WKD);
-
- /* Clear status bits (possibly unnecessary) */
- outl(0x0002ffff, pms_base + CS5536_PM_SSC);
- outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
-
- /* Write SLP_EN bit to start the machinery */
- outl(0x00002000, acpi_base + CS5536_PM1_CNT);
-}
-
-static int __devinit olpc_xo1_probe(struct platform_device *pdev)
-{
- struct resource *res;
- int err;
-
- /* don't run on non-XOs */
- if (!machine_is_olpc())
- return -ENODEV;
-
- err = mfd_cell_enable(pdev);
- if (err)
- return err;
-
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (!res) {
- dev_err(&pdev->dev, "can't fetch device resource info\n");
- return -EIO;
- }
- if (strcmp(pdev->name, "cs5535-pms") == 0)
- pms_base = res->start;
- else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
- acpi_base = res->start;
-
- /* If we have both addresses, we can override the poweroff hook */
- if (pms_base && acpi_base) {
- pm_power_off = xo1_power_off;
- printk(KERN_INFO "OLPC XO-1 support registered\n");
- }
-
- return 0;
-}
-
-static int __devexit olpc_xo1_remove(struct platform_device *pdev)
-{
- mfd_cell_disable(pdev);
-
- if (strcmp(pdev->name, "cs5535-pms") == 0)
- pms_base = 0;
- else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
- acpi_base = 0;
-
- pm_power_off = NULL;
- return 0;
-}
-
-static struct platform_driver cs5535_pms_drv = {
- .driver = {
- .name = "cs5535-pms",
- .owner = THIS_MODULE,
- },
- .probe = olpc_xo1_probe,
- .remove = __devexit_p(olpc_xo1_remove),
-};
-
-static struct platform_driver cs5535_acpi_drv = {
- .driver = {
- .name = "olpc-xo1-pm-acpi",
- .owner = THIS_MODULE,
- },
- .probe = olpc_xo1_probe,
- .remove = __devexit_p(olpc_xo1_remove),
-};
-
-static int __init olpc_xo1_init(void)
-{
- int r;
-
- r = platform_driver_register(&cs5535_pms_drv);
- if (r)
- return r;
-
- r = platform_driver_register(&cs5535_acpi_drv);
- if (r)
- platform_driver_unregister(&cs5535_pms_drv);
-
- return r;
-}
-
-static void __exit olpc_xo1_exit(void)
-{
- platform_driver_unregister(&cs5535_acpi_drv);
- platform_driver_unregister(&cs5535_pms_drv);
-}
-
-MODULE_AUTHOR("Daniel Drake <[email protected]>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:cs5535-pms");
-
-module_init(olpc_xo1_init);
-module_exit(olpc_xo1_exit);
--
1.7.4.4

2011-04-30 12:54:18

by Daniel Drake

[permalink] [raw]
Subject: [PATCH 05/11] x86, olpc: Add XO-1 SCI driver and power button control

The System Control Interrupt is used in the OLPC XO-1 to control various
features of the laptop. Add the driver base and the power button
functionality.

This driver can't be built as a module, because functionality added in
future patches means that some drivers need to know at boot-time whether
SCI-based functionality is available.

Signed-off-by: Daniel Drake <[email protected]>
---
arch/x86/Kconfig | 7 ++
arch/x86/platform/olpc/Makefile | 1 +
arch/x86/platform/olpc/olpc-xo1-sci.c | 191 +++++++++++++++++++++++++++++++++
include/linux/cs5535.h | 8 ++
4 files changed, 207 insertions(+), 0 deletions(-)
create mode 100644 arch/x86/platform/olpc/olpc-xo1-sci.c

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index ec9cf25..c273c62 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2072,6 +2072,13 @@ config OLPC_XO1_PM
---help---
Add support for poweroff and suspend of the OLPC XO-1 laptop.

+config OLPC_XO1_SCI
+ bool "OLPC XO-1 SCI extras"
+ depends on OLPC && OLPC_XO1_PM
+ ---help---
+ Add support for SCI-based features of the OLPC XO-1 laptop:
+ - Power button
+
endif # X86_32

config AMD_NB
diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile
index 1ae7bed..1ec5ade 100644
--- a/arch/x86/platform/olpc/Makefile
+++ b/arch/x86/platform/olpc/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o
obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o xo1-wakeup.o
+obj-$(CONFIG_OLPC_XO1_SCI) += olpc-xo1-sci.o
diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c
new file mode 100644
index 0000000..8fbf961
--- /dev/null
+++ b/arch/x86/platform/olpc/olpc-xo1-sci.c
@@ -0,0 +1,191 @@
+/*
+ * Support for OLPC XO-1 System Control Interrupts (SCI)
+ *
+ * Copyright (C) 2010 One Laptop per Child
+ * Copyright (C) 2006 Red Hat, Inc.
+ * Copyright (C) 2006 Advanced Micro Devices, Inc.
+ *
+ * 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 <linux/cs5535.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/mfd/core.h>
+#include <linux/suspend.h>
+
+#include <asm/io.h>
+#include <asm/msr.h>
+#include <asm/olpc.h>
+
+#define DRV_NAME "olpc-xo1-sci"
+#define PFX DRV_NAME ": "
+
+static unsigned long acpi_base;
+static struct input_dev *power_button_idev;
+static int sci_irq;
+
+static irqreturn_t xo1_sci_intr(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ u32 sts;
+ u32 gpe;
+
+ sts = inl(acpi_base + CS5536_PM1_STS);
+ outl(sts | 0xffff, acpi_base + CS5536_PM1_STS);
+
+ gpe = inl(acpi_base + CS5536_PM_GPE0_STS);
+ outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
+
+ dev_dbg(&pdev->dev, "sts %x gpe %x\n", sts, gpe);
+
+ if (sts & CS5536_PWRBTN_FLAG && !(sts & CS5536_WAK_FLAG)) {
+ input_report_key(power_button_idev, KEY_POWER, 1);
+ input_sync(power_button_idev);
+ input_report_key(power_button_idev, KEY_POWER, 0);
+ input_sync(power_button_idev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int xo1_sci_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ if (device_may_wakeup(&power_button_idev->dev))
+ olpc_xo1_pm_wakeup_set(CS5536_PM_PWRBTN);
+ else
+ olpc_xo1_pm_wakeup_clear(CS5536_PM_PWRBTN);
+ return 0;
+}
+
+static int __devinit setup_sci_interrupt(struct platform_device *pdev)
+{
+ u32 lo, hi;
+ u32 sts;
+ int r;
+
+ rdmsr(0x51400020, lo, hi);
+ sci_irq = (lo >> 20) & 15;
+
+ if (sci_irq) {
+ dev_info(&pdev->dev, "SCI is mapped to IRQ %d\n", sci_irq);
+ } else {
+ /* Zero means masked */
+ dev_info(&pdev->dev, "SCI unmapped. Mapping to IRQ 3\n");
+ sci_irq = 3;
+ lo |= 0x00300000;
+ wrmsrl(0x51400020, lo);
+ }
+
+ /* Select level triggered in PIC */
+ if (sci_irq < 8) {
+ lo = inb(CS5536_PIC_INT_SEL1);
+ lo |= 1 << sci_irq;
+ outb(lo, CS5536_PIC_INT_SEL1);
+ } else {
+ lo = inb(CS5536_PIC_INT_SEL2);
+ lo |= 1 << (sci_irq - 8);
+ outb(lo, CS5536_PIC_INT_SEL2);
+ }
+
+ /* Enable SCI from power button, and clear pending interrupts */
+ sts = inl(acpi_base + CS5536_PM1_STS);
+ outl((CS5536_PM_PWRBTN << 16) | 0xffff, acpi_base + CS5536_PM1_STS);
+
+ r = request_irq(sci_irq, xo1_sci_intr, 0, DRV_NAME, pdev);
+ if (r)
+ dev_err(&pdev->dev, "can't request interrupt\n");
+
+ return r;
+}
+
+static int __devinit setup_power_button(struct platform_device *pdev)
+{
+ int r;
+
+ power_button_idev = input_allocate_device();
+ if (!power_button_idev)
+ return -ENOMEM;
+
+ power_button_idev->name = "Power Button";
+ power_button_idev->phys = DRV_NAME "/input0";
+ set_bit(EV_KEY, power_button_idev->evbit);
+ set_bit(KEY_POWER, power_button_idev->keybit);
+
+ power_button_idev->dev.parent = &pdev->dev;
+ device_init_wakeup(&power_button_idev->dev, 1);
+
+ r = input_register_device(power_button_idev);
+ if (r) {
+ dev_err(&pdev->dev, "failed to register power button: %d\n", r);
+ input_free_device(power_button_idev);
+ }
+
+ return r;
+}
+
+static void free_power_button(void)
+{
+ input_unregister_device(power_button_idev);
+ input_free_device(power_button_idev);
+}
+
+static int __devinit xo1_sci_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int r;
+
+ /* don't run on non-XOs */
+ if (!machine_is_olpc())
+ return -ENODEV;
+
+ r = mfd_cell_enable(pdev);
+ if (r)
+ return r;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "can't fetch device resource info\n");
+ return -EIO;
+ }
+ acpi_base = res->start;
+
+ r = setup_power_button(pdev);
+ if (r)
+ return r;
+
+ r = setup_sci_interrupt(pdev);
+ if (r)
+ free_power_button();
+
+ return r;
+}
+
+static int __devexit xo1_sci_remove(struct platform_device *pdev)
+{
+ mfd_cell_disable(pdev);
+ free_irq(sci_irq, pdev);
+ free_power_button();
+ acpi_base = 0;
+ return 0;
+}
+
+static struct platform_driver xo1_sci_driver = {
+ .driver = {
+ .name = "olpc-xo1-sci-acpi",
+ },
+ .probe = xo1_sci_probe,
+ .remove = __devexit_p(xo1_sci_remove),
+ .suspend = xo1_sci_suspend,
+};
+
+static int __init xo1_sci_init(void)
+{
+ return platform_driver_register(&xo1_sci_driver);
+}
+arch_initcall(xo1_sci_init);
diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h
index 2facf16..6f78235 100644
--- a/include/linux/cs5535.h
+++ b/include/linux/cs5535.h
@@ -43,6 +43,10 @@
#define MSR_GX_GLD_MSR_CONFIG 0xC0002001
#define MSR_GX_MSR_PADSEL 0xC0002011

+/* PIC registers */
+#define CS5536_PIC_INT_SEL1 0x4d0
+#define CS5536_PIC_INT_SEL2 0x4d1
+
/* resource sizes */
#define LBAR_GPIO_SIZE 0xFF
#define LBAR_MFGPT_SIZE 0x40
@@ -70,6 +74,10 @@
#define CS5536_PM1_CNT 0x08
#define CS5536_PM_GPE0_STS 0x18

+/* CS5536_PM1_STS bits */
+#define CS5536_WAK_FLAG (1 << 15)
+#define CS5536_PWRBTN_FLAG (1 << 8)
+
/* CS5536_PM1_EN bits */
#define CS5536_PM_PWRBTN (1 << 8)

--
1.7.4.4

2011-04-30 12:54:16

by Daniel Drake

[permalink] [raw]
Subject: [PATCH 10/11] x86, olpc: Add XO-1 RTC driver

Add a driver to configure the XO-1 RTC via CS5536 MSRs, to be used as a
system wakeup source via olpc-xo1-pm.

Device detection is based on finding the relevant device tree node.

Signed-off-by: Daniel Drake <[email protected]>
Cc: [email protected]
---
.../devicetree/bindings/rtc/olpc-xo1-rtc.txt | 5 +
arch/x86/Kconfig | 7 ++
arch/x86/platform/olpc/Makefile | 1 +
arch/x86/platform/olpc/olpc-xo1-rtc.c | 82 ++++++++++++++++++++
include/linux/cs5535.h | 5 +
5 files changed, 100 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt
create mode 100644 arch/x86/platform/olpc/olpc-xo1-rtc.c

diff --git a/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt b/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt
new file mode 100644
index 0000000..a2891ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt
@@ -0,0 +1,5 @@
+OLPC XO-1 RTC
+~~~~~~~~~~~~~
+
+Required properties:
+ - compatible : "olpc,xo1-rtc"
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 583e1c5..eb9f377 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2072,6 +2072,13 @@ config OLPC_XO1_PM
---help---
Add support for poweroff and suspend of the OLPC XO-1 laptop.

+config OLPC_XO1_RTC
+ bool "OLPC XO-1 Real Time Clock"
+ depends on OLPC_XO1_PM && RTC_DRV_CMOS
+ ---help---
+ Add support for the XO-1 real time clock, which can be used as a
+ programmable wakeup source.
+
config OLPC_XO1_SCI
bool "OLPC XO-1 SCI extras"
depends on OLPC && OLPC_XO1_PM && POWER_SUPPLY
diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile
index 1ec5ade..8922b9b 100644
--- a/arch/x86/platform/olpc/Makefile
+++ b/arch/x86/platform/olpc/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o
obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o xo1-wakeup.o
+obj-$(CONFIG_OLPC_XO1_RTC) += olpc-xo1-rtc.o
obj-$(CONFIG_OLPC_XO1_SCI) += olpc-xo1-sci.o
diff --git a/arch/x86/platform/olpc/olpc-xo1-rtc.c b/arch/x86/platform/olpc/olpc-xo1-rtc.c
new file mode 100644
index 0000000..476cc98
--- /dev/null
+++ b/arch/x86/platform/olpc/olpc-xo1-rtc.c
@@ -0,0 +1,82 @@
+/*
+ * Support for OLPC XO-1 Real Time Clock (RTC)
+ *
+ * Copyright (C) 2011 One Laptop per Child
+ *
+ * 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 <linux/mc146818rtc.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/of.h>
+
+#include <asm/msr.h>
+#include <asm/olpc.h>
+
+static void rtc_wake_on(struct device *dev)
+{
+ olpc_xo1_pm_wakeup_set(CS5536_PM_RTC);
+}
+
+static void rtc_wake_off(struct device *dev)
+{
+ olpc_xo1_pm_wakeup_clear(CS5536_PM_RTC);
+}
+
+static struct resource rtc_platform_resource[] = {
+ [0] = {
+ .start = RTC_PORT(0),
+ .end = RTC_PORT(1),
+ .flags = IORESOURCE_IO,
+ },
+ [1] = {
+ .start = RTC_IRQ,
+ .end = RTC_IRQ,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct cmos_rtc_board_info rtc_info = {
+ .rtc_day_alarm = 0,
+ .rtc_mon_alarm = 0,
+ .rtc_century = 0,
+ .wake_on = rtc_wake_on,
+ .wake_off = rtc_wake_off,
+};
+
+static struct platform_device xo1_rtc_device = {
+ .name = "rtc_cmos",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(rtc_platform_resource),
+ .dev.platform_data = &rtc_info,
+ .resource = rtc_platform_resource,
+};
+
+static int __init xo1_rtc_init(void)
+{
+ int r;
+ struct device_node *node;
+
+ node = of_find_compatible_node(NULL, NULL, "olpc,xo1-rtc");
+ if (!node)
+ return 0;
+ of_node_put(node);
+
+ pr_info("olpc-xo1-rtc: Initializing OLPC XO-1 RTC\n");
+ rdmsrl(MSR_RTC_DOMA_OFFSET, rtc_info.rtc_day_alarm);
+ rdmsrl(MSR_RTC_MONA_OFFSET, rtc_info.rtc_mon_alarm);
+ rdmsrl(MSR_RTC_CEN_OFFSET, rtc_info.rtc_century);
+
+ r = platform_device_register(&xo1_rtc_device);
+ if (r)
+ return r;
+
+ device_init_wakeup(&xo1_rtc_device.dev, 1);
+ return 0;
+}
+arch_initcall(xo1_rtc_init);
+
diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h
index f2b4a2f..3f98d81 100644
--- a/include/linux/cs5535.h
+++ b/include/linux/cs5535.h
@@ -38,6 +38,10 @@
#define MSR_MFGPT_NR 0x51400029
#define MSR_MFGPT_SETUP 0x5140002B

+#define MSR_RTC_DOMA_OFFSET 0x51400055
+#define MSR_RTC_MONA_OFFSET 0x51400056
+#define MSR_RTC_CEN_OFFSET 0x51400057
+
#define MSR_LX_SPARE_MSR 0x80000011 /* DC-specific */

#define MSR_GX_GLD_MSR_CONFIG 0xC0002001
@@ -81,6 +85,7 @@

/* CS5536_PM1_EN bits */
#define CS5536_PM_PWRBTN (1 << 8)
+#define CS5536_PM_RTC (1 << 10)

/* CS5536_PM_GPE0_STS bits */
#define CS5536_GPIOM7_PME_FLAG (1 << 31)
--
1.7.4.4

2011-04-30 13:01:24

by Daniel Drake

[permalink] [raw]
Subject: [PATCH 01/11] x86, olpc: add missing elements to device tree

In response to new device tree code in the kernel, OLPC will start
using it for probing of certain devices. However, some firmware fixes
are needed to put the devicetree into a usable state.

Retain compatibility with old firmware by fixing up the device tree
at boot-time if it does not contain the new nodes/properties that
we need for probing. This is the same approach taken on PPC platforms.

Signed-off-by: Daniel Drake <[email protected]>
Acked-by: Grant Likely <[email protected]>
Acked-by: Andres Salomon <[email protected]>
Cc: [email protected]
---
arch/x86/platform/olpc/olpc_dt.c | 103 ++++++++++++++++++++++++++++++++++++++
1 files changed, 103 insertions(+), 0 deletions(-)

diff --git a/arch/x86/platform/olpc/olpc_dt.c b/arch/x86/platform/olpc/olpc_dt.c
index d39f63d..d6ee929 100644
--- a/arch/x86/platform/olpc/olpc_dt.c
+++ b/arch/x86/platform/olpc/olpc_dt.c
@@ -165,6 +165,107 @@ static struct of_pdt_ops prom_olpc_ops __initdata = {
.pkg2path = olpc_dt_pkg2path,
};

+static phandle __init olpc_dt_finddevice(const char *path)
+{
+ phandle node;
+ const void *args[] = { path };
+ void *res[] = { &node };
+
+ if (olpc_ofw("finddevice", args, res)) {
+ pr_err("olpc_dt: finddevice failed!\n");
+ return 0;
+ }
+
+ if ((s32) node == -1)
+ return 0;
+
+ return node;
+}
+
+static int __init olpc_dt_interpret(const char *words)
+{
+ int result;
+ const void *args[] = { words };
+ void *res[] = { &result };
+
+ if (olpc_ofw("interpret", args, res)) {
+ pr_err("olpc_dt: interpret failed!\n");
+ return -1;
+ }
+
+ return result;
+}
+
+/*
+ * Extract board revision directly from OFW device tree.
+ * We can't use olpc_platform_info because that hasn't been set up yet.
+ */
+static u32 __init olpc_dt_get_board_revision(void)
+{
+ phandle node;
+ __be32 rev;
+ int r;
+
+ node = olpc_dt_finddevice("/");
+ if (!node)
+ return 0;
+
+ r = olpc_dt_getproperty(node, "board-revision-int",
+ (char *) &rev, sizeof(rev));
+ if (r < 0)
+ return 0;
+
+ return be32_to_cpu(rev);
+}
+
+void __init olpc_dt_fixup(void)
+{
+ int r;
+ char buf[64];
+ phandle node;
+ u32 board_rev;
+
+ node = olpc_dt_finddevice("/battery@0");
+ if (!node)
+ return;
+
+ /*
+ * If the battery node has a compatible property, we are running a new
+ * enough firmware and don't have fixups to make.
+ */
+ r = olpc_dt_getproperty(node, "compatible", buf, sizeof(buf));
+ if (r > 0)
+ return;
+
+ pr_info("PROM DT: Old firmware detected, applying fixes\n");
+
+ /* Add olpc,xo1-battery compatible marker to battery node */
+ olpc_dt_interpret("\" /battery@0\" find-device"
+ " \" olpc,xo1-battery\" +compatible"
+ " device-end");
+
+ board_rev = olpc_dt_get_board_revision();
+ if (!board_rev)
+ return;
+
+ if (board_rev >= olpc_board_pre(0xd0)) {
+ /* XO-1.5: add dcon device */
+ olpc_dt_interpret("\" /pci/display@1\" find-device"
+ " new-device"
+ " \" dcon\" device-name \" olpc,xo1-dcon\" +compatible"
+ " finish-device device-end");
+ } else {
+ /* XO-1: add dcon device, mark RTC as olpc,xo1-rtc */
+ olpc_dt_interpret("\" /pci/display@1,1\" find-device"
+ " new-device"
+ " \" dcon\" device-name \" olpc,xo1-dcon\" +compatible"
+ " finish-device device-end"
+ " \" /rtc\" find-device"
+ " \" olpc,xo1-rtc\" +compatible"
+ " device-end");
+ }
+}
+
void __init olpc_dt_build_devicetree(void)
{
phandle root;
@@ -172,6 +273,8 @@ void __init olpc_dt_build_devicetree(void)
if (!olpc_ofw_is_installed())
return;

+ olpc_dt_fixup();
+
root = olpc_dt_getsibling(0);
if (!root) {
pr_err("PROM: unable to get root node from OFW!\n");
--
1.7.4.4

2011-04-30 13:01:28

by Daniel Drake

[permalink] [raw]
Subject: [PATCH 06/11] x86, olpc: EC SCI wakeup mask functionality

Update the EC SCI masks with recent additions.

Add functions to query SCI events and set the wakeup mask, to be used by
followup patches.

Add functions to tweak an event mask used to select certain EC events as
a system wakeup source. Also add a function to determine if EC wakeup
functionality is available, as this depends on child drivers (different
for each laptop model) to configure the SCI interrupt.

Signed-off-by: Daniel Drake <[email protected]>
---
arch/x86/include/asm/olpc.h | 31 ++++++++++++---
arch/x86/platform/olpc/olpc.c | 86 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 111 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h
index 10ea595..0e56d01 100644
--- a/arch/x86/include/asm/olpc.h
+++ b/arch/x86/include/asm/olpc.h
@@ -13,6 +13,7 @@ struct olpc_platform_t {

#define OLPC_F_PRESENT 0x01
#define OLPC_F_DCON 0x02
+#define OLPC_F_EC_WIDE_SCI 0x04

#ifdef CONFIG_OLPC

@@ -62,6 +63,13 @@ static inline int olpc_board_at_least(uint32_t rev)
return olpc_platform_info.boardrev >= rev;
}

+extern void olpc_ec_wakeup_set(u16 value);
+extern void olpc_ec_wakeup_clear(u16 value);
+extern bool olpc_ec_wakeup_available(void);
+
+extern int olpc_ec_mask_write(u16 bits);
+extern int olpc_ec_sci_query(u16 *sci_value);
+
#else

static inline int machine_is_olpc(void)
@@ -74,6 +82,14 @@ static inline int olpc_has_dcon(void)
return 0;
}

+static inline void olpc_ec_wakeup_set(u16 value) { }
+static inline void olpc_ec_wakeup_clear(u16 value) { }
+
+static inline bool olpc_ec_wakeup_available(void)
+{
+ return false;
+}
+
#endif

#ifdef CONFIG_OLPC_XO1_PM
@@ -89,17 +105,18 @@ extern int pci_olpc_init(void);
extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen,
unsigned char *outbuf, size_t outlen);

-extern int olpc_ec_mask_set(uint8_t bits);
-extern int olpc_ec_mask_unset(uint8_t bits);
-
/* EC commands */

#define EC_FIRMWARE_REV 0x08
+#define EC_WRITE_SCI_MASK 0x1b
#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
+#define EC_WRITE_EXT_SCI_MASK 0x38
+#define EC_SCI_QUERY 0x84
+#define EC_EXT_SCI_QUERY 0x85

/* SCI source values */

@@ -108,10 +125,12 @@ extern int olpc_ec_mask_unset(uint8_t bits);
#define EC_SCI_SRC_BATTERY 0x02
#define EC_SCI_SRC_BATSOC 0x04
#define EC_SCI_SRC_BATERR 0x08
-#define EC_SCI_SRC_EBOOK 0x10
-#define EC_SCI_SRC_WLAN 0x20
+#define EC_SCI_SRC_EBOOK 0x10 /* XO-1 only */
+#define EC_SCI_SRC_WLAN 0x20 /* XO-1 only */
#define EC_SCI_SRC_ACPWR 0x40
-#define EC_SCI_SRC_ALL 0x7F
+#define EC_SCI_SRC_BATCRIT 0x80
+#define EC_SCI_SRC_GPWAKE 0x100 /* XO-1.5 only */
+#define EC_SCI_SRC_ALL 0x1FF

/* GPIO assignments */

diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c
index 0060fd5..72fd041 100644
--- a/arch/x86/platform/olpc/olpc.c
+++ b/arch/x86/platform/olpc/olpc.c
@@ -19,6 +19,7 @@
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/of.h>
+#include <linux/syscore_ops.h>

#include <asm/geode.h>
#include <asm/setup.h>
@@ -30,6 +31,9 @@ EXPORT_SYMBOL_GPL(olpc_platform_info);

static DEFINE_SPINLOCK(ec_lock);

+/* EC event mask to be applied during suspend (defining wakeup sources). */
+static u16 ec_wakeup_mask;
+
/* what the timeout *should* be (in ms) */
#define EC_BASE_TIMEOUT 20

@@ -188,6 +192,79 @@ err:
}
EXPORT_SYMBOL_GPL(olpc_ec_cmd);

+void olpc_ec_wakeup_set(u16 value)
+{
+ ec_wakeup_mask |= value;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_wakeup_set);
+
+void olpc_ec_wakeup_clear(u16 value)
+{
+ ec_wakeup_mask &= ~value;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_wakeup_clear);
+
+/*
+ * Returns true if the compile and runtime configurations allow for EC events
+ * to wake the system.
+ */
+bool olpc_ec_wakeup_available(void)
+{
+ if (!machine_is_olpc())
+ return false;
+
+ /*
+ * XO-1 EC wakeups are available when olpc-xo1-sci driver is
+ * compiled in
+ */
+#ifdef CONFIG_OLPC_XO1_SCI
+ if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) /* XO-1 */
+ return true;
+#endif
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_wakeup_available);
+
+int olpc_ec_mask_write(u16 bits)
+{
+ if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) {
+ __be16 ec_word = cpu_to_be16(bits);
+ return olpc_ec_cmd(EC_WRITE_EXT_SCI_MASK, (void *) &ec_word, 2,
+ NULL, 0);
+ } else {
+ unsigned char ec_byte = bits & 0xff;
+ return olpc_ec_cmd(EC_WRITE_SCI_MASK, &ec_byte, 1, NULL, 0);
+ }
+}
+EXPORT_SYMBOL_GPL(olpc_ec_mask_write);
+
+int olpc_ec_sci_query(u16 *sci_value)
+{
+ int ret;
+
+ if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) {
+ __be16 ec_word;
+ ret = olpc_ec_cmd(EC_EXT_SCI_QUERY,
+ NULL, 0, (void *) &ec_word, 2);
+ if (ret == 0)
+ *sci_value = be16_to_cpu(ec_word);
+ } else {
+ unsigned char ec_byte;
+ ret = olpc_ec_cmd(EC_SCI_QUERY, NULL, 0, &ec_byte, 1);
+ if (ret == 0)
+ *sci_value = ec_byte;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_sci_query);
+
+static int olpc_ec_suspend(void)
+{
+ return olpc_ec_mask_write(ec_wakeup_mask);
+}
+
static bool __init check_ofw_architecture(struct device_node *root)
{
const char *olpc_arch;
@@ -242,6 +319,10 @@ static int __init add_xo1_platform_devices(void)
return 0;
}

+static struct syscore_ops olpc_syscore_ops = {
+ .suspend = olpc_ec_suspend,
+};
+
static int __init olpc_init(void)
{
int r = 0;
@@ -266,6 +347,9 @@ static int __init olpc_init(void)
!cs5535_has_vsa2())
x86_init.pci.arch_init = pci_olpc_init;
#endif
+ /* EC version 0x5f adds support for wide SCI mask */
+ if (olpc_platform_info.ecver >= 0x5f)
+ olpc_platform_info.flags |= OLPC_F_EC_WIDE_SCI;

printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n",
((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
@@ -278,6 +362,8 @@ static int __init olpc_init(void)
return r;
}

+ register_syscore_ops(&olpc_syscore_ops);
+
return 0;
}

--
1.7.4.4

2011-04-30 13:01:37

by Daniel Drake

[permalink] [raw]
Subject: [PATCH 11/11] x86, olpc: Add XO-1.5 SCI driver

Add a driver for the ACPI-based EC event interface found on the
OLPC XO-1.5 laptop. This enables notification of battery/AC power events,
and enables various devices to be used as wakeup sources through regular
ACPI mechanisms.

This driver can't be built as a module, because some drivers need to know
at boot-time if SCI-based functionality is available via
olpc_ec_wakeup_available().

Signed-off-by: Daniel Drake <[email protected]>
---
arch/x86/Kconfig | 9 ++
arch/x86/platform/olpc/Makefile | 1 +
arch/x86/platform/olpc/olpc-xo15-sci.c | 168 ++++++++++++++++++++++++++++++++
arch/x86/platform/olpc/olpc.c | 9 ++
4 files changed, 187 insertions(+), 0 deletions(-)
create mode 100644 arch/x86/platform/olpc/olpc-xo15-sci.c

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index eb9f377..b0bbe86 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2091,6 +2091,15 @@ config OLPC_XO1_SCI
- AC adapter status updates
- Battery status updates

+config OLPC_XO15_SCI
+ bool "OLPC XO-1.5 SCI extras"
+ depends on OLPC && ACPI && POWER_SUPPLY
+ ---help---
+ Add support for SCI-based features of the OLPC XO-1.5 laptop:
+ - EC-driven system wakeups
+ - AC adapter status updates
+ - Battery status updates
+
endif # X86_32

config AMD_NB
diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile
index 8922b9b..fd332c5 100644
--- a/arch/x86/platform/olpc/Makefile
+++ b/arch/x86/platform/olpc/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o
obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o xo1-wakeup.o
obj-$(CONFIG_OLPC_XO1_RTC) += olpc-xo1-rtc.o
obj-$(CONFIG_OLPC_XO1_SCI) += olpc-xo1-sci.o
+obj-$(CONFIG_OLPC_XO15_SCI) += olpc-xo15-sci.o
diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c
new file mode 100644
index 0000000..a5990eb
--- /dev/null
+++ b/arch/x86/platform/olpc/olpc-xo15-sci.c
@@ -0,0 +1,168 @@
+/*
+ * Support for OLPC XO-1.5 System Control Interrupts (SCI)
+ *
+ * Copyright (C) 2009-2010 One Laptop per Child
+ *
+ * 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 <linux/device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/power_supply.h>
+
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <asm/olpc.h>
+
+#define DRV_NAME "olpc-xo15-sci"
+#define PFX DRV_NAME ": "
+#define XO15_SCI_CLASS DRV_NAME
+#define XO15_SCI_DEVICE_NAME "OLPC XO-1.5 SCI"
+
+static unsigned long xo15_sci_gpe;
+
+static void battery_status_changed(void)
+{
+ struct power_supply *psy = power_supply_get_by_name("olpc-battery");
+
+ if (psy) {
+ power_supply_changed(psy);
+ put_device(psy->dev);
+ }
+}
+
+static void ac_status_changed(void)
+{
+ struct power_supply *psy = power_supply_get_by_name("olpc-ac");
+
+ if (psy) {
+ power_supply_changed(psy);
+ put_device(psy->dev);
+ }
+}
+
+static void process_sci_queue(void)
+{
+ u16 data;
+ int r;
+
+ do {
+ r = olpc_ec_sci_query(&data);
+ if (r || !data)
+ break;
+
+ pr_debug(PFX "SCI 0x%x received\n", data);
+
+ switch (data) {
+ case EC_SCI_SRC_BATERR:
+ case EC_SCI_SRC_BATSOC:
+ case EC_SCI_SRC_BATTERY:
+ case EC_SCI_SRC_BATCRIT:
+ battery_status_changed();
+ break;
+ case EC_SCI_SRC_ACPWR:
+ ac_status_changed();
+ break;
+ }
+ } while (data);
+
+ if (r)
+ pr_err(PFX "Failed to clear SCI queue");
+}
+
+static void process_sci_queue_work(struct work_struct *work)
+{
+ process_sci_queue();
+}
+
+static DECLARE_WORK(sci_work, process_sci_queue_work);
+
+static u32 xo15_sci_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context)
+{
+ schedule_work(&sci_work);
+ return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
+}
+
+static int xo15_sci_add(struct acpi_device *device)
+{
+ unsigned long long tmp;
+ acpi_status status;
+
+ if (!device)
+ return -EINVAL;
+
+ strcpy(acpi_device_name(device), XO15_SCI_DEVICE_NAME);
+ strcpy(acpi_device_class(device), XO15_SCI_CLASS);
+
+ /* Get GPE bit assignment (EC events). */
+ status = acpi_evaluate_integer(device->handle, "_GPE", NULL, &tmp);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+
+ xo15_sci_gpe = tmp;
+ status = acpi_install_gpe_handler(NULL, xo15_sci_gpe,
+ ACPI_GPE_EDGE_TRIGGERED,
+ xo15_sci_gpe_handler, device);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ dev_info(&device->dev, "Initialized, GPE = 0x%lx\n", xo15_sci_gpe);
+
+ /* Flush queue, and enable all SCI events */
+ process_sci_queue();
+ olpc_ec_mask_write(EC_SCI_SRC_ALL);
+
+ acpi_enable_gpe(NULL, xo15_sci_gpe);
+
+ /* Enable wake-on-EC */
+ if (device->wakeup.flags.valid)
+ device_set_wakeup_enable(&device->dev, true);
+
+ return 0;
+}
+
+static int xo15_sci_remove(struct acpi_device *device, int type)
+{
+ acpi_disable_gpe(NULL, xo15_sci_gpe);
+ acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler);
+ cancel_work_sync(&sci_work);
+ return 0;
+}
+
+static int xo15_sci_resume(struct acpi_device *device)
+{
+ /* Enable all EC events */
+ olpc_ec_mask_write(EC_SCI_SRC_ALL);
+
+ /* Power/battery status might have changed */
+ battery_status_changed();
+ ac_status_changed();
+
+ return 0;
+}
+
+static const struct acpi_device_id xo15_sci_device_ids[] = {
+ {"XO15EC", 0},
+ {"", 0},
+};
+
+static struct acpi_driver xo15_sci_drv = {
+ .name = DRV_NAME,
+ .class = XO15_SCI_CLASS,
+ .ids = xo15_sci_device_ids,
+ .ops = {
+ .add = xo15_sci_add,
+ .remove = xo15_sci_remove,
+ .resume = xo15_sci_resume,
+ },
+};
+
+static int __init xo15_sci_init(void)
+{
+ return acpi_bus_register_driver(&xo15_sci_drv);
+}
+device_initcall(xo15_sci_init);
diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c
index 72fd041..8b9940e 100644
--- a/arch/x86/platform/olpc/olpc.c
+++ b/arch/x86/platform/olpc/olpc.c
@@ -222,6 +222,15 @@ bool olpc_ec_wakeup_available(void)
return true;
#endif

+ /*
+ * XO-1.5 EC wakeups are available when olpc-xo15-sci driver is
+ * compiled in
+ */
+#ifdef CONFIG_OLPC_XO15_SCI
+ if (olpc_platform_info.boardrev >= olpc_board_pre(0xd0)) /* XO-1.5 */
+ return true;
+#endif
+
return false;
}
EXPORT_SYMBOL_GPL(olpc_ec_wakeup_available);
--
1.7.4.4

2011-04-30 13:01:52

by Daniel Drake

[permalink] [raw]
Subject: [PATCH 07/11] x86, olpc-xo1-sci: Add GPE handler and ebook switch functionality

The EC in the OLPC XO-1 delivers GPE events to provide various
notifications. Add the basic code for GPE/EC event processing and
enable the ebook switch, which can be used as a wakeup source.

Signed-off-by: Daniel Drake <[email protected]>
---
arch/x86/Kconfig | 2 +
arch/x86/include/asm/olpc.h | 5 +-
arch/x86/platform/olpc/olpc-xo1-sci.c | 170 ++++++++++++++++++++++++++++++++-
include/linux/cs5535.h | 8 ++
4 files changed, 182 insertions(+), 3 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index c273c62..0e62cd5 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2077,7 +2077,9 @@ config OLPC_XO1_SCI
depends on OLPC && OLPC_XO1_PM
---help---
Add support for SCI-based features of the OLPC XO-1 laptop:
+ - EC-driven system wakeups
- Power button
+ - Ebook switch

endif # X86_32

diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h
index 0e56d01..87bdbca 100644
--- a/arch/x86/include/asm/olpc.h
+++ b/arch/x86/include/asm/olpc.h
@@ -111,6 +111,7 @@ extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen,
#define EC_WRITE_SCI_MASK 0x1b
#define EC_WAKE_UP_WLAN 0x24
#define EC_WLAN_LEAVE_RESET 0x25
+#define EC_READ_EB_MODE 0x2a
#define EC_SET_SCI_INHIBIT 0x32
#define EC_SET_SCI_INHIBIT_RELEASE 0x34
#define EC_WLAN_ENTER_RESET 0x35
@@ -144,7 +145,7 @@ extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen,
#define OLPC_GPIO_SMB_CLK 14
#define OLPC_GPIO_SMB_DATA 15
#define OLPC_GPIO_WORKAUX geode_gpio(24)
-#define OLPC_GPIO_LID geode_gpio(26)
-#define OLPC_GPIO_ECSCI geode_gpio(27)
+#define OLPC_GPIO_LID 26
+#define OLPC_GPIO_ECSCI 27

#endif /* _ASM_X86_OLPC_H */
diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c
index 8fbf961..9de2a00 100644
--- a/arch/x86/platform/olpc/olpc-xo1-sci.c
+++ b/arch/x86/platform/olpc/olpc-xo1-sci.c
@@ -12,12 +12,15 @@
*/

#include <linux/cs5535.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/mfd/core.h>
#include <linux/suspend.h>
+#include <linux/workqueue.h>

#include <asm/io.h>
#include <asm/msr.h>
@@ -28,8 +31,60 @@

static unsigned long acpi_base;
static struct input_dev *power_button_idev;
+static struct input_dev *ebook_switch_idev;
+
static int sci_irq;

+/* Report current ebook switch state through input layer */
+static void send_ebook_state(void)
+{
+ unsigned char state;
+
+ if (olpc_ec_cmd(EC_READ_EB_MODE, NULL, 0, &state, 1)) {
+ pr_err(PFX "failed to get ebook state\n");
+ return;
+ }
+
+ input_report_switch(ebook_switch_idev, SW_TABLET_MODE, state);
+ input_sync(ebook_switch_idev);
+}
+
+/*
+ * Process all items in the EC's SCI queue.
+ *
+ * This is handled in a workqueue because olpc_ec_cmd can be slow (and
+ * can even timeout).
+ *
+ * If propagate_events is false, the queue is drained without events being
+ * generated for the interrupts.
+ */
+static void process_sci_queue(bool propagate_events)
+{
+ int r;
+ u16 data;
+
+ do {
+ r = olpc_ec_sci_query(&data);
+ if (r || !data)
+ break;
+
+ pr_debug(PFX "SCI 0x%x received\n", data);
+
+ if (data == EC_SCI_SRC_EBOOK && propagate_events)
+ send_ebook_state();
+ } while (data);
+
+ if (r)
+ pr_err(PFX "Failed to clear SCI queue");
+}
+
+static void process_sci_queue_work(struct work_struct *work)
+{
+ process_sci_queue(true);
+}
+
+static DECLARE_WORK(sci_work, process_sci_queue_work);
+
static irqreturn_t xo1_sci_intr(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
@@ -51,6 +106,11 @@ static irqreturn_t xo1_sci_intr(int irq, void *dev_id)
input_sync(power_button_idev);
}

+ if (gpe & CS5536_GPIOM7_PME_FLAG) { /* EC GPIO */
+ cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS);
+ schedule_work(&sci_work);
+ }
+
return IRQ_HANDLED;
}

@@ -60,6 +120,19 @@ static int xo1_sci_suspend(struct platform_device *pdev, pm_message_t state)
olpc_xo1_pm_wakeup_set(CS5536_PM_PWRBTN);
else
olpc_xo1_pm_wakeup_clear(CS5536_PM_PWRBTN);
+
+ if (device_may_wakeup(&ebook_switch_idev->dev))
+ olpc_ec_wakeup_set(EC_SCI_SRC_EBOOK);
+ else
+ olpc_ec_wakeup_clear(EC_SCI_SRC_EBOOK);
+
+ return 0;
+}
+
+static int xo1_sci_resume(struct platform_device *pdev)
+{
+ /* Enable all EC events */
+ olpc_ec_mask_write(EC_SCI_SRC_ALL);
return 0;
}

@@ -104,6 +177,37 @@ static int __devinit setup_sci_interrupt(struct platform_device *pdev)
return r;
}

+static int __devinit setup_ec_sci(void)
+{
+ int r;
+
+ r = gpio_request(OLPC_GPIO_ECSCI, "OLPC-ECSCI");
+ if (r)
+ return r;
+
+ gpio_direction_input(OLPC_GPIO_ECSCI);
+
+ /* Clear pending EC SCI events */
+ cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS);
+ cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_POSITIVE_EDGE_STS);
+
+ /* Enable EC SCI events */
+ cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_EVENTS_ENABLE);
+
+ /* Set the SCI to cause a PME event on group 7 */
+ cs5535_gpio_setup_event(OLPC_GPIO_ECSCI, 7, 1);
+
+ /* And have group 7 also fire the SCI interrupt */
+ cs5535_gpio_set_irq(7, sci_irq);
+
+ return 0;
+}
+
+static void free_ec_sci(void)
+{
+ gpio_free(OLPC_GPIO_ECSCI);
+}
+
static int __devinit setup_power_button(struct platform_device *pdev)
{
int r;
@@ -135,6 +239,37 @@ static void free_power_button(void)
input_free_device(power_button_idev);
}

+static int __devinit setup_ebook_switch(struct platform_device *pdev)
+{
+ int r;
+
+ ebook_switch_idev = input_allocate_device();
+ if (!ebook_switch_idev)
+ return -ENOMEM;
+
+ ebook_switch_idev->name = "EBook Switch";
+ ebook_switch_idev->phys = DRV_NAME "/input1";
+ set_bit(EV_SW, ebook_switch_idev->evbit);
+ set_bit(SW_TABLET_MODE, ebook_switch_idev->swbit);
+
+ ebook_switch_idev->dev.parent = &pdev->dev;
+ device_set_wakeup_capable(&ebook_switch_idev->dev, true);
+
+ r = input_register_device(ebook_switch_idev);
+ if (r) {
+ dev_err(&pdev->dev, "failed to register ebook switch: %d\n", r);
+ input_free_device(ebook_switch_idev);
+ }
+
+ return r;
+}
+
+static void free_ebook_switch(void)
+{
+ input_unregister_device(ebook_switch_idev);
+ input_free_device(ebook_switch_idev);
+}
+
static int __devinit xo1_sci_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -159,10 +294,39 @@ static int __devinit xo1_sci_probe(struct platform_device *pdev)
if (r)
return r;

+ r = setup_ebook_switch(pdev);
+ if (r)
+ goto err_ebook;
+
+ r = setup_ec_sci();
+ if (r)
+ goto err_ecsci;
+
+ /* Enable PME generation for EC-generated events */
+ outl(CS5536_GPIOM7_PME_EN, acpi_base + CS5536_PM_GPE0_EN);
+
+ /* Clear pending events */
+ outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
+ process_sci_queue(false);
+
+ /* Initial sync */
+ send_ebook_state();
+
r = setup_sci_interrupt(pdev);
if (r)
- free_power_button();
+ goto err_sci;

+ /* Enable all EC events */
+ olpc_ec_mask_write(EC_SCI_SRC_ALL);
+
+ return r;
+
+err_sci:
+ free_ec_sci();
+err_ecsci:
+ free_ebook_switch();
+err_ebook:
+ free_power_button();
return r;
}

@@ -170,6 +334,9 @@ static int __devexit xo1_sci_remove(struct platform_device *pdev)
{
mfd_cell_disable(pdev);
free_irq(sci_irq, pdev);
+ cancel_work_sync(&sci_work);
+ free_ec_sci();
+ free_ebook_switch();
free_power_button();
acpi_base = 0;
return 0;
@@ -182,6 +349,7 @@ static struct platform_driver xo1_sci_driver = {
.probe = xo1_sci_probe,
.remove = __devexit_p(xo1_sci_remove),
.suspend = xo1_sci_suspend,
+ .resume = xo1_sci_resume,
};

static int __init xo1_sci_init(void)
diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h
index 6f78235..95c16ac 100644
--- a/include/linux/cs5535.h
+++ b/include/linux/cs5535.h
@@ -73,6 +73,7 @@
#define CS5536_PM1_EN 0x02
#define CS5536_PM1_CNT 0x08
#define CS5536_PM_GPE0_STS 0x18
+#define CS5536_PM_GPE0_EN 0x1c

/* CS5536_PM1_STS bits */
#define CS5536_WAK_FLAG (1 << 15)
@@ -81,6 +82,13 @@
/* CS5536_PM1_EN bits */
#define CS5536_PM_PWRBTN (1 << 8)

+/* CS5536_PM_GPE0_STS bits */
+#define CS5536_GPIOM7_PME_FLAG (1 << 31)
+#define CS5536_GPIOM6_PME_FLAG (1 << 30)
+
+/* CS5536_PM_GPE0_EN bits */
+#define CS5536_GPIOM7_PME_EN (1 << 31)
+
/* VSA2 magic values */
#define VSA_VRC_INDEX 0xAC1C
#define VSA_VRC_DATA 0xAC1E
--
1.7.4.4

2011-04-30 17:07:58

by Andres Salomon

[permalink] [raw]
Subject: Re: [PATCH 0/11] OLPC Power Management

On Sat, 30 Apr 2011 13:32:19 +0100
Daniel Drake <[email protected]> wrote:

> Hi,
>
> These patches implement various power management features on the XO-1
> and XO-1.5 laptops.
>
> Review comments very welcome, I'll do my best to act upon them
> quickly.
>
> Daniel
>
>

Great, thanks for sending! Feel free to add my Acked-by to all of
these.

2011-05-14 19:09:36

by Daniel Drake

[permalink] [raw]
Subject: Re: [PATCH 0/11] OLPC Power Management

Hi,

On 30 April 2011 18:07, Andres Salomon <[email protected]> wrote:
> On Sat, 30 Apr 2011 13:32:19 +0100
> Daniel Drake <[email protected]> wrote:
>> These patches implement various power management features on the XO-1
>> and XO-1.5 laptops.
>
> Great, thanks for sending! ?Feel free to add my Acked-by to all of
> these.
>

Just a quick reminder on these patches - still awaiting review
comments. I'm available to make changes based on people's comments.

Thanks,
Daniel

Subject: Re: [PATCH 07/11] x86, olpc-xo1-sci: Add GPE handler and ebook switch functionality

* Daniel Drake | 2011-04-30 13:32:26 [+0100]:

>diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c
>index 8fbf961..9de2a00 100644
>--- a/arch/x86/platform/olpc/olpc-xo1-sci.c
>+++ b/arch/x86/platform/olpc/olpc-xo1-sci.c
>@@ -104,6 +177,37 @@ static int __devinit setup_sci_interrupt(struct platform_device *pdev)
> return r;
> }
>
>+static int __devinit setup_ec_sci(void)
>+{
>+ int r;
>+
>+ r = gpio_request(OLPC_GPIO_ECSCI, "OLPC-ECSCI");
>+ if (r)
>+ return r;
>+
>+ gpio_direction_input(OLPC_GPIO_ECSCI);
>+
>+ /* Clear pending EC SCI events */
>+ cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS);
>+ cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_POSITIVE_EDGE_STS);

why not use the gpio framework here, i.e. gpio_set_value()?

>+
>+ /* Enable EC SCI events */
>+ cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_EVENTS_ENABLE);
>+
>+ /* Set the SCI to cause a PME event on group 7 */
>+ cs5535_gpio_setup_event(OLPC_GPIO_ECSCI, 7, 1);
>+
>+ /* And have group 7 also fire the SCI interrupt */
>+ cs5535_gpio_set_irq(7, sci_irq);

What do you do here? Could this be hidden behind a gpio irq_chip or is
this too olpc specific?

Sebastian

Subject: Re: [PATCH 10/11] x86, olpc: Add XO-1 RTC driver

* Daniel Drake | 2011-04-30 13:32:29 [+0100]:

>Add a driver to configure the XO-1 RTC via CS5536 MSRs, to be used as a
>system wakeup source via olpc-xo1-pm.
>
>Device detection is based on finding the relevant device tree node.
>
>Signed-off-by: Daniel Drake <[email protected]>
>Cc: [email protected]

Looks good to me.

Sebastian

Subject: Re: [PATCH 11/11] x86, olpc: Add XO-1.5 SCI driver

* Daniel Drake | 2011-04-30 13:32:30 [+0100]:

>+static DECLARE_WORK(sci_work, process_sci_queue_work);
>+
>+static u32 xo15_sci_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context)
>+{
>+ schedule_work(&sci_work);
>+ return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
>+}

I know this is too much to ask but acpi_install_gpe_handler() looks like
a custom irq demultiplexor which lacks the support for threaded irqs.

Sebastian

2011-05-16 16:16:21

by Andres Salomon

[permalink] [raw]
Subject: Re: [PATCH 07/11] x86, olpc-xo1-sci: Add GPE handler and ebook switch functionality

On Mon, 16 May 2011 11:08:13 +0200
Sebastian Andrzej Siewior <[email protected]> wrote:

> * Daniel Drake | 2011-04-30 13:32:26 [+0100]:
>
> >diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c
> >b/arch/x86/platform/olpc/olpc-xo1-sci.c index 8fbf961..9de2a00 100644
> >--- a/arch/x86/platform/olpc/olpc-xo1-sci.c
> >+++ b/arch/x86/platform/olpc/olpc-xo1-sci.c
> >@@ -104,6 +177,37 @@ static int __devinit setup_sci_interrupt(struct
> >platform_device *pdev)
> > return r;
> > }
> >
> >+static int __devinit setup_ec_sci(void)
> >+{
> >+ int r;
> >+
> >+ r = gpio_request(OLPC_GPIO_ECSCI, "OLPC-ECSCI");
> >+ if (r)
> >+ return r;
> >+
> >+ gpio_direction_input(OLPC_GPIO_ECSCI);
> >+
> >+ /* Clear pending EC SCI events */
> >+ cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS);
> >+ cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_POSITIVE_EDGE_STS);
>
> why not use the gpio framework here, i.e. gpio_set_value()?

The gpio framework is designed to work with toggling GPIO lines.
To deal with that, the cs5535 gpio driver (when toggling a GPIO line)
would set or clear CS5535_OUTPUT_VAL. The cs5535 hardware, however,
can have a whole range of values set per a single GPIO line; amongst
these, positive and negative edge status. Most users of the cs5535
GPIOs don't care about that, but those that do (like this SCI driver)
must use the custom cs5535 GPIO functions to set/clear it.

>
> >+
> >+ /* Enable EC SCI events */
> >+ cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_EVENTS_ENABLE);
> >+
> >+ /* Set the SCI to cause a PME event on group 7 */
> >+ cs5535_gpio_setup_event(OLPC_GPIO_ECSCI, 7, 1);
> >+
> >+ /* And have group 7 also fire the SCI interrupt */
> >+ cs5535_gpio_set_irq(7, sci_irq);
>
> What do you do here? Could this be hidden behind a gpio irq_chip or is
> this too olpc specific?
>
> Sebastian

2011-05-19 19:35:31

by Grant Likely

[permalink] [raw]
Subject: Re: [PATCH 10/11] x86, olpc: Add XO-1 RTC driver

On Sat, Apr 30, 2011 at 01:32:29PM +0100, Daniel Drake wrote:
> Add a driver to configure the XO-1 RTC via CS5536 MSRs, to be used as a
> system wakeup source via olpc-xo1-pm.
>
> Device detection is based on finding the relevant device tree node.
>
> Signed-off-by: Daniel Drake <[email protected]>
> Cc: [email protected]

Looks good to me.

Acked-by: Grant Likely <[email protected]>

> ---
> .../devicetree/bindings/rtc/olpc-xo1-rtc.txt | 5 +
> arch/x86/Kconfig | 7 ++
> arch/x86/platform/olpc/Makefile | 1 +
> arch/x86/platform/olpc/olpc-xo1-rtc.c | 82 ++++++++++++++++++++
> include/linux/cs5535.h | 5 +
> 5 files changed, 100 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt
> create mode 100644 arch/x86/platform/olpc/olpc-xo1-rtc.c
>
> diff --git a/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt b/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt
> new file mode 100644
> index 0000000..a2891ce
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt
> @@ -0,0 +1,5 @@
> +OLPC XO-1 RTC
> +~~~~~~~~~~~~~
> +
> +Required properties:
> + - compatible : "olpc,xo1-rtc"
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 583e1c5..eb9f377 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -2072,6 +2072,13 @@ config OLPC_XO1_PM
> ---help---
> Add support for poweroff and suspend of the OLPC XO-1 laptop.
>
> +config OLPC_XO1_RTC
> + bool "OLPC XO-1 Real Time Clock"
> + depends on OLPC_XO1_PM && RTC_DRV_CMOS
> + ---help---
> + Add support for the XO-1 real time clock, which can be used as a
> + programmable wakeup source.
> +
> config OLPC_XO1_SCI
> bool "OLPC XO-1 SCI extras"
> depends on OLPC && OLPC_XO1_PM && POWER_SUPPLY
> diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile
> index 1ec5ade..8922b9b 100644
> --- a/arch/x86/platform/olpc/Makefile
> +++ b/arch/x86/platform/olpc/Makefile
> @@ -1,3 +1,4 @@
> obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o
> obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o xo1-wakeup.o
> +obj-$(CONFIG_OLPC_XO1_RTC) += olpc-xo1-rtc.o
> obj-$(CONFIG_OLPC_XO1_SCI) += olpc-xo1-sci.o
> diff --git a/arch/x86/platform/olpc/olpc-xo1-rtc.c b/arch/x86/platform/olpc/olpc-xo1-rtc.c
> new file mode 100644
> index 0000000..476cc98
> --- /dev/null
> +++ b/arch/x86/platform/olpc/olpc-xo1-rtc.c
> @@ -0,0 +1,82 @@
> +/*
> + * Support for OLPC XO-1 Real Time Clock (RTC)
> + *
> + * Copyright (C) 2011 One Laptop per Child
> + *
> + * 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 <linux/mc146818rtc.h>
> +#include <linux/platform_device.h>
> +#include <linux/rtc.h>
> +#include <linux/of.h>
> +
> +#include <asm/msr.h>
> +#include <asm/olpc.h>
> +
> +static void rtc_wake_on(struct device *dev)
> +{
> + olpc_xo1_pm_wakeup_set(CS5536_PM_RTC);
> +}
> +
> +static void rtc_wake_off(struct device *dev)
> +{
> + olpc_xo1_pm_wakeup_clear(CS5536_PM_RTC);
> +}
> +
> +static struct resource rtc_platform_resource[] = {
> + [0] = {
> + .start = RTC_PORT(0),
> + .end = RTC_PORT(1),
> + .flags = IORESOURCE_IO,
> + },
> + [1] = {
> + .start = RTC_IRQ,
> + .end = RTC_IRQ,
> + .flags = IORESOURCE_IRQ,
> + }
> +};
> +
> +static struct cmos_rtc_board_info rtc_info = {
> + .rtc_day_alarm = 0,
> + .rtc_mon_alarm = 0,
> + .rtc_century = 0,
> + .wake_on = rtc_wake_on,
> + .wake_off = rtc_wake_off,
> +};
> +
> +static struct platform_device xo1_rtc_device = {
> + .name = "rtc_cmos",
> + .id = -1,
> + .num_resources = ARRAY_SIZE(rtc_platform_resource),
> + .dev.platform_data = &rtc_info,
> + .resource = rtc_platform_resource,
> +};
> +
> +static int __init xo1_rtc_init(void)
> +{
> + int r;
> + struct device_node *node;
> +
> + node = of_find_compatible_node(NULL, NULL, "olpc,xo1-rtc");
> + if (!node)
> + return 0;
> + of_node_put(node);
> +
> + pr_info("olpc-xo1-rtc: Initializing OLPC XO-1 RTC\n");
> + rdmsrl(MSR_RTC_DOMA_OFFSET, rtc_info.rtc_day_alarm);
> + rdmsrl(MSR_RTC_MONA_OFFSET, rtc_info.rtc_mon_alarm);
> + rdmsrl(MSR_RTC_CEN_OFFSET, rtc_info.rtc_century);
> +
> + r = platform_device_register(&xo1_rtc_device);
> + if (r)
> + return r;
> +
> + device_init_wakeup(&xo1_rtc_device.dev, 1);
> + return 0;
> +}
> +arch_initcall(xo1_rtc_init);
> +
> diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h
> index f2b4a2f..3f98d81 100644
> --- a/include/linux/cs5535.h
> +++ b/include/linux/cs5535.h
> @@ -38,6 +38,10 @@
> #define MSR_MFGPT_NR 0x51400029
> #define MSR_MFGPT_SETUP 0x5140002B
>
> +#define MSR_RTC_DOMA_OFFSET 0x51400055
> +#define MSR_RTC_MONA_OFFSET 0x51400056
> +#define MSR_RTC_CEN_OFFSET 0x51400057
> +
> #define MSR_LX_SPARE_MSR 0x80000011 /* DC-specific */
>
> #define MSR_GX_GLD_MSR_CONFIG 0xC0002001
> @@ -81,6 +85,7 @@
>
> /* CS5536_PM1_EN bits */
> #define CS5536_PM_PWRBTN (1 << 8)
> +#define CS5536_PM_RTC (1 << 10)
>
> /* CS5536_PM_GPE0_STS bits */
> #define CS5536_GPIOM7_PME_FLAG (1 << 31)
> --
> 1.7.4.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/

2011-05-24 21:40:29

by Daniel Drake

[permalink] [raw]
Subject: Re: [PATCH 07/11] x86, olpc-xo1-sci: Add GPE handler and ebook switch functionality

On 16 May 2011 10:08, Sebastian Andrzej Siewior <[email protected]> wrote:
>>+
>>+ ? ? ?/* Enable EC SCI events */
>>+ ? ? ?cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_EVENTS_ENABLE);
>>+
>>+ ? ? ?/* Set the SCI to cause a PME event on group 7 */
>>+ ? ? ?cs5535_gpio_setup_event(OLPC_GPIO_ECSCI, 7, 1);
>>+
>>+ ? ? ?/* And have group 7 also fire the SCI interrupt */
>>+ ? ? ?cs5535_gpio_set_irq(7, sci_irq);
>
> What do you do here? Could this be hidden behind a gpio irq_chip or is
> this too olpc specific?

Thanks for looking at these patches.

I looked into irq_chip alongside the CS5535 docs. Initially this
looked like a good suggestion - any CS5535 GPIO can be mapped to one
of 8 PIC-level interrupts, which seems like a good match for irq_chip.

However, this isn't what we're doing here.

CS5535 GPIOs can either be mapped to interrupts, *or* to a Power
Management Event (PME). A PME is a CS5535-specific concept; its an
event that will bring the system out of suspend if it is sleeping. In
this case, we are mapping a GPIO to a PME. I'm not aware of any
appropriate Linux abstraction for this. Thats all done in
cs5535_gpio_setup_event().

The CS5535 also has a highly programmable PIC, with loads of possible
input and mapping options. The call to cs5535_gpio_set_irq() then
programs the PIC to map PME input to an IRQ line. This function is
both misnamed and misplaced; it is unrelated to GPIOs and is simply
one of the many generic knobs on the PIC. If there was one, this
function should belong in a cs5535-pic driver, named something
according to "IRQ Mapper Unrestricted Z Select High".

So, I'm left feeling that irq_chip isn't an appropriate abstraction,
because the mapping made to the IRQ line is indirect via PME events,
and that is significant in the context of this driver (where wakeup
control is a big thing). However, it may be worthwhile moving
cs5535_gpio_set_irq() into cs5535.h as a static inline with a more
appropriate name. I could also add some explanatory comments in this
part of the code.

What do you think?

Thanks,
Daniel

2011-05-24 21:52:10

by Daniel Drake

[permalink] [raw]
Subject: Re: [PATCH 11/11] x86, olpc: Add XO-1.5 SCI driver

On 16 May 2011 10:24, Sebastian Andrzej Siewior <[email protected]> wrote:
> * Daniel Drake | 2011-04-30 13:32:30 [+0100]:
>
>>+static DECLARE_WORK(sci_work, process_sci_queue_work);
>>+
>>+static u32 xo15_sci_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context)
>>+{
>>+ ? ? ?schedule_work(&sci_work);
>>+ ? ? ?return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
>>+}
>
> I know this is too much to ask but acpi_install_gpe_handler() looks like
> a custom irq demultiplexor which lacks the support for threaded irqs.

Thanks for looking at the patch.

I guess you mean that ACPI should hide all this behind an irq_chip
abstraction. I had a quick look at doing this but not being very
familiar with ACPI nor irq_chip I don't think I'm the right person for
the task. Hopefully it is not seen as a merge requirement...

CCing ACPI list for comments.

Daniel

Subject: Re: [PATCH 07/11] x86, olpc-xo1-sci: Add GPE handler and ebook switch functionality

Daniel Drake wrote:
> I looked into irq_chip alongside the CS5535 docs. Initially this
> looked like a good suggestion - any CS5535 GPIO can be mapped to one
> of 8 PIC-level interrupts, which seems like a good match for irq_chip.
>
> However, this isn't what we're doing here.
>
> CS5535 GPIOs can either be mapped to interrupts, *or* to a Power
> Management Event (PME). A PME is a CS5535-specific concept; its an
> event that will bring the system out of suspend if it is sleeping. In
> this case, we are mapping a GPIO to a PME. I'm not aware of any
> appropriate Linux abstraction for this. Thats all done in
> cs5535_gpio_setup_event().

Is this something like pin multiplexing? So it is either a GPIO pin with
interrupt ability _or_ that PME thingy.
If so, would it make sense to hide it behind enable_irq_wake()? That call
could fail if you have already gpio_request() users for instance.

> The CS5535 also has a highly programmable PIC, with loads of possible
> input and mapping options. The call to cs5535_gpio_set_irq() then
> programs the PIC to map PME input to an IRQ line. This function is
> both misnamed and misplaced; it is unrelated to GPIOs and is simply
> one of the many generic knobs on the PIC. If there was one, this
> function should belong in a cs5535-pic driver, named something
> according to "IRQ Mapper Unrestricted Z Select High".

And once that is done, you can not use as a GPIO anymore, right?

> Thanks,
> Daniel

Sebastian

2011-05-31 20:48:19

by Daniel Drake

[permalink] [raw]
Subject: Re: [PATCH 07/11] x86, olpc-xo1-sci: Add GPE handler and ebook switch functionality

On 31 May 2011 12:28, Sebastian Andrzej Siewior <[email protected]> wrote:
> Is this something like pin multiplexing? So it is either a GPIO pin with
> interrupt ability _or_ that PME thingy.

No, it's not multiplexing in that sense. It is either a GPIO that
generates an interrupt, or a GPIO that generates a PME.

We use it for both its PME functionality and its GPIO functionality.

Thanks,
Daniel

2011-06-09 00:25:21

by Andres Salomon

[permalink] [raw]
Subject: Re: [PATCH 07/11] x86, olpc-xo1-sci: Add GPE handler and ebook switch functionality

On Tue, 31 May 2011 21:48:17 +0100
Daniel Drake <[email protected]> wrote:

> On 31 May 2011 12:28, Sebastian Andrzej Siewior
> <[email protected]> wrote:
> > Is this something like pin multiplexing? So it is either a GPIO pin
> > with interrupt ability _or_ that PME thingy.
>
> No, it's not multiplexing in that sense. It is either a GPIO that
> generates an interrupt, or a GPIO that generates a PME.
>
> We use it for both its PME functionality and its GPIO functionality.
>

So what's the status of this series at this point? Is there any
reason to keep them out of x86 tip? We've got an Ack from Grant so
far..