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