]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge tag 'platform-drivers-x86-v4.8-1' of git://git.infradead.org/users/dvhart/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 27 Jul 2016 00:23:08 +0000 (17:23 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 27 Jul 2016 00:23:08 +0000 (17:23 -0700)
Pull x8 platform driver updates from Darren Hart:
 "Several new quirks and tweaks for new platforms to existing laptop
  drivers.  A new ACPI virtual power button driver, similar to the
  intel-hid driver.  A rework of the dell keymap, using a single sparse
  keymap for all machines.  A few fixes and cleanups.

  Summary:

  intel-vbtn:
   - new driver for Intel Virtual Button

  intel_pmc_core:
   - Convert to DEFINE_DEBUGFS_ATTRIBUTE

  fujitsu-laptop:
   - Rework brightness of eco led

  asus-wmi:
   - Add quirk_no_rfkill_wapf4 for the Asus X456UA
   - Add quirk_no_rfkill_wapf4 for the Asus X456UF
   - Add quirk_no_rfkill for the Asus Z550MA
   - Add quirk_no_rfkill for the Asus U303LB
   - Add quirk_no_rfkill for the Asus N552VW
   - Create quirk for airplane_mode LED
   - Add ambient light sensor toggle key

  asus-wireless:
   - Toggle airplane mode LED

  intel_telemetry:
   - Remove Monitor MWAIT feature dependency

  intel-hid:
   - Remove duplicated acpi_remove_notify_handler

  fujitsu-laptop:
   - Add support for eco LED
   - Support touchpad toggle hotkey on Skylake-based models
   - Remove unused macros
   - Use module name in debug messages

  hp-wmi:
   - Fix wifi cannot be hard-unblocked

  toshiba_acpi:
   - Bump driver version and update copyright year
   - Remove the position sysfs entry
   - Add IIO interface for accelerometer axis data

  dell-wmi:
   - Add a WMI event code for display on/off
   - Generate one sparse keymap for all machines
   - Add information about other WMI event codes
   - Sort WMI event codes and update comments
   - Ignore WMI event code 0xe045"

* tag 'platform-drivers-x86-v4.8-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86: (26 commits)
  intel-vbtn: new driver for Intel Virtual Button
  intel_pmc_core: Convert to DEFINE_DEBUGFS_ATTRIBUTE
  fujitsu-laptop: Rework brightness of eco led
  asus-wmi: Add quirk_no_rfkill_wapf4 for the Asus X456UA
  asus-wmi: Add quirk_no_rfkill_wapf4 for the Asus X456UF
  asus-wmi: Add quirk_no_rfkill for the Asus Z550MA
  asus-wmi: Add quirk_no_rfkill for the Asus U303LB
  asus-wmi: Add quirk_no_rfkill for the Asus N552VW
  asus-wmi: Create quirk for airplane_mode LED
  asus-wireless: Toggle airplane mode LED
  intel_telemetry: Remove Monitor MWAIT feature dependency
  intel-hid: Remove duplicated acpi_remove_notify_handler
  asus-wmi: Add ambient light sensor toggle key
  fujitsu-laptop: Add support for eco LED
  fujitsu-laptop: Support touchpad toggle hotkey on Skylake-based models
  fujitsu-laptop: Remove unused macros
  fujitsu-laptop: Use module name in debug messages
  hp-wmi: Fix wifi cannot be hard-unblocked
  toshiba_acpi: Bump driver version and update copyright year
  toshiba_acpi: Remove the position sysfs entry
  ...

17 files changed:
MAINTAINERS
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/asus-nb-wmi.c
drivers/platform/x86/asus-wireless.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/asus-wmi.h
drivers/platform/x86/dell-wmi.c
drivers/platform/x86/fujitsu-laptop.c
drivers/platform/x86/hp-wmi.c
drivers/platform/x86/intel-hid.c
drivers/platform/x86/intel-vbtn.c [new file with mode: 0644]
drivers/platform/x86/intel_pmc_core.c
drivers/platform/x86/intel_pmc_core.h
drivers/platform/x86/intel_telemetry_debugfs.c
drivers/platform/x86/intel_telemetry_pltdrv.c
drivers/platform/x86/toshiba_acpi.c

index 8312586765784278bd862250ae65356ac122da33..4e1e4e94a83aeebcceffe0afdd1b436a52c09f18 100644 (file)
@@ -5921,6 +5921,12 @@ L:       platform-driver-x86@vger.kernel.org
 S:     Maintained
 F:     drivers/platform/x86/intel-hid.c
 
+INTEL VIRTUAL BUTTON DRIVER
+M:     AceLan Kao <acelan.kao@canonical.com>
+L:     platform-driver-x86@vger.kernel.org
+S:     Maintained
+F:     drivers/platform/x86/intel-vbtn.c
+
 INTEL IDLE DRIVER
 M:     Len Brown <lenb@kernel.org>
 L:     linux-pm@vger.kernel.org
index 3ec0025d19e7e0e069a793cfe11e5452c2661831..81b8dcca8891dc00ca5d0cb48c28329d25e70887 100644 (file)
@@ -603,6 +603,8 @@ config ASUS_WIRELESS
        tristate "Asus Wireless Radio Control Driver"
        depends on ACPI
        depends on INPUT
+       select NEW_LEDS
+       select LEDS_CLASS
        ---help---
          The Asus Wireless Radio Control handles the airplane mode hotkey
          present on some Asus laptops.
@@ -668,6 +670,7 @@ config ACPI_TOSHIBA
        depends on SERIO_I8042 || SERIO_I8042 = n
        depends on ACPI_VIDEO || ACPI_VIDEO = n
        depends on RFKILL || RFKILL = n
+       depends on IIO
        select INPUT_POLLDEV
        select INPUT_SPARSEKMAP
        ---help---
@@ -770,6 +773,18 @@ config INTEL_HID_EVENT
          To compile this driver as a module, choose M here: the module will
          be called intel_hid.
 
+config INTEL_VBTN
+       tristate "INTEL VIRTUAL BUTTON"
+       depends on ACPI
+       depends on INPUT
+       select INPUT_SPARSEKMAP
+       help
+         This driver provides support for the Intel Virtual Button interface.
+         Some laptops require this driver for power button support.
+
+         To compile this driver as a module, choose M here: the module will
+         be called intel_vbtn.
+
 config INTEL_SCU_IPC
        bool "Intel SCU IPC Support"
        depends on X86_INTEL_MID
index 9b11b4073e033f66b59d05d9dce01442aeee3775..2efa86d2a1a7df6cb4a3c5861c0d71e0ada39168 100644 (file)
@@ -44,6 +44,7 @@ obj-$(CONFIG_TOSHIBA_BT_RFKILL)       += toshiba_bluetooth.o
 obj-$(CONFIG_TOSHIBA_HAPS)     += toshiba_haps.o
 obj-$(CONFIG_TOSHIBA_WMI)      += toshiba-wmi.o
 obj-$(CONFIG_INTEL_HID_EVENT)  += intel-hid.o
