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 8548c4306SAzael Avalos * Copyright (C) 2014 Azael Avalos 9b4f9fe12SLen Brown * 10b4f9fe12SLen Brown * This program is free software; you can redistribute it and/or modify 11b4f9fe12SLen Brown * it under the terms of the GNU General Public License as published by 12b4f9fe12SLen Brown * the Free Software Foundation; either version 2 of the License, or 13b4f9fe12SLen Brown * (at your option) any later version. 14b4f9fe12SLen Brown * 15b4f9fe12SLen Brown * This program is distributed in the hope that it will be useful, 16b4f9fe12SLen Brown * but WITHOUT ANY WARRANTY; without even the implied warranty of 17b4f9fe12SLen Brown * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18b4f9fe12SLen Brown * GNU General Public License for more details. 19b4f9fe12SLen Brown * 20b4f9fe12SLen Brown * You should have received a copy of the GNU General Public License 21b4f9fe12SLen Brown * along with this program; if not, write to the Free Software 22b4f9fe12SLen Brown * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23b4f9fe12SLen Brown * 24b4f9fe12SLen Brown * 25b4f9fe12SLen Brown * The devolpment page for this driver is located at 26b4f9fe12SLen Brown * http://memebeam.org/toys/ToshibaAcpiDriver. 27b4f9fe12SLen Brown * 28b4f9fe12SLen Brown * Credits: 29b4f9fe12SLen Brown * Jonathan A. Buzzard - Toshiba HCI info, and critical tips on reverse 30b4f9fe12SLen Brown * engineering the Windows drivers 31b4f9fe12SLen Brown * Yasushi Nagato - changes for linux kernel 2.4 -> 2.5 32b4f9fe12SLen Brown * Rob Miller - TV out and hotkeys help 33b4f9fe12SLen Brown * 34b4f9fe12SLen Brown * 35b4f9fe12SLen Brown * TODO 36b4f9fe12SLen Brown * 37b4f9fe12SLen Brown */ 38b4f9fe12SLen Brown 397e33460dSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 407e33460dSJoe Perches 41548c4306SAzael Avalos #define TOSHIBA_ACPI_VERSION "0.20" 42b4f9fe12SLen Brown #define PROC_INTERFACE_VERSION 1 43b4f9fe12SLen Brown 44b4f9fe12SLen Brown #include <linux/kernel.h> 45b4f9fe12SLen Brown #include <linux/module.h> 46b4f9fe12SLen Brown #include <linux/init.h> 47b4f9fe12SLen Brown #include <linux/types.h> 48b4f9fe12SLen Brown #include <linux/proc_fs.h> 49936c8bcdSAlexey Dobriyan #include <linux/seq_file.h> 50b4f9fe12SLen Brown #include <linux/backlight.h> 51b4f9fe12SLen Brown #include <linux/rfkill.h> 526335e4d5SMatthew Garrett #include <linux/input.h> 53384a7cd9SDmitry Torokhov #include <linux/input/sparse-keymap.h> 546c3f6e6cSPierre Ducroquet #include <linux/leds.h> 555a0e3ad6STejun Heo #include <linux/slab.h> 5629cd293fSSeth Forshee #include <linux/workqueue.h> 5729cd293fSSeth Forshee #include <linux/i8042.h> 588b48463fSLv Zheng #include <linux/acpi.h> 59fe808bfbSTakashi Iwai #include <linux/dmi.h> 60b4f9fe12SLen Brown #include <asm/uaccess.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 66f11f999eSSeth Forshee #define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100" 67f11f999eSSeth 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. 8284a6273fSAzael Avalos * 8384a6273fSAzael Avalos * SCI stands for "System Configuration Interface" which aim is to 8484a6273fSAzael Avalos * conceal differences in hardware between different models. 85b4f9fe12SLen Brown */ 86b4f9fe12SLen Brown 87b4f9fe12SLen Brown #define HCI_WORDS 6 88b4f9fe12SLen Brown 89b4f9fe12SLen Brown /* operations */ 90b4f9fe12SLen Brown #define HCI_SET 0xff00 91b4f9fe12SLen Brown #define HCI_GET 0xfe00 9284a6273fSAzael Avalos #define SCI_OPEN 0xf100 9384a6273fSAzael Avalos #define SCI_CLOSE 0xf200 9484a6273fSAzael Avalos #define SCI_GET 0xf300 9584a6273fSAzael Avalos #define SCI_SET 0xf400 96b4f9fe12SLen Brown 97b4f9fe12SLen Brown /* return codes */ 98b4f9fe12SLen Brown #define HCI_SUCCESS 0x0000 99b4f9fe12SLen Brown #define HCI_FAILURE 0x1000 100b4f9fe12SLen Brown #define HCI_NOT_SUPPORTED 0x8000 101b4f9fe12SLen Brown #define HCI_EMPTY 0x8c00 1025a2813e9SAzael Avalos #define HCI_DATA_NOT_AVAILABLE 0x8d20 1035a2813e9SAzael Avalos #define HCI_NOT_INITIALIZED 0x8d50 10484a6273fSAzael Avalos #define SCI_OPEN_CLOSE_OK 0x0044 10584a6273fSAzael Avalos #define SCI_ALREADY_OPEN 0x8100 10684a6273fSAzael Avalos #define SCI_NOT_OPENED 0x8200 107fdb79081SAzael Avalos #define SCI_INPUT_DATA_ERROR 0x8300 10884a6273fSAzael Avalos #define SCI_NOT_PRESENT 0x8600 109b4f9fe12SLen Brown 110b4f9fe12SLen Brown /* registers */ 111b4f9fe12SLen Brown #define HCI_FAN 0x0004 112121b7b0dSAkio Idehara #define HCI_TR_BACKLIGHT 0x0005 113b4f9fe12SLen Brown #define HCI_SYSTEM_EVENT 0x0016 114b4f9fe12SLen Brown #define HCI_VIDEO_OUT 0x001c 115b4f9fe12SLen Brown #define HCI_HOTKEY_EVENT 0x001e 116b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS 0x002a 117b4f9fe12SLen Brown #define HCI_WIRELESS 0x0056 1185a2813e9SAzael Avalos #define HCI_ACCELEROMETER 0x006d 119360f0f39SAzael Avalos #define HCI_KBD_ILLUMINATION 0x0095 120def6c4e2SAzael Avalos #define HCI_ECO_MODE 0x0097 1215a2813e9SAzael Avalos #define HCI_ACCELEROMETER2 0x00a6 122fdb79081SAzael Avalos #define SCI_ILLUMINATION 0x014e 123360f0f39SAzael Avalos #define SCI_KBD_ILLUM_STATUS 0x015c 1249d8658acSAzael Avalos #define SCI_TOUCHPAD 0x050e 125b4f9fe12SLen Brown 126b4f9fe12SLen Brown /* field definitions */ 1275a2813e9SAzael Avalos #define HCI_ACCEL_MASK 0x7fff 12829cd293fSSeth Forshee #define HCI_HOTKEY_DISABLE 0x0b 12929cd293fSSeth Forshee #define HCI_HOTKEY_ENABLE 0x09 130b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_BITS 3 131b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) 132b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) 133360f0f39SAzael Avalos #define HCI_MISC_SHIFT 0x10 134b4f9fe12SLen Brown #define HCI_VIDEO_OUT_LCD 0x1 135b4f9fe12SLen Brown #define HCI_VIDEO_OUT_CRT 0x2 136b4f9fe12SLen Brown #define HCI_VIDEO_OUT_TV 0x4 137b4f9fe12SLen Brown #define HCI_WIRELESS_KILL_SWITCH 0x01 138b4f9fe12SLen Brown #define HCI_WIRELESS_BT_PRESENT 0x0f 139b4f9fe12SLen Brown #define HCI_WIRELESS_BT_ATTACH 0x40 140b4f9fe12SLen Brown #define HCI_WIRELESS_BT_POWER 0x80 141360f0f39SAzael Avalos #define SCI_KBD_MODE_FNZ 0x1 142360f0f39SAzael Avalos #define SCI_KBD_MODE_AUTO 0x2 143b4f9fe12SLen Brown 144135740deSSeth Forshee struct toshiba_acpi_dev { 145135740deSSeth Forshee struct acpi_device *acpi_dev; 146135740deSSeth Forshee const char *method_hci; 147135740deSSeth Forshee struct rfkill *bt_rfk; 148135740deSSeth Forshee struct input_dev *hotkey_dev; 14929cd293fSSeth Forshee struct work_struct hotkey_work; 150135740deSSeth Forshee struct backlight_device *backlight_dev; 151135740deSSeth Forshee struct led_classdev led_dev; 152360f0f39SAzael Avalos struct led_classdev kbd_led; 153def6c4e2SAzael Avalos struct led_classdev eco_led; 15436d03f93SSeth Forshee 155135740deSSeth Forshee int force_fan; 156135740deSSeth Forshee int last_key_event; 157135740deSSeth Forshee int key_event_valid; 158360f0f39SAzael Avalos int kbd_mode; 159360f0f39SAzael Avalos int kbd_time; 160135740deSSeth Forshee 161592b746cSDan Carpenter unsigned int illumination_supported:1; 162592b746cSDan Carpenter unsigned int video_supported:1; 163592b746cSDan Carpenter unsigned int fan_supported:1; 164592b746cSDan Carpenter unsigned int system_event_supported:1; 16529cd293fSSeth Forshee unsigned int ntfy_supported:1; 16629cd293fSSeth Forshee unsigned int info_supported:1; 167121b7b0dSAkio Idehara unsigned int tr_backlight_supported:1; 168360f0f39SAzael Avalos unsigned int kbd_illum_supported:1; 169360f0f39SAzael Avalos unsigned int kbd_led_registered:1; 1709d8658acSAzael Avalos unsigned int touchpad_supported:1; 171def6c4e2SAzael Avalos unsigned int eco_supported:1; 1725a2813e9SAzael Avalos unsigned int accelerometer_supported:1; 173360f0f39SAzael Avalos unsigned int sysfs_created:1; 17436d03f93SSeth Forshee 175135740deSSeth Forshee struct mutex mutex; 176135740deSSeth Forshee }; 177135740deSSeth Forshee 17829cd293fSSeth Forshee static struct toshiba_acpi_dev *toshiba_acpi; 17929cd293fSSeth Forshee 180b4f9fe12SLen Brown static const struct acpi_device_id toshiba_device_ids[] = { 181b4f9fe12SLen Brown {"TOS6200", 0}, 182b4f9fe12SLen Brown {"TOS6208", 0}, 183b4f9fe12SLen Brown {"TOS1900", 0}, 184b4f9fe12SLen Brown {"", 0}, 185b4f9fe12SLen Brown }; 186b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); 187b4f9fe12SLen Brown 188b859f159SGreg Kroah-Hartman static const struct key_entry toshiba_acpi_keymap[] = { 189fec278a1SUnai Uribarri { KE_KEY, 0x9e, { KEY_RFKILL } }, 190384a7cd9SDmitry Torokhov { KE_KEY, 0x101, { KEY_MUTE } }, 191384a7cd9SDmitry Torokhov { KE_KEY, 0x102, { KEY_ZOOMOUT } }, 192384a7cd9SDmitry Torokhov { KE_KEY, 0x103, { KEY_ZOOMIN } }, 193*408a5d13SAzael Avalos { KE_KEY, 0x10f, { KEY_TAB } }, 194af502837SAzael Avalos { KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } }, 195af502837SAzael Avalos { KE_KEY, 0x139, { KEY_ZOOMRESET } }, 196384a7cd9SDmitry Torokhov { KE_KEY, 0x13b, { KEY_COFFEE } }, 197384a7cd9SDmitry Torokhov { KE_KEY, 0x13c, { KEY_BATTERY } }, 198384a7cd9SDmitry Torokhov { KE_KEY, 0x13d, { KEY_SLEEP } }, 199384a7cd9SDmitry Torokhov { KE_KEY, 0x13e, { KEY_SUSPEND } }, 200384a7cd9SDmitry Torokhov { KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } }, 201384a7cd9SDmitry Torokhov { KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } }, 202384a7cd9SDmitry Torokhov { KE_KEY, 0x141, { KEY_BRIGHTNESSUP } }, 203384a7cd9SDmitry Torokhov { KE_KEY, 0x142, { KEY_WLAN } }, 204af502837SAzael Avalos { KE_KEY, 0x143, { KEY_TOUCHPAD_TOGGLE } }, 205a49010f5SJon Dowland { KE_KEY, 0x17f, { KEY_FN } }, 206384a7cd9SDmitry Torokhov { KE_KEY, 0xb05, { KEY_PROG2 } }, 207384a7cd9SDmitry Torokhov { KE_KEY, 0xb06, { KEY_WWW } }, 208384a7cd9SDmitry Torokhov { KE_KEY, 0xb07, { KEY_MAIL } }, 209384a7cd9SDmitry Torokhov { KE_KEY, 0xb30, { KEY_STOP } }, 210384a7cd9SDmitry Torokhov { KE_KEY, 0xb31, { KEY_PREVIOUSSONG } }, 211384a7cd9SDmitry Torokhov { KE_KEY, 0xb32, { KEY_NEXTSONG } }, 212384a7cd9SDmitry Torokhov { KE_KEY, 0xb33, { KEY_PLAYPAUSE } }, 213384a7cd9SDmitry Torokhov { KE_KEY, 0xb5a, { KEY_MEDIA } }, 214*408a5d13SAzael Avalos { KE_IGNORE, 0x1430, { KEY_RESERVED } }, /* Wake from sleep */ 215*408a5d13SAzael Avalos { KE_IGNORE, 0x1501, { KEY_RESERVED } }, /* Output changed */ 216*408a5d13SAzael Avalos { KE_IGNORE, 0x1502, { KEY_RESERVED } }, /* HDMI plugged/unplugged */ 217*408a5d13SAzael Avalos { KE_IGNORE, 0x1ABE, { KEY_RESERVED } }, /* Protection level set */ 218*408a5d13SAzael Avalos { KE_IGNORE, 0x1ABF, { KEY_RESERVED } }, /* Protection level off */ 219384a7cd9SDmitry Torokhov { KE_END, 0 }, 2206335e4d5SMatthew Garrett }; 2216335e4d5SMatthew Garrett 222fe808bfbSTakashi Iwai /* alternative keymap */ 223fe808bfbSTakashi Iwai static const struct dmi_system_id toshiba_alt_keymap_dmi[] = { 224fe808bfbSTakashi Iwai { 225fe808bfbSTakashi Iwai .matches = { 226fe808bfbSTakashi Iwai DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 227fe808bfbSTakashi Iwai DMI_MATCH(DMI_PRODUCT_NAME, "Satellite M840"), 228fe808bfbSTakashi Iwai }, 229fe808bfbSTakashi Iwai }, 230e6efad7fSAzael Avalos { 231e6efad7fSAzael Avalos .matches = { 232e6efad7fSAzael Avalos DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 233e6efad7fSAzael Avalos DMI_MATCH(DMI_PRODUCT_NAME, "Qosmio X75-A"), 234e6efad7fSAzael Avalos }, 235e6efad7fSAzael Avalos }, 236fe808bfbSTakashi Iwai {} 237fe808bfbSTakashi Iwai }; 238fe808bfbSTakashi Iwai 239fe808bfbSTakashi Iwai static const struct key_entry toshiba_acpi_alt_keymap[] = { 240fe808bfbSTakashi Iwai { KE_KEY, 0x157, { KEY_MUTE } }, 241fe808bfbSTakashi Iwai { KE_KEY, 0x102, { KEY_ZOOMOUT } }, 242fe808bfbSTakashi Iwai { KE_KEY, 0x103, { KEY_ZOOMIN } }, 243e6efad7fSAzael Avalos { KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } }, 244fe808bfbSTakashi Iwai { KE_KEY, 0x139, { KEY_ZOOMRESET } }, 245fe808bfbSTakashi Iwai { KE_KEY, 0x13e, { KEY_SWITCHVIDEOMODE } }, 246fe808bfbSTakashi Iwai { KE_KEY, 0x13c, { KEY_BRIGHTNESSDOWN } }, 247fe808bfbSTakashi Iwai { KE_KEY, 0x13d, { KEY_BRIGHTNESSUP } }, 248fe808bfbSTakashi Iwai { KE_KEY, 0x158, { KEY_WLAN } }, 249fe808bfbSTakashi Iwai { KE_KEY, 0x13f, { KEY_TOUCHPAD_TOGGLE } }, 250fe808bfbSTakashi Iwai { KE_END, 0 }, 251fe808bfbSTakashi Iwai }; 252fe808bfbSTakashi Iwai 253b4f9fe12SLen Brown /* utility 254b4f9fe12SLen Brown */ 255b4f9fe12SLen Brown 256b4f9fe12SLen Brown static __inline__ void _set_bit(u32 * word, u32 mask, int value) 257b4f9fe12SLen Brown { 258b4f9fe12SLen Brown *word = (*word & ~mask) | (mask * value); 259b4f9fe12SLen Brown } 260b4f9fe12SLen Brown 261b4f9fe12SLen Brown /* acpi interface wrappers 262b4f9fe12SLen Brown */ 263b4f9fe12SLen Brown 264b4f9fe12SLen Brown static int write_acpi_int(const char *methodName, int val) 265b4f9fe12SLen Brown { 266b4f9fe12SLen Brown acpi_status status; 267b4f9fe12SLen Brown 268619400daSZhang Rui status = acpi_execute_simple_method(NULL, (char *)methodName, val); 26932bcd5cbSSeth Forshee return (status == AE_OK) ? 0 : -EIO; 270b4f9fe12SLen Brown } 271b4f9fe12SLen Brown 272b4f9fe12SLen Brown /* Perform a raw HCI call. Here we don't care about input or output buffer 273b4f9fe12SLen Brown * format. 274b4f9fe12SLen Brown */ 275135740deSSeth Forshee static acpi_status hci_raw(struct toshiba_acpi_dev *dev, 276135740deSSeth Forshee const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) 277b4f9fe12SLen Brown { 278b4f9fe12SLen Brown struct acpi_object_list params; 279b4f9fe12SLen Brown union acpi_object in_objs[HCI_WORDS]; 280b4f9fe12SLen Brown struct acpi_buffer results; 281b4f9fe12SLen Brown union acpi_object out_objs[HCI_WORDS + 1]; 282b4f9fe12SLen Brown acpi_status status; 283b4f9fe12SLen Brown int i; 284b4f9fe12SLen Brown 285b4f9fe12SLen Brown params.count = HCI_WORDS; 286b4f9fe12SLen Brown params.pointer = in_objs; 287b4f9fe12SLen Brown for (i = 0; i < HCI_WORDS; ++i) { 288b4f9fe12SLen Brown in_objs[i].type = ACPI_TYPE_INTEGER; 289b4f9fe12SLen Brown in_objs[i].integer.value = in[i]; 290b4f9fe12SLen Brown } 291b4f9fe12SLen Brown 292b4f9fe12SLen Brown results.length = sizeof(out_objs); 293b4f9fe12SLen Brown results.pointer = out_objs; 294b4f9fe12SLen Brown 2956e02cc7eSSeth Forshee status = acpi_evaluate_object(dev->acpi_dev->handle, 2966e02cc7eSSeth Forshee (char *)dev->method_hci, ¶ms, 297b4f9fe12SLen Brown &results); 298b4f9fe12SLen Brown if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) { 299b4f9fe12SLen Brown for (i = 0; i < out_objs->package.count; ++i) { 300b4f9fe12SLen Brown out[i] = out_objs->package.elements[i].integer.value; 301b4f9fe12SLen Brown } 302b4f9fe12SLen Brown } 303b4f9fe12SLen Brown 304b4f9fe12SLen Brown return status; 305b4f9fe12SLen Brown } 306b4f9fe12SLen Brown 307b4f9fe12SLen Brown /* common hci tasks (get or set one or two value) 308b4f9fe12SLen Brown * 309b4f9fe12SLen Brown * In addition to the ACPI status, the HCI system returns a result which 310b4f9fe12SLen Brown * may be useful (such as "not supported"). 311b4f9fe12SLen Brown */ 312b4f9fe12SLen Brown 313135740deSSeth Forshee static acpi_status hci_write1(struct toshiba_acpi_dev *dev, u32 reg, 314135740deSSeth Forshee u32 in1, u32 *result) 315b4f9fe12SLen Brown { 316b4f9fe12SLen Brown u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 }; 317b4f9fe12SLen Brown u32 out[HCI_WORDS]; 318135740deSSeth Forshee acpi_status status = hci_raw(dev, in, out); 319b4f9fe12SLen Brown *result = (status == AE_OK) ? out[0] : HCI_FAILURE; 320b4f9fe12SLen Brown return status; 321b4f9fe12SLen Brown } 322b4f9fe12SLen Brown 323135740deSSeth Forshee static acpi_status hci_read1(struct toshiba_acpi_dev *dev, u32 reg, 324135740deSSeth Forshee u32 *out1, u32 *result) 325b4f9fe12SLen Brown { 326b4f9fe12SLen Brown u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 }; 327b4f9fe12SLen Brown u32 out[HCI_WORDS]; 328135740deSSeth Forshee acpi_status status = hci_raw(dev, in, out); 329b4f9fe12SLen Brown *out1 = out[2]; 330b4f9fe12SLen Brown *result = (status == AE_OK) ? out[0] : HCI_FAILURE; 331b4f9fe12SLen Brown return status; 332b4f9fe12SLen Brown } 333b4f9fe12SLen Brown 334135740deSSeth Forshee static acpi_status hci_write2(struct toshiba_acpi_dev *dev, u32 reg, 335135740deSSeth Forshee u32 in1, u32 in2, u32 *result) 336b4f9fe12SLen Brown { 337b4f9fe12SLen Brown u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 }; 338b4f9fe12SLen Brown u32 out[HCI_WORDS]; 339135740deSSeth Forshee acpi_status status = hci_raw(dev, in, out); 340b4f9fe12SLen Brown *result = (status == AE_OK) ? out[0] : HCI_FAILURE; 341b4f9fe12SLen Brown return status; 342b4f9fe12SLen Brown } 343b4f9fe12SLen Brown 344135740deSSeth Forshee static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg, 345135740deSSeth Forshee u32 *out1, u32 *out2, u32 *result) 346b4f9fe12SLen Brown { 347b4f9fe12SLen Brown u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 }; 348b4f9fe12SLen Brown u32 out[HCI_WORDS]; 349135740deSSeth Forshee acpi_status status = hci_raw(dev, in, out); 350b4f9fe12SLen Brown *out1 = out[2]; 351b4f9fe12SLen Brown *out2 = out[3]; 352b4f9fe12SLen Brown *result = (status == AE_OK) ? out[0] : HCI_FAILURE; 353b4f9fe12SLen Brown return status; 354b4f9fe12SLen Brown } 355b4f9fe12SLen Brown 35684a6273fSAzael Avalos /* common sci tasks 35784a6273fSAzael Avalos */ 35884a6273fSAzael Avalos 35984a6273fSAzael Avalos static int sci_open(struct toshiba_acpi_dev *dev) 36084a6273fSAzael Avalos { 36184a6273fSAzael Avalos u32 in[HCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 }; 36284a6273fSAzael Avalos u32 out[HCI_WORDS]; 36384a6273fSAzael Avalos acpi_status status; 36484a6273fSAzael Avalos 36584a6273fSAzael Avalos status = hci_raw(dev, in, out); 36684a6273fSAzael Avalos if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { 36784a6273fSAzael Avalos pr_err("ACPI call to open SCI failed\n"); 36884a6273fSAzael Avalos return 0; 36984a6273fSAzael Avalos } 37084a6273fSAzael Avalos 37184a6273fSAzael Avalos if (out[0] == SCI_OPEN_CLOSE_OK) { 37284a6273fSAzael Avalos return 1; 37384a6273fSAzael Avalos } else if (out[0] == SCI_ALREADY_OPEN) { 37484a6273fSAzael Avalos pr_info("Toshiba SCI already opened\n"); 37584a6273fSAzael Avalos return 1; 37684a6273fSAzael Avalos } else if (out[0] == SCI_NOT_PRESENT) { 37784a6273fSAzael Avalos pr_info("Toshiba SCI is not present\n"); 37884a6273fSAzael Avalos } 37984a6273fSAzael Avalos 38084a6273fSAzael Avalos return 0; 38184a6273fSAzael Avalos } 38284a6273fSAzael Avalos 38384a6273fSAzael Avalos static void sci_close(struct toshiba_acpi_dev *dev) 38484a6273fSAzael Avalos { 38584a6273fSAzael Avalos u32 in[HCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 }; 38684a6273fSAzael Avalos u32 out[HCI_WORDS]; 38784a6273fSAzael Avalos acpi_status status; 38884a6273fSAzael Avalos 38984a6273fSAzael Avalos status = hci_raw(dev, in, out); 39084a6273fSAzael Avalos if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { 39184a6273fSAzael Avalos pr_err("ACPI call to close SCI failed\n"); 39284a6273fSAzael Avalos return; 39384a6273fSAzael Avalos } 39484a6273fSAzael Avalos 39584a6273fSAzael Avalos if (out[0] == SCI_OPEN_CLOSE_OK) 39684a6273fSAzael Avalos return; 39784a6273fSAzael Avalos else if (out[0] == SCI_NOT_OPENED) 39884a6273fSAzael Avalos pr_info("Toshiba SCI not opened\n"); 39984a6273fSAzael Avalos else if (out[0] == SCI_NOT_PRESENT) 40084a6273fSAzael Avalos pr_info("Toshiba SCI is not present\n"); 40184a6273fSAzael Avalos } 40284a6273fSAzael Avalos 40384a6273fSAzael Avalos static acpi_status sci_read(struct toshiba_acpi_dev *dev, u32 reg, 40484a6273fSAzael Avalos u32 *out1, u32 *result) 40584a6273fSAzael Avalos { 40684a6273fSAzael Avalos u32 in[HCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 }; 40784a6273fSAzael Avalos u32 out[HCI_WORDS]; 40884a6273fSAzael Avalos acpi_status status = hci_raw(dev, in, out); 40984a6273fSAzael Avalos *out1 = out[2]; 41084a6273fSAzael Avalos *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE; 41184a6273fSAzael Avalos return status; 41284a6273fSAzael Avalos } 41384a6273fSAzael Avalos 41484a6273fSAzael Avalos static acpi_status sci_write(struct toshiba_acpi_dev *dev, u32 reg, 41584a6273fSAzael Avalos u32 in1, u32 *result) 41684a6273fSAzael Avalos { 41784a6273fSAzael Avalos u32 in[HCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 }; 41884a6273fSAzael Avalos u32 out[HCI_WORDS]; 41984a6273fSAzael Avalos acpi_status status = hci_raw(dev, in, out); 42084a6273fSAzael Avalos *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE; 42184a6273fSAzael Avalos return status; 42284a6273fSAzael Avalos } 42384a6273fSAzael Avalos 4246c3f6e6cSPierre Ducroquet /* Illumination support */ 425135740deSSeth Forshee static int toshiba_illumination_available(struct toshiba_acpi_dev *dev) 4266c3f6e6cSPierre Ducroquet { 427fdb79081SAzael Avalos u32 in[HCI_WORDS] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 }; 4286c3f6e6cSPierre Ducroquet u32 out[HCI_WORDS]; 4296c3f6e6cSPierre Ducroquet acpi_status status; 4306c3f6e6cSPierre Ducroquet 431fdb79081SAzael Avalos if (!sci_open(dev)) 432fdb79081SAzael Avalos return 0; 433fdb79081SAzael Avalos 434135740deSSeth Forshee status = hci_raw(dev, in, out); 435fdb79081SAzael Avalos sci_close(dev); 436fdb79081SAzael Avalos if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { 437fdb79081SAzael Avalos pr_err("ACPI call to query Illumination support failed\n"); 438fdb79081SAzael Avalos return 0; 439fdb79081SAzael Avalos } else if (out[0] == HCI_NOT_SUPPORTED || out[1] != 1) { 4407e33460dSJoe Perches pr_info("Illumination device not available\n"); 4416c3f6e6cSPierre Ducroquet return 0; 4426c3f6e6cSPierre Ducroquet } 443fdb79081SAzael Avalos 4446c3f6e6cSPierre Ducroquet return 1; 4456c3f6e6cSPierre Ducroquet } 4466c3f6e6cSPierre Ducroquet 4476c3f6e6cSPierre Ducroquet static void toshiba_illumination_set(struct led_classdev *cdev, 4486c3f6e6cSPierre Ducroquet enum led_brightness brightness) 4496c3f6e6cSPierre Ducroquet { 450135740deSSeth Forshee struct toshiba_acpi_dev *dev = container_of(cdev, 451135740deSSeth Forshee struct toshiba_acpi_dev, led_dev); 452fdb79081SAzael Avalos u32 state, result; 4536c3f6e6cSPierre Ducroquet acpi_status status; 4546c3f6e6cSPierre Ducroquet 4556c3f6e6cSPierre Ducroquet /* First request : initialize communication. */ 456fdb79081SAzael Avalos if (!sci_open(dev)) 4576c3f6e6cSPierre Ducroquet return; 4586c3f6e6cSPierre Ducroquet 459fdb79081SAzael Avalos /* Switch the illumination on/off */ 460fdb79081SAzael Avalos state = brightness ? 1 : 0; 461fdb79081SAzael Avalos status = sci_write(dev, SCI_ILLUMINATION, state, &result); 462fdb79081SAzael Avalos sci_close(dev); 4636c3f6e6cSPierre Ducroquet if (ACPI_FAILURE(status)) { 464fdb79081SAzael Avalos pr_err("ACPI call for illumination failed\n"); 465fdb79081SAzael Avalos return; 466fdb79081SAzael Avalos } else if (result == HCI_NOT_SUPPORTED) { 467fdb79081SAzael Avalos pr_info("Illumination not supported\n"); 4686c3f6e6cSPierre Ducroquet return; 4696c3f6e6cSPierre Ducroquet } 4706c3f6e6cSPierre Ducroquet } 4716c3f6e6cSPierre Ducroquet 4726c3f6e6cSPierre Ducroquet static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) 4736c3f6e6cSPierre Ducroquet { 474135740deSSeth Forshee struct toshiba_acpi_dev *dev = container_of(cdev, 475135740deSSeth Forshee struct toshiba_acpi_dev, led_dev); 476fdb79081SAzael Avalos u32 state, result; 4776c3f6e6cSPierre Ducroquet acpi_status status; 4786c3f6e6cSPierre Ducroquet 4796c3f6e6cSPierre Ducroquet /* First request : initialize communication. */ 480fdb79081SAzael Avalos if (!sci_open(dev)) 4816c3f6e6cSPierre Ducroquet return LED_OFF; 4826c3f6e6cSPierre Ducroquet 4836c3f6e6cSPierre Ducroquet /* Check the illumination */ 484fdb79081SAzael Avalos status = sci_read(dev, SCI_ILLUMINATION, &state, &result); 485fdb79081SAzael Avalos sci_close(dev); 486fdb79081SAzael Avalos if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { 487fdb79081SAzael Avalos pr_err("ACPI call for illumination failed\n"); 488fdb79081SAzael Avalos return LED_OFF; 489fdb79081SAzael Avalos } else if (result == HCI_NOT_SUPPORTED) { 490fdb79081SAzael Avalos pr_info("Illumination not supported\n"); 4916c3f6e6cSPierre Ducroquet return LED_OFF; 4926c3f6e6cSPierre Ducroquet } 4936c3f6e6cSPierre Ducroquet 494fdb79081SAzael Avalos return state ? LED_FULL : LED_OFF; 4956c3f6e6cSPierre Ducroquet } 4966c3f6e6cSPierre Ducroquet 497360f0f39SAzael Avalos /* KBD Illumination */ 498360f0f39SAzael Avalos static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time) 499360f0f39SAzael Avalos { 500360f0f39SAzael Avalos u32 result; 501360f0f39SAzael Avalos acpi_status status; 502360f0f39SAzael Avalos 503360f0f39SAzael Avalos if (!sci_open(dev)) 504360f0f39SAzael Avalos return -EIO; 505360f0f39SAzael Avalos 506360f0f39SAzael Avalos status = sci_write(dev, SCI_KBD_ILLUM_STATUS, time, &result); 507360f0f39SAzael Avalos sci_close(dev); 508360f0f39SAzael Avalos if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { 509360f0f39SAzael Avalos pr_err("ACPI call to set KBD backlight status failed\n"); 510360f0f39SAzael Avalos return -EIO; 511360f0f39SAzael Avalos } else if (result == HCI_NOT_SUPPORTED) { 512360f0f39SAzael Avalos pr_info("Keyboard backlight status not supported\n"); 513360f0f39SAzael Avalos return -ENODEV; 514360f0f39SAzael Avalos } 515360f0f39SAzael Avalos 516360f0f39SAzael Avalos return 0; 517360f0f39SAzael Avalos } 518360f0f39SAzael Avalos 519360f0f39SAzael Avalos static int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time) 520360f0f39SAzael Avalos { 521360f0f39SAzael Avalos u32 result; 522360f0f39SAzael Avalos acpi_status status; 523360f0f39SAzael Avalos 524360f0f39SAzael Avalos if (!sci_open(dev)) 525360f0f39SAzael Avalos return -EIO; 526360f0f39SAzael Avalos 527360f0f39SAzael Avalos status = sci_read(dev, SCI_KBD_ILLUM_STATUS, time, &result); 528360f0f39SAzael Avalos sci_close(dev); 529360f0f39SAzael Avalos if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { 530360f0f39SAzael Avalos pr_err("ACPI call to get KBD backlight status failed\n"); 531360f0f39SAzael Avalos return -EIO; 532360f0f39SAzael Avalos } else if (result == HCI_NOT_SUPPORTED) { 533360f0f39SAzael Avalos pr_info("Keyboard backlight status not supported\n"); 534360f0f39SAzael Avalos return -ENODEV; 535360f0f39SAzael Avalos } 536360f0f39SAzael Avalos 537360f0f39SAzael Avalos return 0; 538360f0f39SAzael Avalos } 539360f0f39SAzael Avalos 540360f0f39SAzael Avalos static enum led_brightness toshiba_kbd_backlight_get(struct led_classdev *cdev) 541360f0f39SAzael Avalos { 542360f0f39SAzael Avalos struct toshiba_acpi_dev *dev = container_of(cdev, 543360f0f39SAzael Avalos struct toshiba_acpi_dev, kbd_led); 544360f0f39SAzael Avalos u32 state, result; 545360f0f39SAzael Avalos acpi_status status; 546360f0f39SAzael Avalos 547360f0f39SAzael Avalos /* Check the keyboard backlight state */ 548360f0f39SAzael Avalos status = hci_read1(dev, HCI_KBD_ILLUMINATION, &state, &result); 549360f0f39SAzael Avalos if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { 550360f0f39SAzael Avalos pr_err("ACPI call to get the keyboard backlight failed\n"); 551360f0f39SAzael Avalos return LED_OFF; 552360f0f39SAzael Avalos } else if (result == HCI_NOT_SUPPORTED) { 553360f0f39SAzael Avalos pr_info("Keyboard backlight not supported\n"); 554360f0f39SAzael Avalos return LED_OFF; 555360f0f39SAzael Avalos } 556360f0f39SAzael Avalos 557360f0f39SAzael Avalos return state ? LED_FULL : LED_OFF; 558360f0f39SAzael Avalos } 559360f0f39SAzael Avalos 560360f0f39SAzael Avalos static void toshiba_kbd_backlight_set(struct led_classdev *cdev, 561360f0f39SAzael Avalos enum led_brightness brightness) 562360f0f39SAzael Avalos { 563360f0f39SAzael Avalos struct toshiba_acpi_dev *dev = container_of(cdev, 564360f0f39SAzael Avalos struct toshiba_acpi_dev, kbd_led); 565360f0f39SAzael Avalos u32 state, result; 566360f0f39SAzael Avalos acpi_status status; 567360f0f39SAzael Avalos 568360f0f39SAzael Avalos /* Set the keyboard backlight state */ 569360f0f39SAzael Avalos state = brightness ? 1 : 0; 570360f0f39SAzael Avalos status = hci_write1(dev, HCI_KBD_ILLUMINATION, state, &result); 571360f0f39SAzael Avalos if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { 572360f0f39SAzael Avalos pr_err("ACPI call to set KBD Illumination mode failed\n"); 573360f0f39SAzael Avalos return; 574360f0f39SAzael Avalos } else if (result == HCI_NOT_SUPPORTED) { 575360f0f39SAzael Avalos pr_info("Keyboard backlight not supported\n"); 576360f0f39SAzael Avalos return; 577360f0f39SAzael Avalos } 578360f0f39SAzael Avalos } 579360f0f39SAzael Avalos 5809d8658acSAzael Avalos /* TouchPad support */ 5819d8658acSAzael Avalos static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state) 5829d8658acSAzael Avalos { 5839d8658acSAzael Avalos u32 result; 5849d8658acSAzael Avalos acpi_status status; 5859d8658acSAzael Avalos 5869d8658acSAzael Avalos if (!sci_open(dev)) 5879d8658acSAzael Avalos return -EIO; 5889d8658acSAzael Avalos 5899d8658acSAzael Avalos status = sci_write(dev, SCI_TOUCHPAD, state, &result); 5909d8658acSAzael Avalos sci_close(dev); 5919d8658acSAzael Avalos if (ACPI_FAILURE(status)) { 5929d8658acSAzael Avalos pr_err("ACPI call to set the touchpad failed\n"); 5939d8658acSAzael Avalos return -EIO; 5949d8658acSAzael Avalos } else if (result == HCI_NOT_SUPPORTED) { 5959d8658acSAzael Avalos return -ENODEV; 5969d8658acSAzael Avalos } 5979d8658acSAzael Avalos 5989d8658acSAzael Avalos return 0; 5999d8658acSAzael Avalos } 6009d8658acSAzael Avalos 6019d8658acSAzael Avalos static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state) 6029d8658acSAzael Avalos { 6039d8658acSAzael Avalos u32 result; 6049d8658acSAzael Avalos acpi_status status; 6059d8658acSAzael Avalos 6069d8658acSAzael Avalos if (!sci_open(dev)) 6079d8658acSAzael Avalos return -EIO; 6089d8658acSAzael Avalos 6099d8658acSAzael Avalos status = sci_read(dev, SCI_TOUCHPAD, state, &result); 6109d8658acSAzael Avalos sci_close(dev); 6119d8658acSAzael Avalos if (ACPI_FAILURE(status)) { 6129d8658acSAzael Avalos pr_err("ACPI call to query the touchpad failed\n"); 6139d8658acSAzael Avalos return -EIO; 6149d8658acSAzael Avalos } else if (result == HCI_NOT_SUPPORTED) { 6159d8658acSAzael Avalos return -ENODEV; 6169d8658acSAzael Avalos } 6179d8658acSAzael Avalos 6189d8658acSAzael Avalos return 0; 6199d8658acSAzael Avalos } 6209d8658acSAzael Avalos 621def6c4e2SAzael Avalos /* Eco Mode support */ 622def6c4e2SAzael Avalos static int toshiba_eco_mode_available(struct toshiba_acpi_dev *dev) 623def6c4e2SAzael Avalos { 624def6c4e2SAzael Avalos acpi_status status; 625def6c4e2SAzael Avalos u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 }; 626def6c4e2SAzael Avalos u32 out[HCI_WORDS]; 627def6c4e2SAzael Avalos 628def6c4e2SAzael Avalos status = hci_raw(dev, in, out); 629def6c4e2SAzael Avalos if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { 630def6c4e2SAzael Avalos pr_info("ACPI call to get ECO led failed\n"); 631def6c4e2SAzael Avalos return 0; 632def6c4e2SAzael Avalos } 633def6c4e2SAzael Avalos 634def6c4e2SAzael Avalos return 1; 635def6c4e2SAzael Avalos } 636def6c4e2SAzael Avalos 637def6c4e2SAzael Avalos static enum led_brightness toshiba_eco_mode_get_status(struct led_classdev *cdev) 638def6c4e2SAzael Avalos { 639def6c4e2SAzael Avalos struct toshiba_acpi_dev *dev = container_of(cdev, 640def6c4e2SAzael Avalos struct toshiba_acpi_dev, eco_led); 641def6c4e2SAzael Avalos u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 }; 642def6c4e2SAzael Avalos u32 out[HCI_WORDS]; 643def6c4e2SAzael Avalos acpi_status status; 644def6c4e2SAzael Avalos 645def6c4e2SAzael Avalos status = hci_raw(dev, in, out); 646def6c4e2SAzael Avalos if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { 647def6c4e2SAzael Avalos pr_err("ACPI call to get ECO led failed\n"); 648def6c4e2SAzael Avalos return LED_OFF; 649def6c4e2SAzael Avalos } 650def6c4e2SAzael Avalos 651def6c4e2SAzael Avalos return out[2] ? LED_FULL : LED_OFF; 652def6c4e2SAzael Avalos } 653def6c4e2SAzael Avalos 654def6c4e2SAzael Avalos static void toshiba_eco_mode_set_status(struct led_classdev *cdev, 655def6c4e2SAzael Avalos enum led_brightness brightness) 656def6c4e2SAzael Avalos { 657def6c4e2SAzael Avalos struct toshiba_acpi_dev *dev = container_of(cdev, 658def6c4e2SAzael Avalos struct toshiba_acpi_dev, eco_led); 659def6c4e2SAzael Avalos u32 in[HCI_WORDS] = { HCI_SET, HCI_ECO_MODE, 0, 1, 0, 0 }; 660def6c4e2SAzael Avalos u32 out[HCI_WORDS]; 661def6c4e2SAzael Avalos acpi_status status; 662def6c4e2SAzael Avalos 663def6c4e2SAzael Avalos /* Switch the Eco Mode led on/off */ 664def6c4e2SAzael Avalos in[2] = (brightness) ? 1 : 0; 665def6c4e2SAzael Avalos status = hci_raw(dev, in, out); 666def6c4e2SAzael Avalos if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { 667def6c4e2SAzael Avalos pr_err("ACPI call to set ECO led failed\n"); 668def6c4e2SAzael Avalos return; 669def6c4e2SAzael Avalos } 670def6c4e2SAzael Avalos } 671def6c4e2SAzael Avalos 6725a2813e9SAzael Avalos /* Accelerometer support */ 6735a2813e9SAzael Avalos static int toshiba_accelerometer_supported(struct toshiba_acpi_dev *dev) 6745a2813e9SAzael Avalos { 6755a2813e9SAzael Avalos u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER2, 0, 0, 0, 0 }; 6765a2813e9SAzael Avalos u32 out[HCI_WORDS]; 6775a2813e9SAzael Avalos acpi_status status; 6785a2813e9SAzael Avalos 6795a2813e9SAzael Avalos /* Check if the accelerometer call exists, 6805a2813e9SAzael Avalos * this call also serves as initialization 6815a2813e9SAzael Avalos */ 6825a2813e9SAzael Avalos status = hci_raw(dev, in, out); 6835a2813e9SAzael Avalos if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { 6845a2813e9SAzael Avalos pr_err("ACPI call to query the accelerometer failed\n"); 6855a2813e9SAzael Avalos return -EIO; 6865a2813e9SAzael Avalos } else if (out[0] == HCI_DATA_NOT_AVAILABLE || 6875a2813e9SAzael Avalos out[0] == HCI_NOT_INITIALIZED) { 6885a2813e9SAzael Avalos pr_err("Accelerometer not initialized\n"); 6895a2813e9SAzael Avalos return -EIO; 6905a2813e9SAzael Avalos } else if (out[0] == HCI_NOT_SUPPORTED) { 6915a2813e9SAzael Avalos pr_info("Accelerometer not supported\n"); 6925a2813e9SAzael Avalos return -ENODEV; 6935a2813e9SAzael Avalos } 6945a2813e9SAzael Avalos 6955a2813e9SAzael Avalos return 0; 6965a2813e9SAzael Avalos } 6975a2813e9SAzael Avalos 6985a2813e9SAzael Avalos static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev, 6995a2813e9SAzael Avalos u32 *xy, u32 *z) 7005a2813e9SAzael Avalos { 7015a2813e9SAzael Avalos u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER, 0, 1, 0, 0 }; 7025a2813e9SAzael Avalos u32 out[HCI_WORDS]; 7035a2813e9SAzael Avalos acpi_status status; 7045a2813e9SAzael Avalos 7055a2813e9SAzael Avalos /* Check the Accelerometer status */ 7065a2813e9SAzael Avalos status = hci_raw(dev, in, out); 7075a2813e9SAzael Avalos if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { 7085a2813e9SAzael Avalos pr_err("ACPI call to query the accelerometer failed\n"); 7095a2813e9SAzael Avalos return -EIO; 7105a2813e9SAzael Avalos } 7115a2813e9SAzael Avalos 7125a2813e9SAzael Avalos *xy = out[2]; 7135a2813e9SAzael Avalos *z = out[4]; 7145a2813e9SAzael Avalos 7155a2813e9SAzael Avalos return 0; 7165a2813e9SAzael Avalos } 7175a2813e9SAzael Avalos 718b4f9fe12SLen Brown /* Bluetooth rfkill handlers */ 719b4f9fe12SLen Brown 720135740deSSeth Forshee static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present) 721b4f9fe12SLen Brown { 722b4f9fe12SLen Brown u32 hci_result; 723b4f9fe12SLen Brown u32 value, value2; 724b4f9fe12SLen Brown 725b4f9fe12SLen Brown value = 0; 726b4f9fe12SLen Brown value2 = 0; 727135740deSSeth Forshee hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result); 728b4f9fe12SLen Brown if (hci_result == HCI_SUCCESS) 729b4f9fe12SLen Brown *present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false; 730b4f9fe12SLen Brown 731b4f9fe12SLen Brown return hci_result; 732b4f9fe12SLen Brown } 733b4f9fe12SLen Brown 734135740deSSeth Forshee static u32 hci_get_radio_state(struct toshiba_acpi_dev *dev, bool *radio_state) 735b4f9fe12SLen Brown { 736b4f9fe12SLen Brown u32 hci_result; 737b4f9fe12SLen Brown u32 value, value2; 738b4f9fe12SLen Brown 739b4f9fe12SLen Brown value = 0; 740b4f9fe12SLen Brown value2 = 0x0001; 741135740deSSeth Forshee hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result); 742b4f9fe12SLen Brown 743b4f9fe12SLen Brown *radio_state = value & HCI_WIRELESS_KILL_SWITCH; 744b4f9fe12SLen Brown return hci_result; 745b4f9fe12SLen Brown } 746b4f9fe12SLen Brown 74719d337dfSJohannes Berg static int bt_rfkill_set_block(void *data, bool blocked) 748b4f9fe12SLen Brown { 74919d337dfSJohannes Berg struct toshiba_acpi_dev *dev = data; 750b4f9fe12SLen Brown u32 result1, result2; 751b4f9fe12SLen Brown u32 value; 75219d337dfSJohannes Berg int err; 753b4f9fe12SLen Brown bool radio_state; 754b4f9fe12SLen Brown 75519d337dfSJohannes Berg value = (blocked == false); 756b4f9fe12SLen Brown 757b4f9fe12SLen Brown mutex_lock(&dev->mutex); 758135740deSSeth Forshee if (hci_get_radio_state(dev, &radio_state) != HCI_SUCCESS) { 75932bcd5cbSSeth Forshee err = -EIO; 76019d337dfSJohannes Berg goto out; 761b4f9fe12SLen Brown } 762b4f9fe12SLen Brown 76319d337dfSJohannes Berg if (!radio_state) { 76419d337dfSJohannes Berg err = 0; 76519d337dfSJohannes Berg goto out; 76619d337dfSJohannes Berg } 76719d337dfSJohannes Berg 768135740deSSeth Forshee hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1); 769135740deSSeth Forshee hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); 77019d337dfSJohannes Berg 77119d337dfSJohannes Berg if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS) 77232bcd5cbSSeth Forshee err = -EIO; 77319d337dfSJohannes Berg else 77419d337dfSJohannes Berg err = 0; 77519d337dfSJohannes Berg out: 77619d337dfSJohannes Berg mutex_unlock(&dev->mutex); 77719d337dfSJohannes Berg return err; 77819d337dfSJohannes Berg } 77919d337dfSJohannes Berg 78019d337dfSJohannes Berg static void bt_rfkill_poll(struct rfkill *rfkill, void *data) 781b4f9fe12SLen Brown { 782b4f9fe12SLen Brown bool new_rfk_state; 783b4f9fe12SLen Brown bool value; 784b4f9fe12SLen Brown u32 hci_result; 78519d337dfSJohannes Berg struct toshiba_acpi_dev *dev = data; 78619d337dfSJohannes Berg 78719d337dfSJohannes Berg mutex_lock(&dev->mutex); 788b4f9fe12SLen Brown 789135740deSSeth Forshee hci_result = hci_get_radio_state(dev, &value); 79019d337dfSJohannes Berg if (hci_result != HCI_SUCCESS) { 79119d337dfSJohannes Berg /* Can't do anything useful */ 79219d337dfSJohannes Berg mutex_unlock(&dev->mutex); 79382e7784fSJiri Slaby return; 79419d337dfSJohannes Berg } 795b4f9fe12SLen Brown 796b4f9fe12SLen Brown new_rfk_state = value; 797b4f9fe12SLen Brown 798b4f9fe12SLen Brown mutex_unlock(&dev->mutex); 799b4f9fe12SLen Brown 80019d337dfSJohannes Berg if (rfkill_set_hw_state(rfkill, !new_rfk_state)) 80119d337dfSJohannes Berg bt_rfkill_set_block(data, true); 802b4f9fe12SLen Brown } 80319d337dfSJohannes Berg 80419d337dfSJohannes Berg static const struct rfkill_ops toshiba_rfk_ops = { 80519d337dfSJohannes Berg .set_block = bt_rfkill_set_block, 80619d337dfSJohannes Berg .poll = bt_rfkill_poll, 80719d337dfSJohannes Berg }; 808b4f9fe12SLen Brown 809121b7b0dSAkio Idehara static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, bool *enabled) 810121b7b0dSAkio Idehara { 811121b7b0dSAkio Idehara u32 hci_result; 812121b7b0dSAkio Idehara u32 status; 813121b7b0dSAkio Idehara 814121b7b0dSAkio Idehara hci_read1(dev, HCI_TR_BACKLIGHT, &status, &hci_result); 815121b7b0dSAkio Idehara *enabled = !status; 816121b7b0dSAkio Idehara return hci_result == HCI_SUCCESS ? 0 : -EIO; 817121b7b0dSAkio Idehara } 818121b7b0dSAkio Idehara 819121b7b0dSAkio Idehara static int set_tr_backlight_status(struct toshiba_acpi_dev *dev, bool enable) 820121b7b0dSAkio Idehara { 821121b7b0dSAkio Idehara u32 hci_result; 822121b7b0dSAkio Idehara u32 value = !enable; 823121b7b0dSAkio Idehara 824121b7b0dSAkio Idehara hci_write1(dev, HCI_TR_BACKLIGHT, value, &hci_result); 825121b7b0dSAkio Idehara return hci_result == HCI_SUCCESS ? 0 : -EIO; 826121b7b0dSAkio Idehara } 827121b7b0dSAkio Idehara 828b4f9fe12SLen Brown static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; 829b4f9fe12SLen Brown 83062cce752SSeth Forshee static int __get_lcd_brightness(struct toshiba_acpi_dev *dev) 831b4f9fe12SLen Brown { 832b4f9fe12SLen Brown u32 hci_result; 833b4f9fe12SLen Brown u32 value; 834121b7b0dSAkio Idehara int brightness = 0; 835121b7b0dSAkio Idehara 836121b7b0dSAkio Idehara if (dev->tr_backlight_supported) { 837121b7b0dSAkio Idehara bool enabled; 838121b7b0dSAkio Idehara int ret = get_tr_backlight_status(dev, &enabled); 839121b7b0dSAkio Idehara if (ret) 840121b7b0dSAkio Idehara return ret; 841121b7b0dSAkio Idehara if (enabled) 842121b7b0dSAkio Idehara return 0; 843121b7b0dSAkio Idehara brightness++; 844121b7b0dSAkio Idehara } 845b4f9fe12SLen Brown 846135740deSSeth Forshee hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result); 84732bcd5cbSSeth Forshee if (hci_result == HCI_SUCCESS) 848121b7b0dSAkio Idehara return brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT); 84932bcd5cbSSeth Forshee 85032bcd5cbSSeth Forshee return -EIO; 851b4f9fe12SLen Brown } 852b4f9fe12SLen Brown 85362cce752SSeth Forshee static int get_lcd_brightness(struct backlight_device *bd) 85462cce752SSeth Forshee { 85562cce752SSeth Forshee struct toshiba_acpi_dev *dev = bl_get_data(bd); 85662cce752SSeth Forshee return __get_lcd_brightness(dev); 85762cce752SSeth Forshee } 85862cce752SSeth Forshee 859936c8bcdSAlexey Dobriyan static int lcd_proc_show(struct seq_file *m, void *v) 860b4f9fe12SLen Brown { 861135740deSSeth Forshee struct toshiba_acpi_dev *dev = m->private; 862135740deSSeth Forshee int value; 863121b7b0dSAkio Idehara int levels; 864b4f9fe12SLen Brown 865135740deSSeth Forshee if (!dev->backlight_dev) 866135740deSSeth Forshee return -ENODEV; 867135740deSSeth Forshee 868121b7b0dSAkio Idehara levels = dev->backlight_dev->props.max_brightness + 1; 86962cce752SSeth Forshee value = get_lcd_brightness(dev->backlight_dev); 870b4f9fe12SLen Brown if (value >= 0) { 871936c8bcdSAlexey Dobriyan seq_printf(m, "brightness: %d\n", value); 872121b7b0dSAkio Idehara seq_printf(m, "brightness_levels: %d\n", levels); 87332bcd5cbSSeth Forshee return 0; 874b4f9fe12SLen Brown } 875b4f9fe12SLen Brown 87632bcd5cbSSeth Forshee pr_err("Error reading LCD brightness\n"); 87732bcd5cbSSeth Forshee return -EIO; 878936c8bcdSAlexey Dobriyan } 879936c8bcdSAlexey Dobriyan 880936c8bcdSAlexey Dobriyan static int lcd_proc_open(struct inode *inode, struct file *file) 881936c8bcdSAlexey Dobriyan { 882d9dda78bSAl Viro return single_open(file, lcd_proc_show, PDE_DATA(inode)); 883b4f9fe12SLen Brown } 884b4f9fe12SLen Brown 88562cce752SSeth Forshee static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value) 886b4f9fe12SLen Brown { 887f6aac652SAzael Avalos u32 in[HCI_WORDS] = { HCI_SET, HCI_LCD_BRIGHTNESS, 0, 0, 0, 0 }; 888f6aac652SAzael Avalos u32 out[HCI_WORDS]; 889f6aac652SAzael Avalos acpi_status status; 890b4f9fe12SLen Brown 891121b7b0dSAkio Idehara if (dev->tr_backlight_supported) { 892121b7b0dSAkio Idehara bool enable = !value; 893121b7b0dSAkio Idehara int ret = set_tr_backlight_status(dev, enable); 894121b7b0dSAkio Idehara if (ret) 895121b7b0dSAkio Idehara return ret; 896121b7b0dSAkio Idehara if (value) 897121b7b0dSAkio Idehara value--; 898121b7b0dSAkio Idehara } 899121b7b0dSAkio Idehara 900f6aac652SAzael Avalos in[2] = value << HCI_LCD_BRIGHTNESS_SHIFT; 901f6aac652SAzael Avalos status = hci_raw(dev, in, out); 902f6aac652SAzael Avalos if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { 903f6aac652SAzael Avalos pr_err("ACPI call to set brightness failed"); 904f6aac652SAzael Avalos return -EIO; 905f6aac652SAzael Avalos } 906f6aac652SAzael Avalos /* Extra check for "incomplete" backlight method, where the AML code 907f6aac652SAzael Avalos * doesn't check for HCI_SET or HCI_GET and returns HCI_SUCCESS, 908f6aac652SAzael Avalos * the actual brightness, and in some cases the max brightness. 909f6aac652SAzael Avalos */ 910f6aac652SAzael Avalos if (out[2] > 0 || out[3] == 0xE000) 911f6aac652SAzael Avalos return -ENODEV; 912f6aac652SAzael Avalos 913f6aac652SAzael Avalos return out[0] == HCI_SUCCESS ? 0 : -EIO; 914b4f9fe12SLen Brown } 915b4f9fe12SLen Brown 916b4f9fe12SLen Brown static int set_lcd_status(struct backlight_device *bd) 917b4f9fe12SLen Brown { 918135740deSSeth Forshee struct toshiba_acpi_dev *dev = bl_get_data(bd); 91962cce752SSeth Forshee return set_lcd_brightness(dev, bd->props.brightness); 920b4f9fe12SLen Brown } 921b4f9fe12SLen Brown 922936c8bcdSAlexey Dobriyan static ssize_t lcd_proc_write(struct file *file, const char __user *buf, 923936c8bcdSAlexey Dobriyan size_t count, loff_t *pos) 924b4f9fe12SLen Brown { 925d9dda78bSAl Viro struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file)); 926936c8bcdSAlexey Dobriyan char cmd[42]; 927936c8bcdSAlexey Dobriyan size_t len; 928b4f9fe12SLen Brown int value; 929b4f9fe12SLen Brown int ret; 930121b7b0dSAkio Idehara int levels = dev->backlight_dev->props.max_brightness + 1; 931b4f9fe12SLen Brown 932936c8bcdSAlexey Dobriyan len = min(count, sizeof(cmd) - 1); 933936c8bcdSAlexey Dobriyan if (copy_from_user(cmd, buf, len)) 934936c8bcdSAlexey Dobriyan return -EFAULT; 935936c8bcdSAlexey Dobriyan cmd[len] = '\0'; 936936c8bcdSAlexey Dobriyan 937936c8bcdSAlexey Dobriyan if (sscanf(cmd, " brightness : %i", &value) == 1 && 938121b7b0dSAkio Idehara value >= 0 && value < levels) { 93962cce752SSeth Forshee ret = set_lcd_brightness(dev, value); 940b4f9fe12SLen Brown if (ret == 0) 941b4f9fe12SLen Brown ret = count; 942b4f9fe12SLen Brown } else { 943b4f9fe12SLen Brown ret = -EINVAL; 944b4f9fe12SLen Brown } 945b4f9fe12SLen Brown return ret; 946b4f9fe12SLen Brown } 947b4f9fe12SLen Brown 948936c8bcdSAlexey Dobriyan static const struct file_operations lcd_proc_fops = { 949936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 950936c8bcdSAlexey Dobriyan .open = lcd_proc_open, 951936c8bcdSAlexey Dobriyan .read = seq_read, 952936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 953936c8bcdSAlexey Dobriyan .release = single_release, 954936c8bcdSAlexey Dobriyan .write = lcd_proc_write, 955936c8bcdSAlexey Dobriyan }; 956936c8bcdSAlexey Dobriyan 95736d03f93SSeth Forshee static int get_video_status(struct toshiba_acpi_dev *dev, u32 *status) 95836d03f93SSeth Forshee { 95936d03f93SSeth Forshee u32 hci_result; 96036d03f93SSeth Forshee 96136d03f93SSeth Forshee hci_read1(dev, HCI_VIDEO_OUT, status, &hci_result); 96236d03f93SSeth Forshee return hci_result == HCI_SUCCESS ? 0 : -EIO; 96336d03f93SSeth Forshee } 96436d03f93SSeth Forshee 965936c8bcdSAlexey Dobriyan static int video_proc_show(struct seq_file *m, void *v) 966b4f9fe12SLen Brown { 967135740deSSeth Forshee struct toshiba_acpi_dev *dev = m->private; 968b4f9fe12SLen Brown u32 value; 96936d03f93SSeth Forshee int ret; 970b4f9fe12SLen Brown 97136d03f93SSeth Forshee ret = get_video_status(dev, &value); 97236d03f93SSeth Forshee if (!ret) { 973b4f9fe12SLen Brown int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; 974b4f9fe12SLen Brown int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; 975b4f9fe12SLen Brown int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0; 976936c8bcdSAlexey Dobriyan seq_printf(m, "lcd_out: %d\n", is_lcd); 977936c8bcdSAlexey Dobriyan seq_printf(m, "crt_out: %d\n", is_crt); 978936c8bcdSAlexey Dobriyan seq_printf(m, "tv_out: %d\n", is_tv); 979b4f9fe12SLen Brown } 980b4f9fe12SLen Brown 98136d03f93SSeth Forshee return ret; 982b4f9fe12SLen Brown } 983b4f9fe12SLen Brown 984936c8bcdSAlexey Dobriyan static int video_proc_open(struct inode *inode, struct file *file) 985b4f9fe12SLen Brown { 986d9dda78bSAl Viro return single_open(file, video_proc_show, PDE_DATA(inode)); 987936c8bcdSAlexey Dobriyan } 988936c8bcdSAlexey Dobriyan 989936c8bcdSAlexey Dobriyan static ssize_t video_proc_write(struct file *file, const char __user *buf, 990936c8bcdSAlexey Dobriyan size_t count, loff_t *pos) 991936c8bcdSAlexey Dobriyan { 992d9dda78bSAl Viro struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file)); 993936c8bcdSAlexey Dobriyan char *cmd, *buffer; 99436d03f93SSeth Forshee int ret; 995b4f9fe12SLen Brown int value; 996b4f9fe12SLen Brown int remain = count; 997b4f9fe12SLen Brown int lcd_out = -1; 998b4f9fe12SLen Brown int crt_out = -1; 999b4f9fe12SLen Brown int tv_out = -1; 1000b4f9fe12SLen Brown u32 video_out; 1001b4f9fe12SLen Brown 1002936c8bcdSAlexey Dobriyan cmd = kmalloc(count + 1, GFP_KERNEL); 1003936c8bcdSAlexey Dobriyan if (!cmd) 1004936c8bcdSAlexey Dobriyan return -ENOMEM; 1005936c8bcdSAlexey Dobriyan if (copy_from_user(cmd, buf, count)) { 1006936c8bcdSAlexey Dobriyan kfree(cmd); 1007936c8bcdSAlexey Dobriyan return -EFAULT; 1008936c8bcdSAlexey Dobriyan } 1009936c8bcdSAlexey Dobriyan cmd[count] = '\0'; 1010936c8bcdSAlexey Dobriyan 1011936c8bcdSAlexey Dobriyan buffer = cmd; 1012936c8bcdSAlexey Dobriyan 1013b4f9fe12SLen Brown /* scan expression. Multiple expressions may be delimited with ; 1014b4f9fe12SLen Brown * 1015b4f9fe12SLen Brown * NOTE: to keep scanning simple, invalid fields are ignored 1016b4f9fe12SLen Brown */ 1017b4f9fe12SLen Brown while (remain) { 1018b4f9fe12SLen Brown if (sscanf(buffer, " lcd_out : %i", &value) == 1) 1019b4f9fe12SLen Brown lcd_out = value & 1; 1020b4f9fe12SLen Brown else if (sscanf(buffer, " crt_out : %i", &value) == 1) 1021b4f9fe12SLen Brown crt_out = value & 1; 1022b4f9fe12SLen Brown else if (sscanf(buffer, " tv_out : %i", &value) == 1) 1023b4f9fe12SLen Brown tv_out = value & 1; 1024b4f9fe12SLen Brown /* advance to one character past the next ; */ 1025b4f9fe12SLen Brown do { 1026b4f9fe12SLen Brown ++buffer; 1027b4f9fe12SLen Brown --remain; 1028b4f9fe12SLen Brown } 1029b4f9fe12SLen Brown while (remain && *(buffer - 1) != ';'); 1030b4f9fe12SLen Brown } 1031b4f9fe12SLen Brown 1032936c8bcdSAlexey Dobriyan kfree(cmd); 1033936c8bcdSAlexey Dobriyan 103436d03f93SSeth Forshee ret = get_video_status(dev, &video_out); 103536d03f93SSeth Forshee if (!ret) { 1036b4f9fe12SLen Brown unsigned int new_video_out = video_out; 1037b4f9fe12SLen Brown if (lcd_out != -1) 1038b4f9fe12SLen Brown _set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out); 1039b4f9fe12SLen Brown if (crt_out != -1) 1040b4f9fe12SLen Brown _set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out); 1041b4f9fe12SLen Brown if (tv_out != -1) 1042b4f9fe12SLen Brown _set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out); 1043b4f9fe12SLen Brown /* To avoid unnecessary video disruption, only write the new 1044b4f9fe12SLen Brown * video setting if something changed. */ 1045b4f9fe12SLen Brown if (new_video_out != video_out) 104632bcd5cbSSeth Forshee ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out); 1047b4f9fe12SLen Brown } 1048b4f9fe12SLen Brown 104932bcd5cbSSeth Forshee return ret ? ret : count; 1050b4f9fe12SLen Brown } 1051b4f9fe12SLen Brown 1052936c8bcdSAlexey Dobriyan static const struct file_operations video_proc_fops = { 1053936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 1054936c8bcdSAlexey Dobriyan .open = video_proc_open, 1055936c8bcdSAlexey Dobriyan .read = seq_read, 1056936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 1057936c8bcdSAlexey Dobriyan .release = single_release, 1058936c8bcdSAlexey Dobriyan .write = video_proc_write, 1059936c8bcdSAlexey Dobriyan }; 1060936c8bcdSAlexey Dobriyan 106136d03f93SSeth Forshee static int get_fan_status(struct toshiba_acpi_dev *dev, u32 *status) 106236d03f93SSeth Forshee { 106336d03f93SSeth Forshee u32 hci_result; 106436d03f93SSeth Forshee 106536d03f93SSeth Forshee hci_read1(dev, HCI_FAN, status, &hci_result); 106636d03f93SSeth Forshee return hci_result == HCI_SUCCESS ? 0 : -EIO; 106736d03f93SSeth Forshee } 106836d03f93SSeth Forshee 1069936c8bcdSAlexey Dobriyan static int fan_proc_show(struct seq_file *m, void *v) 1070b4f9fe12SLen Brown { 1071135740deSSeth Forshee struct toshiba_acpi_dev *dev = m->private; 107236d03f93SSeth Forshee int ret; 1073b4f9fe12SLen Brown u32 value; 1074b4f9fe12SLen Brown 107536d03f93SSeth Forshee ret = get_fan_status(dev, &value); 107636d03f93SSeth Forshee if (!ret) { 1077936c8bcdSAlexey Dobriyan seq_printf(m, "running: %d\n", (value > 0)); 1078135740deSSeth Forshee seq_printf(m, "force_on: %d\n", dev->force_fan); 1079b4f9fe12SLen Brown } 1080b4f9fe12SLen Brown 108136d03f93SSeth Forshee return ret; 1082b4f9fe12SLen Brown } 1083b4f9fe12SLen Brown 1084936c8bcdSAlexey Dobriyan static int fan_proc_open(struct inode *inode, struct file *file) 1085b4f9fe12SLen Brown { 1086d9dda78bSAl Viro return single_open(file, fan_proc_show, PDE_DATA(inode)); 1087936c8bcdSAlexey Dobriyan } 1088936c8bcdSAlexey Dobriyan 1089936c8bcdSAlexey Dobriyan static ssize_t fan_proc_write(struct file *file, const char __user *buf, 1090936c8bcdSAlexey Dobriyan size_t count, loff_t *pos) 1091936c8bcdSAlexey Dobriyan { 1092d9dda78bSAl Viro struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file)); 1093936c8bcdSAlexey Dobriyan char cmd[42]; 1094936c8bcdSAlexey Dobriyan size_t len; 1095b4f9fe12SLen Brown int value; 1096b4f9fe12SLen Brown u32 hci_result; 1097b4f9fe12SLen Brown 1098936c8bcdSAlexey Dobriyan len = min(count, sizeof(cmd) - 1); 1099936c8bcdSAlexey Dobriyan if (copy_from_user(cmd, buf, len)) 1100936c8bcdSAlexey Dobriyan return -EFAULT; 1101936c8bcdSAlexey Dobriyan cmd[len] = '\0'; 1102936c8bcdSAlexey Dobriyan 1103936c8bcdSAlexey Dobriyan if (sscanf(cmd, " force_on : %i", &value) == 1 && 1104b4f9fe12SLen Brown value >= 0 && value <= 1) { 1105135740deSSeth Forshee hci_write1(dev, HCI_FAN, value, &hci_result); 1106b4f9fe12SLen Brown if (hci_result != HCI_SUCCESS) 110732bcd5cbSSeth Forshee return -EIO; 1108b4f9fe12SLen Brown else 1109135740deSSeth Forshee dev->force_fan = value; 1110b4f9fe12SLen Brown } else { 1111b4f9fe12SLen Brown return -EINVAL; 1112b4f9fe12SLen Brown } 1113b4f9fe12SLen Brown 1114b4f9fe12SLen Brown return count; 1115b4f9fe12SLen Brown } 1116b4f9fe12SLen Brown 1117936c8bcdSAlexey Dobriyan static const struct file_operations fan_proc_fops = { 1118936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 1119936c8bcdSAlexey Dobriyan .open = fan_proc_open, 1120936c8bcdSAlexey Dobriyan .read = seq_read, 1121936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 1122936c8bcdSAlexey Dobriyan .release = single_release, 1123936c8bcdSAlexey Dobriyan .write = fan_proc_write, 1124936c8bcdSAlexey Dobriyan }; 1125936c8bcdSAlexey Dobriyan 1126936c8bcdSAlexey Dobriyan static int keys_proc_show(struct seq_file *m, void *v) 1127b4f9fe12SLen Brown { 1128135740deSSeth Forshee struct toshiba_acpi_dev *dev = m->private; 1129b4f9fe12SLen Brown u32 hci_result; 1130b4f9fe12SLen Brown u32 value; 1131b4f9fe12SLen Brown 113211948b93SSeth Forshee if (!dev->key_event_valid && dev->system_event_supported) { 1133135740deSSeth Forshee hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); 1134b4f9fe12SLen Brown if (hci_result == HCI_SUCCESS) { 1135135740deSSeth Forshee dev->key_event_valid = 1; 1136135740deSSeth Forshee dev->last_key_event = value; 1137b4f9fe12SLen Brown } else if (hci_result == HCI_EMPTY) { 1138b4f9fe12SLen Brown /* better luck next time */ 1139b4f9fe12SLen Brown } else if (hci_result == HCI_NOT_SUPPORTED) { 1140b4f9fe12SLen Brown /* This is a workaround for an unresolved issue on 1141b4f9fe12SLen Brown * some machines where system events sporadically 1142b4f9fe12SLen Brown * become disabled. */ 1143135740deSSeth Forshee hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); 11447e33460dSJoe Perches pr_notice("Re-enabled hotkeys\n"); 1145b4f9fe12SLen Brown } else { 11467e33460dSJoe Perches pr_err("Error reading hotkey status\n"); 114732bcd5cbSSeth Forshee return -EIO; 1148b4f9fe12SLen Brown } 1149b4f9fe12SLen Brown } 1150b4f9fe12SLen Brown 1151135740deSSeth Forshee seq_printf(m, "hotkey_ready: %d\n", dev->key_event_valid); 1152135740deSSeth Forshee seq_printf(m, "hotkey: 0x%04x\n", dev->last_key_event); 1153936c8bcdSAlexey Dobriyan return 0; 1154b4f9fe12SLen Brown } 1155b4f9fe12SLen Brown 1156936c8bcdSAlexey Dobriyan static int keys_proc_open(struct inode *inode, struct file *file) 1157b4f9fe12SLen Brown { 1158d9dda78bSAl Viro return single_open(file, keys_proc_show, PDE_DATA(inode)); 1159936c8bcdSAlexey Dobriyan } 1160936c8bcdSAlexey Dobriyan 1161936c8bcdSAlexey Dobriyan static ssize_t keys_proc_write(struct file *file, const char __user *buf, 1162936c8bcdSAlexey Dobriyan size_t count, loff_t *pos) 1163936c8bcdSAlexey Dobriyan { 1164d9dda78bSAl Viro struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file)); 1165936c8bcdSAlexey Dobriyan char cmd[42]; 1166936c8bcdSAlexey Dobriyan size_t len; 1167b4f9fe12SLen Brown int value; 1168b4f9fe12SLen Brown 1169936c8bcdSAlexey Dobriyan len = min(count, sizeof(cmd) - 1); 1170936c8bcdSAlexey Dobriyan if (copy_from_user(cmd, buf, len)) 1171936c8bcdSAlexey Dobriyan return -EFAULT; 1172936c8bcdSAlexey Dobriyan cmd[len] = '\0'; 1173936c8bcdSAlexey Dobriyan 1174936c8bcdSAlexey Dobriyan if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) { 1175135740deSSeth Forshee dev->key_event_valid = 0; 1176b4f9fe12SLen Brown } else { 1177b4f9fe12SLen Brown return -EINVAL; 1178b4f9fe12SLen Brown } 1179b4f9fe12SLen Brown 1180b4f9fe12SLen Brown return count; 1181b4f9fe12SLen Brown } 1182b4f9fe12SLen Brown 1183936c8bcdSAlexey Dobriyan static const struct file_operations keys_proc_fops = { 1184936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 1185936c8bcdSAlexey Dobriyan .open = keys_proc_open, 1186936c8bcdSAlexey Dobriyan .read = seq_read, 1187936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 1188936c8bcdSAlexey Dobriyan .release = single_release, 1189936c8bcdSAlexey Dobriyan .write = keys_proc_write, 1190936c8bcdSAlexey Dobriyan }; 1191936c8bcdSAlexey Dobriyan 1192936c8bcdSAlexey Dobriyan static int version_proc_show(struct seq_file *m, void *v) 1193b4f9fe12SLen Brown { 1194936c8bcdSAlexey Dobriyan seq_printf(m, "driver: %s\n", TOSHIBA_ACPI_VERSION); 1195936c8bcdSAlexey Dobriyan seq_printf(m, "proc_interface: %d\n", PROC_INTERFACE_VERSION); 1196936c8bcdSAlexey Dobriyan return 0; 1197b4f9fe12SLen Brown } 1198b4f9fe12SLen Brown 1199936c8bcdSAlexey Dobriyan static int version_proc_open(struct inode *inode, struct file *file) 1200936c8bcdSAlexey Dobriyan { 1201d9dda78bSAl Viro return single_open(file, version_proc_show, PDE_DATA(inode)); 1202936c8bcdSAlexey Dobriyan } 1203936c8bcdSAlexey Dobriyan 1204936c8bcdSAlexey Dobriyan static const struct file_operations version_proc_fops = { 1205936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 1206936c8bcdSAlexey Dobriyan .open = version_proc_open, 1207936c8bcdSAlexey Dobriyan .read = seq_read, 1208936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 1209936c8bcdSAlexey Dobriyan .release = single_release, 1210936c8bcdSAlexey Dobriyan }; 1211936c8bcdSAlexey Dobriyan 1212b4f9fe12SLen Brown /* proc and module init 1213b4f9fe12SLen Brown */ 1214b4f9fe12SLen Brown 1215b4f9fe12SLen Brown #define PROC_TOSHIBA "toshiba" 1216b4f9fe12SLen Brown 1217b859f159SGreg Kroah-Hartman static void create_toshiba_proc_entries(struct toshiba_acpi_dev *dev) 1218b4f9fe12SLen Brown { 121936d03f93SSeth Forshee if (dev->backlight_dev) 1220135740deSSeth Forshee proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, 1221135740deSSeth Forshee &lcd_proc_fops, dev); 122236d03f93SSeth Forshee if (dev->video_supported) 1223135740deSSeth Forshee proc_create_data("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, 1224135740deSSeth Forshee &video_proc_fops, dev); 122536d03f93SSeth Forshee if (dev->fan_supported) 1226135740deSSeth Forshee proc_create_data("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, 1227135740deSSeth Forshee &fan_proc_fops, dev); 122836d03f93SSeth Forshee if (dev->hotkey_dev) 1229135740deSSeth Forshee proc_create_data("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, 1230135740deSSeth Forshee &keys_proc_fops, dev); 1231135740deSSeth Forshee proc_create_data("version", S_IRUGO, toshiba_proc_dir, 1232135740deSSeth Forshee &version_proc_fops, dev); 1233b4f9fe12SLen Brown } 1234b4f9fe12SLen Brown 123536d03f93SSeth Forshee static void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev) 1236b4f9fe12SLen Brown { 123736d03f93SSeth Forshee if (dev->backlight_dev) 1238936c8bcdSAlexey Dobriyan remove_proc_entry("lcd", toshiba_proc_dir); 123936d03f93SSeth Forshee if (dev->video_supported) 1240936c8bcdSAlexey Dobriyan remove_proc_entry("video", toshiba_proc_dir); 124136d03f93SSeth Forshee if (dev->fan_supported) 1242936c8bcdSAlexey Dobriyan remove_proc_entry("fan", toshiba_proc_dir); 124336d03f93SSeth Forshee if (dev->hotkey_dev) 1244936c8bcdSAlexey Dobriyan remove_proc_entry("keys", toshiba_proc_dir); 1245936c8bcdSAlexey Dobriyan remove_proc_entry("version", toshiba_proc_dir); 1246b4f9fe12SLen Brown } 1247b4f9fe12SLen Brown 1248acc2472eSLionel Debroux static const struct backlight_ops toshiba_backlight_data = { 1249121b7b0dSAkio Idehara .options = BL_CORE_SUSPENDRESUME, 125062cce752SSeth Forshee .get_brightness = get_lcd_brightness, 1251b4f9fe12SLen Brown .update_status = set_lcd_status, 1252b4f9fe12SLen Brown }; 1253b4f9fe12SLen Brown 1254360f0f39SAzael Avalos /* 1255360f0f39SAzael Avalos * Sysfs files 1256360f0f39SAzael Avalos */ 1257360f0f39SAzael Avalos 1258360f0f39SAzael Avalos static ssize_t toshiba_kbd_bl_mode_store(struct device *dev, 1259360f0f39SAzael Avalos struct device_attribute *attr, 1260360f0f39SAzael Avalos const char *buf, size_t count) 1261360f0f39SAzael Avalos { 1262360f0f39SAzael Avalos struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 1263aeaac098SDan Carpenter int mode; 1264aeaac098SDan Carpenter int time; 1265aeaac098SDan Carpenter int ret; 1266360f0f39SAzael Avalos 1267aeaac098SDan Carpenter 1268aeaac098SDan Carpenter ret = kstrtoint(buf, 0, &mode); 1269aeaac098SDan Carpenter if (ret) 1270aeaac098SDan Carpenter return ret; 1271aeaac098SDan Carpenter if (mode != SCI_KBD_MODE_FNZ && mode != SCI_KBD_MODE_AUTO) 1272360f0f39SAzael Avalos return -EINVAL; 1273360f0f39SAzael Avalos 1274360f0f39SAzael Avalos /* Set the Keyboard Backlight Mode where: 1275360f0f39SAzael Avalos * Mode - Auto (2) | FN-Z (1) 1276360f0f39SAzael Avalos * Auto - KBD backlight turns off automatically in given time 1277360f0f39SAzael Avalos * FN-Z - KBD backlight "toggles" when hotkey pressed 1278360f0f39SAzael Avalos */ 1279aeaac098SDan Carpenter if (toshiba->kbd_mode != mode) { 1280360f0f39SAzael Avalos time = toshiba->kbd_time << HCI_MISC_SHIFT; 1281360f0f39SAzael Avalos time = time + toshiba->kbd_mode; 1282aeaac098SDan Carpenter ret = toshiba_kbd_illum_status_set(toshiba, time); 1283aeaac098SDan Carpenter if (ret) 1284aeaac098SDan Carpenter return ret; 1285360f0f39SAzael Avalos toshiba->kbd_mode = mode; 1286360f0f39SAzael Avalos } 1287360f0f39SAzael Avalos 1288360f0f39SAzael Avalos return count; 1289360f0f39SAzael Avalos } 1290360f0f39SAzael Avalos 1291360f0f39SAzael Avalos static ssize_t toshiba_kbd_bl_mode_show(struct device *dev, 1292360f0f39SAzael Avalos struct device_attribute *attr, 1293360f0f39SAzael Avalos char *buf) 1294360f0f39SAzael Avalos { 1295360f0f39SAzael Avalos struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 1296360f0f39SAzael Avalos u32 time; 1297360f0f39SAzael Avalos 1298360f0f39SAzael Avalos if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) 1299360f0f39SAzael Avalos return -EIO; 1300360f0f39SAzael Avalos 1301360f0f39SAzael Avalos return sprintf(buf, "%i\n", time & 0x07); 1302360f0f39SAzael Avalos } 1303360f0f39SAzael Avalos 1304360f0f39SAzael Avalos static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev, 1305360f0f39SAzael Avalos struct device_attribute *attr, 1306360f0f39SAzael Avalos const char *buf, size_t count) 1307360f0f39SAzael Avalos { 1308360f0f39SAzael Avalos struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 1309360f0f39SAzael Avalos int time = -1; 1310360f0f39SAzael Avalos 1311360f0f39SAzael Avalos if (sscanf(buf, "%i", &time) != 1 && (time < 0 || time > 60)) 1312360f0f39SAzael Avalos return -EINVAL; 1313360f0f39SAzael Avalos 1314360f0f39SAzael Avalos /* Set the Keyboard Backlight Timeout: 0-60 seconds */ 1315360f0f39SAzael Avalos if (time != -1 && toshiba->kbd_time != time) { 1316360f0f39SAzael Avalos time = time << HCI_MISC_SHIFT; 1317360f0f39SAzael Avalos time = (toshiba->kbd_mode == SCI_KBD_MODE_AUTO) ? 1318360f0f39SAzael Avalos time + 1 : time + 2; 1319360f0f39SAzael Avalos if (toshiba_kbd_illum_status_set(toshiba, time) < 0) 1320360f0f39SAzael Avalos return -EIO; 1321360f0f39SAzael Avalos toshiba->kbd_time = time >> HCI_MISC_SHIFT; 1322360f0f39SAzael Avalos } 1323360f0f39SAzael Avalos 1324360f0f39SAzael Avalos return count; 1325360f0f39SAzael Avalos } 1326360f0f39SAzael Avalos 1327360f0f39SAzael Avalos static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev, 1328360f0f39SAzael Avalos struct device_attribute *attr, 1329360f0f39SAzael Avalos char *buf) 1330360f0f39SAzael Avalos { 1331360f0f39SAzael Avalos struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 1332360f0f39SAzael Avalos u32 time; 1333360f0f39SAzael Avalos 1334360f0f39SAzael Avalos if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) 1335360f0f39SAzael Avalos return -EIO; 1336360f0f39SAzael Avalos 1337360f0f39SAzael Avalos return sprintf(buf, "%i\n", time >> HCI_MISC_SHIFT); 1338360f0f39SAzael Avalos } 1339360f0f39SAzael Avalos 13409d8658acSAzael Avalos static ssize_t toshiba_touchpad_store(struct device *dev, 13419d8658acSAzael Avalos struct device_attribute *attr, 13429d8658acSAzael Avalos const char *buf, size_t count) 13439d8658acSAzael Avalos { 13449d8658acSAzael Avalos struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 13459d8658acSAzael Avalos int state; 13469d8658acSAzael Avalos 13479d8658acSAzael Avalos /* Set the TouchPad on/off, 0 - Disable | 1 - Enable */ 13489d8658acSAzael Avalos if (sscanf(buf, "%i", &state) == 1 && (state == 0 || state == 1)) { 13499d8658acSAzael Avalos if (toshiba_touchpad_set(toshiba, state) < 0) 13509d8658acSAzael Avalos return -EIO; 13519d8658acSAzael Avalos } 13529d8658acSAzael Avalos 13539d8658acSAzael Avalos return count; 13549d8658acSAzael Avalos } 13559d8658acSAzael Avalos 13569d8658acSAzael Avalos static ssize_t toshiba_touchpad_show(struct device *dev, 13579d8658acSAzael Avalos struct device_attribute *attr, char *buf) 13589d8658acSAzael Avalos { 13599d8658acSAzael Avalos struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 13609d8658acSAzael Avalos u32 state; 13619d8658acSAzael Avalos int ret; 13629d8658acSAzael Avalos 13639d8658acSAzael Avalos ret = toshiba_touchpad_get(toshiba, &state); 13649d8658acSAzael Avalos if (ret < 0) 13659d8658acSAzael Avalos return ret; 13669d8658acSAzael Avalos 13679d8658acSAzael Avalos return sprintf(buf, "%i\n", state); 13689d8658acSAzael Avalos } 13699d8658acSAzael Avalos 13705a2813e9SAzael Avalos static ssize_t toshiba_position_show(struct device *dev, 13715a2813e9SAzael Avalos struct device_attribute *attr, char *buf) 13725a2813e9SAzael Avalos { 13735a2813e9SAzael Avalos struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 13745a2813e9SAzael Avalos u32 xyval, zval, tmp; 13755a2813e9SAzael Avalos u16 x, y, z; 13765a2813e9SAzael Avalos int ret; 13775a2813e9SAzael Avalos 13785a2813e9SAzael Avalos xyval = zval = 0; 13795a2813e9SAzael Avalos ret = toshiba_accelerometer_get(toshiba, &xyval, &zval); 13805a2813e9SAzael Avalos if (ret < 0) 13815a2813e9SAzael Avalos return ret; 13825a2813e9SAzael Avalos 13835a2813e9SAzael Avalos x = xyval & HCI_ACCEL_MASK; 13845a2813e9SAzael Avalos tmp = xyval >> HCI_MISC_SHIFT; 13855a2813e9SAzael Avalos y = tmp & HCI_ACCEL_MASK; 13865a2813e9SAzael Avalos z = zval & HCI_ACCEL_MASK; 13875a2813e9SAzael Avalos 13885a2813e9SAzael Avalos return sprintf(buf, "%d %d %d\n", x, y, z); 13895a2813e9SAzael Avalos } 13905a2813e9SAzael Avalos 1391360f0f39SAzael Avalos static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR, 1392360f0f39SAzael Avalos toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store); 1393360f0f39SAzael Avalos static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR, 1394360f0f39SAzael Avalos toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store); 13959d8658acSAzael Avalos static DEVICE_ATTR(touchpad, S_IRUGO | S_IWUSR, 13969d8658acSAzael Avalos toshiba_touchpad_show, toshiba_touchpad_store); 13975a2813e9SAzael Avalos static DEVICE_ATTR(position, S_IRUGO, toshiba_position_show, NULL); 1398360f0f39SAzael Avalos 1399360f0f39SAzael Avalos static struct attribute *toshiba_attributes[] = { 1400360f0f39SAzael Avalos &dev_attr_kbd_backlight_mode.attr, 1401360f0f39SAzael Avalos &dev_attr_kbd_backlight_timeout.attr, 14029d8658acSAzael Avalos &dev_attr_touchpad.attr, 14035a2813e9SAzael Avalos &dev_attr_position.attr, 1404360f0f39SAzael Avalos NULL, 1405360f0f39SAzael Avalos }; 1406360f0f39SAzael Avalos 1407360f0f39SAzael Avalos static umode_t toshiba_sysfs_is_visible(struct kobject *kobj, 1408360f0f39SAzael Avalos struct attribute *attr, int idx) 1409360f0f39SAzael Avalos { 1410360f0f39SAzael Avalos struct device *dev = container_of(kobj, struct device, kobj); 1411360f0f39SAzael Avalos struct toshiba_acpi_dev *drv = dev_get_drvdata(dev); 1412360f0f39SAzael Avalos bool exists = true; 1413360f0f39SAzael Avalos 1414360f0f39SAzael Avalos if (attr == &dev_attr_kbd_backlight_mode.attr) 1415360f0f39SAzael Avalos exists = (drv->kbd_illum_supported) ? true : false; 1416360f0f39SAzael Avalos else if (attr == &dev_attr_kbd_backlight_timeout.attr) 1417360f0f39SAzael Avalos exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false; 14189d8658acSAzael Avalos else if (attr == &dev_attr_touchpad.attr) 14199d8658acSAzael Avalos exists = (drv->touchpad_supported) ? true : false; 14205a2813e9SAzael Avalos else if (attr == &dev_attr_position.attr) 14215a2813e9SAzael Avalos exists = (drv->accelerometer_supported) ? true : false; 1422360f0f39SAzael Avalos 1423360f0f39SAzael Avalos return exists ? attr->mode : 0; 1424360f0f39SAzael Avalos } 1425360f0f39SAzael Avalos 1426360f0f39SAzael Avalos static struct attribute_group toshiba_attr_group = { 1427360f0f39SAzael Avalos .is_visible = toshiba_sysfs_is_visible, 1428360f0f39SAzael Avalos .attrs = toshiba_attributes, 1429360f0f39SAzael Avalos }; 1430360f0f39SAzael Avalos 143129cd293fSSeth Forshee static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, 143229cd293fSSeth Forshee struct serio *port) 143329cd293fSSeth Forshee { 143429cd293fSSeth Forshee if (str & 0x20) 143529cd293fSSeth Forshee return false; 143629cd293fSSeth Forshee 143729cd293fSSeth Forshee if (unlikely(data == 0xe0)) 143829cd293fSSeth Forshee return false; 143929cd293fSSeth Forshee 144029cd293fSSeth Forshee if ((data & 0x7f) == TOS1900_FN_SCAN) { 144129cd293fSSeth Forshee schedule_work(&toshiba_acpi->hotkey_work); 144229cd293fSSeth Forshee return true; 144329cd293fSSeth Forshee } 144429cd293fSSeth Forshee 144529cd293fSSeth Forshee return false; 144629cd293fSSeth Forshee } 144729cd293fSSeth Forshee 144829cd293fSSeth Forshee static void toshiba_acpi_hotkey_work(struct work_struct *work) 144929cd293fSSeth Forshee { 145029cd293fSSeth Forshee acpi_handle ec_handle = ec_get_handle(); 145129cd293fSSeth Forshee acpi_status status; 145229cd293fSSeth Forshee 145329cd293fSSeth Forshee if (!ec_handle) 145429cd293fSSeth Forshee return; 145529cd293fSSeth Forshee 145629cd293fSSeth Forshee status = acpi_evaluate_object(ec_handle, "NTFY", NULL, NULL); 145729cd293fSSeth Forshee if (ACPI_FAILURE(status)) 145829cd293fSSeth Forshee pr_err("ACPI NTFY method execution failed\n"); 145929cd293fSSeth Forshee } 146029cd293fSSeth Forshee 146129cd293fSSeth Forshee /* 146229cd293fSSeth Forshee * Returns hotkey scancode, or < 0 on failure. 146329cd293fSSeth Forshee */ 146429cd293fSSeth Forshee static int toshiba_acpi_query_hotkey(struct toshiba_acpi_dev *dev) 146529cd293fSSeth Forshee { 146674facaf7SZhang Rui unsigned long long value; 146729cd293fSSeth Forshee acpi_status status; 146829cd293fSSeth Forshee 146974facaf7SZhang Rui status = acpi_evaluate_integer(dev->acpi_dev->handle, "INFO", 147074facaf7SZhang Rui NULL, &value); 147174facaf7SZhang Rui if (ACPI_FAILURE(status)) { 147229cd293fSSeth Forshee pr_err("ACPI INFO method execution failed\n"); 147329cd293fSSeth Forshee return -EIO; 147429cd293fSSeth Forshee } 147529cd293fSSeth Forshee 147674facaf7SZhang Rui return value; 147729cd293fSSeth Forshee } 147829cd293fSSeth Forshee 147929cd293fSSeth Forshee static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev, 148029cd293fSSeth Forshee int scancode) 148129cd293fSSeth Forshee { 148229cd293fSSeth Forshee if (scancode == 0x100) 148329cd293fSSeth Forshee return; 148429cd293fSSeth Forshee 148529cd293fSSeth Forshee /* act on key press; ignore key release */ 148629cd293fSSeth Forshee if (scancode & 0x80) 148729cd293fSSeth Forshee return; 148829cd293fSSeth Forshee 148929cd293fSSeth Forshee if (!sparse_keymap_report_event(dev->hotkey_dev, scancode, 1, true)) 149029cd293fSSeth Forshee pr_info("Unknown key %x\n", scancode); 149129cd293fSSeth Forshee } 149229cd293fSSeth Forshee 1493b859f159SGreg Kroah-Hartman static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) 14946335e4d5SMatthew Garrett { 1495135740deSSeth Forshee acpi_status status; 1496e2e19606SZhang Rui acpi_handle ec_handle; 1497135740deSSeth Forshee int error; 149829cd293fSSeth Forshee u32 hci_result; 1499fe808bfbSTakashi Iwai const struct key_entry *keymap = toshiba_acpi_keymap; 1500135740deSSeth Forshee 1501135740deSSeth Forshee dev->hotkey_dev = input_allocate_device(); 1502b222cca6SJoe Perches if (!dev->hotkey_dev) 1503135740deSSeth Forshee return -ENOMEM; 1504135740deSSeth Forshee 1505135740deSSeth Forshee dev->hotkey_dev->name = "Toshiba input device"; 15066e02cc7eSSeth Forshee dev->hotkey_dev->phys = "toshiba_acpi/input0"; 1507135740deSSeth Forshee dev->hotkey_dev->id.bustype = BUS_HOST; 1508135740deSSeth Forshee 1509fe808bfbSTakashi Iwai if (dmi_check_system(toshiba_alt_keymap_dmi)) 1510fe808bfbSTakashi Iwai keymap = toshiba_acpi_alt_keymap; 1511fe808bfbSTakashi Iwai error = sparse_keymap_setup(dev->hotkey_dev, keymap, NULL); 1512135740deSSeth Forshee if (error) 1513135740deSSeth Forshee goto err_free_dev; 1514135740deSSeth Forshee 151529cd293fSSeth Forshee /* 151629cd293fSSeth Forshee * For some machines the SCI responsible for providing hotkey 151729cd293fSSeth Forshee * notification doesn't fire. We can trigger the notification 151829cd293fSSeth Forshee * whenever the Fn key is pressed using the NTFY method, if 151929cd293fSSeth Forshee * supported, so if it's present set up an i8042 key filter 152029cd293fSSeth Forshee * for this purpose. 152129cd293fSSeth Forshee */ 152229cd293fSSeth Forshee status = AE_ERROR; 152329cd293fSSeth Forshee ec_handle = ec_get_handle(); 1524e2e19606SZhang Rui if (ec_handle && acpi_has_method(ec_handle, "NTFY")) { 152529cd293fSSeth Forshee INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work); 152629cd293fSSeth Forshee 152729cd293fSSeth Forshee error = i8042_install_filter(toshiba_acpi_i8042_filter); 152829cd293fSSeth Forshee if (error) { 152929cd293fSSeth Forshee pr_err("Error installing key filter\n"); 153029cd293fSSeth Forshee goto err_free_keymap; 153129cd293fSSeth Forshee } 153229cd293fSSeth Forshee 153329cd293fSSeth Forshee dev->ntfy_supported = 1; 153429cd293fSSeth Forshee } 153529cd293fSSeth Forshee 153629cd293fSSeth Forshee /* 153729cd293fSSeth Forshee * Determine hotkey query interface. Prefer using the INFO 153829cd293fSSeth Forshee * method when it is available. 153929cd293fSSeth Forshee */ 1540e2e19606SZhang Rui if (acpi_has_method(dev->acpi_dev->handle, "INFO")) 154129cd293fSSeth Forshee dev->info_supported = 1; 1542e2e19606SZhang Rui else { 154329cd293fSSeth Forshee hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); 154429cd293fSSeth Forshee if (hci_result == HCI_SUCCESS) 154529cd293fSSeth Forshee dev->system_event_supported = 1; 154629cd293fSSeth Forshee } 154729cd293fSSeth Forshee 154829cd293fSSeth Forshee if (!dev->info_supported && !dev->system_event_supported) { 154929cd293fSSeth Forshee pr_warn("No hotkey query interface found\n"); 155029cd293fSSeth Forshee goto err_remove_filter; 155129cd293fSSeth Forshee } 155229cd293fSSeth Forshee 15536e02cc7eSSeth Forshee status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL); 1554135740deSSeth Forshee if (ACPI_FAILURE(status)) { 1555135740deSSeth Forshee pr_info("Unable to enable hotkeys\n"); 1556135740deSSeth Forshee error = -ENODEV; 155729cd293fSSeth Forshee goto err_remove_filter; 1558135740deSSeth Forshee } 1559135740deSSeth Forshee 1560135740deSSeth Forshee error = input_register_device(dev->hotkey_dev); 1561135740deSSeth Forshee if (error) { 1562135740deSSeth Forshee pr_info("Unable to register input device\n"); 156329cd293fSSeth Forshee goto err_remove_filter; 1564135740deSSeth Forshee } 1565135740deSSeth Forshee 156629cd293fSSeth Forshee hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &hci_result); 1567135740deSSeth Forshee return 0; 1568135740deSSeth Forshee 156929cd293fSSeth Forshee err_remove_filter: 157029cd293fSSeth Forshee if (dev->ntfy_supported) 157129cd293fSSeth Forshee i8042_remove_filter(toshiba_acpi_i8042_filter); 1572135740deSSeth Forshee err_free_keymap: 1573135740deSSeth Forshee sparse_keymap_free(dev->hotkey_dev); 1574135740deSSeth Forshee err_free_dev: 1575135740deSSeth Forshee input_free_device(dev->hotkey_dev); 1576135740deSSeth Forshee dev->hotkey_dev = NULL; 1577135740deSSeth Forshee return error; 1578135740deSSeth Forshee } 1579135740deSSeth Forshee 1580b859f159SGreg Kroah-Hartman static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev) 158162cce752SSeth Forshee { 158262cce752SSeth Forshee struct backlight_properties props; 158362cce752SSeth Forshee int brightness; 158462cce752SSeth Forshee int ret; 1585121b7b0dSAkio Idehara bool enabled; 158662cce752SSeth Forshee 158762cce752SSeth Forshee /* 158862cce752SSeth Forshee * Some machines don't support the backlight methods at all, and 158962cce752SSeth Forshee * others support it read-only. Either of these is pretty useless, 159062cce752SSeth Forshee * so only register the backlight device if the backlight method 159162cce752SSeth Forshee * supports both reads and writes. 159262cce752SSeth Forshee */ 159362cce752SSeth Forshee brightness = __get_lcd_brightness(dev); 159462cce752SSeth Forshee if (brightness < 0) 159562cce752SSeth Forshee return 0; 159662cce752SSeth Forshee ret = set_lcd_brightness(dev, brightness); 159762cce752SSeth Forshee if (ret) { 159862cce752SSeth Forshee pr_debug("Backlight method is read-only, disabling backlight support\n"); 159962cce752SSeth Forshee return 0; 160062cce752SSeth Forshee } 160162cce752SSeth Forshee 1602121b7b0dSAkio Idehara /* Determine whether or not BIOS supports transflective backlight */ 1603121b7b0dSAkio Idehara ret = get_tr_backlight_status(dev, &enabled); 1604121b7b0dSAkio Idehara dev->tr_backlight_supported = !ret; 1605121b7b0dSAkio Idehara 160653039f22SMatthew Garrett memset(&props, 0, sizeof(props)); 160762cce752SSeth Forshee props.type = BACKLIGHT_PLATFORM; 160862cce752SSeth Forshee props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; 160962cce752SSeth Forshee 1610121b7b0dSAkio Idehara /* adding an extra level and having 0 change to transflective mode */ 1611121b7b0dSAkio Idehara if (dev->tr_backlight_supported) 1612121b7b0dSAkio Idehara props.max_brightness++; 1613121b7b0dSAkio Idehara 161462cce752SSeth Forshee dev->backlight_dev = backlight_device_register("toshiba", 161562cce752SSeth Forshee &dev->acpi_dev->dev, 161662cce752SSeth Forshee dev, 161762cce752SSeth Forshee &toshiba_backlight_data, 161862cce752SSeth Forshee &props); 161962cce752SSeth Forshee if (IS_ERR(dev->backlight_dev)) { 162062cce752SSeth Forshee ret = PTR_ERR(dev->backlight_dev); 162162cce752SSeth Forshee pr_err("Could not register toshiba backlight device\n"); 162262cce752SSeth Forshee dev->backlight_dev = NULL; 162362cce752SSeth Forshee return ret; 162462cce752SSeth Forshee } 162562cce752SSeth Forshee 162662cce752SSeth Forshee dev->backlight_dev->props.brightness = brightness; 162762cce752SSeth Forshee return 0; 162862cce752SSeth Forshee } 162962cce752SSeth Forshee 163051fac838SRafael J. Wysocki static int toshiba_acpi_remove(struct acpi_device *acpi_dev) 1631135740deSSeth Forshee { 1632135740deSSeth Forshee struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); 1633135740deSSeth Forshee 163436d03f93SSeth Forshee remove_toshiba_proc_entries(dev); 1635135740deSSeth Forshee 1636360f0f39SAzael Avalos if (dev->sysfs_created) 1637360f0f39SAzael Avalos sysfs_remove_group(&dev->acpi_dev->dev.kobj, 1638360f0f39SAzael Avalos &toshiba_attr_group); 1639360f0f39SAzael Avalos 164029cd293fSSeth Forshee if (dev->ntfy_supported) { 164129cd293fSSeth Forshee i8042_remove_filter(toshiba_acpi_i8042_filter); 164229cd293fSSeth Forshee cancel_work_sync(&dev->hotkey_work); 164329cd293fSSeth Forshee } 164429cd293fSSeth Forshee 1645135740deSSeth Forshee if (dev->hotkey_dev) { 1646135740deSSeth Forshee input_unregister_device(dev->hotkey_dev); 1647135740deSSeth Forshee sparse_keymap_free(dev->hotkey_dev); 1648135740deSSeth Forshee } 1649135740deSSeth Forshee 1650135740deSSeth Forshee if (dev->bt_rfk) { 1651135740deSSeth Forshee rfkill_unregister(dev->bt_rfk); 1652135740deSSeth Forshee rfkill_destroy(dev->bt_rfk); 1653135740deSSeth Forshee } 1654135740deSSeth Forshee 1655135740deSSeth Forshee if (dev->backlight_dev) 1656135740deSSeth Forshee backlight_device_unregister(dev->backlight_dev); 1657135740deSSeth Forshee 165836d03f93SSeth Forshee if (dev->illumination_supported) 1659135740deSSeth Forshee led_classdev_unregister(&dev->led_dev); 1660135740deSSeth Forshee 1661360f0f39SAzael Avalos if (dev->kbd_led_registered) 1662360f0f39SAzael Avalos led_classdev_unregister(&dev->kbd_led); 1663360f0f39SAzael Avalos 1664def6c4e2SAzael Avalos if (dev->eco_supported) 1665def6c4e2SAzael Avalos led_classdev_unregister(&dev->eco_led); 1666def6c4e2SAzael Avalos 166729cd293fSSeth Forshee if (toshiba_acpi) 166829cd293fSSeth Forshee toshiba_acpi = NULL; 166929cd293fSSeth Forshee 1670135740deSSeth Forshee kfree(dev); 1671135740deSSeth Forshee 1672135740deSSeth Forshee return 0; 1673135740deSSeth Forshee } 1674135740deSSeth Forshee 1675b859f159SGreg Kroah-Hartman static const char *find_hci_method(acpi_handle handle) 1676a540d6b5SSeth Forshee { 1677e2e19606SZhang Rui if (acpi_has_method(handle, "GHCI")) 1678a540d6b5SSeth Forshee return "GHCI"; 1679a540d6b5SSeth Forshee 1680e2e19606SZhang Rui if (acpi_has_method(handle, "SPFC")) 1681a540d6b5SSeth Forshee return "SPFC"; 1682a540d6b5SSeth Forshee 1683a540d6b5SSeth Forshee return NULL; 1684a540d6b5SSeth Forshee } 1685a540d6b5SSeth Forshee 1686b859f159SGreg Kroah-Hartman static int toshiba_acpi_add(struct acpi_device *acpi_dev) 1687135740deSSeth Forshee { 1688135740deSSeth Forshee struct toshiba_acpi_dev *dev; 1689a540d6b5SSeth Forshee const char *hci_method; 169036d03f93SSeth Forshee u32 dummy; 1691135740deSSeth Forshee bool bt_present; 1692135740deSSeth Forshee int ret = 0; 1693135740deSSeth Forshee 169429cd293fSSeth Forshee if (toshiba_acpi) 169529cd293fSSeth Forshee return -EBUSY; 169629cd293fSSeth Forshee 1697135740deSSeth Forshee pr_info("Toshiba Laptop ACPI Extras version %s\n", 1698135740deSSeth Forshee TOSHIBA_ACPI_VERSION); 1699135740deSSeth Forshee 1700a540d6b5SSeth Forshee hci_method = find_hci_method(acpi_dev->handle); 1701a540d6b5SSeth Forshee if (!hci_method) { 1702a540d6b5SSeth Forshee pr_err("HCI interface not found\n"); 17036e02cc7eSSeth Forshee return -ENODEV; 1704a540d6b5SSeth Forshee } 17056e02cc7eSSeth Forshee 1706135740deSSeth Forshee dev = kzalloc(sizeof(*dev), GFP_KERNEL); 1707135740deSSeth Forshee if (!dev) 1708135740deSSeth Forshee return -ENOMEM; 1709135740deSSeth Forshee dev->acpi_dev = acpi_dev; 1710a540d6b5SSeth Forshee dev->method_hci = hci_method; 1711135740deSSeth Forshee acpi_dev->driver_data = dev; 1712360f0f39SAzael Avalos dev_set_drvdata(&acpi_dev->dev, dev); 1713135740deSSeth Forshee 17146e02cc7eSSeth Forshee if (toshiba_acpi_setup_keyboard(dev)) 1715135740deSSeth Forshee pr_info("Unable to activate hotkeys\n"); 1716135740deSSeth Forshee 1717135740deSSeth Forshee mutex_init(&dev->mutex); 1718135740deSSeth Forshee 171962cce752SSeth Forshee ret = toshiba_acpi_setup_backlight(dev); 172062cce752SSeth Forshee if (ret) 1721135740deSSeth Forshee goto error; 1722135740deSSeth Forshee 1723135740deSSeth Forshee /* Register rfkill switch for Bluetooth */ 1724135740deSSeth Forshee if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) { 1725135740deSSeth Forshee dev->bt_rfk = rfkill_alloc("Toshiba Bluetooth", 1726135740deSSeth Forshee &acpi_dev->dev, 1727135740deSSeth Forshee RFKILL_TYPE_BLUETOOTH, 1728135740deSSeth Forshee &toshiba_rfk_ops, 1729135740deSSeth Forshee dev); 1730135740deSSeth Forshee if (!dev->bt_rfk) { 1731135740deSSeth Forshee pr_err("unable to allocate rfkill device\n"); 1732135740deSSeth Forshee ret = -ENOMEM; 1733135740deSSeth Forshee goto error; 1734135740deSSeth Forshee } 1735135740deSSeth Forshee 1736135740deSSeth Forshee ret = rfkill_register(dev->bt_rfk); 1737135740deSSeth Forshee if (ret) { 1738135740deSSeth Forshee pr_err("unable to register rfkill device\n"); 1739135740deSSeth Forshee rfkill_destroy(dev->bt_rfk); 1740135740deSSeth Forshee goto error; 1741135740deSSeth Forshee } 1742135740deSSeth Forshee } 1743135740deSSeth Forshee 1744135740deSSeth Forshee if (toshiba_illumination_available(dev)) { 1745135740deSSeth Forshee dev->led_dev.name = "toshiba::illumination"; 1746135740deSSeth Forshee dev->led_dev.max_brightness = 1; 1747135740deSSeth Forshee dev->led_dev.brightness_set = toshiba_illumination_set; 1748135740deSSeth Forshee dev->led_dev.brightness_get = toshiba_illumination_get; 1749135740deSSeth Forshee if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev)) 175036d03f93SSeth Forshee dev->illumination_supported = 1; 1751135740deSSeth Forshee } 1752135740deSSeth Forshee 1753def6c4e2SAzael Avalos if (toshiba_eco_mode_available(dev)) { 1754def6c4e2SAzael Avalos dev->eco_led.name = "toshiba::eco_mode"; 1755def6c4e2SAzael Avalos dev->eco_led.max_brightness = 1; 1756def6c4e2SAzael Avalos dev->eco_led.brightness_set = toshiba_eco_mode_set_status; 1757def6c4e2SAzael Avalos dev->eco_led.brightness_get = toshiba_eco_mode_get_status; 1758def6c4e2SAzael Avalos if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led)) 1759def6c4e2SAzael Avalos dev->eco_supported = 1; 1760def6c4e2SAzael Avalos } 1761def6c4e2SAzael Avalos 1762360f0f39SAzael Avalos ret = toshiba_kbd_illum_status_get(dev, &dummy); 1763360f0f39SAzael Avalos if (!ret) { 1764360f0f39SAzael Avalos dev->kbd_time = dummy >> HCI_MISC_SHIFT; 1765360f0f39SAzael Avalos dev->kbd_mode = dummy & 0x07; 1766360f0f39SAzael Avalos } 1767360f0f39SAzael Avalos dev->kbd_illum_supported = !ret; 1768360f0f39SAzael Avalos /* 1769360f0f39SAzael Avalos * Only register the LED if KBD illumination is supported 1770360f0f39SAzael Avalos * and the keyboard backlight operation mode is set to FN-Z 1771360f0f39SAzael Avalos */ 1772360f0f39SAzael Avalos if (dev->kbd_illum_supported && dev->kbd_mode == SCI_KBD_MODE_FNZ) { 1773360f0f39SAzael Avalos dev->kbd_led.name = "toshiba::kbd_backlight"; 1774360f0f39SAzael Avalos dev->kbd_led.max_brightness = 1; 1775360f0f39SAzael Avalos dev->kbd_led.brightness_set = toshiba_kbd_backlight_set; 1776360f0f39SAzael Avalos dev->kbd_led.brightness_get = toshiba_kbd_backlight_get; 1777360f0f39SAzael Avalos if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led)) 1778360f0f39SAzael Avalos dev->kbd_led_registered = 1; 1779360f0f39SAzael Avalos } 1780360f0f39SAzael Avalos 17819d8658acSAzael Avalos ret = toshiba_touchpad_get(dev, &dummy); 17829d8658acSAzael Avalos dev->touchpad_supported = !ret; 17839d8658acSAzael Avalos 17845a2813e9SAzael Avalos ret = toshiba_accelerometer_supported(dev); 17855a2813e9SAzael Avalos dev->accelerometer_supported = !ret; 17865a2813e9SAzael Avalos 178736d03f93SSeth Forshee /* Determine whether or not BIOS supports fan and video interfaces */ 178836d03f93SSeth Forshee 178936d03f93SSeth Forshee ret = get_video_status(dev, &dummy); 179036d03f93SSeth Forshee dev->video_supported = !ret; 179136d03f93SSeth Forshee 179236d03f93SSeth Forshee ret = get_fan_status(dev, &dummy); 179336d03f93SSeth Forshee dev->fan_supported = !ret; 179436d03f93SSeth Forshee 1795360f0f39SAzael Avalos ret = sysfs_create_group(&dev->acpi_dev->dev.kobj, 1796360f0f39SAzael Avalos &toshiba_attr_group); 1797360f0f39SAzael Avalos if (ret) { 1798360f0f39SAzael Avalos dev->sysfs_created = 0; 1799360f0f39SAzael Avalos goto error; 1800360f0f39SAzael Avalos } 1801360f0f39SAzael Avalos dev->sysfs_created = !ret; 1802360f0f39SAzael Avalos 180336d03f93SSeth Forshee create_toshiba_proc_entries(dev); 180436d03f93SSeth Forshee 180529cd293fSSeth Forshee toshiba_acpi = dev; 180629cd293fSSeth Forshee 1807135740deSSeth Forshee return 0; 1808135740deSSeth Forshee 1809135740deSSeth Forshee error: 181051fac838SRafael J. Wysocki toshiba_acpi_remove(acpi_dev); 1811135740deSSeth Forshee return ret; 1812135740deSSeth Forshee } 1813135740deSSeth Forshee 1814135740deSSeth Forshee static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) 1815135740deSSeth Forshee { 1816135740deSSeth Forshee struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); 18176335e4d5SMatthew Garrett u32 hci_result, value; 181811948b93SSeth Forshee int retries = 3; 181929cd293fSSeth Forshee int scancode; 18206335e4d5SMatthew Garrett 182129cd293fSSeth Forshee if (event != 0x80) 18226335e4d5SMatthew Garrett return; 182311948b93SSeth Forshee 182429cd293fSSeth Forshee if (dev->info_supported) { 182529cd293fSSeth Forshee scancode = toshiba_acpi_query_hotkey(dev); 182629cd293fSSeth Forshee if (scancode < 0) 182729cd293fSSeth Forshee pr_err("Failed to query hotkey event\n"); 182829cd293fSSeth Forshee else if (scancode != 0) 182929cd293fSSeth Forshee toshiba_acpi_report_hotkey(dev, scancode); 183029cd293fSSeth Forshee } else if (dev->system_event_supported) { 18316335e4d5SMatthew Garrett do { 1832135740deSSeth Forshee hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); 183311948b93SSeth Forshee switch (hci_result) { 183411948b93SSeth Forshee case HCI_SUCCESS: 183529cd293fSSeth Forshee toshiba_acpi_report_hotkey(dev, (int)value); 183611948b93SSeth Forshee break; 183711948b93SSeth Forshee case HCI_NOT_SUPPORTED: 183829cd293fSSeth Forshee /* 183929cd293fSSeth Forshee * This is a workaround for an unresolved 184029cd293fSSeth Forshee * issue on some machines where system events 184129cd293fSSeth Forshee * sporadically become disabled. 184229cd293fSSeth Forshee */ 184329cd293fSSeth Forshee hci_write1(dev, HCI_SYSTEM_EVENT, 1, 184429cd293fSSeth Forshee &hci_result); 18457e33460dSJoe Perches pr_notice("Re-enabled hotkeys\n"); 184611948b93SSeth Forshee /* fall through */ 184711948b93SSeth Forshee default: 184811948b93SSeth Forshee retries--; 184911948b93SSeth Forshee break; 18506335e4d5SMatthew Garrett } 185111948b93SSeth Forshee } while (retries && hci_result != HCI_EMPTY); 18526335e4d5SMatthew Garrett } 185329cd293fSSeth Forshee } 18546335e4d5SMatthew Garrett 18553567a4e2SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 185643d2fd3bSRafael J. Wysocki static int toshiba_acpi_suspend(struct device *device) 185729cd293fSSeth Forshee { 185843d2fd3bSRafael J. Wysocki struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); 185929cd293fSSeth Forshee u32 result; 186029cd293fSSeth Forshee 186129cd293fSSeth Forshee if (dev->hotkey_dev) 186229cd293fSSeth Forshee hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE, &result); 186329cd293fSSeth Forshee 186429cd293fSSeth Forshee return 0; 186529cd293fSSeth Forshee } 186629cd293fSSeth Forshee 186743d2fd3bSRafael J. Wysocki static int toshiba_acpi_resume(struct device *device) 186829cd293fSSeth Forshee { 186943d2fd3bSRafael J. Wysocki struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); 187029cd293fSSeth Forshee u32 result; 1871e7fdb762SBenjamin Tissoires acpi_status status; 187229cd293fSSeth Forshee 1873e7fdb762SBenjamin Tissoires if (dev->hotkey_dev) { 1874e7fdb762SBenjamin Tissoires status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", 1875e7fdb762SBenjamin Tissoires NULL, NULL); 1876e7fdb762SBenjamin Tissoires if (ACPI_FAILURE(status)) 1877e7fdb762SBenjamin Tissoires pr_info("Unable to re-enable hotkeys\n"); 1878e7fdb762SBenjamin Tissoires 187929cd293fSSeth Forshee hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &result); 1880e7fdb762SBenjamin Tissoires } 188129cd293fSSeth Forshee 188229cd293fSSeth Forshee return 0; 188329cd293fSSeth Forshee } 18843567a4e2SRafael J. Wysocki #endif 18856335e4d5SMatthew Garrett 188643d2fd3bSRafael J. Wysocki static SIMPLE_DEV_PM_OPS(toshiba_acpi_pm, 188743d2fd3bSRafael J. Wysocki toshiba_acpi_suspend, toshiba_acpi_resume); 188843d2fd3bSRafael J. Wysocki 1889135740deSSeth Forshee static struct acpi_driver toshiba_acpi_driver = { 1890135740deSSeth Forshee .name = "Toshiba ACPI driver", 1891135740deSSeth Forshee .owner = THIS_MODULE, 1892135740deSSeth Forshee .ids = toshiba_device_ids, 1893135740deSSeth Forshee .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, 1894135740deSSeth Forshee .ops = { 1895135740deSSeth Forshee .add = toshiba_acpi_add, 1896135740deSSeth Forshee .remove = toshiba_acpi_remove, 1897135740deSSeth Forshee .notify = toshiba_acpi_notify, 1898135740deSSeth Forshee }, 189943d2fd3bSRafael J. Wysocki .drv.pm = &toshiba_acpi_pm, 1900135740deSSeth Forshee }; 1901b4f9fe12SLen Brown 1902b4f9fe12SLen Brown static int __init toshiba_acpi_init(void) 1903b4f9fe12SLen Brown { 1904135740deSSeth Forshee int ret; 1905b4f9fe12SLen Brown 1906f11f999eSSeth Forshee /* 1907f11f999eSSeth Forshee * Machines with this WMI guid aren't supported due to bugs in 1908f11f999eSSeth Forshee * their AML. This check relies on wmi initializing before 1909f11f999eSSeth Forshee * toshiba_acpi to guarantee guids have been identified. 1910f11f999eSSeth Forshee */ 1911f11f999eSSeth Forshee if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) 1912f11f999eSSeth Forshee return -ENODEV; 1913f11f999eSSeth Forshee 1914b4f9fe12SLen Brown toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); 1915b4f9fe12SLen Brown if (!toshiba_proc_dir) { 1916135740deSSeth Forshee pr_err("Unable to create proc dir " PROC_TOSHIBA "\n"); 1917b4f9fe12SLen Brown return -ENODEV; 1918b4f9fe12SLen Brown } 1919b4f9fe12SLen Brown 1920135740deSSeth Forshee ret = acpi_bus_register_driver(&toshiba_acpi_driver); 1921b4f9fe12SLen Brown if (ret) { 1922135740deSSeth Forshee pr_err("Failed to register ACPI driver: %d\n", ret); 1923135740deSSeth Forshee remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); 1924135740deSSeth Forshee } 1925135740deSSeth Forshee 1926b4f9fe12SLen Brown return ret; 1927b4f9fe12SLen Brown } 1928b4f9fe12SLen Brown 1929135740deSSeth Forshee static void __exit toshiba_acpi_exit(void) 1930135740deSSeth Forshee { 1931135740deSSeth Forshee acpi_bus_unregister_driver(&toshiba_acpi_driver); 1932135740deSSeth Forshee if (toshiba_proc_dir) 1933135740deSSeth Forshee remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); 1934b4f9fe12SLen Brown } 1935b4f9fe12SLen Brown 1936b4f9fe12SLen Brown module_init(toshiba_acpi_init); 1937b4f9fe12SLen Brown module_exit(toshiba_acpi_exit); 1938