xref: /linux/drivers/platform/x86/toshiba_acpi.c (revision 84a6273f04fd19cad189c8327d0c3c17a053ab8b)
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>
578b48463fSLv Zheng #include <linux/acpi.h>
58b4f9fe12SLen Brown #include <asm/uaccess.h>
59b4f9fe12SLen Brown 
60b4f9fe12SLen Brown MODULE_AUTHOR("John Belmonte");
61b4f9fe12SLen Brown MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
62b4f9fe12SLen Brown MODULE_LICENSE("GPL");
63b4f9fe12SLen Brown 
64f11f999eSSeth Forshee #define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100"
65f11f999eSSeth Forshee 
6629cd293fSSeth Forshee /* Scan code for Fn key on TOS1900 models */
6729cd293fSSeth Forshee #define TOS1900_FN_SCAN		0x6e
6829cd293fSSeth Forshee 
69b4f9fe12SLen Brown /* Toshiba ACPI method paths */
70b4f9fe12SLen Brown #define METHOD_VIDEO_OUT	"\\_SB_.VALX.DSSX"
71b4f9fe12SLen Brown 
72b4f9fe12SLen Brown /* Toshiba HCI interface definitions
73b4f9fe12SLen Brown  *
74b4f9fe12SLen Brown  * HCI is Toshiba's "Hardware Control Interface" which is supposed to
75b4f9fe12SLen Brown  * be uniform across all their models.  Ideally we would just call
76b4f9fe12SLen Brown  * dedicated ACPI methods instead of using this primitive interface.
77b4f9fe12SLen Brown  * However the ACPI methods seem to be incomplete in some areas (for
78b4f9fe12SLen Brown  * example they allow setting, but not reading, the LCD brightness value),
79b4f9fe12SLen Brown  * so this is still useful.
80*84a6273fSAzael Avalos  *
81*84a6273fSAzael Avalos  * SCI stands for "System Configuration Interface" which aim is to
82*84a6273fSAzael Avalos  * conceal differences in hardware between different models.
83b4f9fe12SLen Brown  */
84b4f9fe12SLen Brown 
85b4f9fe12SLen Brown #define HCI_WORDS			6
86b4f9fe12SLen Brown 
87b4f9fe12SLen Brown /* operations */
88b4f9fe12SLen Brown #define HCI_SET				0xff00
89b4f9fe12SLen Brown #define HCI_GET				0xfe00
90*84a6273fSAzael Avalos #define SCI_OPEN			0xf100
91*84a6273fSAzael Avalos #define SCI_CLOSE			0xf200
92*84a6273fSAzael Avalos #define SCI_GET				0xf300
93*84a6273fSAzael Avalos #define SCI_SET				0xf400
94b4f9fe12SLen Brown 
95b4f9fe12SLen Brown /* return codes */
96b4f9fe12SLen Brown #define HCI_SUCCESS			0x0000
97b4f9fe12SLen Brown #define HCI_FAILURE			0x1000
98b4f9fe12SLen Brown #define HCI_NOT_SUPPORTED		0x8000
99b4f9fe12SLen Brown #define HCI_EMPTY			0x8c00
100*84a6273fSAzael Avalos #define SCI_OPEN_CLOSE_OK		0x0044
101*84a6273fSAzael Avalos #define SCI_ALREADY_OPEN		0x8100
102*84a6273fSAzael Avalos #define SCI_NOT_OPENED			0x8200
103*84a6273fSAzael Avalos #define SCI_NOT_PRESENT			0x8600
104b4f9fe12SLen Brown 
105b4f9fe12SLen Brown /* registers */
106b4f9fe12SLen Brown #define HCI_FAN				0x0004
107121b7b0dSAkio Idehara #define HCI_TR_BACKLIGHT		0x0005
108b4f9fe12SLen Brown #define HCI_SYSTEM_EVENT		0x0016
109b4f9fe12SLen Brown #define HCI_VIDEO_OUT			0x001c
110b4f9fe12SLen Brown #define HCI_HOTKEY_EVENT		0x001e
111b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS		0x002a
112b4f9fe12SLen Brown #define HCI_WIRELESS			0x0056
113b4f9fe12SLen Brown 
114b4f9fe12SLen Brown /* field definitions */
11529cd293fSSeth Forshee #define HCI_HOTKEY_DISABLE		0x0b
11629cd293fSSeth Forshee #define HCI_HOTKEY_ENABLE		0x09
117b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_BITS		3
118b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_SHIFT	(16-HCI_LCD_BRIGHTNESS_BITS)
119b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_LEVELS	(1 << HCI_LCD_BRIGHTNESS_BITS)
120b4f9fe12SLen Brown #define HCI_VIDEO_OUT_LCD		0x1
121b4f9fe12SLen Brown #define HCI_VIDEO_OUT_CRT		0x2
122b4f9fe12SLen Brown #define HCI_VIDEO_OUT_TV		0x4
123b4f9fe12SLen Brown #define HCI_WIRELESS_KILL_SWITCH	0x01
124b4f9fe12SLen Brown #define HCI_WIRELESS_BT_PRESENT		0x0f
125b4f9fe12SLen Brown #define HCI_WIRELESS_BT_ATTACH		0x40
126b4f9fe12SLen Brown #define HCI_WIRELESS_BT_POWER		0x80
127b4f9fe12SLen Brown 
128135740deSSeth Forshee struct toshiba_acpi_dev {
129135740deSSeth Forshee 	struct acpi_device *acpi_dev;
130135740deSSeth Forshee 	const char *method_hci;
131135740deSSeth Forshee 	struct rfkill *bt_rfk;
132135740deSSeth Forshee 	struct input_dev *hotkey_dev;
13329cd293fSSeth Forshee 	struct work_struct hotkey_work;
134135740deSSeth Forshee 	struct backlight_device *backlight_dev;
135135740deSSeth Forshee 	struct led_classdev led_dev;
13636d03f93SSeth Forshee 
137135740deSSeth Forshee 	int force_fan;
138135740deSSeth Forshee 	int last_key_event;
139135740deSSeth Forshee 	int key_event_valid;
140135740deSSeth Forshee 
141592b746cSDan Carpenter 	unsigned int illumination_supported:1;
142592b746cSDan Carpenter 	unsigned int video_supported:1;
143592b746cSDan Carpenter 	unsigned int fan_supported:1;
144592b746cSDan Carpenter 	unsigned int system_event_supported:1;
14529cd293fSSeth Forshee 	unsigned int ntfy_supported:1;
14629cd293fSSeth Forshee 	unsigned int info_supported:1;
147121b7b0dSAkio Idehara 	unsigned int tr_backlight_supported:1;
14836d03f93SSeth Forshee 
149135740deSSeth Forshee 	struct mutex mutex;
150135740deSSeth Forshee };
151135740deSSeth Forshee 
15229cd293fSSeth Forshee static struct toshiba_acpi_dev *toshiba_acpi;
15329cd293fSSeth Forshee 
154b4f9fe12SLen Brown static const struct acpi_device_id toshiba_device_ids[] = {
155b4f9fe12SLen Brown 	{"TOS6200", 0},
156b4f9fe12SLen Brown 	{"TOS6208", 0},
157b4f9fe12SLen Brown 	{"TOS1900", 0},
158b4f9fe12SLen Brown 	{"", 0},
159b4f9fe12SLen Brown };
160b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
161b4f9fe12SLen Brown 
162b859f159SGreg Kroah-Hartman static const struct key_entry toshiba_acpi_keymap[] = {
163fec278a1SUnai Uribarri 	{ KE_KEY, 0x9e, { KEY_RFKILL } },
164384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x101, { KEY_MUTE } },
165384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x102, { KEY_ZOOMOUT } },
166384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x103, { KEY_ZOOMIN } },
167af502837SAzael Avalos 	{ KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } },
168af502837SAzael Avalos 	{ KE_KEY, 0x139, { KEY_ZOOMRESET } },
169384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13b, { KEY_COFFEE } },
170384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13c, { KEY_BATTERY } },
171384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13d, { KEY_SLEEP } },
172384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13e, { KEY_SUSPEND } },
173384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } },
174384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
175384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
176384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x142, { KEY_WLAN } },
177af502837SAzael Avalos 	{ KE_KEY, 0x143, { KEY_TOUCHPAD_TOGGLE } },
178a49010f5SJon Dowland 	{ KE_KEY, 0x17f, { KEY_FN } },
179384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb05, { KEY_PROG2 } },
180384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb06, { KEY_WWW } },
181384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb07, { KEY_MAIL } },
182384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb30, { KEY_STOP } },
183384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb31, { KEY_PREVIOUSSONG } },
184384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb32, { KEY_NEXTSONG } },
185384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
186384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb5a, { KEY_MEDIA } },
187af502837SAzael Avalos 	{ KE_IGNORE, 0x1430, { KEY_RESERVED } },
188384a7cd9SDmitry Torokhov 	{ KE_END, 0 },
1896335e4d5SMatthew Garrett };
1906335e4d5SMatthew Garrett 
191b4f9fe12SLen Brown /* utility
192b4f9fe12SLen Brown  */
193b4f9fe12SLen Brown 
194b4f9fe12SLen Brown static __inline__ void _set_bit(u32 * word, u32 mask, int value)
195b4f9fe12SLen Brown {
196b4f9fe12SLen Brown 	*word = (*word & ~mask) | (mask * value);
197b4f9fe12SLen Brown }
198b4f9fe12SLen Brown 
199b4f9fe12SLen Brown /* acpi interface wrappers
200b4f9fe12SLen Brown  */
201b4f9fe12SLen Brown 
202b4f9fe12SLen Brown static int write_acpi_int(const char *methodName, int val)
203b4f9fe12SLen Brown {
204b4f9fe12SLen Brown 	acpi_status status;
205b4f9fe12SLen Brown 
206619400daSZhang Rui 	status = acpi_execute_simple_method(NULL, (char *)methodName, val);
20732bcd5cbSSeth Forshee 	return (status == AE_OK) ? 0 : -EIO;
208b4f9fe12SLen Brown }
209b4f9fe12SLen Brown 
210b4f9fe12SLen Brown /* Perform a raw HCI call.  Here we don't care about input or output buffer
211b4f9fe12SLen Brown  * format.
212b4f9fe12SLen Brown  */
213135740deSSeth Forshee static acpi_status hci_raw(struct toshiba_acpi_dev *dev,
214135740deSSeth Forshee 			   const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
215b4f9fe12SLen Brown {
216b4f9fe12SLen Brown 	struct acpi_object_list params;
217b4f9fe12SLen Brown 	union acpi_object in_objs[HCI_WORDS];
218b4f9fe12SLen Brown 	struct acpi_buffer results;
219b4f9fe12SLen Brown 	union acpi_object out_objs[HCI_WORDS + 1];
220b4f9fe12SLen Brown 	acpi_status status;
221b4f9fe12SLen Brown 	int i;
222b4f9fe12SLen Brown 
223b4f9fe12SLen Brown 	params.count = HCI_WORDS;
224b4f9fe12SLen Brown 	params.pointer = in_objs;
225b4f9fe12SLen Brown 	for (i = 0; i < HCI_WORDS; ++i) {
226b4f9fe12SLen Brown 		in_objs[i].type = ACPI_TYPE_INTEGER;
227b4f9fe12SLen Brown 		in_objs[i].integer.value = in[i];
228b4f9fe12SLen Brown 	}
229b4f9fe12SLen Brown 
230b4f9fe12SLen Brown 	results.length = sizeof(out_objs);
231b4f9fe12SLen Brown 	results.pointer = out_objs;
232b4f9fe12SLen Brown 
2336e02cc7eSSeth Forshee 	status = acpi_evaluate_object(dev->acpi_dev->handle,
2346e02cc7eSSeth Forshee 				      (char *)dev->method_hci, &params,
235b4f9fe12SLen Brown 				      &results);
236b4f9fe12SLen Brown 	if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) {
237b4f9fe12SLen Brown 		for (i = 0; i < out_objs->package.count; ++i) {
238b4f9fe12SLen Brown 			out[i] = out_objs->package.elements[i].integer.value;
239b4f9fe12SLen Brown 		}
240b4f9fe12SLen Brown 	}
241b4f9fe12SLen Brown 
242b4f9fe12SLen Brown 	return status;
243b4f9fe12SLen Brown }
244b4f9fe12SLen Brown 
245b4f9fe12SLen Brown /* common hci tasks (get or set one or two value)
246b4f9fe12SLen Brown  *
247b4f9fe12SLen Brown  * In addition to the ACPI status, the HCI system returns a result which
248b4f9fe12SLen Brown  * may be useful (such as "not supported").
249b4f9fe12SLen Brown  */
250b4f9fe12SLen Brown 
251135740deSSeth Forshee static acpi_status hci_write1(struct toshiba_acpi_dev *dev, u32 reg,
252135740deSSeth Forshee 			      u32 in1, u32 *result)
253b4f9fe12SLen Brown {
254b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 };
255b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
256135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
257b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
258b4f9fe12SLen Brown 	return status;
259b4f9fe12SLen Brown }
260b4f9fe12SLen Brown 
261135740deSSeth Forshee static acpi_status hci_read1(struct toshiba_acpi_dev *dev, u32 reg,
262135740deSSeth Forshee 			     u32 *out1, u32 *result)
263b4f9fe12SLen Brown {
264b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 };
265b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
266135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
267b4f9fe12SLen Brown 	*out1 = out[2];
268b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
269b4f9fe12SLen Brown 	return status;
270b4f9fe12SLen Brown }
271b4f9fe12SLen Brown 
272135740deSSeth Forshee static acpi_status hci_write2(struct toshiba_acpi_dev *dev, u32 reg,
273135740deSSeth Forshee 			      u32 in1, u32 in2, u32 *result)
274b4f9fe12SLen Brown {
275b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 };
276b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
277135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
278b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
279b4f9fe12SLen Brown 	return status;
280b4f9fe12SLen Brown }
281b4f9fe12SLen Brown 
282135740deSSeth Forshee static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg,
283135740deSSeth Forshee 			     u32 *out1, u32 *out2, u32 *result)
284b4f9fe12SLen Brown {
285b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 };
286b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
287135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
288b4f9fe12SLen Brown 	*out1 = out[2];
289b4f9fe12SLen Brown 	*out2 = out[3];
290b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
291b4f9fe12SLen Brown 	return status;
292b4f9fe12SLen Brown }
293b4f9fe12SLen Brown 
294*84a6273fSAzael Avalos /* common sci tasks
295*84a6273fSAzael Avalos  */
296*84a6273fSAzael Avalos 
297*84a6273fSAzael Avalos static int sci_open(struct toshiba_acpi_dev *dev)
298*84a6273fSAzael Avalos {
299*84a6273fSAzael Avalos 	u32 in[HCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 };
300*84a6273fSAzael Avalos 	u32 out[HCI_WORDS];
301*84a6273fSAzael Avalos 	acpi_status status;
302*84a6273fSAzael Avalos 
303*84a6273fSAzael Avalos 	status = hci_raw(dev, in, out);
304*84a6273fSAzael Avalos 	if  (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
305*84a6273fSAzael Avalos 		pr_err("ACPI call to open SCI failed\n");
306*84a6273fSAzael Avalos 		return 0;
307*84a6273fSAzael Avalos 	}
308*84a6273fSAzael Avalos 
309*84a6273fSAzael Avalos 	if (out[0] == SCI_OPEN_CLOSE_OK) {
310*84a6273fSAzael Avalos 		return 1;
311*84a6273fSAzael Avalos 	} else if (out[0] == SCI_ALREADY_OPEN) {
312*84a6273fSAzael Avalos 		pr_info("Toshiba SCI already opened\n");
313*84a6273fSAzael Avalos 		return 1;
314*84a6273fSAzael Avalos 	} else if (out[0] == SCI_NOT_PRESENT) {
315*84a6273fSAzael Avalos 		pr_info("Toshiba SCI is not present\n");
316*84a6273fSAzael Avalos 	}
317*84a6273fSAzael Avalos 
318*84a6273fSAzael Avalos 	return 0;
319*84a6273fSAzael Avalos }
320*84a6273fSAzael Avalos 
321*84a6273fSAzael Avalos static void sci_close(struct toshiba_acpi_dev *dev)
322*84a6273fSAzael Avalos {
323*84a6273fSAzael Avalos 	u32 in[HCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 };
324*84a6273fSAzael Avalos 	u32 out[HCI_WORDS];
325*84a6273fSAzael Avalos 	acpi_status status;
326*84a6273fSAzael Avalos 
327*84a6273fSAzael Avalos 	status = hci_raw(dev, in, out);
328*84a6273fSAzael Avalos 	if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
329*84a6273fSAzael Avalos 		pr_err("ACPI call to close SCI failed\n");
330*84a6273fSAzael Avalos 		return;
331*84a6273fSAzael Avalos 	}
332*84a6273fSAzael Avalos 
333*84a6273fSAzael Avalos 	if (out[0] == SCI_OPEN_CLOSE_OK)
334*84a6273fSAzael Avalos 		return;
335*84a6273fSAzael Avalos 	else if (out[0] == SCI_NOT_OPENED)
336*84a6273fSAzael Avalos 		pr_info("Toshiba SCI not opened\n");
337*84a6273fSAzael Avalos 	else if (out[0] == SCI_NOT_PRESENT)
338*84a6273fSAzael Avalos 		pr_info("Toshiba SCI is not present\n");
339*84a6273fSAzael Avalos }
340*84a6273fSAzael Avalos 
341*84a6273fSAzael Avalos static acpi_status sci_read(struct toshiba_acpi_dev *dev, u32 reg,
342*84a6273fSAzael Avalos 			    u32 *out1, u32 *result)
343*84a6273fSAzael Avalos {
344*84a6273fSAzael Avalos 	u32 in[HCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 };
345*84a6273fSAzael Avalos 	u32 out[HCI_WORDS];
346*84a6273fSAzael Avalos 	acpi_status status = hci_raw(dev, in, out);
347*84a6273fSAzael Avalos 	*out1 = out[2];
348*84a6273fSAzael Avalos 	*result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE;
349*84a6273fSAzael Avalos 	return status;
350*84a6273fSAzael Avalos }
351*84a6273fSAzael Avalos 
352*84a6273fSAzael Avalos static acpi_status sci_write(struct toshiba_acpi_dev *dev, u32 reg,
353*84a6273fSAzael Avalos 			     u32 in1, u32 *result)
354*84a6273fSAzael Avalos {
355*84a6273fSAzael Avalos 	u32 in[HCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 };
356*84a6273fSAzael Avalos 	u32 out[HCI_WORDS];
357*84a6273fSAzael Avalos 	acpi_status status = hci_raw(dev, in, out);
358*84a6273fSAzael Avalos 	*result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE;
359*84a6273fSAzael Avalos 	return status;
360*84a6273fSAzael Avalos }
361*84a6273fSAzael Avalos 
3626c3f6e6cSPierre Ducroquet /* Illumination support */
363135740deSSeth Forshee static int toshiba_illumination_available(struct toshiba_acpi_dev *dev)
3646c3f6e6cSPierre Ducroquet {
3656c3f6e6cSPierre Ducroquet 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
3666c3f6e6cSPierre Ducroquet 	u32 out[HCI_WORDS];
3676c3f6e6cSPierre Ducroquet 	acpi_status status;
3686c3f6e6cSPierre Ducroquet 
3696c3f6e6cSPierre Ducroquet 	in[0] = 0xf100;
370135740deSSeth Forshee 	status = hci_raw(dev, in, out);
3716c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
3727e33460dSJoe Perches 		pr_info("Illumination device not available\n");
3736c3f6e6cSPierre Ducroquet 		return 0;
3746c3f6e6cSPierre Ducroquet 	}
3756c3f6e6cSPierre Ducroquet 	in[0] = 0xf400;
376135740deSSeth Forshee 	status = hci_raw(dev, in, out);
3776c3f6e6cSPierre Ducroquet 	return 1;
3786c3f6e6cSPierre Ducroquet }
3796c3f6e6cSPierre Ducroquet 
3806c3f6e6cSPierre Ducroquet static void toshiba_illumination_set(struct led_classdev *cdev,
3816c3f6e6cSPierre Ducroquet 				     enum led_brightness brightness)
3826c3f6e6cSPierre Ducroquet {
383135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = container_of(cdev,
384135740deSSeth Forshee 			struct toshiba_acpi_dev, led_dev);
3856c3f6e6cSPierre Ducroquet 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
3866c3f6e6cSPierre Ducroquet 	u32 out[HCI_WORDS];
3876c3f6e6cSPierre Ducroquet 	acpi_status status;
3886c3f6e6cSPierre Ducroquet 
3896c3f6e6cSPierre Ducroquet 	/* First request : initialize communication. */
3906c3f6e6cSPierre Ducroquet 	in[0] = 0xf100;
391135740deSSeth Forshee 	status = hci_raw(dev, in, out);
3926c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
3937e33460dSJoe Perches 		pr_info("Illumination device not available\n");
3946c3f6e6cSPierre Ducroquet 		return;
3956c3f6e6cSPierre Ducroquet 	}
3966c3f6e6cSPierre Ducroquet 
3976c3f6e6cSPierre Ducroquet 	if (brightness) {
3986c3f6e6cSPierre Ducroquet 		/* Switch the illumination on */
3996c3f6e6cSPierre Ducroquet 		in[0] = 0xf400;
4006c3f6e6cSPierre Ducroquet 		in[1] = 0x14e;
4016c3f6e6cSPierre Ducroquet 		in[2] = 1;
402135740deSSeth Forshee 		status = hci_raw(dev, in, out);
4036c3f6e6cSPierre Ducroquet 		if (ACPI_FAILURE(status)) {
4047e33460dSJoe Perches 			pr_info("ACPI call for illumination failed\n");
4056c3f6e6cSPierre Ducroquet 			return;
4066c3f6e6cSPierre Ducroquet 		}
4076c3f6e6cSPierre Ducroquet 	} else {
4086c3f6e6cSPierre Ducroquet 		/* Switch the illumination off */
4096c3f6e6cSPierre Ducroquet 		in[0] = 0xf400;
4106c3f6e6cSPierre Ducroquet 		in[1] = 0x14e;
4116c3f6e6cSPierre Ducroquet 		in[2] = 0;
412135740deSSeth Forshee 		status = hci_raw(dev, in, out);
4136c3f6e6cSPierre Ducroquet 		if (ACPI_FAILURE(status)) {
4147e33460dSJoe Perches 			pr_info("ACPI call for illumination failed.\n");
4156c3f6e6cSPierre Ducroquet 			return;
4166c3f6e6cSPierre Ducroquet 		}
4176c3f6e6cSPierre Ducroquet 	}
4186c3f6e6cSPierre Ducroquet 
4196c3f6e6cSPierre Ducroquet 	/* Last request : close communication. */
4206c3f6e6cSPierre Ducroquet 	in[0] = 0xf200;
4216c3f6e6cSPierre Ducroquet 	in[1] = 0;
4226c3f6e6cSPierre Ducroquet 	in[2] = 0;
423135740deSSeth Forshee 	hci_raw(dev, in, out);
4246c3f6e6cSPierre Ducroquet }
4256c3f6e6cSPierre Ducroquet 
4266c3f6e6cSPierre Ducroquet static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
4276c3f6e6cSPierre Ducroquet {
428135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = container_of(cdev,
429135740deSSeth Forshee 			struct toshiba_acpi_dev, led_dev);
4306c3f6e6cSPierre Ducroquet 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
4316c3f6e6cSPierre Ducroquet 	u32 out[HCI_WORDS];
4326c3f6e6cSPierre Ducroquet 	acpi_status status;
4336c3f6e6cSPierre Ducroquet 	enum led_brightness result;
4346c3f6e6cSPierre Ducroquet 
4356c3f6e6cSPierre Ducroquet 	/* First request : initialize communication. */
4366c3f6e6cSPierre Ducroquet 	in[0] = 0xf100;
437135740deSSeth Forshee 	status = hci_raw(dev, in, out);
4386c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
4397e33460dSJoe Perches 		pr_info("Illumination device not available\n");
4406c3f6e6cSPierre Ducroquet 		return LED_OFF;
4416c3f6e6cSPierre Ducroquet 	}
4426c3f6e6cSPierre Ducroquet 
4436c3f6e6cSPierre Ducroquet 	/* Check the illumination */
4446c3f6e6cSPierre Ducroquet 	in[0] = 0xf300;
4456c3f6e6cSPierre Ducroquet 	in[1] = 0x14e;
446135740deSSeth Forshee 	status = hci_raw(dev, in, out);
4476c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
4487e33460dSJoe Perches 		pr_info("ACPI call for illumination failed.\n");
4496c3f6e6cSPierre Ducroquet 		return LED_OFF;
4506c3f6e6cSPierre Ducroquet 	}
4516c3f6e6cSPierre Ducroquet 
4526c3f6e6cSPierre Ducroquet 	result = out[2] ? LED_FULL : LED_OFF;
4536c3f6e6cSPierre Ducroquet 
4546c3f6e6cSPierre Ducroquet 	/* Last request : close communication. */
4556c3f6e6cSPierre Ducroquet 	in[0] = 0xf200;
4566c3f6e6cSPierre Ducroquet 	in[1] = 0;
4576c3f6e6cSPierre Ducroquet 	in[2] = 0;
458135740deSSeth Forshee 	hci_raw(dev, in, out);
4596c3f6e6cSPierre Ducroquet 
4606c3f6e6cSPierre Ducroquet 	return result;
4616c3f6e6cSPierre Ducroquet }
4626c3f6e6cSPierre Ducroquet 
463b4f9fe12SLen Brown /* Bluetooth rfkill handlers */
464b4f9fe12SLen Brown 
465135740deSSeth Forshee static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present)
466b4f9fe12SLen Brown {
467b4f9fe12SLen Brown 	u32 hci_result;
468b4f9fe12SLen Brown 	u32 value, value2;
469b4f9fe12SLen Brown 
470b4f9fe12SLen Brown 	value = 0;
471b4f9fe12SLen Brown 	value2 = 0;
472135740deSSeth Forshee 	hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result);
473b4f9fe12SLen Brown 	if (hci_result == HCI_SUCCESS)
474b4f9fe12SLen Brown 		*present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false;
475b4f9fe12SLen Brown 
476b4f9fe12SLen Brown 	return hci_result;
477b4f9fe12SLen Brown }
478b4f9fe12SLen Brown 
479135740deSSeth Forshee static u32 hci_get_radio_state(struct toshiba_acpi_dev *dev, bool *radio_state)
480b4f9fe12SLen Brown {
481b4f9fe12SLen Brown 	u32 hci_result;
482b4f9fe12SLen Brown 	u32 value, value2;
483b4f9fe12SLen Brown 
484b4f9fe12SLen Brown 	value = 0;
485b4f9fe12SLen Brown 	value2 = 0x0001;
486135740deSSeth Forshee 	hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result);
487b4f9fe12SLen Brown 
488b4f9fe12SLen Brown 	*radio_state = value & HCI_WIRELESS_KILL_SWITCH;
489b4f9fe12SLen Brown 	return hci_result;
490b4f9fe12SLen Brown }
491b4f9fe12SLen Brown 
49219d337dfSJohannes Berg static int bt_rfkill_set_block(void *data, bool blocked)
493b4f9fe12SLen Brown {
49419d337dfSJohannes Berg 	struct toshiba_acpi_dev *dev = data;
495b4f9fe12SLen Brown 	u32 result1, result2;
496b4f9fe12SLen Brown 	u32 value;
49719d337dfSJohannes Berg 	int err;
498b4f9fe12SLen Brown 	bool radio_state;
499b4f9fe12SLen Brown 
50019d337dfSJohannes Berg 	value = (blocked == false);
501b4f9fe12SLen Brown 
502b4f9fe12SLen Brown 	mutex_lock(&dev->mutex);
503135740deSSeth Forshee 	if (hci_get_radio_state(dev, &radio_state) != HCI_SUCCESS) {
50432bcd5cbSSeth Forshee 		err = -EIO;
50519d337dfSJohannes Berg 		goto out;
506b4f9fe12SLen Brown 	}
507b4f9fe12SLen Brown 
50819d337dfSJohannes Berg 	if (!radio_state) {
50919d337dfSJohannes Berg 		err = 0;
51019d337dfSJohannes Berg 		goto out;
51119d337dfSJohannes Berg 	}
51219d337dfSJohannes Berg 
513135740deSSeth Forshee 	hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
514135740deSSeth Forshee 	hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2);
51519d337dfSJohannes Berg 
51619d337dfSJohannes Berg 	if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
51732bcd5cbSSeth Forshee 		err = -EIO;
51819d337dfSJohannes Berg 	else
51919d337dfSJohannes Berg 		err = 0;
52019d337dfSJohannes Berg  out:
52119d337dfSJohannes Berg 	mutex_unlock(&dev->mutex);
52219d337dfSJohannes Berg 	return err;
52319d337dfSJohannes Berg }
52419d337dfSJohannes Berg 
52519d337dfSJohannes Berg static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
526b4f9fe12SLen Brown {
527b4f9fe12SLen Brown 	bool new_rfk_state;
528b4f9fe12SLen Brown 	bool value;
529b4f9fe12SLen Brown 	u32 hci_result;
53019d337dfSJohannes Berg 	struct toshiba_acpi_dev *dev = data;
53119d337dfSJohannes Berg 
53219d337dfSJohannes Berg 	mutex_lock(&dev->mutex);
533b4f9fe12SLen Brown 
534135740deSSeth Forshee 	hci_result = hci_get_radio_state(dev, &value);
53519d337dfSJohannes Berg 	if (hci_result != HCI_SUCCESS) {
53619d337dfSJohannes Berg 		/* Can't do anything useful */
53719d337dfSJohannes Berg 		mutex_unlock(&dev->mutex);
53882e7784fSJiri Slaby 		return;
53919d337dfSJohannes Berg 	}
540b4f9fe12SLen Brown 
541b4f9fe12SLen Brown 	new_rfk_state = value;
542b4f9fe12SLen Brown 
543b4f9fe12SLen Brown 	mutex_unlock(&dev->mutex);
544b4f9fe12SLen Brown 
54519d337dfSJohannes Berg 	if (rfkill_set_hw_state(rfkill, !new_rfk_state))
54619d337dfSJohannes Berg 		bt_rfkill_set_block(data, true);
547b4f9fe12SLen Brown }
54819d337dfSJohannes Berg 
54919d337dfSJohannes Berg static const struct rfkill_ops toshiba_rfk_ops = {
55019d337dfSJohannes Berg 	.set_block = bt_rfkill_set_block,
55119d337dfSJohannes Berg 	.poll = bt_rfkill_poll,
55219d337dfSJohannes Berg };
553b4f9fe12SLen Brown 
554121b7b0dSAkio Idehara static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, bool *enabled)
555121b7b0dSAkio Idehara {
556121b7b0dSAkio Idehara 	u32 hci_result;
557121b7b0dSAkio Idehara 	u32 status;
558121b7b0dSAkio Idehara 
559121b7b0dSAkio Idehara 	hci_read1(dev, HCI_TR_BACKLIGHT, &status, &hci_result);
560121b7b0dSAkio Idehara 	*enabled = !status;
561121b7b0dSAkio Idehara 	return hci_result == HCI_SUCCESS ? 0 : -EIO;
562121b7b0dSAkio Idehara }
563121b7b0dSAkio Idehara 
564121b7b0dSAkio Idehara static int set_tr_backlight_status(struct toshiba_acpi_dev *dev, bool enable)
565121b7b0dSAkio Idehara {
566121b7b0dSAkio Idehara 	u32 hci_result;
567121b7b0dSAkio Idehara 	u32 value = !enable;
568121b7b0dSAkio Idehara 
569121b7b0dSAkio Idehara 	hci_write1(dev, HCI_TR_BACKLIGHT, value, &hci_result);
570121b7b0dSAkio Idehara 	return hci_result == HCI_SUCCESS ? 0 : -EIO;
571121b7b0dSAkio Idehara }
572121b7b0dSAkio Idehara 
573b4f9fe12SLen Brown static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
574b4f9fe12SLen Brown 
57562cce752SSeth Forshee static int __get_lcd_brightness(struct toshiba_acpi_dev *dev)
576b4f9fe12SLen Brown {
577b4f9fe12SLen Brown 	u32 hci_result;
578b4f9fe12SLen Brown 	u32 value;
579121b7b0dSAkio Idehara 	int brightness = 0;
580121b7b0dSAkio Idehara 
581121b7b0dSAkio Idehara 	if (dev->tr_backlight_supported) {
582121b7b0dSAkio Idehara 		bool enabled;
583121b7b0dSAkio Idehara 		int ret = get_tr_backlight_status(dev, &enabled);
584121b7b0dSAkio Idehara 		if (ret)
585121b7b0dSAkio Idehara 			return ret;
586121b7b0dSAkio Idehara 		if (enabled)
587121b7b0dSAkio Idehara 			return 0;
588121b7b0dSAkio Idehara 		brightness++;
589121b7b0dSAkio Idehara 	}
590b4f9fe12SLen Brown 
591135740deSSeth Forshee 	hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result);
59232bcd5cbSSeth Forshee 	if (hci_result == HCI_SUCCESS)
593121b7b0dSAkio Idehara 		return brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT);
59432bcd5cbSSeth Forshee 
59532bcd5cbSSeth Forshee 	return -EIO;
596b4f9fe12SLen Brown }
597b4f9fe12SLen Brown 
59862cce752SSeth Forshee static int get_lcd_brightness(struct backlight_device *bd)
59962cce752SSeth Forshee {
60062cce752SSeth Forshee 	struct toshiba_acpi_dev *dev = bl_get_data(bd);
60162cce752SSeth Forshee 	return __get_lcd_brightness(dev);
60262cce752SSeth Forshee }
60362cce752SSeth Forshee 
604936c8bcdSAlexey Dobriyan static int lcd_proc_show(struct seq_file *m, void *v)
605b4f9fe12SLen Brown {
606135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
607135740deSSeth Forshee 	int value;
608121b7b0dSAkio Idehara 	int levels;
609b4f9fe12SLen Brown 
610135740deSSeth Forshee 	if (!dev->backlight_dev)
611135740deSSeth Forshee 		return -ENODEV;
612135740deSSeth Forshee 
613121b7b0dSAkio Idehara 	levels = dev->backlight_dev->props.max_brightness + 1;
61462cce752SSeth Forshee 	value = get_lcd_brightness(dev->backlight_dev);
615b4f9fe12SLen Brown 	if (value >= 0) {
616936c8bcdSAlexey Dobriyan 		seq_printf(m, "brightness:              %d\n", value);
617121b7b0dSAkio Idehara 		seq_printf(m, "brightness_levels:       %d\n", levels);
61832bcd5cbSSeth Forshee 		return 0;
619b4f9fe12SLen Brown 	}
620b4f9fe12SLen Brown 
62132bcd5cbSSeth Forshee 	pr_err("Error reading LCD brightness\n");
62232bcd5cbSSeth Forshee 	return -EIO;
623936c8bcdSAlexey Dobriyan }
624936c8bcdSAlexey Dobriyan 
625936c8bcdSAlexey Dobriyan static int lcd_proc_open(struct inode *inode, struct file *file)
626936c8bcdSAlexey Dobriyan {
627d9dda78bSAl Viro 	return single_open(file, lcd_proc_show, PDE_DATA(inode));
628b4f9fe12SLen Brown }
629b4f9fe12SLen Brown 
63062cce752SSeth Forshee static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value)
631b4f9fe12SLen Brown {
632b4f9fe12SLen Brown 	u32 hci_result;
633b4f9fe12SLen Brown 
634121b7b0dSAkio Idehara 	if (dev->tr_backlight_supported) {
635121b7b0dSAkio Idehara 		bool enable = !value;
636121b7b0dSAkio Idehara 		int ret = set_tr_backlight_status(dev, enable);
637121b7b0dSAkio Idehara 		if (ret)
638121b7b0dSAkio Idehara 			return ret;
639121b7b0dSAkio Idehara 		if (value)
640121b7b0dSAkio Idehara 			value--;
641121b7b0dSAkio Idehara 	}
642121b7b0dSAkio Idehara 
643b4f9fe12SLen Brown 	value = value << HCI_LCD_BRIGHTNESS_SHIFT;
644135740deSSeth Forshee 	hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result);
64532bcd5cbSSeth Forshee 	return hci_result == HCI_SUCCESS ? 0 : -EIO;
646b4f9fe12SLen Brown }
647b4f9fe12SLen Brown 
648b4f9fe12SLen Brown static int set_lcd_status(struct backlight_device *bd)
649b4f9fe12SLen Brown {
650135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = bl_get_data(bd);
65162cce752SSeth Forshee 	return set_lcd_brightness(dev, bd->props.brightness);
652b4f9fe12SLen Brown }
653b4f9fe12SLen Brown 
654936c8bcdSAlexey Dobriyan static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
655936c8bcdSAlexey Dobriyan 			      size_t count, loff_t *pos)
656b4f9fe12SLen Brown {
657d9dda78bSAl Viro 	struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
658936c8bcdSAlexey Dobriyan 	char cmd[42];
659936c8bcdSAlexey Dobriyan 	size_t len;
660b4f9fe12SLen Brown 	int value;
661b4f9fe12SLen Brown 	int ret;
662121b7b0dSAkio Idehara 	int levels = dev->backlight_dev->props.max_brightness + 1;
663b4f9fe12SLen Brown 
664936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
665936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
666936c8bcdSAlexey Dobriyan 		return -EFAULT;
667936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
668936c8bcdSAlexey Dobriyan 
669936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " brightness : %i", &value) == 1 &&
670121b7b0dSAkio Idehara 	    value >= 0 && value < levels) {
67162cce752SSeth Forshee 		ret = set_lcd_brightness(dev, value);
672b4f9fe12SLen Brown 		if (ret == 0)
673b4f9fe12SLen Brown 			ret = count;
674b4f9fe12SLen Brown 	} else {
675b4f9fe12SLen Brown 		ret = -EINVAL;
676b4f9fe12SLen Brown 	}
677b4f9fe12SLen Brown 	return ret;
678b4f9fe12SLen Brown }
679b4f9fe12SLen Brown 
680936c8bcdSAlexey Dobriyan static const struct file_operations lcd_proc_fops = {
681936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
682936c8bcdSAlexey Dobriyan 	.open		= lcd_proc_open,
683936c8bcdSAlexey Dobriyan 	.read		= seq_read,
684936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
685936c8bcdSAlexey Dobriyan 	.release	= single_release,
686936c8bcdSAlexey Dobriyan 	.write		= lcd_proc_write,
687936c8bcdSAlexey Dobriyan };
688936c8bcdSAlexey Dobriyan 
68936d03f93SSeth Forshee static int get_video_status(struct toshiba_acpi_dev *dev, u32 *status)
69036d03f93SSeth Forshee {
69136d03f93SSeth Forshee 	u32 hci_result;
69236d03f93SSeth Forshee 
69336d03f93SSeth Forshee 	hci_read1(dev, HCI_VIDEO_OUT, status, &hci_result);
69436d03f93SSeth Forshee 	return hci_result == HCI_SUCCESS ? 0 : -EIO;
69536d03f93SSeth Forshee }
69636d03f93SSeth Forshee 
697936c8bcdSAlexey Dobriyan static int video_proc_show(struct seq_file *m, void *v)
698b4f9fe12SLen Brown {
699135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
700b4f9fe12SLen Brown 	u32 value;
70136d03f93SSeth Forshee 	int ret;
702b4f9fe12SLen Brown 
70336d03f93SSeth Forshee 	ret = get_video_status(dev, &value);
70436d03f93SSeth Forshee 	if (!ret) {
705b4f9fe12SLen Brown 		int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
706b4f9fe12SLen Brown 		int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
707b4f9fe12SLen Brown 		int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
708936c8bcdSAlexey Dobriyan 		seq_printf(m, "lcd_out:                 %d\n", is_lcd);
709936c8bcdSAlexey Dobriyan 		seq_printf(m, "crt_out:                 %d\n", is_crt);
710936c8bcdSAlexey Dobriyan 		seq_printf(m, "tv_out:                  %d\n", is_tv);
711b4f9fe12SLen Brown 	}
712b4f9fe12SLen Brown 
71336d03f93SSeth Forshee 	return ret;
714b4f9fe12SLen Brown }
715b4f9fe12SLen Brown 
716936c8bcdSAlexey Dobriyan static int video_proc_open(struct inode *inode, struct file *file)
717b4f9fe12SLen Brown {
718d9dda78bSAl Viro 	return single_open(file, video_proc_show, PDE_DATA(inode));
719936c8bcdSAlexey Dobriyan }
720936c8bcdSAlexey Dobriyan 
721936c8bcdSAlexey Dobriyan static ssize_t video_proc_write(struct file *file, const char __user *buf,
722936c8bcdSAlexey Dobriyan 				size_t count, loff_t *pos)
723936c8bcdSAlexey Dobriyan {
724d9dda78bSAl Viro 	struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
725936c8bcdSAlexey Dobriyan 	char *cmd, *buffer;
72636d03f93SSeth Forshee 	int ret;
727b4f9fe12SLen Brown 	int value;
728b4f9fe12SLen Brown 	int remain = count;
729b4f9fe12SLen Brown 	int lcd_out = -1;
730b4f9fe12SLen Brown 	int crt_out = -1;
731b4f9fe12SLen Brown 	int tv_out = -1;
732b4f9fe12SLen Brown 	u32 video_out;
733b4f9fe12SLen Brown 
734936c8bcdSAlexey Dobriyan 	cmd = kmalloc(count + 1, GFP_KERNEL);
735936c8bcdSAlexey Dobriyan 	if (!cmd)
736936c8bcdSAlexey Dobriyan 		return -ENOMEM;
737936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, count)) {
738936c8bcdSAlexey Dobriyan 		kfree(cmd);
739936c8bcdSAlexey Dobriyan 		return -EFAULT;
740936c8bcdSAlexey Dobriyan 	}
741936c8bcdSAlexey Dobriyan 	cmd[count] = '\0';
742936c8bcdSAlexey Dobriyan 
743936c8bcdSAlexey Dobriyan 	buffer = cmd;
744936c8bcdSAlexey Dobriyan 
745b4f9fe12SLen Brown 	/* scan expression.  Multiple expressions may be delimited with ;
746b4f9fe12SLen Brown 	 *
747b4f9fe12SLen Brown 	 *  NOTE: to keep scanning simple, invalid fields are ignored
748b4f9fe12SLen Brown 	 */
749b4f9fe12SLen Brown 	while (remain) {
750b4f9fe12SLen Brown 		if (sscanf(buffer, " lcd_out : %i", &value) == 1)
751b4f9fe12SLen Brown 			lcd_out = value & 1;
752b4f9fe12SLen Brown 		else if (sscanf(buffer, " crt_out : %i", &value) == 1)
753b4f9fe12SLen Brown 			crt_out = value & 1;
754b4f9fe12SLen Brown 		else if (sscanf(buffer, " tv_out : %i", &value) == 1)
755b4f9fe12SLen Brown 			tv_out = value & 1;
756b4f9fe12SLen Brown 		/* advance to one character past the next ; */
757b4f9fe12SLen Brown 		do {
758b4f9fe12SLen Brown 			++buffer;
759b4f9fe12SLen Brown 			--remain;
760b4f9fe12SLen Brown 		}
761b4f9fe12SLen Brown 		while (remain && *(buffer - 1) != ';');
762b4f9fe12SLen Brown 	}
763b4f9fe12SLen Brown 
764936c8bcdSAlexey Dobriyan 	kfree(cmd);
765936c8bcdSAlexey Dobriyan 
76636d03f93SSeth Forshee 	ret = get_video_status(dev, &video_out);
76736d03f93SSeth Forshee 	if (!ret) {
768b4f9fe12SLen Brown 		unsigned int new_video_out = video_out;
769b4f9fe12SLen Brown 		if (lcd_out != -1)
770b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out);
771b4f9fe12SLen Brown 		if (crt_out != -1)
772b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out);
773b4f9fe12SLen Brown 		if (tv_out != -1)
774b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out);
775b4f9fe12SLen Brown 		/* To avoid unnecessary video disruption, only write the new
776b4f9fe12SLen Brown 		 * video setting if something changed. */
777b4f9fe12SLen Brown 		if (new_video_out != video_out)
77832bcd5cbSSeth Forshee 			ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out);
779b4f9fe12SLen Brown 	}
780b4f9fe12SLen Brown 
78132bcd5cbSSeth Forshee 	return ret ? ret : count;
782b4f9fe12SLen Brown }
783b4f9fe12SLen Brown 
784936c8bcdSAlexey Dobriyan static const struct file_operations video_proc_fops = {
785936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
786936c8bcdSAlexey Dobriyan 	.open		= video_proc_open,
787936c8bcdSAlexey Dobriyan 	.read		= seq_read,
788936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
789936c8bcdSAlexey Dobriyan 	.release	= single_release,
790936c8bcdSAlexey Dobriyan 	.write		= video_proc_write,
791936c8bcdSAlexey Dobriyan };
792936c8bcdSAlexey Dobriyan 
79336d03f93SSeth Forshee static int get_fan_status(struct toshiba_acpi_dev *dev, u32 *status)
79436d03f93SSeth Forshee {
79536d03f93SSeth Forshee 	u32 hci_result;
79636d03f93SSeth Forshee 
79736d03f93SSeth Forshee 	hci_read1(dev, HCI_FAN, status, &hci_result);
79836d03f93SSeth Forshee 	return hci_result == HCI_SUCCESS ? 0 : -EIO;
79936d03f93SSeth Forshee }
80036d03f93SSeth Forshee 
801936c8bcdSAlexey Dobriyan static int fan_proc_show(struct seq_file *m, void *v)
802b4f9fe12SLen Brown {
803135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
80436d03f93SSeth Forshee 	int ret;
805b4f9fe12SLen Brown 	u32 value;
806b4f9fe12SLen Brown 
80736d03f93SSeth Forshee 	ret = get_fan_status(dev, &value);
80836d03f93SSeth Forshee 	if (!ret) {
809936c8bcdSAlexey Dobriyan 		seq_printf(m, "running:                 %d\n", (value > 0));
810135740deSSeth Forshee 		seq_printf(m, "force_on:                %d\n", dev->force_fan);
811b4f9fe12SLen Brown 	}
812b4f9fe12SLen Brown 
81336d03f93SSeth Forshee 	return ret;
814b4f9fe12SLen Brown }
815b4f9fe12SLen Brown 
816936c8bcdSAlexey Dobriyan static int fan_proc_open(struct inode *inode, struct file *file)
817b4f9fe12SLen Brown {
818d9dda78bSAl Viro 	return single_open(file, fan_proc_show, PDE_DATA(inode));
819936c8bcdSAlexey Dobriyan }
820936c8bcdSAlexey Dobriyan 
821936c8bcdSAlexey Dobriyan static ssize_t fan_proc_write(struct file *file, const char __user *buf,
822936c8bcdSAlexey Dobriyan 			      size_t count, loff_t *pos)
823936c8bcdSAlexey Dobriyan {
824d9dda78bSAl Viro 	struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
825936c8bcdSAlexey Dobriyan 	char cmd[42];
826936c8bcdSAlexey Dobriyan 	size_t len;
827b4f9fe12SLen Brown 	int value;
828b4f9fe12SLen Brown 	u32 hci_result;
829b4f9fe12SLen Brown 
830936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
831936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
832936c8bcdSAlexey Dobriyan 		return -EFAULT;
833936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
834936c8bcdSAlexey Dobriyan 
835936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " force_on : %i", &value) == 1 &&
836b4f9fe12SLen Brown 	    value >= 0 && value <= 1) {
837135740deSSeth Forshee 		hci_write1(dev, HCI_FAN, value, &hci_result);
838b4f9fe12SLen Brown 		if (hci_result != HCI_SUCCESS)
83932bcd5cbSSeth Forshee 			return -EIO;
840b4f9fe12SLen Brown 		else
841135740deSSeth Forshee 			dev->force_fan = value;
842b4f9fe12SLen Brown 	} else {
843b4f9fe12SLen Brown 		return -EINVAL;
844b4f9fe12SLen Brown 	}
845b4f9fe12SLen Brown 
846b4f9fe12SLen Brown 	return count;
847b4f9fe12SLen Brown }
848b4f9fe12SLen Brown 
849936c8bcdSAlexey Dobriyan static const struct file_operations fan_proc_fops = {
850936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
851936c8bcdSAlexey Dobriyan 	.open		= fan_proc_open,
852936c8bcdSAlexey Dobriyan 	.read		= seq_read,
853936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
854936c8bcdSAlexey Dobriyan 	.release	= single_release,
855936c8bcdSAlexey Dobriyan 	.write		= fan_proc_write,
856936c8bcdSAlexey Dobriyan };
857936c8bcdSAlexey Dobriyan 
858936c8bcdSAlexey Dobriyan static int keys_proc_show(struct seq_file *m, void *v)
859b4f9fe12SLen Brown {
860135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
861b4f9fe12SLen Brown 	u32 hci_result;
862b4f9fe12SLen Brown 	u32 value;
863b4f9fe12SLen Brown 
86411948b93SSeth Forshee 	if (!dev->key_event_valid && dev->system_event_supported) {
865135740deSSeth Forshee 		hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
866b4f9fe12SLen Brown 		if (hci_result == HCI_SUCCESS) {
867135740deSSeth Forshee 			dev->key_event_valid = 1;
868135740deSSeth Forshee 			dev->last_key_event = value;
869b4f9fe12SLen Brown 		} else if (hci_result == HCI_EMPTY) {
870b4f9fe12SLen Brown 			/* better luck next time */
871b4f9fe12SLen Brown 		} else if (hci_result == HCI_NOT_SUPPORTED) {
872b4f9fe12SLen Brown 			/* This is a workaround for an unresolved issue on
873b4f9fe12SLen Brown 			 * some machines where system events sporadically
874b4f9fe12SLen Brown 			 * become disabled. */
875135740deSSeth Forshee 			hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
8767e33460dSJoe Perches 			pr_notice("Re-enabled hotkeys\n");
877b4f9fe12SLen Brown 		} else {
8787e33460dSJoe Perches 			pr_err("Error reading hotkey status\n");
87932bcd5cbSSeth Forshee 			return -EIO;
880b4f9fe12SLen Brown 		}
881b4f9fe12SLen Brown 	}
882b4f9fe12SLen Brown 
883135740deSSeth Forshee 	seq_printf(m, "hotkey_ready:            %d\n", dev->key_event_valid);
884135740deSSeth Forshee 	seq_printf(m, "hotkey:                  0x%04x\n", dev->last_key_event);
885936c8bcdSAlexey Dobriyan 	return 0;
886b4f9fe12SLen Brown }
887b4f9fe12SLen Brown 
888936c8bcdSAlexey Dobriyan static int keys_proc_open(struct inode *inode, struct file *file)
889b4f9fe12SLen Brown {
890d9dda78bSAl Viro 	return single_open(file, keys_proc_show, PDE_DATA(inode));
891936c8bcdSAlexey Dobriyan }
892936c8bcdSAlexey Dobriyan 
893936c8bcdSAlexey Dobriyan static ssize_t keys_proc_write(struct file *file, const char __user *buf,
894936c8bcdSAlexey Dobriyan 			       size_t count, loff_t *pos)
895936c8bcdSAlexey Dobriyan {
896d9dda78bSAl Viro 	struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
897936c8bcdSAlexey Dobriyan 	char cmd[42];
898936c8bcdSAlexey Dobriyan 	size_t len;
899b4f9fe12SLen Brown 	int value;
900b4f9fe12SLen Brown 
901936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
902936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
903936c8bcdSAlexey Dobriyan 		return -EFAULT;
904936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
905936c8bcdSAlexey Dobriyan 
906936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) {
907135740deSSeth Forshee 		dev->key_event_valid = 0;
908b4f9fe12SLen Brown 	} else {
909b4f9fe12SLen Brown 		return -EINVAL;
910b4f9fe12SLen Brown 	}
911b4f9fe12SLen Brown 
912b4f9fe12SLen Brown 	return count;
913b4f9fe12SLen Brown }
914b4f9fe12SLen Brown 
915936c8bcdSAlexey Dobriyan static const struct file_operations keys_proc_fops = {
916936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
917936c8bcdSAlexey Dobriyan 	.open		= keys_proc_open,
918936c8bcdSAlexey Dobriyan 	.read		= seq_read,
919936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
920936c8bcdSAlexey Dobriyan 	.release	= single_release,
921936c8bcdSAlexey Dobriyan 	.write		= keys_proc_write,
922936c8bcdSAlexey Dobriyan };
923936c8bcdSAlexey Dobriyan 
924936c8bcdSAlexey Dobriyan static int version_proc_show(struct seq_file *m, void *v)
925b4f9fe12SLen Brown {
926936c8bcdSAlexey Dobriyan 	seq_printf(m, "driver:                  %s\n", TOSHIBA_ACPI_VERSION);
927936c8bcdSAlexey Dobriyan 	seq_printf(m, "proc_interface:          %d\n", PROC_INTERFACE_VERSION);
928936c8bcdSAlexey Dobriyan 	return 0;
929b4f9fe12SLen Brown }
930b4f9fe12SLen Brown 
931936c8bcdSAlexey Dobriyan static int version_proc_open(struct inode *inode, struct file *file)
932936c8bcdSAlexey Dobriyan {
933d9dda78bSAl Viro 	return single_open(file, version_proc_show, PDE_DATA(inode));
934936c8bcdSAlexey Dobriyan }
935936c8bcdSAlexey Dobriyan 
936936c8bcdSAlexey Dobriyan static const struct file_operations version_proc_fops = {
937936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
938936c8bcdSAlexey Dobriyan 	.open		= version_proc_open,
939936c8bcdSAlexey Dobriyan 	.read		= seq_read,
940936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
941936c8bcdSAlexey Dobriyan 	.release	= single_release,
942936c8bcdSAlexey Dobriyan };
943936c8bcdSAlexey Dobriyan 
944b4f9fe12SLen Brown /* proc and module init
945b4f9fe12SLen Brown  */
946b4f9fe12SLen Brown 
947b4f9fe12SLen Brown #define PROC_TOSHIBA		"toshiba"
948b4f9fe12SLen Brown 
949b859f159SGreg Kroah-Hartman static void create_toshiba_proc_entries(struct toshiba_acpi_dev *dev)
950b4f9fe12SLen Brown {
95136d03f93SSeth Forshee 	if (dev->backlight_dev)
952135740deSSeth Forshee 		proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir,
953135740deSSeth Forshee 				 &lcd_proc_fops, dev);
95436d03f93SSeth Forshee 	if (dev->video_supported)
955135740deSSeth Forshee 		proc_create_data("video", S_IRUGO | S_IWUSR, toshiba_proc_dir,
956135740deSSeth Forshee 				 &video_proc_fops, dev);
95736d03f93SSeth Forshee 	if (dev->fan_supported)
958135740deSSeth Forshee 		proc_create_data("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir,
959135740deSSeth Forshee 				 &fan_proc_fops, dev);
96036d03f93SSeth Forshee 	if (dev->hotkey_dev)
961135740deSSeth Forshee 		proc_create_data("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir,
962135740deSSeth Forshee 				 &keys_proc_fops, dev);
963135740deSSeth Forshee 	proc_create_data("version", S_IRUGO, toshiba_proc_dir,
964135740deSSeth Forshee 			 &version_proc_fops, dev);
965b4f9fe12SLen Brown }
966b4f9fe12SLen Brown 
96736d03f93SSeth Forshee static void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev)
968b4f9fe12SLen Brown {
96936d03f93SSeth Forshee 	if (dev->backlight_dev)
970936c8bcdSAlexey Dobriyan 		remove_proc_entry("lcd", toshiba_proc_dir);
97136d03f93SSeth Forshee 	if (dev->video_supported)
972936c8bcdSAlexey Dobriyan 		remove_proc_entry("video", toshiba_proc_dir);
97336d03f93SSeth Forshee 	if (dev->fan_supported)
974936c8bcdSAlexey Dobriyan 		remove_proc_entry("fan", toshiba_proc_dir);
97536d03f93SSeth Forshee 	if (dev->hotkey_dev)
976936c8bcdSAlexey Dobriyan 		remove_proc_entry("keys", toshiba_proc_dir);
977936c8bcdSAlexey Dobriyan 	remove_proc_entry("version", toshiba_proc_dir);
978b4f9fe12SLen Brown }
979b4f9fe12SLen Brown 
980acc2472eSLionel Debroux static const struct backlight_ops toshiba_backlight_data = {
981121b7b0dSAkio Idehara 	.options = BL_CORE_SUSPENDRESUME,
98262cce752SSeth Forshee 	.get_brightness = get_lcd_brightness,
983b4f9fe12SLen Brown 	.update_status  = set_lcd_status,
984b4f9fe12SLen Brown };
985b4f9fe12SLen Brown 
98629cd293fSSeth Forshee static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
98729cd293fSSeth Forshee 				      struct serio *port)
98829cd293fSSeth Forshee {
98929cd293fSSeth Forshee 	if (str & 0x20)
99029cd293fSSeth Forshee 		return false;
99129cd293fSSeth Forshee 
99229cd293fSSeth Forshee 	if (unlikely(data == 0xe0))
99329cd293fSSeth Forshee 		return false;
99429cd293fSSeth Forshee 
99529cd293fSSeth Forshee 	if ((data & 0x7f) == TOS1900_FN_SCAN) {
99629cd293fSSeth Forshee 		schedule_work(&toshiba_acpi->hotkey_work);
99729cd293fSSeth Forshee 		return true;
99829cd293fSSeth Forshee 	}
99929cd293fSSeth Forshee 
100029cd293fSSeth Forshee 	return false;
100129cd293fSSeth Forshee }
100229cd293fSSeth Forshee 
100329cd293fSSeth Forshee static void toshiba_acpi_hotkey_work(struct work_struct *work)
100429cd293fSSeth Forshee {
100529cd293fSSeth Forshee 	acpi_handle ec_handle = ec_get_handle();
100629cd293fSSeth Forshee 	acpi_status status;
100729cd293fSSeth Forshee 
100829cd293fSSeth Forshee 	if (!ec_handle)
100929cd293fSSeth Forshee 		return;
101029cd293fSSeth Forshee 
101129cd293fSSeth Forshee 	status = acpi_evaluate_object(ec_handle, "NTFY", NULL, NULL);
101229cd293fSSeth Forshee 	if (ACPI_FAILURE(status))
101329cd293fSSeth Forshee 		pr_err("ACPI NTFY method execution failed\n");
101429cd293fSSeth Forshee }
101529cd293fSSeth Forshee 
101629cd293fSSeth Forshee /*
101729cd293fSSeth Forshee  * Returns hotkey scancode, or < 0 on failure.
101829cd293fSSeth Forshee  */
101929cd293fSSeth Forshee static int toshiba_acpi_query_hotkey(struct toshiba_acpi_dev *dev)
102029cd293fSSeth Forshee {
102174facaf7SZhang Rui 	unsigned long long value;
102229cd293fSSeth Forshee 	acpi_status status;
102329cd293fSSeth Forshee 
102474facaf7SZhang Rui 	status = acpi_evaluate_integer(dev->acpi_dev->handle, "INFO",
102574facaf7SZhang Rui 				      NULL, &value);
102674facaf7SZhang Rui 	if (ACPI_FAILURE(status)) {
102729cd293fSSeth Forshee 		pr_err("ACPI INFO method execution failed\n");
102829cd293fSSeth Forshee 		return -EIO;
102929cd293fSSeth Forshee 	}
103029cd293fSSeth Forshee 
103174facaf7SZhang Rui 	return value;
103229cd293fSSeth Forshee }
103329cd293fSSeth Forshee 
103429cd293fSSeth Forshee static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev,
103529cd293fSSeth Forshee 				       int scancode)
103629cd293fSSeth Forshee {
103729cd293fSSeth Forshee 	if (scancode == 0x100)
103829cd293fSSeth Forshee 		return;
103929cd293fSSeth Forshee 
104029cd293fSSeth Forshee 	/* act on key press; ignore key release */
104129cd293fSSeth Forshee 	if (scancode & 0x80)
104229cd293fSSeth Forshee 		return;
104329cd293fSSeth Forshee 
104429cd293fSSeth Forshee 	if (!sparse_keymap_report_event(dev->hotkey_dev, scancode, 1, true))
104529cd293fSSeth Forshee 		pr_info("Unknown key %x\n", scancode);
104629cd293fSSeth Forshee }
104729cd293fSSeth Forshee 
1048b859f159SGreg Kroah-Hartman static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
10496335e4d5SMatthew Garrett {
1050135740deSSeth Forshee 	acpi_status status;
1051e2e19606SZhang Rui 	acpi_handle ec_handle;
1052135740deSSeth Forshee 	int error;
105329cd293fSSeth Forshee 	u32 hci_result;
1054135740deSSeth Forshee 
1055135740deSSeth Forshee 	dev->hotkey_dev = input_allocate_device();
1056b222cca6SJoe Perches 	if (!dev->hotkey_dev)
1057135740deSSeth Forshee 		return -ENOMEM;
1058135740deSSeth Forshee 
1059135740deSSeth Forshee 	dev->hotkey_dev->name = "Toshiba input device";
10606e02cc7eSSeth Forshee 	dev->hotkey_dev->phys = "toshiba_acpi/input0";
1061135740deSSeth Forshee 	dev->hotkey_dev->id.bustype = BUS_HOST;
1062135740deSSeth Forshee 
1063135740deSSeth Forshee 	error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL);
1064135740deSSeth Forshee 	if (error)
1065135740deSSeth Forshee 		goto err_free_dev;
1066135740deSSeth Forshee 
106729cd293fSSeth Forshee 	/*
106829cd293fSSeth Forshee 	 * For some machines the SCI responsible for providing hotkey
106929cd293fSSeth Forshee 	 * notification doesn't fire. We can trigger the notification
107029cd293fSSeth Forshee 	 * whenever the Fn key is pressed using the NTFY method, if
107129cd293fSSeth Forshee 	 * supported, so if it's present set up an i8042 key filter
107229cd293fSSeth Forshee 	 * for this purpose.
107329cd293fSSeth Forshee 	 */
107429cd293fSSeth Forshee 	status = AE_ERROR;
107529cd293fSSeth Forshee 	ec_handle = ec_get_handle();
1076e2e19606SZhang Rui 	if (ec_handle && acpi_has_method(ec_handle, "NTFY")) {
107729cd293fSSeth Forshee 		INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);
107829cd293fSSeth Forshee 
107929cd293fSSeth Forshee 		error = i8042_install_filter(toshiba_acpi_i8042_filter);
108029cd293fSSeth Forshee 		if (error) {
108129cd293fSSeth Forshee 			pr_err("Error installing key filter\n");
108229cd293fSSeth Forshee 			goto err_free_keymap;
108329cd293fSSeth Forshee 		}
108429cd293fSSeth Forshee 
108529cd293fSSeth Forshee 		dev->ntfy_supported = 1;
108629cd293fSSeth Forshee 	}
108729cd293fSSeth Forshee 
108829cd293fSSeth Forshee 	/*
108929cd293fSSeth Forshee 	 * Determine hotkey query interface. Prefer using the INFO
109029cd293fSSeth Forshee 	 * method when it is available.
109129cd293fSSeth Forshee 	 */
1092e2e19606SZhang Rui 	if (acpi_has_method(dev->acpi_dev->handle, "INFO"))
109329cd293fSSeth Forshee 		dev->info_supported = 1;
1094e2e19606SZhang Rui 	else {
109529cd293fSSeth Forshee 		hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
109629cd293fSSeth Forshee 		if (hci_result == HCI_SUCCESS)
109729cd293fSSeth Forshee 			dev->system_event_supported = 1;
109829cd293fSSeth Forshee 	}
109929cd293fSSeth Forshee 
110029cd293fSSeth Forshee 	if (!dev->info_supported && !dev->system_event_supported) {
110129cd293fSSeth Forshee 		pr_warn("No hotkey query interface found\n");
110229cd293fSSeth Forshee 		goto err_remove_filter;
110329cd293fSSeth Forshee 	}
110429cd293fSSeth Forshee 
11056e02cc7eSSeth Forshee 	status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL);
1106135740deSSeth Forshee 	if (ACPI_FAILURE(status)) {
1107135740deSSeth Forshee 		pr_info("Unable to enable hotkeys\n");
1108135740deSSeth Forshee 		error = -ENODEV;
110929cd293fSSeth Forshee 		goto err_remove_filter;
1110135740deSSeth Forshee 	}
1111135740deSSeth Forshee 
1112135740deSSeth Forshee 	error = input_register_device(dev->hotkey_dev);
1113135740deSSeth Forshee 	if (error) {
1114135740deSSeth Forshee 		pr_info("Unable to register input device\n");
111529cd293fSSeth Forshee 		goto err_remove_filter;
1116135740deSSeth Forshee 	}
1117135740deSSeth Forshee 
111829cd293fSSeth Forshee 	hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &hci_result);
1119135740deSSeth Forshee 	return 0;
1120135740deSSeth Forshee 
112129cd293fSSeth Forshee  err_remove_filter:
112229cd293fSSeth Forshee 	if (dev->ntfy_supported)
112329cd293fSSeth Forshee 		i8042_remove_filter(toshiba_acpi_i8042_filter);
1124135740deSSeth Forshee  err_free_keymap:
1125135740deSSeth Forshee 	sparse_keymap_free(dev->hotkey_dev);
1126135740deSSeth Forshee  err_free_dev:
1127135740deSSeth Forshee 	input_free_device(dev->hotkey_dev);
1128135740deSSeth Forshee 	dev->hotkey_dev = NULL;
1129135740deSSeth Forshee 	return error;
1130135740deSSeth Forshee }
1131135740deSSeth Forshee 
1132b859f159SGreg Kroah-Hartman static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)
113362cce752SSeth Forshee {
113462cce752SSeth Forshee 	struct backlight_properties props;
113562cce752SSeth Forshee 	int brightness;
113662cce752SSeth Forshee 	int ret;
1137121b7b0dSAkio Idehara 	bool enabled;
113862cce752SSeth Forshee 
113962cce752SSeth Forshee 	/*
114062cce752SSeth Forshee 	 * Some machines don't support the backlight methods at all, and
114162cce752SSeth Forshee 	 * others support it read-only. Either of these is pretty useless,
114262cce752SSeth Forshee 	 * so only register the backlight device if the backlight method
114362cce752SSeth Forshee 	 * supports both reads and writes.
114462cce752SSeth Forshee 	 */
114562cce752SSeth Forshee 	brightness = __get_lcd_brightness(dev);
114662cce752SSeth Forshee 	if (brightness < 0)
114762cce752SSeth Forshee 		return 0;
114862cce752SSeth Forshee 	ret = set_lcd_brightness(dev, brightness);
114962cce752SSeth Forshee 	if (ret) {
115062cce752SSeth Forshee 		pr_debug("Backlight method is read-only, disabling backlight support\n");
115162cce752SSeth Forshee 		return 0;
115262cce752SSeth Forshee 	}
115362cce752SSeth Forshee 
1154121b7b0dSAkio Idehara 	/* Determine whether or not BIOS supports transflective backlight */
1155121b7b0dSAkio Idehara 	ret = get_tr_backlight_status(dev, &enabled);
1156121b7b0dSAkio Idehara 	dev->tr_backlight_supported = !ret;
1157121b7b0dSAkio Idehara 
115853039f22SMatthew Garrett 	memset(&props, 0, sizeof(props));
115962cce752SSeth Forshee 	props.type = BACKLIGHT_PLATFORM;
116062cce752SSeth Forshee 	props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
116162cce752SSeth Forshee 
1162121b7b0dSAkio Idehara 	/* adding an extra level and having 0 change to transflective mode */
1163121b7b0dSAkio Idehara 	if (dev->tr_backlight_supported)
1164121b7b0dSAkio Idehara 		props.max_brightness++;
1165121b7b0dSAkio Idehara 
116662cce752SSeth Forshee 	dev->backlight_dev = backlight_device_register("toshiba",
116762cce752SSeth Forshee 						       &dev->acpi_dev->dev,
116862cce752SSeth Forshee 						       dev,
116962cce752SSeth Forshee 						       &toshiba_backlight_data,
117062cce752SSeth Forshee 						       &props);
117162cce752SSeth Forshee 	if (IS_ERR(dev->backlight_dev)) {
117262cce752SSeth Forshee 		ret = PTR_ERR(dev->backlight_dev);
117362cce752SSeth Forshee 		pr_err("Could not register toshiba backlight device\n");
117462cce752SSeth Forshee 		dev->backlight_dev = NULL;
117562cce752SSeth Forshee 		return ret;
117662cce752SSeth Forshee 	}
117762cce752SSeth Forshee 
117862cce752SSeth Forshee 	dev->backlight_dev->props.brightness = brightness;
117962cce752SSeth Forshee 	return 0;
118062cce752SSeth Forshee }
118162cce752SSeth Forshee 
118251fac838SRafael J. Wysocki static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
1183135740deSSeth Forshee {
1184135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
1185135740deSSeth Forshee 
118636d03f93SSeth Forshee 	remove_toshiba_proc_entries(dev);
1187135740deSSeth Forshee 
118829cd293fSSeth Forshee 	if (dev->ntfy_supported) {
118929cd293fSSeth Forshee 		i8042_remove_filter(toshiba_acpi_i8042_filter);
119029cd293fSSeth Forshee 		cancel_work_sync(&dev->hotkey_work);
119129cd293fSSeth Forshee 	}
119229cd293fSSeth Forshee 
1193135740deSSeth Forshee 	if (dev->hotkey_dev) {
1194135740deSSeth Forshee 		input_unregister_device(dev->hotkey_dev);
1195135740deSSeth Forshee 		sparse_keymap_free(dev->hotkey_dev);
1196135740deSSeth Forshee 	}
1197135740deSSeth Forshee 
1198135740deSSeth Forshee 	if (dev->bt_rfk) {
1199135740deSSeth Forshee 		rfkill_unregister(dev->bt_rfk);
1200135740deSSeth Forshee 		rfkill_destroy(dev->bt_rfk);
1201135740deSSeth Forshee 	}
1202135740deSSeth Forshee 
1203135740deSSeth Forshee 	if (dev->backlight_dev)
1204135740deSSeth Forshee 		backlight_device_unregister(dev->backlight_dev);
1205135740deSSeth Forshee 
120636d03f93SSeth Forshee 	if (dev->illumination_supported)
1207135740deSSeth Forshee 		led_classdev_unregister(&dev->led_dev);
1208135740deSSeth Forshee 
120929cd293fSSeth Forshee 	if (toshiba_acpi)
121029cd293fSSeth Forshee 		toshiba_acpi = NULL;
121129cd293fSSeth Forshee 
1212135740deSSeth Forshee 	kfree(dev);
1213135740deSSeth Forshee 
1214135740deSSeth Forshee 	return 0;
1215135740deSSeth Forshee }
1216135740deSSeth Forshee 
1217b859f159SGreg Kroah-Hartman static const char *find_hci_method(acpi_handle handle)
1218a540d6b5SSeth Forshee {
1219e2e19606SZhang Rui 	if (acpi_has_method(handle, "GHCI"))
1220a540d6b5SSeth Forshee 		return "GHCI";
1221a540d6b5SSeth Forshee 
1222e2e19606SZhang Rui 	if (acpi_has_method(handle, "SPFC"))
1223a540d6b5SSeth Forshee 		return "SPFC";
1224a540d6b5SSeth Forshee 
1225a540d6b5SSeth Forshee 	return NULL;
1226a540d6b5SSeth Forshee }
1227a540d6b5SSeth Forshee 
1228b859f159SGreg Kroah-Hartman static int toshiba_acpi_add(struct acpi_device *acpi_dev)
1229135740deSSeth Forshee {
1230135740deSSeth Forshee 	struct toshiba_acpi_dev *dev;
1231a540d6b5SSeth Forshee 	const char *hci_method;
123236d03f93SSeth Forshee 	u32 dummy;
1233135740deSSeth Forshee 	bool bt_present;
1234135740deSSeth Forshee 	int ret = 0;
1235135740deSSeth Forshee 
123629cd293fSSeth Forshee 	if (toshiba_acpi)
123729cd293fSSeth Forshee 		return -EBUSY;
123829cd293fSSeth Forshee 
1239135740deSSeth Forshee 	pr_info("Toshiba Laptop ACPI Extras version %s\n",
1240135740deSSeth Forshee 	       TOSHIBA_ACPI_VERSION);
1241135740deSSeth Forshee 
1242a540d6b5SSeth Forshee 	hci_method = find_hci_method(acpi_dev->handle);
1243a540d6b5SSeth Forshee 	if (!hci_method) {
1244a540d6b5SSeth Forshee 		pr_err("HCI interface not found\n");
12456e02cc7eSSeth Forshee 		return -ENODEV;
1246a540d6b5SSeth Forshee 	}
12476e02cc7eSSeth Forshee 
1248135740deSSeth Forshee 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1249135740deSSeth Forshee 	if (!dev)
1250135740deSSeth Forshee 		return -ENOMEM;
1251135740deSSeth Forshee 	dev->acpi_dev = acpi_dev;
1252a540d6b5SSeth Forshee 	dev->method_hci = hci_method;
1253135740deSSeth Forshee 	acpi_dev->driver_data = dev;
1254135740deSSeth Forshee 
12556e02cc7eSSeth Forshee 	if (toshiba_acpi_setup_keyboard(dev))
1256135740deSSeth Forshee 		pr_info("Unable to activate hotkeys\n");
1257135740deSSeth Forshee 
1258135740deSSeth Forshee 	mutex_init(&dev->mutex);
1259135740deSSeth Forshee 
126062cce752SSeth Forshee 	ret = toshiba_acpi_setup_backlight(dev);
126162cce752SSeth Forshee 	if (ret)
1262135740deSSeth Forshee 		goto error;
1263135740deSSeth Forshee 
1264135740deSSeth Forshee 	/* Register rfkill switch for Bluetooth */
1265135740deSSeth Forshee 	if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) {
1266135740deSSeth Forshee 		dev->bt_rfk = rfkill_alloc("Toshiba Bluetooth",
1267135740deSSeth Forshee 					   &acpi_dev->dev,
1268135740deSSeth Forshee 					   RFKILL_TYPE_BLUETOOTH,
1269135740deSSeth Forshee 					   &toshiba_rfk_ops,
1270135740deSSeth Forshee 					   dev);
1271135740deSSeth Forshee 		if (!dev->bt_rfk) {
1272135740deSSeth Forshee 			pr_err("unable to allocate rfkill device\n");
1273135740deSSeth Forshee 			ret = -ENOMEM;
1274135740deSSeth Forshee 			goto error;
1275135740deSSeth Forshee 		}
1276135740deSSeth Forshee 
1277135740deSSeth Forshee 		ret = rfkill_register(dev->bt_rfk);
1278135740deSSeth Forshee 		if (ret) {
1279135740deSSeth Forshee 			pr_err("unable to register rfkill device\n");
1280135740deSSeth Forshee 			rfkill_destroy(dev->bt_rfk);
1281135740deSSeth Forshee 			goto error;
1282135740deSSeth Forshee 		}
1283135740deSSeth Forshee 	}
1284135740deSSeth Forshee 
1285135740deSSeth Forshee 	if (toshiba_illumination_available(dev)) {
1286135740deSSeth Forshee 		dev->led_dev.name = "toshiba::illumination";
1287135740deSSeth Forshee 		dev->led_dev.max_brightness = 1;
1288135740deSSeth Forshee 		dev->led_dev.brightness_set = toshiba_illumination_set;
1289135740deSSeth Forshee 		dev->led_dev.brightness_get = toshiba_illumination_get;
1290135740deSSeth Forshee 		if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev))
129136d03f93SSeth Forshee 			dev->illumination_supported = 1;
1292135740deSSeth Forshee 	}
1293135740deSSeth Forshee 
129436d03f93SSeth Forshee 	/* Determine whether or not BIOS supports fan and video interfaces */
129536d03f93SSeth Forshee 
129636d03f93SSeth Forshee 	ret = get_video_status(dev, &dummy);
129736d03f93SSeth Forshee 	dev->video_supported = !ret;
129836d03f93SSeth Forshee 
129936d03f93SSeth Forshee 	ret = get_fan_status(dev, &dummy);
130036d03f93SSeth Forshee 	dev->fan_supported = !ret;
130136d03f93SSeth Forshee 
130236d03f93SSeth Forshee 	create_toshiba_proc_entries(dev);
130336d03f93SSeth Forshee 
130429cd293fSSeth Forshee 	toshiba_acpi = dev;
130529cd293fSSeth Forshee 
1306135740deSSeth Forshee 	return 0;
1307135740deSSeth Forshee 
1308135740deSSeth Forshee error:
130951fac838SRafael J. Wysocki 	toshiba_acpi_remove(acpi_dev);
1310135740deSSeth Forshee 	return ret;
1311135740deSSeth Forshee }
1312135740deSSeth Forshee 
1313135740deSSeth Forshee static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
1314135740deSSeth Forshee {
1315135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
13166335e4d5SMatthew Garrett 	u32 hci_result, value;
131711948b93SSeth Forshee 	int retries = 3;
131829cd293fSSeth Forshee 	int scancode;
13196335e4d5SMatthew Garrett 
132029cd293fSSeth Forshee 	if (event != 0x80)
13216335e4d5SMatthew Garrett 		return;
132211948b93SSeth Forshee 
132329cd293fSSeth Forshee 	if (dev->info_supported) {
132429cd293fSSeth Forshee 		scancode = toshiba_acpi_query_hotkey(dev);
132529cd293fSSeth Forshee 		if (scancode < 0)
132629cd293fSSeth Forshee 			pr_err("Failed to query hotkey event\n");
132729cd293fSSeth Forshee 		else if (scancode != 0)
132829cd293fSSeth Forshee 			toshiba_acpi_report_hotkey(dev, scancode);
132929cd293fSSeth Forshee 	} else if (dev->system_event_supported) {
13306335e4d5SMatthew Garrett 		do {
1331135740deSSeth Forshee 			hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
133211948b93SSeth Forshee 			switch (hci_result) {
133311948b93SSeth Forshee 			case HCI_SUCCESS:
133429cd293fSSeth Forshee 				toshiba_acpi_report_hotkey(dev, (int)value);
133511948b93SSeth Forshee 				break;
133611948b93SSeth Forshee 			case HCI_NOT_SUPPORTED:
133729cd293fSSeth Forshee 				/*
133829cd293fSSeth Forshee 				 * This is a workaround for an unresolved
133929cd293fSSeth Forshee 				 * issue on some machines where system events
134029cd293fSSeth Forshee 				 * sporadically become disabled.
134129cd293fSSeth Forshee 				 */
134229cd293fSSeth Forshee 				hci_write1(dev, HCI_SYSTEM_EVENT, 1,
134329cd293fSSeth Forshee 					   &hci_result);
13447e33460dSJoe Perches 				pr_notice("Re-enabled hotkeys\n");
134511948b93SSeth Forshee 				/* fall through */
134611948b93SSeth Forshee 			default:
134711948b93SSeth Forshee 				retries--;
134811948b93SSeth Forshee 				break;
13496335e4d5SMatthew Garrett 			}
135011948b93SSeth Forshee 		} while (retries && hci_result != HCI_EMPTY);
13516335e4d5SMatthew Garrett 	}
135229cd293fSSeth Forshee }
13536335e4d5SMatthew Garrett 
13543567a4e2SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
135543d2fd3bSRafael J. Wysocki static int toshiba_acpi_suspend(struct device *device)
135629cd293fSSeth Forshee {
135743d2fd3bSRafael J. Wysocki 	struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device));
135829cd293fSSeth Forshee 	u32 result;
135929cd293fSSeth Forshee 
136029cd293fSSeth Forshee 	if (dev->hotkey_dev)
136129cd293fSSeth Forshee 		hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE, &result);
136229cd293fSSeth Forshee 
136329cd293fSSeth Forshee 	return 0;
136429cd293fSSeth Forshee }
136529cd293fSSeth Forshee 
136643d2fd3bSRafael J. Wysocki static int toshiba_acpi_resume(struct device *device)
136729cd293fSSeth Forshee {
136843d2fd3bSRafael J. Wysocki 	struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device));
136929cd293fSSeth Forshee 	u32 result;
137029cd293fSSeth Forshee 
137129cd293fSSeth Forshee 	if (dev->hotkey_dev)
137229cd293fSSeth Forshee 		hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &result);
137329cd293fSSeth Forshee 
137429cd293fSSeth Forshee 	return 0;
137529cd293fSSeth Forshee }
13763567a4e2SRafael J. Wysocki #endif
13776335e4d5SMatthew Garrett 
137843d2fd3bSRafael J. Wysocki static SIMPLE_DEV_PM_OPS(toshiba_acpi_pm,
137943d2fd3bSRafael J. Wysocki 			 toshiba_acpi_suspend, toshiba_acpi_resume);
138043d2fd3bSRafael J. Wysocki 
1381135740deSSeth Forshee static struct acpi_driver toshiba_acpi_driver = {
1382135740deSSeth Forshee 	.name	= "Toshiba ACPI driver",
1383135740deSSeth Forshee 	.owner	= THIS_MODULE,
1384135740deSSeth Forshee 	.ids	= toshiba_device_ids,
1385135740deSSeth Forshee 	.flags	= ACPI_DRIVER_ALL_NOTIFY_EVENTS,
1386135740deSSeth Forshee 	.ops	= {
1387135740deSSeth Forshee 		.add		= toshiba_acpi_add,
1388135740deSSeth Forshee 		.remove		= toshiba_acpi_remove,
1389135740deSSeth Forshee 		.notify		= toshiba_acpi_notify,
1390135740deSSeth Forshee 	},
139143d2fd3bSRafael J. Wysocki 	.drv.pm	= &toshiba_acpi_pm,
1392135740deSSeth Forshee };
1393b4f9fe12SLen Brown 
1394b4f9fe12SLen Brown static int __init toshiba_acpi_init(void)
1395b4f9fe12SLen Brown {
1396135740deSSeth Forshee 	int ret;
1397b4f9fe12SLen Brown 
1398f11f999eSSeth Forshee 	/*
1399f11f999eSSeth Forshee 	 * Machines with this WMI guid aren't supported due to bugs in
1400f11f999eSSeth Forshee 	 * their AML. This check relies on wmi initializing before
1401f11f999eSSeth Forshee 	 * toshiba_acpi to guarantee guids have been identified.
1402f11f999eSSeth Forshee 	 */
1403f11f999eSSeth Forshee 	if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
1404f11f999eSSeth Forshee 		return -ENODEV;
1405f11f999eSSeth Forshee 
1406b4f9fe12SLen Brown 	toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
1407b4f9fe12SLen Brown 	if (!toshiba_proc_dir) {
1408135740deSSeth Forshee 		pr_err("Unable to create proc dir " PROC_TOSHIBA "\n");
1409b4f9fe12SLen Brown 		return -ENODEV;
1410b4f9fe12SLen Brown 	}
1411b4f9fe12SLen Brown 
1412135740deSSeth Forshee 	ret = acpi_bus_register_driver(&toshiba_acpi_driver);
1413b4f9fe12SLen Brown 	if (ret) {
1414135740deSSeth Forshee 		pr_err("Failed to register ACPI driver: %d\n", ret);
1415135740deSSeth Forshee 		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
1416135740deSSeth Forshee 	}
1417135740deSSeth Forshee 
1418b4f9fe12SLen Brown 	return ret;
1419b4f9fe12SLen Brown }
1420b4f9fe12SLen Brown 
1421135740deSSeth Forshee static void __exit toshiba_acpi_exit(void)
1422135740deSSeth Forshee {
1423135740deSSeth Forshee 	acpi_bus_unregister_driver(&toshiba_acpi_driver);
1424135740deSSeth Forshee 	if (toshiba_proc_dir)
1425135740deSSeth Forshee 		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
1426b4f9fe12SLen Brown }
1427b4f9fe12SLen Brown 
1428b4f9fe12SLen Brown module_init(toshiba_acpi_init);
1429b4f9fe12SLen Brown module_exit(toshiba_acpi_exit);
1430