Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/al_eth/al_init_eth_kr.c
39507 views
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
839