diff options
author | Daniel Drake <dsd@laptop.org> | 2011-06-25 12:34:12 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2011-07-06 17:44:34 -0400 |
commit | 7feda8e9f35ebb0e9f90e03acb02280bc137f784 (patch) | |
tree | 767086ef7e722df88eaac511f3dbab8a6e57d9d7 /arch/x86 | |
parent | 97c4cb71c18fe045a763ff6681a8ebbbbbec0b2b (diff) |
x86, olpc: Add XO-1 SCI driver and power button control
The System Control Interrupt is used in the OLPC XO-1 to control various
features of the laptop. Add the driver base and the power button
functionality.
This driver can't be built as a module, because functionality added in
future patches means that some drivers need to know at boot-time whether
SCI-based functionality is available.
Signed-off-by: Daniel Drake <dsd@laptop.org>
Link: http://lkml.kernel.org/r/1309019658-1712-6-git-send-email-dsd@laptop.org
Acked-by: Andres Salomon <dilinger@queued.net>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/Kconfig | 9 | ||||
-rw-r--r-- | arch/x86/platform/olpc/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/platform/olpc/olpc-xo1-sci.c | 191 |
3 files changed, 201 insertions, 0 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f473151ac991..66b7b9d519d5 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -2080,6 +2080,15 @@ config OLPC_XO1_PM | |||
2080 | ---help--- | 2080 | ---help--- |
2081 | Add support for poweroff and suspend of the OLPC XO-1 laptop. | 2081 | Add support for poweroff and suspend of the OLPC XO-1 laptop. |
2082 | 2082 | ||
2083 | config OLPC_XO1_SCI | ||
2084 | bool "OLPC XO-1 SCI extras" | ||
2085 | depends on OLPC && OLPC_XO1_PM | ||
2086 | select GPIO_CS5535 | ||
2087 | select MFD_CORE | ||
2088 | ---help--- | ||
2089 | Add support for SCI-based features of the OLPC XO-1 laptop: | ||
2090 | - Power button | ||
2091 | |||
2083 | endif # X86_32 | 2092 | endif # X86_32 |
2084 | 2093 | ||
2085 | config AMD_NB | 2094 | config AMD_NB |
diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile index 1ae7bed89821..1ec5ade97f44 100644 --- a/arch/x86/platform/olpc/Makefile +++ b/arch/x86/platform/olpc/Makefile | |||
@@ -1,2 +1,3 @@ | |||
1 | obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o | 1 | obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o |
2 | obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o xo1-wakeup.o | 2 | obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o xo1-wakeup.o |
3 | obj-$(CONFIG_OLPC_XO1_SCI) += olpc-xo1-sci.o | ||
diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c new file mode 100644 index 000000000000..8fbf961dae89 --- /dev/null +++ b/arch/x86/platform/olpc/olpc-xo1-sci.c | |||
@@ -0,0 +1,191 @@ | |||
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/input.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/pm.h> | ||
19 | #include <linux/mfd/core.h> | ||
20 | #include <linux/suspend.h> | ||
21 | |||
22 | #include <asm/io.h> | ||
23 | #include <asm/msr.h> | ||
24 | #include <asm/olpc.h> | ||
25 | |||
26 | #define DRV_NAME "olpc-xo1-sci" | ||
27 | #define PFX DRV_NAME ": " | ||
28 | |||
29 | static unsigned long acpi_base; | ||
30 | static struct input_dev *power_button_idev; | ||
31 | static int sci_irq; | ||
32 | |||
33 | static irqreturn_t xo1_sci_intr(int irq, void *dev_id) | ||
34 | { | ||
35 | struct platform_device *pdev = dev_id; | ||
36 | u32 sts; | ||
37 | u32 gpe; | ||
38 | |||
39 | sts = inl(acpi_base + CS5536_PM1_STS); | ||
40 | outl(sts | 0xffff, acpi_base + CS5536_PM1_STS); | ||
41 | |||
42 | gpe = inl(acpi_base + CS5536_PM_GPE0_STS); | ||
43 | outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS); | ||
44 | |||
45 | dev_dbg(&pdev->dev, "sts %x gpe %x\n", sts, gpe); | ||
46 | |||
47 | if (sts & CS5536_PWRBTN_FLAG && !(sts & CS5536_WAK_FLAG)) { | ||
48 | input_report_key(power_button_idev, KEY_POWER, 1); | ||
49 | input_sync(power_button_idev); | ||
50 | input_report_key(power_button_idev, KEY_POWER, 0); | ||
51 | input_sync(power_button_idev); | ||
52 | } | ||
53 | |||
54 | return IRQ_HANDLED; | ||
55 | } | ||
56 | |||
57 | static int xo1_sci_suspend(struct platform_device *pdev, pm_message_t state) | ||
58 | { | ||
59 | if (device_may_wakeup(&power_button_idev->dev)) | ||
60 | olpc_xo1_pm_wakeup_set(CS5536_PM_PWRBTN); | ||
61 | else | ||
62 | olpc_xo1_pm_wakeup_clear(CS5536_PM_PWRBTN); | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static int __devinit setup_sci_interrupt(struct platform_device *pdev) | ||
67 | { | ||
68 | u32 lo, hi; | ||
69 | u32 sts; | ||
70 | int r; | ||
71 | |||
72 | rdmsr(0x51400020, lo, hi); | ||
73 | sci_irq = (lo >> 20) & 15; | ||
74 | |||
75 | if (sci_irq) { | ||
76 | dev_info(&pdev->dev, "SCI is mapped to IRQ %d\n", sci_irq); | ||
77 | } else { | ||
78 | /* Zero means masked */ | ||
79 | dev_info(&pdev->dev, "SCI unmapped. Mapping to IRQ 3\n"); | ||
80 | sci_irq = 3; | ||
81 | lo |= 0x00300000; | ||
82 | wrmsrl(0x51400020, lo); | ||
83 | } | ||
84 | |||
85 | /* Select level triggered in PIC */ | ||
86 | if (sci_irq < 8) { | ||
87 | lo = inb(CS5536_PIC_INT_SEL1); | ||
88 | lo |= 1 << sci_irq; | ||
89 | outb(lo, CS5536_PIC_INT_SEL1); | ||
90 | } else { | ||
91 | lo = inb(CS5536_PIC_INT_SEL2); | ||
92 | lo |= 1 << (sci_irq - 8); | ||
93 | outb(lo, CS5536_PIC_INT_SEL2); | ||
94 | } | ||
95 | |||
96 | /* Enable SCI from power button, and clear pending interrupts */ | ||
97 | sts = inl(acpi_base + CS5536_PM1_STS); | ||
98 | outl((CS5536_PM_PWRBTN << 16) | 0xffff, acpi_base + CS5536_PM1_STS); | ||
99 | |||
100 | r = request_irq(sci_irq, xo1_sci_intr, 0, DRV_NAME, pdev); | ||
101 | if (r) | ||
102 | dev_err(&pdev->dev, "can't request interrupt\n"); | ||
103 | |||
104 | return r; | ||
105 | } | ||
106 | |||
107 | static int __devinit setup_power_button(struct platform_device *pdev) | ||
108 | { | ||
109 | int r; | ||
110 | |||
111 | power_button_idev = input_allocate_device(); | ||
112 | if (!power_button_idev) | ||
113 | return -ENOMEM; | ||
114 | |||
115 | power_button_idev->name = "Power Button"; | ||
116 | power_button_idev->phys = DRV_NAME "/input0"; | ||
117 | set_bit(EV_KEY, power_button_idev->evbit); | ||
118 | set_bit(KEY_POWER, power_button_idev->keybit); | ||
119 | |||
120 | power_button_idev->dev.parent = &pdev->dev; | ||
121 | device_init_wakeup(&power_button_idev->dev, 1); | ||
122 | |||
123 | r = input_register_device(power_button_idev); | ||
124 | if (r) { | ||
125 | dev_err(&pdev->dev, "failed to register power button: %d\n", r); | ||
126 | input_free_device(power_button_idev); | ||
127 | } | ||
128 | |||
129 | return r; | ||
130 | } | ||
131 | |||
132 | static void free_power_button(void) | ||
133 | { | ||
134 | input_unregister_device(power_button_idev); | ||
135 | input_free_device(power_button_idev); | ||
136 | } | ||
137 | |||
138 | static int __devinit xo1_sci_probe(struct platform_device *pdev) | ||
139 | { | ||
140 | struct resource *res; | ||
141 | int r; | ||
142 | |||
143 | /* don't run on non-XOs */ | ||
144 | if (!machine_is_olpc()) | ||
145 | return -ENODEV; | ||
146 | |||
147 | r = mfd_cell_enable(pdev); | ||
148 | if (r) | ||
149 | return r; | ||
150 | |||
151 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | ||
152 | if (!res) { | ||
153 | dev_err(&pdev->dev, "can't fetch device resource info\n"); | ||
154 | return -EIO; | ||
155 | } | ||
156 | acpi_base = res->start; | ||
157 | |||
158 | r = setup_power_button(pdev); | ||
159 | if (r) | ||
160 | return r; | ||
161 | |||
162 | r = setup_sci_interrupt(pdev); | ||
163 | if (r) | ||
164 | free_power_button(); | ||
165 | |||
166 | return r; | ||
167 | } | ||
168 | |||
169 | static int __devexit xo1_sci_remove(struct platform_device *pdev) | ||
170 | { | ||
171 | mfd_cell_disable(pdev); | ||
172 | free_irq(sci_irq, pdev); | ||
173 | free_power_button(); | ||
174 | acpi_base = 0; | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static struct platform_driver xo1_sci_driver = { | ||
179 | .driver = { | ||
180 | .name = "olpc-xo1-sci-acpi", | ||
181 | }, | ||
182 | .probe = xo1_sci_probe, | ||
183 | .remove = __devexit_p(xo1_sci_remove), | ||
184 | .suspend = xo1_sci_suspend, | ||
185 | }; | ||
186 | |||
187 | static int __init xo1_sci_init(void) | ||
188 | { | ||
189 | return platform_driver_register(&xo1_sci_driver); | ||
190 | } | ||
191 | arch_initcall(xo1_sci_init); | ||