blob: 914028c244b2a641da56c490d02ac5b13ef23663 [file] [log] [blame]
Sunil Paidimarri5139aa22017-02-13 11:07:32 -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/slab.h>
14#include <linux/workqueue.h>
15#include <linux/ipa.h>
16#include "ipa_rm_dependency_graph.h"
17#include "ipa_rm_i.h"
18#include "ipa_common_i.h"
19
20static const char *resource_name_to_str[IPA_RM_RESOURCE_MAX] = {
21 __stringify(IPA_RM_RESOURCE_Q6_PROD),
Amir Levy9659e592016-10-27 18:08:27 +030022 __stringify(IPA_RM_RESOURCE_Q6_CONS),
Skylar Changa9516582017-05-09 11:36:47 -070023 __stringify(IPA_RM_RESOURCE_USB_PROD),
Amir Levy9659e592016-10-27 18:08:27 +030024 __stringify(IPA_RM_RESOURCE_USB_CONS),
Skylar Changa9516582017-05-09 11:36:47 -070025 __stringify(IPA_RM_RESOURCE_USB_DPL_DUMMY_PROD),
Amir Levy9659e592016-10-27 18:08:27 +030026 __stringify(IPA_RM_RESOURCE_USB_DPL_CONS),
Skylar Changa9516582017-05-09 11:36:47 -070027 __stringify(IPA_RM_RESOURCE_HSIC_PROD),
Amir Levy9659e592016-10-27 18:08:27 +030028 __stringify(IPA_RM_RESOURCE_HSIC_CONS),
Skylar Changa9516582017-05-09 11:36:47 -070029 __stringify(IPA_RM_RESOURCE_STD_ECM_PROD),
Amir Levy9659e592016-10-27 18:08:27 +030030 __stringify(IPA_RM_RESOURCE_APPS_CONS),
Skylar Changa9516582017-05-09 11:36:47 -070031 __stringify(IPA_RM_RESOURCE_RNDIS_PROD),
32 __stringify(RESERVED_CONS_11),
33 __stringify(IPA_RM_RESOURCE_WWAN_0_PROD),
34 __stringify(RESERVED_CONS_13),
35 __stringify(IPA_RM_RESOURCE_WLAN_PROD),
36 __stringify(IPA_RM_RESOURCE_WLAN_CONS),
37 __stringify(IPA_RM_RESOURCE_ODU_ADAPT_PROD),
Amir Levy9659e592016-10-27 18:08:27 +030038 __stringify(IPA_RM_RESOURCE_ODU_ADAPT_CONS),
Skylar Changa9516582017-05-09 11:36:47 -070039 __stringify(IPA_RM_RESOURCE_MHI_PROD),
Amir Levy9659e592016-10-27 18:08:27 +030040 __stringify(IPA_RM_RESOURCE_MHI_CONS),
Skylar Changa9516582017-05-09 11:36:47 -070041 __stringify(IPA_RM_RESOURCE_ETHERNET_PROD),
Sunil Paidimarri5139aa22017-02-13 11:07:32 -080042 __stringify(IPA_RM_RESOURCE_ETHERNET_CONS),
Amir Levy9659e592016-10-27 18:08:27 +030043};
44
45struct ipa_rm_profile_vote_type {
46 enum ipa_voltage_level volt[IPA_RM_RESOURCE_MAX];
47 enum ipa_voltage_level curr_volt;
Skylar Changa9516582017-05-09 11:36:47 -070048 u32 bw_resources[IPA_RM_RESOURCE_MAX];
Amir Levy9659e592016-10-27 18:08:27 +030049 u32 curr_bw;
50};
51
52struct ipa_rm_context_type {
53 struct ipa_rm_dep_graph *dep_graph;
54 struct workqueue_struct *ipa_rm_wq;
55 spinlock_t ipa_rm_lock;
56 struct ipa_rm_profile_vote_type prof_vote;
57};
58static struct ipa_rm_context_type *ipa_rm_ctx;
59
60struct ipa_rm_notify_ipa_work_type {
61 struct work_struct work;
62 enum ipa_voltage_level volt;
63 u32 bandwidth_mbps;
64};
65
66/**
67 * ipa_rm_create_resource() - create resource
68 * @create_params: [in] parameters needed
69 * for resource initialization
70 *
71 * Returns: 0 on success, negative on failure
72 *
73 * This function is called by IPA RM client to initialize client's resources.
74 * This API should be called before any other IPA RM API on a given resource
75 * name.
76 *
77 */
78int ipa_rm_create_resource(struct ipa_rm_create_params *create_params)
79{
80 struct ipa_rm_resource *resource;
81 unsigned long flags;
82 int result;
83
84 if (unlikely(!ipa_rm_ctx)) {
85 IPA_RM_ERR("IPA RM was not initialized\n");
86 return -EINVAL;
87 }
88
89 if (!create_params) {
90 IPA_RM_ERR("invalid args\n");
91 return -EINVAL;
92 }
93 IPA_RM_DBG("%s\n", ipa_rm_resource_str(create_params->name));
94
95 if (create_params->floor_voltage < 0 ||
96 create_params->floor_voltage >= IPA_VOLTAGE_MAX) {
97 IPA_RM_ERR("invalid voltage %d\n",
98 create_params->floor_voltage);
99 return -EINVAL;
100 }
101
102 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
103 if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
104 create_params->name,
105 &resource) == 0) {
106 IPA_RM_ERR("resource already exists\n");
107 result = -EEXIST;
108 goto bail;
109 }
110 result = ipa_rm_resource_create(create_params,
111 &resource);
112 if (result) {
113 IPA_RM_ERR("ipa_rm_resource_create() failed\n");
114 goto bail;
115 }
116 result = ipa_rm_dep_graph_add(ipa_rm_ctx->dep_graph, resource);
117 if (result) {
118 IPA_RM_ERR("ipa_rm_dep_graph_add() failed\n");
119 ipa_rm_resource_delete(resource);
120 goto bail;
121 }
122bail:
123 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
124 IPA_RM_DBG("EXIT with %d\n", result);
125
126 return result;
127}
128EXPORT_SYMBOL(ipa_rm_create_resource);
129
130/**
131 * ipa_rm_delete_resource() - delete resource
132 * @resource_name: name of resource to be deleted
133 *
134 * Returns: 0 on success, negative on failure
135 *
136 * This function is called by IPA RM client to delete client's resources.
137 *
138 */
139int ipa_rm_delete_resource(enum ipa_rm_resource_name resource_name)
140{
141 struct ipa_rm_resource *resource;
142 unsigned long flags;
143 int result;
144
145 if (unlikely(!ipa_rm_ctx)) {
146 IPA_RM_ERR("IPA RM was not initialized\n");
147 return -EINVAL;
148 }
149
150 IPA_RM_DBG("%s\n", ipa_rm_resource_str(resource_name));
151 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
152 if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
153 resource_name,
154 &resource) != 0) {
155 IPA_RM_ERR("resource does not exist\n");
156 result = -EINVAL;
157 goto bail;
158 }
159 result = ipa_rm_resource_delete(resource);
160 if (result) {
161 IPA_RM_ERR("ipa_rm_resource_delete() failed\n");
162 goto bail;
163 }
164 result = ipa_rm_dep_graph_remove(ipa_rm_ctx->dep_graph,
165 resource_name);
166 if (result) {
167 IPA_RM_ERR("ipa_rm_dep_graph_remove() failed\n");
168 goto bail;
169 }
170bail:
171 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
172 IPA_RM_DBG("EXIT with %d\n", result);
173
174 return result;
175}
176EXPORT_SYMBOL(ipa_rm_delete_resource);
177
178static int _ipa_rm_add_dependency(enum ipa_rm_resource_name resource_name,
179 enum ipa_rm_resource_name depends_on_name,
180 bool userspace_dep)
181{
182 unsigned long flags;
183 int result;
184
185 if (unlikely(!ipa_rm_ctx)) {
186 IPA_RM_ERR("IPA RM was not initialized\n");
187 return -EINVAL;
188 }
189
190 IPA_RM_DBG("%s -> %s\n", ipa_rm_resource_str(resource_name),
191 ipa_rm_resource_str(depends_on_name));
192 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
193 result = ipa_rm_dep_graph_add_dependency(
194 ipa_rm_ctx->dep_graph,
195 resource_name,
196 depends_on_name,
197 userspace_dep);
198 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
199 IPA_RM_DBG("EXIT with %d\n", result);
200
201 return result;
202}
203
204/**
205 * ipa_rm_add_dependency() - create dependency between 2 resources
206 * @resource_name: name of dependent resource
207 * @depends_on_name: name of its dependency
208 *
209 * Returns: 0 on success, negative on failure
210 *
211 * Side effects: IPA_RM_RESORCE_GRANTED could be generated
212 * in case client registered with IPA RM
213 */
214int ipa_rm_add_dependency(enum ipa_rm_resource_name resource_name,
215 enum ipa_rm_resource_name depends_on_name)
216{
217 return _ipa_rm_add_dependency(resource_name, depends_on_name, false);
218}
219EXPORT_SYMBOL(ipa_rm_add_dependency);
220
221/**
222 * ipa_rm_add_dependency_from_ioctl() - create dependency between 2 resources
223 * @resource_name: name of dependent resource
224 * @depends_on_name: name of its dependency
225 *
226 * This function is expected to be called from IOCTL and the dependency will be
227 * marked as is was added by the userspace.
228 *
229 * Returns: 0 on success, negative on failure
230 *
231 * Side effects: IPA_RM_RESORCE_GRANTED could be generated
232 * in case client registered with IPA RM
233 */
234int ipa_rm_add_dependency_from_ioctl(enum ipa_rm_resource_name resource_name,
235 enum ipa_rm_resource_name depends_on_name)
236{
237 return _ipa_rm_add_dependency(resource_name, depends_on_name, true);
238}
239
240static int _ipa_rm_add_dependency_sync(enum ipa_rm_resource_name resource_name,
241 enum ipa_rm_resource_name depends_on_name,
242 bool userspsace_dep)
243{
244 int result;
245 struct ipa_rm_resource *consumer;
246 unsigned long time;
247 unsigned long flags;
248
249 if (unlikely(!ipa_rm_ctx)) {
250 IPA_RM_ERR("IPA RM was not initialized\n");
251 return -EINVAL;
252 }
253
254 IPA_RM_DBG("%s -> %s\n", ipa_rm_resource_str(resource_name),
255 ipa_rm_resource_str(depends_on_name));
256 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
257 result = ipa_rm_dep_graph_add_dependency(
258 ipa_rm_ctx->dep_graph,
259 resource_name,
260 depends_on_name,
261 userspsace_dep);
262 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
263 if (result == -EINPROGRESS) {
264 ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
265 depends_on_name,
266 &consumer);
267 IPA_RM_DBG("%s waits for GRANT of %s.\n",
268 ipa_rm_resource_str(resource_name),
269 ipa_rm_resource_str(depends_on_name));
270 time = wait_for_completion_timeout(
271 &((struct ipa_rm_resource_cons *)consumer)->
272 request_consumer_in_progress,
Gidon Studinski3021a6f2016-11-10 12:48:48 +0200273 HZ * 5);
Amir Levy9659e592016-10-27 18:08:27 +0300274 result = 0;
275 if (!time) {
276 IPA_RM_ERR("TIMEOUT waiting for %s GRANT event.",
277 ipa_rm_resource_str(depends_on_name));
278 result = -ETIME;
Gidon Studinski3021a6f2016-11-10 12:48:48 +0200279 } else {
280 IPA_RM_DBG("%s waited for %s GRANT %lu time.\n",
Amir Levy9659e592016-10-27 18:08:27 +0300281 ipa_rm_resource_str(resource_name),
282 ipa_rm_resource_str(depends_on_name),
283 time);
Gidon Studinski3021a6f2016-11-10 12:48:48 +0200284 }
Amir Levy9659e592016-10-27 18:08:27 +0300285 }
286 IPA_RM_DBG("EXIT with %d\n", result);
287
288 return result;
289}
290/**
291 * ipa_rm_add_dependency_sync() - Create a dependency between 2 resources
292 * in a synchronized fashion. In case a producer resource is in GRANTED state
293 * and the newly added consumer resource is in RELEASED state, the consumer
294 * entity will be requested and the function will block until the consumer
295 * is granted.
296 * @resource_name: name of dependent resource
297 * @depends_on_name: name of its dependency
298 *
299 * This function is expected to be called from IOCTL and the dependency will be
300 * marked as is was added by the userspace.
301 *
302 * Returns: 0 on success, negative on failure
303 *
304 * Side effects: May block. See documentation above.
305 */
306int ipa_rm_add_dependency_sync(enum ipa_rm_resource_name resource_name,
307 enum ipa_rm_resource_name depends_on_name)
308{
309 return _ipa_rm_add_dependency_sync(resource_name, depends_on_name,
310 false);
311}
312EXPORT_SYMBOL(ipa_rm_add_dependency_sync);
313
314/**
315 * ipa_rm_add_dependency_sync_from_ioctl() - Create a dependency between 2
316 * resources in a synchronized fashion. In case a producer resource is in
317 * GRANTED state and the newly added consumer resource is in RELEASED state,
318 * the consumer entity will be requested and the function will block until
319 * the consumer is granted.
320 * @resource_name: name of dependent resource
321 * @depends_on_name: name of its dependency
322 *
323 * Returns: 0 on success, negative on failure
324 *
325 * Side effects: May block. See documentation above.
326 */
327int ipa_rm_add_dependency_sync_from_ioctl(
328 enum ipa_rm_resource_name resource_name,
329 enum ipa_rm_resource_name depends_on_name)
330{
331 return _ipa_rm_add_dependency_sync(resource_name, depends_on_name,
332 true);
333}
334
335static int _ipa_rm_delete_dependency(enum ipa_rm_resource_name resource_name,
336 enum ipa_rm_resource_name depends_on_name,
337 bool userspace_dep)
338{
339 unsigned long flags;
340 int result;
341
342 if (unlikely(!ipa_rm_ctx)) {
343 IPA_RM_ERR("IPA RM was not initialized\n");
344 return -EINVAL;
345 }
346
347 IPA_RM_DBG("%s -> %s\n", ipa_rm_resource_str(resource_name),
348 ipa_rm_resource_str(depends_on_name));
349 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
350 result = ipa_rm_dep_graph_delete_dependency(
351 ipa_rm_ctx->dep_graph,
352 resource_name,
353 depends_on_name,
354 userspace_dep);
355 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
356 IPA_RM_DBG("EXIT with %d\n", result);
357
358 return result;
359}
360
361/**
362 * ipa_rm_delete_dependency() - delete dependency between 2 resources
363 * @resource_name: name of dependent resource
364 * @depends_on_name: name of its dependency
365 *
366 * Returns: 0 on success, negative on failure
367 *
368 * Side effects: IPA_RM_RESORCE_GRANTED could be generated
369 * in case client registered with IPA RM
370 */
371int ipa_rm_delete_dependency(enum ipa_rm_resource_name resource_name,
372 enum ipa_rm_resource_name depends_on_name)
373{
374 return _ipa_rm_delete_dependency(resource_name, depends_on_name, false);
375}
376EXPORT_SYMBOL(ipa_rm_delete_dependency);
377
378/**
379 * ipa_rm_delete_dependency_fron_ioctl() - delete dependency between 2 resources
380 * @resource_name: name of dependent resource
381 * @depends_on_name: name of its dependency
382 *
383 * This function is expected to be called from IOCTL and the dependency will be
384 * marked as is was added by the userspace.
385 *
386 * Returns: 0 on success, negative on failure
387 *
388 * Side effects: IPA_RM_RESORCE_GRANTED could be generated
389 * in case client registered with IPA RM
390 */
391int ipa_rm_delete_dependency_from_ioctl(enum ipa_rm_resource_name resource_name,
392 enum ipa_rm_resource_name depends_on_name)
393{
394 return _ipa_rm_delete_dependency(resource_name, depends_on_name, true);
395}
396
397/**
398 * ipa_rm_request_resource() - request resource
399 * @resource_name: [in] name of the requested resource
400 *
401 * Returns: 0 on success, negative on failure
402 *
403 * All registered callbacks are called with IPA_RM_RESOURCE_GRANTED
404 * on successful completion of this operation.
405 */
406int ipa_rm_request_resource(enum ipa_rm_resource_name resource_name)
407{
408 struct ipa_rm_resource *resource;
409 unsigned long flags;
410 int result;
411
412 if (unlikely(!ipa_rm_ctx)) {
413 IPA_RM_ERR("IPA RM was not initialized\n");
414 return -EINVAL;
415 }
416
417 if (!IPA_RM_RESORCE_IS_PROD(resource_name)) {
418 IPA_RM_ERR("can be called on PROD only\n");
419 return -EINVAL;
420 }
421 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
422 if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
423 resource_name,
424 &resource) != 0) {
425 IPA_RM_ERR("resource does not exists\n");
426 result = -EPERM;
427 goto bail;
428 }
429 result = ipa_rm_resource_producer_request(
430 (struct ipa_rm_resource_prod *)resource);
431
432bail:
433 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
434
435 return result;
436}
437EXPORT_SYMBOL(ipa_rm_request_resource);
438
439void delayed_release_work_func(struct work_struct *work)
440{
441 unsigned long flags;
442 struct ipa_rm_resource *resource;
443 struct ipa_rm_delayed_release_work_type *rwork = container_of(
444 to_delayed_work(work),
445 struct ipa_rm_delayed_release_work_type,
446 work);
447
448 if (!IPA_RM_RESORCE_IS_CONS(rwork->resource_name)) {
449 IPA_RM_ERR("can be called on CONS only\n");
450 kfree(rwork);
451 return;
452 }
453 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
454 if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
455 rwork->resource_name,
456 &resource) != 0) {
457 IPA_RM_ERR("resource does not exists\n");
458 goto bail;
459 }
460
461 ipa_rm_resource_consumer_release(
462 (struct ipa_rm_resource_cons *)resource, rwork->needed_bw,
463 rwork->dec_usage_count);
464
465bail:
466 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
467 kfree(rwork);
468
469}
470
471/**
472 * ipa_rm_request_resource_with_timer() - requests the specified consumer
473 * resource and releases it after 1 second
474 * @resource_name: name of the requested resource
475 *
476 * Returns: 0 on success, negative on failure
477 */
478int ipa_rm_request_resource_with_timer(enum ipa_rm_resource_name resource_name)
479{
480 unsigned long flags;
481 struct ipa_rm_resource *resource;
482 struct ipa_rm_delayed_release_work_type *release_work;
483 int result;
484
485 if (!IPA_RM_RESORCE_IS_CONS(resource_name)) {
486 IPA_RM_ERR("can be called on CONS only\n");
487 return -EINVAL;
488 }
489
490 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
491 if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
492 resource_name,
493 &resource) != 0) {
494 IPA_RM_ERR("resource does not exists\n");
495 result = -EPERM;
496 goto bail;
497 }
498 result = ipa_rm_resource_consumer_request(
499 (struct ipa_rm_resource_cons *)resource, 0, false, true);
500 if (result != 0 && result != -EINPROGRESS) {
501 IPA_RM_ERR("consumer request returned error %d\n", result);
502 result = -EPERM;
503 goto bail;
504 }
505
506 release_work = kzalloc(sizeof(*release_work), GFP_ATOMIC);
507 if (!release_work) {
508 result = -ENOMEM;
509 goto bail;
510 }
511 release_work->resource_name = resource->name;
512 release_work->needed_bw = 0;
513 release_work->dec_usage_count = false;
514 INIT_DELAYED_WORK(&release_work->work, delayed_release_work_func);
515 schedule_delayed_work(&release_work->work,
516 msecs_to_jiffies(IPA_RM_RELEASE_DELAY_IN_MSEC));
517 result = 0;
518bail:
519 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
520
521 return result;
522}
523
524/**
525 * ipa_rm_release_resource() - release resource
526 * @resource_name: [in] name of the requested resource
527 *
528 * Returns: 0 on success, negative on failure
529 *
530 * All registered callbacks are called with IPA_RM_RESOURCE_RELEASED
531 * on successful completion of this operation.
532 */
533int ipa_rm_release_resource(enum ipa_rm_resource_name resource_name)
534{
535 unsigned long flags;
536 struct ipa_rm_resource *resource;
537 int result;
538
539 if (unlikely(!ipa_rm_ctx)) {
540 IPA_RM_ERR("IPA RM was not initialized\n");
541 return -EINVAL;
542 }
543
544 if (!IPA_RM_RESORCE_IS_PROD(resource_name)) {
545 IPA_RM_ERR("can be called on PROD only\n");
546 return -EINVAL;
547 }
548 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
549 if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
550 resource_name,
551 &resource) != 0) {
552 IPA_RM_ERR("resource does not exists\n");
553 result = -EPERM;
554 goto bail;
555 }
556 result = ipa_rm_resource_producer_release(
557 (struct ipa_rm_resource_prod *)resource);
558
559bail:
560 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
561
562 return result;
563}
564EXPORT_SYMBOL(ipa_rm_release_resource);
565
566/**
567 * ipa_rm_register() - register for event
568 * @resource_name: resource name
569 * @reg_params: [in] registration parameters
570 *
571 * Returns: 0 on success, negative on failure
572 *
573 * Registration parameters provided here should be the same
574 * as provided later in ipa_rm_deregister() call.
575 */
576int ipa_rm_register(enum ipa_rm_resource_name resource_name,
577 struct ipa_rm_register_params *reg_params)
578{
579 int result;
580 unsigned long flags;
581 struct ipa_rm_resource *resource;
582
583 IPA_RM_DBG("%s\n", ipa_rm_resource_str(resource_name));
584
585 if (!IPA_RM_RESORCE_IS_PROD(resource_name)) {
586 IPA_RM_ERR("can be called on PROD only\n");
587 return -EINVAL;
588 }
589 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
590 if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
591 resource_name,
592 &resource) != 0) {
593 IPA_RM_ERR("resource does not exists\n");
594 result = -EPERM;
595 goto bail;
596 }
597 result = ipa_rm_resource_producer_register(
598 (struct ipa_rm_resource_prod *)resource,
599 reg_params,
600 true);
601bail:
602 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
603 IPA_RM_DBG("EXIT with %d\n", result);
604
605 return result;
606}
607EXPORT_SYMBOL(ipa_rm_register);
608
609/**
610 * ipa_rm_deregister() - cancel the registration
611 * @resource_name: resource name
612 * @reg_params: [in] registration parameters
613 *
614 * Returns: 0 on success, negative on failure
615 *
616 * Registration parameters provided here should be the same
617 * as provided in ipa_rm_register() call.
618 */
619int ipa_rm_deregister(enum ipa_rm_resource_name resource_name,
620 struct ipa_rm_register_params *reg_params)
621{
622 int result;
623 unsigned long flags;
624 struct ipa_rm_resource *resource;
625
626 IPA_RM_DBG("%s\n", ipa_rm_resource_str(resource_name));
627
628 if (!IPA_RM_RESORCE_IS_PROD(resource_name)) {
629 IPA_RM_ERR("can be called on PROD only\n");
630 return -EINVAL;
631 }
632 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
633 if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
634 resource_name,
635 &resource) != 0) {
636 IPA_RM_ERR("resource does not exists\n");
637 result = -EPERM;
638 goto bail;
639 }
640 result = ipa_rm_resource_producer_deregister(
641 (struct ipa_rm_resource_prod *)resource,
642 reg_params);
643bail:
644 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
645 IPA_RM_DBG("EXIT with %d\n", result);
646
647 return result;
648}
649EXPORT_SYMBOL(ipa_rm_deregister);
650
651/**
652 * ipa_rm_set_perf_profile() - set performance profile
653 * @resource_name: resource name
654 * @profile: [in] profile information.
655 *
656 * Returns: 0 on success, negative on failure
657 *
658 * Set resource performance profile.
659 * Updates IPA driver if performance level changed.
660 */
661int ipa_rm_set_perf_profile(enum ipa_rm_resource_name resource_name,
662 struct ipa_rm_perf_profile *profile)
663{
664 int result;
665 unsigned long flags;
666 struct ipa_rm_resource *resource;
667
668 if (unlikely(!ipa_rm_ctx)) {
669 IPA_RM_ERR("IPA RM was not initialized\n");
670 return -EINVAL;
671 }
672
673 IPA_RM_DBG("%s\n", ipa_rm_resource_str(resource_name));
674 if (profile)
675 IPA_RM_DBG("BW: %d\n", profile->max_supported_bandwidth_mbps);
676
677 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
678 if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
679 resource_name,
680 &resource) != 0) {
681 IPA_RM_ERR("resource does not exists\n");
682 result = -EPERM;
683 goto bail;
684 }
685 result = ipa_rm_resource_set_perf_profile(resource, profile);
686 if (result) {
687 IPA_RM_ERR("ipa_rm_resource_set_perf_profile failed %d\n",
688 result);
689 goto bail;
690 }
691
692 result = 0;
693bail:
694 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
695 IPA_RM_DBG("EXIT with %d\n", result);
696
697 return result;
698}
699EXPORT_SYMBOL(ipa_rm_set_perf_profile);
700
701/**
702 * ipa_rm_notify_completion() -
703 * consumer driver notification for
704 * request_resource / release_resource operations
705 * completion
706 * @event: notified event
707 * @resource_name: resource name
708 *
709 * Returns: 0 on success, negative on failure
710 */
711int ipa_rm_notify_completion(enum ipa_rm_event event,
712 enum ipa_rm_resource_name resource_name)
713{
714 int result;
715
716 if (unlikely(!ipa_rm_ctx)) {
717 IPA_RM_ERR("IPA RM was not initialized\n");
718 return -EINVAL;
719 }
720
721 IPA_RM_DBG("event %d on %s\n", event,
722 ipa_rm_resource_str(resource_name));
723 if (!IPA_RM_RESORCE_IS_CONS(resource_name)) {
724 IPA_RM_ERR("can be called on CONS only\n");
725 result = -EINVAL;
726 goto bail;
727 }
728 ipa_rm_wq_send_cmd(IPA_RM_WQ_RESOURCE_CB,
729 resource_name,
730 event,
731 false);
732 result = 0;
733bail:
734 IPA_RM_DBG("EXIT with %d\n", result);
735
736 return result;
737}
738EXPORT_SYMBOL(ipa_rm_notify_completion);
739
740static void ipa_rm_wq_handler(struct work_struct *work)
741{
742 unsigned long flags;
743 struct ipa_rm_resource *resource;
744 struct ipa_rm_wq_work_type *ipa_rm_work =
745 container_of(work,
746 struct ipa_rm_wq_work_type,
747 work);
748 IPA_RM_DBG_LOW("%s cmd=%d event=%d notify_registered_only=%d\n",
749 ipa_rm_resource_str(ipa_rm_work->resource_name),
750 ipa_rm_work->wq_cmd,
751 ipa_rm_work->event,
752 ipa_rm_work->notify_registered_only);
753 switch (ipa_rm_work->wq_cmd) {
754 case IPA_RM_WQ_NOTIFY_PROD:
755 if (!IPA_RM_RESORCE_IS_PROD(ipa_rm_work->resource_name)) {
756 IPA_RM_ERR("resource is not PROD\n");
757 goto free_work;
758 }
759 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
760 if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
761 ipa_rm_work->resource_name,
762 &resource) != 0){
763 IPA_RM_ERR("resource does not exists\n");
764 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
765 goto free_work;
766 }
767 ipa_rm_resource_producer_notify_clients(
768 (struct ipa_rm_resource_prod *)resource,
769 ipa_rm_work->event,
770 ipa_rm_work->notify_registered_only);
771 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
772 break;
773 case IPA_RM_WQ_NOTIFY_CONS:
774 break;
775 case IPA_RM_WQ_RESOURCE_CB:
776 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
777 if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
778 ipa_rm_work->resource_name,
779 &resource) != 0){
780 IPA_RM_ERR("resource does not exists\n");
781 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
782 goto free_work;
783 }
784 ipa_rm_resource_consumer_handle_cb(
785 (struct ipa_rm_resource_cons *)resource,
786 ipa_rm_work->event);
787 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
788 break;
789 default:
790 break;
791 }
792
793free_work:
794 kfree((void *) work);
795}
796
797static void ipa_rm_wq_resume_handler(struct work_struct *work)
798{
799 unsigned long flags;
800 struct ipa_rm_resource *resource;
801 struct ipa_rm_wq_suspend_resume_work_type *ipa_rm_work =
802 container_of(work,
803 struct ipa_rm_wq_suspend_resume_work_type,
804 work);
805 IPA_RM_DBG_LOW("resume work handler: %s",
806 ipa_rm_resource_str(ipa_rm_work->resource_name));
807
808 if (!IPA_RM_RESORCE_IS_CONS(ipa_rm_work->resource_name)) {
809 IPA_RM_ERR("resource is not CONS\n");
810 return;
811 }
812 IPA_ACTIVE_CLIENTS_INC_RESOURCE(ipa_rm_resource_str(
813 ipa_rm_work->resource_name));
814 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
815 if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
816 ipa_rm_work->resource_name,
817 &resource) != 0){
818 IPA_RM_ERR("resource does not exists\n");
819 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
820 IPA_ACTIVE_CLIENTS_DEC_RESOURCE(ipa_rm_resource_str(
821 ipa_rm_work->resource_name));
822 goto bail;
823 }
824 ipa_rm_resource_consumer_request_work(
825 (struct ipa_rm_resource_cons *)resource,
Gidon Studinski3021a6f2016-11-10 12:48:48 +0200826 ipa_rm_work->prev_state, ipa_rm_work->needed_bw, true,
827 ipa_rm_work->inc_usage_count);
Amir Levy9659e592016-10-27 18:08:27 +0300828 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
829bail:
830 kfree(ipa_rm_work);
831}
832
833
834static void ipa_rm_wq_suspend_handler(struct work_struct *work)
835{
836 unsigned long flags;
837 struct ipa_rm_resource *resource;
838 struct ipa_rm_wq_suspend_resume_work_type *ipa_rm_work =
839 container_of(work,
840 struct ipa_rm_wq_suspend_resume_work_type,
841 work);
842 IPA_RM_DBG_LOW("suspend work handler: %s",
843 ipa_rm_resource_str(ipa_rm_work->resource_name));
844
845 if (!IPA_RM_RESORCE_IS_CONS(ipa_rm_work->resource_name)) {
846 IPA_RM_ERR("resource is not CONS\n");
847 return;
848 }
849 ipa_suspend_resource_sync(ipa_rm_work->resource_name);
850 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
851 if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
852 ipa_rm_work->resource_name,
853 &resource) != 0){
854 IPA_RM_ERR("resource does not exists\n");
855 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
856 return;
857 }
858 ipa_rm_resource_consumer_release_work(
859 (struct ipa_rm_resource_cons *)resource,
860 ipa_rm_work->prev_state,
861 true);
862 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
863
864 kfree(ipa_rm_work);
865}
866
867/**
868 * ipa_rm_wq_send_cmd() - send a command for deferred work
869 * @wq_cmd: command that should be executed
870 * @resource_name: resource on which command should be executed
871 * @notify_registered_only: notify only clients registered by
872 * ipa_rm_register()
873 *
874 * Returns: 0 on success, negative otherwise
875 */
876int ipa_rm_wq_send_cmd(enum ipa_rm_wq_cmd wq_cmd,
877 enum ipa_rm_resource_name resource_name,
878 enum ipa_rm_event event,
879 bool notify_registered_only)
880{
881 int result = -ENOMEM;
882 struct ipa_rm_wq_work_type *work = kzalloc(sizeof(*work), GFP_ATOMIC);
883
884 if (work) {
885 INIT_WORK((struct work_struct *)work, ipa_rm_wq_handler);
886 work->wq_cmd = wq_cmd;
887 work->resource_name = resource_name;
888 work->event = event;
889 work->notify_registered_only = notify_registered_only;
890 result = queue_work(ipa_rm_ctx->ipa_rm_wq,
891 (struct work_struct *)work);
892 } else {
893 IPA_RM_ERR("no mem\n");
894 }
895
896 return result;
897}
898
899int ipa_rm_wq_send_suspend_cmd(enum ipa_rm_resource_name resource_name,
900 enum ipa_rm_resource_state prev_state,
901 u32 needed_bw)
902{
903 int result = -ENOMEM;
904 struct ipa_rm_wq_suspend_resume_work_type *work = kzalloc(sizeof(*work),
905 GFP_ATOMIC);
906 if (work) {
907 INIT_WORK((struct work_struct *)work,
908 ipa_rm_wq_suspend_handler);
909 work->resource_name = resource_name;
910 work->prev_state = prev_state;
911 work->needed_bw = needed_bw;
912 result = queue_work(ipa_rm_ctx->ipa_rm_wq,
913 (struct work_struct *)work);
914 } else {
915 IPA_RM_ERR("no mem\n");
916 }
917
918 return result;
919}
920
921int ipa_rm_wq_send_resume_cmd(enum ipa_rm_resource_name resource_name,
922 enum ipa_rm_resource_state prev_state,
Gidon Studinski3021a6f2016-11-10 12:48:48 +0200923 u32 needed_bw,
924 bool inc_usage_count)
Amir Levy9659e592016-10-27 18:08:27 +0300925{
926 int result = -ENOMEM;
927 struct ipa_rm_wq_suspend_resume_work_type *work = kzalloc(sizeof(*work),
928 GFP_ATOMIC);
929 if (work) {
930 INIT_WORK((struct work_struct *)work, ipa_rm_wq_resume_handler);
931 work->resource_name = resource_name;
932 work->prev_state = prev_state;
933 work->needed_bw = needed_bw;
Gidon Studinski3021a6f2016-11-10 12:48:48 +0200934 work->inc_usage_count = inc_usage_count;
Amir Levy9659e592016-10-27 18:08:27 +0300935 result = queue_work(ipa_rm_ctx->ipa_rm_wq,
936 (struct work_struct *)work);
937 } else {
938 IPA_RM_ERR("no mem\n");
939 }
940
941 return result;
942}
943/**
944 * ipa_rm_initialize() - initialize IPA RM component
945 *
946 * Returns: 0 on success, negative otherwise
947 */
948int ipa_rm_initialize(void)
949{
950 int result;
951
952 ipa_rm_ctx = kzalloc(sizeof(*ipa_rm_ctx), GFP_KERNEL);
953 if (!ipa_rm_ctx) {
954 IPA_RM_ERR("no mem\n");
955 result = -ENOMEM;
956 goto bail;
957 }
958 ipa_rm_ctx->ipa_rm_wq = create_singlethread_workqueue("ipa_rm_wq");
959 if (!ipa_rm_ctx->ipa_rm_wq) {
960 IPA_RM_ERR("create workqueue failed\n");
961 result = -ENOMEM;
962 goto create_wq_fail;
963 }
964 result = ipa_rm_dep_graph_create(&(ipa_rm_ctx->dep_graph));
965 if (result) {
966 IPA_RM_ERR("create dependency graph failed\n");
967 goto graph_alloc_fail;
968 }
969 spin_lock_init(&ipa_rm_ctx->ipa_rm_lock);
970 IPA_RM_DBG("SUCCESS\n");
971
972 return 0;
973graph_alloc_fail:
974 destroy_workqueue(ipa_rm_ctx->ipa_rm_wq);
975create_wq_fail:
976 kfree(ipa_rm_ctx);
977bail:
978 return result;
979}
980
981/**
982 * ipa_rm_stat() - print RM stat
983 * @buf: [in] The user buff used to print
984 * @size: [in] The size of buf
985 * Returns: number of bytes used on success, negative on failure
986 *
987 * This function is called by ipa_debugfs in order to receive
988 * a full picture of the current state of the RM
989 */
990
991int ipa_rm_stat(char *buf, int size)
992{
993 unsigned long flags;
994 int i, cnt = 0, result = EINVAL;
995 struct ipa_rm_resource *resource = NULL;
996 u32 sum_bw_prod = 0;
997 u32 sum_bw_cons = 0;
998
999 if (!buf || size < 0)
1000 return result;
1001
1002 spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
Skylar Changa9516582017-05-09 11:36:47 -07001003 for (i = 0; i < IPA_RM_RESOURCE_MAX; ++i) {
1004 if (!IPA_RM_RESORCE_IS_PROD(i))
1005 continue;
Amir Levy9659e592016-10-27 18:08:27 +03001006 result = ipa_rm_dep_graph_get_resource(
1007 ipa_rm_ctx->dep_graph,
1008 i,
1009 &resource);
1010 if (!result) {
1011 result = ipa_rm_resource_producer_print_stat(
1012 resource, buf + cnt,
1013 size-cnt);
1014 if (result < 0)
1015 goto bail;
1016 cnt += result;
1017 }
1018 }
1019
Skylar Changa9516582017-05-09 11:36:47 -07001020 for (i = 0; i < IPA_RM_RESOURCE_MAX; i++) {
1021 if (IPA_RM_RESORCE_IS_PROD(i))
1022 sum_bw_prod += ipa_rm_ctx->prof_vote.bw_resources[i];
1023 else
1024 sum_bw_cons += ipa_rm_ctx->prof_vote.bw_resources[i];
1025 }
Amir Levy9659e592016-10-27 18:08:27 +03001026
1027 result = scnprintf(buf + cnt, size - cnt,
1028 "All prod bandwidth: %d, All cons bandwidth: %d\n",
1029 sum_bw_prod, sum_bw_cons);
1030 cnt += result;
1031
1032 result = scnprintf(buf + cnt, size - cnt,
1033 "Voting: voltage %d, bandwidth %d\n",
1034 ipa_rm_ctx->prof_vote.curr_volt,
1035 ipa_rm_ctx->prof_vote.curr_bw);
1036 cnt += result;
1037
1038 result = cnt;
1039bail:
1040 spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
1041
1042 return result;
1043}
1044
1045/**
1046 * ipa_rm_resource_str() - returns string that represent the resource
1047 * @resource_name: [in] resource name
1048 */
1049const char *ipa_rm_resource_str(enum ipa_rm_resource_name resource_name)
1050{
1051 if (resource_name < 0 || resource_name >= IPA_RM_RESOURCE_MAX)
1052 return "INVALID RESOURCE";
1053
1054 return resource_name_to_str[resource_name];
1055};
1056
1057static void ipa_rm_perf_profile_notify_to_ipa_work(struct work_struct *work)
1058{
1059 struct ipa_rm_notify_ipa_work_type *notify_work = container_of(work,
1060 struct ipa_rm_notify_ipa_work_type,
1061 work);
1062 int res;
1063
1064 IPA_RM_DBG_LOW("calling to IPA driver. voltage %d bandwidth %d\n",
1065 notify_work->volt, notify_work->bandwidth_mbps);
1066
1067 res = ipa_set_required_perf_profile(notify_work->volt,
1068 notify_work->bandwidth_mbps);
1069 if (res) {
1070 IPA_RM_ERR("ipa_set_required_perf_profile failed %d\n", res);
1071 goto bail;
1072 }
1073
1074 IPA_RM_DBG_LOW("IPA driver notified\n");
1075bail:
1076 kfree(notify_work);
1077}
1078
1079static void ipa_rm_perf_profile_notify_to_ipa(enum ipa_voltage_level volt,
1080 u32 bandwidth)
1081{
1082 struct ipa_rm_notify_ipa_work_type *work;
1083
1084 work = kzalloc(sizeof(*work), GFP_ATOMIC);
1085 if (!work) {
1086 IPA_RM_ERR("no mem\n");
1087 return;
1088 }
1089
1090 INIT_WORK(&work->work, ipa_rm_perf_profile_notify_to_ipa_work);
1091 work->volt = volt;
1092 work->bandwidth_mbps = bandwidth;
1093 queue_work(ipa_rm_ctx->ipa_rm_wq, &work->work);
1094}
1095
1096/**
1097 * ipa_rm_perf_profile_change() - change performance profile vote for resource
1098 * @resource_name: [in] resource name
1099 *
1100 * change bandwidth and voltage vote based on resource state.
1101 */
1102void ipa_rm_perf_profile_change(enum ipa_rm_resource_name resource_name)
1103{
1104 enum ipa_voltage_level old_volt;
1105 u32 *bw_ptr;
1106 u32 old_bw;
1107 struct ipa_rm_resource *resource;
1108 int i;
1109 u32 sum_bw_prod = 0;
1110 u32 sum_bw_cons = 0;
1111
1112 IPA_RM_DBG_LOW("%s\n", ipa_rm_resource_str(resource_name));
1113
1114 if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
1115 resource_name,
1116 &resource) != 0) {
1117 IPA_RM_ERR("resource does not exists\n");
1118 WARN_ON(1);
1119 return;
1120 }
1121
1122 old_volt = ipa_rm_ctx->prof_vote.curr_volt;
1123 old_bw = ipa_rm_ctx->prof_vote.curr_bw;
1124
Skylar Changa9516582017-05-09 11:36:47 -07001125 bw_ptr = &ipa_rm_ctx->prof_vote.bw_resources[resource_name];
Amir Levy9659e592016-10-27 18:08:27 +03001126
1127 switch (resource->state) {
1128 case IPA_RM_GRANTED:
1129 case IPA_RM_REQUEST_IN_PROGRESS:
1130 IPA_RM_DBG_LOW("max_bw = %d, needed_bw = %d\n",
1131 resource->max_bw, resource->needed_bw);
1132 *bw_ptr = min(resource->max_bw, resource->needed_bw);
1133 ipa_rm_ctx->prof_vote.volt[resource_name] =
1134 resource->floor_voltage;
1135 break;
1136
1137 case IPA_RM_RELEASE_IN_PROGRESS:
1138 case IPA_RM_RELEASED:
1139 *bw_ptr = 0;
1140 ipa_rm_ctx->prof_vote.volt[resource_name] = 0;
1141 break;
1142
1143 default:
1144 IPA_RM_ERR("unknown state %d\n", resource->state);
1145 WARN_ON(1);
1146 return;
1147 }
1148 IPA_RM_DBG_LOW("resource bandwidth: %d voltage: %d\n", *bw_ptr,
1149 resource->floor_voltage);
1150
1151 ipa_rm_ctx->prof_vote.curr_volt = IPA_VOLTAGE_UNSPECIFIED;
1152 for (i = 0; i < IPA_RM_RESOURCE_MAX; i++) {
1153 if (ipa_rm_ctx->prof_vote.volt[i] >
1154 ipa_rm_ctx->prof_vote.curr_volt) {
1155 ipa_rm_ctx->prof_vote.curr_volt =
1156 ipa_rm_ctx->prof_vote.volt[i];
1157 }
1158 }
1159
Skylar Changa9516582017-05-09 11:36:47 -07001160 for (i = 0; i < IPA_RM_RESOURCE_MAX; i++) {
1161 if (IPA_RM_RESORCE_IS_PROD(i))
1162 sum_bw_prod += ipa_rm_ctx->prof_vote.bw_resources[i];
1163 else
1164 sum_bw_cons += ipa_rm_ctx->prof_vote.bw_resources[i];
1165 }
Amir Levy9659e592016-10-27 18:08:27 +03001166
1167 IPA_RM_DBG_LOW("all prod bandwidth: %d all cons bandwidth: %d\n",
1168 sum_bw_prod, sum_bw_cons);
1169 ipa_rm_ctx->prof_vote.curr_bw = min(sum_bw_prod, sum_bw_cons);
1170
1171 if (ipa_rm_ctx->prof_vote.curr_volt == old_volt &&
1172 ipa_rm_ctx->prof_vote.curr_bw == old_bw) {
1173 IPA_RM_DBG_LOW("same voting\n");
1174 return;
1175 }
1176
1177 IPA_RM_DBG_LOW("new voting: voltage %d bandwidth %d\n",
1178 ipa_rm_ctx->prof_vote.curr_volt,
1179 ipa_rm_ctx->prof_vote.curr_bw);
1180
1181 ipa_rm_perf_profile_notify_to_ipa(ipa_rm_ctx->prof_vote.curr_volt,
1182 ipa_rm_ctx->prof_vote.curr_bw);
1183
1184 return;
1185};
1186/**
1187 * ipa_rm_exit() - free all IPA RM resources
1188 */
1189void ipa_rm_exit(void)
1190{
1191 IPA_RM_DBG("ENTER\n");
1192 ipa_rm_dep_graph_delete(ipa_rm_ctx->dep_graph);
1193 destroy_workqueue(ipa_rm_ctx->ipa_rm_wq);
1194 kfree(ipa_rm_ctx);
1195 ipa_rm_ctx = NULL;
1196 IPA_RM_DBG("EXIT\n");
1197}