/************************************************************************************
 * configs/open1788/src/lpc17_sdraminitialize.c
 * arch/arm/src/board/lpc17_sdraminitialize.c
 *
 *   Copyright (C) 2013 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * 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 <debug.h>

#include <nuttx/arch.h>
#include <arch/board/board.h>

#include "up_arch.h"
#include "up_internal.h"
#include "chip/lpc17_syscon.h"
#include "lpc17_emc.h"

#include "open1788.h"

#if defined(CONFIG_LPC17_EMC) && defined(CONFIG_ARCH_EXTDRAM)

/************************************************************************************
 * Definitions
 ************************************************************************************/
/* The core clock is LPC17_EMCCLK which may be either LPC17_CCLK* (undivided), or
 * LPC17_CCLK / 2 as determined by settings in the board.h header file.
 *
 * For example:
 *   LPC17_CCLCK      =  120,000,000
 *   EMCCLKSEL        -> LPC17_CCLK divided by 2
 *   LPC17_EMCCLK     =  60,000,000
 *   LPC17_EMCCLK_MHZ =  60 (Rounded to an integer)
 *   EMC_NSPERCLK     =  16.667 (Represented with 4 bits of fraction, 267)
 *
 *   EMC_NS2CLK(63)   = ((63 << 4) + 266) / 267 = 4 (actual 3.78)
 *   EMC_NS2CLK(20)   = ((20 << 4) + 266) / 267 = 2 (actual 1.20)
 */

#define LPC17_EMCCLK_MHZ    ((LPC17_EMCCLK + 500000) / 1000000)
#define EMC_NSPERCLK_B4     (((1000 << 4) + (LPC17_EMCCLK_MHZ >> 1)) / LPC17_EMCCLK_MHZ)
#define EMC_NS2CLK(ns)      (((ns << 4) + (EMC_NSPERCLK_B4 - 1)) / EMC_NSPERCLK_B4)
#define MDKCFG_RASCAS0VAL   0x00000303

/* Set up for 32-bit SDRAM at CS0 */

#define CONFIG_ARCH_SDRAM_32BIT

#ifdef CONFIG_ARCH_SDRAM_16BIT 
#  define SDRAM_SIZE        0x02000000 /* 256Mbit */
#else /* if defined(CONFIG_ARCH_SDRAM_32BIT) */
#  undef CONFIG_ARCH_SDRAM_32BIT
#  define CONFIG_ARCH_SDRAM_32BIT 1
#  define SDRAM_SIZE        0x04000000 /* 512Mbit */
#endif

#define SDRAM_BASE          0xa0000000 /* CS0 */

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

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

/************************************************************************************
 * Name: open1788_sdram_initialize
 *
 * Description:
 *   Initialize SDRAM
 *
 ************************************************************************************/

