qti_userspace_cdev.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2021, The Linux Foundation. All rights reserved.
  4. * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
  5. */
  6. #define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
  7. #include <linux/err.h>
  8. #include <linux/module.h>
  9. #include <linux/of.h>
  10. #include <linux/platform_device.h>
  11. #include <linux/slab.h>
  12. #include <linux/thermal.h>
  13. #define USERSPACE_CDEV_DRIVER "userspace-cdev"
  14. struct userspace_cdev {
  15. struct device_node *np;
  16. char cdev_name[THERMAL_NAME_LENGTH];
  17. struct thermal_cooling_device *cdev;
  18. unsigned int cur_level;
  19. unsigned int max_level;
  20. };
  21. static struct userspace_cdev *cdev_instances;
  22. static int inst_cnt;
  23. static int userspace_get_max_state(struct thermal_cooling_device *cdev,
  24. unsigned long *state)
  25. {
  26. struct userspace_cdev *usr_cdev = cdev->devdata;
  27. if (!usr_cdev)
  28. return -EINVAL;
  29. *state = usr_cdev->max_level;
  30. return 0;
  31. }
  32. static int userspace_get_cur_state(struct thermal_cooling_device *cdev,
  33. unsigned long *state)
  34. {
  35. struct userspace_cdev *usr_cdev = cdev->devdata;
  36. if (!usr_cdev)
  37. return -EINVAL;
  38. *state = usr_cdev->cur_level;
  39. return 0;
  40. }
  41. static int userspace_set_cur_state(struct thermal_cooling_device *cdev,
  42. unsigned long state)
  43. {
  44. struct userspace_cdev *usr_cdev = cdev->devdata;
  45. if (!usr_cdev)
  46. return -EINVAL;
  47. if (state > usr_cdev->max_level)
  48. return -EINVAL;
  49. if (usr_cdev->cur_level == state)
  50. return 0;
  51. usr_cdev->cur_level = state;
  52. return 0;
  53. }
  54. static struct thermal_cooling_device_ops userspace_cdev_ops = {
  55. .get_max_state = userspace_get_max_state,
  56. .get_cur_state = userspace_get_cur_state,
  57. .set_cur_state = userspace_set_cur_state,
  58. };
  59. static void userspace_cdev_cleanup(void)
  60. {
  61. int idx = 0;
  62. struct userspace_cdev *usr_cdev = cdev_instances;
  63. for (; idx < inst_cnt; idx++) {
  64. if (usr_cdev[idx].cdev)
  65. thermal_cooling_device_unregister(
  66. usr_cdev[idx].cdev);
  67. usr_cdev[idx].cdev = NULL;
  68. }
  69. inst_cnt = 0;
  70. }
  71. static int userspace_device_probe(struct platform_device *pdev)
  72. {
  73. struct device *dev = &pdev->dev;
  74. int ret = 0, idx = 0, subsys_cnt = 0;
  75. struct device_node *np = dev->of_node, *subsys_np = NULL;
  76. subsys_cnt = of_get_available_child_count(np);
  77. if (!subsys_cnt) {
  78. dev_err(dev, "No child node to process\n");
  79. return -EFAULT;
  80. }
  81. cdev_instances = devm_kcalloc(dev, subsys_cnt, sizeof(*cdev_instances),
  82. GFP_KERNEL);
  83. if (!cdev_instances)
  84. return -ENOMEM;
  85. inst_cnt = subsys_cnt;
  86. for_each_available_child_of_node(np, subsys_np) {
  87. if (idx >= subsys_cnt) {
  88. of_node_put(subsys_np);
  89. break;
  90. }
  91. ret = of_property_read_u32(subsys_np, "qcom,max-level",
  92. &cdev_instances[idx].max_level);
  93. if (ret) {
  94. dev_err(dev, "error reading qcom,max-level. ret:%d\n",
  95. ret);
  96. goto probe_error;
  97. }
  98. cdev_instances[idx].np = subsys_np;
  99. strscpy(cdev_instances[idx].cdev_name, subsys_np->name,
  100. THERMAL_NAME_LENGTH);
  101. cdev_instances[idx].cdev = thermal_of_cooling_device_register(
  102. subsys_np,
  103. cdev_instances[idx].cdev_name,
  104. &cdev_instances[idx],
  105. &userspace_cdev_ops);
  106. if (IS_ERR(cdev_instances[idx].cdev)) {
  107. dev_err(dev, "Error registering cdev:%s err:%d\n",
  108. cdev_instances[idx].cdev_name,
  109. PTR_ERR(cdev_instances[idx].cdev));
  110. cdev_instances[idx].cdev = NULL;
  111. goto probe_error;
  112. }
  113. dev_info(dev, "cdev:%s lvl:%d registered\n",
  114. cdev_instances[idx].cdev_name,
  115. cdev_instances[idx].max_level);
  116. idx++;
  117. }
  118. of_node_put(np);
  119. return 0;
  120. probe_error:
  121. of_node_put(subsys_np);
  122. of_node_put(np);
  123. inst_cnt = idx;
  124. userspace_cdev_cleanup();
  125. return ret;
  126. }
  127. static int userspace_device_remove(struct platform_device *pdev)
  128. {
  129. userspace_cdev_cleanup();
  130. return 0;
  131. }
  132. static const struct of_device_id userspace_device_match[] = {
  133. {.compatible = "qcom,userspace-cooling-devices"},
  134. {}
  135. };
  136. static struct platform_driver userspace_cdev_driver = {
  137. .probe = userspace_device_probe,
  138. .remove = userspace_device_remove,
  139. .driver = {
  140. .name = USERSPACE_CDEV_DRIVER,
  141. .of_match_table = userspace_device_match,
  142. },
  143. };
  144. static int __init userspace_cdev_init(void)
  145. {
  146. return platform_driver_register(&userspace_cdev_driver);
  147. }
  148. module_init(userspace_cdev_init);
  149. static void __exit userspace_cdev_exit(void)
  150. {
  151. platform_driver_unregister(&userspace_cdev_driver);
  152. }
  153. module_exit(userspace_cdev_exit);
  154. MODULE_LICENSE("GPL");
  155. MODULE_DESCRIPTION("Userspace cooling device driver");