1 /* $NetBSD: fdt_subr.c,v 1.41 2025/09/06 22:53:49 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.41 2025/09/06 22:53:49 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 int 97 fdtbus_get_addr_cells(int phandle) 98 { 99 uint32_t addr_cells; 100 101 if (of_getprop_uint32(phandle, "#address-cells", &addr_cells)) 102 addr_cells = 2; 103 104 return addr_cells; 105 } 106 107 int 108 fdtbus_get_size_cells(int phandle) 109 { 110 uint32_t size_cells; 111 112 if (of_getprop_uint32(phandle, "#size-cells", &size_cells)) 113 size_cells = 0; 114 115 return size_cells; 116 } 117 118 int 119 fdtbus_get_phandle(int phandle, const char *prop) 120 { 121 u_int phandle_ref; 122 const u_int *buf; 123 int len; 124 125 buf = fdt_getprop(fdtbus_get_data(), 126 fdtbus_phandle2offset(phandle), prop, &len); 127 if (buf == NULL || len < sizeof(phandle_ref)) 128 return -1; 129 130 phandle_ref = be32dec(buf); 131 132 return fdtbus_get_phandle_from_native(phandle_ref); 133 } 134 135 int 136 fdtbus_get_phandle_with_data(int phandle, const char *prop, const char *cells, 137 int index, struct fdt_phandle_data *data) 138 { 139 int len; 140 const int offset = 1; 141 142 const u_int *p = fdtbus_get_prop(phandle, prop, &len); 143 if (p == NULL || len <= 0) 144 return EINVAL; 145 146 for (int i = 0; len > 0; i++) { 147 u_int phandle_ref = be32toh(*p); 148 const u_int iparent = fdtbus_get_phandle_from_native(phandle_ref); 149 uint32_t cells_num; 150 of_getprop_uint32(iparent, cells, &cells_num); 151 152 if (index == i) { 153 if (data != NULL) { 154 data->phandle = iparent; 155 data->count = cells_num; 156 data->values = p + offset; 157 } 158 goto done; 159 } 160 161 const u_int reclen = offset + cells_num; 162 len -= reclen * sizeof(u_int); 163 p += reclen; 164 } 165 return EINVAL; 166 167 done: 168 return 0; 169 } 170 171 int 172 fdtbus_get_phandle_from_native(int phandle) 173 { 174 const int off = fdt_node_offset_by_phandle(fdt_data, phandle); 175 if (off < 0) { 176 return -1; 177 } 178 return fdtbus_offset2phandle(off); 179 } 180 181 bool 182 fdtbus_get_path(int phandle, char *buf, size_t buflen) 183 { 184 const int off = fdtbus_phandle2offset(phandle); 185 if (off < 0) { 186 return false; 187 } 188 if (fdt_get_path(fdt_data, off, buf, (int)buflen) != 0) { 189 return false; 190 } 191 return true; 192 } 193 194 uint64_t 195 fdtbus_get_cells(const uint8_t *buf, int cells) 196 { 197 switch (cells) { 198 case 0: return 0; 199 case 1: return be32dec(buf); 200 case 2: return ((uint64_t)be32dec(buf)<<32)|be32dec(buf+4); 201 default: panic("fdtbus_get_cells: bad cells val %d\n", cells); 202 } 203 } 204 205 static uint64_t 206 fdtbus_decode_range(int phandle, uint64_t paddr) 207 { 208 const int parent = OF_parent(phandle); 209 if (parent == -1) 210 return paddr; 211 212 if (!fdtbus_decoderegprop) 213 return paddr; 214 215 const uint8_t *buf; 216 int len; 217 218 buf = fdt_getprop(fdtbus_get_data(), 219 fdtbus_phandle2offset(phandle), "ranges", &len); 220 if (buf == NULL) 221 return paddr; 222 223 if (len == 0) { 224 /* pass through to parent */ 225 return fdtbus_decode_range(parent, paddr); 226 } 227 228 const int addr_cells = fdtbus_get_addr_cells(phandle); 229 const int size_cells = fdtbus_get_size_cells(phandle); 230 const int paddr_cells = fdtbus_get_addr_cells(parent); 231 if (addr_cells == -1 || size_cells == -1 || paddr_cells == -1) 232 return paddr; 233 234 while (len > 0) { 235 uint64_t cba, pba, cl; 236 cba = fdtbus_get_cells(buf, addr_cells); 237 buf += addr_cells * 4; 238 pba = fdtbus_get_cells(buf, paddr_cells); 239 buf += paddr_cells * 4; 240 cl = fdtbus_get_cells(buf, size_cells); 241 buf += size_cells * 4; 242 243 #ifdef FDTBUS_DEBUG 244 printf("%s: %s: cba=%#" PRIx64 ", pba=%#" PRIx64 ", cl=%#" PRIx64 "\n", __func__, fdt_get_name(fdtbus_get_data(), fdtbus_phandle2offset(phandle), NULL), cba, pba, cl); 245 #endif 246 247 if (paddr >= cba && paddr < cba + cl) 248 return fdtbus_decode_range(parent, pba) + (paddr - cba); 249 250 len -= (addr_cells + paddr_cells + size_cells) * 4; 251 } 252 253 /* No mapping found */ 254 return paddr; 255 } 256 257 int 258 fdtbus_get_reg_byname(int phandle, const char *name, bus_addr_t *paddr, 259 bus_size_t *psize) 260 { 261 u_int index; 262 int error; 263 264 error = fdtbus_get_index(phandle, "reg-names", name, &index); 265 if (error != 0) 266 return ENOENT; 267 268 return fdtbus_get_reg(phandle, index, paddr, psize); 269 } 270 271 int 272 fdtbus_get_reg(int phandle, u_int index, bus_addr_t *paddr, bus_size_t *psize) 273 { 274 uint64_t addr, size; 275 int error; 276 277 error = fdtbus_get_reg64(phandle, index, &addr, &size); 278 if (error) 279 return error; 280 281 if (sizeof(bus_addr_t) == 4 && (addr + size) > 0x100000000) 282 return ERANGE; 283 284 if (paddr) 285 *paddr = (bus_addr_t)addr; 286 if (psize) 287 *psize = (bus_size_t)size; 288 289 return 0; 290 } 291 292 int 293 fdtbus_get_reg64(int phandle, u_int index, uint64_t *paddr, uint64_t *psize) 294 { 295 uint64_t addr, size; 296 const uint8_t *buf; 297 int len; 298 299 const int addr_cells = fdtbus_get_addr_cells(OF_parent(phandle)); 300 const int size_cells = fdtbus_get_size_cells(OF_parent(phandle)); 301 if (addr_cells == -1 || size_cells == -1) 302 return EINVAL; 303 304 buf = fdt_getprop(fdtbus_get_data(), 305 fdtbus_phandle2offset(phandle), "reg", &len); 306 if (buf == NULL || len <= 0) 307 return EINVAL; 308 309 const u_int reglen = size_cells * 4 + addr_cells * 4; 310 if (reglen == 0) 311 return EINVAL; 312 313 if (index >= len / reglen) 314 return ENXIO; 315 316 buf += index * reglen; 317 addr = fdtbus_get_cells(buf, addr_cells); 318 buf += addr_cells * 4; 319 size = fdtbus_get_cells(buf, size_cells); 320 321 if (paddr) { 322 *paddr = fdtbus_decode_range(OF_parent(phandle), addr); 323 #ifdef FDTBUS_DEBUG 324 const char *name = fdt_get_name(fdtbus_get_data(), 325 fdtbus_phandle2offset(phandle), NULL); 326 printf("fdt: [%s] decoded addr #%u: %" PRIx64 327 " -> %" PRIx64 "\n", name, index, addr, *paddr); 328 #endif 329 } 330 if (psize) 331 *psize = size; 332 333 return 0; 334 } 335 336 bool 337 fdtbus_status_okay(int phandle) 338 { 339 const int off = fdtbus_phandle2offset(phandle); 340 341 const char *prop = fdt_getprop(fdtbus_get_data(), off, "status", NULL); 342 if (prop == NULL) 343 return true; 344 345 return strncmp(prop, "ok", 2) == 0; 346 } 347 348 const void * 349 fdtbus_get_prop(int phandle, const char *prop, int *plen) 350 { 351 const int off = fdtbus_phandle2offset(phandle); 352 353 return fdt_getprop(fdtbus_get_data(), off, prop, plen); 354 } 355 356 const char * 357 fdtbus_get_string(int phandle, const char *prop) 358 { 359 const int off = fdtbus_phandle2offset(phandle); 360 361 if (strcmp(prop, "name") == 0) 362 return fdt_get_name(fdtbus_get_data(), off, NULL); 363 else 364 return fdt_getprop(fdtbus_get_data(), off, prop, NULL); 365 } 366 367 const char * 368 fdtbus_get_string_index(int phandle, const char *prop, u_int index) 369 { 370 const char *names; 371 int len; 372 373 if ((len = OF_getproplen(phandle, prop)) < 0) 374 return NULL; 375 376 names = fdtbus_get_string(phandle, prop); 377 378 return strlist_string(names, len, index); 379 } 380 381 int 382 fdtbus_get_index(int phandle, const char *prop, const char *name, u_int *idx) 383 { 384 const char *p; 385 int len, index; 386 387 p = fdtbus_get_prop(phandle, prop, &len); 388 if (p == NULL || len <= 0) 389 return -1; 390 391 index = strlist_index(p, len, name); 392 if (index == -1) 393 return -1; 394 395 *idx = index; 396 return 0; 397 } 398