Index: sys/dev/bhnd/bhnd.h =================================================================== --- sys/dev/bhnd/bhnd.h +++ sys/dev/bhnd/bhnd.h @@ -1,7 +1,11 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -32,8 +36,10 @@ #ifndef _BHND_BHND_H_ #define _BHND_BHND_H_ -#include +#include #include +#include +#include #include @@ -289,6 +295,35 @@ #define BHND_DEVICE_IS_END(_d) \ (BHND_MATCH_IS_ANY(&(_d)->core) && (_d)->desc == NULL) +/** + * bhnd device sort order. + */ +typedef enum { + BHND_DEVICE_ORDER_ATTACH, /**< sort by bhnd(4) device attach order; + child devices should be probed/attached + in this order */ + BHND_DEVICE_ORDER_DETACH, /**< sort by bhnd(4) device detach order; + child devices should be detached, suspended, + and shutdown in this order */ +} bhnd_device_order; + +/** + * A registry of bhnd service providers. + */ +struct bhnd_service_registry { + STAILQ_HEAD(,bhnd_service_entry) entries; /**< registered services */ + struct sx lock; /**< state lock */ +}; + +/** + * bhnd service provider flags. + */ +enum { + BHND_SPF_INHERITED = (1<<0), /**< service provider reference was inherited from + a parent bus, and should be deregistered when the + last active reference is released */ +}; + 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); @@ -304,12 +339,23 @@ int bhnd_format_chip_id(char *buffer, size_t size, uint16_t chip_id); -device_t bhnd_match_child(device_t dev, +device_t bhnd_bus_match_child(device_t bus, const struct bhnd_core_match *desc); -device_t bhnd_find_child(device_t dev, +device_t bhnd_bus_find_child(device_t bus, bhnd_devclass_t class, int unit); +int bhnd_bus_get_children(device_t bus, + device_t **devlistp, int *devcountp, + bhnd_device_order order); + +void bhnd_bus_free_children(device_t *devlist); + +int bhnd_bus_probe_children(device_t bus); + +int bhnd_sort_devices(device_t *devlist, + size_t devcount, bhnd_device_order order); + device_t bhnd_find_bridge_root(device_t dev, devclass_t bus_class); @@ -410,6 +456,51 @@ const char *name, void *buf, size_t count, bhnd_nvram_type type); +int bhnd_service_registry_init( + struct bhnd_service_registry *bsr); +int bhnd_service_registry_fini( + struct bhnd_service_registry *bsr); +int bhnd_service_registry_add( + struct bhnd_service_registry *bsr, + device_t provider, + bhnd_service_t service, + uint32_t flags); +int bhnd_service_registry_remove( + struct bhnd_service_registry *bsr, + device_t provider, + bhnd_service_t service); +device_t bhnd_service_registry_retain( + struct bhnd_service_registry *bsr, + bhnd_service_t service); +bool bhnd_service_registry_release( + struct bhnd_service_registry *bsr, + device_t provider, + bhnd_service_t service); + +int bhnd_bus_generic_register_provider( + device_t dev, device_t child, + device_t provider, bhnd_service_t service); +int bhnd_bus_generic_deregister_provider( + device_t dev, device_t child, + device_t provider, bhnd_service_t service); +device_t bhnd_bus_generic_retain_provider(device_t dev, + device_t child, bhnd_service_t service); +void bhnd_bus_generic_release_provider(device_t dev, + device_t child, device_t provider, + bhnd_service_t service); + +int bhnd_bus_generic_sr_register_provider( + device_t dev, device_t child, + device_t provider, bhnd_service_t service); +int bhnd_bus_generic_sr_deregister_provider( + device_t dev, device_t child, + device_t provider, bhnd_service_t service); +device_t bhnd_bus_generic_sr_retain_provider(device_t dev, + device_t child, bhnd_service_t service); +void bhnd_bus_generic_sr_release_provider(device_t dev, + device_t child, device_t provider, + bhnd_service_t service); + bool bhnd_bus_generic_is_hw_disabled(device_t dev, device_t child); bool bhnd_bus_generic_is_region_valid(device_t dev, @@ -458,10 +549,84 @@ * @param dev A bhnd bus device. */ static inline device_t -bhnd_find_hostb_device(device_t dev) { +bhnd_bus_find_hostb_device(device_t dev) { return (BHND_BUS_FIND_HOSTB_DEVICE(dev)); } +/** + * Register a provider for a given @p service. + * + * @param dev The device to register as a service provider + * with its parent bus. + * @param service The service for which @p dev will be registered. + * + * @retval 0 success + * @retval EEXIST if an entry for @p service already exists. + * @retval non-zero if registering @p dev otherwise fails, a regular + * unix error code will be returned. + */ +static inline int +bhnd_register_provider(device_t dev, bhnd_service_t service) +{ + return (BHND_BUS_REGISTER_PROVIDER(device_get_parent(dev), dev, dev, + service)); +} + + /** + * Attempt to remove a service provider registration for @p dev. + * + * @param dev The device to be deregistered as a service provider. + * @param service The service for which @p dev will be deregistered, or + * BHND_SERVICE_INVALID to remove all service registrations + * for @p dev. + * + * @retval 0 success + * @retval EBUSY if active references to @p dev exist; @see + * bhnd_retain_provider() and bhnd_release_provider(). + */ +static inline int +bhnd_deregister_provider(device_t dev, bhnd_service_t service) +{ + return (BHND_BUS_DEREGISTER_PROVIDER(device_get_parent(dev), dev, dev, + service)); +} + +/** + * Retain and return a reference to the registered @p service provider, if any. + * + * @param dev The requesting device. + * @param service The service for which a provider should be returned. + * + * On success, the caller assumes ownership the returned provider, and + * is responsible for releasing this reference via + * BHND_BUS_RELEASE_PROVIDER(). + * + * @retval device_t success + * @retval NULL if no provider is registered for @p service. + */ +static inline device_t +bhnd_retain_provider(device_t dev, bhnd_service_t service) +{ + return (BHND_BUS_RETAIN_PROVIDER(device_get_parent(dev), dev, + service)); +} + +/** + * Release a reference to a provider device previously returned by + * bhnd_retain_provider(). + * + * @param dev The requesting device. + * @param provider The provider to be released. + * @param service The service for which @p provider was previously retained. + */ +static inline void +bhnd_release_provider(device_t dev, device_t provider, + bhnd_service_t service) +{ + return (BHND_BUS_RELEASE_PROVIDER(device_get_parent(dev), dev, + provider, service)); +} + /** * Return true if the hardware components required by @p dev are known to be * unpopulated or otherwise unusable. Index: sys/dev/bhnd/bhnd.c =================================================================== --- sys/dev/bhnd/bhnd.c +++ sys/dev/bhnd/bhnd.c @@ -1,7 +1,11 @@ /*- * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -69,11 +73,9 @@ #include "bhnd.h" #include "bhndvar.h" -MALLOC_DEFINE(M_BHND, "bhnd", "bhnd bus data structures"); +#include "bhnd_private.h" -/* Bus pass at which all bus-required children must be available, and - * attachment may be finalized. */ -#define BHND_FINISH_ATTACH_PASS BUS_PASS_DEFAULT +MALLOC_DEFINE(M_BHND, "bhnd", "bhnd bus data structures"); /** * bhnd_generic_probe_nomatch() reporting configuration. @@ -92,23 +94,8 @@ { BHND_MFGID_INVALID, BHND_COREID_INVALID, false } }; - 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(). * @@ -119,8 +106,6 @@ bhnd_generic_attach(device_t dev) { struct bhnd_softc *sc; - device_t *devs; - int ndevs; int error; if (device_is_attached(dev)) @@ -129,29 +114,13 @@ 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); - - if (error) + if ((error = bhnd_bus_probe_children(dev))) { bhnd_delete_children(sc); + return (error); + } - return (error); + return (0); } /** @@ -164,11 +133,13 @@ int ndevs; int error; - if ((error = device_get_children(sc->dev, &devs, &ndevs))) + /* Fetch children in detach order */ + error = bhnd_bus_get_children(sc->dev, &devs, &ndevs, + BHND_DEVICE_ORDER_DETACH); + if (error) return (error); - /* Detach in the reverse of attach order */ - qsort(devs, ndevs, sizeof(*devs), compare_descending_probe_order); + /* Perform detach */ for (int i = 0; i < ndevs; i++) { device_t child = devs[i]; @@ -178,7 +149,7 @@ } cleanup: - free(devs, M_TEMP); + bhnd_bus_free_children(devs); return (error); } @@ -193,12 +164,17 @@ bhnd_generic_detach(device_t dev) { struct bhnd_softc *sc; + int error; if (!device_is_attached(dev)) return (EBUSY); sc = device_get_softc(dev); - return (bhnd_delete_children(sc)); + + if ((error = bhnd_delete_children(sc))) + return (error); + + return (0); } /** @@ -218,11 +194,13 @@ if (!device_is_attached(dev)) return (EBUSY); - if ((error = device_get_children(dev, &devs, &ndevs))) + /* Fetch children in detach order */ + error = bhnd_bus_get_children(dev, &devs, &ndevs, + BHND_DEVICE_ORDER_DETACH); + if (error) return (error); - /* Shutdown in the reverse of attach order */ - qsort(devs, ndevs, sizeof(*devs), compare_descending_probe_order); + /* Perform shutdown */ for (int i = 0; i < ndevs; i++) { device_t child = devs[i]; @@ -232,7 +210,7 @@ } cleanup: - free(devs, M_TEMP); + bhnd_bus_free_children(devs); return (error); } @@ -253,10 +231,13 @@ if (!device_is_attached(dev)) return (EBUSY); - if ((error = device_get_children(dev, &devs, &ndevs))) + /* Fetch children in attach order */ + error = bhnd_bus_get_children(dev, &devs, &ndevs, + BHND_DEVICE_ORDER_ATTACH); + if (error) return (error); - qsort(devs, ndevs, sizeof(*devs), compare_ascending_probe_order); + /* Perform resume */ for (int i = 0; i < ndevs; i++) { device_t child = devs[i]; @@ -266,7 +247,7 @@ } cleanup: - free(devs, M_TEMP); + bhnd_bus_free_children(devs); return (error); } @@ -289,11 +270,13 @@ if (!device_is_attached(dev)) return (EBUSY); - if ((error = device_get_children(dev, &devs, &ndevs))) + /* Fetch children in detach order */ + error = bhnd_bus_get_children(dev, &devs, &ndevs, + BHND_DEVICE_ORDER_DETACH); + if (error) return (error); - /* Suspend in the reverse of attach order */ - qsort(devs, ndevs, sizeof(*devs), compare_descending_probe_order); + /* Perform suspend */ for (int i = 0; i < ndevs; i++) { device_t child = devs[i]; error = BUS_SUSPEND_CHILD(device_get_parent(child), child); @@ -310,259 +293,10 @@ } cleanup: - free(devs, M_TEMP); + bhnd_bus_free_children(devs); 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; /* for 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: NVRAM %s device not found\n", - bhnd_nvram_src_name(ccaps->nvram_src)); - } - } - - /* Look for a PMU */ - if (ccaps->pmu || ccaps->pwr_ctrl) { - if ((sc->pmu_dev = bhnd_find_pmu(sc)) == NULL) { - device_printf(sc->dev, - "attach failed: supported PMU not found\n"); - return (ENXIO); - } - } - - /* 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) - goto found; - - /* Look for a parent-attached device (e.g. nexus0 -> bhnd_nvram) */ - child = device_find_child(device_get_parent(sc->dev), 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) -{ - /* 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); - } - - - 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. - */ -static int -compare_ascending_probe_order(const void *lhs, const void *rhs) -{ - device_t ldev, rdev; - int lorder, rorder; - - ldev = (*(const device_t *) lhs); - rdev = (*(const device_t *) rhs); - - lorder = BHND_BUS_GET_PROBE_ORDER(device_get_parent(ldev), ldev); - rorder = BHND_BUS_GET_PROBE_ORDER(device_get_parent(rdev), rdev); - - if (lorder < rorder) { - return (-1); - } else if (lorder > rorder) { - return (1); - } else { - return (0); - } -} - -/* - * Descending comparison of bhnd device's probe order. - */ -static int -compare_descending_probe_order(const void *lhs, const void *rhs) -{ - return (compare_ascending_probe_order(rhs, lhs)); -} - /** * Default bhnd(4) bus driver implementation of BHND_BUS_GET_PROBE_ORDER(). * @@ -613,7 +347,7 @@ case BHND_DEVCLASS_EROM: case BHND_DEVCLASS_OTHER: case BHND_DEVCLASS_INVALID: - if (bhnd_find_hostb_device(dev) == child) + if (bhnd_bus_find_hostb_device(dev) == child) return (BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY); return (BHND_PROBE_DEFAULT); @@ -630,7 +364,6 @@ { struct bhnd_softc *sc; struct bhnd_resource *br; - struct chipc_caps *ccaps; struct bhnd_core_pmu_info *pm; struct resource_list *rl; struct resource_list_entry *rle; @@ -646,18 +379,6 @@ pm = bhnd_get_pmu_info(child); pmu_regs = BHND_CLK_CTL_ST; - if ((ccaps = bhnd_find_chipc_caps(sc)) == NULL) { - device_printf(sc->dev, "alloc_pmu failed: chipc " - "capabilities unavailable\n"); - return (ENXIO); - } - - if ((pmu_dev = bhnd_find_pmu(sc)) == NULL) { - device_printf(sc->dev, - "pmu unavailable; cannot allocate request state\n"); - return (ENXIO); - } - /* already allocated? */ if (pm != NULL) { panic("duplicate PMU allocation for %s", @@ -719,23 +440,34 @@ else pmu_regs -= r_addr - rman_get_start(rle->res); + /* Retain PMU reference on behalf of our caller */ + pmu_dev = bhnd_retain_provider(child, BHND_SERVICE_PMU); + if (pmu_dev == NULL) { + device_printf(sc->dev, + "pmu unavailable; cannot allocate request state\n"); + return (ENXIO); + } + /* Allocate and initialize PMU info */ br = malloc(sizeof(struct bhnd_resource), M_BHND, M_NOWAIT); - if (br == NULL) + if (br == NULL) { + bhnd_release_provider(child, pmu_dev, BHND_SERVICE_PMU); return (ENOMEM); + } br->res = rle->res; br->direct = ((rman_get_flags(rle->res) & RF_ACTIVE) != 0); pm = malloc(sizeof(*pm), M_BHND, M_NOWAIT); if (pm == NULL) { + bhnd_release_provider(child, pmu_dev, BHND_SERVICE_PMU); free(br, M_BHND); return (ENOMEM); } pm->pm_dev = child; - pm->pm_pmu = pmu_dev; pm->pm_res = br; pm->pm_regs = pmu_regs; + pm->pm_pmu = pmu_dev; bhnd_set_pmu_info(child, pm); return (0); @@ -749,29 +481,24 @@ { struct bhnd_softc *sc; struct bhnd_core_pmu_info *pm; - device_t pmu; int error; GIANT_REQUIRED; /* for newbus */ sc = device_get_softc(dev); - if ((pmu = bhnd_find_pmu(sc)) == NULL) { - device_printf(sc->dev, - "pmu unavailable; cannot release request state\n"); - return (ENXIO); - } - /* dispatch release request */ pm = bhnd_get_pmu_info(child); if (pm == NULL) panic("pmu over-release for %s", device_get_nameunit(child)); - if ((error = BHND_PMU_CORE_RELEASE(pmu, pm))) + if ((error = BHND_PMU_CORE_RELEASE(pm->pm_pmu, pm))) return (error); /* free PMU info */ bhnd_set_pmu_info(child, NULL); + + bhnd_release_provider(pm->pm_dev, pm->pm_pmu, BHND_SERVICE_PMU); free(pm->pm_res, M_BHND); free(pm, M_BHND); @@ -875,9 +602,9 @@ /** * Default bhnd(4) bus driver implementation of BHND_BUS_GET_NVRAM_VAR(). * - * This implementation searches @p dev for a usable NVRAM child device. + * This implementation searches @p dev for a registered NVRAM child device. * - * If no usable child device is found on @p dev, the request is delegated to + * If no NVRAM device is registered with @p dev, the request is delegated to * the BHND_BUS_GET_NVRAM_VAR() method on the parent of @p dev. */ int @@ -886,12 +613,17 @@ { struct bhnd_softc *sc; device_t nvram, parent; + int error; 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, type); + nvram = bhnd_retain_provider(child, BHND_SERVICE_NVRAM); + if (nvram != NULL) { + error = BHND_NVRAM_GETVAR(nvram, name, buf, size, type); + bhnd_release_provider(child, nvram, BHND_SERVICE_NVRAM); + return (error); + } /* Otherwise, try to delegate to parent */ if ((parent = device_get_parent(dev)) == NULL) @@ -1046,15 +778,6 @@ panic("%s leaked device pmu state\n", device_get_nameunit(child)); } - - /* 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; - } } /** @@ -1176,7 +899,6 @@ DEVMETHOD(device_resume, bhnd_generic_resume), /* Bus interface */ - DEVMETHOD(bus_new_pass, bhnd_new_pass), DEVMETHOD(bus_child_deleted, bhnd_generic_child_deleted), DEVMETHOD(bus_probe_nomatch, bhnd_generic_probe_nomatch), DEVMETHOD(bus_print_child, bhnd_generic_print_child), Index: sys/dev/bhnd/bhnd_bus_if.m =================================================================== --- sys/dev/bhnd/bhnd_bus_if.m +++ sys/dev/bhnd/bhnd_bus_if.m @@ -1,7 +1,11 @@ #- # Copyright (c) 2015-2016 Landon Fuller +# Copyright (c) 2017 The FreeBSD Foundation # All rights reserved. # +# Portions of this software were developed by Landon Fuller +# under sponsorship from the FreeBSD Foundation. +# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: @@ -221,6 +225,12 @@ return (NULL); } + static struct bhnd_service_registry * + bhnd_bus_null_get_service_registry(device_t dev) + { + panic("bhnd_bus_get_service_registry unimplemented"); + } + static bool bhnd_bus_null_is_hw_disabled(device_t dev, device_t child) { @@ -273,6 +283,100 @@ driver_t *driver; } DEFAULT bhnd_bus_null_get_erom_class; +/** + * Register a shared bus @p provider for a given @p service. + * + * @param dev The parent of @p child. + * @param child The requesting child device. + * @param provider The service provider to register. + * @param service The service for which @p provider will be registered. + * + * @retval 0 success + * @retval EEXIST if an entry for @p service already exists. + * @retval non-zero if registering @p provider otherwise fails, a regular + * unix error code will be returned. + */ +METHOD int register_provider { + device_t dev; + device_t child; + device_t provider; + bhnd_service_t service; +} DEFAULT bhnd_bus_generic_register_provider; + + /** + * Attempt to remove the @p service provider registration for @p provider. + * + * @param dev The parent of @p child. + * @param child The requesting child device. + * @param provider The service provider to be deregistered. + * @param service The service for which @p provider will be deregistered, + * or BHND_SERVICE_INVALID to remove all service + * registrations for @p provider. + * + * @retval 0 success + * @retval EBUSY if active references to @p provider exist; @see + * BHND_BUS_RETAIN_PROVIDER() and + * BHND_BUS_RELEASE_PROVIDER(). + */ +METHOD int deregister_provider { + device_t dev; + device_t child; + device_t provider; + bhnd_service_t service; +} DEFAULT bhnd_bus_generic_deregister_provider; + +/** + * Retain and return a reference to the registered @p service provider, if any. + * + * @param dev The parent of @p child. + * @param child The requesting child device. + * @param service The service for which a provider should be returned. + * + * On success, the caller assumes ownership the returned provider, and + * is responsible for releasing this reference via + * BHND_BUS_RELEASE_PROVIDER(). + * + * @retval device_t success + * @retval NULL if no provider is registered for @p service. + */ +METHOD device_t retain_provider { + device_t dev; + device_t child; + bhnd_service_t service; +} DEFAULT bhnd_bus_generic_retain_provider; + + /** + * Release a reference to a service provider previously returned by + * BHND_BUS_RETAIN_PROVIDER(). + * + * @param dev The parent of @p child. + * @param child The requesting child device. + * @param provider The provider to be released. + * @param service The service for which @p provider was previously + * retained. + */ +METHOD void release_provider { + device_t dev; + device_t child; + device_t provider; + bhnd_service_t service; +} DEFAULT bhnd_bus_generic_release_provider; + +/** + * Return a struct bhnd_service_registry. + * + * Used by drivers which use bhnd_bus_generic_sr_register_provider() etc. + * to implement service provider registration. It should return a service + * registry that may be used to resolve provider requests from @p child. + * + * @param dev The parent of @p child. + * @param child The requesting child device. + */ +METHOD struct bhnd_service_registry * get_service_registry { + device_t dev; + device_t child; +} DEFAULT bhnd_bus_null_get_service_registry; + /** * Return the active host bridge core for the bhnd bus, if any. * Index: sys/dev/bhnd/bhnd_private.h =================================================================== --- /dev/null +++ sys/dev/bhnd/bhnd_private.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2017 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Landon Fuller under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _BHND_BHND_PRIVATE_H_ +#define _BHND_BHND_PRIVATE_H_ + +#include +#include +#include +#include + +#include "bhnd_types.h" + +/* + * Private bhnd(4) driver definitions. + */ + +/** + * A bhnd(4) service registry entry. + */ +struct bhnd_service_entry { + device_t provider; /**< service provider */ + bhnd_service_t service; /**< service implemented */ + uint32_t flags; /**< entry flags (see BHND_SPF_*) */ + volatile u_int refs; /**< reference count; updated atomically + with only a shared lock held */ + + STAILQ_ENTRY(bhnd_service_entry) link; +}; + +#endif /* _BHND_BHND_PRIVATE_H_ */ Index: sys/dev/bhnd/bhnd_subr.c =================================================================== --- sys/dev/bhnd/bhnd_subr.c +++ sys/dev/bhnd/bhnd_subr.c @@ -1,7 +1,11 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -32,6 +36,7 @@ #include #include +#include #include #include @@ -51,6 +56,14 @@ #include "bhndreg.h" #include "bhndvar.h" +#include "bhnd_private.h" + +static void bhnd_service_registry_free_entry( + struct bhnd_service_entry *entry); + +static int compare_ascending_probe_order(const void *lhs, const void *rhs); +static int compare_descending_probe_order(const void *lhs, + const void *rhs); /* BHND core device description table. */ static const struct bhnd_core_desc { @@ -331,9 +344,9 @@ } /** - * Find a @p class child device with @p unit on @p dev. + * Find a @p class child device with @p unit on @p bus. * - * @param parent The bhnd-compatible bus to be searched. + * @param bus The bhnd-compatible bus to be searched. * @param class The device class to match on. * @param unit The core unit number; specify -1 to return the first match * regardless of unit number. @@ -342,7 +355,7 @@ * @retval NULL if no matching child device is found. */ device_t -bhnd_find_child(device_t dev, bhnd_devclass_t class, int unit) +bhnd_bus_find_child(device_t bus, bhnd_devclass_t class, int unit) { struct bhnd_core_match md = { BHND_MATCH_CORE_CLASS(class), @@ -352,27 +365,27 @@ if (unit == -1) md.m.match.core_unit = 0; - return bhnd_match_child(dev, &md); + return bhnd_bus_match_child(bus, &md); } /** - * Find the first child device on @p dev that matches @p desc. + * Find the first child device on @p bus that matches @p desc. * - * @param parent The bhnd-compatible bus to be searched. + * @param bus The bhnd-compatible bus to be searched. * @param desc A match descriptor. * * @retval device_t if a matching child device is found. * @retval NULL if no matching child device is found. */ device_t -bhnd_match_child(device_t dev, const struct bhnd_core_match *desc) +bhnd_bus_match_child(device_t bus, const struct bhnd_core_match *desc) { device_t *devlistp; device_t match; int devcnt; int error; - error = device_get_children(dev, &devlistp, &devcnt); + error = device_get_children(bus, &devlistp, &devcnt); if (error != 0) return (NULL); @@ -391,6 +404,146 @@ return match; } +/** + * Retrieve an ordered list of all device instances currently connected to + * @p bus, returning a pointer to the array in @p devlistp and the count + * in @p ndevs. + * + * The memory allocated for the table must be freed via + * bhnd_bus_free_children(). + * + * @param bus The bhnd-compatible bus to be queried. + * @param[out] devlist The array of devices. + * @param[out] devcount The number of devices in @p devlistp + * @param order The order in which devices will be returned + * in @p devlist. + * + * @retval 0 success + * @retval non-zero if an error occurs, a regular unix error code will + * be returned. + */ +int +bhnd_bus_get_children(device_t bus, device_t **devlist, int *devcount, + bhnd_device_order order) +{ + int error; + + /* Fetch device array */ + if ((error = device_get_children(bus, devlist, devcount))) + return (error); + + /* Perform requested sorting */ + if ((error = bhnd_sort_devices(*devlist, *devcount, order))) { + bhnd_bus_free_children(*devlist); + return (error); + } + + return (0); +} + +/** + * Free any memory allocated in a previous call to bhnd_bus_get_children(). + * + * @param devlist The device array returned by bhnd_bus_get_children(). + */ +void +bhnd_bus_free_children(device_t *devlist) +{ + free(devlist, M_TEMP); +} + +/** + * Perform in-place sorting of an array of bhnd device instances. + * + * @param devlist An array of bhnd devices. + * @param devcount The number of devices in @p devs. + * @param order The sort order to be used. + */ +int +bhnd_sort_devices(device_t *devlist, size_t devcount, bhnd_device_order order) +{ + int (*compare)(const void *, const void *); + + switch (order) { + case BHND_DEVICE_ORDER_ATTACH: + compare = compare_ascending_probe_order; + break; + case BHND_DEVICE_ORDER_DETACH: + compare = compare_descending_probe_order; + break; + default: + printf("unknown sort order: %d\n", order); + return (EINVAL); + } + + qsort(devlist, devcount, sizeof(*devlist), compare); + return (0); +} + +/* + * Ascending comparison of bhnd device's probe order. + */ +static int +compare_ascending_probe_order(const void *lhs, const void *rhs) +{ + device_t ldev, rdev; + int lorder, rorder; + + ldev = (*(const device_t *) lhs); + rdev = (*(const device_t *) rhs); + + lorder = BHND_BUS_GET_PROBE_ORDER(device_get_parent(ldev), ldev); + rorder = BHND_BUS_GET_PROBE_ORDER(device_get_parent(rdev), rdev); + + if (lorder < rorder) { + return (-1); + } else if (lorder > rorder) { + return (1); + } else { + return (0); + } +} + +/* + * Descending comparison of bhnd device's probe order. + */ +static int +compare_descending_probe_order(const void *lhs, const void *rhs) +{ + return (compare_ascending_probe_order(rhs, lhs)); +} + +/** + * Call device_probe_and_attach() for each of the bhnd bus device's + * children, in bhnd attach order. + * + * @param bus The bhnd-compatible bus for which all children should be probed + * and attached. + */ +int +bhnd_bus_probe_children(device_t bus) +{ + device_t *devs; + int ndevs; + int error; + + /* Fetch children in attach order */ + error = bhnd_bus_get_children(bus, &devs, &ndevs, + BHND_DEVICE_ORDER_ATTACH); + if (error) + return (error); + + /* Probe and attach all children */ + for (int i = 0; i < ndevs; i++) { + device_t child = devs[i]; + device_probe_and_attach(child); + } + + bhnd_bus_free_children(devs); + + return (0); +} + /** * Walk up the bhnd device hierarchy to locate the root device * to which the bhndb bridge is attached. @@ -727,7 +880,7 @@ uint32_t dflags; parent = device_get_parent(dev); - hostb = bhnd_find_hostb_device(parent); + hostb = bhnd_bus_find_hostb_device(parent); attach_type = bhnd_get_attach_type(dev); for (entry = table; !BHND_DEVICE_IS_END(entry); entry = @@ -1344,6 +1497,276 @@ return (0); } +/** + * Initialize a service provider registry. + * + * @param bsr The service registry to initialize. + * + * @retval 0 success + * @retval non-zero if an error occurs initializing the service registry, + * a regular unix error code will be returned. + + */ +int +bhnd_service_registry_init(struct bhnd_service_registry *bsr) +{ + STAILQ_INIT(&bsr->entries); + sx_init(&bsr->lock, "bhnd_service_registry lock"); + + return (0); +} + +/** + * Release all resources held by @p bsr. + * + * @param bsr A service registry instance previously successfully + * initialized via bhnd_service_registry_init(). + * + * @retval 0 success + * @retval EBUSY if active references to service providers registered + * with @p bsr exist. + */ +int +bhnd_service_registry_fini(struct bhnd_service_registry *bsr) +{ + struct bhnd_service_entry *entry, *enext; + + /* Remove everthing we can */ + sx_xlock(&bsr->lock); + STAILQ_FOREACH_SAFE(entry, &bsr->entries, link, enext) { + if (entry->refs > 0) + continue; + + STAILQ_REMOVE(&bsr->entries, entry, bhnd_service_entry, link); + free(entry, M_BHND); + } + + if (!STAILQ_EMPTY(&bsr->entries)) { + sx_xunlock(&bsr->lock); + return (EBUSY); + } + sx_xunlock(&bsr->lock); + + sx_destroy(&bsr->lock); + return (0); +} + +/** + * Register a @p provider for the given @p service. + * + * @param bsr Service registry to be modified. + * @param provider Service provider to register. + * @param service Service for which @p provider will be registered. + * @param flags Service provider flags (see BHND_SPF_*). + * + * @retval 0 success + * @retval EEXIST if an entry for @p service already exists. + * @retval EINVAL if @p service is BHND_SERVICE_ANY. + * @retval non-zero if registering @p provider otherwise fails, a regular + * unix error code will be returned. + */ +int +bhnd_service_registry_add(struct bhnd_service_registry *bsr, device_t provider, + bhnd_service_t service, uint32_t flags) +{ + struct bhnd_service_entry *entry; + + if (service == BHND_SERVICE_ANY) + return (EINVAL); + + + sx_xlock(&bsr->lock); + + /* Is a service provider already registered? */ + STAILQ_FOREACH(entry, &bsr->entries, link) { + if (entry->service == service) { + sx_xunlock(&bsr->lock); + return (EEXIST); + } + } + + /* Initialize and insert our new entry */ + entry = malloc(sizeof(*entry), M_BHND, M_WAITOK); + + entry->provider = provider; + entry->service = service; + entry->flags = flags; + refcount_init(&entry->refs, 0); + + STAILQ_INSERT_HEAD(&bsr->entries, entry, link); + + sx_xunlock(&bsr->lock); + return (0); +} + +/** + * Free an unreferenced registry entry. + * + * @param entry The entry to be deallocated. + */ +static void +bhnd_service_registry_free_entry(struct bhnd_service_entry *entry) +{ + KASSERT(entry->refs == 0, ("provider has active references")); + free(entry, M_BHND); +} + +/** + * Attempt to remove the @p service provider registration for @p provider. + * + * @param bsr The service registry to be modified. + * @param provider The service provider to be deregistered. + * @param service The service for which @p provider will be deregistered, + * or BHND_SERVICE_ANY to remove all service + * registrations for @p provider. + * + * @retval 0 success + * @retval EBUSY if active references to @p provider exist; @see + * bhnd_service_registry_retain() and + * bhnd_service_registry_release(). + */ +int +bhnd_service_registry_remove(struct bhnd_service_registry *bsr, + device_t provider, bhnd_service_t service) +{ + struct bhnd_service_entry *entry, *enext; + + /* An exclusive lock gaurantees that entry refcounts will not + * be modified out from under us */ + sx_xlock(&bsr->lock); + +#define BHND_PROV_MATCH(_e) \ + ((_e)->provider == provider && \ + (service == BHND_SERVICE_ANY || (_e)->service == service)) + + /* Validate matching provider entries before making any + * modifications */ + STAILQ_FOREACH(entry, &bsr->entries, link) { + /* Skip non-matching entries */ + if (!BHND_PROV_MATCH(entry)) + continue; + + /* Entry is in use? */ + if (entry->refs > 0) { + sx_xunlock(&bsr->lock); + return (EBUSY); + } + } + + /* We can now safely remove matching entries */ + STAILQ_FOREACH_SAFE(entry, &bsr->entries, link, enext) { + /* Skip non-matching entries */ + if (!BHND_PROV_MATCH(entry)) + continue; + + /* Remove from list */ + STAILQ_REMOVE(&bsr->entries, entry, bhnd_service_entry, link); + + /* Free provider entry */ + bhnd_service_registry_free_entry(entry); + } +#undef BHND_PROV_MATCH + + sx_xunlock(&bsr->lock); + return (0); +} + +/** + * Retain and return a reference to a registered @p service provider, if any. + * + * @param bsr The service registry to be queried. + * @param service The service for which a provider should be returned. + * + * On success, the caller assumes ownership the returned provider, and + * is responsible for releasing this reference via + * bhnd_service_registry_release(). + * + * @retval device_t success + * @retval NULL if no provider is registered for @p service. + */ +device_t +bhnd_service_registry_retain(struct bhnd_service_registry *bsr, + bhnd_service_t service) +{ + struct bhnd_service_entry *entry; + + sx_slock(&bsr->lock); + STAILQ_FOREACH(entry, &bsr->entries, link) { + if (entry->service != service) + continue; + + /* With a live refcount, entry is gauranteed to remain alive + * after we release our lock */ + refcount_acquire(&entry->refs); + + sx_sunlock(&bsr->lock); + return (entry->provider); + } + sx_sunlock(&bsr->lock); + + /* Not found */ + return (NULL); +} + +/** + * Release a reference to a service provider previously returned by + * bhnd_service_registry_retain(). + * + * If this is the last reference to an inherited service provider registration + * (@see BHND_SPF_INHERITED), the registration will also be removed, and + * true will be returned. + * + * @param bsr The service registry from which @p provider + * was returned. + * @param provider The provider to be released. + * @param service The service for which @p provider was previously + * retained. + * @retval true The inherited service provider registration was removed; + * the caller should release its own reference to the + * provider. + * @retval false The service provider was not inherited, or active + * references to the provider remain. + */ +bool +bhnd_service_registry_release(struct bhnd_service_registry *bsr, + device_t provider, bhnd_service_t service) +{ + struct bhnd_service_entry *entry; + + /* Exclusive lock, as we need to prevent any new references to the + * entry from being taken if it's to be removed */ + sx_xlock(&bsr->lock); + STAILQ_FOREACH(entry, &bsr->entries, link) { + bool removed; + + if (entry->provider != provider) + continue; + + if (entry->service != service) + continue; + + if (refcount_release(&entry->refs) && + (entry->flags & BHND_SPF_INHERITED)) + { + /* If an inherited entry is no longer actively + * referenced, remove the local registration and inform + * the caller. */ + STAILQ_REMOVE(&bsr->entries, entry, bhnd_service_entry, + link); + bhnd_service_registry_free_entry(entry); + removed = true; + } else { + removed = false; + } + + sx_xunlock(&bsr->lock); + return (removed); + } + + /* Caller owns a reference, but no such provider is registered? */ + panic("invalid service provider reference"); +} + /** * Using the bhnd(4) bus-level core information and a custom core name, * populate @p dev's device description. @@ -1427,6 +1850,222 @@ } +/** + * Helper function for implementing BHND_BUS_REGISTER_PROVIDER(). + * + * This implementation delegates the request to the BHND_BUS_REGISTER_PROVIDER() + * method on the parent of @p dev. If no parent exists, the implementation + * will return an error. + */ +int +bhnd_bus_generic_register_provider(device_t dev, device_t child, + device_t provider, bhnd_service_t service) +{ + device_t parent = device_get_parent(dev); + + if (parent != NULL) { + return (BHND_BUS_REGISTER_PROVIDER(parent, child, + provider, service)); + } + + return (ENXIO); +} + +/** + * Helper function for implementing BHND_BUS_DEREGISTER_PROVIDER(). + * + * This implementation delegates the request to the + * BHND_BUS_DEREGISTER_PROVIDER() method on the parent of @p dev. If no parent + * exists, the implementation will panic. + */ +int +bhnd_bus_generic_deregister_provider(device_t dev, device_t child, + device_t provider, bhnd_service_t service) +{ + device_t parent = device_get_parent(dev); + + if (parent != NULL) { + return (BHND_BUS_DEREGISTER_PROVIDER(parent, child, + provider, service)); + } + + panic("missing BHND_BUS_DEREGISTER_PROVIDER()"); +} + +/** + * Helper function for implementing BHND_BUS_RETAIN_PROVIDER(). + * + * This implementation delegates the request to the + * BHND_BUS_DEREGISTER_PROVIDER() method on the parent of @p dev. If no parent + * exists, the implementation will return NULL. + */ +device_t +bhnd_bus_generic_retain_provider(device_t dev, device_t child, + bhnd_service_t service) +{ + device_t parent = device_get_parent(dev); + + if (parent != NULL) { + return (BHND_BUS_RETAIN_PROVIDER(parent, child, + service)); + } + + return (NULL); +} + +/** + * Helper function for implementing BHND_BUS_RELEASE_PROVIDER(). + * + * This implementation delegates the request to the + * BHND_BUS_DEREGISTER_PROVIDER() method on the parent of @p dev. If no parent + * exists, the implementation will panic. + */ +void +bhnd_bus_generic_release_provider(device_t dev, device_t child, + device_t provider, bhnd_service_t service) +{ + device_t parent = device_get_parent(dev); + + if (parent != NULL) { + return (BHND_BUS_RELEASE_PROVIDER(parent, child, + provider, service)); + } + + panic("missing BHND_BUS_RELEASE_PROVIDER()"); +} + +/** + * Helper function for implementing BHND_BUS_REGISTER_PROVIDER(). + * + * This implementation uses the bhnd_service_registry_add() function to + * do most of the work. It calls BHND_BUS_GET_SERVICE_REGISTRY() to find + * a suitable service registry to edit. + */ +int +bhnd_bus_generic_sr_register_provider(device_t dev, device_t child, + device_t provider, bhnd_service_t service) +{ + struct bhnd_service_registry *bsr; + + bsr = BHND_BUS_GET_SERVICE_REGISTRY(dev, child); + + KASSERT(bsr != NULL, ("NULL service registry")); + + return (bhnd_service_registry_add(bsr, provider, service, 0)); +} + +/** + * Helper function for implementing BHND_BUS_DEREGISTER_PROVIDER(). + * + * This implementation uses the bhnd_service_registry_remove() function to + * do most of the work. It calls BHND_BUS_GET_SERVICE_REGISTRY() to find + * a suitable service registry to edit. + */ +int +bhnd_bus_generic_sr_deregister_provider(device_t dev, device_t child, + device_t provider, bhnd_service_t service) +{ + struct bhnd_service_registry *bsr; + + bsr = BHND_BUS_GET_SERVICE_REGISTRY(dev, child); + + KASSERT(bsr != NULL, ("NULL service registry")); + + return (bhnd_service_registry_remove(bsr, provider, service)); +} + +/** + * Helper function for implementing BHND_BUS_RETAIN_PROVIDER(). + * + * This implementation uses the bhnd_service_registry_retain() function to + * do most of the work. It calls BHND_BUS_GET_SERVICE_REGISTRY() to find + * a suitable service registry. + * + * If a local provider for the service is not available, and a parent device is + * available, this implementation will attempt to fetch and locally register + * a service provider reference from the parent of @p dev. + */ +device_t +bhnd_bus_generic_sr_retain_provider(device_t dev, device_t child, + bhnd_service_t service) +{ + struct bhnd_service_registry *bsr; + device_t parent, provider; + int error; + + bsr = BHND_BUS_GET_SERVICE_REGISTRY(dev, child); + KASSERT(bsr != NULL, ("NULL service registry")); + + /* + * Attempt to fetch a service provider reference from either the local + * service registry, or if not found, from our parent. + * + * If we fetch a provider from our parent, we register the provider + * with the local service registry to prevent conflicting local + * registrations from being added. + */ + while (1) { + /* Check the local service registry first */ + provider = bhnd_service_registry_retain(bsr, service); + if (provider != NULL) + return (provider); + + /* Otherwise, try to delegate to our parent (if any) */ + if ((parent = device_get_parent(dev)) == NULL) + return (NULL); + + provider = BHND_BUS_RETAIN_PROVIDER(parent, dev, service); + if (provider == NULL) + return (NULL); + + /* Register the inherited service registration with the local + * registry */ + error = bhnd_service_registry_add(bsr, provider, service, + BHND_SPF_INHERITED); + if (error) { + BHND_BUS_RELEASE_PROVIDER(parent, dev, provider, + service); + if (error == EEXIST) { + /* A valid service provider was registered + * concurrently; retry fetching from the local + * registry */ + continue; + } + + device_printf(dev, "failed to register service " + "provider: %d\n", error); + return (NULL); + } + } +} + +/** + * Helper function for implementing BHND_BUS_RELEASE_PROVIDER(). + * + * This implementation uses the bhnd_service_registry_release() function to + * do most of the work. It calls BHND_BUS_GET_SERVICE_REGISTRY() to find + * a suitable service registry. + */ +void +bhnd_bus_generic_sr_release_provider(device_t dev, device_t child, + device_t provider, bhnd_service_t service) +{ + struct bhnd_service_registry *bsr; + + bsr = BHND_BUS_GET_SERVICE_REGISTRY(dev, child); + KASSERT(bsr != NULL, ("NULL service registry")); + + /* Release the provider reference; if the refcount hits zero on an + * inherited reference, true will be returned, and we need to drop + * our own bus reference to the provider */ + if (!bhnd_service_registry_release(bsr, provider, service)) + return; + + /* Drop our reference to the borrowed provider */ + BHND_BUS_RELEASE_PROVIDER(device_get_parent(dev), dev, provider, + service); +} + /** * Helper function for implementing BHND_BUS_IS_HW_DISABLED(). * Index: sys/dev/bhnd/bhnd_types.h =================================================================== --- sys/dev/bhnd/bhnd_types.h +++ sys/dev/bhnd/bhnd_types.h @@ -1,7 +1,11 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -65,6 +69,14 @@ BHND_DEVCLASS_INVALID /**< no/invalid class */ } bhnd_devclass_t; +/** bhnd(4) platform services. */ +typedef enum { + BHND_SERVICE_CHIPC, /**< chipcommon service; implements the bhnd_chipc interface */ + BHND_SERVICE_PMU, /**< pmu service; implements the bhnd_pmu interface */ + BHND_SERVICE_NVRAM, /**< nvram service; implements the bhnd_nvram interface */ + + BHND_SERVICE_ANY = 1000, /**< match on any service type */ +} bhnd_service_t; /** * bhnd(4) port types. Index: sys/dev/bhnd/bhndb/bhnd_bhndb.c =================================================================== --- sys/dev/bhnd/bhndb/bhnd_bhndb.c +++ sys/dev/bhnd/bhndb/bhnd_bhndb.c @@ -1,7 +1,11 @@ /*- * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -90,7 +94,7 @@ /* Find the corresponding bus device */ md = bhnd_core_get_match_desc(&core); - return (bhnd_match_child(dev, &md)); + return (bhnd_bus_match_child(dev, &md)); } static int Index: sys/dev/bhnd/bhndb/bhndb.c =================================================================== --- sys/dev/bhnd/bhndb/bhndb.c +++ sys/dev/bhnd/bhndb/bhndb.c @@ -1,7 +1,11 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -522,14 +526,16 @@ sc->parent_dev = device_get_parent(dev); sc->bridge_class = bridge_devclass; + if ((error = bhnd_service_registry_init(&sc->services))) + return (error); + BHNDB_LOCK_INIT(sc); /* Populate generic resource allocation state. */ cfg = BHNDB_BUS_GET_GENERIC_HWCFG(sc->parent_dev, sc->dev); sc->bus_res = bhndb_alloc_resources(dev, sc->parent_dev, cfg); - if (sc->bus_res == NULL) { - return (ENXIO); - } + if (sc->bus_res == NULL) + goto failed; /* Allocate our host resources */ if ((error = bhndb_alloc_host_resources(sc->bus_res))) @@ -573,6 +579,8 @@ if (sc->bus_res != NULL) bhndb_free_resources(sc->bus_res); + bhnd_service_registry_fini(&sc->services); + return (error); } @@ -911,11 +919,15 @@ int error; sc = device_get_softc(dev); - + /* Detach children */ if ((error = bus_generic_detach(dev))) return (error); + /* Clean up our service registry */ + if ((error = bhnd_service_registry_fini(&sc->services))) + return (error); + /* Clean up our driver state. */ bhndb_free_resources(sc->bus_res); @@ -1211,6 +1223,17 @@ return (0); } +/** + * Default bhndb(4) implementation of BHND_BUS_GET_SERVICE_REGISTRY(). + */ +static struct bhnd_service_registry * +bhndb_get_service_registry(device_t dev, device_t child) +{ + struct bhndb_softc *sc = device_get_softc(dev); + + return (&sc->services); +} + /** * Default bhndb(4) implementation of BUS_ALLOC_RESOURCE(). */ @@ -2146,6 +2169,13 @@ DEVMETHOD(bhnd_bus_activate_resource, bhndb_activate_bhnd_resource), DEVMETHOD(bhnd_bus_deactivate_resource, bhndb_deactivate_bhnd_resource), DEVMETHOD(bhnd_bus_get_nvram_var, bhnd_bus_generic_get_nvram_var), + + DEVMETHOD(bhnd_bus_get_service_registry,bhndb_get_service_registry), + DEVMETHOD(bhnd_bus_register_provider, bhnd_bus_generic_sr_register_provider), + DEVMETHOD(bhnd_bus_deregister_provider, bhnd_bus_generic_sr_deregister_provider), + DEVMETHOD(bhnd_bus_retain_provider, bhnd_bus_generic_sr_retain_provider), + DEVMETHOD(bhnd_bus_release_provider, bhnd_bus_generic_sr_release_provider), + DEVMETHOD(bhnd_bus_read_1, bhndb_bus_read_1), DEVMETHOD(bhnd_bus_read_2, bhndb_bus_read_2), DEVMETHOD(bhnd_bus_read_4, bhndb_bus_read_4), Index: sys/dev/bhnd/bhndb/bhndbvar.h =================================================================== --- sys/dev/bhnd/bhndb/bhndbvar.h +++ sys/dev/bhnd/bhndb/bhndbvar.h @@ -1,7 +1,11 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -96,6 +100,8 @@ device_t parent_dev; /**< parent device */ device_t bus_dev; /**< child bhnd(4) bus */ + struct bhnd_service_registry services; /**< local service registry */ + struct mtx sc_mtx; /**< resource lock. */ struct bhndb_resources *bus_res; /**< bus resource state */ }; Index: sys/dev/bhnd/bhndvar.h =================================================================== --- sys/dev/bhnd/bhndvar.h +++ sys/dev/bhnd/bhndvar.h @@ -1,7 +1,11 @@ /*- * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -34,7 +38,10 @@ #include #include +#include #include +#include +#include #include "bhnd.h" @@ -45,57 +52,51 @@ MALLOC_DECLARE(M_BHND); DECLARE_CLASS(bhnd_driver); -int bhnd_generic_attach(device_t dev); -int bhnd_generic_detach(device_t dev); -int bhnd_generic_shutdown(device_t dev); -int bhnd_generic_resume(device_t dev); -int bhnd_generic_suspend(device_t dev); +int bhnd_generic_attach(device_t dev); +int bhnd_generic_detach(device_t dev); +int bhnd_generic_shutdown(device_t dev); +int bhnd_generic_resume(device_t dev); +int bhnd_generic_suspend(device_t dev); -int bhnd_generic_get_probe_order(device_t dev, - device_t child); +int bhnd_generic_get_probe_order(device_t dev, + device_t child); -int bhnd_generic_alloc_pmu(device_t dev, - device_t child); -int bhnd_generic_release_pmu(device_t dev, - device_t child); -int bhnd_generic_request_clock(device_t dev, - device_t child, bhnd_clock clock); -int bhnd_generic_enable_clocks(device_t dev, - device_t child, uint32_t clocks); -int bhnd_generic_request_ext_rsrc(device_t dev, - device_t child, u_int rsrc); -int bhnd_generic_release_ext_rsrc(device_t dev, - device_t child, u_int rsrc); +int bhnd_generic_alloc_pmu(device_t dev, + device_t child); +int bhnd_generic_release_pmu(device_t dev, + device_t child); +int bhnd_generic_request_clock(device_t dev, + device_t child, bhnd_clock clock); +int bhnd_generic_enable_clocks(device_t dev, + device_t child, uint32_t clocks); +int bhnd_generic_request_ext_rsrc(device_t dev, + device_t child, u_int rsrc); +int bhnd_generic_release_ext_rsrc(device_t dev, + device_t child, u_int rsrc); -int bhnd_generic_print_child(device_t dev, - device_t child); -void bhnd_generic_probe_nomatch(device_t dev, - device_t child); +int bhnd_generic_print_child(device_t dev, + device_t child); +void bhnd_generic_probe_nomatch(device_t dev, + device_t child); -void bhnd_generic_child_deleted(device_t dev, - device_t child); -int bhnd_generic_suspend_child(device_t dev, - device_t child); -int bhnd_generic_resume_child(device_t dev, - device_t child); +void bhnd_generic_child_deleted(device_t dev, + device_t child); +int bhnd_generic_suspend_child(device_t dev, + device_t child); +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, bhnd_nvram_type type); +int bhnd_generic_get_nvram_var(device_t dev, + device_t child, const char *name, + void *buf, size_t *size, + bhnd_nvram_type type); /** * bhnd driver instance state. Must be first member of all subclass * softc structures. */ 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 */ + device_t dev; /**< bus device */ }; #endif /* _BHND_BHNDVAR_H_ */ Index: sys/dev/bhnd/cores/chipc/chipc.c =================================================================== --- sys/dev/bhnd/cores/chipc/chipc.c +++ sys/dev/bhnd/cores/chipc/chipc.c @@ -1,8 +1,12 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * Copyright (c) 2016 Michael Zhilin + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * This software was developed by Landon Fuller under sponsorship from + * the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -211,6 +215,10 @@ if ((error = bus_generic_attach(dev))) goto failed; + /* Register ourselves with the bus */ + if ((error = bhnd_register_provider(dev, BHND_SERVICE_CHIPC))) + goto failed; + return (0); failed: @@ -234,6 +242,9 @@ sc = device_get_softc(dev); + if ((error = bhnd_deregister_provider(dev, BHND_SERVICE_ANY))) + return (error); + if ((error = bus_generic_detach(dev))) return (error); @@ -1087,7 +1098,7 @@ mtx_lock(&Giant); /* for newbus */ parent = device_get_parent(sc->dev); - hostb = bhnd_find_hostb_device(parent); + hostb = bhnd_bus_find_hostb_device(parent); if ((error = device_get_children(parent, &devs, &devcount))) { mtx_unlock(&Giant); Index: sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c =================================================================== --- sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c +++ sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c @@ -1,7 +1,11 @@ /*- * Copyright (c) 2016 Landon Fuller - * Copyright (c) 2010, Broadcom Corporation. + * Copyright (c) 2010 Broadcom Corporation. + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. + * + * This software was developed by Landon Fuller under sponsorship from + * the FreeBSD Foundation. * * This file is derived from the siutils.c source distributed with the * Asus RT-N16 firmware source code release. @@ -116,6 +120,7 @@ struct chipc_softc *chipc_sc; bhnd_devclass_t hostb_class; device_t hostb_dev; + device_t bus; int error; sc = device_get_softc(dev); @@ -128,10 +133,12 @@ sc->quirks = bhnd_device_quirks(sc->chipc_dev, pwrctl_devices, sizeof(pwrctl_devices[0])); + bus = device_get_parent(sc->chipc_dev); + /* On devices that lack a slow clock source, HT must always be * enabled. */ hostb_class = BHND_DEVCLASS_INVALID; - hostb_dev = bhnd_find_hostb_device(device_get_parent(sc->chipc_dev)); + hostb_dev = bhnd_bus_find_hostb_device(device_get_parent(sc->chipc_dev)); if (hostb_dev != NULL) hostb_class = bhnd_get_class(hostb_dev); @@ -177,6 +184,13 @@ PWRCTL_UNLOCK(sc); + /* Register as the bus PMU provider */ + if ((error = bhnd_register_provider(dev, BHND_SERVICE_PMU))) { + device_printf(sc->dev, "failed to register PMU with bus : %d\n", + error); + goto cleanup; + } + return (0); cleanup: @@ -193,9 +207,16 @@ sc = device_get_softc(dev); + if ((error = bhnd_deregister_provider(dev, BHND_SERVICE_ANY))) + return (error); + + PWRCTL_LOCK(sc); + if ((error = bhnd_pwrctl_setclk(sc, BHND_CLOCK_DYN))) return (error); + PWRCTL_UNLOCK(sc); + STAILQ_FOREACH_SAFE(clkres, &sc->clkres_list, cr_link, crnext) free(clkres, M_DEVBUF); Index: sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c =================================================================== --- sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c +++ sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c @@ -1,7 +1,11 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -561,7 +565,7 @@ bus_size_t reg; bhnd = device_get_parent(sc->dev); - chipc = bhnd_find_child(bhnd, BHND_DEVCLASS_CC, 0); + chipc = bhnd_bus_find_child(bhnd, BHND_DEVCLASS_CC, 0); KASSERT(chipc != NULL, ("missing chipcommon device")); /* Write SerDes PLL disable flag to the ChipCommon core */ Index: sys/dev/bhnd/cores/pmu/bhnd_pmu.c =================================================================== --- sys/dev/bhnd/cores/pmu/bhnd_pmu.c +++ sys/dev/bhnd/cores/pmu/bhnd_pmu.c @@ -1,7 +1,11 @@ /*- * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -128,7 +132,7 @@ /* Fetch capability flags */ sc->caps = bhnd_bus_read_4(sc->res, BHND_PMU_CAP); - /* Find the bus-attached core */ + /* Find the bus and bus-attached core */ bhnd_class = devclass_find("bhnd"); core = sc->dev; while ((bus = device_get_parent(core)) != NULL) { @@ -153,7 +157,7 @@ } /* Locate ChipCommon device */ - sc->chipc_dev = bhnd_find_child(bus, BHND_DEVCLASS_CC, 0); + sc->chipc_dev = bhnd_bus_find_child(bus, BHND_DEVCLASS_CC, 0); if (sc->chipc_dev == NULL) { device_printf(sc->dev, "chipcommon device not found\n"); return (ENXIO); @@ -186,6 +190,13 @@ goto failed; } + /* Register ourselves with the bus */ + if ((error = bhnd_register_provider(dev, BHND_SERVICE_PMU))) { + device_printf(sc->dev, "failed to register PMU with bus : %d\n", + error); + goto failed; + } + /* Set up sysctl nodes */ ctx = device_get_sysctl_ctx(dev); tree = device_get_sysctl_tree(dev); @@ -217,9 +228,13 @@ bhnd_pmu_detach(device_t dev) { struct bhnd_pmu_softc *sc; + int error; sc = device_get_softc(dev); + if ((error = bhnd_deregister_provider(dev, BHND_SERVICE_ANY))) + return (error); + BPMU_LOCK_DESTROY(sc); bhnd_pmu_query_fini(&sc->query); Index: sys/dev/bhnd/nvram/bhnd_sprom.c =================================================================== --- sys/dev/bhnd/nvram/bhnd_sprom.c +++ sys/dev/bhnd/nvram/bhnd_sprom.c @@ -1,7 +1,11 @@ /*- * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -99,8 +103,10 @@ sc = device_get_softc(dev); sc->dev = dev; + sc->store = NULL; io = NULL; + r = NULL; /* Allocate SPROM resource */ rid = 0; @@ -138,6 +144,16 @@ bhnd_nvram_io_free(io); bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r); + io = NULL; + r = NULL; + + /* Register ourselves with the bus */ + if ((error = bhnd_register_provider(dev, BHND_SERVICE_NVRAM))) { + device_printf(dev, "failed to register NVRAM provider: %d\n", + error); + goto failed; + } + return (0); failed: @@ -145,7 +161,11 @@ if (io != NULL) bhnd_nvram_io_free(io); - bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r); + if (r != NULL) + bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r); + + if (sc->store != NULL) + bhnd_nvram_store_free(sc->store); return (error); } @@ -175,9 +195,13 @@ bhnd_sprom_detach(device_t dev) { struct bhnd_sprom_softc *sc; + int error; sc = device_get_softc(dev); + if ((error = bhnd_deregister_provider(dev, BHND_SERVICE_ANY))) + return (error); + bhnd_nvram_store_free(sc->store); return (0); Index: sys/dev/bhnd/siba/siba_bhndb.c =================================================================== --- sys/dev/bhnd/siba/siba_bhndb.c +++ sys/dev/bhnd/siba/siba_bhndb.c @@ -1,7 +1,11 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -222,14 +226,14 @@ uint32_t imcfg; /* Only applies when bridged by PCIe */ - if ((hostb_dev = bhnd_find_hostb_device(sc->dev)) == NULL) + if ((hostb_dev = bhnd_bus_find_hostb_device(sc->dev)) == NULL) return (ENXIO); if (bhnd_get_class(hostb_dev) != BHND_DEVCLASS_PCIE) return (0); /* Only applies if there's a D11 core */ - d11 = bhnd_match_child(sc->dev, &(struct bhnd_core_match) { + d11 = bhnd_bus_match_child(sc->dev, &(struct bhnd_core_match) { BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_D11), BHND_MATCH_CORE_UNIT(0) }); @@ -259,7 +263,7 @@ uint32_t quirks; int error; - if ((hostb_dev = bhnd_find_hostb_device(sc->dev)) == NULL) + if ((hostb_dev = bhnd_bus_find_hostb_device(sc->dev)) == NULL) return (ENXIO); quirks = bhnd_device_quirks(hostb_dev, bridge_devs, Index: sys/mips/broadcom/bcm_machdep.h =================================================================== --- sys/mips/broadcom/bcm_machdep.h +++ sys/mips/broadcom/bcm_machdep.h @@ -1,7 +1,11 @@ /*- * Copyright (c) 2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -45,33 +49,35 @@ extern const struct bhnd_pmu_io bcm_pmu_soc_io; struct bcm_platform { - struct bhnd_chipid cid; /**< chip id */ - struct bhnd_core_info cc_id; /**< chipc core info */ - uintptr_t cc_addr; /**< chipc core phys address */ - uint32_t cc_caps; /**< chipc capabilities */ - uint32_t cc_caps_ext; /**< chipc extended capabilies */ + struct bhnd_chipid cid; /**< chip id */ + struct bhnd_core_info cc_id; /**< chipc core info */ + uintptr_t cc_addr; /**< chipc core phys address */ + uint32_t cc_caps; /**< chipc capabilities */ + uint32_t cc_caps_ext; /**< chipc extended capabilies */ /* On non-AOB devices, the PMU register block is mapped to chipc; * the pmu_id and pmu_addr values will be copied from cc_id * and cc_addr. */ - struct bhnd_core_info pmu_id; /**< PMU core info */ - uintptr_t pmu_addr; /**< PMU core phys address, or + struct bhnd_core_info pmu_id; /**< PMU core info */ + uintptr_t pmu_addr; /**< PMU core phys address, or 0x0 if no PMU */ - struct bhnd_pmu_query pmu; /**< PMU query instance */ + struct bhnd_pmu_query pmu; /**< PMU query instance */ - bhnd_erom_class_t *erom_impl; /**< erom parser class */ - struct kobj_ops erom_ops; /**< compiled kobj opcache */ + bhnd_erom_class_t *erom_impl; /**< erom parser class */ + struct kobj_ops erom_ops; /**< compiled kobj opcache */ union { bhnd_erom_static_t data; bhnd_erom_t obj; } erom; - struct bhnd_nvram_io *nvram_io; /**< NVRAM I/O context, or NULL if unavailable */ - bhnd_nvram_data_class *nvram_cls; /**< NVRAM data class, or NULL if unavailable */ + struct bhnd_nvram_io *nvram_io; /**< NVRAM I/O context, or NULL if unavailable */ + bhnd_nvram_data_class *nvram_cls; /**< NVRAM data class, or NULL if unavailable */ + + struct bhnd_service_registry services; /**< platform service providers */ #ifdef CFE - int cfe_console; /**< Console handle, or -1 */ + int cfe_console; /**< Console handle, or -1 */ #endif }; Index: sys/mips/broadcom/bcm_machdep.c =================================================================== --- sys/mips/broadcom/bcm_machdep.c +++ sys/mips/broadcom/bcm_machdep.c @@ -2,9 +2,12 @@ * Copyright (c) 2007 Bruce M. Simpson. * Copyright (c) 2016 Michael Zhilin * Copyright (c) 2016 Landon Fuller - * + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -393,6 +396,12 @@ } } + /* Initialize our platform service registry */ + if ((error = bhnd_service_registry_init(&bp->services))) { + BCM_ERR("error initializing service registry: %d\n", error); + return (error); + } + bcm_platform_data_avail = true; return (0); } Index: sys/mips/broadcom/bcm_nvram_cfe.c =================================================================== --- sys/mips/broadcom/bcm_nvram_cfe.c +++ sys/mips/broadcom/bcm_nvram_cfe.c @@ -1,7 +1,11 @@ /*- * Copyright (c) 2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -120,6 +124,13 @@ if (error) return (error); + error = bhnd_service_registry_add(&bp->services, dev, + BHND_SERVICE_NVRAM, 0); + if (error) { + bhnd_nvram_store_free(sc->store); + return (error); + } + return (error); } @@ -138,10 +149,18 @@ static int bhnd_nvram_cfe_detach(device_t dev) { - struct bhnd_nvram_cfe_softc *sc; + struct bcm_platform *bp; + struct bhnd_nvram_cfe_softc *sc; + int error; + bp = bcm_get_platform(); sc = device_get_softc(dev); + error = bhnd_service_registry_remove(&bp->services, dev, + BHND_SERVICE_ANY); + if (error) + return (error); + bhnd_nvram_store_free(sc->store); return (0); Index: sys/mips/broadcom/bhnd_nexus.c =================================================================== --- sys/mips/broadcom/bhnd_nexus.c +++ sys/mips/broadcom/bhnd_nexus.c @@ -1,7 +1,11 @@ /*- * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -54,6 +58,17 @@ #include "bhnd_nexusvar.h" + +/** + * Default bhnd_nexus implementation of BHND_BUS_GET_SERVICE_REGISTRY(). + */ +static struct bhnd_service_registry * +bhnd_nexus_get_service_registry(device_t dev, device_t child) +{ + struct bcm_platform *bp = bcm_get_platform(); + return (&bp->services); +} + /** * Default bhnd_nexus implementation of BHND_BUS_ACTIVATE_RESOURCE(). */ @@ -160,6 +175,11 @@ static device_method_t bhnd_nexus_methods[] = { /* bhnd interface */ + DEVMETHOD(bhnd_bus_get_service_registry,bhnd_nexus_get_service_registry), + DEVMETHOD(bhnd_bus_register_provider, bhnd_bus_generic_sr_register_provider), + DEVMETHOD(bhnd_bus_deregister_provider, bhnd_bus_generic_sr_deregister_provider), + DEVMETHOD(bhnd_bus_retain_provider, bhnd_bus_generic_sr_retain_provider), + DEVMETHOD(bhnd_bus_release_provider, bhnd_bus_generic_sr_release_provider), DEVMETHOD(bhnd_bus_activate_resource, bhnd_nexus_activate_resource), DEVMETHOD(bhnd_bus_deactivate_resource, bhnd_nexus_deactivate_resource), DEVMETHOD(bhnd_bus_is_hw_disabled, bhnd_nexus_is_hw_disabled),