Commit fa8f53ace4af9470d8414427cb3dc3c0ffc4f182

Authored by Linus Torvalds

Merge branch 'x86-olpc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'x86-olpc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  x86, olpc-xo15-sci: Enable EC wakeup capability
  x86, olpc: Fix dependency on POWER_SUPPLY
  x86, olpc: Add XO-1.5 SCI driver
  x86, olpc: Add XO-1 RTC driver
  x86, olpc-xo1-sci: Propagate power supply/battery events
  x86, olpc-xo1-sci: Add lid switch functionality
  x86, olpc-xo1-sci: Add GPE handler and ebook switch functionality
  x86, olpc: EC SCI wakeup mask functionality
  x86, olpc: Add XO-1 SCI driver and power button control
  x86, olpc: Add XO-1 suspend/resume support
  x86, olpc: Rename olpc-xo1 to olpc-xo1-pm
  x86, olpc: Move CS5536-related constants to cs5535.h
  x86, olpc: Add missing elements to device tree

Showing 13 changed files Side-by-side Diff

Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt
  1 +OLPC XO-1 RTC
  2 +~~~~~~~~~~~~~
  3 +
  4 +Required properties:
  5 + - compatible : "olpc,xo1-rtc"
... ... @@ -2024,11 +2024,44 @@
2024 2024 Add support for detecting the unique features of the OLPC
2025 2025 XO hardware.
2026 2026  
2027   -config OLPC_XO1
2028   - tristate "OLPC XO-1 support"
2029   - depends on OLPC && MFD_CS5535
  2027 +config OLPC_XO1_PM
  2028 + bool "OLPC XO-1 Power Management"
  2029 + depends on OLPC && MFD_CS5535 && PM_SLEEP
  2030 + select MFD_CORE
2030 2031 ---help---
2031   - Add support for non-essential features of the OLPC XO-1 laptop.
  2032 + Add support for poweroff and suspend of the OLPC XO-1 laptop.
  2033 +
  2034 +config OLPC_XO1_RTC
  2035 + bool "OLPC XO-1 Real Time Clock"
  2036 + depends on OLPC_XO1_PM && RTC_DRV_CMOS
  2037 + ---help---
  2038 + Add support for the XO-1 real time clock, which can be used as a
  2039 + programmable wakeup source.
  2040 +
  2041 +config OLPC_XO1_SCI
  2042 + bool "OLPC XO-1 SCI extras"
  2043 + depends on OLPC && OLPC_XO1_PM
  2044 + select POWER_SUPPLY
  2045 + select GPIO_CS5535
  2046 + select MFD_CORE
  2047 + ---help---
  2048 + Add support for SCI-based features of the OLPC XO-1 laptop:
  2049 + - EC-driven system wakeups
  2050 + - Power button
  2051 + - Ebook switch
  2052 + - Lid switch
  2053 + - AC adapter status updates
  2054 + - Battery status updates
  2055 +
  2056 +config OLPC_XO15_SCI
  2057 + bool "OLPC XO-1.5 SCI extras"
  2058 + depends on OLPC && ACPI
  2059 + select POWER_SUPPLY
  2060 + ---help---
  2061 + Add support for SCI-based features of the OLPC XO-1.5 laptop:
  2062 + - EC-driven system wakeups
  2063 + - AC adapter status updates
  2064 + - Battery status updates
2032 2065  
2033 2066 endif # X86_32
2034 2067  
arch/x86/include/asm/olpc.h
... ... @@ -13,6 +13,7 @@
13 13  
14 14 #define OLPC_F_PRESENT 0x01
15 15 #define OLPC_F_DCON 0x02
  16 +#define OLPC_F_EC_WIDE_SCI 0x04
16 17  
17 18 #ifdef CONFIG_OLPC
18 19  
... ... @@ -62,6 +63,13 @@
62 63 return olpc_platform_info.boardrev >= rev;
63 64 }
64 65  
  66 +extern void olpc_ec_wakeup_set(u16 value);
  67 +extern void olpc_ec_wakeup_clear(u16 value);
  68 +extern bool olpc_ec_wakeup_available(void);
  69 +
  70 +extern int olpc_ec_mask_write(u16 bits);
  71 +extern int olpc_ec_sci_query(u16 *sci_value);
  72 +
65 73 #else
66 74  
67 75 static inline int machine_is_olpc(void)
68 76  
... ... @@ -74,8 +82,22 @@
74 82 return 0;
75 83 }
76 84  
  85 +static inline void olpc_ec_wakeup_set(u16 value) { }
  86 +static inline void olpc_ec_wakeup_clear(u16 value) { }
  87 +
  88 +static inline bool olpc_ec_wakeup_available(void)
  89 +{
  90 + return false;
  91 +}
  92 +
77 93 #endif
78 94  
  95 +#ifdef CONFIG_OLPC_XO1_PM
  96 +extern void do_olpc_suspend_lowlevel(void);
  97 +extern void olpc_xo1_pm_wakeup_set(u16 value);
  98 +extern void olpc_xo1_pm_wakeup_clear(u16 value);
  99 +#endif
  100 +
79 101 extern int pci_olpc_init(void);
80 102  
81 103 /* EC related functions */
82 104  
... ... @@ -83,14 +105,19 @@
83 105 extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen,
84 106 unsigned char *outbuf, size_t outlen);
85 107  
86   -extern int olpc_ec_mask_set(uint8_t bits);
87   -extern int olpc_ec_mask_unset(uint8_t bits);
88   -
89 108 /* EC commands */
90 109  
91   -#define EC_FIRMWARE_REV 0x08
92   -#define EC_WLAN_ENTER_RESET 0x35
93   -#define EC_WLAN_LEAVE_RESET 0x25
  110 +#define EC_FIRMWARE_REV 0x08
  111 +#define EC_WRITE_SCI_MASK 0x1b
  112 +#define EC_WAKE_UP_WLAN 0x24
  113 +#define EC_WLAN_LEAVE_RESET 0x25
  114 +#define EC_READ_EB_MODE 0x2a
  115 +#define EC_SET_SCI_INHIBIT 0x32
  116 +#define EC_SET_SCI_INHIBIT_RELEASE 0x34
  117 +#define EC_WLAN_ENTER_RESET 0x35
  118 +#define EC_WRITE_EXT_SCI_MASK 0x38
  119 +#define EC_SCI_QUERY 0x84
  120 +#define EC_EXT_SCI_QUERY 0x85
94 121  
95 122 /* SCI source values */
96 123  
97 124  
... ... @@ -99,10 +126,12 @@
99 126 #define EC_SCI_SRC_BATTERY 0x02
100 127 #define EC_SCI_SRC_BATSOC 0x04
101 128 #define EC_SCI_SRC_BATERR 0x08
102   -#define EC_SCI_SRC_EBOOK 0x10
103   -#define EC_SCI_SRC_WLAN 0x20
  129 +#define EC_SCI_SRC_EBOOK 0x10 /* XO-1 only */
  130 +#define EC_SCI_SRC_WLAN 0x20 /* XO-1 only */
104 131 #define EC_SCI_SRC_ACPWR 0x40
105   -#define EC_SCI_SRC_ALL 0x7F
  132 +#define EC_SCI_SRC_BATCRIT 0x80
  133 +#define EC_SCI_SRC_GPWAKE 0x100 /* XO-1.5 only */
  134 +#define EC_SCI_SRC_ALL 0x1FF
106 135  
107 136 /* GPIO assignments */
108 137  
... ... @@ -116,8 +145,8 @@
116 145 #define OLPC_GPIO_SMB_CLK 14
117 146 #define OLPC_GPIO_SMB_DATA 15
118 147 #define OLPC_GPIO_WORKAUX geode_gpio(24)
119   -#define OLPC_GPIO_LID geode_gpio(26)
120   -#define OLPC_GPIO_ECSCI geode_gpio(27)
  148 +#define OLPC_GPIO_LID 26
  149 +#define OLPC_GPIO_ECSCI 27
121 150  
122 151 #endif /* _ASM_X86_OLPC_H */
arch/x86/platform/olpc/Makefile
1 1 obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o
2   -obj-$(CONFIG_OLPC_XO1) += olpc-xo1.o
  2 +obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o xo1-wakeup.o
  3 +obj-$(CONFIG_OLPC_XO1_RTC) += olpc-xo1-rtc.o
  4 +obj-$(CONFIG_OLPC_XO1_SCI) += olpc-xo1-sci.o
  5 +obj-$(CONFIG_OLPC_XO15_SCI) += olpc-xo15-sci.o
