xref: /freebsd/sys/dev/al_eth/al_init_eth_kr.c (revision 535af610)
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 	al_bool page_received = AL_FALSE;
135 	al_bool an_completed = AL_FALSE;
136 	al_bool error = AL_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 	    AL_FALSE, AL_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 == AL_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 bool
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 bool
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 = AL_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 = AL_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 = AL_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 	al_bool page_received = AL_FALSE;
702 	al_bool an_completed = AL_FALSE;
703 	al_bool error = AL_FALSE;
704 	al_bool training_failure = AL_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) == AL_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 == AL_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 = AL_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