]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blobdiff - drivers/platform/x86/toshiba_acpi.c
Merge tag 'pm+acpi-3.15-rc1-3' of git://git.kernel.org/pub/scm/linux/kernel/git/rafae...
[mirror_ubuntu-hirsute-kernel.git] / drivers / platform / x86 / toshiba_acpi.c
index 90dd7645a9e504449fd837ec1ad280775fc84ae9..46473ca7566bcd1ab1e1f286985be68c369fb250 100644 (file)
@@ -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;