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