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
al_eth_kr_an_run(struct al_eth_kr_data * kr_data,struct al_eth_an_adv * an_adv,struct al_eth_an_adv * an_partner_adv)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
al_eth_lt_coeff_set(struct al_eth_kr_data * kr_data,enum al_serdes_tx_deemph_param param,uint32_t op)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
al_eth_coeff_req_handle(struct al_eth_kr_data * kr_data,struct al_eth_kr_coef_up_data * lpcoeff)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
al_eth_kr_lt_receiver_task_init(struct al_eth_kr_data * kr_data)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
al_eth_lp_coeff_up_change(struct al_eth_kr_data * kr_data,struct al_eth_kr_coef_up_data * lpcoeff)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
al_eth_kr_lt_receiver_task_run(struct al_eth_kr_data * kr_data)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
al_eth_kr_lt_transmitter_task_init(struct al_eth_kr_data * kr_data)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
al_eth_kr_lt_all_not_updated(struct al_eth_kr_status_report_data * report)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
al_eth_kr_lt_coef_set(struct al_eth_kr_coef_up_data * ldcoeff,enum al_eth_kr_coef coef,enum al_eth_kr_cl72_coef_op op)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
al_eth_kr_lt_coef_report_get(struct al_eth_kr_status_report_data * report,enum al_eth_kr_coef coef)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
al_eth_kr_lt_transmitter_task_run(struct al_eth_kr_data * kr_data)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
al_eth_kr_run_lt(struct al_eth_kr_data * kr_data)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 */
al_eth_an_lt_execute(struct al_hal_eth_adapter * adapter,struct al_serdes_grp_obj * serdes_obj,enum al_serdes_lane lane,struct al_eth_an_adv * an_adv,struct al_eth_an_adv * partner_adv)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