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 <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"
18 #include "fsl-mc-private.h"
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)
26 * fsl_mc_resource_pool_add_device - add allocatable device to a resource
27 * pool of a given MC bus
29 * @mc_bus: pointer to the MC bus
30 * @pool_type: MC bus pool type
31 * @mc_dev: Pointer to allocatable MC object device
33 * It adds an allocatable MC object device to a container's resource pool of
34 * the given resource type
36 static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus
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;
48 if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES))
50 if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
52 if (WARN_ON(mc_dev->resource))
55 res_pool = &mc_bus->resource_pools[pool_type];
56 if (WARN_ON(res_pool->type != pool_type))
58 if (WARN_ON(res_pool->mc_bus != mc_bus))
61 mutex_lock(&res_pool->mutex);
63 if (WARN_ON(res_pool->max_count < 0))
65 if (WARN_ON(res_pool->free_count < 0 ||
66 res_pool->free_count > res_pool->max_count))
69 resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource),
73 dev_err(&mc_bus_dev->dev,
74 "Failed to allocate memory for fsl_mc_resource\n");
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++;
89 mutex_unlock(&res_pool->mutex);
95 * fsl_mc_resource_pool_remove_device - remove an allocatable device from a
98 * @mc_dev: Pointer to allocatable MC object device
100 * It permanently removes an allocatable MC object device from the resource
101 * pool, the device is currently in, as long as it is in the pool's free list.
103 static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device
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;
112 if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
115 resource = mc_dev->resource;
116 if (WARN_ON(!resource || resource->data != mc_dev))
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]))
125 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_init(&resource->node);
146 res_pool->free_count--;
147 res_pool->max_count--;
149 devm_kfree(&mc_bus_dev->dev, resource);
150 mc_dev->resource = NULL;
153 mutex_unlock(&res_pool->mutex);
158 static 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",
165 static int __must_check object_type_to_pool_type(const char *object_type,
166 enum fsl_mc_pool_type
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) {
181 int __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)
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;
190 BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) !=
191 FSL_MC_NUM_POOL_TYPES);
193 *new_resource = NULL;
194 if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES))
197 res_pool = &mc_bus->resource_pools[pool_type];
198 if (WARN_ON(res_pool->mc_bus != mc_bus))
201 mutex_lock(&res_pool->mutex);
202 resource = list_first_entry_or_null(&res_pool->free_list,
203 struct fsl_mc_resource, node);
206 WARN_ON(res_pool->free_count != 0);
208 dev_err(&mc_bus_dev->dev,
209 "No more resources of type %s left\n",
210 fsl_mc_pool_type_strings[pool_type]);
214 if (WARN_ON(resource->type != pool_type))
216 if (WARN_ON(resource->parent_pool != res_pool))
218 if (WARN_ON(res_pool->free_count <= 0 ||
219 res_pool->free_count > res_pool->max_count))
222 list_del_init(&resource->node);
224 res_pool->free_count--;
227 mutex_unlock(&res_pool->mutex);
228 *new_resource = resource;
232 EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate);
234 void fsl_mc_resource_free(struct fsl_mc_resource *resource)
236 struct fsl_mc_resource_pool *res_pool;
238 res_pool = resource->parent_pool;
239 if (WARN_ON(resource->type != res_pool->type))
242 mutex_lock(&res_pool->mutex);
243 if (WARN_ON(res_pool->free_count < 0 ||
244 res_pool->free_count >= res_pool->max_count))
247 if (WARN_ON(!list_empty(&resource->node)))
250 list_add_tail(&resource->node, &res_pool->free_list);
251 res_pool->free_count++;
253 mutex_unlock(&res_pool->mutex);
255 EXPORT_SYMBOL_GPL(fsl_mc_resource_free);
258 * fsl_mc_object_allocate - Allocates a MC object device of the given
259 * pool type from a given MC bus
261 * @mc_dev: MC device for which the MC object device is to be allocated
262 * @pool_type: MC bus resource pool type
263 * @new_mc_dev: Pointer to area where the pointer to the allocated
264 * MC object device is to be returned
266 * This function allocates a MC object device from the device's parent DPRC,
267 * from the corresponding MC bus' pool of allocatable MC object devices of
268 * the given resource type. mc_dev cannot be a DPRC itself.
270 * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC
271 * portals are allocated using fsl_mc_portal_allocate(), instead of
274 int __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)
278 struct fsl_mc_device *mc_bus_dev;
279 struct fsl_mc_bus *mc_bus;
280 struct fsl_mc_device *mc_adev;
282 struct fsl_mc_resource *resource = NULL;
285 if (WARN_ON(mc_dev->flags & FSL_MC_IS_DPRC))
288 if (WARN_ON(!dev_is_fsl_mc(mc_dev->dev.parent)))
291 if (WARN_ON(pool_type == FSL_MC_POOL_DPMCP))
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);
300 mc_adev = resource->data;
301 if (WARN_ON(!mc_adev))
304 *new_mc_adev = mc_adev;
308 fsl_mc_resource_free(resource);
312 EXPORT_SYMBOL_GPL(fsl_mc_object_allocate);
315 * fsl_mc_object_free - Returns an allocatable MC object device to the
316 * corresponding resource pool of a given MC bus.
318 * @mc_adev: Pointer to the MC object device
320 void fsl_mc_object_free(struct fsl_mc_device *mc_adev)
322 struct fsl_mc_resource *resource;
324 resource = mc_adev->resource;
325 if (WARN_ON(resource->type == FSL_MC_POOL_DPMCP))
327 if (WARN_ON(resource->data != mc_adev))
330 fsl_mc_resource_free(resource);
332 EXPORT_SYMBOL_GPL(fsl_mc_object_free);
335 * Initialize the interrupt pool associated with a MC bus.
336 * It allocates a block of IRQs from the GIC-ITS
338 int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus,
339 unsigned int irq_count)
342 struct msi_desc *msi_desc;
343 struct fsl_mc_device_irq *irq_resources;
344 struct fsl_mc_device_irq *mc_dev_irq;
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];
350 if (WARN_ON(irq_count == 0 ||
351 irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS))
354 error = fsl_mc_msi_domain_alloc_irqs(&mc_bus_dev->dev, irq_count);
358 irq_resources = devm_kzalloc(&mc_bus_dev->dev,
359 sizeof(*irq_resources) * irq_count,
361 if (!irq_resources) {
363 goto cleanup_msi_irqs;
366 for (i = 0; i < irq_count; i++) {
367 mc_dev_irq = &irq_resources[i];
370 * NOTE: This mc_dev_irq's MSI addr/value pair will be set
371 * by the fsl_mc_msi_write_msg() callback
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);
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;
386 res_pool->max_count = irq_count;
387 res_pool->free_count = irq_count;
388 mc_bus->irq_resources = irq_resources;
392 fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev);
395 EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool);
398 * Teardown the interrupt pool associated with an MC bus.
399 * It frees the IRQs that were allocated to the pool, back to the GIC-ITS.
401 void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus)
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];
407 if (WARN_ON(!mc_bus->irq_resources))
410 if (WARN_ON(res_pool->max_count == 0))
413 if (WARN_ON(res_pool->free_count != res_pool->max_count))
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);
422 EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool);
425 * It allocates the IRQs required by a given MC object device. The
426 * IRQs are allocated from the interrupt pool associated with the
427 * MC bus that contains the device, if the device is not a DPRC device.
428 * Otherwise, the IRQs are allocated from the interrupt pool associated
429 * with the MC bus that represents the DPRC device itself.
431 int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev)
435 int res_allocated_count = 0;
437 struct fsl_mc_device_irq **irqs = NULL;
438 struct fsl_mc_bus *mc_bus;
439 struct fsl_mc_resource_pool *res_pool;
441 if (WARN_ON(mc_dev->irqs))
444 irq_count = mc_dev->obj_desc.irq_count;
445 if (WARN_ON(irq_count == 0))
448 if (strcmp(mc_dev->obj_desc.type, "dprc") == 0)
449 mc_bus = to_fsl_mc_bus(mc_dev);
451 mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent));
453 if (WARN_ON(!mc_bus->irq_resources))
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);
463 irqs = devm_kzalloc(&mc_dev->dev, irq_count * sizeof(irqs[0]),
468 for (i = 0; i < irq_count; i++) {
469 struct fsl_mc_resource *resource;
471 error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ,
474 goto error_resource_alloc;
476 irqs[i] = to_fsl_mc_irq(resource);
477 res_allocated_count++;
479 WARN_ON(irqs[i]->mc_dev);
480 irqs[i]->mc_dev = mc_dev;
481 irqs[i]->dev_irq_index = i;
487 error_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);
495 EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs);
498 * It frees the IRQs that were allocated for a MC object device, by
499 * returning them to the corresponding interrupt pool.
501 void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev)
505 struct fsl_mc_bus *mc_bus;
506 struct fsl_mc_device_irq **irqs = mc_dev->irqs;
511 irq_count = mc_dev->obj_desc.irq_count;
513 if (strcmp(mc_dev->obj_desc.type, "dprc") == 0)
514 mc_bus = to_fsl_mc_bus(mc_dev);
516 mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent));
518 if (WARN_ON(!mc_bus->irq_resources))
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);
529 EXPORT_SYMBOL_GPL(fsl_mc_free_irqs);
531 void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
534 struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
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];
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);
549 static void fsl_mc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev,
550 enum fsl_mc_pool_type pool_type)
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];
559 WARN_ON(res_pool->type != pool_type);
560 WARN_ON(res_pool->free_count != res_pool->max_count);
562 list_for_each_entry_safe(resource, next, &res_pool->free_list, node) {
564 WARN_ON(resource->type != res_pool->type);
565 WARN_ON(resource->parent_pool != res_pool);
566 devm_kfree(&mc_bus_dev->dev, resource);
569 WARN_ON(free_count != res_pool->free_count);
572 void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
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);
581 * fsl_mc_allocator_probe - callback invoked when an allocatable device is
582 * being added to the system
584 static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev)
586 enum fsl_mc_pool_type pool_type;
587 struct fsl_mc_device *mc_bus_dev;
588 struct fsl_mc_bus *mc_bus;
591 if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
594 mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
595 if (WARN_ON(!dev_is_fsl_mc(&mc_bus_dev->dev)))
598 mc_bus = to_fsl_mc_bus(mc_bus_dev);
599 error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type);
603 error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev);
607 dev_dbg(&mc_dev->dev,
608 "Allocatable MC object device bound to fsl_mc_allocator driver");
613 * fsl_mc_allocator_remove - callback invoked when an allocatable device is
614 * being removed from the system
616 static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)
620 if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
623 if (mc_dev->resource) {
624 error = fsl_mc_resource_pool_remove_device(mc_dev);
629 dev_dbg(&mc_dev->dev,
630 "Allocatable MC object device unbound from fsl_mc_allocator driver");
634 static const struct fsl_mc_device_id match_id_table[] = {
636 .vendor = FSL_MC_VENDOR_FREESCALE,
640 .vendor = FSL_MC_VENDOR_FREESCALE,
644 .vendor = FSL_MC_VENDOR_FREESCALE,
650 static struct fsl_mc_driver fsl_mc_allocator_driver = {
652 .name = "fsl_mc_allocator",
655 .match_id_table = match_id_table,
656 .probe = fsl_mc_allocator_probe,
657 .remove = fsl_mc_allocator_remove,
660 int __init fsl_mc_allocator_driver_init(void)
662 return fsl_mc_driver_register(&fsl_mc_allocator_driver);
665 void fsl_mc_allocator_driver_exit(void)
667 fsl_mc_driver_unregister(&fsl_mc_allocator_driver);