+obj-$(CONFIG_INTEL_VBTN)       += intel-vbtn.o
 obj-$(CONFIG_INTEL_SCU_IPC)    += intel_scu_ipc.o
 obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
 obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
index 091ca7ada8fc48efe25cdd445e8728d1833398b0..adecc1c555f02a8b18eadca62c0e6eb5e7796b38 100644 (file)
@@ -78,6 +78,15 @@ static struct quirk_entry quirk_asus_x200ca = {
        .wapf = 2,
 };
 
+static struct quirk_entry quirk_no_rfkill = {
+       .no_rfkill = true,
+};
+
+static struct quirk_entry quirk_no_rfkill_wapf4 = {
+       .wapf = 4,
+       .no_rfkill = true,
+};
+
 static int dmi_matched(const struct dmi_system_id *dmi)
 {
        quirks = dmi->driver_data;
@@ -133,7 +142,7 @@ static const struct dmi_system_id asus_quirks[] = {
                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "X456UA"),
                },
-               .driver_data = &quirk_asus_wapf4,
+               .driver_data = &quirk_no_rfkill_wapf4,
        },
        {
                .callback = dmi_matched,
@@ -142,7 +151,7 @@ static const struct dmi_system_id asus_quirks[] = {
                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "X456UF"),
                },
-               .driver_data = &quirk_asus_wapf4,
+               .driver_data = &quirk_no_rfkill_wapf4,
        },
        {
                .callback = dmi_matched,
@@ -306,6 +315,42 @@ static const struct dmi_system_id asus_quirks[] = {
                },
                .driver_data = &quirk_asus_x200ca,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. X555UB",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X555UB"),
+               },
+               .driver_data = &quirk_no_rfkill,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. N552VW",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "N552VW"),
+               },
+               .driver_data = &quirk_no_rfkill,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. U303LB",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "U303LB"),
+               },
+               .driver_data = &quirk_no_rfkill,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. Z550MA",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Z550MA"),
+               },
+               .driver_data = &quirk_no_rfkill,
+       },
        {},
 };
 
