Skip to content

Commit 94ace9e

Browse files
TomAi0ij-intel
authored andcommitted
platform/x86: inspur-platform-profile: Add platform profile support
Add support for Inspur platforms to used the platform profile feature. This will allow users to determine and control the platform modes between low-power, balanced and performance modes. Signed-off-by: Ai Chao <aichao@kylinos.cn> Reviewed-by: Thomas Weißschuh <linux@weissschuh.net> Reviewed-by: Armin Wolf <W_Armin@gmx.de> Link: https://lore.kernel.org/r/20231020024007.1677962-1-aichao@kylinos.cn [ij: Removed kerneldoc markers from non-kerneldoc comments.] Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
1 parent 9166467 commit 94ace9e

File tree

3 files changed

+230
-0
lines changed

3 files changed

+230
-0
lines changed

drivers/platform/x86/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,17 @@ config TOUCHSCREEN_DMI
988988
the OS-image for the device. This option supplies the missing info.
989989
Enable this for x86 tablets with Silead or Chipone touchscreens.
990990

991+
config INSPUR_PLATFORM_PROFILE
992+
tristate "Inspur WMI platform profile driver"
993+
depends on ACPI_WMI
994+
select ACPI_PLATFORM_PROFILE
995+
help
996+
This will allow users to determine and control the platform modes
997+
between low-power, balanced and performance modes.
998+
999+
To compile this driver as a module, choose M here: the module
1000+
will be called inspur-platform-profile.
1001+
9911002
source "drivers/platform/x86/x86-android-tablets/Kconfig"
9921003

9931004
config FW_ATTR_CLASS

drivers/platform/x86/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o
9898
# before toshiba_acpi initializes
9999
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
100100

