/**     ll_post_utils_npa_56x4.c
 **
 **   This file contains a variety of low-level phase two POST supporting
 ** routines for the NPA-56x4 AMC platform.
 **
 ** Author: Ted Schmitz
 **
 ** Created: December 16, 2008
 **
 **/

#if !defined(__U_BOOT__)
#include <stdio.h>    /* standard library headers */
#else
#include "common.h"  /* common library headers for Uboot environment */
#endif

#include "ll_bist.h"
#include "ll_serial_io.h"
#include "diag_mon/errhand.h"
#include "diag_mon/ascii_functions.h"
#include "diag_mon/errcodes.h"
#include "diag_mon/diag_mon.h" /* def for "TRUE" & "FALSE" */
#include "ll_post_utils.h"
#include "ll_post.h"

/* external definitions */
extern unsigned long product_lookup_table[];
extern void mon_write_miiphy(int phy_adrs, int phy_reg, unsigned short data);
extern void mon_dump_marvell_port_regs(void);
extern void mon_dump_marvell_phy_regs(void);
extern void mon_dump_marvell_global_regs(void);
extern void mon_disp_port_stats(int port);
extern void mon_write_marvell_reg(int dev, int reg, unsigned short data);
extern void mon_dump_miiphy_regs(int phy_adrs);

/* local definitions */

/**   The following table contains a simulated hardware configuration
 ** register for each platform this code supports.
 **   The following table contains pointers to all the possible sign on messages
 ** that can be displayed by this code along with their matching Product
 ** Identifiers as read from the TLV entries.  This table also contains all the
 ** simulated hardware availability registers for each product type.
 **
 ** Field Description
 **  Table Header Information
 **  1st  The very first 32-bit entry contains the number or 32-fields in
 **       each of the records following the header information in this table.
 **  Each Record
 **  1st  32-bit product identification as read from the TLV product ID entry.
 **  2nd  32-bit simulated hardware options register.  The bits in this field
 **       represent various hardware options added/deleted from the original
 **       hardware design for this model.
 **  3rd  32-bit pointer to the ASCII sign-on message associated with this
 **       product id.
 **       NOTE:  Only an offset can be stored here because the Uboot code is
 **              compiled as completely relocatable code.  Therefore, the
 **              assembler will not allow any absolute addresses.
 **
 **  WARNING!!! If you change the number of fields in each of the entries below,
 **             then you must update the symbol "NUM_FIELDS_IN_PROD_TABLE"
 **             accordingly.
 **/
unsigned long product_lookup_table[] = 
  {
  NUM_FIELDS_IN_PROD_TABLE,

  PROD_TYPE_NPA_5644,DEFAULT_HW, (unsigned long) NPA_5644_SO_MSG,
  PROD_TYPE_NPA_5654,DEFAULT_HW, (unsigned long) NPA_5654_SO_MSG,

  LOOK_UP_TABLE_TERMINATION
  };

/*****************************************************************************
 **            Save Error Information In Non-Volatile Storage
 **
 **   This function save appropriate error data in non-volatile storage.  Not
 ** all informatoin will fit in non-volatile storage.  This function saves
 ** the data for which there is room.
 **
 ** INPUT - "sf" = pointer to Saved Failure information structure containing
 **         all of the error data for the current failure.
 **
 ** OUTPUT - Appropriate error information will be written to the diagnostic
 **          area of the EEPROM.
 **
 ** RETURN - Nothing
 **
 ****************************************************************************/
