dell-pc.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Driver for Dell laptop extras
  4. *
  5. * Copyright (c) Lyndon Sanche <lsanche@lyndeno.ca>
  6. *
  7. * Based on documentation in the libsmbios package:
  8. * Copyright (C) 2005-2014 Dell Inc.
  9. */
  10. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  11. #include <linux/bitfield.h>
  12. #include <linux/bits.h>
  13. #include <linux/dmi.h>
  14. #include <linux/err.h>
  15. #include <linux/init.h>
  16. #include <linux/kernel.h>
  17. #include <linux/module.h>
  18. #include <linux/platform_profile.h>
  19. #include <linux/slab.h>
  20. #include "dell-smbios.h"
  21. static const struct dmi_system_id dell_device_table[] __initconst = {
  22. {
  23. .ident = "Dell Inc.",
  24. .matches = {
  25. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  26. },
  27. },
  28. {
  29. .ident = "Dell Computer Corporation",
  30. .matches = {
  31. DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
  32. },
  33. },
  34. { }
  35. };
  36. MODULE_DEVICE_TABLE(dmi, dell_device_table);
  37. /* Derived from smbios-thermal-ctl
  38. *
  39. * cbClass 17
  40. * cbSelect 19
  41. * User Selectable Thermal Tables(USTT)
  42. * cbArg1 determines the function to be performed
  43. * cbArg1 0x0 = Get Thermal Information
  44. * cbRES1 Standard return codes (0, -1, -2)
  45. * cbRES2, byte 0 Bitmap of supported thermal modes. A mode is supported if
  46. * its bit is set to 1
  47. * Bit 0 Balanced
  48. * Bit 1 Cool Bottom
  49. * Bit 2 Quiet
  50. * Bit 3 Performance
  51. * cbRES2, byte 1 Bitmap of supported Active Acoustic Controller (AAC) modes.
  52. * Each mode corresponds to the supported thermal modes in
  53. * byte 0. A mode is supported if its bit is set to 1.
  54. * Bit 0 AAC (Balanced)
  55. * Bit 1 AAC (Cool Bottom
  56. * Bit 2 AAC (Quiet)
  57. * Bit 3 AAC (Performance)
  58. * cbRes3, byte 0 Current Thermal Mode
  59. * Bit 0 Balanced
  60. * Bit 1 Cool Bottom
  61. * Bit 2 Quiet
  62. * Bit 3 Performanc
  63. * cbRes3, byte 1 AAC Configuration type
  64. * 0 Global (AAC enable/disable applies to all supported USTT modes)
  65. * 1 USTT mode specific
  66. * cbRes3, byte 2 Current Active Acoustic Controller (AAC) Mode
  67. * If AAC Configuration Type is Global,
  68. * 0 AAC mode disabled
  69. * 1 AAC mode enabled
  70. * If AAC Configuration Type is USTT mode specific (multiple bits may be set),
  71. * Bit 0 AAC (Balanced)
  72. * Bit 1 AAC (Cool Bottom
  73. * Bit 2 AAC (Quiet)
  74. * Bit 3 AAC (Performance)
  75. * cbRes3, byte 3 Current Fan Failure Mode
  76. * Bit 0 Minimal Fan Failure (at least one fan has failed, one fan working)
  77. * Bit 1 Catastrophic Fan Failure (all fans have failed)
  78. *
  79. * cbArg1 0x1 (Set Thermal Information), both desired thermal mode and
  80. * desired AAC mode shall be applied
  81. * cbArg2, byte 0 Desired Thermal Mode to set
  82. * (only one bit may be set for this parameter)
  83. * Bit 0 Balanced
  84. * Bit 1 Cool Bottom
  85. * Bit 2 Quiet
  86. * Bit 3 Performance
  87. * cbArg2, byte 1 Desired Active Acoustic Controller (AAC) Mode to set
  88. * If AAC Configuration Type is Global,
  89. * 0 AAC mode disabled
  90. * 1 AAC mode enabled
  91. * If AAC Configuration Type is USTT mode specific
  92. * (multiple bits may be set for this parameter),
  93. * Bit 0 AAC (Balanced)
  94. * Bit 1 AAC (Cool Bottom
  95. * Bit 2 AAC (Quiet)
  96. * Bit 3 AAC (Performance)
  97. */
  98. #define DELL_ACC_GET_FIELD GENMASK(19, 16)
  99. #define DELL_ACC_SET_FIELD GENMASK(11, 8)
  100. #define DELL_THERMAL_SUPPORTED GENMASK(3, 0)
  101. static struct platform_profile_handler *thermal_handler;
  102. enum thermal_mode_bits {
  103. DELL_BALANCED = BIT(0),
  104. DELL_COOL_BOTTOM = BIT(1),
  105. DELL_QUIET = BIT(2),
  106. DELL_PERFORMANCE = BIT(3),
  107. };
  108. static int thermal_get_mode(void)
  109. {
  110. struct calling_interface_buffer buffer;
  111. int state;
  112. int ret;
  113. dell_fill_request(&buffer, 0x0, 0, 0, 0);
  114. ret = dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT);
  115. if (ret)
  116. return ret;
  117. state = buffer.output[2];
  118. if (state & DELL_BALANCED)
  119. return DELL_BALANCED;
  120. else if (state & DELL_COOL_BOTTOM)
  121. return DELL_COOL_BOTTOM;
  122. else if (state & DELL_QUIET)
  123. return DELL_QUIET;
  124. else if (state & DELL_PERFORMANCE)
  125. return DELL_PERFORMANCE;
  126. else
  127. return -ENXIO;
  128. }
  129. static int thermal_get_supported_modes(int *supported_bits)
  130. {
  131. struct calling_interface_buffer buffer;
  132. int ret;
  133. dell_fill_request(&buffer, 0x0, 0, 0, 0);
  134. ret = dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT);
  135. /* Thermal function not supported */
  136. if (ret == -ENXIO) {
  137. *supported_bits = 0;
  138. return 0;
  139. }
  140. if (ret)
  141. return ret;
  142. *supported_bits = FIELD_GET(DELL_THERMAL_SUPPORTED, buffer.output[1]);
  143. return 0;
  144. }
  145. static int thermal_get_acc_mode(int *acc_mode)
  146. {
  147. struct calling_interface_buffer buffer;
  148. int ret;
  149. dell_fill_request(&buffer, 0x0, 0, 0, 0);
  150. ret = dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT);
  151. if (ret)
  152. return ret;
  153. *acc_mode = FIELD_GET(DELL_ACC_GET_FIELD, buffer.output[3]);
  154. return 0;
  155. }
  156. static int thermal_set_mode(enum thermal_mode_bits state)
  157. {
  158. struct calling_interface_buffer buffer;
  159. int ret;
  160. int acc_mode;
  161. ret = thermal_get_acc_mode(&acc_mode);
  162. if (ret)
  163. return ret;
  164. dell_fill_request(&buffer, 0x1, FIELD_PREP(DELL_ACC_SET_FIELD, acc_mode) | state, 0, 0);
  165. return dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT);
  166. }
  167. static int thermal_platform_profile_set(struct platform_profile_handler *pprof,
  168. enum platform_profile_option profile)
  169. {
  170. switch (profile) {
  171. case PLATFORM_PROFILE_BALANCED:
  172. return thermal_set_mode(DELL_BALANCED);
  173. case PLATFORM_PROFILE_PERFORMANCE:
  174. return thermal_set_mode(DELL_PERFORMANCE);
  175. case PLATFORM_PROFILE_QUIET:
  176. return thermal_set_mode(DELL_QUIET);
  177. case PLATFORM_PROFILE_COOL:
  178. return thermal_set_mode(DELL_COOL_BOTTOM);
  179. default:
  180. return -EOPNOTSUPP;
  181. }
  182. }
  183. static int thermal_platform_profile_get(struct platform_profile_handler *pprof,
  184. enum platform_profile_option *profile)
  185. {
  186. int ret;
  187. ret = thermal_get_mode();
  188. if (ret < 0)
  189. return ret;
  190. switch (ret) {
  191. case DELL_BALANCED:
  192. *profile = PLATFORM_PROFILE_BALANCED;
  193. break;
  194. case DELL_PERFORMANCE:
  195. *profile = PLATFORM_PROFILE_PERFORMANCE;
  196. break;
  197. case DELL_COOL_BOTTOM:
  198. *profile = PLATFORM_PROFILE_COOL;
  199. break;
  200. case DELL_QUIET:
  201. *profile = PLATFORM_PROFILE_QUIET;
  202. break;
  203. default:
  204. return -EINVAL;
  205. }
  206. return 0;
  207. }
  208. static int thermal_init(void)
  209. {
  210. int ret;
  211. int supported_modes;
  212. /* If thermal commands are not supported, exit without error */
  213. if (!dell_smbios_class_is_supported(CLASS_INFO))
  214. return 0;
  215. /* If thermal modes are not supported, exit without error */
  216. ret = thermal_get_supported_modes(&supported_modes);
  217. if (ret < 0)
  218. return ret;
  219. if (!supported_modes)
  220. return 0;
  221. thermal_handler = kzalloc(sizeof(*thermal_handler), GFP_KERNEL);
  222. if (!thermal_handler)
  223. return -ENOMEM;
  224. thermal_handler->profile_get = thermal_platform_profile_get;
  225. thermal_handler->profile_set = thermal_platform_profile_set;
  226. if (supported_modes & DELL_QUIET)
  227. set_bit(PLATFORM_PROFILE_QUIET, thermal_handler->choices);
  228. if (supported_modes & DELL_COOL_BOTTOM)
  229. set_bit(PLATFORM_PROFILE_COOL, thermal_handler->choices);
  230. if (supported_modes & DELL_BALANCED)
  231. set_bit(PLATFORM_PROFILE_BALANCED, thermal_handler->choices);
  232. if (supported_modes & DELL_PERFORMANCE)
  233. set_bit(PLATFORM_PROFILE_PERFORMANCE, thermal_handler->choices);
  234. /* Clean up if failed */
  235. ret = platform_profile_register(thermal_handler);
  236. if (ret) {
  237. kfree(thermal_handler);
  238. thermal_handler = NULL;
  239. }
  240. return ret;
  241. }
  242. static void thermal_cleanup(void)
  243. {
  244. if (thermal_handler) {
  245. platform_profile_remove();
  246. kfree(thermal_handler);
  247. }
  248. }
  249. static int __init dell_init(void)
  250. {
  251. int ret;
  252. if (!dmi_check_system(dell_device_table))
  253. return -ENODEV;
  254. /* Do not fail module if thermal modes not supported, just skip */
  255. ret = thermal_init();
  256. if (ret)
  257. goto fail_thermal;
  258. return 0;
  259. fail_thermal:
  260. thermal_cleanup();
  261. return ret;
  262. }
  263. static void __exit dell_exit(void)
  264. {
  265. thermal_cleanup();
  266. }
  267. module_init(dell_init);
  268. module_exit(dell_exit);
  269. MODULE_AUTHOR("Lyndon Sanche <lsanche@lyndeno.ca>");
  270. MODULE_DESCRIPTION("Dell PC driver");
  271. MODULE_LICENSE("GPL");