18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Asus PC WMI hotkey driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright(C) 2010 Intel Corporation.
68c2ecf20Sopenharmony_ci * Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Portions based on wistron_btns.c:
98c2ecf20Sopenharmony_ci * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
108c2ecf20Sopenharmony_ci * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
118c2ecf20Sopenharmony_ci * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/init.h>
198c2ecf20Sopenharmony_ci#include <linux/types.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/input.h>
228c2ecf20Sopenharmony_ci#include <linux/input/sparse-keymap.h>
238c2ecf20Sopenharmony_ci#include <linux/fb.h>
248c2ecf20Sopenharmony_ci#include <linux/backlight.h>
258c2ecf20Sopenharmony_ci#include <linux/leds.h>
268c2ecf20Sopenharmony_ci#include <linux/rfkill.h>
278c2ecf20Sopenharmony_ci#include <linux/pci.h>
288c2ecf20Sopenharmony_ci#include <linux/pci_hotplug.h>
298c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
308c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
318c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h>
328c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
338c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
348c2ecf20Sopenharmony_ci#include <linux/platform_data/x86/asus-wmi.h>
358c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
368c2ecf20Sopenharmony_ci#include <linux/acpi.h>
378c2ecf20Sopenharmony_ci#include <linux/dmi.h>
388c2ecf20Sopenharmony_ci#include <linux/units.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include <acpi/battery.h>
418c2ecf20Sopenharmony_ci#include <acpi/video.h>
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#include "asus-wmi.h"
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciMODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>, "
468c2ecf20Sopenharmony_ci	      "Yong Wang <yong.y.wang@intel.com>");
478c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Asus Generic WMI Driver");
488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define to_asus_wmi_driver(pdrv)					\
518c2ecf20Sopenharmony_ci	(container_of((pdrv), struct asus_wmi_driver, platform_driver))
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define ASUS_WMI_MGMT_GUID	"97845ED0-4E6D-11DE-8A39-0800200C9A66"
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define NOTIFY_BRNUP_MIN		0x11
568c2ecf20Sopenharmony_ci#define NOTIFY_BRNUP_MAX		0x1f
578c2ecf20Sopenharmony_ci#define NOTIFY_BRNDOWN_MIN		0x20
588c2ecf20Sopenharmony_ci#define NOTIFY_BRNDOWN_MAX		0x2e
598c2ecf20Sopenharmony_ci#define NOTIFY_FNLOCK_TOGGLE		0x4e
608c2ecf20Sopenharmony_ci#define NOTIFY_KBD_DOCK_CHANGE		0x75
618c2ecf20Sopenharmony_ci#define NOTIFY_KBD_BRTUP		0xc4
628c2ecf20Sopenharmony_ci#define NOTIFY_KBD_BRTDWN		0xc5
638c2ecf20Sopenharmony_ci#define NOTIFY_KBD_BRTTOGGLE		0xc7
648c2ecf20Sopenharmony_ci#define NOTIFY_KBD_FBM			0x99
658c2ecf20Sopenharmony_ci#define NOTIFY_KBD_TTP			0xae
668c2ecf20Sopenharmony_ci#define NOTIFY_LID_FLIP			0xfa
678c2ecf20Sopenharmony_ci#define NOTIFY_LID_FLIP_ROG		0xbd
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#define ASUS_WMI_FNLOCK_BIOS_DISABLED	BIT(0)
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define ASUS_FAN_DESC			"cpu_fan"
728c2ecf20Sopenharmony_ci#define ASUS_FAN_MFUN			0x13
738c2ecf20Sopenharmony_ci#define ASUS_FAN_SFUN_READ		0x06
748c2ecf20Sopenharmony_ci#define ASUS_FAN_SFUN_WRITE		0x07
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* Based on standard hwmon pwmX_enable values */
778c2ecf20Sopenharmony_ci#define ASUS_FAN_CTRL_FULLSPEED		0
788c2ecf20Sopenharmony_ci#define ASUS_FAN_CTRL_MANUAL		1
798c2ecf20Sopenharmony_ci#define ASUS_FAN_CTRL_AUTO		2
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#define ASUS_FAN_BOOST_MODE_NORMAL		0
828c2ecf20Sopenharmony_ci#define ASUS_FAN_BOOST_MODE_OVERBOOST		1
838c2ecf20Sopenharmony_ci#define ASUS_FAN_BOOST_MODE_OVERBOOST_MASK	0x01
848c2ecf20Sopenharmony_ci#define ASUS_FAN_BOOST_MODE_SILENT		2
858c2ecf20Sopenharmony_ci#define ASUS_FAN_BOOST_MODE_SILENT_MASK		0x02
868c2ecf20Sopenharmony_ci#define ASUS_FAN_BOOST_MODES_MASK		0x03
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci#define ASUS_THROTTLE_THERMAL_POLICY_DEFAULT	0
898c2ecf20Sopenharmony_ci#define ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST	1
908c2ecf20Sopenharmony_ci#define ASUS_THROTTLE_THERMAL_POLICY_SILENT	2
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#define USB_INTEL_XUSB2PR		0xD0
938c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI	0x9c31
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci#define ASUS_ACPI_UID_ASUSWMI		"ASUSWMI"
968c2ecf20Sopenharmony_ci#define ASUS_ACPI_UID_ATK		"ATK"
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#define WMI_EVENT_QUEUE_SIZE		0x10
998c2ecf20Sopenharmony_ci#define WMI_EVENT_QUEUE_END		0x1
1008c2ecf20Sopenharmony_ci#define WMI_EVENT_MASK			0xFFFF
1018c2ecf20Sopenharmony_ci/* The WMI hotkey event value is always the same. */
1028c2ecf20Sopenharmony_ci#define WMI_EVENT_VALUE_ATK		0xFF
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci#define WMI_EVENT_MASK			0xFFFF
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic bool ashs_present(void)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	int i = 0;
1118c2ecf20Sopenharmony_ci	while (ashs_ids[i]) {
1128c2ecf20Sopenharmony_ci		if (acpi_dev_found(ashs_ids[i++]))
1138c2ecf20Sopenharmony_ci			return true;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci	return false;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistruct bios_args {
1198c2ecf20Sopenharmony_ci	u32 arg0;
1208c2ecf20Sopenharmony_ci	u32 arg1;
1218c2ecf20Sopenharmony_ci	u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */
1228c2ecf20Sopenharmony_ci	u32 arg4;
1238c2ecf20Sopenharmony_ci	u32 arg5;
1248c2ecf20Sopenharmony_ci} __packed;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/*
1278c2ecf20Sopenharmony_ci * Struct that's used for all methods called via AGFN. Naming is
1288c2ecf20Sopenharmony_ci * identically to the AML code.
1298c2ecf20Sopenharmony_ci */
1308c2ecf20Sopenharmony_cistruct agfn_args {
1318c2ecf20Sopenharmony_ci	u16 mfun; /* probably "Multi-function" to be called */
1328c2ecf20Sopenharmony_ci	u16 sfun; /* probably "Sub-function" to be called */
1338c2ecf20Sopenharmony_ci	u16 len;  /* size of the hole struct, including subfunction fields */
1348c2ecf20Sopenharmony_ci	u8 stas;  /* not used by now */
1358c2ecf20Sopenharmony_ci	u8 err;   /* zero on success */
1368c2ecf20Sopenharmony_ci} __packed;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/* struct used for calling fan read and write methods */
1398c2ecf20Sopenharmony_cistruct agfn_fan_args {
1408c2ecf20Sopenharmony_ci	struct agfn_args agfn;	/* common fields */
1418c2ecf20Sopenharmony_ci	u8 fan;			/* fan number: 0: set auto mode 1: 1st fan */
1428c2ecf20Sopenharmony_ci	u32 speed;		/* read: RPM/100 - write: 0-255 */
1438c2ecf20Sopenharmony_ci} __packed;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/*
1468c2ecf20Sopenharmony_ci * <platform>/    - debugfs root directory
1478c2ecf20Sopenharmony_ci *   dev_id      - current dev_id
1488c2ecf20Sopenharmony_ci *   ctrl_param  - current ctrl_param
1498c2ecf20Sopenharmony_ci *   method_id   - current method_id
1508c2ecf20Sopenharmony_ci *   devs        - call DEVS(dev_id, ctrl_param) and print result
1518c2ecf20Sopenharmony_ci *   dsts        - call DSTS(dev_id)  and print result
1528c2ecf20Sopenharmony_ci *   call        - call method_id(dev_id, ctrl_param) and print result
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_cistruct asus_wmi_debug {
1558c2ecf20Sopenharmony_ci	struct dentry *root;
1568c2ecf20Sopenharmony_ci	u32 method_id;
1578c2ecf20Sopenharmony_ci	u32 dev_id;
1588c2ecf20Sopenharmony_ci	u32 ctrl_param;
1598c2ecf20Sopenharmony_ci};
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistruct asus_rfkill {
1628c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
1638c2ecf20Sopenharmony_ci	struct rfkill *rfkill;
1648c2ecf20Sopenharmony_ci	u32 dev_id;
1658c2ecf20Sopenharmony_ci};
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cienum fan_type {
1688c2ecf20Sopenharmony_ci	FAN_TYPE_NONE = 0,
1698c2ecf20Sopenharmony_ci	FAN_TYPE_AGFN,		/* deprecated on newer platforms */
1708c2ecf20Sopenharmony_ci	FAN_TYPE_SPEC83,	/* starting in Spec 8.3, use CPU_FAN_CTRL */
1718c2ecf20Sopenharmony_ci};
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistruct asus_wmi {
1748c2ecf20Sopenharmony_ci	int dsts_id;
1758c2ecf20Sopenharmony_ci	int spec;
1768c2ecf20Sopenharmony_ci	int sfun;
1778c2ecf20Sopenharmony_ci	bool wmi_event_queue;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	struct input_dev *inputdev;
1808c2ecf20Sopenharmony_ci	struct backlight_device *backlight_device;
1818c2ecf20Sopenharmony_ci	struct platform_device *platform_device;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	struct led_classdev wlan_led;
1848c2ecf20Sopenharmony_ci	int wlan_led_wk;
1858c2ecf20Sopenharmony_ci	struct led_classdev tpd_led;
1868c2ecf20Sopenharmony_ci	int tpd_led_wk;
1878c2ecf20Sopenharmony_ci	struct led_classdev kbd_led;
1888c2ecf20Sopenharmony_ci	int kbd_led_wk;
1898c2ecf20Sopenharmony_ci	struct led_classdev lightbar_led;
1908c2ecf20Sopenharmony_ci	int lightbar_led_wk;
1918c2ecf20Sopenharmony_ci	struct workqueue_struct *led_workqueue;
1928c2ecf20Sopenharmony_ci	struct work_struct tpd_led_work;
1938c2ecf20Sopenharmony_ci	struct work_struct wlan_led_work;
1948c2ecf20Sopenharmony_ci	struct work_struct lightbar_led_work;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	struct asus_rfkill wlan;
1978c2ecf20Sopenharmony_ci	struct asus_rfkill bluetooth;
1988c2ecf20Sopenharmony_ci	struct asus_rfkill wimax;
1998c2ecf20Sopenharmony_ci	struct asus_rfkill wwan3g;
2008c2ecf20Sopenharmony_ci	struct asus_rfkill gps;
2018c2ecf20Sopenharmony_ci	struct asus_rfkill uwb;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	int tablet_switch_event_code;
2048c2ecf20Sopenharmony_ci	u32 tablet_switch_dev_id;
2058c2ecf20Sopenharmony_ci	bool tablet_switch_inverted;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	enum fan_type fan_type;
2088c2ecf20Sopenharmony_ci	int fan_pwm_mode;
2098c2ecf20Sopenharmony_ci	int agfn_pwm;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	bool fan_boost_mode_available;
2128c2ecf20Sopenharmony_ci	u8 fan_boost_mode_mask;
2138c2ecf20Sopenharmony_ci	u8 fan_boost_mode;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	bool dgpu_disable_available;
2168c2ecf20Sopenharmony_ci	bool dgpu_disable;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	bool throttle_thermal_policy_available;
2198c2ecf20Sopenharmony_ci	u8 throttle_thermal_policy_mode;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	// The RSOC controls the maximum charging percentage.
2228c2ecf20Sopenharmony_ci	bool battery_rsoc_available;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	struct hotplug_slot hotplug_slot;
2258c2ecf20Sopenharmony_ci	struct mutex hotplug_lock;
2268c2ecf20Sopenharmony_ci	struct mutex wmi_lock;
2278c2ecf20Sopenharmony_ci	struct workqueue_struct *hotplug_workqueue;
2288c2ecf20Sopenharmony_ci	struct work_struct hotplug_work;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	bool fnlock_locked;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	struct asus_wmi_debug debug;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	struct asus_wmi_driver *driver;
2358c2ecf20Sopenharmony_ci};
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/* WMI ************************************************************************/
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic int asus_wmi_evaluate_method3(u32 method_id,
2408c2ecf20Sopenharmony_ci		u32 arg0, u32 arg1, u32 arg2, u32 *retval)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct bios_args args = {
2438c2ecf20Sopenharmony_ci		.arg0 = arg0,
2448c2ecf20Sopenharmony_ci		.arg1 = arg1,
2458c2ecf20Sopenharmony_ci		.arg2 = arg2,
2468c2ecf20Sopenharmony_ci	};
2478c2ecf20Sopenharmony_ci	struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
2488c2ecf20Sopenharmony_ci	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
2498c2ecf20Sopenharmony_ci	acpi_status status;
2508c2ecf20Sopenharmony_ci	union acpi_object *obj;
2518c2ecf20Sopenharmony_ci	u32 tmp = 0;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
2548c2ecf20Sopenharmony_ci				     &input, &output);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
2578c2ecf20Sopenharmony_ci		return -EIO;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	obj = (union acpi_object *)output.pointer;
2608c2ecf20Sopenharmony_ci	if (obj && obj->type == ACPI_TYPE_INTEGER)
2618c2ecf20Sopenharmony_ci		tmp = (u32) obj->integer.value;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	if (retval)
2648c2ecf20Sopenharmony_ci		*retval = tmp;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	kfree(obj);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
2698c2ecf20Sopenharmony_ci		return -ENODEV;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return 0;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ciint asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	return asus_wmi_evaluate_method3(method_id, arg0, arg1, 0, retval);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(asus_wmi_evaluate_method);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	struct acpi_buffer input;
2838c2ecf20Sopenharmony_ci	u64 phys_addr;
2848c2ecf20Sopenharmony_ci	u32 retval;
2858c2ecf20Sopenharmony_ci	u32 status;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/*
2888c2ecf20Sopenharmony_ci	 * Copy to dma capable address otherwise memory corruption occurs as
2898c2ecf20Sopenharmony_ci	 * bios has to be able to access it.
2908c2ecf20Sopenharmony_ci	 */
2918c2ecf20Sopenharmony_ci	input.pointer = kmemdup(args.pointer, args.length, GFP_DMA | GFP_KERNEL);
2928c2ecf20Sopenharmony_ci	input.length = args.length;
2938c2ecf20Sopenharmony_ci	if (!input.pointer)
2948c2ecf20Sopenharmony_ci		return -ENOMEM;
2958c2ecf20Sopenharmony_ci	phys_addr = virt_to_phys(input.pointer);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	status = asus_wmi_evaluate_method(ASUS_WMI_METHODID_AGFN,
2988c2ecf20Sopenharmony_ci					phys_addr, 0, &retval);
2998c2ecf20Sopenharmony_ci	if (!status)
3008c2ecf20Sopenharmony_ci		memcpy(args.pointer, input.pointer, args.length);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	kfree(input.pointer);
3038c2ecf20Sopenharmony_ci	if (status)
3048c2ecf20Sopenharmony_ci		return -ENXIO;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	return retval;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
3158c2ecf20Sopenharmony_ci				 u32 *retval)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id,
3188c2ecf20Sopenharmony_ci					ctrl_param, retval);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci/* Helper for special devices with magic return codes */
3228c2ecf20Sopenharmony_cistatic int asus_wmi_get_devstate_bits(struct asus_wmi *asus,
3238c2ecf20Sopenharmony_ci				      u32 dev_id, u32 mask)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	u32 retval = 0;
3268c2ecf20Sopenharmony_ci	int err;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	err = asus_wmi_get_devstate(asus, dev_id, &retval);
3298c2ecf20Sopenharmony_ci	if (err < 0)
3308c2ecf20Sopenharmony_ci		return err;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (!(retval & ASUS_WMI_DSTS_PRESENCE_BIT))
3338c2ecf20Sopenharmony_ci		return -ENODEV;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (mask == ASUS_WMI_DSTS_STATUS_BIT) {
3368c2ecf20Sopenharmony_ci		if (retval & ASUS_WMI_DSTS_UNKNOWN_BIT)
3378c2ecf20Sopenharmony_ci			return -ENODEV;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	return retval & mask;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	return asus_wmi_get_devstate_bits(asus, dev_id,
3468c2ecf20Sopenharmony_ci					  ASUS_WMI_DSTS_STATUS_BIT);
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	u32 retval;
3528c2ecf20Sopenharmony_ci	int status = asus_wmi_get_devstate(asus, dev_id, &retval);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci/* Input **********************************************************************/
3588c2ecf20Sopenharmony_cistatic void asus_wmi_tablet_sw_report(struct asus_wmi *asus, bool value)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	input_report_switch(asus->inputdev, SW_TABLET_MODE,
3618c2ecf20Sopenharmony_ci			    asus->tablet_switch_inverted ? !value : value);
3628c2ecf20Sopenharmony_ci	input_sync(asus->inputdev);
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic void asus_wmi_tablet_sw_init(struct asus_wmi *asus, u32 dev_id, int event_code)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct device *dev = &asus->platform_device->dev;
3688c2ecf20Sopenharmony_ci	int result;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	result = asus_wmi_get_devstate_simple(asus, dev_id);
3718c2ecf20Sopenharmony_ci	if (result >= 0) {
3728c2ecf20Sopenharmony_ci		input_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE);
3738c2ecf20Sopenharmony_ci		asus_wmi_tablet_sw_report(asus, result);
3748c2ecf20Sopenharmony_ci		asus->tablet_switch_dev_id = dev_id;
3758c2ecf20Sopenharmony_ci		asus->tablet_switch_event_code = event_code;
3768c2ecf20Sopenharmony_ci	} else if (result == -ENODEV) {
3778c2ecf20Sopenharmony_ci		dev_err(dev, "This device has tablet-mode-switch quirk but got ENODEV checking it. This is a bug.");
3788c2ecf20Sopenharmony_ci	} else {
3798c2ecf20Sopenharmony_ci		dev_err(dev, "Error checking for tablet-mode-switch: %d\n", result);
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic int asus_wmi_input_init(struct asus_wmi *asus)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	struct device *dev = &asus->platform_device->dev;
3868c2ecf20Sopenharmony_ci	int err;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	asus->inputdev = input_allocate_device();
3898c2ecf20Sopenharmony_ci	if (!asus->inputdev)
3908c2ecf20Sopenharmony_ci		return -ENOMEM;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	asus->inputdev->name = asus->driver->input_name;
3938c2ecf20Sopenharmony_ci	asus->inputdev->phys = asus->driver->input_phys;
3948c2ecf20Sopenharmony_ci	asus->inputdev->id.bustype = BUS_HOST;
3958c2ecf20Sopenharmony_ci	asus->inputdev->dev.parent = dev;
3968c2ecf20Sopenharmony_ci	set_bit(EV_REP, asus->inputdev->evbit);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);
3998c2ecf20Sopenharmony_ci	if (err)
4008c2ecf20Sopenharmony_ci		goto err_free_dev;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	switch (asus->driver->quirks->tablet_switch_mode) {
4038c2ecf20Sopenharmony_ci	case asus_wmi_no_tablet_switch:
4048c2ecf20Sopenharmony_ci		break;
4058c2ecf20Sopenharmony_ci	case asus_wmi_kbd_dock_devid:
4068c2ecf20Sopenharmony_ci		asus->tablet_switch_inverted = true;
4078c2ecf20Sopenharmony_ci		asus_wmi_tablet_sw_init(asus, ASUS_WMI_DEVID_KBD_DOCK, NOTIFY_KBD_DOCK_CHANGE);
4088c2ecf20Sopenharmony_ci		break;
4098c2ecf20Sopenharmony_ci	case asus_wmi_lid_flip_devid:
4108c2ecf20Sopenharmony_ci		asus_wmi_tablet_sw_init(asus, ASUS_WMI_DEVID_LID_FLIP, NOTIFY_LID_FLIP);
4118c2ecf20Sopenharmony_ci		break;
4128c2ecf20Sopenharmony_ci	case asus_wmi_lid_flip_rog_devid:
4138c2ecf20Sopenharmony_ci		asus_wmi_tablet_sw_init(asus, ASUS_WMI_DEVID_LID_FLIP_ROG, NOTIFY_LID_FLIP_ROG);
4148c2ecf20Sopenharmony_ci		break;
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	err = input_register_device(asus->inputdev);
4188c2ecf20Sopenharmony_ci	if (err)
4198c2ecf20Sopenharmony_ci		goto err_free_dev;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	return 0;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cierr_free_dev:
4248c2ecf20Sopenharmony_ci	input_free_device(asus->inputdev);
4258c2ecf20Sopenharmony_ci	return err;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic void asus_wmi_input_exit(struct asus_wmi *asus)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	if (asus->inputdev)
4318c2ecf20Sopenharmony_ci		input_unregister_device(asus->inputdev);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	asus->inputdev = NULL;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci/* Tablet mode ****************************************************************/
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic void asus_wmi_tablet_mode_get_state(struct asus_wmi *asus)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	int result;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	if (!asus->tablet_switch_dev_id)
4438c2ecf20Sopenharmony_ci		return;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	result = asus_wmi_get_devstate_simple(asus, asus->tablet_switch_dev_id);
4468c2ecf20Sopenharmony_ci	if (result >= 0)
4478c2ecf20Sopenharmony_ci		asus_wmi_tablet_sw_report(asus, result);
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci/* dGPU ********************************************************************/
4518c2ecf20Sopenharmony_cistatic int dgpu_disable_check_present(struct asus_wmi *asus)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	u32 result;
4548c2ecf20Sopenharmony_ci	int err;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	asus->dgpu_disable_available = false;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_DGPU, &result);
4598c2ecf20Sopenharmony_ci	if (err) {
4608c2ecf20Sopenharmony_ci		if (err == -ENODEV)
4618c2ecf20Sopenharmony_ci			return 0;
4628c2ecf20Sopenharmony_ci		return err;
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	if (result & ASUS_WMI_DSTS_PRESENCE_BIT) {
4668c2ecf20Sopenharmony_ci		asus->dgpu_disable_available = true;
4678c2ecf20Sopenharmony_ci		asus->dgpu_disable = result & ASUS_WMI_DSTS_STATUS_BIT;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	return 0;
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic int dgpu_disable_write(struct asus_wmi *asus)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	u32 retval;
4768c2ecf20Sopenharmony_ci	u8 value;
4778c2ecf20Sopenharmony_ci	int err;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	/* Don't rely on type conversion */
4808c2ecf20Sopenharmony_ci	value = asus->dgpu_disable ? 1 : 0;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_DGPU, value, &retval);
4838c2ecf20Sopenharmony_ci	if (err) {
4848c2ecf20Sopenharmony_ci		pr_warn("Failed to set dgpu disable: %d\n", err);
4858c2ecf20Sopenharmony_ci		return err;
4868c2ecf20Sopenharmony_ci	}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (retval > 1 || retval < 0) {
4898c2ecf20Sopenharmony_ci		pr_warn("Failed to set dgpu disable (retval): 0x%x\n", retval);
4908c2ecf20Sopenharmony_ci		return -EIO;
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "dgpu_disable");
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	return 0;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic ssize_t dgpu_disable_show(struct device *dev,
4998c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(dev);
5028c2ecf20Sopenharmony_ci	u8 mode = asus->dgpu_disable;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	return sysfs_emit(buf, "%d\n", mode);
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci/*
5088c2ecf20Sopenharmony_ci * A user may be required to store the value twice, typcial store first, then
5098c2ecf20Sopenharmony_ci * rescan PCI bus to activate power, then store a second time to save correctly.
5108c2ecf20Sopenharmony_ci * The reason for this is that an extra code path in the ACPI is enabled when
5118c2ecf20Sopenharmony_ci * the device and bus are powered.
5128c2ecf20Sopenharmony_ci */
5138c2ecf20Sopenharmony_cistatic ssize_t dgpu_disable_store(struct device *dev,
5148c2ecf20Sopenharmony_ci				    struct device_attribute *attr,
5158c2ecf20Sopenharmony_ci				    const char *buf, size_t count)
5168c2ecf20Sopenharmony_ci{
5178c2ecf20Sopenharmony_ci	bool disable;
5188c2ecf20Sopenharmony_ci	int result;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(dev);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	result = kstrtobool(buf, &disable);
5238c2ecf20Sopenharmony_ci	if (result)
5248c2ecf20Sopenharmony_ci		return result;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	asus->dgpu_disable = disable;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	result = dgpu_disable_write(asus);
5298c2ecf20Sopenharmony_ci	if (result)
5308c2ecf20Sopenharmony_ci		return result;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	return count;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(dgpu_disable);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci/* Battery ********************************************************************/
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci/* The battery maximum charging percentage */
5408c2ecf20Sopenharmony_cistatic int charge_end_threshold;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_cistatic ssize_t charge_control_end_threshold_store(struct device *dev,
5438c2ecf20Sopenharmony_ci						  struct device_attribute *attr,
5448c2ecf20Sopenharmony_ci						  const char *buf, size_t count)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	int value, ret, rv;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	ret = kstrtouint(buf, 10, &value);
5498c2ecf20Sopenharmony_ci	if (ret)
5508c2ecf20Sopenharmony_ci		return ret;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	if (value < 0 || value > 100)
5538c2ecf20Sopenharmony_ci		return -EINVAL;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, value, &rv);
5568c2ecf20Sopenharmony_ci	if (ret)
5578c2ecf20Sopenharmony_ci		return ret;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	if (rv != 1)
5608c2ecf20Sopenharmony_ci		return -EIO;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	/* There isn't any method in the DSDT to read the threshold, so we
5638c2ecf20Sopenharmony_ci	 * save the threshold.
5648c2ecf20Sopenharmony_ci	 */
5658c2ecf20Sopenharmony_ci	charge_end_threshold = value;
5668c2ecf20Sopenharmony_ci	return count;
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_cistatic ssize_t charge_control_end_threshold_show(struct device *device,
5708c2ecf20Sopenharmony_ci						 struct device_attribute *attr,
5718c2ecf20Sopenharmony_ci						 char *buf)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", charge_end_threshold);
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(charge_control_end_threshold);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_cistatic int asus_wmi_battery_add(struct power_supply *battery)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci	/* The WMI method does not provide a way to specific a battery, so we
5818c2ecf20Sopenharmony_ci	 * just assume it is the first battery.
5828c2ecf20Sopenharmony_ci	 * Note: On some newer ASUS laptops (Zenbook UM431DA), the primary/first
5838c2ecf20Sopenharmony_ci	 * battery is named BATT.
5848c2ecf20Sopenharmony_ci	 */
5858c2ecf20Sopenharmony_ci	if (strcmp(battery->desc->name, "BAT0") != 0 &&
5868c2ecf20Sopenharmony_ci	    strcmp(battery->desc->name, "BAT1") != 0 &&
5878c2ecf20Sopenharmony_ci	    strcmp(battery->desc->name, "BATC") != 0 &&
5888c2ecf20Sopenharmony_ci	    strcmp(battery->desc->name, "BATT") != 0)
5898c2ecf20Sopenharmony_ci		return -ENODEV;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	if (device_create_file(&battery->dev,
5928c2ecf20Sopenharmony_ci	    &dev_attr_charge_control_end_threshold))
5938c2ecf20Sopenharmony_ci		return -ENODEV;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	/* The charge threshold is only reset when the system is power cycled,
5968c2ecf20Sopenharmony_ci	 * and we can't get the current threshold so let set it to 100% when
5978c2ecf20Sopenharmony_ci	 * a battery is added.
5988c2ecf20Sopenharmony_ci	 */
5998c2ecf20Sopenharmony_ci	asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, 100, NULL);
6008c2ecf20Sopenharmony_ci	charge_end_threshold = 100;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	return 0;
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic int asus_wmi_battery_remove(struct power_supply *battery)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	device_remove_file(&battery->dev,
6088c2ecf20Sopenharmony_ci			   &dev_attr_charge_control_end_threshold);
6098c2ecf20Sopenharmony_ci	return 0;
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic struct acpi_battery_hook battery_hook = {
6138c2ecf20Sopenharmony_ci	.add_battery = asus_wmi_battery_add,
6148c2ecf20Sopenharmony_ci	.remove_battery = asus_wmi_battery_remove,
6158c2ecf20Sopenharmony_ci	.name = "ASUS Battery Extension",
6168c2ecf20Sopenharmony_ci};
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_cistatic void asus_wmi_battery_init(struct asus_wmi *asus)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	asus->battery_rsoc_available = false;
6218c2ecf20Sopenharmony_ci	if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_RSOC)) {
6228c2ecf20Sopenharmony_ci		asus->battery_rsoc_available = true;
6238c2ecf20Sopenharmony_ci		battery_hook_register(&battery_hook);
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cistatic void asus_wmi_battery_exit(struct asus_wmi *asus)
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	if (asus->battery_rsoc_available)
6308c2ecf20Sopenharmony_ci		battery_hook_unregister(&battery_hook);
6318c2ecf20Sopenharmony_ci}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci/* LEDs ***********************************************************************/
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci/*
6368c2ecf20Sopenharmony_ci * These functions actually update the LED's, and are called from a
6378c2ecf20Sopenharmony_ci * workqueue. By doing this as separate work rather than when the LED
6388c2ecf20Sopenharmony_ci * subsystem asks, we avoid messing with the Asus ACPI stuff during a
6398c2ecf20Sopenharmony_ci * potentially bad time, such as a timer interrupt.
6408c2ecf20Sopenharmony_ci */
6418c2ecf20Sopenharmony_cistatic void tpd_led_update(struct work_struct *work)
6428c2ecf20Sopenharmony_ci{
6438c2ecf20Sopenharmony_ci	int ctrl_param;
6448c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	asus = container_of(work, struct asus_wmi, tpd_led_work);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	ctrl_param = asus->tpd_led_wk;
6498c2ecf20Sopenharmony_ci	asus_wmi_set_devstate(ASUS_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL);
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic void tpd_led_set(struct led_classdev *led_cdev,
6538c2ecf20Sopenharmony_ci			enum led_brightness value)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	asus = container_of(led_cdev, struct asus_wmi, tpd_led);
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	asus->tpd_led_wk = !!value;
6608c2ecf20Sopenharmony_ci	queue_work(asus->led_workqueue, &asus->tpd_led_work);
6618c2ecf20Sopenharmony_ci}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic int read_tpd_led_state(struct asus_wmi *asus)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	return asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_TOUCHPAD_LED);
6668c2ecf20Sopenharmony_ci}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_cistatic enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	asus = container_of(led_cdev, struct asus_wmi, tpd_led);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	return read_tpd_led_state(asus);
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_cistatic void kbd_led_update(struct asus_wmi *asus)
6788c2ecf20Sopenharmony_ci{
6798c2ecf20Sopenharmony_ci	int ctrl_param = 0;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
6828c2ecf20Sopenharmony_ci	asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	int retval;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	/*
6908c2ecf20Sopenharmony_ci	 * bits 0-2: level
6918c2ecf20Sopenharmony_ci	 * bit 7: light on/off
6928c2ecf20Sopenharmony_ci	 * bit 8-10: environment (0: dark, 1: normal, 2: light)
6938c2ecf20Sopenharmony_ci	 * bit 17: status unknown
6948c2ecf20Sopenharmony_ci	 */
6958c2ecf20Sopenharmony_ci	retval = asus_wmi_get_devstate_bits(asus, ASUS_WMI_DEVID_KBD_BACKLIGHT,
6968c2ecf20Sopenharmony_ci					    0xFFFF);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	/* Unknown status is considered as off */
6998c2ecf20Sopenharmony_ci	if (retval == 0x8000)
7008c2ecf20Sopenharmony_ci		retval = 0;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	if (retval < 0)
7038c2ecf20Sopenharmony_ci		return retval;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	if (level)
7068c2ecf20Sopenharmony_ci		*level = retval & 0x7F;
7078c2ecf20Sopenharmony_ci	if (env)
7088c2ecf20Sopenharmony_ci		*env = (retval >> 8) & 0x7F;
7098c2ecf20Sopenharmony_ci	return 0;
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic void do_kbd_led_set(struct led_classdev *led_cdev, int value)
7138c2ecf20Sopenharmony_ci{
7148c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
7158c2ecf20Sopenharmony_ci	int max_level;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	asus = container_of(led_cdev, struct asus_wmi, kbd_led);
7188c2ecf20Sopenharmony_ci	max_level = asus->kbd_led.max_brightness;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	asus->kbd_led_wk = clamp_val(value, 0, max_level);
7218c2ecf20Sopenharmony_ci	kbd_led_update(asus);
7228c2ecf20Sopenharmony_ci}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_cistatic void kbd_led_set(struct led_classdev *led_cdev,
7258c2ecf20Sopenharmony_ci			enum led_brightness value)
7268c2ecf20Sopenharmony_ci{
7278c2ecf20Sopenharmony_ci	/* Prevent disabling keyboard backlight on module unregister */
7288c2ecf20Sopenharmony_ci	if (led_cdev->flags & LED_UNREGISTERING)
7298c2ecf20Sopenharmony_ci		return;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	do_kbd_led_set(led_cdev, value);
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_cistatic void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value)
7358c2ecf20Sopenharmony_ci{
7368c2ecf20Sopenharmony_ci	struct led_classdev *led_cdev = &asus->kbd_led;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	do_kbd_led_set(led_cdev, value);
7398c2ecf20Sopenharmony_ci	led_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk);
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_cistatic enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
7438c2ecf20Sopenharmony_ci{
7448c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
7458c2ecf20Sopenharmony_ci	int retval, value;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	asus = container_of(led_cdev, struct asus_wmi, kbd_led);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	retval = kbd_led_read(asus, &value, NULL);
7508c2ecf20Sopenharmony_ci	if (retval < 0)
7518c2ecf20Sopenharmony_ci		return retval;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	return value;
7548c2ecf20Sopenharmony_ci}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_cistatic int wlan_led_unknown_state(struct asus_wmi *asus)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	u32 result;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	return result & ASUS_WMI_DSTS_UNKNOWN_BIT;
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cistatic void wlan_led_update(struct work_struct *work)
7668c2ecf20Sopenharmony_ci{
7678c2ecf20Sopenharmony_ci	int ctrl_param;
7688c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	asus = container_of(work, struct asus_wmi, wlan_led_work);
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	ctrl_param = asus->wlan_led_wk;
7738c2ecf20Sopenharmony_ci	asus_wmi_set_devstate(ASUS_WMI_DEVID_WIRELESS_LED, ctrl_param, NULL);
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_cistatic void wlan_led_set(struct led_classdev *led_cdev,
7778c2ecf20Sopenharmony_ci			 enum led_brightness value)
7788c2ecf20Sopenharmony_ci{
7798c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	asus = container_of(led_cdev, struct asus_wmi, wlan_led);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	asus->wlan_led_wk = !!value;
7848c2ecf20Sopenharmony_ci	queue_work(asus->led_workqueue, &asus->wlan_led_work);
7858c2ecf20Sopenharmony_ci}
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_cistatic enum led_brightness wlan_led_get(struct led_classdev *led_cdev)
7888c2ecf20Sopenharmony_ci{
7898c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
7908c2ecf20Sopenharmony_ci	u32 result;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	asus = container_of(led_cdev, struct asus_wmi, wlan_led);
7938c2ecf20Sopenharmony_ci	asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_cistatic void lightbar_led_update(struct work_struct *work)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
8018c2ecf20Sopenharmony_ci	int ctrl_param;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	asus = container_of(work, struct asus_wmi, lightbar_led_work);
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	ctrl_param = asus->lightbar_led_wk;
8068c2ecf20Sopenharmony_ci	asus_wmi_set_devstate(ASUS_WMI_DEVID_LIGHTBAR, ctrl_param, NULL);
8078c2ecf20Sopenharmony_ci}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_cistatic void lightbar_led_set(struct led_classdev *led_cdev,
8108c2ecf20Sopenharmony_ci			     enum led_brightness value)
8118c2ecf20Sopenharmony_ci{
8128c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	asus = container_of(led_cdev, struct asus_wmi, lightbar_led);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	asus->lightbar_led_wk = !!value;
8178c2ecf20Sopenharmony_ci	queue_work(asus->led_workqueue, &asus->lightbar_led_work);
8188c2ecf20Sopenharmony_ci}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_cistatic enum led_brightness lightbar_led_get(struct led_classdev *led_cdev)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
8238c2ecf20Sopenharmony_ci	u32 result;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	asus = container_of(led_cdev, struct asus_wmi, lightbar_led);
8268c2ecf20Sopenharmony_ci	asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result);
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	return result & ASUS_WMI_DSTS_LIGHTBAR_MASK;
8298c2ecf20Sopenharmony_ci}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_cistatic void asus_wmi_led_exit(struct asus_wmi *asus)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	led_classdev_unregister(&asus->kbd_led);
8348c2ecf20Sopenharmony_ci	led_classdev_unregister(&asus->tpd_led);
8358c2ecf20Sopenharmony_ci	led_classdev_unregister(&asus->wlan_led);
8368c2ecf20Sopenharmony_ci	led_classdev_unregister(&asus->lightbar_led);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	if (asus->led_workqueue)
8398c2ecf20Sopenharmony_ci		destroy_workqueue(asus->led_workqueue);
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_cistatic int asus_wmi_led_init(struct asus_wmi *asus)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	int rv = 0, led_val;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	asus->led_workqueue = create_singlethread_workqueue("led_workqueue");
8478c2ecf20Sopenharmony_ci	if (!asus->led_workqueue)
8488c2ecf20Sopenharmony_ci		return -ENOMEM;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	if (read_tpd_led_state(asus) >= 0) {
8518c2ecf20Sopenharmony_ci		INIT_WORK(&asus->tpd_led_work, tpd_led_update);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci		asus->tpd_led.name = "asus::touchpad";
8548c2ecf20Sopenharmony_ci		asus->tpd_led.brightness_set = tpd_led_set;
8558c2ecf20Sopenharmony_ci		asus->tpd_led.brightness_get = tpd_led_get;
8568c2ecf20Sopenharmony_ci		asus->tpd_led.max_brightness = 1;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci		rv = led_classdev_register(&asus->platform_device->dev,
8598c2ecf20Sopenharmony_ci					   &asus->tpd_led);
8608c2ecf20Sopenharmony_ci		if (rv)
8618c2ecf20Sopenharmony_ci			goto error;
8628c2ecf20Sopenharmony_ci	}
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	if (!kbd_led_read(asus, &led_val, NULL)) {
8658c2ecf20Sopenharmony_ci		asus->kbd_led_wk = led_val;
8668c2ecf20Sopenharmony_ci		asus->kbd_led.name = "asus::kbd_backlight";
8678c2ecf20Sopenharmony_ci		asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
8688c2ecf20Sopenharmony_ci		asus->kbd_led.brightness_set = kbd_led_set;
8698c2ecf20Sopenharmony_ci		asus->kbd_led.brightness_get = kbd_led_get;
8708c2ecf20Sopenharmony_ci		asus->kbd_led.max_brightness = 3;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci		rv = led_classdev_register(&asus->platform_device->dev,
8738c2ecf20Sopenharmony_ci					   &asus->kbd_led);
8748c2ecf20Sopenharmony_ci		if (rv)
8758c2ecf20Sopenharmony_ci			goto error;
8768c2ecf20Sopenharmony_ci	}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED)
8798c2ecf20Sopenharmony_ci			&& (asus->driver->quirks->wapf > 0)) {
8808c2ecf20Sopenharmony_ci		INIT_WORK(&asus->wlan_led_work, wlan_led_update);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci		asus->wlan_led.name = "asus::wlan";
8838c2ecf20Sopenharmony_ci		asus->wlan_led.brightness_set = wlan_led_set;
8848c2ecf20Sopenharmony_ci		if (!wlan_led_unknown_state(asus))
8858c2ecf20Sopenharmony_ci			asus->wlan_led.brightness_get = wlan_led_get;
8868c2ecf20Sopenharmony_ci		asus->wlan_led.flags = LED_CORE_SUSPENDRESUME;
8878c2ecf20Sopenharmony_ci		asus->wlan_led.max_brightness = 1;
8888c2ecf20Sopenharmony_ci		asus->wlan_led.default_trigger = "asus-wlan";
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci		rv = led_classdev_register(&asus->platform_device->dev,
8918c2ecf20Sopenharmony_ci					   &asus->wlan_led);
8928c2ecf20Sopenharmony_ci		if (rv)
8938c2ecf20Sopenharmony_ci			goto error;
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_LIGHTBAR)) {
8978c2ecf20Sopenharmony_ci		INIT_WORK(&asus->lightbar_led_work, lightbar_led_update);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci		asus->lightbar_led.name = "asus::lightbar";
9008c2ecf20Sopenharmony_ci		asus->lightbar_led.brightness_set = lightbar_led_set;
9018c2ecf20Sopenharmony_ci		asus->lightbar_led.brightness_get = lightbar_led_get;
9028c2ecf20Sopenharmony_ci		asus->lightbar_led.max_brightness = 1;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci		rv = led_classdev_register(&asus->platform_device->dev,
9058c2ecf20Sopenharmony_ci					   &asus->lightbar_led);
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_cierror:
9098c2ecf20Sopenharmony_ci	if (rv)
9108c2ecf20Sopenharmony_ci		asus_wmi_led_exit(asus);
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	return rv;
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci/* RF *************************************************************************/
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci/*
9188c2ecf20Sopenharmony_ci * PCI hotplug (for wlan rfkill)
9198c2ecf20Sopenharmony_ci */
9208c2ecf20Sopenharmony_cistatic bool asus_wlan_rfkill_blocked(struct asus_wmi *asus)
9218c2ecf20Sopenharmony_ci{
9228c2ecf20Sopenharmony_ci	int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	if (result < 0)
9258c2ecf20Sopenharmony_ci		return false;
9268c2ecf20Sopenharmony_ci	return !result;
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_cistatic void asus_rfkill_hotplug(struct asus_wmi *asus)
9308c2ecf20Sopenharmony_ci{
9318c2ecf20Sopenharmony_ci	struct pci_dev *dev;
9328c2ecf20Sopenharmony_ci	struct pci_bus *bus;
9338c2ecf20Sopenharmony_ci	bool blocked;
9348c2ecf20Sopenharmony_ci	bool absent;
9358c2ecf20Sopenharmony_ci	u32 l;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	mutex_lock(&asus->wmi_lock);
9388c2ecf20Sopenharmony_ci	blocked = asus_wlan_rfkill_blocked(asus);
9398c2ecf20Sopenharmony_ci	mutex_unlock(&asus->wmi_lock);
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	mutex_lock(&asus->hotplug_lock);
9428c2ecf20Sopenharmony_ci	pci_lock_rescan_remove();
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	if (asus->wlan.rfkill)
9458c2ecf20Sopenharmony_ci		rfkill_set_sw_state(asus->wlan.rfkill, blocked);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	if (asus->hotplug_slot.ops) {
9488c2ecf20Sopenharmony_ci		bus = pci_find_bus(0, 1);
9498c2ecf20Sopenharmony_ci		if (!bus) {
9508c2ecf20Sopenharmony_ci			pr_warn("Unable to find PCI bus 1?\n");
9518c2ecf20Sopenharmony_ci			goto out_unlock;
9528c2ecf20Sopenharmony_ci		}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci		if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
9558c2ecf20Sopenharmony_ci			pr_err("Unable to read PCI config space?\n");
9568c2ecf20Sopenharmony_ci			goto out_unlock;
9578c2ecf20Sopenharmony_ci		}
9588c2ecf20Sopenharmony_ci		absent = (l == 0xffffffff);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci		if (blocked != absent) {
9618c2ecf20Sopenharmony_ci			pr_warn("BIOS says wireless lan is %s, "
9628c2ecf20Sopenharmony_ci				"but the pci device is %s\n",
9638c2ecf20Sopenharmony_ci				blocked ? "blocked" : "unblocked",
9648c2ecf20Sopenharmony_ci				absent ? "absent" : "present");
9658c2ecf20Sopenharmony_ci			pr_warn("skipped wireless hotplug as probably "
9668c2ecf20Sopenharmony_ci				"inappropriate for this model\n");
9678c2ecf20Sopenharmony_ci			goto out_unlock;
9688c2ecf20Sopenharmony_ci		}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci		if (!blocked) {
9718c2ecf20Sopenharmony_ci			dev = pci_get_slot(bus, 0);
9728c2ecf20Sopenharmony_ci			if (dev) {
9738c2ecf20Sopenharmony_ci				/* Device already present */
9748c2ecf20Sopenharmony_ci				pci_dev_put(dev);
9758c2ecf20Sopenharmony_ci				goto out_unlock;
9768c2ecf20Sopenharmony_ci			}
9778c2ecf20Sopenharmony_ci			dev = pci_scan_single_device(bus, 0);
9788c2ecf20Sopenharmony_ci			if (dev) {
9798c2ecf20Sopenharmony_ci				pci_bus_assign_resources(bus);
9808c2ecf20Sopenharmony_ci				pci_bus_add_device(dev);
9818c2ecf20Sopenharmony_ci			}
9828c2ecf20Sopenharmony_ci		} else {
9838c2ecf20Sopenharmony_ci			dev = pci_get_slot(bus, 0);
9848c2ecf20Sopenharmony_ci			if (dev) {
9858c2ecf20Sopenharmony_ci				pci_stop_and_remove_bus_device(dev);
9868c2ecf20Sopenharmony_ci				pci_dev_put(dev);
9878c2ecf20Sopenharmony_ci			}
9888c2ecf20Sopenharmony_ci		}
9898c2ecf20Sopenharmony_ci	}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ciout_unlock:
9928c2ecf20Sopenharmony_ci	pci_unlock_rescan_remove();
9938c2ecf20Sopenharmony_ci	mutex_unlock(&asus->hotplug_lock);
9948c2ecf20Sopenharmony_ci}
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_cistatic void asus_rfkill_notify(acpi_handle handle, u32 event, void *data)
9978c2ecf20Sopenharmony_ci{
9988c2ecf20Sopenharmony_ci	struct asus_wmi *asus = data;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	if (event != ACPI_NOTIFY_BUS_CHECK)
10018c2ecf20Sopenharmony_ci		return;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	/*
10048c2ecf20Sopenharmony_ci	 * We can't call directly asus_rfkill_hotplug because most
10058c2ecf20Sopenharmony_ci	 * of the time WMBC is still being executed and not reetrant.
10068c2ecf20Sopenharmony_ci	 * There is currently no way to tell ACPICA that  we want this
10078c2ecf20Sopenharmony_ci	 * method to be serialized, we schedule a asus_rfkill_hotplug
10088c2ecf20Sopenharmony_ci	 * call later, in a safer context.
10098c2ecf20Sopenharmony_ci	 */
10108c2ecf20Sopenharmony_ci	queue_work(asus->hotplug_workqueue, &asus->hotplug_work);
10118c2ecf20Sopenharmony_ci}
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_cistatic int asus_register_rfkill_notifier(struct asus_wmi *asus, char *node)
10148c2ecf20Sopenharmony_ci{
10158c2ecf20Sopenharmony_ci	acpi_status status;
10168c2ecf20Sopenharmony_ci	acpi_handle handle;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	status = acpi_get_handle(NULL, node, &handle);
10198c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
10208c2ecf20Sopenharmony_ci		return -ENODEV;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
10238c2ecf20Sopenharmony_ci					     asus_rfkill_notify, asus);
10248c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
10258c2ecf20Sopenharmony_ci		pr_warn("Failed to register notify on %s\n", node);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	return 0;
10288c2ecf20Sopenharmony_ci}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_cistatic void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node)
10318c2ecf20Sopenharmony_ci{
10328c2ecf20Sopenharmony_ci	acpi_status status = AE_OK;
10338c2ecf20Sopenharmony_ci	acpi_handle handle;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	status = acpi_get_handle(NULL, node, &handle);
10368c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
10378c2ecf20Sopenharmony_ci		return;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	status = acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
10408c2ecf20Sopenharmony_ci					    asus_rfkill_notify);
10418c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
10428c2ecf20Sopenharmony_ci		pr_err("Error removing rfkill notify handler %s\n", node);
10438c2ecf20Sopenharmony_ci}
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_cistatic int asus_get_adapter_status(struct hotplug_slot *hotplug_slot,
10468c2ecf20Sopenharmony_ci				   u8 *value)
10478c2ecf20Sopenharmony_ci{
10488c2ecf20Sopenharmony_ci	struct asus_wmi *asus = container_of(hotplug_slot,
10498c2ecf20Sopenharmony_ci					     struct asus_wmi, hotplug_slot);
10508c2ecf20Sopenharmony_ci	int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	if (result < 0)
10538c2ecf20Sopenharmony_ci		return result;
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	*value = !!result;
10568c2ecf20Sopenharmony_ci	return 0;
10578c2ecf20Sopenharmony_ci}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_cistatic const struct hotplug_slot_ops asus_hotplug_slot_ops = {
10608c2ecf20Sopenharmony_ci	.get_adapter_status = asus_get_adapter_status,
10618c2ecf20Sopenharmony_ci	.get_power_status = asus_get_adapter_status,
10628c2ecf20Sopenharmony_ci};
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_cistatic void asus_hotplug_work(struct work_struct *work)
10658c2ecf20Sopenharmony_ci{
10668c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	asus = container_of(work, struct asus_wmi, hotplug_work);
10698c2ecf20Sopenharmony_ci	asus_rfkill_hotplug(asus);
10708c2ecf20Sopenharmony_ci}
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_cistatic int asus_setup_pci_hotplug(struct asus_wmi *asus)
10738c2ecf20Sopenharmony_ci{
10748c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
10758c2ecf20Sopenharmony_ci	struct pci_bus *bus = pci_find_bus(0, 1);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	if (!bus) {
10788c2ecf20Sopenharmony_ci		pr_err("Unable to find wifi PCI bus\n");
10798c2ecf20Sopenharmony_ci		return -ENODEV;
10808c2ecf20Sopenharmony_ci	}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	asus->hotplug_workqueue =
10838c2ecf20Sopenharmony_ci	    create_singlethread_workqueue("hotplug_workqueue");
10848c2ecf20Sopenharmony_ci	if (!asus->hotplug_workqueue)
10858c2ecf20Sopenharmony_ci		goto error_workqueue;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	INIT_WORK(&asus->hotplug_work, asus_hotplug_work);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	asus->hotplug_slot.ops = &asus_hotplug_slot_ops;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	ret = pci_hp_register(&asus->hotplug_slot, bus, 0, "asus-wifi");
10928c2ecf20Sopenharmony_ci	if (ret) {
10938c2ecf20Sopenharmony_ci		pr_err("Unable to register hotplug slot - %d\n", ret);
10948c2ecf20Sopenharmony_ci		goto error_register;
10958c2ecf20Sopenharmony_ci	}
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	return 0;
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_cierror_register:
11008c2ecf20Sopenharmony_ci	asus->hotplug_slot.ops = NULL;
11018c2ecf20Sopenharmony_ci	destroy_workqueue(asus->hotplug_workqueue);
11028c2ecf20Sopenharmony_cierror_workqueue:
11038c2ecf20Sopenharmony_ci	return ret;
11048c2ecf20Sopenharmony_ci}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci/*
11078c2ecf20Sopenharmony_ci * Rfkill devices
11088c2ecf20Sopenharmony_ci */
11098c2ecf20Sopenharmony_cistatic int asus_rfkill_set(void *data, bool blocked)
11108c2ecf20Sopenharmony_ci{
11118c2ecf20Sopenharmony_ci	struct asus_rfkill *priv = data;
11128c2ecf20Sopenharmony_ci	u32 ctrl_param = !blocked;
11138c2ecf20Sopenharmony_ci	u32 dev_id = priv->dev_id;
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	/*
11168c2ecf20Sopenharmony_ci	 * If the user bit is set, BIOS can't set and record the wlan status,
11178c2ecf20Sopenharmony_ci	 * it will report the value read from id ASUS_WMI_DEVID_WLAN_LED
11188c2ecf20Sopenharmony_ci	 * while we query the wlan status through WMI(ASUS_WMI_DEVID_WLAN).
11198c2ecf20Sopenharmony_ci	 * So, we have to record wlan status in id ASUS_WMI_DEVID_WLAN_LED
11208c2ecf20Sopenharmony_ci	 * while setting the wlan status through WMI.
11218c2ecf20Sopenharmony_ci	 * This is also the behavior that windows app will do.
11228c2ecf20Sopenharmony_ci	 */
11238c2ecf20Sopenharmony_ci	if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
11248c2ecf20Sopenharmony_ci	     priv->asus->driver->wlan_ctrl_by_user)
11258c2ecf20Sopenharmony_ci		dev_id = ASUS_WMI_DEVID_WLAN_LED;
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	return asus_wmi_set_devstate(dev_id, ctrl_param, NULL);
11288c2ecf20Sopenharmony_ci}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_cistatic void asus_rfkill_query(struct rfkill *rfkill, void *data)
11318c2ecf20Sopenharmony_ci{
11328c2ecf20Sopenharmony_ci	struct asus_rfkill *priv = data;
11338c2ecf20Sopenharmony_ci	int result;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	result = asus_wmi_get_devstate_simple(priv->asus, priv->dev_id);
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	if (result < 0)
11388c2ecf20Sopenharmony_ci		return;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	rfkill_set_sw_state(priv->rfkill, !result);
11418c2ecf20Sopenharmony_ci}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_cistatic int asus_rfkill_wlan_set(void *data, bool blocked)
11448c2ecf20Sopenharmony_ci{
11458c2ecf20Sopenharmony_ci	struct asus_rfkill *priv = data;
11468c2ecf20Sopenharmony_ci	struct asus_wmi *asus = priv->asus;
11478c2ecf20Sopenharmony_ci	int ret;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	/*
11508c2ecf20Sopenharmony_ci	 * This handler is enabled only if hotplug is enabled.
11518c2ecf20Sopenharmony_ci	 * In this case, the asus_wmi_set_devstate() will
11528c2ecf20Sopenharmony_ci	 * trigger a wmi notification and we need to wait
11538c2ecf20Sopenharmony_ci	 * this call to finish before being able to call
11548c2ecf20Sopenharmony_ci	 * any wmi method
11558c2ecf20Sopenharmony_ci	 */
11568c2ecf20Sopenharmony_ci	mutex_lock(&asus->wmi_lock);
11578c2ecf20Sopenharmony_ci	ret = asus_rfkill_set(data, blocked);
11588c2ecf20Sopenharmony_ci	mutex_unlock(&asus->wmi_lock);
11598c2ecf20Sopenharmony_ci	return ret;
11608c2ecf20Sopenharmony_ci}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_cistatic const struct rfkill_ops asus_rfkill_wlan_ops = {
11638c2ecf20Sopenharmony_ci	.set_block = asus_rfkill_wlan_set,
11648c2ecf20Sopenharmony_ci	.query = asus_rfkill_query,
11658c2ecf20Sopenharmony_ci};
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_cistatic const struct rfkill_ops asus_rfkill_ops = {
11688c2ecf20Sopenharmony_ci	.set_block = asus_rfkill_set,
11698c2ecf20Sopenharmony_ci	.query = asus_rfkill_query,
11708c2ecf20Sopenharmony_ci};
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_cistatic int asus_new_rfkill(struct asus_wmi *asus,
11738c2ecf20Sopenharmony_ci			   struct asus_rfkill *arfkill,
11748c2ecf20Sopenharmony_ci			   const char *name, enum rfkill_type type, int dev_id)
11758c2ecf20Sopenharmony_ci{
11768c2ecf20Sopenharmony_ci	int result = asus_wmi_get_devstate_simple(asus, dev_id);
11778c2ecf20Sopenharmony_ci	struct rfkill **rfkill = &arfkill->rfkill;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	if (result < 0)
11808c2ecf20Sopenharmony_ci		return result;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	arfkill->dev_id = dev_id;
11838c2ecf20Sopenharmony_ci	arfkill->asus = asus;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	if (dev_id == ASUS_WMI_DEVID_WLAN &&
11868c2ecf20Sopenharmony_ci	    asus->driver->quirks->hotplug_wireless)
11878c2ecf20Sopenharmony_ci		*rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,
11888c2ecf20Sopenharmony_ci				       &asus_rfkill_wlan_ops, arfkill);
11898c2ecf20Sopenharmony_ci	else
11908c2ecf20Sopenharmony_ci		*rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,
11918c2ecf20Sopenharmony_ci				       &asus_rfkill_ops, arfkill);
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	if (!*rfkill)
11948c2ecf20Sopenharmony_ci		return -EINVAL;
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
11978c2ecf20Sopenharmony_ci			(asus->driver->quirks->wapf > 0))
11988c2ecf20Sopenharmony_ci		rfkill_set_led_trigger_name(*rfkill, "asus-wlan");
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	rfkill_init_sw_state(*rfkill, !result);
12018c2ecf20Sopenharmony_ci	result = rfkill_register(*rfkill);
12028c2ecf20Sopenharmony_ci	if (result) {
12038c2ecf20Sopenharmony_ci		rfkill_destroy(*rfkill);
12048c2ecf20Sopenharmony_ci		*rfkill = NULL;
12058c2ecf20Sopenharmony_ci		return result;
12068c2ecf20Sopenharmony_ci	}
12078c2ecf20Sopenharmony_ci	return 0;
12088c2ecf20Sopenharmony_ci}
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_cistatic void asus_wmi_rfkill_exit(struct asus_wmi *asus)
12118c2ecf20Sopenharmony_ci{
12128c2ecf20Sopenharmony_ci	if (asus->driver->wlan_ctrl_by_user && ashs_present())
12138c2ecf20Sopenharmony_ci		return;
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P5");
12168c2ecf20Sopenharmony_ci	asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P6");
12178c2ecf20Sopenharmony_ci	asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P7");
12188c2ecf20Sopenharmony_ci	if (asus->wlan.rfkill) {
12198c2ecf20Sopenharmony_ci		rfkill_unregister(asus->wlan.rfkill);
12208c2ecf20Sopenharmony_ci		rfkill_destroy(asus->wlan.rfkill);
12218c2ecf20Sopenharmony_ci		asus->wlan.rfkill = NULL;
12228c2ecf20Sopenharmony_ci	}
12238c2ecf20Sopenharmony_ci	/*
12248c2ecf20Sopenharmony_ci	 * Refresh pci hotplug in case the rfkill state was changed after
12258c2ecf20Sopenharmony_ci	 * asus_unregister_rfkill_notifier()
12268c2ecf20Sopenharmony_ci	 */
12278c2ecf20Sopenharmony_ci	asus_rfkill_hotplug(asus);
12288c2ecf20Sopenharmony_ci	if (asus->hotplug_slot.ops)
12298c2ecf20Sopenharmony_ci		pci_hp_deregister(&asus->hotplug_slot);
12308c2ecf20Sopenharmony_ci	if (asus->hotplug_workqueue)
12318c2ecf20Sopenharmony_ci		destroy_workqueue(asus->hotplug_workqueue);
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	if (asus->bluetooth.rfkill) {
12348c2ecf20Sopenharmony_ci		rfkill_unregister(asus->bluetooth.rfkill);
12358c2ecf20Sopenharmony_ci		rfkill_destroy(asus->bluetooth.rfkill);
12368c2ecf20Sopenharmony_ci		asus->bluetooth.rfkill = NULL;
12378c2ecf20Sopenharmony_ci	}
12388c2ecf20Sopenharmony_ci	if (asus->wimax.rfkill) {
12398c2ecf20Sopenharmony_ci		rfkill_unregister(asus->wimax.rfkill);
12408c2ecf20Sopenharmony_ci		rfkill_destroy(asus->wimax.rfkill);
12418c2ecf20Sopenharmony_ci		asus->wimax.rfkill = NULL;
12428c2ecf20Sopenharmony_ci	}
12438c2ecf20Sopenharmony_ci	if (asus->wwan3g.rfkill) {
12448c2ecf20Sopenharmony_ci		rfkill_unregister(asus->wwan3g.rfkill);
12458c2ecf20Sopenharmony_ci		rfkill_destroy(asus->wwan3g.rfkill);
12468c2ecf20Sopenharmony_ci		asus->wwan3g.rfkill = NULL;
12478c2ecf20Sopenharmony_ci	}
12488c2ecf20Sopenharmony_ci	if (asus->gps.rfkill) {
12498c2ecf20Sopenharmony_ci		rfkill_unregister(asus->gps.rfkill);
12508c2ecf20Sopenharmony_ci		rfkill_destroy(asus->gps.rfkill);
12518c2ecf20Sopenharmony_ci		asus->gps.rfkill = NULL;
12528c2ecf20Sopenharmony_ci	}
12538c2ecf20Sopenharmony_ci	if (asus->uwb.rfkill) {
12548c2ecf20Sopenharmony_ci		rfkill_unregister(asus->uwb.rfkill);
12558c2ecf20Sopenharmony_ci		rfkill_destroy(asus->uwb.rfkill);
12568c2ecf20Sopenharmony_ci		asus->uwb.rfkill = NULL;
12578c2ecf20Sopenharmony_ci	}
12588c2ecf20Sopenharmony_ci}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_cistatic int asus_wmi_rfkill_init(struct asus_wmi *asus)
12618c2ecf20Sopenharmony_ci{
12628c2ecf20Sopenharmony_ci	int result = 0;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	mutex_init(&asus->hotplug_lock);
12658c2ecf20Sopenharmony_ci	mutex_init(&asus->wmi_lock);
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	result = asus_new_rfkill(asus, &asus->wlan, "asus-wlan",
12688c2ecf20Sopenharmony_ci				 RFKILL_TYPE_WLAN, ASUS_WMI_DEVID_WLAN);
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	if (result && result != -ENODEV)
12718c2ecf20Sopenharmony_ci		goto exit;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	result = asus_new_rfkill(asus, &asus->bluetooth,
12748c2ecf20Sopenharmony_ci				 "asus-bluetooth", RFKILL_TYPE_BLUETOOTH,
12758c2ecf20Sopenharmony_ci				 ASUS_WMI_DEVID_BLUETOOTH);
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	if (result && result != -ENODEV)
12788c2ecf20Sopenharmony_ci		goto exit;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	result = asus_new_rfkill(asus, &asus->wimax, "asus-wimax",
12818c2ecf20Sopenharmony_ci				 RFKILL_TYPE_WIMAX, ASUS_WMI_DEVID_WIMAX);
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	if (result && result != -ENODEV)
12848c2ecf20Sopenharmony_ci		goto exit;
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	result = asus_new_rfkill(asus, &asus->wwan3g, "asus-wwan3g",
12878c2ecf20Sopenharmony_ci				 RFKILL_TYPE_WWAN, ASUS_WMI_DEVID_WWAN3G);
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	if (result && result != -ENODEV)
12908c2ecf20Sopenharmony_ci		goto exit;
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	result = asus_new_rfkill(asus, &asus->gps, "asus-gps",
12938c2ecf20Sopenharmony_ci				 RFKILL_TYPE_GPS, ASUS_WMI_DEVID_GPS);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	if (result && result != -ENODEV)
12968c2ecf20Sopenharmony_ci		goto exit;
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	result = asus_new_rfkill(asus, &asus->uwb, "asus-uwb",
12998c2ecf20Sopenharmony_ci				 RFKILL_TYPE_UWB, ASUS_WMI_DEVID_UWB);
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	if (result && result != -ENODEV)
13028c2ecf20Sopenharmony_ci		goto exit;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	if (!asus->driver->quirks->hotplug_wireless)
13058c2ecf20Sopenharmony_ci		goto exit;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	result = asus_setup_pci_hotplug(asus);
13088c2ecf20Sopenharmony_ci	/*
13098c2ecf20Sopenharmony_ci	 * If we get -EBUSY then something else is handling the PCI hotplug -
13108c2ecf20Sopenharmony_ci	 * don't fail in this case
13118c2ecf20Sopenharmony_ci	 */
13128c2ecf20Sopenharmony_ci	if (result == -EBUSY)
13138c2ecf20Sopenharmony_ci		result = 0;
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P5");
13168c2ecf20Sopenharmony_ci	asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P6");
13178c2ecf20Sopenharmony_ci	asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P7");
13188c2ecf20Sopenharmony_ci	/*
13198c2ecf20Sopenharmony_ci	 * Refresh pci hotplug in case the rfkill state was changed during
13208c2ecf20Sopenharmony_ci	 * setup.
13218c2ecf20Sopenharmony_ci	 */
13228c2ecf20Sopenharmony_ci	asus_rfkill_hotplug(asus);
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ciexit:
13258c2ecf20Sopenharmony_ci	if (result && result != -ENODEV)
13268c2ecf20Sopenharmony_ci		asus_wmi_rfkill_exit(asus);
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	if (result == -ENODEV)
13298c2ecf20Sopenharmony_ci		result = 0;
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	return result;
13328c2ecf20Sopenharmony_ci}
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_ci/* Quirks *********************************************************************/
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_cistatic void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
13378c2ecf20Sopenharmony_ci{
13388c2ecf20Sopenharmony_ci	struct pci_dev *xhci_pdev;
13398c2ecf20Sopenharmony_ci	u32 orig_ports_available;
13408c2ecf20Sopenharmony_ci	u32 ports_available = asus->driver->quirks->xusb2pr;
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	xhci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
13438c2ecf20Sopenharmony_ci			PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI,
13448c2ecf20Sopenharmony_ci			NULL);
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	if (!xhci_pdev)
13478c2ecf20Sopenharmony_ci		return;
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	pci_read_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
13508c2ecf20Sopenharmony_ci				&orig_ports_available);
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
13538c2ecf20Sopenharmony_ci				cpu_to_le32(ports_available));
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	pci_dev_put(xhci_pdev);
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	pr_info("set USB_INTEL_XUSB2PR old: 0x%04x, new: 0x%04x\n",
13588c2ecf20Sopenharmony_ci			orig_ports_available, ports_available);
13598c2ecf20Sopenharmony_ci}
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci/*
13628c2ecf20Sopenharmony_ci * Some devices dont support or have borcken get_als method
13638c2ecf20Sopenharmony_ci * but still support set method.
13648c2ecf20Sopenharmony_ci */
13658c2ecf20Sopenharmony_cistatic void asus_wmi_set_als(void)
13668c2ecf20Sopenharmony_ci{
13678c2ecf20Sopenharmony_ci	asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);
13688c2ecf20Sopenharmony_ci}
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci/* Hwmon device ***************************************************************/
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_cistatic int asus_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
13738c2ecf20Sopenharmony_ci					  int *speed)
13748c2ecf20Sopenharmony_ci{
13758c2ecf20Sopenharmony_ci	struct agfn_fan_args args = {
13768c2ecf20Sopenharmony_ci		.agfn.len = sizeof(args),
13778c2ecf20Sopenharmony_ci		.agfn.mfun = ASUS_FAN_MFUN,
13788c2ecf20Sopenharmony_ci		.agfn.sfun = ASUS_FAN_SFUN_READ,
13798c2ecf20Sopenharmony_ci		.fan = fan,
13808c2ecf20Sopenharmony_ci		.speed = 0,
13818c2ecf20Sopenharmony_ci	};
13828c2ecf20Sopenharmony_ci	struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
13838c2ecf20Sopenharmony_ci	int status;
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	if (fan != 1)
13868c2ecf20Sopenharmony_ci		return -EINVAL;
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	status = asus_wmi_evaluate_method_agfn(input);
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	if (status || args.agfn.err)
13918c2ecf20Sopenharmony_ci		return -ENXIO;
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	if (speed)
13948c2ecf20Sopenharmony_ci		*speed = args.speed;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	return 0;
13978c2ecf20Sopenharmony_ci}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_cistatic int asus_agfn_fan_speed_write(struct asus_wmi *asus, int fan,
14008c2ecf20Sopenharmony_ci				     int *speed)
14018c2ecf20Sopenharmony_ci{
14028c2ecf20Sopenharmony_ci	struct agfn_fan_args args = {
14038c2ecf20Sopenharmony_ci		.agfn.len = sizeof(args),
14048c2ecf20Sopenharmony_ci		.agfn.mfun = ASUS_FAN_MFUN,
14058c2ecf20Sopenharmony_ci		.agfn.sfun = ASUS_FAN_SFUN_WRITE,
14068c2ecf20Sopenharmony_ci		.fan = fan,
14078c2ecf20Sopenharmony_ci		.speed = speed ?  *speed : 0,
14088c2ecf20Sopenharmony_ci	};
14098c2ecf20Sopenharmony_ci	struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
14108c2ecf20Sopenharmony_ci	int status;
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	/* 1: for setting 1st fan's speed 0: setting auto mode */
14138c2ecf20Sopenharmony_ci	if (fan != 1 && fan != 0)
14148c2ecf20Sopenharmony_ci		return -EINVAL;
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	status = asus_wmi_evaluate_method_agfn(input);
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	if (status || args.agfn.err)
14198c2ecf20Sopenharmony_ci		return -ENXIO;
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	if (speed && fan == 1)
14228c2ecf20Sopenharmony_ci		asus->agfn_pwm = *speed;
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	return 0;
14258c2ecf20Sopenharmony_ci}
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci/*
14288c2ecf20Sopenharmony_ci * Check if we can read the speed of one fan. If true we assume we can also
14298c2ecf20Sopenharmony_ci * control it.
14308c2ecf20Sopenharmony_ci */
14318c2ecf20Sopenharmony_cistatic bool asus_wmi_has_agfn_fan(struct asus_wmi *asus)
14328c2ecf20Sopenharmony_ci{
14338c2ecf20Sopenharmony_ci	int status;
14348c2ecf20Sopenharmony_ci	int speed;
14358c2ecf20Sopenharmony_ci	u32 value;
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	status = asus_agfn_fan_speed_read(asus, 1, &speed);
14388c2ecf20Sopenharmony_ci	if (status != 0)
14398c2ecf20Sopenharmony_ci		return false;
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	status = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value);
14428c2ecf20Sopenharmony_ci	if (status != 0)
14438c2ecf20Sopenharmony_ci		return false;
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	/*
14468c2ecf20Sopenharmony_ci	 * We need to find a better way, probably using sfun,
14478c2ecf20Sopenharmony_ci	 * bits or spec ...
14488c2ecf20Sopenharmony_ci	 * Currently we disable it if:
14498c2ecf20Sopenharmony_ci	 * - ASUS_WMI_UNSUPPORTED_METHOD is returned
14508c2ecf20Sopenharmony_ci	 * - reverved bits are non-zero
14518c2ecf20Sopenharmony_ci	 * - sfun and presence bit are not set
14528c2ecf20Sopenharmony_ci	 */
14538c2ecf20Sopenharmony_ci	return !(value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000
14548c2ecf20Sopenharmony_ci		 || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT)));
14558c2ecf20Sopenharmony_ci}
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_cistatic int asus_fan_set_auto(struct asus_wmi *asus)
14588c2ecf20Sopenharmony_ci{
14598c2ecf20Sopenharmony_ci	int status;
14608c2ecf20Sopenharmony_ci	u32 retval;
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	switch (asus->fan_type) {
14638c2ecf20Sopenharmony_ci	case FAN_TYPE_SPEC83:
14648c2ecf20Sopenharmony_ci		status = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL,
14658c2ecf20Sopenharmony_ci					       0, &retval);
14668c2ecf20Sopenharmony_ci		if (status)
14678c2ecf20Sopenharmony_ci			return status;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci		if (retval != 1)
14708c2ecf20Sopenharmony_ci			return -EIO;
14718c2ecf20Sopenharmony_ci		break;
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	case FAN_TYPE_AGFN:
14748c2ecf20Sopenharmony_ci		status = asus_agfn_fan_speed_write(asus, 0, NULL);
14758c2ecf20Sopenharmony_ci		if (status)
14768c2ecf20Sopenharmony_ci			return -ENXIO;
14778c2ecf20Sopenharmony_ci		break;
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	default:
14808c2ecf20Sopenharmony_ci		return -ENXIO;
14818c2ecf20Sopenharmony_ci	}
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	return 0;
14858c2ecf20Sopenharmony_ci}
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_cistatic ssize_t pwm1_show(struct device *dev,
14888c2ecf20Sopenharmony_ci			       struct device_attribute *attr,
14898c2ecf20Sopenharmony_ci			       char *buf)
14908c2ecf20Sopenharmony_ci{
14918c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(dev);
14928c2ecf20Sopenharmony_ci	int err;
14938c2ecf20Sopenharmony_ci	int value;
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	/* If we already set a value then just return it */
14968c2ecf20Sopenharmony_ci	if (asus->agfn_pwm >= 0)
14978c2ecf20Sopenharmony_ci		return sprintf(buf, "%d\n", asus->agfn_pwm);
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	/*
15008c2ecf20Sopenharmony_ci	 * If we haven't set already set a value through the AGFN interface,
15018c2ecf20Sopenharmony_ci	 * we read a current value through the (now-deprecated) FAN_CTRL device.
15028c2ecf20Sopenharmony_ci	 */
15038c2ecf20Sopenharmony_ci	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value);
15048c2ecf20Sopenharmony_ci	if (err < 0)
15058c2ecf20Sopenharmony_ci		return err;
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	value &= 0xFF;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	if (value == 1) /* Low Speed */
15108c2ecf20Sopenharmony_ci		value = 85;
15118c2ecf20Sopenharmony_ci	else if (value == 2)
15128c2ecf20Sopenharmony_ci		value = 170;
15138c2ecf20Sopenharmony_ci	else if (value == 3)
15148c2ecf20Sopenharmony_ci		value = 255;
15158c2ecf20Sopenharmony_ci	else if (value) {
15168c2ecf20Sopenharmony_ci		pr_err("Unknown fan speed %#x\n", value);
15178c2ecf20Sopenharmony_ci		value = -1;
15188c2ecf20Sopenharmony_ci	}
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", value);
15218c2ecf20Sopenharmony_ci}
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_cistatic ssize_t pwm1_store(struct device *dev,
15248c2ecf20Sopenharmony_ci				     struct device_attribute *attr,
15258c2ecf20Sopenharmony_ci				     const char *buf, size_t count) {
15268c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(dev);
15278c2ecf20Sopenharmony_ci	int value;
15288c2ecf20Sopenharmony_ci	int state;
15298c2ecf20Sopenharmony_ci	int ret;
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	ret = kstrtouint(buf, 10, &value);
15328c2ecf20Sopenharmony_ci	if (ret)
15338c2ecf20Sopenharmony_ci		return ret;
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	value = clamp(value, 0, 255);
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci	state = asus_agfn_fan_speed_write(asus, 1, &value);
15388c2ecf20Sopenharmony_ci	if (state)
15398c2ecf20Sopenharmony_ci		pr_warn("Setting fan speed failed: %d\n", state);
15408c2ecf20Sopenharmony_ci	else
15418c2ecf20Sopenharmony_ci		asus->fan_pwm_mode = ASUS_FAN_CTRL_MANUAL;
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	return count;
15448c2ecf20Sopenharmony_ci}
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_cistatic ssize_t fan1_input_show(struct device *dev,
15478c2ecf20Sopenharmony_ci					struct device_attribute *attr,
15488c2ecf20Sopenharmony_ci					char *buf)
15498c2ecf20Sopenharmony_ci{
15508c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(dev);
15518c2ecf20Sopenharmony_ci	int value;
15528c2ecf20Sopenharmony_ci	int ret;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	switch (asus->fan_type) {
15558c2ecf20Sopenharmony_ci	case FAN_TYPE_SPEC83:
15568c2ecf20Sopenharmony_ci		ret = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL,
15578c2ecf20Sopenharmony_ci					    &value);
15588c2ecf20Sopenharmony_ci		if (ret < 0)
15598c2ecf20Sopenharmony_ci			return ret;
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci		value &= 0xffff;
15628c2ecf20Sopenharmony_ci		break;
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	case FAN_TYPE_AGFN:
15658c2ecf20Sopenharmony_ci		/* no speed readable on manual mode */
15668c2ecf20Sopenharmony_ci		if (asus->fan_pwm_mode == ASUS_FAN_CTRL_MANUAL)
15678c2ecf20Sopenharmony_ci			return -ENXIO;
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci		ret = asus_agfn_fan_speed_read(asus, 1, &value);
15708c2ecf20Sopenharmony_ci		if (ret) {
15718c2ecf20Sopenharmony_ci			pr_warn("reading fan speed failed: %d\n", ret);
15728c2ecf20Sopenharmony_ci			return -ENXIO;
15738c2ecf20Sopenharmony_ci		}
15748c2ecf20Sopenharmony_ci		break;
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	default:
15778c2ecf20Sopenharmony_ci		return -ENXIO;
15788c2ecf20Sopenharmony_ci	}
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", value < 0 ? -1 : value*100);
15818c2ecf20Sopenharmony_ci}
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_cistatic ssize_t pwm1_enable_show(struct device *dev,
15848c2ecf20Sopenharmony_ci						 struct device_attribute *attr,
15858c2ecf20Sopenharmony_ci						 char *buf)
15868c2ecf20Sopenharmony_ci{
15878c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(dev);
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci	/*
15908c2ecf20Sopenharmony_ci	 * Just read back the cached pwm mode.
15918c2ecf20Sopenharmony_ci	 *
15928c2ecf20Sopenharmony_ci	 * For the CPU_FAN device, the spec indicates that we should be
15938c2ecf20Sopenharmony_ci	 * able to read the device status and consult bit 19 to see if we
15948c2ecf20Sopenharmony_ci	 * are in Full On or Automatic mode. However, this does not work
15958c2ecf20Sopenharmony_ci	 * in practice on X532FL at least (the bit is always 0) and there's
15968c2ecf20Sopenharmony_ci	 * also nothing in the DSDT to indicate that this behaviour exists.
15978c2ecf20Sopenharmony_ci	 */
15988c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", asus->fan_pwm_mode);
15998c2ecf20Sopenharmony_ci}
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_cistatic ssize_t pwm1_enable_store(struct device *dev,
16028c2ecf20Sopenharmony_ci						  struct device_attribute *attr,
16038c2ecf20Sopenharmony_ci						  const char *buf, size_t count)
16048c2ecf20Sopenharmony_ci{
16058c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(dev);
16068c2ecf20Sopenharmony_ci	int status = 0;
16078c2ecf20Sopenharmony_ci	int state;
16088c2ecf20Sopenharmony_ci	int value;
16098c2ecf20Sopenharmony_ci	int ret;
16108c2ecf20Sopenharmony_ci	u32 retval;
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	ret = kstrtouint(buf, 10, &state);
16138c2ecf20Sopenharmony_ci	if (ret)
16148c2ecf20Sopenharmony_ci		return ret;
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	if (asus->fan_type == FAN_TYPE_SPEC83) {
16178c2ecf20Sopenharmony_ci		switch (state) { /* standard documented hwmon values */
16188c2ecf20Sopenharmony_ci		case ASUS_FAN_CTRL_FULLSPEED:
16198c2ecf20Sopenharmony_ci			value = 1;
16208c2ecf20Sopenharmony_ci			break;
16218c2ecf20Sopenharmony_ci		case ASUS_FAN_CTRL_AUTO:
16228c2ecf20Sopenharmony_ci			value = 0;
16238c2ecf20Sopenharmony_ci			break;
16248c2ecf20Sopenharmony_ci		default:
16258c2ecf20Sopenharmony_ci			return -EINVAL;
16268c2ecf20Sopenharmony_ci		}
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci		ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL,
16298c2ecf20Sopenharmony_ci					    value, &retval);
16308c2ecf20Sopenharmony_ci		if (ret)
16318c2ecf20Sopenharmony_ci			return ret;
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci		if (retval != 1)
16348c2ecf20Sopenharmony_ci			return -EIO;
16358c2ecf20Sopenharmony_ci	} else if (asus->fan_type == FAN_TYPE_AGFN) {
16368c2ecf20Sopenharmony_ci		switch (state) {
16378c2ecf20Sopenharmony_ci		case ASUS_FAN_CTRL_MANUAL:
16388c2ecf20Sopenharmony_ci			break;
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci		case ASUS_FAN_CTRL_AUTO:
16418c2ecf20Sopenharmony_ci			status = asus_fan_set_auto(asus);
16428c2ecf20Sopenharmony_ci			if (status)
16438c2ecf20Sopenharmony_ci				return status;
16448c2ecf20Sopenharmony_ci			break;
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci		default:
16478c2ecf20Sopenharmony_ci			return -EINVAL;
16488c2ecf20Sopenharmony_ci		}
16498c2ecf20Sopenharmony_ci	}
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci	asus->fan_pwm_mode = state;
16528c2ecf20Sopenharmony_ci	return count;
16538c2ecf20Sopenharmony_ci}
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_cistatic ssize_t fan1_label_show(struct device *dev,
16568c2ecf20Sopenharmony_ci					  struct device_attribute *attr,
16578c2ecf20Sopenharmony_ci					  char *buf)
16588c2ecf20Sopenharmony_ci{
16598c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", ASUS_FAN_DESC);
16608c2ecf20Sopenharmony_ci}
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_cistatic ssize_t asus_hwmon_temp1(struct device *dev,
16638c2ecf20Sopenharmony_ci				struct device_attribute *attr,
16648c2ecf20Sopenharmony_ci				char *buf)
16658c2ecf20Sopenharmony_ci{
16668c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(dev);
16678c2ecf20Sopenharmony_ci	u32 value;
16688c2ecf20Sopenharmony_ci	int err;
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, &value);
16718c2ecf20Sopenharmony_ci	if (err < 0)
16728c2ecf20Sopenharmony_ci		return err;
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	return sprintf(buf, "%ld\n",
16758c2ecf20Sopenharmony_ci		       deci_kelvin_to_millicelsius(value & 0xFFFF));
16768c2ecf20Sopenharmony_ci}
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci/* Fan1 */
16798c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(pwm1);
16808c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(pwm1_enable);
16818c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(fan1_input);
16828c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(fan1_label);
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci/* Temperature */
16858c2ecf20Sopenharmony_cistatic DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL);
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_cistatic struct attribute *hwmon_attributes[] = {
16888c2ecf20Sopenharmony_ci	&dev_attr_pwm1.attr,
16898c2ecf20Sopenharmony_ci	&dev_attr_pwm1_enable.attr,
16908c2ecf20Sopenharmony_ci	&dev_attr_fan1_input.attr,
16918c2ecf20Sopenharmony_ci	&dev_attr_fan1_label.attr,
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci	&dev_attr_temp1_input.attr,
16948c2ecf20Sopenharmony_ci	NULL
16958c2ecf20Sopenharmony_ci};
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_cistatic umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
16988c2ecf20Sopenharmony_ci					  struct attribute *attr, int idx)
16998c2ecf20Sopenharmony_ci{
17008c2ecf20Sopenharmony_ci	struct device *dev = container_of(kobj, struct device, kobj);
17018c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(dev->parent);
17028c2ecf20Sopenharmony_ci	u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci	if (attr == &dev_attr_pwm1.attr) {
17058c2ecf20Sopenharmony_ci		if (asus->fan_type != FAN_TYPE_AGFN)
17068c2ecf20Sopenharmony_ci			return 0;
17078c2ecf20Sopenharmony_ci	} else if (attr == &dev_attr_fan1_input.attr
17088c2ecf20Sopenharmony_ci	    || attr == &dev_attr_fan1_label.attr
17098c2ecf20Sopenharmony_ci	    || attr == &dev_attr_pwm1_enable.attr) {
17108c2ecf20Sopenharmony_ci		if (asus->fan_type == FAN_TYPE_NONE)
17118c2ecf20Sopenharmony_ci			return 0;
17128c2ecf20Sopenharmony_ci	} else if (attr == &dev_attr_temp1_input.attr) {
17138c2ecf20Sopenharmony_ci		int err = asus_wmi_get_devstate(asus,
17148c2ecf20Sopenharmony_ci						ASUS_WMI_DEVID_THERMAL_CTRL,
17158c2ecf20Sopenharmony_ci						&value);
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci		if (err < 0)
17188c2ecf20Sopenharmony_ci			return 0; /* can't return negative here */
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci		/*
17218c2ecf20Sopenharmony_ci		 * If the temperature value in deci-Kelvin is near the absolute
17228c2ecf20Sopenharmony_ci		 * zero temperature, something is clearly wrong
17238c2ecf20Sopenharmony_ci		 */
17248c2ecf20Sopenharmony_ci		if (value == 0 || value == 1)
17258c2ecf20Sopenharmony_ci			return 0;
17268c2ecf20Sopenharmony_ci	}
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	return attr->mode;
17298c2ecf20Sopenharmony_ci}
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_cistatic const struct attribute_group hwmon_attribute_group = {
17328c2ecf20Sopenharmony_ci	.is_visible = asus_hwmon_sysfs_is_visible,
17338c2ecf20Sopenharmony_ci	.attrs = hwmon_attributes
17348c2ecf20Sopenharmony_ci};
17358c2ecf20Sopenharmony_ci__ATTRIBUTE_GROUPS(hwmon_attribute);
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_cistatic int asus_wmi_hwmon_init(struct asus_wmi *asus)
17388c2ecf20Sopenharmony_ci{
17398c2ecf20Sopenharmony_ci	struct device *dev = &asus->platform_device->dev;
17408c2ecf20Sopenharmony_ci	struct device *hwmon;
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	hwmon = devm_hwmon_device_register_with_groups(dev, "asus", asus,
17438c2ecf20Sopenharmony_ci			hwmon_attribute_groups);
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	if (IS_ERR(hwmon)) {
17468c2ecf20Sopenharmony_ci		pr_err("Could not register asus hwmon device\n");
17478c2ecf20Sopenharmony_ci		return PTR_ERR(hwmon);
17488c2ecf20Sopenharmony_ci	}
17498c2ecf20Sopenharmony_ci	return 0;
17508c2ecf20Sopenharmony_ci}
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_cistatic int asus_wmi_fan_init(struct asus_wmi *asus)
17538c2ecf20Sopenharmony_ci{
17548c2ecf20Sopenharmony_ci	asus->fan_type = FAN_TYPE_NONE;
17558c2ecf20Sopenharmony_ci	asus->agfn_pwm = -1;
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_ci	if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL))
17588c2ecf20Sopenharmony_ci		asus->fan_type = FAN_TYPE_SPEC83;
17598c2ecf20Sopenharmony_ci	else if (asus_wmi_has_agfn_fan(asus))
17608c2ecf20Sopenharmony_ci		asus->fan_type = FAN_TYPE_AGFN;
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci	if (asus->fan_type == FAN_TYPE_NONE)
17638c2ecf20Sopenharmony_ci		return -ENODEV;
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci	asus_fan_set_auto(asus);
17668c2ecf20Sopenharmony_ci	asus->fan_pwm_mode = ASUS_FAN_CTRL_AUTO;
17678c2ecf20Sopenharmony_ci	return 0;
17688c2ecf20Sopenharmony_ci}
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci/* Fan mode *******************************************************************/
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_cistatic int fan_boost_mode_check_present(struct asus_wmi *asus)
17738c2ecf20Sopenharmony_ci{
17748c2ecf20Sopenharmony_ci	u32 result;
17758c2ecf20Sopenharmony_ci	int err;
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	asus->fan_boost_mode_available = false;
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_ci	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_BOOST_MODE,
17808c2ecf20Sopenharmony_ci				    &result);
17818c2ecf20Sopenharmony_ci	if (err) {
17828c2ecf20Sopenharmony_ci		if (err == -ENODEV)
17838c2ecf20Sopenharmony_ci			return 0;
17848c2ecf20Sopenharmony_ci		else
17858c2ecf20Sopenharmony_ci			return err;
17868c2ecf20Sopenharmony_ci	}
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	if ((result & ASUS_WMI_DSTS_PRESENCE_BIT) &&
17898c2ecf20Sopenharmony_ci			(result & ASUS_FAN_BOOST_MODES_MASK)) {
17908c2ecf20Sopenharmony_ci		asus->fan_boost_mode_available = true;
17918c2ecf20Sopenharmony_ci		asus->fan_boost_mode_mask = result & ASUS_FAN_BOOST_MODES_MASK;
17928c2ecf20Sopenharmony_ci	}
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci	return 0;
17958c2ecf20Sopenharmony_ci}
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_cistatic int fan_boost_mode_write(struct asus_wmi *asus)
17988c2ecf20Sopenharmony_ci{
17998c2ecf20Sopenharmony_ci	int err;
18008c2ecf20Sopenharmony_ci	u8 value;
18018c2ecf20Sopenharmony_ci	u32 retval;
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_ci	value = asus->fan_boost_mode;
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_ci	pr_info("Set fan boost mode: %u\n", value);
18068c2ecf20Sopenharmony_ci	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_BOOST_MODE, value,
18078c2ecf20Sopenharmony_ci				    &retval);
18088c2ecf20Sopenharmony_ci	if (err) {
18098c2ecf20Sopenharmony_ci		pr_warn("Failed to set fan boost mode: %d\n", err);
18108c2ecf20Sopenharmony_ci		return err;
18118c2ecf20Sopenharmony_ci	}
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	if (retval != 1) {
18148c2ecf20Sopenharmony_ci		pr_warn("Failed to set fan boost mode (retval): 0x%x\n",
18158c2ecf20Sopenharmony_ci			retval);
18168c2ecf20Sopenharmony_ci		return -EIO;
18178c2ecf20Sopenharmony_ci	}
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	return 0;
18208c2ecf20Sopenharmony_ci}
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_cistatic int fan_boost_mode_switch_next(struct asus_wmi *asus)
18238c2ecf20Sopenharmony_ci{
18248c2ecf20Sopenharmony_ci	u8 mask = asus->fan_boost_mode_mask;
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	if (asus->fan_boost_mode == ASUS_FAN_BOOST_MODE_NORMAL) {
18278c2ecf20Sopenharmony_ci		if (mask & ASUS_FAN_BOOST_MODE_OVERBOOST_MASK)
18288c2ecf20Sopenharmony_ci			asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_OVERBOOST;
18298c2ecf20Sopenharmony_ci		else if (mask & ASUS_FAN_BOOST_MODE_SILENT_MASK)
18308c2ecf20Sopenharmony_ci			asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_SILENT;
18318c2ecf20Sopenharmony_ci	} else if (asus->fan_boost_mode == ASUS_FAN_BOOST_MODE_OVERBOOST) {
18328c2ecf20Sopenharmony_ci		if (mask & ASUS_FAN_BOOST_MODE_SILENT_MASK)
18338c2ecf20Sopenharmony_ci			asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_SILENT;
18348c2ecf20Sopenharmony_ci		else
18358c2ecf20Sopenharmony_ci			asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_NORMAL;
18368c2ecf20Sopenharmony_ci	} else {
18378c2ecf20Sopenharmony_ci		asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_NORMAL;
18388c2ecf20Sopenharmony_ci	}
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	return fan_boost_mode_write(asus);
18418c2ecf20Sopenharmony_ci}
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_cistatic ssize_t fan_boost_mode_show(struct device *dev,
18448c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
18458c2ecf20Sopenharmony_ci{
18468c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(dev);
18478c2ecf20Sopenharmony_ci
18488c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", asus->fan_boost_mode);
18498c2ecf20Sopenharmony_ci}
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_cistatic ssize_t fan_boost_mode_store(struct device *dev,
18528c2ecf20Sopenharmony_ci				    struct device_attribute *attr,
18538c2ecf20Sopenharmony_ci				    const char *buf, size_t count)
18548c2ecf20Sopenharmony_ci{
18558c2ecf20Sopenharmony_ci	int result;
18568c2ecf20Sopenharmony_ci	u8 new_mode;
18578c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(dev);
18588c2ecf20Sopenharmony_ci	u8 mask = asus->fan_boost_mode_mask;
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ci	result = kstrtou8(buf, 10, &new_mode);
18618c2ecf20Sopenharmony_ci	if (result < 0) {
18628c2ecf20Sopenharmony_ci		pr_warn("Trying to store invalid value\n");
18638c2ecf20Sopenharmony_ci		return result;
18648c2ecf20Sopenharmony_ci	}
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci	if (new_mode == ASUS_FAN_BOOST_MODE_OVERBOOST) {
18678c2ecf20Sopenharmony_ci		if (!(mask & ASUS_FAN_BOOST_MODE_OVERBOOST_MASK))
18688c2ecf20Sopenharmony_ci			return -EINVAL;
18698c2ecf20Sopenharmony_ci	} else if (new_mode == ASUS_FAN_BOOST_MODE_SILENT) {
18708c2ecf20Sopenharmony_ci		if (!(mask & ASUS_FAN_BOOST_MODE_SILENT_MASK))
18718c2ecf20Sopenharmony_ci			return -EINVAL;
18728c2ecf20Sopenharmony_ci	} else if (new_mode != ASUS_FAN_BOOST_MODE_NORMAL) {
18738c2ecf20Sopenharmony_ci		return -EINVAL;
18748c2ecf20Sopenharmony_ci	}
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci	asus->fan_boost_mode = new_mode;
18778c2ecf20Sopenharmony_ci	fan_boost_mode_write(asus);
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	return count;
18808c2ecf20Sopenharmony_ci}
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci// Fan boost mode: 0 - normal, 1 - overboost, 2 - silent
18838c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(fan_boost_mode);
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci/* Throttle thermal policy ****************************************************/
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_cistatic int throttle_thermal_policy_check_present(struct asus_wmi *asus)
18888c2ecf20Sopenharmony_ci{
18898c2ecf20Sopenharmony_ci	u32 result;
18908c2ecf20Sopenharmony_ci	int err;
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_ci	asus->throttle_thermal_policy_available = false;
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	err = asus_wmi_get_devstate(asus,
18958c2ecf20Sopenharmony_ci				    ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
18968c2ecf20Sopenharmony_ci				    &result);
18978c2ecf20Sopenharmony_ci	if (err) {
18988c2ecf20Sopenharmony_ci		if (err == -ENODEV)
18998c2ecf20Sopenharmony_ci			return 0;
19008c2ecf20Sopenharmony_ci		return err;
19018c2ecf20Sopenharmony_ci	}
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_ci	if (result & ASUS_WMI_DSTS_PRESENCE_BIT)
19048c2ecf20Sopenharmony_ci		asus->throttle_thermal_policy_available = true;
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci	return 0;
19078c2ecf20Sopenharmony_ci}
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_cistatic int throttle_thermal_policy_write(struct asus_wmi *asus)
19108c2ecf20Sopenharmony_ci{
19118c2ecf20Sopenharmony_ci	int err;
19128c2ecf20Sopenharmony_ci	u8 value;
19138c2ecf20Sopenharmony_ci	u32 retval;
19148c2ecf20Sopenharmony_ci
19158c2ecf20Sopenharmony_ci	value = asus->throttle_thermal_policy_mode;
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
19188c2ecf20Sopenharmony_ci				    value, &retval);
19198c2ecf20Sopenharmony_ci	if (err) {
19208c2ecf20Sopenharmony_ci		pr_warn("Failed to set throttle thermal policy: %d\n", err);
19218c2ecf20Sopenharmony_ci		return err;
19228c2ecf20Sopenharmony_ci	}
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci	if (retval != 1) {
19258c2ecf20Sopenharmony_ci		pr_warn("Failed to set throttle thermal policy (retval): 0x%x\n",
19268c2ecf20Sopenharmony_ci			retval);
19278c2ecf20Sopenharmony_ci		return -EIO;
19288c2ecf20Sopenharmony_ci	}
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci	return 0;
19318c2ecf20Sopenharmony_ci}
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_cistatic int throttle_thermal_policy_set_default(struct asus_wmi *asus)
19348c2ecf20Sopenharmony_ci{
19358c2ecf20Sopenharmony_ci	if (!asus->throttle_thermal_policy_available)
19368c2ecf20Sopenharmony_ci		return 0;
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	asus->throttle_thermal_policy_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
19398c2ecf20Sopenharmony_ci	return throttle_thermal_policy_write(asus);
19408c2ecf20Sopenharmony_ci}
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_cistatic int throttle_thermal_policy_switch_next(struct asus_wmi *asus)
19438c2ecf20Sopenharmony_ci{
19448c2ecf20Sopenharmony_ci	u8 new_mode = asus->throttle_thermal_policy_mode + 1;
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_ci	if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT)
19478c2ecf20Sopenharmony_ci		new_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci	asus->throttle_thermal_policy_mode = new_mode;
19508c2ecf20Sopenharmony_ci	return throttle_thermal_policy_write(asus);
19518c2ecf20Sopenharmony_ci}
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_cistatic ssize_t throttle_thermal_policy_show(struct device *dev,
19548c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
19558c2ecf20Sopenharmony_ci{
19568c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(dev);
19578c2ecf20Sopenharmony_ci	u8 mode = asus->throttle_thermal_policy_mode;
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", mode);
19608c2ecf20Sopenharmony_ci}
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_cistatic ssize_t throttle_thermal_policy_store(struct device *dev,
19638c2ecf20Sopenharmony_ci				    struct device_attribute *attr,
19648c2ecf20Sopenharmony_ci				    const char *buf, size_t count)
19658c2ecf20Sopenharmony_ci{
19668c2ecf20Sopenharmony_ci	int result;
19678c2ecf20Sopenharmony_ci	u8 new_mode;
19688c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(dev);
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci	result = kstrtou8(buf, 10, &new_mode);
19718c2ecf20Sopenharmony_ci	if (result < 0)
19728c2ecf20Sopenharmony_ci		return result;
19738c2ecf20Sopenharmony_ci
19748c2ecf20Sopenharmony_ci	if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT)
19758c2ecf20Sopenharmony_ci		return -EINVAL;
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci	asus->throttle_thermal_policy_mode = new_mode;
19788c2ecf20Sopenharmony_ci	throttle_thermal_policy_write(asus);
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	return count;
19818c2ecf20Sopenharmony_ci}
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci// Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent
19848c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(throttle_thermal_policy);
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci/* Backlight ******************************************************************/
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_cistatic int read_backlight_power(struct asus_wmi *asus)
19898c2ecf20Sopenharmony_ci{
19908c2ecf20Sopenharmony_ci	int ret;
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci	if (asus->driver->quirks->store_backlight_power)
19938c2ecf20Sopenharmony_ci		ret = !asus->driver->panel_power;
19948c2ecf20Sopenharmony_ci	else
19958c2ecf20Sopenharmony_ci		ret = asus_wmi_get_devstate_simple(asus,
19968c2ecf20Sopenharmony_ci						   ASUS_WMI_DEVID_BACKLIGHT);
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_ci	if (ret < 0)
19998c2ecf20Sopenharmony_ci		return ret;
20008c2ecf20Sopenharmony_ci
20018c2ecf20Sopenharmony_ci	return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
20028c2ecf20Sopenharmony_ci}
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_cistatic int read_brightness_max(struct asus_wmi *asus)
20058c2ecf20Sopenharmony_ci{
20068c2ecf20Sopenharmony_ci	u32 retval;
20078c2ecf20Sopenharmony_ci	int err;
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval);
20108c2ecf20Sopenharmony_ci	if (err < 0)
20118c2ecf20Sopenharmony_ci		return err;
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci	retval = retval & ASUS_WMI_DSTS_MAX_BRIGTH_MASK;
20148c2ecf20Sopenharmony_ci	retval >>= 8;
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci	if (!retval)
20178c2ecf20Sopenharmony_ci		return -ENODEV;
20188c2ecf20Sopenharmony_ci
20198c2ecf20Sopenharmony_ci	return retval;
20208c2ecf20Sopenharmony_ci}
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_cistatic int read_brightness(struct backlight_device *bd)
20238c2ecf20Sopenharmony_ci{
20248c2ecf20Sopenharmony_ci	struct asus_wmi *asus = bl_get_data(bd);
20258c2ecf20Sopenharmony_ci	u32 retval;
20268c2ecf20Sopenharmony_ci	int err;
20278c2ecf20Sopenharmony_ci
20288c2ecf20Sopenharmony_ci	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval);
20298c2ecf20Sopenharmony_ci	if (err < 0)
20308c2ecf20Sopenharmony_ci		return err;
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci	return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
20338c2ecf20Sopenharmony_ci}
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_cistatic u32 get_scalar_command(struct backlight_device *bd)
20368c2ecf20Sopenharmony_ci{
20378c2ecf20Sopenharmony_ci	struct asus_wmi *asus = bl_get_data(bd);
20388c2ecf20Sopenharmony_ci	u32 ctrl_param = 0;
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	if ((asus->driver->brightness < bd->props.brightness) ||
20418c2ecf20Sopenharmony_ci	    bd->props.brightness == bd->props.max_brightness)
20428c2ecf20Sopenharmony_ci		ctrl_param = 0x00008001;
20438c2ecf20Sopenharmony_ci	else if ((asus->driver->brightness > bd->props.brightness) ||
20448c2ecf20Sopenharmony_ci		 bd->props.brightness == 0)
20458c2ecf20Sopenharmony_ci		ctrl_param = 0x00008000;
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci	asus->driver->brightness = bd->props.brightness;
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_ci	return ctrl_param;
20508c2ecf20Sopenharmony_ci}
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_cistatic int update_bl_status(struct backlight_device *bd)
20538c2ecf20Sopenharmony_ci{
20548c2ecf20Sopenharmony_ci	struct asus_wmi *asus = bl_get_data(bd);
20558c2ecf20Sopenharmony_ci	u32 ctrl_param;
20568c2ecf20Sopenharmony_ci	int power, err = 0;
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci	power = read_backlight_power(asus);
20598c2ecf20Sopenharmony_ci	if (power != -ENODEV && bd->props.power != power) {
20608c2ecf20Sopenharmony_ci		ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK);
20618c2ecf20Sopenharmony_ci		err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT,
20628c2ecf20Sopenharmony_ci					    ctrl_param, NULL);
20638c2ecf20Sopenharmony_ci		if (asus->driver->quirks->store_backlight_power)
20648c2ecf20Sopenharmony_ci			asus->driver->panel_power = bd->props.power;
20658c2ecf20Sopenharmony_ci
20668c2ecf20Sopenharmony_ci		/* When using scalar brightness, updating the brightness
20678c2ecf20Sopenharmony_ci		 * will mess with the backlight power */
20688c2ecf20Sopenharmony_ci		if (asus->driver->quirks->scalar_panel_brightness)
20698c2ecf20Sopenharmony_ci			return err;
20708c2ecf20Sopenharmony_ci	}
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	if (asus->driver->quirks->scalar_panel_brightness)
20738c2ecf20Sopenharmony_ci		ctrl_param = get_scalar_command(bd);
20748c2ecf20Sopenharmony_ci	else
20758c2ecf20Sopenharmony_ci		ctrl_param = bd->props.brightness;
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS,
20788c2ecf20Sopenharmony_ci				    ctrl_param, NULL);
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci	return err;
20818c2ecf20Sopenharmony_ci}
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_cistatic const struct backlight_ops asus_wmi_bl_ops = {
20848c2ecf20Sopenharmony_ci	.get_brightness = read_brightness,
20858c2ecf20Sopenharmony_ci	.update_status = update_bl_status,
20868c2ecf20Sopenharmony_ci};
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_cistatic int asus_wmi_backlight_notify(struct asus_wmi *asus, int code)
20898c2ecf20Sopenharmony_ci{
20908c2ecf20Sopenharmony_ci	struct backlight_device *bd = asus->backlight_device;
20918c2ecf20Sopenharmony_ci	int old = bd->props.brightness;
20928c2ecf20Sopenharmony_ci	int new = old;
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
20958c2ecf20Sopenharmony_ci		new = code - NOTIFY_BRNUP_MIN + 1;
20968c2ecf20Sopenharmony_ci	else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
20978c2ecf20Sopenharmony_ci		new = code - NOTIFY_BRNDOWN_MIN;
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_ci	bd->props.brightness = new;
21008c2ecf20Sopenharmony_ci	backlight_update_status(bd);
21018c2ecf20Sopenharmony_ci	backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
21028c2ecf20Sopenharmony_ci
21038c2ecf20Sopenharmony_ci	return old;
21048c2ecf20Sopenharmony_ci}
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_cistatic int asus_wmi_backlight_init(struct asus_wmi *asus)
21078c2ecf20Sopenharmony_ci{
21088c2ecf20Sopenharmony_ci	struct backlight_device *bd;
21098c2ecf20Sopenharmony_ci	struct backlight_properties props;
21108c2ecf20Sopenharmony_ci	int max;
21118c2ecf20Sopenharmony_ci	int power;
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci	max = read_brightness_max(asus);
21148c2ecf20Sopenharmony_ci	if (max < 0)
21158c2ecf20Sopenharmony_ci		return max;
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci	power = read_backlight_power(asus);
21188c2ecf20Sopenharmony_ci	if (power == -ENODEV)
21198c2ecf20Sopenharmony_ci		power = FB_BLANK_UNBLANK;
21208c2ecf20Sopenharmony_ci	else if (power < 0)
21218c2ecf20Sopenharmony_ci		return power;
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_ci	memset(&props, 0, sizeof(struct backlight_properties));
21248c2ecf20Sopenharmony_ci	props.type = BACKLIGHT_PLATFORM;
21258c2ecf20Sopenharmony_ci	props.max_brightness = max;
21268c2ecf20Sopenharmony_ci	bd = backlight_device_register(asus->driver->name,
21278c2ecf20Sopenharmony_ci				       &asus->platform_device->dev, asus,
21288c2ecf20Sopenharmony_ci				       &asus_wmi_bl_ops, &props);
21298c2ecf20Sopenharmony_ci	if (IS_ERR(bd)) {
21308c2ecf20Sopenharmony_ci		pr_err("Could not register backlight device\n");
21318c2ecf20Sopenharmony_ci		return PTR_ERR(bd);
21328c2ecf20Sopenharmony_ci	}
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci	asus->backlight_device = bd;
21358c2ecf20Sopenharmony_ci
21368c2ecf20Sopenharmony_ci	if (asus->driver->quirks->store_backlight_power)
21378c2ecf20Sopenharmony_ci		asus->driver->panel_power = power;
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ci	bd->props.brightness = read_brightness(bd);
21408c2ecf20Sopenharmony_ci	bd->props.power = power;
21418c2ecf20Sopenharmony_ci	backlight_update_status(bd);
21428c2ecf20Sopenharmony_ci
21438c2ecf20Sopenharmony_ci	asus->driver->brightness = bd->props.brightness;
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci	return 0;
21468c2ecf20Sopenharmony_ci}
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_cistatic void asus_wmi_backlight_exit(struct asus_wmi *asus)
21498c2ecf20Sopenharmony_ci{
21508c2ecf20Sopenharmony_ci	backlight_device_unregister(asus->backlight_device);
21518c2ecf20Sopenharmony_ci
21528c2ecf20Sopenharmony_ci	asus->backlight_device = NULL;
21538c2ecf20Sopenharmony_ci}
21548c2ecf20Sopenharmony_ci
21558c2ecf20Sopenharmony_cistatic int is_display_toggle(int code)
21568c2ecf20Sopenharmony_ci{
21578c2ecf20Sopenharmony_ci	/* display toggle keys */
21588c2ecf20Sopenharmony_ci	if ((code >= 0x61 && code <= 0x67) ||
21598c2ecf20Sopenharmony_ci	    (code >= 0x8c && code <= 0x93) ||
21608c2ecf20Sopenharmony_ci	    (code >= 0xa0 && code <= 0xa7) ||
21618c2ecf20Sopenharmony_ci	    (code >= 0xd0 && code <= 0xd5))
21628c2ecf20Sopenharmony_ci		return 1;
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci	return 0;
21658c2ecf20Sopenharmony_ci}
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci/* Fn-lock ********************************************************************/
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_cistatic bool asus_wmi_has_fnlock_key(struct asus_wmi *asus)
21708c2ecf20Sopenharmony_ci{
21718c2ecf20Sopenharmony_ci	u32 result;
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci	asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FNLOCK, &result);
21748c2ecf20Sopenharmony_ci
21758c2ecf20Sopenharmony_ci	return (result & ASUS_WMI_DSTS_PRESENCE_BIT) &&
21768c2ecf20Sopenharmony_ci		!(result & ASUS_WMI_FNLOCK_BIOS_DISABLED);
21778c2ecf20Sopenharmony_ci}
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_cistatic void asus_wmi_fnlock_update(struct asus_wmi *asus)
21808c2ecf20Sopenharmony_ci{
21818c2ecf20Sopenharmony_ci	int mode = asus->fnlock_locked;
21828c2ecf20Sopenharmony_ci
21838c2ecf20Sopenharmony_ci	asus_wmi_set_devstate(ASUS_WMI_DEVID_FNLOCK, mode, NULL);
21848c2ecf20Sopenharmony_ci}
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci/* WMI events *****************************************************************/
21878c2ecf20Sopenharmony_ci
21888c2ecf20Sopenharmony_cistatic int asus_wmi_get_event_code(u32 value)
21898c2ecf20Sopenharmony_ci{
21908c2ecf20Sopenharmony_ci	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
21918c2ecf20Sopenharmony_ci	union acpi_object *obj;
21928c2ecf20Sopenharmony_ci	acpi_status status;
21938c2ecf20Sopenharmony_ci	int code;
21948c2ecf20Sopenharmony_ci
21958c2ecf20Sopenharmony_ci	status = wmi_get_event_data(value, &response);
21968c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
21978c2ecf20Sopenharmony_ci		pr_warn("Failed to get WMI notify code: %s\n",
21988c2ecf20Sopenharmony_ci				acpi_format_exception(status));
21998c2ecf20Sopenharmony_ci		return -EIO;
22008c2ecf20Sopenharmony_ci	}
22018c2ecf20Sopenharmony_ci
22028c2ecf20Sopenharmony_ci	obj = (union acpi_object *)response.pointer;
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci	if (obj && obj->type == ACPI_TYPE_INTEGER)
22058c2ecf20Sopenharmony_ci		code = (int)(obj->integer.value & WMI_EVENT_MASK);
22068c2ecf20Sopenharmony_ci	else
22078c2ecf20Sopenharmony_ci		code = -EIO;
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci	kfree(obj);
22108c2ecf20Sopenharmony_ci	return code;
22118c2ecf20Sopenharmony_ci}
22128c2ecf20Sopenharmony_ci
22138c2ecf20Sopenharmony_cistatic void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
22148c2ecf20Sopenharmony_ci{
22158c2ecf20Sopenharmony_ci	unsigned int key_value = 1;
22168c2ecf20Sopenharmony_ci	bool autorelease = 1;
22178c2ecf20Sopenharmony_ci	int orig_code = code;
22188c2ecf20Sopenharmony_ci
22198c2ecf20Sopenharmony_ci	if (asus->driver->key_filter) {
22208c2ecf20Sopenharmony_ci		asus->driver->key_filter(asus->driver, &code, &key_value,
22218c2ecf20Sopenharmony_ci					 &autorelease);
22228c2ecf20Sopenharmony_ci		if (code == ASUS_WMI_KEY_IGNORE)
22238c2ecf20Sopenharmony_ci			return;
22248c2ecf20Sopenharmony_ci	}
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci	if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
22278c2ecf20Sopenharmony_ci		code = ASUS_WMI_BRN_UP;
22288c2ecf20Sopenharmony_ci	else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
22298c2ecf20Sopenharmony_ci		code = ASUS_WMI_BRN_DOWN;
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci	if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) {
22328c2ecf20Sopenharmony_ci		if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
22338c2ecf20Sopenharmony_ci			asus_wmi_backlight_notify(asus, orig_code);
22348c2ecf20Sopenharmony_ci			return;
22358c2ecf20Sopenharmony_ci		}
22368c2ecf20Sopenharmony_ci	}
22378c2ecf20Sopenharmony_ci
22388c2ecf20Sopenharmony_ci	if (code == NOTIFY_KBD_BRTUP) {
22398c2ecf20Sopenharmony_ci		kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
22408c2ecf20Sopenharmony_ci		return;
22418c2ecf20Sopenharmony_ci	}
22428c2ecf20Sopenharmony_ci	if (code == NOTIFY_KBD_BRTDWN) {
22438c2ecf20Sopenharmony_ci		kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1);
22448c2ecf20Sopenharmony_ci		return;
22458c2ecf20Sopenharmony_ci	}
22468c2ecf20Sopenharmony_ci	if (code == NOTIFY_KBD_BRTTOGGLE) {
22478c2ecf20Sopenharmony_ci		if (asus->kbd_led_wk == asus->kbd_led.max_brightness)
22488c2ecf20Sopenharmony_ci			kbd_led_set_by_kbd(asus, 0);
22498c2ecf20Sopenharmony_ci		else
22508c2ecf20Sopenharmony_ci			kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
22518c2ecf20Sopenharmony_ci		return;
22528c2ecf20Sopenharmony_ci	}
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_ci	if (code == NOTIFY_FNLOCK_TOGGLE) {
22558c2ecf20Sopenharmony_ci		asus->fnlock_locked = !asus->fnlock_locked;
22568c2ecf20Sopenharmony_ci		asus_wmi_fnlock_update(asus);
22578c2ecf20Sopenharmony_ci		return;
22588c2ecf20Sopenharmony_ci	}
22598c2ecf20Sopenharmony_ci
22608c2ecf20Sopenharmony_ci	if (code == asus->tablet_switch_event_code) {
22618c2ecf20Sopenharmony_ci		asus_wmi_tablet_mode_get_state(asus);
22628c2ecf20Sopenharmony_ci		return;
22638c2ecf20Sopenharmony_ci	}
22648c2ecf20Sopenharmony_ci
22658c2ecf20Sopenharmony_ci	if (asus->fan_boost_mode_available && code == NOTIFY_KBD_FBM) {
22668c2ecf20Sopenharmony_ci		fan_boost_mode_switch_next(asus);
22678c2ecf20Sopenharmony_ci		return;
22688c2ecf20Sopenharmony_ci	}
22698c2ecf20Sopenharmony_ci
22708c2ecf20Sopenharmony_ci	if (asus->throttle_thermal_policy_available && code == NOTIFY_KBD_TTP) {
22718c2ecf20Sopenharmony_ci		throttle_thermal_policy_switch_next(asus);
22728c2ecf20Sopenharmony_ci		return;
22738c2ecf20Sopenharmony_ci	}
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_ci	if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle)
22768c2ecf20Sopenharmony_ci		return;
22778c2ecf20Sopenharmony_ci
22788c2ecf20Sopenharmony_ci	if (!sparse_keymap_report_event(asus->inputdev, code,
22798c2ecf20Sopenharmony_ci					key_value, autorelease))
22808c2ecf20Sopenharmony_ci		pr_info("Unknown key %x pressed\n", code);
22818c2ecf20Sopenharmony_ci}
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_cistatic void asus_wmi_notify(u32 value, void *context)
22848c2ecf20Sopenharmony_ci{
22858c2ecf20Sopenharmony_ci	struct asus_wmi *asus = context;
22868c2ecf20Sopenharmony_ci	int code;
22878c2ecf20Sopenharmony_ci	int i;
22888c2ecf20Sopenharmony_ci
22898c2ecf20Sopenharmony_ci	for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
22908c2ecf20Sopenharmony_ci		code = asus_wmi_get_event_code(value);
22918c2ecf20Sopenharmony_ci		if (code < 0) {
22928c2ecf20Sopenharmony_ci			pr_warn("Failed to get notify code: %d\n", code);
22938c2ecf20Sopenharmony_ci			return;
22948c2ecf20Sopenharmony_ci		}
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ci		if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
22978c2ecf20Sopenharmony_ci			return;
22988c2ecf20Sopenharmony_ci
22998c2ecf20Sopenharmony_ci		asus_wmi_handle_event_code(code, asus);
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_ci		/*
23028c2ecf20Sopenharmony_ci		 * Double check that queue is present:
23038c2ecf20Sopenharmony_ci		 * ATK (with queue) uses 0xff, ASUSWMI (without) 0xd2.
23048c2ecf20Sopenharmony_ci		 */
23058c2ecf20Sopenharmony_ci		if (!asus->wmi_event_queue || value != WMI_EVENT_VALUE_ATK)
23068c2ecf20Sopenharmony_ci			return;
23078c2ecf20Sopenharmony_ci	}
23088c2ecf20Sopenharmony_ci
23098c2ecf20Sopenharmony_ci	pr_warn("Failed to process event queue, last code: 0x%x\n", code);
23108c2ecf20Sopenharmony_ci}
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_cistatic int asus_wmi_notify_queue_flush(struct asus_wmi *asus)
23138c2ecf20Sopenharmony_ci{
23148c2ecf20Sopenharmony_ci	int code;
23158c2ecf20Sopenharmony_ci	int i;
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci	for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
23188c2ecf20Sopenharmony_ci		code = asus_wmi_get_event_code(WMI_EVENT_VALUE_ATK);
23198c2ecf20Sopenharmony_ci		if (code < 0) {
23208c2ecf20Sopenharmony_ci			pr_warn("Failed to get event during flush: %d\n", code);
23218c2ecf20Sopenharmony_ci			return code;
23228c2ecf20Sopenharmony_ci		}
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_ci		if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
23258c2ecf20Sopenharmony_ci			return 0;
23268c2ecf20Sopenharmony_ci	}
23278c2ecf20Sopenharmony_ci
23288c2ecf20Sopenharmony_ci	pr_warn("Failed to flush event queue\n");
23298c2ecf20Sopenharmony_ci	return -EIO;
23308c2ecf20Sopenharmony_ci}
23318c2ecf20Sopenharmony_ci
23328c2ecf20Sopenharmony_ci/* Sysfs **********************************************************************/
23338c2ecf20Sopenharmony_ci
23348c2ecf20Sopenharmony_cistatic ssize_t store_sys_wmi(struct asus_wmi *asus, int devid,
23358c2ecf20Sopenharmony_ci			     const char *buf, size_t count)
23368c2ecf20Sopenharmony_ci{
23378c2ecf20Sopenharmony_ci	u32 retval;
23388c2ecf20Sopenharmony_ci	int err, value;
23398c2ecf20Sopenharmony_ci
23408c2ecf20Sopenharmony_ci	value = asus_wmi_get_devstate_simple(asus, devid);
23418c2ecf20Sopenharmony_ci	if (value < 0)
23428c2ecf20Sopenharmony_ci		return value;
23438c2ecf20Sopenharmony_ci
23448c2ecf20Sopenharmony_ci	err = kstrtoint(buf, 0, &value);
23458c2ecf20Sopenharmony_ci	if (err)
23468c2ecf20Sopenharmony_ci		return err;
23478c2ecf20Sopenharmony_ci
23488c2ecf20Sopenharmony_ci	err = asus_wmi_set_devstate(devid, value, &retval);
23498c2ecf20Sopenharmony_ci	if (err < 0)
23508c2ecf20Sopenharmony_ci		return err;
23518c2ecf20Sopenharmony_ci
23528c2ecf20Sopenharmony_ci	return count;
23538c2ecf20Sopenharmony_ci}
23548c2ecf20Sopenharmony_ci
23558c2ecf20Sopenharmony_cistatic ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf)
23568c2ecf20Sopenharmony_ci{
23578c2ecf20Sopenharmony_ci	int value = asus_wmi_get_devstate_simple(asus, devid);
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_ci	if (value < 0)
23608c2ecf20Sopenharmony_ci		return value;
23618c2ecf20Sopenharmony_ci
23628c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", value);
23638c2ecf20Sopenharmony_ci}
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_ci#define ASUS_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm)			\
23668c2ecf20Sopenharmony_ci	static ssize_t show_##_name(struct device *dev,			\
23678c2ecf20Sopenharmony_ci				    struct device_attribute *attr,	\
23688c2ecf20Sopenharmony_ci				    char *buf)				\
23698c2ecf20Sopenharmony_ci	{								\
23708c2ecf20Sopenharmony_ci		struct asus_wmi *asus = dev_get_drvdata(dev);		\
23718c2ecf20Sopenharmony_ci									\
23728c2ecf20Sopenharmony_ci		return show_sys_wmi(asus, _cm, buf);			\
23738c2ecf20Sopenharmony_ci	}								\
23748c2ecf20Sopenharmony_ci	static ssize_t store_##_name(struct device *dev,		\
23758c2ecf20Sopenharmony_ci				     struct device_attribute *attr,	\
23768c2ecf20Sopenharmony_ci				     const char *buf, size_t count)	\
23778c2ecf20Sopenharmony_ci	{								\
23788c2ecf20Sopenharmony_ci		struct asus_wmi *asus = dev_get_drvdata(dev);		\
23798c2ecf20Sopenharmony_ci									\
23808c2ecf20Sopenharmony_ci		return store_sys_wmi(asus, _cm, buf, count);		\
23818c2ecf20Sopenharmony_ci	}								\
23828c2ecf20Sopenharmony_ci	static struct device_attribute dev_attr_##_name = {		\
23838c2ecf20Sopenharmony_ci		.attr = {						\
23848c2ecf20Sopenharmony_ci			.name = __stringify(_name),			\
23858c2ecf20Sopenharmony_ci			.mode = _mode },				\
23868c2ecf20Sopenharmony_ci		.show   = show_##_name,					\
23878c2ecf20Sopenharmony_ci		.store  = store_##_name,				\
23888c2ecf20Sopenharmony_ci	}
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_ciASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD);
23918c2ecf20Sopenharmony_ciASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA);
23928c2ecf20Sopenharmony_ciASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER);
23938c2ecf20Sopenharmony_ciASUS_WMI_CREATE_DEVICE_ATTR(lid_resume, 0644, ASUS_WMI_DEVID_LID_RESUME);
23948c2ecf20Sopenharmony_ciASUS_WMI_CREATE_DEVICE_ATTR(als_enable, 0644, ASUS_WMI_DEVID_ALS_ENABLE);
23958c2ecf20Sopenharmony_ci
23968c2ecf20Sopenharmony_cistatic ssize_t cpufv_store(struct device *dev, struct device_attribute *attr,
23978c2ecf20Sopenharmony_ci			   const char *buf, size_t count)
23988c2ecf20Sopenharmony_ci{
23998c2ecf20Sopenharmony_ci	int value, rv;
24008c2ecf20Sopenharmony_ci
24018c2ecf20Sopenharmony_ci	rv = kstrtoint(buf, 0, &value);
24028c2ecf20Sopenharmony_ci	if (rv)
24038c2ecf20Sopenharmony_ci		return rv;
24048c2ecf20Sopenharmony_ci
24058c2ecf20Sopenharmony_ci	if (value < 0 || value > 2)
24068c2ecf20Sopenharmony_ci		return -EINVAL;
24078c2ecf20Sopenharmony_ci
24088c2ecf20Sopenharmony_ci	rv = asus_wmi_evaluate_method(ASUS_WMI_METHODID_CFVS, value, 0, NULL);
24098c2ecf20Sopenharmony_ci	if (rv < 0)
24108c2ecf20Sopenharmony_ci		return rv;
24118c2ecf20Sopenharmony_ci
24128c2ecf20Sopenharmony_ci	return count;
24138c2ecf20Sopenharmony_ci}
24148c2ecf20Sopenharmony_ci
24158c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(cpufv);
24168c2ecf20Sopenharmony_ci
24178c2ecf20Sopenharmony_cistatic struct attribute *platform_attributes[] = {
24188c2ecf20Sopenharmony_ci	&dev_attr_cpufv.attr,
24198c2ecf20Sopenharmony_ci	&dev_attr_camera.attr,
24208c2ecf20Sopenharmony_ci	&dev_attr_cardr.attr,
24218c2ecf20Sopenharmony_ci	&dev_attr_touchpad.attr,
24228c2ecf20Sopenharmony_ci	&dev_attr_dgpu_disable.attr,
24238c2ecf20Sopenharmony_ci	&dev_attr_lid_resume.attr,
24248c2ecf20Sopenharmony_ci	&dev_attr_als_enable.attr,
24258c2ecf20Sopenharmony_ci	&dev_attr_fan_boost_mode.attr,
24268c2ecf20Sopenharmony_ci	&dev_attr_throttle_thermal_policy.attr,
24278c2ecf20Sopenharmony_ci	NULL
24288c2ecf20Sopenharmony_ci};
24298c2ecf20Sopenharmony_ci
24308c2ecf20Sopenharmony_cistatic umode_t asus_sysfs_is_visible(struct kobject *kobj,
24318c2ecf20Sopenharmony_ci				    struct attribute *attr, int idx)
24328c2ecf20Sopenharmony_ci{
24338c2ecf20Sopenharmony_ci	struct device *dev = container_of(kobj, struct device, kobj);
24348c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(dev);
24358c2ecf20Sopenharmony_ci	bool ok = true;
24368c2ecf20Sopenharmony_ci	int devid = -1;
24378c2ecf20Sopenharmony_ci
24388c2ecf20Sopenharmony_ci	if (attr == &dev_attr_camera.attr)
24398c2ecf20Sopenharmony_ci		devid = ASUS_WMI_DEVID_CAMERA;
24408c2ecf20Sopenharmony_ci	else if (attr == &dev_attr_cardr.attr)
24418c2ecf20Sopenharmony_ci		devid = ASUS_WMI_DEVID_CARDREADER;
24428c2ecf20Sopenharmony_ci	else if (attr == &dev_attr_touchpad.attr)
24438c2ecf20Sopenharmony_ci		devid = ASUS_WMI_DEVID_TOUCHPAD;
24448c2ecf20Sopenharmony_ci	else if (attr == &dev_attr_lid_resume.attr)
24458c2ecf20Sopenharmony_ci		devid = ASUS_WMI_DEVID_LID_RESUME;
24468c2ecf20Sopenharmony_ci	else if (attr == &dev_attr_als_enable.attr)
24478c2ecf20Sopenharmony_ci		devid = ASUS_WMI_DEVID_ALS_ENABLE;
24488c2ecf20Sopenharmony_ci	else if (attr == &dev_attr_dgpu_disable.attr)
24498c2ecf20Sopenharmony_ci		ok = asus->dgpu_disable_available;
24508c2ecf20Sopenharmony_ci	else if (attr == &dev_attr_fan_boost_mode.attr)
24518c2ecf20Sopenharmony_ci		ok = asus->fan_boost_mode_available;
24528c2ecf20Sopenharmony_ci	else if (attr == &dev_attr_throttle_thermal_policy.attr)
24538c2ecf20Sopenharmony_ci		ok = asus->throttle_thermal_policy_available;
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_ci	if (devid != -1)
24568c2ecf20Sopenharmony_ci		ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
24578c2ecf20Sopenharmony_ci
24588c2ecf20Sopenharmony_ci	return ok ? attr->mode : 0;
24598c2ecf20Sopenharmony_ci}
24608c2ecf20Sopenharmony_ci
24618c2ecf20Sopenharmony_cistatic const struct attribute_group platform_attribute_group = {
24628c2ecf20Sopenharmony_ci	.is_visible = asus_sysfs_is_visible,
24638c2ecf20Sopenharmony_ci	.attrs = platform_attributes
24648c2ecf20Sopenharmony_ci};
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_cistatic void asus_wmi_sysfs_exit(struct platform_device *device)
24678c2ecf20Sopenharmony_ci{
24688c2ecf20Sopenharmony_ci	sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
24698c2ecf20Sopenharmony_ci}
24708c2ecf20Sopenharmony_ci
24718c2ecf20Sopenharmony_cistatic int asus_wmi_sysfs_init(struct platform_device *device)
24728c2ecf20Sopenharmony_ci{
24738c2ecf20Sopenharmony_ci	return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
24748c2ecf20Sopenharmony_ci}
24758c2ecf20Sopenharmony_ci
24768c2ecf20Sopenharmony_ci/* Platform device ************************************************************/
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_cistatic int asus_wmi_platform_init(struct asus_wmi *asus)
24798c2ecf20Sopenharmony_ci{
24808c2ecf20Sopenharmony_ci	struct device *dev = &asus->platform_device->dev;
24818c2ecf20Sopenharmony_ci	char *wmi_uid;
24828c2ecf20Sopenharmony_ci	int rv;
24838c2ecf20Sopenharmony_ci
24848c2ecf20Sopenharmony_ci	/* INIT enable hotkeys on some models */
24858c2ecf20Sopenharmony_ci	if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_INIT, 0, 0, &rv))
24868c2ecf20Sopenharmony_ci		pr_info("Initialization: %#x\n", rv);
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_ci	/* We don't know yet what to do with this version... */
24898c2ecf20Sopenharmony_ci	if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) {
24908c2ecf20Sopenharmony_ci		pr_info("BIOS WMI version: %d.%d\n", rv >> 16, rv & 0xFF);
24918c2ecf20Sopenharmony_ci		asus->spec = rv;
24928c2ecf20Sopenharmony_ci	}
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci	/*
24958c2ecf20Sopenharmony_ci	 * The SFUN method probably allows the original driver to get the list
24968c2ecf20Sopenharmony_ci	 * of features supported by a given model. For now, 0x0100 or 0x0800
24978c2ecf20Sopenharmony_ci	 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
24988c2ecf20Sopenharmony_ci	 * The significance of others is yet to be found.
24998c2ecf20Sopenharmony_ci	 */
25008c2ecf20Sopenharmony_ci	if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SFUN, 0, 0, &rv)) {
25018c2ecf20Sopenharmony_ci		pr_info("SFUN value: %#x\n", rv);
25028c2ecf20Sopenharmony_ci		asus->sfun = rv;
25038c2ecf20Sopenharmony_ci	}
25048c2ecf20Sopenharmony_ci
25058c2ecf20Sopenharmony_ci	/*
25068c2ecf20Sopenharmony_ci	 * Eee PC and Notebooks seems to have different method_id for DSTS,
25078c2ecf20Sopenharmony_ci	 * but it may also be related to the BIOS's SPEC.
25088c2ecf20Sopenharmony_ci	 * Note, on most Eeepc, there is no way to check if a method exist
25098c2ecf20Sopenharmony_ci	 * or note, while on notebooks, they returns 0xFFFFFFFE on failure,
25108c2ecf20Sopenharmony_ci	 * but once again, SPEC may probably be used for that kind of things.
25118c2ecf20Sopenharmony_ci	 *
25128c2ecf20Sopenharmony_ci	 * Additionally at least TUF Gaming series laptops return nothing for
25138c2ecf20Sopenharmony_ci	 * unknown methods, so the detection in this way is not possible.
25148c2ecf20Sopenharmony_ci	 *
25158c2ecf20Sopenharmony_ci	 * There is strong indication that only ACPI WMI devices that have _UID
25168c2ecf20Sopenharmony_ci	 * equal to "ASUSWMI" use DCTS whereas those with "ATK" use DSTS.
25178c2ecf20Sopenharmony_ci	 */
25188c2ecf20Sopenharmony_ci	wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID);
25198c2ecf20Sopenharmony_ci	if (!wmi_uid)
25208c2ecf20Sopenharmony_ci		return -ENODEV;
25218c2ecf20Sopenharmony_ci
25228c2ecf20Sopenharmony_ci	if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) {
25238c2ecf20Sopenharmony_ci		dev_info(dev, "Detected ASUSWMI, use DCTS\n");
25248c2ecf20Sopenharmony_ci		asus->dsts_id = ASUS_WMI_METHODID_DCTS;
25258c2ecf20Sopenharmony_ci	} else {
25268c2ecf20Sopenharmony_ci		dev_info(dev, "Detected %s, not ASUSWMI, use DSTS\n", wmi_uid);
25278c2ecf20Sopenharmony_ci		asus->dsts_id = ASUS_WMI_METHODID_DSTS;
25288c2ecf20Sopenharmony_ci	}
25298c2ecf20Sopenharmony_ci
25308c2ecf20Sopenharmony_ci	/*
25318c2ecf20Sopenharmony_ci	 * Some devices can have multiple event codes stored in a queue before
25328c2ecf20Sopenharmony_ci	 * the module load if it was unloaded intermittently after calling
25338c2ecf20Sopenharmony_ci	 * the INIT method (enables event handling). The WMI notify handler is
25348c2ecf20Sopenharmony_ci	 * expected to retrieve all event codes until a retrieved code equals
25358c2ecf20Sopenharmony_ci	 * queue end marker (One or Ones). Old codes are flushed from the queue
25368c2ecf20Sopenharmony_ci	 * upon module load. Not enabling this when it should be has minimal
25378c2ecf20Sopenharmony_ci	 * visible impact so fall back if anything goes wrong.
25388c2ecf20Sopenharmony_ci	 */
25398c2ecf20Sopenharmony_ci	wmi_uid = wmi_get_acpi_device_uid(asus->driver->event_guid);
25408c2ecf20Sopenharmony_ci	if (wmi_uid && !strcmp(wmi_uid, ASUS_ACPI_UID_ATK)) {
25418c2ecf20Sopenharmony_ci		dev_info(dev, "Detected ATK, enable event queue\n");
25428c2ecf20Sopenharmony_ci
25438c2ecf20Sopenharmony_ci		if (!asus_wmi_notify_queue_flush(asus))
25448c2ecf20Sopenharmony_ci			asus->wmi_event_queue = true;
25458c2ecf20Sopenharmony_ci	}
25468c2ecf20Sopenharmony_ci
25478c2ecf20Sopenharmony_ci	/* CWAP allow to define the behavior of the Fn+F2 key,
25488c2ecf20Sopenharmony_ci	 * this method doesn't seems to be present on Eee PCs */
25498c2ecf20Sopenharmony_ci	if (asus->driver->quirks->wapf >= 0)
25508c2ecf20Sopenharmony_ci		asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,
25518c2ecf20Sopenharmony_ci				      asus->driver->quirks->wapf, NULL);
25528c2ecf20Sopenharmony_ci
25538c2ecf20Sopenharmony_ci	return 0;
25548c2ecf20Sopenharmony_ci}
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_ci/* debugfs ********************************************************************/
25578c2ecf20Sopenharmony_ci
25588c2ecf20Sopenharmony_cistruct asus_wmi_debugfs_node {
25598c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
25608c2ecf20Sopenharmony_ci	char *name;
25618c2ecf20Sopenharmony_ci	int (*show) (struct seq_file *m, void *data);
25628c2ecf20Sopenharmony_ci};
25638c2ecf20Sopenharmony_ci
25648c2ecf20Sopenharmony_cistatic int show_dsts(struct seq_file *m, void *data)
25658c2ecf20Sopenharmony_ci{
25668c2ecf20Sopenharmony_ci	struct asus_wmi *asus = m->private;
25678c2ecf20Sopenharmony_ci	int err;
25688c2ecf20Sopenharmony_ci	u32 retval = -1;
25698c2ecf20Sopenharmony_ci
25708c2ecf20Sopenharmony_ci	err = asus_wmi_get_devstate(asus, asus->debug.dev_id, &retval);
25718c2ecf20Sopenharmony_ci	if (err < 0)
25728c2ecf20Sopenharmony_ci		return err;
25738c2ecf20Sopenharmony_ci
25748c2ecf20Sopenharmony_ci	seq_printf(m, "DSTS(%#x) = %#x\n", asus->debug.dev_id, retval);
25758c2ecf20Sopenharmony_ci
25768c2ecf20Sopenharmony_ci	return 0;
25778c2ecf20Sopenharmony_ci}
25788c2ecf20Sopenharmony_ci
25798c2ecf20Sopenharmony_cistatic int show_devs(struct seq_file *m, void *data)
25808c2ecf20Sopenharmony_ci{
25818c2ecf20Sopenharmony_ci	struct asus_wmi *asus = m->private;
25828c2ecf20Sopenharmony_ci	int err;
25838c2ecf20Sopenharmony_ci	u32 retval = -1;
25848c2ecf20Sopenharmony_ci
25858c2ecf20Sopenharmony_ci	err = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param,
25868c2ecf20Sopenharmony_ci				    &retval);
25878c2ecf20Sopenharmony_ci	if (err < 0)
25888c2ecf20Sopenharmony_ci		return err;
25898c2ecf20Sopenharmony_ci
25908c2ecf20Sopenharmony_ci	seq_printf(m, "DEVS(%#x, %#x) = %#x\n", asus->debug.dev_id,
25918c2ecf20Sopenharmony_ci		   asus->debug.ctrl_param, retval);
25928c2ecf20Sopenharmony_ci
25938c2ecf20Sopenharmony_ci	return 0;
25948c2ecf20Sopenharmony_ci}
25958c2ecf20Sopenharmony_ci
25968c2ecf20Sopenharmony_cistatic int show_call(struct seq_file *m, void *data)
25978c2ecf20Sopenharmony_ci{
25988c2ecf20Sopenharmony_ci	struct asus_wmi *asus = m->private;
25998c2ecf20Sopenharmony_ci	struct bios_args args = {
26008c2ecf20Sopenharmony_ci		.arg0 = asus->debug.dev_id,
26018c2ecf20Sopenharmony_ci		.arg1 = asus->debug.ctrl_param,
26028c2ecf20Sopenharmony_ci	};
26038c2ecf20Sopenharmony_ci	struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
26048c2ecf20Sopenharmony_ci	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
26058c2ecf20Sopenharmony_ci	union acpi_object *obj;
26068c2ecf20Sopenharmony_ci	acpi_status status;
26078c2ecf20Sopenharmony_ci
26088c2ecf20Sopenharmony_ci	status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID,
26098c2ecf20Sopenharmony_ci				     0, asus->debug.method_id,
26108c2ecf20Sopenharmony_ci				     &input, &output);
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
26138c2ecf20Sopenharmony_ci		return -EIO;
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ci	obj = (union acpi_object *)output.pointer;
26168c2ecf20Sopenharmony_ci	if (obj && obj->type == ACPI_TYPE_INTEGER)
26178c2ecf20Sopenharmony_ci		seq_printf(m, "%#x(%#x, %#x) = %#x\n", asus->debug.method_id,
26188c2ecf20Sopenharmony_ci			   asus->debug.dev_id, asus->debug.ctrl_param,
26198c2ecf20Sopenharmony_ci			   (u32) obj->integer.value);
26208c2ecf20Sopenharmony_ci	else
26218c2ecf20Sopenharmony_ci		seq_printf(m, "%#x(%#x, %#x) = t:%d\n", asus->debug.method_id,
26228c2ecf20Sopenharmony_ci			   asus->debug.dev_id, asus->debug.ctrl_param,
26238c2ecf20Sopenharmony_ci			   obj ? obj->type : -1);
26248c2ecf20Sopenharmony_ci
26258c2ecf20Sopenharmony_ci	kfree(obj);
26268c2ecf20Sopenharmony_ci
26278c2ecf20Sopenharmony_ci	return 0;
26288c2ecf20Sopenharmony_ci}
26298c2ecf20Sopenharmony_ci
26308c2ecf20Sopenharmony_cistatic struct asus_wmi_debugfs_node asus_wmi_debug_files[] = {
26318c2ecf20Sopenharmony_ci	{NULL, "devs", show_devs},
26328c2ecf20Sopenharmony_ci	{NULL, "dsts", show_dsts},
26338c2ecf20Sopenharmony_ci	{NULL, "call", show_call},
26348c2ecf20Sopenharmony_ci};
26358c2ecf20Sopenharmony_ci
26368c2ecf20Sopenharmony_cistatic int asus_wmi_debugfs_open(struct inode *inode, struct file *file)
26378c2ecf20Sopenharmony_ci{
26388c2ecf20Sopenharmony_ci	struct asus_wmi_debugfs_node *node = inode->i_private;
26398c2ecf20Sopenharmony_ci
26408c2ecf20Sopenharmony_ci	return single_open(file, node->show, node->asus);
26418c2ecf20Sopenharmony_ci}
26428c2ecf20Sopenharmony_ci
26438c2ecf20Sopenharmony_cistatic const struct file_operations asus_wmi_debugfs_io_ops = {
26448c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
26458c2ecf20Sopenharmony_ci	.open = asus_wmi_debugfs_open,
26468c2ecf20Sopenharmony_ci	.read = seq_read,
26478c2ecf20Sopenharmony_ci	.llseek = seq_lseek,
26488c2ecf20Sopenharmony_ci	.release = single_release,
26498c2ecf20Sopenharmony_ci};
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_cistatic void asus_wmi_debugfs_exit(struct asus_wmi *asus)
26528c2ecf20Sopenharmony_ci{
26538c2ecf20Sopenharmony_ci	debugfs_remove_recursive(asus->debug.root);
26548c2ecf20Sopenharmony_ci}
26558c2ecf20Sopenharmony_ci
26568c2ecf20Sopenharmony_cistatic void asus_wmi_debugfs_init(struct asus_wmi *asus)
26578c2ecf20Sopenharmony_ci{
26588c2ecf20Sopenharmony_ci	int i;
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_ci	asus->debug.root = debugfs_create_dir(asus->driver->name, NULL);
26618c2ecf20Sopenharmony_ci
26628c2ecf20Sopenharmony_ci	debugfs_create_x32("method_id", S_IRUGO | S_IWUSR, asus->debug.root,
26638c2ecf20Sopenharmony_ci			   &asus->debug.method_id);
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_ci	debugfs_create_x32("dev_id", S_IRUGO | S_IWUSR, asus->debug.root,
26668c2ecf20Sopenharmony_ci			   &asus->debug.dev_id);
26678c2ecf20Sopenharmony_ci
26688c2ecf20Sopenharmony_ci	debugfs_create_x32("ctrl_param", S_IRUGO | S_IWUSR, asus->debug.root,
26698c2ecf20Sopenharmony_ci			   &asus->debug.ctrl_param);
26708c2ecf20Sopenharmony_ci
26718c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(asus_wmi_debug_files); i++) {
26728c2ecf20Sopenharmony_ci		struct asus_wmi_debugfs_node *node = &asus_wmi_debug_files[i];
26738c2ecf20Sopenharmony_ci
26748c2ecf20Sopenharmony_ci		node->asus = asus;
26758c2ecf20Sopenharmony_ci		debugfs_create_file(node->name, S_IFREG | S_IRUGO,
26768c2ecf20Sopenharmony_ci				    asus->debug.root, node,
26778c2ecf20Sopenharmony_ci				    &asus_wmi_debugfs_io_ops);
26788c2ecf20Sopenharmony_ci	}
26798c2ecf20Sopenharmony_ci}
26808c2ecf20Sopenharmony_ci
26818c2ecf20Sopenharmony_ci/* Init / exit ****************************************************************/
26828c2ecf20Sopenharmony_ci
26838c2ecf20Sopenharmony_cistatic int asus_wmi_add(struct platform_device *pdev)
26848c2ecf20Sopenharmony_ci{
26858c2ecf20Sopenharmony_ci	struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
26868c2ecf20Sopenharmony_ci	struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv);
26878c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
26888c2ecf20Sopenharmony_ci	const char *chassis_type;
26898c2ecf20Sopenharmony_ci	acpi_status status;
26908c2ecf20Sopenharmony_ci	int err;
26918c2ecf20Sopenharmony_ci	u32 result;
26928c2ecf20Sopenharmony_ci
26938c2ecf20Sopenharmony_ci	asus = kzalloc(sizeof(struct asus_wmi), GFP_KERNEL);
26948c2ecf20Sopenharmony_ci	if (!asus)
26958c2ecf20Sopenharmony_ci		return -ENOMEM;
26968c2ecf20Sopenharmony_ci
26978c2ecf20Sopenharmony_ci	asus->driver = wdrv;
26988c2ecf20Sopenharmony_ci	asus->platform_device = pdev;
26998c2ecf20Sopenharmony_ci	wdrv->platform_device = pdev;
27008c2ecf20Sopenharmony_ci	platform_set_drvdata(asus->platform_device, asus);
27018c2ecf20Sopenharmony_ci
27028c2ecf20Sopenharmony_ci	if (wdrv->detect_quirks)
27038c2ecf20Sopenharmony_ci		wdrv->detect_quirks(asus->driver);
27048c2ecf20Sopenharmony_ci
27058c2ecf20Sopenharmony_ci	err = asus_wmi_platform_init(asus);
27068c2ecf20Sopenharmony_ci	if (err)
27078c2ecf20Sopenharmony_ci		goto fail_platform;
27088c2ecf20Sopenharmony_ci
27098c2ecf20Sopenharmony_ci	err = dgpu_disable_check_present(asus);
27108c2ecf20Sopenharmony_ci	if (err)
27118c2ecf20Sopenharmony_ci		goto fail_dgpu_disable;
27128c2ecf20Sopenharmony_ci
27138c2ecf20Sopenharmony_ci	err = fan_boost_mode_check_present(asus);
27148c2ecf20Sopenharmony_ci	if (err)
27158c2ecf20Sopenharmony_ci		goto fail_fan_boost_mode;
27168c2ecf20Sopenharmony_ci
27178c2ecf20Sopenharmony_ci	err = throttle_thermal_policy_check_present(asus);
27188c2ecf20Sopenharmony_ci	if (err)
27198c2ecf20Sopenharmony_ci		goto fail_throttle_thermal_policy;
27208c2ecf20Sopenharmony_ci	else
27218c2ecf20Sopenharmony_ci		throttle_thermal_policy_set_default(asus);
27228c2ecf20Sopenharmony_ci
27238c2ecf20Sopenharmony_ci	err = asus_wmi_sysfs_init(asus->platform_device);
27248c2ecf20Sopenharmony_ci	if (err)
27258c2ecf20Sopenharmony_ci		goto fail_sysfs;
27268c2ecf20Sopenharmony_ci
27278c2ecf20Sopenharmony_ci	err = asus_wmi_input_init(asus);
27288c2ecf20Sopenharmony_ci	if (err)
27298c2ecf20Sopenharmony_ci		goto fail_input;
27308c2ecf20Sopenharmony_ci
27318c2ecf20Sopenharmony_ci	err = asus_wmi_fan_init(asus); /* probably no problems on error */
27328c2ecf20Sopenharmony_ci
27338c2ecf20Sopenharmony_ci	err = asus_wmi_hwmon_init(asus);
27348c2ecf20Sopenharmony_ci	if (err)
27358c2ecf20Sopenharmony_ci		goto fail_hwmon;
27368c2ecf20Sopenharmony_ci
27378c2ecf20Sopenharmony_ci	err = asus_wmi_led_init(asus);
27388c2ecf20Sopenharmony_ci	if (err)
27398c2ecf20Sopenharmony_ci		goto fail_leds;
27408c2ecf20Sopenharmony_ci
27418c2ecf20Sopenharmony_ci	asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);
27428c2ecf20Sopenharmony_ci	if (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
27438c2ecf20Sopenharmony_ci		asus->driver->wlan_ctrl_by_user = 1;
27448c2ecf20Sopenharmony_ci
27458c2ecf20Sopenharmony_ci	if (!(asus->driver->wlan_ctrl_by_user && ashs_present())) {
27468c2ecf20Sopenharmony_ci		err = asus_wmi_rfkill_init(asus);
27478c2ecf20Sopenharmony_ci		if (err)
27488c2ecf20Sopenharmony_ci			goto fail_rfkill;
27498c2ecf20Sopenharmony_ci	}
27508c2ecf20Sopenharmony_ci
27518c2ecf20Sopenharmony_ci	if (asus->driver->quirks->wmi_force_als_set)
27528c2ecf20Sopenharmony_ci		asus_wmi_set_als();
27538c2ecf20Sopenharmony_ci
27548c2ecf20Sopenharmony_ci	/* Some Asus desktop boards export an acpi-video backlight interface,
27558c2ecf20Sopenharmony_ci	   stop this from showing up */
27568c2ecf20Sopenharmony_ci	chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
27578c2ecf20Sopenharmony_ci	if (chassis_type && !strcmp(chassis_type, "3"))
27588c2ecf20Sopenharmony_ci		acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
27598c2ecf20Sopenharmony_ci
27608c2ecf20Sopenharmony_ci	if (asus->driver->quirks->wmi_backlight_power)
27618c2ecf20Sopenharmony_ci		acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
27628c2ecf20Sopenharmony_ci
27638c2ecf20Sopenharmony_ci	if (asus->driver->quirks->wmi_backlight_native)
27648c2ecf20Sopenharmony_ci		acpi_video_set_dmi_backlight_type(acpi_backlight_native);
27658c2ecf20Sopenharmony_ci
27668c2ecf20Sopenharmony_ci	if (asus->driver->quirks->xusb2pr)
27678c2ecf20Sopenharmony_ci		asus_wmi_set_xusb2pr(asus);
27688c2ecf20Sopenharmony_ci
27698c2ecf20Sopenharmony_ci	if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
27708c2ecf20Sopenharmony_ci		err = asus_wmi_backlight_init(asus);
27718c2ecf20Sopenharmony_ci		if (err && err != -ENODEV)
27728c2ecf20Sopenharmony_ci			goto fail_backlight;
27738c2ecf20Sopenharmony_ci	} else if (asus->driver->quirks->wmi_backlight_set_devstate)
27748c2ecf20Sopenharmony_ci		err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, 2, NULL);
27758c2ecf20Sopenharmony_ci
27768c2ecf20Sopenharmony_ci	if (asus_wmi_has_fnlock_key(asus)) {
27778c2ecf20Sopenharmony_ci		asus->fnlock_locked = true;
27788c2ecf20Sopenharmony_ci		asus_wmi_fnlock_update(asus);
27798c2ecf20Sopenharmony_ci	}
27808c2ecf20Sopenharmony_ci
27818c2ecf20Sopenharmony_ci	status = wmi_install_notify_handler(asus->driver->event_guid,
27828c2ecf20Sopenharmony_ci					    asus_wmi_notify, asus);
27838c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
27848c2ecf20Sopenharmony_ci		pr_err("Unable to register notify handler - %d\n", status);
27858c2ecf20Sopenharmony_ci		err = -ENODEV;
27868c2ecf20Sopenharmony_ci		goto fail_wmi_handler;
27878c2ecf20Sopenharmony_ci	}
27888c2ecf20Sopenharmony_ci
27898c2ecf20Sopenharmony_ci	if (asus->driver->quirks->i8042_filter) {
27908c2ecf20Sopenharmony_ci		err = i8042_install_filter(asus->driver->quirks->i8042_filter);
27918c2ecf20Sopenharmony_ci		if (err)
27928c2ecf20Sopenharmony_ci			pr_warn("Unable to install key filter - %d\n", err);
27938c2ecf20Sopenharmony_ci	}
27948c2ecf20Sopenharmony_ci
27958c2ecf20Sopenharmony_ci	asus_wmi_battery_init(asus);
27968c2ecf20Sopenharmony_ci
27978c2ecf20Sopenharmony_ci	asus_wmi_debugfs_init(asus);
27988c2ecf20Sopenharmony_ci
27998c2ecf20Sopenharmony_ci	return 0;
28008c2ecf20Sopenharmony_ci
28018c2ecf20Sopenharmony_cifail_wmi_handler:
28028c2ecf20Sopenharmony_ci	asus_wmi_backlight_exit(asus);
28038c2ecf20Sopenharmony_cifail_backlight:
28048c2ecf20Sopenharmony_ci	asus_wmi_rfkill_exit(asus);
28058c2ecf20Sopenharmony_cifail_rfkill:
28068c2ecf20Sopenharmony_ci	asus_wmi_led_exit(asus);
28078c2ecf20Sopenharmony_cifail_leds:
28088c2ecf20Sopenharmony_cifail_hwmon:
28098c2ecf20Sopenharmony_ci	asus_wmi_input_exit(asus);
28108c2ecf20Sopenharmony_cifail_input:
28118c2ecf20Sopenharmony_ci	asus_wmi_sysfs_exit(asus->platform_device);
28128c2ecf20Sopenharmony_cifail_sysfs:
28138c2ecf20Sopenharmony_cifail_throttle_thermal_policy:
28148c2ecf20Sopenharmony_cifail_fan_boost_mode:
28158c2ecf20Sopenharmony_cifail_dgpu_disable:
28168c2ecf20Sopenharmony_cifail_platform:
28178c2ecf20Sopenharmony_ci	kfree(asus);
28188c2ecf20Sopenharmony_ci	return err;
28198c2ecf20Sopenharmony_ci}
28208c2ecf20Sopenharmony_ci
28218c2ecf20Sopenharmony_cistatic int asus_wmi_remove(struct platform_device *device)
28228c2ecf20Sopenharmony_ci{
28238c2ecf20Sopenharmony_ci	struct asus_wmi *asus;
28248c2ecf20Sopenharmony_ci
28258c2ecf20Sopenharmony_ci	asus = platform_get_drvdata(device);
28268c2ecf20Sopenharmony_ci	if (asus->driver->quirks->i8042_filter)
28278c2ecf20Sopenharmony_ci		i8042_remove_filter(asus->driver->quirks->i8042_filter);
28288c2ecf20Sopenharmony_ci	wmi_remove_notify_handler(asus->driver->event_guid);
28298c2ecf20Sopenharmony_ci	asus_wmi_backlight_exit(asus);
28308c2ecf20Sopenharmony_ci	asus_wmi_input_exit(asus);
28318c2ecf20Sopenharmony_ci	asus_wmi_led_exit(asus);
28328c2ecf20Sopenharmony_ci	asus_wmi_rfkill_exit(asus);
28338c2ecf20Sopenharmony_ci	asus_wmi_debugfs_exit(asus);
28348c2ecf20Sopenharmony_ci	asus_wmi_sysfs_exit(asus->platform_device);
28358c2ecf20Sopenharmony_ci	asus_fan_set_auto(asus);
28368c2ecf20Sopenharmony_ci	asus_wmi_battery_exit(asus);
28378c2ecf20Sopenharmony_ci
28388c2ecf20Sopenharmony_ci	kfree(asus);
28398c2ecf20Sopenharmony_ci	return 0;
28408c2ecf20Sopenharmony_ci}
28418c2ecf20Sopenharmony_ci
28428c2ecf20Sopenharmony_ci/* Platform driver - hibernate/resume callbacks *******************************/
28438c2ecf20Sopenharmony_ci
28448c2ecf20Sopenharmony_cistatic int asus_hotk_thaw(struct device *device)
28458c2ecf20Sopenharmony_ci{
28468c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(device);
28478c2ecf20Sopenharmony_ci
28488c2ecf20Sopenharmony_ci	if (asus->wlan.rfkill) {
28498c2ecf20Sopenharmony_ci		bool wlan;
28508c2ecf20Sopenharmony_ci
28518c2ecf20Sopenharmony_ci		/*
28528c2ecf20Sopenharmony_ci		 * Work around bios bug - acpi _PTS turns off the wireless led
28538c2ecf20Sopenharmony_ci		 * during suspend.  Normally it restores it on resume, but
28548c2ecf20Sopenharmony_ci		 * we should kick it ourselves in case hibernation is aborted.
28558c2ecf20Sopenharmony_ci		 */
28568c2ecf20Sopenharmony_ci		wlan = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
28578c2ecf20Sopenharmony_ci		asus_wmi_set_devstate(ASUS_WMI_DEVID_WLAN, wlan, NULL);
28588c2ecf20Sopenharmony_ci	}
28598c2ecf20Sopenharmony_ci
28608c2ecf20Sopenharmony_ci	return 0;
28618c2ecf20Sopenharmony_ci}
28628c2ecf20Sopenharmony_ci
28638c2ecf20Sopenharmony_cistatic int asus_hotk_resume(struct device *device)
28648c2ecf20Sopenharmony_ci{
28658c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(device);
28668c2ecf20Sopenharmony_ci
28678c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
28688c2ecf20Sopenharmony_ci		kbd_led_update(asus);
28698c2ecf20Sopenharmony_ci
28708c2ecf20Sopenharmony_ci	if (asus_wmi_has_fnlock_key(asus))
28718c2ecf20Sopenharmony_ci		asus_wmi_fnlock_update(asus);
28728c2ecf20Sopenharmony_ci
28738c2ecf20Sopenharmony_ci	asus_wmi_tablet_mode_get_state(asus);
28748c2ecf20Sopenharmony_ci	return 0;
28758c2ecf20Sopenharmony_ci}
28768c2ecf20Sopenharmony_ci
28778c2ecf20Sopenharmony_cistatic int asus_hotk_restore(struct device *device)
28788c2ecf20Sopenharmony_ci{
28798c2ecf20Sopenharmony_ci	struct asus_wmi *asus = dev_get_drvdata(device);
28808c2ecf20Sopenharmony_ci	int bl;
28818c2ecf20Sopenharmony_ci
28828c2ecf20Sopenharmony_ci	/* Refresh both wlan rfkill state and pci hotplug */
28838c2ecf20Sopenharmony_ci	if (asus->wlan.rfkill)
28848c2ecf20Sopenharmony_ci		asus_rfkill_hotplug(asus);
28858c2ecf20Sopenharmony_ci
28868c2ecf20Sopenharmony_ci	if (asus->bluetooth.rfkill) {
28878c2ecf20Sopenharmony_ci		bl = !asus_wmi_get_devstate_simple(asus,
28888c2ecf20Sopenharmony_ci						   ASUS_WMI_DEVID_BLUETOOTH);
28898c2ecf20Sopenharmony_ci		rfkill_set_sw_state(asus->bluetooth.rfkill, bl);
28908c2ecf20Sopenharmony_ci	}
28918c2ecf20Sopenharmony_ci	if (asus->wimax.rfkill) {
28928c2ecf20Sopenharmony_ci		bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WIMAX);
28938c2ecf20Sopenharmony_ci		rfkill_set_sw_state(asus->wimax.rfkill, bl);
28948c2ecf20Sopenharmony_ci	}
28958c2ecf20Sopenharmony_ci	if (asus->wwan3g.rfkill) {
28968c2ecf20Sopenharmony_ci		bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WWAN3G);
28978c2ecf20Sopenharmony_ci		rfkill_set_sw_state(asus->wwan3g.rfkill, bl);
28988c2ecf20Sopenharmony_ci	}
28998c2ecf20Sopenharmony_ci	if (asus->gps.rfkill) {
29008c2ecf20Sopenharmony_ci		bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPS);
29018c2ecf20Sopenharmony_ci		rfkill_set_sw_state(asus->gps.rfkill, bl);
29028c2ecf20Sopenharmony_ci	}
29038c2ecf20Sopenharmony_ci	if (asus->uwb.rfkill) {
29048c2ecf20Sopenharmony_ci		bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_UWB);
29058c2ecf20Sopenharmony_ci		rfkill_set_sw_state(asus->uwb.rfkill, bl);
29068c2ecf20Sopenharmony_ci	}
29078c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
29088c2ecf20Sopenharmony_ci		kbd_led_update(asus);
29098c2ecf20Sopenharmony_ci
29108c2ecf20Sopenharmony_ci	if (asus_wmi_has_fnlock_key(asus))
29118c2ecf20Sopenharmony_ci		asus_wmi_fnlock_update(asus);
29128c2ecf20Sopenharmony_ci
29138c2ecf20Sopenharmony_ci	asus_wmi_tablet_mode_get_state(asus);
29148c2ecf20Sopenharmony_ci	return 0;
29158c2ecf20Sopenharmony_ci}
29168c2ecf20Sopenharmony_ci
29178c2ecf20Sopenharmony_cistatic const struct dev_pm_ops asus_pm_ops = {
29188c2ecf20Sopenharmony_ci	.thaw = asus_hotk_thaw,
29198c2ecf20Sopenharmony_ci	.restore = asus_hotk_restore,
29208c2ecf20Sopenharmony_ci	.resume = asus_hotk_resume,
29218c2ecf20Sopenharmony_ci};
29228c2ecf20Sopenharmony_ci
29238c2ecf20Sopenharmony_ci/* Registration ***************************************************************/
29248c2ecf20Sopenharmony_ci
29258c2ecf20Sopenharmony_cistatic int asus_wmi_probe(struct platform_device *pdev)
29268c2ecf20Sopenharmony_ci{
29278c2ecf20Sopenharmony_ci	struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
29288c2ecf20Sopenharmony_ci	struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv);
29298c2ecf20Sopenharmony_ci	int ret;
29308c2ecf20Sopenharmony_ci
29318c2ecf20Sopenharmony_ci	if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) {
29328c2ecf20Sopenharmony_ci		pr_warn("ASUS Management GUID not found\n");
29338c2ecf20Sopenharmony_ci		return -ENODEV;
29348c2ecf20Sopenharmony_ci	}
29358c2ecf20Sopenharmony_ci
29368c2ecf20Sopenharmony_ci	if (wdrv->event_guid && !wmi_has_guid(wdrv->event_guid)) {
29378c2ecf20Sopenharmony_ci		pr_warn("ASUS Event GUID not found\n");
29388c2ecf20Sopenharmony_ci		return -ENODEV;
29398c2ecf20Sopenharmony_ci	}
29408c2ecf20Sopenharmony_ci
29418c2ecf20Sopenharmony_ci	if (wdrv->probe) {
29428c2ecf20Sopenharmony_ci		ret = wdrv->probe(pdev);
29438c2ecf20Sopenharmony_ci		if (ret)
29448c2ecf20Sopenharmony_ci			return ret;
29458c2ecf20Sopenharmony_ci	}
29468c2ecf20Sopenharmony_ci
29478c2ecf20Sopenharmony_ci	return asus_wmi_add(pdev);
29488c2ecf20Sopenharmony_ci}
29498c2ecf20Sopenharmony_ci
29508c2ecf20Sopenharmony_cistatic bool used;
29518c2ecf20Sopenharmony_ci
29528c2ecf20Sopenharmony_ciint __init_or_module asus_wmi_register_driver(struct asus_wmi_driver *driver)
29538c2ecf20Sopenharmony_ci{
29548c2ecf20Sopenharmony_ci	struct platform_driver *platform_driver;
29558c2ecf20Sopenharmony_ci	struct platform_device *platform_device;
29568c2ecf20Sopenharmony_ci
29578c2ecf20Sopenharmony_ci	if (used)
29588c2ecf20Sopenharmony_ci		return -EBUSY;
29598c2ecf20Sopenharmony_ci
29608c2ecf20Sopenharmony_ci	platform_driver = &driver->platform_driver;
29618c2ecf20Sopenharmony_ci	platform_driver->remove = asus_wmi_remove;
29628c2ecf20Sopenharmony_ci	platform_driver->driver.owner = driver->owner;
29638c2ecf20Sopenharmony_ci	platform_driver->driver.name = driver->name;
29648c2ecf20Sopenharmony_ci	platform_driver->driver.pm = &asus_pm_ops;
29658c2ecf20Sopenharmony_ci
29668c2ecf20Sopenharmony_ci	platform_device = platform_create_bundle(platform_driver,
29678c2ecf20Sopenharmony_ci						 asus_wmi_probe,
29688c2ecf20Sopenharmony_ci						 NULL, 0, NULL, 0);
29698c2ecf20Sopenharmony_ci	if (IS_ERR(platform_device))
29708c2ecf20Sopenharmony_ci		return PTR_ERR(platform_device);
29718c2ecf20Sopenharmony_ci
29728c2ecf20Sopenharmony_ci	used = true;
29738c2ecf20Sopenharmony_ci	return 0;
29748c2ecf20Sopenharmony_ci}
29758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(asus_wmi_register_driver);
29768c2ecf20Sopenharmony_ci
29778c2ecf20Sopenharmony_civoid asus_wmi_unregister_driver(struct asus_wmi_driver *driver)
29788c2ecf20Sopenharmony_ci{
29798c2ecf20Sopenharmony_ci	platform_device_unregister(driver->platform_device);
29808c2ecf20Sopenharmony_ci	platform_driver_unregister(&driver->platform_driver);
29818c2ecf20Sopenharmony_ci	used = false;
29828c2ecf20Sopenharmony_ci}
29838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(asus_wmi_unregister_driver);
29848c2ecf20Sopenharmony_ci
29858c2ecf20Sopenharmony_cistatic int __init asus_wmi_init(void)
29868c2ecf20Sopenharmony_ci{
29878c2ecf20Sopenharmony_ci	pr_info("ASUS WMI generic driver loaded\n");
29888c2ecf20Sopenharmony_ci	return 0;
29898c2ecf20Sopenharmony_ci}
29908c2ecf20Sopenharmony_ci
29918c2ecf20Sopenharmony_cistatic void __exit asus_wmi_exit(void)
29928c2ecf20Sopenharmony_ci{
29938c2ecf20Sopenharmony_ci	pr_info("ASUS WMI generic driver unloaded\n");
29948c2ecf20Sopenharmony_ci}
29958c2ecf20Sopenharmony_ci
29968c2ecf20Sopenharmony_cimodule_init(asus_wmi_init);
29978c2ecf20Sopenharmony_cimodule_exit(asus_wmi_exit);
2998