xref: /linux/drivers/platform/x86/toshiba_acpi.c (revision 32bcd5cba02436336053d9c3b7a8ff86f889ebf4)
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>
55b4f9fe12SLen Brown 
56b4f9fe12SLen Brown #include <asm/uaccess.h>
57b4f9fe12SLen Brown 
58b4f9fe12SLen Brown #include <acpi/acpi_drivers.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 
64b4f9fe12SLen Brown /* Toshiba ACPI method paths */
65b4f9fe12SLen Brown #define METHOD_LCD_BRIGHTNESS	"\\_SB_.PCI0.VGA_.LCD_._BCM"
666335e4d5SMatthew Garrett #define TOSH_INTERFACE_1	"\\_SB_.VALD"
676335e4d5SMatthew Garrett #define TOSH_INTERFACE_2	"\\_SB_.VALZ"
68b4f9fe12SLen Brown #define METHOD_VIDEO_OUT	"\\_SB_.VALX.DSSX"
696335e4d5SMatthew Garrett #define GHCI_METHOD		".GHCI"
70b4f9fe12SLen Brown 
71b4f9fe12SLen Brown /* Toshiba HCI interface definitions
72b4f9fe12SLen Brown  *
73b4f9fe12SLen Brown  * HCI is Toshiba's "Hardware Control Interface" which is supposed to
74b4f9fe12SLen Brown  * be uniform across all their models.  Ideally we would just call
75b4f9fe12SLen Brown  * dedicated ACPI methods instead of using this primitive interface.
76b4f9fe12SLen Brown  * However the ACPI methods seem to be incomplete in some areas (for
77b4f9fe12SLen Brown  * example they allow setting, but not reading, the LCD brightness value),
78b4f9fe12SLen Brown  * so this is still useful.
79b4f9fe12SLen Brown  */
80b4f9fe12SLen Brown 
81b4f9fe12SLen Brown #define HCI_WORDS			6
82b4f9fe12SLen Brown 
83b4f9fe12SLen Brown /* operations */
84b4f9fe12SLen Brown #define HCI_SET				0xff00
85b4f9fe12SLen Brown #define HCI_GET				0xfe00
86b4f9fe12SLen Brown 
87b4f9fe12SLen Brown /* return codes */
88b4f9fe12SLen Brown #define HCI_SUCCESS			0x0000
89b4f9fe12SLen Brown #define HCI_FAILURE			0x1000
90b4f9fe12SLen Brown #define HCI_NOT_SUPPORTED		0x8000
91b4f9fe12SLen Brown #define HCI_EMPTY			0x8c00
92b4f9fe12SLen Brown 
93b4f9fe12SLen Brown /* registers */
94b4f9fe12SLen Brown #define HCI_FAN				0x0004
95b4f9fe12SLen Brown #define HCI_SYSTEM_EVENT		0x0016
96b4f9fe12SLen Brown #define HCI_VIDEO_OUT			0x001c
97b4f9fe12SLen Brown #define HCI_HOTKEY_EVENT		0x001e
98b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS		0x002a
99b4f9fe12SLen Brown #define HCI_WIRELESS			0x0056
100b4f9fe12SLen Brown 
101b4f9fe12SLen Brown /* field definitions */
102b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_BITS		3
103b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_SHIFT	(16-HCI_LCD_BRIGHTNESS_BITS)
104b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_LEVELS	(1 << HCI_LCD_BRIGHTNESS_BITS)
105b4f9fe12SLen Brown #define HCI_VIDEO_OUT_LCD		0x1
106b4f9fe12SLen Brown #define HCI_VIDEO_OUT_CRT		0x2
107b4f9fe12SLen Brown #define HCI_VIDEO_OUT_TV		0x4
108b4f9fe12SLen Brown #define HCI_WIRELESS_KILL_SWITCH	0x01
109b4f9fe12SLen Brown #define HCI_WIRELESS_BT_PRESENT		0x0f
110b4f9fe12SLen Brown #define HCI_WIRELESS_BT_ATTACH		0x40
111b4f9fe12SLen Brown #define HCI_WIRELESS_BT_POWER		0x80
112b4f9fe12SLen Brown 
113135740deSSeth Forshee struct toshiba_acpi_dev {
114135740deSSeth Forshee 	struct acpi_device *acpi_dev;
115135740deSSeth Forshee 	const char *method_hci;
116135740deSSeth Forshee 	struct rfkill *bt_rfk;
117135740deSSeth Forshee 	struct input_dev *hotkey_dev;
118135740deSSeth Forshee 	struct backlight_device *backlight_dev;
119135740deSSeth Forshee 	struct led_classdev led_dev;
120135740deSSeth Forshee 	int illumination_installed;
121135740deSSeth Forshee 	int force_fan;
122135740deSSeth Forshee 	int last_key_event;
123135740deSSeth Forshee 	int key_event_valid;
124135740deSSeth Forshee 	acpi_handle handle;
125135740deSSeth Forshee 
126135740deSSeth Forshee 	struct mutex mutex;
127135740deSSeth Forshee };
128135740deSSeth Forshee 
129b4f9fe12SLen Brown static const struct acpi_device_id toshiba_device_ids[] = {
130b4f9fe12SLen Brown 	{"TOS6200", 0},
131b4f9fe12SLen Brown 	{"TOS6208", 0},
132b4f9fe12SLen Brown 	{"TOS1900", 0},
133b4f9fe12SLen Brown 	{"", 0},
134b4f9fe12SLen Brown };
135b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
136b4f9fe12SLen Brown 
137135740deSSeth Forshee static const struct key_entry toshiba_acpi_keymap[] __devinitconst = {
138384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x101, { KEY_MUTE } },
139384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x102, { KEY_ZOOMOUT } },
140384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x103, { KEY_ZOOMIN } },
141384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13b, { KEY_COFFEE } },
142384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13c, { KEY_BATTERY } },
143384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13d, { KEY_SLEEP } },
144384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13e, { KEY_SUSPEND } },
145384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } },
146384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
147384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
148384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x142, { KEY_WLAN } },
149384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x143, { KEY_PROG1 } },
150a49010f5SJon Dowland 	{ KE_KEY, 0x17f, { KEY_FN } },
151384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb05, { KEY_PROG2 } },
152384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb06, { KEY_WWW } },
153384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb07, { KEY_MAIL } },
154384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb30, { KEY_STOP } },
155384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb31, { KEY_PREVIOUSSONG } },
156384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb32, { KEY_NEXTSONG } },
157384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
158384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb5a, { KEY_MEDIA } },
159384a7cd9SDmitry Torokhov 	{ KE_END, 0 },
1606335e4d5SMatthew Garrett };
1616335e4d5SMatthew Garrett 
162b4f9fe12SLen Brown /* utility
163b4f9fe12SLen Brown  */
164b4f9fe12SLen Brown 
165b4f9fe12SLen Brown static __inline__ void _set_bit(u32 * word, u32 mask, int value)
166b4f9fe12SLen Brown {
167b4f9fe12SLen Brown 	*word = (*word & ~mask) | (mask * value);
168b4f9fe12SLen Brown }
169b4f9fe12SLen Brown 
170b4f9fe12SLen Brown /* acpi interface wrappers
171b4f9fe12SLen Brown  */
172b4f9fe12SLen Brown 
173b4f9fe12SLen Brown static int is_valid_acpi_path(const char *methodName)
174b4f9fe12SLen Brown {
175b4f9fe12SLen Brown 	acpi_handle handle;
176b4f9fe12SLen Brown 	acpi_status status;
177b4f9fe12SLen Brown 
178b4f9fe12SLen Brown 	status = acpi_get_handle(NULL, (char *)methodName, &handle);
179b4f9fe12SLen Brown 	return !ACPI_FAILURE(status);
180b4f9fe12SLen Brown }
181b4f9fe12SLen Brown 
182b4f9fe12SLen Brown static int write_acpi_int(const char *methodName, int val)
183b4f9fe12SLen Brown {
184b4f9fe12SLen Brown 	struct acpi_object_list params;
185b4f9fe12SLen Brown 	union acpi_object in_objs[1];
186b4f9fe12SLen Brown 	acpi_status status;
187b4f9fe12SLen Brown 
188b4f9fe12SLen Brown 	params.count = ARRAY_SIZE(in_objs);
189b4f9fe12SLen Brown 	params.pointer = in_objs;
190b4f9fe12SLen Brown 	in_objs[0].type = ACPI_TYPE_INTEGER;
191b4f9fe12SLen Brown 	in_objs[0].integer.value = val;
192b4f9fe12SLen Brown 
193b4f9fe12SLen Brown 	status = acpi_evaluate_object(NULL, (char *)methodName, &params, NULL);
194*32bcd5cbSSeth Forshee 	return (status == AE_OK) ? 0 : -EIO;
195b4f9fe12SLen Brown }
196b4f9fe12SLen Brown 
197b4f9fe12SLen Brown /* Perform a raw HCI call.  Here we don't care about input or output buffer
198b4f9fe12SLen Brown  * format.
199b4f9fe12SLen Brown  */
200135740deSSeth Forshee static acpi_status hci_raw(struct toshiba_acpi_dev *dev,
201135740deSSeth Forshee 			   const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
202b4f9fe12SLen Brown {
203b4f9fe12SLen Brown 	struct acpi_object_list params;
204b4f9fe12SLen Brown 	union acpi_object in_objs[HCI_WORDS];
205b4f9fe12SLen Brown 	struct acpi_buffer results;
206b4f9fe12SLen Brown 	union acpi_object out_objs[HCI_WORDS + 1];
207b4f9fe12SLen Brown 	acpi_status status;
208b4f9fe12SLen Brown 	int i;
209b4f9fe12SLen Brown 
210b4f9fe12SLen Brown 	params.count = HCI_WORDS;
211b4f9fe12SLen Brown 	params.pointer = in_objs;
212b4f9fe12SLen Brown 	for (i = 0; i < HCI_WORDS; ++i) {
213b4f9fe12SLen Brown 		in_objs[i].type = ACPI_TYPE_INTEGER;
214b4f9fe12SLen Brown 		in_objs[i].integer.value = in[i];
215b4f9fe12SLen Brown 	}
216b4f9fe12SLen Brown 
217b4f9fe12SLen Brown 	results.length = sizeof(out_objs);
218b4f9fe12SLen Brown 	results.pointer = out_objs;
219b4f9fe12SLen Brown 
220135740deSSeth Forshee 	status = acpi_evaluate_object(NULL, (char *)dev->method_hci, &params,
221b4f9fe12SLen Brown 				      &results);
222b4f9fe12SLen Brown 	if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) {
223b4f9fe12SLen Brown 		for (i = 0; i < out_objs->package.count; ++i) {
224b4f9fe12SLen Brown 			out[i] = out_objs->package.elements[i].integer.value;
225b4f9fe12SLen Brown 		}
226b4f9fe12SLen Brown 	}
227b4f9fe12SLen Brown 
228b4f9fe12SLen Brown 	return status;
229b4f9fe12SLen Brown }
230b4f9fe12SLen Brown 
231b4f9fe12SLen Brown /* common hci tasks (get or set one or two value)
232b4f9fe12SLen Brown  *
233b4f9fe12SLen Brown  * In addition to the ACPI status, the HCI system returns a result which
234b4f9fe12SLen Brown  * may be useful (such as "not supported").
235b4f9fe12SLen Brown  */
236b4f9fe12SLen Brown 
237135740deSSeth Forshee static acpi_status hci_write1(struct toshiba_acpi_dev *dev, u32 reg,
238135740deSSeth Forshee 			      u32 in1, u32 *result)
239b4f9fe12SLen Brown {
240b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 };
241b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
242135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
243b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
244b4f9fe12SLen Brown 	return status;
245b4f9fe12SLen Brown }
246b4f9fe12SLen Brown 
247135740deSSeth Forshee static acpi_status hci_read1(struct toshiba_acpi_dev *dev, u32 reg,
248135740deSSeth Forshee 			     u32 *out1, u32 *result)
249b4f9fe12SLen Brown {
250b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 };
251b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
252135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
253b4f9fe12SLen Brown 	*out1 = out[2];
254b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
255b4f9fe12SLen Brown 	return status;
256b4f9fe12SLen Brown }
257b4f9fe12SLen Brown 
258135740deSSeth Forshee static acpi_status hci_write2(struct toshiba_acpi_dev *dev, u32 reg,
259135740deSSeth Forshee 			      u32 in1, u32 in2, u32 *result)
260b4f9fe12SLen Brown {
261b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 };
262b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
263135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
264b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
265b4f9fe12SLen Brown 	return status;
266b4f9fe12SLen Brown }
267b4f9fe12SLen Brown 
268135740deSSeth Forshee static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg,
269135740deSSeth Forshee 			     u32 *out1, u32 *out2, u32 *result)
270b4f9fe12SLen Brown {
271b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 };
272b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
273135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
274b4f9fe12SLen Brown 	*out1 = out[2];
275b4f9fe12SLen Brown 	*out2 = out[3];
276b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
277b4f9fe12SLen Brown 	return status;
278b4f9fe12SLen Brown }
279b4f9fe12SLen Brown 
2806c3f6e6cSPierre Ducroquet /* Illumination support */
281135740deSSeth Forshee static int toshiba_illumination_available(struct toshiba_acpi_dev *dev)
2826c3f6e6cSPierre Ducroquet {
2836c3f6e6cSPierre Ducroquet 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
2846c3f6e6cSPierre Ducroquet 	u32 out[HCI_WORDS];
2856c3f6e6cSPierre Ducroquet 	acpi_status status;
2866c3f6e6cSPierre Ducroquet 
2876c3f6e6cSPierre Ducroquet 	in[0] = 0xf100;
288135740deSSeth Forshee 	status = hci_raw(dev, in, out);
2896c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
2907e33460dSJoe Perches 		pr_info("Illumination device not available\n");
2916c3f6e6cSPierre Ducroquet 		return 0;
2926c3f6e6cSPierre Ducroquet 	}
2936c3f6e6cSPierre Ducroquet 	in[0] = 0xf400;
294135740deSSeth Forshee 	status = hci_raw(dev, in, out);
2956c3f6e6cSPierre Ducroquet 	return 1;
2966c3f6e6cSPierre Ducroquet }
2976c3f6e6cSPierre Ducroquet 
2986c3f6e6cSPierre Ducroquet static void toshiba_illumination_set(struct led_classdev *cdev,
2996c3f6e6cSPierre Ducroquet 				     enum led_brightness brightness)
3006c3f6e6cSPierre Ducroquet {
301135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = container_of(cdev,
302135740deSSeth Forshee 			struct toshiba_acpi_dev, led_dev);
3036c3f6e6cSPierre Ducroquet 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
3046c3f6e6cSPierre Ducroquet 	u32 out[HCI_WORDS];
3056c3f6e6cSPierre Ducroquet 	acpi_status status;
3066c3f6e6cSPierre Ducroquet 
3076c3f6e6cSPierre Ducroquet 	/* First request : initialize communication. */
3086c3f6e6cSPierre Ducroquet 	in[0] = 0xf100;
309135740deSSeth Forshee 	status = hci_raw(dev, in, out);
3106c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
3117e33460dSJoe Perches 		pr_info("Illumination device not available\n");
3126c3f6e6cSPierre Ducroquet 		return;
3136c3f6e6cSPierre Ducroquet 	}
3146c3f6e6cSPierre Ducroquet 
3156c3f6e6cSPierre Ducroquet 	if (brightness) {
3166c3f6e6cSPierre Ducroquet 		/* Switch the illumination on */
3176c3f6e6cSPierre Ducroquet 		in[0] = 0xf400;
3186c3f6e6cSPierre Ducroquet 		in[1] = 0x14e;
3196c3f6e6cSPierre Ducroquet 		in[2] = 1;
320135740deSSeth Forshee 		status = hci_raw(dev, in, out);
3216c3f6e6cSPierre Ducroquet 		if (ACPI_FAILURE(status)) {
3227e33460dSJoe Perches 			pr_info("ACPI call for illumination failed\n");
3236c3f6e6cSPierre Ducroquet 			return;
3246c3f6e6cSPierre Ducroquet 		}
3256c3f6e6cSPierre Ducroquet 	} else {
3266c3f6e6cSPierre Ducroquet 		/* Switch the illumination off */
3276c3f6e6cSPierre Ducroquet 		in[0] = 0xf400;
3286c3f6e6cSPierre Ducroquet 		in[1] = 0x14e;
3296c3f6e6cSPierre Ducroquet 		in[2] = 0;
330135740deSSeth Forshee 		status = hci_raw(dev, in, out);
3316c3f6e6cSPierre Ducroquet 		if (ACPI_FAILURE(status)) {
3327e33460dSJoe Perches 			pr_info("ACPI call for illumination failed.\n");
3336c3f6e6cSPierre Ducroquet 			return;
3346c3f6e6cSPierre Ducroquet 		}
3356c3f6e6cSPierre Ducroquet 	}
3366c3f6e6cSPierre Ducroquet 
3376c3f6e6cSPierre Ducroquet 	/* Last request : close communication. */
3386c3f6e6cSPierre Ducroquet 	in[0] = 0xf200;
3396c3f6e6cSPierre Ducroquet 	in[1] = 0;
3406c3f6e6cSPierre Ducroquet 	in[2] = 0;
341135740deSSeth Forshee 	hci_raw(dev, in, out);
3426c3f6e6cSPierre Ducroquet }
3436c3f6e6cSPierre Ducroquet 
3446c3f6e6cSPierre Ducroquet static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
3456c3f6e6cSPierre Ducroquet {
346135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = container_of(cdev,
347135740deSSeth Forshee 			struct toshiba_acpi_dev, led_dev);
3486c3f6e6cSPierre Ducroquet 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
3496c3f6e6cSPierre Ducroquet 	u32 out[HCI_WORDS];
3506c3f6e6cSPierre Ducroquet 	acpi_status status;
3516c3f6e6cSPierre Ducroquet 	enum led_brightness result;
3526c3f6e6cSPierre Ducroquet 
3536c3f6e6cSPierre Ducroquet 	/* First request : initialize communication. */
3546c3f6e6cSPierre Ducroquet 	in[0] = 0xf100;
355135740deSSeth Forshee 	status = hci_raw(dev, in, out);
3566c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
3577e33460dSJoe Perches 		pr_info("Illumination device not available\n");
3586c3f6e6cSPierre Ducroquet 		return LED_OFF;
3596c3f6e6cSPierre Ducroquet 	}
3606c3f6e6cSPierre Ducroquet 
3616c3f6e6cSPierre Ducroquet 	/* Check the illumination */
3626c3f6e6cSPierre Ducroquet 	in[0] = 0xf300;
3636c3f6e6cSPierre Ducroquet 	in[1] = 0x14e;
364135740deSSeth Forshee 	status = hci_raw(dev, in, out);
3656c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
3667e33460dSJoe Perches 		pr_info("ACPI call for illumination failed.\n");
3676c3f6e6cSPierre Ducroquet 		return LED_OFF;
3686c3f6e6cSPierre Ducroquet 	}
3696c3f6e6cSPierre Ducroquet 
3706c3f6e6cSPierre Ducroquet 	result = out[2] ? LED_FULL : LED_OFF;
3716c3f6e6cSPierre Ducroquet 
3726c3f6e6cSPierre Ducroquet 	/* Last request : close communication. */
3736c3f6e6cSPierre Ducroquet 	in[0] = 0xf200;
3746c3f6e6cSPierre Ducroquet 	in[1] = 0;
3756c3f6e6cSPierre Ducroquet 	in[2] = 0;
376135740deSSeth Forshee 	hci_raw(dev, in, out);
3776c3f6e6cSPierre Ducroquet 
3786c3f6e6cSPierre Ducroquet 	return result;
3796c3f6e6cSPierre Ducroquet }
3806c3f6e6cSPierre Ducroquet 
381b4f9fe12SLen Brown /* Bluetooth rfkill handlers */
382b4f9fe12SLen Brown 
383135740deSSeth Forshee static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present)
384b4f9fe12SLen Brown {
385b4f9fe12SLen Brown 	u32 hci_result;
386b4f9fe12SLen Brown 	u32 value, value2;
387b4f9fe12SLen Brown 
388b4f9fe12SLen Brown 	value = 0;
389b4f9fe12SLen Brown 	value2 = 0;
390135740deSSeth Forshee 	hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result);
391b4f9fe12SLen Brown 	if (hci_result == HCI_SUCCESS)
392b4f9fe12SLen Brown 		*present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false;
393b4f9fe12SLen Brown 
394b4f9fe12SLen Brown 	return hci_result;
395b4f9fe12SLen Brown }
396b4f9fe12SLen Brown 
397135740deSSeth Forshee static u32 hci_get_radio_state(struct toshiba_acpi_dev *dev, bool *radio_state)
398b4f9fe12SLen Brown {
399b4f9fe12SLen Brown 	u32 hci_result;
400b4f9fe12SLen Brown 	u32 value, value2;
401b4f9fe12SLen Brown 
402b4f9fe12SLen Brown 	value = 0;
403b4f9fe12SLen Brown 	value2 = 0x0001;
404135740deSSeth Forshee 	hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result);
405b4f9fe12SLen Brown 
406b4f9fe12SLen Brown 	*radio_state = value & HCI_WIRELESS_KILL_SWITCH;
407b4f9fe12SLen Brown 	return hci_result;
408b4f9fe12SLen Brown }
409b4f9fe12SLen Brown 
41019d337dfSJohannes Berg static int bt_rfkill_set_block(void *data, bool blocked)
411b4f9fe12SLen Brown {
41219d337dfSJohannes Berg 	struct toshiba_acpi_dev *dev = data;
413b4f9fe12SLen Brown 	u32 result1, result2;
414b4f9fe12SLen Brown 	u32 value;
41519d337dfSJohannes Berg 	int err;
416b4f9fe12SLen Brown 	bool radio_state;
417b4f9fe12SLen Brown 
41819d337dfSJohannes Berg 	value = (blocked == false);
419b4f9fe12SLen Brown 
420b4f9fe12SLen Brown 	mutex_lock(&dev->mutex);
421135740deSSeth Forshee 	if (hci_get_radio_state(dev, &radio_state) != HCI_SUCCESS) {
422*32bcd5cbSSeth Forshee 		err = -EIO;
42319d337dfSJohannes Berg 		goto out;
424b4f9fe12SLen Brown 	}
425b4f9fe12SLen Brown 
42619d337dfSJohannes Berg 	if (!radio_state) {
42719d337dfSJohannes Berg 		err = 0;
42819d337dfSJohannes Berg 		goto out;
42919d337dfSJohannes Berg 	}
43019d337dfSJohannes Berg 
431135740deSSeth Forshee 	hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
432135740deSSeth Forshee 	hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2);
43319d337dfSJohannes Berg 
43419d337dfSJohannes Berg 	if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
435*32bcd5cbSSeth Forshee 		err = -EIO;
43619d337dfSJohannes Berg 	else
43719d337dfSJohannes Berg 		err = 0;
43819d337dfSJohannes Berg  out:
43919d337dfSJohannes Berg 	mutex_unlock(&dev->mutex);
44019d337dfSJohannes Berg 	return err;
44119d337dfSJohannes Berg }
44219d337dfSJohannes Berg 
44319d337dfSJohannes Berg static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
444b4f9fe12SLen Brown {
445b4f9fe12SLen Brown 	bool new_rfk_state;
446b4f9fe12SLen Brown 	bool value;
447b4f9fe12SLen Brown 	u32 hci_result;
44819d337dfSJohannes Berg 	struct toshiba_acpi_dev *dev = data;
44919d337dfSJohannes Berg 
45019d337dfSJohannes Berg 	mutex_lock(&dev->mutex);
451b4f9fe12SLen Brown 
452135740deSSeth Forshee 	hci_result = hci_get_radio_state(dev, &value);
45319d337dfSJohannes Berg 	if (hci_result != HCI_SUCCESS) {
45419d337dfSJohannes Berg 		/* Can't do anything useful */
45519d337dfSJohannes Berg 		mutex_unlock(&dev->mutex);
45682e7784fSJiri Slaby 		return;
45719d337dfSJohannes Berg 	}
458b4f9fe12SLen Brown 
459b4f9fe12SLen Brown 	new_rfk_state = value;
460b4f9fe12SLen Brown 
461b4f9fe12SLen Brown 	mutex_unlock(&dev->mutex);
462b4f9fe12SLen Brown 
46319d337dfSJohannes Berg 	if (rfkill_set_hw_state(rfkill, !new_rfk_state))
46419d337dfSJohannes Berg 		bt_rfkill_set_block(data, true);
465b4f9fe12SLen Brown }
46619d337dfSJohannes Berg 
46719d337dfSJohannes Berg static const struct rfkill_ops toshiba_rfk_ops = {
46819d337dfSJohannes Berg 	.set_block = bt_rfkill_set_block,
46919d337dfSJohannes Berg 	.poll = bt_rfkill_poll,
47019d337dfSJohannes Berg };
471b4f9fe12SLen Brown 
472b4f9fe12SLen Brown static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
473b4f9fe12SLen Brown 
474b4f9fe12SLen Brown static int get_lcd(struct backlight_device *bd)
475b4f9fe12SLen Brown {
476135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = bl_get_data(bd);
477b4f9fe12SLen Brown 	u32 hci_result;
478b4f9fe12SLen Brown 	u32 value;
479b4f9fe12SLen Brown 
480135740deSSeth Forshee 	hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result);
481*32bcd5cbSSeth Forshee 	if (hci_result == HCI_SUCCESS)
482b4f9fe12SLen Brown 		return (value >> HCI_LCD_BRIGHTNESS_SHIFT);
483*32bcd5cbSSeth Forshee 
484*32bcd5cbSSeth Forshee 	return -EIO;
485b4f9fe12SLen Brown }
486b4f9fe12SLen Brown 
487936c8bcdSAlexey Dobriyan static int lcd_proc_show(struct seq_file *m, void *v)
488b4f9fe12SLen Brown {
489135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
490135740deSSeth Forshee 	int value;
491b4f9fe12SLen Brown 
492135740deSSeth Forshee 	if (!dev->backlight_dev)
493135740deSSeth Forshee 		return -ENODEV;
494135740deSSeth Forshee 
495135740deSSeth Forshee 	value = get_lcd(dev->backlight_dev);
496b4f9fe12SLen Brown 	if (value >= 0) {
497936c8bcdSAlexey Dobriyan 		seq_printf(m, "brightness:              %d\n", value);
498936c8bcdSAlexey Dobriyan 		seq_printf(m, "brightness_levels:       %d\n",
499b4f9fe12SLen Brown 			     HCI_LCD_BRIGHTNESS_LEVELS);
500*32bcd5cbSSeth Forshee 		return 0;
501b4f9fe12SLen Brown 	}
502b4f9fe12SLen Brown 
503*32bcd5cbSSeth Forshee 	pr_err("Error reading LCD brightness\n");
504*32bcd5cbSSeth Forshee 	return -EIO;
505936c8bcdSAlexey Dobriyan }
506936c8bcdSAlexey Dobriyan 
507936c8bcdSAlexey Dobriyan static int lcd_proc_open(struct inode *inode, struct file *file)
508936c8bcdSAlexey Dobriyan {
509135740deSSeth Forshee 	return single_open(file, lcd_proc_show, PDE(inode)->data);
510b4f9fe12SLen Brown }
511b4f9fe12SLen Brown 
512135740deSSeth Forshee static int set_lcd(struct toshiba_acpi_dev *dev, int value)
513b4f9fe12SLen Brown {
514b4f9fe12SLen Brown 	u32 hci_result;
515b4f9fe12SLen Brown 
516b4f9fe12SLen Brown 	value = value << HCI_LCD_BRIGHTNESS_SHIFT;
517135740deSSeth Forshee 	hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result);
518*32bcd5cbSSeth Forshee 	return hci_result == HCI_SUCCESS ? 0 : -EIO;
519b4f9fe12SLen Brown }
520b4f9fe12SLen Brown 
521b4f9fe12SLen Brown static int set_lcd_status(struct backlight_device *bd)
522b4f9fe12SLen Brown {
523135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = bl_get_data(bd);
524135740deSSeth Forshee 	return set_lcd(dev, bd->props.brightness);
525b4f9fe12SLen Brown }
526b4f9fe12SLen Brown 
527936c8bcdSAlexey Dobriyan static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
528936c8bcdSAlexey Dobriyan 			      size_t count, loff_t *pos)
529b4f9fe12SLen Brown {
530135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
531936c8bcdSAlexey Dobriyan 	char cmd[42];
532936c8bcdSAlexey Dobriyan 	size_t len;
533b4f9fe12SLen Brown 	int value;
534b4f9fe12SLen Brown 	int ret;
535b4f9fe12SLen Brown 
536936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
537936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
538936c8bcdSAlexey Dobriyan 		return -EFAULT;
539936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
540936c8bcdSAlexey Dobriyan 
541936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " brightness : %i", &value) == 1 &&
542b4f9fe12SLen Brown 	    value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) {
543135740deSSeth Forshee 		ret = set_lcd(dev, value);
544b4f9fe12SLen Brown 		if (ret == 0)
545b4f9fe12SLen Brown 			ret = count;
546b4f9fe12SLen Brown 	} else {
547b4f9fe12SLen Brown 		ret = -EINVAL;
548b4f9fe12SLen Brown 	}
549b4f9fe12SLen Brown 	return ret;
550b4f9fe12SLen Brown }
551b4f9fe12SLen Brown 
552936c8bcdSAlexey Dobriyan static const struct file_operations lcd_proc_fops = {
553936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
554936c8bcdSAlexey Dobriyan 	.open		= lcd_proc_open,
555936c8bcdSAlexey Dobriyan 	.read		= seq_read,
556936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
557936c8bcdSAlexey Dobriyan 	.release	= single_release,
558936c8bcdSAlexey Dobriyan 	.write		= lcd_proc_write,
559936c8bcdSAlexey Dobriyan };
560936c8bcdSAlexey Dobriyan 
561936c8bcdSAlexey Dobriyan static int video_proc_show(struct seq_file *m, void *v)
562b4f9fe12SLen Brown {
563135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
564b4f9fe12SLen Brown 	u32 hci_result;
565b4f9fe12SLen Brown 	u32 value;
566b4f9fe12SLen Brown 
567135740deSSeth Forshee 	hci_read1(dev, HCI_VIDEO_OUT, &value, &hci_result);
568b4f9fe12SLen Brown 	if (hci_result == HCI_SUCCESS) {
569b4f9fe12SLen Brown 		int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
570b4f9fe12SLen Brown 		int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
571b4f9fe12SLen Brown 		int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
572936c8bcdSAlexey Dobriyan 		seq_printf(m, "lcd_out:                 %d\n", is_lcd);
573936c8bcdSAlexey Dobriyan 		seq_printf(m, "crt_out:                 %d\n", is_crt);
574936c8bcdSAlexey Dobriyan 		seq_printf(m, "tv_out:                  %d\n", is_tv);
575*32bcd5cbSSeth Forshee 		return 0;
576b4f9fe12SLen Brown 	}
577b4f9fe12SLen Brown 
578*32bcd5cbSSeth Forshee 	return -EIO;
579b4f9fe12SLen Brown }
580b4f9fe12SLen Brown 
581936c8bcdSAlexey Dobriyan static int video_proc_open(struct inode *inode, struct file *file)
582b4f9fe12SLen Brown {
583135740deSSeth Forshee 	return single_open(file, video_proc_show, PDE(inode)->data);
584936c8bcdSAlexey Dobriyan }
585936c8bcdSAlexey Dobriyan 
586936c8bcdSAlexey Dobriyan static ssize_t video_proc_write(struct file *file, const char __user *buf,
587936c8bcdSAlexey Dobriyan 				size_t count, loff_t *pos)
588936c8bcdSAlexey Dobriyan {
589135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
590936c8bcdSAlexey Dobriyan 	char *cmd, *buffer;
591*32bcd5cbSSeth Forshee 	int ret = 0;
592b4f9fe12SLen Brown 	int value;
593b4f9fe12SLen Brown 	int remain = count;
594b4f9fe12SLen Brown 	int lcd_out = -1;
595b4f9fe12SLen Brown 	int crt_out = -1;
596b4f9fe12SLen Brown 	int tv_out = -1;
597b4f9fe12SLen Brown 	u32 hci_result;
598b4f9fe12SLen Brown 	u32 video_out;
599b4f9fe12SLen Brown 
600936c8bcdSAlexey Dobriyan 	cmd = kmalloc(count + 1, GFP_KERNEL);
601936c8bcdSAlexey Dobriyan 	if (!cmd)
602936c8bcdSAlexey Dobriyan 		return -ENOMEM;
603936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, count)) {
604936c8bcdSAlexey Dobriyan 		kfree(cmd);
605936c8bcdSAlexey Dobriyan 		return -EFAULT;
606936c8bcdSAlexey Dobriyan 	}
607936c8bcdSAlexey Dobriyan 	cmd[count] = '\0';
608936c8bcdSAlexey Dobriyan 
609936c8bcdSAlexey Dobriyan 	buffer = cmd;
610936c8bcdSAlexey Dobriyan 
611b4f9fe12SLen Brown 	/* scan expression.  Multiple expressions may be delimited with ;
612b4f9fe12SLen Brown 	 *
613b4f9fe12SLen Brown 	 *  NOTE: to keep scanning simple, invalid fields are ignored
614b4f9fe12SLen Brown 	 */
615b4f9fe12SLen Brown 	while (remain) {
616b4f9fe12SLen Brown 		if (sscanf(buffer, " lcd_out : %i", &value) == 1)
617b4f9fe12SLen Brown 			lcd_out = value & 1;
618b4f9fe12SLen Brown 		else if (sscanf(buffer, " crt_out : %i", &value) == 1)
619b4f9fe12SLen Brown 			crt_out = value & 1;
620b4f9fe12SLen Brown 		else if (sscanf(buffer, " tv_out : %i", &value) == 1)
621b4f9fe12SLen Brown 			tv_out = value & 1;
622b4f9fe12SLen Brown 		/* advance to one character past the next ; */
623b4f9fe12SLen Brown 		do {
624b4f9fe12SLen Brown 			++buffer;
625b4f9fe12SLen Brown 			--remain;
626b4f9fe12SLen Brown 		}
627b4f9fe12SLen Brown 		while (remain && *(buffer - 1) != ';');
628b4f9fe12SLen Brown 	}
629b4f9fe12SLen Brown 
630936c8bcdSAlexey Dobriyan 	kfree(cmd);
631936c8bcdSAlexey Dobriyan 
632135740deSSeth Forshee 	hci_read1(dev, HCI_VIDEO_OUT, &video_out, &hci_result);
633b4f9fe12SLen Brown 	if (hci_result == HCI_SUCCESS) {
634b4f9fe12SLen Brown 		unsigned int new_video_out = video_out;
635b4f9fe12SLen Brown 		if (lcd_out != -1)
636b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out);
637b4f9fe12SLen Brown 		if (crt_out != -1)
638b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out);
639b4f9fe12SLen Brown 		if (tv_out != -1)
640b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out);
641b4f9fe12SLen Brown 		/* To avoid unnecessary video disruption, only write the new
642b4f9fe12SLen Brown 		 * video setting if something changed. */
643b4f9fe12SLen Brown 		if (new_video_out != video_out)
644*32bcd5cbSSeth Forshee 			ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out);
645b4f9fe12SLen Brown 	} else {
646*32bcd5cbSSeth Forshee 		ret = -EIO;
647b4f9fe12SLen Brown 	}
648b4f9fe12SLen Brown 
649*32bcd5cbSSeth Forshee 	return ret ? ret : count;
650b4f9fe12SLen Brown }
651b4f9fe12SLen Brown 
652936c8bcdSAlexey Dobriyan static const struct file_operations video_proc_fops = {
653936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
654936c8bcdSAlexey Dobriyan 	.open		= video_proc_open,
655936c8bcdSAlexey Dobriyan 	.read		= seq_read,
656936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
657936c8bcdSAlexey Dobriyan 	.release	= single_release,
658936c8bcdSAlexey Dobriyan 	.write		= video_proc_write,
659936c8bcdSAlexey Dobriyan };
660936c8bcdSAlexey Dobriyan 
661936c8bcdSAlexey Dobriyan static int fan_proc_show(struct seq_file *m, void *v)
662b4f9fe12SLen Brown {
663135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
664b4f9fe12SLen Brown 	u32 hci_result;
665b4f9fe12SLen Brown 	u32 value;
666b4f9fe12SLen Brown 
667135740deSSeth Forshee 	hci_read1(dev, HCI_FAN, &value, &hci_result);
668b4f9fe12SLen Brown 	if (hci_result == HCI_SUCCESS) {
669936c8bcdSAlexey Dobriyan 		seq_printf(m, "running:                 %d\n", (value > 0));
670135740deSSeth Forshee 		seq_printf(m, "force_on:                %d\n", dev->force_fan);
671*32bcd5cbSSeth Forshee 		return 0;
672b4f9fe12SLen Brown 	}
673b4f9fe12SLen Brown 
674*32bcd5cbSSeth Forshee 	return -EIO;
675b4f9fe12SLen Brown }
676b4f9fe12SLen Brown 
677936c8bcdSAlexey Dobriyan static int fan_proc_open(struct inode *inode, struct file *file)
678b4f9fe12SLen Brown {
679135740deSSeth Forshee 	return single_open(file, fan_proc_show, PDE(inode)->data);
680936c8bcdSAlexey Dobriyan }
681936c8bcdSAlexey Dobriyan 
682936c8bcdSAlexey Dobriyan static ssize_t fan_proc_write(struct file *file, const char __user *buf,
683936c8bcdSAlexey Dobriyan 			      size_t count, loff_t *pos)
684936c8bcdSAlexey Dobriyan {
685135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
686936c8bcdSAlexey Dobriyan 	char cmd[42];
687936c8bcdSAlexey Dobriyan 	size_t len;
688b4f9fe12SLen Brown 	int value;
689b4f9fe12SLen Brown 	u32 hci_result;
690b4f9fe12SLen Brown 
691936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
692936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
693936c8bcdSAlexey Dobriyan 		return -EFAULT;
694936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
695936c8bcdSAlexey Dobriyan 
696936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " force_on : %i", &value) == 1 &&
697b4f9fe12SLen Brown 	    value >= 0 && value <= 1) {
698135740deSSeth Forshee 		hci_write1(dev, HCI_FAN, value, &hci_result);
699b4f9fe12SLen Brown 		if (hci_result != HCI_SUCCESS)
700*32bcd5cbSSeth Forshee 			return -EIO;
701b4f9fe12SLen Brown 		else
702135740deSSeth Forshee 			dev->force_fan = value;
703b4f9fe12SLen Brown 	} else {
704b4f9fe12SLen Brown 		return -EINVAL;
705b4f9fe12SLen Brown 	}
706b4f9fe12SLen Brown 
707b4f9fe12SLen Brown 	return count;
708b4f9fe12SLen Brown }
709b4f9fe12SLen Brown 
710936c8bcdSAlexey Dobriyan static const struct file_operations fan_proc_fops = {
711936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
712936c8bcdSAlexey Dobriyan 	.open		= fan_proc_open,
713936c8bcdSAlexey Dobriyan 	.read		= seq_read,
714936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
715936c8bcdSAlexey Dobriyan 	.release	= single_release,
716936c8bcdSAlexey Dobriyan 	.write		= fan_proc_write,
717936c8bcdSAlexey Dobriyan };
718936c8bcdSAlexey Dobriyan 
719936c8bcdSAlexey Dobriyan static int keys_proc_show(struct seq_file *m, void *v)
720b4f9fe12SLen Brown {
721135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
722b4f9fe12SLen Brown 	u32 hci_result;
723b4f9fe12SLen Brown 	u32 value;
724b4f9fe12SLen Brown 
725135740deSSeth Forshee 	if (!dev->key_event_valid) {
726135740deSSeth Forshee 		hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
727b4f9fe12SLen Brown 		if (hci_result == HCI_SUCCESS) {
728135740deSSeth Forshee 			dev->key_event_valid = 1;
729135740deSSeth Forshee 			dev->last_key_event = value;
730b4f9fe12SLen Brown 		} else if (hci_result == HCI_EMPTY) {
731b4f9fe12SLen Brown 			/* better luck next time */
732b4f9fe12SLen Brown 		} else if (hci_result == HCI_NOT_SUPPORTED) {
733b4f9fe12SLen Brown 			/* This is a workaround for an unresolved issue on
734b4f9fe12SLen Brown 			 * some machines where system events sporadically
735b4f9fe12SLen Brown 			 * become disabled. */
736135740deSSeth Forshee 			hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
7377e33460dSJoe Perches 			pr_notice("Re-enabled hotkeys\n");
738b4f9fe12SLen Brown 		} else {
7397e33460dSJoe Perches 			pr_err("Error reading hotkey status\n");
740*32bcd5cbSSeth Forshee 			return -EIO;
741b4f9fe12SLen Brown 		}
742b4f9fe12SLen Brown 	}
743b4f9fe12SLen Brown 
744135740deSSeth Forshee 	seq_printf(m, "hotkey_ready:            %d\n", dev->key_event_valid);
745135740deSSeth Forshee 	seq_printf(m, "hotkey:                  0x%04x\n", dev->last_key_event);
746936c8bcdSAlexey Dobriyan 	return 0;
747b4f9fe12SLen Brown }
748b4f9fe12SLen Brown 
749936c8bcdSAlexey Dobriyan static int keys_proc_open(struct inode *inode, struct file *file)
750b4f9fe12SLen Brown {
751135740deSSeth Forshee 	return single_open(file, keys_proc_show, PDE(inode)->data);
752936c8bcdSAlexey Dobriyan }
753936c8bcdSAlexey Dobriyan 
754936c8bcdSAlexey Dobriyan static ssize_t keys_proc_write(struct file *file, const char __user *buf,
755936c8bcdSAlexey Dobriyan 			       size_t count, loff_t *pos)
756936c8bcdSAlexey Dobriyan {
757135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
758936c8bcdSAlexey Dobriyan 	char cmd[42];
759936c8bcdSAlexey Dobriyan 	size_t len;
760b4f9fe12SLen Brown 	int value;
761b4f9fe12SLen Brown 
762936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
763936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
764936c8bcdSAlexey Dobriyan 		return -EFAULT;
765936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
766936c8bcdSAlexey Dobriyan 
767936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) {
768135740deSSeth Forshee 		dev->key_event_valid = 0;
769b4f9fe12SLen Brown 	} else {
770b4f9fe12SLen Brown 		return -EINVAL;
771b4f9fe12SLen Brown 	}
772b4f9fe12SLen Brown 
773b4f9fe12SLen Brown 	return count;
774b4f9fe12SLen Brown }
775b4f9fe12SLen Brown 
776936c8bcdSAlexey Dobriyan static const struct file_operations keys_proc_fops = {
777936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
778936c8bcdSAlexey Dobriyan 	.open		= keys_proc_open,
779936c8bcdSAlexey Dobriyan 	.read		= seq_read,
780936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
781936c8bcdSAlexey Dobriyan 	.release	= single_release,
782936c8bcdSAlexey Dobriyan 	.write		= keys_proc_write,
783936c8bcdSAlexey Dobriyan };
784936c8bcdSAlexey Dobriyan 
785936c8bcdSAlexey Dobriyan static int version_proc_show(struct seq_file *m, void *v)
786b4f9fe12SLen Brown {
787936c8bcdSAlexey Dobriyan 	seq_printf(m, "driver:                  %s\n", TOSHIBA_ACPI_VERSION);
788936c8bcdSAlexey Dobriyan 	seq_printf(m, "proc_interface:          %d\n", PROC_INTERFACE_VERSION);
789936c8bcdSAlexey Dobriyan 	return 0;
790b4f9fe12SLen Brown }
791b4f9fe12SLen Brown 
792936c8bcdSAlexey Dobriyan static int version_proc_open(struct inode *inode, struct file *file)
793936c8bcdSAlexey Dobriyan {
794936c8bcdSAlexey Dobriyan 	return single_open(file, version_proc_show, PDE(inode)->data);
795936c8bcdSAlexey Dobriyan }
796936c8bcdSAlexey Dobriyan 
797936c8bcdSAlexey Dobriyan static const struct file_operations version_proc_fops = {
798936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
799936c8bcdSAlexey Dobriyan 	.open		= version_proc_open,
800936c8bcdSAlexey Dobriyan 	.read		= seq_read,
801936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
802936c8bcdSAlexey Dobriyan 	.release	= single_release,
803936c8bcdSAlexey Dobriyan };
804936c8bcdSAlexey Dobriyan 
805b4f9fe12SLen Brown /* proc and module init
806b4f9fe12SLen Brown  */
807b4f9fe12SLen Brown 
808b4f9fe12SLen Brown #define PROC_TOSHIBA		"toshiba"
809b4f9fe12SLen Brown 
810135740deSSeth Forshee static void __devinit
811135740deSSeth Forshee create_toshiba_proc_entries(struct toshiba_acpi_dev *dev)
812b4f9fe12SLen Brown {
813135740deSSeth Forshee 	proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir,
814135740deSSeth Forshee 			 &lcd_proc_fops, dev);
815135740deSSeth Forshee 	proc_create_data("video", S_IRUGO | S_IWUSR, toshiba_proc_dir,
816135740deSSeth Forshee 			 &video_proc_fops, dev);
817135740deSSeth Forshee 	proc_create_data("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir,
818135740deSSeth Forshee 			 &fan_proc_fops, dev);
819135740deSSeth Forshee 	proc_create_data("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir,
820135740deSSeth Forshee 			 &keys_proc_fops, dev);
821135740deSSeth Forshee 	proc_create_data("version", S_IRUGO, toshiba_proc_dir,
822135740deSSeth Forshee 			 &version_proc_fops, dev);
823b4f9fe12SLen Brown }
824b4f9fe12SLen Brown 
825f8ef3aecSAxel Lin static void remove_toshiba_proc_entries(void)
826b4f9fe12SLen Brown {
827936c8bcdSAlexey Dobriyan 	remove_proc_entry("lcd", toshiba_proc_dir);
828936c8bcdSAlexey Dobriyan 	remove_proc_entry("video", toshiba_proc_dir);
829936c8bcdSAlexey Dobriyan 	remove_proc_entry("fan", toshiba_proc_dir);
830936c8bcdSAlexey Dobriyan 	remove_proc_entry("keys", toshiba_proc_dir);
831936c8bcdSAlexey Dobriyan 	remove_proc_entry("version", toshiba_proc_dir);
832b4f9fe12SLen Brown }
833b4f9fe12SLen Brown 
834acc2472eSLionel Debroux static const struct backlight_ops toshiba_backlight_data = {
835b4f9fe12SLen Brown         .get_brightness = get_lcd,
836b4f9fe12SLen Brown         .update_status  = set_lcd_status,
837b4f9fe12SLen Brown };
838b4f9fe12SLen Brown 
839135740deSSeth Forshee static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev,
840135740deSSeth Forshee 						 char *device_path)
8416335e4d5SMatthew Garrett {
842135740deSSeth Forshee 	acpi_status status;
843135740deSSeth Forshee 	int error;
844135740deSSeth Forshee 
845135740deSSeth Forshee 	status = acpi_get_handle(NULL, device_path, &dev->handle);
846135740deSSeth Forshee 	if (ACPI_FAILURE(status)) {
847135740deSSeth Forshee 		pr_info("Unable to get notification device\n");
848135740deSSeth Forshee 		return -ENODEV;
849135740deSSeth Forshee 	}
850135740deSSeth Forshee 
851135740deSSeth Forshee 	dev->hotkey_dev = input_allocate_device();
852135740deSSeth Forshee 	if (!dev->hotkey_dev) {
853135740deSSeth Forshee 		pr_info("Unable to register input device\n");
854135740deSSeth Forshee 		return -ENOMEM;
855135740deSSeth Forshee 	}
856135740deSSeth Forshee 
857135740deSSeth Forshee 	dev->hotkey_dev->name = "Toshiba input device";
858135740deSSeth Forshee 	dev->hotkey_dev->phys = device_path;
859135740deSSeth Forshee 	dev->hotkey_dev->id.bustype = BUS_HOST;
860135740deSSeth Forshee 
861135740deSSeth Forshee 	error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL);
862135740deSSeth Forshee 	if (error)
863135740deSSeth Forshee 		goto err_free_dev;
864135740deSSeth Forshee 
865135740deSSeth Forshee 	status = acpi_evaluate_object(dev->handle, "ENAB", NULL, NULL);
866135740deSSeth Forshee 	if (ACPI_FAILURE(status)) {
867135740deSSeth Forshee 		pr_info("Unable to enable hotkeys\n");
868135740deSSeth Forshee 		error = -ENODEV;
869135740deSSeth Forshee 		goto err_free_keymap;
870135740deSSeth Forshee 	}
871135740deSSeth Forshee 
872135740deSSeth Forshee 	error = input_register_device(dev->hotkey_dev);
873135740deSSeth Forshee 	if (error) {
874135740deSSeth Forshee 		pr_info("Unable to register input device\n");
875135740deSSeth Forshee 		goto err_free_keymap;
876135740deSSeth Forshee 	}
877135740deSSeth Forshee 
878135740deSSeth Forshee 	return 0;
879135740deSSeth Forshee 
880135740deSSeth Forshee  err_free_keymap:
881135740deSSeth Forshee 	sparse_keymap_free(dev->hotkey_dev);
882135740deSSeth Forshee  err_free_dev:
883135740deSSeth Forshee 	input_free_device(dev->hotkey_dev);
884135740deSSeth Forshee 	dev->hotkey_dev = NULL;
885135740deSSeth Forshee 	return error;
886135740deSSeth Forshee }
887135740deSSeth Forshee 
888135740deSSeth Forshee static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type)
889135740deSSeth Forshee {
890135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
891135740deSSeth Forshee 
892135740deSSeth Forshee 	remove_toshiba_proc_entries();
893135740deSSeth Forshee 
894135740deSSeth Forshee 	if (dev->hotkey_dev) {
895135740deSSeth Forshee 		input_unregister_device(dev->hotkey_dev);
896135740deSSeth Forshee 		sparse_keymap_free(dev->hotkey_dev);
897135740deSSeth Forshee 	}
898135740deSSeth Forshee 
899135740deSSeth Forshee 	if (dev->bt_rfk) {
900135740deSSeth Forshee 		rfkill_unregister(dev->bt_rfk);
901135740deSSeth Forshee 		rfkill_destroy(dev->bt_rfk);
902135740deSSeth Forshee 	}
903135740deSSeth Forshee 
904135740deSSeth Forshee 	if (dev->backlight_dev)
905135740deSSeth Forshee 		backlight_device_unregister(dev->backlight_dev);
906135740deSSeth Forshee 
907135740deSSeth Forshee 	if (dev->illumination_installed)
908135740deSSeth Forshee 		led_classdev_unregister(&dev->led_dev);
909135740deSSeth Forshee 
910135740deSSeth Forshee 	kfree(dev);
911135740deSSeth Forshee 
912135740deSSeth Forshee 	return 0;
913135740deSSeth Forshee }
914135740deSSeth Forshee 
915135740deSSeth Forshee static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
916135740deSSeth Forshee {
917135740deSSeth Forshee 	struct toshiba_acpi_dev *dev;
918135740deSSeth Forshee 	u32 hci_result;
919135740deSSeth Forshee 	bool bt_present;
920135740deSSeth Forshee 	int ret = 0;
921135740deSSeth Forshee 	struct backlight_properties props;
922135740deSSeth Forshee 
923135740deSSeth Forshee 	pr_info("Toshiba Laptop ACPI Extras version %s\n",
924135740deSSeth Forshee 	       TOSHIBA_ACPI_VERSION);
925135740deSSeth Forshee 
926135740deSSeth Forshee 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
927135740deSSeth Forshee 	if (!dev)
928135740deSSeth Forshee 		return -ENOMEM;
929135740deSSeth Forshee 	dev->acpi_dev = acpi_dev;
930135740deSSeth Forshee 	acpi_dev->driver_data = dev;
931135740deSSeth Forshee 
932135740deSSeth Forshee 	/* simple device detection: look for HCI method */
933135740deSSeth Forshee 	if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) {
934135740deSSeth Forshee 		dev->method_hci = TOSH_INTERFACE_1 GHCI_METHOD;
935135740deSSeth Forshee 		if (toshiba_acpi_setup_keyboard(dev, TOSH_INTERFACE_1))
936135740deSSeth Forshee 			pr_info("Unable to activate hotkeys\n");
937135740deSSeth Forshee 	} else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) {
938135740deSSeth Forshee 		dev->method_hci = TOSH_INTERFACE_2 GHCI_METHOD;
939135740deSSeth Forshee 		if (toshiba_acpi_setup_keyboard(dev, TOSH_INTERFACE_2))
940135740deSSeth Forshee 			pr_info("Unable to activate hotkeys\n");
941135740deSSeth Forshee 	} else {
942135740deSSeth Forshee 		ret = -ENODEV;
943135740deSSeth Forshee 		goto error;
944135740deSSeth Forshee 	}
945135740deSSeth Forshee 
946135740deSSeth Forshee 	pr_info("HCI method: %s\n", dev->method_hci);
947135740deSSeth Forshee 
948135740deSSeth Forshee 	mutex_init(&dev->mutex);
949135740deSSeth Forshee 
950135740deSSeth Forshee 	/* enable event fifo */
951135740deSSeth Forshee 	hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
952135740deSSeth Forshee 
953135740deSSeth Forshee 	create_toshiba_proc_entries(dev);
954135740deSSeth Forshee 
955135740deSSeth Forshee 	props.type = BACKLIGHT_PLATFORM;
956135740deSSeth Forshee 	props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
957135740deSSeth Forshee 	dev->backlight_dev = backlight_device_register("toshiba",
958135740deSSeth Forshee 						       &acpi_dev->dev,
959135740deSSeth Forshee 						       dev,
960135740deSSeth Forshee 						       &toshiba_backlight_data,
961135740deSSeth Forshee 						       &props);
962135740deSSeth Forshee 	if (IS_ERR(dev->backlight_dev)) {
963135740deSSeth Forshee 		ret = PTR_ERR(dev->backlight_dev);
964135740deSSeth Forshee 
965135740deSSeth Forshee 		pr_err("Could not register toshiba backlight device\n");
966135740deSSeth Forshee 		dev->backlight_dev = NULL;
967135740deSSeth Forshee 		goto error;
968135740deSSeth Forshee 	}
969135740deSSeth Forshee 
970135740deSSeth Forshee 	/* Register rfkill switch for Bluetooth */
971135740deSSeth Forshee 	if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) {
972135740deSSeth Forshee 		dev->bt_rfk = rfkill_alloc("Toshiba Bluetooth",
973135740deSSeth Forshee 					   &acpi_dev->dev,
974135740deSSeth Forshee 					   RFKILL_TYPE_BLUETOOTH,
975135740deSSeth Forshee 					   &toshiba_rfk_ops,
976135740deSSeth Forshee 					   dev);
977135740deSSeth Forshee 		if (!dev->bt_rfk) {
978135740deSSeth Forshee 			pr_err("unable to allocate rfkill device\n");
979135740deSSeth Forshee 			ret = -ENOMEM;
980135740deSSeth Forshee 			goto error;
981135740deSSeth Forshee 		}
982135740deSSeth Forshee 
983135740deSSeth Forshee 		ret = rfkill_register(dev->bt_rfk);
984135740deSSeth Forshee 		if (ret) {
985135740deSSeth Forshee 			pr_err("unable to register rfkill device\n");
986135740deSSeth Forshee 			rfkill_destroy(dev->bt_rfk);
987135740deSSeth Forshee 			goto error;
988135740deSSeth Forshee 		}
989135740deSSeth Forshee 	}
990135740deSSeth Forshee 
991135740deSSeth Forshee 	if (toshiba_illumination_available(dev)) {
992135740deSSeth Forshee 		dev->led_dev.name = "toshiba::illumination";
993135740deSSeth Forshee 		dev->led_dev.max_brightness = 1;
994135740deSSeth Forshee 		dev->led_dev.brightness_set = toshiba_illumination_set;
995135740deSSeth Forshee 		dev->led_dev.brightness_get = toshiba_illumination_get;
996135740deSSeth Forshee 		if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev))
997135740deSSeth Forshee 			dev->illumination_installed = 1;
998135740deSSeth Forshee 	}
999135740deSSeth Forshee 
1000135740deSSeth Forshee 	return 0;
1001135740deSSeth Forshee 
1002135740deSSeth Forshee error:
1003135740deSSeth Forshee 	toshiba_acpi_remove(acpi_dev, 0);
1004135740deSSeth Forshee 	return ret;
1005135740deSSeth Forshee }
1006135740deSSeth Forshee 
1007135740deSSeth Forshee static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
1008135740deSSeth Forshee {
1009135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
10106335e4d5SMatthew Garrett 	u32 hci_result, value;
10116335e4d5SMatthew Garrett 
10126335e4d5SMatthew Garrett 	if (event != 0x80)
10136335e4d5SMatthew Garrett 		return;
10146335e4d5SMatthew Garrett 	do {
1015135740deSSeth Forshee 		hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
10166335e4d5SMatthew Garrett 		if (hci_result == HCI_SUCCESS) {
10176335e4d5SMatthew Garrett 			if (value == 0x100)
10186335e4d5SMatthew Garrett 				continue;
1019b466301bSFrans Pop 			/* act on key press; ignore key release */
1020b466301bSFrans Pop 			if (value & 0x80)
1021b466301bSFrans Pop 				continue;
1022b466301bSFrans Pop 
1023135740deSSeth Forshee 			if (!sparse_keymap_report_event(dev->hotkey_dev,
1024384a7cd9SDmitry Torokhov 							value, 1, true)) {
10257e33460dSJoe Perches 				pr_info("Unknown key %x\n",
1026b466301bSFrans Pop 				       value);
10276335e4d5SMatthew Garrett 			}
10286335e4d5SMatthew Garrett 		} else if (hci_result == HCI_NOT_SUPPORTED) {
10296335e4d5SMatthew Garrett 			/* This is a workaround for an unresolved issue on
10306335e4d5SMatthew Garrett 			 * some machines where system events sporadically
10316335e4d5SMatthew Garrett 			 * become disabled. */
1032135740deSSeth Forshee 			hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
10337e33460dSJoe Perches 			pr_notice("Re-enabled hotkeys\n");
10346335e4d5SMatthew Garrett 		}
10356335e4d5SMatthew Garrett 	} while (hci_result != HCI_EMPTY);
10366335e4d5SMatthew Garrett }
10376335e4d5SMatthew Garrett 
10386335e4d5SMatthew Garrett 
1039135740deSSeth Forshee static struct acpi_driver toshiba_acpi_driver = {
1040135740deSSeth Forshee 	.name	= "Toshiba ACPI driver",
1041135740deSSeth Forshee 	.owner	= THIS_MODULE,
1042135740deSSeth Forshee 	.ids	= toshiba_device_ids,
1043135740deSSeth Forshee 	.flags	= ACPI_DRIVER_ALL_NOTIFY_EVENTS,
1044135740deSSeth Forshee 	.ops	= {
1045135740deSSeth Forshee 		.add		= toshiba_acpi_add,
1046135740deSSeth Forshee 		.remove		= toshiba_acpi_remove,
1047135740deSSeth Forshee 		.notify		= toshiba_acpi_notify,
1048135740deSSeth Forshee 	},
1049135740deSSeth Forshee };
1050b4f9fe12SLen Brown 
1051b4f9fe12SLen Brown static int __init toshiba_acpi_init(void)
1052b4f9fe12SLen Brown {
1053135740deSSeth Forshee 	int ret;
1054b4f9fe12SLen Brown 
1055b4f9fe12SLen Brown 	toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
1056b4f9fe12SLen Brown 	if (!toshiba_proc_dir) {
1057135740deSSeth Forshee 		pr_err("Unable to create proc dir " PROC_TOSHIBA "\n");
1058b4f9fe12SLen Brown 		return -ENODEV;
1059b4f9fe12SLen Brown 	}
1060b4f9fe12SLen Brown 
1061135740deSSeth Forshee 	ret = acpi_bus_register_driver(&toshiba_acpi_driver);
1062b4f9fe12SLen Brown 	if (ret) {
1063135740deSSeth Forshee 		pr_err("Failed to register ACPI driver: %d\n", ret);
1064135740deSSeth Forshee 		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
1065135740deSSeth Forshee 	}
1066135740deSSeth Forshee 
1067b4f9fe12SLen Brown 	return ret;
1068b4f9fe12SLen Brown }
1069b4f9fe12SLen Brown 
1070135740deSSeth Forshee static void __exit toshiba_acpi_exit(void)
1071135740deSSeth Forshee {
1072135740deSSeth Forshee 	acpi_bus_unregister_driver(&toshiba_acpi_driver);
1073135740deSSeth Forshee 	if (toshiba_proc_dir)
1074135740deSSeth Forshee 		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
1075b4f9fe12SLen Brown }
1076b4f9fe12SLen Brown 
1077b4f9fe12SLen Brown module_init(toshiba_acpi_init);
1078b4f9fe12SLen Brown module_exit(toshiba_acpi_exit);
1079