xref: /OK3568_Linux_fs/kernel/drivers/platform/x86/huawei-wmi.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  Huawei WMI laptop extras driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (C) 2018	      Ayman Bagabas <ayman.bagabas@gmail.com>
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/acpi.h>
9*4882a593Smuzhiyun #include <linux/debugfs.h>
10*4882a593Smuzhiyun #include <linux/delay.h>
11*4882a593Smuzhiyun #include <linux/dmi.h>
12*4882a593Smuzhiyun #include <linux/input.h>
13*4882a593Smuzhiyun #include <linux/input/sparse-keymap.h>
14*4882a593Smuzhiyun #include <linux/leds.h>
15*4882a593Smuzhiyun #include <linux/module.h>
16*4882a593Smuzhiyun #include <linux/mutex.h>
17*4882a593Smuzhiyun #include <linux/platform_device.h>
18*4882a593Smuzhiyun #include <linux/power_supply.h>
19*4882a593Smuzhiyun #include <linux/sysfs.h>
20*4882a593Smuzhiyun #include <linux/wmi.h>
21*4882a593Smuzhiyun #include <acpi/battery.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun /*
24*4882a593Smuzhiyun  * Huawei WMI GUIDs
25*4882a593Smuzhiyun  */
26*4882a593Smuzhiyun #define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C90629100000"
27*4882a593Smuzhiyun #define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000"
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun /* Legacy GUIDs */
30*4882a593Smuzhiyun #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
31*4882a593Smuzhiyun #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun /* HWMI commands */
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun enum {
36*4882a593Smuzhiyun 	BATTERY_THRESH_GET		= 0x00001103, /* \GBTT */
37*4882a593Smuzhiyun 	BATTERY_THRESH_SET		= 0x00001003, /* \SBTT */
38*4882a593Smuzhiyun 	FN_LOCK_GET			= 0x00000604, /* \GFRS */
39*4882a593Smuzhiyun 	FN_LOCK_SET			= 0x00000704, /* \SFRS */
40*4882a593Smuzhiyun 	MICMUTE_LED_SET			= 0x00000b04, /* \SMLS */
41*4882a593Smuzhiyun };
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun union hwmi_arg {
44*4882a593Smuzhiyun 	u64 cmd;
45*4882a593Smuzhiyun 	u8 args[8];
46*4882a593Smuzhiyun };
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun struct quirk_entry {
49*4882a593Smuzhiyun 	bool battery_reset;
50*4882a593Smuzhiyun 	bool ec_micmute;
51*4882a593Smuzhiyun 	bool report_brightness;
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun static struct quirk_entry *quirks;
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun struct huawei_wmi_debug {
57*4882a593Smuzhiyun 	struct dentry *root;
58*4882a593Smuzhiyun 	u64 arg;
59*4882a593Smuzhiyun };
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun struct huawei_wmi {
62*4882a593Smuzhiyun 	bool battery_available;
63*4882a593Smuzhiyun 	bool fn_lock_available;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	struct huawei_wmi_debug debug;
66*4882a593Smuzhiyun 	struct input_dev *idev[2];
67*4882a593Smuzhiyun 	struct led_classdev cdev;
68*4882a593Smuzhiyun 	struct device *dev;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	struct mutex wmi_lock;
71*4882a593Smuzhiyun };
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun static struct huawei_wmi *huawei_wmi;
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun static const struct key_entry huawei_wmi_keymap[] = {
76*4882a593Smuzhiyun 	{ KE_KEY,    0x281, { KEY_BRIGHTNESSDOWN } },
77*4882a593Smuzhiyun 	{ KE_KEY,    0x282, { KEY_BRIGHTNESSUP } },
78*4882a593Smuzhiyun 	{ KE_KEY,    0x284, { KEY_MUTE } },
79*4882a593Smuzhiyun 	{ KE_KEY,    0x285, { KEY_VOLUMEDOWN } },
80*4882a593Smuzhiyun 	{ KE_KEY,    0x286, { KEY_VOLUMEUP } },
81*4882a593Smuzhiyun 	{ KE_KEY,    0x287, { KEY_MICMUTE } },
82*4882a593Smuzhiyun 	{ KE_KEY,    0x289, { KEY_WLAN } },
83*4882a593Smuzhiyun 	// Huawei |M| key
84*4882a593Smuzhiyun 	{ KE_KEY,    0x28a, { KEY_CONFIG } },
85*4882a593Smuzhiyun 	// Keyboard backlit
86*4882a593Smuzhiyun 	{ KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
87*4882a593Smuzhiyun 	{ KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
88*4882a593Smuzhiyun 	{ KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
89*4882a593Smuzhiyun 	{ KE_END,	 0 }
90*4882a593Smuzhiyun };
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun static int battery_reset = -1;
93*4882a593Smuzhiyun static int report_brightness = -1;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun module_param(battery_reset, bint, 0444);
96*4882a593Smuzhiyun MODULE_PARM_DESC(battery_reset,
97*4882a593Smuzhiyun 		"Reset battery charge values to (0-0) before disabling it using (0-100)");
98*4882a593Smuzhiyun module_param(report_brightness, bint, 0444);
99*4882a593Smuzhiyun MODULE_PARM_DESC(report_brightness,
100*4882a593Smuzhiyun 		"Report brightness keys.");
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun /* Quirks */
103*4882a593Smuzhiyun 
dmi_matched(const struct dmi_system_id * dmi)104*4882a593Smuzhiyun static int __init dmi_matched(const struct dmi_system_id *dmi)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun 	quirks = dmi->driver_data;
107*4882a593Smuzhiyun 	return 1;
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun static struct quirk_entry quirk_unknown = {
111*4882a593Smuzhiyun };
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun static struct quirk_entry quirk_battery_reset = {
114*4882a593Smuzhiyun 	.battery_reset = true,
115*4882a593Smuzhiyun };
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun static struct quirk_entry quirk_matebook_x = {
118*4882a593Smuzhiyun 	.ec_micmute = true,
119*4882a593Smuzhiyun 	.report_brightness = true,
120*4882a593Smuzhiyun };
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun static const struct dmi_system_id huawei_quirks[] = {
123*4882a593Smuzhiyun 	{
124*4882a593Smuzhiyun 		.callback = dmi_matched,
125*4882a593Smuzhiyun 		.ident = "Huawei MACH-WX9",
126*4882a593Smuzhiyun 		.matches = {
127*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
128*4882a593Smuzhiyun 			DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"),
129*4882a593Smuzhiyun 		},
130*4882a593Smuzhiyun 		.driver_data = &quirk_battery_reset
131*4882a593Smuzhiyun 	},
132*4882a593Smuzhiyun 	{
133*4882a593Smuzhiyun 		.callback = dmi_matched,
134*4882a593Smuzhiyun 		.ident = "Huawei MateBook X",
135*4882a593Smuzhiyun 		.matches = {
136*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
137*4882a593Smuzhiyun 			DMI_MATCH(DMI_PRODUCT_NAME, "HUAWEI MateBook X")
138*4882a593Smuzhiyun 		},
139*4882a593Smuzhiyun 		.driver_data = &quirk_matebook_x
140*4882a593Smuzhiyun 	},
141*4882a593Smuzhiyun 	{  }
142*4882a593Smuzhiyun };
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun /* Utils */
145*4882a593Smuzhiyun 
huawei_wmi_call(struct huawei_wmi * huawei,struct acpi_buffer * in,struct acpi_buffer * out)146*4882a593Smuzhiyun static int huawei_wmi_call(struct huawei_wmi *huawei,
147*4882a593Smuzhiyun 			   struct acpi_buffer *in, struct acpi_buffer *out)
148*4882a593Smuzhiyun {
149*4882a593Smuzhiyun 	acpi_status status;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	mutex_lock(&huawei->wmi_lock);
152*4882a593Smuzhiyun 	status = wmi_evaluate_method(HWMI_METHOD_GUID, 0, 1, in, out);
153*4882a593Smuzhiyun 	mutex_unlock(&huawei->wmi_lock);
154*4882a593Smuzhiyun 	if (ACPI_FAILURE(status)) {
155*4882a593Smuzhiyun 		dev_err(huawei->dev, "Failed to evaluate wmi method\n");
156*4882a593Smuzhiyun 		return -ENODEV;
157*4882a593Smuzhiyun 	}
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	return 0;
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun /* HWMI takes a 64 bit input and returns either a package with 2 buffers, one of
163*4882a593Smuzhiyun  * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
164*4882a593Smuzhiyun  * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got a
165*4882a593Smuzhiyun  * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
166*4882a593Smuzhiyun  * the remaining 0x100 sized buffer has the return status of every call. In case
167*4882a593Smuzhiyun  * the return status is non-zero, we return -ENODEV but still copy the returned
168*4882a593Smuzhiyun  * buffer to the given buffer parameter (buf).
169*4882a593Smuzhiyun  */
huawei_wmi_cmd(u64 arg,u8 * buf,size_t buflen)170*4882a593Smuzhiyun static int huawei_wmi_cmd(u64 arg, u8 *buf, size_t buflen)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun 	struct huawei_wmi *huawei = huawei_wmi;
173*4882a593Smuzhiyun 	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
174*4882a593Smuzhiyun 	struct acpi_buffer in;
175*4882a593Smuzhiyun 	union acpi_object *obj;
176*4882a593Smuzhiyun 	size_t len;
177*4882a593Smuzhiyun 	int err, i;
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	in.length = sizeof(arg);
180*4882a593Smuzhiyun 	in.pointer = &arg;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	/* Some models require calling HWMI twice to execute a command. We evaluate
183*4882a593Smuzhiyun 	 * HWMI and if we get a non-zero return status we evaluate it again.
184*4882a593Smuzhiyun 	 */
185*4882a593Smuzhiyun 	for (i = 0; i < 2; i++) {
186*4882a593Smuzhiyun 		err = huawei_wmi_call(huawei, &in, &out);
187*4882a593Smuzhiyun 		if (err)
188*4882a593Smuzhiyun 			goto fail_cmd;
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 		obj = out.pointer;
191*4882a593Smuzhiyun 		if (!obj) {
192*4882a593Smuzhiyun 			err = -EIO;
193*4882a593Smuzhiyun 			goto fail_cmd;
194*4882a593Smuzhiyun 		}
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 		switch (obj->type) {
197*4882a593Smuzhiyun 		/* Models that implement both "legacy" and HWMI tend to return a 0x104
198*4882a593Smuzhiyun 		 * sized buffer instead of a package of 0x4 and 0x100 buffers.
199*4882a593Smuzhiyun 		 */
200*4882a593Smuzhiyun 		case ACPI_TYPE_BUFFER:
201*4882a593Smuzhiyun 			if (obj->buffer.length == 0x104) {
202*4882a593Smuzhiyun 				// Skip the first 4 bytes.
203*4882a593Smuzhiyun 				obj->buffer.pointer += 4;
204*4882a593Smuzhiyun 				len = 0x100;
205*4882a593Smuzhiyun 			} else {
206*4882a593Smuzhiyun 				dev_err(huawei->dev, "Bad buffer length, got %d\n", obj->buffer.length);
207*4882a593Smuzhiyun 				err = -EIO;
208*4882a593Smuzhiyun 				goto fail_cmd;
209*4882a593Smuzhiyun 			}
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 			break;
212*4882a593Smuzhiyun 		/* HWMI returns a package with 2 buffer elements, one of 4 bytes and the
213*4882a593Smuzhiyun 		 * other is 256 bytes.
214*4882a593Smuzhiyun 		 */
215*4882a593Smuzhiyun 		case ACPI_TYPE_PACKAGE:
216*4882a593Smuzhiyun 			if (obj->package.count != 2) {
217*4882a593Smuzhiyun 				dev_err(huawei->dev, "Bad package count, got %d\n", obj->package.count);
218*4882a593Smuzhiyun 				err = -EIO;
219*4882a593Smuzhiyun 				goto fail_cmd;
220*4882a593Smuzhiyun 			}
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 			obj = &obj->package.elements[1];
223*4882a593Smuzhiyun 			if (obj->type != ACPI_TYPE_BUFFER) {
224*4882a593Smuzhiyun 				dev_err(huawei->dev, "Bad package element type, got %d\n", obj->type);
225*4882a593Smuzhiyun 				err = -EIO;
226*4882a593Smuzhiyun 				goto fail_cmd;
227*4882a593Smuzhiyun 			}
228*4882a593Smuzhiyun 			len = obj->buffer.length;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 			break;
231*4882a593Smuzhiyun 		/* Shouldn't get here! */
232*4882a593Smuzhiyun 		default:
233*4882a593Smuzhiyun 			dev_err(huawei->dev, "Unexpected obj type, got: %d\n", obj->type);
234*4882a593Smuzhiyun 			err = -EIO;
235*4882a593Smuzhiyun 			goto fail_cmd;
236*4882a593Smuzhiyun 		}
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 		if (!*obj->buffer.pointer)
239*4882a593Smuzhiyun 			break;
240*4882a593Smuzhiyun 	}
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	err = (*obj->buffer.pointer) ? -ENODEV : 0;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	if (buf) {
245*4882a593Smuzhiyun 		len = min(buflen, len);
246*4882a593Smuzhiyun 		memcpy(buf, obj->buffer.pointer, len);
247*4882a593Smuzhiyun 	}
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun fail_cmd:
250*4882a593Smuzhiyun 	kfree(out.pointer);
251*4882a593Smuzhiyun 	return err;
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun /* LEDs */
255*4882a593Smuzhiyun 
huawei_wmi_micmute_led_set(struct led_classdev * led_cdev,enum led_brightness brightness)256*4882a593Smuzhiyun static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
257*4882a593Smuzhiyun 		enum led_brightness brightness)
258*4882a593Smuzhiyun {
259*4882a593Smuzhiyun 	/* This is a workaround until the "legacy" interface is implemented. */
260*4882a593Smuzhiyun 	if (quirks && quirks->ec_micmute) {
261*4882a593Smuzhiyun 		char *acpi_method;
262*4882a593Smuzhiyun 		acpi_handle handle;
263*4882a593Smuzhiyun 		acpi_status status;
264*4882a593Smuzhiyun 		union acpi_object args[3];
265*4882a593Smuzhiyun 		struct acpi_object_list arg_list = {
266*4882a593Smuzhiyun 			.pointer = args,
267*4882a593Smuzhiyun 			.count = ARRAY_SIZE(args),
268*4882a593Smuzhiyun 		};
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 		handle = ec_get_handle();
271*4882a593Smuzhiyun 		if (!handle)
272*4882a593Smuzhiyun 			return -ENODEV;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 		args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
275*4882a593Smuzhiyun 		args[1].integer.value = 0x04;
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 		if (acpi_has_method(handle, "SPIN")) {
278*4882a593Smuzhiyun 			acpi_method = "SPIN";
279*4882a593Smuzhiyun 			args[0].integer.value = 0;
280*4882a593Smuzhiyun 			args[2].integer.value = brightness ? 1 : 0;
281*4882a593Smuzhiyun 		} else if (acpi_has_method(handle, "WPIN")) {
282*4882a593Smuzhiyun 			acpi_method = "WPIN";
283*4882a593Smuzhiyun 			args[0].integer.value = 1;
284*4882a593Smuzhiyun 			args[2].integer.value = brightness ? 0 : 1;
285*4882a593Smuzhiyun 		} else {
286*4882a593Smuzhiyun 			return -ENODEV;
287*4882a593Smuzhiyun 		}
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 		status = acpi_evaluate_object(handle, acpi_method, &arg_list, NULL);
290*4882a593Smuzhiyun 		if (ACPI_FAILURE(status))
291*4882a593Smuzhiyun 			return -ENODEV;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 		return 0;
294*4882a593Smuzhiyun 	} else {
295*4882a593Smuzhiyun 		union hwmi_arg arg;
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 		arg.cmd = MICMUTE_LED_SET;
298*4882a593Smuzhiyun 		arg.args[2] = brightness;
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 		return huawei_wmi_cmd(arg.cmd, NULL, 0);
301*4882a593Smuzhiyun 	}
302*4882a593Smuzhiyun }
303*4882a593Smuzhiyun 
huawei_wmi_leds_setup(struct device * dev)304*4882a593Smuzhiyun static void huawei_wmi_leds_setup(struct device *dev)
305*4882a593Smuzhiyun {
306*4882a593Smuzhiyun 	struct huawei_wmi *huawei = dev_get_drvdata(dev);
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	huawei->cdev.name = "platform::micmute";
309*4882a593Smuzhiyun 	huawei->cdev.max_brightness = 1;
310*4882a593Smuzhiyun 	huawei->cdev.brightness_set_blocking = &huawei_wmi_micmute_led_set;
311*4882a593Smuzhiyun 	huawei->cdev.default_trigger = "audio-micmute";
312*4882a593Smuzhiyun 	huawei->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
313*4882a593Smuzhiyun 	huawei->cdev.dev = dev;
314*4882a593Smuzhiyun 	huawei->cdev.flags = LED_CORE_SUSPENDRESUME;
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	devm_led_classdev_register(dev, &huawei->cdev);
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun /* Battery protection */
320*4882a593Smuzhiyun 
huawei_wmi_battery_get(int * start,int * end)321*4882a593Smuzhiyun static int huawei_wmi_battery_get(int *start, int *end)
322*4882a593Smuzhiyun {
323*4882a593Smuzhiyun 	u8 ret[0x100];
324*4882a593Smuzhiyun 	int err, i;
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	err = huawei_wmi_cmd(BATTERY_THRESH_GET, ret, 0x100);
327*4882a593Smuzhiyun 	if (err)
328*4882a593Smuzhiyun 		return err;
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	/* Find the last two non-zero values. Return status is ignored. */
331*4882a593Smuzhiyun 	i = 0xff;
332*4882a593Smuzhiyun 	do {
333*4882a593Smuzhiyun 		if (start)
334*4882a593Smuzhiyun 			*start = ret[i-1];
335*4882a593Smuzhiyun 		if (end)
336*4882a593Smuzhiyun 			*end = ret[i];
337*4882a593Smuzhiyun 	} while (i > 2 && !ret[i--]);
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	return 0;
340*4882a593Smuzhiyun }
341*4882a593Smuzhiyun 
huawei_wmi_battery_set(int start,int end)342*4882a593Smuzhiyun static int huawei_wmi_battery_set(int start, int end)
343*4882a593Smuzhiyun {
344*4882a593Smuzhiyun 	union hwmi_arg arg;
345*4882a593Smuzhiyun 	int err;
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	if (start < 0 || end < 0 || start > 100 || end > 100)
348*4882a593Smuzhiyun 		return -EINVAL;
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun 	arg.cmd = BATTERY_THRESH_SET;
351*4882a593Smuzhiyun 	arg.args[2] = start;
352*4882a593Smuzhiyun 	arg.args[3] = end;
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 	/* This is an edge case were some models turn battery protection
355*4882a593Smuzhiyun 	 * off without changing their thresholds values. We clear the
356*4882a593Smuzhiyun 	 * values before turning off protection. Sometimes we need a sleep delay to
357*4882a593Smuzhiyun 	 * make sure these values make their way to EC memory.
358*4882a593Smuzhiyun 	 */
359*4882a593Smuzhiyun 	if (quirks && quirks->battery_reset && start == 0 && end == 100) {
360*4882a593Smuzhiyun 		err = huawei_wmi_battery_set(0, 0);
361*4882a593Smuzhiyun 		if (err)
362*4882a593Smuzhiyun 			return err;
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 		msleep(1000);
365*4882a593Smuzhiyun 	}
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	err = huawei_wmi_cmd(arg.cmd, NULL, 0);
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	return err;
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun 
charge_control_start_threshold_show(struct device * dev,struct device_attribute * attr,char * buf)372*4882a593Smuzhiyun static ssize_t charge_control_start_threshold_show(struct device *dev,
373*4882a593Smuzhiyun 		struct device_attribute *attr,
374*4882a593Smuzhiyun 		char *buf)
375*4882a593Smuzhiyun {
376*4882a593Smuzhiyun 	int err, start;
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	err = huawei_wmi_battery_get(&start, NULL);
379*4882a593Smuzhiyun 	if (err)
380*4882a593Smuzhiyun 		return err;
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	return sprintf(buf, "%d\n", start);
383*4882a593Smuzhiyun }
384*4882a593Smuzhiyun 
charge_control_end_threshold_show(struct device * dev,struct device_attribute * attr,char * buf)385*4882a593Smuzhiyun static ssize_t charge_control_end_threshold_show(struct device *dev,
386*4882a593Smuzhiyun 		struct device_attribute *attr,
387*4882a593Smuzhiyun 		char *buf)
388*4882a593Smuzhiyun {
389*4882a593Smuzhiyun 	int err, end;
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	err = huawei_wmi_battery_get(NULL, &end);
392*4882a593Smuzhiyun 	if (err)
393*4882a593Smuzhiyun 		return err;
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	return sprintf(buf, "%d\n", end);
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun 
charge_control_thresholds_show(struct device * dev,struct device_attribute * attr,char * buf)398*4882a593Smuzhiyun static ssize_t charge_control_thresholds_show(struct device *dev,
399*4882a593Smuzhiyun 		struct device_attribute *attr,
400*4882a593Smuzhiyun 		char *buf)
401*4882a593Smuzhiyun {
402*4882a593Smuzhiyun 	int err, start, end;
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	err = huawei_wmi_battery_get(&start, &end);
405*4882a593Smuzhiyun 	if (err)
406*4882a593Smuzhiyun 		return err;
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	return sprintf(buf, "%d %d\n", start, end);
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun 
charge_control_start_threshold_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)411*4882a593Smuzhiyun static ssize_t charge_control_start_threshold_store(struct device *dev,
412*4882a593Smuzhiyun 		struct device_attribute *attr,
413*4882a593Smuzhiyun 		const char *buf, size_t size)
414*4882a593Smuzhiyun {
415*4882a593Smuzhiyun 	int err, start, end;
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	err = huawei_wmi_battery_get(NULL, &end);
418*4882a593Smuzhiyun 	if (err)
419*4882a593Smuzhiyun 		return err;
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 	if (sscanf(buf, "%d", &start) != 1)
422*4882a593Smuzhiyun 		return -EINVAL;
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	err = huawei_wmi_battery_set(start, end);
425*4882a593Smuzhiyun 	if (err)
426*4882a593Smuzhiyun 		return err;
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	return size;
429*4882a593Smuzhiyun }
430*4882a593Smuzhiyun 
charge_control_end_threshold_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)431*4882a593Smuzhiyun static ssize_t charge_control_end_threshold_store(struct device *dev,
432*4882a593Smuzhiyun 		struct device_attribute *attr,
433*4882a593Smuzhiyun 		const char *buf, size_t size)
434*4882a593Smuzhiyun {
435*4882a593Smuzhiyun 	int err, start, end;
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	err = huawei_wmi_battery_get(&start, NULL);
438*4882a593Smuzhiyun 	if (err)
439*4882a593Smuzhiyun 		return err;
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	if (sscanf(buf, "%d", &end) != 1)
442*4882a593Smuzhiyun 		return -EINVAL;
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 	err = huawei_wmi_battery_set(start, end);
445*4882a593Smuzhiyun 	if (err)
446*4882a593Smuzhiyun 		return err;
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	return size;
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun 
charge_control_thresholds_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)451*4882a593Smuzhiyun static ssize_t charge_control_thresholds_store(struct device *dev,
452*4882a593Smuzhiyun 		struct device_attribute *attr,
453*4882a593Smuzhiyun 		const char *buf, size_t size)
454*4882a593Smuzhiyun {
455*4882a593Smuzhiyun 	int err, start, end;
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	if (sscanf(buf, "%d %d", &start, &end) != 2)
458*4882a593Smuzhiyun 		return -EINVAL;
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	err = huawei_wmi_battery_set(start, end);
461*4882a593Smuzhiyun 	if (err)
462*4882a593Smuzhiyun 		return err;
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun 	return size;
465*4882a593Smuzhiyun }
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun static DEVICE_ATTR_RW(charge_control_start_threshold);
468*4882a593Smuzhiyun static DEVICE_ATTR_RW(charge_control_end_threshold);
469*4882a593Smuzhiyun static DEVICE_ATTR_RW(charge_control_thresholds);
470*4882a593Smuzhiyun 
huawei_wmi_battery_add(struct power_supply * battery)471*4882a593Smuzhiyun static int huawei_wmi_battery_add(struct power_supply *battery)
472*4882a593Smuzhiyun {
473*4882a593Smuzhiyun 	int err = 0;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	err = device_create_file(&battery->dev, &dev_attr_charge_control_start_threshold);
476*4882a593Smuzhiyun 	if (err)
477*4882a593Smuzhiyun 		return err;
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun 	err = device_create_file(&battery->dev, &dev_attr_charge_control_end_threshold);
480*4882a593Smuzhiyun 	if (err)
481*4882a593Smuzhiyun 		device_remove_file(&battery->dev, &dev_attr_charge_control_start_threshold);
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 	return err;
484*4882a593Smuzhiyun }
485*4882a593Smuzhiyun 
huawei_wmi_battery_remove(struct power_supply * battery)486*4882a593Smuzhiyun static int huawei_wmi_battery_remove(struct power_supply *battery)
487*4882a593Smuzhiyun {
488*4882a593Smuzhiyun 	device_remove_file(&battery->dev, &dev_attr_charge_control_start_threshold);
489*4882a593Smuzhiyun 	device_remove_file(&battery->dev, &dev_attr_charge_control_end_threshold);
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	return 0;
492*4882a593Smuzhiyun }
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun static struct acpi_battery_hook huawei_wmi_battery_hook = {
495*4882a593Smuzhiyun 	.add_battery = huawei_wmi_battery_add,
496*4882a593Smuzhiyun 	.remove_battery = huawei_wmi_battery_remove,
497*4882a593Smuzhiyun 	.name = "Huawei Battery Extension"
498*4882a593Smuzhiyun };
499*4882a593Smuzhiyun 
huawei_wmi_battery_setup(struct device * dev)500*4882a593Smuzhiyun static void huawei_wmi_battery_setup(struct device *dev)
501*4882a593Smuzhiyun {
502*4882a593Smuzhiyun 	struct huawei_wmi *huawei = dev_get_drvdata(dev);
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 	huawei->battery_available = true;
505*4882a593Smuzhiyun 	if (huawei_wmi_battery_get(NULL, NULL)) {
506*4882a593Smuzhiyun 		huawei->battery_available = false;
507*4882a593Smuzhiyun 		return;
508*4882a593Smuzhiyun 	}
509*4882a593Smuzhiyun 
510*4882a593Smuzhiyun 	battery_hook_register(&huawei_wmi_battery_hook);
511*4882a593Smuzhiyun 	device_create_file(dev, &dev_attr_charge_control_thresholds);
512*4882a593Smuzhiyun }
513*4882a593Smuzhiyun 
huawei_wmi_battery_exit(struct device * dev)514*4882a593Smuzhiyun static void huawei_wmi_battery_exit(struct device *dev)
515*4882a593Smuzhiyun {
516*4882a593Smuzhiyun 	struct huawei_wmi *huawei = dev_get_drvdata(dev);
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun 	if (huawei->battery_available) {
519*4882a593Smuzhiyun 		battery_hook_unregister(&huawei_wmi_battery_hook);
520*4882a593Smuzhiyun 		device_remove_file(dev, &dev_attr_charge_control_thresholds);
521*4882a593Smuzhiyun 	}
522*4882a593Smuzhiyun }
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun /* Fn lock */
525*4882a593Smuzhiyun 
huawei_wmi_fn_lock_get(int * on)526*4882a593Smuzhiyun static int huawei_wmi_fn_lock_get(int *on)
527*4882a593Smuzhiyun {
528*4882a593Smuzhiyun 	u8 ret[0x100] = { 0 };
529*4882a593Smuzhiyun 	int err, i;
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun 	err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
532*4882a593Smuzhiyun 	if (err)
533*4882a593Smuzhiyun 		return err;
534*4882a593Smuzhiyun 
535*4882a593Smuzhiyun 	/* Find the first non-zero value. Return status is ignored. */
536*4882a593Smuzhiyun 	i = 1;
537*4882a593Smuzhiyun 	do {
538*4882a593Smuzhiyun 		if (on)
539*4882a593Smuzhiyun 			*on = ret[i] - 1; // -1 undefined, 0 off, 1 on.
540*4882a593Smuzhiyun 	} while (i < 0xff && !ret[i++]);
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	return 0;
543*4882a593Smuzhiyun }
544*4882a593Smuzhiyun 
huawei_wmi_fn_lock_set(int on)545*4882a593Smuzhiyun static int huawei_wmi_fn_lock_set(int on)
546*4882a593Smuzhiyun {
547*4882a593Smuzhiyun 	union hwmi_arg arg;
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun 	arg.cmd = FN_LOCK_SET;
550*4882a593Smuzhiyun 	arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	return huawei_wmi_cmd(arg.cmd, NULL, 0);
553*4882a593Smuzhiyun }
554*4882a593Smuzhiyun 
fn_lock_state_show(struct device * dev,struct device_attribute * attr,char * buf)555*4882a593Smuzhiyun static ssize_t fn_lock_state_show(struct device *dev,
556*4882a593Smuzhiyun 		struct device_attribute *attr,
557*4882a593Smuzhiyun 		char *buf)
558*4882a593Smuzhiyun {
559*4882a593Smuzhiyun 	int err, on;
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	err = huawei_wmi_fn_lock_get(&on);
562*4882a593Smuzhiyun 	if (err)
563*4882a593Smuzhiyun 		return err;
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun 	return sprintf(buf, "%d\n", on);
566*4882a593Smuzhiyun }
567*4882a593Smuzhiyun 
fn_lock_state_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)568*4882a593Smuzhiyun static ssize_t fn_lock_state_store(struct device *dev,
569*4882a593Smuzhiyun 		struct device_attribute *attr,
570*4882a593Smuzhiyun 		const char *buf, size_t size)
571*4882a593Smuzhiyun {
572*4882a593Smuzhiyun 	int on, err;
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	if (kstrtoint(buf, 10, &on) ||
575*4882a593Smuzhiyun 			on < 0 || on > 1)
576*4882a593Smuzhiyun 		return -EINVAL;
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 	err = huawei_wmi_fn_lock_set(on);
579*4882a593Smuzhiyun 	if (err)
580*4882a593Smuzhiyun 		return err;
581*4882a593Smuzhiyun 
582*4882a593Smuzhiyun 	return size;
583*4882a593Smuzhiyun }
584*4882a593Smuzhiyun 
585*4882a593Smuzhiyun static DEVICE_ATTR_RW(fn_lock_state);
586*4882a593Smuzhiyun 
huawei_wmi_fn_lock_setup(struct device * dev)587*4882a593Smuzhiyun static void huawei_wmi_fn_lock_setup(struct device *dev)
588*4882a593Smuzhiyun {
589*4882a593Smuzhiyun 	struct huawei_wmi *huawei = dev_get_drvdata(dev);
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 	huawei->fn_lock_available = true;
592*4882a593Smuzhiyun 	if (huawei_wmi_fn_lock_get(NULL)) {
593*4882a593Smuzhiyun 		huawei->fn_lock_available = false;
594*4882a593Smuzhiyun 		return;
595*4882a593Smuzhiyun 	}
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 	device_create_file(dev, &dev_attr_fn_lock_state);
598*4882a593Smuzhiyun }
599*4882a593Smuzhiyun 
huawei_wmi_fn_lock_exit(struct device * dev)600*4882a593Smuzhiyun static void huawei_wmi_fn_lock_exit(struct device *dev)
601*4882a593Smuzhiyun {
602*4882a593Smuzhiyun 	struct huawei_wmi *huawei = dev_get_drvdata(dev);
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun 	if (huawei->fn_lock_available)
605*4882a593Smuzhiyun 		device_remove_file(dev, &dev_attr_fn_lock_state);
606*4882a593Smuzhiyun }
607*4882a593Smuzhiyun 
608*4882a593Smuzhiyun /* debugfs */
609*4882a593Smuzhiyun 
huawei_wmi_debugfs_call_dump(struct seq_file * m,void * data,union acpi_object * obj)610*4882a593Smuzhiyun static void huawei_wmi_debugfs_call_dump(struct seq_file *m, void *data,
611*4882a593Smuzhiyun 		union acpi_object *obj)
612*4882a593Smuzhiyun {
613*4882a593Smuzhiyun 	struct huawei_wmi *huawei = m->private;
614*4882a593Smuzhiyun 	int i;
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun 	switch (obj->type) {
617*4882a593Smuzhiyun 	case ACPI_TYPE_INTEGER:
618*4882a593Smuzhiyun 		seq_printf(m, "0x%llx", obj->integer.value);
619*4882a593Smuzhiyun 		break;
620*4882a593Smuzhiyun 	case ACPI_TYPE_STRING:
621*4882a593Smuzhiyun 		seq_printf(m, "\"%.*s\"", obj->string.length, obj->string.pointer);
622*4882a593Smuzhiyun 		break;
623*4882a593Smuzhiyun 	case ACPI_TYPE_BUFFER:
624*4882a593Smuzhiyun 		seq_puts(m, "{");
625*4882a593Smuzhiyun 		for (i = 0; i < obj->buffer.length; i++) {
626*4882a593Smuzhiyun 			seq_printf(m, "0x%02x", obj->buffer.pointer[i]);
627*4882a593Smuzhiyun 			if (i < obj->buffer.length - 1)
628*4882a593Smuzhiyun 				seq_puts(m, ",");
629*4882a593Smuzhiyun 		}
630*4882a593Smuzhiyun 		seq_puts(m, "}");
631*4882a593Smuzhiyun 		break;
632*4882a593Smuzhiyun 	case ACPI_TYPE_PACKAGE:
633*4882a593Smuzhiyun 		seq_puts(m, "[");
634*4882a593Smuzhiyun 		for (i = 0; i < obj->package.count; i++) {
635*4882a593Smuzhiyun 			huawei_wmi_debugfs_call_dump(m, huawei, &obj->package.elements[i]);
636*4882a593Smuzhiyun 			if (i < obj->package.count - 1)
637*4882a593Smuzhiyun 				seq_puts(m, ",");
638*4882a593Smuzhiyun 		}
639*4882a593Smuzhiyun 		seq_puts(m, "]");
640*4882a593Smuzhiyun 		break;
641*4882a593Smuzhiyun 	default:
642*4882a593Smuzhiyun 		dev_err(huawei->dev, "Unexpected obj type, got %d\n", obj->type);
643*4882a593Smuzhiyun 		return;
644*4882a593Smuzhiyun 	}
645*4882a593Smuzhiyun }
646*4882a593Smuzhiyun 
huawei_wmi_debugfs_call_show(struct seq_file * m,void * data)647*4882a593Smuzhiyun static int huawei_wmi_debugfs_call_show(struct seq_file *m, void *data)
648*4882a593Smuzhiyun {
649*4882a593Smuzhiyun 	struct huawei_wmi *huawei = m->private;
650*4882a593Smuzhiyun 	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
651*4882a593Smuzhiyun 	struct acpi_buffer in;
652*4882a593Smuzhiyun 	union acpi_object *obj;
653*4882a593Smuzhiyun 	int err;
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun 	in.length = sizeof(u64);
656*4882a593Smuzhiyun 	in.pointer = &huawei->debug.arg;
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun 	err = huawei_wmi_call(huawei, &in, &out);
659*4882a593Smuzhiyun 	if (err)
660*4882a593Smuzhiyun 		return err;
661*4882a593Smuzhiyun 
662*4882a593Smuzhiyun 	obj = out.pointer;
663*4882a593Smuzhiyun 	if (!obj) {
664*4882a593Smuzhiyun 		err = -EIO;
665*4882a593Smuzhiyun 		goto fail_debugfs_call;
666*4882a593Smuzhiyun 	}
667*4882a593Smuzhiyun 
668*4882a593Smuzhiyun 	huawei_wmi_debugfs_call_dump(m, huawei, obj);
669*4882a593Smuzhiyun 
670*4882a593Smuzhiyun fail_debugfs_call:
671*4882a593Smuzhiyun 	kfree(out.pointer);
672*4882a593Smuzhiyun 	return err;
673*4882a593Smuzhiyun }
674*4882a593Smuzhiyun 
675*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call);
676*4882a593Smuzhiyun 
huawei_wmi_debugfs_setup(struct device * dev)677*4882a593Smuzhiyun static void huawei_wmi_debugfs_setup(struct device *dev)
678*4882a593Smuzhiyun {
679*4882a593Smuzhiyun 	struct huawei_wmi *huawei = dev_get_drvdata(dev);
680*4882a593Smuzhiyun 
681*4882a593Smuzhiyun 	huawei->debug.root = debugfs_create_dir("huawei-wmi", NULL);
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun 	debugfs_create_x64("arg", 0644, huawei->debug.root,
684*4882a593Smuzhiyun 		&huawei->debug.arg);
685*4882a593Smuzhiyun 	debugfs_create_file("call", 0400,
686*4882a593Smuzhiyun 		huawei->debug.root, huawei, &huawei_wmi_debugfs_call_fops);
687*4882a593Smuzhiyun }
688*4882a593Smuzhiyun 
huawei_wmi_debugfs_exit(struct device * dev)689*4882a593Smuzhiyun static void huawei_wmi_debugfs_exit(struct device *dev)
690*4882a593Smuzhiyun {
691*4882a593Smuzhiyun 	struct huawei_wmi *huawei = dev_get_drvdata(dev);
692*4882a593Smuzhiyun 
693*4882a593Smuzhiyun 	debugfs_remove_recursive(huawei->debug.root);
694*4882a593Smuzhiyun }
695*4882a593Smuzhiyun 
696*4882a593Smuzhiyun /* Input */
697*4882a593Smuzhiyun 
huawei_wmi_process_key(struct input_dev * idev,int code)698*4882a593Smuzhiyun static void huawei_wmi_process_key(struct input_dev *idev, int code)
699*4882a593Smuzhiyun {
700*4882a593Smuzhiyun 	const struct key_entry *key;
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun 	/*
703*4882a593Smuzhiyun 	 * WMI0 uses code 0x80 to indicate a hotkey event.
704*4882a593Smuzhiyun 	 * The actual key is fetched from the method WQ00
705*4882a593Smuzhiyun 	 * using WMI0_EXPENSIVE_GUID.
706*4882a593Smuzhiyun 	 */
707*4882a593Smuzhiyun 	if (code == 0x80) {
708*4882a593Smuzhiyun 		struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
709*4882a593Smuzhiyun 		union acpi_object *obj;
710*4882a593Smuzhiyun 		acpi_status status;
711*4882a593Smuzhiyun 
712*4882a593Smuzhiyun 		status = wmi_query_block(WMI0_EXPENSIVE_GUID, 0, &response);
713*4882a593Smuzhiyun 		if (ACPI_FAILURE(status))
714*4882a593Smuzhiyun 			return;
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun 		obj = (union acpi_object *)response.pointer;
717*4882a593Smuzhiyun 		if (obj && obj->type == ACPI_TYPE_INTEGER)
718*4882a593Smuzhiyun 			code = obj->integer.value;
719*4882a593Smuzhiyun 
720*4882a593Smuzhiyun 		kfree(response.pointer);
721*4882a593Smuzhiyun 	}
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun 	key = sparse_keymap_entry_from_scancode(idev, code);
724*4882a593Smuzhiyun 	if (!key) {
725*4882a593Smuzhiyun 		dev_info(&idev->dev, "Unknown key pressed, code: 0x%04x\n", code);
726*4882a593Smuzhiyun 		return;
727*4882a593Smuzhiyun 	}
728*4882a593Smuzhiyun 
729*4882a593Smuzhiyun 	if (quirks && !quirks->report_brightness &&
730*4882a593Smuzhiyun 			(key->sw.code == KEY_BRIGHTNESSDOWN ||
731*4882a593Smuzhiyun 			key->sw.code == KEY_BRIGHTNESSUP))
732*4882a593Smuzhiyun 		return;
733*4882a593Smuzhiyun 
734*4882a593Smuzhiyun 	sparse_keymap_report_entry(idev, key, 1, true);
735*4882a593Smuzhiyun }
736*4882a593Smuzhiyun 
huawei_wmi_input_notify(u32 value,void * context)737*4882a593Smuzhiyun static void huawei_wmi_input_notify(u32 value, void *context)
738*4882a593Smuzhiyun {
739*4882a593Smuzhiyun 	struct input_dev *idev = (struct input_dev *)context;
740*4882a593Smuzhiyun 	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
741*4882a593Smuzhiyun 	union acpi_object *obj;
742*4882a593Smuzhiyun 	acpi_status status;
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun 	status = wmi_get_event_data(value, &response);
745*4882a593Smuzhiyun 	if (ACPI_FAILURE(status)) {
746*4882a593Smuzhiyun 		dev_err(&idev->dev, "Unable to get event data\n");
747*4882a593Smuzhiyun 		return;
748*4882a593Smuzhiyun 	}
749*4882a593Smuzhiyun 
750*4882a593Smuzhiyun 	obj = (union acpi_object *)response.pointer;
751*4882a593Smuzhiyun 	if (obj && obj->type == ACPI_TYPE_INTEGER)
752*4882a593Smuzhiyun 		huawei_wmi_process_key(idev, obj->integer.value);
753*4882a593Smuzhiyun 	else
754*4882a593Smuzhiyun 		dev_err(&idev->dev, "Bad response type\n");
755*4882a593Smuzhiyun 
756*4882a593Smuzhiyun 	kfree(response.pointer);
757*4882a593Smuzhiyun }
758*4882a593Smuzhiyun 
huawei_wmi_input_setup(struct device * dev,const char * guid,struct input_dev ** idev)759*4882a593Smuzhiyun static int huawei_wmi_input_setup(struct device *dev,
760*4882a593Smuzhiyun 		const char *guid,
761*4882a593Smuzhiyun 		struct input_dev **idev)
762*4882a593Smuzhiyun {
763*4882a593Smuzhiyun 	*idev = devm_input_allocate_device(dev);
764*4882a593Smuzhiyun 	if (!*idev)
765*4882a593Smuzhiyun 		return -ENOMEM;
766*4882a593Smuzhiyun 
767*4882a593Smuzhiyun 	(*idev)->name = "Huawei WMI hotkeys";
768*4882a593Smuzhiyun 	(*idev)->phys = "wmi/input0";
769*4882a593Smuzhiyun 	(*idev)->id.bustype = BUS_HOST;
770*4882a593Smuzhiyun 	(*idev)->dev.parent = dev;
771*4882a593Smuzhiyun 
772*4882a593Smuzhiyun 	return sparse_keymap_setup(*idev, huawei_wmi_keymap, NULL) ||
773*4882a593Smuzhiyun 		input_register_device(*idev) ||
774*4882a593Smuzhiyun 		wmi_install_notify_handler(guid, huawei_wmi_input_notify,
775*4882a593Smuzhiyun 				*idev);
776*4882a593Smuzhiyun }
777*4882a593Smuzhiyun 
huawei_wmi_input_exit(struct device * dev,const char * guid)778*4882a593Smuzhiyun static void huawei_wmi_input_exit(struct device *dev, const char *guid)
779*4882a593Smuzhiyun {
780*4882a593Smuzhiyun 	wmi_remove_notify_handler(guid);
781*4882a593Smuzhiyun }
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun /* Huawei driver */
784*4882a593Smuzhiyun 
785*4882a593Smuzhiyun static const struct wmi_device_id huawei_wmi_events_id_table[] = {
786*4882a593Smuzhiyun 	{ .guid_string = WMI0_EVENT_GUID },
787*4882a593Smuzhiyun 	{ .guid_string = HWMI_EVENT_GUID },
788*4882a593Smuzhiyun 	{  }
789*4882a593Smuzhiyun };
790*4882a593Smuzhiyun 
huawei_wmi_probe(struct platform_device * pdev)791*4882a593Smuzhiyun static int huawei_wmi_probe(struct platform_device *pdev)
792*4882a593Smuzhiyun {
793*4882a593Smuzhiyun 	const struct wmi_device_id *guid = huawei_wmi_events_id_table;
794*4882a593Smuzhiyun 	int err;
795*4882a593Smuzhiyun 
796*4882a593Smuzhiyun 	platform_set_drvdata(pdev, huawei_wmi);
797*4882a593Smuzhiyun 	huawei_wmi->dev = &pdev->dev;
798*4882a593Smuzhiyun 
799*4882a593Smuzhiyun 	while (*guid->guid_string) {
800*4882a593Smuzhiyun 		struct input_dev *idev = *huawei_wmi->idev;
801*4882a593Smuzhiyun 
802*4882a593Smuzhiyun 		if (wmi_has_guid(guid->guid_string)) {
803*4882a593Smuzhiyun 			err = huawei_wmi_input_setup(&pdev->dev, guid->guid_string, &idev);
804*4882a593Smuzhiyun 			if (err) {
805*4882a593Smuzhiyun 				dev_err(&pdev->dev, "Failed to setup input on %s\n", guid->guid_string);
806*4882a593Smuzhiyun 				return err;
807*4882a593Smuzhiyun 			}
808*4882a593Smuzhiyun 		}
809*4882a593Smuzhiyun 
810*4882a593Smuzhiyun 		idev++;
811*4882a593Smuzhiyun 		guid++;
812*4882a593Smuzhiyun 	}
813*4882a593Smuzhiyun 
814*4882a593Smuzhiyun 	if (wmi_has_guid(HWMI_METHOD_GUID)) {
815*4882a593Smuzhiyun 		mutex_init(&huawei_wmi->wmi_lock);
816*4882a593Smuzhiyun 
817*4882a593Smuzhiyun 		huawei_wmi_leds_setup(&pdev->dev);
818*4882a593Smuzhiyun 		huawei_wmi_fn_lock_setup(&pdev->dev);
819*4882a593Smuzhiyun 		huawei_wmi_battery_setup(&pdev->dev);
820*4882a593Smuzhiyun 		huawei_wmi_debugfs_setup(&pdev->dev);
821*4882a593Smuzhiyun 	}
822*4882a593Smuzhiyun 
823*4882a593Smuzhiyun 	return 0;
824*4882a593Smuzhiyun }
825*4882a593Smuzhiyun 
huawei_wmi_remove(struct platform_device * pdev)826*4882a593Smuzhiyun static int huawei_wmi_remove(struct platform_device *pdev)
827*4882a593Smuzhiyun {
828*4882a593Smuzhiyun 	const struct wmi_device_id *guid = huawei_wmi_events_id_table;
829*4882a593Smuzhiyun 
830*4882a593Smuzhiyun 	while (*guid->guid_string) {
831*4882a593Smuzhiyun 		if (wmi_has_guid(guid->guid_string))
832*4882a593Smuzhiyun 			huawei_wmi_input_exit(&pdev->dev, guid->guid_string);
833*4882a593Smuzhiyun 
834*4882a593Smuzhiyun 		guid++;
835*4882a593Smuzhiyun 	}
836*4882a593Smuzhiyun 
837*4882a593Smuzhiyun 	if (wmi_has_guid(HWMI_METHOD_GUID)) {
838*4882a593Smuzhiyun 		huawei_wmi_debugfs_exit(&pdev->dev);
839*4882a593Smuzhiyun 		huawei_wmi_battery_exit(&pdev->dev);
840*4882a593Smuzhiyun 		huawei_wmi_fn_lock_exit(&pdev->dev);
841*4882a593Smuzhiyun 	}
842*4882a593Smuzhiyun 
843*4882a593Smuzhiyun 	return 0;
844*4882a593Smuzhiyun }
845*4882a593Smuzhiyun 
846*4882a593Smuzhiyun static struct platform_driver huawei_wmi_driver = {
847*4882a593Smuzhiyun 	.driver = {
848*4882a593Smuzhiyun 		.name = "huawei-wmi",
849*4882a593Smuzhiyun 	},
850*4882a593Smuzhiyun 	.probe = huawei_wmi_probe,
851*4882a593Smuzhiyun 	.remove = huawei_wmi_remove,
852*4882a593Smuzhiyun };
853*4882a593Smuzhiyun 
huawei_wmi_init(void)854*4882a593Smuzhiyun static __init int huawei_wmi_init(void)
855*4882a593Smuzhiyun {
856*4882a593Smuzhiyun 	struct platform_device *pdev;
857*4882a593Smuzhiyun 	int err;
858*4882a593Smuzhiyun 
859*4882a593Smuzhiyun 	huawei_wmi = kzalloc(sizeof(struct huawei_wmi), GFP_KERNEL);
860*4882a593Smuzhiyun 	if (!huawei_wmi)
861*4882a593Smuzhiyun 		return -ENOMEM;
862*4882a593Smuzhiyun 
863*4882a593Smuzhiyun 	quirks = &quirk_unknown;
864*4882a593Smuzhiyun 	dmi_check_system(huawei_quirks);
865*4882a593Smuzhiyun 	if (battery_reset != -1)
866*4882a593Smuzhiyun 		quirks->battery_reset = battery_reset;
867*4882a593Smuzhiyun 	if (report_brightness != -1)
868*4882a593Smuzhiyun 		quirks->report_brightness = report_brightness;
869*4882a593Smuzhiyun 
870*4882a593Smuzhiyun 	err = platform_driver_register(&huawei_wmi_driver);
871*4882a593Smuzhiyun 	if (err)
872*4882a593Smuzhiyun 		goto pdrv_err;
873*4882a593Smuzhiyun 
874*4882a593Smuzhiyun 	pdev = platform_device_register_simple("huawei-wmi", -1, NULL, 0);
875*4882a593Smuzhiyun 	if (IS_ERR(pdev)) {
876*4882a593Smuzhiyun 		err = PTR_ERR(pdev);
877*4882a593Smuzhiyun 		goto pdev_err;
878*4882a593Smuzhiyun 	}
879*4882a593Smuzhiyun 
880*4882a593Smuzhiyun 	return 0;
881*4882a593Smuzhiyun 
882*4882a593Smuzhiyun pdev_err:
883*4882a593Smuzhiyun 	platform_driver_unregister(&huawei_wmi_driver);
884*4882a593Smuzhiyun pdrv_err:
885*4882a593Smuzhiyun 	kfree(huawei_wmi);
886*4882a593Smuzhiyun 	return err;
887*4882a593Smuzhiyun }
888*4882a593Smuzhiyun 
huawei_wmi_exit(void)889*4882a593Smuzhiyun static __exit void huawei_wmi_exit(void)
890*4882a593Smuzhiyun {
891*4882a593Smuzhiyun 	struct platform_device *pdev = to_platform_device(huawei_wmi->dev);
892*4882a593Smuzhiyun 
893*4882a593Smuzhiyun 	platform_device_unregister(pdev);
894*4882a593Smuzhiyun 	platform_driver_unregister(&huawei_wmi_driver);
895*4882a593Smuzhiyun 
896*4882a593Smuzhiyun 	kfree(huawei_wmi);
897*4882a593Smuzhiyun }
898*4882a593Smuzhiyun 
899*4882a593Smuzhiyun module_init(huawei_wmi_init);
900*4882a593Smuzhiyun module_exit(huawei_wmi_exit);
901*4882a593Smuzhiyun 
902*4882a593Smuzhiyun MODULE_ALIAS("wmi:"HWMI_METHOD_GUID);
903*4882a593Smuzhiyun MODULE_DEVICE_TABLE(wmi, huawei_wmi_events_id_table);
904*4882a593Smuzhiyun MODULE_AUTHOR("Ayman Bagabas <ayman.bagabas@gmail.com>");
905*4882a593Smuzhiyun MODULE_DESCRIPTION("Huawei WMI laptop extras driver");
906*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
907