ipa_rm_inactivity_timer.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2013-2018, 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. struct wakeup_source *pwlock;
  102. char *name;
  103. IPA_RM_DBG_LOW("resource %d\n", resource_name);
  104. if (resource_name < 0 ||
  105. resource_name >= IPA_RM_RESOURCE_MAX) {
  106. IPA_RM_ERR("Invalid parameter\n");
  107. return -EINVAL;
  108. }
  109. if (ipa_rm_it_handles[resource_name].initied) {
  110. IPA_RM_ERR("resource %d already inited\n", resource_name);
  111. return -EINVAL;
  112. }
  113. spin_lock_init(&ipa_rm_it_handles[resource_name].lock);
  114. ipa_rm_it_handles[resource_name].resource_name = resource_name;
  115. ipa_rm_it_handles[resource_name].jiffies = msecs_to_jiffies(msecs);
  116. ipa_rm_it_handles[resource_name].resource_requested = false;
  117. ipa_rm_it_handles[resource_name].reschedule_work = false;
  118. ipa_rm_it_handles[resource_name].work_in_progress = false;
  119. pwlock = &(ipa_rm_it_handles[resource_name].w_lock);
  120. name = ipa_rm_it_handles[resource_name].w_lock_name;
  121. snprintf(name, MAX_WS_NAME, "IPA_RM%d\n", resource_name);
  122. wakeup_source_init(pwlock, name);
  123. INIT_DELAYED_WORK(&ipa_rm_it_handles[resource_name].work,
  124. ipa_rm_inactivity_timer_func);
  125. ipa_rm_it_handles[resource_name].initied = true;
  126. return 0;
  127. }
  128. EXPORT_SYMBOL(ipa_rm_inactivity_timer_init);
  129. /**
  130. * ipa_rm_inactivity_timer_destroy() - De-Init function for IPA
  131. * RM inactivity timer.
  132. * @resource_name: Resource name. @see ipa_rm.h
  133. * Return codes:
  134. * 0: success
  135. * -EINVAL: invalid parameters
  136. */
  137. int ipa_rm_inactivity_timer_destroy(enum ipa_rm_resource_name resource_name)
  138. {
  139. struct wakeup_source *pwlock;
  140. IPA_RM_DBG_LOW("resource %d\n", resource_name);
  141. if (resource_name < 0 ||
  142. resource_name >= IPA_RM_RESOURCE_MAX) {
  143. IPA_RM_ERR("Invalid parameter\n");
  144. return -EINVAL;
  145. }
  146. if (!ipa_rm_it_handles[resource_name].initied) {
  147. IPA_RM_ERR("resource %d already inited\n",
  148. resource_name);
  149. return -EINVAL;
  150. }
  151. cancel_delayed_work_sync(&ipa_rm_it_handles[resource_name].work);
  152. pwlock = &(ipa_rm_it_handles[resource_name].w_lock);
  153. wakeup_source_trash(pwlock);
  154. memset(&ipa_rm_it_handles[resource_name], 0,
  155. sizeof(struct ipa_rm_it_private));
  156. return 0;
  157. }
  158. EXPORT_SYMBOL(ipa_rm_inactivity_timer_destroy);
  159. /**
  160. * ipa_rm_inactivity_timer_request_resource() - Same as
  161. * ipa_rm_request_resource(), with a difference that calling to
  162. * this function will also cancel the inactivity timer, if
  163. * ipa_rm_inactivity_timer_release_resource() was called earlier.
  164. *
  165. * @resource_name: Resource name. @see ipa_rm.h
  166. *
  167. * Return codes:
  168. * 0: success
  169. * -EINVAL: invalid parameters
  170. */
  171. int ipa_rm_inactivity_timer_request_resource(
  172. enum ipa_rm_resource_name resource_name)
  173. {
  174. int ret;
  175. unsigned long flags;
  176. IPA_RM_DBG_LOW("resource %d\n", resource_name);
  177. if (resource_name < 0 ||
  178. resource_name >= IPA_RM_RESOURCE_MAX) {
  179. IPA_RM_ERR("Invalid parameter\n");
  180. return -EINVAL;
  181. }
  182. if (!ipa_rm_it_handles[resource_name].initied) {
  183. IPA_RM_ERR("Not initialized\n");
  184. return -EINVAL;
  185. }
  186. spin_lock_irqsave(&ipa_rm_it_handles[resource_name].lock, flags);
  187. ipa_rm_it_handles[resource_name].resource_requested = true;
  188. spin_unlock_irqrestore(&ipa_rm_it_handles[resource_name].lock, flags);
  189. ret = ipa_rm_request_resource(resource_name);
  190. IPA_RM_DBG_LOW("resource %d: returning %d\n", resource_name, ret);
  191. return ret;
  192. }
  193. EXPORT_SYMBOL(ipa_rm_inactivity_timer_request_resource);
  194. /**
  195. * ipa_rm_inactivity_timer_release_resource() - Sets the
  196. * inactivity timer to the timeout set by
  197. * ipa_rm_inactivity_timer_init(). When the timeout expires, IPA
  198. * RM inactivity timer will call to ipa_rm_release_resource().
  199. * If a call to ipa_rm_inactivity_timer_request_resource() was
  200. * made BEFORE the timeout has expired, rge timer will be
  201. * cancelled.
  202. *
  203. * @resource_name: Resource name. @see ipa_rm.h
  204. *
  205. * Return codes:
  206. * 0: success
  207. * -EINVAL: invalid parameters
  208. */
  209. int ipa_rm_inactivity_timer_release_resource(
  210. enum ipa_rm_resource_name resource_name)
  211. {
  212. unsigned long flags;
  213. IPA_RM_DBG_LOW("resource %d\n", resource_name);
  214. if (resource_name < 0 ||
  215. resource_name >= IPA_RM_RESOURCE_MAX) {
  216. IPA_RM_ERR("Invalid parameter\n");
  217. return -EINVAL;
  218. }
  219. if (!ipa_rm_it_handles[resource_name].initied) {
  220. IPA_RM_ERR("Not initialized\n");
  221. return -EINVAL;
  222. }
  223. spin_lock_irqsave(&ipa_rm_it_handles[resource_name].lock, flags);
  224. ipa_rm_it_handles[resource_name].resource_requested = false;
  225. if (ipa_rm_it_handles[resource_name].work_in_progress) {
  226. IPA_RM_DBG_LOW("Timer already set, no sched again %d\n",
  227. resource_name);
  228. ipa_rm_it_handles[resource_name].reschedule_work = true;
  229. spin_unlock_irqrestore(
  230. &ipa_rm_it_handles[resource_name].lock, flags);
  231. return 0;
  232. }
  233. ipa_rm_it_handles[resource_name].work_in_progress = true;
  234. ipa_rm_it_handles[resource_name].reschedule_work = false;
  235. __pm_stay_awake(&ipa_rm_it_handles[resource_name].w_lock);
  236. IPA_RM_DBG_LOW("setting delayed work\n");
  237. queue_delayed_work(system_unbound_wq,
  238. &ipa_rm_it_handles[resource_name].work,
  239. ipa_rm_it_handles[resource_name].jiffies);
  240. spin_unlock_irqrestore(&ipa_rm_it_handles[resource_name].lock, flags);
  241. return 0;
  242. }
  243. EXPORT_SYMBOL(ipa_rm_inactivity_timer_release_resource);