/**      phase1_test_manager.c
 **
 ** Author: Ted Schmitz
 ** Date of original creation March 31, 2009
 **
 **    This file contains logic used to determine if any low-level POST,
 ** Phase I tests are selected for execution and if so execute them now.
 **
 **   NOTE:  Phase I tests execute after needed hardware is initialized, such
 ** as the SDRAM controller, but before the boot code is running from RAM.
 ** Therefore, there are no global variables available (except those created
 ** and managed through GOT) and no static variables.  All variables must be 
 ** placed in registers (like the dual-port RAM in the FPGA) or on the stack 
 ** (which is created in DCACHE).
 **
 **/
#include "common.h"  /* common library headers for Uboot environment */

#include "../ll_bist.h"
#include "errcodes.h"
#include "../ll_post.h"
#include "ascii_functions.h"
#include "diag_mon.h"
#include "tests.h"
#include "../ll_post_utils.h"
#include "phase1_test_manager.h"
#include "../phase1_error_handler.h"

#include "cvmx.h"

#define CVMX_MF_STATUS_REG(val)   asm volatile ("mfc0 %[rt],$12,0" :  [rt] "=d" (val):)
#define CVMX_MF_DSAVE_REG(val)   asm volatile ("dmfc0 %[rt],$31,0" :  [rt] "=d" (val):)
#define CVMX_MT_DSAVE_REG(val)  asm volatile ("dmtc0 %[rt],$31,0" : : [rt] "d" (val))
/****************************************************************************   
 **                     Phase One Tests Manager
 **
 **   This function determins if low-level testing is enabled and if so
 ** executes any Phase one tests that may be selected for execution.  This 
 ** function also determines the proper course of action if a Phase I test
 ** fails.
 **
 **   To do this the following steps are performed:
 **  1 - If this is a power up or the "TM_CONT_TEST" test mode is active,
 **      display the 1st BIST count-down message.
 **  2 - Process any keys that may have been typed during the count-down
 **      period.
 **  3 - Read up the "test modes" field from the SEEPROM and see if 
 **      diagnostics is disabled.  If so, return to calling code immediately.
 **  4 - Go through the global test table and build a bit-list of Phase one
 **      tests.
 **  5 - Read up the "test selection" bits from the SEEPROM.
 **  6 - Compare the list of phase I tests to the test selection bits and
 **      exit if no Phase I tests are selected for execution.
 **  7 - Call each selected phase one test.
 **      As each test returns from execution, check to see if any failures
 **      were detected (by checking the "TM_ERR_PENDING" bit in the test modes
 **      field).
 **      If the test passed, increment the appropriate test "pass" counter
 **      and call the next phase I test selected for execution.
 **      (NOTE: Incrementing the "Pass counter" can only be done once we have 
 **             added the extra space in in the SEEPROM.)
 **      If the test failed, read up the "Error Modes" field from the SEEPROM
 **      and deal with the failure appropriately as described below:
 **     SKIP_ON_ERROR
 **        Log the failure and call the next phase I test selected for 
 **        execution.
 **      CONT_ON_ERROR
 **        Log the failure and call the next phase I test selected for 
 **        execution.
 **      For all other error modes, log the failure and return to the calling
 **      code immediately. The BIST monitor logic will handle the error 
 **      appropriately.
 **
 **  NOTE:  If a test fails, it is expected to save the appropriate error 
 **         information in non-volatile storage and display "Fail" after the 
 **         test sign-on message before returning to this function.  Thw word
 **         "Fail" is usually display by the function that is called to save
 **         all the error information in non-volatile storage.
 **
 **         If the test passes it is responsible for displaying "Pass" at the
 **         end of the test sign-on message. Can call a common function to do
 **         this.
 **
 ** INPUT - Nothing
 **
 ** OUTPUT - The SEEPROM test statistics will be updated if any tests are
 **          executed and/or if any tests fail.
 **
 ** RETURN - Nothing
 **
 *****************************************************************************/
