20ce2f274a
Fix nxstyle errors to pass CI Signed-off-by: Alin Jerpelea <alin.jerpelea@sony.com>
559 lines
17 KiB
C
559 lines
17 KiB
C
/****************************************************************************
|
|
* arch/arm/src/lpc54xx/lpc54_emc.c
|
|
*
|
|
* Copyright (C) 2017 Gregory Nutt. All rights reserved.
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
|
*
|
|
* Parts of this file were adapted from sample code provided for the LPC54xx
|
|
* family from NXP which has a compatible BSD license.
|
|
*
|
|
* Copyright (c) 2016, Freescale Semiconductor, Inc.
|
|
* Copyright (c) 2016 - 2017 , NXP
|
|
*
|
|
* 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 <nuttx/clock.h>
|
|
|
|
#include "arm_arch.h"
|
|
|
|
#include "hardware/lpc54_syscon.h"
|
|
#include "hardware/lpc54_emc.h"
|
|
#include "lpc54_config.h"
|
|
#include "lpc54_enableclk.h"
|
|
#include "lpc54_reset.h"
|
|
#include "lpc54_emc.h"
|
|
|
|
#ifdef CONFIG_LPC54_EMC
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define EMC_SDRAM_MODE_CL_SHIFT (4)
|
|
#define EMC_SDRAM_MODE_CL_MASK (7 << EMC_SDRAM_MODE_CL_SHIFT)
|
|
|
|
#define EMC_DYNCTL_COLUMNBASE_SHIFT (0)
|
|
#define EMC_DYNCTL_COLUMNBASE_MASK (3 << EMC_DYNCTL_COLUMNBASE_SHIFT)
|
|
#define EMC_DYNCTL_COLUMNPLUS_SHIFT (3)
|
|
#define EMC_DYNCTL_COLUMNPLUS_MASK (3 << EMC_DYNCTL_COLUMNPLUS_SHIFT)
|
|
#define EMC_DYNCTL_BUSWIDTH_MASK (0x80)
|
|
#define EMC_DYNCTL_BUSADDRMAP_MASK (0x20)
|
|
#define EMC_DYNCTL_DEVBANKS_BITS_MASK (0x1c)
|
|
|
|
#define EMC_SDRAM_BANKCS_BA0_MASK (uint32_t)(0x2000)
|
|
#define EMC_SDRAM_BANKCS_BA1_MASK (uint32_t)(0x4000)
|
|
#define EMC_SDRAM_BANKCS_BA_MASK (EMC_SDRAM_BANKCS_BA0_MASK | EMC_SDRAM_BANKCS_BA1_MASK)
|
|
|
|
#define EMC_REFRESH_CLOCK_SCALE 16
|
|
|
|
#define EMC_SDRAM_WAIT_CYCLES 2000
|
|
#define MHZ_PER_HZ 1000000
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const uintptr_t g_dram_csbase[LPC54_EMC_NCS] =
|
|
{
|
|
LPC54_DRAMCS0_BASE, LPC54_DRAMCS1_BASE, LPC54_DRAMCS2_BASE,
|
|
LPC54_DRAMCS3_BASE
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_emc_timercycles
|
|
*
|
|
* Description:
|
|
* Convert nanoseconds to EMC clock cycles and clip to the provided range.
|
|
*
|
|
* Input Parameters:
|
|
* nsec - Nanoseconds to be converted.
|
|
* lower - Lower valid limit
|
|
* upper - Upper valid limit
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint32_t lpc54_emc_timercycles(uint32_t nsec, uint32_t lower,
|
|
uint32_t upper)
|
|
{
|
|
uint32_t cycles;
|
|
|
|
cycles = BOARD_EMC_FREQUENCY / MHZ_PER_HZ * nsec;
|
|
return ((cycles + MSEC_PER_SEC - 1) / MSEC_PER_SEC);
|
|
|
|
/* Decrease according to the plus */
|
|
|
|
if (cycles < lower)
|
|
{
|
|
cycles = lower;
|
|
}
|
|
else if (cycles > upper)
|
|
{
|
|
cycles = upper;
|
|
}
|
|
|
|
return cycles;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_emc_timercycles
|
|
*
|
|
* Description:
|
|
* Get the shift value to shift the mode register content by.
|
|
*
|
|
* Input Parameters:
|
|
* addrmap - EMC address map for the dynamic memory configuration.
|
|
* This is bit 14 ~ bit 7 of the EMC_DYNCONFIG.
|
|
*
|
|
* Returned Value:
|
|
* The offset value to shift the mode register content by.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_LPC54_EMC_DYNAMIC
|
|
static uint32_t lpc54_emc_modeoffset(uint32_t addrmap)
|
|
{
|
|
uint8_t offset = 0;
|
|
uint32_t columbase = addrmap & EMC_DYNCTL_COLUMNBASE_MASK;
|
|
|
|
/* First calculate the column length. */
|
|
|
|
if (columbase == 0x10)
|
|
{
|
|
offset = 8;
|
|
}
|
|
else
|
|
{
|
|
if (!columbase)
|
|
{
|
|
offset = 9;
|
|
}
|
|
else
|
|
{
|
|
offset = 8;
|
|
}
|
|
|
|
/* Add column length increase check. */
|
|
|
|
if (((addrmap & EMC_DYNCTL_COLUMNPLUS_MASK) >>
|
|
EMC_DYNCTL_COLUMNPLUS_SHIFT) == 1)
|
|
{
|
|
offset += 1;
|
|
}
|
|
else if (((addrmap & EMC_DYNCTL_COLUMNPLUS_MASK) >>
|
|
EMC_DYNCTL_COLUMNPLUS_SHIFT) == 2)
|
|
{
|
|
offset += 2;
|
|
}
|
|
else
|
|
{
|
|
/* To avoid MISRA rule 14.10 error. */
|
|
}
|
|
}
|
|
|
|
/* Add Buswidth/16. */
|
|
|
|
if (addrmap & EMC_DYNCTL_BUSWIDTH_MASK)
|
|
{
|
|
offset += 2;
|
|
}
|
|
else
|
|
{
|
|
offset += 1;
|
|
}
|
|
|
|
/* Add bank select bit if the sdram address map mode is RBC
|
|
* (row-bank-column) mode.
|
|
*/
|
|
|
|
if (!(addrmap & EMC_DYNCTL_BUSADDRMAP_MASK))
|
|
{
|
|
if (!(addrmap & EMC_DYNCTL_DEVBANKS_BITS_MASK))
|
|
{
|
|
offset += 1;
|
|
}
|
|
else
|
|
{
|
|
offset += 2;
|
|
}
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
#endif /* CONFIG_LPC54_EMC_DYNAMIC */
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_emc_initialize
|
|
*
|
|
* Description:
|
|
* This function enables the EMC clock, initializes the emc system
|
|
* configuration, and enable the EMC module.
|
|
*
|
|
* Input Parameters:
|
|
* config - Describes the EMC configuration.
|
|
*
|
|
****************************************************************************/
|
|
|
|
void lpc54_emc_initialize(FAR const struct emc_config_s *config)
|
|
{
|
|
uint32_t regval;
|
|
|
|
/* Enable EMC clock */
|
|
|
|
lpc54_emc_enableclk();
|
|
|
|
/* Reset the EMC */
|
|
|
|
lpc54_reset_emc();
|
|
|
|
/* Set the EMC system configuration */
|
|
|
|
putreg32(SYSCON_EMCCLKDIV_DIV(config->clkdiv), LPC54_SYSCON_EMCCLKDIV);
|
|
|
|
regval = config->clksrc ? SYSCON_EMCSYSCTRL_FBCLKINSEL : 0;
|
|
putreg32(regval, LPC54_SYSCON_EMCSYSCTRL);
|
|
|
|
/* Set the endian mode */
|
|
|
|
regval = config->bigendian ? EMC_CONFIG_EM : 0;
|
|
putreg32(regval, LPC54_EMC_CONFIG);
|
|
|
|
/* Enable the EMC module with normal memory map mode and normal
|
|
* work mode.
|
|
*/
|
|
|
|
putreg32(EMC_CONTROL_E, LPC54_EMC_CONTROL);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_emc_sdram_initialize
|
|
*
|
|
* Description:
|
|
* This function initializes the dynamic memory controller in external
|
|
* memory controller. This function must be called after
|
|
* lpc54_emc_initialize and before accessing the external dynamic memory.
|
|
*
|
|
* Input Parameters:
|
|
* timing - The timing and latency for dynamica memory controller
|
|
* setting. It will be used for all dynamic memory chips,
|
|
* therefore the worst timing value for all used chips must be
|
|
* given.
|
|
* chconfig - The EMC dynamic memory controller chip-independent
|
|
* by configuration array. The dimension of the array is given
|
|
* nchips.
|
|
* nchips - The number of chips to configure and the dimension of the
|
|
* chconfig array.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_LPC54_EMC_DYNAMIC
|
|
void lpc54_emc_sdram_initialize(
|
|
FAR const struct emc_dynamic_timing_config_s *timing,
|
|
FAR const struct emc_dynamic_chip_config_s *chconfig,
|
|
unsigned int nchips)
|
|
{
|
|
FAR const struct emc_dynamic_chip_config_s *config;
|
|
uintptr_t addr;
|
|
uint32_t regval;
|
|
uint32_t offset;
|
|
uint32_t data;
|
|
unsigned int i;
|
|
volatile unsigned int j;
|
|
|
|
DEBUGASSERT(timing != NULL && chconfig != NULL && nchips > 0);
|
|
|
|
/* Setting for dynamic memory controller chip independent configuration */
|
|
|
|
for (i = 0, config = chconfig;
|
|
i < nchips && config != NULL;
|
|
i++, config++)
|
|
{
|
|
uint8_t caslat;
|
|
|
|
regval = EMC_DYNCONFIG_MD(config->dyndev) |
|
|
EMC_DYNCONFIG_ADDRMAP(config->addrmap);
|
|
putreg32(regval, LPC54_EMC_DYNCONFIG(config->chndx));
|
|
|
|
/* Abstract CAS latency from the SDRAM mode register setting values */
|
|
|
|
caslat = (config->mode & EMC_SDRAM_MODE_CL_MASK) >>
|
|
EMC_SDRAM_MODE_CL_SHIFT;
|
|
regval = EMC_DYNRASCAS_RAS(config->rasnclk) |
|
|
EMC_DYNRASCAS_CAS(caslat);
|
|
putreg32(regval, LPC54_EMC_DYNRASCAS(config->chndx));
|
|
}
|
|
|
|
/* Configure the Dynamic Memory controller timing/latency for all chips. */
|
|
|
|
regval = EMC_DYNREADCONFIG(timing->rdconfig);
|
|
putreg32(regval, LPC54_EMC_DYNREADCONFIG);
|
|
|
|
regval = lpc54_emc_timercycles(timing->rp, 1, 16);
|
|
putreg32(EMC_DYNRP(regval), LPC54_EMC_DYNRP);
|
|
|
|
regval = lpc54_emc_timercycles(timing->ras, 1, 16);
|
|
putreg32(EMC_DYNRAS(regval), LPC54_EMC_DYNRAS);
|
|
|
|
regval = lpc54_emc_timercycles(timing->srex, 1, 16);
|
|
putreg32(EMC_DYNSREX(regval), LPC54_EMC_DYNSREX);
|
|
|
|
regval = lpc54_emc_timercycles(timing->apr, 1, 16);
|
|
putreg32(EMC_DYNAPR(regval), LPC54_EMC_DYNAPR);
|
|
|
|
regval = lpc54_emc_timercycles(timing->dal, 0, 15);
|
|
putreg32(EMC_DYNDAL(regval), LPC54_EMC_DYNDAL);
|
|
|
|
regval = lpc54_emc_timercycles(timing->wr, 1, 16);
|
|
putreg32(EMC_DYNWR(regval), LPC54_EMC_DYNWR);
|
|
|
|
regval = lpc54_emc_timercycles(timing->rc, 1, 32);
|
|
putreg32(EMC_DYNRC(regval), LPC54_EMC_DYNRC);
|
|
|
|
regval = lpc54_emc_timercycles(timing->rfc, 1, 32);
|
|
putreg32(EMC_DYNRFC(regval), LPC54_EMC_DYNRFC);
|
|
|
|
regval = lpc54_emc_timercycles(timing->xsr, 1, 32);
|
|
putreg32(EMC_DYNXSR(regval), LPC54_EMC_DYNXSR);
|
|
|
|
regval = lpc54_emc_timercycles(timing->rrd, 1, 16);
|
|
putreg32(EMC_DYNRRD(regval), LPC54_EMC_DYNRRD);
|
|
|
|
regval = EMC_DYNRRD(timing->mrd);
|
|
putreg32(regval, LPC54_EMC_DYNMRD);
|
|
|
|
/* Initialize the SDRAM. */
|
|
|
|
for (j = 0; j < EMC_SDRAM_WAIT_CYCLES; j++)
|
|
{
|
|
}
|
|
|
|
/* Step 2. Issue NOP command. */
|
|
|
|
regval = EMC_DYNCONTROL_CE | EMC_DYNCONTROL_CS | EMC_DYNCONTROL_I_MODE;
|
|
putreg32(regval, LPC54_EMC_DYNCONTROL);
|
|
|
|
for (j = 0; j < EMC_SDRAM_WAIT_CYCLES; j++)
|
|
{
|
|
}
|
|
|
|
/* Step 3. Issue precharge all command. */
|
|
|
|
regval = EMC_DYNCONTROL_CE | EMC_DYNCONTROL_CS | EMC_DYNCONTROL_I_PALL;
|
|
putreg32(regval, LPC54_EMC_DYNCONTROL);
|
|
|
|
/* Step 4. Issue two auto-refresh command. */
|
|
|
|
putreg32(2 * EMC_REFRESH_CLOCK_SCALE, LPC54_EMC_DYNREFRESH);
|
|
|
|
for (i = 0; i < EMC_SDRAM_WAIT_CYCLES / 2; i ++)
|
|
{
|
|
}
|
|
|
|
regval = lpc54_emc_timercycles(timing->refresh, 0,
|
|
EMC_REFRESH_CLOCK_SCALE * 2047);
|
|
putreg32(regval / EMC_REFRESH_CLOCK_SCALE, LPC54_EMC_DYNREFRESH);
|
|
|
|
/* Step 5. Issue a mode command and set the mode value. */
|
|
|
|
regval = EMC_DYNCONTROL_CE | EMC_DYNCONTROL_CS | EMC_DYNCONTROL_I_MODE;
|
|
putreg32(regval, LPC54_EMC_DYNCONTROL);
|
|
|
|
/* Calculate the mode settings here and to reach the 8 auto-refresh time
|
|
* requirement.
|
|
*/
|
|
|
|
for (i = 0, config = chconfig;
|
|
i < nchips && config != NULL;
|
|
i++, config++)
|
|
{
|
|
/* Get the shift value first. */
|
|
|
|
offset = lpc54_emc_modeoffset(config->addrmap);
|
|
addr = g_dram_csbase[config->chndx] |
|
|
((uint32_t)(config->mode & ~EMC_SDRAM_BANKCS_BA_MASK) <<
|
|
offset);
|
|
|
|
/* Set the right mode setting value. */
|
|
|
|
data = *(volatile uint32_t *)addr;
|
|
data = data;
|
|
}
|
|
|
|
if (config->dyndev)
|
|
{
|
|
/* Add extended mode register if the low-power sdram is used. */
|
|
|
|
regval = EMC_DYNCONTROL_CE | EMC_DYNCONTROL_CS |
|
|
EMC_DYNCONTROL_I_MODE;
|
|
putreg32(regval, LPC54_EMC_DYNCONTROL);
|
|
|
|
/* Calculate the mode settings for extended mode register. */
|
|
|
|
for (i = 0, config = chconfig;
|
|
i < nchips && config != NULL;
|
|
i++, config++)
|
|
{
|
|
/* Get the shift value first. */
|
|
|
|
offset = lpc54_emc_modeoffset(config->addrmap);
|
|
addr = (g_dram_csbase[config->chndx] |
|
|
(((uint32_t)(config->extmode & ~EMC_SDRAM_BANKCS_BA_MASK) |
|
|
EMC_SDRAM_BANKCS_BA1_MASK) << offset));
|
|
|
|
/* Set the right mode setting value. */
|
|
|
|
data = *(volatile uint32_t *)addr;
|
|
data = data;
|
|
}
|
|
}
|
|
|
|
/* Step 6. Issue normal operation command. */
|
|
|
|
regval = EMC_DYNCONTROL_I_NORMAL;
|
|
putreg32(regval, LPC54_EMC_DYNCONTROL);
|
|
|
|
/* The buffer will be disabled when do the sdram initialization and
|
|
* enabled after the initialization during normal operation.
|
|
*/
|
|
|
|
for (i = 0, config = chconfig;
|
|
i < nchips && config != NULL;
|
|
i++, config++)
|
|
{
|
|
uintptr_t regaddr = LPC54_EMC_DYNCONFIG(config->chndx);
|
|
|
|
regval = getreg32(regaddr);
|
|
regval |= EMC_DYNCONFIG_B;
|
|
putreg32(regval, regaddr);
|
|
}
|
|
}
|
|
#endif /* CONFIG_LPC54_EMC_DYNAMIC */
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_emc_sram_initialize
|
|
*
|
|
* Description:
|
|
* This function initializes the static memory controller in external
|
|
* memory controller. This function must be called after
|
|
* lpc54_emc_initialize and before accessing the external dynamic memory.
|
|
*
|
|
* Input Parameters:
|
|
* extwait - The extended wait timeout or the read/write transfer time.
|
|
* This is common for all static memory chips and set with
|
|
* NULL if not required.
|
|
* statconfig - The EMC static memory controller chip independent
|
|
* configuration array. The dimension of the array is nchips.
|
|
* nchips - The total static memory chip numbers been used and the
|
|
* number of entries in the statconfig array.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_LPC54_EMC_STATIC
|
|
void lpc54_emc_sram_initialize(FAR uint32_t *extwait,
|
|
FAR const struct emc_static_chip_config_s *statconfig,
|
|
uint32_t nchips)
|
|
{
|
|
FAR const struct emc_static_chip_config_s *config;
|
|
uint32_t regval;
|
|
unsigned int i;
|
|
|
|
/* Initialize extended wait. */
|
|
|
|
DEBUGASSERT(statconfig != NULL && nchips > 0);
|
|
|
|
if (extwait)
|
|
{
|
|
#ifdef CONFIG_DEBUG_ASSERTIONS
|
|
for (i = 0, config = statconfig;
|
|
i < nchips && config != NULL; i++,
|
|
config++)
|
|
{
|
|
DEBUGASSERT(config->specconfig & EMC_ASYNCPAGEENABLE);
|
|
}
|
|
#endif
|
|
|
|
regval = lpc54_emc_timercycles(*extwait, 1, 1024);
|
|
putreg32(EMC_STATEXTWAIT(regval), LPC54_EMC_STATEXTWAIT);
|
|
}
|
|
|
|
/* Initialize the static memory chip specific configure. */
|
|
|
|
for (i = 0, config = statconfig;
|
|
i < nchips && config != NULL; i++,
|
|
config++)
|
|
{
|
|
regval = config->specconfig | config->memwidth;
|
|
putreg32(regval, LPC54_EMC_STATCONFIG(config->chndx));
|
|
|
|
regval = lpc54_emc_timercycles(config->waitwriteen, 1, 16);
|
|
putreg32(EMC_STATWAITWEN(regval),
|
|
LPC54_EMC_STATWAITWEN(config->chndx));
|
|
|
|
regval = lpc54_emc_timercycles(config->waitouten, 0, 15);
|
|
putreg32(EMC_STATWAITOEN(regval),
|
|
LPC54_EMC_STATWAITOEN(config->chndx));
|
|
|
|
regval = lpc54_emc_timercycles(config->waitread, 1, 32);
|
|
putreg32(EMC_STATWAITRD(regval),
|
|
LPC54_EMC_STATWAITRD(config->chndx));
|
|
|
|
regval = lpc54_emc_timercycles(config->waitreadpage, 1, 32);
|
|
putreg32(EMC_STATWAITPAGE(regval),
|
|
LPC54_EMC_STATWAITPAGE(config->chndx));
|
|
|
|
regval = lpc54_emc_timercycles(config->waitwrite, 2, 33);
|
|
putreg32(EMC_STATWAITWR(regval),
|
|
LPC54_EMC_STATWAITWR(config->chndx));
|
|
|
|
regval = lpc54_emc_timercycles(config->waitturn, 1, 16);
|
|
putreg32(EMC_STATWAITTURN(regval),
|
|
LPC54_EMC_STATWAITTURN(config->chndx));
|
|
}
|
|
}
|
|
#endif /* CONFIG_LPC54_EMC_STATIC */
|
|
|
|
#endif /* CONFIG_LPC54_EMC */
|