The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/al_eth/al_init_eth_kr.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 2692d00ff0fc8ab56ac6b46ea027a0a7


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.