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