1
2
3
4
5
6
7
8
9
10
11#include <linux/module.h>
12#include <linux/msi.h>
13#include "../include/mc-bus.h"
14#include "../include/mc-sys.h"
15#include "../include/dpbp-cmd.h"
16#include "../include/dpcon-cmd.h"
17
18#include "fsl-mc-private.h"
19
20#define FSL_MC_IS_ALLOCATABLE(_obj_type) \
21 (strcmp(_obj_type, "dpbp") == 0 || \
22 strcmp(_obj_type, "dpmcp") == 0 || \
23 strcmp(_obj_type, "dpcon") == 0)
24
25
26
27
28
29
30
31
32
33
34
35
36static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus
37 *mc_bus,
38 enum fsl_mc_pool_type
39 pool_type,
40 struct fsl_mc_device
41 *mc_dev)
42{
43 struct fsl_mc_resource_pool *res_pool;
44 struct fsl_mc_resource *resource;
45 struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
46 int error = -EINVAL;
47
48 if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES))
49 goto out;
50 if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
51 goto out;
52 if (WARN_ON(mc_dev->resource))
53 goto out;
54
55 res_pool = &mc_bus->resource_pools[pool_type];
56 if (WARN_ON(res_pool->type != pool_type))
57 goto out;
58 if (WARN_ON(res_pool->mc_bus != mc_bus))
59 goto out;
60
61 mutex_lock(&res_pool->mutex);
62
63 if (WARN_ON(res_pool->max_count < 0))
64 goto out_unlock;
65 if (WARN_ON(res_pool->free_count < 0 ||
66 res_pool->free_count > res_pool->max_count))
67 goto out_unlock;
68
69 resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource),
70 GFP_KERNEL);
71 if (!resource) {
72 error = -ENOMEM;
73 dev_err(&mc_bus_dev->dev,
74 "Failed to allocate memory for fsl_mc_resource\n");
75 goto out_unlock;
76 }
77
78 resource->type = pool_type;
79 resource->id = mc_dev->obj_desc.id;
80 resource->data = mc_dev;
81 resource->parent_pool = res_pool;
82 INIT_LIST_HEAD(&resource->node);
83 list_add_tail(&resource->node, &res_pool->free_list);
84 mc_dev->resource = resource;
85 res_pool->free_count++;
86 res_pool->max_count++;
87 error = 0;
88out_unlock:
89 mutex_unlock(&res_pool->mutex);
90out:
91 return error;
92}
93
94
95
96
97
98
99
100
101
102
103static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device
104 *mc_dev)
105{
106 struct fsl_mc_device *mc_bus_dev;
107 struct fsl_mc_bus *mc_bus;
108 struct fsl_mc_resource_pool *res_pool;
109 struct fsl_mc_resource *resource;
110 int error = -EINVAL;
111
112 if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
113 goto out;
114
115 resource = mc_dev->resource;
116 if (WARN_ON(!resource || resource->data != mc_dev))
117 goto out;
118
119 mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
120 mc_bus = to_fsl_mc_bus(mc_bus_dev);
121 res_pool = resource->parent_pool;
122 if (WARN_ON(res_pool != &mc_bus->resource_pools[resource->type]))
123 goto out;
124
125 mutex_lock(&res_pool->mutex);
126
127 if (WARN_ON(res_pool->max_count <= 0))
128 goto out_unlock;
129 if (WARN_ON(res_pool->free_count <= 0 ||
130 res_pool->free_count > res_pool->max_count))
131 goto out_unlock;
132
133
134
135
136
137 if (list_empty(&resource->node)) {
138 error = -EBUSY;
139 dev_err(&mc_bus_dev->dev,
140 "Device %s cannot be removed from resource pool\n",
141 dev_name(&mc_dev->dev));
142 goto out_unlock;
143 }
144
145 list_del_init(&resource->node);
146 res_pool->free_count--;
147 res_pool->max_count--;
148
149 devm_kfree(&mc_bus_dev->dev, resource);
150 mc_dev->resource = NULL;
151 error = 0;
152out_unlock:
153 mutex_unlock(&res_pool->mutex);
154out:
155 return error;
156}
157
158static const char *const fsl_mc_pool_type_strings[] = {
159 [FSL_MC_POOL_DPMCP] = "dpmcp",
160 [FSL_MC_POOL_DPBP] = "dpbp",
161 [FSL_MC_POOL_DPCON] = "dpcon",
162 [FSL_MC_POOL_IRQ] = "irq",
163};
164
165static int __must_check object_type_to_pool_type(const char *object_type,
166 enum fsl_mc_pool_type
167 *pool_type)
168{
169 unsigned int i;
170
171 for (i = 0; i < ARRAY_SIZE(fsl_mc_pool_type_strings); i++) {
172 if (strcmp(object_type, fsl_mc_pool_type_strings[i]) == 0) {
173 *pool_type = i;
174 return 0;
175 }
176 }
177
178 return -EINVAL;
179}
180
181int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus,
182 enum fsl_mc_pool_type pool_type,
183 struct fsl_mc_resource **new_resource)
184{
185 struct fsl_mc_resource_pool *res_pool;
186 struct fsl_mc_resource *resource;
187 struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
188 int error = -EINVAL;
189
190 BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) !=
191 FSL_MC_NUM_POOL_TYPES);
192
193 *new_resource = NULL;
194 if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES))
195 goto out;
196
197 res_pool = &mc_bus->resource_pools[pool_type];
198 if (WARN_ON(res_pool->mc_bus != mc_bus))
199 goto out;
200
201 mutex_lock(&res_pool->mutex);
202 resource = list_first_entry_or_null(&res_pool->free_list,
203 struct fsl_mc_resource, node);
204
205 if (!resource) {
206 WARN_ON(res_pool->free_count != 0);
207 error = -ENXIO;
208 dev_err(&mc_bus_dev->dev,
209 "No more resources of type %s left\n",
210 fsl_mc_pool_type_strings[pool_type]);
211 goto out_unlock;
212 }
213
214 if (WARN_ON(resource->type != pool_type))
215 goto out_unlock;
216 if (WARN_ON(resource->parent_pool != res_pool))
217 goto out_unlock;
218 if (WARN_ON(res_pool->free_count <= 0 ||
219 res_pool->free_count > res_pool->max_count))
220 goto out_unlock;
221
222 list_del_init(&resource->node);
223
224 res_pool->free_count--;
225 error = 0;
226out_unlock:
227 mutex_unlock(&res_pool->mutex);
228 *new_resource = resource;
229out:
230 return error;
231}
232EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate);
233
234void fsl_mc_resource_free(struct fsl_mc_resource *resource)
235{
236 struct fsl_mc_resource_pool *res_pool;
237
238 res_pool = resource->parent_pool;
239 if (WARN_ON(resource->type != res_pool->type))
240 return;
241
242 mutex_lock(&res_pool->mutex);
243 if (WARN_ON(res_pool->free_count < 0 ||
244 res_pool->free_count >= res_pool->max_count))
245 goto out_unlock;
246
247 if (WARN_ON(!list_empty(&resource->node)))
248 goto out_unlock;
249
250 list_add_tail(&resource->node, &res_pool->free_list);
251 res_pool->free_count++;
252out_unlock:
253 mutex_unlock(&res_pool->mutex);
254}
255EXPORT_SYMBOL_GPL(fsl_mc_resource_free);
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev,
275 enum fsl_mc_pool_type pool_type,
276 struct fsl_mc_device **new_mc_adev)
277{
278 struct fsl_mc_device *mc_bus_dev;
279 struct fsl_mc_bus *mc_bus;
280 struct fsl_mc_device *mc_adev;
281 int error = -EINVAL;
282 struct fsl_mc_resource *resource = NULL;
283
284 *new_mc_adev = NULL;
285 if (WARN_ON(mc_dev->flags & FSL_MC_IS_DPRC))
286 goto error;
287
288 if (WARN_ON(!dev_is_fsl_mc(mc_dev->dev.parent)))
289 goto error;
290
291 if (WARN_ON(pool_type == FSL_MC_POOL_DPMCP))
292 goto error;
293
294 mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
295 mc_bus = to_fsl_mc_bus(mc_bus_dev);
296 error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource);
297 if (error < 0)
298 goto error;
299
300 mc_adev = resource->data;
301 if (WARN_ON(!mc_adev))
302 goto error;
303
304 *new_mc_adev = mc_adev;
305 return 0;
306error:
307 if (resource)
308 fsl_mc_resource_free(resource);
309
310 return error;
311}
312EXPORT_SYMBOL_GPL(fsl_mc_object_allocate);
313
314
315
316
317
318
319
320void fsl_mc_object_free(struct fsl_mc_device *mc_adev)
321{
322 struct fsl_mc_resource *resource;
323
324 resource = mc_adev->resource;
325 if (WARN_ON(resource->type == FSL_MC_POOL_DPMCP))
326 return;
327 if (WARN_ON(resource->data != mc_adev))
328 return;
329
330 fsl_mc_resource_free(resource);
331}
332EXPORT_SYMBOL_GPL(fsl_mc_object_free);
333
334
335
336
337
338int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus,
339 unsigned int irq_count)
340{
341 unsigned int i;
342 struct msi_desc *msi_desc;
343 struct fsl_mc_device_irq *irq_resources;
344 struct fsl_mc_device_irq *mc_dev_irq;
345 int error;
346 struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
347 struct fsl_mc_resource_pool *res_pool =
348 &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
349
350 if (WARN_ON(irq_count == 0 ||
351 irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS))
352 return -EINVAL;
353
354 error = fsl_mc_msi_domain_alloc_irqs(&mc_bus_dev->dev, irq_count);
355 if (error < 0)
356 return error;
357
358 irq_resources = devm_kzalloc(&mc_bus_dev->dev,
359 sizeof(*irq_resources) * irq_count,
360 GFP_KERNEL);
361 if (!irq_resources) {
362 error = -ENOMEM;
363 goto cleanup_msi_irqs;
364 }
365
366 for (i = 0; i < irq_count; i++) {
367 mc_dev_irq = &irq_resources[i];
368
369
370
371
372
373 mc_dev_irq->resource.type = res_pool->type;
374 mc_dev_irq->resource.data = mc_dev_irq;
375 mc_dev_irq->resource.parent_pool = res_pool;
376 INIT_LIST_HEAD(&mc_dev_irq->resource.node);
377 list_add_tail(&mc_dev_irq->resource.node, &res_pool->free_list);
378 }
379
380 for_each_msi_entry(msi_desc, &mc_bus_dev->dev) {
381 mc_dev_irq = &irq_resources[msi_desc->fsl_mc.msi_index];
382 mc_dev_irq->msi_desc = msi_desc;
383 mc_dev_irq->resource.id = msi_desc->irq;
384 }
385
386 res_pool->max_count = irq_count;
387 res_pool->free_count = irq_count;
388 mc_bus->irq_resources = irq_resources;
389 return 0;
390
391cleanup_msi_irqs:
392 fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev);
393 return error;
394}
395EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool);
396
397
398
399
400
401void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus)
402{
403 struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
404 struct fsl_mc_resource_pool *res_pool =
405 &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
406
407 if (WARN_ON(!mc_bus->irq_resources))
408 return;
409
410 if (WARN_ON(res_pool->max_count == 0))
411 return;
412
413 if (WARN_ON(res_pool->free_count != res_pool->max_count))
414 return;
415
416 INIT_LIST_HEAD(&res_pool->free_list);
417 res_pool->max_count = 0;
418 res_pool->free_count = 0;
419 mc_bus->irq_resources = NULL;
420 fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev);
421}
422EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool);
423
424
425
426
427
428
429
430
431int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev)
432{
433 int i;
434 int irq_count;
435 int res_allocated_count = 0;
436 int error = -EINVAL;
437 struct fsl_mc_device_irq **irqs = NULL;
438 struct fsl_mc_bus *mc_bus;
439 struct fsl_mc_resource_pool *res_pool;
440
441 if (WARN_ON(mc_dev->irqs))
442 return -EINVAL;
443
444 irq_count = mc_dev->obj_desc.irq_count;
445 if (WARN_ON(irq_count == 0))
446 return -EINVAL;
447
448 if (strcmp(mc_dev->obj_desc.type, "dprc") == 0)
449 mc_bus = to_fsl_mc_bus(mc_dev);
450 else
451 mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent));
452
453 if (WARN_ON(!mc_bus->irq_resources))
454 return -EINVAL;
455
456 res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
457 if (res_pool->free_count < irq_count) {
458 dev_err(&mc_dev->dev,
459 "Not able to allocate %u irqs for device\n", irq_count);
460 return -ENOSPC;
461 }
462
463 irqs = devm_kzalloc(&mc_dev->dev, irq_count * sizeof(irqs[0]),
464 GFP_KERNEL);
465 if (!irqs)
466 return -ENOMEM;
467
468 for (i = 0; i < irq_count; i++) {
469 struct fsl_mc_resource *resource;
470
471 error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ,
472 &resource);
473 if (error < 0)
474 goto error_resource_alloc;
475
476 irqs[i] = to_fsl_mc_irq(resource);
477 res_allocated_count++;
478
479 WARN_ON(irqs[i]->mc_dev);
480 irqs[i]->mc_dev = mc_dev;
481 irqs[i]->dev_irq_index = i;
482 }
483
484 mc_dev->irqs = irqs;
485 return 0;
486
487error_resource_alloc:
488 for (i = 0; i < res_allocated_count; i++) {
489 irqs[i]->mc_dev = NULL;
490 fsl_mc_resource_free(&irqs[i]->resource);
491 }
492
493 return error;
494}
495EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs);
496
497
498
499
500
501void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev)
502{
503 int i;
504 int irq_count;
505 struct fsl_mc_bus *mc_bus;
506 struct fsl_mc_device_irq **irqs = mc_dev->irqs;
507
508 if (WARN_ON(!irqs))
509 return;
510
511 irq_count = mc_dev->obj_desc.irq_count;
512
513 if (strcmp(mc_dev->obj_desc.type, "dprc") == 0)
514 mc_bus = to_fsl_mc_bus(mc_dev);
515 else
516 mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent));
517
518 if (WARN_ON(!mc_bus->irq_resources))
519 return;
520
521 for (i = 0; i < irq_count; i++) {
522 WARN_ON(!irqs[i]->mc_dev);
523 irqs[i]->mc_dev = NULL;
524 fsl_mc_resource_free(&irqs[i]->resource);
525 }
526
527 mc_dev->irqs = NULL;
528}
529EXPORT_SYMBOL_GPL(fsl_mc_free_irqs);
530
531void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
532{
533 int pool_type;
534 struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
535
536 for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) {
537 struct fsl_mc_resource_pool *res_pool =
538 &mc_bus->resource_pools[pool_type];
539
540 res_pool->type = pool_type;
541 res_pool->max_count = 0;
542 res_pool->free_count = 0;
543 res_pool->mc_bus = mc_bus;
544 INIT_LIST_HEAD(&res_pool->free_list);
545 mutex_init(&res_pool->mutex);
546 }
547}
548
549static void fsl_mc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev,
550 enum fsl_mc_pool_type pool_type)
551{
552 struct fsl_mc_resource *resource;
553 struct fsl_mc_resource *next;
554 struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
555 struct fsl_mc_resource_pool *res_pool =
556 &mc_bus->resource_pools[pool_type];
557 int free_count = 0;
558
559 WARN_ON(res_pool->type != pool_type);
560 WARN_ON(res_pool->free_count != res_pool->max_count);
561
562 list_for_each_entry_safe(resource, next, &res_pool->free_list, node) {
563 free_count++;
564 WARN_ON(resource->type != res_pool->type);
565 WARN_ON(resource->parent_pool != res_pool);
566 devm_kfree(&mc_bus_dev->dev, resource);
567 }
568
569 WARN_ON(free_count != res_pool->free_count);
570}
571
572void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
573{
574 int pool_type;
575
576 for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++)
577 fsl_mc_cleanup_resource_pool(mc_bus_dev, pool_type);
578}
579
580
581
582
583
584static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev)
585{
586 enum fsl_mc_pool_type pool_type;
587 struct fsl_mc_device *mc_bus_dev;
588 struct fsl_mc_bus *mc_bus;
589 int error;
590
591 if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
592 return -EINVAL;
593
594 mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
595 if (WARN_ON(!dev_is_fsl_mc(&mc_bus_dev->dev)))
596 return -EINVAL;
597
598 mc_bus = to_fsl_mc_bus(mc_bus_dev);
599 error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type);
600 if (error < 0)
601 return error;
602
603 error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev);
604 if (error < 0)
605 return error;
606
607 dev_dbg(&mc_dev->dev,
608 "Allocatable MC object device bound to fsl_mc_allocator driver");
609 return 0;
610}
611
612
613
614
615
616static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)
617{
618 int error;
619
620 if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
621 return -EINVAL;
622
623 if (mc_dev->resource) {
624 error = fsl_mc_resource_pool_remove_device(mc_dev);
625 if (error < 0)
626 return error;
627 }
628
629 dev_dbg(&mc_dev->dev,
630 "Allocatable MC object device unbound from fsl_mc_allocator driver");
631 return 0;
632}
633
634static const struct fsl_mc_device_id match_id_table[] = {
635 {
636 .vendor = FSL_MC_VENDOR_FREESCALE,
637 .obj_type = "dpbp",
638 },
639 {
640 .vendor = FSL_MC_VENDOR_FREESCALE,
641 .obj_type = "dpmcp",
642 },
643 {
644 .vendor = FSL_MC_VENDOR_FREESCALE,
645 .obj_type = "dpcon",
646 },
647 {.vendor = 0x0},
648};
649
650static struct fsl_mc_driver fsl_mc_allocator_driver = {
651 .driver = {
652 .name = "fsl_mc_allocator",
653 .pm = NULL,
654 },
655 .match_id_table = match_id_table,
656 .probe = fsl_mc_allocator_probe,
657 .remove = fsl_mc_allocator_remove,
658};
659
660int __init fsl_mc_allocator_driver_init(void)
661{
662 return fsl_mc_driver_register(&fsl_mc_allocator_driver);
663}
664
665void fsl_mc_allocator_driver_exit(void)
666{
667 fsl_mc_driver_unregister(&fsl_mc_allocator_driver);
668}
669