void save_error_info_in_nv_storage(struct save_fail * sf)
{
  unsigned char bwork;                  /* 8-bit working space */
  unsigned long lwork;                  /* 32-bit working space */
  uint64_t llwork;                      /* 64-bit working space */
  //int failure_count;                    /* working storage for total fails */
  //int results;                          /* results of some function calls */

#if 0 /* old way depended on test no.  But some failures do not
         have associated test numbers */
  /* get the current test number saved in nv storage */
  if (read_select_data_from_seeprom_timed(&bwork, FF_TEST_NO_OS, 
      TOT_TEST_NUM_BYTES) == PROBLEM_DETECTED)
    { 
    /**   Cannot call the error handler because the error handler 
     ** called this function.
     **/
    printf("\nProblems reading data from SEEPROM in "
           "'save_error_info_in_nv_storage()' function!\n");
    /*report_seeprom_problem(ES1200);*/    /* log the error & exit */
    return;
    }
#else
  /* get the current First Fail error code from nv storage */
  if (read_select_data_from_seeprom_timed((unsigned char *)&lwork, 
        FF_ERROR_CODE_OS, TOT_ERR_CODE_BYTES) == PROBLEM_DETECTED)
    { 
    /**   Cannot call the error handler because the error handler 
     ** called this function.
     **/
    printf("\nProblems reading data from SEEPROM in "
           "'save_error_info_in_nv_storage()' function!\n");
    /*report_seeprom_problem(ES1200);*/    /* log the error & exit */
    return;
    }
#endif
  /**
   **   Save the error information if there is no first fail information
   ** currently saved.  Else, save data in last fail field(s).
   **/
  //if (bwork == 0) /* old way */
  if (lwork == 0)
    {
    /* save test number that failed */
    bwork = (unsigned char) sf->test_no; /* convert test number */
    if (write_select_data_to_seeprom_timed(&bwork, FF_TEST_NO_OS, 
        TOT_TEST_NUM_BYTES) == PROBLEM_DETECTED)
      {
      printf("\nProblems writing data to SEEPROM in "
             "'save_error_info_in_nv_storage()' function!\n");
      /*report_seeprom_problem(ES1250);*/     /* log the error & exit */
      return;
      }

    /* save error number that failed */
    if ((sf->eh.call_type & EI_ERROR_CODE_AVAIL) == EI_ERROR_CODE_AVAIL)
      {
      lwork = (unsigned long) ((sf->eh.error_stop << 16) & 0xFFFF0000);
      lwork |= (unsigned long) (sf->eh.sup_error_stop & 0x0000FFFF);
      }
    else
      lwork = 0; /* no error code so set it to zero */

    if (write_select_data_to_seeprom_timed((unsigned char *) &lwork, 
        FF_ERROR_CODE_OS, TOT_ERR_CODE_BYTES) == PROBLEM_DETECTED)
      {
      printf("\nProblems writing data to SEEPROM in "
             "'save_error_info_in_nv_storage()' function!\n");
      /*report_seeprom_problem(ES1250);*/     /* log the error & exit */
      return;
      }

    /* save 64-bit error address that failed */
    if ((sf->eh.call_type & EI_ERR_ADRS_AVAIL) == EI_ERR_ADRS_AVAIL)
      llwork = sf->eh.fail_adrs;
    else
      llwork = 0; /* no error address so set it to zero */

    if (write_select_data_to_seeprom_timed((unsigned char *) &llwork, 
        FF_ERR_ADRS_OS, TOT_FAIL_ADRS_BYTES) == PROBLEM_DETECTED)
      {
      printf("\nProblems writing data to SEEPROM in "
             "'save_error_info_in_nv_storage()' function!\n");
      /*report_seeprom_problem(ES1250);*/     /* log the error & exit */
      return;
      }

    /* save 64-bit good data expected at at error address */
    if ((sf->eh.call_type & EI_EXP_REC_DATA_AVAIL) == EI_EXP_REC_DATA_AVAIL)
      llwork = sf->eh.data_exp[0];
    else
      llwork = 0; /* no expacted data so set it to zero */

    if (write_select_data_to_seeprom_timed((unsigned char *) &llwork, 
        FF_EXP_DATA_OS, TOT_EXP_DATA_BYTES) == PROBLEM_DETECTED)
      {
      printf("\nProblems writing data to SEEPROM in "
             "'save_error_info_in_nv_storage()' function!\n");
      /*report_seeprom_problem(ES1250);*/     /* log the error & exit */
      return;
      }

    /* save 64-bit bad data read from error address */
    if ((sf->eh.call_type & EI_EXP_REC_DATA_AVAIL) == EI_EXP_REC_DATA_AVAIL)
      llwork = sf->eh.data_rec[0];
    else
      llwork = 0; /* no received data so set it to zero */

    if (write_select_data_to_seeprom_timed((unsigned char *) &llwork, 
      FF_REC_DATA_OS, TOT_REC_DATA_BYTES) == PROBLEM_DETECTED)
      {
      printf("\nProblems writing data to SEEPROM in "
             "'save_error_info_in_nv_storage()' function!\n");
      /*report_seeprom_problem(ES1250);*/     /* log the error & exit */
      return;
      }
#if 0 /* moved to end of this function */
    /* re-calc & store new check sum */
    if (calc_write_seeprom_csum_timed() == PROBLEM_DETECTED)
      {
      printf("\nProblems writing check sum to SEEPROM in "
             "'save_error_info_in_nv_storage()' function!\n");
      /*report_seeprom_problem(ES1260);*/     /* log the error & exit */
      return;
      }
#endif
    }

  /**
   **  Save every failure error code in last fail field(s)
   **/

  /* save error number that failed */
  if ((sf->eh.call_type & EI_ERROR_CODE_AVAIL) == EI_ERROR_CODE_AVAIL)
    {
    lwork = (unsigned long) ((sf->eh.error_stop << 16) & 0xFFFF0000);
    lwork |= (unsigned long) (sf->eh.sup_error_stop & 0x0000FFFF);
    }
  else
    lwork = 0; /* no error code so set it to zero */

  if (write_select_data_to_seeprom_timed((unsigned char *) &lwork, 
      LF_ERROR_CODE_OS, TOT_ERR_CODE_BYTES) == PROBLEM_DETECTED)
    {
    printf("\nProblems writing data to SEEPROM in "
           "'save_error_info_in_nv_storage()' function!\n");
    /*report_seeprom_problem(ES1250);*/     /* log the error & exit */
    return;
    }


#if 0 /* don't use this logic any more for reasons described below */

  /**   NOTE:  I added this logic to increment the total failure counter
   **          but it really is better to let the diagnostic monitor's
   **          "start_testing()" function handle all counters for both 
   **          Phase I & II tests. Also if I leave this logic in place the 
   **          total fails ends up getting incremented twice for the same 
   **          failure sometimes.
   **/

  /**
   **  Increment the total failures
   **/
  results = get_total_fails_count(&failure_count, ES2610); /* get fails */
  failure_count++;                                         /* inc tot fails */
  results = save_total_fails_count(failure_count, ES2620); /* save fails */

#else /* correct way to do this */

  /* csum is calculated in "save_total_fails_count()" call above */
  /* re-calc & store new check sum */
  if (calc_write_seeprom_csum_timed() == PROBLEM_DETECTED)
    {
    printf("\nProblems writing check sum to SEEPROM in "
           "'save_error_info_in_nv_storage()' function!\n");
    /*report_seeprom_problem(ES1260);*/     /* log the error & exit */
    return;
    }
#endif

}