101+
# Inspur
102+
obj-$(CONFIG_INSPUR_PLATFORM_PROFILE) += inspur_platform_profile.o
103+
101104
# Laptop drivers
102105
obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o
103106
obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Inspur WMI Platform Profile
4+
*
5+
* Copyright (C) 2018 Ai Chao <aichao@kylinos.cn>
6+
*/
7+
8+
#include <linux/acpi.h>
9+
#include <linux/device.h>
10+
#include <linux/module.h>
11+
#include <linux/platform_profile.h>
12+
#include <linux/wmi.h>
13+
14+
#define WMI_INSPUR_POWERMODE_BIOS_GUID "596C31E3-332D-43C9-AEE9-585493284F5D"
15+
16+
enum inspur_wmi_method_ids {
17+
INSPUR_WMI_GET_POWERMODE = 0x02,
18+
INSPUR_WMI_SET_POWERMODE = 0x03,
19+
};
20+
21+
/*
22+
* Power Mode:
23+
* 0x0: Balance Mode
24+
* 0x1: Performance Mode
25+
* 0x2: Power Saver Mode
26+
*/
27+
enum inspur_tmp_profile {
28+
INSPUR_TMP_PROFILE_BALANCE = 0,
29+
INSPUR_TMP_PROFILE_PERFORMANCE = 1,
30+
INSPUR_TMP_PROFILE_POWERSAVE = 2,
31+
};
32+
33+
struct inspur_wmi_priv {
34+
struct wmi_device *wdev;
35+
struct platform_profile_handler handler;
36+
};
37+
38+
static int inspur_wmi_perform_query(struct wmi_device *wdev,
39+
enum inspur_wmi_method_ids query_id,
40+
void *buffer, size_t insize,
41+
size_t outsize)
42+
{
43+
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
44+
struct acpi_buffer input = { insize, buffer};
45+
union acpi_object *obj;
46+
acpi_status status;
47+
int ret = 0;
48+
49+
status = wmidev_evaluate_method(wdev, 0, query_id, &input, &output);
50+
if (ACPI_FAILURE(status)) {
51+
dev_err(&wdev->dev, "EC Powermode control failed: %s\n",
52+
acpi_format_exception(status));
53+
return -EIO;
54+
}
55+
56+
obj = output.pointer;
57+
if (!obj)
58+
return -EINVAL;
59+
60+
if (obj->type != ACPI_TYPE_BUFFER ||
61+
obj->buffer.length != outsize) {
62+
ret = -EINVAL;
63+
goto out_free;
64+
}
65+
66+
memcpy(buffer, obj->buffer.pointer, obj->buffer.length);
67+
68+
out_free:
69+
kfree(obj);
70+
return ret;
71+
}
72+
73+
/*
74+
* Set Power Mode to EC RAM. If Power Mode value greater than 0x3,
75+
* return error
76+
* Method ID: 0x3
77+
* Arg: 4 Bytes
78+
* Byte [0]: Power Mode:
79+
* 0x0: Balance Mode
80+
* 0x1: Performance Mode
81+
* 0x2: Power Saver Mode
82+
* Return Value: 4 Bytes
83+
* Byte [0]: Return Code
84+
* 0x0: No Error
85+
* 0x1: Error
86+
*/
87+
static int inspur_platform_profile_set(struct platform_profile_handler *pprof,
88+
enum platform_profile_option profile)
89+
{
90+
struct inspur_wmi_priv *priv = container_of(pprof, struct inspur_wmi_priv,
91+
handler);
92+
u8 ret_code[4] = {0, 0, 0, 0};
93+
int ret;
94+
95+
switch (profile) {
96+
case PLATFORM_PROFILE_BALANCED:
97+
ret_code[0] = INSPUR_TMP_PROFILE_BALANCE;
98+
break;
99+
case PLATFORM_PROFILE_PERFORMANCE:
100+
ret_code[0] = INSPUR_TMP_PROFILE_PERFORMANCE;
101+
break;
102+
case PLATFORM_PROFILE_LOW_POWER:
103+
ret_code[0] = INSPUR_TMP_PROFILE_POWERSAVE;
104+
break;
105+
default:
106+
return -EOPNOTSUPP;
107+
}
108+
109+
ret = inspur_wmi_perform_query(priv->wdev, INSPUR_WMI_SET_POWERMODE,
110+
ret_code, sizeof(ret_code),
111+
sizeof(ret_code));
112+
113+
if (ret < 0)
114+
return ret;
115+
116+
if (ret_code[0])
117+
return -EBADRQC;
118+
119+
return 0;
120+
}
121+
122+
/*
123+
* Get Power Mode from EC RAM, If Power Mode value greater than 0x3,
124+
* return error
125+
* Method ID: 0x2
126+
* Return Value: 4 Bytes
127+
* Byte [0]: Return Code
128+
* 0x0: No Error
129+
* 0x1: Error
130+
* Byte [1]: Power Mode
131+
* 0x0: Balance Mode
132+
* 0x1: Performance Mode
133+
* 0x2: Power Saver Mode
134+
*/
135+
static int inspur_platform_profile_get(struct platform_profile_handler *pprof,
136+
enum platform_profile_option *profile)
137+
{
138+
struct inspur_wmi_priv *priv = container_of(pprof, struct inspur_wmi_priv,
139+
handler);
140+
u8 ret_code[4] = {0, 0, 0, 0};
141+
int ret;
142+
143+
ret = inspur_wmi_perform_query(priv->wdev, INSPUR_WMI_GET_POWERMODE,
144+
&ret_code, sizeof(ret_code),
145+
sizeof(ret_code));
146+
if (ret < 0)
147+
return ret;
148+
149+
if (ret_code[0])
150+
return -EBADRQC;
151+
152+
switch (ret_code[1]) {
153+
case INSPUR_TMP_PROFILE_BALANCE:
154+
*profile = PLATFORM_PROFILE_BALANCED;
155+
break;
156+
case INSPUR_TMP_PROFILE_PERFORMANCE:
157+
*profile = PLATFORM_PROFILE_PERFORMANCE;
158+
break;
159+
case INSPUR_TMP_PROFILE_POWERSAVE:
160+
*profile = PLATFORM_PROFILE_LOW_POWER;
161+
break;
162+
default:
163+
return -EINVAL;
164+
}
165+
166+
return 0;
167+
}
168+
169+
static int inspur_wmi_probe(struct wmi_device *wdev, const void *context)
170+
{
171+
struct inspur_wmi_priv *priv;
172+
173+
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
174+
if (!priv)
175+
return -ENOMEM;
176+
177+
priv->wdev = wdev;
178+
dev_set_drvdata(&wdev->dev, priv);
179+
180+
priv->handler.profile_get = inspur_platform_profile_get;
181+
priv->handler.profile_set = inspur_platform_profile_set;
182+
183+
set_bit(PLATFORM_PROFILE_LOW_POWER, priv->handler.choices);
184+
set_bit(PLATFORM_PROFILE_BALANCED, priv->handler.choices);
185+
set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->handler.choices);
186+
187+
return platform_profile_register(&priv->handler);
188+
}
189+
190+
static void inspur_wmi_remove(struct wmi_device *wdev)
191+
{
192+
platform_profile_remove();
193+
}
194+
195+
static const struct wmi_device_id inspur_wmi_id_table[] = {
196+
{ .guid_string = WMI_INSPUR_POWERMODE_BIOS_GUID },
197+
{ }
198+
};
199+
200+
MODULE_DEVICE_TABLE(wmi, inspur_wmi_id_table);
201+
202+
static struct wmi_driver inspur_wmi_driver = {
203+
.driver = {
204+
.name = "inspur-wmi-platform-profile",
205+
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
206+
},
207+
.id_table = inspur_wmi_id_table,
208+
.probe = inspur_wmi_probe,
209+
.remove = inspur_wmi_remove,
210+
};
211+
212+
module_wmi_driver(inspur_wmi_driver);
213+
214+
MODULE_AUTHOR("Ai Chao <aichao@kylinos.cn>");
215+
MODULE_DESCRIPTION("Platform Profile Support for Inspur");
216+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)