Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ofw/ofwbus.c
Show All 33 Lines | |||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/limits.h> | |||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/pcpu.h> | #include <sys/pcpu.h> | ||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
#ifdef INTRNG | #ifdef INTRNG | ||||
#include <sys/intr.h> | #include <sys/intr.h> | ||||
#endif | #endif | ||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#include <dev/ofw/openfirm.h> | #include <dev/ofw/openfirm.h> | ||||
#include <dev/fdt/simplebus.h> | #include <dev/fdt/simplebus.h> | ||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <machine/resource.h> | #include <machine/resource.h> | ||||
#include "opt_ofw.h" | |||||
/* | /* | ||||
* The ofwbus (which is a pseudo-bus actually) iterates over the nodes that | * 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 | * hang from the Open Firmware root node and adds them as devices to this bus | ||||
* (except some special nodes which are excluded) so that drivers can be | * (except some special nodes which are excluded) so that drivers can be | ||||
* attached to them. | * attached to them. | ||||
* | * | ||||
*/ | */ | ||||
struct ofwbus_softc { | struct ofwbus_softc { | ||||
struct simplebus_softc simplebus_sc; | struct simplebus_softc simplebus_sc; | ||||
struct rman sc_intr_rman; | struct rman sc_intr_rman; | ||||
struct rman sc_mem_rman; | struct rman sc_mem_rman; | ||||
#ifdef OFWBUS_AUTODEP | |||||
TAILQ_HEAD(, ofwbus_dep_entry) sc_dep_head; | |||||
#endif /* OFWBUS_AUTODEP */ | |||||
}; | }; | ||||
#ifndef __aarch64__ | #ifndef __aarch64__ | ||||
static device_identify_t ofwbus_identify; | static device_identify_t ofwbus_identify; | ||||
#endif | #endif | ||||
static device_probe_t ofwbus_probe; | static device_probe_t ofwbus_probe; | ||||
static device_attach_t ofwbus_attach; | static device_attach_t ofwbus_attach; | ||||
static bus_alloc_resource_t ofwbus_alloc_resource; | static bus_alloc_resource_t ofwbus_alloc_resource; | ||||
Show All 11 Lines | #endif | ||||
/* Bus interface */ | /* Bus interface */ | ||||
DEVMETHOD(bus_alloc_resource, ofwbus_alloc_resource), | DEVMETHOD(bus_alloc_resource, ofwbus_alloc_resource), | ||||
DEVMETHOD(bus_adjust_resource, ofwbus_adjust_resource), | DEVMETHOD(bus_adjust_resource, ofwbus_adjust_resource), | ||||
DEVMETHOD(bus_release_resource, ofwbus_release_resource), | DEVMETHOD(bus_release_resource, ofwbus_release_resource), | ||||
DEVMETHOD_END | 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, | DEFINE_CLASS_1(ofwbus, ofwbus_driver, ofwbus_methods, | ||||
sizeof(struct ofwbus_softc), simplebus_driver); | sizeof(struct ofwbus_softc), simplebus_driver); | ||||
static devclass_t ofwbus_devclass; | static devclass_t ofwbus_devclass; | ||||
EARLY_DRIVER_MODULE(ofwbus, nexus, ofwbus_driver, ofwbus_devclass, 0, 0, | EARLY_DRIVER_MODULE(ofwbus, nexus, ofwbus_driver, ofwbus_devclass, 0, 0, | ||||
BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); | BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); | ||||
MODULE_VERSION(ofwbus, 1); | MODULE_VERSION(ofwbus, 1); | ||||
#ifndef __aarch64__ | #ifndef __aarch64__ | ||||
Show All 18 Lines | #ifdef __aarch64__ | ||||
if (OF_peer(0) == 0) | if (OF_peer(0) == 0) | ||||
return (ENXIO); | return (ENXIO); | ||||
#endif | #endif | ||||
device_set_desc(dev, "Open Firmware Device Tree"); | device_set_desc(dev, "Open Firmware Device Tree"); | ||||
return (BUS_PROBE_NOWILDCARD); | return (BUS_PROBE_NOWILDCARD); | ||||
} | } | ||||
#ifdef OFWBUS_AUTODEP | |||||
static int | 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) | ofwbus_attach(device_t dev) | ||||
{ | { | ||||
struct ofwbus_softc *sc; | struct ofwbus_softc *sc; | ||||
phandle_t node; | phandle_t node, pnode; | ||||
struct ofw_bus_devinfo obd; | struct ofw_bus_devinfo obd; | ||||
#ifdef OFWBUS_AUTODEP | |||||
struct ofwbus_dep_entry *np; | |||||
#endif /* OFWBUS_AUTODEP */ | |||||
sc = device_get_softc(dev); | 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 no Open Firmware, bail early | ||||
*/ | */ | ||||
if (node == -1) | if (pnode == -1) | ||||
return (ENXIO); | return (ENXIO); | ||||
/* | /* | ||||
* ofwbus bus starts on unamed node in FDT, so we cannot make | * ofwbus bus starts on unamed node in FDT, so we cannot make | ||||
* ofw_bus_devinfo from it. Pass node to simplebus_init directly. | * 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_type = RMAN_ARRAY; | ||||
sc->sc_intr_rman.rm_descr = "Interrupts"; | sc->sc_intr_rman.rm_descr = "Interrupts"; | ||||
sc->sc_mem_rman.rm_type = RMAN_ARRAY; | sc->sc_mem_rman.rm_type = RMAN_ARRAY; | ||||
sc->sc_mem_rman.rm_descr = "Device Memory"; | sc->sc_mem_rman.rm_descr = "Device Memory"; | ||||
if (rman_init(&sc->sc_intr_rman) != 0 || | if (rman_init(&sc->sc_intr_rman) != 0 || | ||||
rman_init(&sc->sc_mem_rman) != 0 || | rman_init(&sc->sc_mem_rman) != 0 || | ||||
rman_manage_region(&sc->sc_intr_rman, 0, ~0) != 0 || | rman_manage_region(&sc->sc_intr_rman, 0, ~0) != 0 || | ||||
rman_manage_region(&sc->sc_mem_rman, 0, BUS_SPACE_MAXADDR) != 0) | rman_manage_region(&sc->sc_mem_rman, 0, BUS_SPACE_MAXADDR) != 0) | ||||
panic("%s: failed to set up rmans.", __func__); | panic("%s: failed to set up rmans.", __func__); | ||||
/* | /* | ||||
* Allow devices to identify. | * Allow devices to identify. | ||||
*/ | */ | ||||
bus_generic_probe(dev); | bus_generic_probe(dev); | ||||
/* | /* | ||||
* Now walk the OFW tree and attach top-level devices. | * 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) | if (ofw_bus_gen_setup_devinfo(&obd, node) != 0) | ||||
continue; | continue; | ||||
simplebus_add_device(dev, node, 0, NULL, -1, NULL); | 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 * | static struct resource * | ||||
ofwbus_alloc_resource(device_t bus, device_t child, int type, int *rid, | ofwbus_alloc_resource(device_t bus, device_t child, int type, int *rid, | ||||
rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) | rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) | ||||
{ | { | ||||
struct ofwbus_softc *sc; | struct ofwbus_softc *sc; | ||||
struct rman *rm; | struct rman *rm; | ||||
▲ Show 20 Lines • Show All 108 Lines • Show Last 20 Lines |