xref: /freebsd/sys/dev/mpi3mr/mpi3mr_pci.c (revision 7ef62cebc2f965b0f640263e179276928885e33d)
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 
311 	sc = (struct mpi3mr_softc *)arg;
312 	mpi3mr_dprint(sc, MPI3MR_XINFO, "%s entry\n", __func__);
313 
314 	mtx_lock(&sc->mpi3mr_mtx);
315 
316 	mpi3mr_startup(sc);
317 	mtx_unlock(&sc->mpi3mr_mtx);
318 
319 	mpi3mr_dprint(sc, MPI3MR_XINFO, "disestablish config intrhook\n");
320 	config_intrhook_disestablish(&sc->mpi3mr_ich);
321 	sc->mpi3mr_ich.ich_arg = NULL;
322 
323 	mpi3mr_dprint(sc, MPI3MR_XINFO, "%s exit\n", __func__);
324 }
325 
326 /**
327  * mpi3mr_ctrl_security_status -Check controller secure status
328  * @pdev: PCI device instance
329  *
330  * Read the Device Serial Number capability from PCI config
331  * space and decide whether the controller is secure or not.
332  *
333  * Return: 0 on success, non-zero on failure.
334  */
335 static int
336 mpi3mr_ctrl_security_status(device_t dev)
337 {
338 	int dev_serial_num, retval = 0;
339 	uint32_t cap_data, ctrl_status, debug_status;
340 	/* Check if Device serial number extended capability is supported */
341 	if (pci_find_extcap(dev, PCIZ_SERNUM, &dev_serial_num) != 0) {
342 		device_printf(dev,
343 		    "PCIZ_SERNUM is not supported\n");
344 		return -1;
345 	}
346 
347 	cap_data = pci_read_config(dev, dev_serial_num + 4, 4);
348 
349 	debug_status = cap_data & MPI3MR_CTLR_SECURE_DBG_STATUS_MASK;
350 	ctrl_status = cap_data & MPI3MR_CTLR_SECURITY_STATUS_MASK;
351 
352 	switch (ctrl_status) {
353 	case MPI3MR_INVALID_DEVICE:
354 		device_printf(dev,
355 		    "Invalid (Non secure) controller is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n",
356 		    pci_get_device(dev), pci_get_subvendor(dev),
357 		    pci_get_subdevice(dev));
358 		retval = -1;
359 		break;
360 	case MPI3MR_CONFIG_SECURE_DEVICE:
361 		if (!debug_status)
362 			device_printf(dev, "Config secure controller is detected\n");
363 		break;
364 	case MPI3MR_HARD_SECURE_DEVICE:
365 		device_printf(dev, "Hard secure controller is detected\n");
366 		break;
367 	case MPI3MR_TAMPERED_DEVICE:
368 		device_printf(dev,
369 		    "Tampered (Non secure) controller is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n",
370 		    pci_get_device(dev), pci_get_subvendor(dev),
371 		    pci_get_subdevice(dev));
372 		retval = -1;
373 		break;
374 	default:
375 		retval = -1;
376 			break;
377 	}
378 
379 	if (!retval && debug_status) {
380 		device_printf(dev,
381 		    "Secure Debug (Non secure) controller is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n",
382 		    pci_get_device(dev), pci_get_subvendor(dev),
383 		    pci_get_subdevice(dev));
384 		retval = -1;
385 	}
386 
387 	return retval;
388 }
389 /*
390  * mpi3mr_pci_attach - PCI entry point
391  * @dev: pointer to device struct
392  *
393  * This function does the setup of PCI and registers, allocates controller resources,
394  * initializes mutexes, linked lists and registers interrupts, CAM and initializes
395  * the controller.
396  *
397  * Return: 0 on success and proper error codes on failure
398  */
399 static int
400 mpi3mr_pci_attach(device_t dev)
401 {
402 	struct mpi3mr_softc *sc;
403 	int error;
404 
405 	sc = device_get_softc(dev);
406 	bzero(sc, sizeof(*sc));
407 	sc->mpi3mr_dev = dev;
408 
409 	/* Don't load driver for Non-Secure controllers */
410 	if (mpi3mr_ctrl_security_status(dev)) {
411 		sc->secure_ctrl = false;
412 		return 0;
413 	}
414 
415 	sc->secure_ctrl = true;
416 
417 	if ((error = mpi3mr_setup_resources(sc)) != 0)
418 		goto load_failed;
419 
420 	sc->id = sc_ids++;
421 	mpi3mr_atomic_set(&sc->fw_outstanding, 0);
422 	mpi3mr_atomic_set(&sc->pend_ioctls, 0);
423 	sc->admin_req = NULL;
424 	sc->admin_reply = NULL;
425 	sprintf(sc->driver_name, "%s", MPI3MR_DRIVER_NAME);
426 	sprintf(sc->name, "%s%d", sc->driver_name, sc->id);
427 
428 	sc->mpi3mr_dev = dev;
429 	mpi3mr_get_tunables(sc);
430 
431 	if ((error = mpi3mr_initialize_ioc(sc, MPI3MR_INIT_TYPE_INIT)) != 0) {
432 		mpi3mr_dprint(sc, MPI3MR_ERROR, "FW initialization failed\n");
433 		goto load_failed;
434 	}
435 
436 	if ((error = mpi3mr_alloc_requests(sc)) != 0) {
437 		mpi3mr_dprint(sc, MPI3MR_ERROR, "Command frames allocation failed\n");
438 		goto load_failed;
439 	}
440 
441 	if ((error = mpi3mr_cam_attach(sc)) != 0) {
442 		mpi3mr_dprint(sc, MPI3MR_ERROR, "CAM attach failed\n");
443 		goto load_failed;
444 	}
445 
446 	error = mpi3mr_kproc_create(mpi3mr_watchdog_thread, sc,
447 	    &sc->watchdog_thread, 0, 0, "mpi3mr_watchdog%d",
448 	    device_get_unit(sc->mpi3mr_dev));
449 	if (error) {
450 		device_printf(sc->mpi3mr_dev, "Error %d starting OCR thread\n", error);
451 		goto load_failed;
452 	}
453 
454 	sc->mpi3mr_ich.ich_func = mpi3mr_ich_startup;
455 	sc->mpi3mr_ich.ich_arg = sc;
456 	if (config_intrhook_establish(&sc->mpi3mr_ich) != 0) {
457 		mpi3mr_dprint(sc, MPI3MR_ERROR,
458 		    "Cannot establish MPI3MR ICH config hook\n");
459 		error = EINVAL;
460 	}
461 
462 	mpi3mr_dprint(sc, MPI3MR_INFO, "allocating ioctl dma buffers\n");
463 	mpi3mr_alloc_ioctl_dma_memory(sc);
464 
465 	if ((error = mpi3mr_app_attach(sc)) != 0) {
466 		mpi3mr_dprint(sc, MPI3MR_ERROR, "APP/IOCTL attach failed\n");
467 		goto load_failed;
468 	}
469 
470 	mpi3mr_setup_sysctl(sc);
471 
472 	return 0;
473 
474 load_failed:
475 	mpi3mr_cleanup_interrupts(sc);
476 	mpi3mr_free_mem(sc);
477 	mpi3mr_app_detach(sc);
478 	mpi3mr_cam_detach(sc);
479 	mpi3mr_destory_mtx(sc);
480 	mpi3mr_release_resources(sc);
481 	return error;
482 }
483 
484 void mpi3mr_cleanup_interrupts(struct mpi3mr_softc *sc)
485 {
486 	mpi3mr_disable_interrupts(sc);
487 
488 	mpi3mr_teardown_irqs(sc);
489 
490 	if (sc->irq_ctx) {
491 		free(sc->irq_ctx, M_MPI3MR);
492 		sc->irq_ctx = NULL;
493 	}
494 
495 	if (sc->msix_enable)
496 		pci_release_msi(sc->mpi3mr_dev);
497 
498 	sc->msix_count = 0;
499 
500 }
501 
502 int mpi3mr_setup_irqs(struct mpi3mr_softc *sc)
503 {
504 	device_t dev;
505 	int error;
506 	int i, rid, initial_rid;
507 	struct mpi3mr_irq_context *irq_ctx;
508 	struct irq_info *irq_info;
509 
510 	dev = sc->mpi3mr_dev;
511 	error = -1;
512 
513 	if (sc->msix_enable)
514 		initial_rid = 1;
515 	else
516 		initial_rid = 0;
517 
518 	for (i = 0; i < sc->msix_count; i++) {
519 		irq_ctx = &sc->irq_ctx[i];
520 		irq_ctx->msix_index = i;
521 		irq_ctx->sc = sc;
522 		irq_info = &irq_ctx->irq_info;
523 		rid = i + initial_rid;
524 		irq_info->irq_rid = rid;
525 		irq_info->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
526 		    &irq_info->irq_rid, RF_ACTIVE);
527 		if (irq_info->irq == NULL) {
528 			mpi3mr_dprint(sc, MPI3MR_ERROR,
529 			    "Cannot allocate interrupt RID %d\n", rid);
530 			sc->msix_count = i;
531 			break;
532 		}
533 		error = bus_setup_intr(dev, irq_info->irq,
534 		    INTR_MPSAFE | INTR_TYPE_CAM, NULL, mpi3mr_isr,
535 		    irq_ctx, &irq_info->intrhand);
536 		if (error) {
537 			mpi3mr_dprint(sc, MPI3MR_ERROR,
538 			    "Cannot setup interrupt RID %d\n", rid);
539 			sc->msix_count = i;
540 			break;
541 		}
542 	}
543 
544         mpi3mr_dprint(sc, MPI3MR_INFO, "Set up %d MSI-x interrupts\n", sc->msix_count);
545 
546 	return (error);
547 
548 }
549 
550 static void
551 mpi3mr_teardown_irqs(struct mpi3mr_softc *sc)
552 {
553 	struct irq_info *irq_info;
554 	int i;
555 
556 	for (i = 0; i < sc->msix_count; i++) {
557 		irq_info = &sc->irq_ctx[i].irq_info;
558 		if (irq_info->irq != NULL) {
559 			bus_teardown_intr(sc->mpi3mr_dev, irq_info->irq,
560 			    irq_info->intrhand);
561 			bus_release_resource(sc->mpi3mr_dev, SYS_RES_IRQ,
562 			    irq_info->irq_rid, irq_info->irq);
563 		}
564 	}
565 
566 }
567 
568 /*
569  * Allocate, but don't assign interrupts early.  Doing it before requesting
570  * the IOCFacts message informs the firmware that we want to do MSI-X
571  * multiqueue.  We might not use all of the available messages, but there's
572  * no reason to re-alloc if we don't.
573  */
574 int
575 mpi3mr_alloc_interrupts(struct mpi3mr_softc *sc, U16 setup_one)
576 {
577 	int error, msgs;
578 	U16 num_queues;
579 
580 	error = 0;
581 	msgs = 0;
582 
583 	mpi3mr_cleanup_interrupts(sc);
584 
585 	if (setup_one) {
586 		msgs = 1;
587 	} else {
588 		msgs = min(sc->max_msix_vectors, sc->cpu_count);
589 		num_queues = min(sc->facts.max_op_reply_q, sc->facts.max_op_req_q);
590 		msgs = min(msgs, num_queues);
591 
592 		mpi3mr_dprint(sc, MPI3MR_INFO, "Supported MSI-x count: %d "
593 			" CPU count: %d Requested MSI-x count: %d\n",
594 			sc->max_msix_vectors,
595 			sc->cpu_count, msgs);
596 	}
597 
598 	if (msgs != 0) {
599 		error = pci_alloc_msix(sc->mpi3mr_dev, &msgs);
600 		if (error) {
601 			mpi3mr_dprint(sc, MPI3MR_ERROR,
602 			    "Could not allocate MSI-x interrupts Error: %x\n", error);
603 			goto out_failed;
604 		} else
605 			sc->msix_enable = 1;
606 	}
607 
608 	sc->msix_count = msgs;
609 	sc->irq_ctx = malloc(sizeof(struct mpi3mr_irq_context) * msgs,
610 		M_MPI3MR, M_NOWAIT | M_ZERO);
611 
612 	if (!sc->irq_ctx) {
613 		mpi3mr_dprint(sc, MPI3MR_ERROR, "Cannot alloc memory for interrupt info\n");
614 		error = -1;
615 		goto out_failed;
616 	}
617 
618 	mpi3mr_dprint(sc, MPI3MR_XINFO, "Allocated %d MSI-x interrupts\n", msgs);
619 
620 	return error;
621 out_failed:
622 	mpi3mr_cleanup_interrupts(sc);
623 	return (error);
624 }
625 
626 static int
627 mpi3mr_pci_detach(device_t dev)
628 {
629 	struct mpi3mr_softc *sc;
630 	int i = 0;
631 
632 	sc = device_get_softc(dev);
633 
634 	if (!sc->secure_ctrl)
635 		return 0;
636 
637 	sc->mpi3mr_flags |= MPI3MR_FLAGS_SHUTDOWN;
638 
639 	if (sc->sysctl_tree != NULL)
640 		sysctl_ctx_free(&sc->sysctl_ctx);
641 
642 	if (sc->watchdog_thread_active)
643 		wakeup(&sc->watchdog_chan);
644 
645 	while (sc->reset_in_progress && (i < PEND_IOCTLS_COMP_WAIT_TIME)) {
646 		i++;
647 		if (!(i % 5)) {
648 			mpi3mr_dprint(sc, MPI3MR_INFO,
649 			    "[%2d]waiting for reset to be finished from %s\n", i, __func__);
650 		}
651 		pause("mpi3mr_shutdown", hz);
652 	}
653 
654 	i = 0;
655 	while (sc->watchdog_thread_active && (i < 180)) {
656 		i++;
657 		if (!(i % 5)) {
658 			mpi3mr_dprint(sc, MPI3MR_INFO,
659 			    "[%2d]waiting for "
660 			    "mpi3mr_reset thread to quit reset %d\n", i,
661 			    sc->watchdog_thread_active);
662 		}
663 		pause("mpi3mr_shutdown", hz);
664 	}
665 
666 	i = 0;
667 	while (mpi3mr_atomic_read(&sc->pend_ioctls) && (i < 180)) {
668 		i++;
669 		if (!(i % 5)) {
670 			mpi3mr_dprint(sc, MPI3MR_INFO,
671 			    "[%2d]waiting for IOCTL to be finished from %s\n", i, __func__);
672 		}
673 		pause("mpi3mr_shutdown", hz);
674 	}
675 
676 	mpi3mr_cleanup_ioc(sc);
677 	mpi3mr_cleanup_event_taskq(sc);
678 	mpi3mr_app_detach(sc);
679 	mpi3mr_cam_detach(sc);
680 	mpi3mr_cleanup_interrupts(sc);
681 	mpi3mr_destory_mtx(sc);
682 	mpi3mr_free_mem(sc);
683 	mpi3mr_release_resources(sc);
684 	sc_ids--;
685 	return (0);
686 }
687 
688 static int
689 mpi3mr_pci_suspend(device_t dev)
690 {
691 	return (EINVAL);
692 }
693 
694 static int
695 mpi3mr_pci_resume(device_t dev)
696 {
697 	return (EINVAL);
698 }
699