qcom-simple-lpm-sysfs.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  4. */
  5. #include <linux/cpu.h>
  6. #include <linux/pm_domain.h>
  7. #include <linux/slab.h>
  8. #include <linux/string.h>
  9. #include "qcom-simple-lpm.h"
  10. #define MAX_LATENCY_DIV 1000
  11. static struct kobject *qcom_lpm_simple_kobj;
  12. static ssize_t cluster_idle_set(struct kobject *kobj,
  13. struct kobj_attribute *attr,
  14. const char *buf, size_t len)
  15. {
  16. struct qcom_simple_cluster_node *d = container_of(attr,
  17. struct qcom_simple_cluster_node, disable_attr);
  18. bool disable;
  19. int ret;
  20. ret = strtobool(buf, &disable);
  21. if (ret)
  22. return -EINVAL;
  23. d->cluster->state_allowed[d->state_idx] = !disable;
  24. return len;
  25. }
  26. static ssize_t cluster_idle_get(struct kobject *kobj,
  27. struct kobj_attribute *attr,
  28. char *buf)
  29. {
  30. struct qcom_simple_cluster_node *d = container_of(attr,
  31. struct qcom_simple_cluster_node, disable_attr);
  32. return scnprintf(buf, PAGE_SIZE, "%d\n", !d->cluster->state_allowed[d->state_idx]);
  33. }
  34. static int create_simple_cluster_state_node(struct device *dev, struct qcom_simple_cluster_node *d)
  35. {
  36. struct kobj_attribute *attr = &d->disable_attr;
  37. int ret;
  38. d->attr_group = devm_kzalloc(dev, sizeof(struct attribute_group), GFP_KERNEL);
  39. if (!d->attr_group)
  40. return -ENOMEM;
  41. d->attrs = devm_kcalloc(dev, 2, sizeof(struct attribute *), GFP_KERNEL);
  42. if (!d->attrs)
  43. return -ENOMEM;
  44. sysfs_attr_init(&attr->attr);
  45. attr->attr.name = "disable";
  46. attr->attr.mode = 0644;
  47. attr->show = cluster_idle_get;
  48. attr->store = cluster_idle_set;
  49. d->attrs[0] = &attr->attr;
  50. d->attrs[1] = NULL;
  51. d->attr_group->attrs = d->attrs;
  52. ret = sysfs_create_group(d->kobj, d->attr_group);
  53. if (ret)
  54. return -ENOMEM;
  55. return ret;
  56. }
  57. void remove_simple_cluster_sysfs_nodes(struct simple_lpm_cluster *cluster)
  58. {
  59. struct generic_pm_domain *genpd = cluster->genpd;
  60. struct kobject *kobj = cluster->dev_kobj;
  61. int i;
  62. if (!qcom_lpm_simple_kobj || !kobj)
  63. return;
  64. for (i = 0; i < genpd->state_count; i++) {
  65. struct qcom_simple_cluster_node *d = cluster->dev_node[i];
  66. if (d->kobj) {
  67. sysfs_remove_group(d->kobj, d->attr_group);
  68. kobject_put(d->kobj);
  69. }
  70. }
  71. kobject_put(kobj);
  72. }
  73. int create_simple_cluster_sysfs_nodes(struct simple_lpm_cluster *cluster)
  74. {
  75. char name[10];
  76. int i, ret;
  77. struct generic_pm_domain *genpd = cluster->genpd;
  78. if (!qcom_lpm_simple_kobj)
  79. return -EPROBE_DEFER;
  80. cluster->dev_kobj = kobject_create_and_add(genpd->name, qcom_lpm_simple_kobj);
  81. if (!cluster->dev_kobj)
  82. return -ENOMEM;
  83. for (i = 0; i < genpd->state_count; i++) {
  84. struct qcom_simple_cluster_node *d;
  85. d = devm_kzalloc(cluster->dev, sizeof(*d), GFP_KERNEL);
  86. if (!d) {
  87. kobject_put(cluster->dev_kobj);
  88. return -ENOMEM;
  89. }
  90. d->state_idx = i;
  91. d->cluster = cluster;
  92. scnprintf(name, PAGE_SIZE, "D%u", i);
  93. d->kobj = kobject_create_and_add(name, cluster->dev_kobj);
  94. if (!d->kobj) {
  95. kobject_put(cluster->dev_kobj);
  96. return -ENOMEM;
  97. }
  98. ret = create_simple_cluster_state_node(cluster->dev, d);
  99. if (ret) {
  100. kobject_put(d->kobj);
  101. kobject_put(cluster->dev_kobj);
  102. return ret;
  103. }
  104. cluster->dev_node[i] = d;
  105. }
  106. return 0;
  107. }
  108. static ssize_t simple_sleep_disabled_show(struct kobject *kobj,
  109. struct kobj_attribute *attr,
  110. char *buf)
  111. {
  112. return scnprintf(buf, PAGE_SIZE, "%u\n", simple_sleep_disabled);
  113. }
  114. static ssize_t simple_sleep_disabled_store(struct kobject *kobj,
  115. struct kobj_attribute *attr,
  116. const char *buf, size_t count)
  117. {
  118. bool val;
  119. int ret;
  120. ret = kstrtobool(buf, &val);
  121. if (ret) {
  122. pr_err("Invalid argument passed\n");
  123. return count;
  124. }
  125. simple_sleep_disabled = val;
  126. return count;
  127. }
  128. static ssize_t cpu_latency_factor_store(struct kobject *kobj,
  129. struct kobj_attribute *attr,
  130. const char *buf, size_t count)
  131. {
  132. int val, ret;
  133. ret = kstrtoint(buf, 0, &val);
  134. if (ret || !val || val > MAX_LATENCY_DIV) {
  135. pr_err("Invalid argument passed\n");
  136. return count;
  137. }
  138. cur_div = val;
  139. return count;
  140. }
  141. static ssize_t cpu_latency_factor_show(struct kobject *kobj,
  142. struct kobj_attribute *attr,
  143. char *buf)
  144. {
  145. return scnprintf(buf, PAGE_SIZE, "%u\n", cur_div);
  146. }
  147. static ssize_t cluster_latency_factor_store(struct kobject *kobj,
  148. struct kobj_attribute *attr,
  149. const char *buf, size_t count)
  150. {
  151. int val, ret, i;
  152. struct simple_lpm_cluster *cluster_simple_gov;
  153. ret = kstrtoint(buf, 0, &val);
  154. if (ret || !val || val > MAX_LATENCY_DIV) {
  155. pr_err("Invalid argument passed\n");
  156. return count;
  157. }
  158. list_for_each_entry(cluster_simple_gov, &cluster_dev_list, list) {
  159. for (i = 0; i < cluster_simple_gov->genpd->state_count; i++) {
  160. struct generic_pm_domain *genpd = cluster_simple_gov->genpd;
  161. struct genpd_power_state *state = &genpd->states[i];
  162. state->residency_ns = state->residency_ns * cluster_cur_div;
  163. do_div(state->residency_ns, val);
  164. state->power_on_latency_ns = state->power_on_latency_ns * cluster_cur_div;
  165. do_div(state->power_on_latency_ns, val);
  166. state->power_off_latency_ns = state->power_on_latency_ns * cluster_cur_div;
  167. do_div(state->power_off_latency_ns, val);
  168. }
  169. }
  170. cluster_cur_div = val;
  171. return count;
  172. }
  173. static ssize_t cluster_latency_factor_show(struct kobject *kobj,
  174. struct kobj_attribute *attr,
  175. char *buf)
  176. {
  177. return scnprintf(buf, PAGE_SIZE, "%u\n", cluster_cur_div);
  178. }
  179. static struct kobj_attribute attr_simple_sleep_disabled = __ATTR_RW(simple_sleep_disabled);
  180. static struct kobj_attribute attr_cpu_latency_factor = __ATTR_RW(cpu_latency_factor);
  181. static struct kobj_attribute attr_cluster_latency_factor = __ATTR_RW(cluster_latency_factor);
  182. static struct attribute *lpm_simple_gov_attrs[] = {
  183. &attr_simple_sleep_disabled.attr,
  184. &attr_cpu_latency_factor.attr,
  185. &attr_cluster_latency_factor.attr,
  186. NULL
  187. };
  188. static struct attribute_group lpm_gov_attr_group = {
  189. .attrs = lpm_simple_gov_attrs,
  190. .name = "parameters",
  191. };
  192. void remove_simple_gov_global_sysfs_nodes(void)
  193. {
  194. sysfs_remove_group(qcom_lpm_simple_kobj, &lpm_gov_attr_group);
  195. kobject_put(qcom_lpm_simple_kobj);
  196. }
  197. int create_simple_gov_global_sysfs_nodes(void)
  198. {
  199. struct kobject *cpuidle_kobj = &cpu_subsys.dev_root->kobj;
  200. qcom_lpm_simple_kobj = kobject_create_and_add("qcom_simple_lpm", cpuidle_kobj);
  201. if (!qcom_lpm_simple_kobj)
  202. return -ENOMEM;
  203. return sysfs_create_group(qcom_lpm_simple_kobj, &lpm_gov_attr_group);
  204. }