Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

+1546 -162
+5
Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt
··· 1 + OLPC XO-1 RTC 2 + ~~~~~~~~~~~~~ 3 + 4 + Required properties: 5 + - compatible : "olpc,xo1-rtc"
+37 -4
arch/x86/Kconfig
··· 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
+40 -11
arch/x86/include/asm/olpc.h
··· 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 ··· 63 62 return olpc_platform_info.boardrev >= rev; 64 63 } 65 64 65 + extern void olpc_ec_wakeup_set(u16 value); 66 + extern void olpc_ec_wakeup_clear(u16 value); 67 + extern bool olpc_ec_wakeup_available(void); 68 + 69 + extern int olpc_ec_mask_write(u16 bits); 70 + extern int olpc_ec_sci_query(u16 *sci_value); 71 + 66 72 #else 67 73 68 74 static inline int machine_is_olpc(void) ··· 82 74 return 0; 83 75 } 84 76 77 + static inline void olpc_ec_wakeup_set(u16 value) { } 78 + static inline void olpc_ec_wakeup_clear(u16 value) { } 79 + 80 + static inline bool olpc_ec_wakeup_available(void) 81 + { 82 + return false; 83 + } 84 + 85 + #endif 86 + 87 + #ifdef CONFIG_OLPC_XO1_PM 88 + extern void do_olpc_suspend_lowlevel(void); 89 + extern void olpc_xo1_pm_wakeup_set(u16 value); 90 + extern void olpc_xo1_pm_wakeup_clear(u16 value); 85 91 #endif 86 92 87 93 extern int pci_olpc_init(void); ··· 105 83 extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen, 106 84 unsigned char *outbuf, size_t outlen); 107 85 108 - extern int olpc_ec_mask_set(uint8_t bits); 109 - extern int olpc_ec_mask_unset(uint8_t bits); 110 - 111 86 /* EC commands */ 112 87 113 - #define EC_FIRMWARE_REV 0x08 114 - #define EC_WLAN_ENTER_RESET 0x35 115 - #define EC_WLAN_LEAVE_RESET 0x25 88 + #define EC_FIRMWARE_REV 0x08 89 + #define EC_WRITE_SCI_MASK 0x1b 90 + #define EC_WAKE_UP_WLAN 0x24 91 + #define EC_WLAN_LEAVE_RESET 0x25 92 + #define EC_READ_EB_MODE 0x2a 93 + #define EC_SET_SCI_INHIBIT 0x32 94 + #define EC_SET_SCI_INHIBIT_RELEASE 0x34 95 + #define EC_WLAN_ENTER_RESET 0x35 96 + #define EC_WRITE_EXT_SCI_MASK 0x38 97 + #define EC_SCI_QUERY 0x84 98 + #define EC_EXT_SCI_QUERY 0x85 116 99 117 100 /* SCI source values */ 118 101 ··· 126 99 #define EC_SCI_SRC_BATTERY 0x02 127 100 #define EC_SCI_SRC_BATSOC 0x04 128 101 #define EC_SCI_SRC_BATERR 0x08 129 - #define EC_SCI_SRC_EBOOK 0x10 130 - #define EC_SCI_SRC_WLAN 0x20 102 + #define EC_SCI_SRC_EBOOK 0x10 /* XO-1 only */ 103 + #define EC_SCI_SRC_WLAN 0x20 /* XO-1 only */ 131 104 #define EC_SCI_SRC_ACPWR 0x40 132 - #define EC_SCI_SRC_ALL 0x7F 105 + #define EC_SCI_SRC_BATCRIT 0x80 106 + #define EC_SCI_SRC_GPWAKE 0x100 /* XO-1.5 only */ 107 + #define EC_SCI_SRC_ALL 0x1FF 133 108 134 109 /* GPIO assignments */ 135 110 ··· 145 116 #define OLPC_GPIO_SMB_CLK 14 146 117 #define OLPC_GPIO_SMB_DATA 15 147 118 #define OLPC_GPIO_WORKAUX geode_gpio(24) 148 - #define OLPC_GPIO_LID geode_gpio(26) 149 - #define OLPC_GPIO_ECSCI geode_gpio(27) 119 + #define OLPC_GPIO_LID 26 120 + #define OLPC_GPIO_ECSCI 27 150 121 151 122 #endif /* _ASM_X86_OLPC_H */
+4 -1
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
+215
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);
+81
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);
+614
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);
-146
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);
+168
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);
+95
arch/x86/platform/olpc/olpc.c
··· 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 29 EXPORT_SYMBOL_GPL(olpc_platform_info); 31 30 32 31 static DEFINE_SPINLOCK(ec_lock); 32 + 33 + /* EC event mask to be applied during suspend (defining wakeup sources). */ 34 + static u16 ec_wakeup_mask; 33 35 34 36 /* what the timeout *should* be (in ms) */ 35 37 #define EC_BASE_TIMEOUT 20 ··· 192 188 } 193 189 EXPORT_SYMBOL_GPL(olpc_ec_cmd); 194 190 191 + void olpc_ec_wakeup_set(u16 value) 192 + { 193 + ec_wakeup_mask |= value; 194 + } 195 + EXPORT_SYMBOL_GPL(olpc_ec_wakeup_set); 196 + 197 + void olpc_ec_wakeup_clear(u16 value) 198 + { 199 + ec_wakeup_mask &= ~value; 200 + } 201 + EXPORT_SYMBOL_GPL(olpc_ec_wakeup_clear); 202 + 203 + /* 204 + * Returns true if the compile and runtime configurations allow for EC events 205 + * to wake the system. 206 + */ 207 + bool olpc_ec_wakeup_available(void) 208 + { 209 + if (!machine_is_olpc()) 210 + return false; 211 + 212 + /* 213 + * XO-1 EC wakeups are available when olpc-xo1-sci driver is 214 + * compiled in 215 + */ 216 + #ifdef CONFIG_OLPC_XO1_SCI 217 + if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) /* XO-1 */ 218 + return true; 219 + #endif 220 + 221 + /* 222 + * XO-1.5 EC wakeups are available when olpc-xo15-sci driver is 223 + * compiled in 224 + */ 225 + #ifdef CONFIG_OLPC_XO15_SCI 226 + if (olpc_platform_info.boardrev >= olpc_board_pre(0xd0)) /* XO-1.5 */ 227 + return true; 228 + #endif 229 + 230 + return false; 231 + } 232 + EXPORT_SYMBOL_GPL(olpc_ec_wakeup_available); 233 + 234 + int olpc_ec_mask_write(u16 bits) 235 + { 236 + if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) { 237 + __be16 ec_word = cpu_to_be16(bits); 238 + return olpc_ec_cmd(EC_WRITE_EXT_SCI_MASK, (void *) &ec_word, 2, 239 + NULL, 0); 240 + } else { 241 + unsigned char ec_byte = bits & 0xff; 242 + return olpc_ec_cmd(EC_WRITE_SCI_MASK, &ec_byte, 1, NULL, 0); 243 + } 244 + } 245 + EXPORT_SYMBOL_GPL(olpc_ec_mask_write); 246 + 247 + int olpc_ec_sci_query(u16 *sci_value) 248 + { 249 + int ret; 250 + 251 + if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) { 252 + __be16 ec_word; 253 + ret = olpc_ec_cmd(EC_EXT_SCI_QUERY, 254 + NULL, 0, (void *) &ec_word, 2); 255 + if (ret == 0) 256 + *sci_value = be16_to_cpu(ec_word); 257 + } else { 258 + unsigned char ec_byte; 259 + ret = olpc_ec_cmd(EC_SCI_QUERY, NULL, 0, &ec_byte, 1); 260 + if (ret == 0) 261 + *sci_value = ec_byte; 262 + } 263 + 264 + return ret; 265 + } 266 + EXPORT_SYMBOL_GPL(olpc_ec_sci_query); 267 + 268 + static int olpc_ec_suspend(void) 269 + { 270 + return olpc_ec_mask_write(ec_wakeup_mask); 271 + } 272 + 195 273 static bool __init check_ofw_architecture(struct device_node *root) 196 274 { 197 275 const char *olpc_arch; ··· 328 242 return 0; 329 243 } 330 244 245 + static struct syscore_ops olpc_syscore_ops = { 246 + .suspend = olpc_ec_suspend, 247 + }; 248 + 331 249 static int __init olpc_init(void) 332 250 { 333 251 int r = 0; ··· 356 266 !cs5535_has_vsa2()) 357 267 x86_init.pci.arch_init = pci_olpc_init; 358 268 #endif 269 + /* EC version 0x5f adds support for wide SCI mask */ 270 + if (olpc_platform_info.ecver >= 0x5f) 271 + olpc_platform_info.flags |= OLPC_F_EC_WIDE_SCI; 359 272 360 273 printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n", 361 274 ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "", ··· 370 277 if (r) 371 278 return r; 372 279 } 280 + 281 + register_syscore_ops(&olpc_syscore_ops); 373 282 374 283 return 0; 375 284 }
+103
arch/x86/platform/olpc/olpc_dt.c
··· 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) {
+124
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
+60
include/linux/cs5535.h
··· 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 ··· 40 38 #define MSR_MFGPT_NR 0x51400029 41 39 #define MSR_MFGPT_SETUP 0x5140002B 42 40 41 + #define MSR_RTC_DOMA_OFFSET 0x51400055 42 + #define MSR_RTC_MONA_OFFSET 0x51400056 43 + #define MSR_RTC_CEN_OFFSET 0x51400057 44 + 43 45 #define MSR_LX_SPARE_MSR 0x80000011 /* DC-specific */ 44 46 45 47 #define MSR_GX_GLD_MSR_CONFIG 0xC0002001 46 48 #define MSR_GX_MSR_PADSEL 0xC0002011 49 + 50 + static inline int cs5535_pic_unreqz_select_high(unsigned int group, 51 + unsigned int irq) 52 + { 53 + uint32_t lo, hi; 54 + 55 + rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi); 56 + lo &= ~(0xF << (group * 4)); 57 + lo |= (irq & 0xF) << (group * 4); 58 + wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi); 59 + return 0; 60 + } 61 + 62 + /* PIC registers */ 63 + #define CS5536_PIC_INT_SEL1 0x4d0 64 + #define CS5536_PIC_INT_SEL2 0x4d1 47 65 48 66 /* resource sizes */ 49 67 #define LBAR_GPIO_SIZE 0xFF 50 68 #define LBAR_MFGPT_SIZE 0x40 51 69 #define LBAR_ACPI_SIZE 0x40 52 70 #define LBAR_PMS_SIZE 0x80 71 + 72 + /* 73 + * PMC registers (PMS block) 74 + * It is only safe to access these registers as dword accesses. 75 + * See CS5536 Specification Update erratas 17 & 18 76 + */ 77 + #define CS5536_PM_SCLK 0x10 78 + #define CS5536_PM_IN_SLPCTL 0x20 79 + #define CS5536_PM_WKXD 0x34 80 + #define CS5536_PM_WKD 0x30 81 + #define CS5536_PM_SSC 0x54 82 + 83 + /* 84 + * PM registers (ACPI block) 85 + * It is only safe to access these registers as dword accesses. 86 + * See CS5536 Specification Update erratas 17 & 18 87 + */ 88 + #define CS5536_PM1_STS 0x00 89 + #define CS5536_PM1_EN 0x02 90 + #define CS5536_PM1_CNT 0x08 91 + #define CS5536_PM_GPE0_STS 0x18 92 + #define CS5536_PM_GPE0_EN 0x1c 93 + 94 + /* CS5536_PM1_STS bits */ 95 + #define CS5536_WAK_FLAG (1 << 15) 96 + #define CS5536_PWRBTN_FLAG (1 << 8) 97 + 98 + /* CS5536_PM1_EN bits */ 99 + #define CS5536_PM_PWRBTN (1 << 8) 100 + #define CS5536_PM_RTC (1 << 10) 101 + 102 + /* CS5536_PM_GPE0_STS bits */ 103 + #define CS5536_GPIOM7_PME_FLAG (1 << 31) 104 + #define CS5536_GPIOM6_PME_FLAG (1 << 30) 105 + 106 + /* CS5536_PM_GPE0_EN bits */ 107 + #define CS5536_GPIOM7_PME_EN (1 << 31) 108 + #define CS5536_GPIOM6_PME_EN (1 << 30) 53 109 54 110 /* VSA2 magic values */ 55 111 #define VSA_VRC_INDEX 0xAC1C