/*This file is prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief Application to copy and execute single function as well as complete
 *        binary file from external SDRAM
 *
 * - Compiler:           GNU GCC for AVR32 and IAR EWAVR32
 * - Supported devices:  All AVR32 devices with EBI and SDRAMC module can be used.
 *                       Tested for EVK1104/EVK1100 and UC3C-EK evaluation kit
 * - AppNote:			 AVR32825
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support and FAQ: http://support.atmel.no/
 *
 ******************************************************************************/

/*! \page License
 * Copyright (c) 2010 Atmel Corporation. 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. The name of Atmel may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an Atmel
 * AVR product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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
 *
 */

/*! \mainpage
 * \section intro Introduction
 *
 * This application is an example of how to execute a single function as well as
 * complete binary from external SDRAM. The code has been tested on EVK1100, EVK1104
 * and UC3C-EK
 *
 *
 * For executing a single function:
 *
 * Set EXECUTE_FUNCTION_IN_SDRAM to 1 in conf_sdram.h (EXECUTE_CODE_IN_SDRAM to 0)
 * In case of GCC the function "test_function_in_sdram()" would be placed in SDRAM during
 * runtime and would be executed. It is a simple function which turns ON the four LED's
 *
 *
 * For copying and executing complete binary from SDRAM
 *
 * Set EXECUTE_CODE_IN_SDRAM to 1 in conf_sdram.h (EXECUTE_FUNCTION_IN_SDRAM to 0)
 * The example binary "external_sdram_binary.bin" can be loaded either from external
 * dataflash as well as SD/MMC card.
 * Select BOOT_OPTION = DATA_FLASH to copy bin file from Data flash
 * Select BOOT_OPTION = SD_MMC_CARD to copy bin file from SD MMC card
 * and then build the project.
 * To copy the bin file to Data Flash,download the USB Mass Storage example in the on the
 * EVK1104 kit.Perform a hard reset. The data flash (8 MB) will appear as disk drive on your
 * Computer (PC). Copy the external_sdram_binary.bin file to disk drive represented by Data
 * Flash. The same method can be used to copy bin file to SD/MMC card
 *
 * Make sure to name the externally generated binary as "external_sdram_binary.bin" since this
 * has been hard coded.
 *
 * \section compileinfo Compilation Information
 * This software is written for GNU GCC for AVR32 and IAR
 *
 * \section deviceinfo Device Information
 * All AVR32 devices with EBI and SDRAMC can be used.
 *
 * \section configinfo Configuration Information
 * This example has been tested with the following configuration:
 * - EVK1104 evaluation kit;
 * - CPU clock:
 *      -- 12 MHz : EVK1104 evaluation kit;
 *
 * - UC3C_EK evaluation kit;
 * - CPU clock:
 *      -- 16 MHz : UC3C_EK evaluation kit;
 *
 * SDRAM should be initialized with same speed as Main Clock for CPU
 *
 * \section contactinfo Contact Information
 * For further information, visit
 * <A href="http://www.atmel.com/products/AVR32/">Atmel AVR32</A>.\n
 * Support and FAQ: http://support.atmel.no/
 */

// Include Files
#include "board.h"
#include "power_clocks_lib.h"
#include "cycle_counter.h"
#include "gpio.h"
#include "sdramc.h"
#include "flashc.h"
#include "fsaccess.h"
#if ((defined SD_MMC_MCI_0_MEM) && (SD_MMC_MCI_0_MEM == ENABLE)) || ((defined SD_MMC_MCI_1_MEM) && (SD_MMC_MCI_1_MEM == ENABLE))
#include "sd_mmc_mci.h"
#include "sd_mmc_mci_mem.h"
#include "conf_sd_mmc_mci.h"
#endif
#if (defined AT45DBX_MEM) && (AT45DBX_MEM == ENABLE)
#include "spi.h"
#include "conf_at45dbx.h"
#endif
#if (defined SD_MMC_SPI_MEM) && (SD_MMC_SPI_MEM == ENABLE)
#include "sd_mmc_spi.h"
#include "sd_mmc_spi_mem.h"
#include "conf_sd_mmc_spi.h"
#endif
#include "conf_sdram.h"