/****************************************************************************
 **                   Check If Hardware Option Is Available
 **
 **    This routine reads up the TLV entry containing the Product Identifier, 
 ** searches a table to find the proper simulated hardware options register, 
 ** compares the hardware mask to the hardware options supplied by the calling 
 ** code and returns the appropriate flag.  This function essentially performs 
 ** the job of a hardware options register.
 **
 ** INPUT - "hw_mask" will contain the hardware check mask.  This mask will 
 **         contain bits associated with the hardware feature the calling code
 **         is looking for.
 **
 ** OUTPUT - Nothing
 **
 ** RETURNS - "TRUE" will be returned if the bit(s) in "hw_mask" are set in the
 **            simulated hardware options register for this platform. "FALSE"
 **            returned otherwise.
 **
 ****************************************************************************/
int this_hw_is_available_c(unsigned long hw_mask)
{
  unsigned long tlv_prod_id;            /* value read from TLV prod id entry */
  int prod_id;                          /* prod id returned to calling code */
  unsigned long * table_ptr;            /* pointer to product look up table */
  unsigned long record_length;          /* number of 32-bit words in each record
                                           in the product id table */
  int i;

  prod_id = INVALID_ID;                 /* get rid of warning */

  /* get saved TLV prod id from digs SEEPROM space */
  if (read_select_data_from_seeprom_timed((unsigned char *) &tlv_prod_id, 
      PROD_ID_OS, PROD_ID_LEN) == PROBLEM_DETECTED)
    {
    report_seeprom_problem(ES1860);     /* log the error & exit */
    return prod_id;
    }

  table_ptr = (unsigned long *) product_lookup_table; /* get adrs of table */
  record_length = table_ptr[PROD_TABLE_REC_LEN_IDX]; /* get len of ea record */

  /* search product table for a match of this bd's TLV product id entry */
  for (i = PROD_TABLE_1ST_REC_IDX; ; i += record_length)
    {
    if (table_ptr[i+PROD_TABLE_ID_FIELD_IDX] == tlv_prod_id)
      {
      if ((table_ptr[i+PROD_TABLE_SIM_HWR_IDX] & hw_mask) == hw_mask)
        return TRUE;             /* hw match found. return true results */
      else
        return FALSE;
      }
    
    if (table_ptr[i] == 00) /* if reached the end of the table, exit */
      return FALSE;
    }

  return FALSE;                  /* no hw match found. return false results */

}