arch/x86/platform/olpc/olpc-xo1-pm.c
  1 +/*
  2 + * Support for power management features of the OLPC XO-1 laptop
  3 + *
  4 + * Copyright (C) 2010 Andres Salomon <dilinger@queued.net>
  5 + * Copyright (C) 2010 One Laptop per Child
  6 + * Copyright (C) 2006 Red Hat, Inc.
  7 + * Copyright (C) 2006 Advanced Micro Devices, Inc.
  8 + *
  9 + * This program is free software; you can redistribute it and/or modify
  10 + * it under the terms of the GNU General Public License as published by
  11 + * the Free Software Foundation; either version 2 of the License, or
  12 + * (at your option) any later version.
  13 + */
  14 +
  15 +#include <linux/cs5535.h>
  16 +#include <linux/platform_device.h>
  17 +#include <linux/pm.h>
  18 +#include <linux/mfd/core.h>
  19 +#include <linux/suspend.h>
  20 +
  21 +#include <asm/io.h>
  22 +#include <asm/olpc.h>
  23 +
  24 +#define DRV_NAME "olpc-xo1-pm"
  25 +
  26 +static unsigned long acpi_base;
  27 +static unsigned long pms_base;
  28 +
  29 +static u16 wakeup_mask = CS5536_PM_PWRBTN;
  30 +
  31 +static struct {
  32 + unsigned long address;
  33 + unsigned short segment;
  34 +} ofw_bios_entry = { 0xF0000 + PAGE_OFFSET, __KERNEL_CS };
  35 +
  36 +/* Set bits in the wakeup mask */
  37 +void olpc_xo1_pm_wakeup_set(u16 value)
  38 +{
  39 + wakeup_mask |= value;
  40 +}
  41 +EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_set);
  42 +
  43 +/* Clear bits in the wakeup mask */
  44 +void olpc_xo1_pm_wakeup_clear(u16 value)
  45 +{
  46 + wakeup_mask &= ~value;
  47 +}
  48 +EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_clear);
  49 +
  50 +static int xo1_power_state_enter(suspend_state_t pm_state)
  51 +{
  52 + unsigned long saved_sci_mask;
  53 + int r;
  54 +
  55 + /* Only STR is supported */
  56 + if (pm_state != PM_SUSPEND_MEM)
  57 + return -EINVAL;
  58 +
  59 + r = olpc_ec_cmd(EC_SET_SCI_INHIBIT, NULL, 0, NULL, 0);
  60 + if (r)
  61 + return r;
  62 +
  63 + /*
  64 + * Save SCI mask (this gets lost since PM1_EN is used as a mask for
  65 + * wakeup events, which is not necessarily the same event set)
  66 + */
  67 + saved_sci_mask = inl(acpi_base + CS5536_PM1_STS);
  68 + saved_sci_mask &= 0xffff0000;
  69 +
  70 + /* Save CPU state */
  71 + do_olpc_suspend_lowlevel();
  72 +
  73 + /* Resume path starts here */
  74 +
  75 + /* Restore SCI mask (using dword access to CS5536_PM1_EN) */
  76 + outl(saved_sci_mask, acpi_base + CS5536_PM1_STS);
  77 +
  78 + /* Tell the EC to stop inhibiting SCIs */
  79 + olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0);
  80 +
  81 + /*
  82 + * Tell the wireless module to restart USB communication.
  83 + * Must be done twice.
  84 + */
  85 + olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
  86 + olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
  87 +
  88 + return 0;
  89 +}
  90 +
  91 +asmlinkage int xo1_do_sleep(u8 sleep_state)
  92 +{
  93 + void *pgd_addr = __va(read_cr3());
  94 +
  95 + /* Program wakeup mask (using dword access to CS5536_PM1_EN) */
  96 + outl(wakeup_mask << 16, acpi_base + CS5536_PM1_STS);
  97 +
  98 + __asm__("movl %0,%%eax" : : "r" (pgd_addr));
  99 + __asm__("call *(%%edi); cld"
  100 + : : "D" (&ofw_bios_entry));
  101 + __asm__("movb $0x34, %al\n\t"
  102 + "outb %al, $0x70\n\t"
  103 + "movb $0x30, %al\n\t"
  104 + "outb %al, $0x71\n\t");
  105 + return 0;
  106 +}
  107 +
  108 +static void xo1_power_off(void)
  109 +{
  110 + printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
  111 +
  112 + /* Enable all of these controls with 0 delay */
  113 + outl(0x40000000, pms_base + CS5536_PM_SCLK);
  114 + outl(0x40000000, pms_base + CS5536_PM_IN_SLPCTL);
  115 + outl(0x40000000, pms_base + CS5536_PM_WKXD);
  116 + outl(0x40000000, pms_base + CS5536_PM_WKD);
  117 +
  118 + /* Clear status bits (possibly unnecessary) */
  119 + outl(0x0002ffff, pms_base + CS5536_PM_SSC);
  120 + outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
  121 +
  122 + /* Write SLP_EN bit to start the machinery */
  123 + outl(0x00002000, acpi_base + CS5536_PM1_CNT);
  124 +}
  125 +
  126 +static int xo1_power_state_valid(suspend_state_t pm_state)
  127 +{
  128 + /* suspend-to-RAM only */
  129 + return pm_state == PM_SUSPEND_MEM;
  130 +}
  131 +
  132 +static const struct platform_suspend_ops xo1_suspend_ops = {
  133 + .valid = xo1_power_state_valid,
  134 + .enter = xo1_power_state_enter,
  135 +};
  136 +
  137 +static int __devinit xo1_pm_probe(struct platform_device *pdev)
  138 +{
  139 + struct resource *res;
  140 + int err;
  141 +
  142 + /* don't run on non-XOs */
  143 + if (!machine_is_olpc())
  144 + return -ENODEV;
  145 +
  146 + err = mfd_cell_enable(pdev);
  147 + if (err)
  148 + return err;
  149 +
  150 + res = platform_get_resource(pdev, IORESOURCE_IO, 0);
  151 + if (!res) {
  152 + dev_err(&pdev->dev, "can't fetch device resource info\n");
  153 + return -EIO;
  154 + }
  155 + if (strcmp(pdev->name, "cs5535-pms") == 0)
  156 + pms_base = res->start;
  157 + else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
  158 + acpi_base = res->start;
  159 +
  160 + /* If we have both addresses, we can override the poweroff hook */
  161 + if (pms_base && acpi_base) {
  162 + suspend_set_ops(&xo1_suspend_ops);
  163 + pm_power_off = xo1_power_off;
  164 + printk(KERN_INFO "OLPC XO-1 support registered\n");
  165 + }
  166 +
  167 + return 0;
  168 +}
  169 +
  170 +static int __devexit xo1_pm_remove(struct platform_device *pdev)
  171 +{
  172 + mfd_cell_disable(pdev);
  173 +
  174 + if (strcmp(pdev->name, "cs5535-pms") == 0)
  175 + pms_base = 0;
  176 + else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
  177 + acpi_base = 0;
  178 +
  179 + pm_power_off = NULL;
  180 + return 0;
  181 +}
  182 +
  183 +static struct platform_driver cs5535_pms_driver = {
  184 + .driver = {
  185 + .name = "cs5535-pms",
  186 + .owner = THIS_MODULE,
  187 + },
  188 + .probe = xo1_pm_probe,
  189 + .remove = __devexit_p(xo1_pm_remove),
  190 +};
  191 +
  192 +static struct platform_driver cs5535_acpi_driver = {
  193 + .driver = {
  194 + .name = "olpc-xo1-pm-acpi",
  195 + .owner = THIS_MODULE,
  196 + },
  197 + .probe = xo1_pm_probe,
  198 + .remove = __devexit_p(xo1_pm_remove),
  199 +};
  200 +
  201 +static int __init xo1_pm_init(void)
  202 +{
  203 + int r;
  204 +
  205 + r = platform_driver_register(&cs5535_pms_driver);
  206 + if (r)
  207 + return r;
  208 +
  209 + r = platform_driver_register(&cs5535_acpi_driver);
  210 + if (r)
  211 + platform_driver_unregister(&cs5535_pms_driver);
  212 +
  213 + return r;
  214 +}
  215 +arch_initcall(xo1_pm_init);
