diff options
Diffstat (limited to 'drivers/staging/fsl-mc/bus/fsl-mc-allocator.c')
-rw-r--r-- | drivers/staging/fsl-mc/bus/fsl-mc-allocator.c | 648 |
1 files changed, 0 insertions, 648 deletions
diff --git a/drivers/staging/fsl-mc/bus/fsl-mc-allocator.c b/drivers/staging/fsl-mc/bus/fsl-mc-allocator.c deleted file mode 100644 index 8f313a41240b..000000000000 --- a/drivers/staging/fsl-mc/bus/fsl-mc-allocator.c +++ /dev/null @@ -1,648 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * fsl-mc object allocator driver - * - * Copyright (C) 2013-2016 Freescale Semiconductor, Inc. - * - */ - -#include <linux/module.h> -#include <linux/msi.h> -#include "../include/mc.h" - -#include "fsl-mc-private.h" - -static bool __must_check fsl_mc_is_allocatable(struct fsl_mc_device *mc_dev) -{ - return is_fsl_mc_bus_dpbp(mc_dev) || - is_fsl_mc_bus_dpmcp(mc_dev) || - is_fsl_mc_bus_dpcon(mc_dev); -} - -/** - * fsl_mc_resource_pool_add_device - add allocatable object to a resource - * pool of a given fsl-mc bus - * - * @mc_bus: pointer to the fsl-mc bus - * @pool_type: pool type - * @mc_dev: pointer to allocatable fsl-mc device - */ -static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus - *mc_bus, - enum fsl_mc_pool_type - pool_type, - struct fsl_mc_device - *mc_dev) -{ - struct fsl_mc_resource_pool *res_pool; - struct fsl_mc_resource *resource; - struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; - int error = -EINVAL; - - if (pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES) - goto out; - if (!fsl_mc_is_allocatable(mc_dev)) - goto out; - if (mc_dev->resource) - goto out; - - res_pool = &mc_bus->resource_pools[pool_type]; - if (res_pool->type != pool_type) - goto out; - if (res_pool->mc_bus != mc_bus) - goto out; - - mutex_lock(&res_pool->mutex); - - if (res_pool->max_count < 0) - goto out_unlock; - if (res_pool->free_count < 0 || - res_pool->free_count > res_pool->max_count) - goto out_unlock; - - resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource), - GFP_KERNEL); - if (!resource) { - error = -ENOMEM; - dev_err(&mc_bus_dev->dev, - "Failed to allocate memory for fsl_mc_resource\n"); - goto out_unlock; - } - - resource->type = pool_type; - resource->id = mc_dev->obj_desc.id; - resource->data = mc_dev; - resource->parent_pool = res_pool; - INIT_LIST_HEAD(&resource->node); - list_add_tail(&resource->node, &res_pool->free_list); - mc_dev->resource = resource; - res_pool->free_count++; - res_pool->max_count++; - error = 0; -out_unlock: - mutex_unlock(&res_pool->mutex); -out: - return error; -} - -/** - * fsl_mc_resource_pool_remove_device - remove an allocatable device from a - * resource pool - * - * @mc_dev: pointer to allocatable fsl-mc device - * - * It permanently removes an allocatable fsl-mc device from the resource - * pool. It's an error if the device is in use. - */ -static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device - *mc_dev) -{ - struct fsl_mc_device *mc_bus_dev; - struct fsl_mc_bus *mc_bus; - struct fsl_mc_resource_pool *res_pool; - struct fsl_mc_resource *resource; - int error = -EINVAL; - - if (!fsl_mc_is_allocatable(mc_dev)) - goto out; - - resource = mc_dev->resource; - if (!resource || resource->data != mc_dev) - goto out; - - mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); - mc_bus = to_fsl_mc_bus(mc_bus_dev); - res_pool = resource->parent_pool; - if (res_pool != &mc_bus->resource_pools[resource->type]) - goto out; - - mutex_lock(&res_pool->mutex); - - if (res_pool->max_count <= 0) - goto out_unlock; - if (res_pool->free_count <= 0 || - res_pool->free_count > res_pool->max_count) - goto out_unlock; - - /* - * If the device is currently allocated, its resource is not - * in the free list and thus, the device cannot be removed. - */ - if (list_empty(&resource->node)) { - error = -EBUSY; - dev_err(&mc_bus_dev->dev, - "Device %s cannot be removed from resource pool\n", - dev_name(&mc_dev->dev)); - goto out_unlock; - } - - list_del_init(&resource->node); - res_pool->free_count--; - res_pool->max_count--; - - devm_kfree(&mc_bus_dev->dev, resource); - mc_dev->resource = NULL; - error = 0; -out_unlock: - mutex_unlock(&res_pool->mutex); -out: - return error; -} - -static const char *const fsl_mc_pool_type_strings[] = { - [FSL_MC_POOL_DPMCP] = "dpmcp", - [FSL_MC_POOL_DPBP] = "dpbp", - [FSL_MC_POOL_DPCON] = "dpcon", - [FSL_MC_POOL_IRQ] = "irq", -}; - -static int __must_check object_type_to_pool_type(const char *object_type, - enum fsl_mc_pool_type - *pool_type) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(fsl_mc_pool_type_strings); i++) { - if (strcmp(object_type, fsl_mc_pool_type_strings[i]) == 0) { - *pool_type = i; - return 0; - } - } - - return -EINVAL; -} - -int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus, - enum fsl_mc_pool_type pool_type, - struct fsl_mc_resource **new_resource) -{ - struct fsl_mc_resource_pool *res_pool; - struct fsl_mc_resource *resource; - struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; - int error = -EINVAL; - - BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) != - FSL_MC_NUM_POOL_TYPES); - - *new_resource = NULL; - if (pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES) - goto out; - - res_pool = &mc_bus->resource_pools[pool_type]; - if (res_pool->mc_bus != mc_bus) - goto out; - - mutex_lock(&res_pool->mutex); - resource = list_first_entry_or_null(&res_pool->free_list, - struct fsl_mc_resource, node); - - if (!resource) { - error = -ENXIO; - dev_err(&mc_bus_dev->dev, - "No more resources of type %s left\n", - fsl_mc_pool_type_strings[pool_type]); - goto out_unlock; - } - - if (resource->type != pool_type) - goto out_unlock; - if (resource->parent_pool != res_pool) - goto out_unlock; - if (res_pool->free_count <= 0 || - res_pool->free_count > res_pool->max_count) - goto out_unlock; - - list_del_init(&resource->node); - - res_pool->free_count--; - error = 0; -out_unlock: - mutex_unlock(&res_pool->mutex); - *new_resource = resource; -out: - return error; -} -EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate); - -void fsl_mc_resource_free(struct fsl_mc_resource *resource) -{ - struct fsl_mc_resource_pool *res_pool; - - res_pool = resource->parent_pool; - if (resource->type != res_pool->type) - return; - - mutex_lock(&res_pool->mutex); - if (res_pool->free_count < 0 || - res_pool->free_count >= res_pool->max_count) - goto out_unlock; - - if (!list_empty(&resource->node)) - goto out_unlock; - - list_add_tail(&resource->node, &res_pool->free_list); - res_pool->free_count++; -out_unlock: - mutex_unlock(&res_pool->mutex); -} -EXPORT_SYMBOL_GPL(fsl_mc_resource_free); - -/** - * fsl_mc_object_allocate - Allocates an fsl-mc object of the given - * pool type from a given fsl-mc bus instance - * - * @mc_dev: fsl-mc device which is used in conjunction with the - * allocated object - * @pool_type: pool type - * @new_mc_dev: pointer to area where the pointer to the allocated device - * is to be returned - * - * Allocatable objects are always used in conjunction with some functional - * device. This function allocates an object of the specified type from - * the DPRC containing the functional device. - * - * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC - * portals are allocated using fsl_mc_portal_allocate(), instead of - * this function. - */ -int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev, - enum fsl_mc_pool_type pool_type, - struct fsl_mc_device **new_mc_adev) -{ - struct fsl_mc_device *mc_bus_dev; - struct fsl_mc_bus *mc_bus; - struct fsl_mc_device *mc_adev; - int error = -EINVAL; - struct fsl_mc_resource *resource = NULL; - - *new_mc_adev = NULL; - if (mc_dev->flags & FSL_MC_IS_DPRC) - goto error; - - if (!dev_is_fsl_mc(mc_dev->dev.parent)) - goto error; - - if (pool_type == FSL_MC_POOL_DPMCP) - goto error; - - mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); - mc_bus = to_fsl_mc_bus(mc_bus_dev); - error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource); - if (error < 0) - goto error; - - mc_adev = resource->data; - if (!mc_adev) - goto error; - - *new_mc_adev = mc_adev; - return 0; -error: - if (resource) - fsl_mc_resource_free(resource); - - return error; -} -EXPORT_SYMBOL_GPL(fsl_mc_object_allocate); - -/** - * fsl_mc_object_free - Returns an fsl-mc object to the resource - * pool where it came from. - * @mc_adev: Pointer to the fsl-mc device - */ -void fsl_mc_object_free(struct fsl_mc_device *mc_adev) -{ - struct fsl_mc_resource *resource; - - resource = mc_adev->resource; - if (resource->type == FSL_MC_POOL_DPMCP) - return; - if (resource->data != mc_adev) - return; - - fsl_mc_resource_free(resource); -} -EXPORT_SYMBOL_GPL(fsl_mc_object_free); - -/* - * A DPRC and the devices in the DPRC all share the same GIC-ITS device - * ID. A block of IRQs is pre-allocated and maintained in a pool - * from which devices can allocate them when needed. - */ - -/* - * Initialize the interrupt pool associated with an fsl-mc bus. - * It allocates a block of IRQs from the GIC-ITS. - */ -int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus, - unsigned int irq_count) -{ - unsigned int i; - struct msi_desc *msi_desc; - struct fsl_mc_device_irq *irq_resources; - struct fsl_mc_device_irq *mc_dev_irq; - int error; - struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; - struct fsl_mc_resource_pool *res_pool = - &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; - - if (irq_count == 0 || - irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) - return -EINVAL; - - error = fsl_mc_msi_domain_alloc_irqs(&mc_bus_dev->dev, irq_count); - if (error < 0) - return error; - - irq_resources = devm_kzalloc(&mc_bus_dev->dev, - sizeof(*irq_resources) * irq_count, - GFP_KERNEL); - if (!irq_resources) { - error = -ENOMEM; - goto cleanup_msi_irqs; - } - - for (i = 0; i < irq_count; i++) { - mc_dev_irq = &irq_resources[i]; - - /* - * NOTE: This mc_dev_irq's MSI addr/value pair will be set - * by the fsl_mc_msi_write_msg() callback - */ - mc_dev_irq->resource.type = res_pool->type; - mc_dev_irq->resource.data = mc_dev_irq; - mc_dev_irq->resource.parent_pool = res_pool; - INIT_LIST_HEAD(&mc_dev_irq->resource.node); - list_add_tail(&mc_dev_irq->resource.node, &res_pool->free_list); - } - - for_each_msi_entry(msi_desc, &mc_bus_dev->dev) { - mc_dev_irq = &irq_resources[msi_desc->fsl_mc.msi_index]; - mc_dev_irq->msi_desc = msi_desc; - mc_dev_irq->resource.id = msi_desc->irq; - } - - res_pool->max_count = irq_count; - res_pool->free_count = irq_count; - mc_bus->irq_resources = irq_resources; - return 0; - -cleanup_msi_irqs: - fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev); - return error; -} -EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool); - -/** - * Teardown the interrupt pool associated with an fsl-mc bus. - * It frees the IRQs that were allocated to the pool, back to the GIC-ITS. - */ -void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus) -{ - struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; - struct fsl_mc_resource_pool *res_pool = - &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; - - if (!mc_bus->irq_resources) - return; - - if (res_pool->max_count == 0) - return; - - if (res_pool->free_count != res_pool->max_count) - return; - - INIT_LIST_HEAD(&res_pool->free_list); - res_pool->max_count = 0; - res_pool->free_count = 0; - mc_bus->irq_resources = NULL; - fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev); -} -EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool); - -/** - * Allocate the IRQs required by a given fsl-mc device. - */ -int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev) -{ - int i; - int irq_count; - int res_allocated_count = 0; - int error = -EINVAL; - struct fsl_mc_device_irq **irqs = NULL; - struct fsl_mc_bus *mc_bus; - struct fsl_mc_resource_pool *res_pool; - - if (mc_dev->irqs) - return -EINVAL; - - irq_count = mc_dev->obj_desc.irq_count; - if (irq_count == 0) - return -EINVAL; - - if (is_fsl_mc_bus_dprc(mc_dev)) - mc_bus = to_fsl_mc_bus(mc_dev); - else - mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); - - if (!mc_bus->irq_resources) - return -EINVAL; - - res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; - if (res_pool->free_count < irq_count) { - dev_err(&mc_dev->dev, - "Not able to allocate %u irqs for device\n", irq_count); - return -ENOSPC; - } - - irqs = devm_kzalloc(&mc_dev->dev, irq_count * sizeof(irqs[0]), - GFP_KERNEL); - if (!irqs) - return -ENOMEM; - - for (i = 0; i < irq_count; i++) { - struct fsl_mc_resource *resource; - - error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ, - &resource); - if (error < 0) - goto error_resource_alloc; - - irqs[i] = to_fsl_mc_irq(resource); - res_allocated_count++; - - irqs[i]->mc_dev = mc_dev; - irqs[i]->dev_irq_index = i; - } - - mc_dev->irqs = irqs; - return 0; - -error_resource_alloc: - for (i = 0; i < res_allocated_count; i++) { - irqs[i]->mc_dev = NULL; - fsl_mc_resource_free(&irqs[i]->resource); - } - - return error; -} -EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs); - -/* - * Frees the IRQs that were allocated for an fsl-mc device. - */ -void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev) -{ - int i; - int irq_count; - struct fsl_mc_bus *mc_bus; - struct fsl_mc_device_irq **irqs = mc_dev->irqs; - - if (!irqs) - return; - - irq_count = mc_dev->obj_desc.irq_count; - - if (is_fsl_mc_bus_dprc(mc_dev)) - mc_bus = to_fsl_mc_bus(mc_dev); - else - mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); - - if (!mc_bus->irq_resources) - return; - - for (i = 0; i < irq_count; i++) { - irqs[i]->mc_dev = NULL; - fsl_mc_resource_free(&irqs[i]->resource); - } - - mc_dev->irqs = NULL; -} -EXPORT_SYMBOL_GPL(fsl_mc_free_irqs); - -void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev) -{ - int pool_type; - struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); - - for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) { - struct fsl_mc_resource_pool *res_pool = - &mc_bus->resource_pools[pool_type]; - - res_pool->type = pool_type; - res_pool->max_count = 0; - res_pool->free_count = 0; - res_pool->mc_bus = mc_bus; - INIT_LIST_HEAD(&res_pool->free_list); - mutex_init(&res_pool->mutex); - } -} - -static void fsl_mc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev, - enum fsl_mc_pool_type pool_type) -{ - struct fsl_mc_resource *resource; - struct fsl_mc_resource *next; - struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); - struct fsl_mc_resource_pool *res_pool = - &mc_bus->resource_pools[pool_type]; - int free_count = 0; - - list_for_each_entry_safe(resource, next, &res_pool->free_list, node) { - free_count++; - devm_kfree(&mc_bus_dev->dev, resource); - } -} - -void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev) -{ - int pool_type; - - for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) - fsl_mc_cleanup_resource_pool(mc_bus_dev, pool_type); -} - -/** - * fsl_mc_allocator_probe - callback invoked when an allocatable device is - * being added to the system - */ -static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev) -{ - enum fsl_mc_pool_type pool_type; - struct fsl_mc_device *mc_bus_dev; - struct fsl_mc_bus *mc_bus; - int error; - - if (!fsl_mc_is_allocatable(mc_dev)) - return -EINVAL; - - mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); - if (!dev_is_fsl_mc(&mc_bus_dev->dev)) - return -EINVAL; - - mc_bus = to_fsl_mc_bus(mc_bus_dev); - error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type); - if (error < 0) - return error; - - error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev); - if (error < 0) - return error; - - dev_dbg(&mc_dev->dev, - "Allocatable fsl-mc device bound to fsl_mc_allocator driver"); - return 0; -} - -/** - * fsl_mc_allocator_remove - callback invoked when an allocatable device is - * being removed from the system - */ -static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev) -{ - int error; - - if (!fsl_mc_is_allocatable(mc_dev)) - return -EINVAL; - - if (mc_dev->resource) { - error = fsl_mc_resource_pool_remove_device(mc_dev); - if (error < 0) - return error; - } - - dev_dbg(&mc_dev->dev, - "Allocatable fsl-mc device unbound from fsl_mc_allocator driver"); - return 0; -} - -static const struct fsl_mc_device_id match_id_table[] = { - { - .vendor = FSL_MC_VENDOR_FREESCALE, - .obj_type = "dpbp", - }, - { - .vendor = FSL_MC_VENDOR_FREESCALE, - .obj_type = "dpmcp", - }, - { - .vendor = FSL_MC_VENDOR_FREESCALE, - .obj_type = "dpcon", - }, - {.vendor = 0x0}, -}; - -static struct fsl_mc_driver fsl_mc_allocator_driver = { - .driver = { - .name = "fsl_mc_allocator", - .pm = NULL, - }, - .match_id_table = match_id_table, - .probe = fsl_mc_allocator_probe, - .remove = fsl_mc_allocator_remove, -}; - -int __init fsl_mc_allocator_driver_init(void) -{ - return fsl_mc_driver_register(&fsl_mc_allocator_driver); -} |