blob: 93c87ce36bb524d9bd3e6e0670d5f6969a927669 [file] [log] [blame]
/*
* Misc utility routines for accessing chip-specific features
* of the SiliconBackplane-based Broadcom chips.
* For DHD only.
*
* Copyright (C) 2023, Broadcom.
*
* 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.
*
*
* <<Broadcom-WL-IPTag/Dual:>>
*/
#include <typedefs.h>
#include <bcmdefs.h>
#include <bcmutils.h>
#include <osl.h>
#include <siutils.h>
#include "siutils_priv.h"
#include <sbchipc.h>
#ifdef BCMSDIO
#include <bcmsdh.h>
#include <sbsdio.h>
#include <sdio.h>
#endif
#include <hndsoc.h>
#ifdef SOCI_NCI_BUS
#include <nci.h>
#endif
bool
si_buscore_prep(si_t *sih, uint bustype, uint devid, void *sdh)
{
UNUSED_PARAMETER(sih);
BCM_REFERENCE(bustype);
BCM_REFERENCE(devid);
BCM_REFERENCE(sdh);
#if defined(BCMSDIO) && !defined(BCMSDIOLITE)
/* PR 39902, 43618, 44891, 41539 -- avoid backplane accesses that may
* cause SDIO clock requests before a stable ALP clock. Originally had
* this later (just before srom_var_init() below) to guarantee ALP for
* CIS read, but due to these PRs moving it here before backplane use.
*/
/* As it precedes any backplane access, can't check chipid; but may
* be able to qualify with devid if underlying SDIO allows. But should
* be ok for all our SDIO (4318 doesn't support clock and pullup regs,
* but the access attempts don't seem to hurt.) Might elimiante the
* the need for ALP for CIS at all if underlying SDIO uses CMD53...
*/
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);
/* PR 40613: account for possible ALP delay */
OSL_DELAY(65);
}
}
/* Also, disable the extra SDIO pull-ups */
bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
}
#ifdef BCMSPI
/* Avoid backplane accesses before wake-wlan (i.e. htavail) for spi.
* F1 read accesses may return correct data but with data-not-available dstatus bit set.
*/
if (BUSTYPE(bustype) == SPI_BUS) {
int err;
uint32 regdata;
/* wake up wlan function :WAKE_UP goes as HT_AVAIL request in hardware */
regdata = bcmsdh_cfg_read_word(sdh, SDIO_FUNC_0, SPID_CONFIG, NULL);
SI_MSG(("F0 REG0 rd = 0x%x\n", regdata));
regdata |= WAKE_UP;
bcmsdh_cfg_write_word(sdh, SDIO_FUNC_0, SPID_CONFIG, regdata, &err);
/* It takes time for wakeup to take effect. */
OSL_DELAY(100000);
}
#endif /* BCMSPI */
#endif /* BCMSDIO && !BCMSDIOLITE */
return TRUE;
}
uint32
si_get_pmu_reg_addr(si_t *sih, uint32 offset)
{
si_info_t *sii = SI_INFO(sih);
uint32 pmuaddr = INVALID_ADDR;
uint origidx = 0;
SI_MSG(("si_get_pmu_reg_addr: pmu access, offset: %x\n", offset));
if (!(sii->pub.cccaps & CC_CAP_PMU)) {
goto done;
}
if (AOB_ENAB(&sii->pub)) {
uint pmucoreidx;
pmuregs_t *pmu;
SI_MSG(("si_get_pmu_reg_addr: AOBENAB: %x\n", offset));
origidx = sii->curidx;
pmucoreidx = si_findcoreidx(&sii->pub, PMU_CORE_ID, 0);
pmu = si_setcoreidx(&sii->pub, pmucoreidx);
/* note: this function is used by dhd and possible 64 bit compilation needs
* a cast to (unsigned long) for avoiding a compilation error.
*/
pmuaddr = (uint32)(uintptr)((volatile uint8*)pmu + offset);
si_setcoreidx(sih, origidx);
} else
pmuaddr = SI_ENUM_BASE(sih) + offset;
done:
printf("si_get_pmu_reg_addr: addrRET: %x\n", pmuaddr);
return pmuaddr;
}
static void
BCMATTACHFN(si_oob_war_BT_F1)(si_t *sih)
{
uint origidx = si_coreidx(sih);
volatile void *regs;
regs = si_setcore(sih, AXI2AHB_BRIDGE_ID, 0);
ASSERT(regs);
BCM_REFERENCE(regs);
si_wrapperreg(sih, AI_OOBSELINA30, 0xF00, 0x300);
si_setcoreidx(sih, origidx);
}
/**
* Allocate an si handle. This function may be called multiple times. This function is called by
* both si_attach() and si_kattach().
*
* vars - pointer to a to-be created pointer area for "environment" variables. Some callers of this
* function set 'vars' to NULL.
*/
si_info_t *
BCMATTACHFN(si_doattach)(si_info_t *sii, uint devid, osl_t *osh, volatile void *regs,
uint bustype, void *sdh, char **vars, uint *varsz)
{
struct si_pub *sih = &sii->pub;
chipcregs_t *cc;
uint origidx = SI_CC_IDX;
ASSERT(GOODREGS(regs));
#ifdef SI_SPROM_PROBE
si_sprom_init(sih);
#endif /* SI_SPROM_PROBE */
cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0);
ASSERT(cc != NULL);
W_REG(osh, CC_REG_ADDR(cc, GPIOPullup), 0);
W_REG(osh, CC_REG_ADDR(cc, GPIOPulldown), 0);
si_setcoreidx(sih, origidx);
/* Skip PMU initialization from the Dongle Host.
* Firmware will take care of it when it comes up.
*/
/* clear any previous epidiag-induced target abort */
ASSERT(!si_taclear(sih, FALSE));
if (((PCIECOREREV(sih->buscorerev) == 66) || (PCIECOREREV(sih->buscorerev) == 68)) &&
CST4378_CHIPMODE_BTOP(sih->chipst)) {
/*
* HW4378-413 :
* BT oob connections for pcie function 1 seen at oob_ain[5] instead of oob_ain[1]
*/
si_oob_war_BT_F1(sih);
}
/* Enable AXI Error Immediate Enable Scheme */
if ((GCIREV(sih->gcirev) == 26) ||
((GCIREV(sih->gcirev) > 27) && GCIREV(sih->gcirev) != 29)) {
SI_GCI_CC_WRITE(sih, nci_error_immediate_en, 1u);
}
return (sii);
}
/** Return the TCM-RAM size of the ARMCR4 core. */
uint32
si_tcm_size(si_t *sih)
{
const si_info_t *sii = SI_INFO(sih);
uint origidx;
bcm_int_bitmask_t intr_val;
volatile uint8 *regs;
bool wasup;
uint32 corecap;
uint memsize = 0;
uint banku_size = 0;
uint32 nab = 0;
uint32 nbb = 0;
uint32 totb = 0;
uint32 bxinfo = 0;
uint32 idx = 0;
volatile uint32 *arm_cap_reg;
volatile uint32 *arm_bidx;
volatile uint32 *arm_binfo;
SI_MSG_DBG_REG(("%s: Enter\n", __FUNCTION__));
/* Block ints and save current core */
INTR_OFF(sii, &intr_val);
origidx = si_coreidx(sih);
/* Switch to CR4 core */
if (!(regs = si_setcore(sih, ARMCR4_CORE_ID, 0)))
goto done;
/* Get info for determining size. If in reset, come out of reset,
* but remain in halt
*/
if (!(wasup = si_iscoreup(sih)))
si_core_reset(sih, SICF_CPUHALT, SICF_CPUHALT);
arm_cap_reg = (volatile uint32 *)(regs + SI_CR4_CAP);
corecap = R_REG(sii->osh, arm_cap_reg);
nab = (corecap & ARMCR4_TCBANB_MASK) >> ARMCR4_TCBANB_SHIFT;
nbb = (corecap & ARMCR4_TCBBNB_MASK) >> ARMCR4_TCBBNB_SHIFT;
totb = nab + nbb;
arm_bidx = (volatile uint32 *)(regs + SI_CR4_BANKIDX);
arm_binfo = (volatile uint32 *)(regs + SI_CR4_BANKINFO);
for (idx = 0; idx < totb; idx++) {
W_REG(sii->osh, arm_bidx, idx);
bxinfo = R_REG(sii->osh, arm_binfo);
if (bxinfo & ARMCR4_BUNITSZ_MASK) {
banku_size = ARMCR4_BSZ_1K;
} else {
banku_size = ARMCR4_BSZ_8K;
}
memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * banku_size;
}
/* Return to previous state and core */
if (!wasup)
si_core_disable(sih, 0);
si_setcoreidx(sih, origidx);
done:
INTR_RESTORE(sii, &intr_val);
SI_MSG_DBG_REG(("%s: Exit memsize=%d\n", __FUNCTION__, memsize));
return memsize;
}
bool
si_has_flops(si_t *sih)
{
uint origidx, cr4_rev;
/* Find out CR4 core revision */
origidx = si_coreidx(sih);
if (si_setcore(sih, ARMCR4_CORE_ID, 0)) {
cr4_rev = si_corerev(sih);
si_setcoreidx(sih, origidx);
if (cr4_rev == 1 || cr4_rev >= 3)
return TRUE;
}
return FALSE;
}
uint32
si_get_coreaddr(si_t *sih, uint coreidx)
{
if (CHIPTYPE(sih->socitype) == SOCI_NCI) {
return nci_get_coreaddr(sih, coreidx);
}
return 0;
}
uint32
si_ccreg(si_t *sih, uint32 offset, uint32 mask, uint32 val)
{
si_info_t *sii;
uint32 reg_val = 0;
sii = SI_INFO(sih);
/* abort for invalid offset */
if (offset > SI_CORE_SIZE)
return 0;
reg_val = si_corereg(&sii->pub, SI_CC_IDX, offset, mask, val);
return reg_val;
}
/* read from pcie space using back plane indirect access */
/* set below mask for reading 1, 2, 4 bytes in single read */
/* #define SI_BPIND_1BYTE 0x1 */
/* #define SI_BPIND_2BYTE 0x3 */
/* #define SI_BPIND_4BYTE 0xF */
int
si_bpind_access(si_t *sih, uint32 addr_high, uint32 addr_low,
int32 * data, bool read, uint32 us_timeout)
{
uint32 status = 0;
uint8 mask = SI_BPIND_4BYTE;
int ret_val = BCME_OK;
/* program address low and high fields */
si_ccreg(sih, CC_REG_OFF(BackplaneAddrLow), ~0, addr_low);
si_ccreg(sih, CC_REG_OFF(BackplaneAddrHi), ~0, addr_high);
if (read) {
/* start the read */
si_ccreg(sih, CC_REG_OFF(BackplaneIndAccess), ~0,
CC_BP_IND_ACCESS_START_MASK | mask);
} else {
/* write the data and force the trigger */
si_ccreg(sih, CC_REG_OFF(BackplaneData), ~0, *data);
si_ccreg(sih, CC_REG_OFF(BackplaneIndAccess), ~0,
CC_BP_IND_ACCESS_START_MASK |
CC_BP_IND_ACCESS_RDWR_MASK | mask);
}
/* Wait for status to be cleared */
SPINWAIT(((status = si_ccreg(sih, CC_REG_OFF(BackplaneIndAccess), 0, 0)) &
CC_BP_IND_ACCESS_START_MASK), us_timeout);
if (status & (CC_BP_IND_ACCESS_START_MASK | CC_BP_IND_ACCESS_ERROR_MASK)) {
ret_val = BCME_ERROR;
SI_ERROR(("Action Failed for address 0x%08x:0x%08x \t status: 0x%x\n",
addr_high, addr_low, status));
} else if (read) { /* read data */
*data = si_ccreg(sih, CC_REG_OFF(BackplaneData), 0, 0);
}
return ret_val;
}