/****************************************************************************
 **                   Get Product Identification Value
 **
 **   This function performs all the necessary steps to determine
 ** the hardware on which the code is running, then returns the appropriate
 ** product id to the calling code.
 **
 ** INPUT - Nothing
 **
 ** OUTPUT - Nothing
 **
 ** Return - Product idetification
 **
 ****************************************************************************/
int get_prod_id(void)
{
  /* unsigned long tlv_prod_id;            value read from TLV prod id entry */
  int prod_id;                          /* prod id returned to calling code */
  unsigned char version;                /* FPGA version number */

  prod_id = INVALID_ID;                 /* get rid of warning */

  version = *BOARD_VERSION_ID_REG;      /* get FPGA version number */

  if (version == FPGA_VER_TNPA_5644)
    prod_id = PROD_TYPE_NPA_5644;
  else
    prod_id = PROD_TYPE_NPA_5654;

#if 0
  /* get saved TLV prod id from digs SEEPROM space */
  if (read_select_data_from_seeprom_timed((unsigned char *) &tlv_prod_id, 
      PROD_ID_OS, PROD_ID_LEN) == PROBLEM_DETECTED)
    {
    report_seeprom_problem(ES1870);     /* log the error & exit */
    return prod_id;
    }

  prod_id = (int) tlv_prod_id;
#endif

  return prod_id;

}

/*****************************************************************************
 **       Check To See If Low-Level phase 2 POST Is To Be Executed
 **
 **    This routine checks all the appropriate registers and parameters to
 ** to determine if low-level POST should be executed.  This function does
 ** take the platform, on which this code is running, into consideration when
 ** necessary when making the go/no-go decision to run diagnostics.
 **
 **   --- OLD WAY ---
 ** INPUT - Global "test_modes" must contain valid data prior to calling 
 **         this function.
 **   --- NEW WAY ---
 ** INPUT - "test_modes" must contain a valid copy of the test modes parameter
 **         from the diagnostic area of the SEEPROM prior to calling 
 **         this function.
 **
 ** OUTPUT - Nothing
 **
 ** RETURNS - "TRUE" will be returned if diags should be executed, "FALSE"
 **           will be returned otherwise.
 **
 ****************************************************************************/
