summaryrefslogtreecommitdiff
path: root/drivers/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/Kconfig31
-rw-r--r--drivers/platform/x86/Makefile2
-rw-r--r--drivers/platform/x86/acer-wmi.c97
-rw-r--r--drivers/platform/x86/alienware-wmi.c1
-rw-r--r--drivers/platform/x86/asus-wireless.c60
-rw-r--r--drivers/platform/x86/dell-laptop.c6
-rw-r--r--drivers/platform/x86/fujitsu-laptop.c220
-rw-r--r--drivers/platform/x86/hp_accel.c1
-rw-r--r--drivers/platform/x86/intel-hid.c96
-rw-r--r--drivers/platform/x86/intel_mid_powerbtn.c187
-rw-r--r--drivers/platform/x86/intel_mid_thermal.c2
-rw-r--r--drivers/platform/x86/intel_pmc_core.c6
-rw-r--r--drivers/platform/x86/intel_pmc_ipc.c67
-rw-r--r--drivers/platform/x86/intel_turbo_max_3.c151
-rw-r--r--drivers/platform/x86/mlx-platform.c84
-rw-r--r--drivers/platform/x86/silead_dmi.c136
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c124
17 files changed, 951 insertions, 320 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index a9e9c91cf4d4..4bc88eb52712 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -92,9 +92,8 @@ 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
+ tristate
+ select DCDBAS
---help---
This module provides common functions for kernel modules using
Dell SMBIOS.
@@ -103,16 +102,15 @@ config DELL_SMBIOS
config DELL_LAPTOP
tristate "Dell Laptop Extras"
- depends on DELL_SMBIOS
depends on DMI
depends on BACKLIGHT_CLASS_DEVICE
depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on RFKILL || RFKILL = n
depends on SERIO_I8042
+ select DELL_SMBIOS
select POWER_SUPPLY
select LEDS_CLASS
select NEW_LEDS
- default n
---help---
This driver adds support for rfkill and backlight control to Dell
laptops (except for some models covered by the Compal driver).
@@ -123,7 +121,7 @@ config DELL_WMI
depends on DMI
depends on INPUT
depends on ACPI_VIDEO || ACPI_VIDEO = n
- depends on DELL_SMBIOS
+ select DELL_SMBIOS
select INPUT_SPARSEKMAP
---help---
Say Y here if you want to support WMI-based hotkeys on Dell laptops.
@@ -1069,6 +1067,27 @@ config MLX_CPLD_PLATFORM
This driver handles hot-plug events for the power suppliers, power
cables and fans on the wide range Mellanox IB and Ethernet systems.
+config INTEL_TURBO_MAX_3
+ bool "Intel Turbo Boost Max Technology 3.0 enumeration driver"
+ depends on X86_64 && SCHED_MC_PRIO
+ ---help---
+ This driver reads maximum performance ratio of each CPU and set up
+ the scheduler priority metrics. In this way scheduler can prefer
+ CPU with higher performance to schedule tasks.
+ This driver is only required when the system is not using Hardware
+ P-States (HWP). In HWP mode, priority can be read from ACPI tables.
+
+config SILEAD_DMI
+ bool "Tablets with Silead touchscreens"
+ depends on ACPI && DMI && I2C=y && INPUT
+ ---help---
+ Certain ACPI based tablets with Silead touchscreens do not have
+ enough data in ACPI tables for the touchscreen driver to handle
+ the touchscreen properly, as OEMs expected the data to be baked
+ into the tablet model specific version of the driver shipped
+ with the OS-image for the device. This option supplies the missing
+ information. Enable this for x86 tablets with Silead touchscreens.
+
endif # X86_PLATFORM_DEVICES
config PMC_ATOM
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index cf9fc3e930c7..299d0f9e40f7 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o
+obj-$(CONFIG_SILEAD_DMI) += silead_dmi.o
obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o
obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o
@@ -76,3 +77,4 @@ obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o
obj-$(CONFIG_PMC_ATOM) += pmc_atom.o
obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o
obj-$(CONFIG_MLX_CPLD_PLATFORM) += mlxcpld-hotplug.o
+obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index a66192f692e3..dac0fbe87460 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -128,6 +128,7 @@ static const struct key_entry acer_wmi_keymap[] __initconst = {
{KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },
{KE_IGNORE, 0x83, {KEY_TOUCHPAD_TOGGLE} },
{KE_KEY, 0x85, {KEY_TOUCHPAD_TOGGLE} },
+ {KE_KEY, 0x86, {KEY_WLAN} },
{KE_END, 0}
};
@@ -150,15 +151,30 @@ struct event_return_value {
#define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */
#define ACER_WMID3_GDS_TOUCHPAD (1<<1) /* Touchpad */
-struct lm_input_params {
+/* Hotkey Customized Setting and Acer Application Status.
+ * Set Device Default Value and Report Acer Application Status.
+ * When Acer Application starts, it will run this method to inform
+ * BIOS/EC that Acer Application is on.
+ * App Status
+ * Bit[0]: Launch Manager Status
+ * Bit[1]: ePM Status
+ * Bit[2]: Device Control Status
+ * Bit[3]: Acer Power Button Utility Status
+ * Bit[4]: RF Button Status
+ * Bit[5]: ODD PM Status
+ * Bit[6]: Device Default Value Control
+ * Bit[7]: Hall Sensor Application Status
+ */
+struct func_input_params {
u8 function_num; /* Function Number */
u16 commun_devices; /* Communication type devices default status */
u16 devices; /* Other type devices default status */
- u8 lm_status; /* Launch Manager Status */
- u16 reserved;
+ u8 app_status; /* Acer Device Status. LM, ePM, RF Button... */
+ u8 app_mask; /* Bit mask to app_status */
+ u8 reserved;
} __attribute__((packed));
-struct lm_return_value {
+struct func_return_value {
u8 error_code; /* Error Code */
u8 ec_return_value; /* EC Return Value */
u16 reserved;
@@ -1769,13 +1785,13 @@ static void acer_wmi_notify(u32 value, void *context)
}
static acpi_status __init
-wmid3_set_lm_mode(struct lm_input_params *params,
- struct lm_return_value *return_value)
+wmid3_set_function_mode(struct func_input_params *params,
+ struct func_return_value *return_value)
{
acpi_status status;
union acpi_object *obj;
- struct acpi_buffer input = { sizeof(struct lm_input_params), params };
+ struct acpi_buffer input = { sizeof(struct func_input_params), params };
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output);
@@ -1796,7 +1812,7 @@ wmid3_set_lm_mode(struct lm_input_params *params,
return AE_ERROR;
}
- *return_value = *((struct lm_return_value *)obj->buffer.pointer);
+ *return_value = *((struct func_return_value *)obj->buffer.pointer);
kfree(obj);
return status;
@@ -1804,16 +1820,17 @@ wmid3_set_lm_mode(struct lm_input_params *params,
static int __init acer_wmi_enable_ec_raw(void)
{
- struct lm_return_value return_value;
+ struct func_return_value return_value;
acpi_status status;
- struct lm_input_params params = {
+ struct func_input_params params = {
.function_num = 0x1,
.commun_devices = 0xFFFF,
.devices = 0xFFFF,
- .lm_status = 0x00, /* Launch Manager Deactive */
+ .app_status = 0x00, /* Launch Manager Deactive */
+ .app_mask = 0x01,
};
- status = wmid3_set_lm_mode(&params, &return_value);
+ status = wmid3_set_function_mode(&params, &return_value);
if (return_value.error_code || return_value.ec_return_value)
pr_warn("Enabling EC raw mode failed: 0x%x - 0x%x\n",
@@ -1827,16 +1844,17 @@ static int __init acer_wmi_enable_ec_raw(void)
static int __init acer_wmi_enable_lm(void)
{
- struct lm_return_value return_value;
+ struct func_return_value return_value;
acpi_status status;
- struct lm_input_params params = {
+ struct func_input_params params = {
.function_num = 0x1,
.commun_devices = 0xFFFF,
.devices = 0xFFFF,
- .lm_status = 0x01, /* Launch Manager Active */
+ .app_status = 0x01, /* Launch Manager Active */
+ .app_mask = 0x01,
};
- status = wmid3_set_lm_mode(&params, &return_value);
+ status = wmid3_set_function_mode(&params, &return_value);
if (return_value.error_code || return_value.ec_return_value)
pr_warn("Enabling Launch Manager failed: 0x%x - 0x%x\n",
@@ -1846,11 +1864,46 @@ static int __init acer_wmi_enable_lm(void)
return status;
}
+static int __init acer_wmi_enable_rf_button(void)
+{
+ struct func_return_value return_value;
+ acpi_status status;
+ struct func_input_params params = {
+ .function_num = 0x1,
+ .commun_devices = 0xFFFF,
+ .devices = 0xFFFF,
+ .app_status = 0x10, /* RF Button Active */
+ .app_mask = 0x10,
+ };
+
+ status = wmid3_set_function_mode(&params, &return_value);
+
+ if (return_value.error_code || return_value.ec_return_value)
+ pr_warn("Enabling RF Button failed: 0x%x - 0x%x\n",
+ return_value.error_code,
+ return_value.ec_return_value);
+
+ return status;
+}
+
+#define ACER_WMID_ACCEL_HID "BST0001"
+
static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level,
void *ctx, void **retval)
{
+ struct acpi_device *dev;
+
+ if (!strcmp(ctx, "SENR")) {
+ if (acpi_bus_get_device(ah, &dev))
+ return AE_OK;
+ if (!strcmp(ACER_WMID_ACCEL_HID, acpi_device_hid(dev)))
+ return AE_OK;
+ } else
+ return AE_OK;
+
*(acpi_handle *)retval = ah;
- return AE_OK;
+
+ return AE_CTRL_TERMINATE;
}
static int __init acer_wmi_get_handle(const char *name, const char *prop,
@@ -1877,7 +1930,7 @@ static int __init acer_wmi_accel_setup(void)
{
int err;
- err = acer_wmi_get_handle("SENR", "BST0001", &gsensor_handle);
+ err = acer_wmi_get_handle("SENR", ACER_WMID_ACCEL_HID, &gsensor_handle);
if (err)
return err;
@@ -2216,6 +2269,9 @@ static int __init acer_wmi_init(void)
interface->capability &= ~ACER_CAP_BRIGHTNESS;
if (wmi_has_guid(WMID_GUID3)) {
+ if (ACPI_FAILURE(acer_wmi_enable_rf_button()))
+ pr_warn("Cannot enable RF Button Driver\n");
+
if (ec_raw_mode) {
if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) {
pr_err("Cannot enable EC raw mode\n");
@@ -2233,10 +2289,11 @@ static int __init acer_wmi_init(void)
err = acer_wmi_input_setup();
if (err)
return err;
+ err = acer_wmi_accel_setup();
+ if (err)
+ return err;
}
- acer_wmi_accel_setup();
-
err = platform_driver_register(&acer_platform_driver);
if (err) {
pr_err("Unable to register platform driver\n");
diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c
index 005629447b0c..d6b34923fb4e 100644
--- a/drivers/platform/x86/alienware-wmi.c
+++ b/drivers/platform/x86/alienware-wmi.c
@@ -21,7 +21,6 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dmi.h>
-#include <linux/acpi.h>
#include <linux/leds.h>
#define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492"
diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c
index 9f31bc1a47d0..f3796164329e 100644
--- a/drivers/platform/x86/asus-wireless.c
+++ b/drivers/platform/x86/asus-wireless.c
@@ -17,19 +17,41 @@
#include <linux/pci_ids.h>
#include <linux/leds.h>
-#define ASUS_WIRELESS_LED_STATUS 0x2
-#define ASUS_WIRELESS_LED_OFF 0x4
-#define ASUS_WIRELESS_LED_ON 0x5
+struct hswc_params {
+ u8 on;
+ u8 off;
+ u8 status;
+};
struct asus_wireless_data {
struct input_dev *idev;
struct acpi_device *adev;
+ const struct hswc_params *hswc_params;
struct workqueue_struct *wq;
struct work_struct led_work;
struct led_classdev led;
int led_state;
};
+static const struct hswc_params atk4001_id_params = {
+ .on = 0x0,
+ .off = 0x1,
+ .status = 0x2,
+};
+
+static const struct hswc_params atk4002_id_params = {
+ .on = 0x5,
+ .off = 0x4,
+ .status = 0x2,
+};
+
+static const struct acpi_device_id device_ids[] = {
+ {"ATK4001", (kernel_ulong_t)&atk4001_id_params},
+ {"ATK4002", (kernel_ulong_t)&atk4002_id_params},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, device_ids);
+
static u64 asus_wireless_method(acpi_handle handle, const char *method,
int param)
{
@@ -61,8 +83,8 @@ static enum led_brightness led_state_get(struct led_classdev *led)
data = container_of(led, struct asus_wireless_data, led);
s = asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
- ASUS_WIRELESS_LED_STATUS);
- if (s == ASUS_WIRELESS_LED_ON)
+ data->hswc_params->status);
+ if (s == data->hswc_params->on)
return LED_FULL;
return LED_OFF;
}
@@ -76,14 +98,13 @@ static void led_state_update(struct work_struct *work)
data->led_state);
}
-static void led_state_set(struct led_classdev *led,
- enum led_brightness value)
+static void led_state_set(struct led_classdev *led, enum led_brightness value)
{
struct asus_wireless_data *data;
data = container_of(led, struct asus_wireless_data, led);
- data->led_state = value == LED_OFF ? ASUS_WIRELESS_LED_OFF :
- ASUS_WIRELESS_LED_ON;
+ data->led_state = value == LED_OFF ? data->hswc_params->off :
+ data->hswc_params->on;
queue_work(data->wq, &data->led_work);
}
@@ -104,12 +125,14 @@ static void asus_wireless_notify(struct acpi_device *adev, u32 event)
static int asus_wireless_add(struct acpi_device *adev)
{
struct asus_wireless_data *data;
+ const struct acpi_device_id *id;
int err;
data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
adev->driver_data = data;
+ data->adev = adev;
data->idev = devm_input_allocate_device(&adev->dev);
if (!data->idev)
@@ -124,7 +147,16 @@ static int asus_wireless_add(struct acpi_device *adev)
if (err)
return err;
- data->adev = adev;
+ for (id = device_ids; id->id[0]; id++) {
+ if (!strcmp((char *) id->id, acpi_device_hid(adev))) {
+ data->hswc_params =
+ (const struct hswc_params *)id->driver_data;
+ break;
+ }
+ }
+ if (!data->hswc_params)
+ return 0;
+
data->wq = create_singlethread_workqueue("asus_wireless_workqueue");
if (!data->wq)
return -ENOMEM;
@@ -137,6 +169,7 @@ static int asus_wireless_add(struct acpi_device *adev)
err = devm_led_classdev_register(&adev->dev, &data->led);
if (err)
destroy_workqueue(data->wq);
+
return err;
}
@@ -149,13 +182,6 @@ static int asus_wireless_remove(struct acpi_device *adev)
return 0;
}
-static const struct acpi_device_id device_ids[] = {
- {"ATK4001", 0},
- {"ATK4002", 0},
- {"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, device_ids);
-
static struct acpi_driver asus_wireless_driver = {
.name = "Asus Wireless Radio Control Driver",
.class = "hotkey",
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 14392a01ab36..f57dd282a002 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -106,6 +106,12 @@ static const struct dmi_system_id dell_device_table[] __initconst = {
},
},
{
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/
+ },
+ },
+ {
.ident = "Dell Computer Corporation",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
index 82d67715ce76..2b218b1d13e5 100644
--- a/drivers/platform/x86/fujitsu-laptop.c
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -202,6 +202,7 @@ static int radio_led_set(struct led_classdev *cdev,
static struct led_classdev radio_led = {
.name = "fujitsu::radio_led",
+ .default_trigger = "rfkill-any",
.brightness_get = radio_led_get,
.brightness_set_blocking = radio_led_set
};
@@ -270,15 +271,20 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2)
static int logolamp_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
- if (brightness >= LED_FULL) {
- call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON);
- return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON);
- } else if (brightness >= LED_HALF) {
- call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON);
- return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF);
- } else {
- return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF);
- }
+ int poweron = FUNC_LED_ON, always = FUNC_LED_ON;
+ int ret;
+
+ if (brightness < LED_HALF)
+ poweron = FUNC_LED_OFF;
+
+ if (brightness < LED_FULL)
+ always = FUNC_LED_OFF;
+
+ ret = call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron);
+ if (ret < 0)
+ return ret;
+
+ return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always);
}
static int kblamps_set(struct led_classdev *cdev,
@@ -313,17 +319,17 @@ static int eco_led_set(struct led_classdev *cdev,
static enum led_brightness logolamp_get(struct led_classdev *cdev)
{
- enum led_brightness brightness = LED_OFF;
- int poweron, always;
-
- poweron = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
- if (poweron == FUNC_LED_ON) {
- brightness = LED_HALF;
- always = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
- if (always == FUNC_LED_ON)
- brightness = LED_FULL;
- }
- return brightness;
+ int ret;
+
+ ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
+ if (ret == FUNC_LED_ON)
+ return LED_FULL;
+
+ ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
+ if (ret == FUNC_LED_ON)
+ return LED_HALF;
+
+ return LED_OFF;
}
static enum led_brightness kblamps_get(struct led_classdev *cdev)
@@ -1029,107 +1035,117 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device)
return 0;
}
+static void acpi_fujitsu_hotkey_press(int keycode)
+{
+ struct input_dev *input = fujitsu_hotkey->input;
+ int status;
+
+ status = kfifo_in_locked(&fujitsu_hotkey->fifo,
+ (unsigned char *)&keycode, sizeof(keycode),
+ &fujitsu_hotkey->fifo_lock);
+ if (status != sizeof(keycode)) {
+ vdbg_printk(FUJLAPTOP_DBG_WARN,
+ "Could not push keycode [0x%x]\n", keycode);
+ return;
+ }
+ input_report_key(input, keycode, 1);
+ input_sync(input);
+ vdbg_printk(FUJLAPTOP_DBG_TRACE,
+ "Push keycode into ringbuffer [%d]\n", keycode);
+}
+
+static void acpi_fujitsu_hotkey_release(void)
+{
+ struct input_dev *input = fujitsu_hotkey->input;
+ int keycode, status;
+
+ while (true) {
+ status = kfifo_out_locked(&fujitsu_hotkey->fifo,
+ (unsigned char *)&keycode,
+ sizeof(keycode),
+ &fujitsu_hotkey->fifo_lock);
+ if (status != sizeof(keycode))
+ return;
+ input_report_key(input, keycode, 0);
+ input_sync(input);
+ vdbg_printk(FUJLAPTOP_DBG_TRACE,
+ "Pop keycode from ringbuffer [%d]\n", keycode);
+ }
+}
+
static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event)
{
struct input_dev *input;
- int keycode, keycode_r;
+ int keycode;
unsigned int irb = 1;
- int i, status;
+ int i;
input = fujitsu_hotkey->input;
+ if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
+ keycode = KEY_UNKNOWN;
+ vdbg_printk(FUJLAPTOP_DBG_WARN,
+ "Unsupported event [0x%x]\n", event);
+ input_report_key(input, keycode, 1);
+ input_sync(input);
+ input_report_key(input, keycode, 0);
+ input_sync(input);
+ return;
+ }
+
if (fujitsu_hotkey->rfkill_supported)
fujitsu_hotkey->rfkill_state =
call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0);
- switch (event) {
- case ACPI_FUJITSU_NOTIFY_CODE1:
- i = 0;
- while ((irb =
- call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0
- && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) {
- switch (irb & 0x4ff) {
- case KEY1_CODE:
- keycode = fujitsu->keycode1;
- break;
- case KEY2_CODE:
- keycode = fujitsu->keycode2;
- break;
- case KEY3_CODE:
- keycode = fujitsu->keycode3;
- break;
- case KEY4_CODE:
- keycode = fujitsu->keycode4;
- break;
- case KEY5_CODE:
- keycode = fujitsu->keycode5;
- break;
- case 0:
- keycode = 0;
- break;
- default:
- vdbg_printk(FUJLAPTOP_DBG_WARN,
- "Unknown GIRB result [%x]\n", irb);
- keycode = -1;
- break;
- }
- if (keycode > 0) {
- vdbg_printk(FUJLAPTOP_DBG_TRACE,
- "Push keycode into ringbuffer [%d]\n",
- keycode);
- status = kfifo_in_locked(&fujitsu_hotkey->fifo,
- (unsigned char *)&keycode,
- sizeof(keycode),
- &fujitsu_hotkey->fifo_lock);
- if (status != sizeof(keycode)) {
- vdbg_printk(FUJLAPTOP_DBG_WARN,
- "Could not push keycode [0x%x]\n",
- keycode);
- } else {
- input_report_key(input, keycode, 1);
- input_sync(input);
- }
- } else if (keycode == 0) {
- while ((status =
- kfifo_out_locked(
- &fujitsu_hotkey->fifo,
- (unsigned char *) &keycode_r,
- sizeof(keycode_r),
- &fujitsu_hotkey->fifo_lock))
- == sizeof(keycode_r)) {
- input_report_key(input, keycode_r, 0);
- input_sync(input);
- vdbg_printk(FUJLAPTOP_DBG_TRACE,
- "Pop keycode from ringbuffer [%d]\n",
- keycode_r);
- }
- }
+ i = 0;
+ while ((irb =
+ call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0
+ && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) {
+ switch (irb & 0x4ff) {
+ case KEY1_CODE:
+ keycode = fujitsu->keycode1;
+ break;
+ case KEY2_CODE:
+ keycode = fujitsu->keycode2;
+ break;
+ case KEY3_CODE:
+ keycode = fujitsu->keycode3;
+ break;
+ case KEY4_CODE:
+ keycode = fujitsu->keycode4;
+ break;
+ case KEY5_CODE:
+ keycode = fujitsu->keycode5;
+ break;
+ case 0:
+ keycode = 0;
+ break;
+ default:
+ vdbg_printk(FUJLAPTOP_DBG_WARN,
+ "Unknown GIRB result [%x]\n", irb);
+ keycode = -1;
+ break;
}
- /* On some models (first seen on the Skylake-based Lifebook
- * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
- * handled in software; its state is queried using FUNC_RFKILL
- */
- if ((fujitsu_hotkey->rfkill_supported & BIT(26)) &&
- (call_fext_func(FUNC_RFKILL, 0x1, 0x0, 0x0) & BIT(26))) {
- keycode = KEY_TOUCHPAD_TOGGLE;
- input_report_key(input, keycode, 1);
- input_sync(input);
- input_report_key(input, keycode, 0);
- input_sync(input);
- }
+ if (keycode > 0)
+ acpi_fujitsu_hotkey_press(keycode);
+ else if (keycode == 0)
+ acpi_fujitsu_hotkey_release();
+ }
- break;
- default:
- keycode = KEY_UNKNOWN;
- vdbg_printk(FUJLAPTOP_DBG_WARN,
- "Unsupported event [0x%x]\n", event);
+ /* On some models (first seen on the Skylake-based Lifebook
+ * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
+ * handled in software; its state is queried using FUNC_RFKILL
+ */
+ if ((fujitsu_hotkey->rfkill_supported & BIT(26)) &&
+ (call_fext_func(FUNC_RFKILL, 0x1, 0x0, 0x0) & BIT(26))) {
+ keycode = KEY_TOUCHPAD_TOGGLE;
input_report_key(input, keycode, 1);
input_sync(input);
input_report_key(input, keycode, 0);
input_sync(input);
- break;
}
+
}
/* Initialization */
diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c
index 09356684c32f..493d8910a74e 100644
--- a/drivers/platform/x86/hp_accel.c
+++ b/drivers/platform/x86/hp_accel.c
@@ -251,6 +251,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = {
AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap),
AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted),
AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted),
+ AXIS_DMI_MATCH("HPZBook17", "HP ZBook 17", xy_swap_yz_inverted),
{ NULL, }
/* Laptop models without axis info (yet):
* "NC6910" "HP Compaq 6910"
diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c
index cb3ab2b212b1..bcf438f38781 100644
--- a/drivers/platform/x86/intel-hid.c
+++ b/drivers/platform/x86/intel-hid.c
@@ -1,5 +1,5 @@
/*
- * Intel HID event driver for Windows 8
+ * Intel HID event & 5 button array driver
*
* Copyright (C) 2015 Alex Hung <alex.hung@canonical.com>
* Copyright (C) 2015 Andrew Lutomirski <luto@kernel.org>
@@ -57,8 +57,24 @@ static const struct key_entry intel_hid_keymap[] = {
{ KE_END },
};
+/* 5 button array notification value. */
+static const struct key_entry intel_array_keymap[] = {
+ { KE_KEY, 0xC2, { KEY_LEFTMETA } }, /* Press */
+ { KE_IGNORE, 0xC3, { KEY_LEFTMETA } }, /* Release */
+ { KE_KEY, 0xC4, { KEY_VOLUMEUP } }, /* Press */
+ { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* Release */
+ { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* Press */
+ { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* Release */
+ { KE_SW, 0xC8, { .sw = { SW_ROTATE_LOCK, 1 } } }, /* Press */
+ { KE_SW, 0xC9, { .sw = { SW_ROTATE_LOCK, 0 } } }, /* Release */
+ { KE_KEY, 0xCE, { KEY_POWER } }, /* Press */
+ { KE_IGNORE, 0xCF, { KEY_POWER } }, /* Release */
+ { KE_END },
+};
+
struct intel_hid_priv {
struct input_dev *input_dev;
+ struct input_dev *array;
};
static int intel_hid_set_enable(struct device *device, int enable)
@@ -78,15 +94,43 @@ static int intel_hid_set_enable(struct device *device, int enable)
return 0;
}
+static void intel_button_array_enable(struct device *device, bool enable)
+{
+ struct intel_hid_priv *priv = dev_get_drvdata(device);
+ acpi_handle handle = ACPI_HANDLE(device);
+ unsigned long long button_cap;
+ acpi_status status;
+
+ if (!priv->array)
+ return;
+
+ /* Query supported platform features */
+ status = acpi_evaluate_integer(handle, "BTNC", NULL, &button_cap);
+ if (ACPI_FAILURE(status)) {
+ dev_warn(device, "failed to get button capability\n");
+ return;
+ }
+
+ /* Enable|disable features - power button is always enabled */
+ status = acpi_execute_simple_method(handle, "BTNE",
+ enable ? button_cap : 1);
+ if (ACPI_FAILURE(status))
+ dev_warn(device, "failed to set button capability\n");
+}
+
static int intel_hid_pl_suspend_handler(struct device *device)
{
intel_hid_set_enable(device, 0);
+ intel_button_array_enable(device, false);
+
return 0;
}
static int intel_hid_pl_resume_handler(struct device *device)
{
intel_hid_set_enable(device, 1);
+ intel_button_array_enable(device, true);
+
return 0;
}
@@ -126,6 +170,27 @@ err_free_device:
return ret;
}
+static int intel_button_array_input_setup(struct platform_device *device)
+{
+ struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
+ int ret;
+
+ /* Setup input device for 5 button array */
+ priv->array = devm_input_allocate_device(&device->dev);
+ if (!priv->array)
+ return -ENOMEM;
+
+ ret = sparse_keymap_setup(priv->array, intel_array_keymap, NULL);
+ if (ret)
+ return ret;
+
+ priv->array->dev.parent = &device->dev;
+ priv->array->name = "Intel HID 5 button array";
+ priv->array->id.bustype = BUS_HOST;
+
+ return input_register_device(priv->array);
+}
+
static void intel_hid_input_destroy(struct platform_device *device)
{
struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
@@ -140,10 +205,11 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
unsigned long long ev_index;
acpi_status status;
- /* The platform spec only defines one event code: 0xC0. */
+ /* 0xC0 is for HID events, other values are for 5 button array */
if (event != 0xc0) {
- dev_warn(&device->dev, "received unknown event (0x%x)\n",
- event);
+ if (!priv->array ||
+ !sparse_keymap_report_event(priv->array, event, 1, true))
+ dev_info(&device->dev, "unknown event 0x%x\n", event);
return;
}
@@ -161,8 +227,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
static int intel_hid_probe(struct platform_device *device)
{
acpi_handle handle = ACPI_HANDLE(&device->dev);
+ unsigned long long event_cap, mode;
struct intel_hid_priv *priv;
- unsigned long long mode;
acpi_status status;
int err;
@@ -193,6 +259,15 @@ static int intel_hid_probe(struct platform_device *device)
return err;
}
+ /* Setup 5 button array */
+ status = acpi_evaluate_integer(handle, "HEBC", NULL, &event_cap);
+ if (ACPI_SUCCESS(status) && (event_cap & 0x20000)) {
+ dev_info(&device->dev, "platform supports 5 button array\n");
+ err = intel_button_array_input_setup(device);
+ if (err)
+ pr_err("Failed to setup Intel 5 button array hotkeys\n");
+ }
+
status = acpi_install_notify_handler(handle,
ACPI_DEVICE_NOTIFY,
notify_handler,
@@ -206,6 +281,16 @@ static int intel_hid_probe(struct platform_device *device)
if (err)
goto err_remove_notify;
+ if (priv->array) {
+ intel_button_array_enable(&device->dev, true);
+
+ /* Call button load method to enable HID power button */
+ status = acpi_evaluate_object(handle, "BTNL", NULL, NULL);
+ if (ACPI_FAILURE(status))
+ dev_warn(&device->dev,
+ "failed to enable HID power button\n");
+ }
+
return 0;
err_remove_notify:
@@ -224,6 +309,7 @@ static int intel_hid_remove(struct platform_device *device)
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
intel_hid_input_destroy(device);
intel_hid_set_enable(&device->dev, 0);
+ intel_button_array_enable(&device->dev, false);
/*
* Even if we failed to shut off the event stream, we can still
diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c
index 361770568ad0..871cfa682519 100644
--- a/drivers/platform/x86/intel_mid_powerbtn.c
+++ b/drivers/platform/x86/intel_mid_powerbtn.c
@@ -1,7 +1,10 @@
/*
- * Power button driver for Medfield.
+ * Power button driver for Intel MID platforms.
*
- * Copyright (C) 2010 Intel Corp
+ * Copyright (C) 2010,2017 Intel Corp
+ *
+ * Author: Hong Liu <hong.liu@intel.com>
+ * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -11,20 +14,20 @@
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-#include <linux/module.h>
#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
#include <linux/input.h>
+#include <linux/interrupt.h>
#include <linux/mfd/intel_msic.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/pm_wakeirq.h>
+#include <linux/slab.h>
+
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+#include <asm/intel_scu_ipc.h>
#define DRIVER_NAME "msic_power_btn"
@@ -36,37 +39,113 @@
*/
#define MSIC_PWRBTNM (1 << 0)
-static irqreturn_t mfld_pb_isr(int irq, void *dev_id)
+/* Intel Tangier */
+#define BCOVE_PB_LEVEL (1 << 4) /* 1 - release, 0 - press */
+
+/* Basin Cove PMIC */
+#define BCOVE_PBIRQ 0x02
+#define BCOVE_IRQLVL1MSK 0x0c
+#define BCOVE_PBIRQMASK 0x0d
+#define BCOVE_PBSTATUS 0x27
+
+struct mid_pb_ddata {
+ struct device *dev;
+ int irq;
+ struct input_dev *input;
+ unsigned short mirqlvl1_addr;
+ unsigned short pbstat_addr;
+ u8 pbstat_mask;
+ int (*setup)(struct mid_pb_ddata *ddata);
+};
+
+static int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
{
- struct input_dev *input = dev_id;
+ struct input_dev *input = ddata->input;
int ret;
u8 pbstat;
- ret = intel_msic_reg_read(INTEL_MSIC_PBSTATUS, &pbstat);
+ ret = intel_scu_ipc_ioread8(ddata->pbstat_addr, &pbstat);
+ if (ret)
+ return ret;
+
dev_dbg(input->dev.parent, "PB_INT status= %d\n", pbstat);
+ *value = !(pbstat & ddata->pbstat_mask);
+ return 0;
+}
+
+static int mid_irq_ack(struct mid_pb_ddata *ddata)
+{
+ return intel_scu_ipc_update_register(ddata->mirqlvl1_addr, 0, MSIC_PWRBTNM);
+}
+
+static int mrfld_setup(struct mid_pb_ddata *ddata)
+{
+ /* Unmask the PBIRQ and MPBIRQ on Tangier */
+ intel_scu_ipc_update_register(BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
+ intel_scu_ipc_update_register(BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
+
+ return 0;
+}
+
+static irqreturn_t mid_pb_isr(int irq, void *dev_id)
+{
+ struct mid_pb_ddata *ddata = dev_id;
+ struct input_dev *input = ddata->input;
+ int value = 0;
+ int ret;
+
+ ret = mid_pbstat(ddata, &value);
if (ret < 0) {
- dev_err(input->dev.parent, "Read error %d while reading"
- " MSIC_PB_STATUS\n", ret);
+ dev_err(input->dev.parent,
+ "Read error %d while reading MSIC_PB_STATUS\n", ret);
} else {
- input_event(input, EV_KEY, KEY_POWER,
- !(pbstat & MSIC_PB_LEVEL));
+ input_event(input, EV_KEY, KEY_POWER, value);
input_sync(input);
}
+ mid_irq_ack(ddata);
return IRQ_HANDLED;
}
-static int mfld_pb_probe(struct platform_device *pdev)
+static struct mid_pb_ddata mfld_ddata = {
+ .mirqlvl1_addr = INTEL_MSIC_IRQLVL1MSK,
+ .pbstat_addr = INTEL_MSIC_PBSTATUS,
+ .pbstat_mask = MSIC_PB_LEVEL,
+};
+
+static struct mid_pb_ddata mrfld_ddata = {
+ .mirqlvl1_addr = BCOVE_IRQLVL1MSK,
+ .pbstat_addr = BCOVE_PBSTATUS,
+ .pbstat_mask = BCOVE_PB_LEVEL,
+ .setup = mrfld_setup,
+};
+
+#define ICPU(model, ddata) \
+ { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (kernel_ulong_t)&ddata }
+
+static const struct x86_cpu_id mid_pb_cpu_ids[] = {
+ ICPU(INTEL_FAM6_ATOM_PENWELL, mfld_ddata),
+ ICPU(INTEL_FAM6_ATOM_MERRIFIELD, mrfld_ddata),
+ {}
+};
+
+static int mid_pb_probe(struct platform_device *pdev)
{
+ const struct x86_cpu_id *id;
+ struct mid_pb_ddata *ddata;
struct input_dev *input;
int irq = platform_get_irq(pdev, 0);
int error;
+ id = x86_match_cpu(mid_pb_cpu_ids);
+ if (!id)
+ return -ENODEV;
+
if (irq < 0)
return -EINVAL;
- input = input_allocate_device();
+ input = devm_input_allocate_device(&pdev->dev);
if (!input)
return -ENOMEM;
@@ -77,25 +156,36 @@ static int mfld_pb_probe(struct platform_device *pdev)
input_set_capability(input, EV_KEY, KEY_POWER);
- error = request_threaded_irq(irq, NULL, mfld_pb_isr, IRQF_ONESHOT,
- DRIVER_NAME, input);
- if (error) {
- dev_err(&pdev->dev, "Unable to request irq %d for mfld power"
- "button\n", irq);
- goto err_free_input;
+ ddata = (struct mid_pb_ddata *)id->driver_data;
+ if (!ddata)
+ return -ENODATA;
+
+ ddata->dev = &pdev->dev;
+ ddata->irq = irq;
+ ddata->input = input;
+
+ if (ddata->setup) {
+ error = ddata->setup(ddata);
+ if (error)
+ return error;
}
- device_init_wakeup(&pdev->dev, true);
- dev_pm_set_wake_irq(&pdev->dev, irq);
+ error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr,
+ IRQF_ONESHOT, DRIVER_NAME, ddata);
+ if (error) {
+ dev_err(&pdev->dev,
+ "Unable to request irq %d for MID power button\n", irq);
+ return error;
+ }
error = input_register_device(input);
if (error) {
- dev_err(&pdev->dev, "Unable to register input dev, error "
- "%d\n", error);
- goto err_free_irq;
+ dev_err(&pdev->dev,
+ "Unable to register input dev, error %d\n", error);
+ return error;
}
- platform_set_drvdata(pdev, input);
+ platform_set_drvdata(pdev, ddata);
/*
* SCU firmware might send power button interrupts to IA core before
@@ -107,46 +197,39 @@ static int mfld_pb_probe(struct platform_device *pdev)
* initialization. The race happens rarely. So we needn't worry
* about it.
*/
- error = intel_msic_reg_update(INTEL_MSIC_IRQLVL1MSK, 0, MSIC_PWRBTNM);
+ error = mid_irq_ack(ddata);
if (error) {
- dev_err(&pdev->dev, "Unable to clear power button interrupt, "
- "error: %d\n", error);
- goto err_free_irq;
+ dev_err(&pdev->dev,
+ "Unable to clear power button interrupt, error: %d\n",
+ error);
+ return error;
}
- return 0;
+ device_init_wakeup(&pdev->dev, true);
+ dev_pm_set_wake_irq(&pdev->dev, irq);
-err_free_irq:
- free_irq(irq, input);
-err_free_input:
- input_free_device(input);
- return error;
+ return 0;
}
-static int mfld_pb_remove(struct platform_device *pdev)
+static int mid_pb_remove(struct platform_device *pdev)
{
- struct input_dev *input = platform_get_drvdata(pdev);
- int irq = platform_get_irq(pdev, 0);
-
dev_pm_clear_wake_irq(&pdev->dev);
device_init_wakeup(&pdev->dev, false);
- free_irq(irq, input);
- input_unregister_device(input);
return 0;
}
-static struct platform_driver mfld_pb_driver = {
+static struct platform_driver mid_pb_driver = {
.driver = {
.name = DRIVER_NAME,
},
- .probe = mfld_pb_probe,
- .remove = mfld_pb_remove,
+ .probe = mid_pb_probe,
+ .remove = mid_pb_remove,
};
-module_platform_driver(mfld_pb_driver);
+module_platform_driver(mid_pb_driver);
MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>");
-MODULE_DESCRIPTION("Intel Medfield Power Button Driver");
+MODULE_DESCRIPTION("Intel MID Power Button Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c
index 0df3c9d37509..008a76903cbf 100644
--- a/drivers/platform/x86/intel_mid_thermal.c
+++ b/drivers/platform/x86/intel_mid_thermal.c
@@ -549,9 +549,9 @@ static int mid_thermal_remove(struct platform_device *pdev)
static const struct platform_device_id therm_id_table[] = {
{ DRIVER_NAME, 1 },
- { "msic_thermal", 1 },
{ }
};
+MODULE_DEVICE_TABLE(platform, therm_id_table);
static struct platform_driver mid_thermal_driver = {
.driver = {
diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c
index b130b8c9b9d7..914bcd2edbde 100644
--- a/drivers/platform/x86/intel_pmc_core.c
+++ b/drivers/platform/x86/intel_pmc_core.c
@@ -188,8 +188,7 @@ static int pmc_core_check_read_lock_bit(void)
u32 value;
value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_CFG_OFFSET);
- return test_bit(SPT_PMC_READ_DISABLE_BIT,
- (unsigned long *)&value);
+ return value & BIT(SPT_PMC_READ_DISABLE_BIT);
}
#if IS_ENABLED(CONFIG_DEBUG_FS)
@@ -238,8 +237,7 @@ static int pmc_core_mtpmc_link_status(void)
u32 value;
value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_STS_OFFSET);
- return test_bit(SPT_PMC_MSG_FULL_STS_BIT,
- (unsigned long *)&value);
+ return value & BIT(SPT_PMC_MSG_FULL_STS_BIT);
}
static int pmc_core_send_msg(u32 *addr_xram)
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index 0bf51d574fa9..0651d47b8eeb 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -32,7 +32,10 @@
#include <linux/notifier.h>
#include <linux/suspend.h>
#include <linux/acpi.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+
#include <asm/intel_pmc_ipc.h>
+
#include <linux/platform_data/itco_wdt.h>
/*
@@ -54,6 +57,18 @@
#define IPC_WRITE_BUFFER 0x80
#define IPC_READ_BUFFER 0x90
+/* PMC Global Control Registers */
+#define GCR_TELEM_DEEP_S0IX_OFFSET 0x1078
+#define GCR_TELEM_SHLW_S0IX_OFFSET 0x1080
+
+/* Residency with clock rate at 19.2MHz to usecs */
+#define S0IX_RESIDENCY_IN_USECS(d, s) \
+({ \
+ u64 result = 10ull * ((d) + (s)); \
+ do_div(result, 192); \
+ result; \
+})
+
/*
* 16-byte buffer for sending data associated with IPC command.
*/
@@ -68,7 +83,7 @@
#define PLAT_RESOURCE_IPC_INDEX 0
#define PLAT_RESOURCE_IPC_SIZE 0x1000
#define PLAT_RESOURCE_GCR_OFFSET 0x1008
-#define PLAT_RESOURCE_GCR_SIZE 0x4
+#define PLAT_RESOURCE_GCR_SIZE 0x1000
#define PLAT_RESOURCE_BIOS_DATA_INDEX 1
#define PLAT_RESOURCE_BIOS_IFACE_INDEX 2
#define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3
@@ -97,8 +112,6 @@
#define TCO_PMC_OFFSET 0x8
#define TCO_PMC_SIZE 0x4
-static const int iTCO_version = 3;
-
static struct intel_pmc_ipc_dev {
struct device *dev;
void __iomem *ipc_base;
@@ -115,6 +128,7 @@ static struct intel_pmc_ipc_dev {
/* gcr */
resource_size_t gcr_base;
int gcr_size;
+ bool has_gcr_regs;
/* punit */
struct platform_device *punit_dev;
@@ -180,6 +194,11 @@ static inline u32 ipc_data_readl(u32 offset)
return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
}
+static inline u64 gcr_data_readq(u32 offset)
+{
+ return readq(ipcdev.ipc_base + offset);
+}
+
static int intel_pmc_ipc_check_status(void)
{
int status;
@@ -389,6 +408,7 @@ static void ipc_pci_remove(struct pci_dev *pdev)
static const struct pci_device_id ipc_pci_ids[] = {
{PCI_VDEVICE(INTEL, 0x0a94), 0},
{PCI_VDEVICE(INTEL, 0x1a94), 0},
+ {PCI_VDEVICE(INTEL, 0x5a94), 0},
{ 0,}
};
MODULE_DEVICE_TABLE(pci, ipc_pci_ids);
@@ -712,7 +732,8 @@ static int ipc_plat_get_res(struct platform_device *pdev)
dev_err(&pdev->dev, "Failed to get ipc resource\n");
return -ENXIO;
}
- size = PLAT_RESOURCE_IPC_SIZE;
+ size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE;
+
if (!request_mem_region(res->start, size, pdev->name)) {
dev_err(&pdev->dev, "Failed to request ipc resource\n");
return -EBUSY;
@@ -748,6 +769,28 @@ static int ipc_plat_get_res(struct platform_device *pdev)
return 0;
}
+/**
+ * intel_pmc_s0ix_counter_read() - Read S0ix residency.
+ * @data: Out param that contains current S0ix residency count.
+ *
+ * Return: an error code or 0 on success.
+ */
+int intel_pmc_s0ix_counter_read(u64 *data)
+{
+ u64 deep, shlw;
+
+ if (!ipcdev.has_gcr_regs)
+ return -EACCES;
+
+ deep = gcr_data_readq(GCR_TELEM_DEEP_S0IX_OFFSET);
+ shlw = gcr_data_readq(GCR_TELEM_SHLW_S0IX_OFFSET);
+
+ *data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
+
#ifdef CONFIG_ACPI
static const struct acpi_device_id ipc_acpi_ids[] = {
{ "INT34D2", 0},
@@ -797,6 +840,8 @@ static int ipc_plat_probe(struct platform_device *pdev)
goto err_sys;
}
+ ipcdev.has_gcr_regs = true;
+
return 0;
err_sys:
free_irq(ipcdev.irq, &ipcdev);
@@ -808,8 +853,11 @@ err_device:
iounmap(ipcdev.ipc_base);
res = platform_get_resource(pdev, IORESOURCE_MEM,
PLAT_RESOURCE_IPC_INDEX);
- if (res)
- release_mem_region(res->start, PLAT_RESOURCE_IPC_SIZE);
+ if (res) {
+ release_mem_region(res->start,
+ PLAT_RESOURCE_IPC_SIZE +
+ PLAT_RESOURCE_GCR_SIZE);
+ }
return ret;
}
@@ -825,8 +873,11 @@ static int ipc_plat_remove(struct platform_device *pdev)
iounmap(ipcdev.ipc_base);
res = platform_get_resource(pdev, IORESOURCE_MEM,
PLAT_RESOURCE_IPC_INDEX);
- if (res)
- release_mem_region(res->start, PLAT_RESOURCE_IPC_SIZE);
+ if (res) {
+ release_mem_region(res->start,
+ PLAT_RESOURCE_IPC_SIZE +
+ PLAT_RESOURCE_GCR_SIZE);
+ }
ipcdev.dev = NULL;
return 0;
}
diff --git a/drivers/platform/x86/intel_turbo_max_3.c b/drivers/platform/x86/intel_turbo_max_3.c
new file mode 100644
index 000000000000..4f60d8e32a0a
--- /dev/null
+++ b/drivers/platform/x86/intel_turbo_max_3.c
@@ -0,0 +1,151 @@
+/*
+ * Intel Turbo Boost Max Technology 3.0 legacy (non HWP) enumeration driver
+ * Copyright (c) 2017, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/topology.h>
+#include <linux/workqueue.h>
+#include <linux/cpuhotplug.h>
+#include <linux/cpufeature.h>
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+
+#define MSR_OC_MAILBOX 0x150
+#define MSR_OC_MAILBOX_CMD_OFFSET 32
+#define MSR_OC_MAILBOX_RSP_OFFSET 32
+#define MSR_OC_MAILBOX_BUSY_BIT 63
+#define OC_MAILBOX_FC_CONTROL_CMD 0x1C
+
+/*
+ * Typical latency to get mail box response is ~3us, It takes +3 us to
+ * process reading mailbox after issuing mailbox write on a Broadwell 3.4 GHz
+ * system. So for most of the time, the first mailbox read should have the
+ * response, but to avoid some boundary cases retry twice.
+ */
+#define OC_MAILBOX_RETRY_COUNT 2
+
+static int get_oc_core_priority(unsigned int cpu)
+{
+ u64 value, cmd = OC_MAILBOX_FC_CONTROL_CMD;
+ int ret, i;
+
+ /* Issue favored core read command */
+ value = cmd << MSR_OC_MAILBOX_CMD_OFFSET;
+ /* Set the busy bit to indicate OS is trying to issue command */
+ value |= BIT_ULL(MSR_OC_MAILBOX_BUSY_BIT);
+ ret = wrmsrl_safe(MSR_OC_MAILBOX, value);
+ if (ret) {
+ pr_debug("cpu %d OC mailbox write failed\n", cpu);
+ return ret;
+ }
+
+ for (i = 0; i < OC_MAILBOX_RETRY_COUNT; ++i) {
+ ret = rdmsrl_safe(MSR_OC_MAILBOX, &value);
+ if (ret) {
+ pr_debug("cpu %d OC mailbox read failed\n", cpu);
+ break;
+ }
+
+ if (value & BIT_ULL(MSR_OC_MAILBOX_BUSY_BIT)) {
+ pr_debug("cpu %d OC mailbox still processing\n", cpu);
+ ret = -EBUSY;
+ continue;
+ }
+
+ if ((value >> MSR_OC_MAILBOX_RSP_OFFSET) & 0xff) {
+ pr_debug("cpu %d OC mailbox cmd failed\n", cpu);
+ ret = -ENXIO;
+ break;
+ }
+
+ ret = value & 0xff;
+ pr_debug("cpu %d max_ratio %d\n", cpu, ret);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * The work item is needed to avoid CPU hotplug locking issues. The function
+ * itmt_legacy_set_priority() is called from CPU online callback, so can't
+ * call sched_set_itmt_support() from there as this function will aquire
+ * hotplug locks in its path.
+ */
+static void itmt_legacy_work_fn(struct work_struct *work)
+{
+ sched_set_itmt_support();
+}
+
+static DECLARE_WORK(sched_itmt_work, itmt_legacy_work_fn);
+
+static int itmt_legacy_cpu_online(unsigned int cpu)
+{
+ static u32 max_highest_perf = 0, min_highest_perf = U32_MAX;
+ int priority;
+
+ priority = get_oc_core_priority(cpu);
+ if (priority < 0)
+ return 0;
+
+ sched_set_itmt_core_prio(priority, cpu);
+
+ /* Enable ITMT feature when a core with different priority is found */
+ if (max_highest_perf <= min_highest_perf) {
+ if (priority > max_highest_perf)
+ max_highest_perf = priority;
+
+ if (priority < min_highest_perf)
+ min_highest_perf = priority;
+
+ if (max_highest_perf > min_highest_perf)
+ schedule_work(&sched_itmt_work);
+ }
+
+ return 0;
+}
+
+#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }
+
+static const struct x86_cpu_id itmt_legacy_cpu_ids[] = {
+ ICPU(INTEL_FAM6_BROADWELL_X),
+ {}
+};
+
+static int __init itmt_legacy_init(void)
+{
+ const struct x86_cpu_id *id;
+ int ret;
+
+ id = x86_match_cpu(itmt_legacy_cpu_ids);
+ if (!id)
+ return -ENODEV;
+
+ if (boot_cpu_has(X86_FEATURE_HWP))
+ return -ENODEV;
+
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+ "platform/x86/turbo_max_3:online",
+ itmt_legacy_cpu_online, NULL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+late_initcall(itmt_legacy_init)
diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c
index 25f15df5c2d7..8f98c211b440 100644
--- a/drivers/platform/x86/mlx-platform.c
+++ b/drivers/platform/x86/mlx-platform.c
@@ -45,6 +45,10 @@
/* LPC bus IO offsets */
#define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000
#define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500
+#define MLXPLAT_CPLD_LPC_REG_AGGR_ADRR 0x253a
+#define MLXPLAT_CPLD_LPC_REG_PSU_ADRR 0x2558
+#define MLXPLAT_CPLD_LPC_REG_PWR_ADRR 0x2564
+#define MLXPLAT_CPLD_LPC_REG_FAN_ADRR 0x2588
#define MLXPLAT_CPLD_LPC_IO_RANGE 0x100
#define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb
#define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda
@@ -56,6 +60,17 @@
MLXPLAT_CPLD_LPC_I2C_CH2_OFF) | \
MLXPLAT_CPLD_LPC_PIO_OFFSET)
+/* Masks for aggregation, psu, pwr and fan event in CPLD related registers. */
+#define MLXPLAT_CPLD_AGGR_PSU_MASK_DEF 0x08
+#define MLXPLAT_CPLD_AGGR_PWR_MASK_DEF 0x08
+#define MLXPLAT_CPLD_AGGR_FAN_MASK_DEF 0x40
+#define MLXPLAT_CPLD_AGGR_MASK_DEF (MLXPLAT_CPLD_AGGR_PSU_MASK_DEF | \
+ MLXPLAT_CPLD_AGGR_FAN_MASK_DEF)
+#define MLXPLAT_CPLD_AGGR_MASK_MSN21XX 0x04
+#define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0)
+#define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0)
+#define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0)
+
/* Start channel numbers */
#define MLXPLAT_CPLD_CH1 2
#define MLXPLAT_CPLD_CH2 10
@@ -123,7 +138,7 @@ static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = {
};
/* Platform hotplug devices */
-static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_psu[] = {
+static struct mlxcpld_hotplug_device mlxplat_mlxcpld_psu[] = {
{
.brdinfo = { I2C_BOARD_INFO("24c02", 0x51) },
.bus = 10,
@@ -134,7 +149,7 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_psu[] = {
},
};
-static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_pwr[] = {
+static struct mlxcpld_hotplug_device mlxplat_mlxcpld_pwr[] = {
{
.brdinfo = { I2C_BOARD_INFO("dps460", 0x59) },
.bus = 10,
@@ -145,7 +160,7 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_pwr[] = {
},
};
-static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_fan[] = {
+static struct mlxcpld_hotplug_device mlxplat_mlxcpld_fan[] = {
{
.brdinfo = { I2C_BOARD_INFO("24c32", 0x50) },
.bus = 11,
@@ -166,38 +181,38 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_fan[] = {
/* Platform hotplug default data */
static
-struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_hotplug_default_data = {
- .top_aggr_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x3a),
- .top_aggr_mask = 0x48,
- .top_aggr_psu_mask = 0x08,
- .psu_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x58),
- .psu_mask = 0x03,
- .psu_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_psu),
- .psu = mlxplat_mlxcpld_hotplug_psu,
- .top_aggr_pwr_mask = 0x08,
- .pwr_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x64),
- .pwr_mask = 0x03,
- .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_pwr),
- .pwr = mlxplat_mlxcpld_hotplug_pwr,
- .top_aggr_fan_mask = 0x40,
- .fan_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x88),
- .fan_mask = 0x0f,
- .fan_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_fan),
- .fan = mlxplat_mlxcpld_hotplug_fan,
+struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_default_data = {
+ .top_aggr_offset = MLXPLAT_CPLD_LPC_REG_AGGR_ADRR,
+ .top_aggr_mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
+ .top_aggr_psu_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF,
+ .psu_reg_offset = MLXPLAT_CPLD_LPC_REG_PSU_ADRR,
+ .psu_mask = MLXPLAT_CPLD_PSU_MASK,
+ .psu_count = ARRAY_SIZE(mlxplat_mlxcpld_psu),
+ .psu = mlxplat_mlxcpld_psu,
+ .top_aggr_pwr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
+ .pwr_reg_offset = MLXPLAT_CPLD_LPC_REG_PWR_ADRR,
+ .pwr_mask = MLXPLAT_CPLD_PWR_MASK,
+ .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_pwr),
+ .pwr = mlxplat_mlxcpld_pwr,
+ .top_aggr_fan_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF,
+ .fan_reg_offset = MLXPLAT_CPLD_LPC_REG_FAN_ADRR,
+ .fan_mask = MLXPLAT_CPLD_FAN_MASK,
+ .fan_count = ARRAY_SIZE(mlxplat_mlxcpld_fan),
+ .fan = mlxplat_mlxcpld_fan,
};
/* Platform hotplug MSN21xx system family data */
static
-struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_hotplug_msn21xx_data = {
- .top_aggr_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x3a),
- .top_aggr_mask = 0x04,
- .top_aggr_pwr_mask = 0x04,
- .pwr_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x64),
- .pwr_mask = 0x03,
- .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_pwr),
+struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data = {
+ .top_aggr_offset = MLXPLAT_CPLD_LPC_REG_AGGR_ADRR,
+ .top_aggr_mask = MLXPLAT_CPLD_AGGR_MASK_MSN21XX,
+ .top_aggr_pwr_mask = MLXPLAT_CPLD_AGGR_MASK_MSN21XX,
+ .pwr_reg_offset = MLXPLAT_CPLD_LPC_REG_PWR_ADRR,
+ .pwr_mask = MLXPLAT_CPLD_PWR_MASK,
+ .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_pwr),
};
-static struct resource mlxplat_mlxcpld_hotplug_resources[] = {
+static struct resource mlxplat_mlxcpld_resources[] = {
[0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"),
};
@@ -213,7 +228,7 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
mlxplat_mux_data[i].n_values =
ARRAY_SIZE(mlxplat_default_channels[i]);
}
- mlxplat_hotplug = &mlxplat_mlxcpld_hotplug_default_data;
+ mlxplat_hotplug = &mlxplat_mlxcpld_default_data;
return 1;
};
@@ -227,7 +242,7 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
mlxplat_mux_data[i].n_values =
ARRAY_SIZE(mlxplat_msn21xx_channels);
}
- mlxplat_hotplug = &mlxplat_mlxcpld_hotplug_msn21xx_data;
+ mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data;
return 1;
};
@@ -314,9 +329,10 @@ static int __init mlxplat_init(void)
}
priv->pdev_hotplug = platform_device_register_resndata(
- &mlxplat_dev->dev, "mlxcpld-hotplug", -1,
- mlxplat_mlxcpld_hotplug_resources,
- ARRAY_SIZE(mlxplat_mlxcpld_hotplug_resources),
+ &mlxplat_dev->dev, "mlxcpld-hotplug",
+ PLATFORM_DEVID_NONE,
+ mlxplat_mlxcpld_resources,
+ ARRAY_SIZE(mlxplat_mlxcpld_resources),
mlxplat_hotplug, sizeof(*mlxplat_hotplug));
if (IS_ERR(priv->pdev_hotplug)) {
err = PTR_ERR(priv->pdev_hotplug);
diff --git a/drivers/platform/x86/silead_dmi.c b/drivers/platform/x86/silead_dmi.c
new file mode 100644
index 000000000000..02e11fdbf375
--- /dev/null
+++ b/drivers/platform/x86/silead_dmi.c
@@ -0,0 +1,136 @@
+/*
+ * Silead touchscreen driver DMI based configuration code
+ *
+ * Copyright (c) 2017 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Red Hat authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/i2c.h>
+#include <linux/notifier.h>
+#include <linux/property.h>
+#include <linux/string.h>
+
+struct silead_ts_dmi_data {
+ const char *acpi_name;
+ struct property_entry *properties;
+};
+
+static struct property_entry cube_iwork8_air_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1660),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 900),
+ PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+ PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-cube-iwork8-air.fw"),
+ PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+ { }
+};
+
+static const struct silead_ts_dmi_data cube_iwork8_air_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = cube_iwork8_air_props,
+};
+
+static struct property_entry jumper_ezpad_mini3_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1700),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 1150),
+ PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+ PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-jumper-ezpad-mini3.fw"),
+ PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+ { }
+};
+
+static const struct silead_ts_dmi_data jumper_ezpad_mini3_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = jumper_ezpad_mini3_props,
+};
+
+static const struct dmi_system_id silead_ts_dmi_table[] = {
+ {
+ /* CUBE iwork8 Air */
+ .driver_data = (void *)&cube_iwork8_air_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "cube"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "i1-TF"),
+ DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+ },
+ },
+ {
+ /* Jumper EZpad mini3 */
+ .driver_data = (void *)&jumper_ezpad_mini3_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ /* jumperx.T87.KFBNEEA02 with the version-nr dropped */
+ DMI_MATCH(DMI_BIOS_VERSION, "jumperx.T87.KFBNEEA"),
+ },
+ },
+ { },
+};
+
+static void silead_ts_dmi_add_props(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ const struct dmi_system_id *dmi_id;
+ const struct silead_ts_dmi_data *ts_data;
+ int error;
+
+ dmi_id = dmi_first_match(silead_ts_dmi_table);
+ if (!dmi_id)
+ return;
+
+ ts_data = dmi_id->driver_data;
+ if (has_acpi_companion(dev) &&
+ !strncmp(ts_data->acpi_name, client->name, I2C_NAME_SIZE)) {
+ error = device_add_properties(dev, ts_data->properties);
+ if (error)
+ dev_err(dev, "failed to add properties: %d\n", error);
+ }
+}
+
+static int silead_ts_dmi_notifier_call(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ silead_ts_dmi_add_props(dev);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block silead_ts_dmi_notifier = {
+ .notifier_call = silead_ts_dmi_notifier_call,
+};
+
+static int __init silead_ts_dmi_init(void)
+{
+ int error;
+
+ error = bus_register_notifier(&i2c_bus_type, &silead_ts_dmi_notifier);
+ if (error)
+ pr_err("%s: failed to register i2c bus notifier: %d\n",
+ __func__, error);
+
+ return error;
+}
+
+/*
+ * We are registering out notifier after i2c core is initialized and i2c bus
+ * itself is ready (which happens at postcore initcall level), but before
+ * ACPI starts enumerating devices (at subsys initcall level).
+ */
+arch_initcall(silead_ts_dmi_init);
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index cacb43fb1df7..1d18b32628ec 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -163,6 +163,7 @@ enum tpacpi_hkey_event_t {
TP_HKEY_EV_HOTKEY_BASE = 0x1001, /* first hotkey (FN+F1) */
TP_HKEY_EV_BRGHT_UP = 0x1010, /* Brightness up */
TP_HKEY_EV_BRGHT_DOWN = 0x1011, /* Brightness down */
+ TP_HKEY_EV_KBD_LIGHT = 0x1012, /* Thinklight/kbd backlight */
TP_HKEY_EV_VOL_UP = 0x1015, /* Volume up or unmute */
TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */
TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */
@@ -372,11 +373,9 @@ enum led_status_t {
TPACPI_LED_BLINK,
};
-/* Special LED class that can defer work */
+/* tpacpi LED class */
struct tpacpi_led_classdev {
struct led_classdev led_classdev;
- struct work_struct work;
- enum led_status_t new_state;
int led;
};
@@ -1959,7 +1958,7 @@ enum { /* Positions of some of the keys in hotkey masks */
TP_ACPI_HKEY_HIBERNATE_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF12,
TP_ACPI_HKEY_BRGHTUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNHOME,
TP_ACPI_HKEY_BRGHTDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNEND,
- TP_ACPI_HKEY_THNKLGHT_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP,
+ TP_ACPI_HKEY_KBD_LIGHT_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP,
TP_ACPI_HKEY_ZOOM_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNSPACE,
TP_ACPI_HKEY_VOLUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEUP,
TP_ACPI_HKEY_VOLDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEDOWN,
@@ -2344,7 +2343,7 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
n->display_toggle = !!(d & TP_NVRAM_MASK_HKT_DISPLAY);
n->hibernate_toggle = !!(d & TP_NVRAM_MASK_HKT_HIBERNATE);
}
- if (m & TP_ACPI_HKEY_THNKLGHT_MASK) {
+ if (m & TP_ACPI_HKEY_KBD_LIGHT_MASK) {
d = nvram_read_byte(TP_NVRAM_ADDR_THINKLIGHT);
n->thinklight_toggle = !!(d & TP_NVRAM_MASK_THINKLIGHT);
}
@@ -5084,18 +5083,27 @@ static struct ibm_struct video_driver_data = {
* Keyboard backlight subdriver
*/
+static enum led_brightness kbdlight_brightness;
+static DEFINE_MUTEX(kbdlight_mutex);
+
static int kbdlight_set_level(int level)
{
+ int ret = 0;
+
if (!hkey_handle)
return -ENXIO;
+ mutex_lock(&kbdlight_mutex);
+
if (!acpi_evalf(hkey_handle, NULL, "MLCS", "dd", level))
- return -EIO;
+ ret = -EIO;
+ else
+ kbdlight_brightness = level;
- return 0;
-}
+ mutex_unlock(&kbdlight_mutex);
-static int kbdlight_set_level_and_update(int level);
+ return ret;
+}
static int kbdlight_get_level(void)
{
@@ -5158,24 +5166,10 @@ static bool kbdlight_is_supported(void)
return status & BIT(9);
}
-static void kbdlight_set_worker(struct work_struct *work)
-{
- struct tpacpi_led_classdev *data =
- container_of(work, struct tpacpi_led_classdev, work);
-
- if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
- kbdlight_set_level_and_update(data->new_state);
-}
-
-static void kbdlight_sysfs_set(struct led_classdev *led_cdev,
+static int kbdlight_sysfs_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
- struct tpacpi_led_classdev *data =
- container_of(led_cdev,
- struct tpacpi_led_classdev,
- led_classdev);
- data->new_state = brightness;
- queue_work(tpacpi_wq, &data->work);
+ return kbdlight_set_level(brightness);
}
static enum led_brightness kbdlight_sysfs_get(struct led_classdev *led_cdev)
@@ -5193,7 +5187,8 @@ static struct tpacpi_led_classdev tpacpi_led_kbdlight = {
.led_classdev = {
.name = "tpacpi::kbd_backlight",
.max_brightness = 2,
- .brightness_set = &kbdlight_sysfs_set,
+ .flags = LED_BRIGHT_HW_CHANGED,
+ .brightness_set_blocking = &kbdlight_sysfs_set,
.brightness_get = &kbdlight_sysfs_get,
}
};
@@ -5205,7 +5200,6 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm)
vdbg_printk(TPACPI_DBG_INIT, "initializing kbdlight subdriver\n");
TPACPI_ACPIHANDLE_INIT(hkey);
- INIT_WORK(&tpacpi_led_kbdlight.work, kbdlight_set_worker);
if (!kbdlight_is_supported()) {
tp_features.kbdlight = 0;
@@ -5213,6 +5207,7 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm)
return 1;
}
+ kbdlight_brightness = kbdlight_sysfs_get(NULL);
tp_features.kbdlight = 1;
rc = led_classdev_register(&tpacpi_pdev->dev,
@@ -5222,6 +5217,8 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm)
return rc;
}
+ tpacpi_hotkey_driver_mask_set(hotkey_driver_mask |
+ TP_ACPI_HKEY_KBD_LIGHT_MASK);
return 0;
}
@@ -5229,7 +5226,6 @@ static void kbdlight_exit(void)
{
if (tp_features.kbdlight)
led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev);
- flush_workqueue(tpacpi_wq);
}
static int kbdlight_set_level_and_update(int level)
@@ -5358,25 +5354,11 @@ static int light_set_status(int status)
return -ENXIO;
}
-static void light_set_status_worker(struct work_struct *work)
-{
- struct tpacpi_led_classdev *data =
- container_of(work, struct tpacpi_led_classdev, work);
-
- if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
- light_set_status((data->new_state != TPACPI_LED_OFF));
-}
-
-static void light_sysfs_set(struct led_classdev *led_cdev,
+static int light_sysfs_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
- struct tpacpi_led_classdev *data =
- container_of(led_cdev,
- struct tpacpi_led_classdev,
- led_classdev);
- data->new_state = (brightness != LED_OFF) ?
- TPACPI_LED_ON : TPACPI_LED_OFF;
- queue_work(tpacpi_wq, &data->work);
+ return light_set_status((brightness != LED_OFF) ?
+ TPACPI_LED_ON : TPACPI_LED_OFF);
}
static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev)
@@ -5387,7 +5369,7 @@ static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev)
static struct tpacpi_led_classdev tpacpi_led_thinklight = {
.led_classdev = {
.name = "tpacpi::thinklight",
- .brightness_set = &light_sysfs_set,
+ .brightness_set_blocking = &light_sysfs_set,
.brightness_get = &light_sysfs_get,
}
};
@@ -5403,7 +5385,6 @@ static int __init light_init(struct ibm_init_struct *iibm)
TPACPI_ACPIHANDLE_INIT(lght);
}
TPACPI_ACPIHANDLE_INIT(cmos);
- INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker);
/* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
tp_features.light = (cmos_handle || lght_handle) && !ledb_handle;
@@ -5437,7 +5418,6 @@ static int __init light_init(struct ibm_init_struct *iibm)
static void light_exit(void)
{
led_classdev_unregister(&tpacpi_led_thinklight.led_classdev);
- flush_workqueue(tpacpi_wq);
}
static int light_read(struct seq_file *m)
@@ -5704,29 +5684,21 @@ static int led_set_status(const unsigned int led,
return rc;
}
-static void led_set_status_worker(struct work_struct *work)
-{
- struct tpacpi_led_classdev *data =
- container_of(work, struct tpacpi_led_classdev, work);
-
- if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
- led_set_status(data->led, data->new_state);
-}
-
-static void led_sysfs_set(struct led_classdev *led_cdev,
+static int led_sysfs_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct tpacpi_led_classdev *data = container_of(led_cdev,
struct tpacpi_led_classdev, led_classdev);
+ enum led_status_t new_state;
if (brightness == LED_OFF)
- data->new_state = TPACPI_LED_OFF;
+ new_state = TPACPI_LED_OFF;
else if (tpacpi_led_state_cache[data->led] != TPACPI_LED_BLINK)
- data->new_state = TPACPI_LED_ON;
+ new_state = TPACPI_LED_ON;
else
- data->new_state = TPACPI_LED_BLINK;
+ new_state = TPACPI_LED_BLINK;
- queue_work(tpacpi_wq, &data->work);
+ return led_set_status(data->led, new_state);
}
static int led_sysfs_blink_set(struct led_classdev *led_cdev,
@@ -5743,10 +5715,7 @@ static int led_sysfs_blink_set(struct led_classdev *led_cdev,
} else if ((*delay_on != 500) || (*delay_off != 500))
return -EINVAL;
- data->new_state = TPACPI_LED_BLINK;
- queue_work(tpacpi_wq, &data->work);
-
- return 0;
+ return led_set_status(data->led, TPACPI_LED_BLINK);
}
static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev)
@@ -5775,7 +5744,6 @@ static void led_exit(void)
led_classdev_unregister(&tpacpi_leds[i].led_classdev);
}
- flush_workqueue(tpacpi_wq);
kfree(tpacpi_leds);
}
@@ -5789,7 +5757,7 @@ static int __init tpacpi_init_led(unsigned int led)
if (!tpacpi_led_names[led])
return 0;
- tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set;
+ tpacpi_leds[led].led_classdev.brightness_set_blocking = &led_sysfs_set;
tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set;
if (led_supported == TPACPI_LED_570)
tpacpi_leds[led].led_classdev.brightness_get =
@@ -5797,8 +5765,6 @@ static int __init tpacpi_init_led(unsigned int led)
tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led];
- INIT_WORK(&tpacpi_leds[led].work, led_set_status_worker);
-
rc = led_classdev_register(&tpacpi_pdev->dev,
&tpacpi_leds[led].led_classdev);
if (rc < 0)
@@ -9169,6 +9135,24 @@ static void tpacpi_driver_event(const unsigned int hkey_event)
volume_alsa_notify_change();
}
}
+ if (tp_features.kbdlight && hkey_event == TP_HKEY_EV_KBD_LIGHT) {
+ enum led_brightness brightness;
+
+ mutex_lock(&kbdlight_mutex);
+
+ /*
+ * Check the brightness actually changed, setting the brightness
+ * through kbdlight_set_level() also triggers this event.
+ */
+ brightness = kbdlight_sysfs_get(NULL);
+ if (kbdlight_brightness != brightness) {
+ kbdlight_brightness = brightness;
+ led_classdev_notify_brightness_hw_changed(
+ &tpacpi_led_kbdlight.led_classdev, brightness);
+ }
+
+ mutex_unlock(&kbdlight_mutex);
+ }
}
static void hotkey_driver_event(const unsigned int scancode)