0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/module.h>
0010 #include <linux/msi.h>
0011 #include <linux/fsl/mc.h>
0012
0013 #include "fsl-mc-private.h"
0014
0015 static bool __must_check fsl_mc_is_allocatable(struct fsl_mc_device *mc_dev)
0016 {
0017 return is_fsl_mc_bus_dpbp(mc_dev) ||
0018 is_fsl_mc_bus_dpmcp(mc_dev) ||
0019 is_fsl_mc_bus_dpcon(mc_dev);
0020 }
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030 static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus
0031 *mc_bus,
0032 enum fsl_mc_pool_type
0033 pool_type,
0034 struct fsl_mc_device
0035 *mc_dev)
0036 {
0037 struct fsl_mc_resource_pool *res_pool;
0038 struct fsl_mc_resource *resource;
0039 struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
0040 int error = -EINVAL;
0041
0042 if (pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES)
0043 goto out;
0044 if (!fsl_mc_is_allocatable(mc_dev))
0045 goto out;
0046 if (mc_dev->resource)
0047 goto out;
0048
0049 res_pool = &mc_bus->resource_pools[pool_type];
0050 if (res_pool->type != pool_type)
0051 goto out;
0052 if (res_pool->mc_bus != mc_bus)
0053 goto out;
0054
0055 mutex_lock(&res_pool->mutex);
0056
0057 if (res_pool->max_count < 0)
0058 goto out_unlock;
0059 if (res_pool->free_count < 0 ||
0060 res_pool->free_count > res_pool->max_count)
0061 goto out_unlock;
0062
0063 resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource),
0064 GFP_KERNEL);
0065 if (!resource) {
0066 error = -ENOMEM;
0067 dev_err(&mc_bus_dev->dev,
0068 "Failed to allocate memory for fsl_mc_resource\n");
0069 goto out_unlock;
0070 }
0071
0072 resource->type = pool_type;
0073 resource->id = mc_dev->obj_desc.id;
0074 resource->data = mc_dev;
0075 resource->parent_pool = res_pool;
0076 INIT_LIST_HEAD(&resource->node);
0077 list_add_tail(&resource->node, &res_pool->free_list);
0078 mc_dev->resource = resource;
0079 res_pool->free_count++;
0080 res_pool->max_count++;
0081 error = 0;
0082 out_unlock:
0083 mutex_unlock(&res_pool->mutex);
0084 out:
0085 return error;
0086 }
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097 static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device
0098 *mc_dev)
0099 {
0100 struct fsl_mc_device *mc_bus_dev;
0101 struct fsl_mc_bus *mc_bus;
0102 struct fsl_mc_resource_pool *res_pool;
0103 struct fsl_mc_resource *resource;
0104 int error = -EINVAL;
0105
0106 if (!fsl_mc_is_allocatable(mc_dev))
0107 goto out;
0108
0109 resource = mc_dev->resource;
0110 if (!resource || resource->data != mc_dev)
0111 goto out;
0112
0113 mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
0114 mc_bus = to_fsl_mc_bus(mc_bus_dev);
0115 res_pool = resource->parent_pool;
0116 if (res_pool != &mc_bus->resource_pools[resource->type])
0117 goto out;
0118
0119 mutex_lock(&res_pool->mutex);
0120
0121 if (res_pool->max_count <= 0)
0122 goto out_unlock;
0123 if (res_pool->free_count <= 0 ||
0124 res_pool->free_count > res_pool->max_count)
0125 goto out_unlock;
0126
0127
0128
0129
0130
0131 if (list_empty(&resource->node)) {
0132 error = -EBUSY;
0133 dev_err(&mc_bus_dev->dev,
0134 "Device %s cannot be removed from resource pool\n",
0135 dev_name(&mc_dev->dev));
0136 goto out_unlock;
0137 }
0138
0139 list_del_init(&resource->node);
0140 res_pool->free_count--;
0141 res_pool->max_count--;
0142
0143 devm_kfree(&mc_bus_dev->dev, resource);
0144 mc_dev->resource = NULL;
0145 error = 0;
0146 out_unlock:
0147 mutex_unlock(&res_pool->mutex);
0148 out:
0149 return error;
0150 }
0151
0152 static const char *const fsl_mc_pool_type_strings[] = {
0153 [FSL_MC_POOL_DPMCP] = "dpmcp",
0154 [FSL_MC_POOL_DPBP] = "dpbp",
0155 [FSL_MC_POOL_DPCON] = "dpcon",
0156 [FSL_MC_POOL_IRQ] = "irq",
0157 };
0158
0159 static int __must_check object_type_to_pool_type(const char *object_type,
0160 enum fsl_mc_pool_type
0161 *pool_type)
0162 {
0163 unsigned int i;
0164
0165 for (i = 0; i < ARRAY_SIZE(fsl_mc_pool_type_strings); i++) {
0166 if (strcmp(object_type, fsl_mc_pool_type_strings[i]) == 0) {
0167 *pool_type = i;
0168 return 0;
0169 }
0170 }
0171
0172 return -EINVAL;
0173 }
0174
0175 int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus,
0176 enum fsl_mc_pool_type pool_type,
0177 struct fsl_mc_resource **new_resource)
0178 {
0179 struct fsl_mc_resource_pool *res_pool;
0180 struct fsl_mc_resource *resource;
0181 struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
0182 int error = -EINVAL;
0183
0184 BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) !=
0185 FSL_MC_NUM_POOL_TYPES);
0186
0187 *new_resource = NULL;
0188 if (pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES)
0189 goto out;
0190
0191 res_pool = &mc_bus->resource_pools[pool_type];
0192 if (res_pool->mc_bus != mc_bus)
0193 goto out;
0194
0195 mutex_lock(&res_pool->mutex);
0196 resource = list_first_entry_or_null(&res_pool->free_list,
0197 struct fsl_mc_resource, node);
0198
0199 if (!resource) {
0200 error = -ENXIO;
0201 dev_err(&mc_bus_dev->dev,
0202 "No more resources of type %s left\n",
0203 fsl_mc_pool_type_strings[pool_type]);
0204 goto out_unlock;
0205 }
0206
0207 if (resource->type != pool_type)
0208 goto out_unlock;
0209 if (resource->parent_pool != res_pool)
0210 goto out_unlock;
0211 if (res_pool->free_count <= 0 ||
0212 res_pool->free_count > res_pool->max_count)
0213 goto out_unlock;
0214
0215 list_del_init(&resource->node);
0216
0217 res_pool->free_count--;
0218 error = 0;
0219 out_unlock:
0220 mutex_unlock(&res_pool->mutex);
0221 *new_resource = resource;
0222 out:
0223 return error;
0224 }
0225 EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate);
0226
0227 void fsl_mc_resource_free(struct fsl_mc_resource *resource)
0228 {
0229 struct fsl_mc_resource_pool *res_pool;
0230
0231 res_pool = resource->parent_pool;
0232 if (resource->type != res_pool->type)
0233 return;
0234
0235 mutex_lock(&res_pool->mutex);
0236 if (res_pool->free_count < 0 ||
0237 res_pool->free_count >= res_pool->max_count)
0238 goto out_unlock;
0239
0240 if (!list_empty(&resource->node))
0241 goto out_unlock;
0242
0243 list_add_tail(&resource->node, &res_pool->free_list);
0244 res_pool->free_count++;
0245 out_unlock:
0246 mutex_unlock(&res_pool->mutex);
0247 }
0248 EXPORT_SYMBOL_GPL(fsl_mc_resource_free);
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265
0266
0267
0268 int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev,
0269 enum fsl_mc_pool_type pool_type,
0270 struct fsl_mc_device **new_mc_adev)
0271 {
0272 struct fsl_mc_device *mc_bus_dev;
0273 struct fsl_mc_bus *mc_bus;
0274 struct fsl_mc_device *mc_adev;
0275 int error = -EINVAL;
0276 struct fsl_mc_resource *resource = NULL;
0277
0278 *new_mc_adev = NULL;
0279 if (mc_dev->flags & FSL_MC_IS_DPRC)
0280 goto error;
0281
0282 if (!dev_is_fsl_mc(mc_dev->dev.parent))
0283 goto error;
0284
0285 if (pool_type == FSL_MC_POOL_DPMCP)
0286 goto error;
0287
0288 mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
0289 mc_bus = to_fsl_mc_bus(mc_bus_dev);
0290 error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource);
0291 if (error < 0)
0292 goto error;
0293
0294 mc_adev = resource->data;
0295 if (!mc_adev) {
0296 error = -EINVAL;
0297 goto error;
0298 }
0299
0300 mc_adev->consumer_link = device_link_add(&mc_dev->dev,
0301 &mc_adev->dev,
0302 DL_FLAG_AUTOREMOVE_CONSUMER);
0303 if (!mc_adev->consumer_link) {
0304 error = -EINVAL;
0305 goto error;
0306 }
0307
0308 *new_mc_adev = mc_adev;
0309 return 0;
0310 error:
0311 if (resource)
0312 fsl_mc_resource_free(resource);
0313
0314 return error;
0315 }
0316 EXPORT_SYMBOL_GPL(fsl_mc_object_allocate);
0317
0318
0319
0320
0321
0322
0323 void fsl_mc_object_free(struct fsl_mc_device *mc_adev)
0324 {
0325 struct fsl_mc_resource *resource;
0326
0327 resource = mc_adev->resource;
0328 if (resource->type == FSL_MC_POOL_DPMCP)
0329 return;
0330 if (resource->data != mc_adev)
0331 return;
0332
0333 fsl_mc_resource_free(resource);
0334
0335 mc_adev->consumer_link = NULL;
0336 }
0337 EXPORT_SYMBOL_GPL(fsl_mc_object_free);
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349 int fsl_mc_populate_irq_pool(struct fsl_mc_device *mc_bus_dev,
0350 unsigned int irq_count)
0351 {
0352 unsigned int i;
0353 struct fsl_mc_device_irq *irq_resources;
0354 struct fsl_mc_device_irq *mc_dev_irq;
0355 int error;
0356 struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
0357 struct fsl_mc_resource_pool *res_pool =
0358 &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
0359
0360
0361 if (mc_bus->irq_resources)
0362 return 0;
0363
0364 if (irq_count == 0 ||
0365 irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS)
0366 return -EINVAL;
0367
0368 error = fsl_mc_msi_domain_alloc_irqs(&mc_bus_dev->dev, irq_count);
0369 if (error < 0)
0370 return error;
0371
0372 irq_resources = devm_kcalloc(&mc_bus_dev->dev,
0373 irq_count, sizeof(*irq_resources),
0374 GFP_KERNEL);
0375 if (!irq_resources) {
0376 error = -ENOMEM;
0377 goto cleanup_msi_irqs;
0378 }
0379
0380 for (i = 0; i < irq_count; i++) {
0381 mc_dev_irq = &irq_resources[i];
0382
0383
0384
0385
0386
0387 mc_dev_irq->resource.type = res_pool->type;
0388 mc_dev_irq->resource.data = mc_dev_irq;
0389 mc_dev_irq->resource.parent_pool = res_pool;
0390 mc_dev_irq->virq = msi_get_virq(&mc_bus_dev->dev, i);
0391 mc_dev_irq->resource.id = mc_dev_irq->virq;
0392 INIT_LIST_HEAD(&mc_dev_irq->resource.node);
0393 list_add_tail(&mc_dev_irq->resource.node, &res_pool->free_list);
0394 }
0395
0396 res_pool->max_count = irq_count;
0397 res_pool->free_count = irq_count;
0398 mc_bus->irq_resources = irq_resources;
0399 return 0;
0400
0401 cleanup_msi_irqs:
0402 fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev);
0403 return error;
0404 }
0405 EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool);
0406
0407
0408
0409
0410
0411 void fsl_mc_cleanup_irq_pool(struct fsl_mc_device *mc_bus_dev)
0412 {
0413 struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
0414 struct fsl_mc_resource_pool *res_pool =
0415 &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
0416
0417 if (!mc_bus->irq_resources)
0418 return;
0419
0420 if (res_pool->max_count == 0)
0421 return;
0422
0423 if (res_pool->free_count != res_pool->max_count)
0424 return;
0425
0426 INIT_LIST_HEAD(&res_pool->free_list);
0427 res_pool->max_count = 0;
0428 res_pool->free_count = 0;
0429 mc_bus->irq_resources = NULL;
0430 fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev);
0431 }
0432 EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool);
0433
0434
0435
0436
0437 int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev)
0438 {
0439 int i;
0440 int irq_count;
0441 int res_allocated_count = 0;
0442 int error = -EINVAL;
0443 struct fsl_mc_device_irq **irqs = NULL;
0444 struct fsl_mc_bus *mc_bus;
0445 struct fsl_mc_resource_pool *res_pool;
0446
0447 if (mc_dev->irqs)
0448 return -EINVAL;
0449
0450 irq_count = mc_dev->obj_desc.irq_count;
0451 if (irq_count == 0)
0452 return -EINVAL;
0453
0454 if (is_fsl_mc_bus_dprc(mc_dev))
0455 mc_bus = to_fsl_mc_bus(mc_dev);
0456 else
0457 mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent));
0458
0459 if (!mc_bus->irq_resources)
0460 return -EINVAL;
0461
0462 res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
0463 if (res_pool->free_count < irq_count) {
0464 dev_err(&mc_dev->dev,
0465 "Not able to allocate %u irqs for device\n", irq_count);
0466 return -ENOSPC;
0467 }
0468
0469 irqs = devm_kcalloc(&mc_dev->dev, irq_count, sizeof(irqs[0]),
0470 GFP_KERNEL);
0471 if (!irqs)
0472 return -ENOMEM;
0473
0474 for (i = 0; i < irq_count; i++) {
0475 struct fsl_mc_resource *resource;
0476
0477 error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ,
0478 &resource);
0479 if (error < 0)
0480 goto error_resource_alloc;
0481
0482 irqs[i] = to_fsl_mc_irq(resource);
0483 res_allocated_count++;
0484
0485 irqs[i]->mc_dev = mc_dev;
0486 irqs[i]->dev_irq_index = i;
0487 }
0488
0489 mc_dev->irqs = irqs;
0490 return 0;
0491
0492 error_resource_alloc:
0493 for (i = 0; i < res_allocated_count; i++) {
0494 irqs[i]->mc_dev = NULL;
0495 fsl_mc_resource_free(&irqs[i]->resource);
0496 }
0497
0498 return error;
0499 }
0500 EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs);
0501
0502
0503
0504
0505 void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev)
0506 {
0507 int i;
0508 int irq_count;
0509 struct fsl_mc_bus *mc_bus;
0510 struct fsl_mc_device_irq **irqs = mc_dev->irqs;
0511
0512 if (!irqs)
0513 return;
0514
0515 irq_count = mc_dev->obj_desc.irq_count;
0516
0517 if (is_fsl_mc_bus_dprc(mc_dev))
0518 mc_bus = to_fsl_mc_bus(mc_dev);
0519 else
0520 mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent));
0521
0522 if (!mc_bus->irq_resources)
0523 return;
0524
0525 for (i = 0; i < irq_count; i++) {
0526 irqs[i]->mc_dev = NULL;
0527 fsl_mc_resource_free(&irqs[i]->resource);
0528 }
0529
0530 mc_dev->irqs = NULL;
0531 }
0532 EXPORT_SYMBOL_GPL(fsl_mc_free_irqs);
0533
0534 void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
0535 {
0536 int pool_type;
0537 struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
0538
0539 for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) {
0540 struct fsl_mc_resource_pool *res_pool =
0541 &mc_bus->resource_pools[pool_type];
0542
0543 res_pool->type = pool_type;
0544 res_pool->max_count = 0;
0545 res_pool->free_count = 0;
0546 res_pool->mc_bus = mc_bus;
0547 INIT_LIST_HEAD(&res_pool->free_list);
0548 mutex_init(&res_pool->mutex);
0549 }
0550 }
0551
0552 static void fsl_mc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev,
0553 enum fsl_mc_pool_type pool_type)
0554 {
0555 struct fsl_mc_resource *resource;
0556 struct fsl_mc_resource *next;
0557 struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
0558 struct fsl_mc_resource_pool *res_pool =
0559 &mc_bus->resource_pools[pool_type];
0560 int free_count = 0;
0561
0562 list_for_each_entry_safe(resource, next, &res_pool->free_list, node) {
0563 free_count++;
0564 devm_kfree(&mc_bus_dev->dev, resource);
0565 }
0566 }
0567
0568 void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
0569 {
0570 int pool_type;
0571
0572 for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++)
0573 fsl_mc_cleanup_resource_pool(mc_bus_dev, pool_type);
0574 }
0575
0576
0577
0578
0579
0580 static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev)
0581 {
0582 enum fsl_mc_pool_type pool_type;
0583 struct fsl_mc_device *mc_bus_dev;
0584 struct fsl_mc_bus *mc_bus;
0585 int error;
0586
0587 if (!fsl_mc_is_allocatable(mc_dev))
0588 return -EINVAL;
0589
0590 mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
0591 if (!dev_is_fsl_mc(&mc_bus_dev->dev))
0592 return -EINVAL;
0593
0594 mc_bus = to_fsl_mc_bus(mc_bus_dev);
0595 error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type);
0596 if (error < 0)
0597 return error;
0598
0599 error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev);
0600 if (error < 0)
0601 return error;
0602
0603 dev_dbg(&mc_dev->dev,
0604 "Allocatable fsl-mc device bound to fsl_mc_allocator driver");
0605 return 0;
0606 }
0607
0608
0609
0610
0611
0612 static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)
0613 {
0614 int error;
0615
0616 if (!fsl_mc_is_allocatable(mc_dev))
0617 return -EINVAL;
0618
0619 if (mc_dev->resource) {
0620 error = fsl_mc_resource_pool_remove_device(mc_dev);
0621 if (error < 0)
0622 return error;
0623 }
0624
0625 dev_dbg(&mc_dev->dev,
0626 "Allocatable fsl-mc device unbound from fsl_mc_allocator driver");
0627 return 0;
0628 }
0629
0630 static const struct fsl_mc_device_id match_id_table[] = {
0631 {
0632 .vendor = FSL_MC_VENDOR_FREESCALE,
0633 .obj_type = "dpbp",
0634 },
0635 {
0636 .vendor = FSL_MC_VENDOR_FREESCALE,
0637 .obj_type = "dpmcp",
0638 },
0639 {
0640 .vendor = FSL_MC_VENDOR_FREESCALE,
0641 .obj_type = "dpcon",
0642 },
0643 {.vendor = 0x0},
0644 };
0645
0646 static struct fsl_mc_driver fsl_mc_allocator_driver = {
0647 .driver = {
0648 .name = "fsl_mc_allocator",
0649 .pm = NULL,
0650 },
0651 .match_id_table = match_id_table,
0652 .probe = fsl_mc_allocator_probe,
0653 .remove = fsl_mc_allocator_remove,
0654 };
0655
0656 int __init fsl_mc_allocator_driver_init(void)
0657 {
0658 return fsl_mc_driver_register(&fsl_mc_allocator_driver);
0659 }
0660
0661 void fsl_mc_allocator_driver_exit(void)
0662 {
0663 fsl_mc_driver_unregister(&fsl_mc_allocator_driver);
0664 }