| /* ------------------------------------------ |
| * 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(); |
| } |
| |
| } |