bhnd(4): extend the PMU APIs to support bwn(4)

The bwn(4) driver requires a number of extensions to the bhnd(4) PMU
interface to support external configuration of PLLs, LDOs, and other
parameters that require chipset or PHY-specific workarounds.

These changes add support for:

- Writing raw voltage register values to PHY-specific LDO regulator
  registers (required by LP-PHY).
- Enabling/disabling PHY-specific LDOs (required by LP-PHY)
- Writing to arbitrary PMU chipctrl registers (required for common PHY PLL
  reset support).
- Requesting chipset/PLL-specific spurious signal avoidance modes.
- Querying clock frequency and latency.

Additionally, rather than updating legacy PWRCTL support to conform to the
new PMU interface:

- PWRCTL API is now provided by a bhnd_pwrctl_if.m interface.
- Since PWRCTL is only found in older SSB-based chipsets, translation from
  bhnd(4) bus APIs to corresponding PWRCTL operations is now handled
  entirely within the siba(4) driver.
- The PWRCTL-specific host bridge clock gating APIs in bhnd_bus_if.m have
  been lifted out into a standalone bhnd_pwrctl_hostb_if.m interface.

Approved by:	adrian (mentor, implicit)
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D12664
This commit is contained in:
Landon J. Fuller 2017-11-22 20:27:46 +00:00
parent 512bd18da5
commit 4e96bf3a37
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=326102
38 changed files with 2775 additions and 955 deletions

View File

@ -1250,6 +1250,8 @@ 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/pwrctl/bhnd_pwrctl.c optional bhnd
dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_if.m optional bhnd
dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_hostb_if.m 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

View File

@ -193,7 +193,7 @@ bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
case BHND_IVAR_CORE_UNIT:
return (EINVAL);
case BHND_IVAR_PMU_INFO:
dinfo->pmu_info = (struct bhnd_core_pmu_info *) value;
dinfo->pmu_info = (void *)value;
return (0);
default:
return (ENOENT);
@ -349,17 +349,15 @@ bcma_reset_hw(device_t dev, device_t child, uint16_t ioctl)
static int
bcma_suspend_hw(device_t dev, device_t child)
{
struct bcma_devinfo *dinfo;
struct bhnd_core_pmu_info *pm;
struct bhnd_resource *r;
uint32_t rst;
int error;
struct bcma_devinfo *dinfo;
struct bhnd_resource *r;
uint32_t rst;
int error;
if (device_get_parent(child) != dev)
return (EINVAL);
dinfo = device_get_ivars(child);
pm = dinfo->pmu_info;
/* Can't suspend the core without access to the agent registers */
if ((r = dinfo->res_agent) == NULL)
@ -382,12 +380,6 @@ bcma_suspend_hw(device_t dev, device_t child)
if ((error = bhnd_write_ioctl(child, 0x0, UINT16_MAX)))
return (error);
/* Inform PMU that all outstanding request state should be discarded */
if (pm != NULL) {
if ((error = BHND_PMU_CORE_RELEASE(pm->pm_pmu, pm)))
return (error);
}
return (0);
}

View File

