/*- * SPDX-License-Identifier: ISC * * 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 #include #include #include #include #include "bhnd_nvram_map.h" #include "bhnd_pmureg.h" #include "bhnd_pmuvar.h" #include "bhnd_pmu_private.h" #define PMU_LOG(_sc, _fmt, ...) do { \ if (_sc->dev != NULL) \ device_printf(_sc->dev, _fmt, ##__VA_ARGS__); \ else \ printf(_fmt, ##__VA_ARGS__); \ } while (0) #ifdef BCMDBG #define PMU_DEBUG(_sc, _fmt, ...) PMU_LOG(_sc, _fmt, ##__VA_ARGS__) #else #define PMU_DEBUG(_sc, _fmt, ...) #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_query *sc); static const pmu1_xtaltab0_t *bhnd_pmu1_xtaldef0(struct bhnd_pmu_query *sc); static void bhnd_pmu0_pllinit0(struct bhnd_pmu_softc *sc, uint32_t xtal); static uint32_t bhnd_pmu0_cpuclk0(struct bhnd_pmu_query *sc); static uint32_t bhnd_pmu0_alpclk0(struct bhnd_pmu_query *sc); static void bhnd_pmu1_pllinit0(struct bhnd_pmu_softc *sc, uint32_t xtal); static uint32_t bhnd_pmu1_pllfvco0(struct bhnd_pmu_query *sc); static uint32_t bhnd_pmu1_cpuclk0(struct bhnd_pmu_query *sc); static uint32_t bhnd_pmu1_alpclk0(struct bhnd_pmu_query *sc); static uint32_t bhnd_pmu5_clock(struct bhnd_pmu_query *sc, u_int pll0, u_int m); static uint32_t bhnd_pmu6_4706_clock(struct bhnd_pmu_query *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 int bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, bhnd_pmu_spuravoid spuravoid); static void bhnd_pmu_set_4330_plldivs(struct bhnd_pmu_softc *sc); #define BHND_PMU_REV(_sc) \ ((uint8_t)BHND_PMU_GET_BITS((_sc)->caps, BHND_PMU_CAP_REV)) #define PMU_WAIT_CLKST(_sc, _val, _mask) \ bhnd_core_clkctl_wait((_sc)->clkctl, (_val), (_mask)) #define PMURES_BIT(_bit) \ (1 << (BHND_PMU_ ## _bit)) #define PMU_CST4330_SDIOD_CHIPMODE(_sc) \ CHIPC_CST4330_CHIPMODE_SDIOD((_sc)->io->rd_chipst((_sc)->io_ctx)) /** * Initialize @p query state. * * @param[out] query On success, will be populated with a valid query instance * state. * @param dev The device owning @p query, or NULL. * @param id The bhnd chip identification. * @param io I/O callback functions. * @param ctx I/O callback context. * * @retval 0 success * @retval non-zero if the query state could not be initialized. */ int bhnd_pmu_query_init(struct bhnd_pmu_query *query, device_t dev, struct bhnd_chipid id, const struct bhnd_pmu_io *io, void *ctx) { query->dev = dev; query->io = io; query->io_ctx = ctx; query->cid = id; query->caps = BHND_PMU_READ_4(query, BHND_PMU_CAP); return (0); } /** * Release any resources held by @p query. * * @param query A query instance previously initialized via * bhnd_pmu_query_init(). */ void bhnd_pmu_query_fini(struct bhnd_pmu_query *query) { /* nothing to do */ } /** * 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(const struct bhnd_pmu_io *io, void *io_ctx, bus_size_t addr, bus_size_t data, uint32_t reg) { io->wr4(addr, reg, io_ctx); return (io->rd4(data, io_ctx)); } /** * 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(const struct bhnd_pmu_io *io, void *io_ctx, bus_size_t addr, bus_size_t data, uint32_t reg, uint32_t val, uint32_t mask) { uint32_t rval; io->wr4(addr, reg, io_ctx); if (mask != UINT32_MAX) { rval = io->rd4(data, io_ctx); rval &= ~mask | (val & mask); } else { rval = val; } io->wr4(data, rval, io_ctx); } /* 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); } int 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: PMU_LOG(sc, "unknown BCM4328/BCM5354 LDO %hhu\n", ldo); return (ENODEV); } break; case BHND_CHIPID_BCM4312: switch (ldo) { case SET_LDO_VOLTAGE_PAREF: addr = 0; shift = 21; mask = 0x3f; break; default: PMU_LOG(sc, "unknown BCM4312 LDO %hhu\n", ldo); return (ENODEV); } 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: PMU_LOG(sc, "unknown BCM4325 LDO %hhu\n", ldo); return (ENODEV); } 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: PMU_LOG(sc, "unknown BCM4336 LDO %hhu\n", ldo); return (ENODEV); } break; case BHND_CHIPID_BCM4330: switch (ldo) { case SET_LDO_VOLTAGE_CBUCK_PWM: addr = 3; shift = 0; mask = 0x1f; break; default: PMU_LOG(sc, "unknown BCM4330 LDO %hhu\n", ldo); return (ENODEV); } break; case BHND_CHIPID_BCM4331: switch (ldo) { case SET_LDO_VOLTAGE_PAREF: addr = 1; shift = 0; mask = 0xf; break; default: PMU_LOG(sc, "unknown BCM4331 LDO %hhu\n", ldo); return (ENODEV); } break; default: PMU_LOG(sc, "cannot set LDO voltage on unsupported chip %hu\n", sc->cid.chip_id); return (ENODEV); } regctrl = (voltage & mask) << shift; BHND_PMU_REGCTRL_WRITE(sc, addr, regctrl, mask << shift); return (0); } /* d11 slow to fast clock transition time in slow clock cycles */ #define D11SCC_SLOW2FAST_TRANSITION 2 int bhnd_pmu_fast_pwrup_delay(struct bhnd_pmu_softc *sc, u_int *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->query); 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->query); 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->query); 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->query); delay = (uptime + D11SCC_SLOW2FAST_TRANSITION) * ((1000000 + ilp - 1) / ilp); delay = (11 * delay) / 10; break; default: delay = BHND_PMU_MAX_TRANSITION_DLY; break; } *pwrup_delay = 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) { PMU_LOG(sc, "NVRAM error reading %s: %d\n", BHND_NVAR_RMIN, error); return (error); } else if (!error) { PMU_DEBUG(sc, "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) { PMU_LOG(sc, "NVRAM error reading %s: %d\n", BHND_NVAR_RMAX, error); return (error); } else if (!error) { PMU_DEBUG(sc, "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_DEBUG(sc, "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) { PMU_LOG(sc, "NVRAM error reading %s: %d\n", name, error); return (error); } PMU_DEBUG(sc, "Applying %s=%d 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_DEBUG(sc, "Changing rsrc %hhu res_dep_mask to " "%#x\n", i, table->depend_mask); depend_mask = rdep->depend_mask; break; case RES_DEPEND_ADD: PMU_DEBUG(sc, "Adding %#x to rsrc %hhu " "res_dep_mask\n", table->depend_mask, i); depend_mask |= rdep->depend_mask; break; case RES_DEPEND_REMOVE: PMU_DEBUG(sc, "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; } BHND_PMU_WRITE_4(sc, BHND_PMU_RES_DEP_MASK, depend_mask); } } /* 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) { PMU_LOG(sc, "NVRAM error reading %s: %d\n", name, error); return (error); } PMU_DEBUG(sc, "Applying %s=%d 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_DEBUG(sc, "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_DEBUG(sc, "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_query *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_DEBUG(sc, "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_query *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_DEBUG(sc, "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_query *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_DEBUG(sc, "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_query *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) { PMU_LOG(sc, "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_DEBUG(sc, "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_DEBUG(sc, "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_DEBUG(sc, "PLL already programmed for %d.%d MHz\n", xt->freq / 1000, xt->freq % 1000); return; } if (xf != 0) { PMU_DEBUG(sc, "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_DEBUG(sc, "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_DEBUG(sc, "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_DEBUG(sc, "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_query *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_query *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_DEBUG(sc, "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->query) / 1000; buf_strength = 0; ndiv_mode = 1; /* Use h/w default PLL config */ if (xtal == 0) { PMU_DEBUG(sc, "Unspecified xtal frequency, skipping PLL " "configuration\n"); return; } /* Find the frequency in the table */ for (xt = bhnd_pmu1_xtaltab0(&sc->query); 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) { PMU_LOG(sc, "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_DEBUG(sc, "PLL already programmed for %d.%dMHz\n", xt->fref / 1000, xt->fref % 1000); return; } PMU_DEBUG(sc, "XTAL %d.%dMHz (%d)\n", xtal / 1000, xtal % 1000, xt->xf); PMU_DEBUG(sc, "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_DEBUG(sc, "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_DEBUG(sc, "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_DEBUG(sc, "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_CCTRL4319USB_24MHZ_PLL_SEL; break; case XTAL_FREQ_48000MHZ: pll_sel = BHND_PMU_CCTRL4319USB_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_CCTRL4319USB_XTAL_SEL), BHND_PMU_CCTRL4319USB_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_query *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_DEBUG(sc, "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_DEBUG("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; } } /** * Return the ALP/XTAL clock frequency, in Hz. * * @param sc PMU query instance. */ uint32_t bhnd_pmu_alp_clock(struct bhnd_pmu_query *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_DEBUG("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_query *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_LOG(sc, "%s: Bad pll0: %d", __func__, pll0); return (0); } /* Strictly there is an m5 divider, but I'm not sure we use it */ if ((m == 0) || (m > 4)) { PMU_LOG(sc, "%s: Bad m divider: %d", __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 = sc->io->rd_chipst(sc->io_ctx); 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_DEBUG(sc, "%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); } static uint32_t bhnd_pmu6_4706_clock(struct bhnd_pmu_query *sc, u_int pll0, u_int m) { uint32_t chipst, clock; uint32_t ndiv, p1div, p2div, tmp; /* Get N, P1 and P2 dividers to determine CPU clock */ BHND_PMU_WRITE_4(sc, BHND_PMU_PLL_CONTROL_ADDR, pll0 + BHND_PMU6_4706_PROCPLL_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_PMU6_4706_PROC_NDIV_INT); p1div = BHND_PMU_GET_BITS(tmp, BHND_PMU6_4706_PROC_P1DIV); p2div = BHND_PMU_GET_BITS(tmp, BHND_PMU6_4706_PROC_P2DIV); /* Fixed 25MHz reference clock */ clock = 25 * 1000 * 1000; /* The low-cost bonding uses an input divider of 4; otherwise, 2 */ chipst = sc->io->rd_chipst(sc->io_ctx); if (chipst & CHIPC_CST4706_LOWCOST_PKG) clock /= 4; else clock /= 2; clock *= ndiv * p2div / p1div; switch (m) { case BHND_PMU6_MAINPLL_CPU: return (clock); case BHND_PMU6_MAINPLL_MEM: return (clock / 2); case BHND_PMU6_MAINPLL_SI: return (clock / 4); default: PMU_LOG(sc, "bad m divider: %d", m); return (0); } } /** * Return the backplane clock frequency, in Hz. * * On designs that feed the same clock to both backplane * and CPU, this returns the CPU clock speed. * * @param sc PMU query instance. */ uint32_t bhnd_pmu_si_clock(struct bhnd_pmu_query *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_BCM4312: 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 = sc->io->rd_chipst(sc->io_ctx); if (chipst & CHIPC_CST43236_BP_CLK) clock = 120000 * 1000; else clock = 96000 * 1000; break; case BHND_CHIPID_BCM43237: chipst = sc->io->rd_chipst(sc->io_ctx); 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_BCM4706: clock = bhnd_pmu6_4706_clock(sc, BHND_PMU4706_MAINPLL_PLL0, BHND_PMU6_MAINPLL_SI); break; case BHND_CHIPID_BCM53572: clock = 75000000; break; default: PMU_LOG(sc, "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); } /** * Return the CPU clock frequency, in Hz. * * @param sc PMU query instance. */ uint32_t bhnd_pmu_cpu_clock(struct bhnd_pmu_query *sc) { /* 5354 chip uses a non programmable PLL of frequency 240MHz */ if (sc->cid.chip_id == BHND_CHIPID_BCM5354) return (240 * 1000 * 1000); /* 240MHz */ 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) { switch (sc->cid.chip_id) { case BHND_CHIPID_BCM5356: return (bhnd_pmu5_clock(sc, BHND_PMU5356_MAINPLL_PLL0, BHND_PMU5_MAINPLL_CPU)); case BHND_CHIPID_BCM5357: case BHND_CHIPID_BCM4749: return (bhnd_pmu5_clock(sc, BHND_PMU5357_MAINPLL_PLL0, BHND_PMU5_MAINPLL_CPU)); case BHND_CHIPID_BCM4706: return (bhnd_pmu6_4706_clock(sc, BHND_PMU4706_MAINPLL_PLL0, BHND_PMU6_MAINPLL_CPU)); default: return (bhnd_pmu5_clock(sc, BHND_PMU4716_MAINPLL_PLL0, BHND_PMU5_MAINPLL_CPU)); } } else { return (bhnd_pmu_si_clock(sc)); } } /** * Return the memory clock frequency, in Hz. * * @param sc PMU query instance. */ uint32_t bhnd_pmu_mem_clock(struct bhnd_pmu_query *sc) { 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) { switch (sc->cid.chip_id) { case BHND_CHIPID_BCM5356: return (bhnd_pmu5_clock(sc, BHND_PMU5356_MAINPLL_PLL0, BHND_PMU5_MAINPLL_MEM)); case BHND_CHIPID_BCM5357: case BHND_CHIPID_BCM4749: return (bhnd_pmu5_clock(sc, BHND_PMU5357_MAINPLL_PLL0, BHND_PMU5_MAINPLL_MEM)); case BHND_CHIPID_BCM4706: return (bhnd_pmu6_4706_clock(sc, BHND_PMU4706_MAINPLL_PLL0, BHND_PMU6_MAINPLL_MEM)); default: return (bhnd_pmu5_clock(sc, BHND_PMU4716_MAINPLL_PLL0, BHND_PMU5_MAINPLL_MEM)); } } else { return (bhnd_pmu_si_clock(sc)); } } /* Measure ILP clock frequency */ #define ILP_CALC_DUR 10 /* ms, make sure 1000 can be divided by it. */ /** * Measure and return the ILP clock frequency, in Hz. * * @param sc PMU query instance. */ uint32_t bhnd_pmu_ilp_clock(struct bhnd_pmu_query *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; str_tab = NULL; str_mask = 0; str_shift = 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: PMU_LOG(sc, "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_DEBUG(sc, "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) PMU_LOG(sc, "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_DEBUG(sc, "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_DEBUG(sc, "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) { PMU_LOG(sc, "timeout waiting for OTP resource " "enable\n"); return (ENXIO); } } else { PMU_DEBUG(sc, "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_CHIP_CONTROL_ADDR, 1); /* Power Down RCAL Block */ BHND_PMU_AND_4(sc, BHND_PMU_CHIP_CONTROL_DATA, ~0x04); if (sc->cid.chip_id == BHND_CHIPID_BCM4325) { chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev); if (BHND_PMU_GET_BITS(chipst, CHIPC_CST4325_RCAL_VALID)) bluetooth_rcal = true; } /* Power Up RCAL block */ BHND_PMU_AND_4(sc, BHND_PMU_CHIP_CONTROL_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_DEBUG("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_CHIP_CONTROL_ADDR, 0); val = BHND_PMU_READ_4(sc, BHND_PMU_CHIP_CONTROL_DATA); val &= ~((uint32_t) 0x03 << 30); val |= (uint32_t) (rcal_code & 0x03) << 30; BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_DATA, val); BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_ADDR, 1); val = BHND_PMU_READ_4(sc, BHND_PMU_CHIP_CONTROL_DATA); val &= ~(uint32_t) 0x03; val |= (uint32_t) ((rcal_code >> 2) & 0x03); BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_DATA, val); /* Set override in pmu_chip_ctrl[29] */ BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_ADDR, 0); BHND_PMU_OR_4(sc, BHND_PMU_CHIP_CONTROL_DATA, (0x01 << 29)); /* Power off RCal block */ BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_ADDR, 1); BHND_PMU_AND_4(sc, BHND_PMU_CHIP_CONTROL_DATA, ~0x04); break; default: break; } } int bhnd_pmu_set_spuravoid(struct bhnd_pmu_softc *sc, bhnd_pmu_spuravoid spuravoid) { int error; /* force the HT off */ if (sc->cid.chip_id == BHND_CHIPID_BCM4336) { BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK, ~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 */ error = bhnd_pmu_spuravoid_pllupdate(sc, spuravoid); /* enable HT back on */ if (sc->cid.chip_id == BHND_CHIPID_BCM4336) { BHND_PMU_OR_4(sc, BHND_PMU_MAX_RES_MASK, BHND_PMU_RES4336_HT_AVAIL); } return (error); } static int bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc, bhnd_pmu_spuravoid spuravoid) { uint16_t chip_id; uint32_t pmuctrl; uint32_t tmp; /* 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: { uint8_t p1div, ndiv; uint8_t phypll_offset; switch (spuravoid) { case BHND_PMU_SPURAVOID_NONE: p1div = 0x1; ndiv = 0x30; break; case BHND_PMU_SPURAVOID_M1: p1div = 0x5; ndiv = 0xf6; break; case BHND_PMU_SPURAVOID_M2: p1div = 0x5; ndiv = 0xfc; break; default: return (ENODEV); } /* BCM5357 needs to touch PLL1_PLLCTL[02], so offset * PLL0_PLLCTL[02] by 6 */ phypll_offset = 0; if (sc->cid.chip_id == BHND_CHIPID_BCM5357) phypll_offset = 6; /* RMW only the P1 divider */ tmp = BHND_PMU_SET_BITS(p1div, BHND_PMU1_PLL0_PC0_P1DIV); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0 + phypll_offset, tmp, BHND_PMU1_PLL0_PC0_P1DIV_MASK); /* RMW only the int feedback divider */ tmp = BHND_PMU_SET_BITS(ndiv, BHND_PMU1_PLL0_PC2_NDIV_INT); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2 + phypll_offset, tmp, BHND_PMU1_PLL0_PC0_P1DIV_MASK); pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD; break; } case BHND_CHIPID_BCM4331: switch (spuravoid) { case BHND_PMU_SPURAVOID_NONE: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x11100014, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, 0x03000a08, ~0); break; case BHND_PMU_SPURAVOID_M1: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x11500014, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, 0x0F600a08, ~0); break; case BHND_PMU_SPURAVOID_M2: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x11500014, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, 0x0FC00a08, ~0); break; default: return (ENODEV); } pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD; break; case BHND_CHIPID_BCM43224: case BHND_CHIPID_BCM43225: case BHND_CHIPID_BCM43226: case BHND_CHIPID_BCM43421: switch (spuravoid) { case BHND_PMU_SPURAVOID_NONE: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x11100010, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, 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); break; case BHND_PMU_SPURAVOID_M1: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x11500010, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, 0x000C0C06, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, 0x0F600a08, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, 0x00000000, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, 0x2001E920, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, 0x88888815, ~0); break; case BHND_PMU_SPURAVOID_M2: default: return (ENODEV); } pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD; break; case BHND_CHIPID_BCM43111: case BHND_CHIPID_BCM43112: case BHND_CHIPID_BCM43222: case BHND_CHIPID_BCM43420: switch (spuravoid) { case BHND_PMU_SPURAVOID_NONE: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x11100008, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, 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); break; case BHND_PMU_SPURAVOID_M1: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x11500008, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, 0x0c000c06, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, 0x0f600a08, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, 0x00000000, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, 0x2001e920, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, 0x88888815, ~0); break; case BHND_PMU_SPURAVOID_M2: default: return (ENODEV); } pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD; break; case BHND_CHIPID_BCM4716: case BHND_CHIPID_BCM4748: case BHND_CHIPID_BCM47162: switch (spuravoid) { case BHND_PMU_SPURAVOID_NONE: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x11100060, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, 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); break; case BHND_PMU_SPURAVOID_M1: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x11500060, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, 0x080C0C06, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, 0x0F600000, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, 0x00000000, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, 0x2001E924, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, 0x88888815, ~0); break; case BHND_PMU_SPURAVOID_M2: default: return (ENODEV); } pmuctrl = BHND_PMU_CTRL_NOILP_ON_WAIT | BHND_PMU_CTRL_PLL_PLLCTL_UPD; break; case BHND_CHIPID_BCM4319: pmuctrl = 0; 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); switch (spuravoid) { case BHND_PMU_SPURAVOID_NONE: /* enable 40/80/160Mhz clock mode */ BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, 0x05001828, ~0); break; case BHND_PMU_SPURAVOID_M1: /* spur_avoid ON, enable 41/82/164Mhz clock mode */ BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, 0x05201828, ~0); break; case BHND_PMU_SPURAVOID_M2: default: return (ENODEV); } pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD; 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); switch (spuravoid) { case BHND_PMU_SPURAVOID_NONE: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, 0x00762762, ~0); break; case BHND_PMU_SPURAVOID_M1: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, 0x00EC4EC4, ~0); break; case BHND_PMU_SPURAVOID_M2: default: return (ENODEV); } 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 */ switch (spuravoid) { case BHND_PMU_SPURAVOID_NONE: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x11100014, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, 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); break; case BHND_PMU_SPURAVOID_M1: BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x01100014, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, 0x040C0C06, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, 0x03140A08, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3, 0x00333333, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, 0x202C2820, ~0); BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, 0x88888815, ~0); break; case BHND_PMU_SPURAVOID_M2: default: return (ENODEV); } pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD; break; default: PMU_LOG(sc, "%s: unknown spuravoidance settings for chip %#hx, " "not changing PLL", __func__, sc->cid.chip_id); return (ENODEV); } if (pmuctrl != 0) BHND_PMU_OR_4(sc, BHND_PMU_CTRL, pmuctrl); return (0); } 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); } int 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 (ENODEV); } 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); } return (0); } /* 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; } } int bhnd_pmu_radio_enable(struct bhnd_pmu_softc *sc, device_t d11core, bool enable) { uint32_t oobsel; uint32_t rsrcs; int error; if (bhnd_get_device(d11core) != BHND_COREID_D11) { device_printf(sc->dev, "bhnd_pmu_radio_enable() called on non-D11 core"); return (EINVAL); } 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); } return (0); case BHND_CHIPID_BCM4319: error = bhnd_read_config(d11core, BCMA_DMP_OOBSELOUTB74, &oobsel, 4); if (error) return (error); if (enable) { oobsel |= BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN, BCMA_DMP_OOBSEL_5); oobsel |= BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN, BCMA_DMP_OOBSEL_6); } else { oobsel &= ~BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN, BCMA_DMP_OOBSEL_5); oobsel &= ~BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN, BCMA_DMP_OOBSEL_6); } return (bhnd_write_config(d11core, BCMA_DMP_OOBSELOUTB74, &oobsel, 4)); } return (0); } /* 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->query) / 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); }