arch/x86/platform/olpc/olpc-xo1-rtc.c
  1 +/*
  2 + * Support for OLPC XO-1 Real Time Clock (RTC)
  3 + *
  4 + * Copyright (C) 2011 One Laptop per Child
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License as published by
  8 + * the Free Software Foundation; either version 2 of the License, or
  9 + * (at your option) any later version.
  10 + */
  11 +
  12 +#include <linux/mc146818rtc.h>
  13 +#include <linux/platform_device.h>
  14 +#include <linux/rtc.h>
  15 +#include <linux/of.h>
  16 +
  17 +#include <asm/msr.h>
  18 +#include <asm/olpc.h>
  19 +
  20 +static void rtc_wake_on(struct device *dev)
  21 +{
  22 + olpc_xo1_pm_wakeup_set(CS5536_PM_RTC);
  23 +}
  24 +
  25 +static void rtc_wake_off(struct device *dev)
  26 +{
  27 + olpc_xo1_pm_wakeup_clear(CS5536_PM_RTC);
  28 +}
  29 +
  30 +static struct resource rtc_platform_resource[] = {
  31 + [0] = {
  32 + .start = RTC_PORT(0),
  33 + .end = RTC_PORT(1),
  34 + .flags = IORESOURCE_IO,
  35 + },
  36 + [1] = {
  37 + .start = RTC_IRQ,
  38 + .end = RTC_IRQ,
  39 + .flags = IORESOURCE_IRQ,
  40 + }
  41 +};
  42 +
  43 +static struct cmos_rtc_board_info rtc_info = {
  44 + .rtc_day_alarm = 0,
  45 + .rtc_mon_alarm = 0,
  46 + .rtc_century = 0,
  47 + .wake_on = rtc_wake_on,
  48 + .wake_off = rtc_wake_off,
  49 +};
  50 +
  51 +static struct platform_device xo1_rtc_device = {
  52 + .name = "rtc_cmos",
  53 + .id = -1,
  54 + .num_resources = ARRAY_SIZE(rtc_platform_resource),
  55 + .dev.platform_data = &rtc_info,
  56 + .resource = rtc_platform_resource,
  57 +};
  58 +
  59 +static int __init xo1_rtc_init(void)
  60 +{
  61 + int r;
  62 + struct device_node *node;
  63 +
  64 + node = of_find_compatible_node(NULL, NULL, "olpc,xo1-rtc");
  65 + if (!node)
  66 + return 0;
  67 + of_node_put(node);
  68 +
  69 + pr_info("olpc-xo1-rtc: Initializing OLPC XO-1 RTC\n");
  70 + rdmsrl(MSR_RTC_DOMA_OFFSET, rtc_info.rtc_day_alarm);
  71 + rdmsrl(MSR_RTC_MONA_OFFSET, rtc_info.rtc_mon_alarm);
  72 + rdmsrl(MSR_RTC_CEN_OFFSET, rtc_info.rtc_century);
  73 +
  74 + r = platform_device_register(&xo1_rtc_device);
  75 + if (r)
  76 + return r;
  77 +
  78 + device_init_wakeup(&xo1_rtc_device.dev, 1);
  79 + return 0;
  80 +}
  81 +arch_initcall(xo1_rtc_init);