@ -175,17 +175,17 @@ struct bcma_corecfg {
* BCMA per-device info
*/
struct bcma_devinfo {
struct resource_list resources; /**< Slave port memory regions. */
struct bcma_corecfg *corecfg; /**< IP core/block config */
struct resource_list resources; /**< Slave port memory regions. */
struct bcma_corecfg *corecfg; /**< IP core/block config */
struct bhnd_resource *res_agent; /**< Agent (wrapper) resource, or NULL. Not
* all bcma(4) cores have or require an agent. */
int rid_agent; /**< Agent resource ID, or -1 */
struct bhnd_resource *res_agent; /**< Agent (wrapper) resource, or NULL. Not
* all bcma(4) cores have or require an agent. */
int rid_agent; /**< Agent resource ID, or -1 */
u_int num_intrs; /**< number of interrupt descriptors. */
struct bcma_intr_list intrs; /**< interrupt descriptors */
u_int num_intrs; /**< number of interrupt descriptors. */
struct bcma_intr_list intrs; /**< interrupt descriptors */
struct bhnd_core_pmu_info *pmu_info; /**< Bus-managed PMU state, or NULL */
void *pmu_info; /**< Bus-managed PMU state, or NULL */
};

View File

@ -62,15 +62,13 @@ __FBSDID("$FreeBSD$");
#include <sys/rman.h>
#include <machine/resource.h>
#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"
#include "bhnd.h"
#include "bhndreg.h"
#include "bhndvar.h"
#include "bhnd_private.h"
@ -363,24 +361,28 @@ int
bhnd_generic_alloc_pmu(device_t dev, device_t child)
{
struct bhnd_softc *sc;
struct bhnd_resource *br;
struct bhnd_core_pmu_info *pm;
struct bhnd_resource *r;
struct bhnd_core_clkctl *clkctl;
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;
u_int max_latency;
int error;
GIANT_REQUIRED; /* for newbus */
if (device_get_parent(child) != dev)
return (EINVAL);
sc = device_get_softc(dev);
pm = bhnd_get_pmu_info(child);
clkctl = bhnd_get_pmu_info(child);
pmu_regs = BHND_CLK_CTL_ST;
/* already allocated? */
if (pm != NULL) {
if (clkctl != NULL) {
panic("duplicate PMU allocation for %s",
device_get_nameunit(child));
}
@ -440,36 +442,38 @@ 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 */
/* Retain a PMU reference for the clkctl instance state */
pmu_dev = bhnd_retain_provider(child, BHND_SERVICE_PMU);
if (pmu_dev == NULL) {
device_printf(sc->dev,
"pmu unavailable; cannot allocate request state\n");
device_printf(sc->dev, "PMU not found\n");
return (ENXIO);
}
/* Allocate and initialize PMU info */
br = malloc(sizeof(struct bhnd_resource), M_BHND, M_NOWAIT);
if (br == NULL) {
/* Fetch the maximum transition latency from our PMU */
max_latency = bhnd_pmu_get_max_transition_latency(pmu_dev);
/* Allocate a new bhnd_resource wrapping the standard resource we
* fetched from the resource list; we'll free this in
* bhnd_generic_release_pmu() */
r = malloc(sizeof(struct bhnd_resource), M_BHND, M_NOWAIT);
if (r == 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);
r->res = rle->res;
r->direct = ((rman_get_flags(rle->res) & RF_ACTIVE) != 0);
pm = malloc(sizeof(*pm), M_BHND, M_NOWAIT);
if (pm == NULL) {
/* Allocate the clkctl instance */
clkctl = bhnd_alloc_core_clkctl(child, pmu_dev, r, pmu_regs,
max_latency);
if (clkctl == NULL) {
free(r, M_BHND);
bhnd_release_provider(child, pmu_dev, BHND_SERVICE_PMU);
free(br, M_BHND);
return (ENOMEM);
}
pm->pm_dev = child;
pm->pm_res = br;
pm->pm_regs = pmu_regs;
pm->pm_pmu = pmu_dev;
bhnd_set_pmu_info(child, pm);
bhnd_set_pmu_info(child, clkctl);
return (0);
}
@ -479,48 +483,148 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
int
bhnd_generic_release_pmu(device_t dev, device_t child)
{
struct bhnd_softc *sc;
struct bhnd_core_pmu_info *pm;
int error;
struct bhnd_softc *sc;
struct bhnd_core_clkctl *clkctl;
struct bhnd_resource *r;
device_t pmu_dev;
GIANT_REQUIRED; /* for newbus */
sc = device_get_softc(dev);
/* dispatch release request */
pm = bhnd_get_pmu_info(child);
if (pm == NULL)
if (device_get_parent(child) != dev)
return (EINVAL);
clkctl = bhnd_get_pmu_info(child);
if (clkctl == NULL)
panic("pmu over-release for %s", device_get_nameunit(child));
if ((error = BHND_PMU_CORE_RELEASE(pm->pm_pmu, pm)))
return (error);
/* Clear all FORCE, AREQ, and ERSRC flags, unless we're already in
* RESET. Suspending a core clears clkctl automatically (and attempting
* to access the PMU registers in a suspended core will trigger a
* system livelock). */
if (!bhnd_is_hw_suspended(clkctl->cc_dev)) {
BHND_CLKCTL_LOCK(clkctl);
/* free PMU info */
/* Clear all FORCE, AREQ, and ERSRC flags */
BHND_CLKCTL_SET_4(clkctl, 0x0, BHND_CCS_FORCE_MASK |
BHND_CCS_AREQ_MASK | BHND_CCS_ERSRC_REQ_MASK);
BHND_CLKCTL_UNLOCK(clkctl);
}
/* Clear child's PMU info reference */
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);
/* Before freeing the clkctl instance, save a pointer to resources we
* need to clean up manually */
r = clkctl->cc_res;
pmu_dev = clkctl->cc_pmu_dev;
/* Free the clkctl instance */
bhnd_free_core_clkctl(clkctl);
/* Free the child's bhnd resource wrapper */
free(r, M_BHND);
/* Release the child's PMU provider reference */
bhnd_release_provider(child, pmu_dev, BHND_SERVICE_PMU);
return (0);
}
/**
* Default bhnd(4) bus driver implementation of BHND_BUS_GET_CLOCK_LATENCY().
*/
int
bhnd_generic_get_clock_latency(device_t dev, device_t child, bhnd_clock clock,
u_int *latency)
{
struct bhnd_core_clkctl *clkctl;
if (device_get_parent(child) != dev)
return (EINVAL);
if ((clkctl = bhnd_get_pmu_info(child)) == NULL)
panic("no active PMU allocation");
return (bhnd_pmu_get_clock_latency(clkctl->cc_pmu_dev, clock, latency));
}
/**
* Default bhnd(4) bus driver implementation of BHND_BUS_GET_CLOCK_FREQ().
*/
int
bhnd_generic_get_clock_freq(device_t dev, device_t child, bhnd_clock clock,
u_int *freq)
{
struct bhnd_core_clkctl *clkctl;
if (device_get_parent(child) != dev)
return (EINVAL);
if ((clkctl = bhnd_get_pmu_info(child)) == NULL)
panic("no active PMU allocation");
return (bhnd_pmu_get_clock_freq(clkctl->cc_pmu_dev, clock, freq));
}
/**
* 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_core_pmu_info *pm;
struct bhnd_softc *sc;
struct bhnd_core_clkctl *clkctl;
uint32_t avail;
uint32_t req;
int error;
sc = device_get_softc(dev);
if ((pm = bhnd_get_pmu_info(child)) == NULL)
panic("no active PMU request state");
if (device_get_parent(child) != dev)
return (EINVAL);
/* dispatch request to PMU */
return (BHND_PMU_CORE_REQ_CLOCK(pm->pm_pmu, pm, clock));
if ((clkctl = bhnd_get_pmu_info(child)) == NULL)
panic("no active PMU allocation");
BHND_ASSERT_CLKCTL_AVAIL(clkctl);
avail = 0x0;
req = 0x0;
switch (clock) {
case BHND_CLOCK_DYN:
break;
case BHND_CLOCK_ILP:
req |= BHND_CCS_FORCEILP;
break;
case BHND_CLOCK_ALP:
req |= BHND_CCS_FORCEALP;
avail |= BHND_CCS_ALPAVAIL;
break;
case BHND_CLOCK_HT:
req |= BHND_CCS_FORCEHT;
avail |= BHND_CCS_HTAVAIL;
break;
default:
device_printf(dev, "%s requested unknown clock: %#x\n",
device_get_nameunit(clkctl->cc_dev), clock);
return (ENODEV);
}
BHND_CLKCTL_LOCK(clkctl);
/* Issue request */
BHND_CLKCTL_SET_4(clkctl, req, BHND_CCS_FORCE_MASK);
/* Wait for clock availability */
error = bhnd_core_clkctl_wait(clkctl, avail, avail);
BHND_CLKCTL_UNLOCK(clkctl);
return (error);
}
/**
@ -529,16 +633,64 @@ bhnd_generic_request_clock(device_t dev, device_t child, bhnd_clock clock)
int
bhnd_generic_enable_clocks(device_t dev, device_t child, uint32_t clocks)
{
struct bhnd_softc *sc;
struct bhnd_core_pmu_info *pm;
struct bhnd_softc *sc;
struct bhnd_core_clkctl *clkctl;
uint32_t avail;
uint32_t req;
int error;
sc = device_get_softc(dev);
if ((pm = bhnd_get_pmu_info(child)) == NULL)
panic("no active PMU request state");
if (device_get_parent(child) != dev)
return (EINVAL);
/* dispatch request to PMU */
return (BHND_PMU_CORE_EN_CLOCKS(pm->pm_pmu, pm, clocks));
if ((clkctl = bhnd_get_pmu_info(child)) == NULL)
panic("no active PMU allocation");
BHND_ASSERT_CLKCTL_AVAIL(clkctl);
sc = device_get_softc(dev);
avail = 0x0;
req = 0x0;
/* Build clock request flags */
if (clocks & BHND_CLOCK_DYN) /* nothing to enable */
clocks &= ~BHND_CLOCK_DYN;
if (clocks & BHND_CLOCK_ILP) /* nothing to enable */
clocks &= ~BHND_CLOCK_ILP;
if (clocks & BHND_CLOCK_ALP) {
req |= BHND_CCS_ALPAREQ;
avail |= BHND_CCS_ALPAVAIL;
clocks &= ~BHND_CLOCK_ALP;
}
if (clocks & BHND_CLOCK_HT) {
req |= BHND_CCS_HTAREQ;
avail |= BHND_CCS_HTAVAIL;
clocks &= ~BHND_CLOCK_HT;
}
/* Check for unknown clock values */
if (clocks != 0x0) {
device_printf(dev, "%s requested unknown clocks: %#x\n",
device_get_nameunit(clkctl->cc_dev), clocks);
return (ENODEV);
}
BHND_CLKCTL_LOCK(clkctl);
/* Issue request */
BHND_CLKCTL_SET_4(clkctl, req, BHND_CCS_AREQ_MASK);
/* Wait for clock availability */
error = bhnd_core_clkctl_wait(clkctl, avail, avail);
BHND_CLKCTL_UNLOCK(clkctl);
return (error);
}
/**
@ -547,16 +699,41 @@ bhnd_generic_enable_clocks(device_t dev, device_t child, uint32_t clocks)
int
bhnd_generic_request_ext_rsrc(device_t dev, device_t child, u_int rsrc)
{
struct bhnd_softc *sc;
struct bhnd_core_pmu_info *pm;
struct bhnd_softc *sc;
struct bhnd_core_clkctl *clkctl;
uint32_t req;
uint32_t avail;
int error;
sc = device_get_softc(dev);
if ((pm = bhnd_get_pmu_info(child)) == NULL)
panic("no active PMU request state");
if (device_get_parent(child) != dev)
return (EINVAL);
/* dispatch request to PMU */
return (BHND_PMU_CORE_REQ_EXT_RSRC(pm->pm_pmu, pm, rsrc));
if ((clkctl = bhnd_get_pmu_info(child)) == NULL)
panic("no active PMU allocation");
BHND_ASSERT_CLKCTL_AVAIL(clkctl);
sc = device_get_softc(dev);
if (rsrc > BHND_CCS_ERSRC_MAX)
return (EINVAL);
req = BHND_CCS_SET_BITS((1<<rsrc), BHND_CCS_ERSRC_REQ);
avail = BHND_CCS_SET_BITS((1<<rsrc), BHND_CCS_ERSRC_STS);
BHND_CLKCTL_LOCK(clkctl);
/* Write request */
BHND_CLKCTL_SET_4(clkctl, req, req);
/* Wait for resource availability */
error = bhnd_core_clkctl_wait(clkctl, avail, avail);
BHND_CLKCTL_UNLOCK(clkctl);
return (error);
}
/**
@ -565,19 +742,36 @@ bhnd_generic_request_ext_rsrc(device_t dev, device_t child, u_int rsrc)
int
bhnd_generic_release_ext_rsrc(device_t dev, device_t child, u_int rsrc)
{
struct bhnd_softc *sc;
struct bhnd_core_pmu_info *pm;
struct bhnd_softc *sc;
struct bhnd_core_clkctl *clkctl;
uint32_t mask;
sc = device_get_softc(dev);
if ((pm = bhnd_get_pmu_info(child)) == NULL)
panic("no active PMU request state");
if (device_get_parent(child) != dev)
return (EINVAL);
/* dispatch request to PMU */
return (BHND_PMU_CORE_RELEASE_EXT_RSRC(pm->pm_pmu, pm, rsrc));
if ((clkctl = bhnd_get_pmu_info(child)) == NULL)
panic("no active PMU allocation");
BHND_ASSERT_CLKCTL_AVAIL(clkctl);
sc = device_get_softc(dev);
if (rsrc > BHND_CCS_ERSRC_MAX)
return (EINVAL);
mask = BHND_CCS_SET_BITS((1<<rsrc), BHND_CCS_ERSRC_REQ);
/* Clear request */
BHND_CLKCTL_LOCK(clkctl);
BHND_CLKCTL_SET_4(clkctl, 0x0, mask);
BHND_CLKCTL_UNLOCK(clkctl);
return (0);
}
/**
* Default bhnd(4) bus driver implementation of BHND_BUS_IS_REGION_VALID().
*
@ -954,6 +1148,8 @@ static device_method_t bhnd_methods[] = {
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_get_clock_latency, bhnd_generic_get_clock_latency),
DEVMETHOD(bhnd_bus_get_clock_freq, bhnd_generic_get_clock_freq),
DEVMETHOD(bhnd_bus_is_region_valid, bhnd_generic_is_region_valid),
DEVMETHOD(bhnd_bus_get_nvram_var, bhnd_generic_get_nvram_var),

View File

@ -52,8 +52,6 @@
#include "nvram/bhnd_nvram.h"
struct bhnd_core_pmu_info;
extern devclass_t bhnd_devclass;
extern devclass_t bhnd_hostb_devclass;
extern devclass_t bhnd_nvram_devclass;
@ -155,7 +153,7 @@ BHND_ACCESSOR(vendor_name, VENDOR_NAME, const char *);
BHND_ACCESSOR(device_name, DEVICE_NAME, const char *);
BHND_ACCESSOR(core_index, CORE_INDEX, u_int);
BHND_ACCESSOR(core_unit, CORE_UNIT, int);
BHND_ACCESSOR(pmu_info, PMU_INFO, struct bhnd_core_pmu_info *);
BHND_ACCESSOR(pmu_info, PMU_INFO, void *);
#undef BHND_ACCESSOR
@ -858,67 +856,6 @@ bhnd_suspend_hw(device_t dev)
return (BHND_BUS_SUSPEND_HW(device_get_parent(dev), 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.
*
@ -1066,8 +1003,7 @@ bhnd_unmap_intr(device_t dev, rman_res_t irq)
* 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.
* @param dev The requesting bhnd device.
*
* @retval 0 success
* @retval non-zero If allocating PMU request state otherwise fails, a
@ -1083,8 +1019,7 @@ bhnd_alloc_pmu(device_t 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.
* @param dev The requesting bhnd device.
*
* @retval 0 success
* @retval non-zero If releasing PMU request state otherwise fails, a
@ -1097,6 +1032,51 @@ bhnd_release_pmu(device_t dev)
return (BHND_BUS_RELEASE_PMU(device_get_parent(dev), dev));
}
/**
* Return the transition latency required for @p clock in microseconds, if
* known.
*
* The BHND_CLOCK_HT latency value is suitable for use as the D11 core's
* 'fastpwrup_dly' value.
*
* @note A driver must ask the bhnd bus to allocate PMU request state
* via BHND_BUS_ALLOC_PMU() before querying PMU clocks.
*
* @param dev The requesting bhnd device.
* @param clock The clock to be queried for transition latency.
* @param[out] latency On success, the transition latency of @p clock in
* microseconds.
*
* @retval 0 success
* @retval ENODEV If the transition latency for @p clock is not available.
*/
static inline int
bhnd_get_clock_latency(device_t dev, bhnd_clock clock, u_int *latency)
{
return (BHND_BUS_GET_CLOCK_LATENCY(device_get_parent(dev), dev, clock,
latency));
}
/**
* Return the frequency for @p clock in Hz, if known.
*
* @param dev The requesting bhnd device.
* @param clock The clock to be queried.
* @param[out] freq On success, the frequency of @p clock in Hz.
*
* @note A driver must ask the bhnd bus to allocate PMU request state
* via BHND_BUS_ALLOC_PMU() before querying PMU clocks.
*
* @retval 0 success
* @retval ENODEV If the frequency for @p clock is not available.
*/
static inline int
bhnd_get_clock_freq(device_t dev, bhnd_clock clock, u_int *freq)
{
return (BHND_BUS_GET_CLOCK_FREQ(device_get_parent(dev), dev, clock,
freq));
}
/**
* Request that @p clock (or faster) be routed to @p dev.
*

View File

@ -114,27 +114,6 @@ 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,
struct bhnd_board_info *info)
@ -159,6 +138,20 @@ CODE {
panic("bhnd_bus_release_pmu unimplemented");
}
static int
bhnd_bus_null_get_clock_latency(device_t dev, device_t child,
bhnd_clock clock, u_int *latency)
{
panic("bhnd_pmu_get_clock_latency unimplemented");
}
static int
bhnd_bus_null_get_clock_freq(device_t dev, device_t child,
bhnd_clock clock, u_int *freq)
{
panic("bhnd_pmu_get_clock_freq unimplemented");
}
static int
bhnd_bus_null_request_clock(device_t dev, device_t child,
bhnd_clock clock)
@ -671,70 +664,7 @@ METHOD int suspend_hw {
} DEFAULT bhnd_bus_null_suspend_hw;
/**
* 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.
* Allocate per-core PMU resources and enable 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
@ -743,6 +673,10 @@ METHOD int pwrctl_ungate_clock {
*
* @param dev The parent of @p child.
* @param child The requesting bhnd device.
*
* @retval 0 success
* @retval non-zero if enabling per-core PMU request handling fails, a
* regular unix error code will be returned.
*/
METHOD int alloc_pmu {
device_t dev;
@ -761,6 +695,53 @@ METHOD int release_pmu {
device_t child;
} DEFAULT bhnd_bus_null_release_pmu;
/**
* Return the transition latency required for @p clock in microseconds, if
* known.
*
* The BHND_CLOCK_HT latency value is suitable for use as the D11 core's
* 'fastpwrup_dly' value.
*
* @note A driver must ask the bhnd bus to allocate PMU request state
* via BHND_BUS_ALLOC_PMU() before querying PMU clocks.
*
* @param dev The parent of @p child.
* @param child The requesting bhnd device.
* @param clock The clock to be queried for transition latency.
* @param[out] latency On success, the transition latency of @p clock in
* microseconds.
*
* @retval 0 success
* @retval ENODEV If the transition latency for @p clock is not available.
*/
METHOD int get_clock_latency {
device_t dev;
device_t child;
bhnd_clock clock;
u_int *latency;
} DEFAULT bhnd_bus_null_get_clock_latency;
/**
* Return the frequency for @p clock in Hz, if known.
*
* @param dev The parent of @p child.
* @param child The requesting bhnd device.
* @param clock The clock to be queried.
* @param[out] freq On success, the frequency of @p clock in Hz.
*
* @note A driver must ask the bhnd bus to allocate PMU request state
* via BHND_BUS_ALLOC_PMU() before querying PMU clocks.
*
* @retval 0 success
* @retval ENODEV If the frequency for @p clock is not available.
*/
METHOD int get_clock_freq {
device_t dev;
device_t child;
bhnd_clock clock;
u_int *freq;
} DEFAULT bhnd_bus_null_get_clock_freq;
/**
* Request that @p clock (or faster) be routed to @p child.
*
@ -772,11 +753,13 @@ METHOD int release_pmu {
*
* @param dev The parent of @p child.
* @param child The bhnd device requesting @p clock.
* @param clock The requested clock source.
* @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.
* @retval 0 success
* @retval ENODEV If an unsupported clock was requested.
* @retval ETIMEDOUT If the clock request succeeds, but the clock is not
* detected as ready within the PMU's maximum transition
* delay. This should not occur in normal operation.
*/
METHOD int request_clock {
device_t dev;
@ -801,9 +784,11 @@ METHOD int request_clock {
* @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.
* @retval 0 success
* @retval ENODEV If an unsupported clock was requested.
* @retval ETIMEDOUT If the clock request succeeds, but the clock is not
* detected as ready within the PMU's maximum transition
* delay. This should not occur in normal operation.
*/
METHOD int enable_clocks {
device_t dev;
@ -824,9 +809,11 @@ METHOD int enable_clocks {
* @param child The bhnd device requesting @p rsrc.
* @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.
* @retval 0 success
* @retval ENODEV If the PMU does not support @p rsrc.
* @retval ETIMEDOUT If the clock request succeeds, but the clock is not
* detected as ready within the PMU's maximum transition
* delay. This should not occur in normal operation.
*/
METHOD int request_ext_rsrc {
device_t dev;
@ -844,9 +831,11 @@ METHOD int request_ext_rsrc {
* @param child The bhnd device requesting @p rsrc.
* @param rsrc The core-specific external resource number.
*
* @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.
* @retval 0 success
* @retval ENODEV If the PMU does not support @p rsrc.
* @retval ETIMEDOUT If the clock request succeeds, but the clock is not
* detected as ready within the PMU's maximum transition
* delay. This should not occur in normal operation.
*/
METHOD int release_ext_rsrc {
device_t dev;

View File

@ -54,4 +54,49 @@ struct bhnd_service_entry {
STAILQ_ENTRY(bhnd_service_entry) link;
};
/**
* bhnd(4) per-core PMU clkctl quirks.
*/
enum {
/** On BCM4328-derived chipsets, the CLK_CTL_ST register CCS_HTAVAIL
* and CCS_ALPAVAIL bits are swapped in the ChipCommon and PCMCIA
* cores; the BHND_CCS0_* constants should be used. */
BHND_CLKCTL_QUIRK_CCS0 = 1
};
/**
* Per-core bhnd(4) PMU clkctl registers.
*/
struct bhnd_core_clkctl {
device_t cc_dev; /**< core device */
device_t cc_pmu_dev; /**< pmu device */
uint32_t cc_quirks; /**< core-specific clkctl quirks */
struct bhnd_resource *cc_res; /**< resource mapping core's clkctl register */
bus_size_t cc_res_offset; /**< offset to clkctl register */
u_int cc_max_latency; /**< maximum PMU transition latency, in microseconds */
struct mtx cc_mtx; /**< register read/modify/write lock */
};
#define BHND_ASSERT_CLKCTL_AVAIL(_clkctl) \
KASSERT(!bhnd_is_hw_suspended((_clkctl)->cc_dev), \
("reading clkctl on suspended core will trigger system livelock"))
#define BHND_CLKCTL_LOCK_INIT(_clkctl) mtx_init(&(_clkctl)->cc_mtx, \
device_get_nameunit((_clkctl)->cc_dev), NULL, MTX_DEF)
#define BHND_CLKCTL_LOCK(_clkctl) mtx_lock(&(_clkctl)->cc_mtx)
#define BHND_CLKCTL_UNLOCK(_clkctl) mtx_unlock(&(_clkctl)->cc_mtx)
#define BHND_CLKCTL_LOCK_ASSERT(_clkctl, what) \
mtx_assert(&(_clkctl)->cc_mtx, what)
#define BHND_CLKCTL_LOCK_DESTROY(_clkctl) mtx_destroy(&(_clkctl->cc_mtx))
#define BHND_CLKCTL_READ_4(_clkctl) \
bhnd_bus_read_4((_clkctl)->cc_res, (_clkctl)->cc_res_offset)
#define BHND_CLKCTL_WRITE_4(_clkctl, _val) \
bhnd_bus_write_4((_clkctl)->cc_res, (_clkctl)->cc_res_offset, (_val))
#define BHND_CLKCTL_SET_4(_clkctl, _val, _mask) \
BHND_CLKCTL_WRITE_4((_clkctl), \
((_val) & (_mask)) | (BHND_CLKCTL_READ_4(_clkctl) & ~(_mask)))
#endif /* _BHND_BHND_PRIVATE_H_ */

View File

@ -171,6 +171,34 @@ static const struct bhnd_core_desc {
{ 0, 0, 0, NULL }
};
static const struct bhnd_device_quirk bhnd_chipc_clkctl_quirks[];
static const struct bhnd_device_quirk bhnd_pcmcia_clkctl_quirks[];
/**
* Device table entries for core-specific CLKCTL quirk lookup.
*/
static const struct bhnd_device bhnd_clkctl_devices[] = {
BHND_DEVICE(BCM, CC, NULL, bhnd_chipc_clkctl_quirks),
BHND_DEVICE(BCM, PCMCIA, NULL, bhnd_pcmcia_clkctl_quirks),
BHND_DEVICE_END,
};
/** ChipCommon CLKCTL quirks */
static const struct bhnd_device_quirk bhnd_chipc_clkctl_quirks[] = {
/* HTAVAIL/ALPAVAIL are bitswapped in chipc's CLKCTL */
BHND_CHIP_QUIRK(4328, HWREV_ANY, BHND_CLKCTL_QUIRK_CCS0),
BHND_CHIP_QUIRK(5354, HWREV_ANY, BHND_CLKCTL_QUIRK_CCS0),
BHND_DEVICE_QUIRK_END
};
/** PCMCIA CLKCTL quirks */
static const struct bhnd_device_quirk bhnd_pcmcia_clkctl_quirks[] = {
/* HTAVAIL/ALPAVAIL are bitswapped in pcmcia's CLKCTL */
BHND_CHIP_QUIRK(4328, HWREV_ANY, BHND_CLKCTL_QUIRK_CCS0),
BHND_CHIP_QUIRK(5354, HWREV_ANY, BHND_CLKCTL_QUIRK_CCS0),
BHND_DEVICE_QUIRK_END
};
/**
* Return the name for a given JEP106 manufacturer ID.
*
@ -1180,6 +1208,119 @@ bhnd_read_chipid(device_t dev, struct resource_spec *rs,
return (error);
}
/**
* Allocate and return a new per-core PMU clock control/status (clkctl)
* instance for @p dev.
*
* @param dev The bhnd(4) core device mapped by @p r.
* @param pmu_dev The bhnd(4) PMU device, implmenting the bhnd_pmu_if
* interface. The caller is responsible for ensuring that
* this reference remains valid for the lifetime of the
* returned clkctl instance.
* @param r A resource mapping the core's clock control register
* (see BHND_CLK_CTL_ST). The caller is responsible for
* ensuring that this resource remains valid for the
* lifetime of the returned clkctl instance.
* @param offset The offset to the clock control register within @p r.
* @param max_latency The PMU's maximum state transition latency in
* microseconds; this upper bound will be used to busy-wait
* on PMU state transitions.
*
* @retval non-NULL success
* @retval NULL if allocation fails.
*
*/
struct bhnd_core_clkctl *
bhnd_alloc_core_clkctl(device_t dev, device_t pmu_dev, struct bhnd_resource *r,
bus_size_t offset, u_int max_latency)
{
struct bhnd_core_clkctl *clkctl;
clkctl = malloc(sizeof(*clkctl), M_BHND, M_ZERO | M_NOWAIT);
if (clkctl == NULL)
return (NULL);
clkctl->cc_dev = dev;
clkctl->cc_pmu_dev = pmu_dev;
clkctl->cc_res = r;
clkctl->cc_res_offset = offset;
clkctl->cc_max_latency = max_latency;
clkctl->cc_quirks = bhnd_device_quirks(dev, bhnd_clkctl_devices,
sizeof(bhnd_clkctl_devices[0]));
BHND_CLKCTL_LOCK_INIT(clkctl);
return (clkctl);
}
/**
* Free a clkctl instance previously allocated via bhnd_alloc_core_clkctl().
*
* @param clkctl The clkctl instance to be freed.
*/
void
bhnd_free_core_clkctl(struct bhnd_core_clkctl *clkctl)
{
BHND_CLKCTL_LOCK_DESTROY(clkctl);
free(clkctl, M_BHND);
}
/**
* Wait for the per-core clock status to be equal to @p value after
* applying @p mask, timing out after the maximum transition latency is reached.
*
* @param clkctl Per-core clkctl state to be queryied.
* @param value Value to wait for.
* @param mask Mask to apply prior to value comparison.
*
* @retval 0 success
* @retval ETIMEDOUT if the PMU's maximum transition delay is reached before
* the clock status matches @p value and @p mask.
*/
int
bhnd_core_clkctl_wait(struct bhnd_core_clkctl *clkctl, uint32_t value,
uint32_t mask)
{
uint32_t clkst;
BHND_CLKCTL_LOCK_ASSERT(clkctl, MA_OWNED);
/* Bitswapped HTAVAIL/ALPAVAIL work-around */
if (clkctl->cc_quirks & BHND_CLKCTL_QUIRK_CCS0) {
uint32_t fmask, fval;
fmask = mask & ~(BHND_CCS_HTAVAIL | BHND_CCS_ALPAVAIL);
fval = value & ~(BHND_CCS_HTAVAIL | BHND_CCS_ALPAVAIL);
if (mask & BHND_CCS_HTAVAIL)
fmask |= BHND_CCS0_HTAVAIL;
if (value & BHND_CCS_HTAVAIL)
fval |= BHND_CCS0_HTAVAIL;
if (mask & BHND_CCS_ALPAVAIL)
fmask |= BHND_CCS0_ALPAVAIL;
if (value & BHND_CCS_ALPAVAIL)
fval |= BHND_CCS0_ALPAVAIL;
mask = fmask;
value = fval;
}
for (u_int i = 0; i < clkctl->cc_max_latency; i += 10) {
clkst = bhnd_bus_read_4(clkctl->cc_res, clkctl->cc_res_offset);
if ((clkst & mask) == (value & mask))
return (0);
DELAY(10);
}
device_printf(clkctl->cc_dev, "clkst wait timeout (value=%#x, "
"mask=%#x)\n", value, mask);
return (ETIMEDOUT);
}
/**
* Read an NVRAM variable's NUL-terminated string value.
*
@ -2351,4 +2492,4 @@ uintptr_t
bhnd_bus_generic_get_intr_domain(device_t dev, device_t child, bool self)
{
return ((uintptr_t)dev);
}
}

View File

@ -72,6 +72,7 @@ typedef enum {
/** bhnd(4) platform services. */
typedef enum {
BHND_SERVICE_CHIPC, /**< chipcommon service; implements the bhnd_chipc interface */
BHND_SERVICE_PWRCTL, /**< legacy pwrctl service; implements the bhnd_pwrctl interface */
BHND_SERVICE_PMU, /**< pmu service; implements the bhnd_pmu interface */
BHND_SERVICE_NVRAM, /**< nvram service; implements the bhnd_nvram interface */

View File

@ -42,6 +42,8 @@ __FBSDID("$FreeBSD$");
#include <dev/bhnd/bhnd_ids.h>
#include <dev/bhnd/bhnd.h>
#include "bhnd_pwrctl_hostb_if.h"
#include "bhndbvar.h"
/*
@ -116,7 +118,7 @@ bhnd_bhndb_pwrctl_get_clksrc(device_t dev, device_t child,
bhnd_clock clock)
{
/* Delegate to parent bridge */
return (BHND_BUS_PWRCTL_GET_CLKSRC(device_get_parent(dev), child,
return (BHND_PWRCTL_HOSTB_GET_CLKSRC(device_get_parent(dev), child,
clock));
}
@ -125,7 +127,7 @@ bhnd_bhndb_pwrctl_gate_clock(device_t dev, device_t child,
bhnd_clock clock)
{
/* Delegate to parent bridge */
return (BHND_BUS_PWRCTL_GATE_CLOCK(device_get_parent(dev), child,
return (BHND_PWRCTL_HOSTB_GATE_CLOCK(device_get_parent(dev), child,
clock));
}
@ -134,7 +136,7 @@ bhnd_bhndb_pwrctl_ungate_clock(device_t dev, device_t child,
bhnd_clock clock)
{
/* Delegate to parent bridge */
return (BHND_BUS_PWRCTL_UNGATE_CLOCK(device_get_parent(dev), child,
return (BHND_PWRCTL_HOSTB_UNGATE_CLOCK(device_get_parent(dev), child,
clock));
}
@ -171,19 +173,20 @@ bhnd_bhndb_setup_intr(device_t dev, device_t child, struct resource *irq,
static device_method_t bhnd_bhndb_methods[] = {
/* Bus interface */
DEVMETHOD(bus_setup_intr, bhnd_bhndb_setup_intr),
DEVMETHOD(bus_setup_intr, bhnd_bhndb_setup_intr),
/* BHND interface */
DEVMETHOD(bhnd_bus_get_attach_type, bhnd_bhndb_get_attach_type),
DEVMETHOD(bhnd_bus_is_hw_disabled, bhnd_bhndb_is_hw_disabled),
DEVMETHOD(bhnd_bus_find_hostb_device, bhnd_bhndb_find_hostb_device),
DEVMETHOD(bhnd_bus_read_board_info, bhnd_bhndb_read_board_info),
DEVMETHOD(bhnd_bus_map_intr, bhnd_bhndb_map_intr),
DEVMETHOD(bhnd_bus_unmap_intr, bhnd_bhndb_unmap_intr),
DEVMETHOD(bhnd_bus_get_attach_type, bhnd_bhndb_get_attach_type),
DEVMETHOD(bhnd_bus_is_hw_disabled, bhnd_bhndb_is_hw_disabled),
DEVMETHOD(bhnd_bus_find_hostb_device, bhnd_bhndb_find_hostb_device),
DEVMETHOD(bhnd_bus_read_board_info, bhnd_bhndb_read_board_info),
DEVMETHOD(bhnd_bus_map_intr, bhnd_bhndb_map_intr),
DEVMETHOD(bhnd_bus_unmap_intr, bhnd_bhndb_unmap_intr),
DEVMETHOD(bhnd_bus_pwrctl_get_clksrc, bhnd_bhndb_pwrctl_get_clksrc),
DEVMETHOD(bhnd_bus_pwrctl_gate_clock, bhnd_bhndb_pwrctl_gate_clock),
DEVMETHOD(bhnd_bus_pwrctl_ungate_clock, bhnd_bhndb_pwrctl_ungate_clock),
/* BHND PWRCTL hostb interface */
DEVMETHOD(bhnd_pwrctl_hostb_get_clksrc, bhnd_bhndb_pwrctl_get_clksrc),
DEVMETHOD(bhnd_pwrctl_hostb_gate_clock, bhnd_bhndb_pwrctl_gate_clock),
DEVMETHOD(bhnd_pwrctl_hostb_ungate_clock, bhnd_bhndb_pwrctl_ungate_clock),
DEVMETHOD_END
};

View File

@ -68,6 +68,8 @@ __FBSDID("$FreeBSD$");
#include <dev/bhnd/cores/pci/bhnd_pcireg.h>
#include "bhnd_pwrctl_hostb_if.h"
#include "bhndb_pcireg.h"
#include "bhndb_pcivar.h"
#include "bhndb_private.h"
@ -1136,11 +1138,11 @@ bhndb_pci_pwrctl_get_clksrc(device_t dev, device_t child,
/* Only supported on PCI devices */
if (bhndb_is_pcie_attached(sc->dev))
return (ENODEV);
return (BHND_CLKSRC_UNKNOWN);
/* Only ILP is supported */
if (clock != BHND_CLOCK_ILP)
return (ENXIO);
return (BHND_CLKSRC_UNKNOWN);
gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4);
if (gpio_out & BHNDB_PCI_GPIO_SCS)
@ -1451,22 +1453,22 @@ bhndb_pci_eio_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
static device_method_t bhndb_pci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, bhndb_pci_probe),
DEVMETHOD(device_attach, bhndb_pci_attach),
DEVMETHOD(device_resume, bhndb_pci_resume),
DEVMETHOD(device_suspend, bhndb_pci_suspend),
DEVMETHOD(device_detach, bhndb_pci_detach),
/* BHND interface */
DEVMETHOD(bhnd_bus_pwrctl_get_clksrc, bhndb_pci_pwrctl_get_clksrc),
DEVMETHOD(bhnd_bus_pwrctl_gate_clock, bhndb_pci_pwrctl_gate_clock),
DEVMETHOD(bhnd_bus_pwrctl_ungate_clock, bhndb_pci_pwrctl_ungate_clock),
DEVMETHOD(device_probe, bhndb_pci_probe),
DEVMETHOD(device_attach, bhndb_pci_attach),
DEVMETHOD(device_resume, bhndb_pci_resume),
DEVMETHOD(device_suspend, bhndb_pci_suspend),
DEVMETHOD(device_detach, bhndb_pci_detach),
/* BHNDB interface */
DEVMETHOD(bhndb_set_window_addr, bhndb_pci_set_window_addr),
DEVMETHOD(bhndb_populate_board_info, bhndb_pci_populate_board_info),
DEVMETHOD(bhndb_map_intr_isrc, bhndb_pci_map_intr_isrc),
DEVMETHOD(bhndb_route_interrupts, bhndb_pci_route_interrupts),
DEVMETHOD(bhndb_set_window_addr, bhndb_pci_set_window_addr),
DEVMETHOD(bhndb_populate_board_info, bhndb_pci_populate_board_info),
DEVMETHOD(bhndb_map_intr_isrc, bhndb_pci_map_intr_isrc),
DEVMETHOD(bhndb_route_interrupts, bhndb_pci_route_interrupts),
/* BHND PWRCTL hostb interface */
DEVMETHOD(bhnd_pwrctl_hostb_get_clksrc, bhndb_pci_pwrctl_get_clksrc),
DEVMETHOD(bhnd_pwrctl_hostb_gate_clock, bhndb_pci_pwrctl_gate_clock),
DEVMETHOD(bhnd_pwrctl_hostb_ungate_clock, bhndb_pci_pwrctl_ungate_clock),
DEVMETHOD_END
};

View File

@ -1,31 +1,24 @@
/*-
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2010 Broadcom Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
* Portions of this file were derived from the sbchipc.h header contributed by
* Broadcom to to the Linux staging repository, as well as later revisions of
* sbchipc.h distributed with the Asus RT-N16 firmware source code release.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $FreeBSD$
*/
@ -49,4 +42,50 @@
*/
#define BHND_DEFAULT_ENUM_SIZE 0x00100000
#endif /* _BHND_BHNDREG_H_ */
/*
* Common per-core clock control/status register available on PMU-equipped
* devices.
*
* Clock Mode Name Description
* High Throughput (HT) Full bandwidth, low latency. Generally supplied
* from PLL.
* Active Low Power (ALP) Register access, low speed DMA.
* Idle Low Power (ILP) No interconnect activity, or if long latency
* is permitted.
*/
#define BHND_CLK_CTL_ST 0x1e0 /**< clock control and status */
#define BHND_CCS_FORCEALP 0x00000001 /**< force ALP request */
#define BHND_CCS_FORCEHT 0x00000002 /**< force HT request */
#define BHND_CCS_FORCEILP 0x00000004 /**< force ILP request */
#define BHND_CCS_FORCE_MASK 0x0000000F
#define BHND_CCS_ALPAREQ 0x00000008 /**< ALP Avail Request */
#define BHND_CCS_HTAREQ 0x00000010 /**< HT Avail Request */
#define BHND_CCS_AREQ_MASK 0x00000018
#define BHND_CCS_FORCEHWREQOFF 0x00000020 /**< Force HW Clock Request Off */
#define BHND_CCS_ERSRC_REQ_MASK 0x00000700 /**< external resource requests */
#define BHND_CCS_ERSRC_REQ_SHIFT 8
#define BHND_CCS_ERSRC_MAX 2 /**< maximum ERSRC value (corresponding to bits 0-2) */
#define BHND_CCS_ALPAVAIL 0x00010000 /**< ALP is available */
#define BHND_CCS_HTAVAIL 0x00020000 /**< HT is available */
#define BHND_CCS_AVAIL_MASK 0x00030000
#define BHND_CCS_BP_ON_APL 0x00040000 /**< RO: Backplane is running on ALP clock */
#define BHND_CCS_BP_ON_HT 0x00080000 /**< RO: Backplane is running on HT clock */
#define BHND_CCS_ERSRC_STS_MASK 0x07000000 /**< external resource status */
#define BHND_CCS_ERSRC_STS_SHIFT 24
#define BHND_CCS0_HTAVAIL 0x00010000 /**< HT avail in chipc and pcmcia on 4328a0 */
#define BHND_CCS0_ALPAVAIL 0x00020000 /**< ALP avail in chipc and pcmcia on 4328a0 */
#define BHND_CCS_GET_FLAG(_value, _flag) \
(((_value) & _flag) != 0)
#define BHND_CCS_GET_BITS(_value, _field) \
(((_value) & _field ## _MASK) >> _field ## _SHIFT)
#define BHND_CCS_SET_BITS(_value, _field) \
(((_value) << _field ## _SHIFT) & _field ## _MASK)
#endif /* _BHND_BHNDREG_H_ */

View File

@ -38,10 +38,7 @@
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/sx.h>
#include "bhnd.h"
@ -52,6 +49,17 @@
MALLOC_DECLARE(M_BHND);
DECLARE_CLASS(bhnd_driver);
struct bhnd_core_clkctl;
struct bhnd_core_clkctl *bhnd_alloc_core_clkctl(device_t dev,
device_t pmu_dev, struct bhnd_resource *r,
bus_size_t offset, u_int max_latency);
void bhnd_free_core_clkctl(
struct bhnd_core_clkctl *clkctl);
int bhnd_core_clkctl_wait(
struct bhnd_core_clkctl *clkctl,
uint32_t value, uint32_t mask);
int bhnd_generic_attach(device_t dev);
int bhnd_generic_detach(device_t dev);
int bhnd_generic_shutdown(device_t dev);
@ -65,6 +73,12 @@ int bhnd_generic_alloc_pmu(device_t dev,
device_t child);
int bhnd_generic_release_pmu(device_t dev,
device_t child);
int bhnd_generic_get_clock_latency(device_t dev,
device_t child, bhnd_clock clock,
u_int *latency);
int bhnd_generic_get_clock_freq(device_t dev,
device_t child, bhnd_clock clock,
u_int *freq);
int bhnd_generic_request_clock(device_t dev,
device_t child, bhnd_clock clock);
int bhnd_generic_enable_clocks(device_t dev,

View File

@ -4,8 +4,8 @@
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Landon Fuller under sponsorship from
* the FreeBSD Foundation.
* 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
@ -209,11 +209,17 @@ chipc_attach(device_t dev)
if ((error = chipc_add_children(sc)))
goto failed;
if ((error = bus_generic_attach(dev)))
/*
* Register ourselves with the bus; we're fully initialized and can
* response to ChipCommin API requests.
*
* Since our children may need access to ChipCommon, this must be done
* before attaching our children below (via bus_generic_attach).
*/
if ((error = bhnd_register_provider(dev, BHND_SERVICE_CHIPC)))
goto failed;
/* Register ourselves with the bus */
if ((error = bhnd_register_provider(dev, BHND_SERVICE_CHIPC)))
if ((error = bus_generic_attach(dev)))
goto failed;
return (0);
@ -286,12 +292,18 @@ chipc_add_children(struct chipc_softc *sc)
* On AOB ("Always on Bus") devices, the PMU core (if it exists) is
* attached directly to the bhnd(4) bus -- not chipc.
*/
if (sc->caps.pwr_ctrl || (sc->caps.pmu && !sc->caps.aob)) {
child = BUS_ADD_CHILD(sc->dev, 0, "bhnd_pmu", -1);
if (sc->caps.pmu && !sc->caps.aob) {
child = BUS_ADD_CHILD(sc->dev, 0, "bhnd_pmu", 0);
if (child == NULL) {
device_printf(sc->dev, "failed to add pmu\n");
return (ENXIO);
}
} else if (sc->caps.pwr_ctrl) {
child = BUS_ADD_CHILD(sc->dev, 0, "bhnd_pwrctl", 0);
if (child == NULL) {
device_printf(sc->dev, "failed to add pwrctl\n");
return (ENXIO);
}
}
/* All remaining devices are SoC-only */

View File

@ -4,11 +4,11 @@
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Landon Fuller under sponsorship from
* the FreeBSD Foundation.
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* This file is derived from the siutils.c source distributed with the
* Asus RT-N16 firmware source code release.
* Portions of this file were derived from the siutils.c source distributed with
* the Asus RT-N16 firmware source code release.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -45,14 +45,16 @@ __FBSDID("$FreeBSD$");
#include <dev/bhnd/cores/pmu/bhnd_pmureg.h>
#include "bhnd_chipc_if.h"
#include "bhnd_pwrctl_if.h"
#include "bhnd_pwrctl_hostb_if.h"
#include "bhnd_pwrctl_private.h"
/*
* ChipCommon Power Control.
*
* Provides a bhnd_pmu_if-compatible interface to device clocking and
* power management on non-PMU chipsets.
* Provides a runtime interface to device clocking and power management on
* legacy non-PMU chipsets.
*/
typedef enum {
@ -125,9 +127,6 @@ bhnd_pwrctl_attach(device_t dev)
sc = device_get_softc(dev);
/* TODO: Need further testing on actual PWRCTL hardware */
device_printf(dev, "WARNING: Using untested PWRCTL support\n");
sc->dev = dev;
sc->chipc_dev = device_get_parent(dev);
sc->quirks = bhnd_device_quirks(sc->chipc_dev, pwrctl_devices,
@ -184,10 +183,10 @@ bhnd_pwrctl_attach(device_t dev)
PWRCTL_UNLOCK(sc);
/* Register as the bus PMU provider */
if ((error = bhnd_register_provider(dev, BHND_SERVICE_PMU))) {
device_printf(sc->dev, "failed to register PMU with bus : %d\n",
error);
/* Register as the bus PWRCTL provider */
if ((error = bhnd_register_provider(dev, BHND_SERVICE_PWRCTL))) {
device_printf(sc->dev, "failed to register PWRCTL with bus : "
"%d\n", error);
goto cleanup;
}
@ -268,22 +267,61 @@ bhnd_pwrctl_resume(device_t dev)
return (error);
}
static int
bhnd_pwrctl_get_clock_latency(device_t dev, bhnd_clock clock,
u_int *latency)
{
struct bhnd_pwrctl_softc *sc = device_get_softc(dev);
switch (clock) {
case BHND_CLOCK_HT:
PWRCTL_LOCK(sc);
*latency = bhnd_pwrctl_fast_pwrup_delay(sc);
PWRCTL_UNLOCK(sc);
return (0);
default:
return (ENODEV);
}
}
static int
bhnd_pwrctl_get_clock_freq(device_t dev, bhnd_clock clock, u_int *freq)
{
struct bhnd_pwrctl_softc *sc = device_get_softc(dev);
switch (clock) {
case BHND_CLOCK_ALP:
BPMU_LOCK(sc);
*freq = bhnd_pwrctl_getclk_speed(sc);
BPMU_UNLOCK(sc);
return (0);
case BHND_CLOCK_HT:
case BHND_CLOCK_ILP:
case BHND_CLOCK_DYN:
default:
return (ENODEV);
}
}
/**
* Find the clock reservation associated with @p pinfo, if any.
* Find the clock reservation associated with @p owner, if any.
*
* @param sc Driver instance state.
* @param pinfo PMU info for device.
* @param owner The owning device.
*/
static struct bhnd_pwrctl_clkres *
bhnd_pwrctl_find_res(struct bhnd_pwrctl_softc *sc,
struct bhnd_core_pmu_info *pinfo)
bhnd_pwrctl_find_res(struct bhnd_pwrctl_softc *sc, device_t owner)
{
struct bhnd_pwrctl_clkres *clkres;
PWRCTL_LOCK_ASSERT(sc, MA_OWNED);
STAILQ_FOREACH(clkres, &sc->clkres_list, cr_link) {
if (clkres->owner == pinfo->pm_dev)
if (clkres->owner == owner)
return (clkres);
}
@ -357,9 +395,9 @@ bhnd_pwrctl_updateclk(struct bhnd_pwrctl_softc *sc, bhnd_pwrctl_wars wars)
return (bhnd_pwrctl_setclk(sc, clock));
}
/* BHND_PWRCTL_REQUEST_CLOCK() */
static int
bhnd_pwrctl_core_req_clock(device_t dev, struct bhnd_core_pmu_info *pinfo,
bhnd_clock clock)
bhnd_pwrctl_request_clock(device_t dev, device_t child, bhnd_clock clock)
{
struct bhnd_pwrctl_softc *sc;
struct bhnd_pwrctl_clkres *clkres;
@ -370,7 +408,7 @@ bhnd_pwrctl_core_req_clock(device_t dev, struct bhnd_core_pmu_info *pinfo,
PWRCTL_LOCK(sc);
clkres = bhnd_pwrctl_find_res(sc, pinfo);
clkres = bhnd_pwrctl_find_res(sc, child);
/* BHND_CLOCK_DYN discards the clock reservation entirely */
if (clock == BHND_CLOCK_DYN) {
@ -409,12 +447,12 @@ bhnd_pwrctl_core_req_clock(device_t dev, struct bhnd_core_pmu_info *pinfo,
if (clkres == NULL)
return (ENOMEM);
clkres->owner = pinfo->pm_dev;
clkres->owner = child;
clkres->clock = clock;
STAILQ_INSERT_TAIL(&sc->clkres_list, clkres, cr_link);
} else {
KASSERT(clkres->owner == pinfo->pm_dev, ("invalid owner"));
KASSERT(clkres->owner == child, ("invalid owner"));
clkres->clock = clock;
}
@ -430,68 +468,24 @@ bhnd_pwrctl_core_req_clock(device_t dev, struct bhnd_core_pmu_info *pinfo,
return (error);
}
static int
bhnd_pwrctl_core_req_ext_rsrc(device_t dev, struct bhnd_core_pmu_info *pinfo,
u_int rsrc)
{
/* HW does not support per-core external resources */
return (ENODEV);
}
static int
bhnd_pwrctl_core_release_ext_rsrc(device_t dev,
struct bhnd_core_pmu_info *pinfo, u_int rsrc)
{
/* HW does not support per-core external resources */
return (ENODEV);
}
static int
bhnd_pwrctl_core_en_clocks(device_t dev, struct bhnd_core_pmu_info *pinfo,
uint32_t clocks)
{
/* All supported clocks are already enabled by default (?) */
clocks &= ~(BHND_CLOCK_DYN |
BHND_CLOCK_ILP |
BHND_CLOCK_ALP |
BHND_CLOCK_HT);
if (clocks != 0) {
device_printf(dev, "%s requested unknown clocks: %#x\n",
device_get_nameunit(pinfo->pm_dev), clocks);
return (ENODEV);
}
return (0);
}
static int
bhnd_pwrctl_core_release(device_t dev, struct bhnd_core_pmu_info *pinfo)
{
/* Requesting BHND_CLOCK_DYN releases any outstanding clock
* reservations */
return (bhnd_pwrctl_core_req_clock(dev, pinfo, BHND_CLOCK_DYN));
}
static device_method_t bhnd_pwrctl_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, bhnd_pwrctl_probe),
DEVMETHOD(device_attach, bhnd_pwrctl_attach),
DEVMETHOD(device_detach, bhnd_pwrctl_detach),
DEVMETHOD(device_suspend, bhnd_pwrctl_suspend),
DEVMETHOD(device_resume, bhnd_pwrctl_resume),
DEVMETHOD(device_probe, bhnd_pwrctl_probe),
DEVMETHOD(device_attach, bhnd_pwrctl_attach),
DEVMETHOD(device_detach, bhnd_pwrctl_detach),
DEVMETHOD(device_suspend, bhnd_pwrctl_suspend),
DEVMETHOD(device_resume, bhnd_pwrctl_resume),
/* BHND PMU interface */
DEVMETHOD(bhnd_pmu_core_req_clock, bhnd_pwrctl_core_req_clock),
DEVMETHOD(bhnd_pmu_core_en_clocks, bhnd_pwrctl_core_en_clocks),
DEVMETHOD(bhnd_pmu_core_req_ext_rsrc, bhnd_pwrctl_core_req_ext_rsrc),
DEVMETHOD(bhnd_pmu_core_release_ext_rsrc, bhnd_pwrctl_core_release_ext_rsrc),
DEVMETHOD(bhnd_pmu_core_release, bhnd_pwrctl_core_release),
/* BHND PWRCTL interface */
DEVMETHOD(bhnd_pwrctl_request_clock, bhnd_pwrctl_request_clock),
DEVMETHOD(bhnd_pwrctl_get_clock_freq, bhnd_pwrctl_get_clock_freq),
DEVMETHOD(bhnd_pwrctl_get_clock_latency, bhnd_pwrctl_get_clock_latency),
DEVMETHOD_END
};
DEFINE_CLASS_0(bhnd_pmu, bhnd_pwrctl_driver, bhnd_pwrctl_methods,
DEFINE_CLASS_0(bhnd_pwrctl, bhnd_pwrctl_driver, bhnd_pwrctl_methods,
sizeof(struct bhnd_pwrctl_softc));
EARLY_DRIVER_MODULE(bhnd_pwrctl, bhnd_chipc, bhnd_pwrctl_driver,
bhnd_pmu_devclass, NULL, NULL, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);

View File

@ -0,0 +1,94 @@
/*-
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Landon Fuller under sponsorship from
* the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _BHND_PWRCTL_BHND_PWRCTL_H_
#define _BHND_PWRCTL_BHND_PWRCTL_H_
#include <sys/param.h>
#include <sys/bus.h>
#include "bhnd_pwrctl_if.h"
/**
* Request that @p clock (or a faster clock) be enabled on behalf of
* @p child.
*
* @param dev PWRCTL device.
* @param child The requesting bhnd(4) device.
* @param clock Clock requested.
*
* @retval 0 success
* @retval ENODEV If an unsupported clock was requested.
*/
static inline int
bhnd_pwrctl_request_clock(device_t dev, device_t child, bhnd_clock clock)
{
return (BHND_PWRCTL_REQUEST_CLOCK(dev, child, clock));
}
/**
* Return the transition latency required for @p clock in microseconds, if
* known.
*
* The BHND_CLOCK_HT latency value is suitable for use as the D11 core's
* 'fastpwrup_dly' value.
*
* @param dev PWRCTL device.
* @param clock The clock to be queried for transition latency.
* @param[out] latency On success, the transition latency of @p clock in
* microseconds.
*
* @retval 0 success
* @retval ENODEV If the transition latency for @p clock is not available.
*/
static inline int
bhnd_pwrctl_get_clock_latency(device_t dev, bhnd_clock clock, u_int *latency)
{
return (BHND_PWRCTL_GET_CLOCK_LATENCY(dev, clock, latency));
}
/**
* Return the frequency for @p clock in Hz, if known.
*
* @param dev PWRCTL device.
* @param clock The clock to be queried.
* @param[out] freq On success, the frequency of @p clock in Hz.
*
* @retval 0 success
* @retval ENODEV If the frequency for @p clock is not available.
*/
static inline int
bhnd_pwrctl_get_clock_freq(device_t dev, bhnd_clock clock, u_int *freq)
{
return (BHND_PWRCTL_GET_CLOCK_FREQ(dev, clock, freq));
}
#endif /* _BHND_PWRCTL_BHND_PWRCTL_H_ */

View File

@ -0,0 +1,129 @@
#-
# Copyright (c) 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:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# $FreeBSD$
#include <sys/types.h>
#include <sys/bus.h>
#include <dev/bhnd/bhnd.h>
INTERFACE bhnd_pwrctl_hostb;
#
# bhnd(4) PWRCTL host bridge interface.
#
# Provides a common interface to the clock hardware managed by a parent host
# bridge (e.g. bhndb_pci(4)).
#
# Early PWRCTL chipsets[1] expose clock management via their host bridge
# interface, requiring that a host bridge driver (e.g. bhndb(4)) work in
# tandem with the ChipCommon-attached PWRCTL driver.
#
# [1] Currently, this is known to include PCI (not PCIe) devices, with
# ChipCommon core revisions 0-9.
#
HEADER {
#include <dev/bhnd/bhnd.h>
};
CODE {
static bhnd_clksrc
bhnd_pwrctl_hostb_get_clksrc(device_t dev, device_t child,
bhnd_clock clock)
{
return (BHND_CLKSRC_UNKNOWN);
}
static int
bhnd_pwrctl_hostb_gate_clock(device_t dev, device_t child,
bhnd_clock clock)
{
return (ENODEV);
}
static int
bhnd_pwrctl_hostb_ungate_clock(device_t dev, device_t child,
bhnd_clock clock)
{
return (ENODEV);
}
};
/**
* If supported by the chipset, return the clock source for the given clock.
*
* @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 get_clksrc {
device_t dev;
device_t child;
bhnd_clock clock;
} DEFAULT bhnd_pwrctl_hostb_get_clksrc;
/**
* If supported by the chipset, gate the clock source for @p clock.
*
* @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 gate_clock {
device_t dev;
device_t child;
bhnd_clock clock;
} DEFAULT bhnd_pwrctl_hostb_gate_clock;
/**
* If supported by the chipset, ungate the clock source for @p clock.
*
* @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 ungate_clock {
device_t dev;
device_t child;
bhnd_clock clock;
} DEFAULT bhnd_pwrctl_hostb_ungate_clock;

View File

@ -0,0 +1,98 @@
#-
# Copyright (c) 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:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# $FreeBSD$
#include <sys/types.h>
#include <sys/bus.h>
#include <dev/bhnd/bhnd.h>
INTERFACE bhnd_pwrctl;
#
# bhnd(4) PWRCTL interface.
#
HEADER {
#include <dev/bhnd/bhnd.h>
};
/**
* Request that @p clock (or a faster clock) be enabled on behalf of
* @p child.
*
* @param dev PWRCTL device.
* @param child The requesting bhnd(4) device.
* @param clock Clock requested.
*
* @retval 0 success
* @retval ENODEV If an unsupported clock was requested.
*/
METHOD int request_clock {
device_t dev;
device_t child;
bhnd_clock clock;
};
/**
* Return the transition latency required for @p clock in microseconds, if
* known.
*
* The BHND_CLOCK_HT latency value is suitable for use as the D11 core's
* 'fastpwrup_dly' value.
*
* @param dev PWRCTL device.
* @param clock The clock to be queried for transition latency.
* @param[out] latency On success, the transition latency of @p clock in
* microseconds.
*
* @retval 0 success
* @retval ENODEV If the transition latency for @p clock is not available.
*/
METHOD int get_clock_latency {
device_t dev;
bhnd_clock clock;
u_int *latency;
};
/**
* Return the frequency for @p clock in Hz, if known.
*
* @param dev PWRCTL device.
* @param clock The clock to be queried.
* @param[out] freq On success, the frequency of @p clock in Hz.
*
* @retval 0 success
* @retval ENODEV If the frequency for @p clock is not available.
*/
METHOD int get_clock_freq {
device_t dev;
bhnd_clock clock;
u_int *freq;
};

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -32,12 +36,77 @@
#ifndef _BHND_PWRCTL_BHND_PWRCTL_PRIVATE_H_
#define _BHND_PWRCTL_BHND_PWRCTL_PRIVATE_H_
#include "bhnd_pwrctl_hostb_if.h"
#include "bhnd_pwrctlvar.h"
int bhnd_pwrctl_init(struct bhnd_pwrctl_softc *sc);
int bhnd_pwrctl_setclk(struct bhnd_pwrctl_softc *sc,
bhnd_clock clock);
uint32_t bhnd_pwrctl_getclk_speed(struct bhnd_pwrctl_softc *sc);
uint16_t bhnd_pwrctl_fast_pwrup_delay(struct bhnd_pwrctl_softc *sc);
u_int bhnd_pwrctl_fast_pwrup_delay(struct bhnd_pwrctl_softc *sc);
/**
* 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_hostb_get_clksrc(device_t dev, bhnd_clock clock)
{
return (BHND_PWRCTL_HOSTB_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_hostb_gate_clock(device_t dev, bhnd_clock clock)
{
return (BHND_PWRCTL_HOSTB_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_hostb_ungate_clock(device_t dev, bhnd_clock clock)
{
return (BHND_PWRCTL_HOSTB_UNGATE_CLOCK(device_get_parent(dev), dev,
clock));
}
#endif /* _BHND_PWRCTL_BHND_PWRCTL_PRIVATE_H_ */

View File

@ -333,7 +333,8 @@ bhnd_pwrctl_slowclk_src(struct bhnd_pwrctl_softc *sc)
/* Fetch clock source */
if (PWRCTL_QUIRK(sc, PCICLK_CTL)) {
return (bhnd_pwrctl_get_clksrc(sc->chipc_dev, BHND_CLOCK_ILP));
return (bhnd_pwrctl_hostb_get_clksrc(sc->chipc_dev,
BHND_CLOCK_ILP));
} else if (PWRCTL_QUIRK(sc, SLOWCLK_CTL)) {
clkreg = bhnd_bus_read_4(sc->res, CHIPC_PLL_SLOWCLK_CTL);
clksrc = clkreg & CHIPC_SCC_SS_MASK;
@ -460,11 +461,11 @@ bhnd_pwrctl_init(struct bhnd_pwrctl_softc *sc)
/* return the value suitable for writing to the dot11 core
* FAST_PWRUP_DELAY register */
uint16_t
u_int
bhnd_pwrctl_fast_pwrup_delay(struct bhnd_pwrctl_softc *sc)
{
uint32_t pll_on_delay, slowminfreq;
uint16_t fpdelay;
u_int pll_on_delay, slowminfreq;
u_int fpdelay;
fpdelay = 0;
@ -516,7 +517,8 @@ bhnd_pwrctl_setclk(struct bhnd_pwrctl_softc *sc, bhnd_clock clock)
scc |= CHIPC_SCC_IP;
/* force xtal back on before clearing SCC_DYN_XTAL.. */
bhnd_pwrctl_ungate_clock(sc->chipc_dev, BHND_CLOCK_HT);
bhnd_pwrctl_hostb_ungate_clock(sc->chipc_dev,
BHND_CLOCK_HT);
} else if (PWRCTL_QUIRK(sc, INSTACLK_CTL)) {
scc |= CHIPC_SYCC_HR;
} else {
@ -543,7 +545,7 @@ bhnd_pwrctl_setclk(struct bhnd_pwrctl_softc *sc, bhnd_clock clock)
/* for dynamic control, we have to release our xtal_pu
* "force on" */
if (scc & CHIPC_SCC_XC) {
bhnd_pwrctl_gate_clock(sc->chipc_dev,
bhnd_pwrctl_hostb_gate_clock(sc->chipc_dev,
BHND_CLOCK_HT);
}
} else if (PWRCTL_QUIRK(sc, INSTACLK_CTL)) {

View File

@ -47,7 +47,8 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <machine/resource.h>
#include <dev/bhnd/bhnd.h>
#include <dev/bhnd/bhndreg.h>
#include <dev/bhnd/bhndvar.h>
#include <dev/bhnd/cores/chipc/chipc.h>
#include "bhnd_nvram_map.h"
@ -84,20 +85,6 @@ static const struct bhnd_pmu_io bhnd_pmu_res_io = {
.rd_chipst = bhnd_pmu_read_chipst
};
#define BPMU_ASSERT_CLKCTL_AVAIL(_pinfo) \
KASSERT(!bhnd_is_hw_suspended((_pinfo)->pm_dev), \
("reading clkctl on suspended core will trigger system livelock"))
#define BPMU_CLKCTL_READ_4(_pinfo) \
bhnd_bus_read_4((_pinfo)->pm_res, (_pinfo)->pm_regs)
#define BPMU_CLKCTL_WRITE_4(_pinfo, _val) \
bhnd_bus_write_4((_pinfo)->pm_res, (_pinfo)->pm_regs, (_val))
#define BPMU_CLKCTL_SET_4(_pinfo, _val, _mask) \
BPMU_CLKCTL_WRITE_4((_pinfo), \
((_val) & (_mask)) | (BPMU_CLKCTL_READ_4(_pinfo) & ~(_mask)))
/**
* Default bhnd_pmu driver implementation of DEVICE_PROBE().
*/
@ -126,7 +113,6 @@ bhnd_pmu_attach(device_t dev, struct bhnd_resource *res)
sc = device_get_softc(dev);
sc->dev = dev;
sc->quirks = 0;
sc->res = res;
/* Fetch capability flags */
@ -147,6 +133,17 @@ bhnd_pmu_attach(device_t dev, struct bhnd_resource *res)
return (ENXIO);
}
/* Allocate our own core clkctl state directly; we use this to wait on
* PMU state transitions, avoiding a cyclic dependency between bhnd(4)'s
* clkctl handling and registration of this device as a PMU */
sc->clkctl = bhnd_alloc_core_clkctl(core, dev, sc->res, BHND_CLK_CTL_ST,
BHND_PMU_MAX_TRANSITION_DLY);
if (sc->clkctl == NULL) {
device_printf(sc->dev, "failed to allocate clkctl for %s\n",
device_get_nameunit(core));
return (ENOMEM);
}
/* Fetch chip and board info */
sc->cid = *bhnd_get_chipid(core);
@ -157,7 +154,7 @@ bhnd_pmu_attach(device_t dev, struct bhnd_resource *res)
}
/* Locate ChipCommon device */
sc->chipc_dev = bhnd_bus_find_child(bus, BHND_DEVCLASS_CC, 0);
sc->chipc_dev = bhnd_retain_provider(dev, BHND_SERVICE_CHIPC);
if (sc->chipc_dev == NULL) {
device_printf(sc->dev, "chipcommon device not found\n");
return (ENXIO);
@ -173,17 +170,6 @@ bhnd_pmu_attach(device_t dev, struct bhnd_resource *res)
BPMU_LOCK_INIT(sc);
/* Set quirk flags */
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4328:
case BHND_CHIPID_BCM5354:
/* HTAVAIL/ALPAVAIL are bitswapped in CLKCTL */
sc->quirks |= BPMU_QUIRK_CLKCTL_CCS0;
break;
default:
break;
}
/* Initialize PMU */
if ((error = bhnd_pmu_init(sc))) {
device_printf(sc->dev, "PMU init failed: %d\n", error);
@ -218,6 +204,9 @@ bhnd_pmu_attach(device_t dev, struct bhnd_resource *res)
failed:
BPMU_LOCK_DESTROY(sc);
bhnd_pmu_query_fini(&sc->query);
bhnd_free_core_clkctl(sc->clkctl);
bhnd_release_provider(sc->dev, sc->chipc_dev, BHND_SERVICE_CHIPC);
return (error);
}
@ -237,7 +226,9 @@ bhnd_pmu_detach(device_t dev)
BPMU_LOCK_DESTROY(sc);
bhnd_pmu_query_fini(&sc->query);
bhnd_free_core_clkctl(sc->clkctl);
bhnd_release_provider(sc->dev, sc->chipc_dev, BHND_SERVICE_CHIPC);
return (0);
}
@ -315,189 +306,270 @@ bhnd_pmu_sysctl_mem_freq(SYSCTL_HANDLER_ARGS)
return (sysctl_handle_32(oidp, NULL, freq, req));
}
static int
bhnd_pmu_core_req_clock(device_t dev, struct bhnd_core_pmu_info *pinfo,
bhnd_clock clock)
/**
* Default bhnd_pmu driver implementation of BHND_PMU_READ_CHIPCTRL().
*/
static uint32_t
bhnd_pmu_read_chipctrl_method(device_t dev, uint32_t reg)
{
struct bhnd_pmu_softc *sc;
uint32_t avail;
uint32_t req;
BPMU_ASSERT_CLKCTL_AVAIL(pinfo);
struct bhnd_pmu_softc *sc;
uint32_t rval;
sc = device_get_softc(dev);
avail = 0x0;
req = 0x0;
BPMU_LOCK(sc);
rval = BHND_PMU_CCTRL_READ(sc, reg);
BPMU_UNLOCK(sc);
return (rval);
}
/**
* Default bhnd_pmu driver implementation of BHND_PMU_WRITE_CHIPCTRL().
*/
static void
bhnd_pmu_write_chipctrl_method(device_t dev, uint32_t reg, uint32_t value,
uint32_t mask)
{
struct bhnd_pmu_softc *sc = device_get_softc(dev);
BPMU_LOCK(sc);
BHND_PMU_CCTRL_WRITE(sc, reg, value, mask);
BPMU_UNLOCK(sc);
}
/**
* Default bhnd_pmu driver implementation of BHND_PMU_READ_REGCTRL().
*/
static uint32_t
bhnd_pmu_read_regctrl_method(device_t dev, uint32_t reg)
{
struct bhnd_pmu_softc *sc;
uint32_t rval;
sc = device_get_softc(dev);
BPMU_LOCK(sc);
rval = BHND_PMU_REGCTRL_READ(sc, reg);
BPMU_UNLOCK(sc);
return (rval);
}
/**
* Default bhnd_pmu driver implementation of BHND_PMU_WRITE_REGCTRL().
*/
static void
bhnd_pmu_write_regctrl_method(device_t dev, uint32_t reg, uint32_t value,
uint32_t mask)
{
struct bhnd_pmu_softc *sc = device_get_softc(dev);
BPMU_LOCK(sc);
BHND_PMU_REGCTRL_WRITE(sc, reg, value, mask);
BPMU_UNLOCK(sc);
}
/**
* Default bhnd_pmu driver implementation of BHND_PMU_READ_PLLCTRL().
*/
static uint32_t
bhnd_pmu_read_pllctrl_method(device_t dev, uint32_t reg)
{
struct bhnd_pmu_softc *sc;
uint32_t rval;
sc = device_get_softc(dev);
BPMU_LOCK(sc);
rval = BHND_PMU_PLL_READ(sc, reg);
BPMU_UNLOCK(sc);
return (rval);
}
/**
* Default bhnd_pmu driver implementation of BHND_PMU_WRITE_PLLCTRL().
*/
static void
bhnd_pmu_write_pllctrl_method(device_t dev, uint32_t reg, uint32_t value,
uint32_t mask)
{
struct bhnd_pmu_softc *sc = device_get_softc(dev);
BPMU_LOCK(sc);
BHND_PMU_PLL_WRITE(sc, reg, value, mask);
BPMU_UNLOCK(sc);
}
/**
* Default bhnd_pmu driver implementation of BHND_PMU_SET_VOLTAGE_RAW().
*/
static int
bhnd_pmu_set_voltage_raw_method(device_t dev, bhnd_pmu_regulator regulator,
uint32_t value)
{
struct bhnd_pmu_softc *sc;
int error;
sc = device_get_softc(dev);
switch (regulator) {
case BHND_REGULATOR_PAREF_LDO:
if (value > UINT8_MAX)
return (EINVAL);
BPMU_LOCK(sc);
error = bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_PAREF,
value);
BPMU_UNLOCK(sc);
return (error);
default:
return (ENODEV);
}
}
/**
* Default bhnd_pmu driver implementation of BHND_PMU_ENABLE_REGULATOR().
*/
static int
bhnd_pmu_enable_regulator_method(device_t dev, bhnd_pmu_regulator regulator)
{
struct bhnd_pmu_softc *sc;
int error;
sc = device_get_softc(dev);
switch (regulator) {
case BHND_REGULATOR_PAREF_LDO:
BPMU_LOCK(sc);
error = bhnd_pmu_paref_ldo_enable(sc, true);
BPMU_UNLOCK(sc);
return (error);
default:
return (ENODEV);
}
}
/**
* Default bhnd_pmu driver implementation of BHND_PMU_DISABLE_REGULATOR().
*/
static int
bhnd_pmu_disable_regulator_method(device_t dev, bhnd_pmu_regulator regulator)
{
struct bhnd_pmu_softc *sc;
int error;
sc = device_get_softc(dev);
switch (regulator) {
case BHND_REGULATOR_PAREF_LDO:
BPMU_LOCK(sc);
error = bhnd_pmu_paref_ldo_enable(sc, false);
BPMU_UNLOCK(sc);
return (error);
default:
return (ENODEV);
}
}
/**
* Default bhnd_pmu driver implementation of BHND_PMU_GET_CLOCK_LATENCY().
*/
static int
bhnd_pmu_get_clock_latency_method(device_t dev, bhnd_clock clock,
u_int *latency)
{
struct bhnd_pmu_softc *sc;
u_int pwrup_delay;
int error;
sc = device_get_softc(dev);
switch (clock) {
case BHND_CLOCK_DYN:
break;
case BHND_CLOCK_ILP:
req |= BHND_CCS_FORCEILP;
break;
case BHND_CLOCK_ALP:
req |= BHND_CCS_FORCEALP;
avail |= BHND_CCS_ALPAVAIL;
break;
case BHND_CLOCK_HT:
req |= BHND_CCS_FORCEHT;
avail |= BHND_CCS_HTAVAIL;
break;
default:
device_printf(dev, "%s requested unknown clock: %#x\n",
device_get_nameunit(pinfo->pm_dev), clock);
return (ENODEV);
}
BPMU_LOCK(sc);
error = bhnd_pmu_fast_pwrup_delay(sc, &pwrup_delay);
BPMU_UNLOCK(sc);
BPMU_LOCK(sc);
if (error)
return (error);
/* Issue request */
BPMU_CLKCTL_SET_4(pinfo, req, BHND_CCS_FORCE_MASK);
/* Wait for clock availability */
bhnd_pmu_wait_clkst(sc, pinfo->pm_dev, pinfo->pm_res, pinfo->pm_regs,
avail, avail);
BPMU_UNLOCK(sc);
return (0);
}
static int
bhnd_pmu_core_en_clocks(device_t dev, struct bhnd_core_pmu_info *pinfo,
uint32_t clocks)
{
struct bhnd_pmu_softc *sc;
uint32_t avail;
uint32_t req;
BPMU_ASSERT_CLKCTL_AVAIL(pinfo);
sc = device_get_softc(dev);
avail = 0x0;
req = 0x0;
/* Build clock request flags */
if (clocks & BHND_CLOCK_DYN) /* nothing to enable */
clocks &= ~BHND_CLOCK_DYN;
if (clocks & BHND_CLOCK_ILP) /* nothing to enable */
clocks &= ~BHND_CLOCK_ILP;
if (clocks & BHND_CLOCK_ALP) {
req |= BHND_CCS_ALPAREQ;
avail |= BHND_CCS_ALPAVAIL;
clocks &= ~BHND_CLOCK_ALP;
}
if (clocks & BHND_CLOCK_HT) {
req |= BHND_CCS_HTAREQ;
avail |= BHND_CCS_HTAVAIL;
clocks &= ~BHND_CLOCK_HT;
}
/* Check for unknown clock values */
if (clocks != 0x0) {
device_printf(dev, "%s requested unknown clocks: %#x\n",
device_get_nameunit(pinfo->pm_dev), clocks);
return (ENODEV);
}
BPMU_LOCK(sc);
/* Issue request */
BPMU_CLKCTL_SET_4(pinfo, req, BHND_CCS_AREQ_MASK);
/* Wait for clock availability */
bhnd_pmu_wait_clkst(sc, pinfo->pm_dev, pinfo->pm_res, pinfo->pm_regs,
avail, avail);
BPMU_UNLOCK(sc);
return (0);
}
static int
bhnd_pmu_core_req_ext_rsrc(device_t dev, struct bhnd_core_pmu_info *pinfo,
u_int rsrc)
{
struct bhnd_pmu_softc *sc;
uint32_t req;
uint32_t avail;
BPMU_ASSERT_CLKCTL_AVAIL(pinfo);
sc = device_get_softc(dev);
if (rsrc > BHND_CCS_ERSRC_MAX)
return (EINVAL);
req = BHND_PMU_SET_BITS((1<<rsrc), BHND_CCS_ERSRC_REQ);
avail = BHND_PMU_SET_BITS((1<<rsrc), BHND_CCS_ERSRC_STS);
BPMU_LOCK(sc);
/* Write request */
BPMU_CLKCTL_SET_4(pinfo, req, req);
/* Wait for resource availability */
bhnd_pmu_wait_clkst(sc, pinfo->pm_dev, pinfo->pm_res, pinfo->pm_regs,
avail, avail);
BPMU_UNLOCK(sc);
return (0);
}
static int
bhnd_pmu_core_release_ext_rsrc(device_t dev, struct bhnd_core_pmu_info *pinfo,
u_int rsrc)
{
struct bhnd_pmu_softc *sc;
uint32_t mask;
BPMU_ASSERT_CLKCTL_AVAIL(pinfo);
sc = device_get_softc(dev);
if (rsrc > BHND_CCS_ERSRC_MAX)
return (EINVAL);
mask = BHND_PMU_SET_BITS((1<<rsrc), BHND_CCS_ERSRC_REQ);
/* Clear request */
BPMU_LOCK(sc);
BPMU_CLKCTL_SET_4(pinfo, 0x0, mask);
BPMU_UNLOCK(sc);
return (0);
}
static int
bhnd_pmu_core_release(device_t dev, struct bhnd_core_pmu_info *pinfo)
{
struct bhnd_pmu_softc *sc;
sc = device_get_softc(dev);
/* On PMU-equipped hardware, clkctl is cleared on RESET (and
* attempting to access it will trigger a system livelock). */
if (bhnd_is_hw_suspended(pinfo->pm_dev))
*latency = pwrup_delay;
return (0);
BPMU_LOCK(sc);
default:
return (ENODEV);
}
}
/* Clear all FORCE, AREQ, and ERSRC flags */
BPMU_CLKCTL_SET_4(pinfo, 0x0,
BHND_CCS_FORCE_MASK | BHND_CCS_AREQ_MASK | BHND_CCS_ERSRC_REQ_MASK);
/**
* Default bhnd_pmu driver implementation of BHND_PMU_GET_CLOCK_FREQ().
*/
static int
bhnd_pmu_get_clock_freq_method(device_t dev, bhnd_clock clock, uint32_t *freq)
{
struct bhnd_pmu_softc *sc = device_get_softc(dev);
BPMU_LOCK(sc);
switch (clock) {
case BHND_CLOCK_HT:
*freq = bhnd_pmu_si_clock(&sc->query);
break;
case BHND_CLOCK_ALP:
*freq = bhnd_pmu_alp_clock(&sc->query);
break;
case BHND_CLOCK_ILP:
*freq = bhnd_pmu_ilp_clock(&sc->query);
break;
case BHND_CLOCK_DYN:
default:
BPMU_UNLOCK(sc);
return (ENODEV);
}
BPMU_UNLOCK(sc);
return (0);
}
/**
* Default bhnd_pmu driver implementation of BHND_PMU_REQUEST_SPURAVOID().
*/
static int
bhnd_pmu_request_spuravoid_method(device_t dev, bhnd_pmu_spuravoid spuravoid)
{
struct bhnd_pmu_softc *sc;
int error;
sc = device_get_softc(dev);
BPMU_LOCK(sc);
error = bhnd_pmu_set_spuravoid(sc, spuravoid);
BPMU_UNLOCK(sc);
return (error);
}
/**
* Default bhnd_pmu driver implementation of BHND_PMU_GET_TRANSITION_LATENCY().
*/
static u_int
bhnd_pmu_get_max_transition_latency_method(device_t dev)
{
return (BHND_PMU_MAX_TRANSITION_DLY);
}
/* bhnd_pmu_query read_4 callback */
static uint32_t
bhnd_pmu_read_4(bus_size_t reg, void *ctx)
{
@ -505,6 +577,7 @@ bhnd_pmu_read_4(bus_size_t reg, void *ctx)
return (bhnd_bus_read_4(sc->res, reg));
}
/* bhnd_pmu_query write_4 callback */
static void
bhnd_pmu_write_4(bus_size_t reg, uint32_t val, void *ctx)
{
@ -512,6 +585,7 @@ bhnd_pmu_write_4(bus_size_t reg, uint32_t val, void *ctx)
return (bhnd_bus_write_4(sc->res, reg, val));
}
/* bhnd_pmu_query read_chipst callback */
static uint32_t
bhnd_pmu_read_chipst(void *ctx)
{
@ -521,18 +595,28 @@ bhnd_pmu_read_chipst(void *ctx)
static device_method_t bhnd_pmu_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, bhnd_pmu_probe),
DEVMETHOD(device_detach, bhnd_pmu_detach),
DEVMETHOD(device_suspend, bhnd_pmu_suspend),
DEVMETHOD(device_resume, bhnd_pmu_resume),
DEVMETHOD(device_probe, bhnd_pmu_probe),
DEVMETHOD(device_detach, bhnd_pmu_detach),
DEVMETHOD(device_suspend, bhnd_pmu_suspend),
DEVMETHOD(device_resume, bhnd_pmu_resume),
/* BHND PMU interface */
DEVMETHOD(bhnd_pmu_core_req_clock, bhnd_pmu_core_req_clock),
DEVMETHOD(bhnd_pmu_core_en_clocks, bhnd_pmu_core_en_clocks),
DEVMETHOD(bhnd_pmu_core_req_ext_rsrc, bhnd_pmu_core_req_ext_rsrc),
DEVMETHOD(bhnd_pmu_core_release_ext_rsrc, bhnd_pmu_core_release_ext_rsrc),
DEVMETHOD(bhnd_pmu_core_release, bhnd_pmu_core_release),
DEVMETHOD(bhnd_pmu_read_chipctrl, bhnd_pmu_read_chipctrl_method),
DEVMETHOD(bhnd_pmu_write_chipctrl, bhnd_pmu_write_chipctrl_method),
DEVMETHOD(bhnd_pmu_read_regctrl, bhnd_pmu_read_regctrl_method),
DEVMETHOD(bhnd_pmu_write_regctrl, bhnd_pmu_write_regctrl_method),
DEVMETHOD(bhnd_pmu_read_pllctrl, bhnd_pmu_read_pllctrl_method),
DEVMETHOD(bhnd_pmu_write_pllctrl, bhnd_pmu_write_pllctrl_method),
DEVMETHOD(bhnd_pmu_set_voltage_raw, bhnd_pmu_set_voltage_raw_method),
DEVMETHOD(bhnd_pmu_enable_regulator, bhnd_pmu_enable_regulator_method),
DEVMETHOD(bhnd_pmu_disable_regulator, bhnd_pmu_disable_regulator_method),
DEVMETHOD(bhnd_pmu_get_clock_latency, bhnd_pmu_get_clock_latency_method),
DEVMETHOD(bhnd_pmu_get_clock_freq, bhnd_pmu_get_clock_freq_method),
DEVMETHOD(bhnd_pmu_get_max_transition_latency, bhnd_pmu_get_max_transition_latency_method),
DEVMETHOD(bhnd_pmu_request_spuravoid, bhnd_pmu_request_spuravoid_method),
DEVMETHOD_END
};

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -37,18 +41,227 @@
#include <dev/bhnd/bhnd.h>
#include "bhnd_pmu_if.h"
#include "bhnd_pmu_types.h"
/**
* Per-core PMU register information.
* Return the current value of a PMU chipctrl register.
*
* @param dev A bhnd(4) PMU device.
* @param reg The PMU chipctrl register to be read.
*
* Drivers should only use function for functionality that is not
* available via another bhnd_chipc() function.
*
* @returns The chipctrl register value, or 0 if undefined by this hardware.
*/
struct bhnd_core_pmu_info {
device_t pm_dev; /**< core device */
device_t pm_pmu; /**< PMU device */
struct bhnd_resource *pm_res; /**< Resource containing PMU
register block for this
device (if any). */
bus_size_t pm_regs; /**< Offset to PMU register
* block in @p pm_res */
};
static inline uint32_t
bhnd_pmu_read_chipctrl(device_t dev, uint32_t reg)
{
return (BHND_PMU_READ_CHIPCTRL(dev, reg));
}
/**
* Write @p value with @p mask to a PMU chipctrl register.
*
* @param dev A bhnd(4) PMU device.
* @param reg The PMU chipctrl register to be written.
* @param value The value to write.
* @param mask The mask of bits to be written from @p value.
*
* Drivers should only use function for functionality that is not
* available via another bhnd_pmu() function.
*/
static inline void
bhnd_pmu_write_chipctrl(device_t dev, uint32_t reg, uint32_t value,
uint32_t mask)
{
return (BHND_PMU_WRITE_CHIPCTRL(dev, reg, value, mask));
}
/**
* Return the current value of a PMU regulator control register.
*
* @param dev A bhnd(4) PMU device.
* @param reg The PMU regctrl register to be read.
*
* Drivers should only use function for functionality that is not
* available via another bhnd_chipc() function.
*
* @returns The regctrl register value, or 0 if undefined by this hardware.
*/
static inline uint32_t
bhnd_pmu_read_regctrl(device_t dev, uint32_t reg)
{
return (BHND_PMU_READ_REGCTRL(dev, reg));
}
/**
* Write @p value with @p mask to a PMU regulator control register.
*
* @param dev A bhnd(4) PMU device.
* @param reg The PMU regctrl register to be written.
* @param value The value to write.
* @param mask The mask of bits to be written from @p value.
*
* Drivers should only use function for functionality that is not
* available via another bhnd_pmu() function.
*/
static inline void
bhnd_pmu_write_regctrl(device_t dev, uint32_t reg, uint32_t value,
uint32_t mask)
{
return (BHND_PMU_WRITE_REGCTRL(dev, reg, value, mask));
}
/**
* Return the current value of a PMU PLL control register.
*
* @param dev A bhnd(4) PMU device.
* @param reg The PMU pllctrl register to be read.
*
* Drivers should only use function for functionality that is not
* available via another bhnd_chipc() function.
*
* @returns The pllctrl register value, or 0 if undefined by this hardware.
*/
static inline uint32_t
bhnd_pmu_read_pllctrl(device_t dev, uint32_t reg)
{
return (BHND_PMU_READ_PLLCTRL(dev, reg));
}
/**
* Write @p value with @p mask to a PMU PLL control register.
*
* @param dev A bhnd(4) PMU device.
* @param reg The PMU pllctrl register to be written.
* @param value The value to write.
* @param mask The mask of bits to be written from @p value.
*
* Drivers should only use function for functionality that is not
* available via another bhnd_pmu() function.
*/
static inline void
bhnd_pmu_write_pllctrl(device_t dev, uint32_t reg, uint32_t value,
uint32_t mask)
{
return (BHND_PMU_WRITE_PLLCTRL(dev, reg, value, mask));
}
/**
* Set a hardware-specific output voltage register value for @p regulator.
*
* @param dev PMU device.
* @param regulator Regulator to be configured.
* @param value The raw voltage register value.
*
* @retval 0 success
* @retval ENODEV If @p regulator is not supported by this driver.
*/
static inline int
bhnd_pmu_set_voltage_raw(device_t dev, bhnd_pmu_regulator regulator,
uint32_t value)
{
return (BHND_PMU_SET_VOLTAGE_RAW(dev, regulator, value));
}
/**
* Enable the given @p regulator.
*
* @param dev PMU device.
* @param regulator Regulator to be enabled.
*
* @retval 0 success
* @retval ENODEV If @p regulator is not supported by this driver.
*/
static inline int
bhnd_pmu_enable_regulator(device_t dev, bhnd_pmu_regulator regulator)
{
return (BHND_PMU_ENABLE_REGULATOR(dev, regulator));
}
/**
* Disable the given @p regulator.
*
* @param dev PMU device.
* @param regulator Regulator to be disabled.
*
* @retval 0 success
* @retval ENODEV If @p regulator is not supported by this driver.
*/
static inline int
bhnd_pmu_disable_regulator(device_t dev, bhnd_pmu_regulator regulator)
{
return (BHND_PMU_DISABLE_REGULATOR(dev, regulator));
}
/**
* Return the transition latency required for @p clock in microseconds, if
* known.
*
* The BHND_CLOCK_HT latency value is suitable for use as the D11 core's
* 'fastpwrup_dly' value.
*
* @param dev PMU device.
* @param clock The clock to be queried for transition latency.
* @param[out] latency On success, the transition latency of @p clock in
* microseconds.
*
* @retval 0 success
* @retval ENODEV If the transition latency for @p clock is not available.
*/
static inline int
bhnd_pmu_get_clock_latency(device_t dev, bhnd_clock clock, u_int *latency)
{
return (BHND_PMU_GET_CLOCK_LATENCY(dev, clock, latency));
}
/**
* Return the frequency for @p clock in Hz, if known.
*
* @param dev PMU device.
* @param clock The clock to be queried.
* @param[out] freq On success, the frequency of @p clock in Hz.
*
* @retval 0 success
* @retval ENODEV If the frequency for @p clock is not available.
*/
static inline int
bhnd_pmu_get_clock_freq(device_t dev, bhnd_clock clock, u_int *freq)
{
return (BHND_PMU_GET_CLOCK_FREQ(dev, clock, freq));
}
/**
* Request that the PMU configure itself for a given hardware-specific
* spuravoid mode.
*
* @param dev PMU device.
* @param spuravoid The requested mode.
*
* @retval 0 success
* @retval ENODEV If @p regulator is not supported by this driver.
*/
static inline int
bhnd_pmu_request_spuravoid(device_t dev, bhnd_pmu_spuravoid spuravoid)
{
return (BHND_PMU_REQUEST_SPURAVOID(dev, spuravoid));
}
/**
* Return the PMU's maximum state transition latency in microseconds.
*
* This upper bound may be used to busy-wait on PMU clock and resource state
* transitions.
*
* @param dev PMU device.
*/
static inline u_int
bhnd_pmu_get_max_transition_latency(device_t dev)
{
return (BHND_PMU_GET_MAX_TRANSITION_LATENCY(dev));
}
#endif /* _BHND_CORES_PMU_BHND_PMU_H_ */

View File

@ -94,6 +94,14 @@ bhnd_pmu_core_attach(device_t dev)
return (ENXIO);
}
/* Allocate our per-core PMU state */
if ((error = bhnd_alloc_pmu(dev))) {
device_printf(sc->dev, "failed to allocate PMU state: %d\n",
error);
return (error);
}
/* Delegate to common driver implementation */
if ((error = bhnd_pmu_attach(dev, res))) {
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, res);

View File

@ -1,7 +1,11 @@
#-
# Copyright (c) 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:
@ -34,97 +38,307 @@ INTERFACE bhnd_pmu;
#
# bhnd(4) PMU interface.
#
# Provides a common PMU and clock control interface.
# Provides an interface to the PMU hardware found on modern bhnd(4) chipsets.
#
HEADER {
#include <dev/bhnd/cores/pmu/bhnd_pmu_types.h>
struct bhnd_core_pmu_info;
}
/**
* Enabling routing of @p clock (or faster) to a requesting core.
*
* @param dev PMU device.
* @param pinfo PMU info for requesting core.
* @param clock Clock requested.
*
* @retval 0 success
* @retval ENODEV If an unsupported clock was requested.
*/
METHOD int core_req_clock {
device_t dev;
struct bhnd_core_pmu_info *pinfo;
bhnd_clock clock;
};
CODE {
static uint32_t
bhnd_pmu_null_read_chipctrl(device_t dev, uint32_t reg)
{
panic("bhnd_pmu_read_chipctrl unimplemented");
}
/**
* Request that @p clocks be powered on behalf of a requesting core.
*
* This will power any clock sources (XTAL, PLL, etc,) required by
* @p clocks and wait until they are ready, discarding any previous
* requests from the @p pinfo device.
*
* Requests from multiple devices are aggregated by the PMU.
*
* @param dev PMU device.
* @param pinfo PMU info for requesting core.
* @param clocks Clocks requested.
*
* @retval 0 success
* @retval ENODEV If an unsupported clock was requested.
*/
METHOD int core_en_clocks {
device_t dev;
struct bhnd_core_pmu_info *pinfo;
uint32_t clocks;
};
static void
bhnd_pmu_null_write_chipctrl(device_t dev, uint32_t reg, uint32_t value,
uint32_t mask)
{
panic("bhnd_pmu_write_chipctrl unimplemented");
}
static uint32_t
bhnd_pmu_null_read_regctrl(device_t dev, uint32_t reg)
{
panic("bhnd_pmu_read_regctrl unimplemented");
}
static void
bhnd_pmu_null_write_regctrl(device_t dev, uint32_t reg, uint32_t value,
uint32_t mask)
{
panic("bhnd_pmu_write_regctrl unimplemented");
}
static uint32_t
bhnd_pmu_null_read_pllctrl(device_t dev, uint32_t reg)
{
panic("bhnd_pmu_read_pllctrl unimplemented");
}
static void
bhnd_pmu_null_write_pllctrl(device_t dev, uint32_t reg, uint32_t value,
uint32_t mask)
{
panic("bhnd_pmu_write_pllctrl unimplemented");
}
static int
bhnd_pmu_null_request_spuravoid(device_t dev,
bhnd_pmu_spuravoid spuravoid)
{
panic("bhnd_pmu_request_spuravoid unimplemented");
}
static int
bhnd_pmu_null_set_voltage_raw(device_t dev,
bhnd_pmu_regulator regulator, uint32_t value)
{
panic("bhnd_pmu_set_voltage_raw unimplemented");
}
static int
bhnd_pmu_null_enable_regulator(device_t dev,
bhnd_pmu_regulator regulator)
{
panic("bhnd_pmu_enable_regulator unimplemented");
}
static int
bhnd_pmu_null_disable_regulator(device_t dev,
bhnd_pmu_regulator regulator)
{
panic("bhnd_pmu_disable_regulator unimplemented");
}
static int
bhnd_pmu_null_get_clock_latency(device_t dev, bhnd_clock clock,
u_int *latency)
{
panic("bhnd_pmu_get_clock_latency unimplemented");
}
static int
bhnd_pmu_null_get_clock_freq(device_t dev, bhnd_clock clock,
u_int *freq)
{
panic("bhnd_pmu_get_clock_freq unimplemented");
}
}
/**
* Power up a core-specific external resource.
* Return the current value of a PMU chipctrl register.
*
* @param dev The parent of @p child.
* @param pinfo PMU info for requesting core.
* @param rsrc The core-specific external resource identifier.
* @param dev A bhnd(4) PMU device.
* @param reg The PMU chipctrl register to be read.
*
* @retval 0 success
* @retval ENODEV If @p rsrc is not supported by this PMU driver.
* Drivers should only use function for functionality that is not
* available via another bhnd_chipc() function.
*
* @returns The chipctrl register value, or 0 if undefined by this hardware.
*/
METHOD int core_req_ext_rsrc {
device_t dev;
struct bhnd_core_pmu_info *pinfo;
u_int rsrc;
};
METHOD uint32_t read_chipctrl {
device_t dev;
uint32_t reg;
} DEFAULT bhnd_pmu_null_read_chipctrl;
/**
* Power down a core-specific external resource.
* Write @p value with @p mask to a PMU chipctrl register.
*
* @param dev The parent of @p child.
* @param pinfo PMU info for requesting core.
* @param rsrc The core-specific external resource identifier.
* @param dev A bhnd(4) PMU device.
* @param reg The PMU chipctrl register to be written.
* @param value The value to write.
* @param mask The mask of bits to be written from @p value.
*
* @retval 0 success
* @retval ENODEV If @p rsrc is not supported by this PMU driver.
* Drivers should only use function for functionality that is not
* available via another bhnd_pmu() function.
*/
METHOD int core_release_ext_rsrc {
device_t dev;
struct bhnd_core_pmu_info *pinfo;
u_int rsrc;
};
METHOD void write_chipctrl {
device_t dev;
uint32_t reg;
uint32_t value;
uint32_t mask;
} DEFAULT bhnd_pmu_null_write_chipctrl;
/**
* Release all outstanding requests (clocks, resources, etc) associated with
* @p pinfo.
/**
* Return the current value of a PMU regulator control register.
*
* @param dev PMU device.
* @param pinfo PMU info for requesting core.
* @param dev A bhnd(4) PMU device.
* @param reg The PMU regctrl register to be read.
*
* Drivers should only use function for functionality that is not
* available via another bhnd_chipc() function.
*
* @returns The regctrl register value, or 0 if undefined by this hardware.
*/
METHOD uint32_t read_regctrl {
device_t dev;
uint32_t reg;
} DEFAULT bhnd_pmu_null_read_regctrl;
/**
* Write @p value with @p mask to a PMU regulator control register.
*
* @param dev A bhnd(4) PMU device.
* @param reg The PMU regctrl register to be written.
* @param value The value to write.
* @param mask The mask of bits to be written from @p value.
*
* Drivers should only use function for functionality that is not
* available via another bhnd_pmu() function.
*/
METHOD void write_regctrl {
device_t dev;
uint32_t reg;
uint32_t value;
uint32_t mask;
} DEFAULT bhnd_pmu_null_write_regctrl;
/**
* Return the current value of a PMU PLL control register.
*
* @param dev A bhnd(4) PMU device.
* @param reg The PMU pllctrl register to be read.
*
* Drivers should only use function for functionality that is not
* available via another bhnd_chipc() function.
*
* @returns The pllctrl register value, or 0 if undefined by this hardware.
*/
METHOD uint32_t read_pllctrl {
device_t dev;
uint32_t reg;
} DEFAULT bhnd_pmu_null_read_pllctrl;
/**
* Write @p value with @p mask to a PMU PLL control register.
*
* @param dev A bhnd(4) PMU device.
* @param reg The PMU pllctrl register to be written.
* @param value The value to write.
* @param mask The mask of bits to be written from @p value.
*
* Drivers should only use function for functionality that is not
* available via another bhnd_pmu() function.
*/
METHOD void write_pllctrl {
device_t dev;
uint32_t reg;
uint32_t value;
uint32_t mask;
} DEFAULT bhnd_pmu_null_write_pllctrl;
/**
* Set a hardware-specific output voltage register value for @p regulator.
*
* @param dev PMU device.
* @param regulator Regulator to be configured.
* @param value The raw voltage register value.
*
* @retval 0 success
* @retval non-zero If releasing PMU request state fails, a
* regular unix error code will be returned, and
* the request state will be left unmodified.
* @retval ENODEV If @p regulator is not supported by this driver.
*/
METHOD int core_release {
device_t dev;
struct bhnd_core_pmu_info *pinfo;
METHOD int set_voltage_raw {
device_t dev;
bhnd_pmu_regulator regulator;
uint32_t value;
} DEFAULT bhnd_pmu_null_set_voltage_raw;
/**
* Enable the given @p regulator.
*
* @param dev PMU device.
* @param regulator Regulator to be enabled.
*
* @retval 0 success
* @retval ENODEV If @p regulator is not supported by this driver.
*/
METHOD int enable_regulator {
device_t dev;
bhnd_pmu_regulator regulator;
} DEFAULT bhnd_pmu_null_enable_regulator;
/**
* Disable the given @p regulator.
*
* @param dev PMU device.
* @param regulator Regulator to be disabled.
*
* @retval 0 success
* @retval ENODEV If @p regulator is not supported by this driver.
*/
METHOD int disable_regulator {
device_t dev;
bhnd_pmu_regulator regulator;
} DEFAULT bhnd_pmu_null_disable_regulator;
/**
* Return the transition latency required for @p clock in microseconds, if
* known.
*
* The BHND_CLOCK_HT latency value is suitable for use as the D11 core's
* 'fastpwrup_dly' value.
*
* @param dev PMU device.
* @param clock The clock to be queried for transition latency.
* @param[out] latency On success, the transition latency of @p clock in
* microseconds.
*
* @retval 0 success
* @retval ENODEV If the transition latency for @p clock is not available.
*/
METHOD int get_clock_latency {
device_t dev;
bhnd_clock clock;
u_int *latency;
} DEFAULT bhnd_pmu_null_get_clock_latency;
/**
* Return the frequency for @p clock in Hz, if known.
*
* @param dev PMU device.
* @param clock The clock to be queried.
* @param[out] freq On success, the frequency of @p clock in Hz.
*
* @retval 0 success
* @retval ENODEV If the frequency for @p clock is not available.
*/
METHOD int get_clock_freq {
device_t dev;
bhnd_clock clock;
u_int *freq;
} DEFAULT bhnd_pmu_null_get_clock_freq;
/**
* Request that the PMU configure itself for a given hardware-specific
* spuravoid mode.
*
* @param dev PMU device.
* @param spuravoid The requested mode.
*
* @retval 0 success
* @retval ENODEV If @p regulator is not supported by this driver.
*/
METHOD int request_spuravoid {
device_t dev;
bhnd_pmu_spuravoid spuravoid;
} DEFAULT bhnd_pmu_null_request_spuravoid;
/**
* Return the PMU's maximum state transition latency in microseconds.
*
* This upper bound may be used to busy-wait on PMU clock and resource state
* transitions.
*
* @param dev PMU device.
*
* @returns maximum PMU transition latency, in microseconds.
*/
METHOD u_int get_max_transition_latency {
device_t dev;
};

View File

@ -52,11 +52,11 @@
/* Chip Control indirect registers */
#define BHND_PMU_CCTRL_READ(_sc, _reg) \
BHND_PMU_IND_READ((_sc), CHIPCTL, (_reg))
BHND_PMU_IND_READ((_sc), CHIP_CONTROL, (_reg))
#define BHND_PMU_CCTRL_WRITE(_sc, _reg, _val, _mask) \
BHND_PMU_IND_WRITE((_sc), CHIPCTL, (_reg), (_val), (_mask))
BHND_PMU_IND_WRITE((_sc), CHIP_CONTROL, (_reg), (_val), (_mask))
/* Register Control indirect registers */
/* Regulator Control indirect registers */
#define BHND_PMU_REGCTRL_READ(_sc, _reg) \
BHND_PMU_IND_READ((_sc), REG_CONTROL, (_reg))
#define BHND_PMU_REGCTRL_WRITE(_sc, _reg, _val, _mask) \
@ -98,10 +98,6 @@ void bhnd_pmu_ind_write(const struct bhnd_pmu_io *io, void *io_ctx,
bus_size_t addr, bus_size_t data, uint32_t reg,
uint32_t val, uint32_t mask);
bool bhnd_pmu_wait_clkst(struct bhnd_pmu_softc *sc, device_t dev,
struct bhnd_resource *r, bus_size_t clkst_reg,
uint32_t value, uint32_t mask);
int bhnd_pmu_init(struct bhnd_pmu_softc *sc);
void bhnd_pmu_pll_init(struct bhnd_pmu_softc *sc, uint32_t xtalfreq);
int bhnd_pmu_res_init(struct bhnd_pmu_softc *sc);
@ -111,13 +107,13 @@ uint32_t bhnd_pmu_force_ilp(struct bhnd_pmu_softc *sc, bool force);
void bhnd_pmu_set_switcher_voltage(struct bhnd_pmu_softc *sc,
uint8_t bb_voltage, uint8_t rf_voltage);
void bhnd_pmu_set_ldo_voltage(struct bhnd_pmu_softc *sc,
int bhnd_pmu_set_ldo_voltage(struct bhnd_pmu_softc *sc,
uint8_t ldo, uint8_t voltage);
int bhnd_pmu_fast_pwrup_delay(struct bhnd_pmu_softc *sc,
uint16_t *pwrup_delay);
u_int *pwrup_delay);
void bhnd_pmu_rcal(struct bhnd_pmu_softc *sc);
void bhnd_pmu_spuravoid(struct bhnd_pmu_softc *sc,
uint8_t spuravoid);
int bhnd_pmu_set_spuravoid(struct bhnd_pmu_softc *sc,
bhnd_pmu_spuravoid spuravoid);
bool bhnd_pmu_is_otp_powered(struct bhnd_pmu_softc *sc);
uint32_t bhnd_pmu_measure_alpclk(struct bhnd_pmu_softc *sc);
@ -132,7 +128,7 @@ int bhnd_pmu_otp_power(struct bhnd_pmu_softc *sc, bool on);
void bhnd_pmu_sdiod_drive_strength_init(struct bhnd_pmu_softc *sc,
uint32_t drivestrength);
void bhnd_pmu_paref_ldo_enable(struct bhnd_pmu_softc *sc,
int bhnd_pmu_paref_ldo_enable(struct bhnd_pmu_softc *sc,
bool enable);
#endif /* _BHND_CORES_PMU_BHND_PMU_PRIVATE_H_ */

View File

@ -25,7 +25,7 @@ __FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <dev/bhnd/bhnd.h>
#include <dev/bhnd/bhndvar.h>
#include <dev/bhnd/cores/chipc/chipc.h>
#include <dev/bhnd/cores/chipc/chipcreg.h>
@ -84,16 +84,15 @@ static int bhnd_pmu_res_uptime(struct bhnd_pmu_softc *sc, uint8_t rsrc,
static int bhnd_pmu_res_masks(struct bhnd_pmu_softc *sc, uint32_t *pmin,
uint32_t *pmax);
static void bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc,
uint8_t spuravoid);
static int bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc,
bhnd_pmu_spuravoid spuravoid);
static void bhnd_pmu_set_4330_plldivs(struct bhnd_pmu_softc *sc);
#define BHND_PMU_REV(_sc) \
((uint8_t)BHND_PMU_GET_BITS((_sc)->caps, BHND_PMU_CAP_REV))
#define PMU_WAIT_CLKST(_sc, _val, _mask) \
bhnd_pmu_wait_clkst((_sc), (_sc)->dev, (_sc)->res, \
BHND_CLK_CTL_ST, (_val), (_mask))
#define PMU_WAIT_CLKST(_sc, _val, _mask) \
bhnd_core_clkctl_wait((_sc)->clkctl, (_val), (_mask))
#define PMURES_BIT(_bit) \
(1 << (BHND_PMU_ ## _bit))
@ -181,59 +180,6 @@ bhnd_pmu_ind_write(const struct bhnd_pmu_io *io, void *io_ctx, bus_size_t addr,
io->wr4(data, rval, io_ctx);
}
/**
* Wait for up to BHND_PMU_MAX_TRANSITION_DLY microseconds for the per-core
* clock status to be equal to @p value after applying @p mask.
*
* @param sc PMU driver state.
* @param dev Requesting device.
* @param r An active resource mapping the clock status register.
* @param clkst_reg Offset to the CLK_CTL_ST register.
* @param value Value to wait for.
* @param mask Mask to apply prior to value comparison.
*/
bool
bhnd_pmu_wait_clkst(struct bhnd_pmu_softc *sc, device_t dev,
struct bhnd_resource *r, bus_size_t clkst_reg, uint32_t value,
uint32_t mask)
{
uint32_t clkst;
/* Bitswapped HTAVAIL/ALPAVAIL work-around */
if (sc->quirks & BPMU_QUIRK_CLKCTL_CCS0) {
uint32_t fmask, fval;
fmask = mask & ~(BHND_CCS_HTAVAIL | BHND_CCS_ALPAVAIL);
fval = value & ~(BHND_CCS_HTAVAIL | BHND_CCS_ALPAVAIL);
if (mask & BHND_CCS_HTAVAIL)
fmask |= BHND_CCS0_HTAVAIL;
if (value & BHND_CCS_HTAVAIL)
fval |= BHND_CCS0_HTAVAIL;
if (mask & BHND_CCS_ALPAVAIL)
fmask |= BHND_CCS0_ALPAVAIL;
if (value & BHND_CCS_ALPAVAIL)
fval |= BHND_CCS0_ALPAVAIL;
mask = fmask;
value = fval;
}
for (uint32_t i = 0; i < BHND_PMU_MAX_TRANSITION_DLY; i += 10) {
clkst = bhnd_bus_read_4(r, clkst_reg);
if ((clkst & mask) == (value & mask))
return (true);
DELAY(10);
}
device_printf(dev, "clkst wait timeout (value=%#x, "
"mask=%#x)\n", value, mask);
return (false);
}
/* Setup switcher voltage */
void
bhnd_pmu_set_switcher_voltage(struct bhnd_pmu_softc *sc, uint8_t bb_voltage,
@ -243,7 +189,7 @@ bhnd_pmu_set_switcher_voltage(struct bhnd_pmu_softc *sc, uint8_t bb_voltage,
BHND_PMU_REGCTRL_WRITE(sc, 0x00, (rf_voltage & 0x1f) << 14, ~0);
}
void
int
bhnd_pmu_set_ldo_voltage(struct bhnd_pmu_softc *sc, uint8_t ldo,
uint8_t voltage)
{
@ -278,7 +224,8 @@ bhnd_pmu_set_ldo_voltage(struct bhnd_pmu_softc *sc, uint8_t ldo,
mask = 0x3f;
break;
default:
panic("unknown BCM4328/BCM5354 LDO %hhu\n", ldo);
PMU_LOG(sc, "unknown BCM4328/BCM5354 LDO %hhu\n", ldo);
return (ENODEV);
}
break;
case BHND_CHIPID_BCM4312:
@ -289,7 +236,8 @@ bhnd_pmu_set_ldo_voltage(struct bhnd_pmu_softc *sc, uint8_t ldo,
mask = 0x3f;
break;
default:
panic("unknown BCM4312 LDO %hhu\n", ldo);
PMU_LOG(sc, "unknown BCM4312 LDO %hhu\n", ldo);
return (ENODEV);
}
break;
case BHND_CHIPID_BCM4325:
@ -333,7 +281,8 @@ bhnd_pmu_set_ldo_voltage(struct bhnd_pmu_softc *sc, uint8_t ldo,
mask = 0x1;
break;
default:
panic("unknown BCM4325 LDO %hhu\n", ldo);
PMU_LOG(sc, "unknown BCM4325 LDO %hhu\n", ldo);
return (ENODEV);
}
break;
case BHND_CHIPID_BCM4336:
@ -354,7 +303,8 @@ bhnd_pmu_set_ldo_voltage(struct bhnd_pmu_softc *sc, uint8_t ldo,
mask = 0xf;
break;
default:
panic("unknown BCM4336 LDO %hhu\n", ldo);
PMU_LOG(sc, "unknown BCM4336 LDO %hhu\n", ldo);
return (ENODEV);
}
break;
case BHND_CHIPID_BCM4330:
@ -365,7 +315,8 @@ bhnd_pmu_set_ldo_voltage(struct bhnd_pmu_softc *sc, uint8_t ldo,
mask = 0x1f;
break;
default:
panic("unknown BCM4330 LDO %hhu\n", ldo);
PMU_LOG(sc, "unknown BCM4330 LDO %hhu\n", ldo);
return (ENODEV);
}
break;
case BHND_CHIPID_BCM4331:
@ -376,24 +327,27 @@ bhnd_pmu_set_ldo_voltage(struct bhnd_pmu_softc *sc, uint8_t ldo,
mask = 0xf;
break;
default:
panic("unknown BCM4331 LDO %hhu\n", ldo);
PMU_LOG(sc, "unknown BCM4331 LDO %hhu\n", ldo);
return (ENODEV);
}
break;
default:
panic("cannot set LDO voltage on unsupported chip %hu\n",
PMU_LOG(sc, "cannot set LDO voltage on unsupported chip %hu\n",
sc->cid.chip_id);
return;
return (ENODEV);
}
regctrl = (voltage & mask) << shift;
BHND_PMU_REGCTRL_WRITE(sc, addr, regctrl, mask << shift);
return (0);
}
/* d11 slow to fast clock transition time in slow clock cycles */
#define D11SCC_SLOW2FAST_TRANSITION 2
int
bhnd_pmu_fast_pwrup_delay(struct bhnd_pmu_softc *sc, uint16_t *pwrup_delay)
bhnd_pmu_fast_pwrup_delay(struct bhnd_pmu_softc *sc, u_int *pwrup_delay)
{
uint32_t ilp;
uint32_t uptime;
@ -470,7 +424,7 @@ bhnd_pmu_fast_pwrup_delay(struct bhnd_pmu_softc *sc, uint16_t *pwrup_delay)
break;
}
*pwrup_delay = (uint16_t)delay;
*pwrup_delay = delay;
return (0);
}
@ -2005,10 +1959,10 @@ bhnd_pmu1_pllinit0(struct bhnd_pmu_softc *sc, uint32_t xtal)
switch (xt->fref) {
case XTAL_FREQ_24000MHZ:
pll_sel = BHND_PMU_CCTL_4319USB_24MHZ_PLL_SEL;
pll_sel = BHND_PMU_CCTRL4319USB_24MHZ_PLL_SEL;
break;
case XTAL_FREQ_48000MHZ:
pll_sel = BHND_PMU_CCTL_4319USB_48MHZ_PLL_SEL;
pll_sel = BHND_PMU_CCTRL4319USB_48MHZ_PLL_SEL;
break;
default:
panic("unsupported 4319USB XTAL frequency: %hu\n",
@ -2016,8 +1970,8 @@ bhnd_pmu1_pllinit0(struct bhnd_pmu_softc *sc, uint32_t xtal)
}
BHND_PMU_CCTRL_WRITE(sc, BHND_PMU1_PLL0_CHIPCTL2,
BHND_PMU_SET_BITS(pll_sel, BHND_PMU_CCTL_4319USB_XTAL_SEL),
BHND_PMU_CCTL_4319USB_XTAL_SEL_MASK);
BHND_PMU_SET_BITS(pll_sel, BHND_PMU_CCTRL4319USB_XTAL_SEL),
BHND_PMU_CCTRL4319USB_XTAL_SEL_MASK);
}
/* Flush deferred pll control registers writes */
@ -2926,10 +2880,10 @@ bhnd_pmu_rcal(struct bhnd_pmu_softc *sc)
case BHND_CHIPID_BCM4325:
case BHND_CHIPID_BCM4329:
/* Kick RCal */
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIPCTL_ADDR, 1);
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_ADDR, 1);
/* Power Down RCAL Block */
BHND_PMU_AND_4(sc, BHND_PMU_CHIPCTL_DATA, ~0x04);
BHND_PMU_AND_4(sc, BHND_PMU_CHIP_CONTROL_DATA, ~0x04);
if (sc->cid.chip_id == BHND_CHIPID_BCM4325) {
chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev);
@ -2938,7 +2892,7 @@ bhnd_pmu_rcal(struct bhnd_pmu_softc *sc)
}
/* Power Up RCAL block */
BHND_PMU_AND_4(sc, BHND_PMU_CHIPCTL_DATA, 0x04);
BHND_PMU_AND_4(sc, BHND_PMU_CHIP_CONTROL_DATA, 0x04);
/* Wait for completion */
for (int i = 0; i < (10 * 1000 * 1000); i++) {
@ -2975,34 +2929,36 @@ bhnd_pmu_rcal(struct bhnd_pmu_softc *sc)
BHND_PMU_WRITE_4(sc, BHND_PMU_REG_CONTROL_DATA, val);
/* Write RCal code into pmu_chip_ctrl[33:30] */
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIPCTL_ADDR, 0);
val = BHND_PMU_READ_4(sc, BHND_PMU_CHIPCTL_DATA);
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_ADDR, 0);
val = BHND_PMU_READ_4(sc, BHND_PMU_CHIP_CONTROL_DATA);
val &= ~((uint32_t) 0x03 << 30);
val |= (uint32_t) (rcal_code & 0x03) << 30;
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIPCTL_DATA, val);
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_DATA, val);
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIPCTL_ADDR, 1);
val = BHND_PMU_READ_4(sc, BHND_PMU_CHIPCTL_DATA);
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_ADDR, 1);
val = BHND_PMU_READ_4(sc, BHND_PMU_CHIP_CONTROL_DATA);
val &= ~(uint32_t) 0x03;
val |= (uint32_t) ((rcal_code >> 2) & 0x03);
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIPCTL_DATA, val);
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_DATA, val);
/* Set override in pmu_chip_ctrl[29] */
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIPCTL_ADDR, 0);
BHND_PMU_OR_4(sc, BHND_PMU_CHIPCTL_DATA, (0x01 << 29));
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_ADDR, 0);
BHND_PMU_OR_4(sc, BHND_PMU_CHIP_CONTROL_DATA, (0x01 << 29));
/* Power off RCal block */
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIPCTL_ADDR, 1);
BHND_PMU_AND_4(sc, BHND_PMU_CHIPCTL_DATA, ~0x04);
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_ADDR, 1);
BHND_PMU_AND_4(sc, BHND_PMU_CHIP_CONTROL_DATA, ~0x04);
break;
default:
break;
}
}
void
bhnd_pmu_spuravoid(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
int
bhnd_pmu_set_spuravoid(struct bhnd_pmu_softc *sc, bhnd_pmu_spuravoid spuravoid)
{
int error;
/* force the HT off */
if (sc->cid.chip_id == BHND_CHIPID_BCM4336) {
BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK,
@ -3013,25 +2969,24 @@ bhnd_pmu_spuravoid(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
}
/* update the pll changes */
bhnd_pmu_spuravoid_pllupdate(sc, spuravoid);
error = bhnd_pmu_spuravoid_pllupdate(sc, spuravoid);
/* enable HT back on */
if (sc->cid.chip_id == BHND_CHIPID_BCM4336) {
BHND_PMU_OR_4(sc, BHND_PMU_MAX_RES_MASK,
BHND_PMU_RES4336_HT_AVAIL);
}
return (error);
}
static void
bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
static int
bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc,
bhnd_pmu_spuravoid spuravoid)
{
uint16_t chip_id;
uint32_t tmp;
uint32_t pmuctrl;
uint8_t phypll_offset;
uint8_t bcm5357_bcm43236_p1div[] = { 0x1, 0x5, 0x5 };
uint8_t bcm5357_bcm43236_ndiv[] = { 0x30, 0xf6, 0xfc };
uint16_t chip_id;
uint32_t pmuctrl;
uint32_t tmp;
/* 6362a0 has same clks as 4322[4-6] */
chip_id = sc->cid.chip_id;
@ -3050,12 +3005,26 @@ bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
case BHND_CHIPID_BCM43238:
case BHND_CHIPID_BCM43234:
case BHND_CHIPID_BCM43237:
case BHND_CHIPID_BCM53572:
KASSERT(spuravoid < nitems(bcm5357_bcm43236_p1div),
("spuravoid %hhu outside p1div table\n", spuravoid));
case BHND_CHIPID_BCM53572: {
uint8_t p1div, ndiv;
uint8_t phypll_offset;
KASSERT(spuravoid < nitems(bcm5357_bcm43236_ndiv),
("spuravoid %hhu outside ndiv table\n", spuravoid));
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
p1div = 0x1;
ndiv = 0x30;
break;
case BHND_PMU_SPURAVOID_M1:
p1div = 0x5;
ndiv = 0xf6;
break;
case BHND_PMU_SPURAVOID_M2:
p1div = 0x5;
ndiv = 0xfc;
break;
default:
return (ENODEV);
}
/* BCM5357 needs to touch PLL1_PLLCTL[02], so offset
* PLL0_PLLCTL[02] by 6 */
@ -3064,37 +3033,46 @@ bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
phypll_offset = 6;
/* RMW only the P1 divider */
tmp = BHND_PMU_SET_BITS(bcm5357_bcm43236_p1div[spuravoid],
BHND_PMU1_PLL0_PC0_P1DIV);
tmp = BHND_PMU_SET_BITS(p1div, BHND_PMU1_PLL0_PC0_P1DIV);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0 + phypll_offset,
tmp, BHND_PMU1_PLL0_PC0_P1DIV_MASK);
/* RMW only the int feedback divider */
tmp = BHND_PMU_SET_BITS(bcm5357_bcm43236_ndiv[spuravoid],
BHND_PMU1_PLL0_PC2_NDIV_INT);
tmp = BHND_PMU_SET_BITS(ndiv, BHND_PMU1_PLL0_PC2_NDIV_INT);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2 + phypll_offset,
tmp, BHND_PMU1_PLL0_PC0_P1DIV_MASK);
pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD;
break;
}
case BHND_CHIPID_BCM4331:
if (spuravoid == 2) {
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500014, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0FC00a08, ~0);
} else if (spuravoid == 1) {
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500014, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0F600a08, ~0);
} else {
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11100014, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x03000a08, ~0);
break;
case BHND_PMU_SPURAVOID_M1:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500014, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0F600a08, ~0);
break;
case BHND_PMU_SPURAVOID_M2:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500014, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0FC00a08, ~0);
break;
default:
return (ENODEV);
}
pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD;
break;
@ -3102,20 +3080,8 @@ bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
case BHND_CHIPID_BCM43225:
case BHND_CHIPID_BCM43226:
case BHND_CHIPID_BCM43421:
if (spuravoid == 1) {
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500010, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x000C0C06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0F600a08, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00000000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x2001E920, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
} else {
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11100010, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
@ -3128,7 +3094,28 @@ bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
0x200005c0, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
break;
case BHND_PMU_SPURAVOID_M1:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500010, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x000C0C06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0F600a08, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00000000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x2001E920, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
break;
case BHND_PMU_SPURAVOID_M2:
default:
return (ENODEV);
}
pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD;
break;
@ -3136,20 +3123,8 @@ bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
case BHND_CHIPID_BCM43112:
case BHND_CHIPID_BCM43222:
case BHND_CHIPID_BCM43420:
if (spuravoid == 1) {
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500008, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x0c000c06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0f600a08, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00000000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x2001e920, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
} else {
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11100008, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
@ -3162,6 +3137,26 @@ bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
0x200005c0, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888855, ~0);
break;
case BHND_PMU_SPURAVOID_M1:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500008, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x0c000c06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0f600a08, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00000000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x2001e920, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
break;
case BHND_PMU_SPURAVOID_M2:
default:
return (ENODEV);
}
pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD;
@ -3170,20 +3165,8 @@ bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
case BHND_CHIPID_BCM4716:
case BHND_CHIPID_BCM4748:
case BHND_CHIPID_BCM47162:
if (spuravoid == 1) {
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500060, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x080C0C06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0F600000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00000000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x2001E924, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
} else {
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11100060, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
@ -3196,8 +3179,29 @@ bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
0x200005c0, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
break;
case BHND_PMU_SPURAVOID_M1:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500060, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x080C0C06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0F600000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00000000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x2001E924, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
break;
case BHND_PMU_SPURAVOID_M2:
default:
return (ENODEV);
}
pmuctrl = BHND_PMU_CTRL_NOILP_ON_WAIT |
BHND_PMU_CTRL_PLL_PLLCTL_UPD;
break;
@ -3214,14 +3218,22 @@ bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, 0x1014140a, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, 0x88888854, ~0);
if (spuravoid == 1) {
/* spur_avoid ON, enable 41/82/164Mhz clock mode */
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x05201828, ~0);
} else {
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
/* enable 40/80/160Mhz clock mode */
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x05001828, ~0);
break;
case BHND_PMU_SPURAVOID_M1:
/* spur_avoid ON, enable 41/82/164Mhz clock mode */
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x05201828, ~0);
break;
case BHND_PMU_SPURAVOID_M2:
default:
return (ENODEV);
}
pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD;
@ -3235,15 +3247,25 @@ bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, 0x202C2820, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, 0x88888825, ~0);
if (spuravoid == 1) {
tmp = 0x00EC4EC4;
} else {
tmp = 0x00762762;
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00762762, ~0);
break;
case BHND_PMU_SPURAVOID_M1:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00EC4EC4, ~0);
break;
case BHND_PMU_SPURAVOID_M2:
default:
return (ENODEV);
}
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, tmp, ~0);
pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD;
break;
case BHND_CHIPID_BCM43131:
case BHND_CHIPID_BCM43227:
case BHND_CHIPID_BCM43228:
@ -3251,20 +3273,8 @@ bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
/* LCNXN */
/* PLL Settings for spur avoidance on/off mode, no on2 support
* for 43228A0 */
if (spuravoid == 1) {
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x01100014, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x040C0C06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x03140A08, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00333333, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x202C2820, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
} else {
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11100014, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
@ -3277,18 +3287,41 @@ bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, uint8_t spuravoid)
0x200005c0, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
break;
case BHND_PMU_SPURAVOID_M1:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x01100014, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x040C0C06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x03140A08, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00333333, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x202C2820, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
break;
case BHND_PMU_SPURAVOID_M2:
default:
return (ENODEV);
}
pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD;
break;
default:
PMU_LOG(sc, "%s: unknown spuravoidance settings for chip %#hx, "
"not changing PLL", __func__, sc->cid.chip_id);
pmuctrl = 0;
break;
return (ENODEV);
}
if (pmuctrl != 0)
BHND_PMU_OR_4(sc, BHND_PMU_CTRL, pmuctrl);
return (0);
}
bool
@ -3333,7 +3366,7 @@ bhnd_pmu_is_otp_powered(struct bhnd_pmu_softc *sc)
return (true);
}
void
int
bhnd_pmu_paref_ldo_enable(struct bhnd_pmu_softc *sc, bool enable)
{
uint32_t ldo;
@ -3349,7 +3382,7 @@ bhnd_pmu_paref_ldo_enable(struct bhnd_pmu_softc *sc, bool enable)
ldo = PMURES_BIT(RES4312_PA_REF_LDO);
break;
default:
return;
return (ENODEV);
}
if (enable) {
@ -3357,6 +3390,8 @@ bhnd_pmu_paref_ldo_enable(struct bhnd_pmu_softc *sc, bool enable)
} else {
BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~ldo);
}
return (0);
}
/* initialize PMU switch/regulators */

