aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/bhnd/bhnd.c410
-rw-r--r--sys/dev/bhnd/bhnd.h175
-rw-r--r--sys/dev/bhnd/bhnd_bus_if.m104
-rw-r--r--sys/dev/bhnd/bhnd_private.h57
-rw-r--r--sys/dev/bhnd/bhnd_subr.c660
-rw-r--r--sys/dev/bhnd/bhnd_types.h14
-rw-r--r--sys/dev/bhnd/bhndb/bhnd_bhndb.c6
-rw-r--r--sys/dev/bhnd/bhndb/bhndb.c40
-rw-r--r--sys/dev/bhnd/bhndb/bhndbvar.h8
-rw-r--r--sys/dev/bhnd/bhndvar.h81
-rw-r--r--sys/dev/bhnd/cores/chipc/chipc.c13
-rw-r--r--sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c25
-rw-r--r--sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c8
-rw-r--r--sys/dev/bhnd/cores/pmu/bhnd_pmu.c19
-rw-r--r--sys/dev/bhnd/nvram/bhnd_sprom.c26
-rw-r--r--sys/dev/bhnd/siba/siba_bhndb.c12
-rw-r--r--sys/mips/broadcom/bcm_machdep.c11
-rw-r--r--sys/mips/broadcom/bcm_machdep.h32
-rw-r--r--sys/mips/broadcom/bcm_nvram_cfe.c21
-rw-r--r--sys/mips/broadcom/bhnd_nexus.c20
20 files changed, 1308 insertions, 434 deletions
diff --git a/sys/dev/bhnd/bhnd.c b/sys/dev/bhnd/bhnd.c
index 104ca8ecb44a..f4c1ea29cf81 100644
--- a/sys/dev/bhnd/bhnd.c
+++ b/sys/dev/bhnd/bhnd.c
@@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
+ * 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 @@ __FBSDID("$FreeBSD$");
#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 @@ static const struct bhnd_nomatch {
{ 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 @@ int
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 @@ bhnd_generic_attach(device_t dev)
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 @@ bhnd_delete_children(struct bhnd_softc *sc)
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 @@ bhnd_delete_children(struct bhnd_softc *sc)
}
cleanup:
- free(devs, M_TEMP);
+ bhnd_bus_free_children(devs);
return (error);
}
@@ -193,12 +164,17 @@ int
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 @@ bhnd_generic_shutdown(device_t dev)
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 @@ bhnd_generic_shutdown(device_t dev)
}
cleanup:
- free(devs, M_TEMP);
+ bhnd_bus_free_children(devs);
return (error);
}
@@ -253,10 +231,13 @@ bhnd_generic_resume(device_t dev)
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 @@ bhnd_generic_resume(device_t dev)
}
cleanup:
- free(devs, M_TEMP);
+ bhnd_bus_free_children(devs);
return (error);
}
@@ -289,11 +270,13 @@ bhnd_generic_suspend(device_t dev)
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 @@ bhnd_generic_suspend(device_t dev)
}
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 @@ bhnd_generic_get_probe_order(device_t dev, device_t child)
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 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
{
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 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
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 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
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 @@ bhnd_generic_release_pmu(device_t dev, device_t child)
{
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 @@ bhnd_generic_is_region_valid(device_t dev, device_t child,
/**
* 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 @@ bhnd_generic_get_nvram_var(device_t dev, device_t child, const char *name,
{
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 @@ bhnd_generic_child_deleted(device_t dev, device_t child)
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 @@ static device_method_t bhnd_methods[] = {
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),
diff --git a/sys/dev/bhnd/bhnd.h b/sys/dev/bhnd/bhnd.h
index 2532c9d07092..33e34344968a 100644
--- a/sys/dev/bhnd/bhnd.h
+++ b/sys/dev/bhnd/bhnd.h
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * 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 <sys/types.h>
+#include <sys/param.h>
#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
#include <machine/bus.h>
@@ -289,6 +295,35 @@ struct bhnd_device {
#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 mtx 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 @@ bhnd_devclass_t bhnd_core_class(const struct bhnd_core_info *ci);
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 @@ int bhnd_nvram_getvar_array(device_t dev,
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,11 +549,85 @@ bhnd_driver_get_erom_class(driver_t *driver)
* @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.
*
diff --git a/sys/dev/bhnd/bhnd_bus_if.m b/sys/dev/bhnd/bhnd_bus_if.m
index 1b2bff6d4159..28b513959e6e 100644
--- a/sys/dev/bhnd/bhnd_bus_if.m
+++ b/sys/dev/bhnd/bhnd_bus_if.m
@@ -1,7 +1,11 @@
#-
# Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
+# 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 @@ CODE {
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)
{
@@ -274,6 +284,100 @@ STATICMETHOD bhnd_erom_class_t * get_erom_class {
} 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.
*
* @param dev The bhnd bus device.
diff --git a/sys/dev/bhnd/bhnd_private.h b/sys/dev/bhnd/bhnd_private.h
new file mode 100644
index 000000000000..9c2ed900fa15
--- /dev/null
+++ b/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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BHND_BHND_PRIVATE_H_
+#define _BHND_BHND_PRIVATE_H_
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#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_ */
diff --git a/sys/dev/bhnd/bhnd_subr.c b/sys/dev/bhnd/bhnd_subr.c
index 716a8721ecec..992b887288fa 100644
--- a/sys/dev/bhnd/bhnd_subr.c
+++ b/sys/dev/bhnd/bhnd_subr.c
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * 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 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/bus.h>
+#include <sys/refcount.h>
#include <sys/systm.h>
#include <machine/bus.h>
@@ -51,6 +56,14 @@ __FBSDID("$FreeBSD$");
#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 @@ bhnd_get_core_info(device_t dev) {
}
/**
- * 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 @@ bhnd_get_core_info(device_t dev) {
* @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 @@ bhnd_find_child(device_t dev, bhnd_devclass_t class, int unit)
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);
@@ -392,6 +405,146 @@ done:
}
/**
+ * 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 @@ bhnd_device_lookup(device_t dev, const struct bhnd_device *table,
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 =
@@ -1345,6 +1498,277 @@ bhnd_nvram_getvar_array(device_t dev, const char *name, void *buf, size_t size,
}
/**
+ * 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);
+ mtx_init(&bsr->lock, "bhnd_service_registry lock", NULL, MTX_DEF);
+
+ 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 */
+ mtx_lock(&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)) {
+ mtx_unlock(&bsr->lock);
+ return (EBUSY);
+ }
+ mtx_unlock(&bsr->lock);
+
+ mtx_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);
+
+ mtx_lock(&bsr->lock);
+
+ /* Is a service provider already registered? */
+ STAILQ_FOREACH(entry, &bsr->entries, link) {
+ if (entry->service == service) {
+ mtx_unlock(&bsr->lock);
+ return (EEXIST);
+ }
+ }
+
+ /* Initialize and insert our new entry */
+ entry = malloc(sizeof(*entry), M_BHND, M_NOWAIT);
+ if (entry == NULL) {
+ mtx_unlock(&bsr->lock);
+ return (ENOMEM);
+ }
+
+ entry->provider = provider;
+ entry->service = service;
+ entry->flags = flags;
+ refcount_init(&entry->refs, 0);
+
+ STAILQ_INSERT_HEAD(&bsr->entries, entry, link);
+
+ mtx_unlock(&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;
+
+ mtx_lock(&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) {
+ mtx_unlock(&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
+
+ mtx_unlock(&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;
+
+ mtx_lock(&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);
+
+ mtx_unlock(&bsr->lock);
+ return (entry->provider);
+ }
+ mtx_unlock(&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 */
+ mtx_lock(&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;
+ }
+
+ mtx_unlock(&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.
*
@@ -1428,6 +1852,222 @@ bhnd_set_default_bus_desc(device_t dev, const struct bhnd_chipid *chip_id)
}
/**
+ * 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().
*
* If a parent device is available, this implementation delegates the
diff --git a/sys/dev/bhnd/bhnd_types.h b/sys/dev/bhnd/bhnd_types.h
index ffdbf1a47489..d819afedbecb 100644
--- a/sys/dev/bhnd/bhnd_types.h
+++ b/sys/dev/bhnd/bhnd_types.h
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * 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 @@ typedef enum {
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.
diff --git a/sys/dev/bhnd/bhndb/bhnd_bhndb.c b/sys/dev/bhnd/bhndb/bhnd_bhndb.c
index 12efb04c33a4..fdd45ef39819 100644
--- a/sys/dev/bhnd/bhndb/bhnd_bhndb.c
+++ b/sys/dev/bhnd/bhndb/bhnd_bhndb.c
@@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * 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 @@ bhnd_bhndb_find_hostb_device(device_t dev)
/* 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
diff --git a/sys/dev/bhnd/bhndb/bhndb.c b/sys/dev/bhnd/bhndb/bhndb.c
index c5b08276caf1..0178e063fa1a 100644
--- a/sys/dev/bhnd/bhndb/bhndb.c
+++ b/sys/dev/bhnd/bhndb/bhndb.c
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * 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 @@ bhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass)
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 @@ failed:
if (sc->bus_res != NULL)
bhndb_free_resources(sc->bus_res);
+ bhnd_service_registry_fini(&sc->services);
+
return (error);
}
@@ -911,11 +919,15 @@ bhndb_generic_detach(device_t dev)
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);
@@ -1212,6 +1224,17 @@ bhndb_get_hostb_core(device_t dev, device_t child, struct bhnd_core_info *core)
}
/**
+ * 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().
*/
static struct resource *
@@ -2146,6 +2169,13 @@ static device_method_t bhndb_methods[] = {
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),
diff --git a/sys/dev/bhnd/bhndb/bhndbvar.h b/sys/dev/bhnd/bhndb/bhndbvar.h
index 2ce502d14342..f7018f49e11a 100644
--- a/sys/dev/bhnd/bhndb/bhndbvar.h
+++ b/sys/dev/bhnd/bhndb/bhndbvar.h
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * 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 @@ struct bhndb_softc {
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 */
};
diff --git a/sys/dev/bhnd/bhndvar.h b/sys/dev/bhnd/bhndvar.h
index 2602302a1861..6bbbb796ebba 100644
--- a/sys/dev/bhnd/bhndvar.h
+++ b/sys/dev/bhnd/bhndvar.h
@@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
+ * 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 <sys/param.h>
#include <sys/bus.h>
+#include <sys/lock.h>
#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/sx.h>
#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_ */
diff --git a/sys/dev/bhnd/cores/chipc/chipc.c b/sys/dev/bhnd/cores/chipc/chipc.c
index 0d1f893a8a6a..31a5a2d3780c 100644
--- a/sys/dev/bhnd/cores/chipc/chipc.c
+++ b/sys/dev/bhnd/cores/chipc/chipc.c
@@ -1,8 +1,12 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com>
+ * 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 @@ chipc_attach(device_t dev)
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 @@ chipc_detach(device_t dev)
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 @@ chipc_should_enable_muxed_sprom(struct chipc_softc *sc)
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);
diff --git a/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c b/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c
index d2bf54c8ce35..90155418ed7f 100644
--- a/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c
+++ b/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c
@@ -1,7 +1,11 @@
/*-
* Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
- * 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 @@ bhnd_pwrctl_attach(device_t dev)
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 @@ bhnd_pwrctl_attach(device_t dev)
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 @@ bhnd_pwrctl_attach(device_t dev)
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 @@ bhnd_pwrctl_detach(device_t dev)
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);
diff --git a/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c b/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c
index ec2b5b2b9e9a..5d5657a1cf26 100644
--- a/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c
+++ b/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * 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 @@ bhnd_pci_wars_hwup(struct bhnd_pcihb_softc *sc, bhnd_pci_war_state state)
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 */
diff --git a/sys/dev/bhnd/cores/pmu/bhnd_pmu.c b/sys/dev/bhnd/cores/pmu/bhnd_pmu.c
index 6d7e907da4cb..254fa309dd39 100644
--- a/sys/dev/bhnd/cores/pmu/bhnd_pmu.c
+++ b/sys/dev/bhnd/cores/pmu/bhnd_pmu.c
@@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * 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 @@ bhnd_pmu_attach(device_t dev, struct bhnd_resource *res)
/* 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 @@ bhnd_pmu_attach(device_t dev, struct bhnd_resource *res)
}
/* 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 @@ bhnd_pmu_attach(device_t dev, struct bhnd_resource *res)
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 @@ int
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);
diff --git a/sys/dev/bhnd/nvram/bhnd_sprom.c b/sys/dev/bhnd/nvram/bhnd_sprom.c
index d830af9785d1..0eb17b4b1e88 100644
--- a/sys/dev/bhnd/nvram/bhnd_sprom.c
+++ b/sys/dev/bhnd/nvram/bhnd_sprom.c
@@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * 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 @@ bhnd_sprom_attach(device_t dev, bus_size_t offset)
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_sprom_attach(device_t dev, bus_size_t offset)
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 @@ failed:
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 @@ int
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);
diff --git a/sys/dev/bhnd/siba/siba_bhndb.c b/sys/dev/bhnd/siba/siba_bhndb.c
index cfc1180ad930..13082edc2347 100644
--- a/sys/dev/bhnd/siba/siba_bhndb.c
+++ b/sys/dev/bhnd/siba/siba_bhndb.c
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * 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 @@ siba_bhndb_wars_pcie_clear_d11_timeout(struct siba_softc *sc)
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 @@ siba_bhndb_wars_hwup(struct siba_softc *sc)
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,
diff --git a/sys/mips/broadcom/bcm_machdep.c b/sys/mips/broadcom/bcm_machdep.c
index eef277c07bfd..f0b9ba732178 100644
--- a/sys/mips/broadcom/bcm_machdep.c
+++ b/sys/mips/broadcom/bcm_machdep.c
@@ -2,9 +2,12 @@
* Copyright (c) 2007 Bruce M. Simpson.
* Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com>
* Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
- *
+ * 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 @@ bcm_init_platform_data(struct bcm_platform *bp)
}
}
+ /* 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);
}
diff --git a/sys/mips/broadcom/bcm_machdep.h b/sys/mips/broadcom/bcm_machdep.h
index a20f40754c69..64ac40e69c46 100644
--- a/sys/mips/broadcom/bcm_machdep.h
+++ b/sys/mips/broadcom/bcm_machdep.h
@@ -1,7 +1,11 @@
/*-
* Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
+ * 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
};
diff --git a/sys/mips/broadcom/bcm_nvram_cfe.c b/sys/mips/broadcom/bcm_nvram_cfe.c
index 7132a45ae38c..a237e9832534 100644
--- a/sys/mips/broadcom/bcm_nvram_cfe.c
+++ b/sys/mips/broadcom/bcm_nvram_cfe.c
@@ -1,7 +1,11 @@
/*-
* Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
+ * 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 @@ bhnd_nvram_cfe_attach(device_t dev)
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 @@ bhnd_nvram_cfe_suspend(device_t dev)
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);
diff --git a/sys/mips/broadcom/bhnd_nexus.c b/sys/mips/broadcom/bhnd_nexus.c
index f2a4d117b62e..72996f9ee010 100644
--- a/sys/mips/broadcom/bhnd_nexus.c
+++ b/sys/mips/broadcom/bhnd_nexus.c
@@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landon@freebsd.org>
+ * 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 @@ __FBSDID("$FreeBSD$");
#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 @@ bhnd_nexus_assign_intr(device_t dev, device_t child, int rid)
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),