1 /* $NetBSD: fdtbus.c,v 1.34 2020/06/11 02:39:30 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2015 Jared D. McNeill <jmcneill@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: fdtbus.c,v 1.34 2020/06/11 02:39:30 thorpej Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/device.h> 35 #include <sys/kmem.h> 36 #include <sys/cpu.h> 37 38 #include <sys/bus.h> 39 40 #include <dev/ofw/openfirm.h> 41 42 #include <dev/fdt/fdtvar.h> 43 44 #include <libfdt.h> 45 46 #include "locators.h" 47 48 #define FDT_MAX_PATH 256 49 50 struct fdt_node { 51 device_t n_bus; 52 device_t n_dev; 53 int n_phandle; 54 const char *n_name; 55 struct fdt_attach_args n_faa; 56 cfdata_t n_cf; 57 58 u_int n_order; 59 60 bool n_pinctrl_init; 61 62 TAILQ_ENTRY(fdt_node) n_nodes; 63 }; 64 65 static TAILQ_HEAD(, fdt_node) fdt_nodes = 66 TAILQ_HEAD_INITIALIZER(fdt_nodes); 67 static bool fdt_need_rescan = false; 68 69 struct fdt_softc { 70 device_t sc_dev; 71 int sc_phandle; 72 struct fdt_attach_args sc_faa; 73 }; 74 75 static int fdt_match(device_t, cfdata_t, void *); 76 static void fdt_attach(device_t, device_t, void *); 77 static int fdt_rescan(device_t, const char *, const int *); 78 static void fdt_childdet(device_t, device_t); 79 80 static int fdt_scan_submatch(device_t, cfdata_t, const int *, void *); 81 static cfdata_t fdt_scan_best(struct fdt_softc *, struct fdt_node *); 82 static void fdt_scan(struct fdt_softc *, int); 83 static void fdt_add_node(struct fdt_node *); 84 static u_int fdt_get_order(int); 85 static void fdt_pre_attach(struct fdt_node *); 86 static void fdt_post_attach(struct fdt_node *); 87 88 static const char * const fdtbus_compatible[] = 89 { "simple-bus", NULL }; 90 91 CFATTACH_DECL2_NEW(simplebus, sizeof(struct fdt_softc), 92 fdt_match, fdt_attach, NULL, NULL, fdt_rescan, fdt_childdet); 93 94 static int 95 fdt_match(device_t parent, cfdata_t cf, void *aux) 96 { 97 const struct fdt_attach_args *faa = aux; 98 const int phandle = faa->faa_phandle; 99 int match; 100 101 /* Check compatible string */ 102 match = of_match_compatible(phandle, fdtbus_compatible); 103 if (match) 104 return match; 105 106 /* Some nodes have no compatible string */ 107 if (!of_hasprop(phandle, "compatible")) { 108 if (OF_finddevice("/clocks") == phandle) 109 return 1; 110 if (OF_finddevice("/chosen") == phandle) 111 return 1; 112 } 113 114 /* Always match the root node */ 115 return OF_finddevice("/") == phandle; 116 } 117 118 static void 119 fdt_attach(device_t parent, device_t self, void *aux) 120 { 121 struct fdt_softc *sc = device_private(self); 122 const struct fdt_attach_args *faa = aux; 123 const int phandle = faa->faa_phandle; 124 const char *descr, *model; 125 126 sc->sc_dev = self; 127 sc->sc_phandle = phandle; 128 sc->sc_faa = *faa; 129 130 aprint_naive("\n"); 131 132 descr = fdtbus_get_string(phandle, "model"); 133 if (descr) 134 aprint_normal(": %s\n", descr); 135 else 136 aprint_normal("\n"); 137 138 /* Find all child nodes */ 139 fdt_add_bus(self, phandle, &sc->sc_faa); 140 141 /* Only the root bus should scan for devices */ 142 if (OF_finddevice("/") != faa->faa_phandle) 143 return; 144 145 /* Set hw.model if available */ 146 model = fdtbus_get_string(phandle, "compatible"); 147 if (model) 148 cpu_setmodel("%s", model); 149 else if (descr) 150 cpu_setmodel("%s", descr); 151 152 /* Scan devices */ 153 fdt_rescan(self, NULL, NULL); 154 } 155 156 static int 157 fdt_rescan(device_t self, const char *ifattr, const int *locs) 158 { 159 struct fdt_softc *sc = device_private(self); 160 struct fdt_node *node; 161 int pass; 162 163 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) 164 node->n_cf = fdt_scan_best(sc, node); 165 166 pass = 0; 167 fdt_need_rescan = false; 168 do { 169 fdt_scan(sc, pass); 170 if (fdt_need_rescan == true) { 171 pass = 0; 172 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) { 173 if (node->n_dev == NULL) 174 node->n_cf = fdt_scan_best(sc, node); 175 } 176 fdt_need_rescan = false; 177 } else { 178 pass++; 179 } 180 } while (pass <= FDTCF_PASS_DEFAULT); 181 182 return 0; 183 } 184 185 static void 186 fdt_childdet(device_t parent, device_t child) 187 { 188 struct fdt_node *node; 189 190 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) 191 if (node->n_dev == child) { 192 node->n_dev = NULL; 193 break; 194 } 195 } 196 197 static void 198 fdt_init_attach_args(const struct fdt_attach_args *faa_tmpl, struct fdt_node *node, 199 bool quiet, struct fdt_attach_args *faa) 200 { 201 *faa = *faa_tmpl; 202 faa->faa_phandle = node->n_phandle; 203 faa->faa_name = node->n_name; 204 faa->faa_quiet = quiet; 205 faa->faa_dmat = node->n_faa.faa_dmat; 206 } 207 208 static bool 209 fdt_add_bus_stdmatch(void *arg, int child) 210 { 211 return fdtbus_status_okay(child); 212 } 213 214 void 215 fdt_add_bus(device_t bus, const int phandle, struct fdt_attach_args *faa) 216 { 217 fdt_add_bus_match(bus, phandle, faa, fdt_add_bus_stdmatch, NULL); 218 } 219 220 void 221 fdt_add_bus_match(device_t bus, const int phandle, struct fdt_attach_args *faa, 222 bool (*fn)(void *, int), void *fnarg) 223 { 224 int child; 225 226 for (child = OF_child(phandle); child; child = OF_peer(child)) { 227 if (fn && !fn(fnarg, child)) 228 continue; 229 230 fdt_add_child(bus, child, faa, fdt_get_order(child)); 231 } 232 } 233 234 static int 235 fdt_dma_translate(int phandle, struct fdt_dma_range **ranges, u_int *nranges) 236 { 237 const uint8_t *data; 238 int len, n; 239 240 const int parent = OF_parent(phandle); 241 if (parent == -1) 242 return 1; /* done searching */ 243 244 data = fdtbus_get_prop(phandle, "dma-ranges", &len); 245 if (data == NULL) 246 return 1; /* no dma-ranges property, stop searching */ 247 248 if (len == 0) 249 return 0; /* dma-ranges property is empty, keep going */ 250 251 const int addr_cells = fdtbus_get_addr_cells(phandle); 252 const int size_cells = fdtbus_get_size_cells(phandle); 253 const int paddr_cells = fdtbus_get_addr_cells(parent); 254 if (addr_cells == -1 || size_cells == -1 || paddr_cells == -1) 255 return 1; 256 257 const int entry_size = (addr_cells + paddr_cells + size_cells) * 4; 258 259 *nranges = len / entry_size; 260 *ranges = kmem_alloc(sizeof(struct fdt_dma_range) * *nranges, KM_SLEEP); 261 for (n = 0; len >= entry_size; n++, len -= entry_size) { 262 const uint64_t cba = fdtbus_get_cells(data, addr_cells); 263 data += addr_cells * 4; 264 const uint64_t pba = fdtbus_get_cells(data, paddr_cells); 265 data += paddr_cells * 4; 266 const uint64_t cl = fdtbus_get_cells(data, size_cells); 267 data += size_cells * 4; 268 269 (*ranges)[n].dr_sysbase = pba; 270 (*ranges)[n].dr_busbase = cba; 271 (*ranges)[n].dr_len = cl; 272 } 273 274 return 1; 275 } 276 277 static bus_dma_tag_t 278 fdt_get_dma_tag(struct fdt_node *node) 279 { 280 struct fdt_dma_range *ranges = NULL; 281 u_int nranges = 0; 282 int parent; 283 284 parent = OF_parent(node->n_phandle); 285 while (parent != -1) { 286 if (fdt_dma_translate(parent, &ranges, &nranges) != 0) 287 break; 288 parent = OF_parent(parent); 289 } 290 291 return fdtbus_dma_tag_create(node->n_phandle, ranges, nranges); 292 } 293 294 void 295 fdt_add_child(device_t bus, const int child, struct fdt_attach_args *faa, 296 u_int order) 297 { 298 struct fdt_node *node; 299 300 /* Add the node to our device list */ 301 node = kmem_alloc(sizeof(*node), KM_SLEEP); 302 node->n_bus = bus; 303 node->n_dev = NULL; 304 node->n_phandle = child; 305 node->n_name = fdtbus_get_string(child, "name"); 306 node->n_order = order; 307 node->n_faa = *faa; 308 node->n_faa.faa_phandle = child; 309 node->n_faa.faa_name = node->n_name; 310 node->n_faa.faa_dmat = fdt_get_dma_tag(node); 311 312 fdt_add_node(node); 313 fdt_need_rescan = true; 314 } 315 316 static int 317 fdt_scan_submatch(device_t parent, cfdata_t cf, const int *locs, void *aux) 318 { 319 if (locs[FDTCF_PASS] != FDTCF_PASS_DEFAULT && 320 locs[FDTCF_PASS] != cf->cf_loc[FDTCF_PASS]) 321 return 0; 322 323 return config_stdsubmatch(parent, cf, locs, aux); 324 } 325 326 static cfdata_t 327 fdt_scan_best(struct fdt_softc *sc, struct fdt_node *node) 328 { 329 struct fdt_attach_args faa; 330 cfdata_t cf, best_cf; 331 int match, best_match; 332 333 best_cf = NULL; 334 best_match = 0; 335 336 for (int pass = 0; pass <= FDTCF_PASS_DEFAULT; pass++) { 337 const int locs[FDTCF_NLOCS] = { 338 [FDTCF_PASS] = pass 339 }; 340 fdt_init_attach_args(&sc->sc_faa, node, true, &faa); 341 cf = config_search_loc(fdt_scan_submatch, node->n_bus, "fdt", locs, &faa); 342 if (cf == NULL) 343 continue; 344 match = config_match(node->n_bus, cf, &faa); 345 if (match > best_match) { 346 best_match = match; 347 best_cf = cf; 348 } 349 } 350 351 return best_cf; 352 } 353 354 static void 355 fdt_scan(struct fdt_softc *sc, int pass) 356 { 357 struct fdt_node *node; 358 struct fdt_attach_args faa; 359 const int locs[FDTCF_NLOCS] = { 360 [FDTCF_PASS] = pass 361 }; 362 bool quiet = pass != FDTCF_PASS_DEFAULT; 363 prop_dictionary_t dict; 364 char buf[FDT_MAX_PATH]; 365 366 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) { 367 if (node->n_dev != NULL) 368 continue; 369 370 fdt_init_attach_args(&sc->sc_faa, node, quiet, &faa); 371 372 if (quiet) { 373 /* 374 * No match for this device, skip it. 375 */ 376 if (node->n_cf == NULL) 377 continue; 378 379 /* 380 * Make sure we don't attach before a better match in a later pass. 381 */ 382 cfdata_t cf_pass = 383 config_search_loc(fdt_scan_submatch, node->n_bus, "fdt", locs, &faa); 384 if (node->n_cf != cf_pass) 385 continue; 386 387 /* 388 * Attach the device. 389 */ 390 fdt_pre_attach(node); 391 node->n_dev = config_attach_loc(node->n_bus, cf_pass, locs, 392 &faa, fdtbus_print); 393 if (node->n_dev != NULL) 394 fdt_post_attach(node); 395 } else { 396 /* 397 * Default pass. 398 */ 399 fdt_pre_attach(node); 400 node->n_dev = config_found_sm_loc(node->n_bus, "fdt", locs, 401 &faa, fdtbus_print, fdt_scan_submatch); 402 if (node->n_dev != NULL) 403 fdt_post_attach(node); 404 } 405 406 if (node->n_dev) { 407 dict = device_properties(node->n_dev); 408 if (fdtbus_get_path(node->n_phandle, buf, sizeof(buf))) 409 prop_dictionary_set_string(dict, "fdt-path", buf); 410 } 411 } 412 } 413 414 static void 415 fdt_pre_attach(struct fdt_node *node) 416 { 417 const char *cfgname; 418 int error; 419 420 node->n_pinctrl_init = fdtbus_pinctrl_has_config(node->n_phandle, "init"); 421 422 cfgname = node->n_pinctrl_init ? "init" : "default"; 423 424 aprint_debug_dev(node->n_bus, "set %s config for %s\n", cfgname, node->n_name); 425 426 error = fdtbus_pinctrl_set_config(node->n_phandle, cfgname); 427 if (error != 0 && error != ENOENT) 428 aprint_debug_dev(node->n_bus, 429 "failed to set %s config on %s: %d\n", 430 cfgname, node->n_name, error); 431 } 432 433 static void 434 fdt_post_attach(struct fdt_node *node) 435 { 436 int error; 437 438 if (node->n_pinctrl_init) { 439 aprint_debug_dev(node->n_bus, "set default config for %s\n", node->n_name); 440 error = fdtbus_pinctrl_set_config(node->n_phandle, "default"); 441 if (error != 0 && error != ENOENT) 442 aprint_debug_dev(node->n_bus, 443 "failed to set default config on %s: %d\n", 444 node->n_name, error); 445 } 446 } 447 448 static void 449 fdt_add_node(struct fdt_node *new_node) 450 { 451 struct fdt_node *node; 452 453 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) 454 if (node->n_order > new_node->n_order) { 455 TAILQ_INSERT_BEFORE(node, new_node, n_nodes); 456 return; 457 } 458 TAILQ_INSERT_TAIL(&fdt_nodes, new_node, n_nodes); 459 } 460 461 void 462 fdt_remove_byhandle(int phandle) 463 { 464 struct fdt_node *node; 465 466 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) { 467 if (node->n_phandle == phandle) { 468 TAILQ_REMOVE(&fdt_nodes, node, n_nodes); 469 return; 470 } 471 } 472 } 473 474 void 475 fdt_remove_bycompat(const char *compatible[]) 476 { 477 struct fdt_node *node, *next; 478 479 TAILQ_FOREACH_SAFE(node, &fdt_nodes, n_nodes, next) { 480 if (of_match_compatible(node->n_phandle, compatible)) { 481 TAILQ_REMOVE(&fdt_nodes, node, n_nodes); 482 } 483 } 484 } 485 486 int 487 fdt_find_with_property(const char *prop, int *pindex) 488 { 489 struct fdt_node *node; 490 int index = 0; 491 492 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) { 493 if (index++ < *pindex) 494 continue; 495 if (of_hasprop(node->n_phandle, prop)) { 496 *pindex = index; 497 return node->n_phandle; 498 } 499 } 500 501 return -1; 502 } 503 504 static u_int 505 fdt_get_order(int phandle) 506 { 507 u_int val = UINT_MAX; 508 int child; 509 510 of_getprop_uint32(phandle, "phandle", &val); 511 512 for (child = OF_child(phandle); child; child = OF_peer(child)) { 513 u_int child_val = fdt_get_order(child); 514 if (child_val < val) 515 val = child_val; 516 } 517 518 return val; 519 } 520 521 int 522 fdtbus_print(void *aux, const char *pnp) 523 { 524 const struct fdt_attach_args * const faa = aux; 525 char buf[FDT_MAX_PATH]; 526 const char *name = buf; 527 int len; 528 529 if (pnp && faa->faa_quiet) 530 return QUIET; 531 532 /* Skip "not configured" for nodes w/o compatible property */ 533 if (pnp && OF_getproplen(faa->faa_phandle, "compatible") <= 0) 534 return QUIET; 535 536 if (!fdtbus_get_path(faa->faa_phandle, buf, sizeof(buf))) 537 name = faa->faa_name; 538 539 if (pnp) { 540 aprint_normal("%s at %s", name, pnp); 541 const char *compat = fdt_getprop(fdtbus_get_data(), 542 fdtbus_phandle2offset(faa->faa_phandle), "compatible", 543 &len); 544 while (len > 0) { 545 aprint_debug(" <%s>", compat); 546 len -= (strlen(compat) + 1); 547 compat += (strlen(compat) + 1); 548 } 549 } else 550 aprint_debug(" (%s)", name); 551 552 return UNCONF; 553 } 554