View File

@ -0,0 +1,53 @@
/*-
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Landon Fuller under sponsorship from
* the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _BHND_CORES_PMU_BHND_PMU_TYPES_H_
#define _BHND_CORES_PMU_BHND_PMU_TYPES_H_
#include <sys/types.h>
/**
* bhnd_pmu(4) regulators.
*/
typedef enum bhnd_pmu_regulator {
BHND_REGULATOR_PAREF_LDO = 0, /**< PA reference LDO */
} bhnd_pmu_regulator;
/**
* bhnd_pmu(4) spurious signal avoidance modes.
*/
typedef enum bhnd_pmu_spuravoid {
BHND_PMU_SPURAVOID_NONE = 0, /**< spur avoidance disabled */
BHND_PMU_SPURAVOID_M1 = 1, /**< chipset-specific mode 1 */
BHND_PMU_SPURAVOID_M2 = 2, /**< chipset-specific mode 2 */
} bhnd_pmu_spuravoid;
#endif /* _BHND_CORES_PMU_BHND_PMU_TYPES_H_ */

View File

@ -148,8 +148,8 @@
#define BHND_PMU_RRQT_ALP_REQ 0x1000
#define BHND_PMU_RRQT_HT_REQ 0x2000
#define BHND_PMU_RES_REQ_MASK 0x648
#define BHND_PMU_CHIPCTL_ADDR 0x650
#define BHND_PMU_CHIPCTL_DATA 0x654
#define BHND_PMU_CHIP_CONTROL_ADDR 0x650
#define BHND_PMU_CHIP_CONTROL_DATA 0x654
#define BHND_PMU_REG_CONTROL_ADDR 0x658
#define BHND_PMU_REG_CONTROL_DATA 0x65C
#define BHND_PMU_PLL_CONTROL_ADDR 0x660
@ -434,8 +434,8 @@
/* 5357 chip-specific CHIPCTRL register bits */
#define BHND_PMU_CCTRL5357_EXTPA (1<<14) /* extPA in CHIPCTL1, bit 14 */
#define BHND_PMU_CCTRL5357_ANT_MUX_2o3 (1<<15) /* 2o3 in CHIPCTL1, bit 15 */
#define BHND_PMU_CCTRL5357_EXTPA (1<<14) /* extPA in CHIPCTRL1, bit 14 */
#define BHND_PMU_CCTRL5357_ANT_MUX_2o3 (1<<15) /* 2o3 in CHIPCTRL1, bit 15 */
/* 4328 PMU resources */
@ -553,9 +553,9 @@
/* 43224 chip-specific CHIPCTRL register bits */
#define BHND_PMU_CCTRL_43224_GPIO_TOGGLE 0x8000
#define BHND_PMU_CCTRL_43224A0_12MA_LED_DRIVE 0x00F000F0 /* 12 mA drive strength */
#define BHND_PMU_CCTRL_43224B0_12MA_LED_DRIVE 0xF0 /* 12 mA drive strength for later 43224s */
#define BHND_PMU_CCTRL43224_GPIO_TOGGLE 0x8000
#define BHND_PMU_CCTRL43224A0_12MA_LED_DRIVE 0x00F000F0 /* 12 mA drive strength */
#define BHND_PMU_CCTRL43224B0_12MA_LED_DRIVE 0xF0 /* 12 mA drive strength for later 43224s */
/* 43236 PMU resources */
@ -626,10 +626,10 @@
#define BHND_PMU1_PLL0_CHIPCTL0 0
#define BHND_PMU1_PLL0_CHIPCTL1 1
#define BHND_PMU1_PLL0_CHIPCTL2 2
#define BHND_PMU_CCTL_4319USB_XTAL_SEL_MASK 0x00180000
#define BHND_PMU_CCTL_4319USB_XTAL_SEL_SHIFT 19
#define BHND_PMU_CCTL_4319USB_48MHZ_PLL_SEL 1
#define BHND_PMU_CCTL_4319USB_24MHZ_PLL_SEL 2
#define BHND_PMU_CCTRL4319USB_XTAL_SEL_MASK 0x00180000
#define BHND_PMU_CCTRL4319USB_XTAL_SEL_SHIFT 19
#define BHND_PMU_CCTRL4319USB_48MHZ_PLL_SEL 1
#define BHND_PMU_CCTRL4319USB_24MHZ_PLL_SEL 2
/* 4336 PMU resources */
#define BHND_PMU_RES4336_CBUCK_LPOM 0
@ -708,7 +708,7 @@
#define BHND_PMU_RES4313_MACPHY_CLK_AVAIL_RSRC 15
/* 4313 chip-specific CHIPCTRL register bits */
#define BHND_PMU_CCTRL_4313_12MA_LED_DRIVE 0x00000007 /* 12 mA drive strengh for later 4313 */
#define BHND_PMU_CCTRL4313_12MA_LED_DRIVE 0x00000007 /* 12 mA drive strengh for later 4313 */
/* 43228 resources */
#define BHND_PMU_RES43228_NOT_USED 0

