forked from Mirrors/opensbi
		
	platform: generic: andes: add a new Andes SBI call to set up a PMA entry
Implement a new Andes SBI call, which is to set up a NAPOT region with given memory attributes. Signed-off-by: Ben Zong-You Xie <ben717@andestech.com> Reviewed-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
		
				
					committed by
					
						
						Anup Patel
					
				
			
			
				
	
			
			
			
						parent
						
							4a72abb5f4
						
					
				
				
					commit
					aa56084c4d
				
			@@ -94,6 +94,74 @@ static inline bool not_napot(unsigned long addr, unsigned long size)
 | 
			
		||||
	return ((size & (size - 1)) || (addr & (size - 1)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool is_pma_entry_disable(char pmaxcfg)
 | 
			
		||||
{
 | 
			
		||||
	return (pmaxcfg & ANDES_PMACFG_ETYP_MASK) == ANDES_PMACFG_ETYP_OFF ?
 | 
			
		||||
	       true : false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char get_pmaxcfg(int entry_id)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int pmacfg_addr;
 | 
			
		||||
	unsigned long pmacfg_val;
 | 
			
		||||
	char *pmaxcfg;
 | 
			
		||||
 | 
			
		||||
#if __riscv_xlen == 64
 | 
			
		||||
	pmacfg_addr = CSR_PMACFG0 + ((entry_id / 8) ? 2 : 0);
 | 
			
		||||
	pmacfg_val = andes_pma_read_num(pmacfg_addr);
 | 
			
		||||
	pmaxcfg = (char *)&pmacfg_val + (entry_id % 8);
 | 
			
		||||
#elif __riscv_xlen == 32
 | 
			
		||||
	pmacfg_addr = CSR_PMACFG0 + (entry_id / 4);
 | 
			
		||||
	pmacfg_val = andes_pma_read_num(pmacfg_addr);
 | 
			
		||||
	pmaxcfg = (char *)&pmacfg_val + (entry_id % 4);
 | 
			
		||||
#else
 | 
			
		||||
#error "Unexpected __riscv_xlen"
 | 
			
		||||
#endif
 | 
			
		||||
	return *pmaxcfg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void decode_pmaaddrx(int entry_id, unsigned long *start,
 | 
			
		||||
			    unsigned long *size)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long pmaaddr;
 | 
			
		||||
	int k;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Given $pmaaddr, let k = # of trailing 1s of $pmaaddr
 | 
			
		||||
	 * size = 2 ^ (k + 3)
 | 
			
		||||
	 * start = 4 * ($pmaaddr - (size / 8) + 1)
 | 
			
		||||
	 */
 | 
			
		||||
	pmaaddr = andes_pma_read_num(CSR_PMAADDR0 + entry_id);
 | 
			
		||||
	k = sbi_ffz(pmaaddr);
 | 
			
		||||
	*size = 1 << (k + 3);
 | 
			
		||||
	*start = (pmaaddr - (1 << k) + 1) << 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool has_pma_region_overlap(unsigned long start, unsigned long size)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long _start, _size, _end, end;
 | 
			
		||||
	char pmaxcfg;
 | 
			
		||||
 | 
			
		||||
	end = start + size - 1;
 | 
			
		||||
	for (int i = 0; i < ANDES_MAX_PMA_REGIONS; i++) {
 | 
			
		||||
		pmaxcfg = get_pmaxcfg(i);
 | 
			
		||||
		if (is_pma_entry_disable(pmaxcfg))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		decode_pmaaddrx(i, &_start, &_size);
 | 
			
		||||
		_end = _start + _size - 1;
 | 
			
		||||
 | 
			
		||||
		if (MAX(start, _start) <= MIN(end, _end)) {
 | 
			
		||||
			sbi_printf(
 | 
			
		||||
				"ERROR %s(): %#lx ~ %#lx overlaps with PMA%d: %#lx ~ %#lx\n",
 | 
			
		||||
				__func__, start, end, i, _start, _end);
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned long andes_pma_setup(const struct andes_pma_region *pma_region,
 | 
			
		||||
				     unsigned int entry_id)
 | 
			
		||||
{
 | 
			
		||||
@@ -294,3 +362,45 @@ bool andes_sbi_probe_pma(void)
 | 
			
		||||
{
 | 
			
		||||
	return (csr_read(CSR_MMSC_CFG) & MMSC_CFG_PPMA_MASK) ? true : false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int andes_sbi_set_pma(unsigned long pa, unsigned long size, u8 flags)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int entry_id;
 | 
			
		||||
	unsigned long rc;
 | 
			
		||||
	char pmaxcfg;
 | 
			
		||||
	struct andes_pma_region region;
 | 
			
		||||
 | 
			
		||||
	if (!andes_sbi_probe_pma()) {
 | 
			
		||||
		sbi_printf("ERROR %s(): Platform does not support PPMA.\n",
 | 
			
		||||
			   __func__);
 | 
			
		||||
		return SBI_ERR_NOT_SUPPORTED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (has_pma_region_overlap(pa, size))
 | 
			
		||||
		return SBI_ERR_INVALID_PARAM;
 | 
			
		||||
 | 
			
		||||
	for (entry_id = 0; entry_id < ANDES_MAX_PMA_REGIONS; entry_id++) {
 | 
			
		||||
		pmaxcfg = get_pmaxcfg(entry_id);
 | 
			
		||||
		if (is_pma_entry_disable(pmaxcfg)) {
 | 
			
		||||
			region.pa = pa;
 | 
			
		||||
			region.size = size;
 | 
			
		||||
			region.flags = flags;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (entry_id == ANDES_MAX_PMA_REGIONS) {
 | 
			
		||||
		sbi_printf("ERROR %s(): All PMA entries have run out\n",
 | 
			
		||||
			   __func__);
 | 
			
		||||
		return SBI_ERR_FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = andes_pma_setup(®ion, entry_id);
 | 
			
		||||
	if (rc == SBI_EINVAL) {
 | 
			
		||||
		sbi_printf("ERROR %s(): Failed to set PMAADDR%d\n",
 | 
			
		||||
			   __func__, entry_id);
 | 
			
		||||
		return SBI_ERR_FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return SBI_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ enum sbi_ext_andes_fid {
 | 
			
		||||
	SBI_EXT_ANDES_FID0 = 0, /* Reserved for future use */
 | 
			
		||||
	SBI_EXT_ANDES_IOCP_SW_WORKAROUND,
 | 
			
		||||
	SBI_EXT_ANDES_PMA_PROBE,
 | 
			
		||||
	SBI_EXT_ANDES_PMA_SET,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool andes_cache_controllable(void)
 | 
			
		||||
@@ -39,6 +40,8 @@ int andes_sbi_vendor_ext_provider(long funcid,
 | 
			
		||||
				  struct sbi_ecall_return *out,
 | 
			
		||||
				  const struct fdt_match *match)
 | 
			
		||||
{
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	switch (funcid) {
 | 
			
		||||
	case SBI_EXT_ANDES_IOCP_SW_WORKAROUND:
 | 
			
		||||
		out->value = andes_apply_iocp_sw_workaround();
 | 
			
		||||
@@ -46,10 +49,12 @@ int andes_sbi_vendor_ext_provider(long funcid,
 | 
			
		||||
	case SBI_EXT_ANDES_PMA_PROBE:
 | 
			
		||||
		out->value = andes_sbi_probe_pma();
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SBI_EXT_ANDES_PMA_SET:
 | 
			
		||||
		ret = andes_sbi_set_pma(regs->a0, regs->a1, regs->a2);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return SBI_EINVAL;
 | 
			
		||||
		ret = SBI_ENOTSUPP;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,11 +12,15 @@
 | 
			
		||||
 | 
			
		||||
#define ANDES_PMA_GRANULARITY			(1 << 12)
 | 
			
		||||
 | 
			
		||||
#define ANDES_PMACFG_ETYP_OFFSET		0
 | 
			
		||||
#define ANDES_PMACFG_ETYP_MASK			(3 << ANDES_PMACFG_ETYP_OFFSET)
 | 
			
		||||
#define ANDES_PMACFG_ETYP_OFF			(0 << ANDES_PMACFG_ETYP_OFFSET)
 | 
			
		||||
/* Naturally aligned power of 2 region */
 | 
			
		||||
#define ANDES_PMACFG_ETYP_NAPOT			3
 | 
			
		||||
#define ANDES_PMACFG_ETYP_NAPOT			(3 << ANDES_PMACFG_ETYP_OFFSET)
 | 
			
		||||
 | 
			
		||||
#define ANDES_PMACFG_MTYP_OFFSET		2
 | 
			
		||||
/* Memory, Non-cacheable, Bufferable */
 | 
			
		||||
#define ANDES_PMACFG_MTYP_MEM_NON_CACHE_BUF	(3 << 2)
 | 
			
		||||
#define ANDES_PMACFG_MTYP_MEM_NON_CACHE_BUF	(3 << ANDES_PMACFG_MTYP_OFFSET)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * struct andes_pma_region - Describes PMA regions
 | 
			
		||||
@@ -59,4 +63,18 @@ int andes_pma_setup_regions(const struct andes_pma_region *pma_regions,
 | 
			
		||||
 */
 | 
			
		||||
bool andes_sbi_probe_pma(void);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Set a NAPOT region with given memory attributes
 | 
			
		||||
 * @param pa: Start address of the NAPOT region
 | 
			
		||||
 * @param size: Size of the NAPOT region
 | 
			
		||||
 * @param flags: Memory attributes set to the NAPOT region
 | 
			
		||||
 *
 | 
			
		||||
 * @return SBI_SUCCESS on success
 | 
			
		||||
 * @return SBI_ERR_NOT_SUPPORTED if hardware does not support PPMA features
 | 
			
		||||
 * @return SBI_ERR_INVALID_PARAM if the given region is overlapped with the
 | 
			
		||||
 *	   region that has been set already
 | 
			
		||||
 * @return SBI_ERR_FAILED if available entries have run out or setup fails
 | 
			
		||||
 */
 | 
			
		||||
int andes_sbi_set_pma(unsigned long pa, unsigned long size, u8 flags);
 | 
			
		||||
 | 
			
		||||
#endif /* _ANDES_PMA_H_ */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user