@@ -356,6 +401,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
        { KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */
        { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
        { KE_IGNORE, 0x6E, },  /* Low Battery notification */
+       { KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */
        { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
        { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
        { KE_KEY, 0x82, { KEY_CAMERA } },
index 9ec721e26532531e69d5a1decb5da4926b402282..9f31bc1a47d01b9c88072f60444d53917a63af84 100644 (file)
 #include <linux/acpi.h>
 #include <linux/input.h>
 #include <linux/pci_ids.h>
+#include <linux/leds.h>
+
+#define ASUS_WIRELESS_LED_STATUS 0x2
+#define ASUS_WIRELESS_LED_OFF 0x4
+#define ASUS_WIRELESS_LED_ON 0x5
 
 struct asus_wireless_data {
        struct input_dev *idev;
+       struct acpi_device *adev;
+       struct workqueue_struct *wq;
+       struct work_struct led_work;
+       struct led_classdev led;
+       int led_state;
 };
 
+static u64 asus_wireless_method(acpi_handle handle, const char *method,
+                               int param)
+{
+       struct acpi_object_list p;
+       union acpi_object obj;
+       acpi_status s;
+       u64 ret;
+
+       acpi_handle_debug(handle, "Evaluating method %s, parameter %#x\n",
+                         method, param);
+       obj.type = ACPI_TYPE_INTEGER;
+       obj.integer.value = param;
+       p.count = 1;
+       p.pointer = &obj;
+
+       s = acpi_evaluate_integer(handle, (acpi_string) method, &p, &ret);
+       if (ACPI_FAILURE(s))
+               acpi_handle_err(handle,
+                               "Failed to eval method %s, param %#x (%d)\n",
+                               method, param, s);
+       acpi_handle_debug(handle, "%s returned %#x\n", method, (uint) ret);
+       return ret;
+}
+
+static enum led_brightness led_state_get(struct led_classdev *led)
+{
+       struct asus_wireless_data *data;
+       int s;
+
+       data = container_of(led, struct asus_wireless_data, led);
+       s = asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
+                                ASUS_WIRELESS_LED_STATUS);
+       if (s == ASUS_WIRELESS_LED_ON)
+               return LED_FULL;
+       return LED_OFF;
+}
+
+static void led_state_update(struct work_struct *work)
+{
+       struct asus_wireless_data *data;
+
+       data = container_of(work, struct asus_wireless_data, led_work);
+       asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
+                            data->led_state);
+}
+
+static void led_state_set(struct led_classdev *led,
+                                 enum led_brightness value)
+{
+       struct asus_wireless_data *data;
+
+       data = container_of(led, struct asus_wireless_data, led);
+       data->led_state = value == LED_OFF ? ASUS_WIRELESS_LED_OFF :
+                                            ASUS_WIRELESS_LED_ON;
+       queue_work(data->wq, &data->led_work);
+}
+
 static void asus_wireless_notify(struct acpi_device *adev, u32 event)
 {
        struct asus_wireless_data *data = acpi_driver_data(adev);
@@ -37,6 +104,7 @@ static void asus_wireless_notify(struct acpi_device *adev, u32 event)
 static int asus_wireless_add(struct acpi_device *adev)
 {
        struct asus_wireless_data *data;
+       int err;
 
        data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL);
        if (!data)
@@ -52,11 +120,32 @@ static int asus_wireless_add(struct acpi_device *adev)
        data->idev->id.vendor = PCI_VENDOR_ID_ASUSTEK;
        set_bit(EV_KEY, data->idev->evbit);
        set_bit(KEY_RFKILL, data->idev->keybit);
-       return input_register_device(data->idev);
+       err = input_register_device(data->idev);
+       if (err)
+               return err;
+
+       data->adev = adev;
+       data->wq = create_singlethread_workqueue("asus_wireless_workqueue");
+       if (!data->wq)
+               return -ENOMEM;
+       INIT_WORK(&data->led_work, led_state_update);
+       data->led.name = "asus-wireless::airplane";
+       data->led.brightness_set = led_state_set;
+       data->led.brightness_get = led_state_get;
+       data->led.flags = LED_CORE_SUSPENDRESUME;
+       data->led.max_brightness = 1;
+       err = devm_led_classdev_register(&adev->dev, &data->led);
+       if (err)
+               destroy_workqueue(data->wq);
+       return err;
 }
 
 static int asus_wireless_remove(struct acpi_device *adev)
 {
+       struct asus_wireless_data *data = acpi_driver_data(adev);
+
+       if (data->wq)
+               destroy_workqueue(data->wq);
        return 0;
 }
 
index a26dca3640ea750becfceffc9d78aa92d643efb1..7c093a0b78bb995f687a47329011a40c7ff2a9f4 100644 (file)
@@ -2069,9 +2069,11 @@ static int asus_wmi_add(struct platform_device *pdev)
        if (err)
                goto fail_leds;
 
-       err = asus_wmi_rfkill_init(asus);
-       if (err)
-               goto fail_rfkill;
+       if (!asus->driver->quirks->no_rfkill) {
+               err = asus_wmi_rfkill_init(asus);
+               if (err)
+                       goto fail_rfkill;
+       }
 
        /* Some Asus desktop boards export an acpi-video backlight interface,
           stop this from showing up */
index 4da4c8bafe70e926989bfd134fcb866167212646..5de1df510ebd8c3ed39db046e70daf5155a4a7ce 100644 (file)
@@ -38,6 +38,7 @@ struct key_entry;
 struct asus_wmi;
 
 struct quirk_entry {
+       bool no_rfkill;
        bool hotplug_wireless;
        bool scalar_panel_brightness;
        bool store_backlight_power;
index 15c6f1191aec3f1aa7751b3b935d2d871bc599dd..d2bc092defd77732f780acb531931f165d440498 100644 (file)
@@ -80,66 +80,115 @@ static const struct dmi_system_id dell_wmi_smbios_list[] __initconst = {
 };
 
 /*
+ * Keymap for WMI events of type 0x0000
+ *
  * Certain keys are flagged as KE_IGNORE. All of these are either
  * notifications (rather than requests for change) or are also sent
  * via the keyboard controller so should not be sent again.
  */
-
-static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
+static const struct key_entry dell_wmi_keymap_type_0000[] __initconst = {
        { KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },
 
-       { KE_KEY, 0xe045, { KEY_PROG1 } },
-       { KE_KEY, 0xe009, { KEY_EJECTCD } },
-
-       /* These also contain the brightness level at offset 6 */
-       { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
-       { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
+       /* Key code is followed by brightness level */
+       { KE_KEY,    0xe005, { KEY_BRIGHTNESSDOWN } },
+       { KE_KEY,    0xe006, { KEY_BRIGHTNESSUP } },
 
        /* Battery health status button */
-       { KE_KEY, 0xe007, { KEY_BATTERY } },
+       { KE_KEY,    0xe007, { KEY_BATTERY } },
 
-       /* Radio devices state change */
+       /* Radio devices state change, key code is followed by other values */
        { KE_IGNORE, 0xe008, { KEY_RFKILL } },
 
-       /* The next device is at offset 6, the active devices are at
-          offset 8 and the attached devices at offset 10 */
-       { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
+       { KE_KEY,    0xe009, { KEY_EJECTCD } },
 
+       /* Key code is followed by: next, active and attached devices */
+       { KE_KEY,    0xe00b, { KEY_SWITCHVIDEOMODE } },
+
+       /* Key code is followed by keyboard illumination level */
        { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } },
 
        /* BIOS error detected */
        { KE_IGNORE, 0xe00d, { KEY_RESERVED } },
 
+       /* Unknown, defined in ACPI DSDT */
+       /* { KE_IGNORE, 0xe00e, { KEY_RESERVED } }, */
+
        /* Wifi Catcher */
-       { KE_KEY, 0xe011, {KEY_PROG2 } },
+       { KE_KEY,    0xe011, { KEY_PROG2 } },
 
        /* Ambient light sensor toggle */
        { KE_IGNORE, 0xe013, { KEY_RESERVED } },
 
        { KE_IGNORE, 0xe020, { KEY_MUTE } },
 
+       /* Unknown, defined in ACPI DSDT */
+       /* { KE_IGNORE, 0xe023, { KEY_RESERVED } }, */
+
+       /* Untested, Dell Instant Launch key on Inspiron 7520 */
+       /* { KE_IGNORE, 0xe024, { KEY_RESERVED } }, */
+
        /* Dell Instant Launch key */
-       { KE_KEY, 0xe025, { KEY_PROG4 } },
-       { KE_KEY, 0xe029, { KEY_PROG4 } },
+       { KE_KEY,    0xe025, { KEY_PROG4 } },
 
        /* Audio panel key */
        { KE_IGNORE, 0xe026, { KEY_RESERVED } },
 
+       /* LCD Display On/Off Control key */
+       { KE_KEY,    0xe027, { KEY_DISPLAYTOGGLE } },
+
+       /* Untested, Multimedia key on Dell Vostro 3560 */
+       /* { KE_IGNORE, 0xe028, { KEY_RESERVED } }, */
+
+       /* Dell Instant Launch key */
+       { KE_KEY,    0xe029, { KEY_PROG4 } },
+
+       /* Untested, Windows Mobility Center button on Inspiron 7520 */
+       /* { KE_IGNORE, 0xe02a, { KEY_RESERVED } }, */
+
+       /* Unknown, defined in ACPI DSDT */
+       /* { KE_IGNORE, 0xe02b, { KEY_RESERVED } }, */
+
+       /* Untested, Dell Audio With Preset Switch button on Inspiron 7520 */
+       /* { KE_IGNORE, 0xe02c, { KEY_RESERVED } }, */
+
        { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
        { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
        { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
        { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
        { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
+
+       /* NIC Link is Up */
+       { KE_IGNORE, 0xe043, { KEY_RESERVED } },
+
+       /* NIC Link is Down */
+       { KE_IGNORE, 0xe044, { KEY_RESERVED } },
+
+       /*
+        * This entry is very suspicious!
+        * Originally Matthew Garrett created this dell-wmi driver specially for
+        * "button with a picture of a battery" which has event code 0xe045.
+        * Later Mario Limonciello from Dell told us that event code 0xe045 is
+        * reported by Num Lock and should be ignored because key is send also
+        * by keyboard controller.
+        * So for now we will ignore this event to prevent potential double
+        * Num Lock key press.
+        */
        { KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
+
+       /* Scroll lock and also going to tablet mode on portable devices */
        { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
+
+       /* Untested, going from tablet mode on portable devices */
+       /* { KE_IGNORE, 0xe047, { KEY_RESERVED } }, */
+
+       /* Dell Support Center key */
+       { KE_IGNORE, 0xe06e, { KEY_RESERVED } },
+
        { KE_IGNORE, 0xe0f7, { KEY_MUTE } },
        { KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } },
        { KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } },
-       { KE_END, 0 }
 };
 
-static bool dell_new_hk_type;
-
 struct dell_bios_keymap_entry {
        u16 scancode;
        u16 keycode;
@@ -153,6 +202,7 @@ struct dell_bios_hotkey_table {
 
 struct dell_dmi_results {
        int err;
+       int keymap_size;
        struct key_entry *keymap;
 };
 
@@ -201,10 +251,12 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
 };
 
 /*
+ * Keymap for WMI events of type 0x0010
+ *
  * These are applied if the 0xB2 DMI hotkey table is present and doesn't
  * override them.
  */
-static const struct key_entry dell_wmi_extra_keymap[] __initconst = {
+static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = {
        /* Fn-lock */
        { KE_IGNORE, 0x151, { KEY_RESERVED } },
 
@@ -224,21 +276,39 @@ static const struct key_entry dell_wmi_extra_keymap[] __initconst = {
        { KE_IGNORE, 0x155, { KEY_RESERVED } },
 };
 
+/*
+ * Keymap for WMI events of type 0x0011
+ */
+static const struct key_entry dell_wmi_keymap_type_0011[] __initconst = {
+       /* Battery unplugged */
+       { KE_IGNORE, 0xfff0, { KEY_RESERVED } },
+
+       /* Battery inserted */
+       { KE_IGNORE, 0xfff1, { KEY_RESERVED } },
+
+       /* Keyboard backlight level changed */
+       { KE_IGNORE, 0x01e1, { KEY_RESERVED } },
+       { KE_IGNORE, 0x02ea, { KEY_RESERVED } },
+       { KE_IGNORE, 0x02eb, { KEY_RESERVED } },
+       { KE_IGNORE, 0x02ec, { KEY_RESERVED } },
+       { KE_IGNORE, 0x02f6, { KEY_RESERVED } },
+};
+
 static struct input_dev *dell_wmi_input_dev;
 
-static void dell_wmi_process_key(int reported_key)
+static void dell_wmi_process_key(int type, int code)
 {
        const struct key_entry *key;
 
        key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
-                                               reported_key);
+                                               (type << 16) | code);
        if (!key) {
-               pr_info("Unknown key with scancode 0x%x pressed\n",
-                       reported_key);
+               pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n",
+                       type, code);
                return;
        }
 
-       pr_debug("Key %x pressed\n", reported_key);
+       pr_debug("Key with type 0x%04x and code 0x%04x pressed\n", type, code);
 
        /* Don't report brightness notifications that will also come via ACPI */
        if ((key->keycode == KEY_BRIGHTNESSUP ||
@@ -246,7 +316,7 @@ static void dell_wmi_process_key(int reported_key)
            acpi_video_handles_brightness_key_presses())
                return;
 
-       if (reported_key == 0xe025 && !wmi_requires_smbios_request)
+       if (type == 0x0000 && code == 0xe025 && !wmi_requires_smbios_request)
                return;
 
        sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
@@ -284,18 +354,6 @@ static void dell_wmi_notify(u32 value, void *context)
 
        buffer_entry = (u16 *)obj->buffer.pointer;
        buffer_size = obj->buffer.length/2;
-
-       if (!dell_new_hk_type) {
-               if (buffer_size >= 3 && buffer_entry[1] == 0x0)
-                       dell_wmi_process_key(buffer_entry[2]);
-               else if (buffer_size >= 2)
-                       dell_wmi_process_key(buffer_entry[1]);
-               else
-                       pr_info("Received unknown WMI event\n");
-               kfree(obj);
-               return;
-       }
-
        buffer_end = buffer_entry + buffer_size;
 
        /*
@@ -330,62 +388,18 @@ static void dell_wmi_notify(u32 value, void *context)
                pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry);
 
                switch (buffer_entry[1]) {
-               case 0x00:
-                       for (i = 2; i < len; ++i) {
-                               switch (buffer_entry[i]) {
-                               case 0xe043:
-                                       /* NIC Link is Up */
-                                       pr_debug("NIC Link is Up\n");
-                                       break;
-                               case 0xe044:
-                                       /* NIC Link is Down */
-                                       pr_debug("NIC Link is Down\n");
-                                       break;
-                               case 0xe045:
-                                       /* Unknown event but defined in DSDT */
-                               default:
-                                       /* Unknown event */
-                                       pr_info("Unknown WMI event type 0x00: "
-                                               "0x%x\n", (int)buffer_entry[i]);
-                                       break;
-                               }
-                       }
+               case 0x0000: /* One key pressed or event occurred */
+                       if (len > 2)
+                               dell_wmi_process_key(0x0000, buffer_entry[2]);
+                       /* Other entries could contain additional information */
                        break;
-               case 0x10:
-                       /* Keys pressed */
+               case 0x0010: /* Sequence of keys pressed */
+               case 0x0011: /* Sequence of events occurred */
                        for (i = 2; i < len; ++i)
-                               dell_wmi_process_key(buffer_entry[i]);
-                       break;
-               case 0x11:
-                       for (i = 2; i < len; ++i) {
-                               switch (buffer_entry[i]) {
-                               case 0xfff0:
-                                       /* Battery unplugged */
-                                       pr_debug("Battery unplugged\n");
-                                       break;
-                               case 0xfff1:
-                                       /* Battery inserted */
-                                       pr_debug("Battery inserted\n");
-                                       break;
-                               case 0x01e1:
-                               case 0x02ea:
-                               case 0x02eb:
-                               case 0x02ec:
-                               case 0x02f6:
-                                       /* Keyboard backlight level changed */
-                                       pr_debug("Keyboard backlight level "
-                                                "changed\n");
-                                       break;
-                               default:
-                                       /* Unknown event */
-                                       pr_info("Unknown WMI event type 0x11: "
-                                               "0x%x\n", (int)buffer_entry[i]);
-                                       break;
-                               }
-                       }
+                               dell_wmi_process_key(buffer_entry[1],
+                                                    buffer_entry[i]);
                        break;
-               default:
-                       /* Unknown event */
+               default: /* Unknown event */
                        pr_info("Unknown WMI event type 0x%x\n",
                                (int)buffer_entry[1]);
                        break;
@@ -410,7 +424,6 @@ static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)
 }
 
 static void __init handle_dmi_entry(const struct dmi_header *dm,
-
                                    void *opaque)
 
 {
@@ -418,7 +431,6 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
        struct dell_bios_hotkey_table *table;
        int hotkey_num, i, pos = 0;
        struct key_entry *keymap;
-       int num_bios_keys;
 
        if (results->err || results->keymap)
                return;         /* We already found the hotkey table. */
@@ -442,8 +454,7 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
                return;
        }
 
-       keymap = kcalloc(hotkey_num + ARRAY_SIZE(dell_wmi_extra_keymap) + 1,
-                        sizeof(struct key_entry), GFP_KERNEL);
+       keymap = kcalloc(hotkey_num, sizeof(struct key_entry), GFP_KERNEL);
        if (!keymap) {
                results->err = -ENOMEM;
                return;
@@ -480,31 +491,15 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
                pos++;
        }
 
-       num_bios_keys = pos;
-
-       for (i = 0; i < ARRAY_SIZE(dell_wmi_extra_keymap); i++) {
-               const struct key_entry *entry = &dell_wmi_extra_keymap[i];
-
-               /*
-                * Check if we've already found this scancode.  This takes
-                * quadratic time, but it doesn't matter unless the list
-                * of extra keys gets very long.
-                */
-               if (!have_scancode(entry->code, keymap, num_bios_keys)) {
-                       keymap[pos] = *entry;
-                       pos++;
-               }
-       }
-
-       keymap[pos].type = KE_END;
-
        results->keymap = keymap;
+       results->keymap_size = pos;
 }
 
 static int __init dell_wmi_input_setup(void)
 {
        struct dell_dmi_results dmi_results = {};
-       int err;
+       struct key_entry *keymap;
+       int err, i, pos = 0;
 
        dell_wmi_input_dev = input_allocate_device();
        if (!dell_wmi_input_dev)
@@ -528,21 +523,71 @@ static int __init dell_wmi_input_setup(void)
                goto err_free_dev;
        }
 
-       if (dmi_results.keymap) {
-               dell_new_hk_type = true;
+       keymap = kcalloc(dmi_results.keymap_size +
+                        ARRAY_SIZE(dell_wmi_keymap_type_0000) +
+                        ARRAY_SIZE(dell_wmi_keymap_type_0010) +
+                        ARRAY_SIZE(dell_wmi_keymap_type_0011) +
+                        1,
+                        sizeof(struct key_entry), GFP_KERNEL);
+       if (!keymap) {
+               kfree(dmi_results.keymap);
+               err = -ENOMEM;
+               goto err_free_dev;
+       }
+
+       /* Append table with events of type 0x0010 which comes from DMI */
+       for (i = 0; i < dmi_results.keymap_size; i++) {
+               keymap[pos] = dmi_results.keymap[i];
+               keymap[pos].code |= (0x0010 << 16);
+               pos++;
+       }
+
+       kfree(dmi_results.keymap);
 
-               err = sparse_keymap_setup(dell_wmi_input_dev,
-                                         dmi_results.keymap, NULL);
+       /* Append table with extra events of type 0x0010 which are not in DMI */
+       for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0010); i++) {
+               const struct key_entry *entry = &dell_wmi_keymap_type_0010[i];
 
                /*
-                * Sparse keymap library makes a copy of keymap so we
-                * don't need the original one that was allocated.
+                * Check if we've already found this scancode.  This takes
+                * quadratic time, but it doesn't matter unless the list
+                * of extra keys gets very long.
                 */
-               kfree(dmi_results.keymap);
-       } else {
-               err = sparse_keymap_setup(dell_wmi_input_dev,
-                                         dell_wmi_legacy_keymap, NULL);
+               if (dmi_results.keymap_size &&
+                   have_scancode(entry->code | (0x0010 << 16),
+                                 keymap, dmi_results.keymap_size)
+                  )
+                       continue;
+
+               keymap[pos] = *entry;
+               keymap[pos].code |= (0x0010 << 16);
+               pos++;
+       }
+
+       /* Append table with events of type 0x0011 */
+       for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0011); i++) {
+               keymap[pos] = dell_wmi_keymap_type_0011[i];
+               keymap[pos].code |= (0x0011 << 16);
+               pos++;
        }
+
+       /*
+        * Now append also table with "legacy" events of type 0x0000. Some of
+        * them are reported also on laptops which have scancodes in DMI.
+        */
+       for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0000); i++) {
+               keymap[pos] = dell_wmi_keymap_type_0000[i];
+               pos++;
+       }
+
+       keymap[pos].type = KE_END;
+
+       err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
+       /*
+        * Sparse keymap library makes a copy of keymap so we don't need the
+        * original one that was allocated.
+        */
+       kfree(keymap);
        if (err)
                goto err_free_dev;
 
index ce41bc34288df5fa159c35b58237df5b2b500819..61f39abf5dc8f24b738a2dafc5117b40a2521fd5 100644 (file)
@@ -88,9 +88,6 @@
 
 #define ACPI_FUJITSU_NOTIFY_CODE1     0x80
 
-#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS     0x86
-#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS     0x87
-
 /* FUNC interface - command values */
 #define FUNC_RFKILL    0x1000
 #define FUNC_LEDS      0x1001
 #define LOGOLAMP_POWERON 0x2000
 #define LOGOLAMP_ALWAYS  0x4000
 #define RADIO_LED_ON   0x20
+#define ECO_LED        0x10000
+#define ECO_LED_ON     0x80000
 #endif
 
 /* Hotkey details */
 #define RINGBUFFERSIZE 40
 
 /* Debugging */
-#define FUJLAPTOP_LOG     ACPI_FUJITSU_HID ": "
-#define FUJLAPTOP_ERR     KERN_ERR FUJLAPTOP_LOG
-#define FUJLAPTOP_NOTICE   KERN_NOTICE FUJLAPTOP_LOG
-#define FUJLAPTOP_INFO    KERN_INFO FUJLAPTOP_LOG
-#define FUJLAPTOP_DEBUG    KERN_DEBUG FUJLAPTOP_LOG
-
-#define FUJLAPTOP_DBG_ALL        0xffff
 #define FUJLAPTOP_DBG_ERROR      0x0001
 #define FUJLAPTOP_DBG_WARN       0x0002
 #define FUJLAPTOP_DBG_INFO       0x0004
 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
 #define vdbg_printk(a_dbg_level, format, arg...) \
        do { if (dbg_level & a_dbg_level) \
-               printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \
+               printk(KERN_DEBUG pr_fmt("%s: " format), __func__, ## arg); \
        } while (0)
 #else
 #define vdbg_printk(a_dbg_level, format, arg...) \
@@ -176,6 +168,7 @@ struct fujitsu_hotkey_t {
        int logolamp_registered;
        int kblamps_registered;
        int radio_led_registered;
+       int eco_led_registered;
 };
 
 static struct fujitsu_hotkey_t *fujitsu_hotkey;
@@ -212,6 +205,16 @@ static struct led_classdev radio_led = {
  .brightness_get = radio_led_get,
  .brightness_set = radio_led_set
 };
+
+static enum led_brightness eco_led_get(struct led_classdev *cdev);
+static void eco_led_set(struct led_classdev *cdev,
+                              enum led_brightness brightness);
+
+static struct led_classdev eco_led = {
+ .name = "fujitsu::eco_led",
+ .brightness_get = eco_led_get,
+ .brightness_set = eco_led_set
+};
 #endif
 
 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
@@ -296,6 +299,18 @@ static void radio_led_set(struct led_classdev *cdev,
                call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, 0x0);
 }
 
+static void eco_led_set(struct led_classdev *cdev,
+                               enum led_brightness brightness)
+{
+       int curr;
+
+       curr = call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0);
+       if (brightness >= LED_FULL)
+               call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr | ECO_LED_ON);
+       else
+               call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr & ~ECO_LED_ON);
+}
+
 static enum led_brightness logolamp_get(struct led_classdev *cdev)
 {
        enum led_brightness brightness = LED_OFF;
@@ -330,6 +345,16 @@ static enum led_brightness radio_led_get(struct led_classdev *cdev)
 
        return brightness;
 }
