blob: c9894f96543240dce7c9da9bde4e843298f2f867 [file] [log] [blame]
/****************************************************************************
* arch/arm/src/armv7-m/arm_cache.c
*
* SPDX-License-Identifier: BSD-3-Clause
* SPDX-FileCopyrightText: 2015, 2018-2019 Gregory Nutt. All rights reserved.
* SPDX-FileCopyrightText: 2009 - 2014 ARM LIMITED. All rights reserved.
* SPDX-FileContributor: Gregory Nutt <gnutt@nuttx.org>
* SPDX-FileContributor: Bob Feretich <bob.feretich@rafresearch.com>
*
* 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 ARM, 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 <nuttx/cache.h>
#include <arch/barriers.h>
#include "arm_internal.h"
#include "nvic.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Cache Size ID (CCSIDR) register macros used by inline functions
* Given the value of the CCSIDR reginer (n):
*
* CCSIDR_WAYS - Returns the (number of ways) - 1
* CCSIDR_SETS - Returns the (number of sets) - 1
* CCSIDR_LSSHIFT - Returns log2(cache line size in words) - 2
* Eg. 0 -> 4 words
* 1 -> 8 words
* ...
*/
#define CCSIDR_WAYS(n) \
(((n) & NVIC_CCSIDR_ASSOCIATIVITY_MASK) >> NVIC_CCSIDR_ASSOCIATIVITY_SHIFT)
#define CCSIDR_SETS(n) \
(((n) & NVIC_CCSIDR_NUMSETS_MASK) >> NVIC_CCSIDR_NUMSETS_SHIFT)
#define CCSIDR_LSSHIFT(n) \
(((n) & NVIC_CCSIDR_LINESIZE_MASK) >> NVIC_CCSIDR_LINESIZE_SHIFT)
/****************************************************************************
* Inline Functions
****************************************************************************/
/****************************************************************************
* Name: arm_clz
*
* Description:
* Access to CLZ instructions
*
* Input Parameters:
* value - The value to perform the CLZ operation on
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_DCACHE
static inline uint32_t arm_clz(unsigned int value)
{
uint32_t ret;
__asm__ __volatile__ ("clz %0, %1" : "=r"(ret) : "r"(value));
return ret;
}
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: up_get_cache_linesize
*
* Description:
* Get cache linesize
*
* Input Parameters:
* icache - Difference between icache and dcache.
*
* Returned Value:
* Cache line size
*
****************************************************************************/
#if defined(CONFIG_ARMV7M_ICACHE) || defined(CONFIG_ARMV7M_DCACHE)
static size_t up_get_cache_linesize(bool icache)
{
uint32_t ccsidr;
uint32_t csselr;
uint32_t sshift;
csselr = getreg32(NVIC_CSSELR);
if (icache)
{
putreg32((csselr & ~NVIC_CSSELR_IND) |
NVIC_CSSELR_IND_ICACHE, NVIC_CSSELR);
}
else
{
putreg32((csselr & ~NVIC_CSSELR_IND) |
NVIC_CSSELR_IND_DCACHE, NVIC_CSSELR);
}
ccsidr = getreg32(NVIC_CCSIDR);
sshift = CCSIDR_LSSHIFT(ccsidr) + 4; /* log2(cache-line-size-in-bytes) */
putreg32(csselr, NVIC_CSSELR); /* restore csselr */
return 1 << sshift;
}
/****************************************************************************
* Name: up_get_cache_size
*
* Description:
* Get cache size
*
* Input Parameters:
* level - Difference between icache and dcache.
*
* Returned Value:
* Cache size
*
****************************************************************************/
static size_t up_get_cache_size(bool icache)
{
uint32_t ccsidr;
uint32_t csselr;
uint32_t sshift;
uint32_t sets;
uint32_t ways;
uint32_t line;
csselr = getreg32(NVIC_CSSELR);
if (icache)
{
putreg32((csselr & ~NVIC_CSSELR_IND) |
NVIC_CSSELR_IND_ICACHE, NVIC_CSSELR);
}
else
{
putreg32((csselr & ~NVIC_CSSELR_IND) |
NVIC_CSSELR_IND_DCACHE, NVIC_CSSELR);
}
ccsidr = getreg32(NVIC_CCSIDR);
sets = CCSIDR_SETS(ccsidr) + 1;
ways = CCSIDR_WAYS(ccsidr) + 1;
sshift = CCSIDR_LSSHIFT(ccsidr) + 4; /* log2(cache-line-size-in-bytes) */
line = 1 << sshift;
putreg32(csselr, NVIC_CSSELR); /* restore csselr */
return sets * ways * line;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_get_icache_linesize
*
* Description:
* Get icache linesize
*
* Input Parameters:
* None
*
* Returned Value:
* Cache line size
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_ICACHE
size_t up_get_icache_linesize(void)
{
static uint32_t clsize;
if (clsize == 0)
{
clsize = up_get_cache_linesize(true);
}
return clsize;
}
/****************************************************************************
* Name: up_get_icache_size
*
* Description:
* Get icache size
*
* Input Parameters:
* None
*
* Returned Value:
* Cache size
*
****************************************************************************/
size_t up_get_icache_size(void)
{
static uint32_t csize;
if (csize == 0)
{
csize = up_get_cache_size(true);
}
return csize;
}
#endif
/****************************************************************************
* Name: up_enable_icache
*
* Description:
* Enable the I-Cache
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_ICACHE
void up_enable_icache(void)
{
uint32_t regval;
UP_MB();
/* Invalidate the entire I-Cache */
putreg32(0, NVIC_ICIALLU);
/* Enable the I-Cache */
regval = getreg32(NVIC_CFGCON);
regval |= NVIC_CFGCON_IC;
putreg32(regval, NVIC_CFGCON);
UP_MB();
}
#endif
/****************************************************************************
* Name: up_disable_icache
*
* Description:
* Disable the I-Cache
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_ICACHE
void up_disable_icache(void)
{
uint32_t regval;
UP_MB();
/* Disable the I-Cache */
regval = getreg32(NVIC_CFGCON);
regval &= ~NVIC_CFGCON_IC;
putreg32(regval, NVIC_CFGCON);
/* Invalidate the entire I-Cache */
putreg32(0, NVIC_ICIALLU);
UP_MB();
}
#endif
/****************************************************************************
* Name: up_invalidate_icache
*
* Description:
* Invalidate the instruction cache within the specified region.
*
* Input Parameters:
* start - virtual start address of region
* end - virtual end address of region + 1
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_ICACHE
void up_invalidate_icache(uintptr_t start, uintptr_t end)
{
uint32_t ssize = up_get_icache_linesize();
/* Invalidate the I-Cache containing this range of addresses.
* Round down the start address to the nearest cache line boundary.
*
* sshift = 5 : Offset to the beginning of the set field
* (ssize - 1) = 0x007f : Mask of the set field
*/
UP_DSB();
if ((start & (ssize - 1)) != 0)
{
start &= ~(ssize - 1);
putreg32(start, NVIC_ICIMVAU);
start += ssize;
}
while (start + ssize <= end)
{
/* The below store causes the cache to check its directory and
* determine if this address is contained in the cache. If so, it
* invalidate that cache line. Only the cache way containing the
* address is invalidated. If the address is not in the cache, then
* nothing is invalidated.
*/
putreg32(start, NVIC_ICIMVAU);
/* Increment the address by the size of one cache line. */
start += ssize;
}
if (start < end)
{
putreg32(start, NVIC_ICIMVAU);
}
UP_MB();
}
#endif /* CONFIG_ARMV7M_ICACHE */
/****************************************************************************
* Name: up_invalidate_icache_all
*
* Description:
* Invalidate the entire contents of I cache.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_ICACHE
void up_invalidate_icache_all(void)
{
UP_MB();
/* Invalidate the entire I-Cache */
putreg32(0, NVIC_ICIALLU);
UP_MB();
}
#endif
/****************************************************************************
* Name: up_get_dcache_linesize
*
* Description:
* Get dcache linesize
*
* Input Parameters:
* None
*
* Returned Value:
* Cache line size
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_DCACHE
size_t up_get_dcache_linesize(void)
{
static uint32_t clsize;
if (clsize == 0)
{
clsize = up_get_cache_linesize(false);
}
return clsize;
}
/****************************************************************************
* Name: up_get_dcache_size
*
* Description:
* Get icache size
*
* Input Parameters:
* None
*
* Returned Value:
* Cache size
*
****************************************************************************/
size_t up_get_dcache_size(void)
{
static uint32_t csize;
if (csize == 0)
{
csize = up_get_cache_size(false);
}
return csize;
}
#endif
/****************************************************************************
* Name: up_enable_dcache
*
* Description:
* Enable the D-Cache
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_DCACHE
void up_enable_dcache(void)
{
uint32_t ccsidr;
uint32_t ccr;
uint32_t sshift;
uint32_t wshift;
uint32_t sw;
uint32_t sets;
uint32_t ways;
/* If dcache is already enabled, return. */
ccr = getreg32(NVIC_CFGCON);
if ((ccr & NVIC_CFGCON_DC) != 0)
{
return;
}
/* Get the characteristics of the D-Cache */
ccsidr = getreg32(NVIC_CCSIDR);
sets = CCSIDR_SETS(ccsidr); /* (Number of sets) - 1 */
sshift = CCSIDR_LSSHIFT(ccsidr) + 4; /* log2(cache-line-size-in-bytes) */
ways = CCSIDR_WAYS(ccsidr); /* (Number of ways) - 1 */
/* Calculate the bit offset for the way field in the DCISW register by
* counting the number of leading zeroes. For example:
*
* Number of Value of ways Field
* Ways 'ways' Offset
* 2 1 31
* 4 3 30
* 8 7 29
* ...
*/
wshift = arm_clz(ways) & 0x1f;
/* Invalidate the entire D-Cache */
UP_DSB();
do
{
int32_t tmpways = ways;
do
{
sw = ((tmpways << wshift) | (sets << sshift));
putreg32(sw, NVIC_DCISW);
}
while (tmpways--);
}
while (sets--);
UP_DSB();
#ifdef CONFIG_ARMV7M_DCACHE_WRITETHROUGH
ccr = getreg32(NVIC_CACR);
ccr |= NVIC_CACR_FORCEWT;
putreg32(ccr, NVIC_CACR);
#endif
/* Enable the D-Cache */
ccr = getreg32(NVIC_CFGCON);
ccr |= NVIC_CFGCON_DC;
putreg32(ccr, NVIC_CFGCON);
UP_MB();
}
#endif /* CONFIG_ARMV7M_DCACHE */
/****************************************************************************
* Name: up_disable_dcache
*
* Description:
* Disable the D-Cache
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_DCACHE
void up_disable_dcache(void)
{
uint32_t ccsidr;
uint32_t ccr;
uint32_t sshift;
uint32_t wshift;
uint32_t sw;
uint32_t sets;
uint32_t ways;
/* Get the characteristics of the D-Cache */
ccsidr = getreg32(NVIC_CCSIDR);
sets = CCSIDR_SETS(ccsidr); /* (Number of sets) - 1 */
sshift = CCSIDR_LSSHIFT(ccsidr) + 4; /* log2(cache-line-size-in-bytes) */
ways = CCSIDR_WAYS(ccsidr); /* (Number of ways) - 1 */
/* Calculate the bit offset for the way field in the DCCISW register by
* counting the number of leading zeroes. For example:
*
* Number of Value of ways Field
* Ways 'ways' Offset
* 2 1 31
* 4 3 30
* 8 7 29
* ...
*/
wshift = arm_clz(ways) & 0x1f;
UP_DSB();
/* Disable the D-Cache */
ccr = getreg32(NVIC_CFGCON);
ccr &= ~NVIC_CFGCON_DC;
putreg32(ccr, NVIC_CFGCON);
/* Clean and invalidate the entire D-Cache */
do
{
int32_t tmpways = ways;
do
{
sw = ((tmpways << wshift) | (sets << sshift));
putreg32(sw, NVIC_DCCISW);
}
while (tmpways--);
}
while (sets--);
UP_MB();
}
#endif /* CONFIG_ARMV7M_DCACHE */
/****************************************************************************
* Name: up_invalidate_dcache
*
* Description:
* Invalidate the data cache within the specified region; we will be
* performing a DMA operation in this region and we want to purge old data
* in the cache. Note that this function invalidates all cache ways
* in sets that could be associated with the address range, regardless of
* whether the address range is contained in the cache or not.
*
* Input Parameters:
* start - virtual start address of region
* end - virtual end address of region + 1
*
* Returned Value:
* None
*
* Assumptions:
* This operation is not atomic. This function assumes that the caller
* has exclusive access to the address range so that no harm is done if
* the operation is preempted.
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_DCACHE
void up_invalidate_dcache(uintptr_t start, uintptr_t end)
{
uint32_t ssize = up_get_dcache_linesize();
/* Invalidate the D-Cache containing this range of addresses
* Round down the start address to the nearest cache line boundary.
*
* sshift = 5 : Offset to the beginning of the set field
* (ssize - 1) = 0x007f : Mask of the set field
*/
UP_DSB();
if ((start & (ssize - 1)) != 0)
{
start &= ~(ssize - 1);
putreg32(start, NVIC_DCCIMVAC);
start += ssize;
}
while (start + ssize <= end)
{
/* The below store causes the cache to check its directory and
* determine if this address is contained in the cache. If so, it
* invalidate that cache line. Only the cache way containing the
* address is invalidated. If the address is not in the cache, then
* nothing is invalidated.
*/
putreg32(start, NVIC_DCIMVAC);
/* Increment the address by the size of one cache line. */
start += ssize;
}
if (start < end)
{
putreg32(start, NVIC_DCCIMVAC);
}
UP_MB();
}
#endif /* CONFIG_ARMV7M_DCACHE */
/****************************************************************************
* Name: up_invalidate_dcache_all
*
* Description:
* Invalidate the entire contents of D cache.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_DCACHE
void up_invalidate_dcache_all(void)
{
uint32_t ccsidr;
uint32_t sshift;
uint32_t wshift;
uint32_t sw;
uint32_t sets;
uint32_t ways;
/* Get the characteristics of the D-Cache */
ccsidr = getreg32(NVIC_CCSIDR);
sets = CCSIDR_SETS(ccsidr); /* (Number of sets) - 1 */
sshift = CCSIDR_LSSHIFT(ccsidr) + 4; /* log2(cache-line-size-in-bytes) */
ways = CCSIDR_WAYS(ccsidr); /* (Number of ways) - 1 */
/* Calculate the bit offset for the way field in the DCISW register by
* counting the number of leading zeroes. For example:
*
* Number of Value of ways Field
* Ways 'ways' Offset
* 2 1 31
* 4 3 30
* 8 7 29
* ...
*/
wshift = arm_clz(ways) & 0x1f;
UP_DSB();
/* Invalidate the entire D-Cache */
do
{
int32_t tmpways = ways;
do
{
sw = ((tmpways << wshift) | (sets << sshift));
putreg32(sw, NVIC_DCISW);
}
while (tmpways--);
}
while (sets--);
UP_MB();
}
#endif /* CONFIG_ARMV7M_DCACHE */
/****************************************************************************
* Name: up_clean_dcache
*
* Description:
* Clean the data cache within the specified region by flushing the
* contents of the data cache to memory.
*
* NOTE: This operation is un-necessary if the DCACHE is configured in
* write-through mode.
*
* Input Parameters:
* start - virtual start address of region
* end - virtual end address of region + 1
*
* Returned Value:
* None
*
* Assumptions:
* This operation is not atomic. This function assumes that the caller
* has exclusive access to the address range so that no harm is done if
* the operation is preempted.
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_DCACHE
void up_clean_dcache(uintptr_t start, uintptr_t end)
{
#ifndef CONFIG_ARMV7M_DCACHE_WRITETHROUGH
uint32_t ccsidr;
uint32_t sshift;
uint32_t ssize;
uint32_t sets;
uint32_t ways;
/* Get the characteristics of the D-Cache */
ccsidr = getreg32(NVIC_CCSIDR);
sshift = CCSIDR_LSSHIFT(ccsidr) + 4; /* log2(cache-line-size-in-bytes) */
sets = CCSIDR_SETS(ccsidr); /* (Number of sets) - 1 */
ways = CCSIDR_WAYS(ccsidr); /* (Number of ways) - 1 */
/* Clean the D-Cache over the range of addresses */
ssize = (1 << sshift);
#ifndef CONFIG_SMP
if ((end - start) >= ssize * (sets + 1) * (ways + 1))
{
return up_clean_dcache_all();
}
#endif
start &= ~(ssize - 1);
UP_DSB();
do
{
/* The below store causes the cache to check its directory and
* determine if this address is contained in the cache. If so, it
* clean that cache line. Only the cache way containing the
* address is invalidated. If the address is not in the cache, then
* nothing is invalidated.
*/
putreg32(start, NVIC_DCCMVAC);
/* Increment the address by the size of one cache line. */
start += ssize;
}
while (start < end);
#endif /* !CONFIG_ARMV7M_DCACHE_WRITETHROUGH */
UP_MB();
}
#endif /* CONFIG_ARMV7M_DCACHE */
/****************************************************************************
* Name: up_clean_dcache_all
*
* Description:
* Clean the entire data cache within the specified region by flushing the
* contents of the data cache to memory.
*
* NOTE: This operation is un-necessary if the DCACHE is configured in
* write-through mode.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
* Assumptions:
* This operation is not atomic. This function assumes that the caller
* has exclusive access to the address range so that no harm is done if
* the operation is preempted.
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_DCACHE
void up_clean_dcache_all(void)
{
#ifndef CONFIG_ARMV7M_DCACHE_WRITETHROUGH
uint32_t ccsidr;
uint32_t sshift;
uint32_t wshift;
uint32_t sw;
uint32_t sets;
uint32_t ways;
/* Get the characteristics of the D-Cache */
ccsidr = getreg32(NVIC_CCSIDR);
sets = CCSIDR_SETS(ccsidr); /* (Number of sets) - 1 */
sshift = CCSIDR_LSSHIFT(ccsidr) + 4; /* log2(cache-line-size-in-bytes) */
ways = CCSIDR_WAYS(ccsidr); /* (Number of ways) - 1 */
/* Calculate the bit offset for the way field in the DCCSW register by
* counting the number of leading zeroes. For example:
*
* Number of Value of ways Field
* Ways 'ways' Offset
* 2 1 31
* 4 3 30
* 8 7 29
* ...
*/
wshift = arm_clz(ways) & 0x1f;
UP_DSB();
/* Clean the entire D-Cache */
do
{
int32_t tmpways = ways;
do
{
sw = ((tmpways << wshift) | (sets << sshift));
putreg32(sw, NVIC_DCCSW);
}
while (tmpways--);
}
while (sets--);
#endif /* !CONFIG_ARMV7M_DCACHE_WRITETHROUGH */
UP_MB();
}
#endif /* CONFIG_ARMV7M_DCACHE */
/****************************************************************************
* Name: up_flush_dcache
*
* Description:
* Flush the data cache within the specified region by cleaning and
* invalidating the D cache.
*
* NOTE: If DCACHE write-through is configured, then this operation is the
* same as up_invalidate_cache().
*
* Input Parameters:
* start - virtual start address of region
* end - virtual end address of region + 1
*
* Returned Value:
* None
*
* Assumptions:
* This operation is not atomic. This function assumes that the caller
* has exclusive access to the address range so that no harm is done if
* the operation is preempted.
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_DCACHE
void up_flush_dcache(uintptr_t start, uintptr_t end)
{
#ifndef CONFIG_ARMV7M_DCACHE_WRITETHROUGH
uint32_t ccsidr;
uint32_t sshift;
uint32_t ssize;
uint32_t sets;
uint32_t ways;
/* Get the characteristics of the D-Cache */
ccsidr = getreg32(NVIC_CCSIDR);
sshift = CCSIDR_LSSHIFT(ccsidr) + 4; /* log2(cache-line-size-in-bytes) */
sets = CCSIDR_SETS(ccsidr); /* (Number of sets) - 1 */
ways = CCSIDR_WAYS(ccsidr); /* (Number of ways) - 1 */
/* Clean and invalidate the D-Cache over the range of addresses */
ssize = (1 << sshift);
#ifndef CONFIG_SMP
if ((end - start) >= ssize * (sets + 1) * (ways + 1))
{
return up_flush_dcache_all();
}
#endif
start &= ~(ssize - 1);
UP_DSB();
do
{
/* The below store causes the cache to check its directory and
* determine if this address is contained in the cache. If so, it clean
* and invalidate that cache line. Only the cache way containing the
* address is invalidated. If the address is not in the cache, then
* nothing is invalidated.
*/
putreg32(start, NVIC_DCCIMVAC);
/* Increment the address by the size of one cache line. */
start += ssize;
}
while (start < end);
UP_MB();
#else
up_invalidate_dcache(start, end);
#endif /* !CONFIG_ARMV7M_DCACHE_WRITETHROUGH */
}
#endif /* CONFIG_ARMV7M_DCACHE */
/****************************************************************************
* Name: up_flush_dcache_all
*
* Description:
* Flush the entire data cache by cleaning and invalidating the D cache.
*
* NOTE: If DCACHE write-through is configured, then this operation is the
* same as up_invalidate_cache_all().
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
* Assumptions:
* This operation is not atomic. This function assumes that the caller
* has exclusive access to the address range so that no harm is done if
* the operation is preempted.
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_DCACHE
void up_flush_dcache_all(void)
{
#ifndef CONFIG_ARMV7M_DCACHE_WRITETHROUGH
uint32_t ccsidr;
uint32_t sshift;
uint32_t wshift;
uint32_t sw;
uint32_t sets;
uint32_t ways;
/* Get the characteristics of the D-Cache */
ccsidr = getreg32(NVIC_CCSIDR);
sets = CCSIDR_SETS(ccsidr); /* (Number of sets) - 1 */
sshift = CCSIDR_LSSHIFT(ccsidr) + 4; /* log2(cache-line-size-in-bytes) */
ways = CCSIDR_WAYS(ccsidr); /* (Number of ways) - 1 */
/* Calculate the bit offset for the way field in the DCCISW register by
* counting the number of leading zeroes. For example:
*
* Number of Value of ways Field
* Ways 'ways' Offset
* 2 1 31
* 4 3 30
* 8 7 29
* ...
*/
wshift = arm_clz(ways) & 0x1f;
UP_DSB();
/* Clean and invalidate the entire D-Cache */
do
{
int32_t tmpways = ways;
do
{
sw = ((tmpways << wshift) | (sets << sshift));
putreg32(sw, NVIC_DCCISW);
}
while (tmpways--);
}
while (sets--);
UP_MB();
#else
up_invalidate_dcache_all();
#endif /* !CONFIG_ARMV7M_DCACHE_WRITETHROUGH */
}
#endif /* CONFIG_ARMV7M_DCACHE */
/****************************************************************************
* Name: up_coherent_dcache
*
* Description:
* Ensure that the I and D caches are coherent within specified region
* by cleaning the D cache (i.e., flushing the D cache contents to memory
* and invalidating the I cache. This is typically used when code has been
* written to a memory region, and will be executed.
*
* Input Parameters:
* addr - virtual start address of region
* len - Size of the address region in bytes
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_ICACHE
void up_coherent_dcache(uintptr_t addr, size_t len)
{
uintptr_t end;
if (len > 0)
{
/* Flush any dirtcy D-Cache lines to memory */
end = addr + len;
up_clean_dcache(addr, end);
UNUSED(end);
/* Invalidate the entire I-Cache */
up_invalidate_icache_all();
}
}
#endif