Merge tag 'jfs-3.16' of git://github.com/kleikamp/linux-shaggy into next
[cascardo/linux.git] / drivers / platform / x86 / toshiba_acpi.c
index c8e8bfb..46473ca 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,7 +78,7 @@ 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.
  */
@@ -97,6 +98,8 @@ MODULE_LICENSE("GPL");
 #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
@@ -111,13 +114,16 @@ 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
@@ -162,6 +168,7 @@ struct toshiba_acpi_dev {
        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;
@@ -449,7 +456,7 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
 
        return state ? LED_FULL : LED_OFF;
 }
+
 /* KBD Illumination */
 static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time)
 {
@@ -532,7 +539,7 @@ static void toshiba_kbd_backlight_set(struct led_classdev *cdev,
                return;
        }
 }
+
 /* TouchPad support */
 static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state)
 {
@@ -625,6 +632,52 @@ static void toshiba_eco_mode_set_status(struct led_classdev *cdev,
        }
 }
 
+/* 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;
+       }
+
+       *xy = out[2];
+       *z = out[4];
+
+       return 0;
+}
+
 /* Bluetooth rfkill handlers */
 
 static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present)
@@ -1147,7 +1200,7 @@ static const struct backlight_ops toshiba_backlight_data = {
        .get_brightness = get_lcd_brightness,
        .update_status  = set_lcd_status,
 };
+
 /*
  * Sysfs files
  */
@@ -1227,7 +1280,7 @@ static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev,
 
        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)
@@ -1258,17 +1311,40 @@ static ssize_t toshiba_touchpad_show(struct device *dev,
        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,
 };
 
@@ -1285,6 +1361,8 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
                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;
 }
@@ -1495,7 +1573,7 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
        struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
 
        remove_toshiba_proc_entries(dev);
+
        if (dev->sysfs_created)
                sysfs_remove_group(&dev->acpi_dev->dev.kobj,
                                   &toshiba_attr_group);
@@ -1520,10 +1598,10 @@ 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);
 
@@ -1612,7 +1690,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
                if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev))
                        dev->illumination_supported = 1;
        }
+
        if (toshiba_eco_mode_available(dev)) {
                dev->eco_led.name = "toshiba::eco_mode";
                dev->eco_led.max_brightness = 1;
@@ -1621,7 +1699,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
                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;
@@ -1640,10 +1718,13 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
                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);