diff options
| -rw-r--r-- | MAINTAINERS | 8 | ||||
| -rw-r--r-- | arch/x86/include/asm/intel_pmc_ipc.h | 6 | ||||
| -rw-r--r-- | drivers/platform/x86/Kconfig | 31 | ||||
| -rw-r--r-- | drivers/platform/x86/Makefile | 2 | ||||
| -rw-r--r-- | drivers/platform/x86/acer-wmi.c | 97 | ||||
| -rw-r--r-- | drivers/platform/x86/alienware-wmi.c | 1 | ||||
| -rw-r--r-- | drivers/platform/x86/asus-wireless.c | 60 | ||||
| -rw-r--r-- | drivers/platform/x86/dell-laptop.c | 6 | ||||
| -rw-r--r-- | drivers/platform/x86/fujitsu-laptop.c | 220 | ||||
| -rw-r--r-- | drivers/platform/x86/hp_accel.c | 1 | ||||
| -rw-r--r-- | drivers/platform/x86/intel-hid.c | 96 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_mid_powerbtn.c | 187 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_mid_thermal.c | 2 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_pmc_core.c | 6 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_pmc_ipc.c | 67 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_turbo_max_3.c | 151 | ||||
| -rw-r--r-- | drivers/platform/x86/mlx-platform.c | 84 | ||||
| -rw-r--r-- | drivers/platform/x86/silead_dmi.c | 136 | ||||
| -rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 124 |
19 files changed, 965 insertions, 320 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index dedc66c265c9..b4474a8b7e8a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -11456,6 +11456,14 @@ F: drivers/media/usb/siano/ | |||
| 11456 | F: drivers/media/usb/siano/ | 11456 | F: drivers/media/usb/siano/ |
| 11457 | F: drivers/media/mmc/siano/ | 11457 | F: drivers/media/mmc/siano/ |
| 11458 | 11458 | ||
| 11459 | SILEAD TOUCHSCREEN DRIVER | ||
| 11460 | M: Hans de Goede <hdegoede@redhat.com> | ||
| 11461 | L: linux-input@vger.kernel.org | ||
| 11462 | L: platform-driver-x86@vger.kernel.org | ||
| 11463 | S: Maintained | ||
| 11464 | F: drivers/input/touchscreen/silead.c | ||
| 11465 | F: drivers/platform/x86/silead_dmi.c | ||
| 11466 | |||
| 11459 | SIMPLEFB FB DRIVER | 11467 | SIMPLEFB FB DRIVER |
| 11460 | M: Hans de Goede <hdegoede@redhat.com> | 11468 | M: Hans de Goede <hdegoede@redhat.com> |
| 11461 | L: linux-fbdev@vger.kernel.org | 11469 | L: linux-fbdev@vger.kernel.org |
diff --git a/arch/x86/include/asm/intel_pmc_ipc.h b/arch/x86/include/asm/intel_pmc_ipc.h index cd0310e186f4..4291b6a5ddf7 100644 --- a/arch/x86/include/asm/intel_pmc_ipc.h +++ b/arch/x86/include/asm/intel_pmc_ipc.h | |||
| @@ -30,6 +30,7 @@ int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, | |||
| 30 | u32 *out, u32 outlen, u32 dptr, u32 sptr); | 30 | u32 *out, u32 outlen, u32 dptr, u32 sptr); |
| 31 | int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen, | 31 | int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen, |
| 32 | u32 *out, u32 outlen); | 32 | u32 *out, u32 outlen); |
| 33 | int intel_pmc_s0ix_counter_read(u64 *data); | ||
| 33 | 34 | ||
| 34 | #else | 35 | #else |
| 35 | 36 | ||
| @@ -50,6 +51,11 @@ static inline int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen, | |||
| 50 | return -EINVAL; | 51 | return -EINVAL; |
| 51 | } | 52 | } |
| 52 | 53 | ||
| 54 | static inline int intel_pmc_s0ix_counter_read(u64 *data) | ||
| 55 | { | ||
| 56 | return -EINVAL; | ||
| 57 | } | ||
| 58 | |||
| 53 | #endif /*CONFIG_INTEL_PMC_IPC*/ | 59 | #endif /*CONFIG_INTEL_PMC_IPC*/ |
| 54 | 60 | ||
| 55 | #endif | 61 | #endif |
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index a9e9c91cf4d4..4bc88eb52712 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
| @@ -92,9 +92,8 @@ config ASUS_LAPTOP | |||
| 92 | If you have an ACPI-compatible ASUS laptop, say Y or M here. | 92 | If you have an ACPI-compatible ASUS laptop, say Y or M here. |
| 93 | 93 | ||
| 94 | config DELL_SMBIOS | 94 | config DELL_SMBIOS |
| 95 | tristate "Dell SMBIOS Support" | 95 | tristate |
| 96 | depends on DCDBAS | 96 | select DCDBAS |
| 97 | default n | ||
| 98 | ---help--- | 97 | ---help--- |
| 99 | This module provides common functions for kernel modules using | 98 | This module provides common functions for kernel modules using |
| 100 | Dell SMBIOS. | 99 | Dell SMBIOS. |
| @@ -103,16 +102,15 @@ config DELL_SMBIOS | |||
| 103 | 102 | ||
| 104 | config DELL_LAPTOP | 103 | config DELL_LAPTOP |
| 105 | tristate "Dell Laptop Extras" | 104 | tristate "Dell Laptop Extras" |
| 106 | depends on DELL_SMBIOS | ||
| 107 | depends on DMI | 105 | depends on DMI |
| 108 | depends on BACKLIGHT_CLASS_DEVICE | 106 | depends on BACKLIGHT_CLASS_DEVICE |
| 109 | depends on ACPI_VIDEO || ACPI_VIDEO = n | 107 | depends on ACPI_VIDEO || ACPI_VIDEO = n |
| 110 | depends on RFKILL || RFKILL = n | 108 | depends on RFKILL || RFKILL = n |
| 111 | depends on SERIO_I8042 | 109 | depends on SERIO_I8042 |
| 110 | select DELL_SMBIOS | ||
| 112 | select POWER_SUPPLY | 111 | select POWER_SUPPLY |
| 113 | select LEDS_CLASS | 112 | select LEDS_CLASS |
| 114 | select NEW_LEDS | 113 | select NEW_LEDS |
| 115 | default n | ||
| 116 | ---help--- | 114 | ---help--- |
| 117 | This driver adds support for rfkill and backlight control to Dell | 115 | This driver adds support for rfkill and backlight control to Dell |
| 118 | laptops (except for some models covered by the Compal driver). | 116 | laptops (except for some models covered by the Compal driver). |
| @@ -123,7 +121,7 @@ config DELL_WMI | |||
| 123 | depends on DMI | 121 | depends on DMI |
| 124 | depends on INPUT | 122 | depends on INPUT |
| 125 | depends on ACPI_VIDEO || ACPI_VIDEO = n | 123 | depends on ACPI_VIDEO || ACPI_VIDEO = n |
| 126 | depends on DELL_SMBIOS | 124 | select DELL_SMBIOS |
| 127 | select INPUT_SPARSEKMAP | 125 | select INPUT_SPARSEKMAP |
| 128 | ---help--- | 126 | ---help--- |
| 129 | Say Y here if you want to support WMI-based hotkeys on Dell laptops. | 127 | Say Y here if you want to support WMI-based hotkeys on Dell laptops. |
| @@ -1069,6 +1067,27 @@ config MLX_CPLD_PLATFORM | |||
| 1069 | This driver handles hot-plug events for the power suppliers, power | 1067 | This driver handles hot-plug events for the power suppliers, power |
| 1070 | cables and fans on the wide range Mellanox IB and Ethernet systems. | 1068 | cables and fans on the wide range Mellanox IB and Ethernet systems. |
| 1071 | 1069 | ||
| 1070 | config INTEL_TURBO_MAX_3 | ||
| 1071 | bool "Intel Turbo Boost Max Technology 3.0 enumeration driver" | ||
| 1072 | depends on X86_64 && SCHED_MC_PRIO | ||
| 1073 | ---help--- | ||
| 1074 | This driver reads maximum performance ratio of each CPU and set up | ||
| 1075 | the scheduler priority metrics. In this way scheduler can prefer | ||
| 1076 | CPU with higher performance to schedule tasks. | ||
| 1077 | This driver is only required when the system is not using Hardware | ||
| 1078 | P-States (HWP). In HWP mode, priority can be read from ACPI tables. | ||
| 1079 | |||
| 1080 | config SILEAD_DMI | ||
| 1081 | bool "Tablets with Silead touchscreens" | ||
| 1082 | depends on ACPI && DMI && I2C=y && INPUT | ||
| 1083 | ---help--- | ||
| 1084 | Certain ACPI based tablets with Silead touchscreens do not have | ||
| 1085 | enough data in ACPI tables for the touchscreen driver to handle | ||
| 1086 | the touchscreen properly, as OEMs expected the data to be baked | ||
| 1087 | into the tablet model specific version of the driver shipped | ||
| 1088 | with the OS-image for the device. This option supplies the missing | ||
| 1089 | information. Enable this for x86 tablets with Silead touchscreens. | ||
| 1090 | |||
| 1072 | endif # X86_PLATFORM_DEVICES | 1091 | endif # X86_PLATFORM_DEVICES |
| 1073 | 1092 | ||
| 1074 | config PMC_ATOM | 1093 | config PMC_ATOM |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index cf9fc3e930c7..299d0f9e40f7 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
| @@ -65,6 +65,7 @@ obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o | |||
| 65 | obj-$(CONFIG_PVPANIC) += pvpanic.o | 65 | obj-$(CONFIG_PVPANIC) += pvpanic.o |
| 66 | obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o | 66 | obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o |
| 67 | obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o | 67 | obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o |
| 68 | obj-$(CONFIG_SILEAD_DMI) += silead_dmi.o | ||
| 68 | obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o | 69 | obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o |
| 69 | obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o | 70 | obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o |
| 70 | obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o | 71 | obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o |
| @@ -76,3 +77,4 @@ obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o | |||
| 76 | obj-$(CONFIG_PMC_ATOM) += pmc_atom.o | 77 | obj-$(CONFIG_PMC_ATOM) += pmc_atom.o |
| 77 | obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o | 78 | obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o |
| 78 | obj-$(CONFIG_MLX_CPLD_PLATFORM) += mlxcpld-hotplug.o | 79 | obj-$(CONFIG_MLX_CPLD_PLATFORM) += mlxcpld-hotplug.o |
| 80 | obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o | ||
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index a66192f692e3..dac0fbe87460 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c | |||
| @@ -128,6 +128,7 @@ static const struct key_entry acer_wmi_keymap[] __initconst = { | |||
| 128 | {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} }, | 128 | {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} }, |
| 129 | {KE_IGNORE, 0x83, {KEY_TOUCHPAD_TOGGLE} }, | 129 | {KE_IGNORE, 0x83, {KEY_TOUCHPAD_TOGGLE} }, |
| 130 | {KE_KEY, 0x85, {KEY_TOUCHPAD_TOGGLE} }, | 130 | {KE_KEY, 0x85, {KEY_TOUCHPAD_TOGGLE} }, |
| 131 | {KE_KEY, 0x86, {KEY_WLAN} }, | ||
| 131 | {KE_END, 0} | 132 | {KE_END, 0} |
| 132 | }; | 133 | }; |
| 133 | 134 | ||
| @@ -150,15 +151,30 @@ struct event_return_value { | |||
| 150 | #define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */ | 151 | #define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */ |
| 151 | #define ACER_WMID3_GDS_TOUCHPAD (1<<1) /* Touchpad */ | 152 | #define ACER_WMID3_GDS_TOUCHPAD (1<<1) /* Touchpad */ |
| 152 | 153 | ||
| 153 | struct lm_input_params { | 154 | /* Hotkey Customized Setting and Acer Application Status. |
| 155 | * Set Device Default Value and Report Acer Application Status. | ||
| 156 | * When Acer Application starts, it will run this method to inform | ||
| 157 | * BIOS/EC that Acer Application is on. | ||
| 158 | * App Status | ||
| 159 | * Bit[0]: Launch Manager Status | ||
| 160 | * Bit[1]: ePM Status | ||
| 161 | * Bit[2]: Device Control Status | ||
| 162 | * Bit[3]: Acer Power Button Utility Status | ||
| 163 | * Bit[4]: RF Button Status | ||
| 164 | * Bit[5]: ODD PM Status | ||
| 165 | * Bit[6]: Device Default Value Control | ||
| 166 | * Bit[7]: Hall Sensor Application Status | ||
| 167 | */ | ||
| 168 | struct func_input_params { | ||
| 154 | u8 function_num; /* Function Number */ | 169 | u8 function_num; /* Function Number */ |
| 155 | u16 commun_devices; /* Communication type devices default status */ | 170 | u16 commun_devices; /* Communication type devices default status */ |
| 156 | u16 devices; /* Other type devices default status */ | 171 | u16 devices; /* Other type devices default status */ |
| 157 | u8 lm_status; /* Launch Manager Status */ | 172 | u8 app_status; /* Acer Device Status. LM, ePM, RF Button... */ |
| 158 | u16 reserved; | 173 | u8 app_mask; /* Bit mask to app_status */ |
| 174 | u8 reserved; | ||
| 159 | } __attribute__((packed)); | 175 | } __attribute__((packed)); |
| 160 | 176 | ||
| 161 | struct lm_return_value { | 177 | struct func_return_value { |
| 162 | u8 error_code; /* Error Code */ | 178 | u8 error_code; /* Error Code */ |
| 163 | u8 ec_return_value; /* EC Return Value */ | 179 | u8 ec_return_value; /* EC Return Value */ |
| 164 | u16 reserved; | 180 | u16 reserved; |
| @@ -1769,13 +1785,13 @@ static void acer_wmi_notify(u32 value, void *context) | |||
| 1769 | } | 1785 | } |
| 1770 | 1786 | ||
| 1771 | static acpi_status __init | 1787 | static acpi_status __init |
| 1772 | wmid3_set_lm_mode(struct lm_input_params *params, | 1788 | wmid3_set_function_mode(struct func_input_params *params, |
| 1773 | struct lm_return_value *return_value) | 1789 | struct func_return_value *return_value) |
| 1774 | { | 1790 | { |
| 1775 | acpi_status status; | 1791 | acpi_status status; |
| 1776 | union acpi_object *obj; | 1792 | union acpi_object *obj; |
| 1777 | 1793 | ||
| 1778 | struct acpi_buffer input = { sizeof(struct lm_input_params), params }; | 1794 | struct acpi_buffer input = { sizeof(struct func_input_params), params }; |
| 1779 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | 1795 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
| 1780 | 1796 | ||
| 1781 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output); | 1797 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output); |
| @@ -1796,7 +1812,7 @@ wmid3_set_lm_mode(struct lm_input_params *params, | |||
| 1796 | return AE_ERROR; | 1812 | return AE_ERROR; |
| 1797 | } | 1813 | } |
| 1798 | 1814 | ||
| 1799 | *return_value = *((struct lm_return_value *)obj->buffer.pointer); | 1815 | *return_value = *((struct func_return_value *)obj->buffer.pointer); |
| 1800 | kfree(obj); | 1816 | kfree(obj); |
| 1801 | 1817 | ||
| 1802 | return status; | 1818 | return status; |
| @@ -1804,16 +1820,17 @@ wmid3_set_lm_mode(struct lm_input_params *params, | |||
| 1804 | 1820 | ||
| 1805 | static int __init acer_wmi_enable_ec_raw(void) | 1821 | static int __init acer_wmi_enable_ec_raw(void) |
| 1806 | { | 1822 | { |
| 1807 | struct lm_return_value return_value; | 1823 | struct func_return_value return_value; |
| 1808 | acpi_status status; | 1824 | acpi_status status; |
| 1809 | struct lm_input_params params = { | 1825 | struct func_input_params params = { |
| 1810 | .function_num = 0x1, | 1826 | .function_num = 0x1, |
| 1811 | .commun_devices = 0xFFFF, | 1827 | .commun_devices = 0xFFFF, |
| 1812 | .devices = 0xFFFF, | 1828 | .devices = 0xFFFF, |
| 1813 | .lm_status = 0x00, /* Launch Manager Deactive */ | 1829 | .app_status = 0x00, /* Launch Manager Deactive */ |
| 1830 | .app_mask = 0x01, | ||
| 1814 | }; | 1831 | }; |
| 1815 | 1832 | ||
| 1816 | status = wmid3_set_lm_mode(¶ms, &return_value); | 1833 | status = wmid3_set_function_mode(¶ms, &return_value); |
| 1817 | 1834 | ||
| 1818 | if (return_value.error_code || return_value.ec_return_value) | 1835 | if (return_value.error_code || return_value.ec_return_value) |
| 1819 | pr_warn("Enabling EC raw mode failed: 0x%x - 0x%x\n", | 1836 | 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) | |||
| 1827 | 1844 | ||
| 1828 | static int __init acer_wmi_enable_lm(void) | 1845 | static int __init acer_wmi_enable_lm(void) |
| 1829 | { | 1846 | { |
| 1830 | struct lm_return_value return_value; | 1847 | struct func_return_value return_value; |
| 1831 | acpi_status status; | 1848 | acpi_status status; |
| 1832 | struct lm_input_params params = { | 1849 | struct func_input_params params = { |
| 1833 | .function_num = 0x1, | 1850 | .function_num = 0x1, |
| 1834 | .commun_devices = 0xFFFF, | 1851 | .commun_devices = 0xFFFF, |
| 1835 | .devices = 0xFFFF, | 1852 | .devices = 0xFFFF, |
| 1836 | .lm_status = 0x01, /* Launch Manager Active */ | 1853 | .app_status = 0x01, /* Launch Manager Active */ |
| 1854 | .app_mask = 0x01, | ||
| 1837 | }; | 1855 | }; |
| 1838 | 1856 | ||
| 1839 | status = wmid3_set_lm_mode(¶ms, &return_value); | 1857 | status = wmid3_set_function_mode(¶ms, &return_value); |
| 1840 | 1858 | ||
| 1841 | if (return_value.error_code || return_value.ec_return_value) | 1859 | if (return_value.error_code || return_value.ec_return_value) |
| 1842 | pr_warn("Enabling Launch Manager failed: 0x%x - 0x%x\n", | 1860 | pr_warn("Enabling Launch Manager failed: 0x%x - 0x%x\n", |
| @@ -1846,11 +1864,46 @@ static int __init acer_wmi_enable_lm(void) | |||
| 1846 | return status; | 1864 | return status; |
| 1847 | } | 1865 | } |
| 1848 | 1866 | ||
| 1867 | static int __init acer_wmi_enable_rf_button(void) | ||
| 1868 | { | ||
| 1869 | struct func_return_value return_value; | ||
| 1870 | acpi_status status; | ||
| 1871 | struct func_input_params params = { | ||
| 1872 | .function_num = 0x1, | ||
| 1873 | .commun_devices = 0xFFFF, | ||
| 1874 | .devices = 0xFFFF, | ||
| 1875 | .app_status = 0x10, /* RF Button Active */ | ||
| 1876 | .app_mask = 0x10, | ||
| 1877 | }; | ||
| 1878 | |||
| 1879 | status = wmid3_set_function_mode(¶ms, &return_value); | ||
| 1880 | |||
| 1881 | if (return_value.error_code || return_value.ec_return_value) | ||
| 1882 | pr_warn("Enabling RF Button failed: 0x%x - 0x%x\n", | ||
| 1883 | return_value.error_code, | ||
| 1884 | return_value.ec_return_value); | ||
| 1885 | |||
| 1886 | return status; | ||
| 1887 | } | ||
| 1888 | |||
| 1889 | #define ACER_WMID_ACCEL_HID "BST0001" | ||
| 1890 | |||
| 1849 | static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level, | 1891 | static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level, |
| 1850 | void *ctx, void **retval) | 1892 | void *ctx, void **retval) |
| 1851 | { | 1893 | { |
| 1894 | struct acpi_device *dev; | ||
| 1895 | |||
| 1896 | if (!strcmp(ctx, "SENR")) { | ||
| 1897 | if (acpi_bus_get_device(ah, &dev)) | ||
| 1898 | return AE_OK; | ||
| 1899 | if (!strcmp(ACER_WMID_ACCEL_HID, acpi_device_hid(dev))) | ||
| 1900 | return AE_OK; | ||
| 1901 | } else | ||
| 1902 | return AE_OK; | ||
| 1903 | |||
| 1852 | *(acpi_handle *)retval = ah; | 1904 | *(acpi_handle *)retval = ah; |
| 1853 | return AE_OK; | 1905 | |
| 1906 | return AE_CTRL_TERMINATE; | ||
| 1854 | } | 1907 | } |
| 1855 | 1908 | ||
| 1856 | static int __init acer_wmi_get_handle(const char *name, const char *prop, | 1909 | 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) | |||
| 1877 | { | 1930 | { |
| 1878 | int err; | 1931 | int err; |
| 1879 | 1932 | ||
| 1880 | err = acer_wmi_get_handle("SENR", "BST0001", &gsensor_handle); | 1933 | err = acer_wmi_get_handle("SENR", ACER_WMID_ACCEL_HID, &gsensor_handle); |
| 1881 | if (err) | 1934 | if (err) |
| 1882 | return err; | 1935 | return err; |
| 1883 | 1936 | ||
| @@ -2216,6 +2269,9 @@ static int __init acer_wmi_init(void) | |||
| 2216 | interface->capability &= ~ACER_CAP_BRIGHTNESS; | 2269 | interface->capability &= ~ACER_CAP_BRIGHTNESS; |
| 2217 | 2270 | ||
| 2218 | if (wmi_has_guid(WMID_GUID3)) { | 2271 | if (wmi_has_guid(WMID_GUID3)) { |
| 2272 | if (ACPI_FAILURE(acer_wmi_enable_rf_button())) | ||
| 2273 | pr_warn("Cannot enable RF Button Driver\n"); | ||
| 2274 | |||
| 2219 | if (ec_raw_mode) { | 2275 | if (ec_raw_mode) { |
| 2220 | if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) { | 2276 | if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) { |
| 2221 | pr_err("Cannot enable EC raw mode\n"); | 2277 | pr_err("Cannot enable EC raw mode\n"); |
| @@ -2233,10 +2289,11 @@ static int __init acer_wmi_init(void) | |||
| 2233 | err = acer_wmi_input_setup(); | 2289 | err = acer_wmi_input_setup(); |
| 2234 | if (err) | 2290 | if (err) |
| 2235 | return err; | 2291 | return err; |
| 2292 | err = acer_wmi_accel_setup(); | ||
| 2293 | if (err) | ||
| 2294 | return err; | ||
| 2236 | } | 2295 | } |
| 2237 | 2296 | ||
| 2238 | acer_wmi_accel_setup(); | ||
| 2239 | |||
| 2240 | err = platform_driver_register(&acer_platform_driver); | 2297 | err = platform_driver_register(&acer_platform_driver); |
| 2241 | if (err) { | 2298 | if (err) { |
| 2242 | pr_err("Unable to register platform driver\n"); | 2299 | pr_err("Unable to register platform driver\n"); |
diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c index 005629447b0c..d6b34923fb4e 100644 --- a/drivers/platform/x86/alienware-wmi.c +++ b/drivers/platform/x86/alienware-wmi.c | |||
| @@ -21,7 +21,6 @@ | |||
| 21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
| 22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
| 23 | #include <linux/dmi.h> | 23 | #include <linux/dmi.h> |
| 24 | #include <linux/acpi.h> | ||
| 25 | #include <linux/leds.h> | 24 | #include <linux/leds.h> |
| 26 | 25 | ||
| 27 | #define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492" | 26 | #define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492" |
diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c index 9f31bc1a47d0..f3796164329e 100644 --- a/drivers/platform/x86/asus-wireless.c +++ b/drivers/platform/x86/asus-wireless.c | |||
| @@ -17,19 +17,41 @@ | |||
| 17 | #include <linux/pci_ids.h> | 17 | #include <linux/pci_ids.h> |
| 18 | #include <linux/leds.h> | 18 | #include <linux/leds.h> |
| 19 | 19 | ||
| 20 | #define ASUS_WIRELESS_LED_STATUS 0x2 | 20 | struct hswc_params { |
| 21 | #define ASUS_WIRELESS_LED_OFF 0x4 | 21 | u8 on; |
| 22 | #define ASUS_WIRELESS_LED_ON 0x5 | 22 | u8 off; |
| 23 | u8 status; | ||
| 24 | }; | ||
| 23 | 25 | ||
| 24 | struct asus_wireless_data { | 26 | struct asus_wireless_data { |
| 25 | struct input_dev *idev; | 27 | struct input_dev *idev; |
| 26 | struct acpi_device *adev; | 28 | struct acpi_device *adev; |
| 29 | const struct hswc_params *hswc_params; | ||
| 27 | struct workqueue_struct *wq; | 30 | struct workqueue_struct *wq; |
| 28 | struct work_struct led_work; | 31 | struct work_struct led_work; |
| 29 | struct led_classdev led; | 32 | struct led_classdev led; |
| 30 | int led_state; | 33 | int led_state; |
| 31 | }; | 34 | }; |
| 32 | 35 | ||
| 36 | static const struct hswc_params atk4001_id_params = { | ||
| 37 | .on = 0x0, | ||
| 38 | .off = 0x1, | ||
| 39 | .status = 0x2, | ||
| 40 | }; | ||
| 41 | |||
| 42 | static const struct hswc_params atk4002_id_params = { | ||
| 43 | .on = 0x5, | ||
| 44 | .off = 0x4, | ||
| 45 | .status = 0x2, | ||
| 46 | }; | ||
| 47 | |||
| 48 | static const struct acpi_device_id device_ids[] = { | ||
| 49 | {"ATK4001", (kernel_ulong_t)&atk4001_id_params}, | ||
| 50 | {"ATK4002", (kernel_ulong_t)&atk4002_id_params}, | ||
| 51 | {"", 0}, | ||
| 52 | }; | ||
| 53 | MODULE_DEVICE_TABLE(acpi, device_ids); | ||
| 54 | |||
| 33 | static u64 asus_wireless_method(acpi_handle handle, const char *method, | 55 | static u64 asus_wireless_method(acpi_handle handle, const char *method, |
| 34 | int param) | 56 | int param) |
| 35 | { | 57 | { |
| @@ -61,8 +83,8 @@ static enum led_brightness led_state_get(struct led_classdev *led) | |||
| 61 | 83 | ||
| 62 | data = container_of(led, struct asus_wireless_data, led); | 84 | data = container_of(led, struct asus_wireless_data, led); |
| 63 | s = asus_wireless_method(acpi_device_handle(data->adev), "HSWC", | 85 | s = asus_wireless_method(acpi_device_handle(data->adev), "HSWC", |
| 64 | ASUS_WIRELESS_LED_STATUS); | 86 | data->hswc_params->status); |
| 65 | if (s == ASUS_WIRELESS_LED_ON) | 87 | if (s == data->hswc_params->on) |
| 66 | return LED_FULL; | 88 | return LED_FULL; |
| 67 | return LED_OFF; | 89 | return LED_OFF; |
| 68 | } | 90 | } |
| @@ -76,14 +98,13 @@ static void led_state_update(struct work_struct *work) | |||
| 76 | data->led_state); | 98 | data->led_state); |
| 77 | } | 99 | } |
| 78 | 100 | ||
| 79 | static void led_state_set(struct led_classdev *led, | 101 | static void led_state_set(struct led_classdev *led, enum led_brightness value) |
| 80 | enum led_brightness value) | ||
| 81 | { | 102 | { |
| 82 | struct asus_wireless_data *data; | 103 | struct asus_wireless_data *data; |
| 83 | 104 | ||
| 84 | data = container_of(led, struct asus_wireless_data, led); | 105 | data = container_of(led, struct asus_wireless_data, led); |
| 85 | data->led_state = value == LED_OFF ? ASUS_WIRELESS_LED_OFF : | 106 | data->led_state = value == LED_OFF ? data->hswc_params->off : |
| 86 | ASUS_WIRELESS_LED_ON; | 107 | data->hswc_params->on; |
| 87 | queue_work(data->wq, &data->led_work); | 108 | queue_work(data->wq, &data->led_work); |
| 88 | } | 109 | } |
| 89 | 110 | ||
| @@ -104,12 +125,14 @@ static void asus_wireless_notify(struct acpi_device *adev, u32 event) | |||
| 104 | static int asus_wireless_add(struct acpi_device *adev) | 125 | static int asus_wireless_add(struct acpi_device *adev) |
| 105 | { | 126 | { |
| 106 | struct asus_wireless_data *data; | 127 | struct asus_wireless_data *data; |
| 128 | const struct acpi_device_id *id; | ||
| 107 | int err; | 129 | int err; |
| 108 | 130 | ||
| 109 | data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL); | 131 | data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL); |
| 110 | if (!data) | 132 | if (!data) |
| 111 | return -ENOMEM; | 133 | return -ENOMEM; |
| 112 | adev->driver_data = data; | 134 | adev->driver_data = data; |
| 135 | data->adev = adev; | ||
| 113 | 136 | ||
| 114 | data->idev = devm_input_allocate_device(&adev->dev); | 137 | data->idev = devm_input_allocate_device(&adev->dev); |
| 115 | if (!data->idev) | 138 | if (!data->idev) |
| @@ -124,7 +147,16 @@ static int asus_wireless_add(struct acpi_device *adev) | |||
| 124 | if (err) | 147 | if (err) |
| 125 | return err; | 148 | return err; |
| 126 | 149 | ||
| 127 | data->adev = adev; | 150 | for (id = device_ids; id->id[0]; id++) { |
| 151 | if (!strcmp((char *) id->id, acpi_device_hid(adev))) { | ||
| 152 | data->hswc_params = | ||
| 153 | (const struct hswc_params *)id->driver_data; | ||
| 154 | break; | ||
| 155 | } | ||
| 156 | } | ||
| 157 | if (!data->hswc_params) | ||
| 158 | return 0; | ||
| 159 | |||
| 128 | data->wq = create_singlethread_workqueue("asus_wireless_workqueue"); | 160 | data->wq = create_singlethread_workqueue("asus_wireless_workqueue"); |
| 129 | if (!data->wq) | 161 | if (!data->wq) |
| 130 | return -ENOMEM; | 162 | return -ENOMEM; |
| @@ -137,6 +169,7 @@ static int asus_wireless_add(struct acpi_device *adev) | |||
| 137 | err = devm_led_classdev_register(&adev->dev, &data->led); | 169 | err = devm_led_classdev_register(&adev->dev, &data->led); |
| 138 | if (err) | 170 | if (err) |
| 139 | destroy_workqueue(data->wq); | 171 | destroy_workqueue(data->wq); |
| 172 | |||
| 140 | return err; | 173 | return err; |
| 141 | } | 174 | } |
| 142 | 175 | ||
| @@ -149,13 +182,6 @@ static int asus_wireless_remove(struct acpi_device *adev) | |||
| 149 | return 0; | 182 | return 0; |
| 150 | } | 183 | } |
| 151 | 184 | ||
| 152 | static const struct acpi_device_id device_ids[] = { | ||
| 153 | {"ATK4001", 0}, | ||
| 154 | {"ATK4002", 0}, | ||
| 155 | {"", 0}, | ||
| 156 | }; | ||
| 157 | MODULE_DEVICE_TABLE(acpi, device_ids); | ||
| 158 | |||
| 159 | static struct acpi_driver asus_wireless_driver = { | 185 | static struct acpi_driver asus_wireless_driver = { |
| 160 | .name = "Asus Wireless Radio Control Driver", | 186 | .name = "Asus Wireless Radio Control Driver", |
| 161 | .class = "hotkey", | 187 | .class = "hotkey", |
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 14392a01ab36..f57dd282a002 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c | |||
| @@ -106,6 +106,12 @@ static const struct dmi_system_id dell_device_table[] __initconst = { | |||
| 106 | }, | 106 | }, |
| 107 | }, | 107 | }, |
| 108 | { | 108 | { |
| 109 | .matches = { | ||
| 110 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
| 111 | DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/ | ||
| 112 | }, | ||
| 113 | }, | ||
| 114 | { | ||
| 109 | .ident = "Dell Computer Corporation", | 115 | .ident = "Dell Computer Corporation", |
| 110 | .matches = { | 116 | .matches = { |
| 111 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), | 117 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), |
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 82d67715ce76..2b218b1d13e5 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c | |||
| @@ -202,6 +202,7 @@ static int radio_led_set(struct led_classdev *cdev, | |||
| 202 | 202 | ||
| 203 | static struct led_classdev radio_led = { | 203 | static struct led_classdev radio_led = { |
| 204 | .name = "fujitsu::radio_led", | 204 | .name = "fujitsu::radio_led", |
| 205 | .default_trigger = "rfkill-any", | ||
| 205 | .brightness_get = radio_led_get, | 206 | .brightness_get = radio_led_get, |
| 206 | .brightness_set_blocking = radio_led_set | 207 | .brightness_set_blocking = radio_led_set |
| 207 | }; | 208 | }; |
| @@ -270,15 +271,20 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2) | |||
| 270 | static int logolamp_set(struct led_classdev *cdev, | 271 | static int logolamp_set(struct led_classdev *cdev, |
| 271 | enum led_brightness brightness) | 272 | enum led_brightness brightness) |
| 272 | { | 273 | { |
| 273 | if (brightness >= LED_FULL) { | 274 | int poweron = FUNC_LED_ON, always = FUNC_LED_ON; |
| 274 | call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); | 275 | int ret; |
| 275 | return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON); | 276 | |
| 276 | } else if (brightness >= LED_HALF) { | 277 | if (brightness < LED_HALF) |
| 277 | call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); | 278 | poweron = FUNC_LED_OFF; |
| 278 | return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF); | 279 | |
| 279 | } else { | 280 | if (brightness < LED_FULL) |
| 280 | return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF); | 281 | always = FUNC_LED_OFF; |
| 281 | } | 282 | |
| 283 | ret = call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron); | ||
| 284 | if (ret < 0) | ||
| 285 | return ret; | ||
| 286 | |||
| 287 | return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always); | ||
| 282 | } | 288 | } |
| 283 | 289 | ||
| 284 | static int kblamps_set(struct led_classdev *cdev, | 290 | static int kblamps_set(struct led_classdev *cdev, |
| @@ -313,17 +319,17 @@ static int eco_led_set(struct led_classdev *cdev, | |||
| 313 | 319 | ||
| 314 | static enum led_brightness logolamp_get(struct led_classdev *cdev) | 320 | static enum led_brightness logolamp_get(struct led_classdev *cdev) |
| 315 | { | 321 | { |
| 316 | enum led_brightness brightness = LED_OFF; | 322 | int ret; |
| 317 | int poweron, always; | 323 | |
| 318 | 324 | ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); | |
| 319 | poweron = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); | 325 | if (ret == FUNC_LED_ON) |
| 320 | if (poweron == FUNC_LED_ON) { | 326 | return LED_FULL; |
| 321 | brightness = LED_HALF; | 327 | |
| 322 | always = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); | 328 | ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); |
| 323 | if (always == FUNC_LED_ON) | 329 | if (ret == FUNC_LED_ON) |
| 324 | brightness = LED_FULL; | 330 | return LED_HALF; |
| 325 | } | 331 | |
| 326 | return brightness; | 332 | return LED_OFF; |
| 327 | } | 333 | } |
| 328 | 334 | ||
| 329 | static enum led_brightness kblamps_get(struct led_classdev *cdev) | 335 | static enum led_brightness kblamps_get(struct led_classdev *cdev) |
| @@ -1029,107 +1035,117 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device) | |||
| 1029 | return 0; | 1035 | return 0; |
| 1030 | } | 1036 | } |
| 1031 | 1037 | ||
| 1038 | static void acpi_fujitsu_hotkey_press(int keycode) | ||
| 1039 | { | ||
| 1040 | struct input_dev *input = fujitsu_hotkey->input; | ||
| 1041 | int status; | ||
| 1042 | |||
| 1043 | status = kfifo_in_locked(&fujitsu_hotkey->fifo, | ||
| 1044 | (unsigned char *)&keycode, sizeof(keycode), | ||
| 1045 | &fujitsu_hotkey->fifo_lock); | ||
| 1046 | if (status != sizeof(keycode)) { | ||
| 1047 | vdbg_printk(FUJLAPTOP_DBG_WARN, | ||
| 1048 | "Could not push keycode [0x%x]\n", keycode); | ||
| 1049 | return; | ||
| 1050 | } | ||
| 1051 | input_report_key(input, keycode, 1); | ||
| 1052 | input_sync(input); | ||
| 1053 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | ||
| 1054 | "Push keycode into ringbuffer [%d]\n", keycode); | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | static void acpi_fujitsu_hotkey_release(void) | ||
| 1058 | { | ||
| 1059 | struct input_dev *input = fujitsu_hotkey->input; | ||
| 1060 | int keycode, status; | ||
| 1061 | |||
| 1062 | while (true) { | ||
| 1063 | status = kfifo_out_locked(&fujitsu_hotkey->fifo, | ||
| 1064 | (unsigned char *)&keycode, | ||
| 1065 | sizeof(keycode), | ||
| 1066 | &fujitsu_hotkey->fifo_lock); | ||
| 1067 | if (status != sizeof(keycode)) | ||
| 1068 | return; | ||
| 1069 | input_report_key(input, keycode, 0); | ||
| 1070 | input_sync(input); | ||
| 1071 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | ||
| 1072 | "Pop keycode from ringbuffer [%d]\n", keycode); | ||
| 1073 | } | ||
| 1074 | } | ||
| 1075 | |||
| 1032 | static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event) | 1076 | static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event) |
| 1033 | { | 1077 | { |
| 1034 | struct input_dev *input; | 1078 | struct input_dev *input; |
| 1035 | int keycode, keycode_r; | 1079 | int keycode; |
| 1036 | unsigned int irb = 1; | 1080 | unsigned int irb = 1; |
| 1037 | int i, status; | 1081 | int i; |
| 1038 | 1082 | ||
| 1039 | input = fujitsu_hotkey->input; | 1083 | input = fujitsu_hotkey->input; |
| 1040 | 1084 | ||
| 1085 | if (event != ACPI_FUJITSU_NOTIFY_CODE1) { | ||
| 1086 | keycode = KEY_UNKNOWN; | ||
| 1087 | vdbg_printk(FUJLAPTOP_DBG_WARN, | ||
| 1088 | "Unsupported event [0x%x]\n", event); | ||
| 1089 | input_report_key(input, keycode, 1); | ||
| 1090 | input_sync(input); | ||
| 1091 | input_report_key(input, keycode, 0); | ||
| 1092 | input_sync(input); | ||
| 1093 | return; | ||
| 1094 | } | ||
| 1095 | |||
| 1041 | if (fujitsu_hotkey->rfkill_supported) | 1096 | if (fujitsu_hotkey->rfkill_supported) |
| 1042 | fujitsu_hotkey->rfkill_state = | 1097 | fujitsu_hotkey->rfkill_state = |
| 1043 | call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); | 1098 | call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); |
| 1044 | 1099 | ||
| 1045 | switch (event) { | 1100 | i = 0; |
| 1046 | case ACPI_FUJITSU_NOTIFY_CODE1: | 1101 | while ((irb = |
| 1047 | i = 0; | 1102 | call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 |
| 1048 | while ((irb = | 1103 | && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { |
| 1049 | call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 | 1104 | switch (irb & 0x4ff) { |
| 1050 | && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { | 1105 | case KEY1_CODE: |
| 1051 | switch (irb & 0x4ff) { | 1106 | keycode = fujitsu->keycode1; |
| 1052 | case KEY1_CODE: | 1107 | break; |
| 1053 | keycode = fujitsu->keycode1; | 1108 | case KEY2_CODE: |
| 1054 | break; | 1109 | keycode = fujitsu->keycode2; |
| 1055 | case KEY2_CODE: | 1110 | break; |
| 1056 | keycode = fujitsu->keycode2; | 1111 | case KEY3_CODE: |
| 1057 | break; | 1112 | keycode = fujitsu->keycode3; |
| 1058 | case KEY3_CODE: | 1113 | break; |
| 1059 | keycode = fujitsu->keycode3; | 1114 | case KEY4_CODE: |
| 1060 | break; | 1115 | keycode = fujitsu->keycode4; |
| 1061 | case KEY4_CODE: | 1116 | break; |
| 1062 | keycode = fujitsu->keycode4; | 1117 | case KEY5_CODE: |
| 1063 | break; | 1118 | keycode = fujitsu->keycode5; |
| 1064 | case KEY5_CODE: | 1119 | break; |
| 1065 | keycode = fujitsu->keycode5; | 1120 | case 0: |
| 1066 | break; | 1121 | keycode = 0; |
| 1067 | case 0: | 1122 | break; |
| 1068 | keycode = 0; | 1123 | default: |
| 1069 | break; | 1124 | vdbg_printk(FUJLAPTOP_DBG_WARN, |
| 1070 | default: | 1125 | "Unknown GIRB result [%x]\n", irb); |
| 1071 | vdbg_printk(FUJLAPTOP_DBG_WARN, | 1126 | keycode = -1; |
| 1072 | "Unknown GIRB result [%x]\n", irb); | 1127 | break; |
| 1073 | keycode = -1; | ||
| 1074 | break; | ||
| 1075 | } | ||
| 1076 | if (keycode > 0) { | ||
| 1077 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | ||
| 1078 | "Push keycode into ringbuffer [%d]\n", | ||
| 1079 | keycode); | ||
| 1080 | status = kfifo_in_locked(&fujitsu_hotkey->fifo, | ||
| 1081 | (unsigned char *)&keycode, | ||
| 1082 | sizeof(keycode), | ||
| 1083 | &fujitsu_hotkey->fifo_lock); | ||
| 1084 | if (status != sizeof(keycode)) { | ||
| 1085 | vdbg_printk(FUJLAPTOP_DBG_WARN, | ||
| 1086 | "Could not push keycode [0x%x]\n", | ||
| 1087 | keycode); | ||
| 1088 | } else { | ||
| 1089 | input_report_key(input, keycode, 1); | ||
| 1090 | input_sync(input); | ||
| 1091 | } | ||
| 1092 | } else if (keycode == 0) { | ||
| 1093 | while ((status = | ||
| 1094 | kfifo_out_locked( | ||
| 1095 | &fujitsu_hotkey->fifo, | ||
| 1096 | (unsigned char *) &keycode_r, | ||
| 1097 | sizeof(keycode_r), | ||
| 1098 | &fujitsu_hotkey->fifo_lock)) | ||
| 1099 | == sizeof(keycode_r)) { | ||
| 1100 | input_report_key(input, keycode_r, 0); | ||
| 1101 | input_sync(input); | ||
| 1102 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | ||
| 1103 | "Pop keycode from ringbuffer [%d]\n", | ||
| 1104 | keycode_r); | ||
| 1105 | } | ||
| 1106 | } | ||
| 1107 | } | 1128 | } |
| 1108 | 1129 | ||
| 1109 | /* On some models (first seen on the Skylake-based Lifebook | 1130 | if (keycode > 0) |
| 1110 | * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is | 1131 | acpi_fujitsu_hotkey_press(keycode); |
| 1111 | * handled in software; its state is queried using FUNC_RFKILL | 1132 | else if (keycode == 0) |
| 1112 | */ | 1133 | acpi_fujitsu_hotkey_release(); |
| 1113 | if ((fujitsu_hotkey->rfkill_supported & BIT(26)) && | 1134 | } |
| 1114 | (call_fext_func(FUNC_RFKILL, 0x1, 0x0, 0x0) & BIT(26))) { | ||
| 1115 | keycode = KEY_TOUCHPAD_TOGGLE; | ||
| 1116 | input_report_key(input, keycode, 1); | ||
| 1117 | input_sync(input); | ||
| 1118 | input_report_key(input, keycode, 0); | ||
| 1119 | input_sync(input); | ||
| 1120 | } | ||
| 1121 | 1135 | ||
| 1122 | break; | 1136 | /* On some models (first seen on the Skylake-based Lifebook |
| 1123 | default: | 1137 | * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is |
| 1124 | keycode = KEY_UNKNOWN; | 1138 | * handled in software; its state is queried using FUNC_RFKILL |
| 1125 | vdbg_printk(FUJLAPTOP_DBG_WARN, | 1139 | */ |
| 1126 | "Unsupported event [0x%x]\n", event); | 1140 | if ((fujitsu_hotkey->rfkill_supported & BIT(26)) && |
| 1141 | (call_fext_func(FUNC_RFKILL, 0x1, 0x0, 0x0) & BIT(26))) { | ||
| 1142 | keycode = KEY_TOUCHPAD_TOGGLE; | ||
| 1127 | input_report_key(input, keycode, 1); | 1143 | input_report_key(input, keycode, 1); |
| 1128 | input_sync(input); | 1144 | input_sync(input); |
| 1129 | input_report_key(input, keycode, 0); | 1145 | input_report_key(input, keycode, 0); |
| 1130 | input_sync(input); | 1146 | input_sync(input); |
| 1131 | break; | ||
| 1132 | } | 1147 | } |
| 1148 | |||
| 1133 | } | 1149 | } |
| 1134 | 1150 | ||
| 1135 | /* Initialization */ | 1151 | /* Initialization */ |
diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 09356684c32f..493d8910a74e 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c | |||
| @@ -251,6 +251,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = { | |||
| 251 | AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), | 251 | AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), |
| 252 | AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), | 252 | AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), |
| 253 | AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted), | 253 | AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted), |
| 254 | AXIS_DMI_MATCH("HPZBook17", "HP ZBook 17", xy_swap_yz_inverted), | ||
| 254 | { NULL, } | 255 | { NULL, } |
| 255 | /* Laptop models without axis info (yet): | 256 | /* Laptop models without axis info (yet): |
| 256 | * "NC6910" "HP Compaq 6910" | 257 | * "NC6910" "HP Compaq 6910" |
diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index cb3ab2b212b1..bcf438f38781 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Intel HID event driver for Windows 8 | 2 | * Intel HID event & 5 button array driver |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2015 Alex Hung <alex.hung@canonical.com> | 4 | * Copyright (C) 2015 Alex Hung <alex.hung@canonical.com> |
| 5 | * Copyright (C) 2015 Andrew Lutomirski <luto@kernel.org> | 5 | * Copyright (C) 2015 Andrew Lutomirski <luto@kernel.org> |
| @@ -57,8 +57,24 @@ static const struct key_entry intel_hid_keymap[] = { | |||
| 57 | { KE_END }, | 57 | { KE_END }, |
| 58 | }; | 58 | }; |
| 59 | 59 | ||
| 60 | /* 5 button array notification value. */ | ||
| 61 | static const struct key_entry intel_array_keymap[] = { | ||
| 62 | { KE_KEY, 0xC2, { KEY_LEFTMETA } }, /* Press */ | ||
| 63 | { KE_IGNORE, 0xC3, { KEY_LEFTMETA } }, /* Release */ | ||
| 64 | { KE_KEY, 0xC4, { KEY_VOLUMEUP } }, /* Press */ | ||
| 65 | { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* Release */ | ||
| 66 | { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* Press */ | ||
| 67 | { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* Release */ | ||
| 68 | { KE_SW, 0xC8, { .sw = { SW_ROTATE_LOCK, 1 } } }, /* Press */ | ||
| 69 | { KE_SW, 0xC9, { .sw = { SW_ROTATE_LOCK, 0 } } }, /* Release */ | ||
| 70 | { KE_KEY, 0xCE, { KEY_POWER } }, /* Press */ | ||
| 71 | { KE_IGNORE, 0xCF, { KEY_POWER } }, /* Release */ | ||
| 72 | { KE_END }, | ||
| 73 | }; | ||
| 74 | |||
| 60 | struct intel_hid_priv { | 75 | struct intel_hid_priv { |
| 61 | struct input_dev *input_dev; | 76 | struct input_dev *input_dev; |
| 77 | struct input_dev *array; | ||
| 62 | }; | 78 | }; |
| 63 | 79 | ||
| 64 | static int intel_hid_set_enable(struct device *device, int enable) | 80 | 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) | |||
| 78 | return 0; | 94 | return 0; |
| 79 | } | 95 | } |
| 80 | 96 | ||
| 97 | static void intel_button_array_enable(struct device *device, bool enable) | ||
| 98 | { | ||
| 99 | struct intel_hid_priv *priv = dev_get_drvdata(device); | ||
| 100 | acpi_handle handle = ACPI_HANDLE(device); | ||
| 101 | unsigned long long button_cap; | ||
| 102 | acpi_status status; | ||
| 103 | |||
| 104 | if (!priv->array) | ||
| 105 | return; | ||
| 106 | |||
| 107 | /* Query supported platform features */ | ||
| 108 | status = acpi_evaluate_integer(handle, "BTNC", NULL, &button_cap); | ||
| 109 | if (ACPI_FAILURE(status)) { | ||
| 110 | dev_warn(device, "failed to get button capability\n"); | ||
| 111 | return; | ||
| 112 | } | ||
| 113 | |||
| 114 | /* Enable|disable features - power button is always enabled */ | ||
| 115 | status = acpi_execute_simple_method(handle, "BTNE", | ||
| 116 | enable ? button_cap : 1); | ||
| 117 | if (ACPI_FAILURE(status)) | ||
| 118 | dev_warn(device, "failed to set button capability\n"); | ||
| 119 | } | ||
| 120 | |||
| 81 | static int intel_hid_pl_suspend_handler(struct device *device) | 121 | static int intel_hid_pl_suspend_handler(struct device *device) |
| 82 | { | 122 | { |
| 83 | intel_hid_set_enable(device, 0); | 123 | intel_hid_set_enable(device, 0); |
| 124 | intel_button_array_enable(device, false); | ||
| 125 | |||
| 84 | return 0; | 126 | return 0; |
| 85 | } | 127 | } |
| 86 | 128 | ||
| 87 | static int intel_hid_pl_resume_handler(struct device *device) | 129 | static int intel_hid_pl_resume_handler(struct device *device) |
| 88 | { | 130 | { |
| 89 | intel_hid_set_enable(device, 1); | 131 | intel_hid_set_enable(device, 1); |
| 132 | intel_button_array_enable(device, true); | ||
| 133 | |||
| 90 | return 0; | 134 | return 0; |
| 91 | } | 135 | } |
| 92 | 136 | ||
| @@ -126,6 +170,27 @@ err_free_device: | |||
| 126 | return ret; | 170 | return ret; |
| 127 | } | 171 | } |
| 128 | 172 | ||
| 173 | static int intel_button_array_input_setup(struct platform_device *device) | ||
| 174 | { | ||
| 175 | struct intel_hid_priv *priv = dev_get_drvdata(&device->dev); | ||
| 176 | int ret; | ||
| 177 | |||
| 178 | /* Setup input device for 5 button array */ | ||
| 179 | priv->array = devm_input_allocate_device(&device->dev); | ||
| 180 | if (!priv->array) | ||
| 181 | return -ENOMEM; | ||
| 182 | |||
| 183 | ret = sparse_keymap_setup(priv->array, intel_array_keymap, NULL); | ||
| 184 | if (ret) | ||
| 185 | return ret; | ||
| 186 | |||
| 187 | priv->array->dev.parent = &device->dev; | ||
| 188 | priv->array->name = "Intel HID 5 button array"; | ||
| 189 | priv->array->id.bustype = BUS_HOST; | ||
| 190 | |||
| 191 | return input_register_device(priv->array); | ||
| 192 | } | ||
| 193 | |||
| 129 | static void intel_hid_input_destroy(struct platform_device *device) | 194 | static void intel_hid_input_destroy(struct platform_device *device) |
| 130 | { | 195 | { |
| 131 | struct intel_hid_priv *priv = dev_get_drvdata(&device->dev); | 196 | 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) | |||
| 140 | unsigned long long ev_index; | 205 | unsigned long long ev_index; |
| 141 | acpi_status status; | 206 | acpi_status status; |
| 142 | 207 | ||
| 143 | /* The platform spec only defines one event code: 0xC0. */ | 208 | /* 0xC0 is for HID events, other values are for 5 button array */ |
| 144 | if (event != 0xc0) { | 209 | if (event != 0xc0) { |
| 145 | dev_warn(&device->dev, "received unknown event (0x%x)\n", | 210 | if (!priv->array || |
| 146 | event); | 211 | !sparse_keymap_report_event(priv->array, event, 1, true)) |
| 212 | dev_info(&device->dev, "unknown event 0x%x\n", event); | ||
| 147 | return; | 213 | return; |
| 148 | } | 214 | } |
| 149 | 215 | ||
| @@ -161,8 +227,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) | |||
| 161 | static int intel_hid_probe(struct platform_device *device) | 227 | static int intel_hid_probe(struct platform_device *device) |
| 162 | { | 228 | { |
| 163 | acpi_handle handle = ACPI_HANDLE(&device->dev); | 229 | acpi_handle handle = ACPI_HANDLE(&device->dev); |
| 230 | unsigned long long event_cap, mode; | ||
| 164 | struct intel_hid_priv *priv; | 231 | struct intel_hid_priv *priv; |
| 165 | unsigned long long mode; | ||
| 166 | acpi_status status; | 232 | acpi_status status; |
| 167 | int err; | 233 | int err; |
| 168 | 234 | ||
| @@ -193,6 +259,15 @@ static int intel_hid_probe(struct platform_device *device) | |||
| 193 | return err; | 259 | return err; |
| 194 | } | 260 | } |
| 195 | 261 | ||
| 262 | /* Setup 5 button array */ | ||
| 263 | status = acpi_evaluate_integer(handle, "HEBC", NULL, &event_cap); | ||
| 264 | if (ACPI_SUCCESS(status) && (event_cap & 0x20000)) { | ||
| 265 | dev_info(&device->dev, "platform supports 5 button array\n"); | ||
| 266 | err = intel_button_array_input_setup(device); | ||
| 267 | if (err) | ||
| 268 | pr_err("Failed to setup Intel 5 button array hotkeys\n"); | ||
| 269 | } | ||
| 270 | |||
| 196 | status = acpi_install_notify_handler(handle, | 271 | status = acpi_install_notify_handler(handle, |
| 197 | ACPI_DEVICE_NOTIFY, | 272 | ACPI_DEVICE_NOTIFY, |
| 198 | notify_handler, | 273 | notify_handler, |
| @@ -206,6 +281,16 @@ static int intel_hid_probe(struct platform_device *device) | |||
| 206 | if (err) | 281 | if (err) |
| 207 | goto err_remove_notify; | 282 | goto err_remove_notify; |
| 208 | 283 | ||
| 284 | if (priv->array) { | ||
| 285 | intel_button_array_enable(&device->dev, true); | ||
| 286 | |||
| 287 | /* Call button load method to enable HID power button */ | ||
| 288 | status = acpi_evaluate_object(handle, "BTNL", NULL, NULL); | ||
| 289 | if (ACPI_FAILURE(status)) | ||
| 290 | dev_warn(&device->dev, | ||
| 291 | "failed to enable HID power button\n"); | ||
| 292 | } | ||
| 293 | |||
| 209 | return 0; | 294 | return 0; |
| 210 | 295 | ||
| 211 | err_remove_notify: | 296 | err_remove_notify: |
| @@ -224,6 +309,7 @@ static int intel_hid_remove(struct platform_device *device) | |||
| 224 | acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); | 309 | acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); |
| 225 | intel_hid_input_destroy(device); | 310 | intel_hid_input_destroy(device); |
| 226 | intel_hid_set_enable(&device->dev, 0); | 311 | intel_hid_set_enable(&device->dev, 0); |
| 312 | intel_button_array_enable(&device->dev, false); | ||
| 227 | 313 | ||
| 228 | /* | 314 | /* |
| 229 | * Even if we failed to shut off the event stream, we can still | 315 | * Even if we failed to shut off the event stream, we can still |
diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index 361770568ad0..871cfa682519 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c | |||
| @@ -1,7 +1,10 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Power button driver for Medfield. | 2 | * Power button driver for Intel MID platforms. |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2010 Intel Corp | 4 | * Copyright (C) 2010,2017 Intel Corp |
| 5 | * | ||
| 6 | * Author: Hong Liu <hong.liu@intel.com> | ||
| 7 | * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> | ||
| 5 | * | 8 | * |
| 6 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by | 10 | * it under the terms of the GNU General Public License as published by |
| @@ -11,20 +14,20 @@ | |||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 13 | * General Public License for more details. | 16 | * General Public License for more details. |
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License along | ||
| 16 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 17 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 18 | */ | 17 | */ |
| 19 | 18 | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/init.h> | 19 | #include <linux/init.h> |
| 22 | #include <linux/interrupt.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/platform_device.h> | ||
| 25 | #include <linux/input.h> | 20 | #include <linux/input.h> |
| 21 | #include <linux/interrupt.h> | ||
| 26 | #include <linux/mfd/intel_msic.h> | 22 | #include <linux/mfd/intel_msic.h> |
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/platform_device.h> | ||
| 27 | #include <linux/pm_wakeirq.h> | 25 | #include <linux/pm_wakeirq.h> |
| 26 | #include <linux/slab.h> | ||
| 27 | |||
| 28 | #include <asm/cpu_device_id.h> | ||
| 29 | #include <asm/intel-family.h> | ||
| 30 | #include <asm/intel_scu_ipc.h> | ||
| 28 | 31 | ||
| 29 | #define DRIVER_NAME "msic_power_btn" | 32 | #define DRIVER_NAME "msic_power_btn" |
| 30 | 33 | ||
| @@ -36,37 +39,113 @@ | |||
| 36 | */ | 39 | */ |
| 37 | #define MSIC_PWRBTNM (1 << 0) | 40 | #define MSIC_PWRBTNM (1 << 0) |
| 38 | 41 | ||
| 39 | static irqreturn_t mfld_pb_isr(int irq, void *dev_id) | 42 | /* Intel Tangier */ |
| 43 | #define BCOVE_PB_LEVEL (1 << 4) /* 1 - release, 0 - press */ | ||
| 44 | |||
| 45 | /* Basin Cove PMIC */ | ||
| 46 | #define BCOVE_PBIRQ 0x02 | ||
| 47 | #define BCOVE_IRQLVL1MSK 0x0c | ||
| 48 | #define BCOVE_PBIRQMASK 0x0d | ||
| 49 | #define BCOVE_PBSTATUS 0x27 | ||
| 50 | |||
| 51 | struct mid_pb_ddata { | ||
| 52 | struct device *dev; | ||
| 53 | int irq; | ||
| 54 | struct input_dev *input; | ||
| 55 | unsigned short mirqlvl1_addr; | ||
| 56 | unsigned short pbstat_addr; | ||
| 57 | u8 pbstat_mask; | ||
| 58 | int (*setup)(struct mid_pb_ddata *ddata); | ||
| 59 | }; | ||
| 60 | |||
| 61 | static int mid_pbstat(struct mid_pb_ddata *ddata, int *value) | ||
| 40 | { | 62 | { |
| 41 | struct input_dev *input = dev_id; | 63 | struct input_dev *input = ddata->input; |
| 42 | int ret; | 64 | int ret; |
| 43 | u8 pbstat; | 65 | u8 pbstat; |
| 44 | 66 | ||
| 45 | ret = intel_msic_reg_read(INTEL_MSIC_PBSTATUS, &pbstat); | 67 | ret = intel_scu_ipc_ioread8(ddata->pbstat_addr, &pbstat); |
| 68 | if (ret) | ||
| 69 | return ret; | ||
| 70 | |||
| 46 | dev_dbg(input->dev.parent, "PB_INT status= %d\n", pbstat); | 71 | dev_dbg(input->dev.parent, "PB_INT status= %d\n", pbstat); |
| 47 | 72 | ||
| 73 | *value = !(pbstat & ddata->pbstat_mask); | ||
| 74 | return 0; | ||
| 75 | } | ||
| 76 | |||
| 77 | static int mid_irq_ack(struct mid_pb_ddata *ddata) | ||
| 78 | { | ||
| 79 | return intel_scu_ipc_update_register(ddata->mirqlvl1_addr, 0, MSIC_PWRBTNM); | ||
| 80 | } | ||
| 81 | |||
| 82 | static int mrfld_setup(struct mid_pb_ddata *ddata) | ||
| 83 | { | ||
| 84 | /* Unmask the PBIRQ and MPBIRQ on Tangier */ | ||
| 85 | intel_scu_ipc_update_register(BCOVE_PBIRQ, 0, MSIC_PWRBTNM); | ||
| 86 | intel_scu_ipc_update_register(BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM); | ||
| 87 | |||
| 88 | return 0; | ||
| 89 | } | ||
| 90 | |||
| 91 | static irqreturn_t mid_pb_isr(int irq, void *dev_id) | ||
| 92 | { | ||
| 93 | struct mid_pb_ddata *ddata = dev_id; | ||
| 94 | struct input_dev *input = ddata->input; | ||
| 95 | int value = 0; | ||
| 96 | int ret; | ||
| 97 | |||
| 98 | ret = mid_pbstat(ddata, &value); | ||
| 48 | if (ret < 0) { | 99 | if (ret < 0) { |
| 49 | dev_err(input->dev.parent, "Read error %d while reading" | 100 | dev_err(input->dev.parent, |
| 50 | " MSIC_PB_STATUS\n", ret); | 101 | "Read error %d while reading MSIC_PB_STATUS\n", ret); |
| 51 | } else { | 102 | } else { |
| 52 | input_event(input, EV_KEY, KEY_POWER, | 103 | input_event(input, EV_KEY, KEY_POWER, value); |
| 53 | !(pbstat & MSIC_PB_LEVEL)); | ||
| 54 | input_sync(input); | 104 | input_sync(input); |
| 55 | } | 105 | } |
| 56 | 106 | ||
| 107 | mid_irq_ack(ddata); | ||
| 57 | return IRQ_HANDLED; | 108 | return IRQ_HANDLED; |
| 58 | } | 109 | } |
| 59 | 110 | ||
| 60 | static int mfld_pb_probe(struct platform_device *pdev) | 111 | static struct mid_pb_ddata mfld_ddata = { |
| 112 | .mirqlvl1_addr = INTEL_MSIC_IRQLVL1MSK, | ||
| 113 | .pbstat_addr = INTEL_MSIC_PBSTATUS, | ||
| 114 | .pbstat_mask = MSIC_PB_LEVEL, | ||
| 115 | }; | ||
| 116 | |||
| 117 | static struct mid_pb_ddata mrfld_ddata = { | ||
| 118 | .mirqlvl1_addr = BCOVE_IRQLVL1MSK, | ||
| 119 | .pbstat_addr = BCOVE_PBSTATUS, | ||
| 120 | .pbstat_mask = BCOVE_PB_LEVEL, | ||
| 121 | .setup = mrfld_setup, | ||
| 122 | }; | ||
| 123 | |||
| 124 | #define ICPU(model, ddata) \ | ||
| 125 | { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (kernel_ulong_t)&ddata } | ||
| 126 | |||
| 127 | static const struct x86_cpu_id mid_pb_cpu_ids[] = { | ||
| 128 | ICPU(INTEL_FAM6_ATOM_PENWELL, mfld_ddata), | ||
| 129 | ICPU(INTEL_FAM6_ATOM_MERRIFIELD, mrfld_ddata), | ||
| 130 | {} | ||
| 131 | }; | ||
| 132 | |||
| 133 | static int mid_pb_probe(struct platform_device *pdev) | ||
| 61 | { | 134 | { |
| 135 | const struct x86_cpu_id *id; | ||
| 136 | struct mid_pb_ddata *ddata; | ||
| 62 | struct input_dev *input; | 137 | struct input_dev *input; |
| 63 | int irq = platform_get_irq(pdev, 0); | 138 | int irq = platform_get_irq(pdev, 0); |
| 64 | int error; | 139 | int error; |
| 65 | 140 | ||
| 141 | id = x86_match_cpu(mid_pb_cpu_ids); | ||
| 142 | if (!id) | ||
| 143 | return -ENODEV; | ||
| 144 | |||
| 66 | if (irq < 0) | 145 | if (irq < 0) |
| 67 | return -EINVAL; | 146 | return -EINVAL; |
| 68 | 147 | ||
| 69 | input = input_allocate_device(); | 148 | input = devm_input_allocate_device(&pdev->dev); |
| 70 | if (!input) | 149 | if (!input) |
| 71 | return -ENOMEM; | 150 | return -ENOMEM; |
| 72 | 151 | ||
| @@ -77,25 +156,36 @@ static int mfld_pb_probe(struct platform_device *pdev) | |||
| 77 | 156 | ||
| 78 | input_set_capability(input, EV_KEY, KEY_POWER); | 157 | input_set_capability(input, EV_KEY, KEY_POWER); |
| 79 | 158 | ||
| 80 | error = request_threaded_irq(irq, NULL, mfld_pb_isr, IRQF_ONESHOT, | 159 | ddata = (struct mid_pb_ddata *)id->driver_data; |
| 81 | DRIVER_NAME, input); | 160 | if (!ddata) |
| 82 | if (error) { | 161 | return -ENODATA; |
| 83 | dev_err(&pdev->dev, "Unable to request irq %d for mfld power" | 162 | |
| 84 | "button\n", irq); | 163 | ddata->dev = &pdev->dev; |
| 85 | goto err_free_input; | 164 | ddata->irq = irq; |
| 165 | ddata->input = input; | ||
| 166 | |||
| 167 | if (ddata->setup) { | ||
| 168 | error = ddata->setup(ddata); | ||
| 169 | if (error) | ||
| 170 | return error; | ||
| 86 | } | 171 | } |
| 87 | 172 | ||
| 88 | device_init_wakeup(&pdev->dev, true); | 173 | error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr, |
| 89 | dev_pm_set_wake_irq(&pdev->dev, irq); | 174 | IRQF_ONESHOT, DRIVER_NAME, ddata); |
| 175 | if (error) { | ||
| 176 | dev_err(&pdev->dev, | ||
| 177 | "Unable to request irq %d for MID power button\n", irq); | ||
| 178 | return error; | ||
| 179 | } | ||
| 90 | 180 | ||
| 91 | error = input_register_device(input); | 181 | error = input_register_device(input); |
| 92 | if (error) { | 182 | if (error) { |
| 93 | dev_err(&pdev->dev, "Unable to register input dev, error " | 183 | dev_err(&pdev->dev, |
| 94 | "%d\n", error); | 184 | "Unable to register input dev, error %d\n", error); |
| 95 | goto err_free_irq; | 185 | return error; |
| 96 | } | 186 | } |
| 97 | 187 | ||
| 98 | platform_set_drvdata(pdev, input); | 188 | platform_set_drvdata(pdev, ddata); |
| 99 | 189 | ||
| 100 | /* | 190 | /* |
| 101 | * SCU firmware might send power button interrupts to IA core before | 191 | * SCU firmware might send power button interrupts to IA core before |
| @@ -107,46 +197,39 @@ static int mfld_pb_probe(struct platform_device *pdev) | |||
| 107 | * initialization. The race happens rarely. So we needn't worry | 197 | * initialization. The race happens rarely. So we needn't worry |
| 108 | * about it. | 198 | * about it. |
| 109 | */ | 199 | */ |
| 110 | error = intel_msic_reg_update(INTEL_MSIC_IRQLVL1MSK, 0, MSIC_PWRBTNM); | 200 | error = mid_irq_ack(ddata); |
| 111 | if (error) { | 201 | if (error) { |
| 112 | dev_err(&pdev->dev, "Unable to clear power button interrupt, " | 202 | dev_err(&pdev->dev, |
| 113 | "error: %d\n", error); | 203 | "Unable to clear power button interrupt, error: %d\n", |
| 114 | goto err_free_irq; | 204 | error); |
| 205 | return error; | ||
| 115 | } | 206 | } |
| 116 | 207 | ||
| 117 | return 0; | 208 | device_init_wakeup(&pdev->dev, true); |
| 209 | dev_pm_set_wake_irq(&pdev->dev, irq); | ||
| 118 | 210 | ||
| 119 | err_free_irq: | 211 | return 0; |
| 120 | free_irq(irq, input); | ||
| 121 | err_free_input: | ||
| 122 | input_free_device(input); | ||
| 123 | return error; | ||
| 124 | } | 212 | } |
| 125 | 213 | ||
| 126 | static int mfld_pb_remove(struct platform_device *pdev) | 214 | static int mid_pb_remove(struct platform_device *pdev) |
| 127 | { | 215 | { |
| 128 | struct input_dev *input = platform_get_drvdata(pdev); | ||
| 129 | int irq = platform_get_irq(pdev, 0); | ||
| 130 | |||
| 131 | dev_pm_clear_wake_irq(&pdev->dev); | 216 | dev_pm_clear_wake_irq(&pdev->dev); |
| 132 | device_init_wakeup(&pdev->dev, false); | 217 | device_init_wakeup(&pdev->dev, false); |
| 133 | free_irq(irq, input); | ||
| 134 | input_unregister_device(input); | ||
| 135 | 218 | ||
| 136 | return 0; | 219 | return 0; |
| 137 | } | 220 | } |
| 138 | 221 | ||
| 139 | static struct platform_driver mfld_pb_driver = { | 222 | static struct platform_driver mid_pb_driver = { |
| 140 | .driver = { | 223 | .driver = { |
| 141 | .name = DRIVER_NAME, | 224 | .name = DRIVER_NAME, |
| 142 | }, | 225 | }, |
| 143 | .probe = mfld_pb_probe, | 226 | .probe = mid_pb_probe, |
| 144 | .remove = mfld_pb_remove, | 227 | .remove = mid_pb_remove, |
| 145 | }; | 228 | }; |
| 146 | 229 | ||
| 147 | module_platform_driver(mfld_pb_driver); | 230 | module_platform_driver(mid_pb_driver); |
| 148 | 231 | ||
| 149 | MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>"); | 232 | MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>"); |
| 150 | MODULE_DESCRIPTION("Intel Medfield Power Button Driver"); | 233 | MODULE_DESCRIPTION("Intel MID Power Button Driver"); |
| 151 | MODULE_LICENSE("GPL v2"); | 234 | MODULE_LICENSE("GPL v2"); |
| 152 | MODULE_ALIAS("platform:" DRIVER_NAME); | 235 | MODULE_ALIAS("platform:" DRIVER_NAME); |
diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 0df3c9d37509..008a76903cbf 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c | |||
| @@ -549,9 +549,9 @@ static int mid_thermal_remove(struct platform_device *pdev) | |||
| 549 | 549 | ||
| 550 | static const struct platform_device_id therm_id_table[] = { | 550 | static const struct platform_device_id therm_id_table[] = { |
| 551 | { DRIVER_NAME, 1 }, | 551 | { DRIVER_NAME, 1 }, |
| 552 | { "msic_thermal", 1 }, | ||
| 553 | { } | 552 | { } |
| 554 | }; | 553 | }; |
| 554 | MODULE_DEVICE_TABLE(platform, therm_id_table); | ||
| 555 | 555 | ||
| 556 | static struct platform_driver mid_thermal_driver = { | 556 | static struct platform_driver mid_thermal_driver = { |
| 557 | .driver = { | 557 | .driver = { |
diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c index b130b8c9b9d7..914bcd2edbde 100644 --- a/drivers/platform/x86/intel_pmc_core.c +++ b/drivers/platform/x86/intel_pmc_core.c | |||
| @@ -188,8 +188,7 @@ static int pmc_core_check_read_lock_bit(void) | |||
| 188 | u32 value; | 188 | u32 value; |
| 189 | 189 | ||
| 190 | value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_CFG_OFFSET); | 190 | value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_CFG_OFFSET); |
| 191 | return test_bit(SPT_PMC_READ_DISABLE_BIT, | 191 | return value & BIT(SPT_PMC_READ_DISABLE_BIT); |
| 192 | (unsigned long *)&value); | ||
| 193 | } | 192 | } |
| 194 | 193 | ||
| 195 | #if IS_ENABLED(CONFIG_DEBUG_FS) | 194 | #if IS_ENABLED(CONFIG_DEBUG_FS) |
| @@ -238,8 +237,7 @@ static int pmc_core_mtpmc_link_status(void) | |||
| 238 | u32 value; | 237 | u32 value; |
| 239 | 238 | ||
| 240 | value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_STS_OFFSET); | 239 | value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_STS_OFFSET); |
| 241 | return test_bit(SPT_PMC_MSG_FULL_STS_BIT, | 240 | return value & BIT(SPT_PMC_MSG_FULL_STS_BIT); |
| 242 | (unsigned long *)&value); | ||
| 243 | } | 241 | } |
| 244 | 242 | ||
| 245 | static int pmc_core_send_msg(u32 *addr_xram) | 243 | static int pmc_core_send_msg(u32 *addr_xram) |
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index 0bf51d574fa9..0651d47b8eeb 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c | |||
| @@ -32,7 +32,10 @@ | |||
| 32 | #include <linux/notifier.h> | 32 | #include <linux/notifier.h> |
| 33 | #include <linux/suspend.h> | 33 | #include <linux/suspend.h> |
| 34 | #include <linux/acpi.h> | 34 | #include <linux/acpi.h> |
| 35 | #include <linux/io-64-nonatomic-lo-hi.h> | ||
| 36 | |||
| 35 | #include <asm/intel_pmc_ipc.h> | 37 | #include <asm/intel_pmc_ipc.h> |
| 38 | |||
| 36 | #include <linux/platform_data/itco_wdt.h> | 39 | #include <linux/platform_data/itco_wdt.h> |
| 37 | 40 | ||
| 38 | /* | 41 | /* |
| @@ -54,6 +57,18 @@ | |||
| 54 | #define IPC_WRITE_BUFFER 0x80 | 57 | #define IPC_WRITE_BUFFER 0x80 |
| 55 | #define IPC_READ_BUFFER 0x90 | 58 | #define IPC_READ_BUFFER 0x90 |
| 56 | 59 | ||
| 60 | /* PMC Global Control Registers */ | ||
| 61 | #define GCR_TELEM_DEEP_S0IX_OFFSET 0x1078 | ||
| 62 | #define GCR_TELEM_SHLW_S0IX_OFFSET 0x1080 | ||
| 63 | |||
| 64 | /* Residency with clock rate at 19.2MHz to usecs */ | ||
| 65 | #define S0IX_RESIDENCY_IN_USECS(d, s) \ | ||
| 66 | ({ \ | ||
| 67 | u64 result = 10ull * ((d) + (s)); \ | ||
| 68 | do_div(result, 192); \ | ||
| 69 | result; \ | ||
| 70 | }) | ||
| 71 | |||
| 57 | /* | 72 | /* |
| 58 | * 16-byte buffer for sending data associated with IPC command. | 73 | * 16-byte buffer for sending data associated with IPC command. |
| 59 | */ | 74 | */ |
| @@ -68,7 +83,7 @@ | |||
| 68 | #define PLAT_RESOURCE_IPC_INDEX 0 | 83 | #define PLAT_RESOURCE_IPC_INDEX 0 |
| 69 | #define PLAT_RESOURCE_IPC_SIZE 0x1000 | 84 | #define PLAT_RESOURCE_IPC_SIZE 0x1000 |
| 70 | #define PLAT_RESOURCE_GCR_OFFSET 0x1008 | 85 | #define PLAT_RESOURCE_GCR_OFFSET 0x1008 |
| 71 | #define PLAT_RESOURCE_GCR_SIZE 0x4 | 86 | #define PLAT_RESOURCE_GCR_SIZE 0x1000 |
| 72 | #define PLAT_RESOURCE_BIOS_DATA_INDEX 1 | 87 | #define PLAT_RESOURCE_BIOS_DATA_INDEX 1 |
| 73 | #define PLAT_RESOURCE_BIOS_IFACE_INDEX 2 | 88 | #define PLAT_RESOURCE_BIOS_IFACE_INDEX 2 |
| 74 | #define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3 | 89 | #define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3 |
| @@ -97,8 +112,6 @@ | |||
| 97 | #define TCO_PMC_OFFSET 0x8 | 112 | #define TCO_PMC_OFFSET 0x8 |
| 98 | #define TCO_PMC_SIZE 0x4 | 113 | #define TCO_PMC_SIZE 0x4 |
| 99 | 114 | ||
| 100 | static const int iTCO_version = 3; | ||
| 101 | |||
| 102 | static struct intel_pmc_ipc_dev { | 115 | static struct intel_pmc_ipc_dev { |
| 103 | struct device *dev; | 116 | struct device *dev; |
| 104 | void __iomem *ipc_base; | 117 | void __iomem *ipc_base; |
| @@ -115,6 +128,7 @@ static struct intel_pmc_ipc_dev { | |||
| 115 | /* gcr */ | 128 | /* gcr */ |
| 116 | resource_size_t gcr_base; | 129 | resource_size_t gcr_base; |
| 117 | int gcr_size; | 130 | int gcr_size; |
| 131 | bool has_gcr_regs; | ||
| 118 | 132 | ||
| 119 | /* punit */ | 133 | /* punit */ |
| 120 | struct platform_device *punit_dev; | 134 | struct platform_device *punit_dev; |
| @@ -180,6 +194,11 @@ static inline u32 ipc_data_readl(u32 offset) | |||
| 180 | return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset); | 194 | return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset); |
| 181 | } | 195 | } |
| 182 | 196 | ||
| 197 | static inline u64 gcr_data_readq(u32 offset) | ||
| 198 | { | ||
| 199 | return readq(ipcdev.ipc_base + offset); | ||
| 200 | } | ||
| 201 | |||
| 183 | static int intel_pmc_ipc_check_status(void) | 202 | static int intel_pmc_ipc_check_status(void) |
| 184 | { | 203 | { |
| 185 | int status; | 204 | int status; |
| @@ -389,6 +408,7 @@ static void ipc_pci_remove(struct pci_dev *pdev) | |||
| 389 | static const struct pci_device_id ipc_pci_ids[] = { | 408 | static const struct pci_device_id ipc_pci_ids[] = { |
| 390 | {PCI_VDEVICE(INTEL, 0x0a94), 0}, | 409 | {PCI_VDEVICE(INTEL, 0x0a94), 0}, |
| 391 | {PCI_VDEVICE(INTEL, 0x1a94), 0}, | 410 | {PCI_VDEVICE(INTEL, 0x1a94), 0}, |
| 411 | {PCI_VDEVICE(INTEL, 0x5a94), 0}, | ||
| 392 | { 0,} | 412 | { 0,} |
| 393 | }; | 413 | }; |
| 394 | MODULE_DEVICE_TABLE(pci, ipc_pci_ids); | 414 | MODULE_DEVICE_TABLE(pci, ipc_pci_ids); |
| @@ -712,7 +732,8 @@ static int ipc_plat_get_res(struct platform_device *pdev) | |||
| 712 | dev_err(&pdev->dev, "Failed to get ipc resource\n"); | 732 | dev_err(&pdev->dev, "Failed to get ipc resource\n"); |
| 713 | return -ENXIO; | 733 | return -ENXIO; |
| 714 | } | 734 | } |
| 715 | size = PLAT_RESOURCE_IPC_SIZE; | 735 | size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE; |
| 736 | |||
| 716 | if (!request_mem_region(res->start, size, pdev->name)) { | 737 | if (!request_mem_region(res->start, size, pdev->name)) { |
| 717 | dev_err(&pdev->dev, "Failed to request ipc resource\n"); | 738 | dev_err(&pdev->dev, "Failed to request ipc resource\n"); |
| 718 | return -EBUSY; | 739 | return -EBUSY; |
| @@ -748,6 +769,28 @@ static int ipc_plat_get_res(struct platform_device *pdev) | |||
| 748 | return 0; | 769 | return 0; |
| 749 | } | 770 | } |
| 750 | 771 | ||
| 772 | /** | ||
| 773 | * intel_pmc_s0ix_counter_read() - Read S0ix residency. | ||
| 774 | * @data: Out param that contains current S0ix residency count. | ||
| 775 | * | ||
| 776 | * Return: an error code or 0 on success. | ||
| 777 | */ | ||
| 778 | int intel_pmc_s0ix_counter_read(u64 *data) | ||
| 779 | { | ||
| 780 | u64 deep, shlw; | ||
| 781 | |||
| 782 | if (!ipcdev.has_gcr_regs) | ||
| 783 | return -EACCES; | ||
| 784 | |||
| 785 | deep = gcr_data_readq(GCR_TELEM_DEEP_S0IX_OFFSET); | ||
| 786 | shlw = gcr_data_readq(GCR_TELEM_SHLW_S0IX_OFFSET); | ||
| 787 | |||
| 788 | *data = S0IX_RESIDENCY_IN_USECS(deep, shlw); | ||
| 789 | |||
| 790 | return 0; | ||
| 791 | } | ||
| 792 | EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read); | ||
| 793 | |||
| 751 | #ifdef CONFIG_ACPI | 794 | #ifdef CONFIG_ACPI |
| 752 | static const struct acpi_device_id ipc_acpi_ids[] = { | 795 | static const struct acpi_device_id ipc_acpi_ids[] = { |
| 753 | { "INT34D2", 0}, | 796 | { "INT34D2", 0}, |
| @@ -797,6 +840,8 @@ static int ipc_plat_probe(struct platform_device *pdev) | |||
| 797 | goto err_sys; | 840 | goto err_sys; |
| 798 | } | 841 | } |
| 799 | 842 | ||
| 843 | ipcdev.has_gcr_regs = true; | ||
| 844 | |||
| 800 | return 0; | 845 | return 0; |
| 801 | err_sys: | 846 | err_sys: |
| 802 | free_irq(ipcdev.irq, &ipcdev); | 847 | free_irq(ipcdev.irq, &ipcdev); |
| @@ -808,8 +853,11 @@ err_device: | |||
| 808 | iounmap(ipcdev.ipc_base); | 853 | iounmap(ipcdev.ipc_base); |
| 809 | res = platform_get_resource(pdev, IORESOURCE_MEM, | 854 | res = platform_get_resource(pdev, IORESOURCE_MEM, |
| 810 | PLAT_RESOURCE_IPC_INDEX); | 855 | PLAT_RESOURCE_IPC_INDEX); |
| 811 | if (res) | 856 | if (res) { |
| 812 | release_mem_region(res->start, PLAT_RESOURCE_IPC_SIZE); | 857 | release_mem_region(res->start, |
| 858 | PLAT_RESOURCE_IPC_SIZE + | ||
| 859 | PLAT_RESOURCE_GCR_SIZE); | ||
| 860 | } | ||
| 813 | return ret; | 861 | return ret; |
| 814 | } | 862 | } |
| 815 | 863 | ||
| @@ -825,8 +873,11 @@ static int ipc_plat_remove(struct platform_device *pdev) | |||
| 825 | iounmap(ipcdev.ipc_base); | 873 | iounmap(ipcdev.ipc_base); |
| 826 | res = platform_get_resource(pdev, IORESOURCE_MEM, | 874 | res = platform_get_resource(pdev, IORESOURCE_MEM, |
| 827 | PLAT_RESOURCE_IPC_INDEX); | 875 | PLAT_RESOURCE_IPC_INDEX); |
| 828 | if (res) | 876 | if (res) { |
| 829 | release_mem_region(res->start, PLAT_RESOURCE_IPC_SIZE); | 877 | release_mem_region(res->start, |
| 878 | PLAT_RESOURCE_IPC_SIZE + | ||
| 879 | PLAT_RESOURCE_GCR_SIZE); | ||
| 880 | } | ||
| 830 | ipcdev.dev = NULL; | 881 | ipcdev.dev = NULL; |
| 831 | return 0; | 882 | return 0; |
| 832 | } | 883 | } |
diff --git a/drivers/platform/x86/intel_turbo_max_3.c b/drivers/platform/x86/intel_turbo_max_3.c new file mode 100644 index 000000000000..4f60d8e32a0a --- /dev/null +++ b/drivers/platform/x86/intel_turbo_max_3.c | |||
| @@ -0,0 +1,151 @@ | |||
| 1 | /* | ||
| 2 | * Intel Turbo Boost Max Technology 3.0 legacy (non HWP) enumeration driver | ||
| 3 | * Copyright (c) 2017, Intel Corporation. | ||
| 4 | * All rights reserved. | ||
| 5 | * | ||
| 6 | * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify it | ||
| 9 | * under the terms and conditions of the GNU General Public License, | ||
| 10 | * version 2, as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 15 | * more details. | ||
| 16 | * | ||
| 17 | */ | ||
| 18 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 19 | |||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <linux/init.h> | ||
| 22 | #include <linux/topology.h> | ||
| 23 | #include <linux/workqueue.h> | ||
| 24 | #include <linux/cpuhotplug.h> | ||
| 25 | #include <linux/cpufeature.h> | ||
| 26 | #include <asm/cpu_device_id.h> | ||
| 27 | #include <asm/intel-family.h> | ||
| 28 | |||
| 29 | #define MSR_OC_MAILBOX 0x150 | ||
| 30 | #define MSR_OC_MAILBOX_CMD_OFFSET 32 | ||
| 31 | #define MSR_OC_MAILBOX_RSP_OFFSET 32 | ||
| 32 | #define MSR_OC_MAILBOX_BUSY_BIT 63 | ||
| 33 | #define OC_MAILBOX_FC_CONTROL_CMD 0x1C | ||
| 34 | |||
| 35 | /* | ||
| 36 | * Typical latency to get mail box response is ~3us, It takes +3 us to | ||
| 37 | * process reading mailbox after issuing mailbox write on a Broadwell 3.4 GHz | ||
| 38 | * system. So for most of the time, the first mailbox read should have the | ||
| 39 | * response, but to avoid some boundary cases retry twice. | ||
| 40 | */ | ||
| 41 | #define OC_MAILBOX_RETRY_COUNT 2 | ||
| 42 | |||
| 43 | static int get_oc_core_priority(unsigned int cpu) | ||
| 44 | { | ||
| 45 | u64 value, cmd = OC_MAILBOX_FC_CONTROL_CMD; | ||
| 46 | int ret, i; | ||
| 47 | |||
| 48 | /* Issue favored core read command */ | ||
| 49 | value = cmd << MSR_OC_MAILBOX_CMD_OFFSET; | ||
| 50 | /* Set the busy bit to indicate OS is trying to issue command */ | ||
| 51 | value |= BIT_ULL(MSR_OC_MAILBOX_BUSY_BIT); | ||
| 52 | ret = wrmsrl_safe(MSR_OC_MAILBOX, value); | ||
| 53 | if (ret) { | ||
| 54 | pr_debug("cpu %d OC mailbox write failed\n", cpu); | ||
| 55 | return ret; | ||
| 56 | } | ||
| 57 | |||
| 58 | for (i = 0; i < OC_MAILBOX_RETRY_COUNT; ++i) { | ||
| 59 | ret = rdmsrl_safe(MSR_OC_MAILBOX, &value); | ||
| 60 | if (ret) { | ||
| 61 | pr_debug("cpu %d OC mailbox read failed\n", cpu); | ||
| 62 | break; | ||
| 63 | } | ||
| 64 | |||
| 65 | if (value & BIT_ULL(MSR_OC_MAILBOX_BUSY_BIT)) { | ||
| 66 | pr_debug("cpu %d OC mailbox still processing\n", cpu); | ||
| 67 | ret = -EBUSY; | ||
| 68 | continue; | ||
| 69 | } | ||
| 70 | |||
| 71 | if ((value >> MSR_OC_MAILBOX_RSP_OFFSET) & 0xff) { | ||
| 72 | pr_debug("cpu %d OC mailbox cmd failed\n", cpu); | ||
| 73 | ret = -ENXIO; | ||
| 74 | break; | ||
| 75 | } | ||
| 76 | |||
| 77 | ret = value & 0xff; | ||
| 78 | pr_debug("cpu %d max_ratio %d\n", cpu, ret); | ||
| 79 | break; | ||
| 80 | } | ||
| 81 | |||
| 82 | return ret; | ||
| 83 | } | ||
| 84 | |||
| 85 | /* | ||
| 86 | * The work item is needed to avoid CPU hotplug locking issues. The function | ||
| 87 | * itmt_legacy_set_priority() is called from CPU online callback, so can't | ||
| 88 | * call sched_set_itmt_support() from there as this function will aquire | ||
| 89 | * hotplug locks in its path. | ||
| 90 | */ | ||
| 91 | static void itmt_legacy_work_fn(struct work_struct *work) | ||
| 92 | { | ||
| 93 | sched_set_itmt_support(); | ||
| 94 | } | ||
| 95 | |||
| 96 | static DECLARE_WORK(sched_itmt_work, itmt_legacy_work_fn); | ||
| 97 | |||
| 98 | static int itmt_legacy_cpu_online(unsigned int cpu) | ||
| 99 | { | ||
| 100 | static u32 max_highest_perf = 0, min_highest_perf = U32_MAX; | ||
| 101 | int priority; | ||
| 102 | |||
| 103 | priority = get_oc_core_priority(cpu); | ||
| 104 | if (priority < 0) | ||
| 105 | return 0; | ||
| 106 | |||
| 107 | sched_set_itmt_core_prio(priority, cpu); | ||
| 108 | |||
| 109 | /* Enable ITMT feature when a core with different priority is found */ | ||
| 110 | if (max_highest_perf <= min_highest_perf) { | ||
| 111 | if (priority > max_highest_perf) | ||
| 112 | max_highest_perf = priority; | ||
| 113 | |||
| 114 | if (priority < min_highest_perf) | ||
| 115 | min_highest_perf = priority; | ||
| 116 | |||
| 117 | if (max_highest_perf > min_highest_perf) | ||
| 118 | schedule_work(&sched_itmt_work); | ||
| 119 | } | ||
| 120 | |||
| 121 | return 0; | ||
| 122 | } | ||
| 123 | |||
| 124 | #define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, } | ||
| 125 | |||
| 126 | static const struct x86_cpu_id itmt_legacy_cpu_ids[] = { | ||
| 127 | ICPU(INTEL_FAM6_BROADWELL_X), | ||
| 128 | {} | ||
| 129 | }; | ||
| 130 | |||
| 131 | static int __init itmt_legacy_init(void) | ||
| 132 | { | ||
| 133 | const struct x86_cpu_id *id; | ||
| 134 | int ret; | ||
| 135 | |||
| 136 | id = x86_match_cpu(itmt_legacy_cpu_ids); | ||
| 137 | if (!id) | ||
| 138 | return -ENODEV; | ||
| 139 | |||
| 140 | if (boot_cpu_has(X86_FEATURE_HWP)) | ||
| 141 | return -ENODEV; | ||
| 142 | |||
| 143 | ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, | ||
| 144 | "platform/x86/turbo_max_3:online", | ||
| 145 | itmt_legacy_cpu_online, NULL); | ||
| 146 | if (ret < 0) | ||
| 147 | return ret; | ||
| 148 | |||
| 149 | return 0; | ||
| 150 | } | ||
| 151 | late_initcall(itmt_legacy_init) | ||
diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 25f15df5c2d7..8f98c211b440 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c | |||
| @@ -45,6 +45,10 @@ | |||
| 45 | /* LPC bus IO offsets */ | 45 | /* LPC bus IO offsets */ |
| 46 | #define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000 | 46 | #define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000 |
| 47 | #define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500 | 47 | #define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500 |
| 48 | #define MLXPLAT_CPLD_LPC_REG_AGGR_ADRR 0x253a | ||
| 49 | #define MLXPLAT_CPLD_LPC_REG_PSU_ADRR 0x2558 | ||
| 50 | #define MLXPLAT_CPLD_LPC_REG_PWR_ADRR 0x2564 | ||
| 51 | #define MLXPLAT_CPLD_LPC_REG_FAN_ADRR 0x2588 | ||
| 48 | #define MLXPLAT_CPLD_LPC_IO_RANGE 0x100 | 52 | #define MLXPLAT_CPLD_LPC_IO_RANGE 0x100 |
| 49 | #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb | 53 | #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb |
| 50 | #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda | 54 | #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda |
| @@ -56,6 +60,17 @@ | |||
| 56 | MLXPLAT_CPLD_LPC_I2C_CH2_OFF) | \ | 60 | MLXPLAT_CPLD_LPC_I2C_CH2_OFF) | \ |
| 57 | MLXPLAT_CPLD_LPC_PIO_OFFSET) | 61 | MLXPLAT_CPLD_LPC_PIO_OFFSET) |
| 58 | 62 | ||
| 63 | /* Masks for aggregation, psu, pwr and fan event in CPLD related registers. */ | ||
| 64 | #define MLXPLAT_CPLD_AGGR_PSU_MASK_DEF 0x08 | ||
| 65 | #define MLXPLAT_CPLD_AGGR_PWR_MASK_DEF 0x08 | ||
| 66 | #define MLXPLAT_CPLD_AGGR_FAN_MASK_DEF 0x40 | ||
| 67 | #define MLXPLAT_CPLD_AGGR_MASK_DEF (MLXPLAT_CPLD_AGGR_PSU_MASK_DEF | \ | ||
| 68 | MLXPLAT_CPLD_AGGR_FAN_MASK_DEF) | ||
| 69 | #define MLXPLAT_CPLD_AGGR_MASK_MSN21XX 0x04 | ||
| 70 | #define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0) | ||
| 71 | #define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0) | ||
| 72 | #define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0) | ||
| 73 | |||
| 59 | /* Start channel numbers */ | 74 | /* Start channel numbers */ |
| 60 | #define MLXPLAT_CPLD_CH1 2 | 75 | #define MLXPLAT_CPLD_CH1 2 |
| 61 | #define MLXPLAT_CPLD_CH2 10 | 76 | #define MLXPLAT_CPLD_CH2 10 |
| @@ -123,7 +138,7 @@ static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = { | |||
| 123 | }; | 138 | }; |
| 124 | 139 | ||
| 125 | /* Platform hotplug devices */ | 140 | /* Platform hotplug devices */ |
| 126 | static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_psu[] = { | 141 | static struct mlxcpld_hotplug_device mlxplat_mlxcpld_psu[] = { |
| 127 | { | 142 | { |
| 128 | .brdinfo = { I2C_BOARD_INFO("24c02", 0x51) }, | 143 | .brdinfo = { I2C_BOARD_INFO("24c02", 0x51) }, |
| 129 | .bus = 10, | 144 | .bus = 10, |
| @@ -134,7 +149,7 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_psu[] = { | |||
| 134 | }, | 149 | }, |
| 135 | }; | 150 | }; |
| 136 | 151 | ||
| 137 | static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_pwr[] = { | 152 | static struct mlxcpld_hotplug_device mlxplat_mlxcpld_pwr[] = { |
| 138 | { | 153 | { |
| 139 | .brdinfo = { I2C_BOARD_INFO("dps460", 0x59) }, | 154 | .brdinfo = { I2C_BOARD_INFO("dps460", 0x59) }, |
| 140 | .bus = 10, | 155 | .bus = 10, |
| @@ -145,7 +160,7 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_pwr[] = { | |||
| 145 | }, | 160 | }, |
| 146 | }; | 161 | }; |
| 147 | 162 | ||
| 148 | static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_fan[] = { | 163 | static struct mlxcpld_hotplug_device mlxplat_mlxcpld_fan[] = { |
| 149 | { | 164 | { |
| 150 | .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) }, | 165 | .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) }, |
| 151 | .bus = 11, | 166 | .bus = 11, |
| @@ -166,38 +181,38 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_fan[] = { | |||
| 166 | 181 | ||
| 167 | /* Platform hotplug default data */ | 182 | /* Platform hotplug default data */ |
| 168 | static | 183 | static |
| 169 | struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_hotplug_default_data = { | 184 | struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_default_data = { |
| 170 | .top_aggr_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x3a), | 185 | .top_aggr_offset = MLXPLAT_CPLD_LPC_REG_AGGR_ADRR, |
| 171 | .top_aggr_mask = 0x48, | 186 | .top_aggr_mask = MLXPLAT_CPLD_AGGR_MASK_DEF, |
| 172 | .top_aggr_psu_mask = 0x08, | 187 | .top_aggr_psu_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF, |
| 173 | .psu_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x58), | 188 | .psu_reg_offset = MLXPLAT_CPLD_LPC_REG_PSU_ADRR, |
| 174 | .psu_mask = 0x03, | 189 | .psu_mask = MLXPLAT_CPLD_PSU_MASK, |
| 175 | .psu_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_psu), | 190 | .psu_count = ARRAY_SIZE(mlxplat_mlxcpld_psu), |
| 176 | .psu = mlxplat_mlxcpld_hotplug_psu, | 191 | .psu = mlxplat_mlxcpld_psu, |
| 177 | .top_aggr_pwr_mask = 0x08, | 192 | .top_aggr_pwr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF, |
| 178 | .pwr_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x64), | 193 | .pwr_reg_offset = MLXPLAT_CPLD_LPC_REG_PWR_ADRR, |
| 179 | .pwr_mask = 0x03, | 194 | .pwr_mask = MLXPLAT_CPLD_PWR_MASK, |
| 180 | .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_pwr), | 195 | .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), |
| 181 | .pwr = mlxplat_mlxcpld_hotplug_pwr, | 196 | .pwr = mlxplat_mlxcpld_pwr, |
| 182 | .top_aggr_fan_mask = 0x40, | 197 | .top_aggr_fan_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF, |
| 183 | .fan_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x88), | 198 | .fan_reg_offset = MLXPLAT_CPLD_LPC_REG_FAN_ADRR, |
| 184 | .fan_mask = 0x0f, | 199 | .fan_mask = MLXPLAT_CPLD_FAN_MASK, |
| 185 | .fan_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_fan), | 200 | .fan_count = ARRAY_SIZE(mlxplat_mlxcpld_fan), |
| 186 | .fan = mlxplat_mlxcpld_hotplug_fan, | 201 | .fan = mlxplat_mlxcpld_fan, |
| 187 | }; | 202 | }; |
| 188 | 203 | ||
| 189 | /* Platform hotplug MSN21xx system family data */ | 204 | /* Platform hotplug MSN21xx system family data */ |
| 190 | static | 205 | static |
| 191 | struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_hotplug_msn21xx_data = { | 206 | struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data = { |
| 192 | .top_aggr_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x3a), | 207 | .top_aggr_offset = MLXPLAT_CPLD_LPC_REG_AGGR_ADRR, |
| 193 | .top_aggr_mask = 0x04, | 208 | .top_aggr_mask = MLXPLAT_CPLD_AGGR_MASK_MSN21XX, |
| 194 | .top_aggr_pwr_mask = 0x04, | 209 | .top_aggr_pwr_mask = MLXPLAT_CPLD_AGGR_MASK_MSN21XX, |
| 195 | .pwr_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x64), | 210 | .pwr_reg_offset = MLXPLAT_CPLD_LPC_REG_PWR_ADRR, |
| 196 | .pwr_mask = 0x03, | 211 | .pwr_mask = MLXPLAT_CPLD_PWR_MASK, |
| 197 | .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_pwr), | 212 | .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), |
| 198 | }; | 213 | }; |
| 199 | 214 | ||
| 200 | static struct resource mlxplat_mlxcpld_hotplug_resources[] = { | 215 | static struct resource mlxplat_mlxcpld_resources[] = { |
| 201 | [0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"), | 216 | [0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"), |
| 202 | }; | 217 | }; |
| 203 | 218 | ||
| @@ -213,7 +228,7 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) | |||
| 213 | mlxplat_mux_data[i].n_values = | 228 | mlxplat_mux_data[i].n_values = |
| 214 | ARRAY_SIZE(mlxplat_default_channels[i]); | 229 | ARRAY_SIZE(mlxplat_default_channels[i]); |
| 215 | } | 230 | } |
| 216 | mlxplat_hotplug = &mlxplat_mlxcpld_hotplug_default_data; | 231 | mlxplat_hotplug = &mlxplat_mlxcpld_default_data; |
| 217 | 232 | ||
| 218 | return 1; | 233 | return 1; |
| 219 | }; | 234 | }; |
| @@ -227,7 +242,7 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) | |||
| 227 | mlxplat_mux_data[i].n_values = | 242 | mlxplat_mux_data[i].n_values = |
| 228 | ARRAY_SIZE(mlxplat_msn21xx_channels); | 243 | ARRAY_SIZE(mlxplat_msn21xx_channels); |
| 229 | } | 244 | } |
| 230 | mlxplat_hotplug = &mlxplat_mlxcpld_hotplug_msn21xx_data; | 245 | mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data; |
| 231 | 246 | ||
| 232 | return 1; | 247 | return 1; |
| 233 | }; | 248 | }; |
| @@ -314,9 +329,10 @@ static int __init mlxplat_init(void) | |||
| 314 | } | 329 | } |
| 315 | 330 | ||
| 316 | priv->pdev_hotplug = platform_device_register_resndata( | 331 | priv->pdev_hotplug = platform_device_register_resndata( |
| 317 | &mlxplat_dev->dev, "mlxcpld-hotplug", -1, | 332 | &mlxplat_dev->dev, "mlxcpld-hotplug", |
| 318 | mlxplat_mlxcpld_hotplug_resources, | 333 | PLATFORM_DEVID_NONE, |
| 319 | ARRAY_SIZE(mlxplat_mlxcpld_hotplug_resources), | 334 | mlxplat_mlxcpld_resources, |
| 335 | ARRAY_SIZE(mlxplat_mlxcpld_resources), | ||
| 320 | mlxplat_hotplug, sizeof(*mlxplat_hotplug)); | 336 | mlxplat_hotplug, sizeof(*mlxplat_hotplug)); |
| 321 | if (IS_ERR(priv->pdev_hotplug)) { | 337 | if (IS_ERR(priv->pdev_hotplug)) { |
| 322 | err = PTR_ERR(priv->pdev_hotplug); | 338 | err = PTR_ERR(priv->pdev_hotplug); |
diff --git a/drivers/platform/x86/silead_dmi.c b/drivers/platform/x86/silead_dmi.c new file mode 100644 index 000000000000..02e11fdbf375 --- /dev/null +++ b/drivers/platform/x86/silead_dmi.c | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | /* | ||
| 2 | * Silead touchscreen driver DMI based configuration code | ||
| 3 | * | ||
| 4 | * Copyright (c) 2017 Red Hat Inc. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * Red Hat authors: | ||
| 12 | * Hans de Goede <hdegoede@redhat.com> | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/acpi.h> | ||
| 16 | #include <linux/device.h> | ||
| 17 | #include <linux/dmi.h> | ||
| 18 | #include <linux/i2c.h> | ||
| 19 | #include <linux/notifier.h> | ||
| 20 | #include <linux/property.h> | ||
| 21 | #include <linux/string.h> | ||
| 22 | |||
| 23 | struct silead_ts_dmi_data { | ||
| 24 | const char *acpi_name; | ||
| 25 | struct property_entry *properties; | ||
| 26 | }; | ||
| 27 | |||
| 28 | static struct property_entry cube_iwork8_air_props[] = { | ||
| 29 | PROPERTY_ENTRY_U32("touchscreen-size-x", 1660), | ||
| 30 | PROPERTY_ENTRY_U32("touchscreen-size-y", 900), | ||
| 31 | PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), | ||
| 32 | PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-cube-iwork8-air.fw"), | ||
| 33 | PROPERTY_ENTRY_U32("silead,max-fingers", 10), | ||
| 34 | { } | ||
| 35 | }; | ||
| 36 | |||
| 37 | static const struct silead_ts_dmi_data cube_iwork8_air_data = { | ||
| 38 | .acpi_name = "MSSL1680:00", | ||
| 39 | .properties = cube_iwork8_air_props, | ||
| 40 | }; | ||
| 41 | |||
| 42 | static struct property_entry jumper_ezpad_mini3_props[] = { | ||
| 43 | PROPERTY_ENTRY_U32("touchscreen-size-x", 1700), | ||
| 44 | PROPERTY_ENTRY_U32("touchscreen-size-y", 1150), | ||
| 45 | PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), | ||
| 46 | PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-jumper-ezpad-mini3.fw"), | ||
| 47 | PROPERTY_ENTRY_U32("silead,max-fingers", 10), | ||
| 48 | { } | ||
| 49 | }; | ||
| 50 | |||
| 51 | static const struct silead_ts_dmi_data jumper_ezpad_mini3_data = { | ||
| 52 | .acpi_name = "MSSL1680:00", | ||
| 53 | .properties = jumper_ezpad_mini3_props, | ||
| 54 | }; | ||
| 55 | |||
| 56 | static const struct dmi_system_id silead_ts_dmi_table[] = { | ||
| 57 | { | ||
| 58 | /* CUBE iwork8 Air */ | ||
| 59 | .driver_data = (void *)&cube_iwork8_air_data, | ||
| 60 | .matches = { | ||
| 61 | DMI_MATCH(DMI_SYS_VENDOR, "cube"), | ||
| 62 | DMI_MATCH(DMI_PRODUCT_NAME, "i1-TF"), | ||
| 63 | DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), | ||
| 64 | }, | ||
| 65 | }, | ||
| 66 | { | ||
| 67 | /* Jumper EZpad mini3 */ | ||
| 68 | .driver_data = (void *)&jumper_ezpad_mini3_data, | ||
| 69 | .matches = { | ||
| 70 | DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), | ||
| 71 | /* jumperx.T87.KFBNEEA02 with the version-nr dropped */ | ||
| 72 | DMI_MATCH(DMI_BIOS_VERSION, "jumperx.T87.KFBNEEA"), | ||
| 73 | }, | ||
| 74 | }, | ||
| 75 | { }, | ||
| 76 | }; | ||
| 77 | |||
| 78 | static void silead_ts_dmi_add_props(struct device *dev) | ||
| 79 | { | ||
| 80 | struct i2c_client *client = to_i2c_client(dev); | ||
| 81 | const struct dmi_system_id *dmi_id; | ||
| 82 | const struct silead_ts_dmi_data *ts_data; | ||
| 83 | int error; | ||
| 84 | |||
| 85 | dmi_id = dmi_first_match(silead_ts_dmi_table); | ||
| 86 | if (!dmi_id) | ||
| 87 | return; | ||
| 88 | |||
| 89 | ts_data = dmi_id->driver_data; | ||
| 90 | if (has_acpi_companion(dev) && | ||
| 91 | !strncmp(ts_data->acpi_name, client->name, I2C_NAME_SIZE)) { | ||
| 92 | error = device_add_properties(dev, ts_data->properties); | ||
| 93 | if (error) | ||
| 94 | dev_err(dev, "failed to add properties: %d\n", error); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | static int silead_ts_dmi_notifier_call(struct notifier_block *nb, | ||
| 99 | unsigned long action, void *data) | ||
| 100 | { | ||
| 101 | struct device *dev = data; | ||
| 102 | |||
| 103 | switch (action) { | ||
| 104 | case BUS_NOTIFY_ADD_DEVICE: | ||
| 105 | silead_ts_dmi_add_props(dev); | ||
| 106 | break; | ||
| 107 | |||
| 108 | default: | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | |||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | static struct notifier_block silead_ts_dmi_notifier = { | ||
| 116 | .notifier_call = silead_ts_dmi_notifier_call, | ||
| 117 | }; | ||
| 118 | |||
| 119 | static int __init silead_ts_dmi_init(void) | ||
| 120 | { | ||
| 121 | int error; | ||
| 122 | |||
| 123 | error = bus_register_notifier(&i2c_bus_type, &silead_ts_dmi_notifier); | ||
| 124 | if (error) | ||
| 125 | pr_err("%s: failed to register i2c bus notifier: %d\n", | ||
| 126 | __func__, error); | ||
| 127 | |||
| 128 | return error; | ||
| 129 | } | ||
| 130 | |||
| 131 | /* | ||
| 132 | * We are registering out notifier after i2c core is initialized and i2c bus | ||
| 133 | * itself is ready (which happens at postcore initcall level), but before | ||
| 134 | * ACPI starts enumerating devices (at subsys initcall level). | ||
| 135 | */ | ||
| 136 | arch_initcall(silead_ts_dmi_init); | ||
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index cacb43fb1df7..1d18b32628ec 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
| @@ -163,6 +163,7 @@ enum tpacpi_hkey_event_t { | |||
| 163 | TP_HKEY_EV_HOTKEY_BASE = 0x1001, /* first hotkey (FN+F1) */ | 163 | TP_HKEY_EV_HOTKEY_BASE = 0x1001, /* first hotkey (FN+F1) */ |
| 164 | TP_HKEY_EV_BRGHT_UP = 0x1010, /* Brightness up */ | 164 | TP_HKEY_EV_BRGHT_UP = 0x1010, /* Brightness up */ |
| 165 | TP_HKEY_EV_BRGHT_DOWN = 0x1011, /* Brightness down */ | 165 | TP_HKEY_EV_BRGHT_DOWN = 0x1011, /* Brightness down */ |
| 166 | TP_HKEY_EV_KBD_LIGHT = 0x1012, /* Thinklight/kbd backlight */ | ||
| 166 | TP_HKEY_EV_VOL_UP = 0x1015, /* Volume up or unmute */ | 167 | TP_HKEY_EV_VOL_UP = 0x1015, /* Volume up or unmute */ |
| 167 | TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */ | 168 | TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */ |
| 168 | TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */ | 169 | TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */ |
| @@ -372,11 +373,9 @@ enum led_status_t { | |||
| 372 | TPACPI_LED_BLINK, | 373 | TPACPI_LED_BLINK, |
| 373 | }; | 374 | }; |
| 374 | 375 | ||
| 375 | /* Special LED class that can defer work */ | 376 | /* tpacpi LED class */ |
| 376 | struct tpacpi_led_classdev { | 377 | struct tpacpi_led_classdev { |
| 377 | struct led_classdev led_classdev; | 378 | struct led_classdev led_classdev; |
| 378 | struct work_struct work; | ||
| 379 | enum led_status_t new_state; | ||
| 380 | int led; | 379 | int led; |
| 381 | }; | 380 | }; |
| 382 | 381 | ||
| @@ -1959,7 +1958,7 @@ enum { /* Positions of some of the keys in hotkey masks */ | |||
| 1959 | TP_ACPI_HKEY_HIBERNATE_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF12, | 1958 | TP_ACPI_HKEY_HIBERNATE_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF12, |
| 1960 | TP_ACPI_HKEY_BRGHTUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNHOME, | 1959 | TP_ACPI_HKEY_BRGHTUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNHOME, |
| 1961 | TP_ACPI_HKEY_BRGHTDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNEND, | 1960 | TP_ACPI_HKEY_BRGHTDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNEND, |
| 1962 | TP_ACPI_HKEY_THNKLGHT_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP, | 1961 | TP_ACPI_HKEY_KBD_LIGHT_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP, |
| 1963 | TP_ACPI_HKEY_ZOOM_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNSPACE, | 1962 | TP_ACPI_HKEY_ZOOM_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNSPACE, |
| 1964 | TP_ACPI_HKEY_VOLUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEUP, | 1963 | TP_ACPI_HKEY_VOLUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEUP, |
| 1965 | TP_ACPI_HKEY_VOLDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, | 1964 | 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) | |||
| 2344 | n->display_toggle = !!(d & TP_NVRAM_MASK_HKT_DISPLAY); | 2343 | n->display_toggle = !!(d & TP_NVRAM_MASK_HKT_DISPLAY); |
| 2345 | n->hibernate_toggle = !!(d & TP_NVRAM_MASK_HKT_HIBERNATE); | 2344 | n->hibernate_toggle = !!(d & TP_NVRAM_MASK_HKT_HIBERNATE); |
| 2346 | } | 2345 | } |
| 2347 | if (m & TP_ACPI_HKEY_THNKLGHT_MASK) { | 2346 | if (m & TP_ACPI_HKEY_KBD_LIGHT_MASK) { |
| 2348 | d = nvram_read_byte(TP_NVRAM_ADDR_THINKLIGHT); | 2347 | d = nvram_read_byte(TP_NVRAM_ADDR_THINKLIGHT); |
| 2349 | n->thinklight_toggle = !!(d & TP_NVRAM_MASK_THINKLIGHT); | 2348 | n->thinklight_toggle = !!(d & TP_NVRAM_MASK_THINKLIGHT); |
| 2350 | } | 2349 | } |
| @@ -5084,18 +5083,27 @@ static struct ibm_struct video_driver_data = { | |||
| 5084 | * Keyboard backlight subdriver | 5083 | * Keyboard backlight subdriver |
| 5085 | */ | 5084 | */ |
| 5086 | 5085 | ||
| 5086 | static enum led_brightness kbdlight_brightness; | ||
| 5087 | static DEFINE_MUTEX(kbdlight_mutex); | ||
| 5088 | |||
| 5087 | static int kbdlight_set_level(int level) | 5089 | static int kbdlight_set_level(int level) |
| 5088 | { | 5090 | { |
| 5091 | int ret = 0; | ||
| 5092 | |||
| 5089 | if (!hkey_handle) | 5093 | if (!hkey_handle) |
| 5090 | return -ENXIO; | 5094 | return -ENXIO; |
| 5091 | 5095 | ||
| 5096 | mutex_lock(&kbdlight_mutex); | ||
| 5097 | |||
| 5092 | if (!acpi_evalf(hkey_handle, NULL, "MLCS", "dd", level)) | 5098 | if (!acpi_evalf(hkey_handle, NULL, "MLCS", "dd", level)) |
| 5093 | return -EIO; | 5099 | ret = -EIO; |
| 5100 | else | ||
| 5101 | kbdlight_brightness = level; | ||
| 5094 | 5102 | ||
| 5095 | return 0; | 5103 | mutex_unlock(&kbdlight_mutex); |
| 5096 | } | ||
| 5097 | 5104 | ||
| 5098 | static int kbdlight_set_level_and_update(int level); | 5105 | return ret; |
| 5106 | } | ||
| 5099 | 5107 | ||
| 5100 | static int kbdlight_get_level(void) | 5108 | static int kbdlight_get_level(void) |
| 5101 | { | 5109 | { |
| @@ -5158,24 +5166,10 @@ static bool kbdlight_is_supported(void) | |||
| 5158 | return status & BIT(9); | 5166 | return status & BIT(9); |
| 5159 | } | 5167 | } |
| 5160 | 5168 | ||
| 5161 | static void kbdlight_set_worker(struct work_struct *work) | 5169 | static int kbdlight_sysfs_set(struct led_classdev *led_cdev, |
| 5162 | { | ||
| 5163 | struct tpacpi_led_classdev *data = | ||
| 5164 | container_of(work, struct tpacpi_led_classdev, work); | ||
| 5165 | |||
| 5166 | if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) | ||
| 5167 | kbdlight_set_level_and_update(data->new_state); | ||
| 5168 | } | ||
| 5169 | |||
| 5170 | static void kbdlight_sysfs_set(struct led_classdev *led_cdev, | ||
| 5171 | enum led_brightness brightness) | 5170 | enum led_brightness brightness) |
| 5172 | { | 5171 | { |
| 5173 | struct tpacpi_led_classdev *data = | 5172 | return kbdlight_set_level(brightness); |
| 5174 | container_of(led_cdev, | ||
| 5175 | struct tpacpi_led_classdev, | ||
| 5176 | led_classdev); | ||
| 5177 | data->new_state = brightness; | ||
| 5178 | queue_work(tpacpi_wq, &data->work); | ||
| 5179 | } | 5173 | } |
| 5180 | 5174 | ||
| 5181 | static enum led_brightness kbdlight_sysfs_get(struct led_classdev *led_cdev) | 5175 | static enum led_brightness kbdlight_sysfs_get(struct led_classdev *led_cdev) |
| @@ -5193,7 +5187,8 @@ static struct tpacpi_led_classdev tpacpi_led_kbdlight = { | |||
| 5193 | .led_classdev = { | 5187 | .led_classdev = { |
| 5194 | .name = "tpacpi::kbd_backlight", | 5188 | .name = "tpacpi::kbd_backlight", |
| 5195 | .max_brightness = 2, | 5189 | .max_brightness = 2, |
| 5196 | .brightness_set = &kbdlight_sysfs_set, | 5190 | .flags = LED_BRIGHT_HW_CHANGED, |
| 5191 | .brightness_set_blocking = &kbdlight_sysfs_set, | ||
| 5197 | .brightness_get = &kbdlight_sysfs_get, | 5192 | .brightness_get = &kbdlight_sysfs_get, |
| 5198 | } | 5193 | } |
| 5199 | }; | 5194 | }; |
| @@ -5205,7 +5200,6 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm) | |||
| 5205 | vdbg_printk(TPACPI_DBG_INIT, "initializing kbdlight subdriver\n"); | 5200 | vdbg_printk(TPACPI_DBG_INIT, "initializing kbdlight subdriver\n"); |
| 5206 | 5201 | ||
| 5207 | TPACPI_ACPIHANDLE_INIT(hkey); | 5202 | TPACPI_ACPIHANDLE_INIT(hkey); |
| 5208 | INIT_WORK(&tpacpi_led_kbdlight.work, kbdlight_set_worker); | ||
| 5209 | 5203 | ||
| 5210 | if (!kbdlight_is_supported()) { | 5204 | if (!kbdlight_is_supported()) { |
| 5211 | tp_features.kbdlight = 0; | 5205 | tp_features.kbdlight = 0; |
| @@ -5213,6 +5207,7 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm) | |||
| 5213 | return 1; | 5207 | return 1; |
| 5214 | } | 5208 | } |
| 5215 | 5209 | ||
| 5210 | kbdlight_brightness = kbdlight_sysfs_get(NULL); | ||
| 5216 | tp_features.kbdlight = 1; | 5211 | tp_features.kbdlight = 1; |
| 5217 | 5212 | ||
| 5218 | rc = led_classdev_register(&tpacpi_pdev->dev, | 5213 | rc = led_classdev_register(&tpacpi_pdev->dev, |
| @@ -5222,6 +5217,8 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm) | |||
| 5222 | return rc; | 5217 | return rc; |
| 5223 | } | 5218 | } |
| 5224 | 5219 | ||
| 5220 | tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | | ||
| 5221 | TP_ACPI_HKEY_KBD_LIGHT_MASK); | ||
| 5225 | return 0; | 5222 | return 0; |
| 5226 | } | 5223 | } |
| 5227 | 5224 | ||
| @@ -5229,7 +5226,6 @@ static void kbdlight_exit(void) | |||
| 5229 | { | 5226 | { |
| 5230 | if (tp_features.kbdlight) | 5227 | if (tp_features.kbdlight) |
| 5231 | led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev); | 5228 | led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev); |
| 5232 | flush_workqueue(tpacpi_wq); | ||
| 5233 | } | 5229 | } |
| 5234 | 5230 | ||
| 5235 | static int kbdlight_set_level_and_update(int level) | 5231 | static int kbdlight_set_level_and_update(int level) |
| @@ -5358,25 +5354,11 @@ static int light_set_status(int status) | |||
| 5358 | return -ENXIO; | 5354 | return -ENXIO; |
| 5359 | } | 5355 | } |
| 5360 | 5356 | ||
| 5361 | static void light_set_status_worker(struct work_struct *work) | 5357 | static int light_sysfs_set(struct led_classdev *led_cdev, |
| 5362 | { | ||
| 5363 | struct tpacpi_led_classdev *data = | ||
| 5364 | container_of(work, struct tpacpi_led_classdev, work); | ||
| 5365 | |||
| 5366 | if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) | ||
| 5367 | light_set_status((data->new_state != TPACPI_LED_OFF)); | ||
| 5368 | } | ||
| 5369 | |||
| 5370 | static void light_sysfs_set(struct led_classdev *led_cdev, | ||
| 5371 | enum led_brightness brightness) | 5358 | enum led_brightness brightness) |
| 5372 | { | 5359 | { |
| 5373 | struct tpacpi_led_classdev *data = | 5360 | return light_set_status((brightness != LED_OFF) ? |
| 5374 | container_of(led_cdev, | 5361 | TPACPI_LED_ON : TPACPI_LED_OFF); |
| 5375 | struct tpacpi_led_classdev, | ||
| 5376 | led_classdev); | ||
| 5377 | data->new_state = (brightness != LED_OFF) ? | ||
| 5378 | TPACPI_LED_ON : TPACPI_LED_OFF; | ||
| 5379 | queue_work(tpacpi_wq, &data->work); | ||
| 5380 | } | 5362 | } |
| 5381 | 5363 | ||
| 5382 | static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev) | 5364 | 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) | |||
| 5387 | static struct tpacpi_led_classdev tpacpi_led_thinklight = { | 5369 | static struct tpacpi_led_classdev tpacpi_led_thinklight = { |
| 5388 | .led_classdev = { | 5370 | .led_classdev = { |
| 5389 | .name = "tpacpi::thinklight", | 5371 | .name = "tpacpi::thinklight", |
| 5390 | .brightness_set = &light_sysfs_set, | 5372 | .brightness_set_blocking = &light_sysfs_set, |
| 5391 | .brightness_get = &light_sysfs_get, | 5373 | .brightness_get = &light_sysfs_get, |
| 5392 | } | 5374 | } |
| 5393 | }; | 5375 | }; |
| @@ -5403,7 +5385,6 @@ static int __init light_init(struct ibm_init_struct *iibm) | |||
| 5403 | TPACPI_ACPIHANDLE_INIT(lght); | 5385 | TPACPI_ACPIHANDLE_INIT(lght); |
| 5404 | } | 5386 | } |
| 5405 | TPACPI_ACPIHANDLE_INIT(cmos); | 5387 | TPACPI_ACPIHANDLE_INIT(cmos); |
| 5406 | INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker); | ||
| 5407 | 5388 | ||
| 5408 | /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ | 5389 | /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ |
| 5409 | tp_features.light = (cmos_handle || lght_handle) && !ledb_handle; | 5390 | tp_features.light = (cmos_handle || lght_handle) && !ledb_handle; |
| @@ -5437,7 +5418,6 @@ static int __init light_init(struct ibm_init_struct *iibm) | |||
| 5437 | static void light_exit(void) | 5418 | static void light_exit(void) |
| 5438 | { | 5419 | { |
| 5439 | led_classdev_unregister(&tpacpi_led_thinklight.led_classdev); | 5420 | led_classdev_unregister(&tpacpi_led_thinklight.led_classdev); |
| 5440 | flush_workqueue(tpacpi_wq); | ||
| 5441 | } | 5421 | } |
| 5442 | 5422 | ||
| 5443 | static int light_read(struct seq_file *m) | 5423 | static int light_read(struct seq_file *m) |
| @@ -5704,29 +5684,21 @@ static int led_set_status(const unsigned int led, | |||
| 5704 | return rc; | 5684 | return rc; |
| 5705 | } | 5685 | } |
| 5706 | 5686 | ||
| 5707 | static void led_set_status_worker(struct work_struct *work) | 5687 | static int led_sysfs_set(struct led_classdev *led_cdev, |
| 5708 | { | ||
| 5709 | struct tpacpi_led_classdev *data = | ||
| 5710 | container_of(work, struct tpacpi_led_classdev, work); | ||
| 5711 | |||
| 5712 | if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) | ||
| 5713 | led_set_status(data->led, data->new_state); | ||
| 5714 | } | ||
| 5715 | |||
| 5716 | static void led_sysfs_set(struct led_classdev *led_cdev, | ||
| 5717 | enum led_brightness brightness) | 5688 | enum led_brightness brightness) |
| 5718 | { | 5689 | { |
| 5719 | struct tpacpi_led_classdev *data = container_of(led_cdev, | 5690 | struct tpacpi_led_classdev *data = container_of(led_cdev, |
| 5720 | struct tpacpi_led_classdev, led_classdev); | 5691 | struct tpacpi_led_classdev, led_classdev); |
| 5692 | enum led_status_t new_state; | ||
| 5721 | 5693 | ||
| 5722 | if (brightness == LED_OFF) | 5694 | if (brightness == LED_OFF) |
| 5723 | data->new_state = TPACPI_LED_OFF; | 5695 | new_state = TPACPI_LED_OFF; |
| 5724 | else if (tpacpi_led_state_cache[data->led] != TPACPI_LED_BLINK) | 5696 | else if (tpacpi_led_state_cache[data->led] != TPACPI_LED_BLINK) |
| 5725 | data->new_state = TPACPI_LED_ON; | 5697 | new_state = TPACPI_LED_ON; |
| 5726 | else | 5698 | else |
| 5727 | data->new_state = TPACPI_LED_BLINK; | 5699 | new_state = TPACPI_LED_BLINK; |
| 5728 | 5700 | ||
| 5729 | queue_work(tpacpi_wq, &data->work); | 5701 | return led_set_status(data->led, new_state); |
| 5730 | } | 5702 | } |
| 5731 | 5703 | ||
| 5732 | static int led_sysfs_blink_set(struct led_classdev *led_cdev, | 5704 | 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, | |||
| 5743 | } else if ((*delay_on != 500) || (*delay_off != 500)) | 5715 | } else if ((*delay_on != 500) || (*delay_off != 500)) |
| 5744 | return -EINVAL; | 5716 | return -EINVAL; |
| 5745 | 5717 | ||
| 5746 | data->new_state = TPACPI_LED_BLINK; | 5718 | return led_set_status(data->led, TPACPI_LED_BLINK); |
| 5747 | queue_work(tpacpi_wq, &data->work); | ||
| 5748 | |||
| 5749 | return 0; | ||
| 5750 | } | 5719 | } |
| 5751 | 5720 | ||
| 5752 | static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev) | 5721 | static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev) |
| @@ -5775,7 +5744,6 @@ static void led_exit(void) | |||
| 5775 | led_classdev_unregister(&tpacpi_leds[i].led_classdev); | 5744 | led_classdev_unregister(&tpacpi_leds[i].led_classdev); |
| 5776 | } | 5745 | } |
| 5777 | 5746 | ||
| 5778 | flush_workqueue(tpacpi_wq); | ||
| 5779 | kfree(tpacpi_leds); | 5747 | kfree(tpacpi_leds); |
| 5780 | } | 5748 | } |
| 5781 | 5749 | ||
| @@ -5789,7 +5757,7 @@ static int __init tpacpi_init_led(unsigned int led) | |||
| 5789 | if (!tpacpi_led_names[led]) | 5757 | if (!tpacpi_led_names[led]) |
| 5790 | return 0; | 5758 | return 0; |
| 5791 | 5759 | ||
| 5792 | tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set; | 5760 | tpacpi_leds[led].led_classdev.brightness_set_blocking = &led_sysfs_set; |
| 5793 | tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; | 5761 | tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; |
| 5794 | if (led_supported == TPACPI_LED_570) | 5762 | if (led_supported == TPACPI_LED_570) |
| 5795 | tpacpi_leds[led].led_classdev.brightness_get = | 5763 | tpacpi_leds[led].led_classdev.brightness_get = |
| @@ -5797,8 +5765,6 @@ static int __init tpacpi_init_led(unsigned int led) | |||
| 5797 | 5765 | ||
| 5798 | tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led]; | 5766 | tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led]; |
| 5799 | 5767 | ||
| 5800 | INIT_WORK(&tpacpi_leds[led].work, led_set_status_worker); | ||
| 5801 | |||
| 5802 | rc = led_classdev_register(&tpacpi_pdev->dev, | 5768 | rc = led_classdev_register(&tpacpi_pdev->dev, |
| 5803 | &tpacpi_leds[led].led_classdev); | 5769 | &tpacpi_leds[led].led_classdev); |
| 5804 | if (rc < 0) | 5770 | if (rc < 0) |
| @@ -9169,6 +9135,24 @@ static void tpacpi_driver_event(const unsigned int hkey_event) | |||
| 9169 | volume_alsa_notify_change(); | 9135 | volume_alsa_notify_change(); |
| 9170 | } | 9136 | } |
| 9171 | } | 9137 | } |
| 9138 | if (tp_features.kbdlight && hkey_event == TP_HKEY_EV_KBD_LIGHT) { | ||
| 9139 | enum led_brightness brightness; | ||
| 9140 | |||
| 9141 | mutex_lock(&kbdlight_mutex); | ||
| 9142 | |||
| 9143 | /* | ||
| 9144 | * Check the brightness actually changed, setting the brightness | ||
| 9145 | * through kbdlight_set_level() also triggers this event. | ||
| 9146 | */ | ||
| 9147 | brightness = kbdlight_sysfs_get(NULL); | ||
| 9148 | if (kbdlight_brightness != brightness) { | ||
| 9149 | kbdlight_brightness = brightness; | ||
| 9150 | led_classdev_notify_brightness_hw_changed( | ||
| 9151 | &tpacpi_led_kbdlight.led_classdev, brightness); | ||
| 9152 | } | ||
| 9153 | |||
| 9154 | mutex_unlock(&kbdlight_mutex); | ||
| 9155 | } | ||
| 9172 | } | 9156 | } |
| 9173 | 9157 | ||
| 9174 | static void hotkey_driver_event(const unsigned int scancode) | 9158 | static void hotkey_driver_event(const unsigned int scancode) |
