Index: head/sys/dev/bhnd/bhnd.h =================================================================== --- head/sys/dev/bhnd/bhnd.h +++ head/sys/dev/bhnd/bhnd.h @@ -43,6 +43,8 @@ #include "bhnd_bus_if.h" #include "bhnd_match.h" +#include "nvram/bhnd_nvram.h" + extern devclass_t bhnd_devclass; extern devclass_t bhnd_hostb_devclass; extern devclass_t bhnd_nvram_devclass; @@ -242,6 +244,7 @@ const char *bhnd_vendor_name(uint16_t vendor); const char *bhnd_port_type_name(bhnd_port_type port_type); +const char *bhnd_nvram_src_name(bhnd_nvram_src nvram_src); const char *bhnd_find_core_name(uint16_t vendor, uint16_t device); @@ -324,7 +327,7 @@ bool bhnd_bus_generic_is_region_valid(device_t dev, device_t child, bhnd_port_type type, u_int port, u_int region); -int bhnd_bus_generic_read_nvram_var(device_t dev, +int bhnd_bus_generic_get_nvram_var(device_t dev, device_t child, const char *name, void *buf, size_t *size); const struct bhnd_chipid *bhnd_bus_generic_get_chipid(device_t dev, @@ -332,9 +335,6 @@ int bhnd_bus_generic_read_board_info(device_t dev, device_t child, struct bhnd_board_info *info); -int bhnd_bus_generic_get_nvram_var(device_t dev, - device_t child, const char *name, - void *buf, size_t *size); struct bhnd_resource *bhnd_bus_generic_alloc_resource (device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, Index: head/sys/dev/bhnd/bhnd.c =================================================================== --- head/sys/dev/bhnd/bhnd.c +++ head/sys/dev/bhnd/bhnd.c @@ -58,11 +58,20 @@ #include #include +#include + +#include "bhnd_chipc_if.h" +#include "bhnd_nvram_if.h" + #include "bhnd.h" #include "bhndvar.h" MALLOC_DEFINE(M_BHND, "bhnd", "bhnd bus data structures"); +/* Bus pass at which all bus-required children must be available, and + * attachment may be finalized. */ +#define BHND_FINISH_ATTACH_PASS BUS_PASS_DEFAULT + /** * bhnd_generic_probe_nomatch() reporting configuration. */ @@ -80,10 +89,22 @@ { BHND_MFGID_INVALID, BHND_COREID_INVALID, false } }; -static int compare_ascending_probe_order(const void *lhs, - const void *rhs); -static int compare_descending_probe_order(const void *lhs, - const void *rhs); + +static int bhnd_delete_children(struct bhnd_softc *sc); + +static int bhnd_finish_attach(struct bhnd_softc *sc); + +static device_t bhnd_find_chipc(struct bhnd_softc *sc); +static struct chipc_caps *bhnd_find_chipc_caps(struct bhnd_softc *sc); +static device_t bhnd_find_platform_dev(struct bhnd_softc *sc, + const char *classname); +static device_t bhnd_find_pmu(struct bhnd_softc *sc); +static device_t bhnd_find_nvram(struct bhnd_softc *sc); + +static int compare_ascending_probe_order(const void *lhs, + const void *rhs); +static int compare_descending_probe_order(const void *lhs, + const void *rhs); /** * Default bhnd(4) bus driver implementation of DEVICE_ATTACH(). @@ -94,44 +115,53 @@ int bhnd_generic_attach(device_t dev) { - device_t *devs; - int ndevs; - int error; + struct bhnd_softc *sc; + device_t *devs; + int ndevs; + int error; if (device_is_attached(dev)) return (EBUSY); + sc = device_get_softc(dev); + sc->dev = dev; + if ((error = device_get_children(dev, &devs, &ndevs))) return (error); + /* Probe and attach all children */ qsort(devs, ndevs, sizeof(*devs), compare_ascending_probe_order); for (int i = 0; i < ndevs; i++) { device_t child = devs[i]; device_probe_and_attach(child); } + /* Try to finalize attachment */ + if (bus_current_pass >= BHND_FINISH_ATTACH_PASS) { + if ((error = bhnd_finish_attach(sc))) + goto cleanup; + } + +cleanup: free(devs, M_TEMP); - return (0); + + if (error) + bhnd_delete_children(sc); + + return (error); } /** - * Default bhnd(4) bus driver implementation of DEVICE_DETACH(). - * - * This implementation calls device_detach() for each of the device's - * children, in reverse bhnd probe order, terminating if any call to - * device_detach() fails. + * Detach and delete all children, in reverse of their attach order. */ -int -bhnd_generic_detach(device_t dev) +static int +bhnd_delete_children(struct bhnd_softc *sc) { - device_t *devs; - int ndevs; - int error; + device_t *devs; + int ndevs; + int error; - if (!device_is_attached(dev)) - return (EBUSY); - - if ((error = device_get_children(dev, &devs, &ndevs))) + if ((error = device_get_children(sc->dev, &devs, &ndevs))) return (error); /* Detach in the reverse of attach order */ @@ -140,7 +170,7 @@ device_t child = devs[i]; /* Terminate on first error */ - if ((error = device_detach(child))) + if ((error = device_delete_child(sc->dev, child))) goto cleanup; } @@ -150,6 +180,25 @@ } /** + * Default bhnd(4) bus driver implementation of DEVICE_DETACH(). + * + * This implementation calls device_detach() for each of the device's + * children, in reverse bhnd probe order, terminating if any call to + * device_detach() fails. + */ +int +bhnd_generic_detach(device_t dev) +{ + struct bhnd_softc *sc; + + if (!device_is_attached(dev)) + return (EBUSY); + + sc = device_get_softc(dev); + return (bhnd_delete_children(sc)); +} + +/** * Default bhnd(4) bus driver implementation of DEVICE_SHUTDOWN(). * * This implementation calls device_shutdown() for each of the device's @@ -262,6 +311,223 @@ return (error); } +static void +bhnd_new_pass(device_t dev) +{ + struct bhnd_softc *sc; + int error; + + sc = device_get_softc(dev); + + /* Attach any permissible children */ + bus_generic_new_pass(dev); + + /* Finalize attachment */ + if (!sc->attach_done && bus_current_pass >= BHND_FINISH_ATTACH_PASS) { + if ((error = bhnd_finish_attach(sc))) { + panic("bhnd_finish_attach() failed: %d", error); + } + } +} + +/* + * Finish any pending bus attachment operations. + * + * When attached as a SoC bus (as opposed to a bridged WiFi device), our + * platform devices may not be attached until later bus passes, necessitating + * delayed initialization on our part. + */ +static int +bhnd_finish_attach(struct bhnd_softc *sc) +{ + struct chipc_caps *ccaps; + + GIANT_REQUIRED; /* newbus */ + + KASSERT(bus_current_pass >= BHND_FINISH_ATTACH_PASS, + ("bhnd_finish_attach() called in pass %d", bus_current_pass)); + + KASSERT(!sc->attach_done, ("duplicate call to bhnd_finish_attach()")); + + /* Locate chipc device */ + if ((sc->chipc_dev = bhnd_find_chipc(sc)) == NULL) { + device_printf(sc->dev, "error: ChipCommon device not found\n"); + return (ENXIO); + } + + ccaps = BHND_CHIPC_GET_CAPS(sc->chipc_dev); + + /* Look for NVRAM device */ + if (ccaps->nvram_src != BHND_NVRAM_SRC_UNKNOWN) { + if ((sc->nvram_dev = bhnd_find_nvram(sc)) == NULL) { + device_printf(sc->dev, + "warning: %s NVRAM device not found\n", + bhnd_nvram_src_name(ccaps->nvram_src)); + } + } + + /* Look for a PMU */ + if (ccaps->pmu) { + if ((sc->pmu_dev = bhnd_find_pmu(sc)) == NULL) { + device_printf(sc->dev, + "warning: PMU device not found\n"); + } + } + + /* Mark attach as completed */ + sc->attach_done = true; + + return (0); +} + +/* Locate the ChipCommon core. */ +static device_t +bhnd_find_chipc(struct bhnd_softc *sc) +{ + device_t chipc; + + /* Make sure we're holding Giant for newbus */ + GIANT_REQUIRED; + + /* chipc_dev is initialized during attachment */ + if (sc->attach_done) { + if ((chipc = sc->chipc_dev) == NULL) + return (NULL); + + goto found; + } + + /* Locate chipc core with a core unit of 0 */ + chipc = bhnd_find_child(sc->dev, BHND_DEVCLASS_CC, 0); + if (chipc == NULL) + return (NULL); + +found: + if (device_get_state(chipc) < DS_ATTACHING) { + device_printf(sc->dev, "chipc found, but did not attach\n"); + return (NULL); + } + + return (chipc); +} + +/* Locate the ChipCommon core and return the device capabilities */ +static struct chipc_caps * +bhnd_find_chipc_caps(struct bhnd_softc *sc) +{ + device_t chipc; + + if ((chipc = bhnd_find_chipc(sc)) == NULL) { + device_printf(sc->dev, + "chipc unavailable; cannot fetch capabilities\n"); + return (NULL); + } + + return (BHND_CHIPC_GET_CAPS(chipc)); +} + +/** + * Find an attached platform device on @p dev, searching first for cores + * matching @p classname, and if not found, searching the children of the first + * bhnd_chipc device on the bus. + * + * @param sc Driver state. + * @param chipc Attached ChipCommon device. + * @param classname Device class to search for. + * + * @retval device_t A matching device. + * @retval NULL If no matching device is found. + */ +static device_t +bhnd_find_platform_dev(struct bhnd_softc *sc, const char *classname) +{ + device_t chipc, child; + + /* Make sure we're holding Giant for newbus */ + GIANT_REQUIRED; + + /* Look for a directly-attached child */ + child = device_find_child(sc->dev, classname, -1); + if (child != NULL) + goto found; + + /* Look for the first matching ChipCommon child */ + if ((chipc = bhnd_find_chipc(sc)) == NULL) { + device_printf(sc->dev, + "chipc unavailable; cannot locate %s\n", classname); + return (NULL); + } + + child = device_find_child(chipc, classname, -1); + if (child == NULL) + return (NULL); + +found: + if (device_get_state(child) < DS_ATTACHING) + return (NULL); + + return (child); +} + +/* Locate the PMU device, if any */ +static device_t +bhnd_find_pmu(struct bhnd_softc *sc) +{ + struct chipc_caps *ccaps; + + /* Make sure we're holding Giant for newbus */ + GIANT_REQUIRED; + + /* pmu_dev is initialized during attachment */ + if (sc->attach_done) { + if (sc->pmu_dev == NULL) + return (NULL); + + if (device_get_state(sc->pmu_dev) < DS_ATTACHING) + return (NULL); + + return (sc->pmu_dev); + } + + if ((ccaps = bhnd_find_chipc_caps(sc)) == NULL) + return (NULL); + + if (!ccaps->pmu) + return (NULL); + + return (bhnd_find_platform_dev(sc, "bhnd_pmu")); +} + +/* Locate the NVRAM device, if any */ +static device_t +bhnd_find_nvram(struct bhnd_softc *sc) +{ + struct chipc_caps *ccaps; + + /* Make sure we're holding Giant for newbus */ + GIANT_REQUIRED; + + + /* nvram_dev is initialized during attachment */ + if (sc->attach_done) { + if (sc->nvram_dev == NULL) + return (NULL); + + if (device_get_state(sc->nvram_dev) < DS_ATTACHING) + return (NULL); + + return (sc->nvram_dev); + } + + if ((ccaps = bhnd_find_chipc_caps(sc)) == NULL) + return (NULL); + + if (ccaps->nvram_src == BHND_NVRAM_SRC_UNKNOWN) + return (NULL); + + return (bhnd_find_platform_dev(sc, "bhnd_nvram")); +} + /* * Ascending comparison of bhnd device's probe order. */ @@ -376,6 +642,35 @@ } /** + * Default bhnd(4) bus driver implementation of BHND_BUS_GET_NVRAM_VAR(). + * + * This implementation searches @p dev for a usable NVRAM child device. + * + * If no usable child device is found on @p dev, the request is delegated to + * the BHND_BUS_GET_NVRAM_VAR() method on the parent of @p dev. + */ +int +bhnd_generic_get_nvram_var(device_t dev, device_t child, const char *name, + void *buf, size_t *size) +{ + struct bhnd_softc *sc; + device_t nvram, parent; + + sc = device_get_softc(dev); + + /* If a NVRAM device is available, consult it first */ + if ((nvram = bhnd_find_nvram(sc)) != NULL) + return BHND_NVRAM_GETVAR(nvram, name, buf, size); + + /* Otherwise, try to delegate to parent */ + if ((parent = device_get_parent(dev)) == NULL) + return (ENODEV); + + return (BHND_BUS_GET_NVRAM_VAR(device_get_parent(dev), child, + name, buf, size)); +} + +/** * Default bhnd(4) bus driver implementation of BUS_PRINT_CHILD(). * * This implementation requests the device's struct resource_list via @@ -538,6 +833,15 @@ /* Free device info */ if ((dinfo = device_get_ivars(child)) != NULL) BHND_BUS_FREE_DEVINFO(dev, dinfo); + + /* Clean up platform device references */ + if (sc->chipc_dev == child) { + sc->chipc_dev = NULL; + } else if (sc->nvram_dev == child) { + sc->nvram_dev = NULL; + } else if (sc->pmu_dev == child) { + sc->pmu_dev = NULL; + } } /** @@ -659,6 +963,7 @@ DEVMETHOD(device_resume, bhnd_generic_resume), /* Bus interface */ + DEVMETHOD(bus_new_pass, bhnd_new_pass), DEVMETHOD(bus_add_child, bhnd_generic_add_child), DEVMETHOD(bus_child_deleted, bhnd_generic_child_deleted), DEVMETHOD(bus_probe_nomatch, bhnd_generic_probe_nomatch), @@ -691,7 +996,7 @@ DEVMETHOD(bhnd_bus_get_probe_order, bhnd_generic_get_probe_order), DEVMETHOD(bhnd_bus_is_region_valid, bhnd_generic_is_region_valid), DEVMETHOD(bhnd_bus_is_hw_disabled, bhnd_bus_generic_is_hw_disabled), - DEVMETHOD(bhnd_bus_get_nvram_var, bhnd_bus_generic_get_nvram_var), + DEVMETHOD(bhnd_bus_get_nvram_var, bhnd_generic_get_nvram_var), /* BHND interface (bus I/O) */ DEVMETHOD(bhnd_bus_read_1, bhnd_read_1), Index: head/sys/dev/bhnd/bhnd_subr.c =================================================================== --- head/sys/dev/bhnd/bhnd_subr.c +++ head/sys/dev/bhnd/bhnd_subr.c @@ -51,8 +51,6 @@ #include "bhndreg.h" #include "bhndvar.h" -static device_t find_nvram_child(device_t dev); - /* BHND core device description table. */ static const struct bhnd_core_desc { uint16_t vendor; @@ -198,6 +196,25 @@ } } +/** + * Return the name of an NVRAM source. + */ +const char * +bhnd_nvram_src_name(bhnd_nvram_src nvram_src) +{ + switch (nvram_src) { + case BHND_NVRAM_SRC_FLASH: + return ("flash"); + case BHND_NVRAM_SRC_OTP: + return ("OTP"); + case BHND_NVRAM_SRC_SPROM: + return ("SPROM"); + case BHND_NVRAM_SRC_UNKNOWN: + return ("none"); + default: + return ("unknown"); + } +} static const struct bhnd_core_desc * bhnd_find_core_desc(uint16_t vendor, uint16_t device) @@ -293,7 +310,7 @@ * * @param parent The bhnd-compatible bus to be searched. * @param class The device class to match on. - * @param unit The device unit number; specify -1 to return the first match + * @param unit The core unit number; specify -1 to return the first match * regardless of unit number. * * @retval device_t if a matching child device is found. @@ -990,47 +1007,10 @@ #undef BHND_GV_REQ #undef BHND_GV_OPT - -/** - * Find an NVRAM child device on @p dev, if any. - * - * @retval device_t An NVRAM device. - * @retval NULL If no NVRAM device is found. - */ -static device_t -find_nvram_child(device_t dev) -{ - device_t chipc, nvram; - - /* Look for a directly-attached NVRAM child */ - nvram = device_find_child(dev, "bhnd_nvram", 0); - if (nvram != NULL) - return (nvram); - - /* Remaining checks are only applicable when searching a bhnd(4) - * bus. */ - if (device_get_devclass(dev) != bhnd_devclass) - return (NULL); - - /* Look for a ChipCommon-attached NVRAM device */ - if ((chipc = bhnd_find_child(dev, BHND_DEVCLASS_CC, -1)) != NULL) { - nvram = device_find_child(chipc, "bhnd_nvram", 0); - if (nvram != NULL) - return (nvram); - } - - /* Not found */ - return (NULL); -} - /** * Helper function for implementing BHND_BUS_GET_NVRAM_VAR(). * - * This implementation searches @p dev for a usable NVRAM child device: - * - The first child device implementing the bhnd_nvram devclass is - * returned, otherwise - * - If @p dev is a bhnd(4) bus, a ChipCommon core that advertises an - * attached NVRAM source. + * This implementation searches @p dev for a usable NVRAM child device. * * If no usable child device is found on @p dev, the request is delegated to * the BHND_BUS_GET_NVRAM_VAR() method on the parent of @p dev. @@ -1042,8 +1022,11 @@ device_t nvram; device_t parent; - /* Try to find an NVRAM device applicable to @p child */ - if ((nvram = find_nvram_child(dev)) != NULL) + /* Make sure we're holding Giant for newbus */ + GIANT_REQUIRED; + + /* Look for a directly-attached NVRAM child */ + if ((nvram = device_find_child(dev, "bhnd_nvram", -1)) != NULL) return BHND_NVRAM_GETVAR(nvram, name, buf, size); /* Try to delegate to parent */ Index: head/sys/dev/bhnd/bhndb/bhndb.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb.c +++ head/sys/dev/bhnd/bhndb/bhndb.c @@ -1121,7 +1121,11 @@ bhndb_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { - int error; + struct resource_list_entry *rle; + bool passthrough; + int error; + + passthrough = (device_get_parent(child) != dev); /* Deactivate resources */ if (rman_get_flags(r) & RF_ACTIVE) { @@ -1133,6 +1137,14 @@ if ((error = rman_release_resource(r))) return (error); + if (!passthrough) { + /* Clean resource list entry */ + rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child), + type, rid); + if (rle != NULL) + rle->res = NULL; + } + return (0); } Index: head/sys/dev/bhnd/bhndvar.h =================================================================== --- head/sys/dev/bhnd/bhndvar.h +++ head/sys/dev/bhnd/bhndvar.h @@ -56,8 +56,16 @@ * bhnd driver instance state. Must be first member of all subclass * softc structures. */ -struct bhnd_softc {}; +struct bhnd_softc { + device_t dev; /**< bus device */ + bool attach_done; /**< true if initialization of all + * platform devices has been + * completed */ + device_t chipc_dev; /**< bhnd_chipc device */ + device_t nvram_dev; /**< bhnd_nvram device, if any */ + device_t pmu_dev; /**< bhnd_pmu device, if any */ +}; int bhnd_generic_attach(device_t dev); int bhnd_generic_detach(device_t dev); @@ -82,4 +90,8 @@ int bhnd_generic_resume_child(device_t dev, device_t child); +int bhnd_generic_get_nvram_var(device_t dev, + device_t child, const char *name, void *buf, + size_t *size); + #endif /* _BHND_BHNDVAR_H_ */