inspur_platform_profile.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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 platform_profile_handler handler;
  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 platform_profile_handler *pprof,
  76. enum platform_profile_option profile)
  77. {
  78. struct inspur_wmi_priv *priv = container_of(pprof, struct inspur_wmi_priv,
  79. handler);
  80. u8 ret_code[4] = {0, 0, 0, 0};
  81. int ret;
  82. switch (profile) {
  83. case PLATFORM_PROFILE_BALANCED:
  84. ret_code[0] = INSPUR_TMP_PROFILE_BALANCE;
  85. break;
  86. case PLATFORM_PROFILE_PERFORMANCE:
  87. ret_code[0] = INSPUR_TMP_PROFILE_PERFORMANCE;
  88. break;
  89. case PLATFORM_PROFILE_LOW_POWER:
  90. ret_code[0] = INSPUR_TMP_PROFILE_POWERSAVE;
  91. break;
  92. default:
  93. return -EOPNOTSUPP;
  94. }
  95. ret = inspur_wmi_perform_query(priv->wdev, INSPUR_WMI_SET_POWERMODE,
  96. ret_code, sizeof(ret_code),
  97. sizeof(ret_code));
  98. if (ret < 0)
  99. return ret;
  100. if (ret_code[0])
  101. return -EBADRQC;
  102. return 0;
  103. }
  104. /*
  105. * Get Power Mode from EC RAM, If Power Mode value greater than 0x3,
  106. * return error
  107. * Method ID: 0x2
  108. * Return Value: 4 Bytes
  109. * Byte [0]: Return Code
  110. * 0x0: No Error
  111. * 0x1: Error
  112. * Byte [1]: Power Mode
  113. * 0x0: Balance Mode
  114. * 0x1: Performance Mode
  115. * 0x2: Power Saver Mode
  116. */
  117. static int inspur_platform_profile_get(struct platform_profile_handler *pprof,
  118. enum platform_profile_option *profile)
  119. {
  120. struct inspur_wmi_priv *priv = container_of(pprof, struct inspur_wmi_priv,
  121. handler);
  122. u8 ret_code[4] = {0, 0, 0, 0};
  123. int ret;
  124. ret = inspur_wmi_perform_query(priv->wdev, INSPUR_WMI_GET_POWERMODE,
  125. &ret_code, sizeof(ret_code),
  126. sizeof(ret_code));
  127. if (ret < 0)
  128. return ret;
  129. if (ret_code[0])
  130. return -EBADRQC;
  131. switch (ret_code[1]) {
  132. case INSPUR_TMP_PROFILE_BALANCE:
  133. *profile = PLATFORM_PROFILE_BALANCED;
  134. break;
  135. case INSPUR_TMP_PROFILE_PERFORMANCE:
  136. *profile = PLATFORM_PROFILE_PERFORMANCE;
  137. break;
  138. case INSPUR_TMP_PROFILE_POWERSAVE:
  139. *profile = PLATFORM_PROFILE_LOW_POWER;
  140. break;
  141. default:
  142. return -EINVAL;
  143. }
  144. return 0;
  145. }
  146. static int inspur_wmi_probe(struct wmi_device *wdev, const void *context)
  147. {
  148. struct inspur_wmi_priv *priv;
  149. priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
  150. if (!priv)
  151. return -ENOMEM;
  152. priv->wdev = wdev;
  153. dev_set_drvdata(&wdev->dev, priv);
  154. priv->handler.profile_get = inspur_platform_profile_get;
  155. priv->handler.profile_set = inspur_platform_profile_set;
  156. set_bit(PLATFORM_PROFILE_LOW_POWER, priv->handler.choices);
  157. set_bit(PLATFORM_PROFILE_BALANCED, priv->handler.choices);
  158. set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->handler.choices);
  159. return platform_profile_register(&priv->handler);
  160. }
  161. static void inspur_wmi_remove(struct wmi_device *wdev)
  162. {
  163. platform_profile_remove();
  164. }
  165. static const struct wmi_device_id inspur_wmi_id_table[] = {
  166. { .guid_string = WMI_INSPUR_POWERMODE_BIOS_GUID },
  167. { }
  168. };
  169. MODULE_DEVICE_TABLE(wmi, inspur_wmi_id_table);
  170. static struct wmi_driver inspur_wmi_driver = {
  171. .driver = {
  172. .name = "inspur-wmi-platform-profile",
  173. .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  174. },
  175. .id_table = inspur_wmi_id_table,
  176. .probe = inspur_wmi_probe,
  177. .remove = inspur_wmi_remove,
  178. .no_singleton = true,
  179. };
  180. module_wmi_driver(inspur_wmi_driver);
  181. MODULE_AUTHOR("Ai Chao <aichao@kylinos.cn>");
  182. MODULE_DESCRIPTION("Platform Profile Support for Inspur");
  183. MODULE_LICENSE("GPL");