| /* | |
| * Copyright (c) 2016, Freescale Semiconductor, Inc. | |
| * Copyright 2016-2019 NXP | |
| * All rights reserved. | |
| * | |
| * SPDX-License-Identifier: BSD-3-Clause | |
| */ | |
| #include "fsl_emc.h" | |
| /******************************************************************************* | |
| * Definitions | |
| ******************************************************************************/ | |
| /* Component ID definition, used by tools. */ | |
| #ifndef FSL_COMPONENT_ID | |
| #define FSL_COMPONENT_ID "platform.drivers.emc" | |
| #endif | |
| /*! @brief Define macros for EMC driver. */ | |
| #define EMC_REFRESH_CLOCK_PARAM (16U) | |
| #define EMC_SDRAM_WAIT_CYCLES (2000U) | |
| #define EMC_DYNCTL_COLUMNBASE_OFFSET (0U) | |
| #define EMC_DYNCTL_COLUMNBASE_MASK (0x3U) | |
| #define EMC_DYNCTL_COLUMNPLUS_OFFSET (3U) | |
| #define EMC_DYNCTL_COLUMNPLUS_MASK (0x18U) | |
| #define EMC_DYNCTL_BUSWIDTH_MASK (0x80U) | |
| #define EMC_DYNCTL_BUSADDRMAP_MASK (0x20U) | |
| #define EMC_DYNCTL_DEVBANKS_BITS_MASK (0x1cU) | |
| #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_DIV_ROUND_UP(n, m) (((n) + (m)-1U) / (m)) | |
| /******************************************************************************* | |
| * Prototypes | |
| ******************************************************************************/ | |
| /*! | |
| * @brief Get instance number for EMC module. | |
| * | |
| * @param base EMC peripheral base address | |
| */ | |
| static uint32_t EMC_GetInstance(EMC_Type *base); | |
| /*! | |
| * @brief Get the clock cycles of EMC clock. | |
| * The function is used to calculate the multiple of the | |
| * 16 EMCCLKs between the timer_Ns period. | |
| * | |
| * @param base EMC peripheral base address | |
| * @param timer_Ns The timer/period in unit of nanosecond | |
| * @param plus The plus added to the register settings to reach the calculated cycles. | |
| * @return The calculated cycles. | |
| */ | |
| static uint32_t EMC_CalculateTimerCycles(EMC_Type *base, uint32_t timer_Ns, uint32_t plus); | |
| /*! | |
| * @brief Get the shift value to shift the mode register content by. | |
| * | |
| * @param addrMap EMC address map for the dynamic memory configuration. | |
| * It is the bit 14 ~ bit 7 of the EMC_DYNAMICCONFIG. | |
| * @return The offset value to shift the mode register content by. | |
| */ | |
| static uint32_t EMC_ModeOffset(uint32_t addrMap); | |
| /******************************************************************************* | |
| * Variables | |
| ******************************************************************************/ | |
| #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) | |
| /*! @brief Pointers to EMC clocks for each instance. */ | |
| static const clock_ip_name_t s_EMCClock[FSL_FEATURE_SOC_EMC_COUNT] = EMC_CLOCKS; | |
| #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ | |
| #if !(defined(FSL_FEATURE_EMC_HAS_NO_RESET) && FSL_FEATURE_EMC_HAS_NO_RESET) | |
| /*! @brief Pointers to EMC resets for each instance. */ | |
| static const reset_ip_name_t s_emcResets[] = EMC_RSTS; | |
| #endif | |
| /*! @brief Pointers to EMC bases for each instance. */ | |
| static const EMC_Type *const s_EMCBases[] = EMC_BASE_PTRS; | |
| /*! @brief Define the start address for each chip controlled by EMC. */ | |
| static const uint32_t s_EMCDYCSBases[] = EMC_DYCS_ADDRESS; | |
| /******************************************************************************* | |
| * Code | |
| ******************************************************************************/ | |
| static uint32_t EMC_GetInstance(EMC_Type *base) | |
| { | |
| uint32_t instance; | |
| /* Find the instance index from base address mappings. */ | |
| for (instance = 0; instance < ARRAY_SIZE(s_EMCBases); instance++) | |
| { | |
| if (s_EMCBases[instance] == base) | |
| { | |
| break; | |
| } | |
| } | |
| assert(instance < ARRAY_SIZE(s_EMCBases)); | |
| return instance; | |
| } | |
| static uint32_t EMC_CalculateTimerCycles(EMC_Type *base, uint32_t timer_Ns, uint32_t plus) | |
| { | |
| uint32_t cycles; | |
| cycles = CLOCK_GetEmcClkFreq() / EMC_HZ_ONEMHZ * timer_Ns; | |
| cycles = EMC_DIV_ROUND_UP(cycles, EMC_MILLISECS_ONESEC); /* Round up. */ | |
| /* Decrese according to the plus. */ | |
| if (cycles >= plus) | |
| { | |
| cycles = cycles - plus; | |
| } | |
| else | |
| { | |
| cycles = 0; | |
| } | |
| return cycles; | |
| } | |
| static uint32_t EMC_ModeOffset(uint32_t addrMap) | |
| { | |
| uint8_t offset = 0; | |
| uint32_t columbase = addrMap & EMC_DYNCTL_COLUMNBASE_MASK; | |
| /* First calculate the column length. */ | |
| if (columbase == 2U) | |
| { | |
| offset = 8; | |
| } | |
| else | |
| { | |
| if (0U == columbase) | |
| { | |
| offset = 9; | |
| } | |
| else | |
| { | |
| offset = 8; | |
| } | |
| /* Add column length increase check. */ | |
| offset += (uint8_t)((addrMap & EMC_DYNCTL_COLUMNPLUS_MASK) >> EMC_DYNCTL_COLUMNPLUS_OFFSET); | |
| } | |
| /* Add Buswidth/16. */ | |
| if (0U != (addrMap & EMC_DYNCTL_BUSWIDTH_MASK)) | |
| { | |
| offset += 2U; | |
| } | |
| else | |
| { | |
| offset += 1U; | |
| } | |
| /* Add bank select bit if the sdram address map mode is RBC(row-bank-column) mode. */ | |
| if (0U == (addrMap & EMC_DYNCTL_BUSADDRMAP_MASK)) | |
| { | |
| if (0U == (addrMap & EMC_DYNCTL_DEVBANKS_BITS_MASK)) | |
| { | |
| offset += 1U; | |
| } | |
| else | |
| { | |
| offset += 2U; | |
| } | |
| } | |
| return offset; | |
| } | |
| /*! | |
| * brief Initializes the basic for EMC. | |
| * This function ungates the EMC clock, initializes the emc system configure | |
| * and enable the EMC module. This function must be called in the first step to initialize | |
| * the external memory. | |
| * | |
| * param base EMC peripheral base address. | |
| * param config The EMC basic configuration. | |
| */ | |
| void EMC_Init(EMC_Type *base, emc_basic_config_t *config) | |
| { | |
| #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) | |
| /* Enable the clock. */ | |
| CLOCK_EnableClock((s_EMCClock[EMC_GetInstance(base)])); | |
| #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ | |
| #if !(defined(FSL_FEATURE_EMC_HAS_NO_RESET) && FSL_FEATURE_EMC_HAS_NO_RESET) | |
| /* Reset the EMC module */ | |
| RESET_PeripheralReset(s_emcResets[EMC_GetInstance(base)]); | |
| #endif | |
| /* Reset the EMC. */ | |
| SYSCON->PRESETCTRL[2] |= SYSCON_PRESETCTRL_EMC_RESET_MASK; | |
| SYSCON->PRESETCTRL[2] &= ~SYSCON_PRESETCTRL_EMC_RESET_MASK; | |
| /* Set the EMC sytem configure. */ | |
| SYSCON->EMCCLKDIV = SYSCON_EMCCLKDIV_DIV(config->emcClkDiv); | |
| SYSCON->EMCSYSCTRL = SYSCON_EMCSYSCTRL_EMCFBCLKINSEL(config->fbClkSrc); | |
| /* Set the endian mode. */ | |
| base->CONFIG = (uint32_t)config->endian; | |
| /* Enable the EMC module with normal memory map mode and normal work mode. */ | |
| base->CONTROL = EMC_CONTROL_E_MASK; | |
| } | |
| /*! | |
| * brief Initializes the dynamic memory controller. | |
| * This function initializes the dynamic memory controller in external memory controller. | |
| * This function must be called after EMC_Init and before accessing the external dynamic memory. | |
| * | |
| * param base EMC peripheral base address. | |
| * param timing The timing and latency for dynamica memory controller setting. It shall | |
| * be used for all dynamica memory chips, threfore the worst timing value for all | |
| * used chips must be given. | |
| * param configure The EMC dynamic memory controller chip independent configuration pointer. | |
| * This configuration pointer is actually pointer to a configration array. the array number | |
| * depends on the "totalChips". | |
| * param totalChips The total dynamic memory chip numbers been used or the length of the | |
| * "emc_dynamic_chip_config_t" type memory. | |
| */ | |
| void EMC_DynamicMemInit(EMC_Type *base, | |
| emc_dynamic_timing_config_t *timing, | |
| emc_dynamic_chip_config_t *config, | |
| uint32_t totalChips) | |
| { | |
| assert(NULL != config); | |
| assert(NULL != timing); | |
| assert(totalChips <= EMC_DYNAMIC_MEMDEV_NUM); | |
| uint32_t count; | |
| uint32_t casLatency; | |
| uint32_t addr; | |
| uint32_t offset; | |
| uint32_t data; | |
| emc_dynamic_chip_config_t *dynamicConfig = config; | |
| /* Setting for dynamic memory controller chip independent configuration. */ | |
| for (count = 0; (count < totalChips); count++) | |
| { | |
| if (NULL == dynamicConfig) | |
| { | |
| break; | |
| } | |
| else | |
| { | |
| base->DYNAMIC[dynamicConfig->chipIndex].DYNAMICCONFIG = | |
| EMC_DYNAMIC_DYNAMICCONFIG_MD(dynamicConfig->dynamicDevice) | EMC_ADDRMAP(dynamicConfig->devAddrMap); | |
| /* Abstract CAS latency from the sdram mode reigster setting values. */ | |
| casLatency = ((uint32_t)dynamicConfig->sdramModeReg & EMC_SDRAM_MODE_CL_MASK) >> EMC_SDRAM_MODE_CL_SHIFT; | |
| base->DYNAMIC[dynamicConfig->chipIndex].DYNAMICRASCAS = | |
| EMC_DYNAMIC_DYNAMICRASCAS_RAS(dynamicConfig->rAS_Nclk) | EMC_DYNAMIC_DYNAMICRASCAS_CAS(casLatency); | |
| dynamicConfig++; | |
| } | |
| } | |
| /* Configure the Dynamic Memory controller timing/latency for all chips. */ | |
| base->DYNAMICREADCONFIG = EMC_DYNAMICREADCONFIG_RD(timing->readConfig); | |
| base->DYNAMICRP = EMC_CalculateTimerCycles(base, timing->tRp_Ns, 1) & EMC_DYNAMICRP_TRP_MASK; | |
| base->DYNAMICRAS = EMC_CalculateTimerCycles(base, timing->tRas_Ns, 1) & EMC_DYNAMICRAS_TRAS_MASK; | |
| base->DYNAMICSREX = EMC_CalculateTimerCycles(base, timing->tSrex_Ns, 1) & EMC_DYNAMICSREX_TSREX_MASK; | |
| base->DYNAMICAPR = EMC_CalculateTimerCycles(base, timing->tApr_Ns, 1) & EMC_DYNAMICAPR_TAPR_MASK; | |
| base->DYNAMICDAL = EMC_CalculateTimerCycles(base, timing->tDal_Ns, 0) & EMC_DYNAMICDAL_TDAL_MASK; | |
| base->DYNAMICWR = EMC_CalculateTimerCycles(base, timing->tWr_Ns, 1) & EMC_DYNAMICWR_TWR_MASK; | |
| base->DYNAMICRC = EMC_CalculateTimerCycles(base, timing->tRc_Ns, 1) & EMC_DYNAMICRC_TRC_MASK; | |
| base->DYNAMICRFC = EMC_CalculateTimerCycles(base, timing->tRfc_Ns, 1) & EMC_DYNAMICRFC_TRFC_MASK; | |
| base->DYNAMICXSR = EMC_CalculateTimerCycles(base, timing->tXsr_Ns, 1) & EMC_DYNAMICXSR_TXSR_MASK; | |
| base->DYNAMICRRD = EMC_CalculateTimerCycles(base, timing->tRrd_Ns, 1) & EMC_DYNAMICRRD_TRRD_MASK; | |
| base->DYNAMICMRD = EMC_DYNAMICMRD_TMRD((timing->tMrd_Nclk > 0U) ? timing->tMrd_Nclk - 1UL : 0UL); | |
| SDK_DelayAtLeastUs(EMC_SDRAM_NOP_DELAY_US, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); | |
| /* Step 2. issue nop command. */ | |
| base->DYNAMICCONTROL = 0x00000183; | |
| SDK_DelayAtLeastUs(EMC_SDRAM_PRECHARGE_DELAY_US, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); | |
| /* Step 3. issue precharge all command. */ | |
| base->DYNAMICCONTROL = 0x00000103; | |
| /* Step 4. issue two auto-refresh command. */ | |
| base->DYNAMICREFRESH = 2; | |
| SDK_DelayAtLeastUs(EMC_SDRAM_AUTO_REFRESH_DELAY_US, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); | |
| base->DYNAMICREFRESH = EMC_CalculateTimerCycles(base, timing->refreshPeriod_Nanosec, 0) / EMC_REFRESH_CLOCK_PARAM; | |
| /* Step 5. issue a mode command and set the mode value. */ | |
| base->DYNAMICCONTROL = 0x00000083; | |
| /* Calculate the mode settings here and to reach the 8 auto-refresh time requirement. */ | |
| dynamicConfig = config; | |
| for (count = 0; (count < totalChips); count++) | |
| { | |
| if (NULL == dynamicConfig) | |
| { | |
| break; | |
| } | |
| else | |
| { | |
| /* Get the shift value first. */ | |
| offset = EMC_ModeOffset(dynamicConfig->devAddrMap); | |
| addr = (s_EMCDYCSBases[dynamicConfig->chipIndex] | | |
| ((uint32_t)(dynamicConfig->sdramModeReg & ~EMC_SDRAM_BANKCS_BA_MASK) << offset)); | |
| /* Set the right mode setting value. */ | |
| data = *(volatile uint32_t *)addr; | |
| data = data; | |
| dynamicConfig++; | |
| } | |
| } | |
| if (kEMC_Sdram != config->dynamicDevice) | |
| { | |
| /* Add extended mode register if the low-power sdram is used. */ | |
| base->DYNAMICCONTROL = 0x00000083; | |
| /* Calculate the mode settings for extended mode register. */ | |
| dynamicConfig = config; | |
| for (count = 0; (count < totalChips); count++) | |
| { | |
| if (NULL == dynamicConfig) | |
| { | |
| break; | |
| } | |
| else | |
| { | |
| /* Get the shift value first. */ | |
| offset = EMC_ModeOffset(dynamicConfig->devAddrMap); | |
| addr = (s_EMCDYCSBases[dynamicConfig->chipIndex] | | |
| (((uint32_t)(dynamicConfig->sdramExtModeReg & ~EMC_SDRAM_BANKCS_BA_MASK) | | |
| EMC_SDRAM_BANKCS_BA1_MASK) | |
| << offset)); | |
| /* Set the right mode setting value. */ | |
| data = *(volatile uint32_t *)addr; | |
| data = data; | |
| dynamicConfig++; | |
| } | |
| } | |
| } | |
| /* Step 6. issue normal operation command. */ | |
| base->DYNAMICCONTROL = 0x00000000; /* Issue NORMAL command */ | |
| /* The buffer shall be disabled when do the sdram initialization and | |
| * enabled after the initialization during normal opeation. | |
| */ | |
| dynamicConfig = config; | |
| for (count = 0; (count < totalChips); count++) | |
| { | |
| if (NULL == dynamicConfig) | |
| { | |
| break; | |
| } | |
| else | |
| { | |
| base->DYNAMIC[dynamicConfig->chipIndex].DYNAMICCONFIG |= EMC_DYNAMIC_DYNAMICCONFIG_B_MASK; | |
| dynamicConfig++; | |
| } | |
| } | |
| } | |
| /*! | |
| * brief Initializes the static memory controller. | |
| * This function initializes the static memory controller in external memory controller. | |
| * This function must be called after EMC_Init and before accessing the external static memory. | |
| * | |
| * param base EMC peripheral base address. | |
| * param extWait_Ns 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. | |
| * param configure The EMC static memory controller chip independent configuration pointer. | |
| * This configuration pointer is actually pointer to a configration array. the array number | |
| * depends on the "totalChips". | |
| * param totalChips The total static memory chip numbers been used or the length of the | |
| * "emc_static_chip_config_t" type memory. | |
| */ | |
| void EMC_StaticMemInit(EMC_Type *base, uint32_t *extWait_Ns, emc_static_chip_config_t *config, uint32_t totalChips) | |
| { | |
| assert(NULL != config); | |
| uint32_t count; | |
| emc_static_chip_config_t *staticConfig = config; | |
| /* Initialize extended wait. */ | |
| if (NULL != extWait_Ns) | |
| { | |
| for (count = 0; (count < totalChips) && (staticConfig != NULL); count++) | |
| { | |
| assert(0U != (staticConfig->specailConfig & (uint32_t)kEMC_AsynchronosPageEnable)); | |
| } | |
| base->STATICEXTENDEDWAIT = EMC_CalculateTimerCycles(base, *extWait_Ns, 1); | |
| staticConfig++; | |
| } | |
| /* Initialize the static memory chip specific configure. */ | |
| staticConfig = config; | |
| for (count = 0; (count < totalChips); count++) | |
| { | |
| if (NULL == staticConfig) | |
| { | |
| break; | |
| } | |
| else | |
| { | |
| base->STATIC[staticConfig->chipIndex].STATICCONFIG = | |
| (staticConfig->specailConfig | (uint32_t)staticConfig->memWidth); | |
| base->STATIC[staticConfig->chipIndex].STATICWAITWEN = | |
| EMC_CalculateTimerCycles(base, staticConfig->tWaitWriteEn_Ns, 1); | |
| base->STATIC[staticConfig->chipIndex].STATICWAITOEN = | |
| EMC_CalculateTimerCycles(base, staticConfig->tWaitOutEn_Ns, 0); | |
| base->STATIC[staticConfig->chipIndex].STATICWAITRD = | |
| EMC_CalculateTimerCycles(base, staticConfig->tWaitReadNoPage_Ns, 1); | |
| base->STATIC[staticConfig->chipIndex].STATICWAITPAGE = | |
| EMC_CalculateTimerCycles(base, staticConfig->tWaitReadPage_Ns, 1); | |
| base->STATIC[staticConfig->chipIndex].STATICWAITWR = | |
| EMC_CalculateTimerCycles(base, staticConfig->tWaitWrite_Ns, 2); | |
| base->STATIC[staticConfig->chipIndex].STATICWAITTURN = | |
| EMC_CalculateTimerCycles(base, staticConfig->tWaitTurn_Ns, 1); | |
| staticConfig++; | |
| } | |
| } | |
| } | |
| /*! | |
| * brief Deinitializes the EMC module and gates the clock. | |
| * This function gates the EMC controller clock. As a result, the EMC | |
| * module doesn't work after calling this function. | |
| * | |
| * param base EMC peripheral base address. | |
| */ | |
| void EMC_Deinit(EMC_Type *base) | |
| { | |
| /* Deinit the EMC. */ | |
| base->CONTROL &= ~EMC_CONTROL_E_MASK; | |
| #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) | |
| /* Disable the clock. */ | |
| CLOCK_DisableClock(s_EMCClock[EMC_GetInstance(base)]); | |
| #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ | |
| } |