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
16enum {
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
44static 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
66static 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
71static 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
93static 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
120static 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