text: /* ------------------------------------------
   * Copyright (c) 2017, 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.
   *
  --------------------------------------------- */
  
  /**
   * \file
   * \ingroup ARC_HAL_MISC_CACHE
   * \brief implementation of cache related functions
   */
  #undef LIB_SECURESHIELD_OVERRIDES
  #include "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();
  	}
  
  }
  