/* ---------------------------------------------------------------------------- */
/*                  Atmel Microcontroller Software Support                      */
/*                       SAM Software Package License                           */
/* ---------------------------------------------------------------------------- */
/* Copyright (c) 2015, Atmel Corporation                                        */
/*                                                                              */
/* All rights reserved.                                                         */
/*                                                                              */
/* Redistribution and use in source and binary forms, with or without           */
/* modification, are permitted provided that the following condition is met:    */
/*                                                                              */
/* - Redistributions of source code must retain the above copyright notice,     */
/* this list of conditions and the disclaimer below.                            */
/*                                                                              */
/* Atmel's name may not be used to endorse or promote products derived from     */
/* this software without specific prior written permission.                     */
/*                                                                              */
/* DISCLAIMER:  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   */
/* 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.                           */
/* ---------------------------------------------------------------------------- */

/** \addtogroup sdram_module
 * The SDRAMC driver provides the Interface to configure the SDRAM Controller
 * (SDRAMC).
 *  \section Usage
 * <ul>
 *  <li> Configure SDRAM using SDRAMC_Configure().</li>

 * </ul>
 * For more accurate information, please look at the SDRAMC section of the
 * Datasheet.
 * Related files :\n
 * \ref sdramc.c\n
 * \ref sdramc.h.\n
*/

/**
*  \file
*
*  \section Purpose
*
*  Interface for configuring and using SDRAM Controller (SDRAMC).
*
*/

/**
 * \file
 *
 * Implementation of memories configuration on board.
 *
 */
/*----------------------------------------------------------------------------
 *        Headers
 *----------------------------------------------------------------------------*/
#include "chip.h"
#include "sdramc.h"

/*----------------------------------------------------------------------------
 *        Local functions
 *----------------------------------------------------------------------------*/
/**
 * \brief Calculate the sdram controller config register value.
 * \param pMemory  Pointer to the sdram structure.
 * \return Configure register value.
 */
static uint32_t SDRAMC_compute_CR_value(SSdramc_Memory *pMemory)
{
	uint32_t dw = 0;

	dw |= pMemory->cfg.dwColumnBits;
	dw |= pMemory->cfg.dwRowBits;
	dw |= pMemory->cfg.dwBanks;  //NB, number of banks
	dw |= pMemory->cfg.dwCAS;  //CAS, CAS latency
	dw |= pMemory->cfg.dwDataBusWidth;  //DBW, data bus width
	dw |= SDRAMC_CR_TWR(pMemory->cfg.dwWriteRecoveryDelay);
	//TWR, Write Recovery Delay
	dw |= SDRAMC_CR_TRC_TRFC(pMemory->cfg.dwRowCycleDelay_RowRefreshCycle);
	//TRC_TRFC,Row Cycle Delay and Row Refresh Cycle
	dw |= SDRAMC_CR_TRP(pMemory->cfg.dwRowPrechargeDelay);
	//TRP, Row Precharge Delay
	dw |= SDRAMC_CR_TRCD(pMemory->cfg.dwRowColumnDelay);
	//TRCD, Row to Column Delay
	dw |= SDRAMC_CR_TRAS(pMemory->cfg.dwActivePrechargeDelay);
	//TRAS, Active to Precharge Delay
	dw |= SDRAMC_CR_TXSR(pMemory->cfg.dwExitSelfRefreshActiveDelay);
	//TXSR, Exit Self Refresh to Active Delay
	return dw;
}

/*----------------------------------------------------------------------------
 *        Exported functions
 *----------------------------------------------------------------------------*/
/**
 * \brief Configure and initialize the SDRAM controller.
 * \param pMemory  Pointer to the sdram structure.
 * \param dwClockFrequency  SDRAM clock frequency.
 */
