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 }
Cache object: 2692d00ff0fc8ab56ac6b46ea027a0a7
|