aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-07-26 14:11:54 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-07-26 14:11:54 -0400
commitfa8f53ace4af9470d8414427cb3dc3c0ffc4f182 (patch)
tree23fadefaa50c5ee1a68730757dab3bbaf994243f /arch/x86
parent1d87c28e680ce4ecb8c260d8ce070b8339d52abb (diff)
parent07d5b38e14b7ff98eb52e4a6db4e20abcc608da3 (diff)
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
Diffstat (limited to 'arch/x86')
-rw-r--r--arch/x86/Kconfig43
-rw-r--r--arch/x86/include/asm/olpc.h51
-rw-r--r--arch/x86/platform/olpc/Makefile5
-rw-r--r--arch/x86/platform/olpc/olpc-xo1-pm.c215
-rw-r--r--arch/x86/platform/olpc/olpc-xo1-rtc.c81
-rw-r--r--arch/x86/platform/olpc/olpc-xo1-sci.c614
-rw-r--r--arch/x86/platform/olpc/olpc-xo1.c146
-rw-r--r--arch/x86/platform/olpc/olpc-xo15-sci.c168
-rw-r--r--arch/x86/platform/olpc/olpc.c95
-rw-r--r--arch/x86/platform/olpc/olpc_dt.c103
-rw-r--r--arch/x86/platform/olpc/xo1-wakeup.S124
11 files changed, 1482 insertions, 163 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 78fe57dcfc56..153aa6f78299 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2024,11 +2024,44 @@ config OLPC
2024 Add support for detecting the unique features of the OLPC 2024 Add support for detecting the unique features of the OLPC
2025 XO hardware. 2025 XO hardware.
2026 2026
2027config OLPC_XO1 2027config OLPC_XO1_PM
2028 tristate "OLPC XO-1 support" 2028 bool "OLPC XO-1 Power Management"
2029 depends on OLPC && MFD_CS5535 2029 depends on OLPC && MFD_CS5535 && PM_SLEEP
2030 ---help--- 2030 select MFD_CORE
2031 Add support for non-essential features of the OLPC XO-1 laptop. 2031 ---help---
2032 Add support for poweroff and suspend of the OLPC XO-1 laptop.
2033
2034config 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
2041config 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
2056config 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
2033endif # X86_32 2066endif # X86_32
2034 2067
diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h
index 5ca6801b75f3..87bdbca72f94 100644
--- a/arch/x86/include/asm/olpc.h
+++ b/arch/x86/include/asm/olpc.h
@@ -13,6 +13,7 @@ struct olpc_platform_t {
13 13
14#define OLPC_F_PRESENT 0x01 14#define OLPC_F_PRESENT 0x01
15#define OLPC_F_DCON 0x02 15#define OLPC_F_DCON 0x02
16#define OLPC_F_EC_WIDE_SCI 0x04
16 17
17#ifdef CONFIG_OLPC 18#ifdef CONFIG_OLPC
18 19
@@ -62,6 +63,13 @@ static inline int olpc_board_at_least(uint32_t rev)
62 return olpc_platform_info.boardrev >= rev; 63 return olpc_platform_info.boardrev >= rev;
63} 64}
64 65
66extern void olpc_ec_wakeup_set(u16 value);
67extern void olpc_ec_wakeup_clear(u16 value);
68extern bool olpc_ec_wakeup_available(void);
69
70extern int olpc_ec_mask_write(u16 bits);
71extern int olpc_ec_sci_query(u16 *sci_value);
72
65#else 73#else
66 74
67static inline int machine_is_olpc(void) 75static inline int machine_is_olpc(void)
@@ -74,6 +82,20 @@ static inline int olpc_has_dcon(void)
74 return 0; 82 return 0;
75} 83}
76 84
85static inline void olpc_ec_wakeup_set(u16 value) { }
86static inline void olpc_ec_wakeup_clear(u16 value) { }
87
88static inline bool olpc_ec_wakeup_available(void)
89{
90 return false;
91}
92
93#endif
94
95#ifdef CONFIG_OLPC_XO1_PM
96extern void do_olpc_suspend_lowlevel(void);
97extern void olpc_xo1_pm_wakeup_set(u16 value);
98extern void olpc_xo1_pm_wakeup_clear(u16 value);
77#endif 99#endif
78 100
79extern int pci_olpc_init(void); 101extern int pci_olpc_init(void);
@@ -83,14 +105,19 @@ extern int pci_olpc_init(void);
83extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen, 105extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen,
84 unsigned char *outbuf, size_t outlen); 106 unsigned char *outbuf, size_t outlen);
85 107
86extern int olpc_ec_mask_set(uint8_t bits);
87extern int olpc_ec_mask_unset(uint8_t bits);
88
89/* EC commands */ 108/* EC commands */
90 109
91#define EC_FIRMWARE_REV 0x08 110#define EC_FIRMWARE_REV 0x08
92#define EC_WLAN_ENTER_RESET 0x35 111#define EC_WRITE_SCI_MASK 0x1b
93#define EC_WLAN_LEAVE_RESET 0x25 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/* SCI source values */ 122/* SCI source values */
96 123
@@ -99,10 +126,12 @@ extern int olpc_ec_mask_unset(uint8_t bits);
99#define EC_SCI_SRC_BATTERY 0x02 126#define EC_SCI_SRC_BATTERY 0x02
100#define EC_SCI_SRC_BATSOC 0x04 127#define EC_SCI_SRC_BATSOC 0x04
101#define EC_SCI_SRC_BATERR 0x08 128#define EC_SCI_SRC_BATERR 0x08
102#define EC_SCI_SRC_EBOOK 0x10 129#define EC_SCI_SRC_EBOOK 0x10 /* XO-1 only */
103#define EC_SCI_SRC_WLAN 0x20 130#define EC_SCI_SRC_WLAN 0x20 /* XO-1 only */
104#define EC_SCI_SRC_ACPWR 0x40 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/* GPIO assignments */ 136/* GPIO assignments */
108 137
@@ -116,7 +145,7 @@ extern int olpc_ec_mask_unset(uint8_t bits);
116#define OLPC_GPIO_SMB_CLK 14 145#define OLPC_GPIO_SMB_CLK 14
117#define OLPC_GPIO_SMB_DATA 15 146#define OLPC_GPIO_SMB_DATA 15
118#define OLPC_GPIO_WORKAUX geode_gpio(24) 147#define OLPC_GPIO_WORKAUX geode_gpio(24)
119#define OLPC_GPIO_LID geode_gpio(26) 148#define OLPC_GPIO_LID 26
120#define OLPC_GPIO_ECSCI geode_gpio(27) 149#define OLPC_GPIO_ECSCI 27
121 150
122#endif /* _ASM_X86_OLPC_H */ 151#endif /* _ASM_X86_OLPC_H */
diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile
index 81c5e2165c24..fd332c533947 100644
--- a/arch/x86/platform/olpc/Makefile
+++ b/arch/x86/platform/olpc/Makefile
@@ -1,2 +1,5 @@
1obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o 1obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o
2obj-$(CONFIG_OLPC_XO1) += olpc-xo1.o 2obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o xo1-wakeup.o
3obj-$(CONFIG_OLPC_XO1_RTC) += olpc-xo1-rtc.o
4obj-$(CONFIG_OLPC_XO1_SCI) += olpc-xo1-sci.o
5obj-$(CONFIG_OLPC_XO15_SCI) += olpc-xo15-sci.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 000000000000..6f3855a5a2f7
--- /dev/null
+++ b/arch/x86/platform/olpc/olpc-xo1-pm.c
@@ -0,0 +1,215 @@
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
26static unsigned long acpi_base;
27static unsigned long pms_base;
28
29static u16 wakeup_mask = CS5536_PM_PWRBTN;
30
31static 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 */
37void olpc_xo1_pm_wakeup_set(u16 value)
38{
39 wakeup_mask |= value;
40}
41EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_set);
42
43/* Clear bits in the wakeup mask */
44void olpc_xo1_pm_wakeup_clear(u16 value)
45{
46 wakeup_mask &= ~value;
47}
48EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_clear);
49
50static 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
91asmlinkage 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
108static 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
126static 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
132static const struct platform_suspend_ops xo1_suspend_ops = {
133 .valid = xo1_power_state_valid,
134 .enter = xo1_power_state_enter,
135};
136
137static 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
170static 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
183static 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
192static 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
201static 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}
215arch_initcall(xo1_pm_init);
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 000000000000..a2b4efddd61a
--- /dev/null
+++ b/arch/x86/platform/olpc/olpc-xo1-rtc.c
@@ -0,0 +1,81 @@
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
20static void rtc_wake_on(struct device *dev)
21{
22 olpc_xo1_pm_wakeup_set(CS5536_PM_RTC);
23}
24
25static void rtc_wake_off(struct device *dev)
26{
27 olpc_xo1_pm_wakeup_clear(CS5536_PM_RTC);
28}
29
30static 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
43static 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
51static 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
59static 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}
81arch_initcall(xo1_rtc_init);
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 000000000000..1d4c783d7325
--- /dev/null
+++ b/arch/x86/platform/olpc/olpc-xo1-sci.c
@@ -0,0 +1,614 @@
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
33static unsigned long acpi_base;
34static struct input_dev *power_button_idev;
35static struct input_dev *ebook_switch_idev;
36static struct input_dev *lid_switch_idev;
37
38static int sci_irq;
39
40static bool lid_open;
41static bool lid_inverted;
42static int lid_wake_mode;
43
44enum lid_wake_modes {
45 LID_WAKE_ALWAYS,
46 LID_WAKE_OPEN,
47 LID_WAKE_CLOSE,
48};
49
50static const char * const lid_wake_mode_names[] = {
51 [LID_WAKE_ALWAYS] = "always",
52 [LID_WAKE_OPEN] = "open",
53 [LID_WAKE_CLOSE] = "close",
54};
55
56static 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
66static 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 */
77static 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
90static 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
100static 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 */
124static 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
130static 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}
136static 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}
151static 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 */
163static 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
195static void process_sci_queue_work(struct work_struct *work)
196{
197 process_sci_queue(true);
198}
199
200static DECLARE_WORK(sci_work, process_sci_queue_work);
201
202static 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
236static 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
264static 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
283static 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
324static 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
363static void free_ec_sci(void)
364{
365 gpio_free(OLPC_GPIO_ECSCI);
366}
367
368static 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
400static void free_lid_events(void)
401{
402 gpio_free(OLPC_GPIO_LID);
403}
404
405static 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
430static void free_power_button(void)
431{
432 input_unregister_device(power_button_idev);
433 input_free_device(power_button_idev);
434}
435
436static 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
461static void free_ebook_switch(void)
462{
463 input_unregister_device(ebook_switch_idev);
464 input_free_device(ebook_switch_idev);
465}
466
467static 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
497err_create_attr:
498 input_unregister_device(lid_switch_idev);
499err_register:
500 input_free_device(lid_switch_idev);
501 return r;
502}
503
504static 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
511static 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
573err_sci:
574 free_ec_sci();
575err_ecsci:
576 free_lid_events();
577err_lidevt:
578 free_lid_switch();
579err_lid:
580 free_ebook_switch();
581err_ebook:
582 free_power_button();
583 return r;
584}
585
586static 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
600static 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
610static int __init xo1_sci_init(void)
611{
612 return platform_driver_register(&xo1_sci_driver);
613}
614arch_initcall(xo1_sci_init);
diff --git a/arch/x86/platform/olpc/olpc-xo1.c b/arch/x86/platform/olpc/olpc-xo1.c
deleted file mode 100644
index ab81fb271760..000000000000
--- a/arch/x86/platform/olpc/olpc-xo1.c
+++ /dev/null
@@ -1,146 +0,0 @@
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
36static unsigned long acpi_base;
37static unsigned long pms_base;
38
39static 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
57static 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
89static 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
102static 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
111static 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
120static 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
135static void __exit olpc_xo1_exit(void)
136{
137 platform_driver_unregister(&cs5535_acpi_drv);
138 platform_driver_unregister(&cs5535_pms_drv);
139}
140
141MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>");
142MODULE_LICENSE("GPL");
143MODULE_ALIAS("platform:cs5535-pms");
144
145module_init(olpc_xo1_init);
146module_exit(olpc_xo1_exit);
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 000000000000..2b235b77d9ab
--- /dev/null
+++ b/arch/x86/platform/olpc/olpc-xo15-sci.c
@@ -0,0 +1,168 @@
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
26static unsigned long xo15_sci_gpe;
27
28static 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
38static 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
48static 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
77static void process_sci_queue_work(struct work_struct *work)
78{
79 process_sci_queue();
80}
81
82static DECLARE_WORK(sci_work, process_sci_queue_work);
83
84static 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
90static 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
128static 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
136static 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
148static const struct acpi_device_id xo15_sci_device_ids[] = {
149 {"XO15EC", 0},
150 {"", 0},
151};
152
153static 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
164static int __init xo15_sci_init(void)
165{
166 return acpi_bus_register_driver(&xo15_sci_drv);
167}
168device_initcall(xo15_sci_init);
diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c
index 0060fd59ea00..8b9940e78e2f 100644
--- a/arch/x86/platform/olpc/olpc.c
+++ b/arch/x86/platform/olpc/olpc.c
@@ -19,6 +19,7 @@
19#include <linux/string.h> 19#include <linux/string.h>
20#include <linux/platform_device.h> 20#include <linux/platform_device.h>
21#include <linux/of.h> 21#include <linux/of.h>
22#include <linux/syscore_ops.h>
22 23
23#include <asm/geode.h> 24#include <asm/geode.h>
24#include <asm/setup.h> 25#include <asm/setup.h>
@@ -30,6 +31,9 @@ EXPORT_SYMBOL_GPL(olpc_platform_info);
30 31
31static DEFINE_SPINLOCK(ec_lock); 32static DEFINE_SPINLOCK(ec_lock);
32 33
34/* EC event mask to be applied during suspend (defining wakeup sources). */
35static u16 ec_wakeup_mask;
36
33/* what the timeout *should* be (in ms) */ 37/* what the timeout *should* be (in ms) */
34#define EC_BASE_TIMEOUT 20 38#define EC_BASE_TIMEOUT 20
35 39
@@ -188,6 +192,88 @@ err:
188} 192}
189EXPORT_SYMBOL_GPL(olpc_ec_cmd); 193EXPORT_SYMBOL_GPL(olpc_ec_cmd);
190 194
195void olpc_ec_wakeup_set(u16 value)
196{
197 ec_wakeup_mask |= value;
198}
199EXPORT_SYMBOL_GPL(olpc_ec_wakeup_set);
200
201void olpc_ec_wakeup_clear(u16 value)
202{
203 ec_wakeup_mask &= ~value;
204}
205EXPORT_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 */
211bool 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}
236EXPORT_SYMBOL_GPL(olpc_ec_wakeup_available);
237
238int 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}
249EXPORT_SYMBOL_GPL(olpc_ec_mask_write);
250
251int 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}
270EXPORT_SYMBOL_GPL(olpc_ec_sci_query);
271
272static int olpc_ec_suspend(void)
273{
274 return olpc_ec_mask_write(ec_wakeup_mask);
275}
276
191static bool __init check_ofw_architecture(struct device_node *root) 277static bool __init check_ofw_architecture(struct device_node *root)
192{ 278{
193 const char *olpc_arch; 279 const char *olpc_arch;
@@ -242,6 +328,10 @@ static int __init add_xo1_platform_devices(void)
242 return 0; 328 return 0;
243} 329}
244 330
331static struct syscore_ops olpc_syscore_ops = {
332 .suspend = olpc_ec_suspend,
333};
334
245static int __init olpc_init(void) 335static int __init olpc_init(void)
246{ 336{
247 int r = 0; 337 int r = 0;
@@ -266,6 +356,9 @@ static int __init olpc_init(void)
266 !cs5535_has_vsa2()) 356 !cs5535_has_vsa2())
267 x86_init.pci.arch_init = pci_olpc_init; 357 x86_init.pci.arch_init = pci_olpc_init;
268#endif 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 printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n", 363 printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n",
271 ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "", 364 ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
@@ -278,6 +371,8 @@ static int __init olpc_init(void)
278 return r; 371 return r;
279 } 372 }
280 373
374 register_syscore_ops(&olpc_syscore_ops);
375
281 return 0; 376 return 0;
282} 377}
283 378
diff --git a/arch/x86/platform/olpc/olpc_dt.c b/arch/x86/platform/olpc/olpc_dt.c
index d39f63d017d2..d6ee92986920 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 = {
165 .pkg2path = olpc_dt_pkg2path, 165 .pkg2path = olpc_dt_pkg2path,
166}; 166};
167 167
168static 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
185static 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 */
203static 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
221void __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
168void __init olpc_dt_build_devicetree(void) 269void __init olpc_dt_build_devicetree(void)
169{ 270{
170 phandle root; 271 phandle root;
@@ -172,6 +273,8 @@ void __init olpc_dt_build_devicetree(void)
172 if (!olpc_ofw_is_installed()) 273 if (!olpc_ofw_is_installed())
173 return; 274 return;
174 275
276 olpc_dt_fixup();
277
175 root = olpc_dt_getsibling(0); 278 root = olpc_dt_getsibling(0);
176 if (!root) { 279 if (!root) {
177 pr_err("PROM: unable to get root node from OFW!\n"); 280 pr_err("PROM: unable to get root node from OFW!\n");
diff --git a/arch/x86/platform/olpc/xo1-wakeup.S b/arch/x86/platform/olpc/xo1-wakeup.S
new file mode 100644
index 000000000000..948deb289753
--- /dev/null
+++ b/arch/x86/platform/olpc/xo1-wakeup.S
@@ -0,0 +1,124 @@
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
14wakeup_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
361:
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
491:
50 movl %cr3, %eax
51 movl %eax, %cr3
52 wbinvd
53
54 # Go back to the return point
55 jmp ret_point
56
57save_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
81restore_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
92ENTRY(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
104ret_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
114saved_gdt: .long 0,0
115saved_idt: .long 0,0
116saved_ldt: .long 0
117saved_cr4: .long 0
118saved_cr0: .long 0
119saved_context_esp: .long 0
120saved_context_edi: .long 0
121saved_context_esi: .long 0
122saved_context_ebx: .long 0
123saved_context_ebp: .long 0
124saved_context_eflags: .long 0