arch/x86/platform/olpc/olpc-xo1-sci.c
  1 +/*
  2 + * Support for OLPC XO-1 System Control Interrupts (SCI)
  3 + *
  4 + * Copyright (C) 2010 One Laptop per Child
  5 + * Copyright (C) 2006 Red Hat, Inc.
  6 + * Copyright (C) 2006 Advanced Micro Devices, Inc.
  7 + *
  8 + * This program is free software; you can redistribute it and/or modify
  9 + * it under the terms of the GNU General Public License as published by
  10 + * the Free Software Foundation; either version 2 of the License, or
  11 + * (at your option) any later version.
  12 + */
  13 +
  14 +#include <linux/cs5535.h>
  15 +#include <linux/device.h>
  16 +#include <linux/gpio.h>
  17 +#include <linux/input.h>
  18 +#include <linux/interrupt.h>
  19 +#include <linux/platform_device.h>
  20 +#include <linux/pm.h>
  21 +#include <linux/mfd/core.h>
  22 +#include <linux/power_supply.h>
  23 +#include <linux/suspend.h>
  24 +#include <linux/workqueue.h>
  25 +
  26 +#include <asm/io.h>
  27 +#include <asm/msr.h>
  28 +#include <asm/olpc.h>
  29 +
  30 +#define DRV_NAME "olpc-xo1-sci"
  31 +#define PFX DRV_NAME ": "
  32 +
  33 +static unsigned long acpi_base;
  34 +static struct input_dev *power_button_idev;
  35 +static struct input_dev *ebook_switch_idev;
  36 +static struct input_dev *lid_switch_idev;
  37 +
  38 +static int sci_irq;
  39 +
  40 +static bool lid_open;
  41 +static bool lid_inverted;
  42 +static int lid_wake_mode;
  43 +
  44 +enum lid_wake_modes {
  45 + LID_WAKE_ALWAYS,
  46 + LID_WAKE_OPEN,
  47 + LID_WAKE_CLOSE,
  48 +};
  49 +
  50 +static const char * const lid_wake_mode_names[] = {
  51 + [LID_WAKE_ALWAYS] = "always",
  52 + [LID_WAKE_OPEN] = "open",
  53 + [LID_WAKE_CLOSE] = "close",
  54 +};
  55 +
  56 +static void battery_status_changed(void)
  57 +{
  58 + struct power_supply *psy = power_supply_get_by_name("olpc-battery");
  59 +
  60 + if (psy) {
  61 + power_supply_changed(psy);
  62 + put_device(psy->dev);
  63 + }
  64 +}
  65 +
  66 +static void ac_status_changed(void)
  67 +{
  68 + struct power_supply *psy = power_supply_get_by_name("olpc-ac");
  69 +
  70 + if (psy) {
  71 + power_supply_changed(psy);
  72 + put_device(psy->dev);
  73 + }
  74 +}
  75 +
  76 +/* Report current ebook switch state through input layer */
  77 +static void send_ebook_state(void)
  78 +{
  79 + unsigned char state;
  80 +
  81 + if (olpc_ec_cmd(EC_READ_EB_MODE, NULL, 0, &state, 1)) {
  82 + pr_err(PFX "failed to get ebook state\n");
  83 + return;
  84 + }
  85 +
  86 + input_report_switch(ebook_switch_idev, SW_TABLET_MODE, state);
  87 + input_sync(ebook_switch_idev);
  88 +}
  89 +
  90 +static void flip_lid_inverter(void)
  91 +{
  92 + /* gpio is high; invert so we'll get l->h event interrupt */
  93 + if (lid_inverted)
  94 + cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_INPUT_INVERT);
  95 + else
  96 + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_INPUT_INVERT);
  97 + lid_inverted = !lid_inverted;
  98 +}
  99 +
  100 +static void detect_lid_state(void)
  101 +{
  102 + /*
  103 + * the edge detector hookup on the gpio inputs on the geode is
  104 + * odd, to say the least. See http://dev.laptop.org/ticket/5703
  105 + * for details, but in a nutshell: we don't use the edge
  106 + * detectors. instead, we make use of an anomoly: with the both
  107 + * edge detectors turned off, we still get an edge event on a
  108 + * positive edge transition. to take advantage of this, we use the
  109 + * front-end inverter to ensure that that's the edge we're always
  110 + * going to see next.
  111 + */
  112 +
  113 + int state;
  114 +
  115 + state = cs5535_gpio_isset(OLPC_GPIO_LID, GPIO_READ_BACK);
  116 + lid_open = !state ^ !lid_inverted; /* x ^^ y */
  117 + if (!state)
  118 + return;
  119 +
  120 + flip_lid_inverter();
  121 +}
  122 +
  123 +/* Report current lid switch state through input layer */
  124 +static void send_lid_state(void)
  125 +{
  126 + input_report_switch(lid_switch_idev, SW_LID, !lid_open);
  127 + input_sync(lid_switch_idev);
  128 +}
  129 +
  130 +static ssize_t lid_wake_mode_show(struct device *dev,
  131 + struct device_attribute *attr, char *buf)
  132 +{
  133 + const char *mode = lid_wake_mode_names[lid_wake_mode];
  134 + return sprintf(buf, "%s\n", mode);
  135 +}
  136 +static ssize_t lid_wake_mode_set(struct device *dev,
  137 + struct device_attribute *attr,
  138 + const char *buf, size_t count)
  139 +{
  140 + int i;
  141 + for (i = 0; i < ARRAY_SIZE(lid_wake_mode_names); i++) {
  142 + const char *mode = lid_wake_mode_names[i];
  143 + if (strlen(mode) != count || strncasecmp(mode, buf, count))
  144 + continue;
  145 +
  146 + lid_wake_mode = i;
  147 + return count;
  148 + }
  149 + return -EINVAL;
  150 +}
  151 +static DEVICE_ATTR(lid_wake_mode, S_IWUSR | S_IRUGO, lid_wake_mode_show,
  152 + lid_wake_mode_set);
  153 +
  154 +/*
  155 + * Process all items in the EC's SCI queue.
  156 + *
  157 + * This is handled in a workqueue because olpc_ec_cmd can be slow (and
  158 + * can even timeout).
  159 + *
  160 + * If propagate_events is false, the queue is drained without events being
  161 + * generated for the interrupts.
  162 + */
  163 +static void process_sci_queue(bool propagate_events)
  164 +{
  165 + int r;
  166 + u16 data;
  167 +
  168 + do {
  169 + r = olpc_ec_sci_query(&data);
  170 + if (r || !data)
  171 + break;
  172 +
  173 + pr_debug(PFX "SCI 0x%x received\n", data);
  174 +
  175 + switch (data) {
  176 + case EC_SCI_SRC_BATERR:
  177 + case EC_SCI_SRC_BATSOC:
  178 + case EC_SCI_SRC_BATTERY:
  179 + case EC_SCI_SRC_BATCRIT:
  180 + battery_status_changed();
  181 + break;
  182 + case EC_SCI_SRC_ACPWR:
  183 + ac_status_changed();
  184 + break;
  185 + }
  186 +
  187 + if (data == EC_SCI_SRC_EBOOK && propagate_events)
  188 + send_ebook_state();
  189 + } while (data);
  190 +
  191 + if (r)
  192 + pr_err(PFX "Failed to clear SCI queue");
  193 +}
  194 +
  195 +static void process_sci_queue_work(struct work_struct *work)
  196 +{
  197 + process_sci_queue(true);
  198 +}
  199 +
  200 +static DECLARE_WORK(sci_work, process_sci_queue_work);
  201 +
  202 +static irqreturn_t xo1_sci_intr(int irq, void *dev_id)
  203 +{
  204 + struct platform_device *pdev = dev_id;
  205 + u32 sts;
  206 + u32 gpe;
  207 +
  208 + sts = inl(acpi_base + CS5536_PM1_STS);
  209 + outl(sts | 0xffff, acpi_base + CS5536_PM1_STS);
  210 +
  211 + gpe = inl(acpi_base + CS5536_PM_GPE0_STS);
  212 + outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
  213 +
  214 + dev_dbg(&pdev->dev, "sts %x gpe %x\n", sts, gpe);
  215 +
  216 + if (sts & CS5536_PWRBTN_FLAG && !(sts & CS5536_WAK_FLAG)) {
  217 + input_report_key(power_button_idev, KEY_POWER, 1);
  218 + input_sync(power_button_idev);
  219 + input_report_key(power_button_idev, KEY_POWER, 0);
  220 + input_sync(power_button_idev);
  221 + }
  222 +
  223 + if (gpe & CS5536_GPIOM7_PME_FLAG) { /* EC GPIO */
  224 + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS);
  225 + schedule_work(&sci_work);
  226 + }
  227 +
  228 + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS);
  229 + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS);
  230 + detect_lid_state();
  231 + send_lid_state();
  232 +
  233 + return IRQ_HANDLED;
  234 +}
  235 +
  236 +static int xo1_sci_suspend(struct platform_device *pdev, pm_message_t state)
  237 +{
  238 + if (device_may_wakeup(&power_button_idev->dev))
  239 + olpc_xo1_pm_wakeup_set(CS5536_PM_PWRBTN);
  240 + else
  241 + olpc_xo1_pm_wakeup_clear(CS5536_PM_PWRBTN);
  242 +
  243 + if (device_may_wakeup(&ebook_switch_idev->dev))
  244 + olpc_ec_wakeup_set(EC_SCI_SRC_EBOOK);
  245 + else
  246 + olpc_ec_wakeup_clear(EC_SCI_SRC_EBOOK);
  247 +
  248 + if (!device_may_wakeup(&lid_switch_idev->dev)) {
  249 + cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
  250 + } else if ((lid_open && lid_wake_mode == LID_WAKE_OPEN) ||
  251 + (!lid_open && lid_wake_mode == LID_WAKE_CLOSE)) {
  252 + flip_lid_inverter();
  253 +
  254 + /* we may have just caused an event */
  255 + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS);
  256 + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS);
  257 +
  258 + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
  259 + }
  260 +
  261 + return 0;
  262 +}
  263 +
  264 +static int xo1_sci_resume(struct platform_device *pdev)
  265 +{
  266 + /*
  267 + * We don't know what may have happened while we were asleep.
  268 + * Reestablish our lid setup so we're sure to catch all transitions.
  269 + */
  270 + detect_lid_state();
  271 + send_lid_state();
  272 + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
  273 +
  274 + /* Enable all EC events */
  275 + olpc_ec_mask_write(EC_SCI_SRC_ALL);
  276 +
  277 + /* Power/battery status might have changed too */
  278 + battery_status_changed();
  279 + ac_status_changed();
  280 + return 0;
  281 +}
  282 +
  283 +static int __devinit setup_sci_interrupt(struct platform_device *pdev)
  284 +{
  285 + u32 lo, hi;
  286 + u32 sts;
  287 + int r;
  288 +
  289 + rdmsr(0x51400020, lo, hi);
  290 + sci_irq = (lo >> 20) & 15;
  291 +
  292 + if (sci_irq) {
  293 + dev_info(&pdev->dev, "SCI is mapped to IRQ %d\n", sci_irq);
  294 + } else {
  295 + /* Zero means masked */
  296 + dev_info(&pdev->dev, "SCI unmapped. Mapping to IRQ 3\n");
  297 + sci_irq = 3;
  298 + lo |= 0x00300000;
  299 + wrmsrl(0x51400020, lo);
  300 + }
  301 +
  302 + /* Select level triggered in PIC */
  303 + if (sci_irq < 8) {
  304 + lo = inb(CS5536_PIC_INT_SEL1);
  305 + lo |= 1 << sci_irq;
  306 + outb(lo, CS5536_PIC_INT_SEL1);
  307 + } else {
  308 + lo = inb(CS5536_PIC_INT_SEL2);
  309 + lo |= 1 << (sci_irq - 8);
  310 + outb(lo, CS5536_PIC_INT_SEL2);
  311 + }
  312 +
  313 + /* Enable SCI from power button, and clear pending interrupts */
  314 + sts = inl(acpi_base + CS5536_PM1_STS);
  315 + outl((CS5536_PM_PWRBTN << 16) | 0xffff, acpi_base + CS5536_PM1_STS);
  316 +
  317 + r = request_irq(sci_irq, xo1_sci_intr, 0, DRV_NAME, pdev);
  318 + if (r)
  319 + dev_err(&pdev->dev, "can't request interrupt\n");
  320 +
  321 + return r;
  322 +}
  323 +
  324 +static int __devinit setup_ec_sci(void)
  325 +{
  326 + int r;
  327 +
  328 + r = gpio_request(OLPC_GPIO_ECSCI, "OLPC-ECSCI");
  329 + if (r)
  330 + return r;
  331 +
  332 + gpio_direction_input(OLPC_GPIO_ECSCI);
  333 +
  334 + /* Clear pending EC SCI events */
  335 + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS);
  336 + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_POSITIVE_EDGE_STS);
  337 +
  338 + /*
  339 + * Enable EC SCI events, and map them to both a PME and the SCI
  340 + * interrupt.
  341 + *
  342 + * Ordinarily, in addition to functioning as GPIOs, Geode GPIOs can
  343 + * be mapped to regular interrupts *or* Geode-specific Power
  344 + * Management Events (PMEs) - events that bring the system out of
  345 + * suspend. In this case, we want both of those things - the system
  346 + * wakeup, *and* the ability to get an interrupt when an event occurs.
  347 + *
  348 + * To achieve this, we map the GPIO to a PME, and then we use one
  349 + * of the many generic knobs on the CS5535 PIC to additionally map the
  350 + * PME to the regular SCI interrupt line.
  351 + */
  352 + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_EVENTS_ENABLE);
  353 +
  354 + /* Set the SCI to cause a PME event on group 7 */
  355 + cs5535_gpio_setup_event(OLPC_GPIO_ECSCI, 7, 1);
  356 +
  357 + /* And have group 7 also fire the SCI interrupt */
  358 + cs5535_pic_unreqz_select_high(7, sci_irq);
  359 +
  360 + return 0;
  361 +}
  362 +
  363 +static void free_ec_sci(void)
  364 +{
  365 + gpio_free(OLPC_GPIO_ECSCI);
  366 +}
  367 +
  368 +static int __devinit setup_lid_events(void)
  369 +{
  370 + int r;
  371 +
  372 + r = gpio_request(OLPC_GPIO_LID, "OLPC-LID");
  373 + if (r)
  374 + return r;
  375 +
  376 + gpio_direction_input(OLPC_GPIO_LID);
  377 +
  378 + cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_INPUT_INVERT);
  379 + lid_inverted = 0;
  380 +
  381 + /* Clear edge detection and event enable for now */
  382 + cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
  383 + cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_EN);
  384 + cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_EN);
  385 + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS);
  386 + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS);
  387 +
  388 + /* Set the LID to cause an PME event on group 6 */
  389 + cs5535_gpio_setup_event(OLPC_GPIO_LID, 6, 1);
  390 +
  391 + /* Set PME group 6 to fire the SCI interrupt */
  392 + cs5535_gpio_set_irq(6, sci_irq);
  393 +
  394 + /* Enable the event */
  395 + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
  396 +
  397 + return 0;
  398 +}
  399 +
  400 +static void free_lid_events(void)
  401 +{
  402 + gpio_free(OLPC_GPIO_LID);
  403 +}
  404 +
  405 +static int __devinit setup_power_button(struct platform_device *pdev)
  406 +{
  407 + int r;
  408 +
  409 + power_button_idev = input_allocate_device();
  410 + if (!power_button_idev)
  411 + return -ENOMEM;
  412 +
  413 + power_button_idev->name = "Power Button";
  414 + power_button_idev->phys = DRV_NAME "/input0";
  415 + set_bit(EV_KEY, power_button_idev->evbit);
  416 + set_bit(KEY_POWER, power_button_idev->keybit);
  417 +
  418 + power_button_idev->dev.parent = &pdev->dev;
  419 + device_init_wakeup(&power_button_idev->dev, 1);
  420 +
  421 + r = input_register_device(power_button_idev);
  422 + if (r) {
  423 + dev_err(&pdev->dev, "failed to register power button: %d\n", r);
  424 + input_free_device(power_button_idev);
  425 + }
  426 +
  427 + return r;
  428 +}
  429 +
  430 +static void free_power_button(void)
  431 +{
  432 + input_unregister_device(power_button_idev);
  433 + input_free_device(power_button_idev);
  434 +}
  435 +
  436 +static int __devinit setup_ebook_switch(struct platform_device *pdev)
  437 +{
  438 + int r;
  439 +
  440 + ebook_switch_idev = input_allocate_device();
  441 + if (!ebook_switch_idev)
  442 + return -ENOMEM;
  443 +
  444 + ebook_switch_idev->name = "EBook Switch";
  445 + ebook_switch_idev->phys = DRV_NAME "/input1";
  446 + set_bit(EV_SW, ebook_switch_idev->evbit);
  447 + set_bit(SW_TABLET_MODE, ebook_switch_idev->swbit);
  448 +
  449 + ebook_switch_idev->dev.parent = &pdev->dev;
  450 + device_set_wakeup_capable(&ebook_switch_idev->dev, true);
  451 +
  452 + r = input_register_device(ebook_switch_idev);
  453 + if (r) {
  454 + dev_err(&pdev->dev, "failed to register ebook switch: %d\n", r);
  455 + input_free_device(ebook_switch_idev);
  456 + }
  457 +
  458 + return r;
  459 +}
  460 +
  461 +static void free_ebook_switch(void)
  462 +{
  463 + input_unregister_device(ebook_switch_idev);
  464 + input_free_device(ebook_switch_idev);
  465 +}
  466 +
  467 +static int __devinit setup_lid_switch(struct platform_device *pdev)
  468 +{
  469 + int r;
  470 +
  471 + lid_switch_idev = input_allocate_device();
  472 + if (!lid_switch_idev)
  473 + return -ENOMEM;
  474 +
  475 + lid_switch_idev->name = "Lid Switch";
  476 + lid_switch_idev->phys = DRV_NAME "/input2";
  477 + set_bit(EV_SW, lid_switch_idev->evbit);
  478 + set_bit(SW_LID, lid_switch_idev->swbit);
  479 +
  480 + lid_switch_idev->dev.parent = &pdev->dev;
  481 + device_set_wakeup_capable(&lid_switch_idev->dev, true);
  482 +
  483 + r = input_register_device(lid_switch_idev);
  484 + if (r) {
  485 + dev_err(&pdev->dev, "failed to register lid switch: %d\n", r);
  486 + goto err_register;
  487 + }
  488 +
  489 + r = device_create_file(&lid_switch_idev->dev, &dev_attr_lid_wake_mode);
  490 + if (r) {
  491 + dev_err(&pdev->dev, "failed to create wake mode attr: %d\n", r);
  492 + goto err_create_attr;
  493 + }
  494 +
  495 + return 0;
  496 +
  497 +err_create_attr:
  498 + input_unregister_device(lid_switch_idev);
  499 +err_register:
  500 + input_free_device(lid_switch_idev);
  501 + return r;
  502 +}
  503 +
  504 +static void free_lid_switch(void)
  505 +{
  506 + device_remove_file(&lid_switch_idev->dev, &dev_attr_lid_wake_mode);
  507 + input_unregister_device(lid_switch_idev);
  508 + input_free_device(lid_switch_idev);
  509 +}
  510 +
  511 +static int __devinit xo1_sci_probe(struct platform_device *pdev)
  512 +{
  513 + struct resource *res;
  514 + int r;
  515 +
  516 + /* don't run on non-XOs */
  517 + if (!machine_is_olpc())
  518 + return -ENODEV;
  519 +
  520 + r = mfd_cell_enable(pdev);
  521 + if (r)
  522 + return r;
  523 +
  524 + res = platform_get_resource(pdev, IORESOURCE_IO, 0);
  525 + if (!res) {
  526 + dev_err(&pdev->dev, "can't fetch device resource info\n");
  527 + return -EIO;
  528 + }
  529 + acpi_base = res->start;
  530 +
  531 + r = setup_power_button(pdev);
  532 + if (r)
  533 + return r;
  534 +
  535 + r = setup_ebook_switch(pdev);
  536 + if (r)
  537 + goto err_ebook;
  538 +
  539 + r = setup_lid_switch(pdev);
  540 + if (r)
  541 + goto err_lid;
  542 +
  543 + r = setup_lid_events();
  544 + if (r)
  545 + goto err_lidevt;
  546 +
  547 + r = setup_ec_sci();
  548 + if (r)
  549 + goto err_ecsci;
  550 +
  551 + /* Enable PME generation for EC-generated events */
  552 + outl(CS5536_GPIOM6_PME_EN | CS5536_GPIOM7_PME_EN,
  553 + acpi_base + CS5536_PM_GPE0_EN);
  554 +
  555 + /* Clear pending events */
  556 + outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
  557 + process_sci_queue(false);
  558 +
  559 + /* Initial sync */
  560 + send_ebook_state();
  561 + detect_lid_state();
  562 + send_lid_state();
  563 +
  564 + r = setup_sci_interrupt(pdev);
  565 + if (r)
  566 + goto err_sci;
  567 +
  568 + /* Enable all EC events */
  569 + olpc_ec_mask_write(EC_SCI_SRC_ALL);
  570 +
  571 + return r;
  572 +
  573 +err_sci:
  574 + free_ec_sci();
  575 +err_ecsci:
  576 + free_lid_events();
  577 +err_lidevt:
  578 + free_lid_switch();
  579 +err_lid:
  580 + free_ebook_switch();
  581 +err_ebook:
  582 + free_power_button();
  583 + return r;
  584 +}
  585 +
  586 +static int __devexit xo1_sci_remove(struct platform_device *pdev)
  587 +{
  588 + mfd_cell_disable(pdev);
  589 + free_irq(sci_irq, pdev);
  590 + cancel_work_sync(&sci_work);
  591 + free_ec_sci();
  592 + free_lid_events();
  593 + free_lid_switch();
  594 + free_ebook_switch();
  595 + free_power_button();
  596 + acpi_base = 0;
  597 + return 0;
  598 +}
  599 +
  600 +static struct platform_driver xo1_sci_driver = {
  601 + .driver = {
  602 + .name = "olpc-xo1-sci-acpi",
  603 + },
  604 + .probe = xo1_sci_probe,
  605 + .remove = __devexit_p(xo1_sci_remove),
  606 + .suspend = xo1_sci_suspend,
  607 + .resume = xo1_sci_resume,
  608 +};
  609 +
  610 +static int __init xo1_sci_init(void)
  611 +{
  612 + return platform_driver_register(&xo1_sci_driver);
  613 +}
  614 +arch_initcall(xo1_sci_init);
