brintos

brintos / linux-shallow public Read only

0
0
Text · 5.1 KiB · 8440def Raw
218 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 *  Inspur WMI Platform Profile4 *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 Mode24 *           0x1: Performance Mode25 *           0x2: Power Saver Mode26 */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 error76 * Method ID: 0x377 * Arg: 4 Bytes78 * Byte [0]: Power Mode:79 *         0x0: Balance Mode80 *         0x1: Performance Mode81 *         0x2: Power Saver Mode82 * Return Value: 4 Bytes83 * Byte [0]: Return Code84 *         0x0: No Error85 *         0x1: Error86 */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 error125 * Method ID: 0x2126 * Return Value: 4 Bytes127 * Byte [0]: Return Code128 *         0x0: No Error129 *         0x1: Error130 * Byte [1]: Power Mode131 *         0x0: Balance Mode132 *         0x1: Performance Mode133 *         0x2: Power Saver Mode134 */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	.no_singleton = true,211};212 213module_wmi_driver(inspur_wmi_driver);214 215MODULE_AUTHOR("Ai Chao <aichao@kylinos.cn>");216MODULE_DESCRIPTION("Platform Profile Support for Inspur");217MODULE_LICENSE("GPL");218