void execute_phase1_tests(void)
{
  char key_pressed;            /* key pressed during count-down msg */
  int result;                  /* results of various func calls, etc. */
  unsigned short test_modes;   /* test modes retrieved from SEEPROM */
  unsigned long sel_tests;     /* copy of test selection bits */
  unsigned long p1_tests;      /* calc list of available phase 1 tests */
  unsigned char error_mode;    /* error mode read from SEEPROM */
  unsigned long cur_loop;      /* cur exec count read from SEEPROM */
  u64 llwork;                  /* 64-bit working variable */
  int index;                   /* test table index */
  u16 * test_modes_ptr;        /* ptr to sratch cur test modes */
  struct tests * tt = test_table;	/* pointer to tests table */
  void (*test_ptr)(int test_no, int exec_count); /* pointer to relocated test
                                                    function */

  check_diag_area();        /* make sure diag data is valid to start with */

  result = read_select_data_from_seeprom_timed((unsigned char *) &test_modes,
                                               TEST_MODE_OS, TOT_TMODE_BYTES);

  //test_modes |= TM_ESC_PRESSED;    /* DEBUG set "esc key pressed" flag */

  /* Clear some flags */
  test_modes &= ~(TM_ESC_PRESSED | TM_M_KEY_PRESSED | TM_FORCE_TESTING |
                  TM_ERR_PENDING);

  CVMX_MF_DSAVE_REG(llwork); /* get contents of "DESAVE" reg */
  //printf("\nBDebug Save register = %016qx\n",llwork);
  if (llwork == PU_FLAG_WARM_RESET_VAL)
    { /* this is a warm reset */
    printf("\nThis is a warm reset!\n");
    test_modes &= ~TM_PU_RESET_TYPE; /* clear Power up type bit */
    }
  else
    { /* this is a power up so flag it as such */
    printf("\nThis is a power-up reset!\n");
    test_modes |= TM_PU_RESET_TYPE; /* set Power up type bit */
    llwork = PU_FLAG_WARM_RESET_VAL;
    CVMX_MT_DSAVE_REG(llwork);      /* set the power up value for next time */
    CVMX_SYNCW;
    }

  /* write "reset type" flag to seeprom */
  result = write_select_data_to_seeprom_timed((unsigned char *) &test_modes,
                                              TEST_MODE_OS, TOT_TMODE_BYTES);
  if (result == PROBLEM_DETECTED)
    {
    report_seeprom_problem(ES2600);     /* log the error & exit */
    return;
    }

  /* re-calc & store new check sum */
  if (calc_write_seeprom_csum_timed() == PROBLEM_DETECTED)
    report_seeprom_problem(ES2600);     /* log the error & exit */

  //CVMX_MT_DSAVE_REG(llwork);
  CVMX_SYNCW;
  // printf("\nADebug Save register = %016qx\n",llwork);

  //check_diag_area();/* MOVED up - make sure diag data is valid to start with */

  /** (1) - If this is a power up or the "TM_CONT_TEST" test mode is active
   ** then display 1st BIST count-down message & look for input.  Else do not 
   ** display countdown message on warm restarts if the "TM_CONT_TEST" 
   ** test mode is not active.
   **/
  if (((test_modes & TM_PU_RESET_TYPE) == TM_PU_RESET_TYPE) ||
      ((test_modes & TM_CONT_TEST) == TM_CONT_TEST))
    key_pressed = disp_countdown_msg(BIST_P1_CD_MSG);
  else
    {
#ifdef FORCE_P1_POWER_UP_MODE
    /* force display of P1 countdown msg when spec compile flag enabled */
    key_pressed = disp_countdown_msg(BIST_P1_CD_MSG);
#else
    return;          /* this is the normal logic */
#endif
    }

#if 0
  printf("key_pressed = %02X\n",key_pressed);
  printf("ENTER_MENU_CHAR =  %02X\n",ENTER_MENU_CHAR);
#endif

  /* (2) -  Process any keys that may have been typed during the count-down
            period. */
  if (key_pressed == ESCAPE_CHAR)
    { /* Escape pressed.  Log it and exit */
      /* retrieve curent test modes value */
    result = read_select_data_from_seeprom_timed((unsigned char *) &test_modes,
                                               TEST_MODE_OS, TOT_TMODE_BYTES);

    test_modes |= TM_ESC_PRESSED;    /* set "esc key pressed" flag */

    /* write it back to SEEPROM */
    write_select_data_to_seeprom_timed((unsigned char *) &test_modes,
                                       TEST_MODE_OS, TOT_TMODE_BYTES);

    /* re-calc & store new check sum */
    if (calc_write_seeprom_csum_timed() == PROBLEM_DETECTED)
      report_seeprom_problem(ES2600);     /* log the error & exit */

    return;                /* skip all tests - exit */
    }
  else if ((key_pressed | 0x20) == ENTER_MENU_CHAR)
    { /* 'm' pressed.  Log it and exit */
      //printf("m key pressed in phase I code \n");
      /* retrieve curent test modes value */
    result = read_select_data_from_seeprom_timed((unsigned char *) &test_modes,
                                               TEST_MODE_OS, TOT_TMODE_BYTES);

    test_modes |= TM_M_KEY_PRESSED;    /* set "m key pressed" flag */
    test_modes_ptr = (u16 * ) CUR_TEST_MODES; /* set up ptr to tmp glob var */
    *test_modes_ptr = test_modes;      /* save a copy in glob scratch area */

    //printf("Writing test_modes = %02X\n",test_modes);
    /* write it back to SEEPROM */
    write_select_data_to_seeprom_timed((unsigned char *) &test_modes,
                                       TEST_MODE_OS, TOT_TMODE_BYTES);

    /* re-calc & store new check sum */
    if (calc_write_seeprom_csum_timed() == PROBLEM_DETECTED)
      report_seeprom_problem(ES2600);     /* log the error & exit */

    return;            /* exit to enter BIST monitor later in boot process */
    }
  else if ((key_pressed | 0x20) == RUN_TESTS_CHAR)
    { /* 'r' pressed.  Log it and continue */
      /* retrieve curent test modes value */
    result = read_select_data_from_seeprom_timed((unsigned char *) &test_modes,
                                               TEST_MODE_OS, TOT_TMODE_BYTES);

    test_modes |= TM_FORCE_TESTING;    /* set "r key pressed" flag */

    /* write it back to SEEPROM */
    write_select_data_to_seeprom_timed((unsigned char *) &test_modes,
                                       TEST_MODE_OS, TOT_TMODE_BYTES);

    /* re-calc & store new check sum */
    if (calc_write_seeprom_csum_timed() == PROBLEM_DETECTED)
      report_seeprom_problem(ES2600);     /* log the error & exit */
    }

  /* (3) - get the test mode from non-volatile storage and call the common
           function "should_diags_be_executed()" to determine if diagnostics
           is a go.
  */
  result = read_select_data_from_seeprom_timed((unsigned char *) &test_modes,
                                               TEST_MODE_OS, TOT_TMODE_BYTES);
  if (result == PROBLEM_DETECTED)
    {
    report_seeprom_problem(ES2600);   /* log the error & exit */
    return;
    }

  /*   If low-level POST testing is disabled exit */
  if (should_diags_be_executed(test_modes) == FALSE)
    return;

  /* (4) - Build a list of available phase I tests from the test table */
  p1_tests = 0;
  for (index = 0; tt[index].test_number !=0; index++)
    {
    if (tt[index].test_type == TT_P1)
      p1_tests |= (1 << index);
    }

  //printf("\nPhase I test bits = %08lX\n",p1_tests);
  /* (5) - read test selection bits */
  result = read_select_data_from_seeprom_timed((unsigned char *) &sel_tests,
                                               CUR_SEL_TESTS_OS, TEST_SEL_LEN);
  if (result == PROBLEM_DETECTED)
    {
    report_seeprom_problem(ES2600);     /* log the error & exit */
    return;
    }

  /*  At least one phase one test must be selected or exit */
  if ((sel_tests & p1_tests) == 0)
    {
    printf("\nAll low-level phase one POST tests are disabled!\n");
    printf("Use 'SSubtests' command to enable tests!\n\n");
    return;
    }

  //printf("\nTest select bits = %08lX\n",sel_tests);

  result = read_select_data_from_seeprom_timed((unsigned char *)&cur_loop, 
                                               CURRENT_LOOP_COUNT_OS, 
                                               LOOP_COUNT_LEN);
  if (result == PROBLEM_DETECTED)
    {
    report_seeprom_problem(ES2600);     /* log the error & exit */
    return;
    }

  /* reset the current loop count if this is a power up */
  if ((test_modes & TM_PU_RESET_TYPE) == TM_PU_RESET_TYPE)
    {
      //printf("\nReseting loop count to 1! test_modes = %04X\n",test_modes);
    cur_loop = 1;      /* init current testing loop. always start at 1 */
    if (write_select_data_to_seeprom_timed((unsigned char *) &cur_loop,
        CURRENT_LOOP_COUNT_OS, LOOP_COUNT_LEN)== PROBLEM_DETECTED)
      {
      report_seeprom_problem(ES2680);     /* report the error & exit */
      return;
      }
    }

  printf("Testing is underway...\nStarting Phase I Tests: Pass Number %d\n",
         cur_loop);

  /* get the error mode from non-volatile storage */
  result = read_select_data_from_seeprom_timed((unsigned char *)
                                               &error_mode,ERROR_MODE_OS, 
                                               TOT_EMODE_BYTES);
  if (result == PROBLEM_DETECTED)
    {
    report_seeprom_problem(ES2600);     /* log the error & exit */
    return;
    }

  /* retrieve curent test modes value & save it in the global variable */
  result = read_select_data_from_seeprom_timed((unsigned char *) &test_modes,
                                               TEST_MODE_OS, TOT_TMODE_BYTES);

  if (result == PROBLEM_DETECTED)
    {
    report_seeprom_problem(ES2600);     /* log the error & exit */
    return;
    }

    test_modes_ptr = (u16 * ) CUR_TEST_MODES; /* set up ptr to tmp glob var */
    *test_modes_ptr = test_modes;      /* save a copy in glob scratch area */

  /* (7) - call each test and check results */
  for (index = 0; tt[index].test_number !=0; index++)
    { 
    /* see if any tests failed so far */
    result = read_select_data_from_seeprom_timed((unsigned char *) 
                                                 &test_modes, TEST_MODE_OS, 
                                                 TOT_TMODE_BYTES);
    if (result == PROBLEM_DETECTED)
      {
      report_seeprom_problem(ES2600);   /* log the error & exit */
      return;
      }

    /* a previous test has failed. See what error mode we are in */
    if ((test_modes & TM_ERR_PENDING) == TM_ERR_PENDING)
      {
      /* exit now if not in SKIP_ON_ERROR or CONT_ON_ERROR mode */
      if ((error_mode != SKIP_ON_ERROR) && (error_mode != CONT_ON_ERROR))
        return;
      }

    /* if this is a phase 1 test & it is selected, display sign-on msg */
    //printf("\n(1 << index) = %08lX\n",(unsigned long) (1 << index));
    if (tt[index].test_type == TT_P1)
      {
      if ((sel_tests & (unsigned long) (1 << index)) == 
          (unsigned long)(1 << index))
        {
          //printf("Test address = %08lx\n",tt[index].test);
        printf("Starting -> %3d - %s Test...",tt[index].test_number,
               tt[index].sign_on_message);

        /**  INCREMENT COUNT FOR CURRENT TEST HERE **/

        test_ptr = (void *) tt[index].test; /* get abs func adrs */
        (*test_ptr)(tt[index].test_number, cur_loop); /* execute the test */
        } /* end 'if ((p1_tests & ...' */

      } /* end ' if ((tt[index].test_type...' */

    } /* end 'for (index = 0...' */

}

