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