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 */
67enum 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
85static 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. */
93enum 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
106struct 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
129static int
130al_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 *********************************/
184static enum al_eth_kr_cl72_cstate
185al_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 */
224static void
225al_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
266static void
267al_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
276static boolean_t
277al_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 */
299static int
300al_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 ***************************/
329static int
330al_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
364static boolean_t
365al_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
377static void
378al_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
395static enum al_eth_kr_cl72_cstate
396al_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 */
417static int
418al_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 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/*****************************************************************************/
696static int
697al_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		/*
712		 * when locked, for the first time initialize the receiver and
713		 * transmitter tasks to prepare it for detecting coefficient
714		 * update requests.
715		 */
716		al_eth_kr_lt_receiver_task_init(kr_data);
717		ret = al_eth_kr_lt_transmitter_task_init(kr_data);
718		if (ret != 0)
719			goto error;
720
721		cnt = 0;
722		do {
723			ret = al_eth_kr_lt_receiver_task_run(kr_data);
724			if (ret != 0)
725				break; /* stop the link training */
726
727			ret = al_eth_kr_lt_transmitter_task_run(kr_data);
728			if (ret != 0)
729				break;  /* stop the link training */
730
731			cnt++;
732			DELAY(100);
733
734		} while ((al_eth_kr_startup_proto_prog_get(kr_data->adapter,
735		    AL_ETH_AN__LT_LANE_0)) && (cnt <= AL_ETH_KR_LT_MAX_ROUNDS));
736
737		training_failure =
738		    al_eth_kr_training_status_fail_get(kr_data->adapter,
739		    AL_ETH_AN__LT_LANE_0);
740		al_dbg("[%s] training ended after %d rounds, failed = %s\n",
741		    kr_data->adapter->name, cnt,
742		    (training_failure) ? "Yes" : "No");
743		if (training_failure || cnt > AL_ETH_KR_LT_MAX_ROUNDS) {
744			al_warn("[%s] Training Fail: status: %s, timeout: %s\n",
745			    kr_data->adapter->name,
746			    (training_failure) ? "Failed" : "OK",
747			    (cnt > AL_ETH_KR_LT_MAX_ROUNDS) ? "Yes" : "No");
748
749			/*
750			 * note: link is now disabled,
751			 * until training becomes disabled (see below).
752			 */
753			ret = EIO;
754			goto error;
755		}
756
757	} else {
758
759		al_info("[%s] FAILED: did not achieve initial frame lock...\n",
760		    kr_data->adapter->name);
761
762		ret = EIO;
763		goto error;
764	}
765
766	/*
767	 * ensure to stop link training at the end to allow normal PCS
768	 * datapath to operate in case of training failure.
769	 */
770	al_eth_kr_lt_stop(kr_data->adapter, AL_ETH_AN__LT_LANE_0);
771
772	cnt = AL_ETH_KR_LT_DONE_TIMEOUT;
773	while (an_completed == FALSE) {
774		al_eth_kr_an_status_check(kr_data->adapter, &page_received,
775		    &an_completed, &error);
776		DELAY(1);
777		if ((cnt--) == 0) {
778			al_info("%s: wait for an complete timeout!\n", __func__);
779			ret = ETIMEDOUT;
780			goto error;
781		}
782	}
783
784error:
785	al_eth_kr_an_stop(kr_data->adapter);
786
787	return (ret);
788}
789
790/* execute Autonegotiation process */
791int al_eth_an_lt_execute(struct al_hal_eth_adapter	*adapter,
792			 struct al_serdes_grp_obj	*serdes_obj,
793			 enum al_serdes_lane		lane,
794			 struct al_eth_an_adv		*an_adv,
795			 struct al_eth_an_adv		*partner_adv)
796{
797	struct al_eth_kr_data kr_data;
798	int rc;
799	struct al_serdes_adv_rx_params rx_params;
800
801	al_memset(&kr_data, 0, sizeof(struct al_eth_kr_data));
802
803	kr_data.adapter = adapter;
804	kr_data.serdes_obj = serdes_obj;
805	kr_data.lane = lane;
806
807	/*
808	 * the link training progress will run rx equalization so need to make
809	 * sure rx parameters is not been override
810	 */
811	rx_params.override = FALSE;
812	kr_data.serdes_obj->rx_advanced_params_set(
813					kr_data.serdes_obj,
814					kr_data.lane,
815					&rx_params);
816
817	rc = al_eth_kr_an_run(&kr_data, an_adv, partner_adv);
818	if (rc != 0) {
819		al_eth_kr_lt_stop(adapter, AL_ETH_AN__LT_LANE_0);
820		al_eth_kr_an_stop(adapter);
821		al_dbg("%s: auto-negotiation failed!\n", __func__);
822		return (rc);
823	}
824
825	if (partner_adv->technology != AL_ETH_AN_TECH_10GBASE_KR) {
826		al_eth_kr_lt_stop(adapter, AL_ETH_AN__LT_LANE_0);
827		al_eth_kr_an_stop(adapter);
828		al_dbg("%s: link partner isn't 10GBASE_KR.\n", __func__);
829		return (rc);
830	}
831
832	rc = al_eth_kr_run_lt(&kr_data);
833	if (rc != 0) {
834		al_eth_kr_lt_stop(adapter, AL_ETH_AN__LT_LANE_0);
835		al_eth_kr_an_stop(adapter);
836		al_dbg("%s: Link-training failed!\n", __func__);
837		return (rc);
838	}
839
840	return (0);
841}
842