Merge tag 'platform-drivers-x86-v4.6-1' of git://git.infradead.org/users/dvhart/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 24 Mar 2016 00:20:59 +0000 (17:20 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 24 Mar 2016 00:20:59 +0000 (17:20 -0700)
Pull x86 platform driver updates from Darren Hart:
 "Significant refactoring of Dell laptop drivers, modularizing the
  smbios code.  Multiple new platforms added for ideapad, asus, dell,
  and alienware using existing quirks.  A few fixes and cleanups.

  hp-wmi:
   - Remove GPS rfkill support via pre-2009 interface
   - fix unregister order in hp_wmi_rfkill_setup() once again

  ideapad-laptop:
   - Add ideapad Y700 (15) to the no_hw_rfkill DMI list

  fujitsu-laptop:
   - Support radio toggle button

  intel-hid:
   - allocate correct amount of memory for private struct

  platform/x86:
   - Make intel_scu_ipc explicitly non-modular

  intel_pmc_ipc:
   - Avoid pending IPC1 command during legacy suspend
   - Fix GCR register base address and length

  asus-nb-wmi:
   - add wapf=4 quirk for ASUS X75VD

  intel_telemetry_pltdrv:
   - Change verbosity control bits

  dell-rbtn:
   - Add a comment about the XPS 13 9350

  dell-wmi, dell-laptop:
   - depends DMI

  dell-wmi:
   - support Dell Inspiron M5110
   - properly process Dell Instant Launch hotkey
   - enable receiving WMI events on Dell Vostro V131
   - Support new hotkeys on the XPS 13 9350 (Skylake)
   - Clean up hotkey table size check
   - Stop storing pointers to DMI tables

  dell-laptop:
   - move dell_smi_error() to dell-smbios
   - use dell_smbios_find_token() instead of find_token_location()
   - use dell_smbios_find_token() instead of find_token_id()
   - extract SMBIOS-related code to a separate module

  dell-smbios:
   - rename dell_smi_error() to dell_smbios_error()
   - make da_tokens static
   - remove find_token_{id,location}()
   - implement new function for finding DMI table 0xDA tokens
   - make the SMBIOS buffer static
   - return the SMBIOS buffer from dell_smbios_get_buffer()
   - don't return an SMBIOS buffer from dell_smbios_send_request()
   - don't pass an SMBIOS buffer to dell_smbios_send_request()
   - rename dell_send_request() to dell_smbios_send_request()
   - rename release_buffer() to dell_smbios_release_buffer()
   - rename clear_buffer() to dell_smbios_clear_buffer()
   - rename get_buffer() to dell_smbios_get_buffer()

  dell-led:
   - use dell_smbios_send_request() for performing SMBIOS calls
   - use dell_smbios_find_token() for finding mic DMI tokens

  toshiba_acpi:
   - Add a module parameter to disable hotkeys registration
   - Add sysfs entries for the Cooling Method feature
   - Add support for cooling method feature

  Documentation/ABI:
   - Update sysfs-driver-toshiba_acpi file

  thinkpad_acpi:
   - Remove ambiguous logging for "Unsupported brightness interface"

  alienware-wmi:
   - whitespace improvements
   - Add support for two new systems: ASM200 and ASM201.
   - Add support for deep sleep control.
   - Add initial support for alienware graphics amplifier.
   - Add support for new platform: X51-R3
   - Clean up whitespace for ASM100 platform"

* tag 'platform-drivers-x86-v4.6-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86: (47 commits)
  hp-wmi: Remove GPS rfkill support via pre-2009 interface
  hp-wmi: fix unregister order in hp_wmi_rfkill_setup() once again
  dell-wmi: support Dell Inspiron M5110
  dell-wmi: properly process Dell Instant Launch hotkey
  dell-wmi: enable receiving WMI events on Dell Vostro V131
  dell-smbios: rename dell_smi_error() to dell_smbios_error()
  dell-laptop: move dell_smi_error() to dell-smbios
  ideapad-laptop: Add ideapad Y700 (15) to the no_hw_rfkill DMI list
  fujitsu-laptop: Support radio toggle button
  intel-hid: allocate correct amount of memory for private struct
  platform/x86: Make intel_scu_ipc explicitly non-modular
  intel_pmc_ipc: Avoid pending IPC1 command during legacy suspend
  intel_pmc_ipc: Fix GCR register base address and length
  asus-nb-wmi: add wapf=4 quirk for ASUS X75VD
  intel_telemetry_pltdrv: Change verbosity control bits
  dell-rbtn: Add a comment about the XPS 13 9350
  dell-wmi: Support new hotkeys on the XPS 13 9350 (Skylake)
  dell-wmi: Clean up hotkey table size check
  dell-wmi, dell-laptop: depends DMI
  dell-wmi: Stop storing pointers to DMI tables
  ...

21 files changed:
Documentation/ABI/testing/sysfs-driver-toshiba_acpi
drivers/leds/Kconfig
drivers/leds/dell-led.c
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/alienware-wmi.c
drivers/platform/x86/asus-nb-wmi.c
drivers/platform/x86/dell-laptop.c
drivers/platform/x86/dell-rbtn.c
drivers/platform/x86/dell-smbios.c [new file with mode: 0644]
drivers/platform/x86/dell-smbios.h [new file with mode: 0644]
drivers/platform/x86/dell-wmi.c
drivers/platform/x86/fujitsu-laptop.c
drivers/platform/x86/hp-wmi.c
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/intel-hid.c
drivers/platform/x86/intel_pmc_ipc.c
drivers/platform/x86/intel_scu_ipc.c
drivers/platform/x86/intel_telemetry_pltdrv.c
drivers/platform/x86/thinkpad_acpi.c
drivers/platform/x86/toshiba_acpi.c

index eed922ef42e533ae9bf16da6d224a50d2b46e16b..f34221b52b14b796377893207692c48ddaabf7fb 100644 (file)
@@ -179,3 +179,19 @@ Description:       This file controls the USB 3 functionality, valid values are:
                Note that toggling this value requires a reboot for changes to
                take effect.
 Users:         KToshiba
+
+What:          /sys/devices/LNXSYSTM:00/LNXSYBUS:00/TOS{1900,620{0,7,8}}:00/cooling_method
+Date:          2016
+KernelVersion: 4.6
+Contact:       Azael Avalos <coproscefalo@gmail.com>
+Description:   This file controls the Cooling Method feature.
+               Reading this file prints two values, the first is the actual cooling method
+               and the second is the maximum cooling method supported.
+               When the maximum cooling method is ONE, valid values are:
+                       * 0 -> Maximum Performance
+                       * 1 -> Battery Optimized
+               When the maximum cooling method is TWO, valid values are:
+                       * 0 -> Maximum Performance
+                       * 1 -> Performance
+                       * 2 -> Battery Optimized
+Users:         KToshiba
index 1f64151689981335edec187c20bb71bbc5f1feb6..225147863e024fcb93ac6b011976255af1486f29 100644 (file)
@@ -443,6 +443,7 @@ config LEDS_DELL_NETBOOKS
        tristate "External LED on Dell Business Netbooks"
        depends on LEDS_CLASS
        depends on X86 && ACPI_WMI
+       depends on DELL_SMBIOS
        help
          This adds support for the Latitude 2100 and similar
          notebooks that have an external LED.
index c36acaf566a6e4d113e6c4a2314f1eaed60d3263..b3d6e9c15cf93b7c4d98f7d7805dad343d3fece0 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/dmi.h>
 #include <linux/dell-led.h>
+#include "../platform/x86/dell-smbios.h"
 
 MODULE_AUTHOR("Louis Davis/Jim Dailey");
 MODULE_DESCRIPTION("Dell LED Control Driver");
@@ -42,120 +43,32 @@ MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID);
 #define CMD_LED_OFF    17
 #define CMD_LED_BLINK  18
 
-struct app_wmi_args {
-       u16 class;
-       u16 selector;
-       u32 arg1;
-       u32 arg2;
-       u32 arg3;
-       u32 arg4;
-       u32 res1;
-       u32 res2;
-       u32 res3;
-       u32 res4;
-       char dummy[92];
-};
-
 #define GLOBAL_MIC_MUTE_ENABLE 0x364
 #define GLOBAL_MIC_MUTE_DISABLE        0x365
 
-struct dell_bios_data_token {
-       u16 tokenid;
-       u16 location;
-       u16 value;
-};
-
-struct __attribute__ ((__packed__)) dell_bios_calling_interface {
-       struct  dmi_header header;
-       u16     cmd_io_addr;
-       u8      cmd_io_code;
-       u32     supported_cmds;
-       struct  dell_bios_data_token damap[];
-};
-
-static struct dell_bios_data_token dell_mic_tokens[2];
-
-static int dell_wmi_perform_query(struct app_wmi_args *args)
-{
-       struct app_wmi_args *bios_return;
-       union acpi_object *obj;
-       struct acpi_buffer input;
-       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
-       acpi_status status;
-       u32 rc = -EINVAL;
-
-       input.length = 128;
-       input.pointer = args;
-
-       status = wmi_evaluate_method(DELL_APP_GUID, 0, 1, &input, &output);
-       if (!ACPI_SUCCESS(status))
-               goto err_out0;
-
-       obj = output.pointer;
-       if (!obj)
-               goto err_out0;
-
-       if (obj->type != ACPI_TYPE_BUFFER)
-               goto err_out1;
-
-       bios_return = (struct app_wmi_args *)obj->buffer.pointer;
-       rc = bios_return->res1;
-       if (rc)
-               goto err_out1;
-
-       memcpy(args, bios_return, sizeof(struct app_wmi_args));
-       rc = 0;
-
- err_out1:
-       kfree(obj);
- err_out0:
-       return rc;
-}
-
-static void __init find_micmute_tokens(const struct dmi_header *dm, void *dummy)
-{
-       struct dell_bios_calling_interface *calling_interface;
-       struct dell_bios_data_token *token;
-       int token_size = sizeof(struct dell_bios_data_token);
-       int i = 0;
-
-       if (dm->type == 0xda && dm->length > 17) {
-               calling_interface = container_of(dm,
-                               struct dell_bios_calling_interface, header);
-
-               token = &calling_interface->damap[i];
-               while (token->tokenid != 0xffff) {
-                       if (token->tokenid == GLOBAL_MIC_MUTE_DISABLE)
-                               memcpy(&dell_mic_tokens[0], token, token_size);
-                       else if (token->tokenid == GLOBAL_MIC_MUTE_ENABLE)
-                               memcpy(&dell_mic_tokens[1], token, token_size);
-
-                       i++;
-                       token = &calling_interface->damap[i];
-               }
-       }
-}
-
 static int dell_micmute_led_set(int state)
 {
-       struct app_wmi_args args;
-       struct dell_bios_data_token *token;
+       struct calling_interface_buffer *buffer;
+       struct calling_interface_token *token;
 
        if (!wmi_has_guid(DELL_APP_GUID))
                return -ENODEV;
 
-       if (state == 0 || state == 1)
-               token = &dell_mic_tokens[state];
+       if (state == 0)
+               token = dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE);
+       else if (state == 1)
+               token = dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE);
        else
                return -EINVAL;
 
