Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Freescale data path resource container (DPRC) driver
0004  *
0005  * Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
0006  * Copyright 2019-2020 NXP
0007  * Author: German Rivera <German.Rivera@freescale.com>
0008  *
0009  */
0010 
0011 #include <linux/module.h>
0012 #include <linux/slab.h>
0013 #include <linux/interrupt.h>
0014 #include <linux/msi.h>
0015 #include <linux/fsl/mc.h>
0016 
0017 #include "fsl-mc-private.h"
0018 
0019 #define FSL_MC_DPRC_DRIVER_NAME    "fsl_mc_dprc"
0020 
0021 struct fsl_mc_child_objs {
0022     int child_count;
0023     struct fsl_mc_obj_desc *child_array;
0024 };
0025 
0026 static bool fsl_mc_device_match(struct fsl_mc_device *mc_dev,
0027                 struct fsl_mc_obj_desc *obj_desc)
0028 {
0029     return mc_dev->obj_desc.id == obj_desc->id &&
0030            strcmp(mc_dev->obj_desc.type, obj_desc->type) == 0;
0031 }
0032 
0033 static bool fsl_mc_obj_desc_is_allocatable(struct fsl_mc_obj_desc *obj)
0034 {
0035     if (strcmp(obj->type, "dpmcp") == 0 ||
0036         strcmp(obj->type, "dpcon") == 0 ||
0037         strcmp(obj->type, "dpbp") == 0)
0038         return true;
0039     else
0040         return false;
0041 }
0042 
0043 static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data)
0044 {
0045     int i;
0046     struct fsl_mc_child_objs *objs;
0047     struct fsl_mc_device *mc_dev;
0048 
0049     mc_dev = to_fsl_mc_device(dev);
0050     objs = data;
0051 
0052     for (i = 0; i < objs->child_count; i++) {
0053         struct fsl_mc_obj_desc *obj_desc = &objs->child_array[i];
0054 
0055         if (strlen(obj_desc->type) != 0 &&
0056             fsl_mc_device_match(mc_dev, obj_desc))
0057             break;
0058     }
0059 
0060     if (i == objs->child_count)
0061         fsl_mc_device_remove(mc_dev);
0062 
0063     return 0;
0064 }
0065 
0066 static int __fsl_mc_device_remove(struct device *dev, void *data)
0067 {
0068     fsl_mc_device_remove(to_fsl_mc_device(dev));
0069     return 0;
0070 }
0071 
0072 /**
0073  * dprc_remove_devices - Removes devices for objects removed from a DPRC
0074  *
0075  * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
0076  * @obj_desc_array: array of object descriptors for child objects currently
0077  * present in the DPRC in the MC.
0078  * @num_child_objects_in_mc: number of entries in obj_desc_array
0079  *
0080  * Synchronizes the state of the Linux bus driver with the actual state of
0081  * the MC by removing devices that represent MC objects that have
0082  * been dynamically removed in the physical DPRC.
0083  */
0084 void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev,
0085              struct fsl_mc_obj_desc *obj_desc_array,
0086              int num_child_objects_in_mc)
0087 {
0088     if (num_child_objects_in_mc != 0) {
0089         /*
0090          * Remove child objects that are in the DPRC in Linux,
0091          * but not in the MC:
0092          */
0093         struct fsl_mc_child_objs objs;
0094 
0095         objs.child_count = num_child_objects_in_mc;
0096         objs.child_array = obj_desc_array;
0097         device_for_each_child(&mc_bus_dev->dev, &objs,
0098                       __fsl_mc_device_remove_if_not_in_mc);
0099     } else {
0100         /*
0101          * There are no child objects for this DPRC in the MC.
0102          * So, remove all the child devices from Linux:
0103          */
0104         device_for_each_child(&mc_bus_dev->dev, NULL,
0105                       __fsl_mc_device_remove);
0106     }
0107 }
0108 EXPORT_SYMBOL_GPL(dprc_remove_devices);
0109 
0110 static int __fsl_mc_device_match(struct device *dev, void *data)
0111 {
0112     struct fsl_mc_obj_desc *obj_desc = data;
0113     struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
0114 
0115     return fsl_mc_device_match(mc_dev, obj_desc);
0116 }
0117 
0118 struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc *obj_desc,
0119                        struct fsl_mc_device *mc_bus_dev)
0120 {
0121     struct device *dev;
0122 
0123     dev = device_find_child(&mc_bus_dev->dev, obj_desc,
0124                 __fsl_mc_device_match);
0125 
0126     return dev ? to_fsl_mc_device(dev) : NULL;
0127 }
0128 
0129 /**
0130  * check_plugged_state_change - Check change in an MC object's plugged state
0131  *
0132  * @mc_dev: pointer to the fsl-mc device for a given MC object
0133  * @obj_desc: pointer to the MC object's descriptor in the MC
0134  *
0135  * If the plugged state has changed from unplugged to plugged, the fsl-mc
0136  * device is bound to the corresponding device driver.
0137  * If the plugged state has changed from plugged to unplugged, the fsl-mc
0138  * device is unbound from the corresponding device driver.
0139  */
0140 static void check_plugged_state_change(struct fsl_mc_device *mc_dev,
0141                        struct fsl_mc_obj_desc *obj_desc)
0142 {
0143     int error;
0144     u32 plugged_flag_at_mc =
0145             obj_desc->state & FSL_MC_OBJ_STATE_PLUGGED;
0146 
0147     if (plugged_flag_at_mc !=
0148         (mc_dev->obj_desc.state & FSL_MC_OBJ_STATE_PLUGGED)) {
0149         if (plugged_flag_at_mc) {
0150             mc_dev->obj_desc.state |= FSL_MC_OBJ_STATE_PLUGGED;
0151             error = device_attach(&mc_dev->dev);
0152             if (error < 0) {
0153                 dev_err(&mc_dev->dev,
0154                     "device_attach() failed: %d\n",
0155                     error);
0156             }
0157         } else {
0158             mc_dev->obj_desc.state &= ~FSL_MC_OBJ_STATE_PLUGGED;
0159             device_release_driver(&mc_dev->dev);
0160         }
0161     }
0162 }
0163 
0164 static void fsl_mc_obj_device_add(struct fsl_mc_device *mc_bus_dev,
0165                   struct fsl_mc_obj_desc *obj_desc)
0166 {
0167     int error;
0168     struct fsl_mc_device *child_dev;
0169 
0170     /*
0171      * Check if device is already known to Linux:
0172      */
0173     child_dev = fsl_mc_device_lookup(obj_desc, mc_bus_dev);
0174     if (child_dev) {
0175         check_plugged_state_change(child_dev, obj_desc);
0176         put_device(&child_dev->dev);
0177     } else {
0178         error = fsl_mc_device_add(obj_desc, NULL, &mc_bus_dev->dev,
0179                       &child_dev);
0180         if (error < 0)
0181             return;
0182     }
0183 }
0184 
0185 /**
0186  * dprc_add_new_devices - Adds devices to the logical bus for a DPRC
0187  *
0188  * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
0189  * @obj_desc_array: array of device descriptors for child devices currently
0190  * present in the physical DPRC.
0191  * @num_child_objects_in_mc: number of entries in obj_desc_array
0192  *
0193  * Synchronizes the state of the Linux bus driver with the actual
0194  * state of the MC by adding objects that have been newly discovered
0195  * in the physical DPRC.
0196  */
0197 static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev,
0198                  struct fsl_mc_obj_desc *obj_desc_array,
0199                  int num_child_objects_in_mc)
0200 {
0201     int i;
0202 
0203     /* probe the allocable objects first */
0204     for (i = 0; i < num_child_objects_in_mc; i++) {
0205         struct fsl_mc_obj_desc *obj_desc = &obj_desc_array[i];
0206 
0207         if (strlen(obj_desc->type) > 0 &&
0208             fsl_mc_obj_desc_is_allocatable(obj_desc))
0209             fsl_mc_obj_device_add(mc_bus_dev, obj_desc);
0210     }
0211 
0212     for (i = 0; i < num_child_objects_in_mc; i++) {
0213         struct fsl_mc_obj_desc *obj_desc = &obj_desc_array[i];
0214 
0215         if (strlen(obj_desc->type) > 0 &&
0216             !fsl_mc_obj_desc_is_allocatable(obj_desc))
0217             fsl_mc_obj_device_add(mc_bus_dev, obj_desc);
0218     }
0219 }
0220 
0221 /**
0222  * dprc_scan_objects - Discover objects in a DPRC
0223  *
0224  * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
0225  * @alloc_interrupts: if true the function allocates the interrupt pool,
0226  * otherwise the interrupt allocation is delayed
0227  *
0228  * Detects objects added and removed from a DPRC and synchronizes the
0229  * state of the Linux bus driver, MC by adding and removing
0230  * devices accordingly.
0231  * Two types of devices can be found in a DPRC: allocatable objects (e.g.,
0232  * dpbp, dpmcp) and non-allocatable devices (e.g., dprc, dpni).
0233  * All allocatable devices needed to be probed before all non-allocatable
0234  * devices, to ensure that device drivers for non-allocatable
0235  * devices can allocate any type of allocatable devices.
0236  * That is, we need to ensure that the corresponding resource pools are
0237  * populated before they can get allocation requests from probe callbacks
0238  * of the device drivers for the non-allocatable devices.
0239  */
0240 int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,
0241               bool alloc_interrupts)
0242 {
0243     int num_child_objects;
0244     int dprc_get_obj_failures;
0245     int error;
0246     unsigned int irq_count = mc_bus_dev->obj_desc.irq_count;
0247     struct fsl_mc_obj_desc *child_obj_desc_array = NULL;
0248     struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
0249 
0250     error = dprc_get_obj_count(mc_bus_dev->mc_io,
0251                    0,
0252                    mc_bus_dev->mc_handle,
0253                    &num_child_objects);
0254     if (error < 0) {
0255         dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n",
0256             error);
0257         return error;
0258     }
0259 
0260     if (num_child_objects != 0) {
0261         int i;
0262 
0263         child_obj_desc_array =
0264             devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects,
0265                        sizeof(*child_obj_desc_array),
0266                        GFP_KERNEL);
0267         if (!child_obj_desc_array)
0268             return -ENOMEM;
0269 
0270         /*
0271          * Discover objects currently present in the physical DPRC:
0272          */
0273         dprc_get_obj_failures = 0;
0274         for (i = 0; i < num_child_objects; i++) {
0275             struct fsl_mc_obj_desc *obj_desc =
0276                 &child_obj_desc_array[i];
0277 
0278             error = dprc_get_obj(mc_bus_dev->mc_io,
0279                          0,
0280                          mc_bus_dev->mc_handle,
0281                          i, obj_desc);
0282             if (error < 0) {
0283                 dev_err(&mc_bus_dev->dev,
0284                     "dprc_get_obj(i=%d) failed: %d\n",
0285                     i, error);
0286                 /*
0287                  * Mark the obj entry as "invalid", by using the
0288                  * empty string as obj type:
0289                  */
0290                 obj_desc->type[0] = '\0';
0291                 obj_desc->id = error;
0292                 dprc_get_obj_failures++;
0293                 continue;
0294             }
0295 
0296             /*
0297              * add a quirk for all versions of dpsec < 4.0...none
0298              * are coherent regardless of what the MC reports.
0299              */
0300             if ((strcmp(obj_desc->type, "dpseci") == 0) &&
0301                 (obj_desc->ver_major < 4))
0302                 obj_desc->flags |=
0303                     FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY;
0304 
0305             irq_count += obj_desc->irq_count;
0306             dev_dbg(&mc_bus_dev->dev,
0307                 "Discovered object: type %s, id %d\n",
0308                 obj_desc->type, obj_desc->id);
0309         }
0310 
0311         if (dprc_get_obj_failures != 0) {
0312             dev_err(&mc_bus_dev->dev,
0313                 "%d out of %d devices could not be retrieved\n",
0314                 dprc_get_obj_failures, num_child_objects);
0315         }
0316     }
0317 
0318     /*
0319      * Allocate IRQ's before binding the scanned devices with their
0320      * respective drivers.
0321      */
0322     if (dev_get_msi_domain(&mc_bus_dev->dev)) {
0323         if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) {
0324             dev_warn(&mc_bus_dev->dev,
0325                  "IRQs needed (%u) exceed IRQs preallocated (%u)\n",
0326                  irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
0327         }
0328 
0329         if (alloc_interrupts && !mc_bus->irq_resources) {
0330             error = fsl_mc_populate_irq_pool(mc_bus_dev,
0331                      FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
0332             if (error < 0)
0333                 return error;
0334         }
0335     }
0336 
0337     dprc_remove_devices(mc_bus_dev, child_obj_desc_array,
0338                 num_child_objects);
0339 
0340     dprc_add_new_devices(mc_bus_dev, child_obj_desc_array,
0341                  num_child_objects);
0342 
0343     if (child_obj_desc_array)
0344         devm_kfree(&mc_bus_dev->dev, child_obj_desc_array);
0345 
0346     return 0;
0347 }
0348 
0349 /**
0350  * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state
0351  *
0352  * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
0353  * @alloc_interrupts: if true the function allocates the interrupt pool,
0354  *                    otherwise the interrupt allocation is delayed
0355  * Scans the physical DPRC and synchronizes the state of the Linux
0356  * bus driver with the actual state of the MC by adding and removing
0357  * devices as appropriate.
0358  */
0359 int dprc_scan_container(struct fsl_mc_device *mc_bus_dev,
0360             bool alloc_interrupts)
0361 {
0362     int error = 0;
0363     struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
0364 
0365     fsl_mc_init_all_resource_pools(mc_bus_dev);
0366 
0367     /*
0368      * Discover objects in the DPRC:
0369      */
0370     mutex_lock(&mc_bus->scan_mutex);
0371     error = dprc_scan_objects(mc_bus_dev, alloc_interrupts);
0372     mutex_unlock(&mc_bus->scan_mutex);
0373 
0374     return error;
0375 }
0376 EXPORT_SYMBOL_GPL(dprc_scan_container);
0377 
0378 /**
0379  * dprc_irq0_handler - Regular ISR for DPRC interrupt 0
0380  *
0381  * @irq_num: IRQ number of the interrupt being handled
0382  * @arg: Pointer to device structure
0383  */
0384 static irqreturn_t dprc_irq0_handler(int irq_num, void *arg)
0385 {
0386     return IRQ_WAKE_THREAD;
0387 }
0388 
0389 /**
0390  * dprc_irq0_handler_thread - Handler thread function for DPRC interrupt 0
0391  *
0392  * @irq_num: IRQ number of the interrupt being handled
0393  * @arg: Pointer to device structure
0394  */
0395 static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg)
0396 {
0397     int error;
0398     u32 status;
0399     struct device *dev = arg;
0400     struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
0401     struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
0402     struct fsl_mc_io *mc_io = mc_dev->mc_io;
0403     int irq = mc_dev->irqs[0]->virq;
0404 
0405     dev_dbg(dev, "DPRC IRQ %d triggered on CPU %u\n",
0406         irq_num, smp_processor_id());
0407 
0408     if (!(mc_dev->flags & FSL_MC_IS_DPRC))
0409         return IRQ_HANDLED;
0410 
0411     mutex_lock(&mc_bus->scan_mutex);
0412     if (irq != (u32)irq_num)
0413         goto out;
0414 
0415     status = 0;
0416     error = dprc_get_irq_status(mc_io, 0, mc_dev->mc_handle, 0,
0417                     &status);
0418     if (error < 0) {
0419         dev_err(dev,
0420             "dprc_get_irq_status() failed: %d\n", error);
0421         goto out;
0422     }
0423 
0424     error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0,
0425                       status);
0426     if (error < 0) {
0427         dev_err(dev,
0428             "dprc_clear_irq_status() failed: %d\n", error);
0429         goto out;
0430     }
0431 
0432     if (status & (DPRC_IRQ_EVENT_OBJ_ADDED |
0433               DPRC_IRQ_EVENT_OBJ_REMOVED |
0434               DPRC_IRQ_EVENT_CONTAINER_DESTROYED |
0435               DPRC_IRQ_EVENT_OBJ_DESTROYED |
0436               DPRC_IRQ_EVENT_OBJ_CREATED)) {
0437 
0438         error = dprc_scan_objects(mc_dev, true);
0439         if (error < 0) {
0440             /*
0441              * If the error is -ENXIO, we ignore it, as it indicates
0442              * that the object scan was aborted, as we detected that
0443              * an object was removed from the DPRC in the MC, while
0444              * we were scanning the DPRC.
0445              */
0446             if (error != -ENXIO) {
0447                 dev_err(dev, "dprc_scan_objects() failed: %d\n",
0448                     error);
0449             }
0450 
0451             goto out;
0452         }
0453     }
0454 
0455 out:
0456     mutex_unlock(&mc_bus->scan_mutex);
0457     return IRQ_HANDLED;
0458 }
0459 
0460 /*
0461  * Disable and clear interrupt for a given DPRC object
0462  */
0463 int disable_dprc_irq(struct fsl_mc_device *mc_dev)
0464 {
0465     struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
0466     int error;
0467     struct fsl_mc_io *mc_io = mc_dev->mc_io;
0468 
0469     /*
0470      * Disable generation of interrupt, while we configure it:
0471      */
0472     error = dprc_set_irq_enable(mc_io, 0, mc_dev->mc_handle, 0, 0);
0473     if (error < 0) {
0474         dev_err(&mc_dev->dev,
0475             "Disabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n",
0476             error);
0477         return error;
0478     }
0479 
0480     /*
0481      * Disable all interrupt causes for the interrupt:
0482      */
0483     error = dprc_set_irq_mask(mc_io, 0, mc_dev->mc_handle, 0, 0x0);
0484     if (error < 0) {
0485         dev_err(&mc_dev->dev,
0486             "Disabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n",
0487             error);
0488         return error;
0489     }
0490 
0491     /*
0492      * Clear any leftover interrupts:
0493      */
0494     error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0, ~0x0U);
0495     if (error < 0) {
0496         dev_err(&mc_dev->dev,
0497             "Disabling DPRC IRQ failed: dprc_clear_irq_status() failed: %d\n",
0498             error);
0499         return error;
0500     }
0501 
0502     mc_bus->irq_enabled = 0;
0503 
0504     return 0;
0505 }
0506 
0507 int get_dprc_irq_state(struct fsl_mc_device *mc_dev)
0508 {
0509     struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
0510 
0511     return mc_bus->irq_enabled;
0512 }
0513 
0514 static int register_dprc_irq_handler(struct fsl_mc_device *mc_dev)
0515 {
0516     int error;
0517     struct fsl_mc_device_irq *irq = mc_dev->irqs[0];
0518 
0519     /*
0520      * NOTE: devm_request_threaded_irq() invokes the device-specific
0521      * function that programs the MSI physically in the device
0522      */
0523     error = devm_request_threaded_irq(&mc_dev->dev,
0524                       irq->virq,
0525                       dprc_irq0_handler,
0526                       dprc_irq0_handler_thread,
0527                       IRQF_NO_SUSPEND | IRQF_ONESHOT,
0528                       dev_name(&mc_dev->dev),
0529                       &mc_dev->dev);
0530     if (error < 0) {
0531         dev_err(&mc_dev->dev,
0532             "devm_request_threaded_irq() failed: %d\n",
0533             error);
0534         return error;
0535     }
0536 
0537     return 0;
0538 }
0539 
0540 int enable_dprc_irq(struct fsl_mc_device *mc_dev)
0541 {
0542     struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
0543     int error;
0544 
0545     /*
0546      * Enable all interrupt causes for the interrupt:
0547      */
0548     error = dprc_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, 0,
0549                   ~0x0u);
0550     if (error < 0) {
0551         dev_err(&mc_dev->dev,
0552             "Enabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n",
0553             error);
0554 
0555         return error;
0556     }
0557 
0558     /*
0559      * Enable generation of the interrupt:
0560      */
0561     error = dprc_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, 0, 1);
0562     if (error < 0) {
0563         dev_err(&mc_dev->dev,
0564             "Enabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n",
0565             error);
0566 
0567         return error;
0568     }
0569 
0570     mc_bus->irq_enabled = 1;
0571 
0572     return 0;
0573 }
0574 
0575 /*
0576  * Setup interrupt for a given DPRC device
0577  */
0578 static int dprc_setup_irq(struct fsl_mc_device *mc_dev)
0579 {
0580     int error;
0581 
0582     error = fsl_mc_allocate_irqs(mc_dev);
0583     if (error < 0)
0584         return error;
0585 
0586     error = disable_dprc_irq(mc_dev);
0587     if (error < 0)
0588         goto error_free_irqs;
0589 
0590     error = register_dprc_irq_handler(mc_dev);
0591     if (error < 0)
0592         goto error_free_irqs;
0593 
0594     error = enable_dprc_irq(mc_dev);
0595     if (error < 0)
0596         goto error_free_irqs;
0597 
0598     return 0;
0599 
0600 error_free_irqs:
0601     fsl_mc_free_irqs(mc_dev);
0602     return error;
0603 }
0604 
0605 /**
0606  * dprc_setup - opens and creates a mc_io for DPRC
0607  *
0608  * @mc_dev: Pointer to fsl-mc device representing a DPRC
0609  *
0610  * It opens the physical DPRC in the MC.
0611  * It configures the DPRC portal used to communicate with MC
0612  */
0613 
0614 int dprc_setup(struct fsl_mc_device *mc_dev)
0615 {
0616     struct device *parent_dev = mc_dev->dev.parent;
0617     struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
0618     struct irq_domain *mc_msi_domain;
0619     bool mc_io_created = false;
0620     bool msi_domain_set = false;
0621     bool uapi_created = false;
0622     u16 major_ver, minor_ver;
0623     size_t region_size;
0624     int error;
0625 
0626     if (!is_fsl_mc_bus_dprc(mc_dev))
0627         return -EINVAL;
0628 
0629     if (dev_get_msi_domain(&mc_dev->dev))
0630         return -EINVAL;
0631 
0632     if (!mc_dev->mc_io) {
0633         /*
0634          * This is a child DPRC:
0635          */
0636         if (!dev_is_fsl_mc(parent_dev))
0637             return -EINVAL;
0638 
0639         if (mc_dev->obj_desc.region_count == 0)
0640             return -EINVAL;
0641 
0642         region_size = resource_size(mc_dev->regions);
0643 
0644         error = fsl_create_mc_io(&mc_dev->dev,
0645                      mc_dev->regions[0].start,
0646                      region_size,
0647                      NULL,
0648                      FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
0649                      &mc_dev->mc_io);
0650         if (error < 0)
0651             return error;
0652 
0653         mc_io_created = true;
0654     } else {
0655         error = fsl_mc_uapi_create_device_file(mc_bus);
0656         if (error < 0)
0657             return -EPROBE_DEFER;
0658         uapi_created = true;
0659     }
0660 
0661     mc_msi_domain = fsl_mc_find_msi_domain(&mc_dev->dev);
0662     if (!mc_msi_domain) {
0663         dev_warn(&mc_dev->dev,
0664              "WARNING: MC bus without interrupt support\n");
0665     } else {
0666         dev_set_msi_domain(&mc_dev->dev, mc_msi_domain);
0667         msi_domain_set = true;
0668     }
0669 
0670     error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id,
0671               &mc_dev->mc_handle);
0672     if (error < 0) {
0673         dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error);
0674         goto error_cleanup_msi_domain;
0675     }
0676 
0677     error = dprc_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle,
0678                     &mc_bus->dprc_attr);
0679     if (error < 0) {
0680         dev_err(&mc_dev->dev, "dprc_get_attributes() failed: %d\n",
0681             error);
0682         goto error_cleanup_open;
0683     }
0684 
0685     error = dprc_get_api_version(mc_dev->mc_io, 0,
0686                      &major_ver,
0687                      &minor_ver);
0688     if (error < 0) {
0689         dev_err(&mc_dev->dev, "dprc_get_api_version() failed: %d\n",
0690             error);
0691         goto error_cleanup_open;
0692     }
0693 
0694     if (major_ver < DPRC_MIN_VER_MAJOR) {
0695         dev_err(&mc_dev->dev,
0696             "ERROR: DPRC version %d.%d not supported\n",
0697             major_ver, minor_ver);
0698         error = -ENOTSUPP;
0699         goto error_cleanup_open;
0700     }
0701 
0702     return 0;
0703 
0704 error_cleanup_open:
0705     (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
0706 
0707 error_cleanup_msi_domain:
0708     if (msi_domain_set)
0709         dev_set_msi_domain(&mc_dev->dev, NULL);
0710 
0711     if (mc_io_created) {
0712         fsl_destroy_mc_io(mc_dev->mc_io);
0713         mc_dev->mc_io = NULL;
0714     }
0715 
0716     if (uapi_created)
0717         fsl_mc_uapi_remove_device_file(mc_bus);
0718 
0719     return error;
0720 }
0721 EXPORT_SYMBOL_GPL(dprc_setup);
0722 
0723 /**
0724  * dprc_probe - callback invoked when a DPRC is being bound to this driver
0725  *
0726  * @mc_dev: Pointer to fsl-mc device representing a DPRC
0727  *
0728  * It opens the physical DPRC in the MC.
0729  * It scans the DPRC to discover the MC objects contained in it.
0730  * It creates the interrupt pool for the MC bus associated with the DPRC.
0731  * It configures the interrupts for the DPRC device itself.
0732  */
0733 static int dprc_probe(struct fsl_mc_device *mc_dev)
0734 {
0735     int error;
0736 
0737     error = dprc_setup(mc_dev);
0738     if (error < 0)
0739         return error;
0740 
0741     /*
0742      * Discover MC objects in DPRC object:
0743      */
0744     error = dprc_scan_container(mc_dev, true);
0745     if (error < 0)
0746         goto dprc_cleanup;
0747 
0748     /*
0749      * Configure interrupt for the DPRC object associated with this MC bus:
0750      */
0751     error = dprc_setup_irq(mc_dev);
0752     if (error < 0)
0753         goto scan_cleanup;
0754 
0755     dev_info(&mc_dev->dev, "DPRC device bound to driver");
0756     return 0;
0757 
0758 scan_cleanup:
0759     device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
0760 dprc_cleanup:
0761     dprc_cleanup(mc_dev);
0762     return error;
0763 }
0764 
0765 /*
0766  * Tear down interrupt for a given DPRC object
0767  */
0768 static void dprc_teardown_irq(struct fsl_mc_device *mc_dev)
0769 {
0770     struct fsl_mc_device_irq *irq = mc_dev->irqs[0];
0771 
0772     (void)disable_dprc_irq(mc_dev);
0773 
0774     devm_free_irq(&mc_dev->dev, irq->virq, &mc_dev->dev);
0775 
0776     fsl_mc_free_irqs(mc_dev);
0777 }
0778 
0779 /**
0780  * dprc_cleanup - function that cleanups a DPRC
0781  *
0782  * @mc_dev: Pointer to fsl-mc device representing the DPRC
0783  *
0784  * It closes the DPRC device in the MC.
0785  * It destroys the interrupt pool associated with this MC bus.
0786  */
0787 
0788 int dprc_cleanup(struct fsl_mc_device *mc_dev)
0789 {
0790     struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
0791     int error;
0792 
0793     /* this function should be called only for DPRCs, it
0794      * is an error to call it for regular objects
0795      */
0796     if (!is_fsl_mc_bus_dprc(mc_dev))
0797         return -EINVAL;
0798 
0799     if (dev_get_msi_domain(&mc_dev->dev)) {
0800         fsl_mc_cleanup_irq_pool(mc_dev);
0801         dev_set_msi_domain(&mc_dev->dev, NULL);
0802     }
0803 
0804     fsl_mc_cleanup_all_resource_pools(mc_dev);
0805 
0806     /* if this step fails we cannot go further with cleanup as there is no way of
0807      * communicating with the firmware
0808      */
0809     if (!mc_dev->mc_io) {
0810         dev_err(&mc_dev->dev, "mc_io is NULL, tear down cannot be performed in firmware\n");
0811         return -EINVAL;
0812     }
0813 
0814     error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
0815     if (error < 0)
0816         dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error);
0817 
0818     if (!fsl_mc_is_root_dprc(&mc_dev->dev)) {
0819         fsl_destroy_mc_io(mc_dev->mc_io);
0820         mc_dev->mc_io = NULL;
0821     } else {
0822         fsl_mc_uapi_remove_device_file(mc_bus);
0823     }
0824 
0825     return 0;
0826 }
0827 EXPORT_SYMBOL_GPL(dprc_cleanup);
0828 
0829 /**
0830  * dprc_remove - callback invoked when a DPRC is being unbound from this driver
0831  *
0832  * @mc_dev: Pointer to fsl-mc device representing the DPRC
0833  *
0834  * It removes the DPRC's child objects from Linux (not from the MC) and
0835  * closes the DPRC device in the MC.
0836  * It tears down the interrupts that were configured for the DPRC device.
0837  * It destroys the interrupt pool associated with this MC bus.
0838  */
0839 static int dprc_remove(struct fsl_mc_device *mc_dev)
0840 {
0841     struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
0842 
0843     if (!is_fsl_mc_bus_dprc(mc_dev))
0844         return -EINVAL;
0845 
0846     if (!mc_bus->irq_resources)
0847         return -EINVAL;
0848 
0849     if (dev_get_msi_domain(&mc_dev->dev))
0850         dprc_teardown_irq(mc_dev);
0851 
0852     device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
0853 
0854     dprc_cleanup(mc_dev);
0855 
0856     dev_info(&mc_dev->dev, "DPRC device unbound from driver");
0857     return 0;
0858 }
0859 
0860 static const struct fsl_mc_device_id match_id_table[] = {
0861     {
0862      .vendor = FSL_MC_VENDOR_FREESCALE,
0863      .obj_type = "dprc"},
0864     {.vendor = 0x0},
0865 };
0866 
0867 static struct fsl_mc_driver dprc_driver = {
0868     .driver = {
0869            .name = FSL_MC_DPRC_DRIVER_NAME,
0870            .owner = THIS_MODULE,
0871            .pm = NULL,
0872            },
0873     .match_id_table = match_id_table,
0874     .probe = dprc_probe,
0875     .remove = dprc_remove,
0876 };
0877 
0878 int __init dprc_driver_init(void)
0879 {
0880     return fsl_mc_driver_register(&dprc_driver);
0881 }
0882 
0883 void dprc_driver_exit(void)
0884 {
0885     fsl_mc_driver_unregister(&dprc_driver);
0886 }