xref: /freebsd/sys/dev/al_eth/al_init_eth_kr.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
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