Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -1150,19 +1150,26 @@ dev/bhnd/bcma/bcma_erom.c optional bcma bhnd dev/bhnd/bcma/bcma_nexus.c optional bcma_nexus bcma bhnd dev/bhnd/bcma/bcma_subr.c optional bcma bhnd +dev/bhnd/cores/chipc/bhnd_chipc_if.m optional bhnd +dev/bhnd/cores/chipc/bhnd_sprom_chipc.c optional bhnd +dev/bhnd/cores/chipc/bhnd_pmu_chipc.c optional bhnd dev/bhnd/cores/chipc/chipc.c optional bhnd dev/bhnd/cores/chipc/chipc_cfi.c optional bhnd cfi dev/bhnd/cores/chipc/chipc_slicer.c optional bhnd cfi | bhnd spibus dev/bhnd/cores/chipc/chipc_spi.c optional bhnd spibus dev/bhnd/cores/chipc/chipc_subr.c optional bhnd -dev/bhnd/cores/chipc/bhnd_chipc_if.m optional bhnd -dev/bhnd/cores/chipc/bhnd_sprom_chipc.c optional bhnd +dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c optional bhnd +dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_subr.c optional bhnd dev/bhnd/cores/pci/bhnd_pci.c optional bhnd pci dev/bhnd/cores/pci/bhnd_pci_hostb.c optional bhndb bhnd pci dev/bhnd/cores/pci/bhnd_pcib.c optional bhnd_pcib bhnd pci dev/bhnd/cores/pcie2/bhnd_pcie2.c optional bhnd pci dev/bhnd/cores/pcie2/bhnd_pcie2_hostb.c optional bhndb bhnd pci dev/bhnd/cores/pcie2/bhnd_pcie2b.c optional bhnd_pcie2b bhnd pci +dev/bhnd/cores/pmu/bhnd_pmu.c optional bhnd +dev/bhnd/cores/pmu/bhnd_pmu_core.c optional bhnd +dev/bhnd/cores/pmu/bhnd_pmu_if.m optional bhnd +dev/bhnd/cores/pmu/bhnd_pmu_subr.c optional bhnd dev/bhnd/nvram/bhnd_nvram.c optional bhnd dev/bhnd/nvram/bhnd_nvram_common.c optional bhnd dev/bhnd/nvram/bhnd_nvram_cfe.c optional bhnd siba_nexus cfe | \ Index: sys/dev/bhnd/bcma/bcma.c =================================================================== --- sys/dev/bhnd/bcma/bcma.c +++ sys/dev/bhnd/bcma/bcma.c @@ -259,6 +259,78 @@ return (ENXIO); } +static uint32_t +bcma_read_config(device_t dev, device_t child, bus_size_t offset, u_int width) +{ + struct bcma_devinfo *dinfo; + struct bhnd_resource *r; + + /* Must be a directly attached child core */ + if (device_get_parent(child) != dev) + return (UINT32_MAX); + + /* Fetch the agent registers */ + dinfo = device_get_ivars(child); + if ((r = dinfo->res_agent) == NULL) + return (UINT32_MAX); + + /* Verify bounds */ + if (offset > rman_get_size(r->res)) + return (UINT32_MAX); + + if (rman_get_size(r->res) - offset < width) + return (UINT32_MAX); + + switch (width) { + case 1: + return (bhnd_bus_read_1(r, offset)); + case 2: + return (bhnd_bus_read_2(r, offset)); + case 4: + return (bhnd_bus_read_4(r, offset)); + default: + return (UINT32_MAX); + } +} + +static void +bcma_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val, + u_int width) +{ + struct bcma_devinfo *dinfo; + struct bhnd_resource *r; + + /* Must be a directly attached child core */ + if (device_get_parent(child) != dev) + return; + + /* Fetch the agent registers */ + dinfo = device_get_ivars(child); + if ((r = dinfo->res_agent) == NULL) + return; + + /* Verify bounds */ + if (offset > rman_get_size(r->res)) + return; + + if (rman_get_size(r->res) - offset < width) + return; + + switch (width) { + case 1: + bhnd_bus_write_1(r, offset, val); + break; + case 2: + bhnd_bus_write_2(r, offset, val); + break; + case 4: + bhnd_bus_write_4(r, offset, val); + break; + default: + break; + } +} + static u_int bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type) { @@ -473,6 +545,9 @@ * unpopulated, the device shouldn't be used. */ if (bhnd_is_hw_disabled(child)) device_disable(child); + + /* Issue bus callback for fully initialized child. */ + BHND_BUS_CHILD_ADDED(bus, child); } /* Hit EOF parsing cores? */ @@ -504,6 +579,8 @@ DEVMETHOD(bhnd_bus_free_devinfo, bcma_free_bhnd_dinfo), DEVMETHOD(bhnd_bus_reset_core, bcma_reset_core), DEVMETHOD(bhnd_bus_suspend_core, bcma_suspend_core), + DEVMETHOD(bhnd_bus_read_config, bcma_read_config), + DEVMETHOD(bhnd_bus_write_config, bcma_write_config), DEVMETHOD(bhnd_bus_get_port_count, bcma_get_port_count), DEVMETHOD(bhnd_bus_get_region_count, bcma_get_region_count), DEVMETHOD(bhnd_bus_get_port_rid, bcma_get_port_rid), Index: sys/dev/bhnd/bcma/bcma_dmp.h =================================================================== --- sys/dev/bhnd/bcma/bcma_dmp.h +++ sys/dev/bhnd/bcma/bcma_dmp.h @@ -109,6 +109,19 @@ #define BCMA_DMP_OOBDINWIDTH 0x364 #define BCMA_DMP_OOBDOUTWIDTH 0x368 +/* The exact interpretation of these bits is unverified; these + * are our best guesses as to their use */ +#define BCMA_DMP_OOBSEL_MASK 0xFF /**< OOBSEL config mask */ +#define BCMA_DMP_OOBSEL_0_MASK BCMA_DMP_OOBSEL_MASK +#define BCMA_DMP_OOBSEL_1_MASK BCMA_DMP_OOBSEL_MASK +#define BCMA_DMP_OOBSEL_2_MASK BCMA_DMP_OOBSEL_MASK +#define BCMA_DMP_OOBSEL_3_MASK BCMA_DMP_OOBSEL_MASK +#define BCMA_DMP_OOBSEL_0_SHIFT 0 /**< first OOBSEL config */ +#define BCMA_DMP_OOBSEL_1_SHIFT 8 /**< second OOBSEL config */ +#define BCMA_DMP_OOBSEL_2_SHIFT 16 /**< third OOBSEL config */ +#define BCMA_DMP_OOBSEL_3_SHIFT 24 /**< fouth OOBSEL config */ +#define BCMA_DMP_OOBSEL_EN (1 << 7) /**< enable bit */ + // This was inherited from Broadcom's aidmp.h header // Is it required for any of our use-cases? #if 0 /* defined(IL_BIGENDIAN) && defined(BCMHND74K) */ Index: sys/dev/bhnd/bhnd.h =================================================================== --- sys/dev/bhnd/bhnd.h +++ sys/dev/bhnd/bhnd.h @@ -417,6 +417,67 @@ }; /** + * If supported by the chipset, return the clock source for the given clock. + * + * This function is only supported on early PWRCTL-equipped chipsets + * that expose clock management via their host bridge interface. Currently, + * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. + * + * @param dev A bhnd bus child device. + * @param clock The clock for which a clock source will be returned. + * + * @retval bhnd_clksrc The clock source for @p clock. + * @retval BHND_CLKSRC_UNKNOWN If @p clock is unsupported, or its + * clock source is not known to the bus. + */ +static inline bhnd_clksrc +bhnd_pwrctl_get_clksrc(device_t dev, bhnd_clock clock) +{ + return (BHND_BUS_PWRCTL_GET_CLKSRC(device_get_parent(dev), dev, clock)); +} + +/** + * If supported by the chipset, gate @p clock + * + * This function is only supported on early PWRCTL-equipped chipsets + * that expose clock management via their host bridge interface. Currently, + * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. + * + * @param dev A bhnd bus child device. + * @param clock The clock to be disabled. + * + * @retval 0 success + * @retval ENODEV If bus-level clock source management is not supported. + * @retval ENXIO If bus-level management of @p clock is not supported. + */ +static inline int +bhnd_pwrctl_gate_clock(device_t dev, bhnd_clock clock) +{ + return (BHND_BUS_PWRCTL_GATE_CLOCK(device_get_parent(dev), dev, clock)); +} + +/** + * If supported by the chipset, ungate @p clock + * + * This function is only supported on early PWRCTL-equipped chipsets + * that expose clock management via their host bridge interface. Currently, + * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. + * + * @param dev A bhnd bus child device. + * @param clock The clock to be enabled. + * + * @retval 0 success + * @retval ENODEV If bus-level clock source management is not supported. + * @retval ENXIO If bus-level management of @p clock is not supported. + */ +static inline int +bhnd_pwrctl_ungate_clock(device_t dev, bhnd_clock clock) +{ + return (BHND_BUS_PWRCTL_UNGATE_CLOCK(device_get_parent(dev), dev, + clock)); +} + +/** * Return the BHND attachment type of the parent bhnd bus. * * @param dev A bhnd bus child device. @@ -454,6 +515,171 @@ } /** + * Allocate and enable per-core PMU request handling for @p child. + * + * The region containing the core's PMU register block (if any) must be + * allocated via bus_alloc_resource(9) (or bhnd_alloc_resource) before + * calling bhnd_alloc_pmu(), and must not be released until after + * calling bhnd_release_pmu(). + * + * @param dev The parent of @p child. + * @param child The requesting bhnd device. + * + * @retval 0 success + * @retval non-zero If allocating PMU request state otherwise fails, a + * regular unix error code will be returned. + */ +static inline int +bhnd_alloc_pmu(device_t dev) +{ + return (BHND_BUS_ALLOC_PMU(device_get_parent(dev), dev)); +} + +/** + * Release any per-core PMU resources allocated for @p child. Any outstanding + * PMU requests are are discarded. + * + * @param dev The parent of @p child. + * @param child The requesting bhnd device. + * + * @retval 0 success + * @retval non-zero If releasing PMU request state otherwise fails, a + * regular unix error code will be returned, and + * the core state will be left unmodified. + */ +static inline int +bhnd_release_pmu(device_t dev) +{ + return (BHND_BUS_RELEASE_PMU(device_get_parent(dev), dev)); +} + +/** + * Request that @p clock (or faster) be routed to @p dev. + * + * A driver must ask the bhnd bus to allocate clock request state + * via bhnd_alloc_pmu() before it can request clock resources. + * + * Request multiplexing is managed by the bus. + * + * @param dev The bhnd(4) device to which @p clock should be routed. + * @param clock The requested clock source. + * + * @retval 0 success + * @retval ENODEV If an unsupported clock was requested. + * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. + */ +static inline int +bhnd_request_clock(device_t dev, bhnd_clock clock) +{ + return (BHND_BUS_REQUEST_CLOCK(device_get_parent(dev), dev, clock)); +} + +/** + * Request that @p clocks be powered on behalf of @p dev. + * + * This will power any clock sources (e.g. XTAL, PLL, etc) required for + * @p clocks and wait until they are ready, discarding any previous + * requests by @p dev. + * + * Request multiplexing is managed by the bus. + * + * A driver must ask the bhnd bus to allocate clock request state + * via bhnd_alloc_pmu() before it can request clock resources. + * + * @param dev The requesting bhnd(4) device. + * @param clocks The clock(s) to be enabled. + * + * @retval 0 success + * @retval ENODEV If an unsupported clock was requested. + * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. + */ +static inline int +bhnd_enable_clocks(device_t dev, uint32_t clocks) +{ + return (BHND_BUS_ENABLE_CLOCKS(device_get_parent(dev), dev, clocks)); +} + +/** + * Power up an external PMU-managed resource assigned to @p dev. + * + * A driver must ask the bhnd bus to allocate PMU request state + * via bhnd_alloc_pmu() before it can request PMU resources. + * + * @param dev The requesting bhnd(4) device. + * @param rsrc The core-specific external resource identifier. + * + * @retval 0 success + * @retval ENODEV If the PMU does not support @p rsrc. + * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. + */ +static inline int +bhnd_request_ext_rsrc(device_t dev, u_int rsrc) +{ + return (BHND_BUS_REQUEST_EXT_RSRC(device_get_parent(dev), dev, rsrc)); +} + +/** + * Power down an external PMU-managed resource assigned to @p dev. + * + * A driver must ask the bhnd bus to allocate PMU request state + * via bhnd_alloc_pmu() before it can request PMU resources. + * + * @param dev The requesting bhnd(4) device. + * @param rsrc The core-specific external resource identifier. + * + * @retval 0 success + * @retval ENODEV If the PMU does not support @p rsrc. + * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. + */ +static inline int +bhnd_release_ext_rsrc(device_t dev, u_int rsrc) +{ + return (BHND_BUS_RELEASE_EXT_RSRC(device_get_parent(dev), dev, rsrc)); +} + + +/** + * Read @p width bytes at @p offset from the bus-specific agent/config + * space of @p dev. + * + * @param dev The bhnd device for which @p offset should be read. + * @param offset The offset to be read. + * @param width The size of the access. Must be 1, 2 or 4 bytes. + * + * The exact behavior of this method is bus-specific. In the case of + * bcma(4), this method provides access to the first agent port of @p child. + * + * @note Device drivers should only use this API for functionality + * that is not available via another bhnd(4) function. + */ +static inline uint32_t +bhnd_read_config(device_t dev, bus_size_t offset, u_int width) +{ + return (BHND_BUS_READ_CONFIG(device_get_parent(dev), dev, offset, + width)); +} + +/** + * Read @p width bytes at @p offset from the bus-specific agent/config + * space of @p dev. + * + * @param dev The bhnd device for which @p offset should be read. + * @param offset The offset to be written. + * @param width The size of the access. Must be 1, 2 or 4 bytes. + * + * The exact behavior of this method is bus-specific. In the case of + * bcma(4), this method provides access to the first agent port of @p child. + * + * @note Device drivers should only use this API for functionality + * that is not available via another bhnd(4) function. + */ +static inline void +bhnd_write_config(device_t dev, bus_size_t offset, uint32_t val, u_int width) +{ + BHND_BUS_WRITE_CONFIG(device_get_parent(dev), dev, offset, val, width); +} + +/** * Read an NVRAM variable, coerced to the requested @p type. * * @param dev A bhnd bus child device. Index: sys/dev/bhnd/bhnd.c =================================================================== --- sys/dev/bhnd/bhnd.c +++ sys/dev/bhnd/bhnd.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -60,6 +60,9 @@ #include +#include +#include + #include "bhnd_chipc_if.h" #include "bhnd_nvram_if.h" @@ -342,7 +345,7 @@ { struct chipc_caps *ccaps; - GIANT_REQUIRED; /* newbus */ + GIANT_REQUIRED; /* for newbus */ KASSERT(bus_current_pass >= BHND_FINISH_ATTACH_PASS, ("bhnd_finish_attach() called in pass %d", bus_current_pass)); @@ -367,10 +370,11 @@ } /* Look for a PMU */ - if (ccaps->pmu) { + if (ccaps->pmu || ccaps->pwr_ctrl) { if ((sc->pmu_dev = bhnd_find_pmu(sc)) == NULL) { device_printf(sc->dev, - "warning: PMU device not found\n"); + "attach failed: supported PMU not found\n"); + return (ENXIO); } } @@ -478,8 +482,6 @@ static device_t bhnd_find_pmu(struct bhnd_softc *sc) { - struct chipc_caps *ccaps; - /* Make sure we're holding Giant for newbus */ GIANT_REQUIRED; @@ -494,11 +496,6 @@ return (sc->pmu_dev); } - if ((ccaps = bhnd_find_chipc_caps(sc)) == NULL) - return (NULL); - - if (!ccaps->pmu) - return (NULL); return (bhnd_find_platform_dev(sc, "bhnd_pmu")); } @@ -626,6 +623,244 @@ } /** + * Default bhnd(4) bus driver implementation of BHND_BUS_ALLOC_PMU(). + */ +int +bhnd_generic_alloc_pmu(device_t dev, device_t child) +{ + struct bhnd_softc *sc; + struct bhnd_resource *br; + struct chipc_caps *ccaps; + struct bhnd_devinfo *dinfo; + struct bhnd_core_pmu_info *pm; + struct resource_list *rl; + struct resource_list_entry *rle; + device_t pmu_dev; + bhnd_addr_t r_addr; + bhnd_size_t r_size; + bus_size_t pmu_regs; + int error; + + GIANT_REQUIRED; /* for newbus */ + + sc = device_get_softc(dev); + dinfo = device_get_ivars(child); + pmu_regs = BHND_CLK_CTL_ST; + + if ((ccaps = bhnd_find_chipc_caps(sc)) == NULL) { + device_printf(sc->dev, "alloc_pmu failed: chipc " + "capabilities unavailable\n"); + return (ENXIO); + } + + if ((pmu_dev = bhnd_find_pmu(sc)) == NULL) { + device_printf(sc->dev, + "pmu unavailable; cannot allocate request state\n"); + return (ENXIO); + } + + /* already allocated? */ + if (dinfo->pmu_info != NULL) { + panic("duplicate PMU allocation for %s", + device_get_nameunit(child)); + } + + /* Determine address+size of the core's PMU register block */ + error = bhnd_get_region_addr(child, BHND_PORT_DEVICE, 0, 0, &r_addr, + &r_size); + if (error) { + device_printf(sc->dev, "error fetching register block info for " + "%s: %d\n", device_get_nameunit(child), error); + return (error); + } + + if (r_size < (pmu_regs + sizeof(uint32_t))) { + device_printf(sc->dev, "pmu offset %#jx would overrun %s " + "register block\n", (uintmax_t)pmu_regs, + device_get_nameunit(child)); + return (ENODEV); + } + + /* Locate actual resource containing the core's register block */ + if ((rl = BUS_GET_RESOURCE_LIST(dev, child)) == NULL) { + device_printf(dev, "NULL resource list returned for %s\n", + device_get_nameunit(child)); + return (ENXIO); + } + + if ((rle = resource_list_find(rl, SYS_RES_MEMORY, 0)) == NULL) { + device_printf(dev, "cannot locate core register resource " + "for %s\n", device_get_nameunit(child)); + return (ENXIO); + } + + if (rle->res == NULL) { + device_printf(dev, "core register resource unallocated for " + "%s\n", device_get_nameunit(child)); + return (ENXIO); + } + + if (r_addr+pmu_regs < rman_get_start(rle->res) || + r_addr+pmu_regs >= rman_get_end(rle->res)) + { + device_printf(dev, "core register resource does not map PMU " + "registers at %#jx\n for %s\n", r_addr+pmu_regs, + device_get_nameunit(child)); + return (ENXIO); + } + + /* Adjust PMU register offset relative to the actual start address + * of the core's register block allocation. + * + * XXX: The saved offset will be invalid if bus_adjust_resource is + * used to modify the resource's start address. + */ + if (rman_get_start(rle->res) > r_addr) + pmu_regs -= rman_get_start(rle->res) - r_addr; + else + pmu_regs -= r_addr - rman_get_start(rle->res); + + /* Allocate and initialize PMU info */ + br = malloc(sizeof(struct bhnd_resource), M_BHND, M_NOWAIT); + if (br == NULL) + return (ENOMEM); + + br->res = rle->res; + br->direct = ((rman_get_flags(rle->res) & RF_ACTIVE) != 0); + + pm = malloc(sizeof(*dinfo->pmu_info), M_BHND, M_NOWAIT); + if (pm == NULL) { + free(br, M_BHND); + return (ENOMEM); + } + pm->pm_dev = child; + pm->pm_pmu = pmu_dev; + pm->pm_res = br; + pm->pm_regs = pmu_regs; + + dinfo->pmu_info = pm; + return (0); +} + +/** + * Default bhnd(4) bus driver implementation of BHND_BUS_RELEASE_PMU(). + */ +int +bhnd_generic_release_pmu(device_t dev, device_t child) +{ + struct bhnd_softc *sc; + struct bhnd_devinfo *dinfo; + device_t pmu; + int error; + + GIANT_REQUIRED; /* for newbus */ + + sc = device_get_softc(dev); + dinfo = device_get_ivars(child); + + if ((pmu = bhnd_find_pmu(sc)) == NULL) { + device_printf(sc->dev, + "pmu unavailable; cannot release request state\n"); + return (ENXIO); + } + + /* dispatch release request */ + if (dinfo->pmu_info == NULL) + panic("pmu over-release for %s", device_get_nameunit(child)); + + if ((error = BHND_PMU_CORE_RELEASE(pmu, dinfo->pmu_info))) + return (error); + + /* free PMU info */ + free(dinfo->pmu_info->pm_res, M_BHND); + free(dinfo->pmu_info, M_BHND); + dinfo->pmu_info = NULL; + + return (0); +} + +/** + * Default bhnd(4) bus driver implementation of BHND_BUS_REQUEST_CLOCK(). + */ +int +bhnd_generic_request_clock(device_t dev, device_t child, bhnd_clock clock) +{ + struct bhnd_softc *sc; + struct bhnd_devinfo *dinfo; + struct bhnd_core_pmu_info *pm; + + sc = device_get_softc(dev); + dinfo = device_get_ivars(child); + + if ((pm = dinfo->pmu_info) == NULL) + panic("no active PMU request state"); + + /* dispatch request to PMU */ + return (BHND_PMU_CORE_REQ_CLOCK(pm->pm_pmu, pm, clock)); +} + +/** + * Default bhnd(4) bus driver implementation of BHND_BUS_ENABLE_CLOCKS(). + */ +int +bhnd_generic_enable_clocks(device_t dev, device_t child, uint32_t clocks) +{ + struct bhnd_softc *sc; + struct bhnd_devinfo *dinfo; + struct bhnd_core_pmu_info *pm; + + sc = device_get_softc(dev); + dinfo = device_get_ivars(child); + + if ((pm = dinfo->pmu_info) == NULL) + panic("no active PMU request state"); + + /* dispatch request to PMU */ + return (BHND_PMU_CORE_EN_CLOCKS(pm->pm_pmu, pm, clocks)); +} + +/** + * Default bhnd(4) bus driver implementation of BHND_BUS_REQUEST_EXT_RSRC(). + */ +int +bhnd_generic_request_ext_rsrc(device_t dev, device_t child, u_int rsrc) +{ + struct bhnd_softc *sc; + struct bhnd_devinfo *dinfo; + struct bhnd_core_pmu_info *pm; + + sc = device_get_softc(dev); + dinfo = device_get_ivars(child); + + if ((pm = dinfo->pmu_info) == NULL) + panic("no active PMU request state"); + + /* dispatch request to PMU */ + return (BHND_PMU_CORE_REQ_EXT_RSRC(pm->pm_pmu, pm, rsrc)); +} + +/** + * Default bhnd(4) bus driver implementation of BHND_BUS_RELEASE_EXT_RSRC(). + */ +int +bhnd_generic_release_ext_rsrc(device_t dev, device_t child, u_int rsrc) +{ + struct bhnd_softc *sc; + struct bhnd_devinfo *dinfo; + struct bhnd_core_pmu_info *pm; + + sc = device_get_softc(dev); + dinfo = device_get_ivars(child); + + if ((pm = dinfo->pmu_info) == NULL) + panic("no active PMU request state"); + + /* dispatch request to PMU */ + return (BHND_PMU_CORE_RELEASE_EXT_RSRC(pm->pm_pmu, pm, rsrc)); +} + + +/** * Default bhnd(4) bus driver implementation of BHND_BUS_IS_REGION_VALID(). * * This implementation assumes that port and region numbers are 0-indexed and @@ -815,13 +1050,21 @@ device_set_ivars(child, dinfo); - /* Inform concrete bus driver. */ - BHND_BUS_CHILD_ADDED(dev, child); - return (child); } /** + * Default bhnd(4) bus driver implementation of BHND_BUS_CHILD_ADDED(). + * + * This implementation manages internal bhnd(4) state, and must be called + * by subclassing drivers. + */ +void +bhnd_generic_child_added(device_t dev, device_t child) +{ +} + +/** * Default bhnd(4) bus driver implementation of BUS_CHILD_DELETED(). * * This implementation manages internal bhnd(4) state, and must be called @@ -836,8 +1079,17 @@ sc = device_get_softc(dev); /* Free device info */ - if ((dinfo = device_get_ivars(child)) != NULL) + if ((dinfo = device_get_ivars(child)) != NULL) { + if (dinfo->pmu_info != NULL) { + /* Releasing PMU requests automatically would be nice, + * but we can't reference per-core PMU register + * resource after driver detach */ + panic("%s leaked device pmu state\n", + device_get_nameunit(child)); + } + BHND_BUS_FREE_DEVINFO(dev, dinfo); + } /* Clean up platform device references */ if (sc->chipc_dev == child) { @@ -998,9 +1250,20 @@ /* BHND interface */ DEVMETHOD(bhnd_bus_get_chipid, bhnd_bus_generic_get_chipid), + DEVMETHOD(bhnd_bus_is_hw_disabled, bhnd_bus_generic_is_hw_disabled), + DEVMETHOD(bhnd_bus_read_board_info, bhnd_bus_generic_read_board_info), + DEVMETHOD(bhnd_bus_get_probe_order, bhnd_generic_get_probe_order), + + DEVMETHOD(bhnd_bus_alloc_pmu, bhnd_generic_alloc_pmu), + DEVMETHOD(bhnd_bus_release_pmu, bhnd_generic_release_pmu), + DEVMETHOD(bhnd_bus_request_clock, bhnd_generic_request_clock), + DEVMETHOD(bhnd_bus_enable_clocks, bhnd_generic_enable_clocks), + DEVMETHOD(bhnd_bus_request_ext_rsrc, bhnd_generic_request_ext_rsrc), + DEVMETHOD(bhnd_bus_release_ext_rsrc, bhnd_generic_release_ext_rsrc), + + DEVMETHOD(bhnd_bus_child_added, bhnd_generic_child_added), DEVMETHOD(bhnd_bus_is_region_valid, bhnd_generic_is_region_valid), - DEVMETHOD(bhnd_bus_is_hw_disabled, bhnd_bus_generic_is_hw_disabled), DEVMETHOD(bhnd_bus_get_nvram_var, bhnd_generic_get_nvram_var), /* BHND interface (bus I/O) */ Index: sys/dev/bhnd/bhnd_bus_if.m =================================================================== --- sys/dev/bhnd/bhnd_bus_if.m +++ sys/dev/bhnd/bhnd_bus_if.m @@ -61,6 +61,27 @@ { panic("bhnd_bus_get_attach_type unimplemented"); } + + static bhnd_clksrc + bhnd_bus_null_pwrctl_get_clksrc(device_t dev, device_t child, + bhnd_clock clock) + { + return (BHND_CLKSRC_UNKNOWN); + } + + static int + bhnd_bus_null_pwrctl_gate_clock(device_t dev, device_t child, + bhnd_clock clock) + { + return (ENODEV); + } + + static int + bhnd_bus_null_pwrctl_ungate_clock(device_t dev, device_t child, + bhnd_clock clock) + { + return (ENODEV); + } static int bhnd_bus_null_read_board_info(device_t dev, device_t child, @@ -74,6 +95,60 @@ { } + static int + bhnd_bus_null_alloc_pmu(device_t dev, device_t child) + { + panic("bhnd_bus_alloc_pmu unimplemented"); + } + + static int + bhnd_bus_null_release_pmu(device_t dev, device_t child) + { + panic("bhnd_bus_release_pmu unimplemented"); + } + + static int + bhnd_bus_null_request_clock(device_t dev, device_t child, + bhnd_clock clock) + { + panic("bhnd_bus_request_clock unimplemented"); + } + + static int + bhnd_bus_null_enable_clocks(device_t dev, device_t child, + uint32_t clocks) + { + panic("bhnd_bus_enable_clocks unimplemented"); + } + + static int + bhnd_bus_null_request_ext_rsrc(device_t dev, device_t child, + u_int rsrc) + { + panic("bhnd_bus_request_ext_rsrc unimplemented"); + } + + static int + bhnd_bus_null_release_ext_rsrc(device_t dev, device_t child, + u_int rsrc) + { + panic("bhnd_bus_release_ext_rsrc unimplemented"); + } + + static uint32_t + bhnd_bus_null_read_config(device_t dev, device_t child, + bus_size_t offset, u_int width) + { + panic("bhnd_bus_null_read_config unimplemented"); + } + + static void + bhnd_bus_null_write_config(device_t dev, device_t child, + bus_size_t offset, uint32_t val, u_int width) + { + panic("bhnd_bus_null_write_config unimplemented"); + } + static device_t bhnd_bus_null_find_hostb_device(device_t dev) { @@ -261,9 +336,8 @@ /** * Notify a bhnd bus that a child was added. * - * Called at the end of BUS_ADD_CHILD() to allow the concrete bhnd(4) - * driver instance to initialize any additional driver-specific state for the - * child. + * This method must be called by concrete bhnd(4) driver impementations + * after @p child's bus state is fully initialized. * * @param dev The bhnd bus whose child is being added. * @param child The child added to @p dev. @@ -304,6 +378,230 @@ } /** + * If supported by the chipset, return the clock source for the given clock. + * + * This function is only supported on early PWRCTL-equipped chipsets + * that expose clock management via their host bridge interface. Currently, + * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. + * + * @param dev The parent of @p child. + * @param child The bhnd device requesting a clock source. + * @param clock The clock for which a clock source will be returned. + * + * @retval bhnd_clksrc The clock source for @p clock. + * @retval BHND_CLKSRC_UNKNOWN If @p clock is unsupported, or its + * clock source is not known to the bus. + */ +METHOD bhnd_clksrc pwrctl_get_clksrc { + device_t dev; + device_t child; + bhnd_clock clock; +} DEFAULT bhnd_bus_null_pwrctl_get_clksrc; + +/** + * If supported by the chipset, gate the clock source for @p clock + * + * This function is only supported on early PWRCTL-equipped chipsets + * that expose clock management via their host bridge interface. Currently, + * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. + * + * @param dev The parent of @p child. + * @param child The bhnd device requesting clock gating. + * @param clock The clock to be disabled. + * + * @retval 0 success + * @retval ENODEV If bus-level clock source management is not supported. + * @retval ENXIO If bus-level management of @p clock is not supported. + */ +METHOD int pwrctl_gate_clock { + device_t dev; + device_t child; + bhnd_clock clock; +} DEFAULT bhnd_bus_null_pwrctl_gate_clock; + +/** + * If supported by the chipset, ungate the clock source for @p clock + * + * This function is only supported on early PWRCTL-equipped chipsets + * that expose clock management via their host bridge interface. Currently, + * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. + * + * @param dev The parent of @p child. + * @param child The bhnd device requesting clock gating. + * @param clock The clock to be enabled. + * + * @retval 0 success + * @retval ENODEV If bus-level clock source management is not supported. + * @retval ENXIO If bus-level management of @p clock is not supported. + */ +METHOD int pwrctl_ungate_clock { + device_t dev; + device_t child; + bhnd_clock clock; +} DEFAULT bhnd_bus_null_pwrctl_ungate_clock; + +/** + * Allocate and enable per-core PMU request handling for @p child. + * + * The region containing the core's PMU register block (if any) must be + * allocated via bus_alloc_resource(9) (or bhnd_alloc_resource) before + * calling BHND_BUS_ALLOC_PMU(), and must not be released until after + * calling BHND_BUS_RELEASE_PMU(). + * + * @param dev The parent of @p child. + * @param child The requesting bhnd device. + */ +METHOD int alloc_pmu { + device_t dev; + device_t child; +} DEFAULT bhnd_bus_null_alloc_pmu; + +/** + * Release per-core PMU resources allocated for @p child. Any + * outstanding PMU requests are discarded. + * + * @param dev The parent of @p child. + * @param child The requesting bhnd device. + */ +METHOD int release_pmu { + device_t dev; + device_t child; +} DEFAULT bhnd_bus_null_release_pmu; + +/** + * Request that @p clock (or faster) be routed to @p child. + * + * A driver must ask the bhnd bus to allocate PMU request state + * via BHND_BUS_ALLOC_PMU() before it can request clock resources. + * + * Request multiplexing is managed by the bus. + * + * @param dev The parent of @p child. + * @param child The bhnd device requesting @p clock. + * @param clock The requested clock source. + * + * @retval 0 success + * @retval ENODEV If an unsupported clock was requested. + * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. + */ +METHOD int request_clock { + device_t dev; + device_t child; + bhnd_clock clock; +} DEFAULT bhnd_bus_null_request_clock; + +/** + * Request that @p clocks be powered on behalf of @p child. + * + * This will power on clock sources (e.g. XTAL, PLL, etc) required for + * @p clocks and wait until they are ready, discarding any previous + * requests by @p child. + * + * Request multiplexing is managed by the bus. + * + * A driver must ask the bhnd bus to allocate PMU request state + * via BHND_BUS_ALLOC_PMU() before it can request clock resources. + * + * @param dev The parent of @p child. + * @param child The bhnd device requesting @p clock. + * @param clock The requested clock source. + * + * @retval 0 success + * @retval ENODEV If an unsupported clock was requested. + * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. + */ +METHOD int enable_clocks { + device_t dev; + device_t child; + uint32_t clocks; +} DEFAULT bhnd_bus_null_enable_clocks; + +/** + * Power up an external PMU-managed resource assigned to @p child. + * + * A driver must ask the bhnd bus to allocate PMU request state + * via BHND_BUS_ALLOC_PMU() before it can request PMU resources. + * + * @param dev The parent of @p child. + * @param child The bhnd device requesting @p rsrc. + * @param rsrc The core-specific external resource identifier. + * + * @retval 0 success + * @retval ENODEV If the PMU does not support @p rsrc. + * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. + */ +METHOD int request_ext_rsrc { + device_t dev; + device_t child; + u_int rsrc; +} DEFAULT bhnd_bus_null_request_ext_rsrc; + +/** + * Power down an external PMU-managed resource assigned to @p child. + * + * A driver must ask the bhnd bus to allocate PMU request state + * via BHND_BUS_ALLOC_PMU() before it can request PMU resources. + * + * @param dev The parent of @p child. + * @param child The bhnd device requesting @p rsrc. + * @param rsrc The core-specific external resource number. + * + * @retval 0 success + * @retval ENODEV If the PMU does not support @p rsrc. + * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. + */ +METHOD int release_ext_rsrc { + device_t dev; + device_t child; + u_int rsrc; +} DEFAULT bhnd_bus_null_release_ext_rsrc; + +/** + * Read @p width bytes at @p offset from the bus-specific agent/config + * space of @p child. + * + * @param dev The parent of @p child. + * @param child The bhnd device for which @p offset should be read. + * @param offset The offset to be read. + * @param width The size of the access. Must be 1, 2 or 4 bytes. + * + * The exact behavior of this method is bus-specific. In the case of + * bcma(4), this method provides access to the first agent port of @p child. + * + * @note Device drivers should only use this API for functionality + * that is not available via another bhnd(4) function. + */ +METHOD uint32_t read_config { + device_t dev; + device_t child; + bus_size_t offset; + u_int width; +} DEFAULT bhnd_bus_null_read_config; + +/** + * Read @p width bytes at @p offset from the bus-specific agent/config + * space of @p child. + * + * @param dev The parent of @p child. + * @param child The bhnd device for which @p offset should be read. + * @param offset The offset to be written. + * @param width The size of the access. Must be 1, 2 or 4 bytes. + * + * The exact behavior of this method is bus-specific. In the case of + * bcma(4), this method provides access to the first agent port of @p child. + * + * @note Device drivers should only use this API for functionality + * that is not available via another bhnd(4) function. + */ +METHOD void write_config { + device_t dev; + device_t child; + bus_size_t offset; + uint32_t val; + u_int width; +} DEFAULT bhnd_bus_null_write_config; + +/** * Allocate a bhnd resource. * * This method's semantics are functionally identical to the bus API of the same Index: sys/dev/bhnd/bhnd_core.h =================================================================== --- sys/dev/bhnd/bhnd_core.h +++ sys/dev/bhnd/bhnd_core.h @@ -46,40 +46,4 @@ #define BHND_RESET_SF 0x0804 -/* - * A register that is common to all cores to - * communicate w/PMU regarding clock control. - * - * TODO: Determine when this register first appeared. - */ -#define BHND_CLK_CTL_ST 0x1e0 /**< clock control and status */ - -/* - * BHND_CLK_CTL_ST register - * - * 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_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_ALPAREQ 0x00000008 /**< ALP Avail Request */ -#define BHND_CCS_HTAREQ 0x00000010 /**< HT Avail Request */ -#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_ALPAVAIL 0x00010000 /**< ALP is available */ -#define BHND_CCS_HTAVAIL 0x00020000 /**< HT is available */ -#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 */ - #endif /* _BHND_BHND_CORE_H_ */ Index: sys/dev/bhnd/bhnd_ids.h =================================================================== --- sys/dev/bhnd/bhnd_ids.h +++ sys/dev/bhnd/bhnd_ids.h @@ -692,12 +692,6 @@ #define BHND_GPIO_BTC4W_OUT_4313 0x060 /* bit 5 SW_BT, bit 6 SW_WL */ #define BHND_GPIO_BTC4W_OUT_4331_SHARED 0x010 /* GPIO 4 */ -/* Power Control Defines */ -#define BHND_CHIPC_PLL_DELAY 150 /* us pll on delay */ -#define BHND_CHIPC_FREF_DELAY 200 /* us fref change delay */ -#define BHND_CHIPC_MIN_SLOW_CLK 32 /* us Slow clock period */ -#define BHND_CHIPC_XTAL_ON_DELAY 1000 /* us crystal power-on delay */ - /* Board Types */ #define BHND_BOARD_BU4710 0x0400 #define BHND_BOARD_VSIM4710 0x0401 Index: sys/dev/bhnd/bhnd_subr.c =================================================================== --- sys/dev/bhnd/bhnd_subr.c +++ sys/dev/bhnd/bhnd_subr.c @@ -1314,7 +1314,9 @@ OPT_BHND_GV(info->board_vendor, BOARDVENDOR, 0); OPT_BHND_GV(info->board_type, BOARDTYPE, 0); /* srom >= 2 */ REQ_BHND_GV(info->board_rev, BOARDREV); - REQ_BHND_GV(info->board_srom_rev,SROMREV); + OPT_BHND_GV(info->board_srom_rev,SROMREV, 0); /* missing in + some SoC + NVRAM */ REQ_BHND_GV(info->board_flags, BOARDFLAGS); OPT_BHND_GV(info->board_flags2, BOARDFLAGS2, 0); /* srom >= 4 */ OPT_BHND_GV(info->board_flags3, BOARDFLAGS3, 0); /* srom >= 11 */ Index: sys/dev/bhnd/bhnd_types.h =================================================================== --- sys/dev/bhnd/bhnd_types.h +++ sys/dev/bhnd/bhnd_types.h @@ -85,6 +85,66 @@ * SoC */ } bhnd_attach_type; +/** + * bhnd(4) clock types. + */ +typedef enum { + /** + * Dynamically select an appropriate clock source based on all + * outstanding clock requests. + */ + BHND_CLOCK_DYN = (1 << 0), + + /** + * Idle Low-Power (ILP). + * + * No register access is required, or long request latency is + * acceptable. + */ + BHND_CLOCK_ILP = (1 << 1), + + /** + * Active Low-Power (ALP). + * + * Low-latency register access and low-rate DMA. + */ + BHND_CLOCK_ALP = (1 << 2), + + /** + * High Throughput (HT). + * + * High bus throughput and lowest-latency register access. + */ + BHND_CLOCK_HT = (1 << 3) +} bhnd_clock; + +/** + * Given two clock types, return the type with the highest precedence. + */ +static inline bhnd_clock +bhnd_clock_max(bhnd_clock a, bhnd_clock b) { + return (a > b ? a : b); +} + +/** + * bhnd(4) clock sources. + */ +typedef enum { + /** + * Clock is provided by the PCI bus clock + */ + BHND_CLKSRC_PCI = 0, + + /** Clock is provided by a crystal. */ + BHND_CLKSRC_XTAL = 1, + + /** Clock is provided by a low power oscillator. */ + BHND_CLKSRC_LPO = 2, + + /** Clock source is unknown */ + BHND_CLKSRC_UNKNOWN = 3 +} bhnd_clksrc; + /** Evaluates to true if @p cls is a device class that can be configured * as a host bridge device. */ #define BHND_DEVCLASS_SUPPORTS_HOSTB(cls) \ Index: sys/dev/bhnd/bhndb/bhnd_bhndb.c =================================================================== --- sys/dev/bhnd/bhndb/bhnd_bhndb.c +++ sys/dev/bhnd/bhndb/bhnd_bhndb.c @@ -66,11 +66,42 @@ return (BHND_ATTACH_ADAPTER); } +static bhnd_clksrc +bhnd_bhndb_pwrctl_get_clksrc(device_t dev, device_t child, + bhnd_clock clock) +{ + /* Delegate to parent bridge */ + return (BHND_BUS_PWRCTL_GET_CLKSRC(device_get_parent(dev), child, + clock)); +} + +static int +bhnd_bhndb_pwrctl_gate_clock(device_t dev, device_t child, + bhnd_clock clock) +{ + /* Delegate to parent bridge */ + return (BHND_BUS_PWRCTL_GATE_CLOCK(device_get_parent(dev), child, + clock)); +} + +static int +bhnd_bhndb_pwrctl_ungate_clock(device_t dev, device_t child, + bhnd_clock clock) +{ + /* Delegate to parent bridge */ + return (BHND_BUS_PWRCTL_UNGATE_CLOCK(device_get_parent(dev), child, + clock)); +} + static device_method_t bhnd_bhndb_methods[] = { /* BHND interface */ DEVMETHOD(bhnd_bus_get_attach_type, bhnd_bhndb_get_attach_type), DEVMETHOD(bhnd_bus_read_board_info, bhnd_bhndb_read_board_info), + 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), + DEVMETHOD_END }; Index: sys/dev/bhnd/bhndb/bhndb.c =================================================================== --- sys/dev/bhnd/bhndb/bhndb.c +++ sys/dev/bhnd/bhndb/bhndb.c @@ -2027,4 +2027,3 @@ MODULE_VERSION(bhndb, 1); MODULE_DEPEND(bhndb, bhnd, 1, 1, 1); -MODULE_DEPEND(bhndb, bhnd_chipc, 1, 1, 1); Index: sys/dev/bhnd/bhndb/bhndb_pci.c =================================================================== --- sys/dev/bhnd/bhndb/bhndb_pci.c +++ sys/dev/bhnd/bhndb/bhndb_pci.c @@ -606,6 +606,64 @@ return (0); } +static bhnd_clksrc +bhndb_pci_pwrctl_get_clksrc(device_t dev, device_t child, + bhnd_clock clock) +{ + struct bhndb_pci_softc *sc; + uint32_t gpio_out; + + sc = device_get_softc(dev); + + /* Only supported on PCI devices */ + if (sc->pci_devclass != BHND_DEVCLASS_PCI) + return (ENODEV); + + /* Only ILP is supported */ + if (clock != BHND_CLOCK_ILP) + return (ENXIO); + + gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4); + if (gpio_out & BHNDB_PCI_GPIO_SCS) + return (BHND_CLKSRC_PCI); + else + return (BHND_CLKSRC_XTAL); +} + +static int +bhndb_pci_pwrctl_gate_clock(device_t dev, device_t child, + bhnd_clock clock) +{ + struct bhndb_pci_softc *sc = device_get_softc(dev); + + /* Only supported on PCI devices */ + if (sc->pci_devclass != BHND_DEVCLASS_PCI) + return (ENODEV); + + /* Only HT is supported */ + if (clock != BHND_CLOCK_HT) + return (ENXIO); + + return (bhndb_disable_pci_clocks(sc)); +} + +static int +bhndb_pci_pwrctl_ungate_clock(device_t dev, device_t child, + bhnd_clock clock) +{ + struct bhndb_pci_softc *sc = device_get_softc(dev); + + /* Only supported on PCI devices */ + if (sc->pci_devclass != BHND_DEVCLASS_PCI) + return (ENODEV); + + /* Only HT is supported */ + if (clock != BHND_CLOCK_HT) + return (ENXIO); + + return (bhndb_enable_pci_clocks(sc)); +} + static device_method_t bhndb_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bhndb_pci_probe), @@ -614,6 +672,11 @@ 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_init_full_config, bhndb_pci_init_full_config), DEVMETHOD(bhndb_set_window_addr, bhndb_pci_set_window_addr), @@ -627,7 +690,6 @@ MODULE_VERSION(bhndb_pci, 1); MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1); -MODULE_DEPEND(bhndb_pci, bhnd_pcie2_hostb, 1, 1, 1); MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1); MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1); MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1); Index: sys/dev/bhnd/bhndvar.h =================================================================== --- sys/dev/bhnd/bhndvar.h +++ sys/dev/bhnd/bhndvar.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -45,27 +45,7 @@ MALLOC_DECLARE(M_BHND); DECLARE_CLASS(bhnd_driver); -/** - * bhnd per-device info. Must be first member of all subclass - * devinfo structures. - */ -struct bhnd_devinfo { -}; - -/** - * bhnd driver instance state. Must be first member of all subclass - * softc structures. - */ -struct bhnd_softc { - device_t dev; /**< bus device */ - - bool attach_done; /**< true if initialization of all - * platform devices has been - * completed */ - device_t chipc_dev; /**< bhnd_chipc device */ - device_t nvram_dev; /**< bhnd_nvram device, if any */ - device_t pmu_dev; /**< bhnd_pmu device, if any */ -}; +struct bhnd_core_pmu_info; int bhnd_generic_attach(device_t dev); int bhnd_generic_detach(device_t dev); @@ -76,6 +56,19 @@ int bhnd_generic_get_probe_order(device_t dev, device_t child); +int bhnd_generic_alloc_pmu(device_t dev, + device_t child); +int bhnd_generic_release_pmu(device_t dev, + device_t child); +int bhnd_generic_request_clock(device_t dev, + device_t child, bhnd_clock clock); +int bhnd_generic_enable_clocks(device_t dev, + device_t child, uint32_t clocks); +int bhnd_generic_request_ext_rsrc(device_t dev, + device_t child, u_int rsrc); +int bhnd_generic_release_ext_rsrc(device_t dev, + device_t child, u_int rsrc); + int bhnd_generic_print_child(device_t dev, device_t child); void bhnd_generic_probe_nomatch(device_t dev, @@ -83,6 +76,7 @@ device_t bhnd_generic_add_child(device_t dev, u_int order, const char *name, int unit); +void bhnd_generic_child_added(device_t dev, device_t child); void bhnd_generic_child_deleted(device_t dev, device_t child); int bhnd_generic_suspend_child(device_t dev, @@ -94,4 +88,28 @@ device_t child, const char *name, void *buf, size_t *size, bhnd_nvram_type type); + +/** + * bhnd per-device info. Must be first member of all subclass + * devinfo structures. + */ +struct bhnd_devinfo { + struct bhnd_core_pmu_info *pmu_info; /**< PMU info, or NULL */ +}; + +/** + * bhnd driver instance state. Must be first member of all subclass + * softc structures. + */ +struct bhnd_softc { + device_t dev; /**< bus device */ + + bool attach_done; /**< true if initialization of + * all platform devices has + * been completed */ + device_t chipc_dev; /**< bhnd_chipc device */ + device_t nvram_dev; /**< bhnd_nvram device, if any */ + device_t pmu_dev; /**< bhnd_pmu device, if any */ +}; + #endif /* _BHND_BHNDVAR_H_ */ Index: sys/dev/bhnd/cores/chipc/bhnd_chipc_if.m =================================================================== --- sys/dev/bhnd/cores/chipc/bhnd_chipc_if.m +++ sys/dev/bhnd/cores/chipc/bhnd_chipc_if.m @@ -48,6 +48,22 @@ } } + +/** + * Return the current value of the chipstatus register. + * + * @param dev A bhnd(4) ChipCommon device. + * + * Drivers should only use function for functionality that is not + * available via another bhnd_chipc() function. + * + * @returns The chipstatus register value, or 0 if undefined by this + * hardware (e.g. if @p dev is an EXTIF core). + */ +METHOD uint32_t read_chipst { + device_t dev; +} + /** * Write @p value with @p mask directly to the chipctrl register. * Index: sys/dev/bhnd/cores/chipc/bhnd_pmu_chipc.c =================================================================== --- /dev/null +++ sys/dev/bhnd/cores/chipc/bhnd_pmu_chipc.c @@ -0,0 +1,126 @@ +/*- + * Copyright (c) 2016 Landon Fuller + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * ChipCommon attachment support for the bhnd(4) PMU driver. + * + * Supports non-AOB ("Always-on Bus") devices that map the PMU register blocks + * via the ChipCommon core, rather than vending a distinct PMU core on the + * bhnd bus. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "bhnd_chipc_if.h" +#include "bhnd_pmu_if.h" + +#include "chipcvar.h" + +static int +bhnd_pmu_chipc_probe(device_t dev) +{ + struct bhnd_pmu_softc *sc; + struct chipc_caps *ccaps; + struct chipc_softc *chipc_sc; + device_t chipc; + char desc[34]; + int error; + uint32_t pcaps; + uint8_t rev; + + sc = device_get_softc(dev); + + /* Look for chipc parent */ + chipc = device_get_parent(dev); + if (device_get_devclass(chipc) != devclass_find("bhnd_chipc")) + return (ENXIO); + + /* Check the chipc PMU capability flag. */ + ccaps = BHND_CHIPC_GET_CAPS(chipc); + if (!ccaps->pmu) + return (ENXIO); + + /* Delegate to common driver implementation */ + if ((error = bhnd_pmu_probe(dev)) > 0) + return (error); + + /* Fetch PMU capability flags */ + chipc_sc = device_get_softc(chipc); + pcaps = bhnd_bus_read_4(chipc_sc->core, BHND_PMU_CAP); + + /* Set description */ + rev = BHND_PMU_GET_BITS(pcaps, BHND_PMU_CAP_REV); + snprintf(desc, sizeof(desc), "Broadcom ChipCommon PMU, rev %hhu", rev); + device_set_desc_copy(dev, desc); + + return (BUS_PROBE_NOWILDCARD); +} + +static int +bhnd_pmu_chipc_attach(device_t dev) +{ + struct chipc_softc *chipc_sc; + struct bhnd_resource *r; + + /* Fetch core registers from ChipCommon parent */ + chipc_sc = device_get_softc(device_get_parent(dev)); + r = chipc_sc->core; + + return (bhnd_pmu_attach(dev, r)); +} + +static device_method_t bhnd_pmu_chipc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, bhnd_pmu_chipc_probe), + DEVMETHOD(device_attach, bhnd_pmu_chipc_attach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(bhnd_pmu, bhnd_pmu_chipc_driver, bhnd_pmu_chipc_methods, + sizeof(struct bhnd_pmu_softc), bhnd_pmu_driver); +EARLY_DRIVER_MODULE(bhnd_pmu_chipc, bhnd_chipc, bhnd_pmu_chipc_driver, + bhnd_pmu_devclass, NULL, NULL, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); + +MODULE_DEPEND(bhnd_pmu_chipc, bhnd, 1, 1, 1); +MODULE_VERSION(bhnd_pmu_chipc, 1); Index: sys/dev/bhnd/cores/chipc/bhnd_sprom_chipc.c =================================================================== --- sys/dev/bhnd/cores/chipc/bhnd_sprom_chipc.c +++ sys/dev/bhnd/cores/chipc/bhnd_sprom_chipc.c @@ -46,10 +46,9 @@ #include #include -#include "bhnd_nvram_if.h" +#include -#include "chipcvar.h" -#include "chipc_private.h" +#include "bhnd_nvram_if.h" #define CHIPC_VALID_SPROM_SRC(_src) \ ((_src) == BHND_NVRAM_SRC_SPROM || (_src) == BHND_NVRAM_SRC_OTP) Index: sys/dev/bhnd/cores/chipc/chipc.h =================================================================== --- sys/dev/bhnd/cores/chipc/chipc.h +++ sys/dev/bhnd/cores/chipc/chipc.h @@ -37,4 +37,60 @@ #include "bhnd_chipc_if.h" +/** + * Supported ChipCommon flash types. + */ +typedef enum { + CHIPC_FLASH_NONE = 0, /**< No flash, or a type unrecognized + by the ChipCommon driver */ + CHIPC_PFLASH_CFI = 1, /**< CFI-compatible parallel flash */ + CHIPC_SFLASH_ST = 2, /**< ST serial flash */ + CHIPC_SFLASH_AT = 3, /**< Atmel serial flash */ + CHIPC_QSFLASH_ST = 4, /**< ST quad-SPI flash */ + CHIPC_QSFLASH_AT = 5, /**< Atmel quad-SPI flash */ + CHIPC_NFLASH = 6, /**< NAND flash */ + CHIPC_NFLASH_4706 = 7 /**< BCM4706 NAND flash */ +} chipc_flash; + +/** + * ChipCommon capability flags; + */ +struct chipc_caps { + uint8_t num_uarts; /**< Number of attached UARTS (1-3) */ + bool mipseb; /**< MIPS is big-endian */ + uint8_t uart_clock; /**< UART clock source (see CHIPC_CAP_UCLKSEL_*) */ + uint8_t uart_gpio; /**< UARTs own GPIO pins 12-15 */ + + uint8_t extbus_type; /**< ExtBus type (CHIPC_CAP_EXTBUS_*) */ + + chipc_flash flash_type; /**< flash type */ + uint8_t cfi_width; /**< CFI bus width, 0 if unknown or CFI + not present */ + + bhnd_nvram_src nvram_src; /**< identified NVRAM source */ + bus_size_t sprom_offset; /**< Offset to SPROM data within + SPROM/OTP, 0 if unknown or not + present */ + uint8_t otp_size; /**< OTP (row?) size, 0 if not present */ + + uint8_t pll_type; /**< PLL type */ + bool pwr_ctrl; /**< Power/clock control available */ + bool jtag_master; /**< JTAG Master present */ + bool boot_rom; /**< Internal boot ROM is active */ + uint8_t backplane_64; /**< Backplane supports 64-bit addressing. + Note that this does not gaurantee + the CPU itself supports 64-bit + addressing. */ + bool pmu; /**< PMU is present. */ + bool eci; /**< ECI (enhanced coexistence inteface) is present. */ + bool seci; /**< SECI (serial ECI) is present */ + bool sprom; /**< SPROM is present */ + bool gsio; /**< GSIO (SPI/I2C) present */ + bool aob; /**< AOB (always on bus) present. + If set, PMU and GCI registers are + not accessible via ChipCommon, + and are instead accessible via + dedicated cores on the bhnd bus */ +}; + #endif /* _BHND_CORES_CHIPC_CHIPC_H_ */ Index: sys/dev/bhnd/cores/chipc/chipc.c =================================================================== --- sys/dev/bhnd/cores/chipc/chipc.c +++ sys/dev/bhnd/cores/chipc/chipc.c @@ -110,25 +110,27 @@ // FIXME: IRQ shouldn't be hard-coded #define CHIPC_MIPS_IRQ 2 -static int chipc_add_children(struct chipc_softc *sc); +static int chipc_add_children(struct chipc_softc *sc); -static bhnd_nvram_src chipc_find_nvram_src(struct chipc_softc *sc, - struct chipc_caps *caps); -static int chipc_read_caps(struct chipc_softc *sc, - struct chipc_caps *caps); +static bhnd_nvram_src chipc_find_nvram_src(struct chipc_softc *sc, + struct chipc_caps *caps); +static int chipc_read_caps(struct chipc_softc *sc, + struct chipc_caps *caps); -static bool chipc_should_enable_sprom( - struct chipc_softc *sc); +static bool chipc_should_enable_muxed_sprom( + struct chipc_softc *sc); +static int chipc_enable_otp_power(struct chipc_softc *sc); +static void chipc_disable_otp_power(struct chipc_softc *sc); +static int chipc_enable_sprom_pins(struct chipc_softc *sc); +static void chipc_disable_sprom_pins(struct chipc_softc *sc); -static int chipc_try_activate_resource( - struct chipc_softc *sc, device_t child, - int type, int rid, struct resource *r, - bool req_direct); +static int chipc_try_activate_resource(struct chipc_softc *sc, + device_t child, int type, int rid, + struct resource *r, bool req_direct); -static int chipc_init_rman(struct chipc_softc *sc); -static void chipc_free_rman(struct chipc_softc *sc); -static struct rman *chipc_get_rman(struct chipc_softc *sc, - int type); +static int chipc_init_rman(struct chipc_softc *sc); +static void chipc_free_rman(struct chipc_softc *sc); +static struct rman *chipc_get_rman(struct chipc_softc *sc, int type); /* quirk and capability flag convenience macros */ #define CHIPC_QUIRK(_sc, _name) \ @@ -266,40 +268,19 @@ return (error); } -#ifdef notyet /* - * PMU/SLOWCLK/INSTACLK + * PMU/PWR_CTRL * - * On AOB ("Always on Bus") devices, a PMU core (if it exists) is - * enumerated directly by the bhnd(4) bus -- not chipc. - * - * Otherwise, we always add a PMU child device, and let the - * chipc bhnd_pmu drivers probe for it. If the core supports an - * earlier non-PMU clock/power register interface, one of the instaclk, - * powerctl, or null bhnd_pmu drivers will claim the device. + * 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.aob || (sc->caps.aob && !sc->caps.pmu)) { + if (sc->caps.pwr_ctrl || (sc->caps.pmu && !sc->caps.aob)) { child = BUS_ADD_CHILD(sc->dev, 0, "bhnd_pmu", -1); if (child == NULL) { device_printf(sc->dev, "failed to add pmu\n"); return (ENXIO); } - - /* Associate the applicable register block */ - error = 0; - if (sc->caps.pmu) { - error = chipc_set_resource(sc, child, SYS_RES_MEMORY, 0, - CHIPC_PMU, CHIPC_PMU_SIZE, 0, 0); - } else if (sc->caps.power_control) { - error = chipc_set_resource(sc, child, SYS_RES_MEMORY, 0, - CHIPC_PWRCTL, CHIPC_PWRCTL_SIZE, 0, 0); - } - - if (error) - return (error); - } -#endif /* notyet */ /* All remaining devices are SoC-only */ if (bhnd_get_attach_type(sc->dev) != BHND_ATTACH_NATIVE) @@ -422,7 +403,7 @@ caps->uart_clock = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_UCLKSEL); caps->extbus_type = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_EXTBUS); - caps->power_control = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_PWR_CTL); + caps->pwr_ctrl = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_PWR_CTL); caps->jtag_master = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_JTAGP); caps->pll_type = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_PLL); @@ -1089,7 +1070,7 @@ * @param sc chipc driver state. */ static bool -chipc_should_enable_sprom(struct chipc_softc *sc) +chipc_should_enable_muxed_sprom(struct chipc_softc *sc) { device_t *devs; device_t hostb; @@ -1098,17 +1079,19 @@ int error; bool result; - mtx_assert(&Giant, MA_OWNED); /* for newbus */ - /* Nothing to do? */ if (!CHIPC_QUIRK(sc, MUX_SPROM)) return (true); + mtx_lock(&Giant); /* for newbus */ + parent = device_get_parent(sc->dev); hostb = bhnd_find_hostb_device(parent); - if ((error = device_get_children(parent, &devs, &devcount))) + if ((error = device_get_children(parent, &devs, &devcount))) { + mtx_unlock(&Giant); return (false); + } /* Reject any active devices other than ChipCommon, or the * host bridge (if any). */ @@ -1129,42 +1112,112 @@ } free(devs, M_TEMP); + mtx_unlock(&Giant); return (result); } +static int +chipc_enable_sprom(device_t dev) +{ + struct chipc_softc *sc; + int error; + + sc = device_get_softc(dev); + CHIPC_LOCK(sc); + + /* Already enabled? */ + if (sc->sprom_refcnt >= 1) { + sc->sprom_refcnt++; + CHIPC_UNLOCK(sc); + + return (0); + } + + switch (sc->caps.nvram_src) { + case BHND_NVRAM_SRC_SPROM: + error = chipc_enable_sprom_pins(sc); + break; + case BHND_NVRAM_SRC_OTP: + error = chipc_enable_otp_power(sc); + break; + default: + error = 0; + break; + } + + /* Bump the reference count */ + if (error == 0) + sc->sprom_refcnt++; + + CHIPC_UNLOCK(sc); + return (error); +} + +static void +chipc_disable_sprom(device_t dev) +{ + struct chipc_softc *sc; + + sc = device_get_softc(dev); + CHIPC_LOCK(sc); + + /* Check reference count, skip disable if in-use. */ + KASSERT(sc->sprom_refcnt > 0, ("sprom refcnt overrelease")); + sc->sprom_refcnt--; + if (sc->sprom_refcnt > 0) { + CHIPC_UNLOCK(sc); + return; + } + + switch (sc->caps.nvram_src) { + case BHND_NVRAM_SRC_SPROM: + chipc_disable_sprom_pins(sc); + break; + case BHND_NVRAM_SRC_OTP: + chipc_disable_otp_power(sc); + break; + default: + break; + } + + + CHIPC_UNLOCK(sc); +} + +static int +chipc_enable_otp_power(struct chipc_softc *sc) +{ + // TODO: Enable OTP resource via PMU, and wait up to 100 usec for + // OTPS_READY to be set in `optstatus`. + return (0); +} + +static void +chipc_disable_otp_power(struct chipc_softc *sc) +{ + // TODO: Disable OTP resource via PMU +} + /** * If required by this device, enable access to the SPROM. * * @param sc chipc driver state. */ static int -chipc_enable_sprom_pins(device_t dev) +chipc_enable_sprom_pins(struct chipc_softc *sc) { - struct chipc_softc *sc; uint32_t cctrl; - int error; - sc = device_get_softc(dev); + CHIPC_LOCK_ASSERT(sc, MA_OWNED); + KASSERT(sc->sprom_refcnt == 0, ("sprom pins already enabled")); /* Nothing to do? */ if (!CHIPC_QUIRK(sc, MUX_SPROM)) return (0); - /* Make sure we're holding Giant for newbus */ - mtx_lock(&Giant); - CHIPC_LOCK(sc); - - /* Already enabled? */ - if (sc->sprom_refcnt >= 1) { - error = 0; - goto finished; - } - /* Check whether bus is busy */ - if (!chipc_should_enable_sprom(sc)) { - error = EBUSY; - goto finished; - } + if (!chipc_should_enable_muxed_sprom(sc)) + return (EBUSY); cctrl = bhnd_bus_read_4(sc->core, CHIPC_CHIPCTRL); @@ -1179,8 +1232,7 @@ cctrl &= ~CHIPC_CCTRL4331_EXTPA_EN2; bhnd_bus_write_4(sc->core, CHIPC_CHIPCTRL, cctrl); - error = 0; - goto finished; + return (0); } /* 4360 devices */ @@ -1190,17 +1242,7 @@ /* Refuse to proceed on unsupported devices with muxed SPROM pins */ device_printf(sc->dev, "muxed sprom lines on unrecognized device\n"); - error = ENXIO; - -finished: - /* Bump the reference count */ - if (error == 0) - sc->sprom_refcnt++; - - CHIPC_UNLOCK(sc); - mtx_unlock(&Giant); - - return (error); + return (ENXIO); } /** @@ -1210,24 +1252,17 @@ * @param sc chipc driver state. */ static void -chipc_disable_sprom_pins(device_t dev) +chipc_disable_sprom_pins(struct chipc_softc *sc) { - struct chipc_softc *sc; uint32_t cctrl; - sc = device_get_softc(dev); - /* Nothing to do? */ if (!CHIPC_QUIRK(sc, MUX_SPROM)) return; - CHIPC_LOCK(sc); - - /* Check reference count, skip disable if in-use. */ - KASSERT(sc->sprom_refcnt > 0, ("sprom refcnt overrelease")); - sc->sprom_refcnt--; - if (sc->sprom_refcnt > 0) - goto finished; + CHIPC_LOCK_ASSERT(sc, MA_OWNED); + KASSERT(sc->sprom_refcnt != 0, ("sprom pins already disabled")); + KASSERT(sc->sprom_refcnt == 1, ("sprom pins in use")); cctrl = bhnd_bus_read_4(sc->core, CHIPC_CHIPCTRL); @@ -1242,16 +1277,20 @@ cctrl |= CHIPC_CCTRL4331_EXTPA_EN2; bhnd_bus_write_4(sc->core, CHIPC_CHIPCTRL, cctrl); - goto finished; + return; } /* 4360 devices */ if (CHIPC_QUIRK(sc, 4360_FEM_MUX_SPROM)) { /* Unimplemented */ } +} -finished: - CHIPC_UNLOCK(sc); +static uint32_t +chipc_read_chipst(device_t dev) +{ + struct chipc_softc *sc = device_get_softc(dev); + return (bhnd_bus_read_4(sc->core, CHIPC_CHIPST)); } static void @@ -1317,16 +1356,17 @@ DEVMETHOD(bhnd_bus_activate_resource, chipc_activate_bhnd_resource), /* ChipCommon interface */ + DEVMETHOD(bhnd_chipc_read_chipst, chipc_read_chipst), DEVMETHOD(bhnd_chipc_write_chipctrl, chipc_write_chipctrl), - DEVMETHOD(bhnd_chipc_enable_sprom, chipc_enable_sprom_pins), - DEVMETHOD(bhnd_chipc_disable_sprom, chipc_disable_sprom_pins), + DEVMETHOD(bhnd_chipc_enable_sprom, chipc_enable_sprom), + DEVMETHOD(bhnd_chipc_disable_sprom, chipc_disable_sprom), DEVMETHOD(bhnd_chipc_get_caps, chipc_get_caps), DEVMETHOD_END }; -DEFINE_CLASS_0(bhnd_chipc, chipc_driver, chipc_methods, sizeof(struct chipc_softc)); -EARLY_DRIVER_MODULE(bhnd_chipc, bhnd, chipc_driver, bhnd_chipc_devclass, 0, 0, +DEFINE_CLASS_0(bhnd_chipc, bhnd_chipc_driver, chipc_methods, sizeof(struct chipc_softc)); +EARLY_DRIVER_MODULE(bhnd_chipc, bhnd, bhnd_chipc_driver, bhnd_chipc_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); MODULE_DEPEND(bhnd_chipc, bhnd, 1, 1, 1); MODULE_VERSION(bhnd_chipc, 1); Index: sys/dev/bhnd/cores/chipc/chipc_subr.c =================================================================== --- sys/dev/bhnd/cores/chipc/chipc_subr.c +++ sys/dev/bhnd/cores/chipc/chipc_subr.c @@ -248,8 +248,8 @@ CC_TFS(sprom), CC_TFS(otp_size)); device_printf(dev, "CFIsz: 0x%02x | OTPsz: 0x%02x\n", caps->cfi_width, caps->otp_size); - device_printf(dev, "ExtBus: 0x%02x | PwCtl: %s\n", - caps->extbus_type, CC_TFS(power_control)); + device_printf(dev, "ExtBus: 0x%02x | PwrCtrl: %s\n", + caps->extbus_type, CC_TFS(pwr_ctrl)); device_printf(dev, "PLL: 0x%02x | JTAGM: %s\n", caps->pll_type, CC_TFS(jtag_master)); device_printf(dev, "PMU: %-3s | ECI: %s\n", Index: sys/dev/bhnd/cores/chipc/chipcreg.h =================================================================== --- sys/dev/bhnd/cores/chipc/chipcreg.h +++ sys/dev/bhnd/cores/chipc/chipcreg.h @@ -90,24 +90,25 @@ #define CHIPC_GPIOTIMERVAL 0x88 /**< gpio-based LED duty cycle (rev >= 16) */ #define CHIPC_GPIOTIMEROUTMASK 0x8C -/* clock control block */ +/* clock control registers (non-PMU devices) */ #define CHIPC_CLKC_N 0x90 #define CHIPC_CLKC_SB 0x94 /* m0 (backplane) */ #define CHIPC_CLKC_PCI 0x98 /* m1 */ #define CHIPC_CLKC_M2 0x9C /* mii/uart/mipsref */ #define CHIPC_CLKC_M3 0xA0 /* cpu */ #define CHIPC_CLKDIV 0xA4 /* rev >= 3 */ + #define CHIPC_GPIODEBUGSEL 0xA8 /* rev >= 28 */ #define CHIPC_CAPABILITIES_EXT 0xAC -/* pll delay (registers rev >= 4) */ -#define CHIPC_PLL_ON_DELAY 0xB0 -#define CHIPC_PLL_FREFSEL_DELAY 0xB4 -#define CHIPC_PLL_SLOWCLK_CTL 0xB8 /* revs 6-9 */ - -/* "instaclock" registers */ -#define CHIPC_SYS_CLK_CTL 0xC0 /* rev >= 10 */ -#define CHIPC_SYS_CLKSTATESTRETCH 0xC4 /* rev >= 10 */ +/* pll/slowclk clock control registers (rev >= 4) */ +#define CHIPC_PLL_ON_DELAY 0xB0 /* rev >= 4 */ +#define CHIPC_PLL_FREFSEL_DELAY 0xB4 /* rev >= 4 */ +#define CHIPC_PLL_SLOWCLK_CTL 0xB8 /* "slowclock" (rev 6-9) */ + + /* "instaclock" clock control registers */ +#define CHIPC_SYS_CLK_CTL 0xC0 /* "instaclock" (rev >= 10) */ +#define CHIPC_SYS_CLK_ST_STRETCH 0xC4 /* state strech (?) rev >= 10 */ /* indirect backplane access (rev >= 10) */ #define CHIPC_BP_ADDRLOW 0xD0 @@ -125,7 +126,7 @@ #define CHIPC_EROMPTR 0xFC /**< 32-bit EROM base address * on BCMA devices */ - + /* ExtBus control registers (rev >= 3) */ #define CHIPC_PCMCIA_CFG 0x100 #define CHIPC_PCMCIA_MEMWAIT 0x104 @@ -178,38 +179,10 @@ #define CHIPC_UART_MAX 3 /**< max UART blocks */ #define CHIPC_UART(_n) (CHIPC_UART_BASE + (CHIPC_UART_SIZE*_n)) -/* PMU registers (rev >= 20) */ +/* PMU register block (rev >= 20) */ #define CHIPC_PMU_BASE 0x600 #define CHIPC_PMU_SIZE 0x70 -#define CHIPC_PMU_CTRL 0x600 -#define CHIPC_PMU_CAP 0x604 -#define CHIPC_PMU_ST 0x608 -#define CHIPC_PMU_RES_STATE 0x60c -#define CHIPC_PMU_RES_PENDING 0x610 -#define CHIPC_PMU_TIMER 0x614 -#define CHIPC_PMU_MIN_RES_MASK 0x618 -#define CHIPC_PMU_MAX_RES_MASK 0x61c -#define CHIPC_PMU_RES_TABLE_SEL 0x620 -#define CHIPC_PMU_RES_DEP_MASK 0x624 -#define CHIPC_PMU_RES_UPDN_TIMER 0x628 -#define CHIPC_PMU_RES_TIMER 0x62C -#define CHIPC_PMU_CLKSTRETCH 0x630 -#define CHIPC_PMU_WATCHDOG 0x634 -#define CHIPC_PMU_GPIOSEL 0x638 /* pmu rev >= 1 ? */ -#define CHIPC_PMU_GPIOEN 0x63C /* pmu rev >= 1 ? */ -#define CHIPC_PMU_RES_REQ_TIMER_SEL 0x640 -#define CHIPC_PMU_RES_REQ_TIMER 0x644 -#define CHIPC_PMU_RES_REQ_MASK 0x648 -#define CHIPC_CHIPCTL_ADDR 0x650 -#define CHIPC_CHIPCTL_DATA 0x654 -#define CHIPC_PMU_REG_CONTROL_ADDR 0x658 -#define CHIPC_PMU_REG_CONTROL_DATA 0x65C -#define CHIPC_PMU_PLL_CONTROL_ADDR 0x660 -#define CHIPC_PMU_PLL_CONTROL_DATA 0x664 -#define CHIPC_PMU_STRAPOPT 0x668 /* chipc rev >= 28 */ -#define CHIPC_PMU_XTALFREQ 0x66C /* pmu rev >= 10 */ - #define CHIPC_SPROM_OTP 0x800 /* SPROM/OTP address space */ #define CHIPC_SPROM_OTP_SIZE 0x400 @@ -247,7 +220,7 @@ #define CHIPC_CAP_PFLASH 0x7 /* Parallel flash */ #define CHIPC_CAP_PLL_MASK 0x00038000 /* Type of PLL */ #define CHIPC_CAP_PLL_SHIFT 15 -#define CHIPC_CAP_PWR_CTL 0x00040000 /* Power control */ +#define CHIPC_CAP_PWR_CTL 0x00040000 /* Power/clock control */ #define CHIPC_CAP_OTP_SIZE_MASK 0x00380000 /* OTP Size (0 = none) */ #define CHIPC_CAP_OTP_SIZE_SHIFT 19 /* OTP Size shift */ #define CHIPC_CAP_OTP_SIZE_BASE 5 /* OTP Size base */ @@ -295,23 +268,31 @@ #define CHIPC_CST_SPROM_OTP_SEL_R23_SHIFT 6 /* PLL type */ -#define CHIPC_PLL_NONE 0x00000000 -#define CHIPC_PLL_TYPE1 0x00010000 /* 48MHz base, 3 dividers */ -#define CHIPC_PLL_TYPE2 0x00020000 /* 48MHz, 4 dividers */ -#define CHIPC_PLL_TYPE3 0x00030000 /* 25MHz, 2 dividers */ -#define CHIPC_PLL_TYPE4 0x00008000 /* 48MHz, 4 dividers */ -#define CHIPC_PLL_TYPE5 0x00018000 /* 25MHz, 4 dividers */ -#define CHIPC_PLL_TYPE6 0x00028000 /* 100/200 or 120/240 only */ -#define CHIPC_PLL_TYPE7 0x00038000 /* 25MHz, 4 dividers */ - -/* ILP clock */ -#define CHIPC_ILP_CLOCK 32000 - -/* ALP clock on pre-PMU chips */ -#define CHIPC_ALP_CLOCK 20000000 - -/* HT clock */ -#define CHIPC_HT_CLOCK 80000000 +#define CHIPC_PLL_NONE 0x00 +#define CHIPC_PLL_TYPE1 0x10 /* 48MHz base, 3 dividers */ +#define CHIPC_PLL_TYPE2 0x20 /* 48MHz, 4 dividers */ +#define CHIPC_PLL_TYPE3 0x30 /* 25MHz, 2 dividers */ +#define CHIPC_PLL_TYPE4 0x08 /* 48MHz, 4 dividers */ +#define CHIPC_PLL_TYPE5 0x18 /* 25MHz, 4 dividers */ +#define CHIPC_PLL_TYPE6 0x28 /* 100/200 or 120/240 only */ +#define CHIPC_PLL_TYPE7 0x38 /* 25MHz, 4 dividers */ + +/* dynamic clock control defines */ +#define CHIPC_LPOMINFREQ 25000 /* low power oscillator min */ +#define CHIPC_LPOMAXFREQ 43000 /* low power oscillator max */ +#define CHIPC_XTALMINFREQ 19800000 /* 20 MHz - 1% */ +#define CHIPC_XTALMAXFREQ 20200000 /* 20 MHz + 1% */ +#define CHIPC_PCIMINFREQ 25000000 /* 25 MHz */ +#define CHIPC_PCIMAXFREQ 34000000 /* 33 MHz + fudge */ + +#define CHIPC_ILP_DIV_5MHZ 0 /* ILP = 5 MHz */ +#define CHIPC_ILP_DIV_1MHZ 4 /* ILP = 1 MHz */ + +/* Power Control Defines */ +#define CHIPC_PLL_DELAY 150 /* us pll on delay */ +#define CHIPC_FREF_DELAY 200 /* us fref change delay */ +#define CHIPC_MIN_SLOW_CLK 32 /* us Slow clock period */ +#define CHIPC_XTAL_ON_DELAY 1000 /* us crystal power-on delay */ /* corecontrol */ #define CHIPC_UARTCLKO 0x00000001 /* Drive UART with internal clock */ @@ -567,27 +548,12 @@ #define CHIPC_SRC_SIZE_SHIFT 1 #define CHIPC_SRC_PRESENT 0x00000001 -/* Fields in pmucontrol */ -#define CHIPC_PCTL_ILP_DIV_MASK 0xffff0000 -#define CHIPC_PCTL_ILP_DIV_SHIFT 16 -#define CHIPC_PCTL_PLL_PLLCTL_UPD 0x00000400 /* rev 2 */ -#define CHIPC_PCTL_NOILP_ON_WAIT 0x00000200 /* rev 1 */ -#define CHIPC_PCTL_HT_REQ_EN 0x00000100 -#define CHIPC_PCTL_ALP_REQ_EN 0x00000080 -#define CHIPC_PCTL_XTALFREQ_MASK 0x0000007c -#define CHIPC_PCTL_XTALFREQ_SHIFT 2 -#define CHIPC_PCTL_ILP_DIV_EN 0x00000002 -#define CHIPC_PCTL_LPO_SEL 0x00000001 - -/* Fields in clkstretch */ -#define CHIPC_CSTRETCH_HT 0xffff0000 -#define CHIPC_CSTRETCH_ALP 0x0000ffff - /* gpiotimerval */ #define CHIPC_GPIO_ONTIME_SHIFT 16 /* clockcontrol_n */ #define CHIPC_CN_N1_MASK 0x3f /* n1 control */ +#define CHIPC_CN_N1_SHIFT 0 #define CHIPC_CN_N2_MASK 0x3f00 /* n2 control */ #define CHIPC_CN_N2_SHIFT 8 #define CHIPC_CN_PLLC_MASK 0xf0000 /* pll control */ @@ -595,6 +561,7 @@ /* clockcontrol_sb/pci/uart */ #define CHIPC_M1_MASK 0x3f /* m1 control */ +#define CHIPC_M1_SHIFT 0 #define CHIPC_M2_MASK 0x3f00 /* m2 control */ #define CHIPC_M2_SHIFT 8 #define CHIPC_M3_MASK 0x3f0000 /* m3 control */ @@ -778,345 +745,6 @@ #define CHIPC_UART_IER_ETBEI 2 /* enable transmitter holding register empty interrupt */ #define CHIPC_UART_IER_ERBFI 1 /* enable data available interrupt */ -/* pmustatus */ -#define CHIPC_PST_EXTLPOAVAIL 0x0100 -#define CHIPC_PST_WDRESET 0x0080 -#define CHIPC_PST_INTPEND 0x0040 -#define CHIPC_PST_SBCLKST 0x0030 -#define CHIPC_PST_SBCLKST_ILP 0x0010 -#define CHIPC_PST_SBCLKST_ALP 0x0020 -#define CHIPC_PST_SBCLKST_HT 0x0030 -#define CHIPC_PST_ALPAVAIL 0x0008 -#define CHIPC_PST_HTAVAIL 0x0004 -#define CHIPC_PST_RESINIT 0x0003 - -/* pmucapabilities */ -#define CHIPC_PCAP_REV_MASK 0x000000ff -#define CHIPC_PCAP_RC_MASK 0x00001f00 -#define CHIPC_PCAP_RC_SHIFT 8 -#define CHIPC_PCAP_TC_MASK 0x0001e000 -#define CHIPC_PCAP_TC_SHIFT 13 -#define CHIPC_PCAP_PC_MASK 0x001e0000 -#define CHIPC_PCAP_PC_SHIFT 17 -#define CHIPC_PCAP_VC_MASK 0x01e00000 -#define CHIPC_PCAP_VC_SHIFT 21 -#define CHIPC_PCAP_CC_MASK 0x1e000000 -#define CHIPC_PCAP_CC_SHIFT 25 -#define CHIPC_PCAP5_PC_MASK 0x003e0000 /* PMU corerev >= 5 */ -#define CHIPC_PCAP5_PC_SHIFT 17 -#define CHIPC_PCAP5_VC_MASK 0x07c00000 -#define CHIPC_PCAP5_VC_SHIFT 22 -#define CHIPC_PCAP5_CC_MASK 0xf8000000 -#define CHIPC_PCAP5_CC_SHIFT 27 - -/* PMU Resource Request Timer registers */ -/* This is based on PmuRev0 */ -#define CHIPC_PRRT_TIME_MASK 0x03ff -#define CHIPC_PRRT_INTEN 0x0400 -#define CHIPC_PRRT_REQ_ACTIVE 0x0800 -#define CHIPC_PRRT_ALP_REQ 0x1000 -#define CHIPC_PRRT_HT_REQ 0x2000 - -/* PMU resource bit position */ -#define CHIPC_PMURES_BIT(bit) (1 << (bit)) - -/* PMU resource number limit */ -#define CHIPC_PMURES_MAX_RESNUM 30 - -/* PMU chip control0 register */ -#define CHIPC_PMU_CHIPCTL0 0 - -/* PMU chip control1 register */ -#define CHIPC_PMU_CHIPCTL1 1 -#define CHIPC_PMU_CC1_RXC_DLL_BYPASS 0x00010000 - -#define CHIPC_PMU_CC1_IF_TYPE_MASK 0x00000030 -#define CHIPC_PMU_CC1_IF_TYPE_RMII 0x00000000 -#define CHIPC_PMU_CC1_IF_TYPE_MII 0x00000010 -#define CHIPC_PMU_CC1_IF_TYPE_RGMII 0x00000020 - -#define CHIPC_PMU_CC1_SW_TYPE_MASK 0x000000c0 -#define CHIPC_PMU_CC1_SW_TYPE_EPHY 0x00000000 -#define CHIPC_PMU_CC1_SW_TYPE_EPHYMII 0x00000040 -#define CHIPC_PMU_CC1_SW_TYPE_EPHYRMII 0x00000080 -#define CHIPC_PMU_CC1_SW_TYPE_RGMII 0x000000c0 - -/* PMU corerev and chip specific PLL controls. - * PMU_PLL_XX where is PMU corerev and is an arbitrary number - * to differentiate different PLLs controlled by the same PMU rev. - */ -/* pllcontrol registers */ -/* PDIV, div_phy, div_arm, div_adc, dith_sel, ioff, kpd_scale, lsb_sel, mash_sel, lf_c & lf_r */ -#define CHIPC_PMU0_PLL0_PLLCTL0 0 -#define CHIPC_PMU0_PLL0_PC0_PDIV_MASK 1 -#define CHIPC_PMU0_PLL0_PC0_PDIV_FREQ 25000 -#define CHIPC_PMU0_PLL0_PC0_DIV_ARM_MASK 0x00000038 -#define CHIPC_PMU0_PLL0_PC0_DIV_ARM_SHIFT 3 -#define CHIPC_PMU0_PLL0_PC0_DIV_ARM_BASE 8 - -/* PC0_DIV_ARM for PLLOUT_ARM */ -#define CHIPC_PMU0_PLL0_PC0_DIV_ARM_110MHZ 0 -#define CHIPC_PMU0_PLL0_PC0_DIV_ARM_97_7MHZ 1 -#define CHIPC_PMU0_PLL0_PC0_DIV_ARM_88MHZ 2 -#define CHIPC_PMU0_PLL0_PC0_DIV_ARM_80MHZ 3 /* Default */ -#define CHIPC_PMU0_PLL0_PC0_DIV_ARM_73_3MHZ 4 -#define CHIPC_PMU0_PLL0_PC0_DIV_ARM_67_7MHZ 5 -#define CHIPC_PMU0_PLL0_PC0_DIV_ARM_62_9MHZ 6 -#define CHIPC_PMU0_PLL0_PC0_DIV_ARM_58_6MHZ 7 - -/* Wildcard base, stop_mod, en_lf_tp, en_cal & lf_r2 */ -#define CHIPC_PMU0_PLL0_PLLCTL1 1 -#define CHIPC_PMU0_PLL0_PC1_WILD_INT_MASK 0xf0000000 -#define CHIPC_PMU0_PLL0_PC1_WILD_INT_SHIFT 28 -#define CHIPC_PMU0_PLL0_PC1_WILD_FRAC_MASK 0x0fffff00 -#define CHIPC_PMU0_PLL0_PC1_WILD_FRAC_SHIFT 8 -#define CHIPC_PMU0_PLL0_PC1_STOP_MOD 0x00000040 - -/* Wildcard base, vco_calvar, vco_swc, vco_var_selref, vso_ical & vco_sel_avdd */ -#define CHIPC_PMU0_PLL0_PLLCTL2 2 -#define CHIPC_PMU0_PLL0_PC2_WILD_INT_MASK 0xf -#define CHIPC_PMU0_PLL0_PC2_WILD_INT_SHIFT 4 - -/* pllcontrol registers */ -/* ndiv_pwrdn, pwrdn_ch, refcomp_pwrdn, dly_ch, p1div, p2div, _bypass_sdmod */ -#define CHIPC_PMU1_PLL0_PLLCTL0 0 -#define CHIPC_PMU1_PLL0_PC0_P1DIV_MASK 0x00f00000 -#define CHIPC_PMU1_PLL0_PC0_P1DIV_SHIFT 20 -#define CHIPC_PMU1_PLL0_PC0_P2DIV_MASK 0x0f000000 -#define CHIPC_PMU1_PLL0_PC0_P2DIV_SHIFT 24 - -/* mdiv */ -#define CHIPC_PMU1_PLL0_PLLCTL1 1 -#define CHIPC_PMU1_PLL0_PC1_M1DIV_MASK 0x000000ff -#define CHIPC_PMU1_PLL0_PC1_M1DIV_SHIFT 0 -#define CHIPC_PMU1_PLL0_PC1_M2DIV_MASK 0x0000ff00 -#define CHIPC_PMU1_PLL0_PC1_M2DIV_SHIFT 8 -#define CHIPC_PMU1_PLL0_PC1_M3DIV_MASK 0x00ff0000 -#define CHIPC_PMU1_PLL0_PC1_M3DIV_SHIFT 16 -#define CHIPC_PMU1_PLL0_PC1_M4DIV_MASK 0xff000000 -#define CHIPC_PMU1_PLL0_PC1_M4DIV_SHIFT 24 - -#define CHIPC_DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT 8 -#define CHIPC_DOT11MAC_880MHZ_CLK_DIVISOR_MASK (0xFF << DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT) -#define CHIPC_DOT11MAC_880MHZ_CLK_DIVISOR_VAL (0xE << DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT) - -/* mdiv, ndiv_dither_mfb, ndiv_mode, ndiv_int */ -#define CHIPC_PMU1_PLL0_PLLCTL2 2 -#define CHIPC_PMU1_PLL0_PC2_M5DIV_MASK 0x000000ff -#define CHIPC_PMU1_PLL0_PC2_M5DIV_SHIFT 0 -#define CHIPC_PMU1_PLL0_PC2_M6DIV_MASK 0x0000ff00 -#define CHIPC_PMU1_PLL0_PC2_M6DIV_SHIFT 8 -#define CHIPC_PMU1_PLL0_PC2_NDIV_MODE_MASK 0x000e0000 -#define CHIPC_PMU1_PLL0_PC2_NDIV_MODE_SHIFT 17 -#define CHIPC_PMU1_PLL0_PC2_NDIV_MODE_MASH 1 -#define CHIPC_PMU1_PLL0_PC2_NDIV_MODE_MFB 2 /* recommended for 4319 */ -#define CHIPC_PMU1_PLL0_PC2_NDIV_INT_MASK 0x1ff00000 -#define CHIPC_PMU1_PLL0_PC2_NDIV_INT_SHIFT 20 - -/* ndiv_frac */ -#define CHIPC_PMU1_PLL0_PLLCTL3 3 -#define CHIPC_PMU1_PLL0_PC3_NDIV_FRAC_MASK 0x00ffffff -#define CHIPC_PMU1_PLL0_PC3_NDIV_FRAC_SHIFT 0 - -/* pll_ctrl */ -#define CHIPC_PMU1_PLL0_PLLCTL4 4 - -/* pll_ctrl, vco_rng, clkdrive_ch */ -#define CHIPC_PMU1_PLL0_PLLCTL5 5 -#define CHIPC_PMU1_PLL0_PC5_CLK_DRV_MASK 0xffffff00 -#define CHIPC_PMU1_PLL0_PC5_CLK_DRV_SHIFT 8 - -/* PMU rev 2 control words */ -#define CHIPC_PMU2_PHY_PLL_PLLCTL 4 -#define CHIPC_PMU2_SI_PLL_PLLCTL 10 - -/* PMU rev 2 */ -/* pllcontrol registers */ -/* ndiv_pwrdn, pwrdn_ch, refcomp_pwrdn, dly_ch, p1div, p2div, _bypass_sdmod */ -#define CHIPC_PMU2_PLL_PLLCTL0 0 -#define CHIPC_PMU2_PLL_PC0_P1DIV_MASK 0x00f00000 -#define CHIPC_PMU2_PLL_PC0_P1DIV_SHIFT 20 -#define CHIPC_PMU2_PLL_PC0_P2DIV_MASK 0x0f000000 -#define CHIPC_PMU2_PLL_PC0_P2DIV_SHIFT 24 - -/* mdiv */ -#define CHIPC_PMU2_PLL_PLLCTL1 1 -#define CHIPC_PMU2_PLL_PC1_M1DIV_MASK 0x000000ff -#define CHIPC_PMU2_PLL_PC1_M1DIV_SHIFT 0 -#define CHIPC_PMU2_PLL_PC1_M2DIV_MASK 0x0000ff00 -#define CHIPC_PMU2_PLL_PC1_M2DIV_SHIFT 8 -#define CHIPC_PMU2_PLL_PC1_M3DIV_MASK 0x00ff0000 -#define CHIPC_PMU2_PLL_PC1_M3DIV_SHIFT 16 -#define CHIPC_PMU2_PLL_PC1_M4DIV_MASK 0xff000000 -#define CHIPC_PMU2_PLL_PC1_M4DIV_SHIFT 24 - -/* mdiv, ndiv_dither_mfb, ndiv_mode, ndiv_int */ -#define CHIPC_PMU2_PLL_PLLCTL2 2 -#define CHIPC_PMU2_PLL_PC2_M5DIV_MASK 0x000000ff -#define CHIPC_PMU2_PLL_PC2_M5DIV_SHIFT 0 -#define CHIPC_PMU2_PLL_PC2_M6DIV_MASK 0x0000ff00 -#define CHIPC_PMU2_PLL_PC2_M6DIV_SHIFT 8 -#define CHIPC_PMU2_PLL_PC2_NDIV_MODE_MASK 0x000e0000 -#define CHIPC_PMU2_PLL_PC2_NDIV_MODE_SHIFT 17 -#define CHIPC_PMU2_PLL_PC2_NDIV_INT_MASK 0x1ff00000 -#define CHIPC_PMU2_PLL_PC2_NDIV_INT_SHIFT 20 - -/* ndiv_frac */ -#define CHIPC_PMU2_PLL_PLLCTL3 3 -#define CHIPC_PMU2_PLL_PC3_NDIV_FRAC_MASK 0x00ffffff -#define CHIPC_PMU2_PLL_PC3_NDIV_FRAC_SHIFT 0 - -/* pll_ctrl */ -#define CHIPC_PMU2_PLL_PLLCTL4 4 - -/* pll_ctrl, vco_rng, clkdrive_ch */ -#define CHIPC_PMU2_PLL_PLLCTL5 5 -#define CHIPC_PMU2_PLL_PC5_CLKDRIVE_CH1_MASK 0x00000f00 -#define CHIPC_PMU2_PLL_PC5_CLKDRIVE_CH1_SHIFT 8 -#define CHIPC_PMU2_PLL_PC5_CLKDRIVE_CH2_MASK 0x0000f000 -#define CHIPC_PMU2_PLL_PC5_CLKDRIVE_CH2_SHIFT 12 -#define CHIPC_PMU2_PLL_PC5_CLKDRIVE_CH3_MASK 0x000f0000 -#define CHIPC_PMU2_PLL_PC5_CLKDRIVE_CH3_SHIFT 16 -#define CHIPC_PMU2_PLL_PC5_CLKDRIVE_CH4_MASK 0x00f00000 -#define CHIPC_PMU2_PLL_PC5_CLKDRIVE_CH4_SHIFT 20 -#define CHIPC_PMU2_PLL_PC5_CLKDRIVE_CH5_MASK 0x0f000000 -#define CHIPC_PMU2_PLL_PC5_CLKDRIVE_CH5_SHIFT 24 -#define CHIPC_PMU2_PLL_PC5_CLKDRIVE_CH6_MASK 0xf0000000 -#define CHIPC_PMU2_PLL_PC5_CLKDRIVE_CH6_SHIFT 28 - -/* PMU rev 5 (& 6) */ -#define CHIPC_PMU5_PLL_P1P2_OFF 0 -#define CHIPC_PMU5_PLL_P1_MASK 0x0f000000 -#define CHIPC_PMU5_PLL_P1_SHIFT 24 -#define CHIPC_PMU5_PLL_P2_MASK 0x00f00000 -#define CHIPC_PMU5_PLL_P2_SHIFT 20 -#define CHIPC_PMU5_PLL_M14_OFF 1 -#define CHIPC_PMU5_PLL_MDIV_MASK 0x000000ff -#define CHIPC_PMU5_PLL_MDIV_WIDTH 8 -#define CHIPC_PMU5_PLL_NM5_OFF 2 -#define CHIPC_PMU5_PLL_NDIV_MASK 0xfff00000 -#define CHIPC_PMU5_PLL_NDIV_SHIFT 20 -#define CHIPC_PMU5_PLL_NDIV_MODE_MASK 0x000e0000 -#define CHIPC_PMU5_PLL_NDIV_MODE_SHIFT 17 -#define CHIPC_PMU5_PLL_FMAB_OFF 3 -#define CHIPC_PMU5_PLL_MRAT_MASK 0xf0000000 -#define CHIPC_PMU5_PLL_MRAT_SHIFT 28 -#define CHIPC_PMU5_PLL_ABRAT_MASK 0x08000000 -#define CHIPC_PMU5_PLL_ABRAT_SHIFT 27 -#define CHIPC_PMU5_PLL_FDIV_MASK 0x07ffffff -#define CHIPC_PMU5_PLL_PLLCTL_OFF 4 -#define CHIPC_PMU5_PLL_PCHI_OFF 5 -#define CHIPC_PMU5_PLL_PCHI_MASK 0x0000003f - -/* pmu XtalFreqRatio */ -#define CHIPC_PMU_XTALFREQ_REG_ILPCTR_MASK 0x00001FFF -#define CHIPC_PMU_XTALFREQ_REG_MEASURE_MASK 0x80000000 -#define CHIPC_PMU_XTALFREQ_REG_MEASURE_SHIFT 31 - -/* Divider allocation in 4716/47162/5356/5357 */ -#define CHIPC_PMU5_MAINPLL_CPU 1 -#define CHIPC_PMU5_MAINPLL_MEM 2 -#define CHIPC_PMU5_MAINPLL_SI 3 - -#define CHIPC_PMU7_PLL_PLLCTL7 7 -#define CHIPC_PMU7_PLL_PLLCTL8 8 -#define CHIPC_PMU7_PLL_PLLCTL11 11 - -/* PLL usage in 4716/47162 */ -#define CHIPC_PMU4716_MAINPLL_PLL0 12 - -/* PLL usage in 5356/5357 */ -#define CHIPC_PMU5356_MAINPLL_PLL0 0 -#define CHIPC_PMU5357_MAINPLL_PLL0 0 - -/* 4716/47162 resources */ -#define CHIPC_RES4716_PROC_PLL_ON 0x00000040 -#define CHIPC_RES4716_PROC_HT_AVAIL 0x00000080 - -/* 4716/4717/4718 Chip specific ChipControl register bits */ -#define CHIPC_CCTRL471X_I2S_PINS_ENABLE 0x0080 /* I2S pins off by default, shared with pflash */ - -/* 5354 resources */ -#define CHIPC_RES5354_EXT_SWITCHER_PWM 0 /* 0x00001 */ -#define CHIPC_RES5354_BB_SWITCHER_PWM 1 /* 0x00002 */ -#define CHIPC_RES5354_BB_SWITCHER_BURST 2 /* 0x00004 */ -#define CHIPC_RES5354_BB_EXT_SWITCHER_BURST 3 /* 0x00008 */ -#define CHIPC_RES5354_ILP_REQUEST 4 /* 0x00010 */ -#define CHIPC_RES5354_RADIO_SWITCHER_PWM 5 /* 0x00020 */ -#define CHIPC_RES5354_RADIO_SWITCHER_BURST 6 /* 0x00040 */ -#define CHIPC_RES5354_ROM_SWITCH 7 /* 0x00080 */ -#define CHIPC_RES5354_PA_REF_LDO 8 /* 0x00100 */ -#define CHIPC_RES5354_RADIO_LDO 9 /* 0x00200 */ -#define CHIPC_RES5354_AFE_LDO 10 /* 0x00400 */ -#define CHIPC_RES5354_PLL_LDO 11 /* 0x00800 */ -#define CHIPC_RES5354_BG_FILTBYP 12 /* 0x01000 */ -#define CHIPC_RES5354_TX_FILTBYP 13 /* 0x02000 */ -#define CHIPC_RES5354_RX_FILTBYP 14 /* 0x04000 */ -#define CHIPC_RES5354_XTAL_PU 15 /* 0x08000 */ -#define CHIPC_RES5354_XTAL_EN 16 /* 0x10000 */ -#define CHIPC_RES5354_BB_PLL_FILTBYP 17 /* 0x20000 */ -#define CHIPC_RES5354_RF_PLL_FILTBYP 18 /* 0x40000 */ -#define CHIPC_RES5354_BB_PLL_PU 19 /* 0x80000 */ - -/* 5357 Chip specific ChipControl register bits */ -#define CHIPC_CCTRL5357_EXTPA (1<<14) /* extPA in ChipControl 1, bit 14 */ -#define CHIPC_CCTRL5357_ANT_MUX_2o3 (1<<15) /* 2o3 in ChipControl 1, bit 15 */ - -/* 4328 resources */ -#define CHIPC_RES4328_EXT_SWITCHER_PWM 0 /* 0x00001 */ -#define CHIPC_RES4328_BB_SWITCHER_PWM 1 /* 0x00002 */ -#define CHIPC_RES4328_BB_SWITCHER_BURST 2 /* 0x00004 */ -#define CHIPC_RES4328_BB_EXT_SWITCHER_BURST 3 /* 0x00008 */ -#define CHIPC_RES4328_ILP_REQUEST 4 /* 0x00010 */ -#define CHIPC_RES4328_RADIO_SWITCHER_PWM 5 /* 0x00020 */ -#define CHIPC_RES4328_RADIO_SWITCHER_BURST 6 /* 0x00040 */ -#define CHIPC_RES4328_ROM_SWITCH 7 /* 0x00080 */ -#define CHIPC_RES4328_PA_REF_LDO 8 /* 0x00100 */ -#define CHIPC_RES4328_RADIO_LDO 9 /* 0x00200 */ -#define CHIPC_RES4328_AFE_LDO 10 /* 0x00400 */ -#define CHIPC_RES4328_PLL_LDO 11 /* 0x00800 */ -#define CHIPC_RES4328_BG_FILTBYP 12 /* 0x01000 */ -#define CHIPC_RES4328_TX_FILTBYP 13 /* 0x02000 */ -#define CHIPC_RES4328_RX_FILTBYP 14 /* 0x04000 */ -#define CHIPC_RES4328_XTAL_PU 15 /* 0x08000 */ -#define CHIPC_RES4328_XTAL_EN 16 /* 0x10000 */ -#define CHIPC_RES4328_BB_PLL_FILTBYP 17 /* 0x20000 */ -#define CHIPC_RES4328_RF_PLL_FILTBYP 18 /* 0x40000 */ -#define CHIPC_RES4328_BB_PLL_PU 19 /* 0x80000 */ - -/* 4325 A0/A1 resources */ -#define CHIPC_RES4325_BUCK_BOOST_BURST 0 /* 0x00000001 */ -#define CHIPC_RES4325_CBUCK_BURST 1 /* 0x00000002 */ -#define CHIPC_RES4325_CBUCK_PWM 2 /* 0x00000004 */ -#define CHIPC_RES4325_CLDO_CBUCK_BURST 3 /* 0x00000008 */ -#define CHIPC_RES4325_CLDO_CBUCK_PWM 4 /* 0x00000010 */ -#define CHIPC_RES4325_BUCK_BOOST_PWM 5 /* 0x00000020 */ -#define CHIPC_RES4325_ILP_REQUEST 6 /* 0x00000040 */ -#define CHIPC_RES4325_ABUCK_BURST 7 /* 0x00000080 */ -#define CHIPC_RES4325_ABUCK_PWM 8 /* 0x00000100 */ -#define CHIPC_RES4325_LNLDO1_PU 9 /* 0x00000200 */ -#define CHIPC_RES4325_OTP_PU 10 /* 0x00000400 */ -#define CHIPC_RES4325_LNLDO3_PU 11 /* 0x00000800 */ -#define CHIPC_RES4325_LNLDO4_PU 12 /* 0x00001000 */ -#define CHIPC_RES4325_XTAL_PU 13 /* 0x00002000 */ -#define CHIPC_RES4325_ALP_AVAIL 14 /* 0x00004000 */ -#define CHIPC_RES4325_RX_PWRSW_PU 15 /* 0x00008000 */ -#define CHIPC_RES4325_TX_PWRSW_PU 16 /* 0x00010000 */ -#define CHIPC_RES4325_RFPLL_PWRSW_PU 17 /* 0x00020000 */ -#define CHIPC_RES4325_LOGEN_PWRSW_PU 18 /* 0x00040000 */ -#define CHIPC_RES4325_AFE_PWRSW_PU 19 /* 0x00080000 */ -#define CHIPC_RES4325_BBPLL_PWRSW_PU 20 /* 0x00100000 */ -#define CHIPC_RES4325_HT_AVAIL 21 /* 0x00200000 */ - -/* 4325 B0/C0 resources */ -#define CHIPC_RES4325B0_CBUCK_LPOM 1 /* 0x00000002 */ -#define CHIPC_RES4325B0_CBUCK_BURST 2 /* 0x00000004 */ -#define CHIPC_RES4325B0_CBUCK_PWM 3 /* 0x00000008 */ -#define CHIPC_RES4325B0_CLDO_PU 4 /* 0x00000010 */ - -/* 4325 C1 resources */ -#define CHIPC_RES4325C1_LNLDO2_PU 12 /* 0x00001000 */ - /* 4325 chip-specific ChipStatus register bits */ #define CHIPC_CST4325_SPROM_OTP_SEL_MASK CHIPC_CST_SPROM_OTP_SEL_R22_MASK #define CHIPC_CST4325_SPROM_OTP_SEL_SHIFT CHIPC_CST_SPROM_OTP_SEL_R22_SHIFT @@ -1129,29 +757,6 @@ #define CHIPC_CST4325_PMUTOP_2B_MASK 0x00000200 /* 1 for 2b, 0 for to 2a */ #define CHIPC_CST4325_PMUTOP_2B_SHIFT 9 -#define CHIPC_RES4329_RESERVED0 0 /* 0x00000001 */ -#define CHIPC_RES4329_CBUCK_LPOM 1 /* 0x00000002 */ -#define CHIPC_RES4329_CBUCK_BURST 2 /* 0x00000004 */ -#define CHIPC_RES4329_CBUCK_PWM 3 /* 0x00000008 */ -#define CHIPC_RES4329_CLDO_PU 4 /* 0x00000010 */ -#define CHIPC_RES4329_PALDO_PU 5 /* 0x00000020 */ -#define CHIPC_RES4329_ILP_REQUEST 6 /* 0x00000040 */ -#define CHIPC_RES4329_RESERVED7 7 /* 0x00000080 */ -#define CHIPC_RES4329_RESERVED8 8 /* 0x00000100 */ -#define CHIPC_RES4329_LNLDO1_PU 9 /* 0x00000200 */ -#define CHIPC_RES4329_OTP_PU 10 /* 0x00000400 */ -#define CHIPC_RES4329_RESERVED11 11 /* 0x00000800 */ -#define CHIPC_RES4329_LNLDO2_PU 12 /* 0x00001000 */ -#define CHIPC_RES4329_XTAL_PU 13 /* 0x00002000 */ -#define CHIPC_RES4329_ALP_AVAIL 14 /* 0x00004000 */ -#define CHIPC_RES4329_RX_PWRSW_PU 15 /* 0x00008000 */ -#define CHIPC_RES4329_TX_PWRSW_PU 16 /* 0x00010000 */ -#define CHIPC_RES4329_RFPLL_PWRSW_PU 17 /* 0x00020000 */ -#define CHIPC_RES4329_LOGEN_PWRSW_PU 18 /* 0x00040000 */ -#define CHIPC_RES4329_AFE_PWRSW_PU 19 /* 0x00080000 */ -#define CHIPC_RES4329_BBPLL_PWRSW_PU 20 /* 0x00100000 */ -#define CHIPC_RES4329_HT_AVAIL 21 /* 0x00200000 */ - /* 4329 chip-specific ChipStatus register bits */ #define CHIPC_CST4329_SPROM_OTP_SEL_MASK CHIPC_CST_SPROM_OTP_SEL_R22_MASK #define CHIPC_CST4329_SPROM_OTP_SEL_SHIFT CHIPC_CST_SPROM_OTP_SEL_R22_SHIFT @@ -1162,33 +767,6 @@ #define CHIPC_CST4312_SPROM_OTP_SEL_MASK CHIPC_CST_SPROM_OTP_SEL_R22_MASK #define CHIPC_CST4312_SPROM_OTP_SEL_SHIFT CHIPC_CST_SPROM_OTP_SEL_R22_SHIFT -/* 4312 resources (all PMU chips with little memory constraint) */ -#define CHIPC_RES4312_SWITCHER_BURST 0 /* 0x00000001 */ -#define CHIPC_RES4312_SWITCHER_PWM 1 /* 0x00000002 */ -#define CHIPC_RES4312_PA_REF_LDO 2 /* 0x00000004 */ -#define CHIPC_RES4312_CORE_LDO_BURST 3 /* 0x00000008 */ -#define CHIPC_RES4312_CORE_LDO_PWM 4 /* 0x00000010 */ -#define CHIPC_RES4312_RADIO_LDO 5 /* 0x00000020 */ -#define CHIPC_RES4312_ILP_REQUEST 6 /* 0x00000040 */ -#define CHIPC_RES4312_BG_FILTBYP 7 /* 0x00000080 */ -#define CHIPC_RES4312_TX_FILTBYP 8 /* 0x00000100 */ -#define CHIPC_RES4312_RX_FILTBYP 9 /* 0x00000200 */ -#define CHIPC_RES4312_XTAL_PU 10 /* 0x00000400 */ -#define CHIPC_RES4312_ALP_AVAIL 11 /* 0x00000800 */ -#define CHIPC_RES4312_BB_PLL_FILTBYP 12 /* 0x00001000 */ -#define CHIPC_RES4312_RF_PLL_FILTBYP 13 /* 0x00002000 */ -#define CHIPC_RES4312_HT_AVAIL 14 /* 0x00004000 */ - -/* 4322 resources */ -#define CHIPC_RES4322_RF_LDO 0 -#define CHIPC_RES4322_ILP_REQUEST 1 -#define CHIPC_RES4322_XTAL_PU 2 -#define CHIPC_RES4322_ALP_AVAIL 3 -#define CHIPC_RES4322_SI_PLL_ON 4 -#define CHIPC_RES4322_HT_SI_AVAIL 5 -#define CHIPC_RES4322_PHY_PLL_ON 6 -#define CHIPC_RES4322_HT_PHY_AVAIL 7 -#define CHIPC_RES4322_OTP_PU 8 /* 4322 chip-specific ChipStatus register bits */ #define CHIPC_CST4322_XTAL_FREQ_20_40MHZ 0x00000020 @@ -1217,26 +795,6 @@ #define CHIPC_CST4322_CLK_SWITCH_PCI_TO_ALP 0x00020000 #define CHIPC_CST4322_PCI_CARDBUS_MODE 0x00040000 -/* 43224 chip-specific ChipControl register bits */ -#define CHIPC_CCTRL43224_GPIO_TOGGLE 0x8000 -#define CHIPC_CCTRL_43224A0_12MA_LED_DRIVE 0x00F000F0 /* 12 mA drive strength */ -#define CHIPC_CCTRL_43224B0_12MA_LED_DRIVE 0xF0 /* 12 mA drive strength for later 43224s */ - -/* 43236 resources */ -#define CHIPC_RES43236_REGULATOR 0 -#define CHIPC_RES43236_ILP_REQUEST 1 -#define CHIPC_RES43236_XTAL_PU 2 -#define CHIPC_RES43236_ALP_AVAIL 3 -#define CHIPC_RES43236_SI_PLL_ON 4 -#define CHIPC_RES43236_HT_SI_AVAIL 5 - -/* 43236 chip-specific ChipControl register bits */ -#define CHIPC_CCTRL43236_BT_COEXIST (1<<0) /* 0 disable */ -#define CHIPC_CCTRL43236_SECI (1<<1) /* 0 SECI is disabled (JATG functional) */ -#define CHIPC_CCTRL43236_EXT_LNA (1<<2) /* 0 disable */ -#define CHIPC_CCTRL43236_ANT_MUX_2o3 (1<<3) /* 2o3 mux, chipcontrol bit 3 */ -#define CHIPC_CCTRL43236_GSIO (1<<4) /* 0 disable */ - /* 43236 Chip specific ChipStatus register bits */ #define CHIPC_CST43236_SFLASH_MASK 0x00000040 #define CHIPC_CST43236_OTP_SEL_MASK 0x00000080 @@ -1250,15 +808,17 @@ #define CHIPC_CST43236_BOOT_FROM_FLASH 2 /* boot from FLASH */ #define CHIPC_CST43236_BOOT_FROM_INVALID 3 -/* 4331 resources */ -#define CHIPC_RES4331_REGULATOR 0 -#define CHIPC_RES4331_ILP_REQUEST 1 -#define CHIPC_RES4331_XTAL_PU 2 -#define CHIPC_RES4331_ALP_AVAIL 3 -#define CHIPC_RES4331_SI_PLL_ON 4 -#define CHIPC_RES4331_HT_SI_AVAIL 5 +/* 43237 Chip specific ChipStatus register bits */ +#define CHIPC_CST43237_BP_CLK 0x00000200 /* 96/80Mbps */ + +/* 4331 Chip specific ChipStatus register bits */ +#define CHIPC_CST4331_XTAL_FREQ 0x00000001 /* crystal frequency 20/40Mhz */ +#define CHIPC_CST4331_SPROM_PRESENT 0x00000002 +#define CHIPC_CST4331_OTP_PRESENT 0x00000004 +#define CHIPC_CST4331_LDO_RF 0x00000008 +#define CHIPC_CST4331_LDO_PAR 0x00000010 -/* 4331 chip-specific ChipControl register bits */ +/* 4331 chip-specific CHIPCTRL register bits */ #define CHIPC_CCTRL4331_BT_COEXIST (1<<0) /* 0 disable */ #define CHIPC_CCTRL4331_SECI (1<<1) /* 0 SECI is disabled (JATG functional) */ #define CHIPC_CCTRL4331_EXT_LNA (1<<2) /* 0 disable */ @@ -1275,33 +835,6 @@ #define CHIPC_CCTRL4331_BT_SHD0_ON_GPIO4 (1<<16) /* enable bt_shd0 at gpio4 */ #define CHIPC_CCTRL4331_BT_SHD1_ON_GPIO5 (1<<17) /* enable bt_shd1 at gpio5 */ -/* 4331 Chip specific ChipStatus register bits */ -#define CHIPC_CST4331_XTAL_FREQ 0x00000001 /* crystal frequency 20/40Mhz */ -#define CHIPC_CST4331_SPROM_PRESENT 0x00000002 -#define CHIPC_CST4331_OTP_PRESENT 0x00000004 -#define CHIPC_CST4331_LDO_RF 0x00000008 -#define CHIPC_CST4331_LDO_PAR 0x00000010 - -/* 4315 resources */ -#define CHIPC_RES4315_CBUCK_LPOM 1 /* 0x00000002 */ -#define CHIPC_RES4315_CBUCK_BURST 2 /* 0x00000004 */ -#define CHIPC_RES4315_CBUCK_PWM 3 /* 0x00000008 */ -#define CHIPC_RES4315_CLDO_PU 4 /* 0x00000010 */ -#define CHIPC_RES4315_PALDO_PU 5 /* 0x00000020 */ -#define CHIPC_RES4315_ILP_REQUEST 6 /* 0x00000040 */ -#define CHIPC_RES4315_LNLDO1_PU 9 /* 0x00000200 */ -#define CHIPC_RES4315_OTP_PU 10 /* 0x00000400 */ -#define CHIPC_RES4315_LNLDO2_PU 12 /* 0x00001000 */ -#define CHIPC_RES4315_XTAL_PU 13 /* 0x00002000 */ -#define CHIPC_RES4315_ALP_AVAIL 14 /* 0x00004000 */ -#define CHIPC_RES4315_RX_PWRSW_PU 15 /* 0x00008000 */ -#define CHIPC_RES4315_TX_PWRSW_PU 16 /* 0x00010000 */ -#define CHIPC_RES4315_RFPLL_PWRSW_PU 17 /* 0x00020000 */ -#define CHIPC_RES4315_LOGEN_PWRSW_PU 18 /* 0x00040000 */ -#define CHIPC_RES4315_AFE_PWRSW_PU 19 /* 0x00080000 */ -#define CHIPC_RES4315_BBPLL_PWRSW_PU 20 /* 0x00100000 */ -#define CHIPC_RES4315_HT_AVAIL 21 /* 0x00200000 */ - /* 4315 chip-specific ChipStatus register bits */ #define CHIPC_CST4315_SPROM_OTP_SEL_MASK CHIPC_CST_SPROM_OTP_SEL_R22_MASK #define CHIPC_CST4315_SPROM_OTP_SEL_SHIFT CHIPC_CST_SPROM_OTP_SEL_R22_SHIFT @@ -1314,26 +847,6 @@ #define CHIPC_CST4315_CBUCK_MODE_BURST 0x00000400 #define CHIPC_CST4315_CBUCK_MODE_LPBURST 0x00000c00 -/* 4319 resources */ -#define CHIPC_RES4319_CBUCK_LPOM 1 /* 0x00000002 */ -#define CHIPC_RES4319_CBUCK_BURST 2 /* 0x00000004 */ -#define CHIPC_RES4319_CBUCK_PWM 3 /* 0x00000008 */ -#define CHIPC_RES4319_CLDO_PU 4 /* 0x00000010 */ -#define CHIPC_RES4319_PALDO_PU 5 /* 0x00000020 */ -#define CHIPC_RES4319_ILP_REQUEST 6 /* 0x00000040 */ -#define CHIPC_RES4319_LNLDO1_PU 9 /* 0x00000200 */ -#define CHIPC_RES4319_OTP_PU 10 /* 0x00000400 */ -#define CHIPC_RES4319_LNLDO2_PU 12 /* 0x00001000 */ -#define CHIPC_RES4319_XTAL_PU 13 /* 0x00002000 */ -#define CHIPC_RES4319_ALP_AVAIL 14 /* 0x00004000 */ -#define CHIPC_RES4319_RX_PWRSW_PU 15 /* 0x00008000 */ -#define CHIPC_RES4319_TX_PWRSW_PU 16 /* 0x00010000 */ -#define CHIPC_RES4319_RFPLL_PWRSW_PU 17 /* 0x00020000 */ -#define CHIPC_RES4319_LOGEN_PWRSW_PU 18 /* 0x00040000 */ -#define CHIPC_RES4319_AFE_PWRSW_PU 19 /* 0x00080000 */ -#define CHIPC_RES4319_BBPLL_PWRSW_PU 20 /* 0x00100000 */ -#define CHIPC_RES4319_HT_AVAIL 21 /* 0x00200000 */ - /* 4319 chip-specific ChipStatus register bits */ #define CHIPC_CST4319_SPI_CPULESSUSB 0x00000001 #define CHIPC_CST4319_SPI_CLK_POL 0x00000002 @@ -1354,42 +867,6 @@ #define CHIPC_CST4319_RCAL_VALUE_MASK 0x3e000000 #define CHIPC_CST4319_RCAL_VALUE_SHIFT 25 -#define CHIPC_PMU1_PLL0_CHIPCTL0 0 -#define CHIPC_PMU1_PLL0_CHIPCTL1 1 -#define CHIPC_PMU1_PLL0_CHIPCTL2 2 -#define CHIPC_CCTL_4319USB_XTAL_SEL_MASK 0x00180000 -#define CHIPC_CCTL_4319USB_XTAL_SEL_SHIFT 19 -#define CHIPC_CCTL_4319USB_48MHZ_PLL_SEL 1 -#define CHIPC_CCTL_4319USB_24MHZ_PLL_SEL 2 - -/* PMU resources for 4336 */ -#define CHIPC_RES4336_CBUCK_LPOM 0 -#define CHIPC_RES4336_CBUCK_BURST 1 -#define CHIPC_RES4336_CBUCK_LP_PWM 2 -#define CHIPC_RES4336_CBUCK_PWM 3 -#define CHIPC_RES4336_CLDO_PU 4 -#define CHIPC_RES4336_DIS_INT_RESET_PD 5 -#define CHIPC_RES4336_ILP_REQUEST 6 -#define CHIPC_RES4336_LNLDO_PU 7 -#define CHIPC_RES4336_LDO3P3_PU 8 -#define CHIPC_RES4336_OTP_PU 9 -#define CHIPC_RES4336_XTAL_PU 10 -#define CHIPC_RES4336_ALP_AVAIL 11 -#define CHIPC_RES4336_RADIO_PU 12 -#define CHIPC_RES4336_BG_PU 13 -#define CHIPC_RES4336_VREG1p4_PU_PU 14 -#define CHIPC_RES4336_AFE_PWRSW_PU 15 -#define CHIPC_RES4336_RX_PWRSW_PU 16 -#define CHIPC_RES4336_TX_PWRSW_PU 17 -#define CHIPC_RES4336_BB_PWRSW_PU 18 -#define CHIPC_RES4336_SYNTH_PWRSW_PU 19 -#define CHIPC_RES4336_MISC_PWRSW_PU 20 -#define CHIPC_RES4336_LOGEN_PWRSW_PU 21 -#define CHIPC_RES4336_BBPLL_PWRSW_PU 22 -#define CHIPC_RES4336_MACPHY_CLKAVAIL 23 -#define CHIPC_RES4336_HT_AVAIL 24 -#define CHIPC_RES4336_RSVD 25 - /* 4336 chip-specific ChipStatus register bits */ #define CHIPC_CST4336_SPI_MODE_MASK 0x00000001 #define CHIPC_CST4336_SPROM_PRESENT 0x00000002 @@ -1406,36 +883,6 @@ #define CHIPC_CST4336_CBUCK_MODE_MASK 0x00000600 #define CHIPC_CST4336_CBUCK_MODE_SHIFT 9 -/* 4330 resources */ -#define CHIPC_RES4330_CBUCK_LPOM 0 -#define CHIPC_RES4330_CBUCK_BURST 1 -#define CHIPC_RES4330_CBUCK_LP_PWM 2 -#define CHIPC_RES4330_CBUCK_PWM 3 -#define CHIPC_RES4330_CLDO_PU 4 -#define CHIPC_RES4330_DIS_INT_RESET_PD 5 -#define CHIPC_RES4330_ILP_REQUEST 6 -#define CHIPC_RES4330_LNLDO_PU 7 -#define CHIPC_RES4330_LDO3P3_PU 8 -#define CHIPC_RES4330_OTP_PU 9 -#define CHIPC_RES4330_XTAL_PU 10 -#define CHIPC_RES4330_ALP_AVAIL 11 -#define CHIPC_RES4330_RADIO_PU 12 -#define CHIPC_RES4330_BG_PU 13 -#define CHIPC_RES4330_VREG1p4_PU_PU 14 -#define CHIPC_RES4330_AFE_PWRSW_PU 15 -#define CHIPC_RES4330_RX_PWRSW_PU 16 -#define CHIPC_RES4330_TX_PWRSW_PU 17 -#define CHIPC_RES4330_BB_PWRSW_PU 18 -#define CHIPC_RES4330_SYNTH_PWRSW_PU 19 -#define CHIPC_RES4330_MISC_PWRSW_PU 20 -#define CHIPC_RES4330_LOGEN_PWRSW_PU 21 -#define CHIPC_RES4330_BBPLL_PWRSW_PU 22 -#define CHIPC_RES4330_MACPHY_CLKAVAIL 23 -#define CHIPC_RES4330_HT_AVAIL 24 -#define CHIPC_RES4330_5gRX_PWRSW_PU 25 -#define CHIPC_RES4330_5gTX_PWRSW_PU 26 -#define CHIPC_RES4330_5g_LOGEN_PWRSW_PU 27 - /* 4330 chip-specific ChipStatus register bits */ #define CHIPC_CST4330_CHIPMODE_SDIOD(cs) (((cs) & 0x7) < 6) /* SDIO || gSPI */ #define CHIPC_CST4330_CHIPMODE_USB20D(cs) (((cs) & 0x7) >= 6) /* USB || USBDA */ @@ -1458,41 +905,12 @@ #define CHIPC_SOCDEVRAM_4330_BP_ADDR 0x1E000000 #define CHIPC_SOCDEVRAM_4330_ARM_ADDR 0x00800000 -/* 4313 resources */ -#define CHIPC_RES4313_BB_PU_RSRC 0 -#define CHIPC_RES4313_ILP_REQ_RSRC 1 -#define CHIPC_RES4313_XTAL_PU_RSRC 2 -#define CHIPC_RES4313_ALP_AVAIL_RSRC 3 -#define CHIPC_RES4313_RADIO_PU_RSRC 4 -#define CHIPC_RES4313_BG_PU_RSRC 5 -#define CHIPC_RES4313_VREG1P4_PU_RSRC 6 -#define CHIPC_RES4313_AFE_PWRSW_RSRC 7 -#define CHIPC_RES4313_RX_PWRSW_RSRC 8 -#define CHIPC_RES4313_TX_PWRSW_RSRC 9 -#define CHIPC_RES4313_BB_PWRSW_RSRC 10 -#define CHIPC_RES4313_SYNTH_PWRSW_RSRC 11 -#define CHIPC_RES4313_MISC_PWRSW_RSRC 12 -#define CHIPC_RES4313_BB_PLL_PWRSW_RSRC 13 -#define CHIPC_RES4313_HT_AVAIL_RSRC 14 -#define CHIPC_RES4313_MACPHY_CLK_AVAIL_RSRC 15 - /* 4313 chip-specific ChipStatus register bits */ #define CHIPC_CST4313_SPROM_PRESENT 1 #define CHIPC_CST4313_OTP_PRESENT 2 #define CHIPC_CST4313_SPROM_OTP_SEL_MASK 0x00000002 #define CHIPC_CST4313_SPROM_OTP_SEL_SHIFT 0 -/* 4313 Chip specific ChipControl register bits */ -#define CHIPC_CCTRL_4313_12MA_LED_DRIVE 0x00000007 /* 12 mA drive strengh for later 4313 */ - -/* 43228 resources */ -#define CHIPC_RES43228_NOT_USED 0 -#define CHIPC_RES43228_ILP_REQUEST 1 -#define CHIPC_RES43228_XTAL_PU 2 -#define CHIPC_RES43228_ALP_AVAIL 3 -#define CHIPC_RES43228_PLL_EN 4 -#define CHIPC_RES43228_HT_PHY_AVAIL 5 - /* 43228 chipstatus reg bits */ #define CHIPC_CST43228_ILP_DIV_EN 0x1 #define CHIPC_CST43228_OTP_PRESENT 0x2 @@ -1503,15 +921,6 @@ #define CHIPC_CST43228_SDIO_RESET 0x20 /* -* Maximum delay for the PMU state transition in us. -* This is an upper bound intended for spinwaits etc. -*/ -#define CHIPC_PMU_MAX_TRANSITION_DLY 15000 - -/* PMU resource up transition time in ILP cycles */ -#define CHIPC_PMURES_UP_TRANSITION 2 - -/* * Register eci_inputlo bitfield values. * - BT packet type information bits [7:0] */ Index: sys/dev/bhnd/cores/chipc/chipcvar.h =================================================================== --- sys/dev/bhnd/cores/chipc/chipcvar.h +++ sys/dev/bhnd/cores/chipc/chipcvar.h @@ -39,71 +39,15 @@ #include "chipc.h" -DECLARE_CLASS(bhnd_chipc); +DECLARE_CLASS(bhnd_chipc_driver); extern devclass_t bhnd_chipc_devclass; struct chipc_region; -/** - * Supported ChipCommon flash types. - */ -typedef enum { - CHIPC_FLASH_NONE = 0, /**< No flash, or a type unrecognized - by the ChipCommon driver */ - CHIPC_PFLASH_CFI = 1, /**< CFI-compatible parallel flash */ - CHIPC_SFLASH_ST = 2, /**< ST serial flash */ - CHIPC_SFLASH_AT = 3, /**< Atmel serial flash */ - CHIPC_QSFLASH_ST = 4, /**< ST quad-SPI flash */ - CHIPC_QSFLASH_AT = 5, /**< Atmel quad-SPI flash */ - CHIPC_NFLASH = 6, /**< NAND flash */ - CHIPC_NFLASH_4706 = 7 /**< BCM4706 NAND flash */ -} chipc_flash; - const char *chipc_flash_name(chipc_flash type); const char *chipc_flash_bus_name(chipc_flash type); const char *chipc_sflash_device_name(chipc_flash type); -/** - * ChipCommon capability flags; - */ -struct chipc_caps { - uint8_t num_uarts; /**< Number of attached UARTS (1-3) */ - bool mipseb; /**< MIPS is big-endian */ - uint8_t uart_clock; /**< UART clock source (see CHIPC_CAP_UCLKSEL_*) */ - uint8_t uart_gpio; /**< UARTs own GPIO pins 12-15 */ - - uint8_t extbus_type; /**< ExtBus type (CHIPC_CAP_EXTBUS_*) */ - - chipc_flash flash_type; /**< flash type */ - uint8_t cfi_width; /**< CFI bus width, 0 if unknown or CFI - not present */ - - bhnd_nvram_src nvram_src; /**< identified NVRAM source */ - bus_size_t sprom_offset; /**< Offset to SPROM data within - SPROM/OTP, 0 if unknown or not - present */ - uint8_t otp_size; /**< OTP (row?) size, 0 if not present */ - - uint8_t pll_type; /**< PLL type */ - bool power_control; /**< Power control available */ - bool jtag_master; /**< JTAG Master present */ - bool boot_rom; /**< Internal boot ROM is active */ - uint8_t backplane_64; /**< Backplane supports 64-bit addressing. - Note that this does not gaurantee - the CPU itself supports 64-bit - addressing. */ - bool pmu; /**< PMU is present. */ - bool eci; /**< ECI (enhanced coexistence inteface) is present. */ - bool seci; /**< SECI (serial ECI) is present */ - bool sprom; /**< SPROM is present */ - bool gsio; /**< GSIO (SPI/I2C) present */ - bool aob; /**< AOB (always on bus) present. - If set, PMU and GCI registers are - not accessible via ChipCommon, - and are instead accessible via - dedicated cores on the bhnd bus */ -}; - /* * ChipCommon device quirks / features */ Index: sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c =================================================================== --- /dev/null +++ sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c @@ -0,0 +1,476 @@ +/*- + * Copyright (c) 2016 Landon Fuller + * Copyright (c) 2010, Broadcom Corporation. + * All rights reserved. + * + * This file is 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 + * 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. + * + * $Id: siutils.c,v 1.821.2.48 2011-02-11 20:59:28 Exp $ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include "bhnd_chipc_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. + */ + +typedef enum { + BHND_PWRCTL_WAR_UP, /**< apply attach/resume workarounds */ + BHND_PWRCTL_WAR_RUN, /**< apply running workarounds */ + BHND_PWRCTL_WAR_DOWN, /**< apply detach/suspend workarounds */ +} bhnd_pwrctl_wars; + +static int bhnd_pwrctl_updateclk(struct bhnd_pwrctl_softc *sc, + bhnd_pwrctl_wars wars); + +static struct bhnd_device_quirk pwrctl_quirks[]; + + +/* Supported parent core device identifiers */ +static const struct bhnd_device pwrctl_devices[] = { + BHND_DEVICE(BCM, CC, "ChipCommon Power Control", pwrctl_quirks), + BHND_DEVICE_END +}; + +/* Device quirks table */ +static struct bhnd_device_quirk pwrctl_quirks[] = { + BHND_CORE_QUIRK (HWREV_LTE(5), PWRCTL_QUIRK_PCICLK_CTL), + BHND_CORE_QUIRK (HWREV_RANGE(6, 9), PWRCTL_QUIRK_SLOWCLK_CTL), + BHND_CORE_QUIRK (HWREV_RANGE(10, 19), PWRCTL_QUIRK_INSTACLK_CTL), + + BHND_DEVICE_QUIRK_END +}; + +static int +bhnd_pwrctl_probe(device_t dev) +{ + const struct bhnd_device *id; + struct chipc_caps *ccaps; + device_t chipc; + + /* Look for compatible chipc parent */ + chipc = device_get_parent(dev); + if (device_get_devclass(chipc) != devclass_find("bhnd_chipc")) + return (ENXIO); + + if (device_get_driver(chipc) != &bhnd_chipc_driver) + return (ENXIO); + + /* Verify chipc capability flags */ + ccaps = BHND_CHIPC_GET_CAPS(chipc); + if (ccaps->pmu || !ccaps->pwr_ctrl) + return (ENXIO); + + /* Check for chipc device match */ + id = bhnd_device_lookup(chipc, pwrctl_devices, + sizeof(pwrctl_devices[0])); + if (id == NULL) + return (ENXIO); + + device_set_desc(dev, id->desc); + return (BUS_PROBE_NOWILDCARD); +} + +static int +bhnd_pwrctl_attach(device_t dev) +{ + struct bhnd_pwrctl_softc *sc; + const struct bhnd_chipid *cid; + struct chipc_softc *chipc_sc; + bhnd_devclass_t hostb_class; + device_t hostb_dev; + int error; + + sc = device_get_softc(dev); + + sc->dev = dev; + sc->chipc_dev = device_get_parent(dev); + sc->quirks = bhnd_device_quirks(sc->chipc_dev, pwrctl_devices, + sizeof(pwrctl_devices[0])); + + /* On devices that lack a slow clock source, HT must always be + * enabled. */ + hostb_class = BHND_DEVCLASS_INVALID; + hostb_dev = bhnd_find_hostb_device(device_get_parent(sc->chipc_dev)); + if (hostb_dev != NULL) + hostb_class = bhnd_get_class(hostb_dev); + + cid = bhnd_get_chipid(sc->chipc_dev); + switch (cid->chip_id) { + case BHND_CHIPID_BCM4311: + if (cid->chip_rev <= 1 && hostb_class == BHND_DEVCLASS_PCI) + sc->quirks |= PWRCTL_QUIRK_FORCE_HT; + break; + + case BHND_CHIPID_BCM4321: + if (hostb_class == BHND_DEVCLASS_PCIE || + hostb_class == BHND_DEVCLASS_PCI) + sc->quirks |= PWRCTL_QUIRK_FORCE_HT; + break; + + case BHND_CHIPID_BCM4716: + if (hostb_class == BHND_DEVCLASS_PCIE) + sc->quirks |= PWRCTL_QUIRK_FORCE_HT; + break; + } + + /* Fetch core register block from ChipCommon parent */ + chipc_sc = device_get_softc(sc->chipc_dev); + sc->res = chipc_sc->core; + + PWRCTL_LOCK_INIT(sc); + STAILQ_INIT(&sc->clkres_list); + + /* Initialize power control */ + PWRCTL_LOCK(sc); + + if ((error = bhnd_pwrctl_init(sc))) { + PWRCTL_UNLOCK(sc); + goto cleanup; + } + + /* Apply default clock transitions */ + if ((error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_UP))) { + PWRCTL_UNLOCK(sc); + goto cleanup; + } + + PWRCTL_UNLOCK(sc); + + return (0); + +cleanup: + PWRCTL_LOCK_DESTROY(sc); + return (error); +} + +static int +bhnd_pwrctl_detach(device_t dev) +{ + struct bhnd_pwrctl_softc *sc; + struct bhnd_pwrctl_clkres *clkres, *crnext; + int error; + + sc = device_get_softc(dev); + + if ((error = bhnd_pwrctl_setclk(sc, BHND_CLOCK_DYN))) + return (error); + + STAILQ_FOREACH_SAFE(clkres, &sc->clkres_list, cr_link, crnext) + free(clkres, M_DEVBUF); + + PWRCTL_LOCK_DESTROY(sc); + return (0); +} + +static int +bhnd_pwrctl_suspend(device_t dev) +{ + struct bhnd_pwrctl_softc *sc; + int error; + + sc = device_get_softc(dev); + + /* Update clock state */ + PWRCTL_LOCK(sc); + error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_DOWN); + PWRCTL_UNLOCK(sc); + + return (error); +} + +static int +bhnd_pwrctl_resume(device_t dev) +{ + struct bhnd_pwrctl_softc *sc; + int error; + + sc = device_get_softc(dev); + + PWRCTL_LOCK(sc); + + /* Re-initialize power control registers */ + if ((error = bhnd_pwrctl_init(sc))) { + device_printf(sc->dev, "PWRCTL init failed: %d\n", error); + goto cleanup; + } + + /* Restore clock state */ + if ((error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_UP))) { + device_printf(sc->dev, "clock state restore failed: %d\n", + error); + goto cleanup; + } + +cleanup: + PWRCTL_UNLOCK(sc); + return (error); +} + +/** + * Find the clock reservation associated with @p pinfo, if any. + * + * @param sc Driver instance state. + * @param pinfo PMU info for device. + */ +static struct bhnd_pwrctl_clkres * +bhnd_pwrctl_find_res(struct bhnd_pwrctl_softc *sc, + struct bhnd_core_pmu_info *pinfo) +{ + 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) + return (clkres); + } + + /* not found */ + return (NULL); +} + +/** + * Enumerate all active clock requests, compute the minimum required clock, + * and issue any required clock transition. + * + * @param sc Driver instance state. + * @param wars Work-around state. + */ +static int +bhnd_pwrctl_updateclk(struct bhnd_pwrctl_softc *sc, bhnd_pwrctl_wars wars) +{ + struct bhnd_pwrctl_clkres *clkres; + bhnd_clock clock; + + PWRCTL_LOCK_ASSERT(sc, MA_OWNED); + + /* Default clock target */ + clock = BHND_CLOCK_DYN; + + /* Apply quirk-specific overrides to the clock target */ + switch (wars) { + case BHND_PWRCTL_WAR_UP: + /* Force HT clock */ + if (PWRCTL_QUIRK(sc, FORCE_HT)) + clock = BHND_CLOCK_HT; + break; + + case BHND_PWRCTL_WAR_RUN: + /* Cannot transition clock if FORCE_HT */ + if (PWRCTL_QUIRK(sc, FORCE_HT)) + return (0); + break; + + case BHND_PWRCTL_WAR_DOWN: + /* Leave default clock unmodified to permit + * transition back to BHND_CLOCK_DYN on FORCE_HT devices. */ + break; + } + + /* Determine required clock */ + STAILQ_FOREACH(clkres, &sc->clkres_list, cr_link) + clock = bhnd_clock_max(clock, clkres->clock); + + /* Map to supported clock setting */ + switch (clock) { + case BHND_CLOCK_DYN: + case BHND_CLOCK_ILP: + clock = BHND_CLOCK_DYN; + break; + case BHND_CLOCK_ALP: + /* In theory FORCE_ALP is supported by the hardware, but + * there are currently no known use-cases for it; mapping + * to HT is still valid, and allows us to punt on determing + * where FORCE_ALP is supported and functional */ + clock = BHND_CLOCK_HT; + break; + case BHND_CLOCK_HT: + break; + default: + device_printf(sc->dev, "unknown clock: %#x\n", clock); + return (ENODEV); + } + + /* Issue transition */ + return (bhnd_pwrctl_setclk(sc, clock)); +} + +static int +bhnd_pwrctl_core_req_clock(device_t dev, struct bhnd_core_pmu_info *pinfo, + bhnd_clock clock) +{ + struct bhnd_pwrctl_softc *sc; + struct bhnd_pwrctl_clkres *clkres; + int error; + + sc = device_get_softc(dev); + error = 0; + + PWRCTL_LOCK(sc); + + clkres = bhnd_pwrctl_find_res(sc, pinfo); + + /* BHND_CLOCK_DYN discards the clock reservation entirely */ + if (clock == BHND_CLOCK_DYN) { + /* nothing to clean up? */ + if (clkres == NULL) { + PWRCTL_UNLOCK(sc); + return (0); + } + + /* drop reservation and apply clock transition */ + STAILQ_REMOVE(&sc->clkres_list, clkres, + bhnd_pwrctl_clkres, cr_link); + + if ((error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_RUN))) { + device_printf(dev, "clock transition failed: %d\n", + error); + + /* restore reservation */ + STAILQ_INSERT_TAIL(&sc->clkres_list, clkres, cr_link); + + PWRCTL_UNLOCK(sc); + return (error); + } + + /* deallocate orphaned reservation */ + free(clkres, M_DEVBUF); + + PWRCTL_UNLOCK(sc); + return (0); + } + + /* create (or update) reservation */ + if (clkres == NULL) { + clkres = malloc(sizeof(struct bhnd_pwrctl_clkres), M_DEVBUF, + M_NOWAIT); + if (clkres == NULL) + return (ENOMEM); + + clkres->owner = pinfo->pm_dev; + clkres->clock = clock; + + STAILQ_INSERT_TAIL(&sc->clkres_list, clkres, cr_link); + } else { + KASSERT(clkres->owner == pinfo->pm_dev, ("invalid owner")); + clkres->clock = clock; + } + + /* apply clock transition */ + error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_RUN); + if (error) { + STAILQ_REMOVE(&sc->clkres_list, clkres, bhnd_pwrctl_clkres, + cr_link); + free(clkres, M_DEVBUF); + } + + PWRCTL_UNLOCK(sc); + 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), + + /* 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), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(bhnd_pmu, 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); + +MODULE_DEPEND(bhnd_pwrctl, bhnd, 1, 1, 1); +MODULE_VERSION(bhnd_pwrctl, 1); Index: sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_private.h =================================================================== --- sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_private.h +++ sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_private.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,12 +29,15 @@ * $FreeBSD$ */ -#ifndef _BHND_CORES_CHIPC_CHIPC_H_ -#define _BHND_CORES_CHIPC_CHIPC_H_ +#ifndef _BHND_PWRCTL_BHND_PWRCTL_PRIVATE_H_ +#define _BHND_PWRCTL_BHND_PWRCTL_PRIVATE_H_ -#include -#include +#include "bhnd_pwrctlvar.h" -#include "bhnd_chipc_if.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); -#endif /* _BHND_CORES_CHIPC_CHIPC_H_ */ +#endif /* _BHND_PWRCTL_BHND_PWRCTL_PRIVATE_H_ */ Index: sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_subr.c =================================================================== --- /dev/null +++ sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_subr.c @@ -0,0 +1,463 @@ +/*- + * Copyright (c) 2016 Landon Fuller + * Copyright (c) 2010, Broadcom Corporation. + * All rights reserved. + * + * This file is 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 + * 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. + * + * $Id: siutils.c,v 1.821.2.48 2011-02-11 20:59:28 Exp $ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include "bhnd_chipc_if.h" + +#include "bhnd_pwrctl_private.h" + +static uint32_t bhnd_pwrctl_factor6(uint32_t x); +static uint32_t bhnd_pwrctl_clock_rate(uint32_t pll_type, uint32_t n, + uint32_t m); + +/** + * Return the factor value corresponding to a given N3M clock control magic + * field value (CHIPC_F6_*). + */ +static uint32_t +bhnd_pwrctl_factor6(uint32_t x) +{ + switch (x) { + case CHIPC_F6_2: + return (2); + case CHIPC_F6_3: + return (3); + case CHIPC_F6_4: + return (4); + case CHIPC_F6_5: + return (5); + case CHIPC_F6_6: + return (6); + case CHIPC_F6_7: + return (7); + default: + return (0); + } +} + +/** + * Calculate the clock speed (in Hz) for a given a set of clockcontrol + * values. + * + * @param pll_type PLL type (CHIPC_PLL_TYPE*) + * @param n clock control N register value. + * @param m clock control N register value. + */ +static uint32_t +bhnd_pwrctl_clock_rate(uint32_t pll_type, uint32_t n, uint32_t m) +{ + uint32_t clk_base; + uint32_t n1, n2, clock, m1, m2, m3, mc; + + n1 = CHIPC_GET_BITS(n, CHIPC_CN_N1); + n2 = CHIPC_GET_BITS(n, CHIPC_CN_N2); + + switch (pll_type) { + case CHIPC_PLL_TYPE1: + case CHIPC_PLL_TYPE3: + case CHIPC_PLL_TYPE4: + case CHIPC_PLL_TYPE7: + n1 = bhnd_pwrctl_factor6(n1); + n2 += CHIPC_F5_BIAS; + break; + + case CHIPC_PLL_TYPE2: + n1 += CHIPC_T2_BIAS; + n2 += CHIPC_T2_BIAS; + KASSERT(n1 >= 2 && n1 <= 7, ("invalid n1 value")); + KASSERT(n2 >= 5 && n2 <= 23, ("invalid n2 value")); + break; + + case CHIPC_PLL_TYPE5: + return (100000000); + + case CHIPC_PLL_TYPE6: + if (m & CHIPC_T6_MMASK) + return (CHIPC_T6_M1); + else + return (CHIPC_T6_M0); + + default: + printf("unsupported PLL type %u\n", pll_type); + return (0); + } + + /* PLL types 3 and 7 use BASE2 (25Mhz) */ + if (pll_type == CHIPC_PLL_TYPE3 || pll_type == CHIPC_PLL_TYPE7) { + clk_base = CHIPC_CLOCK_BASE2; + } else { + clk_base = CHIPC_CLOCK_BASE1; + } + + clock = clk_base * n1 * n2; + + if (clock == 0) + return (0); + + m1 = CHIPC_GET_BITS(m, CHIPC_M1); + m2 = CHIPC_GET_BITS(m, CHIPC_M2); + m3 = CHIPC_GET_BITS(m, CHIPC_M3); + mc = CHIPC_GET_BITS(m, CHIPC_MC); + + switch (pll_type) { + case CHIPC_PLL_TYPE1: + case CHIPC_PLL_TYPE3: + case CHIPC_PLL_TYPE4: + case CHIPC_PLL_TYPE7: + m1 = bhnd_pwrctl_factor6(m1); + if (pll_type == CHIPC_PLL_TYPE1 || pll_type == CHIPC_PLL_TYPE3) + m2 += CHIPC_F5_BIAS; + else + m2 = bhnd_pwrctl_factor6(m2); + + m3 = bhnd_pwrctl_factor6(m3); + + switch (mc) { + case CHIPC_MC_BYPASS: + return (clock); + case CHIPC_MC_M1: + return (clock / m1); + case CHIPC_MC_M1M2: + return (clock / (m1 * m2)); + case CHIPC_MC_M1M2M3: + return (clock / (m1 * m2 * m3)); + case CHIPC_MC_M1M3: + return (clock / (m1 * m3)); + default: + printf("unsupported pwrctl mc %#x\n", mc); + return (0); + } + case CHIPC_PLL_TYPE2: + m1 += CHIPC_T2_BIAS; + m2 += CHIPC_T2M2_BIAS; + m3 += CHIPC_T2_BIAS; + KASSERT(m1 >= 2 && m1 <= 7, ("invalid m1 value")); + KASSERT(m2 >= 3 && m2 <= 10, ("invalid m2 value")); + KASSERT(m3 >= 2 && m3 <= 7, ("invalid m3 value")); + + if ((mc & CHIPC_T2MC_M1BYP) == 0) + clock /= m1; + if ((mc & CHIPC_T2MC_M2BYP) == 0) + clock /= m2; + if ((mc & CHIPC_T2MC_M3BYP) == 0) + clock /= m3; + + return (clock); + default: + panic("unhandled PLL type %u\n", pll_type); + } +} + +/** + * Return the backplane clock speed in Hz. + * + * @param sc driver instance state. + */ +uint32_t +bhnd_pwrctl_getclk_speed(struct bhnd_pwrctl_softc *sc) +{ + struct chipc_caps *ccaps; + bus_size_t creg; + uint32_t n, m; + uint32_t rate; + + PWRCTL_LOCK_ASSERT(sc, MA_OWNED); + + ccaps = BHND_CHIPC_GET_CAPS(sc->chipc_dev); + + n = bhnd_bus_read_4(sc->res, CHIPC_CLKC_N); + + switch (ccaps->pll_type) { + case CHIPC_PLL_TYPE6: + creg = CHIPC_CLKC_M3; /* non-extif regster */ + break; + case CHIPC_PLL_TYPE3: + creg = CHIPC_CLKC_M2; + break; + default: + creg = CHIPC_CLKC_SB; + break; + } + + m = bhnd_bus_read_4(sc->res, creg); + + /* calculate rate */ + rate = bhnd_pwrctl_clock_rate(ccaps->pll_type, n, m); + + if (ccaps->pll_type == CHIPC_PLL_TYPE3) + rate /= 2; + + return (rate); +} + +/* return the slow clock source */ +static bhnd_clksrc +bhnd_pwrctl_slowclk_src(struct bhnd_pwrctl_softc *sc) +{ + uint32_t clkreg; + uint32_t clksrc; + + /* Fetch clock source */ + if (PWRCTL_QUIRK(sc, PCICLK_CTL)) { + return (bhnd_pwrctl_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; + } else { + /* Instaclock */ + clksrc = CHIPC_SCC_SS_XTAL; + } + + /* Map to bhnd_clksrc */ + switch (clksrc) { + case CHIPC_SCC_SS_PCI: + return (BHND_CLKSRC_PCI); + case CHIPC_SCC_SS_LPO: + return (BHND_CLKSRC_LPO); + case CHIPC_SCC_SS_XTAL: + return (BHND_CLKSRC_XTAL); + default: + return (BHND_CLKSRC_UNKNOWN); + } +} + +/* return the ILP (slowclock) min or max frequency */ +static uint32_t +bhnd_pwrctl_slowclk_freq(struct bhnd_pwrctl_softc *sc, bool max_freq) +{ + bhnd_clksrc slowclk; + uint32_t div; + uint32_t hz; + + slowclk = bhnd_pwrctl_slowclk_src(sc); + + /* Determine clock divisor */ + if (PWRCTL_QUIRK(sc, PCICLK_CTL)) { + if (slowclk == BHND_CLKSRC_PCI) + div = 64; + else + div = 32; + } else if (PWRCTL_QUIRK(sc, SLOWCLK_CTL)) { + div = bhnd_bus_read_4(sc->res, CHIPC_PLL_SLOWCLK_CTL); + div = CHIPC_GET_BITS(div, CHIPC_SCC_CD); + div *= 4; + } else if (PWRCTL_QUIRK(sc, INSTACLK_CTL)) { + if (max_freq) { + div = 1; + } else { + div = bhnd_bus_read_4(sc->res, CHIPC_SYS_CLK_CTL); + div = CHIPC_GET_BITS(div, CHIPC_SYCC_CD); + div = 4 * (div + 1); + } + } else { + device_printf(sc->dev, "unknown device type\n"); + return (0); + } + + /* Determine clock frequency */ + switch (slowclk) { + case BHND_CLKSRC_LPO: + hz = max_freq ? CHIPC_LPOMAXFREQ : CHIPC_LPOMINFREQ; + break; + case BHND_CLKSRC_XTAL: + hz = max_freq ? CHIPC_XTALMAXFREQ : CHIPC_XTALMINFREQ; + break; + case BHND_CLKSRC_PCI: + hz = max_freq ? CHIPC_PCIMAXFREQ : CHIPC_PCIMINFREQ; + break; + default: + device_printf(sc->dev, "unknown slowclk source %#x\n", slowclk); + return (0); + } + + return (hz / div); +} + +/** + * Initialize power control registers. + */ +int +bhnd_pwrctl_init(struct bhnd_pwrctl_softc *sc) +{ + uint32_t clkctl; + uint32_t pll_delay, slowclk, slowmaxfreq; + uint32_t pll_on_delay, fref_sel_delay; + int error; + + pll_delay = CHIPC_PLL_DELAY; + + /* set all Instaclk chip ILP to 1 MHz */ + if (PWRCTL_QUIRK(sc, INSTACLK_CTL)) { + clkctl = (CHIPC_ILP_DIV_1MHZ << CHIPC_SYCC_CD_SHIFT); + clkctl &= CHIPC_SYCC_CD_MASK; + bhnd_bus_write_4(sc->res, CHIPC_SYS_CLK_CTL, clkctl); + } + + /* + * Initialize PLL/FREF delays. + * + * If the slow clock is not sourced by the xtal, include the + * delay required to bring it up. + */ + slowclk = bhnd_pwrctl_slowclk_src(sc); + if (slowclk != CHIPC_SCC_SS_XTAL) + pll_delay += CHIPC_XTAL_ON_DELAY; + + /* Starting with 4318 it is ILP that is used for the delays */ + if (PWRCTL_QUIRK(sc, INSTACLK_CTL)) + slowmaxfreq = bhnd_pwrctl_slowclk_freq(sc, false); + else + slowmaxfreq = bhnd_pwrctl_slowclk_freq(sc, true); + + pll_on_delay = ((slowmaxfreq * pll_delay) + 999999) / 1000000; + fref_sel_delay = ((slowmaxfreq * CHIPC_FREF_DELAY) + 999999) / 1000000; + + bhnd_bus_write_4(sc->res, CHIPC_PLL_ON_DELAY, pll_on_delay); + bhnd_bus_write_4(sc->res, CHIPC_PLL_FREFSEL_DELAY, fref_sel_delay); + + /* If required, force HT */ + if (PWRCTL_QUIRK(sc, FORCE_HT)) { + if ((error = bhnd_pwrctl_setclk(sc, BHND_CLOCK_HT))) + return (error); + } + + return (0); +} + +/* return the value suitable for writing to the dot11 core + * FAST_PWRUP_DELAY register */ +uint16_t +bhnd_pwrctl_fast_pwrup_delay(struct bhnd_pwrctl_softc *sc) +{ + uint32_t pll_on_delay, slowminfreq; + uint16_t fpdelay; + + fpdelay = 0; + + slowminfreq = bhnd_pwrctl_slowclk_freq(sc, false); + + pll_on_delay = bhnd_bus_read_4(sc->res, CHIPC_PLL_ON_DELAY) + 2; + pll_on_delay *= 1000000; + pll_on_delay += (slowminfreq - 1); + fpdelay = pll_on_delay / slowminfreq; + + return (fpdelay); +} + +/** + * Distribute @p clock on backplane. + * + * @param sc Driver instance state. + * @param clock Clock to enable. + * + * @retval 0 success + * @retval ENODEV If @p clock is unsupported, or if the device does not + * support dynamic clock control. + */ +int +bhnd_pwrctl_setclk(struct bhnd_pwrctl_softc *sc, bhnd_clock clock) +{ + uint32_t scc; + + PWRCTL_LOCK_ASSERT(sc, MA_OWNED); + + /* Is dynamic clock control supported? */ + if (PWRCTL_QUIRK(sc, FIXED_CLK)) + return (ENODEV); + + /* Chips with ccrev 10 are EOL and they don't have SYCC_HR used below */ + if (bhnd_get_hwrev(sc->chipc_dev) == 10) + return (ENODEV); + + scc = bhnd_bus_read_4(sc->res, CHIPC_PLL_SLOWCLK_CTL); + + switch (clock) { + case BHND_CLOCK_HT: + /* fast (pll) clock */ + if (PWRCTL_QUIRK(sc, SLOWCLK_CTL)) { + scc &= ~(CHIPC_SCC_XC | CHIPC_SCC_FS | CHIPC_SCC_IP); + scc |= CHIPC_SCC_IP; + + /* force xtal back on before clearing SCC_DYN_XTAL.. */ + bhnd_pwrctl_ungate_clock(sc->chipc_dev, BHND_CLOCK_HT); + } else if (PWRCTL_QUIRK(sc, INSTACLK_CTL)) { + scc |= CHIPC_SYCC_HR; + } else { + return (ENODEV); + } + + bhnd_bus_write_4(sc->res, CHIPC_PLL_SLOWCLK_CTL, scc); + DELAY(CHIPC_PLL_DELAY); + + break; + + case BHND_CLOCK_DYN: + /* enable dynamic clock control */ + if (PWRCTL_QUIRK(sc, SLOWCLK_CTL)) { + scc &= ~(CHIPC_SCC_FS | CHIPC_SCC_IP | CHIPC_SCC_XC); + if ((scc & CHIPC_SCC_SS_MASK) != CHIPC_SCC_SS_XTAL) + scc |= CHIPC_SCC_XC; + + bhnd_bus_write_4(sc->res, CHIPC_PLL_SLOWCLK_CTL, scc); + + /* 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_CLOCK_HT); + } + } else if (PWRCTL_QUIRK(sc, INSTACLK_CTL)) { + /* Instaclock */ + scc &= ~CHIPC_SYCC_HR; + bhnd_bus_write_4(sc->res, CHIPC_SYS_CLK_CTL, scc); + } else { + return (ENODEV); + } + + break; + + default: + return (ENODEV); + } + + return (0); +} Index: sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctlvar.h =================================================================== --- /dev/null +++ sys/dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctlvar.h @@ -0,0 +1,120 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller + * 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. + * + * $FreeBSD$ + */ + +#ifndef _BHND_PWRCTL_BHND_PWRCTLVAR_H_ +#define _BHND_PWRCTL_BHND_PWRCTLVAR_H_ + +#include +#include +#include + +/** + * bhnd pwrctl device quirks. + */ +enum { + /** No quirks */ + PWRCTL_QUIRK_NONE = 0, + + /** + * Early ChipCommon revisions do not support dynamic clock control + */ + PWRCTL_QUIRK_FIXED_CLK = (1 << 0), + + /** + * On PCI (not PCIe) devices, early ChipCommon revisions + * (rev <= 5) vend xtal/pll and clock config registers via the PCI + * config space. + * + * Dynamic clock control is not supported on these devices. + */ + PWRCTL_QUIRK_PCICLK_CTL = (1 << 1) | PWRCTL_QUIRK_FIXED_CLK, + + + /** + * On earliy BCM4311, BCM4321, and BCM4716 PCI(e) devices, no ALP + * clock is available, and the HT clock must be enabled. + */ + PWRCTL_QUIRK_FORCE_HT = (1 << 2), + + /** + * ChipCommon revisions 6-9 use the slowclk register layout. + */ + PWRCTL_QUIRK_SLOWCLK_CTL = (1 << 3), + + /** + * ChipCommon revisions 10-19 support the instaclk register layout. + */ + PWRCTL_QUIRK_INSTACLK_CTL = (1 << 4), + +}; + +/** + * device clock reservation. + */ +struct bhnd_pwrctl_clkres { + device_t owner; /**< bhnd(4) device holding this reservation */ + bhnd_clock clock; /**< requested clock */ + STAILQ_ENTRY(bhnd_pwrctl_clkres) cr_link; +}; + + +/** + * bhnd pwrctl driver instance state. + */ +struct bhnd_pwrctl_softc { + device_t dev; + uint32_t quirks; + + device_t chipc_dev; /**< core device */ + struct bhnd_resource *res; /**< core register block. */ + + struct mtx mtx; /**< state mutex */ + + /** active clock reservations */ + STAILQ_HEAD(, bhnd_pwrctl_clkres) clkres_list; +}; + +#define PWRCTL_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "bhnd pwrctl driver lock", MTX_DEF) +#define PWRCTL_LOCK(sc) mtx_lock(&(sc)->mtx) +#define PWRCTL_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define PWRCTL_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what) +#define PWRCTL_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx) + +/* quirk convenience macro */ +#define PWRCTL_QUIRK(_sc, _name) \ + ((_sc)->quirks & PWRCTL_QUIRK_ ## _name) + +#define PWRCTL_ASSERT_QUIRK(_sc, name) \ + KASSERT(PWRCTL_QUIRK((_sc), name), ("quirk " __STRING(_name) " not set")) + +#endif /* _BHND_PWRCTL_BHND_PWRCTLVAR_H_ */ Index: sys/dev/bhnd/cores/pmu/bhnd_pmu.h =================================================================== --- sys/dev/bhnd/cores/pmu/bhnd_pmu.h +++ sys/dev/bhnd/cores/pmu/bhnd_pmu.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,12 +29,24 @@ * $FreeBSD$ */ -#ifndef _BHND_CORES_CHIPC_CHIPC_H_ -#define _BHND_CORES_CHIPC_CHIPC_H_ +#ifndef _BHND_CORES_PMU_BHND_PMU_H_ +#define _BHND_CORES_PMU_BHND_PMU_H_ -#include -#include +#include -#include "bhnd_chipc_if.h" +#include "bhnd_pmu_if.h" -#endif /* _BHND_CORES_CHIPC_CHIPC_H_ */ +/** + * Per-core PMU register information. + */ +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 */ +}; + +#endif /* _BHND_CORES_PMU_BHND_PMU_H_ */ Index: sys/dev/bhnd/cores/pmu/bhnd_pmu.c =================================================================== --- /dev/null +++ sys/dev/bhnd/cores/pmu/bhnd_pmu.c @@ -0,0 +1,466 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "bhnd_nvram_map.h" + +#include "bhnd_pmureg.h" +#include "bhnd_pmuvar.h" + +#include "bhnd_pmu_private.h" + +/* + * Broadcom PMU driver. + * + * On modern BHND chipsets, the PMU, GCI, and SRENG (Save/Restore Engine?) + * register blocks are found within a dedicated PMU core (attached via + * the AHB 'always on bus'). + * + * On earlier chipsets, these register blocks are found at the same + * offsets within the ChipCommon core. + */ + +devclass_t bhnd_pmu_devclass; /**< bhnd(4) PMU device class */ + +static int bhnd_pmu_sysctl_bus_freq(SYSCTL_HANDLER_ARGS); +static int bhnd_pmu_sysctl_cpu_freq(SYSCTL_HANDLER_ARGS); +static int bhnd_pmu_sysctl_mem_freq(SYSCTL_HANDLER_ARGS); + +#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(). + */ +int +bhnd_pmu_probe(device_t dev) +{ + return (BUS_PROBE_DEFAULT); +} + +/** + * Default bhnd_pmu driver implementation of DEVICE_ATTACH(). + * + * @param dev PMU device. + * @param res The PMU device registers. The driver will maintain a borrowed + * reference to this resource for the lifetime of the device. + */ +int +bhnd_pmu_attach(device_t dev, struct bhnd_resource *res) +{ + struct bhnd_pmu_softc *sc; + struct sysctl_ctx_list *ctx; + struct sysctl_oid *tree; + devclass_t bhnd_class; + device_t core, bus; + int error; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->quirks = 0; + sc->res = res; + + /* Fetch capability flags */ + sc->caps = bhnd_bus_read_4(sc->res, BHND_PMU_CAP); + + /* Find the bus-attached core */ + bhnd_class = devclass_find("bhnd"); + core = sc->dev; + while ((bus = device_get_parent(core)) != NULL) { + if (device_get_devclass(bus) == bhnd_class) + break; + + core = bus; + } + + if (core == NULL) { + device_printf(sc->dev, "bhnd bus not found\n"); + return (ENXIO); + } + + /* Fetch chip and board info */ + sc->cid = *bhnd_get_chipid(core); + + if ((error = bhnd_read_board_info(core, &sc->board))) { + device_printf(sc->dev, "error fetching board info: %d\n", + error); + return (ENXIO); + } + + /* Locate ChipCommon device */ + sc->chipc_dev = bhnd_find_child(bus, BHND_DEVCLASS_CC, 0); + if (sc->chipc_dev == NULL) { + device_printf(sc->dev, "chipcommon device not found\n"); + return (ENXIO); + } + + 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); + goto failed; + } + + /* Set up sysctl nodes */ + ctx = device_get_sysctl_ctx(dev); + tree = device_get_sysctl_tree(dev); + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "bus_freq", CTLTYPE_UINT | CTLFLAG_RD, sc, 0, + bhnd_pmu_sysctl_bus_freq, "IU", "Bus clock frequency"); + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "cpu_freq", CTLTYPE_UINT | CTLFLAG_RD, sc, 0, + bhnd_pmu_sysctl_cpu_freq, "IU", "CPU clock frequency"); + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "mem_freq", CTLTYPE_UINT | CTLFLAG_RD, sc, 0, + bhnd_pmu_sysctl_mem_freq, "IU", "Memory clock frequency"); + + return (0); + +failed: + BPMU_LOCK_DESTROY(sc); + return (error); +} + +/** + * Default bhnd_pmu driver implementation of DEVICE_DETACH(). + */ +int +bhnd_pmu_detach(device_t dev) +{ + struct bhnd_pmu_softc *sc; + + sc = device_get_softc(dev); + + BPMU_LOCK_DESTROY(sc); + + return (0); +} + +/** + * Default bhnd_pmu driver implementation of DEVICE_SUSPEND(). + */ +int +bhnd_pmu_suspend(device_t dev) +{ + return (0); +} + +/** + * Default bhnd_pmu driver implementation of DEVICE_RESUME(). + */ +int +bhnd_pmu_resume(device_t dev) +{ + struct bhnd_pmu_softc *sc; + int error; + + sc = device_get_softc(dev); + + /* Re-initialize PMU */ + if ((error = bhnd_pmu_init(sc))) { + device_printf(sc->dev, "PMU init failed: %d\n", error); + return (error); + } + + return (0); +} + +static int +bhnd_pmu_sysctl_bus_freq(SYSCTL_HANDLER_ARGS) +{ + struct bhnd_pmu_softc *sc; + uint32_t freq; + + sc = arg1; + + BPMU_LOCK(sc); + freq = bhnd_pmu_si_clock(sc); + BPMU_UNLOCK(sc); + + return (sysctl_handle_32(oidp, NULL, freq, req)); +} + +static int +bhnd_pmu_sysctl_cpu_freq(SYSCTL_HANDLER_ARGS) +{ + struct bhnd_pmu_softc *sc; + uint32_t freq; + + sc = arg1; + + BPMU_LOCK(sc); + freq = bhnd_pmu_cpu_clock(sc); + BPMU_UNLOCK(sc); + + return (sysctl_handle_32(oidp, NULL, freq, req)); +} + +static int +bhnd_pmu_sysctl_mem_freq(SYSCTL_HANDLER_ARGS) +{ + struct bhnd_pmu_softc *sc; + uint32_t freq; + + sc = arg1; + + BPMU_LOCK(sc); + freq = bhnd_pmu_mem_clock(sc); + BPMU_UNLOCK(sc); + + 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) +{ + struct bhnd_pmu_softc *sc; + uint32_t avail; + uint32_t req; + + sc = device_get_softc(dev); + + 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(pinfo->pm_dev), clock); + return (ENODEV); + } + + BPMU_LOCK(sc); + + /* Issue request */ + BPMU_CLKCTL_SET_4(pinfo, req, BHND_CCS_FORCE_MASK); + + /* Wait for clock availability */ + bhnd_pmu_wait_clkst(sc, pinfo->pm_dev, pinfo->pm_res, pinfo->pm_regs, + avail, avail); + + BPMU_UNLOCK(sc); + + return (0); +} + +static int +bhnd_pmu_core_en_clocks(device_t dev, struct bhnd_core_pmu_info *pinfo, + uint32_t clocks) +{ + struct bhnd_pmu_softc *sc; + uint32_t avail; + uint32_t req; + + sc = device_get_softc(dev); + + avail = 0x0; + req = 0x0; + + /* Build clock request flags */ + if (clocks & BHND_CLOCK_DYN) /* nothing to enable */ + clocks &= ~BHND_CLOCK_DYN; + + if (clocks & BHND_CLOCK_ILP) /* nothing to enable */ + clocks &= ~BHND_CLOCK_ILP; + + if (clocks & BHND_CLOCK_ALP) { + req |= BHND_CCS_ALPAREQ; + avail |= BHND_CCS_ALPAVAIL; + clocks &= ~BHND_CLOCK_ALP; + } + + if (clocks & BHND_CLOCK_HT) { + req |= BHND_CCS_HTAREQ; + avail |= BHND_CCS_HTAVAIL; + clocks &= ~BHND_CLOCK_HT; + } + + /* Check for unknown clock values */ + if (clocks != 0x0) { + device_printf(dev, "%s requested unknown clocks: %#x\n", + device_get_nameunit(pinfo->pm_dev), clocks); + return (ENODEV); + } + + BPMU_LOCK(sc); + + /* Issue request */ + BPMU_CLKCTL_SET_4(pinfo, req, BHND_CCS_AREQ_MASK); + + /* Wait for clock availability */ + bhnd_pmu_wait_clkst(sc, pinfo->pm_dev, pinfo->pm_res, pinfo->pm_regs, + avail, avail); + + BPMU_UNLOCK(sc); + + return (0); +} + +static int +bhnd_pmu_core_req_ext_rsrc(device_t dev, struct bhnd_core_pmu_info *pinfo, + u_int rsrc) +{ + struct bhnd_pmu_softc *sc; + uint32_t req; + uint32_t avail; + + sc = device_get_softc(dev); + + if (rsrc > BHND_CCS_ERSRC_MAX) + return (EINVAL); + + req = BHND_PMU_SET_BITS((1<pm_dev, pinfo->pm_res, pinfo->pm_regs, + avail, avail); + + BPMU_UNLOCK(sc); + + return (0); +} + +static int +bhnd_pmu_core_release_ext_rsrc(device_t dev, struct bhnd_core_pmu_info *pinfo, + u_int rsrc) +{ + struct bhnd_pmu_softc *sc; + uint32_t mask; + + sc = device_get_softc(dev); + + if (rsrc > BHND_CCS_ERSRC_MAX) + return (EINVAL); + + mask = BHND_PMU_SET_BITS((1< + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "bhnd_pmureg.h" +#include "bhnd_pmuvar.h" + +/* + * PMU core driver. + */ + +/* Supported device identifiers */ +static const struct bhnd_device bhnd_pmucore_devices[] = { + BHND_DEVICE(BCM, PMU, NULL, NULL), + + BHND_DEVICE_END +}; + +static int +bhnd_pmu_core_probe(device_t dev) +{ + const struct bhnd_device *id; + int error; + + id = bhnd_device_lookup(dev, bhnd_pmucore_devices, + sizeof(bhnd_pmucore_devices[0])); + if (id == NULL) + return (ENXIO); + + /* Delegate to common driver implementation */ + if ((error = bhnd_pmu_probe(dev)) > 0) + return (error); + + bhnd_set_default_core_desc(dev); + return (BUS_PROBE_DEFAULT); +} + +static int +bhnd_pmu_core_attach(device_t dev) +{ + struct bhnd_pmu_softc *sc; + struct bhnd_resource *res; + int error; + int rid; + + sc = device_get_softc(dev); + + /* Allocate register block */ + rid = 0; + res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (res == NULL) { + device_printf(dev, "failed to allocate resources\n"); + return (ENXIO); + } + + /* Delegate to common driver implementation */ + if ((error = bhnd_pmu_attach(dev, res))) { + bhnd_release_resource(dev, SYS_RES_MEMORY, rid, res); + return (error); + } + + sc->rid = rid; + return (0); +} + +static int +bhnd_pmu_core_detach(device_t dev) +{ + struct bhnd_pmu_softc *sc; + int error; + + sc = device_get_softc(dev); + + /* Delegate to common driver implementation */ + if ((error = bhnd_pmu_detach(dev))) + return (error); + + bhnd_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); + return (0); +} + +static device_method_t bhnd_pmucore_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, bhnd_pmu_core_probe), + DEVMETHOD(device_attach, bhnd_pmu_core_attach), + DEVMETHOD(device_detach, bhnd_pmu_core_detach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(bhnd_pmu, bhnd_pmucore_driver, bhnd_pmucore_methods, + sizeof(struct bhnd_pmu_softc), bhnd_pmu_driver); +EARLY_DRIVER_MODULE(bhnd_pmu, bhnd, bhnd_pmucore_driver, bhnd_pmu_devclass, + NULL, NULL, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); + +MODULE_DEPEND(bhnd_pmu_core, bhnd_pmu, 1, 1, 1); +MODULE_VERSION(bhnd_pmu_core, 1); Index: sys/dev/bhnd/cores/pmu/bhnd_pmu_if.m =================================================================== --- /dev/null +++ sys/dev/bhnd/cores/pmu/bhnd_pmu_if.m @@ -0,0 +1,130 @@ +#- +# Copyright (c) 2016 Landon Fuller +# 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. +# 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_pmu; + +# +# bhnd(4) PMU interface. +# +# Provides a common PMU and clock control interface. +# + +HEADER { + struct bhnd_core_pmu_info; +} + +/** + * Enabling routing of @p clock (or faster) to a requesting core. + * + * @param dev PMU device. + * @param pinfo PMU info for requesting core. + * @param clock Clock requested. + * + * @retval 0 success + * @retval ENODEV If an unsupported clock was requested. + */ +METHOD int core_req_clock { + device_t dev; + struct bhnd_core_pmu_info *pinfo; + bhnd_clock clock; +}; + + +/** + * Request that @p clocks be powered on behalf of a requesting core. + * + * This will power any clock sources (XTAL, PLL, etc,) required by + * @p clocks and wait until they are ready, discarding any previous + * requests from the @p pinfo device. + * + * Requests from multiple devices are aggregated by the PMU. + * + * @param dev PMU device. + * @param pinfo PMU info for requesting core. + * @param clocks Clocks requested. + * + * @retval 0 success + * @retval ENODEV If an unsupported clock was requested. + */ +METHOD int core_en_clocks { + device_t dev; + struct bhnd_core_pmu_info *pinfo; + uint32_t clocks; +}; + +/** + * Power up a core-specific external resource. + * + * @param dev The parent of @p child. + * @param pinfo PMU info for requesting core. + * @param rsrc The core-specific external resource identifier. + * + * @retval 0 success + * @retval ENODEV If @p rsrc is not supported by this PMU driver. + */ +METHOD int core_req_ext_rsrc { + device_t dev; + struct bhnd_core_pmu_info *pinfo; + u_int rsrc; +}; + +/** + * Power down a core-specific external resource. + * + * @param dev The parent of @p child. + * @param pinfo PMU info for requesting core. + * @param rsrc The core-specific external resource identifier. + * + * @retval 0 success + * @retval ENODEV If @p rsrc is not supported by this PMU driver. + */ +METHOD int core_release_ext_rsrc { + device_t dev; + struct bhnd_core_pmu_info *pinfo; + u_int rsrc; +}; + +/** + * Release all outstanding requests (clocks, resources, etc) associated with + * @p pinfo. + * + * @param dev PMU device. + * @param pinfo PMU info for requesting core. + * + * @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. + */ +METHOD int core_release { + device_t dev; + struct bhnd_core_pmu_info *pinfo; +}; Index: sys/dev/bhnd/cores/pmu/bhnd_pmu_private.h =================================================================== --- /dev/null +++ sys/dev/bhnd/cores/pmu/bhnd_pmu_private.h @@ -0,0 +1,147 @@ +/*- + * Copyright (c) 2016 Landon Fuller + * Copyright (C) 2010, Broadcom Corporation. + * All rights reserved. + * + * This file is derived from the hndpmu.h header contributed by Broadcom + * to to the Linux staging repository, as well as later revisions of hndpmu.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$ + */ + +#ifndef _BHND_CORES_PMU_BHND_PMU_PRIVATE_H_ +#define _BHND_CORES_PMU_BHND_PMU_PRIVATE_H_ + +#include + +#include "bhnd_pmuvar.h" + +/* Register I/O */ +#define BHND_PMU_READ_1(_sc, _reg) bhnd_bus_read_1((_sc)->res, (_reg)) +#define BHND_PMU_READ_2(_sc, _reg) bhnd_bus_read_2((_sc)->res, (_reg)) +#define BHND_PMU_READ_4(_sc, _reg) bhnd_bus_read_4((_sc)->res, (_reg)) +#define BHND_PMU_WRITE_1(_sc, _reg, _val) \ + bhnd_bus_write_1((_sc)->res, (_reg), (_val)) +#define BHND_PMU_WRITE_2(_sc, _reg, _val) \ + bhnd_bus_write_2((_sc)->res, (_reg), (_val)) +#define BHND_PMU_WRITE_4(_sc, _reg, _val) \ + bhnd_bus_write_4((_sc)->res, (_reg), (_val)) + +#define BHND_PMU_AND_4(_sc, _reg, _val) \ + BHND_PMU_WRITE_4((_sc), (_reg), \ + BHND_PMU_READ_4((_sc), (_reg)) & (_val)) +#define BHND_PMU_OR_4(_sc, _reg, _val) \ + BHND_PMU_WRITE_4((_sc), (_reg), \ + BHND_PMU_READ_4((_sc), (_reg)) | (_val)) + +/* Indirect register support */ +#define BHND_PMU_IND_READ(_sc, _src, _reg) \ + bhnd_pmu_ind_read((_sc), BHND_PMU_ ## _src ## _ADDR, \ + BHND_PMU_ ## _src ## _DATA, (_reg)) +#define BHND_PMU_IND_WRITE(_sc, _src, _reg, _val, _mask) \ + bhnd_pmu_ind_write(sc, BHND_PMU_ ## _src ## _ADDR, \ + BHND_PMU_ ## _src ## _DATA, (_reg), (_val), (_mask)) + +/* Chip Control indirect registers */ +#define BHND_PMU_CCTRL_READ(_sc, _reg) \ + BHND_PMU_IND_READ((_sc), CHIPCTL, (_reg)) +#define BHND_PMU_CCTRL_WRITE(_sc, _reg, _val, _mask) \ + BHND_PMU_IND_WRITE((_sc), CHIPCTL, (_reg), (_val), (_mask)) + +/* Register 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) \ + BHND_PMU_IND_WRITE((_sc), REG_CONTROL, (_reg), (_val), (_mask)) + +/* PLL Control indirect registers */ +#define BHND_PMU_PLL_READ(_sc, _reg) \ + BHND_PMU_IND_READ((_sc), PLL_CONTROL, (_reg)) +#define BHND_PMU_PLL_WRITE(_sc, _reg, _val, _mask) \ + BHND_PMU_IND_WRITE((_sc), PLL_CONTROL, (_reg), (_val), (_mask)) + + +/** FVCO frequencies, in Hz */ +enum { + FVCO_880 = 880 * 1000, /**< 880MHz */ + FVCO_1760 = 1760 * 1000, /**< 1760MHz */ + FVCO_1440 = 1440 * 1000, /**< 1440MHz */ + FVCO_960 = 960 * 1000, /**< 960MHz */ +}; + +/** LDO voltage tunables */ +enum { + SET_LDO_VOLTAGE_LDO1 = 1, + SET_LDO_VOLTAGE_LDO2 = 2, + SET_LDO_VOLTAGE_LDO3 = 3, + SET_LDO_VOLTAGE_PAREF = 4, + SET_LDO_VOLTAGE_CLDO_PWM = 5, + SET_LDO_VOLTAGE_CLDO_BURST = 6, + SET_LDO_VOLTAGE_CBUCK_PWM = 7, + SET_LDO_VOLTAGE_CBUCK_BURST = 8, + SET_LDO_VOLTAGE_LNLDO1 = 9, + SET_LDO_VOLTAGE_LNLDO2_SEL = 10, +}; + +uint32_t bhnd_pmu_ind_read(struct bhnd_pmu_softc *sc, bus_size_t addr, + bus_size_t data, uint32_t reg); +void bhnd_pmu_ind_write(struct bhnd_pmu_softc *sc, 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); +void bhnd_pmu_swreg_init(struct bhnd_pmu_softc *sc); + +uint32_t bhnd_pmu_force_ilp(struct bhnd_pmu_softc *sc, bool force); + +uint32_t bhnd_pmu_si_clock(struct bhnd_pmu_softc *sc); +uint32_t bhnd_pmu_cpu_clock(struct bhnd_pmu_softc *sc); +uint32_t bhnd_pmu_mem_clock(struct bhnd_pmu_softc *sc); +uint32_t bhnd_pmu_alp_clock(struct bhnd_pmu_softc *sc); +uint32_t bhnd_pmu_ilp_clock(struct bhnd_pmu_softc *sc); + +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, + uint8_t ldo, uint8_t voltage); +int bhnd_pmu_fast_pwrup_delay(struct bhnd_pmu_softc *sc, + uint16_t *pwrup_delay); +void bhnd_pmu_rcal(struct bhnd_pmu_softc *sc); +void bhnd_pmu_spuravoid(struct bhnd_pmu_softc *sc, + uint8_t spuravoid); + +bool bhnd_pmu_is_otp_powered(struct bhnd_pmu_softc *sc); +uint32_t bhnd_pmu_measure_alpclk(struct bhnd_pmu_softc *sc); + +void bhnd_pmu_radio_enable(struct bhnd_pmu_softc *sc, + device_t d11core, bool enable); + +uint32_t bhnd_pmu_waitforclk_on_backplane(struct bhnd_pmu_softc *sc, + uint32_t clk, uint32_t delay); + +int bhnd_pmu_otp_power(struct bhnd_pmu_softc *sc, bool on); +void bhnd_pmu_sdiod_drive_strength_init(struct bhnd_pmu_softc *sc, + uint32_t drivestrength); + +void bhnd_pmu_paref_ldo_enable(struct bhnd_pmu_softc *sc, + bool enable); + +#endif /* _BHND_CORES_PMU_BHND_PMU_PRIVATE_H_ */ Index: sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c =================================================================== --- /dev/null +++ sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c @@ -0,0 +1,3446 @@ +/*- + * Copyright (c) 2016 Landon Fuller + * Copyright (C) 2010, Broadcom Corporation. + * All rights reserved. + * + * This file is derived from the hndpmu.c source contributed by Broadcom + * to to the Linux staging repository, as well as later revisions of hndpmu.c + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include + +#include + +#include "bhnd_nvram_map.h" + +#include "bhnd_pmureg.h" +#include "bhnd_pmuvar.h" + +#include "bhnd_pmu_private.h" + +#ifdef BCMDBG +#define PMU_MSG(args) printf args +#define PMU_ERROR(args) do { \ + panic args; \ +} while (0) +#else +#define PMU_MSG(args) +#define PMU_ERROR(args) printf args +#endif + +typedef struct pmu0_xtaltab0 pmu0_xtaltab0_t; +typedef struct pmu1_xtaltab0 pmu1_xtaltab0_t; + +/* PLL controls/clocks */ +static const pmu1_xtaltab0_t *bhnd_pmu1_xtaltab0(struct bhnd_pmu_softc *sc); +static const pmu1_xtaltab0_t *bhnd_pmu1_xtaldef0(struct bhnd_pmu_softc *sc); + +static void bhnd_pmu0_pllinit0(struct bhnd_pmu_softc *sc, uint32_t xtal); +static uint32_t bhnd_pmu0_cpuclk0(struct bhnd_pmu_softc *sc); +static uint32_t bhnd_pmu0_alpclk0(struct bhnd_pmu_softc *sc); + +static void bhnd_pmu1_pllinit0(struct bhnd_pmu_softc *sc, uint32_t xtal); +static uint32_t bhnd_pmu1_pllfvco0(struct bhnd_pmu_softc *sc); +static uint32_t bhnd_pmu1_cpuclk0(struct bhnd_pmu_softc *sc); +static uint32_t bhnd_pmu1_alpclk0(struct bhnd_pmu_softc *sc); + +static uint32_t bhnd_pmu5_clock(struct bhnd_pmu_softc *sc, u_int pll0, u_int m); + +/* PMU resources */ +static bool bhnd_pmu_res_depfltr_bb(struct bhnd_pmu_softc *sc); +static bool bhnd_pmu_res_depfltr_ncb(struct bhnd_pmu_softc *sc); +static bool bhnd_pmu_res_depfltr_paldo(struct bhnd_pmu_softc *sc); +static bool bhnd_pmu_res_depfltr_npaldo(struct bhnd_pmu_softc *sc); +static uint32_t bhnd_pmu_res_deps(struct bhnd_pmu_softc *sc, uint32_t rsrcs, + bool all); +static int bhnd_pmu_res_uptime(struct bhnd_pmu_softc *sc, uint8_t rsrc, + uint32_t *uptime); +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 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 PMURES_BIT(_bit) \ + (1 << (BHND_PMU_ ## _bit)) + +#define PMU_CST4330_SDIOD_CHIPMODE(_sc) \ + CHIPC_CST4330_CHIPMODE_SDIOD(BHND_CHIPC_READ_CHIPST((_sc)->chipc_dev)) + +/** + * Perform an indirect register read. + * + * @param addr Offset of the address register. + * @param data Offset of the data register. + * @param reg Indirect register to be read. + */ +uint32_t +bhnd_pmu_ind_read(struct bhnd_pmu_softc *sc, bus_size_t addr, bus_size_t data, + uint32_t reg) +{ + BHND_PMU_WRITE_4(sc, addr, reg); + return (BHND_PMU_READ_4(sc, data)); +} + +/** + * Perform an indirect register write. + * + * @param addr Offset of the address register. + * @param data Offset of the data register. + * @param reg Indirect register to be written. + * @param val Value to be written to @p reg. + * @param mask Only the bits defined by @p mask will be updated from @p val. + */ +void +bhnd_pmu_ind_write(struct bhnd_pmu_softc *sc, bus_size_t addr, + bus_size_t data, uint32_t reg, uint32_t val, uint32_t mask) +{ + uint32_t rval; + + BHND_PMU_WRITE_4(sc, addr, reg); + + if (mask != UINT32_MAX) { + rval = BHND_PMU_READ_4(sc, data); + rval &= ~mask | (val & mask); + } else { + rval = val; + } + + BHND_PMU_WRITE_4(sc, data, rval); +} + +/** + * 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, + uint8_t rf_voltage) +{ + BHND_PMU_REGCTRL_WRITE(sc, 0x01, (bb_voltage & 0x1f) << 22, ~0); + BHND_PMU_REGCTRL_WRITE(sc, 0x00, (rf_voltage & 0x1f) << 14, ~0); +} + +void +bhnd_pmu_set_ldo_voltage(struct bhnd_pmu_softc *sc, uint8_t ldo, + uint8_t voltage) +{ + uint32_t chipst; + uint32_t regctrl; + uint8_t shift; + uint8_t mask; + uint8_t addr; + + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4328: + case BHND_CHIPID_BCM5354: + switch (ldo) { + case SET_LDO_VOLTAGE_LDO1: + addr = 2; + shift = 17 + 8; + mask = 0xf; + break; + case SET_LDO_VOLTAGE_LDO2: + addr = 3; + shift = 1; + mask = 0xf; + break; + case SET_LDO_VOLTAGE_LDO3: + addr = 3; + shift = 9; + mask = 0xf; + break; + case SET_LDO_VOLTAGE_PAREF: + addr = 3; + shift = 17; + mask = 0x3f; + break; + default: + panic("unknown BCM4328/BCM5354 LDO %hhu\n", ldo); + } + break; + case BHND_CHIPID_BCM4312: + switch (ldo) { + case SET_LDO_VOLTAGE_PAREF: + addr = 0; + shift = 21; + mask = 0x3f; + break; + default: + panic("unknown BCM4312 LDO %hhu\n", ldo); + } + break; + case BHND_CHIPID_BCM4325: + switch (ldo) { + case SET_LDO_VOLTAGE_CLDO_PWM: + addr = 5; + shift = 9; + mask = 0xf; + break; + case SET_LDO_VOLTAGE_CLDO_BURST: + addr = 5; + shift = 13; + mask = 0xf; + break; + case SET_LDO_VOLTAGE_CBUCK_PWM: + addr = 3; + shift = 20; + mask = 0x1f; + /* Bit 116 & 119 are inverted in CLB for opt 2b */ + chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev); + if (BHND_PMU_GET_BITS(chipst, CHIPC_CST4325_PMUTOP_2B)) + voltage ^= 0x9; + break; + case SET_LDO_VOLTAGE_CBUCK_BURST: + addr = 3; + shift = 25; + mask = 0x1f; + /* Bit 121 & 124 are inverted in CLB for opt 2b */ + chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev); + if (BHND_PMU_GET_BITS(chipst, CHIPC_CST4325_PMUTOP_2B)) + voltage ^= 0x9; + break; + case SET_LDO_VOLTAGE_LNLDO1: + addr = 5; + shift = 17; + mask = 0x1f; + break; + case SET_LDO_VOLTAGE_LNLDO2_SEL: + addr = 6; + shift = 0; + mask = 0x1; + break; + default: + panic("unknown BCM4325 LDO %hhu\n", ldo); + } + break; + case BHND_CHIPID_BCM4336: + switch (ldo) { + case SET_LDO_VOLTAGE_CLDO_PWM: + addr = 4; + shift = 1; + mask = 0xf; + break; + case SET_LDO_VOLTAGE_CLDO_BURST: + addr = 4; + shift = 5; + mask = 0xf; + break; + case SET_LDO_VOLTAGE_LNLDO1: + addr = 4; + shift = 17; + mask = 0xf; + break; + default: + panic("unknown BCM4336 LDO %hhu\n", ldo); + } + break; + case BHND_CHIPID_BCM4330: + switch (ldo) { + case SET_LDO_VOLTAGE_CBUCK_PWM: + addr = 3; + shift = 0; + mask = 0x1f; + break; + default: + panic("unknown BCM4330 LDO %hhu\n", ldo); + } + break; + case BHND_CHIPID_BCM4331: + switch (ldo) { + case SET_LDO_VOLTAGE_PAREF: + addr = 1; + shift = 0; + mask = 0xf; + break; + default: + panic("unknown BCM4331 LDO %hhu\n", ldo); + } + break; + default: + panic("cannot set LDO voltage on unsupported chip %hu\n", + sc->cid.chip_id); + return; + } + + regctrl = (voltage & mask) << shift; + BHND_PMU_REGCTRL_WRITE(sc, addr, regctrl, mask << shift); +} + +/* 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) +{ + uint32_t ilp; + uint32_t uptime; + u_int delay; + int error; + + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM43224: + case BHND_CHIPID_BCM43225: + case BHND_CHIPID_BCM43421: + case BHND_CHIPID_BCM43235: + case BHND_CHIPID_BCM43236: + case BHND_CHIPID_BCM43238: + case BHND_CHIPID_BCM4331: + case BHND_CHIPID_BCM6362: + case BHND_CHIPID_BCM4313: + delay = 3700; + break; + + case BHND_CHIPID_BCM4325: + error = bhnd_pmu_res_uptime(sc, BHND_PMU_RES4325_HT_AVAIL, + &uptime); + if (error) + return (error); + + ilp = bhnd_pmu_ilp_clock(sc); + delay = (uptime + D11SCC_SLOW2FAST_TRANSITION) * + ((1000000 + ilp - 1) / ilp); + delay = (11 * delay) / 10; + break; + + case BHND_CHIPID_BCM4329: + error = bhnd_pmu_res_uptime(sc, BHND_PMU_RES4329_HT_AVAIL, + &uptime); + if (error) + return (error); + + ilp = bhnd_pmu_ilp_clock(sc); + delay = (uptime + D11SCC_SLOW2FAST_TRANSITION) * + ((1000000 + ilp - 1) / ilp); + delay = (11 * delay) / 10; + break; + + case BHND_CHIPID_BCM4319: + delay = 3700; + break; + + case BHND_CHIPID_BCM4336: + error = bhnd_pmu_res_uptime(sc, BHND_PMU_RES4336_HT_AVAIL, + &uptime); + if (error) + return (error); + + ilp = bhnd_pmu_ilp_clock(sc); + delay = (uptime + D11SCC_SLOW2FAST_TRANSITION) * + ((1000000 + ilp - 1) / ilp); + delay = (11 * delay) / 10; + break; + + case BHND_CHIPID_BCM4330: + error = bhnd_pmu_res_uptime(sc, BHND_PMU_RES4330_HT_AVAIL, + &uptime); + if (error) + return (error); + + ilp = bhnd_pmu_ilp_clock(sc); + delay = (uptime + D11SCC_SLOW2FAST_TRANSITION) * + ((1000000 + ilp - 1) / ilp); + delay = (11 * delay) / 10; + break; + + default: + delay = BHND_PMU_MAX_TRANSITION_DLY; + break; + } + + *pwrup_delay = (uint16_t)delay; + return (0); +} + +uint32_t +bhnd_pmu_force_ilp(struct bhnd_pmu_softc *sc, bool force) +{ + uint32_t orig; + uint32_t pctrl; + + pctrl = BHND_PMU_READ_4(sc, BHND_PMU_CTRL); + orig = pctrl; + + if (force) + pctrl &= ~(BHND_PMU_CTRL_HT_REQ_EN | BHND_PMU_CTRL_ALP_REQ_EN); + else + pctrl |= (BHND_PMU_CTRL_HT_REQ_EN | BHND_PMU_CTRL_ALP_REQ_EN); + + BHND_PMU_WRITE_4(sc, BHND_PMU_CTRL, pctrl); + + return (orig); +} + +/* Setup resource up/down timers */ +typedef struct { + uint8_t resnum; + uint16_t updown; +} pmu_res_updown_t; + +typedef bool (*pmu_res_filter) (struct bhnd_pmu_softc *sc); + +/* Change resource dependencies masks */ +typedef struct { + uint32_t res_mask; /* resources (chip specific) */ + int8_t action; /* action */ + uint32_t depend_mask; /* changes to the dependencies mask */ + pmu_res_filter filter; /* action is taken when filter is NULL or returns true */ +} pmu_res_depend_t; + +/* Resource dependencies mask change action */ +#define RES_DEPEND_SET 0 /* Override the dependencies mask */ +#define RES_DEPEND_ADD 1 /* Add to the dependencies mask */ +#define RES_DEPEND_REMOVE -1 /* Remove from the dependencies mask */ + +static const pmu_res_updown_t bcm4328a0_res_updown[] = { + { + BHND_PMU_RES4328_EXT_SWITCHER_PWM, 0x0101}, { + BHND_PMU_RES4328_BB_SWITCHER_PWM, 0x1f01}, { + BHND_PMU_RES4328_BB_SWITCHER_BURST, 0x010f}, { + BHND_PMU_RES4328_BB_EXT_SWITCHER_BURST, 0x0101}, { + BHND_PMU_RES4328_ILP_REQUEST, 0x0202}, { + BHND_PMU_RES4328_RADIO_SWITCHER_PWM, 0x0f01}, { + BHND_PMU_RES4328_RADIO_SWITCHER_BURST, 0x0f01}, { + BHND_PMU_RES4328_ROM_SWITCH, 0x0101}, { + BHND_PMU_RES4328_PA_REF_LDO, 0x0f01}, { + BHND_PMU_RES4328_RADIO_LDO, 0x0f01}, { + BHND_PMU_RES4328_AFE_LDO, 0x0f01}, { + BHND_PMU_RES4328_PLL_LDO, 0x0f01}, { + BHND_PMU_RES4328_BG_FILTBYP, 0x0101}, { + BHND_PMU_RES4328_TX_FILTBYP, 0x0101}, { + BHND_PMU_RES4328_RX_FILTBYP, 0x0101}, { + BHND_PMU_RES4328_XTAL_PU, 0x0101}, { + BHND_PMU_RES4328_XTAL_EN, 0xa001}, { + BHND_PMU_RES4328_BB_PLL_FILTBYP, 0x0101}, { + BHND_PMU_RES4328_RF_PLL_FILTBYP, 0x0101}, { + BHND_PMU_RES4328_BB_PLL_PU, 0x0701} +}; + +static const pmu_res_depend_t bcm4328a0_res_depend[] = { + /* Adjust ILP request resource not to force ext/BB switchers into burst mode */ + { + PMURES_BIT(RES4328_ILP_REQUEST), + RES_DEPEND_SET, + PMURES_BIT(RES4328_EXT_SWITCHER_PWM) | + PMURES_BIT(RES4328_BB_SWITCHER_PWM), NULL} +}; + +static const pmu_res_updown_t bcm4325a0_res_updown[] = { + { + BHND_PMU_RES4325_XTAL_PU, 0x1501} +}; + +static const pmu_res_depend_t bcm4325a0_res_depend[] = { + /* Adjust OTP PU resource dependencies - remove BB BURST */ + { + PMURES_BIT(RES4325_OTP_PU), + RES_DEPEND_REMOVE, + PMURES_BIT(RES4325_BUCK_BOOST_BURST), NULL}, + /* Adjust ALP/HT Avail resource dependencies - bring up BB along if it is used. */ + { + PMURES_BIT(RES4325_ALP_AVAIL) | PMURES_BIT(RES4325_HT_AVAIL), + RES_DEPEND_ADD, + PMURES_BIT(RES4325_BUCK_BOOST_BURST) | + PMURES_BIT(RES4325_BUCK_BOOST_PWM), bhnd_pmu_res_depfltr_bb}, + /* Adjust HT Avail resource dependencies - bring up RF switches along with HT. */ + { + PMURES_BIT(RES4325_HT_AVAIL), + RES_DEPEND_ADD, + PMURES_BIT(RES4325_RX_PWRSW_PU) | + PMURES_BIT(RES4325_TX_PWRSW_PU) | + PMURES_BIT(RES4325_LOGEN_PWRSW_PU) | + PMURES_BIT(RES4325_AFE_PWRSW_PU), NULL}, + /* Adjust ALL resource dependencies - remove CBUCK dependencies if it is not used. */ + { + PMURES_BIT(RES4325_ILP_REQUEST) | + PMURES_BIT(RES4325_ABUCK_BURST) | + PMURES_BIT(RES4325_ABUCK_PWM) | + PMURES_BIT(RES4325_LNLDO1_PU) | + PMURES_BIT(RES4325C1_LNLDO2_PU) | + PMURES_BIT(RES4325_XTAL_PU) | + PMURES_BIT(RES4325_ALP_AVAIL) | + PMURES_BIT(RES4325_RX_PWRSW_PU) | + PMURES_BIT(RES4325_TX_PWRSW_PU) | + PMURES_BIT(RES4325_RFPLL_PWRSW_PU) | + PMURES_BIT(RES4325_LOGEN_PWRSW_PU) | + PMURES_BIT(RES4325_AFE_PWRSW_PU) | + PMURES_BIT(RES4325_BBPLL_PWRSW_PU) | + PMURES_BIT(RES4325_HT_AVAIL), RES_DEPEND_REMOVE, + PMURES_BIT(RES4325B0_CBUCK_LPOM) | + PMURES_BIT(RES4325B0_CBUCK_BURST) | + PMURES_BIT(RES4325B0_CBUCK_PWM), bhnd_pmu_res_depfltr_ncb} +}; + +static const pmu_res_updown_t bcm4315a0_res_updown[] = { + { + BHND_PMU_RES4315_XTAL_PU, 0x2501} +}; + +static const pmu_res_depend_t bcm4315a0_res_depend[] = { + /* Adjust OTP PU resource dependencies - not need PALDO unless write */ + { + PMURES_BIT(RES4315_OTP_PU), + RES_DEPEND_REMOVE, + PMURES_BIT(RES4315_PALDO_PU), bhnd_pmu_res_depfltr_npaldo}, + /* Adjust ALP/HT Avail resource dependencies - bring up PALDO along if it is used. */ + { + PMURES_BIT(RES4315_ALP_AVAIL) | PMURES_BIT(RES4315_HT_AVAIL), + RES_DEPEND_ADD, + PMURES_BIT(RES4315_PALDO_PU), bhnd_pmu_res_depfltr_paldo}, + /* Adjust HT Avail resource dependencies - bring up RF switches along with HT. */ + { + PMURES_BIT(RES4315_HT_AVAIL), + RES_DEPEND_ADD, + PMURES_BIT(RES4315_RX_PWRSW_PU) | + PMURES_BIT(RES4315_TX_PWRSW_PU) | + PMURES_BIT(RES4315_LOGEN_PWRSW_PU) | + PMURES_BIT(RES4315_AFE_PWRSW_PU), NULL}, + /* Adjust ALL resource dependencies - remove CBUCK dependencies if it is not used. */ + { + PMURES_BIT(RES4315_CLDO_PU) | PMURES_BIT(RES4315_ILP_REQUEST) | + PMURES_BIT(RES4315_LNLDO1_PU) | + PMURES_BIT(RES4315_OTP_PU) | + PMURES_BIT(RES4315_LNLDO2_PU) | + PMURES_BIT(RES4315_XTAL_PU) | + PMURES_BIT(RES4315_ALP_AVAIL) | + PMURES_BIT(RES4315_RX_PWRSW_PU) | + PMURES_BIT(RES4315_TX_PWRSW_PU) | + PMURES_BIT(RES4315_RFPLL_PWRSW_PU) | + PMURES_BIT(RES4315_LOGEN_PWRSW_PU) | + PMURES_BIT(RES4315_AFE_PWRSW_PU) | + PMURES_BIT(RES4315_BBPLL_PWRSW_PU) | + PMURES_BIT(RES4315_HT_AVAIL), RES_DEPEND_REMOVE, + PMURES_BIT(RES4315_CBUCK_LPOM) | + PMURES_BIT(RES4315_CBUCK_BURST) | + PMURES_BIT(RES4315_CBUCK_PWM), bhnd_pmu_res_depfltr_ncb} +}; + +/* 4329 specific. needs to come back this issue later */ +static const pmu_res_updown_t bcm4329_res_updown[] = { + { + BHND_PMU_RES4329_XTAL_PU, 0x1501} +}; + +static const pmu_res_depend_t bcm4329_res_depend[] = { + /* Adjust HT Avail resource dependencies */ + { + PMURES_BIT(RES4329_HT_AVAIL), + RES_DEPEND_ADD, + PMURES_BIT(RES4329_CBUCK_LPOM) | + PMURES_BIT(RES4329_CBUCK_BURST) | + PMURES_BIT(RES4329_CBUCK_PWM) | + PMURES_BIT(RES4329_CLDO_PU) | + PMURES_BIT(RES4329_PALDO_PU) | + PMURES_BIT(RES4329_LNLDO1_PU) | + PMURES_BIT(RES4329_XTAL_PU) | + PMURES_BIT(RES4329_ALP_AVAIL) | + PMURES_BIT(RES4329_RX_PWRSW_PU) | + PMURES_BIT(RES4329_TX_PWRSW_PU) | + PMURES_BIT(RES4329_RFPLL_PWRSW_PU) | + PMURES_BIT(RES4329_LOGEN_PWRSW_PU) | + PMURES_BIT(RES4329_AFE_PWRSW_PU) | + PMURES_BIT(RES4329_BBPLL_PWRSW_PU), NULL} +}; + +static const pmu_res_updown_t bcm4319a0_res_updown[] = { + { + BHND_PMU_RES4319_XTAL_PU, 0x3f01} +}; + +static const pmu_res_depend_t bcm4319a0_res_depend[] = { + /* Adjust OTP PU resource dependencies - not need PALDO unless write */ + { + PMURES_BIT(RES4319_OTP_PU), + RES_DEPEND_REMOVE, + PMURES_BIT(RES4319_PALDO_PU), bhnd_pmu_res_depfltr_npaldo}, + /* Adjust HT Avail resource dependencies - bring up PALDO along if it is used. */ + { + PMURES_BIT(RES4319_HT_AVAIL), + RES_DEPEND_ADD, + PMURES_BIT(RES4319_PALDO_PU), bhnd_pmu_res_depfltr_paldo}, + /* Adjust HT Avail resource dependencies - bring up RF switches along with HT. */ + { + PMURES_BIT(RES4319_HT_AVAIL), + RES_DEPEND_ADD, + PMURES_BIT(RES4319_RX_PWRSW_PU) | + PMURES_BIT(RES4319_TX_PWRSW_PU) | + PMURES_BIT(RES4319_RFPLL_PWRSW_PU) | + PMURES_BIT(RES4319_LOGEN_PWRSW_PU) | + PMURES_BIT(RES4319_AFE_PWRSW_PU), NULL} +}; + +static const pmu_res_updown_t bcm4336a0_res_updown[] = { + { + BHND_PMU_RES4336_HT_AVAIL, 0x0D01} +}; + +static const pmu_res_depend_t bcm4336a0_res_depend[] = { + /* Just a dummy entry for now */ + { + PMURES_BIT(RES4336_RSVD), RES_DEPEND_ADD, 0, NULL} +}; + +static const pmu_res_updown_t bcm4330a0_res_updown[] = { + { + BHND_PMU_RES4330_HT_AVAIL, 0x0e02} +}; + +static const pmu_res_depend_t bcm4330a0_res_depend[] = { + /* Just a dummy entry for now */ + { + PMURES_BIT(RES4330_HT_AVAIL), RES_DEPEND_ADD, 0, NULL} +}; + +/* true if the power topology uses the buck boost to provide 3.3V to VDDIO_RF + * and WLAN PA */ +static bool +bhnd_pmu_res_depfltr_bb(struct bhnd_pmu_softc *sc) +{ + return (BHND_PMU_GET_FLAG(sc->board.board_flags, BHND_BFL_BUCKBOOST)); +} + +/* true if the power topology doesn't use the cbuck. Key on chiprev also if + * the chip is BCM4325. */ +static bool +bhnd_pmu_res_depfltr_ncb(struct bhnd_pmu_softc *sc) +{ + if (sc->cid.chip_id == BHND_CHIPID_BCM4325 && sc->cid.chip_rev <= 1) + return (false); + + return (BHND_PMU_GET_FLAG(sc->board.board_flags, BHND_BFL_NOCBUCK)); +} + +/* true if the power topology uses the PALDO */ +static bool +bhnd_pmu_res_depfltr_paldo(struct bhnd_pmu_softc *sc) +{ + return (BHND_PMU_GET_FLAG(sc->board.board_flags, BHND_BFL_PALDO)); +} + +/* true if the power topology doesn't use the PALDO */ +static bool +bhnd_pmu_res_depfltr_npaldo(struct bhnd_pmu_softc *sc) +{ + return (!BHND_PMU_GET_FLAG(sc->board.board_flags, BHND_BFL_PALDO)); +} + +/* Determine min/max rsrc masks. Value 0 leaves hardware at default. */ +static int +bhnd_pmu_res_masks(struct bhnd_pmu_softc *sc, uint32_t *pmin, uint32_t *pmax) +{ + uint32_t max_mask, min_mask; + uint32_t chipst, otpsel; + uint32_t nval; + uint8_t rsrcs; + int error; + + max_mask = 0; + min_mask = 0; + + /* # resources */ + rsrcs = BHND_PMU_GET_BITS(sc->caps, BHND_PMU_CAP_RC); + + /* determine min/max rsrc masks */ + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4325: + /* If used by this device, enable the CBUCK */ + if (!bhnd_pmu_res_depfltr_ncb(sc)) + min_mask |= PMURES_BIT(RES4325B0_CBUCK_LPOM); + + chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev); + if (BHND_PMU_GET_BITS(chipst, CHIPC_CST4325_PMUTOP_2B)) + min_mask |= PMURES_BIT(RES4325B0_CLDO_PU); + + /* Is OTP required? */ + otpsel = BHND_PMU_GET_BITS(chipst, CHIPC_CST4325_SPROM_OTP_SEL); + if (otpsel != CHIPC_CST_OTP_PWRDN) + min_mask |= PMURES_BIT(RES4325_OTP_PU); + + /* Leave buck boost on in burst mode for certain boards */ + if (sc->board.board_flags & BHND_BFL_BUCKBOOST) { + switch (sc->board.board_type) { + case BHND_BOARD_BCM94325DEVBU: + case BHND_BOARD_BCM94325BGABU: + min_mask |= PMURES_BIT( + RES4325_BUCK_BOOST_BURST); + break; + } + } + + /* Allow all resources to be turned on upon requests */ + max_mask = ~(~0 << rsrcs); + break; + + case BHND_CHIPID_BCM4312: + /* default min_mask = 0x80000cbb is wrong */ + min_mask = 0xcbb; + /* + * max_mask = 0x7fff; + * pmu_res_updown_table_sz = 0; + * pmu_res_depend_table_sz = 0; + */ + break; + + case BHND_CHIPID_BCM4322: + case BHND_CHIPID_BCM43221: + case BHND_CHIPID_BCM43231: + case BHND_CHIPID_BCM4342: + if (sc->cid.chip_rev >= 2) + break; + + /* request ALP(can skip for A1) */ + min_mask = PMURES_BIT(RES4322_RF_LDO) | + PMURES_BIT(RES4322_XTAL_PU) | + PMURES_BIT(RES4322_ALP_AVAIL); + + if (bhnd_get_attach_type(sc->chipc_dev) == BHND_ATTACH_NATIVE) { + min_mask |= + PMURES_BIT(RES4322_SI_PLL_ON) | + PMURES_BIT(RES4322_HT_SI_AVAIL) | + PMURES_BIT(RES4322_PHY_PLL_ON) | + PMURES_BIT(RES4322_OTP_PU) | + PMURES_BIT(RES4322_HT_PHY_AVAIL); + max_mask = 0x1ff; + } + break; + + case BHND_CHIPID_BCM43222: + case BHND_CHIPID_BCM43111: + case BHND_CHIPID_BCM43112: + case BHND_CHIPID_BCM43224: + case BHND_CHIPID_BCM43225: + case BHND_CHIPID_BCM43421: + case BHND_CHIPID_BCM43226: + case BHND_CHIPID_BCM43420: + case BHND_CHIPID_BCM43235: + case BHND_CHIPID_BCM43236: + case BHND_CHIPID_BCM43238: + case BHND_CHIPID_BCM43234: + case BHND_CHIPID_BCM43237: + case BHND_CHIPID_BCM4331: + case BHND_CHIPID_BCM43431: + case BHND_CHIPID_BCM6362: + /* use chip default */ + break; + + case BHND_CHIPID_BCM4328: + min_mask = + PMURES_BIT(RES4328_BB_SWITCHER_PWM) | + PMURES_BIT(RES4328_EXT_SWITCHER_PWM) | + PMURES_BIT(RES4328_XTAL_EN); + max_mask = 0xfffffff; + break; + + case BHND_CHIPID_BCM5354: + /* Allow (but don't require) PLL to turn on */ + max_mask = 0xfffffff; + break; + + case BHND_CHIPID_BCM4329: + /* Down to save the power. */ + if (sc->cid.chip_rev >= 0x2) { + min_mask = + PMURES_BIT(RES4329_CBUCK_LPOM) | + PMURES_BIT(RES4329_LNLDO1_PU) | + PMURES_BIT(RES4329_CLDO_PU); + } else { + min_mask = + PMURES_BIT(RES4329_CBUCK_LPOM) | + PMURES_BIT(RES4329_CLDO_PU); + } + + /* Is OTP required? */ + chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev); + otpsel = BHND_PMU_GET_BITS(chipst, CHIPC_CST4329_SPROM_OTP_SEL); + if (otpsel != CHIPC_CST_OTP_PWRDN) + min_mask |= PMURES_BIT(RES4329_OTP_PU); + + /* Allow (but don't require) PLL to turn on */ + max_mask = 0x3ff63e; + break; + + case BHND_CHIPID_BCM4319: + /* We only need a few resources to be kept on all the time */ + min_mask = PMURES_BIT(RES4319_CBUCK_LPOM) | + PMURES_BIT(RES4319_CLDO_PU); + + /* Allow everything else to be turned on upon requests */ + max_mask = ~(~0 << rsrcs); + break; + + case BHND_CHIPID_BCM4336: + /* Down to save the power. */ + min_mask = + PMURES_BIT(RES4336_CBUCK_LPOM) | + PMURES_BIT(RES4336_CLDO_PU) | + PMURES_BIT(RES4336_LDO3P3_PU) | + PMURES_BIT(RES4336_OTP_PU) | + PMURES_BIT(RES4336_DIS_INT_RESET_PD); + /* Allow (but don't require) PLL to turn on */ + max_mask = 0x1ffffff; + break; + + case BHND_CHIPID_BCM4330: + /* Down to save the power. */ + min_mask = + PMURES_BIT(RES4330_CBUCK_LPOM) | PMURES_BIT(RES4330_CLDO_PU) + | PMURES_BIT(RES4330_DIS_INT_RESET_PD) | + PMURES_BIT(RES4330_LDO3P3_PU) | PMURES_BIT(RES4330_OTP_PU); + /* Allow (but don't require) PLL to turn on */ + max_mask = 0xfffffff; + break; + + case BHND_CHIPID_BCM4313: + min_mask = PMURES_BIT(RES4313_BB_PU_RSRC) | + PMURES_BIT(RES4313_XTAL_PU_RSRC) | + PMURES_BIT(RES4313_ALP_AVAIL_RSRC) | + PMURES_BIT(RES4313_BB_PLL_PWRSW_RSRC); + max_mask = 0xffff; + break; + default: + break; + } + + /* Apply nvram override to min mask */ + error = bhnd_nvram_getvar_uint32(sc->chipc_dev, BHND_NVAR_RMIN, &nval); + if (error && error != ENOENT) { + device_printf(sc->dev, "NVRAM error reading %s: %d\n", + BHND_NVAR_RMIN, error); + return (error); + } else if (!error) { + PMU_MSG(("Applying rmin=%#x to min_mask\n", nval)); + min_mask = nval; + } + + /* Apply nvram override to max mask */ + error = bhnd_nvram_getvar_uint32(sc->chipc_dev, BHND_NVAR_RMAX, &nval); + if (error && error != ENOENT) { + device_printf(sc->dev, "NVRAM error reading %s: %d\n", + BHND_NVAR_RMAX, error); + return (error); + } else if (!error) { + PMU_MSG(("Applying rmax=%#x to max_mask\n", nval)); + min_mask = nval; + } + + if (pmin != NULL) + *pmin = min_mask; + + if (pmax != NULL) + *pmax = max_mask; + + return (0); +} + +/* initialize PMU resources */ +int +bhnd_pmu_res_init(struct bhnd_pmu_softc *sc) +{ + const pmu_res_updown_t *pmu_res_updown_table; + const pmu_res_depend_t *pmu_res_depend_table; + size_t pmu_res_updown_table_sz; + size_t pmu_res_depend_table_sz; + uint32_t max_mask, min_mask; + uint8_t rsrcs; + int error; + + pmu_res_depend_table = NULL; + pmu_res_depend_table_sz = 0; + + pmu_res_updown_table = NULL; + pmu_res_updown_table_sz = 0; + + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4315: + /* Optimize resources up/down timers */ + pmu_res_updown_table = bcm4315a0_res_updown; + pmu_res_updown_table_sz = nitems(bcm4315a0_res_updown); + + /* Optimize resources dependencies */ + pmu_res_depend_table = bcm4315a0_res_depend; + pmu_res_depend_table_sz = nitems(bcm4315a0_res_depend); + break; + + case BHND_CHIPID_BCM4325: + /* Optimize resources up/down timers */ + pmu_res_updown_table = bcm4325a0_res_updown; + pmu_res_updown_table_sz = nitems(bcm4325a0_res_updown); + + /* Optimize resources dependencies */ + pmu_res_depend_table = bcm4325a0_res_depend; + pmu_res_depend_table_sz = nitems(bcm4325a0_res_depend); + break; + + case BHND_CHIPID_BCM4328: + /* Optimize resources up/down timers */ + pmu_res_updown_table = bcm4328a0_res_updown; + pmu_res_updown_table_sz = nitems(bcm4328a0_res_updown); + + /* Optimize resources dependencies */ + pmu_res_depend_table = bcm4328a0_res_depend; + pmu_res_depend_table_sz = nitems(bcm4328a0_res_depend); + break; + + case BHND_CHIPID_BCM4329: + /* Optimize resources up/down timers */ + pmu_res_updown_table = bcm4329_res_updown; + pmu_res_updown_table_sz = nitems(bcm4329_res_updown); + + /* Optimize resources dependencies */ + pmu_res_depend_table = bcm4329_res_depend; + pmu_res_depend_table_sz = nitems(bcm4329_res_depend); + break; + + case BHND_CHIPID_BCM4319: + /* Optimize resources up/down timers */ + pmu_res_updown_table = bcm4319a0_res_updown; + pmu_res_updown_table_sz = nitems(bcm4319a0_res_updown); + + /* Optimize resources dependencies masks */ + pmu_res_depend_table = bcm4319a0_res_depend; + pmu_res_depend_table_sz = nitems(bcm4319a0_res_depend); + break; + + case BHND_CHIPID_BCM4336: + /* Optimize resources up/down timers */ + pmu_res_updown_table = bcm4336a0_res_updown; + pmu_res_updown_table_sz = nitems(bcm4336a0_res_updown); + + /* Optimize resources dependencies masks */ + pmu_res_depend_table = bcm4336a0_res_depend; + pmu_res_depend_table_sz = nitems(bcm4336a0_res_depend); + break; + + case BHND_CHIPID_BCM4330: + /* Optimize resources up/down timers */ + pmu_res_updown_table = bcm4330a0_res_updown; + pmu_res_updown_table_sz = nitems(bcm4330a0_res_updown); + + /* Optimize resources dependencies masks */ + pmu_res_depend_table = bcm4330a0_res_depend; + pmu_res_depend_table_sz = nitems(bcm4330a0_res_depend); + break; + default: + break; + } + + /* # resources */ + rsrcs = BHND_PMU_GET_BITS(sc->caps, BHND_PMU_CAP_RC); + + /* Program up/down timers */ + for (size_t i = 0; i < pmu_res_updown_table_sz; i++) { + const pmu_res_updown_t *updt; + + KASSERT(pmu_res_updown_table != NULL, ("no updown tables")); + + updt = &pmu_res_updown_table[pmu_res_updown_table_sz - i - 1]; + + PMU_MSG(("Changing rsrc %d res_updn_timer to %#x\n", + updt->resnum, updt->updown)); + + BHND_PMU_WRITE_4(sc, BHND_PMU_RES_TABLE_SEL, updt->resnum); + BHND_PMU_WRITE_4(sc, BHND_PMU_RES_UPDN_TIMER, updt->updown); + } + + /* Apply nvram overrides to up/down timers */ + for (uint8_t i = 0; i < rsrcs; i++) { + char name[6]; + uint32_t val; + + snprintf(name, sizeof(name), "r%dt", i); + error = bhnd_nvram_getvar_uint32(sc->chipc_dev, name, &val); + + if (error == ENOENT) { + continue; + } else if (error) { + device_printf(sc->dev, "NVRAM error reading %s: %d\n", + name, error); + return (error); + } + + PMU_MSG(("Applying %s=%s to rsrc %d res_updn_timer\n", name, + val, i)); + + BHND_PMU_WRITE_4(sc, BHND_PMU_RES_TABLE_SEL, i); + BHND_PMU_WRITE_4(sc, BHND_PMU_RES_UPDN_TIMER, val); + } + + /* Program resource dependencies table */ + for (size_t i = 0; i < pmu_res_depend_table_sz; i++) { + const pmu_res_depend_t *rdep; + pmu_res_filter filter; + uint32_t depend_mask; + + KASSERT(pmu_res_depend_table != NULL, ("no depend tables")); + + rdep = &pmu_res_depend_table[pmu_res_depend_table_sz - i - 1]; + filter = rdep->filter; + + if (filter != NULL && !filter(sc)) + continue; + + for (uint8_t i = 0; i < rsrcs; i++) { + if ((rdep->res_mask & BHND_PMURES_BIT(i)) == 0) + continue; + + BHND_PMU_WRITE_4(sc, BHND_PMU_RES_TABLE_SEL, i); + depend_mask = BHND_PMU_READ_4(sc, + BHND_PMU_RES_DEP_MASK); + switch (rdep->action) { + case RES_DEPEND_SET: + PMU_MSG(("Changing rsrc %hhu res_dep_mask to " + "%#x\n", i, table->depend_mask)); + depend_mask = rdep->depend_mask; + break; + + case RES_DEPEND_ADD: + PMU_MSG(("Adding %#x to rsrc %hhu " + "res_dep_mask\n", table->depend_mask, i)); + + depend_mask |= rdep->depend_mask; + break; + + case RES_DEPEND_REMOVE: + PMU_MSG(("Removing %#x from rsrc %hhu " + "res_dep_mask\n", table->depend_mask, i)); + + depend_mask &= ~(rdep->depend_mask); + break; + + default: + panic("unknown RES_DEPEND action: %d\n", + rdep->action); + break; + } + + + } + } + + /* Apply nvram overrides to dependencies masks */ + for (uint8_t i = 0; i < rsrcs; i++) { + char name[6]; + uint32_t val; + + snprintf(name, sizeof(name), "r%dd", i); + error = bhnd_nvram_getvar_uint32(sc->chipc_dev, name, &val); + + if (error == ENOENT) { + continue; + } else if (error) { + device_printf(sc->dev, "NVRAM error reading %s: %d\n", + name, error); + return (error); + } + + PMU_MSG(("Applying %s=%s to rsrc %d res_dep_mask\n", name, val, + i)); + + BHND_PMU_WRITE_4(sc, BHND_PMU_RES_TABLE_SEL, i); + BHND_PMU_WRITE_4(sc, BHND_PMU_RES_DEP_MASK, val); + } + + /* Determine min/max rsrc masks */ + if ((error = bhnd_pmu_res_masks(sc, &min_mask, &max_mask))) + return (error); + + /* It is required to program max_mask first and then min_mask */ + + /* Program max resource mask */ + if (max_mask != 0) { + PMU_MSG(("Changing max_res_mask to 0x%x\n", max_mask)); + BHND_PMU_WRITE_4(sc, BHND_PMU_MAX_RES_MASK, max_mask); + } + + /* Program min resource mask */ + + if (min_mask != 0) { + PMU_MSG(("Changing min_res_mask to 0x%x\n", min_mask)); + BHND_PMU_WRITE_4(sc, BHND_PMU_MIN_RES_MASK, min_mask); + } + + /* Add some delay; allow resources to come up and settle. */ + DELAY(2000); + + return (0); +} + +/* setup pll and query clock speed */ +struct pmu0_xtaltab0 { + uint16_t freq; + uint8_t xf; + uint8_t wbint; + uint32_t wbfrac; +}; + +/* the following table is based on 880Mhz fvco */ +static const pmu0_xtaltab0_t pmu0_xtaltab0[] = { + { + 12000, 1, 73, 349525}, { + 13000, 2, 67, 725937}, { + 14400, 3, 61, 116508}, { + 15360, 4, 57, 305834}, { + 16200, 5, 54, 336579}, { + 16800, 6, 52, 399457}, { + 19200, 7, 45, 873813}, { + 19800, 8, 44, 466033}, { + 20000, 9, 44, 0}, { + 25000, 10, 70, 419430}, { + 26000, 11, 67, 725937}, { + 30000, 12, 58, 699050}, { + 38400, 13, 45, 873813}, { + 40000, 14, 45, 0}, { + 0, 0, 0, 0} +}; + +#define PMU0_XTAL0_DEFAULT 8 + +/* setup pll and query clock speed */ +struct pmu1_xtaltab0 { + uint16_t fref; + uint8_t xf; + uint8_t p1div; + uint8_t p2div; + uint8_t ndiv_int; + uint32_t ndiv_frac; +}; + +static const pmu1_xtaltab0_t pmu1_xtaltab0_880_4329[] = { + { + 12000, 1, 3, 22, 0x9, 0xFFFFEF}, { + 13000, 2, 1, 6, 0xb, 0x483483}, { + 14400, 3, 1, 10, 0xa, 0x1C71C7}, { + 15360, 4, 1, 5, 0xb, 0x755555}, { + 16200, 5, 1, 10, 0x5, 0x6E9E06}, { + 16800, 6, 1, 10, 0x5, 0x3Cf3Cf}, { + 19200, 7, 1, 4, 0xb, 0x755555}, { + 19800, 8, 1, 11, 0x4, 0xA57EB}, { + 20000, 9, 1, 11, 0x4, 0x0}, { + 24000, 10, 3, 11, 0xa, 0x0}, { + 25000, 11, 5, 16, 0xb, 0x0}, { + 26000, 12, 1, 1, 0x21, 0xD89D89}, { + 30000, 13, 3, 8, 0xb, 0x0}, { + 37400, 14, 3, 1, 0x46, 0x969696}, { + 38400, 15, 1, 1, 0x16, 0xEAAAAA}, { + 40000, 16, 1, 2, 0xb, 0}, { + 0, 0, 0, 0, 0, 0} +}; + +/* the following table is based on 880Mhz fvco */ +static const pmu1_xtaltab0_t pmu1_xtaltab0_880[] = { + { + 12000, 1, 3, 22, 0x9, 0xFFFFEF}, { + 13000, 2, 1, 6, 0xb, 0x483483}, { + 14400, 3, 1, 10, 0xa, 0x1C71C7}, { + 15360, 4, 1, 5, 0xb, 0x755555}, { + 16200, 5, 1, 10, 0x5, 0x6E9E06}, { + 16800, 6, 1, 10, 0x5, 0x3Cf3Cf}, { + 19200, 7, 1, 4, 0xb, 0x755555}, { + 19800, 8, 1, 11, 0x4, 0xA57EB}, { + 20000, 9, 1, 11, 0x4, 0x0}, { + 24000, 10, 3, 11, 0xa, 0x0}, { + 25000, 11, 5, 16, 0xb, 0x0}, { + 26000, 12, 1, 2, 0x10, 0xEC4EC4}, { + 30000, 13, 3, 8, 0xb, 0x0}, { + 33600, 14, 1, 2, 0xd, 0x186186}, { + 38400, 15, 1, 2, 0xb, 0x755555}, { + 40000, 16, 1, 2, 0xb, 0}, { + 0, 0, 0, 0, 0, 0} +}; + +#define PMU1_XTALTAB0_880_12000K 0 +#define PMU1_XTALTAB0_880_13000K 1 +#define PMU1_XTALTAB0_880_14400K 2 +#define PMU1_XTALTAB0_880_15360K 3 +#define PMU1_XTALTAB0_880_16200K 4 +#define PMU1_XTALTAB0_880_16800K 5 +#define PMU1_XTALTAB0_880_19200K 6 +#define PMU1_XTALTAB0_880_19800K 7 +#define PMU1_XTALTAB0_880_20000K 8 +#define PMU1_XTALTAB0_880_24000K 9 +#define PMU1_XTALTAB0_880_25000K 10 +#define PMU1_XTALTAB0_880_26000K 11 +#define PMU1_XTALTAB0_880_30000K 12 +#define PMU1_XTALTAB0_880_37400K 13 +#define PMU1_XTALTAB0_880_38400K 14 +#define PMU1_XTALTAB0_880_40000K 15 + +/* the following table is based on 1760Mhz fvco */ +static const pmu1_xtaltab0_t pmu1_xtaltab0_1760[] = { + { + 12000, 1, 3, 44, 0x9, 0xFFFFEF}, { + 13000, 2, 1, 12, 0xb, 0x483483}, { + 14400, 3, 1, 20, 0xa, 0x1C71C7}, { + 15360, 4, 1, 10, 0xb, 0x755555}, { + 16200, 5, 1, 20, 0x5, 0x6E9E06}, { + 16800, 6, 1, 20, 0x5, 0x3Cf3Cf}, { + 19200, 7, 1, 18, 0x5, 0x17B425}, { + 19800, 8, 1, 22, 0x4, 0xA57EB}, { + 20000, 9, 1, 22, 0x4, 0x0}, { + 24000, 10, 3, 22, 0xa, 0x0}, { + 25000, 11, 5, 32, 0xb, 0x0}, { + 26000, 12, 1, 4, 0x10, 0xEC4EC4}, { + 30000, 13, 3, 16, 0xb, 0x0}, { + 38400, 14, 1, 10, 0x4, 0x955555}, { + 40000, 15, 1, 4, 0xb, 0}, { + 0, 0, 0, 0, 0, 0} +}; + +/* table index */ +#define PMU1_XTALTAB0_1760_12000K 0 +#define PMU1_XTALTAB0_1760_13000K 1 +#define PMU1_XTALTAB0_1760_14400K 2 +#define PMU1_XTALTAB0_1760_15360K 3 +#define PMU1_XTALTAB0_1760_16200K 4 +#define PMU1_XTALTAB0_1760_16800K 5 +#define PMU1_XTALTAB0_1760_19200K 6 +#define PMU1_XTALTAB0_1760_19800K 7 +#define PMU1_XTALTAB0_1760_20000K 8 +#define PMU1_XTALTAB0_1760_24000K 9 +#define PMU1_XTALTAB0_1760_25000K 10 +#define PMU1_XTALTAB0_1760_26000K 11 +#define PMU1_XTALTAB0_1760_30000K 12 +#define PMU1_XTALTAB0_1760_38400K 13 +#define PMU1_XTALTAB0_1760_40000K 14 + +/* the following table is based on 1440Mhz fvco */ +static const pmu1_xtaltab0_t pmu1_xtaltab0_1440[] = { + { + 12000, 1, 1, 1, 0x78, 0x0}, { + 13000, 2, 1, 1, 0x6E, 0xC4EC4E}, { + 14400, 3, 1, 1, 0x64, 0x0}, { + 15360, 4, 1, 1, 0x5D, 0xC00000}, { + 16200, 5, 1, 1, 0x58, 0xE38E38}, { + 16800, 6, 1, 1, 0x55, 0xB6DB6D}, { + 19200, 7, 1, 1, 0x4B, 0}, { + 19800, 8, 1, 1, 0x48, 0xBA2E8B}, { + 20000, 9, 1, 1, 0x48, 0x0}, { + 25000, 10, 1, 1, 0x39, 0x999999}, { + 26000, 11, 1, 1, 0x37, 0x627627}, { + 30000, 12, 1, 1, 0x30, 0x0}, { + 37400, 13, 2, 1, 0x4D, 0x15E76}, { + 38400, 13, 2, 1, 0x4B, 0x0}, { + 40000, 14, 2, 1, 0x48, 0x0}, { + 48000, 15, 2, 1, 0x3c, 0x0}, { + 0, 0, 0, 0, 0, 0} +}; + +/* table index */ +#define PMU1_XTALTAB0_1440_12000K 0 +#define PMU1_XTALTAB0_1440_13000K 1 +#define PMU1_XTALTAB0_1440_14400K 2 +#define PMU1_XTALTAB0_1440_15360K 3 +#define PMU1_XTALTAB0_1440_16200K 4 +#define PMU1_XTALTAB0_1440_16800K 5 +#define PMU1_XTALTAB0_1440_19200K 6 +#define PMU1_XTALTAB0_1440_19800K 7 +#define PMU1_XTALTAB0_1440_20000K 8 +#define PMU1_XTALTAB0_1440_25000K 9 +#define PMU1_XTALTAB0_1440_26000K 10 +#define PMU1_XTALTAB0_1440_30000K 11 +#define PMU1_XTALTAB0_1440_37400K 12 +#define PMU1_XTALTAB0_1440_38400K 13 +#define PMU1_XTALTAB0_1440_40000K 14 +#define PMU1_XTALTAB0_1440_48000K 15 + +#define XTAL_FREQ_24000MHZ 24000 +#define XTAL_FREQ_30000MHZ 30000 +#define XTAL_FREQ_37400MHZ 37400 +#define XTAL_FREQ_48000MHZ 48000 + +static const pmu1_xtaltab0_t pmu1_xtaltab0_960[] = { + { + 12000, 1, 1, 1, 0x50, 0x0}, { + 13000, 2, 1, 1, 0x49, 0xD89D89}, { + 14400, 3, 1, 1, 0x42, 0xAAAAAA}, { + 15360, 4, 1, 1, 0x3E, 0x800000}, { + 16200, 5, 1, 1, 0x39, 0x425ED0}, { + 16800, 6, 1, 1, 0x39, 0x249249}, { + 19200, 7, 1, 1, 0x32, 0x0}, { + 19800, 8, 1, 1, 0x30, 0x7C1F07}, { + 20000, 9, 1, 1, 0x30, 0x0}, { + 25000, 10, 1, 1, 0x26, 0x666666}, { + 26000, 11, 1, 1, 0x24, 0xEC4EC4}, { + 30000, 12, 1, 1, 0x20, 0x0}, { + 37400, 13, 2, 1, 0x33, 0x563EF9}, { + 38400, 14, 2, 1, 0x32, 0x0}, { + 40000, 15, 2, 1, 0x30, 0x0}, { + 48000, 16, 2, 1, 0x28, 0x0}, { + 0, 0, 0, 0, 0, 0} +}; + +/* table index */ +#define PMU1_XTALTAB0_960_12000K 0 +#define PMU1_XTALTAB0_960_13000K 1 +#define PMU1_XTALTAB0_960_14400K 2 +#define PMU1_XTALTAB0_960_15360K 3 +#define PMU1_XTALTAB0_960_16200K 4 +#define PMU1_XTALTAB0_960_16800K 5 +#define PMU1_XTALTAB0_960_19200K 6 +#define PMU1_XTALTAB0_960_19800K 7 +#define PMU1_XTALTAB0_960_20000K 8 +#define PMU1_XTALTAB0_960_25000K 9 +#define PMU1_XTALTAB0_960_26000K 10 +#define PMU1_XTALTAB0_960_30000K 11 +#define PMU1_XTALTAB0_960_37400K 12 +#define PMU1_XTALTAB0_960_38400K 13 +#define PMU1_XTALTAB0_960_40000K 14 +#define PMU1_XTALTAB0_960_48000K 15 + +/* select xtal table for each chip */ +static const pmu1_xtaltab0_t * +bhnd_pmu1_xtaltab0(struct bhnd_pmu_softc *sc) +{ + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4315: + return (pmu1_xtaltab0_1760); + case BHND_CHIPID_BCM4319: + return (pmu1_xtaltab0_1440); + case BHND_CHIPID_BCM4325: + return (pmu1_xtaltab0_880); + case BHND_CHIPID_BCM4329: + return (pmu1_xtaltab0_880_4329); + case BHND_CHIPID_BCM4336: + return (pmu1_xtaltab0_960); + case BHND_CHIPID_BCM4330: + if (PMU_CST4330_SDIOD_CHIPMODE(sc)) + return (pmu1_xtaltab0_960); + else + return (pmu1_xtaltab0_1440); + default: + PMU_MSG(("bhnd_pmu1_xtaltab0: Unknown chipid %#hx\n", + sc->cid.chip_id)); + return (NULL); + } +} + +/* select default xtal frequency for each chip */ +static const pmu1_xtaltab0_t * +bhnd_pmu1_xtaldef0(struct bhnd_pmu_softc *sc) +{ + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4315: + /* Default to 26000Khz */ + return (&pmu1_xtaltab0_1760[PMU1_XTALTAB0_1760_26000K]); + case BHND_CHIPID_BCM4319: + /* Default to 30000Khz */ + return (&pmu1_xtaltab0_1440[PMU1_XTALTAB0_1440_30000K]); + case BHND_CHIPID_BCM4325: + /* Default to 26000Khz */ + return (&pmu1_xtaltab0_880[PMU1_XTALTAB0_880_26000K]); + case BHND_CHIPID_BCM4329: + /* Default to 38400Khz */ + return (&pmu1_xtaltab0_880_4329[PMU1_XTALTAB0_880_38400K]); + case BHND_CHIPID_BCM4336: + /* Default to 26000Khz */ + return (&pmu1_xtaltab0_960[PMU1_XTALTAB0_960_26000K]); + case BHND_CHIPID_BCM4330: + /* Default to 37400Khz */ + if (PMU_CST4330_SDIOD_CHIPMODE(sc)) + return (&pmu1_xtaltab0_960[PMU1_XTALTAB0_960_37400K]); + else + return (&pmu1_xtaltab0_1440[PMU1_XTALTAB0_1440_37400K]); + default: + PMU_MSG(("bhnd_pmu1_xtaldef0: Unknown chipid %#hx\n", + sc->cid.chip_id)); + return (NULL); + } +} + +/* select default pll fvco for each chip */ +static uint32_t +bhnd_pmu1_pllfvco0(struct bhnd_pmu_softc *sc) +{ + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4329: + return (FVCO_880); + case BHND_CHIPID_BCM4319: + return (FVCO_1440); + case BHND_CHIPID_BCM4336: + return (FVCO_960); + case BHND_CHIPID_BCM4330: + if (PMU_CST4330_SDIOD_CHIPMODE(sc)) + return (FVCO_960); + else + return (FVCO_1440); + default: + PMU_MSG(("bhnd_pmu1_pllfvco0: Unknown chipid %#hx\n", + sc->cid.chip_id)); + return (0); + } +} + +/* query alp/xtal clock frequency */ +static uint32_t +bhnd_pmu1_alpclk0(struct bhnd_pmu_softc *sc) +{ + const pmu1_xtaltab0_t *xt; + uint32_t xf; + + /* Find the frequency in the table */ + xf = BHND_PMU_READ_4(sc, BHND_PMU_CTRL); + xf = BHND_PMU_GET_BITS(xf, BHND_PMU_CTRL_XTALFREQ); + + for (xt = bhnd_pmu1_xtaltab0(sc); xt != NULL && xt->fref != 0; xt++) { + if (xt->xf == xf) + break; + } + + /* Could not find it so assign a default value */ + if (xt == NULL || xt->fref == 0) + xt = bhnd_pmu1_xtaldef0(sc); + + if (xt == NULL || xt->fref == 0) { + device_printf(sc->dev, + "no matching ALP/XTAL frequency found\n"); + return (0); + } + + return (xt->fref * 1000); +} + +/* Set up PLL registers in the PMU as per the crystal speed. */ +static void +bhnd_pmu0_pllinit0(struct bhnd_pmu_softc *sc, uint32_t xtal) +{ + const pmu0_xtaltab0_t *xt; + uint32_t pll_data, pll_mask; + uint32_t pll_res; + uint32_t pmu_ctrl; + uint32_t xf; + + /* Use h/w default PLL config */ + if (xtal == 0) { + PMU_MSG(("Unspecified xtal frequency, skipping PLL " + "configuration\n")); + return; + } + + /* Find the frequency in the table */ + for (xt = pmu0_xtaltab0; xt->freq; xt ++) { + if (xt->freq == xtal) + break; + } + + if (xt->freq == 0) + xt = &pmu0_xtaltab0[PMU0_XTAL0_DEFAULT]; + + PMU_MSG(("XTAL %d.%d MHz (%d)\n", xtal / 1000, xtal % 1000, xt->xf)); + + /* Check current PLL state */ + pmu_ctrl = BHND_PMU_READ_4(sc, BHND_PMU_CTRL); + xf = BHND_PMU_GET_BITS(pmu_ctrl, BHND_PMU_CTRL_XTALFREQ); + if (xf == xt->xf) { +#ifdef BCMUSBDEV + if (sc->cid.chip_id == BHND_CHIPID_BCM4328) { + bhnd_pmu0_sbclk4328(sc, + BHND_PMU0_PLL0_PC0_DIV_ARM_88MHZ); + return; + } +#endif /* BCMUSBDEV */ + + PMU_MSG(("PLL already programmed for %d.%d MHz\n", + xt->freq / 1000, xt->freq % 1000)); + return; + } + + if (xf != 0) { + PMU_MSG(("Reprogramming PLL for %d.%d MHz (was %d.%dMHz)\n", + xt->freq / 1000, xt->freq % 1000, + pmu0_xtaltab0[tmp-1].freq / 1000, + pmu0_xtaltab0[tmp-1].freq % 1000)); + } else { + PMU_MSG(("Programming PLL for %d.%d MHz\n", + xt->freq / 1000, xt->freq % 1000)); + } + + /* Make sure the PLL is off */ + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4328: + pll_res = PMURES_BIT(RES4328_BB_PLL_PU); + break; + case BHND_CHIPID_BCM5354: + pll_res = PMURES_BIT(RES5354_BB_PLL_PU); + break; + default: + panic("unsupported chipid %#hx\n", sc->cid.chip_id); + } + BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~pll_res); + BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK, ~pll_res); + + /* Wait for HT clock to shutdown. */ + PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL); + + PMU_MSG(("Done masking\n")); + + /* Write PDIV in pllcontrol[0] */ + if (xt->freq >= BHND_PMU0_PLL0_PC0_PDIV_FREQ) { + BHND_PMU_PLL_WRITE(sc, BHND_PMU0_PLL0_PLLCTL0, + BHND_PMU0_PLL0_PC0_PDIV_MASK, BHND_PMU0_PLL0_PC0_PDIV_MASK); + } else { + BHND_PMU_PLL_WRITE(sc, BHND_PMU0_PLL0_PLLCTL0, 0, + BHND_PMU0_PLL0_PC0_PDIV_MASK); + } + + /* Write WILD in pllcontrol[1] */ + pll_data = + BHND_PMU_SET_BITS(xt->wbint, BHND_PMU0_PLL0_PC1_WILD_INT) | + BHND_PMU_SET_BITS(xt->wbfrac, BHND_PMU0_PLL0_PC1_WILD_FRAC); + + if (xt->wbfrac == 0) { + pll_data |= BHND_PMU0_PLL0_PC1_STOP_MOD; + } else { + pll_data &= ~BHND_PMU0_PLL0_PC1_STOP_MOD; + } + + pll_mask = + BHND_PMU0_PLL0_PC1_WILD_INT_MASK | + BHND_PMU0_PLL0_PC1_WILD_FRAC_MASK; + + BHND_PMU_PLL_WRITE(sc, BHND_PMU0_PLL0_PLLCTL1, pll_data, pll_mask); + + /* Write WILD in pllcontrol[2] */ + pll_data = BHND_PMU_SET_BITS(xt->wbint, BHND_PMU0_PLL0_PC2_WILD_INT); + pll_mask = BHND_PMU0_PLL0_PC2_WILD_INT_MASK; + BHND_PMU_PLL_WRITE(sc, BHND_PMU0_PLL0_PLLCTL2, pll_data, pll_mask); + + PMU_MSG(("Done pll\n")); + + /* Write XtalFreq. Set the divisor also. */ + pmu_ctrl = BHND_PMU_READ_4(sc, BHND_PMU_CTRL); + pmu_ctrl &= ~(BHND_PMU_CTRL_ILP_DIV_MASK|BHND_PMU_CTRL_XTALFREQ_MASK); + + pmu_ctrl |= BHND_PMU_SET_BITS(((xt->freq + 127) / 128) - 1, + BHND_PMU_CTRL_ILP_DIV); + pmu_ctrl |= BHND_PMU_SET_BITS(xt->xf, BHND_PMU_CTRL_XTALFREQ); + + BHND_PMU_WRITE_4(sc, BHND_PMU_CTRL, pmu_ctrl); +} + +/* query alp/xtal clock frequency */ +static uint32_t +bhnd_pmu0_alpclk0(struct bhnd_pmu_softc *sc) +{ + const pmu0_xtaltab0_t *xt; + uint32_t xf; + + /* Find the frequency in the table */ + xf = BHND_PMU_READ_4(sc, BHND_PMU_CTRL); + xf = BHND_PMU_GET_BITS(xf, BHND_PMU_CTRL_XTALFREQ); + for (xt = pmu0_xtaltab0; xt->freq; xt++) + if (xt->xf == xf) + break; + + /* PLL must be configured before */ + if (xt == NULL || xt->freq == 0) + panic("unsupported frequency: %u", xf); + + return (xt->freq * 1000); +} + +/* query CPU clock frequency */ +static uint32_t +bhnd_pmu0_cpuclk0(struct bhnd_pmu_softc *sc) +{ + uint32_t tmp, divarm; + uint32_t FVCO; +#ifdef BCMDBG + uint32_t pdiv, wbint, wbfrac, fvco; + uint32_t freq; +#endif + + FVCO = FVCO_880; + + /* Read divarm from pllcontrol[0] */ + tmp = BHND_PMU_PLL_READ(sc, BHND_PMU0_PLL0_PLLCTL0); + divarm = BHND_PMU_GET_BITS(tmp, BHND_PMU0_PLL0_PC0_DIV_ARM); + +#ifdef BCMDBG + /* Calculate fvco based on xtal freq, pdiv, and wild */ + pdiv = tmp & BHND_PMU0_PLL0_PC0_PDIV_MASK; + + tmp = BHND_PMU_PLL_READ(sc, BHND_PMU0_PLL0_PLLCTL1); + wbfrac = BHND_PMU_GET_BITS(tmp, BHND_PMU0_PLL0_PC1_WILD_FRAC); + wbint = BHND_PMU_GET_BITS(tmp, PMU0_PLL0_PC1_WILD_INT); + + tmp = BHND_PMU_PLL_READ(sc, BHND_PMU0_PLL0_PLLCTL2); + wbint += BHND_PMU_GET_BITS(tmp, BHND_PMU0_PLL0_PC2_WILD_INT); + + freq = bhnd_pmu0_alpclk0(sih, osh, cc) / 1000; + + fvco = (freq * wbint) << 8; + fvco += (freq * (wbfrac >> 10)) >> 2; + fvco += (freq * (wbfrac & 0x3ff)) >> 10; + fvco >>= 8; + fvco >>= pdiv; + fvco /= 1000; + fvco *= 1000; + + PMU_MSG(("bhnd_pmu0_cpuclk0: wbint %u wbfrac %u fvco %u\n", + wbint, wbfrac, fvco)); + + FVCO = fvco; +#endif /* BCMDBG */ + + /* Return ARM/SB clock */ + return FVCO / (divarm + BHND_PMU0_PLL0_PC0_DIV_ARM_BASE) * 1000; +} + + + +/* Set up PLL registers in the PMU as per the crystal speed. */ +static void +bhnd_pmu1_pllinit0(struct bhnd_pmu_softc *sc, uint32_t xtal) +{ + const pmu1_xtaltab0_t *xt; + uint32_t buf_strength; + uint32_t plladdr, plldata, pllmask; + uint32_t pmuctrl; + uint32_t FVCO; + uint8_t ndiv_mode; + + FVCO = bhnd_pmu1_pllfvco0(sc) / 1000; + buf_strength = 0; + ndiv_mode = 1; + + /* Use h/w default PLL config */ + if (xtal == 0) { + PMU_MSG(("Unspecified xtal frequency, skipping PLL " + "configuration\n")); + return; + } + + /* Find the frequency in the table */ + for (xt = bhnd_pmu1_xtaltab0(sc); xt != NULL && xt->fref != 0; xt++) { + if (xt->fref == xtal) + break; + } + + /* Check current PLL state, bail out if it has been programmed or + * we don't know how to program it. + */ + if (xt == NULL || xt->fref == 0) { + device_printf(sc->dev, "Unsupported XTAL frequency %d.%dMHz, " + "skipping PLL configuration\n", xtal / 1000, xtal % 1000); + return; + } + + /* For 4319 bootloader already programs the PLL but bootloader does not + * program the PLL4 and PLL5. So Skip this check for 4319. */ + pmuctrl = BHND_PMU_READ_4(sc, BHND_PMU_CTRL); + if (BHND_PMU_GET_BITS(pmuctrl, BHND_PMU_CTRL_XTALFREQ) == xt->xf && + sc->cid.chip_id != BHND_CHIPID_BCM4319 && + sc->cid.chip_id != BHND_CHIPID_BCM4330) + { + PMU_MSG(("PLL already programmed for %d.%dMHz\n", + xt->fref / 1000, xt->fref % 1000)); + return; + } + + PMU_MSG(("XTAL %d.%dMHz (%d)\n", xtal / 1000, xtal % 1000, xt->xf)); + PMU_MSG(("Programming PLL for %d.%dMHz\n", xt->fref / 1000, + xt->fref % 1000)); + + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4325: + /* Change the BBPLL drive strength to 2 for all channels */ + buf_strength = 0x222222; + + BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, + ~(PMURES_BIT(RES4325_BBPLL_PWRSW_PU) | + PMURES_BIT(RES4325_HT_AVAIL))); + BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK, + ~(PMURES_BIT(RES4325_BBPLL_PWRSW_PU) | + PMURES_BIT(RES4325_HT_AVAIL))); + + /* Wait for HT clock to shutdown. */ + PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL); + break; + + case BHND_CHIPID_BCM4329: + /* Change the BBPLL drive strength to 8 for all channels */ + buf_strength = 0x888888; + + BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, + ~(PMURES_BIT(RES4329_BBPLL_PWRSW_PU) | + PMURES_BIT(RES4329_HT_AVAIL))); + BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK, + ~(PMURES_BIT(RES4329_BBPLL_PWRSW_PU) | + PMURES_BIT(RES4329_HT_AVAIL))); + + /* Wait for HT clock to shutdown. */ + PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL); + + /* Initialize PLL4 */ + plladdr = BHND_PMU1_PLL0_PLLCTL4; + if (xt->fref == 38400) + plldata = 0x200024C0; + else if (xt->fref == 37400) + plldata = 0x20004500; + else if (xt->fref == 26000) + plldata = 0x200024C0; + else + plldata = 0x200005C0; /* Chip Dflt Settings */ + + BHND_PMU_PLL_WRITE(sc, plladdr, plldata, ~0); + + /* Initialize PLL5 */ + plladdr = BHND_PMU1_PLL0_PLLCTL5; + + plldata = BHND_PMU_PLL_READ(sc, plladdr); + plldata &= BHND_PMU1_PLL0_PC5_CLK_DRV_MASK; + + if (xt->fref == 38400 || + xt->fref == 37400 || + xt->fref == 26000) { + plldata |= 0x15; + } else { + plldata |= 0x25; /* Chip Dflt Settings */ + } + + BHND_PMU_PLL_WRITE(sc, plladdr, plldata, ~0); + break; + + case BHND_CHIPID_BCM4319: + /* Change the BBPLL drive strength to 2 for all channels */ + buf_strength = 0x222222; + + /* Make sure the PLL is off */ + /* WAR65104: Disable the HT_AVAIL resource first and then + * after a delay (more than downtime for HT_AVAIL) remove the + * BBPLL resource; backplane clock moves to ALP from HT. + */ + BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, + ~(PMURES_BIT(RES4319_HT_AVAIL))); + BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK, + ~(PMURES_BIT(RES4319_HT_AVAIL))); + + DELAY(100); + BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, + ~(PMURES_BIT(RES4319_BBPLL_PWRSW_PU))); + BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK, + ~(PMURES_BIT(RES4319_BBPLL_PWRSW_PU))); + + DELAY(100); + + /* Wait for HT clock to shutdown. */ + PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL); + + plldata = 0x200005c0; + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, plldata, ~0); + break; + + case BHND_CHIPID_BCM4336: + BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, + ~(PMURES_BIT(RES4336_HT_AVAIL) | + PMURES_BIT(RES4336_MACPHY_CLKAVAIL))); + BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK, + ~(PMURES_BIT(RES4336_HT_AVAIL) | + PMURES_BIT(RES4336_MACPHY_CLKAVAIL))); + DELAY(100); + + /* Wait for HT clock to shutdown. */ + PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL); + + break; + + case BHND_CHIPID_BCM4330: + BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, + ~(PMURES_BIT(RES4330_HT_AVAIL) | + PMURES_BIT(RES4330_MACPHY_CLKAVAIL))); + BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK, + ~(PMURES_BIT(RES4330_HT_AVAIL) | + PMURES_BIT(RES4330_MACPHY_CLKAVAIL))); + DELAY(100); + + /* Wait for HT clock to shutdown. */ + PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL); + + break; + + default: + panic("unsupported chipid %#hx\n", sc->cid.chip_id); + } + + PMU_MSG(("Done masking\n")); + + /* Write p1div and p2div to pllcontrol[0] */ + plldata = + BHND_PMU_SET_BITS(xt->p1div, BHND_PMU1_PLL0_PC0_P1DIV) | + BHND_PMU_SET_BITS(xt->p2div, BHND_PMU1_PLL0_PC0_P2DIV); + pllmask = BHND_PMU1_PLL0_PC0_P1DIV_MASK|BHND_PMU1_PLL0_PC0_P2DIV_MASK; + + if (sc->cid.chip_id == BHND_CHIPID_BCM4319) { + plldata &= ~(BHND_PMU1_PLL0_PC0_BYPASS_SDMOD_MASK); + pllmask |= BHND_PMU1_PLL0_PC0_BYPASS_SDMOD_MASK; + if (!xt->ndiv_frac) { + plldata |= BHND_PMU_SET_BITS(1, + BHND_PMU1_PLL0_PC0_BYPASS_SDMOD); + } + } + + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, plldata, pllmask); + + + if (sc->cid.chip_id == BHND_CHIPID_BCM4330) + bhnd_pmu_set_4330_plldivs(sc); + + if (sc->cid.chip_id == BHND_CHIPID_BCM4329 && sc->cid.chip_rev == 0) { + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, + BHND_PMU_DOT11MAC_880MHZ_CLK_DIVISOR_VAL, + BHND_PMU_DOT11MAC_880MHZ_CLK_DIVISOR_MASK); + } + + /* Write ndiv_int and ndiv_mode to pllcontrol[2] */ + if (sc->cid.chip_id == BHND_CHIPID_BCM4336 || + sc->cid.chip_id == BHND_CHIPID_BCM4330) + { + ndiv_mode = BHND_PMU1_PLL0_PC2_NDIV_MODE_MFB; + } else if (sc->cid.chip_id == BHND_CHIPID_BCM4319) { + if (!(xt->ndiv_frac)) + ndiv_mode = BHND_PMU1_PLL0_PC2_NDIV_MODE_INT; + else + ndiv_mode = BHND_PMU1_PLL0_PC2_NDIV_MODE_MFB; + } else { + ndiv_mode = BHND_PMU1_PLL0_PC2_NDIV_MODE_MASH; + } + + + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, + BHND_PMU_SET_BITS(xt->ndiv_int, BHND_PMU1_PLL0_PC2_NDIV_INT) | + BHND_PMU_SET_BITS(ndiv_mode, BHND_PMU1_PLL0_PC2_NDIV_MODE), + BHND_PMU1_PLL0_PC2_NDIV_INT_MASK | + BHND_PMU1_PLL0_PC2_NDIV_MODE_MASK); + + /* Write ndiv_frac to pllcontrol[3] */ + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, + BHND_PMU_SET_BITS(xt->ndiv_frac, BHND_PMU1_PLL0_PC3_NDIV_FRAC), + BHND_PMU1_PLL0_PC3_NDIV_FRAC_MASK); + + /* Writing to pllcontrol[4] */ + if (sc->cid.chip_id == BHND_CHIPID_BCM4319) { + uint8_t xs; + + if (!xt->ndiv_frac) + plldata = 0x200005c0; + else + plldata = 0x202C2820; + + if (FVCO < 1600) + xs = 4; + else + xs = 7; + + plldata &= ~(BHND_PMU1_PLL0_PC4_KVCO_XS_MASK); + plldata |= BHND_PMU_SET_BITS(xs, BHND_PMU1_PLL0_PC4_KVCO_XS); + BHND_PMU_WRITE_4(sc, BHND_PMU1_PLL0_PLLCTL4, plldata); + } + + /* Write clock driving strength to pllcontrol[5] */ + if (buf_strength) { + PMU_MSG(("Adjusting PLL buffer drive strength: %x\n", + buf_strength)); + + plldata = BHND_PMU_SET_BITS(buf_strength, + BHND_PMU1_PLL0_PC5_CLK_DRV); + pllmask = BHND_PMU1_PLL0_PC5_CLK_DRV_MASK; + + if (sc->cid.chip_id == BHND_CHIPID_BCM4319) { + pllmask |= + BHND_PMU1_PLL0_PC5_VCO_RNG_MASK | + BHND_PMU1_PLL0_PC5_PLL_CTRL_37_32_MASK; + + if (!xt->ndiv_frac) { + plldata |= BHND_PMU_SET_BITS(0x25, + BHND_PMU1_PLL0_PC5_PLL_CTRL_37_32); + } else { + plldata |= BHND_PMU_SET_BITS(0x15, + BHND_PMU1_PLL0_PC5_PLL_CTRL_37_32); + } + + if (FVCO >= 1600) { + plldata |= BHND_PMU_SET_BITS(0x1, + BHND_PMU1_PLL0_PC5_VCO_RNG); + } + } + + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, plldata, + pllmask); + } + + PMU_MSG(("Done pll\n")); + + /* to operate the 4319 usb in 24MHz/48MHz; chipcontrol[2][84:83] needs + * to be updated. + */ + if (sc->cid.chip_id == BHND_CHIPID_BCM4319 && + xt->fref != XTAL_FREQ_30000MHZ) + { + uint32_t pll_sel; + + switch (xt->fref) { + case XTAL_FREQ_24000MHZ: + pll_sel = BHND_PMU_CCTL_4319USB_24MHZ_PLL_SEL; + break; + case XTAL_FREQ_48000MHZ: + pll_sel = BHND_PMU_CCTL_4319USB_48MHZ_PLL_SEL; + break; + default: + panic("unsupported 4319USB XTAL frequency: %hu\n", + xt->fref); + } + + 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); + } + + /* Flush deferred pll control registers writes */ + if (BHND_PMU_REV(sc) >= 2) + BHND_PMU_OR_4(sc, BHND_PMU_CTRL, BHND_PMU_CTRL_PLL_PLLCTL_UPD); + + /* Write XtalFreq. Set the divisor also. */ + pmuctrl = BHND_PMU_READ_4(sc, BHND_PMU_CTRL); + pmuctrl = ~(BHND_PMU_CTRL_ILP_DIV_MASK | BHND_PMU_CTRL_XTALFREQ_MASK); + pmuctrl |= BHND_PMU_SET_BITS(((xt->fref + 127) / 128) - 1, + BHND_PMU_CTRL_ILP_DIV); + pmuctrl |= BHND_PMU_SET_BITS(xt->xf, BHND_PMU_CTRL_XTALFREQ); + + if (sc->cid.chip_id == BHND_CHIPID_BCM4329 && sc->cid.chip_rev == 0) { + /* clear the htstretch before clearing HTReqEn */ + BHND_PMU_AND_4(sc, BHND_PMU_CLKSTRETCH, ~BHND_PMU_CLKSTRETCH); + pmuctrl &= ~BHND_PMU_CTRL_HT_REQ_EN; + } + + BHND_PMU_WRITE_4(sc, BHND_PMU_CTRL, pmuctrl); +} + +/* query the CPU clock frequency */ +static uint32_t +bhnd_pmu1_cpuclk0(struct bhnd_pmu_softc *sc) +{ + uint32_t tmp, m1div; +#ifdef BCMDBG + uint32_t ndiv_int, ndiv_frac, p2div, p1div, fvco; + uint32_t fref; +#endif + uint32_t FVCO = bhnd_pmu1_pllfvco0(sc); + + /* Read m1div from pllcontrol[1] */ + tmp = BHND_PMU_PLL_READ(sc, BHND_PMU1_PLL0_PLLCTL1); + m1div = BHND_PMU_GET_BITS(tmp, BHND_PMU1_PLL0_PC1_M1DIV); + +#ifdef BCMDBG + /* Read p2div/p1div from pllcontrol[0] */ + tmp = BHND_PMU_PLL_READ(sc, BHND_PMU1_PLL0_PLLCTL0); + p2div = BHND_PMU_GET_BITS(tmp, BHND_PMU1_PLL0_PC0_P2DIV); + p1div = BHND_PMU_GET_BITS(tmp, BHND_PMU1_PLL0_PC0_P1DIV); + + /* Calculate fvco based on xtal freq and ndiv and pdiv */ + tmp = BHND_PMU_PLL_READ(sc, BHND_PMU1_PLL0_PLLCTL2); + ndiv_int = BHND_PMU_GET_BITS(tmp, BHND_PMU1_PLL0_PC2_NDIV_INT); + + tmp = BHND_PMU_PLL_READ(sc, BHND_PMU1_PLL0_PLLCTL3); + ndiv_frac = BHND_PMU_GET_BITS(tmp, BHND_PMU1_PLL0_PC3_NDIV_FRAC); + + fref = bhnd_pmu1_alpclk0(sc) / 1000; + + fvco = (fref * ndiv_int) << 8; + fvco += (fref * (ndiv_frac >> 12)) >> 4; + fvco += (fref * (ndiv_frac & 0xfff)) >> 12; + fvco >>= 8; + fvco *= p2div; + fvco /= p1div; + fvco /= 1000; + fvco *= 1000; + + PMU_MSG(("bhnd_pmu1_cpuclk0: ndiv_int %u ndiv_frac %u p2div %u " + "p1div %u fvco %u\n", ndiv_int, ndiv_frac, p2div, p1div, fvco)); + + FVCO = fvco; +#endif /* BCMDBG */ + + /* Return ARM/SB clock */ + return (FVCO / m1div * 1000); +} + +/* initialize PLL */ +void +bhnd_pmu_pll_init(struct bhnd_pmu_softc *sc, u_int xtalfreq) +{ + uint32_t max_mask, min_mask; + uint32_t res_ht, res_pll; + + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4312: + /* assume default works */ + break; + case BHND_CHIPID_BCM4322: + case BHND_CHIPID_BCM43221: + case BHND_CHIPID_BCM43231: + case BHND_CHIPID_BCM4342: + if (sc->cid.chip_rev != 0) + break; + + min_mask = BHND_PMU_READ_4(sc, BHND_PMU_MIN_RES_MASK); + max_mask = BHND_PMU_READ_4(sc, BHND_PMU_MIN_RES_MASK); + res_ht = PMURES_BIT(RES4322_HT_SI_AVAIL); + res_pll = PMURES_BIT(RES4322_SI_PLL_ON); + + /* Have to remove HT Avail request before powering off PLL */ + BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~res_ht); + BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK, ~res_ht); + PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL); + + /* Make sure the PLL is off */ + BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~res_pll); + BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK, ~res_pll); + PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL); + + DELAY(1000); + + BHND_PMU_PLL_WRITE(sc, BHND_PMU2_SI_PLL_PLLCTL, 0x380005c0, ~0); + DELAY(100); + + BHND_PMU_WRITE_4(sc, BHND_PMU_MAX_RES_MASK, max_mask); + DELAY(100); + BHND_PMU_WRITE_4(sc, BHND_PMU_MIN_RES_MASK, min_mask); + DELAY(100); + + break; + case BHND_CHIPID_BCM4325: + bhnd_pmu1_pllinit0(sc, xtalfreq); + break; + case BHND_CHIPID_BCM4328: + bhnd_pmu0_pllinit0(sc, xtalfreq); + break; + case BHND_CHIPID_BCM5354: + if (xtalfreq == 0) + xtalfreq = 25000; + bhnd_pmu0_pllinit0(sc, xtalfreq); + break; + case BHND_CHIPID_BCM4329: + if (xtalfreq == 0) + xtalfreq = 38400; + bhnd_pmu1_pllinit0(sc, xtalfreq); + break; + + case BHND_CHIPID_BCM4313: + case BHND_CHIPID_BCM43222: + case BHND_CHIPID_BCM43111: + case BHND_CHIPID_BCM43112: + case BHND_CHIPID_BCM43224: + case BHND_CHIPID_BCM43225: + case BHND_CHIPID_BCM43420: + case BHND_CHIPID_BCM43421: + case BHND_CHIPID_BCM43226: + case BHND_CHIPID_BCM43235: + case BHND_CHIPID_BCM43236: + case BHND_CHIPID_BCM43238: + case BHND_CHIPID_BCM43234: + case BHND_CHIPID_BCM43237: + case BHND_CHIPID_BCM4331: + case BHND_CHIPID_BCM43431: + case BHND_CHIPID_BCM43131: + case BHND_CHIPID_BCM43227: + case BHND_CHIPID_BCM43228: + case BHND_CHIPID_BCM43428: + case BHND_CHIPID_BCM6362: + /* assume default works */ + break; + + case BHND_CHIPID_BCM4315: + case BHND_CHIPID_BCM4319: + case BHND_CHIPID_BCM4336: + case BHND_CHIPID_BCM4330: + bhnd_pmu1_pllinit0(sc, xtalfreq); + break; + default: + PMU_MSG(("No PLL init done for chip %#hx rev %d pmurev %d\n", + sc->cid.chip_id, sc->cid.chip_rev, BHND_PMU_REV(sc))); + break; + } +} + +/* query alp/xtal clock frequency */ +uint32_t +bhnd_pmu_alp_clock(struct bhnd_pmu_softc *sc) +{ + uint32_t clock; + + clock = BHND_PMU_ALP_CLOCK; + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4328: + case BHND_CHIPID_BCM5354: + clock = bhnd_pmu0_alpclk0(sc); + break; + case BHND_CHIPID_BCM4315: + case BHND_CHIPID_BCM4319: + case BHND_CHIPID_BCM4325: + case BHND_CHIPID_BCM4329: + case BHND_CHIPID_BCM4330: + case BHND_CHIPID_BCM4336: + clock = bhnd_pmu1_alpclk0(sc); + break; + case BHND_CHIPID_BCM4312: + case BHND_CHIPID_BCM4322: + case BHND_CHIPID_BCM43221: + case BHND_CHIPID_BCM43231: + case BHND_CHIPID_BCM43222: + case BHND_CHIPID_BCM43111: + case BHND_CHIPID_BCM43112: + case BHND_CHIPID_BCM43224: + case BHND_CHIPID_BCM43225: + case BHND_CHIPID_BCM43420: + case BHND_CHIPID_BCM43421: + case BHND_CHIPID_BCM43226: + case BHND_CHIPID_BCM43235: + case BHND_CHIPID_BCM43236: + case BHND_CHIPID_BCM43238: + case BHND_CHIPID_BCM43234: + case BHND_CHIPID_BCM43237: + case BHND_CHIPID_BCM4331: + case BHND_CHIPID_BCM43431: + case BHND_CHIPID_BCM43131: + case BHND_CHIPID_BCM43227: + case BHND_CHIPID_BCM43228: + case BHND_CHIPID_BCM43428: + case BHND_CHIPID_BCM6362: + case BHND_CHIPID_BCM4342: + case BHND_CHIPID_BCM4716: + case BHND_CHIPID_BCM4748: + case BHND_CHIPID_BCM47162: + case BHND_CHIPID_BCM4313: + case BHND_CHIPID_BCM5357: + case BHND_CHIPID_BCM4749: + case BHND_CHIPID_BCM53572: + /* always 20Mhz */ + clock = 20000 * 1000; + break; + case BHND_CHIPID_BCM5356: + case BHND_CHIPID_BCM4706: + /* always 25Mhz */ + clock = 25000 * 1000; + break; + default: + PMU_MSG(("No ALP clock specified " + "for chip %s rev %d pmurev %d, using default %d Hz\n", + bcm_chipname(sih->chip, chn, 8), sih->chiprev, + sih->pmurev, clock)); + break; + } + + return (clock); +} + +/* Find the output of the "m" pll divider given pll controls that start with + * pllreg "pll0" i.e. 12 for main 6 for phy, 0 for misc. + */ +static uint32_t +bhnd_pmu5_clock(struct bhnd_pmu_softc *sc, u_int pll0, u_int m) +{ + uint32_t div; + uint32_t fc; + uint32_t ndiv; + uint32_t p1, p2; + uint32_t tmp; + + if ((pll0 & 3) || (pll0 > BHND_PMU4716_MAINPLL_PLL0)) { + PMU_ERROR(("%s: Bad pll0: %d\n", __func__, pll0)); + return (0); + } + + /* Strictly there is an m5 divider, but I'm not sure we use it */ + if ((m == 0) || (m > 4)) { + PMU_ERROR(("%s: Bad m divider: %d\n", __func__, m)); + return (0); + } + + if (sc->cid.chip_id == BHND_CHIPID_BCM5357 || + sc->cid.chip_id == BHND_CHIPID_BCM4749) + { + /* Detect failure in clock setting */ + tmp = BHND_CHIPC_READ_CHIPST(sc->chipc_dev); + if ((tmp & 0x40000) != 0) + return (133 * 1000000); + } + + + /* Fetch p1 and p2 */ + BHND_PMU_WRITE_4(sc, BHND_PMU_PLL_CONTROL_ADDR, + pll0 + BHND_PMU5_PLL_P1P2_OFF); + BHND_PMU_READ_4(sc, BHND_PMU_PLL_CONTROL_ADDR); + + tmp = BHND_PMU_READ_4(sc, BHND_PMU_PLL_CONTROL_DATA); + p1 = BHND_PMU_GET_BITS(tmp, BHND_PMU5_PLL_P1); + p2 = BHND_PMU_GET_BITS(tmp, BHND_PMU5_PLL_P2); + + /* Fetch div */ + BHND_PMU_WRITE_4(sc, BHND_PMU_PLL_CONTROL_ADDR, + pll0 + BHND_PMU5_PLL_M14_OFF); + BHND_PMU_READ_4(sc, BHND_PMU_PLL_CONTROL_ADDR); + + tmp = BHND_PMU_READ_4(sc, BHND_PMU_PLL_CONTROL_DATA); + div = (tmp >> ((m - 1) * BHND_PMU5_PLL_MDIV_WIDTH)); + div &= BHND_PMU5_PLL_MDIV_MASK; + + /* Fetch ndiv */ + BHND_PMU_WRITE_4(sc, BHND_PMU_PLL_CONTROL_ADDR, + pll0 + BHND_PMU5_PLL_NM5_OFF); + BHND_PMU_READ_4(sc, BHND_PMU_PLL_CONTROL_ADDR); + + tmp = BHND_PMU_READ_4(sc, BHND_PMU_PLL_CONTROL_DATA); + ndiv = BHND_PMU_GET_BITS(tmp, BHND_PMU5_PLL_NDIV); + + /* Do calculation in Mhz */ + fc = bhnd_pmu_alp_clock(sc) / 1000000; + fc = (p1 * ndiv * fc) / p2; + + PMU_MSG(("%s: p1=%d, p2=%d, ndiv=%d(0x%x), m%d=%d; fc=%d, clock=%d\n", + __func__, p1, p2, ndiv, ndiv, m, div, fc, fc / div)); + + /* Return clock in Hertz */ + return ((fc / div) * 1000000); +} + +/* query backplane clock frequency */ +/* For designs that feed the same clock to both backplane + * and CPU just return the CPU clock speed. + */ +uint32_t +bhnd_pmu_si_clock(struct bhnd_pmu_softc *sc) +{ + uint32_t chipst; + uint32_t clock; + + clock = BHND_PMU_HT_CLOCK; + + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4322: + case BHND_CHIPID_BCM43221: + case BHND_CHIPID_BCM43231: + case BHND_CHIPID_BCM43222: + case BHND_CHIPID_BCM43111: + case BHND_CHIPID_BCM43112: + case BHND_CHIPID_BCM43224: + case BHND_CHIPID_BCM43420: + case BHND_CHIPID_BCM43225: + case BHND_CHIPID_BCM43421: + case BHND_CHIPID_BCM43226: + case BHND_CHIPID_BCM4331: + case BHND_CHIPID_BCM43431: + case BHND_CHIPID_BCM6362: + case BHND_CHIPID_BCM4342: + /* 96MHz backplane clock */ + clock = 96000 * 1000; + break; + + case BHND_CHIPID_BCM4716: + case BHND_CHIPID_BCM4748: + case BHND_CHIPID_BCM47162: + clock = bhnd_pmu5_clock(sc, BHND_PMU4716_MAINPLL_PLL0, + BHND_PMU5_MAINPLL_SI); + break; + + case BHND_CHIPID_BCM4325: + clock = bhnd_pmu1_cpuclk0(sc); + break; + + case BHND_CHIPID_BCM4328: + clock = bhnd_pmu0_cpuclk0(sc); + break; + + case BHND_CHIPID_BCM4329: + if (sc->cid.chip_rev == 0) + clock = 38400 * 1000; + else + clock = bhnd_pmu1_cpuclk0(sc); + break; + + case BHND_CHIPID_BCM4315: + case BHND_CHIPID_BCM4319: + case BHND_CHIPID_BCM4336: + case BHND_CHIPID_BCM4330: + clock = bhnd_pmu1_cpuclk0(sc); + break; + + case BHND_CHIPID_BCM4313: + /* 80MHz backplane clock */ + clock = 80000 * 1000; + break; + + case BHND_CHIPID_BCM43234: + case BHND_CHIPID_BCM43235: + case BHND_CHIPID_BCM43236: + case BHND_CHIPID_BCM43238: + chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev); + if (chipst & CHIPC_CST43236_BP_CLK) + clock = 120000 * 1000; + else + clock = 96000 * 1000; + break; + case BHND_CHIPID_BCM43237: + chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev); + if (chipst & CHIPC_CST43237_BP_CLK) + clock = 96000 * 1000; + else + clock = 80000 * 1000; + break; + case BHND_CHIPID_BCM5356: + clock = bhnd_pmu5_clock(sc, BHND_PMU5356_MAINPLL_PLL0, + BHND_PMU5_MAINPLL_SI); + break; + case BHND_CHIPID_BCM5357: + case BHND_CHIPID_BCM4749: + clock = bhnd_pmu5_clock(sc, BHND_PMU5357_MAINPLL_PLL0, + BHND_PMU5_MAINPLL_SI); + break; + case BHND_CHIPID_BCM53572: + clock = 75000000; + break; + default: + device_printf(sc->dev, "No backplane clock specified for chip " + "%#hx rev %hhd pmurev %hhd, using default %dHz\n", + sc->cid.chip_id, sc->cid.chip_rev, BHND_PMU_REV(sc), clock); + break; + } + + return (clock); +} + +/* query CPU clock frequency */ +uint32_t +bhnd_pmu_cpu_clock(struct bhnd_pmu_softc *sc) +{ + uint32_t clock; + + if (sc->cid.chip_id == BHND_CHIPID_BCM5354) + return (240 * 1000 * 1000); /* 240MHz */ + + /* 5354 chip uses a non programmable PLL of frequency 240MHz */ + if (sc->cid.chip_id == BHND_CHIPID_BCM5354) + return (240000000); + + if (sc->cid.chip_id == BHND_CHIPID_BCM53572) + return (300000000); + + if (BHND_PMU_REV(sc) >= 5 && + sc->cid.chip_id != BHND_CHIPID_BCM4329 && + sc->cid.chip_id != BHND_CHIPID_BCM4319 && + sc->cid.chip_id != BHND_CHIPID_BCM43234 && + sc->cid.chip_id != BHND_CHIPID_BCM43235 && + sc->cid.chip_id != BHND_CHIPID_BCM43236 && + sc->cid.chip_id != BHND_CHIPID_BCM43237 && + sc->cid.chip_id != BHND_CHIPID_BCM43238 && + sc->cid.chip_id != BHND_CHIPID_BCM4336 && + sc->cid.chip_id != BHND_CHIPID_BCM4330) + { + u_int pll; + + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM5356: + pll = BHND_PMU5356_MAINPLL_PLL0; + break; + case BHND_CHIPID_BCM5357: + case BHND_CHIPID_BCM4749: + pll = BHND_PMU5357_MAINPLL_PLL0; + break; + default: + pll = BHND_PMU4716_MAINPLL_PLL0; + break; + } + + clock = bhnd_pmu5_clock(sc, pll, BHND_PMU5_MAINPLL_CPU); + } else { + clock = bhnd_pmu_si_clock(sc); + } + + return (clock); +} + +/* query memory clock frequency */ +uint32_t +bhnd_pmu_mem_clock(struct bhnd_pmu_softc *sc) +{ + uint32_t clock; + + if (BHND_PMU_REV(sc) >= 5 && + sc->cid.chip_id != BHND_CHIPID_BCM4329 && + sc->cid.chip_id != BHND_CHIPID_BCM4319 && + sc->cid.chip_id != BHND_CHIPID_BCM43234 && + sc->cid.chip_id != BHND_CHIPID_BCM43235 && + sc->cid.chip_id != BHND_CHIPID_BCM43236 && + sc->cid.chip_id != BHND_CHIPID_BCM43237 && + sc->cid.chip_id != BHND_CHIPID_BCM43238 && + sc->cid.chip_id != BHND_CHIPID_BCM4336 && + sc->cid.chip_id != BHND_CHIPID_BCM4330) + { + u_int pll; + + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM5356: + pll = BHND_PMU5356_MAINPLL_PLL0; + break; + case BHND_CHIPID_BCM5357: + case BHND_CHIPID_BCM4749: + pll = BHND_PMU5357_MAINPLL_PLL0; + break; + default: + pll = BHND_PMU4716_MAINPLL_PLL0; + break; + } + + clock = bhnd_pmu5_clock(sc, pll, BHND_PMU5_MAINPLL_MEM); + } else { + clock = bhnd_pmu_si_clock(sc); + } + + return (clock); +} + +/* Measure ILP clock frequency */ +#define ILP_CALC_DUR 10 /* ms, make sure 1000 can be divided by it. */ + +uint32_t +bhnd_pmu_ilp_clock(struct bhnd_pmu_softc *sc) +{ + uint32_t start, end, delta; + + if (sc->ilp_cps == 0) { + start = BHND_PMU_READ_4(sc, BHND_PMU_TIMER); + DELAY(ILP_CALC_DUR); + end = BHND_PMU_READ_4(sc, BHND_PMU_TIMER); + delta = end - start; + sc->ilp_cps = delta * (1000 / ILP_CALC_DUR); + } + + return (sc->ilp_cps); +} + +/* SDIO Pad drive strength to select value mappings */ +typedef struct { + uint8_t strength; /* Pad Drive Strength in mA */ + uint8_t sel; /* Chip-specific select value */ +} sdiod_drive_str_t; + +/* SDIO Drive Strength to sel value table for PMU Rev 1 */ +static const sdiod_drive_str_t sdiod_drive_strength_tab1[] = { + { + 4, 0x2}, { + 2, 0x3}, { + 1, 0x0}, { + 0, 0x0} + }; + +/* SDIO Drive Strength to sel value table for PMU Rev 2, 3 */ +static const sdiod_drive_str_t sdiod_drive_strength_tab2[] = { + { + 12, 0x7}, { + 10, 0x6}, { + 8, 0x5}, { + 6, 0x4}, { + 4, 0x2}, { + 2, 0x1}, { + 0, 0x0} + }; + +/* SDIO Drive Strength to sel value table for PMU Rev 8 (1.8V) */ +static const sdiod_drive_str_t sdiod_drive_strength_tab3[] = { + { + 32, 0x7}, { + 26, 0x6}, { + 22, 0x5}, { + 16, 0x4}, { + 12, 0x3}, { + 8, 0x2}, { + 4, 0x1}, { + 0, 0x0} + }; + +#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) + +void +bhnd_pmu_sdiod_drive_strength_init(struct bhnd_pmu_softc *sc, + uint32_t drivestrength) +{ + const sdiod_drive_str_t *str_tab; + uint32_t str_mask; + uint32_t str_shift; + u_int intr_val; + + str_tab = NULL; + str_mask = 0; + str_shift = 0; + intr_val = 0; + + switch (SDIOD_DRVSTR_KEY(sc->cid.chip_id, BHND_PMU_REV(sc))) { + case SDIOD_DRVSTR_KEY(BHND_CHIPID_BCM4325, 1): + str_tab = sdiod_drive_strength_tab1; + str_mask = 0x30000000; + str_shift = 28; + break; + case SDIOD_DRVSTR_KEY(BHND_CHIPID_BCM4325, 2): + case SDIOD_DRVSTR_KEY(BHND_CHIPID_BCM4325, 3): + case SDIOD_DRVSTR_KEY(BHND_CHIPID_BCM4315, 4): + case SDIOD_DRVSTR_KEY(BHND_CHIPID_BCM4319, 7): + str_tab = sdiod_drive_strength_tab2; + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BHND_CHIPID_BCM4336, 8): + str_tab = sdiod_drive_strength_tab3; + str_mask = 0x00003800; + str_shift = 11; + break; + + default: + device_printf(sc->dev, "No SDIO Drive strength init done for " + "chip %#x rev %hhd pmurev %hhd\n", sc->cid.chip_id, + sc->cid.chip_rev, BHND_PMU_REV(sc)); + break; + } + + if (str_tab != NULL) { + uint32_t drivestrength_sel = 0; + uint32_t cc_data_temp; + + for (u_int i = 0; str_tab[i].strength != 0; i++) { + if (drivestrength >= str_tab[i].strength) { + drivestrength_sel = str_tab[i].sel; + break; + } + } + + cc_data_temp = BHND_PMU_CCTRL_READ(sc, 1); + cc_data_temp &= ~str_mask; + drivestrength_sel <<= str_shift; + cc_data_temp |= drivestrength_sel; + BHND_PMU_CCTRL_WRITE(sc, 1, cc_data_temp, ~0); + + PMU_MSG(("SDIO: %dmA drive strength selected, set to 0x%08x\n", + drivestrength, cc_data_temp)); + } +} + +/** + * Initialize the PMU. + */ +int +bhnd_pmu_init(struct bhnd_pmu_softc *sc) +{ + uint32_t xtalfreq; + int error; + + if (BHND_PMU_REV(sc) == 1) { + BHND_PMU_AND_4(sc, BHND_PMU_CTRL, ~BHND_PMU_CTRL_NOILP_ON_WAIT); + } else if (BHND_PMU_REV(sc) >= 2) { + BHND_PMU_OR_4(sc, BHND_PMU_CTRL, BHND_PMU_CTRL_NOILP_ON_WAIT); + } + + if (sc->cid.chip_id == BHND_CHIPID_BCM4329 && sc->cid.chip_rev == 2) { + /* Fix for 4329b0 bad LPOM state. */ + BHND_PMU_REGCTRL_WRITE(sc, 2, 0x100, ~0); + BHND_PMU_REGCTRL_WRITE(sc, 3, 0x4, ~0); + } + + if (sc->cid.chip_id == BHND_CHIPID_BCM4319) { + /* Limiting the PALDO spike during init time */ + BHND_PMU_REGCTRL_WRITE(sc, 2, 0x00000005, 0x00000007); + } + + + /* Fetch target xtalfreq, in KHz */ + error = bhnd_nvram_getvar_uint32(sc->chipc_dev, BHND_NVAR_XTALFREQ, + &xtalfreq); + + /* If not available, log any real errors, and then try to measure it */ + if (error) { + if (error != ENOENT) { + device_printf(sc->dev, "error fetching xtalfreq: %d\n", + error); + } + + xtalfreq = bhnd_pmu_measure_alpclk(sc); + } + + /* Perform PLL initialization */ + bhnd_pmu_pll_init(sc, xtalfreq); + + if ((error = bhnd_pmu_res_init(sc))) + return (error); + + bhnd_pmu_swreg_init(sc); + + return (0); +} + +/* Return up time in ILP cycles for the given resource. */ +static int +bhnd_pmu_res_uptime(struct bhnd_pmu_softc *sc, uint8_t rsrc, uint32_t *uptime) +{ + uint32_t deps; + uint32_t up, dup, dmax; + uint32_t min_mask; + int error; + + /* uptime of resource 'rsrc' */ + BHND_PMU_WRITE_4(sc, BHND_PMU_RES_TABLE_SEL, rsrc); + up = BHND_PMU_READ_4(sc, BHND_PMU_RES_UPDN_TIMER); + up = BHND_PMU_GET_BITS(up, BHND_PMU_RES_UPDN_UPTME); + + /* Find direct dependencies of resource 'rsrc' */ + deps = bhnd_pmu_res_deps(sc, BHND_PMURES_BIT(rsrc), false); + for (uint8_t i = 0; i <= BHND_PMU_RESNUM_MAX; i++) { + if (!(deps & BHND_PMURES_BIT(i))) + continue; + deps &= ~bhnd_pmu_res_deps(sc, BHND_PMURES_BIT(i), true); + } + + /* Exclude the minimum resource set */ + if ((error = bhnd_pmu_res_masks(sc, &min_mask, NULL))) + return (error); + + deps &= ~min_mask; + + /* max uptime of direct dependencies */ + dmax = 0; + for (uint8_t i = 0; i <= BHND_PMU_RESNUM_MAX; i++) { + if (!(deps & BHND_PMURES_BIT(i))) + continue; + + if ((error = bhnd_pmu_res_uptime(sc, i, &dup))) + return (error); + + if (dmax < dup) + dmax = dup; + } + + PMU_MSG(("bhnd_pmu_res_uptime: rsrc %hhu uptime %u(deps 0x%08x " + "uptime %u)\n", rsrc, up, deps, dmax)); + + *uptime = (up + dmax + BHND_PMURES_UP_TRANSITION); + return (0); +} + +/* Return dependencies (direct or all/indirect) for the given resources */ +static uint32_t +bhnd_pmu_res_deps(struct bhnd_pmu_softc *sc, uint32_t rsrcs, bool all) +{ + uint32_t deps; + + deps = 0; + for (uint8_t i = 0; i <= BHND_PMU_RESNUM_MAX; i++) { + if (!(rsrcs & BHND_PMURES_BIT(i))) + continue; + + BHND_PMU_WRITE_4(sc, BHND_PMU_RES_TABLE_SEL, i); + deps |= BHND_PMU_READ_4(sc, BHND_PMU_RES_DEP_MASK); + } + + /* None found? */ + if (deps == 0) + return (0); + + /* Recurse dependencies */ + if (all) + deps |= bhnd_pmu_res_deps(sc, deps, true); + + return (deps); +} + +/* power up/down OTP through PMU resources */ +int +bhnd_pmu_otp_power(struct bhnd_pmu_softc *sc, bool on) +{ + uint32_t deps; + uint32_t min_mask; + uint32_t rsrcs; + int error; + + /* Determine rsrcs to turn on/off OTP power */ + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4322: + case BHND_CHIPID_BCM43221: + case BHND_CHIPID_BCM43231: + case BHND_CHIPID_BCM4342: + rsrcs = PMURES_BIT(RES4322_OTP_PU); + break; + case BHND_CHIPID_BCM4315: + rsrcs = PMURES_BIT(RES4315_OTP_PU); + break; + case BHND_CHIPID_BCM4325: + rsrcs = PMURES_BIT(RES4325_OTP_PU); + break; + case BHND_CHIPID_BCM4329: + rsrcs = PMURES_BIT(RES4329_OTP_PU); + break; + case BHND_CHIPID_BCM4319: + rsrcs = PMURES_BIT(RES4319_OTP_PU); + break; + case BHND_CHIPID_BCM4336: + rsrcs = PMURES_BIT(RES4336_OTP_PU); + break; + case BHND_CHIPID_BCM4330: + rsrcs = PMURES_BIT(RES4330_OTP_PU); + break; + default: + /* Not required? */ + return (0); + } + + /* Fetch all dependencies */ + deps = bhnd_pmu_res_deps(sc, rsrcs, true); + + /* Exclude the minimum resource set */ + if ((error = bhnd_pmu_res_masks(sc, &min_mask, NULL))) + return (error); + + deps &= ~min_mask; + + /* Turn on/off the power */ + if (on) { + uint32_t state; + + PMU_MSG(("Adding rsrc 0x%x to min_res_mask\n", + rsrcs | deps)); + BHND_PMU_OR_4(sc, BHND_PMU_MIN_RES_MASK, (rsrcs|deps)); + + /* Wait for all resources to become available */ + for (int i = 0; i < BHND_PMU_MAX_TRANSITION_DLY; i += 10) { + state = BHND_PMU_READ_4(sc, BHND_PMU_RES_STATE); + if ((state & rsrcs) == rsrcs) + break; + + DELAY(10); + } + + if ((state & rsrcs) != rsrcs) { + device_printf(sc->dev, "timeout waiting for OTP " + "resource enable\n"); + return (ENXIO); + } + } else { + PMU_MSG(("Removing rsrc 0x%x from min_res_mask\n", + rsrcs | deps)); + BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~(rsrcs|deps)); + } + + return (0); +} + +void +bhnd_pmu_rcal(struct bhnd_pmu_softc *sc) +{ + uint32_t chipst; + uint32_t val; + uint8_t rcal_code; + bool bluetooth_rcal; + + + bluetooth_rcal = false; + + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4325: + case BHND_CHIPID_BCM4329: + /* Kick RCal */ + BHND_PMU_WRITE_4(sc, BHND_PMU_CHIPCTL_ADDR, 1); + + /* Power Down RCAL Block */ + BHND_PMU_AND_4(sc, BHND_PMU_CHIPCTL_DATA, ~0x04); + + if (sc->cid.chip_id == BHND_CHIPID_BCM4325) { + chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev); + if (BHND_PMU_GET_BITS(chipst, CHIPC_CST4325_RCAL_VALID)) + bluetooth_rcal = true; + } + + /* Power Up RCAL block */ + BHND_PMU_AND_4(sc, BHND_PMU_CHIPCTL_DATA, 0x04); + + /* Wait for completion */ + for (int i = 0; i < (10 * 1000 * 1000); i++) { + chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev); + + if (chipst & 0x08) + break; + + DELAY(10); + } + KASSERT((chipst & 0x08) != 0, ("rcal completion timeout")); + + if (bluetooth_rcal) { + rcal_code = 0x6; + } else { + /* Drop LSB to convert from 5 bit code to 4 bit code */ + rcal_code = (uint8_t) (chipst >> 5) & 0x0f; + } + + PMU_MSG(("RCal completed, status 0x%x, code 0x%x\n", + R_REG(&cc->chipstatus), rcal_code)); + + /* Write RCal code into pmu_vreg_ctrl[32:29] */ + BHND_PMU_WRITE_4(sc, BHND_PMU_REG_CONTROL_ADDR, 0); + val = BHND_PMU_READ_4(sc, BHND_PMU_REG_CONTROL_DATA); + val &= ~((uint32_t) 0x07 << 29); + val |= (uint32_t) (rcal_code & 0x07) << 29; + BHND_PMU_WRITE_4(sc, BHND_PMU_REG_CONTROL_DATA, val); + + BHND_PMU_WRITE_4(sc, BHND_PMU_REG_CONTROL_ADDR, 1); + val = BHND_PMU_READ_4(sc, BHND_PMU_REG_CONTROL_DATA); + val &= ~(uint32_t) 0x01; + val |= (uint32_t) ((rcal_code >> 3) & 0x01); + 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); + 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_CHIPCTL_ADDR, 1); + val = BHND_PMU_READ_4(sc, BHND_PMU_CHIPCTL_DATA); + val &= ~(uint32_t) 0x03; + val |= (uint32_t) ((rcal_code >> 2) & 0x03); + BHND_PMU_WRITE_4(sc, BHND_PMU_CHIPCTL_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)); + + /* Power off RCal block */ + BHND_PMU_WRITE_4(sc, BHND_PMU_CHIPCTL_ADDR, 1); + BHND_PMU_AND_4(sc, BHND_PMU_CHIPCTL_DATA, ~0x04); + break; + default: + break; + } +} + +void +bhnd_pmu_spuravoid(struct bhnd_pmu_softc *sc, uint8_t spuravoid) +{ + /* force the HT off */ + if (sc->cid.chip_id == BHND_CHIPID_BCM4336) { + BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK, + ~BHND_PMU_RES4336_HT_AVAIL); + + /* wait for the ht to really go away */ + PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL); + } + + /* update the pll changes */ + 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); + } +} + +static void +bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, uint8_t spuravoid) +{ + uint16_t chip_id; + uint32_t tmp; + uint32_t pmuctrl; + uint8_t phypll_offset; + + uint8_t bcm5357_bcm43236_p1div[] = { 0x1, 0x5, 0x5 }; + uint8_t bcm5357_bcm43236_ndiv[] = { 0x30, 0xf6, 0xfc }; + + /* 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) { + chip_id = BHND_CHIPID_BCM43224; + } + + switch (chip_id) { + case BHND_CHIPID_BCM6362: + KASSERT(sc->cid.chip_rev != 0, ("invalid clock config")); + /* fallthrough */ + case BHND_CHIPID_BCM5357: + case BHND_CHIPID_BCM4749: + case BHND_CHIPID_BCM43235: + case BHND_CHIPID_BCM43236: + 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)); + + KASSERT(spuravoid < nitems(bcm5357_bcm43236_ndiv), + ("spuravoid %hhu outside ndiv table\n", spuravoid)); + + /* BCM5357 needs to touch PLL1_PLLCTL[02], so offset + * PLL0_PLLCTL[02] by 6 */ + phypll_offset = 0; + if (sc->cid.chip_id == BHND_CHIPID_BCM5357) + phypll_offset = 6; + + /* RMW only the P1 divider */ + tmp = BHND_PMU_SET_BITS(bcm5357_bcm43236_p1div[spuravoid], + 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); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2 + phypll_offset, + tmp, BHND_PMU1_PLL0_PC0_P1DIV_MASK); + + pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD; + break; + + case BHND_CHIPID_BCM4331: + if (spuravoid == 2) { + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, + 0x11500014, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, + 0x0FC00a08, ~0); + } else if (spuravoid == 1) { + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, + 0x11500014, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, + 0x0F600a08, ~0); + } else { + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, + 0x11100014, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, + 0x03000a08, ~0); + } + pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD; + break; + + case BHND_CHIPID_BCM43224: + case BHND_CHIPID_BCM43225: + case BHND_CHIPID_BCM43226: + case BHND_CHIPID_BCM43421: + if (spuravoid == 1) { + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, + 0x11500010, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, + 0x000C0C06, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, + 0x0F600a08, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, + 0x00000000, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, + 0x2001E920, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, + 0x88888815, ~0); + } else { + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, + 0x11100010, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, + 0x000c0c06, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, + 0x03000a08, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, + 0x00000000, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, + 0x200005c0, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, + 0x88888815, ~0); + } + pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD; + break; + + case BHND_CHIPID_BCM43111: + case BHND_CHIPID_BCM43112: + case BHND_CHIPID_BCM43222: + case BHND_CHIPID_BCM43420: + if (spuravoid == 1) { + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, + 0x11500008, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, + 0x0c000c06, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, + 0x0f600a08, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, + 0x00000000, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, + 0x2001e920, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, + 0x88888815, ~0); + } else { + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, + 0x11100008, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, + 0x0c000c06, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, + 0x03000a08, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, + 0x00000000, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, + 0x200005c0, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, + 0x88888855, ~0); + } + + pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD; + break; + + case BHND_CHIPID_BCM4716: + case BHND_CHIPID_BCM4748: + case BHND_CHIPID_BCM47162: + if (spuravoid == 1) { + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, + 0x11500060, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, + 0x080C0C06, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, + 0x0F600000, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, + 0x00000000, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, + 0x2001E924, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, + 0x88888815, ~0); + } else { + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, + 0x11100060, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, + 0x080c0c06, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, + 0x03000000, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, + 0x00000000, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, + 0x200005c0, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, + 0x88888815, ~0); + } + + pmuctrl = BHND_PMU_CTRL_NOILP_ON_WAIT | + BHND_PMU_CTRL_PLL_PLLCTL_UPD; + break; + + case BHND_CHIPID_BCM4319: + break; + + case BHND_CHIPID_BCM4322: + case BHND_CHIPID_BCM43221: + case BHND_CHIPID_BCM43231: + case BHND_CHIPID_BCM4342: + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x11100070, ~0); + 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 { + /* enable 40/80/160Mhz clock mode */ + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, + 0x05001828, ~0); + } + + pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD; + break; + + case BHND_CHIPID_BCM4336: + /* Looks like these are only for default xtal freq 26MHz */ + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x02100020, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, 0x0C0C0C0C, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, 0x01240C0C, ~0); + 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; + } + 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: + case BHND_CHIPID_BCM43428: + /* LCNXN */ + /* PLL Settings for spur avoidance on/off mode, no on2 support + * for 43228A0 */ + if (spuravoid == 1) { + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, + 0x01100014, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, + 0x040C0C06, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, + 0x03140A08, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, + 0x00333333, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, + 0x202C2820, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, + 0x88888815, ~0); + } else { + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, + 0x11100014, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, + 0x040c0c06, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, + 0x03000a08, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, + 0x00000000, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, + 0x200005c0, ~0); + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, + 0x88888815, ~0); + } + pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD; + break; + default: + PMU_ERROR(("%s: unknown spuravoidance settings for chip %#hx, " + "not changing PLL\n", __func__, sc->cid.chip_id)); + break; + } + + BHND_PMU_OR_4(sc, BHND_PMU_CTRL, pmuctrl); +} + +bool +bhnd_pmu_is_otp_powered(struct bhnd_pmu_softc *sc) +{ + uint32_t otp_res; + + /* Determine per-chip OTP resource */ + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4329: + otp_res = PMURES_BIT(RES4329_OTP_PU); + break; + case BHND_CHIPID_BCM4319: + otp_res = PMURES_BIT(RES4319_OTP_PU); + break; + case BHND_CHIPID_BCM4336: + otp_res = PMURES_BIT(RES4336_OTP_PU); + break; + case BHND_CHIPID_BCM4330: + otp_res = PMURES_BIT(RES4330_OTP_PU); + break; + + /* These chips don't use PMU bit to power up/down OTP. OTP always on. + * Use OTP_INIT command to reset/refresh state. + */ + case BHND_CHIPID_BCM43224: + case BHND_CHIPID_BCM43225: + case BHND_CHIPID_BCM43421: + case BHND_CHIPID_BCM43236: + case BHND_CHIPID_BCM43235: + case BHND_CHIPID_BCM43238: + return (true); + + default: + return (true); + } + + /* Check resource state */ + if ((BHND_PMU_READ_4(sc, BHND_PMU_RES_STATE) & otp_res) == 0) + return (false); + + return (true); +} + +void +bhnd_pmu_paref_ldo_enable(struct bhnd_pmu_softc *sc, bool enable) +{ + uint32_t ldo; + + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4328: + ldo = PMURES_BIT(RES4328_PA_REF_LDO); + break; + case BHND_CHIPID_BCM5354: + ldo = PMURES_BIT(RES5354_PA_REF_LDO); + break; + case BHND_CHIPID_BCM4312: + ldo = PMURES_BIT(RES4312_PA_REF_LDO); + break; + default: + return; + } + + if (enable) { + BHND_PMU_OR_4(sc, BHND_PMU_MIN_RES_MASK, ldo); + } else { + BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~ldo); + } +} + +/* initialize PMU switch/regulators */ +void +bhnd_pmu_swreg_init(struct bhnd_pmu_softc *sc) +{ + uint32_t chipst; + + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4325: + if (sc->cid.chip_rev <= 2) + break; + + chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev); + if (BHND_PMU_GET_BITS(chipst, CHIPC_CST4325_PMUTOP_2B)) { + bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_CLDO_PWM, + 0xf); + bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_CLDO_BURST, + 0xf); + } + + bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_CBUCK_PWM, 0xb); + bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_CBUCK_BURST, 0xb); + + bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_LNLDO1, 0x1); + if (sc->board.board_flags & BHND_BFL_LNLDO2_2P5) { + bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_LNLDO2_SEL, + 0x1); + } + + break; + case BHND_CHIPID_BCM4336: + /* Reduce CLDO PWM output voltage to 1.2V */ + bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_CLDO_PWM, 0xe); + /* Reduce CLDO BURST output voltage to 1.2V */ + bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_CLDO_BURST, 0xe); + /* Reduce LNLDO1 output voltage to 1.2V */ + bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_LNLDO1, 0xe); + if (sc->cid.chip_rev == 0) + BHND_PMU_REGCTRL_WRITE(sc, 2, 0x400000, 0x400000); + break; + + case BHND_CHIPID_BCM4330: + /* CBUCK Voltage is 1.8 by default and set that to 1.5 */ + bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_CBUCK_PWM, 0); + break; + default: + break; + } +} + +void +bhnd_pmu_radio_enable(struct bhnd_pmu_softc *sc, device_t d11core, bool enable) +{ + uint32_t oobsel; + uint32_t rsrcs; + + if (bhnd_get_device(d11core) != BHND_COREID_D11) + panic("bhnd_pmu_radio_enable() called on non-D11 core"); + + switch (sc->cid.chip_id) { + case BHND_CHIPID_BCM4325: + if (sc->board.board_flags & BHND_BFL_FASTPWR) + break; + + if ((sc->board.board_flags & BHND_BFL_BUCKBOOST) == 0) + break; + + rsrcs = PMURES_BIT(RES4325_BUCK_BOOST_BURST); + + if (enable) { + BHND_PMU_OR_4(sc, BHND_PMU_MIN_RES_MASK, rsrcs); + DELAY(100 * 1000); /* 100ms */ + } else { + BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~rsrcs); + } + + break; + case BHND_CHIPID_BCM4319: + oobsel = bhnd_read_config(d11core, BCMA_DMP_OOBSELOUTB74, 4); + + if (enable) { + oobsel |= BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN, + BCMA_DMP_OOBSEL_1); + oobsel |= BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN, + BCMA_DMP_OOBSEL_2); + } else { + oobsel &= ~BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN, + BCMA_DMP_OOBSEL_1); + oobsel &= ~BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN, + BCMA_DMP_OOBSEL_2); + } + + bhnd_write_config(d11core, BCMA_DMP_OOBSELOUTB74, oobsel, 4); + break; + } +} + +/* Wait for a particular clock level to be on the backplane */ +uint32_t +bhnd_pmu_waitforclk_on_backplane(struct bhnd_pmu_softc *sc, uint32_t clk, + uint32_t delay) +{ + uint32_t pmu_st; + + for (uint32_t i = 0; i < delay; i += 10) { + pmu_st = BHND_PMU_READ_4(sc, BHND_PMU_ST); + if ((pmu_st & clk) == clk) + return (clk); + + DELAY(10); + } + + pmu_st = BHND_PMU_READ_4(sc, BHND_PMU_ST); + return (pmu_st & clk); +} + +/* + * Measures the ALP clock frequency in KHz. Returns 0 if not possible. + * Possible only if PMU rev >= 10 and there is an external LPO 32768Hz crystal. + */ + +#define EXT_ILP_HZ 32768 + +uint32_t +bhnd_pmu_measure_alpclk(struct bhnd_pmu_softc *sc) +{ + uint32_t alp_khz; + uint32_t pmu_st; + + if (BHND_PMU_REV(sc) < 10) + return (0); + + pmu_st = BHND_PMU_READ_4(sc, BHND_PMU_ST); + if (pmu_st & BHND_PMU_ST_EXTLPOAVAIL) { + uint32_t alp_hz, ilp_ctr; + + /* Enable frequency measurement */ + BHND_PMU_WRITE_4(sc, BHND_PMU_XTALFREQ, 1U << + BHND_PMU_XTALFREQ_REG_MEASURE_SHIFT); + + /* Delay for well over 4 ILP clocks */ + DELAY(1000); + + /* Read the latched number of ALP ticks per 4 ILP ticks */ + ilp_ctr = BHND_PMU_READ_4(sc, BHND_PMU_XTALFREQ); + ilp_ctr = BHND_PMU_GET_BITS(ilp_ctr, + BHND_PMU_XTALFREQ_REG_ILPCTR); + + /* Turn off PMU_XTALFREQ_REG_MEASURE to save power */ + BHND_PMU_WRITE_4(sc, BHND_PMU_XTALFREQ, 0); + + /* Calculate ALP frequency */ + alp_hz = (ilp_ctr * EXT_ILP_HZ) / 4; + + /* Round to nearest 100KHz and convert to KHz */ + alp_khz = (alp_hz + 50000) / 100000 * 100; + } else { + alp_khz = 0; + } + + return (alp_khz); +} + +static void +bhnd_pmu_set_4330_plldivs(struct bhnd_pmu_softc *sc) +{ + uint32_t FVCO = bhnd_pmu1_pllfvco0(sc) / 1000; + uint32_t m1div, m2div, m3div, m4div, m5div, m6div; + uint32_t pllc1, pllc2; + + m2div = m3div = m4div = m6div = FVCO / 80; + m5div = FVCO / 160; + + if (PMU_CST4330_SDIOD_CHIPMODE(sc)) + m1div = FVCO / 80; + else + m1div = FVCO / 90; + + pllc1 = 0; + pllc1 |= BHND_PMU_SET_BITS(m1div, BHND_PMU1_PLL0_PC1_M1DIV); + pllc1 |= BHND_PMU_SET_BITS(m2div, BHND_PMU1_PLL0_PC1_M2DIV); + pllc1 |= BHND_PMU_SET_BITS(m3div, BHND_PMU1_PLL0_PC1_M3DIV); + pllc1 |= BHND_PMU_SET_BITS(m4div, BHND_PMU1_PLL0_PC1_M4DIV); + + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, pllc1, ~0); + + pllc2 = 0; + pllc2 |= BHND_PMU_SET_BITS(m5div, BHND_PMU1_PLL0_PC2_M5DIV); + pllc2 |= BHND_PMU_SET_BITS(m6div, BHND_PMU1_PLL0_PC2_M6DIV); + + BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, pllc2, + BHND_PMU1_PLL0_PC2_M5DIV_MASK | BHND_PMU1_PLL0_PC2_M6DIV_MASK); +} Index: sys/dev/bhnd/cores/pmu/bhnd_pmureg.h =================================================================== --- /dev/null +++ sys/dev/bhnd/cores/pmu/bhnd_pmureg.h @@ -0,0 +1,712 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2010 Broadcom Corporation + * All rights reserved. + * + * This file is 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$ + */ + +#ifndef _BHND_CORES_PMU_BHND_PMUREG_H_ +#define _BHND_CORES_PMU_BHND_PMUREG_H_ + +#define BHND_PMU_GET_FLAG(_value, _flag) \ + (((_value) & _flag) != 0) +#define BHND_PMU_GET_BITS(_value, _field) \ + (((_value) & _field ## _MASK) >> _field ## _SHIFT) +#define BHND_PMU_SET_BITS(_value, _field) \ + (((_value) & _field ## _MASK) >> _field ## _SHIFT) + +#define BHND_PMU_ILP_CLOCK 32000 /**< default ILP freq */ +#define BHND_PMU_ALP_CLOCK 20000000 /**< default ALP freq */ +#define BHND_PMU_HT_CLOCK 80000000 /**< default HT freq */ + +/** + * Common per-core clock control/status register available on PMU-equipped + * devices. + */ +#define BHND_CLK_CTL_ST 0x1e0 /**< clock control and status */ + +/* + * BHND_CLK_CTL_ST register + * + * 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_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 */ + +/* PMU registers */ +#define BHND_PMU_CTRL 0x600 +#define BHND_PMU_CTRL_ILP_DIV_MASK 0xffff0000 +#define BHND_PMU_CTRL_ILP_DIV_SHIFT 16 +#define BHND_PMU_CTRL_PLL_PLLCTL_UPD 0x00000400 /* rev 2 */ +#define BHND_PMU_CTRL_NOILP_ON_WAIT 0x00000200 /* rev 1 */ +#define BHND_PMU_CTRL_HT_REQ_EN 0x00000100 +#define BHND_PMU_CTRL_ALP_REQ_EN 0x00000080 +#define BHND_PMU_CTRL_XTALFREQ_MASK 0x0000007c +#define BHND_PMU_CTRL_XTALFREQ_SHIFT 2 +#define BHND_PMU_CTRL_ILP_DIV_EN 0x00000002 +#define BHND_PMU_CTRL_LPO_SEL 0x00000001 +#define BHND_PMU_CAP 0x604 +#define BHND_PMU_CAP_REV_MASK 0x000000ff +#define BHND_PMU_CAP_REV_SHIFT 0 +#define BHND_PMU_CAP_RC_MASK 0x00001f00 +#define BHND_PMU_CAP_RC_SHIFT 8 +#define BHND_PMU_CAP_RC_MAX \ + (BHND_PMU_CAP_RC_MASK >> BHND_PMU_CAP_RC_SHIFT) +#define BHND_PMU_CAP_TC_MASK 0x0001e000 +#define BHND_PMU_CAP_TC_SHIFT 13 +#define BHND_PMU_CAP_PC_MASK 0x001e0000 +#define BHND_PMU_CAP_PC_SHIFT 17 +#define BHND_PMU_CAP_VC_MASK 0x01e00000 +#define BHND_PMU_CAP_VC_SHIFT 21 +#define BHND_PMU_CAP_CC_MASK 0x1e000000 +#define BHND_PMU_CAP_CC_SHIFT 25 +#define BHND_PMU_CAP5_PC_MASK 0x003e0000 /* PMU corerev >= 5 */ +#define BHND_PMU_CAP5_PC_SHIFT 17 +#define BHND_PMU_CAP5_VC_MASK 0x07c00000 +#define BHND_PMU_CAP5_VC_SHIFT 22 +#define BHND_PMU_CAP5_CC_MASK 0xf8000000 +#define BHND_PMU_CAP5_CC_SHIFT 27 +#define BHND_PMU_ST 0x608 +#define BHND_PMU_ST_EXTLPOAVAIL 0x0100 +#define BHND_PMU_ST_WDRESET 0x0080 +#define BHND_PMU_ST_INTPEND 0x0040 +#define BHND_PMU_ST_SBCLKST 0x0030 +#define BHND_PMU_ST_SBCLKST_ILP 0x0010 +#define BHND_PMU_ST_SBCLKST_ALP 0x0020 +#define BHND_PMU_ST_SBCLKST_HT 0x0030 +#define BHND_PMU_ST_ALPAVAIL 0x0008 +#define BHND_PMU_ST_HTAVAIL 0x0004 +#define BHND_PMU_ST_RESINIT 0x0003 +#define BHND_PMU_RES_STATE 0x60c +#define BHND_PMU_RES_PENDING 0x610 +#define BHND_PMU_TIMER 0x614 +#define BHND_PMU_MIN_RES_MASK 0x618 +#define BHND_PMU_MAX_RES_MASK 0x61c +#define BHND_PMU_RES_TABLE_SEL 0x620 +#define BHND_PMU_RES_DEP_MASK 0x624 +#define BHND_PMU_RES_UPDN_TIMER 0x628 +#define BHND_PMU_RES_UPDN_UPTME_MASK 0xFF +#define BHND_PMU_RES_UPDN_UPTME_SHIFT 8 +#define BHND_PMU_RES_TIMER 0x62C +#define BHND_PMU_CLKSTRETCH 0x630 +#define BHND_PMU_CSTRETCH_HT 0xffff0000 +#define BHND_PMU_CSTRETCH_ALP 0x0000ffff +#define BHND_PMU_WATCHDOG 0x634 +#define BHND_PMU_GPIOSEL 0x638 /* pmu rev >= 1 ? */ +#define BHND_PMU_GPIOEN 0x63C /* pmu rev >= 1 ? */ +#define BHND_PMU_RES_REQ_TIMER_SEL 0x640 +#define BHND_PMU_RES_REQ_TIMER 0x644 +#define BHND_PMU_RRQT_TIME_MASK 0x03ff +#define BHND_PMU_RRQT_INTEN 0x0400 +#define BHND_PMU_RRQT_REQ_ACTIVE 0x0800 +#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_REG_CONTROL_ADDR 0x658 +#define BHND_PMU_REG_CONTROL_DATA 0x65C +#define BHND_PMU_PLL_CONTROL_ADDR 0x660 +#define BHND_PMU_PLL_CONTROL_DATA 0x664 +#define BHND_PMU_STRAPOPT 0x668 /* chipc rev >= 28 */ +#define BHND_PMU_XTALFREQ 0x66C /* pmu rev >= 10 */ + +/* PMU resource bit position */ +#define BHND_PMURES_BIT(bit) (1 << (bit)) + +/* PMU resource number limit */ +#define BHND_PMU_RESNUM_MAX 30 + +/* PMU chip control0 register */ +#define BHND_PMU_CHIPCTL0 0 + +/* PMU chip control1 register */ +#define BHND_PMU_CHIPCTL1 1 +#define BHND_PMU_CC1_RXC_DLL_BYPASS 0x00010000 + +#define BHND_PMU_CC1_IF_TYPE_MASK 0x00000030 +#define BHND_PMU_CC1_IF_TYPE_RMII 0x00000000 +#define BHND_PMU_CC1_IF_TYPE_MII 0x00000010 +#define BHND_PMU_CC1_IF_TYPE_RGMII 0x00000020 + +#define BHND_PMU_CC1_SW_TYPE_MASK 0x000000c0 +#define BHND_PMU_CC1_SW_TYPE_EPHY 0x00000000 +#define BHND_PMU_CC1_SW_TYPE_EPHYMII 0x00000040 +#define BHND_PMU_CC1_SW_TYPE_EPHYRMII 0x00000080 +#define BHND_PMU_CC1_SW_TYPE_RGMII 0x000000c0 + +/* PMU corerev and chip specific PLL controls. + * PMU_PLL_XX where is PMU corerev and is an arbitrary number + * to differentiate different PLLs controlled by the same PMU rev. + */ + +/* pllcontrol registers */ +/* PDIV, div_phy, div_arm, div_adc, dith_sel, ioff, kpd_scale, lsb_sel, mash_sel, lf_c & lf_r */ +#define BHND_PMU0_PLL0_PLLCTL0 0 +#define BHND_PMU0_PLL0_PC0_PDIV_MASK 1 +#define BHND_PMU0_PLL0_PC0_PDIV_FREQ 25000 +#define BHND_PMU0_PLL0_PC0_DIV_ARM_MASK 0x00000038 +#define BHND_PMU0_PLL0_PC0_DIV_ARM_SHIFT 3 +#define BHND_PMU0_PLL0_PC0_DIV_ARM_BASE 8 + +/* PC0_DIV_ARM for PLLOUT_ARM */ +#define BHND_PMU0_PLL0_PC0_DIV_ARM_110MHZ 0 +#define BHND_PMU0_PLL0_PC0_DIV_ARM_97_7MHZ 1 +#define BHND_PMU0_PLL0_PC0_DIV_ARM_88MHZ 2 +#define BHND_PMU0_PLL0_PC0_DIV_ARM_80MHZ 3 /* Default */ +#define BHND_PMU0_PLL0_PC0_DIV_ARM_73_3MHZ 4 +#define BHND_PMU0_PLL0_PC0_DIV_ARM_67_7MHZ 5 +#define BHND_PMU0_PLL0_PC0_DIV_ARM_62_9MHZ 6 +#define BHND_PMU0_PLL0_PC0_DIV_ARM_58_6MHZ 7 + +/* Wildcard base, stop_mod, en_lf_tp, en_cal & lf_r2 */ +#define BHND_PMU0_PLL0_PLLCTL1 1 +#define BHND_PMU0_PLL0_PC1_WILD_INT_MASK 0xf0000000 +#define BHND_PMU0_PLL0_PC1_WILD_INT_SHIFT 28 +#define BHND_PMU0_PLL0_PC1_WILD_FRAC_MASK 0x0fffff00 +#define BHND_PMU0_PLL0_PC1_WILD_FRAC_SHIFT 8 +#define BHND_PMU0_PLL0_PC1_STOP_MOD 0x00000040 + +/* Wildcard base, vco_calvar, vco_swc, vco_var_selref, vso_ical & vco_sel_avdd */ +#define BHND_PMU0_PLL0_PLLCTL2 2 +#define BHND_PMU0_PLL0_PC2_WILD_INT_MASK 0xf +#define BHND_PMU0_PLL0_PC2_WILD_INT_SHIFT 4 + +/* pllcontrol registers */ +/* ndiv_pwrdn, pwrdn_ch, refcomp_pwrdn, dly_ch, p1div, p2div, _bypass_sdmod */ +#define BHND_PMU1_PLL0_PLLCTL0 0 +#define BHND_PMU1_PLL0_PC0_P1DIV_MASK 0x00f00000 +#define BHND_PMU1_PLL0_PC0_P1DIV_SHIFT 20 +#define BHND_PMU1_PLL0_PC0_P2DIV_MASK 0x0f000000 +#define BHND_PMU1_PLL0_PC0_P2DIV_SHIFT 24 +#define BHND_PMU1_PLL0_PC0_BYPASS_SDMOD_MASK 0x10000000 +#define BHND_PMU1_PLL0_PC0_BYPASS_SDMOD_SHIFT 28 + +/* mdiv */ +#define BHND_PMU1_PLL0_PLLCTL1 1 +#define BHND_PMU1_PLL0_PC1_M1DIV_MASK 0x000000ff +#define BHND_PMU1_PLL0_PC1_M1DIV_SHIFT 0 +#define BHND_PMU1_PLL0_PC1_M2DIV_MASK 0x0000ff00 +#define BHND_PMU1_PLL0_PC1_M2DIV_SHIFT 8 +#define BHND_PMU1_PLL0_PC1_M3DIV_MASK 0x00ff0000 +#define BHND_PMU1_PLL0_PC1_M3DIV_SHIFT 16 +#define BHND_PMU1_PLL0_PC1_M4DIV_MASK 0xff000000 +#define BHND_PMU1_PLL0_PC1_M4DIV_SHIFT 24 + +#define BHND_PMU_DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT 8 +#define BHND_PMU_DOT11MAC_880MHZ_CLK_DIVISOR_MASK (0xFF << BHND_PMU_DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT) +#define BHND_PMU_DOT11MAC_880MHZ_CLK_DIVISOR_VAL (0xE << BHND_PMU_DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT) + +/* mdiv, ndiv_dither_mfb, ndiv_mode, ndiv_int */ +#define BHND_PMU1_PLL0_PLLCTL2 2 +#define BHND_PMU1_PLL0_PC2_M5DIV_MASK 0x000000ff +#define BHND_PMU1_PLL0_PC2_M5DIV_SHIFT 0 +#define BHND_PMU1_PLL0_PC2_M6DIV_MASK 0x0000ff00 +#define BHND_PMU1_PLL0_PC2_M6DIV_SHIFT 8 +#define BHND_PMU1_PLL0_PC2_NDIV_MODE_MASK 0x000e0000 +#define BHND_PMU1_PLL0_PC2_NDIV_MODE_SHIFT 17 +#define BHND_PMU1_PLL0_PC2_NDIV_MODE_INT 0 +#define BHND_PMU1_PLL0_PC2_NDIV_MODE_MASH 1 +#define BHND_PMU1_PLL0_PC2_NDIV_MODE_MFB 2 /* recommended for 4319 */ +#define BHND_PMU1_PLL0_PC2_NDIV_INT_MASK 0x1ff00000 +#define BHND_PMU1_PLL0_PC2_NDIV_INT_SHIFT 20 + +/* ndiv_frac */ +#define BHND_PMU1_PLL0_PLLCTL3 3 +#define BHND_PMU1_PLL0_PC3_NDIV_FRAC_MASK 0x00ffffff +#define BHND_PMU1_PLL0_PC3_NDIV_FRAC_SHIFT 0 + +/* pll_ctrl */ +#define BHND_PMU1_PLL0_PLLCTL4 4 +#define BHND_PMU1_PLL0_PC4_KVCO_XS_MASK 0x38000000 +#define BHND_PMU1_PLL0_PC4_KVCO_XS_SHIFT 27 + +/* pll_ctrl, vco_rng, clkdrive_ch */ +#define BHND_PMU1_PLL0_PLLCTL5 5 +#define BHND_PMU1_PLL0_PC5_CLK_DRV_MASK 0xffffff00 +#define BHND_PMU1_PLL0_PC5_CLK_DRV_SHIFT 8 +#define BHND_PMU1_PLL0_PC5_PLL_CTRL_37_32_MASK 0x0000003f +#define BHND_PMU1_PLL0_PC5_PLL_CTRL_37_32_SHIFT 0 +#define BHND_PMU1_PLL0_PC5_VCO_RNG_MASK 0x000000C0 +#define BHND_PMU1_PLL0_PC5_VCO_RNG_SHIFT 6 + +/* PMU rev 2 control words */ +#define BHND_PMU2_PHY_PLL_PLLCTL 4 +#define BHND_PMU2_SI_PLL_PLLCTL 10 + +/* PMU rev 2 */ +/* pllcontrol registers */ +/* ndiv_pwrdn, pwrdn_ch, refcomp_pwrdn, dly_ch, p1div, p2div, _bypass_sdmod */ +#define BHND_PMU2_PLL_PLLCTL0 0 +#define BHND_PMU2_PLL_PC0_P1DIV_MASK 0x00f00000 +#define BHND_PMU2_PLL_PC0_P1DIV_SHIFT 20 +#define BHND_PMU2_PLL_PC0_P2DIV_MASK 0x0f000000 +#define BHND_PMU2_PLL_PC0_P2DIV_SHIFT 24 + +/* mdiv */ +#define BHND_PMU2_PLL_PLLCTL1 1 +#define BHND_PMU2_PLL_PC1_M1DIV_MASK 0x000000ff +#define BHND_PMU2_PLL_PC1_M1DIV_SHIFT 0 +#define BHND_PMU2_PLL_PC1_M2DIV_MASK 0x0000ff00 +#define BHND_PMU2_PLL_PC1_M2DIV_SHIFT 8 +#define BHND_PMU2_PLL_PC1_M3DIV_MASK 0x00ff0000 +#define BHND_PMU2_PLL_PC1_M3DIV_SHIFT 16 +#define BHND_PMU2_PLL_PC1_M4DIV_MASK 0xff000000 +#define BHND_PMU2_PLL_PC1_M4DIV_SHIFT 24 + +/* mdiv, ndiv_dither_mfb, ndiv_mode, ndiv_int */ +#define BHND_PMU2_PLL_PLLCTL2 2 +#define BHND_PMU2_PLL_PC2_M5DIV_MASK 0x000000ff +#define BHND_PMU2_PLL_PC2_M5DIV_SHIFT 0 +#define BHND_PMU2_PLL_PC2_M6DIV_MASK 0x0000ff00 +#define BHND_PMU2_PLL_PC2_M6DIV_SHIFT 8 +#define BHND_PMU2_PLL_PC2_NDIV_MODE_MASK 0x000e0000 +#define BHND_PMU2_PLL_PC2_NDIV_MODE_SHIFT 17 +#define BHND_PMU2_PLL_PC2_NDIV_INT_MASK 0x1ff00000 +#define BHND_PMU2_PLL_PC2_NDIV_INT_SHIFT 20 + +/* ndiv_frac */ +#define BHND_PMU2_PLL_PLLCTL3 3 +#define BHND_PMU2_PLL_PC3_NDIV_FRAC_MASK 0x00ffffff +#define BHND_PMU2_PLL_PC3_NDIV_FRAC_SHIFT 0 + +/* pll_ctrl */ +#define BHND_PMU2_PLL_PLLCTL4 4 + +/* pll_ctrl, vco_rng, clkdrive_ch */ +#define BHND_PMU2_PLL_PLLCTL5 5 +#define BHND_PMU2_PLL_PC5_CLKDRIVE_CH1_MASK 0x00000f00 +#define BHND_PMU2_PLL_PC5_CLKDRIVE_CH1_SHIFT 8 +#define BHND_PMU2_PLL_PC5_CLKDRIVE_CH2_MASK 0x0000f000 +#define BHND_PMU2_PLL_PC5_CLKDRIVE_CH2_SHIFT 12 +#define BHND_PMU2_PLL_PC5_CLKDRIVE_CH3_MASK 0x000f0000 +#define BHND_PMU2_PLL_PC5_CLKDRIVE_CH3_SHIFT 16 +#define BHND_PMU2_PLL_PC5_CLKDRIVE_CH4_MASK 0x00f00000 +#define BHND_PMU2_PLL_PC5_CLKDRIVE_CH4_SHIFT 20 +#define BHND_PMU2_PLL_PC5_CLKDRIVE_CH5_MASK 0x0f000000 +#define BHND_PMU2_PLL_PC5_CLKDRIVE_CH5_SHIFT 24 +#define BHND_PMU2_PLL_PC5_CLKDRIVE_CH6_MASK 0xf0000000 +#define BHND_PMU2_PLL_PC5_CLKDRIVE_CH6_SHIFT 28 + +/* PMU rev 5 (& 6) */ +#define BHND_PMU5_PLL_P1P2_OFF 0 +#define BHND_PMU5_PLL_P1_MASK 0x0f000000 +#define BHND_PMU5_PLL_P1_SHIFT 24 +#define BHND_PMU5_PLL_P2_MASK 0x00f00000 +#define BHND_PMU5_PLL_P2_SHIFT 20 +#define BHND_PMU5_PLL_M14_OFF 1 +#define BHND_PMU5_PLL_MDIV_MASK 0x000000ff +#define BHND_PMU5_PLL_MDIV_WIDTH 8 +#define BHND_PMU5_PLL_NM5_OFF 2 +#define BHND_PMU5_PLL_NDIV_MASK 0xfff00000 +#define BHND_PMU5_PLL_NDIV_SHIFT 20 +#define BHND_PMU5_PLL_NDIV_MODE_MASK 0x000e0000 +#define BHND_PMU5_PLL_NDIV_MODE_SHIFT 17 +#define BHND_PMU5_PLL_FMAB_OFF 3 +#define BHND_PMU5_PLL_MRAT_MASK 0xf0000000 +#define BHND_PMU5_PLL_MRAT_SHIFT 28 +#define BHND_PMU5_PLL_ABRAT_MASK 0x08000000 +#define BHND_PMU5_PLL_ABRAT_SHIFT 27 +#define BHND_PMU5_PLL_FDIV_MASK 0x07ffffff +#define BHND_PMU5_PLL_PLLCTL_OFF 4 +#define BHND_PMU5_PLL_PCHI_OFF 5 +#define BHND_PMU5_PLL_PCHI_MASK 0x0000003f + +/* pmu XtalFreqRatio */ +#define BHND_PMU_XTALFREQ_REG_ILPCTR_MASK 0x00001FFF +#define BHND_PMU_XTALFREQ_REG_ILPCTR_SHIFT 0 +#define BHND_PMU_XTALFREQ_REG_MEASURE_MASK 0x80000000 +#define BHND_PMU_XTALFREQ_REG_MEASURE_SHIFT 31 + + +/* Divider allocation in 4716/47162/5356/5357 */ +#define BHND_PMU5_MAINPLL_CPU 1 +#define BHND_PMU5_MAINPLL_MEM 2 +#define BHND_PMU5_MAINPLL_SI 3 + +#define BHND_PMU7_PLL_PLLCTL7 7 +#define BHND_PMU7_PLL_PLLCTL8 8 +#define BHND_PMU7_PLL_PLLCTL11 11 + + +/* PLL usage in 4716/47162 */ +#define BHND_PMU4716_MAINPLL_PLL0 12 + + +/* PLL usage in 5356/5357 */ +#define BHND_PMU5356_MAINPLL_PLL0 0 +#define BHND_PMU5357_MAINPLL_PLL0 0 + + +/* 4716/47162 PMU resources */ +#define BHND_PMU_RES4716_PROC_PLL_ON 0x00000040 +#define BHND_PMU_RES4716_PROC_HT_AVAIL 0x00000080 + +/* 4716/4717/4718 chip-specific CHIPCTRL PMU register bits */ +#define BHND_PMU_CCTRL471X_I2S_PINS_ENABLE 0x0080 /* I2S pins off by default, shared with pflash */ + + +/* 5354 PMU resources */ +#define BHND_PMU_RES5354_EXT_SWITCHER_PWM 0 /* 0x00001 */ +#define BHND_PMU_RES5354_BB_SWITCHER_PWM 1 /* 0x00002 */ +#define BHND_PMU_RES5354_BB_SWITCHER_BURST 2 /* 0x00004 */ +#define BHND_PMU_RES5354_BB_EXT_SWITCHER_BURST 3 /* 0x00008 */ +#define BHND_PMU_RES5354_ILP_REQUEST 4 /* 0x00010 */ +#define BHND_PMU_RES5354_RADIO_SWITCHER_PWM 5 /* 0x00020 */ +#define BHND_PMU_RES5354_RADIO_SWITCHER_BURST 6 /* 0x00040 */ +#define BHND_PMU_RES5354_ROM_SWITCH 7 /* 0x00080 */ +#define BHND_PMU_RES5354_PA_REF_LDO 8 /* 0x00100 */ +#define BHND_PMU_RES5354_RADIO_LDO 9 /* 0x00200 */ +#define BHND_PMU_RES5354_AFE_LDO 10 /* 0x00400 */ +#define BHND_PMU_RES5354_PLL_LDO 11 /* 0x00800 */ +#define BHND_PMU_RES5354_BG_FILTBYP 12 /* 0x01000 */ +#define BHND_PMU_RES5354_TX_FILTBYP 13 /* 0x02000 */ +#define BHND_PMU_RES5354_RX_FILTBYP 14 /* 0x04000 */ +#define BHND_PMU_RES5354_XTAL_PU 15 /* 0x08000 */ +#define BHND_PMU_RES5354_XTAL_EN 16 /* 0x10000 */ +#define BHND_PMU_RES5354_BB_PLL_FILTBYP 17 /* 0x20000 */ +#define BHND_PMU_RES5354_RF_PLL_FILTBYP 18 /* 0x40000 */ +#define BHND_PMU_RES5354_BB_PLL_PU 19 /* 0x80000 */ + + +/* 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 */ + + +/* 4328 PMU resources */ +#define BHND_PMU_RES4328_EXT_SWITCHER_PWM 0 /* 0x00001 */ +#define BHND_PMU_RES4328_BB_SWITCHER_PWM 1 /* 0x00002 */ +#define BHND_PMU_RES4328_BB_SWITCHER_BURST 2 /* 0x00004 */ +#define BHND_PMU_RES4328_BB_EXT_SWITCHER_BURST 3 /* 0x00008 */ +#define BHND_PMU_RES4328_ILP_REQUEST 4 /* 0x00010 */ +#define BHND_PMU_RES4328_RADIO_SWITCHER_PWM 5 /* 0x00020 */ +#define BHND_PMU_RES4328_RADIO_SWITCHER_BURST 6 /* 0x00040 */ +#define BHND_PMU_RES4328_ROM_SWITCH 7 /* 0x00080 */ +#define BHND_PMU_RES4328_PA_REF_LDO 8 /* 0x00100 */ +#define BHND_PMU_RES4328_RADIO_LDO 9 /* 0x00200 */ +#define BHND_PMU_RES4328_AFE_LDO 10 /* 0x00400 */ +#define BHND_PMU_RES4328_PLL_LDO 11 /* 0x00800 */ +#define BHND_PMU_RES4328_BG_FILTBYP 12 /* 0x01000 */ +#define BHND_PMU_RES4328_TX_FILTBYP 13 /* 0x02000 */ +#define BHND_PMU_RES4328_RX_FILTBYP 14 /* 0x04000 */ +#define BHND_PMU_RES4328_XTAL_PU 15 /* 0x08000 */ +#define BHND_PMU_RES4328_XTAL_EN 16 /* 0x10000 */ +#define BHND_PMU_RES4328_BB_PLL_FILTBYP 17 /* 0x20000 */ +#define BHND_PMU_RES4328_RF_PLL_FILTBYP 18 /* 0x40000 */ +#define BHND_PMU_RES4328_BB_PLL_PU 19 /* 0x80000 */ + + +/* 4325 A0/A1 PMU resources */ +#define BHND_PMU_RES4325_BUCK_BOOST_BURST 0 /* 0x00000001 */ +#define BHND_PMU_RES4325_CBUCK_BURST 1 /* 0x00000002 */ +#define BHND_PMU_RES4325_CBUCK_PWM 2 /* 0x00000004 */ +#define BHND_PMU_RES4325_CLDO_CBUCK_BURST 3 /* 0x00000008 */ +#define BHND_PMU_RES4325_CLDO_CBUCK_PWM 4 /* 0x00000010 */ +#define BHND_PMU_RES4325_BUCK_BOOST_PWM 5 /* 0x00000020 */ +#define BHND_PMU_RES4325_ILP_REQUEST 6 /* 0x00000040 */ +#define BHND_PMU_RES4325_ABUCK_BURST 7 /* 0x00000080 */ +#define BHND_PMU_RES4325_ABUCK_PWM 8 /* 0x00000100 */ +#define BHND_PMU_RES4325_LNLDO1_PU 9 /* 0x00000200 */ +#define BHND_PMU_RES4325_OTP_PU 10 /* 0x00000400 */ +#define BHND_PMU_RES4325_LNLDO3_PU 11 /* 0x00000800 */ +#define BHND_PMU_RES4325_LNLDO4_PU 12 /* 0x00001000 */ +#define BHND_PMU_RES4325_XTAL_PU 13 /* 0x00002000 */ +#define BHND_PMU_RES4325_ALP_AVAIL 14 /* 0x00004000 */ +#define BHND_PMU_RES4325_RX_PWRSW_PU 15 /* 0x00008000 */ +#define BHND_PMU_RES4325_TX_PWRSW_PU 16 /* 0x00010000 */ +#define BHND_PMU_RES4325_RFPLL_PWRSW_PU 17 /* 0x00020000 */ +#define BHND_PMU_RES4325_LOGEN_PWRSW_PU 18 /* 0x00040000 */ +#define BHND_PMU_RES4325_AFE_PWRSW_PU 19 /* 0x00080000 */ +#define BHND_PMU_RES4325_BBPLL_PWRSW_PU 20 /* 0x00100000 */ +#define BHND_PMU_RES4325_HT_AVAIL 21 /* 0x00200000 */ + + +/* 4325 B0/C0 PMU resources */ +#define BHND_PMU_RES4325B0_CBUCK_LPOM 1 /* 0x00000002 */ +#define BHND_PMU_RES4325B0_CBUCK_BURST 2 /* 0x00000004 */ +#define BHND_PMU_RES4325B0_CBUCK_PWM 3 /* 0x00000008 */ +#define BHND_PMU_RES4325B0_CLDO_PU 4 /* 0x00000010 */ + + +/* 4325 C1 PMU resources */ +#define BHND_PMU_RES4325C1_LNLDO2_PU 12 /* 0x00001000 */ + + +/* 4325 PMU resources */ +#define BHND_PMU_RES4329_RESERVED0 0 /* 0x00000001 */ +#define BHND_PMU_RES4329_CBUCK_LPOM 1 /* 0x00000002 */ +#define BHND_PMU_RES4329_CBUCK_BURST 2 /* 0x00000004 */ +#define BHND_PMU_RES4329_CBUCK_PWM 3 /* 0x00000008 */ +#define BHND_PMU_RES4329_CLDO_PU 4 /* 0x00000010 */ +#define BHND_PMU_RES4329_PALDO_PU 5 /* 0x00000020 */ +#define BHND_PMU_RES4329_ILP_REQUEST 6 /* 0x00000040 */ +#define BHND_PMU_RES4329_RESERVED7 7 /* 0x00000080 */ +#define BHND_PMU_RES4329_RESERVED8 8 /* 0x00000100 */ +#define BHND_PMU_RES4329_LNLDO1_PU 9 /* 0x00000200 */ +#define BHND_PMU_RES4329_OTP_PU 10 /* 0x00000400 */ +#define BHND_PMU_RES4329_RESERVED11 11 /* 0x00000800 */ +#define BHND_PMU_RES4329_LNLDO2_PU 12 /* 0x00001000 */ +#define BHND_PMU_RES4329_XTAL_PU 13 /* 0x00002000 */ +#define BHND_PMU_RES4329_ALP_AVAIL 14 /* 0x00004000 */ +#define BHND_PMU_RES4329_RX_PWRSW_PU 15 /* 0x00008000 */ +#define BHND_PMU_RES4329_TX_PWRSW_PU 16 /* 0x00010000 */ +#define BHND_PMU_RES4329_RFPLL_PWRSW_PU 17 /* 0x00020000 */ +#define BHND_PMU_RES4329_LOGEN_PWRSW_PU 18 /* 0x00040000 */ +#define BHND_PMU_RES4329_AFE_PWRSW_PU 19 /* 0x00080000 */ +#define BHND_PMU_RES4329_BBPLL_PWRSW_PU 20 /* 0x00100000 */ +#define BHND_PMU_RES4329_HT_AVAIL 21 /* 0x00200000 */ + + +/* 4312 PMU resources (all PMU chips with little memory constraint) */ +#define BHND_PMU_RES4312_SWITCHER_BURST 0 /* 0x00000001 */ +#define BHND_PMU_RES4312_SWITCHER_PWM 1 /* 0x00000002 */ +#define BHND_PMU_RES4312_PA_REF_LDO 2 /* 0x00000004 */ +#define BHND_PMU_RES4312_CORE_LDO_BURST 3 /* 0x00000008 */ +#define BHND_PMU_RES4312_CORE_LDO_PWM 4 /* 0x00000010 */ +#define BHND_PMU_RES4312_RADIO_LDO 5 /* 0x00000020 */ +#define BHND_PMU_RES4312_ILP_REQUEST 6 /* 0x00000040 */ +#define BHND_PMU_RES4312_BG_FILTBYP 7 /* 0x00000080 */ +#define BHND_PMU_RES4312_TX_FILTBYP 8 /* 0x00000100 */ +#define BHND_PMU_RES4312_RX_FILTBYP 9 /* 0x00000200 */ +#define BHND_PMU_RES4312_XTAL_PU 10 /* 0x00000400 */ +#define BHND_PMU_RES4312_ALP_AVAIL 11 /* 0x00000800 */ +#define BHND_PMU_RES4312_BB_PLL_FILTBYP 12 /* 0x00001000 */ +#define BHND_PMU_RES4312_RF_PLL_FILTBYP 13 /* 0x00002000 */ +#define BHND_PMU_RES4312_HT_AVAIL 14 /* 0x00004000 */ + + +/* 4322 PMU resources */ +#define BHND_PMU_RES4322_RF_LDO 0 +#define BHND_PMU_RES4322_ILP_REQUEST 1 +#define BHND_PMU_RES4322_XTAL_PU 2 +#define BHND_PMU_RES4322_ALP_AVAIL 3 +#define BHND_PMU_RES4322_SI_PLL_ON 4 +#define BHND_PMU_RES4322_HT_SI_AVAIL 5 +#define BHND_PMU_RES4322_PHY_PLL_ON 6 +#define BHND_PMU_RES4322_HT_PHY_AVAIL 7 +#define BHND_PMU_RES4322_OTP_PU 8 + + +/* 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 */ + + +/* 43236 PMU resources */ +#define BHND_PMU_RES43236_REGULATOR 0 +#define BHND_PMU_RES43236_ILP_REQUEST 1 +#define BHND_PMU_RES43236_XTAL_PU 2 +#define BHND_PMU_RES43236_ALP_AVAIL 3 +#define BHND_PMU_RES43236_SI_PLL_ON 4 +#define BHND_PMU_RES43236_HT_SI_AVAIL 5 + +/* 43236 chip-specific CHIPCTRL register bits */ +#define BHND_PMU_CCTRL43236_BT_COEXIST (1<<0) /* 0 disable */ +#define BHND_PMU_CCTRL43236_SECI (1<<1) /* 0 SECI is disabled (JATG functional) */ +#define BHND_PMU_CCTRL43236_EXT_LNA (1<<2) /* 0 disable */ +#define BHND_PMU_CCTRL43236_ANT_MUX_2o3 (1<<3) /* 2o3 mux, chipcontrol bit 3 */ +#define BHND_PMU_CCTRL43236_GSIO (1<<4) /* 0 disable */ + + +/* 4331 PMU resources */ +#define BHND_PMU_RES4331_REGULATOR 0 +#define BHND_PMU_RES4331_ILP_REQUEST 1 +#define BHND_PMU_RES4331_XTAL_PU 2 +#define BHND_PMU_RES4331_ALP_AVAIL 3 +#define BHND_PMU_RES4331_SI_PLL_ON 4 +#define BHND_PMU_RES4331_HT_SI_AVAIL 5 + +/* 4315 PMU resources */ +#define BHND_PMU_RES4315_CBUCK_LPOM 1 /* 0x00000002 */ +#define BHND_PMU_RES4315_CBUCK_BURST 2 /* 0x00000004 */ +#define BHND_PMU_RES4315_CBUCK_PWM 3 /* 0x00000008 */ +#define BHND_PMU_RES4315_CLDO_PU 4 /* 0x00000010 */ +#define BHND_PMU_RES4315_PALDO_PU 5 /* 0x00000020 */ +#define BHND_PMU_RES4315_ILP_REQUEST 6 /* 0x00000040 */ +#define BHND_PMU_RES4315_LNLDO1_PU 9 /* 0x00000200 */ +#define BHND_PMU_RES4315_OTP_PU 10 /* 0x00000400 */ +#define BHND_PMU_RES4315_LNLDO2_PU 12 /* 0x00001000 */ +#define BHND_PMU_RES4315_XTAL_PU 13 /* 0x00002000 */ +#define BHND_PMU_RES4315_ALP_AVAIL 14 /* 0x00004000 */ +#define BHND_PMU_RES4315_RX_PWRSW_PU 15 /* 0x00008000 */ +#define BHND_PMU_RES4315_TX_PWRSW_PU 16 /* 0x00010000 */ +#define BHND_PMU_RES4315_RFPLL_PWRSW_PU 17 /* 0x00020000 */ +#define BHND_PMU_RES4315_LOGEN_PWRSW_PU 18 /* 0x00040000 */ +#define BHND_PMU_RES4315_AFE_PWRSW_PU 19 /* 0x00080000 */ +#define BHND_PMU_RES4315_BBPLL_PWRSW_PU 20 /* 0x00100000 */ +#define BHND_PMU_RES4315_HT_AVAIL 21 /* 0x00200000 */ + +/* 4319 PMU resources */ +#define BHND_PMU_RES4319_CBUCK_LPOM 1 /* 0x00000002 */ +#define BHND_PMU_RES4319_CBUCK_BURST 2 /* 0x00000004 */ +#define BHND_PMU_RES4319_CBUCK_PWM 3 /* 0x00000008 */ +#define BHND_PMU_RES4319_CLDO_PU 4 /* 0x00000010 */ +#define BHND_PMU_RES4319_PALDO_PU 5 /* 0x00000020 */ +#define BHND_PMU_RES4319_ILP_REQUEST 6 /* 0x00000040 */ +#define BHND_PMU_RES4319_LNLDO1_PU 9 /* 0x00000200 */ +#define BHND_PMU_RES4319_OTP_PU 10 /* 0x00000400 */ +#define BHND_PMU_RES4319_LNLDO2_PU 12 /* 0x00001000 */ +#define BHND_PMU_RES4319_XTAL_PU 13 /* 0x00002000 */ +#define BHND_PMU_RES4319_ALP_AVAIL 14 /* 0x00004000 */ +#define BHND_PMU_RES4319_RX_PWRSW_PU 15 /* 0x00008000 */ +#define BHND_PMU_RES4319_TX_PWRSW_PU 16 /* 0x00010000 */ +#define BHND_PMU_RES4319_RFPLL_PWRSW_PU 17 /* 0x00020000 */ +#define BHND_PMU_RES4319_LOGEN_PWRSW_PU 18 /* 0x00040000 */ +#define BHND_PMU_RES4319_AFE_PWRSW_PU 19 /* 0x00080000 */ +#define BHND_PMU_RES4319_BBPLL_PWRSW_PU 20 /* 0x00100000 */ +#define BHND_PMU_RES4319_HT_AVAIL 21 /* 0x00200000 */ + +/* 4319 chip-specific CHIPCTL register bits */ +#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 + +/* 4336 PMU resources */ +#define BHND_PMU_RES4336_CBUCK_LPOM 0 +#define BHND_PMU_RES4336_CBUCK_BURST 1 +#define BHND_PMU_RES4336_CBUCK_LP_PWM 2 +#define BHND_PMU_RES4336_CBUCK_PWM 3 +#define BHND_PMU_RES4336_CLDO_PU 4 +#define BHND_PMU_RES4336_DIS_INT_RESET_PD 5 +#define BHND_PMU_RES4336_ILP_REQUEST 6 +#define BHND_PMU_RES4336_LNLDO_PU 7 +#define BHND_PMU_RES4336_LDO3P3_PU 8 +#define BHND_PMU_RES4336_OTP_PU 9 +#define BHND_PMU_RES4336_XTAL_PU 10 +#define BHND_PMU_RES4336_ALP_AVAIL 11 +#define BHND_PMU_RES4336_RADIO_PU 12 +#define BHND_PMU_RES4336_BG_PU 13 +#define BHND_PMU_RES4336_VREG1p4_PU_PU 14 +#define BHND_PMU_RES4336_AFE_PWRSW_PU 15 +#define BHND_PMU_RES4336_RX_PWRSW_PU 16 +#define BHND_PMU_RES4336_TX_PWRSW_PU 17 +#define BHND_PMU_RES4336_BB_PWRSW_PU 18 +#define BHND_PMU_RES4336_SYNTH_PWRSW_PU 19 +#define BHND_PMU_RES4336_MISC_PWRSW_PU 20 +#define BHND_PMU_RES4336_LOGEN_PWRSW_PU 21 +#define BHND_PMU_RES4336_BBPLL_PWRSW_PU 22 +#define BHND_PMU_RES4336_MACPHY_CLKAVAIL 23 +#define BHND_PMU_RES4336_HT_AVAIL 24 +#define BHND_PMU_RES4336_RSVD 25 + +/* 4330 resources */ +#define BHND_PMU_RES4330_CBUCK_LPOM 0 +#define BHND_PMU_RES4330_CBUCK_BURST 1 +#define BHND_PMU_RES4330_CBUCK_LP_PWM 2 +#define BHND_PMU_RES4330_CBUCK_PWM 3 +#define BHND_PMU_RES4330_CLDO_PU 4 +#define BHND_PMU_RES4330_DIS_INT_RESET_PD 5 +#define BHND_PMU_RES4330_ILP_REQUEST 6 +#define BHND_PMU_RES4330_LNLDO_PU 7 +#define BHND_PMU_RES4330_LDO3P3_PU 8 +#define BHND_PMU_RES4330_OTP_PU 9 +#define BHND_PMU_RES4330_XTAL_PU 10 +#define BHND_PMU_RES4330_ALP_AVAIL 11 +#define BHND_PMU_RES4330_RADIO_PU 12 +#define BHND_PMU_RES4330_BG_PU 13 +#define BHND_PMU_RES4330_VREG1p4_PU_PU 14 +#define BHND_PMU_RES4330_AFE_PWRSW_PU 15 +#define BHND_PMU_RES4330_RX_PWRSW_PU 16 +#define BHND_PMU_RES4330_TX_PWRSW_PU 17 +#define BHND_PMU_RES4330_BB_PWRSW_PU 18 +#define BHND_PMU_RES4330_SYNTH_PWRSW_PU 19 +#define BHND_PMU_RES4330_MISC_PWRSW_PU 20 +#define BHND_PMU_RES4330_LOGEN_PWRSW_PU 21 +#define BHND_PMU_RES4330_BBPLL_PWRSW_PU 22 +#define BHND_PMU_RES4330_MACPHY_CLKAVAIL 23 +#define BHND_PMU_RES4330_HT_AVAIL 24 +#define BHND_PMU_RES4330_5gRX_PWRSW_PU 25 +#define BHND_PMU_RES4330_5gTX_PWRSW_PU 26 +#define BHND_PMU_RES4330_5g_LOGEN_PWRSW_PU 27 + +/* 4313 resources */ +#define BHND_PMU_RES4313_BB_PU_RSRC 0 +#define BHND_PMU_RES4313_ILP_REQ_RSRC 1 +#define BHND_PMU_RES4313_XTAL_PU_RSRC 2 +#define BHND_PMU_RES4313_ALP_AVAIL_RSRC 3 +#define BHND_PMU_RES4313_RADIO_PU_RSRC 4 +#define BHND_PMU_RES4313_BG_PU_RSRC 5 +#define BHND_PMU_RES4313_VREG1P4_PU_RSRC 6 +#define BHND_PMU_RES4313_AFE_PWRSW_RSRC 7 +#define BHND_PMU_RES4313_RX_PWRSW_RSRC 8 +#define BHND_PMU_RES4313_TX_PWRSW_RSRC 9 +#define BHND_PMU_RES4313_BB_PWRSW_RSRC 10 +#define BHND_PMU_RES4313_SYNTH_PWRSW_RSRC 11 +#define BHND_PMU_RES4313_MISC_PWRSW_RSRC 12 +#define BHND_PMU_RES4313_BB_PLL_PWRSW_RSRC 13 +#define BHND_PMU_RES4313_HT_AVAIL_RSRC 14 +#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 */ + +/* 43228 resources */ +#define BHND_PMU_RES43228_NOT_USED 0 +#define BHND_PMU_RES43228_ILP_REQUEST 1 +#define BHND_PMU_RES43228_XTAL_PU 2 +#define BHND_PMU_RES43228_ALP_AVAIL 3 +#define BHND_PMU_RES43228_PLL_EN 4 +#define BHND_PMU_RES43228_HT_PHY_AVAIL 5 + +/* +* Maximum delay for the PMU state transition in us. +* This is an upper bound intended for spinwaits etc. +*/ +#define BHND_PMU_MAX_TRANSITION_DLY 15000 + +/* PMU resource up transition time in ILP cycles */ +#define BHND_PMURES_UP_TRANSITION 2 + +#endif /* _BHND_CORES_PMU_BHND_PMUREG_H_ */ Index: sys/dev/bhnd/cores/pmu/bhnd_pmuvar.h =================================================================== --- /dev/null +++ sys/dev/bhnd/cores/pmu/bhnd_pmuvar.h @@ -0,0 +1,92 @@ +/*- + * Copyright (c) 2015 Landon Fuller + * 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. + * + * $FreeBSD$ + */ + +#ifndef _BHND_CORES_PMU_BHND_PMUVAR_H_ +#define _BHND_CORES_PMU_BHND_PMUVAR_H_ + +#include +#include + +#include "bhnd_pmu.h" + +DECLARE_CLASS(bhnd_pmu_driver); +extern devclass_t bhnd_pmu_devclass; + +int bhnd_pmu_probe(device_t dev); + +int bhnd_pmu_attach(device_t dev, struct bhnd_resource *res); +int bhnd_pmu_detach(device_t dev); +int bhnd_pmu_suspend(device_t dev); +int bhnd_pmu_resume(device_t dev); + +/* + * 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 +}; + +/** + * bhnd_pmu driver instance state. + */ +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 */ + struct bhnd_board_info board; /**< board identification */ + device_t chipc_dev; /**< chipcommon device */ + + struct bhnd_resource *res; /**< pmu register block. */ + int rid; /**< pmu register RID */ + + struct mtx mtx; /**< state mutex */ + + uint32_t ilp_cps; /**< measured ILP cycles per + second, or 0 */ +}; + +#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) +#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) + +#endif /* _BHND_CORES_PMU_BHND_PMUVAR_H_ */ Index: sys/dev/bhnd/nvram/nvram_map =================================================================== --- sys/dev/bhnd/nvram/nvram_map +++ sys/dev/bhnd/nvram/nvram_map @@ -84,6 +84,21 @@ srom 11 0x1D2 } + +# PMU Info +# + +# PMU min resource mask (embedded-only). +u32 rmin { + sfmt decimal +} + +# PMU min resource max (embedded-only). +u32 rmax { + sfmt decimal +} + + # Antennas available u8 aa2g { srom 1-3 0x5C (&0x30, >>4) Index: sys/dev/bhnd/siba/siba.c =================================================================== --- sys/dev/bhnd/siba/siba.c +++ sys/dev/bhnd/siba/siba.c @@ -260,6 +260,20 @@ return (ENXIO); } +static uint32_t +siba_read_config(device_t dev, device_t child, bus_size_t offset, u_int width) +{ + /* Unsuported */ + return (UINT32_MAX); +} + +static void +siba_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val, + u_int width) +{ + /* Unsuported */ + return; +} static u_int siba_get_port_count(device_t dev, device_t child, bhnd_port_type type) @@ -603,6 +617,9 @@ /* Release our resource */ bus_release_resource(dev, SYS_RES_MEMORY, rid, r); r = NULL; + + /* Issue bus callback for fully initialized child. */ + BHND_BUS_CHILD_ADDED(dev, child); } cleanup: @@ -634,6 +651,8 @@ DEVMETHOD(bhnd_bus_free_devinfo, siba_free_bhnd_dinfo), DEVMETHOD(bhnd_bus_reset_core, siba_reset_core), DEVMETHOD(bhnd_bus_suspend_core, siba_suspend_core), + DEVMETHOD(bhnd_bus_read_config, siba_read_config), + DEVMETHOD(bhnd_bus_write_config, siba_write_config), DEVMETHOD(bhnd_bus_get_port_count, siba_get_port_count), DEVMETHOD(bhnd_bus_get_region_count, siba_get_region_count), DEVMETHOD(bhnd_bus_get_port_rid, siba_get_port_rid), Index: sys/modules/bhnd/Makefile =================================================================== --- sys/modules/bhnd/Makefile +++ sys/modules/bhnd/Makefile @@ -1,22 +1,36 @@ # $FreeBSD$ .PATH: ${.CURDIR}/../../dev/bhnd +.PATH: ${.CURDIR}/../../dev/bhnd/cores/chipc +.PATH: ${.CURDIR}/../../dev/bhnd/cores/chipc/pwrctl +.PATH: ${.CURDIR}/../../dev/bhnd/cores/pmu .PATH: ${.CURDIR}/../../dev/bhnd/nvram KMOD= bhnd -SRCS= bhnd.c \ - bhnd_subr.c +SRCS= bhnd.c bhnd_subr.c +SRCS+= bhnd_bus_if.c bhnd_bus_if.h +# ChipCommon +SRCS+= chipc.c chipc_subr.c +SRCS+= bhnd_sprom_chipc.c \ + bhnd_pmu_chipc.c \ + bhnd_pwrctl.c bhnd_pwrctl_subr.c +SRCS+= bhnd_chipc_if.c bhnd_chipc_if.h + +# PMU +SRCS+= bhnd_pmu.c \ + bhnd_pmu_core.c \ + bhnd_pmu_subr.c +SRCS+= bhnd_pmu_if.c bhnd_pmu_if.h + +# NVRAM/SPROM SRCS+= bhnd_nvram.c \ bhnd_nvram_parser.c \ bhnd_sprom.c \ bhnd_sprom_parser.c SRCS+= bhnd_nvram_common.c SRCS+= bhnd_nvram_map.h bhnd_nvram_map_data.h - -SRCS+= bhnd_bus_if.c bhnd_bus_if.h \ - bhnd_chipc_if.c bhnd_chipc_if.h \ - bhnd_nvram_if.c bhnd_nvram_if.h +SRCS+= bhnd_nvram_if.c bhnd_nvram_if.h SRCS+= device_if.h bus_if.h Index: sys/modules/bhnd/cores/Makefile =================================================================== --- sys/modules/bhnd/cores/Makefile +++ sys/modules/bhnd/cores/Makefile @@ -1,7 +1,6 @@ # $FreeBSD$ -SUBDIR= bhnd_chipc \ - bhnd_pci \ +SUBDIR= bhnd_pci \ bhnd_pci_hostb \ bhnd_pcib Index: sys/modules/bhnd/cores/bhnd_chipc/Makefile =================================================================== --- sys/modules/bhnd/cores/bhnd_chipc/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# $FreeBSD$ - -.PATH: ${.CURDIR}/../../../../dev/bhnd/cores/chipc - -KMOD= bhnd_chipc -SRCS= chipc.c chipc_subr.c \ - bhnd_sprom_chipc.c -SRCS+= device_if.h bus_if.h bhnd_bus_if.h \ - bhnd_chipc_if.h bhnd_nvram_if.h - -.include