extern void SDRAMC_Configure(SSdramc_Memory *pMemory,
							  uint32_t dwClockFrequency)
{
	volatile uint32_t dw;

	/* SDRAM hardware init */
	/* Enable peripheral clock */
	PMC_EnablePeripheral(ID_SMC);

	/* SDRAM device configure */
	/* Step 1. */
	/* Program the features of SDRAM device into the Configuration Register.*/
	SDRAMC->SDRAMC_CR = SDRAMC_compute_CR_value(pMemory);

	/* Step 2. */
	/* For low-power SDRAM, temperature-compensated self refresh (TCSR),
	   drive strength (DS) and partial array self refresh (PASR) must be set
	   in the Low-power Register.*/
	SDRAMC->SDRAMC_LPR = 0;

	/* Step 3. */
	/* Program the memory device type into the Memory Device Register */
	SDRAMC->SDRAMC_MDR = SDRAMC_MDR_MD_SDRAM;

	/* Step 4 */
	/* A minimum pause of 200 ¦Ěs is provided to precede any signal toggle.
	   (6 core cycles per iteration) */
	for (dw = 0; dw < ((dwClockFrequency / 1000000) * 200 / 6); dw++);

	/* Step 5. */
	/* A NOP command is issued to the SDR-SDRAM. Program NOP command into
	 Mode Register, the application must set Mode to 1 in the Mode Register.
	Perform a write access to any SDR-SDRAM address to acknowledge this command.
	Now the clock which drives SDR-SDRAM device is enabled.*/
	SDRAMC->SDRAMC_MR = SDRAMC_MR_MODE_NOP;
	*(uint16_t *)(EBI_SDRAMC_ADDR) = 0;

	/* Step 6. */
	/* An all banks precharge command is issued to the SDR-SDRAM. Program all
	   banks precharge command into Mode Register, the application must set Mode to
	   2 in the Mode Register . Perform a write access to any SDRSDRAM address to
	   acknowledge this command. */
	SDRAMC->SDRAMC_MR = SDRAMC_MR_MODE_ALLBANKS_PRECHARGE;
	*(uint16_t *)(EBI_SDRAMC_ADDR) = 0x0;

	/* add some delays after precharge */
	for (dw = 0; dw < ((dwClockFrequency / 1000000) * 200 / 6); dw++);

	/* Step 7. */
	/* Eight auto-refresh (CBR) cycles are provided. Program the auto refresh
	   command (CBR) into Mode Register, the application must set Mode to 4 in
	   the Mode Register. Once in the idle state, eight AUTO REFRESH cycles must
	   be performed. */
	SDRAMC->SDRAMC_MR = SDRAMC_MR_MODE_AUTO_REFRESH;
	*(uint16_t *)(EBI_SDRAMC_ADDR + 0) = 0x1;

	SDRAMC->SDRAMC_MR = SDRAMC_MR_MODE_AUTO_REFRESH;
	*(uint16_t *)(EBI_SDRAMC_ADDR + 0) = 0x2;

	SDRAMC->SDRAMC_MR = SDRAMC_MR_MODE_AUTO_REFRESH;
	*(uint16_t *)(EBI_SDRAMC_ADDR + 0) = 0x3;

	SDRAMC->SDRAMC_MR = SDRAMC_MR_MODE_AUTO_REFRESH;
	*(uint16_t *)(EBI_SDRAMC_ADDR + 0) = 0x4;

	SDRAMC->SDRAMC_MR = SDRAMC_MR_MODE_AUTO_REFRESH;
	*(uint16_t *)(EBI_SDRAMC_ADDR + 0) = 0x5;

	SDRAMC->SDRAMC_MR = SDRAMC_MR_MODE_AUTO_REFRESH;
	*(uint16_t *)(EBI_SDRAMC_ADDR + 0) = 0x6;

	SDRAMC->SDRAMC_MR = SDRAMC_MR_MODE_AUTO_REFRESH;
	*(uint16_t *)(EBI_SDRAMC_ADDR + 0) = 0x7;

	SDRAMC->SDRAMC_MR = SDRAMC_MR_MODE_AUTO_REFRESH;
	*(uint16_t *)(EBI_SDRAMC_ADDR + 0) = 0x8;

	/* Step 8. */
	/* A Mode Register set (MRS) cycle is issued to program the parameters of
	   the SDRAM devices, in particular CAS latency and burst length. */
	SDRAMC->SDRAMC_MR = SDRAMC_MR_MODE_LOAD_MODEREG;
	*(uint16_t *)(EBI_SDRAMC_ADDR + 0x22) = 0xcafe;

	/* Step 9. */
	/* For low-power SDR-SDRAM initialization, an Extended Mode Register set
	   (EMRS) cycle is issued to program the SDR-SDRAM parameters (TCSR, PASR, DS).
	   The write address must be chosen so that BA[1] is set to 1 and BA[0] is set
	   to 0 */
	SDRAMC->SDRAMC_MR = SDRAMC_MR_MODE_EXT_LOAD_MODEREG;
	*((uint16_t *)(EBI_SDRAMC_ADDR + (1 << pMemory->cfg.dwBK1))) = 0;

	/* Step 10. */
	/* The application must go into Normal Mode, setting Mode to 0 in the Mode
	   Register and perform a write access at any location in the SDRAM to
	   acknowledge this command. */
	SDRAMC->SDRAMC_MR = SDRAMC_MR_MODE_NORMAL;
	*(uint16_t *)(EBI_SDRAMC_ADDR) = 0x0;

	/* Step 11. */
	/* Write the refresh rate into the count field in the SDRAMC Refresh
	   Timer register. Set Refresh timer 15.625 us*/
	dw = dwClockFrequency / 1000u;
	dw *= 15625u;
	dw /= 1000000u;
	SDRAMC->SDRAMC_TR = SDRAMC_TR_COUNT(dw);
}

