platform_profile.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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_register(struct platform_profile_handler *pprof)
  114. {
  115. int err;
  116. mutex_lock(&profile_lock);
  117. /* We can only have one active profile */
  118. if (cur_profile) {
  119. mutex_unlock(&profile_lock);
  120. return -EEXIST;
  121. }
  122. /* Sanity check the profile handler field are set */
  123. if (!pprof || bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST) ||
  124. !pprof->profile_set || !pprof->profile_get) {
  125. mutex_unlock(&profile_lock);
  126. return -EINVAL;
  127. }
  128. err = sysfs_create_group(acpi_kobj, &platform_profile_group);
  129. if (err) {
  130. mutex_unlock(&profile_lock);
  131. return err;
  132. }
  133. cur_profile = pprof;
  134. mutex_unlock(&profile_lock);
  135. return 0;
  136. }
  137. EXPORT_SYMBOL_GPL(platform_profile_register);
  138. int platform_profile_remove(void)
  139. {
  140. sysfs_remove_group(acpi_kobj, &platform_profile_group);
  141. mutex_lock(&profile_lock);
  142. cur_profile = NULL;
  143. mutex_unlock(&profile_lock);
  144. return 0;
  145. }
  146. EXPORT_SYMBOL_GPL(platform_profile_remove);
  147. MODULE_AUTHOR("Mark Pearson <[email protected]>");
  148. MODULE_LICENSE("GPL");