+
+static enum led_brightness eco_led_get(struct led_classdev *cdev)
+{
+       enum led_brightness brightness = LED_OFF;
+
+       if (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)
+               brightness = LED_FULL;
+
+       return brightness;
+}
 #endif
 
 /* Hardware access for LCD brightness control */
@@ -856,6 +881,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
        set_bit(fujitsu->keycode3, input->keybit);
        set_bit(fujitsu->keycode4, input->keybit);
        set_bit(fujitsu->keycode5, input->keybit);
+       set_bit(KEY_TOUCHPAD_TOGGLE, input->keybit);
        set_bit(KEY_UNKNOWN, input->keybit);
 
        error = input_register_device(input);
@@ -943,6 +969,23 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
                               result);
                }
        }
+
+       /* Support for eco led is not always signaled in bit corresponding
+        * to the bit used to control the led. According to the DSDT table,
+        * bit 14 seems to indicate presence of said led as well.
+        * Confirm by testing the status.
+       */
+       if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
+          (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
+               result = led_classdev_register(&fujitsu->pf_device->dev,
+                                               &eco_led);
+               if (result == 0) {
+                       fujitsu_hotkey->eco_led_registered = 1;
+               } else {
+                       pr_err("Could not register LED handler for eco LED, error %i\n",
+                              result);
+               }
+       }
 #endif
 
        return result;
