qti_epm_interface.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
  4. */
  5. #define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
  6. #include <linux/module.h>
  7. #include <linux/of.h>
  8. #include <linux/platform_device.h>
  9. #include <linux/slab.h>
  10. #include "qti_epm.h"
  11. #define EPM_HW "qti-epm-hw"
  12. static inline struct epm_device_pz_data *to_epm_dev_pz(struct powercap_zone *pz)
  13. {
  14. return container_of(pz, struct epm_device_pz_data, pz);
  15. }
  16. static const char * const constraint_name[] = {
  17. "dummy",
  18. };
  19. static int epm_get_temp(struct thermal_zone_device *tz, int *temp)
  20. {
  21. struct epm_tz_device *epm_tz = (struct epm_tz_device *)tz->devdata;
  22. struct epm_priv *epm = epm_tz->priv;
  23. return epm->ops->get_temp(epm_tz, temp);
  24. }
  25. static const struct thermal_zone_device_ops epm_thermal_of_ops = {
  26. .get_temp = epm_get_temp,
  27. };
  28. static int thermal_zone_register(struct epm_priv *epm)
  29. {
  30. int idx;
  31. struct epm_tz_device *epm_tz = NULL;
  32. for (idx = 0; idx < epm->dt_tz_cnt; idx++) {
  33. epm_tz = &epm->epm_tz[idx];
  34. epm_tz->tz = devm_thermal_of_zone_register(
  35. epm->dev, idx, &epm->epm_tz[idx],
  36. &epm_thermal_of_ops);
  37. if (IS_ERR(epm_tz->tz)) {
  38. epm_tz->tz = NULL;
  39. continue;
  40. }
  41. }
  42. return 0;
  43. }
  44. static int epm_get_time_window_us(struct powercap_zone *pcz, int cid, u64 *window)
  45. {
  46. return -EOPNOTSUPP;
  47. }
  48. static int epm_set_time_window_us(struct powercap_zone *pcz, int cid, u64 window)
  49. {
  50. return -EOPNOTSUPP;
  51. }
  52. static int epm_get_max_power_range_uw(struct powercap_zone *pcz, u64 *max_power_uw)
  53. {
  54. struct epm_device_pz_data *epm_pz = to_epm_dev_pz(pcz);
  55. struct epm_device *epm_dev = epm_pz->epm_dev;
  56. struct epm_priv *epm = epm_dev->priv;
  57. if (epm->ops->get_max_power)
  58. epm->ops->get_max_power(epm_dev, max_power_uw);
  59. return 0;
  60. }
  61. static int epm_get_power_uw(struct powercap_zone *pcz, u64 *power_uw)
  62. {
  63. struct epm_device_pz_data *epm_pz = to_epm_dev_pz(pcz);
  64. struct epm_device *epm_dev = epm_pz->epm_dev;
  65. struct epm_priv *epm = epm_dev->priv;
  66. if (epm->ops->get_power)
  67. epm->ops->get_power(epm_dev, epm_pz->type, power_uw);
  68. else
  69. return -EOPNOTSUPP;
  70. return 0;
  71. }
  72. static int epm_release_zone(struct powercap_zone *pcz)
  73. {
  74. struct epm_device_pz_data *epm_pz = to_epm_dev_pz(pcz);
  75. struct epm_device *epm_dev = epm_pz->epm_dev;
  76. struct epm_priv *epm = epm_dev->priv;
  77. if (epm->ops->release)
  78. epm->ops->release(epm);
  79. return 0;
  80. }
  81. static int epm_get_power_limit_uw(struct powercap_zone *pcz,
  82. int cid, u64 *power_limit)
  83. {
  84. return -EOPNOTSUPP;
  85. }
  86. static int epm_set_power_limit_uw(struct powercap_zone *pcz,
  87. int cid, u64 power_limit)
  88. {
  89. return -EOPNOTSUPP;
  90. }
  91. static const char *get_constraint_name(struct powercap_zone *pcz, int cid)
  92. {
  93. return constraint_name[cid];
  94. }
  95. static int epm_get_max_power_uw(struct powercap_zone *pcz, int id, u64 *max_power)
  96. {
  97. struct epm_device_pz_data *epm_pz = to_epm_dev_pz(pcz);
  98. struct epm_device *epm_dev = epm_pz->epm_dev;
  99. struct epm_priv *epm = epm_dev->priv;
  100. if (epm->ops->get_max_power)
  101. return epm->ops->get_max_power(epm_dev, max_power);
  102. else
  103. return -EOPNOTSUPP;
  104. }
  105. static struct powercap_zone_constraint_ops constraint_ops = {
  106. .set_power_limit_uw = epm_set_power_limit_uw,
  107. .get_power_limit_uw = epm_get_power_limit_uw,
  108. .set_time_window_us = epm_set_time_window_us,
  109. .get_time_window_us = epm_get_time_window_us,
  110. .get_max_power_uw = epm_get_max_power_uw,
  111. .get_name = get_constraint_name,
  112. };
  113. static struct powercap_zone_ops zone_ops = {
  114. .get_max_power_range_uw = epm_get_max_power_range_uw,
  115. .get_power_uw = epm_get_power_uw,
  116. .release = epm_release_zone,
  117. };
  118. static int powercap_register(struct epm_priv *epm)
  119. {
  120. struct epm_device *epm_dev;
  121. struct powercap_zone *pcz = NULL;
  122. epm->pct = powercap_register_control_type(NULL, "epm", NULL);
  123. if (IS_ERR(epm->pct)) {
  124. dev_err(epm->dev, "Failed to register control type\n");
  125. return PTR_ERR(epm->pct);
  126. }
  127. list_for_each_entry(epm_dev, &epm->epm_dev_head, epm_node) {
  128. if (!epm_dev->enabled)
  129. continue;
  130. epm_dev->epm_pz[EPM_10S_AVG_DATA].type = EPM_10S_AVG_DATA;
  131. epm_dev->epm_pz[EPM_10S_AVG_DATA].epm_dev = epm_dev;
  132. pcz = powercap_register_zone(
  133. &epm_dev->epm_pz[EPM_10S_AVG_DATA].pz,
  134. epm->pct, epm_dev->name, NULL, &zone_ops, 1,
  135. &constraint_ops);
  136. if (IS_ERR(pcz))
  137. return PTR_ERR(pcz);
  138. }
  139. return 0;
  140. }
  141. static int epm_hw_device_probe(struct platform_device *pdev)
  142. {
  143. int ret;
  144. struct epm_priv *epm;
  145. epm = devm_kzalloc(&pdev->dev, sizeof(*epm), GFP_KERNEL);
  146. if (!epm)
  147. return -ENOMEM;
  148. epm->dev = &pdev->dev;
  149. epm->ops = &epm_hw_ops;
  150. platform_set_drvdata(pdev, epm);
  151. epm->ipc_log = ipc_log_context_create(IPC_LOGPAGES, "qti_epm", 0);
  152. if (!epm->ipc_log)
  153. dev_err(epm->dev, "%s: unable to create IPC Logging for %s\n",
  154. __func__, "qti_epm");
  155. if (!epm->ops || !epm->ops->init || !epm->ops->get_mode ||
  156. !epm->ops->get_power || !epm->ops->release)
  157. return -EINVAL;
  158. ret = epm->ops->init(epm);
  159. if (ret < 0) {
  160. dev_err(&pdev->dev, "%s: init failed\n", __func__);
  161. return ret;
  162. }
  163. switch (epm->ops->get_mode(epm)) {
  164. case EPM_ACAT_MODE:
  165. ret = powercap_register(epm);
  166. break;
  167. default:
  168. break;
  169. }
  170. if (epm->dt_tz_cnt)
  171. thermal_zone_register(epm);
  172. return ret;
  173. }
  174. static int epm_hw_device_remove(struct platform_device *pdev)
  175. {
  176. struct epm_priv *epm = platform_get_drvdata(pdev);
  177. struct epm_device *epm_dev;
  178. list_for_each_entry(epm_dev, &epm->epm_dev_head, epm_node) {
  179. if (epm->pct) {
  180. powercap_unregister_zone(epm->pct,
  181. &epm_dev->epm_pz[EPM_1S_DATA].pz);
  182. powercap_unregister_zone(epm->pct,
  183. &epm_dev->epm_pz[EPM_10S_AVG_DATA].pz);
  184. }
  185. }
  186. if (epm->pct)
  187. powercap_unregister_control_type(epm->pct);
  188. if (epm->ops->release)
  189. epm->ops->release(epm);
  190. return 0;
  191. }
  192. static const struct of_device_id epm_hw_device_match[] = {
  193. {.compatible = "qcom,epm-devices"},
  194. {}
  195. };
  196. static struct platform_driver epm_hw_device_driver = {
  197. .probe = epm_hw_device_probe,
  198. .remove = epm_hw_device_remove,
  199. .driver = {
  200. .name = EPM_HW,
  201. .of_match_table = epm_hw_device_match,
  202. },
  203. };
  204. static int __init epm_hw_device_init(void)
  205. {
  206. return platform_driver_register(&epm_hw_device_driver);
  207. }
  208. module_init(epm_hw_device_init);
  209. static void __exit epm_hw_device_exit(void)
  210. {
  211. platform_driver_unregister(&epm_hw_device_driver);
  212. }
  213. module_exit(epm_hw_device_exit);
  214. MODULE_DESCRIPTION("Qualcomm Technologies, Inc. EPM Hardware driver");
  215. MODULE_LICENSE("GPL");