blob: 508e6ad4779310fac66bef2f19a92978b13ed049 [file] [log] [blame]
Yong Wangee027e42010-03-21 10:26:34 +08001/*
Corentin Charye12e6d92011-02-26 10:20:31 +01002 * Asus PC WMI hotkey driver
Yong Wangee027e42010-03-21 10:26:34 +08003 *
4 * Copyright(C) 2010 Intel Corporation.
Corentin Chary57ab7da2011-02-26 10:20:32 +01005 * Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>
Yong Wangee027e42010-03-21 10:26:34 +08006 *
7 * Portions based on wistron_btns.c:
8 * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
9 * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
10 * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
Yong Wang81248882010-04-11 09:26:33 +080027#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
28
Yong Wangee027e42010-03-21 10:26:34 +080029#include <linux/kernel.h>
30#include <linux/module.h>
31#include <linux/init.h>
32#include <linux/types.h>
Tejun Heoa32f3922010-04-05 11:37:59 +090033#include <linux/slab.h>
Yong Wangee027e42010-03-21 10:26:34 +080034#include <linux/input.h>
35#include <linux/input/sparse-keymap.h>
Yong Wang3d7b1652010-04-11 09:27:54 +080036#include <linux/fb.h>
37#include <linux/backlight.h>
Corentin Chary084fca62010-11-29 08:14:06 +010038#include <linux/leds.h>
Corentin Charyba48fdb2010-11-29 08:14:07 +010039#include <linux/rfkill.h>
Corentin Charyafa7c882011-02-06 13:28:28 +010040#include <linux/pci.h>
41#include <linux/pci_hotplug.h>
Corentin Charye07babd2011-02-26 10:20:42 +010042#include <linux/hwmon.h>
43#include <linux/hwmon-sysfs.h>
Corentin Chary8c1b2d82010-11-29 08:14:09 +010044#include <linux/debugfs.h>
45#include <linux/seq_file.h>
Daniel Drakeffb6ce72018-10-09 14:40:55 +080046#include <linux/platform_data/x86/asus-wmi.h>
Yong Wang45f2c692010-04-11 09:27:19 +080047#include <linux/platform_device.h>
Corentin Chary6118b8a2011-07-01 11:34:36 +020048#include <linux/thermal.h>
Lv Zheng8b484632013-12-03 08:49:16 +080049#include <linux/acpi.h>
Hans de Goeded8c66f62014-07-08 10:47:21 +020050#include <linux/dmi.h>
AceLan Kao272c77d2012-06-13 09:32:06 +020051#include <acpi/video.h>
Yong Wangee027e42010-03-21 10:26:34 +080052
Corentin Charye12e6d92011-02-26 10:20:31 +010053#include "asus-wmi.h"
Yong Wang45f2c692010-04-11 09:27:19 +080054
Corentin Chary5909c652012-12-17 16:00:05 -080055MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>, "
Corentin Charye12e6d92011-02-26 10:20:31 +010056 "Yong Wang <yong.y.wang@intel.com>");
57MODULE_DESCRIPTION("Asus Generic WMI Driver");
Yong Wangee027e42010-03-21 10:26:34 +080058MODULE_LICENSE("GPL");
59
Corentin Charye12e6d92011-02-26 10:20:31 +010060#define to_asus_wmi_driver(pdrv) \
61 (container_of((pdrv), struct asus_wmi_driver, platform_driver))
Yong Wangee027e42010-03-21 10:26:34 +080062
Corentin Charye12e6d92011-02-26 10:20:31 +010063#define ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
Yong Wangee027e42010-03-21 10:26:34 +080064
Corentin Chary33e0e6f2011-02-06 13:28:34 +010065#define NOTIFY_BRNUP_MIN 0x11
66#define NOTIFY_BRNUP_MAX 0x1f
67#define NOTIFY_BRNDOWN_MIN 0x20
68#define NOTIFY_BRNDOWN_MAX 0x2e
Chris Chiu487579b2019-04-18 14:46:48 +080069#define NOTIFY_FNLOCK_TOGGLE 0x4e
Corentin Charye9809c02011-07-01 11:34:31 +020070#define NOTIFY_KBD_BRTUP 0xc4
71#define NOTIFY_KBD_BRTDWN 0xc5
Chris Chiued99d292018-06-20 22:46:45 +080072#define NOTIFY_KBD_BRTTOGGLE 0xc7
Yurii Pavlovskyib096f622019-05-14 21:07:05 +020073#define NOTIFY_KBD_FBM 0x99
Yong Wangee027e42010-03-21 10:26:34 +080074
Chris Chiu487579b2019-04-18 14:46:48 +080075#define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0)
76
Kast Bernd53e755c2015-05-13 16:24:16 +020077#define ASUS_FAN_DESC "cpu_fan"
78#define ASUS_FAN_MFUN 0x13
79#define ASUS_FAN_SFUN_READ 0x06
80#define ASUS_FAN_SFUN_WRITE 0x07
81#define ASUS_FAN_CTRL_MANUAL 1
82#define ASUS_FAN_CTRL_AUTO 2
83
Yurii Pavlovskyib096f622019-05-14 21:07:05 +020084#define ASUS_FAN_MODE_NORMAL 0
85#define ASUS_FAN_MODE_OVERBOOST 1
86#define ASUS_FAN_MODE_OVERBOOST_MASK 0x01
87#define ASUS_FAN_MODE_SILENT 2
88#define ASUS_FAN_MODE_SILENT_MASK 0x02
89#define ASUS_FAN_MODES_MASK 0x03
90
Kai-Chuan Hsieh8023eff2016-09-01 23:55:55 +080091#define USB_INTEL_XUSB2PR 0xD0
92#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
93
Yurii Pavlovskyie0668f22019-05-14 21:00:31 +020094#define ASUS_ACPI_UID_ASUSWMI "ASUSWMI"
Yurii Pavlovskyi1a373d12019-05-14 21:02:09 +020095#define ASUS_ACPI_UID_ATK "ATK"
96
97#define WMI_EVENT_QUEUE_SIZE 0x10
98#define WMI_EVENT_QUEUE_END 0x1
99#define WMI_EVENT_MASK 0xFFFF
100/* The WMI hotkey event value is always the same. */
101#define WMI_EVENT_VALUE_ATK 0xFF
Yurii Pavlovskyie0668f22019-05-14 21:00:31 +0200102
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +0200103#define WMI_EVENT_MASK 0xFFFF
104
João Paulo Rechi Vita71050ae2017-02-20 14:50:22 -0500105static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
106
João Paulo Rechi Vitacf48bf92018-05-22 14:30:15 -0700107static bool ashs_present(void)
108{
109 int i = 0;
110 while (ashs_ids[i]) {
111 if (acpi_dev_found(ashs_ids[i++]))
112 return true;
113 }
114 return false;
115}
116
Yong Wang3d7b1652010-04-11 09:27:54 +0800117struct bios_args {
Corentin Charyd33da3b2011-02-26 10:20:35 +0100118 u32 arg0;
119 u32 arg1;
Yurii Pavlovskyi98e865a2019-05-14 20:54:50 +0200120 u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */
Corentin Charyd33da3b2011-02-26 10:20:35 +0100121} __packed;
Yong Wang3d7b1652010-04-11 09:27:54 +0800122
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100123/*
Kast Bernd53e755c2015-05-13 16:24:16 +0200124 * Struct that's used for all methods called via AGFN. Naming is
125 * identically to the AML code.
126 */
127struct agfn_args {
128 u16 mfun; /* probably "Multi-function" to be called */
129 u16 sfun; /* probably "Sub-function" to be called */
130 u16 len; /* size of the hole struct, including subfunction fields */
131 u8 stas; /* not used by now */
132 u8 err; /* zero on success */
133} __packed;
134
135/* struct used for calling fan read and write methods */
136struct fan_args {
137 struct agfn_args agfn; /* common fields */
138 u8 fan; /* fan number: 0: set auto mode 1: 1st fan */
139 u32 speed; /* read: RPM/100 - write: 0-255 */
140} __packed;
141
142/*
Corentin Charye12e6d92011-02-26 10:20:31 +0100143 * <platform>/ - debugfs root directory
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100144 * dev_id - current dev_id
145 * ctrl_param - current ctrl_param
Corentin Charyef343492011-02-26 10:20:39 +0100146 * method_id - current method_id
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100147 * devs - call DEVS(dev_id, ctrl_param) and print result
148 * dsts - call DSTS(dev_id) and print result
Corentin Charyef343492011-02-26 10:20:39 +0100149 * call - call method_id(dev_id, ctrl_param) and print result
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100150 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100151struct asus_wmi_debug {
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100152 struct dentry *root;
Corentin Charyef343492011-02-26 10:20:39 +0100153 u32 method_id;
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100154 u32 dev_id;
155 u32 ctrl_param;
156};
157
Corentin Charya7ce3f02011-02-26 10:20:33 +0100158struct asus_rfkill {
159 struct asus_wmi *asus;
160 struct rfkill *rfkill;
161 u32 dev_id;
162};
163
Corentin Charye12e6d92011-02-26 10:20:31 +0100164struct asus_wmi {
Corentin Chary1d070f82011-02-26 10:20:36 +0100165 int dsts_id;
Corentin Chary46dbca82011-02-26 10:20:38 +0100166 int spec;
167 int sfun;
Yurii Pavlovskyi1a373d12019-05-14 21:02:09 +0200168 bool wmi_event_queue;
Corentin Chary1d070f82011-02-26 10:20:36 +0100169
Yong Wang81248882010-04-11 09:26:33 +0800170 struct input_dev *inputdev;
Yong Wang3d7b1652010-04-11 09:27:54 +0800171 struct backlight_device *backlight_device;
Corentin Chary27c136c2010-11-29 08:14:05 +0100172 struct platform_device *platform_device;
Corentin Chary084fca62010-11-29 08:14:06 +0100173
AceLan Kao6cae06e2012-07-27 16:51:59 +0800174 struct led_classdev wlan_led;
175 int wlan_led_wk;
Corentin Chary084fca62010-11-29 08:14:06 +0100176 struct led_classdev tpd_led;
177 int tpd_led_wk;
Corentin Charye9809c02011-07-01 11:34:31 +0200178 struct led_classdev kbd_led;
179 int kbd_led_wk;
Maxime Bellengé4c059842017-09-23 18:23:37 +0200180 struct led_classdev lightbar_led;
181 int lightbar_led_wk;
Corentin Chary084fca62010-11-29 08:14:06 +0100182 struct workqueue_struct *led_workqueue;
183 struct work_struct tpd_led_work;
AceLan Kao6cae06e2012-07-27 16:51:59 +0800184 struct work_struct wlan_led_work;
Maxime Bellengé4c059842017-09-23 18:23:37 +0200185 struct work_struct lightbar_led_work;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100186
Corentin Charya7ce3f02011-02-26 10:20:33 +0100187 struct asus_rfkill wlan;
188 struct asus_rfkill bluetooth;
189 struct asus_rfkill wimax;
190 struct asus_rfkill wwan3g;
Corentin Chary43be8bd2011-07-01 11:34:40 +0200191 struct asus_rfkill gps;
Corentin Charya912d322011-07-01 11:34:41 +0200192 struct asus_rfkill uwb;
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100193
Kast Bernd53e755c2015-05-13 16:24:16 +0200194 bool asus_hwmon_fan_manual_mode;
195 int asus_hwmon_num_fans;
196 int asus_hwmon_pwm;
197
Yurii Pavlovskyib096f622019-05-14 21:07:05 +0200198 bool fan_mode_available;
199 u8 fan_mode_mask;
200 u8 fan_mode;
201
Lukas Wunner125450f2018-09-08 09:59:01 +0200202 struct hotplug_slot hotplug_slot;
Corentin Charyafa7c882011-02-06 13:28:28 +0100203 struct mutex hotplug_lock;
Corentin Chary279f8f92011-02-06 13:28:29 +0100204 struct mutex wmi_lock;
205 struct workqueue_struct *hotplug_workqueue;
206 struct work_struct hotplug_work;
Corentin Charyafa7c882011-02-06 13:28:28 +0100207
Chris Chiu487579b2019-04-18 14:46:48 +0800208 bool fnlock_locked;
209
Corentin Charye12e6d92011-02-26 10:20:31 +0100210 struct asus_wmi_debug debug;
211
212 struct asus_wmi_driver *driver;
Yong Wang81248882010-04-11 09:26:33 +0800213};
214
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +0200215/* Input **********************************************************************/
216
Corentin Charye12e6d92011-02-26 10:20:31 +0100217static int asus_wmi_input_init(struct asus_wmi *asus)
Yong Wangee027e42010-03-21 10:26:34 +0800218{
219 int err;
220
Corentin Charye12e6d92011-02-26 10:20:31 +0100221 asus->inputdev = input_allocate_device();
222 if (!asus->inputdev)
Yong Wangee027e42010-03-21 10:26:34 +0800223 return -ENOMEM;
224
Corentin Chary58a9f392011-03-30 16:32:32 +0200225 asus->inputdev->name = asus->driver->input_name;
226 asus->inputdev->phys = asus->driver->input_phys;
Corentin Charye12e6d92011-02-26 10:20:31 +0100227 asus->inputdev->id.bustype = BUS_HOST;
228 asus->inputdev->dev.parent = &asus->platform_device->dev;
Seth Forshee39bbde02011-07-04 09:49:20 +0200229 set_bit(EV_REP, asus->inputdev->evbit);
Yong Wangee027e42010-03-21 10:26:34 +0800230
Corentin Charye12e6d92011-02-26 10:20:31 +0100231 err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);
Yong Wangee027e42010-03-21 10:26:34 +0800232 if (err)
233 goto err_free_dev;
234
Corentin Charye12e6d92011-02-26 10:20:31 +0100235 err = input_register_device(asus->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800236 if (err)
Michał Kępieńc2ef3a12017-03-09 13:11:39 +0100237 goto err_free_dev;
Yong Wangee027e42010-03-21 10:26:34 +0800238
239 return 0;
240
Yong Wangee027e42010-03-21 10:26:34 +0800241err_free_dev:
Corentin Charye12e6d92011-02-26 10:20:31 +0100242 input_free_device(asus->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800243 return err;
244}
245
Corentin Charye12e6d92011-02-26 10:20:31 +0100246static void asus_wmi_input_exit(struct asus_wmi *asus)
Yong Wang81248882010-04-11 09:26:33 +0800247{
Michał Kępieńc2ef3a12017-03-09 13:11:39 +0100248 if (asus->inputdev)
Corentin Charye12e6d92011-02-26 10:20:31 +0100249 input_unregister_device(asus->inputdev);
Yong Wang81248882010-04-11 09:26:33 +0800250
Corentin Charye12e6d92011-02-26 10:20:31 +0100251 asus->inputdev = NULL;
Yong Wang81248882010-04-11 09:26:33 +0800252}
253
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +0200254/* WMI ************************************************************************/
255
Yurii Pavlovskyi98e865a2019-05-14 20:54:50 +0200256static int asus_wmi_evaluate_method3(u32 method_id,
257 u32 arg0, u32 arg1, u32 arg2, u32 *retval)
Yong Wang3d7b1652010-04-11 09:27:54 +0800258{
Corentin Charyd33da3b2011-02-26 10:20:35 +0100259 struct bios_args args = {
260 .arg0 = arg0,
261 .arg1 = arg1,
Yurii Pavlovskyi98e865a2019-05-14 20:54:50 +0200262 .arg2 = arg2,
Corentin Charyd33da3b2011-02-26 10:20:35 +0100263 };
264 struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
Yong Wang3d7b1652010-04-11 09:27:54 +0800265 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
Yong Wang3d7b1652010-04-11 09:27:54 +0800266 acpi_status status;
Corentin Charyd33da3b2011-02-26 10:20:35 +0100267 union acpi_object *obj;
Rickard Strandqvist8ad3be12014-06-01 14:58:52 +0200268 u32 tmp = 0;
Yong Wang3d7b1652010-04-11 09:27:54 +0800269
Pali Rohár0fe57262017-08-12 09:44:16 +0200270 status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
Corentin Charyafa7c882011-02-06 13:28:28 +0100271 &input, &output);
Yong Wang3d7b1652010-04-11 09:27:54 +0800272
273 if (ACPI_FAILURE(status))
Yurii Pavlovskyi1827f3f2019-05-14 21:03:50 +0200274 return -EIO;
Yong Wang3d7b1652010-04-11 09:27:54 +0800275
276 obj = (union acpi_object *)output.pointer;
277 if (obj && obj->type == ACPI_TYPE_INTEGER)
Corentin Charye12e6d92011-02-26 10:20:31 +0100278 tmp = (u32) obj->integer.value;
Yong Wang3d7b1652010-04-11 09:27:54 +0800279
Corentin Chary2a3f0062010-11-29 08:14:10 +0100280 if (retval)
281 *retval = tmp;
Yong Wang3d7b1652010-04-11 09:27:54 +0800282
283 kfree(obj);
284
Corentin Charyd33da3b2011-02-26 10:20:35 +0100285 if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
286 return -ENODEV;
287
288 return 0;
Yong Wang3d7b1652010-04-11 09:27:54 +0800289}
Yurii Pavlovskyi98e865a2019-05-14 20:54:50 +0200290
291int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
292{
293 return asus_wmi_evaluate_method3(method_id, arg0, arg1, 0, retval);
294}
Daniel Drakeffb6ce72018-10-09 14:40:55 +0800295EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method);
Yong Wang3d7b1652010-04-11 09:27:54 +0800296
Kast Bernd53e755c2015-05-13 16:24:16 +0200297static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
298{
299 struct acpi_buffer input;
300 u64 phys_addr;
301 u32 retval;
302 u32 status = -1;
303
304 /*
305 * Copy to dma capable address otherwise memory corruption occurs as
306 * bios has to be able to access it.
307 */
308 input.pointer = kzalloc(args.length, GFP_DMA | GFP_KERNEL);
309 input.length = args.length;
310 if (!input.pointer)
311 return -ENOMEM;
312 phys_addr = virt_to_phys(input.pointer);
313 memcpy(input.pointer, args.pointer, args.length);
314
315 status = asus_wmi_evaluate_method(ASUS_WMI_METHODID_AGFN,
316 phys_addr, 0, &retval);
317 if (!status)
318 memcpy(args.pointer, input.pointer, args.length);
319
320 kfree(input.pointer);
321 if (status)
322 return -ENXIO;
323
324 return retval;
325}
326
Corentin Chary1d070f82011-02-26 10:20:36 +0100327static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
Corentin Charyd33da3b2011-02-26 10:20:35 +0100328{
Corentin Chary1d070f82011-02-26 10:20:36 +0100329 return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);
Corentin Charyd33da3b2011-02-26 10:20:35 +0100330}
331
332static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
Corentin Chary1d070f82011-02-26 10:20:36 +0100333 u32 *retval)
Yong Wang3d7b1652010-04-11 09:27:54 +0800334{
Corentin Charyd33da3b2011-02-26 10:20:35 +0100335 return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id,
336 ctrl_param, retval);
Yong Wang3d7b1652010-04-11 09:27:54 +0800337}
338
Corentin Chary5c956382011-02-06 13:28:31 +0100339/* Helper for special devices with magic return codes */
Corentin Chary1d070f82011-02-26 10:20:36 +0100340static int asus_wmi_get_devstate_bits(struct asus_wmi *asus,
341 u32 dev_id, u32 mask)
Corentin Chary5c956382011-02-06 13:28:31 +0100342{
343 u32 retval = 0;
Corentin Charyd33da3b2011-02-26 10:20:35 +0100344 int err;
Corentin Chary5c956382011-02-06 13:28:31 +0100345
Corentin Chary1d070f82011-02-26 10:20:36 +0100346 err = asus_wmi_get_devstate(asus, dev_id, &retval);
Corentin Chary5c956382011-02-06 13:28:31 +0100347
Corentin Charyd33da3b2011-02-26 10:20:35 +0100348 if (err < 0)
349 return err;
Corentin Chary5c956382011-02-06 13:28:31 +0100350
Corentin Charye12e6d92011-02-26 10:20:31 +0100351 if (!(retval & ASUS_WMI_DSTS_PRESENCE_BIT))
Corentin Chary5c956382011-02-06 13:28:31 +0100352 return -ENODEV;
353
Corentin Charya75fe0d2011-02-26 10:20:34 +0100354 if (mask == ASUS_WMI_DSTS_STATUS_BIT) {
355 if (retval & ASUS_WMI_DSTS_UNKNOWN_BIT)
356 return -ENODEV;
357 }
358
Corentin Charyb7187262011-02-06 13:28:39 +0100359 return retval & mask;
360}
361
Corentin Chary1d070f82011-02-26 10:20:36 +0100362static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id)
Corentin Charyb7187262011-02-06 13:28:39 +0100363{
Corentin Chary1d070f82011-02-26 10:20:36 +0100364 return asus_wmi_get_devstate_bits(asus, dev_id,
365 ASUS_WMI_DSTS_STATUS_BIT);
Corentin Chary5c956382011-02-06 13:28:31 +0100366}
367
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +0200368/* LEDs ***********************************************************************/
369
Corentin Chary084fca62010-11-29 08:14:06 +0100370/*
371 * These functions actually update the LED's, and are called from a
372 * workqueue. By doing this as separate work rather than when the LED
Corentin Charye12e6d92011-02-26 10:20:31 +0100373 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
Corentin Chary084fca62010-11-29 08:14:06 +0100374 * potentially bad time, such as a timer interrupt.
375 */
376static void tpd_led_update(struct work_struct *work)
377{
378 int ctrl_param;
Corentin Charye12e6d92011-02-26 10:20:31 +0100379 struct asus_wmi *asus;
Corentin Chary084fca62010-11-29 08:14:06 +0100380
Corentin Charye12e6d92011-02-26 10:20:31 +0100381 asus = container_of(work, struct asus_wmi, tpd_led_work);
Corentin Chary084fca62010-11-29 08:14:06 +0100382
Corentin Charye12e6d92011-02-26 10:20:31 +0100383 ctrl_param = asus->tpd_led_wk;
384 asus_wmi_set_devstate(ASUS_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL);
Corentin Chary084fca62010-11-29 08:14:06 +0100385}
386
387static void tpd_led_set(struct led_classdev *led_cdev,
388 enum led_brightness value)
389{
Corentin Charye12e6d92011-02-26 10:20:31 +0100390 struct asus_wmi *asus;
Corentin Chary084fca62010-11-29 08:14:06 +0100391
Corentin Charye12e6d92011-02-26 10:20:31 +0100392 asus = container_of(led_cdev, struct asus_wmi, tpd_led);
Corentin Chary084fca62010-11-29 08:14:06 +0100393
Corentin Charye12e6d92011-02-26 10:20:31 +0100394 asus->tpd_led_wk = !!value;
395 queue_work(asus->led_workqueue, &asus->tpd_led_work);
Corentin Chary084fca62010-11-29 08:14:06 +0100396}
397
Corentin Charye12e6d92011-02-26 10:20:31 +0100398static int read_tpd_led_state(struct asus_wmi *asus)
Corentin Chary084fca62010-11-29 08:14:06 +0100399{
Corentin Chary1d070f82011-02-26 10:20:36 +0100400 return asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_TOUCHPAD_LED);
Corentin Chary084fca62010-11-29 08:14:06 +0100401}
402
403static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
404{
Corentin Charye12e6d92011-02-26 10:20:31 +0100405 struct asus_wmi *asus;
Corentin Chary084fca62010-11-29 08:14:06 +0100406
Corentin Charye12e6d92011-02-26 10:20:31 +0100407 asus = container_of(led_cdev, struct asus_wmi, tpd_led);
Corentin Chary084fca62010-11-29 08:14:06 +0100408
Corentin Charye12e6d92011-02-26 10:20:31 +0100409 return read_tpd_led_state(asus);
Corentin Chary084fca62010-11-29 08:14:06 +0100410}
411
Jian-Hong Pan9fe44fc2018-09-27 16:50:09 +0800412static void kbd_led_update(struct asus_wmi *asus)
Corentin Chary084fca62010-11-29 08:14:06 +0100413{
Corentin Charye9809c02011-07-01 11:34:31 +0200414 int ctrl_param = 0;
Corentin Chary084fca62010-11-29 08:14:06 +0100415
Corentin Charye9809c02011-07-01 11:34:31 +0200416 /*
417 * bits 0-2: level
418 * bit 7: light on/off
419 */
420 if (asus->kbd_led_wk > 0)
421 ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
Corentin Chary084fca62010-11-29 08:14:06 +0100422
Corentin Charye9809c02011-07-01 11:34:31 +0200423 asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);
424}
Corentin Chary084fca62010-11-29 08:14:06 +0100425
Corentin Charye9809c02011-07-01 11:34:31 +0200426static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
427{
428 int retval;
429
430 /*
431 * bits 0-2: level
432 * bit 7: light on/off
433 * bit 8-10: environment (0: dark, 1: normal, 2: light)
434 * bit 17: status unknown
435 */
436 retval = asus_wmi_get_devstate_bits(asus, ASUS_WMI_DEVID_KBD_BACKLIGHT,
437 0xFFFF);
438
Corentin Charyaf965e92011-07-01 11:34:34 +0200439 /* Unknown status is considered as off */
Corentin Charye9809c02011-07-01 11:34:31 +0200440 if (retval == 0x8000)
Corentin Charyaf965e92011-07-01 11:34:34 +0200441 retval = 0;
Corentin Charye9809c02011-07-01 11:34:31 +0200442
443 if (retval >= 0) {
444 if (level)
Corentin Charyc09b2232012-03-20 09:53:04 +0100445 *level = retval & 0x7F;
Corentin Charye9809c02011-07-01 11:34:31 +0200446 if (env)
447 *env = (retval >> 8) & 0x7F;
448 retval = 0;
Corentin Chary084fca62010-11-29 08:14:06 +0100449 }
450
Corentin Charye9809c02011-07-01 11:34:31 +0200451 return retval;
452}
453
Chris Chiudbb3d782018-06-20 22:46:44 +0800454static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
Corentin Charye9809c02011-07-01 11:34:31 +0200455{
456 struct asus_wmi *asus;
Chris Chiudbb3d782018-06-20 22:46:44 +0800457 int max_level;
Corentin Charye9809c02011-07-01 11:34:31 +0200458
459 asus = container_of(led_cdev, struct asus_wmi, kbd_led);
Chris Chiudbb3d782018-06-20 22:46:44 +0800460 max_level = asus->kbd_led.max_brightness;
Corentin Charye9809c02011-07-01 11:34:31 +0200461
Chris Chiudbb3d782018-06-20 22:46:44 +0800462 if (value > max_level)
463 value = max_level;
Corentin Charye9809c02011-07-01 11:34:31 +0200464 else if (value < 0)
465 value = 0;
466
467 asus->kbd_led_wk = value;
Jian-Hong Pan9fe44fc2018-09-27 16:50:09 +0800468 kbd_led_update(asus);
Corentin Charye9809c02011-07-01 11:34:31 +0200469}
470
Chris Chiudbb3d782018-06-20 22:46:44 +0800471static void kbd_led_set(struct led_classdev *led_cdev,
472 enum led_brightness value)
473{
Yurii Pavlovskyi3e581672019-05-14 21:07:46 +0200474 /* Prevent disabling keyboard backlight on module unregister */
475 if (led_cdev->flags & LED_UNREGISTERING)
476 return;
477
Chris Chiudbb3d782018-06-20 22:46:44 +0800478 do_kbd_led_set(led_cdev, value);
479}
480
Jian-Hong Pan29f6eb52018-10-22 18:00:04 +0800481static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value)
482{
483 struct led_classdev *led_cdev = &asus->kbd_led;
484
485 do_kbd_led_set(led_cdev, value);
486 led_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk);
487}
488
Corentin Charye9809c02011-07-01 11:34:31 +0200489static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
490{
491 struct asus_wmi *asus;
492 int retval, value;
493
494 asus = container_of(led_cdev, struct asus_wmi, kbd_led);
495
496 retval = kbd_led_read(asus, &value, NULL);
497
498 if (retval < 0)
499 return retval;
500
501 return value;
Corentin Chary084fca62010-11-29 08:14:06 +0100502}
503
AceLan Kao6cae06e2012-07-27 16:51:59 +0800504static int wlan_led_unknown_state(struct asus_wmi *asus)
505{
506 u32 result;
507
508 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
509
510 return result & ASUS_WMI_DSTS_UNKNOWN_BIT;
511}
512
513static int wlan_led_presence(struct asus_wmi *asus)
514{
515 u32 result;
516
517 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
518
519 return result & ASUS_WMI_DSTS_PRESENCE_BIT;
520}
521
522static void wlan_led_update(struct work_struct *work)
523{
524 int ctrl_param;
525 struct asus_wmi *asus;
526
527 asus = container_of(work, struct asus_wmi, wlan_led_work);
528
529 ctrl_param = asus->wlan_led_wk;
530 asus_wmi_set_devstate(ASUS_WMI_DEVID_WIRELESS_LED, ctrl_param, NULL);
531}
532
533static void wlan_led_set(struct led_classdev *led_cdev,
534 enum led_brightness value)
535{
536 struct asus_wmi *asus;
537
538 asus = container_of(led_cdev, struct asus_wmi, wlan_led);
539
540 asus->wlan_led_wk = !!value;
541 queue_work(asus->led_workqueue, &asus->wlan_led_work);
542}
543
544static enum led_brightness wlan_led_get(struct led_classdev *led_cdev)
545{
546 struct asus_wmi *asus;
547 u32 result;
548
549 asus = container_of(led_cdev, struct asus_wmi, wlan_led);
550 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
551
552 return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
553}
554
Maxime Bellengé4c059842017-09-23 18:23:37 +0200555static void lightbar_led_update(struct work_struct *work)
556{
557 struct asus_wmi *asus;
558 int ctrl_param;
559
560 asus = container_of(work, struct asus_wmi, lightbar_led_work);
561
562 ctrl_param = asus->lightbar_led_wk;
563 asus_wmi_set_devstate(ASUS_WMI_DEVID_LIGHTBAR, ctrl_param, NULL);
564}
565
566static void lightbar_led_set(struct led_classdev *led_cdev,
567 enum led_brightness value)
568{
569 struct asus_wmi *asus;
570
571 asus = container_of(led_cdev, struct asus_wmi, lightbar_led);
572
573 asus->lightbar_led_wk = !!value;
574 queue_work(asus->led_workqueue, &asus->lightbar_led_work);
575}
576
577static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev)
578{
579 struct asus_wmi *asus;
580 u32 result;
581
582 asus = container_of(led_cdev, struct asus_wmi, lightbar_led);
583 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result);
584
585 return result & ASUS_WMI_DSTS_LIGHTBAR_MASK;
586}
587
588static int lightbar_led_presence(struct asus_wmi *asus)
589{
590 u32 result;
591
592 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result);
593
594 return result & ASUS_WMI_DSTS_PRESENCE_BIT;
595}
596
Corentin Charye12e6d92011-02-26 10:20:31 +0100597static void asus_wmi_led_exit(struct asus_wmi *asus)
Corentin Chary084fca62010-11-29 08:14:06 +0100598{
Axel Line9298022011-08-08 17:16:01 +0800599 if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
600 led_classdev_unregister(&asus->kbd_led);
601 if (!IS_ERR_OR_NULL(asus->tpd_led.dev))
Corentin Charye12e6d92011-02-26 10:20:31 +0100602 led_classdev_unregister(&asus->tpd_led);
AceLan Kao6cae06e2012-07-27 16:51:59 +0800603 if (!IS_ERR_OR_NULL(asus->wlan_led.dev))
604 led_classdev_unregister(&asus->wlan_led);
Maxime Bellengé4c059842017-09-23 18:23:37 +0200605 if (!IS_ERR_OR_NULL(asus->lightbar_led.dev))
606 led_classdev_unregister(&asus->lightbar_led);
Corentin Charye12e6d92011-02-26 10:20:31 +0100607 if (asus->led_workqueue)
608 destroy_workqueue(asus->led_workqueue);
Corentin Chary084fca62010-11-29 08:14:06 +0100609}
610
Corentin Charye9809c02011-07-01 11:34:31 +0200611static int asus_wmi_led_init(struct asus_wmi *asus)
612{
Oleksij Rempel30734042015-09-14 11:16:30 +0200613 int rv = 0, led_val;
Corentin Charye9809c02011-07-01 11:34:31 +0200614
615 asus->led_workqueue = create_singlethread_workqueue("led_workqueue");
616 if (!asus->led_workqueue)
617 return -ENOMEM;
618
619 if (read_tpd_led_state(asus) >= 0) {
620 INIT_WORK(&asus->tpd_led_work, tpd_led_update);
621
622 asus->tpd_led.name = "asus::touchpad";
623 asus->tpd_led.brightness_set = tpd_led_set;
624 asus->tpd_led.brightness_get = tpd_led_get;
625 asus->tpd_led.max_brightness = 1;
626
627 rv = led_classdev_register(&asus->platform_device->dev,
628 &asus->tpd_led);
629 if (rv)
630 goto error;
631 }
632
Yurii Pavlovskyi8853a2f2019-05-14 20:51:25 +0200633 if (!kbd_led_read(asus, &led_val, NULL)) {
Oleksij Rempel30734042015-09-14 11:16:30 +0200634 asus->kbd_led_wk = led_val;
Corentin Charye9809c02011-07-01 11:34:31 +0200635 asus->kbd_led.name = "asus::kbd_backlight";
Chris Chiudbb3d782018-06-20 22:46:44 +0800636 asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
Corentin Charye9809c02011-07-01 11:34:31 +0200637 asus->kbd_led.brightness_set = kbd_led_set;
638 asus->kbd_led.brightness_get = kbd_led_get;
639 asus->kbd_led.max_brightness = 3;
640
641 rv = led_classdev_register(&asus->platform_device->dev,
642 &asus->kbd_led);
AceLan Kao6cae06e2012-07-27 16:51:59 +0800643 if (rv)
644 goto error;
645 }
646
AceLan Kaof5156232014-07-09 16:18:18 +0800647 if (wlan_led_presence(asus) && (asus->driver->quirks->wapf > 0)) {
AceLan Kao6cae06e2012-07-27 16:51:59 +0800648 INIT_WORK(&asus->wlan_led_work, wlan_led_update);
649
650 asus->wlan_led.name = "asus::wlan";
651 asus->wlan_led.brightness_set = wlan_led_set;
652 if (!wlan_led_unknown_state(asus))
653 asus->wlan_led.brightness_get = wlan_led_get;
654 asus->wlan_led.flags = LED_CORE_SUSPENDRESUME;
655 asus->wlan_led.max_brightness = 1;
656 asus->wlan_led.default_trigger = "asus-wlan";
657
658 rv = led_classdev_register(&asus->platform_device->dev,
659 &asus->wlan_led);
Maxime Bellengé4c059842017-09-23 18:23:37 +0200660 if (rv)
661 goto error;
662 }
663
664 if (lightbar_led_presence(asus)) {
665 INIT_WORK(&asus->lightbar_led_work, lightbar_led_update);
666
667 asus->lightbar_led.name = "asus::lightbar";
668 asus->lightbar_led.brightness_set = lightbar_led_set;
669 asus->lightbar_led.brightness_get = lightbar_led_get;
670 asus->lightbar_led.max_brightness = 1;
671
672 rv = led_classdev_register(&asus->platform_device->dev,
673 &asus->lightbar_led);
Corentin Charye9809c02011-07-01 11:34:31 +0200674 }
675
676error:
677 if (rv)
678 asus_wmi_led_exit(asus);
679
680 return rv;
681}
682
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +0200683/* RF *************************************************************************/
Corentin Charye9809c02011-07-01 11:34:31 +0200684
Corentin Chary084fca62010-11-29 08:14:06 +0100685/*
Corentin Charyafa7c882011-02-06 13:28:28 +0100686 * PCI hotplug (for wlan rfkill)
687 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100688static bool asus_wlan_rfkill_blocked(struct asus_wmi *asus)
Corentin Charyafa7c882011-02-06 13:28:28 +0100689{
Corentin Chary1d070f82011-02-26 10:20:36 +0100690 int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
Corentin Charyafa7c882011-02-06 13:28:28 +0100691
Corentin Chary5c956382011-02-06 13:28:31 +0100692 if (result < 0)
Corentin Charyafa7c882011-02-06 13:28:28 +0100693 return false;
Corentin Chary5c956382011-02-06 13:28:31 +0100694 return !result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100695}
696
Corentin Charye12e6d92011-02-26 10:20:31 +0100697static void asus_rfkill_hotplug(struct asus_wmi *asus)
Corentin Charyafa7c882011-02-06 13:28:28 +0100698{
699 struct pci_dev *dev;
700 struct pci_bus *bus;
Corentin Chary279f8f92011-02-06 13:28:29 +0100701 bool blocked;
Corentin Charyafa7c882011-02-06 13:28:28 +0100702 bool absent;
703 u32 l;
704
Corentin Charye12e6d92011-02-26 10:20:31 +0100705 mutex_lock(&asus->wmi_lock);
706 blocked = asus_wlan_rfkill_blocked(asus);
707 mutex_unlock(&asus->wmi_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +0100708
Corentin Charye12e6d92011-02-26 10:20:31 +0100709 mutex_lock(&asus->hotplug_lock);
Rafael J. Wysocki8b9ec1d2014-01-10 15:27:08 +0100710 pci_lock_rescan_remove();
Corentin Charyafa7c882011-02-06 13:28:28 +0100711
Corentin Charya7ce3f02011-02-26 10:20:33 +0100712 if (asus->wlan.rfkill)
713 rfkill_set_sw_state(asus->wlan.rfkill, blocked);
Corentin Chary279f8f92011-02-06 13:28:29 +0100714
Lukas Wunner125450f2018-09-08 09:59:01 +0200715 if (asus->hotplug_slot.ops) {
Corentin Charyafa7c882011-02-06 13:28:28 +0100716 bus = pci_find_bus(0, 1);
717 if (!bus) {
Joe Perches5ad77dc2011-03-29 15:21:35 -0700718 pr_warn("Unable to find PCI bus 1?\n");
Corentin Charyafa7c882011-02-06 13:28:28 +0100719 goto out_unlock;
720 }
721
722 if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
723 pr_err("Unable to read PCI config space?\n");
724 goto out_unlock;
725 }
726 absent = (l == 0xffffffff);
727
728 if (blocked != absent) {
Joe Perches5ad77dc2011-03-29 15:21:35 -0700729 pr_warn("BIOS says wireless lan is %s, "
730 "but the pci device is %s\n",
731 blocked ? "blocked" : "unblocked",
732 absent ? "absent" : "present");
733 pr_warn("skipped wireless hotplug as probably "
734 "inappropriate for this model\n");
Corentin Charyafa7c882011-02-06 13:28:28 +0100735 goto out_unlock;
736 }
737
738 if (!blocked) {
739 dev = pci_get_slot(bus, 0);
740 if (dev) {
741 /* Device already present */
742 pci_dev_put(dev);
743 goto out_unlock;
744 }
745 dev = pci_scan_single_device(bus, 0);
746 if (dev) {
747 pci_bus_assign_resources(bus);
Yijing Wangc893d132014-05-30 11:01:03 +0800748 pci_bus_add_device(dev);
Corentin Charyafa7c882011-02-06 13:28:28 +0100749 }
750 } else {
751 dev = pci_get_slot(bus, 0);
752 if (dev) {
Yinghai Lu210647a2012-02-25 13:54:20 -0800753 pci_stop_and_remove_bus_device(dev);
Corentin Charyafa7c882011-02-06 13:28:28 +0100754 pci_dev_put(dev);
755 }
756 }
757 }
758
759out_unlock:
Rafael J. Wysocki8b9ec1d2014-01-10 15:27:08 +0100760 pci_unlock_rescan_remove();
Corentin Charye12e6d92011-02-26 10:20:31 +0100761 mutex_unlock(&asus->hotplug_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +0100762}
763
Corentin Charye12e6d92011-02-26 10:20:31 +0100764static void asus_rfkill_notify(acpi_handle handle, u32 event, void *data)
Corentin Charyafa7c882011-02-06 13:28:28 +0100765{
Corentin Charye12e6d92011-02-26 10:20:31 +0100766 struct asus_wmi *asus = data;
Corentin Charyafa7c882011-02-06 13:28:28 +0100767
768 if (event != ACPI_NOTIFY_BUS_CHECK)
769 return;
770
Corentin Chary279f8f92011-02-06 13:28:29 +0100771 /*
Corentin Charye12e6d92011-02-26 10:20:31 +0100772 * We can't call directly asus_rfkill_hotplug because most
Corentin Chary279f8f92011-02-06 13:28:29 +0100773 * of the time WMBC is still being executed and not reetrant.
774 * There is currently no way to tell ACPICA that we want this
Corentin Charye12e6d92011-02-26 10:20:31 +0100775 * method to be serialized, we schedule a asus_rfkill_hotplug
Corentin Chary279f8f92011-02-06 13:28:29 +0100776 * call later, in a safer context.
777 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100778 queue_work(asus->hotplug_workqueue, &asus->hotplug_work);
Corentin Charyafa7c882011-02-06 13:28:28 +0100779}
780
Corentin Charye12e6d92011-02-26 10:20:31 +0100781static int asus_register_rfkill_notifier(struct asus_wmi *asus, char *node)
Corentin Charyafa7c882011-02-06 13:28:28 +0100782{
783 acpi_status status;
784 acpi_handle handle;
785
786 status = acpi_get_handle(NULL, node, &handle);
787
788 if (ACPI_SUCCESS(status)) {
789 status = acpi_install_notify_handler(handle,
790 ACPI_SYSTEM_NOTIFY,
Corentin Charye12e6d92011-02-26 10:20:31 +0100791 asus_rfkill_notify, asus);
Corentin Charyafa7c882011-02-06 13:28:28 +0100792 if (ACPI_FAILURE(status))
Joe Perches5ad77dc2011-03-29 15:21:35 -0700793 pr_warn("Failed to register notify on %s\n", node);
Corentin Charyafa7c882011-02-06 13:28:28 +0100794 } else
795 return -ENODEV;
796
797 return 0;
798}
799
Corentin Charye12e6d92011-02-26 10:20:31 +0100800static void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node)
Corentin Charyafa7c882011-02-06 13:28:28 +0100801{
802 acpi_status status = AE_OK;
803 acpi_handle handle;
804
805 status = acpi_get_handle(NULL, node, &handle);
806
807 if (ACPI_SUCCESS(status)) {
808 status = acpi_remove_notify_handler(handle,
Corentin Charye12e6d92011-02-26 10:20:31 +0100809 ACPI_SYSTEM_NOTIFY,
810 asus_rfkill_notify);
Corentin Charyafa7c882011-02-06 13:28:28 +0100811 if (ACPI_FAILURE(status))
812 pr_err("Error removing rfkill notify handler %s\n",
Corentin Charye12e6d92011-02-26 10:20:31 +0100813 node);
Corentin Charyafa7c882011-02-06 13:28:28 +0100814 }
815}
816
Corentin Charye12e6d92011-02-26 10:20:31 +0100817static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot,
818 u8 *value)
Corentin Charyafa7c882011-02-06 13:28:28 +0100819{
Lukas Wunner125450f2018-09-08 09:59:01 +0200820 struct asus_wmi *asus = container_of(hotplug_slot,
821 struct asus_wmi, hotplug_slot);
Corentin Chary1d070f82011-02-26 10:20:36 +0100822 int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
Corentin Charyafa7c882011-02-06 13:28:28 +0100823
Corentin Chary5c956382011-02-06 13:28:31 +0100824 if (result < 0)
825 return result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100826
Corentin Chary5c956382011-02-06 13:28:31 +0100827 *value = !!result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100828 return 0;
829}
830
Lukas Wunner81c4b5b2018-09-08 09:59:01 +0200831static const struct hotplug_slot_ops asus_hotplug_slot_ops = {
Corentin Charye12e6d92011-02-26 10:20:31 +0100832 .get_adapter_status = asus_get_adapter_status,
833 .get_power_status = asus_get_adapter_status,
Corentin Charyafa7c882011-02-06 13:28:28 +0100834};
835
Corentin Charye12e6d92011-02-26 10:20:31 +0100836static void asus_hotplug_work(struct work_struct *work)
Corentin Chary279f8f92011-02-06 13:28:29 +0100837{
Corentin Charye12e6d92011-02-26 10:20:31 +0100838 struct asus_wmi *asus;
Corentin Chary279f8f92011-02-06 13:28:29 +0100839
Corentin Charye12e6d92011-02-26 10:20:31 +0100840 asus = container_of(work, struct asus_wmi, hotplug_work);
841 asus_rfkill_hotplug(asus);
Corentin Chary279f8f92011-02-06 13:28:29 +0100842}
843
Corentin Charye12e6d92011-02-26 10:20:31 +0100844static int asus_setup_pci_hotplug(struct asus_wmi *asus)
Corentin Charyafa7c882011-02-06 13:28:28 +0100845{
846 int ret = -ENOMEM;
847 struct pci_bus *bus = pci_find_bus(0, 1);
848
849 if (!bus) {
850 pr_err("Unable to find wifi PCI bus\n");
851 return -ENODEV;
852 }
853
Corentin Charye12e6d92011-02-26 10:20:31 +0100854 asus->hotplug_workqueue =
855 create_singlethread_workqueue("hotplug_workqueue");
856 if (!asus->hotplug_workqueue)
Corentin Chary279f8f92011-02-06 13:28:29 +0100857 goto error_workqueue;
858
Corentin Charye12e6d92011-02-26 10:20:31 +0100859 INIT_WORK(&asus->hotplug_work, asus_hotplug_work);
Corentin Chary279f8f92011-02-06 13:28:29 +0100860
Lukas Wunner125450f2018-09-08 09:59:01 +0200861 asus->hotplug_slot.ops = &asus_hotplug_slot_ops;
Corentin Charyafa7c882011-02-06 13:28:28 +0100862
Lukas Wunner125450f2018-09-08 09:59:01 +0200863 ret = pci_hp_register(&asus->hotplug_slot, bus, 0, "asus-wifi");
Corentin Charyafa7c882011-02-06 13:28:28 +0100864 if (ret) {
865 pr_err("Unable to register hotplug slot - %d\n", ret);
866 goto error_register;
867 }
868
869 return 0;
870
871error_register:
Lukas Wunner125450f2018-09-08 09:59:01 +0200872 asus->hotplug_slot.ops = NULL;
Corentin Charye12e6d92011-02-26 10:20:31 +0100873 destroy_workqueue(asus->hotplug_workqueue);
Corentin Chary279f8f92011-02-06 13:28:29 +0100874error_workqueue:
Corentin Charyafa7c882011-02-06 13:28:28 +0100875 return ret;
876}
877
878/*
Corentin Charyba48fdb2010-11-29 08:14:07 +0100879 * Rfkill devices
880 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100881static int asus_rfkill_set(void *data, bool blocked)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100882{
Corentin Charya7ce3f02011-02-26 10:20:33 +0100883 struct asus_rfkill *priv = data;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100884 u32 ctrl_param = !blocked;
AceLan Kaoa50bd122012-07-26 17:13:31 +0800885 u32 dev_id = priv->dev_id;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100886
AceLan Kaoa50bd122012-07-26 17:13:31 +0800887 /*
888 * If the user bit is set, BIOS can't set and record the wlan status,
889 * it will report the value read from id ASUS_WMI_DEVID_WLAN_LED
890 * while we query the wlan status through WMI(ASUS_WMI_DEVID_WLAN).
891 * So, we have to record wlan status in id ASUS_WMI_DEVID_WLAN_LED
892 * while setting the wlan status through WMI.
893 * This is also the behavior that windows app will do.
894 */
895 if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
896 priv->asus->driver->wlan_ctrl_by_user)
897 dev_id = ASUS_WMI_DEVID_WLAN_LED;
898
899 return asus_wmi_set_devstate(dev_id, ctrl_param, NULL);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100900}
901
Corentin Charye12e6d92011-02-26 10:20:31 +0100902static void asus_rfkill_query(struct rfkill *rfkill, void *data)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100903{
Corentin Charya7ce3f02011-02-26 10:20:33 +0100904 struct asus_rfkill *priv = data;
Corentin Chary5c956382011-02-06 13:28:31 +0100905 int result;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100906
Corentin Chary1d070f82011-02-26 10:20:36 +0100907 result = asus_wmi_get_devstate_simple(priv->asus, priv->dev_id);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100908
Corentin Chary5c956382011-02-06 13:28:31 +0100909 if (result < 0)
Corentin Charye12e6d92011-02-26 10:20:31 +0100910 return;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100911
Corentin Charya7ce3f02011-02-26 10:20:33 +0100912 rfkill_set_sw_state(priv->rfkill, !result);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100913}
914
Corentin Charye12e6d92011-02-26 10:20:31 +0100915static int asus_rfkill_wlan_set(void *data, bool blocked)
Corentin Chary279f8f92011-02-06 13:28:29 +0100916{
Corentin Charya7ce3f02011-02-26 10:20:33 +0100917 struct asus_rfkill *priv = data;
918 struct asus_wmi *asus = priv->asus;
Corentin Chary279f8f92011-02-06 13:28:29 +0100919 int ret;
920
921 /*
922 * This handler is enabled only if hotplug is enabled.
Corentin Charye12e6d92011-02-26 10:20:31 +0100923 * In this case, the asus_wmi_set_devstate() will
Corentin Chary279f8f92011-02-06 13:28:29 +0100924 * trigger a wmi notification and we need to wait
925 * this call to finish before being able to call
926 * any wmi method
927 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100928 mutex_lock(&asus->wmi_lock);
Corentin Charya7ce3f02011-02-26 10:20:33 +0100929 ret = asus_rfkill_set(data, blocked);
Corentin Charye12e6d92011-02-26 10:20:31 +0100930 mutex_unlock(&asus->wmi_lock);
Corentin Chary279f8f92011-02-06 13:28:29 +0100931 return ret;
932}
933
Corentin Charye12e6d92011-02-26 10:20:31 +0100934static const struct rfkill_ops asus_rfkill_wlan_ops = {
935 .set_block = asus_rfkill_wlan_set,
Corentin Charya7ce3f02011-02-26 10:20:33 +0100936 .query = asus_rfkill_query,
Corentin Chary279f8f92011-02-06 13:28:29 +0100937};
938
Corentin Charye12e6d92011-02-26 10:20:31 +0100939static const struct rfkill_ops asus_rfkill_ops = {
940 .set_block = asus_rfkill_set,
941 .query = asus_rfkill_query,
Corentin Charyba48fdb2010-11-29 08:14:07 +0100942};
943
Corentin Charye12e6d92011-02-26 10:20:31 +0100944static int asus_new_rfkill(struct asus_wmi *asus,
Corentin Charya7ce3f02011-02-26 10:20:33 +0100945 struct asus_rfkill *arfkill,
Corentin Charye12e6d92011-02-26 10:20:31 +0100946 const char *name, enum rfkill_type type, int dev_id)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100947{
Corentin Chary1d070f82011-02-26 10:20:36 +0100948 int result = asus_wmi_get_devstate_simple(asus, dev_id);
Corentin Charya7ce3f02011-02-26 10:20:33 +0100949 struct rfkill **rfkill = &arfkill->rfkill;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100950
Corentin Chary5c956382011-02-06 13:28:31 +0100951 if (result < 0)
952 return result;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100953
Corentin Charya7ce3f02011-02-26 10:20:33 +0100954 arfkill->dev_id = dev_id;
955 arfkill->asus = asus;
956
AceLan Kaoc87992d2012-03-20 09:53:08 +0100957 if (dev_id == ASUS_WMI_DEVID_WLAN &&
958 asus->driver->quirks->hotplug_wireless)
Corentin Charye12e6d92011-02-26 10:20:31 +0100959 *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,
Corentin Charya7ce3f02011-02-26 10:20:33 +0100960 &asus_rfkill_wlan_ops, arfkill);
Corentin Chary279f8f92011-02-06 13:28:29 +0100961 else
Corentin Charye12e6d92011-02-26 10:20:31 +0100962 *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,
Corentin Charya7ce3f02011-02-26 10:20:33 +0100963 &asus_rfkill_ops, arfkill);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100964
965 if (!*rfkill)
966 return -EINVAL;
967
AceLan Kaoe8f56c82013-05-30 10:31:50 +0800968 if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
AceLan Kaof5156232014-07-09 16:18:18 +0800969 (asus->driver->quirks->wapf > 0))
AceLan Kao6cae06e2012-07-27 16:51:59 +0800970 rfkill_set_led_trigger_name(*rfkill, "asus-wlan");
971
Corentin Chary5c956382011-02-06 13:28:31 +0100972 rfkill_init_sw_state(*rfkill, !result);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100973 result = rfkill_register(*rfkill);
974 if (result) {
975 rfkill_destroy(*rfkill);
976 *rfkill = NULL;
977 return result;
978 }
979 return 0;
980}
981
Corentin Charye12e6d92011-02-26 10:20:31 +0100982static void asus_wmi_rfkill_exit(struct asus_wmi *asus)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100983{
João Paulo Rechi Vitacf48bf92018-05-22 14:30:15 -0700984 if (asus->driver->wlan_ctrl_by_user && ashs_present())
985 return;
986
Corentin Charye12e6d92011-02-26 10:20:31 +0100987 asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P5");
988 asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P6");
989 asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P7");
Corentin Charya7ce3f02011-02-26 10:20:33 +0100990 if (asus->wlan.rfkill) {
991 rfkill_unregister(asus->wlan.rfkill);
992 rfkill_destroy(asus->wlan.rfkill);
993 asus->wlan.rfkill = NULL;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100994 }
Corentin Charyafa7c882011-02-06 13:28:28 +0100995 /*
996 * Refresh pci hotplug in case the rfkill state was changed after
Corentin Charye12e6d92011-02-26 10:20:31 +0100997 * asus_unregister_rfkill_notifier()
Corentin Charyafa7c882011-02-06 13:28:28 +0100998 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100999 asus_rfkill_hotplug(asus);
Lukas Wunner125450f2018-09-08 09:59:01 +02001000 if (asus->hotplug_slot.ops)
1001 pci_hp_deregister(&asus->hotplug_slot);
Corentin Charye12e6d92011-02-26 10:20:31 +01001002 if (asus->hotplug_workqueue)
1003 destroy_workqueue(asus->hotplug_workqueue);
Corentin Charyafa7c882011-02-06 13:28:28 +01001004
Corentin Charya7ce3f02011-02-26 10:20:33 +01001005 if (asus->bluetooth.rfkill) {
1006 rfkill_unregister(asus->bluetooth.rfkill);
1007 rfkill_destroy(asus->bluetooth.rfkill);
1008 asus->bluetooth.rfkill = NULL;
Corentin Charyba48fdb2010-11-29 08:14:07 +01001009 }
Corentin Charya7ce3f02011-02-26 10:20:33 +01001010 if (asus->wimax.rfkill) {
1011 rfkill_unregister(asus->wimax.rfkill);
1012 rfkill_destroy(asus->wimax.rfkill);
1013 asus->wimax.rfkill = NULL;
Corentin Chary2e9e1592011-02-06 13:28:37 +01001014 }
Corentin Charya7ce3f02011-02-26 10:20:33 +01001015 if (asus->wwan3g.rfkill) {
1016 rfkill_unregister(asus->wwan3g.rfkill);
1017 rfkill_destroy(asus->wwan3g.rfkill);
1018 asus->wwan3g.rfkill = NULL;
Corentin Charyba48fdb2010-11-29 08:14:07 +01001019 }
Corentin Chary43be8bd2011-07-01 11:34:40 +02001020 if (asus->gps.rfkill) {
1021 rfkill_unregister(asus->gps.rfkill);
1022 rfkill_destroy(asus->gps.rfkill);
1023 asus->gps.rfkill = NULL;
1024 }
Corentin Charya912d322011-07-01 11:34:41 +02001025 if (asus->uwb.rfkill) {
1026 rfkill_unregister(asus->uwb.rfkill);
1027 rfkill_destroy(asus->uwb.rfkill);
1028 asus->uwb.rfkill = NULL;
1029 }
Corentin Charyba48fdb2010-11-29 08:14:07 +01001030}
1031
Corentin Charye12e6d92011-02-26 10:20:31 +01001032static int asus_wmi_rfkill_init(struct asus_wmi *asus)
Corentin Charyba48fdb2010-11-29 08:14:07 +01001033{
1034 int result = 0;
1035
Corentin Charye12e6d92011-02-26 10:20:31 +01001036 mutex_init(&asus->hotplug_lock);
1037 mutex_init(&asus->wmi_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +01001038
Corentin Charya7ce3f02011-02-26 10:20:33 +01001039 result = asus_new_rfkill(asus, &asus->wlan, "asus-wlan",
1040 RFKILL_TYPE_WLAN, ASUS_WMI_DEVID_WLAN);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001041
1042 if (result && result != -ENODEV)
1043 goto exit;
1044
Corentin Charya7ce3f02011-02-26 10:20:33 +01001045 result = asus_new_rfkill(asus, &asus->bluetooth,
Corentin Charye12e6d92011-02-26 10:20:31 +01001046 "asus-bluetooth", RFKILL_TYPE_BLUETOOTH,
1047 ASUS_WMI_DEVID_BLUETOOTH);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001048
1049 if (result && result != -ENODEV)
1050 goto exit;
1051
Corentin Charya7ce3f02011-02-26 10:20:33 +01001052 result = asus_new_rfkill(asus, &asus->wimax, "asus-wimax",
1053 RFKILL_TYPE_WIMAX, ASUS_WMI_DEVID_WIMAX);
Corentin Chary2e9e1592011-02-06 13:28:37 +01001054
1055 if (result && result != -ENODEV)
1056 goto exit;
1057
Corentin Charya7ce3f02011-02-26 10:20:33 +01001058 result = asus_new_rfkill(asus, &asus->wwan3g, "asus-wwan3g",
1059 RFKILL_TYPE_WWAN, ASUS_WMI_DEVID_WWAN3G);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001060
1061 if (result && result != -ENODEV)
1062 goto exit;
1063
Corentin Chary43be8bd2011-07-01 11:34:40 +02001064 result = asus_new_rfkill(asus, &asus->gps, "asus-gps",
1065 RFKILL_TYPE_GPS, ASUS_WMI_DEVID_GPS);
1066
1067 if (result && result != -ENODEV)
1068 goto exit;
1069
Corentin Charya912d322011-07-01 11:34:41 +02001070 result = asus_new_rfkill(asus, &asus->uwb, "asus-uwb",
1071 RFKILL_TYPE_UWB, ASUS_WMI_DEVID_UWB);
1072
1073 if (result && result != -ENODEV)
1074 goto exit;
1075
AceLan Kaoc87992d2012-03-20 09:53:08 +01001076 if (!asus->driver->quirks->hotplug_wireless)
Corentin Charyc14d4b82011-02-06 13:28:40 +01001077 goto exit;
1078
Corentin Charye12e6d92011-02-26 10:20:31 +01001079 result = asus_setup_pci_hotplug(asus);
Corentin Charyafa7c882011-02-06 13:28:28 +01001080 /*
1081 * If we get -EBUSY then something else is handling the PCI hotplug -
1082 * don't fail in this case
1083 */
1084 if (result == -EBUSY)
1085 result = 0;
1086
Corentin Charye12e6d92011-02-26 10:20:31 +01001087 asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P5");
1088 asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P6");
1089 asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P7");
Corentin Charyafa7c882011-02-06 13:28:28 +01001090 /*
1091 * Refresh pci hotplug in case the rfkill state was changed during
1092 * setup.
1093 */
Corentin Charye12e6d92011-02-26 10:20:31 +01001094 asus_rfkill_hotplug(asus);
Corentin Charyafa7c882011-02-06 13:28:28 +01001095
Corentin Charyba48fdb2010-11-29 08:14:07 +01001096exit:
1097 if (result && result != -ENODEV)
Corentin Charye12e6d92011-02-26 10:20:31 +01001098 asus_wmi_rfkill_exit(asus);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001099
1100 if (result == -ENODEV)
1101 result = 0;
1102
1103 return result;
1104}
1105
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +02001106/* Quirks *********************************************************************/
1107
Kai-Chuan Hsieh8023eff2016-09-01 23:55:55 +08001108static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
1109{
1110 struct pci_dev *xhci_pdev;
1111 u32 orig_ports_available;
1112 u32 ports_available = asus->driver->quirks->xusb2pr;
1113
1114 xhci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1115 PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI,
1116 NULL);
1117
1118 if (!xhci_pdev)
1119 return;
1120
1121 pci_read_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
1122 &orig_ports_available);
1123
1124 pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
1125 cpu_to_le32(ports_available));
1126
1127 pr_info("set USB_INTEL_XUSB2PR old: 0x%04x, new: 0x%04x\n",
1128 orig_ports_available, ports_available);
1129}
1130
Corentin Charyba48fdb2010-11-29 08:14:07 +01001131/*
Oleksij Rempele9b61512017-04-28 16:19:49 +02001132 * Some devices dont support or have borcken get_als method
1133 * but still support set method.
1134 */
1135static void asus_wmi_set_als(void)
1136{
1137 asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);
1138}
1139
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +02001140/* Hwmon device ***************************************************************/
1141
Kast Bernd53e755c2015-05-13 16:24:16 +02001142static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
1143 int *speed)
1144{
1145 struct fan_args args = {
1146 .agfn.len = sizeof(args),
1147 .agfn.mfun = ASUS_FAN_MFUN,
1148 .agfn.sfun = ASUS_FAN_SFUN_READ,
1149 .fan = fan,
1150 .speed = 0,
1151 };
1152 struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
1153 int status;
1154
1155 if (fan != 1)
1156 return -EINVAL;
1157
1158 status = asus_wmi_evaluate_method_agfn(input);
1159
1160 if (status || args.agfn.err)
1161 return -ENXIO;
1162
1163 if (speed)
1164 *speed = args.speed;
1165
1166 return 0;
1167}
1168
1169static int asus_hwmon_agfn_fan_speed_write(struct asus_wmi *asus, int fan,
1170 int *speed)
1171{
1172 struct fan_args args = {
1173 .agfn.len = sizeof(args),
1174 .agfn.mfun = ASUS_FAN_MFUN,
1175 .agfn.sfun = ASUS_FAN_SFUN_WRITE,
1176 .fan = fan,
1177 .speed = speed ? *speed : 0,
1178 };
1179 struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
1180 int status;
1181
1182 /* 1: for setting 1st fan's speed 0: setting auto mode */
1183 if (fan != 1 && fan != 0)
1184 return -EINVAL;
1185
1186 status = asus_wmi_evaluate_method_agfn(input);
1187
1188 if (status || args.agfn.err)
1189 return -ENXIO;
1190
1191 if (speed && fan == 1)
1192 asus->asus_hwmon_pwm = *speed;
1193
1194 return 0;
1195}
1196
1197/*
1198 * Check if we can read the speed of one fan. If true we assume we can also
1199 * control it.
1200 */
1201static int asus_hwmon_get_fan_number(struct asus_wmi *asus, int *num_fans)
1202{
1203 int status;
1204 int speed = 0;
1205
1206 *num_fans = 0;
1207
1208 status = asus_hwmon_agfn_fan_speed_read(asus, 1, &speed);
1209 if (!status)
1210 *num_fans = 1;
1211
1212 return 0;
1213}
1214
1215static int asus_hwmon_fan_set_auto(struct asus_wmi *asus)
1216{
1217 int status;
1218
1219 status = asus_hwmon_agfn_fan_speed_write(asus, 0, NULL);
1220 if (status)
1221 return -ENXIO;
1222
1223 asus->asus_hwmon_fan_manual_mode = false;
1224
1225 return 0;
1226}
1227
1228static int asus_hwmon_fan_rpm_show(struct device *dev, int fan)
1229{
1230 struct asus_wmi *asus = dev_get_drvdata(dev);
1231 int value;
1232 int ret;
1233
1234 /* no speed readable on manual mode */
1235 if (asus->asus_hwmon_fan_manual_mode)
1236 return -ENXIO;
1237
1238 ret = asus_hwmon_agfn_fan_speed_read(asus, fan+1, &value);
1239 if (ret) {
1240 pr_warn("reading fan speed failed: %d\n", ret);
1241 return -ENXIO;
1242 }
1243
1244 return value;
1245}
1246
1247static void asus_hwmon_pwm_show(struct asus_wmi *asus, int fan, int *value)
1248{
1249 int err;
1250
1251 if (asus->asus_hwmon_pwm >= 0) {
1252 *value = asus->asus_hwmon_pwm;
1253 return;
1254 }
1255
1256 err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, value);
1257 if (err < 0)
1258 return;
1259
1260 *value &= 0xFF;
1261
1262 if (*value == 1) /* Low Speed */
1263 *value = 85;
1264 else if (*value == 2)
1265 *value = 170;
1266 else if (*value == 3)
1267 *value = 255;
1268 else if (*value) {
1269 pr_err("Unknown fan speed %#x\n", *value);
1270 *value = -1;
1271 }
1272}
1273
1274static ssize_t pwm1_show(struct device *dev,
Corentin Chary49979d02011-07-01 11:34:26 +02001275 struct device_attribute *attr,
1276 char *buf)
Corentin Charye07babd2011-02-26 10:20:42 +01001277{
1278 struct asus_wmi *asus = dev_get_drvdata(dev);
Kast Bernd53e755c2015-05-13 16:24:16 +02001279 int value;
Corentin Charye07babd2011-02-26 10:20:42 +01001280
Kast Bernd53e755c2015-05-13 16:24:16 +02001281 asus_hwmon_pwm_show(asus, 0, &value);
Corentin Charye07babd2011-02-26 10:20:42 +01001282
1283 return sprintf(buf, "%d\n", value);
1284}
1285
Kast Bernd53e755c2015-05-13 16:24:16 +02001286static ssize_t pwm1_store(struct device *dev,
1287 struct device_attribute *attr,
1288 const char *buf, size_t count) {
1289 struct asus_wmi *asus = dev_get_drvdata(dev);
1290 int value;
1291 int state;
1292 int ret;
1293
1294 ret = kstrtouint(buf, 10, &value);
1295
1296 if (ret)
1297 return ret;
1298
1299 value = clamp(value, 0, 255);
1300
1301 state = asus_hwmon_agfn_fan_speed_write(asus, 1, &value);
1302 if (state)
1303 pr_warn("Setting fan speed failed: %d\n", state);
1304 else
1305 asus->asus_hwmon_fan_manual_mode = true;
1306
1307 return count;
1308}
1309
1310static ssize_t fan1_input_show(struct device *dev,
1311 struct device_attribute *attr,
1312 char *buf)
1313{
1314 int value = asus_hwmon_fan_rpm_show(dev, 0);
1315
1316 return sprintf(buf, "%d\n", value < 0 ? -1 : value*100);
1317
1318}
1319
1320static ssize_t pwm1_enable_show(struct device *dev,
1321 struct device_attribute *attr,
1322 char *buf)
1323{
1324 struct asus_wmi *asus = dev_get_drvdata(dev);
1325
1326 if (asus->asus_hwmon_fan_manual_mode)
1327 return sprintf(buf, "%d\n", ASUS_FAN_CTRL_MANUAL);
1328
1329 return sprintf(buf, "%d\n", ASUS_FAN_CTRL_AUTO);
1330}
1331
1332static ssize_t pwm1_enable_store(struct device *dev,
1333 struct device_attribute *attr,
1334 const char *buf, size_t count)
1335{
1336 struct asus_wmi *asus = dev_get_drvdata(dev);
1337 int status = 0;
1338 int state;
1339 int ret;
1340
1341 ret = kstrtouint(buf, 10, &state);
1342
1343 if (ret)
1344 return ret;
1345
1346 if (state == ASUS_FAN_CTRL_MANUAL)
1347 asus->asus_hwmon_fan_manual_mode = true;
1348 else
1349 status = asus_hwmon_fan_set_auto(asus);
1350
1351 if (status)
1352 return status;
1353
1354 return count;
1355}
1356
1357static ssize_t fan1_label_show(struct device *dev,
1358 struct device_attribute *attr,
1359 char *buf)
1360{
1361 return sprintf(buf, "%s\n", ASUS_FAN_DESC);
1362}
1363
Corentin Chary6118b8a2011-07-01 11:34:36 +02001364static ssize_t asus_hwmon_temp1(struct device *dev,
1365 struct device_attribute *attr,
1366 char *buf)
1367{
1368 struct asus_wmi *asus = dev_get_drvdata(dev);
1369 u32 value;
1370 int err;
1371
1372 err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, &value);
1373
1374 if (err < 0)
1375 return err;
1376
Rasmus Villemoese866a2e2015-10-01 23:45:31 +02001377 value = DECI_KELVIN_TO_CELSIUS((value & 0xFFFF)) * 1000;
Corentin Chary6118b8a2011-07-01 11:34:36 +02001378
1379 return sprintf(buf, "%d\n", value);
1380}
1381
Kast Bernd53e755c2015-05-13 16:24:16 +02001382/* Fan1 */
1383static DEVICE_ATTR_RW(pwm1);
1384static DEVICE_ATTR_RW(pwm1_enable);
1385static DEVICE_ATTR_RO(fan1_input);
1386static DEVICE_ATTR_RO(fan1_label);
1387
1388/* Temperature */
Guenter Roeck50a639f2013-11-23 11:03:17 -08001389static DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL);
Corentin Charye07babd2011-02-26 10:20:42 +01001390
1391static struct attribute *hwmon_attributes[] = {
Guenter Roeck50a639f2013-11-23 11:03:17 -08001392 &dev_attr_pwm1.attr,
Kast Bernd53e755c2015-05-13 16:24:16 +02001393 &dev_attr_pwm1_enable.attr,
1394 &dev_attr_fan1_input.attr,
1395 &dev_attr_fan1_label.attr,
1396
Guenter Roeck50a639f2013-11-23 11:03:17 -08001397 &dev_attr_temp1_input.attr,
Corentin Charye07babd2011-02-26 10:20:42 +01001398 NULL
1399};
1400
Al Viro587a1f12011-07-23 23:11:19 -04001401static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
Corentin Charye02431d2011-07-01 11:34:37 +02001402 struct attribute *attr, int idx)
Corentin Charye07babd2011-02-26 10:20:42 +01001403{
1404 struct device *dev = container_of(kobj, struct device, kobj);
1405 struct platform_device *pdev = to_platform_device(dev->parent);
1406 struct asus_wmi *asus = platform_get_drvdata(pdev);
Corentin Charye07babd2011-02-26 10:20:42 +01001407 int dev_id = -1;
Kast Bernd53e755c2015-05-13 16:24:16 +02001408 int fan_attr = -1;
Corentin Charye07babd2011-02-26 10:20:42 +01001409 u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
Kast Bernd53e755c2015-05-13 16:24:16 +02001410 bool ok = true;
Corentin Charye07babd2011-02-26 10:20:42 +01001411
Guenter Roeck50a639f2013-11-23 11:03:17 -08001412 if (attr == &dev_attr_pwm1.attr)
Corentin Charye07babd2011-02-26 10:20:42 +01001413 dev_id = ASUS_WMI_DEVID_FAN_CTRL;
Guenter Roeck50a639f2013-11-23 11:03:17 -08001414 else if (attr == &dev_attr_temp1_input.attr)
Corentin Charye02431d2011-07-01 11:34:37 +02001415 dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
Corentin Charye07babd2011-02-26 10:20:42 +01001416
Kast Bernd53e755c2015-05-13 16:24:16 +02001417
1418 if (attr == &dev_attr_fan1_input.attr
1419 || attr == &dev_attr_fan1_label.attr
1420 || attr == &dev_attr_pwm1.attr
1421 || attr == &dev_attr_pwm1_enable.attr) {
1422 fan_attr = 1;
1423 }
1424
Corentin Charye07babd2011-02-26 10:20:42 +01001425 if (dev_id != -1) {
1426 int err = asus_wmi_get_devstate(asus, dev_id, &value);
1427
Kast Bernd53e755c2015-05-13 16:24:16 +02001428 if (err < 0 && fan_attr == -1)
Al Viroe772aed2011-07-23 20:59:40 -04001429 return 0; /* can't return negative here */
Corentin Charye07babd2011-02-26 10:20:42 +01001430 }
1431
1432 if (dev_id == ASUS_WMI_DEVID_FAN_CTRL) {
1433 /*
1434 * We need to find a better way, probably using sfun,
1435 * bits or spec ...
1436 * Currently we disable it if:
1437 * - ASUS_WMI_UNSUPPORTED_METHOD is returned
1438 * - reverved bits are non-zero
1439 * - sfun and presence bit are not set
1440 */
Corentin Chary49979d02011-07-01 11:34:26 +02001441 if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000
Corentin Charye07babd2011-02-26 10:20:42 +01001442 || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT)))
1443 ok = false;
Kast Bernd53e755c2015-05-13 16:24:16 +02001444 else
1445 ok = fan_attr <= asus->asus_hwmon_num_fans;
Corentin Charye02431d2011-07-01 11:34:37 +02001446 } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) {
Yurii Pavlovskyi4fd19822019-05-14 21:05:46 +02001447 /*
1448 * If the temperature value in deci-Kelvin is near the absolute
1449 * zero temperature, something is clearly wrong
1450 */
1451 if (value == 0 || value == 1)
Corentin Charye02431d2011-07-01 11:34:37 +02001452 ok = false;
Kast Bernd53e755c2015-05-13 16:24:16 +02001453 } else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) {
1454 ok = true;
1455 } else {
1456 ok = false;
Corentin Charye07babd2011-02-26 10:20:42 +01001457 }
1458
1459 return ok ? attr->mode : 0;
1460}
1461
Arvind Yadave90d9ba2017-07-11 16:18:19 +05301462static const struct attribute_group hwmon_attribute_group = {
Corentin Charye07babd2011-02-26 10:20:42 +01001463 .is_visible = asus_hwmon_sysfs_is_visible,
1464 .attrs = hwmon_attributes
1465};
Guenter Roeck50a639f2013-11-23 11:03:17 -08001466__ATTRIBUTE_GROUPS(hwmon_attribute);
Corentin Charye07babd2011-02-26 10:20:42 +01001467
1468static int asus_wmi_hwmon_init(struct asus_wmi *asus)
1469{
Yurii Pavlovskyicd10ee02019-05-14 20:50:30 +02001470 struct device *dev = &asus->platform_device->dev;
Corentin Charye07babd2011-02-26 10:20:42 +01001471 struct device *hwmon;
Corentin Charye07babd2011-02-26 10:20:42 +01001472
Yurii Pavlovskyicd10ee02019-05-14 20:50:30 +02001473 hwmon = devm_hwmon_device_register_with_groups(dev, "asus", asus,
1474 hwmon_attribute_groups);
1475
Corentin Charye07babd2011-02-26 10:20:42 +01001476 if (IS_ERR(hwmon)) {
1477 pr_err("Could not register asus hwmon device\n");
1478 return PTR_ERR(hwmon);
1479 }
Guenter Roeck50a639f2013-11-23 11:03:17 -08001480 return 0;
Corentin Charye07babd2011-02-26 10:20:42 +01001481}
1482
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +02001483static int asus_wmi_fan_init(struct asus_wmi *asus)
1484{
1485 int status;
1486
1487 asus->asus_hwmon_pwm = -1;
1488 asus->asus_hwmon_num_fans = -1;
1489 asus->asus_hwmon_fan_manual_mode = false;
1490
1491 status = asus_hwmon_get_fan_number(asus, &asus->asus_hwmon_num_fans);
1492 if (status) {
1493 asus->asus_hwmon_num_fans = 0;
1494 pr_warn("Could not determine number of fans: %d\n", status);
1495 return -ENXIO;
1496 }
1497
1498 pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
1499 return 0;
1500}
1501
Yurii Pavlovskyib096f622019-05-14 21:07:05 +02001502/* Fan mode *******************************************************************/
1503
1504static int fan_mode_check_present(struct asus_wmi *asus)
1505{
1506 u32 result;
1507 int err;
1508
1509 asus->fan_mode_available = false;
1510
1511 err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, &result);
1512 if (err) {
1513 if (err == -ENODEV)
1514 return 0;
1515 else
1516 return err;
1517 }
1518
1519 if ((result & ASUS_WMI_DSTS_PRESENCE_BIT) &&
1520 (result & ASUS_FAN_MODES_MASK)) {
1521 asus->fan_mode_available = true;
1522 asus->fan_mode_mask = result & ASUS_FAN_MODES_MASK;
1523 }
1524
1525 return 0;
1526}
1527
1528static int fan_mode_write(struct asus_wmi *asus)
1529{
1530 int err;
1531 u8 value;
1532 u32 retval;
1533
1534 value = asus->fan_mode;
1535
1536 pr_info("Set fan mode: %u\n", value);
1537 err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, &retval);
1538
1539 if (err) {
1540 pr_warn("Failed to set fan mode: %d\n", err);
1541 return err;
1542 }
1543
1544 if (retval != 1) {
1545 pr_warn("Failed to set fan mode (retval): 0x%x\n", retval);
1546 return -EIO;
1547 }
1548
1549 return 0;
1550}
1551
1552static int fan_mode_switch_next(struct asus_wmi *asus)
1553{
1554 if (asus->fan_mode == ASUS_FAN_MODE_NORMAL) {
1555 if (asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK)
1556 asus->fan_mode = ASUS_FAN_MODE_OVERBOOST;
1557 else if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK)
1558 asus->fan_mode = ASUS_FAN_MODE_SILENT;
1559 } else if (asus->fan_mode == ASUS_FAN_MODE_OVERBOOST) {
1560 if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK)
1561 asus->fan_mode = ASUS_FAN_MODE_SILENT;
1562 else
1563 asus->fan_mode = ASUS_FAN_MODE_NORMAL;
1564 } else {
1565 asus->fan_mode = ASUS_FAN_MODE_NORMAL;
1566 }
1567
1568 return fan_mode_write(asus);
1569}
1570
1571static ssize_t fan_mode_show(struct device *dev,
1572 struct device_attribute *attr, char *buf)
1573{
1574 struct asus_wmi *asus = dev_get_drvdata(dev);
1575
1576 return scnprintf(buf, PAGE_SIZE, "%d\n", asus->fan_mode);
1577}
1578
1579static ssize_t fan_mode_store(struct device *dev, struct device_attribute *attr,
1580 const char *buf, size_t count)
1581{
1582 int result;
1583 u8 new_mode;
1584
1585 struct asus_wmi *asus = dev_get_drvdata(dev);
1586
1587 result = kstrtou8(buf, 10, &new_mode);
1588 if (result < 0) {
1589 pr_warn("Trying to store invalid value\n");
1590 return result;
1591 }
1592
1593 if (new_mode == ASUS_FAN_MODE_OVERBOOST) {
1594 if (!(asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK))
1595 return -EINVAL;
1596 } else if (new_mode == ASUS_FAN_MODE_SILENT) {
1597 if (!(asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK))
1598 return -EINVAL;
1599 } else if (new_mode != ASUS_FAN_MODE_NORMAL) {
1600 return -EINVAL;
1601 }
1602
1603 asus->fan_mode = new_mode;
1604 fan_mode_write(asus);
1605
1606 return result;
1607}
1608
1609// Fan mode: 0 - normal, 1 - overboost, 2 - silent
1610static DEVICE_ATTR_RW(fan_mode);
1611
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +02001612/* Backlight ******************************************************************/
1613
Corentin Chary1d070f82011-02-26 10:20:36 +01001614static int read_backlight_power(struct asus_wmi *asus)
Corentin Charyb7187262011-02-06 13:28:39 +01001615{
AceLan Kao6e0044b2012-03-20 09:53:09 +01001616 int ret;
1617 if (asus->driver->quirks->store_backlight_power)
1618 ret = !asus->driver->panel_power;
1619 else
1620 ret = asus_wmi_get_devstate_simple(asus,
1621 ASUS_WMI_DEVID_BACKLIGHT);
Corentin Charyb7187262011-02-06 13:28:39 +01001622
1623 if (ret < 0)
1624 return ret;
1625
1626 return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
1627}
1628
Corentin Chary8fbea012011-02-26 10:20:37 +01001629static int read_brightness_max(struct asus_wmi *asus)
1630{
1631 u32 retval;
1632 int err;
1633
1634 err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval);
1635
1636 if (err < 0)
1637 return err;
1638
1639 retval = retval & ASUS_WMI_DSTS_MAX_BRIGTH_MASK;
1640 retval >>= 8;
1641
1642 if (!retval)
1643 return -ENODEV;
1644
1645 return retval;
1646}
1647
Yong Wang3d7b1652010-04-11 09:27:54 +08001648static int read_brightness(struct backlight_device *bd)
1649{
Corentin Chary1d070f82011-02-26 10:20:36 +01001650 struct asus_wmi *asus = bl_get_data(bd);
Dan Carpenter0986f252011-03-15 10:06:23 +03001651 u32 retval;
1652 int err;
Yong Wang3d7b1652010-04-11 09:27:54 +08001653
Corentin Chary1d070f82011-02-26 10:20:36 +01001654 err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval);
Yong Wang3d7b1652010-04-11 09:27:54 +08001655
Corentin Charyd33da3b2011-02-26 10:20:35 +01001656 if (err < 0)
1657 return err;
1658
1659 return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
Yong Wang3d7b1652010-04-11 09:27:54 +08001660}
1661
AceLan Kaoc87992d2012-03-20 09:53:08 +01001662static u32 get_scalar_command(struct backlight_device *bd)
1663{
1664 struct asus_wmi *asus = bl_get_data(bd);
1665 u32 ctrl_param = 0;
1666
1667 if ((asus->driver->brightness < bd->props.brightness) ||
1668 bd->props.brightness == bd->props.max_brightness)
1669 ctrl_param = 0x00008001;
1670 else if ((asus->driver->brightness > bd->props.brightness) ||
1671 bd->props.brightness == 0)
1672 ctrl_param = 0x00008000;
1673
1674 asus->driver->brightness = bd->props.brightness;
1675
1676 return ctrl_param;
1677}
1678
Yong Wang3d7b1652010-04-11 09:27:54 +08001679static int update_bl_status(struct backlight_device *bd)
1680{
Corentin Chary1d070f82011-02-26 10:20:36 +01001681 struct asus_wmi *asus = bl_get_data(bd);
Corentin Charydfed65d2010-11-29 08:14:12 +01001682 u32 ctrl_param;
AceLan Kao6e0044b2012-03-20 09:53:09 +01001683 int power, err = 0;
Corentin Charyb7187262011-02-06 13:28:39 +01001684
Corentin Chary1d070f82011-02-26 10:20:36 +01001685 power = read_backlight_power(asus);
Corentin Charyb7187262011-02-06 13:28:39 +01001686 if (power != -ENODEV && bd->props.power != power) {
1687 ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK);
Corentin Charyd33da3b2011-02-26 10:20:35 +01001688 err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT,
1689 ctrl_param, NULL);
AceLan Kao6e0044b2012-03-20 09:53:09 +01001690 if (asus->driver->quirks->store_backlight_power)
1691 asus->driver->panel_power = bd->props.power;
AceLan Kao6e0044b2012-03-20 09:53:09 +01001692
Corentin Charyade28ab2012-03-20 09:53:14 +01001693 /* When using scalar brightness, updating the brightness
1694 * will mess with the backlight power */
1695 if (asus->driver->quirks->scalar_panel_brightness)
1696 return err;
Corentin Charyb7187262011-02-06 13:28:39 +01001697 }
Corentin Charyade28ab2012-03-20 09:53:14 +01001698
1699 if (asus->driver->quirks->scalar_panel_brightness)
1700 ctrl_param = get_scalar_command(bd);
1701 else
1702 ctrl_param = bd->props.brightness;
1703
1704 err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS,
1705 ctrl_param, NULL);
1706
Corentin Chary8fbea012011-02-26 10:20:37 +01001707 return err;
Yong Wang3d7b1652010-04-11 09:27:54 +08001708}
1709
Corentin Charye12e6d92011-02-26 10:20:31 +01001710static const struct backlight_ops asus_wmi_bl_ops = {
Yong Wang3d7b1652010-04-11 09:27:54 +08001711 .get_brightness = read_brightness,
1712 .update_status = update_bl_status,
1713};
1714
Corentin Charye12e6d92011-02-26 10:20:31 +01001715static int asus_wmi_backlight_notify(struct asus_wmi *asus, int code)
Yong Wang3d7b1652010-04-11 09:27:54 +08001716{
Corentin Charye12e6d92011-02-26 10:20:31 +01001717 struct backlight_device *bd = asus->backlight_device;
Yong Wang3d7b1652010-04-11 09:27:54 +08001718 int old = bd->props.brightness;
Daniel Mackb7670ed2010-05-19 12:37:01 +02001719 int new = old;
Yong Wang3d7b1652010-04-11 09:27:54 +08001720
1721 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
1722 new = code - NOTIFY_BRNUP_MIN + 1;
1723 else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
1724 new = code - NOTIFY_BRNDOWN_MIN;
1725
1726 bd->props.brightness = new;
1727 backlight_update_status(bd);
1728 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
1729
1730 return old;
1731}
1732
Corentin Charye12e6d92011-02-26 10:20:31 +01001733static int asus_wmi_backlight_init(struct asus_wmi *asus)
Yong Wang3d7b1652010-04-11 09:27:54 +08001734{
1735 struct backlight_device *bd;
1736 struct backlight_properties props;
Corentin Charyb7187262011-02-06 13:28:39 +01001737 int max;
1738 int power;
1739
Corentin Chary8fbea012011-02-26 10:20:37 +01001740 max = read_brightness_max(asus);
Hans de Goede86ac27352014-07-08 10:47:22 +02001741 if (max < 0)
Corentin Chary8fbea012011-02-26 10:20:37 +01001742 return max;
1743
1744 power = read_backlight_power(asus);
1745
Corentin Charyb7187262011-02-06 13:28:39 +01001746 if (power == -ENODEV)
1747 power = FB_BLANK_UNBLANK;
Corentin Chary8fbea012011-02-26 10:20:37 +01001748 else if (power < 0)
1749 return power;
Yong Wang3d7b1652010-04-11 09:27:54 +08001750
1751 memset(&props, 0, sizeof(struct backlight_properties));
Axel Lin60cfa092011-06-29 11:43:30 +08001752 props.type = BACKLIGHT_PLATFORM;
Corentin Charyb7187262011-02-06 13:28:39 +01001753 props.max_brightness = max;
Corentin Charye12e6d92011-02-26 10:20:31 +01001754 bd = backlight_device_register(asus->driver->name,
1755 &asus->platform_device->dev, asus,
1756 &asus_wmi_bl_ops, &props);
Yong Wang3d7b1652010-04-11 09:27:54 +08001757 if (IS_ERR(bd)) {
1758 pr_err("Could not register backlight device\n");
1759 return PTR_ERR(bd);
1760 }
1761
Corentin Charye12e6d92011-02-26 10:20:31 +01001762 asus->backlight_device = bd;
Yong Wang3d7b1652010-04-11 09:27:54 +08001763
AceLan Kao6e0044b2012-03-20 09:53:09 +01001764 if (asus->driver->quirks->store_backlight_power)
1765 asus->driver->panel_power = power;
1766
Yong Wang3d7b1652010-04-11 09:27:54 +08001767 bd->props.brightness = read_brightness(bd);
Corentin Charyb7187262011-02-06 13:28:39 +01001768 bd->props.power = power;
Yong Wang3d7b1652010-04-11 09:27:54 +08001769 backlight_update_status(bd);
1770
AceLan Kaoc87992d2012-03-20 09:53:08 +01001771 asus->driver->brightness = bd->props.brightness;
1772
Yong Wang3d7b1652010-04-11 09:27:54 +08001773 return 0;
1774}
1775
Corentin Charye12e6d92011-02-26 10:20:31 +01001776static void asus_wmi_backlight_exit(struct asus_wmi *asus)
Yong Wang3d7b1652010-04-11 09:27:54 +08001777{
Markus Elfring00981812014-11-24 20:30:29 +01001778 backlight_device_unregister(asus->backlight_device);
Yong Wang3d7b1652010-04-11 09:27:54 +08001779
Corentin Charye12e6d92011-02-26 10:20:31 +01001780 asus->backlight_device = NULL;
Yong Wang3d7b1652010-04-11 09:27:54 +08001781}
1782
AceLan Kaoa2a96f02012-10-03 11:26:31 +02001783static int is_display_toggle(int code)
1784{
1785 /* display toggle keys */
1786 if ((code >= 0x61 && code <= 0x67) ||
1787 (code >= 0x8c && code <= 0x93) ||
1788 (code >= 0xa0 && code <= 0xa7) ||
1789 (code >= 0xd0 && code <= 0xd5))
1790 return 1;
1791
1792 return 0;
1793}
1794
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +02001795/* Fn-lock ********************************************************************/
1796
Chris Chiu487579b2019-04-18 14:46:48 +08001797static bool asus_wmi_has_fnlock_key(struct asus_wmi *asus)
1798{
1799 u32 result;
1800
1801 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FNLOCK, &result);
1802
1803 return (result & ASUS_WMI_DSTS_PRESENCE_BIT) &&
1804 !(result & ASUS_WMI_FNLOCK_BIOS_DISABLED);
1805}
1806
1807static void asus_wmi_fnlock_update(struct asus_wmi *asus)
1808{
1809 int mode = asus->fnlock_locked;
1810
1811 asus_wmi_set_devstate(ASUS_WMI_DEVID_FNLOCK, mode, NULL);
1812}
1813
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +02001814/* WMI events *****************************************************************/
1815
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001816static int asus_wmi_get_event_code(u32 value)
Yong Wang3d7b1652010-04-11 09:27:54 +08001817{
Yong Wang3d7b1652010-04-11 09:27:54 +08001818 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
1819 union acpi_object *obj;
1820 acpi_status status;
1821 int code;
Yong Wang3d7b1652010-04-11 09:27:54 +08001822
1823 status = wmi_get_event_data(value, &response);
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001824 if (ACPI_FAILURE(status)) {
1825 pr_warn("Failed to get WMI notify code: %s\n",
1826 acpi_format_exception(status));
1827 return -EIO;
Yong Wang3d7b1652010-04-11 09:27:54 +08001828 }
1829
1830 obj = (union acpi_object *)response.pointer;
1831
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001832 if (obj && obj->type == ACPI_TYPE_INTEGER)
1833 code = (int)(obj->integer.value & WMI_EVENT_MASK);
1834 else
1835 code = -EIO;
Yong Wang3d7b1652010-04-11 09:27:54 +08001836
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001837 kfree(obj);
1838 return code;
1839}
1840
1841static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
1842{
1843 int orig_code;
1844 unsigned int key_value = 1;
1845 bool autorelease = 1;
1846
Corentin Chary57ab7da2011-02-26 10:20:32 +01001847 orig_code = code;
Yong Wang3d7b1652010-04-11 09:27:54 +08001848
Seth Forsheec4453f62011-07-01 11:34:27 +02001849 if (asus->driver->key_filter) {
1850 asus->driver->key_filter(asus->driver, &code, &key_value,
1851 &autorelease);
1852 if (code == ASUS_WMI_KEY_IGNORE)
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001853 return;
Seth Forsheec4453f62011-07-01 11:34:27 +02001854 }
1855
Corentin Chary57ab7da2011-02-26 10:20:32 +01001856 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
Corentin Chary3ba03022012-11-29 09:12:38 +01001857 code = ASUS_WMI_BRN_UP;
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001858 else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
Corentin Chary3ba03022012-11-29 09:12:38 +01001859 code = ASUS_WMI_BRN_DOWN;
Yong Wang3d7b1652010-04-11 09:27:54 +08001860
Corentin Chary3ba03022012-11-29 09:12:38 +01001861 if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) {
Hans de Goede62c4aa12015-06-16 16:27:58 +02001862 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
Corentin Chary57ab7da2011-02-26 10:20:32 +01001863 asus_wmi_backlight_notify(asus, orig_code);
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001864 return;
AceLan Kaoa2a96f02012-10-03 11:26:31 +02001865 }
AceLan Kaoa2a96f02012-10-03 11:26:31 +02001866 }
1867
Chris Chiudbb3d782018-06-20 22:46:44 +08001868 if (code == NOTIFY_KBD_BRTUP) {
Jian-Hong Pan29f6eb52018-10-22 18:00:04 +08001869 kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001870 return;
Chris Chiudbb3d782018-06-20 22:46:44 +08001871 }
1872 if (code == NOTIFY_KBD_BRTDWN) {
Jian-Hong Pan29f6eb52018-10-22 18:00:04 +08001873 kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1);
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001874 return;
Chris Chiudbb3d782018-06-20 22:46:44 +08001875 }
Chris Chiued99d292018-06-20 22:46:45 +08001876 if (code == NOTIFY_KBD_BRTTOGGLE) {
1877 if (asus->kbd_led_wk == asus->kbd_led.max_brightness)
Jian-Hong Pan29f6eb52018-10-22 18:00:04 +08001878 kbd_led_set_by_kbd(asus, 0);
Chris Chiued99d292018-06-20 22:46:45 +08001879 else
Jian-Hong Pan29f6eb52018-10-22 18:00:04 +08001880 kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001881 return;
Chris Chiued99d292018-06-20 22:46:45 +08001882 }
Chris Chiudbb3d782018-06-20 22:46:44 +08001883
Chris Chiu487579b2019-04-18 14:46:48 +08001884 if (code == NOTIFY_FNLOCK_TOGGLE) {
1885 asus->fnlock_locked = !asus->fnlock_locked;
1886 asus_wmi_fnlock_update(asus);
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001887 return;
Chris Chiu487579b2019-04-18 14:46:48 +08001888 }
1889
Yurii Pavlovskyib096f622019-05-14 21:07:05 +02001890 if (asus->fan_mode_available && code == NOTIFY_KBD_FBM) {
1891 fan_mode_switch_next(asus);
1892 return;
1893 }
1894
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001895 if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle)
1896 return;
AceLan Kaoa2a96f02012-10-03 11:26:31 +02001897
1898 if (!sparse_keymap_report_event(asus->inputdev, code,
1899 key_value, autorelease))
Corentin Chary57ab7da2011-02-26 10:20:32 +01001900 pr_info("Unknown key %x pressed\n", code);
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001901}
Yong Wang3d7b1652010-04-11 09:27:54 +08001902
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001903static void asus_wmi_notify(u32 value, void *context)
1904{
1905 struct asus_wmi *asus = context;
Yurii Pavlovskyi1a373d12019-05-14 21:02:09 +02001906 int code;
1907 int i;
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001908
Yurii Pavlovskyi1a373d12019-05-14 21:02:09 +02001909 for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
1910 code = asus_wmi_get_event_code(value);
1911
1912 if (code < 0) {
1913 pr_warn("Failed to get notify code: %d\n", code);
1914 return;
1915 }
1916
1917 if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
1918 return;
1919
1920 asus_wmi_handle_event_code(code, asus);
1921
1922 /*
1923 * Double check that queue is present:
1924 * ATK (with queue) uses 0xff, ASUSWMI (without) 0xd2.
1925 */
1926 if (!asus->wmi_event_queue || value != WMI_EVENT_VALUE_ATK)
1927 return;
Yurii Pavlovskyi8abd752b2019-05-14 21:01:24 +02001928 }
1929
Yurii Pavlovskyi1a373d12019-05-14 21:02:09 +02001930 pr_warn("Failed to process event queue, last code: 0x%x\n", code);
1931}
1932
1933static int asus_wmi_notify_queue_flush(struct asus_wmi *asus)
1934{
1935 int code;
1936 int i;
1937
1938 for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
1939 code = asus_wmi_get_event_code(WMI_EVENT_VALUE_ATK);
1940
1941 if (code < 0) {
1942 pr_warn("Failed to get event during flush: %d\n", code);
1943 return code;
1944 }
1945
1946 if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
1947 return 0;
1948 }
1949
1950 pr_warn("Failed to flush event queue\n");
1951 return -EIO;
Yong Wang3d7b1652010-04-11 09:27:54 +08001952}
1953
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +02001954/* Sysfs **********************************************************************/
1955
Corentin Chary9e1565b2011-02-06 13:28:36 +01001956static int parse_arg(const char *buf, unsigned long count, int *val)
1957{
1958 if (!count)
1959 return 0;
1960 if (sscanf(buf, "%i", val) != 1)
1961 return -EINVAL;
1962 return count;
1963}
1964
Corentin Chary1d070f82011-02-26 10:20:36 +01001965static ssize_t store_sys_wmi(struct asus_wmi *asus, int devid,
1966 const char *buf, size_t count)
Corentin Chary9e1565b2011-02-06 13:28:36 +01001967{
Corentin Chary9e1565b2011-02-06 13:28:36 +01001968 u32 retval;
Corentin Charyd33da3b2011-02-26 10:20:35 +01001969 int rv, err, value;
Corentin Chary9e1565b2011-02-06 13:28:36 +01001970
Corentin Chary1d070f82011-02-26 10:20:36 +01001971 value = asus_wmi_get_devstate_simple(asus, devid);
Dan Carpenterb8298342015-11-11 01:18:16 +03001972 if (value < 0)
Corentin Chary9e1565b2011-02-06 13:28:36 +01001973 return value;
1974
1975 rv = parse_arg(buf, count, &value);
Corentin Charyd33da3b2011-02-26 10:20:35 +01001976 err = asus_wmi_set_devstate(devid, value, &retval);
Corentin Chary9e1565b2011-02-06 13:28:36 +01001977
Corentin Charyd33da3b2011-02-26 10:20:35 +01001978 if (err < 0)
1979 return err;
1980
Corentin Chary9e1565b2011-02-06 13:28:36 +01001981 return rv;
1982}
1983
Corentin Chary1d070f82011-02-26 10:20:36 +01001984static ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf)
Corentin Chary9e1565b2011-02-06 13:28:36 +01001985{
Corentin Chary1d070f82011-02-26 10:20:36 +01001986 int value = asus_wmi_get_devstate_simple(asus, devid);
Corentin Chary9e1565b2011-02-06 13:28:36 +01001987
1988 if (value < 0)
1989 return value;
1990
1991 return sprintf(buf, "%d\n", value);
1992}
1993
Corentin Charye12e6d92011-02-26 10:20:31 +01001994#define ASUS_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm) \
Corentin Chary9e1565b2011-02-06 13:28:36 +01001995 static ssize_t show_##_name(struct device *dev, \
1996 struct device_attribute *attr, \
1997 char *buf) \
1998 { \
Corentin Chary1d070f82011-02-26 10:20:36 +01001999 struct asus_wmi *asus = dev_get_drvdata(dev); \
2000 \
2001 return show_sys_wmi(asus, _cm, buf); \
Corentin Chary9e1565b2011-02-06 13:28:36 +01002002 } \
2003 static ssize_t store_##_name(struct device *dev, \
2004 struct device_attribute *attr, \
2005 const char *buf, size_t count) \
2006 { \
Corentin Chary1d070f82011-02-26 10:20:36 +01002007 struct asus_wmi *asus = dev_get_drvdata(dev); \
2008 \
2009 return store_sys_wmi(asus, _cm, buf, count); \
Corentin Chary9e1565b2011-02-06 13:28:36 +01002010 } \
2011 static struct device_attribute dev_attr_##_name = { \
2012 .attr = { \
2013 .name = __stringify(_name), \
2014 .mode = _mode }, \
2015 .show = show_##_name, \
2016 .store = store_##_name, \
2017 }
2018
Corentin Charye12e6d92011-02-26 10:20:31 +01002019ASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD);
2020ASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA);
2021ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER);
AceLan Kaoc0b91b62012-06-13 09:32:07 +02002022ASUS_WMI_CREATE_DEVICE_ATTR(lid_resume, 0644, ASUS_WMI_DEVID_LID_RESUME);
Oleksij Rempelaca234f2016-04-01 13:35:21 +02002023ASUS_WMI_CREATE_DEVICE_ATTR(als_enable, 0644, ASUS_WMI_DEVID_ALS_ENABLE);
Corentin Chary9e1565b2011-02-06 13:28:36 +01002024
Jérémy Lefaurea8fe3422017-04-21 22:19:45 -04002025static ssize_t cpufv_store(struct device *dev, struct device_attribute *attr,
Dmitry Torokhov67fa38e2010-11-03 11:14:01 -07002026 const char *buf, size_t count)
Chris Bagwell7f80d732010-10-11 18:47:18 -05002027{
Corentin Chary3df5fda2011-07-01 11:34:38 +02002028 int value, rv;
Chris Bagwell7f80d732010-10-11 18:47:18 -05002029
2030 if (!count || sscanf(buf, "%i", &value) != 1)
2031 return -EINVAL;
2032 if (value < 0 || value > 2)
2033 return -EINVAL;
2034
Corentin Chary3df5fda2011-07-01 11:34:38 +02002035 rv = asus_wmi_evaluate_method(ASUS_WMI_METHODID_CFVS, value, 0, NULL);
2036 if (rv < 0)
2037 return rv;
2038
2039 return count;
Chris Bagwell7f80d732010-10-11 18:47:18 -05002040}
2041
Jérémy Lefaurea8fe3422017-04-21 22:19:45 -04002042static DEVICE_ATTR_WO(cpufv);
Chris Bagwell7f80d732010-10-11 18:47:18 -05002043
Corentin Chary4e37b422010-11-29 08:14:08 +01002044static struct attribute *platform_attributes[] = {
2045 &dev_attr_cpufv.attr,
Corentin Chary9e1565b2011-02-06 13:28:36 +01002046 &dev_attr_camera.attr,
2047 &dev_attr_cardr.attr,
Corentin Chary4615bb62011-02-06 13:28:42 +01002048 &dev_attr_touchpad.attr,
AceLan Kaoc0b91b62012-06-13 09:32:07 +02002049 &dev_attr_lid_resume.attr,
Oleksij Rempelaca234f2016-04-01 13:35:21 +02002050 &dev_attr_als_enable.attr,
Yurii Pavlovskyib096f622019-05-14 21:07:05 +02002051 &dev_attr_fan_mode.attr,
Corentin Chary4e37b422010-11-29 08:14:08 +01002052 NULL
2053};
2054
Al Viro587a1f12011-07-23 23:11:19 -04002055static umode_t asus_sysfs_is_visible(struct kobject *kobj,
Corentin Charye12e6d92011-02-26 10:20:31 +01002056 struct attribute *attr, int idx)
Corentin Chary9e1565b2011-02-06 13:28:36 +01002057{
Corentin Chary1d070f82011-02-26 10:20:36 +01002058 struct device *dev = container_of(kobj, struct device, kobj);
Wolfram Sangd605ca22018-04-19 16:06:10 +02002059 struct asus_wmi *asus = dev_get_drvdata(dev);
Corentin Chary1d070f82011-02-26 10:20:36 +01002060 bool ok = true;
Corentin Chary9e1565b2011-02-06 13:28:36 +01002061 int devid = -1;
2062
2063 if (attr == &dev_attr_camera.attr)
Corentin Charye12e6d92011-02-26 10:20:31 +01002064 devid = ASUS_WMI_DEVID_CAMERA;
Corentin Chary9e1565b2011-02-06 13:28:36 +01002065 else if (attr == &dev_attr_cardr.attr)
Corentin Charye12e6d92011-02-26 10:20:31 +01002066 devid = ASUS_WMI_DEVID_CARDREADER;
Corentin Chary4615bb62011-02-06 13:28:42 +01002067 else if (attr == &dev_attr_touchpad.attr)
Corentin Charye12e6d92011-02-26 10:20:31 +01002068 devid = ASUS_WMI_DEVID_TOUCHPAD;
AceLan Kaoc0b91b62012-06-13 09:32:07 +02002069 else if (attr == &dev_attr_lid_resume.attr)
2070 devid = ASUS_WMI_DEVID_LID_RESUME;
Oleksij Rempelaca234f2016-04-01 13:35:21 +02002071 else if (attr == &dev_attr_als_enable.attr)
2072 devid = ASUS_WMI_DEVID_ALS_ENABLE;
Yurii Pavlovskyib096f622019-05-14 21:07:05 +02002073 else if (attr == &dev_attr_fan_mode.attr)
2074 ok = asus->fan_mode_available;
Corentin Chary9e1565b2011-02-06 13:28:36 +01002075
2076 if (devid != -1)
Corentin Chary1d070f82011-02-26 10:20:36 +01002077 ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
Corentin Chary9e1565b2011-02-06 13:28:36 +01002078
Corentin Chary1d070f82011-02-26 10:20:36 +01002079 return ok ? attr->mode : 0;
Corentin Chary9e1565b2011-02-06 13:28:36 +01002080}
2081
Arvind Yadave90d9ba2017-07-11 16:18:19 +05302082static const struct attribute_group platform_attribute_group = {
Corentin Charye12e6d92011-02-26 10:20:31 +01002083 .is_visible = asus_sysfs_is_visible,
2084 .attrs = platform_attributes
Corentin Chary4e37b422010-11-29 08:14:08 +01002085};
2086
Corentin Charye12e6d92011-02-26 10:20:31 +01002087static void asus_wmi_sysfs_exit(struct platform_device *device)
Chris Bagwell7f80d732010-10-11 18:47:18 -05002088{
Corentin Chary4e37b422010-11-29 08:14:08 +01002089 sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -05002090}
2091
Corentin Charye12e6d92011-02-26 10:20:31 +01002092static int asus_wmi_sysfs_init(struct platform_device *device)
Chris Bagwell7f80d732010-10-11 18:47:18 -05002093{
Corentin Chary4e37b422010-11-29 08:14:08 +01002094 return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -05002095}
2096
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +02002097/* Platform device ************************************************************/
2098
Joe Perches39ddf3b2011-03-29 15:21:32 -07002099static int asus_wmi_platform_init(struct asus_wmi *asus)
Corentin Chary27c136c2010-11-29 08:14:05 +01002100{
Yurii Pavlovskyie0668f22019-05-14 21:00:31 +02002101 struct device *dev = &asus->platform_device->dev;
2102 char *wmi_uid;
Corentin Chary46dbca82011-02-26 10:20:38 +01002103 int rv;
2104
2105 /* INIT enable hotkeys on some models */
2106 if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_INIT, 0, 0, &rv))
vic0ed60652013-05-22 21:32:10 +03002107 pr_info("Initialization: %#x\n", rv);
Corentin Chary46dbca82011-02-26 10:20:38 +01002108
2109 /* We don't know yet what to do with this version... */
2110 if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) {
vic0ed60652013-05-22 21:32:10 +03002111 pr_info("BIOS WMI version: %d.%d\n", rv >> 16, rv & 0xFF);
Corentin Chary46dbca82011-02-26 10:20:38 +01002112 asus->spec = rv;
2113 }
2114
2115 /*
2116 * The SFUN method probably allows the original driver to get the list
2117 * of features supported by a given model. For now, 0x0100 or 0x0800
2118 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
2119 * The significance of others is yet to be found.
2120 */
2121 if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SFUN, 0, 0, &rv)) {
vic0ed60652013-05-22 21:32:10 +03002122 pr_info("SFUN value: %#x\n", rv);
Corentin Chary46dbca82011-02-26 10:20:38 +01002123 asus->sfun = rv;
2124 }
2125
Corentin Chary1d070f82011-02-26 10:20:36 +01002126 /*
2127 * Eee PC and Notebooks seems to have different method_id for DSTS,
2128 * but it may also be related to the BIOS's SPEC.
2129 * Note, on most Eeepc, there is no way to check if a method exist
2130 * or note, while on notebooks, they returns 0xFFFFFFFE on failure,
2131 * but once again, SPEC may probably be used for that kind of things.
Yurii Pavlovskyie0668f22019-05-14 21:00:31 +02002132 *
2133 * Additionally at least TUF Gaming series laptops return nothing for
2134 * unknown methods, so the detection in this way is not possible.
2135 *
2136 * There is strong indication that only ACPI WMI devices that have _UID
2137 * equal to "ASUSWMI" use DCTS whereas those with "ATK" use DSTS.
Corentin Chary1d070f82011-02-26 10:20:36 +01002138 */
Yurii Pavlovskyie0668f22019-05-14 21:00:31 +02002139 wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID);
2140 if (!wmi_uid)
2141 return -ENODEV;
2142
2143 if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) {
2144 dev_info(dev, "Detected ASUSWMI, use DCTS\n");
2145 asus->dsts_id = ASUS_WMI_METHODID_DCTS;
2146 } else {
2147 dev_info(dev, "Detected %s, not ASUSWMI, use DSTS\n", wmi_uid);
Corentin Chary1d070f82011-02-26 10:20:36 +01002148 asus->dsts_id = ASUS_WMI_METHODID_DSTS;
Yurii Pavlovskyie0668f22019-05-14 21:00:31 +02002149 }
Corentin Chary1d070f82011-02-26 10:20:36 +01002150
Yurii Pavlovskyi1a373d12019-05-14 21:02:09 +02002151 /*
2152 * Some devices can have multiple event codes stored in a queue before
2153 * the module load if it was unloaded intermittently after calling
2154 * the INIT method (enables event handling). The WMI notify handler is
2155 * expected to retrieve all event codes until a retrieved code equals
2156 * queue end marker (One or Ones). Old codes are flushed from the queue
2157 * upon module load. Not enabling this when it should be has minimal
2158 * visible impact so fall back if anything goes wrong.
2159 */
2160 wmi_uid = wmi_get_acpi_device_uid(asus->driver->event_guid);
2161 if (wmi_uid && !strcmp(wmi_uid, ASUS_ACPI_UID_ATK)) {
2162 dev_info(dev, "Detected ATK, enable event queue\n");
2163
2164 if (!asus_wmi_notify_queue_flush(asus))
2165 asus->wmi_event_queue = true;
2166 }
2167
Corentin Charyfddbfed2011-07-01 11:34:39 +02002168 /* CWAP allow to define the behavior of the Fn+F2 key,
2169 * this method doesn't seems to be present on Eee PCs */
Corentin Chary6a2bccc2012-03-20 09:53:10 +01002170 if (asus->driver->quirks->wapf >= 0)
Corentin Charyfddbfed2011-07-01 11:34:39 +02002171 asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,
Corentin Chary6a2bccc2012-03-20 09:53:10 +01002172 asus->driver->quirks->wapf, NULL);
Corentin Charyfddbfed2011-07-01 11:34:39 +02002173
Yurii Pavlovskyib096f622019-05-14 21:07:05 +02002174 return 0;
Corentin Chary27c136c2010-11-29 08:14:05 +01002175}
2176
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +02002177/* debugfs ********************************************************************/
2178
Corentin Charye12e6d92011-02-26 10:20:31 +01002179struct asus_wmi_debugfs_node {
2180 struct asus_wmi *asus;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002181 char *name;
Corentin Charye12e6d92011-02-26 10:20:31 +01002182 int (*show) (struct seq_file *m, void *data);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002183};
2184
2185static int show_dsts(struct seq_file *m, void *data)
2186{
Corentin Charye12e6d92011-02-26 10:20:31 +01002187 struct asus_wmi *asus = m->private;
Corentin Charyd33da3b2011-02-26 10:20:35 +01002188 int err;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002189 u32 retval = -1;
2190
Corentin Chary1d070f82011-02-26 10:20:36 +01002191 err = asus_wmi_get_devstate(asus, asus->debug.dev_id, &retval);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002192
Corentin Charyd33da3b2011-02-26 10:20:35 +01002193 if (err < 0)
2194 return err;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002195
Corentin Charyef343492011-02-26 10:20:39 +01002196 seq_printf(m, "DSTS(%#x) = %#x\n", asus->debug.dev_id, retval);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002197
2198 return 0;
2199}
2200
2201static int show_devs(struct seq_file *m, void *data)
2202{
Corentin Charye12e6d92011-02-26 10:20:31 +01002203 struct asus_wmi *asus = m->private;
Corentin Charyd33da3b2011-02-26 10:20:35 +01002204 int err;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002205 u32 retval = -1;
2206
Corentin Charyd33da3b2011-02-26 10:20:35 +01002207 err = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param,
2208 &retval);
2209
2210 if (err < 0)
2211 return err;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002212
Corentin Charyef343492011-02-26 10:20:39 +01002213 seq_printf(m, "DEVS(%#x, %#x) = %#x\n", asus->debug.dev_id,
Corentin Charye12e6d92011-02-26 10:20:31 +01002214 asus->debug.ctrl_param, retval);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002215
2216 return 0;
2217}
2218
Corentin Charyef343492011-02-26 10:20:39 +01002219static int show_call(struct seq_file *m, void *data)
2220{
2221 struct asus_wmi *asus = m->private;
2222 struct bios_args args = {
2223 .arg0 = asus->debug.dev_id,
2224 .arg1 = asus->debug.ctrl_param,
2225 };
2226 struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
2227 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
2228 union acpi_object *obj;
2229 acpi_status status;
2230
2231 status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID,
Pali Rohár0fe57262017-08-12 09:44:16 +02002232 0, asus->debug.method_id,
Corentin Charyef343492011-02-26 10:20:39 +01002233 &input, &output);
2234
2235 if (ACPI_FAILURE(status))
2236 return -EIO;
2237
2238 obj = (union acpi_object *)output.pointer;
2239 if (obj && obj->type == ACPI_TYPE_INTEGER)
2240 seq_printf(m, "%#x(%#x, %#x) = %#x\n", asus->debug.method_id,
2241 asus->debug.dev_id, asus->debug.ctrl_param,
2242 (u32) obj->integer.value);
2243 else
2244 seq_printf(m, "%#x(%#x, %#x) = t:%d\n", asus->debug.method_id,
2245 asus->debug.dev_id, asus->debug.ctrl_param,
Dan Carpentera1d60862011-03-15 10:07:37 +03002246 obj ? obj->type : -1);
Corentin Charyef343492011-02-26 10:20:39 +01002247
2248 kfree(obj);
2249
2250 return 0;
2251}
2252
Corentin Charye12e6d92011-02-26 10:20:31 +01002253static struct asus_wmi_debugfs_node asus_wmi_debug_files[] = {
2254 {NULL, "devs", show_devs},
2255 {NULL, "dsts", show_dsts},
Corentin Charyef343492011-02-26 10:20:39 +01002256 {NULL, "call", show_call},
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002257};
2258
Corentin Charye12e6d92011-02-26 10:20:31 +01002259static int asus_wmi_debugfs_open(struct inode *inode, struct file *file)
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002260{
Corentin Charye12e6d92011-02-26 10:20:31 +01002261 struct asus_wmi_debugfs_node *node = inode->i_private;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002262
Corentin Charye12e6d92011-02-26 10:20:31 +01002263 return single_open(file, node->show, node->asus);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002264}
2265
Corentin Charye12e6d92011-02-26 10:20:31 +01002266static const struct file_operations asus_wmi_debugfs_io_ops = {
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002267 .owner = THIS_MODULE,
Corentin Charye12e6d92011-02-26 10:20:31 +01002268 .open = asus_wmi_debugfs_open,
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002269 .read = seq_read,
2270 .llseek = seq_lseek,
2271 .release = single_release,
2272};
2273
Corentin Charye12e6d92011-02-26 10:20:31 +01002274static void asus_wmi_debugfs_exit(struct asus_wmi *asus)
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002275{
Corentin Charye12e6d92011-02-26 10:20:31 +01002276 debugfs_remove_recursive(asus->debug.root);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002277}
2278
Greg Kroah-Hartmand2785d32019-06-12 14:12:52 +02002279static void asus_wmi_debugfs_init(struct asus_wmi *asus)
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002280{
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002281 int i;
2282
Corentin Charye12e6d92011-02-26 10:20:31 +01002283 asus->debug.root = debugfs_create_dir(asus->driver->name, NULL);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002284
Greg Kroah-Hartmand2785d32019-06-12 14:12:52 +02002285 debugfs_create_x32("method_id", S_IRUGO | S_IWUSR, asus->debug.root,
2286 &asus->debug.method_id);
Corentin Charyef343492011-02-26 10:20:39 +01002287
Greg Kroah-Hartmand2785d32019-06-12 14:12:52 +02002288 debugfs_create_x32("dev_id", S_IRUGO | S_IWUSR, asus->debug.root,
2289 &asus->debug.dev_id);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002290
Greg Kroah-Hartmand2785d32019-06-12 14:12:52 +02002291 debugfs_create_x32("ctrl_param", S_IRUGO | S_IWUSR, asus->debug.root,
2292 &asus->debug.ctrl_param);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002293
Corentin Charye12e6d92011-02-26 10:20:31 +01002294 for (i = 0; i < ARRAY_SIZE(asus_wmi_debug_files); i++) {
2295 struct asus_wmi_debugfs_node *node = &asus_wmi_debug_files[i];
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002296
Corentin Charye12e6d92011-02-26 10:20:31 +01002297 node->asus = asus;
Greg Kroah-Hartmand2785d32019-06-12 14:12:52 +02002298 debugfs_create_file(node->name, S_IFREG | S_IRUGO,
2299 asus->debug.root, node,
2300 &asus_wmi_debugfs_io_ops);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002301 }
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002302}
2303
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +02002304/* Init / exit ****************************************************************/
Kast Bernd53e755c2015-05-13 16:24:16 +02002305
Corentin Charye12e6d92011-02-26 10:20:31 +01002306static int asus_wmi_add(struct platform_device *pdev)
Corentin Charyafa7c882011-02-06 13:28:28 +01002307{
Corentin Charye12e6d92011-02-26 10:20:31 +01002308 struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
2309 struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv);
2310 struct asus_wmi *asus;
Hans de Goeded8c66f62014-07-08 10:47:21 +02002311 const char *chassis_type;
Yong Wangee027e42010-03-21 10:26:34 +08002312 acpi_status status;
Corentin Chary27c136c2010-11-29 08:14:05 +01002313 int err;
AceLan Kaoa50bd122012-07-26 17:13:31 +08002314 u32 result;
Yong Wangee027e42010-03-21 10:26:34 +08002315
Corentin Charye12e6d92011-02-26 10:20:31 +01002316 asus = kzalloc(sizeof(struct asus_wmi), GFP_KERNEL);
2317 if (!asus)
Corentin Charya04ce292011-02-06 13:28:33 +01002318 return -ENOMEM;
2319
Corentin Charye12e6d92011-02-26 10:20:31 +01002320 asus->driver = wdrv;
2321 asus->platform_device = pdev;
2322 wdrv->platform_device = pdev;
2323 platform_set_drvdata(asus->platform_device, asus);
Corentin Chary27c136c2010-11-29 08:14:05 +01002324
AceLan Kaoc87992d2012-03-20 09:53:08 +01002325 if (wdrv->detect_quirks)
2326 wdrv->detect_quirks(asus->driver);
Corentin Charyafa7c882011-02-06 13:28:28 +01002327
Corentin Charye12e6d92011-02-26 10:20:31 +01002328 err = asus_wmi_platform_init(asus);
Corentin Chary27c136c2010-11-29 08:14:05 +01002329 if (err)
2330 goto fail_platform;
Yong Wang45f2c692010-04-11 09:27:19 +08002331
Yurii Pavlovskyib096f622019-05-14 21:07:05 +02002332 err = fan_mode_check_present(asus);
2333 if (err)
2334 goto fail_fan_mode;
2335
2336 err = asus_wmi_sysfs_init(asus->platform_device);
2337 if (err)
2338 goto fail_sysfs;
2339
Corentin Charye12e6d92011-02-26 10:20:31 +01002340 err = asus_wmi_input_init(asus);
Yong Wang45f2c692010-04-11 09:27:19 +08002341 if (err)
Corentin Chary27c136c2010-11-29 08:14:05 +01002342 goto fail_input;
Yong Wang3d7b1652010-04-11 09:27:54 +08002343
Kast Bernd53e755c2015-05-13 16:24:16 +02002344 err = asus_wmi_fan_init(asus); /* probably no problems on error */
2345 asus_hwmon_fan_set_auto(asus);
2346
Corentin Charye07babd2011-02-26 10:20:42 +01002347 err = asus_wmi_hwmon_init(asus);
2348 if (err)
2349 goto fail_hwmon;
2350
Corentin Charye12e6d92011-02-26 10:20:31 +01002351 err = asus_wmi_led_init(asus);
Corentin Chary084fca62010-11-29 08:14:06 +01002352 if (err)
2353 goto fail_leds;
2354
João Paulo Rechi Vita71050ae2017-02-20 14:50:22 -05002355 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);
2356 if (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
2357 asus->driver->wlan_ctrl_by_user = 1;
2358
João Paulo Rechi Vitad1c4e9b2017-02-20 14:50:23 -05002359 if (!(asus->driver->wlan_ctrl_by_user && ashs_present())) {
João Paulo Rechi Vitaa977e592016-06-13 16:57:31 -04002360 err = asus_wmi_rfkill_init(asus);
2361 if (err)
2362 goto fail_rfkill;
2363 }
Corentin Charyba48fdb2010-11-29 08:14:07 +01002364
Oleksij Rempele9b61512017-04-28 16:19:49 +02002365 if (asus->driver->quirks->wmi_force_als_set)
2366 asus_wmi_set_als();
2367
Hans de Goeded8c66f62014-07-08 10:47:21 +02002368 /* Some Asus desktop boards export an acpi-video backlight interface,
2369 stop this from showing up */
2370 chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
2371 if (chassis_type && !strcmp(chassis_type, "3"))
Hans de Goede62c4aa12015-06-16 16:27:58 +02002372 acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
2373
AceLan Kao272c77d2012-06-13 09:32:06 +02002374 if (asus->driver->quirks->wmi_backlight_power)
Hans de Goede62c4aa12015-06-16 16:27:58 +02002375 acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
2376
zino lin999d4372016-08-28 16:12:06 +08002377 if (asus->driver->quirks->wmi_backlight_native)
2378 acpi_video_set_dmi_backlight_type(acpi_backlight_native);
2379
Kai-Chuan Hsieh8023eff2016-09-01 23:55:55 +08002380 if (asus->driver->quirks->xusb2pr)
2381 asus_wmi_set_xusb2pr(asus);
2382
Hans de Goede62c4aa12015-06-16 16:27:58 +02002383 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
Corentin Charye12e6d92011-02-26 10:20:31 +01002384 err = asus_wmi_backlight_init(asus);
Corentin Charyb7187262011-02-06 13:28:39 +01002385 if (err && err != -ENODEV)
Corentin Chary27c136c2010-11-29 08:14:05 +01002386 goto fail_backlight;
Hans de Goede1dd93f82019-06-12 09:02:02 +02002387 } else if (asus->driver->quirks->wmi_backlight_set_devstate)
João Paulo Rechi Vita78f3ac72018-10-31 17:21:26 -07002388 err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, 2, NULL);
Yong Wang45f2c692010-04-11 09:27:19 +08002389
Chris Chiu487579b2019-04-18 14:46:48 +08002390 if (asus_wmi_has_fnlock_key(asus)) {
2391 asus->fnlock_locked = true;
2392 asus_wmi_fnlock_update(asus);
2393 }
2394
Corentin Charye12e6d92011-02-26 10:20:31 +01002395 status = wmi_install_notify_handler(asus->driver->event_guid,
2396 asus_wmi_notify, asus);
Yong Wang45f2c692010-04-11 09:27:19 +08002397 if (ACPI_FAILURE(status)) {
Corentin Charye12e6d92011-02-26 10:20:31 +01002398 pr_err("Unable to register notify handler - %d\n", status);
Yong Wang45f2c692010-04-11 09:27:19 +08002399 err = -ENODEV;
Corentin Chary27c136c2010-11-29 08:14:05 +01002400 goto fail_wmi_handler;
Yong Wang45f2c692010-04-11 09:27:19 +08002401 }
2402
Greg Kroah-Hartmand2785d32019-06-12 14:12:52 +02002403 asus_wmi_debugfs_init(asus);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002404
Corentin Charya04ce292011-02-06 13:28:33 +01002405 return 0;
Yong Wang45f2c692010-04-11 09:27:19 +08002406
Corentin Chary27c136c2010-11-29 08:14:05 +01002407fail_wmi_handler:
Corentin Charye12e6d92011-02-26 10:20:31 +01002408 asus_wmi_backlight_exit(asus);
Corentin Chary27c136c2010-11-29 08:14:05 +01002409fail_backlight:
Corentin Charye12e6d92011-02-26 10:20:31 +01002410 asus_wmi_rfkill_exit(asus);
Corentin Charyba48fdb2010-11-29 08:14:07 +01002411fail_rfkill:
Corentin Charye12e6d92011-02-26 10:20:31 +01002412 asus_wmi_led_exit(asus);
Corentin Chary084fca62010-11-29 08:14:06 +01002413fail_leds:
Corentin Charye07babd2011-02-26 10:20:42 +01002414fail_hwmon:
Corentin Charye12e6d92011-02-26 10:20:31 +01002415 asus_wmi_input_exit(asus);
Corentin Chary27c136c2010-11-29 08:14:05 +01002416fail_input:
Yurii Pavlovskyib096f622019-05-14 21:07:05 +02002417 asus_wmi_sysfs_exit(asus->platform_device);
2418fail_sysfs:
2419fail_fan_mode:
Corentin Chary27c136c2010-11-29 08:14:05 +01002420fail_platform:
Corentin Charye12e6d92011-02-26 10:20:31 +01002421 kfree(asus);
Corentin Charya04ce292011-02-06 13:28:33 +01002422 return err;
Yong Wang45f2c692010-04-11 09:27:19 +08002423}
2424
Corentin Charye12e6d92011-02-26 10:20:31 +01002425static int asus_wmi_remove(struct platform_device *device)
Yong Wang45f2c692010-04-11 09:27:19 +08002426{
Corentin Charye12e6d92011-02-26 10:20:31 +01002427 struct asus_wmi *asus;
Yong Wang45f2c692010-04-11 09:27:19 +08002428
Corentin Charye12e6d92011-02-26 10:20:31 +01002429 asus = platform_get_drvdata(device);
2430 wmi_remove_notify_handler(asus->driver->event_guid);
2431 asus_wmi_backlight_exit(asus);
2432 asus_wmi_input_exit(asus);
2433 asus_wmi_led_exit(asus);
2434 asus_wmi_rfkill_exit(asus);
2435 asus_wmi_debugfs_exit(asus);
Yurii Pavlovskyib096f622019-05-14 21:07:05 +02002436 asus_wmi_sysfs_exit(asus->platform_device);
Kast Bernd53e755c2015-05-13 16:24:16 +02002437 asus_hwmon_fan_set_auto(asus);
Yong Wang45f2c692010-04-11 09:27:19 +08002438
Corentin Charye12e6d92011-02-26 10:20:31 +01002439 kfree(asus);
Yong Wang45f2c692010-04-11 09:27:19 +08002440 return 0;
2441}
2442
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +02002443/* Platform driver - hibernate/resume callbacks *******************************/
2444
Corentin Charye12e6d92011-02-26 10:20:31 +01002445static int asus_hotk_thaw(struct device *device)
Corentin Chary0773d7f2011-02-06 13:28:32 +01002446{
Corentin Charye12e6d92011-02-26 10:20:31 +01002447 struct asus_wmi *asus = dev_get_drvdata(device);
Corentin Chary0773d7f2011-02-06 13:28:32 +01002448
Corentin Charya7ce3f02011-02-26 10:20:33 +01002449 if (asus->wlan.rfkill) {
Corentin Chary0773d7f2011-02-06 13:28:32 +01002450 bool wlan;
2451
2452 /*
2453 * Work around bios bug - acpi _PTS turns off the wireless led
2454 * during suspend. Normally it restores it on resume, but
2455 * we should kick it ourselves in case hibernation is aborted.
2456 */
Corentin Chary1d070f82011-02-26 10:20:36 +01002457 wlan = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
Corentin Charye12e6d92011-02-26 10:20:31 +01002458 asus_wmi_set_devstate(ASUS_WMI_DEVID_WLAN, wlan, NULL);
Corentin Chary0773d7f2011-02-06 13:28:32 +01002459 }
2460
2461 return 0;
2462}
2463
Oleksij Rempel30734042015-09-14 11:16:30 +02002464static int asus_hotk_resume(struct device *device)
2465{
2466 struct asus_wmi *asus = dev_get_drvdata(device);
2467
2468 if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
Jian-Hong Pan9fe44fc2018-09-27 16:50:09 +08002469 kbd_led_update(asus);
Oleksij Rempel30734042015-09-14 11:16:30 +02002470
Chris Chiu487579b2019-04-18 14:46:48 +08002471 if (asus_wmi_has_fnlock_key(asus))
2472 asus_wmi_fnlock_update(asus);
Oleksij Rempel30734042015-09-14 11:16:30 +02002473 return 0;
2474}
2475
Corentin Charye12e6d92011-02-26 10:20:31 +01002476static int asus_hotk_restore(struct device *device)
Corentin Chary0773d7f2011-02-06 13:28:32 +01002477{
Corentin Charye12e6d92011-02-26 10:20:31 +01002478 struct asus_wmi *asus = dev_get_drvdata(device);
Corentin Chary0773d7f2011-02-06 13:28:32 +01002479 int bl;
2480
2481 /* Refresh both wlan rfkill state and pci hotplug */
Corentin Charya7ce3f02011-02-26 10:20:33 +01002482 if (asus->wlan.rfkill)
Corentin Charye12e6d92011-02-26 10:20:31 +01002483 asus_rfkill_hotplug(asus);
Corentin Chary0773d7f2011-02-06 13:28:32 +01002484
Corentin Charya7ce3f02011-02-26 10:20:33 +01002485 if (asus->bluetooth.rfkill) {
Corentin Chary1d070f82011-02-26 10:20:36 +01002486 bl = !asus_wmi_get_devstate_simple(asus,
2487 ASUS_WMI_DEVID_BLUETOOTH);
Corentin Charya7ce3f02011-02-26 10:20:33 +01002488 rfkill_set_sw_state(asus->bluetooth.rfkill, bl);
Corentin Chary2e9e1592011-02-06 13:28:37 +01002489 }
Corentin Charya7ce3f02011-02-26 10:20:33 +01002490 if (asus->wimax.rfkill) {
Corentin Chary1d070f82011-02-26 10:20:36 +01002491 bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WIMAX);
Corentin Charya7ce3f02011-02-26 10:20:33 +01002492 rfkill_set_sw_state(asus->wimax.rfkill, bl);
Corentin Chary2e9e1592011-02-06 13:28:37 +01002493 }
Corentin Charya7ce3f02011-02-26 10:20:33 +01002494 if (asus->wwan3g.rfkill) {
Corentin Chary1d070f82011-02-26 10:20:36 +01002495 bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WWAN3G);
Corentin Charya7ce3f02011-02-26 10:20:33 +01002496 rfkill_set_sw_state(asus->wwan3g.rfkill, bl);
Corentin Chary0773d7f2011-02-06 13:28:32 +01002497 }
Corentin Chary43be8bd2011-07-01 11:34:40 +02002498 if (asus->gps.rfkill) {
2499 bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPS);
2500 rfkill_set_sw_state(asus->gps.rfkill, bl);
2501 }
Corentin Charya912d322011-07-01 11:34:41 +02002502 if (asus->uwb.rfkill) {
2503 bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_UWB);
2504 rfkill_set_sw_state(asus->uwb.rfkill, bl);
2505 }
Oleksij Rempel30734042015-09-14 11:16:30 +02002506 if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
Jian-Hong Pan9fe44fc2018-09-27 16:50:09 +08002507 kbd_led_update(asus);
Corentin Chary0773d7f2011-02-06 13:28:32 +01002508
Chris Chiu487579b2019-04-18 14:46:48 +08002509 if (asus_wmi_has_fnlock_key(asus))
2510 asus_wmi_fnlock_update(asus);
Corentin Chary0773d7f2011-02-06 13:28:32 +01002511 return 0;
2512}
2513
Corentin Charye12e6d92011-02-26 10:20:31 +01002514static const struct dev_pm_ops asus_pm_ops = {
2515 .thaw = asus_hotk_thaw,
2516 .restore = asus_hotk_restore,
Oleksij Rempel30734042015-09-14 11:16:30 +02002517 .resume = asus_hotk_resume,
Corentin Chary0773d7f2011-02-06 13:28:32 +01002518};
2519
Yurii Pavlovskyi54a31212019-05-14 21:04:48 +02002520/* Registration ***************************************************************/
2521
Corentin Charye12e6d92011-02-26 10:20:31 +01002522static int asus_wmi_probe(struct platform_device *pdev)
Corentin Charyd358cb52010-11-29 08:14:14 +01002523{
Corentin Charye12e6d92011-02-26 10:20:31 +01002524 struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
2525 struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv);
2526 int ret;
Corentin Charyd358cb52010-11-29 08:14:14 +01002527
Corentin Charye12e6d92011-02-26 10:20:31 +01002528 if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) {
Hans de Goedec9946112019-01-21 14:24:36 +01002529 pr_warn("ASUS Management GUID not found\n");
Yong Wangee027e42010-03-21 10:26:34 +08002530 return -ENODEV;
2531 }
2532
Corentin Charye12e6d92011-02-26 10:20:31 +01002533 if (wdrv->event_guid && !wmi_has_guid(wdrv->event_guid)) {
Hans de Goedec9946112019-01-21 14:24:36 +01002534 pr_warn("ASUS Event GUID not found\n");
Corentin Charyd358cb52010-11-29 08:14:14 +01002535 return -ENODEV;
2536 }
2537
Corentin Charye12e6d92011-02-26 10:20:31 +01002538 if (wdrv->probe) {
2539 ret = wdrv->probe(pdev);
2540 if (ret)
2541 return ret;
2542 }
2543
2544 return asus_wmi_add(pdev);
Corentin Charya04ce292011-02-06 13:28:33 +01002545}
Yong Wangee027e42010-03-21 10:26:34 +08002546
Corentin Charye12e6d92011-02-26 10:20:31 +01002547static bool used;
Yong Wangee027e42010-03-21 10:26:34 +08002548
Corentin Chary8fe8c252011-07-01 11:34:32 +02002549int __init_or_module asus_wmi_register_driver(struct asus_wmi_driver *driver)
Corentin Charya04ce292011-02-06 13:28:33 +01002550{
Corentin Charye12e6d92011-02-26 10:20:31 +01002551 struct platform_driver *platform_driver;
2552 struct platform_device *platform_device;
2553
2554 if (used)
2555 return -EBUSY;
2556
2557 platform_driver = &driver->platform_driver;
2558 platform_driver->remove = asus_wmi_remove;
2559 platform_driver->driver.owner = driver->owner;
2560 platform_driver->driver.name = driver->name;
2561 platform_driver->driver.pm = &asus_pm_ops;
2562
2563 platform_device = platform_create_bundle(platform_driver,
2564 asus_wmi_probe,
Corentin Charya04ce292011-02-06 13:28:33 +01002565 NULL, 0, NULL, 0);
2566 if (IS_ERR(platform_device))
2567 return PTR_ERR(platform_device);
Corentin Charye12e6d92011-02-26 10:20:31 +01002568
2569 used = true;
2570 return 0;
2571}
2572EXPORT_SYMBOL_GPL(asus_wmi_register_driver);
2573
2574void asus_wmi_unregister_driver(struct asus_wmi_driver *driver)
2575{
2576 platform_device_unregister(driver->platform_device);
2577 platform_driver_unregister(&driver->platform_driver);
2578 used = false;
2579}
2580EXPORT_SYMBOL_GPL(asus_wmi_unregister_driver);
2581
2582static int __init asus_wmi_init(void)
2583{
vic0ed60652013-05-22 21:32:10 +03002584 pr_info("ASUS WMI generic driver loaded\n");
Yong Wangee027e42010-03-21 10:26:34 +08002585 return 0;
2586}
2587
Corentin Charye12e6d92011-02-26 10:20:31 +01002588static void __exit asus_wmi_exit(void)
Yong Wangee027e42010-03-21 10:26:34 +08002589{
vic0ed60652013-05-22 21:32:10 +03002590 pr_info("ASUS WMI generic driver unloaded\n");
Yong Wangee027e42010-03-21 10:26:34 +08002591}
2592
Corentin Charye12e6d92011-02-26 10:20:31 +01002593module_init(asus_wmi_init);
2594module_exit(asus_wmi_exit);