int should_diags_be_executed(unsigned short test_modes)
{
  int return_value;                     /* value retured to calling code */
  unsigned long prod_id;                /* storage for retrieving prod id */
  unsigned long * table_ptr;            /* pointer to product look up table */
  unsigned long record_length;          /* number of 32-bit words in each record
                                           in the product id table */
  int prod_id_recognized;               /* prod id is recognized by BIST */
  int i;

  return_value = TRUE;                  /* init to "run diags" */

  if ((test_modes & TM_DISABLE_POST) == TM_DISABLE_POST)
    {
    printf("\n\nLow-level POST is disabled! Unable to execute tests!\n");
    printf("Use 'TModes' command to enable tests!\n\n");
    return_value = FALSE;               /* do not run diags */

    /* if "R" key was pressed, force execution of tests one time */
    if((test_modes & TM_FORCE_TESTING) == TM_FORCE_TESTING)
      {
      printf("\n'R' key pressed.  Forcing execution of tests!\n");
      return_value = TRUE;
      }
    }
  else
    { /* tests are NOT disabled.  make sure we have a valid product id */
    if ((read_select_data_from_seeprom_timed((unsigned char *) &prod_id,
                                             PROD_ID_OS, PROD_ID_LEN))
        == PROBLEM_DETECTED)
      {
      report_seeprom_problem(ES1810);     /* log the error & exit */
      return_value = FALSE;               /* do not run diags */
      }
    else
      {
      if (prod_id == TLV_NOT_AVAIL)
        { /* product id not available. unable to run tests */
        printf("\n\nUnable to run phase two diagnostic tests because there\n");
        printf("is no product id parameter available in SEEPROM!\n\n");
        return_value = FALSE;               /* do not run diags */
        }
      else
        { /* product id is available.  make sure it is one we recognize or we 
             can't run tests */

        /* search product table for a match of this bd's TLV product id entry */
        table_ptr = (unsigned long *) product_lookup_table; /* get adr of table */
        record_length = table_ptr[PROD_TABLE_REC_LEN_IDX]; /* len of ea record */

        prod_id_recognized = FALSE;
        for (i = PROD_TABLE_1ST_REC_IDX; table_ptr[i] != 00; i += record_length)
          {
          if (table_ptr[i+PROD_TABLE_ID_FIELD_IDX] == prod_id)
            prod_id_recognized = TRUE;   /* flag we found the record */
          }

        if (prod_id_recognized == TRUE)
          return_value = TRUE;
        else
          {
          printf("\n\nUnable to run phase two diagnostic tests because the\n");
          printf("product id parameter available in SEEPROM is not recognized\n"
                 "by low-level diagnostics!\n"
                 "Product id in SEEPROM = %08lX\n",prod_id);
          return_value = FALSE;
          }
        }
      }
    }

  return return_value;
}

/*****************************************************************************
 **                  High Level Spin Wheel Routine
 **
 **    This routine spins a wheel at the current cursor position to indicate 
 ** the test program is busy and not hung and also lights the next LED in
 ** succession.  
 **
 ** INPUT - Nothing
 **
 ** OUTPUT - One of 4 parts of the spinning wheel is written to the RS232 
 **          port and all the fron panel LEDs are cleared then the next one
 **          in sequence is lit.
 **
 ** RETURNS - Nothing
 **
 ****************************************************************************/
