| /* |
| * Misc utility routines for accessing chip-specific features |
| * of the SiliconBackplane-based Broadcom chips. |
| * |
| * Copyright (C) 1999-2011, Broadcom Corporation |
| * |
| * Unless you and Broadcom execute a separate written software license |
| * agreement governing use of this software, this software is licensed to you |
| * under the terms of the GNU General Public License version 2 (the "GPL"), |
| * available at http://www.broadcom.com/licenses/GPLv2.php, with the |
| * following added to such license: |
| * |
| * As a special exception, the copyright holders of this software give you |
| * permission to link this software with independent modules, and to copy and |
| * distribute the resulting executable under terms of your choice, provided that |
| * you also meet, for each linked independent module, the terms and conditions of |
| * the license of that module. An independent module is a module which is not |
| * derived from this software. The special exception does not apply to any |
| * modifications of the software. |
| * |
| * Notwithstanding the above, under no circumstances may you combine this |
| * software in any way with any other Broadcom software provided under a license |
| * other than the GPL, without Broadcom's express prior written consent. |
| * |
| * $Id: siutils.c,v 1.813.2.36 2011-02-10 23:43:55 Exp $ |
| */ |
| |
| #include <typedefs.h> |
| #include <bcmdefs.h> |
| #include <osl.h> |
| #include <bcmutils.h> |
| #include <siutils.h> |
| #include <bcmdevs.h> |
| #include <hndsoc.h> |
| #include <sbchipc.h> |
| #include <pcicfg.h> |
| #include <sbpcmcia.h> |
| #include <sbsocram.h> |
| #include <bcmsdh.h> |
| #include <sdio.h> |
| #include <sbsdio.h> |
| #include <sbhnddma.h> |
| #include <sbsdpcmdev.h> |
| #include <bcmsdpcm.h> |
| #include <hndpmu.h> |
| |
| #include "siutils_priv.h" |
| |
| /* local prototypes */ |
| static si_info_t *si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs, |
| uint bustype, void *sdh, char **vars, uint *varsz); |
| static bool si_buscore_prep(si_info_t *sii, uint bustype, uint devid, void *sdh); |
| static bool si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin, |
| uint *origidx, void *regs); |
| |
| |
| /* global variable to indicate reservation/release of gpio's */ |
| static uint32 si_gpioreservation = 0; |
| |
| /* global flag to prevent shared resources from being initialized multiple times in si_attach() */ |
| |
| /* |
| * Allocate a si handle. |
| * devid - pci device id (used to determine chip#) |
| * osh - opaque OS handle |
| * regs - virtual address of initial core registers |
| * bustype - pci/pcmcia/sb/sdio/etc |
| * vars - pointer to a pointer area for "environment" variables |
| * varsz - pointer to int to return the size of the vars |
| */ |
| si_t * |
| si_attach(uint devid, osl_t *osh, void *regs, |
| uint bustype, void *sdh, char **vars, uint *varsz) |
| { |
| si_info_t *sii; |
| |
| /* alloc si_info_t */ |
| if ((sii = MALLOC(osh, sizeof (si_info_t))) == NULL) { |
| SI_ERROR(("si_attach: malloc failed! malloced %d bytes\n", MALLOCED(osh))); |
| return (NULL); |
| } |
| |
| if (si_doattach(sii, devid, osh, regs, bustype, sdh, vars, varsz) == NULL) { |
| MFREE(osh, sii, sizeof(si_info_t)); |
| return (NULL); |
| } |
| sii->vars = vars ? *vars : NULL; |
| sii->varsz = varsz ? *varsz : 0; |
| |
| return (si_t *)sii; |
| } |
| |
| /* global kernel resource */ |
| static si_info_t ksii; |
| |
| static uint32 wd_msticks; /* watchdog timer ticks normalized to ms */ |
| |
| /* generic kernel variant of si_attach() */ |
| si_t * |
| si_kattach(osl_t *osh) |
| { |
| static bool ksii_attached = FALSE; |
| |
| if (!ksii_attached) { |
| void *regs; |
| regs = REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE); |
| |
| if (si_doattach(&ksii, BCM4710_DEVICE_ID, osh, regs, |
| SI_BUS, NULL, |
| osh != SI_OSH ? &ksii.vars : NULL, |
| osh != SI_OSH ? &ksii.varsz : NULL) == NULL) { |
| SI_ERROR(("si_kattach: si_doattach failed\n")); |
| REG_UNMAP(regs); |
| return NULL; |
| } |
| REG_UNMAP(regs); |
| |
| /* save ticks normalized to ms for si_watchdog_ms() */ |
| if (PMUCTL_ENAB(&ksii.pub)) { |
| /* based on 32KHz ILP clock */ |
| wd_msticks = 32; |
| } else { |
| wd_msticks = ALP_CLOCK / 1000; |
| } |
| |
| ksii_attached = TRUE; |
| SI_MSG(("si_kattach done. ccrev = %d, wd_msticks = %d\n", |
| ksii.pub.ccrev, wd_msticks)); |
| } |
| |
| return &ksii.pub; |
| } |
| |
| |
| static bool |
| si_buscore_prep(si_info_t *sii, uint bustype, uint devid, void *sdh) |
| { |
| /* need to set memseg flag for CF card first before any sb registers access */ |
| if (BUSTYPE(bustype) == PCMCIA_BUS) |
| sii->memseg = TRUE; |
| |
| |
| if (BUSTYPE(bustype) == SDIO_BUS) { |
| int err; |
| uint8 clkset; |
| |
| /* Try forcing SDIO core to do ALPAvail request only */ |
| clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; |
| bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); |
| if (!err) { |
| uint8 clkval; |
| |
| /* If register supported, wait for ALPAvail and then force ALP */ |
| clkval = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, NULL); |
| if ((clkval & ~SBSDIO_AVBITS) == clkset) { |
| SPINWAIT(((clkval = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, |
| SBSDIO_FUNC1_CHIPCLKCSR, NULL)), !SBSDIO_ALPAV(clkval)), |
| PMU_MAX_TRANSITION_DLY); |
| if (!SBSDIO_ALPAV(clkval)) { |
| SI_ERROR(("timeout on ALPAV wait, clkval 0x%02x\n", |
| clkval)); |
| return FALSE; |
| } |
| clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; |
| bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, |
| clkset, &err); |
| OSL_DELAY(65); |
| } |
| } |
| |
| /* Also, disable the extra SDIO pull-ups */ |
| bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); |
| } |
| |
| |
| return TRUE; |
| } |
| |
| static bool |
| si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin, |
| uint *origidx, void *regs) |
| { |
| bool pci, pcie; |
| uint i; |
| uint pciidx, pcieidx, pcirev, pcierev; |
| |
| cc = si_setcoreidx(&sii->pub, SI_CC_IDX); |
| ASSERT((uintptr)cc); |
| |
| /* get chipcommon rev */ |
| sii->pub.ccrev = (int)si_corerev(&sii->pub); |
| |
| /* get chipcommon chipstatus */ |
| if (sii->pub.ccrev >= 11) |
| sii->pub.chipst = R_REG(sii->osh, &cc->chipstatus); |
| |
| /* get chipcommon capabilites */ |
| sii->pub.cccaps = R_REG(sii->osh, &cc->capabilities); |
| /* get chipcommon extended capabilities */ |
| |
| if (sii->pub.ccrev >= 35) |
| sii->pub.cccaps_ext = R_REG(sii->osh, &cc->capabilities_ext); |
| |
| /* get pmu rev and caps */ |
| if (sii->pub.cccaps & CC_CAP_PMU) { |
| sii->pub.pmucaps = R_REG(sii->osh, &cc->pmucapabilities); |
| sii->pub.pmurev = sii->pub.pmucaps & PCAP_REV_MASK; |
| } |
| |
| SI_MSG(("Chipc: rev %d, caps 0x%x, chipst 0x%x pmurev %d, pmucaps 0x%x\n", |
| sii->pub.ccrev, sii->pub.cccaps, sii->pub.chipst, sii->pub.pmurev, |
| sii->pub.pmucaps)); |
| |
| /* figure out bus/orignal core idx */ |
| sii->pub.buscoretype = NODEV_CORE_ID; |
| sii->pub.buscorerev = NOREV; |
| sii->pub.buscoreidx = BADIDX; |
| |
| pci = pcie = FALSE; |
| pcirev = pcierev = NOREV; |
| pciidx = pcieidx = BADIDX; |
| |
| for (i = 0; i < sii->numcores; i++) { |
| uint cid, crev; |
| |
| si_setcoreidx(&sii->pub, i); |
| cid = si_coreid(&sii->pub); |
| crev = si_corerev(&sii->pub); |
| |
| /* Display cores found */ |
| SI_VMSG(("CORE[%d]: id 0x%x rev %d base 0x%x regs 0x%p\n", |
| i, cid, crev, sii->coresba[i], sii->regs[i])); |
| |
| if (BUSTYPE(bustype) == PCI_BUS) { |
| if (cid == PCI_CORE_ID) { |
| pciidx = i; |
| pcirev = crev; |
| pci = TRUE; |
| } else if (cid == PCIE_CORE_ID) { |
| pcieidx = i; |
| pcierev = crev; |
| pcie = TRUE; |
| } |
| } else if ((BUSTYPE(bustype) == PCMCIA_BUS) && |
| (cid == PCMCIA_CORE_ID)) { |
| sii->pub.buscorerev = crev; |
| sii->pub.buscoretype = cid; |
| sii->pub.buscoreidx = i; |
| } |
| else if (((BUSTYPE(bustype) == SDIO_BUS) || |
| (BUSTYPE(bustype) == SPI_BUS)) && |
| ((cid == PCMCIA_CORE_ID) || |
| (cid == SDIOD_CORE_ID))) { |
| sii->pub.buscorerev = crev; |
| sii->pub.buscoretype = cid; |
| sii->pub.buscoreidx = i; |
| } |
| |
| /* find the core idx before entering this func. */ |
| if ((savewin && (savewin == sii->coresba[i])) || |
| (regs == sii->regs[i])) |
| *origidx = i; |
| } |
| |
| if (pci) { |
| sii->pub.buscoretype = PCI_CORE_ID; |
| sii->pub.buscorerev = pcirev; |
| sii->pub.buscoreidx = pciidx; |
| } else if (pcie) { |
| sii->pub.buscoretype = PCIE_CORE_ID; |
| sii->pub.buscorerev = pcierev; |
| sii->pub.buscoreidx = pcieidx; |
| } |
| |
| SI_VMSG(("Buscore id/type/rev %d/0x%x/%d\n", sii->pub.buscoreidx, sii->pub.buscoretype, |
| sii->pub.buscorerev)); |
| |
| if (BUSTYPE(sii->pub.bustype) == SI_BUS && (CHIPID(sii->pub.chip) == BCM4712_CHIP_ID) && |
| (sii->pub.chippkg != BCM4712LARGE_PKG_ID) && (CHIPREV(sii->pub.chiprev) <= 3)) |
| OR_REG(sii->osh, &cc->slow_clk_ctl, SCC_SS_XTAL); |
| |
| |
| /* Make sure any on-chip ARM is off (in case strapping is wrong), or downloaded code was |
| * already running. |
| */ |
| if ((BUSTYPE(bustype) == SDIO_BUS) || (BUSTYPE(bustype) == SPI_BUS)) { |
| if (si_setcore(&sii->pub, ARM7S_CORE_ID, 0) || |
| si_setcore(&sii->pub, ARMCM3_CORE_ID, 0)) |
| si_core_disable(&sii->pub, 0); |
| } |
| |
| /* return to the original core */ |
| si_setcoreidx(&sii->pub, *origidx); |
| |
| return TRUE; |
| } |
| |
| |
| |
| static si_info_t * |
| si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs, |
| uint bustype, void *sdh, char **vars, uint *varsz) |
| { |
| struct si_pub *sih = &sii->pub; |
| uint32 w, savewin; |
| chipcregs_t *cc; |
| char *pvars = NULL; |
| uint origidx; |
| |
| ASSERT(GOODREGS(regs)); |
| |
| bzero((uchar*)sii, sizeof(si_info_t)); |
| |
| savewin = 0; |
| |
| sih->buscoreidx = BADIDX; |
| |
| sii->curmap = regs; |
| sii->sdh = sdh; |
| sii->osh = osh; |
| |
| |
| |
| /* find Chipcommon address */ |
| if (bustype == PCI_BUS) { |
| savewin = OSL_PCI_READ_CONFIG(sii->osh, PCI_BAR0_WIN, sizeof(uint32)); |
| if (!GOODCOREADDR(savewin, SI_ENUM_BASE)) |
| savewin = SI_ENUM_BASE; |
| OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, SI_ENUM_BASE); |
| cc = (chipcregs_t *)regs; |
| } else if ((bustype == SDIO_BUS) || (bustype == SPI_BUS)) { |
| cc = (chipcregs_t *)sii->curmap; |
| } else { |
| cc = (chipcregs_t *)REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE); |
| } |
| |
| sih->bustype = bustype; |
| if (bustype != BUSTYPE(bustype)) { |
| SI_ERROR(("si_doattach: bus type %d does not match configured bus type %d\n", |
| bustype, BUSTYPE(bustype))); |
| return NULL; |
| } |
| |
| /* bus/core/clk setup for register access */ |
| if (!si_buscore_prep(sii, bustype, devid, sdh)) { |
| SI_ERROR(("si_doattach: si_core_clk_prep failed %d\n", bustype)); |
| return NULL; |
| } |
| |
| /* ChipID recognition. |
| * We assume we can read chipid at offset 0 from the regs arg. |
| * If we add other chiptypes (or if we need to support old sdio hosts w/o chipcommon), |
| * some way of recognizing them needs to be added here. |
| */ |
| w = R_REG(osh, &cc->chipid); |
| sih->socitype = (w & CID_TYPE_MASK) >> CID_TYPE_SHIFT; |
| /* Might as wll fill in chip id rev & pkg */ |
| sih->chip = w & CID_ID_MASK; |
| sih->chiprev = (w & CID_REV_MASK) >> CID_REV_SHIFT; |
| sih->chippkg = (w & CID_PKG_MASK) >> CID_PKG_SHIFT; |
| if (CHIPID(sih->chip) == BCM4322_CHIP_ID && (((sih->chipst & CST4322_SPROM_OTP_SEL_MASK) |
| >> CST4322_SPROM_OTP_SEL_SHIFT) == (CST4322_OTP_PRESENT | |
| CST4322_SPROM_PRESENT))) { |
| SI_ERROR(("%s: Invalid setting: both SPROM and OTP strapped.\n", __FUNCTION__)); |
| return NULL; |
| } |
| |
| if ((CHIPID(sih->chip) == BCM4329_CHIP_ID) && (sih->chiprev == 0) && |
| (sih->chippkg != BCM4329_289PIN_PKG_ID)) { |
| sih->chippkg = BCM4329_182PIN_PKG_ID; |
| } |
| |
| sih->issim = IS_SIM(sih->chippkg); |
| |
| /* scan for cores */ |
| if (CHIPTYPE(sii->pub.socitype) == SOCI_SB) { |
| SI_MSG(("Found chip type SB (0x%08x)\n", w)); |
| sb_scan(&sii->pub, regs, devid); |
| } else if (CHIPTYPE(sii->pub.socitype) == SOCI_AI) { |
| SI_MSG(("Found chip type AI (0x%08x)\n", w)); |
| /* pass chipc address instead of original core base */ |
| ai_scan(&sii->pub, (void *)(uintptr)cc, devid); |
| } else if (CHIPTYPE(sii->pub.socitype) == SOCI_UBUS) { |
| SI_MSG(("Found chip type UBUS (0x%08x), chip id = 0x%4x\n", w, sih->chip)); |
| /* pass chipc address instead of original core base */ |
| ub_scan(&sii->pub, (void *)(uintptr)cc, devid); |
| } else { |
| SI_ERROR(("Found chip of unknown type (0x%08x)\n", w)); |
| return NULL; |
| } |
| /* no cores found, bail out */ |
| if (sii->numcores == 0) { |
| SI_ERROR(("si_doattach: could not find any cores\n")); |
| return NULL; |
| } |
| /* bus/core/clk setup */ |
| origidx = SI_CC_IDX; |
| if (!si_buscore_setup(sii, cc, bustype, savewin, &origidx, regs)) { |
| SI_ERROR(("si_doattach: si_buscore_setup failed\n")); |
| goto exit; |
| } |
| |
| /* assume current core is CC */ |
| if ((sii->pub.ccrev == 0x25) && ((CHIPID(sih->chip) == BCM43236_CHIP_ID || |
| CHIPID(sih->chip) == BCM43235_CHIP_ID || |
| CHIPID(sih->chip) == BCM43238_CHIP_ID) && |
| (CHIPREV(sii->pub.chiprev) == 0))) { |
| |
| if ((cc->chipstatus & CST43236_BP_CLK) != 0) { |
| uint clkdiv; |
| clkdiv = R_REG(osh, &cc->clkdiv); |
| /* otp_clk_div is even number, 120/14 < 9mhz */ |
| clkdiv = (clkdiv & ~CLKD_OTP) | (14 << CLKD_OTP_SHIFT); |
| W_REG(osh, &cc->clkdiv, clkdiv); |
| SI_ERROR(("%s: set clkdiv to %x\n", __FUNCTION__, clkdiv)); |
| } |
| OSL_DELAY(10); |
| } |
| |
| |
| pvars = NULL; |
| |
| |
| |
| if (sii->pub.ccrev >= 20) { |
| cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); |
| ASSERT(cc != NULL); |
| W_REG(osh, &cc->gpiopullup, 0); |
| W_REG(osh, &cc->gpiopulldown, 0); |
| si_setcoreidx(sih, origidx); |
| } |
| |
| |
| |
| |
| return (sii); |
| |
| exit: |
| |
| return NULL; |
| } |
| |
| /* may be called with core in reset */ |
| void |
| si_detach(si_t *sih) |
| { |
| si_info_t *sii; |
| uint idx; |
| |
| |
| sii = SI_INFO(sih); |
| |
| if (sii == NULL) |
| return; |
| |
| if (BUSTYPE(sih->bustype) == SI_BUS) |
| for (idx = 0; idx < SI_MAXCORES; idx++) |
| if (sii->regs[idx]) { |
| REG_UNMAP(sii->regs[idx]); |
| sii->regs[idx] = NULL; |
| } |
| |
| |
| |
| #if !defined(BCMBUSTYPE) || (BCMBUSTYPE == SI_BUS) |
| if (sii != &ksii) |
| #endif /* !BCMBUSTYPE || (BCMBUSTYPE == SI_BUS) */ |
| MFREE(sii->osh, sii, sizeof(si_info_t)); |
| } |
| |
| void * |
| si_osh(si_t *sih) |
| { |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| return sii->osh; |
| } |
| |
| void |
| si_setosh(si_t *sih, osl_t *osh) |
| { |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| if (sii->osh != NULL) { |
| SI_ERROR(("osh is already set....\n")); |
| ASSERT(!sii->osh); |
| } |
| sii->osh = osh; |
| } |
| |
| /* register driver interrupt disabling and restoring callback functions */ |
| void |
| si_register_intr_callback(si_t *sih, void *intrsoff_fn, void *intrsrestore_fn, |
| void *intrsenabled_fn, void *intr_arg) |
| { |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| sii->intr_arg = intr_arg; |
| sii->intrsoff_fn = (si_intrsoff_t)intrsoff_fn; |
| sii->intrsrestore_fn = (si_intrsrestore_t)intrsrestore_fn; |
| sii->intrsenabled_fn = (si_intrsenabled_t)intrsenabled_fn; |
| /* save current core id. when this function called, the current core |
| * must be the core which provides driver functions(il, et, wl, etc.) |
| */ |
| sii->dev_coreid = sii->coreid[sii->curidx]; |
| } |
| |
| void |
| si_deregister_intr_callback(si_t *sih) |
| { |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| sii->intrsoff_fn = NULL; |
| } |
| |
| uint |
| si_intflag(si_t *sih) |
| { |
| si_info_t *sii = SI_INFO(sih); |
| |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| return sb_intflag(sih); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| return R_REG(sii->osh, ((uint32 *)(uintptr) |
| (sii->oob_router + OOB_STATUSA))); |
| else { |
| ASSERT(0); |
| return 0; |
| } |
| } |
| |
| uint |
| si_flag(si_t *sih) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| return sb_flag(sih); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| return ai_flag(sih); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| return ub_flag(sih); |
| else { |
| ASSERT(0); |
| return 0; |
| } |
| } |
| |
| void |
| si_setint(si_t *sih, int siflag) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| sb_setint(sih, siflag); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| ai_setint(sih, siflag); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| ub_setint(sih, siflag); |
| else |
| ASSERT(0); |
| } |
| |
| uint |
| si_coreid(si_t *sih) |
| { |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| return sii->coreid[sii->curidx]; |
| } |
| |
| uint |
| si_coreidx(si_t *sih) |
| { |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| return sii->curidx; |
| } |
| |
| /* return the core-type instantiation # of the current core */ |
| uint |
| si_coreunit(si_t *sih) |
| { |
| si_info_t *sii; |
| uint idx; |
| uint coreid; |
| uint coreunit; |
| uint i; |
| |
| sii = SI_INFO(sih); |
| coreunit = 0; |
| |
| idx = sii->curidx; |
| |
| ASSERT(GOODREGS(sii->curmap)); |
| coreid = si_coreid(sih); |
| |
| /* count the cores of our type */ |
| for (i = 0; i < idx; i++) |
| if (sii->coreid[i] == coreid) |
| coreunit++; |
| |
| return (coreunit); |
| } |
| |
| uint |
| si_corevendor(si_t *sih) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| return sb_corevendor(sih); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| return ai_corevendor(sih); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| return ub_corevendor(sih); |
| else { |
| ASSERT(0); |
| return 0; |
| } |
| } |
| |
| bool |
| si_backplane64(si_t *sih) |
| { |
| return ((sih->cccaps & CC_CAP_BKPLN64) != 0); |
| } |
| |
| uint |
| si_corerev(si_t *sih) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| return sb_corerev(sih); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| return ai_corerev(sih); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| return ub_corerev(sih); |
| else { |
| ASSERT(0); |
| return 0; |
| } |
| } |
| |
| /* return index of coreid or BADIDX if not found */ |
| uint |
| si_findcoreidx(si_t *sih, uint coreid, uint coreunit) |
| { |
| si_info_t *sii; |
| uint found; |
| uint i; |
| |
| sii = SI_INFO(sih); |
| |
| found = 0; |
| |
| for (i = 0; i < sii->numcores; i++) |
| if (sii->coreid[i] == coreid) { |
| if (found == coreunit) |
| return (i); |
| found++; |
| } |
| |
| return (BADIDX); |
| } |
| |
| /* return list of found cores */ |
| uint |
| si_corelist(si_t *sih, uint coreid[]) |
| { |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| |
| bcopy((uchar*)sii->coreid, (uchar*)coreid, (sii->numcores * sizeof(uint))); |
| return (sii->numcores); |
| } |
| |
| /* return current register mapping */ |
| void * |
| si_coreregs(si_t *sih) |
| { |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| ASSERT(GOODREGS(sii->curmap)); |
| |
| return (sii->curmap); |
| } |
| |
| /* |
| * This function changes logical "focus" to the indicated core; |
| * must be called with interrupts off. |
| * Moreover, callers should keep interrupts off during switching out of and back to d11 core |
| */ |
| void * |
| si_setcore(si_t *sih, uint coreid, uint coreunit) |
| { |
| uint idx; |
| |
| idx = si_findcoreidx(sih, coreid, coreunit); |
| if (!GOODIDX(idx)) |
| return (NULL); |
| |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| return sb_setcoreidx(sih, idx); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| return ai_setcoreidx(sih, idx); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| return ub_setcoreidx(sih, idx); |
| else { |
| ASSERT(0); |
| return NULL; |
| } |
| } |
| |
| void * |
| si_setcoreidx(si_t *sih, uint coreidx) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| return sb_setcoreidx(sih, coreidx); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| return ai_setcoreidx(sih, coreidx); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| return ub_setcoreidx(sih, coreidx); |
| else { |
| ASSERT(0); |
| return NULL; |
| } |
| } |
| |
| /* Turn off interrupt as required by sb_setcore, before switch core */ |
| void * |
| si_switch_core(si_t *sih, uint coreid, uint *origidx, uint *intr_val) |
| { |
| void *cc; |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| |
| if (SI_FAST(sii)) { |
| /* Overloading the origidx variable to remember the coreid, |
| * this works because the core ids cannot be confused with |
| * core indices. |
| */ |
| *origidx = coreid; |
| if (coreid == CC_CORE_ID) |
| return (void *)CCREGS_FAST(sii); |
| else if (coreid == sih->buscoretype) |
| return (void *)PCIEREGS(sii); |
| } |
| INTR_OFF(sii, *intr_val); |
| *origidx = sii->curidx; |
| cc = si_setcore(sih, coreid, 0); |
| ASSERT(cc != NULL); |
| |
| return cc; |
| } |
| |
| /* restore coreidx and restore interrupt */ |
| void |
| si_restore_core(si_t *sih, uint coreid, uint intr_val) |
| { |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| if (SI_FAST(sii) && ((coreid == CC_CORE_ID) || (coreid == sih->buscoretype))) |
| return; |
| |
| si_setcoreidx(sih, coreid); |
| INTR_RESTORE(sii, intr_val); |
| } |
| |
| int |
| si_numaddrspaces(si_t *sih) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| return sb_numaddrspaces(sih); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| return ai_numaddrspaces(sih); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| return ub_numaddrspaces(sih); |
| else { |
| ASSERT(0); |
| return 0; |
| } |
| } |
| |
| uint32 |
| si_addrspace(si_t *sih, uint asidx) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| return sb_addrspace(sih, asidx); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| return ai_addrspace(sih, asidx); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| return ub_addrspace(sih, asidx); |
| else { |
| ASSERT(0); |
| return 0; |
| } |
| } |
| |
| uint32 |
| si_addrspacesize(si_t *sih, uint asidx) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| return sb_addrspacesize(sih, asidx); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| return ai_addrspacesize(sih, asidx); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| return ub_addrspacesize(sih, asidx); |
| else { |
| ASSERT(0); |
| return 0; |
| } |
| } |
| |
| uint32 |
| si_core_cflags(si_t *sih, uint32 mask, uint32 val) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| return sb_core_cflags(sih, mask, val); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| return ai_core_cflags(sih, mask, val); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| return ub_core_cflags(sih, mask, val); |
| else { |
| ASSERT(0); |
| return 0; |
| } |
| } |
| |
| void |
| si_core_cflags_wo(si_t *sih, uint32 mask, uint32 val) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| sb_core_cflags_wo(sih, mask, val); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| ai_core_cflags_wo(sih, mask, val); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| ub_core_cflags_wo(sih, mask, val); |
| else |
| ASSERT(0); |
| } |
| |
| uint32 |
| si_core_sflags(si_t *sih, uint32 mask, uint32 val) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| return sb_core_sflags(sih, mask, val); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| return ai_core_sflags(sih, mask, val); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| return ub_core_sflags(sih, mask, val); |
| else { |
| ASSERT(0); |
| return 0; |
| } |
| } |
| |
| bool |
| si_iscoreup(si_t *sih) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| return sb_iscoreup(sih); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| return ai_iscoreup(sih); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| return ub_iscoreup(sih); |
| else { |
| ASSERT(0); |
| return FALSE; |
| } |
| } |
| |
| uint |
| si_wrapperreg(si_t *sih, uint32 offset, uint32 mask, uint32 val) |
| { |
| /* only for AI back plane chips */ |
| if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| return (ai_wrap_reg(sih, offset, mask, val)); |
| return 0; |
| } |
| |
| uint |
| si_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| return sb_corereg(sih, coreidx, regoff, mask, val); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| return ai_corereg(sih, coreidx, regoff, mask, val); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| return ub_corereg(sih, coreidx, regoff, mask, val); |
| else { |
| ASSERT(0); |
| return 0; |
| } |
| } |
| |
| void |
| si_core_disable(si_t *sih, uint32 bits) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| sb_core_disable(sih, bits); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| ai_core_disable(sih, bits); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| ub_core_disable(sih, bits); |
| } |
| |
| void |
| si_core_reset(si_t *sih, uint32 bits, uint32 resetbits) |
| { |
| if (CHIPTYPE(sih->socitype) == SOCI_SB) |
| sb_core_reset(sih, bits, resetbits); |
| else if (CHIPTYPE(sih->socitype) == SOCI_AI) |
| ai_core_reset(sih, bits, resetbits); |
| else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) |
| ub_core_reset(sih, bits, resetbits); |
| } |
| |
| /* Run bist on current core. Caller needs to take care of core-specific bist hazards */ |
| int |
| si_corebist(si_t *sih) |
| { |
| uint32 cflags; |
| int result = 0; |
| |
| /* Read core control flags */ |
| cflags = si_core_cflags(sih, 0, 0); |
| |
| /* Set bist & fgc */ |
| si_core_cflags(sih, ~0, (SICF_BIST_EN | SICF_FGC)); |
| |
| /* Wait for bist done */ |
| SPINWAIT(((si_core_sflags(sih, 0, 0) & SISF_BIST_DONE) == 0), 100000); |
| |
| if (si_core_sflags(sih, 0, 0) & SISF_BIST_ERROR) |
| result = BCME_ERROR; |
| |
| /* Reset core control flags */ |
| si_core_cflags(sih, 0xffff, cflags); |
| |
| return result; |
| } |
| |
| static uint32 |
| factor6(uint32 x) |
| { |
| switch (x) { |
| case CC_F6_2: return 2; |
| case CC_F6_3: return 3; |
| case CC_F6_4: return 4; |
| case CC_F6_5: return 5; |
| case CC_F6_6: return 6; |
| case CC_F6_7: return 7; |
| default: return 0; |
| } |
| } |
| |
| /* calculate the speed the SI would run at given a set of clockcontrol values */ |
| uint32 |
| si_clock_rate(uint32 pll_type, uint32 n, uint32 m) |
| { |
| uint32 n1, n2, clock, m1, m2, m3, mc; |
| |
| n1 = n & CN_N1_MASK; |
| n2 = (n & CN_N2_MASK) >> CN_N2_SHIFT; |
| |
| if (pll_type == PLL_TYPE6) { |
| if (m & CC_T6_MMASK) |
| return CC_T6_M1; |
| else |
| return CC_T6_M0; |
| } else if ((pll_type == PLL_TYPE1) || |
| (pll_type == PLL_TYPE3) || |
| (pll_type == PLL_TYPE4) || |
| (pll_type == PLL_TYPE7)) { |
| n1 = factor6(n1); |
| n2 += CC_F5_BIAS; |
| } else if (pll_type == PLL_TYPE2) { |
| n1 += CC_T2_BIAS; |
| n2 += CC_T2_BIAS; |
| ASSERT((n1 >= 2) && (n1 <= 7)); |
| ASSERT((n2 >= 5) && (n2 <= 23)); |
| } else if (pll_type == PLL_TYPE5) { |
| return (100000000); |
| } else |
| ASSERT(0); |
| /* PLL types 3 and 7 use BASE2 (25Mhz) */ |
| if ((pll_type == PLL_TYPE3) || |
| (pll_type == PLL_TYPE7)) { |
| clock = CC_CLOCK_BASE2 * n1 * n2; |
| } else |
| clock = CC_CLOCK_BASE1 * n1 * n2; |
| |
| if (clock == 0) |
| return 0; |
| |
| m1 = m & CC_M1_MASK; |
| m2 = (m & CC_M2_MASK) >> CC_M2_SHIFT; |
| m3 = (m & CC_M3_MASK) >> CC_M3_SHIFT; |
| mc = (m & CC_MC_MASK) >> CC_MC_SHIFT; |
| |
| if ((pll_type == PLL_TYPE1) || |
| (pll_type == PLL_TYPE3) || |
| (pll_type == PLL_TYPE4) || |
| (pll_type == PLL_TYPE7)) { |
| m1 = factor6(m1); |
| if ((pll_type == PLL_TYPE1) || (pll_type == PLL_TYPE3)) |
| m2 += CC_F5_BIAS; |
| else |
| m2 = factor6(m2); |
| m3 = factor6(m3); |
| |
| switch (mc) { |
| case CC_MC_BYPASS: return (clock); |
| case CC_MC_M1: return (clock / m1); |
| case CC_MC_M1M2: return (clock / (m1 * m2)); |
| case CC_MC_M1M2M3: return (clock / (m1 * m2 * m3)); |
| case CC_MC_M1M3: return (clock / (m1 * m3)); |
| default: return (0); |
| } |
| } else { |
| ASSERT(pll_type == PLL_TYPE2); |
| |
| m1 += CC_T2_BIAS; |
| m2 += CC_T2M2_BIAS; |
| m3 += CC_T2_BIAS; |
| ASSERT((m1 >= 2) && (m1 <= 7)); |
| ASSERT((m2 >= 3) && (m2 <= 10)); |
| ASSERT((m3 >= 2) && (m3 <= 7)); |
| |
| if ((mc & CC_T2MC_M1BYP) == 0) |
| clock /= m1; |
| if ((mc & CC_T2MC_M2BYP) == 0) |
| clock /= m2; |
| if ((mc & CC_T2MC_M3BYP) == 0) |
| clock /= m3; |
| |
| return (clock); |
| } |
| } |
| |
| |
| /* set chip watchdog reset timer to fire in 'ticks' */ |
| void |
| si_watchdog(si_t *sih, uint ticks) |
| { |
| uint nb, maxt; |
| |
| if (PMUCTL_ENAB(sih)) { |
| |
| if ((CHIPID(sih->chip) == BCM4319_CHIP_ID) && |
| (CHIPREV(sih->chiprev) == 0) && (ticks != 0)) { |
| si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, clk_ctl_st), ~0, 0x2); |
| si_setcore(sih, USB20D_CORE_ID, 0); |
| si_core_disable(sih, 1); |
| si_setcore(sih, CC_CORE_ID, 0); |
| } |
| |
| nb = (sih->ccrev < 26) ? 16 : ((sih->ccrev >= 37) ? 32 : 24); |
| /* The mips compiler uses the sllv instruction, |
| * so we specially handle the 32-bit case. |
| */ |
| if (nb == 32) |
| maxt = 0xffffffff; |
| else |
| maxt = ((1 << nb) - 1); |
| |
| if (ticks == 1) |
| ticks = 2; |
| else if (ticks > maxt) |
| ticks = maxt; |
| |
| si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, pmuwatchdog), ~0, ticks); |
| } else { |
| maxt = (1 << 28) - 1; |
| if (ticks > maxt) |
| ticks = maxt; |
| |
| si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, watchdog), ~0, ticks); |
| } |
| } |
| |
| /* trigger watchdog reset after ms milliseconds */ |
| void |
| si_watchdog_ms(si_t *sih, uint32 ms) |
| { |
| si_watchdog(sih, wd_msticks * ms); |
| } |
| |
| |
| |
| |
| /* change logical "focus" to the gpio core for optimized access */ |
| void * |
| si_gpiosetcore(si_t *sih) |
| { |
| return (si_setcoreidx(sih, SI_CC_IDX)); |
| } |
| |
| /* mask&set gpiocontrol bits */ |
| uint32 |
| si_gpiocontrol(si_t *sih, uint32 mask, uint32 val, uint8 priority) |
| { |
| uint regoff; |
| |
| regoff = 0; |
| |
| /* gpios could be shared on router platforms |
| * ignore reservation if it's high priority (e.g., test apps) |
| */ |
| if ((priority != GPIO_HI_PRIORITY) && |
| (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { |
| mask = priority ? (si_gpioreservation & mask) : |
| ((si_gpioreservation | mask) & ~(si_gpioreservation)); |
| val &= mask; |
| } |
| |
| regoff = OFFSETOF(chipcregs_t, gpiocontrol); |
| return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); |
| } |
| |
| /* mask&set gpio output enable bits */ |
| uint32 |
| si_gpioouten(si_t *sih, uint32 mask, uint32 val, uint8 priority) |
| { |
| uint regoff; |
| |
| regoff = 0; |
| |
| /* gpios could be shared on router platforms |
| * ignore reservation if it's high priority (e.g., test apps) |
| */ |
| if ((priority != GPIO_HI_PRIORITY) && |
| (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { |
| mask = priority ? (si_gpioreservation & mask) : |
| ((si_gpioreservation | mask) & ~(si_gpioreservation)); |
| val &= mask; |
| } |
| |
| regoff = OFFSETOF(chipcregs_t, gpioouten); |
| return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); |
| } |
| |
| /* mask&set gpio output bits */ |
| uint32 |
| si_gpioout(si_t *sih, uint32 mask, uint32 val, uint8 priority) |
| { |
| uint regoff; |
| |
| regoff = 0; |
| |
| /* gpios could be shared on router platforms |
| * ignore reservation if it's high priority (e.g., test apps) |
| */ |
| if ((priority != GPIO_HI_PRIORITY) && |
| (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { |
| mask = priority ? (si_gpioreservation & mask) : |
| ((si_gpioreservation | mask) & ~(si_gpioreservation)); |
| val &= mask; |
| } |
| |
| regoff = OFFSETOF(chipcregs_t, gpioout); |
| return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); |
| } |
| |
| /* reserve one gpio */ |
| uint32 |
| si_gpioreserve(si_t *sih, uint32 gpio_bitmask, uint8 priority) |
| { |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| |
| /* only cores on SI_BUS share GPIO's and only applcation users need to |
| * reserve/release GPIO |
| */ |
| if ((BUSTYPE(sih->bustype) != SI_BUS) || (!priority)) { |
| ASSERT((BUSTYPE(sih->bustype) == SI_BUS) && (priority)); |
| return 0xffffffff; |
| } |
| /* make sure only one bit is set */ |
| if ((!gpio_bitmask) || ((gpio_bitmask) & (gpio_bitmask - 1))) { |
| ASSERT((gpio_bitmask) && !((gpio_bitmask) & (gpio_bitmask - 1))); |
| return 0xffffffff; |
| } |
| |
| /* already reserved */ |
| if (si_gpioreservation & gpio_bitmask) |
| return 0xffffffff; |
| /* set reservation */ |
| si_gpioreservation |= gpio_bitmask; |
| |
| return si_gpioreservation; |
| } |
| |
| /* release one gpio */ |
| /* |
| * releasing the gpio doesn't change the current value on the GPIO last write value |
| * persists till some one overwrites it |
| */ |
| |
| uint32 |
| si_gpiorelease(si_t *sih, uint32 gpio_bitmask, uint8 priority) |
| { |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| |
| /* only cores on SI_BUS share GPIO's and only applcation users need to |
| * reserve/release GPIO |
| */ |
| if ((BUSTYPE(sih->bustype) != SI_BUS) || (!priority)) { |
| ASSERT((BUSTYPE(sih->bustype) == SI_BUS) && (priority)); |
| return 0xffffffff; |
| } |
| /* make sure only one bit is set */ |
| if ((!gpio_bitmask) || ((gpio_bitmask) & (gpio_bitmask - 1))) { |
| ASSERT((gpio_bitmask) && !((gpio_bitmask) & (gpio_bitmask - 1))); |
| return 0xffffffff; |
| } |
| |
| /* already released */ |
| if (!(si_gpioreservation & gpio_bitmask)) |
| return 0xffffffff; |
| |
| /* clear reservation */ |
| si_gpioreservation &= ~gpio_bitmask; |
| |
| return si_gpioreservation; |
| } |
| |
| /* return the current gpioin register value */ |
| uint32 |
| si_gpioin(si_t *sih) |
| { |
| si_info_t *sii; |
| uint regoff; |
| |
| sii = SI_INFO(sih); |
| regoff = 0; |
| |
| regoff = OFFSETOF(chipcregs_t, gpioin); |
| return (si_corereg(sih, SI_CC_IDX, regoff, 0, 0)); |
| } |
| |
| /* mask&set gpio interrupt polarity bits */ |
| uint32 |
| si_gpiointpolarity(si_t *sih, uint32 mask, uint32 val, uint8 priority) |
| { |
| si_info_t *sii; |
| uint regoff; |
| |
| sii = SI_INFO(sih); |
| regoff = 0; |
| |
| /* gpios could be shared on router platforms */ |
| if ((BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { |
| mask = priority ? (si_gpioreservation & mask) : |
| ((si_gpioreservation | mask) & ~(si_gpioreservation)); |
| val &= mask; |
| } |
| |
| regoff = OFFSETOF(chipcregs_t, gpiointpolarity); |
| return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); |
| } |
| |
| /* mask&set gpio interrupt mask bits */ |
| uint32 |
| si_gpiointmask(si_t *sih, uint32 mask, uint32 val, uint8 priority) |
| { |
| si_info_t *sii; |
| uint regoff; |
| |
| sii = SI_INFO(sih); |
| regoff = 0; |
| |
| /* gpios could be shared on router platforms */ |
| if ((BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { |
| mask = priority ? (si_gpioreservation & mask) : |
| ((si_gpioreservation | mask) & ~(si_gpioreservation)); |
| val &= mask; |
| } |
| |
| regoff = OFFSETOF(chipcregs_t, gpiointmask); |
| return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); |
| } |
| |
| /* assign the gpio to an led */ |
| uint32 |
| si_gpioled(si_t *sih, uint32 mask, uint32 val) |
| { |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| if (sih->ccrev < 16) |
| return 0xffffffff; |
| |
| /* gpio led powersave reg */ |
| return (si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, gpiotimeroutmask), mask, val)); |
| } |
| |
| /* mask&set gpio timer val */ |
| uint32 |
| si_gpiotimerval(si_t *sih, uint32 mask, uint32 gpiotimerval) |
| { |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| |
| if (sih->ccrev < 16) |
| return 0xffffffff; |
| |
| return (si_corereg(sih, SI_CC_IDX, |
| OFFSETOF(chipcregs_t, gpiotimerval), mask, gpiotimerval)); |
| } |
| |
| uint32 |
| si_gpiopull(si_t *sih, bool updown, uint32 mask, uint32 val) |
| { |
| si_info_t *sii; |
| uint offs; |
| |
| sii = SI_INFO(sih); |
| if (sih->ccrev < 20) |
| return 0xffffffff; |
| |
| offs = (updown ? OFFSETOF(chipcregs_t, gpiopulldown) : OFFSETOF(chipcregs_t, gpiopullup)); |
| return (si_corereg(sih, SI_CC_IDX, offs, mask, val)); |
| } |
| |
| uint32 |
| si_gpioevent(si_t *sih, uint regtype, uint32 mask, uint32 val) |
| { |
| si_info_t *sii; |
| uint offs; |
| |
| sii = SI_INFO(sih); |
| if (sih->ccrev < 11) |
| return 0xffffffff; |
| |
| if (regtype == GPIO_REGEVT) |
| offs = OFFSETOF(chipcregs_t, gpioevent); |
| else if (regtype == GPIO_REGEVT_INTMSK) |
| offs = OFFSETOF(chipcregs_t, gpioeventintmask); |
| else if (regtype == GPIO_REGEVT_INTPOL) |
| offs = OFFSETOF(chipcregs_t, gpioeventintpolarity); |
| else |
| return 0xffffffff; |
| |
| return (si_corereg(sih, SI_CC_IDX, offs, mask, val)); |
| } |
| |
| void * |
| si_gpio_handler_register(si_t *sih, uint32 event, |
| bool level, gpio_handler_t cb, void *arg) |
| { |
| si_info_t *sii; |
| gpioh_item_t *gi; |
| |
| ASSERT(event); |
| ASSERT(cb != NULL); |
| |
| sii = SI_INFO(sih); |
| if (sih->ccrev < 11) |
| return NULL; |
| |
| if ((gi = MALLOC(sii->osh, sizeof(gpioh_item_t))) == NULL) |
| return NULL; |
| |
| bzero(gi, sizeof(gpioh_item_t)); |
| gi->event = event; |
| gi->handler = cb; |
| gi->arg = arg; |
| gi->level = level; |
| |
| gi->next = sii->gpioh_head; |
| sii->gpioh_head = gi; |
| |
| return (void *)(gi); |
| } |
| |
| void |
| si_gpio_handler_unregister(si_t *sih, void *gpioh) |
| { |
| si_info_t *sii; |
| gpioh_item_t *p, *n; |
| |
| sii = SI_INFO(sih); |
| if (sih->ccrev < 11) |
| return; |
| |
| ASSERT(sii->gpioh_head != NULL); |
| if ((void*)sii->gpioh_head == gpioh) { |
| sii->gpioh_head = sii->gpioh_head->next; |
| MFREE(sii->osh, gpioh, sizeof(gpioh_item_t)); |
| return; |
| } else { |
| p = sii->gpioh_head; |
| n = p->next; |
| while (n) { |
| if ((void*)n == gpioh) { |
| p->next = n->next; |
| MFREE(sii->osh, gpioh, sizeof(gpioh_item_t)); |
| return; |
| } |
| p = n; |
| n = n->next; |
| } |
| } |
| |
| ASSERT(0); /* Not found in list */ |
| } |
| |
| void |
| si_gpio_handler_process(si_t *sih) |
| { |
| si_info_t *sii; |
| gpioh_item_t *h; |
| uint32 level = si_gpioin(sih); |
| uint32 levelp = si_gpiointpolarity(sih, 0, 0, 0); |
| uint32 edge = si_gpioevent(sih, GPIO_REGEVT, 0, 0); |
| uint32 edgep = si_gpioevent(sih, GPIO_REGEVT_INTPOL, 0, 0); |
| |
| sii = SI_INFO(sih); |
| for (h = sii->gpioh_head; h != NULL; h = h->next) { |
| if (h->handler) { |
| uint32 status = (h->level ? level : edge) & h->event; |
| uint32 polarity = (h->level ? levelp : edgep) & h->event; |
| |
| /* polarity bitval is opposite of status bitval */ |
| if (status ^ polarity) |
| h->handler(status, h->arg); |
| } |
| } |
| |
| si_gpioevent(sih, GPIO_REGEVT, edge, edge); /* clear edge-trigger status */ |
| } |
| |
| uint32 |
| si_gpio_int_enable(si_t *sih, bool enable) |
| { |
| si_info_t *sii; |
| uint offs; |
| |
| sii = SI_INFO(sih); |
| if (sih->ccrev < 11) |
| return 0xffffffff; |
| |
| offs = OFFSETOF(chipcregs_t, intmask); |
| return (si_corereg(sih, SI_CC_IDX, offs, CI_GPIO, (enable ? CI_GPIO : 0))); |
| } |
| |
| |
| /* Return the size of the specified SOCRAM bank */ |
| static uint |
| socram_banksize(si_info_t *sii, sbsocramregs_t *regs, uint8 index, uint8 mem_type) |
| { |
| uint banksize, bankinfo; |
| uint bankidx = index | (mem_type << SOCRAM_BANKIDX_MEMTYPE_SHIFT); |
| |
| ASSERT(mem_type <= SOCRAM_MEMTYPE_DEVRAM); |
| |
| W_REG(sii->osh, ®s->bankidx, bankidx); |
| bankinfo = R_REG(sii->osh, ®s->bankinfo); |
| banksize = SOCRAM_BANKINFO_SZBASE * ((bankinfo & SOCRAM_BANKINFO_SZMASK) + 1); |
| return banksize; |
| } |
| |
| void |
| si_socdevram(si_t *sih, bool set, uint8 *enable, uint8 *protect) |
| { |
| si_info_t *sii; |
| uint origidx; |
| uint intr_val = 0; |
| sbsocramregs_t *regs; |
| bool wasup; |
| uint corerev; |
| |
| sii = SI_INFO(sih); |
| |
| /* Block ints and save current core */ |
| INTR_OFF(sii, intr_val); |
| origidx = si_coreidx(sih); |
| |
| if (!set) |
| *enable = *protect = 0; |
| |
| /* Switch to SOCRAM core */ |
| if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) |
| goto done; |
| |
| /* Get info for determining size */ |
| if (!(wasup = si_iscoreup(sih))) |
| si_core_reset(sih, 0, 0); |
| |
| corerev = si_corerev(sih); |
| if (corerev >= 10) { |
| uint32 extcinfo; |
| uint8 nb; |
| uint8 i; |
| uint32 bankidx, bankinfo; |
| |
| extcinfo = R_REG(sii->osh, ®s->extracoreinfo); |
| nb = ((extcinfo & SOCRAM_DEVRAMBANK_MASK) >> SOCRAM_DEVRAMBANK_SHIFT); |
| for (i = 0; i < nb; i++) { |
| bankidx = i | (SOCRAM_MEMTYPE_DEVRAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT); |
| W_REG(sii->osh, ®s->bankidx, bankidx); |
| bankinfo = R_REG(sii->osh, ®s->bankinfo); |
| if (set) { |
| bankinfo &= ~SOCRAM_BANKINFO_DEVRAMSEL_MASK; |
| bankinfo &= ~SOCRAM_BANKINFO_DEVRAMPRO_MASK; |
| if (*enable) { |
| bankinfo |= (1 << SOCRAM_BANKINFO_DEVRAMSEL_SHIFT); |
| if (*protect) |
| bankinfo |= (1 << SOCRAM_BANKINFO_DEVRAMPRO_SHIFT); |
| } |
| W_REG(sii->osh, ®s->bankinfo, bankinfo); |
| } |
| else if (i == 0) { |
| if (bankinfo & SOCRAM_BANKINFO_DEVRAMSEL_MASK) { |
| *enable = 1; |
| if (bankinfo & SOCRAM_BANKINFO_DEVRAMPRO_MASK) |
| *protect = 1; |
| } |
| } |
| } |
| } |
| |
| /* Return to previous state and core */ |
| if (!wasup) |
| si_core_disable(sih, 0); |
| si_setcoreidx(sih, origidx); |
| |
| done: |
| INTR_RESTORE(sii, intr_val); |
| } |
| |
| bool |
| si_socdevram_pkg(si_t *sih) |
| { |
| if (si_socdevram_size(sih) > 0) |
| return TRUE; |
| else |
| return FALSE; |
| } |
| |
| uint32 |
| si_socdevram_size(si_t *sih) |
| { |
| si_info_t *sii; |
| uint origidx; |
| uint intr_val = 0; |
| uint32 memsize = 0; |
| sbsocramregs_t *regs; |
| bool wasup; |
| uint corerev; |
| |
| sii = SI_INFO(sih); |
| |
| /* Block ints and save current core */ |
| INTR_OFF(sii, intr_val); |
| origidx = si_coreidx(sih); |
| |
| /* Switch to SOCRAM core */ |
| if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) |
| goto done; |
| |
| /* Get info for determining size */ |
| if (!(wasup = si_iscoreup(sih))) |
| si_core_reset(sih, 0, 0); |
| |
| corerev = si_corerev(sih); |
| if (corerev >= 10) { |
| uint32 extcinfo; |
| uint8 nb; |
| uint8 i; |
| |
| extcinfo = R_REG(sii->osh, ®s->extracoreinfo); |
| nb = (((extcinfo & SOCRAM_DEVRAMBANK_MASK) >> SOCRAM_DEVRAMBANK_SHIFT)); |
| for (i = 0; i < nb; i++) |
| memsize += socram_banksize(sii, regs, i, SOCRAM_MEMTYPE_DEVRAM); |
| } |
| |
| /* Return to previous state and core */ |
| if (!wasup) |
| si_core_disable(sih, 0); |
| si_setcoreidx(sih, origidx); |
| |
| done: |
| INTR_RESTORE(sii, intr_val); |
| |
| return memsize; |
| } |
| |
| /* Return the RAM size of the SOCRAM core */ |
| uint32 |
| si_socram_size(si_t *sih) |
| { |
| si_info_t *sii; |
| uint origidx; |
| uint intr_val = 0; |
| |
| sbsocramregs_t *regs; |
| bool wasup; |
| uint corerev; |
| uint32 coreinfo; |
| uint memsize = 0; |
| |
| sii = SI_INFO(sih); |
| |
| /* Block ints and save current core */ |
| INTR_OFF(sii, intr_val); |
| origidx = si_coreidx(sih); |
| |
| /* Switch to SOCRAM core */ |
| if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) |
| goto done; |
| |
| /* Get info for determining size */ |
| if (!(wasup = si_iscoreup(sih))) |
| si_core_reset(sih, 0, 0); |
| corerev = si_corerev(sih); |
| coreinfo = R_REG(sii->osh, ®s->coreinfo); |
| |
| /* Calculate size from coreinfo based on rev */ |
| if (corerev == 0) |
| memsize = 1 << (16 + (coreinfo & SRCI_MS0_MASK)); |
| else if (corerev < 3) { |
| memsize = 1 << (SR_BSZ_BASE + (coreinfo & SRCI_SRBSZ_MASK)); |
| memsize *= (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; |
| } else if ((corerev <= 7) || (corerev == 12)) { |
| uint nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; |
| uint bsz = (coreinfo & SRCI_SRBSZ_MASK); |
| uint lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT; |
| if (lss != 0) |
| nb --; |
| memsize = nb * (1 << (bsz + SR_BSZ_BASE)); |
| if (lss != 0) |
| memsize += (1 << ((lss - 1) + SR_BSZ_BASE)); |
| } else { |
| uint8 i; |
| uint nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; |
| for (i = 0; i < nb; i++) |
| memsize += socram_banksize(sii, regs, i, SOCRAM_MEMTYPE_RAM); |
| } |
| |
| /* Return to previous state and core */ |
| if (!wasup) |
| si_core_disable(sih, 0); |
| si_setcoreidx(sih, origidx); |
| |
| done: |
| INTR_RESTORE(sii, intr_val); |
| |
| return memsize; |
| } |
| |
| |
| void |
| si_btcgpiowar(si_t *sih) |
| { |
| si_info_t *sii; |
| uint origidx; |
| uint intr_val = 0; |
| chipcregs_t *cc; |
| |
| sii = SI_INFO(sih); |
| |
| /* Make sure that there is ChipCommon core present && |
| * UART_TX is strapped to 1 |
| */ |
| if (!(sih->cccaps & CC_CAP_UARTGPIO)) |
| return; |
| |
| /* si_corereg cannot be used as we have to guarantee 8-bit read/writes */ |
| INTR_OFF(sii, intr_val); |
| |
| origidx = si_coreidx(sih); |
| |
| cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); |
| ASSERT(cc != NULL); |
| |
| W_REG(sii->osh, &cc->uart0mcr, R_REG(sii->osh, &cc->uart0mcr) | 0x04); |
| |
| /* restore the original index */ |
| si_setcoreidx(sih, origidx); |
| |
| INTR_RESTORE(sii, intr_val); |
| } |
| |
| uint |
| si_pll_reset(si_t *sih) |
| { |
| uint err = 0; |
| |
| return (err); |
| } |
| |
| /* check if the device is removed */ |
| bool |
| si_deviceremoved(si_t *sih) |
| { |
| uint32 w; |
| si_info_t *sii; |
| |
| sii = SI_INFO(sih); |
| |
| switch (BUSTYPE(sih->bustype)) { |
| case PCI_BUS: |
| ASSERT(sii->osh != NULL); |
| w = OSL_PCI_READ_CONFIG(sii->osh, PCI_CFG_VID, sizeof(uint32)); |
| if ((w & 0xFFFF) != VENDOR_BROADCOM) |
| return TRUE; |
| break; |
| } |
| return FALSE; |
| } |