#include <sys/types.h>
#include <dev/bhnd/bhndvar.h>
#include <dev/bhnd/cores/chipc/chipc.h>
#include <dev/bhnd/cores/chipc/chipcreg.h>
#include <dev/bhnd/bcma/bcma_dmp.h>
#include "bhnd_nvram_map.h"
#include "bhnd_pmureg.h"
#include "bhnd_pmuvar.h"
#include "bhnd_pmu_private.h"
#define PMU_LOG(_sc, _fmt, ...) do { \
if (_sc->dev != NULL) \
device_printf(_sc->dev, _fmt, ##__VA_ARGS__); \
else \
printf(_fmt, ##__VA_ARGS__); \
} while (0)
#ifdef BCMDBG
#define PMU_DEBUG(_sc, _fmt, ...) PMU_LOG(_sc, _fmt, ##__VA_ARGS__)
#else
#define PMU_DEBUG(_sc, _fmt, ...)
#endif
typedef struct pmu0_xtaltab0 pmu0_xtaltab0_t;
typedef struct pmu1_xtaltab0 pmu1_xtaltab0_t;
static const pmu1_xtaltab0_t *bhnd_pmu1_xtaltab0(struct bhnd_pmu_query *sc);
static const pmu1_xtaltab0_t *bhnd_pmu1_xtaldef0(struct bhnd_pmu_query *sc);
static void bhnd_pmu0_pllinit0(struct bhnd_pmu_softc *sc, uint32_t xtal);
static uint32_t bhnd_pmu0_cpuclk0(struct bhnd_pmu_query *sc);
static uint32_t bhnd_pmu0_alpclk0(struct bhnd_pmu_query *sc);
static void bhnd_pmu1_pllinit0(struct bhnd_pmu_softc *sc, uint32_t xtal);
static uint32_t bhnd_pmu1_pllfvco0(struct bhnd_pmu_query *sc);
static uint32_t bhnd_pmu1_cpuclk0(struct bhnd_pmu_query *sc);
static uint32_t bhnd_pmu1_alpclk0(struct bhnd_pmu_query *sc);
static uint32_t bhnd_pmu5_clock(struct bhnd_pmu_query *sc, u_int pll0, u_int m);
static uint32_t bhnd_pmu6_4706_clock(struct bhnd_pmu_query *sc, u_int pll0,
u_int m);
static bool bhnd_pmu_res_depfltr_bb(struct bhnd_pmu_softc *sc);
static bool bhnd_pmu_res_depfltr_ncb(struct bhnd_pmu_softc *sc);
static bool bhnd_pmu_res_depfltr_paldo(struct bhnd_pmu_softc *sc);
static bool bhnd_pmu_res_depfltr_npaldo(struct bhnd_pmu_softc *sc);
static uint32_t bhnd_pmu_res_deps(struct bhnd_pmu_softc *sc, uint32_t rsrcs,
bool all);
static int bhnd_pmu_res_uptime(struct bhnd_pmu_softc *sc, uint8_t rsrc,
uint32_t *uptime);
static int bhnd_pmu_res_masks(struct bhnd_pmu_softc *sc, uint32_t *pmin,
uint32_t *pmax);
static int bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc,
bhnd_pmu_spuravoid spuravoid);
static void bhnd_pmu_set_4330_plldivs(struct bhnd_pmu_softc *sc);
#define BHND_PMU_REV(_sc) \
((uint8_t)BHND_PMU_GET_BITS((_sc)->caps, BHND_PMU_CAP_REV))
#define PMU_WAIT_CLKST(_sc, _val, _mask) \
bhnd_core_clkctl_wait((_sc)->clkctl, (_val), (_mask))
#define PMURES_BIT(_bit) \
(1 << (BHND_PMU_ ## _bit))
#define PMU_CST4330_SDIOD_CHIPMODE(_sc) \
CHIPC_CST4330_CHIPMODE_SDIOD((_sc)->io->rd_chipst((_sc)->io_ctx))
int
bhnd_pmu_query_init(struct bhnd_pmu_query *query, device_t dev,
struct bhnd_chipid id, const struct bhnd_pmu_io *io, void *ctx)
{
query->dev = dev;
query->io = io;
query->io_ctx = ctx;
query->cid = id;
query->caps = BHND_PMU_READ_4(query, BHND_PMU_CAP);
return (0);
}
void
bhnd_pmu_query_fini(struct bhnd_pmu_query *query)
{
}
uint32_t
bhnd_pmu_ind_read(const struct bhnd_pmu_io *io, void *io_ctx, bus_size_t addr,
bus_size_t data, uint32_t reg)
{
io->wr4(addr, reg, io_ctx);
return (io->rd4(data, io_ctx));
}
void
bhnd_pmu_ind_write(const struct bhnd_pmu_io *io, void *io_ctx, bus_size_t addr,
bus_size_t data, uint32_t reg, uint32_t val, uint32_t mask)
{
uint32_t rval;
io->wr4(addr, reg, io_ctx);
if (mask != UINT32_MAX) {
rval = io->rd4(data, io_ctx);
rval &= ~mask | (val & mask);
} else {
rval = val;
}
io->wr4(data, rval, io_ctx);
}
void
bhnd_pmu_set_switcher_voltage(struct bhnd_pmu_softc *sc, uint8_t bb_voltage,
uint8_t rf_voltage)
{
BHND_PMU_REGCTRL_WRITE(sc, 0x01, (bb_voltage & 0x1f) << 22, ~0);
BHND_PMU_REGCTRL_WRITE(sc, 0x00, (rf_voltage & 0x1f) << 14, ~0);
}
int
bhnd_pmu_set_ldo_voltage(struct bhnd_pmu_softc *sc, uint8_t ldo,
uint8_t voltage)
{
uint32_t chipst;
uint32_t regctrl;
uint8_t shift;
uint8_t mask;
uint8_t addr;
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4328:
case BHND_CHIPID_BCM5354:
switch (ldo) {
case SET_LDO_VOLTAGE_LDO1:
addr = 2;
shift = 17 + 8;
mask = 0xf;
break;
case SET_LDO_VOLTAGE_LDO2:
addr = 3;
shift = 1;
mask = 0xf;
break;
case SET_LDO_VOLTAGE_LDO3:
addr = 3;
shift = 9;
mask = 0xf;
break;
case SET_LDO_VOLTAGE_PAREF:
addr = 3;
shift = 17;
mask = 0x3f;
break;
default:
PMU_LOG(sc, "unknown BCM4328/BCM5354 LDO %hhu\n", ldo);
return (ENODEV);
}
break;
case BHND_CHIPID_BCM4312:
switch (ldo) {
case SET_LDO_VOLTAGE_PAREF:
addr = 0;
shift = 21;
mask = 0x3f;
break;
default:
PMU_LOG(sc, "unknown BCM4312 LDO %hhu\n", ldo);
return (ENODEV);
}
break;
case BHND_CHIPID_BCM4325:
switch (ldo) {
case SET_LDO_VOLTAGE_CLDO_PWM:
addr = 5;
shift = 9;
mask = 0xf;
break;
case SET_LDO_VOLTAGE_CLDO_BURST:
addr = 5;
shift = 13;
mask = 0xf;
break;
case SET_LDO_VOLTAGE_CBUCK_PWM:
addr = 3;
shift = 20;
mask = 0x1f;
chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev);
if (BHND_PMU_GET_BITS(chipst, CHIPC_CST4325_PMUTOP_2B))
voltage ^= 0x9;
break;
case SET_LDO_VOLTAGE_CBUCK_BURST:
addr = 3;
shift = 25;
mask = 0x1f;
chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev);
if (BHND_PMU_GET_BITS(chipst, CHIPC_CST4325_PMUTOP_2B))
voltage ^= 0x9;
break;
case SET_LDO_VOLTAGE_LNLDO1:
addr = 5;
shift = 17;
mask = 0x1f;
break;
case SET_LDO_VOLTAGE_LNLDO2_SEL:
addr = 6;
shift = 0;
mask = 0x1;
break;
default:
PMU_LOG(sc, "unknown BCM4325 LDO %hhu\n", ldo);
return (ENODEV);
}
break;
case BHND_CHIPID_BCM4336:
switch (ldo) {
case SET_LDO_VOLTAGE_CLDO_PWM:
addr = 4;
shift = 1;
mask = 0xf;
break;
case SET_LDO_VOLTAGE_CLDO_BURST:
addr = 4;
shift = 5;
mask = 0xf;
break;
case SET_LDO_VOLTAGE_LNLDO1:
addr = 4;
shift = 17;
mask = 0xf;
break;
default:
PMU_LOG(sc, "unknown BCM4336 LDO %hhu\n", ldo);
return (ENODEV);
}
break;
case BHND_CHIPID_BCM4330:
switch (ldo) {
case SET_LDO_VOLTAGE_CBUCK_PWM:
addr = 3;
shift = 0;
mask = 0x1f;
break;
default:
PMU_LOG(sc, "unknown BCM4330 LDO %hhu\n", ldo);
return (ENODEV);
}
break;
case BHND_CHIPID_BCM4331:
switch (ldo) {
case SET_LDO_VOLTAGE_PAREF:
addr = 1;
shift = 0;
mask = 0xf;
break;
default:
PMU_LOG(sc, "unknown BCM4331 LDO %hhu\n", ldo);
return (ENODEV);
}
break;
default:
PMU_LOG(sc, "cannot set LDO voltage on unsupported chip %hu\n",
sc->cid.chip_id);
return (ENODEV);
}
regctrl = (voltage & mask) << shift;
BHND_PMU_REGCTRL_WRITE(sc, addr, regctrl, mask << shift);
return (0);
}
#define D11SCC_SLOW2FAST_TRANSITION 2
int
bhnd_pmu_fast_pwrup_delay(struct bhnd_pmu_softc *sc, u_int *pwrup_delay)
{
uint32_t ilp;
uint32_t uptime;
u_int delay;
int error;
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM43224:
case BHND_CHIPID_BCM43225:
case BHND_CHIPID_BCM43421:
case BHND_CHIPID_BCM43235:
case BHND_CHIPID_BCM43236:
case BHND_CHIPID_BCM43238:
case BHND_CHIPID_BCM4331:
case BHND_CHIPID_BCM6362:
case BHND_CHIPID_BCM4313:
delay = 3700;
break;
case BHND_CHIPID_BCM4325:
error = bhnd_pmu_res_uptime(sc, BHND_PMU_RES4325_HT_AVAIL,
&uptime);
if (error)
return (error);
ilp = bhnd_pmu_ilp_clock(&sc->query);
delay = (uptime + D11SCC_SLOW2FAST_TRANSITION) *
((1000000 + ilp - 1) / ilp);
delay = (11 * delay) / 10;
break;
case BHND_CHIPID_BCM4329:
error = bhnd_pmu_res_uptime(sc, BHND_PMU_RES4329_HT_AVAIL,
&uptime);
if (error)
return (error);
ilp = bhnd_pmu_ilp_clock(&sc->query);
delay = (uptime + D11SCC_SLOW2FAST_TRANSITION) *
((1000000 + ilp - 1) / ilp);
delay = (11 * delay) / 10;
break;
case BHND_CHIPID_BCM4319:
delay = 3700;
break;
case BHND_CHIPID_BCM4336:
error = bhnd_pmu_res_uptime(sc, BHND_PMU_RES4336_HT_AVAIL,
&uptime);
if (error)
return (error);
ilp = bhnd_pmu_ilp_clock(&sc->query);
delay = (uptime + D11SCC_SLOW2FAST_TRANSITION) *
((1000000 + ilp - 1) / ilp);
delay = (11 * delay) / 10;
break;
case BHND_CHIPID_BCM4330:
error = bhnd_pmu_res_uptime(sc, BHND_PMU_RES4330_HT_AVAIL,
&uptime);
if (error)
return (error);
ilp = bhnd_pmu_ilp_clock(&sc->query);
delay = (uptime + D11SCC_SLOW2FAST_TRANSITION) *
((1000000 + ilp - 1) / ilp);
delay = (11 * delay) / 10;
break;
default:
delay = BHND_PMU_MAX_TRANSITION_DLY;
break;
}
*pwrup_delay = delay;
return (0);
}
uint32_t
bhnd_pmu_force_ilp(struct bhnd_pmu_softc *sc, bool force)
{
uint32_t orig;
uint32_t pctrl;
pctrl = BHND_PMU_READ_4(sc, BHND_PMU_CTRL);
orig = pctrl;
if (force)
pctrl &= ~(BHND_PMU_CTRL_HT_REQ_EN | BHND_PMU_CTRL_ALP_REQ_EN);
else
pctrl |= (BHND_PMU_CTRL_HT_REQ_EN | BHND_PMU_CTRL_ALP_REQ_EN);
BHND_PMU_WRITE_4(sc, BHND_PMU_CTRL, pctrl);
return (orig);
}
typedef struct {
uint8_t resnum;
uint16_t updown;
} pmu_res_updown_t;
typedef bool (*pmu_res_filter) (struct bhnd_pmu_softc *sc);
typedef struct {
uint32_t res_mask;
int8_t action;
uint32_t depend_mask;
pmu_res_filter filter;
} pmu_res_depend_t;
#define RES_DEPEND_SET 0
#define RES_DEPEND_ADD 1
#define RES_DEPEND_REMOVE -1
static const pmu_res_updown_t bcm4328a0_res_updown[] = {
{
BHND_PMU_RES4328_EXT_SWITCHER_PWM, 0x0101}, {
BHND_PMU_RES4328_BB_SWITCHER_PWM, 0x1f01}, {
BHND_PMU_RES4328_BB_SWITCHER_BURST, 0x010f}, {
BHND_PMU_RES4328_BB_EXT_SWITCHER_BURST, 0x0101}, {
BHND_PMU_RES4328_ILP_REQUEST, 0x0202}, {
BHND_PMU_RES4328_RADIO_SWITCHER_PWM, 0x0f01}, {
BHND_PMU_RES4328_RADIO_SWITCHER_BURST, 0x0f01}, {
BHND_PMU_RES4328_ROM_SWITCH, 0x0101}, {
BHND_PMU_RES4328_PA_REF_LDO, 0x0f01}, {
BHND_PMU_RES4328_RADIO_LDO, 0x0f01}, {
BHND_PMU_RES4328_AFE_LDO, 0x0f01}, {
BHND_PMU_RES4328_PLL_LDO, 0x0f01}, {
BHND_PMU_RES4328_BG_FILTBYP, 0x0101}, {
BHND_PMU_RES4328_TX_FILTBYP, 0x0101}, {
BHND_PMU_RES4328_RX_FILTBYP, 0x0101}, {
BHND_PMU_RES4328_XTAL_PU, 0x0101}, {
BHND_PMU_RES4328_XTAL_EN, 0xa001}, {
BHND_PMU_RES4328_BB_PLL_FILTBYP, 0x0101}, {
BHND_PMU_RES4328_RF_PLL_FILTBYP, 0x0101}, {
BHND_PMU_RES4328_BB_PLL_PU, 0x0701}
};
static const pmu_res_depend_t bcm4328a0_res_depend[] = {
{
PMURES_BIT(RES4328_ILP_REQUEST),
RES_DEPEND_SET,
PMURES_BIT(RES4328_EXT_SWITCHER_PWM) |
PMURES_BIT(RES4328_BB_SWITCHER_PWM), NULL}
};
static const pmu_res_updown_t bcm4325a0_res_updown[] = {
{
BHND_PMU_RES4325_XTAL_PU, 0x1501}
};
static const pmu_res_depend_t bcm4325a0_res_depend[] = {
{
PMURES_BIT(RES4325_OTP_PU),
RES_DEPEND_REMOVE,
PMURES_BIT(RES4325_BUCK_BOOST_BURST), NULL},
{
PMURES_BIT(RES4325_ALP_AVAIL) | PMURES_BIT(RES4325_HT_AVAIL),
RES_DEPEND_ADD,
PMURES_BIT(RES4325_BUCK_BOOST_BURST) |
PMURES_BIT(RES4325_BUCK_BOOST_PWM), bhnd_pmu_res_depfltr_bb},
{
PMURES_BIT(RES4325_HT_AVAIL),
RES_DEPEND_ADD,
PMURES_BIT(RES4325_RX_PWRSW_PU) |
PMURES_BIT(RES4325_TX_PWRSW_PU) |
PMURES_BIT(RES4325_LOGEN_PWRSW_PU) |
PMURES_BIT(RES4325_AFE_PWRSW_PU), NULL},
{
PMURES_BIT(RES4325_ILP_REQUEST) |
PMURES_BIT(RES4325_ABUCK_BURST) |
PMURES_BIT(RES4325_ABUCK_PWM) |
PMURES_BIT(RES4325_LNLDO1_PU) |
PMURES_BIT(RES4325C1_LNLDO2_PU) |
PMURES_BIT(RES4325_XTAL_PU) |
PMURES_BIT(RES4325_ALP_AVAIL) |
PMURES_BIT(RES4325_RX_PWRSW_PU) |
PMURES_BIT(RES4325_TX_PWRSW_PU) |
PMURES_BIT(RES4325_RFPLL_PWRSW_PU) |
PMURES_BIT(RES4325_LOGEN_PWRSW_PU) |
PMURES_BIT(RES4325_AFE_PWRSW_PU) |
PMURES_BIT(RES4325_BBPLL_PWRSW_PU) |
PMURES_BIT(RES4325_HT_AVAIL), RES_DEPEND_REMOVE,
PMURES_BIT(RES4325B0_CBUCK_LPOM) |
PMURES_BIT(RES4325B0_CBUCK_BURST) |
PMURES_BIT(RES4325B0_CBUCK_PWM), bhnd_pmu_res_depfltr_ncb}
};
static const pmu_res_updown_t bcm4315a0_res_updown[] = {
{
BHND_PMU_RES4315_XTAL_PU, 0x2501}
};
static const pmu_res_depend_t bcm4315a0_res_depend[] = {
{
PMURES_BIT(RES4315_OTP_PU),
RES_DEPEND_REMOVE,
PMURES_BIT(RES4315_PALDO_PU), bhnd_pmu_res_depfltr_npaldo},
{
PMURES_BIT(RES4315_ALP_AVAIL) | PMURES_BIT(RES4315_HT_AVAIL),
RES_DEPEND_ADD,
PMURES_BIT(RES4315_PALDO_PU), bhnd_pmu_res_depfltr_paldo},
{
PMURES_BIT(RES4315_HT_AVAIL),
RES_DEPEND_ADD,
PMURES_BIT(RES4315_RX_PWRSW_PU) |
PMURES_BIT(RES4315_TX_PWRSW_PU) |
PMURES_BIT(RES4315_LOGEN_PWRSW_PU) |
PMURES_BIT(RES4315_AFE_PWRSW_PU), NULL},
{
PMURES_BIT(RES4315_CLDO_PU) | PMURES_BIT(RES4315_ILP_REQUEST) |
PMURES_BIT(RES4315_LNLDO1_PU) |
PMURES_BIT(RES4315_OTP_PU) |
PMURES_BIT(RES4315_LNLDO2_PU) |
PMURES_BIT(RES4315_XTAL_PU) |
PMURES_BIT(RES4315_ALP_AVAIL) |
PMURES_BIT(RES4315_RX_PWRSW_PU) |
PMURES_BIT(RES4315_TX_PWRSW_PU) |
PMURES_BIT(RES4315_RFPLL_PWRSW_PU) |
PMURES_BIT(RES4315_LOGEN_PWRSW_PU) |
PMURES_BIT(RES4315_AFE_PWRSW_PU) |
PMURES_BIT(RES4315_BBPLL_PWRSW_PU) |
PMURES_BIT(RES4315_HT_AVAIL), RES_DEPEND_REMOVE,
PMURES_BIT(RES4315_CBUCK_LPOM) |
PMURES_BIT(RES4315_CBUCK_BURST) |
PMURES_BIT(RES4315_CBUCK_PWM), bhnd_pmu_res_depfltr_ncb}
};
static const pmu_res_updown_t bcm4329_res_updown[] = {
{
BHND_PMU_RES4329_XTAL_PU, 0x1501}
};
static const pmu_res_depend_t bcm4329_res_depend[] = {
{
PMURES_BIT(RES4329_HT_AVAIL),
RES_DEPEND_ADD,
PMURES_BIT(RES4329_CBUCK_LPOM) |
PMURES_BIT(RES4329_CBUCK_BURST) |
PMURES_BIT(RES4329_CBUCK_PWM) |
PMURES_BIT(RES4329_CLDO_PU) |
PMURES_BIT(RES4329_PALDO_PU) |
PMURES_BIT(RES4329_LNLDO1_PU) |
PMURES_BIT(RES4329_XTAL_PU) |
PMURES_BIT(RES4329_ALP_AVAIL) |
PMURES_BIT(RES4329_RX_PWRSW_PU) |
PMURES_BIT(RES4329_TX_PWRSW_PU) |
PMURES_BIT(RES4329_RFPLL_PWRSW_PU) |
PMURES_BIT(RES4329_LOGEN_PWRSW_PU) |
PMURES_BIT(RES4329_AFE_PWRSW_PU) |
PMURES_BIT(RES4329_BBPLL_PWRSW_PU), NULL}
};
static const pmu_res_updown_t bcm4319a0_res_updown[] = {
{
BHND_PMU_RES4319_XTAL_PU, 0x3f01}
};
static const pmu_res_depend_t bcm4319a0_res_depend[] = {
{
PMURES_BIT(RES4319_OTP_PU),
RES_DEPEND_REMOVE,
PMURES_BIT(RES4319_PALDO_PU), bhnd_pmu_res_depfltr_npaldo},
{
PMURES_BIT(RES4319_HT_AVAIL),
RES_DEPEND_ADD,
PMURES_BIT(RES4319_PALDO_PU), bhnd_pmu_res_depfltr_paldo},
{
PMURES_BIT(RES4319_HT_AVAIL),
RES_DEPEND_ADD,
PMURES_BIT(RES4319_RX_PWRSW_PU) |
PMURES_BIT(RES4319_TX_PWRSW_PU) |
PMURES_BIT(RES4319_RFPLL_PWRSW_PU) |
PMURES_BIT(RES4319_LOGEN_PWRSW_PU) |
PMURES_BIT(RES4319_AFE_PWRSW_PU), NULL}
};
static const pmu_res_updown_t bcm4336a0_res_updown[] = {
{
BHND_PMU_RES4336_HT_AVAIL, 0x0D01}
};
static const pmu_res_depend_t bcm4336a0_res_depend[] = {
{
PMURES_BIT(RES4336_RSVD), RES_DEPEND_ADD, 0, NULL}
};
static const pmu_res_updown_t bcm4330a0_res_updown[] = {
{
BHND_PMU_RES4330_HT_AVAIL, 0x0e02}
};
static const pmu_res_depend_t bcm4330a0_res_depend[] = {
{
PMURES_BIT(RES4330_HT_AVAIL), RES_DEPEND_ADD, 0, NULL}
};
static bool
bhnd_pmu_res_depfltr_bb(struct bhnd_pmu_softc *sc)
{
return (BHND_PMU_GET_FLAG(sc->board.board_flags, BHND_BFL_BUCKBOOST));
}
static bool
bhnd_pmu_res_depfltr_ncb(struct bhnd_pmu_softc *sc)
{
if (sc->cid.chip_id == BHND_CHIPID_BCM4325 && sc->cid.chip_rev <= 1)
return (false);
return (BHND_PMU_GET_FLAG(sc->board.board_flags, BHND_BFL_NOCBUCK));
}
static bool
bhnd_pmu_res_depfltr_paldo(struct bhnd_pmu_softc *sc)
{
return (BHND_PMU_GET_FLAG(sc->board.board_flags, BHND_BFL_PALDO));
}
static bool
bhnd_pmu_res_depfltr_npaldo(struct bhnd_pmu_softc *sc)
{
return (!BHND_PMU_GET_FLAG(sc->board.board_flags, BHND_BFL_PALDO));
}
static int
bhnd_pmu_res_masks(struct bhnd_pmu_softc *sc, uint32_t *pmin, uint32_t *pmax)
{
uint32_t max_mask, min_mask;
uint32_t chipst, otpsel;
uint32_t nval;
uint8_t rsrcs;
int error;
max_mask = 0;
min_mask = 0;
rsrcs = BHND_PMU_GET_BITS(sc->caps, BHND_PMU_CAP_RC);
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4325:
if (!bhnd_pmu_res_depfltr_ncb(sc))
min_mask |= PMURES_BIT(RES4325B0_CBUCK_LPOM);
chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev);
if (BHND_PMU_GET_BITS(chipst, CHIPC_CST4325_PMUTOP_2B))
min_mask |= PMURES_BIT(RES4325B0_CLDO_PU);
otpsel = BHND_PMU_GET_BITS(chipst, CHIPC_CST4325_SPROM_OTP_SEL);
if (otpsel != CHIPC_CST_OTP_PWRDN)
min_mask |= PMURES_BIT(RES4325_OTP_PU);
if (sc->board.board_flags & BHND_BFL_BUCKBOOST) {
switch (sc->board.board_type) {
case BHND_BOARD_BCM94325DEVBU:
case BHND_BOARD_BCM94325BGABU:
min_mask |= PMURES_BIT(
RES4325_BUCK_BOOST_BURST);
break;
}
}
max_mask = ~(~0 << rsrcs);
break;
case BHND_CHIPID_BCM4312:
min_mask = 0xcbb;
break;
case BHND_CHIPID_BCM4322:
case BHND_CHIPID_BCM43221:
case BHND_CHIPID_BCM43231:
case BHND_CHIPID_BCM4342:
if (sc->cid.chip_rev >= 2)
break;
min_mask = PMURES_BIT(RES4322_RF_LDO) |
PMURES_BIT(RES4322_XTAL_PU) |
PMURES_BIT(RES4322_ALP_AVAIL);
if (bhnd_get_attach_type(sc->chipc_dev) == BHND_ATTACH_NATIVE) {
min_mask |=
PMURES_BIT(RES4322_SI_PLL_ON) |
PMURES_BIT(RES4322_HT_SI_AVAIL) |
PMURES_BIT(RES4322_PHY_PLL_ON) |
PMURES_BIT(RES4322_OTP_PU) |
PMURES_BIT(RES4322_HT_PHY_AVAIL);
max_mask = 0x1ff;
}
break;
case BHND_CHIPID_BCM43222:
case BHND_CHIPID_BCM43111:
case BHND_CHIPID_BCM43112:
case BHND_CHIPID_BCM43224:
case BHND_CHIPID_BCM43225:
case BHND_CHIPID_BCM43421:
case BHND_CHIPID_BCM43226:
case BHND_CHIPID_BCM43420:
case BHND_CHIPID_BCM43235:
case BHND_CHIPID_BCM43236:
case BHND_CHIPID_BCM43238:
case BHND_CHIPID_BCM43234:
case BHND_CHIPID_BCM43237:
case BHND_CHIPID_BCM4331:
case BHND_CHIPID_BCM43431:
case BHND_CHIPID_BCM6362:
break;
case BHND_CHIPID_BCM4328:
min_mask =
PMURES_BIT(RES4328_BB_SWITCHER_PWM) |
PMURES_BIT(RES4328_EXT_SWITCHER_PWM) |
PMURES_BIT(RES4328_XTAL_EN);
max_mask = 0xfffffff;
break;
case BHND_CHIPID_BCM5354:
max_mask = 0xfffffff;
break;
case BHND_CHIPID_BCM4329:
if (sc->cid.chip_rev >= 0x2) {
min_mask =
PMURES_BIT(RES4329_CBUCK_LPOM) |
PMURES_BIT(RES4329_LNLDO1_PU) |
PMURES_BIT(RES4329_CLDO_PU);
} else {
min_mask =
PMURES_BIT(RES4329_CBUCK_LPOM) |
PMURES_BIT(RES4329_CLDO_PU);
}
chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev);
otpsel = BHND_PMU_GET_BITS(chipst, CHIPC_CST4329_SPROM_OTP_SEL);
if (otpsel != CHIPC_CST_OTP_PWRDN)
min_mask |= PMURES_BIT(RES4329_OTP_PU);
max_mask = 0x3ff63e;
break;
case BHND_CHIPID_BCM4319:
min_mask = PMURES_BIT(RES4319_CBUCK_LPOM) |
PMURES_BIT(RES4319_CLDO_PU);
max_mask = ~(~0 << rsrcs);
break;
case BHND_CHIPID_BCM4336:
min_mask =
PMURES_BIT(RES4336_CBUCK_LPOM) |
PMURES_BIT(RES4336_CLDO_PU) |
PMURES_BIT(RES4336_LDO3P3_PU) |
PMURES_BIT(RES4336_OTP_PU) |
PMURES_BIT(RES4336_DIS_INT_RESET_PD);
max_mask = 0x1ffffff;
break;
case BHND_CHIPID_BCM4330:
min_mask =
PMURES_BIT(RES4330_CBUCK_LPOM) | PMURES_BIT(RES4330_CLDO_PU)
| PMURES_BIT(RES4330_DIS_INT_RESET_PD) |
PMURES_BIT(RES4330_LDO3P3_PU) | PMURES_BIT(RES4330_OTP_PU);
max_mask = 0xfffffff;
break;
case BHND_CHIPID_BCM4313:
min_mask = PMURES_BIT(RES4313_BB_PU_RSRC) |
PMURES_BIT(RES4313_XTAL_PU_RSRC) |
PMURES_BIT(RES4313_ALP_AVAIL_RSRC) |
PMURES_BIT(RES4313_BB_PLL_PWRSW_RSRC);
max_mask = 0xffff;
break;
default:
break;
}
error = bhnd_nvram_getvar_uint32(sc->chipc_dev, BHND_NVAR_RMIN, &nval);
if (error && error != ENOENT) {
PMU_LOG(sc, "NVRAM error reading %s: %d\n",
BHND_NVAR_RMIN, error);
return (error);
} else if (!error) {
PMU_DEBUG(sc, "Applying rmin=%#x to min_mask\n", nval);
min_mask = nval;
}
error = bhnd_nvram_getvar_uint32(sc->chipc_dev, BHND_NVAR_RMAX, &nval);
if (error && error != ENOENT) {
PMU_LOG(sc, "NVRAM error reading %s: %d\n",
BHND_NVAR_RMAX, error);
return (error);
} else if (!error) {
PMU_DEBUG(sc, "Applying rmax=%#x to max_mask\n", nval);
min_mask = nval;
}
if (pmin != NULL)
*pmin = min_mask;
if (pmax != NULL)
*pmax = max_mask;
return (0);
}
int
bhnd_pmu_res_init(struct bhnd_pmu_softc *sc)
{
const pmu_res_updown_t *pmu_res_updown_table;
const pmu_res_depend_t *pmu_res_depend_table;
size_t pmu_res_updown_table_sz;
size_t pmu_res_depend_table_sz;
uint32_t max_mask, min_mask;
uint8_t rsrcs;
int error;
pmu_res_depend_table = NULL;
pmu_res_depend_table_sz = 0;
pmu_res_updown_table = NULL;
pmu_res_updown_table_sz = 0;
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4315:
pmu_res_updown_table = bcm4315a0_res_updown;
pmu_res_updown_table_sz = nitems(bcm4315a0_res_updown);
pmu_res_depend_table = bcm4315a0_res_depend;
pmu_res_depend_table_sz = nitems(bcm4315a0_res_depend);
break;
case BHND_CHIPID_BCM4325:
pmu_res_updown_table = bcm4325a0_res_updown;
pmu_res_updown_table_sz = nitems(bcm4325a0_res_updown);
pmu_res_depend_table = bcm4325a0_res_depend;
pmu_res_depend_table_sz = nitems(bcm4325a0_res_depend);
break;
case BHND_CHIPID_BCM4328:
pmu_res_updown_table = bcm4328a0_res_updown;
pmu_res_updown_table_sz = nitems(bcm4328a0_res_updown);
pmu_res_depend_table = bcm4328a0_res_depend;
pmu_res_depend_table_sz = nitems(bcm4328a0_res_depend);
break;
case BHND_CHIPID_BCM4329:
pmu_res_updown_table = bcm4329_res_updown;
pmu_res_updown_table_sz = nitems(bcm4329_res_updown);
pmu_res_depend_table = bcm4329_res_depend;
pmu_res_depend_table_sz = nitems(bcm4329_res_depend);
break;
case BHND_CHIPID_BCM4319:
pmu_res_updown_table = bcm4319a0_res_updown;
pmu_res_updown_table_sz = nitems(bcm4319a0_res_updown);
pmu_res_depend_table = bcm4319a0_res_depend;
pmu_res_depend_table_sz = nitems(bcm4319a0_res_depend);
break;
case BHND_CHIPID_BCM4336:
pmu_res_updown_table = bcm4336a0_res_updown;
pmu_res_updown_table_sz = nitems(bcm4336a0_res_updown);
pmu_res_depend_table = bcm4336a0_res_depend;
pmu_res_depend_table_sz = nitems(bcm4336a0_res_depend);
break;
case BHND_CHIPID_BCM4330:
pmu_res_updown_table = bcm4330a0_res_updown;
pmu_res_updown_table_sz = nitems(bcm4330a0_res_updown);
pmu_res_depend_table = bcm4330a0_res_depend;
pmu_res_depend_table_sz = nitems(bcm4330a0_res_depend);
break;
default:
break;
}
rsrcs = BHND_PMU_GET_BITS(sc->caps, BHND_PMU_CAP_RC);
for (size_t i = 0; i < pmu_res_updown_table_sz; i++) {
const pmu_res_updown_t *updt;
KASSERT(pmu_res_updown_table != NULL, ("no updown tables"));
updt = &pmu_res_updown_table[pmu_res_updown_table_sz - i - 1];
PMU_DEBUG(sc, "Changing rsrc %d res_updn_timer to %#x\n",
updt->resnum, updt->updown);
BHND_PMU_WRITE_4(sc, BHND_PMU_RES_TABLE_SEL, updt->resnum);
BHND_PMU_WRITE_4(sc, BHND_PMU_RES_UPDN_TIMER, updt->updown);
}
for (uint8_t i = 0; i < rsrcs; i++) {
char name[6];
uint32_t val;
snprintf(name, sizeof(name), "r%dt", i);
error = bhnd_nvram_getvar_uint32(sc->chipc_dev, name, &val);
if (error == ENOENT) {
continue;
} else if (error) {
PMU_LOG(sc, "NVRAM error reading %s: %d\n",
name, error);
return (error);
}
PMU_DEBUG(sc, "Applying %s=%d to rsrc %d res_updn_timer\n",
name, val, i);
BHND_PMU_WRITE_4(sc, BHND_PMU_RES_TABLE_SEL, i);
BHND_PMU_WRITE_4(sc, BHND_PMU_RES_UPDN_TIMER, val);
}
for (size_t i = 0; i < pmu_res_depend_table_sz; i++) {
const pmu_res_depend_t *rdep;
pmu_res_filter filter;
uint32_t depend_mask;
KASSERT(pmu_res_depend_table != NULL, ("no depend tables"));
rdep = &pmu_res_depend_table[pmu_res_depend_table_sz - i - 1];
filter = rdep->filter;
if (filter != NULL && !filter(sc))
continue;
for (uint8_t i = 0; i < rsrcs; i++) {
if ((rdep->res_mask & BHND_PMURES_BIT(i)) == 0)
continue;
BHND_PMU_WRITE_4(sc, BHND_PMU_RES_TABLE_SEL, i);
depend_mask = BHND_PMU_READ_4(sc,
BHND_PMU_RES_DEP_MASK);
switch (rdep->action) {
case RES_DEPEND_SET:
PMU_DEBUG(sc, "Changing rsrc %hhu res_dep_mask to "
"%#x\n", i, table->depend_mask);
depend_mask = rdep->depend_mask;
break;
case RES_DEPEND_ADD:
PMU_DEBUG(sc, "Adding %#x to rsrc %hhu "
"res_dep_mask\n", table->depend_mask, i);
depend_mask |= rdep->depend_mask;
break;
case RES_DEPEND_REMOVE:
PMU_DEBUG(sc, "Removing %#x from rsrc %hhu "
"res_dep_mask\n", table->depend_mask, i);
depend_mask &= ~(rdep->depend_mask);
break;
default:
panic("unknown RES_DEPEND action: %d\n",
rdep->action);
break;
}
BHND_PMU_WRITE_4(sc, BHND_PMU_RES_DEP_MASK,
depend_mask);
}
}
for (uint8_t i = 0; i < rsrcs; i++) {
char name[6];
uint32_t val;
snprintf(name, sizeof(name), "r%dd", i);
error = bhnd_nvram_getvar_uint32(sc->chipc_dev, name, &val);
if (error == ENOENT) {
continue;
} else if (error) {
PMU_LOG(sc, "NVRAM error reading %s: %d\n", name,
error);
return (error);
}
PMU_DEBUG(sc, "Applying %s=%d to rsrc %d res_dep_mask\n", name,
val, i);
BHND_PMU_WRITE_4(sc, BHND_PMU_RES_TABLE_SEL, i);
BHND_PMU_WRITE_4(sc, BHND_PMU_RES_DEP_MASK, val);
}
if ((error = bhnd_pmu_res_masks(sc, &min_mask, &max_mask)))
return (error);
if (max_mask != 0) {
PMU_DEBUG(sc, "Changing max_res_mask to 0x%x\n", max_mask);
BHND_PMU_WRITE_4(sc, BHND_PMU_MAX_RES_MASK, max_mask);
}
if (min_mask != 0) {
PMU_DEBUG(sc, "Changing min_res_mask to 0x%x\n", min_mask);
BHND_PMU_WRITE_4(sc, BHND_PMU_MIN_RES_MASK, min_mask);
}
DELAY(2000);
return (0);
}
struct pmu0_xtaltab0 {
uint16_t freq;
uint8_t xf;
uint8_t wbint;
uint32_t wbfrac;
};
static const pmu0_xtaltab0_t pmu0_xtaltab0[] = {
{
12000, 1, 73, 349525}, {
13000, 2, 67, 725937}, {
14400, 3, 61, 116508}, {
15360, 4, 57, 305834}, {
16200, 5, 54, 336579}, {
16800, 6, 52, 399457}, {
19200, 7, 45, 873813}, {
19800, 8, 44, 466033}, {
20000, 9, 44, 0}, {
25000, 10, 70, 419430}, {
26000, 11, 67, 725937}, {
30000, 12, 58, 699050}, {
38400, 13, 45, 873813}, {
40000, 14, 45, 0}, {
0, 0, 0, 0}
};
#define PMU0_XTAL0_DEFAULT 8
struct pmu1_xtaltab0 {
uint16_t fref;
uint8_t xf;
uint8_t p1div;
uint8_t p2div;
uint8_t ndiv_int;
uint32_t ndiv_frac;
};
static const pmu1_xtaltab0_t pmu1_xtaltab0_880_4329[] = {
{
12000, 1, 3, 22, 0x9, 0xFFFFEF}, {
13000, 2, 1, 6, 0xb, 0x483483}, {
14400, 3, 1, 10, 0xa, 0x1C71C7}, {
15360, 4, 1, 5, 0xb, 0x755555}, {
16200, 5, 1, 10, 0x5, 0x6E9E06}, {
16800, 6, 1, 10, 0x5, 0x3Cf3Cf}, {
19200, 7, 1, 4, 0xb, 0x755555}, {
19800, 8, 1, 11, 0x4, 0xA57EB}, {
20000, 9, 1, 11, 0x4, 0x0}, {
24000, 10, 3, 11, 0xa, 0x0}, {
25000, 11, 5, 16, 0xb, 0x0}, {
26000, 12, 1, 1, 0x21, 0xD89D89}, {
30000, 13, 3, 8, 0xb, 0x0}, {
37400, 14, 3, 1, 0x46, 0x969696}, {
38400, 15, 1, 1, 0x16, 0xEAAAAA}, {
40000, 16, 1, 2, 0xb, 0}, {
0, 0, 0, 0, 0, 0}
};
static const pmu1_xtaltab0_t pmu1_xtaltab0_880[] = {
{
12000, 1, 3, 22, 0x9, 0xFFFFEF}, {
13000, 2, 1, 6, 0xb, 0x483483}, {
14400, 3, 1, 10, 0xa, 0x1C71C7}, {
15360, 4, 1, 5, 0xb, 0x755555}, {
16200, 5, 1, 10, 0x5, 0x6E9E06}, {
16800, 6, 1, 10, 0x5, 0x3Cf3Cf}, {
19200, 7, 1, 4, 0xb, 0x755555}, {
19800, 8, 1, 11, 0x4, 0xA57EB}, {
20000, 9, 1, 11, 0x4, 0x0}, {
24000, 10, 3, 11, 0xa, 0x0}, {
25000, 11, 5, 16, 0xb, 0x0}, {
26000, 12, 1, 2, 0x10, 0xEC4EC4}, {
30000, 13, 3, 8, 0xb, 0x0}, {
33600, 14, 1, 2, 0xd, 0x186186}, {
38400, 15, 1, 2, 0xb, 0x755555}, {
40000, 16, 1, 2, 0xb, 0}, {
0, 0, 0, 0, 0, 0}
};
#define PMU1_XTALTAB0_880_12000K 0
#define PMU1_XTALTAB0_880_13000K 1
#define PMU1_XTALTAB0_880_14400K 2
#define PMU1_XTALTAB0_880_15360K 3
#define PMU1_XTALTAB0_880_16200K 4
#define PMU1_XTALTAB0_880_16800K 5
#define PMU1_XTALTAB0_880_19200K 6
#define PMU1_XTALTAB0_880_19800K 7
#define PMU1_XTALTAB0_880_20000K 8
#define PMU1_XTALTAB0_880_24000K 9
#define PMU1_XTALTAB0_880_25000K 10
#define PMU1_XTALTAB0_880_26000K 11
#define PMU1_XTALTAB0_880_30000K 12
#define PMU1_XTALTAB0_880_37400K 13
#define PMU1_XTALTAB0_880_38400K 14
#define PMU1_XTALTAB0_880_40000K 15
static const pmu1_xtaltab0_t pmu1_xtaltab0_1760[] = {
{
12000, 1, 3, 44, 0x9, 0xFFFFEF}, {
13000, 2, 1, 12, 0xb, 0x483483}, {
14400, 3, 1, 20, 0xa, 0x1C71C7}, {
15360, 4, 1, 10, 0xb, 0x755555}, {
16200, 5, 1, 20, 0x5, 0x6E9E06}, {
16800, 6, 1, 20, 0x5, 0x3Cf3Cf}, {
19200, 7, 1, 18, 0x5, 0x17B425}, {
19800, 8, 1, 22, 0x4, 0xA57EB}, {
20000, 9, 1, 22, 0x4, 0x0}, {
24000, 10, 3, 22, 0xa, 0x0}, {
25000, 11, 5, 32, 0xb, 0x0}, {
26000, 12, 1, 4, 0x10, 0xEC4EC4}, {
30000, 13, 3, 16, 0xb, 0x0}, {
38400, 14, 1, 10, 0x4, 0x955555}, {
40000, 15, 1, 4, 0xb, 0}, {
0, 0, 0, 0, 0, 0}
};
#define PMU1_XTALTAB0_1760_12000K 0
#define PMU1_XTALTAB0_1760_13000K 1
#define PMU1_XTALTAB0_1760_14400K 2
#define PMU1_XTALTAB0_1760_15360K 3
#define PMU1_XTALTAB0_1760_16200K 4
#define PMU1_XTALTAB0_1760_16800K 5
#define PMU1_XTALTAB0_1760_19200K 6
#define PMU1_XTALTAB0_1760_19800K 7
#define PMU1_XTALTAB0_1760_20000K 8
#define PMU1_XTALTAB0_1760_24000K 9
#define PMU1_XTALTAB0_1760_25000K 10
#define PMU1_XTALTAB0_1760_26000K 11
#define PMU1_XTALTAB0_1760_30000K 12
#define PMU1_XTALTAB0_1760_38400K 13
#define PMU1_XTALTAB0_1760_40000K 14
static const pmu1_xtaltab0_t pmu1_xtaltab0_1440[] = {
{
12000, 1, 1, 1, 0x78, 0x0}, {
13000, 2, 1, 1, 0x6E, 0xC4EC4E}, {
14400, 3, 1, 1, 0x64, 0x0}, {
15360, 4, 1, 1, 0x5D, 0xC00000}, {
16200, 5, 1, 1, 0x58, 0xE38E38}, {
16800, 6, 1, 1, 0x55, 0xB6DB6D}, {
19200, 7, 1, 1, 0x4B, 0}, {
19800, 8, 1, 1, 0x48, 0xBA2E8B}, {
20000, 9, 1, 1, 0x48, 0x0}, {
25000, 10, 1, 1, 0x39, 0x999999}, {
26000, 11, 1, 1, 0x37, 0x627627}, {
30000, 12, 1, 1, 0x30, 0x0}, {
37400, 13, 2, 1, 0x4D, 0x15E76}, {
38400, 13, 2, 1, 0x4B, 0x0}, {
40000, 14, 2, 1, 0x48, 0x0}, {
48000, 15, 2, 1, 0x3c, 0x0}, {
0, 0, 0, 0, 0, 0}
};
#define PMU1_XTALTAB0_1440_12000K 0
#define PMU1_XTALTAB0_1440_13000K 1
#define PMU1_XTALTAB0_1440_14400K 2
#define PMU1_XTALTAB0_1440_15360K 3
#define PMU1_XTALTAB0_1440_16200K 4
#define PMU1_XTALTAB0_1440_16800K 5
#define PMU1_XTALTAB0_1440_19200K 6
#define PMU1_XTALTAB0_1440_19800K 7
#define PMU1_XTALTAB0_1440_20000K 8
#define PMU1_XTALTAB0_1440_25000K 9
#define PMU1_XTALTAB0_1440_26000K 10
#define PMU1_XTALTAB0_1440_30000K 11
#define PMU1_XTALTAB0_1440_37400K 12
#define PMU1_XTALTAB0_1440_38400K 13
#define PMU1_XTALTAB0_1440_40000K 14
#define PMU1_XTALTAB0_1440_48000K 15
#define XTAL_FREQ_24000MHZ 24000
#define XTAL_FREQ_30000MHZ 30000
#define XTAL_FREQ_37400MHZ 37400
#define XTAL_FREQ_48000MHZ 48000
static const pmu1_xtaltab0_t pmu1_xtaltab0_960[] = {
{
12000, 1, 1, 1, 0x50, 0x0}, {
13000, 2, 1, 1, 0x49, 0xD89D89}, {
14400, 3, 1, 1, 0x42, 0xAAAAAA}, {
15360, 4, 1, 1, 0x3E, 0x800000}, {
16200, 5, 1, 1, 0x39, 0x425ED0}, {
16800, 6, 1, 1, 0x39, 0x249249}, {
19200, 7, 1, 1, 0x32, 0x0}, {
19800, 8, 1, 1, 0x30, 0x7C1F07}, {
20000, 9, 1, 1, 0x30, 0x0}, {
25000, 10, 1, 1, 0x26, 0x666666}, {
26000, 11, 1, 1, 0x24, 0xEC4EC4}, {
30000, 12, 1, 1, 0x20, 0x0}, {
37400, 13, 2, 1, 0x33, 0x563EF9}, {
38400, 14, 2, 1, 0x32, 0x0}, {
40000, 15, 2, 1, 0x30, 0x0}, {
48000, 16, 2, 1, 0x28, 0x0}, {
0, 0, 0, 0, 0, 0}
};
#define PMU1_XTALTAB0_960_12000K 0
#define PMU1_XTALTAB0_960_13000K 1
#define PMU1_XTALTAB0_960_14400K 2
#define PMU1_XTALTAB0_960_15360K 3
#define PMU1_XTALTAB0_960_16200K 4
#define PMU1_XTALTAB0_960_16800K 5
#define PMU1_XTALTAB0_960_19200K 6
#define PMU1_XTALTAB0_960_19800K 7
#define PMU1_XTALTAB0_960_20000K 8
#define PMU1_XTALTAB0_960_25000K 9
#define PMU1_XTALTAB0_960_26000K 10
#define PMU1_XTALTAB0_960_30000K 11
#define PMU1_XTALTAB0_960_37400K 12
#define PMU1_XTALTAB0_960_38400K 13
#define PMU1_XTALTAB0_960_40000K 14
#define PMU1_XTALTAB0_960_48000K 15
static const pmu1_xtaltab0_t *
bhnd_pmu1_xtaltab0(struct bhnd_pmu_query *sc)
{
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4315:
return (pmu1_xtaltab0_1760);
case BHND_CHIPID_BCM4319:
return (pmu1_xtaltab0_1440);
case BHND_CHIPID_BCM4325:
return (pmu1_xtaltab0_880);
case BHND_CHIPID_BCM4329:
return (pmu1_xtaltab0_880_4329);
case BHND_CHIPID_BCM4336:
return (pmu1_xtaltab0_960);
case BHND_CHIPID_BCM4330:
if (PMU_CST4330_SDIOD_CHIPMODE(sc))
return (pmu1_xtaltab0_960);
else
return (pmu1_xtaltab0_1440);
default:
PMU_DEBUG(sc, "bhnd_pmu1_xtaltab0: Unknown chipid %#hx\n",
sc->cid.chip_id);
return (NULL);
}
}
static const pmu1_xtaltab0_t *
bhnd_pmu1_xtaldef0(struct bhnd_pmu_query *sc)
{
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4315:
return (&pmu1_xtaltab0_1760[PMU1_XTALTAB0_1760_26000K]);
case BHND_CHIPID_BCM4319:
return (&pmu1_xtaltab0_1440[PMU1_XTALTAB0_1440_30000K]);
case BHND_CHIPID_BCM4325:
return (&pmu1_xtaltab0_880[PMU1_XTALTAB0_880_26000K]);
case BHND_CHIPID_BCM4329:
return (&pmu1_xtaltab0_880_4329[PMU1_XTALTAB0_880_38400K]);
case BHND_CHIPID_BCM4336:
return (&pmu1_xtaltab0_960[PMU1_XTALTAB0_960_26000K]);
case BHND_CHIPID_BCM4330:
if (PMU_CST4330_SDIOD_CHIPMODE(sc))
return (&pmu1_xtaltab0_960[PMU1_XTALTAB0_960_37400K]);
else
return (&pmu1_xtaltab0_1440[PMU1_XTALTAB0_1440_37400K]);
default:
PMU_DEBUG(sc, "bhnd_pmu1_xtaldef0: Unknown chipid %#hx\n",
sc->cid.chip_id);
return (NULL);
}
}
static uint32_t
bhnd_pmu1_pllfvco0(struct bhnd_pmu_query *sc)
{
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4329:
return (FVCO_880);
case BHND_CHIPID_BCM4319:
return (FVCO_1440);
case BHND_CHIPID_BCM4336:
return (FVCO_960);
case BHND_CHIPID_BCM4330:
if (PMU_CST4330_SDIOD_CHIPMODE(sc))
return (FVCO_960);
else
return (FVCO_1440);
default:
PMU_DEBUG(sc, "bhnd_pmu1_pllfvco0: Unknown chipid %#hx\n",
sc->cid.chip_id);
return (0);
}
}
static uint32_t
bhnd_pmu1_alpclk0(struct bhnd_pmu_query *sc)
{
const pmu1_xtaltab0_t *xt;
uint32_t xf;
xf = BHND_PMU_READ_4(sc, BHND_PMU_CTRL);
xf = BHND_PMU_GET_BITS(xf, BHND_PMU_CTRL_XTALFREQ);
for (xt = bhnd_pmu1_xtaltab0(sc); xt != NULL && xt->fref != 0; xt++) {
if (xt->xf == xf)
break;
}
if (xt == NULL || xt->fref == 0)
xt = bhnd_pmu1_xtaldef0(sc);
if (xt == NULL || xt->fref == 0) {
PMU_LOG(sc, "no matching ALP/XTAL frequency found\n");
return (0);
}
return (xt->fref * 1000);
}
static void
bhnd_pmu0_pllinit0(struct bhnd_pmu_softc *sc, uint32_t xtal)
{
const pmu0_xtaltab0_t *xt;
uint32_t pll_data, pll_mask;
uint32_t pll_res;
uint32_t pmu_ctrl;
uint32_t xf;
if (xtal == 0) {
PMU_DEBUG(sc, "Unspecified xtal frequency, skipping PLL "
"configuration\n");
return;
}
for (xt = pmu0_xtaltab0; xt->freq; xt ++) {
if (xt->freq == xtal)
break;
}
if (xt->freq == 0)
xt = &pmu0_xtaltab0[PMU0_XTAL0_DEFAULT];
PMU_DEBUG(sc, "XTAL %d.%d MHz (%d)\n", xtal / 1000, xtal % 1000,
xt->xf);
pmu_ctrl = BHND_PMU_READ_4(sc, BHND_PMU_CTRL);
xf = BHND_PMU_GET_BITS(pmu_ctrl, BHND_PMU_CTRL_XTALFREQ);
if (xf == xt->xf) {
#ifdef BCMUSBDEV
if (sc->cid.chip_id == BHND_CHIPID_BCM4328) {
bhnd_pmu0_sbclk4328(sc,
BHND_PMU0_PLL0_PC0_DIV_ARM_88MHZ);
return;
}
#endif
PMU_DEBUG(sc, "PLL already programmed for %d.%d MHz\n",
xt->freq / 1000, xt->freq % 1000);
return;
}
if (xf != 0) {
PMU_DEBUG(sc,
"Reprogramming PLL for %d.%d MHz (was %d.%dMHz)\n",
xt->freq / 1000, xt->freq % 1000,
pmu0_xtaltab0[tmp-1].freq / 1000,
pmu0_xtaltab0[tmp-1].freq % 1000);
} else {
PMU_DEBUG(sc, "Programming PLL for %d.%d MHz\n",
xt->freq / 1000, xt->freq % 1000);
}
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4328:
pll_res = PMURES_BIT(RES4328_BB_PLL_PU);
break;
case BHND_CHIPID_BCM5354:
pll_res = PMURES_BIT(RES5354_BB_PLL_PU);
break;
default:
panic("unsupported chipid %#hx\n", sc->cid.chip_id);
}
BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~pll_res);
BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK, ~pll_res);
PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL);
PMU_DEBUG(sc, "Done masking\n");
if (xt->freq >= BHND_PMU0_PLL0_PC0_PDIV_FREQ) {
BHND_PMU_PLL_WRITE(sc, BHND_PMU0_PLL0_PLLCTL0,
BHND_PMU0_PLL0_PC0_PDIV_MASK, BHND_PMU0_PLL0_PC0_PDIV_MASK);
} else {
BHND_PMU_PLL_WRITE(sc, BHND_PMU0_PLL0_PLLCTL0, 0,
BHND_PMU0_PLL0_PC0_PDIV_MASK);
}
pll_data =
BHND_PMU_SET_BITS(xt->wbint, BHND_PMU0_PLL0_PC1_WILD_INT) |
BHND_PMU_SET_BITS(xt->wbfrac, BHND_PMU0_PLL0_PC1_WILD_FRAC);
if (xt->wbfrac == 0) {
pll_data |= BHND_PMU0_PLL0_PC1_STOP_MOD;
} else {
pll_data &= ~BHND_PMU0_PLL0_PC1_STOP_MOD;
}
pll_mask =
BHND_PMU0_PLL0_PC1_WILD_INT_MASK |
BHND_PMU0_PLL0_PC1_WILD_FRAC_MASK;
BHND_PMU_PLL_WRITE(sc, BHND_PMU0_PLL0_PLLCTL1, pll_data, pll_mask);
pll_data = BHND_PMU_SET_BITS(xt->wbint, BHND_PMU0_PLL0_PC2_WILD_INT);
pll_mask = BHND_PMU0_PLL0_PC2_WILD_INT_MASK;
BHND_PMU_PLL_WRITE(sc, BHND_PMU0_PLL0_PLLCTL2, pll_data, pll_mask);
PMU_DEBUG(sc, "Done pll\n");
pmu_ctrl = BHND_PMU_READ_4(sc, BHND_PMU_CTRL);
pmu_ctrl &= ~(BHND_PMU_CTRL_ILP_DIV_MASK|BHND_PMU_CTRL_XTALFREQ_MASK);
pmu_ctrl |= BHND_PMU_SET_BITS(((xt->freq + 127) / 128) - 1,
BHND_PMU_CTRL_ILP_DIV);
pmu_ctrl |= BHND_PMU_SET_BITS(xt->xf, BHND_PMU_CTRL_XTALFREQ);
BHND_PMU_WRITE_4(sc, BHND_PMU_CTRL, pmu_ctrl);
}
static uint32_t
bhnd_pmu0_alpclk0(struct bhnd_pmu_query *sc)
{
const pmu0_xtaltab0_t *xt;
uint32_t xf;
xf = BHND_PMU_READ_4(sc, BHND_PMU_CTRL);
xf = BHND_PMU_GET_BITS(xf, BHND_PMU_CTRL_XTALFREQ);
for (xt = pmu0_xtaltab0; xt->freq; xt++)
if (xt->xf == xf)
break;
if (xt == NULL || xt->freq == 0)
panic("unsupported frequency: %u", xf);
return (xt->freq * 1000);
}
static uint32_t
bhnd_pmu0_cpuclk0(struct bhnd_pmu_query *sc)
{
uint32_t tmp, divarm;
uint32_t FVCO;
#ifdef BCMDBG
uint32_t pdiv, wbint, wbfrac, fvco;
uint32_t freq;
#endif
FVCO = FVCO_880;
tmp = BHND_PMU_PLL_READ(sc, BHND_PMU0_PLL0_PLLCTL0);
divarm = BHND_PMU_GET_BITS(tmp, BHND_PMU0_PLL0_PC0_DIV_ARM);
#ifdef BCMDBG
pdiv = tmp & BHND_PMU0_PLL0_PC0_PDIV_MASK;
tmp = BHND_PMU_PLL_READ(sc, BHND_PMU0_PLL0_PLLCTL1);
wbfrac = BHND_PMU_GET_BITS(tmp, BHND_PMU0_PLL0_PC1_WILD_FRAC);
wbint = BHND_PMU_GET_BITS(tmp, PMU0_PLL0_PC1_WILD_INT);
tmp = BHND_PMU_PLL_READ(sc, BHND_PMU0_PLL0_PLLCTL2);
wbint += BHND_PMU_GET_BITS(tmp, BHND_PMU0_PLL0_PC2_WILD_INT);
freq = bhnd_pmu0_alpclk0(sih, osh, cc) / 1000;
fvco = (freq * wbint) << 8;
fvco += (freq * (wbfrac >> 10)) >> 2;
fvco += (freq * (wbfrac & 0x3ff)) >> 10;
fvco >>= 8;
fvco >>= pdiv;
fvco /= 1000;
fvco *= 1000;
PMU_DEBUG(sc, "bhnd_pmu0_cpuclk0: wbint %u wbfrac %u fvco %u\n",
wbint, wbfrac, fvco);
FVCO = fvco;
#endif
return FVCO / (divarm + BHND_PMU0_PLL0_PC0_DIV_ARM_BASE) * 1000;
}
static void
bhnd_pmu1_pllinit0(struct bhnd_pmu_softc *sc, uint32_t xtal)
{
const pmu1_xtaltab0_t *xt;
uint32_t buf_strength;
uint32_t plladdr, plldata, pllmask;
uint32_t pmuctrl;
uint32_t FVCO;
uint8_t ndiv_mode;
FVCO = bhnd_pmu1_pllfvco0(&sc->query) / 1000;
buf_strength = 0;
ndiv_mode = 1;
if (xtal == 0) {
PMU_DEBUG(sc, "Unspecified xtal frequency, skipping PLL "
"configuration\n");
return;
}
for (xt = bhnd_pmu1_xtaltab0(&sc->query); xt != NULL && xt->fref != 0;
xt++)
{
if (xt->fref == xtal)
break;
}
if (xt == NULL || xt->fref == 0) {
PMU_LOG(sc, "Unsupported XTAL frequency %d.%dMHz, skipping PLL "
"configuration\n", xtal / 1000, xtal % 1000);
return;
}
pmuctrl = BHND_PMU_READ_4(sc, BHND_PMU_CTRL);
if (BHND_PMU_GET_BITS(pmuctrl, BHND_PMU_CTRL_XTALFREQ) == xt->xf &&
sc->cid.chip_id != BHND_CHIPID_BCM4319 &&
sc->cid.chip_id != BHND_CHIPID_BCM4330)
{
PMU_DEBUG(sc, "PLL already programmed for %d.%dMHz\n",
xt->fref / 1000, xt->fref % 1000);
return;
}
PMU_DEBUG(sc, "XTAL %d.%dMHz (%d)\n", xtal / 1000, xtal % 1000, xt->xf);
PMU_DEBUG(sc, "Programming PLL for %d.%dMHz\n", xt->fref / 1000,
xt->fref % 1000);
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4325:
buf_strength = 0x222222;
BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK,
~(PMURES_BIT(RES4325_BBPLL_PWRSW_PU) |
PMURES_BIT(RES4325_HT_AVAIL)));
BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK,
~(PMURES_BIT(RES4325_BBPLL_PWRSW_PU) |
PMURES_BIT(RES4325_HT_AVAIL)));
PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL);
break;
case BHND_CHIPID_BCM4329:
buf_strength = 0x888888;
BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK,
~(PMURES_BIT(RES4329_BBPLL_PWRSW_PU) |
PMURES_BIT(RES4329_HT_AVAIL)));
BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK,
~(PMURES_BIT(RES4329_BBPLL_PWRSW_PU) |
PMURES_BIT(RES4329_HT_AVAIL)));
PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL);
plladdr = BHND_PMU1_PLL0_PLLCTL4;
if (xt->fref == 38400)
plldata = 0x200024C0;
else if (xt->fref == 37400)
plldata = 0x20004500;
else if (xt->fref == 26000)
plldata = 0x200024C0;
else
plldata = 0x200005C0;
BHND_PMU_PLL_WRITE(sc, plladdr, plldata, ~0);
plladdr = BHND_PMU1_PLL0_PLLCTL5;
plldata = BHND_PMU_PLL_READ(sc, plladdr);
plldata &= BHND_PMU1_PLL0_PC5_CLK_DRV_MASK;
if (xt->fref == 38400 ||
xt->fref == 37400 ||
xt->fref == 26000) {
plldata |= 0x15;
} else {
plldata |= 0x25;
}
BHND_PMU_PLL_WRITE(sc, plladdr, plldata, ~0);
break;
case BHND_CHIPID_BCM4319:
buf_strength = 0x222222;
BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK,
~(PMURES_BIT(RES4319_HT_AVAIL)));
BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK,
~(PMURES_BIT(RES4319_HT_AVAIL)));
DELAY(100);
BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK,
~(PMURES_BIT(RES4319_BBPLL_PWRSW_PU)));
BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK,
~(PMURES_BIT(RES4319_BBPLL_PWRSW_PU)));
DELAY(100);
PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL);
plldata = 0x200005c0;
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, plldata, ~0);
break;
case BHND_CHIPID_BCM4336:
BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK,
~(PMURES_BIT(RES4336_HT_AVAIL) |
PMURES_BIT(RES4336_MACPHY_CLKAVAIL)));
BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK,
~(PMURES_BIT(RES4336_HT_AVAIL) |
PMURES_BIT(RES4336_MACPHY_CLKAVAIL)));
DELAY(100);
PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL);
break;
case BHND_CHIPID_BCM4330:
BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK,
~(PMURES_BIT(RES4330_HT_AVAIL) |
PMURES_BIT(RES4330_MACPHY_CLKAVAIL)));
BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK,
~(PMURES_BIT(RES4330_HT_AVAIL) |
PMURES_BIT(RES4330_MACPHY_CLKAVAIL)));
DELAY(100);
PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL);
break;
default:
panic("unsupported chipid %#hx\n", sc->cid.chip_id);
}
PMU_DEBUG(sc, "Done masking\n");
plldata =
BHND_PMU_SET_BITS(xt->p1div, BHND_PMU1_PLL0_PC0_P1DIV) |
BHND_PMU_SET_BITS(xt->p2div, BHND_PMU1_PLL0_PC0_P2DIV);
pllmask = BHND_PMU1_PLL0_PC0_P1DIV_MASK|BHND_PMU1_PLL0_PC0_P2DIV_MASK;
if (sc->cid.chip_id == BHND_CHIPID_BCM4319) {
plldata &= ~(BHND_PMU1_PLL0_PC0_BYPASS_SDMOD_MASK);
pllmask |= BHND_PMU1_PLL0_PC0_BYPASS_SDMOD_MASK;
if (!xt->ndiv_frac) {
plldata |= BHND_PMU_SET_BITS(1,
BHND_PMU1_PLL0_PC0_BYPASS_SDMOD);
}
}
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, plldata, pllmask);
if (sc->cid.chip_id == BHND_CHIPID_BCM4330)
bhnd_pmu_set_4330_plldivs(sc);
if (sc->cid.chip_id == BHND_CHIPID_BCM4329 && sc->cid.chip_rev == 0) {
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
BHND_PMU_DOT11MAC_880MHZ_CLK_DIVISOR_VAL,
BHND_PMU_DOT11MAC_880MHZ_CLK_DIVISOR_MASK);
}
if (sc->cid.chip_id == BHND_CHIPID_BCM4336 ||
sc->cid.chip_id == BHND_CHIPID_BCM4330)
{
ndiv_mode = BHND_PMU1_PLL0_PC2_NDIV_MODE_MFB;
} else if (sc->cid.chip_id == BHND_CHIPID_BCM4319) {
if (!(xt->ndiv_frac))
ndiv_mode = BHND_PMU1_PLL0_PC2_NDIV_MODE_INT;
else
ndiv_mode = BHND_PMU1_PLL0_PC2_NDIV_MODE_MFB;
} else {
ndiv_mode = BHND_PMU1_PLL0_PC2_NDIV_MODE_MASH;
}
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
BHND_PMU_SET_BITS(xt->ndiv_int, BHND_PMU1_PLL0_PC2_NDIV_INT) |
BHND_PMU_SET_BITS(ndiv_mode, BHND_PMU1_PLL0_PC2_NDIV_MODE),
BHND_PMU1_PLL0_PC2_NDIV_INT_MASK |
BHND_PMU1_PLL0_PC2_NDIV_MODE_MASK);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
BHND_PMU_SET_BITS(xt->ndiv_frac, BHND_PMU1_PLL0_PC3_NDIV_FRAC),
BHND_PMU1_PLL0_PC3_NDIV_FRAC_MASK);
if (sc->cid.chip_id == BHND_CHIPID_BCM4319) {
uint8_t xs;
if (!xt->ndiv_frac)
plldata = 0x200005c0;
else
plldata = 0x202C2820;
if (FVCO < 1600)
xs = 4;
else
xs = 7;
plldata &= ~(BHND_PMU1_PLL0_PC4_KVCO_XS_MASK);
plldata |= BHND_PMU_SET_BITS(xs, BHND_PMU1_PLL0_PC4_KVCO_XS);
BHND_PMU_WRITE_4(sc, BHND_PMU1_PLL0_PLLCTL4, plldata);
}
if (buf_strength) {
PMU_DEBUG(sc, "Adjusting PLL buffer drive strength: %x\n",
buf_strength);
plldata = BHND_PMU_SET_BITS(buf_strength,
BHND_PMU1_PLL0_PC5_CLK_DRV);
pllmask = BHND_PMU1_PLL0_PC5_CLK_DRV_MASK;
if (sc->cid.chip_id == BHND_CHIPID_BCM4319) {
pllmask |=
BHND_PMU1_PLL0_PC5_VCO_RNG_MASK |
BHND_PMU1_PLL0_PC5_PLL_CTRL_37_32_MASK;
if (!xt->ndiv_frac) {
plldata |= BHND_PMU_SET_BITS(0x25,
BHND_PMU1_PLL0_PC5_PLL_CTRL_37_32);
} else {
plldata |= BHND_PMU_SET_BITS(0x15,
BHND_PMU1_PLL0_PC5_PLL_CTRL_37_32);
}
if (FVCO >= 1600) {
plldata |= BHND_PMU_SET_BITS(0x1,
BHND_PMU1_PLL0_PC5_VCO_RNG);
}
}
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, plldata,
pllmask);
}
PMU_DEBUG(sc, "Done pll\n");
if (sc->cid.chip_id == BHND_CHIPID_BCM4319 &&
xt->fref != XTAL_FREQ_30000MHZ)
{
uint32_t pll_sel;
switch (xt->fref) {
case XTAL_FREQ_24000MHZ:
pll_sel = BHND_PMU_CCTRL4319USB_24MHZ_PLL_SEL;
break;
case XTAL_FREQ_48000MHZ:
pll_sel = BHND_PMU_CCTRL4319USB_48MHZ_PLL_SEL;
break;
default:
panic("unsupported 4319USB XTAL frequency: %hu\n",
xt->fref);
}
BHND_PMU_CCTRL_WRITE(sc, BHND_PMU1_PLL0_CHIPCTL2,
BHND_PMU_SET_BITS(pll_sel, BHND_PMU_CCTRL4319USB_XTAL_SEL),
BHND_PMU_CCTRL4319USB_XTAL_SEL_MASK);
}
if (BHND_PMU_REV(sc) >= 2)
BHND_PMU_OR_4(sc, BHND_PMU_CTRL, BHND_PMU_CTRL_PLL_PLLCTL_UPD);
pmuctrl = BHND_PMU_READ_4(sc, BHND_PMU_CTRL);
pmuctrl &= ~(BHND_PMU_CTRL_ILP_DIV_MASK | BHND_PMU_CTRL_XTALFREQ_MASK);
pmuctrl |= BHND_PMU_SET_BITS(((xt->fref + 127) / 128) - 1,
BHND_PMU_CTRL_ILP_DIV);
pmuctrl |= BHND_PMU_SET_BITS(xt->xf, BHND_PMU_CTRL_XTALFREQ);
if (sc->cid.chip_id == BHND_CHIPID_BCM4329 && sc->cid.chip_rev == 0) {
BHND_PMU_AND_4(sc, BHND_PMU_CLKSTRETCH, ~BHND_PMU_CLKSTRETCH);
pmuctrl &= ~BHND_PMU_CTRL_HT_REQ_EN;
}
BHND_PMU_WRITE_4(sc, BHND_PMU_CTRL, pmuctrl);
}
static uint32_t
bhnd_pmu1_cpuclk0(struct bhnd_pmu_query *sc)
{
uint32_t tmp, m1div;
#ifdef BCMDBG
uint32_t ndiv_int, ndiv_frac, p2div, p1div, fvco;
uint32_t fref;
#endif
uint32_t FVCO = bhnd_pmu1_pllfvco0(sc);
tmp = BHND_PMU_PLL_READ(sc, BHND_PMU1_PLL0_PLLCTL1);
m1div = BHND_PMU_GET_BITS(tmp, BHND_PMU1_PLL0_PC1_M1DIV);
#ifdef BCMDBG
tmp = BHND_PMU_PLL_READ(sc, BHND_PMU1_PLL0_PLLCTL0);
p2div = BHND_PMU_GET_BITS(tmp, BHND_PMU1_PLL0_PC0_P2DIV);
p1div = BHND_PMU_GET_BITS(tmp, BHND_PMU1_PLL0_PC0_P1DIV);
tmp = BHND_PMU_PLL_READ(sc, BHND_PMU1_PLL0_PLLCTL2);
ndiv_int = BHND_PMU_GET_BITS(tmp, BHND_PMU1_PLL0_PC2_NDIV_INT);
tmp = BHND_PMU_PLL_READ(sc, BHND_PMU1_PLL0_PLLCTL3);
ndiv_frac = BHND_PMU_GET_BITS(tmp, BHND_PMU1_PLL0_PC3_NDIV_FRAC);
fref = bhnd_pmu1_alpclk0(sc) / 1000;
fvco = (fref * ndiv_int) << 8;
fvco += (fref * (ndiv_frac >> 12)) >> 4;
fvco += (fref * (ndiv_frac & 0xfff)) >> 12;
fvco >>= 8;
fvco *= p2div;
fvco /= p1div;
fvco /= 1000;
fvco *= 1000;
PMU_DEBUG(sc, "bhnd_pmu1_cpuclk0: ndiv_int %u ndiv_frac %u p2div %u "
"p1div %u fvco %u\n", ndiv_int, ndiv_frac, p2div, p1div, fvco);
FVCO = fvco;
#endif
return (FVCO / m1div * 1000);
}
void
bhnd_pmu_pll_init(struct bhnd_pmu_softc *sc, u_int xtalfreq)
{
uint32_t max_mask, min_mask;
uint32_t res_ht, res_pll;
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4312:
break;
case BHND_CHIPID_BCM4322:
case BHND_CHIPID_BCM43221:
case BHND_CHIPID_BCM43231:
case BHND_CHIPID_BCM4342:
if (sc->cid.chip_rev != 0)
break;
min_mask = BHND_PMU_READ_4(sc, BHND_PMU_MIN_RES_MASK);
max_mask = BHND_PMU_READ_4(sc, BHND_PMU_MIN_RES_MASK);
res_ht = PMURES_BIT(RES4322_HT_SI_AVAIL);
res_pll = PMURES_BIT(RES4322_SI_PLL_ON);
BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~res_ht);
BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK, ~res_ht);
PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL);
BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~res_pll);
BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK, ~res_pll);
PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL);
DELAY(1000);
BHND_PMU_PLL_WRITE(sc, BHND_PMU2_SI_PLL_PLLCTL, 0x380005c0, ~0);
DELAY(100);
BHND_PMU_WRITE_4(sc, BHND_PMU_MAX_RES_MASK, max_mask);
DELAY(100);
BHND_PMU_WRITE_4(sc, BHND_PMU_MIN_RES_MASK, min_mask);
DELAY(100);
break;
case BHND_CHIPID_BCM4325:
bhnd_pmu1_pllinit0(sc, xtalfreq);
break;
case BHND_CHIPID_BCM4328:
bhnd_pmu0_pllinit0(sc, xtalfreq);
break;
case BHND_CHIPID_BCM5354:
if (xtalfreq == 0)
xtalfreq = 25000;
bhnd_pmu0_pllinit0(sc, xtalfreq);
break;
case BHND_CHIPID_BCM4329:
if (xtalfreq == 0)
xtalfreq = 38400;
bhnd_pmu1_pllinit0(sc, xtalfreq);
break;
case BHND_CHIPID_BCM4313:
case BHND_CHIPID_BCM43222:
case BHND_CHIPID_BCM43111:
case BHND_CHIPID_BCM43112:
case BHND_CHIPID_BCM43224:
case BHND_CHIPID_BCM43225:
case BHND_CHIPID_BCM43420:
case BHND_CHIPID_BCM43421:
case BHND_CHIPID_BCM43226:
case BHND_CHIPID_BCM43235:
case BHND_CHIPID_BCM43236:
case BHND_CHIPID_BCM43238:
case BHND_CHIPID_BCM43234:
case BHND_CHIPID_BCM43237:
case BHND_CHIPID_BCM4331:
case BHND_CHIPID_BCM43431:
case BHND_CHIPID_BCM43131:
case BHND_CHIPID_BCM43227:
case BHND_CHIPID_BCM43228:
case BHND_CHIPID_BCM43428:
case BHND_CHIPID_BCM6362:
break;
case BHND_CHIPID_BCM4315:
case BHND_CHIPID_BCM4319:
case BHND_CHIPID_BCM4336:
case BHND_CHIPID_BCM4330:
bhnd_pmu1_pllinit0(sc, xtalfreq);
break;
default:
PMU_DEBUG("No PLL init done for chip %#hx rev %d pmurev %d\n",
sc->cid.chip_id, sc->cid.chip_rev, BHND_PMU_REV(sc));
break;
}
}
uint32_t
bhnd_pmu_alp_clock(struct bhnd_pmu_query *sc)
{
uint32_t clock;
clock = BHND_PMU_ALP_CLOCK;
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4328:
case BHND_CHIPID_BCM5354:
clock = bhnd_pmu0_alpclk0(sc);
break;
case BHND_CHIPID_BCM4315:
case BHND_CHIPID_BCM4319:
case BHND_CHIPID_BCM4325:
case BHND_CHIPID_BCM4329:
case BHND_CHIPID_BCM4330:
case BHND_CHIPID_BCM4336:
clock = bhnd_pmu1_alpclk0(sc);
break;
case BHND_CHIPID_BCM4312:
case BHND_CHIPID_BCM4322:
case BHND_CHIPID_BCM43221:
case BHND_CHIPID_BCM43231:
case BHND_CHIPID_BCM43222:
case BHND_CHIPID_BCM43111:
case BHND_CHIPID_BCM43112:
case BHND_CHIPID_BCM43224:
case BHND_CHIPID_BCM43225:
case BHND_CHIPID_BCM43420:
case BHND_CHIPID_BCM43421:
case BHND_CHIPID_BCM43226:
case BHND_CHIPID_BCM43235:
case BHND_CHIPID_BCM43236:
case BHND_CHIPID_BCM43238:
case BHND_CHIPID_BCM43234:
case BHND_CHIPID_BCM43237:
case BHND_CHIPID_BCM4331:
case BHND_CHIPID_BCM43431:
case BHND_CHIPID_BCM43131:
case BHND_CHIPID_BCM43227:
case BHND_CHIPID_BCM43228:
case BHND_CHIPID_BCM43428:
case BHND_CHIPID_BCM6362:
case BHND_CHIPID_BCM4342:
case BHND_CHIPID_BCM4716:
case BHND_CHIPID_BCM4748:
case BHND_CHIPID_BCM47162:
case BHND_CHIPID_BCM4313:
case BHND_CHIPID_BCM5357:
case BHND_CHIPID_BCM4749:
case BHND_CHIPID_BCM53572:
clock = 20000 * 1000;
break;
case BHND_CHIPID_BCM5356:
case BHND_CHIPID_BCM4706:
clock = 25000 * 1000;
break;
default:
PMU_DEBUG("No ALP clock specified "
"for chip %s rev %d pmurev %d, using default %d Hz\n",
bcm_chipname(sih->chip, chn, 8), sih->chiprev,
sih->pmurev, clock);
break;
}
return (clock);
}
static uint32_t
bhnd_pmu5_clock(struct bhnd_pmu_query *sc, u_int pll0, u_int m)
{
uint32_t div;
uint32_t fc;
uint32_t ndiv;
uint32_t p1, p2;
uint32_t tmp;
if ((pll0 & 3) || (pll0 > BHND_PMU4716_MAINPLL_PLL0)) {
PMU_LOG(sc, "%s: Bad pll0: %d", __func__, pll0);
return (0);
}
if ((m == 0) || (m > 4)) {
PMU_LOG(sc, "%s: Bad m divider: %d", __func__, m);
return (0);
}
if (sc->cid.chip_id == BHND_CHIPID_BCM5357 ||
sc->cid.chip_id == BHND_CHIPID_BCM4749)
{
tmp = sc->io->rd_chipst(sc->io_ctx);
if ((tmp & 0x40000) != 0)
return (133 * 1000000);
}
BHND_PMU_WRITE_4(sc, BHND_PMU_PLL_CONTROL_ADDR,
pll0 + BHND_PMU5_PLL_P1P2_OFF);
BHND_PMU_READ_4(sc, BHND_PMU_PLL_CONTROL_ADDR);
tmp = BHND_PMU_READ_4(sc, BHND_PMU_PLL_CONTROL_DATA);
p1 = BHND_PMU_GET_BITS(tmp, BHND_PMU5_PLL_P1);
p2 = BHND_PMU_GET_BITS(tmp, BHND_PMU5_PLL_P2);
BHND_PMU_WRITE_4(sc, BHND_PMU_PLL_CONTROL_ADDR,
pll0 + BHND_PMU5_PLL_M14_OFF);
BHND_PMU_READ_4(sc, BHND_PMU_PLL_CONTROL_ADDR);
tmp = BHND_PMU_READ_4(sc, BHND_PMU_PLL_CONTROL_DATA);
div = (tmp >> ((m - 1) * BHND_PMU5_PLL_MDIV_WIDTH));
div &= BHND_PMU5_PLL_MDIV_MASK;
BHND_PMU_WRITE_4(sc, BHND_PMU_PLL_CONTROL_ADDR,
pll0 + BHND_PMU5_PLL_NM5_OFF);
BHND_PMU_READ_4(sc, BHND_PMU_PLL_CONTROL_ADDR);
tmp = BHND_PMU_READ_4(sc, BHND_PMU_PLL_CONTROL_DATA);
ndiv = BHND_PMU_GET_BITS(tmp, BHND_PMU5_PLL_NDIV);
fc = bhnd_pmu_alp_clock(sc) / 1000000;
fc = (p1 * ndiv * fc) / p2;
PMU_DEBUG(sc, "%s: p1=%d, p2=%d, ndiv=%d(0x%x), m%d=%d; fc=%d, "
"clock=%d\n", __func__, p1, p2, ndiv, ndiv, m, div, fc, fc / div);
return ((fc / div) * 1000000);
}
static uint32_t
bhnd_pmu6_4706_clock(struct bhnd_pmu_query *sc, u_int pll0, u_int m)
{
uint32_t chipst, clock;
uint32_t ndiv, p1div, p2div, tmp;
BHND_PMU_WRITE_4(sc, BHND_PMU_PLL_CONTROL_ADDR,
pll0 + BHND_PMU6_4706_PROCPLL_OFF);
BHND_PMU_READ_4(sc, BHND_PMU_PLL_CONTROL_ADDR);
tmp = BHND_PMU_READ_4(sc, BHND_PMU_PLL_CONTROL_DATA);
ndiv = BHND_PMU_GET_BITS(tmp, BHND_PMU6_4706_PROC_NDIV_INT);
p1div = BHND_PMU_GET_BITS(tmp, BHND_PMU6_4706_PROC_P1DIV);
p2div = BHND_PMU_GET_BITS(tmp, BHND_PMU6_4706_PROC_P2DIV);
clock = 25 * 1000 * 1000;
chipst = sc->io->rd_chipst(sc->io_ctx);
if (chipst & CHIPC_CST4706_LOWCOST_PKG)
clock /= 4;
else
clock /= 2;
clock *= ndiv * p2div / p1div;
switch (m) {
case BHND_PMU6_MAINPLL_CPU:
return (clock);
case BHND_PMU6_MAINPLL_MEM:
return (clock / 2);
case BHND_PMU6_MAINPLL_SI:
return (clock / 4);
default:
PMU_LOG(sc, "bad m divider: %d", m);
return (0);
}
}
uint32_t
bhnd_pmu_si_clock(struct bhnd_pmu_query *sc)
{
uint32_t chipst;
uint32_t clock;
clock = BHND_PMU_HT_CLOCK;
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4322:
case BHND_CHIPID_BCM43221:
case BHND_CHIPID_BCM43231:
case BHND_CHIPID_BCM43222:
case BHND_CHIPID_BCM43111:
case BHND_CHIPID_BCM43112:
case BHND_CHIPID_BCM43224:
case BHND_CHIPID_BCM43420:
case BHND_CHIPID_BCM43225:
case BHND_CHIPID_BCM43421:
case BHND_CHIPID_BCM43226:
case BHND_CHIPID_BCM4331:
case BHND_CHIPID_BCM43431:
case BHND_CHIPID_BCM6362:
case BHND_CHIPID_BCM4342:
clock = 96000 * 1000;
break;
case BHND_CHIPID_BCM4716:
case BHND_CHIPID_BCM4748:
case BHND_CHIPID_BCM47162:
clock = bhnd_pmu5_clock(sc, BHND_PMU4716_MAINPLL_PLL0,
BHND_PMU5_MAINPLL_SI);
break;
case BHND_CHIPID_BCM4325:
clock = bhnd_pmu1_cpuclk0(sc);
break;
case BHND_CHIPID_BCM4328:
clock = bhnd_pmu0_cpuclk0(sc);
break;
case BHND_CHIPID_BCM4329:
if (sc->cid.chip_rev == 0)
clock = 38400 * 1000;
else
clock = bhnd_pmu1_cpuclk0(sc);
break;
case BHND_CHIPID_BCM4315:
case BHND_CHIPID_BCM4319:
case BHND_CHIPID_BCM4336:
case BHND_CHIPID_BCM4330:
clock = bhnd_pmu1_cpuclk0(sc);
break;
case BHND_CHIPID_BCM4312:
case BHND_CHIPID_BCM4313:
clock = 80000 * 1000;
break;
case BHND_CHIPID_BCM43234:
case BHND_CHIPID_BCM43235:
case BHND_CHIPID_BCM43236:
case BHND_CHIPID_BCM43238:
chipst = sc->io->rd_chipst(sc->io_ctx);
if (chipst & CHIPC_CST43236_BP_CLK)
clock = 120000 * 1000;
else
clock = 96000 * 1000;
break;
case BHND_CHIPID_BCM43237:
chipst = sc->io->rd_chipst(sc->io_ctx);
if (chipst & CHIPC_CST43237_BP_CLK)
clock = 96000 * 1000;
else
clock = 80000 * 1000;
break;
case BHND_CHIPID_BCM5356:
clock = bhnd_pmu5_clock(sc, BHND_PMU5356_MAINPLL_PLL0,
BHND_PMU5_MAINPLL_SI);
break;
case BHND_CHIPID_BCM5357:
case BHND_CHIPID_BCM4749:
clock = bhnd_pmu5_clock(sc, BHND_PMU5357_MAINPLL_PLL0,
BHND_PMU5_MAINPLL_SI);
break;
case BHND_CHIPID_BCM4706:
clock = bhnd_pmu6_4706_clock(sc, BHND_PMU4706_MAINPLL_PLL0,
BHND_PMU6_MAINPLL_SI);
break;
case BHND_CHIPID_BCM53572:
clock = 75000000;
break;
default:
PMU_LOG(sc, "No backplane clock specified for chip %#hx rev "
"%hhd pmurev %hhd, using default %dHz\n",
sc->cid.chip_id, sc->cid.chip_rev, BHND_PMU_REV(sc), clock);
break;
}
return (clock);
}
uint32_t
bhnd_pmu_cpu_clock(struct bhnd_pmu_query *sc)
{
if (sc->cid.chip_id == BHND_CHIPID_BCM5354)
return (240 * 1000 * 1000);
if (sc->cid.chip_id == BHND_CHIPID_BCM53572)
return (300000000);
if (BHND_PMU_REV(sc) >= 5 &&
sc->cid.chip_id != BHND_CHIPID_BCM4329 &&
sc->cid.chip_id != BHND_CHIPID_BCM4319 &&
sc->cid.chip_id != BHND_CHIPID_BCM43234 &&
sc->cid.chip_id != BHND_CHIPID_BCM43235 &&
sc->cid.chip_id != BHND_CHIPID_BCM43236 &&
sc->cid.chip_id != BHND_CHIPID_BCM43237 &&
sc->cid.chip_id != BHND_CHIPID_BCM43238 &&
sc->cid.chip_id != BHND_CHIPID_BCM4336 &&
sc->cid.chip_id != BHND_CHIPID_BCM4330)
{
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM5356:
return (bhnd_pmu5_clock(sc, BHND_PMU5356_MAINPLL_PLL0,
BHND_PMU5_MAINPLL_CPU));
case BHND_CHIPID_BCM5357:
case BHND_CHIPID_BCM4749:
return (bhnd_pmu5_clock(sc, BHND_PMU5357_MAINPLL_PLL0,
BHND_PMU5_MAINPLL_CPU));
case BHND_CHIPID_BCM4706:
return (bhnd_pmu6_4706_clock(sc,
BHND_PMU4706_MAINPLL_PLL0, BHND_PMU6_MAINPLL_CPU));
default:
return (bhnd_pmu5_clock(sc, BHND_PMU4716_MAINPLL_PLL0,
BHND_PMU5_MAINPLL_CPU));
}
} else {
return (bhnd_pmu_si_clock(sc));
}
}
uint32_t
bhnd_pmu_mem_clock(struct bhnd_pmu_query *sc)
{
if (BHND_PMU_REV(sc) >= 5 &&
sc->cid.chip_id != BHND_CHIPID_BCM4329 &&
sc->cid.chip_id != BHND_CHIPID_BCM4319 &&
sc->cid.chip_id != BHND_CHIPID_BCM43234 &&
sc->cid.chip_id != BHND_CHIPID_BCM43235 &&
sc->cid.chip_id != BHND_CHIPID_BCM43236 &&
sc->cid.chip_id != BHND_CHIPID_BCM43237 &&
sc->cid.chip_id != BHND_CHIPID_BCM43238 &&
sc->cid.chip_id != BHND_CHIPID_BCM4336 &&
sc->cid.chip_id != BHND_CHIPID_BCM4330)
{
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM5356:
return (bhnd_pmu5_clock(sc, BHND_PMU5356_MAINPLL_PLL0,
BHND_PMU5_MAINPLL_MEM));
case BHND_CHIPID_BCM5357:
case BHND_CHIPID_BCM4749:
return (bhnd_pmu5_clock(sc, BHND_PMU5357_MAINPLL_PLL0,
BHND_PMU5_MAINPLL_MEM));
case BHND_CHIPID_BCM4706:
return (bhnd_pmu6_4706_clock(sc,
BHND_PMU4706_MAINPLL_PLL0, BHND_PMU6_MAINPLL_MEM));
default:
return (bhnd_pmu5_clock(sc, BHND_PMU4716_MAINPLL_PLL0,
BHND_PMU5_MAINPLL_MEM));
}
} else {
return (bhnd_pmu_si_clock(sc));
}
}
#define ILP_CALC_DUR 10
uint32_t
bhnd_pmu_ilp_clock(struct bhnd_pmu_query *sc)
{
uint32_t start, end, delta;
if (sc->ilp_cps == 0) {
start = BHND_PMU_READ_4(sc, BHND_PMU_TIMER);
DELAY(ILP_CALC_DUR);
end = BHND_PMU_READ_4(sc, BHND_PMU_TIMER);
delta = end - start;
sc->ilp_cps = delta * (1000 / ILP_CALC_DUR);
}
return (sc->ilp_cps);
}
typedef struct {
uint8_t strength;
uint8_t sel;
} sdiod_drive_str_t;
static const sdiod_drive_str_t sdiod_drive_strength_tab1[] = {
{
4, 0x2}, {
2, 0x3}, {
1, 0x0}, {
0, 0x0}
};
static const sdiod_drive_str_t sdiod_drive_strength_tab2[] = {
{
12, 0x7}, {
10, 0x6}, {
8, 0x5}, {
6, 0x4}, {
4, 0x2}, {
2, 0x1}, {
0, 0x0}
};
static const sdiod_drive_str_t sdiod_drive_strength_tab3[] = {
{
32, 0x7}, {
26, 0x6}, {
22, 0x5}, {
16, 0x4}, {
12, 0x3}, {
8, 0x2}, {
4, 0x1}, {
0, 0x0}
};
#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
void
bhnd_pmu_sdiod_drive_strength_init(struct bhnd_pmu_softc *sc,
uint32_t drivestrength)
{
const sdiod_drive_str_t *str_tab;
uint32_t str_mask;
uint32_t str_shift;
str_tab = NULL;
str_mask = 0;
str_shift = 0;
switch (SDIOD_DRVSTR_KEY(sc->cid.chip_id, BHND_PMU_REV(sc))) {
case SDIOD_DRVSTR_KEY(BHND_CHIPID_BCM4325, 1):
str_tab = sdiod_drive_strength_tab1;
str_mask = 0x30000000;
str_shift = 28;
break;
case SDIOD_DRVSTR_KEY(BHND_CHIPID_BCM4325, 2):
case SDIOD_DRVSTR_KEY(BHND_CHIPID_BCM4325, 3):
case SDIOD_DRVSTR_KEY(BHND_CHIPID_BCM4315, 4):
case SDIOD_DRVSTR_KEY(BHND_CHIPID_BCM4319, 7):
str_tab = sdiod_drive_strength_tab2;
str_mask = 0x00003800;
str_shift = 11;
break;
case SDIOD_DRVSTR_KEY(BHND_CHIPID_BCM4336, 8):
str_tab = sdiod_drive_strength_tab3;
str_mask = 0x00003800;
str_shift = 11;
break;
default:
PMU_LOG(sc, "No SDIO Drive strength init done for chip %#x "
"rev %hhd pmurev %hhd\n", sc->cid.chip_id, sc->cid.chip_rev,
BHND_PMU_REV(sc));
break;
}
if (str_tab != NULL) {
uint32_t drivestrength_sel = 0;
uint32_t cc_data_temp;
for (u_int i = 0; str_tab[i].strength != 0; i++) {
if (drivestrength >= str_tab[i].strength) {
drivestrength_sel = str_tab[i].sel;
break;
}
}
cc_data_temp = BHND_PMU_CCTRL_READ(sc, 1);
cc_data_temp &= ~str_mask;
drivestrength_sel <<= str_shift;
cc_data_temp |= drivestrength_sel;
BHND_PMU_CCTRL_WRITE(sc, 1, cc_data_temp, ~0);
PMU_DEBUG(sc, "SDIO: %dmA drive strength selected, set to "
"0x%08x\n", drivestrength, cc_data_temp);
}
}
int
bhnd_pmu_init(struct bhnd_pmu_softc *sc)
{
uint32_t xtalfreq;
int error;
if (BHND_PMU_REV(sc) == 1) {
BHND_PMU_AND_4(sc, BHND_PMU_CTRL, ~BHND_PMU_CTRL_NOILP_ON_WAIT);
} else if (BHND_PMU_REV(sc) >= 2) {
BHND_PMU_OR_4(sc, BHND_PMU_CTRL, BHND_PMU_CTRL_NOILP_ON_WAIT);
}
if (sc->cid.chip_id == BHND_CHIPID_BCM4329 && sc->cid.chip_rev == 2) {
BHND_PMU_REGCTRL_WRITE(sc, 2, 0x100, ~0);
BHND_PMU_REGCTRL_WRITE(sc, 3, 0x4, ~0);
}
if (sc->cid.chip_id == BHND_CHIPID_BCM4319) {
BHND_PMU_REGCTRL_WRITE(sc, 2, 0x00000005, 0x00000007);
}
error = bhnd_nvram_getvar_uint32(sc->chipc_dev, BHND_NVAR_XTALFREQ,
&xtalfreq);
if (error) {
if (error != ENOENT)
PMU_LOG(sc, "error fetching xtalfreq: %d\n", error);
xtalfreq = bhnd_pmu_measure_alpclk(sc);
}
bhnd_pmu_pll_init(sc, xtalfreq);
if ((error = bhnd_pmu_res_init(sc)))
return (error);
bhnd_pmu_swreg_init(sc);
return (0);
}
static int
bhnd_pmu_res_uptime(struct bhnd_pmu_softc *sc, uint8_t rsrc, uint32_t *uptime)
{
uint32_t deps;
uint32_t up, dup, dmax;
uint32_t min_mask;
int error;
BHND_PMU_WRITE_4(sc, BHND_PMU_RES_TABLE_SEL, rsrc);
up = BHND_PMU_READ_4(sc, BHND_PMU_RES_UPDN_TIMER);
up = BHND_PMU_GET_BITS(up, BHND_PMU_RES_UPDN_UPTME);
deps = bhnd_pmu_res_deps(sc, BHND_PMURES_BIT(rsrc), false);
for (uint8_t i = 0; i <= BHND_PMU_RESNUM_MAX; i++) {
if (!(deps & BHND_PMURES_BIT(i)))
continue;
deps &= ~bhnd_pmu_res_deps(sc, BHND_PMURES_BIT(i), true);
}
if ((error = bhnd_pmu_res_masks(sc, &min_mask, NULL)))
return (error);
deps &= ~min_mask;
dmax = 0;
for (uint8_t i = 0; i <= BHND_PMU_RESNUM_MAX; i++) {
if (!(deps & BHND_PMURES_BIT(i)))
continue;
if ((error = bhnd_pmu_res_uptime(sc, i, &dup)))
return (error);
if (dmax < dup)
dmax = dup;
}
PMU_DEBUG(sc, "bhnd_pmu_res_uptime: rsrc %hhu uptime %u(deps 0x%08x "
"uptime %u)\n", rsrc, up, deps, dmax);
*uptime = (up + dmax + BHND_PMURES_UP_TRANSITION);
return (0);
}
static uint32_t
bhnd_pmu_res_deps(struct bhnd_pmu_softc *sc, uint32_t rsrcs, bool all)
{
uint32_t deps;
deps = 0;
for (uint8_t i = 0; i <= BHND_PMU_RESNUM_MAX; i++) {
if (!(rsrcs & BHND_PMURES_BIT(i)))
continue;
BHND_PMU_WRITE_4(sc, BHND_PMU_RES_TABLE_SEL, i);
deps |= BHND_PMU_READ_4(sc, BHND_PMU_RES_DEP_MASK);
}
if (deps == 0)
return (0);
if (all)
deps |= bhnd_pmu_res_deps(sc, deps, true);
return (deps);
}
int
bhnd_pmu_otp_power(struct bhnd_pmu_softc *sc, bool on)
{
uint32_t deps;
uint32_t min_mask;
uint32_t rsrcs;
int error;
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4322:
case BHND_CHIPID_BCM43221:
case BHND_CHIPID_BCM43231:
case BHND_CHIPID_BCM4342:
rsrcs = PMURES_BIT(RES4322_OTP_PU);
break;
case BHND_CHIPID_BCM4315:
rsrcs = PMURES_BIT(RES4315_OTP_PU);
break;
case BHND_CHIPID_BCM4325:
rsrcs = PMURES_BIT(RES4325_OTP_PU);
break;
case BHND_CHIPID_BCM4329:
rsrcs = PMURES_BIT(RES4329_OTP_PU);
break;
case BHND_CHIPID_BCM4319:
rsrcs = PMURES_BIT(RES4319_OTP_PU);
break;
case BHND_CHIPID_BCM4336:
rsrcs = PMURES_BIT(RES4336_OTP_PU);
break;
case BHND_CHIPID_BCM4330:
rsrcs = PMURES_BIT(RES4330_OTP_PU);
break;
default:
return (0);
}
deps = bhnd_pmu_res_deps(sc, rsrcs, true);
if ((error = bhnd_pmu_res_masks(sc, &min_mask, NULL)))
return (error);
deps &= ~min_mask;
if (on) {
uint32_t state;
PMU_DEBUG(sc, "Adding rsrc 0x%x to min_res_mask\n",
rsrcs | deps);
BHND_PMU_OR_4(sc, BHND_PMU_MIN_RES_MASK, (rsrcs|deps));
for (int i = 0; i < BHND_PMU_MAX_TRANSITION_DLY; i += 10) {
state = BHND_PMU_READ_4(sc, BHND_PMU_RES_STATE);
if ((state & rsrcs) == rsrcs)
break;
DELAY(10);
}
if ((state & rsrcs) != rsrcs) {
PMU_LOG(sc, "timeout waiting for OTP resource "
"enable\n");
return (ENXIO);
}
} else {
PMU_DEBUG(sc, "Removing rsrc 0x%x from min_res_mask\n",
rsrcs | deps);
BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~(rsrcs|deps));
}
return (0);
}
void
bhnd_pmu_rcal(struct bhnd_pmu_softc *sc)
{
uint32_t chipst;
uint32_t val;
uint8_t rcal_code;
bool bluetooth_rcal;
bluetooth_rcal = false;
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4325:
case BHND_CHIPID_BCM4329:
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_ADDR, 1);
BHND_PMU_AND_4(sc, BHND_PMU_CHIP_CONTROL_DATA, ~0x04);
if (sc->cid.chip_id == BHND_CHIPID_BCM4325) {
chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev);
if (BHND_PMU_GET_BITS(chipst, CHIPC_CST4325_RCAL_VALID))
bluetooth_rcal = true;
}
BHND_PMU_AND_4(sc, BHND_PMU_CHIP_CONTROL_DATA, 0x04);
for (int i = 0; i < (10 * 1000 * 1000); i++) {
chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev);
if (chipst & 0x08)
break;
DELAY(10);
}
KASSERT((chipst & 0x08) != 0, ("rcal completion timeout"));
if (bluetooth_rcal) {
rcal_code = 0x6;
} else {
rcal_code = (uint8_t) (chipst >> 5) & 0x0f;
}
PMU_DEBUG("RCal completed, status 0x%x, code 0x%x\n",
R_REG(&cc->chipstatus), rcal_code);
BHND_PMU_WRITE_4(sc, BHND_PMU_REG_CONTROL_ADDR, 0);
val = BHND_PMU_READ_4(sc, BHND_PMU_REG_CONTROL_DATA);
val &= ~((uint32_t) 0x07 << 29);
val |= (uint32_t) (rcal_code & 0x07) << 29;
BHND_PMU_WRITE_4(sc, BHND_PMU_REG_CONTROL_DATA, val);
BHND_PMU_WRITE_4(sc, BHND_PMU_REG_CONTROL_ADDR, 1);
val = BHND_PMU_READ_4(sc, BHND_PMU_REG_CONTROL_DATA);
val &= ~(uint32_t) 0x01;
val |= (uint32_t) ((rcal_code >> 3) & 0x01);
BHND_PMU_WRITE_4(sc, BHND_PMU_REG_CONTROL_DATA, val);
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_ADDR, 0);
val = BHND_PMU_READ_4(sc, BHND_PMU_CHIP_CONTROL_DATA);
val &= ~((uint32_t) 0x03 << 30);
val |= (uint32_t) (rcal_code & 0x03) << 30;
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_DATA, val);
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_ADDR, 1);
val = BHND_PMU_READ_4(sc, BHND_PMU_CHIP_CONTROL_DATA);
val &= ~(uint32_t) 0x03;
val |= (uint32_t) ((rcal_code >> 2) & 0x03);
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_DATA, val);
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_ADDR, 0);
BHND_PMU_OR_4(sc, BHND_PMU_CHIP_CONTROL_DATA, (0x01 << 29));
BHND_PMU_WRITE_4(sc, BHND_PMU_CHIP_CONTROL_ADDR, 1);
BHND_PMU_AND_4(sc, BHND_PMU_CHIP_CONTROL_DATA, ~0x04);
break;
default:
break;
}
}
int
bhnd_pmu_set_spuravoid(struct bhnd_pmu_softc *sc, bhnd_pmu_spuravoid spuravoid)
{
int error;
if (sc->cid.chip_id == BHND_CHIPID_BCM4336) {
BHND_PMU_AND_4(sc, BHND_PMU_MAX_RES_MASK,
~BHND_PMU_RES4336_HT_AVAIL);
PMU_WAIT_CLKST(sc, 0, BHND_CCS_HTAVAIL);
}
error = bhnd_pmu_spuravoid_pllupdate(sc, spuravoid);
if (sc->cid.chip_id == BHND_CHIPID_BCM4336) {
BHND_PMU_OR_4(sc, BHND_PMU_MAX_RES_MASK,
BHND_PMU_RES4336_HT_AVAIL);
}
return (error);
}
static int
bhnd_pmu_spuravoid_pllupdate(struct bhnd_pmu_softc *sc,
bhnd_pmu_spuravoid spuravoid)
{
uint16_t chip_id;
uint32_t pmuctrl;
uint32_t tmp;
chip_id = sc->cid.chip_id;
if (chip_id == BHND_CHIPID_BCM6362 && sc->cid.chip_rev == 0) {
chip_id = BHND_CHIPID_BCM43224;
}
switch (chip_id) {
case BHND_CHIPID_BCM6362:
KASSERT(sc->cid.chip_rev != 0, ("invalid clock config"));
case BHND_CHIPID_BCM5357:
case BHND_CHIPID_BCM4749:
case BHND_CHIPID_BCM43235:
case BHND_CHIPID_BCM43236:
case BHND_CHIPID_BCM43238:
case BHND_CHIPID_BCM43234:
case BHND_CHIPID_BCM43237:
case BHND_CHIPID_BCM53572: {
uint8_t p1div, ndiv;
uint8_t phypll_offset;
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
p1div = 0x1;
ndiv = 0x30;
break;
case BHND_PMU_SPURAVOID_M1:
p1div = 0x5;
ndiv = 0xf6;
break;
case BHND_PMU_SPURAVOID_M2:
p1div = 0x5;
ndiv = 0xfc;
break;
default:
return (ENODEV);
}
phypll_offset = 0;
if (sc->cid.chip_id == BHND_CHIPID_BCM5357)
phypll_offset = 6;
tmp = BHND_PMU_SET_BITS(p1div, BHND_PMU1_PLL0_PC0_P1DIV);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0 + phypll_offset,
tmp, BHND_PMU1_PLL0_PC0_P1DIV_MASK);
tmp = BHND_PMU_SET_BITS(ndiv, BHND_PMU1_PLL0_PC2_NDIV_INT);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2 + phypll_offset,
tmp, BHND_PMU1_PLL0_PC0_P1DIV_MASK);
pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD;
break;
}
case BHND_CHIPID_BCM4331:
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11100014, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x03000a08, ~0);
break;
case BHND_PMU_SPURAVOID_M1:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500014, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0F600a08, ~0);
break;
case BHND_PMU_SPURAVOID_M2:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500014, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0FC00a08, ~0);
break;
default:
return (ENODEV);
}
pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD;
break;
case BHND_CHIPID_BCM43224:
case BHND_CHIPID_BCM43225:
case BHND_CHIPID_BCM43226:
case BHND_CHIPID_BCM43421:
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11100010, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x000c0c06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x03000a08, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00000000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x200005c0, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
break;
case BHND_PMU_SPURAVOID_M1:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500010, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x000C0C06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0F600a08, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00000000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x2001E920, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
break;
case BHND_PMU_SPURAVOID_M2:
default:
return (ENODEV);
}
pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD;
break;
case BHND_CHIPID_BCM43111:
case BHND_CHIPID_BCM43112:
case BHND_CHIPID_BCM43222:
case BHND_CHIPID_BCM43420:
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11100008, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x0c000c06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x03000a08, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00000000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x200005c0, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888855, ~0);
break;
case BHND_PMU_SPURAVOID_M1:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500008, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x0c000c06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0f600a08, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00000000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x2001e920, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
break;
case BHND_PMU_SPURAVOID_M2:
default:
return (ENODEV);
}
pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD;
break;
case BHND_CHIPID_BCM4716:
case BHND_CHIPID_BCM4748:
case BHND_CHIPID_BCM47162:
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11100060, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x080c0c06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x03000000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00000000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x200005c0, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
break;
case BHND_PMU_SPURAVOID_M1:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11500060, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x080C0C06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x0F600000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00000000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x2001E924, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
break;
case BHND_PMU_SPURAVOID_M2:
default:
return (ENODEV);
}
pmuctrl = BHND_PMU_CTRL_NOILP_ON_WAIT |
BHND_PMU_CTRL_PLL_PLLCTL_UPD;
break;
case BHND_CHIPID_BCM4319:
pmuctrl = 0;
break;
case BHND_CHIPID_BCM4322:
case BHND_CHIPID_BCM43221:
case BHND_CHIPID_BCM43231:
case BHND_CHIPID_BCM4342:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x11100070, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, 0x1014140a, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, 0x88888854, ~0);
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x05001828, ~0);
break;
case BHND_PMU_SPURAVOID_M1:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x05201828, ~0);
break;
case BHND_PMU_SPURAVOID_M2:
default:
return (ENODEV);
}
pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD;
break;
case BHND_CHIPID_BCM4336:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0, 0x02100020, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, 0x0C0C0C0C, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, 0x01240C0C, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4, 0x202C2820, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5, 0x88888825, ~0);
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00762762, ~0);
break;
case BHND_PMU_SPURAVOID_M1:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00EC4EC4, ~0);
break;
case BHND_PMU_SPURAVOID_M2:
default:
return (ENODEV);
}
pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD;
break;
case BHND_CHIPID_BCM43131:
case BHND_CHIPID_BCM43227:
case BHND_CHIPID_BCM43228:
case BHND_CHIPID_BCM43428:
switch (spuravoid) {
case BHND_PMU_SPURAVOID_NONE:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x11100014, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x040c0c06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x03000a08, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00000000, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x200005c0, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
break;
case BHND_PMU_SPURAVOID_M1:
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL0,
0x01100014, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1,
0x040C0C06, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2,
0x03140A08, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL3,
0x00333333, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL4,
0x202C2820, ~0);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL5,
0x88888815, ~0);
break;
case BHND_PMU_SPURAVOID_M2:
default:
return (ENODEV);
}
pmuctrl = BHND_PMU_CTRL_PLL_PLLCTL_UPD;
break;
default:
PMU_LOG(sc, "%s: unknown spuravoidance settings for chip %#hx, "
"not changing PLL", __func__, sc->cid.chip_id);
return (ENODEV);
}
if (pmuctrl != 0)
BHND_PMU_OR_4(sc, BHND_PMU_CTRL, pmuctrl);
return (0);
}
bool
bhnd_pmu_is_otp_powered(struct bhnd_pmu_softc *sc)
{
uint32_t otp_res;
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4329:
otp_res = PMURES_BIT(RES4329_OTP_PU);
break;
case BHND_CHIPID_BCM4319:
otp_res = PMURES_BIT(RES4319_OTP_PU);
break;
case BHND_CHIPID_BCM4336:
otp_res = PMURES_BIT(RES4336_OTP_PU);
break;
case BHND_CHIPID_BCM4330:
otp_res = PMURES_BIT(RES4330_OTP_PU);
break;
case BHND_CHIPID_BCM43224:
case BHND_CHIPID_BCM43225:
case BHND_CHIPID_BCM43421:
case BHND_CHIPID_BCM43236:
case BHND_CHIPID_BCM43235:
case BHND_CHIPID_BCM43238:
return (true);
default:
return (true);
}
if ((BHND_PMU_READ_4(sc, BHND_PMU_RES_STATE) & otp_res) == 0)
return (false);
return (true);
}
int
bhnd_pmu_paref_ldo_enable(struct bhnd_pmu_softc *sc, bool enable)
{
uint32_t ldo;
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4328:
ldo = PMURES_BIT(RES4328_PA_REF_LDO);
break;
case BHND_CHIPID_BCM5354:
ldo = PMURES_BIT(RES5354_PA_REF_LDO);
break;
case BHND_CHIPID_BCM4312:
ldo = PMURES_BIT(RES4312_PA_REF_LDO);
break;
default:
return (ENODEV);
}
if (enable) {
BHND_PMU_OR_4(sc, BHND_PMU_MIN_RES_MASK, ldo);
} else {
BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~ldo);
}
return (0);
}
void
bhnd_pmu_swreg_init(struct bhnd_pmu_softc *sc)
{
uint32_t chipst;
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4325:
if (sc->cid.chip_rev <= 2)
break;
chipst = BHND_CHIPC_READ_CHIPST(sc->chipc_dev);
if (BHND_PMU_GET_BITS(chipst, CHIPC_CST4325_PMUTOP_2B)) {
bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_CLDO_PWM,
0xf);
bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_CLDO_BURST,
0xf);
}
bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_CBUCK_PWM, 0xb);
bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_CBUCK_BURST, 0xb);
bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_LNLDO1, 0x1);
if (sc->board.board_flags & BHND_BFL_LNLDO2_2P5) {
bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_LNLDO2_SEL,
0x1);
}
break;
case BHND_CHIPID_BCM4336:
bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_CLDO_PWM, 0xe);
bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_CLDO_BURST, 0xe);
bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_LNLDO1, 0xe);
if (sc->cid.chip_rev == 0)
BHND_PMU_REGCTRL_WRITE(sc, 2, 0x400000, 0x400000);
break;
case BHND_CHIPID_BCM4330:
bhnd_pmu_set_ldo_voltage(sc, SET_LDO_VOLTAGE_CBUCK_PWM, 0);
break;
default:
break;
}
}
int
bhnd_pmu_radio_enable(struct bhnd_pmu_softc *sc, device_t d11core, bool enable)
{
uint32_t oobsel;
uint32_t rsrcs;
int error;
if (bhnd_get_device(d11core) != BHND_COREID_D11) {
device_printf(sc->dev,
"bhnd_pmu_radio_enable() called on non-D11 core");
return (EINVAL);
}
switch (sc->cid.chip_id) {
case BHND_CHIPID_BCM4325:
if (sc->board.board_flags & BHND_BFL_FASTPWR)
break;
if ((sc->board.board_flags & BHND_BFL_BUCKBOOST) == 0)
break;
rsrcs = PMURES_BIT(RES4325_BUCK_BOOST_BURST);
if (enable) {
BHND_PMU_OR_4(sc, BHND_PMU_MIN_RES_MASK, rsrcs);
DELAY(100 * 1000);
} else {
BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~rsrcs);
}
return (0);
case BHND_CHIPID_BCM4319:
error = bhnd_read_config(d11core, BCMA_DMP_OOBSELOUTB74,
&oobsel, 4);
if (error)
return (error);
if (enable) {
oobsel |= BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN,
BCMA_DMP_OOBSEL_5);
oobsel |= BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN,
BCMA_DMP_OOBSEL_6);
} else {
oobsel &= ~BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN,
BCMA_DMP_OOBSEL_5);
oobsel &= ~BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN,
BCMA_DMP_OOBSEL_6);
}
return (bhnd_write_config(d11core, BCMA_DMP_OOBSELOUTB74,
&oobsel, 4));
}
return (0);
}
uint32_t
bhnd_pmu_waitforclk_on_backplane(struct bhnd_pmu_softc *sc, uint32_t clk,
uint32_t delay)
{
uint32_t pmu_st;
for (uint32_t i = 0; i < delay; i += 10) {
pmu_st = BHND_PMU_READ_4(sc, BHND_PMU_ST);
if ((pmu_st & clk) == clk)
return (clk);
DELAY(10);
}
pmu_st = BHND_PMU_READ_4(sc, BHND_PMU_ST);
return (pmu_st & clk);
}
#define EXT_ILP_HZ 32768
uint32_t
bhnd_pmu_measure_alpclk(struct bhnd_pmu_softc *sc)
{
uint32_t alp_khz;
uint32_t pmu_st;
if (BHND_PMU_REV(sc) < 10)
return (0);
pmu_st = BHND_PMU_READ_4(sc, BHND_PMU_ST);
if (pmu_st & BHND_PMU_ST_EXTLPOAVAIL) {
uint32_t alp_hz, ilp_ctr;
BHND_PMU_WRITE_4(sc, BHND_PMU_XTALFREQ, 1U <<
BHND_PMU_XTALFREQ_REG_MEASURE_SHIFT);
DELAY(1000);
ilp_ctr = BHND_PMU_READ_4(sc, BHND_PMU_XTALFREQ);
ilp_ctr = BHND_PMU_GET_BITS(ilp_ctr,
BHND_PMU_XTALFREQ_REG_ILPCTR);
BHND_PMU_WRITE_4(sc, BHND_PMU_XTALFREQ, 0);
alp_hz = (ilp_ctr * EXT_ILP_HZ) / 4;
alp_khz = (alp_hz + 50000) / 100000 * 100;
} else {
alp_khz = 0;
}
return (alp_khz);
}
static void
bhnd_pmu_set_4330_plldivs(struct bhnd_pmu_softc *sc)
{
uint32_t FVCO = bhnd_pmu1_pllfvco0(&sc->query) / 1000;
uint32_t m1div, m2div, m3div, m4div, m5div, m6div;
uint32_t pllc1, pllc2;
m2div = m3div = m4div = m6div = FVCO / 80;
m5div = FVCO / 160;
if (PMU_CST4330_SDIOD_CHIPMODE(sc))
m1div = FVCO / 80;
else
m1div = FVCO / 90;
pllc1 = 0;
pllc1 |= BHND_PMU_SET_BITS(m1div, BHND_PMU1_PLL0_PC1_M1DIV);
pllc1 |= BHND_PMU_SET_BITS(m2div, BHND_PMU1_PLL0_PC1_M2DIV);
pllc1 |= BHND_PMU_SET_BITS(m3div, BHND_PMU1_PLL0_PC1_M3DIV);
pllc1 |= BHND_PMU_SET_BITS(m4div, BHND_PMU1_PLL0_PC1_M4DIV);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL1, pllc1, ~0);
pllc2 = 0;
pllc2 |= BHND_PMU_SET_BITS(m5div, BHND_PMU1_PLL0_PC2_M5DIV);
pllc2 |= BHND_PMU_SET_BITS(m6div, BHND_PMU1_PLL0_PC2_M6DIV);
BHND_PMU_PLL_WRITE(sc, BHND_PMU1_PLL0_PLLCTL2, pllc2,
BHND_PMU1_PLL0_PC2_M5DIV_MASK | BHND_PMU1_PLL0_PC2_M6DIV_MASK);
}