blob: cf25e684bd9aeed3b3374fb49cdbae337fb8bf6a [file] [log] [blame]
/* ------------------------------------------
* Copyright (c) 2016, Synopsys, Inc. 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 Synopsys, Inc., 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 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.
*
* \version 2016.05
* \date 2014-07-15
* \author Wayne Ren(Wei.Ren@synopsys.com)
--------------------------------------------- */
/**
* \file
* \ingroup ARC_HAL_MISC_CACHE
* \brief implementation of cache related functions
*/
#include "inc/arc/arc_cache.h"
#define DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y))
struct cache_config
{
uint8_t ver; /* version */
uint8_t assoc; /* Cache Associativity */
uint16_t line; /* cache line/block size */
uint32_t capacity; /* capacity */
};
static struct cache_config icache_config, dcache_config;
/**
* \brief invalidate multi instruction cache lines
*
* \param[in] start_addr start address in instruction cache
* \param[in] size the bytes to be invalidated
* \return 0, succeeded, -1, failed
*/
int32_t icache_invalidate_mlines(uint32_t start_addr, uint32_t size)
{
if (!icache_available()) { return -1; }
if ((size == 0) || (size > icache_config.capacity))
{
return -1;
}
uint32_t end_addr;
uint32_t line_size;
uint32_t status;
line_size = (uint32_t)(icache_config.line);
end_addr = start_addr + size - 1;
start_addr &= (uint32_t)(~(line_size - 1));
status = cpu_lock_save();
do
{
_arc_aux_write(AUX_IC_IVIL, start_addr);
Asm("nop_s");
Asm("nop_s");
Asm("nop_s");
start_addr += line_size;
}
while (start_addr <= end_addr);
cpu_unlock_restore(status);
return 0;
}
/**
* \brief lock multi lines in instruction cache
*
* \param[in] start_addr start address in instruction cache
* \param[in] size the bytes to be locked
* \return 0, succeeded, -1, failed (cache already locked or other reasons)
*/
int32_t icache_lock_mlines(uint32_t start_addr, uint32_t size)
{
if (!icache_available()) { return -1; }
if ((size == 0) || (size > icache_config.capacity))
{
return -1;
}
uint32_t end_addr;
uint32_t line_size;
uint32_t status;
int32_t ercd = 0;
line_size = (uint32_t)(icache_config.line);
end_addr = start_addr + size - 1;
start_addr &= (uint32_t)(~(line_size - 1));
status = cpu_lock_save();
do
{
_arc_aux_write(AUX_IC_LIL, start_addr);
if (_arc_aux_read(AUX_IC_CTRL) & IC_CTRL_OP_SUCCEEDED)
{
start_addr += line_size;
}
else
{
ercd = -1; /* the operation failed */
break;
}
}
while (start_addr <= end_addr);
cpu_unlock_restore(status);
return ercd;
}
/**
* \brief directly write icache internal ram
*
* \param[in] cache_addr, icache internal address(way+index+offset)
* \param[in] tag cache tag to write (tag+lock bit+valid bit)
* \param[in] data cache data to write
* \return 0, succeeded, -1, failed
*/
int32_t icache_direct_write(uint32_t cache_addr, uint32_t tag, uint32_t data)
{
if (!icache_available()) { return -1; }
if (_arc_aux_read(AUX_IC_CTRL) & IC_CTRL_INDIRECT_ACCESS)
{
return -1;
}
_arc_aux_write(AUX_IC_RAM_ADDR, cache_addr);
_arc_aux_write(AUX_IC_TAG, tag);
_arc_aux_write(AUX_IC_DATA, data);
return 0;
}
/**
* \brief directly read icache internal ram
*
* \param[in] cache_addr, icache internal address(way+index+offset)
* \param[out] tag cache tag to read (tag+index+lock bit+valid bit)
* \param[out] data cache data to read
* \return 0, succeeded, -1, failed
*/
int32_t icache_direct_read(uint32_t cache_addr, uint32_t *tag, uint32_t *data)
{
if (!icache_available()) { return -1; }
if (_arc_aux_read(AUX_IC_CTRL) & IC_CTRL_INDIRECT_ACCESS)
{
return -1;
}
_arc_aux_write(AUX_IC_RAM_ADDR, cache_addr);
*tag = _arc_aux_read(AUX_IC_TAG);
*data = _arc_aux_read(AUX_IC_DATA);
return 0;
}
/**
* \brief indirectly read icache internal ram
*
* \param[in] mem_addr, memory address
* \param[out] tag cache tag to read
* \param[out] data cache data to read
* \return 0, succeeded, -1, failed
*/
int32_t icache_indirect_read(uint32_t mem_addr, uint32_t *tag, uint32_t *data)
{
if (!icache_available()) { return -1; }
if (!(_arc_aux_read(AUX_IC_CTRL) & IC_CTRL_INDIRECT_ACCESS))
{
return -1;
}
_arc_aux_write(AUX_IC_RAM_ADDR, mem_addr);
if (_arc_aux_read(AUX_IC_CTRL) & IC_CTRL_OP_SUCCEEDED)
{
*tag = _arc_aux_read(AUX_IC_TAG);
*data = _arc_aux_read(AUX_IC_DATA);
}
else
{
return -1; /* the specified memory is not in icache */
}
return 0;
}
/**
* \brief invalidate multi data cache lines
*
* \param[in] start_addr start address in data cache
* \param[in] size the bytes to be invalidated
* \return 0, succeeded, -1, failed
*/
int32_t dcache_invalidate_mlines(uint32_t start_addr, uint32_t size)
{
if (!dcache_available()) { return -1; }
uint32_t end_addr;
uint32_t line_size;
uint32_t status;
if ((size == 0) || (size > dcache_config.capacity))
{
return -1;
}
line_size = (uint32_t)(dcache_config.line);
end_addr = start_addr + size - 1;
start_addr &= (uint32_t)(~(line_size - 1));
status = cpu_lock_save();
do
{
_arc_aux_write(AUX_DC_IVDL, start_addr);
Asm("nop_s");
Asm("nop_s");
Asm("nop_s");
/* wait for flush completion */
while (_arc_aux_read(AUX_DC_CTRL) & DC_CTRL_FLUSH_STATUS);
start_addr += line_size;
}
while (start_addr <= end_addr);
cpu_unlock_restore(status);
return 0;
}
/**
* \brief flush multi lines in data cache
*
* \param[in] start_addr start address
* \param[in] size the bytes to be flushed
* \return 0, succeeded, -1, failed
*/
int32_t dcache_flush_mlines(uint32_t start_addr, uint32_t size)
{
if (!dcache_available()) { return -1; }
if ((size == 0) || (size > dcache_config.capacity))
{
return -1;
}
uint32_t end_addr;
uint32_t line_size;
uint32_t status;
line_size = (uint32_t)(dcache_config.line);
end_addr = start_addr + size - 1;
start_addr &= (uint32_t)(~(line_size - 1));
status = cpu_lock_save();
do
{
_arc_aux_write(AUX_DC_FLDL, start_addr);
Asm("nop_s");
Asm("nop_s");
Asm("nop_s");
/* wait for flush completion */
while (_arc_aux_read(AUX_DC_CTRL) & DC_CTRL_FLUSH_STATUS);
start_addr += line_size;
}
while (start_addr <= end_addr);
cpu_unlock_restore(status);
return 0;
}
/**
* \brief lock multi lines in data cache
*
* \param[in] start_addr start address in data cache
* \param[in] size the bytes to be locked
* \return 0, succeeded, -1, failed
*/
int32_t dcache_lock_mlines(uint32_t start_addr, uint32_t size)
{
if (!dcache_available()) { return -1; }
if ((size == 0) || (size > dcache_config.capacity))
{
return -1;
}
uint32_t end_addr;
uint32_t line_size;
uint32_t status;
int32_t ercd = 0;
line_size = (uint32_t)(dcache_config.line);
end_addr = start_addr + size - 1;
start_addr &= (uint32_t)(~(line_size - 1));
status = cpu_lock_save();
do
{
_arc_aux_write(AUX_DC_LDL, start_addr);
Asm("nop_s");
if (_arc_aux_read(AUX_DC_CTRL) & DC_CTRL_OP_SUCCEEDED)
{
start_addr += line_size;
}
else
{
ercd = -1; /* the operation failed */
break;
}
}
while (start_addr <= end_addr);
cpu_unlock_restore(status);
return ercd;
}
/**
* \brief directly write dcache internal ram
*
* \param[in] cache_addr, dcache internal address(way+index+offset)
* \param[in] tag cache tag to write
* \param[in] data cache data to write
* \return 0, succeeded, -1, failed
*/
int32_t dcache_direct_write(uint32_t cache_addr, uint32_t tag, uint32_t data)
{
if (!dcache_available()) { return -1; }
if (_arc_aux_read(AUX_DC_CTRL) & DC_CTRL_INDIRECT_ACCESS)
{
return -1;
}
_arc_aux_write(AUX_DC_RAM_ADDR, cache_addr);
_arc_aux_write(AUX_DC_TAG, tag);
_arc_aux_write(AUX_DC_DATA, data);
return 0;
}
/**
* \brief directly read dcache internal ram
*
* \param[in] cache_addr, dcache internal address(way+index+offset)
* \param[out] tag cache tag to read
* \param[out] data cache data to read
* \return 0, succeeded, -1, failed
*/
int32_t dcache_direct_read(uint32_t cache_addr, uint32_t *tag, uint32_t *data)
{
if (!dcache_available()) { return -1; }
if (_arc_aux_read(AUX_DC_CTRL) & DC_CTRL_INDIRECT_ACCESS)
{
return -1;
}
_arc_aux_write(AUX_DC_RAM_ADDR, cache_addr);
*tag = _arc_aux_read(AUX_DC_TAG);
*data = _arc_aux_read(AUX_DC_DATA);
return 0;
}
/**
* \brief indirectly read dcache internal ram
*
* \param[in] mem_addr, memory address(tag+index+offset)
* \param[out] tag cache tag to read
* \param[out] data cache data to read
* \return 0, succeeded, -1, failed
*/
int32_t dcache_indirect_read(uint32_t mem_addr, uint32_t *tag, uint32_t *data)
{
if (!dcache_available()) { return -1; }
if (!(_arc_aux_read(AUX_DC_CTRL) & DC_CTRL_INDIRECT_ACCESS))
{
return -1;
}
_arc_aux_write(AUX_DC_RAM_ADDR, mem_addr);
if (_arc_aux_read(AUX_DC_CTRL) & DC_CTRL_OP_SUCCEEDED)
{
*tag = _arc_aux_read(AUX_DC_TAG);
*data = _arc_aux_read(AUX_DC_DATA);
}
else
{
return -1; /* the specified memory is not in dcache */
}
return 0;
}
/**
* \brief initialize cache
* 1. invalidate icache and dcache
* 2. Only support ARCv2 cache
*/
void arc_cache_init(void)
{
uint32_t build_cfg;
build_cfg = _arc_aux_read(AUX_BCR_D_CACHE);
dcache_config.ver = build_cfg & 0xff;
if (dcache_config.ver >= 0x04) /* ARCv2 */
{
dcache_enable(DC_CTRL_DISABLE_FLUSH_LOCKED |
DC_CTRL_INDIRECT_ACCESS | DC_CTRL_INVALID_FLUSH);
dcache_invalidate();
dcache_config.assoc = 1 << ((build_cfg >> 8) & 0xf);
dcache_config.capacity = 512 << ((build_cfg >> 12) & 0xf);
dcache_config.line = 16 << ((build_cfg >> 16) & 0xf);
}
build_cfg = _arc_aux_read(AUX_BCR_I_CACHE);
icache_config.ver = build_cfg & 0xff;
if (icache_config.ver >= 0x04) /* ARCv2 */
{
icache_config.assoc = 1 << ((build_cfg >> 8) & 0xf);
icache_config.capacity = 512 << ((build_cfg >> 12) & 0xf);
icache_config.line = 8 << ((build_cfg >> 16) & 0xf);
icache_enable(IC_CTRL_IC_ENABLE);
icache_invalidate();
}
}