aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorLen Brown <len.brown@intel.com>2008-12-01 00:09:47 -0500
committerLen Brown <len.brown@intel.com>2008-12-19 04:42:32 -0500
commit41b16dce390510f550a4d2b12b98e0258bbed6e2 (patch)
tree03f668f70c47e7d6319a1b5fc21aa0634254cdb1 /drivers/platform
parent8c36790a21eec6c39c372e92c76e81865005a6ce (diff)
create drivers/platform/x86/ from drivers/misc/
Move x86 platform specific drivers from drivers/misc/ to a new home under drivers/platform/x86/. The community has been maintaining x86 vendor-specific platform specific drivers under /drivers/misc/ for a few years. The oldest ones started life under drivers/acpi. They moved out of drivers/acpi/ because they don't actually implement the ACPI specification, but either simply use ACPI, or implement vendor-specific ACPI extensions. In the future we anticipate... drivers/misc/ will go away. other architectures will create drivers/platform/<arch> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/Kconfig5
-rw-r--r--drivers/platform/Makefile5
-rw-r--r--drivers/platform/x86/Kconfig290
-rw-r--r--drivers/platform/x86/Makefile16
-rw-r--r--drivers/platform/x86/acer-wmi.c1345
-rw-r--r--drivers/platform/x86/asus-laptop.c1266
-rw-r--r--drivers/platform/x86/compal-laptop.c406
-rw-r--r--drivers/platform/x86/eeepc-laptop.c872
-rw-r--r--drivers/platform/x86/fujitsu-laptop.c1126
-rw-r--r--drivers/platform/x86/hp-wmi.c512
-rw-r--r--drivers/platform/x86/intel_menlow.c536
-rw-r--r--drivers/platform/x86/msi-laptop.c437
-rw-r--r--drivers/platform/x86/panasonic-laptop.c766
-rw-r--r--drivers/platform/x86/sony-laptop.c2781
-rw-r--r--drivers/platform/x86/tc1100-wmi.c290
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c6949
16 files changed, 17602 insertions, 0 deletions
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
new file mode 100644
index 000000000000..9652c3fe7f5e
--- /dev/null
+++ b/drivers/platform/Kconfig
@@ -0,0 +1,5 @@
1# drivers/platform/Kconfig
2
3if X86
4source "drivers/platform/x86/Kconfig"
5endif
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
new file mode 100644
index 000000000000..782953ae4c03
--- /dev/null
+++ b/drivers/platform/Makefile
@@ -0,0 +1,5 @@
1#
2# Makefile for linux/drivers/platform
3#
4
5obj-$(CONFIG_X86) += x86/
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
new file mode 100644
index 000000000000..0a9a5b9440af
--- /dev/null
+++ b/drivers/platform/x86/Kconfig
@@ -0,0 +1,290 @@
1#
2# X86 Platform Specific Drivers
3#
4
5menuconfig X86_PLATFORM_DEVICES
6 bool "X86 Platform Specific Device Drivers"
7 default y
8 ---help---
9 Say Y here to get to see options for device drivers for various
10 x86 platforms, including vendor-specific laptop extension drivers.
11 This option alone does not add any kernel code.
12
13 If you say N, all options in this submenu will be skipped and disabled.
14
15if X86_PLATFORM_DEVICES
16
17config ACER_WMI
18 tristate "Acer WMI Laptop Extras (EXPERIMENTAL)"
19 depends on EXPERIMENTAL
20 depends on ACPI
21 depends on LEDS_CLASS
22 depends on NEW_LEDS
23 depends on BACKLIGHT_CLASS_DEVICE
24 depends on SERIO_I8042
25 depends on RFKILL
26 select ACPI_WMI
27 ---help---
28 This is a driver for newer Acer (and Wistron) laptops. It adds
29 wireless radio and bluetooth control, and on some laptops,
30 exposes the mail LED and LCD backlight.
31
32 For more information about this driver see
33 <file:Documentation/laptops/acer-wmi.txt>
34
35 If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
36 here.
37
38config ASUS_LAPTOP
39 tristate "Asus Laptop Extras (EXPERIMENTAL)"
40 depends on ACPI
41 depends on EXPERIMENTAL && !ACPI_ASUS
42 depends on LEDS_CLASS
43 depends on NEW_LEDS
44 depends on BACKLIGHT_CLASS_DEVICE
45 ---help---
46 This is the new Linux driver for Asus laptops. It may also support some
47 MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate
48 standard ACPI events that go through /proc/acpi/events. It also adds
49 support for video output switching, LCD backlight control, Bluetooth and
50 Wlan control, and most importantly, allows you to blink those fancy LEDs.
51
52 For more information and a userspace daemon for handling the extra
53 buttons see <http://acpi4asus.sf.net/>.
54
55 If you have an ACPI-compatible ASUS laptop, say Y or M here.
56
57config FUJITSU_LAPTOP
58 tristate "Fujitsu Laptop Extras"
59 depends on ACPI
60 depends on INPUT
61 depends on BACKLIGHT_CLASS_DEVICE
62 ---help---
63 This is a driver for laptops built by Fujitsu:
64
65 * P2xxx/P5xxx/S6xxx/S7xxx series Lifebooks
66 * Possibly other Fujitsu laptop models
67 * Tested with S6410 and S7020
68
69 It adds support for LCD brightness control and some hotkeys.
70
71 If you have a Fujitsu laptop, say Y or M here.
72
73config FUJITSU_LAPTOP_DEBUG
74 bool "Verbose debug mode for Fujitsu Laptop Extras"
75 depends on FUJITSU_LAPTOP
76 default n
77 ---help---
78 Enables extra debug output from the fujitsu extras driver, at the
79 expense of a slight increase in driver size.
80
81 If you are not sure, say N here.
82
83config TC1100_WMI
84 tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)"
85 depends on !X86_64
86 depends on EXPERIMENTAL
87 depends on ACPI
88 select ACPI_WMI
89 ---help---
90 This is a driver for the WMI extensions (wireless and bluetooth power
91 control) of the HP Compaq TC1100 tablet.
92
93config HP_WMI
94 tristate "HP WMI extras"
95 depends on ACPI_WMI
96 depends on INPUT
97 depends on RFKILL
98 help
99 Say Y here if you want to support WMI-based hotkeys on HP laptops and
100 to read data from WMI such as docking or ambient light sensor state.
101
102 To compile this driver as a module, choose M here: the module will
103 be called hp-wmi.
104
105config MSI_LAPTOP
106 tristate "MSI Laptop Extras"
107 depends on ACPI
108 depends on BACKLIGHT_CLASS_DEVICE
109 ---help---
110 This is a driver for laptops built by MSI (MICRO-STAR
111 INTERNATIONAL):
112
113 MSI MegaBook S270 (MS-1013)
114 Cytron/TCM/Medion/Tchibo MD96100/SAM2000
115
116 It adds support for Bluetooth, WLAN and LCD brightness control.
117
118 More information about this driver is available at
119 <http://0pointer.de/lennart/tchibo.html>.
120
121 If you have an MSI S270 laptop, say Y or M here.
122
123config PANASONIC_LAPTOP
124 tristate "Panasonic Laptop Extras"
125 depends on INPUT && ACPI
126 depends on BACKLIGHT_CLASS_DEVICE
127 ---help---
128 This driver adds support for access to backlight control and hotkeys
129 on Panasonic Let's Note laptops.
130
131 If you have a Panasonic Let's note laptop (such as the R1(N variant),
132 R2, R3, R5, T2, W2 and Y2 series), say Y.
133
134config COMPAL_LAPTOP
135 tristate "Compal Laptop Extras"
136 depends on ACPI
137 depends on BACKLIGHT_CLASS_DEVICE
138 ---help---
139 This is a driver for laptops built by Compal:
140
141 Compal FL90/IFL90
142 Compal FL91/IFL91
143 Compal FL92/JFL92
144 Compal FT00/IFT00
145
146 It adds support for Bluetooth, WLAN and LCD brightness control.
147
148 If you have an Compal FL9x/IFL9x/FT00 laptop, say Y or M here.
149
150config SONY_LAPTOP
151 tristate "Sony Laptop Extras"
152 depends on ACPI
153 select BACKLIGHT_CLASS_DEVICE
154 depends on INPUT
155 ---help---
156 This mini-driver drives the SNC and SPIC devices present in the ACPI
157 BIOS of the Sony Vaio laptops.
158
159 It gives access to some extra laptop functionalities like Bluetooth,
160 screen brightness control, Fn keys and allows powering on/off some
161 devices.
162
163 Read <file:Documentation/laptops/sony-laptop.txt> for more information.
164
165config SONYPI_COMPAT
166 bool "Sonypi compatibility"
167 depends on SONY_LAPTOP
168 ---help---
169 Build the sonypi driver compatibility code into the sony-laptop driver.
170
171config THINKPAD_ACPI
172 tristate "ThinkPad ACPI Laptop Extras"
173 depends on ACPI
174 select BACKLIGHT_LCD_SUPPORT
175 select BACKLIGHT_CLASS_DEVICE
176 select HWMON
177 select NVRAM
178 select INPUT
179 select NEW_LEDS
180 select LEDS_CLASS
181 select NET
182 select RFKILL
183 ---help---
184 This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
185 support for Fn-Fx key combinations, Bluetooth control, video
186 output switching, ThinkLight control, UltraBay eject and more.
187 For more information about this driver see
188 <file:Documentation/laptops/thinkpad-acpi.txt> and
189 <http://ibm-acpi.sf.net/> .
190
191 This driver was formerly known as ibm-acpi.
192
193 If you have an IBM or Lenovo ThinkPad laptop, say Y or M here.
194
195config THINKPAD_ACPI_DEBUG
196 bool "Verbose debug mode"
197 depends on THINKPAD_ACPI
198 default n
199 ---help---
200 Enables extra debugging information, at the expense of a slightly
201 increase in driver size.
202
203 If you are not sure, say N here.
204
205config THINKPAD_ACPI_DOCK
206 bool "Legacy Docking Station Support"
207 depends on THINKPAD_ACPI
208 depends on ACPI_DOCK=n
209 default n
210 ---help---
211 Allows the thinkpad_acpi driver to handle docking station events.
212 This support was made obsolete by the generic ACPI docking station
213 support (CONFIG_ACPI_DOCK). It will allow locking and removing the
214 laptop from the docking station, but will not properly connect PCI
215 devices.
216
217 If you are not sure, say N here.
218
219config THINKPAD_ACPI_BAY
220 bool "Legacy Removable Bay Support"
221 depends on THINKPAD_ACPI
222 default y
223 ---help---
224 Allows the thinkpad_acpi driver to handle removable bays. It will
225 electrically disable the device in the bay, and also generate
226 notifications when the bay lever is ejected or inserted.
227
228 If you are not sure, say Y here.
229
230config THINKPAD_ACPI_VIDEO
231 bool "Video output control support"
232 depends on THINKPAD_ACPI
233 default y
234 ---help---
235 Allows the thinkpad_acpi driver to provide an interface to control
236 the various video output ports.
237
238 This feature often won't work well, depending on ThinkPad model,
239 display state, video output devices in use, whether there is a X
240 server running, phase of the moon, and the current mood of
241 Schroedinger's cat. If you can use X.org's RandR to control
242 your ThinkPad's video output ports instead of this feature,
243 don't think twice: do it and say N here to save some memory.
244
245 If you are not sure, say Y here.
246
247config THINKPAD_ACPI_HOTKEY_POLL
248 bool "Support NVRAM polling for hot keys"
249 depends on THINKPAD_ACPI
250 default y
251 ---help---
252 Some thinkpad models benefit from NVRAM polling to detect a few of
253 the hot key press events. If you know your ThinkPad model does not
254 need to do NVRAM polling to support any of the hot keys you use,
255 unselecting this option will save about 1kB of memory.
256
257 ThinkPads T40 and newer, R52 and newer, and X31 and newer are
258 unlikely to need NVRAM polling in their latest BIOS versions.
259
260 NVRAM polling can detect at most the following keys: ThinkPad/Access
261 IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute,
262 Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12).
263
264 If you are not sure, say Y here. The driver enables polling only if
265 it is strictly necessary to do so.
266
267config INTEL_MENLOW
268 tristate "Thermal Management driver for Intel menlow platform"
269 depends on ACPI_THERMAL
270 select THERMAL
271 ---help---
272 ACPI thermal management enhancement driver on
273 Intel Menlow platform.
274
275 If unsure, say N.
276
277config EEEPC_LAPTOP
278 tristate "Eee PC Hotkey Driver (EXPERIMENTAL)"
279 depends on ACPI
280 depends on BACKLIGHT_CLASS_DEVICE
281 depends on HWMON
282 depends on EXPERIMENTAL
283 depends on RFKILL
284 ---help---
285 This driver supports the Fn-Fx keys on Eee PC laptops.
286 It also adds the ability to switch camera/wlan on/off.
287
288 If you have an Eee PC laptop, say Y or M here.
289
290endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
new file mode 100644
index 000000000000..4d26b1bf22ae
--- /dev/null
+++ b/drivers/platform/x86/Makefile
@@ -0,0 +1,16 @@
1#
2# Makefile for linux/drivers/platform/x86
3# x86 Platform-Specific Drivers
4#
5obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
6obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o
7obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
8obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
9obj-$(CONFIG_ACER_WMI) += acer-wmi.o
10obj-$(CONFIG_HP_WMI) += hp-wmi.o
11obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
12obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
13obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
14obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
15obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
16obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
new file mode 100644
index 000000000000..94c9f911824e
--- /dev/null
+++ b/drivers/platform/x86/acer-wmi.c
@@ -0,0 +1,1345 @@
1/*
2 * Acer WMI Laptop Extras
3 *
4 * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
5 *
6 * Based on acer_acpi:
7 * Copyright (C) 2005-2007 E.M. Smith
8 * Copyright (C) 2007-2008 Carlos Corbacho <cathectic@gmail.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/init.h>
28#include <linux/types.h>
29#include <linux/dmi.h>
30#include <linux/fb.h>
31#include <linux/backlight.h>
32#include <linux/leds.h>
33#include <linux/platform_device.h>
34#include <linux/acpi.h>
35#include <linux/i8042.h>
36#include <linux/rfkill.h>
37#include <linux/workqueue.h>
38#include <linux/debugfs.h>
39
40#include <acpi/acpi_drivers.h>
41
42MODULE_AUTHOR("Carlos Corbacho");
43MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
44MODULE_LICENSE("GPL");
45
46#define ACER_LOGPREFIX "acer-wmi: "
47#define ACER_ERR KERN_ERR ACER_LOGPREFIX
48#define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
49#define ACER_INFO KERN_INFO ACER_LOGPREFIX
50
51/*
52 * The following defines quirks to get some specific functions to work
53 * which are known to not be supported over ACPI-WMI (such as the mail LED
54 * on WMID based Acer's)
55 */
56struct acer_quirks {
57 const char *vendor;
58 const char *model;
59 u16 quirks;
60};
61
62/*
63 * Magic Number
64 * Meaning is unknown - this number is required for writing to ACPI for AMW0
65 * (it's also used in acerhk when directly accessing the BIOS)
66 */
67#define ACER_AMW0_WRITE 0x9610
68
69/*
70 * Bit masks for the AMW0 interface
71 */
72#define ACER_AMW0_WIRELESS_MASK 0x35
73#define ACER_AMW0_BLUETOOTH_MASK 0x34
74#define ACER_AMW0_MAILLED_MASK 0x31
75
76/*
77 * Method IDs for WMID interface
78 */
79#define ACER_WMID_GET_WIRELESS_METHODID 1
80#define ACER_WMID_GET_BLUETOOTH_METHODID 2
81#define ACER_WMID_GET_BRIGHTNESS_METHODID 3
82#define ACER_WMID_SET_WIRELESS_METHODID 4
83#define ACER_WMID_SET_BLUETOOTH_METHODID 5
84#define ACER_WMID_SET_BRIGHTNESS_METHODID 6
85#define ACER_WMID_GET_THREEG_METHODID 10
86#define ACER_WMID_SET_THREEG_METHODID 11
87
88/*
89 * Acer ACPI method GUIDs
90 */
91#define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
92#define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C"
93#define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"
94#define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A"
95
96MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
97MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
98
99/* Temporary workaround until the WMI sysfs interface goes in */
100MODULE_ALIAS("dmi:*:*Acer*:*:");
101
102/*
103 * Interface capability flags
104 */
105#define ACER_CAP_MAILLED (1<<0)
106#define ACER_CAP_WIRELESS (1<<1)
107#define ACER_CAP_BLUETOOTH (1<<2)
108#define ACER_CAP_BRIGHTNESS (1<<3)
109#define ACER_CAP_THREEG (1<<4)
110#define ACER_CAP_ANY (0xFFFFFFFF)
111
112/*
113 * Interface type flags
114 */
115enum interface_flags {
116 ACER_AMW0,
117 ACER_AMW0_V2,
118 ACER_WMID,
119};
120
121#define ACER_DEFAULT_WIRELESS 0
122#define ACER_DEFAULT_BLUETOOTH 0
123#define ACER_DEFAULT_MAILLED 0
124#define ACER_DEFAULT_THREEG 0
125
126static int max_brightness = 0xF;
127
128static int mailled = -1;
129static int brightness = -1;
130static int threeg = -1;
131static int force_series;
132
133module_param(mailled, int, 0444);
134module_param(brightness, int, 0444);
135module_param(threeg, int, 0444);
136module_param(force_series, int, 0444);
137MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
138MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
139MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
140MODULE_PARM_DESC(force_series, "Force a different laptop series");
141
142struct acer_data {
143 int mailled;
144 int threeg;
145 int brightness;
146};
147
148struct acer_debug {
149 struct dentry *root;
150 struct dentry *devices;
151 u32 wmid_devices;
152};
153
154static struct rfkill *wireless_rfkill;
155static struct rfkill *bluetooth_rfkill;
156
157/* Each low-level interface must define at least some of the following */
158struct wmi_interface {
159 /* The WMI device type */
160 u32 type;
161
162 /* The capabilities this interface provides */
163 u32 capability;
164
165 /* Private data for the current interface */
166 struct acer_data data;
167
168 /* debugfs entries associated with this interface */
169 struct acer_debug debug;
170};
171
172/* The static interface pointer, points to the currently detected interface */
173static struct wmi_interface *interface;
174
175/*
176 * Embedded Controller quirks
177 * Some laptops require us to directly access the EC to either enable or query
178 * features that are not available through WMI.
179 */
180
181struct quirk_entry {
182 u8 wireless;
183 u8 mailled;
184 s8 brightness;
185 u8 bluetooth;
186};
187
188static struct quirk_entry *quirks;
189
190static void set_quirks(void)
191{
192 if (!interface)
193 return;
194
195 if (quirks->mailled)
196 interface->capability |= ACER_CAP_MAILLED;
197
198 if (quirks->brightness)
199 interface->capability |= ACER_CAP_BRIGHTNESS;
200}
201
202static int dmi_matched(const struct dmi_system_id *dmi)
203{
204 quirks = dmi->driver_data;
205 return 0;
206}
207
208static struct quirk_entry quirk_unknown = {
209};
210
211static struct quirk_entry quirk_acer_aspire_1520 = {
212 .brightness = -1,
213};
214
215static struct quirk_entry quirk_acer_travelmate_2490 = {
216 .mailled = 1,
217};
218
219/* This AMW0 laptop has no bluetooth */
220static struct quirk_entry quirk_medion_md_98300 = {
221 .wireless = 1,
222};
223
224static struct quirk_entry quirk_fujitsu_amilo_li_1718 = {
225 .wireless = 2,
226};
227
228static struct dmi_system_id acer_quirks[] = {
229 {
230 .callback = dmi_matched,
231 .ident = "Acer Aspire 1360",
232 .matches = {
233 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
234 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
235 },
236 .driver_data = &quirk_acer_aspire_1520,
237 },
238 {
239 .callback = dmi_matched,
240 .ident = "Acer Aspire 1520",
241 .matches = {
242 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
243 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1520"),
244 },
245 .driver_data = &quirk_acer_aspire_1520,
246 },
247 {
248 .callback = dmi_matched,
249 .ident = "Acer Aspire 3100",
250 .matches = {
251 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
252 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
253 },
254 .driver_data = &quirk_acer_travelmate_2490,
255 },
256 {
257 .callback = dmi_matched,
258 .ident = "Acer Aspire 3610",
259 .matches = {
260 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
261 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3610"),
262 },
263 .driver_data = &quirk_acer_travelmate_2490,
264 },
265 {
266 .callback = dmi_matched,
267 .ident = "Acer Aspire 5100",
268 .matches = {
269 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
270 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
271 },
272 .driver_data = &quirk_acer_travelmate_2490,
273 },
274 {
275 .callback = dmi_matched,
276 .ident = "Acer Aspire 5610",
277 .matches = {
278 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
279 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
280 },
281 .driver_data = &quirk_acer_travelmate_2490,
282 },
283 {
284 .callback = dmi_matched,
285 .ident = "Acer Aspire 5630",
286 .matches = {
287 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
288 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
289 },
290 .driver_data = &quirk_acer_travelmate_2490,
291 },
292 {
293 .callback = dmi_matched,
294 .ident = "Acer Aspire 5650",
295 .matches = {
296 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
297 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
298 },
299 .driver_data = &quirk_acer_travelmate_2490,
300 },
301 {
302 .callback = dmi_matched,
303 .ident = "Acer Aspire 5680",
304 .matches = {
305 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
306 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
307 },
308 .driver_data = &quirk_acer_travelmate_2490,
309 },
310 {
311 .callback = dmi_matched,
312 .ident = "Acer Aspire 9110",
313 .matches = {
314 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
315 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
316 },
317 .driver_data = &quirk_acer_travelmate_2490,
318 },
319 {
320 .callback = dmi_matched,
321 .ident = "Acer TravelMate 2490",
322 .matches = {
323 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
324 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
325 },
326 .driver_data = &quirk_acer_travelmate_2490,
327 },
328 {
329 .callback = dmi_matched,
330 .ident = "Acer TravelMate 4200",
331 .matches = {
332 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
333 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4200"),
334 },
335 .driver_data = &quirk_acer_travelmate_2490,
336 },
337 {
338 .callback = dmi_matched,
339 .ident = "Fujitsu Siemens Amilo Li 1718",
340 .matches = {
341 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
342 DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Li 1718"),
343 },
344 .driver_data = &quirk_fujitsu_amilo_li_1718,
345 },
346 {
347 .callback = dmi_matched,
348 .ident = "Medion MD 98300",
349 .matches = {
350 DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
351 DMI_MATCH(DMI_PRODUCT_NAME, "WAM2030"),
352 },
353 .driver_data = &quirk_medion_md_98300,
354 },
355 {}
356};
357
358/* Find which quirks are needed for a particular vendor/ model pair */
359static void find_quirks(void)
360{
361 if (!force_series) {
362 dmi_check_system(acer_quirks);
363 } else if (force_series == 2490) {
364 quirks = &quirk_acer_travelmate_2490;
365 }
366
367 if (quirks == NULL)
368 quirks = &quirk_unknown;
369
370 set_quirks();
371}
372
373/*
374 * General interface convenience methods
375 */
376
377static bool has_cap(u32 cap)
378{
379 if ((interface->capability & cap) != 0)
380 return 1;
381
382 return 0;
383}
384
385/*
386 * AMW0 (V1) interface
387 */
388struct wmab_args {
389 u32 eax;
390 u32 ebx;
391 u32 ecx;
392 u32 edx;
393};
394
395struct wmab_ret {
396 u32 eax;
397 u32 ebx;
398 u32 ecx;
399 u32 edx;
400 u32 eex;
401};
402
403static acpi_status wmab_execute(struct wmab_args *regbuf,
404struct acpi_buffer *result)
405{
406 struct acpi_buffer input;
407 acpi_status status;
408 input.length = sizeof(struct wmab_args);
409 input.pointer = (u8 *)regbuf;
410
411 status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
412
413 return status;
414}
415
416static acpi_status AMW0_get_u32(u32 *value, u32 cap,
417struct wmi_interface *iface)
418{
419 int err;
420 u8 result;
421
422 switch (cap) {
423 case ACER_CAP_MAILLED:
424 switch (quirks->mailled) {
425 default:
426 err = ec_read(0xA, &result);
427 if (err)
428 return AE_ERROR;
429 *value = (result >> 7) & 0x1;
430 return AE_OK;
431 }
432 break;
433 case ACER_CAP_WIRELESS:
434 switch (quirks->wireless) {
435 case 1:
436 err = ec_read(0x7B, &result);
437 if (err)
438 return AE_ERROR;
439 *value = result & 0x1;
440 return AE_OK;
441 case 2:
442 err = ec_read(0x71, &result);
443 if (err)
444 return AE_ERROR;
445 *value = result & 0x1;
446 return AE_OK;
447 default:
448 err = ec_read(0xA, &result);
449 if (err)
450 return AE_ERROR;
451 *value = (result >> 2) & 0x1;
452 return AE_OK;
453 }
454 break;
455 case ACER_CAP_BLUETOOTH:
456 switch (quirks->bluetooth) {
457 default:
458 err = ec_read(0xA, &result);
459 if (err)
460 return AE_ERROR;
461 *value = (result >> 4) & 0x1;
462 return AE_OK;
463 }
464 break;
465 case ACER_CAP_BRIGHTNESS:
466 switch (quirks->brightness) {
467 default:
468 err = ec_read(0x83, &result);
469 if (err)
470 return AE_ERROR;
471 *value = result;
472 return AE_OK;
473 }
474 break;
475 default:
476 return AE_ERROR;
477 }
478 return AE_OK;
479}
480
481static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
482{
483 struct wmab_args args;
484
485 args.eax = ACER_AMW0_WRITE;
486 args.ebx = value ? (1<<8) : 0;
487 args.ecx = args.edx = 0;
488
489 switch (cap) {
490 case ACER_CAP_MAILLED:
491 if (value > 1)
492 return AE_BAD_PARAMETER;
493 args.ebx |= ACER_AMW0_MAILLED_MASK;
494 break;
495 case ACER_CAP_WIRELESS:
496 if (value > 1)
497 return AE_BAD_PARAMETER;
498 args.ebx |= ACER_AMW0_WIRELESS_MASK;
499 break;
500 case ACER_CAP_BLUETOOTH:
501 if (value > 1)
502 return AE_BAD_PARAMETER;
503 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
504 break;
505 case ACER_CAP_BRIGHTNESS:
506 if (value > max_brightness)
507 return AE_BAD_PARAMETER;
508 switch (quirks->brightness) {
509 default:
510 return ec_write(0x83, value);
511 break;
512 }
513 default:
514 return AE_ERROR;
515 }
516
517 /* Actually do the set */
518 return wmab_execute(&args, NULL);
519}
520
521static acpi_status AMW0_find_mailled(void)
522{
523 struct wmab_args args;
524 struct wmab_ret ret;
525 acpi_status status = AE_OK;
526 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
527 union acpi_object *obj;
528
529 args.eax = 0x86;
530 args.ebx = args.ecx = args.edx = 0;
531
532 status = wmab_execute(&args, &out);
533 if (ACPI_FAILURE(status))
534 return status;
535
536 obj = (union acpi_object *) out.pointer;
537 if (obj && obj->type == ACPI_TYPE_BUFFER &&
538 obj->buffer.length == sizeof(struct wmab_ret)) {
539 ret = *((struct wmab_ret *) obj->buffer.pointer);
540 } else {
541 return AE_ERROR;
542 }
543
544 if (ret.eex & 0x1)
545 interface->capability |= ACER_CAP_MAILLED;
546
547 kfree(out.pointer);
548
549 return AE_OK;
550}
551
552static acpi_status AMW0_set_capabilities(void)
553{
554 struct wmab_args args;
555 struct wmab_ret ret;
556 acpi_status status = AE_OK;
557 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
558 union acpi_object *obj;
559
560 /*
561 * On laptops with this strange GUID (non Acer), normal probing doesn't
562 * work.
563 */
564 if (wmi_has_guid(AMW0_GUID2)) {
565 interface->capability |= ACER_CAP_WIRELESS;
566 return AE_OK;
567 }
568
569 args.eax = ACER_AMW0_WRITE;
570 args.ecx = args.edx = 0;
571
572 args.ebx = 0xa2 << 8;
573 args.ebx |= ACER_AMW0_WIRELESS_MASK;
574
575 status = wmab_execute(&args, &out);
576 if (ACPI_FAILURE(status))
577 return status;
578
579 obj = (union acpi_object *) out.pointer;
580 if (obj && obj->type == ACPI_TYPE_BUFFER &&
581 obj->buffer.length == sizeof(struct wmab_ret)) {
582 ret = *((struct wmab_ret *) obj->buffer.pointer);
583 } else {
584 return AE_ERROR;
585 }
586
587 if (ret.eax & 0x1)
588 interface->capability |= ACER_CAP_WIRELESS;
589
590 args.ebx = 2 << 8;
591 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
592
593 status = wmab_execute(&args, &out);
594 if (ACPI_FAILURE(status))
595 return status;
596
597 obj = (union acpi_object *) out.pointer;
598 if (obj && obj->type == ACPI_TYPE_BUFFER
599 && obj->buffer.length == sizeof(struct wmab_ret)) {
600 ret = *((struct wmab_ret *) obj->buffer.pointer);
601 } else {
602 return AE_ERROR;
603 }
604
605 if (ret.eax & 0x1)
606 interface->capability |= ACER_CAP_BLUETOOTH;
607
608 kfree(out.pointer);
609
610 /*
611 * This appears to be safe to enable, since all Wistron based laptops
612 * appear to use the same EC register for brightness, even if they
613 * differ for wireless, etc
614 */
615 if (quirks->brightness >= 0)
616 interface->capability |= ACER_CAP_BRIGHTNESS;
617
618 return AE_OK;
619}
620
621static struct wmi_interface AMW0_interface = {
622 .type = ACER_AMW0,
623};
624
625static struct wmi_interface AMW0_V2_interface = {
626 .type = ACER_AMW0_V2,
627};
628
629/*
630 * New interface (The WMID interface)
631 */
632static acpi_status
633WMI_execute_u32(u32 method_id, u32 in, u32 *out)
634{
635 struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
636 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
637 union acpi_object *obj;
638 u32 tmp;
639 acpi_status status;
640
641 status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
642
643 if (ACPI_FAILURE(status))
644 return status;
645
646 obj = (union acpi_object *) result.pointer;
647 if (obj && obj->type == ACPI_TYPE_BUFFER &&
648 obj->buffer.length == sizeof(u32)) {
649 tmp = *((u32 *) obj->buffer.pointer);
650 } else {
651 tmp = 0;
652 }
653
654 if (out)
655 *out = tmp;
656
657 kfree(result.pointer);
658
659 return status;
660}
661
662static acpi_status WMID_get_u32(u32 *value, u32 cap,
663struct wmi_interface *iface)
664{
665 acpi_status status;
666 u8 tmp;
667 u32 result, method_id = 0;
668
669 switch (cap) {
670 case ACER_CAP_WIRELESS:
671 method_id = ACER_WMID_GET_WIRELESS_METHODID;
672 break;
673 case ACER_CAP_BLUETOOTH:
674 method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
675 break;
676 case ACER_CAP_BRIGHTNESS:
677 method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
678 break;
679 case ACER_CAP_THREEG:
680 method_id = ACER_WMID_GET_THREEG_METHODID;
681 break;
682 case ACER_CAP_MAILLED:
683 if (quirks->mailled == 1) {
684 ec_read(0x9f, &tmp);
685 *value = tmp & 0x1;
686 return 0;
687 }
688 default:
689 return AE_ERROR;
690 }
691 status = WMI_execute_u32(method_id, 0, &result);
692
693 if (ACPI_SUCCESS(status))
694 *value = (u8)result;
695
696 return status;
697}
698
699static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
700{
701 u32 method_id = 0;
702 char param;
703
704 switch (cap) {
705 case ACER_CAP_BRIGHTNESS:
706 if (value > max_brightness)
707 return AE_BAD_PARAMETER;
708 method_id = ACER_WMID_SET_BRIGHTNESS_METHODID;
709 break;
710 case ACER_CAP_WIRELESS:
711 if (value > 1)
712 return AE_BAD_PARAMETER;
713 method_id = ACER_WMID_SET_WIRELESS_METHODID;
714 break;
715 case ACER_CAP_BLUETOOTH:
716 if (value > 1)
717 return AE_BAD_PARAMETER;
718 method_id = ACER_WMID_SET_BLUETOOTH_METHODID;
719 break;
720 case ACER_CAP_THREEG:
721 if (value > 1)
722 return AE_BAD_PARAMETER;
723 method_id = ACER_WMID_SET_THREEG_METHODID;
724 break;
725 case ACER_CAP_MAILLED:
726 if (value > 1)
727 return AE_BAD_PARAMETER;
728 if (quirks->mailled == 1) {
729 param = value ? 0x92 : 0x93;
730 i8042_command(&param, 0x1059);
731 return 0;
732 }
733 break;
734 default:
735 return AE_ERROR;
736 }
737 return WMI_execute_u32(method_id, (u32)value, NULL);
738}
739
740static acpi_status WMID_set_capabilities(void)
741{
742 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
743 union acpi_object *obj;
744 acpi_status status;
745 u32 devices;
746
747 status = wmi_query_block(WMID_GUID2, 1, &out);
748 if (ACPI_FAILURE(status))
749 return status;
750
751 obj = (union acpi_object *) out.pointer;
752 if (obj && obj->type == ACPI_TYPE_BUFFER &&
753 obj->buffer.length == sizeof(u32)) {
754 devices = *((u32 *) obj->buffer.pointer);
755 } else {
756 return AE_ERROR;
757 }
758
759 /* Not sure on the meaning of the relevant bits yet to detect these */
760 interface->capability |= ACER_CAP_WIRELESS;
761 interface->capability |= ACER_CAP_THREEG;
762
763 /* WMID always provides brightness methods */
764 interface->capability |= ACER_CAP_BRIGHTNESS;
765
766 if (devices & 0x10)
767 interface->capability |= ACER_CAP_BLUETOOTH;
768
769 if (!(devices & 0x20))
770 max_brightness = 0x9;
771
772 return status;
773}
774
775static struct wmi_interface wmid_interface = {
776 .type = ACER_WMID,
777};
778
779/*
780 * Generic Device (interface-independent)
781 */
782
783static acpi_status get_u32(u32 *value, u32 cap)
784{
785 acpi_status status = AE_ERROR;
786
787 switch (interface->type) {
788 case ACER_AMW0:
789 status = AMW0_get_u32(value, cap, interface);
790 break;
791 case ACER_AMW0_V2:
792 if (cap == ACER_CAP_MAILLED) {
793 status = AMW0_get_u32(value, cap, interface);
794 break;
795 }
796 case ACER_WMID:
797 status = WMID_get_u32(value, cap, interface);
798 break;
799 }
800
801 return status;
802}
803
804static acpi_status set_u32(u32 value, u32 cap)
805{
806 acpi_status status;
807
808 if (interface->capability & cap) {
809 switch (interface->type) {
810 case ACER_AMW0:
811 return AMW0_set_u32(value, cap, interface);
812 case ACER_AMW0_V2:
813 if (cap == ACER_CAP_MAILLED)
814 return AMW0_set_u32(value, cap, interface);
815
816 /*
817 * On some models, some WMID methods don't toggle
818 * properly. For those cases, we want to run the AMW0
819 * method afterwards to be certain we've really toggled
820 * the device state.
821 */
822 if (cap == ACER_CAP_WIRELESS ||
823 cap == ACER_CAP_BLUETOOTH) {
824 status = WMID_set_u32(value, cap, interface);
825 if (ACPI_FAILURE(status))
826 return status;
827
828 return AMW0_set_u32(value, cap, interface);
829 }
830 case ACER_WMID:
831 return WMID_set_u32(value, cap, interface);
832 default:
833 return AE_BAD_PARAMETER;
834 }
835 }
836 return AE_BAD_PARAMETER;
837}
838
839static void __init acer_commandline_init(void)
840{
841 /*
842 * These will all fail silently if the value given is invalid, or the
843 * capability isn't available on the given interface
844 */
845 set_u32(mailled, ACER_CAP_MAILLED);
846 set_u32(threeg, ACER_CAP_THREEG);
847 set_u32(brightness, ACER_CAP_BRIGHTNESS);
848}
849
850/*
851 * LED device (Mail LED only, no other LEDs known yet)
852 */
853static void mail_led_set(struct led_classdev *led_cdev,
854enum led_brightness value)
855{
856 set_u32(value, ACER_CAP_MAILLED);
857}
858
859static struct led_classdev mail_led = {
860 .name = "acer-wmi::mail",
861 .brightness_set = mail_led_set,
862};
863
864static int __devinit acer_led_init(struct device *dev)
865{
866 return led_classdev_register(dev, &mail_led);
867}
868
869static void acer_led_exit(void)
870{
871 led_classdev_unregister(&mail_led);
872}
873
874/*
875 * Backlight device
876 */
877static struct backlight_device *acer_backlight_device;
878
879static int read_brightness(struct backlight_device *bd)
880{
881 u32 value;
882 get_u32(&value, ACER_CAP_BRIGHTNESS);
883 return value;
884}
885
886static int update_bl_status(struct backlight_device *bd)
887{
888 int intensity = bd->props.brightness;
889
890 if (bd->props.power != FB_BLANK_UNBLANK)
891 intensity = 0;
892 if (bd->props.fb_blank != FB_BLANK_UNBLANK)
893 intensity = 0;
894
895 set_u32(intensity, ACER_CAP_BRIGHTNESS);
896
897 return 0;
898}
899
900static struct backlight_ops acer_bl_ops = {
901 .get_brightness = read_brightness,
902 .update_status = update_bl_status,
903};
904
905static int __devinit acer_backlight_init(struct device *dev)
906{
907 struct backlight_device *bd;
908
909 bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops);
910 if (IS_ERR(bd)) {
911 printk(ACER_ERR "Could not register Acer backlight device\n");
912 acer_backlight_device = NULL;
913 return PTR_ERR(bd);
914 }
915
916 acer_backlight_device = bd;
917
918 bd->props.power = FB_BLANK_UNBLANK;
919 bd->props.brightness = max_brightness;
920 bd->props.max_brightness = max_brightness;
921 backlight_update_status(bd);
922 return 0;
923}
924
925static void acer_backlight_exit(void)
926{
927 backlight_device_unregister(acer_backlight_device);
928}
929
930/*
931 * Rfkill devices
932 */
933static void acer_rfkill_update(struct work_struct *ignored);
934static DECLARE_DELAYED_WORK(acer_rfkill_work, acer_rfkill_update);
935static void acer_rfkill_update(struct work_struct *ignored)
936{
937 u32 state;
938 acpi_status status;
939
940 status = get_u32(&state, ACER_CAP_WIRELESS);
941 if (ACPI_SUCCESS(status))
942 rfkill_force_state(wireless_rfkill, state ?
943 RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED);
944
945 if (has_cap(ACER_CAP_BLUETOOTH)) {
946 status = get_u32(&state, ACER_CAP_BLUETOOTH);
947 if (ACPI_SUCCESS(status))
948 rfkill_force_state(bluetooth_rfkill, state ?
949 RFKILL_STATE_UNBLOCKED :
950 RFKILL_STATE_SOFT_BLOCKED);
951 }
952
953 schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
954}
955
956static int acer_rfkill_set(void *data, enum rfkill_state state)
957{
958 acpi_status status;
959 u32 *cap = data;
960 status = set_u32((u32) (state == RFKILL_STATE_UNBLOCKED), *cap);
961 if (ACPI_FAILURE(status))
962 return -ENODEV;
963 return 0;
964}
965
966static struct rfkill * acer_rfkill_register(struct device *dev,
967enum rfkill_type type, char *name, u32 cap)
968{
969 int err;
970 u32 state;
971 u32 *data;
972 struct rfkill *rfkill_dev;
973
974 rfkill_dev = rfkill_allocate(dev, type);
975 if (!rfkill_dev)
976 return ERR_PTR(-ENOMEM);
977 rfkill_dev->name = name;
978 get_u32(&state, cap);
979 rfkill_dev->state = state ? RFKILL_STATE_UNBLOCKED :
980 RFKILL_STATE_SOFT_BLOCKED;
981 data = kzalloc(sizeof(u32), GFP_KERNEL);
982 if (!data) {
983 rfkill_free(rfkill_dev);
984 return ERR_PTR(-ENOMEM);
985 }
986 *data = cap;
987 rfkill_dev->data = data;
988 rfkill_dev->toggle_radio = acer_rfkill_set;
989 rfkill_dev->user_claim_unsupported = 1;
990
991 err = rfkill_register(rfkill_dev);
992 if (err) {
993 kfree(rfkill_dev->data);
994 rfkill_free(rfkill_dev);
995 return ERR_PTR(err);
996 }
997 return rfkill_dev;
998}
999
1000static int acer_rfkill_init(struct device *dev)
1001{
1002 wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN,
1003 "acer-wireless", ACER_CAP_WIRELESS);
1004 if (IS_ERR(wireless_rfkill))
1005 return PTR_ERR(wireless_rfkill);
1006
1007 if (has_cap(ACER_CAP_BLUETOOTH)) {
1008 bluetooth_rfkill = acer_rfkill_register(dev,
1009 RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
1010 ACER_CAP_BLUETOOTH);
1011 if (IS_ERR(bluetooth_rfkill)) {
1012 kfree(wireless_rfkill->data);
1013 rfkill_unregister(wireless_rfkill);
1014 return PTR_ERR(bluetooth_rfkill);
1015 }
1016 }
1017
1018 schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
1019
1020 return 0;
1021}
1022
1023static void acer_rfkill_exit(void)
1024{
1025 cancel_delayed_work_sync(&acer_rfkill_work);
1026 kfree(wireless_rfkill->data);
1027 rfkill_unregister(wireless_rfkill);
1028 if (has_cap(ACER_CAP_BLUETOOTH)) {
1029 kfree(wireless_rfkill->data);
1030 rfkill_unregister(bluetooth_rfkill);
1031 }
1032 return;
1033}
1034
1035/*
1036 * sysfs interface
1037 */
1038static ssize_t show_bool_threeg(struct device *dev,
1039 struct device_attribute *attr, char *buf)
1040{
1041 u32 result; \
1042 acpi_status status = get_u32(&result, ACER_CAP_THREEG);
1043 if (ACPI_SUCCESS(status))
1044 return sprintf(buf, "%u\n", result);
1045 return sprintf(buf, "Read error\n");
1046}
1047
1048static ssize_t set_bool_threeg(struct device *dev,
1049 struct device_attribute *attr, const char *buf, size_t count)
1050{
1051 u32 tmp = simple_strtoul(buf, NULL, 10);
1052 acpi_status status = set_u32(tmp, ACER_CAP_THREEG);
1053 if (ACPI_FAILURE(status))
1054 return -EINVAL;
1055 return count;
1056}
1057static DEVICE_ATTR(threeg, S_IWUGO | S_IRUGO | S_IWUSR, show_bool_threeg,
1058 set_bool_threeg);
1059
1060static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
1061 char *buf)
1062{
1063 switch (interface->type) {
1064 case ACER_AMW0:
1065 return sprintf(buf, "AMW0\n");
1066 case ACER_AMW0_V2:
1067 return sprintf(buf, "AMW0 v2\n");
1068 case ACER_WMID:
1069 return sprintf(buf, "WMID\n");
1070 default:
1071 return sprintf(buf, "Error!\n");
1072 }
1073}
1074
1075static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR,
1076 show_interface, NULL);
1077
1078/*
1079 * debugfs functions
1080 */
1081static u32 get_wmid_devices(void)
1082{
1083 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
1084 union acpi_object *obj;
1085 acpi_status status;
1086
1087 status = wmi_query_block(WMID_GUID2, 1, &out);
1088 if (ACPI_FAILURE(status))
1089 return 0;
1090
1091 obj = (union acpi_object *) out.pointer;
1092 if (obj && obj->type == ACPI_TYPE_BUFFER &&
1093 obj->buffer.length == sizeof(u32)) {
1094 return *((u32 *) obj->buffer.pointer);
1095 } else {
1096 return 0;
1097 }
1098}
1099
1100/*
1101 * Platform device
1102 */
1103static int __devinit acer_platform_probe(struct platform_device *device)
1104{
1105 int err;
1106
1107 if (has_cap(ACER_CAP_MAILLED)) {
1108 err = acer_led_init(&device->dev);
1109 if (err)
1110 goto error_mailled;
1111 }
1112
1113 if (has_cap(ACER_CAP_BRIGHTNESS)) {
1114 err = acer_backlight_init(&device->dev);
1115 if (err)
1116 goto error_brightness;
1117 }
1118
1119 err = acer_rfkill_init(&device->dev);
1120
1121 return err;
1122
1123error_brightness:
1124 acer_led_exit();
1125error_mailled:
1126 return err;
1127}
1128
1129static int acer_platform_remove(struct platform_device *device)
1130{
1131 if (has_cap(ACER_CAP_MAILLED))
1132 acer_led_exit();
1133 if (has_cap(ACER_CAP_BRIGHTNESS))
1134 acer_backlight_exit();
1135
1136 acer_rfkill_exit();
1137 return 0;
1138}
1139
1140static int acer_platform_suspend(struct platform_device *dev,
1141pm_message_t state)
1142{
1143 u32 value;
1144 struct acer_data *data = &interface->data;
1145
1146 if (!data)
1147 return -ENOMEM;
1148
1149 if (has_cap(ACER_CAP_MAILLED)) {
1150 get_u32(&value, ACER_CAP_MAILLED);
1151 data->mailled = value;
1152 }
1153
1154 if (has_cap(ACER_CAP_BRIGHTNESS)) {
1155 get_u32(&value, ACER_CAP_BRIGHTNESS);
1156 data->brightness = value;
1157 }
1158
1159 return 0;
1160}
1161
1162static int acer_platform_resume(struct platform_device *device)
1163{
1164 struct acer_data *data = &interface->data;
1165
1166 if (!data)
1167 return -ENOMEM;
1168
1169 if (has_cap(ACER_CAP_MAILLED))
1170 set_u32(data->mailled, ACER_CAP_MAILLED);
1171
1172 if (has_cap(ACER_CAP_BRIGHTNESS))
1173 set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
1174
1175 return 0;
1176}
1177
1178static struct platform_driver acer_platform_driver = {
1179 .driver = {
1180 .name = "acer-wmi",
1181 .owner = THIS_MODULE,
1182 },
1183 .probe = acer_platform_probe,
1184 .remove = acer_platform_remove,
1185 .suspend = acer_platform_suspend,
1186 .resume = acer_platform_resume,
1187};
1188
1189static struct platform_device *acer_platform_device;
1190
1191static int remove_sysfs(struct platform_device *device)
1192{
1193 if (has_cap(ACER_CAP_THREEG))
1194 device_remove_file(&device->dev, &dev_attr_threeg);
1195
1196 device_remove_file(&device->dev, &dev_attr_interface);
1197
1198 return 0;
1199}
1200
1201static int create_sysfs(void)
1202{
1203 int retval = -ENOMEM;
1204
1205 if (has_cap(ACER_CAP_THREEG)) {
1206 retval = device_create_file(&acer_platform_device->dev,
1207 &dev_attr_threeg);
1208 if (retval)
1209 goto error_sysfs;
1210 }
1211
1212 retval = device_create_file(&acer_platform_device->dev,
1213 &dev_attr_interface);
1214 if (retval)
1215 goto error_sysfs;
1216
1217 return 0;
1218
1219error_sysfs:
1220 remove_sysfs(acer_platform_device);
1221 return retval;
1222}
1223
1224static void remove_debugfs(void)
1225{
1226 debugfs_remove(interface->debug.devices);
1227 debugfs_remove(interface->debug.root);
1228}
1229
1230static int create_debugfs(void)
1231{
1232 interface->debug.root = debugfs_create_dir("acer-wmi", NULL);
1233 if (!interface->debug.root) {
1234 printk(ACER_ERR "Failed to create debugfs directory");
1235 return -ENOMEM;
1236 }
1237
1238 interface->debug.devices = debugfs_create_u32("devices", S_IRUGO,
1239 interface->debug.root,
1240 &interface->debug.wmid_devices);
1241 if (!interface->debug.devices)
1242 goto error_debugfs;
1243
1244 return 0;
1245
1246error_debugfs:
1247 remove_debugfs();
1248 return -ENOMEM;
1249}
1250
1251static int __init acer_wmi_init(void)
1252{
1253 int err;
1254
1255 printk(ACER_INFO "Acer Laptop ACPI-WMI Extras\n");
1256
1257 find_quirks();
1258
1259 /*
1260 * Detect which ACPI-WMI interface we're using.
1261 */
1262 if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1263 interface = &AMW0_V2_interface;
1264
1265 if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1266 interface = &wmid_interface;
1267
1268 if (wmi_has_guid(WMID_GUID2) && interface) {
1269 if (ACPI_FAILURE(WMID_set_capabilities())) {
1270 printk(ACER_ERR "Unable to detect available WMID "
1271 "devices\n");
1272 return -ENODEV;
1273 }
1274 } else if (!wmi_has_guid(WMID_GUID2) && interface) {
1275 printk(ACER_ERR "No WMID device detection method found\n");
1276 return -ENODEV;
1277 }
1278
1279 if (wmi_has_guid(AMW0_GUID1) && !wmi_has_guid(WMID_GUID1)) {
1280 interface = &AMW0_interface;
1281
1282 if (ACPI_FAILURE(AMW0_set_capabilities())) {
1283 printk(ACER_ERR "Unable to detect available AMW0 "
1284 "devices\n");
1285 return -ENODEV;
1286 }
1287 }
1288
1289 if (wmi_has_guid(AMW0_GUID1))
1290 AMW0_find_mailled();
1291
1292 if (!interface) {
1293 printk(ACER_ERR "No or unsupported WMI interface, unable to "
1294 "load\n");
1295 return -ENODEV;
1296 }
1297
1298 set_quirks();
1299
1300 if (!acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) {
1301 interface->capability &= ~ACER_CAP_BRIGHTNESS;
1302 printk(ACER_INFO "Brightness must be controlled by "
1303 "generic video driver\n");
1304 }
1305
1306 if (platform_driver_register(&acer_platform_driver)) {
1307 printk(ACER_ERR "Unable to register platform driver.\n");
1308 goto error_platform_register;
1309 }
1310 acer_platform_device = platform_device_alloc("acer-wmi", -1);
1311 platform_device_add(acer_platform_device);
1312
1313 err = create_sysfs();
1314 if (err)
1315 return err;
1316
1317 if (wmi_has_guid(WMID_GUID2)) {
1318 interface->debug.wmid_devices = get_wmid_devices();
1319 err = create_debugfs();
1320 if (err)
1321 return err;
1322 }
1323
1324 /* Override any initial settings with values from the commandline */
1325 acer_commandline_init();
1326
1327 return 0;
1328
1329error_platform_register:
1330 return -ENODEV;
1331}
1332
1333static void __exit acer_wmi_exit(void)
1334{
1335 remove_sysfs(acer_platform_device);
1336 remove_debugfs();
1337 platform_device_del(acer_platform_device);
1338 platform_driver_unregister(&acer_platform_driver);
1339
1340 printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n");
1341 return;
1342}
1343
1344module_init(acer_wmi_init);
1345module_exit(acer_wmi_exit);
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
new file mode 100644
index 000000000000..8fb8b3591048
--- /dev/null
+++ b/drivers/platform/x86/asus-laptop.c
@@ -0,0 +1,1266 @@
1/*
2 * asus-laptop.c - Asus Laptop Support
3 *
4 *
5 * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
6 * Copyright (C) 2006-2007 Corentin Chary
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 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 *
23 * The development page for this driver is located at
24 * http://sourceforge.net/projects/acpi4asus/
25 *
26 * Credits:
27 * Pontus Fuchs - Helper functions, cleanup
28 * Johann Wiesner - Small compile fixes
29 * John Belmonte - ACPI code for Toshiba laptop was a good starting point.
30 * Eric Burghard - LED display support for W1N
31 * Josh Green - Light Sens support
32 * Thomas Tuttle - His first patch for led support was very helpfull
33 * Sam Lin - GPS support
34 */
35
36#include <linux/kernel.h>
37#include <linux/module.h>
38#include <linux/init.h>
39#include <linux/types.h>
40#include <linux/err.h>
41#include <linux/proc_fs.h>
42#include <linux/backlight.h>
43#include <linux/fb.h>
44#include <linux/leds.h>
45#include <linux/platform_device.h>
46#include <acpi/acpi_drivers.h>
47#include <acpi/acpi_bus.h>
48#include <asm/uaccess.h>
49
50#define ASUS_LAPTOP_VERSION "0.42"
51
52#define ASUS_HOTK_NAME "Asus Laptop Support"
53#define ASUS_HOTK_CLASS "hotkey"
54#define ASUS_HOTK_DEVICE_NAME "Hotkey"
55#define ASUS_HOTK_FILE "asus-laptop"
56#define ASUS_HOTK_PREFIX "\\_SB.ATKD."
57
58/*
59 * Some events we use, same for all Asus
60 */
61#define ATKD_BR_UP 0x10
62#define ATKD_BR_DOWN 0x20
63#define ATKD_LCD_ON 0x33
64#define ATKD_LCD_OFF 0x34
65
66/*
67 * Known bits returned by \_SB.ATKD.HWRS
68 */
69#define WL_HWRS 0x80
70#define BT_HWRS 0x100
71
72/*
73 * Flags for hotk status
74 * WL_ON and BT_ON are also used for wireless_status()
75 */
76#define WL_ON 0x01 //internal Wifi
77#define BT_ON 0x02 //internal Bluetooth
78#define MLED_ON 0x04 //mail LED
79#define TLED_ON 0x08 //touchpad LED
80#define RLED_ON 0x10 //Record LED
81#define PLED_ON 0x20 //Phone LED
82#define GLED_ON 0x40 //Gaming LED
83#define LCD_ON 0x80 //LCD backlight
84#define GPS_ON 0x100 //GPS
85
86#define ASUS_LOG ASUS_HOTK_FILE ": "
87#define ASUS_ERR KERN_ERR ASUS_LOG
88#define ASUS_WARNING KERN_WARNING ASUS_LOG
89#define ASUS_NOTICE KERN_NOTICE ASUS_LOG
90#define ASUS_INFO KERN_INFO ASUS_LOG
91#define ASUS_DEBUG KERN_DEBUG ASUS_LOG
92
93MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
94MODULE_DESCRIPTION(ASUS_HOTK_NAME);
95MODULE_LICENSE("GPL");
96
97/* WAPF defines the behavior of the Fn+Fx wlan key
98 * The significance of values is yet to be found, but
99 * most of the time:
100 * 0x0 will do nothing
101 * 0x1 will allow to control the device with Fn+Fx key.
102 * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key
103 * 0x5 like 0x1 or 0x4
104 * So, if something doesn't work as you want, just try other values =)
105 */
106static uint wapf = 1;
107module_param(wapf, uint, 0644);
108MODULE_PARM_DESC(wapf, "WAPF value");
109
110#define ASUS_HANDLE(object, paths...) \
111 static acpi_handle object##_handle = NULL; \
112 static char *object##_paths[] = { paths }
113
114/* LED */
115ASUS_HANDLE(mled_set, ASUS_HOTK_PREFIX "MLED");
116ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED");
117ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED"); /* W1JC */
118ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED"); /* A7J */
119ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED"); /* G1, G2 (probably) */
120
121/* LEDD */
122ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM");
123
124/* Bluetooth and WLAN
125 * WLED and BLED are not handled like other XLED, because in some dsdt
126 * they also control the WLAN/Bluetooth device.
127 */
128ASUS_HANDLE(wl_switch, ASUS_HOTK_PREFIX "WLED");
129ASUS_HANDLE(bt_switch, ASUS_HOTK_PREFIX "BLED");
130ASUS_HANDLE(wireless_status, ASUS_HOTK_PREFIX "RSTS"); /* All new models */
131
132/* Brightness */
133ASUS_HANDLE(brightness_set, ASUS_HOTK_PREFIX "SPLV");
134ASUS_HANDLE(brightness_get, ASUS_HOTK_PREFIX "GPLV");
135
136/* Backlight */
137ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */
138 "\\_SB.PCI0.ISA.EC0._Q10", /* A1x */
139 "\\_SB.PCI0.PX40.ECD0._Q10", /* L3C */
140 "\\_SB.PCI0.PX40.EC0.Q10", /* M1A */
141 "\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */
142 "\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */
143 "\\_SB.PCI0.PX40.Q10", /* S1x */
144 "\\Q10"); /* A2x, L2D, L3D, M2E */
145
146/* Display */
147ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP");
148ASUS_HANDLE(display_get, "\\_SB.PCI0.P0P1.VGA.GETD", /* A6B, A6K A6R A7D F3JM L4R M6R A3G
149 M6A M6V VX-1 V6J V6V W3Z */
150 "\\_SB.PCI0.P0P2.VGA.GETD", /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V
151 S5A M5A z33A W1Jc W2V G1 */
152 "\\_SB.PCI0.P0P3.VGA.GETD", /* A6V A6Q */
153 "\\_SB.PCI0.P0PA.VGA.GETD", /* A6T, A6M */
154 "\\_SB.PCI0.PCI1.VGAC.NMAP", /* L3C */
155 "\\_SB.PCI0.VGA.GETD", /* Z96F */
156 "\\ACTD", /* A2D */
157 "\\ADVG", /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
158 "\\DNXT", /* P30 */
159 "\\INFB", /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
160 "\\SSTE"); /* A3F A6F A3N A3L M6N W3N W6A */
161
162ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */
163ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */
164
165/* GPS */
166/* R2H use different handle for GPS on/off */
167ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON"); /* R2H */
168ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */
169ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST");
170
171/*
172 * This is the main structure, we can use it to store anything interesting
173 * about the hotk device
174 */
175struct asus_hotk {
176 char *name; //laptop name
177 struct acpi_device *device; //the device we are in
178 acpi_handle handle; //the handle of the hotk device
179 char status; //status of the hotk, for LEDs, ...
180 u32 ledd_status; //status of the LED display
181 u8 light_level; //light sensor level
182 u8 light_switch; //light sensor switch value
183 u16 event_count[128]; //count for each event TODO make this better
184};
185
186/*
187 * This header is made available to allow proper configuration given model,
188 * revision number , ... this info cannot go in struct asus_hotk because it is
189 * available before the hotk
190 */
191static struct acpi_table_header *asus_info;
192
193/* The actual device the driver binds to */
194static struct asus_hotk *hotk;
195
196/*
197 * The hotkey driver declaration
198 */
199static const struct acpi_device_id asus_device_ids[] = {
200 {"ATK0100", 0},
201 {"", 0},
202};
203MODULE_DEVICE_TABLE(acpi, asus_device_ids);
204
205static int asus_hotk_add(struct acpi_device *device);
206static int asus_hotk_remove(struct acpi_device *device, int type);
207static struct acpi_driver asus_hotk_driver = {
208 .name = ASUS_HOTK_NAME,
209 .class = ASUS_HOTK_CLASS,
210 .ids = asus_device_ids,
211 .ops = {
212 .add = asus_hotk_add,
213 .remove = asus_hotk_remove,
214 },
215};
216
217/* The backlight device /sys/class/backlight */
218static struct backlight_device *asus_backlight_device;
219
220/*
221 * The backlight class declaration
222 */
223static int read_brightness(struct backlight_device *bd);
224static int update_bl_status(struct backlight_device *bd);
225static struct backlight_ops asusbl_ops = {
226 .get_brightness = read_brightness,
227 .update_status = update_bl_status,
228};
229
230/* These functions actually update the LED's, and are called from a
231 * workqueue. By doing this as separate work rather than when the LED
232 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
233 * potentially bad time, such as a timer interrupt. */
234static struct workqueue_struct *led_workqueue;
235
236#define ASUS_LED(object, ledname) \
237 static void object##_led_set(struct led_classdev *led_cdev, \
238 enum led_brightness value); \
239 static void object##_led_update(struct work_struct *ignored); \
240 static int object##_led_wk; \
241 static DECLARE_WORK(object##_led_work, object##_led_update); \
242 static struct led_classdev object##_led = { \
243 .name = "asus::" ledname, \
244 .brightness_set = object##_led_set, \
245 }
246
247ASUS_LED(mled, "mail");
248ASUS_LED(tled, "touchpad");
249ASUS_LED(rled, "record");
250ASUS_LED(pled, "phone");
251ASUS_LED(gled, "gaming");
252
253/*
254 * This function evaluates an ACPI method, given an int as parameter, the
255 * method is searched within the scope of the handle, can be NULL. The output
256 * of the method is written is output, which can also be NULL
257 *
258 * returns 0 if write is successful, -1 else.
259 */
260static int write_acpi_int(acpi_handle handle, const char *method, int val,
261 struct acpi_buffer *output)
262{
263 struct acpi_object_list params; //list of input parameters (an int here)
264 union acpi_object in_obj; //the only param we use
265 acpi_status status;
266
267 if (!handle)
268 return 0;
269
270 params.count = 1;
271 params.pointer = &in_obj;
272 in_obj.type = ACPI_TYPE_INTEGER;
273 in_obj.integer.value = val;
274
275 status = acpi_evaluate_object(handle, (char *)method, &params, output);
276 if (status == AE_OK)
277 return 0;
278 else
279 return -1;
280}
281
282static int read_wireless_status(int mask)
283{
284 unsigned long long status;
285 acpi_status rv = AE_OK;
286
287 if (!wireless_status_handle)
288 return (hotk->status & mask) ? 1 : 0;
289
290 rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status);
291 if (ACPI_FAILURE(rv))
292 printk(ASUS_WARNING "Error reading Wireless status\n");
293 else
294 return (status & mask) ? 1 : 0;
295
296 return (hotk->status & mask) ? 1 : 0;
297}
298
299static int read_gps_status(void)
300{
301 unsigned long long status;
302 acpi_status rv = AE_OK;
303
304 rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status);
305 if (ACPI_FAILURE(rv))
306 printk(ASUS_WARNING "Error reading GPS status\n");
307 else
308 return status ? 1 : 0;
309
310 return (hotk->status & GPS_ON) ? 1 : 0;
311}
312
313/* Generic LED functions */
314static int read_status(int mask)
315{
316 /* There is a special method for both wireless devices */
317 if (mask == BT_ON || mask == WL_ON)
318 return read_wireless_status(mask);
319 else if (mask == GPS_ON)
320 return read_gps_status();
321
322 return (hotk->status & mask) ? 1 : 0;
323}
324
325static void write_status(acpi_handle handle, int out, int mask)
326{
327 hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask);
328
329 switch (mask) {
330 case MLED_ON:
331 out = !(out & 0x1);
332 break;
333 case GLED_ON:
334 out = (out & 0x1) + 1;
335 break;
336 case GPS_ON:
337 handle = (out) ? gps_on_handle : gps_off_handle;
338 out = 0x02;
339 break;
340 default:
341 out &= 0x1;
342 break;
343 }
344
345 if (write_acpi_int(handle, NULL, out, NULL))
346 printk(ASUS_WARNING " write failed %x\n", mask);
347}
348
349/* /sys/class/led handlers */
350#define ASUS_LED_HANDLER(object, mask) \
351 static void object##_led_set(struct led_classdev *led_cdev, \
352 enum led_brightness value) \
353 { \
354 object##_led_wk = (value > 0) ? 1 : 0; \
355 queue_work(led_workqueue, &object##_led_work); \
356 } \
357 static void object##_led_update(struct work_struct *ignored) \
358 { \
359 int value = object##_led_wk; \
360 write_status(object##_set_handle, value, (mask)); \
361 }
362
363ASUS_LED_HANDLER(mled, MLED_ON);
364ASUS_LED_HANDLER(pled, PLED_ON);
365ASUS_LED_HANDLER(rled, RLED_ON);
366ASUS_LED_HANDLER(tled, TLED_ON);
367ASUS_LED_HANDLER(gled, GLED_ON);
368
369static int get_lcd_state(void)
370{
371 return read_status(LCD_ON);
372}
373
374static int set_lcd_state(int value)
375{
376 int lcd = 0;
377 acpi_status status = 0;
378
379 lcd = value ? 1 : 0;
380
381 if (lcd == get_lcd_state())
382 return 0;
383
384 if (lcd_switch_handle) {
385 status = acpi_evaluate_object(lcd_switch_handle,
386 NULL, NULL, NULL);
387
388 if (ACPI_FAILURE(status))
389 printk(ASUS_WARNING "Error switching LCD\n");
390 }
391
392 write_status(NULL, lcd, LCD_ON);
393 return 0;
394}
395
396static void lcd_blank(int blank)
397{
398 struct backlight_device *bd = asus_backlight_device;
399
400 if (bd) {
401 bd->props.power = blank;
402 backlight_update_status(bd);
403 }
404}
405
406static int read_brightness(struct backlight_device *bd)
407{
408 unsigned long long value;
409 acpi_status rv = AE_OK;
410
411 rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value);
412 if (ACPI_FAILURE(rv))
413 printk(ASUS_WARNING "Error reading brightness\n");
414
415 return value;
416}
417
418static int set_brightness(struct backlight_device *bd, int value)
419{
420 int ret = 0;
421
422 value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
423 /* 0 <= value <= 15 */
424
425 if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) {
426 printk(ASUS_WARNING "Error changing brightness\n");
427 ret = -EIO;
428 }
429
430 return ret;
431}
432
433static int update_bl_status(struct backlight_device *bd)
434{
435 int rv;
436 int value = bd->props.brightness;
437
438 rv = set_brightness(bd, value);
439 if (rv)
440 return rv;
441
442 value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0;
443 return set_lcd_state(value);
444}
445
446/*
447 * Platform device handlers
448 */
449
450/*
451 * We write our info in page, we begin at offset off and cannot write more
452 * than count bytes. We set eof to 1 if we handle those 2 values. We return the
453 * number of bytes written in page
454 */
455static ssize_t show_infos(struct device *dev,
456 struct device_attribute *attr, char *page)
457{
458 int len = 0;
459 unsigned long long temp;
460 char buf[16]; //enough for all info
461 acpi_status rv = AE_OK;
462
463 /*
464 * We use the easy way, we don't care of off and count, so we don't set eof
465 * to 1
466 */
467
468 len += sprintf(page, ASUS_HOTK_NAME " " ASUS_LAPTOP_VERSION "\n");
469 len += sprintf(page + len, "Model reference : %s\n", hotk->name);
470 /*
471 * The SFUN method probably allows the original driver to get the list
472 * of features supported by a given model. For now, 0x0100 or 0x0800
473 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
474 * The significance of others is yet to be found.
475 */
476 rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp);
477 if (!ACPI_FAILURE(rv))
478 len += sprintf(page + len, "SFUN value : 0x%04x\n",
479 (uint) temp);
480 /*
481 * Another value for userspace: the ASYM method returns 0x02 for
482 * battery low and 0x04 for battery critical, its readings tend to be
483 * more accurate than those provided by _BST.
484 * Note: since not all the laptops provide this method, errors are
485 * silently ignored.
486 */
487 rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp);
488 if (!ACPI_FAILURE(rv))
489 len += sprintf(page + len, "ASYM value : 0x%04x\n",
490 (uint) temp);
491 if (asus_info) {
492 snprintf(buf, 16, "%d", asus_info->length);
493 len += sprintf(page + len, "DSDT length : %s\n", buf);
494 snprintf(buf, 16, "%d", asus_info->checksum);
495 len += sprintf(page + len, "DSDT checksum : %s\n", buf);
496 snprintf(buf, 16, "%d", asus_info->revision);
497 len += sprintf(page + len, "DSDT revision : %s\n", buf);
498 snprintf(buf, 7, "%s", asus_info->oem_id);
499 len += sprintf(page + len, "OEM id : %s\n", buf);
500 snprintf(buf, 9, "%s", asus_info->oem_table_id);
501 len += sprintf(page + len, "OEM table id : %s\n", buf);
502 snprintf(buf, 16, "%x", asus_info->oem_revision);
503 len += sprintf(page + len, "OEM revision : 0x%s\n", buf);
504 snprintf(buf, 5, "%s", asus_info->asl_compiler_id);
505 len += sprintf(page + len, "ASL comp vendor id : %s\n", buf);
506 snprintf(buf, 16, "%x", asus_info->asl_compiler_revision);
507 len += sprintf(page + len, "ASL comp revision : 0x%s\n", buf);
508 }
509
510 return len;
511}
512
513static int parse_arg(const char *buf, unsigned long count, int *val)
514{
515 if (!count)
516 return 0;
517 if (count > 31)
518 return -EINVAL;
519 if (sscanf(buf, "%i", val) != 1)
520 return -EINVAL;
521 return count;
522}
523
524static ssize_t store_status(const char *buf, size_t count,
525 acpi_handle handle, int mask)
526{
527 int rv, value;
528 int out = 0;
529
530 rv = parse_arg(buf, count, &value);
531 if (rv > 0)
532 out = value ? 1 : 0;
533
534 write_status(handle, out, mask);
535
536 return rv;
537}
538
539/*
540 * LEDD display
541 */
542static ssize_t show_ledd(struct device *dev,
543 struct device_attribute *attr, char *buf)
544{
545 return sprintf(buf, "0x%08x\n", hotk->ledd_status);
546}
547
548static ssize_t store_ledd(struct device *dev, struct device_attribute *attr,
549 const char *buf, size_t count)
550{
551 int rv, value;
552
553 rv = parse_arg(buf, count, &value);
554 if (rv > 0) {
555 if (write_acpi_int(ledd_set_handle, NULL, value, NULL))
556 printk(ASUS_WARNING "LED display write failed\n");
557 else
558 hotk->ledd_status = (u32) value;
559 }
560 return rv;
561}
562
563/*
564 * WLAN
565 */
566static ssize_t show_wlan(struct device *dev,
567 struct device_attribute *attr, char *buf)
568{
569 return sprintf(buf, "%d\n", read_status(WL_ON));
570}
571
572static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,
573 const char *buf, size_t count)
574{
575 return store_status(buf, count, wl_switch_handle, WL_ON);
576}
577
578/*
579 * Bluetooth
580 */
581static ssize_t show_bluetooth(struct device *dev,
582 struct device_attribute *attr, char *buf)
583{
584 return sprintf(buf, "%d\n", read_status(BT_ON));
585}
586
587static ssize_t store_bluetooth(struct device *dev,
588 struct device_attribute *attr, const char *buf,
589 size_t count)
590{
591 return store_status(buf, count, bt_switch_handle, BT_ON);
592}
593
594/*
595 * Display
596 */
597static void set_display(int value)
598{
599 /* no sanity check needed for now */
600 if (write_acpi_int(display_set_handle, NULL, value, NULL))
601 printk(ASUS_WARNING "Error setting display\n");
602 return;
603}
604
605static int read_display(void)
606{
607 unsigned long long value = 0;
608 acpi_status rv = AE_OK;
609
610 /* In most of the case, we know how to set the display, but sometime
611 we can't read it */
612 if (display_get_handle) {
613 rv = acpi_evaluate_integer(display_get_handle, NULL,
614 NULL, &value);
615 if (ACPI_FAILURE(rv))
616 printk(ASUS_WARNING "Error reading display status\n");
617 }
618
619 value &= 0x0F; /* needed for some models, shouldn't hurt others */
620
621 return value;
622}
623
624/*
625 * Now, *this* one could be more user-friendly, but so far, no-one has
626 * complained. The significance of bits is the same as in store_disp()
627 */
628static ssize_t show_disp(struct device *dev,
629 struct device_attribute *attr, char *buf)
630{
631 return sprintf(buf, "%d\n", read_display());
632}
633
634/*
635 * Experimental support for display switching. As of now: 1 should activate
636 * the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI.
637 * Any combination (bitwise) of these will suffice. I never actually tested 4
638 * displays hooked up simultaneously, so be warned. See the acpi4asus README
639 * for more info.
640 */
641static ssize_t store_disp(struct device *dev, struct device_attribute *attr,
642 const char *buf, size_t count)
643{
644 int rv, value;
645
646 rv = parse_arg(buf, count, &value);
647 if (rv > 0)
648 set_display(value);
649 return rv;
650}
651
652/*
653 * Light Sens
654 */
655static void set_light_sens_switch(int value)
656{
657 if (write_acpi_int(ls_switch_handle, NULL, value, NULL))
658 printk(ASUS_WARNING "Error setting light sensor switch\n");
659 hotk->light_switch = value;
660}
661
662static ssize_t show_lssw(struct device *dev,
663 struct device_attribute *attr, char *buf)
664{
665 return sprintf(buf, "%d\n", hotk->light_switch);
666}
667
668static ssize_t store_lssw(struct device *dev, struct device_attribute *attr,
669 const char *buf, size_t count)
670{
671 int rv, value;
672
673 rv = parse_arg(buf, count, &value);
674 if (rv > 0)
675 set_light_sens_switch(value ? 1 : 0);
676
677 return rv;
678}
679
680static void set_light_sens_level(int value)
681{
682 if (write_acpi_int(ls_level_handle, NULL, value, NULL))
683 printk(ASUS_WARNING "Error setting light sensor level\n");
684 hotk->light_level = value;
685}
686
687static ssize_t show_lslvl(struct device *dev,
688 struct device_attribute *attr, char *buf)
689{
690 return sprintf(buf, "%d\n", hotk->light_level);
691}
692
693static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr,
694 const char *buf, size_t count)
695{
696 int rv, value;
697
698 rv = parse_arg(buf, count, &value);
699 if (rv > 0) {
700 value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
701 /* 0 <= value <= 15 */
702 set_light_sens_level(value);
703 }
704
705 return rv;
706}
707
708/*
709 * GPS
710 */
711static ssize_t show_gps(struct device *dev,
712 struct device_attribute *attr, char *buf)
713{
714 return sprintf(buf, "%d\n", read_status(GPS_ON));
715}
716
717static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
718 const char *buf, size_t count)
719{
720 return store_status(buf, count, NULL, GPS_ON);
721}
722
723static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
724{
725 /* TODO Find a better way to handle events count. */
726 if (!hotk)
727 return;
728
729 /*
730 * We need to tell the backlight device when the backlight power is
731 * switched
732 */
733 if (event == ATKD_LCD_ON) {
734 write_status(NULL, 1, LCD_ON);
735 lcd_blank(FB_BLANK_UNBLANK);
736 } else if (event == ATKD_LCD_OFF) {
737 write_status(NULL, 0, LCD_ON);
738 lcd_blank(FB_BLANK_POWERDOWN);
739 }
740
741 acpi_bus_generate_proc_event(hotk->device, event,
742 hotk->event_count[event % 128]++);
743
744 return;
745}
746
747#define ASUS_CREATE_DEVICE_ATTR(_name) \
748 struct device_attribute dev_attr_##_name = { \
749 .attr = { \
750 .name = __stringify(_name), \
751 .mode = 0 }, \
752 .show = NULL, \
753 .store = NULL, \
754 }
755
756#define ASUS_SET_DEVICE_ATTR(_name, _mode, _show, _store) \
757 do { \
758 dev_attr_##_name.attr.mode = _mode; \
759 dev_attr_##_name.show = _show; \
760 dev_attr_##_name.store = _store; \
761 } while(0)
762
763static ASUS_CREATE_DEVICE_ATTR(infos);
764static ASUS_CREATE_DEVICE_ATTR(wlan);
765static ASUS_CREATE_DEVICE_ATTR(bluetooth);
766static ASUS_CREATE_DEVICE_ATTR(display);
767static ASUS_CREATE_DEVICE_ATTR(ledd);
768static ASUS_CREATE_DEVICE_ATTR(ls_switch);
769static ASUS_CREATE_DEVICE_ATTR(ls_level);
770static ASUS_CREATE_DEVICE_ATTR(gps);
771
772static struct attribute *asuspf_attributes[] = {
773 &dev_attr_infos.attr,
774 &dev_attr_wlan.attr,
775 &dev_attr_bluetooth.attr,
776 &dev_attr_display.attr,
777 &dev_attr_ledd.attr,
778 &dev_attr_ls_switch.attr,
779 &dev_attr_ls_level.attr,
780 &dev_attr_gps.attr,
781 NULL
782};
783
784static struct attribute_group asuspf_attribute_group = {
785 .attrs = asuspf_attributes
786};
787
788static struct platform_driver asuspf_driver = {
789 .driver = {
790 .name = ASUS_HOTK_FILE,
791 .owner = THIS_MODULE,
792 }
793};
794
795static struct platform_device *asuspf_device;
796
797static void asus_hotk_add_fs(void)
798{
799 ASUS_SET_DEVICE_ATTR(infos, 0444, show_infos, NULL);
800
801 if (wl_switch_handle)
802 ASUS_SET_DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan);
803
804 if (bt_switch_handle)
805 ASUS_SET_DEVICE_ATTR(bluetooth, 0644,
806 show_bluetooth, store_bluetooth);
807
808 if (display_set_handle && display_get_handle)
809 ASUS_SET_DEVICE_ATTR(display, 0644, show_disp, store_disp);
810 else if (display_set_handle)
811 ASUS_SET_DEVICE_ATTR(display, 0200, NULL, store_disp);
812
813 if (ledd_set_handle)
814 ASUS_SET_DEVICE_ATTR(ledd, 0644, show_ledd, store_ledd);
815
816 if (ls_switch_handle && ls_level_handle) {
817 ASUS_SET_DEVICE_ATTR(ls_level, 0644, show_lslvl, store_lslvl);
818 ASUS_SET_DEVICE_ATTR(ls_switch, 0644, show_lssw, store_lssw);
819 }
820
821 if (gps_status_handle && gps_on_handle && gps_off_handle)
822 ASUS_SET_DEVICE_ATTR(gps, 0644, show_gps, store_gps);
823}
824
825static int asus_handle_init(char *name, acpi_handle * handle,
826 char **paths, int num_paths)
827{
828 int i;
829 acpi_status status;
830
831 for (i = 0; i < num_paths; i++) {
832 status = acpi_get_handle(NULL, paths[i], handle);
833 if (ACPI_SUCCESS(status))
834 return 0;
835 }
836
837 *handle = NULL;
838 return -ENODEV;
839}
840
841#define ASUS_HANDLE_INIT(object) \
842 asus_handle_init(#object, &object##_handle, object##_paths, \
843 ARRAY_SIZE(object##_paths))
844
845/*
846 * This function is used to initialize the hotk with right values. In this
847 * method, we can make all the detection we want, and modify the hotk struct
848 */
849static int asus_hotk_get_info(void)
850{
851 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
852 union acpi_object *model = NULL;
853 unsigned long long bsts_result, hwrs_result;
854 char *string = NULL;
855 acpi_status status;
856
857 /*
858 * Get DSDT headers early enough to allow for differentiating between
859 * models, but late enough to allow acpi_bus_register_driver() to fail
860 * before doing anything ACPI-specific. Should we encounter a machine,
861 * which needs special handling (i.e. its hotkey device has a different
862 * HID), this bit will be moved. A global variable asus_info contains
863 * the DSDT header.
864 */
865 status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
866 if (ACPI_FAILURE(status))
867 printk(ASUS_WARNING "Couldn't get the DSDT table header\n");
868
869 /* We have to write 0 on init this far for all ASUS models */
870 if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
871 printk(ASUS_ERR "Hotkey initialization failed\n");
872 return -ENODEV;
873 }
874
875 /* This needs to be called for some laptops to init properly */
876 status =
877 acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result);
878 if (ACPI_FAILURE(status))
879 printk(ASUS_WARNING "Error calling BSTS\n");
880 else if (bsts_result)
881 printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n",
882 (uint) bsts_result);
883
884 /* This too ... */
885 write_acpi_int(hotk->handle, "CWAP", wapf, NULL);
886
887 /*
888 * Try to match the object returned by INIT to the specific model.
889 * Handle every possible object (or the lack of thereof) the DSDT
890 * writers might throw at us. When in trouble, we pass NULL to
891 * asus_model_match() and try something completely different.
892 */
893 if (buffer.pointer) {
894 model = buffer.pointer;
895 switch (model->type) {
896 case ACPI_TYPE_STRING:
897 string = model->string.pointer;
898 break;
899 case ACPI_TYPE_BUFFER:
900 string = model->buffer.pointer;
901 break;
902 default:
903 string = "";
904 break;
905 }
906 }
907 hotk->name = kstrdup(string, GFP_KERNEL);
908 if (!hotk->name)
909 return -ENOMEM;
910
911 if (*string)
912 printk(ASUS_NOTICE " %s model detected\n", string);
913
914 ASUS_HANDLE_INIT(mled_set);
915 ASUS_HANDLE_INIT(tled_set);
916 ASUS_HANDLE_INIT(rled_set);
917 ASUS_HANDLE_INIT(pled_set);
918 ASUS_HANDLE_INIT(gled_set);
919
920 ASUS_HANDLE_INIT(ledd_set);
921
922 /*
923 * The HWRS method return informations about the hardware.
924 * 0x80 bit is for WLAN, 0x100 for Bluetooth.
925 * The significance of others is yet to be found.
926 * If we don't find the method, we assume the device are present.
927 */
928 status =
929 acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &hwrs_result);
930 if (ACPI_FAILURE(status))
931 hwrs_result = WL_HWRS | BT_HWRS;
932
933 if (hwrs_result & WL_HWRS)
934 ASUS_HANDLE_INIT(wl_switch);
935 if (hwrs_result & BT_HWRS)
936 ASUS_HANDLE_INIT(bt_switch);
937
938 ASUS_HANDLE_INIT(wireless_status);
939
940 ASUS_HANDLE_INIT(brightness_set);
941 ASUS_HANDLE_INIT(brightness_get);
942
943 ASUS_HANDLE_INIT(lcd_switch);
944
945 ASUS_HANDLE_INIT(display_set);
946 ASUS_HANDLE_INIT(display_get);
947
948 /* There is a lot of models with "ALSL", but a few get
949 a real light sens, so we need to check it. */
950 if (!ASUS_HANDLE_INIT(ls_switch))
951 ASUS_HANDLE_INIT(ls_level);
952
953 ASUS_HANDLE_INIT(gps_on);
954 ASUS_HANDLE_INIT(gps_off);
955 ASUS_HANDLE_INIT(gps_status);
956
957 kfree(model);
958
959 return AE_OK;
960}
961
962static int asus_hotk_check(void)
963{
964 int result = 0;
965
966 result = acpi_bus_get_status(hotk->device);
967 if (result)
968 return result;
969
970 if (hotk->device->status.present) {
971 result = asus_hotk_get_info();
972 } else {
973 printk(ASUS_ERR "Hotkey device not present, aborting\n");
974 return -EINVAL;
975 }
976
977 return result;
978}
979
980static int asus_hotk_found;
981
982static int asus_hotk_add(struct acpi_device *device)
983{
984 acpi_status status = AE_OK;
985 int result;
986
987 if (!device)
988 return -EINVAL;
989
990 printk(ASUS_NOTICE "Asus Laptop Support version %s\n",
991 ASUS_LAPTOP_VERSION);
992
993 hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
994 if (!hotk)
995 return -ENOMEM;
996
997 hotk->handle = device->handle;
998 strcpy(acpi_device_name(device), ASUS_HOTK_DEVICE_NAME);
999 strcpy(acpi_device_class(device), ASUS_HOTK_CLASS);
1000 device->driver_data = hotk;
1001 hotk->device = device;
1002
1003 result = asus_hotk_check();
1004 if (result)
1005 goto end;
1006
1007 asus_hotk_add_fs();
1008
1009 /*
1010 * We install the handler, it will receive the hotk in parameter, so, we
1011 * could add other data to the hotk struct
1012 */
1013 status = acpi_install_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
1014 asus_hotk_notify, hotk);
1015 if (ACPI_FAILURE(status))
1016 printk(ASUS_ERR "Error installing notify handler\n");
1017
1018 asus_hotk_found = 1;
1019
1020 /* WLED and BLED are on by default */
1021 write_status(bt_switch_handle, 1, BT_ON);
1022 write_status(wl_switch_handle, 1, WL_ON);
1023
1024 /* If the h/w switch is off, we need to check the real status */
1025 write_status(NULL, read_status(BT_ON), BT_ON);
1026 write_status(NULL, read_status(WL_ON), WL_ON);
1027
1028 /* LCD Backlight is on by default */
1029 write_status(NULL, 1, LCD_ON);
1030
1031 /* LED display is off by default */
1032 hotk->ledd_status = 0xFFF;
1033
1034 /* Set initial values of light sensor and level */
1035 hotk->light_switch = 1; /* Default to light sensor disabled */
1036 hotk->light_level = 0; /* level 5 for sensor sensitivity */
1037
1038 if (ls_switch_handle)
1039 set_light_sens_switch(hotk->light_switch);
1040
1041 if (ls_level_handle)
1042 set_light_sens_level(hotk->light_level);
1043
1044 /* GPS is on by default */
1045 write_status(NULL, 1, GPS_ON);
1046
1047 end:
1048 if (result) {
1049 kfree(hotk->name);
1050 kfree(hotk);
1051 }
1052
1053 return result;
1054}
1055
1056static int asus_hotk_remove(struct acpi_device *device, int type)
1057{
1058 acpi_status status = 0;
1059
1060 if (!device || !acpi_driver_data(device))
1061 return -EINVAL;
1062
1063 status = acpi_remove_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
1064 asus_hotk_notify);
1065 if (ACPI_FAILURE(status))
1066 printk(ASUS_ERR "Error removing notify handler\n");
1067
1068 kfree(hotk->name);
1069 kfree(hotk);
1070
1071 return 0;
1072}
1073
1074static void asus_backlight_exit(void)
1075{
1076 if (asus_backlight_device)
1077 backlight_device_unregister(asus_backlight_device);
1078}
1079
1080#define ASUS_LED_UNREGISTER(object) \
1081 if (object##_led.dev) \
1082 led_classdev_unregister(&object##_led)
1083
1084static void asus_led_exit(void)
1085{
1086 destroy_workqueue(led_workqueue);
1087 ASUS_LED_UNREGISTER(mled);
1088 ASUS_LED_UNREGISTER(tled);
1089 ASUS_LED_UNREGISTER(pled);
1090 ASUS_LED_UNREGISTER(rled);
1091 ASUS_LED_UNREGISTER(gled);
1092}
1093
1094static void __exit asus_laptop_exit(void)
1095{
1096 asus_backlight_exit();
1097 asus_led_exit();
1098
1099 acpi_bus_unregister_driver(&asus_hotk_driver);
1100 sysfs_remove_group(&asuspf_device->dev.kobj, &asuspf_attribute_group);
1101 platform_device_unregister(asuspf_device);
1102 platform_driver_unregister(&asuspf_driver);
1103}
1104
1105static int asus_backlight_init(struct device *dev)
1106{
1107 struct backlight_device *bd;
1108
1109 if (brightness_set_handle && lcd_switch_handle) {
1110 bd = backlight_device_register(ASUS_HOTK_FILE, dev,
1111 NULL, &asusbl_ops);
1112 if (IS_ERR(bd)) {
1113 printk(ASUS_ERR
1114 "Could not register asus backlight device\n");
1115 asus_backlight_device = NULL;
1116 return PTR_ERR(bd);
1117 }
1118
1119 asus_backlight_device = bd;
1120
1121 bd->props.max_brightness = 15;
1122 bd->props.brightness = read_brightness(NULL);
1123 bd->props.power = FB_BLANK_UNBLANK;
1124 backlight_update_status(bd);
1125 }
1126 return 0;
1127}
1128
1129static int asus_led_register(acpi_handle handle,
1130 struct led_classdev *ldev, struct device *dev)
1131{
1132 if (!handle)
1133 return 0;
1134
1135 return led_classdev_register(dev, ldev);
1136}
1137
1138#define ASUS_LED_REGISTER(object, device) \
1139 asus_led_register(object##_set_handle, &object##_led, device)
1140
1141static int asus_led_init(struct device *dev)
1142{
1143 int rv;
1144
1145 rv = ASUS_LED_REGISTER(mled, dev);
1146 if (rv)
1147 goto out;
1148
1149 rv = ASUS_LED_REGISTER(tled, dev);
1150 if (rv)
1151 goto out1;
1152
1153 rv = ASUS_LED_REGISTER(rled, dev);
1154 if (rv)
1155 goto out2;
1156
1157 rv = ASUS_LED_REGISTER(pled, dev);
1158 if (rv)
1159 goto out3;
1160
1161 rv = ASUS_LED_REGISTER(gled, dev);
1162 if (rv)
1163 goto out4;
1164
1165 led_workqueue = create_singlethread_workqueue("led_workqueue");
1166 if (!led_workqueue)
1167 goto out5;
1168
1169 return 0;
1170out5:
1171 rv = -ENOMEM;
1172 ASUS_LED_UNREGISTER(gled);
1173out4:
1174 ASUS_LED_UNREGISTER(pled);
1175out3:
1176 ASUS_LED_UNREGISTER(rled);
1177out2:
1178 ASUS_LED_UNREGISTER(tled);
1179out1:
1180 ASUS_LED_UNREGISTER(mled);
1181out:
1182 return rv;
1183}
1184
1185static int __init asus_laptop_init(void)
1186{
1187 struct device *dev;
1188 int result;
1189
1190 if (acpi_disabled)
1191 return -ENODEV;
1192
1193 result = acpi_bus_register_driver(&asus_hotk_driver);
1194 if (result < 0)
1195 return result;
1196
1197 /*
1198 * This is a bit of a kludge. We only want this module loaded
1199 * for ASUS systems, but there's currently no way to probe the
1200 * ACPI namespace for ASUS HIDs. So we just return failure if
1201 * we didn't find one, which will cause the module to be
1202 * unloaded.
1203 */
1204 if (!asus_hotk_found) {
1205 acpi_bus_unregister_driver(&asus_hotk_driver);
1206 return -ENODEV;
1207 }
1208
1209 dev = acpi_get_physical_device(hotk->device->handle);
1210
1211 if (!acpi_video_backlight_support()) {
1212 result = asus_backlight_init(dev);
1213 if (result)
1214 goto fail_backlight;
1215 } else
1216 printk(ASUS_INFO "Brightness ignored, must be controlled by "
1217 "ACPI video driver\n");
1218
1219 result = asus_led_init(dev);
1220 if (result)
1221 goto fail_led;
1222
1223 /* Register platform stuff */
1224 result = platform_driver_register(&asuspf_driver);
1225 if (result)
1226 goto fail_platform_driver;
1227
1228 asuspf_device = platform_device_alloc(ASUS_HOTK_FILE, -1);
1229 if (!asuspf_device) {
1230 result = -ENOMEM;
1231 goto fail_platform_device1;
1232 }
1233
1234 result = platform_device_add(asuspf_device);
1235 if (result)
1236 goto fail_platform_device2;
1237
1238 result = sysfs_create_group(&asuspf_device->dev.kobj,
1239 &asuspf_attribute_group);
1240 if (result)
1241 goto fail_sysfs;
1242
1243 return 0;
1244
1245 fail_sysfs:
1246 platform_device_del(asuspf_device);
1247
1248 fail_platform_device2:
1249 platform_device_put(asuspf_device);
1250
1251 fail_platform_device1:
1252 platform_driver_unregister(&asuspf_driver);
1253
1254 fail_platform_driver:
1255 asus_led_exit();
1256
1257 fail_led:
1258 asus_backlight_exit();
1259
1260 fail_backlight:
1261
1262 return result;
1263}
1264
1265module_init(asus_laptop_init);
1266module_exit(asus_laptop_exit);
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c
new file mode 100644
index 000000000000..11003bba10d3
--- /dev/null
+++ b/drivers/platform/x86/compal-laptop.c
@@ -0,0 +1,406 @@
1/*-*-linux-c-*-*/
2
3/*
4 Copyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com>
5
6 based on MSI driver
7
8 Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 02110-1301, USA.
24 */
25
26/*
27 * comapl-laptop.c - Compal laptop support.
28 *
29 * This driver exports a few files in /sys/devices/platform/compal-laptop/:
30 *
31 * wlan - wlan subsystem state: contains 0 or 1 (rw)
32 *
33 * bluetooth - Bluetooth subsystem state: contains 0 or 1 (rw)
34 *
35 * raw - raw value taken from embedded controller register (ro)
36 *
37 * In addition to these platform device attributes the driver
38 * registers itself in the Linux backlight control subsystem and is
39 * available to userspace under /sys/class/backlight/compal-laptop/.
40 *
41 * This driver might work on other laptops produced by Compal. If you
42 * want to try it you can pass force=1 as argument to the module which
43 * will force it to load even when the DMI data doesn't identify the
44 * laptop as FL9x.
45 */
46
47#include <linux/module.h>
48#include <linux/kernel.h>
49#include <linux/init.h>
50#include <linux/acpi.h>
51#include <linux/dmi.h>
52#include <linux/backlight.h>
53#include <linux/platform_device.h>
54#include <linux/autoconf.h>
55
56#define COMPAL_DRIVER_VERSION "0.2.6"
57
58#define COMPAL_LCD_LEVEL_MAX 8
59
60#define COMPAL_EC_COMMAND_WIRELESS 0xBB
61#define COMPAL_EC_COMMAND_LCD_LEVEL 0xB9
62
63#define KILLSWITCH_MASK 0x10
64#define WLAN_MASK 0x01
65#define BT_MASK 0x02
66
67static int force;
68module_param(force, bool, 0);
69MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
70
71/* Hardware access */
72
73static int set_lcd_level(int level)
74{
75 if (level < 0 || level >= COMPAL_LCD_LEVEL_MAX)
76 return -EINVAL;
77
78 ec_write(COMPAL_EC_COMMAND_LCD_LEVEL, level);
79
80 return 0;
81}
82
83static int get_lcd_level(void)
84{
85 u8 result;
86
87 ec_read(COMPAL_EC_COMMAND_LCD_LEVEL, &result);
88
89 return (int) result;
90}
91
92static int set_wlan_state(int state)
93{
94 u8 result, value;
95
96 ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
97
98 if ((result & KILLSWITCH_MASK) == 0)
99 return -EINVAL;
100 else {
101 if (state)
102 value = (u8) (result | WLAN_MASK);
103 else
104 value = (u8) (result & ~WLAN_MASK);
105 ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
106 }
107
108 return 0;
109}
110
111static int set_bluetooth_state(int state)
112{
113 u8 result, value;
114
115 ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
116
117 if ((result & KILLSWITCH_MASK) == 0)
118 return -EINVAL;
119 else {
120 if (state)
121 value = (u8) (result | BT_MASK);
122 else
123 value = (u8) (result & ~BT_MASK);
124 ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
125 }
126
127 return 0;
128}
129
130static int get_wireless_state(int *wlan, int *bluetooth)
131{
132 u8 result;
133
134 ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
135
136 if (wlan) {
137 if ((result & KILLSWITCH_MASK) == 0)
138 *wlan = 0;
139 else
140 *wlan = result & WLAN_MASK;
141 }
142
143 if (bluetooth) {
144 if ((result & KILLSWITCH_MASK) == 0)
145 *bluetooth = 0;
146 else
147 *bluetooth = (result & BT_MASK) >> 1;
148 }
149
150 return 0;
151}
152
153/* Backlight device stuff */
154
155static int bl_get_brightness(struct backlight_device *b)
156{
157 return get_lcd_level();
158}
159
160
161static int bl_update_status(struct backlight_device *b)
162{
163 return set_lcd_level(b->props.brightness);
164}
165
166static struct backlight_ops compalbl_ops = {
167 .get_brightness = bl_get_brightness,
168 .update_status = bl_update_status,
169};
170
171static struct backlight_device *compalbl_device;
172
173/* Platform device */
174
175static ssize_t show_wlan(struct device *dev,
176 struct device_attribute *attr, char *buf)
177{
178 int ret, enabled;
179
180 ret = get_wireless_state(&enabled, NULL);
181 if (ret < 0)
182 return ret;
183
184 return sprintf(buf, "%i\n", enabled);
185}
186
187static ssize_t show_raw(struct device *dev,
188 struct device_attribute *attr, char *buf)
189{
190 u8 result;
191
192 ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
193
194 return sprintf(buf, "%i\n", result);
195}
196
197static ssize_t show_bluetooth(struct device *dev,
198 struct device_attribute *attr, char *buf)
199{
200 int ret, enabled;
201
202 ret = get_wireless_state(NULL, &enabled);
203 if (ret < 0)
204 return ret;
205
206 return sprintf(buf, "%i\n", enabled);
207}
208
209static ssize_t store_wlan_state(struct device *dev,
210 struct device_attribute *attr, const char *buf, size_t count)
211{
212 int state, ret;
213
214 if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
215 return -EINVAL;
216
217 ret = set_wlan_state(state);
218 if (ret < 0)
219 return ret;
220
221 return count;
222}
223
224static ssize_t store_bluetooth_state(struct device *dev,
225 struct device_attribute *attr, const char *buf, size_t count)
226{
227 int state, ret;
228
229 if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
230 return -EINVAL;
231
232 ret = set_bluetooth_state(state);
233 if (ret < 0)
234 return ret;
235
236 return count;
237}
238
239static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state);
240static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state);
241static DEVICE_ATTR(raw, 0444, show_raw, NULL);
242
243static struct attribute *compal_attributes[] = {
244 &dev_attr_bluetooth.attr,
245 &dev_attr_wlan.attr,
246 &dev_attr_raw.attr,
247 NULL
248};
249
250static struct attribute_group compal_attribute_group = {
251 .attrs = compal_attributes
252};
253
254static struct platform_driver compal_driver = {
255 .driver = {
256 .name = "compal-laptop",
257 .owner = THIS_MODULE,
258 }
259};
260
261static struct platform_device *compal_device;
262
263/* Initialization */
264
265static int dmi_check_cb(const struct dmi_system_id *id)
266{
267 printk(KERN_INFO "compal-laptop: Identified laptop model '%s'.\n",
268 id->ident);
269
270 return 0;
271}
272
273static struct dmi_system_id __initdata compal_dmi_table[] = {
274 {
275 .ident = "FL90/IFL90",
276 .matches = {
277 DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
278 DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
279 },
280 .callback = dmi_check_cb
281 },
282 {
283 .ident = "FL90/IFL90",
284 .matches = {
285 DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
286 DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
287 },
288 .callback = dmi_check_cb
289 },
290 {
291 .ident = "FL91/IFL91",
292 .matches = {
293 DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
294 DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
295 },
296 .callback = dmi_check_cb
297 },
298 {
299 .ident = "FL92/JFL92",
300 .matches = {
301 DMI_MATCH(DMI_BOARD_NAME, "JFL92"),
302 DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
303 },
304 .callback = dmi_check_cb
305 },
306 {
307 .ident = "FT00/IFT00",
308 .matches = {
309 DMI_MATCH(DMI_BOARD_NAME, "IFT00"),
310 DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
311 },
312 .callback = dmi_check_cb
313 },
314 { }
315};
316
317static int __init compal_init(void)
318{
319 int ret;
320
321 if (acpi_disabled)
322 return -ENODEV;
323
324 if (!force && !dmi_check_system(compal_dmi_table))
325 return -ENODEV;
326
327 /* Register backlight stuff */
328
329 if (!acpi_video_backlight_support()) {
330 compalbl_device = backlight_device_register("compal-laptop", NULL, NULL,
331 &compalbl_ops);
332 if (IS_ERR(compalbl_device))
333 return PTR_ERR(compalbl_device);
334
335 compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1;
336 }
337
338 ret = platform_driver_register(&compal_driver);
339 if (ret)
340 goto fail_backlight;
341
342 /* Register platform stuff */
343
344 compal_device = platform_device_alloc("compal-laptop", -1);
345 if (!compal_device) {
346 ret = -ENOMEM;
347 goto fail_platform_driver;
348 }
349
350 ret = platform_device_add(compal_device);
351 if (ret)
352 goto fail_platform_device1;
353
354 ret = sysfs_create_group(&compal_device->dev.kobj,
355 &compal_attribute_group);
356 if (ret)
357 goto fail_platform_device2;
358
359 printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION
360 " successfully loaded.\n");
361
362 return 0;
363
364fail_platform_device2:
365
366 platform_device_del(compal_device);
367
368fail_platform_device1:
369
370 platform_device_put(compal_device);
371
372fail_platform_driver:
373
374 platform_driver_unregister(&compal_driver);
375
376fail_backlight:
377
378 backlight_device_unregister(compalbl_device);
379
380 return ret;
381}
382
383static void __exit compal_cleanup(void)
384{
385
386 sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group);
387 platform_device_unregister(compal_device);
388 platform_driver_unregister(&compal_driver);
389 backlight_device_unregister(compalbl_device);
390
391 printk(KERN_INFO "compal-laptop: driver unloaded.\n");
392}
393
394module_init(compal_init);
395module_exit(compal_cleanup);
396
397MODULE_AUTHOR("Cezary Jackiewicz");
398MODULE_DESCRIPTION("Compal Laptop Support");
399MODULE_VERSION(COMPAL_DRIVER_VERSION);
400MODULE_LICENSE("GPL");
401
402MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*");
403MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*");
404MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*");
405MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*");
406MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*");
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
new file mode 100644
index 000000000000..02fe2b8b8939
--- /dev/null
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -0,0 +1,872 @@
1/*
2 * eepc-laptop.c - Asus Eee PC extras
3 *
4 * Based on asus_acpi.c as patched for the Eee PC by Asus:
5 * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
6 * Based on eee.c from eeepc-linux
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 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/types.h>
23#include <linux/platform_device.h>
24#include <linux/backlight.h>
25#include <linux/fb.h>
26#include <linux/hwmon.h>
27#include <linux/hwmon-sysfs.h>
28#include <acpi/acpi_drivers.h>
29#include <acpi/acpi_bus.h>
30#include <linux/uaccess.h>
31#include <linux/input.h>
32#include <linux/rfkill.h>
33
34#define EEEPC_LAPTOP_VERSION "0.1"
35
36#define EEEPC_HOTK_NAME "Eee PC Hotkey Driver"
37#define EEEPC_HOTK_FILE "eeepc"
38#define EEEPC_HOTK_CLASS "hotkey"
39#define EEEPC_HOTK_DEVICE_NAME "Hotkey"
40#define EEEPC_HOTK_HID "ASUS010"
41
42#define EEEPC_LOG EEEPC_HOTK_FILE ": "
43#define EEEPC_ERR KERN_ERR EEEPC_LOG
44#define EEEPC_WARNING KERN_WARNING EEEPC_LOG
45#define EEEPC_NOTICE KERN_NOTICE EEEPC_LOG
46#define EEEPC_INFO KERN_INFO EEEPC_LOG
47
48/*
49 * Definitions for Asus EeePC
50 */
51#define NOTIFY_WLAN_ON 0x10
52#define NOTIFY_BRN_MIN 0x20
53#define NOTIFY_BRN_MAX 0x2f
54
55enum {
56 DISABLE_ASL_WLAN = 0x0001,
57 DISABLE_ASL_BLUETOOTH = 0x0002,
58 DISABLE_ASL_IRDA = 0x0004,
59 DISABLE_ASL_CAMERA = 0x0008,
60 DISABLE_ASL_TV = 0x0010,
61 DISABLE_ASL_GPS = 0x0020,
62 DISABLE_ASL_DISPLAYSWITCH = 0x0040,
63 DISABLE_ASL_MODEM = 0x0080,
64 DISABLE_ASL_CARDREADER = 0x0100
65};
66
67enum {
68 CM_ASL_WLAN = 0,
69 CM_ASL_BLUETOOTH,
70 CM_ASL_IRDA,
71 CM_ASL_1394,
72 CM_ASL_CAMERA,
73 CM_ASL_TV,
74 CM_ASL_GPS,
75 CM_ASL_DVDROM,
76 CM_ASL_DISPLAYSWITCH,
77 CM_ASL_PANELBRIGHT,
78 CM_ASL_BIOSFLASH,
79 CM_ASL_ACPIFLASH,
80 CM_ASL_CPUFV,
81 CM_ASL_CPUTEMPERATURE,
82 CM_ASL_FANCPU,
83 CM_ASL_FANCHASSIS,
84 CM_ASL_USBPORT1,
85 CM_ASL_USBPORT2,
86 CM_ASL_USBPORT3,
87 CM_ASL_MODEM,
88 CM_ASL_CARDREADER,
89 CM_ASL_LID
90};
91
92static const char *cm_getv[] = {
93 "WLDG", NULL, NULL, NULL,
94 "CAMG", NULL, NULL, NULL,
95 NULL, "PBLG", NULL, NULL,
96 "CFVG", NULL, NULL, NULL,
97 "USBG", NULL, NULL, "MODG",
98 "CRDG", "LIDG"
99};
100
101static const char *cm_setv[] = {
102 "WLDS", NULL, NULL, NULL,
103 "CAMS", NULL, NULL, NULL,
104 "SDSP", "PBLS", "HDPS", NULL,
105 "CFVS", NULL, NULL, NULL,
106 "USBG", NULL, NULL, "MODS",
107 "CRDS", NULL
108};
109
110#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0."
111
112#define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */
113#define EEEPC_EC_SC02 0x63
114#define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */
115#define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */
116#define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */
117#define EEEPC_EC_SFB3 0xD3
118
119/*
120 * This is the main structure, we can use it to store useful information
121 * about the hotk device
122 */
123struct eeepc_hotk {
124 struct acpi_device *device; /* the device we are in */
125 acpi_handle handle; /* the handle of the hotk device */
126 u32 cm_supported; /* the control methods supported
127 by this BIOS */
128 uint init_flag; /* Init flags */
129 u16 event_count[128]; /* count for each event */
130 struct input_dev *inputdev;
131 u16 *keycode_map;
132 struct rfkill *eeepc_wlan_rfkill;
133 struct rfkill *eeepc_bluetooth_rfkill;
134};
135
136/* The actual device the driver binds to */
137static struct eeepc_hotk *ehotk;
138
139/* Platform device/driver */
140static struct platform_driver platform_driver = {
141 .driver = {
142 .name = EEEPC_HOTK_FILE,
143 .owner = THIS_MODULE,
144 }
145};
146
147static struct platform_device *platform_device;
148
149struct key_entry {
150 char type;
151 u8 code;
152 u16 keycode;
153};
154
155enum { KE_KEY, KE_END };
156
157static struct key_entry eeepc_keymap[] = {
158 /* Sleep already handled via generic ACPI code */
159 {KE_KEY, 0x10, KEY_WLAN },
160 {KE_KEY, 0x12, KEY_PROG1 },
161 {KE_KEY, 0x13, KEY_MUTE },
162 {KE_KEY, 0x14, KEY_VOLUMEDOWN },
163 {KE_KEY, 0x15, KEY_VOLUMEUP },
164 {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
165 {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
166 {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
167 {KE_END, 0},
168};
169
170/*
171 * The hotkey driver declaration
172 */
173static int eeepc_hotk_add(struct acpi_device *device);
174static int eeepc_hotk_remove(struct acpi_device *device, int type);
175
176static const struct acpi_device_id eeepc_device_ids[] = {
177 {EEEPC_HOTK_HID, 0},
178 {"", 0},
179};
180MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
181
182static struct acpi_driver eeepc_hotk_driver = {
183 .name = EEEPC_HOTK_NAME,
184 .class = EEEPC_HOTK_CLASS,
185 .ids = eeepc_device_ids,
186 .ops = {
187 .add = eeepc_hotk_add,
188 .remove = eeepc_hotk_remove,
189 },
190};
191
192/* The backlight device /sys/class/backlight */
193static struct backlight_device *eeepc_backlight_device;
194
195/* The hwmon device */
196static struct device *eeepc_hwmon_device;
197
198/*
199 * The backlight class declaration
200 */
201static int read_brightness(struct backlight_device *bd);
202static int update_bl_status(struct backlight_device *bd);
203static struct backlight_ops eeepcbl_ops = {
204 .get_brightness = read_brightness,
205 .update_status = update_bl_status,
206};
207
208MODULE_AUTHOR("Corentin Chary, Eric Cooper");
209MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
210MODULE_LICENSE("GPL");
211
212/*
213 * ACPI Helpers
214 */
215static int write_acpi_int(acpi_handle handle, const char *method, int val,
216 struct acpi_buffer *output)
217{
218 struct acpi_object_list params;
219 union acpi_object in_obj;
220 acpi_status status;
221
222 params.count = 1;
223 params.pointer = &in_obj;
224 in_obj.type = ACPI_TYPE_INTEGER;
225 in_obj.integer.value = val;
226
227 status = acpi_evaluate_object(handle, (char *)method, &params, output);
228 return (status == AE_OK ? 0 : -1);
229}
230
231static int read_acpi_int(acpi_handle handle, const char *method, int *val)
232{
233 acpi_status status;
234 unsigned long long result;
235
236 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
237 if (ACPI_FAILURE(status)) {
238 *val = -1;
239 return -1;
240 } else {
241 *val = result;
242 return 0;
243 }
244}
245
246static int set_acpi(int cm, int value)
247{
248 if (ehotk->cm_supported & (0x1 << cm)) {
249 const char *method = cm_setv[cm];
250 if (method == NULL)
251 return -ENODEV;
252 if (write_acpi_int(ehotk->handle, method, value, NULL))
253 printk(EEEPC_WARNING "Error writing %s\n", method);
254 }
255 return 0;
256}
257
258static int get_acpi(int cm)
259{
260 int value = -1;
261 if ((ehotk->cm_supported & (0x1 << cm))) {
262 const char *method = cm_getv[cm];
263 if (method == NULL)
264 return -ENODEV;
265 if (read_acpi_int(ehotk->handle, method, &value))
266 printk(EEEPC_WARNING "Error reading %s\n", method);
267 }
268 return value;
269}
270
271/*
272 * Backlight
273 */
274static int read_brightness(struct backlight_device *bd)
275{
276 return get_acpi(CM_ASL_PANELBRIGHT);
277}
278
279static int set_brightness(struct backlight_device *bd, int value)
280{
281 value = max(0, min(15, value));
282 return set_acpi(CM_ASL_PANELBRIGHT, value);
283}
284
285static int update_bl_status(struct backlight_device *bd)
286{
287 return set_brightness(bd, bd->props.brightness);
288}
289
290/*
291 * Rfkill helpers
292 */
293
294static int eeepc_wlan_rfkill_set(void *data, enum rfkill_state state)
295{
296 if (state == RFKILL_STATE_SOFT_BLOCKED)
297 return set_acpi(CM_ASL_WLAN, 0);
298 else
299 return set_acpi(CM_ASL_WLAN, 1);
300}
301
302static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
303{
304 if (get_acpi(CM_ASL_WLAN) == 1)
305 *state = RFKILL_STATE_UNBLOCKED;
306 else
307 *state = RFKILL_STATE_SOFT_BLOCKED;
308 return 0;
309}
310
311static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state)
312{
313 if (state == RFKILL_STATE_SOFT_BLOCKED)
314 return set_acpi(CM_ASL_BLUETOOTH, 0);
315 else
316 return set_acpi(CM_ASL_BLUETOOTH, 1);
317}
318
319static int eeepc_bluetooth_rfkill_state(void *data, enum rfkill_state *state)
320{
321 if (get_acpi(CM_ASL_BLUETOOTH) == 1)
322 *state = RFKILL_STATE_UNBLOCKED;
323 else
324 *state = RFKILL_STATE_SOFT_BLOCKED;
325 return 0;
326}
327
328/*
329 * Sys helpers
330 */
331static int parse_arg(const char *buf, unsigned long count, int *val)
332{
333 if (!count)
334 return 0;
335 if (sscanf(buf, "%i", val) != 1)
336 return -EINVAL;
337 return count;
338}
339
340static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
341{
342 int rv, value;
343
344 rv = parse_arg(buf, count, &value);
345 if (rv > 0)
346 set_acpi(cm, value);
347 return rv;
348}
349
350static ssize_t show_sys_acpi(int cm, char *buf)
351{
352 return sprintf(buf, "%d\n", get_acpi(cm));
353}
354
355#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \
356 static ssize_t show_##_name(struct device *dev, \
357 struct device_attribute *attr, \
358 char *buf) \
359 { \
360 return show_sys_acpi(_cm, buf); \
361 } \
362 static ssize_t store_##_name(struct device *dev, \
363 struct device_attribute *attr, \
364 const char *buf, size_t count) \
365 { \
366 return store_sys_acpi(_cm, buf, count); \
367 } \
368 static struct device_attribute dev_attr_##_name = { \
369 .attr = { \
370 .name = __stringify(_name), \
371 .mode = 0644 }, \
372 .show = show_##_name, \
373 .store = store_##_name, \
374 }
375
376EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
377EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
378EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
379
380static struct attribute *platform_attributes[] = {
381 &dev_attr_camera.attr,
382 &dev_attr_cardr.attr,
383 &dev_attr_disp.attr,
384 NULL
385};
386
387static struct attribute_group platform_attribute_group = {
388 .attrs = platform_attributes
389};
390
391/*
392 * Hotkey functions
393 */
394static struct key_entry *eepc_get_entry_by_scancode(int code)
395{
396 struct key_entry *key;
397
398 for (key = eeepc_keymap; key->type != KE_END; key++)
399 if (code == key->code)
400 return key;
401
402 return NULL;
403}
404
405static struct key_entry *eepc_get_entry_by_keycode(int code)
406{
407 struct key_entry *key;
408
409 for (key = eeepc_keymap; key->type != KE_END; key++)
410 if (code == key->keycode && key->type == KE_KEY)
411 return key;
412
413 return NULL;
414}
415
416static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
417{
418 struct key_entry *key = eepc_get_entry_by_scancode(scancode);
419
420 if (key && key->type == KE_KEY) {
421 *keycode = key->keycode;
422 return 0;
423 }
424
425 return -EINVAL;
426}
427
428static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
429{
430 struct key_entry *key;
431 int old_keycode;
432
433 if (keycode < 0 || keycode > KEY_MAX)
434 return -EINVAL;
435
436 key = eepc_get_entry_by_scancode(scancode);
437 if (key && key->type == KE_KEY) {
438 old_keycode = key->keycode;
439 key->keycode = keycode;
440 set_bit(keycode, dev->keybit);
441 if (!eepc_get_entry_by_keycode(old_keycode))
442 clear_bit(old_keycode, dev->keybit);
443 return 0;
444 }
445
446 return -EINVAL;
447}
448
449static int eeepc_hotk_check(void)
450{
451 const struct key_entry *key;
452 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
453 int result;
454
455 result = acpi_bus_get_status(ehotk->device);
456 if (result)
457 return result;
458 if (ehotk->device->status.present) {
459 if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
460 &buffer)) {
461 printk(EEEPC_ERR "Hotkey initialization failed\n");
462 return -ENODEV;
463 } else {
464 printk(EEEPC_NOTICE "Hotkey init flags 0x%x\n",
465 ehotk->init_flag);
466 }
467 /* get control methods supported */
468 if (read_acpi_int(ehotk->handle, "CMSG"
469 , &ehotk->cm_supported)) {
470 printk(EEEPC_ERR
471 "Get control methods supported failed\n");
472 return -ENODEV;
473 } else {
474 printk(EEEPC_INFO
475 "Get control methods supported: 0x%x\n",
476 ehotk->cm_supported);
477 }
478 ehotk->inputdev = input_allocate_device();
479 if (!ehotk->inputdev) {
480 printk(EEEPC_INFO "Unable to allocate input device\n");
481 return 0;
482 }
483 ehotk->inputdev->name = "Asus EeePC extra buttons";
484 ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
485 ehotk->inputdev->id.bustype = BUS_HOST;
486 ehotk->inputdev->getkeycode = eeepc_getkeycode;
487 ehotk->inputdev->setkeycode = eeepc_setkeycode;
488
489 for (key = eeepc_keymap; key->type != KE_END; key++) {
490 switch (key->type) {
491 case KE_KEY:
492 set_bit(EV_KEY, ehotk->inputdev->evbit);
493 set_bit(key->keycode, ehotk->inputdev->keybit);
494 break;
495 }
496 }
497 result = input_register_device(ehotk->inputdev);
498 if (result) {
499 printk(EEEPC_INFO "Unable to register input device\n");
500 input_free_device(ehotk->inputdev);
501 return 0;
502 }
503 } else {
504 printk(EEEPC_ERR "Hotkey device not present, aborting\n");
505 return -EINVAL;
506 }
507 return 0;
508}
509
510static void notify_brn(void)
511{
512 struct backlight_device *bd = eeepc_backlight_device;
513 bd->props.brightness = read_brightness(bd);
514}
515
516static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
517{
518 static struct key_entry *key;
519 if (!ehotk)
520 return;
521 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
522 notify_brn();
523 acpi_bus_generate_proc_event(ehotk->device, event,
524 ehotk->event_count[event % 128]++);
525 if (ehotk->inputdev) {
526 key = eepc_get_entry_by_scancode(event);
527 if (key) {
528 switch (key->type) {
529 case KE_KEY:
530 input_report_key(ehotk->inputdev, key->keycode,
531 1);
532 input_sync(ehotk->inputdev);
533 input_report_key(ehotk->inputdev, key->keycode,
534 0);
535 input_sync(ehotk->inputdev);
536 break;
537 }
538 }
539 }
540}
541
542static int eeepc_hotk_add(struct acpi_device *device)
543{
544 acpi_status status = AE_OK;
545 int result;
546
547 if (!device)
548 return -EINVAL;
549 printk(EEEPC_NOTICE EEEPC_HOTK_NAME "\n");
550 ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
551 if (!ehotk)
552 return -ENOMEM;
553 ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
554 ehotk->handle = device->handle;
555 strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
556 strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
557 device->driver_data = ehotk;
558 ehotk->device = device;
559 result = eeepc_hotk_check();
560 if (result)
561 goto end;
562 status = acpi_install_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
563 eeepc_hotk_notify, ehotk);
564 if (ACPI_FAILURE(status))
565 printk(EEEPC_ERR "Error installing notify handler\n");
566
567 if (get_acpi(CM_ASL_WLAN) != -1) {
568 ehotk->eeepc_wlan_rfkill = rfkill_allocate(&device->dev,
569 RFKILL_TYPE_WLAN);
570
571 if (!ehotk->eeepc_wlan_rfkill)
572 goto end;
573
574 ehotk->eeepc_wlan_rfkill->name = "eeepc-wlan";
575 ehotk->eeepc_wlan_rfkill->toggle_radio = eeepc_wlan_rfkill_set;
576 ehotk->eeepc_wlan_rfkill->get_state = eeepc_wlan_rfkill_state;
577 if (get_acpi(CM_ASL_WLAN) == 1)
578 ehotk->eeepc_wlan_rfkill->state =
579 RFKILL_STATE_UNBLOCKED;
580 else
581 ehotk->eeepc_wlan_rfkill->state =
582 RFKILL_STATE_SOFT_BLOCKED;
583 rfkill_register(ehotk->eeepc_wlan_rfkill);
584 }
585
586 if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
587 ehotk->eeepc_bluetooth_rfkill =
588 rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH);
589
590 if (!ehotk->eeepc_bluetooth_rfkill)
591 goto end;
592
593 ehotk->eeepc_bluetooth_rfkill->name = "eeepc-bluetooth";
594 ehotk->eeepc_bluetooth_rfkill->toggle_radio =
595 eeepc_bluetooth_rfkill_set;
596 ehotk->eeepc_bluetooth_rfkill->get_state =
597 eeepc_bluetooth_rfkill_state;
598 if (get_acpi(CM_ASL_BLUETOOTH) == 1)
599 ehotk->eeepc_bluetooth_rfkill->state =
600 RFKILL_STATE_UNBLOCKED;
601 else
602 ehotk->eeepc_bluetooth_rfkill->state =
603 RFKILL_STATE_SOFT_BLOCKED;
604 rfkill_register(ehotk->eeepc_bluetooth_rfkill);
605 }
606
607 end:
608 if (result) {
609 kfree(ehotk);
610 ehotk = NULL;
611 }
612 return result;
613}
614
615static int eeepc_hotk_remove(struct acpi_device *device, int type)
616{
617 acpi_status status = 0;
618
619 if (!device || !acpi_driver_data(device))
620 return -EINVAL;
621 status = acpi_remove_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
622 eeepc_hotk_notify);
623 if (ACPI_FAILURE(status))
624 printk(EEEPC_ERR "Error removing notify handler\n");
625 kfree(ehotk);
626 return 0;
627}
628
629/*
630 * Hwmon
631 */
632static int eeepc_get_fan_pwm(void)
633{
634 int value = 0;
635
636 read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
637 value = value * 255 / 100;
638 return (value);
639}
640
641static void eeepc_set_fan_pwm(int value)
642{
643 value = SENSORS_LIMIT(value, 0, 255);
644 value = value * 100 / 255;
645 ec_write(EEEPC_EC_SC02, value);
646}
647
648static int eeepc_get_fan_rpm(void)
649{
650 int high = 0;
651 int low = 0;
652
653 read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
654 read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
655 return (high << 8 | low);
656}
657
658static int eeepc_get_fan_ctrl(void)
659{
660 int value = 0;
661
662 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
663 return ((value & 0x02 ? 1 : 0));
664}
665
666static void eeepc_set_fan_ctrl(int manual)
667{
668 int value = 0;
669
670 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
671 if (manual)
672 value |= 0x02;
673 else
674 value &= ~0x02;
675 ec_write(EEEPC_EC_SFB3, value);
676}
677
678static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
679{
680 int rv, value;
681
682 rv = parse_arg(buf, count, &value);
683 if (rv > 0)
684 set(value);
685 return rv;
686}
687
688static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
689{
690 return sprintf(buf, "%d\n", get());
691}
692
693#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
694 static ssize_t show_##_name(struct device *dev, \
695 struct device_attribute *attr, \
696 char *buf) \
697 { \
698 return show_sys_hwmon(_set, buf); \
699 } \
700 static ssize_t store_##_name(struct device *dev, \
701 struct device_attribute *attr, \
702 const char *buf, size_t count) \
703 { \
704 return store_sys_hwmon(_get, buf, count); \
705 } \
706 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
707
708EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
709EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
710 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
711EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
712 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
713
714static ssize_t
715show_name(struct device *dev, struct device_attribute *attr, char *buf)
716{
717 return sprintf(buf, "eeepc\n");
718}
719static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
720
721static struct attribute *hwmon_attributes[] = {
722 &sensor_dev_attr_pwm1.dev_attr.attr,
723 &sensor_dev_attr_fan1_input.dev_attr.attr,
724 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
725 &sensor_dev_attr_name.dev_attr.attr,
726 NULL
727};
728
729static struct attribute_group hwmon_attribute_group = {
730 .attrs = hwmon_attributes
731};
732
733/*
734 * exit/init
735 */
736static void eeepc_backlight_exit(void)
737{
738 if (eeepc_backlight_device)
739 backlight_device_unregister(eeepc_backlight_device);
740 if (ehotk->inputdev)
741 input_unregister_device(ehotk->inputdev);
742 if (ehotk->eeepc_wlan_rfkill)
743 rfkill_unregister(ehotk->eeepc_wlan_rfkill);
744 if (ehotk->eeepc_bluetooth_rfkill)
745 rfkill_unregister(ehotk->eeepc_bluetooth_rfkill);
746 eeepc_backlight_device = NULL;
747}
748
749static void eeepc_hwmon_exit(void)
750{
751 struct device *hwmon;
752
753 hwmon = eeepc_hwmon_device;
754 if (!hwmon)
755 return ;
756 sysfs_remove_group(&hwmon->kobj,
757 &hwmon_attribute_group);
758 hwmon_device_unregister(hwmon);
759 eeepc_hwmon_device = NULL;
760}
761
762static void __exit eeepc_laptop_exit(void)
763{
764 eeepc_backlight_exit();
765 eeepc_hwmon_exit();
766 acpi_bus_unregister_driver(&eeepc_hotk_driver);
767 sysfs_remove_group(&platform_device->dev.kobj,
768 &platform_attribute_group);
769 platform_device_unregister(platform_device);
770 platform_driver_unregister(&platform_driver);
771}
772
773static int eeepc_backlight_init(struct device *dev)
774{
775 struct backlight_device *bd;
776
777 bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
778 NULL, &eeepcbl_ops);
779 if (IS_ERR(bd)) {
780 printk(EEEPC_ERR
781 "Could not register eeepc backlight device\n");
782 eeepc_backlight_device = NULL;
783 return PTR_ERR(bd);
784 }
785 eeepc_backlight_device = bd;
786 bd->props.max_brightness = 15;
787 bd->props.brightness = read_brightness(NULL);
788 bd->props.power = FB_BLANK_UNBLANK;
789 backlight_update_status(bd);
790 return 0;
791}
792
793static int eeepc_hwmon_init(struct device *dev)
794{
795 struct device *hwmon;
796 int result;
797
798 hwmon = hwmon_device_register(dev);
799 if (IS_ERR(hwmon)) {
800 printk(EEEPC_ERR
801 "Could not register eeepc hwmon device\n");
802 eeepc_hwmon_device = NULL;
803 return PTR_ERR(hwmon);
804 }
805 eeepc_hwmon_device = hwmon;
806 result = sysfs_create_group(&hwmon->kobj,
807 &hwmon_attribute_group);
808 if (result)
809 eeepc_hwmon_exit();
810 return result;
811}
812
813static int __init eeepc_laptop_init(void)
814{
815 struct device *dev;
816 int result;
817
818 if (acpi_disabled)
819 return -ENODEV;
820 result = acpi_bus_register_driver(&eeepc_hotk_driver);
821 if (result < 0)
822 return result;
823 if (!ehotk) {
824 acpi_bus_unregister_driver(&eeepc_hotk_driver);
825 return -ENODEV;
826 }
827 dev = acpi_get_physical_device(ehotk->device->handle);
828
829 if (!acpi_video_backlight_support()) {
830 result = eeepc_backlight_init(dev);
831 if (result)
832 goto fail_backlight;
833 } else
834 printk(EEEPC_INFO "Backlight controlled by ACPI video "
835 "driver\n");
836
837 result = eeepc_hwmon_init(dev);
838 if (result)
839 goto fail_hwmon;
840 /* Register platform stuff */
841 result = platform_driver_register(&platform_driver);
842 if (result)
843 goto fail_platform_driver;
844 platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
845 if (!platform_device) {
846 result = -ENOMEM;
847 goto fail_platform_device1;
848 }
849 result = platform_device_add(platform_device);
850 if (result)
851 goto fail_platform_device2;
852 result = sysfs_create_group(&platform_device->dev.kobj,
853 &platform_attribute_group);
854 if (result)
855 goto fail_sysfs;
856 return 0;
857fail_sysfs:
858 platform_device_del(platform_device);
859fail_platform_device2:
860 platform_device_put(platform_device);
861fail_platform_device1:
862 platform_driver_unregister(&platform_driver);
863fail_platform_driver:
864 eeepc_hwmon_exit();
865fail_hwmon:
866 eeepc_backlight_exit();
867fail_backlight:
868 return result;
869}
870
871module_init(eeepc_laptop_init);
872module_exit(eeepc_laptop_exit);
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
new file mode 100644
index 000000000000..a7dd3e9fb79d
--- /dev/null
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -0,0 +1,1126 @@
1/*-*-linux-c-*-*/
2
3/*
4 Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
5 Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
6 Based on earlier work:
7 Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
8 Adrian Yee <brewt-fujitsu@brewt.org>
9
10 Templated from msi-laptop.c and thinkpad_acpi.c which is copyright
11 by its respective authors.
12
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 02110-1301, USA.
27 */
28
29/*
30 * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional
31 * features made available on a range of Fujitsu laptops including the
32 * P2xxx/P5xxx/S6xxx/S7xxx series.
33 *
34 * This driver exports a few files in /sys/devices/platform/fujitsu-laptop/;
35 * others may be added at a later date.
36 *
37 * lcd_level - Screen brightness: contains a single integer in the
38 * range 0..7. (rw)
39 *
40 * In addition to these platform device attributes the driver
41 * registers itself in the Linux backlight control subsystem and is
42 * available to userspace under /sys/class/backlight/fujitsu-laptop/.
43 *
44 * Hotkeys present on certain Fujitsu laptops (eg: the S6xxx series) are
45 * also supported by this driver.
46 *
47 * This driver has been tested on a Fujitsu Lifebook S6410, S7020 and
48 * P8010. It should work on most P-series and S-series Lifebooks, but
49 * YMMV.
50 *
51 * The module parameter use_alt_lcd_levels switches between different ACPI
52 * brightness controls which are used by different Fujitsu laptops. In most
53 * cases the correct method is automatically detected. "use_alt_lcd_levels=1"
54 * is applicable for a Fujitsu Lifebook S6410 if autodetection fails.
55 *
56 */
57
58#include <linux/module.h>
59#include <linux/kernel.h>
60#include <linux/init.h>
61#include <linux/acpi.h>
62#include <linux/dmi.h>
63#include <linux/backlight.h>
64#include <linux/input.h>
65#include <linux/kfifo.h>
66#include <linux/video_output.h>
67#include <linux/platform_device.h>
68
69#define FUJITSU_DRIVER_VERSION "0.4.3"
70
71#define FUJITSU_LCD_N_LEVELS 8
72
73#define ACPI_FUJITSU_CLASS "fujitsu"
74#define ACPI_FUJITSU_HID "FUJ02B1"
75#define ACPI_FUJITSU_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI brightness driver"
76#define ACPI_FUJITSU_DEVICE_NAME "Fujitsu FUJ02B1"
77#define ACPI_FUJITSU_HOTKEY_HID "FUJ02E3"
78#define ACPI_FUJITSU_HOTKEY_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
79#define ACPI_FUJITSU_HOTKEY_DEVICE_NAME "Fujitsu FUJ02E3"
80
81#define ACPI_FUJITSU_NOTIFY_CODE1 0x80
82
83#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86
84#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87
85
86/* Hotkey details */
87#define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */
88#define KEY2_CODE 0x411
89#define KEY3_CODE 0x412
90#define KEY4_CODE 0x413
91
92#define MAX_HOTKEY_RINGBUFFER_SIZE 100
93#define RINGBUFFERSIZE 40
94
95/* Debugging */
96#define FUJLAPTOP_LOG ACPI_FUJITSU_HID ": "
97#define FUJLAPTOP_ERR KERN_ERR FUJLAPTOP_LOG
98#define FUJLAPTOP_NOTICE KERN_NOTICE FUJLAPTOP_LOG
99#define FUJLAPTOP_INFO KERN_INFO FUJLAPTOP_LOG
100#define FUJLAPTOP_DEBUG KERN_DEBUG FUJLAPTOP_LOG
101
102#define FUJLAPTOP_DBG_ALL 0xffff
103#define FUJLAPTOP_DBG_ERROR 0x0001
104#define FUJLAPTOP_DBG_WARN 0x0002
105#define FUJLAPTOP_DBG_INFO 0x0004
106#define FUJLAPTOP_DBG_TRACE 0x0008
107
108#define dbg_printk(a_dbg_level, format, arg...) \
109 do { if (dbg_level & a_dbg_level) \
110 printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \
111 } while (0)
112#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
113#define vdbg_printk(a_dbg_level, format, arg...) \
114 dbg_printk(a_dbg_level, format, ## arg)
115#else
116#define vdbg_printk(a_dbg_level, format, arg...)
117#endif
118
119/* Device controlling the backlight and associated keys */
120struct fujitsu_t {
121 acpi_handle acpi_handle;
122 struct acpi_device *dev;
123 struct input_dev *input;
124 char phys[32];
125 struct backlight_device *bl_device;
126 struct platform_device *pf_device;
127 int keycode1, keycode2, keycode3, keycode4;
128
129 unsigned int max_brightness;
130 unsigned int brightness_changed;
131 unsigned int brightness_level;
132};
133
134static struct fujitsu_t *fujitsu;
135static int use_alt_lcd_levels = -1;
136static int disable_brightness_keys = -1;
137static int disable_brightness_adjust = -1;
138
139/* Device used to access other hotkeys on the laptop */
140struct fujitsu_hotkey_t {
141 acpi_handle acpi_handle;
142 struct acpi_device *dev;
143 struct input_dev *input;
144 char phys[32];
145 struct platform_device *pf_device;
146 struct kfifo *fifo;
147 spinlock_t fifo_lock;
148
149 unsigned int irb; /* info about the pressed buttons */
150};
151
152static struct fujitsu_hotkey_t *fujitsu_hotkey;
153
154static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
155 void *data);
156
157#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
158static u32 dbg_level = 0x03;
159#endif
160
161static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data);
162
163/* Hardware access for LCD brightness control */
164
165static int set_lcd_level(int level)
166{
167 acpi_status status = AE_OK;
168 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
169 struct acpi_object_list arg_list = { 1, &arg0 };
170 acpi_handle handle = NULL;
171
172 vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n",
173 level);
174
175 if (level < 0 || level >= fujitsu->max_brightness)
176 return -EINVAL;
177
178 if (!fujitsu)
179 return -EINVAL;
180
181 status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle);
182 if (ACPI_FAILURE(status)) {
183 vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n");
184 return -ENODEV;
185 }
186
187 arg0.integer.value = level;
188
189 status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
190 if (ACPI_FAILURE(status))
191 return -ENODEV;
192
193 return 0;
194}
195
196static int set_lcd_level_alt(int level)
197{
198 acpi_status status = AE_OK;
199 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
200 struct acpi_object_list arg_list = { 1, &arg0 };
201 acpi_handle handle = NULL;
202
203 vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n",
204 level);
205
206 if (level < 0 || level >= fujitsu->max_brightness)
207 return -EINVAL;
208
209 if (!fujitsu)
210 return -EINVAL;
211
212 status = acpi_get_handle(fujitsu->acpi_handle, "SBL2", &handle);
213 if (ACPI_FAILURE(status)) {
214 vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n");
215 return -ENODEV;
216 }
217
218 arg0.integer.value = level;
219
220 status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
221 if (ACPI_FAILURE(status))
222 return -ENODEV;
223
224 return 0;
225}
226
227static int get_lcd_level(void)
228{
229 unsigned long long state = 0;
230 acpi_status status = AE_OK;
231
232 vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n");
233
234 status =
235 acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state);
236 if (status < 0)
237 return status;
238
239 fujitsu->brightness_level = state & 0x0fffffff;
240
241 if (state & 0x80000000)
242 fujitsu->brightness_changed = 1;
243 else
244 fujitsu->brightness_changed = 0;
245
246 return fujitsu->brightness_level;
247}
248
249static int get_max_brightness(void)
250{
251 unsigned long long state = 0;
252 acpi_status status = AE_OK;
253
254 vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n");
255
256 status =
257 acpi_evaluate_integer(fujitsu->acpi_handle, "RBLL", NULL, &state);
258 if (status < 0)
259 return status;
260
261 fujitsu->max_brightness = state;
262
263 return fujitsu->max_brightness;
264}
265
266static int get_lcd_level_alt(void)
267{
268 unsigned long long state = 0;
269 acpi_status status = AE_OK;
270
271 vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLS\n");
272
273 status =
274 acpi_evaluate_integer(fujitsu->acpi_handle, "GBLS", NULL, &state);
275 if (status < 0)
276 return status;
277
278 fujitsu->brightness_level = state & 0x0fffffff;
279
280 if (state & 0x80000000)
281 fujitsu->brightness_changed = 1;
282 else
283 fujitsu->brightness_changed = 0;
284
285 return fujitsu->brightness_level;
286}
287
288/* Backlight device stuff */
289
290static int bl_get_brightness(struct backlight_device *b)
291{
292 if (use_alt_lcd_levels)
293 return get_lcd_level_alt();
294 else
295 return get_lcd_level();
296}
297
298static int bl_update_status(struct backlight_device *b)
299{
300 if (use_alt_lcd_levels)
301 return set_lcd_level_alt(b->props.brightness);
302 else
303 return set_lcd_level(b->props.brightness);
304}
305
306static struct backlight_ops fujitsubl_ops = {
307 .get_brightness = bl_get_brightness,
308 .update_status = bl_update_status,
309};
310
311/* Platform LCD brightness device */
312
313static ssize_t
314show_max_brightness(struct device *dev,
315 struct device_attribute *attr, char *buf)
316{
317
318 int ret;
319
320 ret = get_max_brightness();
321 if (ret < 0)
322 return ret;
323
324 return sprintf(buf, "%i\n", ret);
325}
326
327static ssize_t
328show_brightness_changed(struct device *dev,
329 struct device_attribute *attr, char *buf)
330{
331
332 int ret;
333
334 ret = fujitsu->brightness_changed;
335 if (ret < 0)
336 return ret;
337
338 return sprintf(buf, "%i\n", ret);
339}
340
341static ssize_t show_lcd_level(struct device *dev,
342 struct device_attribute *attr, char *buf)
343{
344
345 int ret;
346
347 if (use_alt_lcd_levels)
348 ret = get_lcd_level_alt();
349 else
350 ret = get_lcd_level();
351 if (ret < 0)
352 return ret;
353
354 return sprintf(buf, "%i\n", ret);
355}
356
357static ssize_t store_lcd_level(struct device *dev,
358 struct device_attribute *attr, const char *buf,
359 size_t count)
360{
361
362 int level, ret;
363
364 if (sscanf(buf, "%i", &level) != 1
365 || (level < 0 || level >= fujitsu->max_brightness))
366 return -EINVAL;
367
368 if (use_alt_lcd_levels)
369 ret = set_lcd_level_alt(level);
370 else
371 ret = set_lcd_level(level);
372 if (ret < 0)
373 return ret;
374
375 if (use_alt_lcd_levels)
376 ret = get_lcd_level_alt();
377 else
378 ret = get_lcd_level();
379 if (ret < 0)
380 return ret;
381
382 return count;
383}
384
385/* Hardware access for hotkey device */
386
387static int get_irb(void)
388{
389 unsigned long long state = 0;
390 acpi_status status = AE_OK;
391
392 vdbg_printk(FUJLAPTOP_DBG_TRACE, "Get irb\n");
393
394 status =
395 acpi_evaluate_integer(fujitsu_hotkey->acpi_handle, "GIRB", NULL,
396 &state);
397 if (status < 0)
398 return status;
399
400 fujitsu_hotkey->irb = state;
401
402 return fujitsu_hotkey->irb;
403}
404
405static ssize_t
406ignore_store(struct device *dev,
407 struct device_attribute *attr, const char *buf, size_t count)
408{
409 return count;
410}
411
412static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store);
413static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed,
414 ignore_store);
415static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
416
417static struct attribute *fujitsupf_attributes[] = {
418 &dev_attr_brightness_changed.attr,
419 &dev_attr_max_brightness.attr,
420 &dev_attr_lcd_level.attr,
421 NULL
422};
423
424static struct attribute_group fujitsupf_attribute_group = {
425 .attrs = fujitsupf_attributes
426};
427
428static struct platform_driver fujitsupf_driver = {
429 .driver = {
430 .name = "fujitsu-laptop",
431 .owner = THIS_MODULE,
432 }
433};
434
435static void dmi_check_cb_common(const struct dmi_system_id *id)
436{
437 acpi_handle handle;
438 int have_blnf;
439 printk(KERN_INFO "fujitsu-laptop: Identified laptop model '%s'.\n",
440 id->ident);
441 have_blnf = ACPI_SUCCESS
442 (acpi_get_handle(NULL, "\\_SB.PCI0.GFX0.LCD.BLNF", &handle));
443 if (use_alt_lcd_levels == -1) {
444 vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detecting usealt\n");
445 use_alt_lcd_levels = 1;
446 }
447 if (disable_brightness_keys == -1) {
448 vdbg_printk(FUJLAPTOP_DBG_TRACE,
449 "auto-detecting disable_keys\n");
450 disable_brightness_keys = have_blnf ? 1 : 0;
451 }
452 if (disable_brightness_adjust == -1) {
453 vdbg_printk(FUJLAPTOP_DBG_TRACE,
454 "auto-detecting disable_adjust\n");
455 disable_brightness_adjust = have_blnf ? 0 : 1;
456 }
457}
458
459static int dmi_check_cb_s6410(const struct dmi_system_id *id)
460{
461 dmi_check_cb_common(id);
462 fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */
463 fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */
464 return 0;
465}
466
467static int dmi_check_cb_s6420(const struct dmi_system_id *id)
468{
469 dmi_check_cb_common(id);
470 fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */
471 fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */
472 return 0;
473}
474
475static int dmi_check_cb_p8010(const struct dmi_system_id *id)
476{
477 dmi_check_cb_common(id);
478 fujitsu->keycode1 = KEY_HELP; /* "Support" */
479 fujitsu->keycode3 = KEY_SWITCHVIDEOMODE; /* "Presentation" */
480 fujitsu->keycode4 = KEY_WWW; /* "Internet" */
481 return 0;
482}
483
484static struct dmi_system_id fujitsu_dmi_table[] = {
485 {
486 .ident = "Fujitsu Siemens S6410",
487 .matches = {
488 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
489 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
490 },
491 .callback = dmi_check_cb_s6410},
492 {
493 .ident = "Fujitsu Siemens S6420",
494 .matches = {
495 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
496 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"),
497 },
498 .callback = dmi_check_cb_s6420},
499 {
500 .ident = "Fujitsu LifeBook P8010",
501 .matches = {
502 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
503 DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"),
504 },
505 .callback = dmi_check_cb_p8010},
506 {}
507};
508
509/* ACPI device for LCD brightness control */
510
511static int acpi_fujitsu_add(struct acpi_device *device)
512{
513 acpi_status status;
514 acpi_handle handle;
515 int result = 0;
516 int state = 0;
517 struct input_dev *input;
518 int error;
519
520 if (!device)
521 return -EINVAL;
522
523 fujitsu->acpi_handle = device->handle;
524 sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME);
525 sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
526 device->driver_data = fujitsu;
527
528 status = acpi_install_notify_handler(device->handle,
529 ACPI_DEVICE_NOTIFY,
530 acpi_fujitsu_notify, fujitsu);
531
532 if (ACPI_FAILURE(status)) {
533 printk(KERN_ERR "Error installing notify handler\n");
534 error = -ENODEV;
535 goto err_stop;
536 }
537
538 fujitsu->input = input = input_allocate_device();
539 if (!input) {
540 error = -ENOMEM;
541 goto err_uninstall_notify;
542 }
543
544 snprintf(fujitsu->phys, sizeof(fujitsu->phys),
545 "%s/video/input0", acpi_device_hid(device));
546
547 input->name = acpi_device_name(device);
548 input->phys = fujitsu->phys;
549 input->id.bustype = BUS_HOST;
550 input->id.product = 0x06;
551 input->dev.parent = &device->dev;
552 input->evbit[0] = BIT(EV_KEY);
553 set_bit(KEY_BRIGHTNESSUP, input->keybit);
554 set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
555 set_bit(KEY_UNKNOWN, input->keybit);
556
557 error = input_register_device(input);
558 if (error)
559 goto err_free_input_dev;
560
561 result = acpi_bus_get_power(fujitsu->acpi_handle, &state);
562 if (result) {
563 printk(KERN_ERR "Error reading power state\n");
564 goto end;
565 }
566
567 printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
568 acpi_device_name(device), acpi_device_bid(device),
569 !device->power.state ? "on" : "off");
570
571 fujitsu->dev = device;
572
573 if (ACPI_SUCCESS
574 (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
575 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
576 if (ACPI_FAILURE
577 (acpi_evaluate_object
578 (device->handle, METHOD_NAME__INI, NULL, NULL)))
579 printk(KERN_ERR "_INI Method failed\n");
580 }
581
582 /* do config (detect defaults) */
583 use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0;
584 disable_brightness_keys = disable_brightness_keys == 1 ? 1 : 0;
585 disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0;
586 vdbg_printk(FUJLAPTOP_DBG_INFO,
587 "config: [alt interface: %d], [key disable: %d], [adjust disable: %d]\n",
588 use_alt_lcd_levels, disable_brightness_keys,
589 disable_brightness_adjust);
590
591 if (get_max_brightness() <= 0)
592 fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS;
593 if (use_alt_lcd_levels)
594 get_lcd_level_alt();
595 else
596 get_lcd_level();
597
598 return result;
599
600end:
601err_free_input_dev:
602 input_free_device(input);
603err_uninstall_notify:
604 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
605 acpi_fujitsu_notify);
606err_stop:
607
608 return result;
609}
610
611static int acpi_fujitsu_remove(struct acpi_device *device, int type)
612{
613 acpi_status status;
614 struct fujitsu_t *fujitsu = NULL;
615
616 if (!device || !acpi_driver_data(device))
617 return -EINVAL;
618
619 fujitsu = acpi_driver_data(device);
620
621 status = acpi_remove_notify_handler(fujitsu->acpi_handle,
622 ACPI_DEVICE_NOTIFY,
623 acpi_fujitsu_notify);
624
625 if (!device || !acpi_driver_data(device))
626 return -EINVAL;
627
628 fujitsu->acpi_handle = NULL;
629
630 return 0;
631}
632
633/* Brightness notify */
634
635static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data)
636{
637 struct input_dev *input;
638 int keycode;
639 int oldb, newb;
640
641 input = fujitsu->input;
642
643 switch (event) {
644 case ACPI_FUJITSU_NOTIFY_CODE1:
645 keycode = 0;
646 oldb = fujitsu->brightness_level;
647 get_lcd_level(); /* the alt version always yields changed */
648 newb = fujitsu->brightness_level;
649
650 vdbg_printk(FUJLAPTOP_DBG_TRACE,
651 "brightness button event [%i -> %i (%i)]\n",
652 oldb, newb, fujitsu->brightness_changed);
653
654 if (oldb == newb && fujitsu->brightness_changed) {
655 keycode = 0;
656 if (disable_brightness_keys != 1) {
657 if (oldb == 0) {
658 acpi_bus_generate_proc_event
659 (fujitsu->dev,
660 ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS,
661 0);
662 keycode = KEY_BRIGHTNESSDOWN;
663 } else if (oldb ==
664 (fujitsu->max_brightness) - 1) {
665 acpi_bus_generate_proc_event
666 (fujitsu->dev,
667 ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS,
668 0);
669 keycode = KEY_BRIGHTNESSUP;
670 }
671 }
672 } else if (oldb < newb) {
673 if (disable_brightness_adjust != 1) {
674 if (use_alt_lcd_levels)
675 set_lcd_level_alt(newb);
676 else
677 set_lcd_level(newb);
678 }
679 if (disable_brightness_keys != 1) {
680 acpi_bus_generate_proc_event(fujitsu->dev,
681 ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0);
682 keycode = KEY_BRIGHTNESSUP;
683 }
684 } else if (oldb > newb) {
685 if (disable_brightness_adjust != 1) {
686 if (use_alt_lcd_levels)
687 set_lcd_level_alt(newb);
688 else
689 set_lcd_level(newb);
690 }
691 if (disable_brightness_keys != 1) {
692 acpi_bus_generate_proc_event(fujitsu->dev,
693 ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0);
694 keycode = KEY_BRIGHTNESSDOWN;
695 }
696 } else {
697 keycode = KEY_UNKNOWN;
698 }
699 break;
700 default:
701 keycode = KEY_UNKNOWN;
702 vdbg_printk(FUJLAPTOP_DBG_WARN,
703 "unsupported event [0x%x]\n", event);
704 break;
705 }
706
707 if (keycode != 0) {
708 input_report_key(input, keycode, 1);
709 input_sync(input);
710 input_report_key(input, keycode, 0);
711 input_sync(input);
712 }
713
714 return;
715}
716
717/* ACPI device for hotkey handling */
718
719static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
720{
721 acpi_status status;
722 acpi_handle handle;
723 int result = 0;
724 int state = 0;
725 struct input_dev *input;
726 int error;
727 int i;
728
729 if (!device)
730 return -EINVAL;
731
732 fujitsu_hotkey->acpi_handle = device->handle;
733 sprintf(acpi_device_name(device), "%s",
734 ACPI_FUJITSU_HOTKEY_DEVICE_NAME);
735 sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
736 device->driver_data = fujitsu_hotkey;
737
738 status = acpi_install_notify_handler(device->handle,
739 ACPI_DEVICE_NOTIFY,
740 acpi_fujitsu_hotkey_notify,
741 fujitsu_hotkey);
742
743 if (ACPI_FAILURE(status)) {
744 printk(KERN_ERR "Error installing notify handler\n");
745 error = -ENODEV;
746 goto err_stop;
747 }
748
749 /* kfifo */
750 spin_lock_init(&fujitsu_hotkey->fifo_lock);
751 fujitsu_hotkey->fifo =
752 kfifo_alloc(RINGBUFFERSIZE * sizeof(int), GFP_KERNEL,
753 &fujitsu_hotkey->fifo_lock);
754 if (IS_ERR(fujitsu_hotkey->fifo)) {
755 printk(KERN_ERR "kfifo_alloc failed\n");
756 error = PTR_ERR(fujitsu_hotkey->fifo);
757 goto err_stop;
758 }
759
760 fujitsu_hotkey->input = input = input_allocate_device();
761 if (!input) {
762 error = -ENOMEM;
763 goto err_uninstall_notify;
764 }
765
766 snprintf(fujitsu_hotkey->phys, sizeof(fujitsu_hotkey->phys),
767 "%s/video/input0", acpi_device_hid(device));
768
769 input->name = acpi_device_name(device);
770 input->phys = fujitsu_hotkey->phys;
771 input->id.bustype = BUS_HOST;
772 input->id.product = 0x06;
773 input->dev.parent = &device->dev;
774 input->evbit[0] = BIT(EV_KEY);
775 set_bit(fujitsu->keycode1, input->keybit);
776 set_bit(fujitsu->keycode2, input->keybit);
777 set_bit(fujitsu->keycode3, input->keybit);
778 set_bit(fujitsu->keycode4, input->keybit);
779 set_bit(KEY_UNKNOWN, input->keybit);
780
781 error = input_register_device(input);
782 if (error)
783 goto err_free_input_dev;
784
785 result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state);
786 if (result) {
787 printk(KERN_ERR "Error reading power state\n");
788 goto end;
789 }
790
791 printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
792 acpi_device_name(device), acpi_device_bid(device),
793 !device->power.state ? "on" : "off");
794
795 fujitsu_hotkey->dev = device;
796
797 if (ACPI_SUCCESS
798 (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
799 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
800 if (ACPI_FAILURE
801 (acpi_evaluate_object
802 (device->handle, METHOD_NAME__INI, NULL, NULL)))
803 printk(KERN_ERR "_INI Method failed\n");
804 }
805
806 i = 0; /* Discard hotkey ringbuffer */
807 while (get_irb() != 0 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) ;
808 vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);
809
810 return result;
811
812end:
813err_free_input_dev:
814 input_free_device(input);
815err_uninstall_notify:
816 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
817 acpi_fujitsu_hotkey_notify);
818 kfifo_free(fujitsu_hotkey->fifo);
819err_stop:
820
821 return result;
822}
823
824static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type)
825{
826 acpi_status status;
827 struct fujitsu_hotkey_t *fujitsu_hotkey = NULL;
828
829 if (!device || !acpi_driver_data(device))
830 return -EINVAL;
831
832 fujitsu_hotkey = acpi_driver_data(device);
833
834 status = acpi_remove_notify_handler(fujitsu_hotkey->acpi_handle,
835 ACPI_DEVICE_NOTIFY,
836 acpi_fujitsu_hotkey_notify);
837
838 fujitsu_hotkey->acpi_handle = NULL;
839
840 kfifo_free(fujitsu_hotkey->fifo);
841
842 return 0;
843}
844
845static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
846 void *data)
847{
848 struct input_dev *input;
849 int keycode, keycode_r;
850 unsigned int irb = 1;
851 int i, status;
852
853 input = fujitsu_hotkey->input;
854
855 vdbg_printk(FUJLAPTOP_DBG_TRACE, "Hotkey event\n");
856
857 switch (event) {
858 case ACPI_FUJITSU_NOTIFY_CODE1:
859 i = 0;
860 while ((irb = get_irb()) != 0
861 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) {
862 vdbg_printk(FUJLAPTOP_DBG_TRACE, "GIRB result [%x]\n",
863 irb);
864
865 switch (irb & 0x4ff) {
866 case KEY1_CODE:
867 keycode = fujitsu->keycode1;
868 break;
869 case KEY2_CODE:
870 keycode = fujitsu->keycode2;
871 break;
872 case KEY3_CODE:
873 keycode = fujitsu->keycode3;
874 break;
875 case KEY4_CODE:
876 keycode = fujitsu->keycode4;
877 break;
878 case 0:
879 keycode = 0;
880 break;
881 default:
882 vdbg_printk(FUJLAPTOP_DBG_WARN,
883 "Unknown GIRB result [%x]\n", irb);
884 keycode = -1;
885 break;
886 }
887 if (keycode > 0) {
888 vdbg_printk(FUJLAPTOP_DBG_TRACE,
889 "Push keycode into ringbuffer [%d]\n",
890 keycode);
891 status = kfifo_put(fujitsu_hotkey->fifo,
892 (unsigned char *)&keycode,
893 sizeof(keycode));
894 if (status != sizeof(keycode)) {
895 vdbg_printk(FUJLAPTOP_DBG_WARN,
896 "Could not push keycode [0x%x]\n",
897 keycode);
898 } else {
899 input_report_key(input, keycode, 1);
900 input_sync(input);
901 }
902 } else if (keycode == 0) {
903 while ((status =
904 kfifo_get
905 (fujitsu_hotkey->fifo, (unsigned char *)
906 &keycode_r,
907 sizeof
908 (keycode_r))) == sizeof(keycode_r)) {
909 input_report_key(input, keycode_r, 0);
910 input_sync(input);
911 vdbg_printk(FUJLAPTOP_DBG_TRACE,
912 "Pop keycode from ringbuffer [%d]\n",
913 keycode_r);
914 }
915 }
916 }
917
918 break;
919 default:
920 keycode = KEY_UNKNOWN;
921 vdbg_printk(FUJLAPTOP_DBG_WARN,
922 "Unsupported event [0x%x]\n", event);
923 input_report_key(input, keycode, 1);
924 input_sync(input);
925 input_report_key(input, keycode, 0);
926 input_sync(input);
927 break;
928 }
929
930 return;
931}
932
933/* Initialization */
934
935static const struct acpi_device_id fujitsu_device_ids[] = {
936 {ACPI_FUJITSU_HID, 0},
937 {"", 0},
938};
939
940static struct acpi_driver acpi_fujitsu_driver = {
941 .name = ACPI_FUJITSU_DRIVER_NAME,
942 .class = ACPI_FUJITSU_CLASS,
943 .ids = fujitsu_device_ids,
944 .ops = {
945 .add = acpi_fujitsu_add,
946 .remove = acpi_fujitsu_remove,
947 },
948};
949
950static const struct acpi_device_id fujitsu_hotkey_device_ids[] = {
951 {ACPI_FUJITSU_HOTKEY_HID, 0},
952 {"", 0},
953};
954
955static struct acpi_driver acpi_fujitsu_hotkey_driver = {
956 .name = ACPI_FUJITSU_HOTKEY_DRIVER_NAME,
957 .class = ACPI_FUJITSU_CLASS,
958 .ids = fujitsu_hotkey_device_ids,
959 .ops = {
960 .add = acpi_fujitsu_hotkey_add,
961 .remove = acpi_fujitsu_hotkey_remove,
962 },
963};
964
965static int __init fujitsu_init(void)
966{
967 int ret, result, max_brightness;
968
969 if (acpi_disabled)
970 return -ENODEV;
971
972 fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL);
973 if (!fujitsu)
974 return -ENOMEM;
975 memset(fujitsu, 0, sizeof(struct fujitsu_t));
976 fujitsu->keycode1 = KEY_PROG1;
977 fujitsu->keycode2 = KEY_PROG2;
978 fujitsu->keycode3 = KEY_PROG3;
979 fujitsu->keycode4 = KEY_PROG4;
980 dmi_check_system(fujitsu_dmi_table);
981
982 result = acpi_bus_register_driver(&acpi_fujitsu_driver);
983 if (result < 0) {
984 ret = -ENODEV;
985 goto fail_acpi;
986 }
987
988 /* Register platform stuff */
989
990 fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1);
991 if (!fujitsu->pf_device) {
992 ret = -ENOMEM;
993 goto fail_platform_driver;
994 }
995
996 ret = platform_device_add(fujitsu->pf_device);
997 if (ret)
998 goto fail_platform_device1;
999
1000 ret =
1001 sysfs_create_group(&fujitsu->pf_device->dev.kobj,
1002 &fujitsupf_attribute_group);
1003 if (ret)
1004 goto fail_platform_device2;
1005
1006 /* Register backlight stuff */
1007
1008 if (!acpi_video_backlight_support()) {
1009 fujitsu->bl_device =
1010 backlight_device_register("fujitsu-laptop", NULL, NULL,
1011 &fujitsubl_ops);
1012 if (IS_ERR(fujitsu->bl_device))
1013 return PTR_ERR(fujitsu->bl_device);
1014 max_brightness = fujitsu->max_brightness;
1015 fujitsu->bl_device->props.max_brightness = max_brightness - 1;
1016 fujitsu->bl_device->props.brightness = fujitsu->brightness_level;
1017 }
1018
1019 ret = platform_driver_register(&fujitsupf_driver);
1020 if (ret)
1021 goto fail_backlight;
1022
1023 /* Register hotkey driver */
1024
1025 fujitsu_hotkey = kmalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL);
1026 if (!fujitsu_hotkey) {
1027 ret = -ENOMEM;
1028 goto fail_hotkey;
1029 }
1030 memset(fujitsu_hotkey, 0, sizeof(struct fujitsu_hotkey_t));
1031
1032 result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver);
1033 if (result < 0) {
1034 ret = -ENODEV;
1035 goto fail_hotkey1;
1036 }
1037
1038 printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION
1039 " successfully loaded.\n");
1040
1041 return 0;
1042
1043fail_hotkey1:
1044
1045 kfree(fujitsu_hotkey);
1046
1047fail_hotkey:
1048
1049 platform_driver_unregister(&fujitsupf_driver);
1050
1051fail_backlight:
1052
1053 if (fujitsu->bl_device)
1054 backlight_device_unregister(fujitsu->bl_device);
1055
1056fail_platform_device2:
1057
1058 platform_device_del(fujitsu->pf_device);
1059
1060fail_platform_device1:
1061
1062 platform_device_put(fujitsu->pf_device);
1063
1064fail_platform_driver:
1065
1066 acpi_bus_unregister_driver(&acpi_fujitsu_driver);
1067
1068fail_acpi:
1069
1070 kfree(fujitsu);
1071
1072 return ret;
1073}
1074
1075static void __exit fujitsu_cleanup(void)
1076{
1077 sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
1078 &fujitsupf_attribute_group);
1079 platform_device_unregister(fujitsu->pf_device);
1080 platform_driver_unregister(&fujitsupf_driver);
1081 if (fujitsu->bl_device)
1082 backlight_device_unregister(fujitsu->bl_device);
1083
1084 acpi_bus_unregister_driver(&acpi_fujitsu_driver);
1085
1086 kfree(fujitsu);
1087
1088 acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver);
1089
1090 kfree(fujitsu_hotkey);
1091
1092 printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n");
1093}
1094
1095module_init(fujitsu_init);
1096module_exit(fujitsu_cleanup);
1097
1098module_param(use_alt_lcd_levels, uint, 0644);
1099MODULE_PARM_DESC(use_alt_lcd_levels,
1100 "Use alternative interface for lcd_levels (needed for Lifebook s6410).");
1101module_param(disable_brightness_keys, uint, 0644);
1102MODULE_PARM_DESC(disable_brightness_keys,
1103 "Disable brightness keys (eg. if they are already handled by the generic ACPI_VIDEO device).");
1104module_param(disable_brightness_adjust, uint, 0644);
1105MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment .");
1106#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
1107module_param_named(debug, dbg_level, uint, 0644);
1108MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
1109#endif
1110
1111MODULE_AUTHOR("Jonathan Woithe, Peter Gruber");
1112MODULE_DESCRIPTION("Fujitsu laptop extras support");
1113MODULE_VERSION(FUJITSU_DRIVER_VERSION);
1114MODULE_LICENSE("GPL");
1115
1116MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
1117MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*");
1118
1119static struct pnp_device_id pnp_ids[] = {
1120 {.id = "FUJ02bf"},
1121 {.id = "FUJ02B1"},
1122 {.id = "FUJ02E3"},
1123 {.id = ""}
1124};
1125
1126MODULE_DEVICE_TABLE(pnp, pnp_ids);
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
new file mode 100644
index 000000000000..4b7c24c519c3
--- /dev/null
+++ b/drivers/platform/x86/hp-wmi.c
@@ -0,0 +1,512 @@
1/*
2 * HP WMI hotkeys
3 *
4 * Copyright (C) 2008 Red Hat <mjg@redhat.com>
5 *
6 * Portions based on wistron_btns.c:
7 * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
8 * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
9 * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26#include <linux/kernel.h>
27#include <linux/module.h>
28#include <linux/init.h>
29#include <linux/types.h>
30#include <linux/input.h>
31#include <acpi/acpi_drivers.h>
32#include <linux/platform_device.h>
33#include <linux/acpi.h>
34#include <linux/rfkill.h>
35#include <linux/string.h>
36
37MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
38MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
39MODULE_LICENSE("GPL");
40
41MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
42MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
43
44#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
45#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
46
47#define HPWMI_DISPLAY_QUERY 0x1
48#define HPWMI_HDDTEMP_QUERY 0x2
49#define HPWMI_ALS_QUERY 0x3
50#define HPWMI_DOCK_QUERY 0x4
51#define HPWMI_WIRELESS_QUERY 0x5
52#define HPWMI_HOTKEY_QUERY 0xc
53
54static int __init hp_wmi_bios_setup(struct platform_device *device);
55static int __exit hp_wmi_bios_remove(struct platform_device *device);
56
57struct bios_args {
58 u32 signature;
59 u32 command;
60 u32 commandtype;
61 u32 datasize;
62 u32 data;
63};
64
65struct bios_return {
66 u32 sigpass;
67 u32 return_code;
68 u32 value;
69};
70
71struct key_entry {
72 char type; /* See KE_* below */
73 u16 code;
74 u16 keycode;
75};
76
77enum { KE_KEY, KE_SW, KE_END };
78
79static struct key_entry hp_wmi_keymap[] = {
80 {KE_SW, 0x01, SW_DOCK},
81 {KE_KEY, 0x02, KEY_BRIGHTNESSUP},
82 {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
83 {KE_KEY, 0x20e6, KEY_PROG1},
84 {KE_KEY, 0x2142, KEY_MEDIA},
85 {KE_KEY, 0x213b, KEY_INFO},
86 {KE_KEY, 0x231b, KEY_HELP},
87 {KE_END, 0}
88};
89
90static struct input_dev *hp_wmi_input_dev;
91static struct platform_device *hp_wmi_platform_dev;
92
93static struct rfkill *wifi_rfkill;
94static struct rfkill *bluetooth_rfkill;
95static struct rfkill *wwan_rfkill;
96
97static struct platform_driver hp_wmi_driver = {
98 .driver = {
99 .name = "hp-wmi",
100 .owner = THIS_MODULE,
101 },
102 .probe = hp_wmi_bios_setup,
103 .remove = hp_wmi_bios_remove,
104};
105
106static int hp_wmi_perform_query(int query, int write, int value)
107{
108 struct bios_return bios_return;
109 acpi_status status;
110 union acpi_object *obj;
111 struct bios_args args = {
112 .signature = 0x55434553,
113 .command = write ? 0x2 : 0x1,
114 .commandtype = query,
115 .datasize = write ? 0x4 : 0,
116 .data = value,
117 };
118 struct acpi_buffer input = { sizeof(struct bios_args), &args };
119 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
120
121 status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
122
123 obj = output.pointer;
124
125 if (!obj || obj->type != ACPI_TYPE_BUFFER)
126 return -EINVAL;
127
128 bios_return = *((struct bios_return *)obj->buffer.pointer);
129 if (bios_return.return_code > 0)
130 return bios_return.return_code * -1;
131 else
132 return bios_return.value;
133}
134
135static int hp_wmi_display_state(void)
136{
137 return hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, 0);
138}
139
140static int hp_wmi_hddtemp_state(void)
141{
142 return hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, 0);
143}
144
145static int hp_wmi_als_state(void)
146{
147 return hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, 0);
148}
149
150static int hp_wmi_dock_state(void)
151{
152 return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0);
153}
154
155static int hp_wmi_wifi_set(void *data, enum rfkill_state state)
156{
157 if (state)
158 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101);
159 else
160 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100);
161}
162
163static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state)
164{
165 if (state)
166 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202);
167 else
168 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200);
169}
170
171static int hp_wmi_wwan_set(void *data, enum rfkill_state state)
172{
173 if (state)
174 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404);
175 else
176 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400);
177}
178
179static int hp_wmi_wifi_state(void)
180{
181 int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
182
183 if (wireless & 0x100)
184 return RFKILL_STATE_UNBLOCKED;
185 else
186 return RFKILL_STATE_SOFT_BLOCKED;
187}
188
189static int hp_wmi_bluetooth_state(void)
190{
191 int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
192
193 if (wireless & 0x10000)
194 return RFKILL_STATE_UNBLOCKED;
195 else
196 return RFKILL_STATE_SOFT_BLOCKED;
197}
198
199static int hp_wmi_wwan_state(void)
200{
201 int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
202
203 if (wireless & 0x1000000)
204 return RFKILL_STATE_UNBLOCKED;
205 else
206 return RFKILL_STATE_SOFT_BLOCKED;
207}
208
209static ssize_t show_display(struct device *dev, struct device_attribute *attr,
210 char *buf)
211{
212 int value = hp_wmi_display_state();
213 if (value < 0)
214 return -EINVAL;
215 return sprintf(buf, "%d\n", value);
216}
217
218static ssize_t show_hddtemp(struct device *dev, struct device_attribute *attr,
219 char *buf)
220{
221 int value = hp_wmi_hddtemp_state();
222 if (value < 0)
223 return -EINVAL;
224 return sprintf(buf, "%d\n", value);
225}
226
227static ssize_t show_als(struct device *dev, struct device_attribute *attr,
228 char *buf)
229{
230 int value = hp_wmi_als_state();
231 if (value < 0)
232 return -EINVAL;
233 return sprintf(buf, "%d\n", value);
234}
235
236static ssize_t show_dock(struct device *dev, struct device_attribute *attr,
237 char *buf)
238{
239 int value = hp_wmi_dock_state();
240 if (value < 0)
241 return -EINVAL;
242 return sprintf(buf, "%d\n", value);
243}
244
245static ssize_t set_als(struct device *dev, struct device_attribute *attr,
246 const char *buf, size_t count)
247{
248 u32 tmp = simple_strtoul(buf, NULL, 10);
249 hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, tmp);
250 return count;
251}
252
253static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
254static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
255static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
256static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
257
258static struct key_entry *hp_wmi_get_entry_by_scancode(int code)
259{
260 struct key_entry *key;
261
262 for (key = hp_wmi_keymap; key->type != KE_END; key++)
263 if (code == key->code)
264 return key;
265
266 return NULL;
267}
268
269static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode)
270{
271 struct key_entry *key;
272
273 for (key = hp_wmi_keymap; key->type != KE_END; key++)
274 if (key->type == KE_KEY && keycode == key->keycode)
275 return key;
276
277 return NULL;
278}
279
280static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode)
281{
282 struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode);
283
284 if (key && key->type == KE_KEY) {
285 *keycode = key->keycode;
286 return 0;
287 }
288
289 return -EINVAL;
290}
291
292static int hp_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode)
293{
294 struct key_entry *key;
295 int old_keycode;
296
297 if (keycode < 0 || keycode > KEY_MAX)
298 return -EINVAL;
299
300 key = hp_wmi_get_entry_by_scancode(scancode);
301 if (key && key->type == KE_KEY) {
302 old_keycode = key->keycode;
303 key->keycode = keycode;
304 set_bit(keycode, dev->keybit);
305 if (!hp_wmi_get_entry_by_keycode(old_keycode))
306 clear_bit(old_keycode, dev->keybit);
307 return 0;
308 }
309
310 return -EINVAL;
311}
312
313static void hp_wmi_notify(u32 value, void *context)
314{
315 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
316 static struct key_entry *key;
317 union acpi_object *obj;
318
319 wmi_get_event_data(value, &response);
320
321 obj = (union acpi_object *)response.pointer;
322
323 if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) {
324 int eventcode = *((u8 *) obj->buffer.pointer);
325 if (eventcode == 0x4)
326 eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
327 0);
328 key = hp_wmi_get_entry_by_scancode(eventcode);
329 if (key) {
330 switch (key->type) {
331 case KE_KEY:
332 input_report_key(hp_wmi_input_dev,
333 key->keycode, 1);
334 input_sync(hp_wmi_input_dev);
335 input_report_key(hp_wmi_input_dev,
336 key->keycode, 0);
337 input_sync(hp_wmi_input_dev);
338 break;
339 case KE_SW:
340 input_report_switch(hp_wmi_input_dev,
341 key->keycode,
342 hp_wmi_dock_state());
343 input_sync(hp_wmi_input_dev);
344 break;
345 }
346 } else if (eventcode == 0x5) {
347 if (wifi_rfkill)
348 rfkill_force_state(wifi_rfkill,
349 hp_wmi_wifi_state());
350 if (bluetooth_rfkill)
351 rfkill_force_state(bluetooth_rfkill,
352 hp_wmi_bluetooth_state());
353 if (wwan_rfkill)
354 rfkill_force_state(wwan_rfkill,
355 hp_wmi_wwan_state());
356 } else
357 printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
358 eventcode);
359 } else
360 printk(KERN_INFO "HP WMI: Unknown response received\n");
361}
362
363static int __init hp_wmi_input_setup(void)
364{
365 struct key_entry *key;
366 int err;
367
368 hp_wmi_input_dev = input_allocate_device();
369
370 hp_wmi_input_dev->name = "HP WMI hotkeys";
371 hp_wmi_input_dev->phys = "wmi/input0";
372 hp_wmi_input_dev->id.bustype = BUS_HOST;
373 hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode;
374 hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode;
375
376 for (key = hp_wmi_keymap; key->type != KE_END; key++) {
377 switch (key->type) {
378 case KE_KEY:
379 set_bit(EV_KEY, hp_wmi_input_dev->evbit);
380 set_bit(key->keycode, hp_wmi_input_dev->keybit);
381 break;
382 case KE_SW:
383 set_bit(EV_SW, hp_wmi_input_dev->evbit);
384 set_bit(key->keycode, hp_wmi_input_dev->swbit);
385 break;
386 }
387 }
388
389 err = input_register_device(hp_wmi_input_dev);
390
391 if (err) {
392 input_free_device(hp_wmi_input_dev);
393 return err;
394 }
395
396 return 0;
397}
398
399static void cleanup_sysfs(struct platform_device *device)
400{
401 device_remove_file(&device->dev, &dev_attr_display);
402 device_remove_file(&device->dev, &dev_attr_hddtemp);
403 device_remove_file(&device->dev, &dev_attr_als);
404 device_remove_file(&device->dev, &dev_attr_dock);
405}
406
407static int __init hp_wmi_bios_setup(struct platform_device *device)
408{
409 int err;
410 int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
411
412 err = device_create_file(&device->dev, &dev_attr_display);
413 if (err)
414 goto add_sysfs_error;
415 err = device_create_file(&device->dev, &dev_attr_hddtemp);
416 if (err)
417 goto add_sysfs_error;
418 err = device_create_file(&device->dev, &dev_attr_als);
419 if (err)
420 goto add_sysfs_error;
421 err = device_create_file(&device->dev, &dev_attr_dock);
422 if (err)
423 goto add_sysfs_error;
424
425 if (wireless & 0x1) {
426 wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
427 wifi_rfkill->name = "hp-wifi";
428 wifi_rfkill->state = hp_wmi_wifi_state();
429 wifi_rfkill->toggle_radio = hp_wmi_wifi_set;
430 wifi_rfkill->user_claim_unsupported = 1;
431 rfkill_register(wifi_rfkill);
432 }
433
434 if (wireless & 0x2) {
435 bluetooth_rfkill = rfkill_allocate(&device->dev,
436 RFKILL_TYPE_BLUETOOTH);
437 bluetooth_rfkill->name = "hp-bluetooth";
438 bluetooth_rfkill->state = hp_wmi_bluetooth_state();
439 bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set;
440 bluetooth_rfkill->user_claim_unsupported = 1;
441 rfkill_register(bluetooth_rfkill);
442 }
443
444 if (wireless & 0x4) {
445 wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
446 wwan_rfkill->name = "hp-wwan";
447 wwan_rfkill->state = hp_wmi_wwan_state();
448 wwan_rfkill->toggle_radio = hp_wmi_wwan_set;
449 wwan_rfkill->user_claim_unsupported = 1;
450 rfkill_register(wwan_rfkill);
451 }
452
453 return 0;
454add_sysfs_error:
455 cleanup_sysfs(device);
456 return err;
457}
458
459static int __exit hp_wmi_bios_remove(struct platform_device *device)
460{
461 cleanup_sysfs(device);
462
463 if (wifi_rfkill)
464 rfkill_unregister(wifi_rfkill);
465 if (bluetooth_rfkill)
466 rfkill_unregister(bluetooth_rfkill);
467 if (wwan_rfkill)
468 rfkill_unregister(wwan_rfkill);
469
470 return 0;
471}
472
473static int __init hp_wmi_init(void)
474{
475 int err;
476
477 if (wmi_has_guid(HPWMI_EVENT_GUID)) {
478 err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
479 hp_wmi_notify, NULL);
480 if (!err)
481 hp_wmi_input_setup();
482 }
483
484 if (wmi_has_guid(HPWMI_BIOS_GUID)) {
485 err = platform_driver_register(&hp_wmi_driver);
486 if (err)
487 return 0;
488 hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
489 if (!hp_wmi_platform_dev) {
490 platform_driver_unregister(&hp_wmi_driver);
491 return 0;
492 }
493 platform_device_add(hp_wmi_platform_dev);
494 }
495
496 return 0;
497}
498
499static void __exit hp_wmi_exit(void)
500{
501 if (wmi_has_guid(HPWMI_EVENT_GUID)) {
502 wmi_remove_notify_handler(HPWMI_EVENT_GUID);
503 input_unregister_device(hp_wmi_input_dev);
504 }
505 if (hp_wmi_platform_dev) {
506 platform_device_del(hp_wmi_platform_dev);
507 platform_driver_unregister(&hp_wmi_driver);
508 }
509}
510
511module_init(hp_wmi_init);
512module_exit(hp_wmi_exit);
diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c
new file mode 100644
index 000000000000..27b7662955bb
--- /dev/null
+++ b/drivers/platform/x86/intel_menlow.c
@@ -0,0 +1,536 @@
1/*
2 * intel_menlow.c - Intel menlow Driver for thermal management extension
3 *
4 * Copyright (C) 2008 Intel Corp
5 * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
6 * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *
22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 *
24 * This driver creates the sys I/F for programming the sensors.
25 * It also implements the driver for intel menlow memory controller (hardware
26 * id is INT0002) which makes use of the platform specific ACPI methods
27 * to get/set bandwidth.
28 */
29
30#include <linux/kernel.h>
31#include <linux/module.h>
32#include <linux/init.h>
33#include <linux/types.h>
34#include <linux/pci.h>
35#include <linux/pm.h>
36
37#include <linux/thermal.h>
38#include <acpi/acpi_bus.h>
39#include <acpi/acpi_drivers.h>
40
41MODULE_AUTHOR("Thomas Sujith");
42MODULE_AUTHOR("Zhang Rui");
43MODULE_DESCRIPTION("Intel Menlow platform specific driver");
44MODULE_LICENSE("GPL");
45
46/*
47 * Memory controller device control
48 */
49
50#define MEMORY_GET_BANDWIDTH "GTHS"
51#define MEMORY_SET_BANDWIDTH "STHS"
52#define MEMORY_ARG_CUR_BANDWIDTH 1
53#define MEMORY_ARG_MAX_BANDWIDTH 0
54
55/*
56 * GTHS returning 'n' would mean that [0,n-1] states are supported
57 * In that case max_cstate would be n-1
58 * GTHS returning '0' would mean that no bandwidth control states are supported
59 */
60static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev,
61 unsigned long *max_state)
62{
63 struct acpi_device *device = cdev->devdata;
64 acpi_handle handle = device->handle;
65 unsigned long long value;
66 struct acpi_object_list arg_list;
67 union acpi_object arg;
68 acpi_status status = AE_OK;
69
70 arg_list.count = 1;
71 arg_list.pointer = &arg;
72 arg.type = ACPI_TYPE_INTEGER;
73 arg.integer.value = MEMORY_ARG_MAX_BANDWIDTH;
74 status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
75 &arg_list, &value);
76 if (ACPI_FAILURE(status))
77 return -EFAULT;
78
79 if (!value)
80 return -EINVAL;
81
82 *max_state = value - 1;
83 return 0;
84}
85
86static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev,
87 char *buf)
88{
89 unsigned long value;
90 if (memory_get_int_max_bandwidth(cdev, &value))
91 return -EINVAL;
92
93 return sprintf(buf, "%ld\n", value);
94}
95
96static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev,
97 char *buf)
98{
99 struct acpi_device *device = cdev->devdata;
100 acpi_handle handle = device->handle;
101 unsigned long long value;
102 struct acpi_object_list arg_list;
103 union acpi_object arg;
104 acpi_status status = AE_OK;
105
106 arg_list.count = 1;
107 arg_list.pointer = &arg;
108 arg.type = ACPI_TYPE_INTEGER;
109 arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;
110 status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
111 &arg_list, &value);
112 if (ACPI_FAILURE(status))
113 return -EFAULT;
114
115 return sprintf(buf, "%llu\n", value);
116}
117
118static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
119 unsigned int state)
120{
121 struct acpi_device *device = cdev->devdata;
122 acpi_handle handle = device->handle;
123 struct acpi_object_list arg_list;
124 union acpi_object arg;
125 acpi_status status;
126 unsigned long long temp;
127 unsigned long max_state;
128
129 if (memory_get_int_max_bandwidth(cdev, &max_state))
130 return -EFAULT;
131
132 if (state > max_state)
133 return -EINVAL;
134
135 arg_list.count = 1;
136 arg_list.pointer = &arg;
137 arg.type = ACPI_TYPE_INTEGER;
138 arg.integer.value = state;
139
140 status =
141 acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list,
142 &temp);
143
144 printk(KERN_INFO
145 "Bandwidth value was %d: status is %d\n", state, status);
146 if (ACPI_FAILURE(status))
147 return -EFAULT;
148
149 return 0;
150}
151
152static struct thermal_cooling_device_ops memory_cooling_ops = {
153 .get_max_state = memory_get_max_bandwidth,
154 .get_cur_state = memory_get_cur_bandwidth,
155 .set_cur_state = memory_set_cur_bandwidth,
156};
157
158/*
159 * Memory Device Management
160 */
161static int intel_menlow_memory_add(struct acpi_device *device)
162{
163 int result = -ENODEV;
164 acpi_status status = AE_OK;
165 acpi_handle dummy;
166 struct thermal_cooling_device *cdev;
167
168 if (!device)
169 return -EINVAL;
170
171 status = acpi_get_handle(device->handle, MEMORY_GET_BANDWIDTH, &dummy);
172 if (ACPI_FAILURE(status))
173 goto end;
174
175 status = acpi_get_handle(device->handle, MEMORY_SET_BANDWIDTH, &dummy);
176 if (ACPI_FAILURE(status))
177 goto end;
178
179 cdev = thermal_cooling_device_register("Memory controller", device,
180 &memory_cooling_ops);
181 if (IS_ERR(cdev)) {
182 result = PTR_ERR(cdev);
183 goto end;
184 }
185
186 device->driver_data = cdev;
187 result = sysfs_create_link(&device->dev.kobj,
188 &cdev->device.kobj, "thermal_cooling");
189 if (result)
190 goto unregister;
191
192 result = sysfs_create_link(&cdev->device.kobj,
193 &device->dev.kobj, "device");
194 if (result) {
195 sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
196 goto unregister;
197 }
198
199 end:
200 return result;
201
202 unregister:
203 thermal_cooling_device_unregister(cdev);
204 return result;
205
206}
207
208static int intel_menlow_memory_remove(struct acpi_device *device, int type)
209{
210 struct thermal_cooling_device *cdev = acpi_driver_data(device);
211
212 if (!device || !cdev)
213 return -EINVAL;
214
215 sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
216 sysfs_remove_link(&cdev->device.kobj, "device");
217 thermal_cooling_device_unregister(cdev);
218
219 return 0;
220}
221
222static const struct acpi_device_id intel_menlow_memory_ids[] = {
223 {"INT0002", 0},
224 {"", 0},
225};
226
227static struct acpi_driver intel_menlow_memory_driver = {
228 .name = "intel_menlow_thermal_control",
229 .ids = intel_menlow_memory_ids,
230 .ops = {
231 .add = intel_menlow_memory_add,
232 .remove = intel_menlow_memory_remove,
233 },
234};
235
236/*
237 * Sensor control on menlow platform
238 */
239
240#define THERMAL_AUX0 0
241#define THERMAL_AUX1 1
242#define GET_AUX0 "GAX0"
243#define GET_AUX1 "GAX1"
244#define SET_AUX0 "SAX0"
245#define SET_AUX1 "SAX1"
246
247struct intel_menlow_attribute {
248 struct device_attribute attr;
249 struct device *device;
250 acpi_handle handle;
251 struct list_head node;
252};
253
254static LIST_HEAD(intel_menlow_attr_list);
255static DEFINE_MUTEX(intel_menlow_attr_lock);
256
257/*
258 * sensor_get_auxtrip - get the current auxtrip value from sensor
259 * @name: Thermalzone name
260 * @auxtype : AUX0/AUX1
261 * @buf: syfs buffer
262 */
263static int sensor_get_auxtrip(acpi_handle handle, int index,
264 unsigned long long *value)
265{
266 acpi_status status;
267
268 if ((index != 0 && index != 1) || !value)
269 return -EINVAL;
270
271 status = acpi_evaluate_integer(handle, index ? GET_AUX1 : GET_AUX0,
272 NULL, value);
273 if (ACPI_FAILURE(status))
274 return -EIO;
275
276 return 0;
277}
278
279/*
280 * sensor_set_auxtrip - set the new auxtrip value to sensor
281 * @name: Thermalzone name
282 * @auxtype : AUX0/AUX1
283 * @buf: syfs buffer
284 */
285static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
286{
287 acpi_status status;
288 union acpi_object arg = {
289 ACPI_TYPE_INTEGER
290 };
291 struct acpi_object_list args = {
292 1, &arg
293 };
294 unsigned long long temp;
295
296 if (index != 0 && index != 1)
297 return -EINVAL;
298
299 status = acpi_evaluate_integer(handle, index ? GET_AUX0 : GET_AUX1,
300 NULL, &temp);
301 if (ACPI_FAILURE(status))
302 return -EIO;
303 if ((index && value < temp) || (!index && value > temp))
304 return -EINVAL;
305
306 arg.integer.value = value;
307 status = acpi_evaluate_integer(handle, index ? SET_AUX1 : SET_AUX0,
308 &args, &temp);
309 if (ACPI_FAILURE(status))
310 return -EIO;
311
312 /* do we need to check the return value of SAX0/SAX1 ? */
313
314 return 0;
315}
316
317#define to_intel_menlow_attr(_attr) \
318 container_of(_attr, struct intel_menlow_attribute, attr)
319
320static ssize_t aux0_show(struct device *dev,
321 struct device_attribute *dev_attr, char *buf)
322{
323 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
324 unsigned long long value;
325 int result;
326
327 result = sensor_get_auxtrip(attr->handle, 0, &value);
328
329 return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
330}
331
332static ssize_t aux1_show(struct device *dev,
333 struct device_attribute *dev_attr, char *buf)
334{
335 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
336 unsigned long long value;
337 int result;
338
339 result = sensor_get_auxtrip(attr->handle, 1, &value);
340
341 return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
342}
343
344static ssize_t aux0_store(struct device *dev,
345 struct device_attribute *dev_attr,
346 const char *buf, size_t count)
347{
348 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
349 int value;
350 int result;
351
352 /*Sanity check; should be a positive integer */
353 if (!sscanf(buf, "%d", &value))
354 return -EINVAL;
355
356 if (value < 0)
357 return -EINVAL;
358
359 result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_KELVIN(value));
360 return result ? result : count;
361}
362
363static ssize_t aux1_store(struct device *dev,
364 struct device_attribute *dev_attr,
365 const char *buf, size_t count)
366{
367 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
368 int value;
369 int result;
370
371 /*Sanity check; should be a positive integer */
372 if (!sscanf(buf, "%d", &value))
373 return -EINVAL;
374
375 if (value < 0)
376 return -EINVAL;
377
378 result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_KELVIN(value));
379 return result ? result : count;
380}
381
382/* BIOS can enable/disable the thermal user application in dabney platform */
383#define BIOS_ENABLED "\\_TZ.GSTS"
384static ssize_t bios_enabled_show(struct device *dev,
385 struct device_attribute *attr, char *buf)
386{
387 acpi_status status;
388 unsigned long long bios_enabled;
389
390 status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &bios_enabled);
391 if (ACPI_FAILURE(status))
392 return -ENODEV;
393
394 return sprintf(buf, "%s\n", bios_enabled ? "enabled" : "disabled");
395}
396
397static int intel_menlow_add_one_attribute(char *name, int mode, void *show,
398 void *store, struct device *dev,
399 acpi_handle handle)
400{
401 struct intel_menlow_attribute *attr;
402 int result;
403
404 attr = kzalloc(sizeof(struct intel_menlow_attribute), GFP_KERNEL);
405 if (!attr)
406 return -ENOMEM;
407
408 attr->attr.attr.name = name;
409 attr->attr.attr.mode = mode;
410 attr->attr.show = show;
411 attr->attr.store = store;
412 attr->device = dev;
413 attr->handle = handle;
414
415 result = device_create_file(dev, &attr->attr);
416 if (result)
417 return result;
418
419 mutex_lock(&intel_menlow_attr_lock);
420 list_add_tail(&attr->node, &intel_menlow_attr_list);
421 mutex_unlock(&intel_menlow_attr_lock);
422
423 return 0;
424}
425
426static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl,
427 void *context, void **rv)
428{
429 acpi_status status;
430 acpi_handle dummy;
431 struct thermal_zone_device *thermal;
432 int result;
433
434 result = acpi_bus_get_private_data(handle, (void **)&thermal);
435 if (result)
436 return 0;
437
438 /* _TZ must have the AUX0/1 methods */
439 status = acpi_get_handle(handle, GET_AUX0, &dummy);
440 if (ACPI_FAILURE(status))
441 goto not_found;
442
443 status = acpi_get_handle(handle, SET_AUX0, &dummy);
444 if (ACPI_FAILURE(status))
445 goto not_found;
446
447 result = intel_menlow_add_one_attribute("aux0", 0644,
448 aux0_show, aux0_store,
449 &thermal->device, handle);
450 if (result)
451 return AE_ERROR;
452
453 status = acpi_get_handle(handle, GET_AUX1, &dummy);
454 if (ACPI_FAILURE(status))
455 goto not_found;
456
457 status = acpi_get_handle(handle, SET_AUX1, &dummy);
458 if (ACPI_FAILURE(status))
459 goto not_found;
460
461 result = intel_menlow_add_one_attribute("aux1", 0644,
462 aux1_show, aux1_store,
463 &thermal->device, handle);
464 if (result)
465 return AE_ERROR;
466
467 /*
468 * create the "dabney_enabled" attribute which means the user app
469 * should be loaded or not
470 */
471
472 result = intel_menlow_add_one_attribute("bios_enabled", 0444,
473 bios_enabled_show, NULL,
474 &thermal->device, handle);
475 if (result)
476 return AE_ERROR;
477
478 not_found:
479 if (status == AE_NOT_FOUND)
480 return AE_OK;
481 else
482 return status;
483}
484
485static void intel_menlow_unregister_sensor(void)
486{
487 struct intel_menlow_attribute *pos, *next;
488
489 mutex_lock(&intel_menlow_attr_lock);
490 list_for_each_entry_safe(pos, next, &intel_menlow_attr_list, node) {
491 list_del(&pos->node);
492 device_remove_file(pos->device, &pos->attr);
493 kfree(pos);
494 }
495 mutex_unlock(&intel_menlow_attr_lock);
496
497 return;
498}
499
500static int __init intel_menlow_module_init(void)
501{
502 int result = -ENODEV;
503 acpi_status status;
504 unsigned long long enable;
505
506 if (acpi_disabled)
507 return result;
508
509 /* Looking for the \_TZ.GSTS method */
510 status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &enable);
511 if (ACPI_FAILURE(status) || !enable)
512 return -ENODEV;
513
514 /* Looking for ACPI device MEM0 with hardware id INT0002 */
515 result = acpi_bus_register_driver(&intel_menlow_memory_driver);
516 if (result)
517 return result;
518
519 /* Looking for sensors in each ACPI thermal zone */
520 status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
521 ACPI_UINT32_MAX,
522 intel_menlow_register_sensor, NULL, NULL);
523 if (ACPI_FAILURE(status))
524 return -ENODEV;
525
526 return 0;
527}
528
529static void __exit intel_menlow_module_exit(void)
530{
531 acpi_bus_unregister_driver(&intel_menlow_memory_driver);
532 intel_menlow_unregister_sensor();
533}
534
535module_init(intel_menlow_module_init);
536module_exit(intel_menlow_module_exit);
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c
new file mode 100644
index 000000000000..759763d18e4c
--- /dev/null
+++ b/drivers/platform/x86/msi-laptop.c
@@ -0,0 +1,437 @@
1/*-*-linux-c-*-*/
2
3/*
4 Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA.
20 */
21
22/*
23 * msi-laptop.c - MSI S270 laptop support. This laptop is sold under
24 * various brands, including "Cytron/TCM/Medion/Tchibo MD96100".
25 *
26 * Driver also supports S271, S420 models.
27 *
28 * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/:
29 *
30 * lcd_level - Screen brightness: contains a single integer in the
31 * range 0..8. (rw)
32 *
33 * auto_brightness - Enable automatic brightness control: contains
34 * either 0 or 1. If set to 1 the hardware adjusts the screen
35 * brightness automatically when the power cord is
36 * plugged/unplugged. (rw)
37 *
38 * wlan - WLAN subsystem enabled: contains either 0 or 1. (ro)
39 *
40 * bluetooth - Bluetooth subsystem enabled: contains either 0 or 1
41 * Please note that this file is constantly 0 if no Bluetooth
42 * hardware is available. (ro)
43 *
44 * In addition to these platform device attributes the driver
45 * registers itself in the Linux backlight control subsystem and is
46 * available to userspace under /sys/class/backlight/msi-laptop-bl/.
47 *
48 * This driver might work on other laptops produced by MSI. If you
49 * want to try it you can pass force=1 as argument to the module which
50 * will force it to load even when the DMI data doesn't identify the
51 * laptop as MSI S270. YMMV.
52 */
53
54#include <linux/module.h>
55#include <linux/kernel.h>
56#include <linux/init.h>
57#include <linux/acpi.h>
58#include <linux/dmi.h>
59#include <linux/backlight.h>
60#include <linux/platform_device.h>
61
62#define MSI_DRIVER_VERSION "0.5"
63
64#define MSI_LCD_LEVEL_MAX 9
65
66#define MSI_EC_COMMAND_WIRELESS 0x10
67#define MSI_EC_COMMAND_LCD_LEVEL 0x11
68
69static int force;
70module_param(force, bool, 0);
71MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
72
73static int auto_brightness;
74module_param(auto_brightness, int, 0);
75MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
76
77/* Hardware access */
78
79static int set_lcd_level(int level)
80{
81 u8 buf[2];
82
83 if (level < 0 || level >= MSI_LCD_LEVEL_MAX)
84 return -EINVAL;
85
86 buf[0] = 0x80;
87 buf[1] = (u8) (level*31);
88
89 return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0, 1);
90}
91
92static int get_lcd_level(void)
93{
94 u8 wdata = 0, rdata;
95 int result;
96
97 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1);
98 if (result < 0)
99 return result;
100
101 return (int) rdata / 31;
102}
103
104static int get_auto_brightness(void)
105{
106 u8 wdata = 4, rdata;
107 int result;
108
109 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1);
110 if (result < 0)
111 return result;
112
113 return !!(rdata & 8);
114}
115
116static int set_auto_brightness(int enable)
117{
118 u8 wdata[2], rdata;
119 int result;
120
121 wdata[0] = 4;
122
123 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1, 1);
124 if (result < 0)
125 return result;
126
127 wdata[0] = 0x84;
128 wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
129
130 return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1);
131}
132
133static int get_wireless_state(int *wlan, int *bluetooth)
134{
135 u8 wdata = 0, rdata;
136 int result;
137
138 result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1, 1);
139 if (result < 0)
140 return -1;
141
142 if (wlan)
143 *wlan = !!(rdata & 8);
144
145 if (bluetooth)
146 *bluetooth = !!(rdata & 128);
147
148 return 0;
149}
150
151/* Backlight device stuff */
152
153static int bl_get_brightness(struct backlight_device *b)
154{
155 return get_lcd_level();
156}
157
158
159static int bl_update_status(struct backlight_device *b)
160{
161 return set_lcd_level(b->props.brightness);
162}
163
164static struct backlight_ops msibl_ops = {
165 .get_brightness = bl_get_brightness,
166 .update_status = bl_update_status,
167};
168
169static struct backlight_device *msibl_device;
170
171/* Platform device */
172
173static ssize_t show_wlan(struct device *dev,
174 struct device_attribute *attr, char *buf)
175{
176
177 int ret, enabled;
178
179 ret = get_wireless_state(&enabled, NULL);
180 if (ret < 0)
181 return ret;
182
183 return sprintf(buf, "%i\n", enabled);
184}
185
186static ssize_t show_bluetooth(struct device *dev,
187 struct device_attribute *attr, char *buf)
188{
189
190 int ret, enabled;
191
192 ret = get_wireless_state(NULL, &enabled);
193 if (ret < 0)
194 return ret;
195
196 return sprintf(buf, "%i\n", enabled);
197}
198
199static ssize_t show_lcd_level(struct device *dev,
200 struct device_attribute *attr, char *buf)
201{
202
203 int ret;
204
205 ret = get_lcd_level();
206 if (ret < 0)
207 return ret;
208
209 return sprintf(buf, "%i\n", ret);
210}
211
212static ssize_t store_lcd_level(struct device *dev,
213 struct device_attribute *attr, const char *buf, size_t count)
214{
215
216 int level, ret;
217
218 if (sscanf(buf, "%i", &level) != 1 || (level < 0 || level >= MSI_LCD_LEVEL_MAX))
219 return -EINVAL;
220
221 ret = set_lcd_level(level);
222 if (ret < 0)
223 return ret;
224
225 return count;
226}
227
228static ssize_t show_auto_brightness(struct device *dev,
229 struct device_attribute *attr, char *buf)
230{
231
232 int ret;
233
234 ret = get_auto_brightness();
235 if (ret < 0)
236 return ret;
237
238 return sprintf(buf, "%i\n", ret);
239}
240
241static ssize_t store_auto_brightness(struct device *dev,
242 struct device_attribute *attr, const char *buf, size_t count)
243{
244
245 int enable, ret;
246
247 if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
248 return -EINVAL;
249
250 ret = set_auto_brightness(enable);
251 if (ret < 0)
252 return ret;
253
254 return count;
255}
256
257static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
258static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness);
259static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
260static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
261
262static struct attribute *msipf_attributes[] = {
263 &dev_attr_lcd_level.attr,
264 &dev_attr_auto_brightness.attr,
265 &dev_attr_bluetooth.attr,
266 &dev_attr_wlan.attr,
267 NULL
268};
269
270static struct attribute_group msipf_attribute_group = {
271 .attrs = msipf_attributes
272};
273
274static struct platform_driver msipf_driver = {
275 .driver = {
276 .name = "msi-laptop-pf",
277 .owner = THIS_MODULE,
278 }
279};
280
281static struct platform_device *msipf_device;
282
283/* Initialization */
284
285static int dmi_check_cb(const struct dmi_system_id *id)
286{
287 printk("msi-laptop: Identified laptop model '%s'.\n", id->ident);
288 return 0;
289}
290
291static struct dmi_system_id __initdata msi_dmi_table[] = {
292 {
293 .ident = "MSI S270",
294 .matches = {
295 DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
296 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
297 DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
298 DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
299 },
300 .callback = dmi_check_cb
301 },
302 {
303 .ident = "MSI S271",
304 .matches = {
305 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
306 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"),
307 DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
308 DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
309 },
310 .callback = dmi_check_cb
311 },
312 {
313 .ident = "MSI S420",
314 .matches = {
315 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
316 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"),
317 DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
318 DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
319 },
320 .callback = dmi_check_cb
321 },
322 {
323 .ident = "Medion MD96100",
324 .matches = {
325 DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
326 DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
327 DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
328 DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
329 },
330 .callback = dmi_check_cb
331 },
332 { }
333};
334
335static int __init msi_init(void)
336{
337 int ret;
338
339 if (acpi_disabled)
340 return -ENODEV;
341
342 if (!force && !dmi_check_system(msi_dmi_table))
343 return -ENODEV;
344
345 if (auto_brightness < 0 || auto_brightness > 2)
346 return -EINVAL;
347
348 /* Register backlight stuff */
349
350 if (acpi_video_backlight_support()) {
351 printk(KERN_INFO "MSI: Brightness ignored, must be controlled "
352 "by ACPI video driver\n");
353 } else {
354 msibl_device = backlight_device_register("msi-laptop-bl", NULL,
355 NULL, &msibl_ops);
356 if (IS_ERR(msibl_device))
357 return PTR_ERR(msibl_device);
358 msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1;
359 }
360
361 ret = platform_driver_register(&msipf_driver);
362 if (ret)
363 goto fail_backlight;
364
365 /* Register platform stuff */
366
367 msipf_device = platform_device_alloc("msi-laptop-pf", -1);
368 if (!msipf_device) {
369 ret = -ENOMEM;
370 goto fail_platform_driver;
371 }
372
373 ret = platform_device_add(msipf_device);
374 if (ret)
375 goto fail_platform_device1;
376
377 ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group);
378 if (ret)
379 goto fail_platform_device2;
380
381 /* Disable automatic brightness control by default because
382 * this module was probably loaded to do brightness control in
383 * software. */
384
385 if (auto_brightness != 2)
386 set_auto_brightness(auto_brightness);
387
388 printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n");
389
390 return 0;
391
392fail_platform_device2:
393
394 platform_device_del(msipf_device);
395
396fail_platform_device1:
397
398 platform_device_put(msipf_device);
399
400fail_platform_driver:
401
402 platform_driver_unregister(&msipf_driver);
403
404fail_backlight:
405
406 backlight_device_unregister(msibl_device);
407
408 return ret;
409}
410
411static void __exit msi_cleanup(void)
412{
413
414 sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
415 platform_device_unregister(msipf_device);
416 platform_driver_unregister(&msipf_driver);
417 backlight_device_unregister(msibl_device);
418
419 /* Enable automatic brightness control again */
420 if (auto_brightness != 2)
421 set_auto_brightness(1);
422
423 printk(KERN_INFO "msi-laptop: driver unloaded.\n");
424}
425
426module_init(msi_init);
427module_exit(msi_cleanup);
428
429MODULE_AUTHOR("Lennart Poettering");
430MODULE_DESCRIPTION("MSI Laptop Support");
431MODULE_VERSION(MSI_DRIVER_VERSION);
432MODULE_LICENSE("GPL");
433
434MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
435MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
436MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
437MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
new file mode 100644
index 000000000000..4a1bc64485d5
--- /dev/null
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -0,0 +1,766 @@
1/*
2 * Panasonic HotKey and LCD brightness control driver
3 * (C) 2004 Hiroshi Miura <miura@da-cha.org>
4 * (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/
5 * (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>
6 * (C) 2004 David Bronaugh <dbronaugh>
7 * (C) 2006-2008 Harald Welte <laforge@gnumonks.org>
8 *
9 * derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * publicshed by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 *---------------------------------------------------------------------------
25 *
26 * ChangeLog:
27 * Sep.23, 2008 Harald Welte <laforge@gnumonks.org>
28 * -v0.95 rename driver from drivers/acpi/pcc_acpi.c to
29 * drivers/misc/panasonic-laptop.c
30 *
31 * Jul.04, 2008 Harald Welte <laforge@gnumonks.org>
32 * -v0.94 replace /proc interface with device attributes
33 * support {set,get}keycode on th input device
34 *
35 * Jun.27, 2008 Harald Welte <laforge@gnumonks.org>
36 * -v0.92 merge with 2.6.26-rc6 input API changes
37 * remove broken <= 2.6.15 kernel support
38 * resolve all compiler warnings
39 * various coding style fixes (checkpatch.pl)
40 * add support for backlight api
41 * major code restructuring
42 *
43 * Dac.28, 2007 Harald Welte <laforge@gnumonks.org>
44 * -v0.91 merge with 2.6.24-rc6 ACPI changes
45 *
46 * Nov.04, 2006 Hiroshi Miura <miura@da-cha.org>
47 * -v0.9 remove warning about section reference.
48 * remove acpi_os_free
49 * add /proc/acpi/pcc/brightness interface for HAL access
50 * merge dbronaugh's enhancement
51 * Aug.17, 2004 David Bronaugh (dbronaugh)
52 * - Added screen brightness setting interface
53 * Thanks to FreeBSD crew (acpi_panasonic.c)
54 * for the ideas I needed to accomplish it
55 *
56 * May.29, 2006 Hiroshi Miura <miura@da-cha.org>
57 * -v0.8.4 follow to change keyinput structure
58 * thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,
59 * Jacob Bower <jacob.bower@ic.ac.uk> and
60 * Hiroshi Yokota for providing solutions.
61 *
62 * Oct.02, 2004 Hiroshi Miura <miura@da-cha.org>
63 * -v0.8.2 merge code of YOKOTA Hiroshi
64 * <yokota@netlab.is.tsukuba.ac.jp>.
65 * Add sticky key mode interface.
66 * Refactoring acpi_pcc_generate_keyinput().
67 *
68 * Sep.15, 2004 Hiroshi Miura <miura@da-cha.org>
69 * -v0.8 Generate key input event on input subsystem.
70 * This is based on yet another driver written by
71 * Ryuta Nakanishi.
72 *
73 * Sep.10, 2004 Hiroshi Miura <miura@da-cha.org>
74 * -v0.7 Change proc interface functions using seq_file
75 * facility as same as other ACPI drivers.
76 *
77 * Aug.28, 2004 Hiroshi Miura <miura@da-cha.org>
78 * -v0.6.4 Fix a silly error with status checking
79 *
80 * Aug.25, 2004 Hiroshi Miura <miura@da-cha.org>
81 * -v0.6.3 replace read_acpi_int by standard function
82 * acpi_evaluate_integer
83 * some clean up and make smart copyright notice.
84 * fix return value of pcc_acpi_get_key()
85 * fix checking return value of acpi_bus_register_driver()
86 *
87 * Aug.22, 2004 David Bronaugh <dbronaugh@linuxboxen.org>
88 * -v0.6.2 Add check on ACPI data (num_sifr)
89 * Coding style cleanups, better error messages/handling
90 * Fixed an off-by-one error in memory allocation
91 *
92 * Aug.21, 2004 David Bronaugh <dbronaugh@linuxboxen.org>
93 * -v0.6.1 Fix a silly error with status checking
94 *
95 * Aug.20, 2004 David Bronaugh <dbronaugh@linuxboxen.org>
96 * - v0.6 Correct brightness controls to reflect reality
97 * based on information gleaned by Hiroshi Miura
98 * and discussions with Hiroshi Miura
99 *
100 * Aug.10, 2004 Hiroshi Miura <miura@da-cha.org>
101 * - v0.5 support LCD brightness control
102 * based on the disclosed information by MEI.
103 *
104 * Jul.25, 2004 Hiroshi Miura <miura@da-cha.org>
105 * - v0.4 first post version
106 * add function to retrive SIFR
107 *
108 * Jul.24, 2004 Hiroshi Miura <miura@da-cha.org>
109 * - v0.3 get proper status of hotkey
110 *
111 * Jul.22, 2004 Hiroshi Miura <miura@da-cha.org>
112 * - v0.2 add HotKey handler
113 *
114 * Jul.17, 2004 Hiroshi Miura <miura@da-cha.org>
115 * - v0.1 start from toshiba_acpi driver written by John Belmonte
116 *
117 */
118
119#include <linux/kernel.h>
120#include <linux/module.h>
121#include <linux/init.h>
122#include <linux/types.h>
123#include <linux/backlight.h>
124#include <linux/ctype.h>
125#include <linux/seq_file.h>
126#include <linux/uaccess.h>
127#include <acpi/acpi_bus.h>
128#include <acpi/acpi_drivers.h>
129#include <linux/input.h>
130
131
132#ifndef ACPI_HOTKEY_COMPONENT
133#define ACPI_HOTKEY_COMPONENT 0x10000000
134#endif
135
136#define _COMPONENT ACPI_HOTKEY_COMPONENT
137
138MODULE_AUTHOR("Hiroshi Miura, David Bronaugh and Harald Welte");
139MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops");
140MODULE_LICENSE("GPL");
141
142#define LOGPREFIX "pcc_acpi: "
143
144/* Define ACPI PATHs */
145/* Lets note hotkeys */
146#define METHOD_HKEY_QUERY "HINF"
147#define METHOD_HKEY_SQTY "SQTY"
148#define METHOD_HKEY_SINF "SINF"
149#define METHOD_HKEY_SSET "SSET"
150#define HKEY_NOTIFY 0x80
151
152#define ACPI_PCC_DRIVER_NAME "Panasonic Laptop Support"
153#define ACPI_PCC_DEVICE_NAME "Hotkey"
154#define ACPI_PCC_CLASS "pcc"
155
156#define ACPI_PCC_INPUT_PHYS "panasonic/hkey0"
157
158/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
159 ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
160*/
161enum SINF_BITS { SINF_NUM_BATTERIES = 0,
162 SINF_LCD_TYPE,
163 SINF_AC_MAX_BRIGHT,
164 SINF_AC_MIN_BRIGHT,
165 SINF_AC_CUR_BRIGHT,
166 SINF_DC_MAX_BRIGHT,
167 SINF_DC_MIN_BRIGHT,
168 SINF_DC_CUR_BRIGHT,
169 SINF_MUTE,
170 SINF_RESERVED,
171 SINF_ENV_STATE,
172 SINF_STICKY_KEY = 0x80,
173 };
174/* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
175
176static int acpi_pcc_hotkey_add(struct acpi_device *device);
177static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type);
178static int acpi_pcc_hotkey_resume(struct acpi_device *device);
179
180static const struct acpi_device_id pcc_device_ids[] = {
181 { "MAT0012", 0},
182 { "MAT0013", 0},
183 { "MAT0018", 0},
184 { "MAT0019", 0},
185 { "", 0},
186};
187
188static struct acpi_driver acpi_pcc_driver = {
189 .name = ACPI_PCC_DRIVER_NAME,
190 .class = ACPI_PCC_CLASS,
191 .ids = pcc_device_ids,
192 .ops = {
193 .add = acpi_pcc_hotkey_add,
194 .remove = acpi_pcc_hotkey_remove,
195 .resume = acpi_pcc_hotkey_resume,
196 },
197};
198
199#define KEYMAP_SIZE 11
200static const int initial_keymap[KEYMAP_SIZE] = {
201 /* 0 */ KEY_RESERVED,
202 /* 1 */ KEY_BRIGHTNESSDOWN,
203 /* 2 */ KEY_BRIGHTNESSUP,
204 /* 3 */ KEY_DISPLAYTOGGLE,
205 /* 4 */ KEY_MUTE,
206 /* 5 */ KEY_VOLUMEDOWN,
207 /* 6 */ KEY_VOLUMEUP,
208 /* 7 */ KEY_SLEEP,
209 /* 8 */ KEY_PROG1, /* Change CPU boost */
210 /* 9 */ KEY_BATTERY,
211 /* 10 */ KEY_SUSPEND,
212};
213
214struct pcc_acpi {
215 acpi_handle handle;
216 unsigned long num_sifr;
217 int sticky_mode;
218 u32 *sinf;
219 struct acpi_device *device;
220 struct input_dev *input_dev;
221 struct backlight_device *backlight;
222 int keymap[KEYMAP_SIZE];
223};
224
225struct pcc_keyinput {
226 struct acpi_hotkey *hotkey;
227};
228
229/* method access functions */
230static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)
231{
232 union acpi_object in_objs[] = {
233 { .integer.type = ACPI_TYPE_INTEGER,
234 .integer.value = func, },
235 { .integer.type = ACPI_TYPE_INTEGER,
236 .integer.value = val, },
237 };
238 struct acpi_object_list params = {
239 .count = ARRAY_SIZE(in_objs),
240 .pointer = in_objs,
241 };
242 acpi_status status = AE_OK;
243
244 ACPI_FUNCTION_TRACE("acpi_pcc_write_sset");
245
246 status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET,
247 &params, NULL);
248
249 return status == AE_OK;
250}
251
252static inline int acpi_pcc_get_sqty(struct acpi_device *device)
253{
254 unsigned long long s;
255 acpi_status status;
256
257 ACPI_FUNCTION_TRACE("acpi_pcc_get_sqty");
258
259 status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,
260 NULL, &s);
261 if (ACPI_SUCCESS(status))
262 return s;
263 else {
264 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
265 "evaluation error HKEY.SQTY\n"));
266 return -EINVAL;
267 }
268}
269
270static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
271{
272 acpi_status status;
273 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
274 union acpi_object *hkey = NULL;
275 int i;
276
277 ACPI_FUNCTION_TRACE("acpi_pcc_retrieve_biosdata");
278
279 status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, 0,
280 &buffer);
281 if (ACPI_FAILURE(status)) {
282 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
283 "evaluation error HKEY.SINF\n"));
284 return 0;
285 }
286
287 hkey = buffer.pointer;
288 if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
289 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
290 goto end;
291 }
292
293 if (pcc->num_sifr < hkey->package.count) {
294 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
295 "SQTY reports bad SINF length\n"));
296 status = AE_ERROR;
297 goto end;
298 }
299
300 for (i = 0; i < hkey->package.count; i++) {
301 union acpi_object *element = &(hkey->package.elements[i]);
302 if (likely(element->type == ACPI_TYPE_INTEGER)) {
303 sinf[i] = element->integer.value;
304 } else
305 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
306 "Invalid HKEY.SINF data\n"));
307 }
308 sinf[hkey->package.count] = -1;
309
310end:
311 kfree(buffer.pointer);
312 return status == AE_OK;
313}
314
315/* backlight API interface functions */
316
317/* This driver currently treats AC and DC brightness identical,
318 * since we don't need to invent an interface to the core ACPI
319 * logic to receive events in case a power supply is plugged in
320 * or removed */
321
322static int bl_get(struct backlight_device *bd)
323{
324 struct pcc_acpi *pcc = bl_get_data(bd);
325
326 if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
327 return -EIO;
328
329 return pcc->sinf[SINF_AC_CUR_BRIGHT];
330}
331
332static int bl_set_status(struct backlight_device *bd)
333{
334 struct pcc_acpi *pcc = bl_get_data(bd);
335 int bright = bd->props.brightness;
336 int rc;
337
338 if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
339 return -EIO;
340
341 if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
342 bright = pcc->sinf[SINF_AC_MIN_BRIGHT];
343
344 if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT])
345 bright = pcc->sinf[SINF_DC_MIN_BRIGHT];
346
347 if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] ||
348 bright > pcc->sinf[SINF_AC_MAX_BRIGHT])
349 return -EINVAL;
350
351 rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright);
352 if (rc < 0)
353 return rc;
354
355 return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright);
356}
357
358static struct backlight_ops pcc_backlight_ops = {
359 .get_brightness = bl_get,
360 .update_status = bl_set_status,
361};
362
363
364/* sysfs user interface functions */
365
366static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
367 char *buf)
368{
369 struct acpi_device *acpi = to_acpi_device(dev);
370 struct pcc_acpi *pcc = acpi_driver_data(acpi);
371
372 if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
373 return -EIO;
374
375 return sprintf(buf, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
376}
377
378static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
379 char *buf)
380{
381 struct acpi_device *acpi = to_acpi_device(dev);
382 struct pcc_acpi *pcc = acpi_driver_data(acpi);
383
384 if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
385 return -EIO;
386
387 return sprintf(buf, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
388}
389
390static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
391 char *buf)
392{
393 struct acpi_device *acpi = to_acpi_device(dev);
394 struct pcc_acpi *pcc = acpi_driver_data(acpi);
395
396 if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
397 return -EIO;
398
399 return sprintf(buf, "%u\n", pcc->sinf[SINF_MUTE]);
400}
401
402static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
403 char *buf)
404{
405 struct acpi_device *acpi = to_acpi_device(dev);
406 struct pcc_acpi *pcc = acpi_driver_data(acpi);
407
408 if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
409 return -EIO;
410
411 return sprintf(buf, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
412}
413
414static ssize_t set_sticky(struct device *dev, struct device_attribute *attr,
415 const char *buf, size_t count)
416{
417 struct acpi_device *acpi = to_acpi_device(dev);
418 struct pcc_acpi *pcc = acpi_driver_data(acpi);
419 int val;
420
421 if (count && sscanf(buf, "%i", &val) == 1 &&
422 (val == 0 || val == 1)) {
423 acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val);
424 pcc->sticky_mode = val;
425 }
426
427 return count;
428}
429
430static DEVICE_ATTR(numbatt, S_IRUGO, show_numbatt, NULL);
431static DEVICE_ATTR(lcdtype, S_IRUGO, show_lcdtype, NULL);
432static DEVICE_ATTR(mute, S_IRUGO, show_mute, NULL);
433static DEVICE_ATTR(sticky_key, S_IRUGO | S_IWUSR, show_sticky, set_sticky);
434
435static struct attribute *pcc_sysfs_entries[] = {
436 &dev_attr_numbatt.attr,
437 &dev_attr_lcdtype.attr,
438 &dev_attr_mute.attr,
439 &dev_attr_sticky_key.attr,
440 NULL,
441};
442
443static struct attribute_group pcc_attr_group = {
444 .name = NULL, /* put in device directory */
445 .attrs = pcc_sysfs_entries,
446};
447
448
449/* hotkey input device driver */
450
451static int pcc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
452{
453 struct pcc_acpi *pcc = input_get_drvdata(dev);
454
455 if (scancode >= ARRAY_SIZE(pcc->keymap))
456 return -EINVAL;
457
458 *keycode = pcc->keymap[scancode];
459
460 return 0;
461}
462
463static int keymap_get_by_keycode(struct pcc_acpi *pcc, int keycode)
464{
465 int i;
466
467 for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) {
468 if (pcc->keymap[i] == keycode)
469 return i+1;
470 }
471
472 return 0;
473}
474
475static int pcc_setkeycode(struct input_dev *dev, int scancode, int keycode)
476{
477 struct pcc_acpi *pcc = input_get_drvdata(dev);
478 int oldkeycode;
479
480 if (scancode >= ARRAY_SIZE(pcc->keymap))
481 return -EINVAL;
482
483 if (keycode < 0 || keycode > KEY_MAX)
484 return -EINVAL;
485
486 oldkeycode = pcc->keymap[scancode];
487 pcc->keymap[scancode] = keycode;
488
489 set_bit(keycode, dev->keybit);
490
491 if (!keymap_get_by_keycode(pcc, oldkeycode))
492 clear_bit(oldkeycode, dev->keybit);
493
494 return 0;
495}
496
497static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
498{
499 struct input_dev *hotk_input_dev = pcc->input_dev;
500 int rc;
501 int key_code, hkey_num;
502 unsigned long long result;
503
504 ACPI_FUNCTION_TRACE("acpi_pcc_generate_keyinput");
505
506 rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
507 NULL, &result);
508 if (!ACPI_SUCCESS(rc)) {
509 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
510 "error getting hotkey status\n"));
511 return;
512 }
513
514 acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
515
516 hkey_num = result & 0xf;
517
518 if (hkey_num < 0 || hkey_num > ARRAY_SIZE(pcc->keymap)) {
519 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
520 "hotkey number out of range: %d\n",
521 hkey_num));
522 return;
523 }
524
525 key_code = pcc->keymap[hkey_num];
526
527 if (key_code != KEY_RESERVED) {
528 int pushed = (result & 0x80) ? TRUE : FALSE;
529
530 input_report_key(hotk_input_dev, key_code, pushed);
531 input_sync(hotk_input_dev);
532 }
533
534 return;
535}
536
537static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data)
538{
539 struct pcc_acpi *pcc = (struct pcc_acpi *) data;
540
541 ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_notify");
542
543 switch (event) {
544 case HKEY_NOTIFY:
545 acpi_pcc_generate_keyinput(pcc);
546 break;
547 default:
548 /* nothing to do */
549 break;
550 }
551}
552
553static int acpi_pcc_init_input(struct pcc_acpi *pcc)
554{
555 int i, rc;
556
557 ACPI_FUNCTION_TRACE("acpi_pcc_init_input");
558
559 pcc->input_dev = input_allocate_device();
560 if (!pcc->input_dev) {
561 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
562 "Couldn't allocate input device for hotkey"));
563 return -ENOMEM;
564 }
565
566 pcc->input_dev->evbit[0] = BIT(EV_KEY);
567
568 pcc->input_dev->name = ACPI_PCC_DRIVER_NAME;
569 pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS;
570 pcc->input_dev->id.bustype = BUS_HOST;
571 pcc->input_dev->id.vendor = 0x0001;
572 pcc->input_dev->id.product = 0x0001;
573 pcc->input_dev->id.version = 0x0100;
574 pcc->input_dev->getkeycode = pcc_getkeycode;
575 pcc->input_dev->setkeycode = pcc_setkeycode;
576
577 /* load initial keymap */
578 memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap));
579
580 for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++)
581 __set_bit(pcc->keymap[i], pcc->input_dev->keybit);
582 __clear_bit(KEY_RESERVED, pcc->input_dev->keybit);
583
584 input_set_drvdata(pcc->input_dev, pcc);
585
586 rc = input_register_device(pcc->input_dev);
587 if (rc < 0)
588 input_free_device(pcc->input_dev);
589
590 return rc;
591}
592
593/* kernel module interface */
594
595static int acpi_pcc_hotkey_resume(struct acpi_device *device)
596{
597 struct pcc_acpi *pcc = acpi_driver_data(device);
598 acpi_status status = AE_OK;
599
600 ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_resume");
601
602 if (device == NULL || pcc == NULL)
603 return -EINVAL;
604
605 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n",
606 pcc->sticky_mode));
607
608 status = acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
609
610 return status == AE_OK ? 0 : -EINVAL;
611}
612
613static int acpi_pcc_hotkey_add(struct acpi_device *device)
614{
615 acpi_status status;
616 struct pcc_acpi *pcc;
617 int num_sifr, result;
618
619 ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_add");
620
621 if (!device)
622 return -EINVAL;
623
624 num_sifr = acpi_pcc_get_sqty(device);
625
626 if (num_sifr > 255) {
627 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr too large"));
628 return -ENODEV;
629 }
630
631 pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL);
632 if (!pcc) {
633 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
634 "Couldn't allocate mem for pcc"));
635 return -ENOMEM;
636 }
637
638 pcc->sinf = kzalloc(sizeof(u32) * (num_sifr + 1), GFP_KERNEL);
639 if (!pcc->sinf) {
640 result = -ENOMEM;
641 goto out_hotkey;
642 }
643
644 pcc->device = device;
645 pcc->handle = device->handle;
646 pcc->num_sifr = num_sifr;
647 device->driver_data = pcc;
648 strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);
649 strcpy(acpi_device_class(device), ACPI_PCC_CLASS);
650
651 result = acpi_pcc_init_input(pcc);
652 if (result) {
653 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
654 "Error installing keyinput handler\n"));
655 goto out_sinf;
656 }
657
658 /* initialize hotkey input device */
659 status = acpi_install_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
660 acpi_pcc_hotkey_notify, pcc);
661
662 if (ACPI_FAILURE(status)) {
663 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
664 "Error installing notify handler\n"));
665 result = -ENODEV;
666 goto out_input;
667 }
668
669 /* initialize backlight */
670 pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
671 &pcc_backlight_ops);
672 if (IS_ERR(pcc->backlight))
673 goto out_notify;
674
675 if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) {
676 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
677 "Couldn't retrieve BIOS data\n"));
678 goto out_backlight;
679 }
680
681 /* read the initial brightness setting from the hardware */
682 pcc->backlight->props.max_brightness =
683 pcc->sinf[SINF_AC_MAX_BRIGHT];
684 pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
685
686 /* read the initial sticky key mode from the hardware */
687 pcc->sticky_mode = pcc->sinf[SINF_STICKY_KEY];
688
689 /* add sysfs attributes */
690 result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);
691 if (result)
692 goto out_backlight;
693
694 return 0;
695
696out_backlight:
697 backlight_device_unregister(pcc->backlight);
698out_notify:
699 acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
700 acpi_pcc_hotkey_notify);
701out_input:
702 input_unregister_device(pcc->input_dev);
703 /* no need to input_free_device() since core input API refcount and
704 * free()s the device */
705out_sinf:
706 kfree(pcc->sinf);
707out_hotkey:
708 kfree(pcc);
709
710 return result;
711}
712
713static int __init acpi_pcc_init(void)
714{
715 int result = 0;
716
717 ACPI_FUNCTION_TRACE("acpi_pcc_init");
718
719 if (acpi_disabled)
720 return -ENODEV;
721
722 result = acpi_bus_register_driver(&acpi_pcc_driver);
723 if (result < 0) {
724 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
725 "Error registering hotkey driver\n"));
726 return -ENODEV;
727 }
728
729 return 0;
730}
731
732static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
733{
734 struct pcc_acpi *pcc = acpi_driver_data(device);
735
736 ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_remove");
737
738 if (!device || !pcc)
739 return -EINVAL;
740
741 sysfs_remove_group(&device->dev.kobj, &pcc_attr_group);
742
743 backlight_device_unregister(pcc->backlight);
744
745 acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
746 acpi_pcc_hotkey_notify);
747
748 input_unregister_device(pcc->input_dev);
749 /* no need to input_free_device() since core input API refcount and
750 * free()s the device */
751
752 kfree(pcc->sinf);
753 kfree(pcc);
754
755 return 0;
756}
757
758static void __exit acpi_pcc_exit(void)
759{
760 ACPI_FUNCTION_TRACE("acpi_pcc_exit");
761
762 acpi_bus_unregister_driver(&acpi_pcc_driver);
763}
764
765module_init(acpi_pcc_init);
766module_exit(acpi_pcc_exit);
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
new file mode 100644
index 000000000000..571b211608d1
--- /dev/null
+++ b/drivers/platform/x86/sony-laptop.c
@@ -0,0 +1,2781 @@
1/*
2 * ACPI Sony Notebook Control Driver (SNC and SPIC)
3 *
4 * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net>
5 * Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
6 *
7 * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c
8 * which are copyrighted by their respective authors.
9 *
10 * The SNY6001 driver part is based on the sonypi driver which includes
11 * material from:
12 *
13 * Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net>
14 *
15 * Copyright (C) 2005 Narayanan R S <nars@kadamba.org>
16 *
17 * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
18 *
19 * Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
20 *
21 * Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp>
22 *
23 * Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp>
24 *
25 * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
26 *
27 * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
28 *
29 * This program is free software; you can redistribute it and/or modify
30 * it under the terms of the GNU General Public License as published by
31 * the Free Software Foundation; either version 2 of the License, or
32 * (at your option) any later version.
33 *
34 * This program is distributed in the hope that it will be useful,
35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 * GNU General Public License for more details.
38 *
39 * You should have received a copy of the GNU General Public License
40 * along with this program; if not, write to the Free Software
41 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
42 *
43 */
44
45#include <linux/kernel.h>
46#include <linux/module.h>
47#include <linux/moduleparam.h>
48#include <linux/init.h>
49#include <linux/smp_lock.h>
50#include <linux/types.h>
51#include <linux/backlight.h>
52#include <linux/platform_device.h>
53#include <linux/err.h>
54#include <linux/dmi.h>
55#include <linux/pci.h>
56#include <linux/interrupt.h>
57#include <linux/delay.h>
58#include <linux/input.h>
59#include <linux/kfifo.h>
60#include <linux/workqueue.h>
61#include <linux/acpi.h>
62#include <acpi/acpi_drivers.h>
63#include <acpi/acpi_bus.h>
64#include <asm/uaccess.h>
65#include <linux/sonypi.h>
66#include <linux/sony-laptop.h>
67#ifdef CONFIG_SONYPI_COMPAT
68#include <linux/poll.h>
69#include <linux/miscdevice.h>
70#endif
71
72#define DRV_PFX "sony-laptop: "
73#define dprintk(msg...) do { \
74 if (debug) printk(KERN_WARNING DRV_PFX msg); \
75} while (0)
76
77#define SONY_LAPTOP_DRIVER_VERSION "0.6"
78
79#define SONY_NC_CLASS "sony-nc"
80#define SONY_NC_HID "SNY5001"
81#define SONY_NC_DRIVER_NAME "Sony Notebook Control Driver"
82
83#define SONY_PIC_CLASS "sony-pic"
84#define SONY_PIC_HID "SNY6001"
85#define SONY_PIC_DRIVER_NAME "Sony Programmable IO Control Driver"
86
87MODULE_AUTHOR("Stelian Pop, Mattia Dongili");
88MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)");
89MODULE_LICENSE("GPL");
90MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION);
91
92static int debug;
93module_param(debug, int, 0);
94MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
95 "the development of this driver");
96
97static int no_spic; /* = 0 */
98module_param(no_spic, int, 0444);
99MODULE_PARM_DESC(no_spic,
100 "set this if you don't want to enable the SPIC device");
101
102static int compat; /* = 0 */
103module_param(compat, int, 0444);
104MODULE_PARM_DESC(compat,
105 "set this if you want to enable backward compatibility mode");
106
107static unsigned long mask = 0xffffffff;
108module_param(mask, ulong, 0644);
109MODULE_PARM_DESC(mask,
110 "set this to the mask of event you want to enable (see doc)");
111
112static int camera; /* = 0 */
113module_param(camera, int, 0444);
114MODULE_PARM_DESC(camera,
115 "set this to 1 to enable Motion Eye camera controls "
116 "(only use it if you have a C1VE or C1VN model)");
117
118#ifdef CONFIG_SONYPI_COMPAT
119static int minor = -1;
120module_param(minor, int, 0);
121MODULE_PARM_DESC(minor,
122 "minor number of the misc device for the SPIC compatibility code, "
123 "default is -1 (automatic)");
124#endif
125
126/*********** Input Devices ***********/
127
128#define SONY_LAPTOP_BUF_SIZE 128
129struct sony_laptop_input_s {
130 atomic_t users;
131 struct input_dev *jog_dev;
132 struct input_dev *key_dev;
133 struct kfifo *fifo;
134 spinlock_t fifo_lock;
135 struct workqueue_struct *wq;
136};
137static struct sony_laptop_input_s sony_laptop_input = {
138 .users = ATOMIC_INIT(0),
139};
140
141struct sony_laptop_keypress {
142 struct input_dev *dev;
143 int key;
144};
145
146/* Correspondance table between sonypi events
147 * and input layer indexes in the keymap
148 */
149static int sony_laptop_input_index[] = {
150 -1, /* 0 no event */
151 -1, /* 1 SONYPI_EVENT_JOGDIAL_DOWN */
152 -1, /* 2 SONYPI_EVENT_JOGDIAL_UP */
153 -1, /* 3 SONYPI_EVENT_JOGDIAL_DOWN_PRESSED */
154 -1, /* 4 SONYPI_EVENT_JOGDIAL_UP_PRESSED */
155 -1, /* 5 SONYPI_EVENT_JOGDIAL_PRESSED */
156 -1, /* 6 SONYPI_EVENT_JOGDIAL_RELEASED */
157 0, /* 7 SONYPI_EVENT_CAPTURE_PRESSED */
158 1, /* 8 SONYPI_EVENT_CAPTURE_RELEASED */
159 2, /* 9 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */
160 3, /* 10 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */
161 4, /* 11 SONYPI_EVENT_FNKEY_ESC */
162 5, /* 12 SONYPI_EVENT_FNKEY_F1 */
163 6, /* 13 SONYPI_EVENT_FNKEY_F2 */
164 7, /* 14 SONYPI_EVENT_FNKEY_F3 */
165 8, /* 15 SONYPI_EVENT_FNKEY_F4 */
166 9, /* 16 SONYPI_EVENT_FNKEY_F5 */
167 10, /* 17 SONYPI_EVENT_FNKEY_F6 */
168 11, /* 18 SONYPI_EVENT_FNKEY_F7 */
169 12, /* 19 SONYPI_EVENT_FNKEY_F8 */
170 13, /* 20 SONYPI_EVENT_FNKEY_F9 */
171 14, /* 21 SONYPI_EVENT_FNKEY_F10 */
172 15, /* 22 SONYPI_EVENT_FNKEY_F11 */
173 16, /* 23 SONYPI_EVENT_FNKEY_F12 */
174 17, /* 24 SONYPI_EVENT_FNKEY_1 */
175 18, /* 25 SONYPI_EVENT_FNKEY_2 */
176 19, /* 26 SONYPI_EVENT_FNKEY_D */
177 20, /* 27 SONYPI_EVENT_FNKEY_E */
178 21, /* 28 SONYPI_EVENT_FNKEY_F */
179 22, /* 29 SONYPI_EVENT_FNKEY_S */
180 23, /* 30 SONYPI_EVENT_FNKEY_B */
181 24, /* 31 SONYPI_EVENT_BLUETOOTH_PRESSED */
182 25, /* 32 SONYPI_EVENT_PKEY_P1 */
183 26, /* 33 SONYPI_EVENT_PKEY_P2 */
184 27, /* 34 SONYPI_EVENT_PKEY_P3 */
185 28, /* 35 SONYPI_EVENT_BACK_PRESSED */
186 -1, /* 36 SONYPI_EVENT_LID_CLOSED */
187 -1, /* 37 SONYPI_EVENT_LID_OPENED */
188 29, /* 38 SONYPI_EVENT_BLUETOOTH_ON */
189 30, /* 39 SONYPI_EVENT_BLUETOOTH_OFF */
190 31, /* 40 SONYPI_EVENT_HELP_PRESSED */
191 32, /* 41 SONYPI_EVENT_FNKEY_ONLY */
192 33, /* 42 SONYPI_EVENT_JOGDIAL_FAST_DOWN */
193 34, /* 43 SONYPI_EVENT_JOGDIAL_FAST_UP */
194 35, /* 44 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */
195 36, /* 45 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */
196 37, /* 46 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */
197 38, /* 47 SONYPI_EVENT_JOGDIAL_VFAST_UP */
198 39, /* 48 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */
199 40, /* 49 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */
200 41, /* 50 SONYPI_EVENT_ZOOM_PRESSED */
201 42, /* 51 SONYPI_EVENT_THUMBPHRASE_PRESSED */
202 43, /* 52 SONYPI_EVENT_MEYE_FACE */
203 44, /* 53 SONYPI_EVENT_MEYE_OPPOSITE */
204 45, /* 54 SONYPI_EVENT_MEMORYSTICK_INSERT */
205 46, /* 55 SONYPI_EVENT_MEMORYSTICK_EJECT */
206 -1, /* 56 SONYPI_EVENT_ANYBUTTON_RELEASED */
207 -1, /* 57 SONYPI_EVENT_BATTERY_INSERT */
208 -1, /* 58 SONYPI_EVENT_BATTERY_REMOVE */
209 -1, /* 59 SONYPI_EVENT_FNKEY_RELEASED */
210 47, /* 60 SONYPI_EVENT_WIRELESS_ON */
211 48, /* 61 SONYPI_EVENT_WIRELESS_OFF */
212 49, /* 62 SONYPI_EVENT_ZOOM_IN_PRESSED */
213 50, /* 63 SONYPI_EVENT_ZOOM_OUT_PRESSED */
214};
215
216static int sony_laptop_input_keycode_map[] = {
217 KEY_CAMERA, /* 0 SONYPI_EVENT_CAPTURE_PRESSED */
218 KEY_RESERVED, /* 1 SONYPI_EVENT_CAPTURE_RELEASED */
219 KEY_RESERVED, /* 2 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */
220 KEY_RESERVED, /* 3 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */
221 KEY_FN_ESC, /* 4 SONYPI_EVENT_FNKEY_ESC */
222 KEY_FN_F1, /* 5 SONYPI_EVENT_FNKEY_F1 */
223 KEY_FN_F2, /* 6 SONYPI_EVENT_FNKEY_F2 */
224 KEY_FN_F3, /* 7 SONYPI_EVENT_FNKEY_F3 */
225 KEY_FN_F4, /* 8 SONYPI_EVENT_FNKEY_F4 */
226 KEY_FN_F5, /* 9 SONYPI_EVENT_FNKEY_F5 */
227 KEY_FN_F6, /* 10 SONYPI_EVENT_FNKEY_F6 */
228 KEY_FN_F7, /* 11 SONYPI_EVENT_FNKEY_F7 */
229 KEY_FN_F8, /* 12 SONYPI_EVENT_FNKEY_F8 */
230 KEY_FN_F9, /* 13 SONYPI_EVENT_FNKEY_F9 */
231 KEY_FN_F10, /* 14 SONYPI_EVENT_FNKEY_F10 */
232 KEY_FN_F11, /* 15 SONYPI_EVENT_FNKEY_F11 */
233 KEY_FN_F12, /* 16 SONYPI_EVENT_FNKEY_F12 */
234 KEY_FN_F1, /* 17 SONYPI_EVENT_FNKEY_1 */
235 KEY_FN_F2, /* 18 SONYPI_EVENT_FNKEY_2 */
236 KEY_FN_D, /* 19 SONYPI_EVENT_FNKEY_D */
237 KEY_FN_E, /* 20 SONYPI_EVENT_FNKEY_E */
238 KEY_FN_F, /* 21 SONYPI_EVENT_FNKEY_F */
239 KEY_FN_S, /* 22 SONYPI_EVENT_FNKEY_S */
240 KEY_FN_B, /* 23 SONYPI_EVENT_FNKEY_B */
241 KEY_BLUETOOTH, /* 24 SONYPI_EVENT_BLUETOOTH_PRESSED */
242 KEY_PROG1, /* 25 SONYPI_EVENT_PKEY_P1 */
243 KEY_PROG2, /* 26 SONYPI_EVENT_PKEY_P2 */
244 KEY_PROG3, /* 27 SONYPI_EVENT_PKEY_P3 */
245 KEY_BACK, /* 28 SONYPI_EVENT_BACK_PRESSED */
246 KEY_BLUETOOTH, /* 29 SONYPI_EVENT_BLUETOOTH_ON */
247 KEY_BLUETOOTH, /* 30 SONYPI_EVENT_BLUETOOTH_OFF */
248 KEY_HELP, /* 31 SONYPI_EVENT_HELP_PRESSED */
249 KEY_FN, /* 32 SONYPI_EVENT_FNKEY_ONLY */
250 KEY_RESERVED, /* 33 SONYPI_EVENT_JOGDIAL_FAST_DOWN */
251 KEY_RESERVED, /* 34 SONYPI_EVENT_JOGDIAL_FAST_UP */
252 KEY_RESERVED, /* 35 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */
253 KEY_RESERVED, /* 36 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */
254 KEY_RESERVED, /* 37 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */
255 KEY_RESERVED, /* 38 SONYPI_EVENT_JOGDIAL_VFAST_UP */
256 KEY_RESERVED, /* 39 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */
257 KEY_RESERVED, /* 40 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */
258 KEY_ZOOM, /* 41 SONYPI_EVENT_ZOOM_PRESSED */
259 BTN_THUMB, /* 42 SONYPI_EVENT_THUMBPHRASE_PRESSED */
260 KEY_RESERVED, /* 43 SONYPI_EVENT_MEYE_FACE */
261 KEY_RESERVED, /* 44 SONYPI_EVENT_MEYE_OPPOSITE */
262 KEY_RESERVED, /* 45 SONYPI_EVENT_MEMORYSTICK_INSERT */
263 KEY_RESERVED, /* 46 SONYPI_EVENT_MEMORYSTICK_EJECT */
264 KEY_WLAN, /* 47 SONYPI_EVENT_WIRELESS_ON */
265 KEY_WLAN, /* 48 SONYPI_EVENT_WIRELESS_OFF */
266 KEY_ZOOMIN, /* 49 SONYPI_EVENT_ZOOM_IN_PRESSED */
267 KEY_ZOOMOUT /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */
268};
269
270/* release buttons after a short delay if pressed */
271static void do_sony_laptop_release_key(struct work_struct *work)
272{
273 struct sony_laptop_keypress kp;
274
275 while (kfifo_get(sony_laptop_input.fifo, (unsigned char *)&kp,
276 sizeof(kp)) == sizeof(kp)) {
277 msleep(10);
278 input_report_key(kp.dev, kp.key, 0);
279 input_sync(kp.dev);
280 }
281}
282static DECLARE_WORK(sony_laptop_release_key_work,
283 do_sony_laptop_release_key);
284
285/* forward event to the input subsystem */
286static void sony_laptop_report_input_event(u8 event)
287{
288 struct input_dev *jog_dev = sony_laptop_input.jog_dev;
289 struct input_dev *key_dev = sony_laptop_input.key_dev;
290 struct sony_laptop_keypress kp = { NULL };
291
292 if (event == SONYPI_EVENT_FNKEY_RELEASED) {
293 /* Nothing, not all VAIOs generate this event */
294 return;
295 }
296
297 /* report events */
298 switch (event) {
299 /* jog_dev events */
300 case SONYPI_EVENT_JOGDIAL_UP:
301 case SONYPI_EVENT_JOGDIAL_UP_PRESSED:
302 input_report_rel(jog_dev, REL_WHEEL, 1);
303 input_sync(jog_dev);
304 return;
305
306 case SONYPI_EVENT_JOGDIAL_DOWN:
307 case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED:
308 input_report_rel(jog_dev, REL_WHEEL, -1);
309 input_sync(jog_dev);
310 return;
311
312 /* key_dev events */
313 case SONYPI_EVENT_JOGDIAL_PRESSED:
314 kp.key = BTN_MIDDLE;
315 kp.dev = jog_dev;
316 break;
317
318 default:
319 if (event >= ARRAY_SIZE(sony_laptop_input_index)) {
320 dprintk("sony_laptop_report_input_event, event not known: %d\n", event);
321 break;
322 }
323 if (sony_laptop_input_index[event] != -1) {
324 kp.key = sony_laptop_input_keycode_map[sony_laptop_input_index[event]];
325 if (kp.key != KEY_UNKNOWN)
326 kp.dev = key_dev;
327 }
328 break;
329 }
330
331 if (kp.dev) {
332 input_report_key(kp.dev, kp.key, 1);
333 /* we emit the scancode so we can always remap the key */
334 input_event(kp.dev, EV_MSC, MSC_SCAN, event);
335 input_sync(kp.dev);
336 kfifo_put(sony_laptop_input.fifo,
337 (unsigned char *)&kp, sizeof(kp));
338
339 if (!work_pending(&sony_laptop_release_key_work))
340 queue_work(sony_laptop_input.wq,
341 &sony_laptop_release_key_work);
342 } else
343 dprintk("unknown input event %.2x\n", event);
344}
345
346static int sony_laptop_setup_input(struct acpi_device *acpi_device)
347{
348 struct input_dev *jog_dev;
349 struct input_dev *key_dev;
350 int i;
351 int error;
352
353 /* don't run again if already initialized */
354 if (atomic_add_return(1, &sony_laptop_input.users) > 1)
355 return 0;
356
357 /* kfifo */
358 spin_lock_init(&sony_laptop_input.fifo_lock);
359 sony_laptop_input.fifo =
360 kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
361 &sony_laptop_input.fifo_lock);
362 if (IS_ERR(sony_laptop_input.fifo)) {
363 printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
364 error = PTR_ERR(sony_laptop_input.fifo);
365 goto err_dec_users;
366 }
367
368 /* init workqueue */
369 sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop");
370 if (!sony_laptop_input.wq) {
371 printk(KERN_ERR DRV_PFX
372 "Unabe to create workqueue.\n");
373 error = -ENXIO;
374 goto err_free_kfifo;
375 }
376
377 /* input keys */
378 key_dev = input_allocate_device();
379 if (!key_dev) {
380 error = -ENOMEM;
381 goto err_destroy_wq;
382 }
383
384 key_dev->name = "Sony Vaio Keys";
385 key_dev->id.bustype = BUS_ISA;
386 key_dev->id.vendor = PCI_VENDOR_ID_SONY;
387 key_dev->dev.parent = &acpi_device->dev;
388
389 /* Initialize the Input Drivers: special keys */
390 set_bit(EV_KEY, key_dev->evbit);
391 set_bit(EV_MSC, key_dev->evbit);
392 set_bit(MSC_SCAN, key_dev->mscbit);
393 key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]);
394 key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map);
395 key_dev->keycode = &sony_laptop_input_keycode_map;
396 for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) {
397 if (sony_laptop_input_keycode_map[i] != KEY_RESERVED) {
398 set_bit(sony_laptop_input_keycode_map[i],
399 key_dev->keybit);
400 }
401 }
402
403 error = input_register_device(key_dev);
404 if (error)
405 goto err_free_keydev;
406
407 sony_laptop_input.key_dev = key_dev;
408
409 /* jogdial */
410 jog_dev = input_allocate_device();
411 if (!jog_dev) {
412 error = -ENOMEM;
413 goto err_unregister_keydev;
414 }
415
416 jog_dev->name = "Sony Vaio Jogdial";
417 jog_dev->id.bustype = BUS_ISA;
418 jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
419 key_dev->dev.parent = &acpi_device->dev;
420
421 jog_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
422 jog_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_MIDDLE);
423 jog_dev->relbit[0] = BIT_MASK(REL_WHEEL);
424
425 error = input_register_device(jog_dev);
426 if (error)
427 goto err_free_jogdev;
428
429 sony_laptop_input.jog_dev = jog_dev;
430
431 return 0;
432
433err_free_jogdev:
434 input_free_device(jog_dev);
435
436err_unregister_keydev:
437 input_unregister_device(key_dev);
438 /* to avoid kref underflow below at input_free_device */
439 key_dev = NULL;
440
441err_free_keydev:
442 input_free_device(key_dev);
443
444err_destroy_wq:
445 destroy_workqueue(sony_laptop_input.wq);
446
447err_free_kfifo:
448 kfifo_free(sony_laptop_input.fifo);
449
450err_dec_users:
451 atomic_dec(&sony_laptop_input.users);
452 return error;
453}
454
455static void sony_laptop_remove_input(void)
456{
457 /* cleanup only after the last user has gone */
458 if (!atomic_dec_and_test(&sony_laptop_input.users))
459 return;
460
461 /* flush workqueue first */
462 flush_workqueue(sony_laptop_input.wq);
463
464 /* destroy input devs */
465 input_unregister_device(sony_laptop_input.key_dev);
466 sony_laptop_input.key_dev = NULL;
467
468 if (sony_laptop_input.jog_dev) {
469 input_unregister_device(sony_laptop_input.jog_dev);
470 sony_laptop_input.jog_dev = NULL;
471 }
472
473 destroy_workqueue(sony_laptop_input.wq);
474 kfifo_free(sony_laptop_input.fifo);
475}
476
477/*********** Platform Device ***********/
478
479static atomic_t sony_pf_users = ATOMIC_INIT(0);
480static struct platform_driver sony_pf_driver = {
481 .driver = {
482 .name = "sony-laptop",
483 .owner = THIS_MODULE,
484 }
485};
486static struct platform_device *sony_pf_device;
487
488static int sony_pf_add(void)
489{
490 int ret = 0;
491
492 /* don't run again if already initialized */
493 if (atomic_add_return(1, &sony_pf_users) > 1)
494 return 0;
495
496 ret = platform_driver_register(&sony_pf_driver);
497 if (ret)
498 goto out;
499
500 sony_pf_device = platform_device_alloc("sony-laptop", -1);
501 if (!sony_pf_device) {
502 ret = -ENOMEM;
503 goto out_platform_registered;
504 }
505
506 ret = platform_device_add(sony_pf_device);
507 if (ret)
508 goto out_platform_alloced;
509
510 return 0;
511
512 out_platform_alloced:
513 platform_device_put(sony_pf_device);
514 sony_pf_device = NULL;
515 out_platform_registered:
516 platform_driver_unregister(&sony_pf_driver);
517 out:
518 atomic_dec(&sony_pf_users);
519 return ret;
520}
521
522static void sony_pf_remove(void)
523{
524 /* deregister only after the last user has gone */
525 if (!atomic_dec_and_test(&sony_pf_users))
526 return;
527
528 platform_device_del(sony_pf_device);
529 platform_device_put(sony_pf_device);
530 platform_driver_unregister(&sony_pf_driver);
531}
532
533/*********** SNC (SNY5001) Device ***********/
534
535/* the device uses 1-based values, while the backlight subsystem uses
536 0-based values */
537#define SONY_MAX_BRIGHTNESS 8
538
539#define SNC_VALIDATE_IN 0
540#define SNC_VALIDATE_OUT 1
541
542static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *,
543 char *);
544static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *,
545 const char *, size_t);
546static int boolean_validate(const int, const int);
547static int brightness_default_validate(const int, const int);
548
549struct sony_nc_value {
550 char *name; /* name of the entry */
551 char **acpiget; /* names of the ACPI get function */
552 char **acpiset; /* names of the ACPI set function */
553 int (*validate)(const int, const int); /* input/output validation */
554 int value; /* current setting */
555 int valid; /* Has ever been set */
556 int debug; /* active only in debug mode ? */
557 struct device_attribute devattr; /* sysfs atribute */
558};
559
560#define SNC_HANDLE_NAMES(_name, _values...) \
561 static char *snc_##_name[] = { _values, NULL }
562
563#define SNC_HANDLE(_name, _getters, _setters, _validate, _debug) \
564 { \
565 .name = __stringify(_name), \
566 .acpiget = _getters, \
567 .acpiset = _setters, \
568 .validate = _validate, \
569 .debug = _debug, \
570 .devattr = __ATTR(_name, 0, sony_nc_sysfs_show, sony_nc_sysfs_store), \
571 }
572
573#define SNC_HANDLE_NULL { .name = NULL }
574
575SNC_HANDLE_NAMES(fnkey_get, "GHKE");
576
577SNC_HANDLE_NAMES(brightness_def_get, "GPBR");
578SNC_HANDLE_NAMES(brightness_def_set, "SPBR");
579
580SNC_HANDLE_NAMES(cdpower_get, "GCDP");
581SNC_HANDLE_NAMES(cdpower_set, "SCDP", "CDPW");
582
583SNC_HANDLE_NAMES(audiopower_get, "GAZP");
584SNC_HANDLE_NAMES(audiopower_set, "AZPW");
585
586SNC_HANDLE_NAMES(lanpower_get, "GLNP");
587SNC_HANDLE_NAMES(lanpower_set, "LNPW");
588
589SNC_HANDLE_NAMES(lidstate_get, "GLID");
590
591SNC_HANDLE_NAMES(indicatorlamp_get, "GILS");
592SNC_HANDLE_NAMES(indicatorlamp_set, "SILS");
593
594SNC_HANDLE_NAMES(gainbass_get, "GMGB");
595SNC_HANDLE_NAMES(gainbass_set, "CMGB");
596
597SNC_HANDLE_NAMES(PID_get, "GPID");
598
599SNC_HANDLE_NAMES(CTR_get, "GCTR");
600SNC_HANDLE_NAMES(CTR_set, "SCTR");
601
602SNC_HANDLE_NAMES(PCR_get, "GPCR");
603SNC_HANDLE_NAMES(PCR_set, "SPCR");
604
605SNC_HANDLE_NAMES(CMI_get, "GCMI");
606SNC_HANDLE_NAMES(CMI_set, "SCMI");
607
608static struct sony_nc_value sony_nc_values[] = {
609 SNC_HANDLE(brightness_default, snc_brightness_def_get,
610 snc_brightness_def_set, brightness_default_validate, 0),
611 SNC_HANDLE(fnkey, snc_fnkey_get, NULL, NULL, 0),
612 SNC_HANDLE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0),
613 SNC_HANDLE(audiopower, snc_audiopower_get, snc_audiopower_set,
614 boolean_validate, 0),
615 SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set,
616 boolean_validate, 1),
617 SNC_HANDLE(lidstate, snc_lidstate_get, NULL,
618 boolean_validate, 0),
619 SNC_HANDLE(indicatorlamp, snc_indicatorlamp_get, snc_indicatorlamp_set,
620 boolean_validate, 0),
621 SNC_HANDLE(gainbass, snc_gainbass_get, snc_gainbass_set,
622 boolean_validate, 0),
623 /* unknown methods */
624 SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1),
625 SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),
626 SNC_HANDLE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1),
627 SNC_HANDLE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1),
628 SNC_HANDLE_NULL
629};
630
631static acpi_handle sony_nc_acpi_handle;
632static struct acpi_device *sony_nc_acpi_device = NULL;
633
634/*
635 * acpi_evaluate_object wrappers
636 */
637static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
638{
639 struct acpi_buffer output;
640 union acpi_object out_obj;
641 acpi_status status;
642
643 output.length = sizeof(out_obj);
644 output.pointer = &out_obj;
645
646 status = acpi_evaluate_object(handle, name, NULL, &output);
647 if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
648 *result = out_obj.integer.value;
649 return 0;
650 }
651
652 printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n");
653
654 return -1;
655}
656
657static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
658 int *result)
659{
660 struct acpi_object_list params;
661 union acpi_object in_obj;
662 struct acpi_buffer output;
663 union acpi_object out_obj;
664 acpi_status status;
665
666 params.count = 1;
667 params.pointer = &in_obj;
668 in_obj.type = ACPI_TYPE_INTEGER;
669 in_obj.integer.value = value;
670
671 output.length = sizeof(out_obj);
672 output.pointer = &out_obj;
673
674 status = acpi_evaluate_object(handle, name, &params, &output);
675 if (status == AE_OK) {
676 if (result != NULL) {
677 if (out_obj.type != ACPI_TYPE_INTEGER) {
678 printk(KERN_WARNING DRV_PFX "acpi_evaluate_object bad "
679 "return type\n");
680 return -1;
681 }
682 *result = out_obj.integer.value;
683 }
684 return 0;
685 }
686
687 printk(KERN_WARNING DRV_PFX "acpi_evaluate_object failed\n");
688
689 return -1;
690}
691
692/*
693 * sony_nc_values input/output validate functions
694 */
695
696/* brightness_default_validate:
697 *
698 * manipulate input output values to keep consistency with the
699 * backlight framework for which brightness values are 0-based.
700 */
701static int brightness_default_validate(const int direction, const int value)
702{
703 switch (direction) {
704 case SNC_VALIDATE_OUT:
705 return value - 1;
706 case SNC_VALIDATE_IN:
707 if (value >= 0 && value < SONY_MAX_BRIGHTNESS)
708 return value + 1;
709 }
710 return -EINVAL;
711}
712
713/* boolean_validate:
714 *
715 * on input validate boolean values 0/1, on output just pass the
716 * received value.
717 */
718static int boolean_validate(const int direction, const int value)
719{
720 if (direction == SNC_VALIDATE_IN) {
721 if (value != 0 && value != 1)
722 return -EINVAL;
723 }
724 return value;
725}
726
727/*
728 * Sysfs show/store common to all sony_nc_values
729 */
730static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr,
731 char *buffer)
732{
733 int value;
734 struct sony_nc_value *item =
735 container_of(attr, struct sony_nc_value, devattr);
736
737 if (!*item->acpiget)
738 return -EIO;
739
740 if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0)
741 return -EIO;
742
743 if (item->validate)
744 value = item->validate(SNC_VALIDATE_OUT, value);
745
746 return snprintf(buffer, PAGE_SIZE, "%d\n", value);
747}
748
749static ssize_t sony_nc_sysfs_store(struct device *dev,
750 struct device_attribute *attr,
751 const char *buffer, size_t count)
752{
753 int value;
754 struct sony_nc_value *item =
755 container_of(attr, struct sony_nc_value, devattr);
756
757 if (!item->acpiset)
758 return -EIO;
759
760 if (count > 31)
761 return -EINVAL;
762
763 value = simple_strtoul(buffer, NULL, 10);
764
765 if (item->validate)
766 value = item->validate(SNC_VALIDATE_IN, value);
767
768 if (value < 0)
769 return value;
770
771 if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0)
772 return -EIO;
773 item->value = value;
774 item->valid = 1;
775 return count;
776}
777
778
779/*
780 * Backlight device
781 */
782static int sony_backlight_update_status(struct backlight_device *bd)
783{
784 return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
785 bd->props.brightness + 1, NULL);
786}
787
788static int sony_backlight_get_brightness(struct backlight_device *bd)
789{
790 int value;
791
792 if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value))
793 return 0;
794 /* brightness levels are 1-based, while backlight ones are 0-based */
795 return value - 1;
796}
797
798static struct backlight_device *sony_backlight_device;
799static struct backlight_ops sony_backlight_ops = {
800 .update_status = sony_backlight_update_status,
801 .get_brightness = sony_backlight_get_brightness,
802};
803
804/*
805 * New SNC-only Vaios event mapping to driver known keys
806 */
807struct sony_nc_event {
808 u8 data;
809 u8 event;
810};
811
812static struct sony_nc_event *sony_nc_events;
813
814/* Vaio C* --maybe also FE*, N* and AR* ?-- special init sequence
815 * for Fn keys
816 */
817static int sony_nc_C_enable(const struct dmi_system_id *id)
818{
819 int result = 0;
820
821 printk(KERN_NOTICE DRV_PFX "detected %s\n", id->ident);
822
823 sony_nc_events = id->driver_data;
824
825 if (acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x4, &result) < 0
826 || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x2, &result) < 0
827 || acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x10, &result) < 0
828 || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x0, &result) < 0
829 || acpi_callsetfunc(sony_nc_acpi_handle, "SN03", 0x2, &result) < 0
830 || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x101, &result) < 0) {
831 printk(KERN_WARNING DRV_PFX "failed to initialize SNC, some "
832 "functionalities may be missing\n");
833 return 1;
834 }
835 return 0;
836}
837
838static struct sony_nc_event sony_C_events[] = {
839 { 0x81, SONYPI_EVENT_FNKEY_F1 },
840 { 0x01, SONYPI_EVENT_FNKEY_RELEASED },
841 { 0x85, SONYPI_EVENT_FNKEY_F5 },
842 { 0x05, SONYPI_EVENT_FNKEY_RELEASED },
843 { 0x86, SONYPI_EVENT_FNKEY_F6 },
844 { 0x06, SONYPI_EVENT_FNKEY_RELEASED },
845 { 0x87, SONYPI_EVENT_FNKEY_F7 },
846 { 0x07, SONYPI_EVENT_FNKEY_RELEASED },
847 { 0x8A, SONYPI_EVENT_FNKEY_F10 },
848 { 0x0A, SONYPI_EVENT_FNKEY_RELEASED },
849 { 0x8C, SONYPI_EVENT_FNKEY_F12 },
850 { 0x0C, SONYPI_EVENT_FNKEY_RELEASED },
851 { 0, 0 },
852};
853
854/* SNC-only model map */
855static const struct dmi_system_id sony_nc_ids[] = {
856 {
857 .ident = "Sony Vaio FE Series",
858 .callback = sony_nc_C_enable,
859 .driver_data = sony_C_events,
860 .matches = {
861 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
862 DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FE"),
863 },
864 },
865 {
866 .ident = "Sony Vaio FZ Series",
867 .callback = sony_nc_C_enable,
868 .driver_data = sony_C_events,
869 .matches = {
870 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
871 DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ"),
872 },
873 },
874 {
875 .ident = "Sony Vaio C Series",
876 .callback = sony_nc_C_enable,
877 .driver_data = sony_C_events,
878 .matches = {
879 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
880 DMI_MATCH(DMI_PRODUCT_NAME, "VGN-C"),
881 },
882 },
883 {
884 .ident = "Sony Vaio N Series",
885 .callback = sony_nc_C_enable,
886 .driver_data = sony_C_events,
887 .matches = {
888 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
889 DMI_MATCH(DMI_PRODUCT_NAME, "VGN-N"),
890 },
891 },
892 { }
893};
894
895/*
896 * ACPI callbacks
897 */
898static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
899{
900 struct sony_nc_event *evmap;
901 u32 ev = event;
902 int result;
903
904 if (ev == 0x92) {
905 /* read the key pressed from EC.GECR
906 * A call to SN07 with 0x0202 will do it as well respecting
907 * the current protocol on different OSes
908 *
909 * Note: the path for GECR may be
910 * \_SB.PCI0.LPCB.EC (C, FE, AR, N and friends)
911 * \_SB.PCI0.PIB.EC0 (VGN-FR notifications are sent directly, no GECR)
912 *
913 * TODO: we may want to do the same for the older GHKE -need
914 * dmi list- so this snippet may become one more callback.
915 */
916 if (acpi_callsetfunc(handle, "SN07", 0x0202, &result) < 0)
917 dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev);
918 else
919 ev = result & 0xFF;
920 }
921
922 if (sony_nc_events)
923 for (evmap = sony_nc_events; evmap->event; evmap++) {
924 if (evmap->data == ev) {
925 ev = evmap->event;
926 break;
927 }
928 }
929
930 dprintk("sony_acpi_notify, event: 0x%.2x\n", ev);
931 sony_laptop_report_input_event(ev);
932 acpi_bus_generate_proc_event(sony_nc_acpi_device, 1, ev);
933}
934
935static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
936 void *context, void **return_value)
937{
938 struct acpi_namespace_node *node;
939 union acpi_operand_object *operand;
940
941 node = (struct acpi_namespace_node *)handle;
942 operand = (union acpi_operand_object *)node->object;
943
944 printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
945 (u32) operand->method.param_count);
946
947 return AE_OK;
948}
949
950/*
951 * ACPI device
952 */
953static int sony_nc_resume(struct acpi_device *device)
954{
955 struct sony_nc_value *item;
956
957 for (item = sony_nc_values; item->name; item++) {
958 int ret;
959
960 if (!item->valid)
961 continue;
962 ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset,
963 item->value, NULL);
964 if (ret < 0) {
965 printk("%s: %d\n", __func__, ret);
966 break;
967 }
968 }
969
970 /* set the last requested brightness level */
971 if (sony_backlight_device &&
972 !sony_backlight_update_status(sony_backlight_device))
973 printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n");
974
975 /* re-initialize models with specific requirements */
976 dmi_check_system(sony_nc_ids);
977
978 return 0;
979}
980
981static int sony_nc_add(struct acpi_device *device)
982{
983 acpi_status status;
984 int result = 0;
985 acpi_handle handle;
986 struct sony_nc_value *item;
987
988 printk(KERN_INFO DRV_PFX "%s v%s.\n",
989 SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
990
991 sony_nc_acpi_device = device;
992 strcpy(acpi_device_class(device), "sony/hotkey");
993
994 sony_nc_acpi_handle = device->handle;
995
996 /* read device status */
997 result = acpi_bus_get_status(device);
998 /* bail IFF the above call was successful and the device is not present */
999 if (!result && !device->status.present) {
1000 dprintk("Device not present\n");
1001 result = -ENODEV;
1002 goto outwalk;
1003 }
1004
1005 if (debug) {
1006 status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle,
1007 1, sony_walk_callback, NULL, NULL);
1008 if (ACPI_FAILURE(status)) {
1009 printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n");
1010 result = -ENODEV;
1011 goto outwalk;
1012 }
1013 }
1014
1015 /* try to _INI the device if such method exists (ACPI spec 3.0-6.5.1
1016 * should be respected as we already checked for the device presence above */
1017 if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, METHOD_NAME__INI, &handle))) {
1018 dprintk("Invoking _INI\n");
1019 if (ACPI_FAILURE(acpi_evaluate_object(sony_nc_acpi_handle, METHOD_NAME__INI,
1020 NULL, NULL)))
1021 dprintk("_INI Method failed\n");
1022 }
1023
1024 /* setup input devices and helper fifo */
1025 result = sony_laptop_setup_input(device);
1026 if (result) {
1027 printk(KERN_ERR DRV_PFX
1028 "Unabe to create input devices.\n");
1029 goto outwalk;
1030 }
1031
1032 status = acpi_install_notify_handler(sony_nc_acpi_handle,
1033 ACPI_DEVICE_NOTIFY,
1034 sony_acpi_notify, NULL);
1035 if (ACPI_FAILURE(status)) {
1036 printk(KERN_WARNING DRV_PFX "unable to install notify handler (%u)\n", status);
1037 result = -ENODEV;
1038 goto outinput;
1039 }
1040
1041 if (acpi_video_backlight_support()) {
1042 printk(KERN_INFO DRV_PFX "brightness ignored, must be "
1043 "controlled by ACPI video driver\n");
1044 } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
1045 &handle))) {
1046 sony_backlight_device = backlight_device_register("sony", NULL,
1047 NULL,
1048 &sony_backlight_ops);
1049
1050 if (IS_ERR(sony_backlight_device)) {
1051 printk(KERN_WARNING DRV_PFX "unable to register backlight device\n");
1052 sony_backlight_device = NULL;
1053 } else {
1054 sony_backlight_device->props.brightness =
1055 sony_backlight_get_brightness
1056 (sony_backlight_device);
1057 sony_backlight_device->props.max_brightness =
1058 SONY_MAX_BRIGHTNESS - 1;
1059 }
1060
1061 }
1062
1063 /* initialize models with specific requirements */
1064 dmi_check_system(sony_nc_ids);
1065
1066 result = sony_pf_add();
1067 if (result)
1068 goto outbacklight;
1069
1070 /* create sony_pf sysfs attributes related to the SNC device */
1071 for (item = sony_nc_values; item->name; ++item) {
1072
1073 if (!debug && item->debug)
1074 continue;
1075
1076 /* find the available acpiget as described in the DSDT */
1077 for (; item->acpiget && *item->acpiget; ++item->acpiget) {
1078 if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
1079 *item->acpiget,
1080 &handle))) {
1081 dprintk("Found %s getter: %s\n",
1082 item->name, *item->acpiget);
1083 item->devattr.attr.mode |= S_IRUGO;
1084 break;
1085 }
1086 }
1087
1088 /* find the available acpiset as described in the DSDT */
1089 for (; item->acpiset && *item->acpiset; ++item->acpiset) {
1090 if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
1091 *item->acpiset,
1092 &handle))) {
1093 dprintk("Found %s setter: %s\n",
1094 item->name, *item->acpiset);
1095 item->devattr.attr.mode |= S_IWUSR;
1096 break;
1097 }
1098 }
1099
1100 if (item->devattr.attr.mode != 0) {
1101 result =
1102 device_create_file(&sony_pf_device->dev,
1103 &item->devattr);
1104 if (result)
1105 goto out_sysfs;
1106 }
1107 }
1108
1109 return 0;
1110
1111 out_sysfs:
1112 for (item = sony_nc_values; item->name; ++item) {
1113 device_remove_file(&sony_pf_device->dev, &item->devattr);
1114 }
1115 sony_pf_remove();
1116
1117 outbacklight:
1118 if (sony_backlight_device)
1119 backlight_device_unregister(sony_backlight_device);
1120
1121 status = acpi_remove_notify_handler(sony_nc_acpi_handle,
1122 ACPI_DEVICE_NOTIFY,
1123 sony_acpi_notify);
1124 if (ACPI_FAILURE(status))
1125 printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
1126
1127 outinput:
1128 sony_laptop_remove_input();
1129
1130 outwalk:
1131 return result;
1132}
1133
1134static int sony_nc_remove(struct acpi_device *device, int type)
1135{
1136 acpi_status status;
1137 struct sony_nc_value *item;
1138
1139 if (sony_backlight_device)
1140 backlight_device_unregister(sony_backlight_device);
1141
1142 sony_nc_acpi_device = NULL;
1143
1144 status = acpi_remove_notify_handler(sony_nc_acpi_handle,
1145 ACPI_DEVICE_NOTIFY,
1146 sony_acpi_notify);
1147 if (ACPI_FAILURE(status))
1148 printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
1149
1150 for (item = sony_nc_values; item->name; ++item) {
1151 device_remove_file(&sony_pf_device->dev, &item->devattr);
1152 }
1153
1154 sony_pf_remove();
1155 sony_laptop_remove_input();
1156 dprintk(SONY_NC_DRIVER_NAME " removed.\n");
1157
1158 return 0;
1159}
1160
1161static const struct acpi_device_id sony_device_ids[] = {
1162 {SONY_NC_HID, 0},
1163 {SONY_PIC_HID, 0},
1164 {"", 0},
1165};
1166MODULE_DEVICE_TABLE(acpi, sony_device_ids);
1167
1168static const struct acpi_device_id sony_nc_device_ids[] = {
1169 {SONY_NC_HID, 0},
1170 {"", 0},
1171};
1172
1173static struct acpi_driver sony_nc_driver = {
1174 .name = SONY_NC_DRIVER_NAME,
1175 .class = SONY_NC_CLASS,
1176 .ids = sony_nc_device_ids,
1177 .owner = THIS_MODULE,
1178 .ops = {
1179 .add = sony_nc_add,
1180 .remove = sony_nc_remove,
1181 .resume = sony_nc_resume,
1182 },
1183};
1184
1185/*********** SPIC (SNY6001) Device ***********/
1186
1187#define SONYPI_DEVICE_TYPE1 0x00000001
1188#define SONYPI_DEVICE_TYPE2 0x00000002
1189#define SONYPI_DEVICE_TYPE3 0x00000004
1190#define SONYPI_DEVICE_TYPE4 0x00000008
1191
1192#define SONYPI_TYPE1_OFFSET 0x04
1193#define SONYPI_TYPE2_OFFSET 0x12
1194#define SONYPI_TYPE3_OFFSET 0x12
1195#define SONYPI_TYPE4_OFFSET 0x12
1196
1197struct sony_pic_ioport {
1198 struct acpi_resource_io io1;
1199 struct acpi_resource_io io2;
1200 struct list_head list;
1201};
1202
1203struct sony_pic_irq {
1204 struct acpi_resource_irq irq;
1205 struct list_head list;
1206};
1207
1208struct sonypi_eventtypes {
1209 u8 data;
1210 unsigned long mask;
1211 struct sonypi_event *events;
1212};
1213
1214struct device_ctrl {
1215 int model;
1216 int (*handle_irq)(const u8, const u8);
1217 u16 evport_offset;
1218 u8 has_camera;
1219 u8 has_bluetooth;
1220 u8 has_wwan;
1221 struct sonypi_eventtypes *event_types;
1222};
1223
1224struct sony_pic_dev {
1225 struct device_ctrl *control;
1226 struct acpi_device *acpi_dev;
1227 struct sony_pic_irq *cur_irq;
1228 struct sony_pic_ioport *cur_ioport;
1229 struct list_head interrupts;
1230 struct list_head ioports;
1231 struct mutex lock;
1232 u8 camera_power;
1233 u8 bluetooth_power;
1234 u8 wwan_power;
1235};
1236
1237static struct sony_pic_dev spic_dev = {
1238 .interrupts = LIST_HEAD_INIT(spic_dev.interrupts),
1239 .ioports = LIST_HEAD_INIT(spic_dev.ioports),
1240};
1241
1242/* Event masks */
1243#define SONYPI_JOGGER_MASK 0x00000001
1244#define SONYPI_CAPTURE_MASK 0x00000002
1245#define SONYPI_FNKEY_MASK 0x00000004
1246#define SONYPI_BLUETOOTH_MASK 0x00000008
1247#define SONYPI_PKEY_MASK 0x00000010
1248#define SONYPI_BACK_MASK 0x00000020
1249#define SONYPI_HELP_MASK 0x00000040
1250#define SONYPI_LID_MASK 0x00000080
1251#define SONYPI_ZOOM_MASK 0x00000100
1252#define SONYPI_THUMBPHRASE_MASK 0x00000200
1253#define SONYPI_MEYE_MASK 0x00000400
1254#define SONYPI_MEMORYSTICK_MASK 0x00000800
1255#define SONYPI_BATTERY_MASK 0x00001000
1256#define SONYPI_WIRELESS_MASK 0x00002000
1257
1258struct sonypi_event {
1259 u8 data;
1260 u8 event;
1261};
1262
1263/* The set of possible button release events */
1264static struct sonypi_event sonypi_releaseev[] = {
1265 { 0x00, SONYPI_EVENT_ANYBUTTON_RELEASED },
1266 { 0, 0 }
1267};
1268
1269/* The set of possible jogger events */
1270static struct sonypi_event sonypi_joggerev[] = {
1271 { 0x1f, SONYPI_EVENT_JOGDIAL_UP },
1272 { 0x01, SONYPI_EVENT_JOGDIAL_DOWN },
1273 { 0x5f, SONYPI_EVENT_JOGDIAL_UP_PRESSED },
1274 { 0x41, SONYPI_EVENT_JOGDIAL_DOWN_PRESSED },
1275 { 0x1e, SONYPI_EVENT_JOGDIAL_FAST_UP },
1276 { 0x02, SONYPI_EVENT_JOGDIAL_FAST_DOWN },
1277 { 0x5e, SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED },
1278 { 0x42, SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED },
1279 { 0x1d, SONYPI_EVENT_JOGDIAL_VFAST_UP },
1280 { 0x03, SONYPI_EVENT_JOGDIAL_VFAST_DOWN },
1281 { 0x5d, SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED },
1282 { 0x43, SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED },
1283 { 0x40, SONYPI_EVENT_JOGDIAL_PRESSED },
1284 { 0, 0 }
1285};
1286
1287/* The set of possible capture button events */
1288static struct sonypi_event sonypi_captureev[] = {
1289 { 0x05, SONYPI_EVENT_CAPTURE_PARTIALPRESSED },
1290 { 0x07, SONYPI_EVENT_CAPTURE_PRESSED },
1291 { 0x40, SONYPI_EVENT_CAPTURE_PRESSED },
1292 { 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED },
1293 { 0, 0 }
1294};
1295
1296/* The set of possible fnkeys events */
1297static struct sonypi_event sonypi_fnkeyev[] = {
1298 { 0x10, SONYPI_EVENT_FNKEY_ESC },
1299 { 0x11, SONYPI_EVENT_FNKEY_F1 },
1300 { 0x12, SONYPI_EVENT_FNKEY_F2 },
1301 { 0x13, SONYPI_EVENT_FNKEY_F3 },
1302 { 0x14, SONYPI_EVENT_FNKEY_F4 },
1303 { 0x15, SONYPI_EVENT_FNKEY_F5 },
1304 { 0x16, SONYPI_EVENT_FNKEY_F6 },
1305 { 0x17, SONYPI_EVENT_FNKEY_F7 },
1306 { 0x18, SONYPI_EVENT_FNKEY_F8 },
1307 { 0x19, SONYPI_EVENT_FNKEY_F9 },
1308 { 0x1a, SONYPI_EVENT_FNKEY_F10 },
1309 { 0x1b, SONYPI_EVENT_FNKEY_F11 },
1310 { 0x1c, SONYPI_EVENT_FNKEY_F12 },
1311 { 0x1f, SONYPI_EVENT_FNKEY_RELEASED },
1312 { 0x21, SONYPI_EVENT_FNKEY_1 },
1313 { 0x22, SONYPI_EVENT_FNKEY_2 },
1314 { 0x31, SONYPI_EVENT_FNKEY_D },
1315 { 0x32, SONYPI_EVENT_FNKEY_E },
1316 { 0x33, SONYPI_EVENT_FNKEY_F },
1317 { 0x34, SONYPI_EVENT_FNKEY_S },
1318 { 0x35, SONYPI_EVENT_FNKEY_B },
1319 { 0x36, SONYPI_EVENT_FNKEY_ONLY },
1320 { 0, 0 }
1321};
1322
1323/* The set of possible program key events */
1324static struct sonypi_event sonypi_pkeyev[] = {
1325 { 0x01, SONYPI_EVENT_PKEY_P1 },
1326 { 0x02, SONYPI_EVENT_PKEY_P2 },
1327 { 0x04, SONYPI_EVENT_PKEY_P3 },
1328 { 0, 0 }
1329};
1330
1331/* The set of possible bluetooth events */
1332static struct sonypi_event sonypi_blueev[] = {
1333 { 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED },
1334 { 0x59, SONYPI_EVENT_BLUETOOTH_ON },
1335 { 0x5a, SONYPI_EVENT_BLUETOOTH_OFF },
1336 { 0, 0 }
1337};
1338
1339/* The set of possible wireless events */
1340static struct sonypi_event sonypi_wlessev[] = {
1341 { 0x59, SONYPI_EVENT_WIRELESS_ON },
1342 { 0x5a, SONYPI_EVENT_WIRELESS_OFF },
1343 { 0, 0 }
1344};
1345
1346/* The set of possible back button events */
1347static struct sonypi_event sonypi_backev[] = {
1348 { 0x20, SONYPI_EVENT_BACK_PRESSED },
1349 { 0, 0 }
1350};
1351
1352/* The set of possible help button events */
1353static struct sonypi_event sonypi_helpev[] = {
1354 { 0x3b, SONYPI_EVENT_HELP_PRESSED },
1355 { 0, 0 }
1356};
1357
1358
1359/* The set of possible lid events */
1360static struct sonypi_event sonypi_lidev[] = {
1361 { 0x51, SONYPI_EVENT_LID_CLOSED },
1362 { 0x50, SONYPI_EVENT_LID_OPENED },
1363 { 0, 0 }
1364};
1365
1366/* The set of possible zoom events */
1367static struct sonypi_event sonypi_zoomev[] = {
1368 { 0x39, SONYPI_EVENT_ZOOM_PRESSED },
1369 { 0x10, SONYPI_EVENT_ZOOM_IN_PRESSED },
1370 { 0x20, SONYPI_EVENT_ZOOM_OUT_PRESSED },
1371 { 0, 0 }
1372};
1373
1374/* The set of possible thumbphrase events */
1375static struct sonypi_event sonypi_thumbphraseev[] = {
1376 { 0x3a, SONYPI_EVENT_THUMBPHRASE_PRESSED },
1377 { 0, 0 }
1378};
1379
1380/* The set of possible motioneye camera events */
1381static struct sonypi_event sonypi_meyeev[] = {
1382 { 0x00, SONYPI_EVENT_MEYE_FACE },
1383 { 0x01, SONYPI_EVENT_MEYE_OPPOSITE },
1384 { 0, 0 }
1385};
1386
1387/* The set of possible memorystick events */
1388static struct sonypi_event sonypi_memorystickev[] = {
1389 { 0x53, SONYPI_EVENT_MEMORYSTICK_INSERT },
1390 { 0x54, SONYPI_EVENT_MEMORYSTICK_EJECT },
1391 { 0, 0 }
1392};
1393
1394/* The set of possible battery events */
1395static struct sonypi_event sonypi_batteryev[] = {
1396 { 0x20, SONYPI_EVENT_BATTERY_INSERT },
1397 { 0x30, SONYPI_EVENT_BATTERY_REMOVE },
1398 { 0, 0 }
1399};
1400
1401static struct sonypi_eventtypes type1_events[] = {
1402 { 0, 0xffffffff, sonypi_releaseev },
1403 { 0x70, SONYPI_MEYE_MASK, sonypi_meyeev },
1404 { 0x30, SONYPI_LID_MASK, sonypi_lidev },
1405 { 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev },
1406 { 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev },
1407 { 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
1408 { 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
1409 { 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev },
1410 { 0x30, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
1411 { 0x40, SONYPI_BATTERY_MASK, sonypi_batteryev },
1412 { 0 },
1413};
1414static struct sonypi_eventtypes type2_events[] = {
1415 { 0, 0xffffffff, sonypi_releaseev },
1416 { 0x38, SONYPI_LID_MASK, sonypi_lidev },
1417 { 0x11, SONYPI_JOGGER_MASK, sonypi_joggerev },
1418 { 0x61, SONYPI_CAPTURE_MASK, sonypi_captureev },
1419 { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
1420 { 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
1421 { 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev },
1422 { 0x11, SONYPI_BACK_MASK, sonypi_backev },
1423 { 0x21, SONYPI_HELP_MASK, sonypi_helpev },
1424 { 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev },
1425 { 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev },
1426 { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
1427 { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
1428 { 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
1429 { 0 },
1430};
1431static struct sonypi_eventtypes type3_events[] = {
1432 { 0, 0xffffffff, sonypi_releaseev },
1433 { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
1434 { 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
1435 { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
1436 { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
1437 { 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
1438 { 0 },
1439};
1440static struct sonypi_eventtypes type4_events[] = {
1441 { 0, 0xffffffff, sonypi_releaseev },
1442 { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
1443 { 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
1444 { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
1445 { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
1446 { 0x05, SONYPI_PKEY_MASK, sonypi_pkeyev },
1447 { 0x05, SONYPI_ZOOM_MASK, sonypi_zoomev },
1448 { 0x05, SONYPI_CAPTURE_MASK, sonypi_captureev },
1449 { 0 },
1450};
1451
1452/* low level spic calls */
1453#define ITERATIONS_LONG 10000
1454#define ITERATIONS_SHORT 10
1455#define wait_on_command(command, iterations) { \
1456 unsigned int n = iterations; \
1457 while (--n && (command)) \
1458 udelay(1); \
1459 if (!n) \
1460 dprintk("command failed at %s : %s (line %d)\n", \
1461 __FILE__, __func__, __LINE__); \
1462}
1463
1464static u8 sony_pic_call1(u8 dev)
1465{
1466 u8 v1, v2;
1467
1468 wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
1469 ITERATIONS_LONG);
1470 outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
1471 v1 = inb_p(spic_dev.cur_ioport->io1.minimum + 4);
1472 v2 = inb_p(spic_dev.cur_ioport->io1.minimum);
1473 dprintk("sony_pic_call1(0x%.2x): 0x%.4x\n", dev, (v2 << 8) | v1);
1474 return v2;
1475}
1476
1477static u8 sony_pic_call2(u8 dev, u8 fn)
1478{
1479 u8 v1;
1480
1481 wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
1482 ITERATIONS_LONG);
1483 outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
1484 wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
1485 ITERATIONS_LONG);
1486 outb(fn, spic_dev.cur_ioport->io1.minimum);
1487 v1 = inb_p(spic_dev.cur_ioport->io1.minimum);
1488 dprintk("sony_pic_call2(0x%.2x - 0x%.2x): 0x%.4x\n", dev, fn, v1);
1489 return v1;
1490}
1491
1492static u8 sony_pic_call3(u8 dev, u8 fn, u8 v)
1493{
1494 u8 v1;
1495
1496 wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
1497 outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
1498 wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
1499 outb(fn, spic_dev.cur_ioport->io1.minimum);
1500 wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
1501 outb(v, spic_dev.cur_ioport->io1.minimum);
1502 v1 = inb_p(spic_dev.cur_ioport->io1.minimum);
1503 dprintk("sony_pic_call3(0x%.2x - 0x%.2x - 0x%.2x): 0x%.4x\n",
1504 dev, fn, v, v1);
1505 return v1;
1506}
1507
1508/*
1509 * minidrivers for SPIC models
1510 */
1511static int type4_handle_irq(const u8 data_mask, const u8 ev)
1512{
1513 /*
1514 * 0x31 could mean we have to take some extra action and wait for
1515 * the next irq for some Type4 models, it will generate a new
1516 * irq and we can read new data from the device:
1517 * - 0x5c and 0x5f requires 0xA0
1518 * - 0x61 requires 0xB3
1519 */
1520 if (data_mask == 0x31) {
1521 if (ev == 0x5c || ev == 0x5f)
1522 sony_pic_call1(0xA0);
1523 else if (ev == 0x61)
1524 sony_pic_call1(0xB3);
1525 return 0;
1526 }
1527 return 1;
1528}
1529
1530static struct device_ctrl spic_types[] = {
1531 {
1532 .model = SONYPI_DEVICE_TYPE1,
1533 .handle_irq = NULL,
1534 .evport_offset = SONYPI_TYPE1_OFFSET,
1535 .event_types = type1_events,
1536 },
1537 {
1538 .model = SONYPI_DEVICE_TYPE2,
1539 .handle_irq = NULL,
1540 .evport_offset = SONYPI_TYPE2_OFFSET,
1541 .event_types = type2_events,
1542 },
1543 {
1544 .model = SONYPI_DEVICE_TYPE3,
1545 .handle_irq = NULL,
1546 .evport_offset = SONYPI_TYPE3_OFFSET,
1547 .event_types = type3_events,
1548 },
1549 {
1550 .model = SONYPI_DEVICE_TYPE4,
1551 .handle_irq = type4_handle_irq,
1552 .evport_offset = SONYPI_TYPE4_OFFSET,
1553 .event_types = type4_events,
1554 },
1555};
1556
1557static void sony_pic_detect_device_type(struct sony_pic_dev *dev)
1558{
1559 struct pci_dev *pcidev;
1560
1561 pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
1562 PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
1563 if (pcidev) {
1564 dev->control = &spic_types[0];
1565 goto out;
1566 }
1567
1568 pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
1569 PCI_DEVICE_ID_INTEL_ICH6_1, NULL);
1570 if (pcidev) {
1571 dev->control = &spic_types[2];
1572 goto out;
1573 }
1574
1575 pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
1576 PCI_DEVICE_ID_INTEL_ICH7_1, NULL);
1577 if (pcidev) {
1578 dev->control = &spic_types[3];
1579 goto out;
1580 }
1581
1582 pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
1583 PCI_DEVICE_ID_INTEL_ICH8_4, NULL);
1584 if (pcidev) {
1585 dev->control = &spic_types[3];
1586 goto out;
1587 }
1588
1589 /* default */
1590 dev->control = &spic_types[1];
1591
1592out:
1593 if (pcidev)
1594 pci_dev_put(pcidev);
1595
1596 printk(KERN_INFO DRV_PFX "detected Type%d model\n",
1597 dev->control->model == SONYPI_DEVICE_TYPE1 ? 1 :
1598 dev->control->model == SONYPI_DEVICE_TYPE2 ? 2 :
1599 dev->control->model == SONYPI_DEVICE_TYPE3 ? 3 : 4);
1600}
1601
1602/* camera tests and poweron/poweroff */
1603#define SONYPI_CAMERA_PICTURE 5
1604#define SONYPI_CAMERA_CONTROL 0x10
1605
1606#define SONYPI_CAMERA_BRIGHTNESS 0
1607#define SONYPI_CAMERA_CONTRAST 1
1608#define SONYPI_CAMERA_HUE 2
1609#define SONYPI_CAMERA_COLOR 3
1610#define SONYPI_CAMERA_SHARPNESS 4
1611
1612#define SONYPI_CAMERA_EXPOSURE_MASK 0xC
1613#define SONYPI_CAMERA_WHITE_BALANCE_MASK 0x3
1614#define SONYPI_CAMERA_PICTURE_MODE_MASK 0x30
1615#define SONYPI_CAMERA_MUTE_MASK 0x40
1616
1617/* the rest don't need a loop until not 0xff */
1618#define SONYPI_CAMERA_AGC 6
1619#define SONYPI_CAMERA_AGC_MASK 0x30
1620#define SONYPI_CAMERA_SHUTTER_MASK 0x7
1621
1622#define SONYPI_CAMERA_SHUTDOWN_REQUEST 7
1623#define SONYPI_CAMERA_CONTROL 0x10
1624
1625#define SONYPI_CAMERA_STATUS 7
1626#define SONYPI_CAMERA_STATUS_READY 0x2
1627#define SONYPI_CAMERA_STATUS_POSITION 0x4
1628
1629#define SONYPI_DIRECTION_BACKWARDS 0x4
1630
1631#define SONYPI_CAMERA_REVISION 8
1632#define SONYPI_CAMERA_ROMVERSION 9
1633
1634static int __sony_pic_camera_ready(void)
1635{
1636 u8 v;
1637
1638 v = sony_pic_call2(0x8f, SONYPI_CAMERA_STATUS);
1639 return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY));
1640}
1641
1642static int __sony_pic_camera_off(void)
1643{
1644 if (!camera) {
1645 printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
1646 return -ENODEV;
1647 }
1648
1649 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE,
1650 SONYPI_CAMERA_MUTE_MASK),
1651 ITERATIONS_SHORT);
1652
1653 if (spic_dev.camera_power) {
1654 sony_pic_call2(0x91, 0);
1655 spic_dev.camera_power = 0;
1656 }
1657 return 0;
1658}
1659
1660static int __sony_pic_camera_on(void)
1661{
1662 int i, j, x;
1663
1664 if (!camera) {
1665 printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
1666 return -ENODEV;
1667 }
1668
1669 if (spic_dev.camera_power)
1670 return 0;
1671
1672 for (j = 5; j > 0; j--) {
1673
1674 for (x = 0; x < 100 && sony_pic_call2(0x91, 0x1); x++)
1675 msleep(10);
1676 sony_pic_call1(0x93);
1677
1678 for (i = 400; i > 0; i--) {
1679 if (__sony_pic_camera_ready())
1680 break;
1681 msleep(10);
1682 }
1683 if (i)
1684 break;
1685 }
1686
1687 if (j == 0) {
1688 printk(KERN_WARNING DRV_PFX "failed to power on camera\n");
1689 return -ENODEV;
1690 }
1691
1692 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTROL,
1693 0x5a),
1694 ITERATIONS_SHORT);
1695
1696 spic_dev.camera_power = 1;
1697 return 0;
1698}
1699
1700/* External camera command (exported to the motion eye v4l driver) */
1701int sony_pic_camera_command(int command, u8 value)
1702{
1703 if (!camera)
1704 return -EIO;
1705
1706 mutex_lock(&spic_dev.lock);
1707
1708 switch (command) {
1709 case SONY_PIC_COMMAND_SETCAMERA:
1710 if (value)
1711 __sony_pic_camera_on();
1712 else
1713 __sony_pic_camera_off();
1714 break;
1715 case SONY_PIC_COMMAND_SETCAMERABRIGHTNESS:
1716 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_BRIGHTNESS, value),
1717 ITERATIONS_SHORT);
1718 break;
1719 case SONY_PIC_COMMAND_SETCAMERACONTRAST:
1720 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTRAST, value),
1721 ITERATIONS_SHORT);
1722 break;
1723 case SONY_PIC_COMMAND_SETCAMERAHUE:
1724 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_HUE, value),
1725 ITERATIONS_SHORT);
1726 break;
1727 case SONY_PIC_COMMAND_SETCAMERACOLOR:
1728 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_COLOR, value),
1729 ITERATIONS_SHORT);
1730 break;
1731 case SONY_PIC_COMMAND_SETCAMERASHARPNESS:
1732 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_SHARPNESS, value),
1733 ITERATIONS_SHORT);
1734 break;
1735 case SONY_PIC_COMMAND_SETCAMERAPICTURE:
1736 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, value),
1737 ITERATIONS_SHORT);
1738 break;
1739 case SONY_PIC_COMMAND_SETCAMERAAGC:
1740 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_AGC, value),
1741 ITERATIONS_SHORT);
1742 break;
1743 default:
1744 printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n",
1745 command);
1746 break;
1747 }
1748 mutex_unlock(&spic_dev.lock);
1749 return 0;
1750}
1751EXPORT_SYMBOL(sony_pic_camera_command);
1752
1753/* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */
1754static void sony_pic_set_wwanpower(u8 state)
1755{
1756 state = !!state;
1757 mutex_lock(&spic_dev.lock);
1758 if (spic_dev.wwan_power == state) {
1759 mutex_unlock(&spic_dev.lock);
1760 return;
1761 }
1762 sony_pic_call2(0xB0, state);
1763 spic_dev.wwan_power = state;
1764 mutex_unlock(&spic_dev.lock);
1765}
1766
1767static ssize_t sony_pic_wwanpower_store(struct device *dev,
1768 struct device_attribute *attr,
1769 const char *buffer, size_t count)
1770{
1771 unsigned long value;
1772 if (count > 31)
1773 return -EINVAL;
1774
1775 value = simple_strtoul(buffer, NULL, 10);
1776 sony_pic_set_wwanpower(value);
1777
1778 return count;
1779}
1780
1781static ssize_t sony_pic_wwanpower_show(struct device *dev,
1782 struct device_attribute *attr, char *buffer)
1783{
1784 ssize_t count;
1785 mutex_lock(&spic_dev.lock);
1786 count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.wwan_power);
1787 mutex_unlock(&spic_dev.lock);
1788 return count;
1789}
1790
1791/* bluetooth subsystem power state */
1792static void __sony_pic_set_bluetoothpower(u8 state)
1793{
1794 state = !!state;
1795 if (spic_dev.bluetooth_power == state)
1796 return;
1797 sony_pic_call2(0x96, state);
1798 sony_pic_call1(0x82);
1799 spic_dev.bluetooth_power = state;
1800}
1801
1802static ssize_t sony_pic_bluetoothpower_store(struct device *dev,
1803 struct device_attribute *attr,
1804 const char *buffer, size_t count)
1805{
1806 unsigned long value;
1807 if (count > 31)
1808 return -EINVAL;
1809
1810 value = simple_strtoul(buffer, NULL, 10);
1811 mutex_lock(&spic_dev.lock);
1812 __sony_pic_set_bluetoothpower(value);
1813 mutex_unlock(&spic_dev.lock);
1814
1815 return count;
1816}
1817
1818static ssize_t sony_pic_bluetoothpower_show(struct device *dev,
1819 struct device_attribute *attr, char *buffer)
1820{
1821 ssize_t count = 0;
1822 mutex_lock(&spic_dev.lock);
1823 count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.bluetooth_power);
1824 mutex_unlock(&spic_dev.lock);
1825 return count;
1826}
1827
1828/* fan speed */
1829/* FAN0 information (reverse engineered from ACPI tables) */
1830#define SONY_PIC_FAN0_STATUS 0x93
1831static int sony_pic_set_fanspeed(unsigned long value)
1832{
1833 return ec_write(SONY_PIC_FAN0_STATUS, value);
1834}
1835
1836static int sony_pic_get_fanspeed(u8 *value)
1837{
1838 return ec_read(SONY_PIC_FAN0_STATUS, value);
1839}
1840
1841static ssize_t sony_pic_fanspeed_store(struct device *dev,
1842 struct device_attribute *attr,
1843 const char *buffer, size_t count)
1844{
1845 unsigned long value;
1846 if (count > 31)
1847 return -EINVAL;
1848
1849 value = simple_strtoul(buffer, NULL, 10);
1850 if (sony_pic_set_fanspeed(value))
1851 return -EIO;
1852
1853 return count;
1854}
1855
1856static ssize_t sony_pic_fanspeed_show(struct device *dev,
1857 struct device_attribute *attr, char *buffer)
1858{
1859 u8 value = 0;
1860 if (sony_pic_get_fanspeed(&value))
1861 return -EIO;
1862
1863 return snprintf(buffer, PAGE_SIZE, "%d\n", value);
1864}
1865
1866#define SPIC_ATTR(_name, _mode) \
1867struct device_attribute spic_attr_##_name = __ATTR(_name, \
1868 _mode, sony_pic_## _name ##_show, \
1869 sony_pic_## _name ##_store)
1870
1871static SPIC_ATTR(bluetoothpower, 0644);
1872static SPIC_ATTR(wwanpower, 0644);
1873static SPIC_ATTR(fanspeed, 0644);
1874
1875static struct attribute *spic_attributes[] = {
1876 &spic_attr_bluetoothpower.attr,
1877 &spic_attr_wwanpower.attr,
1878 &spic_attr_fanspeed.attr,
1879 NULL
1880};
1881
1882static struct attribute_group spic_attribute_group = {
1883 .attrs = spic_attributes
1884};
1885
1886/******** SONYPI compatibility **********/
1887#ifdef CONFIG_SONYPI_COMPAT
1888
1889/* battery / brightness / temperature addresses */
1890#define SONYPI_BAT_FLAGS 0x81
1891#define SONYPI_LCD_LIGHT 0x96
1892#define SONYPI_BAT1_PCTRM 0xa0
1893#define SONYPI_BAT1_LEFT 0xa2
1894#define SONYPI_BAT1_MAXRT 0xa4
1895#define SONYPI_BAT2_PCTRM 0xa8
1896#define SONYPI_BAT2_LEFT 0xaa
1897#define SONYPI_BAT2_MAXRT 0xac
1898#define SONYPI_BAT1_MAXTK 0xb0
1899#define SONYPI_BAT1_FULL 0xb2
1900#define SONYPI_BAT2_MAXTK 0xb8
1901#define SONYPI_BAT2_FULL 0xba
1902#define SONYPI_TEMP_STATUS 0xC1
1903
1904struct sonypi_compat_s {
1905 struct fasync_struct *fifo_async;
1906 struct kfifo *fifo;
1907 spinlock_t fifo_lock;
1908 wait_queue_head_t fifo_proc_list;
1909 atomic_t open_count;
1910};
1911static struct sonypi_compat_s sonypi_compat = {
1912 .open_count = ATOMIC_INIT(0),
1913};
1914
1915static int sonypi_misc_fasync(int fd, struct file *filp, int on)
1916{
1917 int retval;
1918
1919 retval = fasync_helper(fd, filp, on, &sonypi_compat.fifo_async);
1920 if (retval < 0)
1921 return retval;
1922 return 0;
1923}
1924
1925static int sonypi_misc_release(struct inode *inode, struct file *file)
1926{
1927 atomic_dec(&sonypi_compat.open_count);
1928 return 0;
1929}
1930
1931static int sonypi_misc_open(struct inode *inode, struct file *file)
1932{
1933 /* Flush input queue on first open */
1934 lock_kernel();
1935 if (atomic_inc_return(&sonypi_compat.open_count) == 1)
1936 kfifo_reset(sonypi_compat.fifo);
1937 unlock_kernel();
1938 return 0;
1939}
1940
1941static ssize_t sonypi_misc_read(struct file *file, char __user *buf,
1942 size_t count, loff_t *pos)
1943{
1944 ssize_t ret;
1945 unsigned char c;
1946
1947 if ((kfifo_len(sonypi_compat.fifo) == 0) &&
1948 (file->f_flags & O_NONBLOCK))
1949 return -EAGAIN;
1950
1951 ret = wait_event_interruptible(sonypi_compat.fifo_proc_list,
1952 kfifo_len(sonypi_compat.fifo) != 0);
1953 if (ret)
1954 return ret;
1955
1956 while (ret < count &&
1957 (kfifo_get(sonypi_compat.fifo, &c, sizeof(c)) == sizeof(c))) {
1958 if (put_user(c, buf++))
1959 return -EFAULT;
1960 ret++;
1961 }
1962
1963 if (ret > 0) {
1964 struct inode *inode = file->f_path.dentry->d_inode;
1965 inode->i_atime = current_fs_time(inode->i_sb);
1966 }
1967
1968 return ret;
1969}
1970
1971static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait)
1972{
1973 poll_wait(file, &sonypi_compat.fifo_proc_list, wait);
1974 if (kfifo_len(sonypi_compat.fifo))
1975 return POLLIN | POLLRDNORM;
1976 return 0;
1977}
1978
1979static int ec_read16(u8 addr, u16 *value)
1980{
1981 u8 val_lb, val_hb;
1982 if (ec_read(addr, &val_lb))
1983 return -1;
1984 if (ec_read(addr + 1, &val_hb))
1985 return -1;
1986 *value = val_lb | (val_hb << 8);
1987 return 0;
1988}
1989
1990static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
1991 unsigned int cmd, unsigned long arg)
1992{
1993 int ret = 0;
1994 void __user *argp = (void __user *)arg;
1995 u8 val8;
1996 u16 val16;
1997 int value;
1998
1999 mutex_lock(&spic_dev.lock);
2000 switch (cmd) {
2001 case SONYPI_IOCGBRT:
2002 if (sony_backlight_device == NULL) {
2003 ret = -EIO;
2004 break;
2005 }
2006 if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) {
2007 ret = -EIO;
2008 break;
2009 }
2010 val8 = ((value & 0xff) - 1) << 5;
2011 if (copy_to_user(argp, &val8, sizeof(val8)))
2012 ret = -EFAULT;
2013 break;
2014 case SONYPI_IOCSBRT:
2015 if (sony_backlight_device == NULL) {
2016 ret = -EIO;
2017 break;
2018 }
2019 if (copy_from_user(&val8, argp, sizeof(val8))) {
2020 ret = -EFAULT;
2021 break;
2022 }
2023 if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
2024 (val8 >> 5) + 1, NULL)) {
2025 ret = -EIO;
2026 break;
2027 }
2028 /* sync the backlight device status */
2029 sony_backlight_device->props.brightness =
2030 sony_backlight_get_brightness(sony_backlight_device);
2031 break;
2032 case SONYPI_IOCGBAT1CAP:
2033 if (ec_read16(SONYPI_BAT1_FULL, &val16)) {
2034 ret = -EIO;
2035 break;
2036 }
2037 if (copy_to_user(argp, &val16, sizeof(val16)))
2038 ret = -EFAULT;
2039 break;
2040 case SONYPI_IOCGBAT1REM:
2041 if (ec_read16(SONYPI_BAT1_LEFT, &val16)) {
2042 ret = -EIO;
2043 break;
2044 }
2045 if (copy_to_user(argp, &val16, sizeof(val16)))
2046 ret = -EFAULT;
2047 break;
2048 case SONYPI_IOCGBAT2CAP:
2049 if (ec_read16(SONYPI_BAT2_FULL, &val16)) {
2050 ret = -EIO;
2051 break;
2052 }
2053 if (copy_to_user(argp, &val16, sizeof(val16)))
2054 ret = -EFAULT;
2055 break;
2056 case SONYPI_IOCGBAT2REM:
2057 if (ec_read16(SONYPI_BAT2_LEFT, &val16)) {
2058 ret = -EIO;
2059 break;
2060 }
2061 if (copy_to_user(argp, &val16, sizeof(val16)))
2062 ret = -EFAULT;
2063 break;
2064 case SONYPI_IOCGBATFLAGS:
2065 if (ec_read(SONYPI_BAT_FLAGS, &val8)) {
2066 ret = -EIO;
2067 break;
2068 }
2069 val8 &= 0x07;
2070 if (copy_to_user(argp, &val8, sizeof(val8)))
2071 ret = -EFAULT;
2072 break;
2073 case SONYPI_IOCGBLUE:
2074 val8 = spic_dev.bluetooth_power;
2075 if (copy_to_user(argp, &val8, sizeof(val8)))
2076 ret = -EFAULT;
2077 break;
2078 case SONYPI_IOCSBLUE:
2079 if (copy_from_user(&val8, argp, sizeof(val8))) {
2080 ret = -EFAULT;
2081 break;
2082 }
2083 __sony_pic_set_bluetoothpower(val8);
2084 break;
2085 /* FAN Controls */
2086 case SONYPI_IOCGFAN:
2087 if (sony_pic_get_fanspeed(&val8)) {
2088 ret = -EIO;
2089 break;
2090 }
2091 if (copy_to_user(argp, &val8, sizeof(val8)))
2092 ret = -EFAULT;
2093 break;
2094 case SONYPI_IOCSFAN:
2095 if (copy_from_user(&val8, argp, sizeof(val8))) {
2096 ret = -EFAULT;
2097 break;
2098 }
2099 if (sony_pic_set_fanspeed(val8))
2100 ret = -EIO;
2101 break;
2102 /* GET Temperature (useful under APM) */
2103 case SONYPI_IOCGTEMP:
2104 if (ec_read(SONYPI_TEMP_STATUS, &val8)) {
2105 ret = -EIO;
2106 break;
2107 }
2108 if (copy_to_user(argp, &val8, sizeof(val8)))
2109 ret = -EFAULT;
2110 break;
2111 default:
2112 ret = -EINVAL;
2113 }
2114 mutex_unlock(&spic_dev.lock);
2115 return ret;
2116}
2117
2118static const struct file_operations sonypi_misc_fops = {
2119 .owner = THIS_MODULE,
2120 .read = sonypi_misc_read,
2121 .poll = sonypi_misc_poll,
2122 .open = sonypi_misc_open,
2123 .release = sonypi_misc_release,
2124 .fasync = sonypi_misc_fasync,
2125 .ioctl = sonypi_misc_ioctl,
2126};
2127
2128static struct miscdevice sonypi_misc_device = {
2129 .minor = MISC_DYNAMIC_MINOR,
2130 .name = "sonypi",
2131 .fops = &sonypi_misc_fops,
2132};
2133
2134static void sonypi_compat_report_event(u8 event)
2135{
2136 kfifo_put(sonypi_compat.fifo, (unsigned char *)&event, sizeof(event));
2137 kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN);
2138 wake_up_interruptible(&sonypi_compat.fifo_proc_list);
2139}
2140
2141static int sonypi_compat_init(void)
2142{
2143 int error;
2144
2145 spin_lock_init(&sonypi_compat.fifo_lock);
2146 sonypi_compat.fifo = kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
2147 &sonypi_compat.fifo_lock);
2148 if (IS_ERR(sonypi_compat.fifo)) {
2149 printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
2150 return PTR_ERR(sonypi_compat.fifo);
2151 }
2152
2153 init_waitqueue_head(&sonypi_compat.fifo_proc_list);
2154
2155 if (minor != -1)
2156 sonypi_misc_device.minor = minor;
2157 error = misc_register(&sonypi_misc_device);
2158 if (error) {
2159 printk(KERN_ERR DRV_PFX "misc_register failed\n");
2160 goto err_free_kfifo;
2161 }
2162 if (minor == -1)
2163 printk(KERN_INFO DRV_PFX "device allocated minor is %d\n",
2164 sonypi_misc_device.minor);
2165
2166 return 0;
2167
2168err_free_kfifo:
2169 kfifo_free(sonypi_compat.fifo);
2170 return error;
2171}
2172
2173static void sonypi_compat_exit(void)
2174{
2175 misc_deregister(&sonypi_misc_device);
2176 kfifo_free(sonypi_compat.fifo);
2177}
2178#else
2179static int sonypi_compat_init(void) { return 0; }
2180static void sonypi_compat_exit(void) { }
2181static void sonypi_compat_report_event(u8 event) { }
2182#endif /* CONFIG_SONYPI_COMPAT */
2183
2184/*
2185 * ACPI callbacks
2186 */
2187static acpi_status
2188sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
2189{
2190 u32 i;
2191 struct sony_pic_dev *dev = (struct sony_pic_dev *)context;
2192
2193 switch (resource->type) {
2194 case ACPI_RESOURCE_TYPE_START_DEPENDENT:
2195 {
2196 /* start IO enumeration */
2197 struct sony_pic_ioport *ioport = kzalloc(sizeof(*ioport), GFP_KERNEL);
2198 if (!ioport)
2199 return AE_ERROR;
2200
2201 list_add(&ioport->list, &dev->ioports);
2202 return AE_OK;
2203 }
2204
2205 case ACPI_RESOURCE_TYPE_END_DEPENDENT:
2206 /* end IO enumeration */
2207 return AE_OK;
2208
2209 case ACPI_RESOURCE_TYPE_IRQ:
2210 {
2211 struct acpi_resource_irq *p = &resource->data.irq;
2212 struct sony_pic_irq *interrupt = NULL;
2213 if (!p || !p->interrupt_count) {
2214 /*
2215 * IRQ descriptors may have no IRQ# bits set,
2216 * particularly those those w/ _STA disabled
2217 */
2218 dprintk("Blank IRQ resource\n");
2219 return AE_OK;
2220 }
2221 for (i = 0; i < p->interrupt_count; i++) {
2222 if (!p->interrupts[i]) {
2223 printk(KERN_WARNING DRV_PFX
2224 "Invalid IRQ %d\n",
2225 p->interrupts[i]);
2226 continue;
2227 }
2228 interrupt = kzalloc(sizeof(*interrupt),
2229 GFP_KERNEL);
2230 if (!interrupt)
2231 return AE_ERROR;
2232
2233 list_add(&interrupt->list, &dev->interrupts);
2234 interrupt->irq.triggering = p->triggering;
2235 interrupt->irq.polarity = p->polarity;
2236 interrupt->irq.sharable = p->sharable;
2237 interrupt->irq.interrupt_count = 1;
2238 interrupt->irq.interrupts[0] = p->interrupts[i];
2239 }
2240 return AE_OK;
2241 }
2242 case ACPI_RESOURCE_TYPE_IO:
2243 {
2244 struct acpi_resource_io *io = &resource->data.io;
2245 struct sony_pic_ioport *ioport =
2246 list_first_entry(&dev->ioports, struct sony_pic_ioport, list);
2247 if (!io) {
2248 dprintk("Blank IO resource\n");
2249 return AE_OK;
2250 }
2251
2252 if (!ioport->io1.minimum) {
2253 memcpy(&ioport->io1, io, sizeof(*io));
2254 dprintk("IO1 at 0x%.4x (0x%.2x)\n", ioport->io1.minimum,
2255 ioport->io1.address_length);
2256 }
2257 else if (!ioport->io2.minimum) {
2258 memcpy(&ioport->io2, io, sizeof(*io));
2259 dprintk("IO2 at 0x%.4x (0x%.2x)\n", ioport->io2.minimum,
2260 ioport->io2.address_length);
2261 }
2262 else {
2263 printk(KERN_ERR DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n");
2264 return AE_ERROR;
2265 }
2266 return AE_OK;
2267 }
2268 default:
2269 dprintk("Resource %d isn't an IRQ nor an IO port\n",
2270 resource->type);
2271
2272 case ACPI_RESOURCE_TYPE_END_TAG:
2273 return AE_OK;
2274 }
2275 return AE_CTRL_TERMINATE;
2276}
2277
2278static int sony_pic_possible_resources(struct acpi_device *device)
2279{
2280 int result = 0;
2281 acpi_status status = AE_OK;
2282
2283 if (!device)
2284 return -EINVAL;
2285
2286 /* get device status */
2287 /* see acpi_pci_link_get_current acpi_pci_link_get_possible */
2288 dprintk("Evaluating _STA\n");
2289 result = acpi_bus_get_status(device);
2290 if (result) {
2291 printk(KERN_WARNING DRV_PFX "Unable to read status\n");
2292 goto end;
2293 }
2294
2295 if (!device->status.enabled)
2296 dprintk("Device disabled\n");
2297 else
2298 dprintk("Device enabled\n");
2299
2300 /*
2301 * Query and parse 'method'
2302 */
2303 dprintk("Evaluating %s\n", METHOD_NAME__PRS);
2304 status = acpi_walk_resources(device->handle, METHOD_NAME__PRS,
2305 sony_pic_read_possible_resource, &spic_dev);
2306 if (ACPI_FAILURE(status)) {
2307 printk(KERN_WARNING DRV_PFX
2308 "Failure evaluating %s\n",
2309 METHOD_NAME__PRS);
2310 result = -ENODEV;
2311 }
2312end:
2313 return result;
2314}
2315
2316/*
2317 * Disable the spic device by calling its _DIS method
2318 */
2319static int sony_pic_disable(struct acpi_device *device)
2320{
2321 acpi_status ret = acpi_evaluate_object(device->handle, "_DIS", NULL,
2322 NULL);
2323
2324 if (ACPI_FAILURE(ret) && ret != AE_NOT_FOUND)
2325 return -ENXIO;
2326
2327 dprintk("Device disabled\n");
2328 return 0;
2329}
2330
2331
2332/*
2333 * Based on drivers/acpi/pci_link.c:acpi_pci_link_set
2334 *
2335 * Call _SRS to set current resources
2336 */
2337static int sony_pic_enable(struct acpi_device *device,
2338 struct sony_pic_ioport *ioport, struct sony_pic_irq *irq)
2339{
2340 acpi_status status;
2341 int result = 0;
2342 /* Type 1 resource layout is:
2343 * IO
2344 * IO
2345 * IRQNoFlags
2346 * End
2347 *
2348 * Type 2 and 3 resource layout is:
2349 * IO
2350 * IRQNoFlags
2351 * End
2352 */
2353 struct {
2354 struct acpi_resource res1;
2355 struct acpi_resource res2;
2356 struct acpi_resource res3;
2357 struct acpi_resource res4;
2358 } *resource;
2359 struct acpi_buffer buffer = { 0, NULL };
2360
2361 if (!ioport || !irq)
2362 return -EINVAL;
2363
2364 /* init acpi_buffer */
2365 resource = kzalloc(sizeof(*resource) + 1, GFP_KERNEL);
2366 if (!resource)
2367 return -ENOMEM;
2368
2369 buffer.length = sizeof(*resource) + 1;
2370 buffer.pointer = resource;
2371
2372 /* setup Type 1 resources */
2373 if (spic_dev.control->model == SONYPI_DEVICE_TYPE1) {
2374
2375 /* setup io resources */
2376 resource->res1.type = ACPI_RESOURCE_TYPE_IO;
2377 resource->res1.length = sizeof(struct acpi_resource);
2378 memcpy(&resource->res1.data.io, &ioport->io1,
2379 sizeof(struct acpi_resource_io));
2380
2381 resource->res2.type = ACPI_RESOURCE_TYPE_IO;
2382 resource->res2.length = sizeof(struct acpi_resource);
2383 memcpy(&resource->res2.data.io, &ioport->io2,
2384 sizeof(struct acpi_resource_io));
2385
2386 /* setup irq resource */
2387 resource->res3.type = ACPI_RESOURCE_TYPE_IRQ;
2388 resource->res3.length = sizeof(struct acpi_resource);
2389 memcpy(&resource->res3.data.irq, &irq->irq,
2390 sizeof(struct acpi_resource_irq));
2391 /* we requested a shared irq */
2392 resource->res3.data.irq.sharable = ACPI_SHARED;
2393
2394 resource->res4.type = ACPI_RESOURCE_TYPE_END_TAG;
2395
2396 }
2397 /* setup Type 2/3 resources */
2398 else {
2399 /* setup io resource */
2400 resource->res1.type = ACPI_RESOURCE_TYPE_IO;
2401 resource->res1.length = sizeof(struct acpi_resource);
2402 memcpy(&resource->res1.data.io, &ioport->io1,
2403 sizeof(struct acpi_resource_io));
2404
2405 /* setup irq resource */
2406 resource->res2.type = ACPI_RESOURCE_TYPE_IRQ;
2407 resource->res2.length = sizeof(struct acpi_resource);
2408 memcpy(&resource->res2.data.irq, &irq->irq,
2409 sizeof(struct acpi_resource_irq));
2410 /* we requested a shared irq */
2411 resource->res2.data.irq.sharable = ACPI_SHARED;
2412
2413 resource->res3.type = ACPI_RESOURCE_TYPE_END_TAG;
2414 }
2415
2416 /* Attempt to set the resource */
2417 dprintk("Evaluating _SRS\n");
2418 status = acpi_set_current_resources(device->handle, &buffer);
2419
2420 /* check for total failure */
2421 if (ACPI_FAILURE(status)) {
2422 printk(KERN_ERR DRV_PFX "Error evaluating _SRS\n");
2423 result = -ENODEV;
2424 goto end;
2425 }
2426
2427 /* Necessary device initializations calls (from sonypi) */
2428 sony_pic_call1(0x82);
2429 sony_pic_call2(0x81, 0xff);
2430 sony_pic_call1(compat ? 0x92 : 0x82);
2431
2432end:
2433 kfree(resource);
2434 return result;
2435}
2436
2437/*****************
2438 *
2439 * ISR: some event is available
2440 *
2441 *****************/
2442static irqreturn_t sony_pic_irq(int irq, void *dev_id)
2443{
2444 int i, j;
2445 u8 ev = 0;
2446 u8 data_mask = 0;
2447 u8 device_event = 0;
2448
2449 struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id;
2450
2451 ev = inb_p(dev->cur_ioport->io1.minimum);
2452 if (dev->cur_ioport->io2.minimum)
2453 data_mask = inb_p(dev->cur_ioport->io2.minimum);
2454 else
2455 data_mask = inb_p(dev->cur_ioport->io1.minimum +
2456 dev->control->evport_offset);
2457
2458 dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n",
2459 ev, data_mask, dev->cur_ioport->io1.minimum,
2460 dev->control->evport_offset);
2461
2462 if (ev == 0x00 || ev == 0xff)
2463 return IRQ_HANDLED;
2464
2465 for (i = 0; dev->control->event_types[i].mask; i++) {
2466
2467 if ((data_mask & dev->control->event_types[i].data) !=
2468 dev->control->event_types[i].data)
2469 continue;
2470
2471 if (!(mask & dev->control->event_types[i].mask))
2472 continue;
2473
2474 for (j = 0; dev->control->event_types[i].events[j].event; j++) {
2475 if (ev == dev->control->event_types[i].events[j].data) {
2476 device_event =
2477 dev->control->
2478 event_types[i].events[j].event;
2479 goto found;
2480 }
2481 }
2482 }
2483 /* Still not able to decode the event try to pass
2484 * it over to the minidriver
2485 */
2486 if (dev->control->handle_irq &&
2487 dev->control->handle_irq(data_mask, ev) == 0)
2488 return IRQ_HANDLED;
2489
2490 dprintk("unknown event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n",
2491 ev, data_mask, dev->cur_ioport->io1.minimum,
2492 dev->control->evport_offset);
2493 return IRQ_HANDLED;
2494
2495found:
2496 sony_laptop_report_input_event(device_event);
2497 acpi_bus_generate_proc_event(dev->acpi_dev, 1, device_event);
2498 sonypi_compat_report_event(device_event);
2499
2500 return IRQ_HANDLED;
2501}
2502
2503/*****************
2504 *
2505 * ACPI driver
2506 *
2507 *****************/
2508static int sony_pic_remove(struct acpi_device *device, int type)
2509{
2510 struct sony_pic_ioport *io, *tmp_io;
2511 struct sony_pic_irq *irq, *tmp_irq;
2512
2513 if (sony_pic_disable(device)) {
2514 printk(KERN_ERR DRV_PFX "Couldn't disable device.\n");
2515 return -ENXIO;
2516 }
2517
2518 free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
2519 release_region(spic_dev.cur_ioport->io1.minimum,
2520 spic_dev.cur_ioport->io1.address_length);
2521 if (spic_dev.cur_ioport->io2.minimum)
2522 release_region(spic_dev.cur_ioport->io2.minimum,
2523 spic_dev.cur_ioport->io2.address_length);
2524
2525 sonypi_compat_exit();
2526
2527 sony_laptop_remove_input();
2528
2529 /* pf attrs */
2530 sysfs_remove_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
2531 sony_pf_remove();
2532
2533 list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
2534 list_del(&io->list);
2535 kfree(io);
2536 }
2537 list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
2538 list_del(&irq->list);
2539 kfree(irq);
2540 }
2541 spic_dev.cur_ioport = NULL;
2542 spic_dev.cur_irq = NULL;
2543
2544 dprintk(SONY_PIC_DRIVER_NAME " removed.\n");
2545 return 0;
2546}
2547
2548static int sony_pic_add(struct acpi_device *device)
2549{
2550 int result;
2551 struct sony_pic_ioport *io, *tmp_io;
2552 struct sony_pic_irq *irq, *tmp_irq;
2553
2554 printk(KERN_INFO DRV_PFX "%s v%s.\n",
2555 SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
2556
2557 spic_dev.acpi_dev = device;
2558 strcpy(acpi_device_class(device), "sony/hotkey");
2559 sony_pic_detect_device_type(&spic_dev);
2560 mutex_init(&spic_dev.lock);
2561
2562 /* read _PRS resources */
2563 result = sony_pic_possible_resources(device);
2564 if (result) {
2565 printk(KERN_ERR DRV_PFX
2566 "Unabe to read possible resources.\n");
2567 goto err_free_resources;
2568 }
2569
2570 /* setup input devices and helper fifo */
2571 result = sony_laptop_setup_input(device);
2572 if (result) {
2573 printk(KERN_ERR DRV_PFX
2574 "Unabe to create input devices.\n");
2575 goto err_free_resources;
2576 }
2577
2578 if (sonypi_compat_init())
2579 goto err_remove_input;
2580
2581 /* request io port */
2582 list_for_each_entry_reverse(io, &spic_dev.ioports, list) {
2583 if (request_region(io->io1.minimum, io->io1.address_length,
2584 "Sony Programable I/O Device")) {
2585 dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n",
2586 io->io1.minimum, io->io1.maximum,
2587 io->io1.address_length);
2588 /* Type 1 have 2 ioports */
2589 if (io->io2.minimum) {
2590 if (request_region(io->io2.minimum,
2591 io->io2.address_length,
2592 "Sony Programable I/O Device")) {
2593 dprintk("I/O port2: 0x%.4x (0x%.4x) + 0x%.2x\n",
2594 io->io2.minimum, io->io2.maximum,
2595 io->io2.address_length);
2596 spic_dev.cur_ioport = io;
2597 break;
2598 }
2599 else {
2600 dprintk("Unable to get I/O port2: "
2601 "0x%.4x (0x%.4x) + 0x%.2x\n",
2602 io->io2.minimum, io->io2.maximum,
2603 io->io2.address_length);
2604 release_region(io->io1.minimum,
2605 io->io1.address_length);
2606 }
2607 }
2608 else {
2609 spic_dev.cur_ioport = io;
2610 break;
2611 }
2612 }
2613 }
2614 if (!spic_dev.cur_ioport) {
2615 printk(KERN_ERR DRV_PFX "Failed to request_region.\n");
2616 result = -ENODEV;
2617 goto err_remove_compat;
2618 }
2619
2620 /* request IRQ */
2621 list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) {
2622 if (!request_irq(irq->irq.interrupts[0], sony_pic_irq,
2623 IRQF_SHARED, "sony-laptop", &spic_dev)) {
2624 dprintk("IRQ: %d - triggering: %d - "
2625 "polarity: %d - shr: %d\n",
2626 irq->irq.interrupts[0],
2627 irq->irq.triggering,
2628 irq->irq.polarity,
2629 irq->irq.sharable);
2630 spic_dev.cur_irq = irq;
2631 break;
2632 }
2633 }
2634 if (!spic_dev.cur_irq) {
2635 printk(KERN_ERR DRV_PFX "Failed to request_irq.\n");
2636 result = -ENODEV;
2637 goto err_release_region;
2638 }
2639
2640 /* set resource status _SRS */
2641 result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
2642 if (result) {
2643 printk(KERN_ERR DRV_PFX "Couldn't enable device.\n");
2644 goto err_free_irq;
2645 }
2646
2647 spic_dev.bluetooth_power = -1;
2648 /* create device attributes */
2649 result = sony_pf_add();
2650 if (result)
2651 goto err_disable_device;
2652
2653 result = sysfs_create_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
2654 if (result)
2655 goto err_remove_pf;
2656
2657 return 0;
2658
2659err_remove_pf:
2660 sony_pf_remove();
2661
2662err_disable_device:
2663 sony_pic_disable(device);
2664
2665err_free_irq:
2666 free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
2667
2668err_release_region:
2669 release_region(spic_dev.cur_ioport->io1.minimum,
2670 spic_dev.cur_ioport->io1.address_length);
2671 if (spic_dev.cur_ioport->io2.minimum)
2672 release_region(spic_dev.cur_ioport->io2.minimum,
2673 spic_dev.cur_ioport->io2.address_length);
2674
2675err_remove_compat:
2676 sonypi_compat_exit();
2677
2678err_remove_input:
2679 sony_laptop_remove_input();
2680
2681err_free_resources:
2682 list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
2683 list_del(&io->list);
2684 kfree(io);
2685 }
2686 list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
2687 list_del(&irq->list);
2688 kfree(irq);
2689 }
2690 spic_dev.cur_ioport = NULL;
2691 spic_dev.cur_irq = NULL;
2692
2693 return result;
2694}
2695
2696static int sony_pic_suspend(struct acpi_device *device, pm_message_t state)
2697{
2698 if (sony_pic_disable(device))
2699 return -ENXIO;
2700 return 0;
2701}
2702
2703static int sony_pic_resume(struct acpi_device *device)
2704{
2705 sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
2706 return 0;
2707}
2708
2709static const struct acpi_device_id sony_pic_device_ids[] = {
2710 {SONY_PIC_HID, 0},
2711 {"", 0},
2712};
2713
2714static struct acpi_driver sony_pic_driver = {
2715 .name = SONY_PIC_DRIVER_NAME,
2716 .class = SONY_PIC_CLASS,
2717 .ids = sony_pic_device_ids,
2718 .owner = THIS_MODULE,
2719 .ops = {
2720 .add = sony_pic_add,
2721 .remove = sony_pic_remove,
2722 .suspend = sony_pic_suspend,
2723 .resume = sony_pic_resume,
2724 },
2725};
2726
2727static struct dmi_system_id __initdata sonypi_dmi_table[] = {
2728 {
2729 .ident = "Sony Vaio",
2730 .matches = {
2731 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
2732 DMI_MATCH(DMI_PRODUCT_NAME, "PCG-"),
2733 },
2734 },
2735 {
2736 .ident = "Sony Vaio",
2737 .matches = {
2738 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
2739 DMI_MATCH(DMI_PRODUCT_NAME, "VGN-"),
2740 },
2741 },
2742 { }
2743};
2744
2745static int __init sony_laptop_init(void)
2746{
2747 int result;
2748
2749 if (!no_spic && dmi_check_system(sonypi_dmi_table)) {
2750 result = acpi_bus_register_driver(&sony_pic_driver);
2751 if (result) {
2752 printk(KERN_ERR DRV_PFX
2753 "Unable to register SPIC driver.");
2754 goto out;
2755 }
2756 }
2757
2758 result = acpi_bus_register_driver(&sony_nc_driver);
2759 if (result) {
2760 printk(KERN_ERR DRV_PFX "Unable to register SNC driver.");
2761 goto out_unregister_pic;
2762 }
2763
2764 return 0;
2765
2766out_unregister_pic:
2767 if (!no_spic)
2768 acpi_bus_unregister_driver(&sony_pic_driver);
2769out:
2770 return result;
2771}
2772
2773static void __exit sony_laptop_exit(void)
2774{
2775 acpi_bus_unregister_driver(&sony_nc_driver);
2776 if (!no_spic)
2777 acpi_bus_unregister_driver(&sony_pic_driver);
2778}
2779
2780module_init(sony_laptop_init);
2781module_exit(sony_laptop_exit);
diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c
new file mode 100644
index 000000000000..f25e4c974dcf
--- /dev/null
+++ b/drivers/platform/x86/tc1100-wmi.c
@@ -0,0 +1,290 @@
1/*
2 * HP Compaq TC1100 Tablet WMI Extras Driver
3 *
4 * Copyright (C) 2007 Carlos Corbacho <carlos@strangeworlds.co.uk>
5 * Copyright (C) 2004 Jamey Hicks <jamey.hicks@hp.com>
6 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
7 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
8 *
9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or (at
14 * your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
24 *
25 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
26 */
27
28#include <linux/kernel.h>
29#include <linux/module.h>
30#include <linux/init.h>
31#include <linux/types.h>
32#include <acpi/acpi.h>
33#include <acpi/actypes.h>
34#include <acpi/acpi_bus.h>
35#include <acpi/acpi_drivers.h>
36#include <linux/platform_device.h>
37
38#define GUID "C364AC71-36DB-495A-8494-B439D472A505"
39
40#define TC1100_INSTANCE_WIRELESS 1
41#define TC1100_INSTANCE_JOGDIAL 2
42
43#define TC1100_LOGPREFIX "tc1100-wmi: "
44#define TC1100_INFO KERN_INFO TC1100_LOGPREFIX
45
46MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho");
47MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras");
48MODULE_LICENSE("GPL");
49MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505");
50
51static int tc1100_probe(struct platform_device *device);
52static int tc1100_remove(struct platform_device *device);
53static int tc1100_suspend(struct platform_device *device, pm_message_t state);
54static int tc1100_resume(struct platform_device *device);
55
56static struct platform_driver tc1100_driver = {
57 .driver = {
58 .name = "tc1100-wmi",
59 .owner = THIS_MODULE,
60 },
61 .probe = tc1100_probe,
62 .remove = tc1100_remove,
63 .suspend = tc1100_suspend,
64 .resume = tc1100_resume,
65};
66
67static struct platform_device *tc1100_device;
68
69struct tc1100_data {
70 u32 wireless;
71 u32 jogdial;
72};
73
74static struct tc1100_data suspend_data;
75
76/* --------------------------------------------------------------------------
77 Device Management
78 -------------------------------------------------------------------------- */
79
80static int get_state(u32 *out, u8 instance)
81{
82 u32 tmp;
83 acpi_status status;
84 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
85 union acpi_object *obj;
86
87 if (!out)
88 return -EINVAL;
89
90 if (instance > 2)
91 return -ENODEV;
92
93 status = wmi_query_block(GUID, instance, &result);
94 if (ACPI_FAILURE(status))
95 return -ENODEV;
96
97 obj = (union acpi_object *) result.pointer;
98 if (obj && obj->type == ACPI_TYPE_BUFFER &&
99 obj->buffer.length == sizeof(u32)) {
100 tmp = *((u32 *) obj->buffer.pointer);
101 } else {
102 tmp = 0;
103 }
104
105 if (result.length > 0 && result.pointer)
106 kfree(result.pointer);
107
108 switch (instance) {
109 case TC1100_INSTANCE_WIRELESS:
110 *out = (tmp == 3) ? 1 : 0;
111 return 0;
112 case TC1100_INSTANCE_JOGDIAL:
113 *out = (tmp == 1) ? 1 : 0;
114 return 0;
115 default:
116 return -ENODEV;
117 }
118}
119
120static int set_state(u32 *in, u8 instance)
121{
122 u32 value;
123 acpi_status status;
124 struct acpi_buffer input;
125
126 if (!in)
127 return -EINVAL;
128
129 if (instance > 2)
130 return -ENODEV;
131
132 switch (instance) {
133 case TC1100_INSTANCE_WIRELESS:
134 value = (*in) ? 1 : 2;
135 break;
136 case TC1100_INSTANCE_JOGDIAL:
137 value = (*in) ? 0 : 1;
138 break;
139 default:
140 return -ENODEV;
141 }
142
143 input.length = sizeof(u32);
144 input.pointer = &value;
145
146 status = wmi_set_block(GUID, instance, &input);
147 if (ACPI_FAILURE(status))
148 return -ENODEV;
149
150 return 0;
151}
152
153/* --------------------------------------------------------------------------
154 FS Interface (/sys)
155 -------------------------------------------------------------------------- */
156
157/*
158 * Read/ write bool sysfs macro
159 */
160#define show_set_bool(value, instance) \
161static ssize_t \
162show_bool_##value(struct device *dev, struct device_attribute *attr, \
163 char *buf) \
164{ \
165 u32 result; \
166 acpi_status status = get_state(&result, instance); \
167 if (ACPI_SUCCESS(status)) \
168 return sprintf(buf, "%d\n", result); \
169 return sprintf(buf, "Read error\n"); \
170} \
171\
172static ssize_t \
173set_bool_##value(struct device *dev, struct device_attribute *attr, \
174 const char *buf, size_t count) \
175{ \
176 u32 tmp = simple_strtoul(buf, NULL, 10); \
177 acpi_status status = set_state(&tmp, instance); \
178 if (ACPI_FAILURE(status)) \
179 return -EINVAL; \
180 return count; \
181} \
182static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
183 show_bool_##value, set_bool_##value);
184
185show_set_bool(wireless, TC1100_INSTANCE_WIRELESS);
186show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL);
187
188static void remove_fs(void)
189{
190 device_remove_file(&tc1100_device->dev, &dev_attr_wireless);
191 device_remove_file(&tc1100_device->dev, &dev_attr_jogdial);
192}
193
194static int add_fs(void)
195{
196 int ret;
197
198 ret = device_create_file(&tc1100_device->dev, &dev_attr_wireless);
199 if (ret)
200 goto add_sysfs_error;
201
202 ret = device_create_file(&tc1100_device->dev, &dev_attr_jogdial);
203 if (ret)
204 goto add_sysfs_error;
205
206 return ret;
207
208add_sysfs_error:
209 remove_fs();
210 return ret;
211}
212
213/* --------------------------------------------------------------------------
214 Driver Model
215 -------------------------------------------------------------------------- */
216
217static int tc1100_probe(struct platform_device *device)
218{
219 int result = 0;
220
221 result = add_fs();
222 return result;
223}
224
225
226static int tc1100_remove(struct platform_device *device)
227{
228 remove_fs();
229 return 0;
230}
231
232static int tc1100_suspend(struct platform_device *dev, pm_message_t state)
233{
234 int ret;
235
236 ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
237 if (ret)
238 return ret;
239
240 ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
241 if (ret)
242 return ret;
243
244 return ret;
245}
246
247static int tc1100_resume(struct platform_device *dev)
248{
249 int ret;
250
251 ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
252 if (ret)
253 return ret;
254
255 ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
256 if (ret)
257 return ret;
258
259 return ret;
260}
261
262static int __init tc1100_init(void)
263{
264 int result = 0;
265
266 if (!wmi_has_guid(GUID))
267 return -ENODEV;
268
269 result = platform_driver_register(&tc1100_driver);
270 if (result)
271 return result;
272
273 tc1100_device = platform_device_alloc("tc1100-wmi", -1);
274 platform_device_add(tc1100_device);
275
276 printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras loaded\n");
277
278 return result;
279}
280
281static void __exit tc1100_exit(void)
282{
283 platform_device_del(tc1100_device);
284 platform_driver_unregister(&tc1100_driver);
285
286 printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras unloaded\n");
287}
288
289module_init(tc1100_init);
290module_exit(tc1100_exit);
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
new file mode 100644
index 000000000000..899766e16fa8
--- /dev/null
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -0,0 +1,6949 @@
1/*
2 * thinkpad_acpi.c - ThinkPad ACPI Extras
3 *
4 *
5 * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
6 * Copyright (C) 2006-2008 Henrique de Moraes Holschuh <hmh@hmh.eng.br>
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 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 * 02110-1301, USA.
22 */
23
24#define TPACPI_VERSION "0.21"
25#define TPACPI_SYSFS_VERSION 0x020200
26
27/*
28 * Changelog:
29 * 2007-10-20 changelog trimmed down
30 *
31 * 2007-03-27 0.14 renamed to thinkpad_acpi and moved to
32 * drivers/misc.
33 *
34 * 2006-11-22 0.13 new maintainer
35 * changelog now lives in git commit history, and will
36 * not be updated further in-file.
37 *
38 * 2005-03-17 0.11 support for 600e, 770x
39 * thanks to Jamie Lentin <lentinj@dial.pipex.com>
40 *
41 * 2005-01-16 0.9 use MODULE_VERSION
42 * thanks to Henrik Brix Andersen <brix@gentoo.org>
43 * fix parameter passing on module loading
44 * thanks to Rusty Russell <rusty@rustcorp.com.au>
45 * thanks to Jim Radford <radford@blackbean.org>
46 * 2004-11-08 0.8 fix init error case, don't return from a macro
47 * thanks to Chris Wright <chrisw@osdl.org>
48 */
49
50#include <linux/kernel.h>
51#include <linux/module.h>
52#include <linux/init.h>
53#include <linux/types.h>
54#include <linux/string.h>
55#include <linux/list.h>
56#include <linux/mutex.h>
57#include <linux/kthread.h>
58#include <linux/freezer.h>
59#include <linux/delay.h>
60
61#include <linux/nvram.h>
62#include <linux/proc_fs.h>
63#include <linux/sysfs.h>
64#include <linux/backlight.h>
65#include <linux/fb.h>
66#include <linux/platform_device.h>
67#include <linux/hwmon.h>
68#include <linux/hwmon-sysfs.h>
69#include <linux/input.h>
70#include <linux/leds.h>
71#include <linux/rfkill.h>
72#include <asm/uaccess.h>
73
74#include <linux/dmi.h>
75#include <linux/jiffies.h>
76#include <linux/workqueue.h>
77
78#include <acpi/acpi_drivers.h>
79#include <acpi/acnamesp.h>
80
81#include <linux/pci_ids.h>
82
83
84/* ThinkPad CMOS commands */
85#define TP_CMOS_VOLUME_DOWN 0
86#define TP_CMOS_VOLUME_UP 1
87#define TP_CMOS_VOLUME_MUTE 2
88#define TP_CMOS_BRIGHTNESS_UP 4
89#define TP_CMOS_BRIGHTNESS_DOWN 5
90#define TP_CMOS_THINKLIGHT_ON 12
91#define TP_CMOS_THINKLIGHT_OFF 13
92
93/* NVRAM Addresses */
94enum tp_nvram_addr {
95 TP_NVRAM_ADDR_HK2 = 0x57,
96 TP_NVRAM_ADDR_THINKLIGHT = 0x58,
97 TP_NVRAM_ADDR_VIDEO = 0x59,
98 TP_NVRAM_ADDR_BRIGHTNESS = 0x5e,
99 TP_NVRAM_ADDR_MIXER = 0x60,
100};
101
102/* NVRAM bit masks */
103enum {
104 TP_NVRAM_MASK_HKT_THINKPAD = 0x08,
105 TP_NVRAM_MASK_HKT_ZOOM = 0x20,
106 TP_NVRAM_MASK_HKT_DISPLAY = 0x40,
107 TP_NVRAM_MASK_HKT_HIBERNATE = 0x80,
108 TP_NVRAM_MASK_THINKLIGHT = 0x10,
109 TP_NVRAM_MASK_HKT_DISPEXPND = 0x30,
110 TP_NVRAM_MASK_HKT_BRIGHTNESS = 0x20,
111 TP_NVRAM_MASK_LEVEL_BRIGHTNESS = 0x0f,
112 TP_NVRAM_POS_LEVEL_BRIGHTNESS = 0,
113 TP_NVRAM_MASK_MUTE = 0x40,
114 TP_NVRAM_MASK_HKT_VOLUME = 0x80,
115 TP_NVRAM_MASK_LEVEL_VOLUME = 0x0f,
116 TP_NVRAM_POS_LEVEL_VOLUME = 0,
117};
118
119/* ACPI HIDs */
120#define TPACPI_ACPI_HKEY_HID "IBM0068"
121
122/* Input IDs */
123#define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */
124#define TPACPI_HKEY_INPUT_VERSION 0x4101
125
126
127/****************************************************************************
128 * Main driver
129 */
130
131#define TPACPI_NAME "thinkpad"
132#define TPACPI_DESC "ThinkPad ACPI Extras"
133#define TPACPI_FILE TPACPI_NAME "_acpi"
134#define TPACPI_URL "http://ibm-acpi.sf.net/"
135#define TPACPI_MAIL "ibm-acpi-devel@lists.sourceforge.net"
136
137#define TPACPI_PROC_DIR "ibm"
138#define TPACPI_ACPI_EVENT_PREFIX "ibm"
139#define TPACPI_DRVR_NAME TPACPI_FILE
140#define TPACPI_DRVR_SHORTNAME "tpacpi"
141#define TPACPI_HWMON_DRVR_NAME TPACPI_NAME "_hwmon"
142
143#define TPACPI_NVRAM_KTHREAD_NAME "ktpacpi_nvramd"
144#define TPACPI_WORKQUEUE_NAME "ktpacpid"
145
146#define TPACPI_MAX_ACPI_ARGS 3
147
148/* rfkill switches */
149enum {
150 TPACPI_RFK_BLUETOOTH_SW_ID = 0,
151 TPACPI_RFK_WWAN_SW_ID,
152};
153
154/* Debugging */
155#define TPACPI_LOG TPACPI_FILE ": "
156#define TPACPI_ERR KERN_ERR TPACPI_LOG
157#define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG
158#define TPACPI_INFO KERN_INFO TPACPI_LOG
159#define TPACPI_DEBUG KERN_DEBUG TPACPI_LOG
160
161#define TPACPI_DBG_ALL 0xffff
162#define TPACPI_DBG_INIT 0x0001
163#define TPACPI_DBG_EXIT 0x0002
164#define dbg_printk(a_dbg_level, format, arg...) \
165 do { if (dbg_level & a_dbg_level) \
166 printk(TPACPI_DEBUG "%s: " format, __func__ , ## arg); \
167 } while (0)
168#ifdef CONFIG_THINKPAD_ACPI_DEBUG
169#define vdbg_printk(a_dbg_level, format, arg...) \
170 dbg_printk(a_dbg_level, format, ## arg)
171static const char *str_supported(int is_supported);
172#else
173#define vdbg_printk(a_dbg_level, format, arg...)
174#endif
175
176#define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off")
177#define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
178#define strlencmp(a, b) (strncmp((a), (b), strlen(b)))
179
180
181/****************************************************************************
182 * Driver-wide structs and misc. variables
183 */
184
185struct ibm_struct;
186
187struct tp_acpi_drv_struct {
188 const struct acpi_device_id *hid;
189 struct acpi_driver *driver;
190
191 void (*notify) (struct ibm_struct *, u32);
192 acpi_handle *handle;
193 u32 type;
194 struct acpi_device *device;
195};
196
197struct ibm_struct {
198 char *name;
199
200 int (*read) (char *);
201 int (*write) (char *);
202 void (*exit) (void);
203 void (*resume) (void);
204 void (*suspend) (pm_message_t state);
205
206 struct list_head all_drivers;
207
208 struct tp_acpi_drv_struct *acpi;
209
210 struct {
211 u8 acpi_driver_registered:1;
212 u8 acpi_notify_installed:1;
213 u8 proc_created:1;
214 u8 init_called:1;
215 u8 experimental:1;
216 } flags;
217};
218
219struct ibm_init_struct {
220 char param[32];
221
222 int (*init) (struct ibm_init_struct *);
223 struct ibm_struct *data;
224};
225
226static struct {
227#ifdef CONFIG_THINKPAD_ACPI_BAY
228 u32 bay_status:1;
229 u32 bay_eject:1;
230 u32 bay_status2:1;
231 u32 bay_eject2:1;
232#endif
233 u32 bluetooth:1;
234 u32 hotkey:1;
235 u32 hotkey_mask:1;
236 u32 hotkey_wlsw:1;
237 u32 hotkey_tablet:1;
238 u32 light:1;
239 u32 light_status:1;
240 u32 bright_16levels:1;
241 u32 bright_acpimode:1;
242 u32 wan:1;
243 u32 fan_ctrl_status_undef:1;
244 u32 input_device_registered:1;
245 u32 platform_drv_registered:1;
246 u32 platform_drv_attrs_registered:1;
247 u32 sensors_pdrv_registered:1;
248 u32 sensors_pdrv_attrs_registered:1;
249 u32 sensors_pdev_attrs_registered:1;
250 u32 hotkey_poll_active:1;
251} tp_features;
252
253static struct {
254 u16 hotkey_mask_ff:1;
255 u16 bright_cmos_ec_unsync:1;
256} tp_warned;
257
258struct thinkpad_id_data {
259 unsigned int vendor; /* ThinkPad vendor:
260 * PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */
261
262 char *bios_version_str; /* Something like 1ZET51WW (1.03z) */
263 char *ec_version_str; /* Something like 1ZHT51WW-1.04a */
264
265 u16 bios_model; /* Big Endian, TP-1Y = 0x5931, 0 = unknown */
266 u16 ec_model;
267
268 char *model_str; /* ThinkPad T43 */
269 char *nummodel_str; /* 9384A9C for a 9384-A9C model */
270};
271static struct thinkpad_id_data thinkpad_id;
272
273static enum {
274 TPACPI_LIFE_INIT = 0,
275 TPACPI_LIFE_RUNNING,
276 TPACPI_LIFE_EXITING,
277} tpacpi_lifecycle;
278
279static int experimental;
280static u32 dbg_level;
281
282static struct workqueue_struct *tpacpi_wq;
283
284/* Special LED class that can defer work */
285struct tpacpi_led_classdev {
286 struct led_classdev led_classdev;
287 struct work_struct work;
288 enum led_brightness new_brightness;
289 unsigned int led;
290};
291
292/****************************************************************************
293 ****************************************************************************
294 *
295 * ACPI Helpers and device model
296 *
297 ****************************************************************************
298 ****************************************************************************/
299
300/*************************************************************************
301 * ACPI basic handles
302 */
303
304static acpi_handle root_handle;
305
306#define TPACPI_HANDLE(object, parent, paths...) \
307 static acpi_handle object##_handle; \
308 static acpi_handle *object##_parent = &parent##_handle; \
309 static char *object##_path; \
310 static char *object##_paths[] = { paths }
311
312TPACPI_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */
313 "\\_SB.PCI.ISA.EC", /* 570 */
314 "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */
315 "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */
316 "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */
317 "\\_SB.PCI0.ICH3.EC0", /* R31 */
318 "\\_SB.PCI0.LPC.EC", /* all others */
319 );
320
321TPACPI_HANDLE(ecrd, ec, "ECRD"); /* 570 */
322TPACPI_HANDLE(ecwr, ec, "ECWR"); /* 570 */
323
324TPACPI_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, */
325 /* T4x, X31, X40 */
326 "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */
327 "\\CMS", /* R40, R40e */
328 ); /* all others */
329
330TPACPI_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */
331 "^HKEY", /* R30, R31 */
332 "HKEY", /* all others */
333 ); /* 570 */
334
335TPACPI_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */
336 "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */
337 "\\_SB.PCI0.VID0", /* 770e */
338 "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */
339 "\\_SB.PCI0.AGP.VID", /* all others */
340 ); /* R30, R31 */
341
342
343/*************************************************************************
344 * ACPI helpers
345 */
346
347static int acpi_evalf(acpi_handle handle,
348 void *res, char *method, char *fmt, ...)
349{
350 char *fmt0 = fmt;
351 struct acpi_object_list params;
352 union acpi_object in_objs[TPACPI_MAX_ACPI_ARGS];
353 struct acpi_buffer result, *resultp;
354 union acpi_object out_obj;
355 acpi_status status;
356 va_list ap;
357 char res_type;
358 int success;
359 int quiet;
360
361 if (!*fmt) {
362 printk(TPACPI_ERR "acpi_evalf() called with empty format\n");
363 return 0;
364 }
365
366 if (*fmt == 'q') {
367 quiet = 1;
368 fmt++;
369 } else
370 quiet = 0;
371
372 res_type = *(fmt++);
373
374 params.count = 0;
375 params.pointer = &in_objs[0];
376
377 va_start(ap, fmt);
378 while (*fmt) {
379 char c = *(fmt++);
380 switch (c) {
381 case 'd': /* int */
382 in_objs[params.count].integer.value = va_arg(ap, int);
383 in_objs[params.count++].type = ACPI_TYPE_INTEGER;
384 break;
385 /* add more types as needed */
386 default:
387 printk(TPACPI_ERR "acpi_evalf() called "
388 "with invalid format character '%c'\n", c);
389 return 0;
390 }
391 }
392 va_end(ap);
393
394 if (res_type != 'v') {
395 result.length = sizeof(out_obj);
396 result.pointer = &out_obj;
397 resultp = &result;
398 } else
399 resultp = NULL;
400
401 status = acpi_evaluate_object(handle, method, &params, resultp);
402
403 switch (res_type) {
404 case 'd': /* int */
405 if (res)
406 *(int *)res = out_obj.integer.value;
407 success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
408 break;
409 case 'v': /* void */
410 success = status == AE_OK;
411 break;
412 /* add more types as needed */
413 default:
414 printk(TPACPI_ERR "acpi_evalf() called "
415 "with invalid format character '%c'\n", res_type);
416 return 0;
417 }
418
419 if (!success && !quiet)
420 printk(TPACPI_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
421 method, fmt0, status);
422
423 return success;
424}
425
426static int acpi_ec_read(int i, u8 *p)
427{
428 int v;
429
430 if (ecrd_handle) {
431 if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i))
432 return 0;
433 *p = v;
434 } else {
435 if (ec_read(i, p) < 0)
436 return 0;
437 }
438
439 return 1;
440}
441
442static int acpi_ec_write(int i, u8 v)
443{
444 if (ecwr_handle) {
445 if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v))
446 return 0;
447 } else {
448 if (ec_write(i, v) < 0)
449 return 0;
450 }
451
452 return 1;
453}
454
455#if defined(CONFIG_THINKPAD_ACPI_DOCK) || defined(CONFIG_THINKPAD_ACPI_BAY)
456static int _sta(acpi_handle handle)
457{
458 int status;
459
460 if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
461 status = 0;
462
463 return status;
464}
465#endif
466
467static int issue_thinkpad_cmos_command(int cmos_cmd)
468{
469 if (!cmos_handle)
470 return -ENXIO;
471
472 if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd))
473 return -EIO;
474
475 return 0;
476}
477
478/*************************************************************************
479 * ACPI device model
480 */
481
482#define TPACPI_ACPIHANDLE_INIT(object) \
483 drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \
484 object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
485
486static void drv_acpi_handle_init(char *name,
487 acpi_handle *handle, acpi_handle parent,
488 char **paths, int num_paths, char **path)
489{
490 int i;
491 acpi_status status;
492
493 vdbg_printk(TPACPI_DBG_INIT, "trying to locate ACPI handle for %s\n",
494 name);
495
496 for (i = 0; i < num_paths; i++) {
497 status = acpi_get_handle(parent, paths[i], handle);
498 if (ACPI_SUCCESS(status)) {
499 *path = paths[i];
500 dbg_printk(TPACPI_DBG_INIT,
501 "Found ACPI handle %s for %s\n",
502 *path, name);
503 return;
504 }
505 }
506
507 vdbg_printk(TPACPI_DBG_INIT, "ACPI handle for %s not found\n",
508 name);
509 *handle = NULL;
510}
511
512static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data)
513{
514 struct ibm_struct *ibm = data;
515
516 if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING)
517 return;
518
519 if (!ibm || !ibm->acpi || !ibm->acpi->notify)
520 return;
521
522 ibm->acpi->notify(ibm, event);
523}
524
525static int __init setup_acpi_notify(struct ibm_struct *ibm)
526{
527 acpi_status status;
528 int rc;
529
530 BUG_ON(!ibm->acpi);
531
532 if (!*ibm->acpi->handle)
533 return 0;
534
535 vdbg_printk(TPACPI_DBG_INIT,
536 "setting up ACPI notify for %s\n", ibm->name);
537
538 rc = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device);
539 if (rc < 0) {
540 printk(TPACPI_ERR "acpi_bus_get_device(%s) failed: %d\n",
541 ibm->name, rc);
542 return -ENODEV;
543 }
544
545 ibm->acpi->device->driver_data = ibm;
546 sprintf(acpi_device_class(ibm->acpi->device), "%s/%s",
547 TPACPI_ACPI_EVENT_PREFIX,
548 ibm->name);
549
550 status = acpi_install_notify_handler(*ibm->acpi->handle,
551 ibm->acpi->type, dispatch_acpi_notify, ibm);
552 if (ACPI_FAILURE(status)) {
553 if (status == AE_ALREADY_EXISTS) {
554 printk(TPACPI_NOTICE
555 "another device driver is already "
556 "handling %s events\n", ibm->name);
557 } else {
558 printk(TPACPI_ERR
559 "acpi_install_notify_handler(%s) failed: %d\n",
560 ibm->name, status);
561 }
562 return -ENODEV;
563 }
564 ibm->flags.acpi_notify_installed = 1;
565 return 0;
566}
567
568static int __init tpacpi_device_add(struct acpi_device *device)
569{
570 return 0;
571}
572
573static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
574{
575 int rc;
576
577 dbg_printk(TPACPI_DBG_INIT,
578 "registering %s as an ACPI driver\n", ibm->name);
579
580 BUG_ON(!ibm->acpi);
581
582 ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
583 if (!ibm->acpi->driver) {
584 printk(TPACPI_ERR
585 "failed to allocate memory for ibm->acpi->driver\n");
586 return -ENOMEM;
587 }
588
589 sprintf(ibm->acpi->driver->name, "%s_%s", TPACPI_NAME, ibm->name);
590 ibm->acpi->driver->ids = ibm->acpi->hid;
591
592 ibm->acpi->driver->ops.add = &tpacpi_device_add;
593
594 rc = acpi_bus_register_driver(ibm->acpi->driver);
595 if (rc < 0) {
596 printk(TPACPI_ERR "acpi_bus_register_driver(%s) failed: %d\n",
597 ibm->name, rc);
598 kfree(ibm->acpi->driver);
599 ibm->acpi->driver = NULL;
600 } else if (!rc)
601 ibm->flags.acpi_driver_registered = 1;
602
603 return rc;
604}
605
606
607/****************************************************************************
608 ****************************************************************************
609 *
610 * Procfs Helpers
611 *
612 ****************************************************************************
613 ****************************************************************************/
614
615static int dispatch_procfs_read(char *page, char **start, off_t off,
616 int count, int *eof, void *data)
617{
618 struct ibm_struct *ibm = data;
619 int len;
620
621 if (!ibm || !ibm->read)
622 return -EINVAL;
623
624 len = ibm->read(page);
625 if (len < 0)
626 return len;
627
628 if (len <= off + count)
629 *eof = 1;
630 *start = page + off;
631 len -= off;
632 if (len > count)
633 len = count;
634 if (len < 0)
635 len = 0;
636
637 return len;
638}
639
640static int dispatch_procfs_write(struct file *file,
641 const char __user *userbuf,
642 unsigned long count, void *data)
643{
644 struct ibm_struct *ibm = data;
645 char *kernbuf;
646 int ret;
647
648 if (!ibm || !ibm->write)
649 return -EINVAL;
650
651 kernbuf = kmalloc(count + 2, GFP_KERNEL);
652 if (!kernbuf)
653 return -ENOMEM;
654
655 if (copy_from_user(kernbuf, userbuf, count)) {
656 kfree(kernbuf);
657 return -EFAULT;
658 }
659
660 kernbuf[count] = 0;
661 strcat(kernbuf, ",");
662 ret = ibm->write(kernbuf);
663 if (ret == 0)
664 ret = count;
665
666 kfree(kernbuf);
667
668 return ret;
669}
670
671static char *next_cmd(char **cmds)
672{
673 char *start = *cmds;
674 char *end;
675
676 while ((end = strchr(start, ',')) && end == start)
677 start = end + 1;
678
679 if (!end)
680 return NULL;
681
682 *end = 0;
683 *cmds = end + 1;
684 return start;
685}
686
687
688/****************************************************************************
689 ****************************************************************************
690 *
691 * Device model: input, hwmon and platform
692 *
693 ****************************************************************************
694 ****************************************************************************/
695
696static struct platform_device *tpacpi_pdev;
697static struct platform_device *tpacpi_sensors_pdev;
698static struct device *tpacpi_hwmon;
699static struct input_dev *tpacpi_inputdev;
700static struct mutex tpacpi_inputdev_send_mutex;
701static LIST_HEAD(tpacpi_all_drivers);
702
703static int tpacpi_suspend_handler(struct platform_device *pdev,
704 pm_message_t state)
705{
706 struct ibm_struct *ibm, *itmp;
707
708 list_for_each_entry_safe(ibm, itmp,
709 &tpacpi_all_drivers,
710 all_drivers) {
711 if (ibm->suspend)
712 (ibm->suspend)(state);
713 }
714
715 return 0;
716}
717
718static int tpacpi_resume_handler(struct platform_device *pdev)
719{
720 struct ibm_struct *ibm, *itmp;
721
722 list_for_each_entry_safe(ibm, itmp,
723 &tpacpi_all_drivers,
724 all_drivers) {
725 if (ibm->resume)
726 (ibm->resume)();
727 }
728
729 return 0;
730}
731
732static struct platform_driver tpacpi_pdriver = {
733 .driver = {
734 .name = TPACPI_DRVR_NAME,
735 .owner = THIS_MODULE,
736 },
737 .suspend = tpacpi_suspend_handler,
738 .resume = tpacpi_resume_handler,
739};
740
741static struct platform_driver tpacpi_hwmon_pdriver = {
742 .driver = {
743 .name = TPACPI_HWMON_DRVR_NAME,
744 .owner = THIS_MODULE,
745 },
746};
747
748/*************************************************************************
749 * sysfs support helpers
750 */
751
752struct attribute_set {
753 unsigned int members, max_members;
754 struct attribute_group group;
755};
756
757struct attribute_set_obj {
758 struct attribute_set s;
759 struct attribute *a;
760} __attribute__((packed));
761
762static struct attribute_set *create_attr_set(unsigned int max_members,
763 const char *name)
764{
765 struct attribute_set_obj *sobj;
766
767 if (max_members == 0)
768 return NULL;
769
770 /* Allocates space for implicit NULL at the end too */
771 sobj = kzalloc(sizeof(struct attribute_set_obj) +
772 max_members * sizeof(struct attribute *),
773 GFP_KERNEL);
774 if (!sobj)
775 return NULL;
776 sobj->s.max_members = max_members;
777 sobj->s.group.attrs = &sobj->a;
778 sobj->s.group.name = name;
779
780 return &sobj->s;
781}
782
783#define destroy_attr_set(_set) \
784 kfree(_set);
785
786/* not multi-threaded safe, use it in a single thread per set */
787static int add_to_attr_set(struct attribute_set *s, struct attribute *attr)
788{
789 if (!s || !attr)
790 return -EINVAL;
791
792 if (s->members >= s->max_members)
793 return -ENOMEM;
794
795 s->group.attrs[s->members] = attr;
796 s->members++;
797
798 return 0;
799}
800
801static int add_many_to_attr_set(struct attribute_set *s,
802 struct attribute **attr,
803 unsigned int count)
804{
805 int i, res;
806
807 for (i = 0; i < count; i++) {
808 res = add_to_attr_set(s, attr[i]);
809 if (res)
810 return res;
811 }
812
813 return 0;
814}
815
816static void delete_attr_set(struct attribute_set *s, struct kobject *kobj)
817{
818 sysfs_remove_group(kobj, &s->group);
819 destroy_attr_set(s);
820}
821
822#define register_attr_set_with_sysfs(_attr_set, _kobj) \
823 sysfs_create_group(_kobj, &_attr_set->group)
824
825static int parse_strtoul(const char *buf,
826 unsigned long max, unsigned long *value)
827{
828 char *endp;
829
830 while (*buf && isspace(*buf))
831 buf++;
832 *value = simple_strtoul(buf, &endp, 0);
833 while (*endp && isspace(*endp))
834 endp++;
835 if (*endp || *value > max)
836 return -EINVAL;
837
838 return 0;
839}
840
841static void tpacpi_disable_brightness_delay(void)
842{
843 if (acpi_evalf(hkey_handle, NULL, "PWMS", "qvd", 0))
844 printk(TPACPI_NOTICE
845 "ACPI backlight control delay disabled\n");
846}
847
848static int __init tpacpi_query_bcl_levels(acpi_handle handle)
849{
850 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
851 union acpi_object *obj;
852 int rc;
853
854 if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) {
855 obj = (union acpi_object *)buffer.pointer;
856 if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
857 printk(TPACPI_ERR "Unknown _BCL data, "
858 "please report this to %s\n", TPACPI_MAIL);
859 rc = 0;
860 } else {
861 rc = obj->package.count;
862 }
863 } else {
864 return 0;
865 }
866
867 kfree(buffer.pointer);
868 return rc;
869}
870
871static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle,
872 u32 lvl, void *context, void **rv)
873{
874 char name[ACPI_PATH_SEGMENT_LENGTH];
875 struct acpi_buffer buffer = { sizeof(name), &name };
876
877 if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) &&
878 !strncmp("_BCL", name, sizeof(name) - 1)) {
879 BUG_ON(!rv || !*rv);
880 **(int **)rv = tpacpi_query_bcl_levels(handle);
881 return AE_CTRL_TERMINATE;
882 } else {
883 return AE_OK;
884 }
885}
886
887/*
888 * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map
889 */
890static int __init tpacpi_check_std_acpi_brightness_support(void)
891{
892 int status;
893 int bcl_levels = 0;
894 void *bcl_ptr = &bcl_levels;
895
896 if (!vid_handle) {
897 TPACPI_ACPIHANDLE_INIT(vid);
898 }
899 if (!vid_handle)
900 return 0;
901
902 /*
903 * Search for a _BCL method, and execute it. This is safe on all
904 * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista
905 * BIOS in ACPI backlight control mode. We do NOT have to care
906 * about calling the _BCL method in an enabled video device, any
907 * will do for our purposes.
908 */
909
910 status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3,
911 tpacpi_acpi_walk_find_bcl, NULL,
912 &bcl_ptr);
913
914 if (ACPI_SUCCESS(status) && bcl_levels > 2) {
915 tp_features.bright_acpimode = 1;
916 return (bcl_levels - 2);
917 }
918
919 return 0;
920}
921
922static int __init tpacpi_new_rfkill(const unsigned int id,
923 struct rfkill **rfk,
924 const enum rfkill_type rfktype,
925 const char *name,
926 int (*toggle_radio)(void *, enum rfkill_state),
927 int (*get_state)(void *, enum rfkill_state *))
928{
929 int res;
930 enum rfkill_state initial_state;
931
932 *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype);
933 if (!*rfk) {
934 printk(TPACPI_ERR
935 "failed to allocate memory for rfkill class\n");
936 return -ENOMEM;
937 }
938
939 (*rfk)->name = name;
940 (*rfk)->get_state = get_state;
941 (*rfk)->toggle_radio = toggle_radio;
942
943 if (!get_state(NULL, &initial_state))
944 (*rfk)->state = initial_state;
945
946 res = rfkill_register(*rfk);
947 if (res < 0) {
948 printk(TPACPI_ERR
949 "failed to register %s rfkill switch: %d\n",
950 name, res);
951 rfkill_free(*rfk);
952 *rfk = NULL;
953 return res;
954 }
955
956 return 0;
957}
958
959/*************************************************************************
960 * thinkpad-acpi driver attributes
961 */
962
963/* interface_version --------------------------------------------------- */
964static ssize_t tpacpi_driver_interface_version_show(
965 struct device_driver *drv,
966 char *buf)
967{
968 return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION);
969}
970
971static DRIVER_ATTR(interface_version, S_IRUGO,
972 tpacpi_driver_interface_version_show, NULL);
973
974/* debug_level --------------------------------------------------------- */
975static ssize_t tpacpi_driver_debug_show(struct device_driver *drv,
976 char *buf)
977{
978 return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level);
979}
980
981static ssize_t tpacpi_driver_debug_store(struct device_driver *drv,
982 const char *buf, size_t count)
983{
984 unsigned long t;
985
986 if (parse_strtoul(buf, 0xffff, &t))
987 return -EINVAL;
988
989 dbg_level = t;
990
991 return count;
992}
993
994static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
995 tpacpi_driver_debug_show, tpacpi_driver_debug_store);
996
997/* version ------------------------------------------------------------- */
998static ssize_t tpacpi_driver_version_show(struct device_driver *drv,
999 char *buf)
1000{
1001 return snprintf(buf, PAGE_SIZE, "%s v%s\n",
1002 TPACPI_DESC, TPACPI_VERSION);
1003}
1004
1005static DRIVER_ATTR(version, S_IRUGO,
1006 tpacpi_driver_version_show, NULL);
1007
1008/* --------------------------------------------------------------------- */
1009
1010static struct driver_attribute *tpacpi_driver_attributes[] = {
1011 &driver_attr_debug_level, &driver_attr_version,
1012 &driver_attr_interface_version,
1013};
1014
1015static int __init tpacpi_create_driver_attributes(struct device_driver *drv)
1016{
1017 int i, res;
1018
1019 i = 0;
1020 res = 0;
1021 while (!res && i < ARRAY_SIZE(tpacpi_driver_attributes)) {
1022 res = driver_create_file(drv, tpacpi_driver_attributes[i]);
1023 i++;
1024 }
1025
1026 return res;
1027}
1028
1029static void tpacpi_remove_driver_attributes(struct device_driver *drv)
1030{
1031 int i;
1032
1033 for (i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++)
1034 driver_remove_file(drv, tpacpi_driver_attributes[i]);
1035}
1036
1037/****************************************************************************
1038 ****************************************************************************
1039 *
1040 * Subdrivers
1041 *
1042 ****************************************************************************
1043 ****************************************************************************/
1044
1045/*************************************************************************
1046 * thinkpad-acpi init subdriver
1047 */
1048
1049static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm)
1050{
1051 printk(TPACPI_INFO "%s v%s\n", TPACPI_DESC, TPACPI_VERSION);
1052 printk(TPACPI_INFO "%s\n", TPACPI_URL);
1053
1054 printk(TPACPI_INFO "ThinkPad BIOS %s, EC %s\n",
1055 (thinkpad_id.bios_version_str) ?
1056 thinkpad_id.bios_version_str : "unknown",
1057 (thinkpad_id.ec_version_str) ?
1058 thinkpad_id.ec_version_str : "unknown");
1059
1060 if (thinkpad_id.vendor && thinkpad_id.model_str)
1061 printk(TPACPI_INFO "%s %s, model %s\n",
1062 (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ?
1063 "IBM" : ((thinkpad_id.vendor ==
1064 PCI_VENDOR_ID_LENOVO) ?
1065 "Lenovo" : "Unknown vendor"),
1066 thinkpad_id.model_str,
1067 (thinkpad_id.nummodel_str) ?
1068 thinkpad_id.nummodel_str : "unknown");
1069
1070 return 0;
1071}
1072
1073static int thinkpad_acpi_driver_read(char *p)
1074{
1075 int len = 0;
1076
1077 len += sprintf(p + len, "driver:\t\t%s\n", TPACPI_DESC);
1078 len += sprintf(p + len, "version:\t%s\n", TPACPI_VERSION);
1079
1080 return len;
1081}
1082
1083static struct ibm_struct thinkpad_acpi_driver_data = {
1084 .name = "driver",
1085 .read = thinkpad_acpi_driver_read,
1086};
1087
1088/*************************************************************************
1089 * Hotkey subdriver
1090 */
1091
1092enum { /* hot key scan codes (derived from ACPI DSDT) */
1093 TP_ACPI_HOTKEYSCAN_FNF1 = 0,
1094 TP_ACPI_HOTKEYSCAN_FNF2,
1095 TP_ACPI_HOTKEYSCAN_FNF3,
1096 TP_ACPI_HOTKEYSCAN_FNF4,
1097 TP_ACPI_HOTKEYSCAN_FNF5,
1098 TP_ACPI_HOTKEYSCAN_FNF6,
1099 TP_ACPI_HOTKEYSCAN_FNF7,
1100 TP_ACPI_HOTKEYSCAN_FNF8,
1101 TP_ACPI_HOTKEYSCAN_FNF9,
1102 TP_ACPI_HOTKEYSCAN_FNF10,
1103 TP_ACPI_HOTKEYSCAN_FNF11,
1104 TP_ACPI_HOTKEYSCAN_FNF12,
1105 TP_ACPI_HOTKEYSCAN_FNBACKSPACE,
1106 TP_ACPI_HOTKEYSCAN_FNINSERT,
1107 TP_ACPI_HOTKEYSCAN_FNDELETE,
1108 TP_ACPI_HOTKEYSCAN_FNHOME,
1109 TP_ACPI_HOTKEYSCAN_FNEND,
1110 TP_ACPI_HOTKEYSCAN_FNPAGEUP,
1111 TP_ACPI_HOTKEYSCAN_FNPAGEDOWN,
1112 TP_ACPI_HOTKEYSCAN_FNSPACE,
1113 TP_ACPI_HOTKEYSCAN_VOLUMEUP,
1114 TP_ACPI_HOTKEYSCAN_VOLUMEDOWN,
1115 TP_ACPI_HOTKEYSCAN_MUTE,
1116 TP_ACPI_HOTKEYSCAN_THINKPAD,
1117};
1118
1119enum { /* Keys available through NVRAM polling */
1120 TPACPI_HKEY_NVRAM_KNOWN_MASK = 0x00fb88c0U,
1121 TPACPI_HKEY_NVRAM_GOOD_MASK = 0x00fb8000U,
1122};
1123
1124enum { /* Positions of some of the keys in hotkey masks */
1125 TP_ACPI_HKEY_DISPSWTCH_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF7,
1126 TP_ACPI_HKEY_DISPXPAND_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF8,
1127 TP_ACPI_HKEY_HIBERNATE_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF12,
1128 TP_ACPI_HKEY_BRGHTUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNHOME,
1129 TP_ACPI_HKEY_BRGHTDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNEND,
1130 TP_ACPI_HKEY_THNKLGHT_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP,
1131 TP_ACPI_HKEY_ZOOM_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNSPACE,
1132 TP_ACPI_HKEY_VOLUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEUP,
1133 TP_ACPI_HKEY_VOLDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEDOWN,
1134 TP_ACPI_HKEY_MUTE_MASK = 1 << TP_ACPI_HOTKEYSCAN_MUTE,
1135 TP_ACPI_HKEY_THINKPAD_MASK = 1 << TP_ACPI_HOTKEYSCAN_THINKPAD,
1136};
1137
1138enum { /* NVRAM to ACPI HKEY group map */
1139 TP_NVRAM_HKEY_GROUP_HK2 = TP_ACPI_HKEY_THINKPAD_MASK |
1140 TP_ACPI_HKEY_ZOOM_MASK |
1141 TP_ACPI_HKEY_DISPSWTCH_MASK |
1142 TP_ACPI_HKEY_HIBERNATE_MASK,
1143 TP_NVRAM_HKEY_GROUP_BRIGHTNESS = TP_ACPI_HKEY_BRGHTUP_MASK |
1144 TP_ACPI_HKEY_BRGHTDWN_MASK,
1145 TP_NVRAM_HKEY_GROUP_VOLUME = TP_ACPI_HKEY_VOLUP_MASK |
1146 TP_ACPI_HKEY_VOLDWN_MASK |
1147 TP_ACPI_HKEY_MUTE_MASK,
1148};
1149
1150#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1151struct tp_nvram_state {
1152 u16 thinkpad_toggle:1;
1153 u16 zoom_toggle:1;
1154 u16 display_toggle:1;
1155 u16 thinklight_toggle:1;
1156 u16 hibernate_toggle:1;
1157 u16 displayexp_toggle:1;
1158 u16 display_state:1;
1159 u16 brightness_toggle:1;
1160 u16 volume_toggle:1;
1161 u16 mute:1;
1162
1163 u8 brightness_level;
1164 u8 volume_level;
1165};
1166
1167static struct task_struct *tpacpi_hotkey_task;
1168static u32 hotkey_source_mask; /* bit mask 0=ACPI,1=NVRAM */
1169static int hotkey_poll_freq = 10; /* Hz */
1170static struct mutex hotkey_thread_mutex;
1171static struct mutex hotkey_thread_data_mutex;
1172static unsigned int hotkey_config_change;
1173
1174#else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
1175
1176#define hotkey_source_mask 0U
1177
1178#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
1179
1180static struct mutex hotkey_mutex;
1181
1182static enum { /* Reasons for waking up */
1183 TP_ACPI_WAKEUP_NONE = 0, /* None or unknown */
1184 TP_ACPI_WAKEUP_BAYEJ, /* Bay ejection request */
1185 TP_ACPI_WAKEUP_UNDOCK, /* Undock request */
1186} hotkey_wakeup_reason;
1187
1188static int hotkey_autosleep_ack;
1189
1190static int hotkey_orig_status;
1191static u32 hotkey_orig_mask;
1192static u32 hotkey_all_mask;
1193static u32 hotkey_reserved_mask;
1194static u32 hotkey_mask;
1195
1196static unsigned int hotkey_report_mode;
1197
1198static u16 *hotkey_keycode_map;
1199
1200static struct attribute_set *hotkey_dev_attributes;
1201
1202#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1203#define HOTKEY_CONFIG_CRITICAL_START \
1204 do { \
1205 mutex_lock(&hotkey_thread_data_mutex); \
1206 hotkey_config_change++; \
1207 } while (0);
1208#define HOTKEY_CONFIG_CRITICAL_END \
1209 mutex_unlock(&hotkey_thread_data_mutex);
1210#else
1211#define HOTKEY_CONFIG_CRITICAL_START
1212#define HOTKEY_CONFIG_CRITICAL_END
1213#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
1214
1215/* HKEY.MHKG() return bits */
1216#define TP_HOTKEY_TABLET_MASK (1 << 3)
1217
1218static int hotkey_get_wlsw(int *status)
1219{
1220 if (!acpi_evalf(hkey_handle, status, "WLSW", "d"))
1221 return -EIO;
1222 return 0;
1223}
1224
1225static int hotkey_get_tablet_mode(int *status)
1226{
1227 int s;
1228
1229 if (!acpi_evalf(hkey_handle, &s, "MHKG", "d"))
1230 return -EIO;
1231
1232 *status = ((s & TP_HOTKEY_TABLET_MASK) != 0);
1233 return 0;
1234}
1235
1236/*
1237 * Call with hotkey_mutex held
1238 */
1239static int hotkey_mask_get(void)
1240{
1241 u32 m = 0;
1242
1243 if (tp_features.hotkey_mask) {
1244 if (!acpi_evalf(hkey_handle, &m, "DHKN", "d"))
1245 return -EIO;
1246 }
1247 hotkey_mask = m | (hotkey_source_mask & hotkey_mask);
1248
1249 return 0;
1250}
1251
1252/*
1253 * Call with hotkey_mutex held
1254 */
1255static int hotkey_mask_set(u32 mask)
1256{
1257 int i;
1258 int rc = 0;
1259
1260 if (tp_features.hotkey_mask) {
1261 if (!tp_warned.hotkey_mask_ff &&
1262 (mask == 0xffff || mask == 0xffffff ||
1263 mask == 0xffffffff)) {
1264 tp_warned.hotkey_mask_ff = 1;
1265 printk(TPACPI_NOTICE
1266 "setting the hotkey mask to 0x%08x is likely "
1267 "not the best way to go about it\n", mask);
1268 printk(TPACPI_NOTICE
1269 "please consider using the driver defaults, "
1270 "and refer to up-to-date thinkpad-acpi "
1271 "documentation\n");
1272 }
1273
1274 HOTKEY_CONFIG_CRITICAL_START
1275 for (i = 0; i < 32; i++) {
1276 u32 m = 1 << i;
1277 /* enable in firmware mask only keys not in NVRAM
1278 * mode, but enable the key in the cached hotkey_mask
1279 * regardless of mode, or the key will end up
1280 * disabled by hotkey_mask_get() */
1281 if (!acpi_evalf(hkey_handle,
1282 NULL, "MHKM", "vdd", i + 1,
1283 !!((mask & ~hotkey_source_mask) & m))) {
1284 rc = -EIO;
1285 break;
1286 } else {
1287 hotkey_mask = (hotkey_mask & ~m) | (mask & m);
1288 }
1289 }
1290 HOTKEY_CONFIG_CRITICAL_END
1291
1292 /* hotkey_mask_get must be called unconditionally below */
1293 if (!hotkey_mask_get() && !rc &&
1294 (hotkey_mask & ~hotkey_source_mask) !=
1295 (mask & ~hotkey_source_mask)) {
1296 printk(TPACPI_NOTICE
1297 "requested hot key mask 0x%08x, but "
1298 "firmware forced it to 0x%08x\n",
1299 mask, hotkey_mask);
1300 }
1301 } else {
1302#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1303 HOTKEY_CONFIG_CRITICAL_START
1304 hotkey_mask = mask & hotkey_source_mask;
1305 HOTKEY_CONFIG_CRITICAL_END
1306 hotkey_mask_get();
1307 if (hotkey_mask != mask) {
1308 printk(TPACPI_NOTICE
1309 "requested hot key mask 0x%08x, "
1310 "forced to 0x%08x (NVRAM poll mask is "
1311 "0x%08x): no firmware mask support\n",
1312 mask, hotkey_mask, hotkey_source_mask);
1313 }
1314#else
1315 hotkey_mask_get();
1316 rc = -ENXIO;
1317#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
1318 }
1319
1320 return rc;
1321}
1322
1323static int hotkey_status_get(int *status)
1324{
1325 if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
1326 return -EIO;
1327
1328 return 0;
1329}
1330
1331static int hotkey_status_set(int status)
1332{
1333 if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
1334 return -EIO;
1335
1336 return 0;
1337}
1338
1339static void tpacpi_input_send_tabletsw(void)
1340{
1341 int state;
1342
1343 if (tp_features.hotkey_tablet &&
1344 !hotkey_get_tablet_mode(&state)) {
1345 mutex_lock(&tpacpi_inputdev_send_mutex);
1346
1347 input_report_switch(tpacpi_inputdev,
1348 SW_TABLET_MODE, !!state);
1349 input_sync(tpacpi_inputdev);
1350
1351 mutex_unlock(&tpacpi_inputdev_send_mutex);
1352 }
1353}
1354
1355static void tpacpi_input_send_key(unsigned int scancode)
1356{
1357 unsigned int keycode;
1358
1359 keycode = hotkey_keycode_map[scancode];
1360
1361 if (keycode != KEY_RESERVED) {
1362 mutex_lock(&tpacpi_inputdev_send_mutex);
1363
1364 input_report_key(tpacpi_inputdev, keycode, 1);
1365 if (keycode == KEY_UNKNOWN)
1366 input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
1367 scancode);
1368 input_sync(tpacpi_inputdev);
1369
1370 input_report_key(tpacpi_inputdev, keycode, 0);
1371 if (keycode == KEY_UNKNOWN)
1372 input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
1373 scancode);
1374 input_sync(tpacpi_inputdev);
1375
1376 mutex_unlock(&tpacpi_inputdev_send_mutex);
1377 }
1378}
1379
1380#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1381static struct tp_acpi_drv_struct ibm_hotkey_acpidriver;
1382
1383static void tpacpi_hotkey_send_key(unsigned int scancode)
1384{
1385 tpacpi_input_send_key(scancode);
1386 if (hotkey_report_mode < 2) {
1387 acpi_bus_generate_proc_event(ibm_hotkey_acpidriver.device,
1388 0x80, 0x1001 + scancode);
1389 }
1390}
1391
1392static void hotkey_read_nvram(struct tp_nvram_state *n, u32 m)
1393{
1394 u8 d;
1395
1396 if (m & TP_NVRAM_HKEY_GROUP_HK2) {
1397 d = nvram_read_byte(TP_NVRAM_ADDR_HK2);
1398 n->thinkpad_toggle = !!(d & TP_NVRAM_MASK_HKT_THINKPAD);
1399 n->zoom_toggle = !!(d & TP_NVRAM_MASK_HKT_ZOOM);
1400 n->display_toggle = !!(d & TP_NVRAM_MASK_HKT_DISPLAY);
1401 n->hibernate_toggle = !!(d & TP_NVRAM_MASK_HKT_HIBERNATE);
1402 }
1403 if (m & TP_ACPI_HKEY_THNKLGHT_MASK) {
1404 d = nvram_read_byte(TP_NVRAM_ADDR_THINKLIGHT);
1405 n->thinklight_toggle = !!(d & TP_NVRAM_MASK_THINKLIGHT);
1406 }
1407 if (m & TP_ACPI_HKEY_DISPXPAND_MASK) {
1408 d = nvram_read_byte(TP_NVRAM_ADDR_VIDEO);
1409 n->displayexp_toggle =
1410 !!(d & TP_NVRAM_MASK_HKT_DISPEXPND);
1411 }
1412 if (m & TP_NVRAM_HKEY_GROUP_BRIGHTNESS) {
1413 d = nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS);
1414 n->brightness_level = (d & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
1415 >> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
1416 n->brightness_toggle =
1417 !!(d & TP_NVRAM_MASK_HKT_BRIGHTNESS);
1418 }
1419 if (m & TP_NVRAM_HKEY_GROUP_VOLUME) {
1420 d = nvram_read_byte(TP_NVRAM_ADDR_MIXER);
1421 n->volume_level = (d & TP_NVRAM_MASK_LEVEL_VOLUME)
1422 >> TP_NVRAM_POS_LEVEL_VOLUME;
1423 n->mute = !!(d & TP_NVRAM_MASK_MUTE);
1424 n->volume_toggle = !!(d & TP_NVRAM_MASK_HKT_VOLUME);
1425 }
1426}
1427
1428#define TPACPI_COMPARE_KEY(__scancode, __member) \
1429 do { \
1430 if ((mask & (1 << __scancode)) && \
1431 oldn->__member != newn->__member) \
1432 tpacpi_hotkey_send_key(__scancode); \
1433 } while (0)
1434
1435#define TPACPI_MAY_SEND_KEY(__scancode) \
1436 do { if (mask & (1 << __scancode)) \
1437 tpacpi_hotkey_send_key(__scancode); } while (0)
1438
1439static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
1440 struct tp_nvram_state *newn,
1441 u32 mask)
1442{
1443 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
1444 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
1445 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle);
1446 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF12, hibernate_toggle);
1447
1448 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNPAGEUP, thinklight_toggle);
1449
1450 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF8, displayexp_toggle);
1451
1452 /* handle volume */
1453 if (oldn->volume_toggle != newn->volume_toggle) {
1454 if (oldn->mute != newn->mute) {
1455 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
1456 }
1457 if (oldn->volume_level > newn->volume_level) {
1458 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
1459 } else if (oldn->volume_level < newn->volume_level) {
1460 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
1461 } else if (oldn->mute == newn->mute) {
1462 /* repeated key presses that didn't change state */
1463 if (newn->mute) {
1464 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
1465 } else if (newn->volume_level != 0) {
1466 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
1467 } else {
1468 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
1469 }
1470 }
1471 }
1472
1473 /* handle brightness */
1474 if (oldn->brightness_toggle != newn->brightness_toggle) {
1475 if (oldn->brightness_level < newn->brightness_level) {
1476 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
1477 } else if (oldn->brightness_level > newn->brightness_level) {
1478 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
1479 } else {
1480 /* repeated key presses that didn't change state */
1481 if (newn->brightness_level != 0) {
1482 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
1483 } else {
1484 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
1485 }
1486 }
1487 }
1488}
1489
1490#undef TPACPI_COMPARE_KEY
1491#undef TPACPI_MAY_SEND_KEY
1492
1493static int hotkey_kthread(void *data)
1494{
1495 struct tp_nvram_state s[2];
1496 u32 mask;
1497 unsigned int si, so;
1498 unsigned long t;
1499 unsigned int change_detector, must_reset;
1500
1501 mutex_lock(&hotkey_thread_mutex);
1502
1503 if (tpacpi_lifecycle == TPACPI_LIFE_EXITING)
1504 goto exit;
1505
1506 set_freezable();
1507
1508 so = 0;
1509 si = 1;
1510 t = 0;
1511
1512 /* Initial state for compares */
1513 mutex_lock(&hotkey_thread_data_mutex);
1514 change_detector = hotkey_config_change;
1515 mask = hotkey_source_mask & hotkey_mask;
1516 mutex_unlock(&hotkey_thread_data_mutex);
1517 hotkey_read_nvram(&s[so], mask);
1518
1519 while (!kthread_should_stop() && hotkey_poll_freq) {
1520 if (t == 0)
1521 t = 1000/hotkey_poll_freq;
1522 t = msleep_interruptible(t);
1523 if (unlikely(kthread_should_stop()))
1524 break;
1525 must_reset = try_to_freeze();
1526 if (t > 0 && !must_reset)
1527 continue;
1528
1529 mutex_lock(&hotkey_thread_data_mutex);
1530 if (must_reset || hotkey_config_change != change_detector) {
1531 /* forget old state on thaw or config change */
1532 si = so;
1533 t = 0;
1534 change_detector = hotkey_config_change;
1535 }
1536 mask = hotkey_source_mask & hotkey_mask;
1537 mutex_unlock(&hotkey_thread_data_mutex);
1538
1539 if (likely(mask)) {
1540 hotkey_read_nvram(&s[si], mask);
1541 if (likely(si != so)) {
1542 hotkey_compare_and_issue_event(&s[so], &s[si],
1543 mask);
1544 }
1545 }
1546
1547 so = si;
1548 si ^= 1;
1549 }
1550
1551exit:
1552 mutex_unlock(&hotkey_thread_mutex);
1553 return 0;
1554}
1555
1556static void hotkey_poll_stop_sync(void)
1557{
1558 if (tpacpi_hotkey_task) {
1559 if (frozen(tpacpi_hotkey_task) ||
1560 freezing(tpacpi_hotkey_task))
1561 thaw_process(tpacpi_hotkey_task);
1562
1563 kthread_stop(tpacpi_hotkey_task);
1564 tpacpi_hotkey_task = NULL;
1565 mutex_lock(&hotkey_thread_mutex);
1566 /* at this point, the thread did exit */
1567 mutex_unlock(&hotkey_thread_mutex);
1568 }
1569}
1570
1571/* call with hotkey_mutex held */
1572static void hotkey_poll_setup(int may_warn)
1573{
1574 if ((hotkey_source_mask & hotkey_mask) != 0 &&
1575 hotkey_poll_freq > 0 &&
1576 (tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) {
1577 if (!tpacpi_hotkey_task) {
1578 tpacpi_hotkey_task = kthread_run(hotkey_kthread,
1579 NULL, TPACPI_NVRAM_KTHREAD_NAME);
1580 if (IS_ERR(tpacpi_hotkey_task)) {
1581 tpacpi_hotkey_task = NULL;
1582 printk(TPACPI_ERR
1583 "could not create kernel thread "
1584 "for hotkey polling\n");
1585 }
1586 }
1587 } else {
1588 hotkey_poll_stop_sync();
1589 if (may_warn &&
1590 hotkey_source_mask != 0 && hotkey_poll_freq == 0) {
1591 printk(TPACPI_NOTICE
1592 "hot keys 0x%08x require polling, "
1593 "which is currently disabled\n",
1594 hotkey_source_mask);
1595 }
1596 }
1597}
1598
1599static void hotkey_poll_setup_safe(int may_warn)
1600{
1601 mutex_lock(&hotkey_mutex);
1602 hotkey_poll_setup(may_warn);
1603 mutex_unlock(&hotkey_mutex);
1604}
1605
1606#else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
1607
1608static void hotkey_poll_setup_safe(int __unused)
1609{
1610}
1611
1612#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
1613
1614static int hotkey_inputdev_open(struct input_dev *dev)
1615{
1616 switch (tpacpi_lifecycle) {
1617 case TPACPI_LIFE_INIT:
1618 /*
1619 * hotkey_init will call hotkey_poll_setup_safe
1620 * at the appropriate moment
1621 */
1622 return 0;
1623 case TPACPI_LIFE_EXITING:
1624 return -EBUSY;
1625 case TPACPI_LIFE_RUNNING:
1626 hotkey_poll_setup_safe(0);
1627 return 0;
1628 }
1629
1630 /* Should only happen if tpacpi_lifecycle is corrupt */
1631 BUG();
1632 return -EBUSY;
1633}
1634
1635static void hotkey_inputdev_close(struct input_dev *dev)
1636{
1637 /* disable hotkey polling when possible */
1638 if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING)
1639 hotkey_poll_setup_safe(0);
1640}
1641
1642/* sysfs hotkey enable ------------------------------------------------- */
1643static ssize_t hotkey_enable_show(struct device *dev,
1644 struct device_attribute *attr,
1645 char *buf)
1646{
1647 int res, status;
1648
1649 res = hotkey_status_get(&status);
1650 if (res)
1651 return res;
1652
1653 return snprintf(buf, PAGE_SIZE, "%d\n", status);
1654}
1655
1656static ssize_t hotkey_enable_store(struct device *dev,
1657 struct device_attribute *attr,
1658 const char *buf, size_t count)
1659{
1660 unsigned long t;
1661 int res;
1662
1663 if (parse_strtoul(buf, 1, &t))
1664 return -EINVAL;
1665
1666 res = hotkey_status_set(t);
1667
1668 return (res) ? res : count;
1669}
1670
1671static struct device_attribute dev_attr_hotkey_enable =
1672 __ATTR(hotkey_enable, S_IWUSR | S_IRUGO,
1673 hotkey_enable_show, hotkey_enable_store);
1674
1675/* sysfs hotkey mask --------------------------------------------------- */
1676static ssize_t hotkey_mask_show(struct device *dev,
1677 struct device_attribute *attr,
1678 char *buf)
1679{
1680 int res;
1681
1682 if (mutex_lock_interruptible(&hotkey_mutex))
1683 return -ERESTARTSYS;
1684 res = hotkey_mask_get();
1685 mutex_unlock(&hotkey_mutex);
1686
1687 return (res)?
1688 res : snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_mask);
1689}
1690
1691static ssize_t hotkey_mask_store(struct device *dev,
1692 struct device_attribute *attr,
1693 const char *buf, size_t count)
1694{
1695 unsigned long t;
1696 int res;
1697
1698 if (parse_strtoul(buf, 0xffffffffUL, &t))
1699 return -EINVAL;
1700
1701 if (mutex_lock_interruptible(&hotkey_mutex))
1702 return -ERESTARTSYS;
1703
1704 res = hotkey_mask_set(t);
1705
1706#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1707 hotkey_poll_setup(1);
1708#endif
1709
1710 mutex_unlock(&hotkey_mutex);
1711
1712 return (res) ? res : count;
1713}
1714
1715static struct device_attribute dev_attr_hotkey_mask =
1716 __ATTR(hotkey_mask, S_IWUSR | S_IRUGO,
1717 hotkey_mask_show, hotkey_mask_store);
1718
1719/* sysfs hotkey bios_enabled ------------------------------------------- */
1720static ssize_t hotkey_bios_enabled_show(struct device *dev,
1721 struct device_attribute *attr,
1722 char *buf)
1723{
1724 return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_orig_status);
1725}
1726
1727static struct device_attribute dev_attr_hotkey_bios_enabled =
1728 __ATTR(hotkey_bios_enabled, S_IRUGO, hotkey_bios_enabled_show, NULL);
1729
1730/* sysfs hotkey bios_mask ---------------------------------------------- */
1731static ssize_t hotkey_bios_mask_show(struct device *dev,
1732 struct device_attribute *attr,
1733 char *buf)
1734{
1735 return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_orig_mask);
1736}
1737
1738static struct device_attribute dev_attr_hotkey_bios_mask =
1739 __ATTR(hotkey_bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL);
1740
1741/* sysfs hotkey all_mask ----------------------------------------------- */
1742static ssize_t hotkey_all_mask_show(struct device *dev,
1743 struct device_attribute *attr,
1744 char *buf)
1745{
1746 return snprintf(buf, PAGE_SIZE, "0x%08x\n",
1747 hotkey_all_mask | hotkey_source_mask);
1748}
1749
1750static struct device_attribute dev_attr_hotkey_all_mask =
1751 __ATTR(hotkey_all_mask, S_IRUGO, hotkey_all_mask_show, NULL);
1752
1753/* sysfs hotkey recommended_mask --------------------------------------- */
1754static ssize_t hotkey_recommended_mask_show(struct device *dev,
1755 struct device_attribute *attr,
1756 char *buf)
1757{
1758 return snprintf(buf, PAGE_SIZE, "0x%08x\n",
1759 (hotkey_all_mask | hotkey_source_mask)
1760 & ~hotkey_reserved_mask);
1761}
1762
1763static struct device_attribute dev_attr_hotkey_recommended_mask =
1764 __ATTR(hotkey_recommended_mask, S_IRUGO,
1765 hotkey_recommended_mask_show, NULL);
1766
1767#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1768
1769/* sysfs hotkey hotkey_source_mask ------------------------------------- */
1770static ssize_t hotkey_source_mask_show(struct device *dev,
1771 struct device_attribute *attr,
1772 char *buf)
1773{
1774 return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_source_mask);
1775}
1776
1777static ssize_t hotkey_source_mask_store(struct device *dev,
1778 struct device_attribute *attr,
1779 const char *buf, size_t count)
1780{
1781 unsigned long t;
1782
1783 if (parse_strtoul(buf, 0xffffffffUL, &t) ||
1784 ((t & ~TPACPI_HKEY_NVRAM_KNOWN_MASK) != 0))
1785 return -EINVAL;
1786
1787 if (mutex_lock_interruptible(&hotkey_mutex))
1788 return -ERESTARTSYS;
1789
1790 HOTKEY_CONFIG_CRITICAL_START
1791 hotkey_source_mask = t;
1792 HOTKEY_CONFIG_CRITICAL_END
1793
1794 hotkey_poll_setup(1);
1795
1796 mutex_unlock(&hotkey_mutex);
1797
1798 return count;
1799}
1800
1801static struct device_attribute dev_attr_hotkey_source_mask =
1802 __ATTR(hotkey_source_mask, S_IWUSR | S_IRUGO,
1803 hotkey_source_mask_show, hotkey_source_mask_store);
1804
1805/* sysfs hotkey hotkey_poll_freq --------------------------------------- */
1806static ssize_t hotkey_poll_freq_show(struct device *dev,
1807 struct device_attribute *attr,
1808 char *buf)
1809{
1810 return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_poll_freq);
1811}
1812
1813static ssize_t hotkey_poll_freq_store(struct device *dev,
1814 struct device_attribute *attr,
1815 const char *buf, size_t count)
1816{
1817 unsigned long t;
1818
1819 if (parse_strtoul(buf, 25, &t))
1820 return -EINVAL;
1821
1822 if (mutex_lock_interruptible(&hotkey_mutex))
1823 return -ERESTARTSYS;
1824
1825 hotkey_poll_freq = t;
1826
1827 hotkey_poll_setup(1);
1828 mutex_unlock(&hotkey_mutex);
1829
1830 return count;
1831}
1832
1833static struct device_attribute dev_attr_hotkey_poll_freq =
1834 __ATTR(hotkey_poll_freq, S_IWUSR | S_IRUGO,
1835 hotkey_poll_freq_show, hotkey_poll_freq_store);
1836
1837#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
1838
1839/* sysfs hotkey radio_sw (pollable) ------------------------------------ */
1840static ssize_t hotkey_radio_sw_show(struct device *dev,
1841 struct device_attribute *attr,
1842 char *buf)
1843{
1844 int res, s;
1845 res = hotkey_get_wlsw(&s);
1846 if (res < 0)
1847 return res;
1848
1849 return snprintf(buf, PAGE_SIZE, "%d\n", !!s);
1850}
1851
1852static struct device_attribute dev_attr_hotkey_radio_sw =
1853 __ATTR(hotkey_radio_sw, S_IRUGO, hotkey_radio_sw_show, NULL);
1854
1855static void hotkey_radio_sw_notify_change(void)
1856{
1857 if (tp_features.hotkey_wlsw)
1858 sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
1859 "hotkey_radio_sw");
1860}
1861
1862/* sysfs hotkey tablet mode (pollable) --------------------------------- */
1863static ssize_t hotkey_tablet_mode_show(struct device *dev,
1864 struct device_attribute *attr,
1865 char *buf)
1866{
1867 int res, s;
1868 res = hotkey_get_tablet_mode(&s);
1869 if (res < 0)
1870 return res;
1871
1872 return snprintf(buf, PAGE_SIZE, "%d\n", !!s);
1873}
1874
1875static struct device_attribute dev_attr_hotkey_tablet_mode =
1876 __ATTR(hotkey_tablet_mode, S_IRUGO, hotkey_tablet_mode_show, NULL);
1877
1878static void hotkey_tablet_mode_notify_change(void)
1879{
1880 if (tp_features.hotkey_tablet)
1881 sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
1882 "hotkey_tablet_mode");
1883}
1884
1885/* sysfs hotkey report_mode -------------------------------------------- */
1886static ssize_t hotkey_report_mode_show(struct device *dev,
1887 struct device_attribute *attr,
1888 char *buf)
1889{
1890 return snprintf(buf, PAGE_SIZE, "%d\n",
1891 (hotkey_report_mode != 0) ? hotkey_report_mode : 1);
1892}
1893
1894static struct device_attribute dev_attr_hotkey_report_mode =
1895 __ATTR(hotkey_report_mode, S_IRUGO, hotkey_report_mode_show, NULL);
1896
1897/* sysfs wakeup reason (pollable) -------------------------------------- */
1898static ssize_t hotkey_wakeup_reason_show(struct device *dev,
1899 struct device_attribute *attr,
1900 char *buf)
1901{
1902 return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_wakeup_reason);
1903}
1904
1905static struct device_attribute dev_attr_hotkey_wakeup_reason =
1906 __ATTR(wakeup_reason, S_IRUGO, hotkey_wakeup_reason_show, NULL);
1907
1908static void hotkey_wakeup_reason_notify_change(void)
1909{
1910 if (tp_features.hotkey_mask)
1911 sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
1912 "wakeup_reason");
1913}
1914
1915/* sysfs wakeup hotunplug_complete (pollable) -------------------------- */
1916static ssize_t hotkey_wakeup_hotunplug_complete_show(struct device *dev,
1917 struct device_attribute *attr,
1918 char *buf)
1919{
1920 return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_autosleep_ack);
1921}
1922
1923static struct device_attribute dev_attr_hotkey_wakeup_hotunplug_complete =
1924 __ATTR(wakeup_hotunplug_complete, S_IRUGO,
1925 hotkey_wakeup_hotunplug_complete_show, NULL);
1926
1927static void hotkey_wakeup_hotunplug_complete_notify_change(void)
1928{
1929 if (tp_features.hotkey_mask)
1930 sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
1931 "wakeup_hotunplug_complete");
1932}
1933
1934/* --------------------------------------------------------------------- */
1935
1936static struct attribute *hotkey_attributes[] __initdata = {
1937 &dev_attr_hotkey_enable.attr,
1938 &dev_attr_hotkey_bios_enabled.attr,
1939 &dev_attr_hotkey_report_mode.attr,
1940#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1941 &dev_attr_hotkey_mask.attr,
1942 &dev_attr_hotkey_all_mask.attr,
1943 &dev_attr_hotkey_recommended_mask.attr,
1944 &dev_attr_hotkey_source_mask.attr,
1945 &dev_attr_hotkey_poll_freq.attr,
1946#endif
1947};
1948
1949static struct attribute *hotkey_mask_attributes[] __initdata = {
1950 &dev_attr_hotkey_bios_mask.attr,
1951#ifndef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1952 &dev_attr_hotkey_mask.attr,
1953 &dev_attr_hotkey_all_mask.attr,
1954 &dev_attr_hotkey_recommended_mask.attr,
1955#endif
1956 &dev_attr_hotkey_wakeup_reason.attr,
1957 &dev_attr_hotkey_wakeup_hotunplug_complete.attr,
1958};
1959
1960static void bluetooth_update_rfk(void);
1961static void wan_update_rfk(void);
1962static void tpacpi_send_radiosw_update(void)
1963{
1964 int wlsw;
1965
1966 /* Sync these BEFORE sending any rfkill events */
1967 if (tp_features.bluetooth)
1968 bluetooth_update_rfk();
1969 if (tp_features.wan)
1970 wan_update_rfk();
1971
1972 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
1973 mutex_lock(&tpacpi_inputdev_send_mutex);
1974
1975 input_report_switch(tpacpi_inputdev,
1976 SW_RFKILL_ALL, !!wlsw);
1977 input_sync(tpacpi_inputdev);
1978
1979 mutex_unlock(&tpacpi_inputdev_send_mutex);
1980 }
1981 hotkey_radio_sw_notify_change();
1982}
1983
1984static void hotkey_exit(void)
1985{
1986#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1987 hotkey_poll_stop_sync();
1988#endif
1989
1990 if (hotkey_dev_attributes)
1991 delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
1992
1993 kfree(hotkey_keycode_map);
1994
1995 if (tp_features.hotkey) {
1996 dbg_printk(TPACPI_DBG_EXIT,
1997 "restoring original hot key mask\n");
1998 /* no short-circuit boolean operator below! */
1999 if ((hotkey_mask_set(hotkey_orig_mask) |
2000 hotkey_status_set(hotkey_orig_status)) != 0)
2001 printk(TPACPI_ERR
2002 "failed to restore hot key mask "
2003 "to BIOS defaults\n");
2004 }
2005}
2006
2007static int __init hotkey_init(struct ibm_init_struct *iibm)
2008{
2009 /* Requirements for changing the default keymaps:
2010 *
2011 * 1. Many of the keys are mapped to KEY_RESERVED for very
2012 * good reasons. Do not change them unless you have deep
2013 * knowledge on the IBM and Lenovo ThinkPad firmware for
2014 * the various ThinkPad models. The driver behaves
2015 * differently for KEY_RESERVED: such keys have their
2016 * hot key mask *unset* in mask_recommended, and also
2017 * in the initial hot key mask programmed into the
2018 * firmware at driver load time, which means the firm-
2019 * ware may react very differently if you change them to
2020 * something else;
2021 *
2022 * 2. You must be subscribed to the linux-thinkpad and
2023 * ibm-acpi-devel mailing lists, and you should read the
2024 * list archives since 2007 if you want to change the
2025 * keymaps. This requirement exists so that you will
2026 * know the past history of problems with the thinkpad-
2027 * acpi driver keymaps, and also that you will be
2028 * listening to any bug reports;
2029 *
2030 * 3. Do not send thinkpad-acpi specific patches directly to
2031 * for merging, *ever*. Send them to the linux-acpi
2032 * mailinglist for comments. Merging is to be done only
2033 * through acpi-test and the ACPI maintainer.
2034 *
2035 * If the above is too much to ask, don't change the keymap.
2036 * Ask the thinkpad-acpi maintainer to do it, instead.
2037 */
2038 static u16 ibm_keycode_map[] __initdata = {
2039 /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
2040 KEY_FN_F1, KEY_FN_F2, KEY_COFFEE, KEY_SLEEP,
2041 KEY_WLAN, KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
2042 KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND,
2043
2044 /* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */
2045 KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */
2046 KEY_UNKNOWN, /* 0x0D: FN+INSERT */
2047 KEY_UNKNOWN, /* 0x0E: FN+DELETE */
2048
2049 /* brightness: firmware always reacts to them, unless
2050 * X.org did some tricks in the radeon BIOS scratch
2051 * registers of *some* models */
2052 KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */
2053 KEY_RESERVED, /* 0x10: FN+END (brightness down) */
2054
2055 /* Thinklight: firmware always react to it */
2056 KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */
2057
2058 KEY_UNKNOWN, /* 0x12: FN+PGDOWN */
2059 KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */
2060
2061 /* Volume: firmware always react to it and reprograms
2062 * the built-in *extra* mixer. Never map it to control
2063 * another mixer by default. */
2064 KEY_RESERVED, /* 0x14: VOLUME UP */
2065 KEY_RESERVED, /* 0x15: VOLUME DOWN */
2066 KEY_RESERVED, /* 0x16: MUTE */
2067
2068 KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */
2069
2070 /* (assignments unknown, please report if found) */
2071 KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
2072 KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
2073 };
2074 static u16 lenovo_keycode_map[] __initdata = {
2075 /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
2076 KEY_FN_F1, KEY_COFFEE, KEY_BATTERY, KEY_SLEEP,
2077 KEY_WLAN, KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
2078 KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND,
2079
2080 /* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */
2081 KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */
2082 KEY_UNKNOWN, /* 0x0D: FN+INSERT */
2083 KEY_UNKNOWN, /* 0x0E: FN+DELETE */
2084
2085 /* These either have to go through ACPI video, or
2086 * act like in the IBM ThinkPads, so don't ever
2087 * enable them by default */
2088 KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */
2089 KEY_RESERVED, /* 0x10: FN+END (brightness down) */
2090
2091 KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */
2092
2093 KEY_UNKNOWN, /* 0x12: FN+PGDOWN */
2094 KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */
2095
2096 /* Volume: z60/z61, T60 (BIOS version?): firmware always
2097 * react to it and reprograms the built-in *extra* mixer.
2098 * Never map it to control another mixer by default.
2099 *
2100 * T60?, T61, R60?, R61: firmware and EC tries to send
2101 * these over the regular keyboard, so these are no-ops,
2102 * but there are still weird bugs re. MUTE, so do not
2103 * change unless you get test reports from all Lenovo
2104 * models. May cause the BIOS to interfere with the
2105 * HDA mixer.
2106 */
2107 KEY_RESERVED, /* 0x14: VOLUME UP */
2108 KEY_RESERVED, /* 0x15: VOLUME DOWN */
2109 KEY_RESERVED, /* 0x16: MUTE */
2110
2111 KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */
2112
2113 /* (assignments unknown, please report if found) */
2114 KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
2115 KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
2116 };
2117
2118#define TPACPI_HOTKEY_MAP_LEN ARRAY_SIZE(ibm_keycode_map)
2119#define TPACPI_HOTKEY_MAP_SIZE sizeof(ibm_keycode_map)
2120#define TPACPI_HOTKEY_MAP_TYPESIZE sizeof(ibm_keycode_map[0])
2121
2122 int res, i;
2123 int status;
2124 int hkeyv;
2125
2126 vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n");
2127
2128 BUG_ON(!tpacpi_inputdev);
2129 BUG_ON(tpacpi_inputdev->open != NULL ||
2130 tpacpi_inputdev->close != NULL);
2131
2132 TPACPI_ACPIHANDLE_INIT(hkey);
2133 mutex_init(&hotkey_mutex);
2134
2135#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
2136 mutex_init(&hotkey_thread_mutex);
2137 mutex_init(&hotkey_thread_data_mutex);
2138#endif
2139
2140 /* hotkey not supported on 570 */
2141 tp_features.hotkey = hkey_handle != NULL;
2142
2143 vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n",
2144 str_supported(tp_features.hotkey));
2145
2146 if (!tp_features.hotkey)
2147 return 1;
2148
2149 tpacpi_disable_brightness_delay();
2150
2151 hotkey_dev_attributes = create_attr_set(13, NULL);
2152 if (!hotkey_dev_attributes)
2153 return -ENOMEM;
2154 res = add_many_to_attr_set(hotkey_dev_attributes,
2155 hotkey_attributes,
2156 ARRAY_SIZE(hotkey_attributes));
2157 if (res)
2158 goto err_exit;
2159
2160 /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
2161 A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking
2162 for HKEY interface version 0x100 */
2163 if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
2164 if ((hkeyv >> 8) != 1) {
2165 printk(TPACPI_ERR "unknown version of the "
2166 "HKEY interface: 0x%x\n", hkeyv);
2167 printk(TPACPI_ERR "please report this to %s\n",
2168 TPACPI_MAIL);
2169 } else {
2170 /*
2171 * MHKV 0x100 in A31, R40, R40e,
2172 * T4x, X31, and later
2173 */
2174 tp_features.hotkey_mask = 1;
2175 }
2176 }
2177
2178 vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n",
2179 str_supported(tp_features.hotkey_mask));
2180
2181 if (tp_features.hotkey_mask) {
2182 if (!acpi_evalf(hkey_handle, &hotkey_all_mask,
2183 "MHKA", "qd")) {
2184 printk(TPACPI_ERR
2185 "missing MHKA handler, "
2186 "please report this to %s\n",
2187 TPACPI_MAIL);
2188 /* FN+F12, FN+F4, FN+F3 */
2189 hotkey_all_mask = 0x080cU;
2190 }
2191 }
2192
2193 /* hotkey_source_mask *must* be zero for
2194 * the first hotkey_mask_get */
2195 res = hotkey_status_get(&hotkey_orig_status);
2196 if (res)
2197 goto err_exit;
2198
2199 if (tp_features.hotkey_mask) {
2200 res = hotkey_mask_get();
2201 if (res)
2202 goto err_exit;
2203
2204 hotkey_orig_mask = hotkey_mask;
2205 res = add_many_to_attr_set(
2206 hotkey_dev_attributes,
2207 hotkey_mask_attributes,
2208 ARRAY_SIZE(hotkey_mask_attributes));
2209 if (res)
2210 goto err_exit;
2211 }
2212
2213#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
2214 if (tp_features.hotkey_mask) {
2215 hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK
2216 & ~hotkey_all_mask;
2217 } else {
2218 hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK;
2219 }
2220
2221 vdbg_printk(TPACPI_DBG_INIT,
2222 "hotkey source mask 0x%08x, polling freq %d\n",
2223 hotkey_source_mask, hotkey_poll_freq);
2224#endif
2225
2226 /* Not all thinkpads have a hardware radio switch */
2227 if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {
2228 tp_features.hotkey_wlsw = 1;
2229 printk(TPACPI_INFO
2230 "radio switch found; radios are %s\n",
2231 enabled(status, 0));
2232 }
2233 if (tp_features.hotkey_wlsw)
2234 res = add_to_attr_set(hotkey_dev_attributes,
2235 &dev_attr_hotkey_radio_sw.attr);
2236
2237 /* For X41t, X60t, X61t Tablets... */
2238 if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
2239 tp_features.hotkey_tablet = 1;
2240 printk(TPACPI_INFO
2241 "possible tablet mode switch found; "
2242 "ThinkPad in %s mode\n",
2243 (status & TP_HOTKEY_TABLET_MASK)?
2244 "tablet" : "laptop");
2245 res = add_to_attr_set(hotkey_dev_attributes,
2246 &dev_attr_hotkey_tablet_mode.attr);
2247 }
2248
2249 if (!res)
2250 res = register_attr_set_with_sysfs(
2251 hotkey_dev_attributes,
2252 &tpacpi_pdev->dev.kobj);
2253 if (res)
2254 goto err_exit;
2255
2256 /* Set up key map */
2257
2258 hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE,
2259 GFP_KERNEL);
2260 if (!hotkey_keycode_map) {
2261 printk(TPACPI_ERR
2262 "failed to allocate memory for key map\n");
2263 res = -ENOMEM;
2264 goto err_exit;
2265 }
2266
2267 if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {
2268 dbg_printk(TPACPI_DBG_INIT,
2269 "using Lenovo default hot key map\n");
2270 memcpy(hotkey_keycode_map, &lenovo_keycode_map,
2271 TPACPI_HOTKEY_MAP_SIZE);
2272 } else {
2273 dbg_printk(TPACPI_DBG_INIT,
2274 "using IBM default hot key map\n");
2275 memcpy(hotkey_keycode_map, &ibm_keycode_map,
2276 TPACPI_HOTKEY_MAP_SIZE);
2277 }
2278
2279 set_bit(EV_KEY, tpacpi_inputdev->evbit);
2280 set_bit(EV_MSC, tpacpi_inputdev->evbit);
2281 set_bit(MSC_SCAN, tpacpi_inputdev->mscbit);
2282 tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE;
2283 tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN;
2284 tpacpi_inputdev->keycode = hotkey_keycode_map;
2285 for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) {
2286 if (hotkey_keycode_map[i] != KEY_RESERVED) {
2287 set_bit(hotkey_keycode_map[i],
2288 tpacpi_inputdev->keybit);
2289 } else {
2290 if (i < sizeof(hotkey_reserved_mask)*8)
2291 hotkey_reserved_mask |= 1 << i;
2292 }
2293 }
2294
2295 if (tp_features.hotkey_wlsw) {
2296 set_bit(EV_SW, tpacpi_inputdev->evbit);
2297 set_bit(SW_RFKILL_ALL, tpacpi_inputdev->swbit);
2298 }
2299 if (tp_features.hotkey_tablet) {
2300 set_bit(EV_SW, tpacpi_inputdev->evbit);
2301 set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit);
2302 }
2303
2304 /* Do not issue duplicate brightness change events to
2305 * userspace */
2306 if (!tp_features.bright_acpimode)
2307 /* update bright_acpimode... */
2308 tpacpi_check_std_acpi_brightness_support();
2309
2310 if (tp_features.bright_acpimode) {
2311 printk(TPACPI_INFO
2312 "This ThinkPad has standard ACPI backlight "
2313 "brightness control, supported by the ACPI "
2314 "video driver\n");
2315 printk(TPACPI_NOTICE
2316 "Disabling thinkpad-acpi brightness events "
2317 "by default...\n");
2318
2319 /* The hotkey_reserved_mask change below is not
2320 * necessary while the keys are at KEY_RESERVED in the
2321 * default map, but better safe than sorry, leave it
2322 * here as a marker of what we have to do, especially
2323 * when we finally become able to set this at runtime
2324 * on response to X.org requests */
2325 hotkey_reserved_mask |=
2326 (1 << TP_ACPI_HOTKEYSCAN_FNHOME)
2327 | (1 << TP_ACPI_HOTKEYSCAN_FNEND);
2328 }
2329
2330 dbg_printk(TPACPI_DBG_INIT, "enabling hot key handling\n");
2331 res = hotkey_status_set(1);
2332 if (res) {
2333 hotkey_exit();
2334 return res;
2335 }
2336 res = hotkey_mask_set(((hotkey_all_mask | hotkey_source_mask)
2337 & ~hotkey_reserved_mask)
2338 | hotkey_orig_mask);
2339 if (res < 0 && res != -ENXIO) {
2340 hotkey_exit();
2341 return res;
2342 }
2343
2344 dbg_printk(TPACPI_DBG_INIT,
2345 "legacy hot key reporting over procfs %s\n",
2346 (hotkey_report_mode < 2) ?
2347 "enabled" : "disabled");
2348
2349 tpacpi_inputdev->open = &hotkey_inputdev_open;
2350 tpacpi_inputdev->close = &hotkey_inputdev_close;
2351
2352 hotkey_poll_setup_safe(1);
2353 tpacpi_send_radiosw_update();
2354 tpacpi_input_send_tabletsw();
2355
2356 return 0;
2357
2358err_exit:
2359 delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
2360 hotkey_dev_attributes = NULL;
2361
2362 return (res < 0)? res : 1;
2363}
2364
2365static void hotkey_notify(struct ibm_struct *ibm, u32 event)
2366{
2367 u32 hkey;
2368 unsigned int scancode;
2369 int send_acpi_ev;
2370 int ignore_acpi_ev;
2371 int unk_ev;
2372
2373 if (event != 0x80) {
2374 printk(TPACPI_ERR
2375 "unknown HKEY notification event %d\n", event);
2376 /* forward it to userspace, maybe it knows how to handle it */
2377 acpi_bus_generate_netlink_event(
2378 ibm->acpi->device->pnp.device_class,
2379 ibm->acpi->device->dev.bus_id,
2380 event, 0);
2381 return;
2382 }
2383
2384 while (1) {
2385 if (!acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) {
2386 printk(TPACPI_ERR "failed to retrieve HKEY event\n");
2387 return;
2388 }
2389
2390 if (hkey == 0) {
2391 /* queue empty */
2392 return;
2393 }
2394
2395 send_acpi_ev = 1;
2396 ignore_acpi_ev = 0;
2397 unk_ev = 0;
2398
2399 switch (hkey >> 12) {
2400 case 1:
2401 /* 0x1000-0x1FFF: key presses */
2402 scancode = hkey & 0xfff;
2403 if (scancode > 0 && scancode < 0x21) {
2404 scancode--;
2405 if (!(hotkey_source_mask & (1 << scancode))) {
2406 tpacpi_input_send_key(scancode);
2407 send_acpi_ev = 0;
2408 } else {
2409 ignore_acpi_ev = 1;
2410 }
2411 } else {
2412 unk_ev = 1;
2413 }
2414 break;
2415 case 2:
2416 /* Wakeup reason */
2417 switch (hkey) {
2418 case 0x2304: /* suspend, undock */
2419 case 0x2404: /* hibernation, undock */
2420 hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK;
2421 ignore_acpi_ev = 1;
2422 break;
2423 case 0x2305: /* suspend, bay eject */
2424 case 0x2405: /* hibernation, bay eject */
2425 hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ;
2426 ignore_acpi_ev = 1;
2427 break;
2428 default:
2429 unk_ev = 1;
2430 }
2431 if (hotkey_wakeup_reason != TP_ACPI_WAKEUP_NONE) {
2432 printk(TPACPI_INFO
2433 "woke up due to a hot-unplug "
2434 "request...\n");
2435 hotkey_wakeup_reason_notify_change();
2436 }
2437 break;
2438 case 3:
2439 /* bay-related wakeups */
2440 if (hkey == 0x3003) {
2441 hotkey_autosleep_ack = 1;
2442 printk(TPACPI_INFO
2443 "bay ejected\n");
2444 hotkey_wakeup_hotunplug_complete_notify_change();
2445 } else {
2446 unk_ev = 1;
2447 }
2448 break;
2449 case 4:
2450 /* dock-related wakeups */
2451 if (hkey == 0x4003) {
2452 hotkey_autosleep_ack = 1;
2453 printk(TPACPI_INFO
2454 "undocked\n");
2455 hotkey_wakeup_hotunplug_complete_notify_change();
2456 } else {
2457 unk_ev = 1;
2458 }
2459 break;
2460 case 5:
2461 /* 0x5000-0x5FFF: human interface helpers */
2462 switch (hkey) {
2463 case 0x5010: /* Lenovo new BIOS: brightness changed */
2464 case 0x500b: /* X61t: tablet pen inserted into bay */
2465 case 0x500c: /* X61t: tablet pen removed from bay */
2466 break;
2467 case 0x5009: /* X41t-X61t: swivel up (tablet mode) */
2468 case 0x500a: /* X41t-X61t: swivel down (normal mode) */
2469 tpacpi_input_send_tabletsw();
2470 hotkey_tablet_mode_notify_change();
2471 send_acpi_ev = 0;
2472 break;
2473 case 0x5001:
2474 case 0x5002:
2475 /* LID switch events. Do not propagate */
2476 ignore_acpi_ev = 1;
2477 break;
2478 default:
2479 unk_ev = 1;
2480 }
2481 break;
2482 case 7:
2483 /* 0x7000-0x7FFF: misc */
2484 if (tp_features.hotkey_wlsw && hkey == 0x7000) {
2485 tpacpi_send_radiosw_update();
2486 send_acpi_ev = 0;
2487 break;
2488 }
2489 /* fallthrough to default */
2490 default:
2491 unk_ev = 1;
2492 }
2493 if (unk_ev) {
2494 printk(TPACPI_NOTICE
2495 "unhandled HKEY event 0x%04x\n", hkey);
2496 }
2497
2498 /* Legacy events */
2499 if (!ignore_acpi_ev &&
2500 (send_acpi_ev || hotkey_report_mode < 2)) {
2501 acpi_bus_generate_proc_event(ibm->acpi->device,
2502 event, hkey);
2503 }
2504
2505 /* netlink events */
2506 if (!ignore_acpi_ev && send_acpi_ev) {
2507 acpi_bus_generate_netlink_event(
2508 ibm->acpi->device->pnp.device_class,
2509 ibm->acpi->device->dev.bus_id,
2510 event, hkey);
2511 }
2512 }
2513}
2514
2515static void hotkey_suspend(pm_message_t state)
2516{
2517 /* Do these on suspend, we get the events on early resume! */
2518 hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE;
2519 hotkey_autosleep_ack = 0;
2520}
2521
2522static void hotkey_resume(void)
2523{
2524 tpacpi_disable_brightness_delay();
2525
2526 if (hotkey_mask_get())
2527 printk(TPACPI_ERR
2528 "error while trying to read hot key mask "
2529 "from firmware\n");
2530 tpacpi_send_radiosw_update();
2531 hotkey_tablet_mode_notify_change();
2532 hotkey_wakeup_reason_notify_change();
2533 hotkey_wakeup_hotunplug_complete_notify_change();
2534 hotkey_poll_setup_safe(0);
2535}
2536
2537/* procfs -------------------------------------------------------------- */
2538static int hotkey_read(char *p)
2539{
2540 int res, status;
2541 int len = 0;
2542
2543 if (!tp_features.hotkey) {
2544 len += sprintf(p + len, "status:\t\tnot supported\n");
2545 return len;
2546 }
2547
2548 if (mutex_lock_interruptible(&hotkey_mutex))
2549 return -ERESTARTSYS;
2550 res = hotkey_status_get(&status);
2551 if (!res)
2552 res = hotkey_mask_get();
2553 mutex_unlock(&hotkey_mutex);
2554 if (res)
2555 return res;
2556
2557 len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
2558 if (tp_features.hotkey_mask) {
2559 len += sprintf(p + len, "mask:\t\t0x%08x\n", hotkey_mask);
2560 len += sprintf(p + len,
2561 "commands:\tenable, disable, reset, <mask>\n");
2562 } else {
2563 len += sprintf(p + len, "mask:\t\tnot supported\n");
2564 len += sprintf(p + len, "commands:\tenable, disable, reset\n");
2565 }
2566
2567 return len;
2568}
2569
2570static int hotkey_write(char *buf)
2571{
2572 int res, status;
2573 u32 mask;
2574 char *cmd;
2575
2576 if (!tp_features.hotkey)
2577 return -ENODEV;
2578
2579 if (mutex_lock_interruptible(&hotkey_mutex))
2580 return -ERESTARTSYS;
2581
2582 status = -1;
2583 mask = hotkey_mask;
2584
2585 res = 0;
2586 while ((cmd = next_cmd(&buf))) {
2587 if (strlencmp(cmd, "enable") == 0) {
2588 status = 1;
2589 } else if (strlencmp(cmd, "disable") == 0) {
2590 status = 0;
2591 } else if (strlencmp(cmd, "reset") == 0) {
2592 status = hotkey_orig_status;
2593 mask = hotkey_orig_mask;
2594 } else if (sscanf(cmd, "0x%x", &mask) == 1) {
2595 /* mask set */
2596 } else if (sscanf(cmd, "%x", &mask) == 1) {
2597 /* mask set */
2598 } else {
2599 res = -EINVAL;
2600 goto errexit;
2601 }
2602 }
2603 if (status != -1)
2604 res = hotkey_status_set(status);
2605
2606 if (!res && mask != hotkey_mask)
2607 res = hotkey_mask_set(mask);
2608
2609errexit:
2610 mutex_unlock(&hotkey_mutex);
2611 return res;
2612}
2613
2614static const struct acpi_device_id ibm_htk_device_ids[] = {
2615 {TPACPI_ACPI_HKEY_HID, 0},
2616 {"", 0},
2617};
2618
2619static struct tp_acpi_drv_struct ibm_hotkey_acpidriver = {
2620 .hid = ibm_htk_device_ids,
2621 .notify = hotkey_notify,
2622 .handle = &hkey_handle,
2623 .type = ACPI_DEVICE_NOTIFY,
2624};
2625
2626static struct ibm_struct hotkey_driver_data = {
2627 .name = "hotkey",
2628 .read = hotkey_read,
2629 .write = hotkey_write,
2630 .exit = hotkey_exit,
2631 .resume = hotkey_resume,
2632 .suspend = hotkey_suspend,
2633 .acpi = &ibm_hotkey_acpidriver,
2634};
2635
2636/*************************************************************************
2637 * Bluetooth subdriver
2638 */
2639
2640enum {
2641 /* ACPI GBDC/SBDC bits */
2642 TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */
2643 TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */
2644 TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */
2645};
2646
2647static struct rfkill *tpacpi_bluetooth_rfkill;
2648
2649static int bluetooth_get_radiosw(void)
2650{
2651 int status;
2652
2653 if (!tp_features.bluetooth)
2654 return -ENODEV;
2655
2656 /* WLSW overrides bluetooth in firmware/hardware, reflect that */
2657 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
2658 return RFKILL_STATE_HARD_BLOCKED;
2659
2660 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
2661 return -EIO;
2662
2663 return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ?
2664 RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
2665}
2666
2667static void bluetooth_update_rfk(void)
2668{
2669 int status;
2670
2671 if (!tpacpi_bluetooth_rfkill)
2672 return;
2673
2674 status = bluetooth_get_radiosw();
2675 if (status < 0)
2676 return;
2677 rfkill_force_state(tpacpi_bluetooth_rfkill, status);
2678}
2679
2680static int bluetooth_set_radiosw(int radio_on, int update_rfk)
2681{
2682 int status;
2683
2684 if (!tp_features.bluetooth)
2685 return -ENODEV;
2686
2687 /* WLSW overrides bluetooth in firmware/hardware, but there is no
2688 * reason to risk weird behaviour. */
2689 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
2690 && radio_on)
2691 return -EPERM;
2692
2693 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
2694 return -EIO;
2695 if (radio_on)
2696 status |= TP_ACPI_BLUETOOTH_RADIOSSW;
2697 else
2698 status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
2699 if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
2700 return -EIO;
2701
2702 if (update_rfk)
2703 bluetooth_update_rfk();
2704
2705 return 0;
2706}
2707
2708/* sysfs bluetooth enable ---------------------------------------------- */
2709static ssize_t bluetooth_enable_show(struct device *dev,
2710 struct device_attribute *attr,
2711 char *buf)
2712{
2713 int status;
2714
2715 status = bluetooth_get_radiosw();
2716 if (status < 0)
2717 return status;
2718
2719 return snprintf(buf, PAGE_SIZE, "%d\n",
2720 (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
2721}
2722
2723static ssize_t bluetooth_enable_store(struct device *dev,
2724 struct device_attribute *attr,
2725 const char *buf, size_t count)
2726{
2727 unsigned long t;
2728 int res;
2729
2730 if (parse_strtoul(buf, 1, &t))
2731 return -EINVAL;
2732
2733 res = bluetooth_set_radiosw(t, 1);
2734
2735 return (res) ? res : count;
2736}
2737
2738static struct device_attribute dev_attr_bluetooth_enable =
2739 __ATTR(bluetooth_enable, S_IWUSR | S_IRUGO,
2740 bluetooth_enable_show, bluetooth_enable_store);
2741
2742/* --------------------------------------------------------------------- */
2743
2744static struct attribute *bluetooth_attributes[] = {
2745 &dev_attr_bluetooth_enable.attr,
2746 NULL
2747};
2748
2749static const struct attribute_group bluetooth_attr_group = {
2750 .attrs = bluetooth_attributes,
2751};
2752
2753static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state)
2754{
2755 int bts = bluetooth_get_radiosw();
2756
2757 if (bts < 0)
2758 return bts;
2759
2760 *state = bts;
2761 return 0;
2762}
2763
2764static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state)
2765{
2766 return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
2767}
2768
2769static void bluetooth_exit(void)
2770{
2771 if (tpacpi_bluetooth_rfkill)
2772 rfkill_unregister(tpacpi_bluetooth_rfkill);
2773
2774 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
2775 &bluetooth_attr_group);
2776}
2777
2778static int __init bluetooth_init(struct ibm_init_struct *iibm)
2779{
2780 int res;
2781 int status = 0;
2782
2783 vdbg_printk(TPACPI_DBG_INIT, "initializing bluetooth subdriver\n");
2784
2785 TPACPI_ACPIHANDLE_INIT(hkey);
2786
2787 /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
2788 G4x, R30, R31, R40e, R50e, T20-22, X20-21 */
2789 tp_features.bluetooth = hkey_handle &&
2790 acpi_evalf(hkey_handle, &status, "GBDC", "qd");
2791
2792 vdbg_printk(TPACPI_DBG_INIT, "bluetooth is %s, status 0x%02x\n",
2793 str_supported(tp_features.bluetooth),
2794 status);
2795
2796 if (tp_features.bluetooth &&
2797 !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {
2798 /* no bluetooth hardware present in system */
2799 tp_features.bluetooth = 0;
2800 dbg_printk(TPACPI_DBG_INIT,
2801 "bluetooth hardware not installed\n");
2802 }
2803
2804 if (!tp_features.bluetooth)
2805 return 1;
2806
2807 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
2808 &bluetooth_attr_group);
2809 if (res)
2810 return res;
2811
2812 res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID,
2813 &tpacpi_bluetooth_rfkill,
2814 RFKILL_TYPE_BLUETOOTH,
2815 "tpacpi_bluetooth_sw",
2816 tpacpi_bluetooth_rfk_set,
2817 tpacpi_bluetooth_rfk_get);
2818 if (res) {
2819 bluetooth_exit();
2820 return res;
2821 }
2822
2823 return 0;
2824}
2825
2826/* procfs -------------------------------------------------------------- */
2827static int bluetooth_read(char *p)
2828{
2829 int len = 0;
2830 int status = bluetooth_get_radiosw();
2831
2832 if (!tp_features.bluetooth)
2833 len += sprintf(p + len, "status:\t\tnot supported\n");
2834 else {
2835 len += sprintf(p + len, "status:\t\t%s\n",
2836 (status == RFKILL_STATE_UNBLOCKED) ?
2837 "enabled" : "disabled");
2838 len += sprintf(p + len, "commands:\tenable, disable\n");
2839 }
2840
2841 return len;
2842}
2843
2844static int bluetooth_write(char *buf)
2845{
2846 char *cmd;
2847
2848 if (!tp_features.bluetooth)
2849 return -ENODEV;
2850
2851 while ((cmd = next_cmd(&buf))) {
2852 if (strlencmp(cmd, "enable") == 0) {
2853 bluetooth_set_radiosw(1, 1);
2854 } else if (strlencmp(cmd, "disable") == 0) {
2855 bluetooth_set_radiosw(0, 1);
2856 } else
2857 return -EINVAL;
2858 }
2859
2860 return 0;
2861}
2862
2863static struct ibm_struct bluetooth_driver_data = {
2864 .name = "bluetooth",
2865 .read = bluetooth_read,
2866 .write = bluetooth_write,
2867 .exit = bluetooth_exit,
2868};
2869
2870/*************************************************************************
2871 * Wan subdriver
2872 */
2873
2874enum {
2875 /* ACPI GWAN/SWAN bits */
2876 TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */
2877 TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */
2878 TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */
2879};
2880
2881static struct rfkill *tpacpi_wan_rfkill;
2882
2883static int wan_get_radiosw(void)
2884{
2885 int status;
2886
2887 if (!tp_features.wan)
2888 return -ENODEV;
2889
2890 /* WLSW overrides WWAN in firmware/hardware, reflect that */
2891 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
2892 return RFKILL_STATE_HARD_BLOCKED;
2893
2894 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
2895 return -EIO;
2896
2897 return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ?
2898 RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
2899}
2900
2901static void wan_update_rfk(void)
2902{
2903 int status;
2904
2905 if (!tpacpi_wan_rfkill)
2906 return;
2907
2908 status = wan_get_radiosw();
2909 if (status < 0)
2910 return;
2911 rfkill_force_state(tpacpi_wan_rfkill, status);
2912}
2913
2914static int wan_set_radiosw(int radio_on, int update_rfk)
2915{
2916 int status;
2917
2918 if (!tp_features.wan)
2919 return -ENODEV;
2920
2921 /* WLSW overrides bluetooth in firmware/hardware, but there is no
2922 * reason to risk weird behaviour. */
2923 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
2924 && radio_on)
2925 return -EPERM;
2926
2927 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
2928 return -EIO;
2929 if (radio_on)
2930 status |= TP_ACPI_WANCARD_RADIOSSW;
2931 else
2932 status &= ~TP_ACPI_WANCARD_RADIOSSW;
2933 if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
2934 return -EIO;
2935
2936 if (update_rfk)
2937 wan_update_rfk();
2938
2939 return 0;
2940}
2941
2942/* sysfs wan enable ---------------------------------------------------- */
2943static ssize_t wan_enable_show(struct device *dev,
2944 struct device_attribute *attr,
2945 char *buf)
2946{
2947 int status;
2948
2949 status = wan_get_radiosw();
2950 if (status < 0)
2951 return status;
2952
2953 return snprintf(buf, PAGE_SIZE, "%d\n",
2954 (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
2955}
2956
2957static ssize_t wan_enable_store(struct device *dev,
2958 struct device_attribute *attr,
2959 const char *buf, size_t count)
2960{
2961 unsigned long t;
2962 int res;
2963
2964 if (parse_strtoul(buf, 1, &t))
2965 return -EINVAL;
2966
2967 res = wan_set_radiosw(t, 1);
2968
2969 return (res) ? res : count;
2970}
2971
2972static struct device_attribute dev_attr_wan_enable =
2973 __ATTR(wwan_enable, S_IWUSR | S_IRUGO,
2974 wan_enable_show, wan_enable_store);
2975
2976/* --------------------------------------------------------------------- */
2977
2978static struct attribute *wan_attributes[] = {
2979 &dev_attr_wan_enable.attr,
2980 NULL
2981};
2982
2983static const struct attribute_group wan_attr_group = {
2984 .attrs = wan_attributes,
2985};
2986
2987static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state)
2988{
2989 int wans = wan_get_radiosw();
2990
2991 if (wans < 0)
2992 return wans;
2993
2994 *state = wans;
2995 return 0;
2996}
2997
2998static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state)
2999{
3000 return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
3001}
3002
3003static void wan_exit(void)
3004{
3005 if (tpacpi_wan_rfkill)
3006 rfkill_unregister(tpacpi_wan_rfkill);
3007
3008 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
3009 &wan_attr_group);
3010}
3011
3012static int __init wan_init(struct ibm_init_struct *iibm)
3013{
3014 int res;
3015 int status = 0;
3016
3017 vdbg_printk(TPACPI_DBG_INIT, "initializing wan subdriver\n");
3018
3019 TPACPI_ACPIHANDLE_INIT(hkey);
3020
3021 tp_features.wan = hkey_handle &&
3022 acpi_evalf(hkey_handle, &status, "GWAN", "qd");
3023
3024 vdbg_printk(TPACPI_DBG_INIT, "wan is %s, status 0x%02x\n",
3025 str_supported(tp_features.wan),
3026 status);
3027
3028 if (tp_features.wan &&
3029 !(status & TP_ACPI_WANCARD_HWPRESENT)) {
3030 /* no wan hardware present in system */
3031 tp_features.wan = 0;
3032 dbg_printk(TPACPI_DBG_INIT,
3033 "wan hardware not installed\n");
3034 }
3035
3036 if (!tp_features.wan)
3037 return 1;
3038
3039 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
3040 &wan_attr_group);
3041 if (res)
3042 return res;
3043
3044 res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID,
3045 &tpacpi_wan_rfkill,
3046 RFKILL_TYPE_WWAN,
3047 "tpacpi_wwan_sw",
3048 tpacpi_wan_rfk_set,
3049 tpacpi_wan_rfk_get);
3050 if (res) {
3051 wan_exit();
3052 return res;
3053 }
3054
3055 return 0;
3056}
3057
3058/* procfs -------------------------------------------------------------- */
3059static int wan_read(char *p)
3060{
3061 int len = 0;
3062 int status = wan_get_radiosw();
3063
3064 if (!tp_features.wan)
3065 len += sprintf(p + len, "status:\t\tnot supported\n");
3066 else {
3067 len += sprintf(p + len, "status:\t\t%s\n",
3068 (status == RFKILL_STATE_UNBLOCKED) ?
3069 "enabled" : "disabled");
3070 len += sprintf(p + len, "commands:\tenable, disable\n");
3071 }
3072
3073 return len;
3074}
3075
3076static int wan_write(char *buf)
3077{
3078 char *cmd;
3079
3080 if (!tp_features.wan)
3081 return -ENODEV;
3082
3083 while ((cmd = next_cmd(&buf))) {
3084 if (strlencmp(cmd, "enable") == 0) {
3085 wan_set_radiosw(1, 1);
3086 } else if (strlencmp(cmd, "disable") == 0) {
3087 wan_set_radiosw(0, 1);
3088 } else
3089 return -EINVAL;
3090 }
3091
3092 return 0;
3093}
3094
3095static struct ibm_struct wan_driver_data = {
3096 .name = "wan",
3097 .read = wan_read,
3098 .write = wan_write,
3099 .exit = wan_exit,
3100};
3101
3102/*************************************************************************
3103 * Video subdriver
3104 */
3105
3106#ifdef CONFIG_THINKPAD_ACPI_VIDEO
3107
3108enum video_access_mode {
3109 TPACPI_VIDEO_NONE = 0,
3110 TPACPI_VIDEO_570, /* 570 */
3111 TPACPI_VIDEO_770, /* 600e/x, 770e, 770x */
3112 TPACPI_VIDEO_NEW, /* all others */
3113};
3114
3115enum { /* video status flags, based on VIDEO_570 */
3116 TP_ACPI_VIDEO_S_LCD = 0x01, /* LCD output enabled */
3117 TP_ACPI_VIDEO_S_CRT = 0x02, /* CRT output enabled */
3118 TP_ACPI_VIDEO_S_DVI = 0x08, /* DVI output enabled */
3119};
3120
3121enum { /* TPACPI_VIDEO_570 constants */
3122 TP_ACPI_VIDEO_570_PHSCMD = 0x87, /* unknown magic constant :( */
3123 TP_ACPI_VIDEO_570_PHSMASK = 0x03, /* PHS bits that map to
3124 * video_status_flags */
3125 TP_ACPI_VIDEO_570_PHS2CMD = 0x8b, /* unknown magic constant :( */
3126 TP_ACPI_VIDEO_570_PHS2SET = 0x80, /* unknown magic constant :( */
3127};
3128
3129static enum video_access_mode video_supported;
3130static int video_orig_autosw;
3131
3132static int video_autosw_get(void);
3133static int video_autosw_set(int enable);
3134
3135TPACPI_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */
3136
3137static int __init video_init(struct ibm_init_struct *iibm)
3138{
3139 int ivga;
3140
3141 vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n");
3142
3143 TPACPI_ACPIHANDLE_INIT(vid);
3144 TPACPI_ACPIHANDLE_INIT(vid2);
3145
3146 if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
3147 /* G41, assume IVGA doesn't change */
3148 vid_handle = vid2_handle;
3149
3150 if (!vid_handle)
3151 /* video switching not supported on R30, R31 */
3152 video_supported = TPACPI_VIDEO_NONE;
3153 else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
3154 /* 570 */
3155 video_supported = TPACPI_VIDEO_570;
3156 else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
3157 /* 600e/x, 770e, 770x */
3158 video_supported = TPACPI_VIDEO_770;
3159 else
3160 /* all others */
3161 video_supported = TPACPI_VIDEO_NEW;
3162
3163 vdbg_printk(TPACPI_DBG_INIT, "video is %s, mode %d\n",
3164 str_supported(video_supported != TPACPI_VIDEO_NONE),
3165 video_supported);
3166
3167 return (video_supported != TPACPI_VIDEO_NONE)? 0 : 1;
3168}
3169
3170static void video_exit(void)
3171{
3172 dbg_printk(TPACPI_DBG_EXIT,
3173 "restoring original video autoswitch mode\n");
3174 if (video_autosw_set(video_orig_autosw))
3175 printk(TPACPI_ERR "error while trying to restore original "
3176 "video autoswitch mode\n");
3177}
3178
3179static int video_outputsw_get(void)
3180{
3181 int status = 0;
3182 int i;
3183
3184 switch (video_supported) {
3185 case TPACPI_VIDEO_570:
3186 if (!acpi_evalf(NULL, &i, "\\_SB.PHS", "dd",
3187 TP_ACPI_VIDEO_570_PHSCMD))
3188 return -EIO;
3189 status = i & TP_ACPI_VIDEO_570_PHSMASK;
3190 break;
3191 case TPACPI_VIDEO_770:
3192 if (!acpi_evalf(NULL, &i, "\\VCDL", "d"))
3193 return -EIO;
3194 if (i)
3195 status |= TP_ACPI_VIDEO_S_LCD;
3196 if (!acpi_evalf(NULL, &i, "\\VCDC", "d"))
3197 return -EIO;
3198 if (i)
3199 status |= TP_ACPI_VIDEO_S_CRT;
3200 break;
3201 case TPACPI_VIDEO_NEW:
3202 if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1) ||
3203 !acpi_evalf(NULL, &i, "\\VCDC", "d"))
3204 return -EIO;
3205 if (i)
3206 status |= TP_ACPI_VIDEO_S_CRT;
3207
3208 if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0) ||
3209 !acpi_evalf(NULL, &i, "\\VCDL", "d"))
3210 return -EIO;
3211 if (i)
3212 status |= TP_ACPI_VIDEO_S_LCD;
3213 if (!acpi_evalf(NULL, &i, "\\VCDD", "d"))
3214 return -EIO;
3215 if (i)
3216 status |= TP_ACPI_VIDEO_S_DVI;
3217 break;
3218 default:
3219 return -ENOSYS;
3220 }
3221
3222 return status;
3223}
3224
3225static int video_outputsw_set(int status)
3226{
3227 int autosw;
3228 int res = 0;
3229
3230 switch (video_supported) {
3231 case TPACPI_VIDEO_570:
3232 res = acpi_evalf(NULL, NULL,
3233 "\\_SB.PHS2", "vdd",
3234 TP_ACPI_VIDEO_570_PHS2CMD,
3235 status | TP_ACPI_VIDEO_570_PHS2SET);
3236 break;
3237 case TPACPI_VIDEO_770:
3238 autosw = video_autosw_get();
3239 if (autosw < 0)
3240 return autosw;
3241
3242 res = video_autosw_set(1);
3243 if (res)
3244 return res;
3245 res = acpi_evalf(vid_handle, NULL,
3246 "ASWT", "vdd", status * 0x100, 0);
3247 if (!autosw && video_autosw_set(autosw)) {
3248 printk(TPACPI_ERR
3249 "video auto-switch left enabled due to error\n");
3250 return -EIO;
3251 }
3252 break;
3253 case TPACPI_VIDEO_NEW:
3254 res = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) &&
3255 acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1);
3256 break;
3257 default:
3258 return -ENOSYS;
3259 }
3260
3261 return (res)? 0 : -EIO;
3262}
3263
3264static int video_autosw_get(void)
3265{
3266 int autosw = 0;
3267
3268 switch (video_supported) {
3269 case TPACPI_VIDEO_570:
3270 if (!acpi_evalf(vid_handle, &autosw, "SWIT", "d"))
3271 return -EIO;
3272 break;
3273 case TPACPI_VIDEO_770:
3274 case TPACPI_VIDEO_NEW:
3275 if (!acpi_evalf(vid_handle, &autosw, "^VDEE", "d"))
3276 return -EIO;
3277 break;
3278 default:
3279 return -ENOSYS;
3280 }
3281
3282 return autosw & 1;
3283}
3284
3285static int video_autosw_set(int enable)
3286{
3287 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", (enable)? 1 : 0))
3288 return -EIO;
3289 return 0;
3290}
3291
3292static int video_outputsw_cycle(void)
3293{
3294 int autosw = video_autosw_get();
3295 int res;
3296
3297 if (autosw < 0)
3298 return autosw;
3299
3300 switch (video_supported) {
3301 case TPACPI_VIDEO_570:
3302 res = video_autosw_set(1);
3303 if (res)
3304 return res;
3305 res = acpi_evalf(ec_handle, NULL, "_Q16", "v");
3306 break;
3307 case TPACPI_VIDEO_770:
3308 case TPACPI_VIDEO_NEW:
3309 res = video_autosw_set(1);
3310 if (res)
3311 return res;
3312 res = acpi_evalf(vid_handle, NULL, "VSWT", "v");
3313 break;
3314 default:
3315 return -ENOSYS;
3316 }
3317 if (!autosw && video_autosw_set(autosw)) {
3318 printk(TPACPI_ERR
3319 "video auto-switch left enabled due to error\n");
3320 return -EIO;
3321 }
3322
3323 return (res)? 0 : -EIO;
3324}
3325
3326static int video_expand_toggle(void)
3327{
3328 switch (video_supported) {
3329 case TPACPI_VIDEO_570:
3330 return acpi_evalf(ec_handle, NULL, "_Q17", "v")?
3331 0 : -EIO;
3332 case TPACPI_VIDEO_770:
3333 return acpi_evalf(vid_handle, NULL, "VEXP", "v")?
3334 0 : -EIO;
3335 case TPACPI_VIDEO_NEW:
3336 return acpi_evalf(NULL, NULL, "\\VEXP", "v")?
3337 0 : -EIO;
3338 default:
3339 return -ENOSYS;
3340 }
3341 /* not reached */
3342}
3343
3344static int video_read(char *p)
3345{
3346 int status, autosw;
3347 int len = 0;
3348
3349 if (video_supported == TPACPI_VIDEO_NONE) {
3350 len += sprintf(p + len, "status:\t\tnot supported\n");
3351 return len;
3352 }
3353
3354 status = video_outputsw_get();
3355 if (status < 0)
3356 return status;
3357
3358 autosw = video_autosw_get();
3359 if (autosw < 0)
3360 return autosw;
3361
3362 len += sprintf(p + len, "status:\t\tsupported\n");
3363 len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
3364 len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
3365 if (video_supported == TPACPI_VIDEO_NEW)
3366 len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
3367 len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
3368 len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
3369 len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
3370 if (video_supported == TPACPI_VIDEO_NEW)
3371 len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
3372 len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
3373 len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
3374
3375 return len;
3376}
3377
3378static int video_write(char *buf)
3379{
3380 char *cmd;
3381 int enable, disable, status;
3382 int res;
3383
3384 if (video_supported == TPACPI_VIDEO_NONE)
3385 return -ENODEV;
3386
3387 enable = 0;
3388 disable = 0;
3389
3390 while ((cmd = next_cmd(&buf))) {
3391 if (strlencmp(cmd, "lcd_enable") == 0) {
3392 enable |= TP_ACPI_VIDEO_S_LCD;
3393 } else if (strlencmp(cmd, "lcd_disable") == 0) {
3394 disable |= TP_ACPI_VIDEO_S_LCD;
3395 } else if (strlencmp(cmd, "crt_enable") == 0) {
3396 enable |= TP_ACPI_VIDEO_S_CRT;
3397 } else if (strlencmp(cmd, "crt_disable") == 0) {
3398 disable |= TP_ACPI_VIDEO_S_CRT;
3399 } else if (video_supported == TPACPI_VIDEO_NEW &&
3400 strlencmp(cmd, "dvi_enable") == 0) {
3401 enable |= TP_ACPI_VIDEO_S_DVI;
3402 } else if (video_supported == TPACPI_VIDEO_NEW &&
3403 strlencmp(cmd, "dvi_disable") == 0) {
3404 disable |= TP_ACPI_VIDEO_S_DVI;
3405 } else if (strlencmp(cmd, "auto_enable") == 0) {
3406 res = video_autosw_set(1);
3407 if (res)
3408 return res;
3409 } else if (strlencmp(cmd, "auto_disable") == 0) {
3410 res = video_autosw_set(0);
3411 if (res)
3412 return res;
3413 } else if (strlencmp(cmd, "video_switch") == 0) {
3414 res = video_outputsw_cycle();
3415 if (res)
3416 return res;
3417 } else if (strlencmp(cmd, "expand_toggle") == 0) {
3418 res = video_expand_toggle();
3419 if (res)
3420 return res;
3421 } else
3422 return -EINVAL;
3423 }
3424
3425 if (enable || disable) {
3426 status = video_outputsw_get();
3427 if (status < 0)
3428 return status;
3429 res = video_outputsw_set((status & ~disable) | enable);
3430 if (res)
3431 return res;
3432 }
3433
3434 return 0;
3435}
3436
3437static struct ibm_struct video_driver_data = {
3438 .name = "video",
3439 .read = video_read,
3440 .write = video_write,
3441 .exit = video_exit,
3442};
3443
3444#endif /* CONFIG_THINKPAD_ACPI_VIDEO */
3445
3446/*************************************************************************
3447 * Light (thinklight) subdriver
3448 */
3449
3450TPACPI_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */
3451TPACPI_HANDLE(ledb, ec, "LEDB"); /* G4x */
3452
3453static int light_get_status(void)
3454{
3455 int status = 0;
3456
3457 if (tp_features.light_status) {
3458 if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
3459 return -EIO;
3460 return (!!status);
3461 }
3462
3463 return -ENXIO;
3464}
3465
3466static int light_set_status(int status)
3467{
3468 int rc;
3469
3470 if (tp_features.light) {
3471 if (cmos_handle) {
3472 rc = acpi_evalf(cmos_handle, NULL, NULL, "vd",
3473 (status)?
3474 TP_CMOS_THINKLIGHT_ON :
3475 TP_CMOS_THINKLIGHT_OFF);
3476 } else {
3477 rc = acpi_evalf(lght_handle, NULL, NULL, "vd",
3478 (status)? 1 : 0);
3479 }
3480 return (rc)? 0 : -EIO;
3481 }
3482
3483 return -ENXIO;
3484}
3485
3486static void light_set_status_worker(struct work_struct *work)
3487{
3488 struct tpacpi_led_classdev *data =
3489 container_of(work, struct tpacpi_led_classdev, work);
3490
3491 if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
3492 light_set_status((data->new_brightness != LED_OFF));
3493}
3494
3495static void light_sysfs_set(struct led_classdev *led_cdev,
3496 enum led_brightness brightness)
3497{
3498 struct tpacpi_led_classdev *data =
3499 container_of(led_cdev,
3500 struct tpacpi_led_classdev,
3501 led_classdev);
3502 data->new_brightness = brightness;
3503 queue_work(tpacpi_wq, &data->work);
3504}
3505
3506static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev)
3507{
3508 return (light_get_status() == 1)? LED_FULL : LED_OFF;
3509}
3510
3511static struct tpacpi_led_classdev tpacpi_led_thinklight = {
3512 .led_classdev = {
3513 .name = "tpacpi::thinklight",
3514 .brightness_set = &light_sysfs_set,
3515 .brightness_get = &light_sysfs_get,
3516 }
3517};
3518
3519static int __init light_init(struct ibm_init_struct *iibm)
3520{
3521 int rc;
3522
3523 vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
3524
3525 TPACPI_ACPIHANDLE_INIT(ledb);
3526 TPACPI_ACPIHANDLE_INIT(lght);
3527 TPACPI_ACPIHANDLE_INIT(cmos);
3528 INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker);
3529
3530 /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
3531 tp_features.light = (cmos_handle || lght_handle) && !ledb_handle;
3532
3533 if (tp_features.light)
3534 /* light status not supported on
3535 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */
3536 tp_features.light_status =
3537 acpi_evalf(ec_handle, NULL, "KBLT", "qv");
3538
3539 vdbg_printk(TPACPI_DBG_INIT, "light is %s, light status is %s\n",
3540 str_supported(tp_features.light),
3541 str_supported(tp_features.light_status));
3542
3543 if (!tp_features.light)
3544 return 1;
3545
3546 rc = led_classdev_register(&tpacpi_pdev->dev,
3547 &tpacpi_led_thinklight.led_classdev);
3548
3549 if (rc < 0) {
3550 tp_features.light = 0;
3551 tp_features.light_status = 0;
3552 } else {
3553 rc = 0;
3554 }
3555
3556 return rc;
3557}
3558
3559static void light_exit(void)
3560{
3561 led_classdev_unregister(&tpacpi_led_thinklight.led_classdev);
3562 if (work_pending(&tpacpi_led_thinklight.work))
3563 flush_workqueue(tpacpi_wq);
3564}
3565
3566static int light_read(char *p)
3567{
3568 int len = 0;
3569 int status;
3570
3571 if (!tp_features.light) {
3572 len += sprintf(p + len, "status:\t\tnot supported\n");
3573 } else if (!tp_features.light_status) {
3574 len += sprintf(p + len, "status:\t\tunknown\n");
3575 len += sprintf(p + len, "commands:\ton, off\n");
3576 } else {
3577 status = light_get_status();
3578 if (status < 0)
3579 return status;
3580 len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
3581 len += sprintf(p + len, "commands:\ton, off\n");
3582 }
3583
3584 return len;
3585}
3586
3587static int light_write(char *buf)
3588{
3589 char *cmd;
3590 int newstatus = 0;
3591
3592 if (!tp_features.light)
3593 return -ENODEV;
3594
3595 while ((cmd = next_cmd(&buf))) {
3596 if (strlencmp(cmd, "on") == 0) {
3597 newstatus = 1;
3598 } else if (strlencmp(cmd, "off") == 0) {
3599 newstatus = 0;
3600 } else
3601 return -EINVAL;
3602 }
3603
3604 return light_set_status(newstatus);
3605}
3606
3607static struct ibm_struct light_driver_data = {
3608 .name = "light",
3609 .read = light_read,
3610 .write = light_write,
3611 .exit = light_exit,
3612};
3613
3614/*************************************************************************
3615 * Dock subdriver
3616 */
3617
3618#ifdef CONFIG_THINKPAD_ACPI_DOCK
3619
3620static void dock_notify(struct ibm_struct *ibm, u32 event);
3621static int dock_read(char *p);
3622static int dock_write(char *buf);
3623
3624TPACPI_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */
3625 "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
3626 "\\_SB.PCI0.PCI1.DOCK", /* all others */
3627 "\\_SB.PCI.ISA.SLCE", /* 570 */
3628 ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
3629
3630/* don't list other alternatives as we install a notify handler on the 570 */
3631TPACPI_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */
3632
3633static const struct acpi_device_id ibm_pci_device_ids[] = {
3634 {PCI_ROOT_HID_STRING, 0},
3635 {"", 0},
3636};
3637
3638static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = {
3639 {
3640 .notify = dock_notify,
3641 .handle = &dock_handle,
3642 .type = ACPI_SYSTEM_NOTIFY,
3643 },
3644 {
3645 /* THIS ONE MUST NEVER BE USED FOR DRIVER AUTOLOADING.
3646 * We just use it to get notifications of dock hotplug
3647 * in very old thinkpads */
3648 .hid = ibm_pci_device_ids,
3649 .notify = dock_notify,
3650 .handle = &pci_handle,
3651 .type = ACPI_SYSTEM_NOTIFY,
3652 },
3653};
3654
3655static struct ibm_struct dock_driver_data[2] = {
3656 {
3657 .name = "dock",
3658 .read = dock_read,
3659 .write = dock_write,
3660 .acpi = &ibm_dock_acpidriver[0],
3661 },
3662 {
3663 .name = "dock",
3664 .acpi = &ibm_dock_acpidriver[1],
3665 },
3666};
3667
3668#define dock_docked() (_sta(dock_handle) & 1)
3669
3670static int __init dock_init(struct ibm_init_struct *iibm)
3671{
3672 vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver\n");
3673
3674 TPACPI_ACPIHANDLE_INIT(dock);
3675
3676 vdbg_printk(TPACPI_DBG_INIT, "dock is %s\n",
3677 str_supported(dock_handle != NULL));
3678
3679 return (dock_handle)? 0 : 1;
3680}
3681
3682static int __init dock_init2(struct ibm_init_struct *iibm)
3683{
3684 int dock2_needed;
3685
3686 vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver part 2\n");
3687
3688 if (dock_driver_data[0].flags.acpi_driver_registered &&
3689 dock_driver_data[0].flags.acpi_notify_installed) {
3690 TPACPI_ACPIHANDLE_INIT(pci);
3691 dock2_needed = (pci_handle != NULL);
3692 vdbg_printk(TPACPI_DBG_INIT,
3693 "dock PCI handler for the TP 570 is %s\n",
3694 str_supported(dock2_needed));
3695 } else {
3696 vdbg_printk(TPACPI_DBG_INIT,
3697 "dock subdriver part 2 not required\n");
3698 dock2_needed = 0;
3699 }
3700
3701 return (dock2_needed)? 0 : 1;
3702}
3703
3704static void dock_notify(struct ibm_struct *ibm, u32 event)
3705{
3706 int docked = dock_docked();
3707 int pci = ibm->acpi->hid && ibm->acpi->device &&
3708 acpi_match_device_ids(ibm->acpi->device, ibm_pci_device_ids);
3709 int data;
3710
3711 if (event == 1 && !pci) /* 570 */
3712 data = 1; /* button */
3713 else if (event == 1 && pci) /* 570 */
3714 data = 3; /* dock */
3715 else if (event == 3 && docked)
3716 data = 1; /* button */
3717 else if (event == 3 && !docked)
3718 data = 2; /* undock */
3719 else if (event == 0 && docked)
3720 data = 3; /* dock */
3721 else {
3722 printk(TPACPI_ERR "unknown dock event %d, status %d\n",
3723 event, _sta(dock_handle));
3724 data = 0; /* unknown */
3725 }
3726 acpi_bus_generate_proc_event(ibm->acpi->device, event, data);
3727 acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
3728 ibm->acpi->device->dev.bus_id,
3729 event, data);
3730}
3731
3732static int dock_read(char *p)
3733{
3734 int len = 0;
3735 int docked = dock_docked();
3736
3737 if (!dock_handle)
3738 len += sprintf(p + len, "status:\t\tnot supported\n");
3739 else if (!docked)
3740 len += sprintf(p + len, "status:\t\tundocked\n");
3741 else {
3742 len += sprintf(p + len, "status:\t\tdocked\n");
3743 len += sprintf(p + len, "commands:\tdock, undock\n");
3744 }
3745
3746 return len;
3747}
3748
3749static int dock_write(char *buf)
3750{
3751 char *cmd;
3752
3753 if (!dock_docked())
3754 return -ENODEV;
3755
3756 while ((cmd = next_cmd(&buf))) {
3757 if (strlencmp(cmd, "undock") == 0) {
3758 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
3759 !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
3760 return -EIO;
3761 } else if (strlencmp(cmd, "dock") == 0) {
3762 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
3763 return -EIO;
3764 } else
3765 return -EINVAL;
3766 }
3767
3768 return 0;
3769}
3770
3771#endif /* CONFIG_THINKPAD_ACPI_DOCK */
3772
3773/*************************************************************************
3774 * Bay subdriver
3775 */
3776
3777#ifdef CONFIG_THINKPAD_ACPI_BAY
3778
3779TPACPI_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */
3780 "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */
3781 "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */
3782 "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */
3783 ); /* A21e, R30, R31 */
3784TPACPI_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */
3785 "_EJ0", /* all others */
3786 ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
3787TPACPI_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */
3788 "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */
3789 ); /* all others */
3790TPACPI_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */
3791 "_EJ0", /* 770x */
3792 ); /* all others */
3793
3794static int __init bay_init(struct ibm_init_struct *iibm)
3795{
3796 vdbg_printk(TPACPI_DBG_INIT, "initializing bay subdriver\n");
3797
3798 TPACPI_ACPIHANDLE_INIT(bay);
3799 if (bay_handle)
3800 TPACPI_ACPIHANDLE_INIT(bay_ej);
3801 TPACPI_ACPIHANDLE_INIT(bay2);
3802 if (bay2_handle)
3803 TPACPI_ACPIHANDLE_INIT(bay2_ej);
3804
3805 tp_features.bay_status = bay_handle &&
3806 acpi_evalf(bay_handle, NULL, "_STA", "qv");
3807 tp_features.bay_status2 = bay2_handle &&
3808 acpi_evalf(bay2_handle, NULL, "_STA", "qv");
3809
3810 tp_features.bay_eject = bay_handle && bay_ej_handle &&
3811 (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental);
3812 tp_features.bay_eject2 = bay2_handle && bay2_ej_handle &&
3813 (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental);
3814
3815 vdbg_printk(TPACPI_DBG_INIT,
3816 "bay 1: status %s, eject %s; bay 2: status %s, eject %s\n",
3817 str_supported(tp_features.bay_status),
3818 str_supported(tp_features.bay_eject),
3819 str_supported(tp_features.bay_status2),
3820 str_supported(tp_features.bay_eject2));
3821
3822 return (tp_features.bay_status || tp_features.bay_eject ||
3823 tp_features.bay_status2 || tp_features.bay_eject2)? 0 : 1;
3824}
3825
3826static void bay_notify(struct ibm_struct *ibm, u32 event)
3827{
3828 acpi_bus_generate_proc_event(ibm->acpi->device, event, 0);
3829 acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
3830 ibm->acpi->device->dev.bus_id,
3831 event, 0);
3832}
3833
3834#define bay_occupied(b) (_sta(b##_handle) & 1)
3835
3836static int bay_read(char *p)
3837{
3838 int len = 0;
3839 int occupied = bay_occupied(bay);
3840 int occupied2 = bay_occupied(bay2);
3841 int eject, eject2;
3842
3843 len += sprintf(p + len, "status:\t\t%s\n",
3844 tp_features.bay_status ?
3845 (occupied ? "occupied" : "unoccupied") :
3846 "not supported");
3847 if (tp_features.bay_status2)
3848 len += sprintf(p + len, "status2:\t%s\n", occupied2 ?
3849 "occupied" : "unoccupied");
3850
3851 eject = tp_features.bay_eject && occupied;
3852 eject2 = tp_features.bay_eject2 && occupied2;
3853
3854 if (eject && eject2)
3855 len += sprintf(p + len, "commands:\teject, eject2\n");
3856 else if (eject)
3857 len += sprintf(p + len, "commands:\teject\n");
3858 else if (eject2)
3859 len += sprintf(p + len, "commands:\teject2\n");
3860
3861 return len;
3862}
3863
3864static int bay_write(char *buf)
3865{
3866 char *cmd;
3867
3868 if (!tp_features.bay_eject && !tp_features.bay_eject2)
3869 return -ENODEV;
3870
3871 while ((cmd = next_cmd(&buf))) {
3872 if (tp_features.bay_eject && strlencmp(cmd, "eject") == 0) {
3873 if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1))
3874 return -EIO;
3875 } else if (tp_features.bay_eject2 &&
3876 strlencmp(cmd, "eject2") == 0) {
3877 if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1))
3878 return -EIO;
3879 } else
3880 return -EINVAL;
3881 }
3882
3883 return 0;
3884}
3885
3886static struct tp_acpi_drv_struct ibm_bay_acpidriver = {
3887 .notify = bay_notify,
3888 .handle = &bay_handle,
3889 .type = ACPI_SYSTEM_NOTIFY,
3890};
3891
3892static struct ibm_struct bay_driver_data = {
3893 .name = "bay",
3894 .read = bay_read,
3895 .write = bay_write,
3896 .acpi = &ibm_bay_acpidriver,
3897};
3898
3899#endif /* CONFIG_THINKPAD_ACPI_BAY */
3900
3901/*************************************************************************
3902 * CMOS subdriver
3903 */
3904
3905/* sysfs cmos_command -------------------------------------------------- */
3906static ssize_t cmos_command_store(struct device *dev,
3907 struct device_attribute *attr,
3908 const char *buf, size_t count)
3909{
3910 unsigned long cmos_cmd;
3911 int res;
3912
3913 if (parse_strtoul(buf, 21, &cmos_cmd))
3914 return -EINVAL;
3915
3916 res = issue_thinkpad_cmos_command(cmos_cmd);
3917 return (res)? res : count;
3918}
3919
3920static struct device_attribute dev_attr_cmos_command =
3921 __ATTR(cmos_command, S_IWUSR, NULL, cmos_command_store);
3922
3923/* --------------------------------------------------------------------- */
3924
3925static int __init cmos_init(struct ibm_init_struct *iibm)
3926{
3927 int res;
3928
3929 vdbg_printk(TPACPI_DBG_INIT,
3930 "initializing cmos commands subdriver\n");
3931
3932 TPACPI_ACPIHANDLE_INIT(cmos);
3933
3934 vdbg_printk(TPACPI_DBG_INIT, "cmos commands are %s\n",
3935 str_supported(cmos_handle != NULL));
3936
3937 res = device_create_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
3938 if (res)
3939 return res;
3940
3941 return (cmos_handle)? 0 : 1;
3942}
3943
3944static void cmos_exit(void)
3945{
3946 device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
3947}
3948
3949static int cmos_read(char *p)
3950{
3951 int len = 0;
3952
3953 /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
3954 R30, R31, T20-22, X20-21 */
3955 if (!cmos_handle)
3956 len += sprintf(p + len, "status:\t\tnot supported\n");
3957 else {
3958 len += sprintf(p + len, "status:\t\tsupported\n");
3959 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n");
3960 }
3961
3962 return len;
3963}
3964
3965static int cmos_write(char *buf)
3966{
3967 char *cmd;
3968 int cmos_cmd, res;
3969
3970 while ((cmd = next_cmd(&buf))) {
3971 if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
3972 cmos_cmd >= 0 && cmos_cmd <= 21) {
3973 /* cmos_cmd set */
3974 } else
3975 return -EINVAL;
3976
3977 res = issue_thinkpad_cmos_command(cmos_cmd);
3978 if (res)
3979 return res;
3980 }
3981
3982 return 0;
3983}
3984
3985static struct ibm_struct cmos_driver_data = {
3986 .name = "cmos",
3987 .read = cmos_read,
3988 .write = cmos_write,
3989 .exit = cmos_exit,
3990};
3991
3992/*************************************************************************
3993 * LED subdriver
3994 */
3995
3996enum led_access_mode {
3997 TPACPI_LED_NONE = 0,
3998 TPACPI_LED_570, /* 570 */
3999 TPACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
4000 TPACPI_LED_NEW, /* all others */
4001};
4002
4003enum { /* For TPACPI_LED_OLD */
4004 TPACPI_LED_EC_HLCL = 0x0c, /* EC reg to get led to power on */
4005 TPACPI_LED_EC_HLBL = 0x0d, /* EC reg to blink a lit led */
4006 TPACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */
4007};
4008
4009enum led_status_t {
4010 TPACPI_LED_OFF = 0,
4011 TPACPI_LED_ON,
4012 TPACPI_LED_BLINK,
4013};
4014
4015static enum led_access_mode led_supported;
4016
4017TPACPI_HANDLE(led, ec, "SLED", /* 570 */
4018 "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, */
4019 /* T20-22, X20-21 */
4020 "LED", /* all others */
4021 ); /* R30, R31 */
4022
4023#define TPACPI_LED_NUMLEDS 8
4024static struct tpacpi_led_classdev *tpacpi_leds;
4025static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS];
4026static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
4027 /* there's a limit of 19 chars + NULL before 2.6.26 */
4028 "tpacpi::power",
4029 "tpacpi:orange:batt",
4030 "tpacpi:green:batt",
4031 "tpacpi::dock_active",
4032 "tpacpi::bay_active",
4033 "tpacpi::dock_batt",
4034 "tpacpi::unknown_led",
4035 "tpacpi::standby",
4036};
4037
4038static int led_get_status(const unsigned int led)
4039{
4040 int status;
4041 enum led_status_t led_s;
4042
4043 switch (led_supported) {
4044 case TPACPI_LED_570:
4045 if (!acpi_evalf(ec_handle,
4046 &status, "GLED", "dd", 1 << led))
4047 return -EIO;
4048 led_s = (status == 0)?
4049 TPACPI_LED_OFF :
4050 ((status == 1)?
4051 TPACPI_LED_ON :
4052 TPACPI_LED_BLINK);
4053 tpacpi_led_state_cache[led] = led_s;
4054 return led_s;
4055 default:
4056 return -ENXIO;
4057 }
4058
4059 /* not reached */
4060}
4061
4062static int led_set_status(const unsigned int led,
4063 const enum led_status_t ledstatus)
4064{
4065 /* off, on, blink. Index is led_status_t */
4066 static const unsigned int led_sled_arg1[] = { 0, 1, 3 };
4067 static const unsigned int led_led_arg1[] = { 0, 0x80, 0xc0 };
4068
4069 int rc = 0;
4070
4071 switch (led_supported) {
4072 case TPACPI_LED_570:
4073 /* 570 */
4074 if (led > 7)
4075 return -EINVAL;
4076 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
4077 (1 << led), led_sled_arg1[ledstatus]))
4078 rc = -EIO;
4079 break;
4080 case TPACPI_LED_OLD:
4081 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
4082 if (led > 7)
4083 return -EINVAL;
4084 rc = ec_write(TPACPI_LED_EC_HLMS, (1 << led));
4085 if (rc >= 0)
4086 rc = ec_write(TPACPI_LED_EC_HLBL,
4087 (ledstatus == TPACPI_LED_BLINK) << led);
4088 if (rc >= 0)
4089 rc = ec_write(TPACPI_LED_EC_HLCL,
4090 (ledstatus != TPACPI_LED_OFF) << led);
4091 break;
4092 case TPACPI_LED_NEW:
4093 /* all others */
4094 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
4095 led, led_led_arg1[ledstatus]))
4096 rc = -EIO;
4097 break;
4098 default:
4099 rc = -ENXIO;
4100 }
4101
4102 if (!rc)
4103 tpacpi_led_state_cache[led] = ledstatus;
4104
4105 return rc;
4106}
4107
4108static void led_sysfs_set_status(unsigned int led,
4109 enum led_brightness brightness)
4110{
4111 led_set_status(led,
4112 (brightness == LED_OFF) ?
4113 TPACPI_LED_OFF :
4114 (tpacpi_led_state_cache[led] == TPACPI_LED_BLINK) ?
4115 TPACPI_LED_BLINK : TPACPI_LED_ON);
4116}
4117
4118static void led_set_status_worker(struct work_struct *work)
4119{
4120 struct tpacpi_led_classdev *data =
4121 container_of(work, struct tpacpi_led_classdev, work);
4122
4123 if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
4124 led_sysfs_set_status(data->led, data->new_brightness);
4125}
4126
4127static void led_sysfs_set(struct led_classdev *led_cdev,
4128 enum led_brightness brightness)
4129{
4130 struct tpacpi_led_classdev *data = container_of(led_cdev,
4131 struct tpacpi_led_classdev, led_classdev);
4132
4133 data->new_brightness = brightness;
4134 queue_work(tpacpi_wq, &data->work);
4135}
4136
4137static int led_sysfs_blink_set(struct led_classdev *led_cdev,
4138 unsigned long *delay_on, unsigned long *delay_off)
4139{
4140 struct tpacpi_led_classdev *data = container_of(led_cdev,
4141 struct tpacpi_led_classdev, led_classdev);
4142
4143 /* Can we choose the flash rate? */
4144 if (*delay_on == 0 && *delay_off == 0) {
4145 /* yes. set them to the hardware blink rate (1 Hz) */
4146 *delay_on = 500; /* ms */
4147 *delay_off = 500; /* ms */
4148 } else if ((*delay_on != 500) || (*delay_off != 500))
4149 return -EINVAL;
4150
4151 data->new_brightness = TPACPI_LED_BLINK;
4152 queue_work(tpacpi_wq, &data->work);
4153
4154 return 0;
4155}
4156
4157static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev)
4158{
4159 int rc;
4160
4161 struct tpacpi_led_classdev *data = container_of(led_cdev,
4162 struct tpacpi_led_classdev, led_classdev);
4163
4164 rc = led_get_status(data->led);
4165
4166 if (rc == TPACPI_LED_OFF || rc < 0)
4167 rc = LED_OFF; /* no error handling in led class :( */
4168 else
4169 rc = LED_FULL;
4170
4171 return rc;
4172}
4173
4174static void led_exit(void)
4175{
4176 unsigned int i;
4177
4178 for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
4179 if (tpacpi_leds[i].led_classdev.name)
4180 led_classdev_unregister(&tpacpi_leds[i].led_classdev);
4181 }
4182
4183 kfree(tpacpi_leds);
4184}
4185
4186static int __init led_init(struct ibm_init_struct *iibm)
4187{
4188 unsigned int i;
4189 int rc;
4190
4191 vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
4192
4193 TPACPI_ACPIHANDLE_INIT(led);
4194
4195 if (!led_handle)
4196 /* led not supported on R30, R31 */
4197 led_supported = TPACPI_LED_NONE;
4198 else if (strlencmp(led_path, "SLED") == 0)
4199 /* 570 */
4200 led_supported = TPACPI_LED_570;
4201 else if (strlencmp(led_path, "SYSL") == 0)
4202 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
4203 led_supported = TPACPI_LED_OLD;
4204 else
4205 /* all others */
4206 led_supported = TPACPI_LED_NEW;
4207
4208 vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
4209 str_supported(led_supported), led_supported);
4210
4211 tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS,
4212 GFP_KERNEL);
4213 if (!tpacpi_leds) {
4214 printk(TPACPI_ERR "Out of memory for LED data\n");
4215 return -ENOMEM;
4216 }
4217
4218 for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
4219 tpacpi_leds[i].led = i;
4220
4221 tpacpi_leds[i].led_classdev.brightness_set = &led_sysfs_set;
4222 tpacpi_leds[i].led_classdev.blink_set = &led_sysfs_blink_set;
4223 if (led_supported == TPACPI_LED_570)
4224 tpacpi_leds[i].led_classdev.brightness_get =
4225 &led_sysfs_get;
4226
4227 tpacpi_leds[i].led_classdev.name = tpacpi_led_names[i];
4228
4229 INIT_WORK(&tpacpi_leds[i].work, led_set_status_worker);
4230
4231 rc = led_classdev_register(&tpacpi_pdev->dev,
4232 &tpacpi_leds[i].led_classdev);
4233 if (rc < 0) {
4234 tpacpi_leds[i].led_classdev.name = NULL;
4235 led_exit();
4236 return rc;
4237 }
4238 }
4239
4240 return (led_supported != TPACPI_LED_NONE)? 0 : 1;
4241}
4242
4243#define str_led_status(s) \
4244 ((s) == TPACPI_LED_OFF ? "off" : \
4245 ((s) == TPACPI_LED_ON ? "on" : "blinking"))
4246
4247static int led_read(char *p)
4248{
4249 int len = 0;
4250
4251 if (!led_supported) {
4252 len += sprintf(p + len, "status:\t\tnot supported\n");
4253 return len;
4254 }
4255 len += sprintf(p + len, "status:\t\tsupported\n");
4256
4257 if (led_supported == TPACPI_LED_570) {
4258 /* 570 */
4259 int i, status;
4260 for (i = 0; i < 8; i++) {
4261 status = led_get_status(i);
4262 if (status < 0)
4263 return -EIO;
4264 len += sprintf(p + len, "%d:\t\t%s\n",
4265 i, str_led_status(status));
4266 }
4267 }
4268
4269 len += sprintf(p + len, "commands:\t"
4270 "<led> on, <led> off, <led> blink (<led> is 0-7)\n");
4271
4272 return len;
4273}
4274
4275static int led_write(char *buf)
4276{
4277 char *cmd;
4278 int led, rc;
4279 enum led_status_t s;
4280
4281 if (!led_supported)
4282 return -ENODEV;
4283
4284 while ((cmd = next_cmd(&buf))) {
4285 if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
4286 return -EINVAL;
4287
4288 if (strstr(cmd, "off")) {
4289 s = TPACPI_LED_OFF;
4290 } else if (strstr(cmd, "on")) {
4291 s = TPACPI_LED_ON;
4292 } else if (strstr(cmd, "blink")) {
4293 s = TPACPI_LED_BLINK;
4294 } else {
4295 return -EINVAL;
4296 }
4297
4298 rc = led_set_status(led, s);
4299 if (rc < 0)
4300 return rc;
4301 }
4302
4303 return 0;
4304}
4305
4306static struct ibm_struct led_driver_data = {
4307 .name = "led",
4308 .read = led_read,
4309 .write = led_write,
4310 .exit = led_exit,
4311};
4312
4313/*************************************************************************
4314 * Beep subdriver
4315 */
4316
4317TPACPI_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */
4318
4319static int __init beep_init(struct ibm_init_struct *iibm)
4320{
4321 vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n");
4322
4323 TPACPI_ACPIHANDLE_INIT(beep);
4324
4325 vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n",
4326 str_supported(beep_handle != NULL));
4327
4328 return (beep_handle)? 0 : 1;
4329}
4330
4331static int beep_read(char *p)
4332{
4333 int len = 0;
4334
4335 if (!beep_handle)
4336 len += sprintf(p + len, "status:\t\tnot supported\n");
4337 else {
4338 len += sprintf(p + len, "status:\t\tsupported\n");
4339 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n");
4340 }
4341
4342 return len;
4343}
4344
4345static int beep_write(char *buf)
4346{
4347 char *cmd;
4348 int beep_cmd;
4349
4350 if (!beep_handle)
4351 return -ENODEV;
4352
4353 while ((cmd = next_cmd(&buf))) {
4354 if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
4355 beep_cmd >= 0 && beep_cmd <= 17) {
4356 /* beep_cmd set */
4357 } else
4358 return -EINVAL;
4359 if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0))
4360 return -EIO;
4361 }
4362
4363 return 0;
4364}
4365
4366static struct ibm_struct beep_driver_data = {
4367 .name = "beep",
4368 .read = beep_read,
4369 .write = beep_write,
4370};
4371
4372/*************************************************************************
4373 * Thermal subdriver
4374 */
4375
4376enum thermal_access_mode {
4377 TPACPI_THERMAL_NONE = 0, /* No thermal support */
4378 TPACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */
4379 TPACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */
4380 TPACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */
4381 TPACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */
4382};
4383
4384enum { /* TPACPI_THERMAL_TPEC_* */
4385 TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */
4386 TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */
4387 TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */
4388};
4389
4390#define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */
4391struct ibm_thermal_sensors_struct {
4392 s32 temp[TPACPI_MAX_THERMAL_SENSORS];
4393};
4394
4395static enum thermal_access_mode thermal_read_mode;
4396
4397/* idx is zero-based */
4398static int thermal_get_sensor(int idx, s32 *value)
4399{
4400 int t;
4401 s8 tmp;
4402 char tmpi[5];
4403
4404 t = TP_EC_THERMAL_TMP0;
4405
4406 switch (thermal_read_mode) {
4407#if TPACPI_MAX_THERMAL_SENSORS >= 16
4408 case TPACPI_THERMAL_TPEC_16:
4409 if (idx >= 8 && idx <= 15) {
4410 t = TP_EC_THERMAL_TMP8;
4411 idx -= 8;
4412 }
4413 /* fallthrough */
4414#endif
4415 case TPACPI_THERMAL_TPEC_8:
4416 if (idx <= 7) {
4417 if (!acpi_ec_read(t + idx, &tmp))
4418 return -EIO;
4419 *value = tmp * 1000;
4420 return 0;
4421 }
4422 break;
4423
4424 case TPACPI_THERMAL_ACPI_UPDT:
4425 if (idx <= 7) {
4426 snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
4427 if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))
4428 return -EIO;
4429 if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
4430 return -EIO;
4431 *value = (t - 2732) * 100;
4432 return 0;
4433 }
4434 break;
4435
4436 case TPACPI_THERMAL_ACPI_TMP07:
4437 if (idx <= 7) {
4438 snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
4439 if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
4440 return -EIO;
4441 if (t > 127 || t < -127)
4442 t = TP_EC_THERMAL_TMP_NA;
4443 *value = t * 1000;
4444 return 0;
4445 }
4446 break;
4447
4448 case TPACPI_THERMAL_NONE:
4449 default:
4450 return -ENOSYS;
4451 }
4452
4453 return -EINVAL;
4454}
4455
4456static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
4457{
4458 int res, i;
4459 int n;
4460
4461 n = 8;
4462 i = 0;
4463
4464 if (!s)
4465 return -EINVAL;
4466
4467 if (thermal_read_mode == TPACPI_THERMAL_TPEC_16)
4468 n = 16;
4469
4470 for (i = 0 ; i < n; i++) {
4471 res = thermal_get_sensor(i, &s->temp[i]);
4472 if (res)
4473 return res;
4474 }
4475
4476 return n;
4477}
4478
4479/* sysfs temp##_input -------------------------------------------------- */
4480
4481static ssize_t thermal_temp_input_show(struct device *dev,
4482 struct device_attribute *attr,
4483 char *buf)
4484{
4485 struct sensor_device_attribute *sensor_attr =
4486 to_sensor_dev_attr(attr);
4487 int idx = sensor_attr->index;
4488 s32 value;
4489 int res;
4490
4491 res = thermal_get_sensor(idx, &value);
4492 if (res)
4493 return res;
4494 if (value == TP_EC_THERMAL_TMP_NA * 1000)
4495 return -ENXIO;
4496
4497 return snprintf(buf, PAGE_SIZE, "%d\n", value);
4498}
4499
4500#define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \
4501 SENSOR_ATTR(temp##_idxA##_input, S_IRUGO, \
4502 thermal_temp_input_show, NULL, _idxB)
4503
4504static struct sensor_device_attribute sensor_dev_attr_thermal_temp_input[] = {
4505 THERMAL_SENSOR_ATTR_TEMP(1, 0),
4506 THERMAL_SENSOR_ATTR_TEMP(2, 1),
4507 THERMAL_SENSOR_ATTR_TEMP(3, 2),
4508 THERMAL_SENSOR_ATTR_TEMP(4, 3),
4509 THERMAL_SENSOR_ATTR_TEMP(5, 4),
4510 THERMAL_SENSOR_ATTR_TEMP(6, 5),
4511 THERMAL_SENSOR_ATTR_TEMP(7, 6),
4512 THERMAL_SENSOR_ATTR_TEMP(8, 7),
4513 THERMAL_SENSOR_ATTR_TEMP(9, 8),
4514 THERMAL_SENSOR_ATTR_TEMP(10, 9),
4515 THERMAL_SENSOR_ATTR_TEMP(11, 10),
4516 THERMAL_SENSOR_ATTR_TEMP(12, 11),
4517 THERMAL_SENSOR_ATTR_TEMP(13, 12),
4518 THERMAL_SENSOR_ATTR_TEMP(14, 13),
4519 THERMAL_SENSOR_ATTR_TEMP(15, 14),
4520 THERMAL_SENSOR_ATTR_TEMP(16, 15),
4521};
4522
4523#define THERMAL_ATTRS(X) \
4524 &sensor_dev_attr_thermal_temp_input[X].dev_attr.attr
4525
4526static struct attribute *thermal_temp_input_attr[] = {
4527 THERMAL_ATTRS(8),
4528 THERMAL_ATTRS(9),
4529 THERMAL_ATTRS(10),
4530 THERMAL_ATTRS(11),
4531 THERMAL_ATTRS(12),
4532 THERMAL_ATTRS(13),
4533 THERMAL_ATTRS(14),
4534 THERMAL_ATTRS(15),
4535 THERMAL_ATTRS(0),
4536 THERMAL_ATTRS(1),
4537 THERMAL_ATTRS(2),
4538 THERMAL_ATTRS(3),
4539 THERMAL_ATTRS(4),
4540 THERMAL_ATTRS(5),
4541 THERMAL_ATTRS(6),
4542 THERMAL_ATTRS(7),
4543 NULL
4544};
4545
4546static const struct attribute_group thermal_temp_input16_group = {
4547 .attrs = thermal_temp_input_attr
4548};
4549
4550static const struct attribute_group thermal_temp_input8_group = {
4551 .attrs = &thermal_temp_input_attr[8]
4552};
4553
4554#undef THERMAL_SENSOR_ATTR_TEMP
4555#undef THERMAL_ATTRS
4556
4557/* --------------------------------------------------------------------- */
4558
4559static int __init thermal_init(struct ibm_init_struct *iibm)
4560{
4561 u8 t, ta1, ta2;
4562 int i;
4563 int acpi_tmp7;
4564 int res;
4565
4566 vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n");
4567
4568 acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
4569
4570 if (thinkpad_id.ec_model) {
4571 /*
4572 * Direct EC access mode: sensors at registers
4573 * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for
4574 * non-implemented, thermal sensors return 0x80 when
4575 * not available
4576 */
4577
4578 ta1 = ta2 = 0;
4579 for (i = 0; i < 8; i++) {
4580 if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) {
4581 ta1 |= t;
4582 } else {
4583 ta1 = 0;
4584 break;
4585 }
4586 if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
4587 ta2 |= t;
4588 } else {
4589 ta1 = 0;
4590 break;
4591 }
4592 }
4593 if (ta1 == 0) {
4594 /* This is sheer paranoia, but we handle it anyway */
4595 if (acpi_tmp7) {
4596 printk(TPACPI_ERR
4597 "ThinkPad ACPI EC access misbehaving, "
4598 "falling back to ACPI TMPx access "
4599 "mode\n");
4600 thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
4601 } else {
4602 printk(TPACPI_ERR
4603 "ThinkPad ACPI EC access misbehaving, "
4604 "disabling thermal sensors access\n");
4605 thermal_read_mode = TPACPI_THERMAL_NONE;
4606 }
4607 } else {
4608 thermal_read_mode =
4609 (ta2 != 0) ?
4610 TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
4611 }
4612 } else if (acpi_tmp7) {
4613 if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
4614 /* 600e/x, 770e, 770x */
4615 thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT;
4616 } else {
4617 /* Standard ACPI TMPx access, max 8 sensors */
4618 thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
4619 }
4620 } else {
4621 /* temperatures not supported on 570, G4x, R30, R31, R32 */
4622 thermal_read_mode = TPACPI_THERMAL_NONE;
4623 }
4624
4625 vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n",
4626 str_supported(thermal_read_mode != TPACPI_THERMAL_NONE),
4627 thermal_read_mode);
4628
4629 switch (thermal_read_mode) {
4630 case TPACPI_THERMAL_TPEC_16:
4631 res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
4632 &thermal_temp_input16_group);
4633 if (res)
4634 return res;
4635 break;
4636 case TPACPI_THERMAL_TPEC_8:
4637 case TPACPI_THERMAL_ACPI_TMP07:
4638 case TPACPI_THERMAL_ACPI_UPDT:
4639 res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
4640 &thermal_temp_input8_group);
4641 if (res)
4642 return res;
4643 break;
4644 case TPACPI_THERMAL_NONE:
4645 default:
4646 return 1;
4647 }
4648
4649 return 0;
4650}
4651
4652static void thermal_exit(void)
4653{
4654 switch (thermal_read_mode) {
4655 case TPACPI_THERMAL_TPEC_16:
4656 sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
4657 &thermal_temp_input16_group);
4658 break;
4659 case TPACPI_THERMAL_TPEC_8:
4660 case TPACPI_THERMAL_ACPI_TMP07:
4661 case TPACPI_THERMAL_ACPI_UPDT:
4662 sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
4663 &thermal_temp_input16_group);
4664 break;
4665 case TPACPI_THERMAL_NONE:
4666 default:
4667 break;
4668 }
4669}
4670
4671static int thermal_read(char *p)
4672{
4673 int len = 0;
4674 int n, i;
4675 struct ibm_thermal_sensors_struct t;
4676
4677 n = thermal_get_sensors(&t);
4678 if (unlikely(n < 0))
4679 return n;
4680
4681 len += sprintf(p + len, "temperatures:\t");
4682
4683 if (n > 0) {
4684 for (i = 0; i < (n - 1); i++)
4685 len += sprintf(p + len, "%d ", t.temp[i] / 1000);
4686 len += sprintf(p + len, "%d\n", t.temp[i] / 1000);
4687 } else
4688 len += sprintf(p + len, "not supported\n");
4689
4690 return len;
4691}
4692
4693static struct ibm_struct thermal_driver_data = {
4694 .name = "thermal",
4695 .read = thermal_read,
4696 .exit = thermal_exit,
4697};
4698
4699/*************************************************************************
4700 * EC Dump subdriver
4701 */
4702
4703static u8 ecdump_regs[256];
4704
4705static int ecdump_read(char *p)
4706{
4707 int len = 0;
4708 int i, j;
4709 u8 v;
4710
4711 len += sprintf(p + len, "EC "
4712 " +00 +01 +02 +03 +04 +05 +06 +07"
4713 " +08 +09 +0a +0b +0c +0d +0e +0f\n");
4714 for (i = 0; i < 256; i += 16) {
4715 len += sprintf(p + len, "EC 0x%02x:", i);
4716 for (j = 0; j < 16; j++) {
4717 if (!acpi_ec_read(i + j, &v))
4718 break;
4719 if (v != ecdump_regs[i + j])
4720 len += sprintf(p + len, " *%02x", v);
4721 else
4722 len += sprintf(p + len, " %02x", v);
4723 ecdump_regs[i + j] = v;
4724 }
4725 len += sprintf(p + len, "\n");
4726 if (j != 16)
4727 break;
4728 }
4729
4730 /* These are way too dangerous to advertise openly... */
4731#if 0
4732 len += sprintf(p + len, "commands:\t0x<offset> 0x<value>"
4733 " (<offset> is 00-ff, <value> is 00-ff)\n");
4734 len += sprintf(p + len, "commands:\t0x<offset> <value> "
4735 " (<offset> is 00-ff, <value> is 0-255)\n");
4736#endif
4737 return len;
4738}
4739
4740static int ecdump_write(char *buf)
4741{
4742 char *cmd;
4743 int i, v;
4744
4745 while ((cmd = next_cmd(&buf))) {
4746 if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) {
4747 /* i and v set */
4748 } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) {
4749 /* i and v set */
4750 } else
4751 return -EINVAL;
4752 if (i >= 0 && i < 256 && v >= 0 && v < 256) {
4753 if (!acpi_ec_write(i, v))
4754 return -EIO;
4755 } else
4756 return -EINVAL;
4757 }
4758
4759 return 0;
4760}
4761
4762static struct ibm_struct ecdump_driver_data = {
4763 .name = "ecdump",
4764 .read = ecdump_read,
4765 .write = ecdump_write,
4766 .flags.experimental = 1,
4767};
4768
4769/*************************************************************************
4770 * Backlight/brightness subdriver
4771 */
4772
4773#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen"
4774
4775enum {
4776 TP_EC_BACKLIGHT = 0x31,
4777
4778 /* TP_EC_BACKLIGHT bitmasks */
4779 TP_EC_BACKLIGHT_LVLMSK = 0x1F,
4780 TP_EC_BACKLIGHT_CMDMSK = 0xE0,
4781 TP_EC_BACKLIGHT_MAPSW = 0x20,
4782};
4783
4784static struct backlight_device *ibm_backlight_device;
4785static int brightness_mode;
4786static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */
4787
4788static struct mutex brightness_mutex;
4789
4790/*
4791 * ThinkPads can read brightness from two places: EC 0x31, or
4792 * CMOS NVRAM byte 0x5E, bits 0-3.
4793 *
4794 * EC 0x31 has the following layout
4795 * Bit 7: unknown function
4796 * Bit 6: unknown function
4797 * Bit 5: Z: honour scale changes, NZ: ignore scale changes
4798 * Bit 4: must be set to zero to avoid problems
4799 * Bit 3-0: backlight brightness level
4800 *
4801 * brightness_get_raw returns status data in the EC 0x31 layout
4802 */
4803static int brightness_get_raw(int *status)
4804{
4805 u8 lec = 0, lcmos = 0, level = 0;
4806
4807 if (brightness_mode & 1) {
4808 if (!acpi_ec_read(TP_EC_BACKLIGHT, &lec))
4809 return -EIO;
4810 level = lec & TP_EC_BACKLIGHT_LVLMSK;
4811 };
4812 if (brightness_mode & 2) {
4813 lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
4814 & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
4815 >> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
4816 lcmos &= (tp_features.bright_16levels)? 0x0f : 0x07;
4817 level = lcmos;
4818 }
4819
4820 if (brightness_mode == 3) {
4821 *status = lec; /* Prefer EC, CMOS is just a backing store */
4822 lec &= TP_EC_BACKLIGHT_LVLMSK;
4823 if (lec == lcmos)
4824 tp_warned.bright_cmos_ec_unsync = 0;
4825 else {
4826 if (!tp_warned.bright_cmos_ec_unsync) {
4827 printk(TPACPI_ERR
4828 "CMOS NVRAM (%u) and EC (%u) do not "
4829 "agree on display brightness level\n",
4830 (unsigned int) lcmos,
4831 (unsigned int) lec);
4832 tp_warned.bright_cmos_ec_unsync = 1;
4833 }
4834 return -EIO;
4835 }
4836 } else {
4837 *status = level;
4838 }
4839
4840 return 0;
4841}
4842
4843/* May return EINTR which can always be mapped to ERESTARTSYS */
4844static int brightness_set(int value)
4845{
4846 int cmos_cmd, inc, i, res;
4847 int current_value;
4848 int command_bits;
4849
4850 if (value > ((tp_features.bright_16levels)? 15 : 7) ||
4851 value < 0)
4852 return -EINVAL;
4853
4854 res = mutex_lock_interruptible(&brightness_mutex);
4855 if (res < 0)
4856 return res;
4857
4858 res = brightness_get_raw(&current_value);
4859 if (res < 0)
4860 goto errout;
4861
4862 command_bits = current_value & TP_EC_BACKLIGHT_CMDMSK;
4863 current_value &= TP_EC_BACKLIGHT_LVLMSK;
4864
4865 cmos_cmd = value > current_value ?
4866 TP_CMOS_BRIGHTNESS_UP :
4867 TP_CMOS_BRIGHTNESS_DOWN;
4868 inc = (value > current_value)? 1 : -1;
4869
4870 res = 0;
4871 for (i = current_value; i != value; i += inc) {
4872 if ((brightness_mode & 2) &&
4873 issue_thinkpad_cmos_command(cmos_cmd)) {
4874 res = -EIO;
4875 goto errout;
4876 }
4877 if ((brightness_mode & 1) &&
4878 !acpi_ec_write(TP_EC_BACKLIGHT,
4879 (i + inc) | command_bits)) {
4880 res = -EIO;
4881 goto errout;;
4882 }
4883 }
4884
4885errout:
4886 mutex_unlock(&brightness_mutex);
4887 return res;
4888}
4889
4890/* sysfs backlight class ----------------------------------------------- */
4891
4892static int brightness_update_status(struct backlight_device *bd)
4893{
4894 /* it is the backlight class's job (caller) to handle
4895 * EINTR and other errors properly */
4896 return brightness_set(
4897 (bd->props.fb_blank == FB_BLANK_UNBLANK &&
4898 bd->props.power == FB_BLANK_UNBLANK) ?
4899 bd->props.brightness : 0);
4900}
4901
4902static int brightness_get(struct backlight_device *bd)
4903{
4904 int status, res;
4905
4906 res = brightness_get_raw(&status);
4907 if (res < 0)
4908 return 0; /* FIXME: teach backlight about error handling */
4909
4910 return status & TP_EC_BACKLIGHT_LVLMSK;
4911}
4912
4913static struct backlight_ops ibm_backlight_data = {
4914 .get_brightness = brightness_get,
4915 .update_status = brightness_update_status,
4916};
4917
4918/* --------------------------------------------------------------------- */
4919
4920static int __init brightness_init(struct ibm_init_struct *iibm)
4921{
4922 int b;
4923
4924 vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n");
4925
4926 mutex_init(&brightness_mutex);
4927
4928 /*
4929 * We always attempt to detect acpi support, so as to switch
4930 * Lenovo Vista BIOS to ACPI brightness mode even if we are not
4931 * going to publish a backlight interface
4932 */
4933 b = tpacpi_check_std_acpi_brightness_support();
4934 if (b > 0) {
4935
4936 if (acpi_video_backlight_support()) {
4937 if (brightness_enable > 1) {
4938 printk(TPACPI_NOTICE
4939 "Standard ACPI backlight interface "
4940 "available, not loading native one.\n");
4941 return 1;
4942 } else if (brightness_enable == 1) {
4943 printk(TPACPI_NOTICE
4944 "Backlight control force enabled, even if standard "
4945 "ACPI backlight interface is available\n");
4946 }
4947 } else {
4948 if (brightness_enable > 1) {
4949 printk(TPACPI_NOTICE
4950 "Standard ACPI backlight interface not "
4951 "available, thinkpad_acpi native "
4952 "brightness control enabled\n");
4953 }
4954 }
4955 }
4956
4957 if (!brightness_enable) {
4958 dbg_printk(TPACPI_DBG_INIT,
4959 "brightness support disabled by "
4960 "module parameter\n");
4961 return 1;
4962 }
4963
4964 if (b > 16) {
4965 printk(TPACPI_ERR
4966 "Unsupported brightness interface, "
4967 "please contact %s\n", TPACPI_MAIL);
4968 return 1;
4969 }
4970 if (b == 16)
4971 tp_features.bright_16levels = 1;
4972
4973 if (!brightness_mode) {
4974 if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO)
4975 brightness_mode = 2;
4976 else
4977 brightness_mode = 3;
4978
4979 dbg_printk(TPACPI_DBG_INIT, "selected brightness_mode=%d\n",
4980 brightness_mode);
4981 }
4982
4983 if (brightness_mode > 3)
4984 return -EINVAL;
4985
4986 if (brightness_get_raw(&b) < 0)
4987 return 1;
4988
4989 if (tp_features.bright_16levels)
4990 printk(TPACPI_INFO
4991 "detected a 16-level brightness capable ThinkPad\n");
4992
4993 ibm_backlight_device = backlight_device_register(
4994 TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
4995 &ibm_backlight_data);
4996 if (IS_ERR(ibm_backlight_device)) {
4997 printk(TPACPI_ERR "Could not register backlight device\n");
4998 return PTR_ERR(ibm_backlight_device);
4999 }
5000 vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n");
5001
5002 ibm_backlight_device->props.max_brightness =
5003 (tp_features.bright_16levels)? 15 : 7;
5004 ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK;
5005 backlight_update_status(ibm_backlight_device);
5006
5007 return 0;
5008}
5009
5010static void brightness_exit(void)
5011{
5012 if (ibm_backlight_device) {
5013 vdbg_printk(TPACPI_DBG_EXIT,
5014 "calling backlight_device_unregister()\n");
5015 backlight_device_unregister(ibm_backlight_device);
5016 }
5017}
5018
5019static int brightness_read(char *p)
5020{
5021 int len = 0;
5022 int level;
5023
5024 level = brightness_get(NULL);
5025 if (level < 0) {
5026 len += sprintf(p + len, "level:\t\tunreadable\n");
5027 } else {
5028 len += sprintf(p + len, "level:\t\t%d\n", level);
5029 len += sprintf(p + len, "commands:\tup, down\n");
5030 len += sprintf(p + len, "commands:\tlevel <level>"
5031 " (<level> is 0-%d)\n",
5032 (tp_features.bright_16levels) ? 15 : 7);
5033 }
5034
5035 return len;
5036}
5037
5038static int brightness_write(char *buf)
5039{
5040 int level;
5041 int rc;
5042 char *cmd;
5043 int max_level = (tp_features.bright_16levels) ? 15 : 7;
5044
5045 level = brightness_get(NULL);
5046 if (level < 0)
5047 return level;
5048
5049 while ((cmd = next_cmd(&buf))) {
5050 if (strlencmp(cmd, "up") == 0) {
5051 if (level < max_level)
5052 level++;
5053 } else if (strlencmp(cmd, "down") == 0) {
5054 if (level > 0)
5055 level--;
5056 } else if (sscanf(cmd, "level %d", &level) == 1 &&
5057 level >= 0 && level <= max_level) {
5058 /* new level set */
5059 } else
5060 return -EINVAL;
5061 }
5062
5063 /*
5064 * Now we know what the final level should be, so we try to set it.
5065 * Doing it this way makes the syscall restartable in case of EINTR
5066 */
5067 rc = brightness_set(level);
5068 return (rc == -EINTR)? ERESTARTSYS : rc;
5069}
5070
5071static struct ibm_struct brightness_driver_data = {
5072 .name = "brightness",
5073 .read = brightness_read,
5074 .write = brightness_write,
5075 .exit = brightness_exit,
5076};
5077
5078/*************************************************************************
5079 * Volume subdriver
5080 */
5081
5082static int volume_offset = 0x30;
5083
5084static int volume_read(char *p)
5085{
5086 int len = 0;
5087 u8 level;
5088
5089 if (!acpi_ec_read(volume_offset, &level)) {
5090 len += sprintf(p + len, "level:\t\tunreadable\n");
5091 } else {
5092 len += sprintf(p + len, "level:\t\t%d\n", level & 0xf);
5093 len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6));
5094 len += sprintf(p + len, "commands:\tup, down, mute\n");
5095 len += sprintf(p + len, "commands:\tlevel <level>"
5096 " (<level> is 0-15)\n");
5097 }
5098
5099 return len;
5100}
5101
5102static int volume_write(char *buf)
5103{
5104 int cmos_cmd, inc, i;
5105 u8 level, mute;
5106 int new_level, new_mute;
5107 char *cmd;
5108
5109 while ((cmd = next_cmd(&buf))) {
5110 if (!acpi_ec_read(volume_offset, &level))
5111 return -EIO;
5112 new_mute = mute = level & 0x40;
5113 new_level = level = level & 0xf;
5114
5115 if (strlencmp(cmd, "up") == 0) {
5116 if (mute)
5117 new_mute = 0;
5118 else
5119 new_level = level == 15 ? 15 : level + 1;
5120 } else if (strlencmp(cmd, "down") == 0) {
5121 if (mute)
5122 new_mute = 0;
5123 else
5124 new_level = level == 0 ? 0 : level - 1;
5125 } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
5126 new_level >= 0 && new_level <= 15) {
5127 /* new_level set */
5128 } else if (strlencmp(cmd, "mute") == 0) {
5129 new_mute = 0x40;
5130 } else
5131 return -EINVAL;
5132
5133 if (new_level != level) {
5134 /* mute doesn't change */
5135
5136 cmos_cmd = (new_level > level) ?
5137 TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN;
5138 inc = new_level > level ? 1 : -1;
5139
5140 if (mute && (issue_thinkpad_cmos_command(cmos_cmd) ||
5141 !acpi_ec_write(volume_offset, level)))
5142 return -EIO;
5143
5144 for (i = level; i != new_level; i += inc)
5145 if (issue_thinkpad_cmos_command(cmos_cmd) ||
5146 !acpi_ec_write(volume_offset, i + inc))
5147 return -EIO;
5148
5149 if (mute &&
5150 (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) ||
5151 !acpi_ec_write(volume_offset, new_level + mute))) {
5152 return -EIO;
5153 }
5154 }
5155
5156 if (new_mute != mute) {
5157 /* level doesn't change */
5158
5159 cmos_cmd = (new_mute) ?
5160 TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP;
5161
5162 if (issue_thinkpad_cmos_command(cmos_cmd) ||
5163 !acpi_ec_write(volume_offset, level + new_mute))
5164 return -EIO;
5165 }
5166 }
5167
5168 return 0;
5169}
5170
5171static struct ibm_struct volume_driver_data = {
5172 .name = "volume",
5173 .read = volume_read,
5174 .write = volume_write,
5175};
5176
5177/*************************************************************************
5178 * Fan subdriver
5179 */
5180
5181/*
5182 * FAN ACCESS MODES
5183 *
5184 * TPACPI_FAN_RD_ACPI_GFAN:
5185 * ACPI GFAN method: returns fan level
5186 *
5187 * see TPACPI_FAN_WR_ACPI_SFAN
5188 * EC 0x2f (HFSP) not available if GFAN exists
5189 *
5190 * TPACPI_FAN_WR_ACPI_SFAN:
5191 * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max)
5192 *
5193 * EC 0x2f (HFSP) might be available *for reading*, but do not use
5194 * it for writing.
5195 *
5196 * TPACPI_FAN_WR_TPEC:
5197 * ThinkPad EC register 0x2f (HFSP): fan control loop mode
5198 * Supported on almost all ThinkPads
5199 *
5200 * Fan speed changes of any sort (including those caused by the
5201 * disengaged mode) are usually done slowly by the firmware as the
5202 * maximum ammount of fan duty cycle change per second seems to be
5203 * limited.
5204 *
5205 * Reading is not available if GFAN exists.
5206 * Writing is not available if SFAN exists.
5207 *
5208 * Bits
5209 * 7 automatic mode engaged;
5210 * (default operation mode of the ThinkPad)
5211 * fan level is ignored in this mode.
5212 * 6 full speed mode (takes precedence over bit 7);
5213 * not available on all thinkpads. May disable
5214 * the tachometer while the fan controller ramps up
5215 * the speed (which can take up to a few *minutes*).
5216 * Speeds up fan to 100% duty-cycle, which is far above
5217 * the standard RPM levels. It is not impossible that
5218 * it could cause hardware damage.
5219 * 5-3 unused in some models. Extra bits for fan level
5220 * in others, but still useless as all values above
5221 * 7 map to the same speed as level 7 in these models.
5222 * 2-0 fan level (0..7 usually)
5223 * 0x00 = stop
5224 * 0x07 = max (set when temperatures critical)
5225 * Some ThinkPads may have other levels, see
5226 * TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
5227 *
5228 * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
5229 * boot. Apparently the EC does not intialize it, so unless ACPI DSDT
5230 * does so, its initial value is meaningless (0x07).
5231 *
5232 * For firmware bugs, refer to:
5233 * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
5234 *
5235 * ----
5236 *
5237 * ThinkPad EC register 0x84 (LSB), 0x85 (MSB):
5238 * Main fan tachometer reading (in RPM)
5239 *
5240 * This register is present on all ThinkPads with a new-style EC, and
5241 * it is known not to be present on the A21m/e, and T22, as there is
5242 * something else in offset 0x84 according to the ACPI DSDT. Other
5243 * ThinkPads from this same time period (and earlier) probably lack the
5244 * tachometer as well.
5245 *
5246 * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare
5247 * was never fixed by IBM to report the EC firmware version string
5248 * probably support the tachometer (like the early X models), so
5249 * detecting it is quite hard. We need more data to know for sure.
5250 *
5251 * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings
5252 * might result.
5253 *
5254 * FIRMWARE BUG: may go stale while the EC is switching to full speed
5255 * mode.
5256 *
5257 * For firmware bugs, refer to:
5258 * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
5259 *
5260 * TPACPI_FAN_WR_ACPI_FANS:
5261 * ThinkPad X31, X40, X41. Not available in the X60.
5262 *
5263 * FANS ACPI handle: takes three arguments: low speed, medium speed,
5264 * high speed. ACPI DSDT seems to map these three speeds to levels
5265 * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH
5266 * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3")
5267 *
5268 * The speeds are stored on handles
5269 * (FANA:FAN9), (FANC:FANB), (FANE:FAND).
5270 *
5271 * There are three default speed sets, acessible as handles:
5272 * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
5273 *
5274 * ACPI DSDT switches which set is in use depending on various
5275 * factors.
5276 *
5277 * TPACPI_FAN_WR_TPEC is also available and should be used to
5278 * command the fan. The X31/X40/X41 seems to have 8 fan levels,
5279 * but the ACPI tables just mention level 7.
5280 */
5281
5282enum { /* Fan control constants */
5283 fan_status_offset = 0x2f, /* EC register 0x2f */
5284 fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM)
5285 * 0x84 must be read before 0x85 */
5286
5287 TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */
5288 TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */
5289
5290 TPACPI_FAN_LAST_LEVEL = 0x100, /* Use cached last-seen fan level */
5291};
5292
5293enum fan_status_access_mode {
5294 TPACPI_FAN_NONE = 0, /* No fan status or control */
5295 TPACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */
5296 TPACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */
5297};
5298
5299enum fan_control_access_mode {
5300 TPACPI_FAN_WR_NONE = 0, /* No fan control */
5301 TPACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */
5302 TPACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */
5303 TPACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */
5304};
5305
5306enum fan_control_commands {
5307 TPACPI_FAN_CMD_SPEED = 0x0001, /* speed command */
5308 TPACPI_FAN_CMD_LEVEL = 0x0002, /* level command */
5309 TPACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd,
5310 * and also watchdog cmd */
5311};
5312
5313static int fan_control_allowed;
5314
5315static enum fan_status_access_mode fan_status_access_mode;
5316static enum fan_control_access_mode fan_control_access_mode;
5317static enum fan_control_commands fan_control_commands;
5318
5319static u8 fan_control_initial_status;
5320static u8 fan_control_desired_level;
5321static u8 fan_control_resume_level;
5322static int fan_watchdog_maxinterval;
5323
5324static struct mutex fan_mutex;
5325
5326static void fan_watchdog_fire(struct work_struct *ignored);
5327static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire);
5328
5329TPACPI_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */
5330TPACPI_HANDLE(gfan, ec, "GFAN", /* 570 */
5331 "\\FSPD", /* 600e/x, 770e, 770x */
5332 ); /* all others */
5333TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */
5334 "JFNS", /* 770x-JL */
5335 ); /* all others */
5336
5337/*
5338 * Call with fan_mutex held
5339 */
5340static void fan_update_desired_level(u8 status)
5341{
5342 if ((status &
5343 (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
5344 if (status > 7)
5345 fan_control_desired_level = 7;
5346 else
5347 fan_control_desired_level = status;
5348 }
5349}
5350
5351static int fan_get_status(u8 *status)
5352{
5353 u8 s;
5354
5355 /* TODO:
5356 * Add TPACPI_FAN_RD_ACPI_FANS ? */
5357
5358 switch (fan_status_access_mode) {
5359 case TPACPI_FAN_RD_ACPI_GFAN:
5360 /* 570, 600e/x, 770e, 770x */
5361
5362 if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
5363 return -EIO;
5364
5365 if (likely(status))
5366 *status = s & 0x07;
5367
5368 break;
5369
5370 case TPACPI_FAN_RD_TPEC:
5371 /* all except 570, 600e/x, 770e, 770x */
5372 if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
5373 return -EIO;
5374
5375 if (likely(status))
5376 *status = s;
5377
5378 break;
5379
5380 default:
5381 return -ENXIO;
5382 }
5383
5384 return 0;
5385}
5386
5387static int fan_get_status_safe(u8 *status)
5388{
5389 int rc;
5390 u8 s;
5391
5392 if (mutex_lock_interruptible(&fan_mutex))
5393 return -ERESTARTSYS;
5394 rc = fan_get_status(&s);
5395 if (!rc)
5396 fan_update_desired_level(s);
5397 mutex_unlock(&fan_mutex);
5398
5399 if (status)
5400 *status = s;
5401
5402 return rc;
5403}
5404
5405static int fan_get_speed(unsigned int *speed)
5406{
5407 u8 hi, lo;
5408
5409 switch (fan_status_access_mode) {
5410 case TPACPI_FAN_RD_TPEC:
5411 /* all except 570, 600e/x, 770e, 770x */
5412 if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
5413 !acpi_ec_read(fan_rpm_offset + 1, &hi)))
5414 return -EIO;
5415
5416 if (likely(speed))
5417 *speed = (hi << 8) | lo;
5418
5419 break;
5420
5421 default:
5422 return -ENXIO;
5423 }
5424
5425 return 0;
5426}
5427
5428static int fan_set_level(int level)
5429{
5430 if (!fan_control_allowed)
5431 return -EPERM;
5432
5433 switch (fan_control_access_mode) {
5434 case TPACPI_FAN_WR_ACPI_SFAN:
5435 if (level >= 0 && level <= 7) {
5436 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
5437 return -EIO;
5438 } else
5439 return -EINVAL;
5440 break;
5441
5442 case TPACPI_FAN_WR_ACPI_FANS:
5443 case TPACPI_FAN_WR_TPEC:
5444 if (!(level & TP_EC_FAN_AUTO) &&
5445 !(level & TP_EC_FAN_FULLSPEED) &&
5446 ((level < 0) || (level > 7)))
5447 return -EINVAL;
5448
5449 /* safety net should the EC not support AUTO
5450 * or FULLSPEED mode bits and just ignore them */
5451 if (level & TP_EC_FAN_FULLSPEED)
5452 level |= 7; /* safety min speed 7 */
5453 else if (level & TP_EC_FAN_AUTO)
5454 level |= 4; /* safety min speed 4 */
5455
5456 if (!acpi_ec_write(fan_status_offset, level))
5457 return -EIO;
5458 else
5459 tp_features.fan_ctrl_status_undef = 0;
5460 break;
5461
5462 default:
5463 return -ENXIO;
5464 }
5465 return 0;
5466}
5467
5468static int fan_set_level_safe(int level)
5469{
5470 int rc;
5471
5472 if (!fan_control_allowed)
5473 return -EPERM;
5474
5475 if (mutex_lock_interruptible(&fan_mutex))
5476 return -ERESTARTSYS;
5477
5478 if (level == TPACPI_FAN_LAST_LEVEL)
5479 level = fan_control_desired_level;
5480
5481 rc = fan_set_level(level);
5482 if (!rc)
5483 fan_update_desired_level(level);
5484
5485 mutex_unlock(&fan_mutex);
5486 return rc;
5487}
5488
5489static int fan_set_enable(void)
5490{
5491 u8 s;
5492 int rc;
5493
5494 if (!fan_control_allowed)
5495 return -EPERM;
5496
5497 if (mutex_lock_interruptible(&fan_mutex))
5498 return -ERESTARTSYS;
5499
5500 switch (fan_control_access_mode) {
5501 case TPACPI_FAN_WR_ACPI_FANS:
5502 case TPACPI_FAN_WR_TPEC:
5503 rc = fan_get_status(&s);
5504 if (rc < 0)
5505 break;
5506
5507 /* Don't go out of emergency fan mode */
5508 if (s != 7) {
5509 s &= 0x07;
5510 s |= TP_EC_FAN_AUTO | 4; /* min fan speed 4 */
5511 }
5512
5513 if (!acpi_ec_write(fan_status_offset, s))
5514 rc = -EIO;
5515 else {
5516 tp_features.fan_ctrl_status_undef = 0;
5517 rc = 0;
5518 }
5519 break;
5520
5521 case TPACPI_FAN_WR_ACPI_SFAN:
5522 rc = fan_get_status(&s);
5523 if (rc < 0)
5524 break;
5525
5526 s &= 0x07;
5527
5528 /* Set fan to at least level 4 */
5529 s |= 4;
5530
5531 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s))
5532 rc = -EIO;
5533 else
5534 rc = 0;
5535 break;
5536
5537 default:
5538 rc = -ENXIO;
5539 }
5540
5541 mutex_unlock(&fan_mutex);
5542 return rc;
5543}
5544
5545static int fan_set_disable(void)
5546{
5547 int rc;
5548
5549 if (!fan_control_allowed)
5550 return -EPERM;
5551
5552 if (mutex_lock_interruptible(&fan_mutex))
5553 return -ERESTARTSYS;
5554
5555 rc = 0;
5556 switch (fan_control_access_mode) {
5557 case TPACPI_FAN_WR_ACPI_FANS:
5558 case TPACPI_FAN_WR_TPEC:
5559 if (!acpi_ec_write(fan_status_offset, 0x00))
5560 rc = -EIO;
5561 else {
5562 fan_control_desired_level = 0;
5563 tp_features.fan_ctrl_status_undef = 0;
5564 }
5565 break;
5566
5567 case TPACPI_FAN_WR_ACPI_SFAN:
5568 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00))
5569 rc = -EIO;
5570 else
5571 fan_control_desired_level = 0;
5572 break;
5573
5574 default:
5575 rc = -ENXIO;
5576 }
5577
5578
5579 mutex_unlock(&fan_mutex);
5580 return rc;
5581}
5582
5583static int fan_set_speed(int speed)
5584{
5585 int rc;
5586
5587 if (!fan_control_allowed)
5588 return -EPERM;
5589
5590 if (mutex_lock_interruptible(&fan_mutex))
5591 return -ERESTARTSYS;
5592
5593 rc = 0;
5594 switch (fan_control_access_mode) {
5595 case TPACPI_FAN_WR_ACPI_FANS:
5596 if (speed >= 0 && speed <= 65535) {
5597 if (!acpi_evalf(fans_handle, NULL, NULL, "vddd",
5598 speed, speed, speed))
5599 rc = -EIO;
5600 } else
5601 rc = -EINVAL;
5602 break;
5603
5604 default:
5605 rc = -ENXIO;
5606 }
5607
5608 mutex_unlock(&fan_mutex);
5609 return rc;
5610}
5611
5612static void fan_watchdog_reset(void)
5613{
5614 static int fan_watchdog_active;
5615
5616 if (fan_control_access_mode == TPACPI_FAN_WR_NONE)
5617 return;
5618
5619 if (fan_watchdog_active)
5620 cancel_delayed_work(&fan_watchdog_task);
5621
5622 if (fan_watchdog_maxinterval > 0 &&
5623 tpacpi_lifecycle != TPACPI_LIFE_EXITING) {
5624 fan_watchdog_active = 1;
5625 if (!queue_delayed_work(tpacpi_wq, &fan_watchdog_task,
5626 msecs_to_jiffies(fan_watchdog_maxinterval
5627 * 1000))) {
5628 printk(TPACPI_ERR
5629 "failed to queue the fan watchdog, "
5630 "watchdog will not trigger\n");
5631 }
5632 } else
5633 fan_watchdog_active = 0;
5634}
5635
5636static void fan_watchdog_fire(struct work_struct *ignored)
5637{
5638 int rc;
5639
5640 if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING)
5641 return;
5642
5643 printk(TPACPI_NOTICE "fan watchdog: enabling fan\n");
5644 rc = fan_set_enable();
5645 if (rc < 0) {
5646 printk(TPACPI_ERR "fan watchdog: error %d while enabling fan, "
5647 "will try again later...\n", -rc);
5648 /* reschedule for later */
5649 fan_watchdog_reset();
5650 }
5651}
5652
5653/*
5654 * SYSFS fan layout: hwmon compatible (device)
5655 *
5656 * pwm*_enable:
5657 * 0: "disengaged" mode
5658 * 1: manual mode
5659 * 2: native EC "auto" mode (recommended, hardware default)
5660 *
5661 * pwm*: set speed in manual mode, ignored otherwise.
5662 * 0 is level 0; 255 is level 7. Intermediate points done with linear
5663 * interpolation.
5664 *
5665 * fan*_input: tachometer reading, RPM
5666 *
5667 *
5668 * SYSFS fan layout: extensions
5669 *
5670 * fan_watchdog (driver):
5671 * fan watchdog interval in seconds, 0 disables (default), max 120
5672 */
5673
5674/* sysfs fan pwm1_enable ----------------------------------------------- */
5675static ssize_t fan_pwm1_enable_show(struct device *dev,
5676 struct device_attribute *attr,
5677 char *buf)
5678{
5679 int res, mode;
5680 u8 status;
5681
5682 res = fan_get_status_safe(&status);
5683 if (res)
5684 return res;
5685
5686 if (unlikely(tp_features.fan_ctrl_status_undef)) {
5687 if (status != fan_control_initial_status) {
5688 tp_features.fan_ctrl_status_undef = 0;
5689 } else {
5690 /* Return most likely status. In fact, it
5691 * might be the only possible status */
5692 status = TP_EC_FAN_AUTO;
5693 }
5694 }
5695
5696 if (status & TP_EC_FAN_FULLSPEED) {
5697 mode = 0;
5698 } else if (status & TP_EC_FAN_AUTO) {
5699 mode = 2;
5700 } else
5701 mode = 1;
5702
5703 return snprintf(buf, PAGE_SIZE, "%d\n", mode);
5704}
5705
5706static ssize_t fan_pwm1_enable_store(struct device *dev,
5707 struct device_attribute *attr,
5708 const char *buf, size_t count)
5709{
5710 unsigned long t;
5711 int res, level;
5712
5713 if (parse_strtoul(buf, 2, &t))
5714 return -EINVAL;
5715
5716 switch (t) {
5717 case 0:
5718 level = TP_EC_FAN_FULLSPEED;
5719 break;
5720 case 1:
5721 level = TPACPI_FAN_LAST_LEVEL;
5722 break;
5723 case 2:
5724 level = TP_EC_FAN_AUTO;
5725 break;
5726 case 3:
5727 /* reserved for software-controlled auto mode */
5728 return -ENOSYS;
5729 default:
5730 return -EINVAL;
5731 }
5732
5733 res = fan_set_level_safe(level);
5734 if (res == -ENXIO)
5735 return -EINVAL;
5736 else if (res < 0)
5737 return res;
5738
5739 fan_watchdog_reset();
5740
5741 return count;
5742}
5743
5744static struct device_attribute dev_attr_fan_pwm1_enable =
5745 __ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
5746 fan_pwm1_enable_show, fan_pwm1_enable_store);
5747
5748/* sysfs fan pwm1 ------------------------------------------------------ */
5749static ssize_t fan_pwm1_show(struct device *dev,
5750 struct device_attribute *attr,
5751 char *buf)
5752{
5753 int res;
5754 u8 status;
5755
5756 res = fan_get_status_safe(&status);
5757 if (res)
5758 return res;
5759
5760 if (unlikely(tp_features.fan_ctrl_status_undef)) {
5761 if (status != fan_control_initial_status) {
5762 tp_features.fan_ctrl_status_undef = 0;
5763 } else {
5764 status = TP_EC_FAN_AUTO;
5765 }
5766 }
5767
5768 if ((status &
5769 (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0)
5770 status = fan_control_desired_level;
5771
5772 if (status > 7)
5773 status = 7;
5774
5775 return snprintf(buf, PAGE_SIZE, "%u\n", (status * 255) / 7);
5776}
5777
5778static ssize_t fan_pwm1_store(struct device *dev,
5779 struct device_attribute *attr,
5780 const char *buf, size_t count)
5781{
5782 unsigned long s;
5783 int rc;
5784 u8 status, newlevel;
5785
5786 if (parse_strtoul(buf, 255, &s))
5787 return -EINVAL;
5788
5789 /* scale down from 0-255 to 0-7 */
5790 newlevel = (s >> 5) & 0x07;
5791
5792 if (mutex_lock_interruptible(&fan_mutex))
5793 return -ERESTARTSYS;
5794
5795 rc = fan_get_status(&status);
5796 if (!rc && (status &
5797 (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
5798 rc = fan_set_level(newlevel);
5799 if (rc == -ENXIO)
5800 rc = -EINVAL;
5801 else if (!rc) {
5802 fan_update_desired_level(newlevel);
5803 fan_watchdog_reset();
5804 }
5805 }
5806
5807 mutex_unlock(&fan_mutex);
5808 return (rc)? rc : count;
5809}
5810
5811static struct device_attribute dev_attr_fan_pwm1 =
5812 __ATTR(pwm1, S_IWUSR | S_IRUGO,
5813 fan_pwm1_show, fan_pwm1_store);
5814
5815/* sysfs fan fan1_input ------------------------------------------------ */
5816static ssize_t fan_fan1_input_show(struct device *dev,
5817 struct device_attribute *attr,
5818 char *buf)
5819{
5820 int res;
5821 unsigned int speed;
5822
5823 res = fan_get_speed(&speed);
5824 if (res < 0)
5825 return res;
5826
5827 return snprintf(buf, PAGE_SIZE, "%u\n", speed);
5828}
5829
5830static struct device_attribute dev_attr_fan_fan1_input =
5831 __ATTR(fan1_input, S_IRUGO,
5832 fan_fan1_input_show, NULL);
5833
5834/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */
5835static ssize_t fan_fan_watchdog_show(struct device_driver *drv,
5836 char *buf)
5837{
5838 return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval);
5839}
5840
5841static ssize_t fan_fan_watchdog_store(struct device_driver *drv,
5842 const char *buf, size_t count)
5843{
5844 unsigned long t;
5845
5846 if (parse_strtoul(buf, 120, &t))
5847 return -EINVAL;
5848
5849 if (!fan_control_allowed)
5850 return -EPERM;
5851
5852 fan_watchdog_maxinterval = t;
5853 fan_watchdog_reset();
5854
5855 return count;
5856}
5857
5858static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO,
5859 fan_fan_watchdog_show, fan_fan_watchdog_store);
5860
5861/* --------------------------------------------------------------------- */
5862static struct attribute *fan_attributes[] = {
5863 &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr,
5864 &dev_attr_fan_fan1_input.attr,
5865 NULL
5866};
5867
5868static const struct attribute_group fan_attr_group = {
5869 .attrs = fan_attributes,
5870};
5871
5872static int __init fan_init(struct ibm_init_struct *iibm)
5873{
5874 int rc;
5875
5876 vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n");
5877
5878 mutex_init(&fan_mutex);
5879 fan_status_access_mode = TPACPI_FAN_NONE;
5880 fan_control_access_mode = TPACPI_FAN_WR_NONE;
5881 fan_control_commands = 0;
5882 fan_watchdog_maxinterval = 0;
5883 tp_features.fan_ctrl_status_undef = 0;
5884 fan_control_desired_level = 7;
5885
5886 TPACPI_ACPIHANDLE_INIT(fans);
5887 TPACPI_ACPIHANDLE_INIT(gfan);
5888 TPACPI_ACPIHANDLE_INIT(sfan);
5889
5890 if (gfan_handle) {
5891 /* 570, 600e/x, 770e, 770x */
5892 fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
5893 } else {
5894 /* all other ThinkPads: note that even old-style
5895 * ThinkPad ECs supports the fan control register */
5896 if (likely(acpi_ec_read(fan_status_offset,
5897 &fan_control_initial_status))) {
5898 fan_status_access_mode = TPACPI_FAN_RD_TPEC;
5899
5900 /* In some ThinkPads, neither the EC nor the ACPI
5901 * DSDT initialize the fan status, and it ends up
5902 * being set to 0x07 when it *could* be either
5903 * 0x07 or 0x80.
5904 *
5905 * Enable for TP-1Y (T43), TP-78 (R51e),
5906 * TP-76 (R52), TP-70 (T43, R52), which are known
5907 * to be buggy. */
5908 if (fan_control_initial_status == 0x07) {
5909 switch (thinkpad_id.ec_model) {
5910 case 0x5931: /* TP-1Y */
5911 case 0x3837: /* TP-78 */
5912 case 0x3637: /* TP-76 */
5913 case 0x3037: /* TP-70 */
5914 printk(TPACPI_NOTICE
5915 "fan_init: initial fan status "
5916 "is unknown, assuming it is "
5917 "in auto mode\n");
5918 tp_features.fan_ctrl_status_undef = 1;
5919 ;;
5920 }
5921 }
5922 } else {
5923 printk(TPACPI_ERR
5924 "ThinkPad ACPI EC access misbehaving, "
5925 "fan status and control unavailable\n");
5926 return 1;
5927 }
5928 }
5929
5930 if (sfan_handle) {
5931 /* 570, 770x-JL */
5932 fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN;
5933 fan_control_commands |=
5934 TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE;
5935 } else {
5936 if (!gfan_handle) {
5937 /* gfan without sfan means no fan control */
5938 /* all other models implement TP EC 0x2f control */
5939
5940 if (fans_handle) {
5941 /* X31, X40, X41 */
5942 fan_control_access_mode =
5943 TPACPI_FAN_WR_ACPI_FANS;
5944 fan_control_commands |=
5945 TPACPI_FAN_CMD_SPEED |
5946 TPACPI_FAN_CMD_LEVEL |
5947 TPACPI_FAN_CMD_ENABLE;
5948 } else {
5949 fan_control_access_mode = TPACPI_FAN_WR_TPEC;
5950 fan_control_commands |=
5951 TPACPI_FAN_CMD_LEVEL |
5952 TPACPI_FAN_CMD_ENABLE;
5953 }
5954 }
5955 }
5956
5957 vdbg_printk(TPACPI_DBG_INIT, "fan is %s, modes %d, %d\n",
5958 str_supported(fan_status_access_mode != TPACPI_FAN_NONE ||
5959 fan_control_access_mode != TPACPI_FAN_WR_NONE),
5960 fan_status_access_mode, fan_control_access_mode);
5961
5962 /* fan control master switch */
5963 if (!fan_control_allowed) {
5964 fan_control_access_mode = TPACPI_FAN_WR_NONE;
5965 fan_control_commands = 0;
5966 dbg_printk(TPACPI_DBG_INIT,
5967 "fan control features disabled by parameter\n");
5968 }
5969
5970 /* update fan_control_desired_level */
5971 if (fan_status_access_mode != TPACPI_FAN_NONE)
5972 fan_get_status_safe(NULL);
5973
5974 if (fan_status_access_mode != TPACPI_FAN_NONE ||
5975 fan_control_access_mode != TPACPI_FAN_WR_NONE) {
5976 rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
5977 &fan_attr_group);
5978 if (rc < 0)
5979 return rc;
5980
5981 rc = driver_create_file(&tpacpi_hwmon_pdriver.driver,
5982 &driver_attr_fan_watchdog);
5983 if (rc < 0) {
5984 sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
5985 &fan_attr_group);
5986 return rc;
5987 }
5988 return 0;
5989 } else
5990 return 1;
5991}
5992
5993static void fan_exit(void)
5994{
5995 vdbg_printk(TPACPI_DBG_EXIT,
5996 "cancelling any pending fan watchdog tasks\n");
5997
5998 /* FIXME: can we really do this unconditionally? */
5999 sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &fan_attr_group);
6000 driver_remove_file(&tpacpi_hwmon_pdriver.driver,
6001 &driver_attr_fan_watchdog);
6002
6003 cancel_delayed_work(&fan_watchdog_task);
6004 flush_workqueue(tpacpi_wq);
6005}
6006
6007static void fan_suspend(pm_message_t state)
6008{
6009 int rc;
6010
6011 if (!fan_control_allowed)
6012 return;
6013
6014 /* Store fan status in cache */
6015 fan_control_resume_level = 0;
6016 rc = fan_get_status_safe(&fan_control_resume_level);
6017 if (rc < 0)
6018 printk(TPACPI_NOTICE
6019 "failed to read fan level for later "
6020 "restore during resume: %d\n", rc);
6021
6022 /* if it is undefined, don't attempt to restore it.
6023 * KEEP THIS LAST */
6024 if (tp_features.fan_ctrl_status_undef)
6025 fan_control_resume_level = 0;
6026}
6027
6028static void fan_resume(void)
6029{
6030 u8 current_level = 7;
6031 bool do_set = false;
6032 int rc;
6033
6034 /* DSDT *always* updates status on resume */
6035 tp_features.fan_ctrl_status_undef = 0;
6036
6037 if (!fan_control_allowed ||
6038 !fan_control_resume_level ||
6039 (fan_get_status_safe(&current_level) < 0))
6040 return;
6041
6042 switch (fan_control_access_mode) {
6043 case TPACPI_FAN_WR_ACPI_SFAN:
6044 /* never decrease fan level */
6045 do_set = (fan_control_resume_level > current_level);
6046 break;
6047 case TPACPI_FAN_WR_ACPI_FANS:
6048 case TPACPI_FAN_WR_TPEC:
6049 /* never decrease fan level, scale is:
6050 * TP_EC_FAN_FULLSPEED > 7 >= TP_EC_FAN_AUTO
6051 *
6052 * We expect the firmware to set either 7 or AUTO, but we
6053 * handle FULLSPEED out of paranoia.
6054 *
6055 * So, we can safely only restore FULLSPEED or 7, anything
6056 * else could slow the fan. Restoring AUTO is useless, at
6057 * best that's exactly what the DSDT already set (it is the
6058 * slower it uses).
6059 *
6060 * Always keep in mind that the DSDT *will* have set the
6061 * fans to what the vendor supposes is the best level. We
6062 * muck with it only to speed the fan up.
6063 */
6064 if (fan_control_resume_level != 7 &&
6065 !(fan_control_resume_level & TP_EC_FAN_FULLSPEED))
6066 return;
6067 else
6068 do_set = !(current_level & TP_EC_FAN_FULLSPEED) &&
6069 (current_level != fan_control_resume_level);
6070 break;
6071 default:
6072 return;
6073 }
6074 if (do_set) {
6075 printk(TPACPI_NOTICE
6076 "restoring fan level to 0x%02x\n",
6077 fan_control_resume_level);
6078 rc = fan_set_level_safe(fan_control_resume_level);
6079 if (rc < 0)
6080 printk(TPACPI_NOTICE
6081 "failed to restore fan level: %d\n", rc);
6082 }
6083}
6084
6085static int fan_read(char *p)
6086{
6087 int len = 0;
6088 int rc;
6089 u8 status;
6090 unsigned int speed = 0;
6091
6092 switch (fan_status_access_mode) {
6093 case TPACPI_FAN_RD_ACPI_GFAN:
6094 /* 570, 600e/x, 770e, 770x */
6095 rc = fan_get_status_safe(&status);
6096 if (rc < 0)
6097 return rc;
6098
6099 len += sprintf(p + len, "status:\t\t%s\n"
6100 "level:\t\t%d\n",
6101 (status != 0) ? "enabled" : "disabled", status);
6102 break;
6103
6104 case TPACPI_FAN_RD_TPEC:
6105 /* all except 570, 600e/x, 770e, 770x */
6106 rc = fan_get_status_safe(&status);
6107 if (rc < 0)
6108 return rc;
6109
6110 if (unlikely(tp_features.fan_ctrl_status_undef)) {
6111 if (status != fan_control_initial_status)
6112 tp_features.fan_ctrl_status_undef = 0;
6113 else
6114 /* Return most likely status. In fact, it
6115 * might be the only possible status */
6116 status = TP_EC_FAN_AUTO;
6117 }
6118
6119 len += sprintf(p + len, "status:\t\t%s\n",
6120 (status != 0) ? "enabled" : "disabled");
6121
6122 rc = fan_get_speed(&speed);
6123 if (rc < 0)
6124 return rc;
6125
6126 len += sprintf(p + len, "speed:\t\t%d\n", speed);
6127
6128 if (status & TP_EC_FAN_FULLSPEED)
6129 /* Disengaged mode takes precedence */
6130 len += sprintf(p + len, "level:\t\tdisengaged\n");
6131 else if (status & TP_EC_FAN_AUTO)
6132 len += sprintf(p + len, "level:\t\tauto\n");
6133 else
6134 len += sprintf(p + len, "level:\t\t%d\n", status);
6135 break;
6136
6137 case TPACPI_FAN_NONE:
6138 default:
6139 len += sprintf(p + len, "status:\t\tnot supported\n");
6140 }
6141
6142 if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) {
6143 len += sprintf(p + len, "commands:\tlevel <level>");
6144
6145 switch (fan_control_access_mode) {
6146 case TPACPI_FAN_WR_ACPI_SFAN:
6147 len += sprintf(p + len, " (<level> is 0-7)\n");
6148 break;
6149
6150 default:
6151 len += sprintf(p + len, " (<level> is 0-7, "
6152 "auto, disengaged, full-speed)\n");
6153 break;
6154 }
6155 }
6156
6157 if (fan_control_commands & TPACPI_FAN_CMD_ENABLE)
6158 len += sprintf(p + len, "commands:\tenable, disable\n"
6159 "commands:\twatchdog <timeout> (<timeout> "
6160 "is 0 (off), 1-120 (seconds))\n");
6161
6162 if (fan_control_commands & TPACPI_FAN_CMD_SPEED)
6163 len += sprintf(p + len, "commands:\tspeed <speed>"
6164 " (<speed> is 0-65535)\n");
6165
6166 return len;
6167}
6168
6169static int fan_write_cmd_level(const char *cmd, int *rc)
6170{
6171 int level;
6172
6173 if (strlencmp(cmd, "level auto") == 0)
6174 level = TP_EC_FAN_AUTO;
6175 else if ((strlencmp(cmd, "level disengaged") == 0) |
6176 (strlencmp(cmd, "level full-speed") == 0))
6177 level = TP_EC_FAN_FULLSPEED;
6178 else if (sscanf(cmd, "level %d", &level) != 1)
6179 return 0;
6180
6181 *rc = fan_set_level_safe(level);
6182 if (*rc == -ENXIO)
6183 printk(TPACPI_ERR "level command accepted for unsupported "
6184 "access mode %d", fan_control_access_mode);
6185
6186 return 1;
6187}
6188
6189static int fan_write_cmd_enable(const char *cmd, int *rc)
6190{
6191 if (strlencmp(cmd, "enable") != 0)
6192 return 0;
6193
6194 *rc = fan_set_enable();
6195 if (*rc == -ENXIO)
6196 printk(TPACPI_ERR "enable command accepted for unsupported "
6197 "access mode %d", fan_control_access_mode);
6198
6199 return 1;
6200}
6201
6202static int fan_write_cmd_disable(const char *cmd, int *rc)
6203{
6204 if (strlencmp(cmd, "disable") != 0)
6205 return 0;
6206
6207 *rc = fan_set_disable();
6208 if (*rc == -ENXIO)
6209 printk(TPACPI_ERR "disable command accepted for unsupported "
6210 "access mode %d", fan_control_access_mode);
6211
6212 return 1;
6213}
6214
6215static int fan_write_cmd_speed(const char *cmd, int *rc)
6216{
6217 int speed;
6218
6219 /* TODO:
6220 * Support speed <low> <medium> <high> ? */
6221
6222 if (sscanf(cmd, "speed %d", &speed) != 1)
6223 return 0;
6224
6225 *rc = fan_set_speed(speed);
6226 if (*rc == -ENXIO)
6227 printk(TPACPI_ERR "speed command accepted for unsupported "
6228 "access mode %d", fan_control_access_mode);
6229
6230 return 1;
6231}
6232
6233static int fan_write_cmd_watchdog(const char *cmd, int *rc)
6234{
6235 int interval;
6236
6237 if (sscanf(cmd, "watchdog %d", &interval) != 1)
6238 return 0;
6239
6240 if (interval < 0 || interval > 120)
6241 *rc = -EINVAL;
6242 else
6243 fan_watchdog_maxinterval = interval;
6244
6245 return 1;
6246}
6247
6248static int fan_write(char *buf)
6249{
6250 char *cmd;
6251 int rc = 0;
6252
6253 while (!rc && (cmd = next_cmd(&buf))) {
6254 if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) &&
6255 fan_write_cmd_level(cmd, &rc)) &&
6256 !((fan_control_commands & TPACPI_FAN_CMD_ENABLE) &&
6257 (fan_write_cmd_enable(cmd, &rc) ||
6258 fan_write_cmd_disable(cmd, &rc) ||
6259 fan_write_cmd_watchdog(cmd, &rc))) &&
6260 !((fan_control_commands & TPACPI_FAN_CMD_SPEED) &&
6261 fan_write_cmd_speed(cmd, &rc))
6262 )
6263 rc = -EINVAL;
6264 else if (!rc)
6265 fan_watchdog_reset();
6266 }
6267
6268 return rc;
6269}
6270
6271static struct ibm_struct fan_driver_data = {
6272 .name = "fan",
6273 .read = fan_read,
6274 .write = fan_write,
6275 .exit = fan_exit,
6276 .suspend = fan_suspend,
6277 .resume = fan_resume,
6278};
6279
6280/****************************************************************************
6281 ****************************************************************************
6282 *
6283 * Infrastructure
6284 *
6285 ****************************************************************************
6286 ****************************************************************************/
6287
6288/* sysfs name ---------------------------------------------------------- */
6289static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev,
6290 struct device_attribute *attr,
6291 char *buf)
6292{
6293 return snprintf(buf, PAGE_SIZE, "%s\n", TPACPI_NAME);
6294}
6295
6296static struct device_attribute dev_attr_thinkpad_acpi_pdev_name =
6297 __ATTR(name, S_IRUGO, thinkpad_acpi_pdev_name_show, NULL);
6298
6299/* --------------------------------------------------------------------- */
6300
6301/* /proc support */
6302static struct proc_dir_entry *proc_dir;
6303
6304/*
6305 * Module and infrastructure proble, init and exit handling
6306 */
6307
6308static int force_load;
6309
6310#ifdef CONFIG_THINKPAD_ACPI_DEBUG
6311static const char * __init str_supported(int is_supported)
6312{
6313 static char text_unsupported[] __initdata = "not supported";
6314
6315 return (is_supported)? &text_unsupported[4] : &text_unsupported[0];
6316}
6317#endif /* CONFIG_THINKPAD_ACPI_DEBUG */
6318
6319static void ibm_exit(struct ibm_struct *ibm)
6320{
6321 dbg_printk(TPACPI_DBG_EXIT, "removing %s\n", ibm->name);
6322
6323 list_del_init(&ibm->all_drivers);
6324
6325 if (ibm->flags.acpi_notify_installed) {
6326 dbg_printk(TPACPI_DBG_EXIT,
6327 "%s: acpi_remove_notify_handler\n", ibm->name);
6328 BUG_ON(!ibm->acpi);
6329 acpi_remove_notify_handler(*ibm->acpi->handle,
6330 ibm->acpi->type,
6331 dispatch_acpi_notify);
6332 ibm->flags.acpi_notify_installed = 0;
6333 ibm->flags.acpi_notify_installed = 0;
6334 }
6335
6336 if (ibm->flags.proc_created) {
6337 dbg_printk(TPACPI_DBG_EXIT,
6338 "%s: remove_proc_entry\n", ibm->name);
6339 remove_proc_entry(ibm->name, proc_dir);
6340 ibm->flags.proc_created = 0;
6341 }
6342
6343 if (ibm->flags.acpi_driver_registered) {
6344 dbg_printk(TPACPI_DBG_EXIT,
6345 "%s: acpi_bus_unregister_driver\n", ibm->name);
6346 BUG_ON(!ibm->acpi);
6347 acpi_bus_unregister_driver(ibm->acpi->driver);
6348 kfree(ibm->acpi->driver);
6349 ibm->acpi->driver = NULL;
6350 ibm->flags.acpi_driver_registered = 0;
6351 }
6352
6353 if (ibm->flags.init_called && ibm->exit) {
6354 ibm->exit();
6355 ibm->flags.init_called = 0;
6356 }
6357
6358 dbg_printk(TPACPI_DBG_INIT, "finished removing %s\n", ibm->name);
6359}
6360
6361static int __init ibm_init(struct ibm_init_struct *iibm)
6362{
6363 int ret;
6364 struct ibm_struct *ibm = iibm->data;
6365 struct proc_dir_entry *entry;
6366
6367 BUG_ON(ibm == NULL);
6368
6369 INIT_LIST_HEAD(&ibm->all_drivers);
6370
6371 if (ibm->flags.experimental && !experimental)
6372 return 0;
6373
6374 dbg_printk(TPACPI_DBG_INIT,
6375 "probing for %s\n", ibm->name);
6376
6377 if (iibm->init) {
6378 ret = iibm->init(iibm);
6379 if (ret > 0)
6380 return 0; /* probe failed */
6381 if (ret)
6382 return ret;
6383
6384 ibm->flags.init_called = 1;
6385 }
6386
6387 if (ibm->acpi) {
6388 if (ibm->acpi->hid) {
6389 ret = register_tpacpi_subdriver(ibm);
6390 if (ret)
6391 goto err_out;
6392 }
6393
6394 if (ibm->acpi->notify) {
6395 ret = setup_acpi_notify(ibm);
6396 if (ret == -ENODEV) {
6397 printk(TPACPI_NOTICE "disabling subdriver %s\n",
6398 ibm->name);
6399 ret = 0;
6400 goto err_out;
6401 }
6402 if (ret < 0)
6403 goto err_out;
6404 }
6405 }
6406
6407 dbg_printk(TPACPI_DBG_INIT,
6408 "%s installed\n", ibm->name);
6409
6410 if (ibm->read) {
6411 entry = create_proc_entry(ibm->name,
6412 S_IFREG | S_IRUGO | S_IWUSR,
6413 proc_dir);
6414 if (!entry) {
6415 printk(TPACPI_ERR "unable to create proc entry %s\n",
6416 ibm->name);
6417 ret = -ENODEV;
6418 goto err_out;
6419 }
6420 entry->owner = THIS_MODULE;
6421 entry->data = ibm;
6422 entry->read_proc = &dispatch_procfs_read;
6423 if (ibm->write)
6424 entry->write_proc = &dispatch_procfs_write;
6425 ibm->flags.proc_created = 1;
6426 }
6427
6428 list_add_tail(&ibm->all_drivers, &tpacpi_all_drivers);
6429
6430 return 0;
6431
6432err_out:
6433 dbg_printk(TPACPI_DBG_INIT,
6434 "%s: at error exit path with result %d\n",
6435 ibm->name, ret);
6436
6437 ibm_exit(ibm);
6438 return (ret < 0)? ret : 0;
6439}
6440
6441/* Probing */
6442
6443/* returns 0 - probe ok, or < 0 - probe error.
6444 * Probe ok doesn't mean thinkpad found.
6445 * On error, kfree() cleanup on tp->* is not performed, caller must do it */
6446static int __must_check __init get_thinkpad_model_data(
6447 struct thinkpad_id_data *tp)
6448{
6449 const struct dmi_device *dev = NULL;
6450 char ec_fw_string[18];
6451 char const *s;
6452
6453 if (!tp)
6454 return -EINVAL;
6455
6456 memset(tp, 0, sizeof(*tp));
6457
6458 if (dmi_name_in_vendors("IBM"))
6459 tp->vendor = PCI_VENDOR_ID_IBM;
6460 else if (dmi_name_in_vendors("LENOVO"))
6461 tp->vendor = PCI_VENDOR_ID_LENOVO;
6462 else
6463 return 0;
6464
6465 s = dmi_get_system_info(DMI_BIOS_VERSION);
6466 tp->bios_version_str = kstrdup(s, GFP_KERNEL);
6467 if (s && !tp->bios_version_str)
6468 return -ENOMEM;
6469 if (!tp->bios_version_str)
6470 return 0;
6471 tp->bios_model = tp->bios_version_str[0]
6472 | (tp->bios_version_str[1] << 8);
6473
6474 /*
6475 * ThinkPad T23 or newer, A31 or newer, R50e or newer,
6476 * X32 or newer, all Z series; Some models must have an
6477 * up-to-date BIOS or they will not be detected.
6478 *
6479 * See http://thinkwiki.org/wiki/List_of_DMI_IDs
6480 */
6481 while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
6482 if (sscanf(dev->name,
6483 "IBM ThinkPad Embedded Controller -[%17c",
6484 ec_fw_string) == 1) {
6485 ec_fw_string[sizeof(ec_fw_string) - 1] = 0;
6486 ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
6487
6488 tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL);
6489 if (!tp->ec_version_str)
6490 return -ENOMEM;
6491 tp->ec_model = ec_fw_string[0]
6492 | (ec_fw_string[1] << 8);
6493 break;
6494 }
6495 }
6496
6497 s = dmi_get_system_info(DMI_PRODUCT_VERSION);
6498 if (s && !strnicmp(s, "ThinkPad", 8)) {
6499 tp->model_str = kstrdup(s, GFP_KERNEL);
6500 if (!tp->model_str)
6501 return -ENOMEM;
6502 }
6503
6504 s = dmi_get_system_info(DMI_PRODUCT_NAME);
6505 tp->nummodel_str = kstrdup(s, GFP_KERNEL);
6506 if (s && !tp->nummodel_str)
6507 return -ENOMEM;
6508
6509 return 0;
6510}
6511
6512static int __init probe_for_thinkpad(void)
6513{
6514 int is_thinkpad;
6515
6516 if (acpi_disabled)
6517 return -ENODEV;
6518
6519 /*
6520 * Non-ancient models have better DMI tagging, but very old models
6521 * don't.
6522 */
6523 is_thinkpad = (thinkpad_id.model_str != NULL);
6524
6525 /* ec is required because many other handles are relative to it */
6526 TPACPI_ACPIHANDLE_INIT(ec);
6527 if (!ec_handle) {
6528 if (is_thinkpad)
6529 printk(TPACPI_ERR
6530 "Not yet supported ThinkPad detected!\n");
6531 return -ENODEV;
6532 }
6533
6534 /*
6535 * Risks a regression on very old machines, but reduces potential
6536 * false positives a damn great deal
6537 */
6538 if (!is_thinkpad)
6539 is_thinkpad = (thinkpad_id.vendor == PCI_VENDOR_ID_IBM);
6540
6541 if (!is_thinkpad && !force_load)
6542 return -ENODEV;
6543
6544 return 0;
6545}
6546
6547
6548/* Module init, exit, parameters */
6549
6550static struct ibm_init_struct ibms_init[] __initdata = {
6551 {
6552 .init = thinkpad_acpi_driver_init,
6553 .data = &thinkpad_acpi_driver_data,
6554 },
6555 {
6556 .init = hotkey_init,
6557 .data = &hotkey_driver_data,
6558 },
6559 {
6560 .init = bluetooth_init,
6561 .data = &bluetooth_driver_data,
6562 },
6563 {
6564 .init = wan_init,
6565 .data = &wan_driver_data,
6566 },
6567#ifdef CONFIG_THINKPAD_ACPI_VIDEO
6568 {
6569 .init = video_init,
6570 .data = &video_driver_data,
6571 },
6572#endif
6573 {
6574 .init = light_init,
6575 .data = &light_driver_data,
6576 },
6577#ifdef CONFIG_THINKPAD_ACPI_DOCK
6578 {
6579 .init = dock_init,
6580 .data = &dock_driver_data[0],
6581 },
6582 {
6583 .init = dock_init2,
6584 .data = &dock_driver_data[1],
6585 },
6586#endif
6587#ifdef CONFIG_THINKPAD_ACPI_BAY
6588 {
6589 .init = bay_init,
6590 .data = &bay_driver_data,
6591 },
6592#endif
6593 {
6594 .init = cmos_init,
6595 .data = &cmos_driver_data,
6596 },
6597 {
6598 .init = led_init,
6599 .data = &led_driver_data,
6600 },
6601 {
6602 .init = beep_init,
6603 .data = &beep_driver_data,
6604 },
6605 {
6606 .init = thermal_init,
6607 .data = &thermal_driver_data,
6608 },
6609 {
6610 .data = &ecdump_driver_data,
6611 },
6612 {
6613 .init = brightness_init,
6614 .data = &brightness_driver_data,
6615 },
6616 {
6617 .data = &volume_driver_data,
6618 },
6619 {
6620 .init = fan_init,
6621 .data = &fan_driver_data,
6622 },
6623};
6624
6625static int __init set_ibm_param(const char *val, struct kernel_param *kp)
6626{
6627 unsigned int i;
6628 struct ibm_struct *ibm;
6629
6630 if (!kp || !kp->name || !val)
6631 return -EINVAL;
6632
6633 for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
6634 ibm = ibms_init[i].data;
6635 WARN_ON(ibm == NULL);
6636
6637 if (!ibm || !ibm->name)
6638 continue;
6639
6640 if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
6641 if (strlen(val) > sizeof(ibms_init[i].param) - 2)
6642 return -ENOSPC;
6643 strcpy(ibms_init[i].param, val);
6644 strcat(ibms_init[i].param, ",");
6645 return 0;
6646 }
6647 }
6648
6649 return -EINVAL;
6650}
6651
6652module_param(experimental, int, 0);
6653MODULE_PARM_DESC(experimental,
6654 "Enables experimental features when non-zero");
6655
6656module_param_named(debug, dbg_level, uint, 0);
6657MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
6658
6659module_param(force_load, bool, 0);
6660MODULE_PARM_DESC(force_load,
6661 "Attempts to load the driver even on a "
6662 "mis-identified ThinkPad when true");
6663
6664module_param_named(fan_control, fan_control_allowed, bool, 0);
6665MODULE_PARM_DESC(fan_control,
6666 "Enables setting fan parameters features when true");
6667
6668module_param_named(brightness_mode, brightness_mode, int, 0);
6669MODULE_PARM_DESC(brightness_mode,
6670 "Selects brightness control strategy: "
6671 "0=auto, 1=EC, 2=CMOS, 3=both");
6672
6673module_param(brightness_enable, uint, 0);
6674MODULE_PARM_DESC(brightness_enable,
6675 "Enables backlight control when 1, disables when 0");
6676
6677module_param(hotkey_report_mode, uint, 0);
6678MODULE_PARM_DESC(hotkey_report_mode,
6679 "used for backwards compatibility with userspace, "
6680 "see documentation");
6681
6682#define TPACPI_PARAM(feature) \
6683 module_param_call(feature, set_ibm_param, NULL, NULL, 0); \
6684 MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \
6685 "at module load, see documentation")
6686
6687TPACPI_PARAM(hotkey);
6688TPACPI_PARAM(bluetooth);
6689TPACPI_PARAM(video);
6690TPACPI_PARAM(light);
6691#ifdef CONFIG_THINKPAD_ACPI_DOCK
6692TPACPI_PARAM(dock);
6693#endif
6694#ifdef CONFIG_THINKPAD_ACPI_BAY
6695TPACPI_PARAM(bay);
6696#endif /* CONFIG_THINKPAD_ACPI_BAY */
6697TPACPI_PARAM(cmos);
6698TPACPI_PARAM(led);
6699TPACPI_PARAM(beep);
6700TPACPI_PARAM(ecdump);
6701TPACPI_PARAM(brightness);
6702TPACPI_PARAM(volume);
6703TPACPI_PARAM(fan);
6704
6705static void thinkpad_acpi_module_exit(void)
6706{
6707 struct ibm_struct *ibm, *itmp;
6708
6709 tpacpi_lifecycle = TPACPI_LIFE_EXITING;
6710
6711 list_for_each_entry_safe_reverse(ibm, itmp,
6712 &tpacpi_all_drivers,
6713 all_drivers) {
6714 ibm_exit(ibm);
6715 }
6716
6717 dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n");
6718
6719 if (tpacpi_inputdev) {
6720 if (tp_features.input_device_registered)
6721 input_unregister_device(tpacpi_inputdev);
6722 else
6723 input_free_device(tpacpi_inputdev);
6724 }
6725
6726 if (tpacpi_hwmon)
6727 hwmon_device_unregister(tpacpi_hwmon);
6728
6729 if (tp_features.sensors_pdev_attrs_registered)
6730 device_remove_file(&tpacpi_sensors_pdev->dev,
6731 &dev_attr_thinkpad_acpi_pdev_name);
6732 if (tpacpi_sensors_pdev)
6733 platform_device_unregister(tpacpi_sensors_pdev);
6734 if (tpacpi_pdev)
6735 platform_device_unregister(tpacpi_pdev);
6736
6737 if (tp_features.sensors_pdrv_attrs_registered)
6738 tpacpi_remove_driver_attributes(&tpacpi_hwmon_pdriver.driver);
6739 if (tp_features.platform_drv_attrs_registered)
6740 tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver);
6741
6742 if (tp_features.sensors_pdrv_registered)
6743 platform_driver_unregister(&tpacpi_hwmon_pdriver);
6744
6745 if (tp_features.platform_drv_registered)
6746 platform_driver_unregister(&tpacpi_pdriver);
6747
6748 if (proc_dir)
6749 remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir);
6750
6751 if (tpacpi_wq)
6752 destroy_workqueue(tpacpi_wq);
6753
6754 kfree(thinkpad_id.bios_version_str);
6755 kfree(thinkpad_id.ec_version_str);
6756 kfree(thinkpad_id.model_str);
6757}
6758
6759
6760static int __init thinkpad_acpi_module_init(void)
6761{
6762 int ret, i;
6763
6764 tpacpi_lifecycle = TPACPI_LIFE_INIT;
6765
6766 /* Parameter checking */
6767 if (hotkey_report_mode > 2)
6768 return -EINVAL;
6769
6770 /* Driver-level probe */
6771
6772 ret = get_thinkpad_model_data(&thinkpad_id);
6773 if (ret) {
6774 printk(TPACPI_ERR
6775 "unable to get DMI data: %d\n", ret);
6776 thinkpad_acpi_module_exit();
6777 return ret;
6778 }
6779 ret = probe_for_thinkpad();
6780 if (ret) {
6781 thinkpad_acpi_module_exit();
6782 return ret;
6783 }
6784
6785 /* Driver initialization */
6786
6787 TPACPI_ACPIHANDLE_INIT(ecrd);
6788 TPACPI_ACPIHANDLE_INIT(ecwr);
6789
6790 tpacpi_wq = create_singlethread_workqueue(TPACPI_WORKQUEUE_NAME);
6791 if (!tpacpi_wq) {
6792 thinkpad_acpi_module_exit();
6793 return -ENOMEM;
6794 }
6795
6796 proc_dir = proc_mkdir(TPACPI_PROC_DIR, acpi_root_dir);
6797 if (!proc_dir) {
6798 printk(TPACPI_ERR
6799 "unable to create proc dir " TPACPI_PROC_DIR);
6800 thinkpad_acpi_module_exit();
6801 return -ENODEV;
6802 }
6803 proc_dir->owner = THIS_MODULE;
6804
6805 ret = platform_driver_register(&tpacpi_pdriver);
6806 if (ret) {
6807 printk(TPACPI_ERR
6808 "unable to register main platform driver\n");
6809 thinkpad_acpi_module_exit();
6810 return ret;
6811 }
6812 tp_features.platform_drv_registered = 1;
6813
6814 ret = platform_driver_register(&tpacpi_hwmon_pdriver);
6815 if (ret) {
6816 printk(TPACPI_ERR
6817 "unable to register hwmon platform driver\n");
6818 thinkpad_acpi_module_exit();
6819 return ret;
6820 }
6821 tp_features.sensors_pdrv_registered = 1;
6822
6823 ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver);
6824 if (!ret) {
6825 tp_features.platform_drv_attrs_registered = 1;
6826 ret = tpacpi_create_driver_attributes(
6827 &tpacpi_hwmon_pdriver.driver);
6828 }
6829 if (ret) {
6830 printk(TPACPI_ERR
6831 "unable to create sysfs driver attributes\n");
6832 thinkpad_acpi_module_exit();
6833 return ret;
6834 }
6835 tp_features.sensors_pdrv_attrs_registered = 1;
6836
6837
6838 /* Device initialization */
6839 tpacpi_pdev = platform_device_register_simple(TPACPI_DRVR_NAME, -1,
6840 NULL, 0);
6841 if (IS_ERR(tpacpi_pdev)) {
6842 ret = PTR_ERR(tpacpi_pdev);
6843 tpacpi_pdev = NULL;
6844 printk(TPACPI_ERR "unable to register platform device\n");
6845 thinkpad_acpi_module_exit();
6846 return ret;
6847 }
6848 tpacpi_sensors_pdev = platform_device_register_simple(
6849 TPACPI_HWMON_DRVR_NAME,
6850 -1, NULL, 0);
6851 if (IS_ERR(tpacpi_sensors_pdev)) {
6852 ret = PTR_ERR(tpacpi_sensors_pdev);
6853 tpacpi_sensors_pdev = NULL;
6854 printk(TPACPI_ERR
6855 "unable to register hwmon platform device\n");
6856 thinkpad_acpi_module_exit();
6857 return ret;
6858 }
6859 ret = device_create_file(&tpacpi_sensors_pdev->dev,
6860 &dev_attr_thinkpad_acpi_pdev_name);
6861 if (ret) {
6862 printk(TPACPI_ERR
6863 "unable to create sysfs hwmon device attributes\n");
6864 thinkpad_acpi_module_exit();
6865 return ret;
6866 }
6867 tp_features.sensors_pdev_attrs_registered = 1;
6868 tpacpi_hwmon = hwmon_device_register(&tpacpi_sensors_pdev->dev);
6869 if (IS_ERR(tpacpi_hwmon)) {
6870 ret = PTR_ERR(tpacpi_hwmon);
6871 tpacpi_hwmon = NULL;
6872 printk(TPACPI_ERR "unable to register hwmon device\n");
6873 thinkpad_acpi_module_exit();
6874 return ret;
6875 }
6876 mutex_init(&tpacpi_inputdev_send_mutex);
6877 tpacpi_inputdev = input_allocate_device();
6878 if (!tpacpi_inputdev) {
6879 printk(TPACPI_ERR "unable to allocate input device\n");
6880 thinkpad_acpi_module_exit();
6881 return -ENOMEM;
6882 } else {
6883 /* Prepare input device, but don't register */
6884 tpacpi_inputdev->name = "ThinkPad Extra Buttons";
6885 tpacpi_inputdev->phys = TPACPI_DRVR_NAME "/input0";
6886 tpacpi_inputdev->id.bustype = BUS_HOST;
6887 tpacpi_inputdev->id.vendor = (thinkpad_id.vendor) ?
6888 thinkpad_id.vendor :
6889 PCI_VENDOR_ID_IBM;
6890 tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT;
6891 tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION;
6892 }
6893 for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
6894 ret = ibm_init(&ibms_init[i]);
6895 if (ret >= 0 && *ibms_init[i].param)
6896 ret = ibms_init[i].data->write(ibms_init[i].param);
6897 if (ret < 0) {
6898 thinkpad_acpi_module_exit();
6899 return ret;
6900 }
6901 }
6902 ret = input_register_device(tpacpi_inputdev);
6903 if (ret < 0) {
6904 printk(TPACPI_ERR "unable to register input device\n");
6905 thinkpad_acpi_module_exit();
6906 return ret;
6907 } else {
6908 tp_features.input_device_registered = 1;
6909 }
6910
6911 tpacpi_lifecycle = TPACPI_LIFE_RUNNING;
6912 return 0;
6913}
6914
6915/* Please remove this in year 2009 */
6916MODULE_ALIAS("ibm_acpi");
6917
6918MODULE_ALIAS(TPACPI_DRVR_SHORTNAME);
6919
6920/*
6921 * DMI matching for module autoloading
6922 *
6923 * See http://thinkwiki.org/wiki/List_of_DMI_IDs
6924 * See http://thinkwiki.org/wiki/BIOS_Upgrade_Downloads
6925 *
6926 * Only models listed in thinkwiki will be supported, so add yours
6927 * if it is not there yet.
6928 */
6929#define IBM_BIOS_MODULE_ALIAS(__type) \
6930 MODULE_ALIAS("dmi:bvnIBM:bvr" __type "ET??WW")
6931
6932/* Non-ancient thinkpads */
6933MODULE_ALIAS("dmi:bvnIBM:*:svnIBM:*:pvrThinkPad*:rvnIBM:*");
6934MODULE_ALIAS("dmi:bvnLENOVO:*:svnLENOVO:*:pvrThinkPad*:rvnLENOVO:*");
6935
6936/* Ancient thinkpad BIOSes have to be identified by
6937 * BIOS type or model number, and there are far less
6938 * BIOS types than model numbers... */
6939IBM_BIOS_MODULE_ALIAS("I[B,D,H,I,M,N,O,T,W,V,Y,Z]");
6940IBM_BIOS_MODULE_ALIAS("1[0,3,6,8,A-G,I,K,M-P,S,T]");
6941IBM_BIOS_MODULE_ALIAS("K[U,X-Z]");
6942
6943MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh");
6944MODULE_DESCRIPTION(TPACPI_DESC);
6945MODULE_VERSION(TPACPI_VERSION);
6946MODULE_LICENSE("GPL");
6947
6948module_init(thinkpad_acpi_module_init);
6949module_exit(thinkpad_acpi_module_exit);