1 /*- 2 * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates 3 * All rights reserved. 4 * 5 * Developed by Semihalf. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include "al_init_eth_kr.h" 33 #include "al_serdes.h" 34 35 /** 36 * Ethernet 37 * @{ 38 * @file al_init_eth_kr.c 39 * 40 * @brief auto-negotiation and link training algorithms and state machines 41 * 42 * The link training algorithm implemented in this file going over the 43 * coefficients and looking for the best eye measurement possible for every one 44 * of them. it's using state machine to move between the different states. 45 * the state machine has 3 parts: 46 * - preparation - waiting till the link partner (lp) will be ready and 47 * change his state to preset. 48 * - measurement (per coefficient) - issue decrement for the coefficient 49 * under control till the eye measurement not increasing 50 * and remains in the optimum. 51 * - completion - indicate the receiver is ready and wait for the lp to 52 * finish his work. 53 */ 54 55 /* TODO: fix with more reasonable numbers */ 56 /* timeout in mSec before auto-negotiation will be terminated */ 57 #define AL_ETH_KR_AN_TIMEOUT (500) 58 #define AL_ETH_KR_EYE_MEASURE_TIMEOUT (100) 59 /* timeout in uSec before the process will be terminated */ 60 #define AL_ETH_KR_FRAME_LOCK_TIMEOUT (500 * 1000) 61 #define AL_ETH_KR_LT_DONE_TIMEOUT (500 * 1000) 62 /* number of times the receiver and transmitter tasks will be called before the 63 * algorithm will be terminated */ 64 #define AL_ETH_KR_LT_MAX_ROUNDS (50000) 65 66 /* mac algorithm state machine */ 67 enum al_eth_kr_mac_lt_state { 68 TX_INIT = 0, /* start of all */ 69 WAIT_BEGIN, /* wait for initial training lock */ 70 DO_PRESET, /* issue PRESET to link partner */ 71 DO_HOLD, /* issue HOLD to link partner */ 72 /* preparation is done, start testing the coefficient. */ 73 QMEASURE, /* EyeQ measurement. */ 74 QCHECK, /* Check if measurement shows best value. */ 75 DO_NEXT_TRY, /* issue DEC command to coeff for next measurement. */ 76 END_STEPS, /* perform last steps to go back to optimum. */ 77 END_STEPS_HOLD, /* perform last steps HOLD command. */ 78 COEFF_DONE, /* done with the current coefficient updates. 79 * Check if another should be done. */ 80 /* end of training to all coefficients */ 81 SET_READY, /* indicate local receiver ready */ 82 TX_DONE /* transmit process completed, training can end. */ 83 }; 84 85 static const char * const al_eth_kr_mac_sm_name[] = { 86 "TX_INIT", "WAIT_BEGIN", "DO_PRESET", 87 "DO_HOLD", "QMEASURE", "QCHECK", 88 "DO_NEXT_TRY", "END_STEPS", "END_STEPS_HOLD", 89 "COEFF_DONE", "SET_READY", "TX_DONE" 90 }; 91 92 /* Constants used for the measurement. */ 93 enum al_eth_kr_coef { 94 AL_ETH_KR_COEF_C_MINUS, 95 AL_ETH_KR_COEF_C_ZERO, 96 AL_ETH_KR_COEF_C_PLUS, 97 }; 98 99 /* 100 * test coefficients from COEFF_TO_MANIPULATE to COEFF_TO_MANIPULATE_LAST. 101 */ 102 #define COEFF_TO_MANIPULATE AL_ETH_KR_COEF_C_MINUS 103 #define COEFF_TO_MANIPULATE_LAST AL_ETH_KR_COEF_C_MINUS 104 #define QARRAY_SIZE 3 /**< how many entries we want in our history array. */ 105 106 struct al_eth_kr_data { 107 struct al_hal_eth_adapter *adapter; 108 struct al_serdes_grp_obj *serdes_obj; 109 enum al_serdes_lane lane; 110 111 /* Receiver side data */ 112 struct al_eth_kr_status_report_data status_report; /* report to response */ 113 struct al_eth_kr_coef_up_data last_lpcoeff; /* last coeff received */ 114 115 /* Transmitter side data */ 116 enum al_eth_kr_mac_lt_state algo_state; /* Statemachine. */ 117 unsigned int qarray[QARRAY_SIZE]; /* EyeQ measurements history */ 118 /* How many entries in the array are valid for compares yet. */ 119 unsigned int qarray_cnt; 120 enum al_eth_kr_coef curr_coeff; 121 /* 122 * Status of coefficient during the last 123 * DEC/INC command (before issuing HOLD again). 124 */ 125 unsigned int coeff_status_step; 126 unsigned int end_steps_cnt; /* Number of end steps needed */ 127 }; 128 129 static int 130 al_eth_kr_an_run(struct al_eth_kr_data *kr_data, struct al_eth_an_adv *an_adv, 131 struct al_eth_an_adv *an_partner_adv) 132 { 133 int rc; 134 boolean_t page_received = FALSE; 135 boolean_t an_completed = FALSE; 136 boolean_t error = FALSE; 137 int timeout = AL_ETH_KR_AN_TIMEOUT; 138 139 rc = al_eth_kr_an_init(kr_data->adapter, an_adv); 140 if (rc != 0) { 141 al_err("%s %s autonegotiation init failed\n", 142 kr_data->adapter->name, __func__); 143 return (rc); 144 } 145 146 rc = al_eth_kr_an_start(kr_data->adapter, AL_ETH_AN__LT_LANE_0, 147 FALSE, TRUE); 148 if (rc != 0) { 149 al_err("%s %s autonegotiation enable failed\n", 150 kr_data->adapter->name, __func__); 151 return (rc); 152 } 153 154 do { 155 DELAY(10000); 156 timeout -= 10; 157 if (timeout <= 0) { 158 al_info("%s %s autonegotiation failed on timeout\n", 159 kr_data->adapter->name, __func__); 160 161 return (ETIMEDOUT); 162 } 163 164 al_eth_kr_an_status_check(kr_data->adapter, &page_received, 165 &an_completed, &error); 166 } while (page_received == FALSE); 167 168 if (error != 0) { 169 al_info("%s %s autonegotiation failed (status error)\n", 170 kr_data->adapter->name, __func__); 171 172 return (EIO); 173 } 174 175 al_eth_kr_an_read_adv(kr_data->adapter, an_partner_adv); 176 177 al_dbg("%s %s autonegotiation completed. error = %d\n", 178 kr_data->adapter->name, __func__, error); 179 180 return (0); 181 } 182 183 /***************************** receiver side *********************************/ 184 static enum al_eth_kr_cl72_cstate 185 al_eth_lt_coeff_set(struct al_eth_kr_data *kr_data, 186 enum al_serdes_tx_deemph_param param, uint32_t op) 187 { 188 enum al_eth_kr_cl72_cstate status = 0; 189 190 switch (op) { 191 case AL_PHY_KR_COEF_UP_HOLD: 192 /* no need to update the serdes - return not updated*/ 193 status = C72_CSTATE_NOT_UPDATED; 194 break; 195 case AL_PHY_KR_COEF_UP_INC: 196 status = C72_CSTATE_UPDATED; 197 198 if (kr_data->serdes_obj->tx_deemph_inc( 199 kr_data->serdes_obj, 200 kr_data->lane, 201 param) == 0) 202 status = C72_CSTATE_MAX; 203 break; 204 case AL_PHY_KR_COEF_UP_DEC: 205 status = C72_CSTATE_UPDATED; 206 207 if (kr_data->serdes_obj->tx_deemph_dec( 208 kr_data->serdes_obj, 209 kr_data->lane, 210 param) == 0) 211 status = C72_CSTATE_MIN; 212 break; 213 default: /* 3=reserved */ 214 break; 215 } 216 217 return (status); 218 } 219 220 /* 221 * Inspect the received coefficient update request and update all coefficients 222 * in the serdes accordingly. 223 */ 224 static void 225 al_eth_coeff_req_handle(struct al_eth_kr_data *kr_data, 226 struct al_eth_kr_coef_up_data *lpcoeff) 227 { 228 struct al_eth_kr_status_report_data *report = &kr_data->status_report; 229 230 /* First check for Init and Preset commands. */ 231 if ((lpcoeff->preset != 0) || (lpcoeff->initialize) != 0) { 232 kr_data->serdes_obj->tx_deemph_preset( 233 kr_data->serdes_obj, 234 kr_data->lane); 235 236 /* 237 * in case of preset c(0) should be set to maximum and both c(1) 238 * and c(-1) should be updated 239 */ 240 report->c_minus = C72_CSTATE_UPDATED; 241 242 report->c_plus = C72_CSTATE_UPDATED; 243 244 report->c_zero = C72_CSTATE_MAX; 245 246 return; 247 } 248 249 /* 250 * in case preset and initialize are false need to perform per 251 * coefficient action. 252 */ 253 report->c_minus = al_eth_lt_coeff_set(kr_data, 254 AL_SERDES_TX_DEEMP_C_MINUS, lpcoeff->c_minus); 255 256 report->c_zero = al_eth_lt_coeff_set(kr_data, 257 AL_SERDES_TX_DEEMP_C_ZERO, lpcoeff->c_zero); 258 259 report->c_plus = al_eth_lt_coeff_set(kr_data, 260 AL_SERDES_TX_DEEMP_C_PLUS, lpcoeff->c_plus); 261 262 al_dbg("%s: c(0) = 0x%x c(-1) = 0x%x c(1) = 0x%x\n", 263 __func__, report->c_zero, report->c_plus, report->c_minus); 264 } 265 266 static void 267 al_eth_kr_lt_receiver_task_init(struct al_eth_kr_data *kr_data) 268 { 269 270 al_memset(&kr_data->last_lpcoeff, 0, 271 sizeof(struct al_eth_kr_coef_up_data)); 272 al_memset(&kr_data->status_report, 0, 273 sizeof(struct al_eth_kr_status_report_data)); 274 } 275 276 static boolean_t 277 al_eth_lp_coeff_up_change(struct al_eth_kr_data *kr_data, 278 struct al_eth_kr_coef_up_data *lpcoeff) 279 { 280 struct al_eth_kr_coef_up_data *last_lpcoeff = &kr_data->last_lpcoeff; 281 282 if (al_memcmp(last_lpcoeff, lpcoeff, 283 sizeof(struct al_eth_kr_coef_up_data)) == 0) { 284 return (FALSE); 285 } 286 287 al_memcpy(last_lpcoeff, lpcoeff, sizeof(struct al_eth_kr_coef_up_data)); 288 289 return (TRUE); 290 } 291 292 /* 293 * Run the receiver task for one cycle. 294 * The receiver task continuously inspects the received coefficient update 295 * requests and acts upon. 296 * 297 * @return <0 if error occur 298 */ 299 static int 300 al_eth_kr_lt_receiver_task_run(struct al_eth_kr_data *kr_data) 301 { 302 struct al_eth_kr_coef_up_data new_lpcoeff; 303 304 /* 305 * First inspect status of the link. It may have dropped frame lock as 306 * the remote did some reconfiguration of its serdes. 307 * Then we simply have nothing to do and return immediately as caller 308 * will call us continuously until lock comes back. 309 */ 310 311 if (al_eth_kr_receiver_frame_lock_get(kr_data->adapter, 312 AL_ETH_AN__LT_LANE_0) != 0) { 313 return (0); 314 } 315 316 /* check if a new update command was received */ 317 al_eth_lp_coeff_up_get(kr_data->adapter, 318 AL_ETH_AN__LT_LANE_0, &new_lpcoeff); 319 320 if (al_eth_lp_coeff_up_change(kr_data, &new_lpcoeff) != 0) { 321 /* got some new coefficient update request. */ 322 al_eth_coeff_req_handle(kr_data, &new_lpcoeff); 323 } 324 325 return (0); 326 } 327 328 /******************************** transmitter side ***************************/ 329 static int 330 al_eth_kr_lt_transmitter_task_init(struct al_eth_kr_data *kr_data) 331 { 332 int i; 333 int rc; 334 unsigned int temp_val; 335 336 for (i = 0; i < QARRAY_SIZE; i++) 337 kr_data->qarray[i] = 0; 338 339 kr_data->qarray_cnt = 0; 340 kr_data->algo_state = TX_INIT; 341 kr_data->curr_coeff = COEFF_TO_MANIPULATE; /* first coeff to test. */ 342 kr_data->coeff_status_step = C72_CSTATE_NOT_UPDATED; 343 kr_data->end_steps_cnt = QARRAY_SIZE-1; /* go back to first entry */ 344 345 /* 346 * Perform measure eye here to run the rx equalizer 347 * for the first time to get init values 348 */ 349 rc = kr_data->serdes_obj->eye_measure_run( 350 kr_data->serdes_obj, 351 kr_data->lane, 352 AL_ETH_KR_EYE_MEASURE_TIMEOUT, 353 &temp_val); 354 if (rc != 0) { 355 al_warn("%s: Failed to run Rx equalizer (rc = 0x%x)\n", 356 __func__, rc); 357 358 return (rc); 359 } 360 361 return (0); 362 } 363 364 static boolean_t 365 al_eth_kr_lt_all_not_updated(struct al_eth_kr_status_report_data *report) 366 { 367 368 if ((report->c_zero == C72_CSTATE_NOT_UPDATED) && 369 (report->c_minus == C72_CSTATE_NOT_UPDATED) && 370 (report->c_plus == C72_CSTATE_NOT_UPDATED)) { 371 return (TRUE); 372 } 373 374 return (FALSE); 375 } 376 377 static void 378 al_eth_kr_lt_coef_set(struct al_eth_kr_coef_up_data *ldcoeff, 379 enum al_eth_kr_coef coef, enum al_eth_kr_cl72_coef_op op) 380 { 381 382 switch (coef) { 383 case AL_ETH_KR_COEF_C_MINUS: 384 ldcoeff->c_minus = op; 385 break; 386 case AL_ETH_KR_COEF_C_PLUS: 387 ldcoeff->c_plus = op; 388 break; 389 case AL_ETH_KR_COEF_C_ZERO: 390 ldcoeff->c_zero = op; 391 break; 392 } 393 } 394 395 static enum al_eth_kr_cl72_cstate 396 al_eth_kr_lt_coef_report_get(struct al_eth_kr_status_report_data *report, 397 enum al_eth_kr_coef coef) 398 { 399 400 switch (coef) { 401 case AL_ETH_KR_COEF_C_MINUS: 402 return (report->c_minus); 403 case AL_ETH_KR_COEF_C_PLUS: 404 return (report->c_plus); 405 case AL_ETH_KR_COEF_C_ZERO: 406 return (report->c_zero); 407 } 408 409 return (0); 410 } 411 412 /* 413 * Run the transmitter_task for one cycle. 414 * 415 * @return <0 if error occurs 416 */ 417 static int 418 al_eth_kr_lt_transmitter_task_run(struct al_eth_kr_data *kr_data) 419 { 420 struct al_eth_kr_status_report_data report; 421 unsigned int coeff_status_cur; 422 struct al_eth_kr_coef_up_data ldcoeff = { 0, 0, 0, 0, 0 }; 423 unsigned int val; 424 int i; 425 enum al_eth_kr_mac_lt_state nextstate; 426 int rc = 0; 427 428 /* 429 * do nothing if currently there is no frame lock (which may happen 430 * when remote updates its analogs). 431 */ 432 if (al_eth_kr_receiver_frame_lock_get(kr_data->adapter, 433 AL_ETH_AN__LT_LANE_0) == 0) { 434 return (0); 435 } 436 437 al_eth_lp_status_report_get(kr_data->adapter, 438 AL_ETH_AN__LT_LANE_0, &report); 439 440 /* extract curr status of the coefficient in use */ 441 coeff_status_cur = al_eth_kr_lt_coef_report_get(&report, 442 kr_data->curr_coeff); 443 444 nextstate = kr_data->algo_state; /* default we stay in curr state; */ 445 446 switch (kr_data->algo_state) { 447 case TX_INIT: 448 /* waiting for start */ 449 if (al_eth_kr_startup_proto_prog_get(kr_data->adapter, 450 AL_ETH_AN__LT_LANE_0) != 0) { 451 /* training is on and frame lock */ 452 nextstate = WAIT_BEGIN; 453 } 454 break; 455 case WAIT_BEGIN: 456 kr_data->qarray_cnt = 0; 457 kr_data->curr_coeff = COEFF_TO_MANIPULATE; 458 kr_data->coeff_status_step = C72_CSTATE_NOT_UPDATED; 459 coeff_status_cur = C72_CSTATE_NOT_UPDATED; 460 kr_data->end_steps_cnt = QARRAY_SIZE-1; 461 462 /* Wait for not_updated for all coefficients from remote */ 463 if (al_eth_kr_lt_all_not_updated(&report) != 0) { 464 ldcoeff.preset = TRUE; 465 nextstate = DO_PRESET; 466 } 467 break; 468 case DO_PRESET: 469 /* 470 * Send PRESET and wait for updated for all 471 * coefficients from remote 472 */ 473 if (al_eth_kr_lt_all_not_updated(&report) == 0) 474 nextstate = DO_HOLD; 475 else /* as long as the lp didn't response to the preset 476 * we should continue sending it */ 477 ldcoeff.preset = TRUE; 478 break; 479 case DO_HOLD: 480 /* 481 * clear the PRESET, issue HOLD command and wait for 482 * hold handshake 483 */ 484 if (al_eth_kr_lt_all_not_updated(&report) != 0) 485 nextstate = QMEASURE; 486 break; 487 488 case QMEASURE: 489 /* makes a measurement and fills the new value into the array */ 490 rc = kr_data->serdes_obj->eye_measure_run( 491 kr_data->serdes_obj, 492 kr_data->lane, 493 AL_ETH_KR_EYE_MEASURE_TIMEOUT, 494 &val); 495 if (rc != 0) { 496 al_warn("%s: Rx eye measurement failed\n", __func__); 497 498 return (rc); 499 } 500 501 al_dbg("%s: Rx Measure eye returned 0x%x\n", __func__, val); 502 503 /* put the new value into the array at the top. */ 504 for (i = 0; i < QARRAY_SIZE-1; i++) 505 kr_data->qarray[i] = kr_data->qarray[i+1]; 506 507 kr_data->qarray[QARRAY_SIZE-1] = val; 508 509 if (kr_data->qarray_cnt < QARRAY_SIZE) 510 kr_data->qarray_cnt++; 511 512 nextstate = QCHECK; 513 break; 514 case QCHECK: 515 /* check if we reached the best link quality yet. */ 516 if (kr_data->qarray_cnt < QARRAY_SIZE) { 517 /* keep going until at least the history is 518 * filled. check that we can keep going or if 519 * coefficient has already reached minimum. 520 */ 521 522 if (kr_data->coeff_status_step == C72_CSTATE_MIN) 523 nextstate = COEFF_DONE; 524 else { 525 /* 526 * request a DECREMENT of the 527 * coefficient under control 528 */ 529 al_eth_kr_lt_coef_set(&ldcoeff, 530 kr_data->curr_coeff, AL_PHY_KR_COEF_UP_DEC); 531 532 nextstate = DO_NEXT_TRY; 533 } 534 } else { 535 /* 536 * check if current value and last both are worse than 537 * the 2nd last. This we take as an ending condition 538 * assuming the minimum was reached two tries before 539 * so we will now go back to that point. 540 */ 541 if ((kr_data->qarray[0] < kr_data->qarray[1]) && 542 (kr_data->qarray[0] < kr_data->qarray[2])) { 543 /* 544 * request a INCREMENT of the 545 * coefficient under control 546 */ 547 al_eth_kr_lt_coef_set(&ldcoeff, 548 kr_data->curr_coeff, AL_PHY_KR_COEF_UP_INC); 549 550 /* start going back to the maximum */ 551 nextstate = END_STEPS; 552 if (kr_data->end_steps_cnt > 0) 553 kr_data->end_steps_cnt--; 554 } else { 555 if (kr_data->coeff_status_step == 556 C72_CSTATE_MIN) { 557 nextstate = COEFF_DONE; 558 } else { 559 /* 560 * request a DECREMENT of the 561 * coefficient under control 562 */ 563 al_eth_kr_lt_coef_set(&ldcoeff, 564 kr_data->curr_coeff, 565 AL_PHY_KR_COEF_UP_DEC); 566 567 nextstate = DO_NEXT_TRY; 568 } 569 } 570 } 571 break; 572 case DO_NEXT_TRY: 573 /* 574 * save the status when we issue the DEC step to the remote, 575 * before the HOLD is done again. 576 */ 577 kr_data->coeff_status_step = coeff_status_cur; 578 579 if (coeff_status_cur != C72_CSTATE_NOT_UPDATED) 580 nextstate = DO_HOLD; /* go to next measurement round */ 581 else 582 al_eth_kr_lt_coef_set(&ldcoeff, 583 kr_data->curr_coeff, AL_PHY_KR_COEF_UP_DEC); 584 break; 585 /* 586 * Coefficient iteration completed, go back to the optimum step 587 * In this algorithm we assume 2 before curr was best hence need to do 588 * two INC runs. 589 */ 590 case END_STEPS: 591 if (coeff_status_cur != C72_CSTATE_NOT_UPDATED) 592 nextstate = END_STEPS_HOLD; 593 else 594 al_eth_kr_lt_coef_set(&ldcoeff, 595 kr_data->curr_coeff, AL_PHY_KR_COEF_UP_INC); 596 break; 597 case END_STEPS_HOLD: 598 if (coeff_status_cur == C72_CSTATE_NOT_UPDATED) { 599 if (kr_data->end_steps_cnt != 0) { 600 /* 601 * request a INCREMENT of the 602 * coefficient under control 603 */ 604 al_eth_kr_lt_coef_set(&ldcoeff, 605 kr_data->curr_coeff, AL_PHY_KR_COEF_UP_INC); 606 607 /* go 2nd time - dec the end step count */ 608 nextstate = END_STEPS; 609 610 if (kr_data->end_steps_cnt > 0) 611 kr_data->end_steps_cnt--; 612 613 } else { 614 nextstate = COEFF_DONE; 615 } 616 } 617 break; 618 case COEFF_DONE: 619 /* 620 * now this coefficient is done. 621 * We can now either choose to finish here, 622 * or keep going with another coefficient. 623 */ 624 if ((int)kr_data->curr_coeff < COEFF_TO_MANIPULATE_LAST) { 625 int i; 626 627 for (i = 0; i < QARRAY_SIZE; i++) 628 kr_data->qarray[i] = 0; 629 630 kr_data->qarray_cnt = 0; 631 kr_data->end_steps_cnt = QARRAY_SIZE-1; 632 kr_data->coeff_status_step = C72_CSTATE_NOT_UPDATED; 633 kr_data->curr_coeff++; 634 635 al_dbg("[%s]: doing next coefficient: %d ---\n\n", 636 kr_data->adapter->name, kr_data->curr_coeff); 637 638 nextstate = QMEASURE; 639 } else { 640 nextstate = SET_READY; 641 } 642 break; 643 case SET_READY: 644 /* 645 * our receiver is ready for data. 646 * no training will occur any more. 647 */ 648 kr_data->status_report.receiver_ready = TRUE; 649 /* 650 * in addition to the status we transmit, we also must tell our 651 * local hardware state-machine that we are done, so the 652 * training can eventually complete when the remote indicates 653 * it is ready also. The hardware will then automatically 654 * give control to the PCS layer completing training. 655 */ 656 al_eth_receiver_ready_set(kr_data->adapter, 657 AL_ETH_AN__LT_LANE_0); 658 659 nextstate = TX_DONE; 660 break; 661 case TX_DONE: 662 break; /* nothing else to do */ 663 default: 664 nextstate = kr_data->algo_state; 665 break; 666 } 667 668 /* 669 * The status we want to transmit to remote. 670 * Note that the status combines the receiver status of all coefficients 671 * with the transmitter's rx ready status. 672 */ 673 if (kr_data->algo_state != nextstate) { 674 al_dbg("[%s] [al_eth_kr_lt_transmit_run] STM changes %s -> %s: " 675 " Qarray=%d/%d/%d\n", kr_data->adapter->name, 676 al_eth_kr_mac_sm_name[kr_data->algo_state], 677 al_eth_kr_mac_sm_name[nextstate], 678 kr_data->qarray[0], kr_data->qarray[1], kr_data->qarray[2]); 679 } 680 681 kr_data->algo_state = nextstate; 682 683 /* 684 * write fields for transmission into hardware. 685 * Important: this must be done always, as the receiver may have 686 * received update commands and wants to return its status. 687 */ 688 al_eth_ld_coeff_up_set(kr_data->adapter, AL_ETH_AN__LT_LANE_0, &ldcoeff); 689 al_eth_ld_status_report_set(kr_data->adapter, AL_ETH_AN__LT_LANE_0, 690 &kr_data->status_report); 691 692 return (0); 693 } 694 695 /*****************************************************************************/ 696 static int 697 al_eth_kr_run_lt(struct al_eth_kr_data *kr_data) 698 { 699 unsigned int cnt; 700 int ret = 0; 701 boolean_t page_received = FALSE; 702 boolean_t an_completed = FALSE; 703 boolean_t error = FALSE; 704 boolean_t training_failure = FALSE; 705 706 al_eth_kr_lt_initialize(kr_data->adapter, AL_ETH_AN__LT_LANE_0); 707 708 if (al_eth_kr_lt_frame_lock_wait(kr_data->adapter, AL_ETH_AN__LT_LANE_0, 709 AL_ETH_KR_FRAME_LOCK_TIMEOUT) == TRUE) { 710 /* 711 * when locked, for the first time initialize the receiver and 712 * transmitter tasks to prepare it for detecting coefficient 713 * update requests. 714 */ 715 al_eth_kr_lt_receiver_task_init(kr_data); 716 ret = al_eth_kr_lt_transmitter_task_init(kr_data); 717 if (ret != 0) 718 goto error; 719 720 cnt = 0; 721 do { 722 ret = al_eth_kr_lt_receiver_task_run(kr_data); 723 if (ret != 0) 724 break; /* stop the link training */ 725 726 ret = al_eth_kr_lt_transmitter_task_run(kr_data); 727 if (ret != 0) 728 break; /* stop the link training */ 729 730 cnt++; 731 DELAY(100); 732 733 } while ((al_eth_kr_startup_proto_prog_get(kr_data->adapter, 734 AL_ETH_AN__LT_LANE_0)) && (cnt <= AL_ETH_KR_LT_MAX_ROUNDS)); 735 736 training_failure = 737 al_eth_kr_training_status_fail_get(kr_data->adapter, 738 AL_ETH_AN__LT_LANE_0); 739 al_dbg("[%s] training ended after %d rounds, failed = %s\n", 740 kr_data->adapter->name, cnt, 741 (training_failure) ? "Yes" : "No"); 742 if (training_failure || cnt > AL_ETH_KR_LT_MAX_ROUNDS) { 743 al_warn("[%s] Training Fail: status: %s, timeout: %s\n", 744 kr_data->adapter->name, 745 (training_failure) ? "Failed" : "OK", 746 (cnt > AL_ETH_KR_LT_MAX_ROUNDS) ? "Yes" : "No"); 747 748 /* 749 * note: link is now disabled, 750 * until training becomes disabled (see below). 751 */ 752 ret = EIO; 753 goto error; 754 } 755 756 } else { 757 al_info("[%s] FAILED: did not achieve initial frame lock...\n", 758 kr_data->adapter->name); 759 760 ret = EIO; 761 goto error; 762 } 763 764 /* 765 * ensure to stop link training at the end to allow normal PCS 766 * datapath to operate in case of training failure. 767 */ 768 al_eth_kr_lt_stop(kr_data->adapter, AL_ETH_AN__LT_LANE_0); 769 770 cnt = AL_ETH_KR_LT_DONE_TIMEOUT; 771 while (an_completed == FALSE) { 772 al_eth_kr_an_status_check(kr_data->adapter, &page_received, 773 &an_completed, &error); 774 DELAY(1); 775 if ((cnt--) == 0) { 776 al_info("%s: wait for an complete timeout!\n", __func__); 777 ret = ETIMEDOUT; 778 goto error; 779 } 780 } 781 782 error: 783 al_eth_kr_an_stop(kr_data->adapter); 784 785 return (ret); 786 } 787 788 /* execute Autonegotiation process */ 789 int al_eth_an_lt_execute(struct al_hal_eth_adapter *adapter, 790 struct al_serdes_grp_obj *serdes_obj, 791 enum al_serdes_lane lane, 792 struct al_eth_an_adv *an_adv, 793 struct al_eth_an_adv *partner_adv) 794 { 795 struct al_eth_kr_data kr_data; 796 int rc; 797 struct al_serdes_adv_rx_params rx_params; 798 799 al_memset(&kr_data, 0, sizeof(struct al_eth_kr_data)); 800 801 kr_data.adapter = adapter; 802 kr_data.serdes_obj = serdes_obj; 803 kr_data.lane = lane; 804 805 /* 806 * the link training progress will run rx equalization so need to make 807 * sure rx parameters is not been override 808 */ 809 rx_params.override = FALSE; 810 kr_data.serdes_obj->rx_advanced_params_set( 811 kr_data.serdes_obj, 812 kr_data.lane, 813 &rx_params); 814 815 rc = al_eth_kr_an_run(&kr_data, an_adv, partner_adv); 816 if (rc != 0) { 817 al_eth_kr_lt_stop(adapter, AL_ETH_AN__LT_LANE_0); 818 al_eth_kr_an_stop(adapter); 819 al_dbg("%s: auto-negotiation failed!\n", __func__); 820 return (rc); 821 } 822 823 if (partner_adv->technology != AL_ETH_AN_TECH_10GBASE_KR) { 824 al_eth_kr_lt_stop(adapter, AL_ETH_AN__LT_LANE_0); 825 al_eth_kr_an_stop(adapter); 826 al_dbg("%s: link partner isn't 10GBASE_KR.\n", __func__); 827 return (rc); 828 } 829 830 rc = al_eth_kr_run_lt(&kr_data); 831 if (rc != 0) { 832 al_eth_kr_lt_stop(adapter, AL_ETH_AN__LT_LANE_0); 833 al_eth_kr_an_stop(adapter); 834 al_dbg("%s: Link-training failed!\n", __func__); 835 return (rc); 836 } 837 838 return (0); 839 } 840