1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright (c) 2016, Linaro Limited
4 */
5
6 #include <assert.h>
7 #include <kernel/dt.h>
8 #include <kernel/interrupt.h>
9 #include <kernel/linker.h>
10 #include <libfdt.h>
11 #include <mm/core_memprot.h>
12 #include <mm/core_mmu.h>
13 #include <string.h>
14 #include <trace.h>
15
dt_find_compatible_driver(const void * fdt,int offs)16 const struct dt_driver *dt_find_compatible_driver(const void *fdt, int offs)
17 {
18 const struct dt_device_match *dm;
19 const struct dt_driver *drv;
20
21 for_each_dt_driver(drv) {
22 for (dm = drv->match_table; dm; dm++) {
23 if (!dm->compatible) {
24 break;
25 }
26 if (!fdt_node_check_compatible(fdt, offs,
27 dm->compatible)) {
28 return drv;
29 }
30 }
31 }
32
33 return NULL;
34 }
35
dt_have_prop(const void * fdt,int offs,const char * propname)36 bool dt_have_prop(const void *fdt, int offs, const char *propname)
37 {
38 const void *prop;
39
40 prop = fdt_getprop(fdt, offs, propname, NULL);
41
42 return prop;
43 }
44
dt_disable_status(void * fdt,int node)45 int dt_disable_status(void *fdt, int node)
46 {
47 const char *prop = NULL;
48 int len = 0;
49
50 prop = fdt_getprop(fdt, node, "status", &len);
51 if (!prop) {
52 if (fdt_setprop_string(fdt, node, "status", "disabled"))
53 return -1;
54 } else {
55 /*
56 * Status is there, modify it.
57 * Ask to set "disabled" value to the property. The value
58 * will be automatically truncated with "len" size by the
59 * fdt_setprop_inplace function.
60 * Setting a value different from "ok" or "okay" will disable
61 * the property.
62 * Setting a truncated value of "disabled" with the original
63 * property "len" is preferred to not increase the DT size and
64 * losing time in recalculating the overall DT offsets.
65 * If original length of the status property is larger than
66 * "disabled", the property will start with "disabled" and be
67 * completed with the rest of the original property.
68 */
69 if (fdt_setprop_inplace(fdt, node, "status", "disabled", len))
70 return -1;
71 }
72
73 return 0;
74 }
75
dt_enable_secure_status(void * fdt,int node)76 int dt_enable_secure_status(void *fdt, int node)
77 {
78 if (dt_disable_status(fdt, node)) {
79 EMSG("Unable to disable Normal Status");
80 return -1;
81 }
82
83 if (fdt_setprop_string(fdt, node, "secure-status", "okay"))
84 return -1;
85
86 return 0;
87 }
88
dt_map_dev(const void * fdt,int offs,vaddr_t * base,size_t * size,enum dt_map_dev_directive mapping)89 int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size,
90 enum dt_map_dev_directive mapping)
91 {
92 enum teecore_memtypes mtype;
93 paddr_t pbase;
94 vaddr_t vbase;
95 size_t sz;
96 int st;
97
98 assert(cpu_mmu_enabled());
99
100 st = _fdt_get_status(fdt, offs);
101 if (st == DT_STATUS_DISABLED)
102 return -1;
103
104 pbase = _fdt_reg_base_address(fdt, offs);
105 if (pbase == DT_INFO_INVALID_REG)
106 return -1;
107 sz = _fdt_reg_size(fdt, offs);
108 if (sz == DT_INFO_INVALID_REG_SIZE)
109 return -1;
110
111 switch (mapping) {
112 case DT_MAP_AUTO:
113 if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC))
114 mtype = MEM_AREA_IO_SEC;
115 else
116 mtype = MEM_AREA_IO_NSEC;
117 break;
118 case DT_MAP_SECURE:
119 mtype = MEM_AREA_IO_SEC;
120 break;
121 case DT_MAP_NON_SECURE:
122 mtype = MEM_AREA_IO_NSEC;
123 break;
124 default:
125 panic("Invalid mapping specified");
126 break;
127 }
128
129 /* Check if we have a mapping, create one if needed */
130 vbase = (vaddr_t)core_mmu_add_mapping(mtype, pbase, sz);
131 if (!vbase) {
132 EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA,
133 (size_t)sz, pbase);
134 return -1;
135 }
136
137 *base = vbase;
138 *size = sz;
139 return 0;
140 }
141
142 /* Read a physical address (n=1 or 2 cells) */
_fdt_read_paddr(const uint32_t * cell,int n)143 static paddr_t _fdt_read_paddr(const uint32_t *cell, int n)
144 {
145 paddr_t addr;
146
147 if (n < 1 || n > 2)
148 goto bad;
149
150 addr = fdt32_to_cpu(*cell);
151 cell++;
152 if (n == 2) {
153 #ifdef ARM32
154 if (addr) {
155 /* High order 32 bits can't be nonzero */
156 goto bad;
157 }
158 addr = fdt32_to_cpu(*cell);
159 #else
160 addr = (addr << 32) | fdt32_to_cpu(*cell);
161 #endif
162 }
163
164 return addr;
165 bad:
166 return DT_INFO_INVALID_REG;
167
168 }
169
_fdt_reg_base_address(const void * fdt,int offs)170 paddr_t _fdt_reg_base_address(const void *fdt, int offs)
171 {
172 const void *reg;
173 int ncells;
174 int len;
175 int parent;
176
177 parent = fdt_parent_offset(fdt, offs);
178 if (parent < 0)
179 return DT_INFO_INVALID_REG;
180
181 reg = fdt_getprop(fdt, offs, "reg", &len);
182 if (!reg)
183 return DT_INFO_INVALID_REG;
184
185 ncells = fdt_address_cells(fdt, parent);
186 if (ncells < 0)
187 return DT_INFO_INVALID_REG;
188
189 return _fdt_read_paddr(reg, ncells);
190 }
191
_fdt_reg_size(const void * fdt,int offs)192 size_t _fdt_reg_size(const void *fdt, int offs)
193 {
194 const uint32_t *reg;
195 uint32_t sz;
196 int n;
197 int len;
198 int parent;
199
200 parent = fdt_parent_offset(fdt, offs);
201 if (parent < 0)
202 return DT_INFO_INVALID_REG_SIZE;
203
204 reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len);
205 if (!reg)
206 return DT_INFO_INVALID_REG_SIZE;
207
208 n = fdt_address_cells(fdt, parent);
209 if (n < 1 || n > 2)
210 return DT_INFO_INVALID_REG_SIZE;
211
212 reg += n;
213
214 n = fdt_size_cells(fdt, parent);
215 if (n < 1 || n > 2)
216 return DT_INFO_INVALID_REG_SIZE;
217
218 sz = fdt32_to_cpu(*reg);
219 if (n == 2) {
220 if (sz)
221 return DT_INFO_INVALID_REG_SIZE;
222 reg++;
223 sz = fdt32_to_cpu(*reg);
224 }
225
226 return sz;
227 }
228
is_okay(const char * st,int len)229 static bool is_okay(const char *st, int len)
230 {
231 return !strncmp(st, "ok", len) || !strncmp(st, "okay", len);
232 }
233
_fdt_get_status(const void * fdt,int offs)234 int _fdt_get_status(const void *fdt, int offs)
235 {
236 const char *prop;
237 int st = 0;
238 int len;
239
240 prop = fdt_getprop(fdt, offs, "status", &len);
241 if (!prop || is_okay(prop, len)) {
242 /* If status is not specified, it defaults to "okay" */
243 st |= DT_STATUS_OK_NSEC;
244 }
245
246 prop = fdt_getprop(fdt, offs, "secure-status", &len);
247 if (!prop) {
248 /*
249 * When secure-status is not specified it defaults to the same
250 * value as status
251 */
252 if (st & DT_STATUS_OK_NSEC)
253 st |= DT_STATUS_OK_SEC;
254 } else {
255 if (is_okay(prop, len))
256 st |= DT_STATUS_OK_SEC;
257 }
258
259 return st;
260 }
261
_fdt_fill_device_info(const void * fdt,struct dt_node_info * info,int offs)262 void _fdt_fill_device_info(const void *fdt, struct dt_node_info *info, int offs)
263 {
264 struct dt_node_info dinfo = {
265 .reg = DT_INFO_INVALID_REG,
266 .reg_size = DT_INFO_INVALID_REG_SIZE,
267 .clock = DT_INFO_INVALID_CLOCK,
268 .reset = DT_INFO_INVALID_RESET,
269 .interrupt = DT_INFO_INVALID_INTERRUPT,
270 };
271 const fdt32_t *cuint;
272
273 dinfo.reg = _fdt_reg_base_address(fdt, offs);
274 dinfo.reg_size = _fdt_reg_size(fdt, offs);
275
276 cuint = fdt_getprop(fdt, offs, "clocks", NULL);
277 if (cuint) {
278 cuint++;
279 dinfo.clock = (int)fdt32_to_cpu(*cuint);
280 }
281
282 cuint = fdt_getprop(fdt, offs, "resets", NULL);
283 if (cuint) {
284 cuint++;
285 dinfo.reset = (int)fdt32_to_cpu(*cuint);
286 }
287
288 dinfo.interrupt = dt_get_irq_type_prio(fdt, offs, &dinfo.type,
289 &dinfo.prio);
290
291 dinfo.status = _fdt_get_status(fdt, offs);
292
293 *info = dinfo;
294 }
295
_fdt_read_uint32_array(const void * fdt,int node,const char * prop_name,uint32_t * array,size_t count)296 int _fdt_read_uint32_array(const void *fdt, int node, const char *prop_name,
297 uint32_t *array, size_t count)
298 {
299 const fdt32_t *cuint = NULL;
300 int len = 0;
301 uint32_t i = 0;
302
303 cuint = fdt_getprop(fdt, node, prop_name, &len);
304 if (!cuint)
305 return -FDT_ERR_NOTFOUND;
306
307 if ((uint32_t)len != (count * sizeof(uint32_t)))
308 return -FDT_ERR_BADLAYOUT;
309
310 for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) {
311 *array = fdt32_to_cpu(*cuint);
312 array++;
313 cuint++;
314 }
315
316 return 0;
317 }
318
_fdt_read_uint32(const void * fdt,int node,const char * prop_name,uint32_t * value)319 int _fdt_read_uint32(const void *fdt, int node, const char *prop_name,
320 uint32_t *value)
321 {
322 return _fdt_read_uint32_array(fdt, node, prop_name, value, 1);
323 }
324
_fdt_read_uint32_default(const void * fdt,int node,const char * prop_name,uint32_t dflt_value)325 uint32_t _fdt_read_uint32_default(const void *fdt, int node,
326 const char *prop_name, uint32_t dflt_value)
327 {
328 uint32_t value = 0;
329
330 if (_fdt_read_uint32(fdt, node, prop_name, &value) < 0)
331 return dflt_value;
332
333 return value;
334 }
335