arch/x86/platform/olpc/olpc-xo1.c
1   -/*
2   - * Support for features of the OLPC XO-1 laptop
3   - *
4   - * Copyright (C) 2010 Andres Salomon <dilinger@queued.net>
5   - * Copyright (C) 2010 One Laptop per Child
6   - * Copyright (C) 2006 Red Hat, Inc.
7   - * Copyright (C) 2006 Advanced Micro Devices, Inc.
8   - *
9   - * This program is free software; you can redistribute it and/or modify
10   - * it under the terms of the GNU General Public License as published by
11   - * the Free Software Foundation; either version 2 of the License, or
12   - * (at your option) any later version.
13   - */
14   -
15   -#include <linux/module.h>
16   -#include <linux/platform_device.h>
17   -#include <linux/pm.h>
18   -#include <linux/mfd/core.h>
19   -
20   -#include <asm/io.h>
21   -#include <asm/olpc.h>
22   -
23   -#define DRV_NAME "olpc-xo1"
24   -
25   -/* PMC registers (PMS block) */
26   -#define PM_SCLK 0x10
27   -#define PM_IN_SLPCTL 0x20
28   -#define PM_WKXD 0x34
29   -#define PM_WKD 0x30
30   -#define PM_SSC 0x54
31   -
32   -/* PM registers (ACPI block) */
33   -#define PM1_CNT 0x08
34   -#define PM_GPE0_STS 0x18
35   -
36   -static unsigned long acpi_base;
37   -static unsigned long pms_base;
38   -
39   -static void xo1_power_off(void)
40   -{
41   - printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
42   -
43   - /* Enable all of these controls with 0 delay */
44   - outl(0x40000000, pms_base + PM_SCLK);
45   - outl(0x40000000, pms_base + PM_IN_SLPCTL);
46   - outl(0x40000000, pms_base + PM_WKXD);
47   - outl(0x40000000, pms_base + PM_WKD);
48   -
49   - /* Clear status bits (possibly unnecessary) */
50   - outl(0x0002ffff, pms_base + PM_SSC);
51   - outl(0xffffffff, acpi_base + PM_GPE0_STS);
52   -
53   - /* Write SLP_EN bit to start the machinery */
54   - outl(0x00002000, acpi_base + PM1_CNT);
55   -}
56   -
57   -static int __devinit olpc_xo1_probe(struct platform_device *pdev)
58   -{
59   - struct resource *res;
60   - int err;
61   -
62   - /* don't run on non-XOs */
63   - if (!machine_is_olpc())
64   - return -ENODEV;
65   -
66   - err = mfd_cell_enable(pdev);
67   - if (err)
68   - return err;
69   -
70   - res = platform_get_resource(pdev, IORESOURCE_IO, 0);
71   - if (!res) {
72   - dev_err(&pdev->dev, "can't fetch device resource info\n");
73   - return -EIO;
74   - }
75   - if (strcmp(pdev->name, "cs5535-pms") == 0)
76   - pms_base = res->start;
77   - else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
78   - acpi_base = res->start;
79   -
80   - /* If we have both addresses, we can override the poweroff hook */
81   - if (pms_base && acpi_base) {
82   - pm_power_off = xo1_power_off;
83   - printk(KERN_INFO "OLPC XO-1 support registered\n");
84   - }
85   -
86   - return 0;
87   -}
88   -
89   -static int __devexit olpc_xo1_remove(struct platform_device *pdev)
90   -{
91   - mfd_cell_disable(pdev);
92   -
93   - if (strcmp(pdev->name, "cs5535-pms") == 0)
94   - pms_base = 0;
95   - else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
96   - acpi_base = 0;
97   -
98   - pm_power_off = NULL;
99   - return 0;
100   -}
101   -
102   -static struct platform_driver cs5535_pms_drv = {
103   - .driver = {
104   - .name = "cs5535-pms",
105   - .owner = THIS_MODULE,
106   - },
107   - .probe = olpc_xo1_probe,
108   - .remove = __devexit_p(olpc_xo1_remove),
109   -};
110   -
111   -static struct platform_driver cs5535_acpi_drv = {
112   - .driver = {
113   - .name = "olpc-xo1-pm-acpi",
114   - .owner = THIS_MODULE,
115   - },
116   - .probe = olpc_xo1_probe,
117   - .remove = __devexit_p(olpc_xo1_remove),
118   -};
119   -
120   -static int __init olpc_xo1_init(void)
121   -{
122   - int r;
123   -
124   - r = platform_driver_register(&cs5535_pms_drv);
125   - if (r)
126   - return r;
127   -
128   - r = platform_driver_register(&cs5535_acpi_drv);
129   - if (r)
130   - platform_driver_unregister(&cs5535_pms_drv);
131   -
132   - return r;
133   -}
134   -
135   -static void __exit olpc_xo1_exit(void)
136   -{
137   - platform_driver_unregister(&cs5535_acpi_drv);
138   - platform_driver_unregister(&cs5535_pms_drv);
139   -}
140   -
141   -MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>");
142   -MODULE_LICENSE("GPL");
143   -MODULE_ALIAS("platform:cs5535-pms");
144   -
145   -module_init(olpc_xo1_init);
146   -module_exit(olpc_xo1_exit);
arch/x86/platform/olpc/olpc-xo15-sci.c
  1 +/*
  2 + * Support for OLPC XO-1.5 System Control Interrupts (SCI)
  3 + *
  4 + * Copyright (C) 2009-2010 One Laptop per Child
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License as published by
  8 + * the Free Software Foundation; either version 2 of the License, or
  9 + * (at your option) any later version.
  10 + */
  11 +
  12 +#include <linux/device.h>
  13 +#include <linux/slab.h>
  14 +#include <linux/workqueue.h>
  15 +#include <linux/power_supply.h>
  16 +
  17 +#include <acpi/acpi_bus.h>
  18 +#include <acpi/acpi_drivers.h>
  19 +#include <asm/olpc.h>
  20 +
  21 +#define DRV_NAME "olpc-xo15-sci"
  22 +#define PFX DRV_NAME ": "
  23 +#define XO15_SCI_CLASS DRV_NAME
  24 +#define XO15_SCI_DEVICE_NAME "OLPC XO-1.5 SCI"
  25 +
  26 +static unsigned long xo15_sci_gpe;
  27 +
  28 +static void battery_status_changed(void)
  29 +{
  30 + struct power_supply *psy = power_supply_get_by_name("olpc-battery");
  31 +
  32 + if (psy) {
  33 + power_supply_changed(psy);
  34 + put_device(psy->dev);
  35 + }
  36 +}
  37 +
  38 +static void ac_status_changed(void)
  39 +{
  40 + struct power_supply *psy = power_supply_get_by_name("olpc-ac");
  41 +
  42 + if (psy) {
  43 + power_supply_changed(psy);
  44 + put_device(psy->dev);
  45 + }
  46 +}
  47 +
  48 +static void process_sci_queue(void)
  49 +{
  50 + u16 data;
  51 + int r;
  52 +
  53 + do {
  54 + r = olpc_ec_sci_query(&data);
  55 + if (r || !data)
  56 + break;
  57 +
  58 + pr_debug(PFX "SCI 0x%x received\n", data);
  59 +
  60 + switch (data) {
  61 + case EC_SCI_SRC_BATERR:
  62 + case EC_SCI_SRC_BATSOC:
  63 + case EC_SCI_SRC_BATTERY:
  64 + case EC_SCI_SRC_BATCRIT:
  65 + battery_status_changed();
  66 + break;
  67 + case EC_SCI_SRC_ACPWR:
  68 + ac_status_changed();
  69 + break;
  70 + }
  71 + } while (data);
  72 +
  73 + if (r)
  74 + pr_err(PFX "Failed to clear SCI queue");
  75 +}
  76 +
  77 +static void process_sci_queue_work(struct work_struct *work)
  78 +{
  79 + process_sci_queue();
  80 +}
  81 +
  82 +static DECLARE_WORK(sci_work, process_sci_queue_work);
  83 +
  84 +static u32 xo15_sci_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context)
  85 +{
  86 + schedule_work(&sci_work);
  87 + return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
  88 +}
  89 +
  90 +static int xo15_sci_add(struct acpi_device *device)
  91 +{
  92 + unsigned long long tmp;
  93 + acpi_status status;
  94 +
  95 + if (!device)
  96 + return -EINVAL;
  97 +
  98 + strcpy(acpi_device_name(device), XO15_SCI_DEVICE_NAME);
  99 + strcpy(acpi_device_class(device), XO15_SCI_CLASS);
  100 +
  101 + /* Get GPE bit assignment (EC events). */
  102 + status = acpi_evaluate_integer(device->handle, "_GPE", NULL, &tmp);
  103 + if (ACPI_FAILURE(status))
  104 + return -EINVAL;
  105 +
  106 + xo15_sci_gpe = tmp;
  107 + status = acpi_install_gpe_handler(NULL, xo15_sci_gpe,
  108 + ACPI_GPE_EDGE_TRIGGERED,
  109 + xo15_sci_gpe_handler, device);
  110 + if (ACPI_FAILURE(status))
  111 + return -ENODEV;
  112 +
  113 + dev_info(&device->dev, "Initialized, GPE = 0x%lx\n", xo15_sci_gpe);
  114 +
  115 + /* Flush queue, and enable all SCI events */
  116 + process_sci_queue();
  117 + olpc_ec_mask_write(EC_SCI_SRC_ALL);
  118 +
  119 + acpi_enable_gpe(NULL, xo15_sci_gpe);
  120 +
  121 + /* Enable wake-on-EC */
  122 + if (device->wakeup.flags.valid)
  123 + device_init_wakeup(&device->dev, true);
  124 +
  125 + return 0;
  126 +}
  127 +
  128 +static int xo15_sci_remove(struct acpi_device *device, int type)
  129 +{
  130 + acpi_disable_gpe(NULL, xo15_sci_gpe);
  131 + acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler);
  132 + cancel_work_sync(&sci_work);
  133 + return 0;
  134 +}
  135 +
  136 +static int xo15_sci_resume(struct acpi_device *device)
  137 +{
  138 + /* Enable all EC events */
  139 + olpc_ec_mask_write(EC_SCI_SRC_ALL);
  140 +
  141 + /* Power/battery status might have changed */
  142 + battery_status_changed();
  143 + ac_status_changed();
  144 +
  145 + return 0;
  146 +}
  147 +
  148 +static const struct acpi_device_id xo15_sci_device_ids[] = {
  149 + {"XO15EC", 0},
  150 + {"", 0},
  151 +};
  152 +
  153 +static struct acpi_driver xo15_sci_drv = {
  154 + .name = DRV_NAME,
  155 + .class = XO15_SCI_CLASS,
  156 + .ids = xo15_sci_device_ids,
  157 + .ops = {
  158 + .add = xo15_sci_add,
  159 + .remove = xo15_sci_remove,
  160 + .resume = xo15_sci_resume,
  161 + },
  162 +};
  163 +
  164 +static int __init xo15_sci_init(void)
  165 +{
  166 + return acpi_bus_register_driver(&xo15_sci_drv);
  167 +}
  168 +device_initcall(xo15_sci_init);