//_____ D E C L A R A T I O N S ____________________________________________

static int sdram_check(void);

#if EXECUTE_CODE_IN_SDRAM

#if (BOOT_OPTION == DATA_FLASH)
#if (defined AT45DBX_MEM) && (AT45DBX_MEM == ENABLE)
static void at45dbx_resources_init(void);
#endif
#endif
#if (BOOT_OPTION == SD_MMC_CARD)
#if (defined SD_MMC_SPI_MEM) && (SD_MMC_SPI_MEM == ENABLE)
static void sd_mmc_spi_resources_init(void);
#endif
#if ((defined SD_MMC_MCI_0_MEM) && (SD_MMC_MCI_0_MEM == ENABLE)) || ((defined SD_MMC_MCI_1_MEM) && (SD_MMC_MCI_1_MEM == ENABLE))
static void sd_mmc_mci_resources_init(void);
#endif
#endif
static int copy_binary_to_sdram(char* filepath, void* address);
static void execute_sdram_binary(void* address);

#endif

//_____ D E F I N I T I O N S ______________________________________________

/*! \name System Clock Frequencies
 */
//! @{
static pcl_freq_param_t pcl_freq_param =
{
  .cpu_f        = CPU_SPEED,
  .pba_f        = PBA_SPEED,
  .osc0_f       = FOSC0,
  .osc0_startup = OSC0_STARTUP
};
//! @}

//!
//! @brief This function is executed from external SDRAM
//!
#if __ICCAVR32__
 __ramfunc
#elif __GNUC__
  __attribute__((__section__(".text_sdram")))

#endif
void test_function_in_sdram()
{
	gpio_clr_gpio_pin(LED0_GPIO);
	gpio_clr_gpio_pin(LED1_GPIO);
	gpio_clr_gpio_pin(LED2_GPIO);
	gpio_clr_gpio_pin(LED3_GPIO);
}

//!
//! @brief Startup routine before the main() is called
//!
#if __GNUC__ && __AVR32__
int _init_startup(void)
#elif __ICCAVR32__
int __low_level_init(void)
#endif
{
#if __GNUC__ && __AVR32__
  // Import the Exception Vector Base Address.
  extern void _evba;

  // Load the Exception Vector Base Address in the corresponding system register
  Set_system_register(AVR32_EVBA, (int)&_evba);
#endif

  // Enable exceptions.
  Enable_global_exception();

#if BOARD == UC3C_EK
   // Configure system clocks.
   if (pcl_configure_clocks(&pcl_freq_param) != PASS)
     return 42;
#else
   // switch to external oscillator 0
   pm_switch_to_osc0(&AVR32_PM, FOSC0, OSC0_STARTUP);
#endif

  // Initialize the SDRAM Controller and the external SDRAM chip.
  sdramc_init(FOSC0);

  // run SDRAM self test
  if(sdram_check() != SDRAM_TEST_OK)
  {
	   return 0;
  }

  // EWAVR32: Request initialization of data segments.
  // GCC: Don't-care value.
  return 1;
}

//!
//! @brief This function verifies the integrity of the external SDRAM
//!
static int sdram_check(void)
{
      long i;

	  unsigned long sdram_size = SDRAM_SIZE >> 2;
	  volatile unsigned long*sdram = SDRAM;

	  // Fill the SDRAM with the test pattern.
	  for (i = 0; i < sdram_size; i+=32)
	  {
	    sdram[i] = i;
	  }

	  for (i = 0; i < sdram_size; i+=32)
	  {
	    if(i != sdram[i])
	    {
	    	return SDRAM_TEST_FAIL;
	    }
	  }

	 return SDRAM_TEST_OK;
}

