162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * toshiba_acpi.c - Toshiba Laptop ACPI Extras 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2002-2004 John Belmonte 662306a36Sopenharmony_ci * Copyright (C) 2008 Philip Langdale 762306a36Sopenharmony_ci * Copyright (C) 2010 Pierre Ducroquet 862306a36Sopenharmony_ci * Copyright (C) 2014-2016 Azael Avalos 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * The devolpment page for this driver is located at 1162306a36Sopenharmony_ci * http://memebeam.org/toys/ToshibaAcpiDriver. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Credits: 1462306a36Sopenharmony_ci * Jonathan A. Buzzard - Toshiba HCI info, and critical tips on reverse 1562306a36Sopenharmony_ci * engineering the Windows drivers 1662306a36Sopenharmony_ci * Yasushi Nagato - changes for linux kernel 2.4 -> 2.5 1762306a36Sopenharmony_ci * Rob Miller - TV out and hotkeys help 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define TOSHIBA_ACPI_VERSION "0.24" 2362306a36Sopenharmony_ci#define PROC_INTERFACE_VERSION 1 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/compiler.h> 2662306a36Sopenharmony_ci#include <linux/dmi.h> 2762306a36Sopenharmony_ci#include <linux/kernel.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/moduleparam.h> 3062306a36Sopenharmony_ci#include <linux/init.h> 3162306a36Sopenharmony_ci#include <linux/types.h> 3262306a36Sopenharmony_ci#include <linux/proc_fs.h> 3362306a36Sopenharmony_ci#include <linux/seq_file.h> 3462306a36Sopenharmony_ci#include <linux/backlight.h> 3562306a36Sopenharmony_ci#include <linux/input.h> 3662306a36Sopenharmony_ci#include <linux/input/sparse-keymap.h> 3762306a36Sopenharmony_ci#include <linux/leds.h> 3862306a36Sopenharmony_ci#include <linux/slab.h> 3962306a36Sopenharmony_ci#include <linux/workqueue.h> 4062306a36Sopenharmony_ci#include <linux/i8042.h> 4162306a36Sopenharmony_ci#include <linux/acpi.h> 4262306a36Sopenharmony_ci#include <linux/uaccess.h> 4362306a36Sopenharmony_ci#include <linux/miscdevice.h> 4462306a36Sopenharmony_ci#include <linux/rfkill.h> 4562306a36Sopenharmony_ci#include <linux/hwmon.h> 4662306a36Sopenharmony_ci#include <linux/iio/iio.h> 4762306a36Sopenharmony_ci#include <linux/toshiba.h> 4862306a36Sopenharmony_ci#include <acpi/battery.h> 4962306a36Sopenharmony_ci#include <acpi/video.h> 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ciMODULE_AUTHOR("John Belmonte"); 5262306a36Sopenharmony_ciMODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); 5362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int turn_on_panel_on_resume = -1; 5662306a36Sopenharmony_cimodule_param(turn_on_panel_on_resume, int, 0644); 5762306a36Sopenharmony_ciMODULE_PARM_DESC(turn_on_panel_on_resume, 5862306a36Sopenharmony_ci "Call HCI_PANEL_POWER_ON on resume (-1 = auto, 0 = no, 1 = yes"); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100" 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* Scan code for Fn key on TOS1900 models */ 6362306a36Sopenharmony_ci#define TOS1900_FN_SCAN 0x6e 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* Toshiba ACPI method paths */ 6662306a36Sopenharmony_ci#define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* 6962306a36Sopenharmony_ci * The Toshiba configuration interface is composed of the HCI and the SCI, 7062306a36Sopenharmony_ci * which are defined as follows: 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * HCI is Toshiba's "Hardware Control Interface" which is supposed to 7362306a36Sopenharmony_ci * be uniform across all their models. Ideally we would just call 7462306a36Sopenharmony_ci * dedicated ACPI methods instead of using this primitive interface. 7562306a36Sopenharmony_ci * However the ACPI methods seem to be incomplete in some areas (for 7662306a36Sopenharmony_ci * example they allow setting, but not reading, the LCD brightness value), 7762306a36Sopenharmony_ci * so this is still useful. 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * SCI stands for "System Configuration Interface" which aim is to 8062306a36Sopenharmony_ci * conceal differences in hardware between different models. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define TCI_WORDS 6 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* Operations */ 8662306a36Sopenharmony_ci#define HCI_SET 0xff00 8762306a36Sopenharmony_ci#define HCI_GET 0xfe00 8862306a36Sopenharmony_ci#define SCI_OPEN 0xf100 8962306a36Sopenharmony_ci#define SCI_CLOSE 0xf200 9062306a36Sopenharmony_ci#define SCI_GET 0xf300 9162306a36Sopenharmony_ci#define SCI_SET 0xf400 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* Return codes */ 9462306a36Sopenharmony_ci#define TOS_SUCCESS 0x0000 9562306a36Sopenharmony_ci#define TOS_SUCCESS2 0x0001 9662306a36Sopenharmony_ci#define TOS_OPEN_CLOSE_OK 0x0044 9762306a36Sopenharmony_ci#define TOS_FAILURE 0x1000 9862306a36Sopenharmony_ci#define TOS_NOT_SUPPORTED 0x8000 9962306a36Sopenharmony_ci#define TOS_ALREADY_OPEN 0x8100 10062306a36Sopenharmony_ci#define TOS_NOT_OPENED 0x8200 10162306a36Sopenharmony_ci#define TOS_INPUT_DATA_ERROR 0x8300 10262306a36Sopenharmony_ci#define TOS_WRITE_PROTECTED 0x8400 10362306a36Sopenharmony_ci#define TOS_NOT_PRESENT 0x8600 10462306a36Sopenharmony_ci#define TOS_FIFO_EMPTY 0x8c00 10562306a36Sopenharmony_ci#define TOS_DATA_NOT_AVAILABLE 0x8d20 10662306a36Sopenharmony_ci#define TOS_NOT_INITIALIZED 0x8d50 10762306a36Sopenharmony_ci#define TOS_NOT_INSTALLED 0x8e00 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* Registers */ 11062306a36Sopenharmony_ci#define HCI_PANEL_POWER_ON 0x0002 11162306a36Sopenharmony_ci#define HCI_FAN 0x0004 11262306a36Sopenharmony_ci#define HCI_TR_BACKLIGHT 0x0005 11362306a36Sopenharmony_ci#define HCI_SYSTEM_EVENT 0x0016 11462306a36Sopenharmony_ci#define HCI_VIDEO_OUT 0x001c 11562306a36Sopenharmony_ci#define HCI_HOTKEY_EVENT 0x001e 11662306a36Sopenharmony_ci#define HCI_LCD_BRIGHTNESS 0x002a 11762306a36Sopenharmony_ci#define HCI_FAN_RPM 0x0045 11862306a36Sopenharmony_ci#define HCI_WIRELESS 0x0056 11962306a36Sopenharmony_ci#define HCI_ACCELEROMETER 0x006d 12062306a36Sopenharmony_ci#define HCI_COOLING_METHOD 0x007f 12162306a36Sopenharmony_ci#define HCI_KBD_ILLUMINATION 0x0095 12262306a36Sopenharmony_ci#define HCI_ECO_MODE 0x0097 12362306a36Sopenharmony_ci#define HCI_ACCELEROMETER2 0x00a6 12462306a36Sopenharmony_ci#define HCI_BATTERY_CHARGE_MODE 0x00ba 12562306a36Sopenharmony_ci#define HCI_SYSTEM_INFO 0xc000 12662306a36Sopenharmony_ci#define SCI_PANEL_POWER_ON 0x010d 12762306a36Sopenharmony_ci#define SCI_ILLUMINATION 0x014e 12862306a36Sopenharmony_ci#define SCI_USB_SLEEP_CHARGE 0x0150 12962306a36Sopenharmony_ci#define SCI_KBD_ILLUM_STATUS 0x015c 13062306a36Sopenharmony_ci#define SCI_USB_SLEEP_MUSIC 0x015e 13162306a36Sopenharmony_ci#define SCI_USB_THREE 0x0169 13262306a36Sopenharmony_ci#define SCI_TOUCHPAD 0x050e 13362306a36Sopenharmony_ci#define SCI_KBD_FUNCTION_KEYS 0x0522 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* Field definitions */ 13662306a36Sopenharmony_ci#define HCI_ACCEL_MASK 0x7fff 13762306a36Sopenharmony_ci#define HCI_ACCEL_DIRECTION_MASK 0x8000 13862306a36Sopenharmony_ci#define HCI_HOTKEY_DISABLE 0x0b 13962306a36Sopenharmony_ci#define HCI_HOTKEY_ENABLE 0x09 14062306a36Sopenharmony_ci#define HCI_HOTKEY_SPECIAL_FUNCTIONS 0x10 14162306a36Sopenharmony_ci#define HCI_LCD_BRIGHTNESS_BITS 3 14262306a36Sopenharmony_ci#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) 14362306a36Sopenharmony_ci#define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) 14462306a36Sopenharmony_ci#define HCI_MISC_SHIFT 0x10 14562306a36Sopenharmony_ci#define HCI_SYSTEM_TYPE1 0x10 14662306a36Sopenharmony_ci#define HCI_SYSTEM_TYPE2 0x11 14762306a36Sopenharmony_ci#define HCI_VIDEO_OUT_LCD 0x1 14862306a36Sopenharmony_ci#define HCI_VIDEO_OUT_CRT 0x2 14962306a36Sopenharmony_ci#define HCI_VIDEO_OUT_TV 0x4 15062306a36Sopenharmony_ci#define SCI_KBD_MODE_MASK 0x1f 15162306a36Sopenharmony_ci#define SCI_KBD_MODE_FNZ 0x1 15262306a36Sopenharmony_ci#define SCI_KBD_MODE_AUTO 0x2 15362306a36Sopenharmony_ci#define SCI_KBD_MODE_ON 0x8 15462306a36Sopenharmony_ci#define SCI_KBD_MODE_OFF 0x10 15562306a36Sopenharmony_ci#define SCI_KBD_TIME_MAX 0x3c001a 15662306a36Sopenharmony_ci#define HCI_WIRELESS_STATUS 0x1 15762306a36Sopenharmony_ci#define HCI_WIRELESS_WWAN 0x3 15862306a36Sopenharmony_ci#define HCI_WIRELESS_WWAN_STATUS 0x2000 15962306a36Sopenharmony_ci#define HCI_WIRELESS_WWAN_POWER 0x4000 16062306a36Sopenharmony_ci#define SCI_USB_CHARGE_MODE_MASK 0xff 16162306a36Sopenharmony_ci#define SCI_USB_CHARGE_DISABLED 0x00 16262306a36Sopenharmony_ci#define SCI_USB_CHARGE_ALTERNATE 0x09 16362306a36Sopenharmony_ci#define SCI_USB_CHARGE_TYPICAL 0x11 16462306a36Sopenharmony_ci#define SCI_USB_CHARGE_AUTO 0x21 16562306a36Sopenharmony_ci#define SCI_USB_CHARGE_BAT_MASK 0x7 16662306a36Sopenharmony_ci#define SCI_USB_CHARGE_BAT_LVL_OFF 0x1 16762306a36Sopenharmony_ci#define SCI_USB_CHARGE_BAT_LVL_ON 0x4 16862306a36Sopenharmony_ci#define SCI_USB_CHARGE_BAT_LVL 0x0200 16962306a36Sopenharmony_ci#define SCI_USB_CHARGE_RAPID_DSP 0x0300 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistruct toshiba_acpi_dev { 17262306a36Sopenharmony_ci struct acpi_device *acpi_dev; 17362306a36Sopenharmony_ci const char *method_hci; 17462306a36Sopenharmony_ci struct input_dev *hotkey_dev; 17562306a36Sopenharmony_ci struct work_struct hotkey_work; 17662306a36Sopenharmony_ci struct backlight_device *backlight_dev; 17762306a36Sopenharmony_ci struct led_classdev led_dev; 17862306a36Sopenharmony_ci struct led_classdev kbd_led; 17962306a36Sopenharmony_ci struct led_classdev eco_led; 18062306a36Sopenharmony_ci struct miscdevice miscdev; 18162306a36Sopenharmony_ci struct rfkill *wwan_rfk; 18262306a36Sopenharmony_ci struct iio_dev *indio_dev; 18362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_HWMON) 18462306a36Sopenharmony_ci struct device *hwmon_device; 18562306a36Sopenharmony_ci#endif 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci int force_fan; 18862306a36Sopenharmony_ci int last_key_event; 18962306a36Sopenharmony_ci int key_event_valid; 19062306a36Sopenharmony_ci int kbd_type; 19162306a36Sopenharmony_ci int kbd_mode; 19262306a36Sopenharmony_ci int kbd_time; 19362306a36Sopenharmony_ci int usbsc_bat_level; 19462306a36Sopenharmony_ci int usbsc_mode_base; 19562306a36Sopenharmony_ci int hotkey_event_type; 19662306a36Sopenharmony_ci int max_cooling_method; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci unsigned int illumination_supported:1; 19962306a36Sopenharmony_ci unsigned int video_supported:1; 20062306a36Sopenharmony_ci unsigned int fan_supported:1; 20162306a36Sopenharmony_ci unsigned int fan_rpm_supported:1; 20262306a36Sopenharmony_ci unsigned int system_event_supported:1; 20362306a36Sopenharmony_ci unsigned int ntfy_supported:1; 20462306a36Sopenharmony_ci unsigned int info_supported:1; 20562306a36Sopenharmony_ci unsigned int tr_backlight_supported:1; 20662306a36Sopenharmony_ci unsigned int kbd_illum_supported:1; 20762306a36Sopenharmony_ci unsigned int touchpad_supported:1; 20862306a36Sopenharmony_ci unsigned int eco_supported:1; 20962306a36Sopenharmony_ci unsigned int accelerometer_supported:1; 21062306a36Sopenharmony_ci unsigned int usb_sleep_charge_supported:1; 21162306a36Sopenharmony_ci unsigned int usb_rapid_charge_supported:1; 21262306a36Sopenharmony_ci unsigned int usb_sleep_music_supported:1; 21362306a36Sopenharmony_ci unsigned int kbd_function_keys_supported:1; 21462306a36Sopenharmony_ci unsigned int panel_power_on_supported:1; 21562306a36Sopenharmony_ci unsigned int usb_three_supported:1; 21662306a36Sopenharmony_ci unsigned int wwan_supported:1; 21762306a36Sopenharmony_ci unsigned int cooling_method_supported:1; 21862306a36Sopenharmony_ci unsigned int battery_charge_mode_supported:1; 21962306a36Sopenharmony_ci unsigned int sysfs_created:1; 22062306a36Sopenharmony_ci unsigned int special_functions; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci bool kbd_event_generated; 22362306a36Sopenharmony_ci bool killswitch; 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic struct toshiba_acpi_dev *toshiba_acpi; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic bool disable_hotkeys; 22962306a36Sopenharmony_cimodule_param(disable_hotkeys, bool, 0444); 23062306a36Sopenharmony_ciMODULE_PARM_DESC(disable_hotkeys, "Disables the hotkeys activation"); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic const struct acpi_device_id toshiba_device_ids[] = { 23362306a36Sopenharmony_ci {"TOS6200", 0}, 23462306a36Sopenharmony_ci {"TOS6207", 0}, 23562306a36Sopenharmony_ci {"TOS6208", 0}, 23662306a36Sopenharmony_ci {"TOS1900", 0}, 23762306a36Sopenharmony_ci {"", 0}, 23862306a36Sopenharmony_ci}; 23962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, toshiba_device_ids); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic const struct key_entry toshiba_acpi_keymap[] = { 24262306a36Sopenharmony_ci { KE_KEY, 0x9e, { KEY_RFKILL } }, 24362306a36Sopenharmony_ci { KE_KEY, 0x101, { KEY_MUTE } }, 24462306a36Sopenharmony_ci { KE_KEY, 0x102, { KEY_ZOOMOUT } }, 24562306a36Sopenharmony_ci { KE_KEY, 0x103, { KEY_ZOOMIN } }, 24662306a36Sopenharmony_ci { KE_KEY, 0x10f, { KEY_TAB } }, 24762306a36Sopenharmony_ci { KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } }, 24862306a36Sopenharmony_ci { KE_KEY, 0x139, { KEY_ZOOMRESET } }, 24962306a36Sopenharmony_ci { KE_KEY, 0x13b, { KEY_COFFEE } }, 25062306a36Sopenharmony_ci { KE_KEY, 0x13c, { KEY_BATTERY } }, 25162306a36Sopenharmony_ci { KE_KEY, 0x13d, { KEY_SLEEP } }, 25262306a36Sopenharmony_ci { KE_KEY, 0x13e, { KEY_SUSPEND } }, 25362306a36Sopenharmony_ci { KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } }, 25462306a36Sopenharmony_ci { KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } }, 25562306a36Sopenharmony_ci { KE_KEY, 0x141, { KEY_BRIGHTNESSUP } }, 25662306a36Sopenharmony_ci { KE_KEY, 0x142, { KEY_WLAN } }, 25762306a36Sopenharmony_ci { KE_KEY, 0x143, { KEY_TOUCHPAD_TOGGLE } }, 25862306a36Sopenharmony_ci { KE_KEY, 0x17f, { KEY_FN } }, 25962306a36Sopenharmony_ci { KE_KEY, 0xb05, { KEY_PROG2 } }, 26062306a36Sopenharmony_ci { KE_KEY, 0xb06, { KEY_WWW } }, 26162306a36Sopenharmony_ci { KE_KEY, 0xb07, { KEY_MAIL } }, 26262306a36Sopenharmony_ci { KE_KEY, 0xb30, { KEY_STOP } }, 26362306a36Sopenharmony_ci { KE_KEY, 0xb31, { KEY_PREVIOUSSONG } }, 26462306a36Sopenharmony_ci { KE_KEY, 0xb32, { KEY_NEXTSONG } }, 26562306a36Sopenharmony_ci { KE_KEY, 0xb33, { KEY_PLAYPAUSE } }, 26662306a36Sopenharmony_ci { KE_KEY, 0xb5a, { KEY_MEDIA } }, 26762306a36Sopenharmony_ci { KE_IGNORE, 0x1430, { KEY_RESERVED } }, /* Wake from sleep */ 26862306a36Sopenharmony_ci { KE_IGNORE, 0x1501, { KEY_RESERVED } }, /* Output changed */ 26962306a36Sopenharmony_ci { KE_IGNORE, 0x1502, { KEY_RESERVED } }, /* HDMI plugged/unplugged */ 27062306a36Sopenharmony_ci { KE_IGNORE, 0x1ABE, { KEY_RESERVED } }, /* Protection level set */ 27162306a36Sopenharmony_ci { KE_IGNORE, 0x1ABF, { KEY_RESERVED } }, /* Protection level off */ 27262306a36Sopenharmony_ci { KE_END, 0 }, 27362306a36Sopenharmony_ci}; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic const struct key_entry toshiba_acpi_alt_keymap[] = { 27662306a36Sopenharmony_ci { KE_KEY, 0x102, { KEY_ZOOMOUT } }, 27762306a36Sopenharmony_ci { KE_KEY, 0x103, { KEY_ZOOMIN } }, 27862306a36Sopenharmony_ci { KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } }, 27962306a36Sopenharmony_ci { KE_KEY, 0x139, { KEY_ZOOMRESET } }, 28062306a36Sopenharmony_ci { KE_KEY, 0x13c, { KEY_BRIGHTNESSDOWN } }, 28162306a36Sopenharmony_ci { KE_KEY, 0x13d, { KEY_BRIGHTNESSUP } }, 28262306a36Sopenharmony_ci { KE_KEY, 0x13e, { KEY_SWITCHVIDEOMODE } }, 28362306a36Sopenharmony_ci { KE_KEY, 0x13f, { KEY_TOUCHPAD_TOGGLE } }, 28462306a36Sopenharmony_ci { KE_KEY, 0x157, { KEY_MUTE } }, 28562306a36Sopenharmony_ci { KE_KEY, 0x158, { KEY_WLAN } }, 28662306a36Sopenharmony_ci { KE_END, 0 }, 28762306a36Sopenharmony_ci}; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/* 29062306a36Sopenharmony_ci * Utility 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic inline void _set_bit(u32 *word, u32 mask, int value) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci *word = (*word & ~mask) | (mask * value); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci/* 29962306a36Sopenharmony_ci * ACPI interface wrappers 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int write_acpi_int(const char *methodName, int val) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci acpi_status status; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci status = acpi_execute_simple_method(NULL, (char *)methodName, val); 30762306a36Sopenharmony_ci return (status == AE_OK) ? 0 : -EIO; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* 31162306a36Sopenharmony_ci * Perform a raw configuration call. Here we don't care about input or output 31262306a36Sopenharmony_ci * buffer format. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_cistatic acpi_status tci_raw(struct toshiba_acpi_dev *dev, 31562306a36Sopenharmony_ci const u32 in[TCI_WORDS], u32 out[TCI_WORDS]) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci union acpi_object in_objs[TCI_WORDS], out_objs[TCI_WORDS + 1]; 31862306a36Sopenharmony_ci struct acpi_object_list params; 31962306a36Sopenharmony_ci struct acpi_buffer results; 32062306a36Sopenharmony_ci acpi_status status; 32162306a36Sopenharmony_ci int i; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci params.count = TCI_WORDS; 32462306a36Sopenharmony_ci params.pointer = in_objs; 32562306a36Sopenharmony_ci for (i = 0; i < TCI_WORDS; ++i) { 32662306a36Sopenharmony_ci in_objs[i].type = ACPI_TYPE_INTEGER; 32762306a36Sopenharmony_ci in_objs[i].integer.value = in[i]; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci results.length = sizeof(out_objs); 33162306a36Sopenharmony_ci results.pointer = out_objs; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci status = acpi_evaluate_object(dev->acpi_dev->handle, 33462306a36Sopenharmony_ci (char *)dev->method_hci, ¶ms, 33562306a36Sopenharmony_ci &results); 33662306a36Sopenharmony_ci if ((status == AE_OK) && (out_objs->package.count <= TCI_WORDS)) { 33762306a36Sopenharmony_ci for (i = 0; i < out_objs->package.count; ++i) 33862306a36Sopenharmony_ci out[i] = out_objs->package.elements[i].integer.value; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return status; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci/* 34562306a36Sopenharmony_ci * Common hci tasks 34662306a36Sopenharmony_ci * 34762306a36Sopenharmony_ci * In addition to the ACPI status, the HCI system returns a result which 34862306a36Sopenharmony_ci * may be useful (such as "not supported"). 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic u32 hci_write(struct toshiba_acpi_dev *dev, u32 reg, u32 in1) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 }; 35462306a36Sopenharmony_ci u32 out[TCI_WORDS]; 35562306a36Sopenharmony_ci acpi_status status = tci_raw(dev, in, out); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return ACPI_SUCCESS(status) ? out[0] : TOS_FAILURE; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic u32 hci_read(struct toshiba_acpi_dev *dev, u32 reg, u32 *out1) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 }; 36362306a36Sopenharmony_ci u32 out[TCI_WORDS]; 36462306a36Sopenharmony_ci acpi_status status = tci_raw(dev, in, out); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 36762306a36Sopenharmony_ci return TOS_FAILURE; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci *out1 = out[2]; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return out[0]; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci/* 37562306a36Sopenharmony_ci * Common sci tasks 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic int sci_open(struct toshiba_acpi_dev *dev) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci u32 in[TCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 }; 38162306a36Sopenharmony_ci u32 out[TCI_WORDS]; 38262306a36Sopenharmony_ci acpi_status status = tci_raw(dev, in, out); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 38562306a36Sopenharmony_ci pr_err("ACPI call to open SCI failed\n"); 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (out[0] == TOS_OPEN_CLOSE_OK) { 39062306a36Sopenharmony_ci return 1; 39162306a36Sopenharmony_ci } else if (out[0] == TOS_ALREADY_OPEN) { 39262306a36Sopenharmony_ci pr_info("Toshiba SCI already opened\n"); 39362306a36Sopenharmony_ci return 1; 39462306a36Sopenharmony_ci } else if (out[0] == TOS_NOT_SUPPORTED) { 39562306a36Sopenharmony_ci /* 39662306a36Sopenharmony_ci * Some BIOSes do not have the SCI open/close functions 39762306a36Sopenharmony_ci * implemented and return 0x8000 (Not Supported), failing to 39862306a36Sopenharmony_ci * register some supported features. 39962306a36Sopenharmony_ci * 40062306a36Sopenharmony_ci * Simply return 1 if we hit those affected laptops to make the 40162306a36Sopenharmony_ci * supported features work. 40262306a36Sopenharmony_ci * 40362306a36Sopenharmony_ci * In the case that some laptops really do not support the SCI, 40462306a36Sopenharmony_ci * all the SCI dependent functions check for TOS_NOT_SUPPORTED, 40562306a36Sopenharmony_ci * and thus, not registering support for the queried feature. 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_ci return 1; 40862306a36Sopenharmony_ci } else if (out[0] == TOS_NOT_PRESENT) { 40962306a36Sopenharmony_ci pr_info("Toshiba SCI is not present\n"); 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void sci_close(struct toshiba_acpi_dev *dev) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci u32 in[TCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 }; 41862306a36Sopenharmony_ci u32 out[TCI_WORDS]; 41962306a36Sopenharmony_ci acpi_status status = tci_raw(dev, in, out); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 42262306a36Sopenharmony_ci pr_err("ACPI call to close SCI failed\n"); 42362306a36Sopenharmony_ci return; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (out[0] == TOS_OPEN_CLOSE_OK) 42762306a36Sopenharmony_ci return; 42862306a36Sopenharmony_ci else if (out[0] == TOS_NOT_OPENED) 42962306a36Sopenharmony_ci pr_info("Toshiba SCI not opened\n"); 43062306a36Sopenharmony_ci else if (out[0] == TOS_NOT_PRESENT) 43162306a36Sopenharmony_ci pr_info("Toshiba SCI is not present\n"); 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic u32 sci_read(struct toshiba_acpi_dev *dev, u32 reg, u32 *out1) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci u32 in[TCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 }; 43762306a36Sopenharmony_ci u32 out[TCI_WORDS]; 43862306a36Sopenharmony_ci acpi_status status = tci_raw(dev, in, out); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 44162306a36Sopenharmony_ci return TOS_FAILURE; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci *out1 = out[2]; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return out[0]; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic u32 sci_write(struct toshiba_acpi_dev *dev, u32 reg, u32 in1) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci u32 in[TCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 }; 45162306a36Sopenharmony_ci u32 out[TCI_WORDS]; 45262306a36Sopenharmony_ci acpi_status status = tci_raw(dev, in, out); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return ACPI_SUCCESS(status) ? out[0] : TOS_FAILURE; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci/* Illumination support */ 45862306a36Sopenharmony_cistatic void toshiba_illumination_available(struct toshiba_acpi_dev *dev) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci u32 in[TCI_WORDS] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 }; 46162306a36Sopenharmony_ci u32 out[TCI_WORDS]; 46262306a36Sopenharmony_ci acpi_status status; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci dev->illumination_supported = 0; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (!sci_open(dev)) 46762306a36Sopenharmony_ci return; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci status = tci_raw(dev, in, out); 47062306a36Sopenharmony_ci sci_close(dev); 47162306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 47262306a36Sopenharmony_ci pr_err("ACPI call to query Illumination support failed\n"); 47362306a36Sopenharmony_ci return; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS) 47762306a36Sopenharmony_ci return; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci dev->illumination_supported = 1; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic void toshiba_illumination_set(struct led_classdev *cdev, 48362306a36Sopenharmony_ci enum led_brightness brightness) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = container_of(cdev, 48662306a36Sopenharmony_ci struct toshiba_acpi_dev, led_dev); 48762306a36Sopenharmony_ci u32 result; 48862306a36Sopenharmony_ci u32 state; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* First request : initialize communication. */ 49162306a36Sopenharmony_ci if (!sci_open(dev)) 49262306a36Sopenharmony_ci return; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* Switch the illumination on/off */ 49562306a36Sopenharmony_ci state = brightness ? 1 : 0; 49662306a36Sopenharmony_ci result = sci_write(dev, SCI_ILLUMINATION, state); 49762306a36Sopenharmony_ci sci_close(dev); 49862306a36Sopenharmony_ci if (result == TOS_FAILURE) 49962306a36Sopenharmony_ci pr_err("ACPI call for illumination failed\n"); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = container_of(cdev, 50562306a36Sopenharmony_ci struct toshiba_acpi_dev, led_dev); 50662306a36Sopenharmony_ci u32 result; 50762306a36Sopenharmony_ci u32 state; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* First request : initialize communication. */ 51062306a36Sopenharmony_ci if (!sci_open(dev)) 51162306a36Sopenharmony_ci return LED_OFF; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* Check the illumination */ 51462306a36Sopenharmony_ci result = sci_read(dev, SCI_ILLUMINATION, &state); 51562306a36Sopenharmony_ci sci_close(dev); 51662306a36Sopenharmony_ci if (result == TOS_FAILURE) { 51762306a36Sopenharmony_ci pr_err("ACPI call for illumination failed\n"); 51862306a36Sopenharmony_ci return LED_OFF; 51962306a36Sopenharmony_ci } else if (result != TOS_SUCCESS) { 52062306a36Sopenharmony_ci return LED_OFF; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci return state ? LED_FULL : LED_OFF; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci/* KBD Illumination */ 52762306a36Sopenharmony_cistatic void toshiba_kbd_illum_available(struct toshiba_acpi_dev *dev) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci u32 in[TCI_WORDS] = { SCI_GET, SCI_KBD_ILLUM_STATUS, 0, 0, 0, 0 }; 53062306a36Sopenharmony_ci u32 out[TCI_WORDS]; 53162306a36Sopenharmony_ci acpi_status status; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci dev->kbd_illum_supported = 0; 53462306a36Sopenharmony_ci dev->kbd_event_generated = false; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (!sci_open(dev)) 53762306a36Sopenharmony_ci return; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci status = tci_raw(dev, in, out); 54062306a36Sopenharmony_ci sci_close(dev); 54162306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 54262306a36Sopenharmony_ci pr_err("ACPI call to query kbd illumination support failed\n"); 54362306a36Sopenharmony_ci return; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS) 54762306a36Sopenharmony_ci return; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* 55062306a36Sopenharmony_ci * Check for keyboard backlight timeout max value, 55162306a36Sopenharmony_ci * previous kbd backlight implementation set this to 55262306a36Sopenharmony_ci * 0x3c0003, and now the new implementation set this 55362306a36Sopenharmony_ci * to 0x3c001a, use this to distinguish between them. 55462306a36Sopenharmony_ci */ 55562306a36Sopenharmony_ci if (out[3] == SCI_KBD_TIME_MAX) 55662306a36Sopenharmony_ci dev->kbd_type = 2; 55762306a36Sopenharmony_ci else 55862306a36Sopenharmony_ci dev->kbd_type = 1; 55962306a36Sopenharmony_ci /* Get the current keyboard backlight mode */ 56062306a36Sopenharmony_ci dev->kbd_mode = out[2] & SCI_KBD_MODE_MASK; 56162306a36Sopenharmony_ci /* Get the current time (1-60 seconds) */ 56262306a36Sopenharmony_ci dev->kbd_time = out[2] >> HCI_MISC_SHIFT; 56362306a36Sopenharmony_ci /* Flag as supported */ 56462306a36Sopenharmony_ci dev->kbd_illum_supported = 1; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci u32 result; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (!sci_open(dev)) 57262306a36Sopenharmony_ci return -EIO; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci result = sci_write(dev, SCI_KBD_ILLUM_STATUS, time); 57562306a36Sopenharmony_ci sci_close(dev); 57662306a36Sopenharmony_ci if (result == TOS_FAILURE) 57762306a36Sopenharmony_ci pr_err("ACPI call to set KBD backlight status failed\n"); 57862306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 57962306a36Sopenharmony_ci return -ENODEV; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci u32 result; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (!sci_open(dev)) 58962306a36Sopenharmony_ci return -EIO; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci result = sci_read(dev, SCI_KBD_ILLUM_STATUS, time); 59262306a36Sopenharmony_ci sci_close(dev); 59362306a36Sopenharmony_ci if (result == TOS_FAILURE) 59462306a36Sopenharmony_ci pr_err("ACPI call to get KBD backlight status failed\n"); 59562306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 59662306a36Sopenharmony_ci return -ENODEV; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic enum led_brightness toshiba_kbd_backlight_get(struct led_classdev *cdev) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = container_of(cdev, 60462306a36Sopenharmony_ci struct toshiba_acpi_dev, kbd_led); 60562306a36Sopenharmony_ci u32 result; 60662306a36Sopenharmony_ci u32 state; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* Check the keyboard backlight state */ 60962306a36Sopenharmony_ci result = hci_read(dev, HCI_KBD_ILLUMINATION, &state); 61062306a36Sopenharmony_ci if (result == TOS_FAILURE) { 61162306a36Sopenharmony_ci pr_err("ACPI call to get the keyboard backlight failed\n"); 61262306a36Sopenharmony_ci return LED_OFF; 61362306a36Sopenharmony_ci } else if (result != TOS_SUCCESS) { 61462306a36Sopenharmony_ci return LED_OFF; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci return state ? LED_FULL : LED_OFF; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic void toshiba_kbd_backlight_set(struct led_classdev *cdev, 62162306a36Sopenharmony_ci enum led_brightness brightness) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = container_of(cdev, 62462306a36Sopenharmony_ci struct toshiba_acpi_dev, kbd_led); 62562306a36Sopenharmony_ci u32 result; 62662306a36Sopenharmony_ci u32 state; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* Set the keyboard backlight state */ 62962306a36Sopenharmony_ci state = brightness ? 1 : 0; 63062306a36Sopenharmony_ci result = hci_write(dev, HCI_KBD_ILLUMINATION, state); 63162306a36Sopenharmony_ci if (result == TOS_FAILURE) 63262306a36Sopenharmony_ci pr_err("ACPI call to set KBD Illumination mode failed\n"); 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci/* TouchPad support */ 63662306a36Sopenharmony_cistatic int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci u32 result; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (!sci_open(dev)) 64162306a36Sopenharmony_ci return -EIO; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci result = sci_write(dev, SCI_TOUCHPAD, state); 64462306a36Sopenharmony_ci sci_close(dev); 64562306a36Sopenharmony_ci if (result == TOS_FAILURE) 64662306a36Sopenharmony_ci pr_err("ACPI call to set the touchpad failed\n"); 64762306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 64862306a36Sopenharmony_ci return -ENODEV; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci u32 result; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (!sci_open(dev)) 65862306a36Sopenharmony_ci return -EIO; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci result = sci_read(dev, SCI_TOUCHPAD, state); 66162306a36Sopenharmony_ci sci_close(dev); 66262306a36Sopenharmony_ci if (result == TOS_FAILURE) 66362306a36Sopenharmony_ci pr_err("ACPI call to query the touchpad failed\n"); 66462306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 66562306a36Sopenharmony_ci return -ENODEV; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci/* Eco Mode support */ 67162306a36Sopenharmony_cistatic void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 0, 0, 0 }; 67462306a36Sopenharmony_ci u32 out[TCI_WORDS]; 67562306a36Sopenharmony_ci acpi_status status; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci dev->eco_supported = 0; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci status = tci_raw(dev, in, out); 68062306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 68162306a36Sopenharmony_ci pr_err("ACPI call to get ECO led failed\n"); 68262306a36Sopenharmony_ci return; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (out[0] == TOS_INPUT_DATA_ERROR || out[0] == TOS_NOT_SUPPORTED) { 68662306a36Sopenharmony_ci /* 68762306a36Sopenharmony_ci * If we receive 0x8300 (Input Data Error), it means that the 68862306a36Sopenharmony_ci * LED device is present, but that we just screwed the input 68962306a36Sopenharmony_ci * parameters. 69062306a36Sopenharmony_ci * 69162306a36Sopenharmony_ci * On some laptops 0x8000 (Not supported) is also returned in 69262306a36Sopenharmony_ci * this case, so we need to allow for that as well. 69362306a36Sopenharmony_ci * 69462306a36Sopenharmony_ci * Let's query the status of the LED to see if we really have a 69562306a36Sopenharmony_ci * success response, indicating the actual presense of the LED, 69662306a36Sopenharmony_ci * bail out otherwise. 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_ci in[3] = 1; 69962306a36Sopenharmony_ci status = tci_raw(dev, in, out); 70062306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 70162306a36Sopenharmony_ci pr_err("ACPI call to get ECO led failed\n"); 70262306a36Sopenharmony_ci return; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS) 70662306a36Sopenharmony_ci return; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci dev->eco_supported = 1; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic enum led_brightness 71362306a36Sopenharmony_citoshiba_eco_mode_get_status(struct led_classdev *cdev) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = container_of(cdev, 71662306a36Sopenharmony_ci struct toshiba_acpi_dev, eco_led); 71762306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 }; 71862306a36Sopenharmony_ci u32 out[TCI_WORDS]; 71962306a36Sopenharmony_ci acpi_status status; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci status = tci_raw(dev, in, out); 72262306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 72362306a36Sopenharmony_ci pr_err("ACPI call to get ECO led failed\n"); 72462306a36Sopenharmony_ci return LED_OFF; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS) 72862306a36Sopenharmony_ci return LED_OFF; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci return out[2] ? LED_FULL : LED_OFF; 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic void toshiba_eco_mode_set_status(struct led_classdev *cdev, 73462306a36Sopenharmony_ci enum led_brightness brightness) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = container_of(cdev, 73762306a36Sopenharmony_ci struct toshiba_acpi_dev, eco_led); 73862306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_SET, HCI_ECO_MODE, 0, 1, 0, 0 }; 73962306a36Sopenharmony_ci u32 out[TCI_WORDS]; 74062306a36Sopenharmony_ci acpi_status status; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci /* Switch the Eco Mode led on/off */ 74362306a36Sopenharmony_ci in[2] = (brightness) ? 1 : 0; 74462306a36Sopenharmony_ci status = tci_raw(dev, in, out); 74562306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 74662306a36Sopenharmony_ci pr_err("ACPI call to set ECO led failed\n"); 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci/* Accelerometer support */ 75062306a36Sopenharmony_cistatic void toshiba_accelerometer_available(struct toshiba_acpi_dev *dev) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER2, 0, 0, 0, 0 }; 75362306a36Sopenharmony_ci u32 out[TCI_WORDS]; 75462306a36Sopenharmony_ci acpi_status status; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci dev->accelerometer_supported = 0; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* 75962306a36Sopenharmony_ci * Check if the accelerometer call exists, 76062306a36Sopenharmony_ci * this call also serves as initialization 76162306a36Sopenharmony_ci */ 76262306a36Sopenharmony_ci status = tci_raw(dev, in, out); 76362306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 76462306a36Sopenharmony_ci pr_err("ACPI call to query the accelerometer failed\n"); 76562306a36Sopenharmony_ci return; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS) 76962306a36Sopenharmony_ci return; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci dev->accelerometer_supported = 1; 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev, 77562306a36Sopenharmony_ci u32 *xy, u32 *z) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER, 0, 1, 0, 0 }; 77862306a36Sopenharmony_ci u32 out[TCI_WORDS]; 77962306a36Sopenharmony_ci acpi_status status; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* Check the Accelerometer status */ 78262306a36Sopenharmony_ci status = tci_raw(dev, in, out); 78362306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 78462306a36Sopenharmony_ci pr_err("ACPI call to query the accelerometer failed\n"); 78562306a36Sopenharmony_ci return -EIO; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (out[0] == TOS_NOT_SUPPORTED) 78962306a36Sopenharmony_ci return -ENODEV; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS) 79262306a36Sopenharmony_ci return -EIO; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci *xy = out[2]; 79562306a36Sopenharmony_ci *z = out[4]; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci return 0; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci/* Sleep (Charge and Music) utilities support */ 80162306a36Sopenharmony_cistatic void toshiba_usb_sleep_charge_available(struct toshiba_acpi_dev *dev) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci u32 in[TCI_WORDS] = { SCI_GET, SCI_USB_SLEEP_CHARGE, 0, 0, 0, 0 }; 80462306a36Sopenharmony_ci u32 out[TCI_WORDS]; 80562306a36Sopenharmony_ci acpi_status status; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci dev->usb_sleep_charge_supported = 0; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (!sci_open(dev)) 81062306a36Sopenharmony_ci return; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci status = tci_raw(dev, in, out); 81362306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 81462306a36Sopenharmony_ci pr_err("ACPI call to get USB Sleep and Charge mode failed\n"); 81562306a36Sopenharmony_ci sci_close(dev); 81662306a36Sopenharmony_ci return; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS) { 82062306a36Sopenharmony_ci sci_close(dev); 82162306a36Sopenharmony_ci return; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci dev->usbsc_mode_base = out[4]; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci in[5] = SCI_USB_CHARGE_BAT_LVL; 82762306a36Sopenharmony_ci status = tci_raw(dev, in, out); 82862306a36Sopenharmony_ci sci_close(dev); 82962306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 83062306a36Sopenharmony_ci pr_err("ACPI call to get USB Sleep and Charge mode failed\n"); 83162306a36Sopenharmony_ci return; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS) 83562306a36Sopenharmony_ci return; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci dev->usbsc_bat_level = out[2]; 83862306a36Sopenharmony_ci /* Flag as supported */ 83962306a36Sopenharmony_ci dev->usb_sleep_charge_supported = 1; 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic int toshiba_usb_sleep_charge_get(struct toshiba_acpi_dev *dev, 84362306a36Sopenharmony_ci u32 *mode) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci u32 result; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (!sci_open(dev)) 84862306a36Sopenharmony_ci return -EIO; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci result = sci_read(dev, SCI_USB_SLEEP_CHARGE, mode); 85162306a36Sopenharmony_ci sci_close(dev); 85262306a36Sopenharmony_ci if (result == TOS_FAILURE) 85362306a36Sopenharmony_ci pr_err("ACPI call to set USB S&C mode failed\n"); 85462306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 85562306a36Sopenharmony_ci return -ENODEV; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic int toshiba_usb_sleep_charge_set(struct toshiba_acpi_dev *dev, 86162306a36Sopenharmony_ci u32 mode) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci u32 result; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (!sci_open(dev)) 86662306a36Sopenharmony_ci return -EIO; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci result = sci_write(dev, SCI_USB_SLEEP_CHARGE, mode); 86962306a36Sopenharmony_ci sci_close(dev); 87062306a36Sopenharmony_ci if (result == TOS_FAILURE) 87162306a36Sopenharmony_ci pr_err("ACPI call to set USB S&C mode failed\n"); 87262306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 87362306a36Sopenharmony_ci return -ENODEV; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic int toshiba_sleep_functions_status_get(struct toshiba_acpi_dev *dev, 87962306a36Sopenharmony_ci u32 *mode) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci u32 in[TCI_WORDS] = { SCI_GET, SCI_USB_SLEEP_CHARGE, 0, 0, 0, 0 }; 88262306a36Sopenharmony_ci u32 out[TCI_WORDS]; 88362306a36Sopenharmony_ci acpi_status status; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (!sci_open(dev)) 88662306a36Sopenharmony_ci return -EIO; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci in[5] = SCI_USB_CHARGE_BAT_LVL; 88962306a36Sopenharmony_ci status = tci_raw(dev, in, out); 89062306a36Sopenharmony_ci sci_close(dev); 89162306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 89262306a36Sopenharmony_ci pr_err("ACPI call to get USB S&C battery level failed\n"); 89362306a36Sopenharmony_ci return -EIO; 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci if (out[0] == TOS_NOT_SUPPORTED) 89762306a36Sopenharmony_ci return -ENODEV; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS) 90062306a36Sopenharmony_ci return -EIO; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci *mode = out[2]; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci return 0; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic int toshiba_sleep_functions_status_set(struct toshiba_acpi_dev *dev, 90962306a36Sopenharmony_ci u32 mode) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci u32 in[TCI_WORDS] = { SCI_SET, SCI_USB_SLEEP_CHARGE, 0, 0, 0, 0 }; 91262306a36Sopenharmony_ci u32 out[TCI_WORDS]; 91362306a36Sopenharmony_ci acpi_status status; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (!sci_open(dev)) 91662306a36Sopenharmony_ci return -EIO; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci in[2] = mode; 91962306a36Sopenharmony_ci in[5] = SCI_USB_CHARGE_BAT_LVL; 92062306a36Sopenharmony_ci status = tci_raw(dev, in, out); 92162306a36Sopenharmony_ci sci_close(dev); 92262306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 92362306a36Sopenharmony_ci pr_err("ACPI call to set USB S&C battery level failed\n"); 92462306a36Sopenharmony_ci return -EIO; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (out[0] == TOS_NOT_SUPPORTED) 92862306a36Sopenharmony_ci return -ENODEV; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci return out[0] == TOS_SUCCESS ? 0 : -EIO; 93162306a36Sopenharmony_ci} 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_cistatic int toshiba_usb_rapid_charge_get(struct toshiba_acpi_dev *dev, 93462306a36Sopenharmony_ci u32 *state) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci u32 in[TCI_WORDS] = { SCI_GET, SCI_USB_SLEEP_CHARGE, 0, 0, 0, 0 }; 93762306a36Sopenharmony_ci u32 out[TCI_WORDS]; 93862306a36Sopenharmony_ci acpi_status status; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (!sci_open(dev)) 94162306a36Sopenharmony_ci return -EIO; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci in[5] = SCI_USB_CHARGE_RAPID_DSP; 94462306a36Sopenharmony_ci status = tci_raw(dev, in, out); 94562306a36Sopenharmony_ci sci_close(dev); 94662306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 94762306a36Sopenharmony_ci pr_err("ACPI call to get USB Rapid Charge failed\n"); 94862306a36Sopenharmony_ci return -EIO; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci if (out[0] == TOS_NOT_SUPPORTED) 95262306a36Sopenharmony_ci return -ENODEV; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS && out[0] != TOS_SUCCESS2) 95562306a36Sopenharmony_ci return -EIO; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci *state = out[2]; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci return 0; 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_cistatic int toshiba_usb_rapid_charge_set(struct toshiba_acpi_dev *dev, 96362306a36Sopenharmony_ci u32 state) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci u32 in[TCI_WORDS] = { SCI_SET, SCI_USB_SLEEP_CHARGE, 0, 0, 0, 0 }; 96662306a36Sopenharmony_ci u32 out[TCI_WORDS]; 96762306a36Sopenharmony_ci acpi_status status; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (!sci_open(dev)) 97062306a36Sopenharmony_ci return -EIO; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci in[2] = state; 97362306a36Sopenharmony_ci in[5] = SCI_USB_CHARGE_RAPID_DSP; 97462306a36Sopenharmony_ci status = tci_raw(dev, in, out); 97562306a36Sopenharmony_ci sci_close(dev); 97662306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 97762306a36Sopenharmony_ci pr_err("ACPI call to set USB Rapid Charge failed\n"); 97862306a36Sopenharmony_ci return -EIO; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci if (out[0] == TOS_NOT_SUPPORTED) 98262306a36Sopenharmony_ci return -ENODEV; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci return (out[0] == TOS_SUCCESS || out[0] == TOS_SUCCESS2) ? 0 : -EIO; 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic int toshiba_usb_sleep_music_get(struct toshiba_acpi_dev *dev, u32 *state) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci u32 result; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci if (!sci_open(dev)) 99262306a36Sopenharmony_ci return -EIO; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci result = sci_read(dev, SCI_USB_SLEEP_MUSIC, state); 99562306a36Sopenharmony_ci sci_close(dev); 99662306a36Sopenharmony_ci if (result == TOS_FAILURE) 99762306a36Sopenharmony_ci pr_err("ACPI call to get Sleep and Music failed\n"); 99862306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 99962306a36Sopenharmony_ci return -ENODEV; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cistatic int toshiba_usb_sleep_music_set(struct toshiba_acpi_dev *dev, u32 state) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci u32 result; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (!sci_open(dev)) 100962306a36Sopenharmony_ci return -EIO; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci result = sci_write(dev, SCI_USB_SLEEP_MUSIC, state); 101262306a36Sopenharmony_ci sci_close(dev); 101362306a36Sopenharmony_ci if (result == TOS_FAILURE) 101462306a36Sopenharmony_ci pr_err("ACPI call to set Sleep and Music failed\n"); 101562306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 101662306a36Sopenharmony_ci return -ENODEV; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 101962306a36Sopenharmony_ci} 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci/* Keyboard function keys */ 102262306a36Sopenharmony_cistatic int toshiba_function_keys_get(struct toshiba_acpi_dev *dev, u32 *mode) 102362306a36Sopenharmony_ci{ 102462306a36Sopenharmony_ci u32 result; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci if (!sci_open(dev)) 102762306a36Sopenharmony_ci return -EIO; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci result = sci_read(dev, SCI_KBD_FUNCTION_KEYS, mode); 103062306a36Sopenharmony_ci sci_close(dev); 103162306a36Sopenharmony_ci if (result == TOS_FAILURE) 103262306a36Sopenharmony_ci pr_err("ACPI call to get KBD function keys failed\n"); 103362306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 103462306a36Sopenharmony_ci return -ENODEV; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO; 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_cistatic int toshiba_function_keys_set(struct toshiba_acpi_dev *dev, u32 mode) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci u32 result; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci if (!sci_open(dev)) 104462306a36Sopenharmony_ci return -EIO; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci result = sci_write(dev, SCI_KBD_FUNCTION_KEYS, mode); 104762306a36Sopenharmony_ci sci_close(dev); 104862306a36Sopenharmony_ci if (result == TOS_FAILURE) 104962306a36Sopenharmony_ci pr_err("ACPI call to set KBD function keys failed\n"); 105062306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 105162306a36Sopenharmony_ci return -ENODEV; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO; 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci/* Panel Power ON */ 105762306a36Sopenharmony_cistatic int toshiba_panel_power_on_get(struct toshiba_acpi_dev *dev, u32 *state) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci u32 result; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci if (!sci_open(dev)) 106262306a36Sopenharmony_ci return -EIO; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci result = sci_read(dev, SCI_PANEL_POWER_ON, state); 106562306a36Sopenharmony_ci sci_close(dev); 106662306a36Sopenharmony_ci if (result == TOS_FAILURE) 106762306a36Sopenharmony_ci pr_err("ACPI call to get Panel Power ON failed\n"); 106862306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 106962306a36Sopenharmony_ci return -ENODEV; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_cistatic int toshiba_panel_power_on_set(struct toshiba_acpi_dev *dev, u32 state) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci u32 result; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci if (!sci_open(dev)) 107962306a36Sopenharmony_ci return -EIO; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci result = sci_write(dev, SCI_PANEL_POWER_ON, state); 108262306a36Sopenharmony_ci sci_close(dev); 108362306a36Sopenharmony_ci if (result == TOS_FAILURE) 108462306a36Sopenharmony_ci pr_err("ACPI call to set Panel Power ON failed\n"); 108562306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 108662306a36Sopenharmony_ci return -ENODEV; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 108962306a36Sopenharmony_ci} 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci/* USB Three */ 109262306a36Sopenharmony_cistatic int toshiba_usb_three_get(struct toshiba_acpi_dev *dev, u32 *state) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci u32 result; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci if (!sci_open(dev)) 109762306a36Sopenharmony_ci return -EIO; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci result = sci_read(dev, SCI_USB_THREE, state); 110062306a36Sopenharmony_ci sci_close(dev); 110162306a36Sopenharmony_ci if (result == TOS_FAILURE) 110262306a36Sopenharmony_ci pr_err("ACPI call to get USB 3 failed\n"); 110362306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 110462306a36Sopenharmony_ci return -ENODEV; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO; 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistatic int toshiba_usb_three_set(struct toshiba_acpi_dev *dev, u32 state) 111062306a36Sopenharmony_ci{ 111162306a36Sopenharmony_ci u32 result; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (!sci_open(dev)) 111462306a36Sopenharmony_ci return -EIO; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci result = sci_write(dev, SCI_USB_THREE, state); 111762306a36Sopenharmony_ci sci_close(dev); 111862306a36Sopenharmony_ci if (result == TOS_FAILURE) 111962306a36Sopenharmony_ci pr_err("ACPI call to set USB 3 failed\n"); 112062306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 112162306a36Sopenharmony_ci return -ENODEV; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO; 112462306a36Sopenharmony_ci} 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci/* Hotkey Event type */ 112762306a36Sopenharmony_cistatic int toshiba_hotkey_event_type_get(struct toshiba_acpi_dev *dev, 112862306a36Sopenharmony_ci u32 *type) 112962306a36Sopenharmony_ci{ 113062306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_GET, HCI_SYSTEM_INFO, 0x03, 0, 0, 0 }; 113162306a36Sopenharmony_ci u32 out[TCI_WORDS]; 113262306a36Sopenharmony_ci acpi_status status; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci status = tci_raw(dev, in, out); 113562306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 113662306a36Sopenharmony_ci pr_err("ACPI call to get System type failed\n"); 113762306a36Sopenharmony_ci return -EIO; 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (out[0] == TOS_NOT_SUPPORTED) 114162306a36Sopenharmony_ci return -ENODEV; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS) 114462306a36Sopenharmony_ci return -EIO; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci *type = out[3]; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci return 0; 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci/* Wireless status (RFKill, WLAN, BT, WWAN) */ 115262306a36Sopenharmony_cistatic int toshiba_wireless_status(struct toshiba_acpi_dev *dev) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_GET, HCI_WIRELESS, 0, 0, 0, 0 }; 115562306a36Sopenharmony_ci u32 out[TCI_WORDS]; 115662306a36Sopenharmony_ci acpi_status status; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci in[3] = HCI_WIRELESS_STATUS; 115962306a36Sopenharmony_ci status = tci_raw(dev, in, out); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 116262306a36Sopenharmony_ci pr_err("ACPI call to get Wireless status failed\n"); 116362306a36Sopenharmony_ci return -EIO; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci if (out[0] == TOS_NOT_SUPPORTED) 116762306a36Sopenharmony_ci return -ENODEV; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS) 117062306a36Sopenharmony_ci return -EIO; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci dev->killswitch = !!(out[2] & HCI_WIRELESS_STATUS); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci return 0; 117562306a36Sopenharmony_ci} 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci/* WWAN */ 117862306a36Sopenharmony_cistatic void toshiba_wwan_available(struct toshiba_acpi_dev *dev) 117962306a36Sopenharmony_ci{ 118062306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_GET, HCI_WIRELESS, 0, 0, 0, 0 }; 118162306a36Sopenharmony_ci u32 out[TCI_WORDS]; 118262306a36Sopenharmony_ci acpi_status status; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci dev->wwan_supported = 0; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci /* 118762306a36Sopenharmony_ci * WWAN support can be queried by setting the in[3] value to 118862306a36Sopenharmony_ci * HCI_WIRELESS_WWAN (0x03). 118962306a36Sopenharmony_ci * 119062306a36Sopenharmony_ci * If supported, out[0] contains TOS_SUCCESS and out[2] contains 119162306a36Sopenharmony_ci * HCI_WIRELESS_WWAN_STATUS (0x2000). 119262306a36Sopenharmony_ci * 119362306a36Sopenharmony_ci * If not supported, out[0] contains TOS_INPUT_DATA_ERROR (0x8300) 119462306a36Sopenharmony_ci * or TOS_NOT_SUPPORTED (0x8000). 119562306a36Sopenharmony_ci */ 119662306a36Sopenharmony_ci in[3] = HCI_WIRELESS_WWAN; 119762306a36Sopenharmony_ci status = tci_raw(dev, in, out); 119862306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 119962306a36Sopenharmony_ci pr_err("ACPI call to get WWAN status failed\n"); 120062306a36Sopenharmony_ci return; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS) 120462306a36Sopenharmony_ci return; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci dev->wwan_supported = (out[2] == HCI_WIRELESS_WWAN_STATUS); 120762306a36Sopenharmony_ci} 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_cistatic int toshiba_wwan_set(struct toshiba_acpi_dev *dev, u32 state) 121062306a36Sopenharmony_ci{ 121162306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_SET, HCI_WIRELESS, state, 0, 0, 0 }; 121262306a36Sopenharmony_ci u32 out[TCI_WORDS]; 121362306a36Sopenharmony_ci acpi_status status; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci in[3] = HCI_WIRELESS_WWAN_STATUS; 121662306a36Sopenharmony_ci status = tci_raw(dev, in, out); 121762306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 121862306a36Sopenharmony_ci pr_err("ACPI call to set WWAN status failed\n"); 121962306a36Sopenharmony_ci return -EIO; 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (out[0] == TOS_NOT_SUPPORTED) 122362306a36Sopenharmony_ci return -ENODEV; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS) 122662306a36Sopenharmony_ci return -EIO; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci /* 122962306a36Sopenharmony_ci * Some devices only need to call HCI_WIRELESS_WWAN_STATUS to 123062306a36Sopenharmony_ci * (de)activate the device, but some others need the 123162306a36Sopenharmony_ci * HCI_WIRELESS_WWAN_POWER call as well. 123262306a36Sopenharmony_ci */ 123362306a36Sopenharmony_ci in[3] = HCI_WIRELESS_WWAN_POWER; 123462306a36Sopenharmony_ci status = tci_raw(dev, in, out); 123562306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 123662306a36Sopenharmony_ci pr_err("ACPI call to set WWAN power failed\n"); 123762306a36Sopenharmony_ci return -EIO; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci if (out[0] == TOS_NOT_SUPPORTED) 124162306a36Sopenharmony_ci return -ENODEV; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci return out[0] == TOS_SUCCESS ? 0 : -EIO; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci/* Cooling Method */ 124762306a36Sopenharmony_cistatic void toshiba_cooling_method_available(struct toshiba_acpi_dev *dev) 124862306a36Sopenharmony_ci{ 124962306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_GET, HCI_COOLING_METHOD, 0, 0, 0, 0 }; 125062306a36Sopenharmony_ci u32 out[TCI_WORDS]; 125162306a36Sopenharmony_ci acpi_status status; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci dev->cooling_method_supported = 0; 125462306a36Sopenharmony_ci dev->max_cooling_method = 0; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci status = tci_raw(dev, in, out); 125762306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 125862306a36Sopenharmony_ci pr_err("ACPI call to get Cooling Method failed\n"); 125962306a36Sopenharmony_ci return; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS && out[0] != TOS_SUCCESS2) 126362306a36Sopenharmony_ci return; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci dev->cooling_method_supported = 1; 126662306a36Sopenharmony_ci dev->max_cooling_method = out[3]; 126762306a36Sopenharmony_ci} 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_cistatic int toshiba_cooling_method_get(struct toshiba_acpi_dev *dev, u32 *state) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci u32 result = hci_read(dev, HCI_COOLING_METHOD, state); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci if (result == TOS_FAILURE) 127462306a36Sopenharmony_ci pr_err("ACPI call to get Cooling Method failed\n"); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci if (result == TOS_NOT_SUPPORTED) 127762306a36Sopenharmony_ci return -ENODEV; 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO; 128062306a36Sopenharmony_ci} 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_cistatic int toshiba_cooling_method_set(struct toshiba_acpi_dev *dev, u32 state) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci u32 result = hci_write(dev, HCI_COOLING_METHOD, state); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci if (result == TOS_FAILURE) 128762306a36Sopenharmony_ci pr_err("ACPI call to set Cooling Method failed\n"); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (result == TOS_NOT_SUPPORTED) 129062306a36Sopenharmony_ci return -ENODEV; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO; 129362306a36Sopenharmony_ci} 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci/* Battery charge control */ 129662306a36Sopenharmony_cistatic void toshiba_battery_charge_mode_available(struct toshiba_acpi_dev *dev) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_GET, HCI_BATTERY_CHARGE_MODE, 0, 0, 0, 0 }; 129962306a36Sopenharmony_ci u32 out[TCI_WORDS]; 130062306a36Sopenharmony_ci acpi_status status; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci dev->battery_charge_mode_supported = 0; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci status = tci_raw(dev, in, out); 130562306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 130662306a36Sopenharmony_ci pr_err("ACPI call to get Battery Charge Mode failed\n"); 130762306a36Sopenharmony_ci return; 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci if (out[0] != TOS_SUCCESS && out[0] != TOS_SUCCESS2) 131162306a36Sopenharmony_ci return; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci dev->battery_charge_mode_supported = 1; 131462306a36Sopenharmony_ci} 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_cistatic int toshiba_battery_charge_mode_get(struct toshiba_acpi_dev *dev, u32 *state) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_GET, HCI_BATTERY_CHARGE_MODE, 0, 0, 0, 0x1 }; 131962306a36Sopenharmony_ci u32 out[TCI_WORDS]; 132062306a36Sopenharmony_ci int retries = 3; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci do { 132362306a36Sopenharmony_ci acpi_status status = tci_raw(dev, in, out); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 132662306a36Sopenharmony_ci pr_err("ACPI call to get Battery Charge Mode failed\n"); 132762306a36Sopenharmony_ci switch (out[0]) { 132862306a36Sopenharmony_ci case TOS_SUCCESS: 132962306a36Sopenharmony_ci case TOS_SUCCESS2: 133062306a36Sopenharmony_ci *state = out[2]; 133162306a36Sopenharmony_ci return 0; 133262306a36Sopenharmony_ci case TOS_NOT_SUPPORTED: 133362306a36Sopenharmony_ci return -ENODEV; 133462306a36Sopenharmony_ci case TOS_DATA_NOT_AVAILABLE: 133562306a36Sopenharmony_ci retries--; 133662306a36Sopenharmony_ci break; 133762306a36Sopenharmony_ci default: 133862306a36Sopenharmony_ci return -EIO; 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci } while (retries); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci return -EIO; 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_cistatic int toshiba_battery_charge_mode_set(struct toshiba_acpi_dev *dev, u32 state) 134662306a36Sopenharmony_ci{ 134762306a36Sopenharmony_ci u32 result = hci_write(dev, HCI_BATTERY_CHARGE_MODE, state); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci if (result == TOS_FAILURE) 135062306a36Sopenharmony_ci pr_err("ACPI call to set Battery Charge Mode failed\n"); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (result == TOS_NOT_SUPPORTED) 135362306a36Sopenharmony_ci return -ENODEV; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO; 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci/* Transflective Backlight */ 135962306a36Sopenharmony_cistatic int get_tr_backlight_status(struct toshiba_acpi_dev *dev, u32 *status) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci u32 result = hci_read(dev, HCI_TR_BACKLIGHT, status); 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci if (result == TOS_FAILURE) 136462306a36Sopenharmony_ci pr_err("ACPI call to get Transflective Backlight failed\n"); 136562306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 136662306a36Sopenharmony_ci return -ENODEV; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 136962306a36Sopenharmony_ci} 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_cistatic int set_tr_backlight_status(struct toshiba_acpi_dev *dev, u32 status) 137262306a36Sopenharmony_ci{ 137362306a36Sopenharmony_ci u32 result = hci_write(dev, HCI_TR_BACKLIGHT, !status); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci if (result == TOS_FAILURE) 137662306a36Sopenharmony_ci pr_err("ACPI call to set Transflective Backlight failed\n"); 137762306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 137862306a36Sopenharmony_ci return -ENODEV; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 138162306a36Sopenharmony_ci} 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_cistatic struct proc_dir_entry *toshiba_proc_dir; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci/* LCD Brightness */ 138662306a36Sopenharmony_cistatic int __get_lcd_brightness(struct toshiba_acpi_dev *dev) 138762306a36Sopenharmony_ci{ 138862306a36Sopenharmony_ci int brightness = 0; 138962306a36Sopenharmony_ci u32 result; 139062306a36Sopenharmony_ci u32 value; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci if (dev->tr_backlight_supported) { 139362306a36Sopenharmony_ci int ret = get_tr_backlight_status(dev, &value); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci if (ret) 139662306a36Sopenharmony_ci return ret; 139762306a36Sopenharmony_ci if (value) 139862306a36Sopenharmony_ci return 0; 139962306a36Sopenharmony_ci brightness++; 140062306a36Sopenharmony_ci } 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci result = hci_read(dev, HCI_LCD_BRIGHTNESS, &value); 140362306a36Sopenharmony_ci if (result == TOS_FAILURE) 140462306a36Sopenharmony_ci pr_err("ACPI call to get LCD Brightness failed\n"); 140562306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 140662306a36Sopenharmony_ci return -ENODEV; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci return result == TOS_SUCCESS ? 140962306a36Sopenharmony_ci brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT) : 141062306a36Sopenharmony_ci -EIO; 141162306a36Sopenharmony_ci} 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_cistatic int get_lcd_brightness(struct backlight_device *bd) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = bl_get_data(bd); 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci return __get_lcd_brightness(dev); 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_cistatic int lcd_proc_show(struct seq_file *m, void *v) 142162306a36Sopenharmony_ci{ 142262306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = m->private; 142362306a36Sopenharmony_ci int levels; 142462306a36Sopenharmony_ci int value; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci if (!dev->backlight_dev) 142762306a36Sopenharmony_ci return -ENODEV; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci levels = dev->backlight_dev->props.max_brightness + 1; 143062306a36Sopenharmony_ci value = get_lcd_brightness(dev->backlight_dev); 143162306a36Sopenharmony_ci if (value < 0) { 143262306a36Sopenharmony_ci pr_err("Error reading LCD brightness\n"); 143362306a36Sopenharmony_ci return value; 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci seq_printf(m, "brightness: %d\n", value); 143762306a36Sopenharmony_ci seq_printf(m, "brightness_levels: %d\n", levels); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci return 0; 144062306a36Sopenharmony_ci} 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_cistatic int lcd_proc_open(struct inode *inode, struct file *file) 144362306a36Sopenharmony_ci{ 144462306a36Sopenharmony_ci return single_open(file, lcd_proc_show, pde_data(inode)); 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_cistatic int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value) 144862306a36Sopenharmony_ci{ 144962306a36Sopenharmony_ci u32 result; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci if (dev->tr_backlight_supported) { 145262306a36Sopenharmony_ci int ret = set_tr_backlight_status(dev, !value); 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci if (ret) 145562306a36Sopenharmony_ci return ret; 145662306a36Sopenharmony_ci if (value) 145762306a36Sopenharmony_ci value--; 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci value = value << HCI_LCD_BRIGHTNESS_SHIFT; 146162306a36Sopenharmony_ci result = hci_write(dev, HCI_LCD_BRIGHTNESS, value); 146262306a36Sopenharmony_ci if (result == TOS_FAILURE) 146362306a36Sopenharmony_ci pr_err("ACPI call to set LCD Brightness failed\n"); 146462306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 146562306a36Sopenharmony_ci return -ENODEV; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 146862306a36Sopenharmony_ci} 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_cistatic int set_lcd_status(struct backlight_device *bd) 147162306a36Sopenharmony_ci{ 147262306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = bl_get_data(bd); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci return set_lcd_brightness(dev, bd->props.brightness); 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_cistatic ssize_t lcd_proc_write(struct file *file, const char __user *buf, 147862306a36Sopenharmony_ci size_t count, loff_t *pos) 147962306a36Sopenharmony_ci{ 148062306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = pde_data(file_inode(file)); 148162306a36Sopenharmony_ci char cmd[42]; 148262306a36Sopenharmony_ci size_t len; 148362306a36Sopenharmony_ci int levels; 148462306a36Sopenharmony_ci int value; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci len = min(count, sizeof(cmd) - 1); 148762306a36Sopenharmony_ci if (copy_from_user(cmd, buf, len)) 148862306a36Sopenharmony_ci return -EFAULT; 148962306a36Sopenharmony_ci cmd[len] = '\0'; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci levels = dev->backlight_dev->props.max_brightness + 1; 149262306a36Sopenharmony_ci if (sscanf(cmd, " brightness : %i", &value) != 1 && 149362306a36Sopenharmony_ci value < 0 && value > levels) 149462306a36Sopenharmony_ci return -EINVAL; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci if (set_lcd_brightness(dev, value)) 149762306a36Sopenharmony_ci return -EIO; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci return count; 150062306a36Sopenharmony_ci} 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_cistatic const struct proc_ops lcd_proc_ops = { 150362306a36Sopenharmony_ci .proc_open = lcd_proc_open, 150462306a36Sopenharmony_ci .proc_read = seq_read, 150562306a36Sopenharmony_ci .proc_lseek = seq_lseek, 150662306a36Sopenharmony_ci .proc_release = single_release, 150762306a36Sopenharmony_ci .proc_write = lcd_proc_write, 150862306a36Sopenharmony_ci}; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci/* Video-Out */ 151162306a36Sopenharmony_cistatic int get_video_status(struct toshiba_acpi_dev *dev, u32 *status) 151262306a36Sopenharmony_ci{ 151362306a36Sopenharmony_ci u32 result = hci_read(dev, HCI_VIDEO_OUT, status); 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci if (result == TOS_FAILURE) 151662306a36Sopenharmony_ci pr_err("ACPI call to get Video-Out failed\n"); 151762306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 151862306a36Sopenharmony_ci return -ENODEV; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 152162306a36Sopenharmony_ci} 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_cistatic int video_proc_show(struct seq_file *m, void *v) 152462306a36Sopenharmony_ci{ 152562306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = m->private; 152662306a36Sopenharmony_ci int is_lcd, is_crt, is_tv; 152762306a36Sopenharmony_ci u32 value; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci if (get_video_status(dev, &value)) 153062306a36Sopenharmony_ci return -EIO; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; 153362306a36Sopenharmony_ci is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; 153462306a36Sopenharmony_ci is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0; 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci seq_printf(m, "lcd_out: %d\n", is_lcd); 153762306a36Sopenharmony_ci seq_printf(m, "crt_out: %d\n", is_crt); 153862306a36Sopenharmony_ci seq_printf(m, "tv_out: %d\n", is_tv); 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci return 0; 154162306a36Sopenharmony_ci} 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_cistatic int video_proc_open(struct inode *inode, struct file *file) 154462306a36Sopenharmony_ci{ 154562306a36Sopenharmony_ci return single_open(file, video_proc_show, pde_data(inode)); 154662306a36Sopenharmony_ci} 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_cistatic ssize_t video_proc_write(struct file *file, const char __user *buf, 154962306a36Sopenharmony_ci size_t count, loff_t *pos) 155062306a36Sopenharmony_ci{ 155162306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = pde_data(file_inode(file)); 155262306a36Sopenharmony_ci char *buffer; 155362306a36Sopenharmony_ci char *cmd; 155462306a36Sopenharmony_ci int lcd_out = -1, crt_out = -1, tv_out = -1; 155562306a36Sopenharmony_ci int remain = count; 155662306a36Sopenharmony_ci int value; 155762306a36Sopenharmony_ci int ret; 155862306a36Sopenharmony_ci u32 video_out; 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci cmd = memdup_user_nul(buf, count); 156162306a36Sopenharmony_ci if (IS_ERR(cmd)) 156262306a36Sopenharmony_ci return PTR_ERR(cmd); 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci buffer = cmd; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci /* 156762306a36Sopenharmony_ci * Scan expression. Multiple expressions may be delimited with ; 156862306a36Sopenharmony_ci * NOTE: To keep scanning simple, invalid fields are ignored. 156962306a36Sopenharmony_ci */ 157062306a36Sopenharmony_ci while (remain) { 157162306a36Sopenharmony_ci if (sscanf(buffer, " lcd_out : %i", &value) == 1) 157262306a36Sopenharmony_ci lcd_out = value & 1; 157362306a36Sopenharmony_ci else if (sscanf(buffer, " crt_out : %i", &value) == 1) 157462306a36Sopenharmony_ci crt_out = value & 1; 157562306a36Sopenharmony_ci else if (sscanf(buffer, " tv_out : %i", &value) == 1) 157662306a36Sopenharmony_ci tv_out = value & 1; 157762306a36Sopenharmony_ci /* Advance to one character past the next ; */ 157862306a36Sopenharmony_ci do { 157962306a36Sopenharmony_ci ++buffer; 158062306a36Sopenharmony_ci --remain; 158162306a36Sopenharmony_ci } while (remain && *(buffer - 1) != ';'); 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci kfree(cmd); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci ret = get_video_status(dev, &video_out); 158762306a36Sopenharmony_ci if (!ret) { 158862306a36Sopenharmony_ci unsigned int new_video_out = video_out; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci if (lcd_out != -1) 159162306a36Sopenharmony_ci _set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out); 159262306a36Sopenharmony_ci if (crt_out != -1) 159362306a36Sopenharmony_ci _set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out); 159462306a36Sopenharmony_ci if (tv_out != -1) 159562306a36Sopenharmony_ci _set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out); 159662306a36Sopenharmony_ci /* 159762306a36Sopenharmony_ci * To avoid unnecessary video disruption, only write the new 159862306a36Sopenharmony_ci * video setting if something changed. 159962306a36Sopenharmony_ci */ 160062306a36Sopenharmony_ci if (new_video_out != video_out) 160162306a36Sopenharmony_ci ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out); 160262306a36Sopenharmony_ci } 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci return ret ? -EIO : count; 160562306a36Sopenharmony_ci} 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_cistatic const struct proc_ops video_proc_ops = { 160862306a36Sopenharmony_ci .proc_open = video_proc_open, 160962306a36Sopenharmony_ci .proc_read = seq_read, 161062306a36Sopenharmony_ci .proc_lseek = seq_lseek, 161162306a36Sopenharmony_ci .proc_release = single_release, 161262306a36Sopenharmony_ci .proc_write = video_proc_write, 161362306a36Sopenharmony_ci}; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci/* Fan status */ 161662306a36Sopenharmony_cistatic int get_fan_status(struct toshiba_acpi_dev *dev, u32 *status) 161762306a36Sopenharmony_ci{ 161862306a36Sopenharmony_ci u32 result = hci_read(dev, HCI_FAN, status); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci if (result == TOS_FAILURE) 162162306a36Sopenharmony_ci pr_err("ACPI call to get Fan status failed\n"); 162262306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 162362306a36Sopenharmony_ci return -ENODEV; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 162662306a36Sopenharmony_ci} 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_cistatic int set_fan_status(struct toshiba_acpi_dev *dev, u32 status) 162962306a36Sopenharmony_ci{ 163062306a36Sopenharmony_ci u32 result = hci_write(dev, HCI_FAN, status); 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci if (result == TOS_FAILURE) 163362306a36Sopenharmony_ci pr_err("ACPI call to set Fan status failed\n"); 163462306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 163562306a36Sopenharmony_ci return -ENODEV; 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci return result == TOS_SUCCESS ? 0 : -EIO; 163862306a36Sopenharmony_ci} 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_cistatic int fan_proc_show(struct seq_file *m, void *v) 164162306a36Sopenharmony_ci{ 164262306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = m->private; 164362306a36Sopenharmony_ci u32 value; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci if (get_fan_status(dev, &value)) 164662306a36Sopenharmony_ci return -EIO; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci seq_printf(m, "running: %d\n", (value > 0)); 164962306a36Sopenharmony_ci seq_printf(m, "force_on: %d\n", dev->force_fan); 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci return 0; 165262306a36Sopenharmony_ci} 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_cistatic int fan_proc_open(struct inode *inode, struct file *file) 165562306a36Sopenharmony_ci{ 165662306a36Sopenharmony_ci return single_open(file, fan_proc_show, pde_data(inode)); 165762306a36Sopenharmony_ci} 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_cistatic ssize_t fan_proc_write(struct file *file, const char __user *buf, 166062306a36Sopenharmony_ci size_t count, loff_t *pos) 166162306a36Sopenharmony_ci{ 166262306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = pde_data(file_inode(file)); 166362306a36Sopenharmony_ci char cmd[42]; 166462306a36Sopenharmony_ci size_t len; 166562306a36Sopenharmony_ci int value; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci len = min(count, sizeof(cmd) - 1); 166862306a36Sopenharmony_ci if (copy_from_user(cmd, buf, len)) 166962306a36Sopenharmony_ci return -EFAULT; 167062306a36Sopenharmony_ci cmd[len] = '\0'; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci if (sscanf(cmd, " force_on : %i", &value) != 1 && 167362306a36Sopenharmony_ci value != 0 && value != 1) 167462306a36Sopenharmony_ci return -EINVAL; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci if (set_fan_status(dev, value)) 167762306a36Sopenharmony_ci return -EIO; 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci dev->force_fan = value; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci return count; 168262306a36Sopenharmony_ci} 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_cistatic const struct proc_ops fan_proc_ops = { 168562306a36Sopenharmony_ci .proc_open = fan_proc_open, 168662306a36Sopenharmony_ci .proc_read = seq_read, 168762306a36Sopenharmony_ci .proc_lseek = seq_lseek, 168862306a36Sopenharmony_ci .proc_release = single_release, 168962306a36Sopenharmony_ci .proc_write = fan_proc_write, 169062306a36Sopenharmony_ci}; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci/* Fan RPM */ 169362306a36Sopenharmony_cistatic int get_fan_rpm(struct toshiba_acpi_dev *dev, u32 *rpm) 169462306a36Sopenharmony_ci{ 169562306a36Sopenharmony_ci u32 in[TCI_WORDS] = { HCI_GET, HCI_FAN_RPM, 0, 1, 0, 0 }; 169662306a36Sopenharmony_ci u32 out[TCI_WORDS]; 169762306a36Sopenharmony_ci acpi_status status = tci_raw(dev, in, out); 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 170062306a36Sopenharmony_ci pr_err("ACPI call to get Fan speed failed\n"); 170162306a36Sopenharmony_ci return -EIO; 170262306a36Sopenharmony_ci } 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci if (out[0] == TOS_NOT_SUPPORTED) 170562306a36Sopenharmony_ci return -ENODEV; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci if (out[0] == TOS_SUCCESS) { 170862306a36Sopenharmony_ci *rpm = out[2]; 170962306a36Sopenharmony_ci return 0; 171062306a36Sopenharmony_ci } 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci return -EIO; 171362306a36Sopenharmony_ci} 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_cistatic int keys_proc_show(struct seq_file *m, void *v) 171662306a36Sopenharmony_ci{ 171762306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = m->private; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci seq_printf(m, "hotkey_ready: %d\n", dev->key_event_valid); 172062306a36Sopenharmony_ci seq_printf(m, "hotkey: 0x%04x\n", dev->last_key_event); 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci return 0; 172362306a36Sopenharmony_ci} 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_cistatic int keys_proc_open(struct inode *inode, struct file *file) 172662306a36Sopenharmony_ci{ 172762306a36Sopenharmony_ci return single_open(file, keys_proc_show, pde_data(inode)); 172862306a36Sopenharmony_ci} 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_cistatic ssize_t keys_proc_write(struct file *file, const char __user *buf, 173162306a36Sopenharmony_ci size_t count, loff_t *pos) 173262306a36Sopenharmony_ci{ 173362306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = pde_data(file_inode(file)); 173462306a36Sopenharmony_ci char cmd[42]; 173562306a36Sopenharmony_ci size_t len; 173662306a36Sopenharmony_ci int value; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci len = min(count, sizeof(cmd) - 1); 173962306a36Sopenharmony_ci if (copy_from_user(cmd, buf, len)) 174062306a36Sopenharmony_ci return -EFAULT; 174162306a36Sopenharmony_ci cmd[len] = '\0'; 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) 174462306a36Sopenharmony_ci dev->key_event_valid = 0; 174562306a36Sopenharmony_ci else 174662306a36Sopenharmony_ci return -EINVAL; 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci return count; 174962306a36Sopenharmony_ci} 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_cistatic const struct proc_ops keys_proc_ops = { 175262306a36Sopenharmony_ci .proc_open = keys_proc_open, 175362306a36Sopenharmony_ci .proc_read = seq_read, 175462306a36Sopenharmony_ci .proc_lseek = seq_lseek, 175562306a36Sopenharmony_ci .proc_release = single_release, 175662306a36Sopenharmony_ci .proc_write = keys_proc_write, 175762306a36Sopenharmony_ci}; 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_cistatic int __maybe_unused version_proc_show(struct seq_file *m, void *v) 176062306a36Sopenharmony_ci{ 176162306a36Sopenharmony_ci seq_printf(m, "driver: %s\n", TOSHIBA_ACPI_VERSION); 176262306a36Sopenharmony_ci seq_printf(m, "proc_interface: %d\n", PROC_INTERFACE_VERSION); 176362306a36Sopenharmony_ci return 0; 176462306a36Sopenharmony_ci} 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci/* 176762306a36Sopenharmony_ci * Proc and module init 176862306a36Sopenharmony_ci */ 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci#define PROC_TOSHIBA "toshiba" 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_cistatic void create_toshiba_proc_entries(struct toshiba_acpi_dev *dev) 177362306a36Sopenharmony_ci{ 177462306a36Sopenharmony_ci if (dev->backlight_dev) 177562306a36Sopenharmony_ci proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, 177662306a36Sopenharmony_ci &lcd_proc_ops, dev); 177762306a36Sopenharmony_ci if (dev->video_supported) 177862306a36Sopenharmony_ci proc_create_data("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, 177962306a36Sopenharmony_ci &video_proc_ops, dev); 178062306a36Sopenharmony_ci if (dev->fan_supported) 178162306a36Sopenharmony_ci proc_create_data("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, 178262306a36Sopenharmony_ci &fan_proc_ops, dev); 178362306a36Sopenharmony_ci if (dev->hotkey_dev) 178462306a36Sopenharmony_ci proc_create_data("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, 178562306a36Sopenharmony_ci &keys_proc_ops, dev); 178662306a36Sopenharmony_ci proc_create_single_data("version", S_IRUGO, toshiba_proc_dir, 178762306a36Sopenharmony_ci version_proc_show, dev); 178862306a36Sopenharmony_ci} 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_cistatic void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev) 179162306a36Sopenharmony_ci{ 179262306a36Sopenharmony_ci if (dev->backlight_dev) 179362306a36Sopenharmony_ci remove_proc_entry("lcd", toshiba_proc_dir); 179462306a36Sopenharmony_ci if (dev->video_supported) 179562306a36Sopenharmony_ci remove_proc_entry("video", toshiba_proc_dir); 179662306a36Sopenharmony_ci if (dev->fan_supported) 179762306a36Sopenharmony_ci remove_proc_entry("fan", toshiba_proc_dir); 179862306a36Sopenharmony_ci if (dev->hotkey_dev) 179962306a36Sopenharmony_ci remove_proc_entry("keys", toshiba_proc_dir); 180062306a36Sopenharmony_ci remove_proc_entry("version", toshiba_proc_dir); 180162306a36Sopenharmony_ci} 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_cistatic const struct backlight_ops toshiba_backlight_data = { 180462306a36Sopenharmony_ci .options = BL_CORE_SUSPENDRESUME, 180562306a36Sopenharmony_ci .get_brightness = get_lcd_brightness, 180662306a36Sopenharmony_ci .update_status = set_lcd_status, 180762306a36Sopenharmony_ci}; 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci/* Keyboard backlight work */ 181062306a36Sopenharmony_cistatic void toshiba_acpi_kbd_bl_work(struct work_struct *work); 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_cistatic DECLARE_WORK(kbd_bl_work, toshiba_acpi_kbd_bl_work); 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci/* 181562306a36Sopenharmony_ci * Sysfs files 181662306a36Sopenharmony_ci */ 181762306a36Sopenharmony_cistatic ssize_t version_show(struct device *dev, 181862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 181962306a36Sopenharmony_ci{ 182062306a36Sopenharmony_ci return sprintf(buf, "%s\n", TOSHIBA_ACPI_VERSION); 182162306a36Sopenharmony_ci} 182262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(version); 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_cistatic ssize_t fan_store(struct device *dev, 182562306a36Sopenharmony_ci struct device_attribute *attr, 182662306a36Sopenharmony_ci const char *buf, size_t count) 182762306a36Sopenharmony_ci{ 182862306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 182962306a36Sopenharmony_ci int state; 183062306a36Sopenharmony_ci int ret; 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &state); 183362306a36Sopenharmony_ci if (ret) 183462306a36Sopenharmony_ci return ret; 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci if (state != 0 && state != 1) 183762306a36Sopenharmony_ci return -EINVAL; 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci ret = set_fan_status(toshiba, state); 184062306a36Sopenharmony_ci if (ret) 184162306a36Sopenharmony_ci return ret; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci return count; 184462306a36Sopenharmony_ci} 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_cistatic ssize_t fan_show(struct device *dev, 184762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 184862306a36Sopenharmony_ci{ 184962306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 185062306a36Sopenharmony_ci u32 value; 185162306a36Sopenharmony_ci int ret; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci ret = get_fan_status(toshiba, &value); 185462306a36Sopenharmony_ci if (ret) 185562306a36Sopenharmony_ci return ret; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci return sprintf(buf, "%d\n", value); 185862306a36Sopenharmony_ci} 185962306a36Sopenharmony_cistatic DEVICE_ATTR_RW(fan); 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_cistatic ssize_t kbd_backlight_mode_store(struct device *dev, 186262306a36Sopenharmony_ci struct device_attribute *attr, 186362306a36Sopenharmony_ci const char *buf, size_t count) 186462306a36Sopenharmony_ci{ 186562306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 186662306a36Sopenharmony_ci int mode; 186762306a36Sopenharmony_ci int ret; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &mode); 187162306a36Sopenharmony_ci if (ret) 187262306a36Sopenharmony_ci return ret; 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci /* Check for supported modes depending on keyboard backlight type */ 187562306a36Sopenharmony_ci if (toshiba->kbd_type == 1) { 187662306a36Sopenharmony_ci /* Type 1 supports SCI_KBD_MODE_FNZ and SCI_KBD_MODE_AUTO */ 187762306a36Sopenharmony_ci if (mode != SCI_KBD_MODE_FNZ && mode != SCI_KBD_MODE_AUTO) 187862306a36Sopenharmony_ci return -EINVAL; 187962306a36Sopenharmony_ci } else if (toshiba->kbd_type == 2) { 188062306a36Sopenharmony_ci /* Type 2 doesn't support SCI_KBD_MODE_FNZ */ 188162306a36Sopenharmony_ci if (mode != SCI_KBD_MODE_AUTO && mode != SCI_KBD_MODE_ON && 188262306a36Sopenharmony_ci mode != SCI_KBD_MODE_OFF) 188362306a36Sopenharmony_ci return -EINVAL; 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci /* 188762306a36Sopenharmony_ci * Set the Keyboard Backlight Mode where: 188862306a36Sopenharmony_ci * Auto - KBD backlight turns off automatically in given time 188962306a36Sopenharmony_ci * FN-Z - KBD backlight "toggles" when hotkey pressed 189062306a36Sopenharmony_ci * ON - KBD backlight is always on 189162306a36Sopenharmony_ci * OFF - KBD backlight is always off 189262306a36Sopenharmony_ci */ 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci /* Only make a change if the actual mode has changed */ 189562306a36Sopenharmony_ci if (toshiba->kbd_mode != mode) { 189662306a36Sopenharmony_ci /* Shift the time to "base time" (0x3c0000 == 60 seconds) */ 189762306a36Sopenharmony_ci int time = toshiba->kbd_time << HCI_MISC_SHIFT; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci /* OR the "base time" to the actual method format */ 190062306a36Sopenharmony_ci if (toshiba->kbd_type == 1) { 190162306a36Sopenharmony_ci /* Type 1 requires the current mode */ 190262306a36Sopenharmony_ci time |= toshiba->kbd_mode; 190362306a36Sopenharmony_ci } else if (toshiba->kbd_type == 2) { 190462306a36Sopenharmony_ci /* Type 2 requires the desired mode */ 190562306a36Sopenharmony_ci time |= mode; 190662306a36Sopenharmony_ci } 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci ret = toshiba_kbd_illum_status_set(toshiba, time); 190962306a36Sopenharmony_ci if (ret) 191062306a36Sopenharmony_ci return ret; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci toshiba->kbd_mode = mode; 191362306a36Sopenharmony_ci toshiba_acpi->kbd_mode = mode; 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci /* 191662306a36Sopenharmony_ci * Some laptop models with the second generation backlit 191762306a36Sopenharmony_ci * keyboard (type 2) do not generate the keyboard backlight 191862306a36Sopenharmony_ci * changed event (0x92), and thus, the driver will never update 191962306a36Sopenharmony_ci * the sysfs entries. 192062306a36Sopenharmony_ci * 192162306a36Sopenharmony_ci * The event is generated right when changing the keyboard 192262306a36Sopenharmony_ci * backlight mode and the *notify function will set the 192362306a36Sopenharmony_ci * kbd_event_generated to true. 192462306a36Sopenharmony_ci * 192562306a36Sopenharmony_ci * In case the event is not generated, schedule the keyboard 192662306a36Sopenharmony_ci * backlight work to update the sysfs entries and emulate the 192762306a36Sopenharmony_ci * event via genetlink. 192862306a36Sopenharmony_ci */ 192962306a36Sopenharmony_ci if (toshiba->kbd_type == 2 && 193062306a36Sopenharmony_ci !toshiba->kbd_event_generated) 193162306a36Sopenharmony_ci schedule_work(&kbd_bl_work); 193262306a36Sopenharmony_ci } 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci return count; 193562306a36Sopenharmony_ci} 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_cistatic ssize_t kbd_backlight_mode_show(struct device *dev, 193862306a36Sopenharmony_ci struct device_attribute *attr, 193962306a36Sopenharmony_ci char *buf) 194062306a36Sopenharmony_ci{ 194162306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 194262306a36Sopenharmony_ci u32 time; 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) 194562306a36Sopenharmony_ci return -EIO; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci return sprintf(buf, "%i\n", time & SCI_KBD_MODE_MASK); 194862306a36Sopenharmony_ci} 194962306a36Sopenharmony_cistatic DEVICE_ATTR_RW(kbd_backlight_mode); 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_cistatic ssize_t kbd_type_show(struct device *dev, 195262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 195362306a36Sopenharmony_ci{ 195462306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci return sprintf(buf, "%d\n", toshiba->kbd_type); 195762306a36Sopenharmony_ci} 195862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(kbd_type); 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_cistatic ssize_t available_kbd_modes_show(struct device *dev, 196162306a36Sopenharmony_ci struct device_attribute *attr, 196262306a36Sopenharmony_ci char *buf) 196362306a36Sopenharmony_ci{ 196462306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci if (toshiba->kbd_type == 1) 196762306a36Sopenharmony_ci return sprintf(buf, "0x%x 0x%x\n", 196862306a36Sopenharmony_ci SCI_KBD_MODE_FNZ, SCI_KBD_MODE_AUTO); 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci return sprintf(buf, "0x%x 0x%x 0x%x\n", 197162306a36Sopenharmony_ci SCI_KBD_MODE_AUTO, SCI_KBD_MODE_ON, SCI_KBD_MODE_OFF); 197262306a36Sopenharmony_ci} 197362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(available_kbd_modes); 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_cistatic ssize_t kbd_backlight_timeout_store(struct device *dev, 197662306a36Sopenharmony_ci struct device_attribute *attr, 197762306a36Sopenharmony_ci const char *buf, size_t count) 197862306a36Sopenharmony_ci{ 197962306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 198062306a36Sopenharmony_ci int time; 198162306a36Sopenharmony_ci int ret; 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &time); 198462306a36Sopenharmony_ci if (ret) 198562306a36Sopenharmony_ci return ret; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci /* Check for supported values depending on kbd_type */ 198862306a36Sopenharmony_ci if (toshiba->kbd_type == 1) { 198962306a36Sopenharmony_ci if (time < 0 || time > 60) 199062306a36Sopenharmony_ci return -EINVAL; 199162306a36Sopenharmony_ci } else if (toshiba->kbd_type == 2) { 199262306a36Sopenharmony_ci if (time < 1 || time > 60) 199362306a36Sopenharmony_ci return -EINVAL; 199462306a36Sopenharmony_ci } 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci /* Set the Keyboard Backlight Timeout */ 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci /* Only make a change if the actual timeout has changed */ 199962306a36Sopenharmony_ci if (toshiba->kbd_time != time) { 200062306a36Sopenharmony_ci /* Shift the time to "base time" (0x3c0000 == 60 seconds) */ 200162306a36Sopenharmony_ci time = time << HCI_MISC_SHIFT; 200262306a36Sopenharmony_ci /* OR the "base time" to the actual method format */ 200362306a36Sopenharmony_ci if (toshiba->kbd_type == 1) 200462306a36Sopenharmony_ci time |= SCI_KBD_MODE_FNZ; 200562306a36Sopenharmony_ci else if (toshiba->kbd_type == 2) 200662306a36Sopenharmony_ci time |= SCI_KBD_MODE_AUTO; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci ret = toshiba_kbd_illum_status_set(toshiba, time); 200962306a36Sopenharmony_ci if (ret) 201062306a36Sopenharmony_ci return ret; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci toshiba->kbd_time = time >> HCI_MISC_SHIFT; 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci return count; 201662306a36Sopenharmony_ci} 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_cistatic ssize_t kbd_backlight_timeout_show(struct device *dev, 201962306a36Sopenharmony_ci struct device_attribute *attr, 202062306a36Sopenharmony_ci char *buf) 202162306a36Sopenharmony_ci{ 202262306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 202362306a36Sopenharmony_ci u32 time; 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) 202662306a36Sopenharmony_ci return -EIO; 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci return sprintf(buf, "%i\n", time >> HCI_MISC_SHIFT); 202962306a36Sopenharmony_ci} 203062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(kbd_backlight_timeout); 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_cistatic ssize_t touchpad_store(struct device *dev, 203362306a36Sopenharmony_ci struct device_attribute *attr, 203462306a36Sopenharmony_ci const char *buf, size_t count) 203562306a36Sopenharmony_ci{ 203662306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 203762306a36Sopenharmony_ci int state; 203862306a36Sopenharmony_ci int ret; 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci /* Set the TouchPad on/off, 0 - Disable | 1 - Enable */ 204162306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &state); 204262306a36Sopenharmony_ci if (ret) 204362306a36Sopenharmony_ci return ret; 204462306a36Sopenharmony_ci if (state != 0 && state != 1) 204562306a36Sopenharmony_ci return -EINVAL; 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci ret = toshiba_touchpad_set(toshiba, state); 204862306a36Sopenharmony_ci if (ret) 204962306a36Sopenharmony_ci return ret; 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci return count; 205262306a36Sopenharmony_ci} 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_cistatic ssize_t touchpad_show(struct device *dev, 205562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 205662306a36Sopenharmony_ci{ 205762306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 205862306a36Sopenharmony_ci u32 state; 205962306a36Sopenharmony_ci int ret; 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci ret = toshiba_touchpad_get(toshiba, &state); 206262306a36Sopenharmony_ci if (ret < 0) 206362306a36Sopenharmony_ci return ret; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci return sprintf(buf, "%i\n", state); 206662306a36Sopenharmony_ci} 206762306a36Sopenharmony_cistatic DEVICE_ATTR_RW(touchpad); 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_cistatic ssize_t usb_sleep_charge_show(struct device *dev, 207062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 207162306a36Sopenharmony_ci{ 207262306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 207362306a36Sopenharmony_ci u32 mode; 207462306a36Sopenharmony_ci int ret; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci ret = toshiba_usb_sleep_charge_get(toshiba, &mode); 207762306a36Sopenharmony_ci if (ret < 0) 207862306a36Sopenharmony_ci return ret; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci return sprintf(buf, "%x\n", mode & SCI_USB_CHARGE_MODE_MASK); 208162306a36Sopenharmony_ci} 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_cistatic ssize_t usb_sleep_charge_store(struct device *dev, 208462306a36Sopenharmony_ci struct device_attribute *attr, 208562306a36Sopenharmony_ci const char *buf, size_t count) 208662306a36Sopenharmony_ci{ 208762306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 208862306a36Sopenharmony_ci int state; 208962306a36Sopenharmony_ci u32 mode; 209062306a36Sopenharmony_ci int ret; 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &state); 209362306a36Sopenharmony_ci if (ret) 209462306a36Sopenharmony_ci return ret; 209562306a36Sopenharmony_ci /* 209662306a36Sopenharmony_ci * Check for supported values, where: 209762306a36Sopenharmony_ci * 0 - Disabled 209862306a36Sopenharmony_ci * 1 - Alternate (Non USB conformant devices that require more power) 209962306a36Sopenharmony_ci * 2 - Auto (USB conformant devices) 210062306a36Sopenharmony_ci * 3 - Typical 210162306a36Sopenharmony_ci */ 210262306a36Sopenharmony_ci if (state != 0 && state != 1 && state != 2 && state != 3) 210362306a36Sopenharmony_ci return -EINVAL; 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci /* Set the USB charging mode to internal value */ 210662306a36Sopenharmony_ci mode = toshiba->usbsc_mode_base; 210762306a36Sopenharmony_ci if (state == 0) 210862306a36Sopenharmony_ci mode |= SCI_USB_CHARGE_DISABLED; 210962306a36Sopenharmony_ci else if (state == 1) 211062306a36Sopenharmony_ci mode |= SCI_USB_CHARGE_ALTERNATE; 211162306a36Sopenharmony_ci else if (state == 2) 211262306a36Sopenharmony_ci mode |= SCI_USB_CHARGE_AUTO; 211362306a36Sopenharmony_ci else if (state == 3) 211462306a36Sopenharmony_ci mode |= SCI_USB_CHARGE_TYPICAL; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci ret = toshiba_usb_sleep_charge_set(toshiba, mode); 211762306a36Sopenharmony_ci if (ret) 211862306a36Sopenharmony_ci return ret; 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci return count; 212162306a36Sopenharmony_ci} 212262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(usb_sleep_charge); 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_cistatic ssize_t sleep_functions_on_battery_show(struct device *dev, 212562306a36Sopenharmony_ci struct device_attribute *attr, 212662306a36Sopenharmony_ci char *buf) 212762306a36Sopenharmony_ci{ 212862306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 212962306a36Sopenharmony_ci int bat_lvl, status; 213062306a36Sopenharmony_ci u32 state; 213162306a36Sopenharmony_ci int ret; 213262306a36Sopenharmony_ci int tmp; 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci ret = toshiba_sleep_functions_status_get(toshiba, &state); 213562306a36Sopenharmony_ci if (ret < 0) 213662306a36Sopenharmony_ci return ret; 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci /* Determine the status: 0x4 - Enabled | 0x1 - Disabled */ 213962306a36Sopenharmony_ci tmp = state & SCI_USB_CHARGE_BAT_MASK; 214062306a36Sopenharmony_ci status = (tmp == 0x4) ? 1 : 0; 214162306a36Sopenharmony_ci /* Determine the battery level set */ 214262306a36Sopenharmony_ci bat_lvl = state >> HCI_MISC_SHIFT; 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci return sprintf(buf, "%d %d\n", status, bat_lvl); 214562306a36Sopenharmony_ci} 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_cistatic ssize_t sleep_functions_on_battery_store(struct device *dev, 214862306a36Sopenharmony_ci struct device_attribute *attr, 214962306a36Sopenharmony_ci const char *buf, size_t count) 215062306a36Sopenharmony_ci{ 215162306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 215262306a36Sopenharmony_ci u32 status; 215362306a36Sopenharmony_ci int value; 215462306a36Sopenharmony_ci int ret; 215562306a36Sopenharmony_ci int tmp; 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &value); 215862306a36Sopenharmony_ci if (ret) 215962306a36Sopenharmony_ci return ret; 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci /* 216262306a36Sopenharmony_ci * Set the status of the function: 216362306a36Sopenharmony_ci * 0 - Disabled 216462306a36Sopenharmony_ci * 1-100 - Enabled 216562306a36Sopenharmony_ci */ 216662306a36Sopenharmony_ci if (value < 0 || value > 100) 216762306a36Sopenharmony_ci return -EINVAL; 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci if (value == 0) { 217062306a36Sopenharmony_ci tmp = toshiba->usbsc_bat_level << HCI_MISC_SHIFT; 217162306a36Sopenharmony_ci status = tmp | SCI_USB_CHARGE_BAT_LVL_OFF; 217262306a36Sopenharmony_ci } else { 217362306a36Sopenharmony_ci tmp = value << HCI_MISC_SHIFT; 217462306a36Sopenharmony_ci status = tmp | SCI_USB_CHARGE_BAT_LVL_ON; 217562306a36Sopenharmony_ci } 217662306a36Sopenharmony_ci ret = toshiba_sleep_functions_status_set(toshiba, status); 217762306a36Sopenharmony_ci if (ret < 0) 217862306a36Sopenharmony_ci return ret; 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci toshiba->usbsc_bat_level = status >> HCI_MISC_SHIFT; 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci return count; 218362306a36Sopenharmony_ci} 218462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(sleep_functions_on_battery); 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_cistatic ssize_t usb_rapid_charge_show(struct device *dev, 218762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 218862306a36Sopenharmony_ci{ 218962306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 219062306a36Sopenharmony_ci u32 state; 219162306a36Sopenharmony_ci int ret; 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci ret = toshiba_usb_rapid_charge_get(toshiba, &state); 219462306a36Sopenharmony_ci if (ret < 0) 219562306a36Sopenharmony_ci return ret; 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci return sprintf(buf, "%d\n", state); 219862306a36Sopenharmony_ci} 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_cistatic ssize_t usb_rapid_charge_store(struct device *dev, 220162306a36Sopenharmony_ci struct device_attribute *attr, 220262306a36Sopenharmony_ci const char *buf, size_t count) 220362306a36Sopenharmony_ci{ 220462306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 220562306a36Sopenharmony_ci int state; 220662306a36Sopenharmony_ci int ret; 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &state); 220962306a36Sopenharmony_ci if (ret) 221062306a36Sopenharmony_ci return ret; 221162306a36Sopenharmony_ci if (state != 0 && state != 1) 221262306a36Sopenharmony_ci return -EINVAL; 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci ret = toshiba_usb_rapid_charge_set(toshiba, state); 221562306a36Sopenharmony_ci if (ret) 221662306a36Sopenharmony_ci return ret; 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci return count; 221962306a36Sopenharmony_ci} 222062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(usb_rapid_charge); 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_cistatic ssize_t usb_sleep_music_show(struct device *dev, 222362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 222462306a36Sopenharmony_ci{ 222562306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 222662306a36Sopenharmony_ci u32 state; 222762306a36Sopenharmony_ci int ret; 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci ret = toshiba_usb_sleep_music_get(toshiba, &state); 223062306a36Sopenharmony_ci if (ret < 0) 223162306a36Sopenharmony_ci return ret; 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci return sprintf(buf, "%d\n", state); 223462306a36Sopenharmony_ci} 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_cistatic ssize_t usb_sleep_music_store(struct device *dev, 223762306a36Sopenharmony_ci struct device_attribute *attr, 223862306a36Sopenharmony_ci const char *buf, size_t count) 223962306a36Sopenharmony_ci{ 224062306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 224162306a36Sopenharmony_ci int state; 224262306a36Sopenharmony_ci int ret; 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &state); 224562306a36Sopenharmony_ci if (ret) 224662306a36Sopenharmony_ci return ret; 224762306a36Sopenharmony_ci if (state != 0 && state != 1) 224862306a36Sopenharmony_ci return -EINVAL; 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci ret = toshiba_usb_sleep_music_set(toshiba, state); 225162306a36Sopenharmony_ci if (ret) 225262306a36Sopenharmony_ci return ret; 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci return count; 225562306a36Sopenharmony_ci} 225662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(usb_sleep_music); 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_cistatic ssize_t kbd_function_keys_show(struct device *dev, 225962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 226062306a36Sopenharmony_ci{ 226162306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 226262306a36Sopenharmony_ci int mode; 226362306a36Sopenharmony_ci int ret; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci ret = toshiba_function_keys_get(toshiba, &mode); 226662306a36Sopenharmony_ci if (ret < 0) 226762306a36Sopenharmony_ci return ret; 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci return sprintf(buf, "%d\n", mode); 227062306a36Sopenharmony_ci} 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_cistatic ssize_t kbd_function_keys_store(struct device *dev, 227362306a36Sopenharmony_ci struct device_attribute *attr, 227462306a36Sopenharmony_ci const char *buf, size_t count) 227562306a36Sopenharmony_ci{ 227662306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 227762306a36Sopenharmony_ci int mode; 227862306a36Sopenharmony_ci int ret; 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &mode); 228162306a36Sopenharmony_ci if (ret) 228262306a36Sopenharmony_ci return ret; 228362306a36Sopenharmony_ci /* 228462306a36Sopenharmony_ci * Check for the function keys mode where: 228562306a36Sopenharmony_ci * 0 - Normal operation (F{1-12} as usual and hotkeys via FN-F{1-12}) 228662306a36Sopenharmony_ci * 1 - Special functions (Opposite of the above setting) 228762306a36Sopenharmony_ci */ 228862306a36Sopenharmony_ci if (mode != 0 && mode != 1) 228962306a36Sopenharmony_ci return -EINVAL; 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_ci ret = toshiba_function_keys_set(toshiba, mode); 229262306a36Sopenharmony_ci if (ret) 229362306a36Sopenharmony_ci return ret; 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci pr_info("Reboot for changes to KBD Function Keys to take effect"); 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci return count; 229862306a36Sopenharmony_ci} 229962306a36Sopenharmony_cistatic DEVICE_ATTR_RW(kbd_function_keys); 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_cistatic ssize_t panel_power_on_show(struct device *dev, 230262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 230362306a36Sopenharmony_ci{ 230462306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 230562306a36Sopenharmony_ci u32 state; 230662306a36Sopenharmony_ci int ret; 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci ret = toshiba_panel_power_on_get(toshiba, &state); 230962306a36Sopenharmony_ci if (ret < 0) 231062306a36Sopenharmony_ci return ret; 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci return sprintf(buf, "%d\n", state); 231362306a36Sopenharmony_ci} 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_cistatic ssize_t panel_power_on_store(struct device *dev, 231662306a36Sopenharmony_ci struct device_attribute *attr, 231762306a36Sopenharmony_ci const char *buf, size_t count) 231862306a36Sopenharmony_ci{ 231962306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 232062306a36Sopenharmony_ci int state; 232162306a36Sopenharmony_ci int ret; 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &state); 232462306a36Sopenharmony_ci if (ret) 232562306a36Sopenharmony_ci return ret; 232662306a36Sopenharmony_ci if (state != 0 && state != 1) 232762306a36Sopenharmony_ci return -EINVAL; 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci ret = toshiba_panel_power_on_set(toshiba, state); 233062306a36Sopenharmony_ci if (ret) 233162306a36Sopenharmony_ci return ret; 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci pr_info("Reboot for changes to Panel Power ON to take effect"); 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci return count; 233662306a36Sopenharmony_ci} 233762306a36Sopenharmony_cistatic DEVICE_ATTR_RW(panel_power_on); 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_cistatic ssize_t usb_three_show(struct device *dev, 234062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 234162306a36Sopenharmony_ci{ 234262306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 234362306a36Sopenharmony_ci u32 state; 234462306a36Sopenharmony_ci int ret; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci ret = toshiba_usb_three_get(toshiba, &state); 234762306a36Sopenharmony_ci if (ret < 0) 234862306a36Sopenharmony_ci return ret; 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci return sprintf(buf, "%d\n", state); 235162306a36Sopenharmony_ci} 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_cistatic ssize_t usb_three_store(struct device *dev, 235462306a36Sopenharmony_ci struct device_attribute *attr, 235562306a36Sopenharmony_ci const char *buf, size_t count) 235662306a36Sopenharmony_ci{ 235762306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 235862306a36Sopenharmony_ci int state; 235962306a36Sopenharmony_ci int ret; 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &state); 236262306a36Sopenharmony_ci if (ret) 236362306a36Sopenharmony_ci return ret; 236462306a36Sopenharmony_ci /* 236562306a36Sopenharmony_ci * Check for USB 3 mode where: 236662306a36Sopenharmony_ci * 0 - Disabled (Acts like a USB 2 port, saving power) 236762306a36Sopenharmony_ci * 1 - Enabled 236862306a36Sopenharmony_ci */ 236962306a36Sopenharmony_ci if (state != 0 && state != 1) 237062306a36Sopenharmony_ci return -EINVAL; 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci ret = toshiba_usb_three_set(toshiba, state); 237362306a36Sopenharmony_ci if (ret) 237462306a36Sopenharmony_ci return ret; 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci pr_info("Reboot for changes to USB 3 to take effect"); 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci return count; 237962306a36Sopenharmony_ci} 238062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(usb_three); 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_cistatic ssize_t cooling_method_show(struct device *dev, 238362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 238462306a36Sopenharmony_ci{ 238562306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 238662306a36Sopenharmony_ci int state; 238762306a36Sopenharmony_ci int ret; 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci ret = toshiba_cooling_method_get(toshiba, &state); 239062306a36Sopenharmony_ci if (ret < 0) 239162306a36Sopenharmony_ci return ret; 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci return sprintf(buf, "%d %d\n", state, toshiba->max_cooling_method); 239462306a36Sopenharmony_ci} 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_cistatic ssize_t cooling_method_store(struct device *dev, 239762306a36Sopenharmony_ci struct device_attribute *attr, 239862306a36Sopenharmony_ci const char *buf, size_t count) 239962306a36Sopenharmony_ci{ 240062306a36Sopenharmony_ci struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); 240162306a36Sopenharmony_ci int state; 240262306a36Sopenharmony_ci int ret; 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &state); 240562306a36Sopenharmony_ci if (ret) 240662306a36Sopenharmony_ci return ret; 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci /* 240962306a36Sopenharmony_ci * Check for supported values 241062306a36Sopenharmony_ci * Depending on the laptop model, some only support these two: 241162306a36Sopenharmony_ci * 0 - Maximum Performance 241262306a36Sopenharmony_ci * 1 - Battery Optimized 241362306a36Sopenharmony_ci * 241462306a36Sopenharmony_ci * While some others support all three methods: 241562306a36Sopenharmony_ci * 0 - Maximum Performance 241662306a36Sopenharmony_ci * 1 - Performance 241762306a36Sopenharmony_ci * 2 - Battery Optimized 241862306a36Sopenharmony_ci */ 241962306a36Sopenharmony_ci if (state < 0 || state > toshiba->max_cooling_method) 242062306a36Sopenharmony_ci return -EINVAL; 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci ret = toshiba_cooling_method_set(toshiba, state); 242362306a36Sopenharmony_ci if (ret) 242462306a36Sopenharmony_ci return ret; 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_ci return count; 242762306a36Sopenharmony_ci} 242862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(cooling_method); 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_cistatic struct attribute *toshiba_attributes[] = { 243162306a36Sopenharmony_ci &dev_attr_version.attr, 243262306a36Sopenharmony_ci &dev_attr_fan.attr, 243362306a36Sopenharmony_ci &dev_attr_kbd_backlight_mode.attr, 243462306a36Sopenharmony_ci &dev_attr_kbd_type.attr, 243562306a36Sopenharmony_ci &dev_attr_available_kbd_modes.attr, 243662306a36Sopenharmony_ci &dev_attr_kbd_backlight_timeout.attr, 243762306a36Sopenharmony_ci &dev_attr_touchpad.attr, 243862306a36Sopenharmony_ci &dev_attr_usb_sleep_charge.attr, 243962306a36Sopenharmony_ci &dev_attr_sleep_functions_on_battery.attr, 244062306a36Sopenharmony_ci &dev_attr_usb_rapid_charge.attr, 244162306a36Sopenharmony_ci &dev_attr_usb_sleep_music.attr, 244262306a36Sopenharmony_ci &dev_attr_kbd_function_keys.attr, 244362306a36Sopenharmony_ci &dev_attr_panel_power_on.attr, 244462306a36Sopenharmony_ci &dev_attr_usb_three.attr, 244562306a36Sopenharmony_ci &dev_attr_cooling_method.attr, 244662306a36Sopenharmony_ci NULL, 244762306a36Sopenharmony_ci}; 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_cistatic umode_t toshiba_sysfs_is_visible(struct kobject *kobj, 245062306a36Sopenharmony_ci struct attribute *attr, int idx) 245162306a36Sopenharmony_ci{ 245262306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 245362306a36Sopenharmony_ci struct toshiba_acpi_dev *drv = dev_get_drvdata(dev); 245462306a36Sopenharmony_ci bool exists = true; 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci if (attr == &dev_attr_fan.attr) 245762306a36Sopenharmony_ci exists = (drv->fan_supported) ? true : false; 245862306a36Sopenharmony_ci else if (attr == &dev_attr_kbd_backlight_mode.attr) 245962306a36Sopenharmony_ci exists = (drv->kbd_illum_supported) ? true : false; 246062306a36Sopenharmony_ci else if (attr == &dev_attr_kbd_backlight_timeout.attr) 246162306a36Sopenharmony_ci exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false; 246262306a36Sopenharmony_ci else if (attr == &dev_attr_touchpad.attr) 246362306a36Sopenharmony_ci exists = (drv->touchpad_supported) ? true : false; 246462306a36Sopenharmony_ci else if (attr == &dev_attr_usb_sleep_charge.attr) 246562306a36Sopenharmony_ci exists = (drv->usb_sleep_charge_supported) ? true : false; 246662306a36Sopenharmony_ci else if (attr == &dev_attr_sleep_functions_on_battery.attr) 246762306a36Sopenharmony_ci exists = (drv->usb_sleep_charge_supported) ? true : false; 246862306a36Sopenharmony_ci else if (attr == &dev_attr_usb_rapid_charge.attr) 246962306a36Sopenharmony_ci exists = (drv->usb_rapid_charge_supported) ? true : false; 247062306a36Sopenharmony_ci else if (attr == &dev_attr_usb_sleep_music.attr) 247162306a36Sopenharmony_ci exists = (drv->usb_sleep_music_supported) ? true : false; 247262306a36Sopenharmony_ci else if (attr == &dev_attr_kbd_function_keys.attr) 247362306a36Sopenharmony_ci exists = (drv->kbd_function_keys_supported) ? true : false; 247462306a36Sopenharmony_ci else if (attr == &dev_attr_panel_power_on.attr) 247562306a36Sopenharmony_ci exists = (drv->panel_power_on_supported) ? true : false; 247662306a36Sopenharmony_ci else if (attr == &dev_attr_usb_three.attr) 247762306a36Sopenharmony_ci exists = (drv->usb_three_supported) ? true : false; 247862306a36Sopenharmony_ci else if (attr == &dev_attr_cooling_method.attr) 247962306a36Sopenharmony_ci exists = (drv->cooling_method_supported) ? true : false; 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci return exists ? attr->mode : 0; 248262306a36Sopenharmony_ci} 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_cistatic const struct attribute_group toshiba_attr_group = { 248562306a36Sopenharmony_ci .is_visible = toshiba_sysfs_is_visible, 248662306a36Sopenharmony_ci .attrs = toshiba_attributes, 248762306a36Sopenharmony_ci}; 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_cistatic void toshiba_acpi_kbd_bl_work(struct work_struct *work) 249062306a36Sopenharmony_ci{ 249162306a36Sopenharmony_ci /* Update the sysfs entries */ 249262306a36Sopenharmony_ci if (sysfs_update_group(&toshiba_acpi->acpi_dev->dev.kobj, 249362306a36Sopenharmony_ci &toshiba_attr_group)) 249462306a36Sopenharmony_ci pr_err("Unable to update sysfs entries\n"); 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci /* Notify LED subsystem about keyboard backlight change */ 249762306a36Sopenharmony_ci if (toshiba_acpi->kbd_type == 2 && 249862306a36Sopenharmony_ci toshiba_acpi->kbd_mode != SCI_KBD_MODE_AUTO) 249962306a36Sopenharmony_ci led_classdev_notify_brightness_hw_changed(&toshiba_acpi->kbd_led, 250062306a36Sopenharmony_ci (toshiba_acpi->kbd_mode == SCI_KBD_MODE_ON) ? 250162306a36Sopenharmony_ci LED_FULL : LED_OFF); 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci /* Emulate the keyboard backlight event */ 250462306a36Sopenharmony_ci acpi_bus_generate_netlink_event(toshiba_acpi->acpi_dev->pnp.device_class, 250562306a36Sopenharmony_ci dev_name(&toshiba_acpi->acpi_dev->dev), 250662306a36Sopenharmony_ci 0x92, 0); 250762306a36Sopenharmony_ci} 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci/* 251062306a36Sopenharmony_ci * IIO device 251162306a36Sopenharmony_ci */ 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_cienum toshiba_iio_accel_chan { 251462306a36Sopenharmony_ci AXIS_X, 251562306a36Sopenharmony_ci AXIS_Y, 251662306a36Sopenharmony_ci AXIS_Z 251762306a36Sopenharmony_ci}; 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_cistatic int toshiba_iio_accel_get_axis(enum toshiba_iio_accel_chan chan) 252062306a36Sopenharmony_ci{ 252162306a36Sopenharmony_ci u32 xyval, zval; 252262306a36Sopenharmony_ci int ret; 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci ret = toshiba_accelerometer_get(toshiba_acpi, &xyval, &zval); 252562306a36Sopenharmony_ci if (ret < 0) 252662306a36Sopenharmony_ci return ret; 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci switch (chan) { 252962306a36Sopenharmony_ci case AXIS_X: 253062306a36Sopenharmony_ci return xyval & HCI_ACCEL_DIRECTION_MASK ? 253162306a36Sopenharmony_ci -(xyval & HCI_ACCEL_MASK) : xyval & HCI_ACCEL_MASK; 253262306a36Sopenharmony_ci case AXIS_Y: 253362306a36Sopenharmony_ci return (xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_DIRECTION_MASK ? 253462306a36Sopenharmony_ci -((xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_MASK) : 253562306a36Sopenharmony_ci (xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_MASK; 253662306a36Sopenharmony_ci case AXIS_Z: 253762306a36Sopenharmony_ci return zval & HCI_ACCEL_DIRECTION_MASK ? 253862306a36Sopenharmony_ci -(zval & HCI_ACCEL_MASK) : zval & HCI_ACCEL_MASK; 253962306a36Sopenharmony_ci } 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci return ret; 254262306a36Sopenharmony_ci} 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_cistatic int toshiba_iio_accel_read_raw(struct iio_dev *indio_dev, 254562306a36Sopenharmony_ci struct iio_chan_spec const *chan, 254662306a36Sopenharmony_ci int *val, int *val2, long mask) 254762306a36Sopenharmony_ci{ 254862306a36Sopenharmony_ci int ret; 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci switch (mask) { 255162306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 255262306a36Sopenharmony_ci ret = toshiba_iio_accel_get_axis(chan->channel); 255362306a36Sopenharmony_ci if (ret == -EIO || ret == -ENODEV) 255462306a36Sopenharmony_ci return ret; 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ci *val = ret; 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci return IIO_VAL_INT; 255962306a36Sopenharmony_ci } 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci return -EINVAL; 256262306a36Sopenharmony_ci} 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci#define TOSHIBA_IIO_ACCEL_CHANNEL(axis, chan) { \ 256562306a36Sopenharmony_ci .type = IIO_ACCEL, \ 256662306a36Sopenharmony_ci .modified = 1, \ 256762306a36Sopenharmony_ci .channel = chan, \ 256862306a36Sopenharmony_ci .channel2 = IIO_MOD_##axis, \ 256962306a36Sopenharmony_ci .output = 1, \ 257062306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 257162306a36Sopenharmony_ci} 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_cistatic const struct iio_chan_spec toshiba_iio_accel_channels[] = { 257462306a36Sopenharmony_ci TOSHIBA_IIO_ACCEL_CHANNEL(X, AXIS_X), 257562306a36Sopenharmony_ci TOSHIBA_IIO_ACCEL_CHANNEL(Y, AXIS_Y), 257662306a36Sopenharmony_ci TOSHIBA_IIO_ACCEL_CHANNEL(Z, AXIS_Z), 257762306a36Sopenharmony_ci}; 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_cistatic const struct iio_info toshiba_iio_accel_info = { 258062306a36Sopenharmony_ci .read_raw = &toshiba_iio_accel_read_raw, 258162306a36Sopenharmony_ci}; 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci/* 258462306a36Sopenharmony_ci * Misc device 258562306a36Sopenharmony_ci */ 258662306a36Sopenharmony_cistatic int toshiba_acpi_smm_bridge(SMMRegisters *regs) 258762306a36Sopenharmony_ci{ 258862306a36Sopenharmony_ci u32 in[TCI_WORDS] = { regs->eax, regs->ebx, regs->ecx, 258962306a36Sopenharmony_ci regs->edx, regs->esi, regs->edi }; 259062306a36Sopenharmony_ci u32 out[TCI_WORDS]; 259162306a36Sopenharmony_ci acpi_status status; 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci status = tci_raw(toshiba_acpi, in, out); 259462306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 259562306a36Sopenharmony_ci pr_err("ACPI call to query SMM registers failed\n"); 259662306a36Sopenharmony_ci return -EIO; 259762306a36Sopenharmony_ci } 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci /* Fillout the SMM struct with the TCI call results */ 260062306a36Sopenharmony_ci regs->eax = out[0]; 260162306a36Sopenharmony_ci regs->ebx = out[1]; 260262306a36Sopenharmony_ci regs->ecx = out[2]; 260362306a36Sopenharmony_ci regs->edx = out[3]; 260462306a36Sopenharmony_ci regs->esi = out[4]; 260562306a36Sopenharmony_ci regs->edi = out[5]; 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_ci return 0; 260862306a36Sopenharmony_ci} 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_cistatic long toshiba_acpi_ioctl(struct file *fp, unsigned int cmd, 261162306a36Sopenharmony_ci unsigned long arg) 261262306a36Sopenharmony_ci{ 261362306a36Sopenharmony_ci SMMRegisters __user *argp = (SMMRegisters __user *)arg; 261462306a36Sopenharmony_ci SMMRegisters regs; 261562306a36Sopenharmony_ci int ret; 261662306a36Sopenharmony_ci 261762306a36Sopenharmony_ci if (!argp) 261862306a36Sopenharmony_ci return -EINVAL; 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci switch (cmd) { 262162306a36Sopenharmony_ci case TOSH_SMM: 262262306a36Sopenharmony_ci if (copy_from_user(®s, argp, sizeof(SMMRegisters))) 262362306a36Sopenharmony_ci return -EFAULT; 262462306a36Sopenharmony_ci ret = toshiba_acpi_smm_bridge(®s); 262562306a36Sopenharmony_ci if (ret) 262662306a36Sopenharmony_ci return ret; 262762306a36Sopenharmony_ci if (copy_to_user(argp, ®s, sizeof(SMMRegisters))) 262862306a36Sopenharmony_ci return -EFAULT; 262962306a36Sopenharmony_ci break; 263062306a36Sopenharmony_ci case TOSHIBA_ACPI_SCI: 263162306a36Sopenharmony_ci if (copy_from_user(®s, argp, sizeof(SMMRegisters))) 263262306a36Sopenharmony_ci return -EFAULT; 263362306a36Sopenharmony_ci /* Ensure we are being called with a SCI_{GET, SET} register */ 263462306a36Sopenharmony_ci if (regs.eax != SCI_GET && regs.eax != SCI_SET) 263562306a36Sopenharmony_ci return -EINVAL; 263662306a36Sopenharmony_ci if (!sci_open(toshiba_acpi)) 263762306a36Sopenharmony_ci return -EIO; 263862306a36Sopenharmony_ci ret = toshiba_acpi_smm_bridge(®s); 263962306a36Sopenharmony_ci sci_close(toshiba_acpi); 264062306a36Sopenharmony_ci if (ret) 264162306a36Sopenharmony_ci return ret; 264262306a36Sopenharmony_ci if (copy_to_user(argp, ®s, sizeof(SMMRegisters))) 264362306a36Sopenharmony_ci return -EFAULT; 264462306a36Sopenharmony_ci break; 264562306a36Sopenharmony_ci default: 264662306a36Sopenharmony_ci return -EINVAL; 264762306a36Sopenharmony_ci } 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci return 0; 265062306a36Sopenharmony_ci} 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_cistatic const struct file_operations toshiba_acpi_fops = { 265362306a36Sopenharmony_ci .owner = THIS_MODULE, 265462306a36Sopenharmony_ci .unlocked_ioctl = toshiba_acpi_ioctl, 265562306a36Sopenharmony_ci .llseek = noop_llseek, 265662306a36Sopenharmony_ci}; 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci/* 265962306a36Sopenharmony_ci * WWAN RFKill handlers 266062306a36Sopenharmony_ci */ 266162306a36Sopenharmony_cistatic int toshiba_acpi_wwan_set_block(void *data, bool blocked) 266262306a36Sopenharmony_ci{ 266362306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = data; 266462306a36Sopenharmony_ci int ret; 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci ret = toshiba_wireless_status(dev); 266762306a36Sopenharmony_ci if (ret) 266862306a36Sopenharmony_ci return ret; 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci if (!dev->killswitch) 267162306a36Sopenharmony_ci return 0; 267262306a36Sopenharmony_ci 267362306a36Sopenharmony_ci return toshiba_wwan_set(dev, !blocked); 267462306a36Sopenharmony_ci} 267562306a36Sopenharmony_ci 267662306a36Sopenharmony_cistatic void toshiba_acpi_wwan_poll(struct rfkill *rfkill, void *data) 267762306a36Sopenharmony_ci{ 267862306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = data; 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci if (toshiba_wireless_status(dev)) 268162306a36Sopenharmony_ci return; 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_ci rfkill_set_hw_state(dev->wwan_rfk, !dev->killswitch); 268462306a36Sopenharmony_ci} 268562306a36Sopenharmony_ci 268662306a36Sopenharmony_cistatic const struct rfkill_ops wwan_rfk_ops = { 268762306a36Sopenharmony_ci .set_block = toshiba_acpi_wwan_set_block, 268862306a36Sopenharmony_ci .poll = toshiba_acpi_wwan_poll, 268962306a36Sopenharmony_ci}; 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_cistatic int toshiba_acpi_setup_wwan_rfkill(struct toshiba_acpi_dev *dev) 269262306a36Sopenharmony_ci{ 269362306a36Sopenharmony_ci int ret = toshiba_wireless_status(dev); 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci if (ret) 269662306a36Sopenharmony_ci return ret; 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci dev->wwan_rfk = rfkill_alloc("Toshiba WWAN", 269962306a36Sopenharmony_ci &dev->acpi_dev->dev, 270062306a36Sopenharmony_ci RFKILL_TYPE_WWAN, 270162306a36Sopenharmony_ci &wwan_rfk_ops, 270262306a36Sopenharmony_ci dev); 270362306a36Sopenharmony_ci if (!dev->wwan_rfk) { 270462306a36Sopenharmony_ci pr_err("Unable to allocate WWAN rfkill device\n"); 270562306a36Sopenharmony_ci return -ENOMEM; 270662306a36Sopenharmony_ci } 270762306a36Sopenharmony_ci 270862306a36Sopenharmony_ci rfkill_set_hw_state(dev->wwan_rfk, !dev->killswitch); 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_ci ret = rfkill_register(dev->wwan_rfk); 271162306a36Sopenharmony_ci if (ret) { 271262306a36Sopenharmony_ci pr_err("Unable to register WWAN rfkill device\n"); 271362306a36Sopenharmony_ci rfkill_destroy(dev->wwan_rfk); 271462306a36Sopenharmony_ci } 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci return ret; 271762306a36Sopenharmony_ci} 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_ci/* 272062306a36Sopenharmony_ci * Hotkeys 272162306a36Sopenharmony_ci */ 272262306a36Sopenharmony_cistatic int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev) 272362306a36Sopenharmony_ci{ 272462306a36Sopenharmony_ci acpi_status status; 272562306a36Sopenharmony_ci u32 result; 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci status = acpi_evaluate_object(dev->acpi_dev->handle, 272862306a36Sopenharmony_ci "ENAB", NULL, NULL); 272962306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 273062306a36Sopenharmony_ci return -ENODEV; 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci /* 273362306a36Sopenharmony_ci * Enable the "Special Functions" mode only if they are 273462306a36Sopenharmony_ci * supported and if they are activated. 273562306a36Sopenharmony_ci */ 273662306a36Sopenharmony_ci if (dev->kbd_function_keys_supported && dev->special_functions) 273762306a36Sopenharmony_ci result = hci_write(dev, HCI_HOTKEY_EVENT, 273862306a36Sopenharmony_ci HCI_HOTKEY_SPECIAL_FUNCTIONS); 273962306a36Sopenharmony_ci else 274062306a36Sopenharmony_ci result = hci_write(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE); 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_ci if (result == TOS_FAILURE) 274362306a36Sopenharmony_ci return -EIO; 274462306a36Sopenharmony_ci else if (result == TOS_NOT_SUPPORTED) 274562306a36Sopenharmony_ci return -ENODEV; 274662306a36Sopenharmony_ci 274762306a36Sopenharmony_ci return 0; 274862306a36Sopenharmony_ci} 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_cistatic bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, 275162306a36Sopenharmony_ci struct serio *port) 275262306a36Sopenharmony_ci{ 275362306a36Sopenharmony_ci if (str & I8042_STR_AUXDATA) 275462306a36Sopenharmony_ci return false; 275562306a36Sopenharmony_ci 275662306a36Sopenharmony_ci if (unlikely(data == 0xe0)) 275762306a36Sopenharmony_ci return false; 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci if ((data & 0x7f) == TOS1900_FN_SCAN) { 276062306a36Sopenharmony_ci schedule_work(&toshiba_acpi->hotkey_work); 276162306a36Sopenharmony_ci return true; 276262306a36Sopenharmony_ci } 276362306a36Sopenharmony_ci 276462306a36Sopenharmony_ci return false; 276562306a36Sopenharmony_ci} 276662306a36Sopenharmony_ci 276762306a36Sopenharmony_cistatic void toshiba_acpi_hotkey_work(struct work_struct *work) 276862306a36Sopenharmony_ci{ 276962306a36Sopenharmony_ci acpi_handle ec_handle = ec_get_handle(); 277062306a36Sopenharmony_ci acpi_status status; 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ci if (!ec_handle) 277362306a36Sopenharmony_ci return; 277462306a36Sopenharmony_ci 277562306a36Sopenharmony_ci status = acpi_evaluate_object(ec_handle, "NTFY", NULL, NULL); 277662306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 277762306a36Sopenharmony_ci pr_err("ACPI NTFY method execution failed\n"); 277862306a36Sopenharmony_ci} 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci/* 278162306a36Sopenharmony_ci * Returns hotkey scancode, or < 0 on failure. 278262306a36Sopenharmony_ci */ 278362306a36Sopenharmony_cistatic int toshiba_acpi_query_hotkey(struct toshiba_acpi_dev *dev) 278462306a36Sopenharmony_ci{ 278562306a36Sopenharmony_ci unsigned long long value; 278662306a36Sopenharmony_ci acpi_status status; 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_ci status = acpi_evaluate_integer(dev->acpi_dev->handle, "INFO", 278962306a36Sopenharmony_ci NULL, &value); 279062306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 279162306a36Sopenharmony_ci pr_err("ACPI INFO method execution failed\n"); 279262306a36Sopenharmony_ci return -EIO; 279362306a36Sopenharmony_ci } 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_ci return value; 279662306a36Sopenharmony_ci} 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_cistatic void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev, 279962306a36Sopenharmony_ci int scancode) 280062306a36Sopenharmony_ci{ 280162306a36Sopenharmony_ci if (scancode == 0x100) 280262306a36Sopenharmony_ci return; 280362306a36Sopenharmony_ci 280462306a36Sopenharmony_ci /* Act on key press; ignore key release */ 280562306a36Sopenharmony_ci if (scancode & 0x80) 280662306a36Sopenharmony_ci return; 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci if (!sparse_keymap_report_event(dev->hotkey_dev, scancode, 1, true)) 280962306a36Sopenharmony_ci pr_info("Unknown key %x\n", scancode); 281062306a36Sopenharmony_ci} 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_cistatic void toshiba_acpi_process_hotkeys(struct toshiba_acpi_dev *dev) 281362306a36Sopenharmony_ci{ 281462306a36Sopenharmony_ci if (dev->info_supported) { 281562306a36Sopenharmony_ci int scancode = toshiba_acpi_query_hotkey(dev); 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci if (scancode < 0) { 281862306a36Sopenharmony_ci pr_err("Failed to query hotkey event\n"); 281962306a36Sopenharmony_ci } else if (scancode != 0) { 282062306a36Sopenharmony_ci toshiba_acpi_report_hotkey(dev, scancode); 282162306a36Sopenharmony_ci dev->key_event_valid = 1; 282262306a36Sopenharmony_ci dev->last_key_event = scancode; 282362306a36Sopenharmony_ci } 282462306a36Sopenharmony_ci } else if (dev->system_event_supported) { 282562306a36Sopenharmony_ci u32 result; 282662306a36Sopenharmony_ci u32 value; 282762306a36Sopenharmony_ci int retries = 3; 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci do { 283062306a36Sopenharmony_ci result = hci_read(dev, HCI_SYSTEM_EVENT, &value); 283162306a36Sopenharmony_ci switch (result) { 283262306a36Sopenharmony_ci case TOS_SUCCESS: 283362306a36Sopenharmony_ci toshiba_acpi_report_hotkey(dev, (int)value); 283462306a36Sopenharmony_ci dev->key_event_valid = 1; 283562306a36Sopenharmony_ci dev->last_key_event = value; 283662306a36Sopenharmony_ci break; 283762306a36Sopenharmony_ci case TOS_NOT_SUPPORTED: 283862306a36Sopenharmony_ci /* 283962306a36Sopenharmony_ci * This is a workaround for an unresolved 284062306a36Sopenharmony_ci * issue on some machines where system events 284162306a36Sopenharmony_ci * sporadically become disabled. 284262306a36Sopenharmony_ci */ 284362306a36Sopenharmony_ci result = hci_write(dev, HCI_SYSTEM_EVENT, 1); 284462306a36Sopenharmony_ci if (result == TOS_SUCCESS) 284562306a36Sopenharmony_ci pr_notice("Re-enabled hotkeys\n"); 284662306a36Sopenharmony_ci fallthrough; 284762306a36Sopenharmony_ci default: 284862306a36Sopenharmony_ci retries--; 284962306a36Sopenharmony_ci break; 285062306a36Sopenharmony_ci } 285162306a36Sopenharmony_ci } while (retries && result != TOS_FIFO_EMPTY); 285262306a36Sopenharmony_ci } 285362306a36Sopenharmony_ci} 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_cistatic int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) 285662306a36Sopenharmony_ci{ 285762306a36Sopenharmony_ci const struct key_entry *keymap = toshiba_acpi_keymap; 285862306a36Sopenharmony_ci acpi_handle ec_handle; 285962306a36Sopenharmony_ci int error; 286062306a36Sopenharmony_ci 286162306a36Sopenharmony_ci if (disable_hotkeys) { 286262306a36Sopenharmony_ci pr_info("Hotkeys disabled by module parameter\n"); 286362306a36Sopenharmony_ci return 0; 286462306a36Sopenharmony_ci } 286562306a36Sopenharmony_ci 286662306a36Sopenharmony_ci if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) { 286762306a36Sopenharmony_ci pr_info("WMI event detected, hotkeys will not be monitored\n"); 286862306a36Sopenharmony_ci return 0; 286962306a36Sopenharmony_ci } 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ci error = toshiba_acpi_enable_hotkeys(dev); 287262306a36Sopenharmony_ci if (error) 287362306a36Sopenharmony_ci return error; 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci if (toshiba_hotkey_event_type_get(dev, &dev->hotkey_event_type)) 287662306a36Sopenharmony_ci pr_notice("Unable to query Hotkey Event Type\n"); 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci dev->hotkey_dev = input_allocate_device(); 287962306a36Sopenharmony_ci if (!dev->hotkey_dev) 288062306a36Sopenharmony_ci return -ENOMEM; 288162306a36Sopenharmony_ci 288262306a36Sopenharmony_ci dev->hotkey_dev->name = "Toshiba input device"; 288362306a36Sopenharmony_ci dev->hotkey_dev->phys = "toshiba_acpi/input0"; 288462306a36Sopenharmony_ci dev->hotkey_dev->id.bustype = BUS_HOST; 288562306a36Sopenharmony_ci dev->hotkey_dev->dev.parent = &dev->acpi_dev->dev; 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ci if (dev->hotkey_event_type == HCI_SYSTEM_TYPE1 || 288862306a36Sopenharmony_ci !dev->kbd_function_keys_supported) 288962306a36Sopenharmony_ci keymap = toshiba_acpi_keymap; 289062306a36Sopenharmony_ci else if (dev->hotkey_event_type == HCI_SYSTEM_TYPE2 || 289162306a36Sopenharmony_ci dev->kbd_function_keys_supported) 289262306a36Sopenharmony_ci keymap = toshiba_acpi_alt_keymap; 289362306a36Sopenharmony_ci else 289462306a36Sopenharmony_ci pr_info("Unknown event type received %x\n", 289562306a36Sopenharmony_ci dev->hotkey_event_type); 289662306a36Sopenharmony_ci error = sparse_keymap_setup(dev->hotkey_dev, keymap, NULL); 289762306a36Sopenharmony_ci if (error) 289862306a36Sopenharmony_ci goto err_free_dev; 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_ci /* 290162306a36Sopenharmony_ci * For some machines the SCI responsible for providing hotkey 290262306a36Sopenharmony_ci * notification doesn't fire. We can trigger the notification 290362306a36Sopenharmony_ci * whenever the Fn key is pressed using the NTFY method, if 290462306a36Sopenharmony_ci * supported, so if it's present set up an i8042 key filter 290562306a36Sopenharmony_ci * for this purpose. 290662306a36Sopenharmony_ci */ 290762306a36Sopenharmony_ci ec_handle = ec_get_handle(); 290862306a36Sopenharmony_ci if (ec_handle && acpi_has_method(ec_handle, "NTFY")) { 290962306a36Sopenharmony_ci INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work); 291062306a36Sopenharmony_ci 291162306a36Sopenharmony_ci error = i8042_install_filter(toshiba_acpi_i8042_filter); 291262306a36Sopenharmony_ci if (error) { 291362306a36Sopenharmony_ci pr_err("Error installing key filter\n"); 291462306a36Sopenharmony_ci goto err_free_dev; 291562306a36Sopenharmony_ci } 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_ci dev->ntfy_supported = 1; 291862306a36Sopenharmony_ci } 291962306a36Sopenharmony_ci 292062306a36Sopenharmony_ci /* 292162306a36Sopenharmony_ci * Determine hotkey query interface. Prefer using the INFO 292262306a36Sopenharmony_ci * method when it is available. 292362306a36Sopenharmony_ci */ 292462306a36Sopenharmony_ci if (acpi_has_method(dev->acpi_dev->handle, "INFO")) 292562306a36Sopenharmony_ci dev->info_supported = 1; 292662306a36Sopenharmony_ci else if (hci_write(dev, HCI_SYSTEM_EVENT, 1) == TOS_SUCCESS) 292762306a36Sopenharmony_ci dev->system_event_supported = 1; 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_ci if (!dev->info_supported && !dev->system_event_supported) { 293062306a36Sopenharmony_ci pr_warn("No hotkey query interface found\n"); 293162306a36Sopenharmony_ci error = -EINVAL; 293262306a36Sopenharmony_ci goto err_remove_filter; 293362306a36Sopenharmony_ci } 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_ci error = input_register_device(dev->hotkey_dev); 293662306a36Sopenharmony_ci if (error) { 293762306a36Sopenharmony_ci pr_info("Unable to register input device\n"); 293862306a36Sopenharmony_ci goto err_remove_filter; 293962306a36Sopenharmony_ci } 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ci return 0; 294262306a36Sopenharmony_ci 294362306a36Sopenharmony_ci err_remove_filter: 294462306a36Sopenharmony_ci if (dev->ntfy_supported) 294562306a36Sopenharmony_ci i8042_remove_filter(toshiba_acpi_i8042_filter); 294662306a36Sopenharmony_ci err_free_dev: 294762306a36Sopenharmony_ci input_free_device(dev->hotkey_dev); 294862306a36Sopenharmony_ci dev->hotkey_dev = NULL; 294962306a36Sopenharmony_ci return error; 295062306a36Sopenharmony_ci} 295162306a36Sopenharmony_ci 295262306a36Sopenharmony_cistatic int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev) 295362306a36Sopenharmony_ci{ 295462306a36Sopenharmony_ci struct backlight_properties props; 295562306a36Sopenharmony_ci int brightness; 295662306a36Sopenharmony_ci int ret; 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_ci /* 295962306a36Sopenharmony_ci * Some machines don't support the backlight methods at all, and 296062306a36Sopenharmony_ci * others support it read-only. Either of these is pretty useless, 296162306a36Sopenharmony_ci * so only register the backlight device if the backlight method 296262306a36Sopenharmony_ci * supports both reads and writes. 296362306a36Sopenharmony_ci */ 296462306a36Sopenharmony_ci brightness = __get_lcd_brightness(dev); 296562306a36Sopenharmony_ci if (brightness < 0) 296662306a36Sopenharmony_ci return 0; 296762306a36Sopenharmony_ci /* 296862306a36Sopenharmony_ci * If transflective backlight is supported and the brightness is zero 296962306a36Sopenharmony_ci * (lowest brightness level), the set_lcd_brightness function will 297062306a36Sopenharmony_ci * activate the transflective backlight, making the LCD appear to be 297162306a36Sopenharmony_ci * turned off, simply increment the brightness level to avoid that. 297262306a36Sopenharmony_ci */ 297362306a36Sopenharmony_ci if (dev->tr_backlight_supported && brightness == 0) 297462306a36Sopenharmony_ci brightness++; 297562306a36Sopenharmony_ci ret = set_lcd_brightness(dev, brightness); 297662306a36Sopenharmony_ci if (ret) { 297762306a36Sopenharmony_ci pr_debug("Backlight method is read-only, disabling backlight support\n"); 297862306a36Sopenharmony_ci return 0; 297962306a36Sopenharmony_ci } 298062306a36Sopenharmony_ci 298162306a36Sopenharmony_ci if (acpi_video_get_backlight_type() != acpi_backlight_vendor) 298262306a36Sopenharmony_ci return 0; 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci memset(&props, 0, sizeof(props)); 298562306a36Sopenharmony_ci props.type = BACKLIGHT_PLATFORM; 298662306a36Sopenharmony_ci props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; 298762306a36Sopenharmony_ci 298862306a36Sopenharmony_ci /* Adding an extra level and having 0 change to transflective mode */ 298962306a36Sopenharmony_ci if (dev->tr_backlight_supported) 299062306a36Sopenharmony_ci props.max_brightness++; 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_ci dev->backlight_dev = backlight_device_register("toshiba", 299362306a36Sopenharmony_ci &dev->acpi_dev->dev, 299462306a36Sopenharmony_ci dev, 299562306a36Sopenharmony_ci &toshiba_backlight_data, 299662306a36Sopenharmony_ci &props); 299762306a36Sopenharmony_ci if (IS_ERR(dev->backlight_dev)) { 299862306a36Sopenharmony_ci ret = PTR_ERR(dev->backlight_dev); 299962306a36Sopenharmony_ci pr_err("Could not register toshiba backlight device\n"); 300062306a36Sopenharmony_ci dev->backlight_dev = NULL; 300162306a36Sopenharmony_ci return ret; 300262306a36Sopenharmony_ci } 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_ci dev->backlight_dev->props.brightness = brightness; 300562306a36Sopenharmony_ci return 0; 300662306a36Sopenharmony_ci} 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci/* HWMON support for fan */ 300962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_HWMON) 301062306a36Sopenharmony_cistatic umode_t toshiba_acpi_hwmon_is_visible(const void *drvdata, 301162306a36Sopenharmony_ci enum hwmon_sensor_types type, 301262306a36Sopenharmony_ci u32 attr, int channel) 301362306a36Sopenharmony_ci{ 301462306a36Sopenharmony_ci return 0444; 301562306a36Sopenharmony_ci} 301662306a36Sopenharmony_ci 301762306a36Sopenharmony_cistatic int toshiba_acpi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 301862306a36Sopenharmony_ci u32 attr, int channel, long *val) 301962306a36Sopenharmony_ci{ 302062306a36Sopenharmony_ci /* 302162306a36Sopenharmony_ci * There is only a single channel and single attribute (for the 302262306a36Sopenharmony_ci * fan) at this point. 302362306a36Sopenharmony_ci * This can be replaced with more advanced logic in the future, 302462306a36Sopenharmony_ci * should the need arise. 302562306a36Sopenharmony_ci */ 302662306a36Sopenharmony_ci if (type == hwmon_fan && channel == 0 && attr == hwmon_fan_input) { 302762306a36Sopenharmony_ci u32 value; 302862306a36Sopenharmony_ci int ret; 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_ci ret = get_fan_rpm(toshiba_acpi, &value); 303162306a36Sopenharmony_ci if (ret) 303262306a36Sopenharmony_ci return ret; 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_ci *val = value; 303562306a36Sopenharmony_ci return 0; 303662306a36Sopenharmony_ci } 303762306a36Sopenharmony_ci return -EOPNOTSUPP; 303862306a36Sopenharmony_ci} 303962306a36Sopenharmony_ci 304062306a36Sopenharmony_cistatic const struct hwmon_channel_info * const toshiba_acpi_hwmon_info[] = { 304162306a36Sopenharmony_ci HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT), 304262306a36Sopenharmony_ci NULL 304362306a36Sopenharmony_ci}; 304462306a36Sopenharmony_ci 304562306a36Sopenharmony_cistatic const struct hwmon_ops toshiba_acpi_hwmon_ops = { 304662306a36Sopenharmony_ci .is_visible = toshiba_acpi_hwmon_is_visible, 304762306a36Sopenharmony_ci .read = toshiba_acpi_hwmon_read, 304862306a36Sopenharmony_ci}; 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_cistatic const struct hwmon_chip_info toshiba_acpi_hwmon_chip_info = { 305162306a36Sopenharmony_ci .ops = &toshiba_acpi_hwmon_ops, 305262306a36Sopenharmony_ci .info = toshiba_acpi_hwmon_info, 305362306a36Sopenharmony_ci}; 305462306a36Sopenharmony_ci#endif 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci/* ACPI battery hooking */ 305762306a36Sopenharmony_cistatic ssize_t charge_control_end_threshold_show(struct device *device, 305862306a36Sopenharmony_ci struct device_attribute *attr, 305962306a36Sopenharmony_ci char *buf) 306062306a36Sopenharmony_ci{ 306162306a36Sopenharmony_ci u32 state; 306262306a36Sopenharmony_ci int status; 306362306a36Sopenharmony_ci 306462306a36Sopenharmony_ci if (toshiba_acpi == NULL) { 306562306a36Sopenharmony_ci pr_err("Toshiba ACPI object invalid\n"); 306662306a36Sopenharmony_ci return -ENODEV; 306762306a36Sopenharmony_ci } 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ci status = toshiba_battery_charge_mode_get(toshiba_acpi, &state); 307062306a36Sopenharmony_ci 307162306a36Sopenharmony_ci if (status != 0) 307262306a36Sopenharmony_ci return status; 307362306a36Sopenharmony_ci 307462306a36Sopenharmony_ci if (state == 1) 307562306a36Sopenharmony_ci return sprintf(buf, "80\n"); 307662306a36Sopenharmony_ci else 307762306a36Sopenharmony_ci return sprintf(buf, "100\n"); 307862306a36Sopenharmony_ci} 307962306a36Sopenharmony_ci 308062306a36Sopenharmony_cistatic ssize_t charge_control_end_threshold_store(struct device *dev, 308162306a36Sopenharmony_ci struct device_attribute *attr, 308262306a36Sopenharmony_ci const char *buf, 308362306a36Sopenharmony_ci size_t count) 308462306a36Sopenharmony_ci{ 308562306a36Sopenharmony_ci u32 value; 308662306a36Sopenharmony_ci int rval; 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_ci if (toshiba_acpi == NULL) { 308962306a36Sopenharmony_ci pr_err("Toshiba ACPI object invalid\n"); 309062306a36Sopenharmony_ci return -ENODEV; 309162306a36Sopenharmony_ci } 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci rval = kstrtou32(buf, 10, &value); 309462306a36Sopenharmony_ci if (rval) 309562306a36Sopenharmony_ci return rval; 309662306a36Sopenharmony_ci 309762306a36Sopenharmony_ci if (value < 1 || value > 100) 309862306a36Sopenharmony_ci return -EINVAL; 309962306a36Sopenharmony_ci rval = toshiba_battery_charge_mode_set(toshiba_acpi, 310062306a36Sopenharmony_ci (value < 90) ? 1 : 0); 310162306a36Sopenharmony_ci if (rval < 0) 310262306a36Sopenharmony_ci return rval; 310362306a36Sopenharmony_ci else 310462306a36Sopenharmony_ci return count; 310562306a36Sopenharmony_ci} 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_cistatic DEVICE_ATTR_RW(charge_control_end_threshold); 310862306a36Sopenharmony_ci 310962306a36Sopenharmony_cistatic struct attribute *toshiba_acpi_battery_attrs[] = { 311062306a36Sopenharmony_ci &dev_attr_charge_control_end_threshold.attr, 311162306a36Sopenharmony_ci NULL, 311262306a36Sopenharmony_ci}; 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_ciATTRIBUTE_GROUPS(toshiba_acpi_battery); 311562306a36Sopenharmony_ci 311662306a36Sopenharmony_cistatic int toshiba_acpi_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) 311762306a36Sopenharmony_ci{ 311862306a36Sopenharmony_ci if (toshiba_acpi == NULL) { 311962306a36Sopenharmony_ci pr_err("Init order issue\n"); 312062306a36Sopenharmony_ci return -ENODEV; 312162306a36Sopenharmony_ci } 312262306a36Sopenharmony_ci if (!toshiba_acpi->battery_charge_mode_supported) 312362306a36Sopenharmony_ci return -ENODEV; 312462306a36Sopenharmony_ci if (device_add_groups(&battery->dev, toshiba_acpi_battery_groups)) 312562306a36Sopenharmony_ci return -ENODEV; 312662306a36Sopenharmony_ci return 0; 312762306a36Sopenharmony_ci} 312862306a36Sopenharmony_ci 312962306a36Sopenharmony_cistatic int toshiba_acpi_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook) 313062306a36Sopenharmony_ci{ 313162306a36Sopenharmony_ci device_remove_groups(&battery->dev, toshiba_acpi_battery_groups); 313262306a36Sopenharmony_ci return 0; 313362306a36Sopenharmony_ci} 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_cistatic struct acpi_battery_hook battery_hook = { 313662306a36Sopenharmony_ci .add_battery = toshiba_acpi_battery_add, 313762306a36Sopenharmony_ci .remove_battery = toshiba_acpi_battery_remove, 313862306a36Sopenharmony_ci .name = "Toshiba Battery Extension", 313962306a36Sopenharmony_ci}; 314062306a36Sopenharmony_ci 314162306a36Sopenharmony_cistatic void print_supported_features(struct toshiba_acpi_dev *dev) 314262306a36Sopenharmony_ci{ 314362306a36Sopenharmony_ci pr_info("Supported laptop features:"); 314462306a36Sopenharmony_ci 314562306a36Sopenharmony_ci if (dev->hotkey_dev) 314662306a36Sopenharmony_ci pr_cont(" hotkeys"); 314762306a36Sopenharmony_ci if (dev->backlight_dev) 314862306a36Sopenharmony_ci pr_cont(" backlight"); 314962306a36Sopenharmony_ci if (dev->video_supported) 315062306a36Sopenharmony_ci pr_cont(" video-out"); 315162306a36Sopenharmony_ci if (dev->fan_supported) 315262306a36Sopenharmony_ci pr_cont(" fan"); 315362306a36Sopenharmony_ci if (dev->fan_rpm_supported) 315462306a36Sopenharmony_ci pr_cont(" fan-rpm"); 315562306a36Sopenharmony_ci if (dev->tr_backlight_supported) 315662306a36Sopenharmony_ci pr_cont(" transflective-backlight"); 315762306a36Sopenharmony_ci if (dev->illumination_supported) 315862306a36Sopenharmony_ci pr_cont(" illumination"); 315962306a36Sopenharmony_ci if (dev->kbd_illum_supported) 316062306a36Sopenharmony_ci pr_cont(" keyboard-backlight"); 316162306a36Sopenharmony_ci if (dev->touchpad_supported) 316262306a36Sopenharmony_ci pr_cont(" touchpad"); 316362306a36Sopenharmony_ci if (dev->eco_supported) 316462306a36Sopenharmony_ci pr_cont(" eco-led"); 316562306a36Sopenharmony_ci if (dev->accelerometer_supported) 316662306a36Sopenharmony_ci pr_cont(" accelerometer-axes"); 316762306a36Sopenharmony_ci if (dev->usb_sleep_charge_supported) 316862306a36Sopenharmony_ci pr_cont(" usb-sleep-charge"); 316962306a36Sopenharmony_ci if (dev->usb_rapid_charge_supported) 317062306a36Sopenharmony_ci pr_cont(" usb-rapid-charge"); 317162306a36Sopenharmony_ci if (dev->usb_sleep_music_supported) 317262306a36Sopenharmony_ci pr_cont(" usb-sleep-music"); 317362306a36Sopenharmony_ci if (dev->kbd_function_keys_supported) 317462306a36Sopenharmony_ci pr_cont(" special-function-keys"); 317562306a36Sopenharmony_ci if (dev->panel_power_on_supported) 317662306a36Sopenharmony_ci pr_cont(" panel-power-on"); 317762306a36Sopenharmony_ci if (dev->usb_three_supported) 317862306a36Sopenharmony_ci pr_cont(" usb3"); 317962306a36Sopenharmony_ci if (dev->wwan_supported) 318062306a36Sopenharmony_ci pr_cont(" wwan"); 318162306a36Sopenharmony_ci if (dev->cooling_method_supported) 318262306a36Sopenharmony_ci pr_cont(" cooling-method"); 318362306a36Sopenharmony_ci if (dev->battery_charge_mode_supported) 318462306a36Sopenharmony_ci pr_cont(" battery-charge-mode"); 318562306a36Sopenharmony_ci 318662306a36Sopenharmony_ci pr_cont("\n"); 318762306a36Sopenharmony_ci} 318862306a36Sopenharmony_ci 318962306a36Sopenharmony_cistatic void toshiba_acpi_remove(struct acpi_device *acpi_dev) 319062306a36Sopenharmony_ci{ 319162306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci misc_deregister(&dev->miscdev); 319462306a36Sopenharmony_ci 319562306a36Sopenharmony_ci remove_toshiba_proc_entries(dev); 319662306a36Sopenharmony_ci 319762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_HWMON) 319862306a36Sopenharmony_ci if (dev->hwmon_device) 319962306a36Sopenharmony_ci hwmon_device_unregister(dev->hwmon_device); 320062306a36Sopenharmony_ci#endif 320162306a36Sopenharmony_ci 320262306a36Sopenharmony_ci if (dev->accelerometer_supported && dev->indio_dev) { 320362306a36Sopenharmony_ci iio_device_unregister(dev->indio_dev); 320462306a36Sopenharmony_ci iio_device_free(dev->indio_dev); 320562306a36Sopenharmony_ci } 320662306a36Sopenharmony_ci 320762306a36Sopenharmony_ci if (dev->sysfs_created) 320862306a36Sopenharmony_ci sysfs_remove_group(&dev->acpi_dev->dev.kobj, 320962306a36Sopenharmony_ci &toshiba_attr_group); 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_ci if (dev->ntfy_supported) { 321262306a36Sopenharmony_ci i8042_remove_filter(toshiba_acpi_i8042_filter); 321362306a36Sopenharmony_ci cancel_work_sync(&dev->hotkey_work); 321462306a36Sopenharmony_ci } 321562306a36Sopenharmony_ci 321662306a36Sopenharmony_ci if (dev->hotkey_dev) 321762306a36Sopenharmony_ci input_unregister_device(dev->hotkey_dev); 321862306a36Sopenharmony_ci 321962306a36Sopenharmony_ci backlight_device_unregister(dev->backlight_dev); 322062306a36Sopenharmony_ci 322162306a36Sopenharmony_ci led_classdev_unregister(&dev->led_dev); 322262306a36Sopenharmony_ci led_classdev_unregister(&dev->kbd_led); 322362306a36Sopenharmony_ci led_classdev_unregister(&dev->eco_led); 322462306a36Sopenharmony_ci 322562306a36Sopenharmony_ci if (dev->wwan_rfk) { 322662306a36Sopenharmony_ci rfkill_unregister(dev->wwan_rfk); 322762306a36Sopenharmony_ci rfkill_destroy(dev->wwan_rfk); 322862306a36Sopenharmony_ci } 322962306a36Sopenharmony_ci 323062306a36Sopenharmony_ci if (dev->battery_charge_mode_supported) 323162306a36Sopenharmony_ci battery_hook_unregister(&battery_hook); 323262306a36Sopenharmony_ci 323362306a36Sopenharmony_ci if (toshiba_acpi) 323462306a36Sopenharmony_ci toshiba_acpi = NULL; 323562306a36Sopenharmony_ci 323662306a36Sopenharmony_ci kfree(dev); 323762306a36Sopenharmony_ci} 323862306a36Sopenharmony_ci 323962306a36Sopenharmony_cistatic const char *find_hci_method(acpi_handle handle) 324062306a36Sopenharmony_ci{ 324162306a36Sopenharmony_ci if (acpi_has_method(handle, "GHCI")) 324262306a36Sopenharmony_ci return "GHCI"; 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_ci if (acpi_has_method(handle, "SPFC")) 324562306a36Sopenharmony_ci return "SPFC"; 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci return NULL; 324862306a36Sopenharmony_ci} 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_ci/* 325162306a36Sopenharmony_ci * Some Toshibas have a broken acpi-video interface for brightness control, 325262306a36Sopenharmony_ci * these are quirked in drivers/acpi/video_detect.c to use the GPU native 325362306a36Sopenharmony_ci * (/sys/class/backlight/intel_backlight) instead. 325462306a36Sopenharmony_ci * But these need a HCI_SET call to actually turn the panel back on at resume, 325562306a36Sopenharmony_ci * without this call the screen stays black at resume. 325662306a36Sopenharmony_ci * Either HCI_LCD_BRIGHTNESS (used by acpi_video's _BCM) or HCI_PANEL_POWER_ON 325762306a36Sopenharmony_ci * works. toshiba_acpi_resume() uses HCI_PANEL_POWER_ON to avoid changing 325862306a36Sopenharmony_ci * the configured brightness level. 325962306a36Sopenharmony_ci */ 326062306a36Sopenharmony_cistatic const struct dmi_system_id turn_on_panel_on_resume_dmi_ids[] = { 326162306a36Sopenharmony_ci { 326262306a36Sopenharmony_ci /* Toshiba Portégé R700 */ 326362306a36Sopenharmony_ci /* https://bugzilla.kernel.org/show_bug.cgi?id=21012 */ 326462306a36Sopenharmony_ci .matches = { 326562306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 326662306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R700"), 326762306a36Sopenharmony_ci }, 326862306a36Sopenharmony_ci }, 326962306a36Sopenharmony_ci { 327062306a36Sopenharmony_ci /* Toshiba Satellite/Portégé R830 */ 327162306a36Sopenharmony_ci /* Portégé: https://bugs.freedesktop.org/show_bug.cgi?id=82634 */ 327262306a36Sopenharmony_ci /* Satellite: https://bugzilla.kernel.org/show_bug.cgi?id=21012 */ 327362306a36Sopenharmony_ci .matches = { 327462306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 327562306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "R830"), 327662306a36Sopenharmony_ci }, 327762306a36Sopenharmony_ci }, 327862306a36Sopenharmony_ci { 327962306a36Sopenharmony_ci /* Toshiba Satellite/Portégé Z830 */ 328062306a36Sopenharmony_ci .matches = { 328162306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 328262306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Z830"), 328362306a36Sopenharmony_ci }, 328462306a36Sopenharmony_ci }, 328562306a36Sopenharmony_ci}; 328662306a36Sopenharmony_ci 328762306a36Sopenharmony_cistatic int toshiba_acpi_add(struct acpi_device *acpi_dev) 328862306a36Sopenharmony_ci{ 328962306a36Sopenharmony_ci struct toshiba_acpi_dev *dev; 329062306a36Sopenharmony_ci const char *hci_method; 329162306a36Sopenharmony_ci u32 dummy; 329262306a36Sopenharmony_ci int ret = 0; 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_ci if (toshiba_acpi) 329562306a36Sopenharmony_ci return -EBUSY; 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_ci pr_info("Toshiba Laptop ACPI Extras version %s\n", 329862306a36Sopenharmony_ci TOSHIBA_ACPI_VERSION); 329962306a36Sopenharmony_ci 330062306a36Sopenharmony_ci hci_method = find_hci_method(acpi_dev->handle); 330162306a36Sopenharmony_ci if (!hci_method) { 330262306a36Sopenharmony_ci pr_err("HCI interface not found\n"); 330362306a36Sopenharmony_ci return -ENODEV; 330462306a36Sopenharmony_ci } 330562306a36Sopenharmony_ci 330662306a36Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 330762306a36Sopenharmony_ci if (!dev) 330862306a36Sopenharmony_ci return -ENOMEM; 330962306a36Sopenharmony_ci dev->acpi_dev = acpi_dev; 331062306a36Sopenharmony_ci dev->method_hci = hci_method; 331162306a36Sopenharmony_ci dev->miscdev.minor = MISC_DYNAMIC_MINOR; 331262306a36Sopenharmony_ci dev->miscdev.name = "toshiba_acpi"; 331362306a36Sopenharmony_ci dev->miscdev.fops = &toshiba_acpi_fops; 331462306a36Sopenharmony_ci 331562306a36Sopenharmony_ci ret = misc_register(&dev->miscdev); 331662306a36Sopenharmony_ci if (ret) { 331762306a36Sopenharmony_ci pr_err("Failed to register miscdevice\n"); 331862306a36Sopenharmony_ci kfree(dev); 331962306a36Sopenharmony_ci return ret; 332062306a36Sopenharmony_ci } 332162306a36Sopenharmony_ci 332262306a36Sopenharmony_ci acpi_dev->driver_data = dev; 332362306a36Sopenharmony_ci dev_set_drvdata(&acpi_dev->dev, dev); 332462306a36Sopenharmony_ci 332562306a36Sopenharmony_ci /* Query the BIOS for supported features */ 332662306a36Sopenharmony_ci 332762306a36Sopenharmony_ci /* 332862306a36Sopenharmony_ci * The "Special Functions" are always supported by the laptops 332962306a36Sopenharmony_ci * with the new keyboard layout, query for its presence to help 333062306a36Sopenharmony_ci * determine the keymap layout to use. 333162306a36Sopenharmony_ci */ 333262306a36Sopenharmony_ci ret = toshiba_function_keys_get(dev, &dev->special_functions); 333362306a36Sopenharmony_ci dev->kbd_function_keys_supported = !ret; 333462306a36Sopenharmony_ci 333562306a36Sopenharmony_ci dev->hotkey_event_type = 0; 333662306a36Sopenharmony_ci if (toshiba_acpi_setup_keyboard(dev)) 333762306a36Sopenharmony_ci pr_info("Unable to activate hotkeys\n"); 333862306a36Sopenharmony_ci 333962306a36Sopenharmony_ci /* Determine whether or not BIOS supports transflective backlight */ 334062306a36Sopenharmony_ci ret = get_tr_backlight_status(dev, &dummy); 334162306a36Sopenharmony_ci dev->tr_backlight_supported = !ret; 334262306a36Sopenharmony_ci 334362306a36Sopenharmony_ci ret = toshiba_acpi_setup_backlight(dev); 334462306a36Sopenharmony_ci if (ret) 334562306a36Sopenharmony_ci goto error; 334662306a36Sopenharmony_ci 334762306a36Sopenharmony_ci toshiba_illumination_available(dev); 334862306a36Sopenharmony_ci if (dev->illumination_supported) { 334962306a36Sopenharmony_ci dev->led_dev.name = "toshiba::illumination"; 335062306a36Sopenharmony_ci dev->led_dev.max_brightness = 1; 335162306a36Sopenharmony_ci dev->led_dev.brightness_set = toshiba_illumination_set; 335262306a36Sopenharmony_ci dev->led_dev.brightness_get = toshiba_illumination_get; 335362306a36Sopenharmony_ci led_classdev_register(&acpi_dev->dev, &dev->led_dev); 335462306a36Sopenharmony_ci } 335562306a36Sopenharmony_ci 335662306a36Sopenharmony_ci toshiba_eco_mode_available(dev); 335762306a36Sopenharmony_ci if (dev->eco_supported) { 335862306a36Sopenharmony_ci dev->eco_led.name = "toshiba::eco_mode"; 335962306a36Sopenharmony_ci dev->eco_led.max_brightness = 1; 336062306a36Sopenharmony_ci dev->eco_led.brightness_set = toshiba_eco_mode_set_status; 336162306a36Sopenharmony_ci dev->eco_led.brightness_get = toshiba_eco_mode_get_status; 336262306a36Sopenharmony_ci led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led); 336362306a36Sopenharmony_ci } 336462306a36Sopenharmony_ci 336562306a36Sopenharmony_ci toshiba_kbd_illum_available(dev); 336662306a36Sopenharmony_ci /* 336762306a36Sopenharmony_ci * Only register the LED if KBD illumination is supported 336862306a36Sopenharmony_ci * and the keyboard backlight operation mode is set to FN-Z 336962306a36Sopenharmony_ci * or we detect a second gen keyboard backlight 337062306a36Sopenharmony_ci */ 337162306a36Sopenharmony_ci if (dev->kbd_illum_supported && 337262306a36Sopenharmony_ci (dev->kbd_mode == SCI_KBD_MODE_FNZ || dev->kbd_type == 2)) { 337362306a36Sopenharmony_ci dev->kbd_led.name = "toshiba::kbd_backlight"; 337462306a36Sopenharmony_ci dev->kbd_led.flags = LED_BRIGHT_HW_CHANGED; 337562306a36Sopenharmony_ci dev->kbd_led.max_brightness = 1; 337662306a36Sopenharmony_ci dev->kbd_led.brightness_set = toshiba_kbd_backlight_set; 337762306a36Sopenharmony_ci dev->kbd_led.brightness_get = toshiba_kbd_backlight_get; 337862306a36Sopenharmony_ci led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led); 337962306a36Sopenharmony_ci } 338062306a36Sopenharmony_ci 338162306a36Sopenharmony_ci ret = toshiba_touchpad_get(dev, &dummy); 338262306a36Sopenharmony_ci dev->touchpad_supported = !ret; 338362306a36Sopenharmony_ci 338462306a36Sopenharmony_ci toshiba_accelerometer_available(dev); 338562306a36Sopenharmony_ci if (dev->accelerometer_supported) { 338662306a36Sopenharmony_ci dev->indio_dev = iio_device_alloc(&acpi_dev->dev, sizeof(*dev)); 338762306a36Sopenharmony_ci if (!dev->indio_dev) { 338862306a36Sopenharmony_ci pr_err("Unable to allocate iio device\n"); 338962306a36Sopenharmony_ci goto iio_error; 339062306a36Sopenharmony_ci } 339162306a36Sopenharmony_ci 339262306a36Sopenharmony_ci pr_info("Registering Toshiba accelerometer iio device\n"); 339362306a36Sopenharmony_ci 339462306a36Sopenharmony_ci dev->indio_dev->info = &toshiba_iio_accel_info; 339562306a36Sopenharmony_ci dev->indio_dev->name = "Toshiba accelerometer"; 339662306a36Sopenharmony_ci dev->indio_dev->modes = INDIO_DIRECT_MODE; 339762306a36Sopenharmony_ci dev->indio_dev->channels = toshiba_iio_accel_channels; 339862306a36Sopenharmony_ci dev->indio_dev->num_channels = 339962306a36Sopenharmony_ci ARRAY_SIZE(toshiba_iio_accel_channels); 340062306a36Sopenharmony_ci 340162306a36Sopenharmony_ci ret = iio_device_register(dev->indio_dev); 340262306a36Sopenharmony_ci if (ret < 0) { 340362306a36Sopenharmony_ci pr_err("Unable to register iio device\n"); 340462306a36Sopenharmony_ci iio_device_free(dev->indio_dev); 340562306a36Sopenharmony_ci } 340662306a36Sopenharmony_ci } 340762306a36Sopenharmony_ciiio_error: 340862306a36Sopenharmony_ci 340962306a36Sopenharmony_ci toshiba_usb_sleep_charge_available(dev); 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ci ret = toshiba_usb_rapid_charge_get(dev, &dummy); 341262306a36Sopenharmony_ci dev->usb_rapid_charge_supported = !ret; 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ci ret = toshiba_usb_sleep_music_get(dev, &dummy); 341562306a36Sopenharmony_ci dev->usb_sleep_music_supported = !ret; 341662306a36Sopenharmony_ci 341762306a36Sopenharmony_ci ret = toshiba_panel_power_on_get(dev, &dummy); 341862306a36Sopenharmony_ci dev->panel_power_on_supported = !ret; 341962306a36Sopenharmony_ci 342062306a36Sopenharmony_ci ret = toshiba_usb_three_get(dev, &dummy); 342162306a36Sopenharmony_ci dev->usb_three_supported = !ret; 342262306a36Sopenharmony_ci 342362306a36Sopenharmony_ci ret = get_video_status(dev, &dummy); 342462306a36Sopenharmony_ci dev->video_supported = !ret; 342562306a36Sopenharmony_ci 342662306a36Sopenharmony_ci ret = get_fan_status(dev, &dummy); 342762306a36Sopenharmony_ci dev->fan_supported = !ret; 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_ci ret = get_fan_rpm(dev, &dummy); 343062306a36Sopenharmony_ci dev->fan_rpm_supported = !ret; 343162306a36Sopenharmony_ci 343262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_HWMON) 343362306a36Sopenharmony_ci if (dev->fan_rpm_supported) { 343462306a36Sopenharmony_ci dev->hwmon_device = hwmon_device_register_with_info( 343562306a36Sopenharmony_ci &dev->acpi_dev->dev, "toshiba_acpi_sensors", NULL, 343662306a36Sopenharmony_ci &toshiba_acpi_hwmon_chip_info, NULL); 343762306a36Sopenharmony_ci if (IS_ERR(dev->hwmon_device)) { 343862306a36Sopenharmony_ci dev->hwmon_device = NULL; 343962306a36Sopenharmony_ci pr_warn("unable to register hwmon device, skipping\n"); 344062306a36Sopenharmony_ci } 344162306a36Sopenharmony_ci } 344262306a36Sopenharmony_ci#endif 344362306a36Sopenharmony_ci 344462306a36Sopenharmony_ci if (turn_on_panel_on_resume == -1) 344562306a36Sopenharmony_ci turn_on_panel_on_resume = dmi_check_system(turn_on_panel_on_resume_dmi_ids); 344662306a36Sopenharmony_ci 344762306a36Sopenharmony_ci toshiba_wwan_available(dev); 344862306a36Sopenharmony_ci if (dev->wwan_supported) 344962306a36Sopenharmony_ci toshiba_acpi_setup_wwan_rfkill(dev); 345062306a36Sopenharmony_ci 345162306a36Sopenharmony_ci toshiba_cooling_method_available(dev); 345262306a36Sopenharmony_ci 345362306a36Sopenharmony_ci toshiba_battery_charge_mode_available(dev); 345462306a36Sopenharmony_ci 345562306a36Sopenharmony_ci print_supported_features(dev); 345662306a36Sopenharmony_ci 345762306a36Sopenharmony_ci ret = sysfs_create_group(&dev->acpi_dev->dev.kobj, 345862306a36Sopenharmony_ci &toshiba_attr_group); 345962306a36Sopenharmony_ci if (ret) { 346062306a36Sopenharmony_ci dev->sysfs_created = 0; 346162306a36Sopenharmony_ci goto error; 346262306a36Sopenharmony_ci } 346362306a36Sopenharmony_ci dev->sysfs_created = !ret; 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_ci create_toshiba_proc_entries(dev); 346662306a36Sopenharmony_ci 346762306a36Sopenharmony_ci toshiba_acpi = dev; 346862306a36Sopenharmony_ci 346962306a36Sopenharmony_ci /* 347062306a36Sopenharmony_ci * As the battery hook relies on the static variable toshiba_acpi being 347162306a36Sopenharmony_ci * set, this must be done after toshiba_acpi is assigned. 347262306a36Sopenharmony_ci */ 347362306a36Sopenharmony_ci if (dev->battery_charge_mode_supported) 347462306a36Sopenharmony_ci battery_hook_register(&battery_hook); 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci return 0; 347762306a36Sopenharmony_ci 347862306a36Sopenharmony_cierror: 347962306a36Sopenharmony_ci toshiba_acpi_remove(acpi_dev); 348062306a36Sopenharmony_ci return ret; 348162306a36Sopenharmony_ci} 348262306a36Sopenharmony_ci 348362306a36Sopenharmony_cistatic void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) 348462306a36Sopenharmony_ci{ 348562306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); 348662306a36Sopenharmony_ci 348762306a36Sopenharmony_ci switch (event) { 348862306a36Sopenharmony_ci case 0x80: /* Hotkeys and some system events */ 348962306a36Sopenharmony_ci /* 349062306a36Sopenharmony_ci * Machines with this WMI GUID aren't supported due to bugs in 349162306a36Sopenharmony_ci * their AML. 349262306a36Sopenharmony_ci * 349362306a36Sopenharmony_ci * Return silently to avoid triggering a netlink event. 349462306a36Sopenharmony_ci */ 349562306a36Sopenharmony_ci if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) 349662306a36Sopenharmony_ci return; 349762306a36Sopenharmony_ci toshiba_acpi_process_hotkeys(dev); 349862306a36Sopenharmony_ci break; 349962306a36Sopenharmony_ci case 0x81: /* Dock events */ 350062306a36Sopenharmony_ci case 0x82: 350162306a36Sopenharmony_ci case 0x83: 350262306a36Sopenharmony_ci pr_info("Dock event received %x\n", event); 350362306a36Sopenharmony_ci break; 350462306a36Sopenharmony_ci case 0x88: /* Thermal events */ 350562306a36Sopenharmony_ci pr_info("Thermal event received\n"); 350662306a36Sopenharmony_ci break; 350762306a36Sopenharmony_ci case 0x8f: /* LID closed */ 350862306a36Sopenharmony_ci case 0x90: /* LID is closed and Dock has been ejected */ 350962306a36Sopenharmony_ci break; 351062306a36Sopenharmony_ci case 0x8c: /* SATA power events */ 351162306a36Sopenharmony_ci case 0x8b: 351262306a36Sopenharmony_ci pr_info("SATA power event received %x\n", event); 351362306a36Sopenharmony_ci break; 351462306a36Sopenharmony_ci case 0x92: /* Keyboard backlight mode changed */ 351562306a36Sopenharmony_ci dev->kbd_event_generated = true; 351662306a36Sopenharmony_ci /* Update sysfs entries */ 351762306a36Sopenharmony_ci if (sysfs_update_group(&acpi_dev->dev.kobj, 351862306a36Sopenharmony_ci &toshiba_attr_group)) 351962306a36Sopenharmony_ci pr_err("Unable to update sysfs entries\n"); 352062306a36Sopenharmony_ci /* Notify LED subsystem about keyboard backlight change */ 352162306a36Sopenharmony_ci if (dev->kbd_type == 2 && dev->kbd_mode != SCI_KBD_MODE_AUTO) 352262306a36Sopenharmony_ci led_classdev_notify_brightness_hw_changed(&dev->kbd_led, 352362306a36Sopenharmony_ci (dev->kbd_mode == SCI_KBD_MODE_ON) ? 352462306a36Sopenharmony_ci LED_FULL : LED_OFF); 352562306a36Sopenharmony_ci break; 352662306a36Sopenharmony_ci case 0x85: /* Unknown */ 352762306a36Sopenharmony_ci case 0x8d: /* Unknown */ 352862306a36Sopenharmony_ci case 0x8e: /* Unknown */ 352962306a36Sopenharmony_ci case 0x94: /* Unknown */ 353062306a36Sopenharmony_ci case 0x95: /* Unknown */ 353162306a36Sopenharmony_ci default: 353262306a36Sopenharmony_ci pr_info("Unknown event received %x\n", event); 353362306a36Sopenharmony_ci break; 353462306a36Sopenharmony_ci } 353562306a36Sopenharmony_ci 353662306a36Sopenharmony_ci acpi_bus_generate_netlink_event(acpi_dev->pnp.device_class, 353762306a36Sopenharmony_ci dev_name(&acpi_dev->dev), 353862306a36Sopenharmony_ci event, (event == 0x80) ? 353962306a36Sopenharmony_ci dev->last_key_event : 0); 354062306a36Sopenharmony_ci} 354162306a36Sopenharmony_ci 354262306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 354362306a36Sopenharmony_cistatic int toshiba_acpi_suspend(struct device *device) 354462306a36Sopenharmony_ci{ 354562306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); 354662306a36Sopenharmony_ci 354762306a36Sopenharmony_ci if (dev->hotkey_dev) { 354862306a36Sopenharmony_ci u32 result; 354962306a36Sopenharmony_ci 355062306a36Sopenharmony_ci result = hci_write(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE); 355162306a36Sopenharmony_ci if (result != TOS_SUCCESS) 355262306a36Sopenharmony_ci pr_info("Unable to disable hotkeys\n"); 355362306a36Sopenharmony_ci } 355462306a36Sopenharmony_ci 355562306a36Sopenharmony_ci return 0; 355662306a36Sopenharmony_ci} 355762306a36Sopenharmony_ci 355862306a36Sopenharmony_cistatic int toshiba_acpi_resume(struct device *device) 355962306a36Sopenharmony_ci{ 356062306a36Sopenharmony_ci struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); 356162306a36Sopenharmony_ci 356262306a36Sopenharmony_ci if (dev->hotkey_dev) { 356362306a36Sopenharmony_ci if (toshiba_acpi_enable_hotkeys(dev)) 356462306a36Sopenharmony_ci pr_info("Unable to re-enable hotkeys\n"); 356562306a36Sopenharmony_ci } 356662306a36Sopenharmony_ci 356762306a36Sopenharmony_ci if (dev->wwan_rfk) { 356862306a36Sopenharmony_ci if (!toshiba_wireless_status(dev)) 356962306a36Sopenharmony_ci rfkill_set_hw_state(dev->wwan_rfk, !dev->killswitch); 357062306a36Sopenharmony_ci } 357162306a36Sopenharmony_ci 357262306a36Sopenharmony_ci if (turn_on_panel_on_resume) 357362306a36Sopenharmony_ci hci_write(dev, HCI_PANEL_POWER_ON, 1); 357462306a36Sopenharmony_ci 357562306a36Sopenharmony_ci return 0; 357662306a36Sopenharmony_ci} 357762306a36Sopenharmony_ci#endif 357862306a36Sopenharmony_ci 357962306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(toshiba_acpi_pm, 358062306a36Sopenharmony_ci toshiba_acpi_suspend, toshiba_acpi_resume); 358162306a36Sopenharmony_ci 358262306a36Sopenharmony_cistatic struct acpi_driver toshiba_acpi_driver = { 358362306a36Sopenharmony_ci .name = "Toshiba ACPI driver", 358462306a36Sopenharmony_ci .owner = THIS_MODULE, 358562306a36Sopenharmony_ci .ids = toshiba_device_ids, 358662306a36Sopenharmony_ci .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, 358762306a36Sopenharmony_ci .ops = { 358862306a36Sopenharmony_ci .add = toshiba_acpi_add, 358962306a36Sopenharmony_ci .remove = toshiba_acpi_remove, 359062306a36Sopenharmony_ci .notify = toshiba_acpi_notify, 359162306a36Sopenharmony_ci }, 359262306a36Sopenharmony_ci .drv.pm = &toshiba_acpi_pm, 359362306a36Sopenharmony_ci}; 359462306a36Sopenharmony_ci 359562306a36Sopenharmony_cistatic int __init toshiba_acpi_init(void) 359662306a36Sopenharmony_ci{ 359762306a36Sopenharmony_ci int ret; 359862306a36Sopenharmony_ci 359962306a36Sopenharmony_ci toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); 360062306a36Sopenharmony_ci if (!toshiba_proc_dir) { 360162306a36Sopenharmony_ci pr_err("Unable to create proc dir " PROC_TOSHIBA "\n"); 360262306a36Sopenharmony_ci return -ENODEV; 360362306a36Sopenharmony_ci } 360462306a36Sopenharmony_ci 360562306a36Sopenharmony_ci ret = acpi_bus_register_driver(&toshiba_acpi_driver); 360662306a36Sopenharmony_ci if (ret) { 360762306a36Sopenharmony_ci pr_err("Failed to register ACPI driver: %d\n", ret); 360862306a36Sopenharmony_ci remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); 360962306a36Sopenharmony_ci } 361062306a36Sopenharmony_ci 361162306a36Sopenharmony_ci return ret; 361262306a36Sopenharmony_ci} 361362306a36Sopenharmony_ci 361462306a36Sopenharmony_cistatic void __exit toshiba_acpi_exit(void) 361562306a36Sopenharmony_ci{ 361662306a36Sopenharmony_ci acpi_bus_unregister_driver(&toshiba_acpi_driver); 361762306a36Sopenharmony_ci if (toshiba_proc_dir) 361862306a36Sopenharmony_ci remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); 361962306a36Sopenharmony_ci} 362062306a36Sopenharmony_ci 362162306a36Sopenharmony_cimodule_init(toshiba_acpi_init); 362262306a36Sopenharmony_cimodule_exit(toshiba_acpi_exit); 3623