@@ -972,6 +1015,9 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device)
 
        if (fujitsu_hotkey->radio_led_registered)
                led_classdev_unregister(&radio_led);
+
+       if (fujitsu_hotkey->eco_led_registered)
+               led_classdev_unregister(&eco_led);
 #endif
 
        input_unregister_device(input);
@@ -1060,6 +1106,19 @@ static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event)
                        }
                }
 
+               /* On some models (first seen on the Skylake-based Lifebook
+                * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
+                * handled in software; its state is queried using FUNC_RFKILL
+                */
+               if ((fujitsu_hotkey->rfkill_supported & BIT(26)) &&
+                   (call_fext_func(FUNC_RFKILL, 0x1, 0x0, 0x0) & BIT(26))) {
+                       keycode = KEY_TOUCHPAD_TOGGLE;
+                       input_report_key(input, keycode, 1);
+                       input_sync(input);
+                       input_report_key(input, keycode, 0);
+                       input_sync(input);
+               }
+
                break;
        default:
                keycode = KEY_UNKNOWN;
index 6f145f2d004d86a42858dee42b371f6d9584886d..96ffda493266aa68ef57958eb482b9b0c28900ad 100644 (file)
@@ -718,6 +718,11 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device)
        if (err)
                return err;
 
+       err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, &wireless,
+                                  sizeof(wireless), 0);
+       if (err)
+               return err;
+
        if (wireless & 0x1) {
                wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
                                           RFKILL_TYPE_WLAN,
@@ -882,7 +887,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
        wwan_rfkill = NULL;
        rfkill2_count = 0;
 
-       if (hp_wmi_bios_2009_later() || hp_wmi_rfkill_setup(device))
+       if (hp_wmi_rfkill_setup(device))
                hp_wmi_rfkill2_setup(device);
 
        err = device_create_file(&device->dev, &dev_attr_display);
index a818db6aa08f22dfaf8eca3a5b02049a31363cbd..ed5874217ee76cf4364d8bbd6f8e49f26e19c215 100644 (file)
@@ -122,8 +122,8 @@ static int intel_hid_input_setup(struct platform_device *device)
        return 0;
 
 err_free_device:
-               input_free_device(priv->input_dev);
-               return ret;
+       input_free_device(priv->input_dev);
+       return ret;
 }
 
 static void intel_hid_input_destroy(struct platform_device *device)
