xref: /linux/drivers/platform/x86/uniwill/uniwill-acpi.c (revision 9d588a1140b9ae211581a7a154d0b806d8cd8238)
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, &regval);
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, &regval);
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