xref: /OK3568_Linux_fs/kernel/drivers/platform/x86/hp-wmi.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * HP WMI hotkeys
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2008 Red Hat <mjg@redhat.com>
6*4882a593Smuzhiyun  * Copyright (C) 2010, 2011 Anssi Hannula <anssi.hannula@iki.fi>
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * Portions based on wistron_btns.c:
9*4882a593Smuzhiyun  * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
10*4882a593Smuzhiyun  * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
11*4882a593Smuzhiyun  * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
12*4882a593Smuzhiyun  */
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include <linux/kernel.h>
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <linux/init.h>
19*4882a593Smuzhiyun #include <linux/slab.h>
20*4882a593Smuzhiyun #include <linux/types.h>
21*4882a593Smuzhiyun #include <linux/input.h>
22*4882a593Smuzhiyun #include <linux/input/sparse-keymap.h>
23*4882a593Smuzhiyun #include <linux/platform_device.h>
24*4882a593Smuzhiyun #include <linux/acpi.h>
25*4882a593Smuzhiyun #include <linux/rfkill.h>
26*4882a593Smuzhiyun #include <linux/string.h>
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
29*4882a593Smuzhiyun MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
30*4882a593Smuzhiyun MODULE_LICENSE("GPL");
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
33*4882a593Smuzhiyun MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun static int enable_tablet_mode_sw = -1;
36*4882a593Smuzhiyun module_param(enable_tablet_mode_sw, int, 0444);
37*4882a593Smuzhiyun MODULE_PARM_DESC(enable_tablet_mode_sw, "Enable SW_TABLET_MODE reporting (-1=auto, 0=no, 1=yes)");
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun #define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
40*4882a593Smuzhiyun #define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun enum hp_wmi_radio {
43*4882a593Smuzhiyun 	HPWMI_WIFI	= 0x0,
44*4882a593Smuzhiyun 	HPWMI_BLUETOOTH	= 0x1,
45*4882a593Smuzhiyun 	HPWMI_WWAN	= 0x2,
46*4882a593Smuzhiyun 	HPWMI_GPS	= 0x3,
47*4882a593Smuzhiyun };
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun enum hp_wmi_event_ids {
50*4882a593Smuzhiyun 	HPWMI_DOCK_EVENT		= 0x01,
51*4882a593Smuzhiyun 	HPWMI_PARK_HDD			= 0x02,
52*4882a593Smuzhiyun 	HPWMI_SMART_ADAPTER		= 0x03,
53*4882a593Smuzhiyun 	HPWMI_BEZEL_BUTTON		= 0x04,
54*4882a593Smuzhiyun 	HPWMI_WIRELESS			= 0x05,
55*4882a593Smuzhiyun 	HPWMI_CPU_BATTERY_THROTTLE	= 0x06,
56*4882a593Smuzhiyun 	HPWMI_LOCK_SWITCH		= 0x07,
57*4882a593Smuzhiyun 	HPWMI_LID_SWITCH		= 0x08,
58*4882a593Smuzhiyun 	HPWMI_SCREEN_ROTATION		= 0x09,
59*4882a593Smuzhiyun 	HPWMI_COOLSENSE_SYSTEM_MOBILE	= 0x0A,
60*4882a593Smuzhiyun 	HPWMI_COOLSENSE_SYSTEM_HOT	= 0x0B,
61*4882a593Smuzhiyun 	HPWMI_PROXIMITY_SENSOR		= 0x0C,
62*4882a593Smuzhiyun 	HPWMI_BACKLIT_KB_BRIGHTNESS	= 0x0D,
63*4882a593Smuzhiyun 	HPWMI_PEAKSHIFT_PERIOD		= 0x0F,
64*4882a593Smuzhiyun 	HPWMI_BATTERY_CHARGE_PERIOD	= 0x10,
65*4882a593Smuzhiyun 	HPWMI_SANITIZATION_MODE		= 0x17,
66*4882a593Smuzhiyun 	HPWMI_SMART_EXPERIENCE_APP	= 0x21,
67*4882a593Smuzhiyun };
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun struct bios_args {
70*4882a593Smuzhiyun 	u32 signature;
71*4882a593Smuzhiyun 	u32 command;
72*4882a593Smuzhiyun 	u32 commandtype;
73*4882a593Smuzhiyun 	u32 datasize;
74*4882a593Smuzhiyun 	u8 data[128];
75*4882a593Smuzhiyun };
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun enum hp_wmi_commandtype {
78*4882a593Smuzhiyun 	HPWMI_DISPLAY_QUERY		= 0x01,
79*4882a593Smuzhiyun 	HPWMI_HDDTEMP_QUERY		= 0x02,
80*4882a593Smuzhiyun 	HPWMI_ALS_QUERY			= 0x03,
81*4882a593Smuzhiyun 	HPWMI_HARDWARE_QUERY		= 0x04,
82*4882a593Smuzhiyun 	HPWMI_WIRELESS_QUERY		= 0x05,
83*4882a593Smuzhiyun 	HPWMI_BATTERY_QUERY		= 0x07,
84*4882a593Smuzhiyun 	HPWMI_BIOS_QUERY		= 0x09,
85*4882a593Smuzhiyun 	HPWMI_FEATURE_QUERY		= 0x0b,
86*4882a593Smuzhiyun 	HPWMI_HOTKEY_QUERY		= 0x0c,
87*4882a593Smuzhiyun 	HPWMI_FEATURE2_QUERY		= 0x0d,
88*4882a593Smuzhiyun 	HPWMI_WIRELESS2_QUERY		= 0x1b,
89*4882a593Smuzhiyun 	HPWMI_POSTCODEERROR_QUERY	= 0x2a,
90*4882a593Smuzhiyun 	HPWMI_THERMAL_POLICY_QUERY	= 0x4c,
91*4882a593Smuzhiyun };
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun enum hp_wmi_command {
94*4882a593Smuzhiyun 	HPWMI_READ	= 0x01,
95*4882a593Smuzhiyun 	HPWMI_WRITE	= 0x02,
96*4882a593Smuzhiyun 	HPWMI_ODM	= 0x03,
97*4882a593Smuzhiyun };
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun enum hp_wmi_hardware_mask {
100*4882a593Smuzhiyun 	HPWMI_DOCK_MASK		= 0x01,
101*4882a593Smuzhiyun 	HPWMI_TABLET_MASK	= 0x04,
102*4882a593Smuzhiyun };
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun struct bios_return {
105*4882a593Smuzhiyun 	u32 sigpass;
106*4882a593Smuzhiyun 	u32 return_code;
107*4882a593Smuzhiyun };
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun enum hp_return_value {
110*4882a593Smuzhiyun 	HPWMI_RET_WRONG_SIGNATURE	= 0x02,
111*4882a593Smuzhiyun 	HPWMI_RET_UNKNOWN_COMMAND	= 0x03,
112*4882a593Smuzhiyun 	HPWMI_RET_UNKNOWN_CMDTYPE	= 0x04,
113*4882a593Smuzhiyun 	HPWMI_RET_INVALID_PARAMETERS	= 0x05,
114*4882a593Smuzhiyun };
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun enum hp_wireless2_bits {
117*4882a593Smuzhiyun 	HPWMI_POWER_STATE	= 0x01,
118*4882a593Smuzhiyun 	HPWMI_POWER_SOFT	= 0x02,
119*4882a593Smuzhiyun 	HPWMI_POWER_BIOS	= 0x04,
120*4882a593Smuzhiyun 	HPWMI_POWER_HARD	= 0x08,
121*4882a593Smuzhiyun 	HPWMI_POWER_FW_OR_HW	= HPWMI_POWER_BIOS | HPWMI_POWER_HARD,
122*4882a593Smuzhiyun };
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun #define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW)
125*4882a593Smuzhiyun #define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun struct bios_rfkill2_device_state {
128*4882a593Smuzhiyun 	u8 radio_type;
129*4882a593Smuzhiyun 	u8 bus_type;
130*4882a593Smuzhiyun 	u16 vendor_id;
131*4882a593Smuzhiyun 	u16 product_id;
132*4882a593Smuzhiyun 	u16 subsys_vendor_id;
133*4882a593Smuzhiyun 	u16 subsys_product_id;
134*4882a593Smuzhiyun 	u8 rfkill_id;
135*4882a593Smuzhiyun 	u8 power;
136*4882a593Smuzhiyun 	u8 unknown[4];
137*4882a593Smuzhiyun };
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun /* 7 devices fit into the 128 byte buffer */
140*4882a593Smuzhiyun #define HPWMI_MAX_RFKILL2_DEVICES	7
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun struct bios_rfkill2_state {
143*4882a593Smuzhiyun 	u8 unknown[7];
144*4882a593Smuzhiyun 	u8 count;
145*4882a593Smuzhiyun 	u8 pad[8];
146*4882a593Smuzhiyun 	struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES];
147*4882a593Smuzhiyun };
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun static const struct key_entry hp_wmi_keymap[] = {
150*4882a593Smuzhiyun 	{ KE_KEY, 0x02,   { KEY_BRIGHTNESSUP } },
151*4882a593Smuzhiyun 	{ KE_KEY, 0x03,   { KEY_BRIGHTNESSDOWN } },
152*4882a593Smuzhiyun 	{ KE_KEY, 0x20e6, { KEY_PROG1 } },
153*4882a593Smuzhiyun 	{ KE_KEY, 0x20e8, { KEY_MEDIA } },
154*4882a593Smuzhiyun 	{ KE_KEY, 0x2142, { KEY_MEDIA } },
155*4882a593Smuzhiyun 	{ KE_KEY, 0x213b, { KEY_INFO } },
156*4882a593Smuzhiyun 	{ KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } },
157*4882a593Smuzhiyun 	{ KE_KEY, 0x216a, { KEY_SETUP } },
158*4882a593Smuzhiyun 	{ KE_KEY, 0x231b, { KEY_HELP } },
159*4882a593Smuzhiyun 	{ KE_END, 0 }
160*4882a593Smuzhiyun };
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun static struct input_dev *hp_wmi_input_dev;
163*4882a593Smuzhiyun static struct platform_device *hp_wmi_platform_dev;
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun static struct rfkill *wifi_rfkill;
166*4882a593Smuzhiyun static struct rfkill *bluetooth_rfkill;
167*4882a593Smuzhiyun static struct rfkill *wwan_rfkill;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun struct rfkill2_device {
170*4882a593Smuzhiyun 	u8 id;
171*4882a593Smuzhiyun 	int num;
172*4882a593Smuzhiyun 	struct rfkill *rfkill;
173*4882a593Smuzhiyun };
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun static int rfkill2_count;
176*4882a593Smuzhiyun static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES];
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun /* map output size to the corresponding WMI method id */
encode_outsize_for_pvsz(int outsize)179*4882a593Smuzhiyun static inline int encode_outsize_for_pvsz(int outsize)
180*4882a593Smuzhiyun {
181*4882a593Smuzhiyun 	if (outsize > 4096)
182*4882a593Smuzhiyun 		return -EINVAL;
183*4882a593Smuzhiyun 	if (outsize > 1024)
184*4882a593Smuzhiyun 		return 5;
185*4882a593Smuzhiyun 	if (outsize > 128)
186*4882a593Smuzhiyun 		return 4;
187*4882a593Smuzhiyun 	if (outsize > 4)
188*4882a593Smuzhiyun 		return 3;
189*4882a593Smuzhiyun 	if (outsize > 0)
190*4882a593Smuzhiyun 		return 2;
191*4882a593Smuzhiyun 	return 1;
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun /*
195*4882a593Smuzhiyun  * hp_wmi_perform_query
196*4882a593Smuzhiyun  *
197*4882a593Smuzhiyun  * query:	The commandtype (enum hp_wmi_commandtype)
198*4882a593Smuzhiyun  * write:	The command (enum hp_wmi_command)
199*4882a593Smuzhiyun  * buffer:	Buffer used as input and/or output
200*4882a593Smuzhiyun  * insize:	Size of input buffer
201*4882a593Smuzhiyun  * outsize:	Size of output buffer
202*4882a593Smuzhiyun  *
203*4882a593Smuzhiyun  * returns zero on success
204*4882a593Smuzhiyun  *         an HP WMI query specific error code (which is positive)
205*4882a593Smuzhiyun  *         -EINVAL if the query was not successful at all
206*4882a593Smuzhiyun  *         -EINVAL if the output buffer size exceeds buffersize
207*4882a593Smuzhiyun  *
208*4882a593Smuzhiyun  * Note: The buffersize must at least be the maximum of the input and output
209*4882a593Smuzhiyun  *       size. E.g. Battery info query is defined to have 1 byte input
210*4882a593Smuzhiyun  *       and 128 byte output. The caller would do:
211*4882a593Smuzhiyun  *       buffer = kzalloc(128, GFP_KERNEL);
212*4882a593Smuzhiyun  *       ret = hp_wmi_perform_query(HPWMI_BATTERY_QUERY, HPWMI_READ, buffer, 1, 128)
213*4882a593Smuzhiyun  */
hp_wmi_perform_query(int query,enum hp_wmi_command command,void * buffer,int insize,int outsize)214*4882a593Smuzhiyun static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
215*4882a593Smuzhiyun 				void *buffer, int insize, int outsize)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun 	int mid;
218*4882a593Smuzhiyun 	struct bios_return *bios_return;
219*4882a593Smuzhiyun 	int actual_outsize;
220*4882a593Smuzhiyun 	union acpi_object *obj;
221*4882a593Smuzhiyun 	struct bios_args args = {
222*4882a593Smuzhiyun 		.signature = 0x55434553,
223*4882a593Smuzhiyun 		.command = command,
224*4882a593Smuzhiyun 		.commandtype = query,
225*4882a593Smuzhiyun 		.datasize = insize,
226*4882a593Smuzhiyun 		.data = { 0 },
227*4882a593Smuzhiyun 	};
228*4882a593Smuzhiyun 	struct acpi_buffer input = { sizeof(struct bios_args), &args };
229*4882a593Smuzhiyun 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
230*4882a593Smuzhiyun 	int ret = 0;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	mid = encode_outsize_for_pvsz(outsize);
233*4882a593Smuzhiyun 	if (WARN_ON(mid < 0))
234*4882a593Smuzhiyun 		return mid;
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	if (WARN_ON(insize > sizeof(args.data)))
237*4882a593Smuzhiyun 		return -EINVAL;
238*4882a593Smuzhiyun 	memcpy(&args.data[0], buffer, insize);
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output);
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	obj = output.pointer;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	if (!obj)
245*4882a593Smuzhiyun 		return -EINVAL;
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	if (obj->type != ACPI_TYPE_BUFFER) {
248*4882a593Smuzhiyun 		ret = -EINVAL;
249*4882a593Smuzhiyun 		goto out_free;
250*4882a593Smuzhiyun 	}
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	bios_return = (struct bios_return *)obj->buffer.pointer;
253*4882a593Smuzhiyun 	ret = bios_return->return_code;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	if (ret) {
256*4882a593Smuzhiyun 		if (ret != HPWMI_RET_UNKNOWN_COMMAND &&
257*4882a593Smuzhiyun 		    ret != HPWMI_RET_UNKNOWN_CMDTYPE)
258*4882a593Smuzhiyun 			pr_warn("query 0x%x returned error 0x%x\n", query, ret);
259*4882a593Smuzhiyun 		goto out_free;
260*4882a593Smuzhiyun 	}
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	/* Ignore output data of zero size */
263*4882a593Smuzhiyun 	if (!outsize)
264*4882a593Smuzhiyun 		goto out_free;
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return)));
267*4882a593Smuzhiyun 	memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize);
268*4882a593Smuzhiyun 	memset(buffer + actual_outsize, 0, outsize - actual_outsize);
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun out_free:
271*4882a593Smuzhiyun 	kfree(obj);
272*4882a593Smuzhiyun 	return ret;
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun 
hp_wmi_read_int(int query)275*4882a593Smuzhiyun static int hp_wmi_read_int(int query)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun 	int val = 0, ret;
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	ret = hp_wmi_perform_query(query, HPWMI_READ, &val,
280*4882a593Smuzhiyun 				   sizeof(val), sizeof(val));
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	if (ret)
283*4882a593Smuzhiyun 		return ret < 0 ? ret : -EINVAL;
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	return val;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun 
hp_wmi_hw_state(int mask)288*4882a593Smuzhiyun static int hp_wmi_hw_state(int mask)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun 	int state = hp_wmi_read_int(HPWMI_HARDWARE_QUERY);
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	if (state < 0)
293*4882a593Smuzhiyun 		return state;
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	return !!(state & mask);
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun 
hp_wmi_bios_2008_later(void)298*4882a593Smuzhiyun static int __init hp_wmi_bios_2008_later(void)
299*4882a593Smuzhiyun {
300*4882a593Smuzhiyun 	int state = 0;
301*4882a593Smuzhiyun 	int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, HPWMI_READ, &state,
302*4882a593Smuzhiyun 				       sizeof(state), sizeof(state));
303*4882a593Smuzhiyun 	if (!ret)
304*4882a593Smuzhiyun 		return 1;
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO;
307*4882a593Smuzhiyun }
308*4882a593Smuzhiyun 
hp_wmi_bios_2009_later(void)309*4882a593Smuzhiyun static int __init hp_wmi_bios_2009_later(void)
310*4882a593Smuzhiyun {
311*4882a593Smuzhiyun 	u8 state[128];
312*4882a593Smuzhiyun 	int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, HPWMI_READ, &state,
313*4882a593Smuzhiyun 				       sizeof(state), sizeof(state));
314*4882a593Smuzhiyun 	if (!ret)
315*4882a593Smuzhiyun 		return 1;
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun 
hp_wmi_enable_hotkeys(void)320*4882a593Smuzhiyun static int __init hp_wmi_enable_hotkeys(void)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun 	int value = 0x6e;
323*4882a593Smuzhiyun 	int ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, HPWMI_WRITE, &value,
324*4882a593Smuzhiyun 				       sizeof(value), 0);
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	return ret <= 0 ? ret : -EINVAL;
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun 
hp_wmi_set_block(void * data,bool blocked)329*4882a593Smuzhiyun static int hp_wmi_set_block(void *data, bool blocked)
330*4882a593Smuzhiyun {
331*4882a593Smuzhiyun 	enum hp_wmi_radio r = (enum hp_wmi_radio) data;
332*4882a593Smuzhiyun 	int query = BIT(r + 8) | ((!blocked) << r);
333*4882a593Smuzhiyun 	int ret;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE,
336*4882a593Smuzhiyun 				   &query, sizeof(query), 0);
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	return ret <= 0 ? ret : -EINVAL;
339*4882a593Smuzhiyun }
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun static const struct rfkill_ops hp_wmi_rfkill_ops = {
342*4882a593Smuzhiyun 	.set_block = hp_wmi_set_block,
343*4882a593Smuzhiyun };
344*4882a593Smuzhiyun 
hp_wmi_get_sw_state(enum hp_wmi_radio r)345*4882a593Smuzhiyun static bool hp_wmi_get_sw_state(enum hp_wmi_radio r)
346*4882a593Smuzhiyun {
347*4882a593Smuzhiyun 	int mask = 0x200 << (r * 8);
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY);
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 	/* TBD: Pass error */
352*4882a593Smuzhiyun 	WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY");
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 	return !(wireless & mask);
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun 
hp_wmi_get_hw_state(enum hp_wmi_radio r)357*4882a593Smuzhiyun static bool hp_wmi_get_hw_state(enum hp_wmi_radio r)
358*4882a593Smuzhiyun {
359*4882a593Smuzhiyun 	int mask = 0x800 << (r * 8);
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun 	int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY);
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	/* TBD: Pass error */
364*4882a593Smuzhiyun 	WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY");
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	return !(wireless & mask);
367*4882a593Smuzhiyun }
368*4882a593Smuzhiyun 
hp_wmi_rfkill2_set_block(void * data,bool blocked)369*4882a593Smuzhiyun static int hp_wmi_rfkill2_set_block(void *data, bool blocked)
370*4882a593Smuzhiyun {
371*4882a593Smuzhiyun 	int rfkill_id = (int)(long)data;
372*4882a593Smuzhiyun 	char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked };
373*4882a593Smuzhiyun 	int ret;
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	ret = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_WRITE,
376*4882a593Smuzhiyun 				   buffer, sizeof(buffer), 0);
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	return ret <= 0 ? ret : -EINVAL;
379*4882a593Smuzhiyun }
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun static const struct rfkill_ops hp_wmi_rfkill2_ops = {
382*4882a593Smuzhiyun 	.set_block = hp_wmi_rfkill2_set_block,
383*4882a593Smuzhiyun };
384*4882a593Smuzhiyun 
hp_wmi_rfkill2_refresh(void)385*4882a593Smuzhiyun static int hp_wmi_rfkill2_refresh(void)
386*4882a593Smuzhiyun {
387*4882a593Smuzhiyun 	struct bios_rfkill2_state state;
388*4882a593Smuzhiyun 	int err, i;
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state,
391*4882a593Smuzhiyun 				   sizeof(state), sizeof(state));
392*4882a593Smuzhiyun 	if (err)
393*4882a593Smuzhiyun 		return err;
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	for (i = 0; i < rfkill2_count; i++) {
396*4882a593Smuzhiyun 		int num = rfkill2[i].num;
397*4882a593Smuzhiyun 		struct bios_rfkill2_device_state *devstate;
398*4882a593Smuzhiyun 		devstate = &state.device[num];
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 		if (num >= state.count ||
401*4882a593Smuzhiyun 		    devstate->rfkill_id != rfkill2[i].id) {
402*4882a593Smuzhiyun 			pr_warn("power configuration of the wireless devices unexpectedly changed\n");
403*4882a593Smuzhiyun 			continue;
404*4882a593Smuzhiyun 		}
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 		rfkill_set_states(rfkill2[i].rfkill,
407*4882a593Smuzhiyun 				  IS_SWBLOCKED(devstate->power),
408*4882a593Smuzhiyun 				  IS_HWBLOCKED(devstate->power));
409*4882a593Smuzhiyun 	}
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	return 0;
412*4882a593Smuzhiyun }
413*4882a593Smuzhiyun 
display_show(struct device * dev,struct device_attribute * attr,char * buf)414*4882a593Smuzhiyun static ssize_t display_show(struct device *dev, struct device_attribute *attr,
415*4882a593Smuzhiyun 			    char *buf)
416*4882a593Smuzhiyun {
417*4882a593Smuzhiyun 	int value = hp_wmi_read_int(HPWMI_DISPLAY_QUERY);
418*4882a593Smuzhiyun 	if (value < 0)
419*4882a593Smuzhiyun 		return value;
420*4882a593Smuzhiyun 	return sprintf(buf, "%d\n", value);
421*4882a593Smuzhiyun }
422*4882a593Smuzhiyun 
hddtemp_show(struct device * dev,struct device_attribute * attr,char * buf)423*4882a593Smuzhiyun static ssize_t hddtemp_show(struct device *dev, struct device_attribute *attr,
424*4882a593Smuzhiyun 			    char *buf)
425*4882a593Smuzhiyun {
426*4882a593Smuzhiyun 	int value = hp_wmi_read_int(HPWMI_HDDTEMP_QUERY);
427*4882a593Smuzhiyun 	if (value < 0)
428*4882a593Smuzhiyun 		return value;
429*4882a593Smuzhiyun 	return sprintf(buf, "%d\n", value);
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun 
als_show(struct device * dev,struct device_attribute * attr,char * buf)432*4882a593Smuzhiyun static ssize_t als_show(struct device *dev, struct device_attribute *attr,
433*4882a593Smuzhiyun 			char *buf)
434*4882a593Smuzhiyun {
435*4882a593Smuzhiyun 	int value = hp_wmi_read_int(HPWMI_ALS_QUERY);
436*4882a593Smuzhiyun 	if (value < 0)
437*4882a593Smuzhiyun 		return value;
438*4882a593Smuzhiyun 	return sprintf(buf, "%d\n", value);
439*4882a593Smuzhiyun }
440*4882a593Smuzhiyun 
dock_show(struct device * dev,struct device_attribute * attr,char * buf)441*4882a593Smuzhiyun static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
442*4882a593Smuzhiyun 			 char *buf)
443*4882a593Smuzhiyun {
444*4882a593Smuzhiyun 	int value = hp_wmi_hw_state(HPWMI_DOCK_MASK);
445*4882a593Smuzhiyun 	if (value < 0)
446*4882a593Smuzhiyun 		return value;
447*4882a593Smuzhiyun 	return sprintf(buf, "%d\n", value);
448*4882a593Smuzhiyun }
449*4882a593Smuzhiyun 
tablet_show(struct device * dev,struct device_attribute * attr,char * buf)450*4882a593Smuzhiyun static ssize_t tablet_show(struct device *dev, struct device_attribute *attr,
451*4882a593Smuzhiyun 			   char *buf)
452*4882a593Smuzhiyun {
453*4882a593Smuzhiyun 	int value = hp_wmi_hw_state(HPWMI_TABLET_MASK);
454*4882a593Smuzhiyun 	if (value < 0)
455*4882a593Smuzhiyun 		return value;
456*4882a593Smuzhiyun 	return sprintf(buf, "%d\n", value);
457*4882a593Smuzhiyun }
458*4882a593Smuzhiyun 
postcode_show(struct device * dev,struct device_attribute * attr,char * buf)459*4882a593Smuzhiyun static ssize_t postcode_show(struct device *dev, struct device_attribute *attr,
460*4882a593Smuzhiyun 			     char *buf)
461*4882a593Smuzhiyun {
462*4882a593Smuzhiyun 	/* Get the POST error code of previous boot failure. */
463*4882a593Smuzhiyun 	int value = hp_wmi_read_int(HPWMI_POSTCODEERROR_QUERY);
464*4882a593Smuzhiyun 	if (value < 0)
465*4882a593Smuzhiyun 		return value;
466*4882a593Smuzhiyun 	return sprintf(buf, "0x%x\n", value);
467*4882a593Smuzhiyun }
468*4882a593Smuzhiyun 
als_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)469*4882a593Smuzhiyun static ssize_t als_store(struct device *dev, struct device_attribute *attr,
470*4882a593Smuzhiyun 			 const char *buf, size_t count)
471*4882a593Smuzhiyun {
472*4882a593Smuzhiyun 	u32 tmp;
473*4882a593Smuzhiyun 	int ret;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	ret = kstrtou32(buf, 10, &tmp);
476*4882a593Smuzhiyun 	if (ret)
477*4882a593Smuzhiyun 		return ret;
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun 	ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp,
480*4882a593Smuzhiyun 				       sizeof(tmp), sizeof(tmp));
481*4882a593Smuzhiyun 	if (ret)
482*4882a593Smuzhiyun 		return ret < 0 ? ret : -EINVAL;
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun 	return count;
485*4882a593Smuzhiyun }
486*4882a593Smuzhiyun 
postcode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)487*4882a593Smuzhiyun static ssize_t postcode_store(struct device *dev, struct device_attribute *attr,
488*4882a593Smuzhiyun 			      const char *buf, size_t count)
489*4882a593Smuzhiyun {
490*4882a593Smuzhiyun 	u32 tmp = 1;
491*4882a593Smuzhiyun 	bool clear;
492*4882a593Smuzhiyun 	int ret;
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	ret = kstrtobool(buf, &clear);
495*4882a593Smuzhiyun 	if (ret)
496*4882a593Smuzhiyun 		return ret;
497*4882a593Smuzhiyun 
498*4882a593Smuzhiyun 	if (clear == false)
499*4882a593Smuzhiyun 		return -EINVAL;
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun 	/* Clear the POST error code. It is kept until until cleared. */
502*4882a593Smuzhiyun 	ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp,
503*4882a593Smuzhiyun 				       sizeof(tmp), sizeof(tmp));
504*4882a593Smuzhiyun 	if (ret)
505*4882a593Smuzhiyun 		return ret < 0 ? ret : -EINVAL;
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 	return count;
508*4882a593Smuzhiyun }
509*4882a593Smuzhiyun 
510*4882a593Smuzhiyun static DEVICE_ATTR_RO(display);
511*4882a593Smuzhiyun static DEVICE_ATTR_RO(hddtemp);
512*4882a593Smuzhiyun static DEVICE_ATTR_RW(als);
513*4882a593Smuzhiyun static DEVICE_ATTR_RO(dock);
514*4882a593Smuzhiyun static DEVICE_ATTR_RO(tablet);
515*4882a593Smuzhiyun static DEVICE_ATTR_RW(postcode);
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun static struct attribute *hp_wmi_attrs[] = {
518*4882a593Smuzhiyun 	&dev_attr_display.attr,
519*4882a593Smuzhiyun 	&dev_attr_hddtemp.attr,
520*4882a593Smuzhiyun 	&dev_attr_als.attr,
521*4882a593Smuzhiyun 	&dev_attr_dock.attr,
522*4882a593Smuzhiyun 	&dev_attr_tablet.attr,
523*4882a593Smuzhiyun 	&dev_attr_postcode.attr,
524*4882a593Smuzhiyun 	NULL,
525*4882a593Smuzhiyun };
526*4882a593Smuzhiyun ATTRIBUTE_GROUPS(hp_wmi);
527*4882a593Smuzhiyun 
hp_wmi_notify(u32 value,void * context)528*4882a593Smuzhiyun static void hp_wmi_notify(u32 value, void *context)
529*4882a593Smuzhiyun {
530*4882a593Smuzhiyun 	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
531*4882a593Smuzhiyun 	u32 event_id, event_data;
532*4882a593Smuzhiyun 	union acpi_object *obj;
533*4882a593Smuzhiyun 	acpi_status status;
534*4882a593Smuzhiyun 	u32 *location;
535*4882a593Smuzhiyun 	int key_code;
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun 	status = wmi_get_event_data(value, &response);
538*4882a593Smuzhiyun 	if (status != AE_OK) {
539*4882a593Smuzhiyun 		pr_info("bad event status 0x%x\n", status);
540*4882a593Smuzhiyun 		return;
541*4882a593Smuzhiyun 	}
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 	obj = (union acpi_object *)response.pointer;
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	if (!obj)
546*4882a593Smuzhiyun 		return;
547*4882a593Smuzhiyun 	if (obj->type != ACPI_TYPE_BUFFER) {
548*4882a593Smuzhiyun 		pr_info("Unknown response received %d\n", obj->type);
549*4882a593Smuzhiyun 		kfree(obj);
550*4882a593Smuzhiyun 		return;
551*4882a593Smuzhiyun 	}
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 	/*
554*4882a593Smuzhiyun 	 * Depending on ACPI version the concatenation of id and event data
555*4882a593Smuzhiyun 	 * inside _WED function will result in a 8 or 16 byte buffer.
556*4882a593Smuzhiyun 	 */
557*4882a593Smuzhiyun 	location = (u32 *)obj->buffer.pointer;
558*4882a593Smuzhiyun 	if (obj->buffer.length == 8) {
559*4882a593Smuzhiyun 		event_id = *location;
560*4882a593Smuzhiyun 		event_data = *(location + 1);
561*4882a593Smuzhiyun 	} else if (obj->buffer.length == 16) {
562*4882a593Smuzhiyun 		event_id = *location;
563*4882a593Smuzhiyun 		event_data = *(location + 2);
564*4882a593Smuzhiyun 	} else {
565*4882a593Smuzhiyun 		pr_info("Unknown buffer length %d\n", obj->buffer.length);
566*4882a593Smuzhiyun 		kfree(obj);
567*4882a593Smuzhiyun 		return;
568*4882a593Smuzhiyun 	}
569*4882a593Smuzhiyun 	kfree(obj);
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun 	switch (event_id) {
572*4882a593Smuzhiyun 	case HPWMI_DOCK_EVENT:
573*4882a593Smuzhiyun 		if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit))
574*4882a593Smuzhiyun 			input_report_switch(hp_wmi_input_dev, SW_DOCK,
575*4882a593Smuzhiyun 					    hp_wmi_hw_state(HPWMI_DOCK_MASK));
576*4882a593Smuzhiyun 		if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit))
577*4882a593Smuzhiyun 			input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
578*4882a593Smuzhiyun 					    hp_wmi_hw_state(HPWMI_TABLET_MASK));
579*4882a593Smuzhiyun 		input_sync(hp_wmi_input_dev);
580*4882a593Smuzhiyun 		break;
581*4882a593Smuzhiyun 	case HPWMI_PARK_HDD:
582*4882a593Smuzhiyun 		break;
583*4882a593Smuzhiyun 	case HPWMI_SMART_ADAPTER:
584*4882a593Smuzhiyun 		break;
585*4882a593Smuzhiyun 	case HPWMI_BEZEL_BUTTON:
586*4882a593Smuzhiyun 		key_code = hp_wmi_read_int(HPWMI_HOTKEY_QUERY);
587*4882a593Smuzhiyun 		if (key_code < 0)
588*4882a593Smuzhiyun 			break;
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun 		if (!sparse_keymap_report_event(hp_wmi_input_dev,
591*4882a593Smuzhiyun 						key_code, 1, true))
592*4882a593Smuzhiyun 			pr_info("Unknown key code - 0x%x\n", key_code);
593*4882a593Smuzhiyun 		break;
594*4882a593Smuzhiyun 	case HPWMI_WIRELESS:
595*4882a593Smuzhiyun 		if (rfkill2_count) {
596*4882a593Smuzhiyun 			hp_wmi_rfkill2_refresh();
597*4882a593Smuzhiyun 			break;
598*4882a593Smuzhiyun 		}
599*4882a593Smuzhiyun 
600*4882a593Smuzhiyun 		if (wifi_rfkill)
601*4882a593Smuzhiyun 			rfkill_set_states(wifi_rfkill,
602*4882a593Smuzhiyun 					  hp_wmi_get_sw_state(HPWMI_WIFI),
603*4882a593Smuzhiyun 					  hp_wmi_get_hw_state(HPWMI_WIFI));
604*4882a593Smuzhiyun 		if (bluetooth_rfkill)
605*4882a593Smuzhiyun 			rfkill_set_states(bluetooth_rfkill,
606*4882a593Smuzhiyun 					  hp_wmi_get_sw_state(HPWMI_BLUETOOTH),
607*4882a593Smuzhiyun 					  hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
608*4882a593Smuzhiyun 		if (wwan_rfkill)
609*4882a593Smuzhiyun 			rfkill_set_states(wwan_rfkill,
610*4882a593Smuzhiyun 					  hp_wmi_get_sw_state(HPWMI_WWAN),
611*4882a593Smuzhiyun 					  hp_wmi_get_hw_state(HPWMI_WWAN));
612*4882a593Smuzhiyun 		break;
613*4882a593Smuzhiyun 	case HPWMI_CPU_BATTERY_THROTTLE:
614*4882a593Smuzhiyun 		pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n");
615*4882a593Smuzhiyun 		break;
616*4882a593Smuzhiyun 	case HPWMI_LOCK_SWITCH:
617*4882a593Smuzhiyun 		break;
618*4882a593Smuzhiyun 	case HPWMI_LID_SWITCH:
619*4882a593Smuzhiyun 		break;
620*4882a593Smuzhiyun 	case HPWMI_SCREEN_ROTATION:
621*4882a593Smuzhiyun 		break;
622*4882a593Smuzhiyun 	case HPWMI_COOLSENSE_SYSTEM_MOBILE:
623*4882a593Smuzhiyun 		break;
624*4882a593Smuzhiyun 	case HPWMI_COOLSENSE_SYSTEM_HOT:
625*4882a593Smuzhiyun 		break;
626*4882a593Smuzhiyun 	case HPWMI_PROXIMITY_SENSOR:
627*4882a593Smuzhiyun 		break;
628*4882a593Smuzhiyun 	case HPWMI_BACKLIT_KB_BRIGHTNESS:
629*4882a593Smuzhiyun 		break;
630*4882a593Smuzhiyun 	case HPWMI_PEAKSHIFT_PERIOD:
631*4882a593Smuzhiyun 		break;
632*4882a593Smuzhiyun 	case HPWMI_BATTERY_CHARGE_PERIOD:
633*4882a593Smuzhiyun 		break;
634*4882a593Smuzhiyun 	case HPWMI_SANITIZATION_MODE:
635*4882a593Smuzhiyun 		break;
636*4882a593Smuzhiyun 	case HPWMI_SMART_EXPERIENCE_APP:
637*4882a593Smuzhiyun 		break;
638*4882a593Smuzhiyun 	default:
639*4882a593Smuzhiyun 		pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data);
640*4882a593Smuzhiyun 		break;
641*4882a593Smuzhiyun 	}
642*4882a593Smuzhiyun }
643*4882a593Smuzhiyun 
hp_wmi_input_setup(void)644*4882a593Smuzhiyun static int __init hp_wmi_input_setup(void)
645*4882a593Smuzhiyun {
646*4882a593Smuzhiyun 	acpi_status status;
647*4882a593Smuzhiyun 	int err, val;
648*4882a593Smuzhiyun 
649*4882a593Smuzhiyun 	hp_wmi_input_dev = input_allocate_device();
650*4882a593Smuzhiyun 	if (!hp_wmi_input_dev)
651*4882a593Smuzhiyun 		return -ENOMEM;
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	hp_wmi_input_dev->name = "HP WMI hotkeys";
654*4882a593Smuzhiyun 	hp_wmi_input_dev->phys = "wmi/input0";
655*4882a593Smuzhiyun 	hp_wmi_input_dev->id.bustype = BUS_HOST;
656*4882a593Smuzhiyun 
657*4882a593Smuzhiyun 	__set_bit(EV_SW, hp_wmi_input_dev->evbit);
658*4882a593Smuzhiyun 
659*4882a593Smuzhiyun 	/* Dock */
660*4882a593Smuzhiyun 	val = hp_wmi_hw_state(HPWMI_DOCK_MASK);
661*4882a593Smuzhiyun 	if (!(val < 0)) {
662*4882a593Smuzhiyun 		__set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
663*4882a593Smuzhiyun 		input_report_switch(hp_wmi_input_dev, SW_DOCK, val);
664*4882a593Smuzhiyun 	}
665*4882a593Smuzhiyun 
666*4882a593Smuzhiyun 	/* Tablet mode */
667*4882a593Smuzhiyun 	if (enable_tablet_mode_sw > 0) {
668*4882a593Smuzhiyun 		val = hp_wmi_hw_state(HPWMI_TABLET_MASK);
669*4882a593Smuzhiyun 		if (val >= 0) {
670*4882a593Smuzhiyun 			__set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
671*4882a593Smuzhiyun 			input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, val);
672*4882a593Smuzhiyun 		}
673*4882a593Smuzhiyun 	}
674*4882a593Smuzhiyun 
675*4882a593Smuzhiyun 	err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL);
676*4882a593Smuzhiyun 	if (err)
677*4882a593Smuzhiyun 		goto err_free_dev;
678*4882a593Smuzhiyun 
679*4882a593Smuzhiyun 	/* Set initial hardware state */
680*4882a593Smuzhiyun 	input_sync(hp_wmi_input_dev);
681*4882a593Smuzhiyun 
682*4882a593Smuzhiyun 	if (!hp_wmi_bios_2009_later() && hp_wmi_bios_2008_later())
683*4882a593Smuzhiyun 		hp_wmi_enable_hotkeys();
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun 	status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);
686*4882a593Smuzhiyun 	if (ACPI_FAILURE(status)) {
687*4882a593Smuzhiyun 		err = -EIO;
688*4882a593Smuzhiyun 		goto err_free_dev;
689*4882a593Smuzhiyun 	}
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun 	err = input_register_device(hp_wmi_input_dev);
692*4882a593Smuzhiyun 	if (err)
693*4882a593Smuzhiyun 		goto err_uninstall_notifier;
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 	return 0;
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun  err_uninstall_notifier:
698*4882a593Smuzhiyun 	wmi_remove_notify_handler(HPWMI_EVENT_GUID);
699*4882a593Smuzhiyun  err_free_dev:
700*4882a593Smuzhiyun 	input_free_device(hp_wmi_input_dev);
701*4882a593Smuzhiyun 	return err;
702*4882a593Smuzhiyun }
703*4882a593Smuzhiyun 
hp_wmi_input_destroy(void)704*4882a593Smuzhiyun static void hp_wmi_input_destroy(void)
705*4882a593Smuzhiyun {
706*4882a593Smuzhiyun 	wmi_remove_notify_handler(HPWMI_EVENT_GUID);
707*4882a593Smuzhiyun 	input_unregister_device(hp_wmi_input_dev);
708*4882a593Smuzhiyun }
709*4882a593Smuzhiyun 
hp_wmi_rfkill_setup(struct platform_device * device)710*4882a593Smuzhiyun static int __init hp_wmi_rfkill_setup(struct platform_device *device)
711*4882a593Smuzhiyun {
712*4882a593Smuzhiyun 	int err, wireless;
713*4882a593Smuzhiyun 
714*4882a593Smuzhiyun 	wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY);
715*4882a593Smuzhiyun 	if (wireless < 0)
716*4882a593Smuzhiyun 		return wireless;
717*4882a593Smuzhiyun 
718*4882a593Smuzhiyun 	err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, &wireless,
719*4882a593Smuzhiyun 				   sizeof(wireless), 0);
720*4882a593Smuzhiyun 	if (err)
721*4882a593Smuzhiyun 		return err;
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun 	if (wireless & 0x1) {
724*4882a593Smuzhiyun 		wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
725*4882a593Smuzhiyun 					   RFKILL_TYPE_WLAN,
726*4882a593Smuzhiyun 					   &hp_wmi_rfkill_ops,
727*4882a593Smuzhiyun 					   (void *) HPWMI_WIFI);
728*4882a593Smuzhiyun 		if (!wifi_rfkill)
729*4882a593Smuzhiyun 			return -ENOMEM;
730*4882a593Smuzhiyun 		rfkill_init_sw_state(wifi_rfkill,
731*4882a593Smuzhiyun 				     hp_wmi_get_sw_state(HPWMI_WIFI));
732*4882a593Smuzhiyun 		rfkill_set_hw_state(wifi_rfkill,
733*4882a593Smuzhiyun 				    hp_wmi_get_hw_state(HPWMI_WIFI));
734*4882a593Smuzhiyun 		err = rfkill_register(wifi_rfkill);
735*4882a593Smuzhiyun 		if (err)
736*4882a593Smuzhiyun 			goto register_wifi_error;
737*4882a593Smuzhiyun 	}
738*4882a593Smuzhiyun 
739*4882a593Smuzhiyun 	if (wireless & 0x2) {
740*4882a593Smuzhiyun 		bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev,
741*4882a593Smuzhiyun 						RFKILL_TYPE_BLUETOOTH,
742*4882a593Smuzhiyun 						&hp_wmi_rfkill_ops,
743*4882a593Smuzhiyun 						(void *) HPWMI_BLUETOOTH);
744*4882a593Smuzhiyun 		if (!bluetooth_rfkill) {
745*4882a593Smuzhiyun 			err = -ENOMEM;
746*4882a593Smuzhiyun 			goto register_bluetooth_error;
747*4882a593Smuzhiyun 		}
748*4882a593Smuzhiyun 		rfkill_init_sw_state(bluetooth_rfkill,
749*4882a593Smuzhiyun 				     hp_wmi_get_sw_state(HPWMI_BLUETOOTH));
750*4882a593Smuzhiyun 		rfkill_set_hw_state(bluetooth_rfkill,
751*4882a593Smuzhiyun 				    hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
752*4882a593Smuzhiyun 		err = rfkill_register(bluetooth_rfkill);
753*4882a593Smuzhiyun 		if (err)
754*4882a593Smuzhiyun 			goto register_bluetooth_error;
755*4882a593Smuzhiyun 	}
756*4882a593Smuzhiyun 
757*4882a593Smuzhiyun 	if (wireless & 0x4) {
758*4882a593Smuzhiyun 		wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev,
759*4882a593Smuzhiyun 					   RFKILL_TYPE_WWAN,
760*4882a593Smuzhiyun 					   &hp_wmi_rfkill_ops,
761*4882a593Smuzhiyun 					   (void *) HPWMI_WWAN);
762*4882a593Smuzhiyun 		if (!wwan_rfkill) {
763*4882a593Smuzhiyun 			err = -ENOMEM;
764*4882a593Smuzhiyun 			goto register_wwan_error;
765*4882a593Smuzhiyun 		}
766*4882a593Smuzhiyun 		rfkill_init_sw_state(wwan_rfkill,
767*4882a593Smuzhiyun 				     hp_wmi_get_sw_state(HPWMI_WWAN));
768*4882a593Smuzhiyun 		rfkill_set_hw_state(wwan_rfkill,
769*4882a593Smuzhiyun 				    hp_wmi_get_hw_state(HPWMI_WWAN));
770*4882a593Smuzhiyun 		err = rfkill_register(wwan_rfkill);
771*4882a593Smuzhiyun 		if (err)
772*4882a593Smuzhiyun 			goto register_wwan_error;
773*4882a593Smuzhiyun 	}
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun 	return 0;
776*4882a593Smuzhiyun 
777*4882a593Smuzhiyun register_wwan_error:
778*4882a593Smuzhiyun 	rfkill_destroy(wwan_rfkill);
779*4882a593Smuzhiyun 	wwan_rfkill = NULL;
780*4882a593Smuzhiyun 	if (bluetooth_rfkill)
781*4882a593Smuzhiyun 		rfkill_unregister(bluetooth_rfkill);
782*4882a593Smuzhiyun register_bluetooth_error:
783*4882a593Smuzhiyun 	rfkill_destroy(bluetooth_rfkill);
784*4882a593Smuzhiyun 	bluetooth_rfkill = NULL;
785*4882a593Smuzhiyun 	if (wifi_rfkill)
786*4882a593Smuzhiyun 		rfkill_unregister(wifi_rfkill);
787*4882a593Smuzhiyun register_wifi_error:
788*4882a593Smuzhiyun 	rfkill_destroy(wifi_rfkill);
789*4882a593Smuzhiyun 	wifi_rfkill = NULL;
790*4882a593Smuzhiyun 	return err;
791*4882a593Smuzhiyun }
792*4882a593Smuzhiyun 
hp_wmi_rfkill2_setup(struct platform_device * device)793*4882a593Smuzhiyun static int __init hp_wmi_rfkill2_setup(struct platform_device *device)
794*4882a593Smuzhiyun {
795*4882a593Smuzhiyun 	struct bios_rfkill2_state state;
796*4882a593Smuzhiyun 	int err, i;
797*4882a593Smuzhiyun 
798*4882a593Smuzhiyun 	err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state,
799*4882a593Smuzhiyun 				   sizeof(state), sizeof(state));
800*4882a593Smuzhiyun 	if (err)
801*4882a593Smuzhiyun 		return err < 0 ? err : -EINVAL;
802*4882a593Smuzhiyun 
803*4882a593Smuzhiyun 	if (state.count > HPWMI_MAX_RFKILL2_DEVICES) {
804*4882a593Smuzhiyun 		pr_warn("unable to parse 0x1b query output\n");
805*4882a593Smuzhiyun 		return -EINVAL;
806*4882a593Smuzhiyun 	}
807*4882a593Smuzhiyun 
808*4882a593Smuzhiyun 	for (i = 0; i < state.count; i++) {
809*4882a593Smuzhiyun 		struct rfkill *rfkill;
810*4882a593Smuzhiyun 		enum rfkill_type type;
811*4882a593Smuzhiyun 		char *name;
812*4882a593Smuzhiyun 		switch (state.device[i].radio_type) {
813*4882a593Smuzhiyun 		case HPWMI_WIFI:
814*4882a593Smuzhiyun 			type = RFKILL_TYPE_WLAN;
815*4882a593Smuzhiyun 			name = "hp-wifi";
816*4882a593Smuzhiyun 			break;
817*4882a593Smuzhiyun 		case HPWMI_BLUETOOTH:
818*4882a593Smuzhiyun 			type = RFKILL_TYPE_BLUETOOTH;
819*4882a593Smuzhiyun 			name = "hp-bluetooth";
820*4882a593Smuzhiyun 			break;
821*4882a593Smuzhiyun 		case HPWMI_WWAN:
822*4882a593Smuzhiyun 			type = RFKILL_TYPE_WWAN;
823*4882a593Smuzhiyun 			name = "hp-wwan";
824*4882a593Smuzhiyun 			break;
825*4882a593Smuzhiyun 		case HPWMI_GPS:
826*4882a593Smuzhiyun 			type = RFKILL_TYPE_GPS;
827*4882a593Smuzhiyun 			name = "hp-gps";
828*4882a593Smuzhiyun 			break;
829*4882a593Smuzhiyun 		default:
830*4882a593Smuzhiyun 			pr_warn("unknown device type 0x%x\n",
831*4882a593Smuzhiyun 				state.device[i].radio_type);
832*4882a593Smuzhiyun 			continue;
833*4882a593Smuzhiyun 		}
834*4882a593Smuzhiyun 
835*4882a593Smuzhiyun 		if (!state.device[i].vendor_id) {
836*4882a593Smuzhiyun 			pr_warn("zero device %d while %d reported\n",
837*4882a593Smuzhiyun 				i, state.count);
838*4882a593Smuzhiyun 			continue;
839*4882a593Smuzhiyun 		}
840*4882a593Smuzhiyun 
841*4882a593Smuzhiyun 		rfkill = rfkill_alloc(name, &device->dev, type,
842*4882a593Smuzhiyun 				      &hp_wmi_rfkill2_ops, (void *)(long)i);
843*4882a593Smuzhiyun 		if (!rfkill) {
844*4882a593Smuzhiyun 			err = -ENOMEM;
845*4882a593Smuzhiyun 			goto fail;
846*4882a593Smuzhiyun 		}
847*4882a593Smuzhiyun 
848*4882a593Smuzhiyun 		rfkill2[rfkill2_count].id = state.device[i].rfkill_id;
849*4882a593Smuzhiyun 		rfkill2[rfkill2_count].num = i;
850*4882a593Smuzhiyun 		rfkill2[rfkill2_count].rfkill = rfkill;
851*4882a593Smuzhiyun 
852*4882a593Smuzhiyun 		rfkill_init_sw_state(rfkill,
853*4882a593Smuzhiyun 				     IS_SWBLOCKED(state.device[i].power));
854*4882a593Smuzhiyun 		rfkill_set_hw_state(rfkill,
855*4882a593Smuzhiyun 				    IS_HWBLOCKED(state.device[i].power));
856*4882a593Smuzhiyun 
857*4882a593Smuzhiyun 		if (!(state.device[i].power & HPWMI_POWER_BIOS))
858*4882a593Smuzhiyun 			pr_info("device %s blocked by BIOS\n", name);
859*4882a593Smuzhiyun 
860*4882a593Smuzhiyun 		err = rfkill_register(rfkill);
861*4882a593Smuzhiyun 		if (err) {
862*4882a593Smuzhiyun 			rfkill_destroy(rfkill);
863*4882a593Smuzhiyun 			goto fail;
864*4882a593Smuzhiyun 		}
865*4882a593Smuzhiyun 
866*4882a593Smuzhiyun 		rfkill2_count++;
867*4882a593Smuzhiyun 	}
868*4882a593Smuzhiyun 
869*4882a593Smuzhiyun 	return 0;
870*4882a593Smuzhiyun fail:
871*4882a593Smuzhiyun 	for (; rfkill2_count > 0; rfkill2_count--) {
872*4882a593Smuzhiyun 		rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill);
873*4882a593Smuzhiyun 		rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill);
874*4882a593Smuzhiyun 	}
875*4882a593Smuzhiyun 	return err;
876*4882a593Smuzhiyun }
877*4882a593Smuzhiyun 
thermal_policy_setup(struct platform_device * device)878*4882a593Smuzhiyun static int thermal_policy_setup(struct platform_device *device)
879*4882a593Smuzhiyun {
880*4882a593Smuzhiyun 	int err, tp;
881*4882a593Smuzhiyun 
882*4882a593Smuzhiyun 	tp = hp_wmi_read_int(HPWMI_THERMAL_POLICY_QUERY);
883*4882a593Smuzhiyun 	if (tp < 0)
884*4882a593Smuzhiyun 		return tp;
885*4882a593Smuzhiyun 
886*4882a593Smuzhiyun 	/*
887*4882a593Smuzhiyun 	 * call thermal policy write command to ensure that the firmware correctly
888*4882a593Smuzhiyun 	 * sets the OEM variables for the DPTF
889*4882a593Smuzhiyun 	 */
890*4882a593Smuzhiyun 	err = hp_wmi_perform_query(HPWMI_THERMAL_POLICY_QUERY, HPWMI_WRITE, &tp,
891*4882a593Smuzhiyun 							   sizeof(tp), 0);
892*4882a593Smuzhiyun 	if (err)
893*4882a593Smuzhiyun 		return err;
894*4882a593Smuzhiyun 
895*4882a593Smuzhiyun 	return 0;
896*4882a593Smuzhiyun }
897*4882a593Smuzhiyun 
hp_wmi_bios_setup(struct platform_device * device)898*4882a593Smuzhiyun static int __init hp_wmi_bios_setup(struct platform_device *device)
899*4882a593Smuzhiyun {
900*4882a593Smuzhiyun 	/* clear detected rfkill devices */
901*4882a593Smuzhiyun 	wifi_rfkill = NULL;
902*4882a593Smuzhiyun 	bluetooth_rfkill = NULL;
903*4882a593Smuzhiyun 	wwan_rfkill = NULL;
904*4882a593Smuzhiyun 	rfkill2_count = 0;
905*4882a593Smuzhiyun 
906*4882a593Smuzhiyun 	/*
907*4882a593Smuzhiyun 	 * In pre-2009 BIOS, command 1Bh return 0x4 to indicate that
908*4882a593Smuzhiyun 	 * BIOS no longer controls the power for the wireless
909*4882a593Smuzhiyun 	 * devices. All features supported by this command will no
910*4882a593Smuzhiyun 	 * longer be supported.
911*4882a593Smuzhiyun 	 */
912*4882a593Smuzhiyun 	if (!hp_wmi_bios_2009_later()) {
913*4882a593Smuzhiyun 		if (hp_wmi_rfkill_setup(device))
914*4882a593Smuzhiyun 			hp_wmi_rfkill2_setup(device);
915*4882a593Smuzhiyun 	}
916*4882a593Smuzhiyun 
917*4882a593Smuzhiyun 	thermal_policy_setup(device);
918*4882a593Smuzhiyun 
919*4882a593Smuzhiyun 	return 0;
920*4882a593Smuzhiyun }
921*4882a593Smuzhiyun 
hp_wmi_bios_remove(struct platform_device * device)922*4882a593Smuzhiyun static int __exit hp_wmi_bios_remove(struct platform_device *device)
923*4882a593Smuzhiyun {
924*4882a593Smuzhiyun 	int i;
925*4882a593Smuzhiyun 
926*4882a593Smuzhiyun 	for (i = 0; i < rfkill2_count; i++) {
927*4882a593Smuzhiyun 		rfkill_unregister(rfkill2[i].rfkill);
928*4882a593Smuzhiyun 		rfkill_destroy(rfkill2[i].rfkill);
929*4882a593Smuzhiyun 	}
930*4882a593Smuzhiyun 
931*4882a593Smuzhiyun 	if (wifi_rfkill) {
932*4882a593Smuzhiyun 		rfkill_unregister(wifi_rfkill);
933*4882a593Smuzhiyun 		rfkill_destroy(wifi_rfkill);
934*4882a593Smuzhiyun 	}
935*4882a593Smuzhiyun 	if (bluetooth_rfkill) {
936*4882a593Smuzhiyun 		rfkill_unregister(bluetooth_rfkill);
937*4882a593Smuzhiyun 		rfkill_destroy(bluetooth_rfkill);
938*4882a593Smuzhiyun 	}
939*4882a593Smuzhiyun 	if (wwan_rfkill) {
940*4882a593Smuzhiyun 		rfkill_unregister(wwan_rfkill);
941*4882a593Smuzhiyun 		rfkill_destroy(wwan_rfkill);
942*4882a593Smuzhiyun 	}
943*4882a593Smuzhiyun 
944*4882a593Smuzhiyun 	return 0;
945*4882a593Smuzhiyun }
946*4882a593Smuzhiyun 
hp_wmi_resume_handler(struct device * device)947*4882a593Smuzhiyun static int hp_wmi_resume_handler(struct device *device)
948*4882a593Smuzhiyun {
949*4882a593Smuzhiyun 	/*
950*4882a593Smuzhiyun 	 * Hardware state may have changed while suspended, so trigger
951*4882a593Smuzhiyun 	 * input events for the current state. As this is a switch,
952*4882a593Smuzhiyun 	 * the input layer will only actually pass it on if the state
953*4882a593Smuzhiyun 	 * changed.
954*4882a593Smuzhiyun 	 */
955*4882a593Smuzhiyun 	if (hp_wmi_input_dev) {
956*4882a593Smuzhiyun 		if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit))
957*4882a593Smuzhiyun 			input_report_switch(hp_wmi_input_dev, SW_DOCK,
958*4882a593Smuzhiyun 					    hp_wmi_hw_state(HPWMI_DOCK_MASK));
959*4882a593Smuzhiyun 		if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit))
960*4882a593Smuzhiyun 			input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
961*4882a593Smuzhiyun 					    hp_wmi_hw_state(HPWMI_TABLET_MASK));
962*4882a593Smuzhiyun 		input_sync(hp_wmi_input_dev);
963*4882a593Smuzhiyun 	}
964*4882a593Smuzhiyun 
965*4882a593Smuzhiyun 	if (rfkill2_count)
966*4882a593Smuzhiyun 		hp_wmi_rfkill2_refresh();
967*4882a593Smuzhiyun 
968*4882a593Smuzhiyun 	if (wifi_rfkill)
969*4882a593Smuzhiyun 		rfkill_set_states(wifi_rfkill,
970*4882a593Smuzhiyun 				  hp_wmi_get_sw_state(HPWMI_WIFI),
971*4882a593Smuzhiyun 				  hp_wmi_get_hw_state(HPWMI_WIFI));
972*4882a593Smuzhiyun 	if (bluetooth_rfkill)
973*4882a593Smuzhiyun 		rfkill_set_states(bluetooth_rfkill,
974*4882a593Smuzhiyun 				  hp_wmi_get_sw_state(HPWMI_BLUETOOTH),
975*4882a593Smuzhiyun 				  hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
976*4882a593Smuzhiyun 	if (wwan_rfkill)
977*4882a593Smuzhiyun 		rfkill_set_states(wwan_rfkill,
978*4882a593Smuzhiyun 				  hp_wmi_get_sw_state(HPWMI_WWAN),
979*4882a593Smuzhiyun 				  hp_wmi_get_hw_state(HPWMI_WWAN));
980*4882a593Smuzhiyun 
981*4882a593Smuzhiyun 	return 0;
982*4882a593Smuzhiyun }
983*4882a593Smuzhiyun 
984*4882a593Smuzhiyun static const struct dev_pm_ops hp_wmi_pm_ops = {
985*4882a593Smuzhiyun 	.resume  = hp_wmi_resume_handler,
986*4882a593Smuzhiyun 	.restore  = hp_wmi_resume_handler,
987*4882a593Smuzhiyun };
988*4882a593Smuzhiyun 
989*4882a593Smuzhiyun static struct platform_driver hp_wmi_driver = {
990*4882a593Smuzhiyun 	.driver = {
991*4882a593Smuzhiyun 		.name = "hp-wmi",
992*4882a593Smuzhiyun 		.pm = &hp_wmi_pm_ops,
993*4882a593Smuzhiyun 		.dev_groups = hp_wmi_groups,
994*4882a593Smuzhiyun 	},
995*4882a593Smuzhiyun 	.remove = __exit_p(hp_wmi_bios_remove),
996*4882a593Smuzhiyun };
997*4882a593Smuzhiyun 
hp_wmi_init(void)998*4882a593Smuzhiyun static int __init hp_wmi_init(void)
999*4882a593Smuzhiyun {
1000*4882a593Smuzhiyun 	int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);
1001*4882a593Smuzhiyun 	int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
1002*4882a593Smuzhiyun 	int err;
1003*4882a593Smuzhiyun 
1004*4882a593Smuzhiyun 	if (!bios_capable && !event_capable)
1005*4882a593Smuzhiyun 		return -ENODEV;
1006*4882a593Smuzhiyun 
1007*4882a593Smuzhiyun 	if (event_capable) {
1008*4882a593Smuzhiyun 		err = hp_wmi_input_setup();
1009*4882a593Smuzhiyun 		if (err)
1010*4882a593Smuzhiyun 			return err;
1011*4882a593Smuzhiyun 	}
1012*4882a593Smuzhiyun 
1013*4882a593Smuzhiyun 	if (bios_capable) {
1014*4882a593Smuzhiyun 		hp_wmi_platform_dev =
1015*4882a593Smuzhiyun 			platform_device_register_simple("hp-wmi", -1, NULL, 0);
1016*4882a593Smuzhiyun 		if (IS_ERR(hp_wmi_platform_dev)) {
1017*4882a593Smuzhiyun 			err = PTR_ERR(hp_wmi_platform_dev);
1018*4882a593Smuzhiyun 			goto err_destroy_input;
1019*4882a593Smuzhiyun 		}
1020*4882a593Smuzhiyun 
1021*4882a593Smuzhiyun 		err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup);
1022*4882a593Smuzhiyun 		if (err)
1023*4882a593Smuzhiyun 			goto err_unregister_device;
1024*4882a593Smuzhiyun 	}
1025*4882a593Smuzhiyun 
1026*4882a593Smuzhiyun 	return 0;
1027*4882a593Smuzhiyun 
1028*4882a593Smuzhiyun err_unregister_device:
1029*4882a593Smuzhiyun 	platform_device_unregister(hp_wmi_platform_dev);
1030*4882a593Smuzhiyun err_destroy_input:
1031*4882a593Smuzhiyun 	if (event_capable)
1032*4882a593Smuzhiyun 		hp_wmi_input_destroy();
1033*4882a593Smuzhiyun 
1034*4882a593Smuzhiyun 	return err;
1035*4882a593Smuzhiyun }
1036*4882a593Smuzhiyun module_init(hp_wmi_init);
1037*4882a593Smuzhiyun 
hp_wmi_exit(void)1038*4882a593Smuzhiyun static void __exit hp_wmi_exit(void)
1039*4882a593Smuzhiyun {
1040*4882a593Smuzhiyun 	if (wmi_has_guid(HPWMI_EVENT_GUID))
1041*4882a593Smuzhiyun 		hp_wmi_input_destroy();
1042*4882a593Smuzhiyun 
1043*4882a593Smuzhiyun 	if (hp_wmi_platform_dev) {
1044*4882a593Smuzhiyun 		platform_device_unregister(hp_wmi_platform_dev);
1045*4882a593Smuzhiyun 		platform_driver_unregister(&hp_wmi_driver);
1046*4882a593Smuzhiyun 	}
1047*4882a593Smuzhiyun }
1048*4882a593Smuzhiyun module_exit(hp_wmi_exit);
1049