]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/al_eth/al_init_eth_kr.c
MFV r356143:
[FreeBSD/FreeBSD.git] / sys / dev / al_eth / al_init_eth_kr.c
1 /*-
2  * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
3  * All rights reserved.
4  *
5  * Developed by Semihalf.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include "al_init_eth_kr.h"
33 #include "al_serdes.h"
34
35 /**
36  *  Ethernet
37  *  @{
38  * @file   al_init_eth_kr.c
39  *
40  * @brief  auto-negotiation and link training algorithms and state machines
41  *
42  * The link training algorithm implemented in this file going over the
43  * coefficients and looking for the best eye measurement possible for every one
44  * of them. it's using state machine to move between the different states.
45  * the state machine has 3 parts:
46  *      - preparation - waiting till the link partner (lp) will be ready and
47  *                      change his state to preset.
48  *      - measurement (per coefficient) - issue decrement for the coefficient
49  *                      under control till the eye measurement not increasing
50  *                      and remains in the optimum.
51  *      - completion - indicate the receiver is ready and wait for the lp to
52  *                     finish his work.
53  */
54
55 /* TODO: fix with more reasonable numbers */
56 /* timeout in mSec before auto-negotiation will be terminated */
57 #define AL_ETH_KR_AN_TIMEOUT            (500)
58 #define AL_ETH_KR_EYE_MEASURE_TIMEOUT   (100)
59 /* timeout in uSec before the process will be terminated */
60 #define AL_ETH_KR_FRAME_LOCK_TIMEOUT    (500 * 1000)
61 #define AL_ETH_KR_LT_DONE_TIMEOUT       (500 * 1000)
62 /* number of times the receiver and transmitter tasks will be called before the
63  * algorithm will be terminated */
64 #define AL_ETH_KR_LT_MAX_ROUNDS         (50000)
65
66 /* mac algorithm state machine */
67 enum al_eth_kr_mac_lt_state {
68         TX_INIT = 0,    /* start of all */
69         WAIT_BEGIN,     /* wait for initial training lock */
70         DO_PRESET,      /* issue PRESET to link partner */
71         DO_HOLD,        /* issue HOLD to link partner */
72         /* preparation is done, start testing the coefficient. */
73         QMEASURE,       /* EyeQ measurement. */
74         QCHECK,         /* Check if measurement shows best value. */
75         DO_NEXT_TRY,    /* issue DEC command to coeff for next measurement. */
76         END_STEPS,      /* perform last steps to go back to optimum. */
77         END_STEPS_HOLD, /* perform last steps HOLD command. */
78         COEFF_DONE,     /* done with the current coefficient updates.
79                          * Check if another should be done. */
80         /* end of training to all coefficients */
81         SET_READY,      /* indicate local receiver ready */
82         TX_DONE         /* transmit process completed, training can end. */
83 };
84
85 static const char * const al_eth_kr_mac_sm_name[] = {
86         "TX_INIT", "WAIT_BEGIN", "DO_PRESET",
87         "DO_HOLD", "QMEASURE", "QCHECK",
88         "DO_NEXT_TRY", "END_STEPS", "END_STEPS_HOLD",
89         "COEFF_DONE", "SET_READY", "TX_DONE"
90 };
91
92 /* Constants used for the measurement. */
93 enum al_eth_kr_coef {
94         AL_ETH_KR_COEF_C_MINUS,
95         AL_ETH_KR_COEF_C_ZERO,
96         AL_ETH_KR_COEF_C_PLUS,
97 };
98
99 /*
100  * test coefficients from COEFF_TO_MANIPULATE to COEFF_TO_MANIPULATE_LAST.
101  */
102 #define COEFF_TO_MANIPULATE AL_ETH_KR_COEF_C_MINUS
103 #define COEFF_TO_MANIPULATE_LAST AL_ETH_KR_COEF_C_MINUS
104 #define QARRAY_SIZE     3 /**< how many entries we want in our history array. */
105
106 struct al_eth_kr_data {
107         struct al_hal_eth_adapter       *adapter;
108         struct al_serdes_grp_obj        *serdes_obj;
109         enum al_serdes_lane             lane;
110
111         /* Receiver side data */
112         struct al_eth_kr_status_report_data status_report; /* report to response */
113         struct al_eth_kr_coef_up_data last_lpcoeff; /* last coeff received */
114
115         /* Transmitter side data */
116         enum al_eth_kr_mac_lt_state algo_state; /* Statemachine. */
117         unsigned int qarray[QARRAY_SIZE];       /* EyeQ measurements history */
118         /* How many entries in the array are valid for compares yet. */
119         unsigned int qarray_cnt;
120         enum al_eth_kr_coef curr_coeff;
121         /*
122          * Status of coefficient during the last
123          * DEC/INC command (before issuing HOLD again).
124          */
125         unsigned int coeff_status_step;
126         unsigned int end_steps_cnt;     /* Number of end steps needed */
127 };
128
129 static int
130 al_eth_kr_an_run(struct al_eth_kr_data *kr_data, struct al_eth_an_adv *an_adv,
131     struct al_eth_an_adv *an_partner_adv)
132 {
133         int rc;
134         boolean_t page_received = FALSE;
135         boolean_t an_completed = FALSE;
136         boolean_t error = FALSE;
137         int timeout = AL_ETH_KR_AN_TIMEOUT;
138
139         rc = al_eth_kr_an_init(kr_data->adapter, an_adv);
140         if (rc != 0) {
141                 al_err("%s %s autonegotiation init failed\n",
142                     kr_data->adapter->name, __func__);
143                 return (rc);
144         }
145
146         rc = al_eth_kr_an_start(kr_data->adapter, AL_ETH_AN__LT_LANE_0,
147             FALSE, TRUE);
148         if (rc != 0) {
149                 al_err("%s %s autonegotiation enable failed\n",
150                     kr_data->adapter->name, __func__);
151                 return (rc);
152         }
153
154         do {
155                 DELAY(10000);
156                 timeout -= 10;
157                 if (timeout <= 0) {
158                         al_info("%s %s autonegotiation failed on timeout\n",
159                             kr_data->adapter->name, __func__);
160
161                         return (ETIMEDOUT);
162                 }
163
164                 al_eth_kr_an_status_check(kr_data->adapter, &page_received,
165                     &an_completed, &error);
166         } while (page_received == FALSE);
167
168         if (error != 0) {
169                 al_info("%s %s autonegotiation failed (status error)\n",
170                     kr_data->adapter->name, __func__);
171
172                 return (EIO);
173         }
174
175         al_eth_kr_an_read_adv(kr_data->adapter, an_partner_adv);
176
177         al_dbg("%s %s autonegotiation completed. error = %d\n",
178             kr_data->adapter->name, __func__, error);
179
180         return (0);
181 }
182
183 /***************************** receiver side *********************************/
184 static enum al_eth_kr_cl72_cstate
185 al_eth_lt_coeff_set(struct al_eth_kr_data *kr_data,
186     enum al_serdes_tx_deemph_param param, uint32_t op)
187 {
188         enum al_eth_kr_cl72_cstate status = 0;
189
190         switch (op) {
191         case AL_PHY_KR_COEF_UP_HOLD:
192                 /* no need to update the serdes - return not updated*/
193                 status = C72_CSTATE_NOT_UPDATED;
194                 break;
195         case AL_PHY_KR_COEF_UP_INC:
196                 status = C72_CSTATE_UPDATED;
197
198                 if (kr_data->serdes_obj->tx_deemph_inc(
199                                         kr_data->serdes_obj,
200                                         kr_data->lane,
201                                         param) == 0)
202                         status = C72_CSTATE_MAX;
203                 break;
204         case AL_PHY_KR_COEF_UP_DEC:
205                 status = C72_CSTATE_UPDATED;
206
207                 if (kr_data->serdes_obj->tx_deemph_dec(
208                                         kr_data->serdes_obj,
209                                         kr_data->lane,
210                                         param) == 0)
211                         status = C72_CSTATE_MIN;
212                 break;
213         default: /* 3=reserved */
214                 break;
215         }
216
217         return (status);
218 }
219
220 /*
221  * Inspect the received coefficient update request and update all coefficients
222  * in the serdes accordingly.
223  */
224 static void
225 al_eth_coeff_req_handle(struct al_eth_kr_data *kr_data,
226     struct al_eth_kr_coef_up_data *lpcoeff)
227 {
228         struct al_eth_kr_status_report_data *report = &kr_data->status_report;
229
230         /* First check for Init and Preset commands. */
231         if ((lpcoeff->preset != 0) || (lpcoeff->initialize) != 0) {
232                 kr_data->serdes_obj->tx_deemph_preset(
233                                         kr_data->serdes_obj,
234                                         kr_data->lane);
235
236                 /*
237                  * in case of preset c(0) should be set to maximum and both c(1)
238                  * and c(-1) should be updated
239                  */
240                 report->c_minus = C72_CSTATE_UPDATED;
241
242                 report->c_plus = C72_CSTATE_UPDATED;
243
244                 report->c_zero = C72_CSTATE_MAX;
245
246                 return;
247         }
248
249         /*
250          * in case preset and initialize are false need to perform per
251          * coefficient action.
252          */
253         report->c_minus = al_eth_lt_coeff_set(kr_data,
254             AL_SERDES_TX_DEEMP_C_MINUS, lpcoeff->c_minus);
255
256         report->c_zero = al_eth_lt_coeff_set(kr_data,
257             AL_SERDES_TX_DEEMP_C_ZERO, lpcoeff->c_zero);
258
259         report->c_plus = al_eth_lt_coeff_set(kr_data,
260             AL_SERDES_TX_DEEMP_C_PLUS, lpcoeff->c_plus);
261
262         al_dbg("%s: c(0) = 0x%x c(-1) = 0x%x c(1) = 0x%x\n",
263             __func__, report->c_zero, report->c_plus, report->c_minus);
264 }
265
266 static void
267 al_eth_kr_lt_receiver_task_init(struct al_eth_kr_data *kr_data)
268 {
269
270         al_memset(&kr_data->last_lpcoeff, 0,
271             sizeof(struct al_eth_kr_coef_up_data));
272         al_memset(&kr_data->status_report, 0,
273             sizeof(struct al_eth_kr_status_report_data));
274 }
275
276 static boolean_t
277 al_eth_lp_coeff_up_change(struct al_eth_kr_data *kr_data,
278     struct al_eth_kr_coef_up_data *lpcoeff)
279 {
280         struct al_eth_kr_coef_up_data *last_lpcoeff = &kr_data->last_lpcoeff;
281
282         if (al_memcmp(last_lpcoeff, lpcoeff,
283             sizeof(struct al_eth_kr_coef_up_data)) == 0) {
284                 return (FALSE);
285         }
286
287         al_memcpy(last_lpcoeff, lpcoeff, sizeof(struct al_eth_kr_coef_up_data));
288
289         return (TRUE);
290 }
291
292 /*
293  * Run the receiver task for one cycle.
294  * The receiver task continuously inspects the received coefficient update
295  * requests and acts upon.
296  *
297  * @return <0 if error occur
298  */
299 static int
300 al_eth_kr_lt_receiver_task_run(struct al_eth_kr_data *kr_data)
301 {
302         struct al_eth_kr_coef_up_data new_lpcoeff;
303
304         /*
305          * First inspect status of the link. It may have dropped frame lock as
306          * the remote did some reconfiguration of its serdes.
307          * Then we simply have nothing to do and return immediately as caller
308          * will call us continuously until lock comes back.
309          */
310
311         if (al_eth_kr_receiver_frame_lock_get(kr_data->adapter,
312             AL_ETH_AN__LT_LANE_0) != 0) {
313                 return (0);
314         }
315
316         /* check if a new update command was received */
317         al_eth_lp_coeff_up_get(kr_data->adapter,
318             AL_ETH_AN__LT_LANE_0, &new_lpcoeff);
319
320         if (al_eth_lp_coeff_up_change(kr_data, &new_lpcoeff) != 0) {
321                 /* got some new coefficient update request. */
322                 al_eth_coeff_req_handle(kr_data, &new_lpcoeff);
323         }
324
325         return (0);
326 }
327
328 /******************************** transmitter side ***************************/
329 static int
330 al_eth_kr_lt_transmitter_task_init(struct al_eth_kr_data *kr_data)
331 {
332         int i;
333         int rc;
334         unsigned int temp_val;
335
336         for (i = 0; i < QARRAY_SIZE; i++)
337                 kr_data->qarray[i] = 0;
338
339         kr_data->qarray_cnt = 0;
340         kr_data->algo_state = TX_INIT;
341         kr_data->curr_coeff = COEFF_TO_MANIPULATE;  /* first coeff to test. */
342         kr_data->coeff_status_step  = C72_CSTATE_NOT_UPDATED;
343         kr_data->end_steps_cnt = QARRAY_SIZE-1;  /* go back to first entry */
344
345         /*
346          * Perform measure eye here to run the rx equalizer
347          * for the first time to get init values
348          */
349         rc = kr_data->serdes_obj->eye_measure_run(
350                                 kr_data->serdes_obj,
351                                 kr_data->lane,
352                                 AL_ETH_KR_EYE_MEASURE_TIMEOUT,
353                                 &temp_val);
354         if (rc != 0) {
355                 al_warn("%s: Failed to run Rx equalizer (rc = 0x%x)\n",
356                     __func__, rc);
357
358                 return (rc);
359         }
360
361         return (0);
362 }
363
364 static boolean_t
365 al_eth_kr_lt_all_not_updated(struct al_eth_kr_status_report_data *report)
366 {
367
368         if ((report->c_zero == C72_CSTATE_NOT_UPDATED) &&
369             (report->c_minus == C72_CSTATE_NOT_UPDATED) &&
370             (report->c_plus == C72_CSTATE_NOT_UPDATED)) {
371                 return (TRUE);
372         }
373
374         return (FALSE);
375 }
376
377 static void
378 al_eth_kr_lt_coef_set(struct al_eth_kr_coef_up_data *ldcoeff,
379     enum al_eth_kr_coef coef, enum al_eth_kr_cl72_coef_op op)
380 {
381
382         switch (coef) {
383         case AL_ETH_KR_COEF_C_MINUS:
384                 ldcoeff->c_minus = op;
385                 break;
386         case AL_ETH_KR_COEF_C_PLUS:
387                 ldcoeff->c_plus = op;
388                 break;
389         case AL_ETH_KR_COEF_C_ZERO:
390                 ldcoeff->c_zero = op;
391                 break;
392         }
393 }
394
395 static enum al_eth_kr_cl72_cstate
396 al_eth_kr_lt_coef_report_get(struct al_eth_kr_status_report_data *report,
397     enum al_eth_kr_coef coef)
398 {
399
400         switch (coef) {
401         case AL_ETH_KR_COEF_C_MINUS:
402                 return (report->c_minus);
403         case AL_ETH_KR_COEF_C_PLUS:
404                 return (report->c_plus);
405         case AL_ETH_KR_COEF_C_ZERO:
406                 return (report->c_zero);
407         }
408
409         return (0);
410 }
411
412 /*
413  * Run the transmitter_task for one cycle.
414  *
415  * @return <0 if error occurs
416  */
417 static int
418 al_eth_kr_lt_transmitter_task_run(struct al_eth_kr_data *kr_data)
419 {
420         struct al_eth_kr_status_report_data report;
421         unsigned int coeff_status_cur;
422         struct al_eth_kr_coef_up_data ldcoeff = { 0, 0, 0, 0, 0 };
423         unsigned int val;
424         int i;
425         enum al_eth_kr_mac_lt_state nextstate;
426         int rc = 0;
427
428         /*
429          * do nothing if currently there is no frame lock (which may happen
430          * when remote updates its analogs).
431          */
432         if (al_eth_kr_receiver_frame_lock_get(kr_data->adapter,
433             AL_ETH_AN__LT_LANE_0) == 0) {
434                 return (0);
435         }
436
437         al_eth_lp_status_report_get(kr_data->adapter,
438             AL_ETH_AN__LT_LANE_0, &report);
439
440         /* extract curr status of the coefficient in use */
441         coeff_status_cur = al_eth_kr_lt_coef_report_get(&report,
442             kr_data->curr_coeff);
443
444         nextstate = kr_data->algo_state; /* default we stay in curr state; */
445
446         switch (kr_data->algo_state) {
447         case TX_INIT:
448                 /* waiting for start */
449                 if (al_eth_kr_startup_proto_prog_get(kr_data->adapter,
450                     AL_ETH_AN__LT_LANE_0) != 0) {
451                         /* training is on and frame lock */
452                         nextstate = WAIT_BEGIN;
453                 }
454                 break;
455         case WAIT_BEGIN:
456                 kr_data->qarray_cnt = 0;
457                 kr_data->curr_coeff = COEFF_TO_MANIPULATE;
458                 kr_data->coeff_status_step = C72_CSTATE_NOT_UPDATED;
459                 coeff_status_cur = C72_CSTATE_NOT_UPDATED;
460                 kr_data->end_steps_cnt = QARRAY_SIZE-1;
461
462                 /* Wait for not_updated for all coefficients from remote */
463                 if (al_eth_kr_lt_all_not_updated(&report) != 0) {
464                         ldcoeff.preset = TRUE;
465                         nextstate = DO_PRESET;
466                 }
467                 break;
468         case DO_PRESET:
469                 /*
470                  * Send PRESET and wait for for updated for all
471                  * coefficients from remote
472                  */
473                 if (al_eth_kr_lt_all_not_updated(&report) == 0)
474                         nextstate = DO_HOLD;
475                 else /* as long as the lp didn't response to the preset
476                       * we should continue sending it */
477                         ldcoeff.preset = TRUE;
478                 break;
479         case DO_HOLD:
480                 /*
481                  * clear the PRESET, issue HOLD command and wait for
482                  * hold handshake
483                  */
484                 if (al_eth_kr_lt_all_not_updated(&report) != 0)
485                         nextstate = QMEASURE;
486                 break;
487
488         case QMEASURE:
489                 /* makes a measurement and fills the new value into the array */
490                 rc = kr_data->serdes_obj->eye_measure_run(
491                                         kr_data->serdes_obj,
492                                         kr_data->lane,
493                                         AL_ETH_KR_EYE_MEASURE_TIMEOUT,
494                                         &val);
495                 if (rc != 0) {
496                         al_warn("%s: Rx eye measurement failed\n", __func__);
497
498                         return (rc);
499                 }
500
501                 al_dbg("%s: Rx Measure eye returned 0x%x\n", __func__, val);
502
503                 /* put the new value into the array at the top. */
504                 for (i = 0; i < QARRAY_SIZE-1; i++)
505                         kr_data->qarray[i] = kr_data->qarray[i+1];
506
507                 kr_data->qarray[QARRAY_SIZE-1] = val;
508
509                 if (kr_data->qarray_cnt < QARRAY_SIZE)
510                         kr_data->qarray_cnt++;
511
512                 nextstate = QCHECK;
513                 break;
514         case QCHECK:
515                 /* check if we reached the best link quality yet. */
516                 if (kr_data->qarray_cnt < QARRAY_SIZE) {
517                         /* keep going until at least the history is
518                          * filled. check that we can keep going or if
519                          * coefficient has already reached minimum.
520                          */
521
522                         if (kr_data->coeff_status_step == C72_CSTATE_MIN)
523                                 nextstate = COEFF_DONE;
524                         else {
525                                 /*
526                                  * request a DECREMENT of the
527                                  * coefficient under control
528                                  */
529                                 al_eth_kr_lt_coef_set(&ldcoeff,
530                                     kr_data->curr_coeff, AL_PHY_KR_COEF_UP_DEC);
531
532                                 nextstate = DO_NEXT_TRY;
533                         }
534                 } else {
535                         /*
536                          * check if current value and last both are worse than
537                          * the 2nd last. This we take as an ending condition
538                          * assuming the minimum was reached two tries before
539                          * so we will now go back to that point.
540                          */
541                         if ((kr_data->qarray[0] < kr_data->qarray[1]) &&
542                             (kr_data->qarray[0] < kr_data->qarray[2])) {
543                                 /*
544                                  * request a INCREMENT of the
545                                  * coefficient under control
546                                  */
547                                 al_eth_kr_lt_coef_set(&ldcoeff,
548                                     kr_data->curr_coeff, AL_PHY_KR_COEF_UP_INC);
549
550                                 /* start going back to the maximum */
551                                 nextstate = END_STEPS;
552                                 if (kr_data->end_steps_cnt > 0)
553                                         kr_data->end_steps_cnt--;
554                         } else {
555                                 if (kr_data->coeff_status_step ==
556                                     C72_CSTATE_MIN) {
557                                         nextstate = COEFF_DONE;
558                                 } else {
559                                         /*
560                                          * request a DECREMENT of the
561                                          * coefficient under control
562                                          */
563                                         al_eth_kr_lt_coef_set(&ldcoeff,
564                                             kr_data->curr_coeff,
565                                             AL_PHY_KR_COEF_UP_DEC);
566
567                                         nextstate = DO_NEXT_TRY;
568                                 }
569                         }
570                 }
571                 break;
572         case DO_NEXT_TRY:
573                 /*
574                  * save the status when we issue the DEC step to the remote,
575                  * before the HOLD is done again.
576                  */
577                 kr_data->coeff_status_step = coeff_status_cur;
578
579                 if (coeff_status_cur != C72_CSTATE_NOT_UPDATED)
580                         nextstate = DO_HOLD;  /* go to next measurement round */
581                 else
582                         al_eth_kr_lt_coef_set(&ldcoeff,
583                             kr_data->curr_coeff, AL_PHY_KR_COEF_UP_DEC);
584                 break;
585         /*
586          * Coefficient iteration completed, go back to the optimum step
587          * In this algorithm we assume 2 before curr was best hence need to do
588          * two INC runs.
589          */
590         case END_STEPS:
591                 if (coeff_status_cur != C72_CSTATE_NOT_UPDATED)
592                         nextstate = END_STEPS_HOLD;
593                 else
594                         al_eth_kr_lt_coef_set(&ldcoeff,
595                             kr_data->curr_coeff, AL_PHY_KR_COEF_UP_INC);
596                 break;
597         case END_STEPS_HOLD:
598                 if (coeff_status_cur == C72_CSTATE_NOT_UPDATED) {
599                         if (kr_data->end_steps_cnt != 0) {
600                                 /*
601                                  * request a INCREMENT of the
602                                  * coefficient under control
603                                  */
604                                 al_eth_kr_lt_coef_set(&ldcoeff,
605                                     kr_data->curr_coeff, AL_PHY_KR_COEF_UP_INC);
606
607                                 /* go 2nd time - dec the end step count */
608                                 nextstate = END_STEPS;
609
610                                 if (kr_data->end_steps_cnt > 0)
611                                         kr_data->end_steps_cnt--;
612
613                         } else {
614                                 nextstate = COEFF_DONE;
615                         }
616                 }
617                 break;
618         case COEFF_DONE:
619                 /*
620                  * now this coefficient is done.
621                  * We can now either choose to finish here,
622                  * or keep going with another coefficient.
623                  */
624                 if ((int)kr_data->curr_coeff < COEFF_TO_MANIPULATE_LAST) {
625                         int i;
626
627                         for (i = 0; i < QARRAY_SIZE; i++)
628                                 kr_data->qarray[i] = 0;
629
630                         kr_data->qarray_cnt = 0;
631                         kr_data->end_steps_cnt = QARRAY_SIZE-1;
632                         kr_data->coeff_status_step = C72_CSTATE_NOT_UPDATED;
633                         kr_data->curr_coeff++;
634
635                         al_dbg("[%s]: doing next coefficient: %d ---\n\n",
636                             kr_data->adapter->name, kr_data->curr_coeff);
637
638                         nextstate = QMEASURE;
639                 } else {
640                         nextstate = SET_READY;
641                 }
642                 break;
643         case SET_READY:
644                 /*
645                  * our receiver is ready for data.
646                  * no training will occur any more.
647                  */
648                 kr_data->status_report.receiver_ready = TRUE;
649                 /*
650                  * in addition to the status we transmit, we also must tell our
651                  * local hardware state-machine that we are done, so the
652                  * training can eventually complete when the remote indicates
653                  * it is ready also. The hardware will then automatically
654                  * give control to the PCS layer completing training.
655                  */
656                 al_eth_receiver_ready_set(kr_data->adapter,
657                     AL_ETH_AN__LT_LANE_0);
658
659                 nextstate = TX_DONE;
660                 break;
661         case TX_DONE:
662                 break;  /* nothing else to do */
663         default:
664                 nextstate = kr_data->algo_state;
665                 break;
666         }
667
668         /*
669          * The status we want to transmit to remote.
670          * Note that the status combines the receiver status of all coefficients
671          * with the transmitter's rx ready status.
672          */
673         if (kr_data->algo_state != nextstate) {
674                 al_dbg("[%s] [al_eth_kr_lt_transmit_run] STM changes %s -> %s: "
675                     " Qarray=%d/%d/%d\n", kr_data->adapter->name,
676                     al_eth_kr_mac_sm_name[kr_data->algo_state],
677                     al_eth_kr_mac_sm_name[nextstate],
678                     kr_data->qarray[0], kr_data->qarray[1], kr_data->qarray[2]);
679         }
680
681         kr_data->algo_state = nextstate;
682
683         /*
684          * write fields for transmission into hardware.
685          * Important: this must be done always, as the receiver may have
686          * received update commands and wants to return its status.
687          */
688         al_eth_ld_coeff_up_set(kr_data->adapter, AL_ETH_AN__LT_LANE_0, &ldcoeff);
689         al_eth_ld_status_report_set(kr_data->adapter, AL_ETH_AN__LT_LANE_0,
690             &kr_data->status_report);
691
692         return (0);
693 }
694
695 /*****************************************************************************/
696 static int
697 al_eth_kr_run_lt(struct al_eth_kr_data *kr_data)
698 {
699         unsigned int cnt;
700         int ret = 0;
701         boolean_t page_received = FALSE;
702         boolean_t an_completed = FALSE;
703         boolean_t error = FALSE;
704         boolean_t training_failure = FALSE;
705
706         al_eth_kr_lt_initialize(kr_data->adapter, AL_ETH_AN__LT_LANE_0);
707
708         if (al_eth_kr_lt_frame_lock_wait(kr_data->adapter, AL_ETH_AN__LT_LANE_0,
709             AL_ETH_KR_FRAME_LOCK_TIMEOUT) == TRUE) {
710
711                 /*
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
784 error:
785         al_eth_kr_an_stop(kr_data->adapter);
786
787         return (ret);
788 }
789
790 /* execute Autonegotiation process */
791 int 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 }