162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-or-later */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  ideapad-laptop.h - Lenovo IdeaPad ACPI Extras
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright © 2010 Intel Corporation
662306a36Sopenharmony_ci *  Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#ifndef _IDEAPAD_LAPTOP_H_
1062306a36Sopenharmony_ci#define _IDEAPAD_LAPTOP_H_
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/acpi.h>
1362306a36Sopenharmony_ci#include <linux/jiffies.h>
1462306a36Sopenharmony_ci#include <linux/errno.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cienum {
1762306a36Sopenharmony_ci	VPCCMD_R_VPC1 = 0x10,
1862306a36Sopenharmony_ci	VPCCMD_R_BL_MAX,
1962306a36Sopenharmony_ci	VPCCMD_R_BL,
2062306a36Sopenharmony_ci	VPCCMD_W_BL,
2162306a36Sopenharmony_ci	VPCCMD_R_WIFI,
2262306a36Sopenharmony_ci	VPCCMD_W_WIFI,
2362306a36Sopenharmony_ci	VPCCMD_R_BT,
2462306a36Sopenharmony_ci	VPCCMD_W_BT,
2562306a36Sopenharmony_ci	VPCCMD_R_BL_POWER,
2662306a36Sopenharmony_ci	VPCCMD_R_NOVO,
2762306a36Sopenharmony_ci	VPCCMD_R_VPC2,
2862306a36Sopenharmony_ci	VPCCMD_R_TOUCHPAD,
2962306a36Sopenharmony_ci	VPCCMD_W_TOUCHPAD,
3062306a36Sopenharmony_ci	VPCCMD_R_CAMERA,
3162306a36Sopenharmony_ci	VPCCMD_W_CAMERA,
3262306a36Sopenharmony_ci	VPCCMD_R_3G,
3362306a36Sopenharmony_ci	VPCCMD_W_3G,
3462306a36Sopenharmony_ci	VPCCMD_R_ODD, /* 0x21 */
3562306a36Sopenharmony_ci	VPCCMD_W_FAN,
3662306a36Sopenharmony_ci	VPCCMD_R_RF,
3762306a36Sopenharmony_ci	VPCCMD_W_RF,
3862306a36Sopenharmony_ci	VPCCMD_W_YMC = 0x2A,
3962306a36Sopenharmony_ci	VPCCMD_R_FAN = 0x2B,
4062306a36Sopenharmony_ci	VPCCMD_R_SPECIAL_BUTTONS = 0x31,
4162306a36Sopenharmony_ci	VPCCMD_W_BL_POWER = 0x33,
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic inline int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg, unsigned long *res)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct acpi_object_list params;
4762306a36Sopenharmony_ci	unsigned long long result;
4862306a36Sopenharmony_ci	union acpi_object in_obj;
4962306a36Sopenharmony_ci	acpi_status status;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	params.count = 1;
5262306a36Sopenharmony_ci	params.pointer = &in_obj;
5362306a36Sopenharmony_ci	in_obj.type = ACPI_TYPE_INTEGER;
5462306a36Sopenharmony_ci	in_obj.integer.value = arg;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	status = acpi_evaluate_integer(handle, (char *)name, &params, &result);
5762306a36Sopenharmony_ci	if (ACPI_FAILURE(status))
5862306a36Sopenharmony_ci		return -EIO;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (res)
6162306a36Sopenharmony_ci		*res = result;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return 0;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic inline int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	return eval_int_with_arg(handle, "VPCR", cmd, res);
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic inline int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct acpi_object_list params;
7462306a36Sopenharmony_ci	union acpi_object in_obj[2];
7562306a36Sopenharmony_ci	acpi_status status;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	params.count = 2;
7862306a36Sopenharmony_ci	params.pointer = in_obj;
7962306a36Sopenharmony_ci	in_obj[0].type = ACPI_TYPE_INTEGER;
8062306a36Sopenharmony_ci	in_obj[0].integer.value = cmd;
8162306a36Sopenharmony_ci	in_obj[1].type = ACPI_TYPE_INTEGER;
8262306a36Sopenharmony_ci	in_obj[1].integer.value = data;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
8562306a36Sopenharmony_ci	if (ACPI_FAILURE(status))
8662306a36Sopenharmony_ci		return -EIO;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return 0;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci#define IDEAPAD_EC_TIMEOUT 200 /* in ms */
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic inline int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	unsigned long end_jiffies, val;
9662306a36Sopenharmony_ci	int err;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	err = eval_vpcw(handle, 1, cmd);
9962306a36Sopenharmony_ci	if (err)
10062306a36Sopenharmony_ci		return err;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	while (time_before(jiffies, end_jiffies)) {
10562306a36Sopenharmony_ci		schedule();
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		err = eval_vpcr(handle, 1, &val);
10862306a36Sopenharmony_ci		if (err)
10962306a36Sopenharmony_ci			return err;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		if (val == 0)
11262306a36Sopenharmony_ci			return eval_vpcr(handle, 0, data);
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	acpi_handle_err(handle, "timeout in %s\n", __func__);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return -ETIMEDOUT;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic inline int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	unsigned long end_jiffies, val;
12362306a36Sopenharmony_ci	int err;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	err = eval_vpcw(handle, 0, data);
12662306a36Sopenharmony_ci	if (err)
12762306a36Sopenharmony_ci		return err;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	err = eval_vpcw(handle, 1, cmd);
13062306a36Sopenharmony_ci	if (err)
13162306a36Sopenharmony_ci		return err;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	while (time_before(jiffies, end_jiffies)) {
13662306a36Sopenharmony_ci		schedule();
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		err = eval_vpcr(handle, 1, &val);
13962306a36Sopenharmony_ci		if (err)
14062306a36Sopenharmony_ci			return err;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		if (val == 0)
14362306a36Sopenharmony_ci			return 0;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	acpi_handle_err(handle, "timeout in %s\n", __func__);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return -ETIMEDOUT;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci#undef IDEAPAD_EC_TIMEOUT
15262306a36Sopenharmony_ci#endif /* !_IDEAPAD_LAPTOP_H_ */
153