/****************************************************************************
 * arch/arm/src/tms570/tms570_boot.c
 *
 *   Copyright (C) 2015 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * This is primarily original code.  However, some logic in this file was
 * inspired/leveraged from TI's Project0 which has a compatible BSD license
 * and credit should be given in any case:
 *
 *   Copyright (c) 2012, Texas Instruments Incorporated
 *   All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <stdint.h>
#include <assert.h>
#include <debug.h>

#include <arch/board/board.h>

#include "chip.h"
#include "arm.h"
#include "sctlr.h"
#include "arm_internal.h"

#include <nuttx/init.h>

#include "hardware/tms570_sys.h"
#include "hardware/tms570_esm.h"
#include "hardware/tms570_pbist.h"
#include "tms570_clockconfig.h"
#include "tms570_selftest.h"
#include "tms570_gio.h"
#include "tms570_esm.h"
#include "tms570_boot.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#ifndef CONFIG_ARMV7R_MEMINIT
#  error CONFIG_ARMV7R_MEMINIT is required by this architecture.
#endif

#ifndef CONFIG_ARCH_LOWVECTORS
#  error CONFIG_ARCH_LOWVECTORS is required by this architecture.
#endif

/****************************************************************************
 * Public Data
 ****************************************************************************/

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: tms570_event_export
 *
 * Description:
 *   Enable CPU Event Export by setting the X bit in the PMCR.  In general,
 *   this bit enables the exporting of events to another debug device, such
 *   as a trace macrocell, over an event bus.
 *
 *   For the TMS570, this allows the CPU to signal any single-bit or double
 *   -bit errors detected by its ECC logic for accesses to program flash or
 *   data RAM.
 *
 ****************************************************************************/

static inline void tms570_event_export(void)
{
  cp15_pmu_pmcr(PMCR_X);
}

/****************************************************************************
 * Name: tms570_check_reset
 *
 * Description:
 *   Assert if we go here through any mechanism other than a power-on reset.
 *
 ****************************************************************************/

static inline void tms570_check_reset(void)
{
#ifdef CONFIG_DEBUG_FEATURES
  uint32_t regval;

  /* Read from the system exception status register to identify the cause of
   * the CPU reset.
   */

  regval = getreg32(TMS570_SYS_ESR);

  /* Clear all reset status flags on normal reset */

  regval = getreg32(TMS570_SYS_ESR);
  putreg32(SYS_ESR_RSTALL, TMS570_SYS_ESR);

  /* Check for abnormal reset causes:  Oscillator failures or watchdog
   * timers. Ignore normal reset causes: External reset, software reset, CPU
   * reset, power-on reset
   *
   * REVISIT: The reset cause is not used in the current design.  But if you
   * need to know the cause of the reset, here is where you would want to
   * do that.
   */

#if 0
  DEBUGASSERT((regval & SYS_ESR_FAILALL) == 0);
#else
  UNUSED(regval);
#endif

#else
  /* Clear all reset status flags */

  putreg32(SYS_ESR_RSTALL, TMS570_SYS_ESR);
#endif
}

/****************************************************************************
 * Name: tms570_enable_ramecc
 *
 * Description:
 *   This function enables the CPU's ECC logic for accesses to B0TCM and
 *   B1TCM.
 *
 ****************************************************************************/

static inline void tms570_enable_ramecc(void)
{
  uint32_t actlr = cp15_rdactlr();
  actlr |= 0x0c000000;
  cp15_wractlr(actlr);
}

/****************************************************************************
 * Name: tms570_memory_initialize
 *
 * Description:
 *   Perform memory initialization of selected RAMs
 *
 *   This function uses the system module's hardware for auto-initialization
 *   of memories and their associated protection schemes.
 *
 ****************************************************************************/

