Commit 7bc74b3df73776fe06f3df9fafd2d2698e6ca28a

Authored by Daniel Drake
Committed by H. Peter Anvin
1 parent bc4ecd5a5e

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>

Showing 4 changed files with 209 additions and 3 deletions Side-by-side Diff

... ... @@ -2087,7 +2087,9 @@
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  
arch/x86/include/asm/olpc.h
... ... @@ -111,6 +111,7 @@
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
... ... @@ -144,8 +145,8 @@
144 145 #define OLPC_GPIO_SMB_CLK 14
145 146 #define OLPC_GPIO_SMB_DATA 15
146 147 #define OLPC_GPIO_WORKAUX geode_gpio(24)
147   -#define OLPC_GPIO_LID geode_gpio(26)
148   -#define OLPC_GPIO_ECSCI geode_gpio(27)
  148 +#define OLPC_GPIO_LID 26
  149 +#define OLPC_GPIO_ECSCI 27
149 150  
150 151 #endif /* _ASM_X86_OLPC_H */
arch/x86/platform/olpc/olpc-xo1-sci.c
... ... @@ -12,12 +12,15 @@
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>
24 27  
... ... @@ -28,8 +31,60 @@
28 31  
29 32 static unsigned long acpi_base;
30 33 static struct input_dev *power_button_idev;
  34 +static struct input_dev *ebook_switch_idev;
  35 +
31 36 static int sci_irq;
32 37  
  38 +/* Report current ebook switch state through input layer */
  39 +static void send_ebook_state(void)
  40 +{
  41 + unsigned char state;
  42 +
  43 + if (olpc_ec_cmd(EC_READ_EB_MODE, NULL, 0, &state, 1)) {
  44 + pr_err(PFX "failed to get ebook state\n");
  45 + return;
  46 + }
  47 +
  48 + input_report_switch(ebook_switch_idev, SW_TABLET_MODE, state);
  49 + input_sync(ebook_switch_idev);
  50 +}
  51 +
  52 +/*
  53 + * Process all items in the EC's SCI queue.
  54 + *
  55 + * This is handled in a workqueue because olpc_ec_cmd can be slow (and
  56 + * can even timeout).
  57 + *
  58 + * If propagate_events is false, the queue is drained without events being
  59 + * generated for the interrupts.
  60 + */
  61 +static void process_sci_queue(bool propagate_events)
  62 +{
  63 + int r;
  64 + u16 data;
  65 +
  66 + do {
  67 + r = olpc_ec_sci_query(&data);
  68 + if (r || !data)
  69 + break;
  70 +
  71 + pr_debug(PFX "SCI 0x%x received\n", data);
  72 +
  73 + if (data == EC_SCI_SRC_EBOOK && propagate_events)
  74 + send_ebook_state();
  75 + } while (data);
  76 +
  77 + if (r)
  78 + pr_err(PFX "Failed to clear SCI queue");
  79 +}
  80 +
  81 +static void process_sci_queue_work(struct work_struct *work)
  82 +{
  83 + process_sci_queue(true);
  84 +}
  85 +
  86 +static DECLARE_WORK(sci_work, process_sci_queue_work);
  87 +
33 88 static irqreturn_t xo1_sci_intr(int irq, void *dev_id)
34 89 {
35 90 struct platform_device *pdev = dev_id;
... ... @@ -51,6 +106,11 @@
51 106 input_sync(power_button_idev);
52 107 }
53 108  
  109 + if (gpe & CS5536_GPIOM7_PME_FLAG) { /* EC GPIO */
  110 + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS);
  111 + schedule_work(&sci_work);
  112 + }
  113 +
54 114 return IRQ_HANDLED;
55 115 }
56 116  
57 117  
... ... @@ -60,9 +120,22 @@
60 120 olpc_xo1_pm_wakeup_set(CS5536_PM_PWRBTN);
61 121 else
62 122 olpc_xo1_pm_wakeup_clear(CS5536_PM_PWRBTN);
  123 +
  124 + if (device_may_wakeup(&ebook_switch_idev->dev))
  125 + olpc_ec_wakeup_set(EC_SCI_SRC_EBOOK);
  126 + else
  127 + olpc_ec_wakeup_clear(EC_SCI_SRC_EBOOK);
  128 +
