Index: head/sys/conf/files =================================================================== --- head/sys/conf/files +++ head/sys/conf/files @@ -1250,6 +1250,8 @@ 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 Index: head/sys/dev/bhnd/bcma/bcma.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma.c +++ head/sys/dev/bhnd/bcma/bcma.c @@ -193,7 +193,7 @@ 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 @@ 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) @@ -381,12 +379,6 @@ /* Clear core flags */ 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); } Index: head/sys/dev/bhnd/bcma/bcmavar.h =================================================================== --- head/sys/dev/bhnd/bcma/bcmavar.h +++ head/sys/dev/bhnd/bcma/bcmavar.h @@ -175,17 +175,17 @@ * 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 */ }; Index: head/sys/dev/bhnd/bhnd.h =================================================================== --- head/sys/dev/bhnd/bhnd.h +++ head/sys/dev/bhnd/bhnd.h @@ -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(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 @@ -859,67 +857,6 @@ } /** - * If supported by the chipset, return the clock source for the given clock. - * - * This function is only supported on early PWRCTL-equipped chipsets - * that expose clock management via their host bridge interface. Currently, - * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. - * - * @param dev A bhnd bus child device. - * @param clock The clock for which a clock source will be returned. - * - * @retval bhnd_clksrc The clock source for @p clock. - * @retval BHND_CLKSRC_UNKNOWN If @p clock is unsupported, or its - * clock source is not known to the bus. - */ -static inline bhnd_clksrc -bhnd_pwrctl_get_clksrc(device_t dev, bhnd_clock clock) -{ - return (BHND_BUS_PWRCTL_GET_CLKSRC(device_get_parent(dev), dev, clock)); -} - -/** - * If supported by the chipset, gate @p clock - * - * This function is only supported on early PWRCTL-equipped chipsets - * that expose clock management via their host bridge interface. Currently, - * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. - * - * @param dev A bhnd bus child device. - * @param clock The clock to be disabled. - * - * @retval 0 success - * @retval ENODEV If bus-level clock source management is not supported. - * @retval ENXIO If bus-level management of @p clock is not supported. - */ -static inline int -bhnd_pwrctl_gate_clock(device_t dev, bhnd_clock clock) -{ - return (BHND_BUS_PWRCTL_GATE_CLOCK(device_get_parent(dev), dev, clock)); -} - -/** - * If supported by the chipset, ungate @p clock - * - * This function is only supported on early PWRCTL-equipped chipsets - * that expose clock management via their host bridge interface. Currently, - * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. - * - * @param dev A bhnd bus child device. - * @param clock The clock to be enabled. - * - * @retval 0 success - * @retval ENODEV If bus-level clock source management is not supported. - * @retval ENXIO If bus-level management of @p clock is not supported. - */ -static inline int -bhnd_pwrctl_ungate_clock(device_t dev, bhnd_clock clock) -{ - return (BHND_BUS_PWRCTL_UNGATE_CLOCK(device_get_parent(dev), dev, - clock)); -} - -/** * Return the BHND attachment type of the parent bhnd bus. * * @param dev A bhnd bus child device. @@ -1066,8 +1003,7 @@ * 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 @@ * 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 @@ -1095,6 +1030,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)); } /** Index: head/sys/dev/bhnd/bhnd.c =================================================================== --- head/sys/dev/bhnd/bhnd.c +++ head/sys/dev/bhnd/bhnd.c @@ -62,15 +62,13 @@ #include #include -#include - #include -#include #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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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<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<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_ */ Index: head/sys/dev/bhnd/bhnd_subr.c =================================================================== --- head/sys/dev/bhnd/bhnd_subr.c +++ head/sys/dev/bhnd/bhnd_subr.c @@ -171,7 +171,35 @@ { 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. * * @param vendor A JEP106 Manufacturer ID, including the non-standard ARM 4-bit @@ -1178,6 +1206,119 @@ /* Clean up */ bus_release_resource(dev, rtype, rid, res); 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); } /** @@ -2351,4 +2492,4 @@ bhnd_bus_generic_get_intr_domain(device_t dev, device_t child, bool self) { return ((uintptr_t)dev); -} \ No newline at end of file +} Index: head/sys/dev/bhnd/bhnd_types.h =================================================================== --- head/sys/dev/bhnd/bhnd_types.h +++ head/sys/dev/bhnd/bhnd_types.h @@ -72,6 +72,7 @@ /** 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 */ Index: head/sys/dev/bhnd/bhndb/bhnd_bhndb.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhnd_bhndb.c +++ head/sys/dev/bhnd/bhndb/bhnd_bhndb.c @@ -42,6 +42,8 @@ #include #include +#include "bhnd_pwrctl_hostb_if.h" + #include "bhndbvar.h" /* @@ -116,7 +118,7 @@ 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_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_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 @@ 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 }; Index: head/sys/dev/bhnd/bhndb/bhndb_pci.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_pci.c +++ head/sys/dev/bhnd/bhndb/bhndb_pci.c @@ -68,6 +68,8 @@ #include +#include "bhnd_pwrctl_hostb_if.h" + #include "bhndb_pcireg.h" #include "bhndb_pcivar.h" #include "bhndb_private.h" @@ -1136,11 +1138,11 @@ /* 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 @@ 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), + 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), - /* 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 }; Index: head/sys/dev/bhnd/bhndreg.h =================================================================== --- head/sys/dev/bhnd/bhndreg.h +++ head/sys/dev/bhnd/bhndreg.h @@ -1,31 +1,24 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller + * 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$ */ @@ -48,5 +41,51 @@ * The standard size of the siba(4) and bcma(4) enumeration space. */ #define BHND_DEFAULT_ENUM_SIZE 0x00100000 + +/* + * 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_ */ \ No newline at end of file +#endif /* _BHND_BHNDREG_H_ */ Index: head/sys/dev/bhnd/bhndvar.h =================================================================== --- head/sys/dev/bhnd/bhndvar.h +++ head/sys/dev/bhnd/bhndvar.h @@ -38,10 +38,7 @@ #include #include -#include #include -#include -#include #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 @@ 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, Index: head/sys/dev/bhnd/cores/chipc/chipc.c =================================================================== --- head/sys/dev/bhnd/cores/chipc/chipc.c +++ head/sys/dev/bhnd/cores/chipc/chipc.c @@ -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 @@ 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,10 +292,16 @@ * 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); } } Index: head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.h =================================================================== --- head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.h +++ head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.h @@ -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 +#include + +#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_ */ Index: head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c =================================================================== --- head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c +++ head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c @@ -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 @@ #include #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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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); Index: head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_hostb_if.m =================================================================== --- head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_hostb_if.m +++ head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_hostb_if.m @@ -0,0 +1,129 @@ +#- +# Copyright (c) 2016 Landon Fuller +# 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 +#include + +#include + +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 +}; + +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; \ No newline at end of file Index: head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_if.m =================================================================== --- head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_if.m +++ head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_if.m @@ -0,0 +1,98 @@ +#- +# Copyright (c) 2016 Landon Fuller +# 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 +#include + +#include + +INTERFACE bhnd_pwrctl; + +# +# bhnd(4) PWRCTL interface. +# + +HEADER { + #include +}; + +/** + * 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; +}; Index: head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_private.h =================================================================== --- head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_private.h +++ head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_private.h @@ -1,7 +1,11 @@ /*- * Copyright (c) 2016 Landon Fuller + * 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_ */ Index: head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_subr.c =================================================================== --- head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_subr.c +++ head/sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_subr.c @@ -333,7 +333,8 @@ /* 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 @@ /* 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 @@ 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 @@ /* 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)) { Index: head/sys/dev/bhnd/cores/pmu/bhnd_pmu.h =================================================================== --- head/sys/dev/bhnd/cores/pmu/bhnd_pmu.h +++ head/sys/dev/bhnd/cores/pmu/bhnd_pmu.h @@ -1,7 +1,11 @@ /*- * Copyright (c) 2016 Landon Fuller + * 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 #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_ */ Index: head/sys/dev/bhnd/cores/pmu/bhnd_pmu.c =================================================================== --- head/sys/dev/bhnd/cores/pmu/bhnd_pmu.c +++ head/sys/dev/bhnd/cores/pmu/bhnd_pmu.c @@ -47,7 +47,8 @@ #include #include -#include +#include +#include #include #include "bhnd_nvram_map.h" @@ -84,20 +85,6 @@ .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 @@ sc = device_get_softc(dev); sc->dev = dev; - sc->quirks = 0; sc->res = res; /* Fetch capability flags */ @@ -147,6 +133,17 @@ 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 @@ } /* 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 @@ 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 @@ 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 @@ 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 @@ 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; + struct bhnd_pmu_softc *sc; + uint32_t rval; - BPMU_ASSERT_CLKCTL_AVAIL(pinfo); - sc = device_get_softc(dev); - avail = 0x0; - req = 0x0; + BPMU_LOCK(sc); + rval = BHND_PMU_CCTRL_READ(sc, reg); + BPMU_UNLOCK(sc); - 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); - } + 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); +} - /* Issue request */ - BPMU_CLKCTL_SET_4(pinfo, req, BHND_CCS_FORCE_MASK); +/** + * 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; - /* Wait for clock availability */ - bhnd_pmu_wait_clkst(sc, pinfo->pm_dev, pinfo->pm_res, pinfo->pm_regs, - avail, avail); + sc = device_get_softc(dev); + BPMU_LOCK(sc); + rval = BHND_PMU_REGCTRL_READ(sc, reg); BPMU_UNLOCK(sc); - return (0); + return (rval); } -static int -bhnd_pmu_core_en_clocks(device_t dev, struct bhnd_core_pmu_info *pinfo, - uint32_t clocks) +/** + * 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; - uint32_t avail; - uint32_t req; + struct bhnd_pmu_softc *sc = device_get_softc(dev); - BPMU_ASSERT_CLKCTL_AVAIL(pinfo); + 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); - avail = 0x0; - req = 0x0; + BPMU_LOCK(sc); + rval = BHND_PMU_PLL_READ(sc, reg); + BPMU_UNLOCK(sc); - /* Build clock request flags */ - if (clocks & BHND_CLOCK_DYN) /* nothing to enable */ - clocks &= ~BHND_CLOCK_DYN; + return (rval); +} - if (clocks & BHND_CLOCK_ILP) /* nothing to enable */ - clocks &= ~BHND_CLOCK_ILP; +/** + * 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); - if (clocks & BHND_CLOCK_ALP) { - req |= BHND_CCS_ALPAREQ; - avail |= BHND_CCS_ALPAVAIL; - clocks &= ~BHND_CLOCK_ALP; - } + BPMU_LOCK(sc); + BHND_PMU_PLL_WRITE(sc, reg, value, mask); + BPMU_UNLOCK(sc); +} - if (clocks & BHND_CLOCK_HT) { - req |= BHND_CCS_HTAREQ; - avail |= BHND_CCS_HTAVAIL; - clocks &= ~BHND_CLOCK_HT; - } +/** + * 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; - /* Check for unknown clock values */ - if (clocks != 0x0) { - device_printf(dev, "%s requested unknown clocks: %#x\n", - device_get_nameunit(pinfo->pm_dev), clocks); + 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); } +} - BPMU_LOCK(sc); +/** + * 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; - /* Issue request */ - BPMU_CLKCTL_SET_4(pinfo, req, BHND_CCS_AREQ_MASK); + sc = device_get_softc(dev); - /* Wait for clock availability */ - bhnd_pmu_wait_clkst(sc, pinfo->pm_dev, pinfo->pm_res, pinfo->pm_regs, - avail, avail); + switch (regulator) { + case BHND_REGULATOR_PAREF_LDO: + BPMU_LOCK(sc); + error = bhnd_pmu_paref_ldo_enable(sc, true); + BPMU_UNLOCK(sc); - BPMU_UNLOCK(sc); + return (error); - return (0); + default: + return (ENODEV); + } } +/** + * Default bhnd_pmu driver implementation of BHND_PMU_DISABLE_REGULATOR(). + */ static int -bhnd_pmu_core_req_ext_rsrc(device_t dev, struct bhnd_core_pmu_info *pinfo, - u_int rsrc) +bhnd_pmu_disable_regulator_method(device_t dev, bhnd_pmu_regulator regulator) { struct bhnd_pmu_softc *sc; - uint32_t req; - uint32_t avail; + int error; - BPMU_ASSERT_CLKCTL_AVAIL(pinfo); - sc = device_get_softc(dev); - if (rsrc > BHND_CCS_ERSRC_MAX) - return (EINVAL); + switch (regulator) { + case BHND_REGULATOR_PAREF_LDO: + BPMU_LOCK(sc); + error = bhnd_pmu_paref_ldo_enable(sc, false); + BPMU_UNLOCK(sc); - req = BHND_PMU_SET_BITS((1<pm_dev, pinfo->pm_res, pinfo->pm_regs, - avail, avail); +/** + * 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; - BPMU_UNLOCK(sc); + sc = device_get_softc(dev); - return (0); + switch (clock) { + case BHND_CLOCK_HT: + BPMU_LOCK(sc); + error = bhnd_pmu_fast_pwrup_delay(sc, &pwrup_delay); + BPMU_UNLOCK(sc); + + if (error) + return (error); + + *latency = pwrup_delay; + return (0); + + default: + return (ENODEV); + } } +/** + * Default bhnd_pmu driver implementation of BHND_PMU_GET_CLOCK_FREQ(). + */ static int -bhnd_pmu_core_release_ext_rsrc(device_t dev, struct bhnd_core_pmu_info *pinfo, - u_int rsrc) +bhnd_pmu_get_clock_freq_method(device_t dev, bhnd_clock clock, uint32_t *freq) { - struct bhnd_pmu_softc *sc; - uint32_t mask; + struct bhnd_pmu_softc *sc = device_get_softc(dev); - BPMU_ASSERT_CLKCTL_AVAIL(pinfo); + BPMU_LOCK(sc); + switch (clock) { + case BHND_CLOCK_HT: + *freq = bhnd_pmu_si_clock(&sc->query); + break; - sc = device_get_softc(dev); + case BHND_CLOCK_ALP: + *freq = bhnd_pmu_alp_clock(&sc->query); + break; - if (rsrc > BHND_CCS_ERSRC_MAX) - return (EINVAL); + case BHND_CLOCK_ILP: + *freq = bhnd_pmu_ilp_clock(&sc->query); + break; - mask = BHND_PMU_SET_BITS((1<pm_dev)) - return (0); - BPMU_LOCK(sc); - - /* 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); - + error = bhnd_pmu_set_spuravoid(sc, spuravoid); BPMU_UNLOCK(sc); - return (0); + 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 @@ 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 @@ 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 @@ 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 }; Index: head/sys/dev/bhnd/cores/pmu/bhnd_pmu_core.c =================================================================== --- head/sys/dev/bhnd/cores/pmu/bhnd_pmu_core.c +++ head/sys/dev/bhnd/cores/pmu/bhnd_pmu_core.c @@ -94,6 +94,14 @@ 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); Index: head/sys/dev/bhnd/cores/pmu/bhnd_pmu_if.m =================================================================== --- head/sys/dev/bhnd/cores/pmu/bhnd_pmu_if.m +++ head/sys/dev/bhnd/cores/pmu/bhnd_pmu_if.m @@ -1,7 +1,11 @@ #- # Copyright (c) 2016 Landon Fuller +# 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 @@ # # 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 + struct bhnd_core_pmu_info; } -/** - * Enabling routing of @p clock (or faster) to a requesting core. +CODE { + + static uint32_t + bhnd_pmu_null_read_chipctrl(device_t dev, uint32_t reg) + { + panic("bhnd_pmu_read_chipctrl unimplemented"); + } + + 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"); + } +} + +/** + * Return the current value of a PMU chipctrl register. * - * @param dev PMU device. - * @param pinfo PMU info for requesting core. - * @param clock Clock requested. + * @param dev A bhnd(4) PMU device. + * @param reg The PMU chipctrl register to be read. * - * @retval 0 success - * @retval ENODEV If an unsupported clock was requested. + * 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_clock { - device_t dev; - struct bhnd_core_pmu_info *pinfo; - bhnd_clock clock; -}; +METHOD uint32_t read_chipctrl { + device_t dev; + uint32_t reg; +} DEFAULT bhnd_pmu_null_read_chipctrl; +/** + * 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. + */ +METHOD void write_chipctrl { + device_t dev; + uint32_t reg; + uint32_t value; + uint32_t mask; +} DEFAULT bhnd_pmu_null_write_chipctrl; -/** - * Request that @p clocks be powered on behalf of a requesting core. +/** + * Return the current value of a PMU regulator control register. * - * 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. + * @param dev A bhnd(4) PMU device. + * @param reg The PMU regctrl register to be read. * - * Requests from multiple devices are aggregated by the PMU. + * Drivers should only use function for functionality that is not + * available via another bhnd_chipc() function. * - * @param dev PMU device. - * @param pinfo PMU info for requesting core. - * @param clocks Clocks requested. + * @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. * - * @retval 0 success - * @retval ENODEV If an unsupported clock was requested. + * @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 int core_en_clocks { - device_t dev; - struct bhnd_core_pmu_info *pinfo; - uint32_t clocks; -}; +METHOD void write_regctrl { + device_t dev; + uint32_t reg; + uint32_t value; + uint32_t mask; +} DEFAULT bhnd_pmu_null_write_regctrl; /** - * Power up a core-specific external resource. + * Return the current value of a PMU PLL control 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 pllctrl 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 pllctrl 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_pllctrl { + device_t dev; + uint32_t reg; +} DEFAULT bhnd_pmu_null_read_pllctrl; /** - * Power down a core-specific external resource. + * Write @p value with @p mask to a PMU PLL control 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 pllctrl 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_pllctrl { + device_t dev; + uint32_t reg; + uint32_t value; + uint32_t mask; +} DEFAULT bhnd_pmu_null_write_pllctrl; -/** - * Release all outstanding requests (clocks, resources, etc) associated with - * @p pinfo. +/** + * Set a hardware-specific output voltage register value for @p regulator. * - * @param dev PMU device. - * @param pinfo PMU info for requesting core. + * @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; }; Index: head/sys/dev/bhnd/cores/pmu/bhnd_pmu_private.h =================================================================== --- head/sys/dev/bhnd/cores/pmu/bhnd_pmu_private.h +++ head/sys/dev/bhnd/cores/pmu/bhnd_pmu_private.h @@ -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 @@ 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 @@ 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 @@ 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_ */ Index: head/sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c =================================================================== --- head/sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c +++ head/sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c @@ -25,7 +25,7 @@ #include -#include +#include #include #include @@ -84,16 +84,15 @@ 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 @@ 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_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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ break; } - *pwrup_delay = (uint16_t)delay; + *pwrup_delay = delay; return (0); } @@ -2005,10 +1959,10 @@ 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_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 @@ 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 @@ } /* 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_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,26 +2969,25 @@ } /* 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; + uint16_t chip_id; + uint32_t pmuctrl; + uint32_t tmp; - uint8_t bcm5357_bcm43236_p1div[] = { 0x1, 0x5, 0x5 }; - uint8_t bcm5357_bcm43236_ndiv[] = { 0x30, 0xf6, 0xfc }; - /* 6362a0 has same clks as 4322[4-6] */ chip_id = sc->cid.chip_id; if (chip_id == BHND_CHIPID_BCM6362 && sc->cid.chip_rev == 0) { @@ -3050,12 +3005,26 @@ 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 @@ 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) { + switch (spuravoid) { + case BHND_PMU_SPURAVOID_NONE: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, - 0x11500014, ~0); + 0x11100014, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, - 0x0FC00a08, ~0); - } else if (spuravoid == 1) { + 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); - } else { + break; + + case BHND_PMU_SPURAVOID_M2: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, - 0x11100014, ~0); + 0x11500014, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, - 0x03000a08, ~0); + 0x0FC00a08, ~0); + break; + + default: + return (ENODEV); } + pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD; break; @@ -3102,33 +3080,42 @@ case BHND_CHIPID_BCM43225: case BHND_CHIPID_BCM43226: case BHND_CHIPID_BCM43421: - if (spuravoid == 1) { + switch (spuravoid) { + case BHND_PMU_SPURAVOID_NONE: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, - 0x11500010, ~0); + 0x11100010, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, - 0x000C0C06, ~0); + 0x000c0c06, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, - 0x0F600a08, ~0); + 0x03000a08, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, 0x00000000, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, - 0x2001E920, ~0); + 0x200005c0, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, 0x88888815, ~0); - } else { + break; + + case BHND_PMU_SPURAVOID_M1: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, - 0x11100010, ~0); + 0x11500010, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, - 0x000c0c06, ~0); + 0x000C0C06, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, - 0x03000a08, ~0); + 0x0F600a08, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, 0x00000000, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, - 0x200005c0, ~0); + 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,32 +3123,40 @@ case BHND_CHIPID_BCM43112: case BHND_CHIPID_BCM43222: case BHND_CHIPID_BCM43420: - if (spuravoid == 1) { + switch (spuravoid) { + case BHND_PMU_SPURAVOID_NONE: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, - 0x11500008, ~0); + 0x11100008, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, 0x0c000c06, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, - 0x0f600a08, ~0); + 0x03000a08, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, 0x00000000, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, - 0x2001e920, ~0); + 0x200005c0, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, - 0x88888815, ~0); - } else { + 0x88888855, ~0); + break; + + case BHND_PMU_SPURAVOID_M1: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, - 0x11100008, ~0); + 0x11500008, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, 0x0c000c06, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, - 0x03000a08, ~0); + 0x0f600a08, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, 0x00000000, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, - 0x200005c0, ~0); + 0x2001e920, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, - 0x88888855, ~0); + 0x88888815, ~0); + break; + + case BHND_PMU_SPURAVOID_M2: + default: + return (ENODEV); } pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD; @@ -3170,34 +3165,43 @@ case BHND_CHIPID_BCM4716: case BHND_CHIPID_BCM4748: case BHND_CHIPID_BCM47162: - if (spuravoid == 1) { + switch (spuravoid) { + case BHND_PMU_SPURAVOID_NONE: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, - 0x11500060, ~0); + 0x11100060, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, - 0x080C0C06, ~0); + 0x080c0c06, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, - 0x0F600000, ~0); + 0x03000000, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, 0x00000000, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, - 0x2001E924, ~0); + 0x200005c0, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, 0x88888815, ~0); - } else { + break; + + case BHND_PMU_SPURAVOID_M1: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, - 0x11100060, ~0); + 0x11500060, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, - 0x080c0c06, ~0); + 0x080C0C06, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, - 0x03000000, ~0); + 0x0F600000, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, 0x00000000, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, - 0x200005c0, ~0); + 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_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_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,44 +3273,55 @@ /* LCNXN */ /* PLL Settings for spur avoidance on/off mode, no on2 support * for 43228A0 */ - if (spuravoid == 1) { + switch (spuravoid) { + case BHND_PMU_SPURAVOID_NONE: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, - 0x01100014, ~0); + 0x11100014, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, - 0x040C0C06, ~0); + 0x040c0c06, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, - 0x03140A08, ~0); + 0x03000a08, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, - 0x00333333, ~0); + 0x00000000, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, - 0x202C2820, ~0); + 0x200005c0, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, 0x88888815, ~0); - } else { + break; + + case BHND_PMU_SPURAVOID_M1: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, - 0x11100014, ~0); + 0x01100014, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, - 0x040c0c06, ~0); + 0x040C0C06, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, - 0x03000a08, ~0); + 0x03140A08, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, - 0x00000000, ~0); + 0x00333333, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, - 0x200005c0, ~0); + 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 @@ return (true); } -void +int bhnd_pmu_paref_ldo_enable(struct bhnd_pmu_softc *sc, bool enable) { uint32_t ldo; @@ -3349,7 +3382,7 @@ ldo = PMURES_BIT(RES4312_PA_REF_LDO); break; default: - return; + return (ENODEV); } if (enable) { @@ -3357,6 +3390,8 @@ } else { BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~ldo); } + + return (0); } /* initialize PMU switch/regulators */ Index: head/sys/dev/bhnd/cores/pmu/bhnd_pmu_types.h =================================================================== --- head/sys/dev/bhnd/cores/pmu/bhnd_pmu_types.h +++ head/sys/dev/bhnd/cores/pmu/bhnd_pmu_types.h @@ -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 + +/** + * 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_ */ Index: head/sys/dev/bhnd/cores/pmu/bhnd_pmureg.h =================================================================== --- head/sys/dev/bhnd/cores/pmu/bhnd_pmureg.h +++ head/sys/dev/bhnd/cores/pmu/bhnd_pmureg.h @@ -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 Index: head/sys/dev/bhnd/cores/pmu/bhnd_pmuvar.h =================================================================== --- head/sys/dev/bhnd/cores/pmu/bhnd_pmuvar.h +++ head/sys/dev/bhnd/cores/pmu/bhnd_pmuvar.h @@ -60,20 +60,6 @@ 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_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_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 @@ }; #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_ */ Index: head/sys/dev/bhnd/siba/siba.c =================================================================== --- head/sys/dev/bhnd/siba/siba.c +++ head/sys/dev/bhnd/siba/siba.c @@ -39,12 +39,12 @@ #include #include #include +#include #include #include -#include -#include +#include #include "sibareg.h" #include "sibavar.h" @@ -78,9 +78,12 @@ 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 @@ 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 @@ 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 @@ *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 @@ 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 @@ 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,7 +231,333 @@ 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) { uint32_t tmhigh; @@ -328,8 +706,8 @@ 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 @@ 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 @@ 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))) + /* + * 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); + + if (error) { + device_printf(child, "failed to release clock request: " + "%d", error); return (error); - } + } - return (0); + return (0); + } else { + SIBA_UNLOCK(sc); + return (0); + } } static int @@ -1061,6 +1453,14 @@ /* 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), Index: head/sys/dev/bhnd/siba/siba_bhndb.c =================================================================== --- head/sys/dev/bhnd/siba/siba_bhndb.c +++ head/sys/dev/bhnd/siba/siba_bhndb.c @@ -121,7 +121,7 @@ /* 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 @@ return (0); failed: - device_delete_children(dev); + siba_detach(dev); return (error); } Index: head/sys/dev/bhnd/siba/siba_subr.c =================================================================== --- head/sys/dev/bhnd/siba/siba_subr.c +++ head/sys/dev/bhnd/siba/siba_subr.c @@ -137,6 +137,7 @@ resource_list_init(&dinfo->resources); + dinfo->pmu_state = SIBA_PMU_NONE; dinfo->intr_en = false; return dinfo; Index: head/sys/dev/bhnd/siba/sibavar.h =================================================================== --- head/sys/dev/bhnd/siba/sibavar.h +++ head/sys/dev/bhnd/siba/sibavar.h @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include @@ -53,6 +55,7 @@ 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); @@ -184,26 +187,47 @@ }; /** + * 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_ */ Index: head/sys/mips/broadcom/siba_nexus.c =================================================================== --- head/sys/mips/broadcom/siba_nexus.c +++ head/sys/mips/broadcom/siba_nexus.c @@ -80,7 +80,7 @@ /* 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 @@ return (0); failed: - device_delete_children(dev); + siba_detach(dev); return (error); } Index: head/sys/modules/bhnd/Makefile =================================================================== --- head/sys/modules/bhnd/Makefile +++ head/sys/modules/bhnd/Makefile @@ -16,8 +16,10 @@ # 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 Index: head/sys/modules/bhnd/bhndb/Makefile =================================================================== --- head/sys/modules/bhnd/bhndb/Makefile +++ head/sys/modules/bhnd/bhndb/Makefile @@ -10,6 +10,7 @@ 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 Index: head/sys/modules/bhnd/bhndb_pci/Makefile =================================================================== --- head/sys/modules/bhnd/bhndb_pci/Makefile +++ head/sys/modules/bhnd/bhndb_pci/Makefile @@ -8,6 +8,7 @@ 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 Index: head/sys/modules/bhnd/siba/Makefile =================================================================== --- head/sys/modules/bhnd/siba/Makefile +++ head/sys/modules/bhnd/siba/Makefile @@ -7,6 +7,7 @@ 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