Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 27 Aug 2016 00:03:02 +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: r304870 - in head/sys: conf dev/bhnd dev/bhnd/bcma dev/bhnd/bhndb dev/bhnd/cores/chipc dev/bhnd/cores/chipc/pwrctl dev/bhnd/cores/pmu dev/bhnd/nvram dev/bhnd/pmu dev/bhnd/siba mips/broa...
Message-ID:  <201608270003.u7R032kN062998@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: landonf
Date: Sat Aug 27 00:03:02 2016
New Revision: 304870
URL: https://svnweb.freebsd.org/changeset/base/304870

Log:
  bhnd(4): Initial PMU/PWRCTL power and clock management support.
  
  
  - Added bhnd_pmu driver implementations for PMU and PWRCTL chipsets,
    derived from Broadcom's ISC-licensed HND code.
  - Added bhnd bus-level support for routing per-core clock and resource
    power requests to the PMU device.
  - Lift ChipCommon support out into the bhnd module, dropping
    bhnd_chipc.
  
  Reviewed by:	mizhka
  Approved by:	adrian (mentor)
  Differential Revision:	https://reviews.freebsd.org/D7492

Added:
  head/sys/dev/bhnd/cores/chipc/bhnd_pmu_chipc.c   (contents, props changed)
  head/sys/dev/bhnd/cores/chipc/pwrctl/
  head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c   (contents, props changed)
  head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_private.h   (contents, props changed)
  head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_subr.c   (contents, props changed)
  head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctlvar.h   (contents, props changed)
  head/sys/dev/bhnd/cores/pmu/
  head/sys/dev/bhnd/cores/pmu/bhnd_pmu.c   (contents, props changed)
  head/sys/dev/bhnd/cores/pmu/bhnd_pmu.h   (contents, props changed)
  head/sys/dev/bhnd/cores/pmu/bhnd_pmu_core.c   (contents, props changed)
  head/sys/dev/bhnd/cores/pmu/bhnd_pmu_if.m   (contents, props changed)
  head/sys/dev/bhnd/cores/pmu/bhnd_pmu_private.h   (contents, props changed)
  head/sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c   (contents, props changed)
  head/sys/dev/bhnd/cores/pmu/bhnd_pmureg.h   (contents, props changed)
  head/sys/dev/bhnd/cores/pmu/bhnd_pmuvar.h   (contents, props changed)
  head/sys/dev/bhnd/pmu/
Deleted:
  head/sys/modules/bhnd/cores/bhnd_chipc/
Modified:
  head/sys/conf/files
  head/sys/dev/bhnd/bcma/bcma.c
  head/sys/dev/bhnd/bcma/bcma_dmp.h
  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_core.h
  head/sys/dev/bhnd/bhnd_ids.h
  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/bhndb_pci.c
  head/sys/dev/bhnd/bhndvar.h
  head/sys/dev/bhnd/cores/chipc/bhnd_chipc_if.m
  head/sys/dev/bhnd/cores/chipc/bhnd_sprom_chipc.c
  head/sys/dev/bhnd/cores/chipc/chipc.c
  head/sys/dev/bhnd/cores/chipc/chipc.h
  head/sys/dev/bhnd/cores/chipc/chipc_subr.c
  head/sys/dev/bhnd/cores/chipc/chipcreg.h
  head/sys/dev/bhnd/cores/chipc/chipcvar.h
  head/sys/dev/bhnd/nvram/nvram_map
  head/sys/dev/bhnd/siba/siba.c
  head/sys/mips/broadcom/bcm_machdep.c
  head/sys/modules/bhnd/Makefile
  head/sys/modules/bhnd/cores/Makefile

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Fri Aug 26 23:50:44 2016	(r304869)
+++ head/sys/conf/files	Sat Aug 27 00:03:02 2016	(r304870)
@@ -1157,19 +1157,26 @@ dev/bhnd/bcma/bcma_bhndb.c		optional bcm
 dev/bhnd/bcma/bcma_erom.c		optional bcma bhnd
 dev/bhnd/bcma/bcma_nexus.c		optional bcma_nexus bcma bhnd
 dev/bhnd/bcma/bcma_subr.c		optional bcma bhnd
+dev/bhnd/cores/chipc/bhnd_chipc_if.m	optional bhnd
+dev/bhnd/cores/chipc/bhnd_sprom_chipc.c	optional bhnd
+dev/bhnd/cores/chipc/bhnd_pmu_chipc.c	optional bhnd
 dev/bhnd/cores/chipc/chipc.c		optional bhnd
 dev/bhnd/cores/chipc/chipc_cfi.c	optional bhnd cfi 
 dev/bhnd/cores/chipc/chipc_slicer.c	optional bhnd cfi | bhnd spibus
 dev/bhnd/cores/chipc/chipc_spi.c	optional bhnd spibus
 dev/bhnd/cores/chipc/chipc_subr.c	optional bhnd
-dev/bhnd/cores/chipc/bhnd_chipc_if.m	optional bhnd
-dev/bhnd/cores/chipc/bhnd_sprom_chipc.c	optional bhnd
+dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c	optional bhnd
+dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_subr.c	optional bhnd
 dev/bhnd/cores/pci/bhnd_pci.c		optional bhnd pci
 dev/bhnd/cores/pci/bhnd_pci_hostb.c	optional bhndb bhnd pci
 dev/bhnd/cores/pci/bhnd_pcib.c		optional bhnd_pcib bhnd pci
 dev/bhnd/cores/pcie2/bhnd_pcie2.c	optional bhnd pci
 dev/bhnd/cores/pcie2/bhnd_pcie2_hostb.c	optional bhndb bhnd pci
 dev/bhnd/cores/pcie2/bhnd_pcie2b.c	optional bhnd_pcie2b bhnd pci