63 129 return 0;
64 130 }
65 131  
  132 +static int xo1_sci_resume(struct platform_device *pdev)
  133 +{
  134 + /* Enable all EC events */
  135 + olpc_ec_mask_write(EC_SCI_SRC_ALL);
  136 + return 0;
  137 +}
  138 +
66 139 static int __devinit setup_sci_interrupt(struct platform_device *pdev)
67 140 {
68 141 u32 lo, hi;
... ... @@ -104,6 +177,50 @@
104 177 return r;
105 178 }
106 179  
  180 +static int __devinit setup_ec_sci(void)
  181 +{
  182 + int r;
  183 +
  184 + r = gpio_request(OLPC_GPIO_ECSCI, "OLPC-ECSCI");
  185 + if (r)
  186 + return r;
  187 +
  188 + gpio_direction_input(OLPC_GPIO_ECSCI);
  189 +
  190 + /* Clear pending EC SCI events */
  191 + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS);
  192 + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_POSITIVE_EDGE_STS);
  193 +
  194 + /*
  195 + * Enable EC SCI events, and map them to both a PME and the SCI
  196 + * interrupt.
  197 + *
  198 + * Ordinarily, in addition to functioning as GPIOs, Geode GPIOs can
  199 + * be mapped to regular interrupts *or* Geode-specific Power
  200 + * Management Events (PMEs) - events that bring the system out of
  201 + * suspend. In this case, we want both of those things - the system
  202 + * wakeup, *and* the ability to get an interrupt when an event occurs.
  203 + *
  204 + * To achieve this, we map the GPIO to a PME, and then we use one
  205 + * of the many generic knobs on the CS5535 PIC to additionally map the
  206 + * PME to the regular SCI interrupt line.
  207 + */
  208 + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_EVENTS_ENABLE);
  209 +
  210 + /* Set the SCI to cause a PME event on group 7 */
  211 + cs5535_gpio_setup_event(OLPC_GPIO_ECSCI, 7, 1);
  212 +
  213 + /* And have group 7 also fire the SCI interrupt */
  214 + cs5535_pic_unreqz_select_high(7, sci_irq);
  215 +
  216 + return 0;
  217 +}
  218 +
  219 +static void free_ec_sci(void)
  220 +{
  221 + gpio_free(OLPC_GPIO_ECSCI);
  222 +}
  223 +
107 224 static int __devinit setup_power_button(struct platform_device *pdev)
108 225 {
109 226 int r;
... ... @@ -135,6 +252,37 @@
135 252 input_free_device(power_button_idev);
136 253 }
137 254  
  255 +static int __devinit setup_ebook_switch(struct platform_device *pdev)
  256 +{
  257 + int r;
  258 +
  259 + ebook_switch_idev = input_allocate_device();
  260 + if (!ebook_switch_idev)
  261 + return -ENOMEM;
  262 +
  263 + ebook_switch_idev->name = "EBook Switch";
  264 + ebook_switch_idev->phys = DRV_NAME "/input1";
  265 + set_bit(EV_SW, ebook_switch_idev->evbit);
  266 + set_bit(SW_TABLET_MODE, ebook_switch_idev->swbit);
  267 +
  268 + ebook_switch_idev->dev.parent = &pdev->dev;
  269 + device_set_wakeup_capable(&ebook_switch_idev->dev, true);
  270 +
  271 + r = input_register_device(ebook_switch_idev);
  272 + if (r) {
  273 + dev_err(&pdev->dev, "failed to register ebook switch: %d\n", r);
  274 + input_free_device(ebook_switch_idev);
  275 + }
  276 +
  277 + return r;
  278 +}
  279 +
  280 +static void free_ebook_switch(void)
  281 +{
  282 + input_unregister_device(ebook_switch_idev);
  283 + input_free_device(ebook_switch_idev);
  284 +}
  285 +
