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_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size) 55 { 56 enum teecore_memtypes mtype; 57 paddr_t pbase; 58 vaddr_t vbase; 59 ssize_t sz; 60 int st; 61 62 assert(cpu_mmu_enabled()); 63 64 st = _fdt_get_status(fdt, offs); 65 if (st == DT_STATUS_DISABLED) 66 return -1; 67 68 pbase = _fdt_reg_base_address(fdt, offs); 69 if (pbase == DT_INFO_INVALID_REG) 70 return -1; 71 sz = _fdt_reg_size(fdt, offs); 72 if (sz < 0) 73 return -1; 74 75 if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC)) 76 mtype = MEM_AREA_IO_SEC; 77 else 78 mtype = MEM_AREA_IO_NSEC; 79 80 /* Check if we have a mapping, create one if needed */ 81 if (!core_mmu_add_mapping(mtype, pbase, sz)) { 82 EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA, 83 (size_t)sz, pbase); 84 return -1; 85 } 86 vbase = (vaddr_t)phys_to_virt(pbase, mtype); 87 if (!vbase) { 88 EMSG("Failed to get VA for PA 0x%"PRIxPA, pbase); 89 return -1; 90 } 91 92 *base = vbase; 93 *size = sz; 94 return 0; 95 } 96 97 /* Read a physical address (n=1 or 2 cells) */ 98 static paddr_t _fdt_read_paddr(const uint32_t *cell, int n) 99 { 100 paddr_t addr; 101 102 if (n < 1 || n > 2) 103 goto bad; 104 105 addr = fdt32_to_cpu(*cell); 106 cell++; 107 if (n == 2) { 108 #ifdef ARM32 109 if (addr) { 110 /* High order 32 bits can't be nonzero */ 111 goto bad; 112 } 113 addr = fdt32_to_cpu(*cell); 114 #else 115 addr = (addr << 32) | fdt32_to_cpu(*cell); 116 #endif 117 } 118 119 if (!addr) 120 goto bad; 121 122 return addr; 123 bad: 124 return DT_INFO_INVALID_REG; 125 126 } 127 128 paddr_t _fdt_reg_base_address(const void *fdt, int offs) 129 { 130 const void *reg; 131 int ncells; 132 int len; 133 int parent; 134 135 parent = fdt_parent_offset(fdt, offs); 136 if (parent < 0) 137 return DT_INFO_INVALID_REG; 138 139 reg = fdt_getprop(fdt, offs, "reg", &len); 140 if (!reg) 141 return DT_INFO_INVALID_REG; 142 143 ncells = fdt_address_cells(fdt, parent); 144 if (ncells < 0) 145 return DT_INFO_INVALID_REG; 146 147 return _fdt_read_paddr(reg, ncells); 148 } 149 150 ssize_t _fdt_reg_size(const void *fdt, int offs) 151 { 152 const uint32_t *reg; 153 uint32_t sz; 154 int n; 155 int len; 156 int parent; 157 158 parent = fdt_parent_offset(fdt, offs); 159 if (parent < 0) 160 return DT_INFO_INVALID_REG; 161 162 reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len); 163 if (!reg) 164 return -1; 165 166 n = fdt_address_cells(fdt, parent); 167 if (n < 1 || n > 2) 168 return -1; 169 170 reg += n; 171 172 n = fdt_size_cells(fdt, parent); 173 if (n < 1 || n > 2) 174 return -1; 175 176 sz = fdt32_to_cpu(*reg); 177 if (n == 2) { 178 if (sz) 179 return -1; 180 reg++; 181 sz = fdt32_to_cpu(*reg); 182 } 183 184 return sz; 185 } 186 187 static bool is_okay(const char *st, int len) 188 { 189 return !strncmp(st, "ok", len) || !strncmp(st, "okay", len); 190 } 191 192 int _fdt_get_status(const void *fdt, int offs) 193 { 194 const char *prop; 195 int st = 0; 196 int len; 197 198 prop = fdt_getprop(fdt, offs, "status", &len); 199 if (!prop || is_okay(prop, len)) { 200 /* If status is not specified, it defaults to "okay" */ 201 st |= DT_STATUS_OK_NSEC; 202 } 203 204 prop = fdt_getprop(fdt, offs, "secure-status", &len); 205 if (!prop) { 206 /* 207 * When secure-status is not specified it defaults to the same 208 * value as status 209 */ 210 if (st & DT_STATUS_OK_NSEC) 211 st |= DT_STATUS_OK_SEC; 212 } else { 213 if (is_okay(prop, len)) 214 st |= DT_STATUS_OK_SEC; 215 } 216 217 return st; 218 } 219 220 void _fdt_fill_device_info(void *fdt, struct dt_node_info *info, int offs) 221 { 222 struct dt_node_info dinfo = { 223 .reg = DT_INFO_INVALID_REG, 224 .clock = DT_INFO_INVALID_CLOCK, 225 .reset = DT_INFO_INVALID_RESET, 226 }; 227 const fdt32_t *cuint; 228 229 dinfo.reg = _fdt_reg_base_address(fdt, offs); 230 231 cuint = fdt_getprop(fdt, offs, "clocks", NULL); 232 if (cuint) { 233 cuint++; 234 dinfo.clock = (int)fdt32_to_cpu(*cuint); 235 } 236 237 cuint = fdt_getprop(fdt, offs, "resets", NULL); 238 if (cuint) { 239 cuint++; 240 dinfo.reset = (int)fdt32_to_cpu(*cuint); 241 } 242 243 dinfo.status = _fdt_get_status(fdt, offs); 244 245 *info = dinfo; 246 } 247