static void mpfs_ddr_manual_addcmd_training()

in arch/risc-v/src/mpfs/mpfs_ddr.c [2468:2983]


static void mpfs_ddr_manual_addcmd_training(struct mpfs_ddr_priv_s *priv)
{
  uint32_t bclk_phase;
  uint32_t bclk90_phase;
  uint32_t refclk_phase;
  uint32_t ca_indly;
  uint32_t dpc_vals;
  uint32_t j;
  uint32_t i;

  /* If automatic training is enabled, skip this */

  if ((LIBERO_SETTING_TRAINING_SKIP_SETTING & ADDCMD_BIT) == 0)
    {
      return;
    }

  /* Apply offset & load the phase */

  bclk_phase = ((priv->bclk_answer + SW_TRAINING_BCLK_SCLK_OFFSET) &
                0x07) << 8;
  bclk90_phase = ((priv->bclk_answer + SW_TRAINING_BCLK_SCLK_OFFSET + 2) &
                  0x07) << 11;

  /* Store DRV & VREF initial values (to be re-applied after
   * CA training)
   */

  uint32_t ca_drv = getreg32(MPFS_CFG_DDR_SGMII_PHY_RPC1_DRV);
  uint32_t ca_vref = (getreg32(MPFS_CFG_DDR_SGMII_PHY_DPC_BITS) >> 12) &
                      0x3f;

  uint32_t dpc_bits_new;
  uint32_t vref_answer;
  uint32_t transition_a5_min_last = 129;

  putreg32(0x01, MPFS_CFG_DDR_SGMII_PHY_EXPERT_MODE_EN);

  for (ca_indly = 0; ca_indly < 30; ca_indly = ca_indly + 5)
    {
      putreg32(ca_indly, MPFS_CFG_DDR_SGMII_PHY_RPC145);
      putreg32(ca_indly, MPFS_CFG_DDR_SGMII_PHY_RPC147);
      uint32_t break_loop = 1;
      uint32_t in_window = 0;
      vref_answer = 128;

      /* Begin VREF training */

      for (uint32_t vref = 5; vref < 30; vref++)
        {
          uint32_t transition_a5_max = 0;
          uint32_t transition_a5_min = 128;
          uint32_t rx_a5_last;
          uint32_t rx_a5;
          uint32_t transition_a5;
          uint32_t range_a5 = 0;

          if (transition_a5_min_last > 128)
            {
              transition_a5_min_last = 128;
            }

          putreg32(0, MPFS_IOSCB_BANK_CNTL_DDR_SOFT_RESET);

          /* Set VREF */

          mpfs_wait_cycles(10);
          dpc_bits_new = (getreg32(MPFS_CFG_DDR_SGMII_PHY_DPC_BITS) &
                          0xfffc0fff) | (vref << 12) | (0x1 << 18);
          putreg32(dpc_bits_new, MPFS_CFG_DDR_SGMII_PHY_DPC_BITS);
          mpfs_wait_cycles(10);
          putreg32(1, MPFS_IOSCB_BANK_CNTL_DDR_SOFT_RESET);
          mpfs_wait_cycles(10);

          uint32_t deltat = 128;

          for (j = 0; j < 20; j++)
            {
              putreg32(0x00,
                       MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_DIRECTION_REG1);
              putreg32(0x00, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);
              putreg32(0x180000,
                       MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);
              putreg32(0x00, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);

              putreg32(0x180000,
                       MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_DIRECTION_REG1);
              putreg32(0x00, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);
              putreg32(0x180000,
                       MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);
              putreg32(0x00, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);

              rx_a5_last = 0xf;
              transition_a5 = 0;
              deltat = 128;
              mpfs_wait_cycles(10);

              for (i = 0; i < (128 - ca_indly); i++)
                {
                  putreg32(0x00,
                           MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_MOVE_REG1);
                  putreg32(0x180000,
                           MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_MOVE_REG1);
                  putreg32(0x00,
                           MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_MOVE_REG1);
                  mpfs_wait_cycles(10);

                  rx_a5 = (getreg32(
                          MPFS_CFG_DDR_SGMII_PHY_EXPERT_ADDCMD_LN_READBACK) \
                          & 0x0300) >> 8;

                  if (transition_a5 != 0)
                    {
                      if (((i - transition_a5) > 8))
                        {
                          break;
                        }
                    }

                  if (transition_a5 == 0)
                    {
                      if (((rx_a5 ^ rx_a5_last) & rx_a5))
                        {
                          transition_a5 = i;
                        }
                      else
                        {
                          rx_a5_last = rx_a5;
                        }
                    }
                  else
                    {
                      if ((i - transition_a5) == 4)
                        {
                          if (!((rx_a5 ^ rx_a5_last) & rx_a5))
                            {
                              /* Continue looking for transition */

                              transition_a5 = 0;
                              rx_a5_last = rx_a5;
                            }
                        }
                    }
                }

              if (transition_a5 != 0)
                {
                  if (transition_a5 > transition_a5_max)
                    {
                      transition_a5_max = transition_a5;
                    }

                  if (transition_a5 < transition_a5_min)
                    {
                      transition_a5_min = transition_a5;
                    }
                }
            }

          range_a5 = transition_a5_max - transition_a5_min;

          if (transition_a5_min < 10)
            {
              break_loop = 0;
            }

          if (range_a5 <= 5)
            {
              if (transition_a5_min > transition_a5_min_last)
                {
                  deltat = transition_a5_min - transition_a5_min_last;
                }
              else
                {
                  deltat = transition_a5_min_last - transition_a5_min;
                }

              if (deltat <= 5)
                {
                  in_window = (in_window << 1) | 1;
                }
            }
          else
            {
              in_window = (in_window << 1) | 0;
            }

          if (vref_answer == 128)
            {
              if ((in_window & 0x3) == 0x3)
                {
                  vref_answer = vref;
                  break;
                }
            }

          transition_a5_min_last = transition_a5_min;
        }

      if (break_loop)
        {
          break;
        }
    }

  putreg32(0, MPFS_IOSCB_BANK_CNTL_DDR_SOFT_RESET);

  /* Set VREF */

  mpfs_wait_cycles(10);

  if (vref_answer == 128)
    {
      vref_answer = 0x10;
      dpc_bits_new = (getreg32(MPFS_CFG_DDR_SGMII_PHY_DPC_BITS) & 0xfffc0fff)
                      | (vref_answer << 12) | (0x1 << 18);
    }
  else
    {
      vref_answer = vref_answer;
      dpc_bits_new = (getreg32(MPFS_CFG_DDR_SGMII_PHY_DPC_BITS) & 0xfffc0fff)
                      | (vref_answer << 12) | (0x1 << 18);
    }

  putreg32(dpc_bits_new, MPFS_CFG_DDR_SGMII_PHY_DPC_BITS);
  mpfs_wait_cycles(10);
  putreg32(1, MPFS_IOSCB_BANK_CNTL_DDR_SOFT_RESET);
  mpfs_wait_cycles(10000);

  /* Begin manual addcmd training */

  uint32_t init_del_offset = 0x8;
  uint32_t a5_offset_fail;
  uint32_t rpc147_offset = 0x2;
  uint32_t rpc145_offset = 0x0;
  uint8_t refclk_offset = mpfs_ddr_manual_addcmd_refclk_offset(priv);
  a5_offset_fail = 1;

  while (a5_offset_fail)
    {
      a5_offset_fail = 0; /* 1 indicates a fail */

      putreg32(init_del_offset + rpc147_offset,
               MPFS_CFG_DDR_SGMII_PHY_RPC147);
      putreg32(init_del_offset + rpc145_offset,
               MPFS_CFG_DDR_SGMII_PHY_RPC145);

      putreg32(0x03, MPFS_CFG_DDR_SGMII_PHY_EXPERT_MODE_EN);

      uint32_t rx_a5;
      uint32_t rx_a5_last;
      uint32_t rx_ck;
      uint32_t rx_ck_last;
      uint32_t transition_a5;
      uint32_t transition_ck;
      uint32_t difference[8] = {
        0
      };

      uint32_t transition_ck_array[8] = {
        0
      };

      uint32_t transitions_found;
      uint32_t transition_a5_max = 0;

      for (j = 0; j < 16; j++)
        {
          /* Increase j loop to increase number of samples on transition_a5
           * (for noisy CA in LPDDR4)
           */

          /* Load INDLY */

          putreg32(0x00,
                   MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_DIRECTION_REG1);

          putreg32(0x00, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);
          putreg32(0x180000, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);
          putreg32(0x00, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);

          /* Load OUTDLY */

          putreg32(0x180000,
                   MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_DIRECTION_REG1);

          putreg32(0x00, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);
          putreg32(0x180000, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);
          putreg32(0x00, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);

          refclk_phase = (j % 8U) << 2U;
          putreg32((0x00004003 | bclk_phase | bclk90_phase | refclk_phase),
                   MPFS_IOSCB_DDR_PLL_PHADJ);
          putreg32((0x00000003 | bclk_phase | bclk90_phase | refclk_phase),
                   MPFS_IOSCB_DDR_PLL_PHADJ);
          putreg32((0x00004003 | bclk_phase | bclk90_phase | refclk_phase),
                   MPFS_IOSCB_DDR_PLL_PHADJ);

          rx_a5_last = 0xf;
          rx_ck_last = 0x5;
          transition_a5 = 0;
          transition_ck = 0;

          mpfs_wait_cycles(100);
          transitions_found = 0;
          i = 0;

          while ((!transitions_found) & (i < 128))
            {
              putreg32(0x00, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_MOVE_REG1);
              putreg32(0x180000,
                       MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_MOVE_REG1);
              putreg32(0x00, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_MOVE_REG1);
              mpfs_wait_cycles(10);

              rx_a5 =
                (getreg32(MPFS_CFG_DDR_SGMII_PHY_EXPERT_ADDCMD_LN_READBACK) &
                 0x0300) >> 8;
              rx_ck =
                getreg32(MPFS_CFG_DDR_SGMII_PHY_EXPERT_ADDCMD_LN_READBACK) &
                0x000f;

              if ((transition_a5 != 0) && (transition_ck != 0))
                {
                  if (((i - transition_a5) > 8) && ((i - transition_ck) > 8))
                    {
                      transitions_found = 1;
                    }
                }

              if (transition_ck == 0)
                {
                  if (rx_ck_last != 0x5) /* If edge detected */
                    {
                      if (rx_ck == 0x5)
                        transition_ck = i; /* Transition detected at i */
                    }

                  rx_ck_last = rx_ck;
                }
              else
                {
                  if ((i - transition_ck) == 4)
                    {
                      /* If rx_ck not stable after 4 increments, mark it
                       * a false transition
                       */

                      if (rx_ck != rx_ck_last)
                        {
                          /* Keep looking for transition */

                          transition_ck = 0;
                          rx_ck_last = rx_ck;
                        }
                    }
                }

              if (transition_a5 == 0)
                {
                  if (((rx_a5 ^ rx_a5_last) & rx_a5))
                    {
                      transition_a5 = i;
                    }
                  else
                    {
                      rx_a5_last = rx_a5;
                    }
                }
              else
                {
                  if ((i - transition_a5) == 4)
                    {
                      if (!((rx_a5 ^ rx_a5_last) & rx_a5))
                        {
                          /* Keep looking for transition */

                          transition_a5 = 0;
                          rx_a5_last = rx_a5;
                        }
                    }
                }

              if ((transition_a5 != 0) && (transition_ck != 0))
                {
                  if ((i == transition_a5) || (i == transition_ck))
                    {
                    }
                }

              i++;
            }

          if (transition_a5 > transition_a5_max)
            {
              transition_a5_max = transition_a5;
            }

          if ((transition_a5 != 0) && (transition_ck != 0) && (j < 8))
            {
              transition_ck_array[j] = transition_ck;
            }
        }

      uint32_t min_diff = 0xff;
      uint32_t min_refclk = 0x8;

      if (transition_a5_max < 5)
        {
          a5_offset_fail = a5_offset_fail | 1;
        }

      for (uint32_t k = 0; k < 8; k++)
        {
          if (transition_a5_max >= transition_ck_array[k])
            difference[k] = transition_a5_max - transition_ck_array[k];
          else
            difference[k] = 0xff;

          if (difference[k] < min_diff)
            {
              min_diff = difference[k];
              min_refclk = k;
            }
        }

      if (min_diff == 0xff)
        {
          a5_offset_fail = a5_offset_fail | 1;
        }

      if (min_refclk == 0x8)
        {
          /* If ADDCMD training fails due to extremely low frequency,
           * use PLL to provide offset.
           */

          a5_offset_fail = a5_offset_fail | 4;
        }

      if (a5_offset_fail == 0)
        {
          refclk_phase = ((refclk_offset + min_refclk) & 0x7) << 2;

          putreg32((0x00004003 | bclk_phase | bclk90_phase | refclk_phase),
                   MPFS_IOSCB_DDR_PLL_PHADJ);
          putreg32((0x00000003 | bclk_phase | bclk90_phase | refclk_phase),
                   MPFS_IOSCB_DDR_PLL_PHADJ);
          putreg32((0x00004003 | bclk_phase | bclk90_phase | refclk_phase),
                   MPFS_IOSCB_DDR_PLL_PHADJ);

          /* Load INDLY */

          putreg32(0x0, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_DIRECTION_REG1);

          putreg32(0x0, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);
          putreg32(0x180000, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);
          putreg32(0x0, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);

          /* Load OUTDLY */

          putreg32(0x180000,
                   MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_DIRECTION_REG1);
          putreg32(0x0, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);
          putreg32(0x180000, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);
          putreg32(0x0, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_LOAD_REG1);

          for (uint32_t m = 0; m < min_diff; m++)
            {
              putreg32(0x0, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_MOVE_REG1);
              putreg32(0x180000,
                       MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_MOVE_REG1);
              putreg32(0x0, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_MOVE_REG1);
            }

          putreg32(0x0, MPFS_CFG_DDR_SGMII_PHY_EXPERT_DLYCNT_DIRECTION_REG1);
          putreg32(0x0, MPFS_CFG_DDR_SGMII_PHY_EXPERT_MODE_EN);
        }
      else
        {
          if (a5_offset_fail & 0x1)
            {
              if (init_del_offset < 0xff)
                {
                  /* If transition_a5 too low, increase indly offset on CK
                   * and CA and retrain
                   */

                  init_del_offset += (transition_a5_max) + 5;
                }
              else
                {
                  break;
                }
            }
        }
    }

  /* Set VREF back to configured value */

  putreg32(0, MPFS_IOSCB_BANK_CNTL_DDR_SOFT_RESET);
  mpfs_wait_cycles(10);

  dpc_vals = (getreg32(MPFS_CFG_DDR_SGMII_PHY_DPC_BITS) & 0xfffc0fff) |
              (ca_vref << 12) | (0x1 << 18);
  putreg32(dpc_vals, MPFS_CFG_DDR_SGMII_PHY_DPC_BITS);

  mpfs_wait_cycles(10);

  putreg32(0x01, MPFS_IOSCB_BANK_CNTL_DDR_SOFT_RESET);
  mpfs_wait_cycles(10);

  /* Set CA DRV back to configured value */

  putreg32(ca_drv, MPFS_CFG_DDR_SGMII_PHY_RPC1_DRV);
}