@@ -224,7 +224,6 @@ static int intel_hid_remove(struct platform_device *device)
        acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
        intel_hid_input_destroy(device);
        intel_hid_set_enable(&device->dev, 0);
-       acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
 
        /*
         * Even if we failed to shut off the event stream, we can still
diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c
new file mode 100644 (file)
index 0000000..146d02f
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ *  Intel Virtual Button driver for Windows 8.1+
+ *
+ *  Copyright (C) 2016 AceLan Kao <acelan.kao@canonical.com>
+ *  Copyright (C) 2016 Alex Hung <alex.hung@canonical.com>
+ *
+ *  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
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("AceLan Kao");
+
+static const struct acpi_device_id intel_vbtn_ids[] = {
+       {"INT33D6", 0},
+       {"", 0},
+};
+
+/* In theory, these are HID usages. */
+static const struct key_entry intel_vbtn_keymap[] = {
+       { KE_IGNORE, 0xC0, { KEY_POWER } },     /* power key press */
+       { KE_KEY, 0xC1, { KEY_POWER } },        /* power key release */
+       { KE_END },
+};
+
+struct intel_vbtn_priv {
+       struct input_dev *input_dev;
+};
+
+static int intel_vbtn_input_setup(struct platform_device *device)
+{
+       struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
+       int ret;
+
+       priv->input_dev = input_allocate_device();
+       if (!priv->input_dev)
+               return -ENOMEM;
+
+       ret = sparse_keymap_setup(priv->input_dev, intel_vbtn_keymap, NULL);
+       if (ret)
+               goto err_free_device;
+
+       priv->input_dev->dev.parent = &device->dev;
+       priv->input_dev->name = "Intel Virtual Button driver";
+       priv->input_dev->id.bustype = BUS_HOST;
+
+       ret = input_register_device(priv->input_dev);
+       if (ret)
+               goto err_free_device;
+
+       return 0;
+
+err_free_device:
+       input_free_device(priv->input_dev);
+       return ret;
+}
+
+static void intel_vbtn_input_destroy(struct platform_device *device)
+{
+       struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
+
+       input_unregister_device(priv->input_dev);
+}
+
+static void notify_handler(acpi_handle handle, u32 event, void *context)
+{
+       struct platform_device *device = context;
+       struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
+
+       if (!sparse_keymap_report_event(priv->input_dev, event, 1, true))
+               dev_info(&device->dev, "unknown event index 0x%x\n",
+                        event);
+}
+
+static int intel_vbtn_probe(struct platform_device *device)
+{
+       acpi_handle handle = ACPI_HANDLE(&device->dev);
+       struct intel_vbtn_priv *priv;
+       acpi_status status;
+       int err;
+
+       status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
+       if (!ACPI_SUCCESS(status)) {
+               dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n");
+               return -ENODEV;
+       }
+
+       priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+       dev_set_drvdata(&device->dev, priv);
+
+       err = intel_vbtn_input_setup(device);
+       if (err) {
+               pr_err("Failed to setup Intel Virtual Button\n");
+               return err;
+       }
+
+       status = acpi_install_notify_handler(handle,
+                                            ACPI_DEVICE_NOTIFY,
+                                            notify_handler,
+                                            device);
+       if (ACPI_FAILURE(status)) {
+               err = -EBUSY;
+               goto err_remove_input;
+       }
+
+       return 0;
+
+err_remove_input:
+       intel_vbtn_input_destroy(device);
+
+       return err;
+}
+
+static int intel_vbtn_remove(struct platform_device *device)
+{
+       acpi_handle handle = ACPI_HANDLE(&device->dev);
+
+       intel_vbtn_input_destroy(device);
+       acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
+
+       /*
+        * Even if we failed to shut off the event stream, we can still
+        * safely detach from the device.
+        */
+       return 0;
+}
+
+static struct platform_driver intel_vbtn_pl_driver = {
+       .driver = {
+               .name = "intel-vbtn",
+               .acpi_match_table = intel_vbtn_ids,
+       },
+       .probe = intel_vbtn_probe,
+       .remove = intel_vbtn_remove,
+};
+MODULE_DEVICE_TABLE(acpi, intel_vbtn_ids);
+
+static acpi_status __init
+check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+       const struct acpi_device_id *ids = context;
+       struct acpi_device *dev;
+
+       if (acpi_bus_get_device(handle, &dev) != 0)
+               return AE_OK;
+
+       if (acpi_match_device_ids(dev, ids) == 0)
+               if (acpi_create_platform_device(dev))
+                       dev_info(&dev->dev,
+                                "intel-vbtn: created platform device\n");
+
+       return AE_OK;
+}
+
+static int __init intel_vbtn_init(void)
+{
+       acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+                           ACPI_UINT32_MAX, check_acpi_dev, NULL,
+                           (void *)intel_vbtn_ids, NULL);
+
+       return platform_driver_register(&intel_vbtn_pl_driver);
+}
+module_init(intel_vbtn_init);
+
+static void __exit intel_vbtn_exit(void)
+{
+       platform_driver_unregister(&intel_vbtn_pl_driver);
+}
+module_exit(intel_vbtn_exit);
index e57f923eea0b6a511f851a0d9413787edc7309d3..520b58a04daa8d898a2e7eb4277a55b7d752a5ce 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/pci.h>
-#include <linux/seq_file.h>
 
 #include <asm/cpu_device_id.h>
 #include <asm/intel-family.h>
