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