138 286 static int __devinit xo1_sci_probe(struct platform_device *pdev)
139 287 {
140 288 struct resource *res;
141 289  
142 290  
143 291  
144 292  
... ... @@ -159,17 +307,49 @@
159 307 if (r)
160 308 return r;
161 309  
  310 + r = setup_ebook_switch(pdev);
  311 + if (r)
  312 + goto err_ebook;
  313 +
  314 + r = setup_ec_sci();
  315 + if (r)
  316 + goto err_ecsci;
  317 +
  318 + /* Enable PME generation for EC-generated events */
  319 + outl(CS5536_GPIOM7_PME_EN, acpi_base + CS5536_PM_GPE0_EN);
  320 +
  321 + /* Clear pending events */
  322 + outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
  323 + process_sci_queue(false);
  324 +
  325 + /* Initial sync */
  326 + send_ebook_state();
  327 +
162 328 r = setup_sci_interrupt(pdev);
163 329 if (r)
164   - free_power_button();
  330 + goto err_sci;
165 331  
  332 + /* Enable all EC events */
  333 + olpc_ec_mask_write(EC_SCI_SRC_ALL);
  334 +
166 335 return r;
  336 +
  337 +err_sci:
  338 + free_ec_sci();
  339 +err_ecsci:
  340 + free_ebook_switch();
  341 +err_ebook:
  342 + free_power_button();
  343 + return r;
167 344 }
168 345  
169 346 static int __devexit xo1_sci_remove(struct platform_device *pdev)
170 347 {
171 348 mfd_cell_disable(pdev);
172 349 free_irq(sci_irq, pdev);
  350 + cancel_work_sync(&sci_work);
  351 + free_ec_sci();
  352 + free_ebook_switch();
173 353 free_power_button();
174 354 acpi_base = 0;
175 355 return 0;
... ... @@ -182,6 +362,7 @@
182 362 .probe = xo1_sci_probe,
183 363 .remove = __devexit_p(xo1_sci_remove),
184 364 .suspend = xo1_sci_suspend,
  365 + .resume = xo1_sci_resume,
185 366 };
186 367  
187 368 static int __init xo1_sci_init(void)
include/linux/cs5535.h
... ... @@ -11,6 +11,8 @@
11 11 #ifndef _CS5535_H
12 12 #define _CS5535_H
13 13  
  14 +#include <asm/msr.h>
  15 +
14 16 /* MSRs */
15 17 #define MSR_GLIU_P2D_RO0 0x10000029
16 18  
... ... @@ -43,6 +45,18 @@
43 45 #define MSR_GX_GLD_MSR_CONFIG 0xC0002001
44 46 #define MSR_GX_MSR_PADSEL 0xC0002011
45 47  
  48 +static inline int cs5535_pic_unreqz_select_high(unsigned int group,
  49 + unsigned int irq)
  50 +{
  51 + uint32_t lo, hi;
  52 +
  53 + rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
  54 + lo &= ~(0xF << (group * 4));
  55 + lo |= (irq & 0xF) << (group * 4);
  56 + wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
  57 + return 0;
  58 +}
  59 +
46 60 /* PIC registers */
47 61 #define CS5536_PIC_INT_SEL1 0x4d0
48 62 #define CS5536_PIC_INT_SEL2 0x4d1
... ... @@ -73,6 +87,7 @@
73 87 #define CS5536_PM1_EN 0x02
74 88 #define CS5536_PM1_CNT 0x08
75 89 #define CS5536_PM_GPE0_STS 0x18
  90 +#define CS5536_PM_GPE0_EN 0x1c
76 91  
77 92 /* CS5536_PM1_STS bits */
78 93 #define CS5536_WAK_FLAG (1 << 15)
... ... @@ -80,6 +95,13 @@
80 95  
81 96 /* CS5536_PM1_EN bits */
82 97 #define CS5536_PM_PWRBTN (1 << 8)
  98 +
  99 +/* CS5536_PM_GPE0_STS bits */
  100 +#define CS5536_GPIOM7_PME_FLAG (1 << 31)
  101 +#define CS5536_GPIOM6_PME_FLAG (1 << 30)
  102 +
  103 +/* CS5536_PM_GPE0_EN bits */
  104 +#define CS5536_GPIOM7_PME_EN (1 << 31)
83 105  
84 106 /* VSA2 magic values */
85 107 #define VSA_VRC_INDEX 0xAC1C