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 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 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 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 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 89 int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size) 90 { 91 enum teecore_memtypes mtype; 92 paddr_t pbase; 93 vaddr_t vbase; 94 size_t sz; 95 int st; 96 97 assert(cpu_mmu_enabled()); 98 99 st = _fdt_get_status(fdt, offs); 100 if (st == DT_STATUS_DISABLED) 101 return -1; 102 103 pbase = _fdt_reg_base_address(fdt, offs); 104 if (pbase == DT_INFO_INVALID_REG) 105 return -1; 106 sz = _fdt_reg_size(fdt, offs); 107 if (sz == DT_INFO_INVALID_REG_SIZE) 108 return -1; 109 110 if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC)) 111 mtype = MEM_AREA_IO_SEC; 112 else 113 mtype = MEM_AREA_IO_NSEC; 114 115 /* Check if we have a mapping, create one if needed */ 116 vbase = (vaddr_t)core_mmu_add_mapping(mtype, pbase, sz); 117 if (!vbase) { 118 EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA, 119 (size_t)sz, pbase); 120 return -1; 121 } 122 123 *base = vbase; 124 *size = sz; 125 return 0; 126 } 127 128 /* Read a physical address (n=1 or 2 cells) */ 129 static paddr_t _fdt_read_paddr(const uint32_t *cell, int n) 130 { 131 paddr_t addr; 132 133 if (n < 1 || n > 2) 134 goto bad; 135 136 addr = fdt32_to_cpu(*cell); 137 cell++; 138 if (n == 2) { 139 #ifdef ARM32 140 if (addr) { 141 /* High order 32 bits can't be nonzero */ 142 goto bad; 143 } 144 addr = fdt32_to_cpu(*cell); 145 #else 146 addr = (addr << 32) | fdt32_to_cpu(*cell); 147 #endif 148 } 149 150 if (!addr) 151 goto bad; 152 153 return addr; 154 bad: 155 return DT_INFO_INVALID_REG; 156 157 } 158 159 paddr_t _fdt_reg_base_address(const void *fdt, int offs) 160 { 161 const void *reg; 162 int ncells; 163 int len; 164 int parent; 165 166 parent = fdt_parent_offset(fdt, offs); 167 if (parent < 0) 168 return DT_INFO_INVALID_REG; 169 170 reg = fdt_getprop(fdt, offs, "reg", &len); 171 if (!reg) 172 return DT_INFO_INVALID_REG; 173 174 ncells = fdt_address_cells(fdt, parent); 175 if (ncells < 0) 176 return DT_INFO_INVALID_REG; 177 178 return _fdt_read_paddr(reg, ncells); 179 } 180 181 size_t _fdt_reg_size(const void *fdt, int offs) 182 { 183 const uint32_t *reg; 184 uint32_t sz; 185 int n; 186 int len; 187 int parent; 188 189 parent = fdt_parent_offset(fdt, offs); 190 if (parent < 0) 191 return DT_INFO_INVALID_REG_SIZE; 192 193 reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len); 194 if (!reg) 195 return DT_INFO_INVALID_REG_SIZE; 196 197 n = fdt_address_cells(fdt, parent); 198 if (n < 1 || n > 2) 199 return DT_INFO_INVALID_REG_SIZE; 200 201 reg += n; 202 203 n = fdt_size_cells(fdt, parent); 204 if (n < 1 || n > 2) 205 return DT_INFO_INVALID_REG_SIZE; 206 207 sz = fdt32_to_cpu(*reg); 208 if (n == 2) { 209 if (sz) 210 return DT_INFO_INVALID_REG_SIZE; 211 reg++; 212 sz = fdt32_to_cpu(*reg); 213 } 214 215 return sz; 216 } 217 218 static bool is_okay(const char *st, int len) 219 { 220 return !strncmp(st, "ok", len) || !strncmp(st, "okay", len); 221 } 222 223 int _fdt_get_status(const void *fdt, int offs) 224 { 225 const char *prop; 226 int st = 0; 227 int len; 228 229 prop = fdt_getprop(fdt, offs, "status", &len); 230 if (!prop || is_okay(prop, len)) { 231 /* If status is not specified, it defaults to "okay" */ 232 st |= DT_STATUS_OK_NSEC; 233 } 234 235 prop = fdt_getprop(fdt, offs, "secure-status", &len); 236 if (!prop) { 237 /* 238 * When secure-status is not specified it defaults to the same 239 * value as status 240 */ 241 if (st & DT_STATUS_OK_NSEC) 242 st |= DT_STATUS_OK_SEC; 243 } else { 244 if (is_okay(prop, len)) 245 st |= DT_STATUS_OK_SEC; 246 } 247 248 return st; 249 } 250 251 void _fdt_fill_device_info(const void *fdt, struct dt_node_info *info, int offs) 252 { 253 struct dt_node_info dinfo = { 254 .reg = DT_INFO_INVALID_REG, 255 .reg_size = DT_INFO_INVALID_REG_SIZE, 256 .clock = DT_INFO_INVALID_CLOCK, 257 .reset = DT_INFO_INVALID_RESET, 258 .interrupt = DT_INFO_INVALID_INTERRUPT, 259 }; 260 const fdt32_t *cuint; 261 262 dinfo.reg = _fdt_reg_base_address(fdt, offs); 263 dinfo.reg_size = _fdt_reg_size(fdt, offs); 264 265 cuint = fdt_getprop(fdt, offs, "clocks", NULL); 266 if (cuint) { 267 cuint++; 268 dinfo.clock = (int)fdt32_to_cpu(*cuint); 269 } 270 271 cuint = fdt_getprop(fdt, offs, "resets", NULL); 272 if (cuint) { 273 cuint++; 274 dinfo.reset = (int)fdt32_to_cpu(*cuint); 275 } 276 277 dinfo.interrupt = dt_get_irq_type_prio(fdt, offs, &dinfo.type, 278 &dinfo.prio); 279 280 dinfo.status = _fdt_get_status(fdt, offs); 281 282 *info = dinfo; 283 } 284 285 int _fdt_read_uint32_array(const void *fdt, int node, const char *prop_name, 286 uint32_t *array, size_t count) 287 { 288 const fdt32_t *cuint = NULL; 289 int len = 0; 290 uint32_t i = 0; 291 292 cuint = fdt_getprop(fdt, node, prop_name, &len); 293 if (!cuint) 294 return -FDT_ERR_NOTFOUND; 295 296 if ((uint32_t)len != (count * sizeof(uint32_t))) 297 return -FDT_ERR_BADLAYOUT; 298 299 for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) { 300 *array = fdt32_to_cpu(*cuint); 301 array++; 302 cuint++; 303 } 304 305 return 0; 306 } 307 308 int _fdt_read_uint32(const void *fdt, int node, const char *prop_name, 309 uint32_t *value) 310 { 311 return _fdt_read_uint32_array(fdt, node, prop_name, value, 1); 312 } 313 314 uint32_t _fdt_read_uint32_default(const void *fdt, int node, 315 const char *prop_name, uint32_t dflt_value) 316 { 317 uint32_t value = 0; 318 319 if (_fdt_read_uint32(fdt, node, prop_name, &value) < 0) 320 return dflt_value; 321 322 return value; 323 } 324