~ubuntu-branches/ubuntu/trusty/linux-armadaxp/trusty

« back to all changes in this revision

Viewing changes to arch/x86/platform/olpc/olpc-xo1-sci.c

  • Committer: Package Import Robot
  • Author(s): Michael Casadevall, Bryan Wu, Dann Frazier, Michael Casadeall
  • Date: 2012-03-10 15:00:54 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20120310150054-flugb39zon8vvgwe
Tags: 3.2.0-1600.1
[ Bryan Wu ]
* UBUNTU: import debian/debian.env and debian.armadaxp

[ Dann Frazier ]
* ARM: Armada XP: remove trailing '/' in dirnames in mvRules.mk

[ Michael Casadeall ]
* tools: add some tools for Marvell Armada XP processor
* kernel: timer tick hacking from Marvell
* kernel: Sheeva Errata: add delay on Sheeva when powering down
* net: add Marvell NFP netfilter
* net: socket and skb modifications made by Marvell
* miscdevice: add minor IDs for some Marvell Armada drivers
* fs: introduce memory pool for splice()
* video: EDID detection updates from Marvell Armada XP patchset
* video: backlight: add Marvell Dove LCD backlight driver
* video: display: add THS8200 display driver
* video: framebuffer: add Marvell Dove and Armada XP processor onchip LCD controller driver
* usbtest: add Interrupt transfer testing by Marvell Armada XP code
* usb: ehci: add support for Marvell EHCI controler
* tty/serial: 8250: add support for Marvell Armada XP processor and DeviceTree work
* rtc: add support for Marvell Armada XP onchip RTC controller
* net: pppoe: add Marvell ethernet NFP hook in PPPoE networking driver
* mtd: nand: add support for Marvell Armada XP Nand Flash Controller
* mtd: maps: add Marvell Armada XP specific map driver
* mmc: add support for Marvell Armada XP MMC/SD host controller
* i2c: add support for Marvell Armada XP onchip i2c bus controller
* hwmon: add Kconfig option for Armada XP onchip thermal sensor driver
* dmaengine: add Net DMA support for splice and update Marvell XOR DMA engine driver
* ata: add support for Marvell Armada XP SATA controller and update some quirks
* ARM: add Marvell Armada XP machine to mach-types
* ARM: oprofile: add support for Marvell PJ4B core
* ARM: mm: more ARMv6 switches for Marvell Armada XP
* ARM: remove static declaration to allow compilation
* ARM: alignment access fault trick
* ARM: mm: skip some fault fixing when run on NONE SMP ARMv6 mode during early abort event
* ARM: mm: add Marvell Sheeva CPU Architecture for PJ4B
* ARM: introduce optimized copy operation for Marvell Armada XP
* ARM: SAUCE: hardware breakpoint trick for Marvell Armada XP
* ARM: big endian and little endian tricks for Marvell Armada XP
* ARM: SAUCE: Add Marvell Armada XP build rules to arch/arm/kernel/Makefile
* ARM: vfp: add special handling for Marvell Armada XP
* ARM: add support for Marvell U-Boot
* ARM: add mv_controller_num for ARM PCI drivers
* ARM: add support for local PMUs, general SMP tweaks and cache flushing
* ARM: add Marvell device identifies in glue-proc.h
* ARM: add IPC driver support for Marvell platforms
* ARM: add DMA mapping for Marvell platforms
* ARM: add Sheeva errata and PJ4B code for booting
* ARM: update Kconfig and Makefile to include Marvell Armada XP platforms
* ARM: Armada XP: import LSP from Marvell for Armada XP 3.2 kernel enablement

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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);