inspur_platform_profile.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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. #include <linux/acpi.h>
  8. #include <linux/device.h>
  9. #include <linux/module.h>
  10. #include <linux/platform_profile.h>
  11. #include <linux/wmi.h>
  12. #define WMI_INSPUR_POWERMODE_BIOS_GUID "596C31E3-332D-43C9-AEE9-585493284F5D"
  13. enum inspur_wmi_method_ids {
  14. INSPUR_WMI_GET_POWERMODE = 0x02,
  15. INSPUR_WMI_SET_POWERMODE = 0x03,
  16. };
  17. /*
  18. * Power Mode:
  19. * 0x0: Balance Mode
  20. * 0x1: Performance Mode
  21. * 0x2: Power Saver Mode
  22. */
  23. enum inspur_tmp_profile {
  24. INSPUR_TMP_PROFILE_BALANCE = 0,
  25. INSPUR_TMP_PROFILE_PERFORMANCE = 1,
  26. INSPUR_TMP_PROFILE_POWERSAVE = 2,
  27. };
  28. struct inspur_wmi_priv {
  29. struct wmi_device *wdev;
  30. struct device *ppdev;
  31. };
  32. static int inspur_wmi_perform_query(struct wmi_device *wdev,
  33. enum inspur_wmi_method_ids query_id,
  34. void *buffer, size_t insize,
  35. size_t outsize)
  36. {
  37. struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
  38. struct acpi_buffer input = { insize, buffer};
  39. union acpi_object *obj;
  40. acpi_status status;
  41. int ret = 0;
  42. status = wmidev_evaluate_method(wdev, 0, query_id, &input, &output);
  43. if (ACPI_FAILURE(status)) {
  44. dev_err(&wdev->dev, "EC Powermode control failed: %s\n",
  45. acpi_format_exception(status));
  46. return -EIO;
  47. }
  48. obj = output.pointer;
  49. if (!obj)
  50. return -EINVAL;
  51. if (obj->type != ACPI_TYPE_BUFFER ||
  52. obj->buffer.length != outsize) {
  53. ret = -EINVAL;
  54. goto out_free;
  55. }
  56. memcpy(buffer, obj->buffer.pointer, obj->buffer.length);
  57. out_free:
  58. kfree(obj);
  59. return ret;
  60. }
  61. /*
  62. * Set Power Mode to EC RAM. If Power Mode value greater than 0x3,
  63. * return error
  64. * Method ID: 0x3
  65. * Arg: 4 Bytes
  66. * Byte [0]: Power Mode:
  67. * 0x0: Balance Mode
  68. * 0x1: Performance Mode
  69. * 0x2: Power Saver Mode
  70. * Return Value: 4 Bytes
  71. * Byte [0]: Return Code
  72. * 0x0: No Error
  73. * 0x1: Error
  74. */
  75. static int inspur_platform_profile_set(struct device *dev,
  76. enum platform_profile_option profile)
  77. {
  78. struct inspur_wmi_priv *priv = dev_get_drvdata(dev);
  79. u8 ret_code[4] = {0, 0, 0, 0};
  80. int ret;
  81. switch (profile) {
  82. case PLATFORM_PROFILE_BALANCED:
  83. ret_code[0] = INSPUR_TMP_PROFILE_BALANCE;
  84. break;
  85. case PLATFORM_PROFILE_PERFORMANCE:
  86. ret_code[0] = INSPUR_TMP_PROFILE_PERFORMANCE;
  87. break;
  88. case PLATFORM_PROFILE_LOW_POWER:
  89. ret_code[0] = INSPUR_TMP_PROFILE_POWERSAVE;
  90. break;
  91. default:
  92. return -EOPNOTSUPP;
  93. }
  94. ret = inspur_wmi_perform_query(priv->wdev, INSPUR_WMI_SET_POWERMODE,
  95. ret_code, sizeof(ret_code),
  96. sizeof(ret_code));
  97. if (ret < 0)
  98. return ret;
  99. if (ret_code[0])
  100. return -EBADRQC;
  101. return 0;
  102. }
  103. /*
  104. * Get Power Mode from EC RAM, If Power Mode value greater than 0x3,
  105. * return error
  106. * Method ID: 0x2
  107. * Return Value: 4 Bytes
  108. * Byte [0]: Return Code
  109. * 0x0: No Error
  110. * 0x1: Error
  111. * Byte [1]: Power Mode
  112. * 0x0: Balance Mode
  113. * 0x1: Performance Mode
  114. * 0x2: Power Saver Mode
  115. */
  116. static int inspur_platform_profile_get(struct device *dev,
  117. enum platform_profile_option *profile)
  118. {
  119. struct inspur_wmi_priv *priv = dev_get_drvdata(dev);
  120. u8 ret_code[4] = {0, 0, 0, 0};
  121. int ret;
  122. ret = inspur_wmi_perform_query(priv->wdev, INSPUR_WMI_GET_POWERMODE,
  123. &ret_code, sizeof(ret_code),
  124. sizeof(ret_code));
  125. if (ret < 0)
  126. return ret;
  127. if (ret_code[0])
  128. return -EBADRQC;
  129. switch (ret_code[1]) {
  130. case INSPUR_TMP_PROFILE_BALANCE:
  131. *profile = PLATFORM_PROFILE_BALANCED;
  132. break;
  133. case INSPUR_TMP_PROFILE_PERFORMANCE:
  134. *profile = PLATFORM_PROFILE_PERFORMANCE;
  135. break;
  136. case INSPUR_TMP_PROFILE_POWERSAVE:
  137. *profile = PLATFORM_PROFILE_LOW_POWER;
  138. break;
  139. default:
  140. return -EINVAL;
  141. }
  142. return 0;
  143. }
  144. static int inspur_platform_profile_probe(void *drvdata, unsigned long *choices)
  145. {
  146. set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
  147. set_bit(PLATFORM_PROFILE_BALANCED, choices);
  148. set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
  149. return 0;
  150. }
  151. static const struct platform_profile_ops inspur_platform_profile_ops = {
  152. .probe = inspur_platform_profile_probe,
  153. .profile_get = inspur_platform_profile_get,
  154. .profile_set = inspur_platform_profile_set,
  155. };
  156. static int inspur_wmi_probe(struct wmi_device *wdev, const void *context)
  157. {
  158. struct inspur_wmi_priv *priv;
  159. priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
  160. if (!priv)
  161. return -ENOMEM;
  162. priv->wdev = wdev;
  163. dev_set_drvdata(&wdev->dev, priv);
  164. priv->ppdev = devm_platform_profile_register(&wdev->dev, "inspur-wmi", priv,
  165. &inspur_platform_profile_ops);
  166. return PTR_ERR_OR_ZERO(priv->ppdev);
  167. }
  168. static const struct wmi_device_id inspur_wmi_id_table[] = {
  169. { .guid_string = WMI_INSPUR_POWERMODE_BIOS_GUID },
  170. { }
  171. };
  172. MODULE_DEVICE_TABLE(wmi, inspur_wmi_id_table);
  173. static struct wmi_driver inspur_wmi_driver = {
  174. .driver = {
  175. .name = "inspur-wmi-platform-profile",
  176. .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  177. },
  178. .id_table = inspur_wmi_id_table,
  179. .probe = inspur_wmi_probe,
  180. .no_singleton = true,
  181. };
  182. module_wmi_driver(inspur_wmi_driver);
  183. MODULE_AUTHOR("Ai Chao <aichao@kylinos.cn>");
  184. MODULE_DESCRIPTION("Platform Profile Support for Inspur");
  185. MODULE_LICENSE("GPL");