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
bnx_free_system_resources(um_device_t * const umdevice)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
bnx_attach_attach(um_device_t * umdevice)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, ®Size);
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
bnx_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
bnx_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
bnx_quiesce(dev_info_t * dip)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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)571 _info(struct modinfo *modinfop)
572 {
573 int rc;
574
575 rc = mod_info(&bnx_modlinkage, modinfop);
576
577 return (rc);
578 }
579