-       memset(&args, 0, sizeof(struct app_wmi_args));
-
-       args.class = 1;
-       args.arg1 = token->location;
-       args.arg2 = token->value;
+       if (!token)
+               return -ENODEV;
 
-       dell_wmi_perform_query(&args);
+       buffer = dell_smbios_get_buffer();
+       buffer->input[0] = token->location;
+       buffer->input[1] = token->value;
+       dell_smbios_send_request(1, 0);
+       dell_smbios_release_buffer();
 
        return state;
 }
@@ -177,14 +90,6 @@ int dell_app_wmi_led_set(int whichled, int on)
 }
 EXPORT_SYMBOL_GPL(dell_app_wmi_led_set);
 
-static int __init dell_micmute_led_init(void)
-{
-       memset(dell_mic_tokens, 0, sizeof(struct dell_bios_data_token) * 2);
-       dmi_walk(find_micmute_tokens, NULL);
-
-       return 0;
-}
-
 struct bios_args {
        unsigned char length;
        unsigned char result_code;
@@ -330,9 +235,6 @@ static int __init dell_led_init(void)
        if (!wmi_has_guid(DELL_LED_BIOS_GUID) && !wmi_has_guid(DELL_APP_GUID))
                return -ENODEV;
 
-       if (wmi_has_guid(DELL_APP_GUID))
-               error = dell_micmute_led_init();
-
        if (wmi_has_guid(DELL_LED_BIOS_GUID)) {
                error = led_off();
                if (error != 0)
index 69f93a576e4583a945746f92e14d06f0ad3a70ea..ed2004be13cfcc58a6c9598a234433d9c76ba078 100644 (file)
@@ -91,10 +91,21 @@ config ASUS_LAPTOP
 
          If you have an ACPI-compatible ASUS laptop, say Y or M here.
 
+config DELL_SMBIOS
+       tristate "Dell SMBIOS Support"
+       depends on DCDBAS
+       default n
+       ---help---
+       This module provides common functions for kernel modules using
+       Dell SMBIOS.
+
+       If you have a Dell laptop, say Y or M here.
+
 config DELL_LAPTOP
        tristate "Dell Laptop Extras"
        depends on X86
-       depends on DCDBAS
+       depends on DELL_SMBIOS
+       depends on DMI
        depends on BACKLIGHT_CLASS_DEVICE
        depends on ACPI_VIDEO || ACPI_VIDEO = n
        depends on RFKILL || RFKILL = n
@@ -110,8 +121,10 @@ config DELL_LAPTOP
 config DELL_WMI
        tristate "Dell WMI extras"
        depends on ACPI_WMI
+       depends on DMI
        depends on INPUT
        depends on ACPI_VIDEO || ACPI_VIDEO = n
+       depends on DELL_SMBIOS
        select INPUT_SPARSEKMAP
        ---help---
          Say Y here if you want to support WMI-based hotkeys on Dell laptops.
index 40574e7390f3d69702e76ae62517b70a669c91ef..448443c3baba9652e4c0edaa2d8295b8a1c3e0f1 100644 (file)
@@ -11,6 +11,7 @@ obj-$(CONFIG_EEEPC_WMI)               += eeepc-wmi.o
 obj-$(CONFIG_MSI_LAPTOP)       += msi-laptop.o
 obj-$(CONFIG_ACPI_CMPC)                += classmate-laptop.o
 obj-$(CONFIG_COMPAL_LAPTOP)    += compal-laptop.o
+obj-$(CONFIG_DELL_SMBIOS)      += dell-smbios.o
 obj-$(CONFIG_DELL_LAPTOP)      += dell-laptop.o
 obj-$(CONFIG_DELL_WMI)         += dell-wmi.o
 obj-$(CONFIG_DELL_WMI_AIO)     += dell-wmi-aio.o
index 1e1e594238892a3bf18d99ee0e4e35d58ac96d1f..005629447b0ce624b24ed02835e3d9314065a50a 100644 (file)
@@ -33,6 +33,9 @@
 #define WMAX_METHOD_BRIGHTNESS         0x3
 #define WMAX_METHOD_ZONE_CONTROL       0x4
 #define WMAX_METHOD_HDMI_CABLE         0x5
+#define WMAX_METHOD_AMPLIFIER_CABLE    0x6
+#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
+#define WMAX_METHOD_DEEP_SLEEP_STATUS  0x0C
 
 MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>");
 MODULE_DESCRIPTION("Alienware special feature control");
@@ -60,6 +63,8 @@ enum WMAX_CONTROL_STATES {
 struct quirk_entry {
        u8 num_zones;
        u8 hdmi_mux;
+       u8 amplifier;
+       u8 deepslp;
 };
 
 static struct quirk_entry *quirks;
@@ -67,16 +72,43 @@ static struct quirk_entry *quirks;
 static struct quirk_entry quirk_unknown = {
        .num_zones = 2,
        .hdmi_mux = 0,
+       .amplifier = 0,
+       .deepslp = 0,
 };
 
-static struct quirk_entry quirk_x51_family = {
+static struct quirk_entry quirk_x51_r1_r2 = {
        .num_zones = 3,
-       .hdmi_mux = 0.
+       .hdmi_mux = 0,
+       .amplifier = 0,
+       .deepslp = 0,
+};
+
+static struct quirk_entry quirk_x51_r3 = {
+       .num_zones = 4,
+       .hdmi_mux = 0,
+       .amplifier = 1,
+       .deepslp = 0,
 };
 
 static struct quirk_entry quirk_asm100 = {
        .num_zones = 2,
        .hdmi_mux = 1,
+       .amplifier = 0,
+       .deepslp = 0,
+};
+
+static struct quirk_entry quirk_asm200 = {
+       .num_zones = 2,
+       .hdmi_mux = 1,
+       .amplifier = 0,
+       .deepslp = 1,
+};
+
+static struct quirk_entry quirk_asm201 = {
+       .num_zones = 2,
+       .hdmi_mux = 1,
+       .amplifier = 1,
+       .deepslp = 1,
 };
 
 static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -88,12 +120,12 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
 static const struct dmi_system_id alienware_quirks[] __initconst = {
        {
         .callback = dmi_matched,
-        .ident = "Alienware X51 R1",
+        .ident = "Alienware X51 R3",
         .matches = {
                     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-                    DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
+                    DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
                     },
-        .driver_data = &quirk_x51_family,
+        .driver_data = &quirk_x51_r3,
         },
        {
         .callback = dmi_matched,
@@ -102,17 +134,44 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
                     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
                     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
                     },
-        .driver_data = &quirk_x51_family,
+        .driver_data = &quirk_x51_r1_r2,
+        },
+       {
+        .callback = dmi_matched,
+        .ident = "Alienware X51 R1",
+        .matches = {
+                    DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                    DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
+                    },
+        .driver_data = &quirk_x51_r1_r2,
+        },
+       {
+        .callback = dmi_matched,
+        .ident = "Alienware ASM100",
+        .matches = {
+                    DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                    DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
+                    },
+        .driver_data = &quirk_asm100,
+        },
+       {
+        .callback = dmi_matched,
+        .ident = "Alienware ASM200",
+        .matches = {
+                    DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                    DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
+                    },
+        .driver_data = &quirk_asm200,
         },
        {
-               .callback = dmi_matched,
-               .ident = "Alienware ASM100",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
-               },
-               .driver_data = &quirk_asm100,
-       },
+        .callback = dmi_matched,
+        .ident = "Alienware ASM201",
+        .matches = {
+                    DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                    DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
+                    },
+        .driver_data = &quirk_asm201,
+        },
        {}
 };
 
@@ -133,7 +192,7 @@ struct wmax_brightness_args {
        u32 percentage;
 };
 
-struct hdmi_args {
+struct wmax_basic_args {
        u8 arg;
 };
 
@@ -170,7 +229,7 @@ static u8 global_brightness;
 
 /*
  * Helpers used for zone control
-*/
+ */
 static int parse_rgb(const char *buf, struct platform_zone *zone)
 {
        long unsigned int rgb;
@@ -210,7 +269,7 @@ static struct platform_zone *match_zone(struct device_attribute *attr)
 
 /*
  * Individual RGB zone control
-*/
+ */
 static int alienware_update_led(struct platform_zone *zone)
 {
        int method_id;
@@ -218,16 +277,16 @@ static int alienware_update_led(struct platform_zone *zone)
        char *guid;
        struct acpi_buffer input;
        struct legacy_led_args legacy_args;
-       struct wmax_led_args wmax_args;
+       struct wmax_led_args wmax_basic_args;
        if (interface == WMAX) {
-               wmax_args.led_mask = 1 << zone->location;
-               wmax_args.colors = zone->colors;
-               wmax_args.state = lighting_control_state;
+               wmax_basic_args.led_mask = 1 << zone->location;
+               wmax_basic_args.colors = zone->colors;
+               wmax_basic_args.state = lighting_control_state;
                guid = WMAX_CONTROL_GUID;
                method_id = WMAX_METHOD_ZONE_CONTROL;
 
-               input.length = (acpi_size) sizeof(wmax_args);
-               input.pointer = &wmax_args;
+               input.length = (acpi_size) sizeof(wmax_basic_args);
+               input.pointer = &wmax_basic_args;
        } else {
                legacy_args.colors = zone->colors;
                legacy_args.brightness = global_brightness;
@@ -283,7 +342,7 @@ static ssize_t zone_set(struct device *dev, struct device_attribute *attr,
 
 /*
  * LED Brightness (Global)
-*/
+ */
 static int wmax_brightness(int brightness)
 {
        acpi_status status;
@@ -327,7 +386,7 @@ static struct led_classdev global_led = {
 
 /*
  * Lighting control state device attribute (Global)
-*/
+ */
 static ssize_t show_control_state(struct device *dev,
                                  struct device_attribute *attr, char *buf)
 {
@@ -435,11 +494,7 @@ static void alienware_zone_exit(struct platform_device *dev)
        kfree(zone_attrs);
 }
 
-/*
-       The HDMI mux sysfs node indicates the status of the HDMI input mux.
-       It can toggle between standard system GPU output and HDMI input.
-*/
-static acpi_status alienware_hdmi_command(struct hdmi_args *in_args,
+static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
                                          u32 command, int *out_data)
 {
        acpi_status status;
@@ -467,16 +522,20 @@ static acpi_status alienware_hdmi_command(struct hdmi_args *in_args,
 
 }
 
+/*
+ *     The HDMI mux sysfs node indicates the status of the HDMI input mux.
+ *     It can toggle between standard system GPU output and HDMI input.
+ */
 static ssize_t show_hdmi_cable(struct device *dev,
                               struct device_attribute *attr, char *buf)
 {
        acpi_status status;
        u32 out_data;
-       struct hdmi_args in_args = {
+       struct wmax_basic_args in_args = {
                .arg = 0,
        };
        status =
-           alienware_hdmi_command(&in_args, WMAX_METHOD_HDMI_CABLE,
+           alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
                                   (u32 *) &out_data);
        if (ACPI_SUCCESS(status)) {
                if (out_data == 0)
@@ -495,11 +554,11 @@ static ssize_t show_hdmi_source(struct device *dev,
 {
        acpi_status status;
        u32 out_data;
-       struct hdmi_args in_args = {
+       struct wmax_basic_args in_args = {
                .arg = 0,
        };
        status =
-           alienware_hdmi_command(&in_args, WMAX_METHOD_HDMI_STATUS,
+           alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
                                   (u32 *) &out_data);
 
        if (ACPI_SUCCESS(status)) {
@@ -519,7 +578,7 @@ static ssize_t toggle_hdmi_source(struct device *dev,
                                  const char *buf, size_t count)
 {
        acpi_status status;
-       struct hdmi_args args;
+       struct wmax_basic_args args;
        if (strcmp(buf, "gpu\n") == 0)
                args.arg = 1;
        else if (strcmp(buf, "input\n") == 0)
@@ -528,7 +587,7 @@ static ssize_t toggle_hdmi_source(struct device *dev,
                args.arg = 3;
        pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
 
-       status = alienware_hdmi_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
+       status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
 
        if (ACPI_FAILURE(status))
                pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
@@ -563,11 +622,144 @@ static int create_hdmi(struct platform_device *dev)
 
        ret = sysfs_create_group(&dev->dev.kobj, &hdmi_attribute_group);
        if (ret)
-               goto error_create_hdmi;
-       return 0;
+               remove_hdmi(dev);
+       return ret;
+}
 
-error_create_hdmi:
-       remove_hdmi(dev);
+/*
+ * Alienware GFX amplifier support
+ * - Currently supports reading cable status
+ * - Leaving expansion room to possibly support dock/undock events later
+ */
+static ssize_t show_amplifier_status(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       acpi_status status;
+       u32 out_data;
+       struct wmax_basic_args in_args = {
+               .arg = 0,
+       };
+       status =
+           alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
+                                  (u32 *) &out_data);
+       if (ACPI_SUCCESS(status)) {
+               if (out_data == 0)
+                       return scnprintf(buf, PAGE_SIZE,
+                                        "[unconnected] connected unknown\n");
+               else if (out_data == 1)
+                       return scnprintf(buf, PAGE_SIZE,
+                                        "unconnected [connected] unknown\n");
+       }
+       pr_err("alienware-wmi: unknown amplifier cable status: %d\n", status);
+       return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n");
+}
+
+static DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL);
+
+static struct attribute *amplifier_attrs[] = {
+       &dev_attr_status.attr,
+       NULL,
+};
+
+static struct attribute_group amplifier_attribute_group = {
+       .name = "amplifier",
+       .attrs = amplifier_attrs,
+};
+
+static void remove_amplifier(struct platform_device *dev)
+{
+       if (quirks->amplifier > 0)
+               sysfs_remove_group(&dev->dev.kobj, &amplifier_attribute_group);
+}
+
+static int create_amplifier(struct platform_device *dev)
+{
+       int ret;
+
+       ret = sysfs_create_group(&dev->dev.kobj, &amplifier_attribute_group);
+       if (ret)
+               remove_amplifier(dev);
+       return ret;
+}
+
+/*
+ * Deep Sleep Control support
+ * - Modifies BIOS setting for deep sleep control allowing extra wakeup events
+ */
+static ssize_t show_deepsleep_status(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       acpi_status status;
+       u32 out_data;
+       struct wmax_basic_args in_args = {
+               .arg = 0,
+       };
+       status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
+                                       (u32 *) &out_data);
+       if (ACPI_SUCCESS(status)) {
+               if (out_data == 0)
+                       return scnprintf(buf, PAGE_SIZE,
+                                        "[disabled] s5 s5_s4\n");
+               else if (out_data == 1)
+                       return scnprintf(buf, PAGE_SIZE,
+                                        "disabled [s5] s5_s4\n");
+               else if (out_data == 2)
+                       return scnprintf(buf, PAGE_SIZE,
+                                        "disabled s5 [s5_s4]\n");
+       }
+       pr_err("alienware-wmi: unknown deep sleep status: %d\n", status);
+       return scnprintf(buf, PAGE_SIZE, "disabled s5 s5_s4 [unknown]\n");
+}
+
+static ssize_t toggle_deepsleep(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       acpi_status status;
+       struct wmax_basic_args args;
+
+       if (strcmp(buf, "disabled\n") == 0)
+               args.arg = 0;
+       else if (strcmp(buf, "s5\n") == 0)
+               args.arg = 1;
+       else
+               args.arg = 2;
+       pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
+
+       status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
+                                       NULL);
+
+       if (ACPI_FAILURE(status))
+               pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
+                       status);
+       return count;
+}
+
+static DEVICE_ATTR(deepsleep, S_IRUGO | S_IWUSR, show_deepsleep_status, toggle_deepsleep);
+
+static struct attribute *deepsleep_attrs[] = {
+       &dev_attr_deepsleep.attr,
+       NULL,
+};
+
+static struct attribute_group deepsleep_attribute_group = {
+       .name = "deepsleep",
+       .attrs = deepsleep_attrs,
+};
+
+static void remove_deepsleep(struct platform_device *dev)
+{
+       if (quirks->deepslp > 0)
+               sysfs_remove_group(&dev->dev.kobj, &deepsleep_attribute_group);
+}
+
+static int create_deepsleep(struct platform_device *dev)
+{
+       int ret;
+
+       ret = sysfs_create_group(&dev->dev.kobj, &deepsleep_attribute_group);
+       if (ret)
+               remove_deepsleep(dev);
        return ret;
 }
 
@@ -606,6 +798,18 @@ static int __init alienware_wmi_init(void)
                        goto fail_prep_hdmi;
        }
 
+       if (quirks->amplifier > 0) {
+               ret = create_amplifier(platform_device);
+               if (ret)
+                       goto fail_prep_amplifier;
+       }
+
+       if (quirks->deepslp > 0) {
+               ret = create_deepsleep(platform_device);
+               if (ret)
+                       goto fail_prep_deepsleep;
+       }
+
        ret = alienware_zone_init(platform_device);
        if (ret)
                goto fail_prep_zones;
@@ -614,6 +818,8 @@ static int __init alienware_wmi_init(void)
 
 fail_prep_zones:
        alienware_zone_exit(platform_device);
+fail_prep_deepsleep:
+fail_prep_amplifier:
 fail_prep_hdmi:
        platform_device_del(platform_device);
 fail_platform_device2:
index 131fee2b093eadde86ba6c0b0fc53be458150e82..091ca7ada8fc48efe25cdd445e8728d1833398b0 100644 (file)
@@ -270,6 +270,15 @@ static const struct dmi_system_id asus_quirks[] = {
                },
                .driver_data = &quirk_asus_wapf4,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. X75VD",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X75VD"),
+               },
+               .driver_data = &quirk_asus_wapf4,
+       },
        {
                .callback = dmi_matched,
                .ident = "ASUSTeK COMPUTER INC. 1015E",
index aaeeae81e3a9798de43aaa80fb4aba2f247ad3ab..2c2f02b2e08a13a7421cbcc73aaad4f8ecce0afa 100644 (file)
 #include <linux/acpi.h>
 #include <linux/mm.h>
 #include <linux/i8042.h>
-#include <linux/slab.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <acpi/video.h>
-#include "../../firmware/dcdbas.h"
 #include "dell-rbtn.h"
+#include "dell-smbios.h"
 
 #define BRIGHTNESS_TOKEN 0x7d
 #define KBD_LED_OFF_TOKEN 0x01E1
 #define KBD_LED_AUTO_75_TOKEN 0x02EC
 #define KBD_LED_AUTO_100_TOKEN 0x02F6
 
-/* This structure will be modified by the firmware when we enter
- * system management mode, hence the volatiles */
-
-struct calling_interface_buffer {
-       u16 class;
-       u16 select;
-       volatile u32 input[4];
-       volatile u32 output[4];
-} __packed;
-
-struct calling_interface_token {
-       u16 tokenID;
-       u16 location;
-       union {
-               u16 value;
-               u16 stringlength;
-       };
-};
-
-struct calling_interface_structure {
-       struct dmi_header header;
-       u16 cmdIOAddress;
-       u8 cmdIOCode;
-       u32 supportedCmds;
-       struct calling_interface_token tokens[];
-} __packed;
-
 struct quirk_entry {
        u8 touchpad_led;
 
@@ -103,11 +75,6 @@ static struct quirk_entry quirk_dell_xps13_9333 = {
        .kbd_timeouts = { 0, 5, 15, 60, 5 * 60, 15 * 60, -1 },
 };
 
-static int da_command_address;
-static int da_command_code;
-static int da_num_tokens;
-static struct calling_interface_token *da_tokens;
-
 static struct platform_driver platform_driver = {
        .driver = {
                .name = "dell-laptop",
@@ -306,126 +273,6 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
        { }
 };
 
-static struct calling_interface_buffer *buffer;
-static DEFINE_MUTEX(buffer_mutex);
-
-static void clear_buffer(void)
-{
-       memset(buffer, 0, sizeof(struct calling_interface_buffer));
-}
-
-static void get_buffer(void)
-{
-       mutex_lock(&buffer_mutex);
-       clear_buffer();
-}
-
-static void release_buffer(void)
-{
-       mutex_unlock(&buffer_mutex);
-}
-
-static void __init parse_da_table(const struct dmi_header *dm)
-{
-       /* Final token is a terminator, so we don't want to copy it */
-       int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
-       struct calling_interface_token *new_da_tokens;
-       struct calling_interface_structure *table =
-               container_of(dm, struct calling_interface_structure, header);
-
-       /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
-          6 bytes of entry */
-
-       if (dm->length < 17)
-               return;
-
-       da_command_address = table->cmdIOAddress;
-       da_command_code = table->cmdIOCode;
-
-       new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
-                                sizeof(struct calling_interface_token),
-                                GFP_KERNEL);
-
-       if (!new_da_tokens)
-               return;
-       da_tokens = new_da_tokens;
-
-       memcpy(da_tokens+da_num_tokens, table->tokens,
-              sizeof(struct calling_interface_token) * tokens);
-
-       da_num_tokens += tokens;
-}
-
-static void __init find_tokens(const struct dmi_header *dm, void *dummy)
-{
-       switch (dm->type) {
-       case 0xd4: /* Indexed IO */
-       case 0xd5: /* Protected Area Type 1 */
-       case 0xd6: /* Protected Area Type 2 */
-               break;
-       case 0xda: /* Calling interface */
-               parse_da_table(dm);
-               break;
-       }
-}
-
-static int find_token_id(int tokenid)
-{
-       int i;
-
-       for (i = 0; i < da_num_tokens; i++) {
-               if (da_tokens[i].tokenID == tokenid)
-                       return i;
-       }
-
-       return -1;
-}
-
-static int find_token_location(int tokenid)
-{
-       int id;
-
-       id = find_token_id(tokenid);
-       if (id == -1)
-               return -1;
-
-       return da_tokens[id].location;
-}
-
-static struct calling_interface_buffer *
-dell_send_request(struct calling_interface_buffer *buffer, int class,
-                 int select)
-{
-       struct smi_cmd command;
-
-       command.magic = SMI_CMD_MAGIC;
-       command.command_address = da_command_address;
-       command.command_code = da_command_code;
-       command.ebx = virt_to_phys(buffer);
-       command.ecx = 0x42534931;
-
-       buffer->class = class;
-       buffer->select = select;
-
-       dcdbas_smi_request(&command);
-
-       return buffer;
-}
-
-static inline int dell_smi_error(int value)
-{
-       switch (value) {
-       case 0: /* Completed successfully */
-               return 0;
-       case -1: /* Completed with error */
-               return -EIO;
-       case -2: /* Function not supported */
-               return -ENXIO;
-       default: /* Unknown error */
-               return -EINVAL;
-       }
-}
-
 /*
  * Derived from information in smbios-wireless-ctl:
  *
@@ -548,6 +395,7 @@ static inline int dell_smi_error(int value)
 
 static int dell_rfkill_set(void *data, bool blocked)
 {
+       struct calling_interface_buffer *buffer;
        int disable = blocked ? 1 : 0;
        unsigned long radio = (unsigned long)data;
        int hwswitch_bit = (unsigned long)data - 1;
@@ -555,19 +403,19 @@ static int dell_rfkill_set(void *data, bool blocked)
        int status;
        int ret;
 
-       get_buffer();
+       buffer = dell_smbios_get_buffer();
 
-       dell_send_request(buffer, 17, 11);
+       dell_smbios_send_request(17, 11);
        ret = buffer->output[0];
        status = buffer->output[1];
 
        if (ret != 0)
                goto out;
 
-       clear_buffer();
+       dell_smbios_clear_buffer();
 
        buffer->input[0] = 0x2;
-       dell_send_request(buffer, 17, 11);
+       dell_smbios_send_request(17, 11);
        ret = buffer->output[0];
        hwswitch = buffer->output[1];
 
@@ -577,27 +425,28 @@ static int dell_rfkill_set(void *data, bool blocked)
            (status & BIT(0)) && !(status & BIT(16)))
                disable = 1;
 
-       clear_buffer();
+       dell_smbios_clear_buffer();
 
        buffer->input[0] = (1 | (radio<<8) | (disable << 16));
-       dell_send_request(buffer, 17, 11);
+       dell_smbios_send_request(17, 11);
        ret = buffer->output[0];
 
  out:
-       release_buffer();
-       return dell_smi_error(ret);
+       dell_smbios_release_buffer();
+       return dell_smbios_error(ret);
 }
 
 /* Must be called with the buffer held */
 static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
-                                       int status)
+                                       int status,
+                                       struct calling_interface_buffer *buffer)
 {
        if (status & BIT(0)) {
                /* Has hw-switch, sync sw_state to BIOS */
                int block = rfkill_blocked(rfkill);
-               clear_buffer();
+               dell_smbios_clear_buffer();
                buffer->input[0] = (1 | (radio << 8) | (block << 16));
-               dell_send_request(buffer, 17, 11);
+               dell_smbios_send_request(17, 11);
        } else {
                /* No hw-switch, sync BIOS state to sw_state */
                rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
@@ -613,30 +462,31 @@ static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,
 
 static void dell_rfkill_query(struct rfkill *rfkill, void *data)
 {
+       struct calling_interface_buffer *buffer;
        int radio = ((unsigned long)data & 0xF);
        int hwswitch;
        int status;
        int ret;
 
-       get_buffer();
+       buffer = dell_smbios_get_buffer();
 
-       dell_send_request(buffer, 17, 11);
+       dell_smbios_send_request(17, 11);
        ret = buffer->output[0];
        status = buffer->output[1];
 
        if (ret != 0 || !(status & BIT(0))) {
-               release_buffer();
+               dell_smbios_release_buffer();
                return;
        }
 
-       clear_buffer();
+       dell_smbios_clear_buffer();
 
        buffer->input[0] = 0x2;
-       dell_send_request(buffer, 17, 11);
+       dell_smbios_send_request(17, 11);
        ret = buffer->output[0];
        hwswitch = buffer->output[1];
 
-       release_buffer();
+       dell_smbios_release_buffer();
 
        if (ret != 0)
                return;
@@ -653,25 +503,26 @@ static struct dentry *dell_laptop_dir;
 
 static int dell_debugfs_show(struct seq_file *s, void *data)
 {
+       struct calling_interface_buffer *buffer;
        int hwswitch_state;
        int hwswitch_ret;
        int status;
        int ret;
 
-       get_buffer();
+       buffer = dell_smbios_get_buffer();
 
-       dell_send_request(buffer, 17, 11);
+       dell_smbios_send_request(17, 11);
        ret = buffer->output[0];
        status = buffer->output[1];
 
-       clear_buffer();
+       dell_smbios_clear_buffer();
 
        buffer->input[0] = 0x2;
-       dell_send_request(buffer, 17, 11);
+       dell_smbios_send_request(17, 11);
        hwswitch_ret = buffer->output[0];
        hwswitch_state = buffer->output[1];
 
-       release_buffer();
+       dell_smbios_release_buffer();
 
        seq_printf(s, "return:\t%d\n", ret);
        seq_printf(s, "status:\t0x%X\n", status);
@@ -752,23 +603,24 @@ static const struct file_operations dell_debugfs_fops = {
 
 static void dell_update_rfkill(struct work_struct *ignored)
 {
+       struct calling_interface_buffer *buffer;
        int hwswitch = 0;
        int status;
        int ret;
 
-       get_buffer();
+       buffer = dell_smbios_get_buffer();
 
-       dell_send_request(buffer, 17, 11);
+       dell_smbios_send_request(17, 11);
        ret = buffer->output[0];
        status = buffer->output[1];
 
        if (ret != 0)
                goto out;
 
-       clear_buffer();
+       dell_smbios_clear_buffer();
 
        buffer->input[0] = 0x2;
-       dell_send_request(buffer, 17, 11);
+       dell_smbios_send_request(17, 11);
        ret = buffer->output[0];
 
        if (ret == 0 && (status & BIT(0)))
@@ -776,20 +628,21 @@ static void dell_update_rfkill(struct work_struct *ignored)
 
        if (wifi_rfkill) {
                dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch);
-               dell_rfkill_update_sw_state(wifi_rfkill, 1, status);
+               dell_rfkill_update_sw_state(wifi_rfkill, 1, status, buffer);
        }
        if (bluetooth_rfkill) {
                dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status,
                                            hwswitch);
-               dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status);
+               dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status,
+                                           buffer);
        }
        if (wwan_rfkill) {
                dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch);
-               dell_rfkill_update_sw_state(wwan_rfkill, 3, status);
+               dell_rfkill_update_sw_state(wwan_rfkill, 3, status, buffer);
        }
 
  out:
-       release_buffer();
+       dell_smbios_release_buffer();
 }
 static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
 
@@ -833,6 +686,7 @@ static struct notifier_block dell_laptop_rbtn_notifier = {
 
 static int __init dell_setup_rfkill(void)
 {
+       struct calling_interface_buffer *buffer;
        int status, ret, whitelisted;
        const char *product;
 
@@ -848,11 +702,11 @@ static int __init dell_setup_rfkill(void)
        if (!force_rfkill && !whitelisted)
                return 0;
 
-       get_buffer();
-       dell_send_request(buffer, 17, 11);
+       buffer = dell_smbios_get_buffer();
+       dell_smbios_send_request(17, 11);
        ret = buffer->output[0];
        status = buffer->output[1];
-       release_buffer();
+       dell_smbios_release_buffer();
 
        /* dell wireless info smbios call is not supported */
        if (ret != 0)
@@ -1005,51 +859,53 @@ static void dell_cleanup_rfkill(void)
 
 static int dell_send_intensity(struct backlight_device *bd)
 {
-       int token;
+       struct calling_interface_buffer *buffer;
+       struct calling_interface_token *token;
        int ret;
 
-       token = find_token_location(BRIGHTNESS_TOKEN);
-       if (token == -1)
+       token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
+       if (!token)
                return -ENODEV;
 
-       get_buffer();
-       buffer->input[0] = token;
+       buffer = dell_smbios_get_buffer();
+       buffer->input[0] = token->location;
        buffer->input[1] = bd->props.brightness;
 
        if (power_supply_is_system_supplied() > 0)
-               dell_send_request(buffer, 1, 2);
+               dell_smbios_send_request(1, 2);
        else
-               dell_send_request(buffer, 1, 1);
+               dell_smbios_send_request(1, 1);
 
-       ret = dell_smi_error(buffer->output[0]);
+       ret = dell_smbios_error(buffer->output[0]);
 
-       release_buffer();
+       dell_smbios_release_buffer();
        return ret;
 }
 
 static int dell_get_intensity(struct backlight_device *bd)
 {
-       int token;
+       struct calling_interface_buffer *buffer;
+       struct calling_interface_token *token;
        int ret;
 
-       token = find_token_location(BRIGHTNESS_TOKEN);
-       if (token == -1)
+       token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
+       if (!token)
                return -ENODEV;
 
-       get_buffer();
-       buffer->input[0] = token;
+       buffer = dell_smbios_get_buffer();
+       buffer->input[0] = token->location;
 
        if (power_supply_is_system_supplied() > 0)
-               dell_send_request(buffer, 0, 2);
+               dell_smbios_send_request(0, 2);
        else
-               dell_send_request(buffer, 0, 1);
+               dell_smbios_send_request(0, 1);
 
        if (buffer->output[0])
-               ret = dell_smi_error(buffer->output[0]);
+               ret = dell_smbios_error(buffer->output[0]);
        else
                ret = buffer->output[1];
 
-       release_buffer();
+       dell_smbios_release_buffer();
        return ret;
 }
 
@@ -1293,17 +1149,18 @@ static bool kbd_led_present;
 
 static int kbd_get_info(struct kbd_info *info)
 {
+       struct calling_interface_buffer *buffer;
        u8 units;
        int ret;
 
-       get_buffer();
+       buffer = dell_smbios_get_buffer();
 
        buffer->input[0] = 0x0;
-       dell_send_request(buffer, 4, 11);
+       dell_smbios_send_request(4, 11);
        ret = buffer->output[0];
 
        if (ret) {
-               ret = dell_smi_error(ret);
+               ret = dell_smbios_error(ret);
                goto out;
        }
 
@@ -1323,7 +1180,7 @@ static int kbd_get_info(struct kbd_info *info)
                info->days = (buffer->output[3] >> 24) & 0xFF;
 
  out:
-       release_buffer();
+       dell_smbios_release_buffer();
        return ret;
 }
 
@@ -1382,16 +1239,17 @@ static int kbd_set_level(struct kbd_state *state, u8 level)
 
 static int kbd_get_state(struct kbd_state *state)
 {
+       struct calling_interface_buffer *buffer;
        int ret;
 
-       get_buffer();
+       buffer = dell_smbios_get_buffer();
 
        buffer->input[0] = 0x1;
-       dell_send_request(buffer, 4, 11);
+       dell_smbios_send_request(4, 11);
        ret = buffer->output[0];
 
        if (ret) {
-               ret = dell_smi_error(ret);
+               ret = dell_smbios_error(ret);
                goto out;
        }
 
@@ -1407,15 +1265,16 @@ static int kbd_get_state(struct kbd_state *state)
        state->level = (buffer->output[2] >> 16) & 0xFF;
 
  out:
-       release_buffer();
+       dell_smbios_release_buffer();
        return ret;
 }
 
 static int kbd_set_state(struct kbd_state *state)
 {
+       struct calling_interface_buffer *buffer;
        int ret;
 
-       get_buffer();
+       buffer = dell_smbios_get_buffer();
        buffer->input[0] = 0x2;
        buffer->input[1] = BIT(state->mode_bit) & 0xFFFF;
        buffer->input[1] |= (state->triggers & 0xFF) << 16;
@@ -1423,11 +1282,11 @@ static int kbd_set_state(struct kbd_state *state)
        buffer->input[1] |= (state->timeout_unit & 0x3) << 30;
        buffer->input[2] = state->als_setting & 0xFF;
        buffer->input[2] |= (state->level & 0xFF) << 16;
-       dell_send_request(buffer, 4, 11);
+       dell_smbios_send_request(4, 11);
        ret = buffer->output[0];
-       release_buffer();
+       dell_smbios_release_buffer();
 
-       return dell_smi_error(ret);
+       return dell_smbios_error(ret);
 }
 
 static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
@@ -1452,50 +1311,52 @@ static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
 
 static int kbd_set_token_bit(u8 bit)
 {
-       int id;
+       struct calling_interface_buffer *buffer;
+       struct calling_interface_token *token;
        int ret;
 
        if (bit >= ARRAY_SIZE(kbd_tokens))
                return -EINVAL;
 
-       id = find_token_id(kbd_tokens[bit]);
-       if (id == -1)
+       token = dell_smbios_find_token(kbd_tokens[bit]);
+       if (!token)
                return -EINVAL;
 
-       get_buffer();
-       buffer->input[0] = da_tokens[id].location;
-       buffer->input[1] = da_tokens[id].value;
-       dell_send_request(buffer, 1, 0);
+       buffer = dell_smbios_get_buffer();
+       buffer->input[0] = token->location;
+       buffer->input[1] = token->value;
+       dell_smbios_send_request(1, 0);
        ret = buffer->output[0];
-       release_buffer();
+       dell_smbios_release_buffer();
 
-       return dell_smi_error(ret);
+       return dell_smbios_error(ret);
 }
 
 static int kbd_get_token_bit(u8 bit)
 {
-       int id;
+       struct calling_interface_buffer *buffer;
+       struct calling_interface_token *token;
        int ret;
        int val;
 
        if (bit >= ARRAY_SIZE(kbd_tokens))
                return -EINVAL;
 
-       id = find_token_id(kbd_tokens[bit]);
-       if (id == -1)
+       token = dell_smbios_find_token(kbd_tokens[bit]);
+       if (!token)
                return -EINVAL;
 
-       get_buffer();
-       buffer->input[0] = da_tokens[id].location;
-       dell_send_request(buffer, 0, 0);
+       buffer = dell_smbios_get_buffer();
+       buffer->input[0] = token->location;
+       dell_smbios_send_request(0, 0);
        ret = buffer->output[0];
        val = buffer->output[1];
-       release_buffer();
+       dell_smbios_release_buffer();
 
        if (ret)
-               return dell_smi_error(ret);
+               return dell_smbios_error(ret);
 
-       return (val == da_tokens[id].value);
+       return (val == token->value);
 }
 
 static int kbd_get_first_active_token_bit(void)
@@ -1597,7 +1458,7 @@ static inline void kbd_init_tokens(void)
        int i;
 
        for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i)
-               if (find_token_id(kbd_tokens[i]) != -1)
+               if (dell_smbios_find_token(kbd_tokens[i]))
                        kbd_token_bits |= BIT(i);
 }
 
@@ -2111,8 +1972,9 @@ static void kbd_led_exit(void)
 
 static int __init dell_init(void)
 {
+       struct calling_interface_buffer *buffer;
+       struct calling_interface_token *token;
        int max_intensity = 0;
-       int token;
        int ret;
 
        if (!dmi_check_system(dell_device_table))
@@ -2122,13 +1984,6 @@ static int __init dell_init(void)
        /* find if this machine support other functions */
        dmi_check_system(dell_quirks);
 
-       dmi_walk(find_tokens, NULL);
-
-       if (!da_tokens)  {
-               pr_info("Unable to find dmi tokens\n");
-               return -ENODEV;
-       }
-
        ret = platform_driver_register(&platform_driver);
        if (ret)
                goto fail_platform_driver;
@@ -2141,16 +1996,6 @@ static int __init dell_init(void)
        if (ret)
                goto fail_platform_device2;
 
-       /*
-        * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
-        * is passed to SMI handler.
-        */
-       buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
-       if (!buffer) {
-               ret = -ENOMEM;
-               goto fail_buffer;
-       }
-
        ret = dell_setup_rfkill();
 
        if (ret) {
@@ -2171,14 +2016,14 @@ static int __init dell_init(void)
        if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
                return 0;
 
-       token = find_token_location(BRIGHTNESS_TOKEN);
-       if (token != -1) {
-               get_buffer();
-               buffer->input[0] = token;
-               dell_send_request(buffer, 0, 2);
+       token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
+       if (token) {
+               buffer = dell_smbios_get_buffer();
+               buffer->input[0] = token->location;
+               dell_smbios_send_request(0, 2);
                if (buffer->output[0] == 0)
                        max_intensity = buffer->output[3];
-               release_buffer();
+               dell_smbios_release_buffer();
        }
 
        if (max_intensity) {
@@ -2208,15 +2053,12 @@ static int __init dell_init(void)
 fail_backlight:
        dell_cleanup_rfkill();
 fail_rfkill:
-       free_page((unsigned long)buffer);
-fail_buffer:
        platform_device_del(platform_device);
 fail_platform_device2:
        platform_device_put(platform_device);
 fail_platform_device1:
        platform_driver_unregister(&platform_driver);
 fail_platform_driver:
-       kfree(da_tokens);
        return ret;
 }
 
@@ -2232,8 +2074,6 @@ static void __exit dell_exit(void)
                platform_device_unregister(platform_device);
                platform_driver_unregister(&platform_driver);
        }
-       kfree(da_tokens);
-       free_page((unsigned long)buffer);
 }
 
 /* dell-rbtn.c driver export functions which will not work correctly (and could
index cd410e39255059ffa87d7c4cd1010c4481cc02cc..b51a2008d7821908f3da26ec21868d1dd3a71b59 100644 (file)
@@ -217,6 +217,21 @@ static void rbtn_notify(struct acpi_device *device, u32 event);
 static const struct acpi_device_id rbtn_ids[] = {
        { "DELRBTN", 0 },
        { "DELLABCE", 0 },
+
+       /*
+        * This driver can also handle the "DELLABC6" device that
+        * appears on the XPS 13 9350, but that device is disabled
+        * by the DSDT unless booted with acpi_osi="!Windows 2012"
+        * acpi_osi="!Windows 2013".  Even if we boot that and bind
+        * the driver, we seem to have inconsistent behavior in
+        * which NetworkManager can get out of sync with the rfkill
+        * state.
+        *
+        * On the XPS 13 9350 and similar laptops, we're not supposed to
+        * use DELLABC6 at all.  Instead, we handle the rfkill button
+        * via the intel-hid driver.
+        */
+
        { "", 0 },
 };
 
diff --git a/drivers/platform/x86/dell-smbios.c b/drivers/platform/x86/dell-smbios.c
new file mode 100644 (file)
index 0000000..d2412ab
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ *  Common functions for kernel modules using Dell SMBIOS
+ *
+ *  Copyright (c) Red Hat <mjg@redhat.com>
+ *  Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
+ *  Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
+ *
+ *  Based on documentation in the libsmbios package:
+ *  Copyright (C) 2005-2014 Dell Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/err.h>
+#include <linux/gfp.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include "../../firmware/dcdbas.h"
+#include "dell-smbios.h"
+
+struct calling_interface_structure {
+       struct dmi_header header;
+       u16 cmdIOAddress;
+       u8 cmdIOCode;
+       u32 supportedCmds;
+       struct calling_interface_token tokens[];
+} __packed;
+
+static struct calling_interface_buffer *buffer;
+static DEFINE_MUTEX(buffer_mutex);
+
+static int da_command_address;
+static int da_command_code;
+static int da_num_tokens;
+static struct calling_interface_token *da_tokens;
+
+int dell_smbios_error(int value)
+{
+       switch (value) {
+       case 0: /* Completed successfully */
+               return 0;
+       case -1: /* Completed with error */
+               return -EIO;
+       case -2: /* Function not supported */
+               return -ENXIO;
+       default: /* Unknown error */
+               return -EINVAL;
+       }
+}
+EXPORT_SYMBOL_GPL(dell_smbios_error);
+
+struct calling_interface_buffer *dell_smbios_get_buffer(void)
+{
+       mutex_lock(&buffer_mutex);
+       dell_smbios_clear_buffer();
+       return buffer;
+}
+EXPORT_SYMBOL_GPL(dell_smbios_get_buffer);
+
+void dell_smbios_clear_buffer(void)
+{
+       memset(buffer, 0, sizeof(struct calling_interface_buffer));
+}
+EXPORT_SYMBOL_GPL(dell_smbios_clear_buffer);
+
+void dell_smbios_release_buffer(void)
+{
+       mutex_unlock(&buffer_mutex);
+}
+EXPORT_SYMBOL_GPL(dell_smbios_release_buffer);
+
+void dell_smbios_send_request(int class, int select)
+{
+       struct smi_cmd command;
+
+       command.magic = SMI_CMD_MAGIC;
+       command.command_address = da_command_address;
+       command.command_code = da_command_code;
+       command.ebx = virt_to_phys(buffer);
+       command.ecx = 0x42534931;
+
+       buffer->class = class;
+       buffer->select = select;
+
+       dcdbas_smi_request(&command);
+}
+EXPORT_SYMBOL_GPL(dell_smbios_send_request);
+
+struct calling_interface_token *dell_smbios_find_token(int tokenid)
+{
+       int i;
+
+       for (i = 0; i < da_num_tokens; i++) {
+               if (da_tokens[i].tokenID == tokenid)
+                       return &da_tokens[i];
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(dell_smbios_find_token);
+
+static void __init parse_da_table(const struct dmi_header *dm)
+{
+       /* Final token is a terminator, so we don't want to copy it */
+       int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
+       struct calling_interface_token *new_da_tokens;
+       struct calling_interface_structure *table =
+               container_of(dm, struct calling_interface_structure, header);
+
+       /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
+          6 bytes of entry */
+
+       if (dm->length < 17)
+               return;
+
+       da_command_address = table->cmdIOAddress;
+       da_command_code = table->cmdIOCode;
+
+       new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
+                                sizeof(struct calling_interface_token),
+                                GFP_KERNEL);
+
+       if (!new_da_tokens)
+               return;
+       da_tokens = new_da_tokens;
+
+       memcpy(da_tokens+da_num_tokens, table->tokens,
+              sizeof(struct calling_interface_token) * tokens);
+
+       da_num_tokens += tokens;
+}
+
+static void __init find_tokens(const struct dmi_header *dm, void *dummy)
+{
+       switch (dm->type) {
+       case 0xd4: /* Indexed IO */
+       case 0xd5: /* Protected Area Type 1 */
+       case 0xd6: /* Protected Area Type 2 */
+               break;
+       case 0xda: /* Calling interface */
+               parse_da_table(dm);
+               break;
+       }
+}
+
+static int __init dell_smbios_init(void)
+{
+       int ret;
+
+       dmi_walk(find_tokens, NULL);
+
+       if (!da_tokens)  {
+               pr_info("Unable to find dmi tokens\n");
+               return -ENODEV;
+       }
+
+       /*
+        * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
+        * is passed to SMI handler.
+        */
+       buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
+       if (!buffer) {
+               ret = -ENOMEM;
+               goto fail_buffer;
+       }
+
+       return 0;
+
+fail_buffer:
+       kfree(da_tokens);
+       return ret;
+}
+
+static void __exit dell_smbios_exit(void)
+{
+       kfree(da_tokens);
+       free_page((unsigned long)buffer);
+}
+
+subsys_initcall(dell_smbios_init);
+module_exit(dell_smbios_exit);
+
+MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
+MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
+MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
+MODULE_DESCRIPTION("Common functions for kernel modules using Dell SMBIOS");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/dell-smbios.h b/drivers/platform/x86/dell-smbios.h
new file mode 100644 (file)
index 0000000..ec7d40a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *  Common functions for kernel modules using Dell SMBIOS
+ *
+ *  Copyright (c) Red Hat <mjg@redhat.com>
+ *  Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
+ *  Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
+ *
+ *  Based on documentation in the libsmbios package:
+ *  Copyright (C) 2005-2014 Dell Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#ifndef _DELL_SMBIOS_H_
+#define _DELL_SMBIOS_H_
+
+/* This structure will be modified by the firmware when we enter
+ * system management mode, hence the volatiles */
+
+struct calling_interface_buffer {
+       u16 class;
+       u16 select;
+       volatile u32 input[4];
+       volatile u32 output[4];
+} __packed;
+
+struct calling_interface_token {
+       u16 tokenID;
+       u16 location;
+       union {
+               u16 value;
+               u16 stringlength;
+       };
+};
+
+int dell_smbios_error(int value);
+
+struct calling_interface_buffer *dell_smbios_get_buffer(void);
+void dell_smbios_clear_buffer(void);
+void dell_smbios_release_buffer(void);
+void dell_smbios_send_request(int class, int select);
+
+struct calling_interface_token *dell_smbios_find_token(int tokenid);
+#endif
index 368e193c27418cca3c67a23e882285bc767b4414..15c6f1191aec3f1aa7751b3b935d2d871bc599dd 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/string.h>
 #include <linux/dmi.h>
 #include <acpi/video.h>
+#include "dell-smbios.h"
 
 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
 MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
@@ -47,10 +48,37 @@ MODULE_LICENSE("GPL");
 #define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
 
 static u32 dell_wmi_interface_version;
+static bool wmi_requires_smbios_request;
 
 MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
 MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID);
 
+static int __init dmi_matched(const struct dmi_system_id *dmi)
+{
+       wmi_requires_smbios_request = 1;
+       return 1;
+}
+
+static const struct dmi_system_id dell_wmi_smbios_list[] __initconst = {
+       {
+               .callback = dmi_matched,
+               .ident = "Dell Inspiron M5110",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"),
+               },
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Dell Vostro V131",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
+               },
+       },
+       { }
+};
+
 /*
  * Certain keys are flagged as KE_IGNORE. All of these are either
  * notifications (rather than requests for change) or are also sent
@@ -90,8 +118,11 @@ static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
 
        { KE_IGNORE, 0xe020, { KEY_MUTE } },
 
-       /* Shortcut and audio panel keys */
-       { KE_IGNORE, 0xe025, { KEY_RESERVED } },
+       /* Dell Instant Launch key */
+       { KE_KEY, 0xe025, { KEY_PROG4 } },
+       { KE_KEY, 0xe029, { KEY_PROG4 } },
+
+       /* Audio panel key */
        { KE_IGNORE, 0xe026, { KEY_RESERVED } },
 
        { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
@@ -120,7 +151,10 @@ struct dell_bios_hotkey_table {
 
 };
 
-static const struct dell_bios_hotkey_table *dell_bios_hotkey_table;
+struct dell_dmi_results {
+       int err;
+       struct key_entry *keymap;
+};
 
 /* Uninitialized entries here are KEY_RESERVED == 0. */
 static const u16 bios_to_linux_keycode[256] __initconst = {
@@ -166,6 +200,30 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
        [255]   = KEY_PROG3,
 };
 
+/*
+ * 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 = {
+       /* Fn-lock */
+       { KE_IGNORE, 0x151, { KEY_RESERVED } },
+
+       /* Change keyboard illumination */
+       { KE_IGNORE, 0x152, { KEY_KBDILLUMTOGGLE } },
+
+       /*
+        * Radio disable (notify only -- there is no model for which the
+        * WMI event is supposed to trigger an action).
+        */
+       { KE_IGNORE, 0x153, { KEY_RFKILL } },
+
+       /* RGB keyboard backlight control */
+       { KE_IGNORE, 0x154, { KEY_RESERVED } },
+
+       /* Stealth mode toggle */
+       { KE_IGNORE, 0x155, { KEY_RESERVED } },
+};
+
 static struct input_dev *dell_wmi_input_dev;
 
 static void dell_wmi_process_key(int reported_key)
@@ -188,6 +246,9 @@ static void dell_wmi_process_key(int reported_key)
            acpi_video_handles_brightness_key_presses())
                return;
 
+       if (reported_key == 0xe025 && !wmi_requires_smbios_request)
+               return;
+
        sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
 }
 
@@ -337,20 +398,60 @@ static void dell_wmi_notify(u32 value, void *context)
        kfree(obj);
 }
 
-static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
+static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)
 {
-       int hotkey_num = (dell_bios_hotkey_table->header.length - 4) /
-                               sizeof(struct dell_bios_keymap_entry);
-       struct key_entry *keymap;
        int i;
 
-       keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL);
-       if (!keymap)
-               return NULL;
+       for (i = 0; i < len; i++)
+               if (keymap[i].code == scancode)
+                       return true;
+
+       return false;
+}
+
+static void __init handle_dmi_entry(const struct dmi_header *dm,
+
+                                   void *opaque)
+
+{
+       struct dell_dmi_results *results = opaque;
+       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. */
+
+       if (dm->type != 0xb2)
+               return;
+
+       table = container_of(dm, struct dell_bios_hotkey_table, header);
+
+       hotkey_num = (table->header.length -
+                     sizeof(struct dell_bios_hotkey_table)) /
+                               sizeof(struct dell_bios_keymap_entry);
+       if (hotkey_num < 1) {
+               /*
+                * Historically, dell-wmi would ignore a DMI entry of
+                * fewer than 7 bytes.  Sizes between 4 and 8 bytes are
+                * nonsensical (both the header and all entries are 4
+                * bytes), so we approximate the old behavior by
+                * ignoring tables with fewer than one entry.
+                */
+               return;
+       }
+
+       keymap = kcalloc(hotkey_num + ARRAY_SIZE(dell_wmi_extra_keymap) + 1,
+                        sizeof(struct key_entry), GFP_KERNEL);
+       if (!keymap) {
+               results->err = -ENOMEM;
+               return;
+       }
 
        for (i = 0; i < hotkey_num; i++) {
                const struct dell_bios_keymap_entry *bios_entry =
-                                       &dell_bios_hotkey_table->keymap[i];
+                                       &table->keymap[i];
 
                /* Uninitialized entries are 0 aka KEY_RESERVED. */
                u16 keycode = (bios_entry->keycode <
@@ -370,20 +471,39 @@ static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
                }
 
                if (keycode == KEY_KBDILLUMTOGGLE)
-                       keymap[i].type = KE_IGNORE;
+                       keymap[pos].type = KE_IGNORE;
                else
-                       keymap[i].type = KE_KEY;
-               keymap[i].code = bios_entry->scancode;
-               keymap[i].keycode = keycode;
+                       keymap[pos].type = KE_KEY;
+               keymap[pos].code = bios_entry->scancode;
+               keymap[pos].keycode = keycode;
+
+               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[hotkey_num].type = KE_END;
+       keymap[pos].type = KE_END;
 
-       return keymap;
+       results->keymap = keymap;
 }
 
 static int __init dell_wmi_input_setup(void)
 {
+       struct dell_dmi_results dmi_results = {};
        int err;
 
        dell_wmi_input_dev = input_allocate_device();
@@ -394,20 +514,31 @@ static int __init dell_wmi_input_setup(void)
        dell_wmi_input_dev->phys = "wmi/input0";
        dell_wmi_input_dev->id.bustype = BUS_HOST;
 
-       if (dell_new_hk_type) {
-               const struct key_entry *keymap = dell_wmi_prepare_new_keymap();
-               if (!keymap) {
-                       err = -ENOMEM;
-                       goto err_free_dev;
-               }
+       if (dmi_walk(handle_dmi_entry, &dmi_results)) {
+               /*
+                * Historically, dell-wmi ignored dmi_walk errors.  A failure
+                * is certainly surprising, but it probably just indicates
+                * a very old laptop.
+                */
+               pr_warn("no DMI; using the old-style hotkey interface\n");
+       }
 
-               err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
+       if (dmi_results.err) {
+               err = dmi_results.err;
+               goto err_free_dev;
+       }
+
+       if (dmi_results.keymap) {
+               dell_new_hk_type = true;
+
+               err = sparse_keymap_setup(dell_wmi_input_dev,
+                                         dmi_results.keymap, NULL);
 
                /*
                 * Sparse keymap library makes a copy of keymap so we
                 * don't need the original one that was allocated.
                 */
-               kfree(keymap);
+               kfree(dmi_results.keymap);
        } else {
                err = sparse_keymap_setup(dell_wmi_input_dev,
                                          dell_wmi_legacy_keymap, NULL);
@@ -434,15 +565,6 @@ static void dell_wmi_input_destroy(void)
        input_unregister_device(dell_wmi_input_dev);
 }
 
-static void __init find_hk_type(const struct dmi_header *dm, void *dummy)
-{
-       if (dm->type == 0xb2 && dm->length > 6) {
-               dell_new_hk_type = true;
-               dell_bios_hotkey_table =
-                       container_of(dm, struct dell_bios_hotkey_table, header);
-       }
-}
-
 /*
  * Descriptor buffer is 128 byte long and contains:
  *
@@ -509,6 +631,38 @@ static int __init dell_wmi_check_descriptor_buffer(void)
        return 0;
 }
 
+/*
+ * According to Dell SMBIOS documentation:
+ *
+ * 17  3  Application Program Registration
+ *
+ *     cbArg1 Application ID 1 = 0x00010000
+ *     cbArg2 Application ID 2
+ *            QUICKSET/DCP = 0x51534554 "QSET"
+ *            ALS Driver   = 0x416c7353 "AlsS"
+ *            Latitude ON  = 0x4c6f6e52 "LonR"
+ *     cbArg3 Application version or revision number
+ *     cbArg4 0 = Unregister application
+ *            1 = Register application
+ *     cbRes1 Standard return codes (0, -1, -2)
+ */
+
+static int dell_wmi_events_set_enabled(bool enable)
+{
+       struct calling_interface_buffer *buffer;
+       int ret;
+
+       buffer = dell_smbios_get_buffer();
+       buffer->input[0] = 0x10000;
+       buffer->input[1] = 0x51534554;
+       buffer->input[3] = enable;
+       dell_smbios_send_request(17, 3);
+       ret = buffer->output[0];
+       dell_smbios_release_buffer();
+
+       return dell_smbios_error(ret);
+}
+
 static int __init dell_wmi_init(void)
 {
        int err;
@@ -524,8 +678,6 @@ static int __init dell_wmi_init(void)
        if (err)
                return err;
 
-       dmi_walk(find_hk_type, NULL);
-
        err = dell_wmi_input_setup();
        if (err)
                return err;
@@ -538,12 +690,26 @@ static int __init dell_wmi_init(void)
                return -ENODEV;
        }
 
+       dmi_check_system(dell_wmi_smbios_list);
+
+       if (wmi_requires_smbios_request) {
+               err = dell_wmi_events_set_enabled(true);
+               if (err) {
+                       pr_err("Failed to enable WMI events\n");
+                       wmi_remove_notify_handler(DELL_EVENT_GUID);
+                       dell_wmi_input_destroy();
+                       return err;
+               }
+       }
+
        return 0;
 }
 module_init(dell_wmi_init);
 
 static void __exit dell_wmi_exit(void)
 {
+       if (wmi_requires_smbios_request)
+               dell_wmi_events_set_enabled(false);
        wmi_remove_notify_handler(DELL_EVENT_GUID);
        dell_wmi_input_destroy();
 }
index 1c62caff93fdfce5e75924a84eb33a5af9fc00ae..ffc84cc7b1c79fc8b8bdecd5cd2ae1201b338194 100644 (file)
 #define KEY2_CODE      0x411
 #define KEY3_CODE      0x412
 #define KEY4_CODE      0x413
+#define KEY5_CODE      0x420
 
 #define MAX_HOTKEY_RINGBUFFER_SIZE 100
 #define RINGBUFFERSIZE 40
@@ -149,7 +150,7 @@ struct fujitsu_t {
        char phys[32];
        struct backlight_device *bl_device;
        struct platform_device *pf_device;
-       int keycode1, keycode2, keycode3, keycode4;
+       int keycode1, keycode2, keycode3, keycode4, keycode5;
 
        unsigned int max_brightness;
        unsigned int brightness_changed;
@@ -823,6 +824,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
        set_bit(fujitsu->keycode2, input->keybit);
        set_bit(fujitsu->keycode3, input->keybit);
        set_bit(fujitsu->keycode4, input->keybit);
+       set_bit(fujitsu->keycode5, input->keybit);
        set_bit(KEY_UNKNOWN, input->keybit);
 
        error = input_register_device(input);
@@ -962,6 +964,9 @@ static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event)
                        case KEY4_CODE:
                                keycode = fujitsu->keycode4;
                                break;
+                       case KEY5_CODE:
+                               keycode = fujitsu->keycode5;
+                               break;
                        case 0:
                                keycode = 0;
                                break;
@@ -1072,6 +1077,7 @@ static int __init fujitsu_init(void)
        fujitsu->keycode2 = KEY_PROG2;
        fujitsu->keycode3 = KEY_PROG3;
        fujitsu->keycode4 = KEY_PROG4;
+       fujitsu->keycode5 = KEY_RFKILL;
        dmi_check_system(fujitsu_dmi_table);
 
        result = acpi_bus_register_driver(&acpi_fujitsu_driver);
index fb4dd7b3ee711f9ba9b42b4384331c1df9e32fc0..6f145f2d004d86a42858dee42b371f6d9584886d 100644 (file)
@@ -157,7 +157,6 @@ static struct platform_device *hp_wmi_platform_dev;
 static struct rfkill *wifi_rfkill;
 static struct rfkill *bluetooth_rfkill;
 static struct rfkill *wwan_rfkill;
-static struct rfkill *gps_rfkill;
 
 struct rfkill2_device {
        u8 id;
@@ -613,10 +612,6 @@ static void hp_wmi_notify(u32 value, void *context)
                        rfkill_set_states(wwan_rfkill,
                                          hp_wmi_get_sw_state(HPWMI_WWAN),
                                          hp_wmi_get_hw_state(HPWMI_WWAN));
-               if (gps_rfkill)
-                       rfkill_set_states(gps_rfkill,
-                                         hp_wmi_get_sw_state(HPWMI_GPS),
-                                         hp_wmi_get_hw_state(HPWMI_GPS));
                break;
        case HPWMI_CPU_BATTERY_THROTTLE:
                pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n");
@@ -746,7 +741,7 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device)
                                                (void *) HPWMI_BLUETOOTH);
                if (!bluetooth_rfkill) {
                        err = -ENOMEM;
-                       goto register_wifi_error;
+                       goto register_bluetooth_error;
                }
                rfkill_init_sw_state(bluetooth_rfkill,
                                     hp_wmi_get_sw_state(HPWMI_BLUETOOTH));
@@ -764,7 +759,7 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device)
                                           (void *) HPWMI_WWAN);
                if (!wwan_rfkill) {
                        err = -ENOMEM;
-                       goto register_bluetooth_error;
+                       goto register_wwan_error;
                }
                rfkill_init_sw_state(wwan_rfkill,
                                     hp_wmi_get_sw_state(HPWMI_WWAN));
