blob: 140afa8fd5b85f8fc3cdd8e69a5d61977053756f [file] [log] [blame]
Michael Adisumartaf0424822018-01-15 16:15:33 -08001/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
Michael Adisumartac06df412017-09-19 10:10:35 -07002 *
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/debugfs.h>
14#include "ipa_pm.h"
15#include "ipa_i.h"
16
17static const char *client_state_to_str[IPA_PM_STATE_MAX] = {
18 __stringify(IPA_PM_DEACTIVATED),
19 __stringify(IPA_PM_DEACTIVATE_IN_PROGRESS),
20 __stringify(IPA_PM_ACTIVATE_IN_PROGRESS),
21 __stringify(IPA_PM_ACTIVATED),
22 __stringify(IPA_PM_ACTIVATED_PENDING_DEACTIVATION),
23 __stringify(IPA_PM_ACTIVATED_TIMER_SET),
24 __stringify(IPA_PM_ACTIVATED_PENDING_RESCHEDULE),
25};
26
27static const char *ipa_pm_group_to_str[IPA_PM_GROUP_MAX] = {
28 __stringify(IPA_PM_GROUP_DEFAULT),
29 __stringify(IPA_PM_GROUP_APPS),
30 __stringify(IPA_PM_GROUP_MODEM),
31};
32
33
34#define IPA_PM_DRV_NAME "ipa_pm"
35
36#define IPA_PM_DBG(fmt, args...) \
37 do { \
38 pr_debug(IPA_PM_DRV_NAME " %s:%d " fmt, \
39 __func__, __LINE__, ## args); \
40 IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
41 IPA_PM_DRV_NAME " %s:%d " fmt, ## args); \
42 IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
43 IPA_PM_DRV_NAME " %s:%d " fmt, ## args); \
44 } while (0)
45#define IPA_PM_DBG_LOW(fmt, args...) \
46 do { \
47 pr_debug(IPA_PM_DRV_NAME " %s:%d " fmt, \
48 __func__, __LINE__, ## args); \
49 IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
50 IPA_PM_DRV_NAME " %s:%d " fmt, ## args); \
51 } while (0)
52#define IPA_PM_ERR(fmt, args...) \
53 do { \
54 pr_err(IPA_PM_DRV_NAME " %s:%d " fmt, \
55 __func__, __LINE__, ## args); \
56 IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
57 IPA_PM_DRV_NAME " %s:%d " fmt, ## args); \
58 IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
59 IPA_PM_DRV_NAME " %s:%d " fmt, ## args); \
60 } while (0)
61#define IPA_PM_DBG_STATE(hdl, name, state) \
62 IPA_PM_DBG_LOW("Client[%d] %s: %s\n", hdl, name, \
63 client_state_to_str[state])
64
65
66#if IPA_PM_MAX_CLIENTS > 32
67#error max client greater than 32 all bitmask types should be changed
68#endif
69
70/*
71 * struct ipa_pm_exception_list - holds information about an exception
72 * @pending: number of clients in exception that have not yet been adctivated
73 * @bitmask: bitmask of the clients in the exception based on handle
74 * @threshold: the threshold values for the exception
75 */
76struct ipa_pm_exception_list {
77 char clients[IPA_PM_MAX_EX_CL];
78 int pending;
79 u32 bitmask;
80 int threshold[IPA_PM_THRESHOLD_MAX];
81};
82
83/*
84 * struct clk_scaling_db - holds information about threshholds and exceptions
85 * @lock: lock the bitmasks and thresholds
86 * @exception_list: pointer to the list of exceptions
87 * @work: work for clock scaling algorithm
88 * @active_client_bitmask: the bits represent handles in the clients array that
89 * contain non-null client
90 * @threshold_size: size of the throughput threshold
91 * @exception_size: size of the exception list
92 * @cur_vote: idx of the threshold
93 * @default_threshold: the thresholds used if no exception passes
94 * @current_threshold: the current threshold of the clock plan
95 */
96struct clk_scaling_db {
97 spinlock_t lock;
98 struct ipa_pm_exception_list exception_list[IPA_PM_EXCEPTION_MAX];
99 struct work_struct work;
100 u32 active_client_bitmask;
101 int threshold_size;
102 int exception_size;
103 int cur_vote;
104 int default_threshold[IPA_PM_THRESHOLD_MAX];
105 int *current_threshold;
106};
107
108/*
109 * ipa_pm state names
110 *
111 * Timer free states:
112 * @IPA_PM_DEACTIVATED: client starting state when registered
113 * @IPA_PM_DEACTIVATE_IN_PROGRESS: deactivate was called in progress of a client
114 * activating
115 * @IPA_PM_ACTIVATE_IN_PROGRESS: client is being activated by work_queue
116 * @IPA_PM_ACTIVATED: client is activated without any timers
117 *
118 * Timer set states:
119 * @IPA_PM_ACTIVATED_PENDING_DEACTIVATION: moves to deactivate once timer pass
120 * @IPA_PM_ACTIVATED_TIMER_SET: client was activated while timer was set, so
121 * when the timer pass, client will still be activated
122 *@IPA_PM_ACTIVATED_PENDING_RESCHEDULE: state signifying extended timer when
123 * a client is deferred_deactivated when a time ris still active
124 */
125enum ipa_pm_state {
126 IPA_PM_DEACTIVATED,
127 IPA_PM_DEACTIVATE_IN_PROGRESS,
128 IPA_PM_ACTIVATE_IN_PROGRESS,
129 IPA_PM_ACTIVATED,
130 IPA_PM_ACTIVATED_PENDING_DEACTIVATION,
131 IPA_PM_ACTIVATED_TIMER_SET,
132 IPA_PM_ACTIVATED_PENDING_RESCHEDULE,
133};
134
135#define IPA_PM_STATE_ACTIVE(state) \
136 (state == IPA_PM_ACTIVATED ||\
137 state == IPA_PM_ACTIVATED_PENDING_DEACTIVATION ||\
138 state == IPA_PM_ACTIVATED_TIMER_SET ||\
139 state == IPA_PM_ACTIVATED_PENDING_RESCHEDULE)
140
141#define IPA_PM_STATE_IN_PROGRESS(state) \
142 (state == IPA_PM_ACTIVATE_IN_PROGRESS \
143 || state == IPA_PM_DEACTIVATE_IN_PROGRESS)
144
145/*
146 * struct ipa_pm_client - holds information about a specific IPA client
147 * @name: string name of the client
148 * @callback: pointer to the client's callback function
149 * @callback_params: pointer to the client's callback parameters
150 * @state: Activation state of the client
151 * @skip_clk_vote: 0 if client votes for clock when activated, 1 if no vote
152 * @group: the ipa_pm_group the client belongs to
153 * @hdl: handle of the client
154 * @throughput: the throughput of the client for clock scaling
155 * @state_lock: spinlock to lock the pm_states
156 * @activate_work: work for activate (blocking case)
157 * @deactivate work: delayed work for deferred_deactivate function
158 * @complete: generic wait-for-completion handler
Skylar Chang035d90c2018-01-31 10:02:07 -0800159 * @wlock: wake source to prevent AP suspend
Michael Adisumartac06df412017-09-19 10:10:35 -0700160 */
161struct ipa_pm_client {
162 char name[IPA_PM_MAX_EX_CL];
163 void (*callback)(void*, enum ipa_pm_cb_event);
164 void *callback_params;
165 enum ipa_pm_state state;
166 bool skip_clk_vote;
167 int group;
168 int hdl;
169 int throughput;
170 spinlock_t state_lock;
171 struct work_struct activate_work;
172 struct delayed_work deactivate_work;
173 struct completion complete;
Skylar Chang035d90c2018-01-31 10:02:07 -0800174 struct wakeup_source wlock;
Michael Adisumartac06df412017-09-19 10:10:35 -0700175};
176
177/*
178 * struct ipa_pm_ctx - global ctx that will hold the client arrays and tput info
179 * @clients: array to the clients with the handle as its index
180 * @clients_by_pipe: array to the clients with endpoint as the index
181 * @wq: work queue for deferred deactivate, activate, and clk_scaling work
182 8 @clk_scaling: pointer to clock scaling database
183 * @client_mutex: global mutex to lock the client arrays
184 * @aggragated_tput: aggragated tput value of all valid activated clients
185 * @group_tput: combined throughput for the groups
186 */
187struct ipa_pm_ctx {
188 struct ipa_pm_client *clients[IPA_PM_MAX_CLIENTS];
189 struct ipa_pm_client *clients_by_pipe[IPA3_MAX_NUM_PIPES];
190 struct workqueue_struct *wq;
191 struct clk_scaling_db clk_scaling;
192 struct mutex client_mutex;
193 int aggregated_tput;
194 int group_tput[IPA_PM_GROUP_MAX];
195};
196
197static struct ipa_pm_ctx *ipa_pm_ctx;
198
199/**
200 * pop_max_from_array() -pop the max and move the last element to where the
201 * max was popped
202 * @arr: array to be searched for max
203 * @n: size of the array
204 *
205 * Returns: max value of the array
206 */
207static int pop_max_from_array(int *arr, int *n)
208{
209 int i;
210 int max, max_idx;
211
212 max_idx = *n - 1;
213 max = 0;
214
215 if (*n == 0)
216 return 0;
217
218 for (i = 0; i < *n; i++) {
219 if (arr[i] > max) {
220 max = arr[i];
221 max_idx = i;
222 }
223 }
224 (*n)--;
225 arr[max_idx] = arr[*n];
226
227 return max;
228}
229
230/**
231 * calculate_throughput() - calculate the aggregated throughput
232 * based on active clients
233 *
234 * Returns: aggregated tput value
235 */
236static int calculate_throughput(void)
237{
238 int client_tput[IPA_PM_MAX_CLIENTS] = { 0 };
239 bool group_voted[IPA_PM_GROUP_MAX] = { false };
240 int i, n;
241 int max, second_max, aggregated_tput;
242 struct ipa_pm_client *client;
243
244 /* Create a basic array to hold throughputs*/
Michael Adisumartaf0424822018-01-15 16:15:33 -0800245 for (i = 1, n = 0; i < IPA_PM_MAX_CLIENTS; i++) {
Michael Adisumartac06df412017-09-19 10:10:35 -0700246 client = ipa_pm_ctx->clients[i];
247 if (client != NULL && IPA_PM_STATE_ACTIVE(client->state)) {
248 /* default case */
249 if (client->group == IPA_PM_GROUP_DEFAULT) {
250 client_tput[n++] = client->throughput;
251 } else if (group_voted[client->group] == false) {
252 client_tput[n++] = ipa_pm_ctx->group_tput
253 [client->group];
254 group_voted[client->group] = true;
255 }
256 }
257 }
258 /*the array will only use n+1 spots. n will be the last index used*/
259
260 aggregated_tput = 0;
261
262 /**
263 * throughput algorithm:
264 * 1) pop the max and second_max
265 * 2) add the 2nd max to aggregated tput
266 * 3) insert the value of max - 2nd max
267 * 4) repeat until array is of size 1
268 */
269 while (n > 1) {
270 max = pop_max_from_array(client_tput, &n);
271 second_max = pop_max_from_array(client_tput, &n);
272 client_tput[n++] = max - second_max;
273 aggregated_tput += second_max;
274 }
275
276 IPA_PM_DBG_LOW("Aggregated throughput: %d\n", aggregated_tput);
277
278 return aggregated_tput;
279}
280
281/**
282 * deactivate_client() - turn off the bit in the active client bitmask based on
283 * the handle passed in
284 * @hdl: The index of the client to be deactivated
285 */
286static void deactivate_client(u32 hdl)
287{
288 unsigned long flags;
289
290 spin_lock_irqsave(&ipa_pm_ctx->clk_scaling.lock, flags);
291 ipa_pm_ctx->clk_scaling.active_client_bitmask &= ~(1 << hdl);
292 spin_unlock_irqrestore(&ipa_pm_ctx->clk_scaling.lock, flags);
293 IPA_PM_DBG_LOW("active bitmask: %x\n",
294 ipa_pm_ctx->clk_scaling.active_client_bitmask);
295}
296
297/**
298 * activate_client() - turn on the bit in the active client bitmask based on
299 * the handle passed in
300 * @hdl: The index of the client to be activated
301 */
302static void activate_client(u32 hdl)
303{
304 unsigned long flags;
305
306 spin_lock_irqsave(&ipa_pm_ctx->clk_scaling.lock, flags);
307 ipa_pm_ctx->clk_scaling.active_client_bitmask |= (1 << hdl);
308 spin_unlock_irqrestore(&ipa_pm_ctx->clk_scaling.lock, flags);
309 IPA_PM_DBG_LOW("active bitmask: %x\n",
310 ipa_pm_ctx->clk_scaling.active_client_bitmask);
311}
312
313/**
314 * deactivate_client() - get threshold
315 *
316 * Returns: threshold of the exception that passes or default if none pass
317 */
318static void set_current_threshold(void)
319{
320 int i;
321 struct clk_scaling_db *clk;
322 struct ipa_pm_exception_list *exception;
323 unsigned long flags;
324
325 clk = &ipa_pm_ctx->clk_scaling;
326
327 spin_lock_irqsave(&ipa_pm_ctx->clk_scaling.lock, flags);
328 for (i = 0; i < clk->exception_size; i++) {
329 exception = &clk->exception_list[i];
330 if (exception->pending == 0 && (exception->bitmask
331 & ~clk->active_client_bitmask) == 0) {
332 spin_unlock_irqrestore(&ipa_pm_ctx->clk_scaling.lock,
333 flags);
334 clk->current_threshold = exception->threshold;
335 IPA_PM_DBG("Exception %d set\n", i);
336 return;
337 }
338 }
339 clk->current_threshold = clk->default_threshold;
340 spin_unlock_irqrestore(&ipa_pm_ctx->clk_scaling.lock, flags);
341}
342
343/**
344 * do_clk_scaling() - set the clock based on the activated clients
345 *
346 * Returns: 0 if success, negative otherwise
347 */
348static int do_clk_scaling(void)
349{
350 int i, tput;
351 int new_th_idx = 1;
352 struct clk_scaling_db *clk_scaling;
353
354 clk_scaling = &ipa_pm_ctx->clk_scaling;
355
356 mutex_lock(&ipa_pm_ctx->client_mutex);
Michael Adisumarta03f3ab62017-12-20 15:26:49 -0800357 IPA_PM_DBG_LOW("clock scaling started\n");
Michael Adisumartac06df412017-09-19 10:10:35 -0700358 tput = calculate_throughput();
359 ipa_pm_ctx->aggregated_tput = tput;
360 set_current_threshold();
361
362 mutex_unlock(&ipa_pm_ctx->client_mutex);
363
364 for (i = 0; i < clk_scaling->threshold_size; i++) {
365 if (tput > clk_scaling->current_threshold[i])
366 new_th_idx++;
367 }
368
Michael Adisumarta03f3ab62017-12-20 15:26:49 -0800369 IPA_PM_DBG_LOW("old idx was at %d\n", ipa_pm_ctx->clk_scaling.cur_vote);
Michael Adisumartac06df412017-09-19 10:10:35 -0700370
371
372 if (ipa_pm_ctx->clk_scaling.cur_vote != new_th_idx) {
373 ipa_pm_ctx->clk_scaling.cur_vote = new_th_idx;
374 ipa3_set_clock_plan_from_pm(ipa_pm_ctx->clk_scaling.cur_vote);
375 }
376
Michael Adisumarta03f3ab62017-12-20 15:26:49 -0800377 IPA_PM_DBG_LOW("new idx is at %d\n", ipa_pm_ctx->clk_scaling.cur_vote);
Michael Adisumartac06df412017-09-19 10:10:35 -0700378
379 return 0;
380}
381
382/**
383 * clock_scaling_func() - set the clock on a work queue
384 */
385static void clock_scaling_func(struct work_struct *work)
386{
387 do_clk_scaling();
388}
389
390/**
391 * activate_work_func - activate a client and vote for clock on a work queue
392 */
393static void activate_work_func(struct work_struct *work)
394{
395 struct ipa_pm_client *client;
396 bool dec_clk = false;
397 unsigned long flags;
398
399 client = container_of(work, struct ipa_pm_client, activate_work);
Skylar Chang035d90c2018-01-31 10:02:07 -0800400 if (!client->skip_clk_vote) {
Michael Adisumartac06df412017-09-19 10:10:35 -0700401 IPA_ACTIVE_CLIENTS_INC_SPECIAL(client->name);
Skylar Chang035d90c2018-01-31 10:02:07 -0800402 if (client->group == IPA_PM_GROUP_APPS)
403 __pm_stay_awake(&client->wlock);
404 }
Michael Adisumartac06df412017-09-19 10:10:35 -0700405
406 spin_lock_irqsave(&client->state_lock, flags);
407 IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
408 if (client->state == IPA_PM_ACTIVATE_IN_PROGRESS) {
409 client->state = IPA_PM_ACTIVATED;
410 } else if (client->state == IPA_PM_DEACTIVATE_IN_PROGRESS) {
411 client->state = IPA_PM_DEACTIVATED;
412 dec_clk = true;
413 } else {
414 IPA_PM_ERR("unexpected state %d\n", client->state);
415 WARN_ON(1);
416 }
417 spin_unlock_irqrestore(&client->state_lock, flags);
418
419 complete_all(&client->complete);
420
421 if (dec_clk) {
Skylar Chang035d90c2018-01-31 10:02:07 -0800422 if (!client->skip_clk_vote) {
Michael Adisumartac06df412017-09-19 10:10:35 -0700423 IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name);
Skylar Chang035d90c2018-01-31 10:02:07 -0800424 if (client->group == IPA_PM_GROUP_APPS)
425 __pm_relax(&client->wlock);
426 }
Michael Adisumartac06df412017-09-19 10:10:35 -0700427
428 IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
429 return;
430 }
431
432 activate_client(client->hdl);
433
434 mutex_lock(&ipa_pm_ctx->client_mutex);
Michael Adisumartace75fc72017-10-04 11:42:11 -0700435 if (client->callback) {
436 client->callback(client->callback_params,
437 IPA_PM_CLIENT_ACTIVATED);
438 } else {
439 IPA_PM_ERR("client has no callback");
440 WARN_ON(1);
441 }
Michael Adisumartac06df412017-09-19 10:10:35 -0700442 mutex_unlock(&ipa_pm_ctx->client_mutex);
443
444 IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
445 do_clk_scaling();
446}
447
448/**
449 * delayed_deferred_deactivate_work_func - deferred deactivate on a work queue
450 */
451static void delayed_deferred_deactivate_work_func(struct work_struct *work)
452{
453 struct delayed_work *dwork;
454 struct ipa_pm_client *client;
455 unsigned long flags;
456
457 dwork = container_of(work, struct delayed_work, work);
458 client = container_of(dwork, struct ipa_pm_client, deactivate_work);
459
460 spin_lock_irqsave(&client->state_lock, flags);
461 IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
462 switch (client->state) {
463 case IPA_PM_ACTIVATED_TIMER_SET:
464 client->state = IPA_PM_ACTIVATED;
465 goto bail;
466 case IPA_PM_ACTIVATED_PENDING_RESCHEDULE:
467 queue_delayed_work(ipa_pm_ctx->wq, &client->deactivate_work,
468 msecs_to_jiffies(IPA_PM_DEFERRED_TIMEOUT));
469 client->state = IPA_PM_ACTIVATED_PENDING_DEACTIVATION;
470 goto bail;
471 case IPA_PM_ACTIVATED_PENDING_DEACTIVATION:
472 client->state = IPA_PM_DEACTIVATED;
473 IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
474 spin_unlock_irqrestore(&client->state_lock, flags);
Skylar Chang035d90c2018-01-31 10:02:07 -0800475 if (!client->skip_clk_vote) {
Michael Adisumartac06df412017-09-19 10:10:35 -0700476 IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name);
Skylar Chang035d90c2018-01-31 10:02:07 -0800477 if (client->group == IPA_PM_GROUP_APPS)
478 __pm_relax(&client->wlock);
479 }
Michael Adisumartac06df412017-09-19 10:10:35 -0700480
481 deactivate_client(client->hdl);
482 do_clk_scaling();
483 return;
484 default:
485 IPA_PM_ERR("unexpected state %d\n", client->state);
486 WARN_ON(1);
487 goto bail;
488 }
489
490bail:
491 IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
492 spin_unlock_irqrestore(&client->state_lock, flags);
493}
494
495static int find_next_open_array_element(const char *name)
496{
497 int i, n;
498
499 n = -ENOBUFS;
500
Michael Adisumartaf0424822018-01-15 16:15:33 -0800501 /* 0 is not a valid handle */
502 for (i = IPA_PM_MAX_CLIENTS - 1; i >= 1; i--) {
Michael Adisumartac06df412017-09-19 10:10:35 -0700503 if (ipa_pm_ctx->clients[i] == NULL) {
504 n = i;
505 continue;
506 }
507
508 if (strlen(name) == strlen(ipa_pm_ctx->clients[i]->name))
509 if (!strcmp(name, ipa_pm_ctx->clients[i]->name))
510 return -EEXIST;
511 }
512 return n;
513}
514
515/**
516 * add_client_to_exception_list() - add client to the exception list and
517 * update pending if necessary
518 * @hdl: index of the IPA client
519 *
520 * Returns: 0 if success, negative otherwise
521 */
522static int add_client_to_exception_list(u32 hdl)
523{
524 int i;
525 struct ipa_pm_exception_list *exception;
526
527 mutex_lock(&ipa_pm_ctx->client_mutex);
528 for (i = 0; i < ipa_pm_ctx->clk_scaling.exception_size; i++) {
529 exception = &ipa_pm_ctx->clk_scaling.exception_list[i];
530 if (strnstr(exception->clients, ipa_pm_ctx->clients[hdl]->name,
531 strlen(exception->clients))) {
532 exception->pending--;
533
534 if (exception->pending < 0) {
535 WARN_ON(1);
536 exception->pending = 0;
537 mutex_unlock(&ipa_pm_ctx->client_mutex);
538 return -EPERM;
539 }
540 exception->bitmask |= (1 << hdl);
541 }
542 }
543 IPA_PM_DBG("%s added to exception list\n",
544 ipa_pm_ctx->clients[hdl]->name);
545 mutex_unlock(&ipa_pm_ctx->client_mutex);
546
547 return 0;
548}
549
550/**
551 * remove_client_to_exception_list() - remove client from the exception list and
552 * update pending if necessary
553 * @hdl: index of the IPA client
554 *
555 * Returns: 0 if success, negative otherwise
556 */
557static int remove_client_from_exception_list(u32 hdl)
558{
559 int i;
560 struct ipa_pm_exception_list *exception;
561
562 for (i = 0; i < ipa_pm_ctx->clk_scaling.exception_size; i++) {
563 exception = &ipa_pm_ctx->clk_scaling.exception_list[i];
564 if (exception->bitmask & (1 << hdl)) {
565 exception->pending++;
566 exception->bitmask &= ~(1 << hdl);
567 }
568 }
569 IPA_PM_DBG("Client %d removed from exception list\n", hdl);
570
571 return 0;
572}
573
574/**
575 * ipa_pm_init() - initialize IPA PM Components
576 * @ipa_pm_init_params: parameters needed to fill exceptions and thresholds
577 *
578 * Returns: 0 on success, negative on failure
579 */
580int ipa_pm_init(struct ipa_pm_init_params *params)
581{
582 int i, j;
583 struct clk_scaling_db *clk_scaling;
584
585 if (params == NULL) {
586 IPA_PM_ERR("Invalid Params\n");
587 return -EINVAL;
588 }
589
590 if (params->threshold_size <= 0
591 || params->threshold_size > IPA_PM_THRESHOLD_MAX) {
592 IPA_PM_ERR("Invalid threshold size\n");
593 return -EINVAL;
594 }
595
596 if (params->exception_size < 0
597 || params->exception_size > IPA_PM_EXCEPTION_MAX) {
598 IPA_PM_ERR("Invalid exception size\n");
599 return -EINVAL;
600 }
601
602 IPA_PM_DBG("IPA PM initialization started\n");
603
604 if (ipa_pm_ctx != NULL) {
605 IPA_PM_ERR("Already initialized\n");
606 return -EPERM;
607 }
608
609
610 ipa_pm_ctx = kzalloc(sizeof(*ipa_pm_ctx), GFP_KERNEL);
611 if (!ipa_pm_ctx) {
612 IPA_PM_ERR(":kzalloc err.\n");
613 return -ENOMEM;
614 }
615
616 ipa_pm_ctx->wq = create_singlethread_workqueue("ipa_pm_activate");
617 if (!ipa_pm_ctx->wq) {
618 IPA_PM_ERR("create workqueue failed\n");
619 kfree(ipa_pm_ctx);
620 return -ENOMEM;
621 }
622
623 mutex_init(&ipa_pm_ctx->client_mutex);
624
625 /* Populate and init locks in clk_scaling_db */
626 clk_scaling = &ipa_pm_ctx->clk_scaling;
627 spin_lock_init(&clk_scaling->lock);
628 clk_scaling->threshold_size = params->threshold_size;
629 clk_scaling->exception_size = params->exception_size;
630 INIT_WORK(&clk_scaling->work, clock_scaling_func);
631
632 for (i = 0; i < params->threshold_size; i++)
633 clk_scaling->default_threshold[i] =
634 params->default_threshold[i];
635
636 /* Populate exception list*/
637 for (i = 0; i < params->exception_size; i++) {
638 strlcpy(clk_scaling->exception_list[i].clients,
639 params->exceptions[i].usecase, IPA_PM_MAX_EX_CL);
640 IPA_PM_DBG("Usecase: %s\n", params->exceptions[i].usecase);
641
642 /* Parse the commas to count the size of the clients */
643 for (j = 0; j < IPA_PM_MAX_EX_CL &&
644 clk_scaling->exception_list[i].clients[j]; j++) {
645 if (clk_scaling->exception_list[i].clients[j] == ',')
646 clk_scaling->exception_list[i].pending++;
647 }
648
649 clk_scaling->exception_list[i].pending++;
650 IPA_PM_DBG("Pending: %d\n", clk_scaling->
651 exception_list[i].pending);
652
653 /* populate the threshold */
654 for (j = 0; j < params->threshold_size; j++) {
655 clk_scaling->exception_list[i].threshold[j]
656 = params->exceptions[i].threshold[j];
657 }
658
659 }
660 IPA_PM_DBG("initialization success");
661
662 return 0;
663}
664
665int ipa_pm_destroy(void)
666{
667 IPA_PM_DBG("IPA PM destroy started\n");
668
669 if (ipa_pm_ctx == NULL) {
670 IPA_PM_ERR("Already destroyed\n");
671 return -EPERM;
672 }
673
674 destroy_workqueue(ipa_pm_ctx->wq);
675
676 kfree(ipa_pm_ctx);
677 ipa_pm_ctx = NULL;
678
679 return 0;
680}
681
682/**
Skylar Chang035d90c2018-01-31 10:02:07 -0800683 * ipa_pm_register() - register an IPA PM client with the PM
Michael Adisumartac06df412017-09-19 10:10:35 -0700684 * @register_params: params for a client like throughput, callback, etc.
685 * @hdl: int pointer that will be used as an index to access the client
686 *
687 * Returns: 0 on success, negative on failure
688 *
689 * Side effects: *hdl is replaced with the client index or -EEXIST if
690 * client is already registered
691 */
692int ipa_pm_register(struct ipa_pm_register_params *params, u32 *hdl)
693{
694 struct ipa_pm_client *client;
Skylar Chang035d90c2018-01-31 10:02:07 -0800695 struct wakeup_source *wlock;
Michael Adisumartac06df412017-09-19 10:10:35 -0700696
Michael Adisumarta45653fe2017-12-13 20:32:44 -0800697 if (ipa_pm_ctx == NULL) {
698 IPA_PM_ERR("PM_ctx is null\n");
699 return -EINVAL;
700 }
701
Michael Adisumartace75fc72017-10-04 11:42:11 -0700702 if (params == NULL || hdl == NULL || params->name == NULL) {
Michael Adisumartac06df412017-09-19 10:10:35 -0700703 IPA_PM_ERR("Invalid Params\n");
704 return -EINVAL;
705 }
706
707 IPA_PM_DBG("IPA PM registering client\n");
708
709 mutex_lock(&ipa_pm_ctx->client_mutex);
710
711 *hdl = find_next_open_array_element(params->name);
712
713 if (*hdl > IPA_CLIENT_MAX) {
714 mutex_unlock(&ipa_pm_ctx->client_mutex);
715 IPA_PM_ERR("client is already registered or array is full\n");
716 return *hdl;
717 }
718
719 ipa_pm_ctx->clients[*hdl] = kzalloc(sizeof
720 (struct ipa_pm_client), GFP_KERNEL);
721 if (!ipa_pm_ctx->clients[*hdl]) {
722 mutex_unlock(&ipa_pm_ctx->client_mutex);
723 IPA_PM_ERR(":kzalloc err.\n");
724 return -ENOMEM;
725 }
726 mutex_unlock(&ipa_pm_ctx->client_mutex);
727
728 client = ipa_pm_ctx->clients[*hdl];
729
730 spin_lock_init(&client->state_lock);
731
732 INIT_DELAYED_WORK(&client->deactivate_work,
733 delayed_deferred_deactivate_work_func);
734
735 INIT_WORK(&client->activate_work, activate_work_func);
736
737 /* populate fields */
738 strlcpy(client->name, params->name, IPA_PM_MAX_EX_CL);
739 client->callback = params->callback;
740 client->callback_params = params->user_data;
741 client->group = params->group;
742 client->hdl = *hdl;
743 client->skip_clk_vote = params->skip_clk_vote;
Skylar Chang035d90c2018-01-31 10:02:07 -0800744 wlock = &client->wlock;
745 wakeup_source_init(wlock, client->name);
Michael Adisumartac06df412017-09-19 10:10:35 -0700746
747 /* add client to exception list */
748 if (add_client_to_exception_list(*hdl)) {
749 ipa_pm_deregister(*hdl);
750 IPA_PM_ERR("Fail to add client to exception_list\n");
751 return -EPERM;
752 }
753
754 IPA_PM_DBG("IPA PM client registered with handle %d\n", *hdl);
755 return 0;
756}
757
758/**
759 * ipa_pm_deregister() - deregister IPA client from the PM
760 * @hdl: index of the client in the array
761 *
762 * Returns: 0 on success, negative on failure
763 */
764int ipa_pm_deregister(u32 hdl)
765{
766 struct ipa_pm_client *client;
767 int i;
768 unsigned long flags;
769
Michael Adisumarta45653fe2017-12-13 20:32:44 -0800770 if (ipa_pm_ctx == NULL) {
771 IPA_PM_ERR("PM_ctx is null\n");
772 return -EINVAL;
773 }
774
Michael Adisumartac06df412017-09-19 10:10:35 -0700775 if (hdl >= IPA_PM_MAX_CLIENTS) {
776 IPA_PM_ERR("Invalid Param\n");
777 return -EINVAL;
778 }
779
780 if (ipa_pm_ctx->clients[hdl] == NULL) {
781 IPA_PM_ERR("Client is Null\n");
782 return -EINVAL;
783 }
784
785 IPA_PM_DBG("IPA PM deregistering client\n");
786
787 client = ipa_pm_ctx->clients[hdl];
788 spin_lock_irqsave(&client->state_lock, flags);
789 if (IPA_PM_STATE_IN_PROGRESS(client->state)) {
790 spin_unlock_irqrestore(&client->state_lock, flags);
791 wait_for_completion(&client->complete);
792 spin_lock_irqsave(&client->state_lock, flags);
793 }
794
795 if (IPA_PM_STATE_ACTIVE(client->state)) {
796 IPA_PM_DBG("Activated clients cannot be deregistered");
797 spin_unlock_irqrestore(&client->state_lock, flags);
798 return -EPERM;
799 }
800 spin_unlock_irqrestore(&client->state_lock, flags);
801
802 mutex_lock(&ipa_pm_ctx->client_mutex);
803
804 /* nullify pointers in pipe array */
805 for (i = 0; i < IPA3_MAX_NUM_PIPES; i++) {
806 if (ipa_pm_ctx->clients_by_pipe[i] == ipa_pm_ctx->clients[hdl])
807 ipa_pm_ctx->clients_by_pipe[i] = NULL;
808 }
Skylar Chang035d90c2018-01-31 10:02:07 -0800809 wakeup_source_trash(&client->wlock);
Michael Adisumartac06df412017-09-19 10:10:35 -0700810 kfree(client);
811 ipa_pm_ctx->clients[hdl] = NULL;
812
813 remove_client_from_exception_list(hdl);
814 IPA_PM_DBG("IPA PM client %d deregistered\n", hdl);
815 mutex_unlock(&ipa_pm_ctx->client_mutex);
816
817 return 0;
818}
819
820/**
821 * ipa_pm_associate_ipa_cons_to_client() - add mapping to pipe with ipa cllent
822 * @hdl: index of the client to be mapped
823 * @consumer: the pipe/consumer name to be pipped to the client
824 *
825 * Returns: 0 on success, negative on failure
826 *
827 * Side effects: multiple pipes are allowed to be mapped to a single client
828 */
829int ipa_pm_associate_ipa_cons_to_client(u32 hdl, enum ipa_client_type consumer)
830{
831 int idx;
832
Michael Adisumarta45653fe2017-12-13 20:32:44 -0800833 if (ipa_pm_ctx == NULL) {
834 IPA_PM_ERR("PM_ctx is null\n");
835 return -EINVAL;
836 }
837
Michael Adisumartac06df412017-09-19 10:10:35 -0700838 if (hdl >= IPA_PM_MAX_CLIENTS || consumer < 0 ||
839 consumer >= IPA_CLIENT_MAX) {
840 IPA_PM_ERR("invalid params\n");
841 return -EINVAL;
842 }
843
844 mutex_lock(&ipa_pm_ctx->client_mutex);
845 idx = ipa_get_ep_mapping(consumer);
846
847 IPA_PM_DBG("Mapping pipe %d to client %d\n", idx, hdl);
848
849 if (idx < 0) {
850 mutex_unlock(&ipa_pm_ctx->client_mutex);
851 IPA_PM_DBG("Pipe is not used\n");
852 return 0;
853 }
854
855 if (ipa_pm_ctx->clients[hdl] == NULL) {
856 mutex_unlock(&ipa_pm_ctx->client_mutex);
857 IPA_PM_ERR("Client is NULL\n");
858 return -EPERM;
859 }
860
861 if (ipa_pm_ctx->clients_by_pipe[idx] != NULL) {
862 mutex_unlock(&ipa_pm_ctx->client_mutex);
863 IPA_PM_ERR("Pipe is already mapped\n");
864 return -EPERM;
865 }
866 ipa_pm_ctx->clients_by_pipe[idx] = ipa_pm_ctx->clients[hdl];
867 mutex_unlock(&ipa_pm_ctx->client_mutex);
868
869 IPA_PM_DBG("Pipe %d is mapped to client %d\n", idx, hdl);
870
871 return 0;
872}
873
874static int ipa_pm_activate_helper(struct ipa_pm_client *client, bool sync)
875{
876 struct ipa_active_client_logging_info log_info;
877 int result = 0;
878 unsigned long flags;
879
880 spin_lock_irqsave(&client->state_lock, flags);
881 IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
882
883 if (IPA_PM_STATE_IN_PROGRESS(client->state)) {
884 if (sync) {
885 spin_unlock_irqrestore(&client->state_lock, flags);
886 wait_for_completion(&client->complete);
887 spin_lock_irqsave(&client->state_lock, flags);
888 } else {
889 client->state = IPA_PM_ACTIVATE_IN_PROGRESS;
890 spin_unlock_irqrestore(&client->state_lock, flags);
891 return -EINPROGRESS;
892 }
893 }
894
895 switch (client->state) {
896 case IPA_PM_ACTIVATED_PENDING_RESCHEDULE:
897 case IPA_PM_ACTIVATED_PENDING_DEACTIVATION:
898 client->state = IPA_PM_ACTIVATED_TIMER_SET;
899 case IPA_PM_ACTIVATED:
900 case IPA_PM_ACTIVATED_TIMER_SET:
901 spin_unlock_irqrestore(&client->state_lock, flags);
902 return 0;
903 case IPA_PM_DEACTIVATED:
904 break;
905 default:
906 IPA_PM_ERR("Invalid State\n");
907 spin_unlock_irqrestore(&client->state_lock, flags);
908 return -EPERM;
909 }
910 IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
911
912 IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log_info, client->name);
913 if (!client->skip_clk_vote) {
914 if (sync) {
915 client->state = IPA_PM_ACTIVATE_IN_PROGRESS;
916 spin_unlock_irqrestore(&client->state_lock, flags);
917 IPA_ACTIVE_CLIENTS_INC_SPECIAL(client->name);
918 spin_lock_irqsave(&client->state_lock, flags);
919 } else
920 result = ipa3_inc_client_enable_clks_no_block
921 (&log_info);
922 }
923
924 /* we got the clocks */
925 if (result == 0) {
926 client->state = IPA_PM_ACTIVATED;
Skylar Chang035d90c2018-01-31 10:02:07 -0800927 if (client->group == IPA_PM_GROUP_APPS)
928 __pm_stay_awake(&client->wlock);
Michael Adisumartac06df412017-09-19 10:10:35 -0700929 spin_unlock_irqrestore(&client->state_lock, flags);
930 activate_client(client->hdl);
931 if (sync)
932 do_clk_scaling();
933 else
934 queue_work(ipa_pm_ctx->wq,
935 &ipa_pm_ctx->clk_scaling.work);
936 IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
937 return 0;
938 }
939
940 client->state = IPA_PM_ACTIVATE_IN_PROGRESS;
941 init_completion(&client->complete);
942 queue_work(ipa_pm_ctx->wq, &client->activate_work);
943 spin_unlock_irqrestore(&client->state_lock, flags);
944 IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
945 return -EINPROGRESS;
946}
947
948/**
949 * ipa_pm_activate(): activate ipa client to vote for clock(). Can be called
950 * from atomic context and returns -EINPROGRESS if cannot be done synchronously
951 * @hdl: index of the client in the array
952 *
953 * Returns: 0 on success, -EINPROGRESS if operation cannot be done synchronously
954 * and other negatives on failure
955 */
956int ipa_pm_activate(u32 hdl)
957{
Michael Adisumarta45653fe2017-12-13 20:32:44 -0800958 if (ipa_pm_ctx == NULL) {
959 IPA_PM_ERR("PM_ctx is null\n");
960 return -EINVAL;
961 }
962
Michael Adisumartac06df412017-09-19 10:10:35 -0700963 if (hdl >= IPA_PM_MAX_CLIENTS || ipa_pm_ctx->clients[hdl] == NULL) {
964 IPA_PM_ERR("Invalid Param\n");
965 return -EINVAL;
966 }
967
968 return ipa_pm_activate_helper(ipa_pm_ctx->clients[hdl], false);
969}
970
971/**
972 * ipa_pm_activate(): activate ipa client to vote for clock synchronously.
973 * Cannot be called from an atomic contex.
974 * @hdl: index of the client in the array
975 *
976 * Returns: 0 on success, negative on failure
977 */
978int ipa_pm_activate_sync(u32 hdl)
979{
Michael Adisumarta45653fe2017-12-13 20:32:44 -0800980 if (ipa_pm_ctx == NULL) {
981 IPA_PM_ERR("PM_ctx is null\n");
982 return -EINVAL;
983 }
984
Michael Adisumartac06df412017-09-19 10:10:35 -0700985 if (hdl >= IPA_PM_MAX_CLIENTS || ipa_pm_ctx->clients[hdl] == NULL) {
986 IPA_PM_ERR("Invalid Param\n");
987 return -EINVAL;
988 }
989
990 return ipa_pm_activate_helper(ipa_pm_ctx->clients[hdl], true);
991}
992
993/**
994 * ipa_pm_deferred_deactivate(): schedule a timer to deactivate client and
995 * devote clock. Can be called from atomic context (asynchronously)
996 * @hdl: index of the client in the array
997 *
998 * Returns: 0 on success, negative on failure
999 */
1000int ipa_pm_deferred_deactivate(u32 hdl)
1001{
1002 struct ipa_pm_client *client;
1003 unsigned long flags;
1004
Michael Adisumarta45653fe2017-12-13 20:32:44 -08001005 if (ipa_pm_ctx == NULL) {
1006 IPA_PM_ERR("PM_ctx is null\n");
1007 return -EINVAL;
1008 }
1009
Michael Adisumartac06df412017-09-19 10:10:35 -07001010 if (hdl >= IPA_PM_MAX_CLIENTS || ipa_pm_ctx->clients[hdl] == NULL) {
1011 IPA_PM_ERR("Invalid Param\n");
1012 return -EINVAL;
1013 }
1014
1015 client = ipa_pm_ctx->clients[hdl];
1016 IPA_PM_DBG_STATE(hdl, client->name, client->state);
1017
1018 spin_lock_irqsave(&client->state_lock, flags);
1019 switch (client->state) {
1020 case IPA_PM_ACTIVATE_IN_PROGRESS:
1021 client->state = IPA_PM_DEACTIVATE_IN_PROGRESS;
1022 case IPA_PM_DEACTIVATED:
1023 IPA_PM_DBG_STATE(hdl, client->name, client->state);
1024 spin_unlock_irqrestore(&client->state_lock, flags);
1025 return 0;
1026 case IPA_PM_ACTIVATED:
1027 client->state = IPA_PM_ACTIVATED_PENDING_DEACTIVATION;
1028 queue_delayed_work(ipa_pm_ctx->wq, &client->deactivate_work,
1029 msecs_to_jiffies(IPA_PM_DEFERRED_TIMEOUT));
1030 break;
1031 case IPA_PM_ACTIVATED_TIMER_SET:
1032 case IPA_PM_ACTIVATED_PENDING_DEACTIVATION:
1033 client->state = IPA_PM_ACTIVATED_PENDING_RESCHEDULE;
1034 case IPA_PM_DEACTIVATE_IN_PROGRESS:
1035 case IPA_PM_ACTIVATED_PENDING_RESCHEDULE:
1036 break;
1037 }
1038 IPA_PM_DBG_STATE(hdl, client->name, client->state);
1039 spin_unlock_irqrestore(&client->state_lock, flags);
1040
1041 return 0;
1042}
1043
1044/**
1045 * ipa_pm_deactivate_all_deferred(): Cancel the deferred deactivation timer and
1046 * immediately devotes for IPA clocks
1047 *
1048 * Returns: 0 on success, negative on failure
1049 */
1050int ipa_pm_deactivate_all_deferred(void)
1051{
1052 int i;
1053 bool run_algorithm = false;
1054 struct ipa_pm_client *client;
1055 unsigned long flags;
1056
Michael Adisumarta45653fe2017-12-13 20:32:44 -08001057 if (ipa_pm_ctx == NULL) {
1058 IPA_PM_ERR("PM_ctx is null\n");
1059 return -EINVAL;
1060 }
1061
Michael Adisumartaf0424822018-01-15 16:15:33 -08001062 for (i = 1; i < IPA_PM_MAX_CLIENTS; i++) {
Michael Adisumartac06df412017-09-19 10:10:35 -07001063 client = ipa_pm_ctx->clients[i];
1064
1065 if (client == NULL)
1066 continue;
1067
1068 cancel_delayed_work_sync(&client->deactivate_work);
1069
1070 if (IPA_PM_STATE_IN_PROGRESS(client->state)) {
1071 wait_for_completion(&client->complete);
1072 continue;
1073 }
1074
1075 spin_lock_irqsave(&client->state_lock, flags);
1076 IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
1077
1078 if (client->state == IPA_PM_ACTIVATED_TIMER_SET) {
1079 client->state = IPA_PM_ACTIVATED;
1080 IPA_PM_DBG_STATE(client->hdl, client->name,
1081 client->state);
1082 spin_unlock_irqrestore(&client->state_lock, flags);
Ghanim Fodia7af6332017-11-28 20:32:07 +02001083 } else if (client->state ==
Chris Fries012712b2017-12-12 23:30:04 +02001084 IPA_PM_ACTIVATED_PENDING_DEACTIVATION ||
1085 client->state ==
1086 IPA_PM_ACTIVATED_PENDING_RESCHEDULE) {
Michael Adisumartac06df412017-09-19 10:10:35 -07001087 run_algorithm = true;
1088 client->state = IPA_PM_DEACTIVATED;
1089 IPA_PM_DBG_STATE(client->hdl, client->name,
1090 client->state);
1091 spin_unlock_irqrestore(&client->state_lock, flags);
Skylar Chang035d90c2018-01-31 10:02:07 -08001092 if (!client->skip_clk_vote) {
Michael Adisumartac06df412017-09-19 10:10:35 -07001093 IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name);
Skylar Chang035d90c2018-01-31 10:02:07 -08001094 if (client->group == IPA_PM_GROUP_APPS)
1095 __pm_relax(&client->wlock);
1096 }
Michael Adisumartac06df412017-09-19 10:10:35 -07001097 deactivate_client(client->hdl);
1098 } else /* if activated or deactivated, we do nothing */
1099 spin_unlock_irqrestore(&client->state_lock, flags);
1100 }
1101
1102 if (run_algorithm)
1103 do_clk_scaling();
1104
1105 return 0;
1106}
1107
1108/**
1109 * ipa_pm_deactivate_sync(): deactivate ipa client and devote clock. Cannot be
1110 * called from atomic context.
1111 * @hdl: index of the client in the array
1112 *
1113 * Returns: 0 on success, negative on failure
1114 */
1115int ipa_pm_deactivate_sync(u32 hdl)
1116{
Michael Adisumarta45653fe2017-12-13 20:32:44 -08001117 struct ipa_pm_client *client;
Michael Adisumartac06df412017-09-19 10:10:35 -07001118 unsigned long flags;
1119
Michael Adisumarta45653fe2017-12-13 20:32:44 -08001120 if (ipa_pm_ctx == NULL) {
1121 IPA_PM_ERR("PM_ctx is null\n");
1122 return -EINVAL;
1123 }
1124
Michael Adisumartac06df412017-09-19 10:10:35 -07001125 if (hdl >= IPA_PM_MAX_CLIENTS || ipa_pm_ctx->clients[hdl] == NULL) {
1126 IPA_PM_ERR("Invalid Param\n");
1127 return -EINVAL;
1128 }
Michael Adisumarta45653fe2017-12-13 20:32:44 -08001129 client = ipa_pm_ctx->clients[hdl];
Michael Adisumartac06df412017-09-19 10:10:35 -07001130
1131 cancel_delayed_work_sync(&client->deactivate_work);
1132
1133 if (IPA_PM_STATE_IN_PROGRESS(client->state))
1134 wait_for_completion(&client->complete);
1135
1136 spin_lock_irqsave(&client->state_lock, flags);
1137 IPA_PM_DBG_STATE(hdl, client->name, client->state);
1138
1139 if (client->state == IPA_PM_DEACTIVATED) {
1140 spin_unlock_irqrestore(&client->state_lock, flags);
1141 return 0;
1142 }
1143
1144 spin_unlock_irqrestore(&client->state_lock, flags);
1145
1146 /* else case (Deactivates all Activated cases)*/
Skylar Chang035d90c2018-01-31 10:02:07 -08001147 if (!client->skip_clk_vote) {
Michael Adisumartac06df412017-09-19 10:10:35 -07001148 IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name);
Skylar Chang035d90c2018-01-31 10:02:07 -08001149 if (client->group == IPA_PM_GROUP_APPS)
1150 __pm_relax(&client->wlock);
1151 }
Michael Adisumartac06df412017-09-19 10:10:35 -07001152
1153 spin_lock_irqsave(&client->state_lock, flags);
1154 client->state = IPA_PM_DEACTIVATED;
1155 IPA_PM_DBG_STATE(hdl, client->name, client->state);
1156 spin_unlock_irqrestore(&client->state_lock, flags);
1157 deactivate_client(hdl);
1158 do_clk_scaling();
1159
1160 return 0;
1161}
1162
1163/**
1164 * ipa_pm_handle_suspend(): calls the callbacks of suspended clients to wake up
1165 * @pipe_bitmask: the bits represent the indexes of the clients to be woken up
1166 *
1167 * Returns: 0 on success, negative on failure
1168 */
1169int ipa_pm_handle_suspend(u32 pipe_bitmask)
1170{
1171 int i;
1172 struct ipa_pm_client *client;
1173 bool client_notified[IPA_PM_MAX_CLIENTS] = { false };
1174
Michael Adisumarta45653fe2017-12-13 20:32:44 -08001175 if (ipa_pm_ctx == NULL) {
1176 IPA_PM_ERR("PM_ctx is null\n");
1177 return -EINVAL;
1178 }
1179
Michael Adisumartac06df412017-09-19 10:10:35 -07001180 IPA_PM_DBG_LOW("bitmask: %d", pipe_bitmask);
1181
1182 if (pipe_bitmask == 0)
1183 return 0;
1184
1185 mutex_lock(&ipa_pm_ctx->client_mutex);
1186 for (i = 0; i < IPA3_MAX_NUM_PIPES; i++) {
1187 if (pipe_bitmask & (1 << i)) {
1188 client = ipa_pm_ctx->clients_by_pipe[i];
1189 if (client && client_notified[client->hdl] == false) {
Michael Adisumartace75fc72017-10-04 11:42:11 -07001190 if (client->callback) {
1191 client->callback(client->callback_params
1192 , IPA_PM_REQUEST_WAKEUP);
1193 client_notified[client->hdl] = true;
1194 } else {
1195 IPA_PM_ERR("client has no callback");
1196 WARN_ON(1);
1197 }
Michael Adisumartac06df412017-09-19 10:10:35 -07001198 }
1199 }
1200 }
1201 mutex_unlock(&ipa_pm_ctx->client_mutex);
1202 return 0;
1203}
1204
1205/**
1206 * ipa_pm_set_perf_profile(): Adds/changes the throughput requirement to IPA PM
1207 * to be used for clock scaling
1208 * @hdl: index of the client in the array
1209 * @throughput: the new throughput value to be set for that client
1210 *
1211 * Returns: 0 on success, negative on failure
1212 */
1213int ipa_pm_set_perf_profile(u32 hdl, int throughput)
1214{
Michael Adisumarta45653fe2017-12-13 20:32:44 -08001215 struct ipa_pm_client *client;
Michael Adisumartac06df412017-09-19 10:10:35 -07001216 unsigned long flags;
1217
Michael Adisumarta45653fe2017-12-13 20:32:44 -08001218 if (ipa_pm_ctx == NULL) {
1219 IPA_PM_ERR("PM_ctx is null\n");
1220 return -EINVAL;
1221 }
1222
Michael Adisumartac06df412017-09-19 10:10:35 -07001223 if (hdl >= IPA_PM_MAX_CLIENTS || ipa_pm_ctx->clients[hdl] == NULL
1224 || throughput < 0) {
1225 IPA_PM_ERR("Invalid Params\n");
1226 return -EINVAL;
1227 }
Michael Adisumarta45653fe2017-12-13 20:32:44 -08001228 client = ipa_pm_ctx->clients[hdl];
Michael Adisumartac06df412017-09-19 10:10:35 -07001229
1230 mutex_lock(&ipa_pm_ctx->client_mutex);
1231 if (client->group == IPA_PM_GROUP_DEFAULT)
1232 IPA_PM_DBG_LOW("Old throughput: %d\n", client->throughput);
1233 else
1234 IPA_PM_DBG_LOW("old Group %d throughput: %d\n",
1235 client->group, ipa_pm_ctx->group_tput[client->group]);
1236
1237 if (client->group == IPA_PM_GROUP_DEFAULT)
1238 client->throughput = throughput;
1239 else
1240 ipa_pm_ctx->group_tput[client->group] = throughput;
1241
1242 if (client->group == IPA_PM_GROUP_DEFAULT)
1243 IPA_PM_DBG_LOW("New throughput: %d\n", client->throughput);
1244 else
1245 IPA_PM_DBG_LOW("New Group %d throughput: %d\n",
1246 client->group, ipa_pm_ctx->group_tput[client->group]);
1247 mutex_unlock(&ipa_pm_ctx->client_mutex);
1248
1249 spin_lock_irqsave(&client->state_lock, flags);
Michael Adisumarta3a6f6002017-09-26 16:48:12 -07001250 if (IPA_PM_STATE_ACTIVE(client->state) || (client->group !=
1251 IPA_PM_GROUP_DEFAULT)) {
Michael Adisumartac06df412017-09-19 10:10:35 -07001252 spin_unlock_irqrestore(&client->state_lock, flags);
1253 do_clk_scaling();
1254 return 0;
1255 }
1256 spin_unlock_irqrestore(&client->state_lock, flags);
1257
1258 return 0;
1259}
1260
1261/**
1262 * ipa_pm_stat() - print PM stat
1263 * @buf: [in] The user buff used to print
1264 * @size: [in] The size of buf
1265 * Returns: number of bytes used on success, negative on failure
1266 *
1267 * This function is called by ipa_debugfs in order to receive
1268 * a picture of the clients in the PM and the throughput, threshold and cur vote
1269 */
1270int ipa_pm_stat(char *buf, int size)
1271{
1272 struct ipa_pm_client *client;
1273 struct clk_scaling_db *clk = &ipa_pm_ctx->clk_scaling;
1274 int i, j, tput, cnt = 0, result = 0;
1275 unsigned long flags;
1276
1277 if (!buf || size < 0)
1278 return -EINVAL;
1279
1280 mutex_lock(&ipa_pm_ctx->client_mutex);
1281
1282 result = scnprintf(buf + cnt, size - cnt, "\n\nCurrent threshold: [");
1283 cnt += result;
1284
1285 for (i = 0; i < clk->threshold_size; i++) {
1286 result = scnprintf(buf + cnt, size - cnt,
1287 "%d, ", clk->current_threshold[i]);
1288 cnt += result;
1289 }
1290
1291 result = scnprintf(buf + cnt, size - cnt, "\b\b]\n");
1292 cnt += result;
1293
1294 result = scnprintf(buf + cnt, size - cnt,
1295 "Aggregated tput: %d, Cur vote: %d",
1296 ipa_pm_ctx->aggregated_tput, clk->cur_vote);
1297 cnt += result;
1298
1299 result = scnprintf(buf + cnt, size - cnt, "\n\nRegistered Clients:\n");
1300 cnt += result;
1301
1302
Michael Adisumartaf0424822018-01-15 16:15:33 -08001303 for (i = 1; i < IPA_PM_MAX_CLIENTS; i++) {
Michael Adisumartac06df412017-09-19 10:10:35 -07001304 client = ipa_pm_ctx->clients[i];
1305
1306 if (client == NULL)
1307 continue;
1308
1309 spin_lock_irqsave(&client->state_lock, flags);
1310 if (client->group == IPA_PM_GROUP_DEFAULT)
1311 tput = client->throughput;
1312 else
1313 tput = ipa_pm_ctx->group_tput[client->group];
1314
1315 result = scnprintf(buf + cnt, size - cnt,
1316 "Client[%d]: %s State:%s\nGroup: %s Throughput: %d Pipes: ",
1317 i, client->name, client_state_to_str[client->state],
1318 ipa_pm_group_to_str[client->group], tput);
1319 cnt += result;
1320
1321 for (j = 0; j < IPA3_MAX_NUM_PIPES; j++) {
1322 if (ipa_pm_ctx->clients_by_pipe[j] == client) {
1323 result = scnprintf(buf + cnt, size - cnt,
1324 "%d, ", j);
1325 cnt += result;
1326 }
1327 }
1328
1329 result = scnprintf(buf + cnt, size - cnt, "\b\b\n\n");
1330 cnt += result;
1331 spin_unlock_irqrestore(&client->state_lock, flags);
1332 }
1333 mutex_unlock(&ipa_pm_ctx->client_mutex);
1334
1335 return cnt;
1336}
1337
1338/**
1339 * ipa_pm_exceptions_stat() - print PM exceptions stat
1340 * @buf: [in] The user buff used to print
1341 * @size: [in] The size of buf
1342 * Returns: number of bytes used on success, negative on failure
1343 *
1344 * This function is called by ipa_debugfs in order to receive
1345 * a full picture of the exceptions in the PM
1346 */
1347int ipa_pm_exceptions_stat(char *buf, int size)
1348{
1349 int i, j, cnt = 0, result = 0;
1350 struct ipa_pm_exception_list *exception;
1351
1352 if (!buf || size < 0)
1353 return -EINVAL;
1354
1355 result = scnprintf(buf + cnt, size - cnt, "\n");
1356 cnt += result;
1357
1358 mutex_lock(&ipa_pm_ctx->client_mutex);
1359 for (i = 0; i < ipa_pm_ctx->clk_scaling.exception_size; i++) {
1360 exception = &ipa_pm_ctx->clk_scaling.exception_list[i];
1361 if (exception == NULL) {
1362 result = scnprintf(buf + cnt, size - cnt,
1363 "Exception %d is NULL\n\n", i);
1364 cnt += result;
1365 continue;
1366 }
1367
1368 result = scnprintf(buf + cnt, size - cnt,
1369 "Exception %d: %s\nPending: %d Bitmask: %d Threshold: ["
1370 , i, exception->clients, exception->pending,
1371 exception->bitmask);
1372 cnt += result;
1373 for (j = 0; j < ipa_pm_ctx->clk_scaling.threshold_size; j++) {
1374 result = scnprintf(buf + cnt, size - cnt,
1375 "%d, ", exception->threshold[j]);
1376 cnt += result;
1377 }
1378 result = scnprintf(buf + cnt, size - cnt, "\b\b]\n\n");
1379 cnt += result;
1380 }
1381 mutex_unlock(&ipa_pm_ctx->client_mutex);
1382
1383 return cnt;
1384}