xref: /illumos-gate/usr/src/uts/common/io/bnx/bnxmod.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * Copyright 2014-2017 Cavium, Inc.
3  * The contents of this file are subject to the terms of the Common Development
4  * and Distribution License, v.1,  (the "License").
5  *
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the License at available
9  * at http://opensource.org/licenses/CDDL-1.0
10  *
11  * See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14 
15 /*
16  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
17  * Copyright (c) 2019, Joyent, Inc.
18  */
19 
20 #include "bnx.h"
21 #include "bnxgld.h"
22 #include "bnxhwi.h"
23 #include "bnxint.h"
24 #include "bnxtmr.h"
25 #include "bnxcfg.h"
26 
27 #define	BNX_PRODUCT_BANNER "QLogic 570x/571x Gigabit Ethernet Driver "\
28     BRCMVERSION
29 
30 #define	BNX_PRODUCT_INFO "QLogic 570x/571x GbE "\
31     BRCMVERSION
32 
33 ddi_device_acc_attr_t bnxAccessAttribBAR = {
34 	DDI_DEVICE_ATTR_V0,	/* devacc_attr_version */
35 	DDI_STRUCTURE_LE_ACC,	/* devacc_attr_endian_flags */
36 	DDI_STRICTORDER_ACC,	/* devacc_attr_dataorder */
37 	DDI_DEFAULT_ACC		/* devacc_attr_access */
38 };
39 
40 ddi_device_acc_attr_t bnxAccessAttribBUF = {
41 	DDI_DEVICE_ATTR_V0,	/* devacc_attr_version */
42 	DDI_NEVERSWAP_ACC,	/* devacc_attr_endian_flags */
43 	DDI_STRICTORDER_ACC,	/* devacc_attr_dataorder */
44 	DDI_DEFAULT_ACC		/* devacc_attr_access */
45 };
46 
47 
48 /*
49  * Name:    bnx_free_system_resources
50  *
51  * Input:   ptr to device structure
52  *
53  * Return:  void
54  *
55  * Description:
56  *          This function is called from detach() entry point to free most
57  *          resources held by this device instance.
58  */
59 static int
60 bnx_free_system_resources(um_device_t * const umdevice)
61 {
62 	if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_MINOR_NODE) {
63 		umdevice->os_param.active_resc_flag &= ~DRV_RESOURCE_MINOR_NODE;
64 #ifdef _USE_FRIENDLY_NAME
65 		ddi_remove_minor_node(umdevice->os_param.dip,
66 		    (char *)ddi_driver_name(umdevice->os_param.dip));
67 #else
68 		ddi_remove_minor_node(umdevice->os_param.dip,
69 		    ddi_get_name(umdevice->os_param.dip));
70 #endif
71 	}
72 
73 	if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_TIMER) {
74 		umdevice->os_param.active_resc_flag &=
75 		    ~DRV_RESOURCE_TIMER;
76 		bnx_timer_fini(umdevice);
77 	}
78 
79 	if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_GLD_REGISTER) {
80 		if (bnx_gld_fini(umdevice)) {
81 			/*
82 			 * FIXME -- If bnx_gld_fini() fails, we need to
83 			 * reactivate resources.
84 			 */
85 			return (-1);
86 		}
87 		umdevice->os_param.active_resc_flag &=
88 		    ~DRV_RESOURCE_GLD_REGISTER;
89 	}
90 
91 	if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_KSTAT) {
92 		umdevice->os_param.active_resc_flag &= ~DRV_RESOURCE_KSTAT;
93 		bnx_kstat_fini(umdevice);
94 	}
95 
96 	if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_HDWR_REGISTER) {
97 		umdevice->os_param.active_resc_flag &=
98 		    ~DRV_RESOURCE_HDWR_REGISTER;
99 		bnx_hdwr_fini(umdevice);
100 	}
101 
102 	if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_MUTEX) {
103 		umdevice->os_param.active_resc_flag &= ~DRV_RESOURCE_MUTEX;
104 		mutex_destroy(&umdevice->os_param.ind_mutex);
105 		mutex_destroy(&umdevice->os_param.phy_mutex);
106 		mutex_destroy(&umdevice->os_param.rcv_mutex);
107 	}
108 
109 	if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_INTR_1) {
110 		umdevice->os_param.active_resc_flag &= ~DRV_RESOURCE_INTR_1;
111 		bnxIntrFini(umdevice);
112 	}
113 
114 	if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_MAP_REGS) {
115 		umdevice->os_param.active_resc_flag &= ~DRV_RESOURCE_MAP_REGS;
116 		ddi_regs_map_free(&umdevice->os_param.reg_acc_handle);
117 		umdevice->lm_dev.vars.dmaRegAccHandle = NULL;
118 		umdevice->os_param.reg_acc_handle = NULL;
119 	}
120 
121 	if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_PCICFG_MAPPED) {
122 		umdevice->os_param.active_resc_flag &=
123 		    ~DRV_RESOURCE_PCICFG_MAPPED;
124 		pci_config_teardown(&umdevice->os_param.pci_cfg_handle);
125 	}
126 
127 	return (0);
128 }
129 
130 
131 
132 /*
133  * Name:    bnx_attach_attach
134  *
135  * Input:   ptr to dev_info_t
136  *
137  * Return:  DDI_SUCCESS or DDI_FAILURE.
138  *
139  * Description: This is the main code involving all important driver data struct
140  *		and device initialization stuff. This function allocates driver
141  *		soft state for this instance of the driver,  sets access up
142  *		attributes for the device, maps BAR register space, initializes
143  *		the hardware, determines interrupt pin, registers interrupt
144  *		service routine with the OS and initializes receive/transmit
145  *		mutex. After successful completion of above mentioned tasks,
146  *		the driver registers with the GLD and creates minor node in
147  *		the file system tree for this device.
148  */
149 static int
150 bnx_attach_attach(um_device_t *umdevice)
151 {
152 	int rc;
153 	int instance;
154 	unsigned int val;
155 	int chip_id;
156 	int device_id;
157 	int subdevice_id;
158 	off_t regSize;
159 
160 	dev_info_t *dip;
161 
162 	dip = umdevice->os_param.dip;
163 
164 	umdevice->os_param.active_resc_flag = 0;
165 
166 	rc = pci_config_setup(umdevice->os_param.dip,
167 	    &umdevice->os_param.pci_cfg_handle);
168 	if (rc != DDI_SUCCESS) {
169 		cmn_err(CE_WARN,
170 		    "%s: Failed to setup PCI configuration space accesses.\n",
171 		    umdevice->dev_name);
172 		goto error;
173 	}
174 
175 	umdevice->os_param.active_resc_flag |= DRV_RESOURCE_PCICFG_MAPPED;
176 
177 	rc = ddi_dev_regsize(dip, 1, &regSize);
178 	if (rc != DDI_SUCCESS) {
179 		cmn_err(CE_WARN, "%s: failed to determine register set size.",
180 		    umdevice->dev_name);
181 	}
182 
183 	/*
184 	 * Setup device memory mapping so that LM driver can start accessing it.
185 	 */
186 	rc = ddi_regs_map_setup(dip,
187 	    1, /* BAR */
188 	    &umdevice->os_param.regs_addr,
189 	    0, /* OFFSET */
190 	    regSize,
191 	    &bnxAccessAttribBAR,
192 	    &umdevice->os_param.reg_acc_handle);
193 	if (rc != DDI_SUCCESS) {
194 		cmn_err(CE_WARN,
195 		    "%s: Failed to memory map device.\n",
196 		    umdevice->dev_name);
197 		goto error;
198 	}
199 
200 	umdevice->os_param.active_resc_flag |= DRV_RESOURCE_MAP_REGS;
201 
202 	bnx_cfg_msix(umdevice);
203 
204 	if (bnxIntrInit(umdevice) != 0) {
205 		goto error;
206 	}
207 
208 	umdevice->os_param.active_resc_flag |= DRV_RESOURCE_INTR_1;
209 
210 	mutex_init(&umdevice->os_param.rcv_mutex, NULL,
211 	    MUTEX_DRIVER, DDI_INTR_PRI(umdevice->intrPriority));
212 	mutex_init(&umdevice->os_param.phy_mutex, NULL,
213 	    MUTEX_DRIVER, DDI_INTR_PRI(umdevice->intrPriority));
214 	mutex_init(&umdevice->os_param.ind_mutex, NULL,
215 	    MUTEX_DRIVER, DDI_INTR_PRI(umdevice->intrPriority));
216 
217 	umdevice->os_param.active_resc_flag |= DRV_RESOURCE_MUTEX;
218 
219 	/*
220 	 * Call lower module's initialization routines to initialize
221 	 * hardware and related components within BNX.
222 	 */
223 	if (bnx_hdwr_init(umdevice)) {
224 		goto error;
225 	}
226 
227 	umdevice->os_param.active_resc_flag |= DRV_RESOURCE_HDWR_REGISTER;
228 
229 	if (!bnx_kstat_init(umdevice)) {
230 		goto error;
231 	}
232 
233 	umdevice->os_param.active_resc_flag |= DRV_RESOURCE_KSTAT;
234 
235 	if (bnx_gld_init(umdevice)) {
236 		goto error;
237 	}
238 
239 	umdevice->os_param.active_resc_flag |= DRV_RESOURCE_GLD_REGISTER;
240 
241 	bnx_timer_init(umdevice);
242 
243 	umdevice->os_param.active_resc_flag |= DRV_RESOURCE_TIMER;
244 
245 	instance = ddi_get_instance(umdevice->os_param.dip);
246 
247 	/* Create a minor node entry in /devices . */
248 #ifdef _USE_FRIENDLY_NAME
249 	rc = ddi_create_minor_node(dip, (char *)ddi_driver_name(dip),
250 	    S_IFCHR, instance, DDI_PSEUDO, 0);
251 #else
252 	rc = ddi_create_minor_node(dip, ddi_get_name(dip),
253 	    S_IFCHR, instance, DDI_PSEUDO, 0);
254 #endif
255 	if (rc == DDI_FAILURE) {
256 		cmn_err(CE_WARN, "%s: Failed to create device minor node.\n",
257 		    umdevice->dev_name);
258 		goto error;
259 	}
260 
261 	umdevice->os_param.active_resc_flag |= DRV_RESOURCE_MINOR_NODE;
262 
263 	ddi_report_dev(dip);
264 
265 	device_id = pci_config_get16(umdevice->os_param.pci_cfg_handle,
266 	    0x2);
267 	subdevice_id = pci_config_get16(umdevice->os_param.pci_cfg_handle,
268 	    0x2e);
269 
270 	/*  Dip into PCI config space to determine if we have 5716's */
271 	if ((device_id == 0x163b) && (subdevice_id == 0x163b)) {
272 		chip_id = 0x5716;
273 	} else {
274 		chip_id = CHIP_NUM(&umdevice->lm_dev) >> 16;
275 	}
276 
277 	(void) snprintf(umdevice->version, sizeof (umdevice->version), "%s",
278 	    BRCMVERSION);
279 
280 	/* Get firmware version. */
281 	REG_RD_IND(&umdevice->lm_dev,
282 	    umdevice->lm_dev.hw_info.shmem_base +
283 	    OFFSETOF(shmem_region_t, dev_info.bc_rev), &val);
284 	umdevice->dev_var.fw_ver = (val & 0xFFFF0000) | ((val & 0xFF00) >> 8);
285 
286 	(void) snprintf(umdevice->versionFW, sizeof (umdevice->versionFW),
287 	    "0x%x", umdevice->dev_var.fw_ver);
288 
289 	(void) snprintf(umdevice->chipName, sizeof (umdevice->chipName),
290 	    "BCM%x", chip_id);
291 
292 	(void) snprintf(umdevice->intrAlloc, sizeof (umdevice->intrAlloc),
293 	    "1 %s", (umdevice->intrType == DDI_INTR_TYPE_MSIX) ? "MSIX" :
294 	    (umdevice->intrType == DDI_INTR_TYPE_MSI)  ? "MSI"  :
295 	    "Fixed");
296 
297 	cmn_err(CE_NOTE,
298 	    "!%s: (%s) BCM%x device with F/W Ver%x is initialized (%s)",
299 	    umdevice->dev_name, umdevice->version,
300 	    chip_id, umdevice->dev_var.fw_ver,
301 	    umdevice->intrAlloc);
302 
303 	return (0);
304 
305 error:
306 	(void) bnx_free_system_resources(umdevice);
307 
308 	return (-1);
309 }
310 
311 /*
312  * Name:    bnx_attach
313  *
314  * Input:   ptr to dev_info_t, command code for the task to be executed
315  *
316  * Return:  DDI_SUCCESS or DDI_FAILURE.
317  *
318  * Description:	OS determined module attach entry point.
319  */
320 static int
321 bnx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
322 {
323 	um_device_t *umdevice;
324 	int ret_val = DDI_SUCCESS;
325 
326 	switch (cmd) {
327 		case DDI_ATTACH:
328 			umdevice = kmem_zalloc(sizeof (um_device_t),
329 			    KM_NOSLEEP);
330 			if (umdevice == NULL) {
331 				cmn_err(CE_WARN, "%s: Failed to allocate "
332 				    "device memory.\n", __func__);
333 				ret_val = DDI_FAILURE;
334 				break;
335 			}
336 
337 			/* Save dev_info_t info in the driver struture. */
338 			umdevice->os_param.dip = dip;
339 
340 			/*
341 			 * Obtain a human-readable name to prepend all our
342 			 * messages with.
343 			 */
344 			umdevice->instance = ddi_get_instance(dip);
345 			(void) snprintf(umdevice->dev_name,
346 			    sizeof (umdevice->dev_name), "%s%d", "bnx",
347 			    umdevice->instance);
348 
349 			/*
350 			 * Set driver private pointer to per device structure
351 			 * ptr.
352 			 */
353 			ddi_set_driver_private(dip, (caddr_t)umdevice);
354 
355 			umdevice->magic = BNX_MAGIC;
356 
357 			if (bnx_attach_attach(umdevice)) {
358 				ddi_set_driver_private(dip, (caddr_t)NULL);
359 				kmem_free(umdevice, sizeof (um_device_t));
360 				ret_val = DDI_FAILURE;
361 			}
362 			break;
363 
364 		case DDI_RESUME:
365 			/* Retrieve our device structure. */
366 			umdevice = ddi_get_driver_private(dip);
367 			if (umdevice == NULL) {
368 				ret_val = DDI_FAILURE;
369 				break;
370 			}
371 			break;
372 
373 		default:
374 			ret_val = DDI_FAILURE;
375 			break;
376 	}
377 
378 	return (ret_val);
379 }
380 
381 /*
382  * Name:    bnx_detach
383  *
384  * Input:   ptr to dev_info_t, command code for the task to be executed
385  *
386  * Return:  DDI_SUCCESS or DDI_FAILURE.
387  *
388  * Description:	OS determined module detach entry point.
389  */
390 static int
391 bnx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
392 {
393 	um_device_t *umdevice;
394 	int ret_val = DDI_SUCCESS;
395 
396 	switch (cmd) {
397 		case DDI_DETACH:
398 			umdevice = ddi_get_driver_private(dip);
399 			if (umdevice == NULL) {
400 				/* Must have failed attach. */
401 				ret_val = DDI_SUCCESS;
402 				break;
403 			}
404 
405 			/* Sanity check. */
406 			if (umdevice == NULL) {
407 				cmn_err(CE_WARN,
408 				    "%s: Sanity check failed(1).", __func__);
409 				ret_val = DDI_SUCCESS;
410 				break;
411 			}
412 
413 			/* Sanity check. */
414 			if (umdevice->os_param.dip != dip) {
415 				cmn_err(CE_WARN,
416 				    "%s: Sanity check failed(2).", __func__);
417 				ret_val = DDI_SUCCESS;
418 				break;
419 			}
420 
421 			/* Another sanity check. */
422 			if (umdevice->intr_enabled != B_FALSE) {
423 				cmn_err(CE_WARN, "%s: Detaching a device "
424 				    "that is currently running!!!\n",
425 				    umdevice->dev_name);
426 				ret_val = DDI_FAILURE;
427 				break;
428 			}
429 
430 			if (bnx_free_system_resources(umdevice)) {
431 				ret_val = DDI_FAILURE;
432 				break;
433 			}
434 
435 			ddi_set_driver_private(dip, (caddr_t)NULL);
436 			kmem_free(umdevice, sizeof (um_device_t));
437 			break;
438 
439 		case DDI_SUSPEND:
440 			/* Retrieve our device structure. */
441 			umdevice = ddi_get_driver_private(dip);
442 			if (umdevice == NULL) {
443 				ret_val = DDI_FAILURE;
444 				break;
445 			}
446 			break;
447 
448 		default:
449 			ret_val = DDI_FAILURE;
450 			break;
451 	}
452 
453 	return (ret_val);
454 }
455 
456 /*
457  * Name:    bnx_quiesce
458  *
459  * Input:   ptr to dev_info_t
460  *
461  * Return:  DDI_SUCCESS or DDI_FAILURE.
462  *
463  * Description: quiesce(9E) entry point.
464  *              This function will make sure no more interrupts and DMA of
465  *              the hardware. It is called when the system is single-threaded
466  *              at high PIL with preemption disabled. Thus this function should
467  *              not be blocked.
468  */
469 static int
470 bnx_quiesce(dev_info_t *dip)
471 {
472 	um_device_t *umdevice;
473 
474 	umdevice = ddi_get_driver_private(dip);
475 
476 	/* Sanity check. */
477 	if (umdevice == NULL || umdevice->os_param.dip != dip) {
478 		cmn_err(CE_WARN, "%s: Sanity check failed.", __func__);
479 		return (DDI_FAILURE);
480 	}
481 
482 	/* Stop the device from generating any interrupts. */
483 	lm_disable_int(&(umdevice->lm_dev));
484 
485 	/* Set RX mask to stop receiving any further packets */
486 	(void) lm_set_rx_mask(&(umdevice->lm_dev), RX_FILTER_USER_IDX0,
487 	    LM_RX_MASK_ACCEPT_NONE);
488 
489 	return (DDI_SUCCESS);
490 }
491 
492 DDI_DEFINE_STREAM_OPS(bnx_dev_ops, nulldev, nulldev, bnx_attach, bnx_detach, \
493     nodev, NULL, (D_MP | D_64BIT), NULL, bnx_quiesce);
494 
495 static struct modldrv bnx_modldrv = {
496 	&mod_driverops,		/* drv_modops */
497 	BNX_PRODUCT_INFO,	/* drv_linkinfo */
498 	&bnx_dev_ops		/* drv_dev_ops */
499 };
500 
501 static struct modlinkage bnx_modlinkage = {
502 	MODREV_1,	/* ml_rev */
503 	&bnx_modldrv,	/* ml_linkage */
504 	NULL		/* NULL termination */
505 };
506 
507 /*
508  * Name:        _init
509  *
510  * Input:       None
511  *
512  * Return:      SUCCESS or FAILURE.
513  *
514  * Description: OS determined driver module load entry point.
515  */
516 int
517 _init(void)
518 {
519 	int rc;
520 
521 	mac_init_ops(&bnx_dev_ops, "bnx");
522 
523 	/* Install module information with O/S */
524 	rc = mod_install(&bnx_modlinkage);
525 	if (rc != 0) {
526 		cmn_err(CE_WARN, "%s:_init - mod_install returned 0x%x", "bnx",
527 		    rc);
528 		return (rc);
529 	}
530 
531 	cmn_err(CE_NOTE, "!%s", BNX_PRODUCT_BANNER);
532 
533 	return (rc);
534 }
535 
536 
537 
538 /*
539  * Name:        _fini
540  *
541  * Input:       None
542  *
543  * Return:      SUCCESS or FAILURE.
544  *
545  * Description: OS determined driver module unload entry point.
546  */
547 int
548 _fini(void)
549 {
550 	int rc;
551 
552 	rc = mod_remove(&bnx_modlinkage);
553 
554 	if (rc == 0) {
555 		mac_fini_ops(&bnx_dev_ops);
556 	}
557 
558 	return (rc);
559 }
560 
561 /*
562  * Name:        _info
563  *
564  * Input:       None
565  *
566  * Return:      SUCCESS or FAILURE.
567  *
568  * Description: OS determined module info entry point.
569  */
570 int
571 _info(struct modinfo *modinfop)
572 {
573 	int rc;
574 
575 	rc = mod_info(&bnx_modlinkage, modinfop);
576 
577 	return (rc);
578 }
579