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