1 /* $NetBSD: fdt_subr.c,v 1.43 2026/08/07 14:31:36 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2015 Jared D. McNeill <jmcneill (at) invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: fdt_subr.c,v 1.43 2026/08/07 14:31:36 thorpej Exp $"); 31 32 #include "opt_fdt.h" 33 34 #include <sys/param.h> 35 #include <sys/bus.h> 36 37 #include <libfdt.h> 38 #include <dev/fdt/fdtvar.h> 39 #include <dev/fdt/fdt_private.h> 40 41 #ifndef FDT_DEFAULT_STDOUT_PATH 42 #define FDT_DEFAULT_STDOUT_PATH "serial0:115200n8" 43 #endif 44 45 static const void *fdt_data; 46 47 bool 48 fdtbus_init(const void *data) 49 { 50 KASSERT(fdt_data == NULL); 51 if (fdt_check_header(data) != 0) { 52 return false; 53 } 54 fdt_data = data; 55 56 return true; 57 } 58 59 const void * 60 fdtbus_get_data(void) 61 { 62 return fdt_data; 63 } 64 65 int 66 fdtbus_offset2phandle(int offset) 67 { 68 if (offset < 0) 69 return 0; 70 71 return offset + fdt_off_dt_struct(fdt_data); 72 } 73 74 int 75 fdtbus_phandle2offset(int phandle) 76 { 77 const int dtoff = fdt_off_dt_struct(fdt_data); 78 79 if (phandle == -1) 80 phandle = dtoff; 81 82 if (phandle < dtoff) 83 return -1; 84 85 return phandle - dtoff; 86 } 87 88 static bool fdtbus_decoderegprop = true; 89 90 void 91 fdtbus_set_decoderegprop(bool decode) 92 { 93 fdtbus_decoderegprop = decode; 94 } 95 96 static int 97 fdtbus_get_u32prop_with_default(int phandle, const char *prop, int dflt) 98 { 99 uint32_t val; 100 101 if (of_getprop_uint32(phandle, prop, &val)) { 102 val = dflt; 103 } 104 105 return val; 106 } 107 108 int 109 fdtbus_get_addr_cells(int phandle) 110 { 111 return fdtbus_get_u32prop_with_default(phandle, "#address-cells", 2); 112 } 113 114 int 115 fdtbus_get_size_cells(int phandle) 116 { 117 return fdtbus_get_u32prop_with_default(phandle, "#size-cells", 0); 118 } 119 120 int 121 fdtbus_get_reg_shift(int phandle, int dflt) 122 { 123 return fdtbus_get_u32prop_with_default(phandle, "reg-shift", dflt); 124 } 125 126 int 127 fdtbus_get_phandle(int phandle, const char *prop) 128 { 129 u_int phandle_ref; 130 const u_int *buf; 131 int len; 132 133 buf = fdt_getprop(fdtbus_get_data(), 134 fdtbus_phandle2offset(phandle), prop, &len); 135 if (buf == NULL || len < sizeof(phandle_ref)) 136 return -1; 137 138 phandle_ref = be32dec(buf); 139 140 return fdtbus_get_phandle_from_native(phandle_ref); 141 } 142 143 int 144 fdtbus_get_phandle_with_data(int phandle, const char *prop, const char *cells, 145 int index, struct fdt_phandle_data *data) 146 { 147 int len; 148 const int offset = 1; 149 150 const u_int *p = fdtbus_get_prop(phandle, prop, &len); 151 if (p == NULL || len <= 0) 152 return EINVAL; 153 154 for (int i = 0; len > 0; i++) { 155 u_int phandle_ref = be32toh(*p); 156 const u_int iparent = fdtbus_get_phandle_from_native(phandle_ref); 157 uint32_t cells_num; 158 of_getprop_uint32(iparent, cells, &cells_num); 159 160 if (index == i) { 161 if (data != NULL) { 162 data->phandle = iparent; 163 data->count = cells_num; 164 data->values = p + offset; 165 } 166 goto done; 167 } 168 169 const u_int reclen = offset + cells_num; 170 len -= reclen * sizeof(u_int); 171 p += reclen; 172 } 173 return EINVAL; 174 175 done: 176 return 0; 177 } 178 179 int 180 fdtbus_get_phandle_from_native(int phandle) 181 { 182 const int off = fdt_node_offset_by_phandle(fdt_data, phandle); 183 if (off < 0) { 184 return -1; 185 } 186 return fdtbus_offset2phandle(off); 187 } 188 189 bool 190 fdtbus_get_path(int phandle, char *buf, size_t buflen) 191 { 192 const int off = fdtbus_phandle2offset(phandle); 193 if (off < 0) { 194 return false; 195 } 196 if (fdt_get_path(fdt_data, off, buf, (int)buflen) != 0) { 197 return false; 198 } 199 return true; 200 } 201 202 uint64_t 203 fdtbus_get_cells(const uint8_t *buf, int cells) 204 { 205 switch (cells) { 206 case 0: return 0; 207 case 1: return be32dec(buf); 208 case 2: return ((uint64_t)be32dec(buf)<<32)|be32dec(buf+4); 209 default: panic("fdtbus_get_cells: bad cells val %d\n", cells); 210 } 211 } 212 213 static uint64_t 214 fdtbus_decode_range(int phandle, uint64_t paddr) 215 { 216 const int parent = OF_parent(phandle); 217 if (parent == -1) 218 return paddr; 219 220 if (!fdtbus_decoderegprop) 221 return paddr; 222 223 const uint8_t *buf; 224 int len; 225 226 buf = fdt_getprop(fdtbus_get_data(), 227 fdtbus_phandle2offset(phandle), "ranges", &len); 228 if (buf == NULL) 229 return paddr; 230 231 if (len == 0) { 232 /* pass through to parent */ 233 return fdtbus_decode_range(parent, paddr); 234 } 235 236 const int addr_cells = fdtbus_get_addr_cells(phandle); 237 const int size_cells = fdtbus_get_size_cells(phandle); 238 const int paddr_cells = fdtbus_get_addr_cells(parent); 239 if (addr_cells == -1 || size_cells == -1 || paddr_cells == -1) 240 return paddr; 241 242 while (len > 0) { 243 uint64_t cba, pba, cl; 244 cba = fdtbus_get_cells(buf, addr_cells); 245 buf += addr_cells * 4; 246 pba = fdtbus_get_cells(buf, paddr_cells); 247 buf += paddr_cells * 4; 248 cl = fdtbus_get_cells(buf, size_cells); 249 buf += size_cells * 4; 250 251 #ifdef FDTBUS_DEBUG 252 printf("%s: %s: cba=%#" PRIx64 ", pba=%#" PRIx64 ", cl=%#" PRIx64 "\n", __func__, fdt_get_name(fdtbus_get_data(), fdtbus_phandle2offset(phandle), NULL), cba, pba, cl); 253 #endif 254 255 if (paddr >= cba && paddr < cba + cl) 256 return fdtbus_decode_range(parent, pba) + (paddr - cba); 257 258 len -= (addr_cells + paddr_cells + size_cells) * 4; 259 } 260 261 /* No mapping found */ 262 return paddr; 263 } 264 265 int 266 fdtbus_get_reg_byname(int phandle, const char *name, bus_addr_t *paddr, 267 bus_size_t *psize) 268 { 269 u_int index; 270 int error; 271 272 error = fdtbus_get_index(phandle, "reg-names", name, &index); 273 if (error != 0) 274 return ENOENT; 275 276 return fdtbus_get_reg(phandle, index, paddr, psize); 277 } 278 279 int 280 fdtbus_get_reg(int phandle, u_int index, bus_addr_t *paddr, bus_size_t *psize) 281 { 282 uint64_t addr, size; 283 int error; 284 285 error = fdtbus_get_reg64(phandle, index, &addr, &size); 286 if (error) 287 return error; 288 289 if (sizeof(bus_addr_t) == 4 && (addr + size) > 0x100000000) 290 return ERANGE; 291 292 if (paddr) 293 *paddr = (bus_addr_t)addr; 294 if (psize) 295 *psize = (bus_size_t)size; 296 297 return 0; 298 } 299 300 int 301 fdtbus_get_reg64(int phandle, u_int index, uint64_t *paddr, uint64_t *psize) 302 { 303 uint64_t addr, size; 304 const uint8_t *buf; 305 int len; 306 307 const int addr_cells = fdtbus_get_addr_cells(OF_parent(phandle)); 308 const int size_cells = fdtbus_get_size_cells(OF_parent(phandle)); 309 if (addr_cells == -1 || size_cells == -1) 310 return EINVAL; 311 312 buf = fdt_getprop(fdtbus_get_data(), 313 fdtbus_phandle2offset(phandle), "reg", &len); 314 if (buf == NULL || len <= 0) 315 return EINVAL; 316 317 const u_int reglen = size_cells * 4 + addr_cells * 4; 318 if (reglen == 0) 319 return EINVAL; 320 321 if (index >= len / reglen) 322 return ENXIO; 323 324 buf += index * reglen; 325 addr = fdtbus_get_cells(buf, addr_cells); 326 buf += addr_cells * 4; 327 size = fdtbus_get_cells(buf, size_cells); 328 329 if (paddr) { 330 *paddr = fdtbus_decode_range(OF_parent(phandle), addr); 331 #ifdef FDTBUS_DEBUG 332 const char *name = fdt_get_name(fdtbus_get_data(), 333 fdtbus_phandle2offset(phandle), NULL); 334 printf("fdt: [%s] decoded addr #%u: %" PRIx64 335 " -> %" PRIx64 "\n", name, index, addr, *paddr); 336 #endif 337 } 338 if (psize) 339 *psize = size; 340 341 return 0; 342 } 343 344 bool 345 fdtbus_status_okay(int phandle) 346 { 347 const int off = fdtbus_phandle2offset(phandle); 348 349 const char *prop = fdt_getprop(fdtbus_get_data(), off, "status", NULL); 350 if (prop == NULL) 351 return true; 352 353 return strncmp(prop, "ok", 2) == 0; 354 } 355 356 const void * 357 fdtbus_get_prop(int phandle, const char *prop, int *plen) 358 { 359 const int off = fdtbus_phandle2offset(phandle); 360 361 return fdt_getprop(fdtbus_get_data(), off, prop, plen); 362 } 363 364 const char * 365 fdtbus_get_string(int phandle, const char *prop) 366 { 367 const int off = fdtbus_phandle2offset(phandle); 368 369 if (strcmp(prop, "name") == 0) 370 return fdt_get_name(fdtbus_get_data(), off, NULL); 371 else 372 return fdt_getprop(fdtbus_get_data(), off, prop, NULL); 373 } 374 375 const char * 376 fdtbus_get_string_index(int phandle, const char *prop, u_int index) 377 { 378 const char *names; 379 int len; 380 381 if ((len = OF_getproplen(phandle, prop)) < 0) 382 return NULL; 383 384 names = fdtbus_get_string(phandle, prop); 385 386 return strlist_string(names, len, index); 387 } 388 389 int 390 fdtbus_get_index(int phandle, const char *prop, const char *name, u_int *idx) 391 { 392 const char *p; 393 int len, index; 394 395 p = fdtbus_get_prop(phandle, prop, &len); 396 if (p == NULL || len <= 0) 397 return -1; 398 399 index = strlist_index(p, len, name); 400 if (index == -1) 401 return -1; 402 403 *idx = index; 404 return 0; 405 } 406