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
  ...
这个提交包含在:
Linus Torvalds
2016-03-23 17:20:59 -07:00
当前提交 5a010c73cd
修改 21 个文件,包含 1026 行新增536 行删除

查看文件

@@ -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 ASM100",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
},
.driver_data = &quirk_asm100,
},
.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 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 },
};

查看文件

@@ -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");

查看文件

@@ -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

查看文件

@@ -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++;
}
keymap[hotkey_num].type = KE_END;
num_bios_keys = pos;
return keymap;
for (i = 0; i < ARRAY_SIZE(dell_wmi_extra_keymap); i++) {
const struct key_entry *entry = &dell_wmi_extra_keymap[i];
/*
* Check if we've already found this scancode. This takes
* quadratic time, but it doesn't matter unless the list
* of extra keys gets very long.
*/
if (!have_scancode(entry->code, keymap, num_bios_keys)) {
keymap[pos] = *entry;
pos++;
}
}
keymap[pos].type = KE_END;
results->keymap = keymap;
}
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,