void hl_spin_wheel(void)
{
  DECLARE_GLOBAL_DATA_PTR;
  u32 * wheel_count_ptr;                /* ptr to sratch wheel counter var */
  u16 * cur_test_modes_ptr;             /* ptr to sratch cur test modes */
#if 0
  static int wheel_count = 0;           /* counter to identify which part of the
                                           wheel is to be displayed next. */
#endif 
  char next_wheel_char;                 /* next part of wheel to be displayed */
  int result;                           /* result of various func calls */
  static unsigned short cur_test_modes = 0; /* private copy of test modes */
  u16 local_test_modes;                 /* temp local copy of test modes */
  //char bwork;                           /* 8-bit working register */
  //u8 disp_flag;                         /* flag to determine of wheel spins */

  /* set up temp scratch RAM variable pointers */
  wheel_count_ptr = (u32 *) DCACHE_NEXT_WHEEL_POS;
  cur_test_modes_ptr = (u16 *) CUR_TEST_MODES;

  //printf("\nIn .'hl_spin_wheel()...'\n");
  /* first clear all the LEDs */
  //bwork = SYS_LED_REG;                  /* read cur contents of LED reg */
  // bwork &= (~ALL_SYS_LEDS_MASK & 0xFF); /* clear all sys led bits */
  //SYS_LED_REG = bwork;                  /* turn off all sys LEDs */
  if (*wheel_count_ptr == 0)
    {
    next_wheel_char='|';                /* next part of wheel to be dsplyd */
    //bwork = SYS_LED_REG;                /* read cur contets of LED reg */
    //bwork |= HB_LED;                    /* set LED bit */
    //SYS_LED_REG = bwork;                /* turn on 1st LED (Heart Beat) */
    }
  else if (*wheel_count_ptr == 1)
    {
    next_wheel_char='/';                /* next part of wheel to be dsplyd */
    //bwork = SYS_LED_REG;                /* read cur contets of LED reg */
    //bwork |= FAULT_LED;                 /* set LED bit */
    // SYS_LED_REG = bwork;                /* turn on 2nd LED (Fault) */
    }
  else if (*wheel_count_ptr == 2)
    {
    next_wheel_char='-';                /* next part of wheel to be dsplyd */
    //bwork = SYS_LED_REG;                /* read cur contets of LED reg */
    //bwork |= GOOD_LED;                  /* set LED bit */
    //SYS_LED_REG = bwork;                /* turn on 3rd LED (Good) */
    }
  else
    {
    next_wheel_char=0x5C;   /* back slash next part of wheel to be dsplyd */
    //bwork = SYS_LED_REG;                /* read cur contets of LED reg */
    //bwork |= POWER_LED;                  /* set LED bit */
    //SYS_LED_REG = bwork;                /* turn on 4th LED (Power) */
    }

  /**   To increase speed of this function, only retrieve test modes from
   ** non-volatile storage when the global variable changed from
   ** the last time we executed this function.
   **/
  if ((gd->flags & GD_FLG_RELOC) == GD_FLG_RELOC)
    { /* take this path if we are running from RAM */
    if (cur_test_modes != test_modes)
      {
      /* get the test mode from non-volatile storage */
      result = read_select_data_from_seeprom_timed((unsigned char *) &test_modes,
                                                 TEST_MODE_OS, TOT_TMODE_BYTES);
      if (result == PROBLEM_DETECTED)
        {
        report_seeprom_problem(ES1800);   /* log the error & exit */
        return;
        }

      cur_test_modes = test_modes;        /* keep a copy for future use */
      }
    local_test_modes = cur_test_modes; /* get test modes from global RAM */
    }
  else
    { /* take this path if we are still running from FLASH */
    local_test_modes = *cur_test_modes_ptr; /* get test modes from temp ram */
    }

  /* do not spin wheel if "busy indicato}}}rs" are disabled */
  if ((local_test_modes & TM_DIS_BUSY_IND) != TM_DIS_BUSY_IND)
    {
#ifdef STANDALONE
      putchar(next_wheel_char); /* displ next part of wheel */
      putchar(0x08);           /* backspace over the char */
#else
    llputchar_when_ready(next_wheel_char);/* displ next part of wheel */
    llputchar_when_ready(0x08);           /* backspace over the char */
#endif
    }

  *wheel_count_ptr += 1;

  if (*wheel_count_ptr > 3)               /* reset wheel count if reach end */
    *wheel_count_ptr = 0;
}