@@ -775,35 +770,13 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device)
                        goto register_wwan_error;
        }
 
-       if (wireless & 0x8) {
-               gps_rfkill = rfkill_alloc("hp-gps", &device->dev,
-                                               RFKILL_TYPE_GPS,
-                                               &hp_wmi_rfkill_ops,
-                                               (void *) HPWMI_GPS);
-               if (!gps_rfkill) {
-                       err = -ENOMEM;
-                       goto register_wwan_error;
-               }
-               rfkill_init_sw_state(gps_rfkill,
-                                    hp_wmi_get_sw_state(HPWMI_GPS));
-               rfkill_set_hw_state(gps_rfkill,
-                                   hp_wmi_get_hw_state(HPWMI_GPS));
-               err = rfkill_register(gps_rfkill);
-               if (err)
-                       goto register_gps_error;
-       }
-
        return 0;
-register_gps_error:
-       rfkill_destroy(gps_rfkill);
-       gps_rfkill = NULL;
-       if (bluetooth_rfkill)
-               rfkill_unregister(bluetooth_rfkill);
+
 register_wwan_error:
        rfkill_destroy(wwan_rfkill);
        wwan_rfkill = NULL;
-       if (gps_rfkill)
-               rfkill_unregister(gps_rfkill);
+       if (bluetooth_rfkill)
+               rfkill_unregister(bluetooth_rfkill);
 register_bluetooth_error:
        rfkill_destroy(bluetooth_rfkill);
        bluetooth_rfkill = NULL;
