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 <lan...@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <land...@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 ***
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to