View File

@ -60,20 +60,6 @@ uint32_t bhnd_pmu_mem_clock(struct bhnd_pmu_query *sc);
uint32_t bhnd_pmu_alp_clock(struct bhnd_pmu_query *sc);
uint32_t bhnd_pmu_ilp_clock(struct bhnd_pmu_query *sc);
/*
* BHND PMU device quirks / features
*/
enum {
/** No quirks */
BPMU_QUIRK_NONE = 0,
/** On BCM4328-derived chipsets, the CLK_CTL_ST register CCS_HTAVAIL
* and CCS_ALPAVAIL bits are swapped; the BHND_CCS0_* constants should
* be used. */
BPMU_QUIRK_CLKCTL_CCS0 = 1
};
/**
* PMU read-only query support.
*
@ -110,7 +96,6 @@ struct bhnd_pmu_io {
*/
struct bhnd_pmu_softc {
device_t dev;
uint32_t quirks; /**< device quirk flags */
uint32_t caps; /**< pmu capability flags. */
struct bhnd_chipid cid; /**< chip identification */
@ -121,6 +106,7 @@ struct bhnd_pmu_softc {
struct bhnd_resource *res; /**< pmu register block. */
int rid; /**< pmu register RID */
struct bhnd_core_clkctl *clkctl; /**< pmu clkctl register */
struct mtx mtx; /**< state mutex */
@ -132,11 +118,10 @@ struct bhnd_pmu_softc {
};
#define BPMU_LOCK_INIT(sc) \
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
"BHND chipc driver lock", MTX_DEF)
#define BPMU_LOCK(sc) mtx_lock(&(sc)->mtx)
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), NULL, MTX_DEF)
#define BPMU_LOCK(sc) mtx_lock(&(sc)->mtx)
#define BPMU_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
#define BPMU_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what)
#define BPMU_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx)
#define BPMU_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what)
#define BPMU_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx)
#endif /* _BHND_CORES_PMU_BHND_PMUVAR_H_ */

