2 * Freescale MC object device allocator driver
4 * Copyright (C) 2013 Freescale Semiconductor, Inc.
6 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
11 #include "../include/mc-private.h"
12 #include "../include/mc-sys.h"
13 #include <linux/module.h>
14 #include "../include/dpbp-cmd.h"
15 #include "../include/dpcon-cmd.h"
16 #include "dpmcp-cmd.h"
18 #include <linux/msi.h>
21 * fsl_mc_resource_pool_add_device - add allocatable device to a resource
22 * pool of a given MC bus
24 * @mc_bus: pointer to the MC bus
25 * @pool_type: MC bus pool type
26 * @mc_dev: Pointer to allocatable MC object device
28 * It adds an allocatable MC object device to a container's resource pool of
29 * the given resource type
31 static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus
38 struct fsl_mc_resource_pool *res_pool;
39 struct fsl_mc_resource *resource;
40 struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
42 bool mutex_locked = false;
44 if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES))
46 if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
48 if (WARN_ON(mc_dev->resource))
51 res_pool = &mc_bus->resource_pools[pool_type];
52 if (WARN_ON(res_pool->type != pool_type))
54 if (WARN_ON(res_pool->mc_bus != mc_bus))
57 mutex_lock(&res_pool->mutex);
60 if (WARN_ON(res_pool->max_count < 0))
62 if (WARN_ON(res_pool->free_count < 0 ||
63 res_pool->free_count > res_pool->max_count))
66 resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource),
70 dev_err(&mc_bus_dev->dev,
71 "Failed to allocate memory for fsl_mc_resource\n");
75 resource->type = pool_type;
76 resource->id = mc_dev->obj_desc.id;
77 resource->data = mc_dev;
78 resource->parent_pool = res_pool;
79 INIT_LIST_HEAD(&resource->node);
80 list_add_tail(&resource->node, &res_pool->free_list);
81 mc_dev->resource = resource;
82 res_pool->free_count++;
83 res_pool->max_count++;
87 mutex_unlock(&res_pool->mutex);
93 * fsl_mc_resource_pool_remove_device - remove an allocatable device from a
96 * @mc_dev: Pointer to allocatable MC object device
98 * It permanently removes an allocatable MC object device from the resource
99 * pool, the device is currently in, as long as it is in the pool's free list.
101 static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device
104 struct fsl_mc_device *mc_bus_dev;
105 struct fsl_mc_bus *mc_bus;
106 struct fsl_mc_resource_pool *res_pool;
107 struct fsl_mc_resource *resource;
109 bool mutex_locked = false;
111 if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
114 resource = mc_dev->resource;
115 if (WARN_ON(!resource || resource->data != mc_dev))
118 mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
119 mc_bus = to_fsl_mc_bus(mc_bus_dev);
120 res_pool = resource->parent_pool;
121 if (WARN_ON(res_pool != &mc_bus->resource_pools[resource->type]))
124 mutex_lock(&res_pool->mutex);
127 if (WARN_ON(res_pool->max_count <= 0))
129 if (WARN_ON(res_pool->free_count <= 0 ||
130 res_pool->free_count > res_pool->max_count))
134 * If the device is currently allocated, its resource is not
135 * in the free list and thus, the device cannot be removed.
137 if (list_empty(&resource->node)) {
139 dev_err(&mc_bus_dev->dev,
140 "Device %s cannot be removed from resource pool\n",
141 dev_name(&mc_dev->dev));
145 list_del(&resource->node);
146 INIT_LIST_HEAD(&resource->node);
147 res_pool->free_count--;
148 res_pool->max_count--;
150 devm_kfree(&mc_bus_dev->dev, resource);
151 mc_dev->resource = NULL;
155 mutex_unlock(&res_pool->mutex);
160 static const char *const fsl_mc_pool_type_strings[] = {
161 [FSL_MC_POOL_DPMCP] = "dpmcp",
162 [FSL_MC_POOL_DPBP] = "dpbp",
163 [FSL_MC_POOL_DPCON] = "dpcon",
164 [FSL_MC_POOL_IRQ] = "irq",
167 static int __must_check object_type_to_pool_type(const char *object_type,
168 enum fsl_mc_pool_type
173 for (i = 0; i < ARRAY_SIZE(fsl_mc_pool_type_strings); i++) {
174 if (strcmp(object_type, fsl_mc_pool_type_strings[i]) == 0) {
183 int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus,
184 enum fsl_mc_pool_type pool_type,
185 struct fsl_mc_resource **new_resource)
187 struct fsl_mc_resource_pool *res_pool;
188 struct fsl_mc_resource *resource;
189 struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
191 bool mutex_locked = false;
193 BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) !=
194 FSL_MC_NUM_POOL_TYPES);
196 *new_resource = NULL;
197 if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES))
200 res_pool = &mc_bus->resource_pools[pool_type];
201 if (WARN_ON(res_pool->mc_bus != mc_bus))
204 mutex_lock(&res_pool->mutex);
206 resource = list_first_entry_or_null(&res_pool->free_list,
207 struct fsl_mc_resource, node);
210 WARN_ON(res_pool->free_count != 0);
212 dev_err(&mc_bus_dev->dev,
213 "No more resources of type %s left\n",
214 fsl_mc_pool_type_strings[pool_type]);
218 if (WARN_ON(resource->type != pool_type))
220 if (WARN_ON(resource->parent_pool != res_pool))
222 if (WARN_ON(res_pool->free_count <= 0 ||
223 res_pool->free_count > res_pool->max_count))
226 list_del(&resource->node);
227 INIT_LIST_HEAD(&resource->node);
229 res_pool->free_count--;
230 mutex_unlock(&res_pool->mutex);
231 *new_resource = resource;
235 mutex_unlock(&res_pool->mutex);
239 EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate);
241 void fsl_mc_resource_free(struct fsl_mc_resource *resource)
243 struct fsl_mc_resource_pool *res_pool;
244 bool mutex_locked = false;
246 res_pool = resource->parent_pool;
247 if (WARN_ON(resource->type != res_pool->type))
250 mutex_lock(&res_pool->mutex);
252 if (WARN_ON(res_pool->free_count < 0 ||
253 res_pool->free_count >= res_pool->max_count))
256 if (WARN_ON(!list_empty(&resource->node)))
259 list_add_tail(&resource->node, &res_pool->free_list);
260 res_pool->free_count++;
263 mutex_unlock(&res_pool->mutex);
265 EXPORT_SYMBOL_GPL(fsl_mc_resource_free);
268 * fsl_mc_portal_allocate - Allocates an MC portal
270 * @mc_dev: MC device for which the MC portal is to be allocated
271 * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated
273 * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object
274 * that wraps the allocated MC portal is to be returned
276 * This function allocates an MC portal from the device's parent DPRC,
277 * from the corresponding MC bus' pool of MC portals and wraps
278 * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the
279 * portal is allocated from its own MC bus.
281 int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev,
283 struct fsl_mc_io **new_mc_io)
285 struct fsl_mc_device *mc_bus_dev;
286 struct fsl_mc_bus *mc_bus;
287 phys_addr_t mc_portal_phys_addr;
288 size_t mc_portal_size;
289 struct fsl_mc_device *dpmcp_dev;
291 struct fsl_mc_resource *resource = NULL;
292 struct fsl_mc_io *mc_io = NULL;
294 if (mc_dev->flags & FSL_MC_IS_DPRC) {
297 if (WARN_ON(mc_dev->dev.parent->bus != &fsl_mc_bus_type))
300 mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
303 mc_bus = to_fsl_mc_bus(mc_bus_dev);
305 error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource);
309 dpmcp_dev = resource->data;
310 if (WARN_ON(!dpmcp_dev))
311 goto error_cleanup_resource;
313 if (WARN_ON(dpmcp_dev->obj_desc.region_count == 0))
314 goto error_cleanup_resource;
316 mc_portal_phys_addr = dpmcp_dev->regions[0].start;
317 mc_portal_size = dpmcp_dev->regions[0].end -
318 dpmcp_dev->regions[0].start + 1;
320 if (WARN_ON(mc_portal_size != mc_bus_dev->mc_io->portal_size))
321 goto error_cleanup_resource;
323 error = fsl_create_mc_io(&mc_bus_dev->dev,
325 mc_portal_size, dpmcp_dev,
326 mc_io_flags, &mc_io);
328 goto error_cleanup_resource;
333 error_cleanup_resource:
334 fsl_mc_resource_free(resource);
337 EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate);
340 * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals
343 * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
345 void fsl_mc_portal_free(struct fsl_mc_io *mc_io)
347 struct fsl_mc_device *dpmcp_dev;
348 struct fsl_mc_resource *resource;
351 * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed
352 * to have a DPMCP object associated with.
354 dpmcp_dev = mc_io->dpmcp_dev;
355 if (WARN_ON(!dpmcp_dev))
358 resource = dpmcp_dev->resource;
359 if (WARN_ON(!resource || resource->type != FSL_MC_POOL_DPMCP))
362 if (WARN_ON(resource->data != dpmcp_dev))
365 fsl_destroy_mc_io(mc_io);
366 fsl_mc_resource_free(resource);
368 EXPORT_SYMBOL_GPL(fsl_mc_portal_free);
371 * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object
373 * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
375 int fsl_mc_portal_reset(struct fsl_mc_io *mc_io)
378 struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
380 if (WARN_ON(!dpmcp_dev))
383 error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle);
385 dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error);
391 EXPORT_SYMBOL_GPL(fsl_mc_portal_reset);
394 * fsl_mc_object_allocate - Allocates a MC object device of the given
395 * pool type from a given MC bus
397 * @mc_dev: MC device for which the MC object device is to be allocated
398 * @pool_type: MC bus resource pool type
399 * @new_mc_dev: Pointer to area where the pointer to the allocated
400 * MC object device is to be returned
402 * This function allocates a MC object device from the device's parent DPRC,
403 * from the corresponding MC bus' pool of allocatable MC object devices of
404 * the given resource type. mc_dev cannot be a DPRC itself.
406 * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC
407 * portals are allocated using fsl_mc_portal_allocate(), instead of
410 int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev,
411 enum fsl_mc_pool_type pool_type,
412 struct fsl_mc_device **new_mc_adev)
414 struct fsl_mc_device *mc_bus_dev;
415 struct fsl_mc_bus *mc_bus;
416 struct fsl_mc_device *mc_adev;
418 struct fsl_mc_resource *resource = NULL;
421 if (WARN_ON(mc_dev->flags & FSL_MC_IS_DPRC))
424 if (WARN_ON(mc_dev->dev.parent->bus != &fsl_mc_bus_type))
427 if (WARN_ON(pool_type == FSL_MC_POOL_DPMCP))
430 mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
431 mc_bus = to_fsl_mc_bus(mc_bus_dev);
432 error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource);
436 mc_adev = resource->data;
437 if (WARN_ON(!mc_adev))
440 *new_mc_adev = mc_adev;
444 fsl_mc_resource_free(resource);
448 EXPORT_SYMBOL_GPL(fsl_mc_object_allocate);
451 * fsl_mc_object_free - Returns an allocatable MC object device to the
452 * corresponding resource pool of a given MC bus.
454 * @mc_adev: Pointer to the MC object device
456 void fsl_mc_object_free(struct fsl_mc_device *mc_adev)
458 struct fsl_mc_resource *resource;
460 resource = mc_adev->resource;
461 if (WARN_ON(resource->type == FSL_MC_POOL_DPMCP))
463 if (WARN_ON(resource->data != mc_adev))
466 fsl_mc_resource_free(resource);
468 EXPORT_SYMBOL_GPL(fsl_mc_object_free);
471 * Initialize the interrupt pool associated with a MC bus.
472 * It allocates a block of IRQs from the GIC-ITS
474 int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus,
475 unsigned int irq_count)
478 struct msi_desc *msi_desc;
479 struct fsl_mc_device_irq *irq_resources;
480 struct fsl_mc_device_irq *mc_dev_irq;
482 struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
483 struct fsl_mc_resource_pool *res_pool =
484 &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
486 if (WARN_ON(irq_count == 0 ||
487 irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS))
490 error = fsl_mc_msi_domain_alloc_irqs(&mc_bus_dev->dev, irq_count);
494 irq_resources = devm_kzalloc(&mc_bus_dev->dev,
495 sizeof(*irq_resources) * irq_count,
497 if (!irq_resources) {
499 goto cleanup_msi_irqs;
502 for (i = 0; i < irq_count; i++) {
503 mc_dev_irq = &irq_resources[i];
506 * NOTE: This mc_dev_irq's MSI addr/value pair will be set
507 * by the fsl_mc_msi_write_msg() callback
509 mc_dev_irq->resource.type = res_pool->type;
510 mc_dev_irq->resource.data = mc_dev_irq;
511 mc_dev_irq->resource.parent_pool = res_pool;
512 INIT_LIST_HEAD(&mc_dev_irq->resource.node);
513 list_add_tail(&mc_dev_irq->resource.node, &res_pool->free_list);
516 for_each_msi_entry(msi_desc, &mc_bus_dev->dev) {
517 mc_dev_irq = &irq_resources[msi_desc->fsl_mc.msi_index];
518 mc_dev_irq->msi_desc = msi_desc;
519 mc_dev_irq->resource.id = msi_desc->irq;
522 res_pool->max_count = irq_count;
523 res_pool->free_count = irq_count;
524 mc_bus->irq_resources = irq_resources;
528 fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev);
531 EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool);
534 * Teardown the interrupt pool associated with an MC bus.
535 * It frees the IRQs that were allocated to the pool, back to the GIC-ITS.
537 void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus)
539 struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
540 struct fsl_mc_resource_pool *res_pool =
541 &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
543 if (WARN_ON(!mc_bus->irq_resources))
546 if (WARN_ON(res_pool->max_count == 0))
549 if (WARN_ON(res_pool->free_count != res_pool->max_count))
552 INIT_LIST_HEAD(&res_pool->free_list);
553 res_pool->max_count = 0;
554 res_pool->free_count = 0;
555 mc_bus->irq_resources = NULL;
556 fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev);
558 EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool);
561 * It allocates the IRQs required by a given MC object device. The
562 * IRQs are allocated from the interrupt pool associated with the
563 * MC bus that contains the device, if the device is not a DPRC device.
564 * Otherwise, the IRQs are allocated from the interrupt pool associated
565 * with the MC bus that represents the DPRC device itself.
567 int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev)
571 int res_allocated_count = 0;
573 struct fsl_mc_device_irq **irqs = NULL;
574 struct fsl_mc_bus *mc_bus;
575 struct fsl_mc_resource_pool *res_pool;
577 if (WARN_ON(mc_dev->irqs))
580 irq_count = mc_dev->obj_desc.irq_count;
581 if (WARN_ON(irq_count == 0))
584 if (strcmp(mc_dev->obj_desc.type, "dprc") == 0)
585 mc_bus = to_fsl_mc_bus(mc_dev);
587 mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent));
589 if (WARN_ON(!mc_bus->irq_resources))
592 res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
593 if (res_pool->free_count < irq_count) {
594 dev_err(&mc_dev->dev,
595 "Not able to allocate %u irqs for device\n", irq_count);
599 irqs = devm_kzalloc(&mc_dev->dev, irq_count * sizeof(irqs[0]),
604 for (i = 0; i < irq_count; i++) {
605 struct fsl_mc_resource *resource;
607 error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ,
610 goto error_resource_alloc;
612 irqs[i] = to_fsl_mc_irq(resource);
613 res_allocated_count++;
615 WARN_ON(irqs[i]->mc_dev);
616 irqs[i]->mc_dev = mc_dev;
617 irqs[i]->dev_irq_index = i;
623 error_resource_alloc:
624 for (i = 0; i < res_allocated_count; i++) {
625 irqs[i]->mc_dev = NULL;
626 fsl_mc_resource_free(&irqs[i]->resource);
631 EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs);
634 * It frees the IRQs that were allocated for a MC object device, by
635 * returning them to the corresponding interrupt pool.
637 void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev)
641 struct fsl_mc_bus *mc_bus;
642 struct fsl_mc_device_irq **irqs = mc_dev->irqs;
647 irq_count = mc_dev->obj_desc.irq_count;
649 if (strcmp(mc_dev->obj_desc.type, "dprc") == 0)
650 mc_bus = to_fsl_mc_bus(mc_dev);
652 mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent));
654 if (WARN_ON(!mc_bus->irq_resources))
657 for (i = 0; i < irq_count; i++) {
658 WARN_ON(!irqs[i]->mc_dev);
659 irqs[i]->mc_dev = NULL;
660 fsl_mc_resource_free(&irqs[i]->resource);
665 EXPORT_SYMBOL_GPL(fsl_mc_free_irqs);
668 * fsl_mc_allocator_probe - callback invoked when an allocatable device is
669 * being added to the system
671 static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev)
673 enum fsl_mc_pool_type pool_type;
674 struct fsl_mc_device *mc_bus_dev;
675 struct fsl_mc_bus *mc_bus;
678 if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
681 mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
682 if (WARN_ON(mc_bus_dev->dev.bus != &fsl_mc_bus_type))
685 mc_bus = to_fsl_mc_bus(mc_bus_dev);
686 error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type);
690 error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev);
694 dev_dbg(&mc_dev->dev,
695 "Allocatable MC object device bound to fsl_mc_allocator driver");
700 * fsl_mc_allocator_remove - callback invoked when an allocatable device is
701 * being removed from the system
703 static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)
707 if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
710 if (mc_dev->resource) {
711 error = fsl_mc_resource_pool_remove_device(mc_dev);
716 dev_dbg(&mc_dev->dev,
717 "Allocatable MC object device unbound from fsl_mc_allocator driver");
721 static const struct fsl_mc_device_match_id match_id_table[] = {
723 .vendor = FSL_MC_VENDOR_FREESCALE,
725 .ver_major = DPBP_VER_MAJOR,
726 .ver_minor = DPBP_VER_MINOR
729 .vendor = FSL_MC_VENDOR_FREESCALE,
731 .ver_major = DPMCP_VER_MAJOR,
732 .ver_minor = DPMCP_VER_MINOR
735 .vendor = FSL_MC_VENDOR_FREESCALE,
737 .ver_major = DPCON_VER_MAJOR,
738 .ver_minor = DPCON_VER_MINOR
743 static struct fsl_mc_driver fsl_mc_allocator_driver = {
745 .name = "fsl_mc_allocator",
746 .owner = THIS_MODULE,
749 .match_id_table = match_id_table,
750 .probe = fsl_mc_allocator_probe,
751 .remove = fsl_mc_allocator_remove,
754 int __init fsl_mc_allocator_driver_init(void)
756 return fsl_mc_driver_register(&fsl_mc_allocator_driver);
759 void __exit fsl_mc_allocator_driver_exit(void)
761 fsl_mc_driver_unregister(&fsl_mc_allocator_driver);