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