xref: /linux/drivers/platform/x86/toshiba_acpi.c (revision 9d8658acd6be9139ef91dfe6c001796e7a03ded6)
1b4f9fe12SLen Brown /*
2b4f9fe12SLen Brown  *  toshiba_acpi.c - Toshiba Laptop ACPI Extras
3b4f9fe12SLen Brown  *
4b4f9fe12SLen Brown  *
5b4f9fe12SLen Brown  *  Copyright (C) 2002-2004 John Belmonte
6b4f9fe12SLen Brown  *  Copyright (C) 2008 Philip Langdale
76c3f6e6cSPierre Ducroquet  *  Copyright (C) 2010 Pierre Ducroquet
8b4f9fe12SLen Brown  *
9b4f9fe12SLen Brown  *  This program is free software; you can redistribute it and/or modify
10b4f9fe12SLen Brown  *  it under the terms of the GNU General Public License as published by
11b4f9fe12SLen Brown  *  the Free Software Foundation; either version 2 of the License, or
12b4f9fe12SLen Brown  *  (at your option) any later version.
13b4f9fe12SLen Brown  *
14b4f9fe12SLen Brown  *  This program is distributed in the hope that it will be useful,
15b4f9fe12SLen Brown  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16b4f9fe12SLen Brown  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17b4f9fe12SLen Brown  *  GNU General Public License for more details.
18b4f9fe12SLen Brown  *
19b4f9fe12SLen Brown  *  You should have received a copy of the GNU General Public License
20b4f9fe12SLen Brown  *  along with this program; if not, write to the Free Software
21b4f9fe12SLen Brown  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22b4f9fe12SLen Brown  *
23b4f9fe12SLen Brown  *
24b4f9fe12SLen Brown  *  The devolpment page for this driver is located at
25b4f9fe12SLen Brown  *  http://memebeam.org/toys/ToshibaAcpiDriver.
26b4f9fe12SLen Brown  *
27b4f9fe12SLen Brown  *  Credits:
28b4f9fe12SLen Brown  *	Jonathan A. Buzzard - Toshiba HCI info, and critical tips on reverse
29b4f9fe12SLen Brown  *		engineering the Windows drivers
30b4f9fe12SLen Brown  *	Yasushi Nagato - changes for linux kernel 2.4 -> 2.5
31b4f9fe12SLen Brown  *	Rob Miller - TV out and hotkeys help
32b4f9fe12SLen Brown  *
33b4f9fe12SLen Brown  *
34b4f9fe12SLen Brown  *  TODO
35b4f9fe12SLen Brown  *
36b4f9fe12SLen Brown  */
37b4f9fe12SLen Brown 
387e33460dSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
397e33460dSJoe Perches 
40b4f9fe12SLen Brown #define TOSHIBA_ACPI_VERSION	"0.19"
41b4f9fe12SLen Brown #define PROC_INTERFACE_VERSION	1
42b4f9fe12SLen Brown 
43b4f9fe12SLen Brown #include <linux/kernel.h>
44b4f9fe12SLen Brown #include <linux/module.h>
45b4f9fe12SLen Brown #include <linux/init.h>
46b4f9fe12SLen Brown #include <linux/types.h>
47b4f9fe12SLen Brown #include <linux/proc_fs.h>
48936c8bcdSAlexey Dobriyan #include <linux/seq_file.h>
49b4f9fe12SLen Brown #include <linux/backlight.h>
50b4f9fe12SLen Brown #include <linux/rfkill.h>
516335e4d5SMatthew Garrett #include <linux/input.h>
52384a7cd9SDmitry Torokhov #include <linux/input/sparse-keymap.h>
536c3f6e6cSPierre Ducroquet #include <linux/leds.h>
545a0e3ad6STejun Heo #include <linux/slab.h>
5529cd293fSSeth Forshee #include <linux/workqueue.h>
5629cd293fSSeth Forshee #include <linux/i8042.h>
578b48463fSLv Zheng #include <linux/acpi.h>
58b4f9fe12SLen Brown #include <asm/uaccess.h>
59b4f9fe12SLen Brown 
60b4f9fe12SLen Brown MODULE_AUTHOR("John Belmonte");
61b4f9fe12SLen Brown MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
62b4f9fe12SLen Brown MODULE_LICENSE("GPL");
63b4f9fe12SLen Brown 
64f11f999eSSeth Forshee #define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100"
65f11f999eSSeth Forshee 
6629cd293fSSeth Forshee /* Scan code for Fn key on TOS1900 models */
6729cd293fSSeth Forshee #define TOS1900_FN_SCAN		0x6e
6829cd293fSSeth Forshee 
69b4f9fe12SLen Brown /* Toshiba ACPI method paths */
70b4f9fe12SLen Brown #define METHOD_VIDEO_OUT	"\\_SB_.VALX.DSSX"
71b4f9fe12SLen Brown 
72b4f9fe12SLen Brown /* Toshiba HCI interface definitions
73b4f9fe12SLen Brown  *
74b4f9fe12SLen Brown  * HCI is Toshiba's "Hardware Control Interface" which is supposed to
75b4f9fe12SLen Brown  * be uniform across all their models.  Ideally we would just call
76b4f9fe12SLen Brown  * dedicated ACPI methods instead of using this primitive interface.
77b4f9fe12SLen Brown  * However the ACPI methods seem to be incomplete in some areas (for
78b4f9fe12SLen Brown  * example they allow setting, but not reading, the LCD brightness value),
79b4f9fe12SLen Brown  * so this is still useful.
8084a6273fSAzael Avalos  *
8184a6273fSAzael Avalos  * SCI stands for "System Configuration Interface" which aim is to
8284a6273fSAzael Avalos  * conceal differences in hardware between different models.
83b4f9fe12SLen Brown  */
84b4f9fe12SLen Brown 
85b4f9fe12SLen Brown #define HCI_WORDS			6
86b4f9fe12SLen Brown 
87b4f9fe12SLen Brown /* operations */
88b4f9fe12SLen Brown #define HCI_SET				0xff00
89b4f9fe12SLen Brown #define HCI_GET				0xfe00
9084a6273fSAzael Avalos #define SCI_OPEN			0xf100
9184a6273fSAzael Avalos #define SCI_CLOSE			0xf200
9284a6273fSAzael Avalos #define SCI_GET				0xf300
9384a6273fSAzael Avalos #define SCI_SET				0xf400
94b4f9fe12SLen Brown 
95b4f9fe12SLen Brown /* return codes */
96b4f9fe12SLen Brown #define HCI_SUCCESS			0x0000
97b4f9fe12SLen Brown #define HCI_FAILURE			0x1000
98b4f9fe12SLen Brown #define HCI_NOT_SUPPORTED		0x8000
99b4f9fe12SLen Brown #define HCI_EMPTY			0x8c00
10084a6273fSAzael Avalos #define SCI_OPEN_CLOSE_OK		0x0044
10184a6273fSAzael Avalos #define SCI_ALREADY_OPEN		0x8100
10284a6273fSAzael Avalos #define SCI_NOT_OPENED			0x8200
103fdb79081SAzael Avalos #define SCI_INPUT_DATA_ERROR		0x8300
10484a6273fSAzael Avalos #define SCI_NOT_PRESENT			0x8600
105b4f9fe12SLen Brown 
106b4f9fe12SLen Brown /* registers */
107b4f9fe12SLen Brown #define HCI_FAN				0x0004
108121b7b0dSAkio Idehara #define HCI_TR_BACKLIGHT		0x0005
109b4f9fe12SLen Brown #define HCI_SYSTEM_EVENT		0x0016
110b4f9fe12SLen Brown #define HCI_VIDEO_OUT			0x001c
111b4f9fe12SLen Brown #define HCI_HOTKEY_EVENT		0x001e
112b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS		0x002a
113b4f9fe12SLen Brown #define HCI_WIRELESS			0x0056
114360f0f39SAzael Avalos #define HCI_KBD_ILLUMINATION		0x0095
115fdb79081SAzael Avalos #define SCI_ILLUMINATION		0x014e
116360f0f39SAzael Avalos #define SCI_KBD_ILLUM_STATUS		0x015c
117*9d8658acSAzael Avalos #define SCI_TOUCHPAD			0x050e
118b4f9fe12SLen Brown 
119b4f9fe12SLen Brown /* field definitions */
12029cd293fSSeth Forshee #define HCI_HOTKEY_DISABLE		0x0b
12129cd293fSSeth Forshee #define HCI_HOTKEY_ENABLE		0x09
122b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_BITS		3
123b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_SHIFT	(16-HCI_LCD_BRIGHTNESS_BITS)
124b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_LEVELS	(1 << HCI_LCD_BRIGHTNESS_BITS)
125360f0f39SAzael Avalos #define HCI_MISC_SHIFT			0x10
126b4f9fe12SLen Brown #define HCI_VIDEO_OUT_LCD		0x1
127b4f9fe12SLen Brown #define HCI_VIDEO_OUT_CRT		0x2
128b4f9fe12SLen Brown #define HCI_VIDEO_OUT_TV		0x4
129b4f9fe12SLen Brown #define HCI_WIRELESS_KILL_SWITCH	0x01
130b4f9fe12SLen Brown #define HCI_WIRELESS_BT_PRESENT		0x0f
131b4f9fe12SLen Brown #define HCI_WIRELESS_BT_ATTACH		0x40
132b4f9fe12SLen Brown #define HCI_WIRELESS_BT_POWER		0x80
133360f0f39SAzael Avalos #define SCI_KBD_MODE_FNZ		0x1
134360f0f39SAzael Avalos #define SCI_KBD_MODE_AUTO		0x2
135b4f9fe12SLen Brown 
136135740deSSeth Forshee struct toshiba_acpi_dev {
137135740deSSeth Forshee 	struct acpi_device *acpi_dev;
138135740deSSeth Forshee 	const char *method_hci;
139135740deSSeth Forshee 	struct rfkill *bt_rfk;
140135740deSSeth Forshee 	struct input_dev *hotkey_dev;
14129cd293fSSeth Forshee 	struct work_struct hotkey_work;
142135740deSSeth Forshee 	struct backlight_device *backlight_dev;
143135740deSSeth Forshee 	struct led_classdev led_dev;
144360f0f39SAzael Avalos 	struct led_classdev kbd_led;
14536d03f93SSeth Forshee 
146135740deSSeth Forshee 	int force_fan;
147135740deSSeth Forshee 	int last_key_event;
148135740deSSeth Forshee 	int key_event_valid;
149360f0f39SAzael Avalos 	int kbd_mode;
150360f0f39SAzael Avalos 	int kbd_time;
151135740deSSeth Forshee 
152592b746cSDan Carpenter 	unsigned int illumination_supported:1;
153592b746cSDan Carpenter 	unsigned int video_supported:1;
154592b746cSDan Carpenter 	unsigned int fan_supported:1;
155592b746cSDan Carpenter 	unsigned int system_event_supported:1;
15629cd293fSSeth Forshee 	unsigned int ntfy_supported:1;
15729cd293fSSeth Forshee 	unsigned int info_supported:1;
158121b7b0dSAkio Idehara 	unsigned int tr_backlight_supported:1;
159360f0f39SAzael Avalos 	unsigned int kbd_illum_supported:1;
160360f0f39SAzael Avalos 	unsigned int kbd_led_registered:1;
161*9d8658acSAzael Avalos 	unsigned int touchpad_supported:1;
162360f0f39SAzael Avalos 	unsigned int sysfs_created:1;
16336d03f93SSeth Forshee 
164135740deSSeth Forshee 	struct mutex mutex;
165135740deSSeth Forshee };
166135740deSSeth Forshee 
16729cd293fSSeth Forshee static struct toshiba_acpi_dev *toshiba_acpi;
16829cd293fSSeth Forshee 
169b4f9fe12SLen Brown static const struct acpi_device_id toshiba_device_ids[] = {
170b4f9fe12SLen Brown 	{"TOS6200", 0},
171b4f9fe12SLen Brown 	{"TOS6208", 0},
172b4f9fe12SLen Brown 	{"TOS1900", 0},
173b4f9fe12SLen Brown 	{"", 0},
174b4f9fe12SLen Brown };
175b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
176b4f9fe12SLen Brown 
177b859f159SGreg Kroah-Hartman static const struct key_entry toshiba_acpi_keymap[] = {
178fec278a1SUnai Uribarri 	{ KE_KEY, 0x9e, { KEY_RFKILL } },
179384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x101, { KEY_MUTE } },
180384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x102, { KEY_ZOOMOUT } },
181384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x103, { KEY_ZOOMIN } },
182af502837SAzael Avalos 	{ KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } },
183af502837SAzael Avalos 	{ KE_KEY, 0x139, { KEY_ZOOMRESET } },
184384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13b, { KEY_COFFEE } },
185384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13c, { KEY_BATTERY } },
186384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13d, { KEY_SLEEP } },
187384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13e, { KEY_SUSPEND } },
188384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } },
189384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
190384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
191384a7cd9SDmitry Torokhov 	{ KE_KEY, 0x142, { KEY_WLAN } },
192af502837SAzael Avalos 	{ KE_KEY, 0x143, { KEY_TOUCHPAD_TOGGLE } },
193a49010f5SJon Dowland 	{ KE_KEY, 0x17f, { KEY_FN } },
194384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb05, { KEY_PROG2 } },
195384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb06, { KEY_WWW } },
196384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb07, { KEY_MAIL } },
197384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb30, { KEY_STOP } },
198384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb31, { KEY_PREVIOUSSONG } },
199384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb32, { KEY_NEXTSONG } },
200384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
201384a7cd9SDmitry Torokhov 	{ KE_KEY, 0xb5a, { KEY_MEDIA } },
202af502837SAzael Avalos 	{ KE_IGNORE, 0x1430, { KEY_RESERVED } },
203384a7cd9SDmitry Torokhov 	{ KE_END, 0 },
2046335e4d5SMatthew Garrett };
2056335e4d5SMatthew Garrett 
206b4f9fe12SLen Brown /* utility
207b4f9fe12SLen Brown  */
208b4f9fe12SLen Brown 
209b4f9fe12SLen Brown static __inline__ void _set_bit(u32 * word, u32 mask, int value)
210b4f9fe12SLen Brown {
211b4f9fe12SLen Brown 	*word = (*word & ~mask) | (mask * value);
212b4f9fe12SLen Brown }
213b4f9fe12SLen Brown 
214b4f9fe12SLen Brown /* acpi interface wrappers
215b4f9fe12SLen Brown  */
216b4f9fe12SLen Brown 
217b4f9fe12SLen Brown static int write_acpi_int(const char *methodName, int val)
218b4f9fe12SLen Brown {
219b4f9fe12SLen Brown 	acpi_status status;
220b4f9fe12SLen Brown 
221619400daSZhang Rui 	status = acpi_execute_simple_method(NULL, (char *)methodName, val);
22232bcd5cbSSeth Forshee 	return (status == AE_OK) ? 0 : -EIO;
223b4f9fe12SLen Brown }
224b4f9fe12SLen Brown 
225b4f9fe12SLen Brown /* Perform a raw HCI call.  Here we don't care about input or output buffer
226b4f9fe12SLen Brown  * format.
227b4f9fe12SLen Brown  */
228135740deSSeth Forshee static acpi_status hci_raw(struct toshiba_acpi_dev *dev,
229135740deSSeth Forshee 			   const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
230b4f9fe12SLen Brown {
231b4f9fe12SLen Brown 	struct acpi_object_list params;
232b4f9fe12SLen Brown 	union acpi_object in_objs[HCI_WORDS];
233b4f9fe12SLen Brown 	struct acpi_buffer results;
234b4f9fe12SLen Brown 	union acpi_object out_objs[HCI_WORDS + 1];
235b4f9fe12SLen Brown 	acpi_status status;
236b4f9fe12SLen Brown 	int i;
237b4f9fe12SLen Brown 
238b4f9fe12SLen Brown 	params.count = HCI_WORDS;
239b4f9fe12SLen Brown 	params.pointer = in_objs;
240b4f9fe12SLen Brown 	for (i = 0; i < HCI_WORDS; ++i) {
241b4f9fe12SLen Brown 		in_objs[i].type = ACPI_TYPE_INTEGER;
242b4f9fe12SLen Brown 		in_objs[i].integer.value = in[i];
243b4f9fe12SLen Brown 	}
244b4f9fe12SLen Brown 
245b4f9fe12SLen Brown 	results.length = sizeof(out_objs);
246b4f9fe12SLen Brown 	results.pointer = out_objs;
247b4f9fe12SLen Brown 
2486e02cc7eSSeth Forshee 	status = acpi_evaluate_object(dev->acpi_dev->handle,
2496e02cc7eSSeth Forshee 				      (char *)dev->method_hci, &params,
250b4f9fe12SLen Brown 				      &results);
251b4f9fe12SLen Brown 	if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) {
252b4f9fe12SLen Brown 		for (i = 0; i < out_objs->package.count; ++i) {
253b4f9fe12SLen Brown 			out[i] = out_objs->package.elements[i].integer.value;
254b4f9fe12SLen Brown 		}
255b4f9fe12SLen Brown 	}
256b4f9fe12SLen Brown 
257b4f9fe12SLen Brown 	return status;
258b4f9fe12SLen Brown }
259b4f9fe12SLen Brown 
260b4f9fe12SLen Brown /* common hci tasks (get or set one or two value)
261b4f9fe12SLen Brown  *
262b4f9fe12SLen Brown  * In addition to the ACPI status, the HCI system returns a result which
263b4f9fe12SLen Brown  * may be useful (such as "not supported").
264b4f9fe12SLen Brown  */
265b4f9fe12SLen Brown 
266135740deSSeth Forshee static acpi_status hci_write1(struct toshiba_acpi_dev *dev, u32 reg,
267135740deSSeth Forshee 			      u32 in1, u32 *result)
268b4f9fe12SLen Brown {
269b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 };
270b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
271135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
272b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
273b4f9fe12SLen Brown 	return status;
274b4f9fe12SLen Brown }
275b4f9fe12SLen Brown 
276135740deSSeth Forshee static acpi_status hci_read1(struct toshiba_acpi_dev *dev, u32 reg,
277135740deSSeth Forshee 			     u32 *out1, u32 *result)
278b4f9fe12SLen Brown {
279b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 };
280b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
281135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
282b4f9fe12SLen Brown 	*out1 = out[2];
283b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
284b4f9fe12SLen Brown 	return status;
285b4f9fe12SLen Brown }
286b4f9fe12SLen Brown 
287135740deSSeth Forshee static acpi_status hci_write2(struct toshiba_acpi_dev *dev, u32 reg,
288135740deSSeth Forshee 			      u32 in1, u32 in2, u32 *result)
289b4f9fe12SLen Brown {
290b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 };
291b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
292135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
293b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
294b4f9fe12SLen Brown 	return status;
295b4f9fe12SLen Brown }
296b4f9fe12SLen Brown 
297135740deSSeth Forshee static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg,
298135740deSSeth Forshee 			     u32 *out1, u32 *out2, u32 *result)
299b4f9fe12SLen Brown {
300b4f9fe12SLen Brown 	u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 };
301b4f9fe12SLen Brown 	u32 out[HCI_WORDS];
302135740deSSeth Forshee 	acpi_status status = hci_raw(dev, in, out);
303b4f9fe12SLen Brown 	*out1 = out[2];
304b4f9fe12SLen Brown 	*out2 = out[3];
305b4f9fe12SLen Brown 	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
306b4f9fe12SLen Brown 	return status;
307b4f9fe12SLen Brown }
308b4f9fe12SLen Brown 
30984a6273fSAzael Avalos /* common sci tasks
31084a6273fSAzael Avalos  */
31184a6273fSAzael Avalos 
31284a6273fSAzael Avalos static int sci_open(struct toshiba_acpi_dev *dev)
31384a6273fSAzael Avalos {
31484a6273fSAzael Avalos 	u32 in[HCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 };
31584a6273fSAzael Avalos 	u32 out[HCI_WORDS];
31684a6273fSAzael Avalos 	acpi_status status;
31784a6273fSAzael Avalos 
31884a6273fSAzael Avalos 	status = hci_raw(dev, in, out);
31984a6273fSAzael Avalos 	if  (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
32084a6273fSAzael Avalos 		pr_err("ACPI call to open SCI failed\n");
32184a6273fSAzael Avalos 		return 0;
32284a6273fSAzael Avalos 	}
32384a6273fSAzael Avalos 
32484a6273fSAzael Avalos 	if (out[0] == SCI_OPEN_CLOSE_OK) {
32584a6273fSAzael Avalos 		return 1;
32684a6273fSAzael Avalos 	} else if (out[0] == SCI_ALREADY_OPEN) {
32784a6273fSAzael Avalos 		pr_info("Toshiba SCI already opened\n");
32884a6273fSAzael Avalos 		return 1;
32984a6273fSAzael Avalos 	} else if (out[0] == SCI_NOT_PRESENT) {
33084a6273fSAzael Avalos 		pr_info("Toshiba SCI is not present\n");
33184a6273fSAzael Avalos 	}
33284a6273fSAzael Avalos 
33384a6273fSAzael Avalos 	return 0;
33484a6273fSAzael Avalos }
33584a6273fSAzael Avalos 
33684a6273fSAzael Avalos static void sci_close(struct toshiba_acpi_dev *dev)
33784a6273fSAzael Avalos {
33884a6273fSAzael Avalos 	u32 in[HCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 };
33984a6273fSAzael Avalos 	u32 out[HCI_WORDS];
34084a6273fSAzael Avalos 	acpi_status status;
34184a6273fSAzael Avalos 
34284a6273fSAzael Avalos 	status = hci_raw(dev, in, out);
34384a6273fSAzael Avalos 	if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
34484a6273fSAzael Avalos 		pr_err("ACPI call to close SCI failed\n");
34584a6273fSAzael Avalos 		return;
34684a6273fSAzael Avalos 	}
34784a6273fSAzael Avalos 
34884a6273fSAzael Avalos 	if (out[0] == SCI_OPEN_CLOSE_OK)
34984a6273fSAzael Avalos 		return;
35084a6273fSAzael Avalos 	else if (out[0] == SCI_NOT_OPENED)
35184a6273fSAzael Avalos 		pr_info("Toshiba SCI not opened\n");
35284a6273fSAzael Avalos 	else if (out[0] == SCI_NOT_PRESENT)
35384a6273fSAzael Avalos 		pr_info("Toshiba SCI is not present\n");
35484a6273fSAzael Avalos }
35584a6273fSAzael Avalos 
35684a6273fSAzael Avalos static acpi_status sci_read(struct toshiba_acpi_dev *dev, u32 reg,
35784a6273fSAzael Avalos 			    u32 *out1, u32 *result)
35884a6273fSAzael Avalos {
35984a6273fSAzael Avalos 	u32 in[HCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 };
36084a6273fSAzael Avalos 	u32 out[HCI_WORDS];
36184a6273fSAzael Avalos 	acpi_status status = hci_raw(dev, in, out);
36284a6273fSAzael Avalos 	*out1 = out[2];
36384a6273fSAzael Avalos 	*result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE;
36484a6273fSAzael Avalos 	return status;
36584a6273fSAzael Avalos }
36684a6273fSAzael Avalos 
36784a6273fSAzael Avalos static acpi_status sci_write(struct toshiba_acpi_dev *dev, u32 reg,
36884a6273fSAzael Avalos 			     u32 in1, u32 *result)
36984a6273fSAzael Avalos {
37084a6273fSAzael Avalos 	u32 in[HCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 };
37184a6273fSAzael Avalos 	u32 out[HCI_WORDS];
37284a6273fSAzael Avalos 	acpi_status status = hci_raw(dev, in, out);
37384a6273fSAzael Avalos 	*result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE;
37484a6273fSAzael Avalos 	return status;
37584a6273fSAzael Avalos }
37684a6273fSAzael Avalos 
3776c3f6e6cSPierre Ducroquet /* Illumination support */
378135740deSSeth Forshee static int toshiba_illumination_available(struct toshiba_acpi_dev *dev)
3796c3f6e6cSPierre Ducroquet {
380fdb79081SAzael Avalos 	u32 in[HCI_WORDS] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 };
3816c3f6e6cSPierre Ducroquet 	u32 out[HCI_WORDS];
3826c3f6e6cSPierre Ducroquet 	acpi_status status;
3836c3f6e6cSPierre Ducroquet 
384fdb79081SAzael Avalos 	if (!sci_open(dev))
385fdb79081SAzael Avalos 		return 0;
386fdb79081SAzael Avalos 
387135740deSSeth Forshee 	status = hci_raw(dev, in, out);
388fdb79081SAzael Avalos 	sci_close(dev);
389fdb79081SAzael Avalos 	if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
390fdb79081SAzael Avalos 		pr_err("ACPI call to query Illumination support failed\n");
391fdb79081SAzael Avalos 		return 0;
392fdb79081SAzael Avalos 	} else if (out[0] == HCI_NOT_SUPPORTED || out[1] != 1) {
3937e33460dSJoe Perches 		pr_info("Illumination device not available\n");
3946c3f6e6cSPierre Ducroquet 		return 0;
3956c3f6e6cSPierre Ducroquet 	}
396fdb79081SAzael Avalos 
3976c3f6e6cSPierre Ducroquet 	return 1;
3986c3f6e6cSPierre Ducroquet }
3996c3f6e6cSPierre Ducroquet 
4006c3f6e6cSPierre Ducroquet static void toshiba_illumination_set(struct led_classdev *cdev,
4016c3f6e6cSPierre Ducroquet 				     enum led_brightness brightness)
4026c3f6e6cSPierre Ducroquet {
403135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = container_of(cdev,
404135740deSSeth Forshee 			struct toshiba_acpi_dev, led_dev);
405fdb79081SAzael Avalos 	u32 state, result;
4066c3f6e6cSPierre Ducroquet 	acpi_status status;
4076c3f6e6cSPierre Ducroquet 
4086c3f6e6cSPierre Ducroquet 	/* First request : initialize communication. */
409fdb79081SAzael Avalos 	if (!sci_open(dev))
4106c3f6e6cSPierre Ducroquet 		return;
4116c3f6e6cSPierre Ducroquet 
412fdb79081SAzael Avalos 	/* Switch the illumination on/off */
413fdb79081SAzael Avalos 	state = brightness ? 1 : 0;
414fdb79081SAzael Avalos 	status = sci_write(dev, SCI_ILLUMINATION, state, &result);
415fdb79081SAzael Avalos 	sci_close(dev);
4166c3f6e6cSPierre Ducroquet 	if (ACPI_FAILURE(status)) {
417fdb79081SAzael Avalos 		pr_err("ACPI call for illumination failed\n");
418fdb79081SAzael Avalos 		return;
419fdb79081SAzael Avalos 	} else if (result == HCI_NOT_SUPPORTED) {
420fdb79081SAzael Avalos 		pr_info("Illumination not supported\n");
4216c3f6e6cSPierre Ducroquet 		return;
4226c3f6e6cSPierre Ducroquet 	}
4236c3f6e6cSPierre Ducroquet }
4246c3f6e6cSPierre Ducroquet 
4256c3f6e6cSPierre Ducroquet static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
4266c3f6e6cSPierre Ducroquet {
427135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = container_of(cdev,
428135740deSSeth Forshee 			struct toshiba_acpi_dev, led_dev);
429fdb79081SAzael Avalos 	u32 state, result;
4306c3f6e6cSPierre Ducroquet 	acpi_status status;
4316c3f6e6cSPierre Ducroquet 
4326c3f6e6cSPierre Ducroquet 	/* First request : initialize communication. */
433fdb79081SAzael Avalos 	if (!sci_open(dev))
4346c3f6e6cSPierre Ducroquet 		return LED_OFF;
4356c3f6e6cSPierre Ducroquet 
4366c3f6e6cSPierre Ducroquet 	/* Check the illumination */
437fdb79081SAzael Avalos 	status = sci_read(dev, SCI_ILLUMINATION, &state, &result);
438fdb79081SAzael Avalos 	sci_close(dev);
439fdb79081SAzael Avalos 	if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
440fdb79081SAzael Avalos 		pr_err("ACPI call for illumination failed\n");
441fdb79081SAzael Avalos 		return LED_OFF;
442fdb79081SAzael Avalos 	} else if (result == HCI_NOT_SUPPORTED) {
443fdb79081SAzael Avalos 		pr_info("Illumination not supported\n");
4446c3f6e6cSPierre Ducroquet 		return LED_OFF;
4456c3f6e6cSPierre Ducroquet 	}
4466c3f6e6cSPierre Ducroquet 
447fdb79081SAzael Avalos 	return state ? LED_FULL : LED_OFF;
4486c3f6e6cSPierre Ducroquet }
4496c3f6e6cSPierre Ducroquet 
450360f0f39SAzael Avalos /* KBD Illumination */
451360f0f39SAzael Avalos static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time)
452360f0f39SAzael Avalos {
453360f0f39SAzael Avalos 	u32 result;
454360f0f39SAzael Avalos 	acpi_status status;
455360f0f39SAzael Avalos 
456360f0f39SAzael Avalos 	if (!sci_open(dev))
457360f0f39SAzael Avalos 		return -EIO;
458360f0f39SAzael Avalos 
459360f0f39SAzael Avalos 	status = sci_write(dev, SCI_KBD_ILLUM_STATUS, time, &result);
460360f0f39SAzael Avalos 	sci_close(dev);
461360f0f39SAzael Avalos 	if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
462360f0f39SAzael Avalos 		pr_err("ACPI call to set KBD backlight status failed\n");
463360f0f39SAzael Avalos 		return -EIO;
464360f0f39SAzael Avalos 	} else if (result == HCI_NOT_SUPPORTED) {
465360f0f39SAzael Avalos 		pr_info("Keyboard backlight status not supported\n");
466360f0f39SAzael Avalos 		return -ENODEV;
467360f0f39SAzael Avalos 	}
468360f0f39SAzael Avalos 
469360f0f39SAzael Avalos 	return 0;
470360f0f39SAzael Avalos }
471360f0f39SAzael Avalos 
472360f0f39SAzael Avalos static int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time)
473360f0f39SAzael Avalos {
474360f0f39SAzael Avalos 	u32 result;
475360f0f39SAzael Avalos 	acpi_status status;
476360f0f39SAzael Avalos 
477360f0f39SAzael Avalos 	if (!sci_open(dev))
478360f0f39SAzael Avalos 		return -EIO;
479360f0f39SAzael Avalos 
480360f0f39SAzael Avalos 	status = sci_read(dev, SCI_KBD_ILLUM_STATUS, time, &result);
481360f0f39SAzael Avalos 	sci_close(dev);
482360f0f39SAzael Avalos 	if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
483360f0f39SAzael Avalos 		pr_err("ACPI call to get KBD backlight status failed\n");
484360f0f39SAzael Avalos 		return -EIO;
485360f0f39SAzael Avalos 	} else if (result == HCI_NOT_SUPPORTED) {
486360f0f39SAzael Avalos 		pr_info("Keyboard backlight status not supported\n");
487360f0f39SAzael Avalos 		return -ENODEV;
488360f0f39SAzael Avalos 	}
489360f0f39SAzael Avalos 
490360f0f39SAzael Avalos 	return 0;
491360f0f39SAzael Avalos }
492360f0f39SAzael Avalos 
493360f0f39SAzael Avalos static enum led_brightness toshiba_kbd_backlight_get(struct led_classdev *cdev)
494360f0f39SAzael Avalos {
495360f0f39SAzael Avalos 	struct toshiba_acpi_dev *dev = container_of(cdev,
496360f0f39SAzael Avalos 			struct toshiba_acpi_dev, kbd_led);
497360f0f39SAzael Avalos 	u32 state, result;
498360f0f39SAzael Avalos 	acpi_status status;
499360f0f39SAzael Avalos 
500360f0f39SAzael Avalos 	/* Check the keyboard backlight state */
501360f0f39SAzael Avalos 	status = hci_read1(dev, HCI_KBD_ILLUMINATION, &state, &result);
502360f0f39SAzael Avalos 	if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
503360f0f39SAzael Avalos 		pr_err("ACPI call to get the keyboard backlight failed\n");
504360f0f39SAzael Avalos 		return LED_OFF;
505360f0f39SAzael Avalos 	} else if (result == HCI_NOT_SUPPORTED) {
506360f0f39SAzael Avalos 		pr_info("Keyboard backlight not supported\n");
507360f0f39SAzael Avalos 		return LED_OFF;
508360f0f39SAzael Avalos 	}
509360f0f39SAzael Avalos 
510360f0f39SAzael Avalos 	return state ? LED_FULL : LED_OFF;
511360f0f39SAzael Avalos }
512360f0f39SAzael Avalos 
513360f0f39SAzael Avalos static void toshiba_kbd_backlight_set(struct led_classdev *cdev,
514360f0f39SAzael Avalos 				     enum led_brightness brightness)
515360f0f39SAzael Avalos {
516360f0f39SAzael Avalos 	struct toshiba_acpi_dev *dev = container_of(cdev,
517360f0f39SAzael Avalos 			struct toshiba_acpi_dev, kbd_led);
518360f0f39SAzael Avalos 	u32 state, result;
519360f0f39SAzael Avalos 	acpi_status status;
520360f0f39SAzael Avalos 
521360f0f39SAzael Avalos 	/* Set the keyboard backlight state */
522360f0f39SAzael Avalos 	state = brightness ? 1 : 0;
523360f0f39SAzael Avalos 	status = hci_write1(dev, HCI_KBD_ILLUMINATION, state, &result);
524360f0f39SAzael Avalos 	if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
525360f0f39SAzael Avalos 		pr_err("ACPI call to set KBD Illumination mode failed\n");
526360f0f39SAzael Avalos 		return;
527360f0f39SAzael Avalos 	} else if (result == HCI_NOT_SUPPORTED) {
528360f0f39SAzael Avalos 		pr_info("Keyboard backlight not supported\n");
529360f0f39SAzael Avalos 		return;
530360f0f39SAzael Avalos 	}
531360f0f39SAzael Avalos }
532360f0f39SAzael Avalos 
533*9d8658acSAzael Avalos /* TouchPad support */
534*9d8658acSAzael Avalos static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state)
535*9d8658acSAzael Avalos {
536*9d8658acSAzael Avalos 	u32 result;
537*9d8658acSAzael Avalos 	acpi_status status;
538*9d8658acSAzael Avalos 
539*9d8658acSAzael Avalos 	if (!sci_open(dev))
540*9d8658acSAzael Avalos 		return -EIO;
541*9d8658acSAzael Avalos 
542*9d8658acSAzael Avalos 	status = sci_write(dev, SCI_TOUCHPAD, state, &result);
543*9d8658acSAzael Avalos 	sci_close(dev);
544*9d8658acSAzael Avalos 	if (ACPI_FAILURE(status)) {
545*9d8658acSAzael Avalos 		pr_err("ACPI call to set the touchpad failed\n");
546*9d8658acSAzael Avalos 		return -EIO;
547*9d8658acSAzael Avalos 	} else if (result == HCI_NOT_SUPPORTED) {
548*9d8658acSAzael Avalos 		return -ENODEV;
549*9d8658acSAzael Avalos 	}
550*9d8658acSAzael Avalos 
551*9d8658acSAzael Avalos 	return 0;
552*9d8658acSAzael Avalos }
553*9d8658acSAzael Avalos 
554*9d8658acSAzael Avalos static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state)
555*9d8658acSAzael Avalos {
556*9d8658acSAzael Avalos 	u32 result;
557*9d8658acSAzael Avalos 	acpi_status status;
558*9d8658acSAzael Avalos 
559*9d8658acSAzael Avalos 	if (!sci_open(dev))
560*9d8658acSAzael Avalos 		return -EIO;
561*9d8658acSAzael Avalos 
562*9d8658acSAzael Avalos 	status = sci_read(dev, SCI_TOUCHPAD, state, &result);
563*9d8658acSAzael Avalos 	sci_close(dev);
564*9d8658acSAzael Avalos 	if (ACPI_FAILURE(status)) {
565*9d8658acSAzael Avalos 		pr_err("ACPI call to query the touchpad failed\n");
566*9d8658acSAzael Avalos 		return -EIO;
567*9d8658acSAzael Avalos 	} else if (result == HCI_NOT_SUPPORTED) {
568*9d8658acSAzael Avalos 		return -ENODEV;
569*9d8658acSAzael Avalos 	}
570*9d8658acSAzael Avalos 
571*9d8658acSAzael Avalos 	return 0;
572*9d8658acSAzael Avalos }
573*9d8658acSAzael Avalos 
574b4f9fe12SLen Brown /* Bluetooth rfkill handlers */
575b4f9fe12SLen Brown 
576135740deSSeth Forshee static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present)
577b4f9fe12SLen Brown {
578b4f9fe12SLen Brown 	u32 hci_result;
579b4f9fe12SLen Brown 	u32 value, value2;
580b4f9fe12SLen Brown 
581b4f9fe12SLen Brown 	value = 0;
582b4f9fe12SLen Brown 	value2 = 0;
583135740deSSeth Forshee 	hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result);
584b4f9fe12SLen Brown 	if (hci_result == HCI_SUCCESS)
585b4f9fe12SLen Brown 		*present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false;
586b4f9fe12SLen Brown 
587b4f9fe12SLen Brown 	return hci_result;
588b4f9fe12SLen Brown }
589b4f9fe12SLen Brown 
590135740deSSeth Forshee static u32 hci_get_radio_state(struct toshiba_acpi_dev *dev, bool *radio_state)
591b4f9fe12SLen Brown {
592b4f9fe12SLen Brown 	u32 hci_result;
593b4f9fe12SLen Brown 	u32 value, value2;
594b4f9fe12SLen Brown 
595b4f9fe12SLen Brown 	value = 0;
596b4f9fe12SLen Brown 	value2 = 0x0001;
597135740deSSeth Forshee 	hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result);
598b4f9fe12SLen Brown 
599b4f9fe12SLen Brown 	*radio_state = value & HCI_WIRELESS_KILL_SWITCH;
600b4f9fe12SLen Brown 	return hci_result;
601b4f9fe12SLen Brown }
602b4f9fe12SLen Brown 
60319d337dfSJohannes Berg static int bt_rfkill_set_block(void *data, bool blocked)
604b4f9fe12SLen Brown {
60519d337dfSJohannes Berg 	struct toshiba_acpi_dev *dev = data;
606b4f9fe12SLen Brown 	u32 result1, result2;
607b4f9fe12SLen Brown 	u32 value;
60819d337dfSJohannes Berg 	int err;
609b4f9fe12SLen Brown 	bool radio_state;
610b4f9fe12SLen Brown 
61119d337dfSJohannes Berg 	value = (blocked == false);
612b4f9fe12SLen Brown 
613b4f9fe12SLen Brown 	mutex_lock(&dev->mutex);
614135740deSSeth Forshee 	if (hci_get_radio_state(dev, &radio_state) != HCI_SUCCESS) {
61532bcd5cbSSeth Forshee 		err = -EIO;
61619d337dfSJohannes Berg 		goto out;
617b4f9fe12SLen Brown 	}
618b4f9fe12SLen Brown 
61919d337dfSJohannes Berg 	if (!radio_state) {
62019d337dfSJohannes Berg 		err = 0;
62119d337dfSJohannes Berg 		goto out;
62219d337dfSJohannes Berg 	}
62319d337dfSJohannes Berg 
624135740deSSeth Forshee 	hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
625135740deSSeth Forshee 	hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2);
62619d337dfSJohannes Berg 
62719d337dfSJohannes Berg 	if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
62832bcd5cbSSeth Forshee 		err = -EIO;
62919d337dfSJohannes Berg 	else
63019d337dfSJohannes Berg 		err = 0;
63119d337dfSJohannes Berg  out:
63219d337dfSJohannes Berg 	mutex_unlock(&dev->mutex);
63319d337dfSJohannes Berg 	return err;
63419d337dfSJohannes Berg }
63519d337dfSJohannes Berg 
63619d337dfSJohannes Berg static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
637b4f9fe12SLen Brown {
638b4f9fe12SLen Brown 	bool new_rfk_state;
639b4f9fe12SLen Brown 	bool value;
640b4f9fe12SLen Brown 	u32 hci_result;
64119d337dfSJohannes Berg 	struct toshiba_acpi_dev *dev = data;
64219d337dfSJohannes Berg 
64319d337dfSJohannes Berg 	mutex_lock(&dev->mutex);
644b4f9fe12SLen Brown 
645135740deSSeth Forshee 	hci_result = hci_get_radio_state(dev, &value);
64619d337dfSJohannes Berg 	if (hci_result != HCI_SUCCESS) {
64719d337dfSJohannes Berg 		/* Can't do anything useful */
64819d337dfSJohannes Berg 		mutex_unlock(&dev->mutex);
64982e7784fSJiri Slaby 		return;
65019d337dfSJohannes Berg 	}
651b4f9fe12SLen Brown 
652b4f9fe12SLen Brown 	new_rfk_state = value;
653b4f9fe12SLen Brown 
654b4f9fe12SLen Brown 	mutex_unlock(&dev->mutex);
655b4f9fe12SLen Brown 
65619d337dfSJohannes Berg 	if (rfkill_set_hw_state(rfkill, !new_rfk_state))
65719d337dfSJohannes Berg 		bt_rfkill_set_block(data, true);
658b4f9fe12SLen Brown }
65919d337dfSJohannes Berg 
66019d337dfSJohannes Berg static const struct rfkill_ops toshiba_rfk_ops = {
66119d337dfSJohannes Berg 	.set_block = bt_rfkill_set_block,
66219d337dfSJohannes Berg 	.poll = bt_rfkill_poll,
66319d337dfSJohannes Berg };
664b4f9fe12SLen Brown 
665121b7b0dSAkio Idehara static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, bool *enabled)
666121b7b0dSAkio Idehara {
667121b7b0dSAkio Idehara 	u32 hci_result;
668121b7b0dSAkio Idehara 	u32 status;
669121b7b0dSAkio Idehara 
670121b7b0dSAkio Idehara 	hci_read1(dev, HCI_TR_BACKLIGHT, &status, &hci_result);
671121b7b0dSAkio Idehara 	*enabled = !status;
672121b7b0dSAkio Idehara 	return hci_result == HCI_SUCCESS ? 0 : -EIO;
673121b7b0dSAkio Idehara }
674121b7b0dSAkio Idehara 
675121b7b0dSAkio Idehara static int set_tr_backlight_status(struct toshiba_acpi_dev *dev, bool enable)
676121b7b0dSAkio Idehara {
677121b7b0dSAkio Idehara 	u32 hci_result;
678121b7b0dSAkio Idehara 	u32 value = !enable;
679121b7b0dSAkio Idehara 
680121b7b0dSAkio Idehara 	hci_write1(dev, HCI_TR_BACKLIGHT, value, &hci_result);
681121b7b0dSAkio Idehara 	return hci_result == HCI_SUCCESS ? 0 : -EIO;
682121b7b0dSAkio Idehara }
683121b7b0dSAkio Idehara 
684b4f9fe12SLen Brown static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
685b4f9fe12SLen Brown 
68662cce752SSeth Forshee static int __get_lcd_brightness(struct toshiba_acpi_dev *dev)
687b4f9fe12SLen Brown {
688b4f9fe12SLen Brown 	u32 hci_result;
689b4f9fe12SLen Brown 	u32 value;
690121b7b0dSAkio Idehara 	int brightness = 0;
691121b7b0dSAkio Idehara 
692121b7b0dSAkio Idehara 	if (dev->tr_backlight_supported) {
693121b7b0dSAkio Idehara 		bool enabled;
694121b7b0dSAkio Idehara 		int ret = get_tr_backlight_status(dev, &enabled);
695121b7b0dSAkio Idehara 		if (ret)
696121b7b0dSAkio Idehara 			return ret;
697121b7b0dSAkio Idehara 		if (enabled)
698121b7b0dSAkio Idehara 			return 0;
699121b7b0dSAkio Idehara 		brightness++;
700121b7b0dSAkio Idehara 	}
701b4f9fe12SLen Brown 
702135740deSSeth Forshee 	hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result);
70332bcd5cbSSeth Forshee 	if (hci_result == HCI_SUCCESS)
704121b7b0dSAkio Idehara 		return brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT);
70532bcd5cbSSeth Forshee 
70632bcd5cbSSeth Forshee 	return -EIO;
707b4f9fe12SLen Brown }
708b4f9fe12SLen Brown 
70962cce752SSeth Forshee static int get_lcd_brightness(struct backlight_device *bd)
71062cce752SSeth Forshee {
71162cce752SSeth Forshee 	struct toshiba_acpi_dev *dev = bl_get_data(bd);
71262cce752SSeth Forshee 	return __get_lcd_brightness(dev);
71362cce752SSeth Forshee }
71462cce752SSeth Forshee 
715936c8bcdSAlexey Dobriyan static int lcd_proc_show(struct seq_file *m, void *v)
716b4f9fe12SLen Brown {
717135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
718135740deSSeth Forshee 	int value;
719121b7b0dSAkio Idehara 	int levels;
720b4f9fe12SLen Brown 
721135740deSSeth Forshee 	if (!dev->backlight_dev)
722135740deSSeth Forshee 		return -ENODEV;
723135740deSSeth Forshee 
724121b7b0dSAkio Idehara 	levels = dev->backlight_dev->props.max_brightness + 1;
72562cce752SSeth Forshee 	value = get_lcd_brightness(dev->backlight_dev);
726b4f9fe12SLen Brown 	if (value >= 0) {
727936c8bcdSAlexey Dobriyan 		seq_printf(m, "brightness:              %d\n", value);
728121b7b0dSAkio Idehara 		seq_printf(m, "brightness_levels:       %d\n", levels);
72932bcd5cbSSeth Forshee 		return 0;
730b4f9fe12SLen Brown 	}
731b4f9fe12SLen Brown 
73232bcd5cbSSeth Forshee 	pr_err("Error reading LCD brightness\n");
73332bcd5cbSSeth Forshee 	return -EIO;
734936c8bcdSAlexey Dobriyan }
735936c8bcdSAlexey Dobriyan 
736936c8bcdSAlexey Dobriyan static int lcd_proc_open(struct inode *inode, struct file *file)
737936c8bcdSAlexey Dobriyan {
738d9dda78bSAl Viro 	return single_open(file, lcd_proc_show, PDE_DATA(inode));
739b4f9fe12SLen Brown }
740b4f9fe12SLen Brown 
74162cce752SSeth Forshee static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value)
742b4f9fe12SLen Brown {
743b4f9fe12SLen Brown 	u32 hci_result;
744b4f9fe12SLen Brown 
745121b7b0dSAkio Idehara 	if (dev->tr_backlight_supported) {
746121b7b0dSAkio Idehara 		bool enable = !value;
747121b7b0dSAkio Idehara 		int ret = set_tr_backlight_status(dev, enable);
748121b7b0dSAkio Idehara 		if (ret)
749121b7b0dSAkio Idehara 			return ret;
750121b7b0dSAkio Idehara 		if (value)
751121b7b0dSAkio Idehara 			value--;
752121b7b0dSAkio Idehara 	}
753121b7b0dSAkio Idehara 
754b4f9fe12SLen Brown 	value = value << HCI_LCD_BRIGHTNESS_SHIFT;
755135740deSSeth Forshee 	hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result);
75632bcd5cbSSeth Forshee 	return hci_result == HCI_SUCCESS ? 0 : -EIO;
757b4f9fe12SLen Brown }
758b4f9fe12SLen Brown 
759b4f9fe12SLen Brown static int set_lcd_status(struct backlight_device *bd)
760b4f9fe12SLen Brown {
761135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = bl_get_data(bd);
76262cce752SSeth Forshee 	return set_lcd_brightness(dev, bd->props.brightness);
763b4f9fe12SLen Brown }
764b4f9fe12SLen Brown 
765936c8bcdSAlexey Dobriyan static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
766936c8bcdSAlexey Dobriyan 			      size_t count, loff_t *pos)
767b4f9fe12SLen Brown {
768d9dda78bSAl Viro 	struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
769936c8bcdSAlexey Dobriyan 	char cmd[42];
770936c8bcdSAlexey Dobriyan 	size_t len;
771b4f9fe12SLen Brown 	int value;
772b4f9fe12SLen Brown 	int ret;
773121b7b0dSAkio Idehara 	int levels = dev->backlight_dev->props.max_brightness + 1;
774b4f9fe12SLen Brown 
775936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
776936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
777936c8bcdSAlexey Dobriyan 		return -EFAULT;
778936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
779936c8bcdSAlexey Dobriyan 
780936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " brightness : %i", &value) == 1 &&
781121b7b0dSAkio Idehara 	    value >= 0 && value < levels) {
78262cce752SSeth Forshee 		ret = set_lcd_brightness(dev, value);
783b4f9fe12SLen Brown 		if (ret == 0)
784b4f9fe12SLen Brown 			ret = count;
785b4f9fe12SLen Brown 	} else {
786b4f9fe12SLen Brown 		ret = -EINVAL;
787b4f9fe12SLen Brown 	}
788b4f9fe12SLen Brown 	return ret;
789b4f9fe12SLen Brown }
790b4f9fe12SLen Brown 
791936c8bcdSAlexey Dobriyan static const struct file_operations lcd_proc_fops = {
792936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
793936c8bcdSAlexey Dobriyan 	.open		= lcd_proc_open,
794936c8bcdSAlexey Dobriyan 	.read		= seq_read,
795936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
796936c8bcdSAlexey Dobriyan 	.release	= single_release,
797936c8bcdSAlexey Dobriyan 	.write		= lcd_proc_write,
798936c8bcdSAlexey Dobriyan };
799936c8bcdSAlexey Dobriyan 
80036d03f93SSeth Forshee static int get_video_status(struct toshiba_acpi_dev *dev, u32 *status)
80136d03f93SSeth Forshee {
80236d03f93SSeth Forshee 	u32 hci_result;
80336d03f93SSeth Forshee 
80436d03f93SSeth Forshee 	hci_read1(dev, HCI_VIDEO_OUT, status, &hci_result);
80536d03f93SSeth Forshee 	return hci_result == HCI_SUCCESS ? 0 : -EIO;
80636d03f93SSeth Forshee }
80736d03f93SSeth Forshee 
808936c8bcdSAlexey Dobriyan static int video_proc_show(struct seq_file *m, void *v)
809b4f9fe12SLen Brown {
810135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
811b4f9fe12SLen Brown 	u32 value;
81236d03f93SSeth Forshee 	int ret;
813b4f9fe12SLen Brown 
81436d03f93SSeth Forshee 	ret = get_video_status(dev, &value);
81536d03f93SSeth Forshee 	if (!ret) {
816b4f9fe12SLen Brown 		int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
817b4f9fe12SLen Brown 		int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
818b4f9fe12SLen Brown 		int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
819936c8bcdSAlexey Dobriyan 		seq_printf(m, "lcd_out:                 %d\n", is_lcd);
820936c8bcdSAlexey Dobriyan 		seq_printf(m, "crt_out:                 %d\n", is_crt);
821936c8bcdSAlexey Dobriyan 		seq_printf(m, "tv_out:                  %d\n", is_tv);
822b4f9fe12SLen Brown 	}
823b4f9fe12SLen Brown 
82436d03f93SSeth Forshee 	return ret;
825b4f9fe12SLen Brown }
826b4f9fe12SLen Brown 
827936c8bcdSAlexey Dobriyan static int video_proc_open(struct inode *inode, struct file *file)
828b4f9fe12SLen Brown {
829d9dda78bSAl Viro 	return single_open(file, video_proc_show, PDE_DATA(inode));
830936c8bcdSAlexey Dobriyan }
831936c8bcdSAlexey Dobriyan 
832936c8bcdSAlexey Dobriyan static ssize_t video_proc_write(struct file *file, const char __user *buf,
833936c8bcdSAlexey Dobriyan 				size_t count, loff_t *pos)
834936c8bcdSAlexey Dobriyan {
835d9dda78bSAl Viro 	struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
836936c8bcdSAlexey Dobriyan 	char *cmd, *buffer;
83736d03f93SSeth Forshee 	int ret;
838b4f9fe12SLen Brown 	int value;
839b4f9fe12SLen Brown 	int remain = count;
840b4f9fe12SLen Brown 	int lcd_out = -1;
841b4f9fe12SLen Brown 	int crt_out = -1;
842b4f9fe12SLen Brown 	int tv_out = -1;
843b4f9fe12SLen Brown 	u32 video_out;
844b4f9fe12SLen Brown 
845936c8bcdSAlexey Dobriyan 	cmd = kmalloc(count + 1, GFP_KERNEL);
846936c8bcdSAlexey Dobriyan 	if (!cmd)
847936c8bcdSAlexey Dobriyan 		return -ENOMEM;
848936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, count)) {
849936c8bcdSAlexey Dobriyan 		kfree(cmd);
850936c8bcdSAlexey Dobriyan 		return -EFAULT;
851936c8bcdSAlexey Dobriyan 	}
852936c8bcdSAlexey Dobriyan 	cmd[count] = '\0';
853936c8bcdSAlexey Dobriyan 
854936c8bcdSAlexey Dobriyan 	buffer = cmd;
855936c8bcdSAlexey Dobriyan 
856b4f9fe12SLen Brown 	/* scan expression.  Multiple expressions may be delimited with ;
857b4f9fe12SLen Brown 	 *
858b4f9fe12SLen Brown 	 *  NOTE: to keep scanning simple, invalid fields are ignored
859b4f9fe12SLen Brown 	 */
860b4f9fe12SLen Brown 	while (remain) {
861b4f9fe12SLen Brown 		if (sscanf(buffer, " lcd_out : %i", &value) == 1)
862b4f9fe12SLen Brown 			lcd_out = value & 1;
863b4f9fe12SLen Brown 		else if (sscanf(buffer, " crt_out : %i", &value) == 1)
864b4f9fe12SLen Brown 			crt_out = value & 1;
865b4f9fe12SLen Brown 		else if (sscanf(buffer, " tv_out : %i", &value) == 1)
866b4f9fe12SLen Brown 			tv_out = value & 1;
867b4f9fe12SLen Brown 		/* advance to one character past the next ; */
868b4f9fe12SLen Brown 		do {
869b4f9fe12SLen Brown 			++buffer;
870b4f9fe12SLen Brown 			--remain;
871b4f9fe12SLen Brown 		}
872b4f9fe12SLen Brown 		while (remain && *(buffer - 1) != ';');
873b4f9fe12SLen Brown 	}
874b4f9fe12SLen Brown 
875936c8bcdSAlexey Dobriyan 	kfree(cmd);
876936c8bcdSAlexey Dobriyan 
87736d03f93SSeth Forshee 	ret = get_video_status(dev, &video_out);
87836d03f93SSeth Forshee 	if (!ret) {
879b4f9fe12SLen Brown 		unsigned int new_video_out = video_out;
880b4f9fe12SLen Brown 		if (lcd_out != -1)
881b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out);
882b4f9fe12SLen Brown 		if (crt_out != -1)
883b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out);
884b4f9fe12SLen Brown 		if (tv_out != -1)
885b4f9fe12SLen Brown 			_set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out);
886b4f9fe12SLen Brown 		/* To avoid unnecessary video disruption, only write the new
887b4f9fe12SLen Brown 		 * video setting if something changed. */
888b4f9fe12SLen Brown 		if (new_video_out != video_out)
88932bcd5cbSSeth Forshee 			ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out);
890b4f9fe12SLen Brown 	}
891b4f9fe12SLen Brown 
89232bcd5cbSSeth Forshee 	return ret ? ret : count;
893b4f9fe12SLen Brown }
894b4f9fe12SLen Brown 
895936c8bcdSAlexey Dobriyan static const struct file_operations video_proc_fops = {
896936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
897936c8bcdSAlexey Dobriyan 	.open		= video_proc_open,
898936c8bcdSAlexey Dobriyan 	.read		= seq_read,
899936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
900936c8bcdSAlexey Dobriyan 	.release	= single_release,
901936c8bcdSAlexey Dobriyan 	.write		= video_proc_write,
902936c8bcdSAlexey Dobriyan };
903936c8bcdSAlexey Dobriyan 
90436d03f93SSeth Forshee static int get_fan_status(struct toshiba_acpi_dev *dev, u32 *status)
90536d03f93SSeth Forshee {
90636d03f93SSeth Forshee 	u32 hci_result;
90736d03f93SSeth Forshee 
90836d03f93SSeth Forshee 	hci_read1(dev, HCI_FAN, status, &hci_result);
90936d03f93SSeth Forshee 	return hci_result == HCI_SUCCESS ? 0 : -EIO;
91036d03f93SSeth Forshee }
91136d03f93SSeth Forshee 
912936c8bcdSAlexey Dobriyan static int fan_proc_show(struct seq_file *m, void *v)
913b4f9fe12SLen Brown {
914135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
91536d03f93SSeth Forshee 	int ret;
916b4f9fe12SLen Brown 	u32 value;
917b4f9fe12SLen Brown 
91836d03f93SSeth Forshee 	ret = get_fan_status(dev, &value);
91936d03f93SSeth Forshee 	if (!ret) {
920936c8bcdSAlexey Dobriyan 		seq_printf(m, "running:                 %d\n", (value > 0));
921135740deSSeth Forshee 		seq_printf(m, "force_on:                %d\n", dev->force_fan);
922b4f9fe12SLen Brown 	}
923b4f9fe12SLen Brown 
92436d03f93SSeth Forshee 	return ret;
925b4f9fe12SLen Brown }
926b4f9fe12SLen Brown 
927936c8bcdSAlexey Dobriyan static int fan_proc_open(struct inode *inode, struct file *file)
928b4f9fe12SLen Brown {
929d9dda78bSAl Viro 	return single_open(file, fan_proc_show, PDE_DATA(inode));
930936c8bcdSAlexey Dobriyan }
931936c8bcdSAlexey Dobriyan 
932936c8bcdSAlexey Dobriyan static ssize_t fan_proc_write(struct file *file, const char __user *buf,
933936c8bcdSAlexey Dobriyan 			      size_t count, loff_t *pos)
934936c8bcdSAlexey Dobriyan {
935d9dda78bSAl Viro 	struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
936936c8bcdSAlexey Dobriyan 	char cmd[42];
937936c8bcdSAlexey Dobriyan 	size_t len;
938b4f9fe12SLen Brown 	int value;
939b4f9fe12SLen Brown 	u32 hci_result;
940b4f9fe12SLen Brown 
941936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
942936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
943936c8bcdSAlexey Dobriyan 		return -EFAULT;
944936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
945936c8bcdSAlexey Dobriyan 
946936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " force_on : %i", &value) == 1 &&
947b4f9fe12SLen Brown 	    value >= 0 && value <= 1) {
948135740deSSeth Forshee 		hci_write1(dev, HCI_FAN, value, &hci_result);
949b4f9fe12SLen Brown 		if (hci_result != HCI_SUCCESS)
95032bcd5cbSSeth Forshee 			return -EIO;
951b4f9fe12SLen Brown 		else
952135740deSSeth Forshee 			dev->force_fan = value;
953b4f9fe12SLen Brown 	} else {
954b4f9fe12SLen Brown 		return -EINVAL;
955b4f9fe12SLen Brown 	}
956b4f9fe12SLen Brown 
957b4f9fe12SLen Brown 	return count;
958b4f9fe12SLen Brown }
959b4f9fe12SLen Brown 
960936c8bcdSAlexey Dobriyan static const struct file_operations fan_proc_fops = {
961936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
962936c8bcdSAlexey Dobriyan 	.open		= fan_proc_open,
963936c8bcdSAlexey Dobriyan 	.read		= seq_read,
964936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
965936c8bcdSAlexey Dobriyan 	.release	= single_release,
966936c8bcdSAlexey Dobriyan 	.write		= fan_proc_write,
967936c8bcdSAlexey Dobriyan };
968936c8bcdSAlexey Dobriyan 
969936c8bcdSAlexey Dobriyan static int keys_proc_show(struct seq_file *m, void *v)
970b4f9fe12SLen Brown {
971135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = m->private;
972b4f9fe12SLen Brown 	u32 hci_result;
973b4f9fe12SLen Brown 	u32 value;
974b4f9fe12SLen Brown 
97511948b93SSeth Forshee 	if (!dev->key_event_valid && dev->system_event_supported) {
976135740deSSeth Forshee 		hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
977b4f9fe12SLen Brown 		if (hci_result == HCI_SUCCESS) {
978135740deSSeth Forshee 			dev->key_event_valid = 1;
979135740deSSeth Forshee 			dev->last_key_event = value;
980b4f9fe12SLen Brown 		} else if (hci_result == HCI_EMPTY) {
981b4f9fe12SLen Brown 			/* better luck next time */
982b4f9fe12SLen Brown 		} else if (hci_result == HCI_NOT_SUPPORTED) {
983b4f9fe12SLen Brown 			/* This is a workaround for an unresolved issue on
984b4f9fe12SLen Brown 			 * some machines where system events sporadically
985b4f9fe12SLen Brown 			 * become disabled. */
986135740deSSeth Forshee 			hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
9877e33460dSJoe Perches 			pr_notice("Re-enabled hotkeys\n");
988b4f9fe12SLen Brown 		} else {
9897e33460dSJoe Perches 			pr_err("Error reading hotkey status\n");
99032bcd5cbSSeth Forshee 			return -EIO;
991b4f9fe12SLen Brown 		}
992b4f9fe12SLen Brown 	}
993b4f9fe12SLen Brown 
994135740deSSeth Forshee 	seq_printf(m, "hotkey_ready:            %d\n", dev->key_event_valid);
995135740deSSeth Forshee 	seq_printf(m, "hotkey:                  0x%04x\n", dev->last_key_event);
996936c8bcdSAlexey Dobriyan 	return 0;
997b4f9fe12SLen Brown }
998b4f9fe12SLen Brown 
999936c8bcdSAlexey Dobriyan static int keys_proc_open(struct inode *inode, struct file *file)
1000b4f9fe12SLen Brown {
1001d9dda78bSAl Viro 	return single_open(file, keys_proc_show, PDE_DATA(inode));
1002936c8bcdSAlexey Dobriyan }
1003936c8bcdSAlexey Dobriyan 
1004936c8bcdSAlexey Dobriyan static ssize_t keys_proc_write(struct file *file, const char __user *buf,
1005936c8bcdSAlexey Dobriyan 			       size_t count, loff_t *pos)
1006936c8bcdSAlexey Dobriyan {
1007d9dda78bSAl Viro 	struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
1008936c8bcdSAlexey Dobriyan 	char cmd[42];
1009936c8bcdSAlexey Dobriyan 	size_t len;
1010b4f9fe12SLen Brown 	int value;
1011b4f9fe12SLen Brown 
1012936c8bcdSAlexey Dobriyan 	len = min(count, sizeof(cmd) - 1);
1013936c8bcdSAlexey Dobriyan 	if (copy_from_user(cmd, buf, len))
1014936c8bcdSAlexey Dobriyan 		return -EFAULT;
1015936c8bcdSAlexey Dobriyan 	cmd[len] = '\0';
1016936c8bcdSAlexey Dobriyan 
1017936c8bcdSAlexey Dobriyan 	if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) {
1018135740deSSeth Forshee 		dev->key_event_valid = 0;
1019b4f9fe12SLen Brown 	} else {
1020b4f9fe12SLen Brown 		return -EINVAL;
1021b4f9fe12SLen Brown 	}
1022b4f9fe12SLen Brown 
1023b4f9fe12SLen Brown 	return count;
1024b4f9fe12SLen Brown }
1025b4f9fe12SLen Brown 
1026936c8bcdSAlexey Dobriyan static const struct file_operations keys_proc_fops = {
1027936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
1028936c8bcdSAlexey Dobriyan 	.open		= keys_proc_open,
1029936c8bcdSAlexey Dobriyan 	.read		= seq_read,
1030936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
1031936c8bcdSAlexey Dobriyan 	.release	= single_release,
1032936c8bcdSAlexey Dobriyan 	.write		= keys_proc_write,
1033936c8bcdSAlexey Dobriyan };
1034936c8bcdSAlexey Dobriyan 
1035936c8bcdSAlexey Dobriyan static int version_proc_show(struct seq_file *m, void *v)
1036b4f9fe12SLen Brown {
1037936c8bcdSAlexey Dobriyan 	seq_printf(m, "driver:                  %s\n", TOSHIBA_ACPI_VERSION);
1038936c8bcdSAlexey Dobriyan 	seq_printf(m, "proc_interface:          %d\n", PROC_INTERFACE_VERSION);
1039936c8bcdSAlexey Dobriyan 	return 0;
1040b4f9fe12SLen Brown }
1041b4f9fe12SLen Brown 
1042936c8bcdSAlexey Dobriyan static int version_proc_open(struct inode *inode, struct file *file)
1043936c8bcdSAlexey Dobriyan {
1044d9dda78bSAl Viro 	return single_open(file, version_proc_show, PDE_DATA(inode));
1045936c8bcdSAlexey Dobriyan }
1046936c8bcdSAlexey Dobriyan 
1047936c8bcdSAlexey Dobriyan static const struct file_operations version_proc_fops = {
1048936c8bcdSAlexey Dobriyan 	.owner		= THIS_MODULE,
1049936c8bcdSAlexey Dobriyan 	.open		= version_proc_open,
1050936c8bcdSAlexey Dobriyan 	.read		= seq_read,
1051936c8bcdSAlexey Dobriyan 	.llseek		= seq_lseek,
1052936c8bcdSAlexey Dobriyan 	.release	= single_release,
1053936c8bcdSAlexey Dobriyan };
1054936c8bcdSAlexey Dobriyan 
1055b4f9fe12SLen Brown /* proc and module init
1056b4f9fe12SLen Brown  */
1057b4f9fe12SLen Brown 
1058b4f9fe12SLen Brown #define PROC_TOSHIBA		"toshiba"
1059b4f9fe12SLen Brown 
1060b859f159SGreg Kroah-Hartman static void create_toshiba_proc_entries(struct toshiba_acpi_dev *dev)
1061b4f9fe12SLen Brown {
106236d03f93SSeth Forshee 	if (dev->backlight_dev)
1063135740deSSeth Forshee 		proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir,
1064135740deSSeth Forshee 				 &lcd_proc_fops, dev);
106536d03f93SSeth Forshee 	if (dev->video_supported)
1066135740deSSeth Forshee 		proc_create_data("video", S_IRUGO | S_IWUSR, toshiba_proc_dir,
1067135740deSSeth Forshee 				 &video_proc_fops, dev);
106836d03f93SSeth Forshee 	if (dev->fan_supported)
1069135740deSSeth Forshee 		proc_create_data("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir,
1070135740deSSeth Forshee 				 &fan_proc_fops, dev);
107136d03f93SSeth Forshee 	if (dev->hotkey_dev)
1072135740deSSeth Forshee 		proc_create_data("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir,
1073135740deSSeth Forshee 				 &keys_proc_fops, dev);
1074135740deSSeth Forshee 	proc_create_data("version", S_IRUGO, toshiba_proc_dir,
1075135740deSSeth Forshee 			 &version_proc_fops, dev);
1076b4f9fe12SLen Brown }
1077b4f9fe12SLen Brown 
107836d03f93SSeth Forshee static void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev)
1079b4f9fe12SLen Brown {
108036d03f93SSeth Forshee 	if (dev->backlight_dev)
1081936c8bcdSAlexey Dobriyan 		remove_proc_entry("lcd", toshiba_proc_dir);
108236d03f93SSeth Forshee 	if (dev->video_supported)
1083936c8bcdSAlexey Dobriyan 		remove_proc_entry("video", toshiba_proc_dir);
108436d03f93SSeth Forshee 	if (dev->fan_supported)
1085936c8bcdSAlexey Dobriyan 		remove_proc_entry("fan", toshiba_proc_dir);
108636d03f93SSeth Forshee 	if (dev->hotkey_dev)
1087936c8bcdSAlexey Dobriyan 		remove_proc_entry("keys", toshiba_proc_dir);
1088936c8bcdSAlexey Dobriyan 	remove_proc_entry("version", toshiba_proc_dir);
1089b4f9fe12SLen Brown }
1090b4f9fe12SLen Brown 
1091acc2472eSLionel Debroux static const struct backlight_ops toshiba_backlight_data = {
1092121b7b0dSAkio Idehara 	.options = BL_CORE_SUSPENDRESUME,
109362cce752SSeth Forshee 	.get_brightness = get_lcd_brightness,
1094b4f9fe12SLen Brown 	.update_status  = set_lcd_status,
1095b4f9fe12SLen Brown };
1096b4f9fe12SLen Brown 
1097360f0f39SAzael Avalos /*
1098360f0f39SAzael Avalos  * Sysfs files
1099360f0f39SAzael Avalos  */
1100360f0f39SAzael Avalos 
1101360f0f39SAzael Avalos static ssize_t toshiba_kbd_bl_mode_store(struct device *dev,
1102360f0f39SAzael Avalos 					 struct device_attribute *attr,
1103360f0f39SAzael Avalos 					 const char *buf, size_t count)
1104360f0f39SAzael Avalos {
1105360f0f39SAzael Avalos 	struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
1106360f0f39SAzael Avalos 	int mode = -1;
1107360f0f39SAzael Avalos 	int time = -1;
1108360f0f39SAzael Avalos 
1109360f0f39SAzael Avalos 	if (sscanf(buf, "%i", &mode) != 1 && (mode != 2 || mode != 1))
1110360f0f39SAzael Avalos 		return -EINVAL;
1111360f0f39SAzael Avalos 
1112360f0f39SAzael Avalos 	/* Set the Keyboard Backlight Mode where:
1113360f0f39SAzael Avalos 	 * Mode - Auto (2) | FN-Z (1)
1114360f0f39SAzael Avalos 	 *	Auto - KBD backlight turns off automatically in given time
1115360f0f39SAzael Avalos 	 *	FN-Z - KBD backlight "toggles" when hotkey pressed
1116360f0f39SAzael Avalos 	 */
1117360f0f39SAzael Avalos 	if (mode != -1 && toshiba->kbd_mode != mode) {
1118360f0f39SAzael Avalos 		time = toshiba->kbd_time << HCI_MISC_SHIFT;
1119360f0f39SAzael Avalos 		time = time + toshiba->kbd_mode;
1120360f0f39SAzael Avalos 		if (toshiba_kbd_illum_status_set(toshiba, time) < 0)
1121360f0f39SAzael Avalos 			return -EIO;
1122360f0f39SAzael Avalos 		toshiba->kbd_mode = mode;
1123360f0f39SAzael Avalos 	}
1124360f0f39SAzael Avalos 
1125360f0f39SAzael Avalos 	return count;
1126360f0f39SAzael Avalos }
1127360f0f39SAzael Avalos 
1128360f0f39SAzael Avalos static ssize_t toshiba_kbd_bl_mode_show(struct device *dev,
1129360f0f39SAzael Avalos 					struct device_attribute *attr,
1130360f0f39SAzael Avalos 					char *buf)
1131360f0f39SAzael Avalos {
1132360f0f39SAzael Avalos 	struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
1133360f0f39SAzael Avalos 	u32 time;
1134360f0f39SAzael Avalos 
1135360f0f39SAzael Avalos 	if (toshiba_kbd_illum_status_get(toshiba, &time) < 0)
1136360f0f39SAzael Avalos 		return -EIO;
1137360f0f39SAzael Avalos 
1138360f0f39SAzael Avalos 	return sprintf(buf, "%i\n", time & 0x07);
1139360f0f39SAzael Avalos }
1140360f0f39SAzael Avalos 
1141360f0f39SAzael Avalos static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev,
1142360f0f39SAzael Avalos 					    struct device_attribute *attr,
1143360f0f39SAzael Avalos 					    const char *buf, size_t count)
1144360f0f39SAzael Avalos {
1145360f0f39SAzael Avalos 	struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
1146360f0f39SAzael Avalos 	int time = -1;
1147360f0f39SAzael Avalos 
1148360f0f39SAzael Avalos 	if (sscanf(buf, "%i", &time) != 1 && (time < 0 || time > 60))
1149360f0f39SAzael Avalos 		return -EINVAL;
1150360f0f39SAzael Avalos 
1151360f0f39SAzael Avalos 	/* Set the Keyboard Backlight Timeout: 0-60 seconds */
1152360f0f39SAzael Avalos 	if (time != -1 && toshiba->kbd_time != time) {
1153360f0f39SAzael Avalos 		time = time << HCI_MISC_SHIFT;
1154360f0f39SAzael Avalos 		time = (toshiba->kbd_mode == SCI_KBD_MODE_AUTO) ?
1155360f0f39SAzael Avalos 							time + 1 : time + 2;
1156360f0f39SAzael Avalos 		if (toshiba_kbd_illum_status_set(toshiba, time) < 0)
1157360f0f39SAzael Avalos 			return -EIO;
1158360f0f39SAzael Avalos 		toshiba->kbd_time = time >> HCI_MISC_SHIFT;
1159360f0f39SAzael Avalos 	}
1160360f0f39SAzael Avalos 
1161360f0f39SAzael Avalos 	return count;
1162360f0f39SAzael Avalos }
1163360f0f39SAzael Avalos 
1164360f0f39SAzael Avalos static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev,
1165360f0f39SAzael Avalos 					   struct device_attribute *attr,
1166360f0f39SAzael Avalos 					   char *buf)
1167360f0f39SAzael Avalos {
1168360f0f39SAzael Avalos 	struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
1169360f0f39SAzael Avalos 	u32 time;
1170360f0f39SAzael Avalos 
1171360f0f39SAzael Avalos 	if (toshiba_kbd_illum_status_get(toshiba, &time) < 0)
1172360f0f39SAzael Avalos 		return -EIO;
1173360f0f39SAzael Avalos 
1174360f0f39SAzael Avalos 	return sprintf(buf, "%i\n", time >> HCI_MISC_SHIFT);
1175360f0f39SAzael Avalos }
1176360f0f39SAzael Avalos 
1177*9d8658acSAzael Avalos static ssize_t toshiba_touchpad_store(struct device *dev,
1178*9d8658acSAzael Avalos 				      struct device_attribute *attr,
1179*9d8658acSAzael Avalos 				      const char *buf, size_t count)
1180*9d8658acSAzael Avalos {
1181*9d8658acSAzael Avalos 	struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
1182*9d8658acSAzael Avalos 	int state;
1183*9d8658acSAzael Avalos 
1184*9d8658acSAzael Avalos 	/* Set the TouchPad on/off, 0 - Disable | 1 - Enable */
1185*9d8658acSAzael Avalos 	if (sscanf(buf, "%i", &state) == 1 && (state == 0 || state == 1)) {
1186*9d8658acSAzael Avalos 		if (toshiba_touchpad_set(toshiba, state) < 0)
1187*9d8658acSAzael Avalos 			return -EIO;
1188*9d8658acSAzael Avalos 	}
1189*9d8658acSAzael Avalos 
1190*9d8658acSAzael Avalos 	return count;
1191*9d8658acSAzael Avalos }
1192*9d8658acSAzael Avalos 
1193*9d8658acSAzael Avalos static ssize_t toshiba_touchpad_show(struct device *dev,
1194*9d8658acSAzael Avalos 				     struct device_attribute *attr, char *buf)
1195*9d8658acSAzael Avalos {
1196*9d8658acSAzael Avalos 	struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
1197*9d8658acSAzael Avalos 	u32 state;
1198*9d8658acSAzael Avalos 	int ret;
1199*9d8658acSAzael Avalos 
1200*9d8658acSAzael Avalos 	ret = toshiba_touchpad_get(toshiba, &state);
1201*9d8658acSAzael Avalos 	if (ret < 0)
1202*9d8658acSAzael Avalos 		return ret;
1203*9d8658acSAzael Avalos 
1204*9d8658acSAzael Avalos 	return sprintf(buf, "%i\n", state);
1205*9d8658acSAzael Avalos }
1206*9d8658acSAzael Avalos 
1207360f0f39SAzael Avalos static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR,
1208360f0f39SAzael Avalos 		   toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store);
1209360f0f39SAzael Avalos static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR,
1210360f0f39SAzael Avalos 		   toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store);
1211*9d8658acSAzael Avalos static DEVICE_ATTR(touchpad, S_IRUGO | S_IWUSR,
1212*9d8658acSAzael Avalos 		   toshiba_touchpad_show, toshiba_touchpad_store);
1213360f0f39SAzael Avalos 
1214360f0f39SAzael Avalos static struct attribute *toshiba_attributes[] = {
1215360f0f39SAzael Avalos 	&dev_attr_kbd_backlight_mode.attr,
1216360f0f39SAzael Avalos 	&dev_attr_kbd_backlight_timeout.attr,
1217*9d8658acSAzael Avalos 	&dev_attr_touchpad.attr,
1218360f0f39SAzael Avalos 	NULL,
1219360f0f39SAzael Avalos };
1220360f0f39SAzael Avalos 
1221360f0f39SAzael Avalos static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
1222360f0f39SAzael Avalos 					struct attribute *attr, int idx)
1223360f0f39SAzael Avalos {
1224360f0f39SAzael Avalos 	struct device *dev = container_of(kobj, struct device, kobj);
1225360f0f39SAzael Avalos 	struct toshiba_acpi_dev *drv = dev_get_drvdata(dev);
1226360f0f39SAzael Avalos 	bool exists = true;
1227360f0f39SAzael Avalos 
1228360f0f39SAzael Avalos 	if (attr == &dev_attr_kbd_backlight_mode.attr)
1229360f0f39SAzael Avalos 		exists = (drv->kbd_illum_supported) ? true : false;
1230360f0f39SAzael Avalos 	else if (attr == &dev_attr_kbd_backlight_timeout.attr)
1231360f0f39SAzael Avalos 		exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false;
1232*9d8658acSAzael Avalos 	else if (attr == &dev_attr_touchpad.attr)
1233*9d8658acSAzael Avalos 		exists = (drv->touchpad_supported) ? true : false;
1234360f0f39SAzael Avalos 
1235360f0f39SAzael Avalos 	return exists ? attr->mode : 0;
1236360f0f39SAzael Avalos }
1237360f0f39SAzael Avalos 
1238360f0f39SAzael Avalos static struct attribute_group toshiba_attr_group = {
1239360f0f39SAzael Avalos 	.is_visible = toshiba_sysfs_is_visible,
1240360f0f39SAzael Avalos 	.attrs = toshiba_attributes,
1241360f0f39SAzael Avalos };
1242360f0f39SAzael Avalos 
124329cd293fSSeth Forshee static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
124429cd293fSSeth Forshee 				      struct serio *port)
124529cd293fSSeth Forshee {
124629cd293fSSeth Forshee 	if (str & 0x20)
124729cd293fSSeth Forshee 		return false;
124829cd293fSSeth Forshee 
124929cd293fSSeth Forshee 	if (unlikely(data == 0xe0))
125029cd293fSSeth Forshee 		return false;
125129cd293fSSeth Forshee 
125229cd293fSSeth Forshee 	if ((data & 0x7f) == TOS1900_FN_SCAN) {
125329cd293fSSeth Forshee 		schedule_work(&toshiba_acpi->hotkey_work);
125429cd293fSSeth Forshee 		return true;
125529cd293fSSeth Forshee 	}
125629cd293fSSeth Forshee 
125729cd293fSSeth Forshee 	return false;
125829cd293fSSeth Forshee }
125929cd293fSSeth Forshee 
126029cd293fSSeth Forshee static void toshiba_acpi_hotkey_work(struct work_struct *work)
126129cd293fSSeth Forshee {
126229cd293fSSeth Forshee 	acpi_handle ec_handle = ec_get_handle();
126329cd293fSSeth Forshee 	acpi_status status;
126429cd293fSSeth Forshee 
126529cd293fSSeth Forshee 	if (!ec_handle)
126629cd293fSSeth Forshee 		return;
126729cd293fSSeth Forshee 
126829cd293fSSeth Forshee 	status = acpi_evaluate_object(ec_handle, "NTFY", NULL, NULL);
126929cd293fSSeth Forshee 	if (ACPI_FAILURE(status))
127029cd293fSSeth Forshee 		pr_err("ACPI NTFY method execution failed\n");
127129cd293fSSeth Forshee }
127229cd293fSSeth Forshee 
127329cd293fSSeth Forshee /*
127429cd293fSSeth Forshee  * Returns hotkey scancode, or < 0 on failure.
127529cd293fSSeth Forshee  */
127629cd293fSSeth Forshee static int toshiba_acpi_query_hotkey(struct toshiba_acpi_dev *dev)
127729cd293fSSeth Forshee {
127874facaf7SZhang Rui 	unsigned long long value;
127929cd293fSSeth Forshee 	acpi_status status;
128029cd293fSSeth Forshee 
128174facaf7SZhang Rui 	status = acpi_evaluate_integer(dev->acpi_dev->handle, "INFO",
128274facaf7SZhang Rui 				      NULL, &value);
128374facaf7SZhang Rui 	if (ACPI_FAILURE(status)) {
128429cd293fSSeth Forshee 		pr_err("ACPI INFO method execution failed\n");
128529cd293fSSeth Forshee 		return -EIO;
128629cd293fSSeth Forshee 	}
128729cd293fSSeth Forshee 
128874facaf7SZhang Rui 	return value;
128929cd293fSSeth Forshee }
129029cd293fSSeth Forshee 
129129cd293fSSeth Forshee static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev,
129229cd293fSSeth Forshee 				       int scancode)
129329cd293fSSeth Forshee {
129429cd293fSSeth Forshee 	if (scancode == 0x100)
129529cd293fSSeth Forshee 		return;
129629cd293fSSeth Forshee 
129729cd293fSSeth Forshee 	/* act on key press; ignore key release */
129829cd293fSSeth Forshee 	if (scancode & 0x80)
129929cd293fSSeth Forshee 		return;
130029cd293fSSeth Forshee 
130129cd293fSSeth Forshee 	if (!sparse_keymap_report_event(dev->hotkey_dev, scancode, 1, true))
130229cd293fSSeth Forshee 		pr_info("Unknown key %x\n", scancode);
130329cd293fSSeth Forshee }
130429cd293fSSeth Forshee 
1305b859f159SGreg Kroah-Hartman static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
13066335e4d5SMatthew Garrett {
1307135740deSSeth Forshee 	acpi_status status;
1308e2e19606SZhang Rui 	acpi_handle ec_handle;
1309135740deSSeth Forshee 	int error;
131029cd293fSSeth Forshee 	u32 hci_result;
1311135740deSSeth Forshee 
1312135740deSSeth Forshee 	dev->hotkey_dev = input_allocate_device();
1313b222cca6SJoe Perches 	if (!dev->hotkey_dev)
1314135740deSSeth Forshee 		return -ENOMEM;
1315135740deSSeth Forshee 
1316135740deSSeth Forshee 	dev->hotkey_dev->name = "Toshiba input device";
13176e02cc7eSSeth Forshee 	dev->hotkey_dev->phys = "toshiba_acpi/input0";
1318135740deSSeth Forshee 	dev->hotkey_dev->id.bustype = BUS_HOST;
1319135740deSSeth Forshee 
1320135740deSSeth Forshee 	error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL);
1321135740deSSeth Forshee 	if (error)
1322135740deSSeth Forshee 		goto err_free_dev;
1323135740deSSeth Forshee 
132429cd293fSSeth Forshee 	/*
132529cd293fSSeth Forshee 	 * For some machines the SCI responsible for providing hotkey
132629cd293fSSeth Forshee 	 * notification doesn't fire. We can trigger the notification
132729cd293fSSeth Forshee 	 * whenever the Fn key is pressed using the NTFY method, if
132829cd293fSSeth Forshee 	 * supported, so if it's present set up an i8042 key filter
132929cd293fSSeth Forshee 	 * for this purpose.
133029cd293fSSeth Forshee 	 */
133129cd293fSSeth Forshee 	status = AE_ERROR;
133229cd293fSSeth Forshee 	ec_handle = ec_get_handle();
1333e2e19606SZhang Rui 	if (ec_handle && acpi_has_method(ec_handle, "NTFY")) {
133429cd293fSSeth Forshee 		INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);
133529cd293fSSeth Forshee 
133629cd293fSSeth Forshee 		error = i8042_install_filter(toshiba_acpi_i8042_filter);
133729cd293fSSeth Forshee 		if (error) {
133829cd293fSSeth Forshee 			pr_err("Error installing key filter\n");
133929cd293fSSeth Forshee 			goto err_free_keymap;
134029cd293fSSeth Forshee 		}
134129cd293fSSeth Forshee 
134229cd293fSSeth Forshee 		dev->ntfy_supported = 1;
134329cd293fSSeth Forshee 	}
134429cd293fSSeth Forshee 
134529cd293fSSeth Forshee 	/*
134629cd293fSSeth Forshee 	 * Determine hotkey query interface. Prefer using the INFO
134729cd293fSSeth Forshee 	 * method when it is available.
134829cd293fSSeth Forshee 	 */
1349e2e19606SZhang Rui 	if (acpi_has_method(dev->acpi_dev->handle, "INFO"))
135029cd293fSSeth Forshee 		dev->info_supported = 1;
1351e2e19606SZhang Rui 	else {
135229cd293fSSeth Forshee 		hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
135329cd293fSSeth Forshee 		if (hci_result == HCI_SUCCESS)
135429cd293fSSeth Forshee 			dev->system_event_supported = 1;
135529cd293fSSeth Forshee 	}
135629cd293fSSeth Forshee 
135729cd293fSSeth Forshee 	if (!dev->info_supported && !dev->system_event_supported) {
135829cd293fSSeth Forshee 		pr_warn("No hotkey query interface found\n");
135929cd293fSSeth Forshee 		goto err_remove_filter;
136029cd293fSSeth Forshee 	}
136129cd293fSSeth Forshee 
13626e02cc7eSSeth Forshee 	status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL);
1363135740deSSeth Forshee 	if (ACPI_FAILURE(status)) {
1364135740deSSeth Forshee 		pr_info("Unable to enable hotkeys\n");
1365135740deSSeth Forshee 		error = -ENODEV;
136629cd293fSSeth Forshee 		goto err_remove_filter;
1367135740deSSeth Forshee 	}
1368135740deSSeth Forshee 
1369135740deSSeth Forshee 	error = input_register_device(dev->hotkey_dev);
1370135740deSSeth Forshee 	if (error) {
1371135740deSSeth Forshee 		pr_info("Unable to register input device\n");
137229cd293fSSeth Forshee 		goto err_remove_filter;
1373135740deSSeth Forshee 	}
1374135740deSSeth Forshee 
137529cd293fSSeth Forshee 	hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &hci_result);
1376135740deSSeth Forshee 	return 0;
1377135740deSSeth Forshee 
137829cd293fSSeth Forshee  err_remove_filter:
137929cd293fSSeth Forshee 	if (dev->ntfy_supported)
138029cd293fSSeth Forshee 		i8042_remove_filter(toshiba_acpi_i8042_filter);
1381135740deSSeth Forshee  err_free_keymap:
1382135740deSSeth Forshee 	sparse_keymap_free(dev->hotkey_dev);
1383135740deSSeth Forshee  err_free_dev:
1384135740deSSeth Forshee 	input_free_device(dev->hotkey_dev);
1385135740deSSeth Forshee 	dev->hotkey_dev = NULL;
1386135740deSSeth Forshee 	return error;
1387135740deSSeth Forshee }
1388135740deSSeth Forshee 
1389b859f159SGreg Kroah-Hartman static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)
139062cce752SSeth Forshee {
139162cce752SSeth Forshee 	struct backlight_properties props;
139262cce752SSeth Forshee 	int brightness;
139362cce752SSeth Forshee 	int ret;
1394121b7b0dSAkio Idehara 	bool enabled;
139562cce752SSeth Forshee 
139662cce752SSeth Forshee 	/*
139762cce752SSeth Forshee 	 * Some machines don't support the backlight methods at all, and
139862cce752SSeth Forshee 	 * others support it read-only. Either of these is pretty useless,
139962cce752SSeth Forshee 	 * so only register the backlight device if the backlight method
140062cce752SSeth Forshee 	 * supports both reads and writes.
140162cce752SSeth Forshee 	 */
140262cce752SSeth Forshee 	brightness = __get_lcd_brightness(dev);
140362cce752SSeth Forshee 	if (brightness < 0)
140462cce752SSeth Forshee 		return 0;
140562cce752SSeth Forshee 	ret = set_lcd_brightness(dev, brightness);
140662cce752SSeth Forshee 	if (ret) {
140762cce752SSeth Forshee 		pr_debug("Backlight method is read-only, disabling backlight support\n");
140862cce752SSeth Forshee 		return 0;
140962cce752SSeth Forshee 	}
141062cce752SSeth Forshee 
1411121b7b0dSAkio Idehara 	/* Determine whether or not BIOS supports transflective backlight */
1412121b7b0dSAkio Idehara 	ret = get_tr_backlight_status(dev, &enabled);
1413121b7b0dSAkio Idehara 	dev->tr_backlight_supported = !ret;
1414121b7b0dSAkio Idehara 
141553039f22SMatthew Garrett 	memset(&props, 0, sizeof(props));
141662cce752SSeth Forshee 	props.type = BACKLIGHT_PLATFORM;
141762cce752SSeth Forshee 	props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
141862cce752SSeth Forshee 
1419121b7b0dSAkio Idehara 	/* adding an extra level and having 0 change to transflective mode */
1420121b7b0dSAkio Idehara 	if (dev->tr_backlight_supported)
1421121b7b0dSAkio Idehara 		props.max_brightness++;
1422121b7b0dSAkio Idehara 
142362cce752SSeth Forshee 	dev->backlight_dev = backlight_device_register("toshiba",
142462cce752SSeth Forshee 						       &dev->acpi_dev->dev,
142562cce752SSeth Forshee 						       dev,
142662cce752SSeth Forshee 						       &toshiba_backlight_data,
142762cce752SSeth Forshee 						       &props);
142862cce752SSeth Forshee 	if (IS_ERR(dev->backlight_dev)) {
142962cce752SSeth Forshee 		ret = PTR_ERR(dev->backlight_dev);
143062cce752SSeth Forshee 		pr_err("Could not register toshiba backlight device\n");
143162cce752SSeth Forshee 		dev->backlight_dev = NULL;
143262cce752SSeth Forshee 		return ret;
143362cce752SSeth Forshee 	}
143462cce752SSeth Forshee 
143562cce752SSeth Forshee 	dev->backlight_dev->props.brightness = brightness;
143662cce752SSeth Forshee 	return 0;
143762cce752SSeth Forshee }
143862cce752SSeth Forshee 
143951fac838SRafael J. Wysocki static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
1440135740deSSeth Forshee {
1441135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
1442135740deSSeth Forshee 
144336d03f93SSeth Forshee 	remove_toshiba_proc_entries(dev);
1444135740deSSeth Forshee 
1445360f0f39SAzael Avalos 	if (dev->sysfs_created)
1446360f0f39SAzael Avalos 		sysfs_remove_group(&dev->acpi_dev->dev.kobj,
1447360f0f39SAzael Avalos 				   &toshiba_attr_group);
1448360f0f39SAzael Avalos 
144929cd293fSSeth Forshee 	if (dev->ntfy_supported) {
145029cd293fSSeth Forshee 		i8042_remove_filter(toshiba_acpi_i8042_filter);
145129cd293fSSeth Forshee 		cancel_work_sync(&dev->hotkey_work);
145229cd293fSSeth Forshee 	}
145329cd293fSSeth Forshee 
1454135740deSSeth Forshee 	if (dev->hotkey_dev) {
1455135740deSSeth Forshee 		input_unregister_device(dev->hotkey_dev);
1456135740deSSeth Forshee 		sparse_keymap_free(dev->hotkey_dev);
1457135740deSSeth Forshee 	}
1458135740deSSeth Forshee 
1459135740deSSeth Forshee 	if (dev->bt_rfk) {
1460135740deSSeth Forshee 		rfkill_unregister(dev->bt_rfk);
1461135740deSSeth Forshee 		rfkill_destroy(dev->bt_rfk);
1462135740deSSeth Forshee 	}
1463135740deSSeth Forshee 
1464135740deSSeth Forshee 	if (dev->backlight_dev)
1465135740deSSeth Forshee 		backlight_device_unregister(dev->backlight_dev);
1466135740deSSeth Forshee 
146736d03f93SSeth Forshee 	if (dev->illumination_supported)
1468135740deSSeth Forshee 		led_classdev_unregister(&dev->led_dev);
1469135740deSSeth Forshee 
1470360f0f39SAzael Avalos 	if (dev->kbd_led_registered)
1471360f0f39SAzael Avalos 		led_classdev_unregister(&dev->kbd_led);
1472360f0f39SAzael Avalos 
147329cd293fSSeth Forshee 	if (toshiba_acpi)
147429cd293fSSeth Forshee 		toshiba_acpi = NULL;
147529cd293fSSeth Forshee 
1476135740deSSeth Forshee 	kfree(dev);
1477135740deSSeth Forshee 
1478135740deSSeth Forshee 	return 0;
1479135740deSSeth Forshee }
1480135740deSSeth Forshee 
1481b859f159SGreg Kroah-Hartman static const char *find_hci_method(acpi_handle handle)
1482a540d6b5SSeth Forshee {
1483e2e19606SZhang Rui 	if (acpi_has_method(handle, "GHCI"))
1484a540d6b5SSeth Forshee 		return "GHCI";
1485a540d6b5SSeth Forshee 
1486e2e19606SZhang Rui 	if (acpi_has_method(handle, "SPFC"))
1487a540d6b5SSeth Forshee 		return "SPFC";
1488a540d6b5SSeth Forshee 
1489a540d6b5SSeth Forshee 	return NULL;
1490a540d6b5SSeth Forshee }
1491a540d6b5SSeth Forshee 
1492b859f159SGreg Kroah-Hartman static int toshiba_acpi_add(struct acpi_device *acpi_dev)
1493135740deSSeth Forshee {
1494135740deSSeth Forshee 	struct toshiba_acpi_dev *dev;
1495a540d6b5SSeth Forshee 	const char *hci_method;
149636d03f93SSeth Forshee 	u32 dummy;
1497135740deSSeth Forshee 	bool bt_present;
1498135740deSSeth Forshee 	int ret = 0;
1499135740deSSeth Forshee 
150029cd293fSSeth Forshee 	if (toshiba_acpi)
150129cd293fSSeth Forshee 		return -EBUSY;
150229cd293fSSeth Forshee 
1503135740deSSeth Forshee 	pr_info("Toshiba Laptop ACPI Extras version %s\n",
1504135740deSSeth Forshee 	       TOSHIBA_ACPI_VERSION);
1505135740deSSeth Forshee 
1506a540d6b5SSeth Forshee 	hci_method = find_hci_method(acpi_dev->handle);
1507a540d6b5SSeth Forshee 	if (!hci_method) {
1508a540d6b5SSeth Forshee 		pr_err("HCI interface not found\n");
15096e02cc7eSSeth Forshee 		return -ENODEV;
1510a540d6b5SSeth Forshee 	}
15116e02cc7eSSeth Forshee 
1512135740deSSeth Forshee 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1513135740deSSeth Forshee 	if (!dev)
1514135740deSSeth Forshee 		return -ENOMEM;
1515135740deSSeth Forshee 	dev->acpi_dev = acpi_dev;
1516a540d6b5SSeth Forshee 	dev->method_hci = hci_method;
1517135740deSSeth Forshee 	acpi_dev->driver_data = dev;
1518360f0f39SAzael Avalos 	dev_set_drvdata(&acpi_dev->dev, dev);
1519135740deSSeth Forshee 
15206e02cc7eSSeth Forshee 	if (toshiba_acpi_setup_keyboard(dev))
1521135740deSSeth Forshee 		pr_info("Unable to activate hotkeys\n");
1522135740deSSeth Forshee 
1523135740deSSeth Forshee 	mutex_init(&dev->mutex);
1524135740deSSeth Forshee 
152562cce752SSeth Forshee 	ret = toshiba_acpi_setup_backlight(dev);
152662cce752SSeth Forshee 	if (ret)
1527135740deSSeth Forshee 		goto error;
1528135740deSSeth Forshee 
1529135740deSSeth Forshee 	/* Register rfkill switch for Bluetooth */
1530135740deSSeth Forshee 	if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) {
1531135740deSSeth Forshee 		dev->bt_rfk = rfkill_alloc("Toshiba Bluetooth",
1532135740deSSeth Forshee 					   &acpi_dev->dev,
1533135740deSSeth Forshee 					   RFKILL_TYPE_BLUETOOTH,
1534135740deSSeth Forshee 					   &toshiba_rfk_ops,
1535135740deSSeth Forshee 					   dev);
1536135740deSSeth Forshee 		if (!dev->bt_rfk) {
1537135740deSSeth Forshee 			pr_err("unable to allocate rfkill device\n");
1538135740deSSeth Forshee 			ret = -ENOMEM;
1539135740deSSeth Forshee 			goto error;
1540135740deSSeth Forshee 		}
1541135740deSSeth Forshee 
1542135740deSSeth Forshee 		ret = rfkill_register(dev->bt_rfk);
1543135740deSSeth Forshee 		if (ret) {
1544135740deSSeth Forshee 			pr_err("unable to register rfkill device\n");
1545135740deSSeth Forshee 			rfkill_destroy(dev->bt_rfk);
1546135740deSSeth Forshee 			goto error;
1547135740deSSeth Forshee 		}
1548135740deSSeth Forshee 	}
1549135740deSSeth Forshee 
1550135740deSSeth Forshee 	if (toshiba_illumination_available(dev)) {
1551135740deSSeth Forshee 		dev->led_dev.name = "toshiba::illumination";
1552135740deSSeth Forshee 		dev->led_dev.max_brightness = 1;
1553135740deSSeth Forshee 		dev->led_dev.brightness_set = toshiba_illumination_set;
1554135740deSSeth Forshee 		dev->led_dev.brightness_get = toshiba_illumination_get;
1555135740deSSeth Forshee 		if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev))
155636d03f93SSeth Forshee 			dev->illumination_supported = 1;
1557135740deSSeth Forshee 	}
1558135740deSSeth Forshee 
1559360f0f39SAzael Avalos 	ret = toshiba_kbd_illum_status_get(dev, &dummy);
1560360f0f39SAzael Avalos 	if (!ret) {
1561360f0f39SAzael Avalos 		dev->kbd_time = dummy >> HCI_MISC_SHIFT;
1562360f0f39SAzael Avalos 		dev->kbd_mode = dummy & 0x07;
1563360f0f39SAzael Avalos 	}
1564360f0f39SAzael Avalos 	dev->kbd_illum_supported = !ret;
1565360f0f39SAzael Avalos 	/*
1566360f0f39SAzael Avalos 	 * Only register the LED if KBD illumination is supported
1567360f0f39SAzael Avalos 	 * and the keyboard backlight operation mode is set to FN-Z
1568360f0f39SAzael Avalos 	 */
1569360f0f39SAzael Avalos 	if (dev->kbd_illum_supported && dev->kbd_mode == SCI_KBD_MODE_FNZ) {
1570360f0f39SAzael Avalos 		dev->kbd_led.name = "toshiba::kbd_backlight";
1571360f0f39SAzael Avalos 		dev->kbd_led.max_brightness = 1;
1572360f0f39SAzael Avalos 		dev->kbd_led.brightness_set = toshiba_kbd_backlight_set;
1573360f0f39SAzael Avalos 		dev->kbd_led.brightness_get = toshiba_kbd_backlight_get;
1574360f0f39SAzael Avalos 		if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led))
1575360f0f39SAzael Avalos 			dev->kbd_led_registered = 1;
1576360f0f39SAzael Avalos 	}
1577360f0f39SAzael Avalos 
1578*9d8658acSAzael Avalos 	ret = toshiba_touchpad_get(dev, &dummy);
1579*9d8658acSAzael Avalos 	dev->touchpad_supported = !ret;
1580*9d8658acSAzael Avalos 
158136d03f93SSeth Forshee 	/* Determine whether or not BIOS supports fan and video interfaces */
158236d03f93SSeth Forshee 
158336d03f93SSeth Forshee 	ret = get_video_status(dev, &dummy);
158436d03f93SSeth Forshee 	dev->video_supported = !ret;
158536d03f93SSeth Forshee 
158636d03f93SSeth Forshee 	ret = get_fan_status(dev, &dummy);
158736d03f93SSeth Forshee 	dev->fan_supported = !ret;
158836d03f93SSeth Forshee 
1589360f0f39SAzael Avalos 	ret = sysfs_create_group(&dev->acpi_dev->dev.kobj,
1590360f0f39SAzael Avalos 				 &toshiba_attr_group);
1591360f0f39SAzael Avalos 	if (ret) {
1592360f0f39SAzael Avalos 		dev->sysfs_created = 0;
1593360f0f39SAzael Avalos 		goto error;
1594360f0f39SAzael Avalos 	}
1595360f0f39SAzael Avalos 	dev->sysfs_created = !ret;
1596360f0f39SAzael Avalos 
159736d03f93SSeth Forshee 	create_toshiba_proc_entries(dev);
159836d03f93SSeth Forshee 
159929cd293fSSeth Forshee 	toshiba_acpi = dev;
160029cd293fSSeth Forshee 
1601135740deSSeth Forshee 	return 0;
1602135740deSSeth Forshee 
1603135740deSSeth Forshee error:
160451fac838SRafael J. Wysocki 	toshiba_acpi_remove(acpi_dev);
1605135740deSSeth Forshee 	return ret;
1606135740deSSeth Forshee }
1607135740deSSeth Forshee 
1608135740deSSeth Forshee static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
1609135740deSSeth Forshee {
1610135740deSSeth Forshee 	struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
16116335e4d5SMatthew Garrett 	u32 hci_result, value;
161211948b93SSeth Forshee 	int retries = 3;
161329cd293fSSeth Forshee 	int scancode;
16146335e4d5SMatthew Garrett 
161529cd293fSSeth Forshee 	if (event != 0x80)
16166335e4d5SMatthew Garrett 		return;
161711948b93SSeth Forshee 
161829cd293fSSeth Forshee 	if (dev->info_supported) {
161929cd293fSSeth Forshee 		scancode = toshiba_acpi_query_hotkey(dev);
162029cd293fSSeth Forshee 		if (scancode < 0)
162129cd293fSSeth Forshee 			pr_err("Failed to query hotkey event\n");
162229cd293fSSeth Forshee 		else if (scancode != 0)
162329cd293fSSeth Forshee 			toshiba_acpi_report_hotkey(dev, scancode);
162429cd293fSSeth Forshee 	} else if (dev->system_event_supported) {
16256335e4d5SMatthew Garrett 		do {
1626135740deSSeth Forshee 			hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
162711948b93SSeth Forshee 			switch (hci_result) {
162811948b93SSeth Forshee 			case HCI_SUCCESS:
162929cd293fSSeth Forshee 				toshiba_acpi_report_hotkey(dev, (int)value);
163011948b93SSeth Forshee 				break;
163111948b93SSeth Forshee 			case HCI_NOT_SUPPORTED:
163229cd293fSSeth Forshee 				/*
163329cd293fSSeth Forshee 				 * This is a workaround for an unresolved
163429cd293fSSeth Forshee 				 * issue on some machines where system events
163529cd293fSSeth Forshee 				 * sporadically become disabled.
163629cd293fSSeth Forshee 				 */
163729cd293fSSeth Forshee 				hci_write1(dev, HCI_SYSTEM_EVENT, 1,
163829cd293fSSeth Forshee 					   &hci_result);
16397e33460dSJoe Perches 				pr_notice("Re-enabled hotkeys\n");
164011948b93SSeth Forshee 				/* fall through */
164111948b93SSeth Forshee 			default:
164211948b93SSeth Forshee 				retries--;
164311948b93SSeth Forshee 				break;
16446335e4d5SMatthew Garrett 			}
164511948b93SSeth Forshee 		} while (retries && hci_result != HCI_EMPTY);
16466335e4d5SMatthew Garrett 	}
164729cd293fSSeth Forshee }
16486335e4d5SMatthew Garrett 
16493567a4e2SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
165043d2fd3bSRafael J. Wysocki static int toshiba_acpi_suspend(struct device *device)
165129cd293fSSeth Forshee {
165243d2fd3bSRafael J. Wysocki 	struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device));
165329cd293fSSeth Forshee 	u32 result;
165429cd293fSSeth Forshee 
165529cd293fSSeth Forshee 	if (dev->hotkey_dev)
165629cd293fSSeth Forshee 		hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE, &result);
165729cd293fSSeth Forshee 
165829cd293fSSeth Forshee 	return 0;
165929cd293fSSeth Forshee }
166029cd293fSSeth Forshee 
166143d2fd3bSRafael J. Wysocki static int toshiba_acpi_resume(struct device *device)
166229cd293fSSeth Forshee {
166343d2fd3bSRafael J. Wysocki 	struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device));
166429cd293fSSeth Forshee 	u32 result;
166529cd293fSSeth Forshee 
166629cd293fSSeth Forshee 	if (dev->hotkey_dev)
166729cd293fSSeth Forshee 		hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &result);
166829cd293fSSeth Forshee 
166929cd293fSSeth Forshee 	return 0;
167029cd293fSSeth Forshee }
16713567a4e2SRafael J. Wysocki #endif
16726335e4d5SMatthew Garrett 
167343d2fd3bSRafael J. Wysocki static SIMPLE_DEV_PM_OPS(toshiba_acpi_pm,
167443d2fd3bSRafael J. Wysocki 			 toshiba_acpi_suspend, toshiba_acpi_resume);
167543d2fd3bSRafael J. Wysocki 
1676135740deSSeth Forshee static struct acpi_driver toshiba_acpi_driver = {
1677135740deSSeth Forshee 	.name	= "Toshiba ACPI driver",
1678135740deSSeth Forshee 	.owner	= THIS_MODULE,
1679135740deSSeth Forshee 	.ids	= toshiba_device_ids,
1680135740deSSeth Forshee 	.flags	= ACPI_DRIVER_ALL_NOTIFY_EVENTS,
1681135740deSSeth Forshee 	.ops	= {
1682135740deSSeth Forshee 		.add		= toshiba_acpi_add,
1683135740deSSeth Forshee 		.remove		= toshiba_acpi_remove,
1684135740deSSeth Forshee 		.notify		= toshiba_acpi_notify,
1685135740deSSeth Forshee 	},
168643d2fd3bSRafael J. Wysocki 	.drv.pm	= &toshiba_acpi_pm,
1687135740deSSeth Forshee };
1688b4f9fe12SLen Brown 
1689b4f9fe12SLen Brown static int __init toshiba_acpi_init(void)
1690b4f9fe12SLen Brown {
1691135740deSSeth Forshee 	int ret;
1692b4f9fe12SLen Brown 
1693f11f999eSSeth Forshee 	/*
1694f11f999eSSeth Forshee 	 * Machines with this WMI guid aren't supported due to bugs in
1695f11f999eSSeth Forshee 	 * their AML. This check relies on wmi initializing before
1696f11f999eSSeth Forshee 	 * toshiba_acpi to guarantee guids have been identified.
1697f11f999eSSeth Forshee 	 */
1698f11f999eSSeth Forshee 	if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
1699f11f999eSSeth Forshee 		return -ENODEV;
1700f11f999eSSeth Forshee 
1701b4f9fe12SLen Brown 	toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
1702b4f9fe12SLen Brown 	if (!toshiba_proc_dir) {
1703135740deSSeth Forshee 		pr_err("Unable to create proc dir " PROC_TOSHIBA "\n");
1704b4f9fe12SLen Brown 		return -ENODEV;
1705b4f9fe12SLen Brown 	}
1706b4f9fe12SLen Brown 
1707135740deSSeth Forshee 	ret = acpi_bus_register_driver(&toshiba_acpi_driver);
1708b4f9fe12SLen Brown 	if (ret) {
1709135740deSSeth Forshee 		pr_err("Failed to register ACPI driver: %d\n", ret);
1710135740deSSeth Forshee 		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
1711135740deSSeth Forshee 	}
1712135740deSSeth Forshee 
1713b4f9fe12SLen Brown 	return ret;
1714b4f9fe12SLen Brown }
1715b4f9fe12SLen Brown 
1716135740deSSeth Forshee static void __exit toshiba_acpi_exit(void)
1717135740deSSeth Forshee {
1718135740deSSeth Forshee 	acpi_bus_unregister_driver(&toshiba_acpi_driver);
1719135740deSSeth Forshee 	if (toshiba_proc_dir)
1720135740deSSeth Forshee 		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
1721b4f9fe12SLen Brown }
1722b4f9fe12SLen Brown 
1723b4f9fe12SLen Brown module_init(toshiba_acpi_init);
1724b4f9fe12SLen Brown module_exit(toshiba_acpi_exit);
1725