xref: /linux/drivers/platform/x86/toshiba_acpi.c (revision 384a7cd9ace5b37a17ffea436f09170cdf671c88)
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 
38b4f9fe12SLen Brown #define TOSHIBA_ACPI_VERSION	"0.19"
39b4f9fe12SLen Brown #define PROC_INTERFACE_VERSION	1
40b4f9fe12SLen Brown 
41b4f9fe12SLen Brown #include <linux/kernel.h>
42b4f9fe12SLen Brown #include <linux/module.h>
43b4f9fe12SLen Brown #include <linux/init.h>
44b4f9fe12SLen Brown #include <linux/types.h>
45b4f9fe12SLen Brown #include <linux/proc_fs.h>
46936c8bcdSAlexey Dobriyan #include <linux/seq_file.h>
47b4f9fe12SLen Brown #include <linux/backlight.h>
48b4f9fe12SLen Brown #include <linux/platform_device.h>
49b4f9fe12SLen Brown #include <linux/rfkill.h>
506335e4d5SMatthew Garrett #include <linux/input.h>
51*384a7cd9SDmitry Torokhov #include <linux/input/sparse-keymap.h>
526c3f6e6cSPierre Ducroquet #include <linux/leds.h>
535a0e3ad6STejun Heo #include <linux/slab.h>
54b4f9fe12SLen Brown 
55b4f9fe12SLen Brown #include <asm/uaccess.h>
56b4f9fe12SLen Brown 
57b4f9fe12SLen Brown #include <acpi/acpi_drivers.h>
58b4f9fe12SLen Brown 
59b4f9fe12SLen Brown MODULE_AUTHOR("John Belmonte");
60b4f9fe12SLen Brown MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
61b4f9fe12SLen Brown MODULE_LICENSE("GPL");
62b4f9fe12SLen Brown 
63b4f9fe12SLen Brown #define MY_LOGPREFIX "toshiba_acpi: "
64b4f9fe12SLen Brown #define MY_ERR KERN_ERR MY_LOGPREFIX
65b4f9fe12SLen Brown #define MY_NOTICE KERN_NOTICE MY_LOGPREFIX
66b4f9fe12SLen Brown #define MY_INFO KERN_INFO MY_LOGPREFIX
67b4f9fe12SLen Brown 
68b4f9fe12SLen Brown /* Toshiba ACPI method paths */
69b4f9fe12SLen Brown #define METHOD_LCD_BRIGHTNESS	"\\_SB_.PCI0.VGA_.LCD_._BCM"
706335e4d5SMatthew Garrett #define TOSH_INTERFACE_1	"\\_SB_.VALD"
716335e4d5SMatthew Garrett #define TOSH_INTERFACE_2	"\\_SB_.VALZ"
72b4f9fe12SLen Brown #define METHOD_VIDEO_OUT	"\\_SB_.VALX.DSSX"
736335e4d5SMatthew Garrett #define GHCI_METHOD		".GHCI"
74b4f9fe12SLen Brown 
75b4f9fe12SLen Brown /* Toshiba HCI interface definitions
76b4f9fe12SLen Brown  *
77b4f9fe12SLen Brown  * HCI is Toshiba's "Hardware Control Interface" which is supposed to
78b4f9fe12SLen Brown  * be uniform across all their models.  Ideally we would just call
79b4f9fe12SLen Brown  * dedicated ACPI methods instead of using this primitive interface.
80b4f9fe12SLen Brown  * However the ACPI methods seem to be incomplete in some areas (for
81b4f9fe12SLen Brown  * example they allow setting, but not reading, the LCD brightness value),
82b4f9fe12SLen Brown  * so this is still useful.
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
90b4f9fe12SLen Brown 
91b4f9fe12SLen Brown /* return codes */
92b4f9fe12SLen Brown #define HCI_SUCCESS			0x0000
93b4f9fe12SLen Brown #define HCI_FAILURE			0x1000
94b4f9fe12SLen Brown #define HCI_NOT_SUPPORTED		0x8000
95b4f9fe12SLen Brown #define HCI_EMPTY			0x8c00
96b4f9fe12SLen Brown 
97b4f9fe12SLen Brown /* registers */
98b4f9fe12SLen Brown #define HCI_FAN				0x0004
99b4f9fe12SLen Brown #define HCI_SYSTEM_EVENT		0x0016
100b4f9fe12SLen Brown #define HCI_VIDEO_OUT			0x001c
101b4f9fe12SLen Brown #define HCI_HOTKEY_EVENT		0x001e
102b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS		0x002a
103b4f9fe12SLen Brown #define HCI_WIRELESS			0x0056
104b4f9fe12SLen Brown 
105b4f9fe12SLen Brown /* field definitions */
106b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_BITS		3
107b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_SHIFT	(16-HCI_LCD_BRIGHTNESS_BITS)
108b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_LEVELS	(1 << HCI_LCD_BRIGHTNESS_BITS)
109b4f9fe12SLen Brown #define HCI_VIDEO_OUT_LCD		0x1
110b4f9fe12SLen Brown #define HCI_VIDEO_OUT_CRT		0x2
111b4f9fe12SLen Brown #define HCI_VIDEO_OUT_TV		0x4
112b4f9fe12SLen Brown #define HCI_WIRELESS_KILL_SWITCH	0x01
113b4f9fe12SLen Brown #define HCI_WIRELESS_BT_PRESENT		0x0f
114b4f9fe12SLen Brown #define HCI_WIRELESS_BT_ATTACH		0x40
115b4f9fe12SLen Brown #define HCI_WIRELESS_BT_POWER		0x80
116b4f9fe12SLen Brown 
117b4f9fe12SLen Brown static const struct acpi_device_id toshiba_device_ids[] = {
118b4f9fe12SLen Brown 	{"TOS6200", 0},
119b4f9fe12SLen Brown 	{"TOS6208", 0},
120b4f9fe12SLen Brown 	{"TOS1900", 0},
121b4f9fe12SLen Brown 	{"", 0},
122b4f9fe12SLen Brown };
123b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
124b4f9fe12SLen Brown 
125*384a7cd9SDmitry Torokhov static const struct key_entry toshiba_acpi_keymap[] __initconst = {
126*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x101, { KEY_MUTE } },
127*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x102, { KEY_ZOOMOUT } },
128*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x103, { KEY_ZOOMIN } },
129*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13b, { KEY_COFFEE } },
130*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13c, { KEY_BATTERY } },
131*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13d, { KEY_SLEEP } },
132*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13e, { KEY_SUSPEND } },
133*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } },
134*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
135*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
136*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x142, { KEY_WLAN } },
137*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x143, { KEY_PROG1 } },
138*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb05, { KEY_PROG2 } },
139*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb06, { KEY_WWW } },
140*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb07, { KEY_MAIL } },
141*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb30, { KEY_STOP } },
142*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb31, { KEY_PREVIOUSSONG } },
143*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb32, { KEY_NEXTSONG } },
144*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
145*384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb5a, { KEY_MEDIA } },
146*384a7cd9SDmitry Torokhov 	{ KE_END, 0 },
1476335e4d5SMatthew Garrett };
1486335e4d5SMatthew Garrett 
149b4f9fe12SLen Brown /* utility
150b4f9fe12SLen Brown  */
151b4f9fe12SLen Brown 
152b4f9fe12SLen Brown static __inline__ void _set_bit(u32 * word, u32 mask, int value)
153b4f9fe12SLen Brown {
154b4f9fe12SLen Brown 	*word = (*word & ~mask) | (mask * value);
155b4f9fe12SLen Brown }
156b4f9fe12SLen Brown 
157b4f9fe12SLen Brown /* acpi interface wrappers
158b4f9fe12SLen Brown  */
159b4f9fe12SLen Brown 
160b4f9fe12SLen Brown static int is_valid_acpi_path(const char *methodName)
161b4f9fe12SLen Brown {
162b4f9fe12SLen Brown 	acpi_handle handle;
163b4f9fe12SLen Brown 	acpi_status status;
164b4f9fe12SLen Brown 
165b4f9fe12SLen Brown 	status = acpi_get_handle(NULL, (char *)methodName, &handle);
166b4f9fe12SLen Brown 	return !ACPI_FAILURE(status);
167b4f9fe12SLen Brown }
168b4f9fe12SLen Brown 
169b4f9fe12SLen Brown static int write_acpi_int(const char *methodName, int val)
170b4f9fe12SLen Brown {
171b4f9fe12SLen Brown 	struct acpi_object_list params;
172b4f9fe12SLen Brown 	union acpi_object in_objs[1];
173b4f9fe12SLen Brown 	acpi_status status;
174b4f9fe12SLen Brown 
175b4f9fe12SLen Brown 	params.count = ARRAY_SIZE(in_objs);
176b4f9fe12SLen Brown 	params.pointer = in_objs;
177b4f9fe12SLen Brown 	in_objs[0].type = ACPI_TYPE_INTEGER;
178b4f9fe12SLen Brown 	in_objs[0].integer.value = val;
179b4f9fe12SLen Brown 
180b4f9fe12SLen Brown 	status = acpi_evaluate_object(NULL, (char *)methodName, &params, NULL);
181b4f9fe12SLen Brown 	return (status == AE_OK);
182b4f9fe12SLen Brown }
183b4f9fe12SLen Brown 
184b4f9fe12SLen Brown #if 0
185b4f9fe12SLen Brown static int read_acpi_int(const char *methodName, int *pVal)
186b4f9fe12SLen Brown {
187b4f9fe12SLen Brown 	struct acpi_buffer results;
188b4f9fe12SLen Brown 	union acpi_object out_objs[1];
189b4f9fe12SLen Brown 	acpi_status status;
190b4f9fe12SLen Brown 
191b4f9fe12SLen Brown 	results.length = sizeof(out_objs);
192b4f9fe12SLen Brown 	results.pointer = out_objs;
193b4f9fe12SLen Brown 
194b4f9fe12SLen Brown 	status = acpi_evaluate_object(0, (char *)methodName, 0, &results);
195b4f9fe12SLen Brown 	*pVal = out_objs[0].integer.value;
196b4f9fe12SLen Brown 
197b4f9fe12SLen Brown 	return (status == AE_OK) && (out_objs[0].type == ACPI_TYPE_INTEGER);
198b4f9fe12SLen Brown }
199b4f9fe12SLen Brown #endif
200b4f9fe12SLen Brown 
201b4f9fe12SLen Brown static const char *method_hci /*= 0*/ ;
202b4f9fe12SLen Brown 
203b4f9fe12SLen Brown /* Perform a raw HCI call.  Here we don't care about input or output buffer
204b4f9fe12SLen Brown  * format.
205b4f9fe12SLen Brown  */
206b4f9fe12SLen Brown static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
207b4f9fe12SLen Brown {
208b4f9fe12SLen Brown 	struct acpi_object_list params;
209b4f9fe12SLen Brown 	union acpi_object in_objs[HCI_WORDS];
210b4f9fe12SLen Brown 	struct acpi_buffer results;
211b4f9fe12SLen Brown 	union acpi_object out_objs[HCI_WORDS + 1];
212b4f9fe12SLen Brown 	acpi_status status;
213b4f9fe12SLen Brown 	int i;
214b4f9fe12SLen Brown 
215b4f9fe12SLen Brown 	params.count = HCI_WORDS;
216b4f9fe12SLen Brown 	params.pointer = in_objs;
217b4f9fe12SLen Brown 	for (i = 0; i < HCI_WORDS; ++i) {
218b4f9fe12SLen Brown 		in_objs[i].type = ACPI_TYPE_INTEGER;
219b4f9fe12SLen Brown 		in_objs[i].integer.value = in[i];
220b4f9fe12SLen Brown 	}
221b4f9fe12SLen Brown 
222b4f9fe12SLen Brown 	results.length = sizeof(out_objs);
223b4f9fe12SLen Brown 	results.pointer = out_objs;
224b4f9fe12SLen Brown 
225b4f9fe12SLen Brown 	status = acpi_evaluate_object(NULL, (char *)method_hci, &params,
226b4f9fe12SLen Brown 				      &results);
227b4f9fe12SLen Brown 	if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) {
228b4f9fe12SLen Brown 		for (i = 0; i < out_objs->package.count; ++i) {
229b4f9fe12SLen Brown 			out[i] = out_objs->package.elements[i].integer.value;
230b4f9fe12SLen Brown 		}
231b4f9fe12SLen Brown 	}
232b4f9fe12SLen Brown 
233b4f9fe12SLen Brown 	return status;
234b4f9fe12SLen Brown }
235b4f9fe12SLen Brown 
236b4f9fe12SLen Brown /* common hci tasks (get or set one or two value)
237b4f9fe12SLen Brown  *
238b4f9fe12SLen Brown  * In addition to the ACPI status, the HCI system returns a result which
239b4f9fe12SLen Brown  * may be useful (such as "not supported").
240b4f9fe12SLen Brown  */
241b4f9fe12SLen Brown 
242b4f9fe12SLen Brown static acpi_status hci_write1(u32 reg, u32 in1, u32 * result)
243b4f9fe12SLen Brown {
244b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 };
245b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
246b4f9fe12SLen Brown 	acpi_status status = hci_raw(in, out);
247b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
248b4f9fe12SLen Brown 	return status;
249b4f9fe12SLen Brown }
250b4f9fe12SLen Brown 
251b4f9fe12SLen Brown static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result)
252b4f9fe12SLen Brown {
253b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 };
254b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
255b4f9fe12SLen Brown 	acpi_status status = hci_raw(in, out);
256b4f9fe12SLen Brown 	*out1 = out[2];
257b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
258b4f9fe12SLen Brown 	return status;
259b4f9fe12SLen Brown }
260b4f9fe12SLen Brown 
261b4f9fe12SLen Brown static acpi_status hci_write2(u32 reg, u32 in1, u32 in2, u32 *result)
262b4f9fe12SLen Brown {
263b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 };
264b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
265b4f9fe12SLen Brown 	acpi_status status = hci_raw(in, out);
266b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
267b4f9fe12SLen Brown 	return status;
268b4f9fe12SLen Brown }
269b4f9fe12SLen Brown 
270b4f9fe12SLen Brown static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result)
271b4f9fe12SLen Brown {
272b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 };
273b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
274b4f9fe12SLen Brown 	acpi_status status = hci_raw(in, out);
275b4f9fe12SLen Brown 	*out1 = out[2];
276b4f9fe12SLen Brown 	*out2 = out[3];
277b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
278b4f9fe12SLen Brown 	return status;
279b4f9fe12SLen Brown }
280b4f9fe12SLen Brown 
281b4f9fe12SLen Brown struct toshiba_acpi_dev {
282b4f9fe12SLen Brown 	struct platform_device *p_dev;
28319d337dfSJohannes Berg 	struct rfkill *bt_rfk;
2846335e4d5SMatthew Garrett 	struct input_dev *hotkey_dev;
2856c3f6e6cSPierre Ducroquet 	int illumination_installed;
2866335e4d5SMatthew Garrett 	acpi_handle handle;
287b4f9fe12SLen Brown 
288b4f9fe12SLen Brown 	const char *bt_name;
289b4f9fe12SLen Brown 
290b4f9fe12SLen Brown 	struct mutex mutex;
291b4f9fe12SLen Brown };
292b4f9fe12SLen Brown 
2936c3f6e6cSPierre Ducroquet /* Illumination support */
2946c3f6e6cSPierre Ducroquet static int toshiba_illumination_available(void)
2956c3f6e6cSPierre Ducroquet {
2966c3f6e6cSPierre Ducroquet 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
2976c3f6e6cSPierre Ducroquet 	u32 out[HCI_WORDS];
2986c3f6e6cSPierre Ducroquet 	acpi_status status;
2996c3f6e6cSPierre Ducroquet 
3006c3f6e6cSPierre Ducroquet 	in[0] = 0xf100;
3016c3f6e6cSPierre Ducroquet 	status = hci_raw(in, out);
3026c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
3036c3f6e6cSPierre Ducroquet 		printk(MY_INFO "Illumination device not available\n");
3046c3f6e6cSPierre Ducroquet 		return 0;
3056c3f6e6cSPierre Ducroquet 	}
3066c3f6e6cSPierre Ducroquet 	in[0] = 0xf400;
3076c3f6e6cSPierre Ducroquet 	status = hci_raw(in, out);
3086c3f6e6cSPierre Ducroquet 	return 1;
3096c3f6e6cSPierre Ducroquet }
3106c3f6e6cSPierre Ducroquet 
3116c3f6e6cSPierre Ducroquet static void toshiba_illumination_set(struct led_classdev *cdev,
3126c3f6e6cSPierre Ducroquet 				     enum led_brightness brightness)
3136c3f6e6cSPierre Ducroquet {
3146c3f6e6cSPierre Ducroquet 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
3156c3f6e6cSPierre Ducroquet 	u32 out[HCI_WORDS];
3166c3f6e6cSPierre Ducroquet 	acpi_status status;
3176c3f6e6cSPierre Ducroquet 
3186c3f6e6cSPierre Ducroquet 	/* First request : initialize communication. */
3196c3f6e6cSPierre Ducroquet 	in[0] = 0xf100;
3206c3f6e6cSPierre Ducroquet 	status = hci_raw(in, out);
3216c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
3226c3f6e6cSPierre Ducroquet 		printk(MY_INFO "Illumination device not available\n");
3236c3f6e6cSPierre Ducroquet 		return;
3246c3f6e6cSPierre Ducroquet 	}
3256c3f6e6cSPierre Ducroquet 
3266c3f6e6cSPierre Ducroquet 	if (brightness) {
3276c3f6e6cSPierre Ducroquet 		/* Switch the illumination on */
3286c3f6e6cSPierre Ducroquet 		in[0] = 0xf400;
3296c3f6e6cSPierre Ducroquet 		in[1] = 0x14e;
3306c3f6e6cSPierre Ducroquet 		in[2] = 1;
3316c3f6e6cSPierre Ducroquet 		status = hci_raw(in, out);
3326c3f6e6cSPierre Ducroquet 		if (ACPI_FAILURE(status)) {
3336c3f6e6cSPierre Ducroquet 			printk(MY_INFO "ACPI call for illumination failed.\n");
3346c3f6e6cSPierre Ducroquet 			return;
3356c3f6e6cSPierre Ducroquet 		}
3366c3f6e6cSPierre Ducroquet 	} else {
3376c3f6e6cSPierre Ducroquet 		/* Switch the illumination off */
3386c3f6e6cSPierre Ducroquet 		in[0] = 0xf400;
3396c3f6e6cSPierre Ducroquet 		in[1] = 0x14e;
3406c3f6e6cSPierre Ducroquet 		in[2] = 0;
3416c3f6e6cSPierre Ducroquet 		status = hci_raw(in, out);
3426c3f6e6cSPierre Ducroquet 		if (ACPI_FAILURE(status)) {
3436c3f6e6cSPierre Ducroquet 			printk(MY_INFO "ACPI call for illumination failed.\n");
3446c3f6e6cSPierre Ducroquet 			return;
3456c3f6e6cSPierre Ducroquet 		}
3466c3f6e6cSPierre Ducroquet 	}
3476c3f6e6cSPierre Ducroquet 
3486c3f6e6cSPierre Ducroquet 	/* Last request : close communication. */
3496c3f6e6cSPierre Ducroquet 	in[0] = 0xf200;
3506c3f6e6cSPierre Ducroquet 	in[1] = 0;
3516c3f6e6cSPierre Ducroquet 	in[2] = 0;
3526c3f6e6cSPierre Ducroquet 	hci_raw(in, out);
3536c3f6e6cSPierre Ducroquet }
3546c3f6e6cSPierre Ducroquet 
3556c3f6e6cSPierre Ducroquet static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
3566c3f6e6cSPierre Ducroquet {
3576c3f6e6cSPierre Ducroquet 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
3586c3f6e6cSPierre Ducroquet 	u32 out[HCI_WORDS];
3596c3f6e6cSPierre Ducroquet 	acpi_status status;
3606c3f6e6cSPierre Ducroquet 	enum led_brightness result;
3616c3f6e6cSPierre Ducroquet 
3626c3f6e6cSPierre Ducroquet 	/* First request : initialize communication. */
3636c3f6e6cSPierre Ducroquet 	in[0] = 0xf100;
3646c3f6e6cSPierre Ducroquet 	status = hci_raw(in, out);
3656c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
3666c3f6e6cSPierre Ducroquet 		printk(MY_INFO "Illumination device not available\n");
3676c3f6e6cSPierre Ducroquet 		return LED_OFF;
3686c3f6e6cSPierre Ducroquet 	}
3696c3f6e6cSPierre Ducroquet 
3706c3f6e6cSPierre Ducroquet 	/* Check the illumination */
3716c3f6e6cSPierre Ducroquet 	in[0] = 0xf300;
3726c3f6e6cSPierre Ducroquet 	in[1] = 0x14e;
3736c3f6e6cSPierre Ducroquet 	status = hci_raw(in, out);
3746c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
3756c3f6e6cSPierre Ducroquet 		printk(MY_INFO "ACPI call for illumination failed.\n");
3766c3f6e6cSPierre Ducroquet 		return LED_OFF;
3776c3f6e6cSPierre Ducroquet 	}
3786c3f6e6cSPierre Ducroquet 
3796c3f6e6cSPierre Ducroquet 	result = out[2] ? LED_FULL : LED_OFF;
3806c3f6e6cSPierre Ducroquet 
3816c3f6e6cSPierre Ducroquet 	/* Last request : close communication. */
3826c3f6e6cSPierre Ducroquet 	in[0] = 0xf200;
3836c3f6e6cSPierre Ducroquet 	in[1] = 0;
3846c3f6e6cSPierre Ducroquet 	in[2] = 0;
3856c3f6e6cSPierre Ducroquet 	hci_raw(in, out);
3866c3f6e6cSPierre Ducroquet 
3876c3f6e6cSPierre Ducroquet 	return result;
3886c3f6e6cSPierre Ducroquet }
3896c3f6e6cSPierre Ducroquet 
3906c3f6e6cSPierre Ducroquet static struct led_classdev toshiba_led = {
3916c3f6e6cSPierre Ducroquet 	.name           = "toshiba::illumination",
3926c3f6e6cSPierre Ducroquet 	.max_brightness = 1,
3936c3f6e6cSPierre Ducroquet 	.brightness_set = toshiba_illumination_set,
3946c3f6e6cSPierre Ducroquet 	.brightness_get = toshiba_illumination_get,
3956c3f6e6cSPierre Ducroquet };
3966c3f6e6cSPierre Ducroquet 
397b4f9fe12SLen Brown static struct toshiba_acpi_dev toshiba_acpi = {
398b4f9fe12SLen Brown 	.bt_name = "Toshiba Bluetooth",
399b4f9fe12SLen Brown };
400b4f9fe12SLen Brown 
401b4f9fe12SLen Brown /* Bluetooth rfkill handlers */
402b4f9fe12SLen Brown 
403b4f9fe12SLen Brown static u32 hci_get_bt_present(bool *present)
404b4f9fe12SLen Brown {
405b4f9fe12SLen Brown 	u32 hci_result;
406b4f9fe12SLen Brown 	u32 value, value2;
407b4f9fe12SLen Brown 
408b4f9fe12SLen Brown 	value = 0;
409b4f9fe12SLen Brown 	value2 = 0;
410b4f9fe12SLen Brown 	hci_read2(HCI_WIRELESS, &value, &value2, &hci_result);
411b4f9fe12SLen Brown 	if (hci_result == HCI_SUCCESS)
412b4f9fe12SLen Brown 		*present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false;
413b4f9fe12SLen Brown 
414b4f9fe12SLen Brown 	return hci_result;
415b4f9fe12SLen Brown }
416b4f9fe12SLen Brown 
417b4f9fe12SLen Brown static u32 hci_get_radio_state(bool *radio_state)
418b4f9fe12SLen Brown {
419b4f9fe12SLen Brown 	u32 hci_result;
420b4f9fe12SLen Brown 	u32 value, value2;
421b4f9fe12SLen Brown 
422b4f9fe12SLen Brown 	value = 0;
423b4f9fe12SLen Brown 	value2 = 0x0001;
424b4f9fe12SLen Brown 	hci_read2(HCI_WIRELESS, &value, &value2, &hci_result);
425b4f9fe12SLen Brown 
426b4f9fe12SLen Brown 	*radio_state = value & HCI_WIRELESS_KILL_SWITCH;
427b4f9fe12SLen Brown 	return hci_result;
428b4f9fe12SLen Brown }
429b4f9fe12SLen Brown 
43019d337dfSJohannes Berg static int bt_rfkill_set_block(void *data, bool blocked)
431b4f9fe12SLen Brown {
43219d337dfSJohannes Berg 	struct toshiba_acpi_dev *dev = data;
433b4f9fe12SLen Brown 	u32 result1, result2;
434b4f9fe12SLen Brown 	u32 value;
43519d337dfSJohannes Berg 	int err;
436b4f9fe12SLen Brown 	bool radio_state;
437b4f9fe12SLen Brown 
43819d337dfSJohannes Berg 	value = (blocked == false);
439b4f9fe12SLen Brown 
440b4f9fe12SLen Brown 	mutex_lock(&dev->mutex);
44119d337dfSJohannes Berg 	if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) {
44219d337dfSJohannes Berg 		err = -EBUSY;
44319d337dfSJohannes Berg 		goto out;
444b4f9fe12SLen Brown 	}
445b4f9fe12SLen Brown 
44619d337dfSJohannes Berg 	if (!radio_state) {
44719d337dfSJohannes Berg 		err = 0;
44819d337dfSJohannes Berg 		goto out;
44919d337dfSJohannes Berg 	}
45019d337dfSJohannes Berg 
45119d337dfSJohannes Berg 	hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
45219d337dfSJohannes Berg 	hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2);
45319d337dfSJohannes Berg 
45419d337dfSJohannes Berg 	if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
45519d337dfSJohannes Berg 		err = -EBUSY;
45619d337dfSJohannes Berg 	else
45719d337dfSJohannes Berg 		err = 0;
45819d337dfSJohannes Berg  out:
45919d337dfSJohannes Berg 	mutex_unlock(&dev->mutex);
46019d337dfSJohannes Berg 	return err;
46119d337dfSJohannes Berg }
46219d337dfSJohannes Berg 
46319d337dfSJohannes Berg static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
464b4f9fe12SLen Brown {
465b4f9fe12SLen Brown 	bool new_rfk_state;
466b4f9fe12SLen Brown 	bool value;
467b4f9fe12SLen Brown 	u32 hci_result;
46819d337dfSJohannes Berg 	struct toshiba_acpi_dev *dev = data;
46919d337dfSJohannes Berg 
47019d337dfSJohannes Berg 	mutex_lock(&dev->mutex);
471b4f9fe12SLen Brown 
472b4f9fe12SLen Brown 	hci_result = hci_get_radio_state(&value);
47319d337dfSJohannes Berg 	if (hci_result != HCI_SUCCESS) {
47419d337dfSJohannes Berg 		/* Can't do anything useful */
47519d337dfSJohannes Berg 		mutex_unlock(&dev->mutex);
47682e7784fSJiri Slaby 		return;
47719d337dfSJohannes Berg 	}
478b4f9fe12SLen Brown 
479b4f9fe12SLen Brown 	new_rfk_state = value;
480b4f9fe12SLen Brown 
481b4f9fe12SLen Brown 	mutex_unlock(&dev->mutex);
482b4f9fe12SLen Brown 
48319d337dfSJohannes Berg 	if (rfkill_set_hw_state(rfkill, !new_rfk_state))
48419d337dfSJohannes Berg 		bt_rfkill_set_block(data, true);
485b4f9fe12SLen Brown }
48619d337dfSJohannes Berg 
48719d337dfSJohannes Berg static const struct rfkill_ops toshiba_rfk_ops = {
48819d337dfSJohannes Berg 	.set_block = bt_rfkill_set_block,
48919d337dfSJohannes Berg 	.poll = bt_rfkill_poll,
49019d337dfSJohannes Berg };
491b4f9fe12SLen Brown 
492b4f9fe12SLen Brown static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
493b4f9fe12SLen Brown static struct backlight_device *toshiba_backlight_device;
494b4f9fe12SLen Brown static int force_fan;
495b4f9fe12SLen Brown static int last_key_event;
496b4f9fe12SLen Brown static int key_event_valid;
497b4f9fe12SLen Brown 
498b4f9fe12SLen Brown static int get_lcd(struct backlight_device *bd)
499b4f9fe12SLen Brown {
500b4f9fe12SLen Brown 	u32 hci_result;
501b4f9fe12SLen Brown 	u32 value;
502b4f9fe12SLen Brown 
503b4f9fe12SLen Brown 	hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result);
504b4f9fe12SLen Brown 	if (hci_result == HCI_SUCCESS) {
505b4f9fe12SLen Brown 		return (value >> HCI_LCD_BRIGHTNESS_SHIFT);
506b4f9fe12SLen Brown 	} else
507b4f9fe12SLen Brown 		return -EFAULT;
508b4f9fe12SLen Brown }
509b4f9fe12SLen Brown 
510936c8bcdSAlexey Dobriyan static int lcd_proc_show(struct seq_file *m, void *v)
511b4f9fe12SLen Brown {
512b4f9fe12SLen Brown 	int value = get_lcd(NULL);
513b4f9fe12SLen Brown 
514b4f9fe12SLen Brown 	if (value >= 0) {
515936c8bcdSAlexey Dobriyan 		seq_printf(m, "brightness:              %d\n", value);
516936c8bcdSAlexey Dobriyan 		seq_printf(m, "brightness_levels:       %d\n",
517b4f9fe12SLen Brown 			     HCI_LCD_BRIGHTNESS_LEVELS);
518b4f9fe12SLen Brown 	} else {
519b4f9fe12SLen Brown 		printk(MY_ERR "Error reading LCD brightness\n");
520b4f9fe12SLen Brown 	}
521b4f9fe12SLen Brown 
522936c8bcdSAlexey Dobriyan 	return 0;
523936c8bcdSAlexey Dobriyan }
524936c8bcdSAlexey Dobriyan 
525936c8bcdSAlexey Dobriyan static int lcd_proc_open(struct inode *inode, struct file *file)
526936c8bcdSAlexey Dobriyan {
527936c8bcdSAlexey Dobriyan 	return single_open(file, lcd_proc_show, NULL);
528b4f9fe12SLen Brown }
529b4f9fe12SLen Brown 
530b4f9fe12SLen Brown static int set_lcd(int value)
531b4f9fe12SLen Brown {
532b4f9fe12SLen Brown 	u32 hci_result;
533b4f9fe12SLen Brown 
534b4f9fe12SLen Brown 	value = value << HCI_LCD_BRIGHTNESS_SHIFT;
535b4f9fe12SLen Brown 	hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result);
536b4f9fe12SLen Brown 	if (hci_result != HCI_SUCCESS)
537b4f9fe12SLen Brown 		return -EFAULT;
538b4f9fe12SLen Brown 
539b4f9fe12SLen Brown 	return 0;
540b4f9fe12SLen Brown }
541b4f9fe12SLen Brown 
542b4f9fe12SLen Brown static int set_lcd_status(struct backlight_device *bd)
543b4f9fe12SLen Brown {
544b4f9fe12SLen Brown 	return set_lcd(bd->props.brightness);
545b4f9fe12SLen Brown }
546b4f9fe12SLen Brown 
547936c8bcdSAlexey Dobriyan static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
548936c8bcdSAlexey Dobriyan 			      size_t count, loff_t *pos)
549b4f9fe12SLen Brown {
550936c8bcdSAlexey Dobriyan 	char cmd[42];
551936c8bcdSAlexey Dobriyan 	size_t len;
552b4f9fe12SLen Brown 	int value;
553b4f9fe12SLen Brown 	int ret;
554b4f9fe12SLen Brown 
555936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
556936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
557936c8bcdSAlexey Dobriyan 		return -EFAULT;
558936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
559936c8bcdSAlexey Dobriyan 
560936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " brightness : %i", &value) == 1 &&
561b4f9fe12SLen Brown 	    value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) {
562b4f9fe12SLen Brown 		ret = set_lcd(value);
563b4f9fe12SLen Brown 		if (ret == 0)
564b4f9fe12SLen Brown 			ret = count;
565b4f9fe12SLen Brown 	} else {
566b4f9fe12SLen Brown 		ret = -EINVAL;
567b4f9fe12SLen Brown 	}
568b4f9fe12SLen Brown 	return ret;
569b4f9fe12SLen Brown }
570b4f9fe12SLen Brown 
571936c8bcdSAlexey Dobriyan static const struct file_operations lcd_proc_fops = {
572936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
573936c8bcdSAlexey Dobriyan 	.open		= lcd_proc_open,
574936c8bcdSAlexey Dobriyan 	.read		= seq_read,
575936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
576936c8bcdSAlexey Dobriyan 	.release	= single_release,
577936c8bcdSAlexey Dobriyan 	.write		= lcd_proc_write,
578936c8bcdSAlexey Dobriyan };
579936c8bcdSAlexey Dobriyan 
580936c8bcdSAlexey Dobriyan static int video_proc_show(struct seq_file *m, void *v)
581b4f9fe12SLen Brown {
582b4f9fe12SLen Brown 	u32 hci_result;
583b4f9fe12SLen Brown 	u32 value;
584b4f9fe12SLen Brown 
585b4f9fe12SLen Brown 	hci_read1(HCI_VIDEO_OUT, &value, &hci_result);
586b4f9fe12SLen Brown 	if (hci_result == HCI_SUCCESS) {
587b4f9fe12SLen Brown 		int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
588b4f9fe12SLen Brown 		int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
589b4f9fe12SLen Brown 		int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
590936c8bcdSAlexey Dobriyan 		seq_printf(m, "lcd_out:                 %d\n", is_lcd);
591936c8bcdSAlexey Dobriyan 		seq_printf(m, "crt_out:                 %d\n", is_crt);
592936c8bcdSAlexey Dobriyan 		seq_printf(m, "tv_out:                  %d\n", is_tv);
593b4f9fe12SLen Brown 	} else {
594b4f9fe12SLen Brown 		printk(MY_ERR "Error reading video out status\n");
595b4f9fe12SLen Brown 	}
596b4f9fe12SLen Brown 
597936c8bcdSAlexey Dobriyan 	return 0;
598b4f9fe12SLen Brown }
599b4f9fe12SLen Brown 
600936c8bcdSAlexey Dobriyan static int video_proc_open(struct inode *inode, struct file *file)
601b4f9fe12SLen Brown {
602936c8bcdSAlexey Dobriyan 	return single_open(file, video_proc_show, NULL);
603936c8bcdSAlexey Dobriyan }
604936c8bcdSAlexey Dobriyan 
605936c8bcdSAlexey Dobriyan static ssize_t video_proc_write(struct file *file, const char __user *buf,
606936c8bcdSAlexey Dobriyan 				size_t count, loff_t *pos)
607936c8bcdSAlexey Dobriyan {
608936c8bcdSAlexey Dobriyan 	char *cmd, *buffer;
609b4f9fe12SLen Brown 	int value;
610b4f9fe12SLen Brown 	int remain = count;
611b4f9fe12SLen Brown 	int lcd_out = -1;
612b4f9fe12SLen Brown 	int crt_out = -1;
613b4f9fe12SLen Brown 	int tv_out = -1;
614b4f9fe12SLen Brown 	u32 hci_result;
615b4f9fe12SLen Brown 	u32 video_out;
616b4f9fe12SLen Brown 
617936c8bcdSAlexey Dobriyan 	cmd = kmalloc(count + 1, GFP_KERNEL);
618936c8bcdSAlexey Dobriyan 	if (!cmd)
619936c8bcdSAlexey Dobriyan 		return -ENOMEM;
620936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, count)) {
621936c8bcdSAlexey Dobriyan 		kfree(cmd);
622936c8bcdSAlexey Dobriyan 		return -EFAULT;
623936c8bcdSAlexey Dobriyan 	}
624936c8bcdSAlexey Dobriyan 	cmd[count] = '\0';
625936c8bcdSAlexey Dobriyan 
626936c8bcdSAlexey Dobriyan 	buffer = cmd;
627936c8bcdSAlexey Dobriyan 
628b4f9fe12SLen Brown 	/* scan expression.  Multiple expressions may be delimited with ;
629b4f9fe12SLen Brown 	 *
630b4f9fe12SLen Brown 	 *  NOTE: to keep scanning simple, invalid fields are ignored
631b4f9fe12SLen Brown 	 */
632b4f9fe12SLen Brown 	while (remain) {
633b4f9fe12SLen Brown 		if (sscanf(buffer, " lcd_out : %i", &value) == 1)
634b4f9fe12SLen Brown 			lcd_out = value & 1;
635b4f9fe12SLen Brown 		else if (sscanf(buffer, " crt_out : %i", &value) == 1)
636b4f9fe12SLen Brown 			crt_out = value & 1;
637b4f9fe12SLen Brown 		else if (sscanf(buffer, " tv_out : %i", &value) == 1)
638b4f9fe12SLen Brown 			tv_out = value & 1;
639b4f9fe12SLen Brown 		/* advance to one character past the next ; */
640b4f9fe12SLen Brown 		do {
641b4f9fe12SLen Brown 			++buffer;
642b4f9fe12SLen Brown 			--remain;
643b4f9fe12SLen Brown 		}
644b4f9fe12SLen Brown 		while (remain && *(buffer - 1) != ';');
645b4f9fe12SLen Brown 	}
646b4f9fe12SLen Brown 
647936c8bcdSAlexey Dobriyan 	kfree(cmd);
648936c8bcdSAlexey Dobriyan 
649b4f9fe12SLen Brown 	hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result);
650b4f9fe12SLen Brown 	if (hci_result == HCI_SUCCESS) {
651b4f9fe12SLen Brown 		unsigned int new_video_out = video_out;
652b4f9fe12SLen Brown 		if (lcd_out != -1)
653b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out);
654b4f9fe12SLen Brown 		if (crt_out != -1)
655b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out);
656b4f9fe12SLen Brown 		if (tv_out != -1)
657b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out);
658b4f9fe12SLen Brown 		/* To avoid unnecessary video disruption, only write the new
659b4f9fe12SLen Brown 		 * video setting if something changed. */
660b4f9fe12SLen Brown 		if (new_video_out != video_out)
661b4f9fe12SLen Brown 			write_acpi_int(METHOD_VIDEO_OUT, new_video_out);
662b4f9fe12SLen Brown 	} else {
663b4f9fe12SLen Brown 		return -EFAULT;
664b4f9fe12SLen Brown 	}
665b4f9fe12SLen Brown 
666b4f9fe12SLen Brown 	return count;
667b4f9fe12SLen Brown }
668b4f9fe12SLen Brown 
669936c8bcdSAlexey Dobriyan static const struct file_operations video_proc_fops = {
670936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
671936c8bcdSAlexey Dobriyan 	.open		= video_proc_open,
672936c8bcdSAlexey Dobriyan 	.read		= seq_read,
673936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
674936c8bcdSAlexey Dobriyan 	.release	= single_release,
675936c8bcdSAlexey Dobriyan 	.write		= video_proc_write,
676936c8bcdSAlexey Dobriyan };
677936c8bcdSAlexey Dobriyan 
678936c8bcdSAlexey Dobriyan static int fan_proc_show(struct seq_file *m, void *v)
679b4f9fe12SLen Brown {
680b4f9fe12SLen Brown 	u32 hci_result;
681b4f9fe12SLen Brown 	u32 value;
682b4f9fe12SLen Brown 
683b4f9fe12SLen Brown 	hci_read1(HCI_FAN, &value, &hci_result);
684b4f9fe12SLen Brown 	if (hci_result == HCI_SUCCESS) {
685936c8bcdSAlexey Dobriyan 		seq_printf(m, "running:                 %d\n", (value > 0));
686936c8bcdSAlexey Dobriyan 		seq_printf(m, "force_on:                %d\n", force_fan);
687b4f9fe12SLen Brown 	} else {
688b4f9fe12SLen Brown 		printk(MY_ERR "Error reading fan status\n");
689b4f9fe12SLen Brown 	}
690b4f9fe12SLen Brown 
691936c8bcdSAlexey Dobriyan 	return 0;
692b4f9fe12SLen Brown }
693b4f9fe12SLen Brown 
694936c8bcdSAlexey Dobriyan static int fan_proc_open(struct inode *inode, struct file *file)
695b4f9fe12SLen Brown {
696936c8bcdSAlexey Dobriyan 	return single_open(file, fan_proc_show, NULL);
697936c8bcdSAlexey Dobriyan }
698936c8bcdSAlexey Dobriyan 
699936c8bcdSAlexey Dobriyan static ssize_t fan_proc_write(struct file *file, const char __user *buf,
700936c8bcdSAlexey Dobriyan 			      size_t count, loff_t *pos)
701936c8bcdSAlexey Dobriyan {
702936c8bcdSAlexey Dobriyan 	char cmd[42];
703936c8bcdSAlexey Dobriyan 	size_t len;
704b4f9fe12SLen Brown 	int value;
705b4f9fe12SLen Brown 	u32 hci_result;
706b4f9fe12SLen Brown 
707936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
708936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
709936c8bcdSAlexey Dobriyan 		return -EFAULT;
710936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
711936c8bcdSAlexey Dobriyan 
712936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " force_on : %i", &value) == 1 &&
713b4f9fe12SLen Brown 	    value >= 0 && value <= 1) {
714b4f9fe12SLen Brown 		hci_write1(HCI_FAN, value, &hci_result);
715b4f9fe12SLen Brown 		if (hci_result != HCI_SUCCESS)
716b4f9fe12SLen Brown 			return -EFAULT;
717b4f9fe12SLen Brown 		else
718b4f9fe12SLen Brown 			force_fan = value;
719b4f9fe12SLen Brown 	} else {
720b4f9fe12SLen Brown 		return -EINVAL;
721b4f9fe12SLen Brown 	}
722b4f9fe12SLen Brown 
723b4f9fe12SLen Brown 	return count;
724b4f9fe12SLen Brown }
725b4f9fe12SLen Brown 
726936c8bcdSAlexey Dobriyan static const struct file_operations fan_proc_fops = {
727936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
728936c8bcdSAlexey Dobriyan 	.open		= fan_proc_open,
729936c8bcdSAlexey Dobriyan 	.read		= seq_read,
730936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
731936c8bcdSAlexey Dobriyan 	.release	= single_release,
732936c8bcdSAlexey Dobriyan 	.write		= fan_proc_write,
733936c8bcdSAlexey Dobriyan };
734936c8bcdSAlexey Dobriyan 
735936c8bcdSAlexey Dobriyan static int keys_proc_show(struct seq_file *m, void *v)
736b4f9fe12SLen Brown {
737b4f9fe12SLen Brown 	u32 hci_result;
738b4f9fe12SLen Brown 	u32 value;
739b4f9fe12SLen Brown 
740b4f9fe12SLen Brown 	if (!key_event_valid) {
741b4f9fe12SLen Brown 		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
742b4f9fe12SLen Brown 		if (hci_result == HCI_SUCCESS) {
743b4f9fe12SLen Brown 			key_event_valid = 1;
744b4f9fe12SLen Brown 			last_key_event = value;
745b4f9fe12SLen Brown 		} else if (hci_result == HCI_EMPTY) {
746b4f9fe12SLen Brown 			/* better luck next time */
747b4f9fe12SLen Brown 		} else if (hci_result == HCI_NOT_SUPPORTED) {
748b4f9fe12SLen Brown 			/* This is a workaround for an unresolved issue on
749b4f9fe12SLen Brown 			 * some machines where system events sporadically
750b4f9fe12SLen Brown 			 * become disabled. */
751b4f9fe12SLen Brown 			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
752b4f9fe12SLen Brown 			printk(MY_NOTICE "Re-enabled hotkeys\n");
753b4f9fe12SLen Brown 		} else {
754b4f9fe12SLen Brown 			printk(MY_ERR "Error reading hotkey status\n");
755b4f9fe12SLen Brown 			goto end;
756b4f9fe12SLen Brown 		}
757b4f9fe12SLen Brown 	}
758b4f9fe12SLen Brown 
759936c8bcdSAlexey Dobriyan 	seq_printf(m, "hotkey_ready:            %d\n", key_event_valid);
760936c8bcdSAlexey Dobriyan 	seq_printf(m, "hotkey:                  0x%04x\n", last_key_event);
761b4f9fe12SLen Brown end:
762936c8bcdSAlexey Dobriyan 	return 0;
763b4f9fe12SLen Brown }
764b4f9fe12SLen Brown 
765936c8bcdSAlexey Dobriyan static int keys_proc_open(struct inode *inode, struct file *file)
766b4f9fe12SLen Brown {
767936c8bcdSAlexey Dobriyan 	return single_open(file, keys_proc_show, NULL);
768936c8bcdSAlexey Dobriyan }
769936c8bcdSAlexey Dobriyan 
770936c8bcdSAlexey Dobriyan static ssize_t keys_proc_write(struct file *file, const char __user *buf,
771936c8bcdSAlexey Dobriyan 			       size_t count, loff_t *pos)
772936c8bcdSAlexey Dobriyan {
773936c8bcdSAlexey Dobriyan 	char cmd[42];
774936c8bcdSAlexey Dobriyan 	size_t len;
775b4f9fe12SLen Brown 	int value;
776b4f9fe12SLen Brown 
777936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
778936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
779936c8bcdSAlexey Dobriyan 		return -EFAULT;
780936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
781936c8bcdSAlexey Dobriyan 
782936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) {
783b4f9fe12SLen Brown 		key_event_valid = 0;
784b4f9fe12SLen Brown 	} else {
785b4f9fe12SLen Brown 		return -EINVAL;
786b4f9fe12SLen Brown 	}
787b4f9fe12SLen Brown 
788b4f9fe12SLen Brown 	return count;
789b4f9fe12SLen Brown }
790b4f9fe12SLen Brown 
791936c8bcdSAlexey Dobriyan static const struct file_operations keys_proc_fops = {
792936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
793936c8bcdSAlexey Dobriyan 	.open		= keys_proc_open,
794936c8bcdSAlexey Dobriyan 	.read		= seq_read,
795936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
796936c8bcdSAlexey Dobriyan 	.release	= single_release,
797936c8bcdSAlexey Dobriyan 	.write		= keys_proc_write,
798936c8bcdSAlexey Dobriyan };
799936c8bcdSAlexey Dobriyan 
800936c8bcdSAlexey Dobriyan static int version_proc_show(struct seq_file *m, void *v)
801b4f9fe12SLen Brown {
802936c8bcdSAlexey Dobriyan 	seq_printf(m, "driver:                  %s\n", TOSHIBA_ACPI_VERSION);
803936c8bcdSAlexey Dobriyan 	seq_printf(m, "proc_interface:          %d\n", PROC_INTERFACE_VERSION);
804936c8bcdSAlexey Dobriyan 	return 0;
805b4f9fe12SLen Brown }
806b4f9fe12SLen Brown 
807936c8bcdSAlexey Dobriyan static int version_proc_open(struct inode *inode, struct file *file)
808936c8bcdSAlexey Dobriyan {
809936c8bcdSAlexey Dobriyan 	return single_open(file, version_proc_show, PDE(inode)->data);
810936c8bcdSAlexey Dobriyan }
811936c8bcdSAlexey Dobriyan 
812936c8bcdSAlexey Dobriyan static const struct file_operations version_proc_fops = {
813936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
814936c8bcdSAlexey Dobriyan 	.open		= version_proc_open,
815936c8bcdSAlexey Dobriyan 	.read		= seq_read,
816936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
817936c8bcdSAlexey Dobriyan 	.release	= single_release,
818936c8bcdSAlexey Dobriyan };
819936c8bcdSAlexey Dobriyan 
820b4f9fe12SLen Brown /* proc and module init
821b4f9fe12SLen Brown  */
822b4f9fe12SLen Brown 
823b4f9fe12SLen Brown #define PROC_TOSHIBA		"toshiba"
824b4f9fe12SLen Brown 
825f8ef3aecSAxel Lin static void __init create_toshiba_proc_entries(void)
826b4f9fe12SLen Brown {
827936c8bcdSAlexey Dobriyan 	proc_create("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, &lcd_proc_fops);
828936c8bcdSAlexey Dobriyan 	proc_create("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, &video_proc_fops);
829936c8bcdSAlexey Dobriyan 	proc_create("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, &fan_proc_fops);
830936c8bcdSAlexey Dobriyan 	proc_create("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, &keys_proc_fops);
831936c8bcdSAlexey Dobriyan 	proc_create("version", S_IRUGO, toshiba_proc_dir, &version_proc_fops);
832b4f9fe12SLen Brown }
833b4f9fe12SLen Brown 
834f8ef3aecSAxel Lin static void remove_toshiba_proc_entries(void)
835b4f9fe12SLen Brown {
836936c8bcdSAlexey Dobriyan 	remove_proc_entry("lcd", toshiba_proc_dir);
837936c8bcdSAlexey Dobriyan 	remove_proc_entry("video", toshiba_proc_dir);
838936c8bcdSAlexey Dobriyan 	remove_proc_entry("fan", toshiba_proc_dir);
839936c8bcdSAlexey Dobriyan 	remove_proc_entry("keys", toshiba_proc_dir);
840936c8bcdSAlexey Dobriyan 	remove_proc_entry("version", toshiba_proc_dir);
841b4f9fe12SLen Brown }
842b4f9fe12SLen Brown 
843b4f9fe12SLen Brown static struct backlight_ops toshiba_backlight_data = {
844b4f9fe12SLen Brown         .get_brightness = get_lcd,
845b4f9fe12SLen Brown         .update_status  = set_lcd_status,
846b4f9fe12SLen Brown };
847b4f9fe12SLen Brown 
8486335e4d5SMatthew Garrett static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
8496335e4d5SMatthew Garrett {
8506335e4d5SMatthew Garrett 	u32 hci_result, value;
8516335e4d5SMatthew Garrett 
8526335e4d5SMatthew Garrett 	if (event != 0x80)
8536335e4d5SMatthew Garrett 		return;
8546335e4d5SMatthew Garrett 	do {
8556335e4d5SMatthew Garrett 		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
8566335e4d5SMatthew Garrett 		if (hci_result == HCI_SUCCESS) {
8576335e4d5SMatthew Garrett 			if (value == 0x100)
8586335e4d5SMatthew Garrett 				continue;
859b466301bSFrans Pop 			/* act on key press; ignore key release */
860b466301bSFrans Pop 			if (value & 0x80)
861b466301bSFrans Pop 				continue;
862b466301bSFrans Pop 
863*384a7cd9SDmitry Torokhov 			if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev,
864*384a7cd9SDmitry Torokhov 							value, 1, true)) {
8656335e4d5SMatthew Garrett 				printk(MY_INFO "Unknown key %x\n",
866b466301bSFrans Pop 				       value);
8676335e4d5SMatthew Garrett 			}
8686335e4d5SMatthew Garrett 		} else if (hci_result == HCI_NOT_SUPPORTED) {
8696335e4d5SMatthew Garrett 			/* This is a workaround for an unresolved issue on
8706335e4d5SMatthew Garrett 			 * some machines where system events sporadically
8716335e4d5SMatthew Garrett 			 * become disabled. */
8726335e4d5SMatthew Garrett 			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
8736335e4d5SMatthew Garrett 			printk(MY_NOTICE "Re-enabled hotkeys\n");
8746335e4d5SMatthew Garrett 		}
8756335e4d5SMatthew Garrett 	} while (hci_result != HCI_EMPTY);
8766335e4d5SMatthew Garrett }
8776335e4d5SMatthew Garrett 
878*384a7cd9SDmitry Torokhov static int __init toshiba_acpi_setup_keyboard(char *device)
8796335e4d5SMatthew Garrett {
8806335e4d5SMatthew Garrett 	acpi_status status;
881*384a7cd9SDmitry Torokhov 	int error;
8826335e4d5SMatthew Garrett 
883*384a7cd9SDmitry Torokhov 	status = acpi_get_handle(NULL, device, &toshiba_acpi.handle);
8846335e4d5SMatthew Garrett 	if (ACPI_FAILURE(status)) {
8856335e4d5SMatthew Garrett 		printk(MY_INFO "Unable to get notification device\n");
8866335e4d5SMatthew Garrett 		return -ENODEV;
8876335e4d5SMatthew Garrett 	}
8886335e4d5SMatthew Garrett 
8896335e4d5SMatthew Garrett 	toshiba_acpi.hotkey_dev = input_allocate_device();
8906335e4d5SMatthew Garrett 	if (!toshiba_acpi.hotkey_dev) {
8916335e4d5SMatthew Garrett 		printk(MY_INFO "Unable to register input device\n");
8926335e4d5SMatthew Garrett 		return -ENOMEM;
8936335e4d5SMatthew Garrett 	}
8946335e4d5SMatthew Garrett 
8956335e4d5SMatthew Garrett 	toshiba_acpi.hotkey_dev->name = "Toshiba input device";
8966335e4d5SMatthew Garrett 	toshiba_acpi.hotkey_dev->phys = device;
8976335e4d5SMatthew Garrett 	toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;
8986335e4d5SMatthew Garrett 
899*384a7cd9SDmitry Torokhov 	error = sparse_keymap_setup(toshiba_acpi.hotkey_dev,
900*384a7cd9SDmitry Torokhov 				    toshiba_acpi_keymap, NULL);
901*384a7cd9SDmitry Torokhov 	if (error)
902*384a7cd9SDmitry Torokhov 		goto err_free_dev;
903*384a7cd9SDmitry Torokhov 
904*384a7cd9SDmitry Torokhov 	status = acpi_install_notify_handler(toshiba_acpi.handle,
905*384a7cd9SDmitry Torokhov 				ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL);
906*384a7cd9SDmitry Torokhov 	if (ACPI_FAILURE(status)) {
907*384a7cd9SDmitry Torokhov 		printk(MY_INFO "Unable to install hotkey notification\n");
908*384a7cd9SDmitry Torokhov 		error = -ENODEV;
909*384a7cd9SDmitry Torokhov 		goto err_free_keymap;
9106335e4d5SMatthew Garrett 	}
9116335e4d5SMatthew Garrett 
912*384a7cd9SDmitry Torokhov 	status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL);
913*384a7cd9SDmitry Torokhov 	if (ACPI_FAILURE(status)) {
914*384a7cd9SDmitry Torokhov 		printk(MY_INFO "Unable to enable hotkeys\n");
915*384a7cd9SDmitry Torokhov 		error = -ENODEV;
916*384a7cd9SDmitry Torokhov 		goto err_remove_notify;
917*384a7cd9SDmitry Torokhov 	}
918*384a7cd9SDmitry Torokhov 
919*384a7cd9SDmitry Torokhov 	error = input_register_device(toshiba_acpi.hotkey_dev);
920*384a7cd9SDmitry Torokhov 	if (error) {
9216335e4d5SMatthew Garrett 		printk(MY_INFO "Unable to register input device\n");
922*384a7cd9SDmitry Torokhov 		goto err_remove_notify;
9236335e4d5SMatthew Garrett 	}
9246335e4d5SMatthew Garrett 
9256335e4d5SMatthew Garrett 	return 0;
926*384a7cd9SDmitry Torokhov 
927*384a7cd9SDmitry Torokhov  err_remove_notify:
928*384a7cd9SDmitry Torokhov 	acpi_remove_notify_handler(toshiba_acpi.handle,
929*384a7cd9SDmitry Torokhov 				   ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
930*384a7cd9SDmitry Torokhov  err_free_keymap:
931*384a7cd9SDmitry Torokhov 	sparse_keymap_free(toshiba_acpi.hotkey_dev);
932*384a7cd9SDmitry Torokhov  err_free_dev:
933*384a7cd9SDmitry Torokhov 	input_free_device(toshiba_acpi.hotkey_dev);
934*384a7cd9SDmitry Torokhov 	toshiba_acpi.hotkey_dev = NULL;
935*384a7cd9SDmitry Torokhov 	return error;
9366335e4d5SMatthew Garrett }
9376335e4d5SMatthew Garrett 
938b4f9fe12SLen Brown static void toshiba_acpi_exit(void)
939b4f9fe12SLen Brown {
940*384a7cd9SDmitry Torokhov 	if (toshiba_acpi.hotkey_dev) {
941*384a7cd9SDmitry Torokhov 		acpi_remove_notify_handler(toshiba_acpi.handle,
942*384a7cd9SDmitry Torokhov 				ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
943*384a7cd9SDmitry Torokhov 		sparse_keymap_free(toshiba_acpi.hotkey_dev);
9446335e4d5SMatthew Garrett 		input_unregister_device(toshiba_acpi.hotkey_dev);
945*384a7cd9SDmitry Torokhov 	}
9466335e4d5SMatthew Garrett 
94719d337dfSJohannes Berg 	if (toshiba_acpi.bt_rfk) {
94819d337dfSJohannes Berg 		rfkill_unregister(toshiba_acpi.bt_rfk);
94919d337dfSJohannes Berg 		rfkill_destroy(toshiba_acpi.bt_rfk);
950b4f9fe12SLen Brown 	}
951b4f9fe12SLen Brown 
952b4f9fe12SLen Brown 	if (toshiba_backlight_device)
953b4f9fe12SLen Brown 		backlight_device_unregister(toshiba_backlight_device);
954b4f9fe12SLen Brown 
955f8ef3aecSAxel Lin 	remove_toshiba_proc_entries();
956b4f9fe12SLen Brown 
957b4f9fe12SLen Brown 	if (toshiba_proc_dir)
958b4f9fe12SLen Brown 		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
959b4f9fe12SLen Brown 
9606c3f6e6cSPierre Ducroquet 	if (toshiba_acpi.illumination_installed)
9616c3f6e6cSPierre Ducroquet 		led_classdev_unregister(&toshiba_led);
9626c3f6e6cSPierre Ducroquet 
963b4f9fe12SLen Brown 	platform_device_unregister(toshiba_acpi.p_dev);
964b4f9fe12SLen Brown 
965b4f9fe12SLen Brown 	return;
966b4f9fe12SLen Brown }
967b4f9fe12SLen Brown 
968b4f9fe12SLen Brown static int __init toshiba_acpi_init(void)
969b4f9fe12SLen Brown {
970b4f9fe12SLen Brown 	u32 hci_result;
971b4f9fe12SLen Brown 	bool bt_present;
972b4f9fe12SLen Brown 	int ret = 0;
973a19a6ee6SMatthew Garrett 	struct backlight_properties props;
974b4f9fe12SLen Brown 
975b4f9fe12SLen Brown 	if (acpi_disabled)
976b4f9fe12SLen Brown 		return -ENODEV;
977b4f9fe12SLen Brown 
978b4f9fe12SLen Brown 	/* simple device detection: look for HCI method */
9796335e4d5SMatthew Garrett 	if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) {
9806335e4d5SMatthew Garrett 		method_hci = TOSH_INTERFACE_1 GHCI_METHOD;
9816335e4d5SMatthew Garrett 		if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1))
9826335e4d5SMatthew Garrett 			printk(MY_INFO "Unable to activate hotkeys\n");
9836335e4d5SMatthew Garrett 	} else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) {
9846335e4d5SMatthew Garrett 		method_hci = TOSH_INTERFACE_2 GHCI_METHOD;
9856335e4d5SMatthew Garrett 		if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2))
9866335e4d5SMatthew Garrett 			printk(MY_INFO "Unable to activate hotkeys\n");
9876335e4d5SMatthew Garrett 	} else
988b4f9fe12SLen Brown 		return -ENODEV;
989b4f9fe12SLen Brown 
990b4f9fe12SLen Brown 	printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n",
991b4f9fe12SLen Brown 	       TOSHIBA_ACPI_VERSION);
992b4f9fe12SLen Brown 	printk(MY_INFO "    HCI method: %s\n", method_hci);
993b4f9fe12SLen Brown 
994b4f9fe12SLen Brown 	mutex_init(&toshiba_acpi.mutex);
995b4f9fe12SLen Brown 
996b4f9fe12SLen Brown 	toshiba_acpi.p_dev = platform_device_register_simple("toshiba_acpi",
997b4f9fe12SLen Brown 							      -1, NULL, 0);
998b4f9fe12SLen Brown 	if (IS_ERR(toshiba_acpi.p_dev)) {
999b4f9fe12SLen Brown 		ret = PTR_ERR(toshiba_acpi.p_dev);
1000b4f9fe12SLen Brown 		printk(MY_ERR "unable to register platform device\n");
1001b4f9fe12SLen Brown 		toshiba_acpi.p_dev = NULL;
1002b4f9fe12SLen Brown 		toshiba_acpi_exit();
1003b4f9fe12SLen Brown 		return ret;
1004b4f9fe12SLen Brown 	}
1005b4f9fe12SLen Brown 
1006b4f9fe12SLen Brown 	force_fan = 0;
1007b4f9fe12SLen Brown 	key_event_valid = 0;
1008b4f9fe12SLen Brown 
1009b4f9fe12SLen Brown 	/* enable event fifo */
1010b4f9fe12SLen Brown 	hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
1011b4f9fe12SLen Brown 
1012b4f9fe12SLen Brown 	toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
1013b4f9fe12SLen Brown 	if (!toshiba_proc_dir) {
1014b4f9fe12SLen Brown 		toshiba_acpi_exit();
1015b4f9fe12SLen Brown 		return -ENODEV;
1016b4f9fe12SLen Brown 	} else {
1017f8ef3aecSAxel Lin 		create_toshiba_proc_entries();
1018b4f9fe12SLen Brown 	}
1019b4f9fe12SLen Brown 
1020a19a6ee6SMatthew Garrett 	props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
1021b4f9fe12SLen Brown 	toshiba_backlight_device = backlight_device_register("toshiba",
1022b4f9fe12SLen Brown 							     &toshiba_acpi.p_dev->dev,
1023b4f9fe12SLen Brown 							     NULL,
1024a19a6ee6SMatthew Garrett 							     &toshiba_backlight_data,
1025a19a6ee6SMatthew Garrett 							     &props);
1026b4f9fe12SLen Brown         if (IS_ERR(toshiba_backlight_device)) {
1027b4f9fe12SLen Brown 		ret = PTR_ERR(toshiba_backlight_device);
1028b4f9fe12SLen Brown 
1029b4f9fe12SLen Brown 		printk(KERN_ERR "Could not register toshiba backlight device\n");
1030b4f9fe12SLen Brown 		toshiba_backlight_device = NULL;
1031b4f9fe12SLen Brown 		toshiba_acpi_exit();
1032b4f9fe12SLen Brown 		return ret;
1033b4f9fe12SLen Brown 	}
1034b4f9fe12SLen Brown 
1035b4f9fe12SLen Brown 	/* Register rfkill switch for Bluetooth */
1036b4f9fe12SLen Brown 	if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) {
103719d337dfSJohannes Berg 		toshiba_acpi.bt_rfk = rfkill_alloc(toshiba_acpi.bt_name,
103819d337dfSJohannes Berg 						   &toshiba_acpi.p_dev->dev,
103919d337dfSJohannes Berg 						   RFKILL_TYPE_BLUETOOTH,
104019d337dfSJohannes Berg 						   &toshiba_rfk_ops,
104119d337dfSJohannes Berg 						   &toshiba_acpi);
104219d337dfSJohannes Berg 		if (!toshiba_acpi.bt_rfk) {
1043b4f9fe12SLen Brown 			printk(MY_ERR "unable to allocate rfkill device\n");
1044b4f9fe12SLen Brown 			toshiba_acpi_exit();
1045b4f9fe12SLen Brown 			return -ENOMEM;
1046b4f9fe12SLen Brown 		}
1047b4f9fe12SLen Brown 
104819d337dfSJohannes Berg 		ret = rfkill_register(toshiba_acpi.bt_rfk);
1049b4f9fe12SLen Brown 		if (ret) {
1050b4f9fe12SLen Brown 			printk(MY_ERR "unable to register rfkill device\n");
105119d337dfSJohannes Berg 			rfkill_destroy(toshiba_acpi.bt_rfk);
1052b4f9fe12SLen Brown 			toshiba_acpi_exit();
1053b4f9fe12SLen Brown 			return ret;
1054b4f9fe12SLen Brown 		}
1055b4f9fe12SLen Brown 	}
1056b4f9fe12SLen Brown 
10576c3f6e6cSPierre Ducroquet 	toshiba_acpi.illumination_installed = 0;
10586c3f6e6cSPierre Ducroquet 	if (toshiba_illumination_available()) {
10596c3f6e6cSPierre Ducroquet 		if (!led_classdev_register(&(toshiba_acpi.p_dev->dev),
10606c3f6e6cSPierre Ducroquet 					   &toshiba_led))
10616c3f6e6cSPierre Ducroquet 			toshiba_acpi.illumination_installed = 1;
10626c3f6e6cSPierre Ducroquet 	}
10636c3f6e6cSPierre Ducroquet 
1064b4f9fe12SLen Brown 	return 0;
1065b4f9fe12SLen Brown }
1066b4f9fe12SLen Brown 
1067b4f9fe12SLen Brown module_init(toshiba_acpi_init);
1068b4f9fe12SLen Brown module_exit(toshiba_acpi_exit);
1069