arch/x86/platform/olpc/olpc.c
... ... @@ -19,6 +19,7 @@
19 19 #include <linux/string.h>
20 20 #include <linux/platform_device.h>
21 21 #include <linux/of.h>
  22 +#include <linux/syscore_ops.h>
22 23  
23 24 #include <asm/geode.h>
24 25 #include <asm/setup.h>
... ... @@ -30,6 +31,9 @@
30 31  
31 32 static DEFINE_SPINLOCK(ec_lock);
32 33  
  34 +/* EC event mask to be applied during suspend (defining wakeup sources). */
  35 +static u16 ec_wakeup_mask;
  36 +
33 37 /* what the timeout *should* be (in ms) */
34 38 #define EC_BASE_TIMEOUT 20
35 39  
... ... @@ -188,6 +192,88 @@
188 192 }
189 193 EXPORT_SYMBOL_GPL(olpc_ec_cmd);
190 194  
  195 +void olpc_ec_wakeup_set(u16 value)
  196 +{
  197 + ec_wakeup_mask |= value;
  198 +}
  199 +EXPORT_SYMBOL_GPL(olpc_ec_wakeup_set);
  200 +
  201 +void olpc_ec_wakeup_clear(u16 value)
  202 +{
  203 + ec_wakeup_mask &= ~value;
  204 +}
  205 +EXPORT_SYMBOL_GPL(olpc_ec_wakeup_clear);
  206 +
  207 +/*
  208 + * Returns true if the compile and runtime configurations allow for EC events
  209 + * to wake the system.
  210 + */
  211 +bool olpc_ec_wakeup_available(void)
  212 +{
  213 + if (!machine_is_olpc())
  214 + return false;
  215 +
  216 + /*
  217 + * XO-1 EC wakeups are available when olpc-xo1-sci driver is
  218 + * compiled in
  219 + */
  220 +#ifdef CONFIG_OLPC_XO1_SCI
  221 + if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) /* XO-1 */
  222 + return true;
  223 +#endif
  224 +
  225 + /*
  226 + * XO-1.5 EC wakeups are available when olpc-xo15-sci driver is
  227 + * compiled in
  228 + */
  229 +#ifdef CONFIG_OLPC_XO15_SCI
  230 + if (olpc_platform_info.boardrev >= olpc_board_pre(0xd0)) /* XO-1.5 */
  231 + return true;
  232 +#endif
  233 +
  234 + return false;
  235 +}
  236 +EXPORT_SYMBOL_GPL(olpc_ec_wakeup_available);
  237 +
  238 +int olpc_ec_mask_write(u16 bits)
  239 +{
  240 + if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) {
  241 + __be16 ec_word = cpu_to_be16(bits);
  242 + return olpc_ec_cmd(EC_WRITE_EXT_SCI_MASK, (void *) &ec_word, 2,
  243 + NULL, 0);
  244 + } else {
  245 + unsigned char ec_byte = bits & 0xff;
  246 + return olpc_ec_cmd(EC_WRITE_SCI_MASK, &ec_byte, 1, NULL, 0);
  247 + }
  248 +}
  249 +EXPORT_SYMBOL_GPL(olpc_ec_mask_write);
  250 +
  251 +int olpc_ec_sci_query(u16 *sci_value)
  252 +{
  253 + int ret;
  254 +
  255 + if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) {
  256 + __be16 ec_word;
  257 + ret = olpc_ec_cmd(EC_EXT_SCI_QUERY,
  258 + NULL, 0, (void *) &ec_word, 2);
  259 + if (ret == 0)
  260 + *sci_value = be16_to_cpu(ec_word);
  261 + } else {
  262 + unsigned char ec_byte;
  263 + ret = olpc_ec_cmd(EC_SCI_QUERY, NULL, 0, &ec_byte, 1);
  264 + if (ret == 0)
  265 + *sci_value = ec_byte;
  266 + }
  267 +
  268 + return ret;
  269 +}
  270 +EXPORT_SYMBOL_GPL(olpc_ec_sci_query);
  271 +
  272 +static int olpc_ec_suspend(void)
  273 +{
  274 + return olpc_ec_mask_write(ec_wakeup_mask);
  275 +}
  276 +
