xref: /linux/drivers/platform/x86/toshiba_acpi.c (revision f11f999e989061952f1a27bd0c49645a46d13173)
1b4f9fe12SLen Brown /*
2b4f9fe12SLen Brown  *  toshiba_acpi.c - Toshiba Laptop ACPI Extras
3b4f9fe12SLen Brown  *
4b4f9fe12SLen Brown  *
5b4f9fe12SLen Brown  *  Copyright (C) 2002-2004 John Belmonte
6b4f9fe12SLen Brown  *  Copyright (C) 2008 Philip Langdale
76c3f6e6cSPierre Ducroquet  *  Copyright (C) 2010 Pierre Ducroquet
8b4f9fe12SLen Brown  *
9b4f9fe12SLen Brown  *  This program is free software; you can redistribute it and/or modify
10b4f9fe12SLen Brown  *  it under the terms of the GNU General Public License as published by
11b4f9fe12SLen Brown  *  the Free Software Foundation; either version 2 of the License, or
12b4f9fe12SLen Brown  *  (at your option) any later version.
13b4f9fe12SLen Brown  *
14b4f9fe12SLen Brown  *  This program is distributed in the hope that it will be useful,
15b4f9fe12SLen Brown  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16b4f9fe12SLen Brown  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17b4f9fe12SLen Brown  *  GNU General Public License for more details.
18b4f9fe12SLen Brown  *
19b4f9fe12SLen Brown  *  You should have received a copy of the GNU General Public License
20b4f9fe12SLen Brown  *  along with this program; if not, write to the Free Software
21b4f9fe12SLen Brown  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22b4f9fe12SLen Brown  *
23b4f9fe12SLen Brown  *
24b4f9fe12SLen Brown  *  The devolpment page for this driver is located at
25b4f9fe12SLen Brown  *  http://memebeam.org/toys/ToshibaAcpiDriver.
26b4f9fe12SLen Brown  *
27b4f9fe12SLen Brown  *  Credits:
28b4f9fe12SLen Brown  *	Jonathan A. Buzzard - Toshiba HCI info, and critical tips on reverse
29b4f9fe12SLen Brown  *		engineering the Windows drivers
30b4f9fe12SLen Brown  *	Yasushi Nagato - changes for linux kernel 2.4 -> 2.5
31b4f9fe12SLen Brown  *	Rob Miller - TV out and hotkeys help
32b4f9fe12SLen Brown  *
33b4f9fe12SLen Brown  *
34b4f9fe12SLen Brown  *  TODO
35b4f9fe12SLen Brown  *
36b4f9fe12SLen Brown  */
37b4f9fe12SLen Brown 
387e33460dSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
397e33460dSJoe Perches 
40b4f9fe12SLen Brown #define TOSHIBA_ACPI_VERSION	"0.19"
41b4f9fe12SLen Brown #define PROC_INTERFACE_VERSION	1
42b4f9fe12SLen Brown 
43b4f9fe12SLen Brown #include <linux/kernel.h>
44b4f9fe12SLen Brown #include <linux/module.h>
45b4f9fe12SLen Brown #include <linux/init.h>
46b4f9fe12SLen Brown #include <linux/types.h>
47b4f9fe12SLen Brown #include <linux/proc_fs.h>
48936c8bcdSAlexey Dobriyan #include <linux/seq_file.h>
49b4f9fe12SLen Brown #include <linux/backlight.h>
50b4f9fe12SLen Brown #include <linux/rfkill.h>
516335e4d5SMatthew Garrett #include <linux/input.h>
52384a7cd9SDmitry Torokhov #include <linux/input/sparse-keymap.h>
536c3f6e6cSPierre Ducroquet #include <linux/leds.h>
545a0e3ad6STejun Heo #include <linux/slab.h>
5529cd293fSSeth Forshee #include <linux/workqueue.h>
5629cd293fSSeth Forshee #include <linux/i8042.h>
57b4f9fe12SLen Brown 
58b4f9fe12SLen Brown #include <asm/uaccess.h>
59b4f9fe12SLen Brown 
60b4f9fe12SLen Brown #include <acpi/acpi_drivers.h>
61b4f9fe12SLen Brown 
62b4f9fe12SLen Brown MODULE_AUTHOR("John Belmonte");
63b4f9fe12SLen Brown MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
64b4f9fe12SLen Brown MODULE_LICENSE("GPL");
65b4f9fe12SLen Brown 
66*f11f999eSSeth Forshee #define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100"
67*f11f999eSSeth Forshee 
6829cd293fSSeth Forshee /* Scan code for Fn key on TOS1900 models */
6929cd293fSSeth Forshee #define TOS1900_FN_SCAN		0x6e
7029cd293fSSeth Forshee 
71b4f9fe12SLen Brown /* Toshiba ACPI method paths */
72b4f9fe12SLen Brown #define METHOD_VIDEO_OUT	"\\_SB_.VALX.DSSX"
73b4f9fe12SLen Brown 
74b4f9fe12SLen Brown /* Toshiba HCI interface definitions
75b4f9fe12SLen Brown  *
76b4f9fe12SLen Brown  * HCI is Toshiba's "Hardware Control Interface" which is supposed to
77b4f9fe12SLen Brown  * be uniform across all their models.  Ideally we would just call
78b4f9fe12SLen Brown  * dedicated ACPI methods instead of using this primitive interface.
79b4f9fe12SLen Brown  * However the ACPI methods seem to be incomplete in some areas (for
80b4f9fe12SLen Brown  * example they allow setting, but not reading, the LCD brightness value),
81b4f9fe12SLen Brown  * so this is still useful.
82b4f9fe12SLen Brown  */
83b4f9fe12SLen Brown 
84b4f9fe12SLen Brown #define HCI_WORDS			6
85b4f9fe12SLen Brown 
86b4f9fe12SLen Brown /* operations */
87b4f9fe12SLen Brown #define HCI_SET				0xff00
88b4f9fe12SLen Brown #define HCI_GET				0xfe00
89b4f9fe12SLen Brown 
90b4f9fe12SLen Brown /* return codes */
91b4f9fe12SLen Brown #define HCI_SUCCESS			0x0000
92b4f9fe12SLen Brown #define HCI_FAILURE			0x1000
93b4f9fe12SLen Brown #define HCI_NOT_SUPPORTED		0x8000
94b4f9fe12SLen Brown #define HCI_EMPTY			0x8c00
95b4f9fe12SLen Brown 
96b4f9fe12SLen Brown /* registers */
97b4f9fe12SLen Brown #define HCI_FAN				0x0004
98b4f9fe12SLen Brown #define HCI_SYSTEM_EVENT		0x0016
99b4f9fe12SLen Brown #define HCI_VIDEO_OUT			0x001c
100b4f9fe12SLen Brown #define HCI_HOTKEY_EVENT		0x001e
101b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS		0x002a
102b4f9fe12SLen Brown #define HCI_WIRELESS			0x0056
103b4f9fe12SLen Brown 
104b4f9fe12SLen Brown /* field definitions */
10529cd293fSSeth Forshee #define HCI_HOTKEY_DISABLE		0x0b
10629cd293fSSeth Forshee #define HCI_HOTKEY_ENABLE		0x09
107b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_BITS		3
108b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_SHIFT	(16-HCI_LCD_BRIGHTNESS_BITS)
109b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_LEVELS	(1 << HCI_LCD_BRIGHTNESS_BITS)
110b4f9fe12SLen Brown #define HCI_VIDEO_OUT_LCD		0x1
111b4f9fe12SLen Brown #define HCI_VIDEO_OUT_CRT		0x2
112b4f9fe12SLen Brown #define HCI_VIDEO_OUT_TV		0x4
113b4f9fe12SLen Brown #define HCI_WIRELESS_KILL_SWITCH	0x01
114b4f9fe12SLen Brown #define HCI_WIRELESS_BT_PRESENT		0x0f
115b4f9fe12SLen Brown #define HCI_WIRELESS_BT_ATTACH		0x40
116b4f9fe12SLen Brown #define HCI_WIRELESS_BT_POWER		0x80
117b4f9fe12SLen Brown 
118135740deSSeth Forshee struct toshiba_acpi_dev {
119135740deSSeth Forshee 	struct acpi_device *acpi_dev;
120135740deSSeth Forshee 	const char *method_hci;
121135740deSSeth Forshee 	struct rfkill *bt_rfk;
122135740deSSeth Forshee 	struct input_dev *hotkey_dev;
12329cd293fSSeth Forshee 	struct work_struct hotkey_work;
124135740deSSeth Forshee 	struct backlight_device *backlight_dev;
125135740deSSeth Forshee 	struct led_classdev led_dev;
12636d03f93SSeth Forshee 
127135740deSSeth Forshee 	int force_fan;
128135740deSSeth Forshee 	int last_key_event;
129135740deSSeth Forshee 	int key_event_valid;
130135740deSSeth Forshee 
131592b746cSDan Carpenter 	unsigned int illumination_supported:1;
132592b746cSDan Carpenter 	unsigned int video_supported:1;
133592b746cSDan Carpenter 	unsigned int fan_supported:1;
134592b746cSDan Carpenter 	unsigned int system_event_supported:1;
13529cd293fSSeth Forshee 	unsigned int ntfy_supported:1;
13629cd293fSSeth Forshee 	unsigned int info_supported:1;
13736d03f93SSeth Forshee 
138135740deSSeth Forshee 	struct mutex mutex;
139135740deSSeth Forshee };
140135740deSSeth Forshee 
14129cd293fSSeth Forshee static struct toshiba_acpi_dev *toshiba_acpi;
14229cd293fSSeth Forshee 
143b4f9fe12SLen Brown static const struct acpi_device_id toshiba_device_ids[] = {
144b4f9fe12SLen Brown 	{"TOS6200", 0},
145b4f9fe12SLen Brown 	{"TOS6208", 0},
146b4f9fe12SLen Brown 	{"TOS1900", 0},
147b4f9fe12SLen Brown 	{"", 0},
148b4f9fe12SLen Brown };
149b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
150b4f9fe12SLen Brown 
151135740deSSeth Forshee static const struct key_entry toshiba_acpi_keymap[] __devinitconst = {
152384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x101, { KEY_MUTE } },
153384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x102, { KEY_ZOOMOUT } },
154384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x103, { KEY_ZOOMIN } },
155af502837SAzael Avalos 	{ KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } },
156af502837SAzael Avalos 	{ KE_KEY, 0x139, { KEY_ZOOMRESET } },
157384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13b, { KEY_COFFEE } },
158384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13c, { KEY_BATTERY } },
159384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13d, { KEY_SLEEP } },
160384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13e, { KEY_SUSPEND } },
161384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } },
162384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
163384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
164384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x142, { KEY_WLAN } },
165af502837SAzael Avalos 	{ KE_KEY, 0x143, { KEY_TOUCHPAD_TOGGLE } },
166a49010f5SJon Dowland 	{ KE_KEY, 0x17f, { KEY_FN } },
167384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb05, { KEY_PROG2 } },
168384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb06, { KEY_WWW } },
169384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb07, { KEY_MAIL } },
170384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb30, { KEY_STOP } },
171384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb31, { KEY_PREVIOUSSONG } },
172384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb32, { KEY_NEXTSONG } },
173384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
174384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb5a, { KEY_MEDIA } },
175af502837SAzael Avalos 	{ KE_IGNORE, 0x1430, { KEY_RESERVED } },
176384a7cd9SDmitry Torokhov 	{ KE_END, 0 },
1776335e4d5SMatthew Garrett };
1786335e4d5SMatthew Garrett 
179b4f9fe12SLen Brown /* utility
180b4f9fe12SLen Brown  */
181b4f9fe12SLen Brown 
182b4f9fe12SLen Brown static __inline__ void _set_bit(u32 * word, u32 mask, int value)
183b4f9fe12SLen Brown {
184b4f9fe12SLen Brown 	*word = (*word & ~mask) | (mask * value);
185b4f9fe12SLen Brown }
186b4f9fe12SLen Brown 
187b4f9fe12SLen Brown /* acpi interface wrappers
188b4f9fe12SLen Brown  */
189b4f9fe12SLen Brown 
190b4f9fe12SLen Brown static int write_acpi_int(const char *methodName, int val)
191b4f9fe12SLen Brown {
192b4f9fe12SLen Brown 	struct acpi_object_list params;
193b4f9fe12SLen Brown 	union acpi_object in_objs[1];
194b4f9fe12SLen Brown 	acpi_status status;
195b4f9fe12SLen Brown 
196b4f9fe12SLen Brown 	params.count = ARRAY_SIZE(in_objs);
197b4f9fe12SLen Brown 	params.pointer = in_objs;
198b4f9fe12SLen Brown 	in_objs[0].type = ACPI_TYPE_INTEGER;
199b4f9fe12SLen Brown 	in_objs[0].integer.value = val;
200b4f9fe12SLen Brown 
201b4f9fe12SLen Brown 	status = acpi_evaluate_object(NULL, (char *)methodName, &params, NULL);
20232bcd5cbSSeth Forshee 	return (status == AE_OK) ? 0 : -EIO;
203b4f9fe12SLen Brown }
204b4f9fe12SLen Brown 
205b4f9fe12SLen Brown /* Perform a raw HCI call.  Here we don't care about input or output buffer
206b4f9fe12SLen Brown  * format.
207b4f9fe12SLen Brown  */
208135740deSSeth Forshee static acpi_status hci_raw(struct toshiba_acpi_dev *dev,
209135740deSSeth Forshee 			   const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
210b4f9fe12SLen Brown {
211b4f9fe12SLen Brown 	struct acpi_object_list params;
212b4f9fe12SLen Brown 	union acpi_object in_objs[HCI_WORDS];
213b4f9fe12SLen Brown 	struct acpi_buffer results;
214b4f9fe12SLen Brown 	union acpi_object out_objs[HCI_WORDS + 1];
215b4f9fe12SLen Brown 	acpi_status status;
216b4f9fe12SLen Brown 	int i;
217b4f9fe12SLen Brown 
218b4f9fe12SLen Brown 	params.count = HCI_WORDS;
219b4f9fe12SLen Brown 	params.pointer = in_objs;
220b4f9fe12SLen Brown 	for (i = 0; i < HCI_WORDS; ++i) {
221b4f9fe12SLen Brown 		in_objs[i].type = ACPI_TYPE_INTEGER;
222b4f9fe12SLen Brown 		in_objs[i].integer.value = in[i];
223b4f9fe12SLen Brown 	}
224b4f9fe12SLen Brown 
225b4f9fe12SLen Brown 	results.length = sizeof(out_objs);
226b4f9fe12SLen Brown 	results.pointer = out_objs;
227b4f9fe12SLen Brown 
2286e02cc7eSSeth Forshee 	status = acpi_evaluate_object(dev->acpi_dev->handle,
2296e02cc7eSSeth Forshee 				      (char *)dev->method_hci, &params,
230b4f9fe12SLen Brown 				      &results);
231b4f9fe12SLen Brown 	if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) {
232b4f9fe12SLen Brown 		for (i = 0; i < out_objs->package.count; ++i) {
233b4f9fe12SLen Brown 			out[i] = out_objs->package.elements[i].integer.value;
234b4f9fe12SLen Brown 		}
235b4f9fe12SLen Brown 	}
236b4f9fe12SLen Brown 
237b4f9fe12SLen Brown 	return status;
238b4f9fe12SLen Brown }
239b4f9fe12SLen Brown 
240b4f9fe12SLen Brown /* common hci tasks (get or set one or two value)
241b4f9fe12SLen Brown  *
242b4f9fe12SLen Brown  * In addition to the ACPI status, the HCI system returns a result which
243b4f9fe12SLen Brown  * may be useful (such as "not supported").
244b4f9fe12SLen Brown  */
245b4f9fe12SLen Brown 
246135740deSSeth Forshee static acpi_status hci_write1(struct toshiba_acpi_dev *dev, u32 reg,
247135740deSSeth Forshee 			      u32 in1, u32 *result)
248b4f9fe12SLen Brown {
249b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 };
250b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
251135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
252b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
253b4f9fe12SLen Brown 	return status;
254b4f9fe12SLen Brown }
255b4f9fe12SLen Brown 
256135740deSSeth Forshee static acpi_status hci_read1(struct toshiba_acpi_dev *dev, u32 reg,
257135740deSSeth Forshee 			     u32 *out1, u32 *result)
258b4f9fe12SLen Brown {
259b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 };
260b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
261135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
262b4f9fe12SLen Brown 	*out1 = out[2];
263b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
264b4f9fe12SLen Brown 	return status;
265b4f9fe12SLen Brown }
266b4f9fe12SLen Brown 
267135740deSSeth Forshee static acpi_status hci_write2(struct toshiba_acpi_dev *dev, u32 reg,
268135740deSSeth Forshee 			      u32 in1, u32 in2, u32 *result)
269b4f9fe12SLen Brown {
270b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 };
271b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
272135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
273b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
274b4f9fe12SLen Brown 	return status;
275b4f9fe12SLen Brown }
276b4f9fe12SLen Brown 
277135740deSSeth Forshee static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg,
278135740deSSeth Forshee 			     u32 *out1, u32 *out2, u32 *result)
279b4f9fe12SLen Brown {
280b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 };
281b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
282135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
283b4f9fe12SLen Brown 	*out1 = out[2];
284b4f9fe12SLen Brown 	*out2 = out[3];
285b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
286b4f9fe12SLen Brown 	return status;
287b4f9fe12SLen Brown }
288b4f9fe12SLen Brown 
2896c3f6e6cSPierre Ducroquet /* Illumination support */
290135740deSSeth Forshee static int toshiba_illumination_available(struct toshiba_acpi_dev *dev)
2916c3f6e6cSPierre Ducroquet {
2926c3f6e6cSPierre Ducroquet 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
2936c3f6e6cSPierre Ducroquet 	u32 out[HCI_WORDS];
2946c3f6e6cSPierre Ducroquet 	acpi_status status;
2956c3f6e6cSPierre Ducroquet 
2966c3f6e6cSPierre Ducroquet 	in[0] = 0xf100;
297135740deSSeth Forshee 	status = hci_raw(dev, in, out);
2986c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
2997e33460dSJoe Perches 		pr_info("Illumination device not available\n");
3006c3f6e6cSPierre Ducroquet 		return 0;
3016c3f6e6cSPierre Ducroquet 	}
3026c3f6e6cSPierre Ducroquet 	in[0] = 0xf400;
303135740deSSeth Forshee 	status = hci_raw(dev, in, out);
3046c3f6e6cSPierre Ducroquet 	return 1;
3056c3f6e6cSPierre Ducroquet }
3066c3f6e6cSPierre Ducroquet 
3076c3f6e6cSPierre Ducroquet static void toshiba_illumination_set(struct led_classdev *cdev,
3086c3f6e6cSPierre Ducroquet 				     enum led_brightness brightness)
3096c3f6e6cSPierre Ducroquet {
310135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = container_of(cdev,
311135740deSSeth Forshee 			struct toshiba_acpi_dev, led_dev);
3126c3f6e6cSPierre Ducroquet 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
3136c3f6e6cSPierre Ducroquet 	u32 out[HCI_WORDS];
3146c3f6e6cSPierre Ducroquet 	acpi_status status;
3156c3f6e6cSPierre Ducroquet 
3166c3f6e6cSPierre Ducroquet 	/* First request : initialize communication. */
3176c3f6e6cSPierre Ducroquet 	in[0] = 0xf100;
318135740deSSeth Forshee 	status = hci_raw(dev, in, out);
3196c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
3207e33460dSJoe Perches 		pr_info("Illumination device not available\n");
3216c3f6e6cSPierre Ducroquet 		return;
3226c3f6e6cSPierre Ducroquet 	}
3236c3f6e6cSPierre Ducroquet 
3246c3f6e6cSPierre Ducroquet 	if (brightness) {
3256c3f6e6cSPierre Ducroquet 		/* Switch the illumination on */
3266c3f6e6cSPierre Ducroquet 		in[0] = 0xf400;
3276c3f6e6cSPierre Ducroquet 		in[1] = 0x14e;
3286c3f6e6cSPierre Ducroquet 		in[2] = 1;
329135740deSSeth Forshee 		status = hci_raw(dev, in, out);
3306c3f6e6cSPierre Ducroquet 		if (ACPI_FAILURE(status)) {
3317e33460dSJoe Perches 			pr_info("ACPI call for illumination failed\n");
3326c3f6e6cSPierre Ducroquet 			return;
3336c3f6e6cSPierre Ducroquet 		}
3346c3f6e6cSPierre Ducroquet 	} else {
3356c3f6e6cSPierre Ducroquet 		/* Switch the illumination off */
3366c3f6e6cSPierre Ducroquet 		in[0] = 0xf400;
3376c3f6e6cSPierre Ducroquet 		in[1] = 0x14e;
3386c3f6e6cSPierre Ducroquet 		in[2] = 0;
339135740deSSeth Forshee 		status = hci_raw(dev, in, out);
3406c3f6e6cSPierre Ducroquet 		if (ACPI_FAILURE(status)) {
3417e33460dSJoe Perches 			pr_info("ACPI call for illumination failed.\n");
3426c3f6e6cSPierre Ducroquet 			return;
3436c3f6e6cSPierre Ducroquet 		}
3446c3f6e6cSPierre Ducroquet 	}
3456c3f6e6cSPierre Ducroquet 
3466c3f6e6cSPierre Ducroquet 	/* Last request : close communication. */
3476c3f6e6cSPierre Ducroquet 	in[0] = 0xf200;
3486c3f6e6cSPierre Ducroquet 	in[1] = 0;
3496c3f6e6cSPierre Ducroquet 	in[2] = 0;
350135740deSSeth Forshee 	hci_raw(dev, in, out);
3516c3f6e6cSPierre Ducroquet }
3526c3f6e6cSPierre Ducroquet 
3536c3f6e6cSPierre Ducroquet static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
3546c3f6e6cSPierre Ducroquet {
355135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = container_of(cdev,
356135740deSSeth Forshee 			struct toshiba_acpi_dev, led_dev);
3576c3f6e6cSPierre Ducroquet 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
3586c3f6e6cSPierre Ducroquet 	u32 out[HCI_WORDS];
3596c3f6e6cSPierre Ducroquet 	acpi_status status;
3606c3f6e6cSPierre Ducroquet 	enum led_brightness result;
3616c3f6e6cSPierre Ducroquet 
3626c3f6e6cSPierre Ducroquet 	/* First request : initialize communication. */
3636c3f6e6cSPierre Ducroquet 	in[0] = 0xf100;
364135740deSSeth Forshee 	status = hci_raw(dev, in, out);
3656c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
3667e33460dSJoe Perches 		pr_info("Illumination device not available\n");
3676c3f6e6cSPierre Ducroquet 		return LED_OFF;
3686c3f6e6cSPierre Ducroquet 	}
3696c3f6e6cSPierre Ducroquet 
3706c3f6e6cSPierre Ducroquet 	/* Check the illumination */
3716c3f6e6cSPierre Ducroquet 	in[0] = 0xf300;
3726c3f6e6cSPierre Ducroquet 	in[1] = 0x14e;
373135740deSSeth Forshee 	status = hci_raw(dev, in, out);
3746c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
3757e33460dSJoe Perches 		pr_info("ACPI call for illumination failed.\n");
3766c3f6e6cSPierre Ducroquet 		return LED_OFF;
3776c3f6e6cSPierre Ducroquet 	}
3786c3f6e6cSPierre Ducroquet 
3796c3f6e6cSPierre Ducroquet 	result = out[2] ? LED_FULL : LED_OFF;
3806c3f6e6cSPierre Ducroquet 
3816c3f6e6cSPierre Ducroquet 	/* Last request : close communication. */
3826c3f6e6cSPierre Ducroquet 	in[0] = 0xf200;
3836c3f6e6cSPierre Ducroquet 	in[1] = 0;
3846c3f6e6cSPierre Ducroquet 	in[2] = 0;
385135740deSSeth Forshee 	hci_raw(dev, in, out);
3866c3f6e6cSPierre Ducroquet 
3876c3f6e6cSPierre Ducroquet 	return result;
3886c3f6e6cSPierre Ducroquet }
3896c3f6e6cSPierre Ducroquet 
390b4f9fe12SLen Brown /* Bluetooth rfkill handlers */
391b4f9fe12SLen Brown 
392135740deSSeth Forshee static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present)
393b4f9fe12SLen Brown {
394b4f9fe12SLen Brown 	u32 hci_result;
395b4f9fe12SLen Brown 	u32 value, value2;
396b4f9fe12SLen Brown 
397b4f9fe12SLen Brown 	value = 0;
398b4f9fe12SLen Brown 	value2 = 0;
399135740deSSeth Forshee 	hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result);
400b4f9fe12SLen Brown 	if (hci_result == HCI_SUCCESS)
401b4f9fe12SLen Brown 		*present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false;
402b4f9fe12SLen Brown 
403b4f9fe12SLen Brown 	return hci_result;
404b4f9fe12SLen Brown }
405b4f9fe12SLen Brown 
406135740deSSeth Forshee static u32 hci_get_radio_state(struct toshiba_acpi_dev *dev, bool *radio_state)
407b4f9fe12SLen Brown {
408b4f9fe12SLen Brown 	u32 hci_result;
409b4f9fe12SLen Brown 	u32 value, value2;
410b4f9fe12SLen Brown 
411b4f9fe12SLen Brown 	value = 0;
412b4f9fe12SLen Brown 	value2 = 0x0001;
413135740deSSeth Forshee 	hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result);
414b4f9fe12SLen Brown 
415b4f9fe12SLen Brown 	*radio_state = value & HCI_WIRELESS_KILL_SWITCH;
416b4f9fe12SLen Brown 	return hci_result;
417b4f9fe12SLen Brown }
418b4f9fe12SLen Brown 
41919d337dfSJohannes Berg static int bt_rfkill_set_block(void *data, bool blocked)
420b4f9fe12SLen Brown {
42119d337dfSJohannes Berg 	struct toshiba_acpi_dev *dev = data;
422b4f9fe12SLen Brown 	u32 result1, result2;
423b4f9fe12SLen Brown 	u32 value;
42419d337dfSJohannes Berg 	int err;
425b4f9fe12SLen Brown 	bool radio_state;
426b4f9fe12SLen Brown 
42719d337dfSJohannes Berg 	value = (blocked == false);
428b4f9fe12SLen Brown 
429b4f9fe12SLen Brown 	mutex_lock(&dev->mutex);
430135740deSSeth Forshee 	if (hci_get_radio_state(dev, &radio_state) != HCI_SUCCESS) {
43132bcd5cbSSeth Forshee 		err = -EIO;
43219d337dfSJohannes Berg 		goto out;
433b4f9fe12SLen Brown 	}
434b4f9fe12SLen Brown 
43519d337dfSJohannes Berg 	if (!radio_state) {
43619d337dfSJohannes Berg 		err = 0;
43719d337dfSJohannes Berg 		goto out;
43819d337dfSJohannes Berg 	}
43919d337dfSJohannes Berg 
440135740deSSeth Forshee 	hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
441135740deSSeth Forshee 	hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2);
44219d337dfSJohannes Berg 
44319d337dfSJohannes Berg 	if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
44432bcd5cbSSeth Forshee 		err = -EIO;
44519d337dfSJohannes Berg 	else
44619d337dfSJohannes Berg 		err = 0;
44719d337dfSJohannes Berg  out:
44819d337dfSJohannes Berg 	mutex_unlock(&dev->mutex);
44919d337dfSJohannes Berg 	return err;
45019d337dfSJohannes Berg }
45119d337dfSJohannes Berg 
45219d337dfSJohannes Berg static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
453b4f9fe12SLen Brown {
454b4f9fe12SLen Brown 	bool new_rfk_state;
455b4f9fe12SLen Brown 	bool value;
456b4f9fe12SLen Brown 	u32 hci_result;
45719d337dfSJohannes Berg 	struct toshiba_acpi_dev *dev = data;
45819d337dfSJohannes Berg 
45919d337dfSJohannes Berg 	mutex_lock(&dev->mutex);
460b4f9fe12SLen Brown 
461135740deSSeth Forshee 	hci_result = hci_get_radio_state(dev, &value);
46219d337dfSJohannes Berg 	if (hci_result != HCI_SUCCESS) {
46319d337dfSJohannes Berg 		/* Can't do anything useful */
46419d337dfSJohannes Berg 		mutex_unlock(&dev->mutex);
46582e7784fSJiri Slaby 		return;
46619d337dfSJohannes Berg 	}
467b4f9fe12SLen Brown 
468b4f9fe12SLen Brown 	new_rfk_state = value;
469b4f9fe12SLen Brown 
470b4f9fe12SLen Brown 	mutex_unlock(&dev->mutex);
471b4f9fe12SLen Brown 
47219d337dfSJohannes Berg 	if (rfkill_set_hw_state(rfkill, !new_rfk_state))
47319d337dfSJohannes Berg 		bt_rfkill_set_block(data, true);
474b4f9fe12SLen Brown }
47519d337dfSJohannes Berg 
47619d337dfSJohannes Berg static const struct rfkill_ops toshiba_rfk_ops = {
47719d337dfSJohannes Berg 	.set_block = bt_rfkill_set_block,
47819d337dfSJohannes Berg 	.poll = bt_rfkill_poll,
47919d337dfSJohannes Berg };
480b4f9fe12SLen Brown 
481b4f9fe12SLen Brown static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
482b4f9fe12SLen Brown 
483b4f9fe12SLen Brown static int get_lcd(struct backlight_device *bd)
484b4f9fe12SLen Brown {
485135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = bl_get_data(bd);
486b4f9fe12SLen Brown 	u32 hci_result;
487b4f9fe12SLen Brown 	u32 value;
488b4f9fe12SLen Brown 
489135740deSSeth Forshee 	hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result);
49032bcd5cbSSeth Forshee 	if (hci_result == HCI_SUCCESS)
491b4f9fe12SLen Brown 		return (value >> HCI_LCD_BRIGHTNESS_SHIFT);
49232bcd5cbSSeth Forshee 
49332bcd5cbSSeth Forshee 	return -EIO;
494b4f9fe12SLen Brown }
495b4f9fe12SLen Brown 
496936c8bcdSAlexey Dobriyan static int lcd_proc_show(struct seq_file *m, void *v)
497b4f9fe12SLen Brown {
498135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
499135740deSSeth Forshee 	int value;
500b4f9fe12SLen Brown 
501135740deSSeth Forshee 	if (!dev->backlight_dev)
502135740deSSeth Forshee 		return -ENODEV;
503135740deSSeth Forshee 
504135740deSSeth Forshee 	value = get_lcd(dev->backlight_dev);
505b4f9fe12SLen Brown 	if (value >= 0) {
506936c8bcdSAlexey Dobriyan 		seq_printf(m, "brightness:              %d\n", value);
507936c8bcdSAlexey Dobriyan 		seq_printf(m, "brightness_levels:       %d\n",
508b4f9fe12SLen Brown 			     HCI_LCD_BRIGHTNESS_LEVELS);
50932bcd5cbSSeth Forshee 		return 0;
510b4f9fe12SLen Brown 	}
511b4f9fe12SLen Brown 
51232bcd5cbSSeth Forshee 	pr_err("Error reading LCD brightness\n");
51332bcd5cbSSeth Forshee 	return -EIO;
514936c8bcdSAlexey Dobriyan }
515936c8bcdSAlexey Dobriyan 
516936c8bcdSAlexey Dobriyan static int lcd_proc_open(struct inode *inode, struct file *file)
517936c8bcdSAlexey Dobriyan {
518135740deSSeth Forshee 	return single_open(file, lcd_proc_show, PDE(inode)->data);
519b4f9fe12SLen Brown }
520b4f9fe12SLen Brown 
521135740deSSeth Forshee static int set_lcd(struct toshiba_acpi_dev *dev, int value)
522b4f9fe12SLen Brown {
523b4f9fe12SLen Brown 	u32 hci_result;
524b4f9fe12SLen Brown 
525b4f9fe12SLen Brown 	value = value << HCI_LCD_BRIGHTNESS_SHIFT;
526135740deSSeth Forshee 	hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result);
52732bcd5cbSSeth Forshee 	return hci_result == HCI_SUCCESS ? 0 : -EIO;
528b4f9fe12SLen Brown }
529b4f9fe12SLen Brown 
530b4f9fe12SLen Brown static int set_lcd_status(struct backlight_device *bd)
531b4f9fe12SLen Brown {
532135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = bl_get_data(bd);
533135740deSSeth Forshee 	return set_lcd(dev, bd->props.brightness);
534b4f9fe12SLen Brown }
535b4f9fe12SLen Brown 
536936c8bcdSAlexey Dobriyan static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
537936c8bcdSAlexey Dobriyan 			      size_t count, loff_t *pos)
538b4f9fe12SLen Brown {
539135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
540936c8bcdSAlexey Dobriyan 	char cmd[42];
541936c8bcdSAlexey Dobriyan 	size_t len;
542b4f9fe12SLen Brown 	int value;
543b4f9fe12SLen Brown 	int ret;
544b4f9fe12SLen Brown 
545936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
546936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
547936c8bcdSAlexey Dobriyan 		return -EFAULT;
548936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
549936c8bcdSAlexey Dobriyan 
550936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " brightness : %i", &value) == 1 &&
551b4f9fe12SLen Brown 	    value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) {
552135740deSSeth Forshee 		ret = set_lcd(dev, value);
553b4f9fe12SLen Brown 		if (ret == 0)
554b4f9fe12SLen Brown 			ret = count;
555b4f9fe12SLen Brown 	} else {
556b4f9fe12SLen Brown 		ret = -EINVAL;
557b4f9fe12SLen Brown 	}
558b4f9fe12SLen Brown 	return ret;
559b4f9fe12SLen Brown }
560b4f9fe12SLen Brown 
561936c8bcdSAlexey Dobriyan static const struct file_operations lcd_proc_fops = {
562936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
563936c8bcdSAlexey Dobriyan 	.open		= lcd_proc_open,
564936c8bcdSAlexey Dobriyan 	.read		= seq_read,
565936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
566936c8bcdSAlexey Dobriyan 	.release	= single_release,
567936c8bcdSAlexey Dobriyan 	.write		= lcd_proc_write,
568936c8bcdSAlexey Dobriyan };
569936c8bcdSAlexey Dobriyan 
57036d03f93SSeth Forshee static int get_video_status(struct toshiba_acpi_dev *dev, u32 *status)
57136d03f93SSeth Forshee {
57236d03f93SSeth Forshee 	u32 hci_result;
57336d03f93SSeth Forshee 
57436d03f93SSeth Forshee 	hci_read1(dev, HCI_VIDEO_OUT, status, &hci_result);
57536d03f93SSeth Forshee 	return hci_result == HCI_SUCCESS ? 0 : -EIO;
57636d03f93SSeth Forshee }
57736d03f93SSeth Forshee 
578936c8bcdSAlexey Dobriyan static int video_proc_show(struct seq_file *m, void *v)
579b4f9fe12SLen Brown {
580135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
581b4f9fe12SLen Brown 	u32 value;
58236d03f93SSeth Forshee 	int ret;
583b4f9fe12SLen Brown 
58436d03f93SSeth Forshee 	ret = get_video_status(dev, &value);
58536d03f93SSeth Forshee 	if (!ret) {
586b4f9fe12SLen Brown 		int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
587b4f9fe12SLen Brown 		int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
588b4f9fe12SLen Brown 		int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
589936c8bcdSAlexey Dobriyan 		seq_printf(m, "lcd_out:                 %d\n", is_lcd);
590936c8bcdSAlexey Dobriyan 		seq_printf(m, "crt_out:                 %d\n", is_crt);
591936c8bcdSAlexey Dobriyan 		seq_printf(m, "tv_out:                  %d\n", is_tv);
592b4f9fe12SLen Brown 	}
593b4f9fe12SLen Brown 
59436d03f93SSeth Forshee 	return ret;
595b4f9fe12SLen Brown }
596b4f9fe12SLen Brown 
597936c8bcdSAlexey Dobriyan static int video_proc_open(struct inode *inode, struct file *file)
598b4f9fe12SLen Brown {
599135740deSSeth Forshee 	return single_open(file, video_proc_show, PDE(inode)->data);
600936c8bcdSAlexey Dobriyan }
601936c8bcdSAlexey Dobriyan 
602936c8bcdSAlexey Dobriyan static ssize_t video_proc_write(struct file *file, const char __user *buf,
603936c8bcdSAlexey Dobriyan 				size_t count, loff_t *pos)
604936c8bcdSAlexey Dobriyan {
605135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
606936c8bcdSAlexey Dobriyan 	char *cmd, *buffer;
60736d03f93SSeth Forshee 	int ret;
608b4f9fe12SLen Brown 	int value;
609b4f9fe12SLen Brown 	int remain = count;
610b4f9fe12SLen Brown 	int lcd_out = -1;
611b4f9fe12SLen Brown 	int crt_out = -1;
612b4f9fe12SLen Brown 	int tv_out = -1;
613b4f9fe12SLen Brown 	u32 video_out;
614b4f9fe12SLen Brown 
615936c8bcdSAlexey Dobriyan 	cmd = kmalloc(count + 1, GFP_KERNEL);
616936c8bcdSAlexey Dobriyan 	if (!cmd)
617936c8bcdSAlexey Dobriyan 		return -ENOMEM;
618936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, count)) {
619936c8bcdSAlexey Dobriyan 		kfree(cmd);
620936c8bcdSAlexey Dobriyan 		return -EFAULT;
621936c8bcdSAlexey Dobriyan 	}
622936c8bcdSAlexey Dobriyan 	cmd[count] = '\0';
623936c8bcdSAlexey Dobriyan 
624936c8bcdSAlexey Dobriyan 	buffer = cmd;
625936c8bcdSAlexey Dobriyan 
626b4f9fe12SLen Brown 	/* scan expression.  Multiple expressions may be delimited with ;
627b4f9fe12SLen Brown 	 *
628b4f9fe12SLen Brown 	 *  NOTE: to keep scanning simple, invalid fields are ignored
629b4f9fe12SLen Brown 	 */
630b4f9fe12SLen Brown 	while (remain) {
631b4f9fe12SLen Brown 		if (sscanf(buffer, " lcd_out : %i", &value) == 1)
632b4f9fe12SLen Brown 			lcd_out = value & 1;
633b4f9fe12SLen Brown 		else if (sscanf(buffer, " crt_out : %i", &value) == 1)
634b4f9fe12SLen Brown 			crt_out = value & 1;
635b4f9fe12SLen Brown 		else if (sscanf(buffer, " tv_out : %i", &value) == 1)
636b4f9fe12SLen Brown 			tv_out = value & 1;
637b4f9fe12SLen Brown 		/* advance to one character past the next ; */
638b4f9fe12SLen Brown 		do {
639b4f9fe12SLen Brown 			++buffer;
640b4f9fe12SLen Brown 			--remain;
641b4f9fe12SLen Brown 		}
642b4f9fe12SLen Brown 		while (remain && *(buffer - 1) != ';');
643b4f9fe12SLen Brown 	}
644b4f9fe12SLen Brown 
645936c8bcdSAlexey Dobriyan 	kfree(cmd);
646936c8bcdSAlexey Dobriyan 
64736d03f93SSeth Forshee 	ret = get_video_status(dev, &video_out);
64836d03f93SSeth Forshee 	if (!ret) {
649b4f9fe12SLen Brown 		unsigned int new_video_out = video_out;
650b4f9fe12SLen Brown 		if (lcd_out != -1)
651b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out);
652b4f9fe12SLen Brown 		if (crt_out != -1)
653b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out);
654b4f9fe12SLen Brown 		if (tv_out != -1)
655b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out);
656b4f9fe12SLen Brown 		/* To avoid unnecessary video disruption, only write the new
657b4f9fe12SLen Brown 		 * video setting if something changed. */
658b4f9fe12SLen Brown 		if (new_video_out != video_out)
65932bcd5cbSSeth Forshee 			ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out);
660b4f9fe12SLen Brown 	}
661b4f9fe12SLen Brown 
66232bcd5cbSSeth Forshee 	return ret ? ret : count;
663b4f9fe12SLen Brown }
664b4f9fe12SLen Brown 
665936c8bcdSAlexey Dobriyan static const struct file_operations video_proc_fops = {
666936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
667936c8bcdSAlexey Dobriyan 	.open		= video_proc_open,
668936c8bcdSAlexey Dobriyan 	.read		= seq_read,
669936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
670936c8bcdSAlexey Dobriyan 	.release	= single_release,
671936c8bcdSAlexey Dobriyan 	.write		= video_proc_write,
672936c8bcdSAlexey Dobriyan };
673936c8bcdSAlexey Dobriyan 
67436d03f93SSeth Forshee static int get_fan_status(struct toshiba_acpi_dev *dev, u32 *status)
67536d03f93SSeth Forshee {
67636d03f93SSeth Forshee 	u32 hci_result;
67736d03f93SSeth Forshee 
67836d03f93SSeth Forshee 	hci_read1(dev, HCI_FAN, status, &hci_result);
67936d03f93SSeth Forshee 	return hci_result == HCI_SUCCESS ? 0 : -EIO;
68036d03f93SSeth Forshee }
68136d03f93SSeth Forshee 
682936c8bcdSAlexey Dobriyan static int fan_proc_show(struct seq_file *m, void *v)
683b4f9fe12SLen Brown {
684135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
68536d03f93SSeth Forshee 	int ret;
686b4f9fe12SLen Brown 	u32 value;
687b4f9fe12SLen Brown 
68836d03f93SSeth Forshee 	ret = get_fan_status(dev, &value);
68936d03f93SSeth Forshee 	if (!ret) {
690936c8bcdSAlexey Dobriyan 		seq_printf(m, "running:                 %d\n", (value > 0));
691135740deSSeth Forshee 		seq_printf(m, "force_on:                %d\n", dev->force_fan);
692b4f9fe12SLen Brown 	}
693b4f9fe12SLen Brown 
69436d03f93SSeth Forshee 	return ret;
695b4f9fe12SLen Brown }
696b4f9fe12SLen Brown 
697936c8bcdSAlexey Dobriyan static int fan_proc_open(struct inode *inode, struct file *file)
698b4f9fe12SLen Brown {
699135740deSSeth Forshee 	return single_open(file, fan_proc_show, PDE(inode)->data);
700936c8bcdSAlexey Dobriyan }
701936c8bcdSAlexey Dobriyan 
702936c8bcdSAlexey Dobriyan static ssize_t fan_proc_write(struct file *file, const char __user *buf,
703936c8bcdSAlexey Dobriyan 			      size_t count, loff_t *pos)
704936c8bcdSAlexey Dobriyan {
705135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
706936c8bcdSAlexey Dobriyan 	char cmd[42];
707936c8bcdSAlexey Dobriyan 	size_t len;
708b4f9fe12SLen Brown 	int value;
709b4f9fe12SLen Brown 	u32 hci_result;
710b4f9fe12SLen Brown 
711936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
712936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
713936c8bcdSAlexey Dobriyan 		return -EFAULT;
714936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
715936c8bcdSAlexey Dobriyan 
716936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " force_on : %i", &value) == 1 &&
717b4f9fe12SLen Brown 	    value >= 0 && value <= 1) {
718135740deSSeth Forshee 		hci_write1(dev, HCI_FAN, value, &hci_result);
719b4f9fe12SLen Brown 		if (hci_result != HCI_SUCCESS)
72032bcd5cbSSeth Forshee 			return -EIO;
721b4f9fe12SLen Brown 		else
722135740deSSeth Forshee 			dev->force_fan = value;
723b4f9fe12SLen Brown 	} else {
724b4f9fe12SLen Brown 		return -EINVAL;
725b4f9fe12SLen Brown 	}
726b4f9fe12SLen Brown 
727b4f9fe12SLen Brown 	return count;
728b4f9fe12SLen Brown }
729b4f9fe12SLen Brown 
730936c8bcdSAlexey Dobriyan static const struct file_operations fan_proc_fops = {
731936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
732936c8bcdSAlexey Dobriyan 	.open		= fan_proc_open,
733936c8bcdSAlexey Dobriyan 	.read		= seq_read,
734936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
735936c8bcdSAlexey Dobriyan 	.release	= single_release,
736936c8bcdSAlexey Dobriyan 	.write		= fan_proc_write,
737936c8bcdSAlexey Dobriyan };
738936c8bcdSAlexey Dobriyan 
739936c8bcdSAlexey Dobriyan static int keys_proc_show(struct seq_file *m, void *v)
740b4f9fe12SLen Brown {
741135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
742b4f9fe12SLen Brown 	u32 hci_result;
743b4f9fe12SLen Brown 	u32 value;
744b4f9fe12SLen Brown 
74511948b93SSeth Forshee 	if (!dev->key_event_valid && dev->system_event_supported) {
746135740deSSeth Forshee 		hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
747b4f9fe12SLen Brown 		if (hci_result == HCI_SUCCESS) {
748135740deSSeth Forshee 			dev->key_event_valid = 1;
749135740deSSeth Forshee 			dev->last_key_event = value;
750b4f9fe12SLen Brown 		} else if (hci_result == HCI_EMPTY) {
751b4f9fe12SLen Brown 			/* better luck next time */
752b4f9fe12SLen Brown 		} else if (hci_result == HCI_NOT_SUPPORTED) {
753b4f9fe12SLen Brown 			/* This is a workaround for an unresolved issue on
754b4f9fe12SLen Brown 			 * some machines where system events sporadically
755b4f9fe12SLen Brown 			 * become disabled. */
756135740deSSeth Forshee 			hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
7577e33460dSJoe Perches 			pr_notice("Re-enabled hotkeys\n");
758b4f9fe12SLen Brown 		} else {
7597e33460dSJoe Perches 			pr_err("Error reading hotkey status\n");
76032bcd5cbSSeth Forshee 			return -EIO;
761b4f9fe12SLen Brown 		}
762b4f9fe12SLen Brown 	}
763b4f9fe12SLen Brown 
764135740deSSeth Forshee 	seq_printf(m, "hotkey_ready:            %d\n", dev->key_event_valid);
765135740deSSeth Forshee 	seq_printf(m, "hotkey:                  0x%04x\n", dev->last_key_event);
766936c8bcdSAlexey Dobriyan 	return 0;
767b4f9fe12SLen Brown }
768b4f9fe12SLen Brown 
769936c8bcdSAlexey Dobriyan static int keys_proc_open(struct inode *inode, struct file *file)
770b4f9fe12SLen Brown {
771135740deSSeth Forshee 	return single_open(file, keys_proc_show, PDE(inode)->data);
772936c8bcdSAlexey Dobriyan }
773936c8bcdSAlexey Dobriyan 
774936c8bcdSAlexey Dobriyan static ssize_t keys_proc_write(struct file *file, const char __user *buf,
775936c8bcdSAlexey Dobriyan 			       size_t count, loff_t *pos)
776936c8bcdSAlexey Dobriyan {
777135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
778936c8bcdSAlexey Dobriyan 	char cmd[42];
779936c8bcdSAlexey Dobriyan 	size_t len;
780b4f9fe12SLen Brown 	int value;
781b4f9fe12SLen Brown 
782936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
783936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
784936c8bcdSAlexey Dobriyan 		return -EFAULT;
785936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
786936c8bcdSAlexey Dobriyan 
787936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) {
788135740deSSeth Forshee 		dev->key_event_valid = 0;
789b4f9fe12SLen Brown 	} else {
790b4f9fe12SLen Brown 		return -EINVAL;
791b4f9fe12SLen Brown 	}
792b4f9fe12SLen Brown 
793b4f9fe12SLen Brown 	return count;
794b4f9fe12SLen Brown }
795b4f9fe12SLen Brown 
796936c8bcdSAlexey Dobriyan static const struct file_operations keys_proc_fops = {
797936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
798936c8bcdSAlexey Dobriyan 	.open		= keys_proc_open,
799936c8bcdSAlexey Dobriyan 	.read		= seq_read,
800936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
801936c8bcdSAlexey Dobriyan 	.release	= single_release,
802936c8bcdSAlexey Dobriyan 	.write		= keys_proc_write,
803936c8bcdSAlexey Dobriyan };
804936c8bcdSAlexey Dobriyan 
805936c8bcdSAlexey Dobriyan static int version_proc_show(struct seq_file *m, void *v)
806b4f9fe12SLen Brown {
807936c8bcdSAlexey Dobriyan 	seq_printf(m, "driver:                  %s\n", TOSHIBA_ACPI_VERSION);
808936c8bcdSAlexey Dobriyan 	seq_printf(m, "proc_interface:          %d\n", PROC_INTERFACE_VERSION);
809936c8bcdSAlexey Dobriyan 	return 0;
810b4f9fe12SLen Brown }
811b4f9fe12SLen Brown 
812936c8bcdSAlexey Dobriyan static int version_proc_open(struct inode *inode, struct file *file)
813936c8bcdSAlexey Dobriyan {
814936c8bcdSAlexey Dobriyan 	return single_open(file, version_proc_show, PDE(inode)->data);
815936c8bcdSAlexey Dobriyan }
816936c8bcdSAlexey Dobriyan 
817936c8bcdSAlexey Dobriyan static const struct file_operations version_proc_fops = {
818936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
819936c8bcdSAlexey Dobriyan 	.open		= version_proc_open,
820936c8bcdSAlexey Dobriyan 	.read		= seq_read,
821936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
822936c8bcdSAlexey Dobriyan 	.release	= single_release,
823936c8bcdSAlexey Dobriyan };
824936c8bcdSAlexey Dobriyan 
825b4f9fe12SLen Brown /* proc and module init
826b4f9fe12SLen Brown  */
827b4f9fe12SLen Brown 
828b4f9fe12SLen Brown #define PROC_TOSHIBA		"toshiba"
829b4f9fe12SLen Brown 
830135740deSSeth Forshee static void __devinit
831135740deSSeth Forshee create_toshiba_proc_entries(struct toshiba_acpi_dev *dev)
832b4f9fe12SLen Brown {
83336d03f93SSeth Forshee 	if (dev->backlight_dev)
834135740deSSeth Forshee 		proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir,
835135740deSSeth Forshee 				 &lcd_proc_fops, dev);
83636d03f93SSeth Forshee 	if (dev->video_supported)
837135740deSSeth Forshee 		proc_create_data("video", S_IRUGO | S_IWUSR, toshiba_proc_dir,
838135740deSSeth Forshee 				 &video_proc_fops, dev);
83936d03f93SSeth Forshee 	if (dev->fan_supported)
840135740deSSeth Forshee 		proc_create_data("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir,
841135740deSSeth Forshee 				 &fan_proc_fops, dev);
84236d03f93SSeth Forshee 	if (dev->hotkey_dev)
843135740deSSeth Forshee 		proc_create_data("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir,
844135740deSSeth Forshee 				 &keys_proc_fops, dev);
845135740deSSeth Forshee 	proc_create_data("version", S_IRUGO, toshiba_proc_dir,
846135740deSSeth Forshee 			 &version_proc_fops, dev);
847b4f9fe12SLen Brown }
848b4f9fe12SLen Brown 
84936d03f93SSeth Forshee static void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev)
850b4f9fe12SLen Brown {
85136d03f93SSeth Forshee 	if (dev->backlight_dev)
852936c8bcdSAlexey Dobriyan 		remove_proc_entry("lcd", toshiba_proc_dir);
85336d03f93SSeth Forshee 	if (dev->video_supported)
854936c8bcdSAlexey Dobriyan 		remove_proc_entry("video", toshiba_proc_dir);
85536d03f93SSeth Forshee 	if (dev->fan_supported)
856936c8bcdSAlexey Dobriyan 		remove_proc_entry("fan", toshiba_proc_dir);
85736d03f93SSeth Forshee 	if (dev->hotkey_dev)
858936c8bcdSAlexey Dobriyan 		remove_proc_entry("keys", toshiba_proc_dir);
859936c8bcdSAlexey Dobriyan 	remove_proc_entry("version", toshiba_proc_dir);
860b4f9fe12SLen Brown }
861b4f9fe12SLen Brown 
862acc2472eSLionel Debroux static const struct backlight_ops toshiba_backlight_data = {
863b4f9fe12SLen Brown         .get_brightness = get_lcd,
864b4f9fe12SLen Brown         .update_status  = set_lcd_status,
865b4f9fe12SLen Brown };
866b4f9fe12SLen Brown 
86729cd293fSSeth Forshee static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
86829cd293fSSeth Forshee 				      struct serio *port)
86929cd293fSSeth Forshee {
87029cd293fSSeth Forshee 	if (str & 0x20)
87129cd293fSSeth Forshee 		return false;
87229cd293fSSeth Forshee 
87329cd293fSSeth Forshee 	if (unlikely(data == 0xe0))
87429cd293fSSeth Forshee 		return false;
87529cd293fSSeth Forshee 
87629cd293fSSeth Forshee 	if ((data & 0x7f) == TOS1900_FN_SCAN) {
87729cd293fSSeth Forshee 		schedule_work(&toshiba_acpi->hotkey_work);
87829cd293fSSeth Forshee 		return true;
87929cd293fSSeth Forshee 	}
88029cd293fSSeth Forshee 
88129cd293fSSeth Forshee 	return false;
88229cd293fSSeth Forshee }
88329cd293fSSeth Forshee 
88429cd293fSSeth Forshee static void toshiba_acpi_hotkey_work(struct work_struct *work)
88529cd293fSSeth Forshee {
88629cd293fSSeth Forshee 	acpi_handle ec_handle = ec_get_handle();
88729cd293fSSeth Forshee 	acpi_status status;
88829cd293fSSeth Forshee 
88929cd293fSSeth Forshee 	if (!ec_handle)
89029cd293fSSeth Forshee 		return;
89129cd293fSSeth Forshee 
89229cd293fSSeth Forshee 	status = acpi_evaluate_object(ec_handle, "NTFY", NULL, NULL);
89329cd293fSSeth Forshee 	if (ACPI_FAILURE(status))
89429cd293fSSeth Forshee 		pr_err("ACPI NTFY method execution failed\n");
89529cd293fSSeth Forshee }
89629cd293fSSeth Forshee 
89729cd293fSSeth Forshee /*
89829cd293fSSeth Forshee  * Returns hotkey scancode, or < 0 on failure.
89929cd293fSSeth Forshee  */
90029cd293fSSeth Forshee static int toshiba_acpi_query_hotkey(struct toshiba_acpi_dev *dev)
90129cd293fSSeth Forshee {
90229cd293fSSeth Forshee 	struct acpi_buffer buf;
90329cd293fSSeth Forshee 	union acpi_object out_obj;
90429cd293fSSeth Forshee 	acpi_status status;
90529cd293fSSeth Forshee 
90629cd293fSSeth Forshee 	buf.pointer = &out_obj;
90729cd293fSSeth Forshee 	buf.length = sizeof(out_obj);
90829cd293fSSeth Forshee 
90929cd293fSSeth Forshee 	status = acpi_evaluate_object(dev->acpi_dev->handle, "INFO",
91029cd293fSSeth Forshee 				      NULL, &buf);
91129cd293fSSeth Forshee 	if (ACPI_FAILURE(status) || out_obj.type != ACPI_TYPE_INTEGER) {
91229cd293fSSeth Forshee 		pr_err("ACPI INFO method execution failed\n");
91329cd293fSSeth Forshee 		return -EIO;
91429cd293fSSeth Forshee 	}
91529cd293fSSeth Forshee 
91629cd293fSSeth Forshee 	return out_obj.integer.value;
91729cd293fSSeth Forshee }
91829cd293fSSeth Forshee 
91929cd293fSSeth Forshee static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev,
92029cd293fSSeth Forshee 				       int scancode)
92129cd293fSSeth Forshee {
92229cd293fSSeth Forshee 	if (scancode == 0x100)
92329cd293fSSeth Forshee 		return;
92429cd293fSSeth Forshee 
92529cd293fSSeth Forshee 	/* act on key press; ignore key release */
92629cd293fSSeth Forshee 	if (scancode & 0x80)
92729cd293fSSeth Forshee 		return;
92829cd293fSSeth Forshee 
92929cd293fSSeth Forshee 	if (!sparse_keymap_report_event(dev->hotkey_dev, scancode, 1, true))
93029cd293fSSeth Forshee 		pr_info("Unknown key %x\n", scancode);
93129cd293fSSeth Forshee }
93229cd293fSSeth Forshee 
9336e02cc7eSSeth Forshee static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
9346335e4d5SMatthew Garrett {
935135740deSSeth Forshee 	acpi_status status;
93629cd293fSSeth Forshee 	acpi_handle ec_handle, handle;
937135740deSSeth Forshee 	int error;
93829cd293fSSeth Forshee 	u32 hci_result;
939135740deSSeth Forshee 
940135740deSSeth Forshee 	dev->hotkey_dev = input_allocate_device();
941135740deSSeth Forshee 	if (!dev->hotkey_dev) {
942135740deSSeth Forshee 		pr_info("Unable to register input device\n");
943135740deSSeth Forshee 		return -ENOMEM;
944135740deSSeth Forshee 	}
945135740deSSeth Forshee 
946135740deSSeth Forshee 	dev->hotkey_dev->name = "Toshiba input device";
9476e02cc7eSSeth Forshee 	dev->hotkey_dev->phys = "toshiba_acpi/input0";
948135740deSSeth Forshee 	dev->hotkey_dev->id.bustype = BUS_HOST;
949135740deSSeth Forshee 
950135740deSSeth Forshee 	error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL);
951135740deSSeth Forshee 	if (error)
952135740deSSeth Forshee 		goto err_free_dev;
953135740deSSeth Forshee 
95429cd293fSSeth Forshee 	/*
95529cd293fSSeth Forshee 	 * For some machines the SCI responsible for providing hotkey
95629cd293fSSeth Forshee 	 * notification doesn't fire. We can trigger the notification
95729cd293fSSeth Forshee 	 * whenever the Fn key is pressed using the NTFY method, if
95829cd293fSSeth Forshee 	 * supported, so if it's present set up an i8042 key filter
95929cd293fSSeth Forshee 	 * for this purpose.
96029cd293fSSeth Forshee 	 */
96129cd293fSSeth Forshee 	status = AE_ERROR;
96229cd293fSSeth Forshee 	ec_handle = ec_get_handle();
96329cd293fSSeth Forshee 	if (ec_handle)
96429cd293fSSeth Forshee 		status = acpi_get_handle(ec_handle, "NTFY", &handle);
96529cd293fSSeth Forshee 
96629cd293fSSeth Forshee 	if (ACPI_SUCCESS(status)) {
96729cd293fSSeth Forshee 		INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);
96829cd293fSSeth Forshee 
96929cd293fSSeth Forshee 		error = i8042_install_filter(toshiba_acpi_i8042_filter);
97029cd293fSSeth Forshee 		if (error) {
97129cd293fSSeth Forshee 			pr_err("Error installing key filter\n");
97229cd293fSSeth Forshee 			goto err_free_keymap;
97329cd293fSSeth Forshee 		}
97429cd293fSSeth Forshee 
97529cd293fSSeth Forshee 		dev->ntfy_supported = 1;
97629cd293fSSeth Forshee 	}
97729cd293fSSeth Forshee 
97829cd293fSSeth Forshee 	/*
97929cd293fSSeth Forshee 	 * Determine hotkey query interface. Prefer using the INFO
98029cd293fSSeth Forshee 	 * method when it is available.
98129cd293fSSeth Forshee 	 */
98229cd293fSSeth Forshee 	status = acpi_get_handle(dev->acpi_dev->handle, "INFO", &handle);
98329cd293fSSeth Forshee 	if (ACPI_SUCCESS(status)) {
98429cd293fSSeth Forshee 		dev->info_supported = 1;
98529cd293fSSeth Forshee 	} else {
98629cd293fSSeth Forshee 		hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
98729cd293fSSeth Forshee 		if (hci_result == HCI_SUCCESS)
98829cd293fSSeth Forshee 			dev->system_event_supported = 1;
98929cd293fSSeth Forshee 	}
99029cd293fSSeth Forshee 
99129cd293fSSeth Forshee 	if (!dev->info_supported && !dev->system_event_supported) {
99229cd293fSSeth Forshee 		pr_warn("No hotkey query interface found\n");
99329cd293fSSeth Forshee 		goto err_remove_filter;
99429cd293fSSeth Forshee 	}
99529cd293fSSeth Forshee 
9966e02cc7eSSeth Forshee 	status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL);
997135740deSSeth Forshee 	if (ACPI_FAILURE(status)) {
998135740deSSeth Forshee 		pr_info("Unable to enable hotkeys\n");
999135740deSSeth Forshee 		error = -ENODEV;
100029cd293fSSeth Forshee 		goto err_remove_filter;
1001135740deSSeth Forshee 	}
1002135740deSSeth Forshee 
1003135740deSSeth Forshee 	error = input_register_device(dev->hotkey_dev);
1004135740deSSeth Forshee 	if (error) {
1005135740deSSeth Forshee 		pr_info("Unable to register input device\n");
100629cd293fSSeth Forshee 		goto err_remove_filter;
1007135740deSSeth Forshee 	}
1008135740deSSeth Forshee 
100929cd293fSSeth Forshee 	hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &hci_result);
1010135740deSSeth Forshee 	return 0;
1011135740deSSeth Forshee 
101229cd293fSSeth Forshee  err_remove_filter:
101329cd293fSSeth Forshee 	if (dev->ntfy_supported)
101429cd293fSSeth Forshee 		i8042_remove_filter(toshiba_acpi_i8042_filter);
1015135740deSSeth Forshee  err_free_keymap:
1016135740deSSeth Forshee 	sparse_keymap_free(dev->hotkey_dev);
1017135740deSSeth Forshee  err_free_dev:
1018135740deSSeth Forshee 	input_free_device(dev->hotkey_dev);
1019135740deSSeth Forshee 	dev->hotkey_dev = NULL;
1020135740deSSeth Forshee 	return error;
1021135740deSSeth Forshee }
1022135740deSSeth Forshee 
1023135740deSSeth Forshee static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type)
1024135740deSSeth Forshee {
1025135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
1026135740deSSeth Forshee 
102736d03f93SSeth Forshee 	remove_toshiba_proc_entries(dev);
1028135740deSSeth Forshee 
102929cd293fSSeth Forshee 	if (dev->ntfy_supported) {
103029cd293fSSeth Forshee 		i8042_remove_filter(toshiba_acpi_i8042_filter);
103129cd293fSSeth Forshee 		cancel_work_sync(&dev->hotkey_work);
103229cd293fSSeth Forshee 	}
103329cd293fSSeth Forshee 
1034135740deSSeth Forshee 	if (dev->hotkey_dev) {
1035135740deSSeth Forshee 		input_unregister_device(dev->hotkey_dev);
1036135740deSSeth Forshee 		sparse_keymap_free(dev->hotkey_dev);
1037135740deSSeth Forshee 	}
1038135740deSSeth Forshee 
1039135740deSSeth Forshee 	if (dev->bt_rfk) {
1040135740deSSeth Forshee 		rfkill_unregister(dev->bt_rfk);
1041135740deSSeth Forshee 		rfkill_destroy(dev->bt_rfk);
1042135740deSSeth Forshee 	}
1043135740deSSeth Forshee 
1044135740deSSeth Forshee 	if (dev->backlight_dev)
1045135740deSSeth Forshee 		backlight_device_unregister(dev->backlight_dev);
1046135740deSSeth Forshee 
104736d03f93SSeth Forshee 	if (dev->illumination_supported)
1048135740deSSeth Forshee 		led_classdev_unregister(&dev->led_dev);
1049135740deSSeth Forshee 
105029cd293fSSeth Forshee 	if (toshiba_acpi)
105129cd293fSSeth Forshee 		toshiba_acpi = NULL;
105229cd293fSSeth Forshee 
1053135740deSSeth Forshee 	kfree(dev);
1054135740deSSeth Forshee 
1055135740deSSeth Forshee 	return 0;
1056135740deSSeth Forshee }
1057135740deSSeth Forshee 
1058a540d6b5SSeth Forshee static const char * __devinit find_hci_method(acpi_handle handle)
1059a540d6b5SSeth Forshee {
1060a540d6b5SSeth Forshee 	acpi_status status;
1061a540d6b5SSeth Forshee 	acpi_handle hci_handle;
1062a540d6b5SSeth Forshee 
1063a540d6b5SSeth Forshee 	status = acpi_get_handle(handle, "GHCI", &hci_handle);
1064a540d6b5SSeth Forshee 	if (ACPI_SUCCESS(status))
1065a540d6b5SSeth Forshee 		return "GHCI";
1066a540d6b5SSeth Forshee 
1067a540d6b5SSeth Forshee 	status = acpi_get_handle(handle, "SPFC", &hci_handle);
1068a540d6b5SSeth Forshee 	if (ACPI_SUCCESS(status))
1069a540d6b5SSeth Forshee 		return "SPFC";
1070a540d6b5SSeth Forshee 
1071a540d6b5SSeth Forshee 	return NULL;
1072a540d6b5SSeth Forshee }
1073a540d6b5SSeth Forshee 
1074135740deSSeth Forshee static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
1075135740deSSeth Forshee {
1076135740deSSeth Forshee 	struct toshiba_acpi_dev *dev;
1077a540d6b5SSeth Forshee 	const char *hci_method;
107836d03f93SSeth Forshee 	u32 dummy;
1079135740deSSeth Forshee 	bool bt_present;
1080135740deSSeth Forshee 	int ret = 0;
1081135740deSSeth Forshee 	struct backlight_properties props;
1082135740deSSeth Forshee 
108329cd293fSSeth Forshee 	if (toshiba_acpi)
108429cd293fSSeth Forshee 		return -EBUSY;
108529cd293fSSeth Forshee 
1086135740deSSeth Forshee 	pr_info("Toshiba Laptop ACPI Extras version %s\n",
1087135740deSSeth Forshee 	       TOSHIBA_ACPI_VERSION);
1088135740deSSeth Forshee 
1089a540d6b5SSeth Forshee 	hci_method = find_hci_method(acpi_dev->handle);
1090a540d6b5SSeth Forshee 	if (!hci_method) {
1091a540d6b5SSeth Forshee 		pr_err("HCI interface not found\n");
10926e02cc7eSSeth Forshee 		return -ENODEV;
1093a540d6b5SSeth Forshee 	}
10946e02cc7eSSeth Forshee 
1095135740deSSeth Forshee 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1096135740deSSeth Forshee 	if (!dev)
1097135740deSSeth Forshee 		return -ENOMEM;
1098135740deSSeth Forshee 	dev->acpi_dev = acpi_dev;
1099a540d6b5SSeth Forshee 	dev->method_hci = hci_method;
1100135740deSSeth Forshee 	acpi_dev->driver_data = dev;
1101135740deSSeth Forshee 
11026e02cc7eSSeth Forshee 	if (toshiba_acpi_setup_keyboard(dev))
1103135740deSSeth Forshee 		pr_info("Unable to activate hotkeys\n");
1104135740deSSeth Forshee 
1105135740deSSeth Forshee 	mutex_init(&dev->mutex);
1106135740deSSeth Forshee 
1107135740deSSeth Forshee 	props.type = BACKLIGHT_PLATFORM;
1108135740deSSeth Forshee 	props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
1109135740deSSeth Forshee 	dev->backlight_dev = backlight_device_register("toshiba",
1110135740deSSeth Forshee 						       &acpi_dev->dev,
1111135740deSSeth Forshee 						       dev,
1112135740deSSeth Forshee 						       &toshiba_backlight_data,
1113135740deSSeth Forshee 						       &props);
1114135740deSSeth Forshee 	if (IS_ERR(dev->backlight_dev)) {
1115135740deSSeth Forshee 		ret = PTR_ERR(dev->backlight_dev);
1116135740deSSeth Forshee 
1117135740deSSeth Forshee 		pr_err("Could not register toshiba backlight device\n");
1118135740deSSeth Forshee 		dev->backlight_dev = NULL;
1119135740deSSeth Forshee 		goto error;
1120135740deSSeth Forshee 	}
1121ac2dad88SSeth Forshee 	dev->backlight_dev->props.brightness = get_lcd(dev->backlight_dev);
1122135740deSSeth Forshee 
1123135740deSSeth Forshee 	/* Register rfkill switch for Bluetooth */
1124135740deSSeth Forshee 	if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) {
1125135740deSSeth Forshee 		dev->bt_rfk = rfkill_alloc("Toshiba Bluetooth",
1126135740deSSeth Forshee 					   &acpi_dev->dev,
1127135740deSSeth Forshee 					   RFKILL_TYPE_BLUETOOTH,
1128135740deSSeth Forshee 					   &toshiba_rfk_ops,
1129135740deSSeth Forshee 					   dev);
1130135740deSSeth Forshee 		if (!dev->bt_rfk) {
1131135740deSSeth Forshee 			pr_err("unable to allocate rfkill device\n");
1132135740deSSeth Forshee 			ret = -ENOMEM;
1133135740deSSeth Forshee 			goto error;
1134135740deSSeth Forshee 		}
1135135740deSSeth Forshee 
1136135740deSSeth Forshee 		ret = rfkill_register(dev->bt_rfk);
1137135740deSSeth Forshee 		if (ret) {
1138135740deSSeth Forshee 			pr_err("unable to register rfkill device\n");
1139135740deSSeth Forshee 			rfkill_destroy(dev->bt_rfk);
1140135740deSSeth Forshee 			goto error;
1141135740deSSeth Forshee 		}
1142135740deSSeth Forshee 	}
1143135740deSSeth Forshee 
1144135740deSSeth Forshee 	if (toshiba_illumination_available(dev)) {
1145135740deSSeth Forshee 		dev->led_dev.name = "toshiba::illumination";
1146135740deSSeth Forshee 		dev->led_dev.max_brightness = 1;
1147135740deSSeth Forshee 		dev->led_dev.brightness_set = toshiba_illumination_set;
1148135740deSSeth Forshee 		dev->led_dev.brightness_get = toshiba_illumination_get;
1149135740deSSeth Forshee 		if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev))
115036d03f93SSeth Forshee 			dev->illumination_supported = 1;
1151135740deSSeth Forshee 	}
1152135740deSSeth Forshee 
115336d03f93SSeth Forshee 	/* Determine whether or not BIOS supports fan and video interfaces */
115436d03f93SSeth Forshee 
115536d03f93SSeth Forshee 	ret = get_video_status(dev, &dummy);
115636d03f93SSeth Forshee 	dev->video_supported = !ret;
115736d03f93SSeth Forshee 
115836d03f93SSeth Forshee 	ret = get_fan_status(dev, &dummy);
115936d03f93SSeth Forshee 	dev->fan_supported = !ret;
116036d03f93SSeth Forshee 
116136d03f93SSeth Forshee 	create_toshiba_proc_entries(dev);
116236d03f93SSeth Forshee 
116329cd293fSSeth Forshee 	toshiba_acpi = dev;
116429cd293fSSeth Forshee 
1165135740deSSeth Forshee 	return 0;
1166135740deSSeth Forshee 
1167135740deSSeth Forshee error:
1168135740deSSeth Forshee 	toshiba_acpi_remove(acpi_dev, 0);
1169135740deSSeth Forshee 	return ret;
1170135740deSSeth Forshee }
1171135740deSSeth Forshee 
1172135740deSSeth Forshee static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
1173135740deSSeth Forshee {
1174135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
11756335e4d5SMatthew Garrett 	u32 hci_result, value;
117611948b93SSeth Forshee 	int retries = 3;
117729cd293fSSeth Forshee 	int scancode;
11786335e4d5SMatthew Garrett 
117929cd293fSSeth Forshee 	if (event != 0x80)
11806335e4d5SMatthew Garrett 		return;
118111948b93SSeth Forshee 
118229cd293fSSeth Forshee 	if (dev->info_supported) {
118329cd293fSSeth Forshee 		scancode = toshiba_acpi_query_hotkey(dev);
118429cd293fSSeth Forshee 		if (scancode < 0)
118529cd293fSSeth Forshee 			pr_err("Failed to query hotkey event\n");
118629cd293fSSeth Forshee 		else if (scancode != 0)
118729cd293fSSeth Forshee 			toshiba_acpi_report_hotkey(dev, scancode);
118829cd293fSSeth Forshee 	} else if (dev->system_event_supported) {
11896335e4d5SMatthew Garrett 		do {
1190135740deSSeth Forshee 			hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
119111948b93SSeth Forshee 			switch (hci_result) {
119211948b93SSeth Forshee 			case HCI_SUCCESS:
119329cd293fSSeth Forshee 				toshiba_acpi_report_hotkey(dev, (int)value);
119411948b93SSeth Forshee 				break;
119511948b93SSeth Forshee 			case HCI_NOT_SUPPORTED:
119629cd293fSSeth Forshee 				/*
119729cd293fSSeth Forshee 				 * This is a workaround for an unresolved
119829cd293fSSeth Forshee 				 * issue on some machines where system events
119929cd293fSSeth Forshee 				 * sporadically become disabled.
120029cd293fSSeth Forshee 				 */
120129cd293fSSeth Forshee 				hci_write1(dev, HCI_SYSTEM_EVENT, 1,
120229cd293fSSeth Forshee 					   &hci_result);
12037e33460dSJoe Perches 				pr_notice("Re-enabled hotkeys\n");
120411948b93SSeth Forshee 				/* fall through */
120511948b93SSeth Forshee 			default:
120611948b93SSeth Forshee 				retries--;
120711948b93SSeth Forshee 				break;
12086335e4d5SMatthew Garrett 			}
120911948b93SSeth Forshee 		} while (retries && hci_result != HCI_EMPTY);
12106335e4d5SMatthew Garrett 	}
121129cd293fSSeth Forshee }
12126335e4d5SMatthew Garrett 
121329cd293fSSeth Forshee static int toshiba_acpi_suspend(struct acpi_device *acpi_dev,
121429cd293fSSeth Forshee 				pm_message_t state)
121529cd293fSSeth Forshee {
121629cd293fSSeth Forshee 	struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
121729cd293fSSeth Forshee 	u32 result;
121829cd293fSSeth Forshee 
121929cd293fSSeth Forshee 	if (dev->hotkey_dev)
122029cd293fSSeth Forshee 		hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE, &result);
122129cd293fSSeth Forshee 
122229cd293fSSeth Forshee 	return 0;
122329cd293fSSeth Forshee }
122429cd293fSSeth Forshee 
122529cd293fSSeth Forshee static int toshiba_acpi_resume(struct acpi_device *acpi_dev)
122629cd293fSSeth Forshee {
122729cd293fSSeth Forshee 	struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
122829cd293fSSeth Forshee 	u32 result;
122929cd293fSSeth Forshee 
123029cd293fSSeth Forshee 	if (dev->hotkey_dev)
123129cd293fSSeth Forshee 		hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &result);
123229cd293fSSeth Forshee 
123329cd293fSSeth Forshee 	return 0;
123429cd293fSSeth Forshee }
12356335e4d5SMatthew Garrett 
1236135740deSSeth Forshee static struct acpi_driver toshiba_acpi_driver = {
1237135740deSSeth Forshee 	.name	= "Toshiba ACPI driver",
1238135740deSSeth Forshee 	.owner	= THIS_MODULE,
1239135740deSSeth Forshee 	.ids	= toshiba_device_ids,
1240135740deSSeth Forshee 	.flags	= ACPI_DRIVER_ALL_NOTIFY_EVENTS,
1241135740deSSeth Forshee 	.ops	= {
1242135740deSSeth Forshee 		.add		= toshiba_acpi_add,
1243135740deSSeth Forshee 		.remove		= toshiba_acpi_remove,
1244135740deSSeth Forshee 		.notify		= toshiba_acpi_notify,
124529cd293fSSeth Forshee 		.suspend	= toshiba_acpi_suspend,
124629cd293fSSeth Forshee 		.resume		= toshiba_acpi_resume,
1247135740deSSeth Forshee 	},
1248135740deSSeth Forshee };
1249b4f9fe12SLen Brown 
1250b4f9fe12SLen Brown static int __init toshiba_acpi_init(void)
1251b4f9fe12SLen Brown {
1252135740deSSeth Forshee 	int ret;
1253b4f9fe12SLen Brown 
1254*f11f999eSSeth Forshee 	/*
1255*f11f999eSSeth Forshee 	 * Machines with this WMI guid aren't supported due to bugs in
1256*f11f999eSSeth Forshee 	 * their AML. This check relies on wmi initializing before
1257*f11f999eSSeth Forshee 	 * toshiba_acpi to guarantee guids have been identified.
1258*f11f999eSSeth Forshee 	 */
1259*f11f999eSSeth Forshee 	if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
1260*f11f999eSSeth Forshee 		return -ENODEV;
1261*f11f999eSSeth Forshee 
1262b4f9fe12SLen Brown 	toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
1263b4f9fe12SLen Brown 	if (!toshiba_proc_dir) {
1264135740deSSeth Forshee 		pr_err("Unable to create proc dir " PROC_TOSHIBA "\n");
1265b4f9fe12SLen Brown 		return -ENODEV;
1266b4f9fe12SLen Brown 	}
1267b4f9fe12SLen Brown 
1268135740deSSeth Forshee 	ret = acpi_bus_register_driver(&toshiba_acpi_driver);
1269b4f9fe12SLen Brown 	if (ret) {
1270135740deSSeth Forshee 		pr_err("Failed to register ACPI driver: %d\n", ret);
1271135740deSSeth Forshee 		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
1272135740deSSeth Forshee 	}
1273135740deSSeth Forshee 
1274b4f9fe12SLen Brown 	return ret;
1275b4f9fe12SLen Brown }
1276b4f9fe12SLen Brown 
1277135740deSSeth Forshee static void __exit toshiba_acpi_exit(void)
1278135740deSSeth Forshee {
1279135740deSSeth Forshee 	acpi_bus_unregister_driver(&toshiba_acpi_driver);
1280135740deSSeth Forshee 	if (toshiba_proc_dir)
1281135740deSSeth Forshee 		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
1282b4f9fe12SLen Brown }
1283b4f9fe12SLen Brown 
1284b4f9fe12SLen Brown module_init(toshiba_acpi_init);
1285b4f9fe12SLen Brown module_exit(toshiba_acpi_exit);
1286