blob: 63f50506078b4a759b41479d7d9d7b46cf81a6c5 [file] [log] [blame]
Daniel Drake7feda8e2011-06-25 17:34:12 +01001/*
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>
Daniel Drake7bc74b32011-06-25 17:34:14 +010015#include <linux/device.h>
16#include <linux/gpio.h>
Daniel Drake7feda8e2011-06-25 17:34:12 +010017#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/suspend.h>
Daniel Drake7bc74b32011-06-25 17:34:14 +010023#include <linux/workqueue.h>
Daniel Drake7feda8e2011-06-25 17:34:12 +010024
25#include <asm/io.h>
26#include <asm/msr.h>
27#include <asm/olpc.h>
28
29#define DRV_NAME "olpc-xo1-sci"
30#define PFX DRV_NAME ": "
31
32static unsigned long acpi_base;
33static struct input_dev *power_button_idev;
Daniel Drake7bc74b32011-06-25 17:34:14 +010034static struct input_dev *ebook_switch_idev;
35
Daniel Drake7feda8e2011-06-25 17:34:12 +010036static int sci_irq;
37
Daniel Drake7bc74b32011-06-25 17:34:14 +010038/* Report current ebook switch state through input layer */
39static 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 */
61static 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
81static void process_sci_queue_work(struct work_struct *work)
82{
83 process_sci_queue(true);
84}
85
86static DECLARE_WORK(sci_work, process_sci_queue_work);
87
Daniel Drake7feda8e2011-06-25 17:34:12 +010088static irqreturn_t xo1_sci_intr(int irq, void *dev_id)
89{
90 struct platform_device *pdev = dev_id;
91 u32 sts;
92 u32 gpe;
93
94 sts = inl(acpi_base + CS5536_PM1_STS);
95 outl(sts | 0xffff, acpi_base + CS5536_PM1_STS);
96
97 gpe = inl(acpi_base + CS5536_PM_GPE0_STS);
98 outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
99
100 dev_dbg(&pdev->dev, "sts %x gpe %x\n", sts, gpe);
101
102 if (sts & CS5536_PWRBTN_FLAG && !(sts & CS5536_WAK_FLAG)) {
103 input_report_key(power_button_idev, KEY_POWER, 1);
104 input_sync(power_button_idev);
105 input_report_key(power_button_idev, KEY_POWER, 0);
106 input_sync(power_button_idev);
107 }
108
Daniel Drake7bc74b32011-06-25 17:34:14 +0100109 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
Daniel Drake7feda8e2011-06-25 17:34:12 +0100114 return IRQ_HANDLED;
115}
116
117static int xo1_sci_suspend(struct platform_device *pdev, pm_message_t state)
118{
119 if (device_may_wakeup(&power_button_idev->dev))
120 olpc_xo1_pm_wakeup_set(CS5536_PM_PWRBTN);
121 else
122 olpc_xo1_pm_wakeup_clear(CS5536_PM_PWRBTN);
Daniel Drake7bc74b32011-06-25 17:34:14 +0100123
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
129 return 0;
130}
131
132static int xo1_sci_resume(struct platform_device *pdev)
133{
134 /* Enable all EC events */
135 olpc_ec_mask_write(EC_SCI_SRC_ALL);
Daniel Drake7feda8e2011-06-25 17:34:12 +0100136 return 0;
137}
138
139static int __devinit setup_sci_interrupt(struct platform_device *pdev)
140{
141 u32 lo, hi;
142 u32 sts;
143 int r;
144
145 rdmsr(0x51400020, lo, hi);
146 sci_irq = (lo >> 20) & 15;
147
148 if (sci_irq) {
149 dev_info(&pdev->dev, "SCI is mapped to IRQ %d\n", sci_irq);
150 } else {
151 /* Zero means masked */
152 dev_info(&pdev->dev, "SCI unmapped. Mapping to IRQ 3\n");
153 sci_irq = 3;
154 lo |= 0x00300000;
155 wrmsrl(0x51400020, lo);
156 }
157
158 /* Select level triggered in PIC */
159 if (sci_irq < 8) {
160 lo = inb(CS5536_PIC_INT_SEL1);
161 lo |= 1 << sci_irq;
162 outb(lo, CS5536_PIC_INT_SEL1);
163 } else {
164 lo = inb(CS5536_PIC_INT_SEL2);
165 lo |= 1 << (sci_irq - 8);
166 outb(lo, CS5536_PIC_INT_SEL2);
167 }
168
169 /* Enable SCI from power button, and clear pending interrupts */
170 sts = inl(acpi_base + CS5536_PM1_STS);
171 outl((CS5536_PM_PWRBTN << 16) | 0xffff, acpi_base + CS5536_PM1_STS);
172
173 r = request_irq(sci_irq, xo1_sci_intr, 0, DRV_NAME, pdev);
174 if (r)
175 dev_err(&pdev->dev, "can't request interrupt\n");
176
177 return r;
178}
179
Daniel Drake7bc74b32011-06-25 17:34:14 +0100180static 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
219static void free_ec_sci(void)
220{
221 gpio_free(OLPC_GPIO_ECSCI);
222}
223
Daniel Drake7feda8e2011-06-25 17:34:12 +0100224static int __devinit setup_power_button(struct platform_device *pdev)
225{
226 int r;
227
228 power_button_idev = input_allocate_device();
229 if (!power_button_idev)
230 return -ENOMEM;
231
232 power_button_idev->name = "Power Button";
233 power_button_idev->phys = DRV_NAME "/input0";
234 set_bit(EV_KEY, power_button_idev->evbit);
235 set_bit(KEY_POWER, power_button_idev->keybit);
236
237 power_button_idev->dev.parent = &pdev->dev;
238 device_init_wakeup(&power_button_idev->dev, 1);
239
240 r = input_register_device(power_button_idev);
241 if (r) {
242 dev_err(&pdev->dev, "failed to register power button: %d\n", r);
243 input_free_device(power_button_idev);
244 }
245
246 return r;
247}
248
249static void free_power_button(void)
250{
251 input_unregister_device(power_button_idev);
252 input_free_device(power_button_idev);
253}
254
Daniel Drake7bc74b32011-06-25 17:34:14 +0100255static 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
280static void free_ebook_switch(void)
281{
282 input_unregister_device(ebook_switch_idev);
283 input_free_device(ebook_switch_idev);
284}
285
Daniel Drake7feda8e2011-06-25 17:34:12 +0100286static int __devinit xo1_sci_probe(struct platform_device *pdev)
287{
288 struct resource *res;
289 int r;
290
291 /* don't run on non-XOs */
292 if (!machine_is_olpc())
293 return -ENODEV;
294
295 r = mfd_cell_enable(pdev);
296 if (r)
297 return r;
298
299 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
300 if (!res) {
301 dev_err(&pdev->dev, "can't fetch device resource info\n");
302 return -EIO;
303 }
304 acpi_base = res->start;
305
306 r = setup_power_button(pdev);
307 if (r)
308 return r;
309
Daniel Drake7bc74b32011-06-25 17:34:14 +0100310 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
Daniel Drake7feda8e2011-06-25 17:34:12 +0100328 r = setup_sci_interrupt(pdev);
329 if (r)
Daniel Drake7bc74b32011-06-25 17:34:14 +0100330 goto err_sci;
Daniel Drake7feda8e2011-06-25 17:34:12 +0100331
Daniel Drake7bc74b32011-06-25 17:34:14 +0100332 /* Enable all EC events */
333 olpc_ec_mask_write(EC_SCI_SRC_ALL);
334
335 return r;
336
337err_sci:
338 free_ec_sci();
339err_ecsci:
340 free_ebook_switch();
341err_ebook:
342 free_power_button();
Daniel Drake7feda8e2011-06-25 17:34:12 +0100343 return r;
344}
345
346static int __devexit xo1_sci_remove(struct platform_device *pdev)
347{
348 mfd_cell_disable(pdev);
349 free_irq(sci_irq, pdev);
Daniel Drake7bc74b32011-06-25 17:34:14 +0100350 cancel_work_sync(&sci_work);
351 free_ec_sci();
352 free_ebook_switch();
Daniel Drake7feda8e2011-06-25 17:34:12 +0100353 free_power_button();
354 acpi_base = 0;
355 return 0;
356}
357
358static struct platform_driver xo1_sci_driver = {
359 .driver = {
360 .name = "olpc-xo1-sci-acpi",
361 },
362 .probe = xo1_sci_probe,
363 .remove = __devexit_p(xo1_sci_remove),
364 .suspend = xo1_sci_suspend,
Daniel Drake7bc74b32011-06-25 17:34:14 +0100365 .resume = xo1_sci_resume,
Daniel Drake7feda8e2011-06-25 17:34:12 +0100366};
367
368static int __init xo1_sci_init(void)
369{
370 return platform_driver_register(&xo1_sci_driver);
371}
372arch_initcall(xo1_sci_init);