From dab97de4ea36b6842f5b8ea30a75d7bd80323230 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sun, 10 Dec 2017 16:07:30 -0600 Subject: [PATCH] arch/arm/src/lpc54xx: Bring in NXP support for external SDRAM. --- arch/arm/src/lpc54xx/Kconfig | 2 +- arch/arm/src/lpc54xx/Make.defs | 4 +- arch/arm/src/lpc54xx/chip/lpc54_emc.h | 49 ++- arch/arm/src/lpc54xx/chip/lpc54_syscon.h | 2 +- arch/arm/src/lpc54xx/lpc54_emc.c | 371 +++++++++++++++++- arch/arm/src/lpc54xx/lpc54_emc.h | 108 ++++- configs/lpcxpresso-lpc54628/include/board.h | 9 + configs/lpcxpresso-lpc54628/nsh/defconfig | 2 + configs/lpcxpresso-lpc54628/src/Makefile | 6 + configs/lpcxpresso-lpc54628/src/lpc54_sdram.c | 52 ++- 10 files changed, 558 insertions(+), 47 deletions(-) diff --git a/arch/arm/src/lpc54xx/Kconfig b/arch/arm/src/lpc54xx/Kconfig index 034a8be6a6..eaaa786ac6 100644 --- a/arch/arm/src/lpc54xx/Kconfig +++ b/arch/arm/src/lpc54xx/Kconfig @@ -253,6 +253,6 @@ config LPC54_EMC_STATIC config LPC54_EMC_DYNAMIC bool "EMC dynamic memory support" - default n + default y endmenu # LPC54xx Peripheral Selection diff --git a/arch/arm/src/lpc54xx/Make.defs b/arch/arm/src/lpc54xx/Make.defs index d0facb74c6..fda9880927 100644 --- a/arch/arm/src/lpc54xx/Make.defs +++ b/arch/arm/src/lpc54xx/Make.defs @@ -98,11 +98,11 @@ ifneq ($(CONFIG_ARCH_IDLE_CUSTOM),y) CHIP_CSRCS += lpc54_idle.c endif -ifneq ($(CONFIG_LPC54_GPIOIRQ),y) +ifeq ($(CONFIG_LPC54_GPIOIRQ),y) CHIP_CSRCS += lpc54_gpioirq.c endif -ifneq ($(CONFIG_LPC54_EMC),y) +ifeq ($(CONFIG_LPC54_EMC),y) CHIP_CSRCS += lpc54_emc.c endif diff --git a/arch/arm/src/lpc54xx/chip/lpc54_emc.h b/arch/arm/src/lpc54xx/chip/lpc54_emc.h index c8fb601979..9a722b86b2 100644 --- a/arch/arm/src/lpc54xx/chip/lpc54_emc.h +++ b/arch/arm/src/lpc54xx/chip/lpc54_emc.h @@ -47,10 +47,11 @@ * Pre-processor Definitions ****************************************************************************************************/ -#define LPC54_EMC_CS0 0 -#define LPC54_EMC_CS1 1 -#define LPC54_EMC_CS2 2 -#define LPC54_EMC_CS3 3 +#define LPC54_EMC_CS0 0 +#define LPC54_EMC_CS1 1 +#define LPC54_EMC_CS2 2 +#define LPC54_EMC_CS3 3 +#define LPC54_EMC_NCS 4 /* Register offsets *********************************************************************************/ @@ -75,16 +76,16 @@ /* Per-chip select dynamic memory registers */ -#define LPC54_EMC_DYNCS_OFFSET(n) (0x0100 + (n) << 5) +#define LPC54_EMC_DYNCS_OFFSET(n) (0x0100 + ((uintptr_t)(n) << 5)) #define LPC54_EMC_DYNCONFIG_OFFSET 0x0000 /* Configuration information for CSn */ #define LPC54_EMC_DYNRASCAS_OFFSET 0x0004 /* RAS and CAS latencies for CSn */ -#define LPC54_EMC_DYNCONFIGn_OFFSET(n) (0x0100 + (n) << 5) -#define LPC54_EMC_DYNRASCASn_OFFSET(n) (0x0104 + (n) << 5) +#define LPC54_EMC_DYNCONFIGn_OFFSET(n) (0x0100 + ((uintptr_t)(n) << 5)) +#define LPC54_EMC_DYNRASCASn_OFFSET(n) (0x0104 + ((uintptr_t)(n) << 5)) /* Per-chip select static memory registers */ -#define LPC54_EMC_STATCS_OFFSET(n) (0x0200 + (n) << 5) +#define LPC54_EMC_STATCS_OFFSET(n) (0x0200 + ((uintptr_t)(n) << 5)) #define LPC54_EMC_STATCONFIG_OFFSET 0x0000 /* Configuration for CSn */ #define LPC54_EMC_STATWAITWEN_OFFSET 0x0004 /* Delay to write enable */ #define LPC54_EMC_STATWAITOEN_OFFSET 0x0008 /* Delay to output enable */ @@ -93,13 +94,13 @@ #define LPC54_EMC_STATWAITWR_OFFSET 0x0014 /* Delay from EMC_CS0 to a write access */ #define LPC54_EMC_STATWAITTURN_OFFSET 0x0018 /* Number of bus turnaround cycles */ -#define LPC54_EMC_STATCONFIGn_OFFSET(n) (0x0200 + (n) << 5) -#define LPC54_EMC_STATWAITWENn_OFFSET(n) (0x0204 + (n) << 5) -#define LPC54_EMC_STATWAITOENn_OFFSET(n) (0x0208 + (n) << 5) -#define LPC54_EMC_STATWAITRDn_OFFSET(n) (0x020c + (n) << 5) -#define LPC54_EMC_STATWAITPAGEn_OFFSET(n) (0x0210 + (n) << 5) -#define LPC54_EMC_STATWAITWRn_OFFSET(n) (0x0214 + (n) << 5) -#define LPC54_EMC_STATWAITTURNn_OFFSET(n) (0x0218 + (n) << 5) +#define LPC54_EMC_STATCONFIGn_OFFSET(n) (0x0200 + ((uintptr_t)(n) << 5)) +#define LPC54_EMC_STATWAITWENn_OFFSET(n) (0x0204 + ((uintptr_t)(n) << 5)) +#define LPC54_EMC_STATWAITOENn_OFFSET(n) (0x0208 + ((uintptr_t)(n) << 5)) +#define LPC54_EMC_STATWAITRDn_OFFSET(n) (0x020c + ((uintptr_t)(n) << 5)) +#define LPC54_EMC_STATWAITPAGEn_OFFSET(n) (0x0210 + ((uintptr_t)(n) << 5)) +#define LPC54_EMC_STATWAITWRn_OFFSET(n) (0x0214 + ((uintptr_t)(n) << 5)) +#define LPC54_EMC_STATWAITTURNn_OFFSET(n) (0x0218 + ((uintptr_t)(n) << 5)) /* Register addresses *******************************************************************************/ @@ -163,8 +164,8 @@ #define EMC_DYNCONTROL_CE (1 << 0) /* Bit 0: Dynamic memory clock enable */ #define EMC_DYNCONTROL_CS (1 << 1) /* Bit 1: Dynamic memory clock control */ #define EMC_DYNCONTROL_SR (1 << 2) /* Bit 2: Self-refresh request, EMCSREFREQ */ -#define EMC_DYNCONTROL_MMC (1 << 5) /* Bit 5 Memory clock control */ -#define EMC_DYNCONTROL_I_SHIFT (7) /* Bit 7-8: SDRAM initialization */ */ +#define EMC_DYNCONTROL_MMC (1 << 5) /* Bit 5: Memory clock control */ +#define EMC_DYNCONTROL_I_SHIFT (7) /* Bit 7-8: SDRAM initialization */ #define EMC_DYNCONTROL_I_MASK (3 << EMC_DYNCONTROL_I_SHIFT) # define EMC_DYNCONTROL_I_NORMAL (0 << EMC_DYNCONTROL_I_SHIFT) /* Issue SDRAM NORMAL operation command */ # define EMC_DYNCONTROL_I_MODE (1 << EMC_DYNCONTROL_I_SHIFT) /* Issue SDRAM MODE command */ @@ -182,9 +183,10 @@ #define EMC_DYNREADCONFIG_SHIFT (0) /* Bits 0-1: Read data strategy */ #define EMC_DYNREADCONFIG_MASK (3 << EMC_DYNREADCONFIG_SHIFT) +# define EMC_DYNREADCONFIG(n) ((uint32_t)(n) << EMC_DYNREADCONFIG_SHIFT) # define EMC_DYNREADCONFIG_PLUS0 (1 << EMC_DYNREADCONFIG_SHIFT) /* Using EMCCLKDELAY */ -# define EMC_DYNREADCONFIG_PLUS1 (2 << EMC_DYNREADCONFIG_SHIFT) /* Plus one clock cycle using EMCCLKDELAY */ -# define EMC_DYNREADCONFIG_PLUS2 (3 << EMC_DYNREADCONFIG_SHIFT) /* Plus two clock cycles using EMCCLKDELAY */ +# define EMC_DYNREADCONFIG_PLUS1 (2 << EMC_DYNREADCONFIG_SHIFT) /* Plus one clock cycle using EMCCLKDELAY */ +# define EMC_DYNREADCONFIG_PLUS2 (3 << EMC_DYNREADCONFIG_SHIFT) /* Plus two clock cycles using EMCCLKDELAY */ /* Precharge command period */ @@ -214,7 +216,7 @@ #define EMC_DYNDAL_SHIFT (0) /* Bits 0-3: Data-in to active command */ #define EMC_DYNDAL_MASK (15 << EMC_DYNDAL_SHIFT) -# define EMC_DYNDAL(n) ((uint32_t)((n)-1) << EMC_DYNDAL_SHIFT) +# define EMC_DYNDAL(n) ((uint32_t)(n) << EMC_DYNDAL_SHIFT) /* Write recovery time */ @@ -263,6 +265,7 @@ #define EMC_DYNCONFIG_ #define EMC_DYNCONFIG_MD_SHIFT (3) /* Bits 3-4: Memory device */ #define EMC_DYNCONFIG_MD_MASK (3 << EMC_DYNCONFIG_MD_SHIFT) +# define EMC_DYNCONFIG_MD(n) ((uint32_t)(n) << EMC_DYNCONFIG_MD_SHIFT) # define EMC_DYNCONFIG_MD_SDRAM (0 << EMC_DYNCONFIG_MD_SHIFT) /* SDRAM */ # define EMC_DYNCONFIG_MD_LPDRAM (1 << EMC_DYNCONFIG_MD_SHIFT) /* Low-power SDRAM */ #define EMC_DYNCONFIG_AM0_SHIFT (7) /* Bits 7-12: See Table 656 in User Manual */ @@ -272,12 +275,16 @@ #define EMC_DYNCONFIG_B (1 << 19) /* Bit 19: Buffer enable */ #define EMC_DYNCONFIG_P (1 << 20) /* Bit 20: Write protect */ +#define EMC_DYNCONFIG_ADDRMAP_SHIFT EMC_DYNCONFIG_AM0_SHIFT +#define EMC_DYNCONFIG_ADDRMAP_MASK (EMC_DYNCONFIG_AM0_MASK | EMC_DYNCONFIG_AM1) +# define EMC_DYNCONFIG_ADDRMAP(n) ((uint32_t)(n) << EMC_DYNCONFIG_ADDRMAP_SHIFT) + /* Dynamic Memory RAS and CAS Delay registers */ #define EMC_DYNRASCAS_RAS_SHIFT (0) /* Bits 0-1: RAS latency */ #define EMC_DYNRASCAS_RAS_MASK (3 << EMC_DYNRASCAS_RAS_SHIFT) # define EMC_DYNRASCAS_RAS(n) ((uint32_t)(n) << EMC_DYNRASCAS_RAS_SHIFT) -#define EMC_DYNRASCAS_CAS_SHIFT (8) /* Bits 9-9: CAS latency */ +#define EMC_DYNRASCAS_CAS_SHIFT (8) /* Bits 8-9: CAS latency */ #define EMC_DYNRASCAS_CAS_MASK (3 << EMC_DYNRASCAS_CAS_SHIFT) # define EMC_DYNRASCAS_CAS(n) ((uint32_t)(n) << EMC_DYNRASCAS_CAS_SHIFT) diff --git a/arch/arm/src/lpc54xx/chip/lpc54_syscon.h b/arch/arm/src/lpc54xx/chip/lpc54_syscon.h index 55f22219bd..7365cd1fb6 100644 --- a/arch/arm/src/lpc54xx/chip/lpc54_syscon.h +++ b/arch/arm/src/lpc54xx/chip/lpc54_syscon.h @@ -619,7 +619,7 @@ #define SYSCON_EMCCLKDIV_DIV_SHIFT (9) /* Bits 0-7: Clock divider value */ #define SYSCON_EMCCLKDIV_DIV_MASK (0xff < +#include + +#include + #include "up_arch.h" +#include "chip/lpc54_syscon.h" #include "chip/lpc54_emc.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 ****************************************************************************/ @@ -60,13 +221,15 @@ * Name: lpc54_emc_initialize * * Description: - * This function enables the EMC clock, initializes the emc system + * 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(uintptr_t base, - FAR const struct emc_config_s *config) +void lpc54_emc_initialize(FAR const struct emc_config_s *config) { uint32_t regval; @@ -78,7 +241,7 @@ void lpc54_emc_initialize(uintptr_t base, putreg32(SYSCON_PRESETCTRL2_EMC, LPC54_SYSCON_PRESETCTRLSET2); putreg32(SYSCON_PRESETCTRL2_EMC, LPC54_SYSCON_PRESETCTRLCLR2); - + /* Set the EMC sytem configure */ putreg32(SYSCON_EMCCLKDIV_DIV(config->clkdiv), LPC54_SYSCON_EMCCLKDIV); @@ -88,7 +251,7 @@ void lpc54_emc_initialize(uintptr_t base, /* Set the endian mode */ - regval = config->endian ? EMC_CONFIG_EM : 0; + 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. */ @@ -96,4 +259,202 @@ void lpc54_emc_initialize(uintptr_t base, 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 + * configuration array. The dimension of the array is given by + * 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 struct emc_dynamic_timing_config_s *timing, + FAR struct emc_dynamic_chip_config_s *chconfig, + unsigned int nchips) +{ + FAR 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; + + /* 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 reigster 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 opeation. + */ + + 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 */ + #endif /* CONFIG_LPC54_EMC */ diff --git a/arch/arm/src/lpc54xx/lpc54_emc.h b/arch/arm/src/lpc54xx/lpc54_emc.h index a170e3137a..15d82ab7f2 100644 --- a/arch/arm/src/lpc54xx/lpc54_emc.h +++ b/arch/arm/src/lpc54xx/lpc54_emc.h @@ -47,6 +47,10 @@ ****************************************************************************/ #include + +#include +#include + #include "lpc54_config.h" #ifdef CONFIG_LPC54_EMC @@ -57,7 +61,7 @@ /* EMC Feedback clock input source selection */ -enum _emc_fbclk_src_e +enum emc_fbclksrc_e { EMC_INTLOOPBACK = 0, /* Use the internal loop back from EMC_CLK output */ EMC_FBCLLK /* Use the external EMC_FBCLK input */ @@ -67,9 +71,70 @@ enum _emc_fbclk_src_e struct emc_config_s { - bool bigendian; /* True: Memory is big-endian */ - uint8_t clksrc; /* The feedback clock source. */ - uint8_t clkdiv; /* EMC_CLK = AHB_CLK / (emc_clkDiv + 1). */ + bool bigendian; /* True: Memory is big-endian */ + uint8_t clksrc; /* The feedback clock source. */ + uint8_t clkdiv; /* EMC_CLK = AHB_CLK / (emc_clkDiv + 1). */ +}; + +/* EMC dynamic read strategy. */ + +enum emc_dynamic_read_e +{ + EMC_NODELAY = 0, /* No delay */ + EMC_CMDDELAY, /* Command delayed strategy, using EMCCLKDELAY */ + EMC_CMDDELAYPLUS1, /* Command delayed strategy pluse one clock cycle + * using EMCCLKDELAY */ + EMC_CMDDELAYPLUS2, /* Command delayed strategy pulse two clock cycle + * using EMCCLKDELAY */ +}; + +/* EMC dynamic timing/delay configure structure. */ + +struct emc_dynamic_timing_config_s +{ + uint8_t rdconfig; /* Dynamic read strategy (see enum emc_dynamic_read_e) */ + uint32_t refresh; /* The refresh period in units of nanoseconds */ + uint32_t rp; /* Precharge command period in units of nanoseconds */ + uint32_t ras; /* Active to precharge command period in units of + * nanoseconds */ + uint32_t srex; /* Self-refresh exit time in units of nanoseconds */ + uint32_t apr; /* Last data out to active command time in units of + * nanoseconds */ + uint32_t dal; /* Data-in to active command in units of nanoseconds */ + uint32_t wr; /* Write recovery time in unit of nanosecond */ + uint32_t rc; /* Active to active command period in units of + * nanoseconds. */ + uint32_t rfc; /* Auto-refresh period and auto-refresh to active + * command period in unit of nanosecond */ + uint32_t xsr; /* Exit self-refresh to active command time in units + * of nanoseconds */ + uint32_t rrd; /* Active bank A to active bank B latency in units of + * nanoseconds */ + uint8_t mrd; /* Load mode register to active command time in units + * of EMCCLK cycles */ +}; + +/* EMC dynamic memory device. */ + +enum emc_dynamic_device_e +{ + EMC_SDRAM = 0, /* Dynamic memory device: SDRAM. */ + EMC_LPSDRAM /* Dynamic memory device: Low-power SDRAM. */ +}; + +/* EMC dynamic memory controller independent chip configuration structure */ + +struct emc_dynamic_chip_config_s +{ + uint8_t chndx; /* Chip Index, range from 0 ~ EMC_DYNAMIC_MEMDEV_NUM - 1. */ + uint8_t dyndev; /* All chips shall use the same device setting. mixed + * use are not supported. */ + uint8_t rasnclk; /* Active to read/write delay tRCD. */ + uint16_t mode; /* Sdram mode register setting. */ + uint16_t extmode; /* Used for low-power sdram device. The extended mode + * register. */ + uint8_t addrmap; /* Dynamic device address mapping, choose the address + * mapping for your specific device. */ }; /**************************************************************************** @@ -80,13 +145,42 @@ struct emc_config_s * Name: lpc54_emc_initialize * * Description: - * This function enables the EMC clock, initializes the emc system + * 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(uintptr_t base, - FAR const struct emc_config_s *config); +void lpc54_emc_initialize(FAR const struct emc_config_s *config); + +/**************************************************************************** + * 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 + * configuration array. The dimension of the array is given by + * 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 struct emc_dynamic_timing_config_s *timing, + FAR struct emc_dynamic_chip_config_s *chconfig, + unsigned int nchips); +#endif /* CONFIG_LPC54_EMC_DYNAMIC */ #endif /* CONFIG_LPC54_EMC */ #endif /* __ARCH_ARM_SRC_LPC54XX_LPC54_EMC_H */ diff --git a/configs/lpcxpresso-lpc54628/include/board.h b/configs/lpcxpresso-lpc54628/include/board.h index f0dbd64fce..26600dcc15 100644 --- a/configs/lpcxpresso-lpc54628/include/board.h +++ b/configs/lpcxpresso-lpc54628/include/board.h @@ -178,6 +178,15 @@ #define BOARD_FLEXCOMM0_CLKSEL SYSCON_FCLKSEL_FRO12M #define BOARD_FLEXCOMM0_FCLK LPC54_FRO_12MHZ +/* EMC */ + +#ifdef BOARD_220MHz +#define BOARD_EMC_CLKDIV 3 /* EMC Clock = CPU FREQ/3 */ +#else /* if BOARD_180MHz */ +#define BOARD_EMC_CLKDIV 2 /* EMC Clock = CPU FREQ/2 */ +#endif +#define BOARD_EMC_FREQUENCY (BOARD_CPU_FREQUENCY / BOARD_EMC_CLKDIV) + /* LED definitions *********************************************************/ /* The LPCXpress-LPC54628 has three user LEDs: D9, D11, and D12. These * LEDs are for application use. They are illuminated when the driving diff --git a/configs/lpcxpresso-lpc54628/nsh/defconfig b/configs/lpcxpresso-lpc54628/nsh/defconfig index 7345c43ee9..47d9885229 100644 --- a/configs/lpcxpresso-lpc54628/nsh/defconfig +++ b/configs/lpcxpresso-lpc54628/nsh/defconfig @@ -13,6 +13,8 @@ CONFIG_FAT_LCNAMES=y CONFIG_FAT_LFN=y CONFIG_FS_FAT=y CONFIG_FS_PROCFS=y +CONFIG_LPC54_EMC_DYNAMIC=y +CONFIG_LPC54_EMC=y CONFIG_LPC54_USART0=y CONFIG_MAX_TASKS=16 CONFIG_MAX_WDOGPARMS=2 diff --git a/configs/lpcxpresso-lpc54628/src/Makefile b/configs/lpcxpresso-lpc54628/src/Makefile index f305e1be1a..b87994bd86 100644 --- a/configs/lpcxpresso-lpc54628/src/Makefile +++ b/configs/lpcxpresso-lpc54628/src/Makefile @@ -46,4 +46,10 @@ ifeq ($(CONFIG_LIB_BOARDCTL),y) CSRCS += lpc54_appinit.c endif +ifeq ($(CONFIG_LPC54_EMC),y) +ifeq ($(CONFIG_LPC54_EMC_DYNAMIC),y) +CSRCS += lpc54_sdram.c +endif +endif + include $(TOPDIR)/configs/Board.mk diff --git a/configs/lpcxpresso-lpc54628/src/lpc54_sdram.c b/configs/lpcxpresso-lpc54628/src/lpc54_sdram.c index 317c40e476..c00c144dc1 100644 --- a/configs/lpcxpresso-lpc54628/src/lpc54_sdram.c +++ b/configs/lpcxpresso-lpc54628/src/lpc54_sdram.c @@ -44,7 +44,13 @@ #include -#ifdef CONFIG_LPC54_EMC +#if defined(CONFIG_LPC54_EMC) && defined(CONFIG_LPC54_EMC_DYNAMIC) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define EMC_CLOCK_PERIOD_NS (1000000000 / BOARD_EMC_FREQUENCY) /**************************************************************************** * Private Data @@ -63,6 +69,37 @@ static const struct emc_config_s g_emc_config = #endif }; +/* Dynamic memory timing configuration. */ + +static const struct emc_dynamic_timing_config_s g_emc_dynconfig = +{ + .rdconfig = EMC_CMDDELAY; + .refresh = (64 * 1000000 / 4096) /* 4096 rows/ 64ms */; + .rp = 18; + .ras = 42; + .srex = 67; + .apr = 18; + .wr = EMC_CLOCK_PERIOD_NS + 6; /* one clk + 6ns */ + .dal = EMC_CLOCK_PERIOD_NS + 24; + .rc = 60; + .rfc = 60; + .xsr = 67; + .rrd = 23; + .mrd = 2; +}; + +/* Dynamic memory chip specific configuration: Chip 0 - MTL48LC8M16A2B4-6A */ + +static onst struct emc_dynamic_chip_config_s g_emc_dynchipconfig; +{ + .chndx = 0; + .dyndev = EMC_SDRAM; + .rasnclk = 2; + .mode = 0x23; + .extmode = 0; /* LPSDRAM only */ + .addrmap = 0x09; /* 128Mbits (8M*16, 4banks, 12 rows, 9 columns)*/ +}; + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -77,18 +114,13 @@ static const struct emc_config_s g_emc_config = void lpc54_sdram_initialize(void) { - /* Dynamic memory timing configuration. */ -#warning Missing logic - - /* Dynamic memory chip specific configuration: Chip 0 - MTL48LC8M16A2B4-6A */ -#warning Missing logic - /* EMC Basic configuration. */ - lpc54_emc_initialize(EMC, &g_emc_config); + lpc54_emc_initialize(&g_emc_config); /* EMC Dynamc memory configuration. */ -#warning Missing logic + + lpc54_emc_dram_initialize(&g_emc_dynconfig, &g_emc_dynchipconfig, 1); } -#endif /* CONFIG_LPC54_EMC */ +#endif /* CONFIG_LPC54_EMC && CONFIG_LPC54_EMC_DYNAMIC */