1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  *  ideapad-laptop.h - Lenovo IdeaPad ACPI Extras
4  *
5  *  Copyright © 2010 Intel Corporation
6  *  Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
7  */
8 
9 #ifndef _IDEAPAD_LAPTOP_H_
10 #define _IDEAPAD_LAPTOP_H_
11 
12 #include <linux/acpi.h>
13 #include <linux/jiffies.h>
14 #include <linux/errno.h>
15 
16 enum {
17 	VPCCMD_R_VPC1 = 0x10,
18 	VPCCMD_R_BL_MAX,
19 	VPCCMD_R_BL,
20 	VPCCMD_W_BL,
21 	VPCCMD_R_WIFI,
22 	VPCCMD_W_WIFI,
23 	VPCCMD_R_BT,
24 	VPCCMD_W_BT,
25 	VPCCMD_R_BL_POWER,
26 	VPCCMD_R_NOVO,
27 	VPCCMD_R_VPC2,
28 	VPCCMD_R_TOUCHPAD,
29 	VPCCMD_W_TOUCHPAD,
30 	VPCCMD_R_CAMERA,
31 	VPCCMD_W_CAMERA,
32 	VPCCMD_R_3G,
33 	VPCCMD_W_3G,
34 	VPCCMD_R_ODD, /* 0x21 */
35 	VPCCMD_W_FAN,
36 	VPCCMD_R_RF,
37 	VPCCMD_W_RF,
38 	VPCCMD_W_YMC = 0x2A,
39 	VPCCMD_R_FAN = 0x2B,
40 	VPCCMD_R_SPECIAL_BUTTONS = 0x31,
41 	VPCCMD_W_BL_POWER = 0x33,
42 };
43 
44 static inline int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg, unsigned long *res)
45 {
46 	struct acpi_object_list params;
47 	unsigned long long result;
48 	union acpi_object in_obj;
49 	acpi_status status;
50 
51 	params.count = 1;
52 	params.pointer = &in_obj;
53 	in_obj.type = ACPI_TYPE_INTEGER;
54 	in_obj.integer.value = arg;
55 
56 	status = acpi_evaluate_integer(handle, (char *)name, &params, &result);
57 	if (ACPI_FAILURE(status))
58 		return -EIO;
59 
60 	if (res)
61 		*res = result;
62 
63 	return 0;
64 }
65 
66 static inline int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res)
67 {
68 	return eval_int_with_arg(handle, "VPCR", cmd, res);
69 }
70 
71 static inline int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data)
72 {
73 	struct acpi_object_list params;
74 	union acpi_object in_obj[2];
75 	acpi_status status;
76 
77 	params.count = 2;
78 	params.pointer = in_obj;
79 	in_obj[0].type = ACPI_TYPE_INTEGER;
80 	in_obj[0].integer.value = cmd;
81 	in_obj[1].type = ACPI_TYPE_INTEGER;
82 	in_obj[1].integer.value = data;
83 
84 	status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
85 	if (ACPI_FAILURE(status))
86 		return -EIO;
87 
88 	return 0;
89 }
90 
91 #define IDEAPAD_EC_TIMEOUT 200 /* in ms */
92 
93 static inline int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data)
94 {
95 	unsigned long end_jiffies, val;
96 	int err;
97 
98 	err = eval_vpcw(handle, 1, cmd);
99 	if (err)
100 		return err;
101 
102 	end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
103 
104 	while (time_before(jiffies, end_jiffies)) {
105 		schedule();
106 
107 		err = eval_vpcr(handle, 1, &val);
108 		if (err)
109 			return err;
110 
111 		if (val == 0)
112 			return eval_vpcr(handle, 0, data);
113 	}
114 
115 	acpi_handle_err(handle, "timeout in %s\n", __func__);
116 
117 	return -ETIMEDOUT;
118 }
119 
120 static inline int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data)
121 {
122 	unsigned long end_jiffies, val;
123 	int err;
124 
125 	err = eval_vpcw(handle, 0, data);
126 	if (err)
127 		return err;
128 
129 	err = eval_vpcw(handle, 1, cmd);
130 	if (err)
131 		return err;
132 
133 	end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
134 
135 	while (time_before(jiffies, end_jiffies)) {
136 		schedule();
137 
138 		err = eval_vpcr(handle, 1, &val);
139 		if (err)
140 			return err;
141 
142 		if (val == 0)
143 			return 0;
144 	}
145 
146 	acpi_handle_err(handle, "timeout in %s\n", __func__);
147 
148 	return -ETIMEDOUT;
149 }
150 
151 #undef IDEAPAD_EC_TIMEOUT
152 #endif /* !_IDEAPAD_LAPTOP_H_ */
153