@@ -78,30 +77,18 @@ int intel_pmc_slp_s0_counter_read(u32 *data)
 }
 EXPORT_SYMBOL_GPL(intel_pmc_slp_s0_counter_read);
 
-#if IS_ENABLED(CONFIG_DEBUG_FS)
-static int pmc_core_dev_state_show(struct seq_file *s, void *unused)
+static int pmc_core_dev_state_get(void *data, u64 *val)
 {
-       struct pmc_dev *pmcdev = s->private;
-       u32 counter_val;
+       struct pmc_dev *pmcdev = data;
+       u32 value;
 
-       counter_val = pmc_core_reg_read(pmcdev,
-                                       SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
-       seq_printf(s, "%u\n", pmc_core_adjust_slp_s0_step(counter_val));
+       value = pmc_core_reg_read(pmcdev, SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
+       *val = pmc_core_adjust_slp_s0_step(value);
 
        return 0;
 }
 
-static int pmc_core_dev_state_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, pmc_core_dev_state_show, inode->i_private);
-}
-
-static const struct file_operations pmc_core_dev_state_ops = {
-       .open           = pmc_core_dev_state_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_DEBUGFS_ATTRIBUTE(pmc_core_dev_state, pmc_core_dev_state_get, NULL, "%llu\n");
 
 static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
 {
@@ -113,12 +100,12 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
        struct dentry *dir, *file;
 
        dir = debugfs_create_dir("pmc_core", NULL);
-       if (!dir)
+       if (IS_ERR_OR_NULL(dir))
                return -ENOMEM;
 
        pmcdev->dbgfs_dir = dir;
        file = debugfs_create_file("slp_s0_residency_usec", S_IFREG | S_IRUGO,
-                                  dir, pmcdev, &pmc_core_dev_state_ops);
+                                  dir, pmcdev, &pmc_core_dev_state);
 
        if (!file) {
                pmc_core_dbgfs_unregister(pmcdev);
@@ -127,16 +114,6 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
 
        return 0;
 }
-#else
-static inline int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
-{
-       return 0;
-}
-
-static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
-{
-}
-#endif /* CONFIG_DEBUG_FS */
 
 static const struct x86_cpu_id intel_pmc_core_ids[] = {
        { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_MOBILE, X86_FEATURE_MWAIT,
@@ -183,10 +160,8 @@ static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id)
        }
 
        err = pmc_core_dbgfs_register(pmcdev);
-       if (err < 0) {
-               dev_err(&dev->dev, "PMC Core: debugfs register failed.\n");
-               return err;
-       }
+       if (err < 0)
+               dev_warn(&dev->dev, "PMC Core: debugfs register failed.\n");
 
        pmc.has_slp_s0_res = true;
        return 0;
index a9dadaf787c15223f59342863fc5b6cd6562f640..e3f671f4d12228e8ee9a11c471c5f8d68dc1a549 100644 (file)
@@ -23,6 +23,7 @@
 
 /* Sunrise Point Power Management Controller PCI Device ID */
 #define SPT_PMC_PCI_DEVICE_ID                  0x9d21
+
 #define SPT_PMC_BASE_ADDR_OFFSET               0x48
 #define SPT_PMC_SLP_S0_RES_COUNTER_OFFSET      0x13c
 #define SPT_PMC_MMIO_REG_LEN                   0x100
@@ -42,9 +43,7 @@
 struct pmc_dev {
        u32 base_addr;
        void __iomem *regbase;
-#if IS_ENABLED(CONFIG_DEBUG_FS)
        struct dentry *dbgfs_dir;
-#endif /* CONFIG_DEBUG_FS */
        bool has_slp_s0_res;
 };
 
index 815a7c5e9566f0f4f2237c08431a16f20438a97f..ef29f18b195164745bb0a67d9a0852e5b3a676bc 100644 (file)
@@ -79,7 +79,7 @@
 #define TELEM_EVT_LEN(x) (sizeof(x)/sizeof((x)[0]))
 
 #define TELEM_DEBUGFS_CPU(model, data) \
-       { X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&data}
+       { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data}
 
 #define TELEM_CHECK_AND_PARSE_EVTS(EVTID, EVTNUM, BUF, EVTLOG, EVTDAT, MASK) { \
        if (evtlog[index].telem_evtid == (EVTID)) { \
index 6d884f7d1b9f4d950d2bf5799a688a5d978e893f..6ebdbd2b04fc994e06c683ddd71a3e071cb8a8e4 100644 (file)
@@ -83,7 +83,7 @@
 #define TELEM_SET_VERBOSITY_BITS(x, y) ((x) |= ((y) << 27))
 
 #define TELEM_CPU(model, data) \
-       { X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&data }
+       { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data }
 
 enum telemetry_action {
        TELEM_UPDATE = 0,
index 01e12d221a8b9b819b2ff396f2231a0a51492b76..9d60a40d8b3fbd63a326271305a61c6f6675825a 100644 (file)
@@ -4,7 +4,7 @@
  *  Copyright (C) 2002-2004 John Belmonte
  *  Copyright (C) 2008 Philip Langdale
  *  Copyright (C) 2010 Pierre Ducroquet
- *  Copyright (C) 2014-2015 Azael Avalos
+ *  Copyright (C) 2014-2016 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
@@ -31,7 +31,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#define TOSHIBA_ACPI_VERSION   "0.23"
+#define TOSHIBA_ACPI_VERSION   "0.24"
 #define PROC_INTERFACE_VERSION 1
 
 #include <linux/kernel.h>
@@ -53,6 +53,7 @@
 #include <linux/uaccess.h>
 #include <linux/miscdevice.h>
 #include <linux/rfkill.h>
+#include <linux/iio/iio.h>
 #include <linux/toshiba.h>
 #include <acpi/video.h>
 
@@ -134,6 +135,7 @@ MODULE_LICENSE("GPL");
 
 /* Field definitions */
 #define HCI_ACCEL_MASK                 0x7fff
+#define HCI_ACCEL_DIRECTION_MASK       0x8000
 #define HCI_HOTKEY_DISABLE             0x0b
 #define HCI_HOTKEY_ENABLE              0x09
 #define HCI_HOTKEY_SPECIAL_FUNCTIONS   0x10
@@ -178,6 +180,7 @@ struct toshiba_acpi_dev {
        struct led_classdev eco_led;
        struct miscdevice miscdev;
        struct rfkill *wwan_rfk;
+       struct iio_dev *indio_dev;
 
        int force_fan;
        int last_key_event;
@@ -1958,28 +1961,6 @@ static ssize_t touchpad_show(struct device *dev,
 }
 static DEVICE_ATTR_RW(touchpad);
 
-static ssize_t 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_RO(position);
-
 static ssize_t usb_sleep_charge_show(struct device *dev,
                                     struct device_attribute *attr, char *buf)
 {
@@ -2350,7 +2331,6 @@ static struct attribute *toshiba_attributes[] = {
        &dev_attr_available_kbd_modes.attr,
        &dev_attr_kbd_backlight_timeout.attr,
        &dev_attr_touchpad.attr,
-       &dev_attr_position.attr,
        &dev_attr_usb_sleep_charge.attr,
        &dev_attr_sleep_functions_on_battery.attr,
        &dev_attr_usb_rapid_charge.attr,
@@ -2377,8 +2357,6 @@ 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;
        else if (attr == &dev_attr_usb_sleep_charge.attr)
                exists = (drv->usb_sleep_charge_supported) ? true : false;
        else if (attr == &dev_attr_sleep_functions_on_battery.attr)
@@ -2419,6 +2397,81 @@ static void toshiba_acpi_kbd_bl_work(struct work_struct *work)
                                        0x92, 0);
 }
 
+/*
+ * IIO device
+ */
+
+enum toshiba_iio_accel_chan {
+       AXIS_X,
+       AXIS_Y,
+       AXIS_Z
+};
+
+static int toshiba_iio_accel_get_axis(enum toshiba_iio_accel_chan chan)
+{
+       u32 xyval, zval;
+       int ret;
+
+       ret = toshiba_accelerometer_get(toshiba_acpi, &xyval, &zval);
+       if (ret < 0)
+               return ret;
+
+       switch (chan) {
+       case AXIS_X:
+               return xyval & HCI_ACCEL_DIRECTION_MASK ?
+                       -(xyval & HCI_ACCEL_MASK) : xyval & HCI_ACCEL_MASK;
+       case AXIS_Y:
+               return (xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_DIRECTION_MASK ?
+                       -((xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_MASK) :
+                       (xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_MASK;
+       case AXIS_Z:
+               return zval & HCI_ACCEL_DIRECTION_MASK ?
+                       -(zval & HCI_ACCEL_MASK) : zval & HCI_ACCEL_MASK;
+       }
+
+       return ret;
+}
+
+static int toshiba_iio_accel_read_raw(struct iio_dev *indio_dev,
+                                     struct iio_chan_spec const *chan,
+                                     int *val, int *val2, long mask)
+{
+       int ret;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               ret = toshiba_iio_accel_get_axis(chan->channel);
+               if (ret == -EIO || ret == -ENODEV)
+                       return ret;
+
+               *val = ret;
+
+               return IIO_VAL_INT;
+       }
+
+       return -EINVAL;
+}
+
+#define TOSHIBA_IIO_ACCEL_CHANNEL(axis, chan) { \
+       .type = IIO_ACCEL, \
+       .modified = 1, \
+       .channel = chan, \
+       .channel2 = IIO_MOD_##axis, \
+       .output = 1, \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+}
+
+static const struct iio_chan_spec toshiba_iio_accel_channels[] = {
+       TOSHIBA_IIO_ACCEL_CHANNEL(X, AXIS_X),
+       TOSHIBA_IIO_ACCEL_CHANNEL(Y, AXIS_Y),
+       TOSHIBA_IIO_ACCEL_CHANNEL(Z, AXIS_Z),
+};
+
+static const struct iio_info toshiba_iio_accel_info = {
+       .driver_module = THIS_MODULE,
+       .read_raw = &toshiba_iio_accel_read_raw,
+};
+
 /*
  * Misc device
  */
@@ -2904,6 +2957,11 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
 
        remove_toshiba_proc_entries(dev);
 
+       if (dev->accelerometer_supported && dev->indio_dev) {
+               iio_device_unregister(dev->indio_dev);
+               iio_device_free(dev->indio_dev);
+       }
+
        if (dev->sysfs_created)
                sysfs_remove_group(&dev->acpi_dev->dev.kobj,
                                   &toshiba_attr_group);
@@ -3051,6 +3109,30 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
        dev->touchpad_supported = !ret;
 
        toshiba_accelerometer_available(dev);
+       if (dev->accelerometer_supported) {
+               dev->indio_dev = iio_device_alloc(sizeof(*dev));
+               if (!dev->indio_dev) {
+                       pr_err("Unable to allocate iio device\n");
+                       goto iio_error;
+               }
+
+               pr_info("Registering Toshiba accelerometer iio device\n");
+
+               dev->indio_dev->info = &toshiba_iio_accel_info;
+               dev->indio_dev->name = "Toshiba accelerometer";
+               dev->indio_dev->dev.parent = &acpi_dev->dev;
+               dev->indio_dev->modes = INDIO_DIRECT_MODE;
+               dev->indio_dev->channels = toshiba_iio_accel_channels;
+               dev->indio_dev->num_channels =
+                                       ARRAY_SIZE(toshiba_iio_accel_channels);
+
+               ret = iio_device_register(dev->indio_dev);
+               if (ret < 0) {
+                       pr_err("Unable to register iio device\n");
+                       iio_device_free(dev->indio_dev);
+               }
+       }
+iio_error:
 
        toshiba_usb_sleep_charge_available(dev);