xref: /openbmc/linux/drivers/platform/x86/asus-wmi.c (revision 278002edb19bce2c628fafb0af936e77000f3a5b)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
25b799d4fSCorentin Chary /*
3e12e6d94SCorentin Chary  * Asus PC WMI hotkey driver
45b799d4fSCorentin Chary  *
55b799d4fSCorentin Chary  * Copyright(C) 2010 Intel Corporation.
657ab7daeSCorentin Chary  * Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>
75b799d4fSCorentin Chary  *
85b799d4fSCorentin Chary  * Portions based on wistron_btns.c:
95b799d4fSCorentin Chary  * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
105b799d4fSCorentin Chary  * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
115b799d4fSCorentin Chary  * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
125b799d4fSCorentin Chary  */
135b799d4fSCorentin Chary 
145b799d4fSCorentin Chary #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
155b799d4fSCorentin Chary 
16c545a70dSAndy Shevchenko #include <linux/acpi.h>
175b799d4fSCorentin Chary #include <linux/backlight.h>
18c545a70dSAndy Shevchenko #include <linux/debugfs.h>
19c545a70dSAndy Shevchenko #include <linux/dmi.h>
20c545a70dSAndy Shevchenko #include <linux/fb.h>
21e07babdeSCorentin Chary #include <linux/hwmon.h>
22e07babdeSCorentin Chary #include <linux/hwmon-sysfs.h>
23c545a70dSAndy Shevchenko #include <linux/init.h>
24c545a70dSAndy Shevchenko #include <linux/input.h>
25c545a70dSAndy Shevchenko #include <linux/input/sparse-keymap.h>
26c545a70dSAndy Shevchenko #include <linux/kernel.h>
27c545a70dSAndy Shevchenko #include <linux/leds.h>
28c545a70dSAndy Shevchenko #include <linux/module.h>
29c545a70dSAndy Shevchenko #include <linux/pci.h>
30c545a70dSAndy Shevchenko #include <linux/pci_hotplug.h>
31ffb6ce70SDaniel Drake #include <linux/platform_data/x86/asus-wmi.h>
325b799d4fSCorentin Chary #include <linux/platform_device.h>
33c545a70dSAndy Shevchenko #include <linux/platform_profile.h>
34c545a70dSAndy Shevchenko #include <linux/power_supply.h>
35c545a70dSAndy Shevchenko #include <linux/rfkill.h>
36c545a70dSAndy Shevchenko #include <linux/seq_file.h>
37c545a70dSAndy Shevchenko #include <linux/slab.h>
38c545a70dSAndy Shevchenko #include <linux/types.h>
39f07b9fdfSAkinobu Mita #include <linux/units.h>
407973353eSKristian Klausen 
417973353eSKristian Klausen #include <acpi/battery.h>
42272c77d5SAceLan Kao #include <acpi/video.h>
435b799d4fSCorentin Chary 
44e12e6d94SCorentin Chary #include "asus-wmi.h"
455b799d4fSCorentin Chary 
46522fbca4SAndy Shevchenko MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>");
47522fbca4SAndy Shevchenko MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>");
48e12e6d94SCorentin Chary MODULE_DESCRIPTION("Asus Generic WMI Driver");
495b799d4fSCorentin Chary MODULE_LICENSE("GPL");
505b799d4fSCorentin Chary 
51ce357fd3SLuca Stefani static bool fnlock_default = true;
52ce357fd3SLuca Stefani module_param(fnlock_default, bool, 0444);
53ce357fd3SLuca Stefani 
54e12e6d94SCorentin Chary #define to_asus_wmi_driver(pdrv)					\
55e12e6d94SCorentin Chary 	(container_of((pdrv), struct asus_wmi_driver, platform_driver))
565b799d4fSCorentin Chary 
57e12e6d94SCorentin Chary #define ASUS_WMI_MGMT_GUID	"97845ED0-4E6D-11DE-8A39-0800200C9A66"
585b799d4fSCorentin Chary 
595b799d4fSCorentin Chary #define NOTIFY_BRNUP_MIN		0x11
605b799d4fSCorentin Chary #define NOTIFY_BRNUP_MAX		0x1f
615b799d4fSCorentin Chary #define NOTIFY_BRNDOWN_MIN		0x20
625b799d4fSCorentin Chary #define NOTIFY_BRNDOWN_MAX		0x2e
63487579baSChris Chiu #define NOTIFY_FNLOCK_TOGGLE		0x4e
64b0dbd97dSHans de Goede #define NOTIFY_KBD_DOCK_CHANGE		0x75
65e9809c0bSCorentin Chary #define NOTIFY_KBD_BRTUP		0xc4
66e9809c0bSCorentin Chary #define NOTIFY_KBD_BRTDWN		0xc5
67ed99d29bSChris Chiu #define NOTIFY_KBD_BRTTOGGLE		0xc7
68b096f626SYurii Pavlovskyi #define NOTIFY_KBD_FBM			0x99
692daa86e7SLeonid Maksymchuk #define NOTIFY_KBD_TTP			0xae
70ea856ec2SSamuel Čavoj #define NOTIFY_LID_FLIP			0xfa
71e397c3c4SLuke D. Jones #define NOTIFY_LID_FLIP_ROG		0xbd
725b799d4fSCorentin Chary 
73487579baSChris Chiu #define ASUS_WMI_FNLOCK_BIOS_DISABLED	BIT(0)
74487579baSChris Chiu 
75536fce82SLuke D. Jones #define ASUS_MID_FAN_DESC		"mid_fan"
7691809918SLuke D. Jones #define ASUS_GPU_FAN_DESC		"gpu_fan"
7753e755c2SKast Bernd #define ASUS_FAN_DESC			"cpu_fan"
7853e755c2SKast Bernd #define ASUS_FAN_MFUN			0x13
7953e755c2SKast Bernd #define ASUS_FAN_SFUN_READ		0x06
8053e755c2SKast Bernd #define ASUS_FAN_SFUN_WRITE		0x07
812889ffcfSDaniel Drake 
822889ffcfSDaniel Drake /* Based on standard hwmon pwmX_enable values */
83e3168b87SDaniel Drake #define ASUS_FAN_CTRL_FULLSPEED		0
8453e755c2SKast Bernd #define ASUS_FAN_CTRL_MANUAL		1
8553e755c2SKast Bernd #define ASUS_FAN_CTRL_AUTO		2
8653e755c2SKast Bernd 
879af93db9SDaniel Drake #define ASUS_FAN_BOOST_MODE_NORMAL		0
889af93db9SDaniel Drake #define ASUS_FAN_BOOST_MODE_OVERBOOST		1
899af93db9SDaniel Drake #define ASUS_FAN_BOOST_MODE_OVERBOOST_MASK	0x01
909af93db9SDaniel Drake #define ASUS_FAN_BOOST_MODE_SILENT		2
919af93db9SDaniel Drake #define ASUS_FAN_BOOST_MODE_SILENT_MASK		0x02
929af93db9SDaniel Drake #define ASUS_FAN_BOOST_MODES_MASK		0x03
93b096f626SYurii Pavlovskyi 
942daa86e7SLeonid Maksymchuk #define ASUS_THROTTLE_THERMAL_POLICY_DEFAULT	0
952daa86e7SLeonid Maksymchuk #define ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST	1
962daa86e7SLeonid Maksymchuk #define ASUS_THROTTLE_THERMAL_POLICY_SILENT	2
972daa86e7SLeonid Maksymchuk 
986051a4b1SMohamed Ghanmi #define ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO	0
996051a4b1SMohamed Ghanmi #define ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO	1
1006051a4b1SMohamed Ghanmi #define ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO	2
1016051a4b1SMohamed Ghanmi 
1026051a4b1SMohamed Ghanmi #define PLATFORM_PROFILE_MAX 2
1036051a4b1SMohamed Ghanmi 
1048023eff1SKai-Chuan Hsieh #define USB_INTEL_XUSB2PR		0xD0
1058023eff1SKai-Chuan Hsieh #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI	0x9c31
1068023eff1SKai-Chuan Hsieh 
107e0668f28SYurii Pavlovskyi #define ASUS_ACPI_UID_ASUSWMI		"ASUSWMI"
1081a373d15SYurii Pavlovskyi #define ASUS_ACPI_UID_ATK		"ATK"
1091a373d15SYurii Pavlovskyi 
1101a373d15SYurii Pavlovskyi #define WMI_EVENT_QUEUE_SIZE		0x10
1111a373d15SYurii Pavlovskyi #define WMI_EVENT_QUEUE_END		0x1
1121a373d15SYurii Pavlovskyi #define WMI_EVENT_MASK			0xFFFF
1131a373d15SYurii Pavlovskyi /* The WMI hotkey event value is always the same. */
1141a373d15SYurii Pavlovskyi #define WMI_EVENT_VALUE_ATK		0xFF
115e0668f28SYurii Pavlovskyi 
1168abd752bSYurii Pavlovskyi #define WMI_EVENT_MASK			0xFFFF
1178abd752bSYurii Pavlovskyi 
1180f0ac158SLuke D. Jones #define FAN_CURVE_POINTS		8
119a2bdf10cSLuke D. Jones #define FAN_CURVE_BUF_LEN		32
1200f0ac158SLuke D. Jones #define FAN_CURVE_DEV_CPU		0x00
1210f0ac158SLuke D. Jones #define FAN_CURVE_DEV_GPU		0x01
122ee887807SLuke D. Jones #define FAN_CURVE_DEV_MID		0x02
1230f0ac158SLuke D. Jones /* Mask to determine if setting temperature or percentage */
1240f0ac158SLuke D. Jones #define FAN_CURVE_PWM_MASK		0x04
1250f0ac158SLuke D. Jones 
126e0b278e7SLuke D. Jones /* Limits for tunables available on ASUS ROG laptops */
127e0b278e7SLuke D. Jones #define PPT_TOTAL_MIN		5
128e0b278e7SLuke D. Jones #define PPT_TOTAL_MAX		250
129e0b278e7SLuke D. Jones #define PPT_CPU_MIN			5
130e0b278e7SLuke D. Jones #define PPT_CPU_MAX			130
131e0b278e7SLuke D. Jones #define NVIDIA_BOOST_MIN	5
132e0b278e7SLuke D. Jones #define NVIDIA_BOOST_MAX	25
133e0b278e7SLuke D. Jones #define NVIDIA_TEMP_MIN		75
134e0b278e7SLuke D. Jones #define NVIDIA_TEMP_MAX		87
135e0b278e7SLuke D. Jones 
13671050ae7SJoão Paulo Rechi Vita static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
13771050ae7SJoão Paulo Rechi Vita 
1380f0ac158SLuke D. Jones static int throttle_thermal_policy_write(struct asus_wmi *);
1390f0ac158SLuke D. Jones 
ashs_present(void)140cf48bf9eSJoão Paulo Rechi Vita static bool ashs_present(void)
141cf48bf9eSJoão Paulo Rechi Vita {
142cf48bf9eSJoão Paulo Rechi Vita 	int i = 0;
143cf48bf9eSJoão Paulo Rechi Vita 	while (ashs_ids[i]) {
144cf48bf9eSJoão Paulo Rechi Vita 		if (acpi_dev_found(ashs_ids[i++]))
145cf48bf9eSJoão Paulo Rechi Vita 			return true;
146cf48bf9eSJoão Paulo Rechi Vita 	}
147cf48bf9eSJoão Paulo Rechi Vita 	return false;
148cf48bf9eSJoão Paulo Rechi Vita }
149cf48bf9eSJoão Paulo Rechi Vita 
1505b799d4fSCorentin Chary struct bios_args {
151d33da3b6SCorentin Chary 	u32 arg0;
152d33da3b6SCorentin Chary 	u32 arg1;
15398e865a5SYurii Pavlovskyi 	u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */
1540f0ac158SLuke D. Jones 	u32 arg3;
1550f0ac158SLuke D. Jones 	u32 arg4; /* Some ROG laptops require a full 5 input args */
1567b91f156SChris Chiu 	u32 arg5;
157d33da3b6SCorentin Chary } __packed;
1585b799d4fSCorentin Chary 
1595b799d4fSCorentin Chary /*
16053e755c2SKast Bernd  * Struct that's used for all methods called via AGFN. Naming is
16153e755c2SKast Bernd  * identically to the AML code.
16253e755c2SKast Bernd  */
16353e755c2SKast Bernd struct agfn_args {
16453e755c2SKast Bernd 	u16 mfun; /* probably "Multi-function" to be called */
16553e755c2SKast Bernd 	u16 sfun; /* probably "Sub-function" to be called */
16653e755c2SKast Bernd 	u16 len;  /* size of the hole struct, including subfunction fields */
16753e755c2SKast Bernd 	u8 stas;  /* not used by now */
16853e755c2SKast Bernd 	u8 err;   /* zero on success */
16953e755c2SKast Bernd } __packed;
17053e755c2SKast Bernd 
17153e755c2SKast Bernd /* struct used for calling fan read and write methods */
1722889ffcfSDaniel Drake struct agfn_fan_args {
17353e755c2SKast Bernd 	struct agfn_args agfn;	/* common fields */
17453e755c2SKast Bernd 	u8 fan;			/* fan number: 0: set auto mode 1: 1st fan */
17553e755c2SKast Bernd 	u32 speed;		/* read: RPM/100 - write: 0-255 */
17653e755c2SKast Bernd } __packed;
17753e755c2SKast Bernd 
17853e755c2SKast Bernd /*
179e12e6d94SCorentin Chary  * <platform>/    - debugfs root directory
1805b799d4fSCorentin Chary  *   dev_id      - current dev_id
1815b799d4fSCorentin Chary  *   ctrl_param  - current ctrl_param
182ef343491SCorentin Chary  *   method_id   - current method_id
1835b799d4fSCorentin Chary  *   devs        - call DEVS(dev_id, ctrl_param) and print result
1845b799d4fSCorentin Chary  *   dsts        - call DSTS(dev_id)  and print result
185ef343491SCorentin Chary  *   call        - call method_id(dev_id, ctrl_param) and print result
1865b799d4fSCorentin Chary  */
187e12e6d94SCorentin Chary struct asus_wmi_debug {
1885b799d4fSCorentin Chary 	struct dentry *root;
189ef343491SCorentin Chary 	u32 method_id;
1905b799d4fSCorentin Chary 	u32 dev_id;
1915b799d4fSCorentin Chary 	u32 ctrl_param;
1925b799d4fSCorentin Chary };
1935b799d4fSCorentin Chary 
194a7ce3f04SCorentin Chary struct asus_rfkill {
195a7ce3f04SCorentin Chary 	struct asus_wmi *asus;
196a7ce3f04SCorentin Chary 	struct rfkill *rfkill;
197a7ce3f04SCorentin Chary 	u32 dev_id;
198a7ce3f04SCorentin Chary };
199a7ce3f04SCorentin Chary 
2002889ffcfSDaniel Drake enum fan_type {
2012889ffcfSDaniel Drake 	FAN_TYPE_NONE = 0,
2022889ffcfSDaniel Drake 	FAN_TYPE_AGFN,		/* deprecated on newer platforms */
203e3168b87SDaniel Drake 	FAN_TYPE_SPEC83,	/* starting in Spec 8.3, use CPU_FAN_CTRL */
2042889ffcfSDaniel Drake };
2052889ffcfSDaniel Drake 
2060f0ac158SLuke D. Jones struct fan_curve_data {
2070f0ac158SLuke D. Jones 	bool enabled;
2080f0ac158SLuke D. Jones 	u32 device_id;
2090f0ac158SLuke D. Jones 	u8 temps[FAN_CURVE_POINTS];
2100f0ac158SLuke D. Jones 	u8 percents[FAN_CURVE_POINTS];
2110f0ac158SLuke D. Jones };
2120f0ac158SLuke D. Jones 
213e12e6d94SCorentin Chary struct asus_wmi {
2141d070f89SCorentin Chary 	int dsts_id;
21546dbca87SCorentin Chary 	int spec;
21646dbca87SCorentin Chary 	int sfun;
2171a373d15SYurii Pavlovskyi 	bool wmi_event_queue;
2181d070f89SCorentin Chary 
2195b799d4fSCorentin Chary 	struct input_dev *inputdev;
2205b799d4fSCorentin Chary 	struct backlight_device *backlight_device;
2215b799d4fSCorentin Chary 	struct platform_device *platform_device;
2225b799d4fSCorentin Chary 
2236cae06e6SAceLan Kao 	struct led_classdev wlan_led;
2246cae06e6SAceLan Kao 	int wlan_led_wk;
2255b799d4fSCorentin Chary 	struct led_classdev tpd_led;
2265b799d4fSCorentin Chary 	int tpd_led_wk;
227e9809c0bSCorentin Chary 	struct led_classdev kbd_led;
228e9809c0bSCorentin Chary 	int kbd_led_wk;
2294c059844SMaxime Bellengé 	struct led_classdev lightbar_led;
2304c059844SMaxime Bellengé 	int lightbar_led_wk;
231b644c955SPaddyKP_Yao 	struct led_classdev micmute_led;
2325b799d4fSCorentin Chary 	struct workqueue_struct *led_workqueue;
2335b799d4fSCorentin Chary 	struct work_struct tpd_led_work;
2346cae06e6SAceLan Kao 	struct work_struct wlan_led_work;
2354c059844SMaxime Bellengé 	struct work_struct lightbar_led_work;
2365b799d4fSCorentin Chary 
237a7ce3f04SCorentin Chary 	struct asus_rfkill wlan;
238a7ce3f04SCorentin Chary 	struct asus_rfkill bluetooth;
239a7ce3f04SCorentin Chary 	struct asus_rfkill wimax;
240a7ce3f04SCorentin Chary 	struct asus_rfkill wwan3g;
24143be8bdeSCorentin Chary 	struct asus_rfkill gps;
242a912d329SCorentin Chary 	struct asus_rfkill uwb;
2435b799d4fSCorentin Chary 
2441ea0d3b4SHans de Goede 	int tablet_switch_event_code;
2451ea0d3b4SHans de Goede 	u32 tablet_switch_dev_id;
246fdcc0602SHans de Goede 	bool tablet_switch_inverted;
2471ea0d3b4SHans de Goede 
2482889ffcfSDaniel Drake 	enum fan_type fan_type;
24912ff4c80SLuke D. Jones 	enum fan_type gpu_fan_type;
250536fce82SLuke D. Jones 	enum fan_type mid_fan_type;
2512889ffcfSDaniel Drake 	int fan_pwm_mode;
25212ff4c80SLuke D. Jones 	int gpu_fan_pwm_mode;
253536fce82SLuke D. Jones 	int mid_fan_pwm_mode;
2542889ffcfSDaniel Drake 	int agfn_pwm;
25553e755c2SKast Bernd 
2569af93db9SDaniel Drake 	bool fan_boost_mode_available;
2579af93db9SDaniel Drake 	u8 fan_boost_mode_mask;
2589af93db9SDaniel Drake 	u8 fan_boost_mode;
259b096f626SYurii Pavlovskyi 
26077ee9d29SLuke D. Jones 	bool charge_mode_available;
26136450e7dSLuke D. Jones 	bool egpu_enable_available;
262d4eca58aSLuke D. Jones 	bool egpu_connect_available;
26398829e84SLuke D. Jones 	bool dgpu_disable_available;
26401ef026aSLuke D. Jones 	bool gpu_mux_mode_available;
26598829e84SLuke D. Jones 
266e0b278e7SLuke D. Jones 	/* Tunables provided by ASUS for gaming laptops */
267e0b278e7SLuke D. Jones 	bool ppt_pl2_sppt_available;
268e0b278e7SLuke D. Jones 	bool ppt_pl1_spl_available;
269e0b278e7SLuke D. Jones 	bool ppt_apu_sppt_available;
270e0b278e7SLuke D. Jones 	bool ppt_plat_sppt_available;
271e0b278e7SLuke D. Jones 	bool ppt_fppt_available;
272e0b278e7SLuke D. Jones 	bool nv_dyn_boost_available;
273e0b278e7SLuke D. Jones 	bool nv_temp_tgt_available;
274e0b278e7SLuke D. Jones 
275e305a71cSLuke D. Jones 	bool kbd_rgb_mode_available;
27661f64515SLuke D. Jones 	bool kbd_rgb_state_available;
277e305a71cSLuke D. Jones 
2782daa86e7SLeonid Maksymchuk 	u8 throttle_thermal_policy_mode;
2796051a4b1SMohamed Ghanmi 	u32 throttle_thermal_policy_dev;
2802daa86e7SLeonid Maksymchuk 
2810f0ac158SLuke D. Jones 	bool cpu_fan_curve_available;
2820f0ac158SLuke D. Jones 	bool gpu_fan_curve_available;
283ee887807SLuke D. Jones 	bool mid_fan_curve_available;
284ee887807SLuke D. Jones 	struct fan_curve_data custom_fan_curves[3];
2850f0ac158SLuke D. Jones 
286c63d44aeSLuke D. Jones 	struct platform_profile_handler platform_profile_handler;
287c63d44aeSLuke D. Jones 	bool platform_profile_support;
288c63d44aeSLuke D. Jones 
2897973353eSKristian Klausen 	// The RSOC controls the maximum charging percentage.
2907973353eSKristian Klausen 	bool battery_rsoc_available;
291d507a54fSKristian Klausen 
292ca91ea34SLuke D. Jones 	bool panel_overdrive_available;
293abac4259SLuke D. Jones 	bool mini_led_mode_available;
294ca91ea34SLuke D. Jones 
295125450f8SLukas Wunner 	struct hotplug_slot hotplug_slot;
2965b799d4fSCorentin Chary 	struct mutex hotplug_lock;
2975b799d4fSCorentin Chary 	struct mutex wmi_lock;
2985b799d4fSCorentin Chary 	struct workqueue_struct *hotplug_workqueue;
2995b799d4fSCorentin Chary 	struct work_struct hotplug_work;
3005b799d4fSCorentin Chary 
301487579baSChris Chiu 	bool fnlock_locked;
302487579baSChris Chiu 
303e12e6d94SCorentin Chary 	struct asus_wmi_debug debug;
304e12e6d94SCorentin Chary 
305e12e6d94SCorentin Chary 	struct asus_wmi_driver *driver;
3065b799d4fSCorentin Chary };
3075b799d4fSCorentin Chary 
30854a3121fSYurii Pavlovskyi /* WMI ************************************************************************/
30954a3121fSYurii Pavlovskyi 
asus_wmi_evaluate_method3(u32 method_id,u32 arg0,u32 arg1,u32 arg2,u32 * retval)31098e865a5SYurii Pavlovskyi static int asus_wmi_evaluate_method3(u32 method_id,
31198e865a5SYurii Pavlovskyi 		u32 arg0, u32 arg1, u32 arg2, u32 *retval)
3125b799d4fSCorentin Chary {
313d33da3b6SCorentin Chary 	struct bios_args args = {
314d33da3b6SCorentin Chary 		.arg0 = arg0,
315d33da3b6SCorentin Chary 		.arg1 = arg1,
31698e865a5SYurii Pavlovskyi 		.arg2 = arg2,
317d33da3b6SCorentin Chary 	};
318d33da3b6SCorentin Chary 	struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
3195b799d4fSCorentin Chary 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
3205b799d4fSCorentin Chary 	acpi_status status;
321d33da3b6SCorentin Chary 	union acpi_object *obj;
3228ad3be1eSRickard Strandqvist 	u32 tmp = 0;
3235b799d4fSCorentin Chary 
3240fe57261SPali Rohár 	status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
3255b799d4fSCorentin Chary 				     &input, &output);
3265b799d4fSCorentin Chary 
3275b799d4fSCorentin Chary 	if (ACPI_FAILURE(status))
3281827f3f0SYurii Pavlovskyi 		return -EIO;
3295b799d4fSCorentin Chary 
3305b799d4fSCorentin Chary 	obj = (union acpi_object *)output.pointer;
3315b799d4fSCorentin Chary 	if (obj && obj->type == ACPI_TYPE_INTEGER)
3325b799d4fSCorentin Chary 		tmp = (u32) obj->integer.value;
3335b799d4fSCorentin Chary 
3345b799d4fSCorentin Chary 	if (retval)
3355b799d4fSCorentin Chary 		*retval = tmp;
3365b799d4fSCorentin Chary 
3375b799d4fSCorentin Chary 	kfree(obj);
3385b799d4fSCorentin Chary 
339d33da3b6SCorentin Chary 	if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
340d33da3b6SCorentin Chary 		return -ENODEV;
341d33da3b6SCorentin Chary 
342d33da3b6SCorentin Chary 	return 0;
3435b799d4fSCorentin Chary }
34498e865a5SYurii Pavlovskyi 
asus_wmi_evaluate_method(u32 method_id,u32 arg0,u32 arg1,u32 * retval)34598e865a5SYurii Pavlovskyi int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
34698e865a5SYurii Pavlovskyi {
34798e865a5SYurii Pavlovskyi 	return asus_wmi_evaluate_method3(method_id, arg0, arg1, 0, retval);
34898e865a5SYurii Pavlovskyi }
349ffb6ce70SDaniel Drake EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method);
3505b799d4fSCorentin Chary 
asus_wmi_evaluate_method5(u32 method_id,u32 arg0,u32 arg1,u32 arg2,u32 arg3,u32 arg4,u32 * retval)3510f0ac158SLuke D. Jones static int asus_wmi_evaluate_method5(u32 method_id,
3520f0ac158SLuke D. Jones 		u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 *retval)
3530f0ac158SLuke D. Jones {
3540f0ac158SLuke D. Jones 	struct bios_args args = {
3550f0ac158SLuke D. Jones 		.arg0 = arg0,
3560f0ac158SLuke D. Jones 		.arg1 = arg1,
3570f0ac158SLuke D. Jones 		.arg2 = arg2,
3580f0ac158SLuke D. Jones 		.arg3 = arg3,
3590f0ac158SLuke D. Jones 		.arg4 = arg4,
3600f0ac158SLuke D. Jones 	};
3610f0ac158SLuke D. Jones 	struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
3620f0ac158SLuke D. Jones 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
3630f0ac158SLuke D. Jones 	acpi_status status;
3640f0ac158SLuke D. Jones 	union acpi_object *obj;
3650f0ac158SLuke D. Jones 	u32 tmp = 0;
3660f0ac158SLuke D. Jones 
3670f0ac158SLuke D. Jones 	status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
3680f0ac158SLuke D. Jones 				     &input, &output);
3690f0ac158SLuke D. Jones 
3700f0ac158SLuke D. Jones 	if (ACPI_FAILURE(status))
3710f0ac158SLuke D. Jones 		return -EIO;
3720f0ac158SLuke D. Jones 
3730f0ac158SLuke D. Jones 	obj = (union acpi_object *)output.pointer;
3740f0ac158SLuke D. Jones 	if (obj && obj->type == ACPI_TYPE_INTEGER)
3750f0ac158SLuke D. Jones 		tmp = (u32) obj->integer.value;
3760f0ac158SLuke D. Jones 
3770f0ac158SLuke D. Jones 	if (retval)
3780f0ac158SLuke D. Jones 		*retval = tmp;
3790f0ac158SLuke D. Jones 
3800f0ac158SLuke D. Jones 	kfree(obj);
3810f0ac158SLuke D. Jones 
3820f0ac158SLuke D. Jones 	if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
3830f0ac158SLuke D. Jones 		return -ENODEV;
3840f0ac158SLuke D. Jones 
3850f0ac158SLuke D. Jones 	return 0;
3860f0ac158SLuke D. Jones }
3870f0ac158SLuke D. Jones 
3880f0ac158SLuke D. Jones /*
3890f0ac158SLuke D. Jones  * Returns as an error if the method output is not a buffer. Typically this
3900f0ac158SLuke D. Jones  * means that the method called is unsupported.
3910f0ac158SLuke D. Jones  */
asus_wmi_evaluate_method_buf(u32 method_id,u32 arg0,u32 arg1,u8 * ret_buffer,size_t size)3920f0ac158SLuke D. Jones static int asus_wmi_evaluate_method_buf(u32 method_id,
3930f0ac158SLuke D. Jones 		u32 arg0, u32 arg1, u8 *ret_buffer, size_t size)
3940f0ac158SLuke D. Jones {
3950f0ac158SLuke D. Jones 	struct bios_args args = {
3960f0ac158SLuke D. Jones 		.arg0 = arg0,
3970f0ac158SLuke D. Jones 		.arg1 = arg1,
3980f0ac158SLuke D. Jones 		.arg2 = 0,
3990f0ac158SLuke D. Jones 	};
4000f0ac158SLuke D. Jones 	struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
4010f0ac158SLuke D. Jones 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
4020f0ac158SLuke D. Jones 	acpi_status status;
4030f0ac158SLuke D. Jones 	union acpi_object *obj;
4040f0ac158SLuke D. Jones 	int err = 0;
4050f0ac158SLuke D. Jones 
4060f0ac158SLuke D. Jones 	status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
4070f0ac158SLuke D. Jones 				     &input, &output);
4080f0ac158SLuke D. Jones 
4090f0ac158SLuke D. Jones 	if (ACPI_FAILURE(status))
4100f0ac158SLuke D. Jones 		return -EIO;
4110f0ac158SLuke D. Jones 
4120f0ac158SLuke D. Jones 	obj = (union acpi_object *)output.pointer;
4130f0ac158SLuke D. Jones 
4140f0ac158SLuke D. Jones 	switch (obj->type) {
4150f0ac158SLuke D. Jones 	case ACPI_TYPE_BUFFER:
416d2833762SDan Carpenter 		if (obj->buffer.length > size) {
4170f0ac158SLuke D. Jones 			err = -ENOSPC;
418d2833762SDan Carpenter 			break;
419d2833762SDan Carpenter 		}
420d2833762SDan Carpenter 		if (obj->buffer.length == 0) {
4210f0ac158SLuke D. Jones 			err = -ENODATA;
422d2833762SDan Carpenter 			break;
423d2833762SDan Carpenter 		}
4240f0ac158SLuke D. Jones 
4250f0ac158SLuke D. Jones 		memcpy(ret_buffer, obj->buffer.pointer, obj->buffer.length);
4260f0ac158SLuke D. Jones 		break;
4270f0ac158SLuke D. Jones 	case ACPI_TYPE_INTEGER:
4280f0ac158SLuke D. Jones 		err = (u32)obj->integer.value;
4290f0ac158SLuke D. Jones 
4300f0ac158SLuke D. Jones 		if (err == ASUS_WMI_UNSUPPORTED_METHOD)
4310f0ac158SLuke D. Jones 			err = -ENODEV;
4320f0ac158SLuke D. Jones 		/*
4330f0ac158SLuke D. Jones 		 * At least one method returns a 0 with no buffer if no arg
4340f0ac158SLuke D. Jones 		 * is provided, such as ASUS_WMI_DEVID_CPU_FAN_CURVE
4350f0ac158SLuke D. Jones 		 */
4360f0ac158SLuke D. Jones 		if (err == 0)
4370f0ac158SLuke D. Jones 			err = -ENODATA;
4380f0ac158SLuke D. Jones 		break;
4390f0ac158SLuke D. Jones 	default:
4400f0ac158SLuke D. Jones 		err = -ENODATA;
4410f0ac158SLuke D. Jones 		break;
4420f0ac158SLuke D. Jones 	}
4430f0ac158SLuke D. Jones 
4440f0ac158SLuke D. Jones 	kfree(obj);
4450f0ac158SLuke D. Jones 
4460f0ac158SLuke D. Jones 	if (err)
4470f0ac158SLuke D. Jones 		return err;
4480f0ac158SLuke D. Jones 
4490f0ac158SLuke D. Jones 	return 0;
4500f0ac158SLuke D. Jones }
4510f0ac158SLuke D. Jones 
asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)45253e755c2SKast Bernd static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
45353e755c2SKast Bernd {
45453e755c2SKast Bernd 	struct acpi_buffer input;
45553e755c2SKast Bernd 	u64 phys_addr;
45653e755c2SKast Bernd 	u32 retval;
4576568d0c0SColin Ian King 	u32 status;
45853e755c2SKast Bernd 
45953e755c2SKast Bernd 	/*
46053e755c2SKast Bernd 	 * Copy to dma capable address otherwise memory corruption occurs as
46153e755c2SKast Bernd 	 * bios has to be able to access it.
46253e755c2SKast Bernd 	 */
463340f25ffSFuqian Huang 	input.pointer = kmemdup(args.pointer, args.length, GFP_DMA | GFP_KERNEL);
46453e755c2SKast Bernd 	input.length = args.length;
46553e755c2SKast Bernd 	if (!input.pointer)
46653e755c2SKast Bernd 		return -ENOMEM;
46753e755c2SKast Bernd 	phys_addr = virt_to_phys(input.pointer);
46853e755c2SKast Bernd 
46953e755c2SKast Bernd 	status = asus_wmi_evaluate_method(ASUS_WMI_METHODID_AGFN,
47053e755c2SKast Bernd 					phys_addr, 0, &retval);
47153e755c2SKast Bernd 	if (!status)
47253e755c2SKast Bernd 		memcpy(args.pointer, input.pointer, args.length);
47353e755c2SKast Bernd 
47453e755c2SKast Bernd 	kfree(input.pointer);
47553e755c2SKast Bernd 	if (status)
47653e755c2SKast Bernd 		return -ENXIO;
47753e755c2SKast Bernd 
47853e755c2SKast Bernd 	return retval;
47953e755c2SKast Bernd }
48053e755c2SKast Bernd 
asus_wmi_get_devstate(struct asus_wmi * asus,u32 dev_id,u32 * retval)4811d070f89SCorentin Chary static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
482d33da3b6SCorentin Chary {
4831d070f89SCorentin Chary 	return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);
484d33da3b6SCorentin Chary }
485d33da3b6SCorentin Chary 
asus_wmi_set_devstate(u32 dev_id,u32 ctrl_param,u32 * retval)486d33da3b6SCorentin Chary static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
4875b799d4fSCorentin Chary 				 u32 *retval)
4885b799d4fSCorentin Chary {
489d33da3b6SCorentin Chary 	return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id,
490d33da3b6SCorentin Chary 					ctrl_param, retval);
4915b799d4fSCorentin Chary }
4925b799d4fSCorentin Chary 
4935b799d4fSCorentin Chary /* Helper for special devices with magic return codes */
asus_wmi_get_devstate_bits(struct asus_wmi * asus,u32 dev_id,u32 mask)4941d070f89SCorentin Chary static int asus_wmi_get_devstate_bits(struct asus_wmi *asus,
4951d070f89SCorentin Chary 				      u32 dev_id, u32 mask)
4965b799d4fSCorentin Chary {
4975b799d4fSCorentin Chary 	u32 retval = 0;
498d33da3b6SCorentin Chary 	int err;
4995b799d4fSCorentin Chary 
5001d070f89SCorentin Chary 	err = asus_wmi_get_devstate(asus, dev_id, &retval);
501d33da3b6SCorentin Chary 	if (err < 0)
502d33da3b6SCorentin Chary 		return err;
5035b799d4fSCorentin Chary 
504e12e6d94SCorentin Chary 	if (!(retval & ASUS_WMI_DSTS_PRESENCE_BIT))
5055b799d4fSCorentin Chary 		return -ENODEV;
5065b799d4fSCorentin Chary 
507a75fe0d7SCorentin Chary 	if (mask == ASUS_WMI_DSTS_STATUS_BIT) {
508a75fe0d7SCorentin Chary 		if (retval & ASUS_WMI_DSTS_UNKNOWN_BIT)
509a75fe0d7SCorentin Chary 			return -ENODEV;
510a75fe0d7SCorentin Chary 	}
511a75fe0d7SCorentin Chary 
5125b799d4fSCorentin Chary 	return retval & mask;
5135b799d4fSCorentin Chary }
5145b799d4fSCorentin Chary 
asus_wmi_get_devstate_simple(struct asus_wmi * asus,u32 dev_id)5151d070f89SCorentin Chary static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id)
5165b799d4fSCorentin Chary {
5171d070f89SCorentin Chary 	return asus_wmi_get_devstate_bits(asus, dev_id,
5181d070f89SCorentin Chary 					  ASUS_WMI_DSTS_STATUS_BIT);
5195b799d4fSCorentin Chary }
5205b799d4fSCorentin Chary 
asus_wmi_dev_is_present(struct asus_wmi * asus,u32 dev_id)521f1fc0321SDaniel Drake static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id)
522f1fc0321SDaniel Drake {
523f1fc0321SDaniel Drake 	u32 retval;
524f1fc0321SDaniel Drake 	int status = asus_wmi_get_devstate(asus, dev_id, &retval);
525f1fc0321SDaniel Drake 
526f1fc0321SDaniel Drake 	return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT);
527f1fc0321SDaniel Drake }
528f1fc0321SDaniel Drake 
529fed5003dSHans de Goede /* Input **********************************************************************/
asus_wmi_tablet_sw_report(struct asus_wmi * asus,bool value)530fdcc0602SHans de Goede static void asus_wmi_tablet_sw_report(struct asus_wmi *asus, bool value)
531fdcc0602SHans de Goede {
532fdcc0602SHans de Goede 	input_report_switch(asus->inputdev, SW_TABLET_MODE,
533fdcc0602SHans de Goede 			    asus->tablet_switch_inverted ? !value : value);
534fdcc0602SHans de Goede 	input_sync(asus->inputdev);
535fdcc0602SHans de Goede }
536fdcc0602SHans de Goede 
asus_wmi_tablet_sw_init(struct asus_wmi * asus,u32 dev_id,int event_code)537c98dc61eSHans de Goede static void asus_wmi_tablet_sw_init(struct asus_wmi *asus, u32 dev_id, int event_code)
538c98dc61eSHans de Goede {
539c98dc61eSHans de Goede 	struct device *dev = &asus->platform_device->dev;
540c98dc61eSHans de Goede 	int result;
541c98dc61eSHans de Goede 
542c98dc61eSHans de Goede 	result = asus_wmi_get_devstate_simple(asus, dev_id);
543c98dc61eSHans de Goede 	if (result >= 0) {
544c98dc61eSHans de Goede 		input_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE);
545fdcc0602SHans de Goede 		asus_wmi_tablet_sw_report(asus, result);
5461ea0d3b4SHans de Goede 		asus->tablet_switch_dev_id = dev_id;
5471ea0d3b4SHans de Goede 		asus->tablet_switch_event_code = event_code;
548c98dc61eSHans de Goede 	} else if (result == -ENODEV) {
549c98dc61eSHans de Goede 		dev_err(dev, "This device has tablet-mode-switch quirk but got ENODEV checking it. This is a bug.");
550c98dc61eSHans de Goede 	} else {
551c98dc61eSHans de Goede 		dev_err(dev, "Error checking for tablet-mode-switch: %d\n", result);
552c98dc61eSHans de Goede 	}
553c98dc61eSHans de Goede }
554fed5003dSHans de Goede 
asus_wmi_input_init(struct asus_wmi * asus)555fed5003dSHans de Goede static int asus_wmi_input_init(struct asus_wmi *asus)
556fed5003dSHans de Goede {
557c98dc61eSHans de Goede 	struct device *dev = &asus->platform_device->dev;
558c98dc61eSHans de Goede 	int err;
55900aa8469SLuke D. Jones 
560fed5003dSHans de Goede 	asus->inputdev = input_allocate_device();
561fed5003dSHans de Goede 	if (!asus->inputdev)
562fed5003dSHans de Goede 		return -ENOMEM;
563fed5003dSHans de Goede 
564fed5003dSHans de Goede 	asus->inputdev->name = asus->driver->input_name;
565fed5003dSHans de Goede 	asus->inputdev->phys = asus->driver->input_phys;
566fed5003dSHans de Goede 	asus->inputdev->id.bustype = BUS_HOST;
56700aa8469SLuke D. Jones 	asus->inputdev->dev.parent = dev;
568fed5003dSHans de Goede 	set_bit(EV_REP, asus->inputdev->evbit);
569fed5003dSHans de Goede 
570fed5003dSHans de Goede 	err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);
571fed5003dSHans de Goede 	if (err)
572fed5003dSHans de Goede 		goto err_free_dev;
573fed5003dSHans de Goede 
57400aa8469SLuke D. Jones 	switch (asus->driver->quirks->tablet_switch_mode) {
57500aa8469SLuke D. Jones 	case asus_wmi_no_tablet_switch:
57600aa8469SLuke D. Jones 		break;
57700aa8469SLuke D. Jones 	case asus_wmi_kbd_dock_devid:
578fdcc0602SHans de Goede 		asus->tablet_switch_inverted = true;
579c98dc61eSHans de Goede 		asus_wmi_tablet_sw_init(asus, ASUS_WMI_DEVID_KBD_DOCK, NOTIFY_KBD_DOCK_CHANGE);
58000aa8469SLuke D. Jones 		break;
58100aa8469SLuke D. Jones 	case asus_wmi_lid_flip_devid:
582c98dc61eSHans de Goede 		asus_wmi_tablet_sw_init(asus, ASUS_WMI_DEVID_LID_FLIP, NOTIFY_LID_FLIP);
58300aa8469SLuke D. Jones 		break;
584e397c3c4SLuke D. Jones 	case asus_wmi_lid_flip_rog_devid:
585c98dc61eSHans de Goede 		asus_wmi_tablet_sw_init(asus, ASUS_WMI_DEVID_LID_FLIP_ROG, NOTIFY_LID_FLIP_ROG);
586e397c3c4SLuke D. Jones 		break;
587ea856ec2SSamuel Čavoj 	}
588ea856ec2SSamuel Čavoj 
589fed5003dSHans de Goede 	err = input_register_device(asus->inputdev);
590fed5003dSHans de Goede 	if (err)
591fed5003dSHans de Goede 		goto err_free_dev;
592fed5003dSHans de Goede 
593fed5003dSHans de Goede 	return 0;
594fed5003dSHans de Goede 
595fed5003dSHans de Goede err_free_dev:
596fed5003dSHans de Goede 	input_free_device(asus->inputdev);
597fed5003dSHans de Goede 	return err;
598fed5003dSHans de Goede }
599fed5003dSHans de Goede 
asus_wmi_input_exit(struct asus_wmi * asus)600fed5003dSHans de Goede static void asus_wmi_input_exit(struct asus_wmi *asus)
601fed5003dSHans de Goede {
602fed5003dSHans de Goede 	if (asus->inputdev)
603fed5003dSHans de Goede 		input_unregister_device(asus->inputdev);
604fed5003dSHans de Goede 
605fed5003dSHans de Goede 	asus->inputdev = NULL;
606fed5003dSHans de Goede }
607fed5003dSHans de Goede 
608ea856ec2SSamuel Čavoj /* Tablet mode ****************************************************************/
609ea856ec2SSamuel Čavoj 
asus_wmi_tablet_mode_get_state(struct asus_wmi * asus)6101ea0d3b4SHans de Goede static void asus_wmi_tablet_mode_get_state(struct asus_wmi *asus)
611ea856ec2SSamuel Čavoj {
61200aa8469SLuke D. Jones 	int result;
613ea856ec2SSamuel Čavoj 
6141ea0d3b4SHans de Goede 	if (!asus->tablet_switch_dev_id)
6151ea0d3b4SHans de Goede 		return;
616ea856ec2SSamuel Čavoj 
6171ea0d3b4SHans de Goede 	result = asus_wmi_get_devstate_simple(asus, asus->tablet_switch_dev_id);
618fdcc0602SHans de Goede 	if (result >= 0)
619fdcc0602SHans de Goede 		asus_wmi_tablet_sw_report(asus, result);
620e397c3c4SLuke D. Jones }
621e397c3c4SLuke D. Jones 
62277ee9d29SLuke D. Jones /* Charging mode, 1=Barrel, 2=USB ******************************************/
charge_mode_show(struct device * dev,struct device_attribute * attr,char * buf)62377ee9d29SLuke D. Jones static ssize_t charge_mode_show(struct device *dev,
62477ee9d29SLuke D. Jones 				   struct device_attribute *attr, char *buf)
62577ee9d29SLuke D. Jones {
62677ee9d29SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
62777ee9d29SLuke D. Jones 	int result, value;
62877ee9d29SLuke D. Jones 
62977ee9d29SLuke D. Jones 	result = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_CHARGE_MODE, &value);
63077ee9d29SLuke D. Jones 	if (result < 0)
63177ee9d29SLuke D. Jones 		return result;
63277ee9d29SLuke D. Jones 
63377ee9d29SLuke D. Jones 	return sysfs_emit(buf, "%d\n", value & 0xff);
63477ee9d29SLuke D. Jones }
63577ee9d29SLuke D. Jones 
63677ee9d29SLuke D. Jones static DEVICE_ATTR_RO(charge_mode);
63777ee9d29SLuke D. Jones 
63898829e84SLuke D. Jones /* dGPU ********************************************************************/
dgpu_disable_show(struct device * dev,struct device_attribute * attr,char * buf)63998829e84SLuke D. Jones static ssize_t dgpu_disable_show(struct device *dev,
64098829e84SLuke D. Jones 				   struct device_attribute *attr, char *buf)
64198829e84SLuke D. Jones {
64298829e84SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
643cdf36fc8SLuke D. Jones 	int result;
64498829e84SLuke D. Jones 
645cdf36fc8SLuke D. Jones 	result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_DGPU);
646cdf36fc8SLuke D. Jones 	if (result < 0)
647cdf36fc8SLuke D. Jones 		return result;
648cdf36fc8SLuke D. Jones 
649cdf36fc8SLuke D. Jones 	return sysfs_emit(buf, "%d\n", result);
65098829e84SLuke D. Jones }
65198829e84SLuke D. Jones 
65298829e84SLuke D. Jones /*
65398829e84SLuke D. Jones  * A user may be required to store the value twice, typcial store first, then
65498829e84SLuke D. Jones  * rescan PCI bus to activate power, then store a second time to save correctly.
65598829e84SLuke D. Jones  * The reason for this is that an extra code path in the ACPI is enabled when
65698829e84SLuke D. Jones  * the device and bus are powered.
65798829e84SLuke D. Jones  */
dgpu_disable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)65898829e84SLuke D. Jones static ssize_t dgpu_disable_store(struct device *dev,
65998829e84SLuke D. Jones 				    struct device_attribute *attr,
66098829e84SLuke D. Jones 				    const char *buf, size_t count)
66198829e84SLuke D. Jones {
662cdf36fc8SLuke D. Jones 	int result, err;
663cdf36fc8SLuke D. Jones 	u32 disable;
66498829e84SLuke D. Jones 
66598829e84SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
66698829e84SLuke D. Jones 
667cdf36fc8SLuke D. Jones 	result = kstrtou32(buf, 10, &disable);
66898829e84SLuke D. Jones 	if (result)
66998829e84SLuke D. Jones 		return result;
67098829e84SLuke D. Jones 
671cdf36fc8SLuke D. Jones 	if (disable > 1)
672cdf36fc8SLuke D. Jones 		return -EINVAL;
67398829e84SLuke D. Jones 
674609b3670SLuke D. Jones 	if (asus->gpu_mux_mode_available) {
675609b3670SLuke D. Jones 		result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPU_MUX);
676609b3670SLuke D. Jones 		if (result < 0)
677609b3670SLuke D. Jones 			/* An error here may signal greater failure of GPU handling */
678609b3670SLuke D. Jones 			return result;
679609b3670SLuke D. Jones 		if (!result && disable) {
680609b3670SLuke D. Jones 			err = -ENODEV;
681609b3670SLuke D. Jones 			pr_warn("Can not disable dGPU when the MUX is in dGPU mode: %d\n", err);
682609b3670SLuke D. Jones 			return err;
683609b3670SLuke D. Jones 		}
684609b3670SLuke D. Jones 	}
685609b3670SLuke D. Jones 
686cdf36fc8SLuke D. Jones 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_DGPU, disable, &result);
687cdf36fc8SLuke D. Jones 	if (err) {
688cdf36fc8SLuke D. Jones 		pr_warn("Failed to set dgpu disable: %d\n", err);
689cdf36fc8SLuke D. Jones 		return err;
690cdf36fc8SLuke D. Jones 	}
691cdf36fc8SLuke D. Jones 
692cdf36fc8SLuke D. Jones 	if (result > 1) {
693cdf36fc8SLuke D. Jones 		pr_warn("Failed to set dgpu disable (result): 0x%x\n", result);
694cdf36fc8SLuke D. Jones 		return -EIO;
695cdf36fc8SLuke D. Jones 	}
696cdf36fc8SLuke D. Jones 
697cdf36fc8SLuke D. Jones 	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "dgpu_disable");
69898829e84SLuke D. Jones 
69998829e84SLuke D. Jones 	return count;
70098829e84SLuke D. Jones }
70198829e84SLuke D. Jones static DEVICE_ATTR_RW(dgpu_disable);
70298829e84SLuke D. Jones 
703382b91dbSLuke D. Jones /* eGPU ********************************************************************/
egpu_enable_show(struct device * dev,struct device_attribute * attr,char * buf)704382b91dbSLuke D. Jones static ssize_t egpu_enable_show(struct device *dev,
705382b91dbSLuke D. Jones 				   struct device_attribute *attr, char *buf)
706382b91dbSLuke D. Jones {
707382b91dbSLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
70836450e7dSLuke D. Jones 	int result;
709382b91dbSLuke D. Jones 
71036450e7dSLuke D. Jones 	result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_EGPU);
71136450e7dSLuke D. Jones 	if (result < 0)
71236450e7dSLuke D. Jones 		return result;
71336450e7dSLuke D. Jones 
71436450e7dSLuke D. Jones 	return sysfs_emit(buf, "%d\n", result);
715382b91dbSLuke D. Jones }
716382b91dbSLuke D. Jones 
717382b91dbSLuke D. Jones /* The ACPI call to enable the eGPU also disables the internal dGPU */
egpu_enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)718382b91dbSLuke D. Jones static ssize_t egpu_enable_store(struct device *dev,
719382b91dbSLuke D. Jones 				    struct device_attribute *attr,
720382b91dbSLuke D. Jones 				    const char *buf, size_t count)
721382b91dbSLuke D. Jones {
72236450e7dSLuke D. Jones 	int result, err;
72336450e7dSLuke D. Jones 	u32 enable;
724382b91dbSLuke D. Jones 
725382b91dbSLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
726382b91dbSLuke D. Jones 
72736450e7dSLuke D. Jones 	err = kstrtou32(buf, 10, &enable);
72836450e7dSLuke D. Jones 	if (err)
72936450e7dSLuke D. Jones 		return err;
730382b91dbSLuke D. Jones 
73136450e7dSLuke D. Jones 	if (enable > 1)
73236450e7dSLuke D. Jones 		return -EINVAL;
733382b91dbSLuke D. Jones 
734d49f4d1aSLuke D. Jones 	err = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_EGPU_CONNECTED);
735acce85a7SLuke D. Jones 	if (err < 0) {
736acce85a7SLuke D. Jones 		pr_warn("Failed to get egpu connection status: %d\n", err);
737d49f4d1aSLuke D. Jones 		return err;
738d49f4d1aSLuke D. Jones 	}
739d49f4d1aSLuke D. Jones 
740609b3670SLuke D. Jones 	if (asus->gpu_mux_mode_available) {
741609b3670SLuke D. Jones 		result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPU_MUX);
742acce85a7SLuke D. Jones 		if (result < 0) {
743609b3670SLuke D. Jones 			/* An error here may signal greater failure of GPU handling */
744acce85a7SLuke D. Jones 			pr_warn("Failed to get gpu mux status: %d\n", result);
745609b3670SLuke D. Jones 			return result;
746acce85a7SLuke D. Jones 		}
747609b3670SLuke D. Jones 		if (!result && enable) {
748609b3670SLuke D. Jones 			err = -ENODEV;
749609b3670SLuke D. Jones 			pr_warn("Can not enable eGPU when the MUX is in dGPU mode: %d\n", err);
750609b3670SLuke D. Jones 			return err;
751609b3670SLuke D. Jones 		}
752609b3670SLuke D. Jones 	}
753609b3670SLuke D. Jones 
75436450e7dSLuke D. Jones 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_EGPU, enable, &result);
75536450e7dSLuke D. Jones 	if (err) {
756acce85a7SLuke D. Jones 		pr_warn("Failed to set egpu state: %d\n", err);
75736450e7dSLuke D. Jones 		return err;
75836450e7dSLuke D. Jones 	}
759382b91dbSLuke D. Jones 
76036450e7dSLuke D. Jones 	if (result > 1) {
761acce85a7SLuke D. Jones 		pr_warn("Failed to set egpu state (retval): 0x%x\n", result);
76236450e7dSLuke D. Jones 		return -EIO;
76336450e7dSLuke D. Jones 	}
76436450e7dSLuke D. Jones 
76536450e7dSLuke D. Jones 	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "egpu_enable");
766382b91dbSLuke D. Jones 
767382b91dbSLuke D. Jones 	return count;
768382b91dbSLuke D. Jones }
769382b91dbSLuke D. Jones static DEVICE_ATTR_RW(egpu_enable);
770382b91dbSLuke D. Jones 
771d4eca58aSLuke D. Jones /* Is eGPU connected? *********************************************************/
egpu_connected_show(struct device * dev,struct device_attribute * attr,char * buf)772d4eca58aSLuke D. Jones static ssize_t egpu_connected_show(struct device *dev,
773d4eca58aSLuke D. Jones 				   struct device_attribute *attr, char *buf)
774d4eca58aSLuke D. Jones {
775d4eca58aSLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
776d4eca58aSLuke D. Jones 	int result;
777d4eca58aSLuke D. Jones 
778d4eca58aSLuke D. Jones 	result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_EGPU_CONNECTED);
779d4eca58aSLuke D. Jones 	if (result < 0)
780d4eca58aSLuke D. Jones 		return result;
781d4eca58aSLuke D. Jones 
782d4eca58aSLuke D. Jones 	return sysfs_emit(buf, "%d\n", result);
783d4eca58aSLuke D. Jones }
784d4eca58aSLuke D. Jones 
785d4eca58aSLuke D. Jones static DEVICE_ATTR_RO(egpu_connected);
786d4eca58aSLuke D. Jones 
78701ef026aSLuke D. Jones /* gpu mux switch *************************************************************/
gpu_mux_mode_show(struct device * dev,struct device_attribute * attr,char * buf)78801ef026aSLuke D. Jones static ssize_t gpu_mux_mode_show(struct device *dev,
78901ef026aSLuke D. Jones 				 struct device_attribute *attr, char *buf)
79001ef026aSLuke D. Jones {
79101ef026aSLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
79201ef026aSLuke D. Jones 	int result;
79301ef026aSLuke D. Jones 
79401ef026aSLuke D. Jones 	result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPU_MUX);
79501ef026aSLuke D. Jones 	if (result < 0)
79601ef026aSLuke D. Jones 		return result;
79701ef026aSLuke D. Jones 
79801ef026aSLuke D. Jones 	return sysfs_emit(buf, "%d\n", result);
79901ef026aSLuke D. Jones }
80001ef026aSLuke D. Jones 
gpu_mux_mode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)80101ef026aSLuke D. Jones static ssize_t gpu_mux_mode_store(struct device *dev,
80201ef026aSLuke D. Jones 				  struct device_attribute *attr,
80301ef026aSLuke D. Jones 				  const char *buf, size_t count)
80401ef026aSLuke D. Jones {
80501ef026aSLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
80601ef026aSLuke D. Jones 	int result, err;
80701ef026aSLuke D. Jones 	u32 optimus;
80801ef026aSLuke D. Jones 
80901ef026aSLuke D. Jones 	err = kstrtou32(buf, 10, &optimus);
81001ef026aSLuke D. Jones 	if (err)
81101ef026aSLuke D. Jones 		return err;
81201ef026aSLuke D. Jones 
81301ef026aSLuke D. Jones 	if (optimus > 1)
81401ef026aSLuke D. Jones 		return -EINVAL;
81501ef026aSLuke D. Jones 
816609b3670SLuke D. Jones 	if (asus->dgpu_disable_available) {
817609b3670SLuke D. Jones 		result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_DGPU);
818609b3670SLuke D. Jones 		if (result < 0)
819609b3670SLuke D. Jones 			/* An error here may signal greater failure of GPU handling */
820609b3670SLuke D. Jones 			return result;
821609b3670SLuke D. Jones 		if (result && !optimus) {
822609b3670SLuke D. Jones 			err = -ENODEV;
823609b3670SLuke D. Jones 			pr_warn("Can not switch MUX to dGPU mode when dGPU is disabled: %d\n", err);
824609b3670SLuke D. Jones 			return err;
825609b3670SLuke D. Jones 		}
826609b3670SLuke D. Jones 	}
827609b3670SLuke D. Jones 
828609b3670SLuke D. Jones 	if (asus->egpu_enable_available) {
829609b3670SLuke D. Jones 		result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_EGPU);
830609b3670SLuke D. Jones 		if (result < 0)
831609b3670SLuke D. Jones 			/* An error here may signal greater failure of GPU handling */
832609b3670SLuke D. Jones 			return result;
833609b3670SLuke D. Jones 		if (result && !optimus) {
834609b3670SLuke D. Jones 			err = -ENODEV;
835609b3670SLuke D. Jones 			pr_warn("Can not switch MUX to dGPU mode when eGPU is enabled: %d\n", err);
836609b3670SLuke D. Jones 			return err;
837609b3670SLuke D. Jones 		}
838609b3670SLuke D. Jones 	}
839609b3670SLuke D. Jones 
84001ef026aSLuke D. Jones 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_GPU_MUX, optimus, &result);
84101ef026aSLuke D. Jones 	if (err) {
84201ef026aSLuke D. Jones 		dev_err(dev, "Failed to set GPU MUX mode: %d\n", err);
84301ef026aSLuke D. Jones 		return err;
84401ef026aSLuke D. Jones 	}
84501ef026aSLuke D. Jones 	/* !1 is considered a fail by ASUS */
84601ef026aSLuke D. Jones 	if (result != 1) {
84701ef026aSLuke D. Jones 		dev_warn(dev, "Failed to set GPU MUX mode (result): 0x%x\n", result);
84801ef026aSLuke D. Jones 		return -EIO;
84901ef026aSLuke D. Jones 	}
85001ef026aSLuke D. Jones 
85101ef026aSLuke D. Jones 	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "gpu_mux_mode");
85201ef026aSLuke D. Jones 
85301ef026aSLuke D. Jones 	return count;
85401ef026aSLuke D. Jones }
85501ef026aSLuke D. Jones static DEVICE_ATTR_RW(gpu_mux_mode);
85601ef026aSLuke D. Jones 
857e305a71cSLuke D. Jones /* TUF Laptop Keyboard RGB Modes **********************************************/
kbd_rgb_mode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)858e305a71cSLuke D. Jones static ssize_t kbd_rgb_mode_store(struct device *dev,
859e305a71cSLuke D. Jones 				 struct device_attribute *attr,
860e305a71cSLuke D. Jones 				 const char *buf, size_t count)
861e305a71cSLuke D. Jones {
862e305a71cSLuke D. Jones 	u32 cmd, mode, r, g, b, speed;
863e305a71cSLuke D. Jones 	int err;
864e305a71cSLuke D. Jones 
865e305a71cSLuke D. Jones 	if (sscanf(buf, "%d %d %d %d %d %d", &cmd, &mode, &r, &g, &b, &speed) != 6)
866e305a71cSLuke D. Jones 		return -EINVAL;
867e305a71cSLuke D. Jones 
8686a758a3eSKristian Angelov 	/* B3 is set and B4 is save to BIOS */
8696a758a3eSKristian Angelov 	switch (cmd) {
8706a758a3eSKristian Angelov 	case 0:
8716a758a3eSKristian Angelov 		cmd = 0xb3;
8726a758a3eSKristian Angelov 		break;
8736a758a3eSKristian Angelov 	case 1:
8746a758a3eSKristian Angelov 		cmd = 0xb4;
8756a758a3eSKristian Angelov 		break;
8766a758a3eSKristian Angelov 	default:
8776a758a3eSKristian Angelov 		return -EINVAL;
8786a758a3eSKristian Angelov 	}
879e305a71cSLuke D. Jones 
880e305a71cSLuke D. Jones 	/* These are the known usable modes across all TUF/ROG */
881e305a71cSLuke D. Jones 	if (mode >= 12 || mode == 9)
882e305a71cSLuke D. Jones 		mode = 10;
883e305a71cSLuke D. Jones 
884e305a71cSLuke D. Jones 	switch (speed) {
885e305a71cSLuke D. Jones 	case 0:
886e305a71cSLuke D. Jones 		speed = 0xe1;
887e305a71cSLuke D. Jones 		break;
888e305a71cSLuke D. Jones 	case 1:
889e305a71cSLuke D. Jones 		speed = 0xeb;
890e305a71cSLuke D. Jones 		break;
891e305a71cSLuke D. Jones 	case 2:
892e305a71cSLuke D. Jones 		speed = 0xf5;
893e305a71cSLuke D. Jones 		break;
894e305a71cSLuke D. Jones 	default:
895e305a71cSLuke D. Jones 		speed = 0xeb;
896e305a71cSLuke D. Jones 	}
897e305a71cSLuke D. Jones 
898e305a71cSLuke D. Jones 	err = asus_wmi_evaluate_method3(ASUS_WMI_METHODID_DEVS, ASUS_WMI_DEVID_TUF_RGB_MODE,
899e305a71cSLuke D. Jones 			cmd | (mode << 8) | (r << 16) | (g << 24), b | (speed << 8), NULL);
900e305a71cSLuke D. Jones 	if (err)
901e305a71cSLuke D. Jones 		return err;
902e305a71cSLuke D. Jones 
903e305a71cSLuke D. Jones 	return count;
904e305a71cSLuke D. Jones }
905e305a71cSLuke D. Jones static DEVICE_ATTR_WO(kbd_rgb_mode);
906e305a71cSLuke D. Jones 
kbd_rgb_mode_index_show(struct device * device,struct device_attribute * attr,char * buf)907e305a71cSLuke D. Jones static ssize_t kbd_rgb_mode_index_show(struct device *device,
908e305a71cSLuke D. Jones 						 struct device_attribute *attr,
909e305a71cSLuke D. Jones 						 char *buf)
910e305a71cSLuke D. Jones {
911e305a71cSLuke D. Jones 	return sysfs_emit(buf, "%s\n", "cmd mode red green blue speed");
912e305a71cSLuke D. Jones }
913e305a71cSLuke D. Jones static DEVICE_ATTR_RO(kbd_rgb_mode_index);
914e305a71cSLuke D. Jones 
915e305a71cSLuke D. Jones static struct attribute *kbd_rgb_mode_attrs[] = {
916e305a71cSLuke D. Jones 	&dev_attr_kbd_rgb_mode.attr,
917e305a71cSLuke D. Jones 	&dev_attr_kbd_rgb_mode_index.attr,
918e305a71cSLuke D. Jones 	NULL,
919e305a71cSLuke D. Jones };
920e305a71cSLuke D. Jones 
921e305a71cSLuke D. Jones static const struct attribute_group kbd_rgb_mode_group = {
922e305a71cSLuke D. Jones 	.attrs = kbd_rgb_mode_attrs,
923e305a71cSLuke D. Jones };
924e305a71cSLuke D. Jones 
92561f64515SLuke D. Jones /* TUF Laptop Keyboard RGB State **********************************************/
kbd_rgb_state_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)92661f64515SLuke D. Jones static ssize_t kbd_rgb_state_store(struct device *dev,
92761f64515SLuke D. Jones 				 struct device_attribute *attr,
92861f64515SLuke D. Jones 				 const char *buf, size_t count)
92961f64515SLuke D. Jones {
93061f64515SLuke D. Jones 	u32 flags, cmd, boot, awake, sleep, keyboard;
93161f64515SLuke D. Jones 	int err;
93261f64515SLuke D. Jones 
93361f64515SLuke D. Jones 	if (sscanf(buf, "%d %d %d %d %d", &cmd, &boot, &awake, &sleep, &keyboard) != 5)
93461f64515SLuke D. Jones 		return -EINVAL;
93561f64515SLuke D. Jones 
93661f64515SLuke D. Jones 	if (cmd)
93761f64515SLuke D. Jones 		cmd = BIT(2);
93861f64515SLuke D. Jones 
93961f64515SLuke D. Jones 	flags = 0;
94061f64515SLuke D. Jones 	if (boot)
94161f64515SLuke D. Jones 		flags |= BIT(1);
94261f64515SLuke D. Jones 	if (awake)
94361f64515SLuke D. Jones 		flags |= BIT(3);
94461f64515SLuke D. Jones 	if (sleep)
94561f64515SLuke D. Jones 		flags |= BIT(5);
94661f64515SLuke D. Jones 	if (keyboard)
94761f64515SLuke D. Jones 		flags |= BIT(7);
94861f64515SLuke D. Jones 
94961f64515SLuke D. Jones 	/* 0xbd is the required default arg0 for the method. Nothing happens otherwise */
95061f64515SLuke D. Jones 	err = asus_wmi_evaluate_method3(ASUS_WMI_METHODID_DEVS,
95161f64515SLuke D. Jones 			ASUS_WMI_DEVID_TUF_RGB_STATE, 0xbd | cmd << 8 | (flags << 16), 0, NULL);
95261f64515SLuke D. Jones 	if (err)
95361f64515SLuke D. Jones 		return err;
95461f64515SLuke D. Jones 
95561f64515SLuke D. Jones 	return count;
95661f64515SLuke D. Jones }
95761f64515SLuke D. Jones static DEVICE_ATTR_WO(kbd_rgb_state);
95861f64515SLuke D. Jones 
kbd_rgb_state_index_show(struct device * device,struct device_attribute * attr,char * buf)95961f64515SLuke D. Jones static ssize_t kbd_rgb_state_index_show(struct device *device,
96061f64515SLuke D. Jones 						 struct device_attribute *attr,
96161f64515SLuke D. Jones 						 char *buf)
96261f64515SLuke D. Jones {
96361f64515SLuke D. Jones 	return sysfs_emit(buf, "%s\n", "cmd boot awake sleep keyboard");
96461f64515SLuke D. Jones }
96561f64515SLuke D. Jones static DEVICE_ATTR_RO(kbd_rgb_state_index);
96661f64515SLuke D. Jones 
96761f64515SLuke D. Jones static struct attribute *kbd_rgb_state_attrs[] = {
96861f64515SLuke D. Jones 	&dev_attr_kbd_rgb_state.attr,
96961f64515SLuke D. Jones 	&dev_attr_kbd_rgb_state_index.attr,
97061f64515SLuke D. Jones 	NULL,
97161f64515SLuke D. Jones };
97261f64515SLuke D. Jones 
97361f64515SLuke D. Jones static const struct attribute_group kbd_rgb_state_group = {
97461f64515SLuke D. Jones 	.attrs = kbd_rgb_state_attrs,
97561f64515SLuke D. Jones };
97661f64515SLuke D. Jones 
9777318b613SHans de Goede static const struct attribute_group *kbd_rgb_mode_groups[] = {
978e305a71cSLuke D. Jones 	NULL,
979e305a71cSLuke D. Jones 	NULL,
98061f64515SLuke D. Jones 	NULL,
981e305a71cSLuke D. Jones };
982e305a71cSLuke D. Jones 
983e0b278e7SLuke D. Jones /* Tunable: PPT: Intel=PL1, AMD=SPPT *****************************************/
ppt_pl2_sppt_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)984e0b278e7SLuke D. Jones static ssize_t ppt_pl2_sppt_store(struct device *dev,
985e0b278e7SLuke D. Jones 				    struct device_attribute *attr,
986e0b278e7SLuke D. Jones 				    const char *buf, size_t count)
987e0b278e7SLuke D. Jones {
988e0b278e7SLuke D. Jones 	int result, err;
989e0b278e7SLuke D. Jones 	u32 value;
990e0b278e7SLuke D. Jones 
991e0b278e7SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
992e0b278e7SLuke D. Jones 
993e0b278e7SLuke D. Jones 	result = kstrtou32(buf, 10, &value);
994e0b278e7SLuke D. Jones 	if (result)
995e0b278e7SLuke D. Jones 		return result;
996e0b278e7SLuke D. Jones 
997e0b278e7SLuke D. Jones 	if (value < PPT_TOTAL_MIN || value > PPT_TOTAL_MAX)
998e0b278e7SLuke D. Jones 		return -EINVAL;
999e0b278e7SLuke D. Jones 
1000e0b278e7SLuke D. Jones 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_PL2_SPPT, value, &result);
1001e0b278e7SLuke D. Jones 	if (err) {
1002e0b278e7SLuke D. Jones 		pr_warn("Failed to set ppt_pl2_sppt: %d\n", err);
1003e0b278e7SLuke D. Jones 		return err;
1004e0b278e7SLuke D. Jones 	}
1005e0b278e7SLuke D. Jones 
1006e0b278e7SLuke D. Jones 	if (result > 1) {
1007e0b278e7SLuke D. Jones 		pr_warn("Failed to set ppt_pl2_sppt (result): 0x%x\n", result);
1008e0b278e7SLuke D. Jones 		return -EIO;
1009e0b278e7SLuke D. Jones 	}
1010e0b278e7SLuke D. Jones 
1011e0b278e7SLuke D. Jones 	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_pl2_sppt");
1012e0b278e7SLuke D. Jones 
1013e0b278e7SLuke D. Jones 	return count;
1014e0b278e7SLuke D. Jones }
1015e0b278e7SLuke D. Jones static DEVICE_ATTR_WO(ppt_pl2_sppt);
1016e0b278e7SLuke D. Jones 
1017e0b278e7SLuke D. Jones /* Tunable: PPT, Intel=PL1, AMD=SPL ******************************************/
ppt_pl1_spl_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1018e0b278e7SLuke D. Jones static ssize_t ppt_pl1_spl_store(struct device *dev,
1019e0b278e7SLuke D. Jones 				    struct device_attribute *attr,
1020e0b278e7SLuke D. Jones 				    const char *buf, size_t count)
1021e0b278e7SLuke D. Jones {
1022e0b278e7SLuke D. Jones 	int result, err;
1023e0b278e7SLuke D. Jones 	u32 value;
1024e0b278e7SLuke D. Jones 
1025e0b278e7SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
1026e0b278e7SLuke D. Jones 
1027e0b278e7SLuke D. Jones 	result = kstrtou32(buf, 10, &value);
1028e0b278e7SLuke D. Jones 	if (result)
1029e0b278e7SLuke D. Jones 		return result;
1030e0b278e7SLuke D. Jones 
1031e0b278e7SLuke D. Jones 	if (value < PPT_TOTAL_MIN || value > PPT_TOTAL_MAX)
1032e0b278e7SLuke D. Jones 		return -EINVAL;
1033e0b278e7SLuke D. Jones 
1034e0b278e7SLuke D. Jones 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_PL1_SPL, value, &result);
1035e0b278e7SLuke D. Jones 	if (err) {
1036e0b278e7SLuke D. Jones 		pr_warn("Failed to set ppt_pl1_spl: %d\n", err);
1037e0b278e7SLuke D. Jones 		return err;
1038e0b278e7SLuke D. Jones 	}
1039e0b278e7SLuke D. Jones 
1040e0b278e7SLuke D. Jones 	if (result > 1) {
1041e0b278e7SLuke D. Jones 		pr_warn("Failed to set ppt_pl1_spl (result): 0x%x\n", result);
1042e0b278e7SLuke D. Jones 		return -EIO;
1043e0b278e7SLuke D. Jones 	}
1044e0b278e7SLuke D. Jones 
1045e0b278e7SLuke D. Jones 	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_pl1_spl");
1046e0b278e7SLuke D. Jones 
1047e0b278e7SLuke D. Jones 	return count;
1048e0b278e7SLuke D. Jones }
1049e0b278e7SLuke D. Jones static DEVICE_ATTR_WO(ppt_pl1_spl);
1050e0b278e7SLuke D. Jones 
1051e0b278e7SLuke D. Jones /* Tunable: PPT APU FPPT ******************************************************/
ppt_fppt_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1052e0b278e7SLuke D. Jones static ssize_t ppt_fppt_store(struct device *dev,
1053e0b278e7SLuke D. Jones 				    struct device_attribute *attr,
1054e0b278e7SLuke D. Jones 				    const char *buf, size_t count)
1055e0b278e7SLuke D. Jones {
1056e0b278e7SLuke D. Jones 	int result, err;
1057e0b278e7SLuke D. Jones 	u32 value;
1058e0b278e7SLuke D. Jones 
1059e0b278e7SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
1060e0b278e7SLuke D. Jones 
1061e0b278e7SLuke D. Jones 	result = kstrtou32(buf, 10, &value);
1062e0b278e7SLuke D. Jones 	if (result)
1063e0b278e7SLuke D. Jones 		return result;
1064e0b278e7SLuke D. Jones 
1065e0b278e7SLuke D. Jones 	if (value < PPT_TOTAL_MIN || value > PPT_TOTAL_MAX)
1066e0b278e7SLuke D. Jones 		return -EINVAL;
1067e0b278e7SLuke D. Jones 
1068e0b278e7SLuke D. Jones 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_FPPT, value, &result);
1069e0b278e7SLuke D. Jones 	if (err) {
1070e0b278e7SLuke D. Jones 		pr_warn("Failed to set ppt_fppt: %d\n", err);
1071e0b278e7SLuke D. Jones 		return err;
1072e0b278e7SLuke D. Jones 	}
1073e0b278e7SLuke D. Jones 
1074e0b278e7SLuke D. Jones 	if (result > 1) {
1075e0b278e7SLuke D. Jones 		pr_warn("Failed to set ppt_fppt (result): 0x%x\n", result);
1076e0b278e7SLuke D. Jones 		return -EIO;
1077e0b278e7SLuke D. Jones 	}
1078e0b278e7SLuke D. Jones 
1079e0b278e7SLuke D. Jones 	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_fpu_sppt");
1080e0b278e7SLuke D. Jones 
1081e0b278e7SLuke D. Jones 	return count;
1082e0b278e7SLuke D. Jones }
1083e0b278e7SLuke D. Jones static DEVICE_ATTR_WO(ppt_fppt);
1084e0b278e7SLuke D. Jones 
1085e0b278e7SLuke D. Jones /* Tunable: PPT APU SPPT *****************************************************/
ppt_apu_sppt_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1086e0b278e7SLuke D. Jones static ssize_t ppt_apu_sppt_store(struct device *dev,
1087e0b278e7SLuke D. Jones 				    struct device_attribute *attr,
1088e0b278e7SLuke D. Jones 				    const char *buf, size_t count)
1089e0b278e7SLuke D. Jones {
1090e0b278e7SLuke D. Jones 	int result, err;
1091e0b278e7SLuke D. Jones 	u32 value;
1092e0b278e7SLuke D. Jones 
1093e0b278e7SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
1094e0b278e7SLuke D. Jones 
1095e0b278e7SLuke D. Jones 	result = kstrtou32(buf, 10, &value);
1096e0b278e7SLuke D. Jones 	if (result)
1097e0b278e7SLuke D. Jones 		return result;
1098e0b278e7SLuke D. Jones 
1099e0b278e7SLuke D. Jones 	if (value < PPT_CPU_MIN || value > PPT_CPU_MAX)
1100e0b278e7SLuke D. Jones 		return -EINVAL;
1101e0b278e7SLuke D. Jones 
1102e0b278e7SLuke D. Jones 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_APU_SPPT, value, &result);
1103e0b278e7SLuke D. Jones 	if (err) {
1104e0b278e7SLuke D. Jones 		pr_warn("Failed to set ppt_apu_sppt: %d\n", err);
1105e0b278e7SLuke D. Jones 		return err;
1106e0b278e7SLuke D. Jones 	}
1107e0b278e7SLuke D. Jones 
1108e0b278e7SLuke D. Jones 	if (result > 1) {
1109e0b278e7SLuke D. Jones 		pr_warn("Failed to set ppt_apu_sppt (result): 0x%x\n", result);
1110e0b278e7SLuke D. Jones 		return -EIO;
1111e0b278e7SLuke D. Jones 	}
1112e0b278e7SLuke D. Jones 
1113e0b278e7SLuke D. Jones 	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_apu_sppt");
1114e0b278e7SLuke D. Jones 
1115e0b278e7SLuke D. Jones 	return count;
1116e0b278e7SLuke D. Jones }
1117e0b278e7SLuke D. Jones static DEVICE_ATTR_WO(ppt_apu_sppt);
1118e0b278e7SLuke D. Jones 
1119e0b278e7SLuke D. Jones /* Tunable: PPT platform SPPT ************************************************/
ppt_platform_sppt_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1120e0b278e7SLuke D. Jones static ssize_t ppt_platform_sppt_store(struct device *dev,
1121e0b278e7SLuke D. Jones 				    struct device_attribute *attr,
1122e0b278e7SLuke D. Jones 				    const char *buf, size_t count)
1123e0b278e7SLuke D. Jones {
1124e0b278e7SLuke D. Jones 	int result, err;
1125e0b278e7SLuke D. Jones 	u32 value;
1126e0b278e7SLuke D. Jones 
1127e0b278e7SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
1128e0b278e7SLuke D. Jones 
1129e0b278e7SLuke D. Jones 	result = kstrtou32(buf, 10, &value);
1130e0b278e7SLuke D. Jones 	if (result)
1131e0b278e7SLuke D. Jones 		return result;
1132e0b278e7SLuke D. Jones 
1133e0b278e7SLuke D. Jones 	if (value < PPT_CPU_MIN || value > PPT_CPU_MAX)
1134e0b278e7SLuke D. Jones 		return -EINVAL;
1135e0b278e7SLuke D. Jones 
1136e0b278e7SLuke D. Jones 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_PLAT_SPPT, value, &result);
1137e0b278e7SLuke D. Jones 	if (err) {
1138e0b278e7SLuke D. Jones 		pr_warn("Failed to set ppt_platform_sppt: %d\n", err);
1139e0b278e7SLuke D. Jones 		return err;
1140e0b278e7SLuke D. Jones 	}
1141e0b278e7SLuke D. Jones 
1142e0b278e7SLuke D. Jones 	if (result > 1) {
1143e0b278e7SLuke D. Jones 		pr_warn("Failed to set ppt_platform_sppt (result): 0x%x\n", result);
1144e0b278e7SLuke D. Jones 		return -EIO;
1145e0b278e7SLuke D. Jones 	}
1146e0b278e7SLuke D. Jones 
1147e0b278e7SLuke D. Jones 	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_platform_sppt");
1148e0b278e7SLuke D. Jones 
1149e0b278e7SLuke D. Jones 	return count;
1150e0b278e7SLuke D. Jones }
1151e0b278e7SLuke D. Jones static DEVICE_ATTR_WO(ppt_platform_sppt);
1152e0b278e7SLuke D. Jones 
1153e0b278e7SLuke D. Jones /* Tunable: NVIDIA dynamic boost *********************************************/
nv_dynamic_boost_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1154e0b278e7SLuke D. Jones static ssize_t nv_dynamic_boost_store(struct device *dev,
1155e0b278e7SLuke D. Jones 				    struct device_attribute *attr,
1156e0b278e7SLuke D. Jones 				    const char *buf, size_t count)
1157e0b278e7SLuke D. Jones {
1158e0b278e7SLuke D. Jones 	int result, err;
1159e0b278e7SLuke D. Jones 	u32 value;
1160e0b278e7SLuke D. Jones 
1161e0b278e7SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
1162e0b278e7SLuke D. Jones 
1163e0b278e7SLuke D. Jones 	result = kstrtou32(buf, 10, &value);
1164e0b278e7SLuke D. Jones 	if (result)
1165e0b278e7SLuke D. Jones 		return result;
1166e0b278e7SLuke D. Jones 
1167e0b278e7SLuke D. Jones 	if (value < NVIDIA_BOOST_MIN || value > NVIDIA_BOOST_MAX)
1168e0b278e7SLuke D. Jones 		return -EINVAL;
1169e0b278e7SLuke D. Jones 
1170e0b278e7SLuke D. Jones 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_NV_DYN_BOOST, value, &result);
1171e0b278e7SLuke D. Jones 	if (err) {
1172e0b278e7SLuke D. Jones 		pr_warn("Failed to set nv_dynamic_boost: %d\n", err);
1173e0b278e7SLuke D. Jones 		return err;
1174e0b278e7SLuke D. Jones 	}
1175e0b278e7SLuke D. Jones 
1176e0b278e7SLuke D. Jones 	if (result > 1) {
1177e0b278e7SLuke D. Jones 		pr_warn("Failed to set nv_dynamic_boost (result): 0x%x\n", result);
1178e0b278e7SLuke D. Jones 		return -EIO;
1179e0b278e7SLuke D. Jones 	}
1180e0b278e7SLuke D. Jones 
1181e0b278e7SLuke D. Jones 	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "nv_dynamic_boost");
1182e0b278e7SLuke D. Jones 
1183e0b278e7SLuke D. Jones 	return count;
1184e0b278e7SLuke D. Jones }
1185e0b278e7SLuke D. Jones static DEVICE_ATTR_WO(nv_dynamic_boost);
1186e0b278e7SLuke D. Jones 
1187e0b278e7SLuke D. Jones /* Tunable: NVIDIA temperature target ****************************************/
nv_temp_target_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1188e0b278e7SLuke D. Jones static ssize_t nv_temp_target_store(struct device *dev,
1189e0b278e7SLuke D. Jones 				    struct device_attribute *attr,
1190e0b278e7SLuke D. Jones 				    const char *buf, size_t count)
1191e0b278e7SLuke D. Jones {
1192e0b278e7SLuke D. Jones 	int result, err;
1193e0b278e7SLuke D. Jones 	u32 value;
1194e0b278e7SLuke D. Jones 
1195e0b278e7SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
1196e0b278e7SLuke D. Jones 
1197e0b278e7SLuke D. Jones 	result = kstrtou32(buf, 10, &value);
1198e0b278e7SLuke D. Jones 	if (result)
1199e0b278e7SLuke D. Jones 		return result;
1200e0b278e7SLuke D. Jones 
1201e0b278e7SLuke D. Jones 	if (value < NVIDIA_TEMP_MIN || value > NVIDIA_TEMP_MAX)
1202e0b278e7SLuke D. Jones 		return -EINVAL;
1203e0b278e7SLuke D. Jones 
1204e0b278e7SLuke D. Jones 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_NV_THERM_TARGET, value, &result);
1205e0b278e7SLuke D. Jones 	if (err) {
1206e0b278e7SLuke D. Jones 		pr_warn("Failed to set nv_temp_target: %d\n", err);
1207e0b278e7SLuke D. Jones 		return err;
1208e0b278e7SLuke D. Jones 	}
1209e0b278e7SLuke D. Jones 
1210e0b278e7SLuke D. Jones 	if (result > 1) {
1211e0b278e7SLuke D. Jones 		pr_warn("Failed to set nv_temp_target (result): 0x%x\n", result);
1212e0b278e7SLuke D. Jones 		return -EIO;
1213e0b278e7SLuke D. Jones 	}
1214e0b278e7SLuke D. Jones 
1215e0b278e7SLuke D. Jones 	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "nv_temp_target");
1216e0b278e7SLuke D. Jones 
1217e0b278e7SLuke D. Jones 	return count;
1218e0b278e7SLuke D. Jones }
1219e0b278e7SLuke D. Jones static DEVICE_ATTR_WO(nv_temp_target);
1220e0b278e7SLuke D. Jones 
12217973353eSKristian Klausen /* Battery ********************************************************************/
12227973353eSKristian Klausen 
12237973353eSKristian Klausen /* The battery maximum charging percentage */
12247973353eSKristian Klausen static int charge_end_threshold;
12257973353eSKristian Klausen 
charge_control_end_threshold_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)12267973353eSKristian Klausen static ssize_t charge_control_end_threshold_store(struct device *dev,
12277973353eSKristian Klausen 						  struct device_attribute *attr,
12287973353eSKristian Klausen 						  const char *buf, size_t count)
12297973353eSKristian Klausen {
12307973353eSKristian Klausen 	int value, ret, rv;
12317973353eSKristian Klausen 
12327973353eSKristian Klausen 	ret = kstrtouint(buf, 10, &value);
12337973353eSKristian Klausen 	if (ret)
12347973353eSKristian Klausen 		return ret;
12357973353eSKristian Klausen 
12367973353eSKristian Klausen 	if (value < 0 || value > 100)
12377973353eSKristian Klausen 		return -EINVAL;
12387973353eSKristian Klausen 
12397973353eSKristian Klausen 	ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, value, &rv);
12407973353eSKristian Klausen 	if (ret)
12417973353eSKristian Klausen 		return ret;
12427973353eSKristian Klausen 
12437973353eSKristian Klausen 	if (rv != 1)
12447973353eSKristian Klausen 		return -EIO;
12457973353eSKristian Klausen 
12467973353eSKristian Klausen 	/* There isn't any method in the DSDT to read the threshold, so we
12477973353eSKristian Klausen 	 * save the threshold.
12487973353eSKristian Klausen 	 */
12497973353eSKristian Klausen 	charge_end_threshold = value;
12507973353eSKristian Klausen 	return count;
12517973353eSKristian Klausen }
12527973353eSKristian Klausen 
charge_control_end_threshold_show(struct device * device,struct device_attribute * attr,char * buf)12537973353eSKristian Klausen static ssize_t charge_control_end_threshold_show(struct device *device,
12547973353eSKristian Klausen 						 struct device_attribute *attr,
12557973353eSKristian Klausen 						 char *buf)
12567973353eSKristian Klausen {
1257170f0da2SLuke D. Jones 	return sysfs_emit(buf, "%d\n", charge_end_threshold);
12587973353eSKristian Klausen }
12597973353eSKristian Klausen 
12607973353eSKristian Klausen static DEVICE_ATTR_RW(charge_control_end_threshold);
12617973353eSKristian Klausen 
asus_wmi_battery_add(struct power_supply * battery,struct acpi_battery_hook * hook)1262878a82c2SArmin Wolf static int asus_wmi_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
12637973353eSKristian Klausen {
12647973353eSKristian Klausen 	/* The WMI method does not provide a way to specific a battery, so we
12657973353eSKristian Klausen 	 * just assume it is the first battery.
12666b3586d4SKristian Klausen 	 * Note: On some newer ASUS laptops (Zenbook UM431DA), the primary/first
12676b3586d4SKristian Klausen 	 * battery is named BATT.
12687973353eSKristian Klausen 	 */
12696b3586d4SKristian Klausen 	if (strcmp(battery->desc->name, "BAT0") != 0 &&
12709a33e375SVasiliy Kupriakov 	    strcmp(battery->desc->name, "BAT1") != 0 &&
12711d2dd379SMarius Iacob 	    strcmp(battery->desc->name, "BATC") != 0 &&
12726b3586d4SKristian Klausen 	    strcmp(battery->desc->name, "BATT") != 0)
12737973353eSKristian Klausen 		return -ENODEV;
12747973353eSKristian Klausen 
12757973353eSKristian Klausen 	if (device_create_file(&battery->dev,
12767973353eSKristian Klausen 	    &dev_attr_charge_control_end_threshold))
12777973353eSKristian Klausen 		return -ENODEV;
12787973353eSKristian Klausen 
12797973353eSKristian Klausen 	/* The charge threshold is only reset when the system is power cycled,
12807973353eSKristian Klausen 	 * and we can't get the current threshold so let set it to 100% when
12817973353eSKristian Klausen 	 * a battery is added.
12827973353eSKristian Klausen 	 */
12837973353eSKristian Klausen 	asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, 100, NULL);
12847973353eSKristian Klausen 	charge_end_threshold = 100;
12857973353eSKristian Klausen 
12867973353eSKristian Klausen 	return 0;
12877973353eSKristian Klausen }
12887973353eSKristian Klausen 
asus_wmi_battery_remove(struct power_supply * battery,struct acpi_battery_hook * hook)1289878a82c2SArmin Wolf static int asus_wmi_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook)
12907973353eSKristian Klausen {
12917973353eSKristian Klausen 	device_remove_file(&battery->dev,
12927973353eSKristian Klausen 			   &dev_attr_charge_control_end_threshold);
12937973353eSKristian Klausen 	return 0;
12947973353eSKristian Klausen }
12957973353eSKristian Klausen 
12967973353eSKristian Klausen static struct acpi_battery_hook battery_hook = {
12977973353eSKristian Klausen 	.add_battery = asus_wmi_battery_add,
12987973353eSKristian Klausen 	.remove_battery = asus_wmi_battery_remove,
12997973353eSKristian Klausen 	.name = "ASUS Battery Extension",
13007973353eSKristian Klausen };
13017973353eSKristian Klausen 
asus_wmi_battery_init(struct asus_wmi * asus)13027973353eSKristian Klausen static void asus_wmi_battery_init(struct asus_wmi *asus)
13037973353eSKristian Klausen {
13047973353eSKristian Klausen 	asus->battery_rsoc_available = false;
13057973353eSKristian Klausen 	if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_RSOC)) {
13067973353eSKristian Klausen 		asus->battery_rsoc_available = true;
13077973353eSKristian Klausen 		battery_hook_register(&battery_hook);
13087973353eSKristian Klausen 	}
13097973353eSKristian Klausen }
13107973353eSKristian Klausen 
asus_wmi_battery_exit(struct asus_wmi * asus)13117973353eSKristian Klausen static void asus_wmi_battery_exit(struct asus_wmi *asus)
13127973353eSKristian Klausen {
13137973353eSKristian Klausen 	if (asus->battery_rsoc_available)
13147973353eSKristian Klausen 		battery_hook_unregister(&battery_hook);
13157973353eSKristian Klausen }
13167973353eSKristian Klausen 
131754a3121fSYurii Pavlovskyi /* LEDs ***********************************************************************/
131854a3121fSYurii Pavlovskyi 
13195b799d4fSCorentin Chary /*
13205b799d4fSCorentin Chary  * These functions actually update the LED's, and are called from a
13215b799d4fSCorentin Chary  * workqueue. By doing this as separate work rather than when the LED
1322e12e6d94SCorentin Chary  * subsystem asks, we avoid messing with the Asus ACPI stuff during a
13235b799d4fSCorentin Chary  * potentially bad time, such as a timer interrupt.
13245b799d4fSCorentin Chary  */
tpd_led_update(struct work_struct * work)13255b799d4fSCorentin Chary static void tpd_led_update(struct work_struct *work)
13265b799d4fSCorentin Chary {
13275b799d4fSCorentin Chary 	int ctrl_param;
1328e12e6d94SCorentin Chary 	struct asus_wmi *asus;
13295b799d4fSCorentin Chary 
1330e12e6d94SCorentin Chary 	asus = container_of(work, struct asus_wmi, tpd_led_work);
13315b799d4fSCorentin Chary 
1332e12e6d94SCorentin Chary 	ctrl_param = asus->tpd_led_wk;
1333e12e6d94SCorentin Chary 	asus_wmi_set_devstate(ASUS_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL);
13345b799d4fSCorentin Chary }
13355b799d4fSCorentin Chary 
tpd_led_set(struct led_classdev * led_cdev,enum led_brightness value)13365b799d4fSCorentin Chary static void tpd_led_set(struct led_classdev *led_cdev,
13375b799d4fSCorentin Chary 			enum led_brightness value)
13385b799d4fSCorentin Chary {
1339e12e6d94SCorentin Chary 	struct asus_wmi *asus;
13405b799d4fSCorentin Chary 
1341e12e6d94SCorentin Chary 	asus = container_of(led_cdev, struct asus_wmi, tpd_led);
13425b799d4fSCorentin Chary 
1343e12e6d94SCorentin Chary 	asus->tpd_led_wk = !!value;
1344e12e6d94SCorentin Chary 	queue_work(asus->led_workqueue, &asus->tpd_led_work);
13455b799d4fSCorentin Chary }
13465b799d4fSCorentin Chary 
read_tpd_led_state(struct asus_wmi * asus)1347e12e6d94SCorentin Chary static int read_tpd_led_state(struct asus_wmi *asus)
13485b799d4fSCorentin Chary {
13491d070f89SCorentin Chary 	return asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_TOUCHPAD_LED);
13505b799d4fSCorentin Chary }
13515b799d4fSCorentin Chary 
tpd_led_get(struct led_classdev * led_cdev)13525b799d4fSCorentin Chary static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
13535b799d4fSCorentin Chary {
1354e12e6d94SCorentin Chary 	struct asus_wmi *asus;
13555b799d4fSCorentin Chary 
1356e12e6d94SCorentin Chary 	asus = container_of(led_cdev, struct asus_wmi, tpd_led);
13575b799d4fSCorentin Chary 
1358e12e6d94SCorentin Chary 	return read_tpd_led_state(asus);
13595b799d4fSCorentin Chary }
13605b799d4fSCorentin Chary 
kbd_led_update(struct asus_wmi * asus)13619fe44fc9SJian-Hong Pan static void kbd_led_update(struct asus_wmi *asus)
13625b799d4fSCorentin Chary {
1363e9809c0bSCorentin Chary 	int ctrl_param = 0;
13645b799d4fSCorentin Chary 
1365e9809c0bSCorentin Chary 	ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
1366e9809c0bSCorentin Chary 	asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);
13675b799d4fSCorentin Chary }
13685b799d4fSCorentin Chary 
kbd_led_read(struct asus_wmi * asus,int * level,int * env)1369e9809c0bSCorentin Chary static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
1370e9809c0bSCorentin Chary {
1371e9809c0bSCorentin Chary 	int retval;
1372e9809c0bSCorentin Chary 
1373e9809c0bSCorentin Chary 	/*
1374e9809c0bSCorentin Chary 	 * bits 0-2: level
1375e9809c0bSCorentin Chary 	 * bit 7: light on/off
1376e9809c0bSCorentin Chary 	 * bit 8-10: environment (0: dark, 1: normal, 2: light)
1377e9809c0bSCorentin Chary 	 * bit 17: status unknown
1378e9809c0bSCorentin Chary 	 */
1379e9809c0bSCorentin Chary 	retval = asus_wmi_get_devstate_bits(asus, ASUS_WMI_DEVID_KBD_BACKLIGHT,
1380e9809c0bSCorentin Chary 					    0xFFFF);
1381e9809c0bSCorentin Chary 
1382af965e97SCorentin Chary 	/* Unknown status is considered as off */
1383e9809c0bSCorentin Chary 	if (retval == 0x8000)
1384af965e97SCorentin Chary 		retval = 0;
1385e9809c0bSCorentin Chary 
1386127e1dfcSAndy Shevchenko 	if (retval < 0)
1387127e1dfcSAndy Shevchenko 		return retval;
1388127e1dfcSAndy Shevchenko 
1389e9809c0bSCorentin Chary 	if (level)
1390c09b2237SCorentin Chary 		*level = retval & 0x7F;
1391e9809c0bSCorentin Chary 	if (env)
1392e9809c0bSCorentin Chary 		*env = (retval >> 8) & 0x7F;
1393127e1dfcSAndy Shevchenko 	return 0;
1394e9809c0bSCorentin Chary }
1395e9809c0bSCorentin Chary 
do_kbd_led_set(struct led_classdev * led_cdev,int value)1396dbb3d78fSChris Chiu static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
1397e9809c0bSCorentin Chary {
1398e9809c0bSCorentin Chary 	struct asus_wmi *asus;
1399dbb3d78fSChris Chiu 	int max_level;
1400e9809c0bSCorentin Chary 
1401e9809c0bSCorentin Chary 	asus = container_of(led_cdev, struct asus_wmi, kbd_led);
1402dbb3d78fSChris Chiu 	max_level = asus->kbd_led.max_brightness;
1403e9809c0bSCorentin Chary 
140422757520SAndy Shevchenko 	asus->kbd_led_wk = clamp_val(value, 0, max_level);
14059fe44fc9SJian-Hong Pan 	kbd_led_update(asus);
1406e9809c0bSCorentin Chary }
1407e9809c0bSCorentin Chary 
kbd_led_set(struct led_classdev * led_cdev,enum led_brightness value)1408dbb3d78fSChris Chiu static void kbd_led_set(struct led_classdev *led_cdev,
1409dbb3d78fSChris Chiu 			enum led_brightness value)
1410dbb3d78fSChris Chiu {
14113e58167aSYurii Pavlovskyi 	/* Prevent disabling keyboard backlight on module unregister */
14123e58167aSYurii Pavlovskyi 	if (led_cdev->flags & LED_UNREGISTERING)
14133e58167aSYurii Pavlovskyi 		return;
14143e58167aSYurii Pavlovskyi 
1415dbb3d78fSChris Chiu 	do_kbd_led_set(led_cdev, value);
1416dbb3d78fSChris Chiu }
1417dbb3d78fSChris Chiu 
kbd_led_set_by_kbd(struct asus_wmi * asus,enum led_brightness value)141829f6eb53SJian-Hong Pan static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value)
141929f6eb53SJian-Hong Pan {
142029f6eb53SJian-Hong Pan 	struct led_classdev *led_cdev = &asus->kbd_led;
142129f6eb53SJian-Hong Pan 
142229f6eb53SJian-Hong Pan 	do_kbd_led_set(led_cdev, value);
142329f6eb53SJian-Hong Pan 	led_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk);
142429f6eb53SJian-Hong Pan }
142529f6eb53SJian-Hong Pan 
kbd_led_get(struct led_classdev * led_cdev)1426e9809c0bSCorentin Chary static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
1427e9809c0bSCorentin Chary {
1428e9809c0bSCorentin Chary 	struct asus_wmi *asus;
1429e9809c0bSCorentin Chary 	int retval, value;
1430e9809c0bSCorentin Chary 
1431e9809c0bSCorentin Chary 	asus = container_of(led_cdev, struct asus_wmi, kbd_led);
1432e9809c0bSCorentin Chary 
1433e9809c0bSCorentin Chary 	retval = kbd_led_read(asus, &value, NULL);
1434e9809c0bSCorentin Chary 	if (retval < 0)
1435e9809c0bSCorentin Chary 		return retval;
1436e9809c0bSCorentin Chary 
1437e9809c0bSCorentin Chary 	return value;
14385b799d4fSCorentin Chary }
14395b799d4fSCorentin Chary 
wlan_led_unknown_state(struct asus_wmi * asus)14406cae06e6SAceLan Kao static int wlan_led_unknown_state(struct asus_wmi *asus)
14416cae06e6SAceLan Kao {
14426cae06e6SAceLan Kao 	u32 result;
14436cae06e6SAceLan Kao 
14446cae06e6SAceLan Kao 	asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
14456cae06e6SAceLan Kao 
14466cae06e6SAceLan Kao 	return result & ASUS_WMI_DSTS_UNKNOWN_BIT;
14476cae06e6SAceLan Kao }
14486cae06e6SAceLan Kao 
wlan_led_update(struct work_struct * work)14496cae06e6SAceLan Kao static void wlan_led_update(struct work_struct *work)
14506cae06e6SAceLan Kao {
14516cae06e6SAceLan Kao 	int ctrl_param;
14526cae06e6SAceLan Kao 	struct asus_wmi *asus;
14536cae06e6SAceLan Kao 
14546cae06e6SAceLan Kao 	asus = container_of(work, struct asus_wmi, wlan_led_work);
14556cae06e6SAceLan Kao 
14566cae06e6SAceLan Kao 	ctrl_param = asus->wlan_led_wk;
14576cae06e6SAceLan Kao 	asus_wmi_set_devstate(ASUS_WMI_DEVID_WIRELESS_LED, ctrl_param, NULL);
14586cae06e6SAceLan Kao }
14596cae06e6SAceLan Kao 
wlan_led_set(struct led_classdev * led_cdev,enum led_brightness value)14606cae06e6SAceLan Kao static void wlan_led_set(struct led_classdev *led_cdev,
14616cae06e6SAceLan Kao 			 enum led_brightness value)
14626cae06e6SAceLan Kao {
14636cae06e6SAceLan Kao 	struct asus_wmi *asus;
14646cae06e6SAceLan Kao 
14656cae06e6SAceLan Kao 	asus = container_of(led_cdev, struct asus_wmi, wlan_led);
14666cae06e6SAceLan Kao 
14676cae06e6SAceLan Kao 	asus->wlan_led_wk = !!value;
14686cae06e6SAceLan Kao 	queue_work(asus->led_workqueue, &asus->wlan_led_work);
14696cae06e6SAceLan Kao }
14706cae06e6SAceLan Kao 
wlan_led_get(struct led_classdev * led_cdev)14716cae06e6SAceLan Kao static enum led_brightness wlan_led_get(struct led_classdev *led_cdev)
14726cae06e6SAceLan Kao {
14736cae06e6SAceLan Kao 	struct asus_wmi *asus;
14746cae06e6SAceLan Kao 	u32 result;
14756cae06e6SAceLan Kao 
14766cae06e6SAceLan Kao 	asus = container_of(led_cdev, struct asus_wmi, wlan_led);
14776cae06e6SAceLan Kao 	asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
14786cae06e6SAceLan Kao 
14796cae06e6SAceLan Kao 	return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
14806cae06e6SAceLan Kao }
14816cae06e6SAceLan Kao 
lightbar_led_update(struct work_struct * work)14824c059844SMaxime Bellengé static void lightbar_led_update(struct work_struct *work)
14834c059844SMaxime Bellengé {
14844c059844SMaxime Bellengé 	struct asus_wmi *asus;
14854c059844SMaxime Bellengé 	int ctrl_param;
14864c059844SMaxime Bellengé 
14874c059844SMaxime Bellengé 	asus = container_of(work, struct asus_wmi, lightbar_led_work);
14884c059844SMaxime Bellengé 
14894c059844SMaxime Bellengé 	ctrl_param = asus->lightbar_led_wk;
14904c059844SMaxime Bellengé 	asus_wmi_set_devstate(ASUS_WMI_DEVID_LIGHTBAR, ctrl_param, NULL);
14914c059844SMaxime Bellengé }
14924c059844SMaxime Bellengé 
lightbar_led_set(struct led_classdev * led_cdev,enum led_brightness value)14934c059844SMaxime Bellengé static void lightbar_led_set(struct led_classdev *led_cdev,
14944c059844SMaxime Bellengé 			     enum led_brightness value)
14954c059844SMaxime Bellengé {
14964c059844SMaxime Bellengé 	struct asus_wmi *asus;
14974c059844SMaxime Bellengé 
14984c059844SMaxime Bellengé 	asus = container_of(led_cdev, struct asus_wmi, lightbar_led);
14994c059844SMaxime Bellengé 
15004c059844SMaxime Bellengé 	asus->lightbar_led_wk = !!value;
15014c059844SMaxime Bellengé 	queue_work(asus->led_workqueue, &asus->lightbar_led_work);
15024c059844SMaxime Bellengé }
15034c059844SMaxime Bellengé 
lightbar_led_get(struct led_classdev * led_cdev)15044c059844SMaxime Bellengé static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev)
15054c059844SMaxime Bellengé {
15064c059844SMaxime Bellengé 	struct asus_wmi *asus;
15074c059844SMaxime Bellengé 	u32 result;
15084c059844SMaxime Bellengé 
15094c059844SMaxime Bellengé 	asus = container_of(led_cdev, struct asus_wmi, lightbar_led);
15104c059844SMaxime Bellengé 	asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result);
15114c059844SMaxime Bellengé 
15124c059844SMaxime Bellengé 	return result & ASUS_WMI_DSTS_LIGHTBAR_MASK;
15134c059844SMaxime Bellengé }
15144c059844SMaxime Bellengé 
micmute_led_set(struct led_classdev * led_cdev,enum led_brightness brightness)1515b644c955SPaddyKP_Yao static int micmute_led_set(struct led_classdev *led_cdev,
1516b644c955SPaddyKP_Yao 			   enum led_brightness brightness)
1517b644c955SPaddyKP_Yao {
1518b644c955SPaddyKP_Yao 	int state = brightness != LED_OFF;
1519b644c955SPaddyKP_Yao 	int err;
1520b644c955SPaddyKP_Yao 
1521b644c955SPaddyKP_Yao 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MICMUTE_LED, state, NULL);
1522b644c955SPaddyKP_Yao 	return err < 0 ? err : 0;
1523b644c955SPaddyKP_Yao }
1524b644c955SPaddyKP_Yao 
asus_wmi_led_exit(struct asus_wmi * asus)1525e12e6d94SCorentin Chary static void asus_wmi_led_exit(struct asus_wmi *asus)
15265b799d4fSCorentin Chary {
1527e9298028SAxel Lin 	led_classdev_unregister(&asus->kbd_led);
1528e12e6d94SCorentin Chary 	led_classdev_unregister(&asus->tpd_led);
15296cae06e6SAceLan Kao 	led_classdev_unregister(&asus->wlan_led);
15304c059844SMaxime Bellengé 	led_classdev_unregister(&asus->lightbar_led);
1531b644c955SPaddyKP_Yao 	led_classdev_unregister(&asus->micmute_led);
15322225dba2SAndy Shevchenko 
1533e12e6d94SCorentin Chary 	if (asus->led_workqueue)
1534e12e6d94SCorentin Chary 		destroy_workqueue(asus->led_workqueue);
15355b799d4fSCorentin Chary }
15365b799d4fSCorentin Chary 
asus_wmi_led_init(struct asus_wmi * asus)1537e9809c0bSCorentin Chary static int asus_wmi_led_init(struct asus_wmi *asus)
1538e9809c0bSCorentin Chary {
1539e305a71cSLuke D. Jones 	int rv = 0, num_rgb_groups = 0, led_val;
1540e305a71cSLuke D. Jones 
1541e305a71cSLuke D. Jones 	if (asus->kbd_rgb_mode_available)
1542e305a71cSLuke D. Jones 		kbd_rgb_mode_groups[num_rgb_groups++] = &kbd_rgb_mode_group;
154361f64515SLuke D. Jones 	if (asus->kbd_rgb_state_available)
154461f64515SLuke D. Jones 		kbd_rgb_mode_groups[num_rgb_groups++] = &kbd_rgb_state_group;
1545e9809c0bSCorentin Chary 
1546e9809c0bSCorentin Chary 	asus->led_workqueue = create_singlethread_workqueue("led_workqueue");
1547e9809c0bSCorentin Chary 	if (!asus->led_workqueue)
1548e9809c0bSCorentin Chary 		return -ENOMEM;
1549e9809c0bSCorentin Chary 
1550e9809c0bSCorentin Chary 	if (read_tpd_led_state(asus) >= 0) {
1551e9809c0bSCorentin Chary 		INIT_WORK(&asus->tpd_led_work, tpd_led_update);
1552e9809c0bSCorentin Chary 
1553e9809c0bSCorentin Chary 		asus->tpd_led.name = "asus::touchpad";
1554e9809c0bSCorentin Chary 		asus->tpd_led.brightness_set = tpd_led_set;
1555e9809c0bSCorentin Chary 		asus->tpd_led.brightness_get = tpd_led_get;
1556e9809c0bSCorentin Chary 		asus->tpd_led.max_brightness = 1;
1557e9809c0bSCorentin Chary 
1558e9809c0bSCorentin Chary 		rv = led_classdev_register(&asus->platform_device->dev,
1559e9809c0bSCorentin Chary 					   &asus->tpd_led);
1560e9809c0bSCorentin Chary 		if (rv)
1561e9809c0bSCorentin Chary 			goto error;
1562e9809c0bSCorentin Chary 	}
1563e9809c0bSCorentin Chary 
15648853a2f6SYurii Pavlovskyi 	if (!kbd_led_read(asus, &led_val, NULL)) {
156530734049SOleksij Rempel 		asus->kbd_led_wk = led_val;
1566e9809c0bSCorentin Chary 		asus->kbd_led.name = "asus::kbd_backlight";
1567dbb3d78fSChris Chiu 		asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
1568e9809c0bSCorentin Chary 		asus->kbd_led.brightness_set = kbd_led_set;
1569e9809c0bSCorentin Chary 		asus->kbd_led.brightness_get = kbd_led_get;
1570e9809c0bSCorentin Chary 		asus->kbd_led.max_brightness = 3;
1571e9809c0bSCorentin Chary 
1572e305a71cSLuke D. Jones 		if (num_rgb_groups != 0)
1573e305a71cSLuke D. Jones 			asus->kbd_led.groups = kbd_rgb_mode_groups;
1574e305a71cSLuke D. Jones 
1575e9809c0bSCorentin Chary 		rv = led_classdev_register(&asus->platform_device->dev,
1576e9809c0bSCorentin Chary 					   &asus->kbd_led);
15776cae06e6SAceLan Kao 		if (rv)
15786cae06e6SAceLan Kao 			goto error;
15796cae06e6SAceLan Kao 	}
15806cae06e6SAceLan Kao 
1581f1fc0321SDaniel Drake 	if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED)
1582f1fc0321SDaniel Drake 			&& (asus->driver->quirks->wapf > 0)) {
15836cae06e6SAceLan Kao 		INIT_WORK(&asus->wlan_led_work, wlan_led_update);
15846cae06e6SAceLan Kao 
15856cae06e6SAceLan Kao 		asus->wlan_led.name = "asus::wlan";
15866cae06e6SAceLan Kao 		asus->wlan_led.brightness_set = wlan_led_set;
15876cae06e6SAceLan Kao 		if (!wlan_led_unknown_state(asus))
15886cae06e6SAceLan Kao 			asus->wlan_led.brightness_get = wlan_led_get;
15896cae06e6SAceLan Kao 		asus->wlan_led.flags = LED_CORE_SUSPENDRESUME;
15906cae06e6SAceLan Kao 		asus->wlan_led.max_brightness = 1;
15916cae06e6SAceLan Kao 		asus->wlan_led.default_trigger = "asus-wlan";
15926cae06e6SAceLan Kao 
15936cae06e6SAceLan Kao 		rv = led_classdev_register(&asus->platform_device->dev,
15946cae06e6SAceLan Kao 					   &asus->wlan_led);
15954c059844SMaxime Bellengé 		if (rv)
15964c059844SMaxime Bellengé 			goto error;
15974c059844SMaxime Bellengé 	}
15984c059844SMaxime Bellengé 
1599f1fc0321SDaniel Drake 	if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_LIGHTBAR)) {
16004c059844SMaxime Bellengé 		INIT_WORK(&asus->lightbar_led_work, lightbar_led_update);
16014c059844SMaxime Bellengé 
16024c059844SMaxime Bellengé 		asus->lightbar_led.name = "asus::lightbar";
16034c059844SMaxime Bellengé 		asus->lightbar_led.brightness_set = lightbar_led_set;
16044c059844SMaxime Bellengé 		asus->lightbar_led.brightness_get = lightbar_led_get;
16054c059844SMaxime Bellengé 		asus->lightbar_led.max_brightness = 1;
16064c059844SMaxime Bellengé 
16074c059844SMaxime Bellengé 		rv = led_classdev_register(&asus->platform_device->dev,
16084c059844SMaxime Bellengé 					   &asus->lightbar_led);
1609e9809c0bSCorentin Chary 	}
1610e9809c0bSCorentin Chary 
1611b644c955SPaddyKP_Yao 	if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MICMUTE_LED)) {
16128028d5d4SPaddyKP_Yao 		asus->micmute_led.name = "platform::micmute";
1613b644c955SPaddyKP_Yao 		asus->micmute_led.max_brightness = 1;
1614b644c955SPaddyKP_Yao 		asus->micmute_led.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
1615b644c955SPaddyKP_Yao 		asus->micmute_led.brightness_set_blocking = micmute_led_set;
1616b644c955SPaddyKP_Yao 		asus->micmute_led.default_trigger = "audio-micmute";
1617b644c955SPaddyKP_Yao 
1618b644c955SPaddyKP_Yao 		rv = led_classdev_register(&asus->platform_device->dev,
1619b644c955SPaddyKP_Yao 						&asus->micmute_led);
1620b644c955SPaddyKP_Yao 		if (rv)
1621b644c955SPaddyKP_Yao 			goto error;
1622b644c955SPaddyKP_Yao 	}
1623b644c955SPaddyKP_Yao 
1624e9809c0bSCorentin Chary error:
1625e9809c0bSCorentin Chary 	if (rv)
1626e9809c0bSCorentin Chary 		asus_wmi_led_exit(asus);
1627e9809c0bSCorentin Chary 
1628e9809c0bSCorentin Chary 	return rv;
1629e9809c0bSCorentin Chary }
1630e9809c0bSCorentin Chary 
163154a3121fSYurii Pavlovskyi /* RF *************************************************************************/
1632e9809c0bSCorentin Chary 
16335b799d4fSCorentin Chary /*
16345b799d4fSCorentin Chary  * PCI hotplug (for wlan rfkill)
16355b799d4fSCorentin Chary  */
asus_wlan_rfkill_blocked(struct asus_wmi * asus)1636e12e6d94SCorentin Chary static bool asus_wlan_rfkill_blocked(struct asus_wmi *asus)
16375b799d4fSCorentin Chary {
16381d070f89SCorentin Chary 	int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
16395b799d4fSCorentin Chary 
16405b799d4fSCorentin Chary 	if (result < 0)
16415b799d4fSCorentin Chary 		return false;
16425b799d4fSCorentin Chary 	return !result;
16435b799d4fSCorentin Chary }
16445b799d4fSCorentin Chary 
asus_rfkill_hotplug(struct asus_wmi * asus)1645e12e6d94SCorentin Chary static void asus_rfkill_hotplug(struct asus_wmi *asus)
16465b799d4fSCorentin Chary {
16475b799d4fSCorentin Chary 	struct pci_dev *dev;
16485b799d4fSCorentin Chary 	struct pci_bus *bus;
16495b799d4fSCorentin Chary 	bool blocked;
16505b799d4fSCorentin Chary 	bool absent;
16515b799d4fSCorentin Chary 	u32 l;
16525b799d4fSCorentin Chary 
1653e12e6d94SCorentin Chary 	mutex_lock(&asus->wmi_lock);
1654e12e6d94SCorentin Chary 	blocked = asus_wlan_rfkill_blocked(asus);
1655e12e6d94SCorentin Chary 	mutex_unlock(&asus->wmi_lock);
16565b799d4fSCorentin Chary 
1657e12e6d94SCorentin Chary 	mutex_lock(&asus->hotplug_lock);
16588b9ec1daSRafael J. Wysocki 	pci_lock_rescan_remove();
16595b799d4fSCorentin Chary 
1660a7ce3f04SCorentin Chary 	if (asus->wlan.rfkill)
1661a7ce3f04SCorentin Chary 		rfkill_set_sw_state(asus->wlan.rfkill, blocked);
16625b799d4fSCorentin Chary 
1663125450f8SLukas Wunner 	if (asus->hotplug_slot.ops) {
16645b799d4fSCorentin Chary 		bus = pci_find_bus(0, 1);
16655b799d4fSCorentin Chary 		if (!bus) {
16665ad77dcfSJoe Perches 			pr_warn("Unable to find PCI bus 1?\n");
16675b799d4fSCorentin Chary 			goto out_unlock;
16685b799d4fSCorentin Chary 		}
16695b799d4fSCorentin Chary 
16705b799d4fSCorentin Chary 		if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
16715b799d4fSCorentin Chary 			pr_err("Unable to read PCI config space?\n");
16725b799d4fSCorentin Chary 			goto out_unlock;
16735b799d4fSCorentin Chary 		}
16745b799d4fSCorentin Chary 		absent = (l == 0xffffffff);
16755b799d4fSCorentin Chary 
16765b799d4fSCorentin Chary 		if (blocked != absent) {
16773ac7bf0dSAndy Shevchenko 			pr_warn("BIOS says wireless lan is %s, but the pci device is %s\n",
16785b799d4fSCorentin Chary 				blocked ? "blocked" : "unblocked",
16795b799d4fSCorentin Chary 				absent ? "absent" : "present");
16803ac7bf0dSAndy Shevchenko 			pr_warn("skipped wireless hotplug as probably inappropriate for this model\n");
16815b799d4fSCorentin Chary 			goto out_unlock;
16825b799d4fSCorentin Chary 		}
16835b799d4fSCorentin Chary 
16845b799d4fSCorentin Chary 		if (!blocked) {
16855b799d4fSCorentin Chary 			dev = pci_get_slot(bus, 0);
16865b799d4fSCorentin Chary 			if (dev) {
16875b799d4fSCorentin Chary 				/* Device already present */
16885b799d4fSCorentin Chary 				pci_dev_put(dev);
16895b799d4fSCorentin Chary 				goto out_unlock;
16905b799d4fSCorentin Chary 			}
16915b799d4fSCorentin Chary 			dev = pci_scan_single_device(bus, 0);
16925b799d4fSCorentin Chary 			if (dev) {
16935b799d4fSCorentin Chary 				pci_bus_assign_resources(bus);
1694c893d133SYijing Wang 				pci_bus_add_device(dev);
16955b799d4fSCorentin Chary 			}
16965b799d4fSCorentin Chary 		} else {
16975b799d4fSCorentin Chary 			dev = pci_get_slot(bus, 0);
16985b799d4fSCorentin Chary 			if (dev) {
1699210647afSYinghai Lu 				pci_stop_and_remove_bus_device(dev);
17005b799d4fSCorentin Chary 				pci_dev_put(dev);
17015b799d4fSCorentin Chary 			}
17025b799d4fSCorentin Chary 		}
17035b799d4fSCorentin Chary 	}
17045b799d4fSCorentin Chary 
17055b799d4fSCorentin Chary out_unlock:
17068b9ec1daSRafael J. Wysocki 	pci_unlock_rescan_remove();
1707e12e6d94SCorentin Chary 	mutex_unlock(&asus->hotplug_lock);
17085b799d4fSCorentin Chary }
17095b799d4fSCorentin Chary 
asus_rfkill_notify(acpi_handle handle,u32 event,void * data)1710e12e6d94SCorentin Chary static void asus_rfkill_notify(acpi_handle handle, u32 event, void *data)
17115b799d4fSCorentin Chary {
1712e12e6d94SCorentin Chary 	struct asus_wmi *asus = data;
17135b799d4fSCorentin Chary 
17145b799d4fSCorentin Chary 	if (event != ACPI_NOTIFY_BUS_CHECK)
17155b799d4fSCorentin Chary 		return;
17165b799d4fSCorentin Chary 
17175b799d4fSCorentin Chary 	/*
1718e12e6d94SCorentin Chary 	 * We can't call directly asus_rfkill_hotplug because most
17195b799d4fSCorentin Chary 	 * of the time WMBC is still being executed and not reetrant.
17205b799d4fSCorentin Chary 	 * There is currently no way to tell ACPICA that  we want this
1721e12e6d94SCorentin Chary 	 * method to be serialized, we schedule a asus_rfkill_hotplug
17225b799d4fSCorentin Chary 	 * call later, in a safer context.
17235b799d4fSCorentin Chary 	 */
1724e12e6d94SCorentin Chary 	queue_work(asus->hotplug_workqueue, &asus->hotplug_work);
17255b799d4fSCorentin Chary }
17265b799d4fSCorentin Chary 
asus_register_rfkill_notifier(struct asus_wmi * asus,char * node)1727e12e6d94SCorentin Chary static int asus_register_rfkill_notifier(struct asus_wmi *asus, char *node)
17285b799d4fSCorentin Chary {
17295b799d4fSCorentin Chary 	acpi_status status;
17305b799d4fSCorentin Chary 	acpi_handle handle;
17315b799d4fSCorentin Chary 
17325b799d4fSCorentin Chary 	status = acpi_get_handle(NULL, node, &handle);
1733127e1dfcSAndy Shevchenko 	if (ACPI_FAILURE(status))
1734127e1dfcSAndy Shevchenko 		return -ENODEV;
17355b799d4fSCorentin Chary 
1736127e1dfcSAndy Shevchenko 	status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
1737e12e6d94SCorentin Chary 					     asus_rfkill_notify, asus);
17385b799d4fSCorentin Chary 	if (ACPI_FAILURE(status))
17395ad77dcfSJoe Perches 		pr_warn("Failed to register notify on %s\n", node);
17405b799d4fSCorentin Chary 
17415b799d4fSCorentin Chary 	return 0;
17425b799d4fSCorentin Chary }
17435b799d4fSCorentin Chary 
asus_unregister_rfkill_notifier(struct asus_wmi * asus,char * node)1744e12e6d94SCorentin Chary static void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node)
17455b799d4fSCorentin Chary {
17465b799d4fSCorentin Chary 	acpi_status status = AE_OK;
17475b799d4fSCorentin Chary 	acpi_handle handle;
17485b799d4fSCorentin Chary 
17495b799d4fSCorentin Chary 	status = acpi_get_handle(NULL, node, &handle);
1750127e1dfcSAndy Shevchenko 	if (ACPI_FAILURE(status))
1751127e1dfcSAndy Shevchenko 		return;
17525b799d4fSCorentin Chary 
1753127e1dfcSAndy Shevchenko 	status = acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
1754e12e6d94SCorentin Chary 					    asus_rfkill_notify);
17555b799d4fSCorentin Chary 	if (ACPI_FAILURE(status))
1756127e1dfcSAndy Shevchenko 		pr_err("Error removing rfkill notify handler %s\n", node);
17575b799d4fSCorentin Chary }
17585b799d4fSCorentin Chary 
asus_get_adapter_status(struct hotplug_slot * hotplug_slot,u8 * value)1759e12e6d94SCorentin Chary static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot,
17605b799d4fSCorentin Chary 				   u8 *value)
17615b799d4fSCorentin Chary {
1762125450f8SLukas Wunner 	struct asus_wmi *asus = container_of(hotplug_slot,
1763125450f8SLukas Wunner 					     struct asus_wmi, hotplug_slot);
17641d070f89SCorentin Chary 	int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
17655b799d4fSCorentin Chary 
17665b799d4fSCorentin Chary 	if (result < 0)
17675b799d4fSCorentin Chary 		return result;
17685b799d4fSCorentin Chary 
17695b799d4fSCorentin Chary 	*value = !!result;
17705b799d4fSCorentin Chary 	return 0;
17715b799d4fSCorentin Chary }
17725b799d4fSCorentin Chary 
177381c4b5bfSLukas Wunner static const struct hotplug_slot_ops asus_hotplug_slot_ops = {
1774e12e6d94SCorentin Chary 	.get_adapter_status = asus_get_adapter_status,
1775e12e6d94SCorentin Chary 	.get_power_status = asus_get_adapter_status,
17765b799d4fSCorentin Chary };
17775b799d4fSCorentin Chary 
asus_hotplug_work(struct work_struct * work)1778e12e6d94SCorentin Chary static void asus_hotplug_work(struct work_struct *work)
17795b799d4fSCorentin Chary {
1780e12e6d94SCorentin Chary 	struct asus_wmi *asus;
17815b799d4fSCorentin Chary 
1782e12e6d94SCorentin Chary 	asus = container_of(work, struct asus_wmi, hotplug_work);
1783e12e6d94SCorentin Chary 	asus_rfkill_hotplug(asus);
17845b799d4fSCorentin Chary }
17855b799d4fSCorentin Chary 
asus_setup_pci_hotplug(struct asus_wmi * asus)1786e12e6d94SCorentin Chary static int asus_setup_pci_hotplug(struct asus_wmi *asus)
17875b799d4fSCorentin Chary {
17885b799d4fSCorentin Chary 	int ret = -ENOMEM;
17895b799d4fSCorentin Chary 	struct pci_bus *bus = pci_find_bus(0, 1);
17905b799d4fSCorentin Chary 
17915b799d4fSCorentin Chary 	if (!bus) {
17925b799d4fSCorentin Chary 		pr_err("Unable to find wifi PCI bus\n");
17935b799d4fSCorentin Chary 		return -ENODEV;
17945b799d4fSCorentin Chary 	}
17955b799d4fSCorentin Chary 
1796e12e6d94SCorentin Chary 	asus->hotplug_workqueue =
17975b799d4fSCorentin Chary 	    create_singlethread_workqueue("hotplug_workqueue");
1798e12e6d94SCorentin Chary 	if (!asus->hotplug_workqueue)
17995b799d4fSCorentin Chary 		goto error_workqueue;
18005b799d4fSCorentin Chary 
1801e12e6d94SCorentin Chary 	INIT_WORK(&asus->hotplug_work, asus_hotplug_work);
18025b799d4fSCorentin Chary 
1803125450f8SLukas Wunner 	asus->hotplug_slot.ops = &asus_hotplug_slot_ops;
18045b799d4fSCorentin Chary 
1805125450f8SLukas Wunner 	ret = pci_hp_register(&asus->hotplug_slot, bus, 0, "asus-wifi");
18065b799d4fSCorentin Chary 	if (ret) {
18075b799d4fSCorentin Chary 		pr_err("Unable to register hotplug slot - %d\n", ret);
18085b799d4fSCorentin Chary 		goto error_register;
18095b799d4fSCorentin Chary 	}
18105b799d4fSCorentin Chary 
18115b799d4fSCorentin Chary 	return 0;
18125b799d4fSCorentin Chary 
18135b799d4fSCorentin Chary error_register:
1814125450f8SLukas Wunner 	asus->hotplug_slot.ops = NULL;
1815e12e6d94SCorentin Chary 	destroy_workqueue(asus->hotplug_workqueue);
18165b799d4fSCorentin Chary error_workqueue:
18175b799d4fSCorentin Chary 	return ret;
18185b799d4fSCorentin Chary }
18195b799d4fSCorentin Chary 
18205b799d4fSCorentin Chary /*
18215b799d4fSCorentin Chary  * Rfkill devices
18225b799d4fSCorentin Chary  */
asus_rfkill_set(void * data,bool blocked)1823e12e6d94SCorentin Chary static int asus_rfkill_set(void *data, bool blocked)
18245b799d4fSCorentin Chary {
1825a7ce3f04SCorentin Chary 	struct asus_rfkill *priv = data;
18265b799d4fSCorentin Chary 	u32 ctrl_param = !blocked;
1827a50bd128SAceLan Kao 	u32 dev_id = priv->dev_id;
18285b799d4fSCorentin Chary 
1829a50bd128SAceLan Kao 	/*
1830a50bd128SAceLan Kao 	 * If the user bit is set, BIOS can't set and record the wlan status,
1831a50bd128SAceLan Kao 	 * it will report the value read from id ASUS_WMI_DEVID_WLAN_LED
1832a50bd128SAceLan Kao 	 * while we query the wlan status through WMI(ASUS_WMI_DEVID_WLAN).
1833a50bd128SAceLan Kao 	 * So, we have to record wlan status in id ASUS_WMI_DEVID_WLAN_LED
1834a50bd128SAceLan Kao 	 * while setting the wlan status through WMI.
1835a50bd128SAceLan Kao 	 * This is also the behavior that windows app will do.
1836a50bd128SAceLan Kao 	 */
1837a50bd128SAceLan Kao 	if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
1838a50bd128SAceLan Kao 	     priv->asus->driver->wlan_ctrl_by_user)
1839a50bd128SAceLan Kao 		dev_id = ASUS_WMI_DEVID_WLAN_LED;
1840a50bd128SAceLan Kao 
1841a50bd128SAceLan Kao 	return asus_wmi_set_devstate(dev_id, ctrl_param, NULL);
18425b799d4fSCorentin Chary }
18435b799d4fSCorentin Chary 
asus_rfkill_query(struct rfkill * rfkill,void * data)1844e12e6d94SCorentin Chary static void asus_rfkill_query(struct rfkill *rfkill, void *data)
18455b799d4fSCorentin Chary {
1846a7ce3f04SCorentin Chary 	struct asus_rfkill *priv = data;
18475b799d4fSCorentin Chary 	int result;
18485b799d4fSCorentin Chary 
18491d070f89SCorentin Chary 	result = asus_wmi_get_devstate_simple(priv->asus, priv->dev_id);
18505b799d4fSCorentin Chary 
18515b799d4fSCorentin Chary 	if (result < 0)
18525b799d4fSCorentin Chary 		return;
18535b799d4fSCorentin Chary 
1854a7ce3f04SCorentin Chary 	rfkill_set_sw_state(priv->rfkill, !result);
18555b799d4fSCorentin Chary }
18565b799d4fSCorentin Chary 
asus_rfkill_wlan_set(void * data,bool blocked)1857e12e6d94SCorentin Chary static int asus_rfkill_wlan_set(void *data, bool blocked)
18585b799d4fSCorentin Chary {
1859a7ce3f04SCorentin Chary 	struct asus_rfkill *priv = data;
1860a7ce3f04SCorentin Chary 	struct asus_wmi *asus = priv->asus;
18615b799d4fSCorentin Chary 	int ret;
18625b799d4fSCorentin Chary 
18635b799d4fSCorentin Chary 	/*
18645b799d4fSCorentin Chary 	 * This handler is enabled only if hotplug is enabled.
1865e12e6d94SCorentin Chary 	 * In this case, the asus_wmi_set_devstate() will
18665b799d4fSCorentin Chary 	 * trigger a wmi notification and we need to wait
18675b799d4fSCorentin Chary 	 * this call to finish before being able to call
18685b799d4fSCorentin Chary 	 * any wmi method
18695b799d4fSCorentin Chary 	 */
1870e12e6d94SCorentin Chary 	mutex_lock(&asus->wmi_lock);
1871a7ce3f04SCorentin Chary 	ret = asus_rfkill_set(data, blocked);
1872e12e6d94SCorentin Chary 	mutex_unlock(&asus->wmi_lock);
18735b799d4fSCorentin Chary 	return ret;
18745b799d4fSCorentin Chary }
18755b799d4fSCorentin Chary 
1876e12e6d94SCorentin Chary static const struct rfkill_ops asus_rfkill_wlan_ops = {
1877e12e6d94SCorentin Chary 	.set_block = asus_rfkill_wlan_set,
1878a7ce3f04SCorentin Chary 	.query = asus_rfkill_query,
18795b799d4fSCorentin Chary };
18805b799d4fSCorentin Chary 
1881e12e6d94SCorentin Chary static const struct rfkill_ops asus_rfkill_ops = {
1882e12e6d94SCorentin Chary 	.set_block = asus_rfkill_set,
1883e12e6d94SCorentin Chary 	.query = asus_rfkill_query,
18845b799d4fSCorentin Chary };
18855b799d4fSCorentin Chary 
asus_new_rfkill(struct asus_wmi * asus,struct asus_rfkill * arfkill,const char * name,enum rfkill_type type,int dev_id)1886e12e6d94SCorentin Chary static int asus_new_rfkill(struct asus_wmi *asus,
1887a7ce3f04SCorentin Chary 			   struct asus_rfkill *arfkill,
1888e12e6d94SCorentin Chary 			   const char *name, enum rfkill_type type, int dev_id)
18895b799d4fSCorentin Chary {
18901d070f89SCorentin Chary 	int result = asus_wmi_get_devstate_simple(asus, dev_id);
1891a7ce3f04SCorentin Chary 	struct rfkill **rfkill = &arfkill->rfkill;
18925b799d4fSCorentin Chary 
18935b799d4fSCorentin Chary 	if (result < 0)
18945b799d4fSCorentin Chary 		return result;
18955b799d4fSCorentin Chary 
1896a7ce3f04SCorentin Chary 	arfkill->dev_id = dev_id;
1897a7ce3f04SCorentin Chary 	arfkill->asus = asus;
1898a7ce3f04SCorentin Chary 
1899c87992d1SAceLan Kao 	if (dev_id == ASUS_WMI_DEVID_WLAN &&
1900c87992d1SAceLan Kao 	    asus->driver->quirks->hotplug_wireless)
1901e12e6d94SCorentin Chary 		*rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,
1902a7ce3f04SCorentin Chary 				       &asus_rfkill_wlan_ops, arfkill);
19035b799d4fSCorentin Chary 	else
1904e12e6d94SCorentin Chary 		*rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,
1905a7ce3f04SCorentin Chary 				       &asus_rfkill_ops, arfkill);
19065b799d4fSCorentin Chary 
19075b799d4fSCorentin Chary 	if (!*rfkill)
19085b799d4fSCorentin Chary 		return -EINVAL;
19095b799d4fSCorentin Chary 
1910e8f56c80SAceLan Kao 	if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
1911f515623cSAceLan Kao 			(asus->driver->quirks->wapf > 0))
19126cae06e6SAceLan Kao 		rfkill_set_led_trigger_name(*rfkill, "asus-wlan");
19136cae06e6SAceLan Kao 
19145b799d4fSCorentin Chary 	rfkill_init_sw_state(*rfkill, !result);
19155b799d4fSCorentin Chary 	result = rfkill_register(*rfkill);
19165b799d4fSCorentin Chary 	if (result) {
19175b799d4fSCorentin Chary 		rfkill_destroy(*rfkill);
19185b799d4fSCorentin Chary 		*rfkill = NULL;
19195b799d4fSCorentin Chary 		return result;
19205b799d4fSCorentin Chary 	}
19215b799d4fSCorentin Chary 	return 0;
19225b799d4fSCorentin Chary }
19235b799d4fSCorentin Chary 
asus_wmi_rfkill_exit(struct asus_wmi * asus)1924e12e6d94SCorentin Chary static void asus_wmi_rfkill_exit(struct asus_wmi *asus)
19255b799d4fSCorentin Chary {
1926cf48bf9eSJoão Paulo Rechi Vita 	if (asus->driver->wlan_ctrl_by_user && ashs_present())
1927cf48bf9eSJoão Paulo Rechi Vita 		return;
1928cf48bf9eSJoão Paulo Rechi Vita 
1929e12e6d94SCorentin Chary 	asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P5");
1930e12e6d94SCorentin Chary 	asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P6");
1931e12e6d94SCorentin Chary 	asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P7");
1932a7ce3f04SCorentin Chary 	if (asus->wlan.rfkill) {
1933a7ce3f04SCorentin Chary 		rfkill_unregister(asus->wlan.rfkill);
1934a7ce3f04SCorentin Chary 		rfkill_destroy(asus->wlan.rfkill);
1935a7ce3f04SCorentin Chary 		asus->wlan.rfkill = NULL;
19365b799d4fSCorentin Chary 	}
19375b799d4fSCorentin Chary 	/*
19385b799d4fSCorentin Chary 	 * Refresh pci hotplug in case the rfkill state was changed after
1939e12e6d94SCorentin Chary 	 * asus_unregister_rfkill_notifier()
19405b799d4fSCorentin Chary 	 */
1941e12e6d94SCorentin Chary 	asus_rfkill_hotplug(asus);
1942125450f8SLukas Wunner 	if (asus->hotplug_slot.ops)
1943125450f8SLukas Wunner 		pci_hp_deregister(&asus->hotplug_slot);
1944e12e6d94SCorentin Chary 	if (asus->hotplug_workqueue)
1945e12e6d94SCorentin Chary 		destroy_workqueue(asus->hotplug_workqueue);
19465b799d4fSCorentin Chary 
1947a7ce3f04SCorentin Chary 	if (asus->bluetooth.rfkill) {
1948a7ce3f04SCorentin Chary 		rfkill_unregister(asus->bluetooth.rfkill);
1949a7ce3f04SCorentin Chary 		rfkill_destroy(asus->bluetooth.rfkill);
1950a7ce3f04SCorentin Chary 		asus->bluetooth.rfkill = NULL;
19515b799d4fSCorentin Chary 	}
1952a7ce3f04SCorentin Chary 	if (asus->wimax.rfkill) {
1953a7ce3f04SCorentin Chary 		rfkill_unregister(asus->wimax.rfkill);
1954a7ce3f04SCorentin Chary 		rfkill_destroy(asus->wimax.rfkill);
1955a7ce3f04SCorentin Chary 		asus->wimax.rfkill = NULL;
19565b799d4fSCorentin Chary 	}
1957a7ce3f04SCorentin Chary 	if (asus->wwan3g.rfkill) {
1958a7ce3f04SCorentin Chary 		rfkill_unregister(asus->wwan3g.rfkill);
1959a7ce3f04SCorentin Chary 		rfkill_destroy(asus->wwan3g.rfkill);
1960a7ce3f04SCorentin Chary 		asus->wwan3g.rfkill = NULL;
19615b799d4fSCorentin Chary 	}
196243be8bdeSCorentin Chary 	if (asus->gps.rfkill) {
196343be8bdeSCorentin Chary 		rfkill_unregister(asus->gps.rfkill);
196443be8bdeSCorentin Chary 		rfkill_destroy(asus->gps.rfkill);
196543be8bdeSCorentin Chary 		asus->gps.rfkill = NULL;
196643be8bdeSCorentin Chary 	}
1967a912d329SCorentin Chary 	if (asus->uwb.rfkill) {
1968a912d329SCorentin Chary 		rfkill_unregister(asus->uwb.rfkill);
1969a912d329SCorentin Chary 		rfkill_destroy(asus->uwb.rfkill);
1970a912d329SCorentin Chary 		asus->uwb.rfkill = NULL;
1971a912d329SCorentin Chary 	}
19725b799d4fSCorentin Chary }
19735b799d4fSCorentin Chary 
asus_wmi_rfkill_init(struct asus_wmi * asus)1974e12e6d94SCorentin Chary static int asus_wmi_rfkill_init(struct asus_wmi *asus)
19755b799d4fSCorentin Chary {
19765b799d4fSCorentin Chary 	int result = 0;
19775b799d4fSCorentin Chary 
1978e12e6d94SCorentin Chary 	mutex_init(&asus->hotplug_lock);
1979e12e6d94SCorentin Chary 	mutex_init(&asus->wmi_lock);
19805b799d4fSCorentin Chary 
1981a7ce3f04SCorentin Chary 	result = asus_new_rfkill(asus, &asus->wlan, "asus-wlan",
1982a7ce3f04SCorentin Chary 				 RFKILL_TYPE_WLAN, ASUS_WMI_DEVID_WLAN);
19835b799d4fSCorentin Chary 
19845b799d4fSCorentin Chary 	if (result && result != -ENODEV)
19855b799d4fSCorentin Chary 		goto exit;
19865b799d4fSCorentin Chary 
1987a7ce3f04SCorentin Chary 	result = asus_new_rfkill(asus, &asus->bluetooth,
1988e12e6d94SCorentin Chary 				 "asus-bluetooth", RFKILL_TYPE_BLUETOOTH,
1989e12e6d94SCorentin Chary 				 ASUS_WMI_DEVID_BLUETOOTH);
19905b799d4fSCorentin Chary 
19915b799d4fSCorentin Chary 	if (result && result != -ENODEV)
19925b799d4fSCorentin Chary 		goto exit;
19935b799d4fSCorentin Chary 
1994a7ce3f04SCorentin Chary 	result = asus_new_rfkill(asus, &asus->wimax, "asus-wimax",
1995a7ce3f04SCorentin Chary 				 RFKILL_TYPE_WIMAX, ASUS_WMI_DEVID_WIMAX);
19965b799d4fSCorentin Chary 
19975b799d4fSCorentin Chary 	if (result && result != -ENODEV)
19985b799d4fSCorentin Chary 		goto exit;
19995b799d4fSCorentin Chary 
2000a7ce3f04SCorentin Chary 	result = asus_new_rfkill(asus, &asus->wwan3g, "asus-wwan3g",
2001a7ce3f04SCorentin Chary 				 RFKILL_TYPE_WWAN, ASUS_WMI_DEVID_WWAN3G);
20025b799d4fSCorentin Chary 
20035b799d4fSCorentin Chary 	if (result && result != -ENODEV)
20045b799d4fSCorentin Chary 		goto exit;
20055b799d4fSCorentin Chary 
200643be8bdeSCorentin Chary 	result = asus_new_rfkill(asus, &asus->gps, "asus-gps",
200743be8bdeSCorentin Chary 				 RFKILL_TYPE_GPS, ASUS_WMI_DEVID_GPS);
200843be8bdeSCorentin Chary 
200943be8bdeSCorentin Chary 	if (result && result != -ENODEV)
201043be8bdeSCorentin Chary 		goto exit;
201143be8bdeSCorentin Chary 
2012a912d329SCorentin Chary 	result = asus_new_rfkill(asus, &asus->uwb, "asus-uwb",
2013a912d329SCorentin Chary 				 RFKILL_TYPE_UWB, ASUS_WMI_DEVID_UWB);
2014a912d329SCorentin Chary 
2015a912d329SCorentin Chary 	if (result && result != -ENODEV)
2016a912d329SCorentin Chary 		goto exit;
2017a912d329SCorentin Chary 
2018c87992d1SAceLan Kao 	if (!asus->driver->quirks->hotplug_wireless)
20195b799d4fSCorentin Chary 		goto exit;
20205b799d4fSCorentin Chary 
2021e12e6d94SCorentin Chary 	result = asus_setup_pci_hotplug(asus);
20225b799d4fSCorentin Chary 	/*
20235b799d4fSCorentin Chary 	 * If we get -EBUSY then something else is handling the PCI hotplug -
20245b799d4fSCorentin Chary 	 * don't fail in this case
20255b799d4fSCorentin Chary 	 */
20265b799d4fSCorentin Chary 	if (result == -EBUSY)
20275b799d4fSCorentin Chary 		result = 0;
20285b799d4fSCorentin Chary 
2029e12e6d94SCorentin Chary 	asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P5");
2030e12e6d94SCorentin Chary 	asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P6");
2031e12e6d94SCorentin Chary 	asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P7");
20325b799d4fSCorentin Chary 	/*
20335b799d4fSCorentin Chary 	 * Refresh pci hotplug in case the rfkill state was changed during
20345b799d4fSCorentin Chary 	 * setup.
20355b799d4fSCorentin Chary 	 */
2036e12e6d94SCorentin Chary 	asus_rfkill_hotplug(asus);
20375b799d4fSCorentin Chary 
20385b799d4fSCorentin Chary exit:
20395b799d4fSCorentin Chary 	if (result && result != -ENODEV)
2040e12e6d94SCorentin Chary 		asus_wmi_rfkill_exit(asus);
20415b799d4fSCorentin Chary 
20425b799d4fSCorentin Chary 	if (result == -ENODEV)
20435b799d4fSCorentin Chary 		result = 0;
20445b799d4fSCorentin Chary 
20455b799d4fSCorentin Chary 	return result;
20465b799d4fSCorentin Chary }
20475b799d4fSCorentin Chary 
2048ca91ea34SLuke D. Jones /* Panel Overdrive ************************************************************/
panel_od_show(struct device * dev,struct device_attribute * attr,char * buf)2049ca91ea34SLuke D. Jones static ssize_t panel_od_show(struct device *dev,
2050ca91ea34SLuke D. Jones 				   struct device_attribute *attr, char *buf)
2051ca91ea34SLuke D. Jones {
2052ca91ea34SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
2053ebc443adSLuke D. Jones 	int result;
2054ca91ea34SLuke D. Jones 
2055ebc443adSLuke D. Jones 	result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_PANEL_OD);
2056ebc443adSLuke D. Jones 	if (result < 0)
2057ebc443adSLuke D. Jones 		return result;
2058ebc443adSLuke D. Jones 
2059ebc443adSLuke D. Jones 	return sysfs_emit(buf, "%d\n", result);
2060ca91ea34SLuke D. Jones }
2061ca91ea34SLuke D. Jones 
panel_od_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)2062ca91ea34SLuke D. Jones static ssize_t panel_od_store(struct device *dev,
2063ca91ea34SLuke D. Jones 				    struct device_attribute *attr,
2064ca91ea34SLuke D. Jones 				    const char *buf, size_t count)
2065ca91ea34SLuke D. Jones {
2066ebc443adSLuke D. Jones 	int result, err;
2067ebc443adSLuke D. Jones 	u32 overdrive;
2068ca91ea34SLuke D. Jones 
2069ca91ea34SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
2070ca91ea34SLuke D. Jones 
2071ebc443adSLuke D. Jones 	result = kstrtou32(buf, 10, &overdrive);
2072ca91ea34SLuke D. Jones 	if (result)
2073ca91ea34SLuke D. Jones 		return result;
2074ca91ea34SLuke D. Jones 
2075ebc443adSLuke D. Jones 	if (overdrive > 1)
2076ebc443adSLuke D. Jones 		return -EINVAL;
2077ca91ea34SLuke D. Jones 
2078ebc443adSLuke D. Jones 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PANEL_OD, overdrive, &result);
2079ebc443adSLuke D. Jones 
2080ebc443adSLuke D. Jones 	if (err) {
2081ebc443adSLuke D. Jones 		pr_warn("Failed to set panel overdrive: %d\n", err);
2082ebc443adSLuke D. Jones 		return err;
2083ebc443adSLuke D. Jones 	}
2084ebc443adSLuke D. Jones 
2085ebc443adSLuke D. Jones 	if (result > 1) {
2086ebc443adSLuke D. Jones 		pr_warn("Failed to set panel overdrive (result): 0x%x\n", result);
2087ebc443adSLuke D. Jones 		return -EIO;
2088ebc443adSLuke D. Jones 	}
2089ebc443adSLuke D. Jones 
2090ebc443adSLuke D. Jones 	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "panel_od");
2091ca91ea34SLuke D. Jones 
2092ca91ea34SLuke D. Jones 	return count;
2093ca91ea34SLuke D. Jones }
2094ca91ea34SLuke D. Jones static DEVICE_ATTR_RW(panel_od);
2095ca91ea34SLuke D. Jones 
2096abac4259SLuke D. Jones /* Mini-LED mode **************************************************************/
mini_led_mode_show(struct device * dev,struct device_attribute * attr,char * buf)2097abac4259SLuke D. Jones static ssize_t mini_led_mode_show(struct device *dev,
2098abac4259SLuke D. Jones 				   struct device_attribute *attr, char *buf)
2099abac4259SLuke D. Jones {
2100abac4259SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
2101abac4259SLuke D. Jones 	int result;
2102abac4259SLuke D. Jones 
2103abac4259SLuke D. Jones 	result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_MINI_LED_MODE);
2104abac4259SLuke D. Jones 	if (result < 0)
2105abac4259SLuke D. Jones 		return result;
2106abac4259SLuke D. Jones 
2107abac4259SLuke D. Jones 	return sysfs_emit(buf, "%d\n", result);
2108abac4259SLuke D. Jones }
2109abac4259SLuke D. Jones 
mini_led_mode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)2110abac4259SLuke D. Jones static ssize_t mini_led_mode_store(struct device *dev,
2111abac4259SLuke D. Jones 				    struct device_attribute *attr,
2112abac4259SLuke D. Jones 				    const char *buf, size_t count)
2113abac4259SLuke D. Jones {
2114abac4259SLuke D. Jones 	int result, err;
2115abac4259SLuke D. Jones 	u32 mode;
2116abac4259SLuke D. Jones 
2117abac4259SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
2118abac4259SLuke D. Jones 
2119abac4259SLuke D. Jones 	result = kstrtou32(buf, 10, &mode);
2120abac4259SLuke D. Jones 	if (result)
2121abac4259SLuke D. Jones 		return result;
2122abac4259SLuke D. Jones 
2123abac4259SLuke D. Jones 	if (mode > 1)
2124abac4259SLuke D. Jones 		return -EINVAL;
2125abac4259SLuke D. Jones 
2126abac4259SLuke D. Jones 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MINI_LED_MODE, mode, &result);
2127abac4259SLuke D. Jones 
2128abac4259SLuke D. Jones 	if (err) {
2129abac4259SLuke D. Jones 		pr_warn("Failed to set mini-LED: %d\n", err);
2130abac4259SLuke D. Jones 		return err;
2131abac4259SLuke D. Jones 	}
2132abac4259SLuke D. Jones 
2133abac4259SLuke D. Jones 	if (result > 1) {
2134abac4259SLuke D. Jones 		pr_warn("Failed to set mini-LED mode (result): 0x%x\n", result);
2135abac4259SLuke D. Jones 		return -EIO;
2136abac4259SLuke D. Jones 	}
2137abac4259SLuke D. Jones 
2138abac4259SLuke D. Jones 	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "mini_led_mode");
2139abac4259SLuke D. Jones 
2140abac4259SLuke D. Jones 	return count;
2141abac4259SLuke D. Jones }
2142abac4259SLuke D. Jones static DEVICE_ATTR_RW(mini_led_mode);
2143abac4259SLuke D. Jones 
214454a3121fSYurii Pavlovskyi /* Quirks *********************************************************************/
214554a3121fSYurii Pavlovskyi 
asus_wmi_set_xusb2pr(struct asus_wmi * asus)21468023eff1SKai-Chuan Hsieh static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
21478023eff1SKai-Chuan Hsieh {
21488023eff1SKai-Chuan Hsieh 	struct pci_dev *xhci_pdev;
21498023eff1SKai-Chuan Hsieh 	u32 orig_ports_available;
21508023eff1SKai-Chuan Hsieh 	u32 ports_available = asus->driver->quirks->xusb2pr;
21518023eff1SKai-Chuan Hsieh 
21528023eff1SKai-Chuan Hsieh 	xhci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
21538023eff1SKai-Chuan Hsieh 			PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI,
21548023eff1SKai-Chuan Hsieh 			NULL);
21558023eff1SKai-Chuan Hsieh 
21568023eff1SKai-Chuan Hsieh 	if (!xhci_pdev)
21578023eff1SKai-Chuan Hsieh 		return;
21588023eff1SKai-Chuan Hsieh 
21598023eff1SKai-Chuan Hsieh 	pci_read_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
21608023eff1SKai-Chuan Hsieh 				&orig_ports_available);
21618023eff1SKai-Chuan Hsieh 
21628023eff1SKai-Chuan Hsieh 	pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
21638023eff1SKai-Chuan Hsieh 				cpu_to_le32(ports_available));
21648023eff1SKai-Chuan Hsieh 
2165d0cdd850SXiongfeng Wang 	pci_dev_put(xhci_pdev);
2166d0cdd850SXiongfeng Wang 
21678023eff1SKai-Chuan Hsieh 	pr_info("set USB_INTEL_XUSB2PR old: 0x%04x, new: 0x%04x\n",
21688023eff1SKai-Chuan Hsieh 			orig_ports_available, ports_available);
21698023eff1SKai-Chuan Hsieh }
21708023eff1SKai-Chuan Hsieh 
21715b799d4fSCorentin Chary /*
2172e9b61518SOleksij Rempel  * Some devices dont support or have borcken get_als method
2173e9b61518SOleksij Rempel  * but still support set method.
2174e9b61518SOleksij Rempel  */
asus_wmi_set_als(void)2175e9b61518SOleksij Rempel static void asus_wmi_set_als(void)
2176e9b61518SOleksij Rempel {
2177e9b61518SOleksij Rempel 	asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);
2178e9b61518SOleksij Rempel }
2179e9b61518SOleksij Rempel 
218054a3121fSYurii Pavlovskyi /* Hwmon device ***************************************************************/
218154a3121fSYurii Pavlovskyi 
asus_agfn_fan_speed_read(struct asus_wmi * asus,int fan,int * speed)21822889ffcfSDaniel Drake static int asus_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
218353e755c2SKast Bernd 					  int *speed)
218453e755c2SKast Bernd {
21852889ffcfSDaniel Drake 	struct agfn_fan_args args = {
218653e755c2SKast Bernd 		.agfn.len = sizeof(args),
218753e755c2SKast Bernd 		.agfn.mfun = ASUS_FAN_MFUN,
218853e755c2SKast Bernd 		.agfn.sfun = ASUS_FAN_SFUN_READ,
218953e755c2SKast Bernd 		.fan = fan,
219053e755c2SKast Bernd 		.speed = 0,
219153e755c2SKast Bernd 	};
219253e755c2SKast Bernd 	struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
219353e755c2SKast Bernd 	int status;
219453e755c2SKast Bernd 
219553e755c2SKast Bernd 	if (fan != 1)
219653e755c2SKast Bernd 		return -EINVAL;
219753e755c2SKast Bernd 
219853e755c2SKast Bernd 	status = asus_wmi_evaluate_method_agfn(input);
219953e755c2SKast Bernd 
220053e755c2SKast Bernd 	if (status || args.agfn.err)
220153e755c2SKast Bernd 		return -ENXIO;
220253e755c2SKast Bernd 
220353e755c2SKast Bernd 	if (speed)
220453e755c2SKast Bernd 		*speed = args.speed;
220553e755c2SKast Bernd 
220653e755c2SKast Bernd 	return 0;
220753e755c2SKast Bernd }
220853e755c2SKast Bernd 
asus_agfn_fan_speed_write(struct asus_wmi * asus,int fan,int * speed)22092889ffcfSDaniel Drake static int asus_agfn_fan_speed_write(struct asus_wmi *asus, int fan,
221053e755c2SKast Bernd 				     int *speed)
221153e755c2SKast Bernd {
22122889ffcfSDaniel Drake 	struct agfn_fan_args args = {
221353e755c2SKast Bernd 		.agfn.len = sizeof(args),
221453e755c2SKast Bernd 		.agfn.mfun = ASUS_FAN_MFUN,
221553e755c2SKast Bernd 		.agfn.sfun = ASUS_FAN_SFUN_WRITE,
221653e755c2SKast Bernd 		.fan = fan,
221753e755c2SKast Bernd 		.speed = speed ?  *speed : 0,
221853e755c2SKast Bernd 	};
221953e755c2SKast Bernd 	struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
222053e755c2SKast Bernd 	int status;
222153e755c2SKast Bernd 
222253e755c2SKast Bernd 	/* 1: for setting 1st fan's speed 0: setting auto mode */
222353e755c2SKast Bernd 	if (fan != 1 && fan != 0)
222453e755c2SKast Bernd 		return -EINVAL;
222553e755c2SKast Bernd 
222653e755c2SKast Bernd 	status = asus_wmi_evaluate_method_agfn(input);
222753e755c2SKast Bernd 
222853e755c2SKast Bernd 	if (status || args.agfn.err)
222953e755c2SKast Bernd 		return -ENXIO;
223053e755c2SKast Bernd 
223153e755c2SKast Bernd 	if (speed && fan == 1)
22322889ffcfSDaniel Drake 		asus->agfn_pwm = *speed;
223353e755c2SKast Bernd 
223453e755c2SKast Bernd 	return 0;
223553e755c2SKast Bernd }
223653e755c2SKast Bernd 
223753e755c2SKast Bernd /*
223853e755c2SKast Bernd  * Check if we can read the speed of one fan. If true we assume we can also
223953e755c2SKast Bernd  * control it.
224053e755c2SKast Bernd  */
asus_wmi_has_agfn_fan(struct asus_wmi * asus)22412889ffcfSDaniel Drake static bool asus_wmi_has_agfn_fan(struct asus_wmi *asus)
224253e755c2SKast Bernd {
224353e755c2SKast Bernd 	int status;
22442889ffcfSDaniel Drake 	int speed;
22452889ffcfSDaniel Drake 	u32 value;
224653e755c2SKast Bernd 
22472889ffcfSDaniel Drake 	status = asus_agfn_fan_speed_read(asus, 1, &speed);
22482889ffcfSDaniel Drake 	if (status != 0)
22492889ffcfSDaniel Drake 		return false;
225053e755c2SKast Bernd 
22512889ffcfSDaniel Drake 	status = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value);
22522889ffcfSDaniel Drake 	if (status != 0)
22532889ffcfSDaniel Drake 		return false;
225453e755c2SKast Bernd 
22552889ffcfSDaniel Drake 	/*
22562889ffcfSDaniel Drake 	 * We need to find a better way, probably using sfun,
22572889ffcfSDaniel Drake 	 * bits or spec ...
22582889ffcfSDaniel Drake 	 * Currently we disable it if:
22592889ffcfSDaniel Drake 	 * - ASUS_WMI_UNSUPPORTED_METHOD is returned
22602889ffcfSDaniel Drake 	 * - reverved bits are non-zero
22612889ffcfSDaniel Drake 	 * - sfun and presence bit are not set
22622889ffcfSDaniel Drake 	 */
22632889ffcfSDaniel Drake 	return !(value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000
22642889ffcfSDaniel Drake 		 || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT)));
226553e755c2SKast Bernd }
226653e755c2SKast Bernd 
asus_fan_set_auto(struct asus_wmi * asus)22672889ffcfSDaniel Drake static int asus_fan_set_auto(struct asus_wmi *asus)
226853e755c2SKast Bernd {
226953e755c2SKast Bernd 	int status;
2270e3168b87SDaniel Drake 	u32 retval;
227153e755c2SKast Bernd 
2272e3168b87SDaniel Drake 	switch (asus->fan_type) {
2273e3168b87SDaniel Drake 	case FAN_TYPE_SPEC83:
2274e3168b87SDaniel Drake 		status = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL,
2275e3168b87SDaniel Drake 					       0, &retval);
2276e3168b87SDaniel Drake 		if (status)
2277e3168b87SDaniel Drake 			return status;
2278e3168b87SDaniel Drake 
2279e3168b87SDaniel Drake 		if (retval != 1)
2280e3168b87SDaniel Drake 			return -EIO;
2281e3168b87SDaniel Drake 		break;
2282e3168b87SDaniel Drake 
2283e3168b87SDaniel Drake 	case FAN_TYPE_AGFN:
22842889ffcfSDaniel Drake 		status = asus_agfn_fan_speed_write(asus, 0, NULL);
228553e755c2SKast Bernd 		if (status)
228653e755c2SKast Bernd 			return -ENXIO;
2287e3168b87SDaniel Drake 		break;
2288e3168b87SDaniel Drake 
2289e3168b87SDaniel Drake 	default:
2290e3168b87SDaniel Drake 		return -ENXIO;
2291e3168b87SDaniel Drake 	}
2292e3168b87SDaniel Drake 
229312ff4c80SLuke D. Jones 	/*
229412ff4c80SLuke D. Jones 	 * Modern models like the G713 also have GPU fan control (this is not AGFN)
229512ff4c80SLuke D. Jones 	 */
229612ff4c80SLuke D. Jones 	if (asus->gpu_fan_type == FAN_TYPE_SPEC83) {
229712ff4c80SLuke D. Jones 		status = asus_wmi_set_devstate(ASUS_WMI_DEVID_GPU_FAN_CTRL,
229812ff4c80SLuke D. Jones 					       0, &retval);
229912ff4c80SLuke D. Jones 		if (status)
230012ff4c80SLuke D. Jones 			return status;
230112ff4c80SLuke D. Jones 
230212ff4c80SLuke D. Jones 		if (retval != 1)
230312ff4c80SLuke D. Jones 			return -EIO;
230412ff4c80SLuke D. Jones 	}
230553e755c2SKast Bernd 
230653e755c2SKast Bernd 	return 0;
230753e755c2SKast Bernd }
230853e755c2SKast Bernd 
pwm1_show(struct device * dev,struct device_attribute * attr,char * buf)230953e755c2SKast Bernd static ssize_t pwm1_show(struct device *dev,
2310e07babdeSCorentin Chary 			       struct device_attribute *attr,
2311e07babdeSCorentin Chary 			       char *buf)
2312e07babdeSCorentin Chary {
2313e07babdeSCorentin Chary 	struct asus_wmi *asus = dev_get_drvdata(dev);
23142889ffcfSDaniel Drake 	int err;
231553e755c2SKast Bernd 	int value;
2316e07babdeSCorentin Chary 
23172889ffcfSDaniel Drake 	/* If we already set a value then just return it */
23182889ffcfSDaniel Drake 	if (asus->agfn_pwm >= 0)
23192889ffcfSDaniel Drake 		return sprintf(buf, "%d\n", asus->agfn_pwm);
23202889ffcfSDaniel Drake 
23212889ffcfSDaniel Drake 	/*
23222889ffcfSDaniel Drake 	 * If we haven't set already set a value through the AGFN interface,
23232889ffcfSDaniel Drake 	 * we read a current value through the (now-deprecated) FAN_CTRL device.
23242889ffcfSDaniel Drake 	 */
23252889ffcfSDaniel Drake 	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value);
23262889ffcfSDaniel Drake 	if (err < 0)
23272889ffcfSDaniel Drake 		return err;
23282889ffcfSDaniel Drake 
23292889ffcfSDaniel Drake 	value &= 0xFF;
23302889ffcfSDaniel Drake 
23312889ffcfSDaniel Drake 	if (value == 1) /* Low Speed */
23322889ffcfSDaniel Drake 		value = 85;
23332889ffcfSDaniel Drake 	else if (value == 2)
23342889ffcfSDaniel Drake 		value = 170;
23352889ffcfSDaniel Drake 	else if (value == 3)
23362889ffcfSDaniel Drake 		value = 255;
23372889ffcfSDaniel Drake 	else if (value) {
23382889ffcfSDaniel Drake 		pr_err("Unknown fan speed %#x\n", value);
23392889ffcfSDaniel Drake 		value = -1;
23402889ffcfSDaniel Drake 	}
2341e07babdeSCorentin Chary 
2342170f0da2SLuke D. Jones 	return sysfs_emit(buf, "%d\n", value);
2343e07babdeSCorentin Chary }
2344e07babdeSCorentin Chary 
pwm1_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)234553e755c2SKast Bernd static ssize_t pwm1_store(struct device *dev,
234653e755c2SKast Bernd 				     struct device_attribute *attr,
234753e755c2SKast Bernd 				     const char *buf, size_t count) {
234853e755c2SKast Bernd 	struct asus_wmi *asus = dev_get_drvdata(dev);
234953e755c2SKast Bernd 	int value;
235053e755c2SKast Bernd 	int state;
235153e755c2SKast Bernd 	int ret;
235253e755c2SKast Bernd 
235353e755c2SKast Bernd 	ret = kstrtouint(buf, 10, &value);
235453e755c2SKast Bernd 	if (ret)
235553e755c2SKast Bernd 		return ret;
235653e755c2SKast Bernd 
235753e755c2SKast Bernd 	value = clamp(value, 0, 255);
235853e755c2SKast Bernd 
23592889ffcfSDaniel Drake 	state = asus_agfn_fan_speed_write(asus, 1, &value);
236053e755c2SKast Bernd 	if (state)
236153e755c2SKast Bernd 		pr_warn("Setting fan speed failed: %d\n", state);
236253e755c2SKast Bernd 	else
23632889ffcfSDaniel Drake 		asus->fan_pwm_mode = ASUS_FAN_CTRL_MANUAL;
236453e755c2SKast Bernd 
236553e755c2SKast Bernd 	return count;
236653e755c2SKast Bernd }
236753e755c2SKast Bernd 
fan1_input_show(struct device * dev,struct device_attribute * attr,char * buf)236853e755c2SKast Bernd static ssize_t fan1_input_show(struct device *dev,
236953e755c2SKast Bernd 					struct device_attribute *attr,
237053e755c2SKast Bernd 					char *buf)
237153e755c2SKast Bernd {
23722889ffcfSDaniel Drake 	struct asus_wmi *asus = dev_get_drvdata(dev);
23732889ffcfSDaniel Drake 	int value;
23742889ffcfSDaniel Drake 	int ret;
23752889ffcfSDaniel Drake 
2376e3168b87SDaniel Drake 	switch (asus->fan_type) {
2377e3168b87SDaniel Drake 	case FAN_TYPE_SPEC83:
2378e3168b87SDaniel Drake 		ret = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL,
2379e3168b87SDaniel Drake 					    &value);
2380e3168b87SDaniel Drake 		if (ret < 0)
2381e3168b87SDaniel Drake 			return ret;
2382e3168b87SDaniel Drake 
2383e3168b87SDaniel Drake 		value &= 0xffff;
2384e3168b87SDaniel Drake 		break;
2385e3168b87SDaniel Drake 
2386e3168b87SDaniel Drake 	case FAN_TYPE_AGFN:
23872889ffcfSDaniel Drake 		/* no speed readable on manual mode */
23882889ffcfSDaniel Drake 		if (asus->fan_pwm_mode == ASUS_FAN_CTRL_MANUAL)
23892889ffcfSDaniel Drake 			return -ENXIO;
23902889ffcfSDaniel Drake 
23912889ffcfSDaniel Drake 		ret = asus_agfn_fan_speed_read(asus, 1, &value);
23922889ffcfSDaniel Drake 		if (ret) {
23932889ffcfSDaniel Drake 			pr_warn("reading fan speed failed: %d\n", ret);
23942889ffcfSDaniel Drake 			return -ENXIO;
23952889ffcfSDaniel Drake 		}
2396e3168b87SDaniel Drake 		break;
2397e3168b87SDaniel Drake 
2398e3168b87SDaniel Drake 	default:
2399e3168b87SDaniel Drake 		return -ENXIO;
2400e3168b87SDaniel Drake 	}
240153e755c2SKast Bernd 
2402170f0da2SLuke D. Jones 	return sysfs_emit(buf, "%d\n", value < 0 ? -1 : value * 100);
240353e755c2SKast Bernd }
240453e755c2SKast Bernd 
pwm1_enable_show(struct device * dev,struct device_attribute * attr,char * buf)240553e755c2SKast Bernd static ssize_t pwm1_enable_show(struct device *dev,
240653e755c2SKast Bernd 						 struct device_attribute *attr,
240753e755c2SKast Bernd 						 char *buf)
240853e755c2SKast Bernd {
240953e755c2SKast Bernd 	struct asus_wmi *asus = dev_get_drvdata(dev);
241053e755c2SKast Bernd 
2411e3168b87SDaniel Drake 	/*
2412e3168b87SDaniel Drake 	 * Just read back the cached pwm mode.
2413e3168b87SDaniel Drake 	 *
2414e3168b87SDaniel Drake 	 * For the CPU_FAN device, the spec indicates that we should be
2415e3168b87SDaniel Drake 	 * able to read the device status and consult bit 19 to see if we
2416e3168b87SDaniel Drake 	 * are in Full On or Automatic mode. However, this does not work
2417e3168b87SDaniel Drake 	 * in practice on X532FL at least (the bit is always 0) and there's
2418e3168b87SDaniel Drake 	 * also nothing in the DSDT to indicate that this behaviour exists.
2419e3168b87SDaniel Drake 	 */
2420170f0da2SLuke D. Jones 	return sysfs_emit(buf, "%d\n", asus->fan_pwm_mode);
242153e755c2SKast Bernd }
242253e755c2SKast Bernd 
pwm1_enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)242353e755c2SKast Bernd static ssize_t pwm1_enable_store(struct device *dev,
242453e755c2SKast Bernd 						  struct device_attribute *attr,
242553e755c2SKast Bernd 						  const char *buf, size_t count)
242653e755c2SKast Bernd {
242753e755c2SKast Bernd 	struct asus_wmi *asus = dev_get_drvdata(dev);
242853e755c2SKast Bernd 	int status = 0;
242953e755c2SKast Bernd 	int state;
2430e3168b87SDaniel Drake 	int value;
243153e755c2SKast Bernd 	int ret;
2432e3168b87SDaniel Drake 	u32 retval;
243353e755c2SKast Bernd 
243453e755c2SKast Bernd 	ret = kstrtouint(buf, 10, &state);
243553e755c2SKast Bernd 	if (ret)
243653e755c2SKast Bernd 		return ret;
243753e755c2SKast Bernd 
2438e3168b87SDaniel Drake 	if (asus->fan_type == FAN_TYPE_SPEC83) {
2439e3168b87SDaniel Drake 		switch (state) { /* standard documented hwmon values */
2440e3168b87SDaniel Drake 		case ASUS_FAN_CTRL_FULLSPEED:
2441e3168b87SDaniel Drake 			value = 1;
2442e3168b87SDaniel Drake 			break;
2443e3168b87SDaniel Drake 		case ASUS_FAN_CTRL_AUTO:
2444e3168b87SDaniel Drake 			value = 0;
2445e3168b87SDaniel Drake 			break;
2446e3168b87SDaniel Drake 		default:
2447e3168b87SDaniel Drake 			return -EINVAL;
2448e3168b87SDaniel Drake 		}
2449e3168b87SDaniel Drake 
2450e3168b87SDaniel Drake 		ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL,
2451e3168b87SDaniel Drake 					    value, &retval);
2452e3168b87SDaniel Drake 		if (ret)
2453e3168b87SDaniel Drake 			return ret;
2454e3168b87SDaniel Drake 
2455e3168b87SDaniel Drake 		if (retval != 1)
2456e3168b87SDaniel Drake 			return -EIO;
2457e3168b87SDaniel Drake 	} else if (asus->fan_type == FAN_TYPE_AGFN) {
24582889ffcfSDaniel Drake 		switch (state) {
24592889ffcfSDaniel Drake 		case ASUS_FAN_CTRL_MANUAL:
24602889ffcfSDaniel Drake 			break;
246153e755c2SKast Bernd 
24622889ffcfSDaniel Drake 		case ASUS_FAN_CTRL_AUTO:
24632889ffcfSDaniel Drake 			status = asus_fan_set_auto(asus);
246453e755c2SKast Bernd 			if (status)
246553e755c2SKast Bernd 				return status;
24662889ffcfSDaniel Drake 			break;
246753e755c2SKast Bernd 
24682889ffcfSDaniel Drake 		default:
24692889ffcfSDaniel Drake 			return -EINVAL;
24702889ffcfSDaniel Drake 		}
2471e3168b87SDaniel Drake 	}
24722889ffcfSDaniel Drake 
24732889ffcfSDaniel Drake 	asus->fan_pwm_mode = state;
24740f0ac158SLuke D. Jones 
24750f0ac158SLuke D. Jones 	/* Must set to disabled if mode is toggled */
24760f0ac158SLuke D. Jones 	if (asus->cpu_fan_curve_available)
24770f0ac158SLuke D. Jones 		asus->custom_fan_curves[FAN_CURVE_DEV_CPU].enabled = false;
24780f0ac158SLuke D. Jones 	if (asus->gpu_fan_curve_available)
24790f0ac158SLuke D. Jones 		asus->custom_fan_curves[FAN_CURVE_DEV_GPU].enabled = false;
2480ee887807SLuke D. Jones 	if (asus->mid_fan_curve_available)
2481ee887807SLuke D. Jones 		asus->custom_fan_curves[FAN_CURVE_DEV_MID].enabled = false;
24820f0ac158SLuke D. Jones 
248353e755c2SKast Bernd 	return count;
248453e755c2SKast Bernd }
248553e755c2SKast Bernd 
fan1_label_show(struct device * dev,struct device_attribute * attr,char * buf)248653e755c2SKast Bernd static ssize_t fan1_label_show(struct device *dev,
248753e755c2SKast Bernd 					  struct device_attribute *attr,
248853e755c2SKast Bernd 					  char *buf)
248953e755c2SKast Bernd {
2490170f0da2SLuke D. Jones 	return sysfs_emit(buf, "%s\n", ASUS_FAN_DESC);
249153e755c2SKast Bernd }
249253e755c2SKast Bernd 
asus_hwmon_temp1(struct device * dev,struct device_attribute * attr,char * buf)24936118b8adSCorentin Chary static ssize_t asus_hwmon_temp1(struct device *dev,
24946118b8adSCorentin Chary 				struct device_attribute *attr,
24956118b8adSCorentin Chary 				char *buf)
24966118b8adSCorentin Chary {
24976118b8adSCorentin Chary 	struct asus_wmi *asus = dev_get_drvdata(dev);
24986118b8adSCorentin Chary 	u32 value;
24996118b8adSCorentin Chary 	int err;
25006118b8adSCorentin Chary 
25016118b8adSCorentin Chary 	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, &value);
25026118b8adSCorentin Chary 	if (err < 0)
25036118b8adSCorentin Chary 		return err;
25046118b8adSCorentin Chary 
2505f07b9fdfSAkinobu Mita 	return sprintf(buf, "%ld\n",
2506f07b9fdfSAkinobu Mita 		       deci_kelvin_to_millicelsius(value & 0xFFFF));
25076118b8adSCorentin Chary }
25086118b8adSCorentin Chary 
250912ff4c80SLuke D. Jones /* GPU fan on modern ROG laptops */
fan2_input_show(struct device * dev,struct device_attribute * attr,char * buf)251091809918SLuke D. Jones static ssize_t fan2_input_show(struct device *dev,
251191809918SLuke D. Jones 					struct device_attribute *attr,
251291809918SLuke D. Jones 					char *buf)
251391809918SLuke D. Jones {
251491809918SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
251591809918SLuke D. Jones 	int value;
251691809918SLuke D. Jones 	int ret;
251791809918SLuke D. Jones 
251891809918SLuke D. Jones 	ret = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_GPU_FAN_CTRL, &value);
251991809918SLuke D. Jones 	if (ret < 0)
252091809918SLuke D. Jones 		return ret;
252191809918SLuke D. Jones 
252291809918SLuke D. Jones 	value &= 0xffff;
252391809918SLuke D. Jones 
252491809918SLuke D. Jones 	return sysfs_emit(buf, "%d\n", value * 100);
252591809918SLuke D. Jones }
252691809918SLuke D. Jones 
fan2_label_show(struct device * dev,struct device_attribute * attr,char * buf)252791809918SLuke D. Jones static ssize_t fan2_label_show(struct device *dev,
252891809918SLuke D. Jones 					  struct device_attribute *attr,
252991809918SLuke D. Jones 					  char *buf)
253091809918SLuke D. Jones {
253191809918SLuke D. Jones 	return sysfs_emit(buf, "%s\n", ASUS_GPU_FAN_DESC);
253291809918SLuke D. Jones }
253391809918SLuke D. Jones 
2534536fce82SLuke D. Jones /* Middle/Center fan on modern ROG laptops */
fan3_input_show(struct device * dev,struct device_attribute * attr,char * buf)2535536fce82SLuke D. Jones static ssize_t fan3_input_show(struct device *dev,
2536536fce82SLuke D. Jones 					struct device_attribute *attr,
2537536fce82SLuke D. Jones 					char *buf)
2538536fce82SLuke D. Jones {
2539536fce82SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
2540536fce82SLuke D. Jones 	int value;
2541536fce82SLuke D. Jones 	int ret;
2542536fce82SLuke D. Jones 
2543536fce82SLuke D. Jones 	ret = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_MID_FAN_CTRL, &value);
2544536fce82SLuke D. Jones 	if (ret < 0)
2545536fce82SLuke D. Jones 		return ret;
2546536fce82SLuke D. Jones 
2547536fce82SLuke D. Jones 	value &= 0xffff;
2548536fce82SLuke D. Jones 
2549536fce82SLuke D. Jones 	return sysfs_emit(buf, "%d\n", value * 100);
2550536fce82SLuke D. Jones }
2551536fce82SLuke D. Jones 
fan3_label_show(struct device * dev,struct device_attribute * attr,char * buf)2552536fce82SLuke D. Jones static ssize_t fan3_label_show(struct device *dev,
2553536fce82SLuke D. Jones 					  struct device_attribute *attr,
2554536fce82SLuke D. Jones 					  char *buf)
2555536fce82SLuke D. Jones {
2556536fce82SLuke D. Jones 	return sysfs_emit(buf, "%s\n", ASUS_MID_FAN_DESC);
2557536fce82SLuke D. Jones }
2558536fce82SLuke D. Jones 
pwm2_enable_show(struct device * dev,struct device_attribute * attr,char * buf)255912ff4c80SLuke D. Jones static ssize_t pwm2_enable_show(struct device *dev,
256012ff4c80SLuke D. Jones 				struct device_attribute *attr,
256112ff4c80SLuke D. Jones 				char *buf)
256212ff4c80SLuke D. Jones {
256312ff4c80SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
256412ff4c80SLuke D. Jones 
256512ff4c80SLuke D. Jones 	return sysfs_emit(buf, "%d\n", asus->gpu_fan_pwm_mode);
256612ff4c80SLuke D. Jones }
256712ff4c80SLuke D. Jones 
pwm2_enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)256812ff4c80SLuke D. Jones static ssize_t pwm2_enable_store(struct device *dev,
256912ff4c80SLuke D. Jones 				 struct device_attribute *attr,
257012ff4c80SLuke D. Jones 				 const char *buf, size_t count)
257112ff4c80SLuke D. Jones {
257212ff4c80SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
257312ff4c80SLuke D. Jones 	int state;
257412ff4c80SLuke D. Jones 	int value;
257512ff4c80SLuke D. Jones 	int ret;
257612ff4c80SLuke D. Jones 	u32 retval;
257712ff4c80SLuke D. Jones 
257812ff4c80SLuke D. Jones 	ret = kstrtouint(buf, 10, &state);
257912ff4c80SLuke D. Jones 	if (ret)
258012ff4c80SLuke D. Jones 		return ret;
258112ff4c80SLuke D. Jones 
258212ff4c80SLuke D. Jones 	switch (state) { /* standard documented hwmon values */
258312ff4c80SLuke D. Jones 	case ASUS_FAN_CTRL_FULLSPEED:
258412ff4c80SLuke D. Jones 		value = 1;
258512ff4c80SLuke D. Jones 		break;
258612ff4c80SLuke D. Jones 	case ASUS_FAN_CTRL_AUTO:
258712ff4c80SLuke D. Jones 		value = 0;
258812ff4c80SLuke D. Jones 		break;
258912ff4c80SLuke D. Jones 	default:
259012ff4c80SLuke D. Jones 		return -EINVAL;
259112ff4c80SLuke D. Jones 	}
259212ff4c80SLuke D. Jones 
259312ff4c80SLuke D. Jones 	ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_GPU_FAN_CTRL,
259412ff4c80SLuke D. Jones 				    value, &retval);
259512ff4c80SLuke D. Jones 	if (ret)
259612ff4c80SLuke D. Jones 		return ret;
259712ff4c80SLuke D. Jones 
259812ff4c80SLuke D. Jones 	if (retval != 1)
259912ff4c80SLuke D. Jones 		return -EIO;
260012ff4c80SLuke D. Jones 
260112ff4c80SLuke D. Jones 	asus->gpu_fan_pwm_mode = state;
260212ff4c80SLuke D. Jones 	return count;
260312ff4c80SLuke D. Jones }
260412ff4c80SLuke D. Jones 
pwm3_enable_show(struct device * dev,struct device_attribute * attr,char * buf)2605536fce82SLuke D. Jones static ssize_t pwm3_enable_show(struct device *dev,
2606536fce82SLuke D. Jones 				struct device_attribute *attr,
2607536fce82SLuke D. Jones 				char *buf)
2608536fce82SLuke D. Jones {
2609536fce82SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
2610536fce82SLuke D. Jones 
2611536fce82SLuke D. Jones 	return sysfs_emit(buf, "%d\n", asus->mid_fan_pwm_mode);
2612536fce82SLuke D. Jones }
2613536fce82SLuke D. Jones 
pwm3_enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)2614536fce82SLuke D. Jones static ssize_t pwm3_enable_store(struct device *dev,
2615536fce82SLuke D. Jones 				 struct device_attribute *attr,
2616536fce82SLuke D. Jones 				 const char *buf, size_t count)
2617536fce82SLuke D. Jones {
2618536fce82SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
2619536fce82SLuke D. Jones 	int state;
2620536fce82SLuke D. Jones 	int value;
2621536fce82SLuke D. Jones 	int ret;
2622536fce82SLuke D. Jones 	u32 retval;
2623536fce82SLuke D. Jones 
2624536fce82SLuke D. Jones 	ret = kstrtouint(buf, 10, &state);
2625536fce82SLuke D. Jones 	if (ret)
2626536fce82SLuke D. Jones 		return ret;
2627536fce82SLuke D. Jones 
2628536fce82SLuke D. Jones 	switch (state) { /* standard documented hwmon values */
2629536fce82SLuke D. Jones 	case ASUS_FAN_CTRL_FULLSPEED:
2630536fce82SLuke D. Jones 		value = 1;
2631536fce82SLuke D. Jones 		break;
2632536fce82SLuke D. Jones 	case ASUS_FAN_CTRL_AUTO:
2633536fce82SLuke D. Jones 		value = 0;
2634536fce82SLuke D. Jones 		break;
2635536fce82SLuke D. Jones 	default:
2636536fce82SLuke D. Jones 		return -EINVAL;
2637536fce82SLuke D. Jones 	}
2638536fce82SLuke D. Jones 
2639536fce82SLuke D. Jones 	ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_MID_FAN_CTRL,
2640536fce82SLuke D. Jones 				    value, &retval);
2641536fce82SLuke D. Jones 	if (ret)
2642536fce82SLuke D. Jones 		return ret;
2643536fce82SLuke D. Jones 
2644536fce82SLuke D. Jones 	if (retval != 1)
2645536fce82SLuke D. Jones 		return -EIO;
2646536fce82SLuke D. Jones 
2647536fce82SLuke D. Jones 	asus->mid_fan_pwm_mode = state;
2648536fce82SLuke D. Jones 	return count;
2649536fce82SLuke D. Jones }
2650536fce82SLuke D. Jones 
265153e755c2SKast Bernd /* Fan1 */
265253e755c2SKast Bernd static DEVICE_ATTR_RW(pwm1);
265353e755c2SKast Bernd static DEVICE_ATTR_RW(pwm1_enable);
265453e755c2SKast Bernd static DEVICE_ATTR_RO(fan1_input);
265553e755c2SKast Bernd static DEVICE_ATTR_RO(fan1_label);
265691809918SLuke D. Jones /* Fan2 - GPU fan */
265791809918SLuke D. Jones static DEVICE_ATTR_RW(pwm2_enable);
265891809918SLuke D. Jones static DEVICE_ATTR_RO(fan2_input);
265991809918SLuke D. Jones static DEVICE_ATTR_RO(fan2_label);
2660536fce82SLuke D. Jones /* Fan3 - Middle/center fan */
2661536fce82SLuke D. Jones static DEVICE_ATTR_RW(pwm3_enable);
2662536fce82SLuke D. Jones static DEVICE_ATTR_RO(fan3_input);
2663536fce82SLuke D. Jones static DEVICE_ATTR_RO(fan3_label);
266453e755c2SKast Bernd 
266553e755c2SKast Bernd /* Temperature */
266650a639fbSGuenter Roeck static DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL);
2667e07babdeSCorentin Chary 
2668e07babdeSCorentin Chary static struct attribute *hwmon_attributes[] = {
266950a639fbSGuenter Roeck 	&dev_attr_pwm1.attr,
267053e755c2SKast Bernd 	&dev_attr_pwm1_enable.attr,
267112ff4c80SLuke D. Jones 	&dev_attr_pwm2_enable.attr,
2672536fce82SLuke D. Jones 	&dev_attr_pwm3_enable.attr,
267353e755c2SKast Bernd 	&dev_attr_fan1_input.attr,
267453e755c2SKast Bernd 	&dev_attr_fan1_label.attr,
267591809918SLuke D. Jones 	&dev_attr_fan2_input.attr,
267691809918SLuke D. Jones 	&dev_attr_fan2_label.attr,
2677536fce82SLuke D. Jones 	&dev_attr_fan3_input.attr,
2678536fce82SLuke D. Jones 	&dev_attr_fan3_label.attr,
267953e755c2SKast Bernd 
268050a639fbSGuenter Roeck 	&dev_attr_temp1_input.attr,
2681e07babdeSCorentin Chary 	NULL
2682e07babdeSCorentin Chary };
2683e07babdeSCorentin Chary 
asus_hwmon_sysfs_is_visible(struct kobject * kobj,struct attribute * attr,int idx)2684587a1f16SAl Viro static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
2685e07babdeSCorentin Chary 					  struct attribute *attr, int idx)
2686e07babdeSCorentin Chary {
2687a8f9c36cSye xingchen 	struct device *dev = kobj_to_dev(kobj);
26888e8fe446SFuqian Huang 	struct asus_wmi *asus = dev_get_drvdata(dev->parent);
2689e07babdeSCorentin Chary 	u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
269053e755c2SKast Bernd 
2691e3168b87SDaniel Drake 	if (attr == &dev_attr_pwm1.attr) {
2692e3168b87SDaniel Drake 		if (asus->fan_type != FAN_TYPE_AGFN)
2693e3168b87SDaniel Drake 			return 0;
2694e3168b87SDaniel Drake 	} else if (attr == &dev_attr_fan1_input.attr
269553e755c2SKast Bernd 	    || attr == &dev_attr_fan1_label.attr
269653e755c2SKast Bernd 	    || attr == &dev_attr_pwm1_enable.attr) {
26972889ffcfSDaniel Drake 		if (asus->fan_type == FAN_TYPE_NONE)
26982889ffcfSDaniel Drake 			return 0;
269991809918SLuke D. Jones 	} else if (attr == &dev_attr_fan2_input.attr
270091809918SLuke D. Jones 	    || attr == &dev_attr_fan2_label.attr
270191809918SLuke D. Jones 	    || attr == &dev_attr_pwm2_enable.attr) {
270212ff4c80SLuke D. Jones 		if (asus->gpu_fan_type == FAN_TYPE_NONE)
270312ff4c80SLuke D. Jones 			return 0;
2704536fce82SLuke D. Jones 	} else if (attr == &dev_attr_fan3_input.attr
2705536fce82SLuke D. Jones 	    || attr == &dev_attr_fan3_label.attr
2706536fce82SLuke D. Jones 	    || attr == &dev_attr_pwm3_enable.attr) {
2707536fce82SLuke D. Jones 		if (asus->mid_fan_type == FAN_TYPE_NONE)
2708536fce82SLuke D. Jones 			return 0;
27092889ffcfSDaniel Drake 	} else if (attr == &dev_attr_temp1_input.attr) {
27102889ffcfSDaniel Drake 		int err = asus_wmi_get_devstate(asus,
27112889ffcfSDaniel Drake 						ASUS_WMI_DEVID_THERMAL_CTRL,
27122889ffcfSDaniel Drake 						&value);
271353e755c2SKast Bernd 
27142889ffcfSDaniel Drake 		if (err < 0)
2715e772aed3SAl Viro 			return 0; /* can't return negative here */
2716e07babdeSCorentin Chary 
27174fd19825SYurii Pavlovskyi 		/*
27184fd19825SYurii Pavlovskyi 		 * If the temperature value in deci-Kelvin is near the absolute
27194fd19825SYurii Pavlovskyi 		 * zero temperature, something is clearly wrong
27204fd19825SYurii Pavlovskyi 		 */
27214fd19825SYurii Pavlovskyi 		if (value == 0 || value == 1)
27222889ffcfSDaniel Drake 			return 0;
2723e07babdeSCorentin Chary 	}
2724e07babdeSCorentin Chary 
27252889ffcfSDaniel Drake 	return attr->mode;
2726e07babdeSCorentin Chary }
2727e07babdeSCorentin Chary 
2728e90d9ba8SArvind Yadav static const struct attribute_group hwmon_attribute_group = {
2729e07babdeSCorentin Chary 	.is_visible = asus_hwmon_sysfs_is_visible,
2730e07babdeSCorentin Chary 	.attrs = hwmon_attributes
2731e07babdeSCorentin Chary };
273250a639fbSGuenter Roeck __ATTRIBUTE_GROUPS(hwmon_attribute);
2733e07babdeSCorentin Chary 
asus_wmi_hwmon_init(struct asus_wmi * asus)2734e07babdeSCorentin Chary static int asus_wmi_hwmon_init(struct asus_wmi *asus)
2735e07babdeSCorentin Chary {
2736cd10ee00SYurii Pavlovskyi 	struct device *dev = &asus->platform_device->dev;
2737e07babdeSCorentin Chary 	struct device *hwmon;
2738e07babdeSCorentin Chary 
2739cd10ee00SYurii Pavlovskyi 	hwmon = devm_hwmon_device_register_with_groups(dev, "asus", asus,
274050a639fbSGuenter Roeck 			hwmon_attribute_groups);
2741cd10ee00SYurii Pavlovskyi 
2742e07babdeSCorentin Chary 	if (IS_ERR(hwmon)) {
2743e07babdeSCorentin Chary 		pr_err("Could not register asus hwmon device\n");
2744e07babdeSCorentin Chary 		return PTR_ERR(hwmon);
2745e07babdeSCorentin Chary 	}
274650a639fbSGuenter Roeck 	return 0;
2747e07babdeSCorentin Chary }
2748e07babdeSCorentin Chary 
asus_wmi_fan_init(struct asus_wmi * asus)274954a3121fSYurii Pavlovskyi static int asus_wmi_fan_init(struct asus_wmi *asus)
275054a3121fSYurii Pavlovskyi {
275112ff4c80SLuke D. Jones 	asus->gpu_fan_type = FAN_TYPE_NONE;
2752536fce82SLuke D. Jones 	asus->mid_fan_type = FAN_TYPE_NONE;
27532889ffcfSDaniel Drake 	asus->fan_type = FAN_TYPE_NONE;
27542889ffcfSDaniel Drake 	asus->agfn_pwm = -1;
275554a3121fSYurii Pavlovskyi 
2756c874b6deSThomas Weißschuh 	if (asus->driver->quirks->wmi_ignore_fan)
2757c874b6deSThomas Weißschuh 		asus->fan_type = FAN_TYPE_NONE;
2758c874b6deSThomas Weißschuh 	else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL))
2759e3168b87SDaniel Drake 		asus->fan_type = FAN_TYPE_SPEC83;
2760e3168b87SDaniel Drake 	else if (asus_wmi_has_agfn_fan(asus))
27612889ffcfSDaniel Drake 		asus->fan_type = FAN_TYPE_AGFN;
2762e3168b87SDaniel Drake 
276312ff4c80SLuke D. Jones 	/*  Modern models like G713 also have GPU fan control */
276412ff4c80SLuke D. Jones 	if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_FAN_CTRL))
276512ff4c80SLuke D. Jones 		asus->gpu_fan_type = FAN_TYPE_SPEC83;
276612ff4c80SLuke D. Jones 
2767536fce82SLuke D. Jones 	/* Some models also have a center/middle fan */
2768536fce82SLuke D. Jones 	if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MID_FAN_CTRL))
2769536fce82SLuke D. Jones 		asus->mid_fan_type = FAN_TYPE_SPEC83;
2770536fce82SLuke D. Jones 
2771e3168b87SDaniel Drake 	if (asus->fan_type == FAN_TYPE_NONE)
2772e3168b87SDaniel Drake 		return -ENODEV;
2773e3168b87SDaniel Drake 
27742889ffcfSDaniel Drake 	asus_fan_set_auto(asus);
27752889ffcfSDaniel Drake 	asus->fan_pwm_mode = ASUS_FAN_CTRL_AUTO;
2776e3168b87SDaniel Drake 	return 0;
277754a3121fSYurii Pavlovskyi }
277854a3121fSYurii Pavlovskyi 
2779b096f626SYurii Pavlovskyi /* Fan mode *******************************************************************/
2780b096f626SYurii Pavlovskyi 
fan_boost_mode_check_present(struct asus_wmi * asus)27819af93db9SDaniel Drake static int fan_boost_mode_check_present(struct asus_wmi *asus)
2782b096f626SYurii Pavlovskyi {
2783b096f626SYurii Pavlovskyi 	u32 result;
2784b096f626SYurii Pavlovskyi 	int err;
2785b096f626SYurii Pavlovskyi 
27869af93db9SDaniel Drake 	asus->fan_boost_mode_available = false;
2787b096f626SYurii Pavlovskyi 
27889af93db9SDaniel Drake 	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_BOOST_MODE,
27899af93db9SDaniel Drake 				    &result);
2790b096f626SYurii Pavlovskyi 	if (err) {
2791b096f626SYurii Pavlovskyi 		if (err == -ENODEV)
2792b096f626SYurii Pavlovskyi 			return 0;
2793b096f626SYurii Pavlovskyi 		else
2794b096f626SYurii Pavlovskyi 			return err;
2795b096f626SYurii Pavlovskyi 	}
2796b096f626SYurii Pavlovskyi 
2797b096f626SYurii Pavlovskyi 	if ((result & ASUS_WMI_DSTS_PRESENCE_BIT) &&
27989af93db9SDaniel Drake 			(result & ASUS_FAN_BOOST_MODES_MASK)) {
27999af93db9SDaniel Drake 		asus->fan_boost_mode_available = true;
28009af93db9SDaniel Drake 		asus->fan_boost_mode_mask = result & ASUS_FAN_BOOST_MODES_MASK;
2801b096f626SYurii Pavlovskyi 	}
2802b096f626SYurii Pavlovskyi 
2803b096f626SYurii Pavlovskyi 	return 0;
2804b096f626SYurii Pavlovskyi }
2805b096f626SYurii Pavlovskyi 
fan_boost_mode_write(struct asus_wmi * asus)28069af93db9SDaniel Drake static int fan_boost_mode_write(struct asus_wmi *asus)
2807b096f626SYurii Pavlovskyi {
2808b096f626SYurii Pavlovskyi 	u32 retval;
28090f0ac158SLuke D. Jones 	u8 value;
28100f0ac158SLuke D. Jones 	int err;
2811b096f626SYurii Pavlovskyi 
28129af93db9SDaniel Drake 	value = asus->fan_boost_mode;
2813b096f626SYurii Pavlovskyi 
28149af93db9SDaniel Drake 	pr_info("Set fan boost mode: %u\n", value);
28159af93db9SDaniel Drake 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_BOOST_MODE, value,
28169af93db9SDaniel Drake 				    &retval);
281772ceec58SVasiliy Kupriakov 
281872ceec58SVasiliy Kupriakov 	sysfs_notify(&asus->platform_device->dev.kobj, NULL,
281972ceec58SVasiliy Kupriakov 			"fan_boost_mode");
282072ceec58SVasiliy Kupriakov 
2821b096f626SYurii Pavlovskyi 	if (err) {
28229af93db9SDaniel Drake 		pr_warn("Failed to set fan boost mode: %d\n", err);
2823b096f626SYurii Pavlovskyi 		return err;
2824b096f626SYurii Pavlovskyi 	}
2825b096f626SYurii Pavlovskyi 
2826b096f626SYurii Pavlovskyi 	if (retval != 1) {
28279af93db9SDaniel Drake 		pr_warn("Failed to set fan boost mode (retval): 0x%x\n",
28289af93db9SDaniel Drake 			retval);
2829b096f626SYurii Pavlovskyi 		return -EIO;
2830b096f626SYurii Pavlovskyi 	}
2831b096f626SYurii Pavlovskyi 
2832b096f626SYurii Pavlovskyi 	return 0;
2833b096f626SYurii Pavlovskyi }
2834b096f626SYurii Pavlovskyi 
fan_boost_mode_switch_next(struct asus_wmi * asus)28359af93db9SDaniel Drake static int fan_boost_mode_switch_next(struct asus_wmi *asus)
2836b096f626SYurii Pavlovskyi {
28379af93db9SDaniel Drake 	u8 mask = asus->fan_boost_mode_mask;
28389af93db9SDaniel Drake 
28399af93db9SDaniel Drake 	if (asus->fan_boost_mode == ASUS_FAN_BOOST_MODE_NORMAL) {
28409af93db9SDaniel Drake 		if (mask & ASUS_FAN_BOOST_MODE_OVERBOOST_MASK)
28419af93db9SDaniel Drake 			asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_OVERBOOST;
28429af93db9SDaniel Drake 		else if (mask & ASUS_FAN_BOOST_MODE_SILENT_MASK)
28439af93db9SDaniel Drake 			asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_SILENT;
28449af93db9SDaniel Drake 	} else if (asus->fan_boost_mode == ASUS_FAN_BOOST_MODE_OVERBOOST) {
28459af93db9SDaniel Drake 		if (mask & ASUS_FAN_BOOST_MODE_SILENT_MASK)
28469af93db9SDaniel Drake 			asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_SILENT;
2847b096f626SYurii Pavlovskyi 		else
28489af93db9SDaniel Drake 			asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_NORMAL;
2849b096f626SYurii Pavlovskyi 	} else {
28509af93db9SDaniel Drake 		asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_NORMAL;
2851b096f626SYurii Pavlovskyi 	}
2852b096f626SYurii Pavlovskyi 
28539af93db9SDaniel Drake 	return fan_boost_mode_write(asus);
2854b096f626SYurii Pavlovskyi }
2855b096f626SYurii Pavlovskyi 
fan_boost_mode_show(struct device * dev,struct device_attribute * attr,char * buf)28569af93db9SDaniel Drake static ssize_t fan_boost_mode_show(struct device *dev,
2857b096f626SYurii Pavlovskyi 				   struct device_attribute *attr, char *buf)
2858b096f626SYurii Pavlovskyi {
2859b096f626SYurii Pavlovskyi 	struct asus_wmi *asus = dev_get_drvdata(dev);
2860b096f626SYurii Pavlovskyi 
2861170f0da2SLuke D. Jones 	return sysfs_emit(buf, "%d\n", asus->fan_boost_mode);
2862b096f626SYurii Pavlovskyi }
2863b096f626SYurii Pavlovskyi 
fan_boost_mode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)28649af93db9SDaniel Drake static ssize_t fan_boost_mode_store(struct device *dev,
28659af93db9SDaniel Drake 				    struct device_attribute *attr,
2866b096f626SYurii Pavlovskyi 				    const char *buf, size_t count)
2867b096f626SYurii Pavlovskyi {
2868b096f626SYurii Pavlovskyi 	struct asus_wmi *asus = dev_get_drvdata(dev);
28699af93db9SDaniel Drake 	u8 mask = asus->fan_boost_mode_mask;
28700f0ac158SLuke D. Jones 	u8 new_mode;
28710f0ac158SLuke D. Jones 	int result;
2872b096f626SYurii Pavlovskyi 
2873b096f626SYurii Pavlovskyi 	result = kstrtou8(buf, 10, &new_mode);
2874b096f626SYurii Pavlovskyi 	if (result < 0) {
2875b096f626SYurii Pavlovskyi 		pr_warn("Trying to store invalid value\n");
2876b096f626SYurii Pavlovskyi 		return result;
2877b096f626SYurii Pavlovskyi 	}
2878b096f626SYurii Pavlovskyi 
28799af93db9SDaniel Drake 	if (new_mode == ASUS_FAN_BOOST_MODE_OVERBOOST) {
28809af93db9SDaniel Drake 		if (!(mask & ASUS_FAN_BOOST_MODE_OVERBOOST_MASK))
2881b096f626SYurii Pavlovskyi 			return -EINVAL;
28829af93db9SDaniel Drake 	} else if (new_mode == ASUS_FAN_BOOST_MODE_SILENT) {
28839af93db9SDaniel Drake 		if (!(mask & ASUS_FAN_BOOST_MODE_SILENT_MASK))
2884b096f626SYurii Pavlovskyi 			return -EINVAL;
28859af93db9SDaniel Drake 	} else if (new_mode != ASUS_FAN_BOOST_MODE_NORMAL) {
2886b096f626SYurii Pavlovskyi 		return -EINVAL;
2887b096f626SYurii Pavlovskyi 	}
2888b096f626SYurii Pavlovskyi 
28899af93db9SDaniel Drake 	asus->fan_boost_mode = new_mode;
28909af93db9SDaniel Drake 	fan_boost_mode_write(asus);
2891b096f626SYurii Pavlovskyi 
2892edeee341SLeonid Maksymchuk 	return count;
2893b096f626SYurii Pavlovskyi }
2894b096f626SYurii Pavlovskyi 
28959af93db9SDaniel Drake // Fan boost mode: 0 - normal, 1 - overboost, 2 - silent
28969af93db9SDaniel Drake static DEVICE_ATTR_RW(fan_boost_mode);
2897b096f626SYurii Pavlovskyi 
28980f0ac158SLuke D. Jones /* Custom fan curves **********************************************************/
28990f0ac158SLuke D. Jones 
fan_curve_copy_from_buf(struct fan_curve_data * data,u8 * buf)29000f0ac158SLuke D. Jones static void fan_curve_copy_from_buf(struct fan_curve_data *data, u8 *buf)
29010f0ac158SLuke D. Jones {
29020f0ac158SLuke D. Jones 	int i;
29030f0ac158SLuke D. Jones 
29040f0ac158SLuke D. Jones 	for (i = 0; i < FAN_CURVE_POINTS; i++) {
29050f0ac158SLuke D. Jones 		data->temps[i] = buf[i];
29060f0ac158SLuke D. Jones 	}
29070f0ac158SLuke D. Jones 
29080f0ac158SLuke D. Jones 	for (i = 0; i < FAN_CURVE_POINTS; i++) {
29090f0ac158SLuke D. Jones 		data->percents[i] =
29100f0ac158SLuke D. Jones 			255 * buf[i + FAN_CURVE_POINTS] / 100;
29110f0ac158SLuke D. Jones 	}
29120f0ac158SLuke D. Jones }
29130f0ac158SLuke D. Jones 
fan_curve_get_factory_default(struct asus_wmi * asus,u32 fan_dev)29140f0ac158SLuke D. Jones static int fan_curve_get_factory_default(struct asus_wmi *asus, u32 fan_dev)
29150f0ac158SLuke D. Jones {
29160f0ac158SLuke D. Jones 	struct fan_curve_data *curves;
29170f0ac158SLuke D. Jones 	u8 buf[FAN_CURVE_BUF_LEN];
2918fa69653fSLuke D. Jones 	int err, fan_idx;
29190f0ac158SLuke D. Jones 	u8 mode = 0;
29200f0ac158SLuke D. Jones 
29216051a4b1SMohamed Ghanmi 	if (asus->throttle_thermal_policy_dev)
29220f0ac158SLuke D. Jones 		mode = asus->throttle_thermal_policy_mode;
29230f0ac158SLuke D. Jones 	/* DEVID_<C/G>PU_FAN_CURVE is switched for OVERBOOST vs SILENT */
29240f0ac158SLuke D. Jones 	if (mode == 2)
29250f0ac158SLuke D. Jones 		mode = 1;
29260f0ac158SLuke D. Jones 	else if (mode == 1)
29270f0ac158SLuke D. Jones 		mode = 2;
29280f0ac158SLuke D. Jones 
29290f0ac158SLuke D. Jones 	err = asus_wmi_evaluate_method_buf(asus->dsts_id, fan_dev, mode, buf,
29300f0ac158SLuke D. Jones 					   FAN_CURVE_BUF_LEN);
2931a2bdf10cSLuke D. Jones 	if (err) {
2932a2bdf10cSLuke D. Jones 		pr_warn("%s (0x%08x) failed: %d\n", __func__, fan_dev, err);
29330f0ac158SLuke D. Jones 		return err;
2934a2bdf10cSLuke D. Jones 	}
29350f0ac158SLuke D. Jones 
2936fa69653fSLuke D. Jones 	fan_idx = FAN_CURVE_DEV_CPU;
2937fa69653fSLuke D. Jones 	if (fan_dev == ASUS_WMI_DEVID_GPU_FAN_CURVE)
2938fa69653fSLuke D. Jones 		fan_idx = FAN_CURVE_DEV_GPU;
2939fa69653fSLuke D. Jones 
2940fa69653fSLuke D. Jones 	if (fan_dev == ASUS_WMI_DEVID_MID_FAN_CURVE)
2941fa69653fSLuke D. Jones 		fan_idx = FAN_CURVE_DEV_MID;
2942fa69653fSLuke D. Jones 
2943fa69653fSLuke D. Jones 	curves = &asus->custom_fan_curves[fan_idx];
29440f0ac158SLuke D. Jones 	curves->device_id = fan_dev;
29450f0ac158SLuke D. Jones 
2946fa69653fSLuke D. Jones 	fan_curve_copy_from_buf(curves, buf);
29470f0ac158SLuke D. Jones 	return 0;
29480f0ac158SLuke D. Jones }
29490f0ac158SLuke D. Jones 
29500f0ac158SLuke D. Jones /* Check if capability exists, and populate defaults */
fan_curve_check_present(struct asus_wmi * asus,bool * available,u32 fan_dev)29510f0ac158SLuke D. Jones static int fan_curve_check_present(struct asus_wmi *asus, bool *available,
29520f0ac158SLuke D. Jones 				   u32 fan_dev)
29530f0ac158SLuke D. Jones {
29540f0ac158SLuke D. Jones 	int err;
29550f0ac158SLuke D. Jones 
29560f0ac158SLuke D. Jones 	*available = false;
29570f0ac158SLuke D. Jones 
295801fd7e78SThomas Weißschuh 	if (asus->fan_type == FAN_TYPE_NONE)
295901fd7e78SThomas Weißschuh 		return 0;
296001fd7e78SThomas Weißschuh 
29610f0ac158SLuke D. Jones 	err = fan_curve_get_factory_default(asus, fan_dev);
29620f0ac158SLuke D. Jones 	if (err) {
29630f0ac158SLuke D. Jones 		return 0;
29640f0ac158SLuke D. Jones 	}
29650f0ac158SLuke D. Jones 
29660f0ac158SLuke D. Jones 	*available = true;
29670f0ac158SLuke D. Jones 	return 0;
29680f0ac158SLuke D. Jones }
29690f0ac158SLuke D. Jones 
29700f0ac158SLuke D. Jones /* Determine which fan the attribute is for if SENSOR_ATTR */
fan_curve_attr_select(struct asus_wmi * asus,struct device_attribute * attr)29710f0ac158SLuke D. Jones static struct fan_curve_data *fan_curve_attr_select(struct asus_wmi *asus,
29720f0ac158SLuke D. Jones 					      struct device_attribute *attr)
29730f0ac158SLuke D. Jones {
29740f0ac158SLuke D. Jones 	int index = to_sensor_dev_attr(attr)->index;
29750f0ac158SLuke D. Jones 
2976fa69653fSLuke D. Jones 	return &asus->custom_fan_curves[index];
29770f0ac158SLuke D. Jones }
29780f0ac158SLuke D. Jones 
29790f0ac158SLuke D. Jones /* Determine which fan the attribute is for if SENSOR_ATTR_2 */
fan_curve_attr_2_select(struct asus_wmi * asus,struct device_attribute * attr)29800f0ac158SLuke D. Jones static struct fan_curve_data *fan_curve_attr_2_select(struct asus_wmi *asus,
29810f0ac158SLuke D. Jones 					    struct device_attribute *attr)
29820f0ac158SLuke D. Jones {
29830f0ac158SLuke D. Jones 	int nr = to_sensor_dev_attr_2(attr)->nr;
29840f0ac158SLuke D. Jones 
2985fa69653fSLuke D. Jones 	return &asus->custom_fan_curves[nr & ~FAN_CURVE_PWM_MASK];
29860f0ac158SLuke D. Jones }
29870f0ac158SLuke D. Jones 
fan_curve_show(struct device * dev,struct device_attribute * attr,char * buf)29880f0ac158SLuke D. Jones static ssize_t fan_curve_show(struct device *dev,
29890f0ac158SLuke D. Jones 			      struct device_attribute *attr, char *buf)
29900f0ac158SLuke D. Jones {
29910f0ac158SLuke D. Jones 	struct sensor_device_attribute_2 *dev_attr = to_sensor_dev_attr_2(attr);
29920f0ac158SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
29930f0ac158SLuke D. Jones 	struct fan_curve_data *data;
2994fa69653fSLuke D. Jones 	int value, pwm, index;
29950f0ac158SLuke D. Jones 
29960f0ac158SLuke D. Jones 	data = fan_curve_attr_2_select(asus, attr);
2997fa69653fSLuke D. Jones 	pwm = dev_attr->nr & FAN_CURVE_PWM_MASK;
29980f0ac158SLuke D. Jones 	index = dev_attr->index;
29990f0ac158SLuke D. Jones 
3000fa69653fSLuke D. Jones 	if (pwm)
30010f0ac158SLuke D. Jones 		value = data->percents[index];
30020f0ac158SLuke D. Jones 	else
30030f0ac158SLuke D. Jones 		value = data->temps[index];
30040f0ac158SLuke D. Jones 
30050f0ac158SLuke D. Jones 	return sysfs_emit(buf, "%d\n", value);
30060f0ac158SLuke D. Jones }
30070f0ac158SLuke D. Jones 
30080f0ac158SLuke D. Jones /*
30090f0ac158SLuke D. Jones  * "fan_dev" is the related WMI method such as ASUS_WMI_DEVID_CPU_FAN_CURVE.
30100f0ac158SLuke D. Jones  */
fan_curve_write(struct asus_wmi * asus,struct fan_curve_data * data)30110f0ac158SLuke D. Jones static int fan_curve_write(struct asus_wmi *asus,
30120f0ac158SLuke D. Jones 			   struct fan_curve_data *data)
30130f0ac158SLuke D. Jones {
30140f0ac158SLuke D. Jones 	u32 arg1 = 0, arg2 = 0, arg3 = 0, arg4 = 0;
30150f0ac158SLuke D. Jones 	u8 *percents = data->percents;
30160f0ac158SLuke D. Jones 	u8 *temps = data->temps;
30170f0ac158SLuke D. Jones 	int ret, i, shift = 0;
30180f0ac158SLuke D. Jones 
30190f0ac158SLuke D. Jones 	if (!data->enabled)
30200f0ac158SLuke D. Jones 		return 0;
30210f0ac158SLuke D. Jones 
30220f0ac158SLuke D. Jones 	for (i = 0; i < FAN_CURVE_POINTS / 2; i++) {
30230f0ac158SLuke D. Jones 		arg1 += (temps[i]) << shift;
30240f0ac158SLuke D. Jones 		arg2 += (temps[i + 4]) << shift;
30250f0ac158SLuke D. Jones 		/* Scale to percentage for device */
30260f0ac158SLuke D. Jones 		arg3 += (100 * percents[i] / 255) << shift;
30270f0ac158SLuke D. Jones 		arg4 += (100 * percents[i + 4] / 255) << shift;
30280f0ac158SLuke D. Jones 		shift += 8;
30290f0ac158SLuke D. Jones 	}
30300f0ac158SLuke D. Jones 
30310f0ac158SLuke D. Jones 	return asus_wmi_evaluate_method5(ASUS_WMI_METHODID_DEVS,
30320f0ac158SLuke D. Jones 					 data->device_id,
30330f0ac158SLuke D. Jones 					 arg1, arg2, arg3, arg4, &ret);
30340f0ac158SLuke D. Jones }
30350f0ac158SLuke D. Jones 
fan_curve_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)30360f0ac158SLuke D. Jones static ssize_t fan_curve_store(struct device *dev,
30370f0ac158SLuke D. Jones 			       struct device_attribute *attr, const char *buf,
30380f0ac158SLuke D. Jones 			       size_t count)
30390f0ac158SLuke D. Jones {
30400f0ac158SLuke D. Jones 	struct sensor_device_attribute_2 *dev_attr = to_sensor_dev_attr_2(attr);
30410f0ac158SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
30420f0ac158SLuke D. Jones 	struct fan_curve_data *data;
3043fa69653fSLuke D. Jones 	int err, pwm, index;
30440f0ac158SLuke D. Jones 	u8 value;
30450f0ac158SLuke D. Jones 
30460f0ac158SLuke D. Jones 	data = fan_curve_attr_2_select(asus, attr);
3047fa69653fSLuke D. Jones 	pwm = dev_attr->nr & FAN_CURVE_PWM_MASK;
3048fa69653fSLuke D. Jones 	index = dev_attr->index;
30490f0ac158SLuke D. Jones 
30500f0ac158SLuke D. Jones 	err = kstrtou8(buf, 10, &value);
30510f0ac158SLuke D. Jones 	if (err < 0)
30520f0ac158SLuke D. Jones 		return err;
30530f0ac158SLuke D. Jones 
3054fa69653fSLuke D. Jones 	if (pwm)
30550f0ac158SLuke D. Jones 		data->percents[index] = value;
3056fa69653fSLuke D. Jones 	else
30570f0ac158SLuke D. Jones 		data->temps[index] = value;
30580f0ac158SLuke D. Jones 
30590f0ac158SLuke D. Jones 	/*
30600f0ac158SLuke D. Jones 	 * Mark as disabled so the user has to explicitly enable to apply a
30610f0ac158SLuke D. Jones 	 * changed fan curve. This prevents potential lockups from writing out
30620f0ac158SLuke D. Jones 	 * many changes as one-write-per-change.
30630f0ac158SLuke D. Jones 	 */
30640f0ac158SLuke D. Jones 	data->enabled = false;
30650f0ac158SLuke D. Jones 
30660f0ac158SLuke D. Jones 	return count;
30670f0ac158SLuke D. Jones }
30680f0ac158SLuke D. Jones 
fan_curve_enable_show(struct device * dev,struct device_attribute * attr,char * buf)30690f0ac158SLuke D. Jones static ssize_t fan_curve_enable_show(struct device *dev,
30700f0ac158SLuke D. Jones 				     struct device_attribute *attr, char *buf)
30710f0ac158SLuke D. Jones {
30720f0ac158SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
30730f0ac158SLuke D. Jones 	struct fan_curve_data *data;
30740f0ac158SLuke D. Jones 	int out = 2;
30750f0ac158SLuke D. Jones 
30760f0ac158SLuke D. Jones 	data = fan_curve_attr_select(asus, attr);
30770f0ac158SLuke D. Jones 
30780f0ac158SLuke D. Jones 	if (data->enabled)
30790f0ac158SLuke D. Jones 		out = 1;
30800f0ac158SLuke D. Jones 
30810f0ac158SLuke D. Jones 	return sysfs_emit(buf, "%d\n", out);
30820f0ac158SLuke D. Jones }
30830f0ac158SLuke D. Jones 
fan_curve_enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)30840f0ac158SLuke D. Jones static ssize_t fan_curve_enable_store(struct device *dev,
30850f0ac158SLuke D. Jones 				      struct device_attribute *attr,
30860f0ac158SLuke D. Jones 				      const char *buf, size_t count)
30870f0ac158SLuke D. Jones {
30880f0ac158SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev);
30890f0ac158SLuke D. Jones 	struct fan_curve_data *data;
30900f0ac158SLuke D. Jones 	int value, err;
30910f0ac158SLuke D. Jones 
30920f0ac158SLuke D. Jones 	data = fan_curve_attr_select(asus, attr);
30930f0ac158SLuke D. Jones 
30940f0ac158SLuke D. Jones 	err = kstrtoint(buf, 10, &value);
30950f0ac158SLuke D. Jones 	if (err < 0)
30960f0ac158SLuke D. Jones 		return err;
30970f0ac158SLuke D. Jones 
30980f0ac158SLuke D. Jones 	switch (value) {
30990f0ac158SLuke D. Jones 	case 1:
31000f0ac158SLuke D. Jones 		data->enabled = true;
31010f0ac158SLuke D. Jones 		break;
31020f0ac158SLuke D. Jones 	case 2:
31030f0ac158SLuke D. Jones 		data->enabled = false;
31040f0ac158SLuke D. Jones 		break;
31050f0ac158SLuke D. Jones 	/*
31060f0ac158SLuke D. Jones 	 * Auto + reset the fan curve data to defaults. Make it an explicit
31070f0ac158SLuke D. Jones 	 * option so that users don't accidentally overwrite a set fan curve.
31080f0ac158SLuke D. Jones 	 */
31090f0ac158SLuke D. Jones 	case 3:
31100f0ac158SLuke D. Jones 		err = fan_curve_get_factory_default(asus, data->device_id);
31110f0ac158SLuke D. Jones 		if (err)
31120f0ac158SLuke D. Jones 			return err;
31130f0ac158SLuke D. Jones 		data->enabled = false;
31140f0ac158SLuke D. Jones 		break;
31150f0ac158SLuke D. Jones 	default:
31160f0ac158SLuke D. Jones 		return -EINVAL;
311737f34df8SYang Li 	}
31180f0ac158SLuke D. Jones 
31190f0ac158SLuke D. Jones 	if (data->enabled) {
31200f0ac158SLuke D. Jones 		err = fan_curve_write(asus, data);
31210f0ac158SLuke D. Jones 		if (err)
31220f0ac158SLuke D. Jones 			return err;
31230f0ac158SLuke D. Jones 	} else {
31240f0ac158SLuke D. Jones 		/*
31250f0ac158SLuke D. Jones 		 * For machines with throttle this is the only way to reset fans
31260f0ac158SLuke D. Jones 		 * to default mode of operation (does not erase curve data).
31270f0ac158SLuke D. Jones 		 */
31286051a4b1SMohamed Ghanmi 		if (asus->throttle_thermal_policy_dev) {
31290f0ac158SLuke D. Jones 			err = throttle_thermal_policy_write(asus);
31300f0ac158SLuke D. Jones 			if (err)
31310f0ac158SLuke D. Jones 				return err;
31320f0ac158SLuke D. Jones 		/* Similar is true for laptops with this fan */
31330f0ac158SLuke D. Jones 		} else if (asus->fan_type == FAN_TYPE_SPEC83) {
31340f0ac158SLuke D. Jones 			err = asus_fan_set_auto(asus);
31350f0ac158SLuke D. Jones 			if (err)
31360f0ac158SLuke D. Jones 				return err;
31370f0ac158SLuke D. Jones 		} else {
31380f0ac158SLuke D. Jones 			/* Safeguard against fautly ACPI tables */
31390f0ac158SLuke D. Jones 			err = fan_curve_get_factory_default(asus, data->device_id);
31400f0ac158SLuke D. Jones 			if (err)
31410f0ac158SLuke D. Jones 				return err;
31420f0ac158SLuke D. Jones 			err = fan_curve_write(asus, data);
31430f0ac158SLuke D. Jones 			if (err)
31440f0ac158SLuke D. Jones 				return err;
31450f0ac158SLuke D. Jones 		}
31460f0ac158SLuke D. Jones 	}
31470f0ac158SLuke D. Jones 	return count;
31480f0ac158SLuke D. Jones }
31490f0ac158SLuke D. Jones 
31500f0ac158SLuke D. Jones /* CPU */
31510f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_RW(pwm1_enable, fan_curve_enable, FAN_CURVE_DEV_CPU);
31520f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point1_temp, fan_curve,
31530f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU, 0);
31540f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point2_temp, fan_curve,
31550f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU, 1);
31560f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point3_temp, fan_curve,
31570f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU, 2);
31580f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point4_temp, fan_curve,
31590f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU, 3);
31600f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point5_temp, fan_curve,
31610f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU, 4);
31620f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point6_temp, fan_curve,
31630f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU, 5);
31640f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point7_temp, fan_curve,
31650f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU, 6);
31660f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point8_temp, fan_curve,
31670f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU, 7);
31680f0ac158SLuke D. Jones 
31690f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point1_pwm, fan_curve,
31700f0ac158SLuke D. Jones 				FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 0);
31710f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point2_pwm, fan_curve,
31720f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 1);
31730f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point3_pwm, fan_curve,
31740f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 2);
31750f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point4_pwm, fan_curve,
31760f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 3);
31770f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point5_pwm, fan_curve,
31780f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 4);
31790f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point6_pwm, fan_curve,
31800f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 5);
31810f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point7_pwm, fan_curve,
31820f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 6);
31830f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point8_pwm, fan_curve,
31840f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 7);
31850f0ac158SLuke D. Jones 
31860f0ac158SLuke D. Jones /* GPU */
31870f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_RW(pwm2_enable, fan_curve_enable, FAN_CURVE_DEV_GPU);
31880f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point1_temp, fan_curve,
31890f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU, 0);
31900f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point2_temp, fan_curve,
31910f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU, 1);
31920f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point3_temp, fan_curve,
31930f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU, 2);
31940f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point4_temp, fan_curve,
31950f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU, 3);
31960f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point5_temp, fan_curve,
31970f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU, 4);
31980f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point6_temp, fan_curve,
31990f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU, 5);
32000f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point7_temp, fan_curve,
32010f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU, 6);
32020f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point8_temp, fan_curve,
32030f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU, 7);
32040f0ac158SLuke D. Jones 
32050f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point1_pwm, fan_curve,
32060f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 0);
32070f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point2_pwm, fan_curve,
32080f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 1);
32090f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point3_pwm, fan_curve,
32100f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 2);
32110f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point4_pwm, fan_curve,
32120f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 3);
32130f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point5_pwm, fan_curve,
32140f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 4);
32150f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point6_pwm, fan_curve,
32160f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 5);
32170f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point7_pwm, fan_curve,
32180f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 6);
32190f0ac158SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point8_pwm, fan_curve,
32200f0ac158SLuke D. Jones 			       FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 7);
32210f0ac158SLuke D. Jones 
3222ee887807SLuke D. Jones /* MID */
3223fa69653fSLuke D. Jones static SENSOR_DEVICE_ATTR_RW(pwm3_enable, fan_curve_enable, FAN_CURVE_DEV_MID);
3224ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point1_temp, fan_curve,
3225fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID, 0);
3226ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point2_temp, fan_curve,
3227fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID, 1);
3228ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point3_temp, fan_curve,
3229fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID, 2);
3230ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point4_temp, fan_curve,
3231fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID, 3);
3232ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point5_temp, fan_curve,
3233fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID, 4);
3234ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point6_temp, fan_curve,
3235fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID, 5);
3236ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point7_temp, fan_curve,
3237fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID, 6);
3238ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point8_temp, fan_curve,
3239fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID, 7);
3240ee887807SLuke D. Jones 
3241ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point1_pwm, fan_curve,
3242fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 0);
3243ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point2_pwm, fan_curve,
3244fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 1);
3245ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point3_pwm, fan_curve,
3246fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 2);
3247ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point4_pwm, fan_curve,
3248fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 3);
3249ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point5_pwm, fan_curve,
3250fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 4);
3251ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point6_pwm, fan_curve,
3252fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 5);
3253ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point7_pwm, fan_curve,
3254fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 6);
3255ee887807SLuke D. Jones static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point8_pwm, fan_curve,
3256fa69653fSLuke D. Jones 			       FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 7);
3257ee887807SLuke D. Jones 
32580f0ac158SLuke D. Jones static struct attribute *asus_fan_curve_attr[] = {
32590f0ac158SLuke D. Jones 	/* CPU */
32600f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
32610f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
32620f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
32630f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr,
32640f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr,
32650f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr,
32660f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr,
32670f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point7_temp.dev_attr.attr,
32680f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point8_temp.dev_attr.attr,
32690f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
32700f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
32710f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr,
32720f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr,
32730f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr,
32740f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr,
32750f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point7_pwm.dev_attr.attr,
32760f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm1_auto_point8_pwm.dev_attr.attr,
32770f0ac158SLuke D. Jones 	/* GPU */
32780f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_enable.dev_attr.attr,
32790f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr,
32800f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr,
32810f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr,
32820f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr,
32830f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point5_temp.dev_attr.attr,
32840f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point6_temp.dev_attr.attr,
32850f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point7_temp.dev_attr.attr,
32860f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point8_temp.dev_attr.attr,
32870f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
32880f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
32890f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr,
32900f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr,
32910f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point5_pwm.dev_attr.attr,
32920f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point6_pwm.dev_attr.attr,
32930f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point7_pwm.dev_attr.attr,
32940f0ac158SLuke D. Jones 	&sensor_dev_attr_pwm2_auto_point8_pwm.dev_attr.attr,
3295ee887807SLuke D. Jones 	/* MID */
3296ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_enable.dev_attr.attr,
3297ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr,
3298ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr,
3299ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr,
3300ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point4_temp.dev_attr.attr,
3301ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point5_temp.dev_attr.attr,
3302ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point6_temp.dev_attr.attr,
3303ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point7_temp.dev_attr.attr,
3304ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point8_temp.dev_attr.attr,
3305ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
3306ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
3307ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point3_pwm.dev_attr.attr,
3308ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point4_pwm.dev_attr.attr,
3309ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point5_pwm.dev_attr.attr,
3310ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point6_pwm.dev_attr.attr,
3311ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point7_pwm.dev_attr.attr,
3312ee887807SLuke D. Jones 	&sensor_dev_attr_pwm3_auto_point8_pwm.dev_attr.attr,
33130f0ac158SLuke D. Jones 	NULL
33140f0ac158SLuke D. Jones };
33150f0ac158SLuke D. Jones 
asus_fan_curve_is_visible(struct kobject * kobj,struct attribute * attr,int idx)33160f0ac158SLuke D. Jones static umode_t asus_fan_curve_is_visible(struct kobject *kobj,
33170f0ac158SLuke D. Jones 					 struct attribute *attr, int idx)
33180f0ac158SLuke D. Jones {
3319242e85a7SMinghao Chi 	struct device *dev = kobj_to_dev(kobj);
33200f0ac158SLuke D. Jones 	struct asus_wmi *asus = dev_get_drvdata(dev->parent);
33210f0ac158SLuke D. Jones 
33220f0ac158SLuke D. Jones 	/*
33230f0ac158SLuke D. Jones 	 * Check the char instead of casting attr as there are two attr types
33240f0ac158SLuke D. Jones 	 * involved here (attr1 and attr2)
33250f0ac158SLuke D. Jones 	 */
33260f0ac158SLuke D. Jones 	if (asus->cpu_fan_curve_available && attr->name[3] == '1')
33270f0ac158SLuke D. Jones 		return 0644;
33280f0ac158SLuke D. Jones 
33290f0ac158SLuke D. Jones 	if (asus->gpu_fan_curve_available && attr->name[3] == '2')
33300f0ac158SLuke D. Jones 		return 0644;
33310f0ac158SLuke D. Jones 
3332ee887807SLuke D. Jones 	if (asus->mid_fan_curve_available && attr->name[3] == '3')
3333ee887807SLuke D. Jones 		return 0644;
3334ee887807SLuke D. Jones 
33350f0ac158SLuke D. Jones 	return 0;
33360f0ac158SLuke D. Jones }
33370f0ac158SLuke D. Jones 
33380f0ac158SLuke D. Jones static const struct attribute_group asus_fan_curve_attr_group = {
33390f0ac158SLuke D. Jones 	.is_visible = asus_fan_curve_is_visible,
33400f0ac158SLuke D. Jones 	.attrs = asus_fan_curve_attr,
33410f0ac158SLuke D. Jones };
33420f0ac158SLuke D. Jones __ATTRIBUTE_GROUPS(asus_fan_curve_attr);
33430f0ac158SLuke D. Jones 
33440f0ac158SLuke D. Jones /*
33456051a4b1SMohamed Ghanmi  * Must be initialised after throttle_thermal_policy_dev is set as
33466051a4b1SMohamed Ghanmi  * we check the status of throttle_thermal_policy_dev during init.
33470f0ac158SLuke D. Jones  */
asus_wmi_custom_fan_curve_init(struct asus_wmi * asus)33480f0ac158SLuke D. Jones static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus)
33490f0ac158SLuke D. Jones {
33500f0ac158SLuke D. Jones 	struct device *dev = &asus->platform_device->dev;
33510f0ac158SLuke D. Jones 	struct device *hwmon;
33520f0ac158SLuke D. Jones 	int err;
33530f0ac158SLuke D. Jones 
33540f0ac158SLuke D. Jones 	err = fan_curve_check_present(asus, &asus->cpu_fan_curve_available,
33550f0ac158SLuke D. Jones 				      ASUS_WMI_DEVID_CPU_FAN_CURVE);
33560f0ac158SLuke D. Jones 	if (err)
33570f0ac158SLuke D. Jones 		return err;
33580f0ac158SLuke D. Jones 
33590f0ac158SLuke D. Jones 	err = fan_curve_check_present(asus, &asus->gpu_fan_curve_available,
33600f0ac158SLuke D. Jones 				      ASUS_WMI_DEVID_GPU_FAN_CURVE);
33610f0ac158SLuke D. Jones 	if (err)
33620f0ac158SLuke D. Jones 		return err;
33630f0ac158SLuke D. Jones 
3364ee887807SLuke D. Jones 	err = fan_curve_check_present(asus, &asus->mid_fan_curve_available,
3365ee887807SLuke D. Jones 				      ASUS_WMI_DEVID_MID_FAN_CURVE);
3366ee887807SLuke D. Jones 	if (err)
3367ee887807SLuke D. Jones 		return err;
3368ee887807SLuke D. Jones 
3369ee887807SLuke D. Jones 	if (!asus->cpu_fan_curve_available
3370ee887807SLuke D. Jones 		&& !asus->gpu_fan_curve_available
3371ee887807SLuke D. Jones 		&& !asus->mid_fan_curve_available)
33720f0ac158SLuke D. Jones 		return 0;
33730f0ac158SLuke D. Jones 
33740f0ac158SLuke D. Jones 	hwmon = devm_hwmon_device_register_with_groups(
33750f0ac158SLuke D. Jones 		dev, "asus_custom_fan_curve", asus, asus_fan_curve_attr_groups);
33760f0ac158SLuke D. Jones 
33770f0ac158SLuke D. Jones 	if (IS_ERR(hwmon)) {
33780f0ac158SLuke D. Jones 		dev_err(dev,
33790f0ac158SLuke D. Jones 			"Could not register asus_custom_fan_curve device\n");
33800f0ac158SLuke D. Jones 		return PTR_ERR(hwmon);
33810f0ac158SLuke D. Jones 	}
33820f0ac158SLuke D. Jones 
33830f0ac158SLuke D. Jones 	return 0;
33840f0ac158SLuke D. Jones }
33850f0ac158SLuke D. Jones 
33862daa86e7SLeonid Maksymchuk /* Throttle thermal policy ****************************************************/
throttle_thermal_policy_write(struct asus_wmi * asus)33872daa86e7SLeonid Maksymchuk static int throttle_thermal_policy_write(struct asus_wmi *asus)
33882daa86e7SLeonid Maksymchuk {
33895a4f732eSArmin Wolf 	u8 value;
33906051a4b1SMohamed Ghanmi 	int err;
33912daa86e7SLeonid Maksymchuk 
33925a4f732eSArmin Wolf 	if (asus->throttle_thermal_policy_dev == ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO) {
33935a4f732eSArmin Wolf 		switch (asus->throttle_thermal_policy_mode) {
33945a4f732eSArmin Wolf 		case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT:
33955a4f732eSArmin Wolf 			value = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO;
33965a4f732eSArmin Wolf 			break;
33975a4f732eSArmin Wolf 		case ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST:
33985a4f732eSArmin Wolf 			value = ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO;
33995a4f732eSArmin Wolf 			break;
34005a4f732eSArmin Wolf 		case ASUS_THROTTLE_THERMAL_POLICY_SILENT:
34015a4f732eSArmin Wolf 			value = ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO;
34025a4f732eSArmin Wolf 			break;
34035a4f732eSArmin Wolf 		default:
34045a4f732eSArmin Wolf 			return -EINVAL;
34055a4f732eSArmin Wolf 		}
34065a4f732eSArmin Wolf 	} else {
34075a4f732eSArmin Wolf 		value = asus->throttle_thermal_policy_mode;
34085a4f732eSArmin Wolf 	}
34095a4f732eSArmin Wolf 
34104d5a573fSArmin Wolf 	/* Some machines do not return an error code as a result, so we ignore it */
34114d5a573fSArmin Wolf 	err = asus_wmi_set_devstate(asus->throttle_thermal_policy_dev, value, NULL);
341272ceec58SVasiliy Kupriakov 
341372ceec58SVasiliy Kupriakov 	sysfs_notify(&asus->platform_device->dev.kobj, NULL,
341472ceec58SVasiliy Kupriakov 			"throttle_thermal_policy");
341572ceec58SVasiliy Kupriakov 
34162daa86e7SLeonid Maksymchuk 	if (err) {
34172daa86e7SLeonid Maksymchuk 		pr_warn("Failed to set throttle thermal policy: %d\n", err);
34182daa86e7SLeonid Maksymchuk 		return err;
34192daa86e7SLeonid Maksymchuk 	}
34202daa86e7SLeonid Maksymchuk 
34210f0ac158SLuke D. Jones 	/* Must set to disabled if mode is toggled */
34220f0ac158SLuke D. Jones 	if (asus->cpu_fan_curve_available)
34230f0ac158SLuke D. Jones 		asus->custom_fan_curves[FAN_CURVE_DEV_CPU].enabled = false;
34240f0ac158SLuke D. Jones 	if (asus->gpu_fan_curve_available)
34250f0ac158SLuke D. Jones 		asus->custom_fan_curves[FAN_CURVE_DEV_GPU].enabled = false;
3426ee887807SLuke D. Jones 	if (asus->mid_fan_curve_available)
3427ee887807SLuke D. Jones 		asus->custom_fan_curves[FAN_CURVE_DEV_MID].enabled = false;
34280f0ac158SLuke D. Jones 
34292daa86e7SLeonid Maksymchuk 	return 0;
34302daa86e7SLeonid Maksymchuk }
34312daa86e7SLeonid Maksymchuk 
throttle_thermal_policy_set_default(struct asus_wmi * asus)3432a2821584SLeonid Maksymchuk static int throttle_thermal_policy_set_default(struct asus_wmi *asus)
3433a2821584SLeonid Maksymchuk {
34346051a4b1SMohamed Ghanmi 	if (!asus->throttle_thermal_policy_dev)
3435a2821584SLeonid Maksymchuk 		return 0;
3436a2821584SLeonid Maksymchuk 
3437a2821584SLeonid Maksymchuk 	asus->throttle_thermal_policy_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
3438a2821584SLeonid Maksymchuk 	return throttle_thermal_policy_write(asus);
3439a2821584SLeonid Maksymchuk }
3440a2821584SLeonid Maksymchuk 
throttle_thermal_policy_switch_next(struct asus_wmi * asus)34412daa86e7SLeonid Maksymchuk static int throttle_thermal_policy_switch_next(struct asus_wmi *asus)
34422daa86e7SLeonid Maksymchuk {
34432daa86e7SLeonid Maksymchuk 	u8 new_mode = asus->throttle_thermal_policy_mode + 1;
3444c63d44aeSLuke D. Jones 	int err;
34452daa86e7SLeonid Maksymchuk 
34466051a4b1SMohamed Ghanmi 	if (new_mode > PLATFORM_PROFILE_MAX)
34472daa86e7SLeonid Maksymchuk 		new_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
34482daa86e7SLeonid Maksymchuk 
34492daa86e7SLeonid Maksymchuk 	asus->throttle_thermal_policy_mode = new_mode;
3450c63d44aeSLuke D. Jones 	err = throttle_thermal_policy_write(asus);
3451c63d44aeSLuke D. Jones 	if (err)
3452c63d44aeSLuke D. Jones 		return err;
3453c63d44aeSLuke D. Jones 
3454c63d44aeSLuke D. Jones 	/*
3455c63d44aeSLuke D. Jones 	 * Ensure that platform_profile updates userspace with the change to ensure
3456c63d44aeSLuke D. Jones 	 * that platform_profile and throttle_thermal_policy_mode are in sync.
3457c63d44aeSLuke D. Jones 	 */
3458c63d44aeSLuke D. Jones 	platform_profile_notify();
3459c63d44aeSLuke D. Jones 
3460c63d44aeSLuke D. Jones 	return 0;
34612daa86e7SLeonid Maksymchuk }
34622daa86e7SLeonid Maksymchuk 
throttle_thermal_policy_show(struct device * dev,struct device_attribute * attr,char * buf)34632daa86e7SLeonid Maksymchuk static ssize_t throttle_thermal_policy_show(struct device *dev,
34642daa86e7SLeonid Maksymchuk 				   struct device_attribute *attr, char *buf)
34652daa86e7SLeonid Maksymchuk {
34662daa86e7SLeonid Maksymchuk 	struct asus_wmi *asus = dev_get_drvdata(dev);
34672daa86e7SLeonid Maksymchuk 	u8 mode = asus->throttle_thermal_policy_mode;
34682daa86e7SLeonid Maksymchuk 
3469170f0da2SLuke D. Jones 	return sysfs_emit(buf, "%d\n", mode);
34702daa86e7SLeonid Maksymchuk }
34712daa86e7SLeonid Maksymchuk 
throttle_thermal_policy_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)34722daa86e7SLeonid Maksymchuk static ssize_t throttle_thermal_policy_store(struct device *dev,
34732daa86e7SLeonid Maksymchuk 				    struct device_attribute *attr,
34742daa86e7SLeonid Maksymchuk 				    const char *buf, size_t count)
34752daa86e7SLeonid Maksymchuk {
34762daa86e7SLeonid Maksymchuk 	struct asus_wmi *asus = dev_get_drvdata(dev);
3477c63d44aeSLuke D. Jones 	u8 new_mode;
3478c63d44aeSLuke D. Jones 	int result;
3479c63d44aeSLuke D. Jones 	int err;
34802daa86e7SLeonid Maksymchuk 
34812daa86e7SLeonid Maksymchuk 	result = kstrtou8(buf, 10, &new_mode);
34822daa86e7SLeonid Maksymchuk 	if (result < 0)
34832daa86e7SLeonid Maksymchuk 		return result;
34842daa86e7SLeonid Maksymchuk 
34856051a4b1SMohamed Ghanmi 	if (new_mode > PLATFORM_PROFILE_MAX)
34862daa86e7SLeonid Maksymchuk 		return -EINVAL;
34872daa86e7SLeonid Maksymchuk 
34882daa86e7SLeonid Maksymchuk 	asus->throttle_thermal_policy_mode = new_mode;
3489c63d44aeSLuke D. Jones 	err = throttle_thermal_policy_write(asus);
3490c63d44aeSLuke D. Jones 	if (err)
3491c63d44aeSLuke D. Jones 		return err;
3492c63d44aeSLuke D. Jones 
3493c63d44aeSLuke D. Jones 	/*
3494c63d44aeSLuke D. Jones 	 * Ensure that platform_profile updates userspace with the change to ensure
3495c63d44aeSLuke D. Jones 	 * that platform_profile and throttle_thermal_policy_mode are in sync.
3496c63d44aeSLuke D. Jones 	 */
3497c63d44aeSLuke D. Jones 	platform_profile_notify();
34982daa86e7SLeonid Maksymchuk 
34992daa86e7SLeonid Maksymchuk 	return count;
35002daa86e7SLeonid Maksymchuk }
35012daa86e7SLeonid Maksymchuk 
35026051a4b1SMohamed Ghanmi /*
35036051a4b1SMohamed Ghanmi  * Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent
35046051a4b1SMohamed Ghanmi  */
35052daa86e7SLeonid Maksymchuk static DEVICE_ATTR_RW(throttle_thermal_policy);
35062daa86e7SLeonid Maksymchuk 
3507c63d44aeSLuke D. Jones /* Platform profile ***********************************************************/
asus_wmi_platform_profile_get(struct platform_profile_handler * pprof,enum platform_profile_option * profile)35083aa539a5SMario Limonciello static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
3509c63d44aeSLuke D. Jones 					enum platform_profile_option *profile)
3510c63d44aeSLuke D. Jones {
3511c63d44aeSLuke D. Jones 	struct asus_wmi *asus;
3512c63d44aeSLuke D. Jones 	int tp;
3513c63d44aeSLuke D. Jones 
3514c63d44aeSLuke D. Jones 	asus = container_of(pprof, struct asus_wmi, platform_profile_handler);
3515c63d44aeSLuke D. Jones 	tp = asus->throttle_thermal_policy_mode;
3516c63d44aeSLuke D. Jones 
35175a4f732eSArmin Wolf 	switch (tp) {
3518c63d44aeSLuke D. Jones 	case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT:
3519c63d44aeSLuke D. Jones 		*profile = PLATFORM_PROFILE_BALANCED;
3520c63d44aeSLuke D. Jones 		break;
3521c63d44aeSLuke D. Jones 	case ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST:
3522c63d44aeSLuke D. Jones 		*profile = PLATFORM_PROFILE_PERFORMANCE;
3523c63d44aeSLuke D. Jones 		break;
3524c63d44aeSLuke D. Jones 	case ASUS_THROTTLE_THERMAL_POLICY_SILENT:
3525c63d44aeSLuke D. Jones 		*profile = PLATFORM_PROFILE_QUIET;
3526c63d44aeSLuke D. Jones 		break;
3527c63d44aeSLuke D. Jones 	default:
3528c63d44aeSLuke D. Jones 		return -EINVAL;
3529c63d44aeSLuke D. Jones 	}
3530c63d44aeSLuke D. Jones 
3531c63d44aeSLuke D. Jones 	return 0;
3532c63d44aeSLuke D. Jones }
3533c63d44aeSLuke D. Jones 
asus_wmi_platform_profile_set(struct platform_profile_handler * pprof,enum platform_profile_option profile)35343aa539a5SMario Limonciello static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof,
3535c63d44aeSLuke D. Jones 					enum platform_profile_option profile)
3536c63d44aeSLuke D. Jones {
3537c63d44aeSLuke D. Jones 	struct asus_wmi *asus;
3538c63d44aeSLuke D. Jones 	int tp;
3539c63d44aeSLuke D. Jones 
3540c63d44aeSLuke D. Jones 	asus = container_of(pprof, struct asus_wmi, platform_profile_handler);
3541c63d44aeSLuke D. Jones 
3542c63d44aeSLuke D. Jones 	switch (profile) {
3543c63d44aeSLuke D. Jones 	case PLATFORM_PROFILE_PERFORMANCE:
3544c63d44aeSLuke D. Jones 		tp = ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST;
3545c63d44aeSLuke D. Jones 		break;
3546c63d44aeSLuke D. Jones 	case PLATFORM_PROFILE_BALANCED:
3547c63d44aeSLuke D. Jones 		tp = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
3548c63d44aeSLuke D. Jones 		break;
3549c63d44aeSLuke D. Jones 	case PLATFORM_PROFILE_QUIET:
3550c63d44aeSLuke D. Jones 		tp = ASUS_THROTTLE_THERMAL_POLICY_SILENT;
3551c63d44aeSLuke D. Jones 		break;
3552c63d44aeSLuke D. Jones 	default:
3553c63d44aeSLuke D. Jones 		return -EOPNOTSUPP;
3554c63d44aeSLuke D. Jones 	}
3555c63d44aeSLuke D. Jones 
35565a4f732eSArmin Wolf 	asus->throttle_thermal_policy_mode = tp;
3557c63d44aeSLuke D. Jones 	return throttle_thermal_policy_write(asus);
3558c63d44aeSLuke D. Jones }
3559c63d44aeSLuke D. Jones 
platform_profile_setup(struct asus_wmi * asus)3560c63d44aeSLuke D. Jones static int platform_profile_setup(struct asus_wmi *asus)
3561c63d44aeSLuke D. Jones {
3562c63d44aeSLuke D. Jones 	struct device *dev = &asus->platform_device->dev;
3563c63d44aeSLuke D. Jones 	int err;
3564c63d44aeSLuke D. Jones 
3565c63d44aeSLuke D. Jones 	/*
3566c63d44aeSLuke D. Jones 	 * Not an error if a component platform_profile relies on is unavailable
3567c63d44aeSLuke D. Jones 	 * so early return, skipping the setup of platform_profile.
3568c63d44aeSLuke D. Jones 	 */
35696051a4b1SMohamed Ghanmi 	if (!asus->throttle_thermal_policy_dev)
3570c63d44aeSLuke D. Jones 		return 0;
3571c63d44aeSLuke D. Jones 
3572*5eb15d2fSArmin Wolf 	/*
3573*5eb15d2fSArmin Wolf 	 * We need to set the default thermal profile during probe or otherwise
3574*5eb15d2fSArmin Wolf 	 * the system will often remain in silent mode, causing low performance.
3575*5eb15d2fSArmin Wolf 	 */
3576*5eb15d2fSArmin Wolf 	err = throttle_thermal_policy_set_default(asus);
3577*5eb15d2fSArmin Wolf 	if (err < 0) {
3578*5eb15d2fSArmin Wolf 		pr_warn("Failed to set default thermal profile\n");
3579*5eb15d2fSArmin Wolf 		return err;
3580*5eb15d2fSArmin Wolf 	}
3581*5eb15d2fSArmin Wolf 
3582c63d44aeSLuke D. Jones 	dev_info(dev, "Using throttle_thermal_policy for platform_profile support\n");
3583c63d44aeSLuke D. Jones 
35843aa539a5SMario Limonciello 	asus->platform_profile_handler.profile_get = asus_wmi_platform_profile_get;
35853aa539a5SMario Limonciello 	asus->platform_profile_handler.profile_set = asus_wmi_platform_profile_set;
3586c63d44aeSLuke D. Jones 
3587c63d44aeSLuke D. Jones 	set_bit(PLATFORM_PROFILE_QUIET, asus->platform_profile_handler.choices);
3588c63d44aeSLuke D. Jones 	set_bit(PLATFORM_PROFILE_BALANCED,
3589c63d44aeSLuke D. Jones 		asus->platform_profile_handler.choices);
3590c63d44aeSLuke D. Jones 	set_bit(PLATFORM_PROFILE_PERFORMANCE,
3591c63d44aeSLuke D. Jones 		asus->platform_profile_handler.choices);
3592c63d44aeSLuke D. Jones 
3593c63d44aeSLuke D. Jones 	err = platform_profile_register(&asus->platform_profile_handler);
3594c63d44aeSLuke D. Jones 	if (err)
3595c63d44aeSLuke D. Jones 		return err;
3596c63d44aeSLuke D. Jones 
3597c63d44aeSLuke D. Jones 	asus->platform_profile_support = true;
3598c63d44aeSLuke D. Jones 	return 0;
3599c63d44aeSLuke D. Jones }
3600c63d44aeSLuke D. Jones 
360154a3121fSYurii Pavlovskyi /* Backlight ******************************************************************/
360254a3121fSYurii Pavlovskyi 
read_backlight_power(struct asus_wmi * asus)36031d070f89SCorentin Chary static int read_backlight_power(struct asus_wmi *asus)
36045b799d4fSCorentin Chary {
36056e0044beSAceLan Kao 	int ret;
3606109e8adfSAndy Shevchenko 
36076e0044beSAceLan Kao 	if (asus->driver->quirks->store_backlight_power)
36086e0044beSAceLan Kao 		ret = !asus->driver->panel_power;
36096e0044beSAceLan Kao 	else
36106e0044beSAceLan Kao 		ret = asus_wmi_get_devstate_simple(asus,
36116e0044beSAceLan Kao 						   ASUS_WMI_DEVID_BACKLIGHT);
36125b799d4fSCorentin Chary 
36135b799d4fSCorentin Chary 	if (ret < 0)
36145b799d4fSCorentin Chary 		return ret;
36155b799d4fSCorentin Chary 
36165b799d4fSCorentin Chary 	return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
36175b799d4fSCorentin Chary }
36185b799d4fSCorentin Chary 
read_brightness_max(struct asus_wmi * asus)36198fbea019SCorentin Chary static int read_brightness_max(struct asus_wmi *asus)
36208fbea019SCorentin Chary {
36218fbea019SCorentin Chary 	u32 retval;
36228fbea019SCorentin Chary 	int err;
36238fbea019SCorentin Chary 
36248fbea019SCorentin Chary 	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval);
36258fbea019SCorentin Chary 	if (err < 0)
36268fbea019SCorentin Chary 		return err;
36278fbea019SCorentin Chary 
36288fbea019SCorentin Chary 	retval = retval & ASUS_WMI_DSTS_MAX_BRIGTH_MASK;
36298fbea019SCorentin Chary 	retval >>= 8;
36308fbea019SCorentin Chary 
36318fbea019SCorentin Chary 	if (!retval)
36328fbea019SCorentin Chary 		return -ENODEV;
36338fbea019SCorentin Chary 
36348fbea019SCorentin Chary 	return retval;
36358fbea019SCorentin Chary }
36368fbea019SCorentin Chary 
read_brightness(struct backlight_device * bd)36375b799d4fSCorentin Chary static int read_brightness(struct backlight_device *bd)
36385b799d4fSCorentin Chary {
36391d070f89SCorentin Chary 	struct asus_wmi *asus = bl_get_data(bd);
36400986f25fSDan Carpenter 	u32 retval;
36410986f25fSDan Carpenter 	int err;
36425b799d4fSCorentin Chary 
36431d070f89SCorentin Chary 	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval);
3644d33da3b6SCorentin Chary 	if (err < 0)
3645d33da3b6SCorentin Chary 		return err;
3646d33da3b6SCorentin Chary 
3647e12e6d94SCorentin Chary 	return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
36485b799d4fSCorentin Chary }
36495b799d4fSCorentin Chary 
get_scalar_command(struct backlight_device * bd)3650c87992d1SAceLan Kao static u32 get_scalar_command(struct backlight_device *bd)
3651c87992d1SAceLan Kao {
3652c87992d1SAceLan Kao 	struct asus_wmi *asus = bl_get_data(bd);
3653c87992d1SAceLan Kao 	u32 ctrl_param = 0;
3654c87992d1SAceLan Kao 
3655c87992d1SAceLan Kao 	if ((asus->driver->brightness < bd->props.brightness) ||
3656c87992d1SAceLan Kao 	    bd->props.brightness == bd->props.max_brightness)
3657c87992d1SAceLan Kao 		ctrl_param = 0x00008001;
3658c87992d1SAceLan Kao 	else if ((asus->driver->brightness > bd->props.brightness) ||
3659c87992d1SAceLan Kao 		 bd->props.brightness == 0)
3660c87992d1SAceLan Kao 		ctrl_param = 0x00008000;
3661c87992d1SAceLan Kao 
3662c87992d1SAceLan Kao 	asus->driver->brightness = bd->props.brightness;
3663c87992d1SAceLan Kao 
3664c87992d1SAceLan Kao 	return ctrl_param;
3665c87992d1SAceLan Kao }
3666c87992d1SAceLan Kao 
update_bl_status(struct backlight_device * bd)36675b799d4fSCorentin Chary static int update_bl_status(struct backlight_device *bd)
36685b799d4fSCorentin Chary {
36691d070f89SCorentin Chary 	struct asus_wmi *asus = bl_get_data(bd);
36705b799d4fSCorentin Chary 	u32 ctrl_param;
36716e0044beSAceLan Kao 	int power, err = 0;
36725b799d4fSCorentin Chary 
36731d070f89SCorentin Chary 	power = read_backlight_power(asus);
36745b799d4fSCorentin Chary 	if (power != -ENODEV && bd->props.power != power) {
36755b799d4fSCorentin Chary 		ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK);
3676d33da3b6SCorentin Chary 		err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT,
36775b799d4fSCorentin Chary 					    ctrl_param, NULL);
36786e0044beSAceLan Kao 		if (asus->driver->quirks->store_backlight_power)
36796e0044beSAceLan Kao 			asus->driver->panel_power = bd->props.power;
3680ade28abdSCorentin Chary 
3681ade28abdSCorentin Chary 		/* When using scalar brightness, updating the brightness
3682ade28abdSCorentin Chary 		 * will mess with the backlight power */
3683ade28abdSCorentin Chary 		if (asus->driver->quirks->scalar_panel_brightness)
3684ade28abdSCorentin Chary 			return err;
36855b799d4fSCorentin Chary 	}
3686ade28abdSCorentin Chary 
3687c87992d1SAceLan Kao 	if (asus->driver->quirks->scalar_panel_brightness)
3688c87992d1SAceLan Kao 		ctrl_param = get_scalar_command(bd);
3689c87992d1SAceLan Kao 	else
36905b799d4fSCorentin Chary 		ctrl_param = bd->props.brightness;
36915b799d4fSCorentin Chary 
36925b799d4fSCorentin Chary 	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS,
36935b799d4fSCorentin Chary 				    ctrl_param, NULL);
3694ade28abdSCorentin Chary 
36958fbea019SCorentin Chary 	return err;
36965b799d4fSCorentin Chary }
36975b799d4fSCorentin Chary 
3698e12e6d94SCorentin Chary static const struct backlight_ops asus_wmi_bl_ops = {
36995b799d4fSCorentin Chary 	.get_brightness = read_brightness,
37005b799d4fSCorentin Chary 	.update_status = update_bl_status,
37015b799d4fSCorentin Chary };
37025b799d4fSCorentin Chary 
asus_wmi_backlight_notify(struct asus_wmi * asus,int code)3703e12e6d94SCorentin Chary static int asus_wmi_backlight_notify(struct asus_wmi *asus, int code)
37045b799d4fSCorentin Chary {
3705e12e6d94SCorentin Chary 	struct backlight_device *bd = asus->backlight_device;
37065b799d4fSCorentin Chary 	int old = bd->props.brightness;
37075b799d4fSCorentin Chary 	int new = old;
37085b799d4fSCorentin Chary 
37095b799d4fSCorentin Chary 	if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
37105b799d4fSCorentin Chary 		new = code - NOTIFY_BRNUP_MIN + 1;
37115b799d4fSCorentin Chary 	else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
37125b799d4fSCorentin Chary 		new = code - NOTIFY_BRNDOWN_MIN;
37135b799d4fSCorentin Chary 
37145b799d4fSCorentin Chary 	bd->props.brightness = new;
37155b799d4fSCorentin Chary 	backlight_update_status(bd);
37165b799d4fSCorentin Chary 	backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
37175b799d4fSCorentin Chary 
37185b799d4fSCorentin Chary 	return old;
37195b799d4fSCorentin Chary }
37205b799d4fSCorentin Chary 
asus_wmi_backlight_init(struct asus_wmi * asus)3721e12e6d94SCorentin Chary static int asus_wmi_backlight_init(struct asus_wmi *asus)
37225b799d4fSCorentin Chary {
37235b799d4fSCorentin Chary 	struct backlight_device *bd;
37245b799d4fSCorentin Chary 	struct backlight_properties props;
37255b799d4fSCorentin Chary 	int max;
37265b799d4fSCorentin Chary 	int power;
37275b799d4fSCorentin Chary 
37288fbea019SCorentin Chary 	max = read_brightness_max(asus);
372986ac2735SHans de Goede 	if (max < 0)
37308fbea019SCorentin Chary 		return max;
37318fbea019SCorentin Chary 
37328fbea019SCorentin Chary 	power = read_backlight_power(asus);
37335b799d4fSCorentin Chary 	if (power == -ENODEV)
37345b799d4fSCorentin Chary 		power = FB_BLANK_UNBLANK;
37358fbea019SCorentin Chary 	else if (power < 0)
37368fbea019SCorentin Chary 		return power;
37375b799d4fSCorentin Chary 
37385b799d4fSCorentin Chary 	memset(&props, 0, sizeof(struct backlight_properties));
373960cfa098SAxel Lin 	props.type = BACKLIGHT_PLATFORM;
37405b799d4fSCorentin Chary 	props.max_brightness = max;
3741e12e6d94SCorentin Chary 	bd = backlight_device_register(asus->driver->name,
3742e12e6d94SCorentin Chary 				       &asus->platform_device->dev, asus,
3743e12e6d94SCorentin Chary 				       &asus_wmi_bl_ops, &props);
37445b799d4fSCorentin Chary 	if (IS_ERR(bd)) {
37455b799d4fSCorentin Chary 		pr_err("Could not register backlight device\n");
37465b799d4fSCorentin Chary 		return PTR_ERR(bd);
37475b799d4fSCorentin Chary 	}
37485b799d4fSCorentin Chary 
3749e12e6d94SCorentin Chary 	asus->backlight_device = bd;
37505b799d4fSCorentin Chary 
37516e0044beSAceLan Kao 	if (asus->driver->quirks->store_backlight_power)
37526e0044beSAceLan Kao 		asus->driver->panel_power = power;
37536e0044beSAceLan Kao 
37545b799d4fSCorentin Chary 	bd->props.brightness = read_brightness(bd);
37555b799d4fSCorentin Chary 	bd->props.power = power;
37565b799d4fSCorentin Chary 	backlight_update_status(bd);
37575b799d4fSCorentin Chary 
3758c87992d1SAceLan Kao 	asus->driver->brightness = bd->props.brightness;
3759c87992d1SAceLan Kao 
37605b799d4fSCorentin Chary 	return 0;
37615b799d4fSCorentin Chary }
37625b799d4fSCorentin Chary 
asus_wmi_backlight_exit(struct asus_wmi * asus)3763e12e6d94SCorentin Chary static void asus_wmi_backlight_exit(struct asus_wmi *asus)
37645b799d4fSCorentin Chary {
3765e12e6d94SCorentin Chary 	backlight_device_unregister(asus->backlight_device);
37665b799d4fSCorentin Chary 
3767e12e6d94SCorentin Chary 	asus->backlight_device = NULL;
37685b799d4fSCorentin Chary }
37695b799d4fSCorentin Chary 
is_display_toggle(int code)3770a2a96f0cSAceLan Kao static int is_display_toggle(int code)
3771a2a96f0cSAceLan Kao {
3772a2a96f0cSAceLan Kao 	/* display toggle keys */
3773a2a96f0cSAceLan Kao 	if ((code >= 0x61 && code <= 0x67) ||
3774a2a96f0cSAceLan Kao 	    (code >= 0x8c && code <= 0x93) ||
3775a2a96f0cSAceLan Kao 	    (code >= 0xa0 && code <= 0xa7) ||
3776a2a96f0cSAceLan Kao 	    (code >= 0xd0 && code <= 0xd5))
3777a2a96f0cSAceLan Kao 		return 1;
3778a2a96f0cSAceLan Kao 
3779a2a96f0cSAceLan Kao 	return 0;
3780a2a96f0cSAceLan Kao }
3781a2a96f0cSAceLan Kao 
378254a3121fSYurii Pavlovskyi /* Fn-lock ********************************************************************/
378354a3121fSYurii Pavlovskyi 
asus_wmi_has_fnlock_key(struct asus_wmi * asus)3784487579baSChris Chiu static bool asus_wmi_has_fnlock_key(struct asus_wmi *asus)
3785487579baSChris Chiu {
3786487579baSChris Chiu 	u32 result;
3787487579baSChris Chiu 
3788487579baSChris Chiu 	asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FNLOCK, &result);
3789487579baSChris Chiu 
3790487579baSChris Chiu 	return (result & ASUS_WMI_DSTS_PRESENCE_BIT) &&
3791487579baSChris Chiu 		!(result & ASUS_WMI_FNLOCK_BIOS_DISABLED);
3792487579baSChris Chiu }
3793487579baSChris Chiu 
asus_wmi_fnlock_update(struct asus_wmi * asus)3794487579baSChris Chiu static void asus_wmi_fnlock_update(struct asus_wmi *asus)
3795487579baSChris Chiu {
3796487579baSChris Chiu 	int mode = asus->fnlock_locked;
3797487579baSChris Chiu 
3798487579baSChris Chiu 	asus_wmi_set_devstate(ASUS_WMI_DEVID_FNLOCK, mode, NULL);
3799487579baSChris Chiu }
3800487579baSChris Chiu 
380154a3121fSYurii Pavlovskyi /* WMI events *****************************************************************/
380254a3121fSYurii Pavlovskyi 
asus_wmi_get_event_code(u32 value)38038abd752bSYurii Pavlovskyi static int asus_wmi_get_event_code(u32 value)
38045b799d4fSCorentin Chary {
38055b799d4fSCorentin Chary 	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
38065b799d4fSCorentin Chary 	union acpi_object *obj;
38075b799d4fSCorentin Chary 	acpi_status status;
38085b799d4fSCorentin Chary 	int code;
38095b799d4fSCorentin Chary 
38105b799d4fSCorentin Chary 	status = wmi_get_event_data(value, &response);
38118abd752bSYurii Pavlovskyi 	if (ACPI_FAILURE(status)) {
38128abd752bSYurii Pavlovskyi 		pr_warn("Failed to get WMI notify code: %s\n",
38138abd752bSYurii Pavlovskyi 				acpi_format_exception(status));
38148abd752bSYurii Pavlovskyi 		return -EIO;
38155b799d4fSCorentin Chary 	}
38165b799d4fSCorentin Chary 
38175b799d4fSCorentin Chary 	obj = (union acpi_object *)response.pointer;
38185b799d4fSCorentin Chary 
38198abd752bSYurii Pavlovskyi 	if (obj && obj->type == ACPI_TYPE_INTEGER)
38208abd752bSYurii Pavlovskyi 		code = (int)(obj->integer.value & WMI_EVENT_MASK);
38218abd752bSYurii Pavlovskyi 	else
38228abd752bSYurii Pavlovskyi 		code = -EIO;
382357ab7daeSCorentin Chary 
38248abd752bSYurii Pavlovskyi 	kfree(obj);
38258abd752bSYurii Pavlovskyi 	return code;
38268abd752bSYurii Pavlovskyi }
38278abd752bSYurii Pavlovskyi 
asus_wmi_handle_event_code(int code,struct asus_wmi * asus)38288abd752bSYurii Pavlovskyi static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
38298abd752bSYurii Pavlovskyi {
38308abd752bSYurii Pavlovskyi 	unsigned int key_value = 1;
38318abd752bSYurii Pavlovskyi 	bool autorelease = 1;
38325b799d4fSCorentin Chary 
3833c4453f6aSSeth Forshee 	if (asus->driver->key_filter) {
3834c4453f6aSSeth Forshee 		asus->driver->key_filter(asus->driver, &code, &key_value,
3835c4453f6aSSeth Forshee 					 &autorelease);
3836c4453f6aSSeth Forshee 		if (code == ASUS_WMI_KEY_IGNORE)
38378abd752bSYurii Pavlovskyi 			return;
3838c4453f6aSSeth Forshee 	}
3839c4453f6aSSeth Forshee 
3840a5b92be2SHans de Goede 	if (acpi_video_get_backlight_type() == acpi_backlight_vendor &&
3841a5b92be2SHans de Goede 	    code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNDOWN_MAX) {
3842a5b92be2SHans de Goede 		asus_wmi_backlight_notify(asus, code);
38438abd752bSYurii Pavlovskyi 		return;
3844a2a96f0cSAceLan Kao 	}
3845a2a96f0cSAceLan Kao 
3846dbb3d78fSChris Chiu 	if (code == NOTIFY_KBD_BRTUP) {
384729f6eb53SJian-Hong Pan 		kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
38488abd752bSYurii Pavlovskyi 		return;
3849dbb3d78fSChris Chiu 	}
3850dbb3d78fSChris Chiu 	if (code == NOTIFY_KBD_BRTDWN) {
385129f6eb53SJian-Hong Pan 		kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1);
38528abd752bSYurii Pavlovskyi 		return;
3853dbb3d78fSChris Chiu 	}
3854ed99d29bSChris Chiu 	if (code == NOTIFY_KBD_BRTTOGGLE) {
3855ed99d29bSChris Chiu 		if (asus->kbd_led_wk == asus->kbd_led.max_brightness)
385629f6eb53SJian-Hong Pan 			kbd_led_set_by_kbd(asus, 0);
3857ed99d29bSChris Chiu 		else
385829f6eb53SJian-Hong Pan 			kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
38598abd752bSYurii Pavlovskyi 		return;
3860ed99d29bSChris Chiu 	}
3861dbb3d78fSChris Chiu 
3862487579baSChris Chiu 	if (code == NOTIFY_FNLOCK_TOGGLE) {
3863487579baSChris Chiu 		asus->fnlock_locked = !asus->fnlock_locked;
3864487579baSChris Chiu 		asus_wmi_fnlock_update(asus);
38658abd752bSYurii Pavlovskyi 		return;
3866487579baSChris Chiu 	}
3867487579baSChris Chiu 
38681ea0d3b4SHans de Goede 	if (code == asus->tablet_switch_event_code) {
38691ea0d3b4SHans de Goede 		asus_wmi_tablet_mode_get_state(asus);
3870e397c3c4SLuke D. Jones 		return;
3871e397c3c4SLuke D. Jones 	}
3872e397c3c4SLuke D. Jones 
3873601eb4c8SLuke D. Jones 	if (code == NOTIFY_KBD_FBM || code == NOTIFY_KBD_TTP) {
3874601eb4c8SLuke D. Jones 		if (asus->fan_boost_mode_available)
38759af93db9SDaniel Drake 			fan_boost_mode_switch_next(asus);
38766051a4b1SMohamed Ghanmi 		if (asus->throttle_thermal_policy_dev)
38772daa86e7SLeonid Maksymchuk 			throttle_thermal_policy_switch_next(asus);
38782daa86e7SLeonid Maksymchuk 		return;
3879601eb4c8SLuke D. Jones 
38802daa86e7SLeonid Maksymchuk 	}
38812daa86e7SLeonid Maksymchuk 
38828abd752bSYurii Pavlovskyi 	if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle)
38838abd752bSYurii Pavlovskyi 		return;
3884a2a96f0cSAceLan Kao 
3885a2a96f0cSAceLan Kao 	if (!sparse_keymap_report_event(asus->inputdev, code,
3886c4453f6aSSeth Forshee 					key_value, autorelease))
38873e70a57bSLuca Stefani 		pr_info("Unknown key code 0x%x\n", code);
38885b799d4fSCorentin Chary }
38895b799d4fSCorentin Chary 
asus_wmi_notify(u32 value,void * context)38908abd752bSYurii Pavlovskyi static void asus_wmi_notify(u32 value, void *context)
38918abd752bSYurii Pavlovskyi {
38928abd752bSYurii Pavlovskyi 	struct asus_wmi *asus = context;
38931a373d15SYurii Pavlovskyi 	int code;
38941a373d15SYurii Pavlovskyi 	int i;
38951a373d15SYurii Pavlovskyi 
38961a373d15SYurii Pavlovskyi 	for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
38971a373d15SYurii Pavlovskyi 		code = asus_wmi_get_event_code(value);
38988abd752bSYurii Pavlovskyi 		if (code < 0) {
38998abd752bSYurii Pavlovskyi 			pr_warn("Failed to get notify code: %d\n", code);
39008abd752bSYurii Pavlovskyi 			return;
39018abd752bSYurii Pavlovskyi 		}
39028abd752bSYurii Pavlovskyi 
39031a373d15SYurii Pavlovskyi 		if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
39041a373d15SYurii Pavlovskyi 			return;
39051a373d15SYurii Pavlovskyi 
39068abd752bSYurii Pavlovskyi 		asus_wmi_handle_event_code(code, asus);
39071a373d15SYurii Pavlovskyi 
39085b799d4fSCorentin Chary 		/*
39091a373d15SYurii Pavlovskyi 		 * Double check that queue is present:
39101a373d15SYurii Pavlovskyi 		 * ATK (with queue) uses 0xff, ASUSWMI (without) 0xd2.
39115b799d4fSCorentin Chary 		 */
39121a373d15SYurii Pavlovskyi 		if (!asus->wmi_event_queue || value != WMI_EVENT_VALUE_ATK)
39131a373d15SYurii Pavlovskyi 			return;
39141a373d15SYurii Pavlovskyi 	}
39151a373d15SYurii Pavlovskyi 
39161a373d15SYurii Pavlovskyi 	pr_warn("Failed to process event queue, last code: 0x%x\n", code);
39171a373d15SYurii Pavlovskyi }
39181a373d15SYurii Pavlovskyi 
asus_wmi_notify_queue_flush(struct asus_wmi * asus)39191a373d15SYurii Pavlovskyi static int asus_wmi_notify_queue_flush(struct asus_wmi *asus)
39201a373d15SYurii Pavlovskyi {
39211a373d15SYurii Pavlovskyi 	int code;
39221a373d15SYurii Pavlovskyi 	int i;
39231a373d15SYurii Pavlovskyi 
39241a373d15SYurii Pavlovskyi 	for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
39251a373d15SYurii Pavlovskyi 		code = asus_wmi_get_event_code(WMI_EVENT_VALUE_ATK);
39261a373d15SYurii Pavlovskyi 		if (code < 0) {
39271a373d15SYurii Pavlovskyi 			pr_warn("Failed to get event during flush: %d\n", code);
39281a373d15SYurii Pavlovskyi 			return code;
39291a373d15SYurii Pavlovskyi 		}
39301a373d15SYurii Pavlovskyi 
39311a373d15SYurii Pavlovskyi 		if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
39321a373d15SYurii Pavlovskyi 			return 0;
39331a373d15SYurii Pavlovskyi 	}
39341a373d15SYurii Pavlovskyi 
39351a373d15SYurii Pavlovskyi 	pr_warn("Failed to flush event queue\n");
39361a373d15SYurii Pavlovskyi 	return -EIO;
39375b799d4fSCorentin Chary }
39385b799d4fSCorentin Chary 
393954a3121fSYurii Pavlovskyi /* Sysfs **********************************************************************/
394054a3121fSYurii Pavlovskyi 
store_sys_wmi(struct asus_wmi * asus,int devid,const char * buf,size_t count)39411d070f89SCorentin Chary static ssize_t store_sys_wmi(struct asus_wmi *asus, int devid,
39421d070f89SCorentin Chary 			     const char *buf, size_t count)
39435b799d4fSCorentin Chary {
39445b799d4fSCorentin Chary 	u32 retval;
3945a5556fa1SAndy Shevchenko 	int err, value;
39465b799d4fSCorentin Chary 
39471d070f89SCorentin Chary 	value = asus_wmi_get_devstate_simple(asus, devid);
3948b8298340SDan Carpenter 	if (value < 0)
39495b799d4fSCorentin Chary 		return value;
39505b799d4fSCorentin Chary 
3951a5556fa1SAndy Shevchenko 	err = kstrtoint(buf, 0, &value);
3952a5556fa1SAndy Shevchenko 	if (err)
3953a5556fa1SAndy Shevchenko 		return err;
39545b799d4fSCorentin Chary 
3955a5556fa1SAndy Shevchenko 	err = asus_wmi_set_devstate(devid, value, &retval);
3956d33da3b6SCorentin Chary 	if (err < 0)
3957d33da3b6SCorentin Chary 		return err;
3958d33da3b6SCorentin Chary 
3959a5556fa1SAndy Shevchenko 	return count;
39605b799d4fSCorentin Chary }
39615b799d4fSCorentin Chary 
show_sys_wmi(struct asus_wmi * asus,int devid,char * buf)39621d070f89SCorentin Chary static ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf)
39635b799d4fSCorentin Chary {
39641d070f89SCorentin Chary 	int value = asus_wmi_get_devstate_simple(asus, devid);
39655b799d4fSCorentin Chary 
39665b799d4fSCorentin Chary 	if (value < 0)
39675b799d4fSCorentin Chary 		return value;
39685b799d4fSCorentin Chary 
39695b799d4fSCorentin Chary 	return sprintf(buf, "%d\n", value);
39705b799d4fSCorentin Chary }
39715b799d4fSCorentin Chary 
3972e12e6d94SCorentin Chary #define ASUS_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm)			\
39735b799d4fSCorentin Chary 	static ssize_t show_##_name(struct device *dev,			\
39745b799d4fSCorentin Chary 				    struct device_attribute *attr,	\
39755b799d4fSCorentin Chary 				    char *buf)				\
39765b799d4fSCorentin Chary 	{								\
39771d070f89SCorentin Chary 		struct asus_wmi *asus = dev_get_drvdata(dev);		\
39781d070f89SCorentin Chary 									\
39791d070f89SCorentin Chary 		return show_sys_wmi(asus, _cm, buf);			\
39805b799d4fSCorentin Chary 	}								\
39815b799d4fSCorentin Chary 	static ssize_t store_##_name(struct device *dev,		\
39825b799d4fSCorentin Chary 				     struct device_attribute *attr,	\
39835b799d4fSCorentin Chary 				     const char *buf, size_t count)	\
39845b799d4fSCorentin Chary 	{								\
39851d070f89SCorentin Chary 		struct asus_wmi *asus = dev_get_drvdata(dev);		\
39861d070f89SCorentin Chary 									\
39871d070f89SCorentin Chary 		return store_sys_wmi(asus, _cm, buf, count);		\
39885b799d4fSCorentin Chary 	}								\
39895b799d4fSCorentin Chary 	static struct device_attribute dev_attr_##_name = {		\
39905b799d4fSCorentin Chary 		.attr = {						\
39915b799d4fSCorentin Chary 			.name = __stringify(_name),			\
39925b799d4fSCorentin Chary 			.mode = _mode },				\
39935b799d4fSCorentin Chary 		.show   = show_##_name,					\
39945b799d4fSCorentin Chary 		.store  = store_##_name,				\
39955b799d4fSCorentin Chary 	}
39965b799d4fSCorentin Chary 
3997e12e6d94SCorentin Chary ASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD);
3998e12e6d94SCorentin Chary ASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA);
3999e12e6d94SCorentin Chary ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER);
4000c0b91b6dSAceLan Kao ASUS_WMI_CREATE_DEVICE_ATTR(lid_resume, 0644, ASUS_WMI_DEVID_LID_RESUME);
4001aca234f6SOleksij Rempel ASUS_WMI_CREATE_DEVICE_ATTR(als_enable, 0644, ASUS_WMI_DEVID_ALS_ENABLE);
40025b799d4fSCorentin Chary 
cpufv_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)4003a8fe3428SJérémy Lefaure static ssize_t cpufv_store(struct device *dev, struct device_attribute *attr,
40045b799d4fSCorentin Chary 			   const char *buf, size_t count)
40055b799d4fSCorentin Chary {
40063df5fdadSCorentin Chary 	int value, rv;
40075b799d4fSCorentin Chary 
4008a5556fa1SAndy Shevchenko 	rv = kstrtoint(buf, 0, &value);
4009a5556fa1SAndy Shevchenko 	if (rv)
4010a5556fa1SAndy Shevchenko 		return rv;
4011a5556fa1SAndy Shevchenko 
40125b799d4fSCorentin Chary 	if (value < 0 || value > 2)
40135b799d4fSCorentin Chary 		return -EINVAL;
40145b799d4fSCorentin Chary 
40153df5fdadSCorentin Chary 	rv = asus_wmi_evaluate_method(ASUS_WMI_METHODID_CFVS, value, 0, NULL);
40163df5fdadSCorentin Chary 	if (rv < 0)
40173df5fdadSCorentin Chary 		return rv;
40183df5fdadSCorentin Chary 
40193df5fdadSCorentin Chary 	return count;
40205b799d4fSCorentin Chary }
40215b799d4fSCorentin Chary 
4022a8fe3428SJérémy Lefaure static DEVICE_ATTR_WO(cpufv);
40235b799d4fSCorentin Chary 
40245b799d4fSCorentin Chary static struct attribute *platform_attributes[] = {
40255b799d4fSCorentin Chary 	&dev_attr_cpufv.attr,
40265b799d4fSCorentin Chary 	&dev_attr_camera.attr,
40275b799d4fSCorentin Chary 	&dev_attr_cardr.attr,
40285b799d4fSCorentin Chary 	&dev_attr_touchpad.attr,
402977ee9d29SLuke D. Jones 	&dev_attr_charge_mode.attr,
4030382b91dbSLuke D. Jones 	&dev_attr_egpu_enable.attr,
4031d4eca58aSLuke D. Jones 	&dev_attr_egpu_connected.attr,
403298829e84SLuke D. Jones 	&dev_attr_dgpu_disable.attr,
403301ef026aSLuke D. Jones 	&dev_attr_gpu_mux_mode.attr,
4034c0b91b6dSAceLan Kao 	&dev_attr_lid_resume.attr,
4035aca234f6SOleksij Rempel 	&dev_attr_als_enable.attr,
40369af93db9SDaniel Drake 	&dev_attr_fan_boost_mode.attr,
40372daa86e7SLeonid Maksymchuk 	&dev_attr_throttle_thermal_policy.attr,
4038e0b278e7SLuke D. Jones 	&dev_attr_ppt_pl2_sppt.attr,
4039e0b278e7SLuke D. Jones 	&dev_attr_ppt_pl1_spl.attr,
4040e0b278e7SLuke D. Jones 	&dev_attr_ppt_fppt.attr,
4041e0b278e7SLuke D. Jones 	&dev_attr_ppt_apu_sppt.attr,
4042e0b278e7SLuke D. Jones 	&dev_attr_ppt_platform_sppt.attr,
4043e0b278e7SLuke D. Jones 	&dev_attr_nv_dynamic_boost.attr,
4044e0b278e7SLuke D. Jones 	&dev_attr_nv_temp_target.attr,
4045ca91ea34SLuke D. Jones 	&dev_attr_panel_od.attr,
4046abac4259SLuke D. Jones 	&dev_attr_mini_led_mode.attr,
40475b799d4fSCorentin Chary 	NULL
40485b799d4fSCorentin Chary };
40495b799d4fSCorentin Chary 
asus_sysfs_is_visible(struct kobject * kobj,struct attribute * attr,int idx)4050587a1f16SAl Viro static umode_t asus_sysfs_is_visible(struct kobject *kobj,
4051e12e6d94SCorentin Chary 				    struct attribute *attr, int idx)
40525b799d4fSCorentin Chary {
4053a8f9c36cSye xingchen 	struct device *dev = kobj_to_dev(kobj);
4054d605ca29SWolfram Sang 	struct asus_wmi *asus = dev_get_drvdata(dev);
40551d070f89SCorentin Chary 	bool ok = true;
40565b799d4fSCorentin Chary 	int devid = -1;
40575b799d4fSCorentin Chary 
40585b799d4fSCorentin Chary 	if (attr == &dev_attr_camera.attr)
4059e12e6d94SCorentin Chary 		devid = ASUS_WMI_DEVID_CAMERA;
40605b799d4fSCorentin Chary 	else if (attr == &dev_attr_cardr.attr)
4061e12e6d94SCorentin Chary 		devid = ASUS_WMI_DEVID_CARDREADER;
40625b799d4fSCorentin Chary 	else if (attr == &dev_attr_touchpad.attr)
4063e12e6d94SCorentin Chary 		devid = ASUS_WMI_DEVID_TOUCHPAD;
4064c0b91b6dSAceLan Kao 	else if (attr == &dev_attr_lid_resume.attr)
4065c0b91b6dSAceLan Kao 		devid = ASUS_WMI_DEVID_LID_RESUME;
4066aca234f6SOleksij Rempel 	else if (attr == &dev_attr_als_enable.attr)
4067aca234f6SOleksij Rempel 		devid = ASUS_WMI_DEVID_ALS_ENABLE;
406877ee9d29SLuke D. Jones 	else if (attr == &dev_attr_charge_mode.attr)
406977ee9d29SLuke D. Jones 		ok = asus->charge_mode_available;
4070382b91dbSLuke D. Jones 	else if (attr == &dev_attr_egpu_enable.attr)
4071382b91dbSLuke D. Jones 		ok = asus->egpu_enable_available;
4072d4eca58aSLuke D. Jones 	else if (attr == &dev_attr_egpu_connected.attr)
4073d4eca58aSLuke D. Jones 		ok = asus->egpu_connect_available;
407498829e84SLuke D. Jones 	else if (attr == &dev_attr_dgpu_disable.attr)
407598829e84SLuke D. Jones 		ok = asus->dgpu_disable_available;
407601ef026aSLuke D. Jones 	else if (attr == &dev_attr_gpu_mux_mode.attr)
407701ef026aSLuke D. Jones 		ok = asus->gpu_mux_mode_available;
40789af93db9SDaniel Drake 	else if (attr == &dev_attr_fan_boost_mode.attr)
40799af93db9SDaniel Drake 		ok = asus->fan_boost_mode_available;
40802daa86e7SLeonid Maksymchuk 	else if (attr == &dev_attr_throttle_thermal_policy.attr)
40816051a4b1SMohamed Ghanmi 		ok = asus->throttle_thermal_policy_dev != 0;
4082e0b278e7SLuke D. Jones 	else if (attr == &dev_attr_ppt_pl2_sppt.attr)
4083e0b278e7SLuke D. Jones 		ok = asus->ppt_pl2_sppt_available;
4084e0b278e7SLuke D. Jones 	else if (attr == &dev_attr_ppt_pl1_spl.attr)
4085e0b278e7SLuke D. Jones 		ok = asus->ppt_pl1_spl_available;
4086e0b278e7SLuke D. Jones 	else if (attr == &dev_attr_ppt_fppt.attr)
4087e0b278e7SLuke D. Jones 		ok = asus->ppt_fppt_available;
4088e0b278e7SLuke D. Jones 	else if (attr == &dev_attr_ppt_apu_sppt.attr)
4089e0b278e7SLuke D. Jones 		ok = asus->ppt_apu_sppt_available;
4090e0b278e7SLuke D. Jones 	else if (attr == &dev_attr_ppt_platform_sppt.attr)
4091e0b278e7SLuke D. Jones 		ok = asus->ppt_plat_sppt_available;
4092e0b278e7SLuke D. Jones 	else if (attr == &dev_attr_nv_dynamic_boost.attr)
4093e0b278e7SLuke D. Jones 		ok = asus->nv_dyn_boost_available;
4094e0b278e7SLuke D. Jones 	else if (attr == &dev_attr_nv_temp_target.attr)
4095e0b278e7SLuke D. Jones 		ok = asus->nv_temp_tgt_available;
4096ca91ea34SLuke D. Jones 	else if (attr == &dev_attr_panel_od.attr)
4097ca91ea34SLuke D. Jones 		ok = asus->panel_overdrive_available;
4098abac4259SLuke D. Jones 	else if (attr == &dev_attr_mini_led_mode.attr)
4099abac4259SLuke D. Jones 		ok = asus->mini_led_mode_available;
41005b799d4fSCorentin Chary 
41015b799d4fSCorentin Chary 	if (devid != -1)
41021d070f89SCorentin Chary 		ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
41035b799d4fSCorentin Chary 
41041d070f89SCorentin Chary 	return ok ? attr->mode : 0;
41055b799d4fSCorentin Chary }
41065b799d4fSCorentin Chary 
4107e90d9ba8SArvind Yadav static const struct attribute_group platform_attribute_group = {
4108e12e6d94SCorentin Chary 	.is_visible = asus_sysfs_is_visible,
41095b799d4fSCorentin Chary 	.attrs = platform_attributes
41105b799d4fSCorentin Chary };
41115b799d4fSCorentin Chary 
asus_wmi_sysfs_exit(struct platform_device * device)4112e12e6d94SCorentin Chary static void asus_wmi_sysfs_exit(struct platform_device *device)
41135b799d4fSCorentin Chary {
41145b799d4fSCorentin Chary 	sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
41155b799d4fSCorentin Chary }
41165b799d4fSCorentin Chary 
asus_wmi_sysfs_init(struct platform_device * device)4117e12e6d94SCorentin Chary static int asus_wmi_sysfs_init(struct platform_device *device)
41185b799d4fSCorentin Chary {
41195b799d4fSCorentin Chary 	return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
41205b799d4fSCorentin Chary }
41215b799d4fSCorentin Chary 
412254a3121fSYurii Pavlovskyi /* Platform device ************************************************************/
412354a3121fSYurii Pavlovskyi 
asus_wmi_platform_init(struct asus_wmi * asus)412439ddf3bfSJoe Perches static int asus_wmi_platform_init(struct asus_wmi *asus)
41255b799d4fSCorentin Chary {
4126e0668f28SYurii Pavlovskyi 	struct device *dev = &asus->platform_device->dev;
4127e0668f28SYurii Pavlovskyi 	char *wmi_uid;
412846dbca87SCorentin Chary 	int rv;
412946dbca87SCorentin Chary 
413046dbca87SCorentin Chary 	/* INIT enable hotkeys on some models */
413146dbca87SCorentin Chary 	if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_INIT, 0, 0, &rv))
41320ed60654Svic 		pr_info("Initialization: %#x\n", rv);
413346dbca87SCorentin Chary 
413446dbca87SCorentin Chary 	/* We don't know yet what to do with this version... */
413546dbca87SCorentin Chary 	if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) {
41360ed60654Svic 		pr_info("BIOS WMI version: %d.%d\n", rv >> 16, rv & 0xFF);
413746dbca87SCorentin Chary 		asus->spec = rv;
413846dbca87SCorentin Chary 	}
413946dbca87SCorentin Chary 
414046dbca87SCorentin Chary 	/*
414146dbca87SCorentin Chary 	 * The SFUN method probably allows the original driver to get the list
414246dbca87SCorentin Chary 	 * of features supported by a given model. For now, 0x0100 or 0x0800
414346dbca87SCorentin Chary 	 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
414446dbca87SCorentin Chary 	 * The significance of others is yet to be found.
414546dbca87SCorentin Chary 	 */
414646dbca87SCorentin Chary 	if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SFUN, 0, 0, &rv)) {
41470ed60654Svic 		pr_info("SFUN value: %#x\n", rv);
414846dbca87SCorentin Chary 		asus->sfun = rv;
414946dbca87SCorentin Chary 	}
415046dbca87SCorentin Chary 
41511d070f89SCorentin Chary 	/*
41521d070f89SCorentin Chary 	 * Eee PC and Notebooks seems to have different method_id for DSTS,
41531d070f89SCorentin Chary 	 * but it may also be related to the BIOS's SPEC.
41541d070f89SCorentin Chary 	 * Note, on most Eeepc, there is no way to check if a method exist
41551d070f89SCorentin Chary 	 * or note, while on notebooks, they returns 0xFFFFFFFE on failure,
41561d070f89SCorentin Chary 	 * but once again, SPEC may probably be used for that kind of things.
4157e0668f28SYurii Pavlovskyi 	 *
4158e0668f28SYurii Pavlovskyi 	 * Additionally at least TUF Gaming series laptops return nothing for
4159e0668f28SYurii Pavlovskyi 	 * unknown methods, so the detection in this way is not possible.
4160e0668f28SYurii Pavlovskyi 	 *
4161e0668f28SYurii Pavlovskyi 	 * There is strong indication that only ACPI WMI devices that have _UID
4162e0668f28SYurii Pavlovskyi 	 * equal to "ASUSWMI" use DCTS whereas those with "ATK" use DSTS.
41631d070f89SCorentin Chary 	 */
4164e0668f28SYurii Pavlovskyi 	wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID);
4165e0668f28SYurii Pavlovskyi 	if (!wmi_uid)
4166e0668f28SYurii Pavlovskyi 		return -ENODEV;
4167e0668f28SYurii Pavlovskyi 
4168e0668f28SYurii Pavlovskyi 	if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) {
4169e0668f28SYurii Pavlovskyi 		dev_info(dev, "Detected ASUSWMI, use DCTS\n");
4170e0668f28SYurii Pavlovskyi 		asus->dsts_id = ASUS_WMI_METHODID_DCTS;
4171e0668f28SYurii Pavlovskyi 	} else {
4172e0668f28SYurii Pavlovskyi 		dev_info(dev, "Detected %s, not ASUSWMI, use DSTS\n", wmi_uid);
41731d070f89SCorentin Chary 		asus->dsts_id = ASUS_WMI_METHODID_DSTS;
4174e0668f28SYurii Pavlovskyi 	}
41751d070f89SCorentin Chary 
41761a373d15SYurii Pavlovskyi 	/*
41771a373d15SYurii Pavlovskyi 	 * Some devices can have multiple event codes stored in a queue before
41781a373d15SYurii Pavlovskyi 	 * the module load if it was unloaded intermittently after calling
41791a373d15SYurii Pavlovskyi 	 * the INIT method (enables event handling). The WMI notify handler is
41801a373d15SYurii Pavlovskyi 	 * expected to retrieve all event codes until a retrieved code equals
41811a373d15SYurii Pavlovskyi 	 * queue end marker (One or Ones). Old codes are flushed from the queue
41821a373d15SYurii Pavlovskyi 	 * upon module load. Not enabling this when it should be has minimal
41831a373d15SYurii Pavlovskyi 	 * visible impact so fall back if anything goes wrong.
41841a373d15SYurii Pavlovskyi 	 */
41851a373d15SYurii Pavlovskyi 	wmi_uid = wmi_get_acpi_device_uid(asus->driver->event_guid);
41861a373d15SYurii Pavlovskyi 	if (wmi_uid && !strcmp(wmi_uid, ASUS_ACPI_UID_ATK)) {
41871a373d15SYurii Pavlovskyi 		dev_info(dev, "Detected ATK, enable event queue\n");
41881a373d15SYurii Pavlovskyi 
41891a373d15SYurii Pavlovskyi 		if (!asus_wmi_notify_queue_flush(asus))
41901a373d15SYurii Pavlovskyi 			asus->wmi_event_queue = true;
41911a373d15SYurii Pavlovskyi 	}
41921d070f89SCorentin Chary 
4193fddbfed5SCorentin Chary 	/* CWAP allow to define the behavior of the Fn+F2 key,
4194fddbfed5SCorentin Chary 	 * this method doesn't seems to be present on Eee PCs */
41956a2bcccdSCorentin Chary 	if (asus->driver->quirks->wapf >= 0)
4196fddbfed5SCorentin Chary 		asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,
41976a2bcccdSCorentin Chary 				      asus->driver->quirks->wapf, NULL);
4198fddbfed5SCorentin Chary 
4199b096f626SYurii Pavlovskyi 	return 0;
42005b799d4fSCorentin Chary }
42015b799d4fSCorentin Chary 
420254a3121fSYurii Pavlovskyi /* debugfs ********************************************************************/
42035b799d4fSCorentin Chary 
4204e12e6d94SCorentin Chary struct asus_wmi_debugfs_node {
4205e12e6d94SCorentin Chary 	struct asus_wmi *asus;
42065b799d4fSCorentin Chary 	char *name;
42075b799d4fSCorentin Chary 	int (*show) (struct seq_file *m, void *data);
42085b799d4fSCorentin Chary };
42095b799d4fSCorentin Chary 
show_dsts(struct seq_file * m,void * data)42105b799d4fSCorentin Chary static int show_dsts(struct seq_file *m, void *data)
42115b799d4fSCorentin Chary {
4212e12e6d94SCorentin Chary 	struct asus_wmi *asus = m->private;
4213d33da3b6SCorentin Chary 	int err;
42145b799d4fSCorentin Chary 	u32 retval = -1;
42155b799d4fSCorentin Chary 
42161d070f89SCorentin Chary 	err = asus_wmi_get_devstate(asus, asus->debug.dev_id, &retval);
4217d33da3b6SCorentin Chary 	if (err < 0)
4218d33da3b6SCorentin Chary 		return err;
42195b799d4fSCorentin Chary 
4220ef343491SCorentin Chary 	seq_printf(m, "DSTS(%#x) = %#x\n", asus->debug.dev_id, retval);
42215b799d4fSCorentin Chary 
42225b799d4fSCorentin Chary 	return 0;
42235b799d4fSCorentin Chary }
42245b799d4fSCorentin Chary 
show_devs(struct seq_file * m,void * data)42255b799d4fSCorentin Chary static int show_devs(struct seq_file *m, void *data)
42265b799d4fSCorentin Chary {
4227e12e6d94SCorentin Chary 	struct asus_wmi *asus = m->private;
4228d33da3b6SCorentin Chary 	int err;
42295b799d4fSCorentin Chary 	u32 retval = -1;
42305b799d4fSCorentin Chary 
4231d33da3b6SCorentin Chary 	err = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param,
4232d33da3b6SCorentin Chary 				    &retval);
4233d33da3b6SCorentin Chary 	if (err < 0)
4234d33da3b6SCorentin Chary 		return err;
42355b799d4fSCorentin Chary 
4236ef343491SCorentin Chary 	seq_printf(m, "DEVS(%#x, %#x) = %#x\n", asus->debug.dev_id,
4237e12e6d94SCorentin Chary 		   asus->debug.ctrl_param, retval);
42385b799d4fSCorentin Chary 
42395b799d4fSCorentin Chary 	return 0;
42405b799d4fSCorentin Chary }
42415b799d4fSCorentin Chary 
show_call(struct seq_file * m,void * data)4242ef343491SCorentin Chary static int show_call(struct seq_file *m, void *data)
4243ef343491SCorentin Chary {
4244ef343491SCorentin Chary 	struct asus_wmi *asus = m->private;
4245ef343491SCorentin Chary 	struct bios_args args = {
4246ef343491SCorentin Chary 		.arg0 = asus->debug.dev_id,
4247ef343491SCorentin Chary 		.arg1 = asus->debug.ctrl_param,
4248ef343491SCorentin Chary 	};
4249ef343491SCorentin Chary 	struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
4250ef343491SCorentin Chary 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
4251ef343491SCorentin Chary 	union acpi_object *obj;
4252ef343491SCorentin Chary 	acpi_status status;
4253ef343491SCorentin Chary 
4254ef343491SCorentin Chary 	status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID,
42550fe57261SPali Rohár 				     0, asus->debug.method_id,
4256ef343491SCorentin Chary 				     &input, &output);
4257ef343491SCorentin Chary 
4258ef343491SCorentin Chary 	if (ACPI_FAILURE(status))
4259ef343491SCorentin Chary 		return -EIO;
4260ef343491SCorentin Chary 
4261ef343491SCorentin Chary 	obj = (union acpi_object *)output.pointer;
4262ef343491SCorentin Chary 	if (obj && obj->type == ACPI_TYPE_INTEGER)
4263ef343491SCorentin Chary 		seq_printf(m, "%#x(%#x, %#x) = %#x\n", asus->debug.method_id,
4264ef343491SCorentin Chary 			   asus->debug.dev_id, asus->debug.ctrl_param,
4265ef343491SCorentin Chary 			   (u32) obj->integer.value);
4266ef343491SCorentin Chary 	else
4267ef343491SCorentin Chary 		seq_printf(m, "%#x(%#x, %#x) = t:%d\n", asus->debug.method_id,
4268ef343491SCorentin Chary 			   asus->debug.dev_id, asus->debug.ctrl_param,
4269a1d60867SDan Carpenter 			   obj ? obj->type : -1);
4270ef343491SCorentin Chary 
4271ef343491SCorentin Chary 	kfree(obj);
4272ef343491SCorentin Chary 
4273ef343491SCorentin Chary 	return 0;
4274ef343491SCorentin Chary }
4275ef343491SCorentin Chary 
4276e12e6d94SCorentin Chary static struct asus_wmi_debugfs_node asus_wmi_debug_files[] = {
42775b799d4fSCorentin Chary 	{NULL, "devs", show_devs},
42785b799d4fSCorentin Chary 	{NULL, "dsts", show_dsts},
4279ef343491SCorentin Chary 	{NULL, "call", show_call},
42805b799d4fSCorentin Chary };
42815b799d4fSCorentin Chary 
asus_wmi_debugfs_open(struct inode * inode,struct file * file)4282e12e6d94SCorentin Chary static int asus_wmi_debugfs_open(struct inode *inode, struct file *file)
42835b799d4fSCorentin Chary {
4284e12e6d94SCorentin Chary 	struct asus_wmi_debugfs_node *node = inode->i_private;
42855b799d4fSCorentin Chary 
4286e12e6d94SCorentin Chary 	return single_open(file, node->show, node->asus);
42875b799d4fSCorentin Chary }
42885b799d4fSCorentin Chary 
4289e12e6d94SCorentin Chary static const struct file_operations asus_wmi_debugfs_io_ops = {
42905b799d4fSCorentin Chary 	.owner = THIS_MODULE,
4291e12e6d94SCorentin Chary 	.open = asus_wmi_debugfs_open,
42925b799d4fSCorentin Chary 	.read = seq_read,
42935b799d4fSCorentin Chary 	.llseek = seq_lseek,
42945b799d4fSCorentin Chary 	.release = single_release,
42955b799d4fSCorentin Chary };
42965b799d4fSCorentin Chary 
asus_wmi_debugfs_exit(struct asus_wmi * asus)4297e12e6d94SCorentin Chary static void asus_wmi_debugfs_exit(struct asus_wmi *asus)
42985b799d4fSCorentin Chary {
4299e12e6d94SCorentin Chary 	debugfs_remove_recursive(asus->debug.root);
43005b799d4fSCorentin Chary }
43015b799d4fSCorentin Chary 
asus_wmi_debugfs_init(struct asus_wmi * asus)4302d2785d37SGreg Kroah-Hartman static void asus_wmi_debugfs_init(struct asus_wmi *asus)
43035b799d4fSCorentin Chary {
43045b799d4fSCorentin Chary 	int i;
43055b799d4fSCorentin Chary 
4306e12e6d94SCorentin Chary 	asus->debug.root = debugfs_create_dir(asus->driver->name, NULL);
43075b799d4fSCorentin Chary 
4308d2785d37SGreg Kroah-Hartman 	debugfs_create_x32("method_id", S_IRUGO | S_IWUSR, asus->debug.root,
4309d2785d37SGreg Kroah-Hartman 			   &asus->debug.method_id);
4310ef343491SCorentin Chary 
4311d2785d37SGreg Kroah-Hartman 	debugfs_create_x32("dev_id", S_IRUGO | S_IWUSR, asus->debug.root,
4312d2785d37SGreg Kroah-Hartman 			   &asus->debug.dev_id);
43135b799d4fSCorentin Chary 
4314d2785d37SGreg Kroah-Hartman 	debugfs_create_x32("ctrl_param", S_IRUGO | S_IWUSR, asus->debug.root,
4315d2785d37SGreg Kroah-Hartman 			   &asus->debug.ctrl_param);
43165b799d4fSCorentin Chary 
4317e12e6d94SCorentin Chary 	for (i = 0; i < ARRAY_SIZE(asus_wmi_debug_files); i++) {
4318e12e6d94SCorentin Chary 		struct asus_wmi_debugfs_node *node = &asus_wmi_debug_files[i];
43195b799d4fSCorentin Chary 
4320e12e6d94SCorentin Chary 		node->asus = asus;
4321d2785d37SGreg Kroah-Hartman 		debugfs_create_file(node->name, S_IFREG | S_IRUGO,
4322e12e6d94SCorentin Chary 				    asus->debug.root, node,
4323e12e6d94SCorentin Chary 				    &asus_wmi_debugfs_io_ops);
43245b799d4fSCorentin Chary 	}
43255b799d4fSCorentin Chary }
43265b799d4fSCorentin Chary 
432754a3121fSYurii Pavlovskyi /* Init / exit ****************************************************************/
43285b799d4fSCorentin Chary 
asus_wmi_add(struct platform_device * pdev)4329e12e6d94SCorentin Chary static int asus_wmi_add(struct platform_device *pdev)
43305b799d4fSCorentin Chary {
4331e12e6d94SCorentin Chary 	struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
4332e12e6d94SCorentin Chary 	struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv);
4333e12e6d94SCorentin Chary 	struct asus_wmi *asus;
43345b799d4fSCorentin Chary 	acpi_status status;
43355b799d4fSCorentin Chary 	int err;
4336a50bd128SAceLan Kao 	u32 result;
43375b799d4fSCorentin Chary 
4338e12e6d94SCorentin Chary 	asus = kzalloc(sizeof(struct asus_wmi), GFP_KERNEL);
4339e12e6d94SCorentin Chary 	if (!asus)
43405b799d4fSCorentin Chary 		return -ENOMEM;
43415b799d4fSCorentin Chary 
4342e12e6d94SCorentin Chary 	asus->driver = wdrv;
4343e12e6d94SCorentin Chary 	asus->platform_device = pdev;
4344e12e6d94SCorentin Chary 	wdrv->platform_device = pdev;
4345e12e6d94SCorentin Chary 	platform_set_drvdata(asus->platform_device, asus);
43465b799d4fSCorentin Chary 
4347c87992d1SAceLan Kao 	if (wdrv->detect_quirks)
4348c87992d1SAceLan Kao 		wdrv->detect_quirks(asus->driver);
43495b799d4fSCorentin Chary 
4350e12e6d94SCorentin Chary 	err = asus_wmi_platform_init(asus);
43515b799d4fSCorentin Chary 	if (err)
43525b799d4fSCorentin Chary 		goto fail_platform;
43535b799d4fSCorentin Chary 
435477ee9d29SLuke D. Jones 	asus->charge_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CHARGE_MODE);
43553c3b5556SHans de Goede 	asus->egpu_enable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_EGPU);
4356d4eca58aSLuke D. Jones 	asus->egpu_connect_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_EGPU_CONNECTED);
43573c3b5556SHans de Goede 	asus->dgpu_disable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_DGPU);
435801ef026aSLuke D. Jones 	asus->gpu_mux_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_MUX);
4359e305a71cSLuke D. Jones 	asus->kbd_rgb_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE);
436061f64515SLuke D. Jones 	asus->kbd_rgb_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_STATE);
4361e0b278e7SLuke D. Jones 	asus->ppt_pl2_sppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_PL2_SPPT);
4362e0b278e7SLuke D. Jones 	asus->ppt_pl1_spl_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_PL1_SPL);
4363e0b278e7SLuke D. Jones 	asus->ppt_fppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_FPPT);
4364e0b278e7SLuke D. Jones 	asus->ppt_apu_sppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_APU_SPPT);
4365e0b278e7SLuke D. Jones 	asus->ppt_plat_sppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_PLAT_SPPT);
4366e0b278e7SLuke D. Jones 	asus->nv_dyn_boost_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_NV_DYN_BOOST);
4367e0b278e7SLuke D. Jones 	asus->nv_temp_tgt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_NV_THERM_TARGET);
43683c3b5556SHans de Goede 	asus->panel_overdrive_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PANEL_OD);
4369abac4259SLuke D. Jones 	asus->mini_led_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE);
437098829e84SLuke D. Jones 
43716051a4b1SMohamed Ghanmi 	if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY))
43726051a4b1SMohamed Ghanmi 		asus->throttle_thermal_policy_dev = ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY;
43736051a4b1SMohamed Ghanmi 	else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO))
43746051a4b1SMohamed Ghanmi 		asus->throttle_thermal_policy_dev = ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO;
43756051a4b1SMohamed Ghanmi 
43769af93db9SDaniel Drake 	err = fan_boost_mode_check_present(asus);
4377b096f626SYurii Pavlovskyi 	if (err)
43789af93db9SDaniel Drake 		goto fail_fan_boost_mode;
4379b096f626SYurii Pavlovskyi 
4380c63d44aeSLuke D. Jones 	err = platform_profile_setup(asus);
4381c63d44aeSLuke D. Jones 	if (err)
4382c63d44aeSLuke D. Jones 		goto fail_platform_profile_setup;
4383c63d44aeSLuke D. Jones 
4384b096f626SYurii Pavlovskyi 	err = asus_wmi_sysfs_init(asus->platform_device);
4385b096f626SYurii Pavlovskyi 	if (err)
4386b096f626SYurii Pavlovskyi 		goto fail_sysfs;
4387b096f626SYurii Pavlovskyi 
4388e12e6d94SCorentin Chary 	err = asus_wmi_input_init(asus);
43895b799d4fSCorentin Chary 	if (err)
43905b799d4fSCorentin Chary 		goto fail_input;
43915b799d4fSCorentin Chary 
439253e755c2SKast Bernd 	err = asus_wmi_fan_init(asus); /* probably no problems on error */
439353e755c2SKast Bernd 
4394e07babdeSCorentin Chary 	err = asus_wmi_hwmon_init(asus);
4395e07babdeSCorentin Chary 	if (err)
4396e07babdeSCorentin Chary 		goto fail_hwmon;
4397e07babdeSCorentin Chary 
43980f0ac158SLuke D. Jones 	err = asus_wmi_custom_fan_curve_init(asus);
43990f0ac158SLuke D. Jones 	if (err)
44000f0ac158SLuke D. Jones 		goto fail_custom_fan_curve;
44010f0ac158SLuke D. Jones 
4402e12e6d94SCorentin Chary 	err = asus_wmi_led_init(asus);
44035b799d4fSCorentin Chary 	if (err)
44045b799d4fSCorentin Chary 		goto fail_leds;
44055b799d4fSCorentin Chary 
440671050ae7SJoão Paulo Rechi Vita 	asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);
440771050ae7SJoão Paulo Rechi Vita 	if (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
440871050ae7SJoão Paulo Rechi Vita 		asus->driver->wlan_ctrl_by_user = 1;
440971050ae7SJoão Paulo Rechi Vita 
4410d1c4e9bfSJoão Paulo Rechi Vita 	if (!(asus->driver->wlan_ctrl_by_user && ashs_present())) {
4411e12e6d94SCorentin Chary 		err = asus_wmi_rfkill_init(asus);
44125b799d4fSCorentin Chary 		if (err)
44135b799d4fSCorentin Chary 			goto fail_rfkill;
4414a977e59cSJoão Paulo Rechi Vita 	}
44155b799d4fSCorentin Chary 
4416e9b61518SOleksij Rempel 	if (asus->driver->quirks->wmi_force_als_set)
4417e9b61518SOleksij Rempel 		asus_wmi_set_als();
4418e9b61518SOleksij Rempel 
44198023eff1SKai-Chuan Hsieh 	if (asus->driver->quirks->xusb2pr)
44208023eff1SKai-Chuan Hsieh 		asus_wmi_set_xusb2pr(asus);
44218023eff1SKai-Chuan Hsieh 
442262c4aa1aSHans de Goede 	if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
4423e12e6d94SCorentin Chary 		err = asus_wmi_backlight_init(asus);
44245b799d4fSCorentin Chary 		if (err && err != -ENODEV)
44255b799d4fSCorentin Chary 			goto fail_backlight;
4426401fee81SHans de Goede 	} else if (asus->driver->quirks->wmi_backlight_set_devstate)
442778f3ac76SJoão Paulo Rechi Vita 		err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, 2, NULL);
44285b799d4fSCorentin Chary 
4429487579baSChris Chiu 	if (asus_wmi_has_fnlock_key(asus)) {
4430ce357fd3SLuca Stefani 		asus->fnlock_locked = fnlock_default;
4431487579baSChris Chiu 		asus_wmi_fnlock_update(asus);
4432487579baSChris Chiu 	}
4433487579baSChris Chiu 
4434e12e6d94SCorentin Chary 	status = wmi_install_notify_handler(asus->driver->event_guid,
4435e12e6d94SCorentin Chary 					    asus_wmi_notify, asus);
44365b799d4fSCorentin Chary 	if (ACPI_FAILURE(status)) {
4437e12e6d94SCorentin Chary 		pr_err("Unable to register notify handler - %d\n", status);
44385b799d4fSCorentin Chary 		err = -ENODEV;
44395b799d4fSCorentin Chary 		goto fail_wmi_handler;
44405b799d4fSCorentin Chary 	}
44415b799d4fSCorentin Chary 
4442cb6b8919SHans de Goede 	if (asus->driver->quirks->i8042_filter) {
4443cb6b8919SHans de Goede 		err = i8042_install_filter(asus->driver->quirks->i8042_filter);
4444cb6b8919SHans de Goede 		if (err)
4445cb6b8919SHans de Goede 			pr_warn("Unable to install key filter - %d\n", err);
4446cb6b8919SHans de Goede 	}
4447cb6b8919SHans de Goede 
44487973353eSKristian Klausen 	asus_wmi_battery_init(asus);
44497973353eSKristian Klausen 
4450d2785d37SGreg Kroah-Hartman 	asus_wmi_debugfs_init(asus);
44515b799d4fSCorentin Chary 
44525b799d4fSCorentin Chary 	return 0;
44535b799d4fSCorentin Chary 
44545b799d4fSCorentin Chary fail_wmi_handler:
4455e12e6d94SCorentin Chary 	asus_wmi_backlight_exit(asus);
44565b799d4fSCorentin Chary fail_backlight:
4457e12e6d94SCorentin Chary 	asus_wmi_rfkill_exit(asus);
44585b799d4fSCorentin Chary fail_rfkill:
4459e12e6d94SCorentin Chary 	asus_wmi_led_exit(asus);
44605b799d4fSCorentin Chary fail_leds:
4461e07babdeSCorentin Chary fail_hwmon:
4462e12e6d94SCorentin Chary 	asus_wmi_input_exit(asus);
44635b799d4fSCorentin Chary fail_input:
4464b096f626SYurii Pavlovskyi 	asus_wmi_sysfs_exit(asus->platform_device);
4465b096f626SYurii Pavlovskyi fail_sysfs:
44660f0ac158SLuke D. Jones fail_custom_fan_curve:
4467c63d44aeSLuke D. Jones fail_platform_profile_setup:
4468c63d44aeSLuke D. Jones 	if (asus->platform_profile_support)
4469c63d44aeSLuke D. Jones 		platform_profile_remove();
44709af93db9SDaniel Drake fail_fan_boost_mode:
44715b799d4fSCorentin Chary fail_platform:
4472e12e6d94SCorentin Chary 	kfree(asus);
44735b799d4fSCorentin Chary 	return err;
44745b799d4fSCorentin Chary }
44755b799d4fSCorentin Chary 
asus_wmi_remove(struct platform_device * device)4476e12e6d94SCorentin Chary static int asus_wmi_remove(struct platform_device *device)
44775b799d4fSCorentin Chary {
4478e12e6d94SCorentin Chary 	struct asus_wmi *asus;
44795b799d4fSCorentin Chary 
4480e12e6d94SCorentin Chary 	asus = platform_get_drvdata(device);
4481cb6b8919SHans de Goede 	if (asus->driver->quirks->i8042_filter)
4482cb6b8919SHans de Goede 		i8042_remove_filter(asus->driver->quirks->i8042_filter);
4483e12e6d94SCorentin Chary 	wmi_remove_notify_handler(asus->driver->event_guid);
4484e12e6d94SCorentin Chary 	asus_wmi_backlight_exit(asus);
4485e12e6d94SCorentin Chary 	asus_wmi_input_exit(asus);
4486e12e6d94SCorentin Chary 	asus_wmi_led_exit(asus);
4487e12e6d94SCorentin Chary 	asus_wmi_rfkill_exit(asus);
4488e12e6d94SCorentin Chary 	asus_wmi_debugfs_exit(asus);
4489b096f626SYurii Pavlovskyi 	asus_wmi_sysfs_exit(asus->platform_device);
44902889ffcfSDaniel Drake 	asus_fan_set_auto(asus);
44910f0ac158SLuke D. Jones 	throttle_thermal_policy_set_default(asus);
44927973353eSKristian Klausen 	asus_wmi_battery_exit(asus);
44935b799d4fSCorentin Chary 
4494c63d44aeSLuke D. Jones 	if (asus->platform_profile_support)
4495c63d44aeSLuke D. Jones 		platform_profile_remove();
4496c63d44aeSLuke D. Jones 
4497e12e6d94SCorentin Chary 	kfree(asus);
44985b799d4fSCorentin Chary 	return 0;
44995b799d4fSCorentin Chary }
45005b799d4fSCorentin Chary 
450154a3121fSYurii Pavlovskyi /* Platform driver - hibernate/resume callbacks *******************************/
450254a3121fSYurii Pavlovskyi 
asus_hotk_thaw(struct device * device)4503e12e6d94SCorentin Chary static int asus_hotk_thaw(struct device *device)
45045b799d4fSCorentin Chary {
4505e12e6d94SCorentin Chary 	struct asus_wmi *asus = dev_get_drvdata(device);
45065b799d4fSCorentin Chary 
4507a7ce3f04SCorentin Chary 	if (asus->wlan.rfkill) {
45085b799d4fSCorentin Chary 		bool wlan;
45095b799d4fSCorentin Chary 
45105b799d4fSCorentin Chary 		/*
45115b799d4fSCorentin Chary 		 * Work around bios bug - acpi _PTS turns off the wireless led
45125b799d4fSCorentin Chary 		 * during suspend.  Normally it restores it on resume, but
45135b799d4fSCorentin Chary 		 * we should kick it ourselves in case hibernation is aborted.
45145b799d4fSCorentin Chary 		 */
45151d070f89SCorentin Chary 		wlan = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
4516e12e6d94SCorentin Chary 		asus_wmi_set_devstate(ASUS_WMI_DEVID_WLAN, wlan, NULL);
45175b799d4fSCorentin Chary 	}
45185b799d4fSCorentin Chary 
45195b799d4fSCorentin Chary 	return 0;
45205b799d4fSCorentin Chary }
45215b799d4fSCorentin Chary 
asus_hotk_resume(struct device * device)452230734049SOleksij Rempel static int asus_hotk_resume(struct device *device)
452330734049SOleksij Rempel {
452430734049SOleksij Rempel 	struct asus_wmi *asus = dev_get_drvdata(device);
452530734049SOleksij Rempel 
452630734049SOleksij Rempel 	if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
45279fe44fc9SJian-Hong Pan 		kbd_led_update(asus);
452830734049SOleksij Rempel 
4529487579baSChris Chiu 	if (asus_wmi_has_fnlock_key(asus))
4530487579baSChris Chiu 		asus_wmi_fnlock_update(asus);
4531ea856ec2SSamuel Čavoj 
45321ea0d3b4SHans de Goede 	asus_wmi_tablet_mode_get_state(asus);
453330734049SOleksij Rempel 	return 0;
453430734049SOleksij Rempel }
453530734049SOleksij Rempel 
asus_hotk_restore(struct device * device)4536e12e6d94SCorentin Chary static int asus_hotk_restore(struct device *device)
45375b799d4fSCorentin Chary {
4538e12e6d94SCorentin Chary 	struct asus_wmi *asus = dev_get_drvdata(device);
45395b799d4fSCorentin Chary 	int bl;
45405b799d4fSCorentin Chary 
45415b799d4fSCorentin Chary 	/* Refresh both wlan rfkill state and pci hotplug */
4542a7ce3f04SCorentin Chary 	if (asus->wlan.rfkill)
4543e12e6d94SCorentin Chary 		asus_rfkill_hotplug(asus);
45445b799d4fSCorentin Chary 
4545a7ce3f04SCorentin Chary 	if (asus->bluetooth.rfkill) {
45461d070f89SCorentin Chary 		bl = !asus_wmi_get_devstate_simple(asus,
45471d070f89SCorentin Chary 						   ASUS_WMI_DEVID_BLUETOOTH);
4548a7ce3f04SCorentin Chary 		rfkill_set_sw_state(asus->bluetooth.rfkill, bl);
45495b799d4fSCorentin Chary 	}
4550a7ce3f04SCorentin Chary 	if (asus->wimax.rfkill) {
45511d070f89SCorentin Chary 		bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WIMAX);
4552a7ce3f04SCorentin Chary 		rfkill_set_sw_state(asus->wimax.rfkill, bl);
45535b799d4fSCorentin Chary 	}
4554a7ce3f04SCorentin Chary 	if (asus->wwan3g.rfkill) {
45551d070f89SCorentin Chary 		bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WWAN3G);
4556a7ce3f04SCorentin Chary 		rfkill_set_sw_state(asus->wwan3g.rfkill, bl);
45575b799d4fSCorentin Chary 	}
455843be8bdeSCorentin Chary 	if (asus->gps.rfkill) {
455943be8bdeSCorentin Chary 		bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPS);
456043be8bdeSCorentin Chary 		rfkill_set_sw_state(asus->gps.rfkill, bl);
456143be8bdeSCorentin Chary 	}
4562a912d329SCorentin Chary 	if (asus->uwb.rfkill) {
4563a912d329SCorentin Chary 		bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_UWB);
4564a912d329SCorentin Chary 		rfkill_set_sw_state(asus->uwb.rfkill, bl);
4565a912d329SCorentin Chary 	}
456630734049SOleksij Rempel 	if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
45679fe44fc9SJian-Hong Pan 		kbd_led_update(asus);
45685b799d4fSCorentin Chary 
4569487579baSChris Chiu 	if (asus_wmi_has_fnlock_key(asus))
4570487579baSChris Chiu 		asus_wmi_fnlock_update(asus);
4571ea856ec2SSamuel Čavoj 
45721ea0d3b4SHans de Goede 	asus_wmi_tablet_mode_get_state(asus);
45735b799d4fSCorentin Chary 	return 0;
45745b799d4fSCorentin Chary }
45755b799d4fSCorentin Chary 
4576e12e6d94SCorentin Chary static const struct dev_pm_ops asus_pm_ops = {
4577e12e6d94SCorentin Chary 	.thaw = asus_hotk_thaw,
4578e12e6d94SCorentin Chary 	.restore = asus_hotk_restore,
457930734049SOleksij Rempel 	.resume = asus_hotk_resume,
45805b799d4fSCorentin Chary };
45815b799d4fSCorentin Chary 
458254a3121fSYurii Pavlovskyi /* Registration ***************************************************************/
458354a3121fSYurii Pavlovskyi 
asus_wmi_probe(struct platform_device * pdev)4584e12e6d94SCorentin Chary static int asus_wmi_probe(struct platform_device *pdev)
45855b799d4fSCorentin Chary {
4586e12e6d94SCorentin Chary 	struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
4587e12e6d94SCorentin Chary 	struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv);
4588e12e6d94SCorentin Chary 	int ret;
45895b799d4fSCorentin Chary 
4590e12e6d94SCorentin Chary 	if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) {
4591c994611aSHans de Goede 		pr_warn("ASUS Management GUID not found\n");
45925b799d4fSCorentin Chary 		return -ENODEV;
45935b799d4fSCorentin Chary 	}
45945b799d4fSCorentin Chary 
4595e12e6d94SCorentin Chary 	if (wdrv->event_guid && !wmi_has_guid(wdrv->event_guid)) {
4596c994611aSHans de Goede 		pr_warn("ASUS Event GUID not found\n");
45975b799d4fSCorentin Chary 		return -ENODEV;
45985b799d4fSCorentin Chary 	}
45995b799d4fSCorentin Chary 
4600e12e6d94SCorentin Chary 	if (wdrv->probe) {
4601e12e6d94SCorentin Chary 		ret = wdrv->probe(pdev);
4602e12e6d94SCorentin Chary 		if (ret)
4603e12e6d94SCorentin Chary 			return ret;
46045b799d4fSCorentin Chary 	}
46055b799d4fSCorentin Chary 
4606e12e6d94SCorentin Chary 	return asus_wmi_add(pdev);
4607e12e6d94SCorentin Chary }
46085b799d4fSCorentin Chary 
4609e12e6d94SCorentin Chary static bool used;
4610e12e6d94SCorentin Chary 
asus_wmi_register_driver(struct asus_wmi_driver * driver)46118fe8c25eSCorentin Chary int __init_or_module asus_wmi_register_driver(struct asus_wmi_driver *driver)
46125b799d4fSCorentin Chary {
4613e12e6d94SCorentin Chary 	struct platform_driver *platform_driver;
4614e12e6d94SCorentin Chary 	struct platform_device *platform_device;
4615e12e6d94SCorentin Chary 
4616e12e6d94SCorentin Chary 	if (used)
4617e12e6d94SCorentin Chary 		return -EBUSY;
4618e12e6d94SCorentin Chary 
4619e12e6d94SCorentin Chary 	platform_driver = &driver->platform_driver;
4620e12e6d94SCorentin Chary 	platform_driver->remove = asus_wmi_remove;
4621e12e6d94SCorentin Chary 	platform_driver->driver.owner = driver->owner;
4622e12e6d94SCorentin Chary 	platform_driver->driver.name = driver->name;
4623e12e6d94SCorentin Chary 	platform_driver->driver.pm = &asus_pm_ops;
4624e12e6d94SCorentin Chary 
4625e12e6d94SCorentin Chary 	platform_device = platform_create_bundle(platform_driver,
4626e12e6d94SCorentin Chary 						 asus_wmi_probe,
46275b799d4fSCorentin Chary 						 NULL, 0, NULL, 0);
46285b799d4fSCorentin Chary 	if (IS_ERR(platform_device))
46295b799d4fSCorentin Chary 		return PTR_ERR(platform_device);
4630e12e6d94SCorentin Chary 
4631e12e6d94SCorentin Chary 	used = true;
4632e12e6d94SCorentin Chary 	return 0;
4633e12e6d94SCorentin Chary }
4634e12e6d94SCorentin Chary EXPORT_SYMBOL_GPL(asus_wmi_register_driver);
4635e12e6d94SCorentin Chary 
asus_wmi_unregister_driver(struct asus_wmi_driver * driver)4636e12e6d94SCorentin Chary void asus_wmi_unregister_driver(struct asus_wmi_driver *driver)
4637e12e6d94SCorentin Chary {
4638e12e6d94SCorentin Chary 	platform_device_unregister(driver->platform_device);
4639e12e6d94SCorentin Chary 	platform_driver_unregister(&driver->platform_driver);
4640e12e6d94SCorentin Chary 	used = false;
4641e12e6d94SCorentin Chary }
4642e12e6d94SCorentin Chary EXPORT_SYMBOL_GPL(asus_wmi_unregister_driver);
4643e12e6d94SCorentin Chary 
asus_wmi_init(void)4644e12e6d94SCorentin Chary static int __init asus_wmi_init(void)
4645e12e6d94SCorentin Chary {
46460ed60654Svic 	pr_info("ASUS WMI generic driver loaded\n");
46475b799d4fSCorentin Chary 	return 0;
46485b799d4fSCorentin Chary }
46495b799d4fSCorentin Chary 
asus_wmi_exit(void)4650e12e6d94SCorentin Chary static void __exit asus_wmi_exit(void)
46515b799d4fSCorentin Chary {
46520ed60654Svic 	pr_info("ASUS WMI generic driver unloaded\n");
46535b799d4fSCorentin Chary }
46545b799d4fSCorentin Chary 
4655e12e6d94SCorentin Chary module_init(asus_wmi_init);
4656e12e6d94SCorentin Chary module_exit(asus_wmi_exit);
4657