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