diff options
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/chrome/cros_ec_dev.c | 8 | ||||
-rw-r--r-- | drivers/platform/x86/Kconfig | 25 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/asus-nb-wmi.c | 50 | ||||
-rw-r--r-- | drivers/platform/x86/asus-wireless.c | 91 | ||||
-rw-r--r-- | drivers/platform/x86/asus-wmi.c | 8 | ||||
-rw-r--r-- | drivers/platform/x86/asus-wmi.h | 1 | ||||
-rw-r--r-- | drivers/platform/x86/dell-wmi.c | 293 | ||||
-rw-r--r-- | drivers/platform/x86/fujitsu-laptop.c | 81 | ||||
-rw-r--r-- | drivers/platform/x86/hp-wmi.c | 7 | ||||
-rw-r--r-- | drivers/platform/x86/ideapad-laptop.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/intel-hid.c | 5 | ||||
-rw-r--r-- | drivers/platform/x86/intel-vbtn.c | 188 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmc_core.c | 54 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmc_core.h | 3 | ||||
-rw-r--r-- | drivers/platform/x86/intel_telemetry_debugfs.c | 5 | ||||
-rw-r--r-- | drivers/platform/x86/intel_telemetry_pltdrv.c | 5 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 87 | ||||
-rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 136 |
19 files changed, 802 insertions, 248 deletions
diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c index 6d8ee3b..8abd80d 100644 --- a/drivers/platform/chrome/cros_ec_dev.c +++ b/drivers/platform/chrome/cros_ec_dev.c @@ -151,13 +151,19 @@ static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg) goto exit; } + if (u_cmd.outsize != s_cmd->outsize || + u_cmd.insize != s_cmd->insize) { + ret = -EINVAL; + goto exit; + } + s_cmd->command += ec->cmd_offset; ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd); /* Only copy data to userland if data was received. */ if (ret < 0) goto exit; - if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + u_cmd.insize)) + if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + s_cmd->insize)) ret = -EFAULT; exit: kfree(s_cmd); diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index c06bb85..81b8dcc 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -103,7 +103,6 @@ config DELL_SMBIOS config DELL_LAPTOP tristate "Dell Laptop Extras" - depends on X86 depends on DELL_SMBIOS depends on DMI depends on BACKLIGHT_CLASS_DEVICE @@ -505,7 +504,7 @@ config THINKPAD_ACPI_HOTKEY_POLL config SENSORS_HDAPS tristate "Thinkpad Hard Drive Active Protection System (hdaps)" - depends on INPUT && X86 + depends on INPUT select INPUT_POLLDEV default n help @@ -604,6 +603,8 @@ config ASUS_WIRELESS tristate "Asus Wireless Radio Control Driver" depends on ACPI depends on INPUT + select NEW_LEDS + select LEDS_CLASS ---help--- The Asus Wireless Radio Control handles the airplane mode hotkey present on some Asus laptops. @@ -669,6 +670,7 @@ config ACPI_TOSHIBA depends on SERIO_I8042 || SERIO_I8042 = n depends on ACPI_VIDEO || ACPI_VIDEO = n depends on RFKILL || RFKILL = n + depends on IIO select INPUT_POLLDEV select INPUT_SPARSEKMAP ---help--- @@ -749,7 +751,7 @@ config TOSHIBA_WMI config ACPI_CMPC tristate "CMPC Laptop Extras" - depends on X86 && ACPI + depends on ACPI depends on RFKILL || RFKILL=n select INPUT select BACKLIGHT_CLASS_DEVICE @@ -771,6 +773,18 @@ config INTEL_HID_EVENT To compile this driver as a module, choose M here: the module will be called intel_hid. +config INTEL_VBTN + tristate "INTEL VIRTUAL BUTTON" + depends on ACPI + depends on INPUT + select INPUT_SPARSEKMAP + help + This driver provides support for the Intel Virtual Button interface. + Some laptops require this driver for power button support. + + To compile this driver as a module, choose M here: the module will + be called intel_vbtn. + config INTEL_SCU_IPC bool "Intel SCU IPC Support" depends on X86_INTEL_MID @@ -848,7 +862,7 @@ config INTEL_IMR config INTEL_PMC_CORE bool "Intel PMC Core driver" - depends on X86 && PCI + depends on PCI ---help--- The Intel Platform Controller Hub for Intel Core SoCs provides access to Power Management Controller registers via a PCI interface. This @@ -860,7 +874,7 @@ config INTEL_PMC_CORE config IBM_RTL tristate "Device driver to enable PRTL support" - depends on X86 && PCI + depends on PCI ---help--- Enable support for IBM Premium Real Time Mode (PRTM). This module will allow you the enter and exit PRTM in the BIOS via @@ -894,7 +908,6 @@ config XO15_EBOOK config SAMSUNG_LAPTOP tristate "Samsung Laptop driver" - depends on X86 depends on RFKILL || RFKILL = n depends on ACPI_VIDEO || ACPI_VIDEO = n depends on BACKLIGHT_CLASS_DEVICE diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 9b11b40..2efa86d 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o +obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 091ca7a..adecc1c 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -78,6 +78,15 @@ static struct quirk_entry quirk_asus_x200ca = { .wapf = 2, }; +static struct quirk_entry quirk_no_rfkill = { + .no_rfkill = true, +}; + +static struct quirk_entry quirk_no_rfkill_wapf4 = { + .wapf = 4, + .no_rfkill = true, +}; + static int dmi_matched(const struct dmi_system_id *dmi) { quirks = dmi->driver_data; @@ -133,7 +142,7 @@ static const struct dmi_system_id asus_quirks[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "X456UA"), }, - .driver_data = &quirk_asus_wapf4, + .driver_data = &quirk_no_rfkill_wapf4, }, { .callback = dmi_matched, @@ -142,7 +151,7 @@ static const struct dmi_system_id asus_quirks[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "X456UF"), }, - .driver_data = &quirk_asus_wapf4, + .driver_data = &quirk_no_rfkill_wapf4, }, { .callback = dmi_matched, @@ -306,6 +315,42 @@ static const struct dmi_system_id asus_quirks[] = { }, .driver_data = &quirk_asus_x200ca, }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X555UB", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X555UB"), + }, + .driver_data = &quirk_no_rfkill, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. N552VW", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "N552VW"), + }, + .driver_data = &quirk_no_rfkill, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. U303LB", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "U303LB"), + }, + .driver_data = &quirk_no_rfkill, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. Z550MA", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "Z550MA"), + }, + .driver_data = &quirk_no_rfkill, + }, {}, }; @@ -356,6 +401,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */ { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, { KE_IGNORE, 0x6E, }, /* Low Battery notification */ + { KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */ { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */ { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */ { KE_KEY, 0x82, { KEY_CAMERA } }, diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c index 9ec721e..9f31bc1 100644 --- a/drivers/platform/x86/asus-wireless.c +++ b/drivers/platform/x86/asus-wireless.c @@ -15,11 +15,78 @@ #include <linux/acpi.h> #include <linux/input.h> #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 asus_wireless_data { struct input_dev *idev; + struct acpi_device *adev; + struct workqueue_struct *wq; + struct work_struct led_work; + struct led_classdev led; + int led_state; }; +static u64 asus_wireless_method(acpi_handle handle, const char *method, + int param) +{ + struct acpi_object_list p; + union acpi_object obj; + acpi_status s; + u64 ret; + + acpi_handle_debug(handle, "Evaluating method %s, parameter %#x\n", + method, param); + obj.type = ACPI_TYPE_INTEGER; + obj.integer.value = param; + p.count = 1; + p.pointer = &obj; + + s = acpi_evaluate_integer(handle, (acpi_string) method, &p, &ret); + if (ACPI_FAILURE(s)) + acpi_handle_err(handle, + "Failed to eval method %s, param %#x (%d)\n", + method, param, s); + acpi_handle_debug(handle, "%s returned %#x\n", method, (uint) ret); + return ret; +} + +static enum led_brightness led_state_get(struct led_classdev *led) +{ + struct asus_wireless_data *data; + int s; + + 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) + return LED_FULL; + return LED_OFF; +} + +static void led_state_update(struct work_struct *work) +{ + struct asus_wireless_data *data; + + data = container_of(work, struct asus_wireless_data, led_work); + asus_wireless_method(acpi_device_handle(data->adev), "HSWC", + data->led_state); +} + +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; + queue_work(data->wq, &data->led_work); +} + static void asus_wireless_notify(struct acpi_device *adev, u32 event) { struct asus_wireless_data *data = acpi_driver_data(adev); @@ -37,6 +104,7 @@ 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; + int err; data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -52,11 +120,32 @@ static int asus_wireless_add(struct acpi_device *adev) data->idev->id.vendor = PCI_VENDOR_ID_ASUSTEK; set_bit(EV_KEY, data->idev->evbit); set_bit(KEY_RFKILL, data->idev->keybit); - return input_register_device(data->idev); + err = input_register_device(data->idev); + if (err) + return err; + + data->adev = adev; + data->wq = create_singlethread_workqueue("asus_wireless_workqueue"); + if (!data->wq) + return -ENOMEM; + INIT_WORK(&data->led_work, led_state_update); + data->led.name = "asus-wireless::airplane"; + data->led.brightness_set = led_state_set; + data->led.brightness_get = led_state_get; + data->led.flags = LED_CORE_SUSPENDRESUME; + data->led.max_brightness = 1; + err = devm_led_classdev_register(&adev->dev, &data->led); + if (err) + destroy_workqueue(data->wq); + return err; } static int asus_wireless_remove(struct acpi_device *adev) { + struct asus_wireless_data *data = acpi_driver_data(adev); + + if (data->wq) + destroy_workqueue(data->wq); return 0; } diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index a26dca3..7c093a0 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -2069,9 +2069,11 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_leds; - err = asus_wmi_rfkill_init(asus); - if (err) - goto fail_rfkill; + if (!asus->driver->quirks->no_rfkill) { + err = asus_wmi_rfkill_init(asus); + if (err) + goto fail_rfkill; + } /* Some Asus desktop boards export an acpi-video backlight interface, stop this from showing up */ diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index 4da4c8b..5de1df5 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -38,6 +38,7 @@ struct key_entry; struct asus_wmi; struct quirk_entry { + bool no_rfkill; bool hotplug_wireless; bool scalar_panel_brightness; bool store_backlight_power; diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 15c6f11..d2bc092 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -80,66 +80,115 @@ static const struct dmi_system_id dell_wmi_smbios_list[] __initconst = { }; /* + * Keymap for WMI events of type 0x0000 + * * Certain keys are flagged as KE_IGNORE. All of these are either * notifications (rather than requests for change) or are also sent * via the keyboard controller so should not be sent again. */ - -static const struct key_entry dell_wmi_legacy_keymap[] __initconst = { +static const struct key_entry dell_wmi_keymap_type_0000[] __initconst = { { KE_IGNORE, 0x003a, { KEY_CAPSLOCK } }, - { KE_KEY, 0xe045, { KEY_PROG1 } }, - { KE_KEY, 0xe009, { KEY_EJECTCD } }, - - /* These also contain the brightness level at offset 6 */ - { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } }, - { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } }, + /* Key code is followed by brightness level */ + { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } }, /* Battery health status button */ - { KE_KEY, 0xe007, { KEY_BATTERY } }, + { KE_KEY, 0xe007, { KEY_BATTERY } }, - /* Radio devices state change */ + /* Radio devices state change, key code is followed by other values */ { KE_IGNORE, 0xe008, { KEY_RFKILL } }, - /* The next device is at offset 6, the active devices are at - offset 8 and the attached devices at offset 10 */ - { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0xe009, { KEY_EJECTCD } }, + /* Key code is followed by: next, active and attached devices */ + { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } }, + + /* Key code is followed by keyboard illumination level */ { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } }, /* BIOS error detected */ { KE_IGNORE, 0xe00d, { KEY_RESERVED } }, + /* Unknown, defined in ACPI DSDT */ + /* { KE_IGNORE, 0xe00e, { KEY_RESERVED } }, */ + /* Wifi Catcher */ - { KE_KEY, 0xe011, {KEY_PROG2 } }, + { KE_KEY, 0xe011, { KEY_PROG2 } }, /* Ambient light sensor toggle */ { KE_IGNORE, 0xe013, { KEY_RESERVED } }, { KE_IGNORE, 0xe020, { KEY_MUTE } }, + /* Unknown, defined in ACPI DSDT */ + /* { KE_IGNORE, 0xe023, { KEY_RESERVED } }, */ + + /* Untested, Dell Instant Launch key on Inspiron 7520 */ + /* { KE_IGNORE, 0xe024, { KEY_RESERVED } }, */ + /* Dell Instant Launch key */ - { KE_KEY, 0xe025, { KEY_PROG4 } }, - { KE_KEY, 0xe029, { KEY_PROG4 } }, + { KE_KEY, 0xe025, { KEY_PROG4 } }, /* Audio panel key */ { KE_IGNORE, 0xe026, { KEY_RESERVED } }, + /* LCD Display On/Off Control key */ + { KE_KEY, 0xe027, { KEY_DISPLAYTOGGLE } }, + + /* Untested, Multimedia key on Dell Vostro 3560 */ + /* { KE_IGNORE, 0xe028, { KEY_RESERVED } }, */ + + /* Dell Instant Launch key */ + { KE_KEY, 0xe029, { KEY_PROG4 } }, + + /* Untested, Windows Mobility Center button on Inspiron 7520 */ + /* { KE_IGNORE, 0xe02a, { KEY_RESERVED } }, */ + + /* Unknown, defined in ACPI DSDT */ + /* { KE_IGNORE, 0xe02b, { KEY_RESERVED } }, */ + + /* Untested, Dell Audio With Preset Switch button on Inspiron 7520 */ + /* { KE_IGNORE, 0xe02c, { KEY_RESERVED } }, */ + { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } }, { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } }, { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } }, { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } }, { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } }, + + /* NIC Link is Up */ + { KE_IGNORE, 0xe043, { KEY_RESERVED } }, + + /* NIC Link is Down */ + { KE_IGNORE, 0xe044, { KEY_RESERVED } }, + + /* + * This entry is very suspicious! + * Originally Matthew Garrett created this dell-wmi driver specially for + * "button with a picture of a battery" which has event code 0xe045. + * Later Mario Limonciello from Dell told us that event code 0xe045 is + * reported by Num Lock and should be ignored because key is send also + * by keyboard controller. + * So for now we will ignore this event to prevent potential double + * Num Lock key press. + */ { KE_IGNORE, 0xe045, { KEY_NUMLOCK } }, + + /* Scroll lock and also going to tablet mode on portable devices */ { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } }, + + /* Untested, going from tablet mode on portable devices */ + /* { KE_IGNORE, 0xe047, { KEY_RESERVED } }, */ + + /* Dell Support Center key */ + { KE_IGNORE, 0xe06e, { KEY_RESERVED } }, + { KE_IGNORE, 0xe0f7, { KEY_MUTE } }, { KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } }, { KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } }, - { KE_END, 0 } }; -static bool dell_new_hk_type; - struct dell_bios_keymap_entry { u16 scancode; u16 keycode; @@ -153,6 +202,7 @@ struct dell_bios_hotkey_table { struct dell_dmi_results { int err; + int keymap_size; struct key_entry *keymap; }; @@ -201,10 +251,12 @@ static const u16 bios_to_linux_keycode[256] __initconst = { }; /* + * Keymap for WMI events of type 0x0010 + * * These are applied if the 0xB2 DMI hotkey table is present and doesn't * override them. */ -static const struct key_entry dell_wmi_extra_keymap[] __initconst = { +static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = { /* Fn-lock */ { KE_IGNORE, 0x151, { KEY_RESERVED } }, @@ -224,21 +276,39 @@ static const struct key_entry dell_wmi_extra_keymap[] __initconst = { { KE_IGNORE, 0x155, { KEY_RESERVED } }, }; +/* + * Keymap for WMI events of type 0x0011 + */ +static const struct key_entry dell_wmi_keymap_type_0011[] __initconst = { + /* Battery unplugged */ + { KE_IGNORE, 0xfff0, { KEY_RESERVED } }, + + /* Battery inserted */ + { KE_IGNORE, 0xfff1, { KEY_RESERVED } }, + + /* Keyboard backlight level changed */ + { KE_IGNORE, 0x01e1, { KEY_RESERVED } }, + { KE_IGNORE, 0x02ea, { KEY_RESERVED } }, + { KE_IGNORE, 0x02eb, { KEY_RESERVED } }, + { KE_IGNORE, 0x02ec, { KEY_RESERVED } }, + { KE_IGNORE, 0x02f6, { KEY_RESERVED } }, +}; + static struct input_dev *dell_wmi_input_dev; -static void dell_wmi_process_key(int reported_key) +static void dell_wmi_process_key(int type, int code) { const struct key_entry *key; key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev, - reported_key); + (type << 16) | code); if (!key) { - pr_info("Unknown key with scancode 0x%x pressed\n", - reported_key); + pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n", + type, code); return; } - pr_debug("Key %x pressed\n", reported_key); + pr_debug("Key with type 0x%04x and code 0x%04x pressed\n", type, code); /* Don't report brightness notifications that will also come via ACPI */ if ((key->keycode == KEY_BRIGHTNESSUP || @@ -246,7 +316,7 @@ static void dell_wmi_process_key(int reported_key) acpi_video_handles_brightness_key_presses()) return; - if (reported_key == 0xe025 && !wmi_requires_smbios_request) + if (type == 0x0000 && code == 0xe025 && !wmi_requires_smbios_request) return; sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true); @@ -284,18 +354,6 @@ static void dell_wmi_notify(u32 value, void *context) buffer_entry = (u16 *)obj->buffer.pointer; buffer_size = obj->buffer.length/2; - - if (!dell_new_hk_type) { - if (buffer_size >= 3 && buffer_entry[1] == 0x0) - dell_wmi_process_key(buffer_entry[2]); - else if (buffer_size >= 2) - dell_wmi_process_key(buffer_entry[1]); - else - pr_info("Received unknown WMI event\n"); - kfree(obj); - return; - } - buffer_end = buffer_entry + buffer_size; /* @@ -330,62 +388,18 @@ static void dell_wmi_notify(u32 value, void *context) pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry); switch (buffer_entry[1]) { - case 0x00: - for (i = 2; i < len; ++i) { - switch (buffer_entry[i]) { - case 0xe043: - /* NIC Link is Up */ - pr_debug("NIC Link is Up\n"); - break; - case 0xe044: - /* NIC Link is Down */ - pr_debug("NIC Link is Down\n"); - break; - case 0xe045: - /* Unknown event but defined in DSDT */ - default: - /* Unknown event */ - pr_info("Unknown WMI event type 0x00: " - "0x%x\n", (int)buffer_entry[i]); - break; - } - } + case 0x0000: /* One key pressed or event occurred */ + if (len > 2) + dell_wmi_process_key(0x0000, buffer_entry[2]); + /* Other entries could contain additional information */ break; - case 0x10: - /* Keys pressed */ + case 0x0010: /* Sequence of keys pressed */ + case 0x0011: /* Sequence of events occurred */ for (i = 2; i < len; ++i) - dell_wmi_process_key(buffer_entry[i]); - break; - case 0x11: - for (i = 2; i < len; ++i) { - switch (buffer_entry[i]) { - case 0xfff0: - /* Battery unplugged */ - pr_debug("Battery unplugged\n"); - break; - case 0xfff1: - /* Battery inserted */ - pr_debug("Battery inserted\n"); - break; - case 0x01e1: - case 0x02ea: - case 0x02eb: - case 0x02ec: - case 0x02f6: - /* Keyboard backlight level changed */ - pr_debug("Keyboard backlight level " - "changed\n"); - break; - default: - /* Unknown event */ - pr_info("Unknown WMI event type 0x11: " - "0x%x\n", (int)buffer_entry[i]); - break; - } - } + dell_wmi_process_key(buffer_entry[1], + buffer_entry[i]); break; - default: - /* Unknown event */ + default: /* Unknown event */ pr_info("Unknown WMI event type 0x%x\n", (int)buffer_entry[1]); break; @@ -410,7 +424,6 @@ static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len) } static void __init handle_dmi_entry(const struct dmi_header *dm, - void *opaque) { @@ -418,7 +431,6 @@ static void __init handle_dmi_entry(const struct dmi_header *dm, struct dell_bios_hotkey_table *table; int hotkey_num, i, pos = 0; struct key_entry *keymap; - int num_bios_keys; if (results->err || results->keymap) return; /* We already found the hotkey table. */ @@ -442,8 +454,7 @@ static void __init handle_dmi_entry(const struct dmi_header *dm, return; } - keymap = kcalloc(hotkey_num + ARRAY_SIZE(dell_wmi_extra_keymap) + 1, - sizeof(struct key_entry), GFP_KERNEL); + keymap = kcalloc(hotkey_num, sizeof(struct key_entry), GFP_KERNEL); if (!keymap) { results->err = -ENOMEM; return; @@ -480,31 +491,15 @@ static void __init handle_dmi_entry(const struct dmi_header *dm, pos++; } - num_bios_keys = pos; - - for (i = 0; i < ARRAY_SIZE(dell_wmi_extra_keymap); i++) { - const struct key_entry *entry = &dell_wmi_extra_keymap[i]; - - /* - * Check if we've already found this scancode. This takes - * quadratic time, but it doesn't matter unless the list - * of extra keys gets very long. - */ - if (!have_scancode(entry->code, keymap, num_bios_keys)) { - keymap[pos] = *entry; - pos++; - } - } - - keymap[pos].type = KE_END; - results->keymap = keymap; + results->keymap_size = pos; } static int __init dell_wmi_input_setup(void) { struct dell_dmi_results dmi_results = {}; - int err; + struct key_entry *keymap; + int err, i, pos = 0; dell_wmi_input_dev = input_allocate_device(); if (!dell_wmi_input_dev) @@ -528,21 +523,71 @@ static int __init dell_wmi_input_setup(void) goto err_free_dev; } - if (dmi_results.keymap) { - dell_new_hk_type = true; + keymap = kcalloc(dmi_results.keymap_size + + ARRAY_SIZE(dell_wmi_keymap_type_0000) + + ARRAY_SIZE(dell_wmi_keymap_type_0010) + + ARRAY_SIZE(dell_wmi_keymap_type_0011) + + 1, + sizeof(struct key_entry), GFP_KERNEL); + if (!keymap) { + kfree(dmi_results.keymap); + err = -ENOMEM; + goto err_free_dev; + } + + /* Append table with events of type 0x0010 which comes from DMI */ + for (i = 0; i < dmi_results.keymap_size; i++) { + keymap[pos] = dmi_results.keymap[i]; + keymap[pos].code |= (0x0010 << 16); + pos++; + } + + kfree(dmi_results.keymap); - err = sparse_keymap_setup(dell_wmi_input_dev, - dmi_results.keymap, NULL); + /* Append table with extra events of type 0x0010 which are not in DMI */ + for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0010); i++) { + const struct key_entry *entry = &dell_wmi_keymap_type_0010[i]; /* - * Sparse keymap library makes a copy of keymap so we - * don't need the original one that was allocated. + * Check if we've already found this scancode. This takes + * quadratic time, but it doesn't matter unless the list + * of extra keys gets very long. */ - kfree(dmi_results.keymap); - } else { - err = sparse_keymap_setup(dell_wmi_input_dev, - dell_wmi_legacy_keymap, NULL); + if (dmi_results.keymap_size && + have_scancode(entry->code | (0x0010 << 16), + keymap, dmi_results.keymap_size) + ) + continue; + + keymap[pos] = *entry; + keymap[pos].code |= (0x0010 << 16); + pos++; + } + + /* Append table with events of type 0x0011 */ + for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0011); i++) { + keymap[pos] = dell_wmi_keymap_type_0011[i]; + keymap[pos].code |= (0x0011 << 16); + pos++; } + + /* + * Now append also table with "legacy" events of type 0x0000. Some of + * them are reported also on laptops which have scancodes in DMI. + */ + for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0000); i++) { + keymap[pos] = dell_wmi_keymap_type_0000[i]; + pos++; + } + + keymap[pos].type = KE_END; + + err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL); + /* + * Sparse keymap library makes a copy of keymap so we don't need the + * original one that was allocated. + */ + kfree(keymap); if (err) goto err_free_dev; diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index ce41bc3..61f39ab 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -88,9 +88,6 @@ #define ACPI_FUJITSU_NOTIFY_CODE1 0x80 -#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86 -#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87 - /* FUNC interface - command values */ #define FUNC_RFKILL 0x1000 #define FUNC_LEDS 0x1001 @@ -108,6 +105,8 @@ #define LOGOLAMP_POWERON 0x2000 #define LOGOLAMP_ALWAYS 0x4000 #define RADIO_LED_ON 0x20 +#define ECO_LED 0x10000 +#define ECO_LED_ON 0x80000 #endif /* Hotkey details */ @@ -121,13 +120,6 @@ #define RINGBUFFERSIZE 40 /* Debugging */ -#define FUJLAPTOP_LOG ACPI_FUJITSU_HID ": " -#define FUJLAPTOP_ERR KERN_ERR FUJLAPTOP_LOG -#define FUJLAPTOP_NOTICE KERN_NOTICE FUJLAPTOP_LOG -#define FUJLAPTOP_INFO KERN_INFO FUJLAPTOP_LOG -#define FUJLAPTOP_DEBUG KERN_DEBUG FUJLAPTOP_LOG - -#define FUJLAPTOP_DBG_ALL 0xffff #define FUJLAPTOP_DBG_ERROR 0x0001 #define FUJLAPTOP_DBG_WARN 0x0002 #define FUJLAPTOP_DBG_INFO 0x0004 @@ -136,7 +128,7 @@ #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG #define vdbg_printk(a_dbg_level, format, arg...) \ do { if (dbg_level & a_dbg_level) \ - printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \ + printk(KERN_DEBUG pr_fmt("%s: " format), __func__, ## arg); \ } while (0) #else #define vdbg_printk(a_dbg_level, format, arg...) \ @@ -176,6 +168,7 @@ struct fujitsu_hotkey_t { int logolamp_registered; int kblamps_registered; int radio_led_registered; + int eco_led_registered; }; static struct fujitsu_hotkey_t *fujitsu_hotkey; @@ -212,6 +205,16 @@ static struct led_classdev radio_led = { .brightness_get = radio_led_get, .brightness_set = radio_led_set }; + +static enum led_brightness eco_led_get(struct led_classdev *cdev); +static void eco_led_set(struct led_classdev *cdev, + enum led_brightness brightness); + +static struct led_classdev eco_led = { + .name = "fujitsu::eco_led", + .brightness_get = eco_led_get, + .brightness_set = eco_led_set +}; #endif #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG @@ -296,6 +299,18 @@ static void radio_led_set(struct led_classdev *cdev, call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, 0x0); } +static void eco_led_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + int curr; + + curr = call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0); + if (brightness >= LED_FULL) + call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr | ECO_LED_ON); + else + call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr & ~ECO_LED_ON); +} + static enum led_brightness logolamp_get(struct led_classdev *cdev) { enum led_brightness brightness = LED_OFF; @@ -330,6 +345,16 @@ static enum led_brightness radio_led_get(struct led_classdev *cdev) return brightness; } + +static enum led_brightness eco_led_get(struct led_classdev *cdev) +{ + enum led_brightness brightness = LED_OFF; + + if (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON) + brightness = LED_FULL; + + return brightness; +} #endif /* Hardware access for LCD brightness control */ @@ -856,6 +881,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) set_bit(fujitsu->keycode3, input->keybit); set_bit(fujitsu->keycode4, input->keybit); set_bit(fujitsu->keycode5, input->keybit); + set_bit(KEY_TOUCHPAD_TOGGLE, input->keybit); set_bit(KEY_UNKNOWN, input->keybit); error = input_register_device(input); @@ -943,6 +969,23 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) result); } } + + /* Support for eco led is not always signaled in bit corresponding + * to the bit used to control the led. According to the DSDT table, + * bit 14 seems to indicate presence of said led as well. + * Confirm by testing the status. + */ + if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) && + (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) { + result = led_classdev_register(&fujitsu->pf_device->dev, + &eco_led); + if (result == 0) { + fujitsu_hotkey->eco_led_registered = 1; + } else { + pr_err("Could not register LED handler for eco LED, error %i\n", + result); + } + } #endif return result; @@ -972,6 +1015,9 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device) if (fujitsu_hotkey->radio_led_registered) led_classdev_unregister(&radio_led); + + if (fujitsu_hotkey->eco_led_registered) + led_classdev_unregister(&eco_led); #endif input_unregister_device(input); @@ -1060,6 +1106,19 @@ static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event) } } + /* On some models (first seen on the Skylake-based Lifebook + * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is + * handled in software; its state is queried using FUNC_RFKILL + */ + if ((fujitsu_hotkey->rfkill_supported & BIT(26)) && + (call_fext_func(FUNC_RFKILL, 0x1, 0x0, 0x0) & BIT(26))) { + keycode = KEY_TOUCHPAD_TOGGLE; + input_report_key(input, keycode, 1); + input_sync(input); + input_report_key(input, keycode, 0); + input_sync(input); + } + break; default: keycode = KEY_UNKNOWN; diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 6f145f2..96ffda4 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -718,6 +718,11 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device) if (err) return err; + err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, &wireless, + sizeof(wireless), 0); + if (err) + return err; + if (wireless & 0x1) { wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, RFKILL_TYPE_WLAN, @@ -882,7 +887,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) wwan_rfkill = NULL; rfkill2_count = 0; - if (hp_wmi_bios_2009_later() || hp_wmi_rfkill_setup(device)) + if (hp_wmi_rfkill_setup(device)) hp_wmi_rfkill2_setup(device); err = device_create_file(&device->dev, &dev_attr_display); diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 4a23fbc..d1a091b 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -567,6 +567,7 @@ static void ideapad_sysfs_exit(struct ideapad_private *priv) static const struct key_entry ideapad_keymap[] = { { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } }, { KE_KEY, 7, { KEY_CAMERA } }, + { KE_KEY, 8, { KEY_MICMUTE } }, { KE_KEY, 11, { KEY_F16 } }, { KE_KEY, 13, { KEY_WLAN } }, { KE_KEY, 16, { KEY_PROG1 } }, @@ -809,6 +810,7 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) break; case 13: case 11: + case 8: case 7: case 6: ideapad_input_report(priv, vpc_bit); diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index a818db6..ed58742 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -122,8 +122,8 @@ static int intel_hid_input_setup(struct platform_device *device) return 0; err_free_device: - input_free_device(priv->input_dev); - return ret; + input_free_device(priv->input_dev); + return ret; } static void intel_hid_input_destroy(struct platform_device *device) @@ -224,7 +224,6 @@ 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); - acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); /* * Even if we failed to shut off the event stream, we can still diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c new file mode 100644 index 0000000..146d02f --- /dev/null +++ b/drivers/platform/x86/intel-vbtn.c @@ -0,0 +1,188 @@ +/* + * Intel Virtual Button driver for Windows 8.1+ + * + * Copyright (C) 2016 AceLan Kao <acelan.kao@canonical.com> + * Copyright (C) 2016 Alex Hung <alex.hung@canonical.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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/input/sparse-keymap.h> +#include <linux/acpi.h> +#include <acpi/acpi_bus.h> + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("AceLan Kao"); + +static const struct acpi_device_id intel_vbtn_ids[] = { + {"INT33D6", 0}, + {"", 0}, +}; + +/* In theory, these are HID usages. */ +static const struct key_entry intel_vbtn_keymap[] = { + { KE_IGNORE, 0xC0, { KEY_POWER } }, /* power key press */ + { KE_KEY, 0xC1, { KEY_POWER } }, /* power key release */ + { KE_END }, +}; + +struct intel_vbtn_priv { + struct input_dev *input_dev; +}; + +static int intel_vbtn_input_setup(struct platform_device *device) +{ + struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev); + int ret; + + priv->input_dev = input_allocate_device(); + if (!priv->input_dev) + return -ENOMEM; + + ret = sparse_keymap_setup(priv->input_dev, intel_vbtn_keymap, NULL); + if (ret) + goto err_free_device; + + priv->input_dev->dev.parent = &device->dev; + priv->input_dev->name = "Intel Virtual Button driver"; + priv->input_dev->id.bustype = BUS_HOST; + + ret = input_register_device(priv->input_dev); + if (ret) + goto err_free_device; + + return 0; + +err_free_device: + input_free_device(priv->input_dev); + return ret; +} + +static void intel_vbtn_input_destroy(struct platform_device *device) +{ + struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev); + + input_unregister_device(priv->input_dev); +} + +static void notify_handler(acpi_handle handle, u32 event, void *context) +{ + struct platform_device *device = context; + struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev); + + if (!sparse_keymap_report_event(priv->input_dev, event, 1, true)) + dev_info(&device->dev, "unknown event index 0x%x\n", + event); +} + +static int intel_vbtn_probe(struct platform_device *device) +{ + acpi_handle handle = ACPI_HANDLE(&device->dev); + struct intel_vbtn_priv *priv; + acpi_status status; + int err; + + status = acpi_evaluate_object(handle, "VBDL", NULL, NULL); + if (!ACPI_SUCCESS(status)) { + dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n"); + return -ENODEV; + } + + priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + dev_set_drvdata(&device->dev, priv); + + err = intel_vbtn_input_setup(device); + if (err) { + pr_err("Failed to setup Intel Virtual Button\n"); + return err; + } + + status = acpi_install_notify_handler(handle, + ACPI_DEVICE_NOTIFY, + notify_handler, + device); + if (ACPI_FAILURE(status)) { + err = -EBUSY; + goto err_remove_input; + } + + return 0; + +err_remove_input: + intel_vbtn_input_destroy(device); + + return err; +} + +static int intel_vbtn_remove(struct platform_device *device) +{ + acpi_handle handle = ACPI_HANDLE(&device->dev); + + intel_vbtn_input_destroy(device); + acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); + + /* + * Even if we failed to shut off the event stream, we can still + * safely detach from the device. + */ + return 0; +} + +static struct platform_driver intel_vbtn_pl_driver = { + .driver = { + .name = "intel-vbtn", + .acpi_match_table = intel_vbtn_ids, + }, + .probe = intel_vbtn_probe, + .remove = intel_vbtn_remove, +}; +MODULE_DEVICE_TABLE(acpi, intel_vbtn_ids); + +static acpi_status __init +check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + const struct acpi_device_id *ids = context; + struct acpi_device *dev; + + if (acpi_bus_get_device(handle, &dev) != 0) + return AE_OK; + + if (acpi_match_device_ids(dev, ids) == 0) + if (acpi_create_platform_device(dev)) + dev_info(&dev->dev, + "intel-vbtn: created platform device\n"); + + return AE_OK; +} + +static int __init intel_vbtn_init(void) +{ + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, check_acpi_dev, NULL, + (void *)intel_vbtn_ids, NULL); + + return platform_driver_register(&intel_vbtn_pl_driver); +} +module_init(intel_vbtn_init); + +static void __exit intel_vbtn_exit(void) +{ + platform_driver_unregister(&intel_vbtn_pl_driver); +} +module_exit(intel_vbtn_exit); diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c index 2776bec..520b58a 100644 --- a/drivers/platform/x86/intel_pmc_core.c +++ b/drivers/platform/x86/intel_pmc_core.c @@ -23,9 +23,9 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/pci.h> -#include <linux/seq_file.h> #include <asm/cpu_device_id.h> +#include <asm/intel-family.h> #include <asm/pmc_core.h> #include "intel_pmc_core.h" @@ -77,30 +77,18 @@ int intel_pmc_slp_s0_counter_read(u32 *data) } EXPORT_SYMBOL_GPL(intel_pmc_slp_s0_counter_read); -#if IS_ENABLED(CONFIG_DEBUG_FS) -static int pmc_core_dev_state_show(struct seq_file *s, void *unused) +static int pmc_core_dev_state_get(void *data, u64 *val) { - struct pmc_dev *pmcdev = s->private; - u32 counter_val; + struct pmc_dev *pmcdev = data; + u32 value; - counter_val = pmc_core_reg_read(pmcdev, - SPT_PMC_SLP_S0_RES_COUNTER_OFFSET); - seq_printf(s, "%u\n", pmc_core_adjust_slp_s0_step(counter_val)); + value = pmc_core_reg_read(pmcdev, SPT_PMC_SLP_S0_RES_COUNTER_OFFSET); + *val = pmc_core_adjust_slp_s0_step(value); return 0; } -static int pmc_core_dev_state_open(struct inode *inode, struct file *file) -{ - return single_open(file, pmc_core_dev_state_show, inode->i_private); -} - -static const struct file_operations pmc_core_dev_state_ops = { - .open = pmc_core_dev_state_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_DEBUGFS_ATTRIBUTE(pmc_core_dev_state, pmc_core_dev_state_get, NULL, "%llu\n"); static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev) { @@ -112,12 +100,12 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev) struct dentry *dir, *file; dir = debugfs_create_dir("pmc_core", NULL); - if (!dir) + if (IS_ERR_OR_NULL(dir)) return -ENOMEM; pmcdev->dbgfs_dir = dir; file = debugfs_create_file("slp_s0_residency_usec", S_IFREG | S_IRUGO, - dir, pmcdev, &pmc_core_dev_state_ops); + dir, pmcdev, &pmc_core_dev_state); if (!file) { pmc_core_dbgfs_unregister(pmcdev); @@ -126,22 +114,12 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev) return 0; } -#else -static inline int pmc_core_dbgfs_register(struct pmc_dev *pmcdev) -{ - return 0; -} - -static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev) -{ -} -#endif /* CONFIG_DEBUG_FS */ static const struct x86_cpu_id intel_pmc_core_ids[] = { - { X86_VENDOR_INTEL, 6, 0x4e, X86_FEATURE_MWAIT, - (kernel_ulong_t)NULL}, /* Skylake CPUID Signature */ - { X86_VENDOR_INTEL, 6, 0x5e, X86_FEATURE_MWAIT, - (kernel_ulong_t)NULL}, /* Skylake CPUID Signature */ + { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_MOBILE, X86_FEATURE_MWAIT, + (kernel_ulong_t)NULL}, + { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_DESKTOP, X86_FEATURE_MWAIT, + (kernel_ulong_t)NULL}, {} }; @@ -182,10 +160,8 @@ static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id) } err = pmc_core_dbgfs_register(pmcdev); - if (err < 0) { - dev_err(&dev->dev, "PMC Core: debugfs register failed.\n"); - return err; - } + if (err < 0) + dev_warn(&dev->dev, "PMC Core: debugfs register failed.\n"); pmc.has_slp_s0_res = true; return 0; diff --git a/drivers/platform/x86/intel_pmc_core.h b/drivers/platform/x86/intel_pmc_core.h index a9dadaf..e3f671f 100644 --- a/drivers/platform/x86/intel_pmc_core.h +++ b/drivers/platform/x86/intel_pmc_core.h @@ -23,6 +23,7 @@ /* Sunrise Point Power Management Controller PCI Device ID */ #define SPT_PMC_PCI_DEVICE_ID 0x9d21 + #define SPT_PMC_BASE_ADDR_OFFSET 0x48 #define SPT_PMC_SLP_S0_RES_COUNTER_OFFSET 0x13c #define SPT_PMC_MMIO_REG_LEN 0x100 @@ -42,9 +43,7 @@ struct pmc_dev { u32 base_addr; void __iomem *regbase; -#if IS_ENABLED(CONFIG_DEBUG_FS) struct dentry *dbgfs_dir; -#endif /* CONFIG_DEBUG_FS */ bool has_slp_s0_res; }; diff --git a/drivers/platform/x86/intel_telemetry_debugfs.c b/drivers/platform/x86/intel_telemetry_debugfs.c index f5134ac..ef29f18 100644 --- a/drivers/platform/x86/intel_telemetry_debugfs.c +++ b/drivers/platform/x86/intel_telemetry_debugfs.c @@ -32,6 +32,7 @@ #include <linux/suspend.h> #include <asm/cpu_device_id.h> +#include <asm/intel-family.h> #include <asm/intel_pmc_ipc.h> #include <asm/intel_punit_ipc.h> #include <asm/intel_telemetry.h> @@ -78,7 +79,7 @@ #define TELEM_EVT_LEN(x) (sizeof(x)/sizeof((x)[0])) #define TELEM_DEBUGFS_CPU(model, data) \ - { X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&data} + { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data} #define TELEM_CHECK_AND_PARSE_EVTS(EVTID, EVTNUM, BUF, EVTLOG, EVTDAT, MASK) { \ if (evtlog[index].telem_evtid == (EVTID)) { \ @@ -331,7 +332,7 @@ static struct telemetry_debugfs_conf telem_apl_debugfs_conf = { }; static const struct x86_cpu_id telemetry_debugfs_cpu_ids[] = { - TELEM_DEBUGFS_CPU(0x5c, telem_apl_debugfs_conf), + TELEM_DEBUGFS_CPU(INTEL_FAM6_ATOM_GOLDMONT, telem_apl_debugfs_conf), {} }; diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c index 09c84a2..6ebdbd2 100644 --- a/drivers/platform/x86/intel_telemetry_pltdrv.c +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c @@ -28,6 +28,7 @@ #include <linux/platform_device.h> #include <asm/cpu_device_id.h> +#include <asm/intel-family.h> #include <asm/intel_pmc_ipc.h> #include <asm/intel_punit_ipc.h> #include <asm/intel_telemetry.h> @@ -82,7 +83,7 @@ #define TELEM_SET_VERBOSITY_BITS(x, y) ((x) |= ((y) << 27)) #define TELEM_CPU(model, data) \ - { X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&data } + { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data } enum telemetry_action { TELEM_UPDATE = 0, @@ -163,7 +164,7 @@ static struct telemetry_plt_config telem_apl_config = { }; static const struct x86_cpu_id telemetry_cpu_ids[] = { - TELEM_CPU(0x5c, telem_apl_config), + TELEM_CPU(INTEL_FAM6_ATOM_GOLDMONT, telem_apl_config), {} }; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index c3bfa1fe..b65ce75 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -2043,6 +2043,7 @@ static int hotkey_autosleep_ack; static u32 hotkey_orig_mask; /* events the BIOS had enabled */ static u32 hotkey_all_mask; /* all events supported in fw */ +static u32 hotkey_adaptive_all_mask; /* all adaptive events supported in fw */ static u32 hotkey_reserved_mask; /* events better left disabled */ static u32 hotkey_driver_mask; /* events needed by the driver */ static u32 hotkey_user_mask; /* events visible to userspace */ @@ -2742,6 +2743,17 @@ static ssize_t hotkey_all_mask_show(struct device *dev, static DEVICE_ATTR_RO(hotkey_all_mask); +/* sysfs hotkey all_mask ----------------------------------------------- */ +static ssize_t hotkey_adaptive_all_mask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%08x\n", + hotkey_adaptive_all_mask | hotkey_source_mask); +} + +static DEVICE_ATTR_RO(hotkey_adaptive_all_mask); + /* sysfs hotkey recommended_mask --------------------------------------- */ static ssize_t hotkey_recommended_mask_show(struct device *dev, struct device_attribute *attr, @@ -2985,6 +2997,7 @@ static struct attribute *hotkey_attributes[] __initdata = { &dev_attr_wakeup_hotunplug_complete.attr, &dev_attr_hotkey_mask.attr, &dev_attr_hotkey_all_mask.attr, + &dev_attr_hotkey_adaptive_all_mask.attr, &dev_attr_hotkey_recommended_mask.attr, #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL &dev_attr_hotkey_source_mask.attr, @@ -3321,20 +3334,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) if (!tp_features.hotkey) return 1; - /* - * Check if we have an adaptive keyboard, like on the - * Lenovo Carbon X1 2014 (2nd Gen). - */ - if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { - if ((hkeyv >> 8) == 2) { - tp_features.has_adaptive_kbd = true; - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, - &adaptive_kbd_attr_group); - if (res) - goto err_exit; - } - } - quirks = tpacpi_check_quirks(tpacpi_hotkey_qtable, ARRAY_SIZE(tpacpi_hotkey_qtable)); @@ -3357,30 +3356,70 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking for HKEY interface version 0x100 */ if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { - if ((hkeyv >> 8) != 1) { - pr_err("unknown version of the HKEY interface: 0x%x\n", - hkeyv); - pr_err("please report this to %s\n", TPACPI_MAIL); - } else { + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, + "firmware HKEY interface version: 0x%x\n", + hkeyv); + + switch (hkeyv >> 8) { + case 1: /* * MHKV 0x100 in A31, R40, R40e, * T4x, X31, and later */ - vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, - "firmware HKEY interface version: 0x%x\n", - hkeyv); /* Paranoia check AND init hotkey_all_mask */ if (!acpi_evalf(hkey_handle, &hotkey_all_mask, "MHKA", "qd")) { - pr_err("missing MHKA handler, " - "please report this to %s\n", + pr_err("missing MHKA handler, please report this to %s\n", TPACPI_MAIL); /* Fallback: pre-init for FN+F3,F4,F12 */ hotkey_all_mask = 0x080cU; } else { tp_features.hotkey_mask = 1; } + break; + + case 2: + /* + * MHKV 0x200 in X1, T460s, X260, T560, X1 Tablet (2016) + */ + + /* Paranoia check AND init hotkey_all_mask */ + if (!acpi_evalf(hkey_handle, &hotkey_all_mask, + "MHKA", "dd", 1)) { + pr_err("missing MHKA handler, please report this to %s\n", + TPACPI_MAIL); + /* Fallback: pre-init for FN+F3,F4,F12 */ + hotkey_all_mask = 0x080cU; + } else { + tp_features.hotkey_mask = 1; + } + + /* + * Check if we have an adaptive keyboard, like on the + * Lenovo Carbon X1 2014 (2nd Gen). + */ + if (acpi_evalf(hkey_handle, &hotkey_adaptive_all_mask, + "MHKA", "dd", 2)) { + if (hotkey_adaptive_all_mask != 0) { + tp_features.has_adaptive_kbd = true; + res = sysfs_create_group( + &tpacpi_pdev->dev.kobj, + &adaptive_kbd_attr_group); + if (res) + goto err_exit; + } + } else { + tp_features.has_adaptive_kbd = false; + hotkey_adaptive_all_mask = 0x0U; + } + break; + + default: + pr_err("unknown version of the HKEY interface: 0x%x\n", + hkeyv); + pr_err("please report this to %s\n", TPACPI_MAIL); + break; } } diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 01e12d2..9d60a40 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -4,7 +4,7 @@ * Copyright (C) 2002-2004 John Belmonte * Copyright (C) 2008 Philip Langdale * Copyright (C) 2010 Pierre Ducroquet - * Copyright (C) 2014-2015 Azael Avalos + * Copyright (C) 2014-2016 Azael Avalos * * 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 @@ -31,7 +31,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define TOSHIBA_ACPI_VERSION "0.23" +#define TOSHIBA_ACPI_VERSION "0.24" #define PROC_INTERFACE_VERSION 1 #include <linux/kernel.h> @@ -53,6 +53,7 @@ #include <linux/uaccess.h> #include <linux/miscdevice.h> #include <linux/rfkill.h> +#include <linux/iio/iio.h> #include <linux/toshiba.h> #include <acpi/video.h> @@ -134,6 +135,7 @@ MODULE_LICENSE("GPL"); /* Field definitions */ #define HCI_ACCEL_MASK 0x7fff +#define HCI_ACCEL_DIRECTION_MASK 0x8000 #define HCI_HOTKEY_DISABLE 0x0b #define HCI_HOTKEY_ENABLE 0x09 #define HCI_HOTKEY_SPECIAL_FUNCTIONS 0x10 @@ -178,6 +180,7 @@ struct toshiba_acpi_dev { struct led_classdev eco_led; struct miscdevice miscdev; struct rfkill *wwan_rfk; + struct iio_dev *indio_dev; int force_fan; int last_key_event; @@ -1958,28 +1961,6 @@ static ssize_t touchpad_show(struct device *dev, } static DEVICE_ATTR_RW(touchpad); -static ssize_t position_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); - u32 xyval, zval, tmp; - u16 x, y, z; - int ret; - - xyval = zval = 0; - ret = toshiba_accelerometer_get(toshiba, &xyval, &zval); - if (ret < 0) - return ret; - - x = xyval & HCI_ACCEL_MASK; - tmp = xyval >> HCI_MISC_SHIFT; - y = tmp & HCI_ACCEL_MASK; - z = zval & HCI_ACCEL_MASK; - - return sprintf(buf, "%d %d %d\n", x, y, z); -} -static DEVICE_ATTR_RO(position); - static ssize_t usb_sleep_charge_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -2350,7 +2331,6 @@ static struct attribute *toshiba_attributes[] = { &dev_attr_available_kbd_modes.attr, &dev_attr_kbd_backlight_timeout.attr, &dev_attr_touchpad.attr, - &dev_attr_position.attr, &dev_attr_usb_sleep_charge.attr, &dev_attr_sleep_functions_on_battery.attr, &dev_attr_usb_rapid_charge.attr, @@ -2377,8 +2357,6 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj, exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false; else if (attr == &dev_attr_touchpad.attr) exists = (drv->touchpad_supported) ? true : false; - else if (attr == &dev_attr_position.attr) - exists = (drv->accelerometer_supported) ? true : false; else if (attr == &dev_attr_usb_sleep_charge.attr) exists = (drv->usb_sleep_charge_supported) ? true : false; else if (attr == &dev_attr_sleep_functions_on_battery.attr) @@ -2420,6 +2398,81 @@ static void toshiba_acpi_kbd_bl_work(struct work_struct *work) } /* + * IIO device + */ + +enum toshiba_iio_accel_chan { + AXIS_X, + AXIS_Y, + AXIS_Z +}; + +static int toshiba_iio_accel_get_axis(enum toshiba_iio_accel_chan chan) +{ + u32 xyval, zval; + int ret; + + ret = toshiba_accelerometer_get(toshiba_acpi, &xyval, &zval); + if (ret < 0) + return ret; + + switch (chan) { + case AXIS_X: + return xyval & HCI_ACCEL_DIRECTION_MASK ? + -(xyval & HCI_ACCEL_MASK) : xyval & HCI_ACCEL_MASK; + case AXIS_Y: + return (xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_DIRECTION_MASK ? + -((xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_MASK) : + (xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_MASK; + case AXIS_Z: + return zval & HCI_ACCEL_DIRECTION_MASK ? + -(zval & HCI_ACCEL_MASK) : zval & HCI_ACCEL_MASK; + } + + return ret; +} + +static int toshiba_iio_accel_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = toshiba_iio_accel_get_axis(chan->channel); + if (ret == -EIO || ret == -ENODEV) + return ret; + + *val = ret; + + return IIO_VAL_INT; + } + + return -EINVAL; +} + +#define TOSHIBA_IIO_ACCEL_CHANNEL(axis, chan) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel = chan, \ + .channel2 = IIO_MOD_##axis, \ + .output = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ +} + +static const struct iio_chan_spec toshiba_iio_accel_channels[] = { + TOSHIBA_IIO_ACCEL_CHANNEL(X, AXIS_X), + TOSHIBA_IIO_ACCEL_CHANNEL(Y, AXIS_Y), + TOSHIBA_IIO_ACCEL_CHANNEL(Z, AXIS_Z), +}; + +static const struct iio_info toshiba_iio_accel_info = { + .driver_module = THIS_MODULE, + .read_raw = &toshiba_iio_accel_read_raw, +}; + +/* * Misc device */ static int toshiba_acpi_smm_bridge(SMMRegisters *regs) @@ -2904,6 +2957,11 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) remove_toshiba_proc_entries(dev); + if (dev->accelerometer_supported && dev->indio_dev) { + iio_device_unregister(dev->indio_dev); + iio_device_free(dev->indio_dev); + } + if (dev->sysfs_created) sysfs_remove_group(&dev->acpi_dev->dev.kobj, &toshiba_attr_group); @@ -3051,6 +3109,30 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) dev->touchpad_supported = !ret; toshiba_accelerometer_available(dev); + if (dev->accelerometer_supported) { + dev->indio_dev = iio_device_alloc(sizeof(*dev)); + if (!dev->indio_dev) { + pr_err("Unable to allocate iio device\n"); + goto iio_error; + } + + pr_info("Registering Toshiba accelerometer iio device\n"); + + dev->indio_dev->info = &toshiba_iio_accel_info; + dev->indio_dev->name = "Toshiba accelerometer"; + dev->indio_dev->dev.parent = &acpi_dev->dev; + dev->indio_dev->modes = INDIO_DIRECT_MODE; + dev->indio_dev->channels = toshiba_iio_accel_channels; + dev->indio_dev->num_channels = + ARRAY_SIZE(toshiba_iio_accel_channels); + + ret = iio_device_register(dev->indio_dev); + if (ret < 0) { + pr_err("Unable to register iio device\n"); + iio_device_free(dev->indio_dev); + } + } +iio_error: toshiba_usb_sleep_charge_available(dev); |