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

x86, olpc-xo1-sci: Add GPE handler and ebook switch functionality

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

Signed-off-by: Daniel Drake <dsd@laptop.org>
Link: http://lkml.kernel.org/r/1309019658-1712-8-git-send-email-dsd@laptop.org
Acked-by: Andres Salomon <dilinger@queued.net>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>

authored by

Daniel Drake and committed by
H. Peter Anvin
7bc74b3d bc4ecd5a

+209 -3
+2
arch/x86/Kconfig
··· 2087 2087 select MFD_CORE 2088 2088 ---help--- 2089 2089 Add support for SCI-based features of the OLPC XO-1 laptop: 2090 + - EC-driven system wakeups 2090 2091 - Power button 2092 + - Ebook switch 2091 2093 2092 2094 endif # X86_32 2093 2095
+3 -2
arch/x86/include/asm/olpc.h
··· 111 111 #define EC_WRITE_SCI_MASK 0x1b 112 112 #define EC_WAKE_UP_WLAN 0x24 113 113 #define EC_WLAN_LEAVE_RESET 0x25 114 + #define EC_READ_EB_MODE 0x2a 114 115 #define EC_SET_SCI_INHIBIT 0x32 115 116 #define EC_SET_SCI_INHIBIT_RELEASE 0x34 116 117 #define EC_WLAN_ENTER_RESET 0x35 ··· 145 144 #define OLPC_GPIO_SMB_CLK 14 146 145 #define OLPC_GPIO_SMB_DATA 15 147 146 #define OLPC_GPIO_WORKAUX geode_gpio(24) 148 - #define OLPC_GPIO_LID geode_gpio(26) 149 - #define OLPC_GPIO_ECSCI geode_gpio(27) 147 + #define OLPC_GPIO_LID 26 148 + #define OLPC_GPIO_ECSCI 27 150 149 151 150 #endif /* _ASM_X86_OLPC_H */
+182 -1
arch/x86/platform/olpc/olpc-xo1-sci.c
··· 12 12 */ 13 13 14 14 #include <linux/cs5535.h> 15 + #include <linux/device.h> 16 + #include <linux/gpio.h> 15 17 #include <linux/input.h> 16 18 #include <linux/interrupt.h> 17 19 #include <linux/platform_device.h> 18 20 #include <linux/pm.h> 19 21 #include <linux/mfd/core.h> 20 22 #include <linux/suspend.h> 23 + #include <linux/workqueue.h> 21 24 22 25 #include <asm/io.h> 23 26 #include <asm/msr.h> ··· 31 28 32 29 static unsigned long acpi_base; 33 30 static struct input_dev *power_button_idev; 31 + static struct input_dev *ebook_switch_idev; 32 + 34 33 static int sci_irq; 34 + 35 + /* Report current ebook switch state through input layer */ 36 + static void send_ebook_state(void) 37 + { 38 + unsigned char state; 39 + 40 + if (olpc_ec_cmd(EC_READ_EB_MODE, NULL, 0, &state, 1)) { 41 + pr_err(PFX "failed to get ebook state\n"); 42 + return; 43 + } 44 + 45 + input_report_switch(ebook_switch_idev, SW_TABLET_MODE, state); 46 + input_sync(ebook_switch_idev); 47 + } 48 + 49 + /* 50 + * Process all items in the EC's SCI queue. 51 + * 52 + * This is handled in a workqueue because olpc_ec_cmd can be slow (and 53 + * can even timeout). 54 + * 55 + * If propagate_events is false, the queue is drained without events being 56 + * generated for the interrupts. 57 + */ 58 + static void process_sci_queue(bool propagate_events) 59 + { 60 + int r; 61 + u16 data; 62 + 63 + do { 64 + r = olpc_ec_sci_query(&data); 65 + if (r || !data) 66 + break; 67 + 68 + pr_debug(PFX "SCI 0x%x received\n", data); 69 + 70 + if (data == EC_SCI_SRC_EBOOK && propagate_events) 71 + send_ebook_state(); 72 + } while (data); 73 + 74 + if (r) 75 + pr_err(PFX "Failed to clear SCI queue"); 76 + } 77 + 78 + static void process_sci_queue_work(struct work_struct *work) 79 + { 80 + process_sci_queue(true); 81 + } 82 + 83 + static DECLARE_WORK(sci_work, process_sci_queue_work); 35 84 36 85 static irqreturn_t xo1_sci_intr(int irq, void *dev_id) 37 86 { ··· 106 51 input_sync(power_button_idev); 107 52 } 108 53 54 + if (gpe & CS5536_GPIOM7_PME_FLAG) { /* EC GPIO */ 55 + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS); 56 + schedule_work(&sci_work); 57 + } 58 + 109 59 return IRQ_HANDLED; 110 60 } 111 61 ··· 120 60 olpc_xo1_pm_wakeup_set(CS5536_PM_PWRBTN); 121 61 else 122 62 olpc_xo1_pm_wakeup_clear(CS5536_PM_PWRBTN); 63 + 64 + if (device_may_wakeup(&ebook_switch_idev->dev)) 65 + olpc_ec_wakeup_set(EC_SCI_SRC_EBOOK); 66 + else 67 + olpc_ec_wakeup_clear(EC_SCI_SRC_EBOOK); 68 + 69 + return 0; 70 + } 71 + 72 + static int xo1_sci_resume(struct platform_device *pdev) 73 + { 74 + /* Enable all EC events */ 75 + olpc_ec_mask_write(EC_SCI_SRC_ALL); 123 76 return 0; 124 77 } 125 78 ··· 177 104 return r; 178 105 } 179 106 107 + static int __devinit setup_ec_sci(void) 108 + { 109 + int r; 110 + 111 + r = gpio_request(OLPC_GPIO_ECSCI, "OLPC-ECSCI"); 112 + if (r) 113 + return r; 114 + 115 + gpio_direction_input(OLPC_GPIO_ECSCI); 116 + 117 + /* Clear pending EC SCI events */ 118 + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS); 119 + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_POSITIVE_EDGE_STS); 120 + 121 + /* 122 + * Enable EC SCI events, and map them to both a PME and the SCI 123 + * interrupt. 124 + * 125 + * Ordinarily, in addition to functioning as GPIOs, Geode GPIOs can 126 + * be mapped to regular interrupts *or* Geode-specific Power 127 + * Management Events (PMEs) - events that bring the system out of 128 + * suspend. In this case, we want both of those things - the system 129 + * wakeup, *and* the ability to get an interrupt when an event occurs. 130 + * 131 + * To achieve this, we map the GPIO to a PME, and then we use one 132 + * of the many generic knobs on the CS5535 PIC to additionally map the 133 + * PME to the regular SCI interrupt line. 134 + */ 135 + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_EVENTS_ENABLE); 136 + 137 + /* Set the SCI to cause a PME event on group 7 */ 138 + cs5535_gpio_setup_event(OLPC_GPIO_ECSCI, 7, 1); 139 + 140 + /* And have group 7 also fire the SCI interrupt */ 141 + cs5535_pic_unreqz_select_high(7, sci_irq); 142 + 143 + return 0; 144 + } 145 + 146 + static void free_ec_sci(void) 147 + { 148 + gpio_free(OLPC_GPIO_ECSCI); 149 + } 150 + 180 151 static int __devinit setup_power_button(struct platform_device *pdev) 181 152 { 182 153 int r; ··· 252 135 input_free_device(power_button_idev); 253 136 } 254 137 138 + static int __devinit setup_ebook_switch(struct platform_device *pdev) 139 + { 140 + int r; 141 + 142 + ebook_switch_idev = input_allocate_device(); 143 + if (!ebook_switch_idev) 144 + return -ENOMEM; 145 + 146 + ebook_switch_idev->name = "EBook Switch"; 147 + ebook_switch_idev->phys = DRV_NAME "/input1"; 148 + set_bit(EV_SW, ebook_switch_idev->evbit); 149 + set_bit(SW_TABLET_MODE, ebook_switch_idev->swbit); 150 + 151 + ebook_switch_idev->dev.parent = &pdev->dev; 152 + device_set_wakeup_capable(&ebook_switch_idev->dev, true); 153 + 154 + r = input_register_device(ebook_switch_idev); 155 + if (r) { 156 + dev_err(&pdev->dev, "failed to register ebook switch: %d\n", r); 157 + input_free_device(ebook_switch_idev); 158 + } 159 + 160 + return r; 161 + } 162 + 163 + static void free_ebook_switch(void) 164 + { 165 + input_unregister_device(ebook_switch_idev); 166 + input_free_device(ebook_switch_idev); 167 + } 168 + 255 169 static int __devinit xo1_sci_probe(struct platform_device *pdev) 256 170 { 257 171 struct resource *res; ··· 307 159 if (r) 308 160 return r; 309 161 162 + r = setup_ebook_switch(pdev); 163 + if (r) 164 + goto err_ebook; 165 + 166 + r = setup_ec_sci(); 167 + if (r) 168 + goto err_ecsci; 169 + 170 + /* Enable PME generation for EC-generated events */ 171 + outl(CS5536_GPIOM7_PME_EN, acpi_base + CS5536_PM_GPE0_EN); 172 + 173 + /* Clear pending events */ 174 + outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS); 175 + process_sci_queue(false); 176 + 177 + /* Initial sync */ 178 + send_ebook_state(); 179 + 310 180 r = setup_sci_interrupt(pdev); 311 181 if (r) 312 - free_power_button(); 182 + goto err_sci; 313 183 184 + /* Enable all EC events */ 185 + olpc_ec_mask_write(EC_SCI_SRC_ALL); 186 + 187 + return r; 188 + 189 + err_sci: 190 + free_ec_sci(); 191 + err_ecsci: 192 + free_ebook_switch(); 193 + err_ebook: 194 + free_power_button(); 314 195 return r; 315 196 } 316 197 ··· 347 170 { 348 171 mfd_cell_disable(pdev); 349 172 free_irq(sci_irq, pdev); 173 + cancel_work_sync(&sci_work); 174 + free_ec_sci(); 175 + free_ebook_switch(); 350 176 free_power_button(); 351 177 acpi_base = 0; 352 178 return 0; ··· 362 182 .probe = xo1_sci_probe, 363 183 .remove = __devexit_p(xo1_sci_remove), 364 184 .suspend = xo1_sci_suspend, 185 + .resume = xo1_sci_resume, 365 186 }; 366 187 367 188 static int __init xo1_sci_init(void)
+22
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 ··· 45 43 #define MSR_GX_GLD_MSR_CONFIG 0xC0002001 46 44 #define MSR_GX_MSR_PADSEL 0xC0002011 47 45 46 + static inline int cs5535_pic_unreqz_select_high(unsigned int group, 47 + unsigned int irq) 48 + { 49 + uint32_t lo, hi; 50 + 51 + rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi); 52 + lo &= ~(0xF << (group * 4)); 53 + lo |= (irq & 0xF) << (group * 4); 54 + wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi); 55 + return 0; 56 + } 57 + 48 58 /* PIC registers */ 49 59 #define CS5536_PIC_INT_SEL1 0x4d0 50 60 #define CS5536_PIC_INT_SEL2 0x4d1 ··· 87 73 #define CS5536_PM1_EN 0x02 88 74 #define CS5536_PM1_CNT 0x08 89 75 #define CS5536_PM_GPE0_STS 0x18 76 + #define CS5536_PM_GPE0_EN 0x1c 90 77 91 78 /* CS5536_PM1_STS bits */ 92 79 #define CS5536_WAK_FLAG (1 << 15) ··· 95 80 96 81 /* CS5536_PM1_EN bits */ 97 82 #define CS5536_PM_PWRBTN (1 << 8) 83 + 84 + /* CS5536_PM_GPE0_STS bits */ 85 + #define CS5536_GPIOM7_PME_FLAG (1 << 31) 86 + #define CS5536_GPIOM6_PME_FLAG (1 << 30) 87 + 88 + /* CS5536_PM_GPE0_EN bits */ 89 + #define CS5536_GPIOM7_PME_EN (1 << 31) 98 90 99 91 /* VSA2 magic values */ 100 92 #define VSA_VRC_INDEX 0xAC1C