#if  EXECUTE_CODE_IN_SDRAM

//!
//! @brief This function initializes the SPI module for SD/MMC card
//!
#if (BOOT_OPTION == SD_MMC_CARD)
#if (defined SD_MMC_SPI_MEM) && (SD_MMC_SPI_MEM == ENABLE)

static void sd_mmc_spi_resources_init(void)
{
  static const gpio_map_t SD_MMC_SPI_GPIO_MAP =
  {
    {SD_MMC_SPI_SCK_PIN,  SD_MMC_SPI_SCK_FUNCTION },  // SPI Clock.
    {SD_MMC_SPI_MISO_PIN, SD_MMC_SPI_MISO_FUNCTION},  // MISO.
    {SD_MMC_SPI_MOSI_PIN, SD_MMC_SPI_MOSI_FUNCTION},  // MOSI.
    {SD_MMC_SPI_NPCS_PIN, SD_MMC_SPI_NPCS_FUNCTION}   // Chip Select NPCS.
  };

  // SPI options.
  spi_options_t spiOptions =
  {
    .reg          = SD_MMC_SPI_NPCS,
    .baudrate     = SD_MMC_SPI_MASTER_SPEED,  // Defined in conf_sd_mmc_spi.h.
    .bits         = SD_MMC_SPI_BITS,          // Defined in conf_sd_mmc_spi.h.
    .spck_delay   = 0,
    .trans_delay  = 0,
    .stay_act     = 1,
    .spi_mode     = 0,
    .modfdis      = 1
  };

  // Assign I/Os to SPI.
  gpio_enable_module(SD_MMC_SPI_GPIO_MAP,
                     sizeof(SD_MMC_SPI_GPIO_MAP) / sizeof(SD_MMC_SPI_GPIO_MAP[0]));

  // If the SPI used by the SD/MMC is not enabled.
  if (!spi_is_enabled(SD_MMC_SPI))
  {
    // Initialize as master.
    spi_initMaster(SD_MMC_SPI, &spiOptions);

    // Set selection mode: variable_ps, pcs_decode, delay.
    spi_selectionMode(SD_MMC_SPI, 0, 0, 0);

    // Enable SPI.
    spi_enable(SD_MMC_SPI);
  }

  // Initialize SD/MMC with SPI PB clock.
  sd_mmc_spi_init(spiOptions, pcl_freq_param.pba_f);
}

#endif  // SD_MMC_SPI_MEM == ENABLE


//!
//! @brief This function initializes the MCI module for SD/MMC card
//!

#if ((defined SD_MMC_MCI_0_MEM) && (SD_MMC_MCI_0_MEM == ENABLE)) || ((defined SD_MMC_MCI_1_MEM) && (SD_MMC_MCI_1_MEM == ENABLE))

