Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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
 16enum 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 */
 27enum inspur_tmp_profile {
 28	INSPUR_TMP_PROFILE_BALANCE	= 0,
 29	INSPUR_TMP_PROFILE_PERFORMANCE	= 1,
 30	INSPUR_TMP_PROFILE_POWERSAVE	= 2,
 31};
 32
 33struct inspur_wmi_priv {
 34	struct wmi_device *wdev;
 35	struct platform_profile_handler handler;
 36};
 37
 38static 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
 68out_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 */
 87static 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 */
135static 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
169static 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
190static void inspur_wmi_remove(struct wmi_device *wdev)
191{
192	platform_profile_remove();
193}
194
195static const struct wmi_device_id inspur_wmi_id_table[] = {
196	{ .guid_string = WMI_INSPUR_POWERMODE_BIOS_GUID },
197	{  }
198};
199
200MODULE_DEVICE_TABLE(wmi, inspur_wmi_id_table);
201
202static 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
212module_wmi_driver(inspur_wmi_driver);
213
214MODULE_AUTHOR("Ai Chao <aichao@kylinos.cn>");
215MODULE_DESCRIPTION("Platform Profile Support for Inspur");
216MODULE_LICENSE("GPL");