//***************************************************************************** // // am_hal_cachectrl.c //! @file //! //! @brief Functions for interfacing with the CACHE controller. //! //! @addtogroup clkgen3p Clock Generator (CACHE) //! @ingroup apollo3phal //! @{ // //***************************************************************************** //***************************************************************************** // // Copyright (c) 2020, Ambiq Micro // All rights reserved. // // 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 of the copyright holder nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // Third party software included in this distribution is subject to the // additional license terms as defined in the /docs/licenses directory. // // 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 HOLDER 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. // // This is part of revision 2.4.2 of the AmbiqSuite Development Package. // //***************************************************************************** #include #include #include "am_mcu_apollo.h" //***************************************************************************** // // Default settings for the cache. // //***************************************************************************** const am_hal_cachectrl_config_t am_hal_cachectrl_defaults = { .bLRU = 0, .eDescript = AM_HAL_CACHECTRL_DESCR_1WAY_128B_1024E, .eMode = AM_HAL_CACHECTRL_CONFIG_MODE_INSTR_DATA, }; //***************************************************************************** // // Configure the cache with given and recommended settings, but do not enable. // //***************************************************************************** uint32_t am_hal_cachectrl_config(const am_hal_cachectrl_config_t *psConfig) { // // In the case where cache is currently enabled, we need to gracefully // bow out of that configuration before reconfiguring. The best way to // accomplish that is to shut down the ID bits, leaving the cache enabled. // Once the instr and data caches have been disabled, we can safely set // any new configuration, including disabling the controller. // AM_CRITICAL_BEGIN CACHECTRL->CACHECFG &= ~(CACHECTRL_CACHECFG_DCACHE_ENABLE_Msk | CACHECTRL_CACHECFG_ICACHE_ENABLE_Msk); AM_CRITICAL_END CACHECTRL->CACHECFG = _VAL2FLD(CACHECTRL_CACHECFG_ENABLE, 0) | _VAL2FLD(CACHECTRL_CACHECFG_CACHE_CLKGATE, 1) | _VAL2FLD(CACHECTRL_CACHECFG_CACHE_LS, 0) | _VAL2FLD(CACHECTRL_CACHECFG_DATA_CLKGATE, 1) | _VAL2FLD(CACHECTRL_CACHECFG_ENABLE_MONITOR, 0) | _VAL2FLD(CACHECTRL_CACHECFG_LRU, psConfig->bLRU) | _VAL2FLD(CACHECTRL_CACHECFG_CONFIG, psConfig->eDescript) | ((psConfig->eMode << CACHECTRL_CACHECFG_ICACHE_ENABLE_Pos) & (CACHECTRL_CACHECFG_DCACHE_ENABLE_Msk | CACHECTRL_CACHECFG_ICACHE_ENABLE_Msk)); return AM_HAL_STATUS_SUCCESS; } // am_hal_cachectrl_config() //***************************************************************************** // // Enable the cache. // //***************************************************************************** uint32_t am_hal_cachectrl_enable(void) { // // Enable the cache // CACHECTRL->CACHECFG |= _VAL2FLD(CACHECTRL_CACHECFG_ENABLE, 1); return AM_HAL_STATUS_SUCCESS; } // am_hal_cachectrl_enable() //***************************************************************************** // // Disable the cache. // //***************************************************************************** uint32_t am_hal_cachectrl_disable(void) { // // Shut down as gracefully as possible. // Disable the I/D cache enable bits first to allow a little time // for any in-flight transactions to hand off to the line buffer. // Then clear the enable. // AM_CRITICAL_BEGIN CACHECTRL->CACHECFG &= ~(_VAL2FLD(CACHECTRL_CACHECFG_ICACHE_ENABLE, 1) | _VAL2FLD(CACHECTRL_CACHECFG_DCACHE_ENABLE, 1)); CACHECTRL->CACHECFG &= ~_VAL2FLD(CACHECTRL_CACHECFG_ENABLE, 1); AM_CRITICAL_END return AM_HAL_STATUS_SUCCESS; } // am_hal_cachectrl_disable() //***************************************************************************** // // Control helper functions. // //***************************************************************************** static bool set_LPMMODE(uint32_t ui32value) { uint32_t ui32Val, ui32inst; uint32_t *pui32RegAddr; if ( ui32value > (CACHECTRL_FLASH0CFG_LPMMODE0_Msk >> CACHECTRL_FLASH0CFG_LPMMODE0_Pos) ) { return false; } // // We need to set all 4 instances. // for ( ui32inst = 0; ui32inst < 4; ui32inst++ ) { // // Compute register address (assumes each reg is 1 word offset). // pui32RegAddr = (uint32_t*)&CACHECTRL->FLASH0CFG; pui32RegAddr += ui32inst; AM_CRITICAL_BEGIN ui32Val = am_hal_flash_load_ui32(pui32RegAddr); ui32Val &= ~(CACHECTRL_FLASH0CFG_LPMMODE0_Msk | CACHECTRL_FLASH0CFG_LPMRDWAIT0_Msk); ui32Val |= _VAL2FLD(CACHECTRL_FLASH0CFG_LPMMODE0, ui32value) | _VAL2FLD(CACHECTRL_FLASH0CFG_LPMRDWAIT0, 0x7); am_hal_flash_store_ui32(pui32RegAddr, ui32Val); AM_CRITICAL_END } return true; } // set_LPMMODE() static bool set_SEDELAY(uint32_t ui32value) { uint32_t ui32Val, ui32inst; uint32_t *pui32RegAddr; if ( ui32value > (CACHECTRL_FLASH0CFG_SEDELAY0_Msk >> CACHECTRL_FLASH0CFG_SEDELAY0_Pos) ) { return false; } // // We need to set all 4 instances. // for ( ui32inst = 0; ui32inst < 4; ui32inst++ ) { // // Compute register address (assumes each reg is 1 word offset). // pui32RegAddr = (uint32_t*)&CACHECTRL->FLASH0CFG; pui32RegAddr += ui32inst; AM_CRITICAL_BEGIN ui32Val = am_hal_flash_load_ui32(pui32RegAddr); ui32Val &= ~(CACHECTRL_FLASH0CFG_SEDELAY0_Msk | CACHECTRL_FLASH0CFG_LPMRDWAIT0_Msk); ui32Val |= _VAL2FLD(CACHECTRL_FLASH0CFG_SEDELAY0, ui32value) | _VAL2FLD(CACHECTRL_FLASH0CFG_LPMRDWAIT0, 0x7); am_hal_flash_store_ui32(pui32RegAddr, ui32Val); AM_CRITICAL_END } return true; } // set_SEDELAY() static bool set_RDWAIT(uint32_t ui32value) { uint32_t ui32Val, ui32inst; uint32_t *pui32RegAddr; if ( ui32value > (CACHECTRL_FLASH0CFG_RDWAIT0_Msk >> CACHECTRL_FLASH0CFG_RDWAIT0_Pos) ) { return false; } // // We need to set all 4 instances. // for ( ui32inst = 0; ui32inst < 4; ui32inst++ ) { // // Compute register address (assumes each reg is 1 word offset). // pui32RegAddr = (uint32_t*)&CACHECTRL->FLASH0CFG; pui32RegAddr += ui32inst; AM_CRITICAL_BEGIN ui32Val = am_hal_flash_load_ui32(pui32RegAddr); ui32Val &= ~(CACHECTRL_FLASH0CFG_RDWAIT0_Msk | CACHECTRL_FLASH0CFG_LPMRDWAIT0_Msk); ui32Val |= _VAL2FLD(CACHECTRL_FLASH0CFG_RDWAIT0, ui32value) | _VAL2FLD(CACHECTRL_FLASH0CFG_LPMRDWAIT0, 0x7); am_hal_flash_store_ui32(pui32RegAddr, ui32Val); AM_CRITICAL_END } return true; } // set_RDWAIT() //***************************************************************************** // // Select the cache configuration type. // //***************************************************************************** uint32_t am_hal_cachectrl_control(am_hal_cachectrl_control_e eControl, void *pArgs) { uint32_t ui32Arg; uint32_t ui32SetMask = 0; switch ( eControl ) { case AM_HAL_CACHECTRL_CONTROL_FLASH_CACHE_INVALIDATE: ui32SetMask = CACHECTRL_CTRL_INVALIDATE_Msk; break; case AM_HAL_CACHECTRL_CONTROL_STATISTICS_RESET: if ( !_FLD2VAL(CACHECTRL_CACHECFG_ENABLE_MONITOR, CACHECTRL->CACHECFG) ) { // // The monitor must be enabled for the reset to have any affect. // return AM_HAL_STATUS_INVALID_OPERATION; } else { ui32SetMask = CACHECTRL_CTRL_RESET_STAT_Msk; } break; case AM_HAL_CACHECTRL_CONTROL_FLASH_ALL_SLEEP_ENABLE: ui32SetMask = CACHECTRL_CTRL_FLASH0_SLM_ENABLE_Msk | CACHECTRL_CTRL_FLASH1_SLM_ENABLE_Msk; break; case AM_HAL_CACHECTRL_CONTROL_FLASH_ALL_SLEEP_DISABLE: ui32SetMask = CACHECTRL_CTRL_FLASH0_SLM_DISABLE_Msk | CACHECTRL_CTRL_FLASH1_SLM_DISABLE_Msk; break; case AM_HAL_CACHECTRL_CONTROL_FLASH0_SLEEP_ENABLE: ui32SetMask = CACHECTRL_CTRL_FLASH0_SLM_ENABLE_Msk; break; case AM_HAL_CACHECTRL_CONTROL_FLASH0_SLEEP_DISABLE: ui32SetMask = CACHECTRL_CTRL_FLASH0_SLM_DISABLE_Msk; break; case AM_HAL_CACHECTRL_CONTROL_FLASH1_SLEEP_ENABLE: ui32SetMask = CACHECTRL_CTRL_FLASH1_SLM_ENABLE_Msk; break; case AM_HAL_CACHECTRL_CONTROL_FLASH1_SLEEP_DISABLE: ui32SetMask = CACHECTRL_CTRL_FLASH1_SLM_DISABLE_Msk; break; case AM_HAL_CACHECTRL_CONTROL_MONITOR_ENABLE: ui32SetMask = 0; AM_CRITICAL_BEGIN CACHECTRL->CACHECFG |= CACHECTRL_CACHECFG_ENABLE_MONITOR_Msk; AM_CRITICAL_END break; case AM_HAL_CACHECTRL_CONTROL_MONITOR_DISABLE: ui32SetMask = 0; AM_CRITICAL_BEGIN CACHECTRL->CACHECFG &= ~CACHECTRL_CACHECFG_ENABLE_MONITOR_Msk; AM_CRITICAL_END break; case AM_HAL_CACHECTRL_CONTROL_LPMMODE_RESET: // // Safely set the reset values for LPMMODE, SEDELAY, and RDWAIT. // if ( !set_LPMMODE(AM_HAL_CACHECTRL_FLASHCFG_LPMMODE_NEVER) || !set_SEDELAY(0x7) || !set_RDWAIT(0x3) ) { return AM_HAL_STATUS_FAIL; } break; case AM_HAL_CACHECTRL_CONTROL_LPMMODE_RECOMMENDED: // // Safely set the as recommended values (from the datasheet) // for LPMMODE, SEDELAY, and RDWAIT. // if ( !set_LPMMODE(AM_HAL_CACHECTRL_FLASHCFG_LPMMODE_STANDBY) || !set_SEDELAY(0x5) || !set_RDWAIT(0x1) ) { return AM_HAL_STATUS_FAIL; } break; case AM_HAL_CACHECTRL_CONTROL_LPMMODE_AGGRESSIVE: // // Safely set aggressive values for LPMMODE, SEDELAY, and RDWAIT. // (For now select recommended values.) // if ( !set_LPMMODE(AM_HAL_CACHECTRL_FLASHCFG_LPMMODE_STANDBY) || !set_SEDELAY(0x6) || !set_RDWAIT(0x1) ) { return AM_HAL_STATUS_FAIL; } break; case AM_HAL_CACHECTRL_CONTROL_LPMMODE_SET: // // Safely set LPMMODE, SEDELAY, or RDWAIT. // The new value is passed by reference via pArgs. That is, pArgs is // assumed to be a pointer to a uint32_t of the new value. // if ( !pArgs ) { return AM_HAL_STATUS_INVALID_ARG; } ui32Arg = *(uint32_t*)pArgs; if ( !set_LPMMODE(ui32Arg) ) { return AM_HAL_STATUS_FAIL; } break; case AM_HAL_CACHECTRL_CONTROL_SEDELAY_SET: if ( !pArgs ) { return AM_HAL_STATUS_INVALID_ARG; } ui32Arg = *(uint32_t*)pArgs; if ( !set_SEDELAY(ui32Arg) ) { return AM_HAL_STATUS_FAIL; } break; case AM_HAL_CACHECTRL_CONTROL_RDWAIT_SET: if ( !pArgs ) { return AM_HAL_STATUS_INVALID_ARG; } ui32Arg = *(uint32_t*)pArgs; if ( !set_RDWAIT(ui32Arg) ) { return AM_HAL_STATUS_FAIL; } break; case AM_HAL_CACHECTRL_CONTROL_NC_CFG: { if ( pArgs == NULL ) { return AM_HAL_STATUS_INVALID_ARG; } am_hal_cachectrl_nc_cfg_t *pNcCfg; pNcCfg = (am_hal_cachectrl_nc_cfg_t *)pArgs; #ifndef AM_HAL_DISABLE_API_VALIDATION // Make sure the addresses are valid if ((pNcCfg->ui32StartAddr & ~CACHECTRL_NCR0START_ADDR_Msk) || (pNcCfg->ui32EndAddr & ~CACHECTRL_NCR0START_ADDR_Msk)) { return AM_HAL_STATUS_INVALID_ARG; } #endif // AM_HAL_DISABLE_API_VALIDATION if (pNcCfg->eNCRegion == AM_HAL_CACHECTRL_NCR0) { CACHECTRL->NCR0START = pNcCfg->ui32StartAddr; CACHECTRL->NCR0END = pNcCfg->ui32EndAddr; CACHECTRL->CACHECFG_b.ENABLE_NC0 = pNcCfg->bEnable; } else if (pNcCfg->eNCRegion == AM_HAL_CACHECTRL_NCR1) { CACHECTRL->NCR1START = pNcCfg->ui32StartAddr; CACHECTRL->NCR1END = pNcCfg->ui32EndAddr; CACHECTRL->CACHECFG_b.ENABLE_NC1 = pNcCfg->bEnable; } #ifndef AM_HAL_DISABLE_API_VALIDATION else { return AM_HAL_STATUS_INVALID_ARG; } #endif // AM_HAL_DISABLE_API_VALIDATION return AM_HAL_STATUS_SUCCESS; } default: return AM_HAL_STATUS_INVALID_ARG; } // // All fields in the CACHECTRL register are write-only or read-only. // A write to CACHECTRL acts as a mask-set. That is, only the bits // written as '1' have an effect, any bits written as '0' are unaffected. // // Important note - setting of an enable and disable simultanously has // unpredicable results. // if ( ui32SetMask ) { CACHECTRL->CTRL = ui32SetMask; } return AM_HAL_STATUS_SUCCESS; } // am_hal_cachectrl_control() //***************************************************************************** // // Cache controller status function // //***************************************************************************** uint32_t am_hal_cachectrl_status_get(am_hal_cachectrl_status_t *psStatus) { uint32_t ui32Status; if ( psStatus == NULL ) { return AM_HAL_STATUS_INVALID_ARG; } ui32Status = CACHECTRL->CTRL; psStatus->bFlash0SleepMode = _FLD2VAL(CACHECTRL_CTRL_FLASH0_SLM_STATUS, ui32Status); psStatus->bFlash1SleepMode = _FLD2VAL(CACHECTRL_CTRL_FLASH1_SLM_STATUS, ui32Status); psStatus->bCacheReady = _FLD2VAL(CACHECTRL_CTRL_CACHE_READY, ui32Status); return AM_HAL_STATUS_SUCCESS; } // am_hal_cachectrl_status_get() //***************************************************************************** // // End Doxygen group. //! @} // //*****************************************************************************