191 277 static bool __init check_ofw_architecture(struct device_node *root)
192 278 {
193 279 const char *olpc_arch;
... ... @@ -242,6 +328,10 @@
242 328 return 0;
243 329 }
244 330  
  331 +static struct syscore_ops olpc_syscore_ops = {
  332 + .suspend = olpc_ec_suspend,
  333 +};
  334 +
245 335 static int __init olpc_init(void)
246 336 {
247 337 int r = 0;
... ... @@ -266,6 +356,9 @@
266 356 !cs5535_has_vsa2())
267 357 x86_init.pci.arch_init = pci_olpc_init;
268 358 #endif
  359 + /* EC version 0x5f adds support for wide SCI mask */
  360 + if (olpc_platform_info.ecver >= 0x5f)
  361 + olpc_platform_info.flags |= OLPC_F_EC_WIDE_SCI;
269 362  
270 363 printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n",
271 364 ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
... ... @@ -277,6 +370,8 @@
277 370 if (r)
278 371 return r;
279 372 }
  373 +
  374 + register_syscore_ops(&olpc_syscore_ops);
280 375  
281 376 return 0;
282 377 }
arch/x86/platform/olpc/olpc_dt.c
... ... @@ -165,12 +165,115 @@
165 165 .pkg2path = olpc_dt_pkg2path,
166 166 };
167 167  
  168 +static phandle __init olpc_dt_finddevice(const char *path)
  169 +{
  170 + phandle node;
  171 + const void *args[] = { path };
  172 + void *res[] = { &node };
  173 +
  174 + if (olpc_ofw("finddevice", args, res)) {
  175 + pr_err("olpc_dt: finddevice failed!\n");
  176 + return 0;
  177 + }
  178 +
  179 + if ((s32) node == -1)
  180 + return 0;
  181 +
  182 + return node;
  183 +}
  184 +
  185 +static int __init olpc_dt_interpret(const char *words)
  186 +{
  187 + int result;
  188 + const void *args[] = { words };
  189 + void *res[] = { &result };
  190 +
  191 + if (olpc_ofw("interpret", args, res)) {
  192 + pr_err("olpc_dt: interpret failed!\n");
  193 + return -1;
  194 + }
  195 +
  196 + return result;
  197 +}
  198 +
  199 +/*
  200 + * Extract board revision directly from OFW device tree.
  201 + * We can't use olpc_platform_info because that hasn't been set up yet.
  202 + */
  203 +static u32 __init olpc_dt_get_board_revision(void)
  204 +{
  205 + phandle node;
  206 + __be32 rev;
  207 + int r;
  208 +
  209 + node = olpc_dt_finddevice("/");
  210 + if (!node)
  211 + return 0;
  212 +
  213 + r = olpc_dt_getproperty(node, "board-revision-int",
  214 + (char *) &rev, sizeof(rev));
  215 + if (r < 0)
  216 + return 0;
  217 +
  218 + return be32_to_cpu(rev);
  219 +}
  220 +
  221 +void __init olpc_dt_fixup(void)
  222 +{
  223 + int r;
  224 + char buf[64];
  225 + phandle node;
  226 + u32 board_rev;
  227 +
  228 + node = olpc_dt_finddevice("/battery@0");
  229 + if (!node)
  230 + return;
  231 +
  232 + /*
  233 + * If the battery node has a compatible property, we are running a new
  234 + * enough firmware and don't have fixups to make.
  235 + */
  236 + r = olpc_dt_getproperty(node, "compatible", buf, sizeof(buf));
  237 + if (r > 0)
  238 + return;
  239 +
  240 + pr_info("PROM DT: Old firmware detected, applying fixes\n");
  241 +
  242 + /* Add olpc,xo1-battery compatible marker to battery node */
  243 + olpc_dt_interpret("\" /battery@0\" find-device"
  244 + " \" olpc,xo1-battery\" +compatible"
  245 + " device-end");
  246 +
  247 + board_rev = olpc_dt_get_board_revision();
  248 + if (!board_rev)
  249 + return;
  250 +
  251 + if (board_rev >= olpc_board_pre(0xd0)) {
  252 + /* XO-1.5: add dcon device */
  253 + olpc_dt_interpret("\" /pci/display@1\" find-device"
  254 + " new-device"
  255 + " \" dcon\" device-name \" olpc,xo1-dcon\" +compatible"
  256 + " finish-device device-end");
  257 + } else {
  258 + /* XO-1: add dcon device, mark RTC as olpc,xo1-rtc */
  259 + olpc_dt_interpret("\" /pci/display@1,1\" find-device"
  260 + " new-device"
  261 + " \" dcon\" device-name \" olpc,xo1-dcon\" +compatible"
  262 + " finish-device device-end"
  263 + " \" /rtc\" find-device"
  264 + " \" olpc,xo1-rtc\" +compatible"
  265 + " device-end");
  266 + }
  267 +}
  268 +
