162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * fsl-mc object allocator driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013-2016 Freescale Semiconductor, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/msi.h> 1162306a36Sopenharmony_ci#include <linux/fsl/mc.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "fsl-mc-private.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic bool __must_check fsl_mc_is_allocatable(struct fsl_mc_device *mc_dev) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci return is_fsl_mc_bus_dpbp(mc_dev) || 1862306a36Sopenharmony_ci is_fsl_mc_bus_dpmcp(mc_dev) || 1962306a36Sopenharmony_ci is_fsl_mc_bus_dpcon(mc_dev); 2062306a36Sopenharmony_ci} 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/** 2362306a36Sopenharmony_ci * fsl_mc_resource_pool_add_device - add allocatable object to a resource 2462306a36Sopenharmony_ci * pool of a given fsl-mc bus 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * @mc_bus: pointer to the fsl-mc bus 2762306a36Sopenharmony_ci * @pool_type: pool type 2862306a36Sopenharmony_ci * @mc_dev: pointer to allocatable fsl-mc device 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_cistatic int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus 3162306a36Sopenharmony_ci *mc_bus, 3262306a36Sopenharmony_ci enum fsl_mc_pool_type 3362306a36Sopenharmony_ci pool_type, 3462306a36Sopenharmony_ci struct fsl_mc_device 3562306a36Sopenharmony_ci *mc_dev) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct fsl_mc_resource_pool *res_pool; 3862306a36Sopenharmony_ci struct fsl_mc_resource *resource; 3962306a36Sopenharmony_ci struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; 4062306a36Sopenharmony_ci int error = -EINVAL; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES) 4362306a36Sopenharmony_ci goto out; 4462306a36Sopenharmony_ci if (!fsl_mc_is_allocatable(mc_dev)) 4562306a36Sopenharmony_ci goto out; 4662306a36Sopenharmony_ci if (mc_dev->resource) 4762306a36Sopenharmony_ci goto out; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci res_pool = &mc_bus->resource_pools[pool_type]; 5062306a36Sopenharmony_ci if (res_pool->type != pool_type) 5162306a36Sopenharmony_ci goto out; 5262306a36Sopenharmony_ci if (res_pool->mc_bus != mc_bus) 5362306a36Sopenharmony_ci goto out; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci mutex_lock(&res_pool->mutex); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (res_pool->max_count < 0) 5862306a36Sopenharmony_ci goto out_unlock; 5962306a36Sopenharmony_ci if (res_pool->free_count < 0 || 6062306a36Sopenharmony_ci res_pool->free_count > res_pool->max_count) 6162306a36Sopenharmony_ci goto out_unlock; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource), 6462306a36Sopenharmony_ci GFP_KERNEL); 6562306a36Sopenharmony_ci if (!resource) { 6662306a36Sopenharmony_ci error = -ENOMEM; 6762306a36Sopenharmony_ci dev_err(&mc_bus_dev->dev, 6862306a36Sopenharmony_ci "Failed to allocate memory for fsl_mc_resource\n"); 6962306a36Sopenharmony_ci goto out_unlock; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci resource->type = pool_type; 7362306a36Sopenharmony_ci resource->id = mc_dev->obj_desc.id; 7462306a36Sopenharmony_ci resource->data = mc_dev; 7562306a36Sopenharmony_ci resource->parent_pool = res_pool; 7662306a36Sopenharmony_ci INIT_LIST_HEAD(&resource->node); 7762306a36Sopenharmony_ci list_add_tail(&resource->node, &res_pool->free_list); 7862306a36Sopenharmony_ci mc_dev->resource = resource; 7962306a36Sopenharmony_ci res_pool->free_count++; 8062306a36Sopenharmony_ci res_pool->max_count++; 8162306a36Sopenharmony_ci error = 0; 8262306a36Sopenharmony_ciout_unlock: 8362306a36Sopenharmony_ci mutex_unlock(&res_pool->mutex); 8462306a36Sopenharmony_ciout: 8562306a36Sopenharmony_ci return error; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/** 8962306a36Sopenharmony_ci * fsl_mc_resource_pool_remove_device - remove an allocatable device from a 9062306a36Sopenharmony_ci * resource pool 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * @mc_dev: pointer to allocatable fsl-mc device 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * It permanently removes an allocatable fsl-mc device from the resource 9562306a36Sopenharmony_ci * pool. It's an error if the device is in use. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cistatic int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device 9862306a36Sopenharmony_ci *mc_dev) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct fsl_mc_device *mc_bus_dev; 10162306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus; 10262306a36Sopenharmony_ci struct fsl_mc_resource_pool *res_pool; 10362306a36Sopenharmony_ci struct fsl_mc_resource *resource; 10462306a36Sopenharmony_ci int error = -EINVAL; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); 10762306a36Sopenharmony_ci mc_bus = to_fsl_mc_bus(mc_bus_dev); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci resource = mc_dev->resource; 11062306a36Sopenharmony_ci if (!resource || resource->data != mc_dev) { 11162306a36Sopenharmony_ci dev_err(&mc_bus_dev->dev, "resource mismatch\n"); 11262306a36Sopenharmony_ci goto out; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci res_pool = resource->parent_pool; 11662306a36Sopenharmony_ci if (res_pool != &mc_bus->resource_pools[resource->type]) { 11762306a36Sopenharmony_ci dev_err(&mc_bus_dev->dev, "pool mismatch\n"); 11862306a36Sopenharmony_ci goto out; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci mutex_lock(&res_pool->mutex); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (res_pool->max_count <= 0) { 12462306a36Sopenharmony_ci dev_err(&mc_bus_dev->dev, "max_count underflow\n"); 12562306a36Sopenharmony_ci goto out_unlock; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci if (res_pool->free_count <= 0 || 12862306a36Sopenharmony_ci res_pool->free_count > res_pool->max_count) { 12962306a36Sopenharmony_ci dev_err(&mc_bus_dev->dev, "free_count mismatch\n"); 13062306a36Sopenharmony_ci goto out_unlock; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* 13462306a36Sopenharmony_ci * If the device is currently allocated, its resource is not 13562306a36Sopenharmony_ci * in the free list and thus, the device cannot be removed. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci if (list_empty(&resource->node)) { 13862306a36Sopenharmony_ci error = -EBUSY; 13962306a36Sopenharmony_ci dev_err(&mc_bus_dev->dev, 14062306a36Sopenharmony_ci "Device %s cannot be removed from resource pool\n", 14162306a36Sopenharmony_ci dev_name(&mc_dev->dev)); 14262306a36Sopenharmony_ci goto out_unlock; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci list_del_init(&resource->node); 14662306a36Sopenharmony_ci res_pool->free_count--; 14762306a36Sopenharmony_ci res_pool->max_count--; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci devm_kfree(&mc_bus_dev->dev, resource); 15062306a36Sopenharmony_ci mc_dev->resource = NULL; 15162306a36Sopenharmony_ci error = 0; 15262306a36Sopenharmony_ciout_unlock: 15362306a36Sopenharmony_ci mutex_unlock(&res_pool->mutex); 15462306a36Sopenharmony_ciout: 15562306a36Sopenharmony_ci return error; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic const char *const fsl_mc_pool_type_strings[] = { 15962306a36Sopenharmony_ci [FSL_MC_POOL_DPMCP] = "dpmcp", 16062306a36Sopenharmony_ci [FSL_MC_POOL_DPBP] = "dpbp", 16162306a36Sopenharmony_ci [FSL_MC_POOL_DPCON] = "dpcon", 16262306a36Sopenharmony_ci [FSL_MC_POOL_IRQ] = "irq", 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int __must_check object_type_to_pool_type(const char *object_type, 16662306a36Sopenharmony_ci enum fsl_mc_pool_type 16762306a36Sopenharmony_ci *pool_type) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci unsigned int i; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fsl_mc_pool_type_strings); i++) { 17262306a36Sopenharmony_ci if (strcmp(object_type, fsl_mc_pool_type_strings[i]) == 0) { 17362306a36Sopenharmony_ci *pool_type = i; 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return -EINVAL; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ciint __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus, 18262306a36Sopenharmony_ci enum fsl_mc_pool_type pool_type, 18362306a36Sopenharmony_ci struct fsl_mc_resource **new_resource) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct fsl_mc_resource_pool *res_pool; 18662306a36Sopenharmony_ci struct fsl_mc_resource *resource; 18762306a36Sopenharmony_ci struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; 18862306a36Sopenharmony_ci int error = -EINVAL; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) != 19162306a36Sopenharmony_ci FSL_MC_NUM_POOL_TYPES); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci *new_resource = NULL; 19462306a36Sopenharmony_ci if (pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES) 19562306a36Sopenharmony_ci goto out; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci res_pool = &mc_bus->resource_pools[pool_type]; 19862306a36Sopenharmony_ci if (res_pool->mc_bus != mc_bus) 19962306a36Sopenharmony_ci goto out; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci mutex_lock(&res_pool->mutex); 20262306a36Sopenharmony_ci resource = list_first_entry_or_null(&res_pool->free_list, 20362306a36Sopenharmony_ci struct fsl_mc_resource, node); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (!resource) { 20662306a36Sopenharmony_ci error = -ENXIO; 20762306a36Sopenharmony_ci dev_err(&mc_bus_dev->dev, 20862306a36Sopenharmony_ci "No more resources of type %s left\n", 20962306a36Sopenharmony_ci fsl_mc_pool_type_strings[pool_type]); 21062306a36Sopenharmony_ci goto out_unlock; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (resource->type != pool_type) 21462306a36Sopenharmony_ci goto out_unlock; 21562306a36Sopenharmony_ci if (resource->parent_pool != res_pool) 21662306a36Sopenharmony_ci goto out_unlock; 21762306a36Sopenharmony_ci if (res_pool->free_count <= 0 || 21862306a36Sopenharmony_ci res_pool->free_count > res_pool->max_count) 21962306a36Sopenharmony_ci goto out_unlock; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci list_del_init(&resource->node); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci res_pool->free_count--; 22462306a36Sopenharmony_ci error = 0; 22562306a36Sopenharmony_ciout_unlock: 22662306a36Sopenharmony_ci mutex_unlock(&res_pool->mutex); 22762306a36Sopenharmony_ci *new_resource = resource; 22862306a36Sopenharmony_ciout: 22962306a36Sopenharmony_ci return error; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_resource_allocate); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_civoid fsl_mc_resource_free(struct fsl_mc_resource *resource) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct fsl_mc_resource_pool *res_pool; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci res_pool = resource->parent_pool; 23862306a36Sopenharmony_ci if (resource->type != res_pool->type) 23962306a36Sopenharmony_ci return; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci mutex_lock(&res_pool->mutex); 24262306a36Sopenharmony_ci if (res_pool->free_count < 0 || 24362306a36Sopenharmony_ci res_pool->free_count >= res_pool->max_count) 24462306a36Sopenharmony_ci goto out_unlock; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (!list_empty(&resource->node)) 24762306a36Sopenharmony_ci goto out_unlock; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci list_add_tail(&resource->node, &res_pool->free_list); 25062306a36Sopenharmony_ci res_pool->free_count++; 25162306a36Sopenharmony_ciout_unlock: 25262306a36Sopenharmony_ci mutex_unlock(&res_pool->mutex); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_resource_free); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/** 25762306a36Sopenharmony_ci * fsl_mc_object_allocate - Allocates an fsl-mc object of the given 25862306a36Sopenharmony_ci * pool type from a given fsl-mc bus instance 25962306a36Sopenharmony_ci * 26062306a36Sopenharmony_ci * @mc_dev: fsl-mc device which is used in conjunction with the 26162306a36Sopenharmony_ci * allocated object 26262306a36Sopenharmony_ci * @pool_type: pool type 26362306a36Sopenharmony_ci * @new_mc_adev: pointer to area where the pointer to the allocated device 26462306a36Sopenharmony_ci * is to be returned 26562306a36Sopenharmony_ci * 26662306a36Sopenharmony_ci * Allocatable objects are always used in conjunction with some functional 26762306a36Sopenharmony_ci * device. This function allocates an object of the specified type from 26862306a36Sopenharmony_ci * the DPRC containing the functional device. 26962306a36Sopenharmony_ci * 27062306a36Sopenharmony_ci * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC 27162306a36Sopenharmony_ci * portals are allocated using fsl_mc_portal_allocate(), instead of 27262306a36Sopenharmony_ci * this function. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_ciint __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev, 27562306a36Sopenharmony_ci enum fsl_mc_pool_type pool_type, 27662306a36Sopenharmony_ci struct fsl_mc_device **new_mc_adev) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct fsl_mc_device *mc_bus_dev; 27962306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus; 28062306a36Sopenharmony_ci struct fsl_mc_device *mc_adev; 28162306a36Sopenharmony_ci int error = -EINVAL; 28262306a36Sopenharmony_ci struct fsl_mc_resource *resource = NULL; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci *new_mc_adev = NULL; 28562306a36Sopenharmony_ci if (mc_dev->flags & FSL_MC_IS_DPRC) 28662306a36Sopenharmony_ci goto error; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (!dev_is_fsl_mc(mc_dev->dev.parent)) 28962306a36Sopenharmony_ci goto error; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (pool_type == FSL_MC_POOL_DPMCP) 29262306a36Sopenharmony_ci goto error; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); 29562306a36Sopenharmony_ci mc_bus = to_fsl_mc_bus(mc_bus_dev); 29662306a36Sopenharmony_ci error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource); 29762306a36Sopenharmony_ci if (error < 0) 29862306a36Sopenharmony_ci goto error; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci mc_adev = resource->data; 30162306a36Sopenharmony_ci if (!mc_adev) { 30262306a36Sopenharmony_ci error = -EINVAL; 30362306a36Sopenharmony_ci goto error; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci mc_adev->consumer_link = device_link_add(&mc_dev->dev, 30762306a36Sopenharmony_ci &mc_adev->dev, 30862306a36Sopenharmony_ci DL_FLAG_AUTOREMOVE_CONSUMER); 30962306a36Sopenharmony_ci if (!mc_adev->consumer_link) { 31062306a36Sopenharmony_ci error = -EINVAL; 31162306a36Sopenharmony_ci goto error; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci *new_mc_adev = mc_adev; 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_cierror: 31762306a36Sopenharmony_ci if (resource) 31862306a36Sopenharmony_ci fsl_mc_resource_free(resource); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return error; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_object_allocate); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/** 32562306a36Sopenharmony_ci * fsl_mc_object_free - Returns an fsl-mc object to the resource 32662306a36Sopenharmony_ci * pool where it came from. 32762306a36Sopenharmony_ci * @mc_adev: Pointer to the fsl-mc device 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_civoid fsl_mc_object_free(struct fsl_mc_device *mc_adev) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct fsl_mc_resource *resource; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci resource = mc_adev->resource; 33462306a36Sopenharmony_ci if (resource->type == FSL_MC_POOL_DPMCP) 33562306a36Sopenharmony_ci return; 33662306a36Sopenharmony_ci if (resource->data != mc_adev) 33762306a36Sopenharmony_ci return; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci fsl_mc_resource_free(resource); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci mc_adev->consumer_link = NULL; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_object_free); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/* 34662306a36Sopenharmony_ci * A DPRC and the devices in the DPRC all share the same GIC-ITS device 34762306a36Sopenharmony_ci * ID. A block of IRQs is pre-allocated and maintained in a pool 34862306a36Sopenharmony_ci * from which devices can allocate them when needed. 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/* 35262306a36Sopenharmony_ci * Initialize the interrupt pool associated with an fsl-mc bus. 35362306a36Sopenharmony_ci * It allocates a block of IRQs from the GIC-ITS. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ciint fsl_mc_populate_irq_pool(struct fsl_mc_device *mc_bus_dev, 35662306a36Sopenharmony_ci unsigned int irq_count) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci unsigned int i; 35962306a36Sopenharmony_ci struct fsl_mc_device_irq *irq_resources; 36062306a36Sopenharmony_ci struct fsl_mc_device_irq *mc_dev_irq; 36162306a36Sopenharmony_ci int error; 36262306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); 36362306a36Sopenharmony_ci struct fsl_mc_resource_pool *res_pool = 36462306a36Sopenharmony_ci &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* do nothing if the IRQ pool is already populated */ 36762306a36Sopenharmony_ci if (mc_bus->irq_resources) 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (irq_count == 0 || 37162306a36Sopenharmony_ci irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) 37262306a36Sopenharmony_ci return -EINVAL; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci error = fsl_mc_msi_domain_alloc_irqs(&mc_bus_dev->dev, irq_count); 37562306a36Sopenharmony_ci if (error < 0) 37662306a36Sopenharmony_ci return error; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci irq_resources = devm_kcalloc(&mc_bus_dev->dev, 37962306a36Sopenharmony_ci irq_count, sizeof(*irq_resources), 38062306a36Sopenharmony_ci GFP_KERNEL); 38162306a36Sopenharmony_ci if (!irq_resources) { 38262306a36Sopenharmony_ci error = -ENOMEM; 38362306a36Sopenharmony_ci goto cleanup_msi_irqs; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci for (i = 0; i < irq_count; i++) { 38762306a36Sopenharmony_ci mc_dev_irq = &irq_resources[i]; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* 39062306a36Sopenharmony_ci * NOTE: This mc_dev_irq's MSI addr/value pair will be set 39162306a36Sopenharmony_ci * by the fsl_mc_msi_write_msg() callback 39262306a36Sopenharmony_ci */ 39362306a36Sopenharmony_ci mc_dev_irq->resource.type = res_pool->type; 39462306a36Sopenharmony_ci mc_dev_irq->resource.data = mc_dev_irq; 39562306a36Sopenharmony_ci mc_dev_irq->resource.parent_pool = res_pool; 39662306a36Sopenharmony_ci mc_dev_irq->virq = msi_get_virq(&mc_bus_dev->dev, i); 39762306a36Sopenharmony_ci mc_dev_irq->resource.id = mc_dev_irq->virq; 39862306a36Sopenharmony_ci INIT_LIST_HEAD(&mc_dev_irq->resource.node); 39962306a36Sopenharmony_ci list_add_tail(&mc_dev_irq->resource.node, &res_pool->free_list); 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci res_pool->max_count = irq_count; 40362306a36Sopenharmony_ci res_pool->free_count = irq_count; 40462306a36Sopenharmony_ci mc_bus->irq_resources = irq_resources; 40562306a36Sopenharmony_ci return 0; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cicleanup_msi_irqs: 40862306a36Sopenharmony_ci fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev); 40962306a36Sopenharmony_ci return error; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/* 41462306a36Sopenharmony_ci * Teardown the interrupt pool associated with an fsl-mc bus. 41562306a36Sopenharmony_ci * It frees the IRQs that were allocated to the pool, back to the GIC-ITS. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_civoid fsl_mc_cleanup_irq_pool(struct fsl_mc_device *mc_bus_dev) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); 42062306a36Sopenharmony_ci struct fsl_mc_resource_pool *res_pool = 42162306a36Sopenharmony_ci &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (!mc_bus->irq_resources) 42462306a36Sopenharmony_ci return; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (res_pool->max_count == 0) 42762306a36Sopenharmony_ci return; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (res_pool->free_count != res_pool->max_count) 43062306a36Sopenharmony_ci return; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci INIT_LIST_HEAD(&res_pool->free_list); 43362306a36Sopenharmony_ci res_pool->max_count = 0; 43462306a36Sopenharmony_ci res_pool->free_count = 0; 43562306a36Sopenharmony_ci mc_bus->irq_resources = NULL; 43662306a36Sopenharmony_ci fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/* 44162306a36Sopenharmony_ci * Allocate the IRQs required by a given fsl-mc device. 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_ciint __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci int i; 44662306a36Sopenharmony_ci int irq_count; 44762306a36Sopenharmony_ci int res_allocated_count = 0; 44862306a36Sopenharmony_ci int error = -EINVAL; 44962306a36Sopenharmony_ci struct fsl_mc_device_irq **irqs = NULL; 45062306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus; 45162306a36Sopenharmony_ci struct fsl_mc_resource_pool *res_pool; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (mc_dev->irqs) 45462306a36Sopenharmony_ci return -EINVAL; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci irq_count = mc_dev->obj_desc.irq_count; 45762306a36Sopenharmony_ci if (irq_count == 0) 45862306a36Sopenharmony_ci return -EINVAL; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (is_fsl_mc_bus_dprc(mc_dev)) 46162306a36Sopenharmony_ci mc_bus = to_fsl_mc_bus(mc_dev); 46262306a36Sopenharmony_ci else 46362306a36Sopenharmony_ci mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (!mc_bus->irq_resources) 46662306a36Sopenharmony_ci return -EINVAL; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; 46962306a36Sopenharmony_ci if (res_pool->free_count < irq_count) { 47062306a36Sopenharmony_ci dev_err(&mc_dev->dev, 47162306a36Sopenharmony_ci "Not able to allocate %u irqs for device\n", irq_count); 47262306a36Sopenharmony_ci return -ENOSPC; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci irqs = devm_kcalloc(&mc_dev->dev, irq_count, sizeof(irqs[0]), 47662306a36Sopenharmony_ci GFP_KERNEL); 47762306a36Sopenharmony_ci if (!irqs) 47862306a36Sopenharmony_ci return -ENOMEM; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci for (i = 0; i < irq_count; i++) { 48162306a36Sopenharmony_ci struct fsl_mc_resource *resource; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ, 48462306a36Sopenharmony_ci &resource); 48562306a36Sopenharmony_ci if (error < 0) 48662306a36Sopenharmony_ci goto error_resource_alloc; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci irqs[i] = to_fsl_mc_irq(resource); 48962306a36Sopenharmony_ci res_allocated_count++; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci irqs[i]->mc_dev = mc_dev; 49262306a36Sopenharmony_ci irqs[i]->dev_irq_index = i; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci mc_dev->irqs = irqs; 49662306a36Sopenharmony_ci return 0; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cierror_resource_alloc: 49962306a36Sopenharmony_ci for (i = 0; i < res_allocated_count; i++) { 50062306a36Sopenharmony_ci irqs[i]->mc_dev = NULL; 50162306a36Sopenharmony_ci fsl_mc_resource_free(&irqs[i]->resource); 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci return error; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci/* 50962306a36Sopenharmony_ci * Frees the IRQs that were allocated for an fsl-mc device. 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_civoid fsl_mc_free_irqs(struct fsl_mc_device *mc_dev) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci int i; 51462306a36Sopenharmony_ci int irq_count; 51562306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus; 51662306a36Sopenharmony_ci struct fsl_mc_device_irq **irqs = mc_dev->irqs; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (!irqs) 51962306a36Sopenharmony_ci return; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci irq_count = mc_dev->obj_desc.irq_count; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (is_fsl_mc_bus_dprc(mc_dev)) 52462306a36Sopenharmony_ci mc_bus = to_fsl_mc_bus(mc_dev); 52562306a36Sopenharmony_ci else 52662306a36Sopenharmony_ci mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (!mc_bus->irq_resources) 52962306a36Sopenharmony_ci return; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci for (i = 0; i < irq_count; i++) { 53262306a36Sopenharmony_ci irqs[i]->mc_dev = NULL; 53362306a36Sopenharmony_ci fsl_mc_resource_free(&irqs[i]->resource); 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci mc_dev->irqs = NULL; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_free_irqs); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_civoid fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci int pool_type; 54362306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) { 54662306a36Sopenharmony_ci struct fsl_mc_resource_pool *res_pool = 54762306a36Sopenharmony_ci &mc_bus->resource_pools[pool_type]; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci res_pool->type = pool_type; 55062306a36Sopenharmony_ci res_pool->max_count = 0; 55162306a36Sopenharmony_ci res_pool->free_count = 0; 55262306a36Sopenharmony_ci res_pool->mc_bus = mc_bus; 55362306a36Sopenharmony_ci INIT_LIST_HEAD(&res_pool->free_list); 55462306a36Sopenharmony_ci mutex_init(&res_pool->mutex); 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic void fsl_mc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev, 55962306a36Sopenharmony_ci enum fsl_mc_pool_type pool_type) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct fsl_mc_resource *resource; 56262306a36Sopenharmony_ci struct fsl_mc_resource *next; 56362306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); 56462306a36Sopenharmony_ci struct fsl_mc_resource_pool *res_pool = 56562306a36Sopenharmony_ci &mc_bus->resource_pools[pool_type]; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci list_for_each_entry_safe(resource, next, &res_pool->free_list, node) 56862306a36Sopenharmony_ci devm_kfree(&mc_bus_dev->dev, resource); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_civoid fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci int pool_type; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) 57662306a36Sopenharmony_ci fsl_mc_cleanup_resource_pool(mc_bus_dev, pool_type); 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci/* 58062306a36Sopenharmony_ci * fsl_mc_allocator_probe - callback invoked when an allocatable device is 58162306a36Sopenharmony_ci * being added to the system 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_cistatic int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci enum fsl_mc_pool_type pool_type; 58662306a36Sopenharmony_ci struct fsl_mc_device *mc_bus_dev; 58762306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus; 58862306a36Sopenharmony_ci int error; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (!fsl_mc_is_allocatable(mc_dev)) 59162306a36Sopenharmony_ci return -EINVAL; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); 59462306a36Sopenharmony_ci if (!dev_is_fsl_mc(&mc_bus_dev->dev)) 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci mc_bus = to_fsl_mc_bus(mc_bus_dev); 59862306a36Sopenharmony_ci error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type); 59962306a36Sopenharmony_ci if (error < 0) 60062306a36Sopenharmony_ci return error; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev); 60362306a36Sopenharmony_ci if (error < 0) 60462306a36Sopenharmony_ci return error; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci dev_dbg(&mc_dev->dev, 60762306a36Sopenharmony_ci "Allocatable fsl-mc device bound to fsl_mc_allocator driver"); 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci/* 61262306a36Sopenharmony_ci * fsl_mc_allocator_remove - callback invoked when an allocatable device is 61362306a36Sopenharmony_ci * being removed from the system 61462306a36Sopenharmony_ci */ 61562306a36Sopenharmony_cistatic void fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci int error; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (mc_dev->resource) { 62062306a36Sopenharmony_ci error = fsl_mc_resource_pool_remove_device(mc_dev); 62162306a36Sopenharmony_ci if (error < 0) 62262306a36Sopenharmony_ci return; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci dev_dbg(&mc_dev->dev, 62662306a36Sopenharmony_ci "Allocatable fsl-mc device unbound from fsl_mc_allocator driver"); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic const struct fsl_mc_device_id match_id_table[] = { 63062306a36Sopenharmony_ci { 63162306a36Sopenharmony_ci .vendor = FSL_MC_VENDOR_FREESCALE, 63262306a36Sopenharmony_ci .obj_type = "dpbp", 63362306a36Sopenharmony_ci }, 63462306a36Sopenharmony_ci { 63562306a36Sopenharmony_ci .vendor = FSL_MC_VENDOR_FREESCALE, 63662306a36Sopenharmony_ci .obj_type = "dpmcp", 63762306a36Sopenharmony_ci }, 63862306a36Sopenharmony_ci { 63962306a36Sopenharmony_ci .vendor = FSL_MC_VENDOR_FREESCALE, 64062306a36Sopenharmony_ci .obj_type = "dpcon", 64162306a36Sopenharmony_ci }, 64262306a36Sopenharmony_ci {.vendor = 0x0}, 64362306a36Sopenharmony_ci}; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic struct fsl_mc_driver fsl_mc_allocator_driver = { 64662306a36Sopenharmony_ci .driver = { 64762306a36Sopenharmony_ci .name = "fsl_mc_allocator", 64862306a36Sopenharmony_ci .pm = NULL, 64962306a36Sopenharmony_ci }, 65062306a36Sopenharmony_ci .match_id_table = match_id_table, 65162306a36Sopenharmony_ci .probe = fsl_mc_allocator_probe, 65262306a36Sopenharmony_ci .remove = fsl_mc_allocator_remove, 65362306a36Sopenharmony_ci}; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ciint __init fsl_mc_allocator_driver_init(void) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci return fsl_mc_driver_register(&fsl_mc_allocator_driver); 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_civoid fsl_mc_allocator_driver_exit(void) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci fsl_mc_driver_unregister(&fsl_mc_allocator_driver); 66362306a36Sopenharmony_ci} 664