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 2009 Emulex. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Source file containing the implementation of the driver entry points 29 * and related helper functions 30 */ 31 32 #include <oce_impl.h> 33 #include <oce_ioctl.h> 34 35 /* array of properties supported by this driver */ 36 mac_priv_prop_t oce_priv_props[] = { 37 {"_tx_ring_size", MAC_PROP_PERM_READ}, 38 {"_tx_bcopy_limit", MAC_PROP_PERM_RW}, 39 {"_rx_ring_size", MAC_PROP_PERM_READ}, 40 }; 41 uint32_t oce_num_props = sizeof (oce_priv_props) / sizeof (mac_priv_prop_t); 42 43 44 /* ---[ static function declarations ]----------------------------------- */ 45 static int oce_power10(int power); 46 static int oce_set_priv_prop(struct oce_dev *dev, const char *name, 47 uint_t size, const void *val); 48 49 static int oce_get_priv_prop(struct oce_dev *dev, const char *name, 50 uint_t flags, uint_t size, void *val); 51 52 /* ---[ GLD entry points ]----------------------------------------------- */ 53 int 54 oce_m_start(void *arg) 55 { 56 struct oce_dev *dev = arg; 57 int ret; 58 59 mutex_enter(&dev->dev_lock); 60 61 if (dev->state & STATE_MAC_STARTED) { 62 mutex_exit(&dev->dev_lock); 63 return (0); 64 } 65 66 if (dev->suspended) { 67 mutex_exit(&dev->dev_lock); 68 return (EIO); 69 } 70 71 if (oce_fm_check_acc_handle(dev, dev->db_handle)) { 72 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 73 mutex_exit(&dev->dev_lock); 74 return (EIO); 75 } 76 77 if (oce_fm_check_acc_handle(dev, dev->csr_handle)) { 78 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 79 mutex_exit(&dev->dev_lock); 80 return (EIO); 81 } 82 83 if (oce_fm_check_acc_handle(dev, dev->cfg_handle)) { 84 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 85 mutex_exit(&dev->dev_lock); 86 return (EIO); 87 } 88 89 ret = oce_start(dev); 90 if (ret != DDI_SUCCESS) { 91 mutex_exit(&dev->dev_lock); 92 return (EIO); 93 } 94 95 dev->state |= STATE_MAC_STARTED; 96 mutex_exit(&dev->dev_lock); 97 98 /* enable interrupts */ 99 oce_ei(dev); 100 101 return (DDI_SUCCESS); 102 } 103 104 int 105 oce_start(struct oce_dev *dev) 106 { 107 int qidx = 0; 108 int ret; 109 110 ret = oce_hw_init(dev); 111 if (ret != DDI_SUCCESS) { 112 oce_log(dev, CE_WARN, MOD_CONFIG, 113 "Hardware initialization failed with %d", ret); 114 return (ret); 115 } 116 117 ret = oce_chip_hw_init(dev); 118 if (ret != DDI_SUCCESS) { 119 oce_log(dev, CE_WARN, MOD_CONFIG, 120 "Chip initialization failed: %d", ret); 121 oce_hw_fini(dev); 122 return (ret); 123 } 124 ret = oce_setup_handlers(dev); 125 if (ret != DDI_SUCCESS) { 126 oce_log(dev, CE_WARN, MOD_CONFIG, 127 "Interrupt handler setup failed with %d", ret); 128 oce_chip_hw_fini(dev); 129 oce_hw_fini(dev); 130 return (ret); 131 } 132 133 (void) oce_start_wq(dev->wq[0]); 134 (void) oce_start_rq(dev->rq[0]); 135 136 /* get link status */ 137 (void) oce_get_link_status(dev, &dev->link); 138 139 if (dev->link.mac_speed == PHY_LINK_SPEED_ZERO) { 140 oce_log(dev, CE_NOTE, MOD_CONFIG, 141 "LINK_DOWN: 0x%x", dev->link.mac_speed); 142 mac_link_update(dev->mac_handle, LINK_STATE_DOWN); 143 } else { 144 oce_log(dev, CE_NOTE, MOD_CONFIG, 145 "(f,s,d,pp)=(0x%x, 0x%x, 0x%x, 0x%x)", 146 dev->link.mac_fault, dev->link.mac_speed, 147 dev->link.mac_duplex, dev->link.physical_port); 148 mac_link_update(dev->mac_handle, LINK_STATE_UP); 149 } 150 /* arm the eqs */ 151 for (qidx = 0; qidx < dev->neqs; qidx++) { 152 oce_arm_eq(dev, dev->eq[qidx]->eq_id, 0, B_TRUE, B_FALSE); 153 } 154 155 /* update state */ 156 return (DDI_SUCCESS); 157 } /* oce_start */ 158 159 160 void 161 oce_m_stop(void *arg) 162 { 163 struct oce_dev *dev = arg; 164 165 /* disable interrupts */ 166 oce_di(dev); 167 168 mutex_enter(&dev->dev_lock); 169 170 dev->state |= STATE_MAC_STOPPING; 171 172 if (dev->suspended) { 173 mutex_exit(&dev->dev_lock); 174 return; 175 } 176 177 oce_stop(dev); 178 179 dev->state &= ~(STATE_MAC_STOPPING | STATE_MAC_STARTED); 180 181 mutex_exit(&dev->dev_lock); 182 } 183 184 void 185 oce_stop(struct oce_dev *dev) 186 { 187 /* complete the pending Tx */ 188 oce_stop_wq(dev->wq[0]); 189 190 oce_chip_hw_fini(dev); 191 192 OCE_MSDELAY(200); 193 oce_stop_mq(dev->mq); 194 oce_stop_rq(dev->rq[0]); 195 196 /* remove interrupt handlers */ 197 oce_remove_handler(dev); 198 /* release hw resources */ 199 oce_hw_fini(dev); 200 201 } /* oce_stop */ 202 203 int 204 oce_m_multicast(void *arg, boolean_t add, const uint8_t *mca) 205 { 206 207 struct oce_dev *dev = (struct oce_dev *)arg; 208 struct ether_addr *mca_drv_list; 209 struct ether_addr *mca_hw_list; 210 int new_mcnt = 0; 211 int ret; 212 int i; 213 214 /* check the address */ 215 if ((mca[0] & 0x1) == 0) { 216 return (EINVAL); 217 } 218 219 /* Allocate the local array for holding the addresses temporarily */ 220 mca_hw_list = kmem_zalloc(OCE_MAX_MCA * sizeof (struct ether_addr), 221 KM_NOSLEEP); 222 223 if (mca_hw_list == NULL) 224 return (ENOMEM); 225 226 mca_drv_list = &dev->multi_cast[0]; 227 if (add) { 228 /* check if we exceeded hw max supported */ 229 if (dev->num_mca >= OCE_MAX_MCA) { 230 return (ENOENT); 231 } 232 /* copy entire dev mca to the mbx */ 233 bcopy((void*)mca_drv_list, 234 (void *)mca_hw_list, 235 (dev->num_mca * sizeof (struct ether_addr))); 236 /* Append the new one to local list */ 237 bcopy(mca, &mca_hw_list[dev->num_mca], 238 sizeof (struct ether_addr)); 239 new_mcnt = dev->num_mca + 1; 240 241 } else { 242 struct ether_addr *hwlistp = mca_hw_list; 243 for (i = 0; i < dev->num_mca; i++) { 244 /* copy only if it does not match */ 245 if (bcmp((mca_drv_list + i), mca, ETHERADDRL)) { 246 bcopy(mca_drv_list + i, hwlistp, 247 ETHERADDRL); 248 hwlistp++; 249 } 250 } 251 new_mcnt = dev->num_mca - 1; 252 } 253 254 mutex_enter(&dev->dev_lock); 255 if (dev->suspended) { 256 mutex_exit(&dev->dev_lock); 257 goto finish; 258 } 259 mutex_exit(&dev->dev_lock); 260 261 ret = oce_set_multicast_table(dev, mca_hw_list, new_mcnt, B_FALSE); 262 if (ret != 0) { 263 kmem_free(mca_hw_list, 264 OCE_MAX_MCA * sizeof (struct ether_addr)); 265 return (EIO); 266 } 267 /* 268 * Copy the local structure to dev structure 269 */ 270 finish: 271 bcopy(mca_hw_list, mca_drv_list, 272 new_mcnt * sizeof (struct ether_addr)); 273 dev->num_mca = (uint16_t)new_mcnt; 274 kmem_free(mca_hw_list, OCE_MAX_MCA * sizeof (struct ether_addr)); 275 return (0); 276 } /* oce_m_multicast */ 277 278 int 279 oce_m_unicast(void *arg, const uint8_t *uca) 280 { 281 struct oce_dev *dev = arg; 282 int ret; 283 284 DEV_LOCK(dev); 285 if (dev->suspended) { 286 bcopy(uca, dev->unicast_addr, ETHERADDRL); 287 DEV_UNLOCK(dev); 288 return (DDI_SUCCESS); 289 } 290 DEV_UNLOCK(dev); 291 292 /* Delete previous one and add new one */ 293 ret = oce_del_mac(dev, &dev->pmac_id); 294 if (ret != DDI_SUCCESS) { 295 return (EIO); 296 } 297 298 /* Set the New MAC addr earlier is no longer valid */ 299 ret = oce_add_mac(dev, uca, &dev->pmac_id); 300 if (ret != DDI_SUCCESS) { 301 return (EIO); 302 } 303 return (ret); 304 } /* oce_m_unicast */ 305 306 mblk_t * 307 oce_m_send(void *arg, mblk_t *mp) 308 { 309 struct oce_dev *dev = arg; 310 mblk_t *nxt_pkt; 311 mblk_t *rmp = NULL; 312 313 DEV_LOCK(dev); 314 if (dev->suspended) { 315 DEV_UNLOCK(dev); 316 freemsg(mp); 317 return (NULL); 318 } 319 DEV_UNLOCK(dev); 320 321 while (mp != NULL) { 322 /* Save the Pointer since mp will be freed in case of copy */ 323 nxt_pkt = mp->b_next; 324 mp->b_next = NULL; 325 /* Hardcode wq since we have only one */ 326 rmp = oce_send_packet(dev->wq[0], mp); 327 if (rmp != NULL) { 328 /* restore the chain */ 329 rmp->b_next = nxt_pkt; 330 break; 331 } 332 mp = nxt_pkt; 333 } 334 return (rmp); 335 } /* oce_send */ 336 337 boolean_t 338 oce_m_getcap(void *arg, mac_capab_t cap, void *data) 339 { 340 struct oce_dev *dev = arg; 341 boolean_t ret = B_TRUE; 342 switch (cap) { 343 344 case MAC_CAPAB_HCKSUM: { 345 uint32_t *csum_flags = u32ptr(data); 346 *csum_flags = HCKSUM_ENABLE | 347 HCKSUM_INET_FULL_V4 | 348 HCKSUM_IPHDRCKSUM; 349 break; 350 } 351 case MAC_CAPAB_LSO: { 352 mac_capab_lso_t *mcap_lso = (mac_capab_lso_t *)data; 353 if (dev->lso_capable) { 354 mcap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4; 355 mcap_lso->lso_basic_tcp_ipv4.lso_max = OCE_LSO_MAX_SIZE; 356 } else { 357 ret = B_FALSE; 358 } 359 break; 360 } 361 default: 362 ret = B_FALSE; 363 break; 364 } 365 return (ret); 366 } /* oce_m_getcap */ 367 368 int 369 oce_m_setprop(void *arg, const char *name, mac_prop_id_t id, 370 uint_t size, const void *val) 371 { 372 struct oce_dev *dev = arg; 373 int ret = 0; 374 375 switch (id) { 376 case MAC_PROP_MTU: { 377 uint32_t mtu; 378 379 bcopy(val, &mtu, sizeof (uint32_t)); 380 381 if (dev->mtu == mtu) { 382 ret = 0; 383 break; 384 } 385 386 if (mtu != OCE_MIN_MTU && mtu != OCE_MAX_MTU) { 387 ret = EINVAL; 388 break; 389 } 390 391 ret = mac_maxsdu_update(dev->mac_handle, mtu); 392 if (0 == ret) { 393 dev->mtu = mtu; 394 break; 395 } 396 break; 397 } 398 399 case MAC_PROP_FLOWCTRL: { 400 link_flowctrl_t flowctrl; 401 uint32_t fc = 0; 402 403 bcopy(val, &flowctrl, sizeof (link_flowctrl_t)); 404 405 switch (flowctrl) { 406 case LINK_FLOWCTRL_NONE: 407 fc = 0; 408 break; 409 410 case LINK_FLOWCTRL_RX: 411 fc = OCE_FC_RX; 412 break; 413 414 case LINK_FLOWCTRL_TX: 415 fc = OCE_FC_TX; 416 break; 417 418 case LINK_FLOWCTRL_BI: 419 fc = OCE_FC_RX | OCE_FC_TX; 420 break; 421 default: 422 ret = EINVAL; 423 break; 424 } /* switch flowctrl */ 425 426 if (ret) 427 break; 428 429 if (fc == dev->flow_control) 430 break; 431 432 if (dev->suspended) { 433 dev->flow_control = fc; 434 break; 435 } 436 /* call to set flow control */ 437 ret = oce_set_flow_control(dev, fc); 438 /* store the new fc setting on success */ 439 if (ret == 0) { 440 dev->flow_control = fc; 441 } 442 break; 443 } 444 445 case MAC_PROP_PRIVATE: 446 ret = oce_set_priv_prop(dev, name, size, val); 447 break; 448 449 default: 450 ret = ENOTSUP; 451 break; 452 } /* switch id */ 453 454 return (ret); 455 } /* oce_m_setprop */ 456 457 int 458 oce_m_getprop(void *arg, const char *name, mac_prop_id_t id, 459 uint_t flags, uint_t size, void *val, uint_t *perm) 460 { 461 struct oce_dev *dev = arg; 462 uint32_t ret = 0; 463 464 switch (id) { 465 case MAC_PROP_AUTONEG: 466 case MAC_PROP_EN_AUTONEG: 467 case MAC_PROP_ADV_1000FDX_CAP: 468 case MAC_PROP_EN_1000FDX_CAP: 469 case MAC_PROP_ADV_1000HDX_CAP: 470 case MAC_PROP_EN_1000HDX_CAP: 471 case MAC_PROP_ADV_100FDX_CAP: 472 case MAC_PROP_EN_100FDX_CAP: 473 case MAC_PROP_ADV_100HDX_CAP: 474 case MAC_PROP_EN_100HDX_CAP: 475 case MAC_PROP_ADV_10FDX_CAP: 476 case MAC_PROP_EN_10FDX_CAP: 477 case MAC_PROP_ADV_10HDX_CAP: 478 case MAC_PROP_EN_10HDX_CAP: 479 case MAC_PROP_ADV_100T4_CAP: 480 case MAC_PROP_EN_100T4_CAP: { 481 *perm = MAC_PROP_PERM_READ; 482 *(uint8_t *)val = 0x0; 483 break; 484 } 485 486 case MAC_PROP_ADV_10GFDX_CAP: 487 case MAC_PROP_EN_10GFDX_CAP: { 488 *perm = MAC_PROP_PERM_READ; 489 *(uint8_t *)val = 0x01; 490 break; 491 } 492 493 case MAC_PROP_DUPLEX: { 494 if (size >= sizeof (link_duplex_t)) { 495 uint32_t *mode = (uint32_t *)val; 496 497 *perm = MAC_PROP_PERM_READ; 498 if (dev->state & STATE_MAC_STARTED) 499 *mode = LINK_DUPLEX_FULL; 500 else 501 *mode = LINK_DUPLEX_UNKNOWN; 502 503 } else 504 ret = EINVAL; 505 break; 506 } 507 508 case MAC_PROP_SPEED: { 509 if (size >= sizeof (uint64_t)) { 510 uint64_t *speed = (uint64_t *)val; 511 512 *perm = MAC_PROP_PERM_READ; 513 *speed = 0; 514 if ((dev->state & STATE_MAC_STARTED) && 515 (dev->link.mac_speed != 0)) { 516 *speed = 1000000ull * 517 oce_power10(dev->link.mac_speed); 518 } 519 } else 520 ret = EINVAL; 521 break; 522 } 523 524 case MAC_PROP_MTU: { 525 mac_propval_range_t range; 526 527 *perm = MAC_PROP_PERM_RW; 528 if (!(flags & MAC_PROP_POSSIBLE)) { 529 ret = ENOTSUP; 530 break; 531 } 532 range.mpr_count = 1; 533 range.mpr_type = MAC_PROPVAL_UINT32; 534 range.range_uint32[0].mpur_min = OCE_MIN_MTU; 535 range.range_uint32[0].mpur_max = OCE_MAX_MTU; 536 bcopy(&range, val, sizeof (mac_propval_range_t)); 537 break; 538 } 539 540 case MAC_PROP_FLOWCTRL: { 541 link_flowctrl_t *fc = (link_flowctrl_t *)val; 542 543 if (size < sizeof (link_flowctrl_t)) { 544 ret = EINVAL; 545 break; 546 } 547 548 if (size >= sizeof (link_flowctrl_t)) { 549 if (dev->flow_control & OCE_FC_TX && 550 dev->flow_control & OCE_FC_RX) 551 *fc = LINK_FLOWCTRL_BI; 552 else if (dev->flow_control == OCE_FC_TX) 553 *fc = LINK_FLOWCTRL_TX; 554 else if (dev->flow_control == OCE_FC_RX) 555 *fc = LINK_FLOWCTRL_RX; 556 else if (dev->flow_control == 0) 557 *fc = LINK_FLOWCTRL_NONE; 558 else 559 ret = EINVAL; 560 } 561 break; 562 } 563 564 case MAC_PROP_PRIVATE: { 565 ret = oce_get_priv_prop(dev, name, flags, size, val); 566 break; 567 } 568 default: 569 break; 570 } /* switch id */ 571 return (ret); 572 } /* oce_m_getprop */ 573 574 /* 575 * function to handle dlpi streams message from GLDv3 mac layer 576 */ 577 void 578 oce_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) 579 { 580 struct oce_dev *dev = arg; 581 struct iocblk *iocp; 582 int cmd; 583 uint32_t payload_length; 584 int ret; 585 586 iocp = (struct iocblk *)voidptr(mp->b_rptr); 587 iocp->ioc_error = 0; 588 cmd = iocp->ioc_cmd; 589 590 DEV_LOCK(dev); 591 if (dev->suspended) { 592 miocnak(wq, mp, 0, EINVAL); 593 DEV_UNLOCK(dev); 594 return; 595 } 596 DEV_UNLOCK(dev); 597 598 switch (cmd) { 599 600 case OCE_ISSUE_MBOX: { 601 ret = oce_issue_mbox(dev, wq, mp, &payload_length); 602 if (ret != 0) { 603 miocnak(wq, mp, payload_length, ret); 604 } else { 605 miocack(wq, mp, payload_length, 0); 606 } 607 break; 608 } 609 610 default: 611 miocnak(wq, mp, 0, ENOTSUP); 612 break; 613 } 614 } /* oce_m_ioctl */ 615 616 int 617 oce_m_promiscuous(void *arg, boolean_t enable) 618 { 619 struct oce_dev *dev = arg; 620 int ret = 0; 621 622 DEV_LOCK(dev); 623 624 if (dev->promisc == enable) { 625 DEV_UNLOCK(dev); 626 return (ret); 627 } 628 dev->promisc = enable; 629 630 if (dev->suspended) { 631 DEV_UNLOCK(dev); 632 return (ret); 633 } 634 635 DEV_UNLOCK(dev); 636 637 ret = oce_set_promiscuous(dev, enable); 638 639 return (ret); 640 } /* oce_m_promiscuous */ 641 642 static int 643 oce_power10(int power) 644 { 645 int ret = 1; 646 647 while (power) { 648 ret *= 10; 649 power--; 650 } 651 return (ret); 652 } 653 654 /* 655 * function to set a private property. 656 * Called from the set_prop GLD entry point 657 * 658 * dev - sofware handle to the device 659 * name - string containing the property name 660 * size - length of the string in name 661 * val - pointer to a location where the value to set is stored 662 * 663 * return EINVAL => invalid value in val 0 => success 664 */ 665 static int 666 oce_set_priv_prop(struct oce_dev *dev, const char *name, 667 uint_t size, const void *val) 668 { 669 int ret = ENOTSUP; 670 long result; 671 672 _NOTE(ARGUNUSED(size)); 673 674 if (NULL == val) { 675 ret = EINVAL; 676 return (ret); 677 } 678 679 if (strcmp(name, "_tx_bcopy_limit") == 0) { 680 (void) ddi_strtol(val, (char **)NULL, 0, &result); 681 if (result <= OCE_WQ_BUF_SIZE) { 682 if (result != dev->bcopy_limit) 683 dev->bcopy_limit = (uint32_t)result; 684 ret = 0; 685 } else { 686 ret = EINVAL; 687 } 688 } 689 690 return (ret); 691 } /* oce_set_priv_prop */ 692 693 /* 694 * function to get the value of a private property. Called from get_prop 695 * 696 * dev - software handle to the device 697 * name - string containing the property name 698 * flags - flags sent by the OS to get_prop 699 * size - length of the string contained name 700 * val - [OUT] pointer to the location where the result is returned 701 * 702 * return EINVAL => invalid request 0 => success 703 */ 704 static int 705 oce_get_priv_prop(struct oce_dev *dev, const char *name, 706 uint_t flags, uint_t size, void *val) 707 { 708 int ret = ENOTSUP; 709 int value; 710 boolean_t is_default = (flags & MAC_PROP_DEFAULT); 711 712 if (NULL == val) { 713 ret = EINVAL; 714 return (ret); 715 } 716 717 if (strcmp(name, "_tx_ring_size") == 0) { 718 value = is_default ? OCE_DEFAULT_TX_RING_SIZE : 719 dev->tx_ring_size; 720 ret = 0; 721 goto done; 722 } 723 724 if (strcmp(name, "_tx_bcopy_limit") == 0) { 725 value = dev->bcopy_limit; 726 ret = 0; 727 goto done; 728 } 729 730 if (strcmp(name, "_rx_ring_size") == 0) { 731 value = is_default ? OCE_DEFAULT_RX_RING_SIZE : 732 dev->rx_ring_size; 733 ret = 0; 734 goto done; 735 } 736 737 done: 738 if (ret != 0) { 739 (void) snprintf(val, size, "%d", value); 740 } 741 return (ret); 742 } /* oce_get_priv_prop */ 743