diff options
Diffstat (limited to 'drivers/bus/fsl-mc/dprc-driver.c')
-rw-r--r-- | drivers/bus/fsl-mc/dprc-driver.c | 190 |
1 files changed, 118 insertions, 72 deletions
diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c index 2a473c09bc33..91dc015963a8 100644 --- a/drivers/bus/fsl-mc/dprc-driver.c +++ b/drivers/bus/fsl-mc/dprc-driver.c @@ -3,6 +3,7 @@ * Freescale data path resource container (DPRC) driver * * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. + * Copyright 2019-2020 NXP * Author: German Rivera <German.Rivera@freescale.com> * */ @@ -80,9 +81,9 @@ static int __fsl_mc_device_remove(struct device *dev, void *data) * the MC by removing devices that represent MC objects that have * been dynamically removed in the physical DPRC. */ -static void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev, - struct fsl_mc_obj_desc *obj_desc_array, - int num_child_objects_in_mc) +void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev, + struct fsl_mc_obj_desc *obj_desc_array, + int num_child_objects_in_mc) { if (num_child_objects_in_mc != 0) { /* @@ -104,6 +105,7 @@ static void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev, __fsl_mc_device_remove); } } +EXPORT_SYMBOL_GPL(dprc_remove_devices); static int __fsl_mc_device_match(struct device *dev, void *data) { @@ -220,8 +222,8 @@ static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev, * dprc_scan_objects - Discover objects in a DPRC * * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object - * @total_irq_count: If argument is provided the function populates the - * total number of IRQs created by objects in the DPRC. + * @alloc_interrupts: if true the function allocates the interrupt pool, + * otherwise the interrupt allocation is delayed * * Detects objects added and removed from a DPRC and synchronizes the * state of the Linux bus driver, MC by adding and removing @@ -236,7 +238,7 @@ static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev, * of the device drivers for the non-allocatable devices. */ static int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, - unsigned int *total_irq_count) + bool alloc_interrupts) { int num_child_objects; int dprc_get_obj_failures; @@ -317,22 +319,21 @@ static int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, * Allocate IRQ's before binding the scanned devices with their * respective drivers. */ - if (dev_get_msi_domain(&mc_bus_dev->dev) && !mc_bus->irq_resources) { + if (dev_get_msi_domain(&mc_bus_dev->dev)) { if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) { dev_warn(&mc_bus_dev->dev, "IRQs needed (%u) exceed IRQs preallocated (%u)\n", irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); } - error = fsl_mc_populate_irq_pool(mc_bus, - FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); - if (error < 0) - return error; + if (alloc_interrupts && !mc_bus->irq_resources) { + error = fsl_mc_populate_irq_pool(mc_bus_dev, + FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); + if (error < 0) + return error; + } } - if (total_irq_count) - *total_irq_count = irq_count; - dprc_remove_devices(mc_bus_dev, child_obj_desc_array, num_child_objects); @@ -354,9 +355,10 @@ static int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, * bus driver with the actual state of the MC by adding and removing * devices as appropriate. */ -static int dprc_scan_container(struct fsl_mc_device *mc_bus_dev) +int dprc_scan_container(struct fsl_mc_device *mc_bus_dev, + bool alloc_interrupts) { - int error; + int error = 0; struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); fsl_mc_init_all_resource_pools(mc_bus_dev); @@ -365,16 +367,12 @@ static int dprc_scan_container(struct fsl_mc_device *mc_bus_dev) * Discover objects in the DPRC: */ mutex_lock(&mc_bus->scan_mutex); - error = dprc_scan_objects(mc_bus_dev, NULL); + error = dprc_scan_objects(mc_bus_dev, alloc_interrupts); mutex_unlock(&mc_bus->scan_mutex); - if (error < 0) { - fsl_mc_cleanup_all_resource_pools(mc_bus_dev); - return error; - } - return 0; + return error; } - +EXPORT_SYMBOL_GPL(dprc_scan_container); /** * dprc_irq0_handler - Regular ISR for DPRC interrupt 0 * @@ -434,9 +432,8 @@ static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg) DPRC_IRQ_EVENT_CONTAINER_DESTROYED | DPRC_IRQ_EVENT_OBJ_DESTROYED | DPRC_IRQ_EVENT_OBJ_CREATED)) { - unsigned int irq_count; - error = dprc_scan_objects(mc_dev, &irq_count); + error = dprc_scan_objects(mc_dev, true); if (error < 0) { /* * If the error is -ENXIO, we ignore it, as it indicates @@ -451,12 +448,6 @@ static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg) goto out; } - - if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) { - dev_warn(dev, - "IRQs needed (%u) exceed IRQs preallocated (%u)\n", - irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); - } } out: @@ -597,25 +588,24 @@ error_free_irqs: } /** - * dprc_probe - callback invoked when a DPRC is being bound to this driver + * dprc_setup - opens and creates a mc_io for DPRC * * @mc_dev: Pointer to fsl-mc device representing a DPRC * * It opens the physical DPRC in the MC. - * It scans the DPRC to discover the MC objects contained in it. - * It creates the interrupt pool for the MC bus associated with the DPRC. - * It configures the interrupts for the DPRC device itself. + * It configures the DPRC portal used to communicate with MC */ -static int dprc_probe(struct fsl_mc_device *mc_dev) + +int dprc_setup(struct fsl_mc_device *mc_dev) { - int error; - size_t region_size; struct device *parent_dev = mc_dev->dev.parent; struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); + struct irq_domain *mc_msi_domain; bool mc_io_created = false; bool msi_domain_set = false; u16 major_ver, minor_ver; - struct irq_domain *mc_msi_domain; + size_t region_size; + int error; if (!is_fsl_mc_bus_dprc(mc_dev)) return -EINVAL; @@ -690,37 +680,63 @@ static int dprc_probe(struct fsl_mc_device *mc_dev) goto error_cleanup_open; } - mutex_init(&mc_bus->scan_mutex); + return 0; + +error_cleanup_open: + (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); + +error_cleanup_msi_domain: + if (msi_domain_set) + dev_set_msi_domain(&mc_dev->dev, NULL); + + if (mc_io_created) { + fsl_destroy_mc_io(mc_dev->mc_io); + mc_dev->mc_io = NULL; + } + + return error; +} +EXPORT_SYMBOL_GPL(dprc_setup); + +/** + * dprc_probe - callback invoked when a DPRC is being bound to this driver + * + * @mc_dev: Pointer to fsl-mc device representing a DPRC + * + * It opens the physical DPRC in the MC. + * It scans the DPRC to discover the MC objects contained in it. + * It creates the interrupt pool for the MC bus associated with the DPRC. + * It configures the interrupts for the DPRC device itself. + */ +static int dprc_probe(struct fsl_mc_device *mc_dev) +{ + int error; + + error = dprc_setup(mc_dev); + if (error < 0) + return error; /* * Discover MC objects in DPRC object: */ - error = dprc_scan_container(mc_dev); + error = dprc_scan_container(mc_dev, true); if (error < 0) - goto error_cleanup_open; + goto dprc_cleanup; /* * Configure interrupt for the DPRC object associated with this MC bus: */ error = dprc_setup_irq(mc_dev); if (error < 0) - goto error_cleanup_open; + goto scan_cleanup; dev_info(&mc_dev->dev, "DPRC device bound to driver"); return 0; -error_cleanup_open: - (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); - -error_cleanup_msi_domain: - if (msi_domain_set) - dev_set_msi_domain(&mc_dev->dev, NULL); - - if (mc_io_created) { - fsl_destroy_mc_io(mc_dev->mc_io); - mc_dev->mc_io = NULL; - } - +scan_cleanup: + device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove); +dprc_cleanup: + dprc_cleanup(mc_dev); return error; } @@ -739,40 +755,39 @@ static void dprc_teardown_irq(struct fsl_mc_device *mc_dev) } /** - * dprc_remove - callback invoked when a DPRC is being unbound from this driver + * dprc_cleanup - function that cleanups a DPRC * * @mc_dev: Pointer to fsl-mc device representing the DPRC * - * It removes the DPRC's child objects from Linux (not from the MC) and - * closes the DPRC device in the MC. - * It tears down the interrupts that were configured for the DPRC device. + * It closes the DPRC device in the MC. * It destroys the interrupt pool associated with this MC bus. */ -static int dprc_remove(struct fsl_mc_device *mc_dev) + +int dprc_cleanup(struct fsl_mc_device *mc_dev) { int error; - struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); + /* this function should be called only for DPRCs, it + * is an error to call it for regular objects + */ if (!is_fsl_mc_bus_dprc(mc_dev)) return -EINVAL; - if (!mc_dev->mc_io) - return -EINVAL; - - if (!mc_bus->irq_resources) - return -EINVAL; - - if (dev_get_msi_domain(&mc_dev->dev)) - dprc_teardown_irq(mc_dev); - - device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove); if (dev_get_msi_domain(&mc_dev->dev)) { - fsl_mc_cleanup_irq_pool(mc_bus); + fsl_mc_cleanup_irq_pool(mc_dev); dev_set_msi_domain(&mc_dev->dev, NULL); } fsl_mc_cleanup_all_resource_pools(mc_dev); + /* if this step fails we cannot go further with cleanup as there is no way of + * communicating with the firmware + */ + if (!mc_dev->mc_io) { + dev_err(&mc_dev->dev, "mc_io is NULL, tear down cannot be performed in firmware\n"); + return -EINVAL; + } + error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); if (error < 0) dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error); @@ -782,6 +797,37 @@ static int dprc_remove(struct fsl_mc_device *mc_dev) mc_dev->mc_io = NULL; } + return 0; +} +EXPORT_SYMBOL_GPL(dprc_cleanup); + +/** + * dprc_remove - callback invoked when a DPRC is being unbound from this driver + * + * @mc_dev: Pointer to fsl-mc device representing the DPRC + * + * It removes the DPRC's child objects from Linux (not from the MC) and + * closes the DPRC device in the MC. + * It tears down the interrupts that were configured for the DPRC device. + * It destroys the interrupt pool associated with this MC bus. + */ +static int dprc_remove(struct fsl_mc_device *mc_dev) +{ + struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); + + if (!is_fsl_mc_bus_dprc(mc_dev)) + return -EINVAL; + + if (!mc_bus->irq_resources) + return -EINVAL; + + if (dev_get_msi_domain(&mc_dev->dev)) + dprc_teardown_irq(mc_dev); + + device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove); + + dprc_cleanup(mc_dev); + dev_info(&mc_dev->dev, "DPRC device unbound from driver"); return 0; } |