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, ®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 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