linux/drivers/staging/fsl-mc/bus/mc-allocator.c
<<
>>
Prefs
   1/*
   2 * Freescale MC object device allocator driver
   3 *
   4 * Copyright (C) 2013 Freescale Semiconductor, Inc.
   5 *
   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.
   9 */
  10
  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"
  17#include "dpmcp.h"
  18
  19/**
  20 * fsl_mc_resource_pool_add_device - add allocatable device to a resource
  21 * pool of a given MC bus
  22 *
  23 * @mc_bus: pointer to the MC bus
  24 * @pool_type: MC bus pool type
  25 * @mc_dev: Pointer to allocatable MC object device
  26 *
  27 * It adds an allocatable MC object device to a container's resource pool of
  28 * the given resource type
  29 */
  30static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus
  31                                                                *mc_bus,
  32                                                        enum fsl_mc_pool_type
  33                                                                pool_type,
  34                                                        struct fsl_mc_device
  35                                                                *mc_dev)
  36{
  37        struct fsl_mc_resource_pool *res_pool;
  38        struct fsl_mc_resource *resource;
  39        struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
  40        int error = -EINVAL;
  41        bool mutex_locked = false;
  42
  43        if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES))
  44                goto out;
  45        if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
  46                goto out;
  47        if (WARN_ON(mc_dev->resource))
  48                goto out;
  49
  50        res_pool = &mc_bus->resource_pools[pool_type];
  51        if (WARN_ON(res_pool->type != pool_type))
  52                goto out;
  53        if (WARN_ON(res_pool->mc_bus != mc_bus))
  54                goto out;
  55
  56        mutex_lock(&res_pool->mutex);
  57        mutex_locked = true;
  58
  59        if (WARN_ON(res_pool->max_count < 0))
  60                goto out;
  61        if (WARN_ON(res_pool->free_count < 0 ||
  62                    res_pool->free_count > res_pool->max_count))
  63                goto out;
  64
  65        resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource),
  66                                GFP_KERNEL);
  67        if (!resource) {
  68                error = -ENOMEM;
  69                dev_err(&mc_bus_dev->dev,
  70                        "Failed to allocate memory for fsl_mc_resource\n");
  71                goto out;
  72        }
  73
  74        resource->type = pool_type;
  75        resource->id = mc_dev->obj_desc.id;
  76        resource->data = mc_dev;
  77        resource->parent_pool = res_pool;
  78        INIT_LIST_HEAD(&resource->node);
  79        list_add_tail(&resource->node, &res_pool->free_list);
  80        mc_dev->resource = resource;
  81        res_pool->free_count++;
  82        res_pool->max_count++;
  83        error = 0;
  84out:
  85        if (mutex_locked)
  86                mutex_unlock(&res_pool->mutex);
  87
  88        return error;
  89}
  90
  91/**
  92 * fsl_mc_resource_pool_remove_device - remove an allocatable device from a
  93 * resource pool
  94 *
  95 * @mc_dev: Pointer to allocatable MC object device
  96 *
  97 * It permanently removes an allocatable MC object device from the resource
  98 * pool, the device is currently in, as long as it is in the pool's free list.
  99 */
 100static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device
 101                                                                   *mc_dev)
 102{
 103        struct fsl_mc_device *mc_bus_dev;
 104        struct fsl_mc_bus *mc_bus;
 105        struct fsl_mc_resource_pool *res_pool;
 106        struct fsl_mc_resource *resource;
 107        int error = -EINVAL;
 108        bool mutex_locked = false;
 109
 110        if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
 111                goto out;
 112
 113        resource = mc_dev->resource;
 114        if (WARN_ON(resource->data != mc_dev))
 115                goto out;
 116
 117        mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
 118        mc_bus = to_fsl_mc_bus(mc_bus_dev);
 119        res_pool = resource->parent_pool;
 120        if (WARN_ON(res_pool != &mc_bus->resource_pools[resource->type]))
 121                goto out;
 122
 123        mutex_lock(&res_pool->mutex);
 124        mutex_locked = true;
 125
 126        if (WARN_ON(res_pool->max_count <= 0))
 127                goto out;
 128        if (WARN_ON(res_pool->free_count <= 0 ||
 129                    res_pool->free_count > res_pool->max_count))
 130                goto out;
 131
 132        /*
 133         * If the device is currently allocated, its resource is not
 134         * in the free list and thus, the device cannot be removed.
 135         */
 136        if (list_empty(&resource->node)) {
 137                error = -EBUSY;
 138                dev_err(&mc_bus_dev->dev,
 139                        "Device %s cannot be removed from resource pool\n",
 140                        dev_name(&mc_dev->dev));
 141                goto out;
 142        }
 143
 144        list_del(&resource->node);
 145        INIT_LIST_HEAD(&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:
 153        if (mutex_locked)
 154                mutex_unlock(&res_pool->mutex);
 155
 156        return error;
 157}
 158
 159static const char *const fsl_mc_pool_type_strings[] = {
 160        [FSL_MC_POOL_DPMCP] = "dpmcp",
 161        [FSL_MC_POOL_DPBP] = "dpbp",
 162        [FSL_MC_POOL_DPCON] = "dpcon",
 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        bool mutex_locked = false;
 190
 191        BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) !=
 192                     FSL_MC_NUM_POOL_TYPES);
 193
 194        *new_resource = NULL;
 195        if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES))
 196                goto error;
 197
 198        res_pool = &mc_bus->resource_pools[pool_type];
 199        if (WARN_ON(res_pool->mc_bus != mc_bus))
 200                goto error;
 201
 202        mutex_lock(&res_pool->mutex);
 203        mutex_locked = true;
 204        resource = list_first_entry_or_null(&res_pool->free_list,
 205                                            struct fsl_mc_resource, node);
 206
 207        if (!resource) {
 208                WARN_ON(res_pool->free_count != 0);
 209                error = -ENXIO;
 210                dev_err(&mc_bus_dev->dev,
 211                        "No more resources of type %s left\n",
 212                        fsl_mc_pool_type_strings[pool_type]);
 213                goto error;
 214        }
 215
 216        if (WARN_ON(resource->type != pool_type))
 217                goto error;
 218        if (WARN_ON(resource->parent_pool != res_pool))
 219                goto error;
 220        if (WARN_ON(res_pool->free_count <= 0 ||
 221                    res_pool->free_count > res_pool->max_count))
 222                goto error;
 223
 224        list_del(&resource->node);
 225        INIT_LIST_HEAD(&resource->node);
 226
 227        res_pool->free_count--;
 228        mutex_unlock(&res_pool->mutex);
 229        *new_resource = resource;
 230        return 0;
 231error:
 232        if (mutex_locked)
 233                mutex_unlock(&res_pool->mutex);
 234
 235        return error;
 236}
 237EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate);
 238
 239void fsl_mc_resource_free(struct fsl_mc_resource *resource)
 240{
 241        struct fsl_mc_resource_pool *res_pool;
 242        bool mutex_locked = false;
 243
 244        res_pool = resource->parent_pool;
 245        if (WARN_ON(resource->type != res_pool->type))
 246                goto out;
 247
 248        mutex_lock(&res_pool->mutex);
 249        mutex_locked = true;
 250        if (WARN_ON(res_pool->free_count < 0 ||
 251                    res_pool->free_count >= res_pool->max_count))
 252                goto out;
 253
 254        if (WARN_ON(!list_empty(&resource->node)))
 255                goto out;
 256
 257        list_add_tail(&resource->node, &res_pool->free_list);
 258        res_pool->free_count++;
 259out:
 260        if (mutex_locked)
 261                mutex_unlock(&res_pool->mutex);
 262}
 263EXPORT_SYMBOL_GPL(fsl_mc_resource_free);
 264
 265/**
 266 * fsl_mc_portal_allocate - Allocates an MC portal
 267 *
 268 * @mc_dev: MC device for which the MC portal is to be allocated
 269 * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated
 270 * MC portal.
 271 * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object
 272 * that wraps the allocated MC portal is to be returned
 273 *
 274 * This function allocates an MC portal from the device's parent DPRC,
 275 * from the corresponding MC bus' pool of MC portals and wraps
 276 * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the
 277 * portal is allocated from its own MC bus.
 278 */
 279int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev,
 280                                        uint16_t mc_io_flags,
 281                                        struct fsl_mc_io **new_mc_io)
 282{
 283        struct fsl_mc_device *mc_bus_dev;
 284        struct fsl_mc_bus *mc_bus;
 285        phys_addr_t mc_portal_phys_addr;
 286        size_t mc_portal_size;
 287        struct fsl_mc_device *mc_adev;
 288        int error = -EINVAL;
 289        struct fsl_mc_resource *resource = NULL;
 290        struct fsl_mc_io *mc_io = NULL;
 291
 292        if (mc_dev->flags & FSL_MC_IS_DPRC) {
 293                mc_bus_dev = mc_dev;
 294        } else {
 295                if (WARN_ON(mc_dev->dev.parent->bus != &fsl_mc_bus_type))
 296                        return error;
 297
 298                mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
 299        }
 300
 301        mc_bus = to_fsl_mc_bus(mc_bus_dev);
 302        *new_mc_io = NULL;
 303        error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource);
 304        if (error < 0)
 305                return error;
 306
 307        mc_adev = resource->data;
 308        if (WARN_ON(!mc_adev))
 309                goto error_cleanup_resource;
 310
 311        if (WARN_ON(mc_adev->obj_desc.region_count == 0))
 312                goto error_cleanup_resource;
 313
 314        mc_portal_phys_addr = mc_adev->regions[0].start;
 315        mc_portal_size = mc_adev->regions[0].end -
 316                         mc_adev->regions[0].start + 1;
 317
 318        if (WARN_ON(mc_portal_size != mc_bus_dev->mc_io->portal_size))
 319                goto error_cleanup_resource;
 320
 321        error = fsl_create_mc_io(&mc_bus_dev->dev,
 322                                 mc_portal_phys_addr,
 323                                 mc_portal_size, resource,
 324                                 mc_io_flags, &mc_io);
 325        if (error < 0)
 326                goto error_cleanup_resource;
 327
 328        *new_mc_io = mc_io;
 329        return 0;
 330
 331error_cleanup_resource:
 332        fsl_mc_resource_free(resource);
 333        return error;
 334}
 335EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate);
 336
 337/**
 338 * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals
 339 * of a given MC bus
 340 *
 341 * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
 342 */
 343void fsl_mc_portal_free(struct fsl_mc_io *mc_io)
 344{
 345        struct fsl_mc_resource *resource;
 346
 347        resource = mc_io->resource;
 348        if (WARN_ON(resource->type != FSL_MC_POOL_DPMCP))
 349                return;
 350        if (WARN_ON(!resource->data))
 351                return;
 352
 353        fsl_destroy_mc_io(mc_io);
 354        fsl_mc_resource_free(resource);
 355}
 356EXPORT_SYMBOL_GPL(fsl_mc_portal_free);
 357
 358/**
 359 * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object
 360 *
 361 * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
 362 */
 363int fsl_mc_portal_reset(struct fsl_mc_io *mc_io)
 364{
 365        int error;
 366        uint16_t token;
 367        struct fsl_mc_resource *resource = mc_io->resource;
 368        struct fsl_mc_device *mc_dev = resource->data;
 369
 370        if (WARN_ON(resource->type != FSL_MC_POOL_DPMCP))
 371                return -EINVAL;
 372
 373        if (WARN_ON(!mc_dev))
 374                return -EINVAL;
 375
 376        error = dpmcp_open(mc_io, mc_dev->obj_desc.id, &token);
 377        if (error < 0) {
 378                dev_err(&mc_dev->dev, "dpmcp_open() failed: %d\n", error);
 379                return error;
 380        }
 381
 382        error = dpmcp_reset(mc_io, token);
 383        if (error < 0) {
 384                dev_err(&mc_dev->dev, "dpmcp_reset() failed: %d\n", error);
 385                return error;
 386        }
 387
 388        error = dpmcp_close(mc_io, token);
 389        if (error < 0) {
 390                dev_err(&mc_dev->dev, "dpmcp_close() failed: %d\n", error);
 391                return error;
 392        }
 393
 394        return 0;
 395}
 396EXPORT_SYMBOL_GPL(fsl_mc_portal_reset);
 397
 398/**
 399 * fsl_mc_object_allocate - Allocates a MC object device of the given
 400 * pool type from a given MC bus
 401 *
 402 * @mc_dev: MC device for which the MC object device is to be allocated
 403 * @pool_type: MC bus resource pool type
 404 * @new_mc_dev: Pointer to area where the pointer to the allocated
 405 * MC object device is to be returned
 406 *
 407 * This function allocates a MC object device from the device's parent DPRC,
 408 * from the corresponding MC bus' pool of allocatable MC object devices of
 409 * the given resource type. mc_dev cannot be a DPRC itself.
 410 *
 411 * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC
 412 * portals are allocated using fsl_mc_portal_allocate(), instead of
 413 * this function.
 414 */
 415int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev,
 416                                        enum fsl_mc_pool_type pool_type,
 417                                        struct fsl_mc_device **new_mc_adev)
 418{
 419        struct fsl_mc_device *mc_bus_dev;
 420        struct fsl_mc_bus *mc_bus;
 421        struct fsl_mc_device *mc_adev;
 422        int error = -EINVAL;
 423        struct fsl_mc_resource *resource = NULL;
 424
 425        *new_mc_adev = NULL;
 426        if (WARN_ON(mc_dev->flags & FSL_MC_IS_DPRC))
 427                goto error;
 428
 429        if (WARN_ON(mc_dev->dev.parent->bus != &fsl_mc_bus_type))
 430                goto error;
 431
 432        if (WARN_ON(pool_type == FSL_MC_POOL_DPMCP))
 433                goto error;
 434
 435        mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
 436        mc_bus = to_fsl_mc_bus(mc_bus_dev);
 437        error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource);
 438        if (error < 0)
 439                goto error;
 440
 441        mc_adev = resource->data;
 442        if (WARN_ON(!mc_adev))
 443                goto error;
 444
 445        *new_mc_adev = mc_adev;
 446        return 0;
 447error:
 448        if (resource)
 449                fsl_mc_resource_free(resource);
 450
 451        return error;
 452}
 453EXPORT_SYMBOL_GPL(fsl_mc_object_allocate);
 454
 455/**
 456 * fsl_mc_object_free - Returns an allocatable MC object device to the
 457 * corresponding resource pool of a given MC bus.
 458 *
 459 * @mc_adev: Pointer to the MC object device
 460 */
 461void fsl_mc_object_free(struct fsl_mc_device *mc_adev)
 462{
 463        struct fsl_mc_resource *resource;
 464
 465        resource = mc_adev->resource;
 466        if (WARN_ON(resource->type == FSL_MC_POOL_DPMCP))
 467                return;
 468        if (WARN_ON(resource->data != mc_adev))
 469                return;
 470
 471        fsl_mc_resource_free(resource);
 472}
 473EXPORT_SYMBOL_GPL(fsl_mc_object_free);
 474
 475/**
 476 * fsl_mc_allocator_probe - callback invoked when an allocatable device is
 477 * being added to the system
 478 */
 479static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev)
 480{
 481        enum fsl_mc_pool_type pool_type;
 482        struct fsl_mc_device *mc_bus_dev;
 483        struct fsl_mc_bus *mc_bus;
 484        int error = -EINVAL;
 485
 486        if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
 487                goto error;
 488
 489        mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
 490        if (WARN_ON(mc_bus_dev->dev.bus != &fsl_mc_bus_type))
 491                goto error;
 492
 493        mc_bus = to_fsl_mc_bus(mc_bus_dev);
 494        error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type);
 495        if (error < 0)
 496                goto error;
 497
 498        error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev);
 499        if (error < 0)
 500                goto error;
 501
 502        dev_info(&mc_dev->dev,
 503                 "Allocatable MC object device bound to fsl_mc_allocator driver");
 504        return 0;
 505error:
 506
 507        return error;
 508}
 509
 510/**
 511 * fsl_mc_allocator_remove - callback invoked when an allocatable device is
 512 * being removed from the system
 513 */
 514static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)
 515{
 516        int error = -EINVAL;
 517
 518        if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
 519                goto out;
 520
 521        error = fsl_mc_resource_pool_remove_device(mc_dev);
 522        if (error < 0)
 523                goto out;
 524
 525        dev_info(&mc_dev->dev,
 526                 "Allocatable MC object device unbound from fsl_mc_allocator driver");
 527        error = 0;
 528out:
 529        return error;
 530}
 531
 532static const struct fsl_mc_device_match_id match_id_table[] = {
 533        {
 534         .vendor = FSL_MC_VENDOR_FREESCALE,
 535         .obj_type = "dpbp",
 536         .ver_major = DPBP_VER_MAJOR,
 537         .ver_minor = DPBP_VER_MINOR
 538        },
 539        {
 540         .vendor = FSL_MC_VENDOR_FREESCALE,
 541         .obj_type = "dpmcp",
 542         .ver_major = DPMCP_VER_MAJOR,
 543         .ver_minor = DPMCP_VER_MINOR
 544        },
 545        {
 546         .vendor = FSL_MC_VENDOR_FREESCALE,
 547         .obj_type = "dpcon",
 548         .ver_major = DPCON_VER_MAJOR,
 549         .ver_minor = DPCON_VER_MINOR
 550        },
 551        {.vendor = 0x0},
 552};
 553
 554static struct fsl_mc_driver fsl_mc_allocator_driver = {
 555        .driver = {
 556                   .name = "fsl_mc_allocator",
 557                   .owner = THIS_MODULE,
 558                   .pm = NULL,
 559                   },
 560        .match_id_table = match_id_table,
 561        .probe = fsl_mc_allocator_probe,
 562        .remove = fsl_mc_allocator_remove,
 563};
 564
 565int __init fsl_mc_allocator_driver_init(void)
 566{
 567        return fsl_mc_driver_register(&fsl_mc_allocator_driver);
 568}
 569
 570void __exit fsl_mc_allocator_driver_exit(void)
 571{
 572        fsl_mc_driver_unregister(&fsl_mc_allocator_driver);
 573}
 574