static void tms570_memory_initialize(uint32_t ramset)
{
  /* Enable Memory Hardware Initialization */

  putreg32(SYS_MINITGCR_ENABLE, TMS570_SYS_MINITGCR);

  /* Enable Memory Hardware Initialization for selected RAM's */

  putreg32(ramset, TMS570_SYS_MSIENA);

  /* Wait until Memory Hardware Initialization complete */

  while ((getreg32(TMS570_SYS_MSTCGSTAT) & SYS_MSTCGSTAT_MINIDONE) == 0);

  /* Disable Memory Hardware Initialization */

  putreg32(SYS_MINITGCR_DISABLE, TMS570_SYS_MINITGCR);
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: arm_boot
 *
 * Description:
 *   Complete boot operations started in arm_head.S
 *
 * Boot Sequence
 *
 *   1.  The __start entry point in armv7-r/arm_head.S is invoked upon power-
 *       on reset.
 *   2.  __start prepares CPU for code execution.
 *   3a. If CONFIG_ARMV7R_MEMINIT is not defined, then __start will prepare
 *       memory resources by calling arm_data_initialize() and will then
 *       branch this function.
 *   3b. Otherwise, this function will be called without having initialized
 *       memory resources!  We need to be very careful in this case.  Here,
 *       this function will call tms570_boardinitialize() which, among other
 *       things, must initialize SDRAM memory.  After initializatino of the
 *       memories, this function will call arm_data_initialize() to
 *       initialize the memory resources
 *   4.  This function will then branch to nx_start() to start the operating
 *       system.
 *
 ****************************************************************************/

void arm_boot(void)
{
#ifdef CONFIG_TMS570_SELFTEST
  int check;
#endif /* CONFIG_TMS570_SELFTEST */

  /* Enable CPU Event Export.
   *
   * This allows the CPU to signal any single-bit or double-bit errors
   * detected by its ECC logic for accesses to program flash or data RAM.
   */

  tms570_event_export();

  /* Verify that we got here via a power-up reset */

  tms570_check_reset();

  /* Check if there were ESM group3 errors during power-up.
   *
   * These could occur during eFuse auto-load or during reads from flash OTP
   * during power-up. Device operation is not reliable and not recommended
   * in this case.
   *
   * An ESM group3 error only drives the nERROR pin low. An external circuit
   * that monitors the nERROR pin must take the appropriate action to ensure
   * that the system is placed in a safe state, as determined by the
   * application.
   */

  DEBUGASSERT(getreg32(TMS570_ESM_SR3) == 0);

  /* Initialize clocking to settings provided by board-specific logic */

  tms570_clockconfig();

#ifdef CONFIG_TMS570_SELFTEST
  /* Run a diagnostic check on the memory self-test controller.
   *
   * REVISIT: This is a destructive test.  It will most likely clobber the
   * current stack content and result in a failure if this function were to
   * attempt to return.
   */

  tms570_memtest_selftest();

  /* Run the memory selftest on CPU RAM. */

  tms570_memtest_start(PBIST_RINFOL_ESRAM1_RAM);
  check = tms570_memtest_complete();
  DEBUGASSERT(check == OK);
#endif /* CONFIG_TMS570_SELFTEST */

  /* Initialize CPU RAM. */

  tms570_memory_initialize(SYS_MSIENA_RAM);

  /* Enable ECC checking for TCRAM accesses. */

  tms570_enable_ramecc();

#ifdef CONFIG_TMS570_SELFTEST
  /* Perform PBIST on all dual-port memories */

  tms570_memtest_start(PBIST_RINFOL_VIM_RAM
#ifdef CONFIG_TMS570_DCAN1
                       | PBIST_RINFOL_DCAN1_RAM
#endif
#ifdef CONFIG_TMS570_DCAN2
                       | PBIST_RINFOL_DCAN2_RAM
#endif
#ifdef CONFIG_TMS570_MIBASPI1
                       | PBIST_RINFOL_MIBSPI1_RAM
#endif
#ifdef CONFIG_TMS570_MIBASPI1
                       | PBIST_RINFOL_MIBADC_RAM
#endif
#ifdef CONFIG_TMS570_N2HET
                       | PBIST_RINFOL_N2HET_RAM
                       | PBIST_RINFOL_HET_TU_RAM
#endif
                      );

  /* Test the CPU ECC mechanism for RAM accesses. */

  tms570_cpuecc_selftest();

  /* Wait for the memory test to complete */

  check = tms570_memtest_complete();
  DEBUGASSERT(check == OK);
  UNUSED(check);
#endif /* CONFIG_TMS570_SELFTEST */

#ifdef CONFIG_TMS570_MIBASPI1
  /* Release the MibSPI1 modules from local reset.
   *
   * This will cause the MibSPI1 RAMs to be initialized along with the
   * parity memory.
   */

  putreg32(MIBSPI_GCR0_RESET, TMS570_MIBSPI_GCR0);
#endif

  /* Initialize all on-chip SRAMs except for MibSPIx RAMs.
   *
   * The MibSPIx modules have their own auto-initialization mechanism which
   * is triggered as soon as the modules are brought out of local reset.
   *
   * The system module auto-init will hang on the MibSPI RAM if the module
   * is still in local reset.
   */

  tms570_memory_initialize(SYS_MSIENA_VIM_RAM
#ifdef CONFIG_TMS570_N2HET
                           | SYS_MSIENA_N2HET_RAM | SYS_MSIENA_HTU_RAM
#endif
#ifdef CONFIG_TMS570_DCAN1
                           | SYS_MSIENA_DCAN1_RAM
#endif
#ifdef CONFIG_TMS570_DCAN2
                           | SYS_MSIENA_DCAN2_RAM
#endif
#ifdef CONFIG_TMS570_MIBADC
                           | SYS_MSIENA_MIBADC_RAM
#endif
    );

#ifdef CONFIG_TMS570_SELFTEST
  /* Test the parity protection mechanism for peripheral RAMs */

#warning Missing logic
#endif

#ifdef CONFIG_TMS570_MIBASPI1
  /* Wait for MibSPI1 RAM to complete initialization */

#warning Missing logic
#endif

  /* Configure system response to error conditions */

  tms570_esm_initialize();

  /* Initialize the FPU */

  arm_fpuconfig();

#ifdef CONFIG_ARMV7R_MEMINIT
  /* Initialize the .bss and .data sections as well as RAM functions
   * now after RAM has been initialized.
   *
   * NOTE that if SDRAM were supported, this call might have to be
   * performed after returning from tms570_board_initialize()
   */

  arm_data_initialize();
#endif

  /* Initialize GIO for use by board initialization logic */

  tms570_gio_initialize();

  /* Perform board-specific initialization,  This must include:
   *
   * - Initialization of board-specific memory resources (e.g., SDRAM)
   * - Configuration of board specific resources (GIOs, LEDs, etc).
   *
   * NOTE: We must use caution prior to this point to make sure that
   * the logic does not access any global variables that might lie
   * in SDRAM.
   */

  tms570_board_initialize();

  /* Perform common, low-level chip initialization (might do nothing) */

  tms570_lowsetup();

  /* Then start NuttX */

  nx_start();
}