blob: 613bed3983e5230fa98869ba77298069bcf57bee [file] [log] [blame]
Skylar Chang3cf51852017-11-29 18:13:38 -08001/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
Amir Levy9659e592016-10-27 18:08:27 +03002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/jiffies.h>
14#include <linux/kernel.h>
15#include <linux/slab.h>
16#include <linux/spinlock.h>
17#include <linux/timer.h>
18#include <linux/unistd.h>
19#include <linux/workqueue.h>
20#include <linux/ipa.h>
21#include "ipa_rm_i.h"
22
Skylar Chang3cf51852017-11-29 18:13:38 -080023#define MAX_WS_NAME 20
24
Amir Levy9659e592016-10-27 18:08:27 +030025/**
26 * struct ipa_rm_it_private - IPA RM Inactivity Timer private
27 * data
28 * @initied: indicates if instance was initialized
29 * @lock - spinlock for mutual exclusion
30 * @resource_name - resource name
31 * @work: delayed work object for running delayed releas
32 * function
33 * @resource_requested: boolean flag indicates if resource was requested
34 * @reschedule_work: boolean flag indicates to not release and to
35 * reschedule the release work.
36 * @work_in_progress: boolean flag indicates is release work was scheduled.
37 * @jiffies: number of jiffies for timeout
38 *
39 * WWAN private - holds all relevant info about WWAN driver
40 */
41struct ipa_rm_it_private {
42 bool initied;
43 enum ipa_rm_resource_name resource_name;
44 spinlock_t lock;
45 struct delayed_work work;
46 bool resource_requested;
47 bool reschedule_work;
48 bool work_in_progress;
49 unsigned long jiffies;
Skylar Chang3cf51852017-11-29 18:13:38 -080050 struct wakeup_source w_lock;
51 char w_lock_name[MAX_WS_NAME];
Amir Levy9659e592016-10-27 18:08:27 +030052};
53
54static struct ipa_rm_it_private ipa_rm_it_handles[IPA_RM_RESOURCE_MAX];
55
56/**
57 * ipa_rm_inactivity_timer_func() - called when timer expired in
58 * the context of the shared workqueue. Checks internally if
59 * reschedule_work flag is set. In case it is not set this function calls to
60 * ipa_rm_release_resource(). In case reschedule_work is set this function
61 * reschedule the work. This flag is cleared cleared when
62 * calling to ipa_rm_inactivity_timer_release_resource().
63 *
64 * @work: work object provided by the work queue
65 *
66 * Return codes:
67 * None
68 */
69static void ipa_rm_inactivity_timer_func(struct work_struct *work)
70{
71
72 struct ipa_rm_it_private *me = container_of(to_delayed_work(work),
73 struct ipa_rm_it_private,
74 work);
75 unsigned long flags;
76
77 IPA_RM_DBG_LOW("%s: timer expired for resource %d!\n", __func__,
78 me->resource_name);
79
80 spin_lock_irqsave(
81 &ipa_rm_it_handles[me->resource_name].lock, flags);
82 if (ipa_rm_it_handles[me->resource_name].reschedule_work) {
83 IPA_RM_DBG_LOW("%s: setting delayed work\n", __func__);
84 ipa_rm_it_handles[me->resource_name].reschedule_work = false;
85 queue_delayed_work(system_unbound_wq,
86 &ipa_rm_it_handles[me->resource_name].work,
87 ipa_rm_it_handles[me->resource_name].jiffies);
88 } else if (ipa_rm_it_handles[me->resource_name].resource_requested) {
89 IPA_RM_DBG_LOW("%s: not calling release\n", __func__);
90 ipa_rm_it_handles[me->resource_name].work_in_progress = false;
91 } else {
92 IPA_RM_DBG_LOW("%s: calling release_resource on resource %d!\n",
93 __func__, me->resource_name);
Skylar Chang3cf51852017-11-29 18:13:38 -080094 __pm_relax(&ipa_rm_it_handles[me->resource_name].w_lock);
Amir Levy9659e592016-10-27 18:08:27 +030095 ipa_rm_release_resource(me->resource_name);
96 ipa_rm_it_handles[me->resource_name].work_in_progress = false;
97 }
98 spin_unlock_irqrestore(
99 &ipa_rm_it_handles[me->resource_name].lock, flags);
100}
101
102/**
103* ipa_rm_inactivity_timer_init() - Init function for IPA RM
104* inactivity timer. This function shall be called prior calling
105* any other API of IPA RM inactivity timer.
106*
107* @resource_name: Resource name. @see ipa_rm.h
108* @msecs: time in miliseccond, that IPA RM inactivity timer
109* shall wait prior calling to ipa_rm_release_resource().
110*
111* Return codes:
112* 0: success
113* -EINVAL: invalid parameters
114*/
115int ipa_rm_inactivity_timer_init(enum ipa_rm_resource_name resource_name,
116 unsigned long msecs)
117{
Skylar Chang3cf51852017-11-29 18:13:38 -0800118 struct wakeup_source *pwlock;
119 char *name;
120
Amir Levy9659e592016-10-27 18:08:27 +0300121 IPA_RM_DBG_LOW("%s: resource %d\n", __func__, resource_name);
122
123 if (resource_name < 0 ||
124 resource_name >= IPA_RM_RESOURCE_MAX) {
125 IPA_RM_ERR("%s: Invalid parameter\n", __func__);
126 return -EINVAL;
127 }
128
129 if (ipa_rm_it_handles[resource_name].initied) {
130 IPA_RM_ERR("%s: resource %d already inited\n",
131 __func__, resource_name);
132 return -EINVAL;
133 }
134
135 spin_lock_init(&ipa_rm_it_handles[resource_name].lock);
136 ipa_rm_it_handles[resource_name].resource_name = resource_name;
137 ipa_rm_it_handles[resource_name].jiffies = msecs_to_jiffies(msecs);
138 ipa_rm_it_handles[resource_name].resource_requested = false;
139 ipa_rm_it_handles[resource_name].reschedule_work = false;
140 ipa_rm_it_handles[resource_name].work_in_progress = false;
Skylar Chang3cf51852017-11-29 18:13:38 -0800141 pwlock = &(ipa_rm_it_handles[resource_name].w_lock);
142 name = ipa_rm_it_handles[resource_name].w_lock_name;
143 snprintf(name, MAX_WS_NAME, "IPA_RM%d\n", resource_name);
144 wakeup_source_init(pwlock, name);
Amir Levy9659e592016-10-27 18:08:27 +0300145 INIT_DELAYED_WORK(&ipa_rm_it_handles[resource_name].work,
146 ipa_rm_inactivity_timer_func);
147 ipa_rm_it_handles[resource_name].initied = 1;
148
149 return 0;
150}
151EXPORT_SYMBOL(ipa_rm_inactivity_timer_init);
152
153/**
154* ipa_rm_inactivity_timer_destroy() - De-Init function for IPA
155* RM inactivity timer.
156*
157* @resource_name: Resource name. @see ipa_rm.h
158*
159* Return codes:
160* 0: success
161* -EINVAL: invalid parameters
162*/
163int ipa_rm_inactivity_timer_destroy(enum ipa_rm_resource_name resource_name)
164{
Skylar Chang3cf51852017-11-29 18:13:38 -0800165 struct wakeup_source *pwlock;
166
Amir Levy9659e592016-10-27 18:08:27 +0300167 IPA_RM_DBG_LOW("%s: resource %d\n", __func__, resource_name);
168
169 if (resource_name < 0 ||
170 resource_name >= IPA_RM_RESOURCE_MAX) {
171 IPA_RM_ERR("%s: Invalid parameter\n", __func__);
172 return -EINVAL;
173 }
174
175 if (!ipa_rm_it_handles[resource_name].initied) {
176 IPA_RM_ERR("%s: resource %d already inited\n",
177 __func__, resource_name);
178 return -EINVAL;
179 }
180
181 cancel_delayed_work_sync(&ipa_rm_it_handles[resource_name].work);
Skylar Chang3cf51852017-11-29 18:13:38 -0800182 pwlock = &(ipa_rm_it_handles[resource_name].w_lock);
183 wakeup_source_trash(pwlock);
Amir Levy9659e592016-10-27 18:08:27 +0300184
185 memset(&ipa_rm_it_handles[resource_name], 0,
186 sizeof(struct ipa_rm_it_private));
187
188 return 0;
189}
190EXPORT_SYMBOL(ipa_rm_inactivity_timer_destroy);
191
192/**
193* ipa_rm_inactivity_timer_request_resource() - Same as
194* ipa_rm_request_resource(), with a difference that calling to
195* this function will also cancel the inactivity timer, if
196* ipa_rm_inactivity_timer_release_resource() was called earlier.
197*
198* @resource_name: Resource name. @see ipa_rm.h
199*
200* Return codes:
201* 0: success
202* -EINVAL: invalid parameters
203*/
204int ipa_rm_inactivity_timer_request_resource(
205 enum ipa_rm_resource_name resource_name)
206{
207 int ret;
208 unsigned long flags;
209
210 IPA_RM_DBG_LOW("%s: resource %d\n", __func__, resource_name);
211
212 if (resource_name < 0 ||
213 resource_name >= IPA_RM_RESOURCE_MAX) {
214 IPA_RM_ERR("%s: Invalid parameter\n", __func__);
215 return -EINVAL;
216 }
217
218 if (!ipa_rm_it_handles[resource_name].initied) {
219 IPA_RM_ERR("%s: Not initialized\n", __func__);
220 return -EINVAL;
221 }
222
223 spin_lock_irqsave(&ipa_rm_it_handles[resource_name].lock, flags);
224 ipa_rm_it_handles[resource_name].resource_requested = true;
225 spin_unlock_irqrestore(&ipa_rm_it_handles[resource_name].lock, flags);
226 ret = ipa_rm_request_resource(resource_name);
227 IPA_RM_DBG_LOW("%s: resource %d: returning %d\n", __func__,
228 resource_name, ret);
229
230 return ret;
231}
232EXPORT_SYMBOL(ipa_rm_inactivity_timer_request_resource);
233
234/**
235* ipa_rm_inactivity_timer_release_resource() - Sets the
236* inactivity timer to the timeout set by
237* ipa_rm_inactivity_timer_init(). When the timeout expires, IPA
238* RM inactivity timer will call to ipa_rm_release_resource().
239* If a call to ipa_rm_inactivity_timer_request_resource() was
240* made BEFORE the timout has expired, rge timer will be
241* cancelled.
242*
243* @resource_name: Resource name. @see ipa_rm.h
244*
245* Return codes:
246* 0: success
247* -EINVAL: invalid parameters
248*/
249int ipa_rm_inactivity_timer_release_resource(
250 enum ipa_rm_resource_name resource_name)
251{
252 unsigned long flags;
253
254 IPA_RM_DBG_LOW("%s: resource %d\n", __func__, resource_name);
255
256 if (resource_name < 0 ||
257 resource_name >= IPA_RM_RESOURCE_MAX) {
258 IPA_RM_ERR("%s: Invalid parameter\n", __func__);
259 return -EINVAL;
260 }
261
262 if (!ipa_rm_it_handles[resource_name].initied) {
263 IPA_RM_ERR("%s: Not initialized\n", __func__);
264 return -EINVAL;
265 }
266
267 spin_lock_irqsave(&ipa_rm_it_handles[resource_name].lock, flags);
268 ipa_rm_it_handles[resource_name].resource_requested = false;
269 if (ipa_rm_it_handles[resource_name].work_in_progress) {
270 IPA_RM_DBG_LOW("%s: Timer already set, no sched again %d\n",
271 __func__, resource_name);
272 ipa_rm_it_handles[resource_name].reschedule_work = true;
273 spin_unlock_irqrestore(
274 &ipa_rm_it_handles[resource_name].lock, flags);
275 return 0;
276 }
277 ipa_rm_it_handles[resource_name].work_in_progress = true;
278 ipa_rm_it_handles[resource_name].reschedule_work = false;
Skylar Chang3cf51852017-11-29 18:13:38 -0800279 __pm_stay_awake(&ipa_rm_it_handles[resource_name].w_lock);
Amir Levy9659e592016-10-27 18:08:27 +0300280 IPA_RM_DBG_LOW("%s: setting delayed work\n", __func__);
281 queue_delayed_work(system_unbound_wq,
282 &ipa_rm_it_handles[resource_name].work,
283 ipa_rm_it_handles[resource_name].jiffies);
284 spin_unlock_irqrestore(&ipa_rm_it_handles[resource_name].lock, flags);
285
286 return 0;
287}
288EXPORT_SYMBOL(ipa_rm_inactivity_timer_release_resource);
289