View File

@ -39,12 +39,12 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/refcount.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <dev/bhnd/cores/chipc/chipcreg.h>
#include <dev/bhnd/cores/pmu/bhnd_pmu.h>
#include <dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.h>
#include "sibareg.h"
#include "sibavar.h"
@ -78,9 +78,12 @@ siba_attach(device_t dev)
sc = device_get_softc(dev);
sc->dev = dev;
SIBA_LOCK_INIT(sc);
/* Enumerate children */
if ((error = siba_add_children(dev))) {
device_delete_children(dev);
SIBA_LOCK_DESTROY(sc);
return (error);
}
@ -90,7 +93,17 @@ siba_attach(device_t dev)
int
siba_detach(device_t dev)
{
return (bhnd_generic_detach(dev));
struct siba_softc *sc;
int error;
sc = device_get_softc(dev);
if ((error = bhnd_generic_detach(dev)))
return (error);
SIBA_LOCK_DESTROY(sc);
return (0);
}
int
@ -108,9 +121,11 @@ siba_suspend(device_t dev)
static int
siba_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
{
const struct siba_devinfo *dinfo;
const struct bhnd_core_info *cfg;
struct siba_softc *sc;
const struct siba_devinfo *dinfo;
const struct bhnd_core_info *cfg;
sc = device_get_softc(dev);
dinfo = device_get_ivars(child);
cfg = &dinfo->core_id.core_info;
@ -140,8 +155,27 @@ siba_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
*result = cfg->unit;
return (0);
case BHND_IVAR_PMU_INFO:
*result = (uintptr_t) dinfo->pmu_info;
return (0);
SIBA_LOCK(sc);
switch (dinfo->pmu_state) {
case SIBA_PMU_NONE:
*result = (uintptr_t)NULL;
SIBA_UNLOCK(sc);
return (0);
case SIBA_PMU_BHND:
*result = (uintptr_t)dinfo->pmu.bhnd_info;
SIBA_UNLOCK(sc);
return (0);
case SIBA_PMU_PWRCTL:
panic("bhnd_get_pmu_info() called with "
"SIBA_PMU_PWRCTL");
return (ENXIO);
}
panic("invalid PMU state: %d", dinfo->pmu_state);
return (ENXIO);
default:
return (ENOENT);
}
@ -150,8 +184,10 @@ siba_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
static int
siba_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
{
struct siba_devinfo *dinfo;
struct siba_softc *sc;
struct siba_devinfo *dinfo;
sc = device_get_softc(dev);
dinfo = device_get_ivars(child);
switch (index) {
@ -165,8 +201,24 @@ siba_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
case BHND_IVAR_CORE_UNIT:
return (EINVAL);
case BHND_IVAR_PMU_INFO:
dinfo->pmu_info = (struct bhnd_core_pmu_info *) value;
return (0);
SIBA_LOCK(sc);
switch (dinfo->pmu_state) {
case SIBA_PMU_NONE:
case SIBA_PMU_BHND:
dinfo->pmu.bhnd_info = (void *)value;
dinfo->pmu_state = SIBA_PMU_BHND;
SIBA_UNLOCK(sc);
return (0);
case SIBA_PMU_PWRCTL:
panic("bhnd_set_pmu_info() called with "
"SIBA_PMU_PWRCTL");
return (ENXIO);
}
panic("invalid PMU state: %d", dinfo->pmu_state);
return (ENXIO);
default:
return (ENOENT);
}
@ -179,6 +231,332 @@ siba_get_resource_list(device_t dev, device_t child)
return (&dinfo->resources);
}
/* BHND_BUS_ALLOC_PMU() */
static int
siba_alloc_pmu(device_t dev, device_t child)
{
struct siba_softc *sc;
struct siba_devinfo *dinfo;
device_t pwrctl;
int error;
if (device_get_parent(child) != dev)
return (EINVAL);
sc = device_get_softc(dev);
dinfo = device_get_ivars(child);
pwrctl = bhnd_retain_provider(child, BHND_SERVICE_PWRCTL);
/* Unless this is a legacy PWRCTL chipset, defer to bhnd(4)'s PMU
* implementation */
if (pwrctl == NULL) {
if ((error = bhnd_generic_alloc_pmu(dev, child)))
return (error);
KASSERT(dinfo->pmu_state == SIBA_PMU_BHND,
("unexpected PMU state: %d", dinfo->pmu_state));
return (0);
}
/* This is a legacy PWRCTL chipset; we need to map all bhnd(4) bus PMU
* to PWRCTL operations ourselves.*/
SIBA_LOCK(sc);
/* Per-core PMU state already allocated? */
if (dinfo->pmu_state != SIBA_PMU_NONE) {
panic("duplicate PMU allocation for %s",
device_get_nameunit(child));
}
/* Update the child's PMU allocation state, and transfer ownership of
* the PWRCTL provider reference */
dinfo->pmu_state = SIBA_PMU_PWRCTL;
dinfo->pmu.pwrctl = pwrctl;
SIBA_UNLOCK(sc);
return (0);
}
/* BHND_BUS_RELEASE_PMU() */
static int
siba_release_pmu(device_t dev, device_t child)
{
struct siba_softc *sc;
struct siba_devinfo *dinfo;
device_t pwrctl;
int error;
if (device_get_parent(child) != dev)
return (EINVAL);
sc = device_get_softc(dev);
dinfo = device_get_ivars(child);
SIBA_LOCK(sc);
switch(dinfo->pmu_state) {
case SIBA_PMU_NONE:
panic("pmu over-release for %s", device_get_nameunit(child));
SIBA_UNLOCK(sc);
return (ENXIO);
case SIBA_PMU_BHND:
SIBA_UNLOCK(sc);
return (bhnd_generic_release_pmu(dev, child));
case SIBA_PMU_PWRCTL:
/* Requesting BHND_CLOCK_DYN releases any outstanding clock
* reservations */
pwrctl = dinfo->pmu.pwrctl;
error = bhnd_pwrctl_request_clock(pwrctl, child,
BHND_CLOCK_DYN);
if (error) {
SIBA_UNLOCK(sc);
return (error);
}
/* Clean up the child's PMU state */
dinfo->pmu_state = SIBA_PMU_NONE;
dinfo->pmu.pwrctl = NULL;
SIBA_UNLOCK(sc);
/* Release the provider reference */
bhnd_release_provider(child, pwrctl, BHND_SERVICE_PWRCTL);
return (0);
}
panic("invalid PMU state: %d", dinfo->pmu_state);
}
/* BHND_BUS_GET_CLOCK_LATENCY() */
static int
siba_get_clock_latency(device_t dev, device_t child, bhnd_clock clock,
u_int *latency)
{
struct siba_softc *sc;
struct siba_devinfo *dinfo;
int error;
if (device_get_parent(child) != dev)
return (EINVAL);
sc = device_get_softc(dev);
dinfo = device_get_ivars(child);
SIBA_LOCK(sc);
switch(dinfo->pmu_state) {
case SIBA_PMU_NONE:
panic("no active PMU request state");
SIBA_UNLOCK(sc);
return (ENXIO);
case SIBA_PMU_BHND:
SIBA_UNLOCK(sc);
return (bhnd_generic_get_clock_latency(dev, child, clock,
latency));
case SIBA_PMU_PWRCTL:
error = bhnd_pwrctl_get_clock_latency(dinfo->pmu.pwrctl, clock,
latency);
SIBA_UNLOCK(sc);
return (error);
}
panic("invalid PMU state: %d", dinfo->pmu_state);
}
/* BHND_BUS_GET_CLOCK_FREQ() */
static int
siba_get_clock_freq(device_t dev, device_t child, bhnd_clock clock,
u_int *freq)
{
struct siba_softc *sc;
struct siba_devinfo *dinfo;
int error;
if (device_get_parent(child) != dev)
return (EINVAL);
sc = device_get_softc(dev);
dinfo = device_get_ivars(child);
SIBA_LOCK(sc);
switch(dinfo->pmu_state) {
case SIBA_PMU_NONE:
panic("no active PMU request state");
SIBA_UNLOCK(sc);
return (ENXIO);
case SIBA_PMU_BHND:
SIBA_UNLOCK(sc);
return (bhnd_generic_get_clock_freq(dev, child, clock, freq));
case SIBA_PMU_PWRCTL:
error = bhnd_pwrctl_get_clock_freq(dinfo->pmu.pwrctl, clock,
freq);
SIBA_UNLOCK(sc);
return (error);
}
panic("invalid PMU state: %d", dinfo->pmu_state);
}
/* BHND_BUS_REQUEST_EXT_RSRC() */
static int
siba_request_ext_rsrc(device_t dev, device_t child, u_int rsrc)
{
struct siba_softc *sc;
struct siba_devinfo *dinfo;
if (device_get_parent(child) != dev)
return (EINVAL);
sc = device_get_softc(dev);
dinfo = device_get_ivars(child);
SIBA_LOCK(sc);
switch(dinfo->pmu_state) {
case SIBA_PMU_NONE:
panic("no active PMU request state");
SIBA_UNLOCK(sc);
return (ENXIO);
case SIBA_PMU_BHND:
SIBA_UNLOCK(sc);
return (bhnd_generic_request_ext_rsrc(dev, child, rsrc));
case SIBA_PMU_PWRCTL:
/* HW does not support per-core external resources */
SIBA_UNLOCK(sc);
return (ENODEV);
}
panic("invalid PMU state: %d", dinfo->pmu_state);
}
/* BHND_BUS_RELEASE_EXT_RSRC() */
static int
siba_release_ext_rsrc(device_t dev, device_t child, u_int rsrc)
{
struct siba_softc *sc;
struct siba_devinfo *dinfo;
if (device_get_parent(child) != dev)
return (EINVAL);
sc = device_get_softc(dev);
dinfo = device_get_ivars(child);
SIBA_LOCK(sc);
switch(dinfo->pmu_state) {
case SIBA_PMU_NONE:
panic("no active PMU request state");
SIBA_UNLOCK(sc);
return (ENXIO);
case SIBA_PMU_BHND:
SIBA_UNLOCK(sc);
return (bhnd_generic_release_ext_rsrc(dev, child, rsrc));
case SIBA_PMU_PWRCTL:
/* HW does not support per-core external resources */
SIBA_UNLOCK(sc);
return (ENODEV);
}
panic("invalid PMU state: %d", dinfo->pmu_state);
}
/* BHND_BUS_REQUEST_CLOCK() */
static int
siba_request_clock(device_t dev, device_t child, bhnd_clock clock)
{
struct siba_softc *sc;
struct siba_devinfo *dinfo;
int error;
if (device_get_parent(child) != dev)
return (EINVAL);
sc = device_get_softc(dev);
dinfo = device_get_ivars(child);
SIBA_LOCK(sc);
switch(dinfo->pmu_state) {
case SIBA_PMU_NONE:
panic("no active PMU request state");
SIBA_UNLOCK(sc);
return (ENXIO);
case SIBA_PMU_BHND:
SIBA_UNLOCK(sc);
return (bhnd_generic_request_clock(dev, child, clock));
case SIBA_PMU_PWRCTL:
error = bhnd_pwrctl_request_clock(dinfo->pmu.pwrctl, child,
clock);
SIBA_UNLOCK(sc);
return (error);
}
panic("invalid PMU state: %d", dinfo->pmu_state);
}
/* BHND_BUS_ENABLE_CLOCKS() */
static int
siba_enable_clocks(device_t dev, device_t child, uint32_t clocks)
{
struct siba_softc *sc;
struct siba_devinfo *dinfo;
if (device_get_parent(child) != dev)
return (EINVAL);
sc = device_get_softc(dev);
dinfo = device_get_ivars(child);
SIBA_LOCK(sc);
switch(dinfo->pmu_state) {
case SIBA_PMU_NONE:
panic("no active PMU request state");
SIBA_UNLOCK(sc);
return (ENXIO);
case SIBA_PMU_BHND:
SIBA_UNLOCK(sc);
return (bhnd_generic_enable_clocks(dev, child, clocks));
case SIBA_PMU_PWRCTL:
SIBA_UNLOCK(sc);
/* All (supported) clocks are already enabled by default */
clocks &= ~(BHND_CLOCK_DYN |
BHND_CLOCK_ILP |
BHND_CLOCK_ALP |
BHND_CLOCK_HT);
if (clocks != 0) {
device_printf(dev, "%s requested unknown clocks: %#x\n",
device_get_nameunit(child), clocks);
return (ENODEV);
}
return (0);
}
panic("invalid PMU state: %d", dinfo->pmu_state);
}
static int
siba_read_iost(device_t dev, device_t child, uint16_t *iost)
{
@ -328,8 +706,8 @@ siba_reset_hw(device_t dev, device_t child, uint16_t ioctl)
static int
siba_suspend_hw(device_t dev, device_t child)
{
struct siba_softc *sc;
struct siba_devinfo *dinfo;
struct bhnd_core_pmu_info *pm;
struct bhnd_resource *r;
uint32_t idl, ts_low;
uint16_t ioctl;
@ -338,8 +716,8 @@ siba_suspend_hw(device_t dev, device_t child)
if (device_get_parent(child) != dev)
return (EINVAL);
sc = device_get_softc(dev);
dinfo = device_get_ivars(child);
pm = dinfo->pmu_info;
/* Can't suspend the core without access to the CFG0 registers */
if ((r = dinfo->cfg_res[0]) == NULL)
@ -412,16 +790,30 @@ siba_suspend_hw(device_t dev, device_t child)
return (error);
}
/* Core is now in RESET, with clocks disabled and REJ not asserted.
*
* We lastly need to inform the PMU, releasing any outstanding per-core
* PMU requests */
if (pm != NULL) {
if ((error = BHND_PMU_CORE_RELEASE(pm->pm_pmu, pm)))
return (error);
}
/*
* Core is now in RESET, with clocks disabled and REJ not asserted.
*
* If the core holds any PWRCTL clock reservations, we need to release
* those now. This emulates the standard bhnd(4) PMU behavior of RESET
* automatically clearing clkctl
*/
SIBA_LOCK(sc);
if (dinfo->pmu_state == SIBA_PMU_PWRCTL) {
error = bhnd_pwrctl_request_clock(dinfo->pmu.pwrctl, child,
BHND_CLOCK_DYN);
SIBA_UNLOCK(sc);
return (0);
if (error) {
device_printf(child, "failed to release clock request: "
"%d", error);
return (error);
}
return (0);
} else {
SIBA_UNLOCK(sc);
return (0);
}
}
static int
@ -1061,6 +1453,14 @@ static device_method_t siba_methods[] = {
/* BHND interface */
DEVMETHOD(bhnd_bus_get_erom_class, siba_get_erom_class),
DEVMETHOD(bhnd_bus_alloc_pmu, siba_alloc_pmu),
DEVMETHOD(bhnd_bus_release_pmu, siba_release_pmu),
DEVMETHOD(bhnd_bus_request_clock, siba_request_clock),
DEVMETHOD(bhnd_bus_enable_clocks, siba_enable_clocks),
DEVMETHOD(bhnd_bus_request_ext_rsrc, siba_request_ext_rsrc),
DEVMETHOD(bhnd_bus_release_ext_rsrc, siba_release_ext_rsrc),
DEVMETHOD(bhnd_bus_get_clock_freq, siba_get_clock_freq),
DEVMETHOD(bhnd_bus_get_clock_latency, siba_get_clock_latency),
DEVMETHOD(bhnd_bus_read_ioctl, siba_read_ioctl),
DEVMETHOD(bhnd_bus_write_ioctl, siba_write_ioctl),
DEVMETHOD(bhnd_bus_read_iost, siba_read_iost),

View File

@ -121,7 +121,7 @@ siba_bhndb_attach(device_t dev)
/* Perform initial attach and enumerate our children. */
if ((error = siba_attach(dev)))
goto failed;
return (error);
/* Fetch bus-level quirks required by the host bridge core */
if ((hostb = bhnd_bus_find_hostb_device(dev)) != NULL) {
@ -140,7 +140,7 @@ siba_bhndb_attach(device_t dev)
return (0);
failed:
device_delete_children(dev);
siba_detach(dev);
return (error);
}

View File

@ -137,6 +137,7 @@ siba_alloc_dinfo(device_t bus)
resource_list_init(&dinfo->resources);
dinfo->pmu_state = SIBA_PMU_NONE;
dinfo->intr_en = false;
return dinfo;

View File

@ -39,6 +39,8 @@
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <machine/bus.h>
#include <sys/rman.h>
@ -53,6 +55,7 @@ struct siba_addrspace;
struct siba_cfg_block;
struct siba_devinfo;
struct siba_core_id;
struct siba_softc;
int siba_probe(device_t dev);
int siba_attach(device_t dev);
@ -183,27 +186,48 @@ struct siba_core_id {
space */
};
/**
* siba(4) per-core PMU allocation state.
*/
typedef enum {
SIBA_PMU_NONE, /**< If the core has not yet allocated PMU state */
SIBA_PMU_BHND, /**< If standard bhnd(4) PMU support should be used */
SIBA_PMU_PWRCTL, /**< If legacy PWRCTL PMU support should be used */
} siba_pmu_state;
/**
* siba(4) per-device info
*/
struct siba_devinfo {
struct resource_list resources; /**< per-core memory regions. */
struct siba_core_id core_id; /**< core identification info */
struct siba_addrspace addrspace[SIBA_MAX_ADDRSPACE]; /**< memory map descriptors */
struct siba_cfg_block cfg[SIBA_MAX_CFG]; /**< config block descriptors */
struct siba_intr intr; /**< interrupt flag descriptor, if any */
bool intr_en; /**< if true, core has an assigned interrupt flag */
struct resource_list resources; /**< per-core memory regions. */
struct siba_core_id core_id; /**< core identification info */
struct siba_addrspace addrspace[SIBA_MAX_ADDRSPACE]; /**< memory map descriptors */
struct siba_cfg_block cfg[SIBA_MAX_CFG]; /**< config block descriptors */
struct siba_intr intr; /**< interrupt flag descriptor, if any */
bool intr_en; /**< if true, core has an assigned interrupt flag */
struct bhnd_resource *cfg_res[SIBA_MAX_CFG]; /**< bus-mapped config block registers */
int cfg_rid[SIBA_MAX_CFG]; /**< bus-mapped config block resource IDs */
struct bhnd_core_pmu_info *pmu_info; /**< Bus-managed PMU state, or NULL */
struct bhnd_resource *cfg_res[SIBA_MAX_CFG]; /**< bus-mapped config block registers */
int cfg_rid[SIBA_MAX_CFG]; /**< bus-mapped config block resource IDs */
siba_pmu_state pmu_state; /**< per-core PMU state */
union {
void *bhnd_info; /**< if SIBA_PMU_BHND, bhnd(4)-managed per-core PMU info. */
device_t pwrctl; /**< if SIBA_PMU_PWRCTL, legacy PWRCTL provider. */
} pmu;
};
/** siba(4) per-instance state */
struct siba_softc {
struct bhnd_softc bhnd_sc; /**< bhnd state */
device_t dev; /**< siba device */
struct bhnd_softc bhnd_sc; /**< bhnd state */
device_t dev; /**< siba device */
struct mtx mtx; /**< state mutex */
};
#define SIBA_LOCK_INIT(sc) \
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), NULL, MTX_DEF)
#define SIBA_LOCK(sc) mtx_lock(&(sc)->mtx)
#define SIBA_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
#define SIBA_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what)
#define SIBA_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx)
#endif /* _SIBA_SIBAVAR_H_ */

