ipa_rm_inactivity_timer.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
  4. */
  5. #include <linux/jiffies.h>
  6. #include <linux/kernel.h>
  7. #include <linux/slab.h>
  8. #include <linux/spinlock.h>
  9. #include <linux/timer.h>
  10. #include <linux/unistd.h>
  11. #include <linux/workqueue.h>
  12. #include <linux/ipa.h>
  13. #include "ipa_rm_i.h"
  14. #define MAX_WS_NAME 20
  15. /**
  16. * struct ipa_rm_it_private - IPA RM Inactivity Timer private
  17. * data
  18. * @initied: indicates if instance was initialized
  19. * @lock - spinlock for mutual exclusion
  20. * @resource_name - resource name
  21. * @work: delayed work object for running delayed releas
  22. * function
  23. * @resource_requested: boolean flag indicates if resource was requested
  24. * @reschedule_work: boolean flag indicates to not release and to
  25. * reschedule the release work.
  26. * @work_in_progress: boolean flag indicates is release work was scheduled.
  27. * @jiffies: number of jiffies for timeout
  28. *
  29. * WWAN private - holds all relevant info about WWAN driver
  30. */
  31. struct ipa_rm_it_private {
  32. bool initied;
  33. enum ipa_rm_resource_name resource_name;
  34. spinlock_t lock;
  35. struct delayed_work work;
  36. bool resource_requested;
  37. bool reschedule_work;
  38. bool work_in_progress;
  39. unsigned long jiffies;
  40. struct wakeup_source *w_lock;
  41. char w_lock_name[MAX_WS_NAME];
  42. };
  43. static struct ipa_rm_it_private ipa_rm_it_handles[IPA_RM_RESOURCE_MAX];
  44. /**
  45. * ipa_rm_inactivity_timer_func() - called when timer expired in
  46. * the context of the shared workqueue. Checks internally if
  47. * reschedule_work flag is set. In case it is not set this function calls to
  48. * ipa_rm_release_resource(). In case reschedule_work is set this function
  49. * reschedule the work. This flag is cleared cleared when
  50. * calling to ipa_rm_inactivity_timer_release_resource().
  51. *
  52. * @work: work object provided by the work queue
  53. *
  54. * Return codes:
  55. * None
  56. */
  57. static void ipa_rm_inactivity_timer_func(struct work_struct *work)
  58. {
  59. struct ipa_rm_it_private *me = container_of(to_delayed_work(work),
  60. struct ipa_rm_it_private,
  61. work);
  62. unsigned long flags;
  63. IPA_RM_DBG_LOW("timer expired for resource %d\n", me->resource_name);
  64. spin_lock_irqsave(
  65. &ipa_rm_it_handles[me->resource_name].lock, flags);
  66. if (ipa_rm_it_handles[me->resource_name].reschedule_work) {
  67. IPA_RM_DBG_LOW("setting delayed work\n");
  68. ipa_rm_it_handles[me->resource_name].reschedule_work = false;
  69. queue_delayed_work(system_unbound_wq,
  70. &ipa_rm_it_handles[me->resource_name].work,
  71. ipa_rm_it_handles[me->resource_name].jiffies);
  72. } else if (ipa_rm_it_handles[me->resource_name].resource_requested) {
  73. IPA_RM_DBG_LOW("not calling release\n");
  74. ipa_rm_it_handles[me->resource_name].work_in_progress = false;
  75. } else {
  76. IPA_RM_DBG_LOW("calling release_resource on resource %d\n",
  77. me->resource_name);
  78. __pm_relax(ipa_rm_it_handles[me->resource_name].w_lock);
  79. ipa_rm_release_resource(me->resource_name);
  80. ipa_rm_it_handles[me->resource_name].work_in_progress = false;
  81. }
  82. spin_unlock_irqrestore(
  83. &ipa_rm_it_handles[me->resource_name].lock, flags);
  84. }
  85. /**
  86. * ipa_rm_inactivity_timer_init() - Init function for IPA RM
  87. * inactivity timer. This function shall be called prior calling
  88. * any other API of IPA RM inactivity timer.
  89. *
  90. * @resource_name: Resource name. @see ipa_rm.h
  91. * @msecs: time in miliseccond, that IPA RM inactivity timer
  92. * shall wait prior calling to ipa_rm_release_resource().
  93. *
  94. * Return codes:
  95. * 0: success
  96. * -EINVAL: invalid parameters
  97. */
  98. int ipa_rm_inactivity_timer_init(enum ipa_rm_resource_name resource_name,
  99. unsigned long msecs)
  100. {
  101. char *name;
  102. IPA_RM_DBG_LOW("resource %d\n", resource_name);
  103. if (resource_name < 0 ||
  104. resource_name >= IPA_RM_RESOURCE_MAX) {
  105. IPA_RM_ERR("Invalid parameter\n");
  106. return -EINVAL;
  107. }
  108. if (ipa_rm_it_handles[resource_name].initied) {
  109. IPA_RM_ERR("resource %d already inited\n", resource_name);
  110. return -EINVAL;
  111. }
  112. spin_lock_init(&ipa_rm_it_handles[resource_name].lock);
  113. ipa_rm_it_handles[resource_name].resource_name = resource_name;
  114. ipa_rm_it_handles[resource_name].jiffies = msecs_to_jiffies(msecs);
  115. ipa_rm_it_handles[resource_name].resource_requested = false;
  116. ipa_rm_it_handles[resource_name].reschedule_work = false;
  117. ipa_rm_it_handles[resource_name].work_in_progress = false;
  118. name = ipa_rm_it_handles[resource_name].w_lock_name;
  119. snprintf(name, MAX_WS_NAME, "IPA_RM%d\n", resource_name);
  120. ipa_rm_it_handles[resource_name].w_lock =
  121. wakeup_source_register(NULL, name);
  122. if (!ipa_rm_it_handles[resource_name].w_lock) {
  123. IPA_RM_ERR("IPA wakeup source register failed %s\n",
  124. name);
  125. return -ENOMEM;
  126. }
  127. INIT_DELAYED_WORK(&ipa_rm_it_handles[resource_name].work,
  128. ipa_rm_inactivity_timer_func);
  129. ipa_rm_it_handles[resource_name].initied = true;
  130. return 0;
  131. }
  132. EXPORT_SYMBOL(ipa_rm_inactivity_timer_init);
  133. /**
  134. * ipa_rm_inactivity_timer_destroy() - De-Init function for IPA
  135. * RM inactivity timer.
  136. * @resource_name: Resource name. @see ipa_rm.h
  137. * Return codes:
  138. * 0: success
  139. * -EINVAL: invalid parameters
  140. */
  141. int ipa_rm_inactivity_timer_destroy(enum ipa_rm_resource_name resource_name)
  142. {
  143. IPA_RM_DBG_LOW("resource %d\n", resource_name);
  144. if (resource_name < 0 ||
  145. resource_name >= IPA_RM_RESOURCE_MAX) {
  146. IPA_RM_ERR("Invalid parameter\n");
  147. return -EINVAL;
  148. }
  149. if (!ipa_rm_it_handles[resource_name].initied) {
  150. IPA_RM_ERR("resource %d already inited\n",
  151. resource_name);
  152. return -EINVAL;
  153. }
  154. cancel_delayed_work_sync(&ipa_rm_it_handles[resource_name].work);
  155. wakeup_source_unregister(ipa_rm_it_handles[resource_name].w_lock);
  156. memset(&ipa_rm_it_handles[resource_name], 0,
  157. sizeof(struct ipa_rm_it_private));
  158. return 0;
  159. }
  160. EXPORT_SYMBOL(ipa_rm_inactivity_timer_destroy);
  161. /**
  162. * ipa_rm_inactivity_timer_request_resource() - Same as
  163. * ipa_rm_request_resource(), with a difference that calling to
  164. * this function will also cancel the inactivity timer, if
  165. * ipa_rm_inactivity_timer_release_resource() was called earlier.
  166. *
  167. * @resource_name: Resource name. @see ipa_rm.h
  168. *
  169. * Return codes:
  170. * 0: success
  171. * -EINVAL: invalid parameters
  172. */
  173. int ipa_rm_inactivity_timer_request_resource(
  174. enum ipa_rm_resource_name resource_name)
  175. {
  176. int ret;
  177. unsigned long flags;
  178. IPA_RM_DBG_LOW("resource %d\n", resource_name);
  179. if (resource_name < 0 ||
  180. resource_name >= IPA_RM_RESOURCE_MAX) {
  181. IPA_RM_ERR("Invalid parameter\n");
  182. return -EINVAL;
  183. }
  184. if (!ipa_rm_it_handles[resource_name].initied) {
  185. IPA_RM_ERR("Not initialized\n");
  186. return -EINVAL;
  187. }
  188. spin_lock_irqsave(&ipa_rm_it_handles[resource_name].lock, flags);
  189. ipa_rm_it_handles[resource_name].resource_requested = true;
  190. spin_unlock_irqrestore(&ipa_rm_it_handles[resource_name].lock, flags);
  191. ret = ipa_rm_request_resource(resource_name);
  192. IPA_RM_DBG_LOW("resource %d: returning %d\n", resource_name, ret);
  193. return ret;
  194. }
  195. EXPORT_SYMBOL(ipa_rm_inactivity_timer_request_resource);
  196. /**
  197. * ipa_rm_inactivity_timer_release_resource() - Sets the
  198. * inactivity timer to the timeout set by
  199. * ipa_rm_inactivity_timer_init(). When the timeout expires, IPA
  200. * RM inactivity timer will call to ipa_rm_release_resource().
  201. * If a call to ipa_rm_inactivity_timer_request_resource() was
  202. * made BEFORE the timeout has expired, rge timer will be
  203. * cancelled.
  204. *
  205. * @resource_name: Resource name. @see ipa_rm.h
  206. *
  207. * Return codes:
  208. * 0: success
  209. * -EINVAL: invalid parameters
  210. */
  211. int ipa_rm_inactivity_timer_release_resource(
  212. enum ipa_rm_resource_name resource_name)
  213. {
  214. unsigned long flags;
  215. IPA_RM_DBG_LOW("resource %d\n", resource_name);
  216. if (resource_name < 0 ||
  217. resource_name >= IPA_RM_RESOURCE_MAX) {
  218. IPA_RM_ERR("Invalid parameter\n");
  219. return -EINVAL;
  220. }
  221. if (!ipa_rm_it_handles[resource_name].initied) {
  222. IPA_RM_ERR("Not initialized\n");
  223. return -EINVAL;
  224. }
  225. spin_lock_irqsave(&ipa_rm_it_handles[resource_name].lock, flags);
  226. ipa_rm_it_handles[resource_name].resource_requested = false;
  227. if (ipa_rm_it_handles[resource_name].work_in_progress) {
  228. IPA_RM_DBG_LOW("Timer already set, no sched again %d\n",
  229. resource_name);
  230. ipa_rm_it_handles[resource_name].reschedule_work = true;
  231. spin_unlock_irqrestore(
  232. &ipa_rm_it_handles[resource_name].lock, flags);
  233. return 0;
  234. }
  235. ipa_rm_it_handles[resource_name].work_in_progress = true;
  236. ipa_rm_it_handles[resource_name].reschedule_work = false;
  237. __pm_stay_awake(ipa_rm_it_handles[resource_name].w_lock);
  238. IPA_RM_DBG_LOW("setting delayed work\n");
  239. queue_delayed_work(system_unbound_wq,
  240. &ipa_rm_it_handles[resource_name].work,
  241. ipa_rm_it_handles[resource_name].jiffies);
  242. spin_unlock_irqrestore(&ipa_rm_it_handles[resource_name].lock, flags);
  243. return 0;
  244. }
  245. EXPORT_SYMBOL(ipa_rm_inactivity_timer_release_resource);