@@ -907,7 +880,6 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
        wifi_rfkill = NULL;
        bluetooth_rfkill = NULL;
        wwan_rfkill = NULL;
-       gps_rfkill = NULL;
        rfkill2_count = 0;
 
        if (hp_wmi_bios_2009_later() || hp_wmi_rfkill_setup(device))
@@ -960,10 +932,6 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
                rfkill_unregister(wwan_rfkill);
                rfkill_destroy(wwan_rfkill);
        }
-       if (gps_rfkill) {
-               rfkill_unregister(gps_rfkill);
-               rfkill_destroy(gps_rfkill);
-       }
 
        return 0;
 }
@@ -999,10 +967,6 @@ static int hp_wmi_resume_handler(struct device *device)
                rfkill_set_states(wwan_rfkill,
                                  hp_wmi_get_sw_state(HPWMI_WWAN),
                                  hp_wmi_get_hw_state(HPWMI_WWAN));
-       if (gps_rfkill)
-               rfkill_set_states(gps_rfkill,
-                                 hp_wmi_get_sw_state(HPWMI_GPS),
-                                 hp_wmi_get_hw_state(HPWMI_GPS));
 
        return 0;
 }
index d78ee151c9e4a703bbeb071cda98032cd03dccee..be3bc2f4edd4279d04749aba6db4f93aedf06c4f 100644 (file)
@@ -864,6 +864,20 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo G50-30"),
                },
        },