/****************************************************************************
 **                Phase One Error Logic Test Function
 **
 **   This function is used to test the Phase I error handler logic.  It does 
 ** not test any hardware and is normally disabled.
 **
 ** INPUT - "test_no" = test number for this test.
 **       - "exec_count" = current execution count.
 **
 ** OUTPUT - An error, including all the error information, will be reported
 **          to the error handler.
 **
 ** RETURN - Nothing
 **
 ****************************************************************************/
void ph1_err_logic_test(int test_no, int exec_count)
{
  struct error_data eh;         /* create an Phase I err hand struct */
  char err_msg[] = "This is a test of the Phase 1 error handler logic.  No "
                   "test has\n"
                   "actually failed!!!";

  eh.call_type = EH_DISP_TYP_INFO_MSG;
  eh.error_stop = ES1330;
  eh.sup_error_stop = SES0000;
  eh.msgp = (void *) err_msg;
  eh.fail_adrs = 0x8000000020001400ull;
  eh.exp_data = 0xF0F0F0F0F0F0F0F0ull;
  eh.rec_data = 0xF0F0F0F0F070F0F0ull;
  eh.length = (int) BYTES_8;
  eh.test_no = test_no;
  eh.exec_count = exec_count;

  p1_error_handler(&eh);        /* report the error to the system */
      
}



