1 /* $NetBSD: fdtbus.c,v 1.29 2019/05/25 19:21:34 jmcneill 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.29 2019/05/25 19:21:34 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/device.h> 35 #include <sys/kmem.h> 36 37 #include <sys/bus.h> 38 39 #include <dev/ofw/openfirm.h> 40 41 #include <dev/fdt/fdtvar.h> 42 43 #include <libfdt.h> 44 45 #include "locators.h" 46 47 #define FDT_MAX_PATH 256 48 49 struct fdt_node { 50 device_t n_bus; 51 device_t n_dev; 52 int n_phandle; 53 const char *n_name; 54 struct fdt_attach_args n_faa; 55 cfdata_t n_cf; 56 57 u_int n_order; 58 59 TAILQ_ENTRY(fdt_node) n_nodes; 60 }; 61 62 static TAILQ_HEAD(, fdt_node) fdt_nodes = 63 TAILQ_HEAD_INITIALIZER(fdt_nodes); 64 static bool fdt_need_rescan = false; 65 66 struct fdt_softc { 67 device_t sc_dev; 68 int sc_phandle; 69 struct fdt_attach_args sc_faa; 70 }; 71 72 static int fdt_match(device_t, cfdata_t, void *); 73 static void fdt_attach(device_t, device_t, void *); 74 static int fdt_rescan(device_t, const char *, const int *); 75 static void fdt_childdet(device_t, device_t); 76 77 static int fdt_scan_submatch(device_t, cfdata_t, const int *, void *); 78 static cfdata_t fdt_scan_best(struct fdt_softc *, struct fdt_node *); 79 static void fdt_scan(struct fdt_softc *, int); 80 static void fdt_add_node(struct fdt_node *); 81 static u_int fdt_get_order(int); 82 83 static const char * const fdtbus_compatible[] = 84 { "simple-bus", NULL }; 85 86 CFATTACH_DECL2_NEW(simplebus, sizeof(struct fdt_softc), 87 fdt_match, fdt_attach, NULL, NULL, fdt_rescan, fdt_childdet); 88 89 static int 90 fdt_match(device_t parent, cfdata_t cf, void *aux) 91 { 92 const struct fdt_attach_args *faa = aux; 93 const int phandle = faa->faa_phandle; 94 int match; 95 96 /* Check compatible string */ 97 match = of_match_compatible(phandle, fdtbus_compatible); 98 if (match) 99 return match; 100 101 /* Some nodes have no compatible string */ 102 if (!of_hasprop(phandle, "compatible")) { 103 if (OF_finddevice("/clocks") == phandle) 104 return 1; 105 if (OF_finddevice("/chosen") == phandle) 106 return 1; 107 } 108 109 /* Always match the root node */ 110 return OF_finddevice("/") == phandle; 111 } 112 113 static void 114 fdt_attach(device_t parent, device_t self, void *aux) 115 { 116 struct fdt_softc *sc = device_private(self); 117 const struct fdt_attach_args *faa = aux; 118 const int phandle = faa->faa_phandle; 119 const char *descr; 120 121 sc->sc_dev = self; 122 sc->sc_phandle = phandle; 123 sc->sc_faa = *faa; 124 125 aprint_naive("\n"); 126 127 descr = fdtbus_get_string(phandle, "model"); 128 if (descr) 129 aprint_normal(": %s\n", descr); 130 else 131 aprint_normal("\n"); 132 133 /* Find all child nodes */ 134 fdt_add_bus(self, phandle, &sc->sc_faa); 135 136 /* Only the root bus should scan for devices */ 137 if (OF_finddevice("/") != faa->faa_phandle) 138 return; 139 140 /* Scan devices */ 141 fdt_rescan(self, NULL, NULL); 142 } 143 144 static int 145 fdt_rescan(device_t self, const char *ifattr, const int *locs) 146 { 147 struct fdt_softc *sc = device_private(self); 148 struct fdt_node *node; 149 int pass; 150 151 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) 152 node->n_cf = fdt_scan_best(sc, node); 153 154 pass = 0; 155 fdt_need_rescan = false; 156 do { 157 fdt_scan(sc, pass); 158 if (fdt_need_rescan == true) { 159 pass = 0; 160 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) { 161 if (node->n_dev == NULL) 162 node->n_cf = fdt_scan_best(sc, node); 163 } 164 fdt_need_rescan = false; 165 } else { 166 pass++; 167 } 168 } while (pass <= FDTCF_PASS_DEFAULT); 169 170 return 0; 171 } 172 173 static void 174 fdt_childdet(device_t parent, device_t child) 175 { 176 struct fdt_node *node; 177 178 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) 179 if (node->n_dev == child) { 180 node->n_dev = NULL; 181 break; 182 } 183 } 184 185 static void 186 fdt_init_attach_args(const struct fdt_attach_args *faa_tmpl, struct fdt_node *node, 187 bool quiet, struct fdt_attach_args *faa) 188 { 189 *faa = *faa_tmpl; 190 faa->faa_phandle = node->n_phandle; 191 faa->faa_name = node->n_name; 192 faa->faa_quiet = quiet; 193 } 194 195 static bool 196 fdt_add_bus_stdmatch(void *arg, int child) 197 { 198 return fdtbus_status_okay(child); 199 } 200 201 void 202 fdt_add_bus(device_t bus, const int phandle, struct fdt_attach_args *faa) 203 { 204 fdt_add_bus_match(bus, phandle, faa, fdt_add_bus_stdmatch, NULL); 205 } 206 207 void 208 fdt_add_bus_match(device_t bus, const int phandle, struct fdt_attach_args *faa, 209 bool (*fn)(void *, int), void *fnarg) 210 { 211 int child; 212 213 for (child = OF_child(phandle); child; child = OF_peer(child)) { 214 if (fn && !fn(fnarg, child)) 215 continue; 216 217 fdt_add_child(bus, child, faa, fdt_get_order(child)); 218 } 219 } 220 221 void 222 fdt_add_child(device_t bus, const int child, struct fdt_attach_args *faa, 223 u_int order) 224 { 225 struct fdt_node *node; 226 227 /* Add the node to our device list */ 228 node = kmem_alloc(sizeof(*node), KM_SLEEP); 229 node->n_bus = bus; 230 node->n_dev = NULL; 231 node->n_phandle = child; 232 node->n_name = fdtbus_get_string(child, "name"); 233 node->n_order = order; 234 node->n_faa = *faa; 235 node->n_faa.faa_phandle = child; 236 node->n_faa.faa_name = node->n_name; 237 238 fdt_add_node(node); 239 fdt_need_rescan = true; 240 } 241 242 static int 243 fdt_scan_submatch(device_t parent, cfdata_t cf, const int *locs, void *aux) 244 { 245 if (locs[FDTCF_PASS] != FDTCF_PASS_DEFAULT && 246 locs[FDTCF_PASS] != cf->cf_loc[FDTCF_PASS]) 247 return 0; 248 249 return config_stdsubmatch(parent, cf, locs, aux); 250 } 251 252 static cfdata_t 253 fdt_scan_best(struct fdt_softc *sc, struct fdt_node *node) 254 { 255 struct fdt_attach_args faa; 256 cfdata_t cf, best_cf; 257 int match, best_match; 258 259 best_cf = NULL; 260 best_match = 0; 261 262 for (int pass = 0; pass <= FDTCF_PASS_DEFAULT; pass++) { 263 const int locs[FDTCF_NLOCS] = { 264 [FDTCF_PASS] = pass 265 }; 266 fdt_init_attach_args(&sc->sc_faa, node, true, &faa); 267 cf = config_search_loc(fdt_scan_submatch, node->n_bus, "fdt", locs, &faa); 268 if (cf == NULL) 269 continue; 270 match = config_match(node->n_bus, cf, &faa); 271 if (match > best_match) { 272 best_match = match; 273 best_cf = cf; 274 } 275 } 276 277 return best_cf; 278 } 279 280 static void 281 fdt_scan(struct fdt_softc *sc, int pass) 282 { 283 struct fdt_node *node; 284 struct fdt_attach_args faa; 285 const int locs[FDTCF_NLOCS] = { 286 [FDTCF_PASS] = pass 287 }; 288 bool quiet = pass != FDTCF_PASS_DEFAULT; 289 prop_dictionary_t dict; 290 char buf[FDT_MAX_PATH]; 291 292 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) { 293 if (node->n_dev != NULL) 294 continue; 295 296 fdt_init_attach_args(&sc->sc_faa, node, quiet, &faa); 297 298 if (quiet) { 299 /* 300 * No match for this device, skip it. 301 */ 302 if (node->n_cf == NULL) 303 continue; 304 305 /* 306 * Make sure we don't attach before a better match in a later pass. 307 */ 308 cfdata_t cf_pass = 309 config_search_loc(fdt_scan_submatch, node->n_bus, "fdt", locs, &faa); 310 if (node->n_cf != cf_pass) 311 continue; 312 313 /* 314 * Attach the device. 315 */ 316 node->n_dev = config_attach_loc(node->n_bus, cf_pass, locs, 317 &faa, fdtbus_print); 318 } else { 319 /* 320 * Default pass. 321 */ 322 node->n_dev = config_found_sm_loc(node->n_bus, "fdt", locs, 323 &faa, fdtbus_print, fdt_scan_submatch); 324 } 325 326 if (node->n_dev) { 327 dict = device_properties(node->n_dev); 328 if (fdtbus_get_path(node->n_phandle, buf, sizeof(buf))) 329 prop_dictionary_set_cstring(dict, "fdt-path", buf); 330 } 331 } 332 } 333 334 static void 335 fdt_add_node(struct fdt_node *new_node) 336 { 337 struct fdt_node *node; 338 339 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) 340 if (node->n_order > new_node->n_order) { 341 TAILQ_INSERT_BEFORE(node, new_node, n_nodes); 342 return; 343 } 344 TAILQ_INSERT_TAIL(&fdt_nodes, new_node, n_nodes); 345 } 346 347 void 348 fdt_remove_byhandle(int phandle) 349 { 350 struct fdt_node *node; 351 352 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) { 353 if (node->n_phandle == phandle) { 354 TAILQ_REMOVE(&fdt_nodes, node, n_nodes); 355 return; 356 } 357 } 358 } 359 360 void 361 fdt_remove_bycompat(const char *compatible[]) 362 { 363 struct fdt_node *node, *next; 364 365 TAILQ_FOREACH_SAFE(node, &fdt_nodes, n_nodes, next) { 366 if (of_match_compatible(node->n_phandle, compatible)) { 367 TAILQ_REMOVE(&fdt_nodes, node, n_nodes); 368 } 369 } 370 } 371 372 int 373 fdt_find_with_property(const char *prop, int *pindex) 374 { 375 struct fdt_node *node; 376 int index = 0; 377 378 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) { 379 if (index++ < *pindex) 380 continue; 381 if (of_hasprop(node->n_phandle, prop)) { 382 *pindex = index; 383 return node->n_phandle; 384 } 385 } 386 387 return -1; 388 } 389 390 static u_int 391 fdt_get_order(int phandle) 392 { 393 u_int val = UINT_MAX; 394 int child; 395 396 of_getprop_uint32(phandle, "phandle", &val); 397 398 for (child = OF_child(phandle); child; child = OF_peer(child)) { 399 u_int child_val = fdt_get_order(child); 400 if (child_val < val) 401 val = child_val; 402 } 403 404 return val; 405 } 406 407 int 408 fdtbus_print(void *aux, const char *pnp) 409 { 410 const struct fdt_attach_args * const faa = aux; 411 char buf[FDT_MAX_PATH]; 412 const char *name = buf; 413 int len; 414 415 if (pnp && faa->faa_quiet) 416 return QUIET; 417 418 /* Skip "not configured" for nodes w/o compatible property */ 419 if (pnp && OF_getproplen(faa->faa_phandle, "compatible") <= 0) 420 return QUIET; 421 422 if (!fdtbus_get_path(faa->faa_phandle, buf, sizeof(buf))) 423 name = faa->faa_name; 424 425 if (pnp) { 426 aprint_normal("%s at %s", name, pnp); 427 const char *compat = fdt_getprop(fdtbus_get_data(), 428 fdtbus_phandle2offset(faa->faa_phandle), "compatible", 429 &len); 430 while (len > 0) { 431 aprint_debug(" <%s>", compat); 432 len -= (strlen(compat) + 1); 433 compat += (strlen(compat) + 1); 434 } 435 } else 436 aprint_debug(" (%s)", name); 437 438 return UNCONF; 439 } 440