Commit 94eae803 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull x86 platform driver updates from Darren Hart:
 "Big picture:

   - New intel_turbo_max_3 driver, providing max core frequency
     information to the scheduler. Intel PMC APL support, s0ix read API,
     and fixes.

   - New Silead touchscreen platform touchscreen descriptions.
     Additional hotkey support for the intel-hid driver.

   - New model support for dell-laptop and hp_accel.

   - Several cleanups, especially to the fujitsu-laptop and
     intel_mid_powerbtn drivers.

  Detail summary:

  platorm/x86:
   - silead depends on I2C being built-in
   - add support for devices with Silead touchscreens
   - Support Turbo Boost Max 3.0 for non HWP systems

  intel_turbo_max_3:
   - make it explicitly non-modular

  dell-laptop:
   - Add Latitude 7480 and others to the DMI whitelist

  intel-hid:
   - Support 5 button array

  thinkpad_acpi:
   - Call led_classdev_notify_brightness_hw_changed on kbd brightness change
   - Use brightness_set_blocking callback for LEDs
   - Stop setting led_classdev brightness directly

  acer-wmi:
   - add another KEY_WLAN keycode
   - Inform firmware that RF Button Driver is active
   - setup accelerometer when machine has appropriate notify event

  asus-wireless:
   - Fix indentation
   - Use per-HID HSWC parameters

  intel_pmc_ipc:
   - Add APL PMC PCI Id
   - read s0ix residency API
   - Remove unused iTCO_version variable

  alienware-wmi:
   - Remove header duplicate

  intel_pmc_core:
   - fix out-of-bounds accesses on stack

  intel_mid_powerbtn:
   - Use SCU IPC directly
   - Unify IRQ acknowledgment
   - Move comment to where it belongs
   - Unify PBSTATUS access
   - Remove snail address
   - Sort headers alphabetically
   - Join string literals
   - Enable driver for Merrifield
   - Acknowledge interrupts
   - Factor out mfld_ack()
   - Introduce driver data
   - Substitute mfld by mid
   - Convert to use devm_*()

  fujitsu-laptop:
   - make hotkey handling functions more similar
   - break up complex loop condition
   - move keycode processing to separate functions
   - decrease indentation in acpi_fujitsu_hotkey_notify()
   - simplify logolamp_get()
   - rework logolamp_set() to properly handle errors
   - set default trigger for radio LED to rfkill-any

  dell-smbios:
   - Auto-select as needed

  intel_mid_thermal:
   - Fix module autoload
   - Remove duplicated platform device ID

  mlx-platform:
   - mlxcpld-hotplug driver style fixes

  hp_accel:
   - Add support for HP ZBook 17"

* tag 'platform-drivers-x86-v4.11-1' of git://git.infradead.org/linux-platform-drivers-x86: (45 commits)
  platform/x86: intel_turbo_max_3: make it explicitly non-modular
  platform/x86: dell-laptop: Add Latitude 7480 and others to the DMI whitelist
  platform/x86: intel-hid: Support 5 button array
  platform/x86: thinkpad_acpi: Call led_classdev_notify_brightness_hw_changed on kbd brightness change
  platform/x86: thinkpad_acpi: Use brightness_set_blocking callback for LEDs
  platform/x86: thinkpad_acpi: Stop setting led_classdev brightness directly
  leds: class: Add new optional brightness_hw_changed attribute
  platform/x86: acer-wmi: add another KEY_WLAN keycode
  platform/x86: acer-wmi: Inform firmware that RF Button Driver is active
  platform/x86: asus-wireless: Fix indentation
  platform/x86: asus-wireless: Use per-HID HSWC parameters
  platform/x86: intel_pmc_ipc: Add APL PMC PCI Id
  platform/x86: intel_pmc_ipc: read s0ix residency API
  platform/x86: alienware-wmi: Remove header duplicate
  platform/x86: intel_mid_powerbtn: Use SCU IPC directly
  platform/x86: intel_mid_powerbtn: Unify IRQ acknowledgment
  platform/x86: intel_mid_powerbtn: Move comment to where it belongs
  platform/x86: intel_mid_powerbtn: Unify PBSTATUS access
  platform/x86: intel_pmc_core: fix out-of-bounds accesses on stack
  platform/x86: silead depends on I2C being built-in
  ...
parents 5d8a00ee af050abb
......@@ -11456,6 +11456,14 @@ F: drivers/media/usb/siano/
F: drivers/media/usb/siano/
F: drivers/media/mmc/siano/
SILEAD TOUCHSCREEN DRIVER
M: Hans de Goede <hdegoede@redhat.com>
L: linux-input@vger.kernel.org
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/input/touchscreen/silead.c
F: drivers/platform/x86/silead_dmi.c
SIMPLEFB FB DRIVER
M: Hans de Goede <hdegoede@redhat.com>
L: linux-fbdev@vger.kernel.org
......
......@@ -30,6 +30,7 @@ int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen,
u32 *out, u32 outlen, u32 dptr, u32 sptr);
int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
u32 *out, u32 outlen);
int intel_pmc_s0ix_counter_read(u64 *data);
#else
......@@ -50,6 +51,11 @@ static inline int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
return -EINVAL;
}
static inline int intel_pmc_s0ix_counter_read(u64 *data)
{
return -EINVAL;
}
#endif /*CONFIG_INTEL_PMC_IPC*/
#endif
......@@ -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
......
......@@ -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
......@@ -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)
{
*(acpi_handle *)retval = ah;
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_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");
......
......@@ -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"
......
......@@ -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",
......
......@@ -105,6 +105,12 @@ static const struct dmi_system_id dell_device_table[] __initconst = {
DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/
},
},
{
.ident = "Dell Computer Corporation",
.matches = {
......
......@@ -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;
int ret;
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;
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,21 +1035,68 @@ 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
......@@ -1073,37 +1126,11 @@ static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event)
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);
}
}
if (keycode > 0)
acpi_fujitsu_hotkey_press(keycode);
else if (keycode == 0)
acpi_fujitsu_hotkey_release();
}
/* On some models (first seen on the Skylake-based Lifebook
......@@ -1119,17 +1146,6 @@ static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event)
input_sync(input);
}
break;
default:
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);
break;
}
}
/* Initialization */
......
......@@ -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"
......
/*
* 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 @@ static int intel_hid_input_setup(struct platform_device *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
......
/*
* 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);
......@@ -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 = {
......
......@@ -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)
......
......@@ -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 @@ static int ipc_plat_probe(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);
}
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;
}
......
/*
* 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)
......@@ -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);
......
/*
* 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);
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment