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
...
This commit is contained in:
@@ -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(¶ms, &return_value);
|
||||
status = wmid3_set_function_mode(¶ms, &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(¶ms, &return_value);
|
||||
status = wmid3_set_function_mode(¶ms, &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(¶ms, &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");
|
||||
|
||||
@@ -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,98 +1035,54 @@ 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 (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
default:
|
||||
if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
|
||||
keycode = KEY_UNKNOWN;
|
||||
vdbg_printk(FUJLAPTOP_DBG_WARN,
|
||||
"Unsupported event [0x%x]\n", event);
|
||||
@@ -1128,8 +1090,62 @@ static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event)
|
||||
input_sync(input);
|
||||
input_report_key(input, keycode, 0);
|
||||
input_sync(input);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fujitsu_hotkey->rfkill_supported)
|
||||
fujitsu_hotkey->rfkill_state =
|
||||
call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0);
|
||||
|
||||
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)
|
||||
acpi_fujitsu_hotkey_press(keycode);
|
||||
else if (keycode == 0)
|
||||
acpi_fujitsu_hotkey_release();
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* 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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 @@ 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;
|
||||
}
|
||||
|
||||
151
drivers/platform/x86/intel_turbo_max_3.c
Normal file
151
drivers/platform/x86/intel_turbo_max_3.c
Normal file
@@ -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)
|
||||
@@ -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);
|
||||
|
||||
136
drivers/platform/x86/silead_dmi.c
Normal file
136
drivers/platform/x86/silead_dmi.c
Normal file
@@ -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);
|
||||
@@ -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,19 +5083,28 @@ 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);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kbdlight_set_level_and_update(int level);
|
||||
|
||||
static int kbdlight_get_level(void)
|
||||
{
|
||||
int status = 0;
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user