1 /*
2  * Copyright (c) 2021 Andes Technology Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "soc_v5.h"
8 
9 #include <zephyr/init.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/arch/cpu.h>
12 #include <zephyr/linker/linker-defs.h>
13 #include <zephyr/arch/riscv/csr.h>
14 
15 #ifndef CONFIG_ASSERT
16 #define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(pma_init, LOG_LEVEL);
19 #endif
20 
21 /* Programmable PMA mechanism is supported */
22 #define MMSC_CFG_PPMA		BIT(30)
23 
24 /*
25  * PMA Configuration (PMACFG) bitfields
26  */
27 
28 /* ETYPE: Entry address matching mode */
29 #define PMACFG_ETYPE_MASK	BIT_MASK(2)
30 #define PMACFG_ETYPE_OFF	0
31 #define PMACFG_ETYPE_TOR	1
32 #define PMACFG_ETYPE_NA4	2
33 #define PMACFG_ETYPE_NAPOT	3
34 
35 /* MTYPE: Memory type attribute */
36 #define PMACFG_MTYPE_MASK			(0xF << 2)
37 /* non-cacheable attributes (bufferable or not) */
38 #define PMACFG_MTYPE_MEMORY_NOCACHE_BUFFERABLE	(3 << 2)
39 /* cacheable attributes (write-through/back, no/read/write/RW-allocate) */
40 #define PMACFG_MTYPE_MEMORY_WBCACHE_RWALLOC	(11 << 2)
41 
42 /* pmaaddr is 4-byte granularity in each mode */
43 #define TO_PMA_ADDR(addr)		((addr) >> 2)
44 
45 /* The base address is aligned to size */
46 #define NAPOT_BASE(start, size)		TO_PMA_ADDR((start) & ~((size) - 1))
47 /* The encoding of size is 0b01...1, (change the leading bit of bitmask to 0) */
48 #define NAPOT_SIZE(size)		TO_PMA_ADDR(((size) - 1) >> 1)
49 
50 #define NA4_ENCODING(start)		TO_PMA_ADDR(start)
51 #define NAPOT_ENCODING(start, size)	(NAPOT_BASE(start, size) \
52 					 | NAPOT_SIZE(size))
53 
54 #ifdef CONFIG_64BIT
55 /* In riscv64, CSR pmacfg number are even number (0, 2, ...) */
56 # define PMACFG_NUM(index)		((index / RV_REGSIZE) * 2)
57 #else
58 # define PMACFG_NUM(index)		(index / RV_REGSIZE)
59 #endif
60 #define PMACFG_SHIFT(index)		((index % RV_REGSIZE) * 8)
61 
62 struct pma_region_attr {
63 	/* Attributes belonging to pmacfg{i} */
64 	uint8_t pmacfg;
65 };
66 
67 struct pma_region {
68 	unsigned long start;
69 	unsigned long size;
70 	struct pma_region_attr attr;
71 };
72 
73 #ifdef CONFIG_NOCACHE_MEMORY
74 /*
75  * Write value to CSRs pmaaddr{i}
76  */
write_pmaaddr_csr(const uint32_t index,unsigned long value)77 static void write_pmaaddr_csr(const uint32_t index, unsigned long value)
78 {
79 	#define SWITCH_CASE_PMAADDR_WRITE(x)		\
80 		case (x):				\
81 		csr_write(NDS_PMAADDR##x,  value); break;
82 
83 	switch (index) {
84 	FOR_EACH(SWITCH_CASE_PMAADDR_WRITE, (;), 0, 1, 2, 3, 4, 5, 6, 7,
85 	8, 9, 10, 11, 12, 13, 14, 15);
86 	}
87 }
88 
89 /*
90  * Write value to pma{i}cfg entry which are packed into CSRs pmacfg{j}
91  */
write_pmacfg_entry(const uint32_t entry_index,uint8_t entry_value)92 static void write_pmacfg_entry(const uint32_t entry_index, uint8_t entry_value)
93 {
94 	/* 1-byte pma{i}cfg entries are packed into XLEN-byte CSRs pmacfg{j} */
95 	uint32_t index = PMACFG_NUM(entry_index);
96 	uint8_t shift = PMACFG_SHIFT(entry_index);
97 	unsigned long pmacfg = 0;
98 
99 	#define SWITCH_CASE_PMACFG_READ(x)		\
100 		case (x):				\
101 		pmacfg = csr_read(NDS_PMACFG##x); break;
102 
103 	switch (index) {
104 	FOR_EACH(SWITCH_CASE_PMACFG_READ, (;), 0, 1, 2, 3);
105 	}
106 
107 	/* clear old value in pmacfg entry */
108 	pmacfg &= ~(0xFF << shift);
109 	/* set new value to pmacfg entry value */
110 	pmacfg |= entry_value << shift;
111 
112 	#define SWITCH_CASE_PMACFG_WRITE(x)		\
113 		case (x):				\
114 		csr_write(NDS_PMACFG##x, pmacfg); break;
115 
116 	switch (index) {
117 	FOR_EACH(SWITCH_CASE_PMACFG_WRITE, (;), 0, 1, 2, 3);
118 	}
119 }
120 
121 /*
122  * This internal function performs PMA region initialization.
123  *
124  * Note:
125  *   The caller must provide a valid region index.
126  */
region_init(const uint32_t index,const struct pma_region * region_conf)127 static void region_init(const uint32_t index,
128 	const struct pma_region *region_conf)
129 {
130 	unsigned long pmaaddr;
131 	uint8_t pmacfg;
132 
133 	if (region_conf->size == 4) {
134 		pmaaddr = NA4_ENCODING(region_conf->start);
135 		pmacfg = region_conf->attr.pmacfg | PMACFG_ETYPE_NA4;
136 	} else {
137 		pmaaddr = NAPOT_ENCODING(region_conf->start, region_conf->size);
138 		pmacfg = region_conf->attr.pmacfg | PMACFG_ETYPE_NAPOT;
139 	}
140 
141 	write_pmaaddr_csr(index, pmaaddr);
142 	write_pmacfg_entry(index, pmacfg);
143 }
144 
145 /*
146  * This internal function performs run-time sanity check for
147  * PMA region start address and size.
148  */
pma_region_is_valid(const struct pma_region * region)149 static int pma_region_is_valid(const struct pma_region *region)
150 {
151 	/* Region size must greater or equal to the minimum PMA region size */
152 	if (region->size < CONFIG_SOC_ANDES_V5_PMA_REGION_MIN_ALIGN_AND_SIZE) {
153 		return -EINVAL;
154 	}
155 
156 	/* Region size must be power-of-two */
157 	if (region->size & (region->size - 1)) {
158 		return -EINVAL;
159 	}
160 
161 	/* Start address of the region must align with size */
162 	if (region->start & (region->size - 1)) {
163 		return -EINVAL;
164 	}
165 
166 	return 0;
167 }
168 
configure_nocache_region(void)169 static void configure_nocache_region(void)
170 {
171 	const struct pma_region nocache_region = {
172 		.start = (unsigned long)&_nocache_ram_start,
173 		.size = (unsigned long)&_nocache_ram_size,
174 		.attr = {PMACFG_MTYPE_MEMORY_NOCACHE_BUFFERABLE},
175 	};
176 
177 	if (pma_region_is_valid(&nocache_region)) {
178 		/* Skip PMA configuration if nocache region size is 0 */
179 		if (nocache_region.size != 0) {
180 			__ASSERT(0, "Configuring PMA region of nocache region "
181 				    "failed\n");
182 		}
183 	} else {
184 		/* Initialize nocache region at PMA region 0 */
185 		region_init(0, &nocache_region);
186 	}
187 }
188 #endif /* CONFIG_NOCACHE_MEMORY */
189 
pma_init_per_core(void)190 void pma_init_per_core(void)
191 {
192 #ifdef CONFIG_NOCACHE_MEMORY
193 	configure_nocache_region();
194 #endif /* CONFIG_NOCACHE_MEMORY */
195 }
196 
pma_init(void)197 void pma_init(void)
198 {
199 	unsigned long mmsc_cfg;
200 
201 	mmsc_cfg = csr_read(NDS_MMSC_CFG);
202 
203 	if (!(mmsc_cfg & MMSC_CFG_PPMA)) {
204 		/* This CPU doesn't support PMA */
205 
206 		__ASSERT(0, "CPU doesn't support PMA. "
207 			    "Please disable CONFIG_SOC_ANDES_V5_PMA\n");
208 #ifndef CONFIG_ASSERT
209 		LOG_ERR("CPU doesn't support PMA. "
210 			"Please disable CONFIG_SOC_ANDES_V5_PMA");
211 #endif
212 		return;
213 	}
214 }
215