+       {
+               .ident = "Lenovo ideapad Y700-15ISK",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-15ISK"),
+               },
+       },
+       {
+               .ident = "Lenovo ideapad Y700 Touch-15ISK",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700 Touch-15ISK"),
+               },
+       },
        {
                .ident = "Lenovo ideapad Y700-17ISK",
                .matches = {
index e20f23e04c24ce8071ea03ea238b5a1a765d70ff..f93abc8c1424ad956f62b8dbb5a6dadffd6b4c5b 100644 (file)
@@ -180,8 +180,7 @@ static int intel_hid_probe(struct platform_device *device)
                return -ENODEV;
        }
 
-       priv = devm_kzalloc(&device->dev,
-                           sizeof(struct intel_hid_priv *), GFP_KERNEL);
+       priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
        dev_set_drvdata(&device->dev, priv);
index 092519e37de628c1da5ad26d011cd03a07b7c831..3fb1d85c70a89d8d89ab700627691f70a63d03c1 100644 (file)
@@ -67,7 +67,8 @@
 /* exported resources from IFWI */
 #define PLAT_RESOURCE_IPC_INDEX                0
 #define PLAT_RESOURCE_IPC_SIZE         0x1000
-#define PLAT_RESOURCE_GCR_SIZE         0x1000
+#define PLAT_RESOURCE_GCR_OFFSET       0x1008
+#define PLAT_RESOURCE_GCR_SIZE         0x4
 #define PLAT_RESOURCE_BIOS_DATA_INDEX  1
 #define PLAT_RESOURCE_BIOS_IFACE_INDEX 2
 #define PLAT_RESOURCE_TELEM_SSRAM_INDEX        3
@@ -766,7 +767,7 @@ static int ipc_plat_get_res(struct platform_device *pdev)
        }
        ipcdev.ipc_base = addr;
 
