platform_profile.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /* Platform profile sysfs interface */
  3. #include <linux/acpi.h>
  4. #include <linux/bits.h>
  5. #include <linux/init.h>
  6. #include <linux/mutex.h>
  7. #include <linux/platform_profile.h>
  8. #include <linux/sysfs.h>
  9. static struct platform_profile_handler *cur_profile;
  10. static DEFINE_MUTEX(profile_lock);
  11. static const char * const profile_names[] = {
  12. [PLATFORM_PROFILE_LOW_POWER] = "low-power",
  13. [PLATFORM_PROFILE_COOL] = "cool",
  14. [PLATFORM_PROFILE_QUIET] = "quiet",
  15. [PLATFORM_PROFILE_BALANCED] = "balanced",
  16. [PLATFORM_PROFILE_BALANCED_PERFORMANCE] = "balanced-performance",
  17. [PLATFORM_PROFILE_PERFORMANCE] = "performance",
  18. };
  19. static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST);
  20. static ssize_t platform_profile_choices_show(struct device *dev,
  21. struct device_attribute *attr,
  22. char *buf)
  23. {
  24. int len = 0;
  25. int err, i;
  26. err = mutex_lock_interruptible(&profile_lock);
  27. if (err)
  28. return err;
  29. if (!cur_profile) {
  30. mutex_unlock(&profile_lock);
  31. return -ENODEV;
  32. }
  33. for_each_set_bit(i, cur_profile->choices, PLATFORM_PROFILE_LAST) {
  34. if (len == 0)
  35. len += sysfs_emit_at(buf, len, "%s", profile_names[i]);
  36. else
  37. len += sysfs_emit_at(buf, len, " %s", profile_names[i]);
  38. }
  39. len += sysfs_emit_at(buf, len, "\n");
  40. mutex_unlock(&profile_lock);
  41. return len;
  42. }
  43. static ssize_t platform_profile_show(struct device *dev,
  44. struct device_attribute *attr,
  45. char *buf)
  46. {
  47. enum platform_profile_option profile = PLATFORM_PROFILE_BALANCED;
  48. int err;
  49. err = mutex_lock_interruptible(&profile_lock);
  50. if (err)
  51. return err;
  52. if (!cur_profile) {
  53. mutex_unlock(&profile_lock);
  54. return -ENODEV;
  55. }
  56. err = cur_profile->profile_get(cur_profile, &profile);
  57. mutex_unlock(&profile_lock);
  58. if (err)
  59. return err;
  60. /* Check that profile is valid index */
  61. if (WARN_ON((profile < 0) || (profile >= ARRAY_SIZE(profile_names))))
  62. return -EIO;
  63. return sysfs_emit(buf, "%s\n", profile_names[profile]);
  64. }
  65. static ssize_t platform_profile_store(struct device *dev,
  66. struct device_attribute *attr,
  67. const char *buf, size_t count)
  68. {
  69. int err, i;
  70. err = mutex_lock_interruptible(&profile_lock);
  71. if (err)
  72. return err;
  73. if (!cur_profile) {
  74. mutex_unlock(&profile_lock);
  75. return -ENODEV;
  76. }
  77. /* Scan for a matching profile */
  78. i = sysfs_match_string(profile_names, buf);
  79. if (i < 0) {
  80. mutex_unlock(&profile_lock);
  81. return -EINVAL;
  82. }
  83. /* Check that platform supports this profile choice */
  84. if (!test_bit(i, cur_profile->choices)) {
  85. mutex_unlock(&profile_lock);
  86. return -EOPNOTSUPP;
  87. }
  88. err = cur_profile->profile_set(cur_profile, i);
  89. if (!err)
  90. sysfs_notify(acpi_kobj, NULL, "platform_profile");
  91. mutex_unlock(&profile_lock);
  92. if (err)
  93. return err;
  94. return count;
  95. }
  96. static DEVICE_ATTR_RO(platform_profile_choices);
  97. static DEVICE_ATTR_RW(platform_profile);
  98. static struct attribute *platform_profile_attrs[] = {
  99. &dev_attr_platform_profile_choices.attr,
  100. &dev_attr_platform_profile.attr,
  101. NULL
  102. };
  103. static const struct attribute_group platform_profile_group = {
  104. .attrs = platform_profile_attrs
  105. };
  106. void platform_profile_notify(void)
  107. {
  108. if (!cur_profile)
  109. return;
  110. sysfs_notify(acpi_kobj, NULL, "platform_profile");
  111. }
  112. EXPORT_SYMBOL_GPL(platform_profile_notify);
  113. int platform_profile_cycle(void)
  114. {
  115. enum platform_profile_option profile;
  116. enum platform_profile_option next;
  117. int err;
  118. err = mutex_lock_interruptible(&profile_lock);
  119. if (err)
  120. return err;
  121. if (!cur_profile) {
  122. mutex_unlock(&profile_lock);
  123. return -ENODEV;
  124. }
  125. err = cur_profile->profile_get(cur_profile, &profile);
  126. if (err) {
  127. mutex_unlock(&profile_lock);
  128. return err;
  129. }
  130. next = find_next_bit_wrap(cur_profile->choices, PLATFORM_PROFILE_LAST,
  131. profile + 1);
  132. if (WARN_ON(next == PLATFORM_PROFILE_LAST)) {
  133. mutex_unlock(&profile_lock);
  134. return -EINVAL;
  135. }
  136. err = cur_profile->profile_set(cur_profile, next);
  137. mutex_unlock(&profile_lock);
  138. if (!err)
  139. sysfs_notify(acpi_kobj, NULL, "platform_profile");
  140. return err;
  141. }
  142. EXPORT_SYMBOL_GPL(platform_profile_cycle);
  143. int platform_profile_register(struct platform_profile_handler *pprof)
  144. {
  145. int err;
  146. mutex_lock(&profile_lock);
  147. /* We can only have one active profile */
  148. if (cur_profile) {
  149. mutex_unlock(&profile_lock);
  150. return -EEXIST;
  151. }
  152. /* Sanity check the profile handler field are set */
  153. if (!pprof || bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST) ||
  154. !pprof->profile_set || !pprof->profile_get) {
  155. mutex_unlock(&profile_lock);
  156. return -EINVAL;
  157. }
  158. err = sysfs_create_group(acpi_kobj, &platform_profile_group);
  159. if (err) {
  160. mutex_unlock(&profile_lock);
  161. return err;
  162. }
  163. cur_profile = pprof;
  164. mutex_unlock(&profile_lock);
  165. return 0;
  166. }
  167. EXPORT_SYMBOL_GPL(platform_profile_register);
  168. int platform_profile_remove(void)
  169. {
  170. sysfs_remove_group(acpi_kobj, &platform_profile_group);
  171. mutex_lock(&profile_lock);
  172. cur_profile = NULL;
  173. mutex_unlock(&profile_lock);
  174. return 0;
  175. }
  176. EXPORT_SYMBOL_GPL(platform_profile_remove);
  177. MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
  178. MODULE_DESCRIPTION("ACPI platform profile sysfs interface");
  179. MODULE_LICENSE("GPL");