Index: sys/conf/options =================================================================== --- sys/conf/options +++ sys/conf/options @@ -961,6 +961,11 @@ # Flattened device tree options FDT opt_platform.h FDT_DTB_STATIC opt_platform.h +# Open Firmware +# Enable automatic ordering by device dependency. +OFWBUS_AUTODEP opt_ofw.h +# Quirk to allow to auto-ordered attach of generic devices. +OFWBUS_AUTODEP_BUS_PASS_OVERRIDE opt_ofw.h # OFED Infiniband stack OFED opt_ofed.h Index: sys/dev/ofw/ofw_bus_subr.h =================================================================== --- sys/dev/ofw/ofw_bus_subr.h +++ sys/dev/ofw/ofw_bus_subr.h @@ -34,12 +34,14 @@ #define _DEV_OFW_OFW_BUS_SUBR_H_ #include +#include #ifdef INTRNG #include #endif #include #include "ofw_bus_if.h" +#include "opt_ofw.h" #define ORIP_NOINT -1 #define ORIR_NOTFOUND 0xffffffff @@ -64,6 +66,27 @@ pcell_t cells[]; }; #endif + +#ifdef OFWBUS_AUTODEP +struct ofwbus_static_req_props { + char *ofwbus_srp_prop; + char *ofwbus_srp_propcells; + int ofwbus_srp_flags; +#define OFWBUS_REQ_FIND_ATTPARENT 1 /* Traverse parents up to that + having "compatible" prop. */ +#define OFWBUS_REQ_FIND_PROP 2 /* Search for prop with link. */ +}; + +SET_DECLARE(ofwbus_static_req_props_set, struct ofwbus_static_req_props); +#define OFWBUS_STATIC_REQ_PROP(r) DATA_SET(ofwbus_static_req_props_set, r) + +#ifdef OFWBUS_AUTODEP_BUS_PASS_OVERRIDE +#undef BUS_PASS_TIMER +#define BUS_PASS_TIMER BUS_PASS_DEFAULT +#undef BUS_PASS_ORDER_MIDDLE +#define BUS_PASS_ORDER_MIDDLE 0 +#endif /* OFWBUS_AUTODEP_BUS_PASS_OVERRIDE */ +#endif /* OFWBUS_AUTODEP */ #define FDTCOMPAT_PNP_DESCR "Z:compat;P:#;" #define FDTCOMPAT_PNP_INFO(t, busname) \ Index: sys/dev/ofw/ofwbus.c =================================================================== --- sys/dev/ofw/ofwbus.c +++ sys/dev/ofw/ofwbus.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,8 @@ #include #include +#include "opt_ofw.h" + /* * The ofwbus (which is a pseudo-bus actually) iterates over the nodes that * hang from the Open Firmware root node and adds them as devices to this bus @@ -70,6 +73,9 @@ struct simplebus_softc simplebus_sc; struct rman sc_intr_rman; struct rman sc_mem_rman; +#ifdef OFWBUS_AUTODEP + TAILQ_HEAD(, ofwbus_dep_entry) sc_dep_head; +#endif /* OFWBUS_AUTODEP */ }; #ifndef __aarch64__ @@ -97,6 +103,16 @@ DEVMETHOD_END }; +#ifdef OFWBUS_AUTODEP +struct ofwbus_dep_entry { + TAILQ_ENTRY(ofwbus_dep_entry) entries; + phandle_t pnode; + phandle_t node; + phandle_t rnode; + int flags; +}; +#endif /* OFWBUS_AUTODEP */ + DEFINE_CLASS_1(ofwbus, ofwbus_driver, ofwbus_methods, sizeof(struct ofwbus_softc), simplebus_driver); static devclass_t ofwbus_devclass; @@ -131,28 +147,184 @@ return (BUS_PROBE_NOWILDCARD); } +#ifdef OFWBUS_AUTODEP static int +__topmost_req(struct ofwbus_softc *sc, phandle_t node, phandle_t *cur_top_node, + int cur_top) +{ + struct ofwbus_dep_entry *np; + int i; + + i = 0; + TAILQ_FOREACH(np, &sc->sc_dep_head, entries) { + if (node == np->node) { + *cur_top_node = np->node; + return (i); + } + i ++; + /* + * if current position more than cur_top, left cur_top_node + * untouched. + */ + if (i >= cur_top) + return (cur_top); + } + + return (i); +} + +static int +ofwbus_device_require(struct ofwbus_softc *sc, phandle_t pnode, phandle_t node, + phandle_t rnode, int flags) +{ + struct ofwbus_dep_entry *n1, *np; + phandle_t requiring; + int top; + + requiring = 0; + top = INT_MAX; + n1 = malloc(sizeof(struct ofwbus_dep_entry), M_TEMP, M_WAITOK); + n1->pnode = pnode; + n1->node = node; + n1->rnode = rnode; + n1->flags = flags; + + /* 0 mean default requirement of device by bus. */ + if (rnode != 0) { + TAILQ_INSERT_TAIL(&sc->sc_dep_head, n1, entries); + return (0); + } + /* Find device that require me. */ + TAILQ_FOREACH(np, &sc->sc_dep_head, entries) + if (node == np->rnode) { + top = __topmost_req(sc, np->node, &requiring, top); + } + /* Requiring node not found, insert at tail. */ + if (requiring == 0) { + TAILQ_INSERT_TAIL(&sc->sc_dep_head, n1, entries); + return (0); + } + /* Now find top record of requiring device. */ + TAILQ_FOREACH(np, &sc->sc_dep_head, entries) + if (requiring == np->node) { + TAILQ_INSERT_BEFORE(np, n1, entries); + break; + } + return (0); +} + +static int +ofwbus_reg_std_prop_req(struct ofwbus_softc *sc, phandle_t pnode, phandle_t + node, char *prop, char *propcells, int flags) +{ + phandle_t p, xref; + pcell_t *cells; + int i, ncells, ret, total; + + ret = 0; + if (flags & OFWBUS_REQ_FIND_PROP) { + if (OF_searchencprop(node, prop, &p, sizeof(p)) != -1) { + p = OF_node_from_xref(p); + if (p == 0) + return (ENXIO); + ret = ofwbus_device_require(sc, pnode, node, p, flags); + } + return (ret); + } + + ret = ofw_bus_parse_xref_list_get_length(node, prop, propcells, &total); + if (ret) + return (ret); + for (i = 0; i < total; i++) { + ret = ofw_bus_parse_xref_list_alloc(node, prop, propcells, i, + &xref, &ncells, &cells); + if (flags & OFWBUS_REQ_FIND_ATTPARENT) { + for (p = OF_node_from_xref(xref); p; p = OF_parent(p)) { + if (OF_hasprop(p, "compatible")) { + ofwbus_device_require(sc, pnode, node, + p, flags); + break; + } + } + } else { + ofwbus_device_require(sc, pnode, node, + OF_node_from_xref(xref), flags); + } + OF_prop_free(cells); + } + + return (ret); +} + +static int +ofwbus_device_standard_requirement(struct ofwbus_softc *sc, phandle_t pnode, + phandle_t node) +{ + struct ofwbus_static_req_props **spr; + phandle_t n; + int ret; + + if (OF_hasprop(node, "interrupts")) { + n = ofw_bus_find_iparent(node); + ofwbus_device_require(sc, pnode, node, OF_node_from_xref(n), 0); + } +#define OFW_STD_REQ_PROCESS(name, flags) \ + if (OF_hasprop(node, name "s")) { \ + ret = ofwbus_reg_std_prop_req(sc, pnode, node, \ + name "s", "#" name "-cells", (flags)); \ + } + OFW_STD_REQ_PROCESS("clock", 0); + OFW_STD_REQ_PROCESS("power-domain", 0); + OFW_STD_REQ_PROCESS("reset", 0); + OFW_STD_REQ_PROCESS("phy", OFWBUS_REQ_FIND_ATTPARENT); + OFW_STD_REQ_PROCESS("dma", 0); + OFW_STD_REQ_PROCESS("gpio", 0); +#undef OFW_STD_REQ_PROCESS + + ret = ofwbus_reg_std_prop_req(sc, pnode, node, "freebsd,phandle", NULL, + OFWBUS_REQ_FIND_PROP); + if (ret) + return (ret); + + SET_FOREACH(spr, ofwbus_static_req_props_set) { + ret = ofwbus_reg_std_prop_req(sc, pnode, node, + (*spr)->ofwbus_srp_prop, (*spr)->ofwbus_srp_propcells, + (*spr)->ofwbus_srp_flags); + if (ret) + return (ret); + } + return (0); +} +#endif /* OFWBUS_AUTODEP */ + +static int ofwbus_attach(device_t dev) { struct ofwbus_softc *sc; - phandle_t node; + phandle_t node, pnode; struct ofw_bus_devinfo obd; +#ifdef OFWBUS_AUTODEP + struct ofwbus_dep_entry *np; +#endif /* OFWBUS_AUTODEP */ sc = device_get_softc(dev); +#ifdef OFWBUS_AUTODEP + TAILQ_INIT(&sc->sc_dep_head); +#endif /* OFWBUS_AUTODEP */ - node = OF_peer(0); + pnode = OF_peer(0); /* * If no Open Firmware, bail early */ - if (node == -1) + if (pnode == -1) return (ENXIO); /* * ofwbus bus starts on unamed node in FDT, so we cannot make * ofw_bus_devinfo from it. Pass node to simplebus_init directly. */ - simplebus_init(dev, node); + simplebus_init(dev, pnode); sc->sc_intr_rman.rm_type = RMAN_ARRAY; sc->sc_intr_rman.rm_descr = "Interrupts"; sc->sc_mem_rman.rm_type = RMAN_ARRAY; @@ -171,12 +343,35 @@ /* * Now walk the OFW tree and attach top-level devices. */ - for (node = OF_child(node); node > 0; node = OF_peer(node)) { +#ifndef OFWBUS_AUTODEP + for (node = OF_child(pnode); node > 0; node = OF_peer(node)) { if (ofw_bus_gen_setup_devinfo(&obd, node) != 0) continue; simplebus_add_device(dev, node, 0, NULL, -1, NULL); } - return (bus_generic_attach(dev)); +#else + for (node = OF_child(pnode); node > 0; node = OF_peer(node)) { + if (ofw_bus_gen_setup_devinfo(&obd, node) != 0) + continue; + ofwbus_device_require(sc, pnode, node, 0, 1); + ofwbus_device_standard_requirement(sc, pnode, node); + } + + TAILQ_FOREACH(np, &sc->sc_dep_head, entries) { + if (ofw_bus_find_child_device_by_phandle(dev, np->node) == NULL) + { + simplebus_add_device(dev, np->node, 0, NULL, -1, NULL); + } + } + + while (!TAILQ_EMPTY(&sc->sc_dep_head)) { + np = TAILQ_FIRST(&sc->sc_dep_head); + TAILQ_REMOVE(&sc->sc_dep_head, np, entries); + free(np, M_TEMP); + } + TAILQ_INIT(&sc->sc_dep_head); +#endif + return (0); } static struct resource *