-       ipcdev.gcr_base = res->start + size;
+       ipcdev.gcr_base = res->start + PLAT_RESOURCE_GCR_OFFSET;
        ipcdev.gcr_size = PLAT_RESOURCE_GCR_SIZE;
        dev_info(&pdev->dev, "ipc res: %pR\n", res);
 
@@ -824,7 +825,8 @@ static int ipc_plat_probe(struct platform_device *pdev)
                goto err_device;
        }
 
-       if (request_irq(ipcdev.irq, ioc, 0, "intel_pmc_ipc", &ipcdev)) {
+       if (request_irq(ipcdev.irq, ioc, IRQF_NO_SUSPEND,
+                       "intel_pmc_ipc", &ipcdev)) {
                dev_err(&pdev->dev, "Failed to request irq\n");
                ret = -EBUSY;
                goto err_irq;
index f94b730540e240334e5fe631bd9f60997f0b2dbc..e81daff65f62270cf4a7a4dd0365439c9c1d5348 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/pci.h>
 #include <linux/interrupt.h>
 #include <linux/sfi.h>
-#include <linux/module.h>
 #include <asm/intel-mid.h>
 #include <asm/intel_scu_ipc.h>
 
@@ -611,28 +610,6 @@ static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        return 0;
 }
 
-/**
- *     ipc_remove      -       remove a bound IPC device
- *     @pdev: PCI device
- *
- *     In practice the SCU is not removable but this function is also
- *     called for each device on a module unload or cleanup which is the
- *     path that will get used.
- *
- *     Free up the mappings and release the PCI resources
- */
-static void ipc_remove(struct pci_dev *pdev)
-{
-       struct intel_scu_ipc_dev *scu = pci_get_drvdata(pdev);
-
-       mutex_lock(&ipclock);
-       scu->dev = NULL;
-       mutex_unlock(&ipclock);
-
-       iounmap(scu->i2c_base);
-       intel_scu_devices_destroy();
-}
-
 static const struct pci_device_id pci_ids[] = {
        {
                PCI_VDEVICE(INTEL, PCI_DEVICE_ID_LINCROFT),
@@ -650,17 +627,13 @@ static const struct pci_device_id pci_ids[] = {
                0,
        }
 };
-MODULE_DEVICE_TABLE(pci, pci_ids);
 
 static struct pci_driver ipc_driver = {
+       .driver = {
+               .suppress_bind_attrs = true,
+       },
        .name = "intel_scu_ipc",
        .id_table = pci_ids,
        .probe = ipc_probe,
-       .remove = ipc_remove,
 };
-
-module_pci_driver(ipc_driver);
-
-MODULE_AUTHOR("Sreedhara DS <sreedhara.ds@intel.com>");
-MODULE_DESCRIPTION("Intel SCU IPC driver");
-MODULE_LICENSE("GPL");
+builtin_pci_driver(ipc_driver);
index f97019b0106f2c67277c85d8000d2185af6e50f1..397119f83e82384913a305065a70f1ca7ae815d8 100644 (file)
@@ -1029,9 +1029,20 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
        mutex_lock(&(telm_conf->telem_trace_lock));
        switch (telem_unit) {
        case TELEM_PSS:
+               ret = intel_punit_ipc_command(
+                               IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
+                               0, 0, NULL, &temp);
+               if (ret) {
+                       pr_err("PSS TRACE_CTRL Read Failed\n");
+                       goto out;
+               }
+
+               TELEM_CLEAR_VERBOSITY_BITS(temp);
+               TELEM_SET_VERBOSITY_BITS(temp, verbosity);
+
                ret = intel_punit_ipc_command(
                                IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL,
-                               0, 0, &verbosity, NULL);
+                               0, 0, &temp, NULL);
                if (ret) {
                        pr_err("PSS TRACE_CTRL Verbosity Set Failed\n");
                        goto out;
index a268a7abf8abe754f30fba55941b04fef2ffdef8..e305ab541a2227e1f94ff3e80a0da369ed385946 100644 (file)
@@ -6653,18 +6653,16 @@ static void __init tpacpi_detect_brightness_capabilities(void)
        switch (b) {
        case 16:
                bright_maxlvl = 15;
-               pr_info("detected a 16-level brightness capable ThinkPad\n");
                break;
        case 8:
        case 0:
                bright_maxlvl = 7;
-               pr_info("detected a 8-level brightness capable ThinkPad\n");
                break;
        default:
-               pr_info("Unsupported brightness interface\n");
                tp_features.bright_unkfw = 1;
                bright_maxlvl = b - 1;
        }
+       pr_debug("detected %u brightness levels\n", bright_maxlvl + 1);
 }
 
 static int __init brightness_init(struct ibm_init_struct *iibm)
index 73833079bac89a0b97381037b7eacef125ca15fe..df1f1a76a8629fdf4c1b709ff519040ffad18552 100644 (file)
@@ -36,6 +36,7 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/proc_fs.h>
@@ -117,6 +118,7 @@ MODULE_LICENSE("GPL");
 #define HCI_LCD_BRIGHTNESS             0x002a
 #define HCI_WIRELESS                   0x0056
 #define HCI_ACCELEROMETER              0x006d
+#define HCI_COOLING_METHOD             0x007f
 #define HCI_KBD_ILLUMINATION           0x0095
 #define HCI_ECO_MODE                   0x0097
 #define HCI_ACCELEROMETER2             0x00a6
@@ -186,6 +188,7 @@ struct toshiba_acpi_dev {
        int usbsc_bat_level;
        int usbsc_mode_base;
        int hotkey_event_type;
+       int max_cooling_method;
 
        unsigned int illumination_supported:1;
        unsigned int video_supported:1;
@@ -205,6 +208,7 @@ struct toshiba_acpi_dev {
        unsigned int panel_power_on_supported:1;
        unsigned int usb_three_supported:1;
        unsigned int wwan_supported:1;
+       unsigned int cooling_method_supported:1;
        unsigned int sysfs_created:1;
        unsigned int special_functions;
 
@@ -217,6 +221,10 @@ struct toshiba_acpi_dev {
 
 static struct toshiba_acpi_dev *toshiba_acpi;
 
+static bool disable_hotkeys;
+module_param(disable_hotkeys, bool, 0444);
+MODULE_PARM_DESC(disable_hotkeys, "Disables the hotkeys activation");
+
 static const struct acpi_device_id toshiba_device_ids[] = {
        {"TOS6200", 0},
        {"TOS6207", 0},
@@ -1194,6 +1202,53 @@ static int toshiba_wwan_set(struct toshiba_acpi_dev *dev, u32 state)
        return out[0] == TOS_SUCCESS ? 0 : -EIO;
 }
 
+/* Cooling Method */
+static void toshiba_cooling_method_available(struct toshiba_acpi_dev *dev)
+{
+       u32 in[TCI_WORDS] = { HCI_GET, HCI_COOLING_METHOD, 0, 0, 0, 0 };
+       u32 out[TCI_WORDS];
+       acpi_status status;
+
+       dev->cooling_method_supported = 0;
+       dev->max_cooling_method = 0;
+
+       status = tci_raw(dev, in, out);
+       if (ACPI_FAILURE(status))
+               pr_err("ACPI call to get Cooling Method failed\n");
+
+       if (out[0] != TOS_SUCCESS && out[0] != TOS_SUCCESS2)
+               return;
+
+       dev->cooling_method_supported = 1;
+       dev->max_cooling_method = out[3];
+}
+
+static int toshiba_cooling_method_get(struct toshiba_acpi_dev *dev, u32 *state)
+{
+       u32 result = hci_read(dev, HCI_COOLING_METHOD, state);
+
+       if (result == TOS_FAILURE)
+               pr_err("ACPI call to get Cooling Method failed\n");
+
+       if (result == TOS_NOT_SUPPORTED)
+               return -ENODEV;
+
+       return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO;
+}
+
+static int toshiba_cooling_method_set(struct toshiba_acpi_dev *dev, u32 state)
+{
+       u32 result = hci_write(dev, HCI_COOLING_METHOD, state);
+
+       if (result == TOS_FAILURE)
+               pr_err("ACPI call to get Cooling Method failed\n");
+
+       if (result == TOS_NOT_SUPPORTED)
+               return -ENODEV;
+
+       return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO;
+}
+
 /* Transflective Backlight */
 static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, u32 *status)
 {
@@ -2239,6 +2294,54 @@ static ssize_t usb_three_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(usb_three);
 
+static ssize_t cooling_method_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+       int state;
+       int ret;
+
+       ret = toshiba_cooling_method_get(toshiba, &state);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%d %d\n", state, toshiba->max_cooling_method);
+}
+
+static ssize_t cooling_method_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;
+       int ret;
+
+       ret = kstrtoint(buf, 0, &state);
+       if (ret)
+               return ret;
+
+       /*
+        * Check for supported values
+        * Depending on the laptop model, some only support these two:
+        * 0 - Maximum Performance
+        * 1 - Battery Optimized
+        *
+        * While some others support all three methods:
+        * 0 - Maximum Performance
+        * 1 - Performance
+        * 2 - Battery Optimized
+        */
+       if (state < 0 || state > toshiba->max_cooling_method)
+               return -EINVAL;
+
+       ret = toshiba_cooling_method_set(toshiba, state);
+       if (ret)
+               return ret;
+
+       return count;
+}
+static DEVICE_ATTR_RW(cooling_method);
+
 static struct attribute *toshiba_attributes[] = {
        &dev_attr_version.attr,
        &dev_attr_fan.attr,
@@ -2255,6 +2358,7 @@ static struct attribute *toshiba_attributes[] = {
        &dev_attr_kbd_function_keys.attr,
        &dev_attr_panel_power_on.attr,
        &dev_attr_usb_three.attr,
+       &dev_attr_cooling_method.attr,
        NULL,
 };
 
@@ -2289,6 +2393,8 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
                exists = (drv->panel_power_on_supported) ? true : false;
        else if (attr == &dev_attr_usb_three.attr)
                exists = (drv->usb_three_supported) ? true : false;
+       else if (attr == &dev_attr_cooling_method.attr)
+               exists = (drv->cooling_method_supported) ? true : false;
 
        return exists ? attr->mode : 0;
 }
@@ -2591,6 +2697,11 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
        acpi_handle ec_handle;
        int error;
 
+       if (disable_hotkeys) {
+               pr_info("Hotkeys disabled by module parameter\n");
+               return 0;
+       }
+
        if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) {
                pr_info("WMI event detected, hotkeys will not be monitored\n");
                return 0;
@@ -2779,6 +2890,8 @@ static void print_supported_features(struct toshiba_acpi_dev *dev)
                pr_cont(" usb3");
        if (dev->wwan_supported)
                pr_cont(" wwan");
+       if (dev->cooling_method_supported)
+               pr_cont(" cooling-method");
 
        pr_cont("\n");
 }
@@ -2963,6 +3076,8 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
        if (dev->wwan_supported)
                toshiba_acpi_setup_wwan_rfkill(dev);
 
+       toshiba_cooling_method_available(dev);
+
        print_supported_features(dev);
 
        ret = sysfs_create_group(&dev->acpi_dev->dev.kobj,