168 269 void __init olpc_dt_build_devicetree(void)
169 270 {
170 271 phandle root;
171 272  
172 273 if (!olpc_ofw_is_installed())
173 274 return;
  275 +
  276 + olpc_dt_fixup();
174 277  
175 278 root = olpc_dt_getsibling(0);
176 279 if (!root) {
arch/x86/platform/olpc/xo1-wakeup.S
  1 +.text
  2 +#include <linux/linkage.h>
  3 +#include <asm/segment.h>
  4 +#include <asm/page.h>
  5 +#include <asm/pgtable_32.h>
  6 +
  7 + .macro writepost,value
  8 + movb $0x34, %al
  9 + outb %al, $0x70
  10 + movb $\value, %al
  11 + outb %al, $0x71
  12 + .endm
  13 +
  14 +wakeup_start:
  15 + # OFW lands us here, running in protected mode, with a
  16 + # kernel-compatible GDT already setup.
  17 +
  18 + # Clear any dangerous flags
  19 + pushl $0
  20 + popfl
  21 +
  22 + writepost 0x31
  23 +
  24 + # Set up %cr3
  25 + movl $initial_page_table - __PAGE_OFFSET, %eax
  26 + movl %eax, %cr3
  27 +
  28 + movl saved_cr4, %eax
  29 + movl %eax, %cr4
  30 +
  31 + movl saved_cr0, %eax
  32 + movl %eax, %cr0
  33 +
  34 + # Control registers were modified, pipeline resync is needed
  35 + jmp 1f
  36 +1:
  37 +
  38 + movw $__KERNEL_DS, %ax
  39 + movw %ax, %ss
  40 + movw %ax, %ds
  41 + movw %ax, %es
  42 + movw %ax, %fs
  43 + movw %ax, %gs
  44 +
  45 + lgdt saved_gdt
  46 + lidt saved_idt
  47 + lldt saved_ldt
  48 + ljmp $(__KERNEL_CS),$1f
  49 +1:
  50 + movl %cr3, %eax
  51 + movl %eax, %cr3
  52 + wbinvd
  53 +
  54 + # Go back to the return point
  55 + jmp ret_point
  56 +
  57 +save_registers:
  58 + sgdt saved_gdt
  59 + sidt saved_idt
  60 + sldt saved_ldt
  61 +
  62 + pushl %edx
  63 + movl %cr4, %edx
  64 + movl %edx, saved_cr4
  65 +
  66 + movl %cr0, %edx
  67 + movl %edx, saved_cr0
  68 +
  69 + popl %edx
  70 +
  71 + movl %ebx, saved_context_ebx
  72 + movl %ebp, saved_context_ebp
  73 + movl %esi, saved_context_esi
  74 + movl %edi, saved_context_edi
  75 +
  76 + pushfl
  77 + popl saved_context_eflags
  78 +
  79 + ret
  80 +
  81 +restore_registers:
  82 + movl saved_context_ebp, %ebp
  83 + movl saved_context_ebx, %ebx
  84 + movl saved_context_esi, %esi
  85 + movl saved_context_edi, %edi
  86 +
  87 + pushl saved_context_eflags
  88 + popfl
  89 +
  90 + ret
  91 +
  92 +ENTRY(do_olpc_suspend_lowlevel)
  93 + call save_processor_state
  94 + call save_registers
  95 +
  96 + # This is the stack context we want to remember
  97 + movl %esp, saved_context_esp
  98 +
  99 + pushl $3
  100 + call xo1_do_sleep
  101 +
  102 + jmp wakeup_start
  103 + .p2align 4,,7
  104 +ret_point:
  105 + movl saved_context_esp, %esp
  106 +
  107 + writepost 0x32
  108 +
  109 + call restore_registers
  110 + call restore_processor_state
  111 + ret
  112 +
  113 +.data
  114 +saved_gdt: .long 0,0
  115 +saved_idt: .long 0,0
  116 +saved_ldt: .long 0
  117 +saved_cr4: .long 0
  118 +saved_cr0: .long 0
  119 +saved_context_esp: .long 0
  120 +saved_context_edi: .long 0
  121 +saved_context_esi: .long 0
  122 +saved_context_ebx: .long 0
  123 +saved_context_ebp: .long 0
  124 +saved_context_eflags: .long 0
include/linux/cs5535.h
... ... @@ -11,6 +11,8 @@
11 11 #ifndef _CS5535_H
12 12 #define _CS5535_H
13 13  
  14 +#include <asm/msr.h>
  15 +
14 16 /* MSRs */
15 17 #define MSR_GLIU_P2D_RO0 0x10000029
16 18  
17 19  
18 20  
... ... @@ -38,16 +40,74 @@
38 40 #define MSR_MFGPT_NR 0x51400029
39 41 #define MSR_MFGPT_SETUP 0x5140002B
40 42  
  43 +#define MSR_RTC_DOMA_OFFSET 0x51400055
  44 +#define MSR_RTC_MONA_OFFSET 0x51400056
  45 +#define MSR_RTC_CEN_OFFSET 0x51400057
  46 +
41 47 #define MSR_LX_SPARE_MSR 0x80000011 /* DC-specific */
42 48  
43 49 #define MSR_GX_GLD_MSR_CONFIG 0xC0002001
44 50 #define MSR_GX_MSR_PADSEL 0xC0002011
45 51  
  52 +static inline int cs5535_pic_unreqz_select_high(unsigned int group,
  53 + unsigned int irq)
  54 +{
  55 + uint32_t lo, hi;
  56 +
  57 + rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
  58 + lo &= ~(0xF << (group * 4));
  59 + lo |= (irq & 0xF) << (group * 4);
  60 + wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
  61 + return 0;
  62 +}
  63 +
  64 +/* PIC registers */
  65 +#define CS5536_PIC_INT_SEL1 0x4d0
  66 +#define CS5536_PIC_INT_SEL2 0x4d1
  67 +
46 68 /* resource sizes */
47 69 #define LBAR_GPIO_SIZE 0xFF
48 70 #define LBAR_MFGPT_SIZE 0x40
49 71 #define LBAR_ACPI_SIZE 0x40
50 72 #define LBAR_PMS_SIZE 0x80
  73 +
  74 +/*
  75 + * PMC registers (PMS block)
  76 + * It is only safe to access these registers as dword accesses.
  77 + * See CS5536 Specification Update erratas 17 & 18
  78 + */
  79 +#define CS5536_PM_SCLK 0x10
  80 +#define CS5536_PM_IN_SLPCTL 0x20
  81 +#define CS5536_PM_WKXD 0x34
  82 +#define CS5536_PM_WKD 0x30
  83 +#define CS5536_PM_SSC 0x54
  84 +
  85 +/*
  86 + * PM registers (ACPI block)
  87 + * It is only safe to access these registers as dword accesses.
  88 + * See CS5536 Specification Update erratas 17 & 18
  89 + */
  90 +#define CS5536_PM1_STS 0x00
  91 +#define CS5536_PM1_EN 0x02
  92 +#define CS5536_PM1_CNT 0x08
  93 +#define CS5536_PM_GPE0_STS 0x18
  94 +#define CS5536_PM_GPE0_EN 0x1c
  95 +
  96 +/* CS5536_PM1_STS bits */
  97 +#define CS5536_WAK_FLAG (1 << 15)
  98 +#define CS5536_PWRBTN_FLAG (1 << 8)
  99 +
  100 +/* CS5536_PM1_EN bits */
  101 +#define CS5536_PM_PWRBTN (1 << 8)
  102 +#define CS5536_PM_RTC (1 << 10)
  103 +
  104 +/* CS5536_PM_GPE0_STS bits */
  105 +#define CS5536_GPIOM7_PME_FLAG (1 << 31)
  106 +#define CS5536_GPIOM6_PME_FLAG (1 << 30)
  107 +
  108 +/* CS5536_PM_GPE0_EN bits */
  109 +#define CS5536_GPIOM7_PME_EN (1 << 31)
  110 +#define CS5536_GPIOM6_PME_EN (1 << 30)
51 111  
52 112 /* VSA2 magic values */
53 113 #define VSA_VRC_INDEX 0xAC1C