summaryrefslogtreecommitdiff
path: root/drivers/platform/x86/toshiba_acpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/toshiba_acpi.c')
-rw-r--r--drivers/platform/x86/toshiba_acpi.c635
1 files changed, 578 insertions, 57 deletions
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 90dd7645a9e5..46473ca7566b 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -5,6 +5,7 @@
* Copyright (C) 2002-2004 John Belmonte
* Copyright (C) 2008 Philip Langdale
* Copyright (C) 2010 Pierre Ducroquet
+ * Copyright (C) 2014 Azael Avalos
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -37,7 +38,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#define TOSHIBA_ACPI_VERSION "0.19"
+#define TOSHIBA_ACPI_VERSION "0.20"
#define PROC_INTERFACE_VERSION 1
#include <linux/kernel.h>
@@ -77,6 +78,9 @@ MODULE_LICENSE("GPL");
* However the ACPI methods seem to be incomplete in some areas (for
* example they allow setting, but not reading, the LCD brightness value),
* so this is still useful.
+ *
+ * SCI stands for "System Configuration Interface" which aim is to
+ * conceal differences in hardware between different models.
*/
#define HCI_WORDS 6
@@ -84,12 +88,23 @@ MODULE_LICENSE("GPL");
/* operations */
#define HCI_SET 0xff00
#define HCI_GET 0xfe00
+#define SCI_OPEN 0xf100
+#define SCI_CLOSE 0xf200
+#define SCI_GET 0xf300
+#define SCI_SET 0xf400
/* return codes */
#define HCI_SUCCESS 0x0000
#define HCI_FAILURE 0x1000
#define HCI_NOT_SUPPORTED 0x8000
#define HCI_EMPTY 0x8c00
+#define HCI_DATA_NOT_AVAILABLE 0x8d20
+#define HCI_NOT_INITIALIZED 0x8d50
+#define SCI_OPEN_CLOSE_OK 0x0044
+#define SCI_ALREADY_OPEN 0x8100
+#define SCI_NOT_OPENED 0x8200
+#define SCI_INPUT_DATA_ERROR 0x8300
+#define SCI_NOT_PRESENT 0x8600
/* registers */
#define HCI_FAN 0x0004
@@ -99,13 +114,22 @@ MODULE_LICENSE("GPL");
#define HCI_HOTKEY_EVENT 0x001e
#define HCI_LCD_BRIGHTNESS 0x002a
#define HCI_WIRELESS 0x0056
+#define HCI_ACCELEROMETER 0x006d
+#define HCI_KBD_ILLUMINATION 0x0095
+#define HCI_ECO_MODE 0x0097
+#define HCI_ACCELEROMETER2 0x00a6
+#define SCI_ILLUMINATION 0x014e
+#define SCI_KBD_ILLUM_STATUS 0x015c
+#define SCI_TOUCHPAD 0x050e
/* field definitions */
+#define HCI_ACCEL_MASK 0x7fff
#define HCI_HOTKEY_DISABLE 0x0b
#define HCI_HOTKEY_ENABLE 0x09
#define HCI_LCD_BRIGHTNESS_BITS 3
#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS)
#define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS)
+#define HCI_MISC_SHIFT 0x10
#define HCI_VIDEO_OUT_LCD 0x1
#define HCI_VIDEO_OUT_CRT 0x2
#define HCI_VIDEO_OUT_TV 0x4
@@ -113,6 +137,8 @@ MODULE_LICENSE("GPL");
#define HCI_WIRELESS_BT_PRESENT 0x0f
#define HCI_WIRELESS_BT_ATTACH 0x40
#define HCI_WIRELESS_BT_POWER 0x80
+#define SCI_KBD_MODE_FNZ 0x1
+#define SCI_KBD_MODE_AUTO 0x2
struct toshiba_acpi_dev {
struct acpi_device *acpi_dev;
@@ -122,10 +148,14 @@ struct toshiba_acpi_dev {
struct work_struct hotkey_work;
struct backlight_device *backlight_dev;
struct led_classdev led_dev;
+ struct led_classdev kbd_led;
+ struct led_classdev eco_led;
int force_fan;
int last_key_event;
int key_event_valid;
+ int kbd_mode;
+ int kbd_time;
unsigned int illumination_supported:1;
unsigned int video_supported:1;
@@ -134,6 +164,12 @@ struct toshiba_acpi_dev {
unsigned int ntfy_supported:1;
unsigned int info_supported:1;
unsigned int tr_backlight_supported:1;
+ unsigned int kbd_illum_supported:1;
+ unsigned int kbd_led_registered:1;
+ unsigned int touchpad_supported:1;
+ unsigned int eco_supported:1;
+ unsigned int accelerometer_supported:1;
+ unsigned int sysfs_created:1;
struct mutex mutex;
};
@@ -280,21 +316,94 @@ static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg,
return status;
}
+/* common sci tasks
+ */
+
+static int sci_open(struct toshiba_acpi_dev *dev)
+{
+ u32 in[HCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
+ pr_err("ACPI call to open SCI failed\n");
+ return 0;
+ }
+
+ if (out[0] == SCI_OPEN_CLOSE_OK) {
+ return 1;
+ } else if (out[0] == SCI_ALREADY_OPEN) {
+ pr_info("Toshiba SCI already opened\n");
+ return 1;
+ } else if (out[0] == SCI_NOT_PRESENT) {
+ pr_info("Toshiba SCI is not present\n");
+ }
+
+ return 0;
+}
+
+static void sci_close(struct toshiba_acpi_dev *dev)
+{
+ u32 in[HCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
+ pr_err("ACPI call to close SCI failed\n");
+ return;
+ }
+
+ if (out[0] == SCI_OPEN_CLOSE_OK)
+ return;
+ else if (out[0] == SCI_NOT_OPENED)
+ pr_info("Toshiba SCI not opened\n");
+ else if (out[0] == SCI_NOT_PRESENT)
+ pr_info("Toshiba SCI is not present\n");
+}
+
+static acpi_status sci_read(struct toshiba_acpi_dev *dev, u32 reg,
+ u32 *out1, u32 *result)
+{
+ u32 in[HCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status = hci_raw(dev, in, out);
+ *out1 = out[2];
+ *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE;
+ return status;
+}
+
+static acpi_status sci_write(struct toshiba_acpi_dev *dev, u32 reg,
+ u32 in1, u32 *result)
+{
+ u32 in[HCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status = hci_raw(dev, in, out);
+ *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE;
+ return status;
+}
+
/* Illumination support */
static int toshiba_illumination_available(struct toshiba_acpi_dev *dev)
{
- u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
+ u32 in[HCI_WORDS] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status;
- in[0] = 0xf100;
+ if (!sci_open(dev))
+ return 0;
+
status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
+ pr_err("ACPI call to query Illumination support failed\n");
+ return 0;
+ } else if (out[0] == HCI_NOT_SUPPORTED || out[1] != 1) {
pr_info("Illumination device not available\n");
return 0;
}
- in[0] = 0xf400;
- status = hci_raw(dev, in, out);
+
return 1;
}
@@ -303,82 +412,270 @@ static void toshiba_illumination_set(struct led_classdev *cdev,
{
struct toshiba_acpi_dev *dev = container_of(cdev,
struct toshiba_acpi_dev, led_dev);
- u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
- u32 out[HCI_WORDS];
+ u32 state, result;
acpi_status status;
/* First request : initialize communication. */
- in[0] = 0xf100;
- status = hci_raw(dev, in, out);
+ if (!sci_open(dev))
+ return;
+
+ /* Switch the illumination on/off */
+ state = brightness ? 1 : 0;
+ status = sci_write(dev, SCI_ILLUMINATION, state, &result);
+ sci_close(dev);
if (ACPI_FAILURE(status)) {
- pr_info("Illumination device not available\n");
+ pr_err("ACPI call for illumination failed\n");
+ return;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Illumination not supported\n");
return;
}
+}
- if (brightness) {
- /* Switch the illumination on */
- in[0] = 0xf400;
- in[1] = 0x14e;
- in[2] = 1;
- status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("ACPI call for illumination failed\n");
- return;
- }
- } else {
- /* Switch the illumination off */
- in[0] = 0xf400;
- in[1] = 0x14e;
- in[2] = 0;
- status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("ACPI call for illumination failed.\n");
- return;
- }
+static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, led_dev);
+ u32 state, result;
+ acpi_status status;
+
+ /* First request : initialize communication. */
+ if (!sci_open(dev))
+ return LED_OFF;
+
+ /* Check the illumination */
+ status = sci_read(dev, SCI_ILLUMINATION, &state, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call for illumination failed\n");
+ return LED_OFF;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Illumination not supported\n");
+ return LED_OFF;
}
- /* Last request : close communication. */
- in[0] = 0xf200;
- in[1] = 0;
- in[2] = 0;
- hci_raw(dev, in, out);
+ return state ? LED_FULL : LED_OFF;
}
-static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
+/* KBD Illumination */
+static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_write(dev, SCI_KBD_ILLUM_STATUS, time, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to set KBD backlight status failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight status not supported\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_read(dev, SCI_KBD_ILLUM_STATUS, time, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to get KBD backlight status failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight status not supported\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static enum led_brightness toshiba_kbd_backlight_get(struct led_classdev *cdev)
{
struct toshiba_acpi_dev *dev = container_of(cdev,
- struct toshiba_acpi_dev, led_dev);
- u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
+ struct toshiba_acpi_dev, kbd_led);
+ u32 state, result;
+ acpi_status status;
+
+ /* Check the keyboard backlight state */
+ status = hci_read1(dev, HCI_KBD_ILLUMINATION, &state, &result);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to get the keyboard backlight failed\n");
+ return LED_OFF;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight not supported\n");
+ return LED_OFF;
+ }
+
+ return state ? LED_FULL : LED_OFF;
+}
+
+static void toshiba_kbd_backlight_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, kbd_led);
+ u32 state, result;
+ acpi_status status;
+
+ /* Set the keyboard backlight state */
+ state = brightness ? 1 : 0;
+ status = hci_write1(dev, HCI_KBD_ILLUMINATION, state, &result);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to set KBD Illumination mode failed\n");
+ return;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight not supported\n");
+ return;
+ }
+}
+
+/* TouchPad support */
+static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_write(dev, SCI_TOUCHPAD, state, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status)) {
+ pr_err("ACPI call to set the touchpad failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_read(dev, SCI_TOUCHPAD, state, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status)) {
+ pr_err("ACPI call to query the touchpad failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/* Eco Mode support */
+static int toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
+{
+ acpi_status status;
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 };
+ u32 out[HCI_WORDS];
+
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_info("ACPI call to get ECO led failed\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static enum led_brightness toshiba_eco_mode_get_status(struct led_classdev *cdev)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, eco_led);
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status;
- enum led_brightness result;
- /* First request : initialize communication. */
- in[0] = 0xf100;
status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("Illumination device not available\n");
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to get ECO led failed\n");
return LED_OFF;
}
- /* Check the illumination */
- in[0] = 0xf300;
- in[1] = 0x14e;
+ return out[2] ? LED_FULL : LED_OFF;
+}
+
+static void toshiba_eco_mode_set_status(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, eco_led);
+ u32 in[HCI_WORDS] = { HCI_SET, HCI_ECO_MODE, 0, 1, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ /* Switch the Eco Mode led on/off */
+ in[2] = (brightness) ? 1 : 0;
status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("ACPI call for illumination failed.\n");
- return LED_OFF;
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to set ECO led failed\n");
+ return;
}
+}
- result = out[2] ? LED_FULL : LED_OFF;
+/* Accelerometer support */
+static int toshiba_accelerometer_supported(struct toshiba_acpi_dev *dev)
+{
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER2, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ /* Check if the accelerometer call exists,
+ * this call also serves as initialization
+ */
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to query the accelerometer failed\n");
+ return -EIO;
+ } else if (out[0] == HCI_DATA_NOT_AVAILABLE ||
+ out[0] == HCI_NOT_INITIALIZED) {
+ pr_err("Accelerometer not initialized\n");
+ return -EIO;
+ } else if (out[0] == HCI_NOT_SUPPORTED) {
+ pr_info("Accelerometer not supported\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev,
+ u32 *xy, u32 *z)
+{
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER, 0, 1, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ /* Check the Accelerometer status */
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to query the accelerometer failed\n");
+ return -EIO;
+ }
- /* Last request : close communication. */
- in[0] = 0xf200;
- in[1] = 0;
- in[2] = 0;
- hci_raw(dev, in, out);
+ *xy = out[2];
+ *z = out[4];
- return result;
+ return 0;
}
/* Bluetooth rfkill handlers */
@@ -904,6 +1201,177 @@ static const struct backlight_ops toshiba_backlight_data = {
.update_status = set_lcd_status,
};
+/*
+ * Sysfs files
+ */
+
+static ssize_t toshiba_kbd_bl_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ int mode = -1;
+ int time = -1;
+
+ if (sscanf(buf, "%i", &mode) != 1 && (mode != 2 || mode != 1))
+ return -EINVAL;
+
+ /* Set the Keyboard Backlight Mode where:
+ * Mode - Auto (2) | FN-Z (1)
+ * Auto - KBD backlight turns off automatically in given time
+ * FN-Z - KBD backlight "toggles" when hotkey pressed
+ */
+ if (mode != -1 && toshiba->kbd_mode != mode) {
+ time = toshiba->kbd_time << HCI_MISC_SHIFT;
+ time = time + toshiba->kbd_mode;
+ if (toshiba_kbd_illum_status_set(toshiba, time) < 0)
+ return -EIO;
+ toshiba->kbd_mode = mode;
+ }
+
+ return count;
+}
+
+static ssize_t toshiba_kbd_bl_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 time;
+
+ if (toshiba_kbd_illum_status_get(toshiba, &time) < 0)
+ return -EIO;
+
+ return sprintf(buf, "%i\n", time & 0x07);
+}
+
+static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ int time = -1;
+
+ if (sscanf(buf, "%i", &time) != 1 && (time < 0 || time > 60))
+ return -EINVAL;
+
+ /* Set the Keyboard Backlight Timeout: 0-60 seconds */
+ if (time != -1 && toshiba->kbd_time != time) {
+ time = time << HCI_MISC_SHIFT;
+ time = (toshiba->kbd_mode == SCI_KBD_MODE_AUTO) ?
+ time + 1 : time + 2;
+ if (toshiba_kbd_illum_status_set(toshiba, time) < 0)
+ return -EIO;
+ toshiba->kbd_time = time >> HCI_MISC_SHIFT;
+ }
+
+ return count;
+}
+
+static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 time;
+
+ if (toshiba_kbd_illum_status_get(toshiba, &time) < 0)
+ return -EIO;
+
+ return sprintf(buf, "%i\n", time >> HCI_MISC_SHIFT);
+}
+
+static ssize_t toshiba_touchpad_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ int state;
+
+ /* Set the TouchPad on/off, 0 - Disable | 1 - Enable */
+ if (sscanf(buf, "%i", &state) == 1 && (state == 0 || state == 1)) {
+ if (toshiba_touchpad_set(toshiba, state) < 0)
+ return -EIO;
+ }
+
+ return count;
+}
+
+static ssize_t toshiba_touchpad_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 state;
+ int ret;
+
+ ret = toshiba_touchpad_get(toshiba, &state);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%i\n", state);
+}
+
+static ssize_t toshiba_position_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 xyval, zval, tmp;
+ u16 x, y, z;
+ int ret;
+
+ xyval = zval = 0;
+ ret = toshiba_accelerometer_get(toshiba, &xyval, &zval);
+ if (ret < 0)
+ return ret;
+
+ x = xyval & HCI_ACCEL_MASK;
+ tmp = xyval >> HCI_MISC_SHIFT;
+ y = tmp & HCI_ACCEL_MASK;
+ z = zval & HCI_ACCEL_MASK;
+
+ return sprintf(buf, "%d %d %d\n", x, y, z);
+}
+
+static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR,
+ toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store);
+static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR,
+ toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store);
+static DEVICE_ATTR(touchpad, S_IRUGO | S_IWUSR,
+ toshiba_touchpad_show, toshiba_touchpad_store);
+static DEVICE_ATTR(position, S_IRUGO, toshiba_position_show, NULL);
+
+static struct attribute *toshiba_attributes[] = {
+ &dev_attr_kbd_backlight_mode.attr,
+ &dev_attr_kbd_backlight_timeout.attr,
+ &dev_attr_touchpad.attr,
+ &dev_attr_position.attr,
+ NULL,
+};
+
+static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
+ struct attribute *attr, int idx)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct toshiba_acpi_dev *drv = dev_get_drvdata(dev);
+ bool exists = true;
+
+ if (attr == &dev_attr_kbd_backlight_mode.attr)
+ exists = (drv->kbd_illum_supported) ? true : false;
+ else if (attr == &dev_attr_kbd_backlight_timeout.attr)
+ exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false;
+ else if (attr == &dev_attr_touchpad.attr)
+ exists = (drv->touchpad_supported) ? true : false;
+ else if (attr == &dev_attr_position.attr)
+ exists = (drv->accelerometer_supported) ? true : false;
+
+ return exists ? attr->mode : 0;
+}
+
+static struct attribute_group toshiba_attr_group = {
+ .is_visible = toshiba_sysfs_is_visible,
+ .attrs = toshiba_attributes,
+};
+
static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
{
@@ -1106,6 +1574,10 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
remove_toshiba_proc_entries(dev);
+ if (dev->sysfs_created)
+ sysfs_remove_group(&dev->acpi_dev->dev.kobj,
+ &toshiba_attr_group);
+
if (dev->ntfy_supported) {
i8042_remove_filter(toshiba_acpi_i8042_filter);
cancel_work_sync(&dev->hotkey_work);
@@ -1127,6 +1599,12 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
if (dev->illumination_supported)
led_classdev_unregister(&dev->led_dev);
+ if (dev->kbd_led_registered)
+ led_classdev_unregister(&dev->kbd_led);
+
+ if (dev->eco_supported)
+ led_classdev_unregister(&dev->eco_led);
+
if (toshiba_acpi)
toshiba_acpi = NULL;
@@ -1172,6 +1650,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
dev->acpi_dev = acpi_dev;
dev->method_hci = hci_method;
acpi_dev->driver_data = dev;
+ dev_set_drvdata(&acpi_dev->dev, dev);
if (toshiba_acpi_setup_keyboard(dev))
pr_info("Unable to activate hotkeys\n");
@@ -1212,6 +1691,40 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
dev->illumination_supported = 1;
}
+ if (toshiba_eco_mode_available(dev)) {
+ dev->eco_led.name = "toshiba::eco_mode";
+ dev->eco_led.max_brightness = 1;
+ dev->eco_led.brightness_set = toshiba_eco_mode_set_status;
+ dev->eco_led.brightness_get = toshiba_eco_mode_get_status;
+ if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led))
+ dev->eco_supported = 1;
+ }
+
+ ret = toshiba_kbd_illum_status_get(dev, &dummy);
+ if (!ret) {
+ dev->kbd_time = dummy >> HCI_MISC_SHIFT;
+ dev->kbd_mode = dummy & 0x07;
+ }
+ dev->kbd_illum_supported = !ret;
+ /*
+ * Only register the LED if KBD illumination is supported
+ * and the keyboard backlight operation mode is set to FN-Z
+ */
+ if (dev->kbd_illum_supported && dev->kbd_mode == SCI_KBD_MODE_FNZ) {
+ dev->kbd_led.name = "toshiba::kbd_backlight";
+ dev->kbd_led.max_brightness = 1;
+ dev->kbd_led.brightness_set = toshiba_kbd_backlight_set;
+ dev->kbd_led.brightness_get = toshiba_kbd_backlight_get;
+ if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led))
+ dev->kbd_led_registered = 1;
+ }
+
+ ret = toshiba_touchpad_get(dev, &dummy);
+ dev->touchpad_supported = !ret;
+
+ ret = toshiba_accelerometer_supported(dev);
+ dev->accelerometer_supported = !ret;
+
/* Determine whether or not BIOS supports fan and video interfaces */
ret = get_video_status(dev, &dummy);
@@ -1220,6 +1733,14 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
ret = get_fan_status(dev, &dummy);
dev->fan_supported = !ret;
+ ret = sysfs_create_group(&dev->acpi_dev->dev.kobj,
+ &toshiba_attr_group);
+ if (ret) {
+ dev->sysfs_created = 0;
+ goto error;
+ }
+ dev->sysfs_created = !ret;
+
create_toshiba_proc_entries(dev);
toshiba_acpi = dev;