Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 27 Sep 2017 19:44:23 +0000 (UTC)
From:      "Landon J. Fuller" <landonf@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r324070 - in head/sys: dev/bhnd dev/bhnd/bhndb dev/bhnd/cores/chipc dev/bhnd/cores/chipc/pwrctl dev/bhnd/cores/pci dev/bhnd/cores/pmu dev/bhnd/nvram dev/bhnd/siba mips/broadcom
Message-ID:  <201709271944.v8RJiNjg022010@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: landonf
Date: Wed Sep 27 19:44:23 2017
New Revision: 324070
URL: https://svnweb.freebsd.org/changeset/base/324070

Log:
  bhnd: Implement bhnd(4) platform device registration.
  
  Add bhnd(4) API for explicitly registering BHND platform devices (ChipCommon,
  PMU, NVRAM, etc) with the bus, rather than walking the newbus hierarchy to
  discover platform devices. These devices are now also refcounted; attempting
  to deregister an actively used platform device will return EBUSY.
  
  This resolves a lock ordering incompatibility with bwn(4)'s firmware loading
  threads; previously it was necessary to acquire Giant to protect newbus access
  when locating and querying the NVRAM device.
  
  Approved by:	adrian (mentor)
  Sponsored by:	The FreeBSD Foundation
  Differential Revision:	https://reviews.freebsd.org/D12392

Added:
  head/sys/dev/bhnd/bhnd_private.h   (contents, props changed)
Modified:
  head/sys/dev/bhnd/bhnd.c
  head/sys/dev/bhnd/bhnd.h
  head/sys/dev/bhnd/bhnd_bus_if.m
  head/sys/dev/bhnd/bhnd_subr.c
  head/sys/dev/bhnd/bhnd_types.h
  head/sys/dev/bhnd/bhndb/bhnd_bhndb.c
  head/sys/dev/bhnd/bhndb/bhndb.c
  head/sys/dev/bhnd/bhndb/bhndbvar.h
  head/sys/dev/bhnd/bhndvar.h
  head/sys/dev/bhnd/cores/chipc/chipc.c
  head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c
  head/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c
  head/sys/dev/bhnd/cores/pmu/bhnd_pmu.c
  head/sys/dev/bhnd/nvram/bhnd_sprom.c
  head/sys/dev/bhnd/siba/siba_bhndb.c
  head/sys/mips/broadcom/bcm_machdep.c
  head/sys/mips/broadcom/bcm_machdep.h
  head/sys/mips/broadcom/bcm_nvram_cfe.c
  head/sys/mips/broadcom/bhnd_nexus.c

Modified: head/sys/dev/bhnd/bhnd.c
==============================================================================
--- head/sys/dev/bhnd/bhnd.c	Wed Sep 27 19:22:10 2017	(r324069)
+++ head/sys/dev/bhnd/bhnd.c	Wed Sep 27 19:44:23 2017	(r324070)
@@ -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,12 +73,10 @@ __FBSDID("$FreeBSD$");
 #include "bhnd.h"
 #include "bhndvar.h"
 
+#include "bhnd_private.h"
+
 MALLOC_DEFINE(M_BHND, "bhnd", "bhnd bus data structures");
 
-/* Bus pass at which all bus-required children must be available, and
- * attachment may be finalized. */
-#define	BHND_FINISH_ATTACH_PASS	BUS_PASS_DEFAULT
-
 /**
  * bhnd_generic_probe_nomatch() reporting configuration.
  */
@@ -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);
+	if ((error = bhnd_bus_probe_children(dev))) {
+		bhnd_delete_children(sc);
+		return (error);
 	}
 
-	/* 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)
-		bhnd_delete_children(sc);
-
-	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,260 +293,11 @@ 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().
  *
  * This implementation determines probe ordering based on the device's class
@@ -613,7 +347,7 @@ bhnd_generic_get_probe_order(device_t dev, device_t ch
 	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 ch
 /**
  * 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 chil
 {
 	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 chil
 		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),

Modified: head/sys/dev/bhnd/bhnd.h
==============================================================================
--- head/sys/dev/bhnd/bhnd.h	Wed Sep 27 19:22:10 2017	(r324069)
+++ head/sys/dev/bhnd/bhnd.h	Wed Sep 27 19:44:23 2017	(r324070)
@@ -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_c
 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,8 +549,82 @@ 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));
 }
 
 /**

Modified: head/sys/dev/bhnd/bhnd_bus_if.m
==============================================================================
--- head/sys/dev/bhnd/bhnd_bus_if.m	Wed Sep 27 19:22:10 2017	(r324069)
+++ head/sys/dev/bhnd/bhnd_bus_if.m	Wed Sep 27 19:44:23 2017	(r324070)
@@ -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)
 	{
@@ -272,6 +282,100 @@ CODE {
 STATICMETHOD bhnd_erom_class_t * get_erom_class {
 	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.

Added: head/sys/dev/bhnd/bhnd_private.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/bhnd/bhnd_private.h	Wed Sep 27 19:44:23 2017	(r324070)
@@ -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

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201709271944.v8RJiNjg022010>