1 /* 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2020-2023, Broadcom Inc. All rights reserved. 5 * Support: <fbsd-storage-driver.pdl@broadcom.com> 6 * 7 * Authors: Sumit Saxena <sumit.saxena@broadcom.com> 8 * Chandrakanth Patil <chandrakanth.patil@broadcom.com> 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions are 12 * met: 13 * 14 * 1. Redistributions of source code must retain the above copyright notice, 15 * this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright notice, 17 * this list of conditions and the following disclaimer in the documentation and/or other 18 * materials provided with the distribution. 19 * 3. Neither the name of the Broadcom Inc. nor the names of its contributors 20 * may be used to endorse or promote products derived from this software without 21 * specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 * POSSIBILITY OF SUCH DAMAGE. 34 * 35 * The views and conclusions contained in the software and documentation are 36 * those of the authors and should not be interpreted as representing 37 * official policies,either expressed or implied, of the FreeBSD Project. 38 * 39 * Mail to: Broadcom Inc 1320 Ridder Park Dr, San Jose, CA 95131 40 * 41 * Broadcom Inc. (Broadcom) MPI3MR Adapter FreeBSD 42 */ 43 44 #include "mpi3mr.h" 45 #include "mpi3mr_cam.h" 46 #include "mpi3mr_app.h" 47 48 static int sc_ids; 49 static int mpi3mr_pci_probe(device_t); 50 static int mpi3mr_pci_attach(device_t); 51 static int mpi3mr_pci_detach(device_t); 52 static int mpi3mr_pci_suspend(device_t); 53 static int mpi3mr_pci_resume(device_t); 54 static int mpi3mr_setup_resources(struct mpi3mr_softc *sc); 55 static void mpi3mr_release_resources(struct mpi3mr_softc *); 56 static void mpi3mr_teardown_irqs(struct mpi3mr_softc *sc); 57 58 extern void mpi3mr_watchdog_thread(void *arg); 59 60 static device_method_t mpi3mr_methods[] = { 61 DEVMETHOD(device_probe, mpi3mr_pci_probe), 62 DEVMETHOD(device_attach, mpi3mr_pci_attach), 63 DEVMETHOD(device_detach, mpi3mr_pci_detach), 64 DEVMETHOD(device_suspend, mpi3mr_pci_suspend), 65 DEVMETHOD(device_resume, mpi3mr_pci_resume), 66 DEVMETHOD(bus_print_child, bus_generic_print_child), 67 DEVMETHOD(bus_driver_added, bus_generic_driver_added), 68 { 0, 0 } 69 }; 70 71 char fmt_os_ver[16]; 72 73 SYSCTL_NODE(_hw, OID_AUTO, mpi3mr, CTLFLAG_RD, 0, "MPI3MR Driver Parameters"); 74 MALLOC_DEFINE(M_MPI3MR, "mpi3mrbuf", "Buffers for the MPI3MR driver"); 75 76 static driver_t mpi3mr_pci_driver = { 77 "mpi3mr", 78 mpi3mr_methods, 79 sizeof(struct mpi3mr_softc) 80 }; 81 82 struct mpi3mr_ident { 83 uint16_t vendor; 84 uint16_t device; 85 uint16_t subvendor; 86 uint16_t subdevice; 87 u_int flags; 88 const char *desc; 89 } mpi3mr_identifiers[] = { 90 { MPI3_MFGPAGE_VENDORID_BROADCOM, MPI3_MFGPAGE_DEVID_SAS4116, 91 0xffff, 0xffff, 0, "Broadcom MPIMR 3.0 controller" }, 92 }; 93 94 DRIVER_MODULE(mpi3mr, pci, mpi3mr_pci_driver, 0, 0); 95 MODULE_PNP_INFO("U16:vendor;U16:device;U16:subvendor;U16:subdevice;D:#", pci, 96 mpi3mr, mpi3mr_identifiers, nitems(mpi3mr_identifiers) - 1); 97 98 MODULE_DEPEND(mpi3mr, cam, 1, 1, 1); 99 100 /* 101 * mpi3mr_setup_sysctl: setup sysctl values for mpi3mr 102 * input: Adapter instance soft state 103 * 104 * Setup sysctl entries for mpi3mr driver. 105 */ 106 static void 107 mpi3mr_setup_sysctl(struct mpi3mr_softc *sc) 108 { 109 struct sysctl_ctx_list *sysctl_ctx = NULL; 110 struct sysctl_oid *sysctl_tree = NULL; 111 char tmpstr[80], tmpstr2[80]; 112 113 /* 114 * Setup the sysctl variable so the user can change the debug level 115 * on the fly. 116 */ 117 snprintf(tmpstr, sizeof(tmpstr), "MPI3MR controller %d", 118 device_get_unit(sc->mpi3mr_dev)); 119 snprintf(tmpstr2, sizeof(tmpstr2), "%d", device_get_unit(sc->mpi3mr_dev)); 120 121 sysctl_ctx = device_get_sysctl_ctx(sc->mpi3mr_dev); 122 if (sysctl_ctx != NULL) 123 sysctl_tree = device_get_sysctl_tree(sc->mpi3mr_dev); 124 125 if (sysctl_tree == NULL) { 126 sysctl_ctx_init(&sc->sysctl_ctx); 127 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 128 SYSCTL_STATIC_CHILDREN(_hw_mpi3mr), OID_AUTO, tmpstr2, 129 CTLFLAG_RD, 0, tmpstr); 130 if (sc->sysctl_tree == NULL) 131 return; 132 sysctl_ctx = &sc->sysctl_ctx; 133 sysctl_tree = sc->sysctl_tree; 134 } 135 136 SYSCTL_ADD_STRING(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 137 OID_AUTO, "driver_version", CTLFLAG_RD, MPI3MR_DRIVER_VERSION, 138 strlen(MPI3MR_DRIVER_VERSION), "driver version"); 139 140 SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 141 OID_AUTO, "fw_outstanding", CTLFLAG_RD, 142 &sc->fw_outstanding.val_rdonly, 0, "FW outstanding commands"); 143 144 SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 145 OID_AUTO, "io_cmds_highwater", CTLFLAG_RD, 146 &sc->io_cmds_highwater, 0, "Max FW outstanding commands"); 147 148 SYSCTL_ADD_UINT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 149 OID_AUTO, "mpi3mr_debug", CTLFLAG_RW, &sc->mpi3mr_debug, 0, 150 "Driver debug level"); 151 SYSCTL_ADD_UINT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 152 OID_AUTO, "reset", CTLFLAG_RW, &sc->reset.type, 0, 153 "Soft reset(1)/Diag reset(2)"); 154 SYSCTL_ADD_UINT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 155 OID_AUTO, "iot_enable", CTLFLAG_RW, &sc->iot_enable, 0, 156 "IO throttling enable at driver level(for debug purpose)"); 157 } 158 159 /* 160 * mpi3mr_get_tunables: get tunable parameters. 161 * input: Adapter instance soft state 162 * 163 * Get tunable parameters. This will help to debug driver at boot time. 164 */ 165 static void 166 mpi3mr_get_tunables(struct mpi3mr_softc *sc) 167 { 168 char tmpstr[80]; 169 170 sc->mpi3mr_debug = 171 (MPI3MR_ERROR | MPI3MR_INFO | MPI3MR_FAULT); 172 173 sc->reset_in_progress = 0; 174 sc->reset.type = 0; 175 sc->iot_enable = 1; 176 /* 177 * Grab the global variables. 178 */ 179 TUNABLE_INT_FETCH("hw.mpi3mr.debug_level", &sc->mpi3mr_debug); 180 TUNABLE_INT_FETCH("hw.mpi3mr.ctrl_reset", &sc->reset.type); 181 TUNABLE_INT_FETCH("hw.mpi3mr.iot_enable", &sc->iot_enable); 182 183 /* Grab the unit-instance variables */ 184 snprintf(tmpstr, sizeof(tmpstr), "dev.mpi3mr.%d.debug_level", 185 device_get_unit(sc->mpi3mr_dev)); 186 TUNABLE_INT_FETCH(tmpstr, &sc->mpi3mr_debug); 187 188 snprintf(tmpstr, sizeof(tmpstr), "dev.mpi3mr.%d.reset", 189 device_get_unit(sc->mpi3mr_dev)); 190 TUNABLE_INT_FETCH(tmpstr, &sc->reset.type); 191 192 snprintf(tmpstr, sizeof(tmpstr), "dev.mpi3mr.%d.iot_enable", 193 device_get_unit(sc->mpi3mr_dev)); 194 TUNABLE_INT_FETCH(tmpstr, &sc->iot_enable); 195 } 196 197 static struct mpi3mr_ident * 198 mpi3mr_find_ident(device_t dev) 199 { 200 struct mpi3mr_ident *m; 201 202 for (m = mpi3mr_identifiers; m->vendor != 0; m++) { 203 if (m->vendor != pci_get_vendor(dev)) 204 continue; 205 if (m->device != pci_get_device(dev)) 206 continue; 207 if ((m->subvendor != 0xffff) && 208 (m->subvendor != pci_get_subvendor(dev))) 209 continue; 210 if ((m->subdevice != 0xffff) && 211 (m->subdevice != pci_get_subdevice(dev))) 212 continue; 213 return (m); 214 } 215 216 return (NULL); 217 } 218 219 static int 220 mpi3mr_pci_probe(device_t dev) 221 { 222 static u_int8_t first_ctrl = 1; 223 struct mpi3mr_ident *id; 224 char raw_os_ver[16]; 225 226 if ((id = mpi3mr_find_ident(dev)) != NULL) { 227 if (first_ctrl) { 228 first_ctrl = 0; 229 MPI3MR_OS_VERSION(raw_os_ver, fmt_os_ver); 230 printf("mpi3mr: Loading Broadcom mpi3mr driver version: %s OS version: %s\n", 231 MPI3MR_DRIVER_VERSION, fmt_os_ver); 232 } 233 device_set_desc(dev, id->desc); 234 device_set_desc(dev, id->desc); 235 return (BUS_PROBE_DEFAULT); 236 } 237 return (ENXIO); 238 } 239 240 static void 241 mpi3mr_release_resources(struct mpi3mr_softc *sc) 242 { 243 if (sc->mpi3mr_parent_dmat != NULL) { 244 bus_dma_tag_destroy(sc->mpi3mr_parent_dmat); 245 } 246 247 if (sc->mpi3mr_regs_resource != NULL) { 248 bus_release_resource(sc->mpi3mr_dev, SYS_RES_MEMORY, 249 sc->mpi3mr_regs_rid, sc->mpi3mr_regs_resource); 250 } 251 } 252 253 static int mpi3mr_setup_resources(struct mpi3mr_softc *sc) 254 { 255 int i; 256 device_t dev = sc->mpi3mr_dev; 257 258 pci_enable_busmaster(dev); 259 260 for (i = 0; i < PCI_MAXMAPS_0; i++) { 261 sc->mpi3mr_regs_rid = PCIR_BAR(i); 262 263 if ((sc->mpi3mr_regs_resource = bus_alloc_resource_any(dev, 264 SYS_RES_MEMORY, &sc->mpi3mr_regs_rid, RF_ACTIVE)) != NULL) 265 break; 266 } 267 268 if (sc->mpi3mr_regs_resource == NULL) { 269 mpi3mr_printf(sc, "Cannot allocate PCI registers\n"); 270 return (ENXIO); 271 } 272 273 sc->mpi3mr_btag = rman_get_bustag(sc->mpi3mr_regs_resource); 274 sc->mpi3mr_bhandle = rman_get_bushandle(sc->mpi3mr_regs_resource); 275 276 /* Allocate the parent DMA tag */ 277 if (bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 278 1, 0, /* algnmnt, boundary */ 279 BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ 280 BUS_SPACE_MAXADDR, /* highaddr */ 281 NULL, NULL, /* filter, filterarg */ 282 BUS_SPACE_MAXSIZE_32BIT,/* maxsize */ 283 BUS_SPACE_UNRESTRICTED, /* nsegments */ 284 BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 285 0, /* flags */ 286 NULL, NULL, /* lockfunc, lockarg */ 287 &sc->mpi3mr_parent_dmat)) { 288 mpi3mr_dprint(sc, MPI3MR_ERROR, "Cannot allocate parent DMA tag\n"); 289 return (ENOMEM); 290 } 291 292 sc->max_msix_vectors = pci_msix_count(dev); 293 294 return 0; 295 } 296 297 static int 298 mpi3mr_startup(struct mpi3mr_softc *sc) 299 { 300 sc->mpi3mr_flags &= ~MPI3MR_FLAGS_PORT_ENABLE_DONE; 301 mpi3mr_issue_port_enable(sc, 1); 302 return (0); 303 } 304 305 /* Run through any late-start handlers. */ 306 static void 307 mpi3mr_ich_startup(void *arg) 308 { 309 struct mpi3mr_softc *sc; 310 int error; 311 312 sc = (struct mpi3mr_softc *)arg; 313 mpi3mr_dprint(sc, MPI3MR_XINFO, "%s entry\n", __func__); 314 315 mtx_lock(&sc->mpi3mr_mtx); 316 317 mpi3mr_startup(sc); 318 319 mtx_unlock(&sc->mpi3mr_mtx); 320 321 error = mpi3mr_kproc_create(mpi3mr_watchdog_thread, sc, 322 &sc->watchdog_thread, 0, 0, "mpi3mr_watchdog%d", 323 device_get_unit(sc->mpi3mr_dev)); 324 325 if (error) 326 device_printf(sc->mpi3mr_dev, "Error %d starting OCR thread\n", error); 327 328 mpi3mr_dprint(sc, MPI3MR_XINFO, "disestablish config intrhook\n"); 329 config_intrhook_disestablish(&sc->mpi3mr_ich); 330 sc->mpi3mr_ich.ich_arg = NULL; 331 332 mpi3mr_dprint(sc, MPI3MR_XINFO, "%s exit\n", __func__); 333 } 334 335 /** 336 * mpi3mr_ctrl_security_status -Check controller secure status 337 * @pdev: PCI device instance 338 * 339 * Read the Device Serial Number capability from PCI config 340 * space and decide whether the controller is secure or not. 341 * 342 * Return: 0 on success, non-zero on failure. 343 */ 344 static int 345 mpi3mr_ctrl_security_status(device_t dev) 346 { 347 int dev_serial_num, retval = 0; 348 uint32_t cap_data, ctrl_status, debug_status; 349 /* Check if Device serial number extended capability is supported */ 350 if (pci_find_extcap(dev, PCIZ_SERNUM, &dev_serial_num) != 0) { 351 device_printf(dev, 352 "PCIZ_SERNUM is not supported\n"); 353 return -1; 354 } 355 356 cap_data = pci_read_config(dev, dev_serial_num + 4, 4); 357 358 debug_status = cap_data & MPI3MR_CTLR_SECURE_DBG_STATUS_MASK; 359 ctrl_status = cap_data & MPI3MR_CTLR_SECURITY_STATUS_MASK; 360 361 switch (ctrl_status) { 362 case MPI3MR_INVALID_DEVICE: 363 device_printf(dev, 364 "Invalid (Non secure) controller is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n", 365 pci_get_device(dev), pci_get_subvendor(dev), 366 pci_get_subdevice(dev)); 367 retval = -1; 368 break; 369 case MPI3MR_CONFIG_SECURE_DEVICE: 370 if (!debug_status) 371 device_printf(dev, "Config secure controller is detected\n"); 372 break; 373 case MPI3MR_HARD_SECURE_DEVICE: 374 device_printf(dev, "Hard secure controller is detected\n"); 375 break; 376 case MPI3MR_TAMPERED_DEVICE: 377 device_printf(dev, 378 "Tampered (Non secure) controller is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n", 379 pci_get_device(dev), pci_get_subvendor(dev), 380 pci_get_subdevice(dev)); 381 retval = -1; 382 break; 383 default: 384 retval = -1; 385 break; 386 } 387 388 if (!retval && debug_status) { 389 device_printf(dev, 390 "Secure Debug (Non secure) controller is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n", 391 pci_get_device(dev), pci_get_subvendor(dev), 392 pci_get_subdevice(dev)); 393 retval = -1; 394 } 395 396 return retval; 397 } 398 /* 399 * mpi3mr_pci_attach - PCI entry point 400 * @dev: pointer to device struct 401 * 402 * This function does the setup of PCI and registers, allocates controller resources, 403 * initializes mutexes, linked lists and registers interrupts, CAM and initializes 404 * the controller. 405 * 406 * Return: 0 on success and proper error codes on failure 407 */ 408 static int 409 mpi3mr_pci_attach(device_t dev) 410 { 411 struct mpi3mr_softc *sc; 412 int error; 413 414 sc = device_get_softc(dev); 415 bzero(sc, sizeof(*sc)); 416 sc->mpi3mr_dev = dev; 417 418 /* Don't load driver for Non-Secure controllers */ 419 if (mpi3mr_ctrl_security_status(dev)) { 420 sc->secure_ctrl = false; 421 return 0; 422 } 423 424 sc->secure_ctrl = true; 425 426 if ((error = mpi3mr_setup_resources(sc)) != 0) 427 goto load_failed; 428 429 sc->id = sc_ids++; 430 mpi3mr_atomic_set(&sc->fw_outstanding, 0); 431 mpi3mr_atomic_set(&sc->pend_ioctls, 0); 432 sc->admin_req = NULL; 433 sc->admin_reply = NULL; 434 sprintf(sc->driver_name, "%s", MPI3MR_DRIVER_NAME); 435 sprintf(sc->name, "%s%d", sc->driver_name, sc->id); 436 437 sc->mpi3mr_dev = dev; 438 mpi3mr_get_tunables(sc); 439 440 if ((error = mpi3mr_initialize_ioc(sc, MPI3MR_INIT_TYPE_INIT)) != 0) { 441 mpi3mr_dprint(sc, MPI3MR_ERROR, "FW initialization failed\n"); 442 goto load_failed; 443 } 444 445 if ((error = mpi3mr_alloc_requests(sc)) != 0) { 446 mpi3mr_dprint(sc, MPI3MR_ERROR, "Command frames allocation failed\n"); 447 goto load_failed; 448 } 449 450 if ((error = mpi3mr_cam_attach(sc)) != 0) { 451 mpi3mr_dprint(sc, MPI3MR_ERROR, "CAM attach failed\n"); 452 goto load_failed; 453 } 454 455 sc->mpi3mr_ich.ich_func = mpi3mr_ich_startup; 456 sc->mpi3mr_ich.ich_arg = sc; 457 if (config_intrhook_establish(&sc->mpi3mr_ich) != 0) { 458 mpi3mr_dprint(sc, MPI3MR_ERROR, 459 "Cannot establish MPI3MR ICH config hook\n"); 460 error = EINVAL; 461 } 462 463 mpi3mr_dprint(sc, MPI3MR_INFO, "allocating ioctl dma buffers\n"); 464 mpi3mr_alloc_ioctl_dma_memory(sc); 465 466 if ((error = mpi3mr_app_attach(sc)) != 0) { 467 mpi3mr_dprint(sc, MPI3MR_ERROR, "APP/IOCTL attach failed\n"); 468 goto load_failed; 469 } 470 471 mpi3mr_setup_sysctl(sc); 472 473 return 0; 474 475 load_failed: 476 mpi3mr_cleanup_interrupts(sc); 477 mpi3mr_free_mem(sc); 478 mpi3mr_app_detach(sc); 479 mpi3mr_cam_detach(sc); 480 mpi3mr_destory_mtx(sc); 481 mpi3mr_release_resources(sc); 482 return error; 483 } 484 485 void mpi3mr_cleanup_interrupts(struct mpi3mr_softc *sc) 486 { 487 mpi3mr_disable_interrupts(sc); 488 489 mpi3mr_teardown_irqs(sc); 490 491 if (sc->irq_ctx) { 492 free(sc->irq_ctx, M_MPI3MR); 493 sc->irq_ctx = NULL; 494 } 495 496 if (sc->msix_enable) 497 pci_release_msi(sc->mpi3mr_dev); 498 499 sc->msix_count = 0; 500 501 } 502 503 int mpi3mr_setup_irqs(struct mpi3mr_softc *sc) 504 { 505 device_t dev; 506 int error; 507 int i, rid, initial_rid; 508 struct mpi3mr_irq_context *irq_ctx; 509 struct irq_info *irq_info; 510 511 dev = sc->mpi3mr_dev; 512 error = -1; 513 514 if (sc->msix_enable) 515 initial_rid = 1; 516 else 517 initial_rid = 0; 518 519 for (i = 0; i < sc->msix_count; i++) { 520 irq_ctx = &sc->irq_ctx[i]; 521 irq_ctx->msix_index = i; 522 irq_ctx->sc = sc; 523 irq_info = &irq_ctx->irq_info; 524 rid = i + initial_rid; 525 irq_info->irq_rid = rid; 526 irq_info->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 527 &irq_info->irq_rid, RF_ACTIVE); 528 if (irq_info->irq == NULL) { 529 mpi3mr_dprint(sc, MPI3MR_ERROR, 530 "Cannot allocate interrupt RID %d\n", rid); 531 sc->msix_count = i; 532 break; 533 } 534 error = bus_setup_intr(dev, irq_info->irq, 535 INTR_MPSAFE | INTR_TYPE_CAM, NULL, mpi3mr_isr, 536 irq_ctx, &irq_info->intrhand); 537 if (error) { 538 mpi3mr_dprint(sc, MPI3MR_ERROR, 539 "Cannot setup interrupt RID %d\n", rid); 540 sc->msix_count = i; 541 break; 542 } 543 } 544 545 mpi3mr_dprint(sc, MPI3MR_INFO, "Set up %d MSI-x interrupts\n", sc->msix_count); 546 547 return (error); 548 549 } 550 551 static void 552 mpi3mr_teardown_irqs(struct mpi3mr_softc *sc) 553 { 554 struct irq_info *irq_info; 555 int i; 556 557 for (i = 0; i < sc->msix_count; i++) { 558 irq_info = &sc->irq_ctx[i].irq_info; 559 if (irq_info->irq != NULL) { 560 bus_teardown_intr(sc->mpi3mr_dev, irq_info->irq, 561 irq_info->intrhand); 562 bus_release_resource(sc->mpi3mr_dev, SYS_RES_IRQ, 563 irq_info->irq_rid, irq_info->irq); 564 } 565 } 566 567 } 568 569 /* 570 * Allocate, but don't assign interrupts early. Doing it before requesting 571 * the IOCFacts message informs the firmware that we want to do MSI-X 572 * multiqueue. We might not use all of the available messages, but there's 573 * no reason to re-alloc if we don't. 574 */ 575 int 576 mpi3mr_alloc_interrupts(struct mpi3mr_softc *sc, U16 setup_one) 577 { 578 int error, msgs; 579 U16 num_queues; 580 581 error = 0; 582 msgs = 0; 583 584 mpi3mr_cleanup_interrupts(sc); 585 586 if (setup_one) { 587 msgs = 1; 588 } else { 589 msgs = min(sc->max_msix_vectors, sc->cpu_count); 590 num_queues = min(sc->facts.max_op_reply_q, sc->facts.max_op_req_q); 591 msgs = min(msgs, num_queues); 592 593 mpi3mr_dprint(sc, MPI3MR_INFO, "Supported MSI-x count: %d " 594 " CPU count: %d Requested MSI-x count: %d\n", 595 sc->max_msix_vectors, 596 sc->cpu_count, msgs); 597 } 598 599 if (msgs != 0) { 600 error = pci_alloc_msix(sc->mpi3mr_dev, &msgs); 601 if (error) { 602 mpi3mr_dprint(sc, MPI3MR_ERROR, 603 "Could not allocate MSI-x interrupts Error: %x\n", error); 604 goto out_failed; 605 } else 606 sc->msix_enable = 1; 607 } 608 609 sc->msix_count = msgs; 610 sc->irq_ctx = malloc(sizeof(struct mpi3mr_irq_context) * msgs, 611 M_MPI3MR, M_NOWAIT | M_ZERO); 612 613 if (!sc->irq_ctx) { 614 mpi3mr_dprint(sc, MPI3MR_ERROR, "Cannot alloc memory for interrupt info\n"); 615 error = -1; 616 goto out_failed; 617 } 618 619 mpi3mr_dprint(sc, MPI3MR_XINFO, "Allocated %d MSI-x interrupts\n", msgs); 620 621 return error; 622 out_failed: 623 mpi3mr_cleanup_interrupts(sc); 624 return (error); 625 } 626 627 static int 628 mpi3mr_pci_detach(device_t dev) 629 { 630 struct mpi3mr_softc *sc; 631 int i = 0; 632 633 sc = device_get_softc(dev); 634 635 if (!sc->secure_ctrl) 636 return 0; 637 638 sc->mpi3mr_flags |= MPI3MR_FLAGS_SHUTDOWN; 639 640 if (sc->sysctl_tree != NULL) 641 sysctl_ctx_free(&sc->sysctl_ctx); 642 643 if (sc->watchdog_thread_active) 644 wakeup(&sc->watchdog_chan); 645 646 while (sc->reset_in_progress && (i < PEND_IOCTLS_COMP_WAIT_TIME)) { 647 i++; 648 if (!(i % 5)) { 649 mpi3mr_dprint(sc, MPI3MR_INFO, 650 "[%2d]waiting for reset to be finished from %s\n", i, __func__); 651 } 652 pause("mpi3mr_shutdown", hz); 653 } 654 655 i = 0; 656 while (sc->watchdog_thread_active && (i < 180)) { 657 i++; 658 if (!(i % 5)) { 659 mpi3mr_dprint(sc, MPI3MR_INFO, 660 "[%2d]waiting for " 661 "mpi3mr_reset thread to quit reset %d\n", i, 662 sc->watchdog_thread_active); 663 } 664 pause("mpi3mr_shutdown", hz); 665 } 666 667 i = 0; 668 while (mpi3mr_atomic_read(&sc->pend_ioctls) && (i < 180)) { 669 i++; 670 if (!(i % 5)) { 671 mpi3mr_dprint(sc, MPI3MR_INFO, 672 "[%2d]waiting for IOCTL to be finished from %s\n", i, __func__); 673 } 674 pause("mpi3mr_shutdown", hz); 675 } 676 677 mpi3mr_cleanup_ioc(sc); 678 mpi3mr_cleanup_event_taskq(sc); 679 mpi3mr_app_detach(sc); 680 mpi3mr_cam_detach(sc); 681 mpi3mr_cleanup_interrupts(sc); 682 mpi3mr_destory_mtx(sc); 683 mpi3mr_free_mem(sc); 684 mpi3mr_release_resources(sc); 685 sc_ids--; 686 return (0); 687 } 688 689 static int 690 mpi3mr_pci_suspend(device_t dev) 691 { 692 return (EINVAL); 693 } 694 695 static int 696 mpi3mr_pci_resume(device_t dev) 697 { 698 return (EINVAL); 699 } 700