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