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