mirror of https://github.com/hak5/openwrt.git
1257 lines
32 KiB
C
1257 lines
32 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
#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);
|
|
}
|