xref: /titanic_50/usr/src/uts/common/io/bnxe/bnxe_main.c (revision 2777f608ce96fd0d3b91accea29d21d6aacbe7e5)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2014 QLogic Corporation
24  * The contents of this file are subject to the terms of the
25  * QLogic End User License (the "License").
26  * You may not use this file except in compliance with the License.
27  *
28  * You can obtain a copy of the License at
29  * http://www.qlogic.com/Resources/Documents/DriverDownloadHelp/
30  * QLogic_End_User_Software_License.txt
31  * See the License for the specific language governing permissions
32  * and limitations under the License.
33  */
34 
35 /*
36  * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
37  */
38 
39 #include "bnxe.h"
40 
41 #ifndef STRINGIFY
42 #define XSTRINGIFY(x) #x
43 #define STRINGIFY(x) XSTRINGIFY(x)
44 #endif
45 
46 #define BNXE_PRODUCT_BANNER "QLogic NetXtreme II 10 Gigabit Ethernet Driver v" STRINGIFY(MAJVERSION) "." STRINGIFY(MINVERSION) "." STRINGIFY(REVVERSION)
47 #define BNXE_PRODUCT_INFO   "QLogic NXII 10 GbE v" STRINGIFY(MAJVERSION) "." STRINGIFY(MINVERSION) "." STRINGIFY(REVVERSION)
48 
49 #define BNXE_REGISTER_BAR_NUM      1
50 #define BNXE_REGS_MAP_OFFSET       0
51 #define BNXE_L2_MEMORY_WINDOW_SIZE 0x40000 /* 256K for PCI Config Registers */
52 
53 u32_t    dbg_code_path   = CP_ALL;
54 u8_t     dbg_trace_level = LV_VERBOSE;
55 u32_t    g_dbg_flags     = 0;
56 
57 kmutex_t bnxeLoaderMutex;
58 u32_t    bnxeNumPlumbed;
59 
60 extern ddi_dma_attr_t bnxeDmaPageAttrib;
61 extern ddi_dma_attr_t bnxeRxDmaAttrib;
62 extern ddi_dma_attr_t bnxeTxDmaAttrib;
63 extern ddi_dma_attr_t bnxeTxCbDmaAttrib;
64 
65 
66 u8_t BnxeInstance(void * pDev)
67 {
68     um_device_t * pUM = (um_device_t *)pDev;
69     return (pUM == NULL) ? 0xf : pUM->instance;
70 }
71 
72 
73 /* pass in pointer to either lm_device_t or um_device_t */
74 char * BnxeDevName(void * pDev)
75 {
76     um_device_t * pUM = (um_device_t *)pDev;
77     return ((pUM == NULL) || (*pUM->devName == 0)) ? "(bnxe)" : pUM->devName;
78 }
79 
80 
81 char * BnxeChipName(um_device_t * pUM)
82 {
83     switch (CHIP_NUM(&pUM->lm_dev) >> 16)
84     {
85     case 0x164e: return "BCM57710";
86     case 0x164f: return "BCM57711";
87     case 0x1650: return "BCM57711E";
88     case 0x1662: return "BCM57712";
89     case 0x1663: return "BCM57712NP";
90     case 0x16a1: return "BCM57840";
91     case 0x168d: return "BCM57840";
92     case 0x16a4: return "BCM57840NP";
93     case 0x16ab: return "BCM57840NP";
94     case 0x168e: return "BCM57810";
95     case 0x16ae: return "BCM57810NP";
96     case 0x168a: return "BCM57800";
97     case 0x16a5: return "BCM57800NP";
98     default:     return "UNKNOWN";
99     }
100 }
101 
102 
103 boolean_t BnxeProtoSupport(um_device_t * pUM, int proto)
104 {
105     boolean_t do_eth;
106     boolean_t do_fcoe;
107     uint32_t port_feature_config_sf;
108 
109     if (IS_MULTI_VNIC(&pUM->lm_dev))
110     {
111         do_eth  = B_FALSE;
112         do_fcoe = B_FALSE;
113 
114         if (pUM->lm_dev.hw_info.mcp_detected == 1)
115         {
116             if (pUM->lm_dev.params.mf_proto_support_flags &
117                 LM_PROTO_SUPPORT_ETHERNET)
118             {
119                 do_eth = B_TRUE;
120             }
121 
122             if (pUM->lm_dev.params.mf_proto_support_flags &
123                 LM_PROTO_SUPPORT_FCOE)
124             {
125                 do_fcoe = B_TRUE;
126             }
127         }
128         else
129         {
130             /* mcp is not present so allow enumeration */
131             do_eth  = B_TRUE;
132             do_fcoe = B_TRUE;
133         }
134     }
135     else /* SF */
136     {
137         do_eth  = B_TRUE;
138         do_fcoe = B_FALSE;
139 
140         /* check per port storage personality config from NVRAM */
141         port_feature_config_sf = (pUM->lm_dev.hw_info.port_feature_config &
142                                   PORT_FEAT_CFG_STORAGE_PERSONALITY_MASK);
143 
144         switch (port_feature_config_sf)
145         {
146         case PORT_FEAT_CFG_STORAGE_PERSONALITY_ISCSI:
147             break;
148 
149         case PORT_FEAT_CFG_STORAGE_PERSONALITY_FCOE:
150         case PORT_FEAT_CFG_STORAGE_PERSONALITY_BOTH:
151         case PORT_FEAT_CFG_STORAGE_PERSONALITY_DEFAULT:
152         default:
153             do_fcoe = B_TRUE;
154             break;
155         }
156     }
157 
158     if (pUM->lm_dev.params.max_func_fcoe_cons == 0)
159     {
160         do_fcoe = B_FALSE;
161     }
162 
163     return (((proto == LM_PROTO_SUPPORT_ETHERNET) && do_eth) ||
164             ((proto == LM_PROTO_SUPPORT_FCOE) && do_fcoe));
165 }
166 
167 
168 boolean_t BnxeProtoFcoeAfex(um_device_t * pUM)
169 {
170     return ((pUM->lm_dev.params.mf_mode == MULTI_FUNCTION_AFEX) &&
171             BnxeProtoSupport(pUM, LM_PROTO_SUPPORT_FCOE)) ? B_TRUE : B_FALSE;
172 }
173 
174 
175 static boolean_t BnxePciInit(um_device_t * pUM)
176 {
177     /* setup resources needed for accessing the PCI configuration space */
178     if (pci_config_setup(pUM->pDev, &pUM->pPciCfg) != DDI_SUCCESS)
179     {
180         BnxeLogWarn(pUM, "Failed to setup PCI config");
181         return B_FALSE;
182     }
183 
184     return B_TRUE;
185 }
186 
187 
188 static void BnxePciDestroy(um_device_t * pUM)
189 {
190     if (pUM->pPciCfg)
191     {
192         pci_config_teardown(&pUM->pPciCfg);
193         pUM->pPciCfg = NULL;
194     }
195 }
196 
197 
198 static void BnxeBarMemDestroy(um_device_t * pUM)
199 {
200     BnxeMemRegion * pMemRegion;
201 
202     /* free the BAR mappings */
203     while (!d_list_is_empty(&pUM->memRegionList))
204     {
205         pMemRegion = (BnxeMemRegion *)d_list_peek_head(&pUM->memRegionList);
206         mm_unmap_io_space(&pUM->lm_dev,
207                           pMemRegion->pRegAddr,
208                           pMemRegion->size);
209     }
210 }
211 
212 
213 static void BnxeMutexInit(um_device_t * pUM)
214 {
215     lm_device_t * pLM = &pUM->lm_dev;
216     int idx;
217 
218     for (idx = 0; idx < (MAX_RSS_CHAINS + 1); idx++)
219     {
220         mutex_init(&pUM->intrMutex[idx], NULL,
221                    MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
222         mutex_init(&pUM->intrFlipMutex[idx], NULL,
223                    MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
224         mutex_init(&pUM->sbMutex[idx], NULL,
225                    MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
226     }
227 
228     for (idx = 0; idx < MAX_ETH_CONS; idx++)
229     {
230         mutex_init(&pUM->txq[idx].txMutex, NULL,
231                    MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
232         mutex_init(&pUM->txq[idx].freeTxDescMutex, NULL,
233                    MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
234         pUM->txq[idx].pUM = pUM;
235         pUM->txq[idx].idx = idx;
236     }
237 
238     for (idx = 0; idx < MAX_ETH_CONS; idx++)
239     {
240         mutex_init(&pUM->rxq[idx].rxMutex, NULL,
241                    MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
242         mutex_init(&pUM->rxq[idx].doneRxMutex, NULL,
243                    MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
244         pUM->rxq[idx].pUM = pUM;
245         pUM->rxq[idx].idx = idx;
246     }
247 
248     for (idx = 0; idx < USER_OPTION_RX_RING_GROUPS_MAX; idx++)
249     {
250         pUM->rxqGroup[idx].pUM = pUM;
251         pUM->rxqGroup[idx].idx = idx;
252     }
253 
254     mutex_init(&pUM->ethConMutex, NULL,
255                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
256     mutex_init(&pUM->mcpMutex, NULL,
257                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
258     mutex_init(&pUM->phyMutex, NULL,
259                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
260     mutex_init(&pUM->indMutex, NULL,
261                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
262     mutex_init(&pUM->cidMutex, NULL,
263                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
264     mutex_init(&pUM->spqMutex, NULL,
265                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
266     mutex_init(&pUM->spReqMutex, NULL,
267                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
268     mutex_init(&pUM->rrReqMutex, NULL,
269                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
270     mutex_init(&pUM->islesCtrlMutex, NULL,
271                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
272     mutex_init(&pUM->toeMutex, NULL,
273                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
274     mutex_init(&pUM->memMutex, NULL,
275                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
276     mutex_init(&pUM->offloadMutex, NULL,
277                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
278     mutex_init(&pUM->hwInitMutex, NULL,
279                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
280     mutex_init(&pUM->gldMutex, NULL,
281                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
282     rw_init(&pUM->gldTxMutex, NULL, RW_DRIVER, NULL);
283     mutex_init(&pUM->timerMutex, NULL,
284                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
285     mutex_init(&pUM->kstatMutex, NULL,
286                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
287 }
288 
289 
290 static void BnxeMutexDestroy(um_device_t * pUM)
291 {
292     lm_device_t * pLM = &pUM->lm_dev;
293     int idx;
294 
295     for (idx = 0; idx < (MAX_RSS_CHAINS + 1); idx++)
296     {
297         mutex_destroy(&pUM->intrMutex[idx]);
298         mutex_destroy(&pUM->intrFlipMutex[idx]);
299         mutex_destroy(&pUM->sbMutex[idx]);
300     }
301 
302     for (idx = 0; idx < MAX_ETH_CONS; idx++)
303     {
304         mutex_destroy(&pUM->txq[idx].txMutex);
305         mutex_destroy(&pUM->txq[idx].freeTxDescMutex);
306     }
307 
308     for (idx = 0; idx < MAX_ETH_CONS; idx++)
309     {
310         mutex_destroy(&pUM->rxq[idx].rxMutex);
311         mutex_destroy(&pUM->rxq[idx].doneRxMutex);
312     }
313 
314     mutex_destroy(&pUM->ethConMutex);
315     mutex_destroy(&pUM->mcpMutex);
316     mutex_destroy(&pUM->phyMutex);
317     mutex_destroy(&pUM->indMutex);
318     mutex_destroy(&pUM->cidMutex);
319     mutex_destroy(&pUM->spqMutex);
320     mutex_destroy(&pUM->spReqMutex);
321     mutex_destroy(&pUM->rrReqMutex);
322     mutex_destroy(&pUM->islesCtrlMutex);
323     mutex_destroy(&pUM->toeMutex);
324     mutex_destroy(&pUM->memMutex);   /* not until all mem deleted */
325     mutex_destroy(&pUM->offloadMutex);
326     mutex_destroy(&pUM->hwInitMutex);
327     mutex_destroy(&pUM->gldMutex);
328     rw_destroy(&pUM->gldTxMutex);
329     mutex_destroy(&pUM->timerMutex);
330     mutex_destroy(&pUM->kstatMutex);
331 }
332 
333 
334 /* FMA support */
335 
336 int BnxeCheckAccHandle(ddi_acc_handle_t handle)
337 {
338     ddi_fm_error_t de;
339 
340     ddi_fm_acc_err_get(handle, &de, DDI_FME_VERSION);
341     ddi_fm_acc_err_clear(handle, DDI_FME_VERSION);
342 
343     return (de.fme_status);
344 }
345 
346 
347 int BnxeCheckDmaHandle(ddi_dma_handle_t handle)
348 {
349     ddi_fm_error_t de;
350 
351     ddi_fm_dma_err_get(handle, &de, DDI_FME_VERSION);
352 
353     return (de.fme_status);
354 }
355 
356 
357 /* The IO fault service error handling callback function */
358 static int BnxeFmErrorCb(dev_info_t *     pDev,
359                          ddi_fm_error_t * err,
360                          const void *     impl_data)
361 {
362     /*
363      * As the driver can always deal with an error in any dma or
364      * access handle, we can just return the fme_status value.
365      */
366     pci_ereport_post(pDev, err, NULL);
367 
368     return (err->fme_status);
369 }
370 
371 
372 static void BnxeFmInit(um_device_t * pUM)
373 {
374     ddi_iblock_cookie_t iblk;
375     int fma_acc_flag;
376     int fma_dma_flag;
377 
378     /* Only register with IO Fault Services if we have some capability */
379     if (pUM->fmCapabilities & DDI_FM_ACCCHK_CAPABLE)
380     {
381         bnxeAccessAttribBAR.devacc_attr_version = DDI_DEVICE_ATTR_V1;
382         bnxeAccessAttribBAR.devacc_attr_access = DDI_FLAGERR_ACC;
383     }
384 
385     if (pUM->fmCapabilities & DDI_FM_DMACHK_CAPABLE)
386     {
387         bnxeDmaPageAttrib.dma_attr_flags = DDI_DMA_FLAGERR;
388         bnxeRxDmaAttrib.dma_attr_flags   = DDI_DMA_FLAGERR;
389         bnxeTxDmaAttrib.dma_attr_flags   = DDI_DMA_FLAGERR;
390         bnxeTxCbDmaAttrib.dma_attr_flags = DDI_DMA_FLAGERR;
391     }
392 
393     if (pUM->fmCapabilities)
394     {
395         /* Register capabilities with IO Fault Services */
396         ddi_fm_init(pUM->pDev, &pUM->fmCapabilities, &iblk);
397 
398         /* Initialize pci ereport capabilities if ereport capable */
399         if (DDI_FM_EREPORT_CAP(pUM->fmCapabilities) ||
400             DDI_FM_ERRCB_CAP(pUM->fmCapabilities))
401         {
402             pci_ereport_setup(pUM->pDev);
403         }
404 
405         /* Register error callback if error callback capable */
406         if (DDI_FM_ERRCB_CAP(pUM->fmCapabilities))
407         {
408             ddi_fm_handler_register(pUM->pDev, BnxeFmErrorCb, (void *)pUM);
409         }
410     }
411 }
412 
413 
414 static void BnxeFmFini(um_device_t * pUM)
415 {
416     /* Only unregister FMA capabilities if we registered some */
417     if (pUM->fmCapabilities)
418     {
419         /* Release any resources allocated by pci_ereport_setup() */
420         if (DDI_FM_EREPORT_CAP(pUM->fmCapabilities) ||
421             DDI_FM_ERRCB_CAP(pUM->fmCapabilities))
422         {
423             pci_ereport_teardown(pUM->pDev);
424         }
425 
426         /* Un-register error callback if error callback capable */
427         if (DDI_FM_ERRCB_CAP(pUM->fmCapabilities))
428         {
429             ddi_fm_handler_unregister(pUM->pDev);
430         }
431 
432         /* Unregister from IO Fault Services */
433         ddi_fm_fini(pUM->pDev);
434     }
435 }
436 
437 
438 void BnxeFmErrorReport(um_device_t * pUM,
439                        char *        detail)
440 {
441     uint64_t ena;
442     char buf[FM_MAX_CLASS];
443 
444     (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail);
445 
446     ena = fm_ena_generate(0, FM_ENA_FMT1);
447 
448     if (DDI_FM_EREPORT_CAP(pUM->fmCapabilities))
449     {
450         ddi_fm_ereport_post(pUM->pDev, buf, ena, DDI_NOSLEEP,
451                             FM_VERSION, DATA_TYPE_UINT8,
452                             FM_EREPORT_VERS0, NULL);
453     }
454 }
455 
456 
457 static boolean_t BnxeAttachDevice(um_device_t * pUM)
458 {
459     int rc;
460     int * props = NULL;
461     uint_t numProps;
462     u32_t vendor_id;
463     u32_t device_id;
464 
465     /* fm-capable in bnxe.conf can be used to set fmCapabilities. */
466     pUM->fmCapabilities = ddi_prop_get_int(DDI_DEV_T_ANY,
467                                            pUM->pDev,
468                                            DDI_PROP_DONTPASS,
469                                            "fm-capable",
470                                            (DDI_FM_EREPORT_CAPABLE |
471                                             DDI_FM_ACCCHK_CAPABLE  |
472                                             DDI_FM_DMACHK_CAPABLE  |
473                                             DDI_FM_ERRCB_CAPABLE));
474 
475     /* Register capabilities with IO Fault Services. */
476     BnxeFmInit(pUM);
477 
478     if (!BnxePciInit(pUM))
479     {
480         BnxeFmFini(pUM);
481 
482         return B_FALSE;
483     }
484 
485     BnxeMutexInit(pUM);
486 
487     if (!BnxeWorkQueueInit(pUM))
488     {
489         return B_FALSE;
490     }
491 
492     rc = lm_get_dev_info(&pUM->lm_dev);
493 
494     if (pUM->fmCapabilities &&
495         BnxeCheckAccHandle(pUM->pPciCfg) != DDI_FM_OK)
496     {
497         ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_LOST);
498         BnxeWorkQueueWaitAndDestroy(pUM);
499         BnxeMutexDestroy(pUM);
500         BnxePciDestroy(pUM);
501         BnxeFmFini(pUM);
502 
503         return B_FALSE;
504     }
505 
506     if (pUM->fmCapabilities &&
507         BnxeCheckAccHandle(pUM->lm_dev.vars.reg_handle[BAR_0]) != DDI_FM_OK)
508     {
509         ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_LOST);
510         BnxeWorkQueueWaitAndDestroy(pUM);
511         BnxeMutexDestroy(pUM);
512         BnxePciDestroy(pUM);
513         BnxeFmFini(pUM);
514 
515         return B_FALSE;
516     }
517 
518     if (rc != LM_STATUS_SUCCESS)
519     {
520         BnxeFmErrorReport(pUM, DDI_FM_DEVICE_INVAL_STATE);
521         ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_LOST);
522         BnxeWorkQueueWaitAndDestroy(pUM);
523         BnxeMutexDestroy(pUM);
524         BnxePciDestroy(pUM);
525         BnxeFmFini(pUM);
526 
527         BnxeLogWarn(pUM, "Failed to get device information");
528         return B_FALSE;
529     }
530 
531 #if 0
532     if (IS_PFDEV(&pUM->lm_dev) && lm_check_if_pf_assigned_to_vm(&pUM->lm_dev))
533     {
534         lm_set_virt_mode(&pUM->lm_dev, DEVICE_TYPE_PF, VT_ASSIGNED_TO_VM_PF);
535     }
536 #endif
537 
538     /* check if FCoE is enabled on this function */
539 #if 0
540     pUM->do_fcoe =
541         ((CHIP_IS_E2(&pUM->lm_dev) || CHIP_IS_E3(&pUM->lm_dev)) &&
542          BnxeProtoSupport(pUM, LM_PROTO_SUPPORT_FCOE)) ? B_TRUE :
543                                                          B_FALSE;
544 #else
545     pUM->do_fcoe = B_FALSE;
546 #endif
547 
548     lm_get_iscsi_boot_info_block(&pUM->lm_dev, &pUM->iscsiInfo);
549     if (pUM->iscsiInfo.signature != 0)
550     {
551         BnxeLogInfo(pUM, "MBA FCoE boot occurred on this interface.");
552     }
553 
554     if (!BnxeIntrInit(pUM))
555     {
556         BnxeBarMemDestroy(pUM);
557         BnxeWorkQueueWaitAndDestroy(pUM);
558         BnxeMutexDestroy(pUM);
559         BnxePciDestroy(pUM);
560         BnxeFmFini(pUM);
561 
562         return B_FALSE;
563     }
564 
565     if (!BnxeKstatInit(pUM))
566     {
567         BnxeIntrFini(pUM);
568         BnxeBarMemDestroy(pUM);
569         BnxeWorkQueueWaitAndDestroy(pUM);
570         BnxeMutexDestroy(pUM);
571         BnxePciDestroy(pUM);
572         BnxeFmFini(pUM);
573 
574         return B_FALSE;
575     }
576 
577     if (BnxeProtoFcoeAfex(pUM))
578     {
579         /* No support for L2 on FCoE enabled AFEX function! */
580         BnxeLogInfo(pUM, "FCoE AFEX function, not registering with GLD.");
581 #if 0
582         /*
583          * The following is wonky. Doing a CLONE_DEV makes it visible to
584          * various L2 networking commands even though the instance was
585          * not registered with GLDv3 via mac_register().
586          */
587 
588         /* Create a style-2 DLPI device */
589         if (ddi_create_minor_node(pUM->pDev,
590                                   (char *)ddi_driver_name(pUM->pDev),
591                                   S_IFCHR,
592                                   0,
593                                   DDI_PSEUDO, //DDI_NT_NET,
594                                   CLONE_DEV) != DDI_SUCCESS)
595         {
596             BnxeLogWarn(pUM, "Failed to create device minor node.");
597             BnxeKstatFini(pUM);
598             BnxeIntrFini(pUM);
599             BnxeBarMemDestroy(pUM);
600             BnxeWorkQueueWaitAndDestroy(pUM);
601             BnxeMutexDestroy(pUM);
602             BnxePciDestroy(pUM);
603             BnxeFmFini(pUM);
604 
605             return B_FALSE;
606         }
607 
608         /* Create a style-1 DLPI device */
609         if (ddi_create_minor_node(pUM->pDev,
610                                   pUM->devName,
611                                   S_IFCHR,
612                                   pUM->instance,
613                                   DDI_PSEUDO, //DDI_NT_NET,
614                                   0) != DDI_SUCCESS)
615         {
616             BnxeLogWarn(pUM, "Failed to create device instance minor node.");
617             ddi_remove_minor_node(pUM->pDev, (char *)ddi_driver_name(pUM->pDev));
618             BnxeKstatFini(pUM);
619             BnxeIntrFini(pUM);
620             BnxeBarMemDestroy(pUM);
621             BnxeWorkQueueWaitAndDestroy(pUM);
622             BnxeMutexDestroy(pUM);
623             BnxePciDestroy(pUM);
624             BnxeFmFini(pUM);
625 
626             return B_FALSE;
627         }
628 #endif
629     }
630     else
631     {
632         /* register with the GLDv3 MAC layer */
633         if (!BnxeGldInit(pUM))
634         {
635             BnxeKstatFini(pUM);
636             BnxeIntrFini(pUM);
637             BnxeBarMemDestroy(pUM);
638             BnxeWorkQueueWaitAndDestroy(pUM);
639             BnxeMutexDestroy(pUM);
640             BnxePciDestroy(pUM);
641             BnxeFmFini(pUM);
642 
643             return B_FALSE;
644         }
645     }
646 
647     snprintf(pUM->version,
648              sizeof(pUM->version),
649              "%d.%d.%d",
650              MAJVERSION,
651              MINVERSION,
652              REVVERSION);
653 
654     snprintf(pUM->versionLM,
655              sizeof(pUM->versionLM),
656              "%d.%d.%d",
657              LM_DRIVER_MAJOR_VER,
658              LM_DRIVER_MINOR_VER,
659              LM_DRIVER_FIX_NUM);
660 
661     snprintf(pUM->versionFW,
662              sizeof(pUM->versionFW),
663              "%d.%d.%d.%d",
664              BCM_5710_FW_MAJOR_VERSION,
665              BCM_5710_FW_MINOR_VERSION,
666              BCM_5710_FW_REVISION_VERSION,
667              BCM_5710_FW_ENGINEERING_VERSION);
668 
669     snprintf(pUM->versionBC,
670              sizeof(pUM->versionBC),
671              "%d.%d.%d",
672              ((pUM->lm_dev.hw_info.bc_rev >> 24) & 0xff),
673              ((pUM->lm_dev.hw_info.bc_rev >> 16) & 0xff),
674              ((pUM->lm_dev.hw_info.bc_rev >>  8) & 0xff));
675 
676     snprintf(pUM->chipName,
677              sizeof(pUM->chipName),
678              "%s",
679              BnxeChipName(pUM));
680 
681     snprintf(pUM->chipID,
682              sizeof(pUM->chipID),
683              "0x%x",
684              pUM->lm_dev.hw_info.chip_id);
685 
686     *pUM->bus_dev_func = 0;
687 	rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, pUM->pDev,
688                                    0, "reg", &props, &numProps);
689 	if ((rc == DDI_PROP_SUCCESS) && (numProps > 0))
690     {
691         snprintf(pUM->bus_dev_func,
692                  sizeof(pUM->bus_dev_func),
693                  "%04x:%02x:%02x",
694                  PCI_REG_BUS_G(props[0]),
695                  PCI_REG_DEV_G(props[0]),
696                  PCI_REG_FUNC_G(props[0]));
697 		ddi_prop_free(props);
698 	}
699 
700     vendor_id = 0;
701 	rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, pUM->pDev,
702                                    0, "vendor-id", &props, &numProps);
703 	if ((rc == DDI_PROP_SUCCESS) && (numProps > 0))
704     {
705         vendor_id = props[0];
706 		ddi_prop_free(props);
707 	}
708 
709     device_id = 0;
710 	rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, pUM->pDev,
711                                    0, "device-id", &props, &numProps);
712 	if ((rc == DDI_PROP_SUCCESS) && (numProps > 0))
713     {
714         device_id = props[0];
715 		ddi_prop_free(props);
716 	}
717 
718     snprintf(pUM->vendor_device,
719              sizeof(pUM->vendor_device),
720              "%04x:%04x",
721              vendor_id,
722              device_id);
723 
724     snprintf(pUM->intrAlloc,
725              sizeof(pUM->intrAlloc),
726              "%d %s",
727              (pUM->intrType == DDI_INTR_TYPE_FIXED) ? 1 : (pUM->defIntr.intrCount +
728                                                            pUM->fcoeIntr.intrCount +
729                                                            pUM->rssIntr.intrCount),
730              (pUM->intrType == DDI_INTR_TYPE_MSIX) ? "MSIX" :
731              (pUM->intrType == DDI_INTR_TYPE_MSI)  ? "MSI"  :
732                                                      "Fixed");
733 
734     BnxeLogInfo(pUM,
735                 "(0x%p) %s %s - v%s - FW v%s - BC v%s - %s (%s)",
736                 pUM,
737                 pUM->chipName,
738                 pUM->chipID,
739                 pUM->version,
740                 pUM->versionFW,
741                 pUM->versionBC,
742                 IS_MULTI_VNIC(&pUM->lm_dev) ? "MF" : "SF",
743                 pUM->intrAlloc);
744 
745     return B_TRUE;
746 }
747 
748 
749 static boolean_t BnxeDetachDevice(um_device_t * pUM)
750 {
751     int rc;
752 
753     rc = BnxeFcoeFini(pUM);
754 
755     if ((rc != 0) && (rc != ENOTSUP) && (rc != ENODEV))
756     {
757         return B_FALSE;
758     }
759 
760     if (BnxeProtoFcoeAfex(pUM))
761     {
762         /* No support for L2 on FCoE enabled AFEX function! */
763         ;
764 #if 0
765         ddi_remove_minor_node(pUM->pDev, pUM->devName);
766         ddi_remove_minor_node(pUM->pDev, (char *)ddi_driver_name(pUM->pDev));
767 #endif
768     }
769     else
770     {
771         if (!BnxeGldFini(pUM))
772         {
773             return B_FALSE;
774         }
775     }
776 
777     BnxeKstatFini(pUM);
778     BnxeIntrFini(pUM);
779     BnxeBarMemDestroy(pUM);
780     BnxeWorkQueueWaitAndDestroy(pUM);
781     BnxeMutexDestroy(pUM);
782     BnxePciDestroy(pUM);
783     BnxeFmFini(pUM);
784 
785     return B_TRUE;
786 }
787 
788 
789 static int BnxeAttach(dev_info_t * pDev, ddi_attach_cmd_t cmd)
790 {
791     um_device_t * pUM;
792 
793     switch (cmd)
794     {
795     case DDI_ATTACH:
796 
797         if ((pUM = kmem_zalloc(sizeof(um_device_t), KM_SLEEP)) == NULL)
798         {
799             BnxeLogWarn(NULL, "failed to allocate device structure");
800             return DDI_FAILURE;
801         }
802 
803         ddi_set_driver_private(pDev, pUM);
804 
805         /* set magic number for identification */
806         pUM->magic = BNXE_MAGIC;
807 
808         /* default for debug logging is dump everything */
809         pUM->devParams.debug_level = (CP_ALL | LV_MASK);
810 
811         /* save dev_info_t in the driver structure */
812         pUM->pDev = pDev;
813 
814         d_list_clear(&pUM->memBlockList);
815         d_list_clear(&pUM->memDmaList);
816         d_list_clear(&pUM->memRegionList);
817 #ifdef BNXE_DEBUG_DMA_LIST
818         d_list_clear(&pUM->memDmaListSaved);
819 #endif
820 
821         /* obtain a human-readable device name log messages with */
822         pUM->instance = ddi_get_instance(pDev);
823         snprintf(pUM->devName, sizeof(pUM->devName),
824                  "bnxe%d", pUM->instance);
825 
826         if (!BnxeAttachDevice(pUM))
827         {
828             kmem_free(pUM, sizeof(um_device_t));
829             return DDI_FAILURE;
830         }
831 
832         if (BNXE_FCOE(pUM) && pUM->devParams.fcoeEnable)
833         {
834             BnxeFcoeStartStop(pUM);
835         }
836 
837         return DDI_SUCCESS;
838 
839     case DDI_RESUME:
840 #if !(defined(__S11) || defined(__S12))
841     case DDI_PM_RESUME:
842 #endif
843 
844         pUM = (um_device_t *)ddi_get_driver_private(pDev);
845 
846         /* sanity check */
847         if (pUM == NULL || pUM->pDev != pDev)
848         {
849             BnxeLogWarn(NULL, "%s: dev_info_t match failed", __func__);
850             return DDI_FAILURE;
851         }
852 
853         if (BnxeHwResume(pUM) != 0)
854         {
855             BnxeLogWarn(pUM, "Fail to resume this device!");
856             return DDI_FAILURE;
857         }
858 
859         return DDI_SUCCESS;
860 
861     default:
862 
863         return DDI_FAILURE;
864     }
865 }
866 
867 
868 static int BnxeDetach(dev_info_t * pDev, ddi_detach_cmd_t cmd)
869 {
870     um_device_t * pUM;
871 
872     switch (cmd)
873     {
874     case DDI_DETACH:
875 
876         pUM = (um_device_t *)ddi_get_driver_private(pDev);
877 
878         /* sanity check */
879         if (pUM == NULL || pUM->pDev != pDev)
880         {
881             BnxeLogWarn(NULL, "%s: dev_info_t match failed", __func__);
882             return DDI_FAILURE;
883         }
884 
885         if (pUM->intrEnabled != B_FALSE)
886         {
887             BnxeLogWarn(pUM, "Detaching a device that is currently running!");
888             return DDI_FAILURE;
889         }
890 
891         if (!BnxeDetachDevice(pUM))
892         {
893             BnxeLogWarn(pUM, "Can't detach it now, please try again later!");
894             return DDI_FAILURE;
895         }
896 
897         kmem_free(pUM, sizeof(um_device_t));
898 
899         return DDI_SUCCESS;
900 
901     case DDI_SUSPEND:
902 #if !(defined(__S11) || defined(__S12))
903     case DDI_PM_SUSPEND:
904 #endif
905 
906         pUM = (um_device_t *)ddi_get_driver_private(pDev);
907 
908         /* sanity check */
909         if (pUM == NULL || pUM->pDev != pDev)
910         {
911             BnxeLogWarn(NULL, "%s: dev_info_t match failed", __func__);
912             return DDI_FAILURE;
913         }
914 
915         if (BnxeHwSuspend(pUM) != 0)
916         {
917             BnxeLogWarn(pUM, "Fail to suspend this device!");
918             return DDI_FAILURE;
919         }
920 
921         return DDI_SUCCESS;
922 
923     default:
924 
925         return DDI_FAILURE;
926     }
927 }
928 
929 
930 #if (DEVO_REV > 3)
931 
932 static int BnxeQuiesce(dev_info_t * pDev)
933 {
934     um_device_t * pUM;
935 
936     pUM = (um_device_t *)ddi_get_driver_private(pDev);
937 
938     /* sanity check */
939     if (pUM == NULL || pUM->pDev != pDev)
940     {
941         BnxeLogWarn(NULL, "%s: dev_info_t match failed", __func__);
942         return DDI_FAILURE;
943     }
944 
945     if (!pUM->plumbed)
946     {
947         return DDI_SUCCESS;
948     }
949 
950     if (BnxeHwQuiesce(pUM) != 0)
951     {
952         BnxeLogWarn(pUM, "Failed to quiesce the device!");
953         return DDI_FAILURE;
954     }
955 
956     return DDI_SUCCESS;
957 }
958 
959 #endif
960 
961 
962 void BnxeFcoeInitChild(dev_info_t * pDev,
963                        dev_info_t * cDip)
964 {
965     um_device_t *pUM = (um_device_t *) ddi_get_driver_private(pDev);
966 
967     if ((pUM == NULL) || (pUM->pDev != pDev))
968     {
969         BnxeLogWarn(NULL, "%s: dev_info_t match failed ", __func__);
970         return;
971     }
972 
973     ddi_set_name_addr(cDip, ddi_get_name_addr(pUM->pDev));
974 }
975 
976 
977 void BnxeFcoeUninitChild(dev_info_t * pDev,
978                          dev_info_t * cDip)
979 {
980 	ddi_set_name_addr(cDip, NULL);
981 }
982 
983 
984 static int BnxeBusCtl(dev_info_t *   pDev,
985                       dev_info_t *   pRDev,
986                       ddi_ctl_enum_t op,
987                       void *         pArg,
988                       void *         pResult)
989 {
990     um_device_t * pUM = (um_device_t *)ddi_get_driver_private(pDev);
991 
992     /* sanity check */
993     if (pUM == NULL || pUM->pDev != pDev)
994     {
995         BnxeLogWarn(NULL, "%s: dev_info_t match failed", __func__);
996         return DDI_FAILURE;
997     }
998 
999     BnxeLogDbg(pUM, "BnxeBusCtl (%d)", op);
1000 
1001     switch (op)
1002     {
1003     case DDI_CTLOPS_REPORTDEV:
1004     case DDI_CTLOPS_IOMIN:
1005         break;
1006     case DDI_CTLOPS_INITCHILD:
1007         BnxeFcoeInitChild(pDev, (dev_info_t *) pArg);
1008         break;
1009     case DDI_CTLOPS_UNINITCHILD:
1010         BnxeFcoeUninitChild(pDev, (dev_info_t *) pArg);
1011         break;
1012 
1013     default:
1014 
1015         return (ddi_ctlops(pDev, pRDev, op, pArg, pResult));
1016     }
1017 
1018     return DDI_SUCCESS;
1019 }
1020 
1021 
1022 static int BnxeCbIoctl(dev_t    dev,
1023                        int      cmd,
1024                        intptr_t arg,
1025                        int      mode,
1026                        cred_t * credp,
1027                        int *    rvalp)
1028 {
1029     BnxeBinding * pBinding = (BnxeBinding *)arg;
1030     um_device_t * pUM;
1031 
1032     (void)dev;
1033     (void)mode;
1034     (void)credp;
1035     (void)rvalp;
1036 
1037     if ((pBinding == NULL) ||
1038         (pBinding->pCliDev == NULL) ||
1039         (pBinding->pPrvDev == NULL))
1040     {
1041         BnxeLogWarn(NULL, "Invalid binding arg to ioctl %d", cmd);
1042         return DDI_FAILURE;
1043     }
1044 
1045     pUM = (um_device_t *)ddi_get_driver_private(pBinding->pPrvDev);
1046 
1047     /* sanity checks */
1048 
1049     if (pBinding->version != BNXE_BINDING_VERSION)
1050     {
1051         BnxeLogWarn(NULL, "%s: Invalid binding version (0x%08x)",
1052                     __func__, pBinding->version);
1053         return DDI_FAILURE;
1054     }
1055 
1056     if ((pUM == NULL) ||
1057         (pUM->fcoe.pDev != pBinding->pCliDev) ||
1058         (pUM->pDev != pBinding->pPrvDev))
1059     {
1060         BnxeLogWarn(NULL, "%s: dev_info_t match failed", __func__);
1061         return DDI_FAILURE;
1062     }
1063 
1064     switch (cmd)
1065     {
1066     case BNXE_BIND_FCOE:
1067 
1068         /* copy the binding struct and fill in the provider callback */
1069 
1070         BnxeLogInfo(pUM, "FCoE BIND start");
1071 
1072         if (!CLIENT_DEVI(pUM, LM_CLI_IDX_FCOE))
1073         {
1074             BnxeLogWarn(pUM, "FCoE BIND when DEVI is offline!");
1075             return DDI_FAILURE;
1076         }
1077 
1078         if (CLIENT_BOUND(pUM, LM_CLI_IDX_FCOE))
1079         {
1080             BnxeLogWarn(pUM, "FCoE BIND when alread bound!");
1081             return DDI_FAILURE;
1082         }
1083 
1084         pUM->fcoe.bind = *pBinding;
1085 
1086         pUM->fcoe.bind.prvCtl           = pBinding->prvCtl           = BnxeFcoePrvCtl;
1087         pUM->fcoe.bind.prvTx            = pBinding->prvTx            = BnxeFcoePrvTx;
1088         pUM->fcoe.bind.prvPoll          = pBinding->prvPoll          = BnxeFcoePrvPoll;
1089         pUM->fcoe.bind.prvSendWqes      = pBinding->prvSendWqes      = BnxeFcoePrvSendWqes;
1090         pUM->fcoe.bind.prvMapMailboxq   = pBinding->prvMapMailboxq   = BnxeFcoePrvMapMailboxq;
1091         pUM->fcoe.bind.prvUnmapMailboxq = pBinding->prvUnmapMailboxq = BnxeFcoePrvUnmapMailboxq;
1092 
1093         pUM->devParams.numRxDesc[LM_CLI_IDX_FCOE] = pBinding->numRxDescs;
1094         pUM->devParams.numTxDesc[LM_CLI_IDX_FCOE] = pBinding->numTxDescs;
1095 
1096         pUM->lm_dev.params.l2_rx_desc_cnt[LM_CLI_IDX_FCOE] = pBinding->numRxDescs;
1097         BnxeInitBdCnts(pUM, LM_CLI_IDX_FCOE);
1098 
1099         if (BnxeHwStartFCOE(pUM))
1100         {
1101             return DDI_FAILURE;
1102         }
1103 
1104         CLIENT_BIND_SET(pUM, LM_CLI_IDX_FCOE);
1105         lm_mcp_indicate_client_bind(&pUM->lm_dev, LM_CLI_IDX_FCOE);
1106 
1107         BnxeLogInfo(pUM, "FCoE BIND done");
1108         return DDI_SUCCESS;
1109 
1110     case BNXE_UNBIND_FCOE:
1111 
1112         /* clear the binding struct and stats */
1113 
1114         BnxeLogInfo(pUM, "FCoE UNBIND start");
1115 
1116         if (CLIENT_DEVI(pUM, LM_CLI_IDX_FCOE))
1117         {
1118             BnxeLogWarn(pUM, "FCoE UNBIND when DEVI is online!");
1119             return DDI_FAILURE;
1120         }
1121 
1122         if (!CLIENT_BOUND(pUM, LM_CLI_IDX_FCOE))
1123         {
1124             BnxeLogWarn(pUM, "FCoE UNBIND when not bound!");
1125             return DDI_FAILURE;
1126         }
1127 
1128         /* We must not detach until all packets held by fcoe are retrieved. */
1129         if (!BnxeWaitForPacketsFromClient(pUM, LM_CLI_IDX_FCOE))
1130         {
1131             return DDI_FAILURE;
1132         }
1133 
1134         lm_mcp_indicate_client_unbind(&pUM->lm_dev, LM_CLI_IDX_FCOE);
1135         CLIENT_BIND_RESET(pUM, LM_CLI_IDX_FCOE);
1136 
1137         BnxeHwStopFCOE(pUM);
1138 
1139         memset(&pUM->fcoe.bind, 0, sizeof(pUM->fcoe.bind));
1140         memset(&pUM->fcoe.stats, 0, sizeof(pUM->fcoe.stats));
1141 
1142         pBinding->prvCtl           = NULL;
1143         pBinding->prvTx            = NULL;
1144         pBinding->prvPoll          = NULL;
1145         pBinding->prvSendWqes      = NULL;
1146         pBinding->prvMapMailboxq   = NULL;
1147         pBinding->prvUnmapMailboxq = NULL;
1148 
1149         pUM->fcoe.pDev = NULL; /* sketchy? */
1150 
1151         BnxeLogInfo(pUM, "FCoE UNBIND done");
1152         return DDI_SUCCESS;
1153 
1154     default:
1155 
1156         BnxeLogWarn(pUM, "Unknown ioctl %d", cmd);
1157         return DDI_FAILURE;
1158     }
1159 }
1160 
1161 #ifndef ILLUMOS
1162 static struct bus_ops bnxe_bus_ops =
1163 {
1164     BUSO_REV,
1165     nullbusmap,        /* bus_map */
1166     NULL,              /* bus_get_intrspec */
1167     NULL,              /* bus_add_intrspec */
1168     NULL,              /* bus_remove_intrspec */
1169     i_ddi_map_fault,   /* bus_map_fault */
1170     ddi_dma_map,       /* bus_dma_map */
1171     ddi_dma_allochdl,  /* bus_dma_allochdl */
1172     ddi_dma_freehdl,   /* bus_dma_freehdl */
1173     ddi_dma_bindhdl,   /* bus_dma_bindhdl */
1174     ddi_dma_unbindhdl, /* bus_unbindhdl */
1175     ddi_dma_flush,     /* bus_dma_flush */
1176     ddi_dma_win,       /* bus_dma_win */
1177     ddi_dma_mctl,      /* bus_dma_ctl */
1178     BnxeBusCtl,        /* bus_ctl */
1179     ddi_bus_prop_op,   /* bus_prop_op */
1180     NULL,              /* bus_get_eventcookie */
1181     NULL,              /* bus_add_eventcall */
1182     NULL,              /* bus_remove_event */
1183     NULL,              /* bus_post_event */
1184     NULL,              /* bus_intr_ctl */
1185     NULL,              /* bus_config */
1186     NULL,              /* bus_unconfig */
1187     NULL,              /* bus_fm_init */
1188     NULL,              /* bus_fm_fini */
1189     NULL,              /* bus_fm_access_enter */
1190     NULL,              /* bus_fm_access_exit */
1191     NULL,              /* bus_power */
1192     NULL
1193 };
1194 #endif	/* ILLUMOS */
1195 
1196 
1197 static struct cb_ops bnxe_cb_ops =
1198 {
1199     nulldev,               /* cb_open */
1200     nulldev,               /* cb_close */
1201     nodev,                 /* cb_strategy */
1202     nodev,                 /* cb_print */
1203     nodev,                 /* cb_dump */
1204     nodev,                 /* cb_read */
1205     nodev,                 /* cb_write */
1206     BnxeCbIoctl,           /* cb_ioctl */
1207     nodev,                 /* cb_devmap */
1208     nodev,                 /* cb_mmap */
1209     nodev,                 /* cb_segmap */
1210     nochpoll,              /* cb_chpoll */
1211     ddi_prop_op,           /* cb_prop_op */
1212     NULL,                  /* cb_stream */
1213     (int)(D_MP | D_64BIT), /* cb_flag */
1214     CB_REV,                /* cb_rev */
1215     nodev,                 /* cb_aread */
1216     nodev,                 /* cb_awrite */
1217 };
1218 
1219 
1220 #if (DEVO_REV > 3)
1221 
1222 static struct dev_ops bnxe_dev_ops =
1223 {
1224     DEVO_REV,      /* devo_rev */
1225     0,             /* devo_refcnt */
1226     NULL,          /* devo_getinfo */
1227     nulldev,       /* devo_identify */
1228     nulldev,       /* devo_probe */
1229     BnxeAttach,    /* devo_attach */
1230     BnxeDetach,    /* devo_detach */
1231     nodev,         /* devo_reset */
1232     &bnxe_cb_ops,  /* devo_cb_ops */
1233 #ifndef	ILLUMOS
1234     &bnxe_bus_ops, /* devo_bus_ops */
1235 #else
1236     NULL,          /* devo_bus_ops */
1237 #endif
1238     NULL,          /* devo_power */
1239     BnxeQuiesce    /* devo_quiesce */
1240 };
1241 
1242 #else
1243 
1244 static struct dev_ops bnxe_dev_ops =
1245 {
1246     DEVO_REV,      /* devo_rev */
1247     0,             /* devo_refcnt */
1248     NULL,          /* devo_getinfo */
1249     nulldev,       /* devo_identify */
1250     nulldev,       /* devo_probe */
1251     BnxeAttach,    /* devo_attach */
1252     BnxeDetach,    /* devo_detach */
1253     nodev,         /* devo_reset */
1254     &bnxe_cb_ops,  /* devo_cb_ops */
1255     &bnxe_bus_ops, /* devo_bus_ops */
1256     NULL           /* devo_power */
1257 };
1258 
1259 #endif
1260 
1261 
1262 static struct modldrv bnxe_modldrv =
1263 {
1264     &mod_driverops,    /* drv_modops (must be mod_driverops for drivers) */
1265     BNXE_PRODUCT_INFO, /* drv_linkinfo (string displayed by modinfo) */
1266     &bnxe_dev_ops      /* drv_dev_ops */
1267 };
1268 
1269 
1270 static struct modlinkage bnxe_modlinkage =
1271 {
1272     MODREV_1,        /* ml_rev */
1273     {
1274       &bnxe_modldrv, /* ml_linkage */
1275       NULL           /* NULL termination */
1276     }
1277 };
1278 
1279 
1280 int _init(void)
1281 {
1282     int rc;
1283 
1284     mac_init_ops(&bnxe_dev_ops, "bnxe");
1285 
1286     /* Install module information with O/S */
1287     if ((rc = mod_install(&bnxe_modlinkage)) != DDI_SUCCESS)
1288     {
1289         BnxeLogWarn(NULL, "mod_install returned 0x%x", rc);
1290         mac_fini_ops(&bnxe_dev_ops);
1291         return rc;
1292     }
1293 
1294     mutex_init(&bnxeLoaderMutex, NULL, MUTEX_DRIVER, NULL);
1295     bnxeNumPlumbed = 0;
1296 
1297     BnxeLogInfo(NULL, "%s", BNXE_PRODUCT_BANNER);
1298 
1299     return rc;
1300 }
1301 
1302 
1303 int _fini(void)
1304 {
1305     int rc;
1306 
1307     if ((rc = mod_remove(&bnxe_modlinkage)) == DDI_SUCCESS)
1308     {
1309         mac_fini_ops(&bnxe_dev_ops);
1310         mutex_destroy(&bnxeLoaderMutex);
1311 
1312         if (bnxeNumPlumbed > 0)
1313         {
1314             /*
1315              * This shouldn't be possible since modunload must only call _fini
1316              * when no instances are currently plumbed.
1317              */
1318             BnxeLogWarn(NULL, "%d instances have not been unplumbed", bnxeNumPlumbed);
1319         }
1320     }
1321 
1322     return rc;
1323 }
1324 
1325 
1326 int _info(struct modinfo * pModinfo)
1327 {
1328     return mod_info(&bnxe_modlinkage, pModinfo);
1329 }
1330 
1331