/* * 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: hndpmu.c,v 1.1.1.1 2008/07/21 09:20:53 james26_jang Exp $ */ #include #include #include #include #include #include #include #include #include /* debug/trace */ #define PMU_ERROR(args) #define PMU_MSG(args) /* 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); #if defined(BCM4325) || defined(BCM4312) /* 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); #endif /* 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; #if defined(BCM4312) 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; #endif /* defined(BCM4312) */ 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) { #if defined(BCM4328) case BCM4328_CHIP_ID: ldo = RES4328_PA_REF_LDO; break; #endif /* defined(BCM4328) */ case BCM5354_CHIP_ID: ldo = RES5354_PA_REF_LDO; break; #if defined(BCM4312) case BCM4312_CHIP_ID: ldo = RES4312_PA_REF_LDO; break; #endif /* defined(BCM4312) */ 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) { #if defined(BCM4328) case BCM4328_CHIP_ID: delay = 7000; break; #endif /* BCM4328 */ #if defined(BCM4325) || defined(BCM4312) case BCM4325_CHIP_ID: case BCM4312_CHIP_ID: #ifdef BCMQT delay = 70; #else delay = 2800; #endif break; #endif /* BCM4325 || BCM4312 */ 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; #if defined(BCM4328) 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) } }; #endif /* BCM4328 */ #if defined(BCM4325) #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) } }; #endif /* BCM4325 */ 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) { #if defined(BCM4328) 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); /* 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; #endif /* BCM4328 */ #if defined(BCM4312) 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; #endif /* BCM4312 */ case BCM5354_CHIP_ID: /* Allow (but don't require) PLL to turn on */ max_mask = 0xfffff; break; #if defined(BCM4325) 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; #endif /* BCM4325 */ 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 #if defined(BCM4328) #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 */ #endif /* BCM4328 */ /* 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))); #if defined(BCM4328) #ifdef BCMUSBDEV if (sbh->chip == BCM4328_CHIP_ID) sb_pmu0_sbclk4328(sbh, PMU0_PLL0_PC0_DIV_ARM_88MHZ); #endif #endif /* BCM4328 */ 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) { #if defined(BCM4328) 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; #endif 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; 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; /* Return ARM/SB clock */ return PMU0_PLL0_FVCO / (divarm + PMU0_PLL0_PC0_DIV_ARM_BASE) * 1000; } /* PMU corerev 1 pll programming for BCM4325 */ #if defined(BCM4325) || defined(BCM4312) /* 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) { #if defined(BCM4325) 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; #endif 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; /* 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; /* Return ARM/SB clock */ return PMU1_PLL0_FVCO / m1div * 1000; } #endif 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) { #if defined(BCM4328) case BCM4328_CHIP_ID: sb_pmu0_pllinit0(sbh, osh, cc, xtalfreq); break; #endif case BCM5354_CHIP_ID: sb_pmu0_pllinit0(sbh, osh, cc, xtalfreq); break; #if defined(BCM4325) case BCM4325_CHIP_ID: sb_pmu1_pllinit0(sbh, osh, cc, xtalfreq); break; #endif #if defined(BCM4312) case BCM4312_CHIP_ID: sb_pmu1_pllinit0(sbh, osh, cc, xtalfreq); break; #endif 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) { #if defined(BCM4328) case BCM4328_CHIP_ID: clock = sb_pmu0_alpclk0(sbh, osh, cc); break; #endif case BCM5354_CHIP_ID: clock = sb_pmu0_alpclk0(sbh, osh, cc); break; #if defined(BCM4325) case BCM4325_CHIP_ID: clock = sb_pmu1_alpclk0(sbh, osh, cc); break; #endif #if defined(BCM4312) case BCM4312_CHIP_ID: clock = sb_pmu1_alpclk0(sbh, osh, cc); /* always 20Mhz */ clock = 20000 * 1000; break; #endif 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) { #if defined(BCM4328) case BCM4328_CHIP_ID: clock = sb_pmu0_cpuclk0(sbh, osh, cc); break; #endif case BCM5354_CHIP_ID: clock = sb_pmu0_cpuclk0(sbh, osh, cc); break; #if defined(BCM4325) case BCM4325_CHIP_ID: clock = sb_pmu1_cpuclk0(sbh, osh, cc); break; #endif #if defined(BCM4312) case BCM4312_CHIP_ID: clock = sb_pmu1_cpuclk0(sbh, osh, cc); break; #endif 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 defined(BCM4325) if (sbh->chip == BCM4325_CHIP_ID && sbh->chiprev <= 1) AND_REG(osh, &cc->pmucontrol, ~PCTL_NOILP_ON_WAIT); else #endif /* BCM4325 */ 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) { #if defined(BCM4325) 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; #endif /* BCM4325 */ 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) { #if defined(BCM4325) 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; } #endif /* BCM4325 */ default: break; } /* Return to original core */ sb_setcoreidx(sbh, origidx); }