static void sd_mmc_mci_resources_init(void)
{
  static const gpio_map_t SD_MMC_MCI_GPIO_MAP =
  {
    {SD_SLOT_8BITS_CLK_PIN,   SD_SLOT_8BITS_CLK_FUNCTION  },  // SD CLK.
    {SD_SLOT_8BITS_CMD_PIN,   SD_SLOT_8BITS_CMD_FUNCTION  },  // SD CMD.
    {SD_SLOT_8BITS_DATA0_PIN, SD_SLOT_8BITS_DATA0_FUNCTION},  // SD DAT[0].
    {SD_SLOT_8BITS_DATA1_PIN, SD_SLOT_8BITS_DATA1_FUNCTION},  // DATA Pin.
    {SD_SLOT_8BITS_DATA2_PIN, SD_SLOT_8BITS_DATA2_FUNCTION},  // DATA Pin.
    {SD_SLOT_8BITS_DATA3_PIN, SD_SLOT_8BITS_DATA3_FUNCTION},  // DATA Pin.
    {SD_SLOT_8BITS_DATA4_PIN, SD_SLOT_8BITS_DATA4_FUNCTION},  // DATA Pin.
    {SD_SLOT_8BITS_DATA5_PIN, SD_SLOT_8BITS_DATA5_FUNCTION},  // DATA Pin.
    {SD_SLOT_8BITS_DATA6_PIN, SD_SLOT_8BITS_DATA6_FUNCTION},  // DATA Pin.
    {SD_SLOT_8BITS_DATA7_PIN, SD_SLOT_8BITS_DATA7_FUNCTION}   // DATA Pin.
  };

  // MCI options.
  static const mci_options_t MCI_OPTIONS =
  {
    .card_speed = 400000,
    .card_slot  = SD_SLOT_8BITS, // Default card initialization
  };

  // Assign I/Os to MCI.
  gpio_enable_module(SD_MMC_MCI_GPIO_MAP,
                     sizeof(SD_MMC_MCI_GPIO_MAP) / sizeof(SD_MMC_MCI_GPIO_MAP[0]));

  // Enable pull-up for Card Detect.
  gpio_enable_pin_pull_up(SD_SLOT_8BITS_CARD_DETECT);

  // Enable pull-up for Write Protect.
  gpio_enable_pin_pull_up(SD_SLOT_8BITS_WRITE_PROTECT);

  // Initialize SD/MMC with MCI PB clock.
  sd_mmc_mci_init(&MCI_OPTIONS, pcl_freq_param.pba_f, pcl_freq_param.cpu_f);
}

#endif  // SD_MMC_MCI_0_MEM == ENABLE || SD_MMC_MCI_1_MEM == ENABLE
#endif


//!
//! @brief This function initializes the SPI lines for DATA FLASH
//!
#if (BOOT_OPTION == DATA_FLASH)
#if (defined AT45DBX_MEM) && (AT45DBX_MEM == ENABLE)

