1 /*
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2020-2023, Broadcom Inc. All rights reserved.
5 * Support: <[email protected]>
6 *
7 * Authors: Sumit Saxena <[email protected]>
8 * Chandrakanth Patil <[email protected]>
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
mpi3mr_setup_sysctl(struct mpi3mr_softc * sc)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_STRING(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
149 OID_AUTO, "firmware_version", CTLFLAG_RD, sc->fw_version,
150 strlen(sc->fw_version), "firmware version");
151
152 SYSCTL_ADD_UINT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
153 OID_AUTO, "mpi3mr_debug", CTLFLAG_RW, &sc->mpi3mr_debug, 0,
154 "Driver debug level");
155 SYSCTL_ADD_UINT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
156 OID_AUTO, "reset", CTLFLAG_RW, &sc->reset.type, 0,
157 "Soft reset(1)/Diag reset(2)");
158 SYSCTL_ADD_UINT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
159 OID_AUTO, "iot_enable", CTLFLAG_RW, &sc->iot_enable, 0,
160 "IO throttling enable at driver level(for debug purpose)");
161 }
162
163 /*
164 * mpi3mr_get_tunables: get tunable parameters.
165 * input: Adapter instance soft state
166 *
167 * Get tunable parameters. This will help to debug driver at boot time.
168 */
169 static void
mpi3mr_get_tunables(struct mpi3mr_softc * sc)170 mpi3mr_get_tunables(struct mpi3mr_softc *sc)
171 {
172 char tmpstr[80];
173
174 sc->mpi3mr_debug =
175 (MPI3MR_ERROR | MPI3MR_INFO | MPI3MR_FAULT);
176
177 sc->reset_in_progress = 0;
178 sc->reset.type = 0;
179 sc->iot_enable = 1;
180 /*
181 * Grab the global variables.
182 */
183 TUNABLE_INT_FETCH("hw.mpi3mr.debug_level", &sc->mpi3mr_debug);
184 TUNABLE_INT_FETCH("hw.mpi3mr.ctrl_reset", &sc->reset.type);
185 TUNABLE_INT_FETCH("hw.mpi3mr.iot_enable", &sc->iot_enable);
186
187 /* Grab the unit-instance variables */
188 snprintf(tmpstr, sizeof(tmpstr), "dev.mpi3mr.%d.debug_level",
189 device_get_unit(sc->mpi3mr_dev));
190 TUNABLE_INT_FETCH(tmpstr, &sc->mpi3mr_debug);
191
192 snprintf(tmpstr, sizeof(tmpstr), "dev.mpi3mr.%d.reset",
193 device_get_unit(sc->mpi3mr_dev));
194 TUNABLE_INT_FETCH(tmpstr, &sc->reset.type);
195
196 snprintf(tmpstr, sizeof(tmpstr), "dev.mpi3mr.%d.iot_enable",
197 device_get_unit(sc->mpi3mr_dev));
198 TUNABLE_INT_FETCH(tmpstr, &sc->iot_enable);
199 }
200
201 static struct mpi3mr_ident *
mpi3mr_find_ident(device_t dev)202 mpi3mr_find_ident(device_t dev)
203 {
204 struct mpi3mr_ident *m;
205
206 for (m = mpi3mr_identifiers; m->vendor != 0; m++) {
207 if (m->vendor != pci_get_vendor(dev))
208 continue;
209 if (m->device != pci_get_device(dev))
210 continue;
211 if ((m->subvendor != 0xffff) &&
212 (m->subvendor != pci_get_subvendor(dev)))
213 continue;
214 if ((m->subdevice != 0xffff) &&
215 (m->subdevice != pci_get_subdevice(dev)))
216 continue;
217 return (m);
218 }
219
220 return (NULL);
221 }
222
223 static int
mpi3mr_pci_probe(device_t dev)224 mpi3mr_pci_probe(device_t dev)
225 {
226 static u_int8_t first_ctrl = 1;
227 struct mpi3mr_ident *id;
228 char raw_os_ver[16];
229
230 if ((id = mpi3mr_find_ident(dev)) != NULL) {
231 if (first_ctrl) {
232 first_ctrl = 0;
233 MPI3MR_OS_VERSION(raw_os_ver, fmt_os_ver);
234 printf("mpi3mr: Loading Broadcom mpi3mr driver version: %s OS version: %s\n",
235 MPI3MR_DRIVER_VERSION, fmt_os_ver);
236 }
237 device_set_desc(dev, id->desc);
238 device_set_desc(dev, id->desc);
239 return (BUS_PROBE_DEFAULT);
240 }
241 return (ENXIO);
242 }
243
244 static void
mpi3mr_release_resources(struct mpi3mr_softc * sc)245 mpi3mr_release_resources(struct mpi3mr_softc *sc)
246 {
247 if (sc->mpi3mr_parent_dmat != NULL) {
248 bus_dma_tag_destroy(sc->mpi3mr_parent_dmat);
249 }
250
251 if (sc->mpi3mr_regs_resource != NULL) {
252 bus_release_resource(sc->mpi3mr_dev, SYS_RES_MEMORY,
253 sc->mpi3mr_regs_rid, sc->mpi3mr_regs_resource);
254 }
255 }
256
mpi3mr_setup_resources(struct mpi3mr_softc * sc)257 static int mpi3mr_setup_resources(struct mpi3mr_softc *sc)
258 {
259 bus_dma_template_t t;
260 int i;
261 device_t dev = sc->mpi3mr_dev;
262
263 pci_enable_busmaster(dev);
264
265 for (i = 0; i < PCI_MAXMAPS_0; i++) {
266 sc->mpi3mr_regs_rid = PCIR_BAR(i);
267
268 if ((sc->mpi3mr_regs_resource = bus_alloc_resource_any(dev,
269 SYS_RES_MEMORY, &sc->mpi3mr_regs_rid, RF_ACTIVE)) != NULL)
270 break;
271 }
272
273 if (sc->mpi3mr_regs_resource == NULL) {
274 mpi3mr_printf(sc, "Cannot allocate PCI registers\n");
275 return (ENXIO);
276 }
277
278 sc->mpi3mr_btag = rman_get_bustag(sc->mpi3mr_regs_resource);
279 sc->mpi3mr_bhandle = rman_get_bushandle(sc->mpi3mr_regs_resource);
280
281 /*
282 * XXX Perhaps we should move this to after we read iocfacts and use
283 * that to create the proper parent tag. However, to get the iocfacts
284 * we need to have a dmatag for both the admin queue and the iocfacts
285 * DMA transfer. So for now, we just create a 'no restriction' tag and
286 * use sc->dma_loaddr for all the other tag_create calls to get the
287 * right value. It would be nice if one could retroactively adjust a
288 * created tag. The Linux driver effectively does this by setting the
289 * dma_mask on the device.
290 */
291 /* Allocate the parent DMA tag */
292 bus_dma_template_init(&t, bus_get_dma_tag(dev));
293 if (bus_dma_template_tag(&t, &sc->mpi3mr_parent_dmat)) {
294 mpi3mr_dprint(sc, MPI3MR_ERROR, "Cannot allocate parent DMA tag\n");
295 return (ENOMEM);
296 }
297
298 sc->max_msix_vectors = pci_msix_count(dev);
299
300 return 0;
301 }
302
303 static int
mpi3mr_startup(struct mpi3mr_softc * sc)304 mpi3mr_startup(struct mpi3mr_softc *sc)
305 {
306 sc->mpi3mr_flags &= ~MPI3MR_FLAGS_PORT_ENABLE_DONE;
307 mpi3mr_issue_port_enable(sc, 1);
308 return (0);
309 }
310
311 /* Run through any late-start handlers. */
312 static void
mpi3mr_ich_startup(void * arg)313 mpi3mr_ich_startup(void *arg)
314 {
315 struct mpi3mr_softc *sc;
316 int error;
317
318 sc = (struct mpi3mr_softc *)arg;
319 mpi3mr_dprint(sc, MPI3MR_XINFO, "%s entry\n", __func__);
320
321 mtx_lock(&sc->mpi3mr_mtx);
322
323 mpi3mr_startup(sc);
324
325 mtx_unlock(&sc->mpi3mr_mtx);
326
327 error = mpi3mr_kproc_create(mpi3mr_watchdog_thread, sc,
328 &sc->watchdog_thread, 0, 0, "mpi3mr_watchdog%d",
329 device_get_unit(sc->mpi3mr_dev));
330
331 if (error)
332 device_printf(sc->mpi3mr_dev, "Error %d starting OCR thread\n", error);
333
334 mpi3mr_dprint(sc, MPI3MR_XINFO, "disestablish config intrhook\n");
335 config_intrhook_disestablish(&sc->mpi3mr_ich);
336 sc->mpi3mr_ich.ich_arg = NULL;
337
338 mpi3mr_dprint(sc, MPI3MR_XINFO, "%s exit\n", __func__);
339 }
340
341 /**
342 * mpi3mr_ctrl_security_status -Check controller secure status
343 * @pdev: PCI device instance
344 *
345 * Read the Device Serial Number capability from PCI config
346 * space and decide whether the controller is secure or not.
347 *
348 * Return: 0 on success, non-zero on failure.
349 */
350 static int
mpi3mr_ctrl_security_status(device_t dev)351 mpi3mr_ctrl_security_status(device_t dev)
352 {
353 int dev_serial_num, retval = 0;
354 uint32_t cap_data, ctrl_status, debug_status;
355 /* Check if Device serial number extended capability is supported */
356 if (pci_find_extcap(dev, PCIZ_SERNUM, &dev_serial_num) != 0) {
357 device_printf(dev,
358 "PCIZ_SERNUM is not supported\n");
359 return -1;
360 }
361
362 cap_data = pci_read_config(dev, dev_serial_num + 4, 4);
363
364 debug_status = cap_data & MPI3MR_CTLR_SECURE_DBG_STATUS_MASK;
365 ctrl_status = cap_data & MPI3MR_CTLR_SECURITY_STATUS_MASK;
366
367 switch (ctrl_status) {
368 case MPI3MR_INVALID_DEVICE:
369 device_printf(dev,
370 "Invalid (Non secure) controller is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n",
371 pci_get_device(dev), pci_get_subvendor(dev),
372 pci_get_subdevice(dev));
373 retval = -1;
374 break;
375 case MPI3MR_CONFIG_SECURE_DEVICE:
376 if (!debug_status)
377 device_printf(dev, "Config secure controller is detected\n");
378 break;
379 case MPI3MR_HARD_SECURE_DEVICE:
380 device_printf(dev, "Hard secure controller is detected\n");
381 break;
382 case MPI3MR_TAMPERED_DEVICE:
383 device_printf(dev,
384 "Tampered (Non secure) controller is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n",
385 pci_get_device(dev), pci_get_subvendor(dev),
386 pci_get_subdevice(dev));
387 retval = -1;
388 break;
389 default:
390 retval = -1;
391 break;
392 }
393
394 if (!retval && debug_status) {
395 device_printf(dev,
396 "Secure Debug (Non secure) controller is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n",
397 pci_get_device(dev), pci_get_subvendor(dev),
398 pci_get_subdevice(dev));
399 retval = -1;
400 }
401
402 return retval;
403 }
404 /*
405 * mpi3mr_pci_attach - PCI entry point
406 * @dev: pointer to device struct
407 *
408 * This function does the setup of PCI and registers, allocates controller resources,
409 * initializes mutexes, linked lists and registers interrupts, CAM and initializes
410 * the controller.
411 *
412 * Return: 0 on success and proper error codes on failure
413 */
414 static int
mpi3mr_pci_attach(device_t dev)415 mpi3mr_pci_attach(device_t dev)
416 {
417 struct mpi3mr_softc *sc;
418 int error;
419
420 sc = device_get_softc(dev);
421 bzero(sc, sizeof(*sc));
422 sc->mpi3mr_dev = dev;
423
424 /* Don't load driver for Non-Secure controllers */
425 if (mpi3mr_ctrl_security_status(dev)) {
426 sc->secure_ctrl = false;
427 return 0;
428 }
429
430 sc->secure_ctrl = true;
431
432 if ((error = mpi3mr_setup_resources(sc)) != 0)
433 goto load_failed;
434
435 sc->id = sc_ids++;
436 mpi3mr_atomic_set(&sc->fw_outstanding, 0);
437 mpi3mr_atomic_set(&sc->pend_ioctls, 0);
438 sc->admin_req = NULL;
439 sc->admin_reply = NULL;
440 sprintf(sc->driver_name, "%s", MPI3MR_DRIVER_NAME);
441 sprintf(sc->name, "%s%d", sc->driver_name, sc->id);
442
443 sc->mpi3mr_dev = dev;
444 mpi3mr_get_tunables(sc);
445
446 if ((error = mpi3mr_initialize_ioc(sc, MPI3MR_INIT_TYPE_INIT)) != 0) {
447 mpi3mr_dprint(sc, MPI3MR_ERROR, "FW initialization failed\n");
448 goto load_failed;
449 }
450
451 if ((error = mpi3mr_alloc_requests(sc)) != 0) {
452 mpi3mr_dprint(sc, MPI3MR_ERROR, "Command frames allocation failed\n");
453 goto load_failed;
454 }
455
456 if ((error = mpi3mr_cam_attach(sc)) != 0) {
457 mpi3mr_dprint(sc, MPI3MR_ERROR, "CAM attach failed\n");
458 goto load_failed;
459 }
460
461 sc->mpi3mr_ich.ich_func = mpi3mr_ich_startup;
462 sc->mpi3mr_ich.ich_arg = sc;
463 if (config_intrhook_establish(&sc->mpi3mr_ich) != 0) {
464 mpi3mr_dprint(sc, MPI3MR_ERROR,
465 "Cannot establish MPI3MR ICH config hook\n");
466 error = EINVAL;
467 }
468
469 mpi3mr_dprint(sc, MPI3MR_INFO, "allocating ioctl dma buffers\n");
470 mpi3mr_alloc_ioctl_dma_memory(sc);
471
472 if ((error = mpi3mr_app_attach(sc)) != 0) {
473 mpi3mr_dprint(sc, MPI3MR_ERROR, "APP/IOCTL attach failed\n");
474 goto load_failed;
475 }
476
477 mpi3mr_setup_sysctl(sc);
478
479 return 0;
480
481 load_failed:
482 mpi3mr_cleanup_interrupts(sc);
483 mpi3mr_free_mem(sc);
484 mpi3mr_app_detach(sc);
485 mpi3mr_cam_detach(sc);
486 mpi3mr_destory_mtx(sc);
487 mpi3mr_release_resources(sc);
488 return error;
489 }
490
mpi3mr_cleanup_interrupts(struct mpi3mr_softc * sc)491 void mpi3mr_cleanup_interrupts(struct mpi3mr_softc *sc)
492 {
493 mpi3mr_disable_interrupts(sc);
494
495 mpi3mr_teardown_irqs(sc);
496
497 if (sc->irq_ctx) {
498 free(sc->irq_ctx, M_MPI3MR);
499 sc->irq_ctx = NULL;
500 }
501
502 if (sc->msix_enable)
503 pci_release_msi(sc->mpi3mr_dev);
504
505 sc->msix_count = 0;
506
507 }
508
mpi3mr_setup_irqs(struct mpi3mr_softc * sc)509 int mpi3mr_setup_irqs(struct mpi3mr_softc *sc)
510 {
511 device_t dev;
512 int error;
513 int i, rid, initial_rid;
514 struct mpi3mr_irq_context *irq_ctx;
515 struct irq_info *irq_info;
516
517 dev = sc->mpi3mr_dev;
518 error = -1;
519
520 if (sc->msix_enable)
521 initial_rid = 1;
522 else
523 initial_rid = 0;
524
525 for (i = 0; i < sc->msix_count; i++) {
526 irq_ctx = &sc->irq_ctx[i];
527 irq_ctx->msix_index = i;
528 irq_ctx->sc = sc;
529 irq_info = &irq_ctx->irq_info;
530 rid = i + initial_rid;
531 irq_info->irq_rid = rid;
532 irq_info->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
533 &irq_info->irq_rid, RF_ACTIVE);
534 if (irq_info->irq == NULL) {
535 mpi3mr_dprint(sc, MPI3MR_ERROR,
536 "Cannot allocate interrupt RID %d\n", rid);
537 sc->msix_count = i;
538 break;
539 }
540 error = bus_setup_intr(dev, irq_info->irq,
541 INTR_MPSAFE | INTR_TYPE_CAM, NULL, mpi3mr_isr,
542 irq_ctx, &irq_info->intrhand);
543 if (error) {
544 mpi3mr_dprint(sc, MPI3MR_ERROR,
545 "Cannot setup interrupt RID %d\n", rid);
546 sc->msix_count = i;
547 break;
548 }
549 }
550
551 mpi3mr_dprint(sc, MPI3MR_INFO, "Set up %d MSI-x interrupts\n", sc->msix_count);
552
553 return (error);
554
555 }
556
557 static void
mpi3mr_teardown_irqs(struct mpi3mr_softc * sc)558 mpi3mr_teardown_irqs(struct mpi3mr_softc *sc)
559 {
560 struct irq_info *irq_info;
561 int i;
562
563 for (i = 0; i < sc->msix_count; i++) {
564 irq_info = &sc->irq_ctx[i].irq_info;
565 if (irq_info->irq != NULL) {
566 bus_teardown_intr(sc->mpi3mr_dev, irq_info->irq,
567 irq_info->intrhand);
568 bus_release_resource(sc->mpi3mr_dev, SYS_RES_IRQ,
569 irq_info->irq_rid, irq_info->irq);
570 }
571 }
572
573 }
574
575 /*
576 * Allocate, but don't assign interrupts early. Doing it before requesting
577 * the IOCFacts message informs the firmware that we want to do MSI-X
578 * multiqueue. We might not use all of the available messages, but there's
579 * no reason to re-alloc if we don't.
580 */
581 int
mpi3mr_alloc_interrupts(struct mpi3mr_softc * sc,U16 setup_one)582 mpi3mr_alloc_interrupts(struct mpi3mr_softc *sc, U16 setup_one)
583 {
584 int error, msgs;
585 U16 num_queues;
586
587 error = 0;
588 msgs = 0;
589
590 mpi3mr_cleanup_interrupts(sc);
591
592 if (setup_one) {
593 msgs = 1;
594 } else {
595 msgs = min(sc->max_msix_vectors, sc->cpu_count);
596 num_queues = min(sc->facts.max_op_reply_q, sc->facts.max_op_req_q);
597 msgs = min(msgs, num_queues);
598
599 mpi3mr_dprint(sc, MPI3MR_INFO, "Supported MSI-x count: %d "
600 " CPU count: %d Requested MSI-x count: %d\n",
601 sc->max_msix_vectors,
602 sc->cpu_count, msgs);
603 }
604
605 if (msgs != 0) {
606 error = pci_alloc_msix(sc->mpi3mr_dev, &msgs);
607 if (error) {
608 mpi3mr_dprint(sc, MPI3MR_ERROR,
609 "Could not allocate MSI-x interrupts Error: %x\n", error);
610 goto out_failed;
611 } else
612 sc->msix_enable = 1;
613 }
614
615 sc->msix_count = msgs;
616 sc->irq_ctx = malloc(sizeof(struct mpi3mr_irq_context) * msgs,
617 M_MPI3MR, M_NOWAIT | M_ZERO);
618
619 if (!sc->irq_ctx) {
620 mpi3mr_dprint(sc, MPI3MR_ERROR, "Cannot alloc memory for interrupt info\n");
621 error = -1;
622 goto out_failed;
623 }
624
625 mpi3mr_dprint(sc, MPI3MR_XINFO, "Allocated %d MSI-x interrupts\n", msgs);
626
627 return error;
628 out_failed:
629 mpi3mr_cleanup_interrupts(sc);
630 return (error);
631 }
632
633 static int
mpi3mr_pci_detach(device_t dev)634 mpi3mr_pci_detach(device_t dev)
635 {
636 struct mpi3mr_softc *sc;
637 int i = 0;
638
639 sc = device_get_softc(dev);
640
641 if (!sc->secure_ctrl)
642 return 0;
643
644
645 if (sc->sysctl_tree != NULL)
646 sysctl_ctx_free(&sc->sysctl_ctx);
647
648 mtx_lock(&sc->reset_mutex);
649 sc->mpi3mr_flags |= MPI3MR_FLAGS_SHUTDOWN;
650 if (sc->watchdog_thread_active)
651 wakeup(&sc->watchdog_chan);
652 mtx_unlock(&sc->reset_mutex);
653
654 while (sc->reset_in_progress && (i < PEND_IOCTLS_COMP_WAIT_TIME)) {
655 i++;
656 if (!(i % 5)) {
657 mpi3mr_dprint(sc, MPI3MR_INFO,
658 "[%2d]waiting for reset to be finished from %s\n", i, __func__);
659 }
660 pause("mpi3mr_shutdown", hz);
661 }
662
663 i = 0;
664 while (sc->watchdog_thread_active && (i < 180)) {
665 i++;
666 if (!(i % 5)) {
667 mpi3mr_dprint(sc, MPI3MR_INFO,
668 "[%2d]waiting for "
669 "mpi3mr_reset thread to quit reset %d\n", i,
670 sc->watchdog_thread_active);
671 }
672 pause("mpi3mr_shutdown", hz);
673 }
674
675 i = 0;
676 while (mpi3mr_atomic_read(&sc->pend_ioctls) && (i < 180)) {
677 i++;
678 if (!(i % 5)) {
679 mpi3mr_dprint(sc, MPI3MR_INFO,
680 "[%2d]waiting for IOCTL to be finished from %s\n", i, __func__);
681 }
682 pause("mpi3mr_shutdown", hz);
683 }
684
685 mpi3mr_cleanup_ioc(sc);
686 mpi3mr_cleanup_event_taskq(sc);
687 mpi3mr_app_detach(sc);
688 mpi3mr_cam_detach(sc);
689 mpi3mr_cleanup_interrupts(sc);
690 mpi3mr_destory_mtx(sc);
691 mpi3mr_free_mem(sc);
692 mpi3mr_release_resources(sc);
693 sc_ids--;
694 return (0);
695 }
696
697 static int
mpi3mr_pci_suspend(device_t dev)698 mpi3mr_pci_suspend(device_t dev)
699 {
700 return (EINVAL);
701 }
702
703 static int
mpi3mr_pci_resume(device_t dev)704 mpi3mr_pci_resume(device_t dev)
705 {
706 return (EINVAL);
707 }
708