diff options
Diffstat (limited to 'target/linux/brcm-2.4/files/arch/mips/bcm947xx/hndpmu.c')
-rw-r--r-- | target/linux/brcm-2.4/files/arch/mips/bcm947xx/hndpmu.c | 1257 |
1 files changed, 1257 insertions, 0 deletions
diff --git a/target/linux/brcm-2.4/files/arch/mips/bcm947xx/hndpmu.c b/target/linux/brcm-2.4/files/arch/mips/bcm947xx/hndpmu.c new file mode 100644 index 0000000..c7d7b3b --- /dev/null +++ b/target/linux/brcm-2.4/files/arch/mips/bcm947xx/hndpmu.c @@ -0,0 +1,1257 @@ +/* + * Misc utility routines for accessing PMU corerev specific features + * of the SiliconBackplane-based Broadcom chips. + * + * Copyright 2007, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * $Id$ + */ + +#include <typedefs.h> +#include <bcmdefs.h> +#include <osl.h> +#include <sbutils.h> +#include <bcmdevs.h> +#include <sbconfig.h> +#include <sbchipc.h> +#include <hndpmu.h> + +/* debug/trace */ +#define PMU_ERROR(args) + +#ifdef BCMDBG +#define PMU_MSG(args) printf args +#else +#define PMU_MSG(args) +#endif /* BCMDBG */ + +/* PMU & control */ +/* PMU rev 0 pll control for BCM4328 and BCM5354 */ +static void sb_pmu0_pllinit0 (sb_t * sbh, osl_t * osh, chipcregs_t * cc, + uint32 xtal); +static uint32 sb_pmu0_alpclk0 (sb_t * sbh, osl_t * osh, chipcregs_t * cc); +static uint32 sb_pmu0_cpuclk0 (sb_t * sbh, osl_t * osh, chipcregs_t * cc); +/* PMU rev 0 pll control for BCM4325 BCM4329 */ +static void sb_pmu1_pllinit0 (sb_t * sbh, osl_t * osh, chipcregs_t * cc, + uint32 xtal); +static uint32 sb_pmu1_cpuclk0 (sb_t * sbh, osl_t * osh, chipcregs_t * cc); +static uint32 sb_pmu1_alpclk0 (sb_t * sbh, osl_t * osh, chipcregs_t * cc); + +/* Setup switcher voltage */ +void +BCMINITFN (sb_pmu_set_switcher_voltage) (sb_t * sbh, osl_t * osh, + uint8 bb_voltage, uint8 rf_voltage) +{ + chipcregs_t *cc; + uint origidx; + + ASSERT (sbh->cccaps & CC_CAP_PMU); + + /* Remember original core before switch to chipc */ + origidx = sb_coreidx (sbh); + cc = sb_setcore (sbh, SB_CC, 0); + ASSERT (cc); + + W_REG (osh, &cc->regcontrol_addr, 0x01); + W_REG (osh, &cc->regcontrol_data, (uint32) (bb_voltage & 0x1f) << 22); + + W_REG (osh, &cc->regcontrol_addr, 0x00); + W_REG (osh, &cc->regcontrol_data, (uint32) (rf_voltage & 0x1f) << 14); + + /* Return to original core */ + sb_setcoreidx (sbh, origidx); +} + +void +sb_pmu_set_ldo_voltage (sb_t * sbh, osl_t * osh, uint8 ldo, uint8 voltage) +{ + uint8 sr_cntl_shift, rc_shift, shift, mask; + uint32 addr; + + ASSERT (sbh->cccaps & CC_CAP_PMU); + + switch (sbh->chip) + { + case BCM4328_CHIP_ID: + case BCM5354_CHIP_ID: + switch (ldo) + { + case SET_LDO_VOLTAGE_LDO1: + addr = 2; + sr_cntl_shift = 8; + rc_shift = 17; + mask = 0xf; + break; + case SET_LDO_VOLTAGE_LDO2: + addr = 3; + sr_cntl_shift = 0; + rc_shift = 1; + mask = 0xf; + break; + case SET_LDO_VOLTAGE_LDO3: + addr = 3; + sr_cntl_shift = 0; + rc_shift = 9; + mask = 0xf; + break; + case SET_LDO_VOLTAGE_PAREF: + addr = 3; + sr_cntl_shift = 0; + rc_shift = 17; + mask = 0x3f; + break; + default: + ASSERT (FALSE); + return; + } + break; + case BCM4312_CHIP_ID: + switch (ldo) + { + case SET_LDO_VOLTAGE_PAREF: + addr = 0; + sr_cntl_shift = 0; + rc_shift = 21; + mask = 0x3f; + break; + default: + ASSERT (FALSE); + return; + } + break; + default: + ASSERT (FALSE); + return; + } + + shift = sr_cntl_shift + rc_shift; + + sb_corereg (sbh, SB_CC_IDX, OFFSETOF (chipcregs_t, regcontrol_addr), + ~0, addr); + sb_corereg (sbh, SB_CC_IDX, OFFSETOF (chipcregs_t, regcontrol_data), + mask << shift, (voltage & mask) << shift); +} + +void +sb_pmu_paref_ldo_enable (sb_t * sbh, osl_t * osh, bool enable) +{ + uint ldo = 0; + + ASSERT (sbh->cccaps & CC_CAP_PMU); + + switch (sbh->chip) + { + case BCM4328_CHIP_ID: + ldo = RES4328_PA_REF_LDO; + break; + case BCM5354_CHIP_ID: + ldo = RES5354_PA_REF_LDO; + break; + case BCM4312_CHIP_ID: + ldo = RES4312_PA_REF_LDO; + break; + default: + return; + } + + sb_corereg (sbh, SB_CC_IDX, OFFSETOF (chipcregs_t, min_res_mask), + PMURES_BIT (ldo), enable ? PMURES_BIT (ldo) : 0); +} + +uint16 BCMINITFN (sb_pmu_fast_pwrup_delay) (sb_t * sbh, osl_t * osh) +{ + uint16 delay = PMU_MAX_TRANSITION_DLY; + + ASSERT (sbh->cccaps & CC_CAP_PMU); + + switch (sbh->chip) + { + case BCM4328_CHIP_ID: + delay = 7000; + break; + + case BCM4325_CHIP_ID: + case BCM4312_CHIP_ID: +#ifdef BCMQT + delay = 70; +#else + delay = 2800; +#endif + break; + + default: + PMU_MSG (("No PMU fast power up delay specified " + "for chip %x rev %d, using default %d us\n", + sbh->chip, sbh->chiprev, delay)); + break; + } + + return delay; +} + +uint32 BCMINITFN (sb_pmu_force_ilp) (sb_t * sbh, osl_t * osh, bool force) +{ + chipcregs_t *cc; + uint origidx; + uint32 oldpmucontrol; + + ASSERT (sbh->cccaps & CC_CAP_PMU); + + /* Remember original core before switch to chipc */ + origidx = sb_coreidx (sbh); + cc = sb_setcore (sbh, SB_CC, 0); + ASSERT (cc); + + oldpmucontrol = R_REG (osh, &cc->pmucontrol); + if (force) + W_REG (osh, &cc->pmucontrol, oldpmucontrol & + ~(PCTL_HT_REQ_EN | PCTL_ALP_REQ_EN)); + else + W_REG (osh, &cc->pmucontrol, oldpmucontrol | + (PCTL_HT_REQ_EN | PCTL_ALP_REQ_EN)); + + /* Return to original core */ + sb_setcoreidx (sbh, origidx); + + return oldpmucontrol; +} + +/* Setup min/max resources and up/down timers */ +typedef struct +{ + uint8 resnum; + uint16 updown; +} pmu_res_updown_t; + +typedef struct +{ + uint8 resnum; + int8 action; /* 0 - set, 1 - add, -1 - remove */ + uint32 depend_mask; +} pmu_res_depend_t; + +static const pmu_res_updown_t +BCMINITDATA (bcm4328a0_res_updown)[] = +{ + { + RES4328_EXT_SWITCHER_PWM, 0x0101}, + { + RES4328_BB_SWITCHER_PWM, 0x1f01}, + { + RES4328_BB_SWITCHER_BURST, 0x010f}, + { + RES4328_BB_EXT_SWITCHER_BURST, 0x0101}, + { + RES4328_ILP_REQUEST, 0x0202}, + { + RES4328_RADIO_SWITCHER_PWM, 0x0f01}, + { + RES4328_RADIO_SWITCHER_BURST, 0x0f01}, + { + RES4328_ROM_SWITCH, 0x0101}, + { + RES4328_PA_REF_LDO, 0x0f01}, + { + RES4328_RADIO_LDO, 0x0f01}, + { + RES4328_AFE_LDO, 0x0f01}, + { + RES4328_PLL_LDO, 0x0f01}, + { + RES4328_BG_FILTBYP, 0x0101}, + { + RES4328_TX_FILTBYP, 0x0101}, + { + RES4328_RX_FILTBYP, 0x0101}, + { + RES4328_XTAL_PU, 0x0101}, + { + RES4328_XTAL_EN, 0xa001}, + { + RES4328_BB_PLL_FILTBYP, 0x0101}, + { + RES4328_RF_PLL_FILTBYP, 0x0101}, + { + RES4328_BB_PLL_PU, 0x0701} +}; + +static const pmu_res_depend_t +BCMINITDATA (bcm4328a0_res_depend)[] = +{ + /* Adjust ILP request resource not to force ext/BB switchers into burst mode */ + { + RES4328_ILP_REQUEST, 0, + PMURES_BIT (RES4328_EXT_SWITCHER_PWM) | + PMURES_BIT (RES4328_BB_SWITCHER_PWM)} +}; + +#ifdef BCMQT /* for power save on slow QT/small beacon interval */ +static const pmu_res_updown_t +BCMINITDATA (bcm4325a0_res_updown_qt)[] = +{ + { + RES4325_HT_AVAIL, 0x0300}, + { + RES4325_BBPLL_PWRSW_PU, 0x0101}, + { + RES4325_RFPLL_PWRSW_PU, 0x0101}, + { + RES4325_ALP_AVAIL, 0x0100}, + { + RES4325_XTAL_PU, 0x1000}, + { + RES4325_LNLDO1_PU, 0x0800}, + { + RES4325_CLDO_CBUCK_PWM, 0x0101}, + { + RES4325_CBUCK_PWM, 0x0803} +}; +#else +static const pmu_res_updown_t +BCMINITDATA (bcm4325a0_res_updown)[] = +{ + { + RES4325_XTAL_PU, 0x1501} +}; +#endif /* !BCMQT */ + +static const pmu_res_depend_t +BCMINITDATA (bcm4325a0_res_depend)[] = +{ + /* Adjust HT Avail resource dependencies */ + { + RES4325_HT_AVAIL, 1, + PMURES_BIT (RES4325_RX_PWRSW_PU) | PMURES_BIT (RES4325_TX_PWRSW_PU) | + PMURES_BIT (RES4325_LOGEN_PWRSW_PU) | PMURES_BIT (RES4325_AFE_PWRSW_PU)} +}; + +void BCMINITFN (sb_pmu_res_init) (sb_t * sbh, osl_t * osh) +{ + chipcregs_t *cc; + uint origidx; + const pmu_res_updown_t *pmu_res_updown_table = NULL; + int pmu_res_updown_table_sz = 0; + const pmu_res_depend_t *pmu_res_depend_table = NULL; + int pmu_res_depend_table_sz = 0; + uint32 min_mask = 0, max_mask = 0; + + ASSERT (sbh->cccaps & CC_CAP_PMU); + + /* Remember original core before switch to chipc */ + origidx = sb_coreidx (sbh); + cc = sb_setcore (sbh, SB_CC, 0); + ASSERT (cc); + + switch (sbh->chip) + { + case BCM4328_CHIP_ID: + /* Down to ILP request excluding ROM */ + min_mask = PMURES_BIT (RES4328_EXT_SWITCHER_PWM) | + PMURES_BIT (RES4328_BB_SWITCHER_PWM) | PMURES_BIT (RES4328_XTAL_EN); +#ifdef BCMROMOFFLOAD + /* Including ROM */ + min_mask |= PMURES_BIT (RES4328_ROM_SWITCH); +#endif + /* Allow (but don't require) PLL to turn on */ + max_mask = 0xfffff; + pmu_res_updown_table = bcm4328a0_res_updown; + pmu_res_updown_table_sz = ARRAYSIZE (bcm4328a0_res_updown); + pmu_res_depend_table = bcm4328a0_res_depend; + pmu_res_depend_table_sz = ARRAYSIZE (bcm4328a0_res_depend); + break; + case BCM4312_CHIP_ID: + /* keep default + * min_mask = 0xcbb; max_mask = 0x7ffff; + * pmu_res_updown_table_sz = 0; + * pmu_res_depend_table_sz = 0; + */ + break; + case BCM5354_CHIP_ID: + /* Allow (but don't require) PLL to turn on */ + max_mask = 0xfffff; + break; + + case BCM4325_CHIP_ID: + /* Leave OTP powered up and power it down later. */ + min_mask = + PMURES_BIT (RES4325_CBUCK_BURST) | PMURES_BIT (RES4325_LNLDO2_PU); + if (((sbh->chipst & CST4325_PMUTOP_2B_MASK) >> + CST4325_PMUTOP_2B_SHIFT) == 1) + min_mask |= PMURES_BIT (RES4325_CLDO_CBUCK_BURST); + /* Allow (but don't require) PLL to turn on */ + max_mask = 0x3fffff; +#ifdef BCMQT + pmu_res_updown_table = bcm4325a0_res_updown_qt; + pmu_res_updown_table_sz = ARRAYSIZE (bcm4325a0_res_updown_qt); +#else + pmu_res_updown_table = bcm4325a0_res_updown; + pmu_res_updown_table_sz = ARRAYSIZE (bcm4325a0_res_updown); + pmu_res_depend_table = bcm4325a0_res_depend; + pmu_res_depend_table_sz = ARRAYSIZE (bcm4325a0_res_depend); +#endif + break; + + default: + break; + } + + /* Program up/down timers */ + while (pmu_res_updown_table_sz--) + { + ASSERT (pmu_res_updown_table); + W_REG (osh, &cc->res_table_sel, + pmu_res_updown_table[pmu_res_updown_table_sz].resnum); + W_REG (osh, &cc->res_updn_timer, + pmu_res_updown_table[pmu_res_updown_table_sz].updown); + } + + /* Program resource dependencies table */ + while (pmu_res_depend_table_sz--) + { + ASSERT (pmu_res_depend_table); + W_REG (osh, &cc->res_table_sel, + pmu_res_depend_table[pmu_res_depend_table_sz].resnum); + switch (pmu_res_depend_table[pmu_res_depend_table_sz].action) + { + case 0: + W_REG (osh, &cc->res_dep_mask, + pmu_res_depend_table[pmu_res_depend_table_sz].depend_mask); + break; + case 1: + OR_REG (osh, &cc->res_dep_mask, + pmu_res_depend_table[pmu_res_depend_table_sz].depend_mask); + break; + case -1: + AND_REG (osh, &cc->res_dep_mask, + ~pmu_res_depend_table[pmu_res_depend_table_sz]. + depend_mask); + break; + default: + ASSERT (0); + break; + } + } + + /* program min resource mask */ + if (min_mask) + { + PMU_MSG (("Changing min_res_mask to 0x%x\n", min_mask)); + W_REG (osh, &cc->min_res_mask, min_mask); + } + /* program max resource mask */ + if (max_mask) + { + PMU_MSG (("Changing max_res_mask to 0x%x\n", max_mask)); + W_REG (osh, &cc->max_res_mask, max_mask); + } + + /* Return to original core */ + sb_setcoreidx (sbh, origidx); +} + +/* setup pll and query clock speed */ +typedef struct +{ + uint16 freq; + uint8 xf; + uint8 wbint; + uint32 wbfrac; +} pmu0_xtaltab0_t; + +/* the following table is based on 880Mhz Fvco */ +#define PMU0_PLL0_FVCO 880000 /* Fvco 880Mhz */ +static const pmu0_xtaltab0_t +BCMINITDATA (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} +}; + +#ifdef BCMUSBDEV +#define PMU0_XTAL0_DEFAULT 11 +#else +#define PMU0_XTAL0_DEFAULT 8 +#endif + +#ifdef BCMUSBDEV +/* + * Set new backplane PLL clock frequency + */ +static void BCMINITFN (sb_pmu0_sbclk4328) (sb_t * sbh, int freq) +{ + uint32 tmp, oldmax, oldmin, origidx; + chipcregs_t *cc; + + /* Remember original core before switch to chipc */ + origidx = sb_coreidx (sbh); + cc = sb_setcore (sbh, SB_CC, 0); + ASSERT (cc); + + /* Set new backplane PLL clock */ + W_REG (osh, &cc->pllcontrol_addr, PMU0_PLL0_PLLCTL0); + tmp = R_REG (osh, &cc->pllcontrol_data); + tmp &= ~(PMU0_PLL0_PC0_DIV_ARM_MASK); + tmp |= freq << PMU0_PLL0_PC0_DIV_ARM_SHIFT; + W_REG (osh, &cc->pllcontrol_data, tmp); + + /* Power cycle BB_PLL_PU by disabling/enabling it to take on new freq */ + /* Disable PLL */ + oldmin = R_REG (osh, &cc->min_res_mask); + oldmax = R_REG (osh, &cc->max_res_mask); + W_REG (osh, &cc->min_res_mask, oldmin & ~PMURES_BIT (RES4328_BB_PLL_PU)); + W_REG (osh, &cc->max_res_mask, oldmax & ~PMURES_BIT (RES4328_BB_PLL_PU)); + + /* It takes over several hundred usec to re-enable the PLL since the + * sequencer state machines run on ILP clock. Set delay at 450us to be safe. + * + * Be sure PLL is powered down first before re-enabling it. + */ + + OSL_DELAY (PLL_DELAY); + SPINWAIT ((R_REG (osh, &cc->res_state) & PMURES_BIT (RES4328_BB_PLL_PU)), + PLL_DELAY * 3); + + if (R_REG (osh, &cc->res_state) & PMURES_BIT (RES4328_BB_PLL_PU)) + { + /* If BB_PLL not powered down yet, new backplane PLL clock + * may not take effect. + * + * Still early during bootup so no serial output here. + */ + PMU_ERROR (("Fatal: BB_PLL not power down yet!\n")); + ASSERT (! + (R_REG (osh, &cc->res_state) & PMURES_BIT (RES4328_BB_PLL_PU))); + } + + /* Enable PLL */ + W_REG (osh, &cc->max_res_mask, oldmax); + + /* Return to original core */ + sb_setcoreidx (sbh, origidx); +} +#endif /* BCMUSBDEV */ + +/* Set up PLL registers in the PMU as per the crystal speed. + * Uses xtalfreq variable, or passed-in default. + */ +static void +BCMINITFN (sb_pmu0_pllinit0) (sb_t * sbh, osl_t * osh, chipcregs_t * cc, + uint32 xtal) +{ + uint32 tmp; + const pmu0_xtaltab0_t *xt; + + if ((sb_chip (sbh) == BCM5354_CHIP_ID) && (xtal == 0)) + { + /* 5354 has xtal freq of 25MHz */ + xtal = 25000; + } + + /* 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)\n", xtal, xt->xf)); + + /* Check current PLL state */ + tmp = (R_REG (osh, &cc->pmucontrol) & PCTL_XTALFREQ_MASK) >> + PCTL_XTALFREQ_SHIFT; + if (tmp == xt->xf) + { + PMU_MSG (("PLL already programmed for %d.%d MHz\n", + (xt->freq / 1000), (xt->freq % 1000))); + +#ifdef BCMUSBDEV + if (sbh->chip == BCM4328_CHIP_ID) + sb_pmu0_sbclk4328 (sbh, PMU0_PLL0_PC0_DIV_ARM_88MHZ); +#endif + return; + } + + if (tmp) + { + 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 (sbh->chip) + { + case BCM4328_CHIP_ID: + AND_REG (osh, &cc->min_res_mask, ~PMURES_BIT (RES4328_BB_PLL_PU)); + AND_REG (osh, &cc->max_res_mask, ~PMURES_BIT (RES4328_BB_PLL_PU)); + break; + case BCM5354_CHIP_ID: + AND_REG (osh, &cc->min_res_mask, ~PMURES_BIT (RES5354_BB_PLL_PU)); + AND_REG (osh, &cc->max_res_mask, ~PMURES_BIT (RES5354_BB_PLL_PU)); + break; + default: + ASSERT (0); + } + SPINWAIT (R_REG (osh, &cc->clk_ctl_st) & CCS0_HTAVAIL, + PMU_MAX_TRANSITION_DLY); + ASSERT (!(R_REG (osh, &cc->clk_ctl_st) & CCS0_HTAVAIL)); + + PMU_MSG (("Done masking\n")); + + /* Write PDIV in pllcontrol[0] */ + W_REG (osh, &cc->pllcontrol_addr, PMU0_PLL0_PLLCTL0); + tmp = R_REG (osh, &cc->pllcontrol_data); + if (xt->freq >= PMU0_PLL0_PC0_PDIV_FREQ) + tmp |= PMU0_PLL0_PC0_PDIV_MASK; + else + tmp &= ~PMU0_PLL0_PC0_PDIV_MASK; + W_REG (osh, &cc->pllcontrol_data, tmp); + + /* Write WILD in pllcontrol[1] */ + W_REG (osh, &cc->pllcontrol_addr, PMU0_PLL0_PLLCTL1); + tmp = R_REG (osh, &cc->pllcontrol_data); + tmp = + ((tmp & ~(PMU0_PLL0_PC1_WILD_INT_MASK | PMU0_PLL0_PC1_WILD_FRAC_MASK)) | + (((xt-> + wbint << PMU0_PLL0_PC1_WILD_INT_SHIFT) & PMU0_PLL0_PC1_WILD_INT_MASK) + | ((xt->wbfrac << PMU0_PLL0_PC1_WILD_FRAC_SHIFT) & + PMU0_PLL0_PC1_WILD_FRAC_MASK))); + if (xt->wbfrac == 0) + tmp |= PMU0_PLL0_PC1_STOP_MOD; + else + tmp &= ~PMU0_PLL0_PC1_STOP_MOD; + W_REG (osh, &cc->pllcontrol_data, tmp); + + /* Write WILD in pllcontrol[2] */ + W_REG (osh, &cc->pllcontrol_addr, PMU0_PLL0_PLLCTL2); + tmp = R_REG (osh, &cc->pllcontrol_data); + tmp = ((tmp & ~PMU0_PLL0_PC2_WILD_INT_MASK) | + ((xt->wbint >> PMU0_PLL0_PC2_WILD_INT_SHIFT) & + PMU0_PLL0_PC2_WILD_INT_MASK)); + W_REG (osh, &cc->pllcontrol_data, tmp); + + PMU_MSG (("Done pll\n")); + + /* Write XtalFreq. Set the divisor also. */ + tmp = R_REG (osh, &cc->pmucontrol); + tmp = ((tmp & ~PCTL_ILP_DIV_MASK) | + (((((xt->freq + 127) / 128) - 1) << PCTL_ILP_DIV_SHIFT) & + PCTL_ILP_DIV_MASK)); + tmp = ((tmp & ~PCTL_XTALFREQ_MASK) | + ((xt->xf << PCTL_XTALFREQ_SHIFT) & PCTL_XTALFREQ_MASK)); + W_REG (osh, &cc->pmucontrol, tmp); +} + +static uint32 +BCMINITFN (sb_pmu0_alpclk0) (sb_t * sbh, osl_t * osh, chipcregs_t * cc) +{ + const pmu0_xtaltab0_t *xt; + uint32 xf; + + /* Find the frequency in the table */ + xf = (R_REG (osh, &cc->pmucontrol) & PCTL_XTALFREQ_MASK) >> + PCTL_XTALFREQ_SHIFT; + for (xt = pmu0_xtaltab0; xt->freq; xt++) + if (xt->xf == xf) + break; + if (xt->freq == 0) + xt = &pmu0_xtaltab0[PMU0_XTAL0_DEFAULT]; + + return xt->freq * 1000; +} + +static uint32 +BCMINITFN (sb_pmu0_cpuclk0) (sb_t * sbh, osl_t * osh, chipcregs_t * cc) +{ + const pmu0_xtaltab0_t *xt; + uint32 xf, tmp, divarm; +#ifdef BCMDBG + uint32 pdiv, wbint, wbfrac, fvco; +#endif + + if (sb_chip (sbh) == BCM5354_CHIP_ID) + { + /* 5354 gets sb clock of 120MHz from main pll */ + return 120000000; + } + + /* Find the xtal frequency in the table */ + xf = (R_REG (osh, &cc->pmucontrol) & PCTL_XTALFREQ_MASK) >> + PCTL_XTALFREQ_SHIFT; + for (xt = pmu0_xtaltab0; xt->freq; xt++) + if (xt->xf == xf) + break; + if (xt->freq == 0) + xt = &pmu0_xtaltab0[PMU0_XTAL0_DEFAULT]; + + /* Read divarm from pllcontrol[0] */ + W_REG (osh, &cc->pllcontrol_addr, PMU0_PLL0_PLLCTL0); + tmp = R_REG (osh, &cc->pllcontrol_data); + divarm = (tmp & PMU0_PLL0_PC0_DIV_ARM_MASK) >> PMU0_PLL0_PC0_DIV_ARM_SHIFT; + +#ifdef BCMDBG + /* Calculate Fvco based on xtal freq, pdiv, and wild */ + pdiv = tmp & PMU0_PLL0_PC0_PDIV_MASK; + + W_REG (osh, &cc->pllcontrol_addr, PMU0_PLL0_PLLCTL1); + tmp = R_REG (osh, &cc->pllcontrol_data); + wbfrac = + (tmp & PMU0_PLL0_PC1_WILD_FRAC_MASK) >> PMU0_PLL0_PC1_WILD_FRAC_SHIFT; + wbint = (tmp & PMU0_PLL0_PC1_WILD_INT_MASK) >> PMU0_PLL0_PC1_WILD_INT_SHIFT; + + W_REG (osh, &cc->pllcontrol_addr, PMU0_PLL0_PLLCTL2); + tmp = R_REG (osh, &cc->pllcontrol_data); + wbint += + (tmp & PMU0_PLL0_PC2_WILD_INT_MASK) << PMU0_PLL0_PC2_WILD_INT_SHIFT; + + fvco = (xt->freq * wbint) << 8; + fvco += (xt->freq * (wbfrac >> 10)) >> 2; + fvco += (xt->freq * (wbfrac & 0x3ff)) >> 10; + fvco >>= 8; + fvco >>= pdiv; + fvco /= 1000; + fvco *= 1000; + + PMU_MSG (("sb_pmu0_cpuclk0: wbint %u wbfrac %u fvco %u\n", + wbint, wbfrac, fvco)); + ASSERT (fvco == PMU0_PLL0_FVCO); +#endif /* BCMDBG */ + + /* Return ARM/SB clock */ + return PMU0_PLL0_FVCO / (divarm + PMU0_PLL0_PC0_DIV_ARM_BASE) * 1000; +} + +/* PMU corerev 1 pll programming for BCM4325 */ +/* setup pll and query clock speed */ +typedef struct +{ + uint16 fref; + uint8 xf; + uint8 p1div; + uint8 p2div; + uint8 ndiv_int; + uint32 ndiv_frac; +} pmu1_xtaltab0_t; + +/* the following table is based on 880Mhz Fvco */ +#define PMU1_PLL0_FVCO 880000 /* Fvco 880Mhz */ +static const pmu1_xtaltab0_t +BCMINITDATA (pmu1_xtaltab0)[] = +{ + { + 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, 9, 0x5, 0x17B425}, + { + 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}, + { + 38400, 14, 1, 5, 0x4, 0x955555}, + { + 40000, 15, 1, 2, 0xb, 0}, + { + 0, 0, 0, 0, 0, 0} +}; + +/* Default to 15360Khz crystal */ +#define PMU1_XTAL0_DEFAULT 3 + +static uint32 +BCMINITFN (sb_pmu1_alpclk0) (sb_t * sbh, osl_t * osh, chipcregs_t * cc) +{ + const pmu1_xtaltab0_t *xt; + uint32 xf; + + /* Find the frequency in the table */ + xf = (R_REG (osh, &cc->pmucontrol) & PCTL_XTALFREQ_MASK) >> + PCTL_XTALFREQ_SHIFT; + for (xt = pmu1_xtaltab0; xt->fref; xt++) + if (xt->xf == xf) + break; + if (xt->fref == 0) + xt = &pmu1_xtaltab0[PMU1_XTAL0_DEFAULT]; + + return xt->fref * 1000; +} + +/* Set up PLL registers in the PMU as per the crystal speed. + * Uses xtalfreq variable, or passed-in default. + */ +static void +BCMINITFN (sb_pmu1_pllinit0) (sb_t * sbh, osl_t * osh, chipcregs_t * cc, + uint32 xtal) +{ + const pmu1_xtaltab0_t *xt; + uint32 tmp; + uint32 buf_strength = 0; + + /* 4312: assume default works */ + if (sbh->chip == BCM4312_CHIP_ID) + return; + + /* Find the frequency in the table */ + for (xt = pmu1_xtaltab0; xt->fref; xt++) + if (xt->fref == xtal) + break; + if (xt->fref == 0) + xt = &pmu1_xtaltab0[PMU1_XTAL0_DEFAULT]; + + PMU_MSG (("XTAL %d (%d)\n", xtal, xt->xf)); + + /* Check current PLL state */ + if (((R_REG (osh, &cc->pmucontrol) & PCTL_XTALFREQ_MASK) >> + PCTL_XTALFREQ_SHIFT) == xt->xf) + { + PMU_MSG (("PLL already programmed for %d.%d MHz\n", + (xt->fref / 1000), (xt->fref % 1000))); + return; + } + + PMU_MSG (("Programming PLL for %d.%d MHz\n", (xt->fref / 1000), + (xt->fref % 1000))); + + /* Make sure the PLL is off */ + switch (sbh->chip) + { + case BCM4325_CHIP_ID: + AND_REG (osh, &cc->min_res_mask, + ~(PMURES_BIT (RES4325_BBPLL_PWRSW_PU) | + PMURES_BIT (RES4325_HT_AVAIL))); + AND_REG (osh, &cc->max_res_mask, + ~(PMURES_BIT (RES4325_BBPLL_PWRSW_PU) | + PMURES_BIT (RES4325_HT_AVAIL))); + + /* Change the BBPLL drive strength to 2 for all channels */ + buf_strength = 0x222222; + break; + default: + ASSERT (0); + } + SPINWAIT (R_REG (osh, &cc->clk_ctl_st) & CCS_HTAVAIL, + PMU_MAX_TRANSITION_DLY); + ASSERT (!(R_REG (osh, &cc->clk_ctl_st) & CCS_HTAVAIL)); + + PMU_MSG (("Done masking\n")); + + /* Write p1div and p2div to pllcontrol[0] */ + W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL0); + tmp = R_REG (osh, &cc->pllcontrol_data) & + ~(PMU1_PLL0_PC0_P1DIV_MASK | PMU1_PLL0_PC0_P2DIV_MASK); + tmp |= + ((xt-> + p1div << PMU1_PLL0_PC0_P1DIV_SHIFT) & PMU1_PLL0_PC0_P1DIV_MASK) | ((xt-> + p2div + << + PMU1_PLL0_PC0_P2DIV_SHIFT) + & + PMU1_PLL0_PC0_P2DIV_MASK); + W_REG (osh, &cc->pllcontrol_data, tmp); + + /* Write ndiv_int and ndiv_mode to pllcontrol[2] */ + W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL2); + tmp = R_REG (osh, &cc->pllcontrol_data) & + ~(PMU1_PLL0_PC2_NDIV_INT_MASK | PMU1_PLL0_PC2_NDIV_MODE_MASK); + tmp |= + ((xt-> + ndiv_int << PMU1_PLL0_PC2_NDIV_INT_SHIFT) & PMU1_PLL0_PC2_NDIV_INT_MASK) + | ((1 << PMU1_PLL0_PC2_NDIV_MODE_SHIFT) & PMU1_PLL0_PC2_NDIV_MODE_MASK); + W_REG (osh, &cc->pllcontrol_data, tmp); + + /* Write ndiv_frac to pllcontrol[3] */ + W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL3); + tmp = R_REG (osh, &cc->pllcontrol_data) & ~PMU1_PLL0_PC3_NDIV_FRAC_MASK; + tmp |= ((xt->ndiv_frac << PMU1_PLL0_PC3_NDIV_FRAC_SHIFT) & + PMU1_PLL0_PC3_NDIV_FRAC_MASK); + W_REG (osh, &cc->pllcontrol_data, tmp); + + if (buf_strength) + { + PMU_MSG (("Adjusting PLL buffer drive strength: %x\n", buf_strength)); + + W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL5); + tmp = R_REG (osh, &cc->pllcontrol_data) & ~PMU1_PLL0_PC5_CLK_DRV_MASK; + tmp |= (buf_strength << PMU1_PLL0_PC5_CLK_DRV_SHIFT); + W_REG (osh, &cc->pllcontrol_data, tmp); + } + + PMU_MSG (("Done pll\n")); + + /* Write XtalFreq. Set the divisor also. */ + tmp = R_REG (osh, &cc->pmucontrol) & + ~(PCTL_ILP_DIV_MASK | PCTL_XTALFREQ_MASK); + tmp |= (((((xt->fref + 127) / 128) - 1) << PCTL_ILP_DIV_SHIFT) & + PCTL_ILP_DIV_MASK) | + ((xt->xf << PCTL_XTALFREQ_SHIFT) & PCTL_XTALFREQ_MASK); + W_REG (osh, &cc->pmucontrol, tmp); +} + + +static uint32 +BCMINITFN (sb_pmu1_cpuclk0) (sb_t * sbh, osl_t * osh, chipcregs_t * cc) +{ + const pmu1_xtaltab0_t *xt; + uint32 xf, tmp, m1div; +#ifdef BCMDBG + uint32 ndiv_int, ndiv_frac, p2div, p1div, fvco; +#endif + + /* Find the xtal frequency in the table */ + xf = (R_REG (osh, &cc->pmucontrol) & PCTL_XTALFREQ_MASK) >> + PCTL_XTALFREQ_SHIFT; + for (xt = pmu1_xtaltab0; xt->fref; xt++) + if (xt->xf == xf) + break; + if (xt->fref == 0) + xt = &pmu1_xtaltab0[PMU1_XTAL0_DEFAULT]; + + /* Read m1div from pllcontrol[1] */ + W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL1); + tmp = R_REG (osh, &cc->pllcontrol_data); + m1div = (tmp & PMU1_PLL0_PC1_M1DIV_MASK) >> PMU1_PLL0_PC1_M1DIV_SHIFT; + +#ifdef BCMDBG + /* Read p2div/p1div from pllcontrol[0] */ + W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL0); + tmp = R_REG (osh, &cc->pllcontrol_data); + p2div = (tmp & PMU1_PLL0_PC0_P2DIV_MASK) >> PMU1_PLL0_PC0_P2DIV_SHIFT; + p1div = (tmp & PMU1_PLL0_PC0_P1DIV_MASK) >> PMU1_PLL0_PC0_P1DIV_SHIFT; + + /* Calculate Fvco based on xtal freq and ndiv and pdiv */ + W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL2); + tmp = R_REG (osh, &cc->pllcontrol_data); + ndiv_int = + (tmp & PMU1_PLL0_PC2_NDIV_INT_MASK) >> PMU1_PLL0_PC2_NDIV_INT_SHIFT; + + W_REG (osh, &cc->pllcontrol_addr, PMU1_PLL0_PLLCTL3); + tmp = R_REG (osh, &cc->pllcontrol_data); + ndiv_frac = + (tmp & PMU1_PLL0_PC3_NDIV_FRAC_MASK) >> PMU1_PLL0_PC3_NDIV_FRAC_SHIFT; + + fvco = (xt->fref * ndiv_int) << 8; + fvco += (xt->fref * (ndiv_frac >> 12)) >> 4; + fvco += (xt->fref * (ndiv_frac & 0xfff)) >> 12; + fvco >>= 8; + fvco *= p2div; + fvco /= p1div; + fvco /= 1000; + fvco *= 1000; + + PMU_MSG (("sb_pmu0_cpuclk0: ndiv_int %u ndiv_frac %u " + "p2div %u p1div %u fvco %u\n", + ndiv_int, ndiv_frac, p2div, p1div, fvco)); + ASSERT (fvco == PMU1_PLL0_FVCO); +#endif /* BCMDBG */ + + /* Return ARM/SB clock */ + return PMU1_PLL0_FVCO / m1div * 1000; +} + +void BCMINITFN (sb_pmu_pll_init) (sb_t * sbh, osl_t * osh, uint xtalfreq) +{ + chipcregs_t *cc; + uint origidx; + + ASSERT (sbh->cccaps & CC_CAP_PMU); + + /* Remember original core before switch to chipc */ + origidx = sb_coreidx (sbh); + cc = sb_setcore (sbh, SB_CC, 0); + ASSERT (cc); + + switch (sbh->chip) + { + case BCM4328_CHIP_ID: + sb_pmu0_pllinit0 (sbh, osh, cc, xtalfreq); + break; + case BCM5354_CHIP_ID: + sb_pmu0_pllinit0 (sbh, osh, cc, xtalfreq); + break; + case BCM4325_CHIP_ID: + sb_pmu1_pllinit0 (sbh, osh, cc, xtalfreq); + break; + case BCM4312_CHIP_ID: + sb_pmu1_pllinit0 (sbh, osh, cc, xtalfreq); + break; + default: + PMU_MSG (("No PLL init done for chip %x rev %d pmurev %d\n", + sbh->chip, sbh->chiprev, sbh->pmurev)); + break; + } + + /* Return to original core */ + sb_setcoreidx (sbh, origidx); +} + +uint32 BCMINITFN (sb_pmu_alp_clock) (sb_t * sbh, osl_t * osh) +{ + chipcregs_t *cc; + uint origidx; + uint32 clock = ALP_CLOCK; + + ASSERT (sbh->cccaps & CC_CAP_PMU); + + /* Remember original core before switch to chipc */ + origidx = sb_coreidx (sbh); + cc = sb_setcore (sbh, SB_CC, 0); + ASSERT (cc); + + switch (sbh->chip) + { + case BCM4328_CHIP_ID: + clock = sb_pmu0_alpclk0 (sbh, osh, cc); + break; + case BCM5354_CHIP_ID: + clock = sb_pmu0_alpclk0 (sbh, osh, cc); + break; + case BCM4325_CHIP_ID: + clock = sb_pmu1_alpclk0 (sbh, osh, cc); + break; + case BCM4312_CHIP_ID: + clock = sb_pmu1_alpclk0 (sbh, osh, cc); + /* always 20Mhz */ + clock = 20000 * 1000; + break; + default: + PMU_MSG (("No ALP clock specified " + "for chip %x rev %d pmurev %d, using default %d Hz\n", + sbh->chip, sbh->chiprev, sbh->pmurev, clock)); + break; + } + + /* Return to original core */ + sb_setcoreidx (sbh, origidx); + return clock; +} + +uint BCMINITFN (sb_pmu_cpu_clock) (sb_t * sbh, osl_t * osh) +{ + chipcregs_t *cc; + uint origidx; + uint32 clock = HT_CLOCK; + + ASSERT (sbh->cccaps & CC_CAP_PMU); + + /* Remember original core before switch to chipc */ + origidx = sb_coreidx (sbh); + cc = sb_setcore (sbh, SB_CC, 0); + ASSERT (cc); + + switch (sbh->chip) + { + case BCM4328_CHIP_ID: + clock = sb_pmu0_cpuclk0 (sbh, osh, cc); + break; + case BCM5354_CHIP_ID: + clock = sb_pmu0_cpuclk0 (sbh, osh, cc); + break; + case BCM4325_CHIP_ID: + clock = sb_pmu1_cpuclk0 (sbh, osh, cc); + break; + case BCM4312_CHIP_ID: + clock = sb_pmu1_cpuclk0 (sbh, osh, cc); + break; + default: + PMU_MSG (("No CPU clock specified " + "for chip %x rev %d pmurev %d, using default %d Hz\n", + sbh->chip, sbh->chiprev, sbh->pmurev, clock)); + break; + } + + /* Return to original core */ + sb_setcoreidx (sbh, origidx); + return clock; +} + +void BCMINITFN (sb_pmu_init) (sb_t * sbh, osl_t * osh) +{ + chipcregs_t *cc; + uint origidx; + + ASSERT (sbh->cccaps & CC_CAP_PMU); + + /* Remember original core before switch to chipc */ + origidx = sb_coreidx (sbh); + cc = sb_setcore (sbh, SB_CC, 0); + ASSERT (cc); + + if (sbh->pmurev >= 1) + { + if (sbh->chip == BCM4325_CHIP_ID && sbh->chiprev <= 1) + AND_REG (osh, &cc->pmucontrol, ~PCTL_NOILP_ON_WAIT); + else + OR_REG (osh, &cc->pmucontrol, PCTL_NOILP_ON_WAIT); + } + + /* Return to original core */ + sb_setcoreidx (sbh, origidx); +} + +void BCMINITFN (sb_pmu_otp_power) (sb_t * sbh, osl_t * osh, bool on) +{ + chipcregs_t *cc; + uint origidx; + + ASSERT (sbh->cccaps & CC_CAP_PMU); + + /* Remember original core before switch to chipc */ + origidx = sb_coreidx (sbh); + cc = sb_setcore (sbh, SB_CC, 0); + ASSERT (cc); + + switch (sbh->chip) + { + case BCM4325_CHIP_ID: + if (on) + { + OR_REG (osh, &cc->min_res_mask, PMURES_BIT (RES4325_LNLDO2_PU)); + if (sbh->boardflags & BFL_BUCKBOOST) + AND_REG (osh, &cc->min_res_mask, + ~PMURES_BIT (RES4325_BUCK_BOOST_PWM)); + OSL_DELAY (500); + } + else + { + if (sbh->boardflags & BFL_BUCKBOOST) + OR_REG (osh, &cc->min_res_mask, + PMURES_BIT (RES4325_BUCK_BOOST_PWM)); + AND_REG (osh, &cc->min_res_mask, ~PMURES_BIT (RES4325_LNLDO2_PU)); + } + break; + default: + break; + } + + /* Return to original core */ + sb_setcoreidx (sbh, origidx); +} + +void +sb_pmu_rcal (sb_t * sbh, osl_t * osh) +{ + chipcregs_t *cc; + uint origidx; + + ASSERT (sbh->cccaps & CC_CAP_PMU); + + /* Remember original core before switch to chipc */ + origidx = sb_coreidx (sbh); + cc = sb_setcore (sbh, SB_CC, 0); + ASSERT (cc); + + switch (sbh->chip) + { + case BCM4325_CHIP_ID: + { + uint8 rcal_code; + uint32 val; + + /* Kick RCal */ + W_REG (osh, &cc->chipcontrol_addr, 1); + AND_REG (osh, &cc->chipcontrol_data, ~0x04); + OR_REG (osh, &cc->chipcontrol_data, 0x04); + + /* Wait for completion */ + SPINWAIT (0 == (R_REG (osh, &cc->chipstatus) & 0x08), + 10 * 1000 * 1000); + ASSERT (R_REG (osh, &cc->chipstatus) & 0x08); + + /* Drop the LSB to convert from 5 bit code to 4 bit code */ + rcal_code = (uint8) (R_REG (osh, &cc->chipstatus) >> 5) & 0x0f; + PMU_MSG (("RCal completed, status 0x%x, code 0x%x\n", + R_REG (osh, &cc->chipstatus), rcal_code)); + + /* Write RCal code into pmu_vreg_ctrl[32:29] */ + W_REG (osh, &cc->regcontrol_addr, 0); + val = R_REG (osh, &cc->regcontrol_data) & ~((uint32) 0x07 << 29); + val |= (uint32) (rcal_code & 0x07) << 29; + W_REG (osh, &cc->regcontrol_data, val); + W_REG (osh, &cc->regcontrol_addr, 1); + val = R_REG (osh, &cc->regcontrol_data) & ~(uint32) 0x01; + val |= (uint32) ((rcal_code >> 3) & 0x01); + W_REG (osh, &cc->regcontrol_data, val); + + /* Write RCal code into pmu_chip_ctrl[33:30] */ + W_REG (osh, &cc->chipcontrol_addr, 0); + val = R_REG (osh, &cc->chipcontrol_data) & ~((uint32) 0x03 << 30); + val |= (uint32) (rcal_code & 0x03) << 30; + W_REG (osh, &cc->chipcontrol_data, val); + W_REG (osh, &cc->chipcontrol_addr, 1); + val = R_REG (osh, &cc->chipcontrol_data) & ~(uint32) 0x03; + val |= (uint32) ((rcal_code >> 2) & 0x03); + W_REG (osh, &cc->chipcontrol_data, val); + + /* Set override in pmu_chip_ctrl[29] */ + W_REG (osh, &cc->chipcontrol_addr, 0); + OR_REG (osh, &cc->chipcontrol_data, (0x01 << 29)); + + /* Power off RCal block */ + W_REG (osh, &cc->chipcontrol_addr, 1); + AND_REG (osh, &cc->chipcontrol_data, ~0x04); + + break; + } + default: + break; + } + + /* Return to original core */ + sb_setcoreidx (sbh, origidx); +} |