static void at45dbx_resources_init(void)
{
  static const gpio_map_t AT45DBX_SPI_GPIO_MAP =
  {
    {AT45DBX_SPI_SCK_PIN,          AT45DBX_SPI_SCK_FUNCTION         },  // SPI Clock.
    {AT45DBX_SPI_MISO_PIN,         AT45DBX_SPI_MISO_FUNCTION        },  // MISO.
    {AT45DBX_SPI_MOSI_PIN,         AT45DBX_SPI_MOSI_FUNCTION        },  // MOSI.
#define AT45DBX_ENABLE_NPCS_PIN(NPCS, unused) \
    {AT45DBX_SPI_NPCS##NPCS##_PIN, AT45DBX_SPI_NPCS##NPCS##_FUNCTION},  // Chip Select NPCS.
    MREPEAT(AT45DBX_MEM_CNT, AT45DBX_ENABLE_NPCS_PIN, ~)
#undef AT45DBX_ENABLE_NPCS_PIN
  };

  // SPI options.
  spi_options_t spiOptions =
  {
    .reg          = AT45DBX_SPI_FIRST_NPCS,   // Defined in conf_at45dbx.h.
    .baudrate     = AT45DBX_SPI_MASTER_SPEED, // Defined in conf_at45dbx.h.
    .bits         = AT45DBX_SPI_BITS,         // Defined in conf_at45dbx.h.
    .spck_delay   = 0,
    .trans_delay  = 0,
    .stay_act     = 1,
    .spi_mode     = 0,
    .modfdis      = 1
  };

  // Assign I/Os to SPI.
  gpio_enable_module(AT45DBX_SPI_GPIO_MAP,
                     sizeof(AT45DBX_SPI_GPIO_MAP) / sizeof(AT45DBX_SPI_GPIO_MAP[0]));

  // If the SPI used by the AT45DBX is not enabled.
  if (!spi_is_enabled(AT45DBX_SPI))
  {
    // Initialize as master.
    spi_initMaster(AT45DBX_SPI, &spiOptions);

    // Set selection mode: variable_ps, pcs_decode, delay.
    spi_selectionMode(AT45DBX_SPI, 0, 0, 0);

    // Enable SPI.
    spi_enable(AT45DBX_SPI);
  }

  // Initialize data flash with SPI PB clock.
  at45dbx_init(spiOptions, PBA_SPEED);
}

#endif  // AT45DBX_MEM == ENABLE
#endif

//!
//! @brief This function reads the binary file from specified location
//!
static int copy_binary_to_sdram(char* filepath, void* address)
{
   int fd;
   long size, count;
   unsigned char *p = address;

   if ((fd = open(filepath, O_RDONLY)) < 0)
      return ERROR_FILE_NOT_FOUND;

   size = fsaccess_file_get_size(fd);

   while (size > 0)
   {
      count = read(fd, p, FS_SIZE_OF_SECTOR);
      p += count;
      size -= count;

      if (size != 0 && count == 0)
         return ERROR_FILE_READ;
   }

   close(fd);
   return FILE_COPY_SUCCESS;
}


//!
//! @brief This function jumps to start address of SDRAM and starts execution
//!
static void execute_sdram_binary(void* address)
{
  (*(void (*)(void))SDRAM_START_ADDRESS)(); // Executed in SDRAM.
}


//!
//! @brief This function copies the binary from SD/DATAFLASH
//!
static int sdram_boot(void)
{
   int iFileCopyStatus = FILE_COPY_SUCCESS;

   //Drive letter depends on the LUN options conf_access.h file
#if(BOOT_OPTION == DATA_FLASH)
   iFileCopyStatus = copy_binary_to_sdram("A:\\external_sdram_binary.bin", SDRAM);
#elif (BOOT_OPTION == SD_MMC_CARD)
   iFileCopyStatus = copy_binary_to_sdram("B:\\external_sdram_binary.bin", SDRAM);
#endif

   if (iFileCopyStatus == FILE_COPY_SUCCESS)
   {
	 // this function should not return
     execute_sdram_binary(SDRAM);
   }

   return 0;
}

#endif //EXECUTE_CODE_IN_SDRAM

//!
//! @brief This function initializes clock, sdram, MCI and SPI
//!
int main(void)
{

#if  EXECUTE_CODE_IN_SDRAM

   // If loading the binary from DATA FLASH
#if (BOOT_OPTION == DATA_FLASH)
#if (defined AT45DBX_MEM) && (AT45DBX_MEM == ENABLE)
   // init AT45DBX data flash resources
   at45dbx_resources_init();

   // check if the AT45DBX flash is ready
   if (mem_test_unit_ready(LUN_ID_AT45DBX_MEM) != CTRL_GOOD)
   {
	   return 0;
   }
#endif
#endif // BOOT_OPTION == DATA_FLASH

   // If loading the binary from SDCARD
#if (BOOT_OPTION == SD_MMC_CARD)

   //SPI or MCI
#if SD_MMC_SPI_MEM == ENABLE
  sd_mmc_spi_resources_init();
  // test for sd_mmc card presence
  while ( sd_mmc_spi_mem_check() != OK );

#else
   // Initialize SD/MMC driver resources: GPIO, SDIO and SD/MMC.
   sd_mmc_mci_resources_init();
   // test for sd_mmc card presence
   while ( sd_mmc_mci_mem_check(SD_SLOT_8BITS) != OK );

#endif
#endif // BOOT_OPTION == SD_MMC_CARD

   // init the FAT navigation module
   if (b_fsaccess_init() == FALSE)
   {
	   return 0;
   }

   // Copy image to SDRAM and jump to start of SDRAM
   if (sdram_boot() == 0)
   {
	   // This point should never be reached
	   return 0;
   }

#elif EXECUTE_FUNCTION_IN_SDRAM

   /* Start the function execution in SDRAM */
   test_function_in_sdram();

   while(1)
	   ;

#endif

   // This point should never be reached
   return 0;
}


