| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Inspur WMI Platform Profile
- *
- * Copyright (C) 2018 Ai Chao <aichao@kylinos.cn>
- */
- #include <linux/acpi.h>
- #include <linux/device.h>
- #include <linux/module.h>
- #include <linux/platform_profile.h>
- #include <linux/wmi.h>
- #define WMI_INSPUR_POWERMODE_BIOS_GUID "596C31E3-332D-43C9-AEE9-585493284F5D"
- enum inspur_wmi_method_ids {
- INSPUR_WMI_GET_POWERMODE = 0x02,
- INSPUR_WMI_SET_POWERMODE = 0x03,
- };
- /*
- * Power Mode:
- * 0x0: Balance Mode
- * 0x1: Performance Mode
- * 0x2: Power Saver Mode
- */
- enum inspur_tmp_profile {
- INSPUR_TMP_PROFILE_BALANCE = 0,
- INSPUR_TMP_PROFILE_PERFORMANCE = 1,
- INSPUR_TMP_PROFILE_POWERSAVE = 2,
- };
- struct inspur_wmi_priv {
- struct wmi_device *wdev;
- struct device *ppdev;
- };
- static int inspur_wmi_perform_query(struct wmi_device *wdev,
- enum inspur_wmi_method_ids query_id,
- void *buffer, size_t insize,
- size_t outsize)
- {
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- struct acpi_buffer input = { insize, buffer};
- union acpi_object *obj;
- acpi_status status;
- int ret = 0;
- status = wmidev_evaluate_method(wdev, 0, query_id, &input, &output);
- if (ACPI_FAILURE(status)) {
- dev_err(&wdev->dev, "EC Powermode control failed: %s\n",
- acpi_format_exception(status));
- return -EIO;
- }
- obj = output.pointer;
- if (!obj)
- return -EINVAL;
- if (obj->type != ACPI_TYPE_BUFFER ||
- obj->buffer.length != outsize) {
- ret = -EINVAL;
- goto out_free;
- }
- memcpy(buffer, obj->buffer.pointer, obj->buffer.length);
- out_free:
- kfree(obj);
- return ret;
- }
- /*
- * Set Power Mode to EC RAM. If Power Mode value greater than 0x3,
- * return error
- * Method ID: 0x3
- * Arg: 4 Bytes
- * Byte [0]: Power Mode:
- * 0x0: Balance Mode
- * 0x1: Performance Mode
- * 0x2: Power Saver Mode
- * Return Value: 4 Bytes
- * Byte [0]: Return Code
- * 0x0: No Error
- * 0x1: Error
- */
- static int inspur_platform_profile_set(struct device *dev,
- enum platform_profile_option profile)
- {
- struct inspur_wmi_priv *priv = dev_get_drvdata(dev);
- u8 ret_code[4] = {0, 0, 0, 0};
- int ret;
- switch (profile) {
- case PLATFORM_PROFILE_BALANCED:
- ret_code[0] = INSPUR_TMP_PROFILE_BALANCE;
- break;
- case PLATFORM_PROFILE_PERFORMANCE:
- ret_code[0] = INSPUR_TMP_PROFILE_PERFORMANCE;
- break;
- case PLATFORM_PROFILE_LOW_POWER:
- ret_code[0] = INSPUR_TMP_PROFILE_POWERSAVE;
- break;
- default:
- return -EOPNOTSUPP;
- }
- ret = inspur_wmi_perform_query(priv->wdev, INSPUR_WMI_SET_POWERMODE,
- ret_code, sizeof(ret_code),
- sizeof(ret_code));
- if (ret < 0)
- return ret;
- if (ret_code[0])
- return -EBADRQC;
- return 0;
- }
- /*
- * Get Power Mode from EC RAM, If Power Mode value greater than 0x3,
- * return error
- * Method ID: 0x2
- * Return Value: 4 Bytes
- * Byte [0]: Return Code
- * 0x0: No Error
- * 0x1: Error
- * Byte [1]: Power Mode
- * 0x0: Balance Mode
- * 0x1: Performance Mode
- * 0x2: Power Saver Mode
- */
- static int inspur_platform_profile_get(struct device *dev,
- enum platform_profile_option *profile)
- {
- struct inspur_wmi_priv *priv = dev_get_drvdata(dev);
- u8 ret_code[4] = {0, 0, 0, 0};
- int ret;
- ret = inspur_wmi_perform_query(priv->wdev, INSPUR_WMI_GET_POWERMODE,
- &ret_code, sizeof(ret_code),
- sizeof(ret_code));
- if (ret < 0)
- return ret;
- if (ret_code[0])
- return -EBADRQC;
- switch (ret_code[1]) {
- case INSPUR_TMP_PROFILE_BALANCE:
- *profile = PLATFORM_PROFILE_BALANCED;
- break;
- case INSPUR_TMP_PROFILE_PERFORMANCE:
- *profile = PLATFORM_PROFILE_PERFORMANCE;
- break;
- case INSPUR_TMP_PROFILE_POWERSAVE:
- *profile = PLATFORM_PROFILE_LOW_POWER;
- break;
- default:
- return -EINVAL;
- }
- return 0;
- }
- static int inspur_platform_profile_probe(void *drvdata, unsigned long *choices)
- {
- set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
- set_bit(PLATFORM_PROFILE_BALANCED, choices);
- set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
- return 0;
- }
- static const struct platform_profile_ops inspur_platform_profile_ops = {
- .probe = inspur_platform_profile_probe,
- .profile_get = inspur_platform_profile_get,
- .profile_set = inspur_platform_profile_set,
- };
- static int inspur_wmi_probe(struct wmi_device *wdev, const void *context)
- {
- struct inspur_wmi_priv *priv;
- priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- priv->wdev = wdev;
- dev_set_drvdata(&wdev->dev, priv);
- priv->ppdev = devm_platform_profile_register(&wdev->dev, "inspur-wmi", priv,
- &inspur_platform_profile_ops);
- return PTR_ERR_OR_ZERO(priv->ppdev);
- }
- static const struct wmi_device_id inspur_wmi_id_table[] = {
- { .guid_string = WMI_INSPUR_POWERMODE_BIOS_GUID },
- { }
- };
- MODULE_DEVICE_TABLE(wmi, inspur_wmi_id_table);
- static struct wmi_driver inspur_wmi_driver = {
- .driver = {
- .name = "inspur-wmi-platform-profile",
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- },
- .id_table = inspur_wmi_id_table,
- .probe = inspur_wmi_probe,
- .no_singleton = true,
- };
- module_wmi_driver(inspur_wmi_driver);
- MODULE_AUTHOR("Ai Chao <aichao@kylinos.cn>");
- MODULE_DESCRIPTION("Platform Profile Support for Inspur");
- MODULE_LICENSE("GPL");
|