void open1788_sdram_initialize(void)
{
  uint32_t regval;
#ifdef CONFIG_ARCH_SDRAM_16BIT
  volatile uint16_t dummy;
#else
  volatile uint32_t dummy;
#endif
  int i;

  /* Reconfigure delays:
   *
   * CMDDLY: Programmable delay value for EMC outputs in command delayed
   *   mode.  The delay amount is roughly CMDDLY * 250 picoseconds.
   * FBCLKDLY: Programmable delay value for the feedback clock that controls
   *   input data sampling.  The delay amount is roughly (FBCLKDLY+1) * 250
   *   picoseconds.
   * CLKOUT0DLY: Programmable delay value for the CLKOUT0 output. This would
   *   typically be used in clock delayed mode.  The delay amount is roughly
   *  (CLKOUT0DLY+1) * 250 picoseconds.
   * CLKOUT1DLY: Programmable delay value for the CLKOUT1 output. This would
   *  typically be used in clock delayed mode.  The delay amount is roughly
   *  (CLKOUT1DLY+1) * 250 picoseconds.
   */

  regval = SYSCON_EMCDLYCTL_CMDDLY(32) |
           SYSCON_EMCDLYCTL_FBCLKDLY(32) |
           SYSCON_EMCDLYCTL_CLKOUT0DLY(1) |
           SYSCON_EMCDLYCTL_CLKOUT1DLY(1);
  putreg32(regval, LPC17_SYSCON_EMCDLYCTL);

  /* Configure the SDRAM */

  putreg32(    EMC_NS2CLK(20), LPC17_EMC_DYNAMICRP);   /* TRP   = 20 nS */
  putreg32(                15, LPC17_EMC_DYNAMICRAS);  /* RAS   = 42ns to 100K ns,  */
  putreg32(                 0, LPC17_EMC_DYNAMICSREX); /* TSREX = 1 clock */
  putreg32(                 1, LPC17_EMC_DYNAMICAPR);  /* TAPR  = 2 clocks? */
  putreg32(EMC_NS2CLK(20) + 2, LPC17_EMC_DYNAMICDAL);  /* TDAL  = TRP + TDPL = 20ns + 2clk  */
  putreg32(                 1, LPC17_EMC_DYNAMICWR);   /* TWR   = 2 clocks */
  putreg32(    EMC_NS2CLK(63), LPC17_EMC_DYNAMICRC);   /* H57V2562GTR-75C TRC = 63ns(min)*/
  putreg32(    EMC_NS2CLK(63), LPC17_EMC_DYNAMICRFC);  /* H57V2562GTR-75C TRFC = TRC */
  putreg32(                15, LPC17_EMC_DYNAMICXSR);  /* Exit self-refresh to active */
  putreg32(    EMC_NS2CLK(63), LPC17_EMC_DYNAMICRRD);  /* 3 clock, TRRD = 15ns (min) */
  putreg32(                 1, LPC17_EMC_DYNAMICMRD);  /* 2 clock, TMRD = 2 clocks (min) */

  /* Command delayed strategy, using EMCCLKDELAY */

  putreg32(EMC_DYNAMICREADCONFIG_RD_CMD, LPC17_EMC_DYNAMICREADCONFIG);

  /* H57V2562GTR-75C: TCL=3CLK, TRCD = 20ns(min), 3 CLK = 24ns */

  putreg32(MDKCFG_RASCAS0VAL, LPC17_EMC_DYNAMICRASCAS0);

  #ifdef CONFIG_ARCH_SDRAM_16BIT
    /* For Manley lpc1778 SDRAM: H57V2562GTR-75C, 256Mb, 16Mx16, 4 banks, row=13, column=9:
     *
     * 256Mb, 16Mx16, 4 banks, row=13, column=9, RBC
     */

  putreg32(EMC_DYNAMICCONFIG_MD_SDRAM |  EMC_DYNAMICCONFIG_AM0(13),
           LPC17_EMC_DYNAMICCONFIG0);

#elif defined CONFIG_ARCH_SDRAM_32BIT
    /* 256Mb, 16Mx16, 4 banks, row=13, column=9, RBC */

  putreg32(EMC_DYNAMICCONFIG_MD_SDRAM |  EMC_DYNAMICCONFIG_AM0(13) | EMC_DYNAMICCONFIG_AM1,
           LPC17_EMC_DYNAMICCONFIG0);
#endif

  up_mdelay(100);

  /* Issue NOP command */

  putreg32(EMC_DYNAMICCONTROL_CE | EMC_DYNAMICCONTROL_CS | EMC_DYNAMICCONTROL_I_NOP,
           LPC17_EMC_DYNAMICCONTROL);

  /* Wait 200 Msec */

  up_mdelay(200);

  /* Issue PALL command */

  putreg32(EMC_DYNAMICCONTROL_CE | EMC_DYNAMICCONTROL_CS | EMC_DYNAMICCONTROL_I_PALL,
           LPC17_EMC_DYNAMICCONTROL);

  putreg32(2, LPC17_EMC_DYNAMICREFRESH); /* ( n * 16 ) -> 32 clock cycles */

  /* Wait 128 AHB clock cycles */

  for(i = 0; i < 128; i++);
 
  /* 64ms/8192 = 7.8125us, nx16x8.33ns < 7.8125us, n < 58.6*/

  regval = 64000000 / (1 << 13);
  regval -= 16;
  regval >>= 4;
  regval = regval * LPC17_EMCCLK_MHZ / 1000;
  putreg32(regval, LPC17_EMC_DYNAMICREFRESH);

  /* Issue MODE command */

  putreg32(EMC_DYNAMICCONTROL_CE | EMC_DYNAMICCONTROL_CS | EMC_DYNAMICCONTROL_I_MODE,
           LPC17_EMC_DYNAMICCONTROL);

#ifdef CONFIG_ARCH_SDRAM_16BIT
  dummy = getreg16(SDRAM_BASE | (0x33 << 12));  /* 8 burst, 3 CAS latency */
#elif defined CONFIG_ARCH_SDRAM_32BIT
  dummy = getreg32(SDRAM_BASE | (0x32 << 13)); /* 4 burst, 3 CAS latency */
#endif

  /* Issue NORMAL command */

  putreg32(EMC_DYNAMICCONTROL_I_NORMAL, LPC17_EMC_DYNAMICCONTROL);

  /* Enable buffer */

  regval = getreg32(LPC17_EMC_DYNAMICCONFIG0);
  regval |= EMC_DYNAMICCONFIG_B;
  putreg32(regval, LPC17_EMC_DYNAMICCONFIG0);
  up_mdelay(12);

  regval = getreg32(LPC17_SYSCON_EMCDLYCTL);
  regval &= ~SYSCON_EMCDLYCTL_CMDDLY_MASK;
  regval |= SYSCON_EMCDLYCTL_CMDDLY(18);
  putreg32(regval, LPC17_SYSCON_EMCDLYCTL);
}

#endif /* CONFIG_LPC17_EMC && CONFIG_ARCH_EXTDRAM */