View File

@ -80,7 +80,7 @@ siba_nexus_attach(device_t dev)
/* Perform initial attach and enumerate our children. */
if ((error = siba_attach(dev)))
goto failed;
return (error);
/* Delegate remainder to standard bhnd method implementation */
if ((error = bhnd_generic_attach(dev)))
@ -89,7 +89,7 @@ siba_nexus_attach(device_t dev)
return (0);
failed:
device_delete_children(dev);
siba_detach(dev);
return (error);
}

View File

@ -16,8 +16,10 @@ SRCS+= bhnd_erom_if.c bhnd_erom_if.h
# ChipCommon
SRCS+= chipc.c chipc_subr.c
SRCS+= bhnd_sprom_chipc.c \
bhnd_pmu_chipc.c \
bhnd_pwrctl.c bhnd_pwrctl_subr.c
bhnd_pmu_chipc.c
SRCS+= bhnd_pwrctl.c bhnd_pwrctl_subr.c \
bhnd_pwrctl_if.c bhnd_pwrctl_if.h \
bhnd_pwrctl_hostb_if.c bhnd_pwrctl_hostb_if.h
SRCS+= bhnd_chipc_if.c bhnd_chipc_if.h
# PMU

View File

@ -10,6 +10,7 @@ SRCS= bhndb.c bhndb_subr.c bhndb_hwdata.c \
SRCS+= bhnd_bus_if.h \
bhnd_chipc_if.h \
bhnd_erom_if.h \
bhnd_pwrctl_hostb_if.h \
bhnd_nvram_if.h
SRCS+= device_if.h bus_if.h pci_if.h

View File

@ -8,6 +8,7 @@ SRCS= bhndb_pci.c bhndb_pci_hwdata.c \
SRCS+= bhnd_bus_if.h bhndb_bus_if.h bhndb_if.h
SRCS+= bhnd_erom_if.h
SRCS+= bhnd_nvram_if.h
SRCS+= bhnd_pwrctl_hostb_if.h
SRCS+= device_if.h bus_if.h pci_if.h

View File

@ -7,6 +7,7 @@ SRCS= siba.c siba_subr.c \
siba_erom.c
SRCS+= device_if.h bus_if.h
SRCS+= bhnd_bus_if.h bhnd_erom_if.h bhnd_pmu_if.h
SRCS+= bhnd_bus_if.h bhnd_erom_if.h \
bhnd_pmu_if.h bhnd_pwrctl_if.h
.include <bsd.kmod.mk>