File Index
Files and directories
Sign in
FILE

inspur_platform_profile.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/inspur_platform_profile.c C
  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 device *ppdev;
 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 device *dev,
 88				       enum platform_profile_option profile)
 89{
 90	struct inspur_wmi_priv *priv = dev_get_drvdata(dev);
 91	u8 ret_code[4] = {0, 0, 0, 0};
 92	int ret;
 93
 94	switch (profile) {
 95	case PLATFORM_PROFILE_BALANCED:
 96		ret_code[0] = INSPUR_TMP_PROFILE_BALANCE;
 97		break;
 98	case PLATFORM_PROFILE_PERFORMANCE:
 99		ret_code[0] = INSPUR_TMP_PROFILE_PERFORMANCE;
100		break;
101	case PLATFORM_PROFILE_LOW_POWER:
102		ret_code[0] = INSPUR_TMP_PROFILE_POWERSAVE;
103		break;
104	default:
105		return -EOPNOTSUPP;
106	}
107
108	ret = inspur_wmi_perform_query(priv->wdev, INSPUR_WMI_SET_POWERMODE,
109				       ret_code, sizeof(ret_code),
110				       sizeof(ret_code));
111
112	if (ret < 0)
113		return ret;
114
115	if (ret_code[0])
116		return -EBADRQC;
117
118	return 0;
119}
120
121/*
122 * Get Power Mode from EC RAM, If Power Mode value greater than 0x3,
123 * return error
124 * Method ID: 0x2
125 * Return Value: 4 Bytes
126 * Byte [0]: Return Code
127 *         0x0: No Error
128 *         0x1: Error
129 * Byte [1]: Power Mode
130 *         0x0: Balance Mode
131 *         0x1: Performance Mode
132 *         0x2: Power Saver Mode
133 */
134static int inspur_platform_profile_get(struct device *dev,
135				       enum platform_profile_option *profile)
136{
137	struct inspur_wmi_priv *priv = dev_get_drvdata(dev);
138	u8 ret_code[4] = {0, 0, 0, 0};
139	int ret;
140
141	ret = inspur_wmi_perform_query(priv->wdev, INSPUR_WMI_GET_POWERMODE,
142				       &ret_code, sizeof(ret_code),
143				       sizeof(ret_code));
144	if (ret < 0)
145		return ret;
146
147	if (ret_code[0])
148		return -EBADRQC;
149
150	switch (ret_code[1]) {
151	case INSPUR_TMP_PROFILE_BALANCE:
152		*profile = PLATFORM_PROFILE_BALANCED;
153		break;
154	case INSPUR_TMP_PROFILE_PERFORMANCE:
155		*profile = PLATFORM_PROFILE_PERFORMANCE;
156		break;
157	case INSPUR_TMP_PROFILE_POWERSAVE:
158		*profile = PLATFORM_PROFILE_LOW_POWER;
159		break;
160	default:
161		return -EINVAL;
162	}
163
164	return 0;
165}
166
167static int inspur_platform_profile_probe(void *drvdata, unsigned long *choices)
168{
169	set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
170	set_bit(PLATFORM_PROFILE_BALANCED, choices);
171	set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
172
173	return 0;
174}
175
176static const struct platform_profile_ops inspur_platform_profile_ops = {
177	.probe = inspur_platform_profile_probe,
178	.profile_get = inspur_platform_profile_get,
179	.profile_set = inspur_platform_profile_set,
180};
181
182static int inspur_wmi_probe(struct wmi_device *wdev, const void *context)
183{
184	struct inspur_wmi_priv *priv;
185
186	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
187	if (!priv)
188		return -ENOMEM;
189
190	priv->wdev = wdev;
191	dev_set_drvdata(&wdev->dev, priv);
192
193	priv->ppdev = devm_platform_profile_register(&wdev->dev, "inspur-wmi", priv,
194						     &inspur_platform_profile_ops);
195
196	return PTR_ERR_OR_ZERO(priv->ppdev);
197}
198
199static const struct wmi_device_id inspur_wmi_id_table[] = {
200	{ .guid_string = WMI_INSPUR_POWERMODE_BIOS_GUID },
201	{  }
202};
203
204MODULE_DEVICE_TABLE(wmi, inspur_wmi_id_table);
205
206static struct wmi_driver inspur_wmi_driver = {
207	.driver = {
208		.name = "inspur-wmi-platform-profile",
209		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
210	},
211	.id_table = inspur_wmi_id_table,
212	.probe = inspur_wmi_probe,
213	.no_singleton = true,
214};
215
216module_wmi_driver(inspur_wmi_driver);
217
218MODULE_AUTHOR("Ai Chao <aichao@kylinos.cn>");
219MODULE_DESCRIPTION("Platform Profile Support for Inspur");
220MODULE_LICENSE("GPL");
~/ Read-only file preview.