481 lines
17 KiB
C
Raw Permalink Normal View History

2022-10-23 23:45:43 -07:00
//*****************************************************************************
//
// 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 <stdint.h>
#include <stdbool.h>
#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.
//! @}
//
//*****************************************************************************