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