sw_an.c (31769B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * SW AN thread is an optional feature to support 7 * consortium AN mode on devices using PM4x25 D0. THis module is responsible for 8 * driving the Page exchange An state machine as recomennded by IEEE802.3by/MSA spec 9 * Once the page exchange is completed and speed is resolved HW will take over and complete 10 * the Autoneg. 11 */ 12 13 #include <shared/bsl.h> 14 #ifdef SW_AUTONEG_SUPPORT 15 16 #include <sal/types.h> 17 #include <shared/alloc.h> 18 #include <sal/core/spl.h> 19 #include <soc/drv.h> 20 21 #include <bcm/error.h> 22 #include <bcm/port.h> 23 #include <bcm_int/esw/port.h> 24 #include <bcm_int/common/sw_an.h> 25 26 #include <soc/phy/phymod_ctrl.h> 27 #include <soc/phy/phyctrl.h> 28 29 #include <sal/appl/io.h> 30 31 #ifdef PORTMOD_SUPPORT 32 #include <soc/portmod/portmod.h> 33 #include <soc/portmod/portmod_common.h> 34 #include <soc/esw/portctrl.h> 35 #include <bcm_int/esw/portctrl.h> 36 37 #define PORT_MAX_PHY_ACCESS_STRUCTURES (6) 38 #endif 39 40 /* 41 * SW AN Port context 42 */ 43 44 typedef struct _sw_an_port_ctxt_s { 45 bcm_port_sw_an_event_t an_event; 46 phymod_sw_an_ctxt_t phymod_sw_an_ctxt; 47 int an_retry; 48 uint32_t an_state; 49 #ifdef PORTMOD_SUPPORT 50 phymod_phy_access_t phy_access[PORT_MAX_PHY_ACCESS_STRUCTURES]; 51 #endif 52 } _sw_an_port_ctxt_t; 53 54 /* 55 * AW AN module context structure 56 */ 57 typedef struct _sw_autoneg_ctxt_s { 58 int enable; /* enable SW AN Thread */ 59 sal_mutex_t lock; /* lock to access the port bitmap */ 60 sal_sem_t signal_event; /* semaphore to signal new event to AN thread */ 61 sal_thread_t thread_id; /* Therad id */ 62 char thread_name[SAL_THREAD_NAME_MAX_LEN]; 63 pbmp_t add_ports; /* bitmap of ports that are part of the SW AN */ 64 pbmp_t process_ports; /* list of ports currently serviced by SW AN */ 65 _sw_an_port_ctxt_t port_sw_an_ctxt[SOC_MAX_NUM_PORTS]; /* per port SW AN context */ 66 uint32_t polling_interval; /* AN thread polling intervakl in msec */ 67 } _sw_autoneg_ctxt_t; 68 69 /* static alocation of SW AN CTXT for all units */ 70 _sw_autoneg_ctxt_t *_sw_autoneg_ctxt[BCM_LOCAL_UNITS_MAX]; 71 72 #define SW_AN_CTXT(unit) (_sw_autoneg_ctxt[unit]) 73 74 typedef enum _sw_an_page_exchange_sm_cmd_e { 75 SW_AN_PAGE_EXCHANGE_SM_CMD_STOP = 0, 76 SW_AN_PAGE_EXCHANGE_SM_CMD_START, 77 SW_AN_PAGE_EXCHANGE_SM_CMD_MAX 78 } _sw_an_page_exchange_sm_cmd_t; 79 80 /* 81 * Define: 82 * SW_AN_LOCK/SW_AN_UNLOCK 83 * Purpose: 84 * Serialization Macros for access to _sw_autoneg_ctxt_t structure. 85 */ 86 87 #define SW_AN_LOCK(unit) \ 88 sal_mutex_take(_sw_autoneg_ctxt[unit]->lock, sal_mutex_FOREVER) 89 90 #define SW_AN_UNLOCK(unit) \ 91 sal_mutex_give(_sw_autoneg_ctxt[unit]->lock) 92 93 #define SW_AN_WAIT_FOR_EVENT(unit) \ 94 sal_sem_take(_sw_autoneg_ctxt[unit]->signal_event, sal_sem_FOREVER) 95 96 #define SW_AN_POST_EVENT(unit) \ 97 sal_sem_give(_sw_autoneg_ctxt[unit]->signal_event) 98 99 #define UNIT_VALID_CHECK(unit) \ 100 if (((unit) < 0) || ((unit) >= BCM_LOCAL_UNITS_MAX)) { return BCM_E_UNIT; } 101 102 #define UNIT_INIT_CHECK(unit) \ 103 do { \ 104 UNIT_VALID_CHECK(unit); \ 105 if (_sw_autoneg_ctxt[unit] == NULL) { return BCM_E_INIT; } \ 106 } while (0) 107 108 109 #define SW_AN_THREAD_PRIORITY 50 /* 0 the highest and 255 the lowest*/ 110 #define SW_AN_NUM_PAGES_TO_TX 0x3 111 112 #define SW_AN_POLLING_INTERVAL 50 113 114 static int _bcm_sw_an_page_exchange_sm(int unit, bcm_port_t port, bcm_sw_an_fsm_event_t event); 115 int _bcm_sw_an_event_get(int unit, bcm_port_t port, bcm_sw_an_fsm_event_t *event); 116 phymod_phy_access_t * _bcm_sw_an_phy_access_get(int unit, bcm_port_t port); 117 118 int 119 bcm_sw_an_module_deinit(int unit) 120 { 121 _sw_autoneg_ctxt_t *sw_an_ctxt; 122 123 UNIT_VALID_CHECK(unit); 124 125 sw_an_ctxt = SW_AN_CTXT(unit); 126 127 if (sw_an_ctxt == NULL) { 128 return BCM_E_NONE; 129 } 130 131 if (sw_an_ctxt->signal_event != NULL) { 132 sal_sem_destroy(sw_an_ctxt->signal_event); 133 sw_an_ctxt->signal_event = NULL; 134 } 135 136 if (sw_an_ctxt->lock != NULL) { 137 sal_mutex_destroy(sw_an_ctxt->lock); 138 } 139 140 /* sal_thread_destroy is supported only with netbsd 141 * thread is should be NULL once we reach this 142 * function. Incase if we delete a thread while its holding lock 143 * we may have unpredicatble behaviour in non linux machines 144 */ 145 if (sw_an_ctxt->thread_id != NULL) { 146 sal_thread_destroy(sw_an_ctxt->thread_id); 147 } 148 sal_free(sw_an_ctxt); 149 150 SW_AN_CTXT(unit) = NULL; 151 152 return BCM_E_NONE; 153 } 154 155 int 156 bcm_sw_an_module_init(int unit) 157 { 158 _sw_autoneg_ctxt_t *sw_an_ctxt; 159 uint32 size; 160 uint32 polling_interval; 161 int rv = BCM_E_NONE; 162 163 164 UNIT_VALID_CHECK(unit); 165 166 if (SW_AN_CTXT(unit) != NULL) { 167 /* free the allocated resource */ 168 rv = bcm_sw_an_module_deinit(unit); 169 if (rv < BCM_E_NONE) { 170 return rv; 171 } 172 } 173 174 size = sizeof(_sw_autoneg_ctxt_t); 175 if ((sw_an_ctxt = sal_alloc(size, "sw_an_module")) == NULL) { 176 return BCM_E_MEMORY; 177 } 178 sal_memset(sw_an_ctxt, 0, size); 179 180 /* initalize the mutex and semaphore */ 181 sw_an_ctxt->lock = sal_mutex_create("SW_AN_LOCK"); 182 if (sw_an_ctxt->lock == NULL) { 183 rv = BCM_E_MEMORY; 184 } 185 186 if (BCM_SUCCESS(rv)) { 187 sw_an_ctxt->signal_event = sal_sem_create("SW_AN_signal_event", 188 sal_sem_BINARY, 189 0); 190 if (sw_an_ctxt->signal_event == NULL) { 191 sal_mutex_destroy(sw_an_ctxt->lock); 192 rv = BCM_E_MEMORY; 193 } 194 } 195 196 if (BCM_FAILURE(rv)) { 197 sal_free(sw_an_ctxt); 198 } 199 200 /* initialize the port bitmap */ 201 BCM_PBMP_CLEAR(sw_an_ctxt->add_ports); 202 BCM_PBMP_CLEAR(sw_an_ctxt->process_ports); 203 204 sw_an_ctxt->thread_id = NULL; 205 206 polling_interval = SW_AN_POLLING_INTERVAL; 207 polling_interval = soc_property_get(unit, spn_SW_AUTONEG_POLLING_INTERVAL, 208 polling_interval); 209 210 sw_an_ctxt->polling_interval = polling_interval; 211 212 _sw_autoneg_ctxt[unit] = sw_an_ctxt; 213 214 return BCM_E_NONE; 215 } 216 217 /* Run the through each port in the bit map and if they are 218 * scheduled to run service that port 219 */ 220 STATIC void 221 _bcm_sw_an_thread(int unit) 222 { 223 _sw_autoneg_ctxt_t *sw_an_ctxt; 224 _sw_an_port_ctxt_t *port_an_ctxt; 225 bcm_sw_an_fsm_event_t event; 226 bcm_port_t port; 227 int rv = BCM_E_NONE; 228 int is_process = 0; 229 230 sw_an_ctxt = SW_AN_CTXT(unit); 231 if (sw_an_ctxt == NULL) { 232 sal_thread_exit(0); 233 return; 234 } 235 236 sw_an_ctxt->thread_id = sal_thread_self(); 237 238 while(sw_an_ctxt->enable) { 239 SW_AN_LOCK(unit); 240 SOC_PBMP_ASSIGN(sw_an_ctxt->process_ports, 241 sw_an_ctxt->add_ports); 242 SW_AN_UNLOCK(unit); 243 /* run through the port bitmap */ 244 do 245 { 246 is_process = 0; 247 PBMP_ITER(sw_an_ctxt->process_ports, port) { 248 /* check if the port needs to be serviced */ 249 port_an_ctxt = &(sw_an_ctxt->port_sw_an_ctxt[port]); 250 if (port_an_ctxt->an_event != BCM_PORT_SW_AN_EVENT_AN_STOP) { 251 is_process = 1; 252 switch(port_an_ctxt->an_event) { 253 case BCM_PORT_SW_AN_EVENT_AN_START: 254 PORT_LOCK(unit); 255 rv = _bcm_sw_an_page_exchange_sm(unit, port, BCM_FSM_SW_AN_EVT_START); 256 PORT_UNLOCK(unit); 257 if (rv < BCM_E_NONE) { 258 SW_AN_LOCK(unit); 259 port_an_ctxt->an_event = BCM_PORT_SW_AN_EVENT_AN_DOWN; 260 SW_AN_UNLOCK(unit); 261 /* log error */ 262 } 263 break; 264 case BCM_PORT_SW_AN_EVENT_AN_RESTART: 265 case BCM_PORT_SW_AN_EVENT_AN_LINK_DOWN: 266 /* update the an retry count */ 267 port_an_ctxt->an_retry++; 268 PORT_LOCK(unit); 269 rv = _bcm_sw_an_page_exchange_sm(unit, port, BCM_FSM_SW_AN_EVT_SEND_RESTART); 270 PORT_UNLOCK(unit); 271 if (rv < BCM_E_NONE) { 272 SW_AN_LOCK(unit); 273 port_an_ctxt->an_event = BCM_PORT_SW_AN_EVENT_AN_DOWN; 274 SW_AN_UNLOCK(unit); 275 /* log error */ 276 } 277 break; 278 case BCM_PORT_SW_AN_EVENT_AN_IN_PROCESS: 279 PORT_LOCK(unit); 280 _bcm_sw_an_event_get(unit, port, &event); 281 rv = _bcm_sw_an_page_exchange_sm(unit, port, event); 282 PORT_UNLOCK(unit); 283 if (rv < BCM_E_NONE) { 284 SW_AN_LOCK(unit); 285 port_an_ctxt->an_event = BCM_PORT_SW_AN_EVENT_AN_DOWN; 286 SW_AN_UNLOCK(unit); 287 /* log error */ 288 } 289 break; 290 case BCM_PORT_SW_AN_EVENT_AN_STOP: 291 break; 292 default: 293 break; 294 } 295 } 296 } 297 sal_msleep(sw_an_ctxt->polling_interval); 298 SW_AN_LOCK(unit); 299 SOC_PBMP_ASSIGN(sw_an_ctxt->process_ports, 300 sw_an_ctxt->add_ports); 301 SW_AN_UNLOCK(unit); 302 } while(is_process); 303 304 /* wait on a semaphore */ 305 SW_AN_WAIT_FOR_EVENT(unit); 306 } 307 308 sw_an_ctxt->thread_id = NULL; 309 sal_thread_exit(0); 310 return; 311 } 312 313 314 315 int 316 bcm_sw_an_enable_set(int unit, int enable) 317 { 318 _sw_autoneg_ctxt_t *sw_an_ctxt = NULL; 319 int rv = BCM_E_NONE; 320 soc_timeout_t to; 321 sal_usecs_t wait_usec; 322 323 /* Time to wait for thread to start/end (longer for BCMSIM) */ 324 wait_usec = (SAL_BOOT_BCMSIM || SAL_BOOT_QUICKTURN) ? 30000000 : 10000000; 325 326 UNIT_INIT_CHECK(unit); 327 328 sw_an_ctxt = SW_AN_CTXT(unit); 329 330 if (enable) { 331 332 if (sw_an_ctxt->thread_id != NULL) { 333 /* wakeup the SW AN thread */ 334 SW_AN_POST_EVENT(unit); 335 return BCM_E_NONE; 336 } 337 338 sw_an_ctxt->enable = enable; 339 340 sal_snprintf(sw_an_ctxt->thread_name, 341 sizeof (sw_an_ctxt->thread_name), 342 "SW_AN.%0x", 343 unit); 344 345 if (sal_thread_create(sw_an_ctxt->thread_name, 346 SAL_THREAD_STKSZ, 347 SW_AN_THREAD_PRIORITY, 348 (void (*)(void*))_bcm_sw_an_thread, 349 INT_TO_PTR(unit)) == SAL_THREAD_ERROR) { 350 rv = BCM_E_MEMORY; 351 } else { 352 /* wait for the timeout period to expire to 353 * check if the thread creation is successful 354 */ 355 soc_timeout_init(&to, wait_usec, 0); 356 357 while(sw_an_ctxt->thread_id == NULL) { 358 if ((soc_timeout_check(&to))) { 359 LOG_ERROR(BSL_LS_BCM_PORT, 360 (BSL_META_U(unit, 361 "%s: Thread did not start\n"), 362 sw_an_ctxt->thread_name)); 363 rv = BCM_E_INTERNAL; 364 break; 365 } 366 } 367 368 } 369 } else { 370 /* set the enable flag to 0 and wait for the 371 * the thread to exit 372 */ 373 sw_an_ctxt->enable = enable; 374 375 } 376 return rv; 377 } 378 379 int 380 bcm_sw_an_enable_get(int unit, int *enable) 381 { 382 _sw_autoneg_ctxt_t *sw_an_ctxt = NULL; 383 384 UNIT_INIT_CHECK(unit); 385 386 sw_an_ctxt = SW_AN_CTXT(unit); 387 388 *enable = sw_an_ctxt->enable; 389 390 return BCM_E_NONE; 391 } 392 393 int 394 bcm_sw_an_post_event(int unit, bcm_port_t port, bcm_port_sw_an_event_t event) 395 { 396 _sw_autoneg_ctxt_t *sw_an_ctxt; 397 _sw_an_port_ctxt_t *port_an_ctxt; 398 399 UNIT_INIT_CHECK(unit); 400 401 if (event <= BCM_PORT_SW_AN_EVENT_NONE || 402 event >= BCM_PORT_SW_AN_EVENT_AN_MAX) { 403 /* nothing to notify the thread */ 404 return BCM_E_NONE; 405 } 406 407 sw_an_ctxt = SW_AN_CTXT(unit); 408 409 if (!SOC_PBMP_MEMBER(sw_an_ctxt->add_ports, port)) { 410 /* given port is not part of SW AN process thread 411 * nothing to notify 412 */ 413 return BCM_E_NONE; 414 } 415 416 port_an_ctxt = &(sw_an_ctxt->port_sw_an_ctxt[port]); 417 418 SW_AN_LOCK(unit); 419 port_an_ctxt->an_event = event; 420 SW_AN_UNLOCK(unit); 421 422 /* notify the SW AN thread */ 423 SW_AN_POST_EVENT(unit); 424 425 return BCM_E_NONE; 426 } 427 428 int 429 bcm_sw_an_port_register(int unit, bcm_port_t port) 430 { 431 _sw_autoneg_ctxt_t *sw_an_ctxt; 432 int enable = 0; 433 int rv = BCM_E_NONE; 434 435 UNIT_INIT_CHECK(unit); 436 437 sw_an_ctxt = SW_AN_CTXT(unit); 438 439 if (!SOC_PBMP_MEMBER(PBMP_PORT_ALL(unit), port) || 440 !SOC_PORT_VALID(unit, port) || !IS_PORT(unit, port)) { 441 return BCM_E_PORT; 442 } 443 444 /* if AN Thread is not enabled enable the AN thread */ 445 rv = bcm_sw_an_enable_get(unit, &enable); 446 if (rv != BCM_E_NONE) { 447 return rv; 448 } 449 450 if (!enable) { 451 rv = bcm_sw_an_enable_set(unit, 1); 452 if (rv != BCM_E_NONE) { 453 return rv; 454 } 455 } 456 457 if (!SOC_PBMP_MEMBER(sw_an_ctxt->add_ports, port)) { 458 SW_AN_LOCK(unit); 459 BCM_PBMP_PORT_ADD(sw_an_ctxt->add_ports, port); 460 SW_AN_UNLOCK(unit); 461 462 rv = bcm_sw_an_post_event(unit, port, BCM_PORT_SW_AN_EVENT_AN_START); 463 return rv; 464 } else { 465 /* SW AN is already enabled for this port restart SW AN */ 466 rv = bcm_sw_an_post_event(unit, port, BCM_PORT_SW_AN_EVENT_AN_RESTART); 467 } 468 469 return BCM_E_NONE; 470 } 471 472 int 473 bcm_sw_an_port_unregister(int unit, bcm_port_t port) 474 { 475 _sw_autoneg_ctxt_t *sw_an_ctxt; 476 phymod_phy_access_t *phy = NULL; 477 478 UNIT_INIT_CHECK(unit); 479 480 sw_an_ctxt = SW_AN_CTXT(unit); 481 482 if (!SOC_PBMP_MEMBER(sw_an_ctxt->add_ports, port) || 483 !SOC_PORT_VALID(unit, port) || !IS_PORT(unit, port)) { 484 return BCM_E_PORT; 485 } 486 487 SW_AN_LOCK(unit); 488 BCM_PBMP_PORT_REMOVE(sw_an_ctxt->add_ports, port); 489 SW_AN_UNLOCK(unit); 490 491 phy = _bcm_sw_an_phy_access_get(unit, port); 492 if (NULL == phy) { 493 return BCM_E_INTERNAL; 494 } 495 496 PHYMOD_IF_ERR_RETURN 497 (phymod_phy_sw_autoneg_enable(phy, 0)); 498 499 return BCM_E_NONE; 500 } 501 502 #ifdef PORTMOD_SUPPORT 503 phymod_phy_access_t * 504 _bcm_sw_an_portmod_phy_access_get(int unit, bcm_port_t port) { 505 _sw_autoneg_ctxt_t *sw_an_ctxt; 506 pm_info_t pm_info; 507 int chain_length = 0; 508 portmod_dispatch_type_t __type__; 509 int rv = 0; 510 511 512 sw_an_ctxt = SW_AN_CTXT(unit); 513 514 rv = portmod_port_pm_type_get(unit, port, &port, &__type__); 515 if (rv != BCM_E_NONE) { 516 return NULL; 517 } 518 519 rv = portmod_pm_info_get(unit, port, &pm_info); 520 if (rv != BCM_E_NONE) { 521 return NULL; 522 } 523 524 rv = portmod_port_chain_phy_access_get(unit, port, pm_info, sw_an_ctxt->port_sw_an_ctxt[port].phy_access, 525 PORT_MAX_PHY_ACCESS_STRUCTURES, 526 &chain_length); 527 528 if (rv != BCM_E_NONE) { 529 return NULL; 530 } 531 532 return &(sw_an_ctxt->port_sw_an_ctxt[port].phy_access[0]); 533 } 534 #endif 535 536 phymod_phy_access_t * 537 _bcm_sw_an_phy_access_get(int unit, bcm_port_t port) 538 { 539 phy_ctrl_t *pc; 540 soc_phymod_ctrl_t *pmc; 541 soc_phymod_phy_t *phy; 542 543 544 if (SOC_USE_PORTCTRL(unit)) { 545 return _bcm_sw_an_portmod_phy_access_get(unit, port); 546 } 547 548 /* locate phy control */ 549 pc = INT_PHY_SW_STATE(unit, port); 550 if (pc == NULL) { 551 return NULL; 552 } 553 554 pmc = &pc->phymod_ctrl; 555 556 /* only request autoneg on the first core */ 557 phy = pmc->phy[0]; 558 if (phy == NULL) { 559 return NULL; 560 } 561 562 return (&phy->pm_phy); 563 } 564 565 int 566 _bcm_sw_an_event_get(int unit, bcm_port_t port, bcm_sw_an_fsm_event_t *event) 567 { 568 phymod_sw_an_ctrl_status_t an_ctrl_status; 569 phymod_phy_access_t *phy = NULL; 570 571 phymod_sw_an_ctrl_status_t_init(&an_ctrl_status); 572 phy = _bcm_sw_an_phy_access_get(unit, port); 573 if (NULL == phy) { 574 return BCM_E_INTERNAL; 575 } 576 /* call the phymod API to get the event*/ 577 PHYMOD_IF_ERR_RETURN 578 (phymod_phy_sw_an_control_status_get(phy, &an_ctrl_status)); 579 580 *event = BCM_FSM_SW_AN_EVT_WAIT; 581 582 if (an_ctrl_status.seq_restart) { 583 /* not handled */ 584 } else if (an_ctrl_status.page_req) { 585 /* handled as part of lp_page_rdy*/ 586 } else if (an_ctrl_status.lp_page_rdy) { 587 *event = BCM_FSM_SW_AN_EVT_LP_PAGE_RDY; 588 } else if (an_ctrl_status.an_completed) { 589 *event = BCM_FSM_SW_AN_EVT_PAGE_EX_COMPLETE; 590 } else { 591 /* do nothing */ 592 } 593 return BCM_E_NONE; 594 } 595 596 int 597 _bcm_sw_an_page_exchange_sm(int unit, bcm_port_t port, bcm_sw_an_fsm_event_t event) 598 { 599 _sw_autoneg_ctxt_t *sw_an_ctxt; 600 _sw_an_port_ctxt_t *port_an_ctxt; 601 phymod_sw_an_ctxt_t *phymod_an_ctxt; 602 phymod_phy_access_t *phy; 603 int rv; 604 605 UNIT_INIT_CHECK(unit); 606 607 sw_an_ctxt = SW_AN_CTXT(unit); 608 rv = BCM_E_NONE; 609 610 phy = _bcm_sw_an_phy_access_get(unit, port); 611 if (NULL == phy) { 612 return BCM_E_INTERNAL; 613 } 614 615 port_an_ctxt = &(sw_an_ctxt->port_sw_an_ctxt[port]); 616 phymod_an_ctxt = &port_an_ctxt->phymod_sw_an_ctxt; 617 618 port_an_ctxt->an_state |= 0x1 << event; 619 620 switch(event) { 621 case BCM_FSM_SW_AN_EVT_START: 622 phymod_an_ctxt->tx_pages_cnt = 0; 623 phymod_an_ctxt->rx_pages_cnt = 0; 624 phymod_an_ctxt->no_pages_to_be_txed = SW_AN_NUM_PAGES_TO_TX; 625 PHYMOD_IF_ERR_RETURN 626 (phymod_phy_sw_an_base_page_exchange_handler(phy, phymod_an_ctxt)); 627 bcm_sw_an_post_event(unit, port, BCM_PORT_SW_AN_EVENT_AN_IN_PROCESS); 628 break; 629 case BCM_FSM_SW_AN_EVT_SEND_RESTART: 630 phymod_an_ctxt->tx_pages_cnt = 0; 631 phymod_an_ctxt->rx_pages_cnt = 0; 632 phymod_an_ctxt->no_pages_to_be_txed = SW_AN_NUM_PAGES_TO_TX; 633 /* Diable the SW AN to toggle SW AN bit */ 634 PHYMOD_IF_ERR_RETURN 635 (phymod_phy_sw_autoneg_enable(phy, 0)); 636 PHYMOD_IF_ERR_RETURN 637 (phymod_phy_sw_an_base_page_exchange_handler(phy, phymod_an_ctxt)); 638 bcm_sw_an_post_event(unit, port, BCM_PORT_SW_AN_EVENT_AN_IN_PROCESS); 639 break; 640 case BCM_FSM_SW_AN_EVT_LP_PAGE_RDY: 641 PHYMOD_IF_ERR_RETURN 642 (phymod_phy_sw_an_lp_page_rdy_handler(phy, phymod_an_ctxt)); 643 bcm_sw_an_post_event(unit, port, BCM_PORT_SW_AN_EVENT_AN_IN_PROCESS); 644 break; 645 case BCM_FSM_SW_AN_EVT_LD_PAGE: 646 /* do nothing */ 647 break; 648 case BCM_FSM_SW_AN_EVT_PAGE_EX_COMPLETE: 649 bcm_sw_an_post_event(unit, port, BCM_PORT_SW_AN_EVENT_AN_STOP); 650 break; 651 case BCM_FSM_SW_AN_EVT_WAIT: 652 bcm_sw_an_post_event(unit, port, BCM_PORT_SW_AN_EVENT_AN_IN_PROCESS); 653 break; 654 default: 655 break; 656 } 657 return rv; 658 } 659 660 int 661 bcm_sw_an_ability_advert_set(int unit, soc_port_t port, 662 soc_port_ability_t *ability) 663 { 664 int port_num_lanes; 665 bcm_port_if_t line_interface; 666 uint32_t an_tech_ability; 667 uint32_t an_bam37_ability; 668 uint32_t an_bam73_ability; 669 _shr_port_mode_t speed_full_duplex; 670 phymod_autoneg_ability_t phymod_autoneg_ability; 671 int rv = BCM_E_NONE; 672 uint8_t sw_an_mode = 0; 673 674 port_num_lanes = SOC_INFO(unit).port_num_lanes[port]; 675 rv = bcmi_esw_portctrl_interface_get(unit, port, &line_interface); 676 if (rv != BCM_E_NONE) { 677 return rv; 678 } 679 680 an_tech_ability = 0; 681 an_bam37_ability = 0; 682 an_bam73_ability = 0; 683 speed_full_duplex = ability->speed_full_duplex; 684 685 PHYMOD_IF_ERR_RETURN 686 (phymod_autoneg_ability_t_init(&phymod_autoneg_ability)); 687 688 sw_an_mode = soc_property_port_get(unit, port, 689 spn_PHY_AN_C73, sw_an_mode); 690 691 if (port_num_lanes == 4) { 692 if (speed_full_duplex & SOC_PA_SPEED_100GB) { 693 if (ability->medium & SOC_PA_MEDIUM_BACKPLANE) { 694 PHYMOD_AN_CAP_100G_KR4_SET(an_tech_ability); 695 } else if (ability->medium & SOC_PA_MEDIUM_COPPER) { 696 PHYMOD_AN_CAP_100G_CR4_SET(an_tech_ability); 697 } else { 698 PHYMOD_AN_CAP_100G_KR4_SET(an_tech_ability); 699 } 700 } 701 if (speed_full_duplex & SOC_PA_SPEED_40GB) { 702 if (ability->medium & SOC_PA_MEDIUM_BACKPLANE) { 703 PHYMOD_AN_CAP_40G_KR4_SET(an_tech_ability); 704 } else if (ability->medium & SOC_PA_MEDIUM_COPPER) { 705 PHYMOD_AN_CAP_40G_CR4_SET(an_tech_ability); 706 } else { 707 PHYMOD_AN_CAP_40G_KR4_SET(an_tech_ability); 708 } 709 } 710 } else if (port_num_lanes == 2) { 711 /* Supports only 50G in SW AN */ 712 if(speed_full_duplex & SOC_PA_SPEED_50GB) { 713 /* need to fix CR2 bit shift issue */ 714 if (ability->medium & SOC_PA_MEDIUM_BACKPLANE) { 715 PHYMOD_BAM_CL73_CAP_50G_KR2_SET(an_bam73_ability); 716 } else if (ability->medium & SOC_PA_MEDIUM_COPPER) { 717 PHYMOD_BAM_CL73_CAP_50G_CR2_SET(an_bam73_ability); 718 } else { 719 PHYMOD_BAM_CL73_CAP_50G_KR2_SET(an_bam73_ability); 720 } 721 } 722 } else { 723 if(speed_full_duplex & SOC_PA_SPEED_25GB) { 724 if (ability->medium & SOC_PA_MEDIUM_BACKPLANE) { 725 if (sw_an_mode != SW_AN_MODE_MSA_ONLY) { 726 if (ability->channel & SOC_PA_CHANNEL_SHORT) { 727 PHYMOD_AN_CAP_25G_KRS1_SET(an_tech_ability); 728 } else { 729 PHYMOD_AN_CAP_25G_KR1_SET(an_tech_ability); 730 } 731 } 732 PHYMOD_BAM_CL73_CAP_25G_KR1_SET(an_bam73_ability); 733 } else if (ability->medium & SOC_PA_MEDIUM_COPPER) { 734 if (sw_an_mode != SW_AN_MODE_MSA_ONLY) { 735 if (ability->channel & SOC_PA_CHANNEL_SHORT) { 736 PHYMOD_AN_CAP_25G_CRS1_SET(an_tech_ability); 737 } else { 738 PHYMOD_AN_CAP_25G_CR1_SET(an_tech_ability); 739 } 740 } 741 PHYMOD_BAM_CL73_CAP_25G_CR1_SET(an_bam73_ability); 742 } else { 743 if (sw_an_mode != SW_AN_MODE_MSA_ONLY) { 744 if (ability->channel & SOC_PA_CHANNEL_SHORT) { 745 PHYMOD_AN_CAP_25G_KRS1_SET(an_tech_ability); 746 } else { 747 PHYMOD_AN_CAP_25G_KR1_SET(an_tech_ability); 748 } 749 } 750 PHYMOD_BAM_CL73_CAP_25G_KR1_SET(an_bam73_ability); 751 } 752 } 753 if (speed_full_duplex & SOC_PA_SPEED_10GB) { 754 PHYMOD_AN_CAP_10G_KR_SET(an_tech_ability); 755 } 756 } 757 758 phymod_autoneg_ability.an_cap = an_tech_ability; 759 phymod_autoneg_ability.cl73bam_cap = an_bam73_ability; 760 phymod_autoneg_ability.cl37bam_cap = an_bam37_ability; 761 phymod_autoneg_ability.sgmii_speed = 0; 762 763 switch (ability->pause & (SOC_PA_PAUSE_TX | SOC_PA_PAUSE_RX)) { 764 case SOC_PA_PAUSE_TX: 765 PHYMOD_AN_CAP_ASYM_PAUSE_SET(&phymod_autoneg_ability); 766 break; 767 case SOC_PA_PAUSE_RX: 768 /* an_adv |= MII_ANA_C37_PAUSE | MII_ANA_C37_ASYM_PAUSE; */ 769 PHYMOD_AN_CAP_ASYM_PAUSE_SET(&phymod_autoneg_ability); 770 PHYMOD_AN_CAP_SYMM_PAUSE_SET(&phymod_autoneg_ability); 771 break; 772 case SOC_PA_PAUSE_TX | SOC_PA_PAUSE_RX: 773 PHYMOD_AN_CAP_SYMM_PAUSE_SET(&phymod_autoneg_ability); 774 break; 775 } 776 777 phymod_autoneg_ability.an_cl72 = 1; 778 779 780 /*next need to check FEC ability */ 781 if (ability->fec == SOC_PA_FEC_NONE) { 782 PHYMOD_AN_FEC_OFF_SET(phymod_autoneg_ability.an_fec); 783 } else { 784 if (ability->fec & SOC_PA_FEC_CL74) 785 PHYMOD_AN_FEC_CL74_SET(phymod_autoneg_ability.an_fec); 786 if (ability->fec & SOC_PA_FEC_CL91) 787 PHYMOD_AN_FEC_CL91_SET(phymod_autoneg_ability.an_fec); 788 } 789 790 rv = bcm_sw_an_advert_set(unit, port, &phymod_autoneg_ability); 791 792 return rv; 793 794 } 795 796 int 797 bcm_sw_an_advert_set(int unit, bcm_port_t port, phymod_autoneg_ability_t *ability) 798 { 799 _sw_autoneg_ctxt_t *sw_an_ctxt; 800 phymod_sw_an_ctxt_t *phymod_an_ctxt; 801 phymod_phy_access_t *phy; 802 uint8_t sw_an_mode = 0; 803 804 UNIT_INIT_CHECK(unit); 805 806 phy = _bcm_sw_an_phy_access_get(unit, port); 807 if (NULL == phy) { 808 return BCM_E_INTERNAL; 809 } 810 811 sw_an_ctxt = SW_AN_CTXT(unit); 812 phymod_an_ctxt = &(sw_an_ctxt->port_sw_an_ctxt[port].phymod_sw_an_ctxt); 813 814 sw_an_mode = soc_property_port_get(unit, port, 815 spn_PHY_AN_C73, sw_an_mode); 816 817 switch(sw_an_mode) { 818 case SW_AN_MODE_CL73_MSA: 819 SW_AN_LOCK(unit); 820 phymod_an_ctxt->an_mode = phymod_AN_MODE_CL73_MSA; 821 SW_AN_UNLOCK(unit); 822 break; 823 case SW_AN_MODE_MSA_ONLY: 824 SW_AN_LOCK(unit); 825 phymod_an_ctxt->an_mode = phymod_AN_MODE_MSA; 826 SW_AN_UNLOCK(unit); 827 break; 828 default: 829 /* Ideally we should not hit this case*/ 830 SW_AN_LOCK(unit); 831 phymod_an_ctxt->an_mode = phymod_AN_MODE_CL73_MSA; 832 SW_AN_UNLOCK(unit); 833 break; 834 } 835 836 PHYMOD_IF_ERR_RETURN 837 (phymod_phy_sw_an_advert_set(phy, ability, phymod_an_ctxt)); 838 839 return BCM_E_NONE; 840 } 841 842 int 843 _bcm_sw_an_ability_get(const phymod_an_pages_t *an_pages, soc_port_ability_t *portAbility) 844 { 845 uint16_t ability = 0, pause_cap = 0; 846 _shr_port_mode_t spd_fd = 0; 847 uint32_t msa_code_13_23 = 0, msa_code_2_12 = 0, msa_code_0_1 =0; 848 uint16_t msa_mode = 0; 849 850 portAbility->channel = SOC_PA_CHANNEL_LONG; 851 portAbility->medium = SOC_PA_MEDIUM_BACKPLANE; 852 portAbility->fec = 0; 853 portAbility->pause = 0; 854 855 /* Get PAUSE ability */ 856 ability = an_pages->base_page.page_0; 857 pause_cap = (ability >> SW_AN_BASE0_PAGE_CAP_OFFSET) & SW_AN_BASE0_PAGE_CAP_MASK; 858 if (pause_cap == 3) { 859 portAbility->pause = SOC_PA_PAUSE_RX; 860 } else if (pause_cap == 2) { 861 portAbility->pause = SOC_PA_PAUSE_TX; 862 } else if (pause_cap == 1) { 863 portAbility->pause = SOC_PA_PAUSE_TX | SOC_PA_PAUSE_RX; 864 } 865 866 /* Get CL73 speed ability */ 867 ability = an_pages->base_page.page_1; 868 if ((ability >> SW_AN_BASE_TECH_ABILITY_25GKR_OFFSET) & SW_AN_BASE_TECH_ABILITY_25GKR_MASK) { 869 spd_fd|= SOC_PA_SPEED_25GB; 870 } 871 if ((ability >> SW_AN_BASE_TECH_ABILITY_25GKRS_OFFSET) & SW_AN_BASE_TECH_ABILITY_25GKRS_MASK) { 872 spd_fd|= SOC_PA_SPEED_25GB; 873 portAbility->channel = SOC_PA_CHANNEL_SHORT; 874 } 875 if ((ability >> SW_AN_BASE_TECH_ABILITY_10GKR_OFFSET) & SW_AN_BASE_TECH_ABILITY_10GKR_MASK) { 876 spd_fd|= SOC_PA_SPEED_10GB; 877 } 878 879 /* Get CL73 FEC ability */ 880 ability = an_pages->base_page.page_2; 881 if ((ability >> SW_AN_BASE_CL91_ABILITY_REQ_OFFSET) & SW_AN_BASE_CL91_ABILITY_REQ_MASK) { 882 portAbility->fec |= SOC_PA_FEC_CL91; 883 } 884 if ((ability >> SW_AN_BASE_CL74_25GKRS_REQ_OFFSET) & SW_AN_BASE_CL74_25GKRS_REQ_MASK) { 885 portAbility->fec |= SOC_PA_FEC_CL74; 886 } 887 if ((ability >> SW_AN_BASE_CL74_ABILITY_REQ_OFFSET) & SW_AN_BASE_CL74_ABILITY_REQ_MASK) { 888 portAbility->fec |= SOC_PA_FEC_CL74; 889 } 890 891 msa_code_13_23 = ((an_pages->msg_page.page_1 >> SW_AN_MSG_PAGE1_OUI_13to23_OFFSET) & 892 SW_AN_MSG_PAGE1_OUI_13to23_MASK); 893 msa_code_2_12 = ((an_pages->msg_page.page_2 >> SW_AN_MSG_PAGE2_OUI_2to12_OFFSET) & 894 SW_AN_MSG_PAGE2_OUI_2to12_MASK); 895 msa_code_0_1 = ((an_pages->ufmt_page.page_0 >> SW_AN_UF_PAGE0_OUI_OFFSET) & 896 SW_AN_UF_PAGE0_OUI_MASK); 897 msa_mode = ((an_pages->base_page.page_0 >> SW_AN_BASE0_PAGE_NP_OFFSET) & SW_AN_BASE0_PAGE_NP_MASK); 898 899 /* Check MSA ability if applicable */ 900 if ((msa_mode) && (msa_code_0_1 == SW_AN_MSA_OUI_0to1) && 901 (msa_code_13_23 == SW_AN_MSA_OUI_13to23) && (msa_code_2_12 == SW_AN_MSA_OUI_2to12)) { 902 ability = an_pages->ufmt_page.page_1; 903 if ((ability >> SW_AN_UF_PAGE1_50G_KR2_ABILITY_OFFSET) & SW_AN_UF_PAGE1_50G_KR2_ABILITY_MASK) { 904 spd_fd|= SOC_PA_SPEED_50GB; 905 } 906 if ((ability >> SW_AN_UF_PAGE1_50G_CR2_ABILITY_OFFSET) & SW_AN_UF_PAGE1_50G_CR2_ABILITY_MASK) { 907 spd_fd|= SOC_PA_SPEED_50GB; 908 portAbility->medium = SOC_PA_MEDIUM_COPPER; 909 } 910 if ((ability >> SW_AN_UF_PAGE1_25G_KR1_ABILITY_OFFSET) & SW_AN_UF_PAGE1_25G_KR1_ABILITY_MASK) { 911 spd_fd|= SOC_PA_SPEED_25GB; 912 } 913 if ((ability >> SW_AN_UF_PAGE1_25G_CR1_ABILITY_OFFSET) & SW_AN_UF_PAGE1_25G_CR1_ABILITY_MASK) { 914 spd_fd|= SOC_PA_SPEED_25GB; 915 portAbility->medium = SOC_PA_MEDIUM_COPPER; 916 } 917 ability = an_pages->ufmt_page.page_2; 918 if ((ability >> SW_AN_UF_PAGE2_CL91_REQ_OFFSET) & SW_AN_UF_PAGE2_CL91_REQ_MASK) { 919 portAbility->fec |= SOC_PA_FEC_CL91; 920 } 921 if ((ability >> SW_AN_UF_PAGE2_CL74_REQ_OFFSET) & SW_AN_UF_PAGE2_CL74_REQ_MASK) { 922 portAbility->fec |= SOC_PA_FEC_CL74; 923 } 924 } 925 portAbility->speed_full_duplex = spd_fd; 926 927 /* If there's no FEC ability, set FEC_NONE */ 928 if (portAbility->fec == 0) { 929 portAbility->fec = SOC_PA_FEC_NONE; 930 } 931 932 return BCM_E_NONE; 933 } 934 935 936 int 937 bcm_sw_an_ability_advert_get(int unit, soc_port_t port, soc_port_ability_t *portAbility) 938 { 939 _sw_autoneg_ctxt_t *sw_an_ctxt; 940 phymod_sw_an_ctxt_t *phymod_an_ctxt; 941 phymod_an_pages_t *an_pages; 942 int rv = 0; 943 944 UNIT_INIT_CHECK(unit); 945 946 sw_an_ctxt = SW_AN_CTXT(unit); 947 phymod_an_ctxt = &(sw_an_ctxt->port_sw_an_ctxt[port].phymod_sw_an_ctxt); 948 an_pages = &phymod_an_ctxt->tx_pages; 949 950 rv = _bcm_sw_an_ability_get(an_pages, portAbility); 951 952 return rv; 953 } 954 955 int 956 bcm_sw_an_ability_remote_get(int unit, soc_port_t port, soc_port_ability_t *portAbility) 957 { 958 _sw_autoneg_ctxt_t *sw_an_ctxt; 959 phymod_sw_an_ctxt_t *phymod_an_ctxt; 960 phymod_an_pages_t *an_pages; 961 int rv = 0; 962 963 UNIT_INIT_CHECK(unit); 964 965 sw_an_ctxt = SW_AN_CTXT(unit); 966 phymod_an_ctxt = &(sw_an_ctxt->port_sw_an_ctxt[port].phymod_sw_an_ctxt); 967 an_pages = &phymod_an_ctxt->rx_pages; 968 969 rv = _bcm_sw_an_ability_get(an_pages, portAbility); 970 971 return rv; 972 } 973 974 int 975 bcm_sw_an_port_diag(int unit, int port) 976 { 977 _sw_autoneg_ctxt_t *sw_an_ctxt; 978 phymod_sw_an_ctxt_t *phymod_an_ctxt; 979 _sw_an_port_ctxt_t *port_an_ctxt; 980 981 UNIT_INIT_CHECK(unit); 982 983 sw_an_ctxt = SW_AN_CTXT(unit); 984 phymod_an_ctxt = &(sw_an_ctxt->port_sw_an_ctxt[port].phymod_sw_an_ctxt); 985 port_an_ctxt = &(sw_an_ctxt->port_sw_an_ctxt[port]); 986 987 sal_printf("********** SW AN CONTEXT INFORMATION FOR THE UNIT:%0x PORT:%0x **********\n", unit, port); 988 sal_printf("PORT SW AN STATE: \t %0x\n", port_an_ctxt->an_state); 989 sal_printf("PORT SW AN TX PAGES: \t %0x\n", phymod_an_ctxt->tx_pages_cnt); 990 sal_printf("PORT SW AN RX PAGES: \t %0x\n", phymod_an_ctxt->rx_pages_cnt); 991 992 sal_printf("PORT SW AN TX BASE PAGE: \t (48-32):%0x (31-16):%0x (15-0):%0x\n", 993 phymod_an_ctxt->tx_pages.base_page.page_2, phymod_an_ctxt->tx_pages.base_page.page_1, 994 phymod_an_ctxt->tx_pages.base_page.page_0); 995 sal_printf("PORT SW AN TX MSG PAGE: \t (48-32):%0x (31-16):%0x (15-0):%0x\n", 996 phymod_an_ctxt->tx_pages.msg_page.page_2, phymod_an_ctxt->tx_pages.msg_page.page_1, 997 phymod_an_ctxt->tx_pages.msg_page.page_0); 998 sal_printf("PORT SW AN TX UP PAGE: \t (48-32):%0x (31-16):%0x (15-0):%0x\n", 999 phymod_an_ctxt->tx_pages.ufmt_page.page_2, phymod_an_ctxt->tx_pages.ufmt_page.page_1, 1000 phymod_an_ctxt->tx_pages.ufmt_page.page_0); 1001 1002 sal_printf("PORT SW AN RX BASE PAGE: \t (48-32):%0x (31-16):%0x (15-0):%0x\n", 1003 phymod_an_ctxt->rx_pages.base_page.page_2, phymod_an_ctxt->rx_pages.base_page.page_1, 1004 phymod_an_ctxt->rx_pages.base_page.page_0); 1005 sal_printf("PORT SW AN RX MSG PAGE: \t (48-32):%0x (31-16):%0x (15-0):%0x\n", 1006 phymod_an_ctxt->rx_pages.msg_page.page_2, phymod_an_ctxt->rx_pages.msg_page.page_1, 1007 phymod_an_ctxt->rx_pages.msg_page.page_0); 1008 sal_printf("PORT SW AN RX UP PAGE: \t (48-32):%0x (31-16):%0x (15-0):%0x\n", 1009 phymod_an_ctxt->rx_pages.ufmt_page.page_2, phymod_an_ctxt->rx_pages.ufmt_page.page_1, 1010 phymod_an_ctxt->rx_pages.ufmt_page.page_0); 1011 1012 sal_printf("PORT SW AN EXT PAGE: \t (48-32):%0x (31-16):%0x (15-0):%0x\n", 1013 phymod_an_ctxt->tx_pages.null_page.page_2, phymod_an_ctxt->tx_pages.null_page.page_1, 1014 phymod_an_ctxt->tx_pages.null_page.page_0); 1015 sal_printf("PORT SW AN EXT PAGE: \t (48-32):%0x (31-16):%0x (15-0):%0x\n", 1016 phymod_an_ctxt->rx_pages.null_page.page_2, phymod_an_ctxt->rx_pages.null_page.page_1, 1017 phymod_an_ctxt->rx_pages.null_page.page_0); 1018 1019 return BCM_E_NONE; 1020 } 1021 #endif /* SW_AUTONEG_SUPPORT */ 1022