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