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
33ddi_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
40ddi_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 */
59static int
60bnx_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 */
149static int
150bnx_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
305error:
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 */
320static int
321bnx_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 */
390static int
391bnx_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 */
469static int
470bnx_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
492DDI_DEFINE_STREAM_OPS(bnx_dev_ops, nulldev, nulldev, bnx_attach, bnx_detach, \
493    nodev, NULL, (D_MP | D_64BIT), NULL, bnx_quiesce);
494
495static struct modldrv bnx_modldrv = {
496	&mod_driverops,		/* drv_modops */
497	BNX_PRODUCT_INFO,	/* drv_linkinfo */
498	&bnx_dev_ops		/* drv_dev_ops */
499};
500
501static 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 */
516int
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 */
547int
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 */
570int
571_info(struct modinfo *modinfop)
572{
573	int rc;
574
575	rc = mod_info(&bnx_modlinkage, modinfop);
576
577	return (rc);
578}
579