linux/drivers/bus/fsl-mc/dprc-driver.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Freescale data path resource container (DPRC) driver
   4 *
   5 * Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
   6 * Author: German Rivera <German.Rivera@freescale.com>
   7 *
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/slab.h>
  12#include <linux/interrupt.h>
  13#include <linux/msi.h>
  14#include <linux/fsl/mc.h>
  15
  16#include "fsl-mc-private.h"
  17
  18#define FSL_MC_DPRC_DRIVER_NAME    "fsl_mc_dprc"
  19
  20struct fsl_mc_child_objs {
  21        int child_count;
  22        struct fsl_mc_obj_desc *child_array;
  23};
  24
  25static bool fsl_mc_device_match(struct fsl_mc_device *mc_dev,
  26                                struct fsl_mc_obj_desc *obj_desc)
  27{
  28        return mc_dev->obj_desc.id == obj_desc->id &&
  29               strcmp(mc_dev->obj_desc.type, obj_desc->type) == 0;
  30
  31}
  32
  33static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data)
  34{
  35        int i;
  36        struct fsl_mc_child_objs *objs;
  37        struct fsl_mc_device *mc_dev;
  38
  39        mc_dev = to_fsl_mc_device(dev);
  40        objs = data;
  41
  42        for (i = 0; i < objs->child_count; i++) {
  43                struct fsl_mc_obj_desc *obj_desc = &objs->child_array[i];
  44
  45                if (strlen(obj_desc->type) != 0 &&
  46                    fsl_mc_device_match(mc_dev, obj_desc))
  47                        break;
  48        }
  49
  50        if (i == objs->child_count)
  51                fsl_mc_device_remove(mc_dev);
  52
  53        return 0;
  54}
  55
  56static int __fsl_mc_device_remove(struct device *dev, void *data)
  57{
  58        fsl_mc_device_remove(to_fsl_mc_device(dev));
  59        return 0;
  60}
  61
  62/**
  63 * dprc_remove_devices - Removes devices for objects removed from a DPRC
  64 *
  65 * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
  66 * @obj_desc_array: array of object descriptors for child objects currently
  67 * present in the DPRC in the MC.
  68 * @num_child_objects_in_mc: number of entries in obj_desc_array
  69 *
  70 * Synchronizes the state of the Linux bus driver with the actual state of
  71 * the MC by removing devices that represent MC objects that have
  72 * been dynamically removed in the physical DPRC.
  73 */
  74static void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev,
  75                                struct fsl_mc_obj_desc *obj_desc_array,
  76                                int num_child_objects_in_mc)
  77{
  78        if (num_child_objects_in_mc != 0) {
  79                /*
  80                 * Remove child objects that are in the DPRC in Linux,
  81                 * but not in the MC:
  82                 */
  83                struct fsl_mc_child_objs objs;
  84
  85                objs.child_count = num_child_objects_in_mc;
  86                objs.child_array = obj_desc_array;
  87                device_for_each_child(&mc_bus_dev->dev, &objs,
  88                                      __fsl_mc_device_remove_if_not_in_mc);
  89        } else {
  90                /*
  91                 * There are no child objects for this DPRC in the MC.
  92                 * So, remove all the child devices from Linux:
  93                 */
  94                device_for_each_child(&mc_bus_dev->dev, NULL,
  95                                      __fsl_mc_device_remove);
  96        }
  97}
  98
  99static int __fsl_mc_device_match(struct device *dev, void *data)
 100{
 101        struct fsl_mc_obj_desc *obj_desc = data;
 102        struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
 103
 104        return fsl_mc_device_match(mc_dev, obj_desc);
 105}
 106
 107static struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc
 108                                                                *obj_desc,
 109                                                  struct fsl_mc_device
 110                                                                *mc_bus_dev)
 111{
 112        struct device *dev;
 113
 114        dev = device_find_child(&mc_bus_dev->dev, obj_desc,
 115                                __fsl_mc_device_match);
 116
 117        return dev ? to_fsl_mc_device(dev) : NULL;
 118}
 119
 120/**
 121 * check_plugged_state_change - Check change in an MC object's plugged state
 122 *
 123 * @mc_dev: pointer to the fsl-mc device for a given MC object
 124 * @obj_desc: pointer to the MC object's descriptor in the MC
 125 *
 126 * If the plugged state has changed from unplugged to plugged, the fsl-mc
 127 * device is bound to the corresponding device driver.
 128 * If the plugged state has changed from plugged to unplugged, the fsl-mc
 129 * device is unbound from the corresponding device driver.
 130 */
 131static void check_plugged_state_change(struct fsl_mc_device *mc_dev,
 132                                       struct fsl_mc_obj_desc *obj_desc)
 133{
 134        int error;
 135        u32 plugged_flag_at_mc =
 136                        obj_desc->state & FSL_MC_OBJ_STATE_PLUGGED;
 137
 138        if (plugged_flag_at_mc !=
 139            (mc_dev->obj_desc.state & FSL_MC_OBJ_STATE_PLUGGED)) {
 140                if (plugged_flag_at_mc) {
 141                        mc_dev->obj_desc.state |= FSL_MC_OBJ_STATE_PLUGGED;
 142                        error = device_attach(&mc_dev->dev);
 143                        if (error < 0) {
 144                                dev_err(&mc_dev->dev,
 145                                        "device_attach() failed: %d\n",
 146                                        error);
 147                        }
 148                } else {
 149                        mc_dev->obj_desc.state &= ~FSL_MC_OBJ_STATE_PLUGGED;
 150                        device_release_driver(&mc_dev->dev);
 151                }
 152        }
 153}
 154
 155/**
 156 * dprc_add_new_devices - Adds devices to the logical bus for a DPRC
 157 *
 158 * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
 159 * @obj_desc_array: array of device descriptors for child devices currently
 160 * present in the physical DPRC.
 161 * @num_child_objects_in_mc: number of entries in obj_desc_array
 162 *
 163 * Synchronizes the state of the Linux bus driver with the actual
 164 * state of the MC by adding objects that have been newly discovered
 165 * in the physical DPRC.
 166 */
 167static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev,
 168                                 struct fsl_mc_obj_desc *obj_desc_array,
 169                                 int num_child_objects_in_mc)
 170{
 171        int error;
 172        int i;
 173
 174        for (i = 0; i < num_child_objects_in_mc; i++) {
 175                struct fsl_mc_device *child_dev;
 176                struct fsl_mc_obj_desc *obj_desc = &obj_desc_array[i];
 177
 178                if (strlen(obj_desc->type) == 0)
 179                        continue;
 180
 181                /*
 182                 * Check if device is already known to Linux:
 183                 */
 184                child_dev = fsl_mc_device_lookup(obj_desc, mc_bus_dev);
 185                if (child_dev) {
 186                        check_plugged_state_change(child_dev, obj_desc);
 187                        put_device(&child_dev->dev);
 188                        continue;
 189                }
 190
 191                error = fsl_mc_device_add(obj_desc, NULL, &mc_bus_dev->dev,
 192                                          &child_dev);
 193                if (error < 0)
 194                        continue;
 195        }
 196}
 197
 198/**
 199 * dprc_scan_objects - Discover objects in a DPRC
 200 *
 201 * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
 202 * @total_irq_count: If argument is provided the function populates the
 203 * total number of IRQs created by objects in the DPRC.
 204 *
 205 * Detects objects added and removed from a DPRC and synchronizes the
 206 * state of the Linux bus driver, MC by adding and removing
 207 * devices accordingly.
 208 * Two types of devices can be found in a DPRC: allocatable objects (e.g.,
 209 * dpbp, dpmcp) and non-allocatable devices (e.g., dprc, dpni).
 210 * All allocatable devices needed to be probed before all non-allocatable
 211 * devices, to ensure that device drivers for non-allocatable
 212 * devices can allocate any type of allocatable devices.
 213 * That is, we need to ensure that the corresponding resource pools are
 214 * populated before they can get allocation requests from probe callbacks
 215 * of the device drivers for the non-allocatable devices.
 216 */
 217static int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,
 218                             unsigned int *total_irq_count)
 219{
 220        int num_child_objects;
 221        int dprc_get_obj_failures;
 222        int error;
 223        unsigned int irq_count = mc_bus_dev->obj_desc.irq_count;
 224        struct fsl_mc_obj_desc *child_obj_desc_array = NULL;
 225        struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
 226
 227        error = dprc_get_obj_count(mc_bus_dev->mc_io,
 228                                   0,
 229                                   mc_bus_dev->mc_handle,
 230                                   &num_child_objects);
 231        if (error < 0) {
 232                dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n",
 233                        error);
 234                return error;
 235        }
 236
 237        if (num_child_objects != 0) {
 238                int i;
 239
 240                child_obj_desc_array =
 241                    devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects,
 242                                       sizeof(*child_obj_desc_array),
 243                                       GFP_KERNEL);
 244                if (!child_obj_desc_array)
 245                        return -ENOMEM;
 246
 247                /*
 248                 * Discover objects currently present in the physical DPRC:
 249                 */
 250                dprc_get_obj_failures = 0;
 251                for (i = 0; i < num_child_objects; i++) {
 252                        struct fsl_mc_obj_desc *obj_desc =
 253                            &child_obj_desc_array[i];
 254
 255                        error = dprc_get_obj(mc_bus_dev->mc_io,
 256                                             0,
 257                                             mc_bus_dev->mc_handle,
 258                                             i, obj_desc);
 259                        if (error < 0) {
 260                                dev_err(&mc_bus_dev->dev,
 261                                        "dprc_get_obj(i=%d) failed: %d\n",
 262                                        i, error);
 263                                /*
 264                                 * Mark the obj entry as "invalid", by using the
 265                                 * empty string as obj type:
 266                                 */
 267                                obj_desc->type[0] = '\0';
 268                                obj_desc->id = error;
 269                                dprc_get_obj_failures++;
 270                                continue;
 271                        }
 272
 273                        /*
 274                         * add a quirk for all versions of dpsec < 4.0...none
 275                         * are coherent regardless of what the MC reports.
 276                         */
 277                        if ((strcmp(obj_desc->type, "dpseci") == 0) &&
 278                            (obj_desc->ver_major < 4))
 279                                obj_desc->flags |=
 280                                        FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY;
 281
 282                        irq_count += obj_desc->irq_count;
 283                        dev_dbg(&mc_bus_dev->dev,
 284                                "Discovered object: type %s, id %d\n",
 285                                obj_desc->type, obj_desc->id);
 286                }
 287
 288                if (dprc_get_obj_failures != 0) {
 289                        dev_err(&mc_bus_dev->dev,
 290                                "%d out of %d devices could not be retrieved\n",
 291                                dprc_get_obj_failures, num_child_objects);
 292                }
 293        }
 294
 295        /*
 296         * Allocate IRQ's before binding the scanned devices with their
 297         * respective drivers.
 298         */
 299        if (dev_get_msi_domain(&mc_bus_dev->dev) && !mc_bus->irq_resources) {
 300                if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) {
 301                        dev_warn(&mc_bus_dev->dev,
 302                                 "IRQs needed (%u) exceed IRQs preallocated (%u)\n",
 303                                 irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
 304                }
 305
 306                error = fsl_mc_populate_irq_pool(mc_bus,
 307                                FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
 308                if (error < 0)
 309                        return error;
 310        }
 311
 312        if (total_irq_count)
 313                *total_irq_count = irq_count;
 314
 315        dprc_remove_devices(mc_bus_dev, child_obj_desc_array,
 316                            num_child_objects);
 317
 318        dprc_add_new_devices(mc_bus_dev, child_obj_desc_array,
 319                             num_child_objects);
 320
 321        if (child_obj_desc_array)
 322                devm_kfree(&mc_bus_dev->dev, child_obj_desc_array);
 323
 324        return 0;
 325}
 326
 327/**
 328 * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state
 329 *
 330 * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
 331 *
 332 * Scans the physical DPRC and synchronizes the state of the Linux
 333 * bus driver with the actual state of the MC by adding and removing
 334 * devices as appropriate.
 335 */
 336static int dprc_scan_container(struct fsl_mc_device *mc_bus_dev)
 337{
 338        int error;
 339        struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
 340
 341        fsl_mc_init_all_resource_pools(mc_bus_dev);
 342
 343        /*
 344         * Discover objects in the DPRC:
 345         */
 346        mutex_lock(&mc_bus->scan_mutex);
 347        error = dprc_scan_objects(mc_bus_dev, NULL);
 348        mutex_unlock(&mc_bus->scan_mutex);
 349        if (error < 0) {
 350                fsl_mc_cleanup_all_resource_pools(mc_bus_dev);
 351                return error;
 352        }
 353
 354        return 0;
 355}
 356
 357/**
 358 * dprc_irq0_handler - Regular ISR for DPRC interrupt 0
 359 *
 360 * @irq: IRQ number of the interrupt being handled
 361 * @arg: Pointer to device structure
 362 */
 363static irqreturn_t dprc_irq0_handler(int irq_num, void *arg)
 364{
 365        return IRQ_WAKE_THREAD;
 366}
 367
 368/**
 369 * dprc_irq0_handler_thread - Handler thread function for DPRC interrupt 0
 370 *
 371 * @irq: IRQ number of the interrupt being handled
 372 * @arg: Pointer to device structure
 373 */
 374static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg)
 375{
 376        int error;
 377        u32 status;
 378        struct device *dev = arg;
 379        struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
 380        struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
 381        struct fsl_mc_io *mc_io = mc_dev->mc_io;
 382        struct msi_desc *msi_desc = mc_dev->irqs[0]->msi_desc;
 383
 384        dev_dbg(dev, "DPRC IRQ %d triggered on CPU %u\n",
 385                irq_num, smp_processor_id());
 386
 387        if (!(mc_dev->flags & FSL_MC_IS_DPRC))
 388                return IRQ_HANDLED;
 389
 390        mutex_lock(&mc_bus->scan_mutex);
 391        if (!msi_desc || msi_desc->irq != (u32)irq_num)
 392                goto out;
 393
 394        status = 0;
 395        error = dprc_get_irq_status(mc_io, 0, mc_dev->mc_handle, 0,
 396                                    &status);
 397        if (error < 0) {
 398                dev_err(dev,
 399                        "dprc_get_irq_status() failed: %d\n", error);
 400                goto out;
 401        }
 402
 403        error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0,
 404                                      status);
 405        if (error < 0) {
 406                dev_err(dev,
 407                        "dprc_clear_irq_status() failed: %d\n", error);
 408                goto out;
 409        }
 410
 411        if (status & (DPRC_IRQ_EVENT_OBJ_ADDED |
 412                      DPRC_IRQ_EVENT_OBJ_REMOVED |
 413                      DPRC_IRQ_EVENT_CONTAINER_DESTROYED |
 414                      DPRC_IRQ_EVENT_OBJ_DESTROYED |
 415                      DPRC_IRQ_EVENT_OBJ_CREATED)) {
 416                unsigned int irq_count;
 417
 418                error = dprc_scan_objects(mc_dev, &irq_count);
 419                if (error < 0) {
 420                        /*
 421                         * If the error is -ENXIO, we ignore it, as it indicates
 422                         * that the object scan was aborted, as we detected that
 423                         * an object was removed from the DPRC in the MC, while
 424                         * we were scanning the DPRC.
 425                         */
 426                        if (error != -ENXIO) {
 427                                dev_err(dev, "dprc_scan_objects() failed: %d\n",
 428                                        error);
 429                        }
 430
 431                        goto out;
 432                }
 433
 434                if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) {
 435                        dev_warn(dev,
 436                                 "IRQs needed (%u) exceed IRQs preallocated (%u)\n",
 437                                 irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
 438                }
 439        }
 440
 441out:
 442        mutex_unlock(&mc_bus->scan_mutex);
 443        return IRQ_HANDLED;
 444}
 445
 446/*
 447 * Disable and clear interrupt for a given DPRC object
 448 */
 449static int disable_dprc_irq(struct fsl_mc_device *mc_dev)
 450{
 451        int error;
 452        struct fsl_mc_io *mc_io = mc_dev->mc_io;
 453
 454        /*
 455         * Disable generation of interrupt, while we configure it:
 456         */
 457        error = dprc_set_irq_enable(mc_io, 0, mc_dev->mc_handle, 0, 0);
 458        if (error < 0) {
 459                dev_err(&mc_dev->dev,
 460                        "Disabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n",
 461                        error);
 462                return error;
 463        }
 464
 465        /*
 466         * Disable all interrupt causes for the interrupt:
 467         */
 468        error = dprc_set_irq_mask(mc_io, 0, mc_dev->mc_handle, 0, 0x0);
 469        if (error < 0) {
 470                dev_err(&mc_dev->dev,
 471                        "Disabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n",
 472                        error);
 473                return error;
 474        }
 475
 476        /*
 477         * Clear any leftover interrupts:
 478         */
 479        error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0, ~0x0U);
 480        if (error < 0) {
 481                dev_err(&mc_dev->dev,
 482                        "Disabling DPRC IRQ failed: dprc_clear_irq_status() failed: %d\n",
 483                        error);
 484                return error;
 485        }
 486
 487        return 0;
 488}
 489
 490static int register_dprc_irq_handler(struct fsl_mc_device *mc_dev)
 491{
 492        int error;
 493        struct fsl_mc_device_irq *irq = mc_dev->irqs[0];
 494
 495        /*
 496         * NOTE: devm_request_threaded_irq() invokes the device-specific
 497         * function that programs the MSI physically in the device
 498         */
 499        error = devm_request_threaded_irq(&mc_dev->dev,
 500                                          irq->msi_desc->irq,
 501                                          dprc_irq0_handler,
 502                                          dprc_irq0_handler_thread,
 503                                          IRQF_NO_SUSPEND | IRQF_ONESHOT,
 504                                          dev_name(&mc_dev->dev),
 505                                          &mc_dev->dev);
 506        if (error < 0) {
 507                dev_err(&mc_dev->dev,
 508                        "devm_request_threaded_irq() failed: %d\n",
 509                        error);
 510                return error;
 511        }
 512
 513        return 0;
 514}
 515
 516static int enable_dprc_irq(struct fsl_mc_device *mc_dev)
 517{
 518        int error;
 519
 520        /*
 521         * Enable all interrupt causes for the interrupt:
 522         */
 523        error = dprc_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, 0,
 524                                  ~0x0u);
 525        if (error < 0) {
 526                dev_err(&mc_dev->dev,
 527                        "Enabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n",
 528                        error);
 529
 530                return error;
 531        }
 532
 533        /*
 534         * Enable generation of the interrupt:
 535         */
 536        error = dprc_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, 0, 1);
 537        if (error < 0) {
 538                dev_err(&mc_dev->dev,
 539                        "Enabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n",
 540                        error);
 541
 542                return error;
 543        }
 544
 545        return 0;
 546}
 547
 548/*
 549 * Setup interrupt for a given DPRC device
 550 */
 551static int dprc_setup_irq(struct fsl_mc_device *mc_dev)
 552{
 553        int error;
 554
 555        error = fsl_mc_allocate_irqs(mc_dev);
 556        if (error < 0)
 557                return error;
 558
 559        error = disable_dprc_irq(mc_dev);
 560        if (error < 0)
 561                goto error_free_irqs;
 562
 563        error = register_dprc_irq_handler(mc_dev);
 564        if (error < 0)
 565                goto error_free_irqs;
 566
 567        error = enable_dprc_irq(mc_dev);
 568        if (error < 0)
 569                goto error_free_irqs;
 570
 571        return 0;
 572
 573error_free_irqs:
 574        fsl_mc_free_irqs(mc_dev);
 575        return error;
 576}
 577
 578/**
 579 * dprc_probe - callback invoked when a DPRC is being bound to this driver
 580 *
 581 * @mc_dev: Pointer to fsl-mc device representing a DPRC
 582 *
 583 * It opens the physical DPRC in the MC.
 584 * It scans the DPRC to discover the MC objects contained in it.
 585 * It creates the interrupt pool for the MC bus associated with the DPRC.
 586 * It configures the interrupts for the DPRC device itself.
 587 */
 588static int dprc_probe(struct fsl_mc_device *mc_dev)
 589{
 590        int error;
 591        size_t region_size;
 592        struct device *parent_dev = mc_dev->dev.parent;
 593        struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
 594        bool mc_io_created = false;
 595        bool msi_domain_set = false;
 596        u16 major_ver, minor_ver;
 597
 598        if (!is_fsl_mc_bus_dprc(mc_dev))
 599                return -EINVAL;
 600
 601        if (dev_get_msi_domain(&mc_dev->dev))
 602                return -EINVAL;
 603
 604        if (!mc_dev->mc_io) {
 605                /*
 606                 * This is a child DPRC:
 607                 */
 608                if (!dev_is_fsl_mc(parent_dev))
 609                        return -EINVAL;
 610
 611                if (mc_dev->obj_desc.region_count == 0)
 612                        return -EINVAL;
 613
 614                region_size = resource_size(mc_dev->regions);
 615
 616                error = fsl_create_mc_io(&mc_dev->dev,
 617                                         mc_dev->regions[0].start,
 618                                         region_size,
 619                                         NULL,
 620                                         FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
 621                                         &mc_dev->mc_io);
 622                if (error < 0)
 623                        return error;
 624
 625                mc_io_created = true;
 626
 627                /*
 628                 * Inherit parent MSI domain:
 629                 */
 630                dev_set_msi_domain(&mc_dev->dev,
 631                                   dev_get_msi_domain(parent_dev));
 632                msi_domain_set = true;
 633        } else {
 634                /*
 635                 * This is a root DPRC
 636                 */
 637                struct irq_domain *mc_msi_domain;
 638
 639                if (dev_is_fsl_mc(parent_dev))
 640                        return -EINVAL;
 641
 642                error = fsl_mc_find_msi_domain(parent_dev,
 643                                               &mc_msi_domain);
 644                if (error < 0) {
 645                        dev_warn(&mc_dev->dev,
 646                                 "WARNING: MC bus without interrupt support\n");
 647                } else {
 648                        dev_set_msi_domain(&mc_dev->dev, mc_msi_domain);
 649                        msi_domain_set = true;
 650                }
 651        }
 652
 653        error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id,
 654                          &mc_dev->mc_handle);
 655        if (error < 0) {
 656                dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error);
 657                goto error_cleanup_msi_domain;
 658        }
 659
 660        error = dprc_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle,
 661                                    &mc_bus->dprc_attr);
 662        if (error < 0) {
 663                dev_err(&mc_dev->dev, "dprc_get_attributes() failed: %d\n",
 664                        error);
 665                goto error_cleanup_open;
 666        }
 667
 668        error = dprc_get_api_version(mc_dev->mc_io, 0,
 669                                     &major_ver,
 670                                     &minor_ver);
 671        if (error < 0) {
 672                dev_err(&mc_dev->dev, "dprc_get_api_version() failed: %d\n",
 673                        error);
 674                goto error_cleanup_open;
 675        }
 676
 677        if (major_ver < DPRC_MIN_VER_MAJOR ||
 678            (major_ver == DPRC_MIN_VER_MAJOR &&
 679             minor_ver < DPRC_MIN_VER_MINOR)) {
 680                dev_err(&mc_dev->dev,
 681                        "ERROR: DPRC version %d.%d not supported\n",
 682                        major_ver, minor_ver);
 683                error = -ENOTSUPP;
 684                goto error_cleanup_open;
 685        }
 686
 687        mutex_init(&mc_bus->scan_mutex);
 688
 689        /*
 690         * Discover MC objects in DPRC object:
 691         */
 692        error = dprc_scan_container(mc_dev);
 693        if (error < 0)
 694                goto error_cleanup_open;
 695
 696        /*
 697         * Configure interrupt for the DPRC object associated with this MC bus:
 698         */
 699        error = dprc_setup_irq(mc_dev);
 700        if (error < 0)
 701                goto error_cleanup_open;
 702
 703        dev_info(&mc_dev->dev, "DPRC device bound to driver");
 704        return 0;
 705
 706error_cleanup_open:
 707        (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
 708
 709error_cleanup_msi_domain:
 710        if (msi_domain_set)
 711                dev_set_msi_domain(&mc_dev->dev, NULL);
 712
 713        if (mc_io_created) {
 714                fsl_destroy_mc_io(mc_dev->mc_io);
 715                mc_dev->mc_io = NULL;
 716        }
 717
 718        return error;
 719}
 720
 721/*
 722 * Tear down interrupt for a given DPRC object
 723 */
 724static void dprc_teardown_irq(struct fsl_mc_device *mc_dev)
 725{
 726        struct fsl_mc_device_irq *irq = mc_dev->irqs[0];
 727
 728        (void)disable_dprc_irq(mc_dev);
 729
 730        devm_free_irq(&mc_dev->dev, irq->msi_desc->irq, &mc_dev->dev);
 731
 732        fsl_mc_free_irqs(mc_dev);
 733}
 734
 735/**
 736 * dprc_remove - callback invoked when a DPRC is being unbound from this driver
 737 *
 738 * @mc_dev: Pointer to fsl-mc device representing the DPRC
 739 *
 740 * It removes the DPRC's child objects from Linux (not from the MC) and
 741 * closes the DPRC device in the MC.
 742 * It tears down the interrupts that were configured for the DPRC device.
 743 * It destroys the interrupt pool associated with this MC bus.
 744 */
 745static int dprc_remove(struct fsl_mc_device *mc_dev)
 746{
 747        int error;
 748        struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
 749
 750        if (!is_fsl_mc_bus_dprc(mc_dev))
 751                return -EINVAL;
 752        if (!mc_dev->mc_io)
 753                return -EINVAL;
 754
 755        if (!mc_bus->irq_resources)
 756                return -EINVAL;
 757
 758        if (dev_get_msi_domain(&mc_dev->dev))
 759                dprc_teardown_irq(mc_dev);
 760
 761        device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
 762
 763        if (dev_get_msi_domain(&mc_dev->dev)) {
 764                fsl_mc_cleanup_irq_pool(mc_bus);
 765                dev_set_msi_domain(&mc_dev->dev, NULL);
 766        }
 767
 768        fsl_mc_cleanup_all_resource_pools(mc_dev);
 769
 770        error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
 771        if (error < 0)
 772                dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error);
 773
 774        if (!fsl_mc_is_root_dprc(&mc_dev->dev)) {
 775                fsl_destroy_mc_io(mc_dev->mc_io);
 776                mc_dev->mc_io = NULL;
 777        }
 778
 779        dev_info(&mc_dev->dev, "DPRC device unbound from driver");
 780        return 0;
 781}
 782
 783static const struct fsl_mc_device_id match_id_table[] = {
 784        {
 785         .vendor = FSL_MC_VENDOR_FREESCALE,
 786         .obj_type = "dprc"},
 787        {.vendor = 0x0},
 788};
 789
 790static struct fsl_mc_driver dprc_driver = {
 791        .driver = {
 792                   .name = FSL_MC_DPRC_DRIVER_NAME,
 793                   .owner = THIS_MODULE,
 794                   .pm = NULL,
 795                   },
 796        .match_id_table = match_id_table,
 797        .probe = dprc_probe,
 798        .remove = dprc_remove,
 799};
 800
 801int __init dprc_driver_init(void)
 802{
 803        return fsl_mc_driver_register(&dprc_driver);
 804}
 805
 806void dprc_driver_exit(void)
 807{
 808        fsl_mc_driver_unregister(&dprc_driver);
 809}
 810