+dev/bhnd/cores/pmu/bhnd_pmu.c		optional bhnd
+dev/bhnd/cores/pmu/bhnd_pmu_core.c	optional bhnd
+dev/bhnd/cores/pmu/bhnd_pmu_if.m	optional bhnd
+dev/bhnd/cores/pmu/bhnd_pmu_subr.c	optional bhnd
 dev/bhnd/nvram/bhnd_nvram.c		optional bhnd
 dev/bhnd/nvram/bhnd_nvram_common.c	optional bhnd
 dev/bhnd/nvram/bhnd_nvram_cfe.c		optional bhnd siba_nexus cfe | \

Modified: head/sys/dev/bhnd/bcma/bcma.c
==============================================================================
--- head/sys/dev/bhnd/bcma/bcma.c	Fri Aug 26 23:50:44 2016	(r304869)
+++ head/sys/dev/bhnd/bcma/bcma.c	Sat Aug 27 00:03:02 2016	(r304870)
@@ -259,6 +259,78 @@ bcma_suspend_core(device_t dev, device_t
 	return (ENXIO);
 }
 
+static uint32_t
+bcma_read_config(device_t dev, device_t child, bus_size_t offset, u_int width)
+{
+	struct bcma_devinfo	*dinfo;
+	struct bhnd_resource	*r;
+
+	/* Must be a directly attached child core */
+	if (device_get_parent(child) != dev)
+		return (UINT32_MAX);
+
+	/* Fetch the agent registers */
+	dinfo = device_get_ivars(child);
+	if ((r = dinfo->res_agent) == NULL)
+		return (UINT32_MAX);
+
+	/* Verify bounds */
+	if (offset > rman_get_size(r->res))
+		return (UINT32_MAX);
+
+	if (rman_get_size(r->res) - offset < width)
+		return (UINT32_MAX);
+
+	switch (width) {
+	case 1:
+		return (bhnd_bus_read_1(r, offset));
+	case 2:
+		return (bhnd_bus_read_2(r, offset));
+	case 4:
+		return (bhnd_bus_read_4(r, offset));
+	default:
+		return (UINT32_MAX);
+	}
+}
+
+static void
+bcma_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val,
+    u_int width)
+{
+	struct bcma_devinfo	*dinfo;
+	struct bhnd_resource	*r;
+
+	/* Must be a directly attached child core */
+	if (device_get_parent(child) != dev)
+		return;
+
+	/* Fetch the agent registers */
+	dinfo = device_get_ivars(child);
+	if ((r = dinfo->res_agent) == NULL)
+		return;
+
+	/* Verify bounds */
+	if (offset > rman_get_size(r->res))
+		return;
+
+	if (rman_get_size(r->res) - offset < width)
+		return;
+
+	switch (width) {
+	case 1:
+		bhnd_bus_write_1(r, offset, val);
+		break;
+	case 2:
+		bhnd_bus_write_2(r, offset, val);
+		break;
+	case 4:
+		bhnd_bus_write_4(r, offset, val);
+		break;
+	default:
+		break;
+	}
+}
+
 static u_int
 bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type)
 {
@@ -473,6 +545,9 @@ bcma_add_children(device_t bus, struct r
 		 * unpopulated, the device shouldn't be used. */
 		if (bhnd_is_hw_disabled(child))
 			device_disable(child);
+
+		/* Issue bus callback for fully initialized child. */
+		BHND_BUS_CHILD_ADDED(bus, child);
 	}
 
 	/* Hit EOF parsing cores? */
@@ -504,6 +579,8 @@ static device_method_t bcma_methods[] = 
 	DEVMETHOD(bhnd_bus_free_devinfo,	bcma_free_bhnd_dinfo),
 	DEVMETHOD(bhnd_bus_reset_core,		bcma_reset_core),
 	DEVMETHOD(bhnd_bus_suspend_core,	bcma_suspend_core),
+	DEVMETHOD(bhnd_bus_read_config,		bcma_read_config),
+	DEVMETHOD(bhnd_bus_write_config,	bcma_write_config),
 	DEVMETHOD(bhnd_bus_get_port_count,	bcma_get_port_count),
 	DEVMETHOD(bhnd_bus_get_region_count,	bcma_get_region_count),
 	DEVMETHOD(bhnd_bus_get_port_rid,	bcma_get_port_rid),

Modified: head/sys/dev/bhnd/bcma/bcma_dmp.h
==============================================================================
--- head/sys/dev/bhnd/bcma/bcma_dmp.h	Fri Aug 26 23:50:44 2016	(r304869)
+++ head/sys/dev/bhnd/bcma/bcma_dmp.h	Sat Aug 27 00:03:02 2016	(r304870)
@@ -109,6 +109,19 @@
 #define	BCMA_DMP_OOBDINWIDTH	0x364
 #define	BCMA_DMP_OOBDOUTWIDTH	0x368
 
+/* The exact interpretation of these bits is unverified; these
+ * are our best guesses as to their use */
+#define	BCMA_DMP_OOBSEL_MASK	0xFF		/**< OOBSEL config mask */
+#define	BCMA_DMP_OOBSEL_0_MASK	BCMA_DMP_OOBSEL_MASK
+#define	BCMA_DMP_OOBSEL_1_MASK	BCMA_DMP_OOBSEL_MASK
+#define	BCMA_DMP_OOBSEL_2_MASK	BCMA_DMP_OOBSEL_MASK
+#define	BCMA_DMP_OOBSEL_3_MASK	BCMA_DMP_OOBSEL_MASK
+#define	BCMA_DMP_OOBSEL_0_SHIFT	0		/**< first OOBSEL config */
+#define	BCMA_DMP_OOBSEL_1_SHIFT	8		/**< second OOBSEL config */
+#define	BCMA_DMP_OOBSEL_2_SHIFT	16		/**< third OOBSEL config */
+#define	BCMA_DMP_OOBSEL_3_SHIFT	24		/**< fouth OOBSEL config */
+#define	BCMA_DMP_OOBSEL_EN	(1 << 7)	/**< enable bit */
+
 // This was inherited from Broadcom's aidmp.h header
 // Is it required for any of our use-cases?
 #if 0 /* defined(IL_BIGENDIAN) && defined(BCMHND74K) */

Modified: head/sys/dev/bhnd/bhnd.c
==============================================================================
--- head/sys/dev/bhnd/bhnd.c	Fri Aug 26 23:50:44 2016	(r304869)
+++ head/sys/dev/bhnd/bhnd.c	Sat Aug 27 00:03:02 2016	(r304870)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -60,6 +60,9 @@ __FBSDID("$FreeBSD$");
 
 #include <dev/bhnd/cores/chipc/chipcvar.h>
 
+#include <dev/bhnd/cores/pmu/bhnd_pmu.h>
+#include <dev/bhnd/cores/pmu/bhnd_pmureg.h>
+
 #include "bhnd_chipc_if.h"
 #include "bhnd_nvram_if.h"
 
@@ -342,7 +345,7 @@ bhnd_finish_attach(struct bhnd_softc *sc
 {
 	struct chipc_caps	*ccaps;
 
-	GIANT_REQUIRED;	/* newbus */
+	GIANT_REQUIRED;	/* for newbus */
 
 	KASSERT(bus_current_pass >= BHND_FINISH_ATTACH_PASS,
 	    ("bhnd_finish_attach() called in pass %d", bus_current_pass));
@@ -367,10 +370,11 @@ bhnd_finish_attach(struct bhnd_softc *sc
 	}
 
 	/* Look for a PMU  */
-	if (ccaps->pmu) {
+	if (ccaps->pmu || ccaps->pwr_ctrl) {
 		if ((sc->pmu_dev = bhnd_find_pmu(sc)) == NULL) {
 			device_printf(sc->dev,
-			    "warning: PMU device not found\n");
+			    "attach failed: supported PMU not found\n");
+			return (ENXIO);
 		}
 	}
 
@@ -478,8 +482,6 @@ found:
 static device_t
 bhnd_find_pmu(struct bhnd_softc *sc)
 {
-	struct chipc_caps	*ccaps;
-
         /* Make sure we're holding Giant for newbus */
 	GIANT_REQUIRED;
 
@@ -494,11 +496,6 @@ bhnd_find_pmu(struct bhnd_softc *sc)
 		return (sc->pmu_dev);
 	}
 
-	if ((ccaps = bhnd_find_chipc_caps(sc)) == NULL)
-		return (NULL);
-
-	if (!ccaps->pmu)
-		return (NULL);
 
 	return (bhnd_find_platform_dev(sc, "bhnd_pmu"));
 }
@@ -626,6 +623,244 @@ bhnd_generic_get_probe_order(device_t de
 }
 
 /**
+ * Default bhnd(4) bus driver implementation of BHND_BUS_ALLOC_PMU().
+ */
+int
+bhnd_generic_alloc_pmu(device_t dev, device_t child)
+{
+	struct bhnd_softc		*sc;
+	struct bhnd_resource		*br;
+	struct chipc_caps		*ccaps;
+	struct bhnd_devinfo		*dinfo;	
+	struct bhnd_core_pmu_info	*pm;
+	struct resource_list		*rl;
+	struct resource_list_entry	*rle;
+	device_t			 pmu_dev;
+	bhnd_addr_t			 r_addr;
+	bhnd_size_t			 r_size;
+	bus_size_t			 pmu_regs;
+	int				 error;
+
+	GIANT_REQUIRED;	/* for newbus */
+	
+	sc = device_get_softc(dev);
+	dinfo = device_get_ivars(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 (dinfo->pmu_info != NULL) {
+		panic("duplicate PMU allocation for %s",
+		    device_get_nameunit(child));
+	}
+
+	/* Determine address+size of the core's PMU register block */
+	error = bhnd_get_region_addr(child, BHND_PORT_DEVICE, 0, 0, &r_addr,
+	    &r_size);
+	if (error) {
+		device_printf(sc->dev, "error fetching register block info for "
+		    "%s: %d\n", device_get_nameunit(child), error);
+		return (error);
+	}
+
+	if (r_size < (pmu_regs + sizeof(uint32_t))) {
+		device_printf(sc->dev, "pmu offset %#jx would overrun %s "
+		    "register block\n", (uintmax_t)pmu_regs,
+		    device_get_nameunit(child));
+		return (ENODEV);
+	}
+
+	/* Locate actual resource containing the core's register block */
+	if ((rl = BUS_GET_RESOURCE_LIST(dev, child)) == NULL) {
+		device_printf(dev, "NULL resource list returned for %s\n",
+		    device_get_nameunit(child));
+		return (ENXIO);
+	}
+
+	if ((rle = resource_list_find(rl, SYS_RES_MEMORY, 0)) == NULL) {
+		device_printf(dev, "cannot locate core register resource "
+		    "for %s\n", device_get_nameunit(child));
+		return (ENXIO);
+	}
+
+	if (rle->res == NULL) {
+		device_printf(dev, "core register resource unallocated for "
+		    "%s\n", device_get_nameunit(child));
+		return (ENXIO);
+	}
+
+	if (r_addr+pmu_regs < rman_get_start(rle->res) ||
+	    r_addr+pmu_regs >= rman_get_end(rle->res))
+	{
+		device_printf(dev, "core register resource does not map PMU "
+		    "registers at %#jx\n for %s\n", r_addr+pmu_regs,
+		    device_get_nameunit(child));
+		return (ENXIO);
+	}
+
+	/* Adjust PMU register offset relative to the actual start address
+	 * of the core's register block allocation.
+	 * 
+	 * XXX: The saved offset will be invalid if bus_adjust_resource is
+	 * used to modify the resource's start address.
+	 */
+	if (rman_get_start(rle->res) > r_addr)
+		pmu_regs -= rman_get_start(rle->res) - r_addr;
+	else
+		pmu_regs -= r_addr - rman_get_start(rle->res);
+
+	/* Allocate and initialize PMU info */
+	br = malloc(sizeof(struct bhnd_resource), M_BHND, M_NOWAIT);
+	if (br == NULL)
+		return (ENOMEM);
+
+	br->res = rle->res;
+	br->direct = ((rman_get_flags(rle->res) & RF_ACTIVE) != 0);
+
+	pm = malloc(sizeof(*dinfo->pmu_info), M_BHND, M_NOWAIT);
+	if (pm == NULL) {
+		free(br, M_BHND);
+		return (ENOMEM);
+	}
+	pm->pm_dev = child;
+	pm->pm_pmu = pmu_dev;
+	pm->pm_res = br;
+	pm->pm_regs = pmu_regs;
+
+	dinfo->pmu_info = pm;
+	return (0);
+}
+
+/**
+ * Default bhnd(4) bus driver implementation of BHND_BUS_RELEASE_PMU().
+ */
+int
+bhnd_generic_release_pmu(device_t dev, device_t child)
+{
+	struct bhnd_softc		*sc;
+	struct bhnd_devinfo		*dinfo;
+	device_t			 pmu;
+	int				 error;
+
+	GIANT_REQUIRED;	/* for newbus */
+	
+	sc = device_get_softc(dev);
+	dinfo = device_get_ivars(child);
+
+	if ((pmu = bhnd_find_pmu(sc)) == NULL) {
+		device_printf(sc->dev, 
+		    "pmu unavailable; cannot release request state\n");
+		return (ENXIO);
+	}
+
+	/* dispatch release request */
+	if (dinfo->pmu_info == NULL)
+		panic("pmu over-release for %s", device_get_nameunit(child));
+
+	if ((error = BHND_PMU_CORE_RELEASE(pmu, dinfo->pmu_info)))
+		return (error);
+
+	/* free PMU info */
+	free(dinfo->pmu_info->pm_res, M_BHND);
+	free(dinfo->pmu_info, M_BHND);
+	dinfo->pmu_info = NULL;
+
+	return (0);
+}
+
+/**
+ * Default bhnd(4) bus driver implementation of BHND_BUS_REQUEST_CLOCK().
+ */
+int
+bhnd_generic_request_clock(device_t dev, device_t child, bhnd_clock clock)
+{
+	struct bhnd_softc		*sc;
+	struct bhnd_devinfo		*dinfo;
+	struct bhnd_core_pmu_info	*pm;
+
+	sc = device_get_softc(dev);
+	dinfo = device_get_ivars(child);
+
+	if ((pm = dinfo->pmu_info) == NULL)
+		panic("no active PMU request state");
+
+	/* dispatch request to PMU */
+	return (BHND_PMU_CORE_REQ_CLOCK(pm->pm_pmu, pm, clock));
+}
+
+/**
+ * Default bhnd(4) bus driver implementation of BHND_BUS_ENABLE_CLOCKS().
+ */
+int
+bhnd_generic_enable_clocks(device_t dev, device_t child, uint32_t clocks)
+{
+	struct bhnd_softc		*sc;
+	struct bhnd_devinfo		*dinfo;
+	struct bhnd_core_pmu_info	*pm;
+
+	sc = device_get_softc(dev);
+	dinfo = device_get_ivars(child);
+
+	if ((pm = dinfo->pmu_info) == NULL)
+		panic("no active PMU request state");
+
+	/* dispatch request to PMU */
+	return (BHND_PMU_CORE_EN_CLOCKS(pm->pm_pmu, pm, clocks));
+}
+
+/**
+ * Default bhnd(4) bus driver implementation of BHND_BUS_REQUEST_EXT_RSRC().
+ */
+int
+bhnd_generic_request_ext_rsrc(device_t dev, device_t child, u_int rsrc)
+{
+	struct bhnd_softc		*sc;
+	struct bhnd_devinfo		*dinfo;
+	struct bhnd_core_pmu_info	*pm;
+
+	sc = device_get_softc(dev);
+	dinfo = device_get_ivars(child);
+
+	if ((pm = dinfo->pmu_info) == NULL)
+		panic("no active PMU request state");
+
+	/* dispatch request to PMU */
+	return (BHND_PMU_CORE_REQ_EXT_RSRC(pm->pm_pmu, pm, rsrc));
+}
+
+/**
+ * Default bhnd(4) bus driver implementation of BHND_BUS_RELEASE_EXT_RSRC().
+ */
+int
+bhnd_generic_release_ext_rsrc(device_t dev, device_t child, u_int rsrc)
+{
+	struct bhnd_softc		*sc;
+	struct bhnd_devinfo		*dinfo;
+	struct bhnd_core_pmu_info	*pm;
+
+	sc = device_get_softc(dev);
+	dinfo = device_get_ivars(child);
+
+	if ((pm = dinfo->pmu_info) == NULL)
+		panic("no active PMU request state");
+
+	/* dispatch request to PMU */
+	return (BHND_PMU_CORE_RELEASE_EXT_RSRC(pm->pm_pmu, pm, rsrc));
+}
+
+
+/**
  * Default bhnd(4) bus driver implementation of BHND_BUS_IS_REGION_VALID().
  * 
  * This implementation assumes that port and region numbers are 0-indexed and
@@ -815,13 +1050,21 @@ bhnd_generic_add_child(device_t dev, u_i
 
 	device_set_ivars(child, dinfo);
 
-	/* Inform concrete bus driver. */
-	BHND_BUS_CHILD_ADDED(dev, child);
-
 	return (child);
 }
 
 /**
+ * Default bhnd(4) bus driver implementation of BHND_BUS_CHILD_ADDED().
+ * 
+ * This implementation manages internal bhnd(4) state, and must be called
+ * by subclassing drivers.
+ */
+void
+bhnd_generic_child_added(device_t dev, device_t child)
+{
+}
+
+/**
  * Default bhnd(4) bus driver implementation of BUS_CHILD_DELETED().
  * 
  * This implementation manages internal bhnd(4) state, and must be called
@@ -836,8 +1079,17 @@ bhnd_generic_child_deleted(device_t dev,
 	sc = device_get_softc(dev);
 
 	/* Free device info */
-	if ((dinfo = device_get_ivars(child)) != NULL)
+	if ((dinfo = device_get_ivars(child)) != NULL) {
+		if (dinfo->pmu_info != NULL) {
+			/* Releasing PMU requests automatically would be nice,
+			 * but we can't reference per-core PMU register
+			 * resource after driver detach */
+			panic("%s leaked device pmu state\n",
+			    device_get_nameunit(child));
+		}
+
 		BHND_BUS_FREE_DEVINFO(dev, dinfo);
+	}
 
 	/* Clean up platform device references */
 	if (sc->chipc_dev == child) {
@@ -998,9 +1250,20 @@ static device_method_t bhnd_methods[] = 
 
 	/* BHND interface */
 	DEVMETHOD(bhnd_bus_get_chipid,		bhnd_bus_generic_get_chipid),
+	DEVMETHOD(bhnd_bus_is_hw_disabled,	bhnd_bus_generic_is_hw_disabled),
+	DEVMETHOD(bhnd_bus_read_board_info,	bhnd_bus_generic_read_board_info),
+
 	DEVMETHOD(bhnd_bus_get_probe_order,	bhnd_generic_get_probe_order),
+
+	DEVMETHOD(bhnd_bus_alloc_pmu,		bhnd_generic_alloc_pmu),
+	DEVMETHOD(bhnd_bus_release_pmu,		bhnd_generic_release_pmu),
+	DEVMETHOD(bhnd_bus_request_clock,	bhnd_generic_request_clock),
+	DEVMETHOD(bhnd_bus_enable_clocks,	bhnd_generic_enable_clocks),
+	DEVMETHOD(bhnd_bus_request_ext_rsrc,	bhnd_generic_request_ext_rsrc),
+	DEVMETHOD(bhnd_bus_release_ext_rsrc,	bhnd_generic_release_ext_rsrc),
+
+	DEVMETHOD(bhnd_bus_child_added,		bhnd_generic_child_added),
 	DEVMETHOD(bhnd_bus_is_region_valid,	bhnd_generic_is_region_valid),
-	DEVMETHOD(bhnd_bus_is_hw_disabled,	bhnd_bus_generic_is_hw_disabled),
 	DEVMETHOD(bhnd_bus_get_nvram_var,	bhnd_generic_get_nvram_var),
 
 	/* BHND interface (bus I/O) */

Modified: head/sys/dev/bhnd/bhnd.h
==============================================================================
--- head/sys/dev/bhnd/bhnd.h	Fri Aug 26 23:50:44 2016	(r304869)
+++ head/sys/dev/bhnd/bhnd.h	Sat Aug 27 00:03:02 2016	(r304870)
@@ -417,6 +417,67 @@ bhnd_get_chipid(device_t dev) {
 };
 
 /**
+ * If supported by the chipset, return the clock source for the given clock.
+ *
+ * This function is only supported on early PWRCTL-equipped chipsets
+ * that expose clock management via their host bridge interface. Currently,
+ * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9.
+ *
+ * @param dev A bhnd bus child device.
+ * @param clock The clock for which a clock source will be returned.
+ *
+ * @retval	bhnd_clksrc		The clock source for @p clock.
+ * @retval	BHND_CLKSRC_UNKNOWN	If @p clock is unsupported, or its
+ *					clock source is not known to the bus.
+ */
+static inline bhnd_clksrc
+bhnd_pwrctl_get_clksrc(device_t dev, bhnd_clock clock)
+{
+	return (BHND_BUS_PWRCTL_GET_CLKSRC(device_get_parent(dev), dev, clock));
+}
+
+/**
+ * If supported by the chipset, gate @p clock
+ *
+ * This function is only supported on early PWRCTL-equipped chipsets
+ * that expose clock management via their host bridge interface. Currently,
+ * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9.
+ *
+ * @param dev A bhnd bus child device.
+ * @param clock The clock to be disabled.
+ *
+ * @retval 0 success
+ * @retval ENODEV If bus-level clock source management is not supported.
+ * @retval ENXIO If bus-level management of @p clock is not supported.
+ */
+static inline int
+bhnd_pwrctl_gate_clock(device_t dev, bhnd_clock clock)
+{
+	return (BHND_BUS_PWRCTL_GATE_CLOCK(device_get_parent(dev), dev, clock));
+}
+
+/**
+ * If supported by the chipset, ungate @p clock
+ *
+ * This function is only supported on early PWRCTL-equipped chipsets
+ * that expose clock management via their host bridge interface. Currently,
+ * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9.
+ *
+ * @param dev A bhnd bus child device.
+ * @param clock The clock to be enabled.
+ *
+ * @retval 0 success
+ * @retval ENODEV If bus-level clock source management is not supported.
+ * @retval ENXIO If bus-level management of @p clock is not supported.
+ */
+static inline int
+bhnd_pwrctl_ungate_clock(device_t dev, bhnd_clock clock)
+{
+	return (BHND_BUS_PWRCTL_UNGATE_CLOCK(device_get_parent(dev), dev,
+	    clock));
+}
+
+/**
  * Return the BHND attachment type of the parent bhnd bus.
  *
  * @param dev A bhnd bus child device.
@@ -454,6 +515,171 @@ bhnd_read_board_info(device_t dev, struc
 }
 
 /**
+ * Allocate and enable per-core PMU request handling for @p child.
+ *
+ * The region containing the core's PMU register block (if any) must be
+ * allocated via bus_alloc_resource(9) (or bhnd_alloc_resource) before
+ * calling bhnd_alloc_pmu(), and must not be released until after
+ * calling bhnd_release_pmu().
+ *
+ * @param dev The parent of @p child.
+ * @param child The requesting bhnd device.
+ * 
+ * @retval 0           success
+ * @retval non-zero    If allocating PMU request state otherwise fails, a
+ *                     regular unix error code will be returned.
+ */
+static inline int
+bhnd_alloc_pmu(device_t dev)
+{
+	return (BHND_BUS_ALLOC_PMU(device_get_parent(dev), dev));
+}
+
+/**
+ * Release any per-core PMU resources allocated for @p child. Any outstanding
+ * PMU requests are are discarded.
+ *
+ * @param dev The parent of @p child.
+ * @param child The requesting bhnd device.
+ * 
+ * @retval 0           success
+ * @retval non-zero    If releasing PMU request state otherwise fails, a
+ *                     regular unix error code will be returned, and
+ *                     the core state will be left unmodified.
+ */
+static inline int
+bhnd_release_pmu(device_t dev)
+{
+	return (BHND_BUS_RELEASE_PMU(device_get_parent(dev), dev));
+}
+
+/** 
+ * Request that @p clock (or faster) be routed to @p dev.
+ * 
+ * A driver must ask the bhnd bus to allocate clock request state
+ * via bhnd_alloc_pmu() before it can request clock resources.
+ * 
+ * Request multiplexing is managed by the bus.
+ *
+ * @param dev The bhnd(4) device to which @p clock should be routed.
+ * @param clock The requested clock source. 
+ *
+ * @retval 0 success
+ * @retval ENODEV If an unsupported clock was requested.
+ * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable.
+ */
+static inline int
+bhnd_request_clock(device_t dev, bhnd_clock clock)
+{
+	return (BHND_BUS_REQUEST_CLOCK(device_get_parent(dev), dev, clock));
+}
+
+/**
+ * Request that @p clocks be powered on behalf of @p dev.
+ *
+ * This will power any clock sources (e.g. XTAL, PLL, etc) required for
+ * @p clocks and wait until they are ready, discarding any previous
+ * requests by @p dev.
+ *
+ * Request multiplexing is managed by the bus.
+ * 
+ * A driver must ask the bhnd bus to allocate clock request state
+ * via bhnd_alloc_pmu() before it can request clock resources.
+ *
+ * @param dev The requesting bhnd(4) device.
+ * @param clocks The clock(s) to be enabled.
+ *
+ * @retval 0 success
+ * @retval ENODEV If an unsupported clock was requested.
+ * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable.
+ */
+static inline int
+bhnd_enable_clocks(device_t dev, uint32_t clocks)
+{
+	return (BHND_BUS_ENABLE_CLOCKS(device_get_parent(dev), dev, clocks));
+}
+
+/**
+ * Power up an external PMU-managed resource assigned to @p dev.
+ * 
+ * A driver must ask the bhnd bus to allocate PMU request state
+ * via bhnd_alloc_pmu() before it can request PMU resources.
+ *
+ * @param dev The requesting bhnd(4) device.
+ * @param rsrc The core-specific external resource identifier.
+ *
+ * @retval 0 success
+ * @retval ENODEV If the PMU does not support @p rsrc.
+ * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable.
+ */
+static inline int
+bhnd_request_ext_rsrc(device_t dev, u_int rsrc)
+{
+	return (BHND_BUS_REQUEST_EXT_RSRC(device_get_parent(dev), dev, rsrc));
+}
+
+/**
+ * Power down an external PMU-managed resource assigned to @p dev.
+ * 
+ * A driver must ask the bhnd bus to allocate PMU request state
+ * via bhnd_alloc_pmu() before it can request PMU resources.
+ *
+ * @param dev The requesting bhnd(4) device.
+ * @param rsrc The core-specific external resource identifier.
+ *
+ * @retval 0 success
+ * @retval ENODEV If the PMU does not support @p rsrc.
+ * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable.
+ */
+static inline int
+bhnd_release_ext_rsrc(device_t dev, u_int rsrc)
+{
+	return (BHND_BUS_RELEASE_EXT_RSRC(device_get_parent(dev), dev, rsrc));
+}
+
+
+/**
+ * Read @p width bytes at @p offset from the bus-specific agent/config
+ * space of @p dev.
+ *
+ * @param dev The bhnd device for which @p offset should be read.
+ * @param offset The offset to be read.
+ * @param width The size of the access. Must be 1, 2 or 4 bytes.
+ *
+ * The exact behavior of this method is bus-specific. In the case of
+ * bcma(4), this method provides access to the first agent port of @p child.
+ *
+ * @note Device drivers should only use this API for functionality
+ * that is not available via another bhnd(4) function.
+ */
+static inline uint32_t
+bhnd_read_config(device_t dev, bus_size_t offset, u_int width)
+{
+	return (BHND_BUS_READ_CONFIG(device_get_parent(dev), dev, offset,
+	    width));
+}
+
+/**
+ * Read @p width bytes at @p offset from the bus-specific agent/config
+ * space of @p dev.
+ *
+ * @param dev The bhnd device for which @p offset should be read.
+ * @param offset The offset to be written.
+ * @param width The size of the access. Must be 1, 2 or 4 bytes.
+ *
+ * The exact behavior of this method is bus-specific. In the case of
+ * bcma(4), this method provides access to the first agent port of @p child.
+ *
+ * @note Device drivers should only use this API for functionality
+ * that is not available via another bhnd(4) function.
+ */
+static inline void
+bhnd_write_config(device_t dev, bus_size_t offset, uint32_t val, u_int width)
+{
+	BHND_BUS_WRITE_CONFIG(device_get_parent(dev), dev, offset, val, width);
+}
+
+/**
  * Read an NVRAM variable, coerced to the requested @p type.
  *
  * @param 		dev	A bhnd bus child device.

Modified: head/sys/dev/bhnd/bhnd_bus_if.m
==============================================================================
--- head/sys/dev/bhnd/bhnd_bus_if.m	Fri Aug 26 23:50:44 2016	(r304869)
+++ head/sys/dev/bhnd/bhnd_bus_if.m	Sat Aug 27 00:03:02 2016	(r304870)
@@ -61,6 +61,27 @@ CODE {
 	{
 		panic("bhnd_bus_get_attach_type unimplemented");
 	}
+	
+	static bhnd_clksrc
+	bhnd_bus_null_pwrctl_get_clksrc(device_t dev, device_t child,
+	    bhnd_clock clock)
+	{
+		return (BHND_CLKSRC_UNKNOWN);
+	}
+
+	static int
+	bhnd_bus_null_pwrctl_gate_clock(device_t dev, device_t child,
+	    bhnd_clock clock)
+	{
+		return (ENODEV);
+	}
+
+	static int
+	bhnd_bus_null_pwrctl_ungate_clock(device_t dev, device_t child,
+	    bhnd_clock clock)
+	{
+		return (ENODEV);
+	}
 
 	static int
 	bhnd_bus_null_read_board_info(device_t dev, device_t child,
@@ -74,6 +95,60 @@ CODE {
 	{
 	}
 
+	static int
+	bhnd_bus_null_alloc_pmu(device_t dev, device_t child)
+	{
+		panic("bhnd_bus_alloc_pmu unimplemented");
+	}
+
+	static int
+	bhnd_bus_null_release_pmu(device_t dev, device_t child)
+	{
+		panic("bhnd_bus_release_pmu unimplemented");
+	}
+
+	static int
+	bhnd_bus_null_request_clock(device_t dev, device_t child,
+	    bhnd_clock clock)
+	{
+		panic("bhnd_bus_request_clock unimplemented");
+	}
+
+	static int
+	bhnd_bus_null_enable_clocks(device_t dev, device_t child,
+	    uint32_t clocks)
+	{
+		panic("bhnd_bus_enable_clocks unimplemented");
+	}
+	
+	static int
+	bhnd_bus_null_request_ext_rsrc(device_t dev, device_t child,
+	    u_int rsrc)
+	{
+		panic("bhnd_bus_request_ext_rsrc unimplemented");
+	}
+
+	static int
+	bhnd_bus_null_release_ext_rsrc(device_t dev, device_t child,
+	    u_int rsrc)
+	{
+		panic("bhnd_bus_release_ext_rsrc unimplemented");
+	}
+
+	static uint32_t
+	bhnd_bus_null_read_config(device_t dev, device_t child,
+	    bus_size_t offset, u_int width)
+	{
+		panic("bhnd_bus_null_read_config unimplemented");
+	}
+
+	static void
+	bhnd_bus_null_write_config(device_t dev, device_t child,
+	    bus_size_t offset, uint32_t val, u_int width)
+	{
+		panic("bhnd_bus_null_write_config unimplemented");
+	}
+
 	static device_t
 	bhnd_bus_null_find_hostb_device(device_t dev)
 	{
@@ -261,9 +336,8 @@ METHOD void free_devinfo {
 /**
  * Notify a bhnd bus that a child was added.
  *
- * Called at the end of BUS_ADD_CHILD() to allow the concrete bhnd(4)
- * driver instance to initialize any additional driver-specific state for the
- * child.
+ * This method must be called by concrete bhnd(4) driver impementations
+ * after @p child's bus state is fully initialized.
  *
  * @param dev The bhnd bus whose child is being added.
  * @param child The child added to @p dev.
@@ -304,6 +378,230 @@ METHOD int suspend_core {
 }
 
 /**
+ * If supported by the chipset, return the clock source for the given clock.
+ *
+ * This function is only supported on early PWRCTL-equipped chipsets
+ * that expose clock management via their host bridge interface. Currently,
+ * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9.
+ *
+ * @param dev The parent of @p child.
+ * @param child The bhnd device requesting a clock source.
+ * @param clock The clock for which a clock source will be returned.
+ *
+ * @retval	bhnd_clksrc		The clock source for @p clock.
+ * @retval	BHND_CLKSRC_UNKNOWN	If @p clock is unsupported, or its
+ *					clock source is not known to the bus.
+ */
+METHOD bhnd_clksrc pwrctl_get_clksrc {
+	device_t dev;
+	device_t child;
+	bhnd_clock clock;
+} DEFAULT bhnd_bus_null_pwrctl_get_clksrc;
+
+/**
+ * If supported by the chipset, gate the clock source for @p clock
+ *
+ * This function is only supported on early PWRCTL-equipped chipsets
+ * that expose clock management via their host bridge interface. Currently,
+ * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9.
+ *
+ * @param dev The parent of @p child.
+ * @param child The bhnd device requesting clock gating.
+ * @param clock The clock to be disabled.
+ *
+ * @retval 0 success
+ * @retval ENODEV If bus-level clock source management is not supported.
+ * @retval ENXIO If bus-level management of @p clock is not supported.
+ */
+METHOD int pwrctl_gate_clock {
+	device_t dev;
+	device_t child;
+	bhnd_clock clock;
+} DEFAULT bhnd_bus_null_pwrctl_gate_clock;
+
+/**
+ * If supported by the chipset, ungate the clock source for @p clock
+ *
+ * This function is only supported on early PWRCTL-equipped chipsets
+ * that expose clock management via their host bridge interface. Currently,
+ * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9.
+ *
+ * @param dev The parent of @p child.
+ * @param child The bhnd device requesting clock gating.
+ * @param clock The clock to be enabled.
+ *
+ * @retval 0 success
+ * @retval ENODEV If bus-level clock source management is not supported.
+ * @retval ENXIO If bus-level management of @p clock is not supported.
+ */
+METHOD int pwrctl_ungate_clock {
+	device_t dev;
+	device_t child;
+	bhnd_clock clock;
+} DEFAULT bhnd_bus_null_pwrctl_ungate_clock;
+
+/**
+ * Allocate and enable per-core PMU request handling for @p child.
+ *
+ * The region containing the core's PMU register block (if any) must be
+ * allocated via bus_alloc_resource(9) (or bhnd_alloc_resource) before
+ * calling BHND_BUS_ALLOC_PMU(), and must not be released until after
+ * calling BHND_BUS_RELEASE_PMU().
+ *
+ * @param dev The parent of @p child.
+ * @param child The requesting bhnd device.
+ */
+METHOD int alloc_pmu {
+	device_t dev;
+	device_t child;
+} DEFAULT bhnd_bus_null_alloc_pmu;
+
+/**
+ * Release per-core PMU resources allocated for @p child. Any
+ * outstanding PMU requests are discarded.
+ *
+ * @param dev The parent of @p child.
+ * @param child The requesting bhnd device.
+ */
+METHOD int release_pmu {
+	device_t dev;
+	device_t child;
+} DEFAULT bhnd_bus_null_release_pmu;
+
+/** 
+ * Request that @p clock (or faster) be routed to @p child.
+ * 
+ * A driver must ask the bhnd bus to allocate PMU request state
+ * via BHND_BUS_ALLOC_PMU() before it can request clock resources.
+ * 
+ * Request multiplexing is managed by the bus.
+ *
+ * @param dev The parent of @p child.
+ * @param child The bhnd device requesting @p clock.
+ * @param clock The requested clock source. 
+ *
+ * @retval 0 success
+ * @retval ENODEV If an unsupported clock was requested.
+ * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable.
+ */
+METHOD int request_clock {
+	device_t dev;
+	device_t child;
+	bhnd_clock clock;
+} DEFAULT bhnd_bus_null_request_clock;
+
+/**
+ * Request that @p clocks be powered on behalf of @p child.
+ *
+ * This will power on clock sources (e.g. XTAL, PLL, etc) required for
+ * @p clocks and wait until they are ready, discarding any previous
+ * requests by @p child.
+ *
+ * Request multiplexing is managed by the bus.
+ * 
+ * A driver must ask the bhnd bus to allocate PMU request state
+ * via BHND_BUS_ALLOC_PMU() before it can request clock resources.
+ *

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



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