1d0504796SArmin Wolf // SPDX-License-Identifier: GPL-2.0-or-later
2d0504796SArmin Wolf /*
3d0504796SArmin Wolf * Linux driver for Uniwill notebooks.
4d0504796SArmin Wolf *
5d0504796SArmin Wolf * Special thanks go to Pőcze Barnabás, Christoffer Sandberg and Werner Sembach
6d0504796SArmin Wolf * for supporting the development of this driver either through prior work or
7d0504796SArmin Wolf * by answering questions regarding the underlying ACPI and WMI interfaces.
8d0504796SArmin Wolf *
9d0504796SArmin Wolf * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
10d0504796SArmin Wolf */
11d0504796SArmin Wolf
12d0504796SArmin Wolf #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13d0504796SArmin Wolf
14d0504796SArmin Wolf #include <linux/acpi.h>
15d0504796SArmin Wolf #include <linux/array_size.h>
16d0504796SArmin Wolf #include <linux/bits.h>
17d0504796SArmin Wolf #include <linux/bitfield.h>
18d0504796SArmin Wolf #include <linux/cleanup.h>
19d0504796SArmin Wolf #include <linux/debugfs.h>
20d0504796SArmin Wolf #include <linux/delay.h>
21d0504796SArmin Wolf #include <linux/device.h>
22d0504796SArmin Wolf #include <linux/device/driver.h>
23d0504796SArmin Wolf #include <linux/dmi.h>
24d0504796SArmin Wolf #include <linux/errno.h>
25d0504796SArmin Wolf #include <linux/fixp-arith.h>
26d0504796SArmin Wolf #include <linux/hwmon.h>
27d0504796SArmin Wolf #include <linux/hwmon-sysfs.h>
28d0504796SArmin Wolf #include <linux/init.h>
29d0504796SArmin Wolf #include <linux/input.h>
30d0504796SArmin Wolf #include <linux/input/sparse-keymap.h>
31d0504796SArmin Wolf #include <linux/kernel.h>
32d0504796SArmin Wolf #include <linux/kstrtox.h>
33d0504796SArmin Wolf #include <linux/leds.h>
34d0504796SArmin Wolf #include <linux/led-class-multicolor.h>
35d0504796SArmin Wolf #include <linux/limits.h>
36d0504796SArmin Wolf #include <linux/list.h>
37d0504796SArmin Wolf #include <linux/minmax.h>
38d0504796SArmin Wolf #include <linux/module.h>
39d0504796SArmin Wolf #include <linux/mutex.h>
40d0504796SArmin Wolf #include <linux/notifier.h>
41d0504796SArmin Wolf #include <linux/platform_device.h>
42d0504796SArmin Wolf #include <linux/pm.h>
43d0504796SArmin Wolf #include <linux/printk.h>
44d0504796SArmin Wolf #include <linux/regmap.h>
45d0504796SArmin Wolf #include <linux/string.h>
46d0504796SArmin Wolf #include <linux/sysfs.h>
47d0504796SArmin Wolf #include <linux/types.h>
48d0504796SArmin Wolf #include <linux/units.h>
49d0504796SArmin Wolf
50d0504796SArmin Wolf #include <acpi/battery.h>
51d0504796SArmin Wolf
52d0504796SArmin Wolf #include "uniwill-wmi.h"
53d0504796SArmin Wolf
54d0504796SArmin Wolf #define EC_ADDR_BAT_POWER_UNIT_1 0x0400
55d0504796SArmin Wolf
56d0504796SArmin Wolf #define EC_ADDR_BAT_POWER_UNIT_2 0x0401
57d0504796SArmin Wolf
58d0504796SArmin Wolf #define EC_ADDR_BAT_DESIGN_CAPACITY_1 0x0402
59d0504796SArmin Wolf
60d0504796SArmin Wolf #define EC_ADDR_BAT_DESIGN_CAPACITY_2 0x0403
61d0504796SArmin Wolf
62d0504796SArmin Wolf #define EC_ADDR_BAT_FULL_CAPACITY_1 0x0404
63d0504796SArmin Wolf
64d0504796SArmin Wolf #define EC_ADDR_BAT_FULL_CAPACITY_2 0x0405
65d0504796SArmin Wolf
66d0504796SArmin Wolf #define EC_ADDR_BAT_DESIGN_VOLTAGE_1 0x0408
67d0504796SArmin Wolf
68d0504796SArmin Wolf #define EC_ADDR_BAT_DESIGN_VOLTAGE_2 0x0409
69d0504796SArmin Wolf
70d0504796SArmin Wolf #define EC_ADDR_BAT_STATUS_1 0x0432
71d0504796SArmin Wolf #define BAT_DISCHARGING BIT(0)
72d0504796SArmin Wolf
73d0504796SArmin Wolf #define EC_ADDR_BAT_STATUS_2 0x0433
74d0504796SArmin Wolf
75d0504796SArmin Wolf #define EC_ADDR_BAT_CURRENT_1 0x0434
76d0504796SArmin Wolf
77d0504796SArmin Wolf #define EC_ADDR_BAT_CURRENT_2 0x0435
78d0504796SArmin Wolf
79d0504796SArmin Wolf #define EC_ADDR_BAT_REMAIN_CAPACITY_1 0x0436
80d0504796SArmin Wolf
81d0504796SArmin Wolf #define EC_ADDR_BAT_REMAIN_CAPACITY_2 0x0437
82d0504796SArmin Wolf
83d0504796SArmin Wolf #define EC_ADDR_BAT_VOLTAGE_1 0x0438
84d0504796SArmin Wolf
85d0504796SArmin Wolf #define EC_ADDR_BAT_VOLTAGE_2 0x0439
86d0504796SArmin Wolf
87d0504796SArmin Wolf #define EC_ADDR_CPU_TEMP 0x043E
88d0504796SArmin Wolf
89d0504796SArmin Wolf #define EC_ADDR_GPU_TEMP 0x044F
90d0504796SArmin Wolf
91d0504796SArmin Wolf #define EC_ADDR_MAIN_FAN_RPM_1 0x0464
92d0504796SArmin Wolf
93d0504796SArmin Wolf #define EC_ADDR_MAIN_FAN_RPM_2 0x0465
94d0504796SArmin Wolf
95d0504796SArmin Wolf #define EC_ADDR_SECOND_FAN_RPM_1 0x046C
96d0504796SArmin Wolf
97d0504796SArmin Wolf #define EC_ADDR_SECOND_FAN_RPM_2 0x046D
98d0504796SArmin Wolf
99d0504796SArmin Wolf #define EC_ADDR_DEVICE_STATUS 0x047B
100d0504796SArmin Wolf #define WIFI_STATUS_ON BIT(7)
101d0504796SArmin Wolf /* BIT(5) is also unset depending on the rfkill state (bluetooth?) */
102d0504796SArmin Wolf
103d0504796SArmin Wolf #define EC_ADDR_BAT_ALERT 0x0494
104d0504796SArmin Wolf
105d0504796SArmin Wolf #define EC_ADDR_BAT_CYCLE_COUNT_1 0x04A6
106d0504796SArmin Wolf
107d0504796SArmin Wolf #define EC_ADDR_BAT_CYCLE_COUNT_2 0x04A7
108d0504796SArmin Wolf
109d0504796SArmin Wolf #define EC_ADDR_PROJECT_ID 0x0740
110d0504796SArmin Wolf
111d0504796SArmin Wolf #define EC_ADDR_AP_OEM 0x0741
112d0504796SArmin Wolf #define ENABLE_MANUAL_CTRL BIT(0)
113d0504796SArmin Wolf #define ITE_KBD_EFFECT_REACTIVE BIT(3)
114d0504796SArmin Wolf #define FAN_ABNORMAL BIT(5)
115d0504796SArmin Wolf
116d0504796SArmin Wolf #define EC_ADDR_SUPPORT_5 0x0742
117d0504796SArmin Wolf #define FAN_TURBO_SUPPORTED BIT(4)
118d0504796SArmin Wolf #define FAN_SUPPORT BIT(5)
119d0504796SArmin Wolf
120d0504796SArmin Wolf #define EC_ADDR_CTGP_DB_CTRL 0x0743
121d0504796SArmin Wolf #define CTGP_DB_GENERAL_ENABLE BIT(0)
122d0504796SArmin Wolf #define CTGP_DB_DB_ENABLE BIT(1)
123d0504796SArmin Wolf #define CTGP_DB_CTGP_ENABLE BIT(2)
124d0504796SArmin Wolf
125d0504796SArmin Wolf #define EC_ADDR_CTGP_OFFSET 0x0744
126d0504796SArmin Wolf
127d0504796SArmin Wolf #define EC_ADDR_TPP_OFFSET 0x0745
128d0504796SArmin Wolf
129d0504796SArmin Wolf #define EC_ADDR_MAX_TGP 0x0746
130d0504796SArmin Wolf
131d0504796SArmin Wolf #define EC_ADDR_LIGHTBAR_AC_CTRL 0x0748
132d0504796SArmin Wolf #define LIGHTBAR_APP_EXISTS BIT(0)
133d0504796SArmin Wolf #define LIGHTBAR_POWER_SAVE BIT(1)
134d0504796SArmin Wolf #define LIGHTBAR_S0_OFF BIT(2)
135d0504796SArmin Wolf #define LIGHTBAR_S3_OFF BIT(3) // Breathing animation when suspended
136d0504796SArmin Wolf #define LIGHTBAR_WELCOME BIT(7) // Rainbow animation
137d0504796SArmin Wolf
138d0504796SArmin Wolf #define EC_ADDR_LIGHTBAR_AC_RED 0x0749
139d0504796SArmin Wolf
140d0504796SArmin Wolf #define EC_ADDR_LIGHTBAR_AC_GREEN 0x074A
141d0504796SArmin Wolf
142d0504796SArmin Wolf #define EC_ADDR_LIGHTBAR_AC_BLUE 0x074B
143d0504796SArmin Wolf
144d0504796SArmin Wolf #define EC_ADDR_BIOS_OEM 0x074E
145d0504796SArmin Wolf #define FN_LOCK_STATUS BIT(4)
146d0504796SArmin Wolf
147d0504796SArmin Wolf #define EC_ADDR_MANUAL_FAN_CTRL 0x0751
148d0504796SArmin Wolf #define FAN_LEVEL_MASK GENMASK(2, 0)
149d0504796SArmin Wolf #define FAN_MODE_TURBO BIT(4)
150d0504796SArmin Wolf #define FAN_MODE_HIGH BIT(5)
151d0504796SArmin Wolf #define FAN_MODE_BOOST BIT(6)
152d0504796SArmin Wolf #define FAN_MODE_USER BIT(7)
153d0504796SArmin Wolf
154d0504796SArmin Wolf #define EC_ADDR_PWM_1 0x075B
155d0504796SArmin Wolf
156d0504796SArmin Wolf #define EC_ADDR_PWM_2 0x075C
157d0504796SArmin Wolf
158d0504796SArmin Wolf /* Unreliable */
159d0504796SArmin Wolf #define EC_ADDR_SUPPORT_1 0x0765
160d0504796SArmin Wolf #define AIRPLANE_MODE BIT(0)
161d0504796SArmin Wolf #define GPS_SWITCH BIT(1)
162d0504796SArmin Wolf #define OVERCLOCK BIT(2)
163d0504796SArmin Wolf #define MACRO_KEY BIT(3)
164d0504796SArmin Wolf #define SHORTCUT_KEY BIT(4)
165d0504796SArmin Wolf #define SUPER_KEY_LOCK BIT(5)
166d0504796SArmin Wolf #define LIGHTBAR BIT(6)
167d0504796SArmin Wolf #define FAN_BOOST BIT(7)
168d0504796SArmin Wolf
169d0504796SArmin Wolf #define EC_ADDR_SUPPORT_2 0x0766
170d0504796SArmin Wolf #define SILENT_MODE BIT(0)
171d0504796SArmin Wolf #define USB_CHARGING BIT(1)
172d0504796SArmin Wolf #define RGB_KEYBOARD BIT(2)
173d0504796SArmin Wolf #define CHINA_MODE BIT(5)
174d0504796SArmin Wolf #define MY_BATTERY BIT(6)
175d0504796SArmin Wolf
176d0504796SArmin Wolf #define EC_ADDR_TRIGGER 0x0767
177d0504796SArmin Wolf #define TRIGGER_SUPER_KEY_LOCK BIT(0)
178d0504796SArmin Wolf #define TRIGGER_LIGHTBAR BIT(1)
179d0504796SArmin Wolf #define TRIGGER_FAN_BOOST BIT(2)
180d0504796SArmin Wolf #define TRIGGER_SILENT_MODE BIT(3)
181d0504796SArmin Wolf #define TRIGGER_USB_CHARGING BIT(4)
182d0504796SArmin Wolf #define RGB_APPLY_COLOR BIT(5)
183d0504796SArmin Wolf #define RGB_LOGO_EFFECT BIT(6)
184d0504796SArmin Wolf #define RGB_RAINBOW_EFFECT BIT(7)
185d0504796SArmin Wolf
186d0504796SArmin Wolf #define EC_ADDR_SWITCH_STATUS 0x0768
187d0504796SArmin Wolf #define SUPER_KEY_LOCK_STATUS BIT(0)
188d0504796SArmin Wolf #define LIGHTBAR_STATUS BIT(1)
189d0504796SArmin Wolf #define FAN_BOOST_STATUS BIT(2)
190d0504796SArmin Wolf #define MACRO_KEY_STATUS BIT(3)
191d0504796SArmin Wolf #define MY_BAT_POWER_BAT_STATUS BIT(4)
192d0504796SArmin Wolf
193d0504796SArmin Wolf #define EC_ADDR_RGB_RED 0x0769
194d0504796SArmin Wolf
195d0504796SArmin Wolf #define EC_ADDR_RGB_GREEN 0x076A
196d0504796SArmin Wolf
197d0504796SArmin Wolf #define EC_ADDR_RGB_BLUE 0x076B
198d0504796SArmin Wolf
199d0504796SArmin Wolf #define EC_ADDR_ROMID_START 0x0770
200d0504796SArmin Wolf #define ROMID_LENGTH 14
201d0504796SArmin Wolf
202d0504796SArmin Wolf #define EC_ADDR_ROMID_EXTRA_1 0x077E
203d0504796SArmin Wolf
204d0504796SArmin Wolf #define EC_ADDR_ROMID_EXTRA_2 0x077F
205d0504796SArmin Wolf
206d0504796SArmin Wolf #define EC_ADDR_BIOS_OEM_2 0x0782
207d0504796SArmin Wolf #define FAN_V2_NEW BIT(0)
208d0504796SArmin Wolf #define FAN_QKEY BIT(1)
209d0504796SArmin Wolf #define FAN_TABLE_OFFICE_MODE BIT(2)
210d0504796SArmin Wolf #define FAN_V3 BIT(3)
211d0504796SArmin Wolf #define DEFAULT_MODE BIT(4)
212d0504796SArmin Wolf
213d0504796SArmin Wolf #define EC_ADDR_PL1_SETTING 0x0783
214d0504796SArmin Wolf
215d0504796SArmin Wolf #define EC_ADDR_PL2_SETTING 0x0784
216d0504796SArmin Wolf
217d0504796SArmin Wolf #define EC_ADDR_PL4_SETTING 0x0785
218d0504796SArmin Wolf
219d0504796SArmin Wolf #define EC_ADDR_FAN_DEFAULT 0x0786
220d0504796SArmin Wolf #define FAN_CURVE_LENGTH 5
221d0504796SArmin Wolf
222d0504796SArmin Wolf #define EC_ADDR_KBD_STATUS 0x078C
223d0504796SArmin Wolf #define KBD_WHITE_ONLY BIT(0) // ~single color
224d0504796SArmin Wolf #define KBD_SINGLE_COLOR_OFF BIT(1)
225d0504796SArmin Wolf #define KBD_TURBO_LEVEL_MASK GENMASK(3, 2)
226d0504796SArmin Wolf #define KBD_APPLY BIT(4)
227d0504796SArmin Wolf #define KBD_BRIGHTNESS GENMASK(7, 5)
228d0504796SArmin Wolf
229d0504796SArmin Wolf #define EC_ADDR_FAN_CTRL 0x078E
230d0504796SArmin Wolf #define FAN3P5 BIT(1)
231d0504796SArmin Wolf #define CHARGING_PROFILE BIT(3)
232d0504796SArmin Wolf #define UNIVERSAL_FAN_CTRL BIT(6)
233d0504796SArmin Wolf
234d0504796SArmin Wolf #define EC_ADDR_BIOS_OEM_3 0x07A3
235d0504796SArmin Wolf #define FAN_REDUCED_DURY_CYCLE BIT(5)
236d0504796SArmin Wolf #define FAN_ALWAYS_ON BIT(6)
237d0504796SArmin Wolf
238d0504796SArmin Wolf #define EC_ADDR_BIOS_BYTE 0x07A4
239d0504796SArmin Wolf #define FN_LOCK_SWITCH BIT(3)
240d0504796SArmin Wolf
241d0504796SArmin Wolf #define EC_ADDR_OEM_3 0x07A5
242d0504796SArmin Wolf #define POWER_LED_MASK GENMASK(1, 0)
243d0504796SArmin Wolf #define POWER_LED_LEFT 0x00
244d0504796SArmin Wolf #define POWER_LED_BOTH 0x01
245d0504796SArmin Wolf #define POWER_LED_NONE 0x02
246d0504796SArmin Wolf #define FAN_QUIET BIT(2)
247d0504796SArmin Wolf #define OVERBOOST BIT(4)
248d0504796SArmin Wolf #define HIGH_POWER BIT(7)
249d0504796SArmin Wolf
250d0504796SArmin Wolf #define EC_ADDR_OEM_4 0x07A6
251d0504796SArmin Wolf #define OVERBOOST_DYN_TEMP_OFF BIT(1)
252d0504796SArmin Wolf #define TOUCHPAD_TOGGLE_OFF BIT(6)
253d0504796SArmin Wolf
254d0504796SArmin Wolf #define EC_ADDR_CHARGE_CTRL 0x07B9
255d0504796SArmin Wolf #define CHARGE_CTRL_MASK GENMASK(6, 0)
256d0504796SArmin Wolf #define CHARGE_CTRL_REACHED BIT(7)
257d0504796SArmin Wolf
258d0504796SArmin Wolf #define EC_ADDR_UNIVERSAL_FAN_CTRL 0x07C5
259d0504796SArmin Wolf #define SPLIT_TABLES BIT(7)
260d0504796SArmin Wolf
261d0504796SArmin Wolf #define EC_ADDR_AP_OEM_6 0x07C6
262d0504796SArmin Wolf #define ENABLE_UNIVERSAL_FAN_CTRL BIT(2)
263d0504796SArmin Wolf #define BATTERY_CHARGE_FULL_OVER_24H BIT(3)
264d0504796SArmin Wolf #define BATTERY_ERM_STATUS_REACHED BIT(4)
265d0504796SArmin Wolf
266d0504796SArmin Wolf #define EC_ADDR_CHARGE_PRIO 0x07CC
267d0504796SArmin Wolf #define CHARGING_PERFORMANCE BIT(7)
268d0504796SArmin Wolf
269d0504796SArmin Wolf /* Same bits as EC_ADDR_LIGHTBAR_AC_CTRL except LIGHTBAR_S3_OFF */
270d0504796SArmin Wolf #define EC_ADDR_LIGHTBAR_BAT_CTRL 0x07E2
271d0504796SArmin Wolf
272d0504796SArmin Wolf #define EC_ADDR_LIGHTBAR_BAT_RED 0x07E3
273d0504796SArmin Wolf
274d0504796SArmin Wolf #define EC_ADDR_LIGHTBAR_BAT_GREEN 0x07E4
275d0504796SArmin Wolf
276d0504796SArmin Wolf #define EC_ADDR_LIGHTBAR_BAT_BLUE 0x07E5
277d0504796SArmin Wolf
278d0504796SArmin Wolf #define EC_ADDR_CPU_TEMP_END_TABLE 0x0F00
279d0504796SArmin Wolf
280d0504796SArmin Wolf #define EC_ADDR_CPU_TEMP_START_TABLE 0x0F10
281d0504796SArmin Wolf
282d0504796SArmin Wolf #define EC_ADDR_CPU_FAN_SPEED_TABLE 0x0F20
283d0504796SArmin Wolf
284d0504796SArmin Wolf #define EC_ADDR_GPU_TEMP_END_TABLE 0x0F30
285d0504796SArmin Wolf
286d0504796SArmin Wolf #define EC_ADDR_GPU_TEMP_START_TABLE 0x0F40
287d0504796SArmin Wolf
288d0504796SArmin Wolf #define EC_ADDR_GPU_FAN_SPEED_TABLE 0x0F50
289d0504796SArmin Wolf
290d0504796SArmin Wolf /*
291d0504796SArmin Wolf * Those two registers technically allow for manual fan control,
292d0504796SArmin Wolf * but are unstable on some models and are likely not meant to
293d0504796SArmin Wolf * be used by applications as they are only accessible when using
294d0504796SArmin Wolf * the WMI interface.
295d0504796SArmin Wolf */
296d0504796SArmin Wolf #define EC_ADDR_PWM_1_WRITEABLE 0x1804
297d0504796SArmin Wolf
298d0504796SArmin Wolf #define EC_ADDR_PWM_2_WRITEABLE 0x1809
299d0504796SArmin Wolf
300d0504796SArmin Wolf #define DRIVER_NAME "uniwill"
301d0504796SArmin Wolf
302d0504796SArmin Wolf /*
303d0504796SArmin Wolf * The OEM software always sleeps up to 6 ms after reading/writing EC
304d0504796SArmin Wolf * registers, so we emulate this behaviour for maximum compatibility.
305d0504796SArmin Wolf */
306d0504796SArmin Wolf #define UNIWILL_EC_DELAY_US 6000
307d0504796SArmin Wolf
308d0504796SArmin Wolf #define PWM_MAX 200
309d0504796SArmin Wolf #define FAN_TABLE_LENGTH 16
310d0504796SArmin Wolf
311d0504796SArmin Wolf #define LED_CHANNELS 3
312d0504796SArmin Wolf #define LED_MAX_BRIGHTNESS 200
313d0504796SArmin Wolf
314d0504796SArmin Wolf #define UNIWILL_FEATURE_FN_LOCK_TOGGLE BIT(0)
315d0504796SArmin Wolf #define UNIWILL_FEATURE_SUPER_KEY_TOGGLE BIT(1)
316d0504796SArmin Wolf #define UNIWILL_FEATURE_TOUCHPAD_TOGGLE BIT(2)
317d0504796SArmin Wolf #define UNIWILL_FEATURE_LIGHTBAR BIT(3)
318d0504796SArmin Wolf #define UNIWILL_FEATURE_BATTERY BIT(4)
319d0504796SArmin Wolf #define UNIWILL_FEATURE_HWMON BIT(5)
320d0504796SArmin Wolf
321d0504796SArmin Wolf struct uniwill_data {
322d0504796SArmin Wolf struct device *dev;
323d0504796SArmin Wolf acpi_handle handle;
324d0504796SArmin Wolf struct regmap *regmap;
325d0504796SArmin Wolf struct acpi_battery_hook hook;
326d0504796SArmin Wolf unsigned int last_charge_ctrl;
327d0504796SArmin Wolf struct mutex battery_lock; /* Protects the list of currently registered batteries */
328d0504796SArmin Wolf unsigned int last_switch_status;
329d0504796SArmin Wolf struct mutex super_key_lock; /* Protects the toggling of the super key lock state */
330d0504796SArmin Wolf struct list_head batteries;
331d0504796SArmin Wolf struct mutex led_lock; /* Protects writes to the lightbar registers */
332d0504796SArmin Wolf struct led_classdev_mc led_mc_cdev;
333d0504796SArmin Wolf struct mc_subled led_mc_subled_info[LED_CHANNELS];
334d0504796SArmin Wolf struct mutex input_lock; /* Protects input sequence during notify */
335d0504796SArmin Wolf struct input_dev *input_device;
336d0504796SArmin Wolf struct notifier_block nb;
337d0504796SArmin Wolf };
338d0504796SArmin Wolf
339d0504796SArmin Wolf struct uniwill_battery_entry {
340d0504796SArmin Wolf struct list_head head;
341d0504796SArmin Wolf struct power_supply *battery;
342d0504796SArmin Wolf };
343d0504796SArmin Wolf
344d0504796SArmin Wolf static bool force;
345d0504796SArmin Wolf module_param_unsafe(force, bool, 0);
346d0504796SArmin Wolf MODULE_PARM_DESC(force, "Force loading without checking for supported devices\n");
347d0504796SArmin Wolf
348d0504796SArmin Wolf /* Feature bitmask since the associated registers are not reliable */
349d0504796SArmin Wolf static unsigned int supported_features;
350d0504796SArmin Wolf
351d0504796SArmin Wolf static const char * const uniwill_temp_labels[] = {
352d0504796SArmin Wolf "CPU",
353d0504796SArmin Wolf "GPU",
354d0504796SArmin Wolf };
355d0504796SArmin Wolf
356d0504796SArmin Wolf static const char * const uniwill_fan_labels[] = {
357d0504796SArmin Wolf "Main",
358d0504796SArmin Wolf "Secondary",
359d0504796SArmin Wolf };
360d0504796SArmin Wolf
361d0504796SArmin Wolf static const struct key_entry uniwill_keymap[] = {
362d0504796SArmin Wolf /* Reported via keyboard controller */
363d0504796SArmin Wolf { KE_IGNORE, UNIWILL_OSD_CAPSLOCK, { KEY_CAPSLOCK }},
364d0504796SArmin Wolf { KE_IGNORE, UNIWILL_OSD_NUMLOCK, { KEY_NUMLOCK }},
365d0504796SArmin Wolf
366d0504796SArmin Wolf /* Reported when the user locks/unlocks the super key */
367d0504796SArmin Wolf { KE_IGNORE, UNIWILL_OSD_SUPER_KEY_LOCK_ENABLE, { KEY_UNKNOWN }},
368d0504796SArmin Wolf { KE_IGNORE, UNIWILL_OSD_SUPER_KEY_LOCK_DISABLE, { KEY_UNKNOWN }},
369d0504796SArmin Wolf /* Optional, might not be reported by all devices */
370d0504796SArmin Wolf { KE_IGNORE, UNIWILL_OSD_SUPER_KEY_LOCK_CHANGED, { KEY_UNKNOWN }},
371d0504796SArmin Wolf
372d0504796SArmin Wolf /* Reported in manual mode when toggling the airplane mode status */
373d0504796SArmin Wolf { KE_KEY, UNIWILL_OSD_RFKILL, { KEY_RFKILL }},
37433303671SWerner Sembach { KE_IGNORE, UNIWILL_OSD_RADIOON, { KEY_UNKNOWN }},
37533303671SWerner Sembach { KE_IGNORE, UNIWILL_OSD_RADIOOFF, { KEY_UNKNOWN }},
376d0504796SArmin Wolf
377d0504796SArmin Wolf /* Reported when user wants to cycle the platform profile */
37833303671SWerner Sembach { KE_KEY, UNIWILL_OSD_PERFORMANCE_MODE_TOGGLE, { KEY_F14 }},
379d0504796SArmin Wolf
380d0504796SArmin Wolf /* Reported when the user wants to adjust the brightness of the keyboard */
381d0504796SArmin Wolf { KE_KEY, UNIWILL_OSD_KBDILLUMDOWN, { KEY_KBDILLUMDOWN }},
382d0504796SArmin Wolf { KE_KEY, UNIWILL_OSD_KBDILLUMUP, { KEY_KBDILLUMUP }},
383d0504796SArmin Wolf
384d0504796SArmin Wolf /* Reported when the user wants to toggle the microphone mute status */
385d0504796SArmin Wolf { KE_KEY, UNIWILL_OSD_MIC_MUTE, { KEY_MICMUTE }},
386d0504796SArmin Wolf
38733303671SWerner Sembach /* Reported when the user wants to toggle the mute status */
38833303671SWerner Sembach { KE_IGNORE, UNIWILL_OSD_MUTE, { KEY_MUTE }},
38933303671SWerner Sembach
390d0504796SArmin Wolf /* Reported when the user locks/unlocks the Fn key */
391d0504796SArmin Wolf { KE_IGNORE, UNIWILL_OSD_FN_LOCK, { KEY_FN_ESC }},
392d0504796SArmin Wolf
393d0504796SArmin Wolf /* Reported when the user wants to toggle the brightness of the keyboard */
394d0504796SArmin Wolf { KE_KEY, UNIWILL_OSD_KBDILLUMTOGGLE, { KEY_KBDILLUMTOGGLE }},
39533303671SWerner Sembach { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL0, { KEY_KBDILLUMTOGGLE }},
39633303671SWerner Sembach { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL1, { KEY_KBDILLUMTOGGLE }},
39733303671SWerner Sembach { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL2, { KEY_KBDILLUMTOGGLE }},
39833303671SWerner Sembach { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL3, { KEY_KBDILLUMTOGGLE }},
39933303671SWerner Sembach { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL4, { KEY_KBDILLUMTOGGLE }},
400d0504796SArmin Wolf
401d0504796SArmin Wolf /* FIXME: find out the exact meaning of those events */
402d0504796SArmin Wolf { KE_IGNORE, UNIWILL_OSD_BAT_CHARGE_FULL_24_H, { KEY_UNKNOWN }},
403d0504796SArmin Wolf { KE_IGNORE, UNIWILL_OSD_BAT_ERM_UPDATE, { KEY_UNKNOWN }},
404d0504796SArmin Wolf
405d0504796SArmin Wolf /* Reported when the user wants to toggle the benchmark mode status */
406d0504796SArmin Wolf { KE_IGNORE, UNIWILL_OSD_BENCHMARK_MODE_TOGGLE, { KEY_UNKNOWN }},
407d0504796SArmin Wolf
40833303671SWerner Sembach /* Reported when the user wants to toggle the webcam */
40933303671SWerner Sembach { KE_IGNORE, UNIWILL_OSD_WEBCAM_TOGGLE, { KEY_UNKNOWN }},
41033303671SWerner Sembach
411d0504796SArmin Wolf { KE_END }
412d0504796SArmin Wolf };
413d0504796SArmin Wolf
uniwill_ec_reg_write(void * context,unsigned int reg,unsigned int val)414d0504796SArmin Wolf static int uniwill_ec_reg_write(void *context, unsigned int reg, unsigned int val)
415d0504796SArmin Wolf {
416d0504796SArmin Wolf union acpi_object params[2] = {
417d0504796SArmin Wolf {
418d0504796SArmin Wolf .integer = {
419d0504796SArmin Wolf .type = ACPI_TYPE_INTEGER,
420d0504796SArmin Wolf .value = reg,
421d0504796SArmin Wolf },
422d0504796SArmin Wolf },
423d0504796SArmin Wolf {
424d0504796SArmin Wolf .integer = {
425d0504796SArmin Wolf .type = ACPI_TYPE_INTEGER,
426d0504796SArmin Wolf .value = val,
427d0504796SArmin Wolf },
428d0504796SArmin Wolf },
429d0504796SArmin Wolf };
430d0504796SArmin Wolf struct uniwill_data *data = context;
431d0504796SArmin Wolf struct acpi_object_list input = {
432d0504796SArmin Wolf .count = ARRAY_SIZE(params),
433d0504796SArmin Wolf .pointer = params,
434d0504796SArmin Wolf };
435d0504796SArmin Wolf acpi_status status;
436d0504796SArmin Wolf
437d0504796SArmin Wolf status = acpi_evaluate_object(data->handle, "ECRW", &input, NULL);
438d0504796SArmin Wolf if (ACPI_FAILURE(status))
439d0504796SArmin Wolf return -EIO;
440d0504796SArmin Wolf
441d0504796SArmin Wolf usleep_range(UNIWILL_EC_DELAY_US, UNIWILL_EC_DELAY_US * 2);
442d0504796SArmin Wolf
443d0504796SArmin Wolf return 0;
444d0504796SArmin Wolf }
445d0504796SArmin Wolf
uniwill_ec_reg_read(void * context,unsigned int reg,unsigned int * val)446d0504796SArmin Wolf static int uniwill_ec_reg_read(void *context, unsigned int reg, unsigned int *val)
447d0504796SArmin Wolf {
448d0504796SArmin Wolf union acpi_object params[1] = {
449d0504796SArmin Wolf {
450d0504796SArmin Wolf .integer = {
451d0504796SArmin Wolf .type = ACPI_TYPE_INTEGER,
452d0504796SArmin Wolf .value = reg,
453d0504796SArmin Wolf },
454d0504796SArmin Wolf },
455d0504796SArmin Wolf };
456d0504796SArmin Wolf struct uniwill_data *data = context;
457d0504796SArmin Wolf struct acpi_object_list input = {
458d0504796SArmin Wolf .count = ARRAY_SIZE(params),
459d0504796SArmin Wolf .pointer = params,
460d0504796SArmin Wolf };
461d0504796SArmin Wolf unsigned long long output;
462d0504796SArmin Wolf acpi_status status;
463d0504796SArmin Wolf
464d0504796SArmin Wolf status = acpi_evaluate_integer(data->handle, "ECRR", &input, &output);
465d0504796SArmin Wolf if (ACPI_FAILURE(status))
466d0504796SArmin Wolf return -EIO;
467d0504796SArmin Wolf
468d0504796SArmin Wolf if (output > U8_MAX)
469d0504796SArmin Wolf return -ENXIO;
470d0504796SArmin Wolf
471d0504796SArmin Wolf usleep_range(UNIWILL_EC_DELAY_US, UNIWILL_EC_DELAY_US * 2);
472d0504796SArmin Wolf
473d0504796SArmin Wolf *val = output;
474d0504796SArmin Wolf
475d0504796SArmin Wolf return 0;
476d0504796SArmin Wolf }
477d0504796SArmin Wolf
478d0504796SArmin Wolf static const struct regmap_bus uniwill_ec_bus = {
479d0504796SArmin Wolf .reg_write = uniwill_ec_reg_write,
480d0504796SArmin Wolf .reg_read = uniwill_ec_reg_read,
481d0504796SArmin Wolf .reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
482d0504796SArmin Wolf .val_format_endian_default = REGMAP_ENDIAN_LITTLE,
483d0504796SArmin Wolf };
484d0504796SArmin Wolf
uniwill_writeable_reg(struct device * dev,unsigned int reg)485d0504796SArmin Wolf static bool uniwill_writeable_reg(struct device *dev, unsigned int reg)
486d0504796SArmin Wolf {
487d0504796SArmin Wolf switch (reg) {
488d0504796SArmin Wolf case EC_ADDR_AP_OEM:
489d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_AC_CTRL:
490d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_AC_RED:
491d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_AC_GREEN:
492d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_AC_BLUE:
493d0504796SArmin Wolf case EC_ADDR_BIOS_OEM:
494d0504796SArmin Wolf case EC_ADDR_TRIGGER:
495d0504796SArmin Wolf case EC_ADDR_OEM_4:
496d0504796SArmin Wolf case EC_ADDR_CHARGE_CTRL:
497d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_BAT_CTRL:
498d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_BAT_RED:
499d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_BAT_GREEN:
500d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_BAT_BLUE:
501d0504796SArmin Wolf return true;
502d0504796SArmin Wolf default:
503d0504796SArmin Wolf return false;
504d0504796SArmin Wolf }
505d0504796SArmin Wolf }
506d0504796SArmin Wolf
uniwill_readable_reg(struct device * dev,unsigned int reg)507d0504796SArmin Wolf static bool uniwill_readable_reg(struct device *dev, unsigned int reg)
508d0504796SArmin Wolf {
509d0504796SArmin Wolf switch (reg) {
510d0504796SArmin Wolf case EC_ADDR_CPU_TEMP:
511d0504796SArmin Wolf case EC_ADDR_GPU_TEMP:
512d0504796SArmin Wolf case EC_ADDR_MAIN_FAN_RPM_1:
513d0504796SArmin Wolf case EC_ADDR_MAIN_FAN_RPM_2:
514d0504796SArmin Wolf case EC_ADDR_SECOND_FAN_RPM_1:
515d0504796SArmin Wolf case EC_ADDR_SECOND_FAN_RPM_2:
516d0504796SArmin Wolf case EC_ADDR_BAT_ALERT:
517d0504796SArmin Wolf case EC_ADDR_PROJECT_ID:
518d0504796SArmin Wolf case EC_ADDR_AP_OEM:
519d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_AC_CTRL:
520d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_AC_RED:
521d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_AC_GREEN:
522d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_AC_BLUE:
523d0504796SArmin Wolf case EC_ADDR_BIOS_OEM:
524d0504796SArmin Wolf case EC_ADDR_PWM_1:
525d0504796SArmin Wolf case EC_ADDR_PWM_2:
526d0504796SArmin Wolf case EC_ADDR_TRIGGER:
527d0504796SArmin Wolf case EC_ADDR_SWITCH_STATUS:
528d0504796SArmin Wolf case EC_ADDR_OEM_4:
529d0504796SArmin Wolf case EC_ADDR_CHARGE_CTRL:
530d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_BAT_CTRL:
531d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_BAT_RED:
532d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_BAT_GREEN:
533d0504796SArmin Wolf case EC_ADDR_LIGHTBAR_BAT_BLUE:
534d0504796SArmin Wolf return true;
535d0504796SArmin Wolf default:
536d0504796SArmin Wolf return false;
537d0504796SArmin Wolf }
538d0504796SArmin Wolf }
539d0504796SArmin Wolf
uniwill_volatile_reg(struct device * dev,unsigned int reg)540d0504796SArmin Wolf static bool uniwill_volatile_reg(struct device *dev, unsigned int reg)
541d0504796SArmin Wolf {
542d0504796SArmin Wolf switch (reg) {
543d0504796SArmin Wolf case EC_ADDR_CPU_TEMP:
544d0504796SArmin Wolf case EC_ADDR_GPU_TEMP:
545d0504796SArmin Wolf case EC_ADDR_MAIN_FAN_RPM_1:
546d0504796SArmin Wolf case EC_ADDR_MAIN_FAN_RPM_2:
547d0504796SArmin Wolf case EC_ADDR_SECOND_FAN_RPM_1:
548d0504796SArmin Wolf case EC_ADDR_SECOND_FAN_RPM_2:
549d0504796SArmin Wolf case EC_ADDR_BAT_ALERT:
550d0504796SArmin Wolf case EC_ADDR_PWM_1:
551d0504796SArmin Wolf case EC_ADDR_PWM_2:
552d0504796SArmin Wolf case EC_ADDR_TRIGGER:
553d0504796SArmin Wolf case EC_ADDR_SWITCH_STATUS:
554d0504796SArmin Wolf case EC_ADDR_CHARGE_CTRL:
555d0504796SArmin Wolf return true;
556d0504796SArmin Wolf default:
557d0504796SArmin Wolf return false;
558d0504796SArmin Wolf }
559d0504796SArmin Wolf }
560d0504796SArmin Wolf
561d0504796SArmin Wolf static const struct regmap_config uniwill_ec_config = {
562d0504796SArmin Wolf .reg_bits = 16,
563d0504796SArmin Wolf .val_bits = 8,
564d0504796SArmin Wolf .writeable_reg = uniwill_writeable_reg,
565d0504796SArmin Wolf .readable_reg = uniwill_readable_reg,
566d0504796SArmin Wolf .volatile_reg = uniwill_volatile_reg,
567d0504796SArmin Wolf .can_sleep = true,
568d0504796SArmin Wolf .max_register = 0xFFF,
569d0504796SArmin Wolf .cache_type = REGCACHE_MAPLE,
570d0504796SArmin Wolf .use_single_read = true,
571d0504796SArmin Wolf .use_single_write = true,
572d0504796SArmin Wolf };
573d0504796SArmin Wolf
fn_lock_toggle_enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)574d0504796SArmin Wolf static ssize_t fn_lock_toggle_enable_store(struct device *dev, struct device_attribute *attr,
575d0504796SArmin Wolf const char *buf, size_t count)
576d0504796SArmin Wolf {
577d0504796SArmin Wolf struct uniwill_data *data = dev_get_drvdata(dev);
578d0504796SArmin Wolf unsigned int value;
579d0504796SArmin Wolf bool enable;
580d0504796SArmin Wolf int ret;
581d0504796SArmin Wolf
582d0504796SArmin Wolf ret = kstrtobool(buf, &enable);
583d0504796SArmin Wolf if (ret < 0)
584d0504796SArmin Wolf return ret;
585d0504796SArmin Wolf
586d0504796SArmin Wolf if (enable)
587d0504796SArmin Wolf value = FN_LOCK_STATUS;
588d0504796SArmin Wolf else
589d0504796SArmin Wolf value = 0;
590d0504796SArmin Wolf
591d0504796SArmin Wolf ret = regmap_update_bits(data->regmap, EC_ADDR_BIOS_OEM, FN_LOCK_STATUS, value);
592d0504796SArmin Wolf if (ret < 0)
593d0504796SArmin Wolf return ret;
594d0504796SArmin Wolf
595d0504796SArmin Wolf return count;
596d0504796SArmin Wolf }
597d0504796SArmin Wolf
fn_lock_toggle_enable_show(struct device * dev,struct device_attribute * attr,char * buf)598d0504796SArmin Wolf static ssize_t fn_lock_toggle_enable_show(struct device *dev, struct device_attribute *attr,
599d0504796SArmin Wolf char *buf)
600d0504796SArmin Wolf {
601d0504796SArmin Wolf struct uniwill_data *data = dev_get_drvdata(dev);
602d0504796SArmin Wolf unsigned int value;
603d0504796SArmin Wolf int ret;
604d0504796SArmin Wolf
605d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_BIOS_OEM, &value);
606d0504796SArmin Wolf if (ret < 0)
607d0504796SArmin Wolf return ret;
608d0504796SArmin Wolf
609d0504796SArmin Wolf return sysfs_emit(buf, "%d\n", !!(value & FN_LOCK_STATUS));
610d0504796SArmin Wolf }
611d0504796SArmin Wolf
612d0504796SArmin Wolf static DEVICE_ATTR_RW(fn_lock_toggle_enable);
613d0504796SArmin Wolf
super_key_toggle_enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)614d0504796SArmin Wolf static ssize_t super_key_toggle_enable_store(struct device *dev, struct device_attribute *attr,
615d0504796SArmin Wolf const char *buf, size_t count)
616d0504796SArmin Wolf {
617d0504796SArmin Wolf struct uniwill_data *data = dev_get_drvdata(dev);
618d0504796SArmin Wolf unsigned int value;
619d0504796SArmin Wolf bool enable;
620d0504796SArmin Wolf int ret;
621d0504796SArmin Wolf
622d0504796SArmin Wolf ret = kstrtobool(buf, &enable);
623d0504796SArmin Wolf if (ret < 0)
624d0504796SArmin Wolf return ret;
625d0504796SArmin Wolf
626d0504796SArmin Wolf guard(mutex)(&data->super_key_lock);
627d0504796SArmin Wolf
628d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value);
629d0504796SArmin Wolf if (ret < 0)
630d0504796SArmin Wolf return ret;
631d0504796SArmin Wolf
632d0504796SArmin Wolf /*
633d0504796SArmin Wolf * We can only toggle the super key lock, so we return early if the setting
634d0504796SArmin Wolf * is already in the correct state.
635d0504796SArmin Wolf */
636d0504796SArmin Wolf if (enable == !(value & SUPER_KEY_LOCK_STATUS))
637d0504796SArmin Wolf return count;
638d0504796SArmin Wolf
639d0504796SArmin Wolf ret = regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_SUPER_KEY_LOCK,
640d0504796SArmin Wolf TRIGGER_SUPER_KEY_LOCK);
641d0504796SArmin Wolf if (ret < 0)
642d0504796SArmin Wolf return ret;
643d0504796SArmin Wolf
644d0504796SArmin Wolf return count;
645d0504796SArmin Wolf }
646d0504796SArmin Wolf
super_key_toggle_enable_show(struct device * dev,struct device_attribute * attr,char * buf)647d0504796SArmin Wolf static ssize_t super_key_toggle_enable_show(struct device *dev, struct device_attribute *attr,
648d0504796SArmin Wolf char *buf)
649d0504796SArmin Wolf {
650d0504796SArmin Wolf struct uniwill_data *data = dev_get_drvdata(dev);
651d0504796SArmin Wolf unsigned int value;
652d0504796SArmin Wolf int ret;
653d0504796SArmin Wolf
654d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value);
655d0504796SArmin Wolf if (ret < 0)
656d0504796SArmin Wolf return ret;
657d0504796SArmin Wolf
658d0504796SArmin Wolf return sysfs_emit(buf, "%d\n", !(value & SUPER_KEY_LOCK_STATUS));
659d0504796SArmin Wolf }
660d0504796SArmin Wolf
661d0504796SArmin Wolf static DEVICE_ATTR_RW(super_key_toggle_enable);
662d0504796SArmin Wolf
touchpad_toggle_enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)663d0504796SArmin Wolf static ssize_t touchpad_toggle_enable_store(struct device *dev, struct device_attribute *attr,
664d0504796SArmin Wolf const char *buf, size_t count)
665d0504796SArmin Wolf {
666d0504796SArmin Wolf struct uniwill_data *data = dev_get_drvdata(dev);
667d0504796SArmin Wolf unsigned int value;
668d0504796SArmin Wolf bool enable;
669d0504796SArmin Wolf int ret;
670d0504796SArmin Wolf
671d0504796SArmin Wolf ret = kstrtobool(buf, &enable);
672d0504796SArmin Wolf if (ret < 0)
673d0504796SArmin Wolf return ret;
674d0504796SArmin Wolf
675d0504796SArmin Wolf if (enable)
676d0504796SArmin Wolf value = 0;
677d0504796SArmin Wolf else
678d0504796SArmin Wolf value = TOUCHPAD_TOGGLE_OFF;
679d0504796SArmin Wolf
680d0504796SArmin Wolf ret = regmap_update_bits(data->regmap, EC_ADDR_OEM_4, TOUCHPAD_TOGGLE_OFF, value);
681d0504796SArmin Wolf if (ret < 0)
682d0504796SArmin Wolf return ret;
683d0504796SArmin Wolf
684d0504796SArmin Wolf return count;
685d0504796SArmin Wolf }
686d0504796SArmin Wolf
touchpad_toggle_enable_show(struct device * dev,struct device_attribute * attr,char * buf)687d0504796SArmin Wolf static ssize_t touchpad_toggle_enable_show(struct device *dev, struct device_attribute *attr,
688d0504796SArmin Wolf char *buf)
689d0504796SArmin Wolf {
690d0504796SArmin Wolf struct uniwill_data *data = dev_get_drvdata(dev);
691d0504796SArmin Wolf unsigned int value;
692d0504796SArmin Wolf int ret;
693d0504796SArmin Wolf
694d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_OEM_4, &value);
695d0504796SArmin Wolf if (ret < 0)
696d0504796SArmin Wolf return ret;
697d0504796SArmin Wolf
698d0504796SArmin Wolf return sysfs_emit(buf, "%d\n", !(value & TOUCHPAD_TOGGLE_OFF));
699d0504796SArmin Wolf }
700d0504796SArmin Wolf
701d0504796SArmin Wolf static DEVICE_ATTR_RW(touchpad_toggle_enable);
702d0504796SArmin Wolf
rainbow_animation_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)703d0504796SArmin Wolf static ssize_t rainbow_animation_store(struct device *dev, struct device_attribute *attr,
704d0504796SArmin Wolf const char *buf, size_t count)
705d0504796SArmin Wolf {
706d0504796SArmin Wolf struct uniwill_data *data = dev_get_drvdata(dev);
707d0504796SArmin Wolf unsigned int value;
708d0504796SArmin Wolf bool enable;
709d0504796SArmin Wolf int ret;
710d0504796SArmin Wolf
711d0504796SArmin Wolf ret = kstrtobool(buf, &enable);
712d0504796SArmin Wolf if (ret < 0)
713d0504796SArmin Wolf return ret;
714d0504796SArmin Wolf
715d0504796SArmin Wolf if (enable)
716d0504796SArmin Wolf value = LIGHTBAR_WELCOME;
717d0504796SArmin Wolf else
718d0504796SArmin Wolf value = 0;
719d0504796SArmin Wolf
720d0504796SArmin Wolf guard(mutex)(&data->led_lock);
721d0504796SArmin Wolf
722d0504796SArmin Wolf ret = regmap_update_bits(data->regmap, EC_ADDR_LIGHTBAR_AC_CTRL, LIGHTBAR_WELCOME, value);
723d0504796SArmin Wolf if (ret < 0)
724d0504796SArmin Wolf return ret;
725d0504796SArmin Wolf
726d0504796SArmin Wolf ret = regmap_update_bits(data->regmap, EC_ADDR_LIGHTBAR_BAT_CTRL, LIGHTBAR_WELCOME, value);
727d0504796SArmin Wolf if (ret < 0)
728d0504796SArmin Wolf return ret;
729d0504796SArmin Wolf
730d0504796SArmin Wolf return count;
731d0504796SArmin Wolf }
732d0504796SArmin Wolf
rainbow_animation_show(struct device * dev,struct device_attribute * attr,char * buf)733d0504796SArmin Wolf static ssize_t rainbow_animation_show(struct device *dev, struct device_attribute *attr, char *buf)
734d0504796SArmin Wolf {
735d0504796SArmin Wolf struct uniwill_data *data = dev_get_drvdata(dev);
736d0504796SArmin Wolf unsigned int value;
737d0504796SArmin Wolf int ret;
738d0504796SArmin Wolf
739d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_LIGHTBAR_AC_CTRL, &value);
740d0504796SArmin Wolf if (ret < 0)
741d0504796SArmin Wolf return ret;
742d0504796SArmin Wolf
743d0504796SArmin Wolf return sysfs_emit(buf, "%d\n", !!(value & LIGHTBAR_WELCOME));
744d0504796SArmin Wolf }
745d0504796SArmin Wolf
746d0504796SArmin Wolf static DEVICE_ATTR_RW(rainbow_animation);
747d0504796SArmin Wolf
breathing_in_suspend_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)748d0504796SArmin Wolf static ssize_t breathing_in_suspend_store(struct device *dev, struct device_attribute *attr,
749d0504796SArmin Wolf const char *buf, size_t count)
750d0504796SArmin Wolf {
751d0504796SArmin Wolf struct uniwill_data *data = dev_get_drvdata(dev);
752d0504796SArmin Wolf unsigned int value;
753d0504796SArmin Wolf bool enable;
754d0504796SArmin Wolf int ret;
755d0504796SArmin Wolf
756d0504796SArmin Wolf ret = kstrtobool(buf, &enable);
757d0504796SArmin Wolf if (ret < 0)
758d0504796SArmin Wolf return ret;
759d0504796SArmin Wolf
760d0504796SArmin Wolf if (enable)
761d0504796SArmin Wolf value = 0;
762d0504796SArmin Wolf else
763d0504796SArmin Wolf value = LIGHTBAR_S3_OFF;
764d0504796SArmin Wolf
765d0504796SArmin Wolf /* We only access a single register here, so we do not need to use data->led_lock */
766d0504796SArmin Wolf ret = regmap_update_bits(data->regmap, EC_ADDR_LIGHTBAR_AC_CTRL, LIGHTBAR_S3_OFF, value);
767d0504796SArmin Wolf if (ret < 0)
768d0504796SArmin Wolf return ret;
769d0504796SArmin Wolf
770d0504796SArmin Wolf return count;
771d0504796SArmin Wolf }
772d0504796SArmin Wolf
breathing_in_suspend_show(struct device * dev,struct device_attribute * attr,char * buf)773d0504796SArmin Wolf static ssize_t breathing_in_suspend_show(struct device *dev, struct device_attribute *attr,
774d0504796SArmin Wolf char *buf)
775d0504796SArmin Wolf {
776d0504796SArmin Wolf struct uniwill_data *data = dev_get_drvdata(dev);
777d0504796SArmin Wolf unsigned int value;
778d0504796SArmin Wolf int ret;
779d0504796SArmin Wolf
780d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_LIGHTBAR_AC_CTRL, &value);
781d0504796SArmin Wolf if (ret < 0)
782d0504796SArmin Wolf return ret;
783d0504796SArmin Wolf
784d0504796SArmin Wolf return sysfs_emit(buf, "%d\n", !(value & LIGHTBAR_S3_OFF));
785d0504796SArmin Wolf }
786d0504796SArmin Wolf
787d0504796SArmin Wolf static DEVICE_ATTR_RW(breathing_in_suspend);
788d0504796SArmin Wolf
789d0504796SArmin Wolf static struct attribute *uniwill_attrs[] = {
790d0504796SArmin Wolf /* Keyboard-related */
791d0504796SArmin Wolf &dev_attr_fn_lock_toggle_enable.attr,
792d0504796SArmin Wolf &dev_attr_super_key_toggle_enable.attr,
793d0504796SArmin Wolf &dev_attr_touchpad_toggle_enable.attr,
794d0504796SArmin Wolf /* Lightbar-related */
795d0504796SArmin Wolf &dev_attr_rainbow_animation.attr,
796d0504796SArmin Wolf &dev_attr_breathing_in_suspend.attr,
797d0504796SArmin Wolf NULL
798d0504796SArmin Wolf };
799d0504796SArmin Wolf
uniwill_attr_is_visible(struct kobject * kobj,struct attribute * attr,int n)800d0504796SArmin Wolf static umode_t uniwill_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
801d0504796SArmin Wolf {
802d0504796SArmin Wolf if (attr == &dev_attr_fn_lock_toggle_enable.attr) {
803d0504796SArmin Wolf if (supported_features & UNIWILL_FEATURE_FN_LOCK_TOGGLE)
804d0504796SArmin Wolf return attr->mode;
805d0504796SArmin Wolf }
806d0504796SArmin Wolf
807d0504796SArmin Wolf if (attr == &dev_attr_super_key_toggle_enable.attr) {
808d0504796SArmin Wolf if (supported_features & UNIWILL_FEATURE_SUPER_KEY_TOGGLE)
809d0504796SArmin Wolf return attr->mode;
810d0504796SArmin Wolf }
811d0504796SArmin Wolf
812d0504796SArmin Wolf if (attr == &dev_attr_touchpad_toggle_enable.attr) {
813d0504796SArmin Wolf if (supported_features & UNIWILL_FEATURE_TOUCHPAD_TOGGLE)
814d0504796SArmin Wolf return attr->mode;
815d0504796SArmin Wolf }
816d0504796SArmin Wolf
817d0504796SArmin Wolf if (attr == &dev_attr_rainbow_animation.attr ||
818d0504796SArmin Wolf attr == &dev_attr_breathing_in_suspend.attr) {
819d0504796SArmin Wolf if (supported_features & UNIWILL_FEATURE_LIGHTBAR)
820d0504796SArmin Wolf return attr->mode;
821d0504796SArmin Wolf }
822d0504796SArmin Wolf
823d0504796SArmin Wolf return 0;
824d0504796SArmin Wolf }
825d0504796SArmin Wolf
826d0504796SArmin Wolf static const struct attribute_group uniwill_group = {
827d0504796SArmin Wolf .is_visible = uniwill_attr_is_visible,
828d0504796SArmin Wolf .attrs = uniwill_attrs,
829d0504796SArmin Wolf };
830d0504796SArmin Wolf
831d0504796SArmin Wolf static const struct attribute_group *uniwill_groups[] = {
832d0504796SArmin Wolf &uniwill_group,
833d0504796SArmin Wolf NULL
834d0504796SArmin Wolf };
835d0504796SArmin Wolf
uniwill_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)836d0504796SArmin Wolf static int uniwill_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
837d0504796SArmin Wolf long *val)
838d0504796SArmin Wolf {
839d0504796SArmin Wolf struct uniwill_data *data = dev_get_drvdata(dev);
840d0504796SArmin Wolf unsigned int value;
841d0504796SArmin Wolf __be16 rpm;
842d0504796SArmin Wolf int ret;
843d0504796SArmin Wolf
844d0504796SArmin Wolf switch (type) {
845d0504796SArmin Wolf case hwmon_temp:
846d0504796SArmin Wolf switch (channel) {
847d0504796SArmin Wolf case 0:
848d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_CPU_TEMP, &value);
849d0504796SArmin Wolf break;
850d0504796SArmin Wolf case 1:
851d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_GPU_TEMP, &value);
852d0504796SArmin Wolf break;
853d0504796SArmin Wolf default:
854d0504796SArmin Wolf return -EOPNOTSUPP;
855d0504796SArmin Wolf }
856d0504796SArmin Wolf
857d0504796SArmin Wolf if (ret < 0)
858d0504796SArmin Wolf return ret;
859d0504796SArmin Wolf
860d0504796SArmin Wolf *val = value * MILLIDEGREE_PER_DEGREE;
861d0504796SArmin Wolf return 0;
862d0504796SArmin Wolf case hwmon_fan:
863d0504796SArmin Wolf switch (channel) {
864d0504796SArmin Wolf case 0:
865d0504796SArmin Wolf ret = regmap_bulk_read(data->regmap, EC_ADDR_MAIN_FAN_RPM_1, &rpm,
866d0504796SArmin Wolf sizeof(rpm));
867d0504796SArmin Wolf break;
868d0504796SArmin Wolf case 1:
869d0504796SArmin Wolf ret = regmap_bulk_read(data->regmap, EC_ADDR_SECOND_FAN_RPM_1, &rpm,
870d0504796SArmin Wolf sizeof(rpm));
871d0504796SArmin Wolf break;
872d0504796SArmin Wolf default:
873d0504796SArmin Wolf return -EOPNOTSUPP;
874d0504796SArmin Wolf }
875d0504796SArmin Wolf
876d0504796SArmin Wolf if (ret < 0)
877d0504796SArmin Wolf return ret;
878d0504796SArmin Wolf
879d0504796SArmin Wolf *val = be16_to_cpu(rpm);
880d0504796SArmin Wolf return 0;
881d0504796SArmin Wolf case hwmon_pwm:
882d0504796SArmin Wolf switch (channel) {
883d0504796SArmin Wolf case 0:
884d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_PWM_1, &value);
885d0504796SArmin Wolf break;
886d0504796SArmin Wolf case 1:
887d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_PWM_2, &value);
888d0504796SArmin Wolf break;
889d0504796SArmin Wolf default:
890d0504796SArmin Wolf return -EOPNOTSUPP;
891d0504796SArmin Wolf }
892d0504796SArmin Wolf
893d0504796SArmin Wolf if (ret < 0)
894d0504796SArmin Wolf return ret;
895d0504796SArmin Wolf
896d0504796SArmin Wolf *val = fixp_linear_interpolate(0, 0, PWM_MAX, U8_MAX, value);
897d0504796SArmin Wolf return 0;
898d0504796SArmin Wolf default:
899d0504796SArmin Wolf return -EOPNOTSUPP;
900d0504796SArmin Wolf }
901d0504796SArmin Wolf }
902d0504796SArmin Wolf
uniwill_read_string(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,const char ** str)903d0504796SArmin Wolf static int uniwill_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
904d0504796SArmin Wolf int channel, const char **str)
905d0504796SArmin Wolf {
906d0504796SArmin Wolf switch (type) {
907d0504796SArmin Wolf case hwmon_temp:
908d0504796SArmin Wolf *str = uniwill_temp_labels[channel];
909d0504796SArmin Wolf return 0;
910d0504796SArmin Wolf case hwmon_fan:
911d0504796SArmin Wolf *str = uniwill_fan_labels[channel];
912d0504796SArmin Wolf return 0;
913d0504796SArmin Wolf default:
914d0504796SArmin Wolf return -EOPNOTSUPP;
915d0504796SArmin Wolf }
916d0504796SArmin Wolf }
917d0504796SArmin Wolf
918d0504796SArmin Wolf static const struct hwmon_ops uniwill_ops = {
919d0504796SArmin Wolf .visible = 0444,
920d0504796SArmin Wolf .read = uniwill_read,
921d0504796SArmin Wolf .read_string = uniwill_read_string,
922d0504796SArmin Wolf };
923d0504796SArmin Wolf
924d0504796SArmin Wolf static const struct hwmon_channel_info * const uniwill_info[] = {
925d0504796SArmin Wolf HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
926d0504796SArmin Wolf HWMON_CHANNEL_INFO(temp,
927d0504796SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL,
928d0504796SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL),
929d0504796SArmin Wolf HWMON_CHANNEL_INFO(fan,
930d0504796SArmin Wolf HWMON_F_INPUT | HWMON_F_LABEL,
931d0504796SArmin Wolf HWMON_F_INPUT | HWMON_F_LABEL),
932d0504796SArmin Wolf HWMON_CHANNEL_INFO(pwm,
933d0504796SArmin Wolf HWMON_PWM_INPUT,
934d0504796SArmin Wolf HWMON_PWM_INPUT),
935d0504796SArmin Wolf NULL
936d0504796SArmin Wolf };
937d0504796SArmin Wolf
938d0504796SArmin Wolf static const struct hwmon_chip_info uniwill_chip_info = {
939d0504796SArmin Wolf .ops = &uniwill_ops,
940d0504796SArmin Wolf .info = uniwill_info,
941d0504796SArmin Wolf };
942d0504796SArmin Wolf
uniwill_hwmon_init(struct uniwill_data * data)943d0504796SArmin Wolf static int uniwill_hwmon_init(struct uniwill_data *data)
944d0504796SArmin Wolf {
945d0504796SArmin Wolf struct device *hdev;
946d0504796SArmin Wolf
947d0504796SArmin Wolf if (!(supported_features & UNIWILL_FEATURE_HWMON))
948d0504796SArmin Wolf return 0;
949d0504796SArmin Wolf
950d0504796SArmin Wolf hdev = devm_hwmon_device_register_with_info(data->dev, "uniwill", data,
951d0504796SArmin Wolf &uniwill_chip_info, NULL);
952d0504796SArmin Wolf
953d0504796SArmin Wolf return PTR_ERR_OR_ZERO(hdev);
954d0504796SArmin Wolf }
955d0504796SArmin Wolf
956d0504796SArmin Wolf static const unsigned int uniwill_led_channel_to_bat_reg[LED_CHANNELS] = {
957d0504796SArmin Wolf EC_ADDR_LIGHTBAR_BAT_RED,
958d0504796SArmin Wolf EC_ADDR_LIGHTBAR_BAT_GREEN,
959d0504796SArmin Wolf EC_ADDR_LIGHTBAR_BAT_BLUE,
960d0504796SArmin Wolf };
961d0504796SArmin Wolf
962d0504796SArmin Wolf static const unsigned int uniwill_led_channel_to_ac_reg[LED_CHANNELS] = {
963d0504796SArmin Wolf EC_ADDR_LIGHTBAR_AC_RED,
964d0504796SArmin Wolf EC_ADDR_LIGHTBAR_AC_GREEN,
965d0504796SArmin Wolf EC_ADDR_LIGHTBAR_AC_BLUE,
966d0504796SArmin Wolf };
967d0504796SArmin Wolf
uniwill_led_brightness_set(struct led_classdev * led_cdev,enum led_brightness brightness)968d0504796SArmin Wolf static int uniwill_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness)
969d0504796SArmin Wolf {
970d0504796SArmin Wolf struct led_classdev_mc *led_mc_cdev = lcdev_to_mccdev(led_cdev);
971d0504796SArmin Wolf struct uniwill_data *data = container_of(led_mc_cdev, struct uniwill_data, led_mc_cdev);
972d0504796SArmin Wolf unsigned int value;
973d0504796SArmin Wolf int ret;
974d0504796SArmin Wolf
975d0504796SArmin Wolf ret = led_mc_calc_color_components(led_mc_cdev, brightness);
976d0504796SArmin Wolf if (ret < 0)
977d0504796SArmin Wolf return ret;
978d0504796SArmin Wolf
979d0504796SArmin Wolf guard(mutex)(&data->led_lock);
980d0504796SArmin Wolf
981d0504796SArmin Wolf for (int i = 0; i < LED_CHANNELS; i++) {
982d0504796SArmin Wolf /* Prevent the brightness values from overflowing */
983d0504796SArmin Wolf value = min(LED_MAX_BRIGHTNESS, data->led_mc_subled_info[i].brightness);
984d0504796SArmin Wolf ret = regmap_write(data->regmap, uniwill_led_channel_to_ac_reg[i], value);
985d0504796SArmin Wolf if (ret < 0)
986d0504796SArmin Wolf return ret;
987d0504796SArmin Wolf
988d0504796SArmin Wolf ret = regmap_write(data->regmap, uniwill_led_channel_to_bat_reg[i], value);
989d0504796SArmin Wolf if (ret < 0)
990d0504796SArmin Wolf return ret;
991d0504796SArmin Wolf }
992d0504796SArmin Wolf
993d0504796SArmin Wolf if (brightness)
994d0504796SArmin Wolf value = 0;
995d0504796SArmin Wolf else
996d0504796SArmin Wolf value = LIGHTBAR_S0_OFF;
997d0504796SArmin Wolf
998d0504796SArmin Wolf ret = regmap_update_bits(data->regmap, EC_ADDR_LIGHTBAR_AC_CTRL, LIGHTBAR_S0_OFF, value);
999d0504796SArmin Wolf if (ret < 0)
1000d0504796SArmin Wolf return ret;
1001d0504796SArmin Wolf
1002d0504796SArmin Wolf return regmap_update_bits(data->regmap, EC_ADDR_LIGHTBAR_BAT_CTRL, LIGHTBAR_S0_OFF, value);
1003d0504796SArmin Wolf }
1004d0504796SArmin Wolf
1005d0504796SArmin Wolf #define LIGHTBAR_MASK (LIGHTBAR_APP_EXISTS | LIGHTBAR_S0_OFF | LIGHTBAR_S3_OFF | LIGHTBAR_WELCOME)
1006d0504796SArmin Wolf
uniwill_led_init(struct uniwill_data * data)1007d0504796SArmin Wolf static int uniwill_led_init(struct uniwill_data *data)
1008d0504796SArmin Wolf {
1009d0504796SArmin Wolf struct led_init_data init_data = {
1010d0504796SArmin Wolf .devicename = DRIVER_NAME,
1011d0504796SArmin Wolf .default_label = "multicolor:" LED_FUNCTION_STATUS,
1012d0504796SArmin Wolf .devname_mandatory = true,
1013d0504796SArmin Wolf };
1014d0504796SArmin Wolf unsigned int color_indices[3] = {
1015d0504796SArmin Wolf LED_COLOR_ID_RED,
1016d0504796SArmin Wolf LED_COLOR_ID_GREEN,
1017d0504796SArmin Wolf LED_COLOR_ID_BLUE,
1018d0504796SArmin Wolf };
1019d0504796SArmin Wolf unsigned int value;
1020d0504796SArmin Wolf int ret;
1021d0504796SArmin Wolf
1022d0504796SArmin Wolf if (!(supported_features & UNIWILL_FEATURE_LIGHTBAR))
1023d0504796SArmin Wolf return 0;
1024d0504796SArmin Wolf
1025d0504796SArmin Wolf ret = devm_mutex_init(data->dev, &data->led_lock);
1026d0504796SArmin Wolf if (ret < 0)
1027d0504796SArmin Wolf return ret;
1028d0504796SArmin Wolf
1029d0504796SArmin Wolf /*
1030d0504796SArmin Wolf * The EC has separate lightbar settings for AC and battery mode,
1031d0504796SArmin Wolf * so we have to ensure that both settings are the same.
1032d0504796SArmin Wolf */
1033d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_LIGHTBAR_AC_CTRL, &value);
1034d0504796SArmin Wolf if (ret < 0)
1035d0504796SArmin Wolf return ret;
1036d0504796SArmin Wolf
1037d0504796SArmin Wolf value |= LIGHTBAR_APP_EXISTS;
1038d0504796SArmin Wolf ret = regmap_write(data->regmap, EC_ADDR_LIGHTBAR_AC_CTRL, value);
1039d0504796SArmin Wolf if (ret < 0)
1040d0504796SArmin Wolf return ret;
1041d0504796SArmin Wolf
1042d0504796SArmin Wolf /*
1043d0504796SArmin Wolf * The breathing animation during suspend is not supported when
1044d0504796SArmin Wolf * running on battery power.
1045d0504796SArmin Wolf */
1046d0504796SArmin Wolf value |= LIGHTBAR_S3_OFF;
1047d0504796SArmin Wolf ret = regmap_update_bits(data->regmap, EC_ADDR_LIGHTBAR_BAT_CTRL, LIGHTBAR_MASK, value);
1048d0504796SArmin Wolf if (ret < 0)
1049d0504796SArmin Wolf return ret;
1050d0504796SArmin Wolf
1051d0504796SArmin Wolf data->led_mc_cdev.led_cdev.color = LED_COLOR_ID_MULTI;
1052d0504796SArmin Wolf data->led_mc_cdev.led_cdev.max_brightness = LED_MAX_BRIGHTNESS;
1053d0504796SArmin Wolf data->led_mc_cdev.led_cdev.flags = LED_REJECT_NAME_CONFLICT;
1054d0504796SArmin Wolf data->led_mc_cdev.led_cdev.brightness_set_blocking = uniwill_led_brightness_set;
1055d0504796SArmin Wolf
1056d0504796SArmin Wolf if (value & LIGHTBAR_S0_OFF)
1057d0504796SArmin Wolf data->led_mc_cdev.led_cdev.brightness = 0;
1058d0504796SArmin Wolf else
1059d0504796SArmin Wolf data->led_mc_cdev.led_cdev.brightness = LED_MAX_BRIGHTNESS;
1060d0504796SArmin Wolf
1061d0504796SArmin Wolf for (int i = 0; i < LED_CHANNELS; i++) {
1062d0504796SArmin Wolf data->led_mc_subled_info[i].color_index = color_indices[i];
1063d0504796SArmin Wolf
1064d0504796SArmin Wolf ret = regmap_read(data->regmap, uniwill_led_channel_to_ac_reg[i], &value);
1065d0504796SArmin Wolf if (ret < 0)
1066d0504796SArmin Wolf return ret;
1067d0504796SArmin Wolf
1068d0504796SArmin Wolf /*
1069d0504796SArmin Wolf * Make sure that the initial intensity value is not greater than
1070d0504796SArmin Wolf * the maximum brightness.
1071d0504796SArmin Wolf */
1072d0504796SArmin Wolf value = min(LED_MAX_BRIGHTNESS, value);
1073d0504796SArmin Wolf ret = regmap_write(data->regmap, uniwill_led_channel_to_ac_reg[i], value);
1074d0504796SArmin Wolf if (ret < 0)
1075d0504796SArmin Wolf return ret;
1076d0504796SArmin Wolf
1077d0504796SArmin Wolf ret = regmap_write(data->regmap, uniwill_led_channel_to_bat_reg[i], value);
1078d0504796SArmin Wolf if (ret < 0)
1079d0504796SArmin Wolf return ret;
1080d0504796SArmin Wolf
1081d0504796SArmin Wolf data->led_mc_subled_info[i].intensity = value;
1082d0504796SArmin Wolf data->led_mc_subled_info[i].channel = i;
1083d0504796SArmin Wolf }
1084d0504796SArmin Wolf
1085d0504796SArmin Wolf data->led_mc_cdev.subled_info = data->led_mc_subled_info;
1086d0504796SArmin Wolf data->led_mc_cdev.num_colors = LED_CHANNELS;
1087d0504796SArmin Wolf
1088d0504796SArmin Wolf return devm_led_classdev_multicolor_register_ext(data->dev, &data->led_mc_cdev,
1089d0504796SArmin Wolf &init_data);
1090d0504796SArmin Wolf }
1091d0504796SArmin Wolf
uniwill_get_property(struct power_supply * psy,const struct power_supply_ext * ext,void * drvdata,enum power_supply_property psp,union power_supply_propval * val)1092d0504796SArmin Wolf static int uniwill_get_property(struct power_supply *psy, const struct power_supply_ext *ext,
1093d0504796SArmin Wolf void *drvdata, enum power_supply_property psp,
1094d0504796SArmin Wolf union power_supply_propval *val)
1095d0504796SArmin Wolf {
1096d0504796SArmin Wolf struct uniwill_data *data = drvdata;
1097d0504796SArmin Wolf union power_supply_propval prop;
1098d0504796SArmin Wolf unsigned int regval;
1099d0504796SArmin Wolf int ret;
1100d0504796SArmin Wolf
1101d0504796SArmin Wolf switch (psp) {
1102d0504796SArmin Wolf case POWER_SUPPLY_PROP_HEALTH:
1103d0504796SArmin Wolf ret = power_supply_get_property_direct(psy, POWER_SUPPLY_PROP_PRESENT, &prop);
1104d0504796SArmin Wolf if (ret < 0)
1105d0504796SArmin Wolf return ret;
1106d0504796SArmin Wolf
1107d0504796SArmin Wolf if (!prop.intval) {
1108d0504796SArmin Wolf val->intval = POWER_SUPPLY_HEALTH_NO_BATTERY;
1109d0504796SArmin Wolf return 0;
1110d0504796SArmin Wolf }
1111d0504796SArmin Wolf
1112d0504796SArmin Wolf ret = power_supply_get_property_direct(psy, POWER_SUPPLY_PROP_STATUS, &prop);
1113d0504796SArmin Wolf if (ret < 0)
1114d0504796SArmin Wolf return ret;
1115d0504796SArmin Wolf
1116d0504796SArmin Wolf if (prop.intval == POWER_SUPPLY_STATUS_UNKNOWN) {
1117d0504796SArmin Wolf val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
1118d0504796SArmin Wolf return 0;
1119d0504796SArmin Wolf }
1120d0504796SArmin Wolf
1121d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_BAT_ALERT, ®val);
1122d0504796SArmin Wolf if (ret < 0)
1123d0504796SArmin Wolf return ret;
1124d0504796SArmin Wolf
1125d0504796SArmin Wolf if (regval) {
1126d0504796SArmin Wolf /* Charging issue */
1127d0504796SArmin Wolf val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
1128d0504796SArmin Wolf return 0;
1129d0504796SArmin Wolf }
1130d0504796SArmin Wolf
1131d0504796SArmin Wolf val->intval = POWER_SUPPLY_HEALTH_GOOD;
1132d0504796SArmin Wolf return 0;
1133d0504796SArmin Wolf case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
1134d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, ®val);
1135d0504796SArmin Wolf if (ret < 0)
1136d0504796SArmin Wolf return ret;
1137d0504796SArmin Wolf
1138d0504796SArmin Wolf val->intval = clamp_val(FIELD_GET(CHARGE_CTRL_MASK, regval), 0, 100);
1139d0504796SArmin Wolf return 0;
1140d0504796SArmin Wolf default:
1141d0504796SArmin Wolf return -EINVAL;
1142d0504796SArmin Wolf }
1143d0504796SArmin Wolf }
1144d0504796SArmin Wolf
uniwill_set_property(struct power_supply * psy,const struct power_supply_ext * ext,void * drvdata,enum power_supply_property psp,const union power_supply_propval * val)1145d0504796SArmin Wolf static int uniwill_set_property(struct power_supply *psy, const struct power_supply_ext *ext,
1146d0504796SArmin Wolf void *drvdata, enum power_supply_property psp,
1147d0504796SArmin Wolf const union power_supply_propval *val)
1148d0504796SArmin Wolf {
1149d0504796SArmin Wolf struct uniwill_data *data = drvdata;
1150d0504796SArmin Wolf
1151d0504796SArmin Wolf switch (psp) {
1152d0504796SArmin Wolf case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
1153d0504796SArmin Wolf if (val->intval < 1 || val->intval > 100)
1154d0504796SArmin Wolf return -EINVAL;
1155d0504796SArmin Wolf
1156d0504796SArmin Wolf return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, CHARGE_CTRL_MASK,
1157d0504796SArmin Wolf val->intval);
1158d0504796SArmin Wolf default:
1159d0504796SArmin Wolf return -EINVAL;
1160d0504796SArmin Wolf }
1161d0504796SArmin Wolf }
1162d0504796SArmin Wolf
uniwill_property_is_writeable(struct power_supply * psy,const struct power_supply_ext * ext,void * drvdata,enum power_supply_property psp)1163d0504796SArmin Wolf static int uniwill_property_is_writeable(struct power_supply *psy,
1164d0504796SArmin Wolf const struct power_supply_ext *ext, void *drvdata,
1165d0504796SArmin Wolf enum power_supply_property psp)
1166d0504796SArmin Wolf {
1167d0504796SArmin Wolf if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD)
1168d0504796SArmin Wolf return true;
1169d0504796SArmin Wolf
1170d0504796SArmin Wolf return false;
1171d0504796SArmin Wolf }
1172d0504796SArmin Wolf
1173d0504796SArmin Wolf static const enum power_supply_property uniwill_properties[] = {
1174d0504796SArmin Wolf POWER_SUPPLY_PROP_HEALTH,
1175d0504796SArmin Wolf POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
1176d0504796SArmin Wolf };
1177d0504796SArmin Wolf
1178d0504796SArmin Wolf static const struct power_supply_ext uniwill_extension = {
1179d0504796SArmin Wolf .name = DRIVER_NAME,
1180d0504796SArmin Wolf .properties = uniwill_properties,
1181d0504796SArmin Wolf .num_properties = ARRAY_SIZE(uniwill_properties),
1182d0504796SArmin Wolf .get_property = uniwill_get_property,
1183d0504796SArmin Wolf .set_property = uniwill_set_property,
1184d0504796SArmin Wolf .property_is_writeable = uniwill_property_is_writeable,
1185d0504796SArmin Wolf };
1186d0504796SArmin Wolf
uniwill_add_battery(struct power_supply * battery,struct acpi_battery_hook * hook)1187d0504796SArmin Wolf static int uniwill_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
1188d0504796SArmin Wolf {
1189d0504796SArmin Wolf struct uniwill_data *data = container_of(hook, struct uniwill_data, hook);
1190d0504796SArmin Wolf struct uniwill_battery_entry *entry;
1191d0504796SArmin Wolf int ret;
1192d0504796SArmin Wolf
1193d0504796SArmin Wolf entry = kzalloc(sizeof(*entry), GFP_KERNEL);
1194d0504796SArmin Wolf if (!entry)
1195d0504796SArmin Wolf return -ENOMEM;
1196d0504796SArmin Wolf
1197d0504796SArmin Wolf ret = power_supply_register_extension(battery, &uniwill_extension, data->dev, data);
1198d0504796SArmin Wolf if (ret < 0) {
1199d0504796SArmin Wolf kfree(entry);
1200d0504796SArmin Wolf return ret;
1201d0504796SArmin Wolf }
1202d0504796SArmin Wolf
1203d0504796SArmin Wolf guard(mutex)(&data->battery_lock);
1204d0504796SArmin Wolf
1205d0504796SArmin Wolf entry->battery = battery;
1206d0504796SArmin Wolf list_add(&entry->head, &data->batteries);
1207d0504796SArmin Wolf
1208d0504796SArmin Wolf return 0;
1209d0504796SArmin Wolf }
1210d0504796SArmin Wolf
uniwill_remove_battery(struct power_supply * battery,struct acpi_battery_hook * hook)1211d0504796SArmin Wolf static int uniwill_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
1212d0504796SArmin Wolf {
1213d0504796SArmin Wolf struct uniwill_data *data = container_of(hook, struct uniwill_data, hook);
1214d0504796SArmin Wolf struct uniwill_battery_entry *entry, *tmp;
1215d0504796SArmin Wolf
1216d0504796SArmin Wolf scoped_guard(mutex, &data->battery_lock) {
1217d0504796SArmin Wolf list_for_each_entry_safe(entry, tmp, &data->batteries, head) {
1218d0504796SArmin Wolf if (entry->battery == battery) {
1219d0504796SArmin Wolf list_del(&entry->head);
1220d0504796SArmin Wolf kfree(entry);
1221d0504796SArmin Wolf break;
1222d0504796SArmin Wolf }
1223d0504796SArmin Wolf }
1224d0504796SArmin Wolf }
1225d0504796SArmin Wolf
1226d0504796SArmin Wolf power_supply_unregister_extension(battery, &uniwill_extension);
1227d0504796SArmin Wolf
1228d0504796SArmin Wolf return 0;
1229d0504796SArmin Wolf }
1230d0504796SArmin Wolf
uniwill_battery_init(struct uniwill_data * data)1231d0504796SArmin Wolf static int uniwill_battery_init(struct uniwill_data *data)
1232d0504796SArmin Wolf {
1233d0504796SArmin Wolf int ret;
1234d0504796SArmin Wolf
1235d0504796SArmin Wolf if (!(supported_features & UNIWILL_FEATURE_BATTERY))
1236d0504796SArmin Wolf return 0;
1237d0504796SArmin Wolf
1238d0504796SArmin Wolf ret = devm_mutex_init(data->dev, &data->battery_lock);
1239d0504796SArmin Wolf if (ret < 0)
1240d0504796SArmin Wolf return ret;
1241d0504796SArmin Wolf
1242d0504796SArmin Wolf INIT_LIST_HEAD(&data->batteries);
1243d0504796SArmin Wolf data->hook.name = "Uniwill Battery Extension";
1244d0504796SArmin Wolf data->hook.add_battery = uniwill_add_battery;
1245d0504796SArmin Wolf data->hook.remove_battery = uniwill_remove_battery;
1246d0504796SArmin Wolf
1247d0504796SArmin Wolf return devm_battery_hook_register(data->dev, &data->hook);
1248d0504796SArmin Wolf }
1249d0504796SArmin Wolf
uniwill_notifier_call(struct notifier_block * nb,unsigned long action,void * dummy)1250d0504796SArmin Wolf static int uniwill_notifier_call(struct notifier_block *nb, unsigned long action, void *dummy)
1251d0504796SArmin Wolf {
1252d0504796SArmin Wolf struct uniwill_data *data = container_of(nb, struct uniwill_data, nb);
1253d0504796SArmin Wolf struct uniwill_battery_entry *entry;
1254d0504796SArmin Wolf
1255d0504796SArmin Wolf switch (action) {
1256d0504796SArmin Wolf case UNIWILL_OSD_BATTERY_ALERT:
1257d0504796SArmin Wolf mutex_lock(&data->battery_lock);
1258d0504796SArmin Wolf list_for_each_entry(entry, &data->batteries, head) {
1259d0504796SArmin Wolf power_supply_changed(entry->battery);
1260d0504796SArmin Wolf }
1261d0504796SArmin Wolf mutex_unlock(&data->battery_lock);
1262d0504796SArmin Wolf
1263d0504796SArmin Wolf return NOTIFY_OK;
126433303671SWerner Sembach case UNIWILL_OSD_DC_ADAPTER_CHANGED:
126533303671SWerner Sembach /* noop for the time being, will change once charging priority
126633303671SWerner Sembach * gets implemented.
126733303671SWerner Sembach */
126833303671SWerner Sembach
126933303671SWerner Sembach return NOTIFY_OK;
1270d0504796SArmin Wolf default:
1271d0504796SArmin Wolf mutex_lock(&data->input_lock);
1272d0504796SArmin Wolf sparse_keymap_report_event(data->input_device, action, 1, true);
1273d0504796SArmin Wolf mutex_unlock(&data->input_lock);
1274d0504796SArmin Wolf
1275d0504796SArmin Wolf return NOTIFY_OK;
1276d0504796SArmin Wolf }
1277d0504796SArmin Wolf }
1278d0504796SArmin Wolf
uniwill_input_init(struct uniwill_data * data)1279d0504796SArmin Wolf static int uniwill_input_init(struct uniwill_data *data)
1280d0504796SArmin Wolf {
1281d0504796SArmin Wolf int ret;
1282d0504796SArmin Wolf
1283d0504796SArmin Wolf ret = devm_mutex_init(data->dev, &data->input_lock);
1284d0504796SArmin Wolf if (ret < 0)
1285d0504796SArmin Wolf return ret;
1286d0504796SArmin Wolf
1287d0504796SArmin Wolf data->input_device = devm_input_allocate_device(data->dev);
1288d0504796SArmin Wolf if (!data->input_device)
1289d0504796SArmin Wolf return -ENOMEM;
1290d0504796SArmin Wolf
1291d0504796SArmin Wolf ret = sparse_keymap_setup(data->input_device, uniwill_keymap, NULL);
1292d0504796SArmin Wolf if (ret < 0)
1293d0504796SArmin Wolf return ret;
1294d0504796SArmin Wolf
1295d0504796SArmin Wolf data->input_device->name = "Uniwill WMI hotkeys";
1296d0504796SArmin Wolf data->input_device->phys = "wmi/input0";
1297d0504796SArmin Wolf data->input_device->id.bustype = BUS_HOST;
1298d0504796SArmin Wolf ret = input_register_device(data->input_device);
1299d0504796SArmin Wolf if (ret < 0)
1300d0504796SArmin Wolf return ret;
1301d0504796SArmin Wolf
1302d0504796SArmin Wolf data->nb.notifier_call = uniwill_notifier_call;
1303d0504796SArmin Wolf
1304d0504796SArmin Wolf return devm_uniwill_wmi_register_notifier(data->dev, &data->nb);
1305d0504796SArmin Wolf }
1306d0504796SArmin Wolf
uniwill_disable_manual_control(void * context)1307d0504796SArmin Wolf static void uniwill_disable_manual_control(void *context)
1308d0504796SArmin Wolf {
1309d0504796SArmin Wolf struct uniwill_data *data = context;
1310d0504796SArmin Wolf
1311d0504796SArmin Wolf regmap_clear_bits(data->regmap, EC_ADDR_AP_OEM, ENABLE_MANUAL_CTRL);
1312d0504796SArmin Wolf }
1313d0504796SArmin Wolf
uniwill_ec_init(struct uniwill_data * data)1314d0504796SArmin Wolf static int uniwill_ec_init(struct uniwill_data *data)
1315d0504796SArmin Wolf {
1316d0504796SArmin Wolf unsigned int value;
1317d0504796SArmin Wolf int ret;
1318d0504796SArmin Wolf
1319d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_PROJECT_ID, &value);
1320d0504796SArmin Wolf if (ret < 0)
1321d0504796SArmin Wolf return ret;
1322d0504796SArmin Wolf
1323d0504796SArmin Wolf dev_dbg(data->dev, "Project ID: %u\n", value);
1324d0504796SArmin Wolf
1325d0504796SArmin Wolf ret = regmap_set_bits(data->regmap, EC_ADDR_AP_OEM, ENABLE_MANUAL_CTRL);
1326d0504796SArmin Wolf if (ret < 0)
1327d0504796SArmin Wolf return ret;
1328d0504796SArmin Wolf
1329d0504796SArmin Wolf return devm_add_action_or_reset(data->dev, uniwill_disable_manual_control, data);
1330d0504796SArmin Wolf }
1331d0504796SArmin Wolf
uniwill_probe(struct platform_device * pdev)1332d0504796SArmin Wolf static int uniwill_probe(struct platform_device *pdev)
1333d0504796SArmin Wolf {
1334d0504796SArmin Wolf struct uniwill_data *data;
1335d0504796SArmin Wolf struct regmap *regmap;
1336d0504796SArmin Wolf acpi_handle handle;
1337d0504796SArmin Wolf int ret;
1338d0504796SArmin Wolf
1339d0504796SArmin Wolf handle = ACPI_HANDLE(&pdev->dev);
1340d0504796SArmin Wolf if (!handle)
1341d0504796SArmin Wolf return -ENODEV;
1342d0504796SArmin Wolf
1343d0504796SArmin Wolf data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
1344d0504796SArmin Wolf if (!data)
1345d0504796SArmin Wolf return -ENOMEM;
1346d0504796SArmin Wolf
1347d0504796SArmin Wolf data->dev = &pdev->dev;
1348d0504796SArmin Wolf data->handle = handle;
1349d0504796SArmin Wolf platform_set_drvdata(pdev, data);
1350d0504796SArmin Wolf
1351d0504796SArmin Wolf regmap = devm_regmap_init(&pdev->dev, &uniwill_ec_bus, data, &uniwill_ec_config);
1352d0504796SArmin Wolf if (IS_ERR(regmap))
1353d0504796SArmin Wolf return PTR_ERR(regmap);
1354d0504796SArmin Wolf
1355d0504796SArmin Wolf data->regmap = regmap;
1356d0504796SArmin Wolf ret = devm_mutex_init(&pdev->dev, &data->super_key_lock);
1357d0504796SArmin Wolf if (ret < 0)
1358d0504796SArmin Wolf return ret;
1359d0504796SArmin Wolf
1360d0504796SArmin Wolf ret = uniwill_ec_init(data);
1361d0504796SArmin Wolf if (ret < 0)
1362d0504796SArmin Wolf return ret;
1363d0504796SArmin Wolf
1364d0504796SArmin Wolf ret = uniwill_battery_init(data);
1365d0504796SArmin Wolf if (ret < 0)
1366d0504796SArmin Wolf return ret;
1367d0504796SArmin Wolf
1368d0504796SArmin Wolf ret = uniwill_led_init(data);
1369d0504796SArmin Wolf if (ret < 0)
1370d0504796SArmin Wolf return ret;
1371d0504796SArmin Wolf
1372d0504796SArmin Wolf ret = uniwill_hwmon_init(data);
1373d0504796SArmin Wolf if (ret < 0)
1374d0504796SArmin Wolf return ret;
1375d0504796SArmin Wolf
1376d0504796SArmin Wolf return uniwill_input_init(data);
1377d0504796SArmin Wolf }
1378d0504796SArmin Wolf
uniwill_shutdown(struct platform_device * pdev)1379d0504796SArmin Wolf static void uniwill_shutdown(struct platform_device *pdev)
1380d0504796SArmin Wolf {
1381d0504796SArmin Wolf struct uniwill_data *data = platform_get_drvdata(pdev);
1382d0504796SArmin Wolf
1383d0504796SArmin Wolf regmap_clear_bits(data->regmap, EC_ADDR_AP_OEM, ENABLE_MANUAL_CTRL);
1384d0504796SArmin Wolf }
1385d0504796SArmin Wolf
uniwill_suspend_keyboard(struct uniwill_data * data)1386d0504796SArmin Wolf static int uniwill_suspend_keyboard(struct uniwill_data *data)
1387d0504796SArmin Wolf {
1388d0504796SArmin Wolf if (!(supported_features & UNIWILL_FEATURE_SUPER_KEY_TOGGLE))
1389d0504796SArmin Wolf return 0;
1390d0504796SArmin Wolf
1391d0504796SArmin Wolf /*
1392d0504796SArmin Wolf * The EC_ADDR_SWITCH_STATUS is marked as volatile, so we have to restore it
1393d0504796SArmin Wolf * ourselves.
1394d0504796SArmin Wolf */
1395d0504796SArmin Wolf return regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &data->last_switch_status);
1396d0504796SArmin Wolf }
1397d0504796SArmin Wolf
uniwill_suspend_battery(struct uniwill_data * data)1398d0504796SArmin Wolf static int uniwill_suspend_battery(struct uniwill_data *data)
1399d0504796SArmin Wolf {
1400d0504796SArmin Wolf if (!(supported_features & UNIWILL_FEATURE_BATTERY))
1401d0504796SArmin Wolf return 0;
1402d0504796SArmin Wolf
1403d0504796SArmin Wolf /*
1404d0504796SArmin Wolf * Save the current charge limit in order to restore it during resume.
1405d0504796SArmin Wolf * We cannot use the regmap code for that since this register needs to
1406d0504796SArmin Wolf * be declared as volatile due to CHARGE_CTRL_REACHED.
1407d0504796SArmin Wolf */
1408d0504796SArmin Wolf return regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &data->last_charge_ctrl);
1409d0504796SArmin Wolf }
1410d0504796SArmin Wolf
uniwill_suspend(struct device * dev)1411d0504796SArmin Wolf static int uniwill_suspend(struct device *dev)
1412d0504796SArmin Wolf {
1413d0504796SArmin Wolf struct uniwill_data *data = dev_get_drvdata(dev);
1414d0504796SArmin Wolf int ret;
1415d0504796SArmin Wolf
1416d0504796SArmin Wolf ret = uniwill_suspend_keyboard(data);
1417d0504796SArmin Wolf if (ret < 0)
1418d0504796SArmin Wolf return ret;
1419d0504796SArmin Wolf
1420d0504796SArmin Wolf ret = uniwill_suspend_battery(data);
1421d0504796SArmin Wolf if (ret < 0)
1422d0504796SArmin Wolf return ret;
1423d0504796SArmin Wolf
1424d0504796SArmin Wolf regcache_cache_only(data->regmap, true);
1425d0504796SArmin Wolf regcache_mark_dirty(data->regmap);
1426d0504796SArmin Wolf
1427d0504796SArmin Wolf return 0;
1428d0504796SArmin Wolf }
1429d0504796SArmin Wolf
uniwill_resume_keyboard(struct uniwill_data * data)1430d0504796SArmin Wolf static int uniwill_resume_keyboard(struct uniwill_data *data)
1431d0504796SArmin Wolf {
1432d0504796SArmin Wolf unsigned int value;
1433d0504796SArmin Wolf int ret;
1434d0504796SArmin Wolf
1435d0504796SArmin Wolf if (!(supported_features & UNIWILL_FEATURE_SUPER_KEY_TOGGLE))
1436d0504796SArmin Wolf return 0;
1437d0504796SArmin Wolf
1438d0504796SArmin Wolf ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value);
1439d0504796SArmin Wolf if (ret < 0)
1440d0504796SArmin Wolf return ret;
1441d0504796SArmin Wolf
1442d0504796SArmin Wolf if ((data->last_switch_status & SUPER_KEY_LOCK_STATUS) == (value & SUPER_KEY_LOCK_STATUS))
1443d0504796SArmin Wolf return 0;
1444d0504796SArmin Wolf
1445d0504796SArmin Wolf return regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_SUPER_KEY_LOCK,
1446d0504796SArmin Wolf TRIGGER_SUPER_KEY_LOCK);
1447d0504796SArmin Wolf }
1448d0504796SArmin Wolf
uniwill_resume_battery(struct uniwill_data * data)1449d0504796SArmin Wolf static int uniwill_resume_battery(struct uniwill_data *data)
1450d0504796SArmin Wolf {
1451d0504796SArmin Wolf if (!(supported_features & UNIWILL_FEATURE_BATTERY))
1452d0504796SArmin Wolf return 0;
1453d0504796SArmin Wolf
1454d0504796SArmin Wolf return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, CHARGE_CTRL_MASK,
1455d0504796SArmin Wolf data->last_charge_ctrl);
1456d0504796SArmin Wolf }
1457d0504796SArmin Wolf
uniwill_resume(struct device * dev)1458d0504796SArmin Wolf static int uniwill_resume(struct device *dev)
1459d0504796SArmin Wolf {
1460d0504796SArmin Wolf struct uniwill_data *data = dev_get_drvdata(dev);
1461d0504796SArmin Wolf int ret;
1462d0504796SArmin Wolf
1463d0504796SArmin Wolf regcache_cache_only(data->regmap, false);
1464d0504796SArmin Wolf
1465d0504796SArmin Wolf ret = regcache_sync(data->regmap);
1466d0504796SArmin Wolf if (ret < 0)
1467d0504796SArmin Wolf return ret;
1468d0504796SArmin Wolf
1469d0504796SArmin Wolf ret = uniwill_resume_keyboard(data);
1470d0504796SArmin Wolf if (ret < 0)
1471d0504796SArmin Wolf return ret;
1472d0504796SArmin Wolf
1473d0504796SArmin Wolf return uniwill_resume_battery(data);
1474d0504796SArmin Wolf }
1475d0504796SArmin Wolf
1476d0504796SArmin Wolf static DEFINE_SIMPLE_DEV_PM_OPS(uniwill_pm_ops, uniwill_suspend, uniwill_resume);
1477d0504796SArmin Wolf
1478d0504796SArmin Wolf /*
1479d0504796SArmin Wolf * We only use the DMI table for auoloading because the ACPI device itself
1480d0504796SArmin Wolf * does not guarantee that the underlying EC implementation is supported.
1481d0504796SArmin Wolf */
1482d0504796SArmin Wolf static const struct acpi_device_id uniwill_id_table[] = {
1483d0504796SArmin Wolf { "INOU0000" },
1484d0504796SArmin Wolf { },
1485d0504796SArmin Wolf };
1486d0504796SArmin Wolf
1487d0504796SArmin Wolf static struct platform_driver uniwill_driver = {
1488d0504796SArmin Wolf .driver = {
1489d0504796SArmin Wolf .name = DRIVER_NAME,
1490d0504796SArmin Wolf .dev_groups = uniwill_groups,
1491d0504796SArmin Wolf .probe_type = PROBE_PREFER_ASYNCHRONOUS,
1492d0504796SArmin Wolf .acpi_match_table = uniwill_id_table,
1493d0504796SArmin Wolf .pm = pm_sleep_ptr(&uniwill_pm_ops),
1494d0504796SArmin Wolf },
1495d0504796SArmin Wolf .probe = uniwill_probe,
1496d0504796SArmin Wolf .shutdown = uniwill_shutdown,
1497d0504796SArmin Wolf };
1498d0504796SArmin Wolf
1499d0504796SArmin Wolf static const struct dmi_system_id uniwill_dmi_table[] __initconst = {
1500d0504796SArmin Wolf {
1501*5c14bff5SWerner Sembach .ident = "XMG FUSION 15",
1502*5c14bff5SWerner Sembach .matches = {
1503*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"),
1504*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71A"),
1505*5c14bff5SWerner Sembach },
1506*5c14bff5SWerner Sembach },
1507*5c14bff5SWerner Sembach {
1508*5c14bff5SWerner Sembach .ident = "XMG FUSION 15",
1509*5c14bff5SWerner Sembach .matches = {
1510*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"),
1511*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71B"),
1512*5c14bff5SWerner Sembach },
1513*5c14bff5SWerner Sembach },
1514*5c14bff5SWerner Sembach {
1515d0504796SArmin Wolf .ident = "Intel NUC x15",
1516d0504796SArmin Wolf .matches = {
1517d0504796SArmin Wolf DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"),
1518d0504796SArmin Wolf DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LAPAC71H"),
1519d0504796SArmin Wolf },
1520d0504796SArmin Wolf .driver_data = (void *)(UNIWILL_FEATURE_FN_LOCK_TOGGLE |
1521d0504796SArmin Wolf UNIWILL_FEATURE_SUPER_KEY_TOGGLE |
1522d0504796SArmin Wolf UNIWILL_FEATURE_TOUCHPAD_TOGGLE |
1523d0504796SArmin Wolf UNIWILL_FEATURE_BATTERY |
1524d0504796SArmin Wolf UNIWILL_FEATURE_HWMON),
1525d0504796SArmin Wolf },
1526d0504796SArmin Wolf {
1527d0504796SArmin Wolf .ident = "Intel NUC x15",
1528d0504796SArmin Wolf .matches = {
1529d0504796SArmin Wolf DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"),
1530d0504796SArmin Wolf DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LAPKC71F"),
1531d0504796SArmin Wolf },
1532d0504796SArmin Wolf .driver_data = (void *)(UNIWILL_FEATURE_FN_LOCK_TOGGLE |
1533d0504796SArmin Wolf UNIWILL_FEATURE_SUPER_KEY_TOGGLE |
1534d0504796SArmin Wolf UNIWILL_FEATURE_TOUCHPAD_TOGGLE |
1535d0504796SArmin Wolf UNIWILL_FEATURE_LIGHTBAR |
1536d0504796SArmin Wolf UNIWILL_FEATURE_BATTERY |
1537d0504796SArmin Wolf UNIWILL_FEATURE_HWMON),
1538d0504796SArmin Wolf },
1539*5c14bff5SWerner Sembach {
1540*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Pro 14 Gen6 Intel",
1541*5c14bff5SWerner Sembach .matches = {
1542*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1543*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxTxX1"),
1544*5c14bff5SWerner Sembach },
1545*5c14bff5SWerner Sembach },
1546*5c14bff5SWerner Sembach {
1547*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Pro 14 Gen6 Intel",
1548*5c14bff5SWerner Sembach .matches = {
1549*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1550*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxTQx1"),
1551*5c14bff5SWerner Sembach },
1552*5c14bff5SWerner Sembach },
1553*5c14bff5SWerner Sembach {
1554*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Pro 14/16 Gen7 Intel",
1555*5c14bff5SWerner Sembach .matches = {
1556*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1557*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxARX1_PHxAQF1"),
1558*5c14bff5SWerner Sembach },
1559*5c14bff5SWerner Sembach },
1560*5c14bff5SWerner Sembach {
1561*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Pro 16 Gen7 Intel/Commodore Omnia-Book Pro Gen 7",
1562*5c14bff5SWerner Sembach .matches = {
1563*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1564*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH6AG01_PH6AQ71_PH6AQI1"),
1565*5c14bff5SWerner Sembach },
1566*5c14bff5SWerner Sembach },
1567*5c14bff5SWerner Sembach {
1568*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Pro 14/16 Gen8 Intel/Commodore Omnia-Book Pro Gen 8",
1569*5c14bff5SWerner Sembach .matches = {
1570*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1571*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH4PRX1_PH6PRX1"),
1572*5c14bff5SWerner Sembach },
1573*5c14bff5SWerner Sembach },
1574*5c14bff5SWerner Sembach {
1575*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Pro 14 Gen8 Intel/Commodore Omnia-Book Pro Gen 8",
1576*5c14bff5SWerner Sembach .matches = {
1577*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1578*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH4PG31"),
1579*5c14bff5SWerner Sembach },
1580*5c14bff5SWerner Sembach },
1581*5c14bff5SWerner Sembach {
1582*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Pro 16 Gen8 Intel",
1583*5c14bff5SWerner Sembach .matches = {
1584*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1585*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH6PG01_PH6PG71"),
1586*5c14bff5SWerner Sembach },
1587*5c14bff5SWerner Sembach },
1588*5c14bff5SWerner Sembach {
1589*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Pro 14/15 Gen9 AMD",
1590*5c14bff5SWerner Sembach .matches = {
1591*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1592*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GXxHRXx"),
1593*5c14bff5SWerner Sembach },
1594*5c14bff5SWerner Sembach },
1595*5c14bff5SWerner Sembach {
1596*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Pro 14/15 Gen9 Intel/Commodore Omnia-Book 15 Gen9",
1597*5c14bff5SWerner Sembach .matches = {
1598*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1599*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GXxMRXx"),
1600*5c14bff5SWerner Sembach },
1601*5c14bff5SWerner Sembach },
1602*5c14bff5SWerner Sembach {
1603*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Pro 14/15 Gen10 AMD",
1604*5c14bff5SWerner Sembach .matches = {
1605*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1606*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxHP4NAx"),
1607*5c14bff5SWerner Sembach },
1608*5c14bff5SWerner Sembach },
1609*5c14bff5SWerner Sembach {
1610*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Pro 14/15 Gen10 AMD",
1611*5c14bff5SWerner Sembach .matches = {
1612*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1613*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxKK4NAx_XxSP4NAx"),
1614*5c14bff5SWerner Sembach },
1615*5c14bff5SWerner Sembach },
1616*5c14bff5SWerner Sembach {
1617*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Pro 15 Gen10 Intel",
1618*5c14bff5SWerner Sembach .matches = {
1619*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1620*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxAR4NAx"),
1621*5c14bff5SWerner Sembach },
1622*5c14bff5SWerner Sembach },
1623*5c14bff5SWerner Sembach {
1624*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Max 15 Gen10 AMD",
1625*5c14bff5SWerner Sembach .matches = {
1626*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1627*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "X5KK45xS_X5SP45xS"),
1628*5c14bff5SWerner Sembach },
1629*5c14bff5SWerner Sembach },
1630*5c14bff5SWerner Sembach {
1631*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Max 16 Gen10 AMD",
1632*5c14bff5SWerner Sembach .matches = {
1633*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1634*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6HP45xU"),
1635*5c14bff5SWerner Sembach },
1636*5c14bff5SWerner Sembach },
1637*5c14bff5SWerner Sembach {
1638*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Max 16 Gen10 AMD",
1639*5c14bff5SWerner Sembach .matches = {
1640*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1641*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6KK45xU_X6SP45xU"),
1642*5c14bff5SWerner Sembach },
1643*5c14bff5SWerner Sembach },
1644*5c14bff5SWerner Sembach {
1645*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Max 15 Gen10 Intel",
1646*5c14bff5SWerner Sembach .matches = {
1647*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1648*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "X5AR45xS"),
1649*5c14bff5SWerner Sembach },
1650*5c14bff5SWerner Sembach },
1651*5c14bff5SWerner Sembach {
1652*5c14bff5SWerner Sembach .ident = "TUXEDO InfinityBook Max 16 Gen10 Intel",
1653*5c14bff5SWerner Sembach .matches = {
1654*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1655*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR55xU"),
1656*5c14bff5SWerner Sembach },
1657*5c14bff5SWerner Sembach },
1658*5c14bff5SWerner Sembach {
1659*5c14bff5SWerner Sembach .ident = "TUXEDO Polaris 15 Gen1 AMD",
1660*5c14bff5SWerner Sembach .matches = {
1661*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1662*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501A1650TI"),
1663*5c14bff5SWerner Sembach },
1664*5c14bff5SWerner Sembach },
1665*5c14bff5SWerner Sembach {
1666*5c14bff5SWerner Sembach .ident = "TUXEDO Polaris 15 Gen1 AMD",
1667*5c14bff5SWerner Sembach .matches = {
1668*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1669*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501A2060"),
1670*5c14bff5SWerner Sembach },
1671*5c14bff5SWerner Sembach },
1672*5c14bff5SWerner Sembach {
1673*5c14bff5SWerner Sembach .ident = "TUXEDO Polaris 17 Gen1 AMD",
1674*5c14bff5SWerner Sembach .matches = {
1675*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1676*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701A1650TI"),
1677*5c14bff5SWerner Sembach },
1678*5c14bff5SWerner Sembach },
1679*5c14bff5SWerner Sembach {
1680*5c14bff5SWerner Sembach .ident = "TUXEDO Polaris 17 Gen1 AMD",
1681*5c14bff5SWerner Sembach .matches = {
1682*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1683*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701A2060"),
1684*5c14bff5SWerner Sembach },
1685*5c14bff5SWerner Sembach },
1686*5c14bff5SWerner Sembach {
1687*5c14bff5SWerner Sembach .ident = "TUXEDO Polaris 15 Gen1 Intel",
1688*5c14bff5SWerner Sembach .matches = {
1689*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1690*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501I1650TI"),
1691*5c14bff5SWerner Sembach },
1692*5c14bff5SWerner Sembach },
1693*5c14bff5SWerner Sembach {
1694*5c14bff5SWerner Sembach .ident = "TUXEDO Polaris 15 Gen1 Intel",
1695*5c14bff5SWerner Sembach .matches = {
1696*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1697*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501I2060"),
1698*5c14bff5SWerner Sembach },
1699*5c14bff5SWerner Sembach },
1700*5c14bff5SWerner Sembach {
1701*5c14bff5SWerner Sembach .ident = "TUXEDO Polaris 17 Gen1 Intel",
1702*5c14bff5SWerner Sembach .matches = {
1703*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1704*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701I1650TI"),
1705*5c14bff5SWerner Sembach },
1706*5c14bff5SWerner Sembach },
1707*5c14bff5SWerner Sembach {
1708*5c14bff5SWerner Sembach .ident = "TUXEDO Polaris 17 Gen1 Intel",
1709*5c14bff5SWerner Sembach .matches = {
1710*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1711*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701I2060"),
1712*5c14bff5SWerner Sembach },
1713*5c14bff5SWerner Sembach },
1714*5c14bff5SWerner Sembach {
1715*5c14bff5SWerner Sembach .ident = "TUXEDO Trinity 15 Intel Gen1",
1716*5c14bff5SWerner Sembach .matches = {
1717*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1718*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "TRINITY1501I"),
1719*5c14bff5SWerner Sembach },
1720*5c14bff5SWerner Sembach },
1721*5c14bff5SWerner Sembach {
1722*5c14bff5SWerner Sembach .ident = "TUXEDO Trinity 17 Intel Gen1",
1723*5c14bff5SWerner Sembach .matches = {
1724*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1725*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "TRINITY1701I"),
1726*5c14bff5SWerner Sembach },
1727*5c14bff5SWerner Sembach },
1728*5c14bff5SWerner Sembach {
1729*5c14bff5SWerner Sembach .ident = "TUXEDO Polaris 15/17 Gen2 AMD",
1730*5c14bff5SWerner Sembach .matches = {
1731*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1732*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxMGxx"),
1733*5c14bff5SWerner Sembach },
1734*5c14bff5SWerner Sembach },
1735*5c14bff5SWerner Sembach {
1736*5c14bff5SWerner Sembach .ident = "TUXEDO Polaris 15/17 Gen2 Intel",
1737*5c14bff5SWerner Sembach .matches = {
1738*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1739*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxNGxx"),
1740*5c14bff5SWerner Sembach },
1741*5c14bff5SWerner Sembach },
1742*5c14bff5SWerner Sembach {
1743*5c14bff5SWerner Sembach .ident = "TUXEDO Stellaris/Polaris 15/17 Gen3 AMD",
1744*5c14bff5SWerner Sembach .matches = {
1745*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1746*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxZGxx"),
1747*5c14bff5SWerner Sembach },
1748*5c14bff5SWerner Sembach },
1749*5c14bff5SWerner Sembach {
1750*5c14bff5SWerner Sembach .ident = "TUXEDO Stellaris/Polaris 15/17 Gen3 Intel",
1751*5c14bff5SWerner Sembach .matches = {
1752*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1753*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxTGxx"),
1754*5c14bff5SWerner Sembach },
1755*5c14bff5SWerner Sembach },
1756*5c14bff5SWerner Sembach {
1757*5c14bff5SWerner Sembach .ident = "TUXEDO Stellaris/Polaris 15/17 Gen4 AMD",
1758*5c14bff5SWerner Sembach .matches = {
1759*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1760*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxRGxx"),
1761*5c14bff5SWerner Sembach },
1762*5c14bff5SWerner Sembach },
1763*5c14bff5SWerner Sembach {
1764*5c14bff5SWerner Sembach .ident = "TUXEDO Stellaris 15 Gen4 Intel",
1765*5c14bff5SWerner Sembach .matches = {
1766*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1767*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxAGxx"),
1768*5c14bff5SWerner Sembach },
1769*5c14bff5SWerner Sembach },
1770*5c14bff5SWerner Sembach {
1771*5c14bff5SWerner Sembach .ident = "TUXEDO Polaris 15/17 Gen5 AMD",
1772*5c14bff5SWerner Sembach .matches = {
1773*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1774*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxXGxx"),
1775*5c14bff5SWerner Sembach },
1776*5c14bff5SWerner Sembach },
1777*5c14bff5SWerner Sembach {
1778*5c14bff5SWerner Sembach .ident = "TUXEDO Stellaris 16 Gen5 AMD",
1779*5c14bff5SWerner Sembach .matches = {
1780*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1781*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6XGxX"),
1782*5c14bff5SWerner Sembach },
1783*5c14bff5SWerner Sembach },
1784*5c14bff5SWerner Sembach {
1785*5c14bff5SWerner Sembach .ident = "TUXEDO Stellaris 16/17 Gen5 Intel/Commodore ORION Gen 5",
1786*5c14bff5SWerner Sembach .matches = {
1787*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1788*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxPXxx"),
1789*5c14bff5SWerner Sembach },
1790*5c14bff5SWerner Sembach },
1791*5c14bff5SWerner Sembach {
1792*5c14bff5SWerner Sembach .ident = "TUXEDO Stellaris Slim 15 Gen6 AMD",
1793*5c14bff5SWerner Sembach .matches = {
1794*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1795*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxHGxx"),
1796*5c14bff5SWerner Sembach },
1797*5c14bff5SWerner Sembach },
1798*5c14bff5SWerner Sembach {
1799*5c14bff5SWerner Sembach .ident = "TUXEDO Stellaris Slim 15 Gen6 Intel/Commodore ORION Slim 15 Gen6",
1800*5c14bff5SWerner Sembach .matches = {
1801*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1802*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM5IXxA"),
1803*5c14bff5SWerner Sembach },
1804*5c14bff5SWerner Sembach },
1805*5c14bff5SWerner Sembach {
1806*5c14bff5SWerner Sembach .ident = "TUXEDO Stellaris 16 Gen6 Intel/Commodore ORION 16 Gen6",
1807*5c14bff5SWerner Sembach .matches = {
1808*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1809*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6IXxB_MB1"),
1810*5c14bff5SWerner Sembach },
1811*5c14bff5SWerner Sembach },
1812*5c14bff5SWerner Sembach {
1813*5c14bff5SWerner Sembach .ident = "TUXEDO Stellaris 16 Gen6 Intel/Commodore ORION 16 Gen6",
1814*5c14bff5SWerner Sembach .matches = {
1815*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1816*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6IXxB_MB2"),
1817*5c14bff5SWerner Sembach },
1818*5c14bff5SWerner Sembach },
1819*5c14bff5SWerner Sembach {
1820*5c14bff5SWerner Sembach .ident = "TUXEDO Stellaris 17 Gen6 Intel/Commodore ORION 17 Gen6",
1821*5c14bff5SWerner Sembach .matches = {
1822*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1823*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM7IXxN"),
1824*5c14bff5SWerner Sembach },
1825*5c14bff5SWerner Sembach },
1826*5c14bff5SWerner Sembach {
1827*5c14bff5SWerner Sembach .ident = "TUXEDO Stellaris 16 Gen7 AMD",
1828*5c14bff5SWerner Sembach .matches = {
1829*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1830*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6FR5xxY"),
1831*5c14bff5SWerner Sembach },
1832*5c14bff5SWerner Sembach },
1833*5c14bff5SWerner Sembach {
1834*5c14bff5SWerner Sembach .ident = "TUXEDO Stellaris 16 Gen7 Intel",
1835*5c14bff5SWerner Sembach .matches = {
1836*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1837*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR5xxY"),
1838*5c14bff5SWerner Sembach },
1839*5c14bff5SWerner Sembach },
1840*5c14bff5SWerner Sembach {
1841*5c14bff5SWerner Sembach .ident = "TUXEDO Stellaris 16 Gen7 Intel",
1842*5c14bff5SWerner Sembach .matches = {
1843*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1844*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR5xxY_mLED"),
1845*5c14bff5SWerner Sembach },
1846*5c14bff5SWerner Sembach },
1847*5c14bff5SWerner Sembach {
1848*5c14bff5SWerner Sembach .ident = "TUXEDO Pulse 14 Gen1 AMD",
1849*5c14bff5SWerner Sembach .matches = {
1850*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1851*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "PULSE1401"),
1852*5c14bff5SWerner Sembach },
1853*5c14bff5SWerner Sembach },
1854*5c14bff5SWerner Sembach {
1855*5c14bff5SWerner Sembach .ident = "TUXEDO Pulse 15 Gen1 AMD",
1856*5c14bff5SWerner Sembach .matches = {
1857*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1858*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "PULSE1501"),
1859*5c14bff5SWerner Sembach },
1860*5c14bff5SWerner Sembach },
1861*5c14bff5SWerner Sembach {
1862*5c14bff5SWerner Sembach .ident = "TUXEDO Pulse 15 Gen2 AMD",
1863*5c14bff5SWerner Sembach .matches = {
1864*5c14bff5SWerner Sembach DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1865*5c14bff5SWerner Sembach DMI_EXACT_MATCH(DMI_BOARD_NAME, "PF5LUXG"),
1866*5c14bff5SWerner Sembach },
1867*5c14bff5SWerner Sembach },
1868d0504796SArmin Wolf { }
1869d0504796SArmin Wolf };
1870d0504796SArmin Wolf MODULE_DEVICE_TABLE(dmi, uniwill_dmi_table);
1871d0504796SArmin Wolf
uniwill_init(void)1872d0504796SArmin Wolf static int __init uniwill_init(void)
1873d0504796SArmin Wolf {
1874d0504796SArmin Wolf const struct dmi_system_id *id;
1875d0504796SArmin Wolf int ret;
1876d0504796SArmin Wolf
1877d0504796SArmin Wolf id = dmi_first_match(uniwill_dmi_table);
1878d0504796SArmin Wolf if (!id) {
1879d0504796SArmin Wolf if (!force)
1880d0504796SArmin Wolf return -ENODEV;
1881d0504796SArmin Wolf
1882d0504796SArmin Wolf /* Assume that the device supports all features */
1883d0504796SArmin Wolf supported_features = UINT_MAX;
1884d0504796SArmin Wolf pr_warn("Loading on a potentially unsupported device\n");
1885d0504796SArmin Wolf } else {
1886d0504796SArmin Wolf supported_features = (uintptr_t)id->driver_data;
1887d0504796SArmin Wolf }
1888d0504796SArmin Wolf
1889d0504796SArmin Wolf ret = platform_driver_register(&uniwill_driver);
1890d0504796SArmin Wolf if (ret < 0)
1891d0504796SArmin Wolf return ret;
1892d0504796SArmin Wolf
1893d0504796SArmin Wolf ret = uniwill_wmi_register_driver();
1894d0504796SArmin Wolf if (ret < 0) {
1895d0504796SArmin Wolf platform_driver_unregister(&uniwill_driver);
1896d0504796SArmin Wolf return ret;
1897d0504796SArmin Wolf }
1898d0504796SArmin Wolf
1899d0504796SArmin Wolf return 0;
1900d0504796SArmin Wolf }
1901d0504796SArmin Wolf module_init(uniwill_init);
1902d0504796SArmin Wolf
uniwill_exit(void)1903d0504796SArmin Wolf static void __exit uniwill_exit(void)
1904d0504796SArmin Wolf {
1905d0504796SArmin Wolf uniwill_wmi_unregister_driver();
1906d0504796SArmin Wolf platform_driver_unregister(&uniwill_driver);
1907d0504796SArmin Wolf }
1908d0504796SArmin Wolf module_exit(uniwill_exit);
1909d0504796SArmin Wolf
1910d0504796SArmin Wolf MODULE_AUTHOR("Armin Wolf <W_Armin@gmx.de>");
1911d0504796SArmin Wolf MODULE_DESCRIPTION("Uniwill notebook driver");
1912d0504796SArmin Wolf MODULE_LICENSE("GPL");
1913