Commit 5a010c73 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'platform-drivers-x86-v4.6-1' of...

Merge tag 'platform-drivers-x86-v4.6-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86

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
  ...
parents b615d3d4 fffcad87
......@@ -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
......@@ -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.
......
......@@ -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)
......
......@@ -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.
......
......@@ -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
......
......@@ -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:
......
......@@ -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",
......
......@@ -28,12 +28,11 @@
#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
......@@ -44,33 +43,6 @@
#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
......
......@@ -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 },
};
......
/*
* 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");
/*
* 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
......@@ -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();
}
......
......@@ -114,6 +114,7 @@
#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);
......
......@@ -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;
}
......
......@@ -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 = {
......
......@@ -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);
......
......@@ -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;
......
......@@ -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);
......@@ -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;
......
......@@ -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)
......
......@@ -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,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment