/* * Copyright (c) 2011 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* ***** SDIO interface chip backplane handle functions ***** */ #include #include #include #include #include #include #include #include #include "dhd.h" #include "dhd_dbg.h" #include "sdio_host.h" #include "sdio_chip.h" /* chip core base & ramsize */ /* bcm4329 */ /* SDIO device core, ID 0x829 */ #define BCM4329_CORE_BUS_BASE 0x18011000 /* internal memory core, ID 0x80e */ #define BCM4329_CORE_SOCRAM_BASE 0x18003000 /* ARM Cortex M3 core, ID 0x82a */ #define BCM4329_CORE_ARM_BASE 0x18002000 #define BCM4329_RAMSIZE 0x48000 /* SB regs */ /* sbidhigh */ #define SBIDH_RC_MASK 0x000f /* revision code */ #define SBIDH_RCE_MASK 0x7000 /* revision code extension field */ #define SBIDH_RCE_SHIFT 8 #define SBCOREREV(sbidh) \ ((((sbidh) & SBIDH_RCE_MASK) >> SBIDH_RCE_SHIFT) | \ ((sbidh) & SBIDH_RC_MASK)) #define SBIDH_CC_MASK 0x8ff0 /* core code */ #define SBIDH_CC_SHIFT 4 #define SBIDH_VC_MASK 0xffff0000 /* vendor code */ #define SBIDH_VC_SHIFT 16 static u32 brcmf_sdio_chip_corerev(struct brcmf_sdio_dev *sdiodev, u32 corebase) { u32 regdata; regdata = brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbidhigh), 4); return SBCOREREV(regdata); } bool brcmf_sdio_chip_iscoreup(struct brcmf_sdio_dev *sdiodev, u32 corebase) { u32 regdata; regdata = brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbtmstatelow), 4); regdata &= (SBTML_RESET | SBTML_REJ_MASK | (SICF_CLOCK_EN << SBTML_SICF_SHIFT)); return ((SICF_CLOCK_EN << SBTML_SICF_SHIFT) == regdata); } void brcmf_sdio_chip_coredisable(struct brcmf_sdio_dev *sdiodev, u32 corebase) { u32 regdata; regdata = brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbtmstatelow), 4); if (regdata & SBTML_RESET) return; regdata = brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbtmstatelow), 4); if ((regdata & (SICF_CLOCK_EN << SBTML_SICF_SHIFT)) != 0) { /* * set target reject and spin until busy is clear * (preserve core-specific bits) */ regdata = brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbtmstatelow), 4); brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), 4, regdata | SBTML_REJ); regdata = brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbtmstatelow), 4); udelay(1); SPINWAIT((brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbtmstatehigh), 4) & SBTMH_BUSY), 100000); regdata = brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbtmstatehigh), 4); if (regdata & SBTMH_BUSY) brcmf_dbg(ERROR, "core state still busy\n"); regdata = brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbidlow), 4); if (regdata & SBIDL_INIT) { regdata = brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbimstate), 4) | SBIM_RJ; brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbimstate), 4, regdata); regdata = brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbimstate), 4); udelay(1); SPINWAIT((brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbimstate), 4) & SBIM_BY), 100000); } /* set reset and reject while enabling the clocks */ brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), 4, (((SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) | SBTML_REJ | SBTML_RESET)); regdata = brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbtmstatelow), 4); udelay(10); /* clear the initiator reject bit */ regdata = brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbidlow), 4); if (regdata & SBIDL_INIT) { regdata = brcmf_sdcard_reg_read(sdiodev, CORE_SB(corebase, sbimstate), 4) & ~SBIM_RJ; brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbimstate), 4, regdata); } } /* leave reset and reject asserted */ brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), 4, (SBTML_REJ | SBTML_RESET)); udelay(1); } static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u32 regs) { u32 regdata; /* * Get CC core rev * Chipid is assume to be at offset 0 from regs arg * For different chiptypes or old sdio hosts w/o chipcommon, * other ways of recognition should be added here. */ ci->cccorebase = regs; regdata = brcmf_sdcard_reg_read(sdiodev, CORE_CC_REG(ci->cccorebase, chipid), 4); ci->chip = regdata & CID_ID_MASK; ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; brcmf_dbg(INFO, "chipid=0x%x chiprev=%d\n", ci->chip, ci->chiprev); /* Address of cores for new chips should be added here */ switch (ci->chip) { case BCM4329_CHIP_ID: ci->buscorebase = BCM4329_CORE_BUS_BASE; ci->ramcorebase = BCM4329_CORE_SOCRAM_BASE; ci->armcorebase = BCM4329_CORE_ARM_BASE; ci->ramsize = BCM4329_RAMSIZE; break; default: brcmf_dbg(ERROR, "chipid 0x%x is not supported\n", ci->chip); return -ENODEV; } return 0; } static int brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev) { int err = 0; u8 clkval, clkset; /* Try forcing SDIO core to do ALPAvail request only */ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); if (err) { brcmf_dbg(ERROR, "error writing for HT off\n"); return err; } /* If register supported, wait for ALPAvail and then force ALP */ /* This may take up to 15 milliseconds */ clkval = brcmf_sdcard_cfg_read(sdiodev, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, NULL); if ((clkval & ~SBSDIO_AVBITS) != clkset) { brcmf_dbg(ERROR, "ChipClkCSR access: wrote 0x%02x read 0x%02x\n", clkset, clkval); return -EACCES; } SPINWAIT(((clkval = brcmf_sdcard_cfg_read(sdiodev, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, NULL)), !SBSDIO_ALPAV(clkval)), PMU_MAX_TRANSITION_DLY); if (!SBSDIO_ALPAV(clkval)) { brcmf_dbg(ERROR, "timeout on ALPAV wait, clkval 0x%02x\n", clkval); return -EBUSY; } clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); udelay(65); /* Also, disable the extra SDIO pull-ups */ brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); return 0; } static void brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci) { u32 regdata; /* get chipcommon rev */ ci->ccrev = brcmf_sdio_chip_corerev(sdiodev, ci->cccorebase); /* get chipcommon capabilites */ ci->cccaps = brcmf_sdcard_reg_read(sdiodev, CORE_CC_REG(ci->cccorebase, capabilities), 4); /* get pmu caps & rev */ if (ci->cccaps & CC_CAP_PMU) { ci->pmucaps = brcmf_sdcard_reg_read(sdiodev, CORE_CC_REG(ci->cccorebase, pmucapabilities), 4); ci->pmurev = ci->pmucaps & PCAP_REV_MASK; } ci->buscorerev = brcmf_sdio_chip_corerev(sdiodev, ci->buscorebase); regdata = brcmf_sdcard_reg_read(sdiodev, CORE_SB(ci->buscorebase, sbidhigh), 4); ci->buscoretype = (regdata & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT; brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n", ci->ccrev, ci->pmurev, ci->buscorerev, ci->buscoretype); /* * Make sure any on-chip ARM is off (in case strapping is wrong), * or downloaded code was already running. */ brcmf_sdio_chip_coredisable(sdiodev, ci->armcorebase); } int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, struct chip_info **ci_ptr, u32 regs) { int ret; struct chip_info *ci; brcmf_dbg(TRACE, "Enter\n"); /* alloc chip_info_t */ ci = kzalloc(sizeof(struct chip_info), GFP_ATOMIC); if (!ci) return -ENOMEM; ret = brcmf_sdio_chip_buscoreprep(sdiodev); if (ret != 0) goto err; ret = brcmf_sdio_chip_recognition(sdiodev, ci, regs); if (ret != 0) goto err; brcmf_sdio_chip_buscoresetup(sdiodev, ci); brcmf_sdcard_reg_write(sdiodev, CORE_CC_REG(ci->cccorebase, gpiopullup), 4, 0); brcmf_sdcard_reg_write(sdiodev, CORE_CC_REG(ci->cccorebase, gpiopulldown), 4, 0); *ci_ptr = ci; return 0; err: kfree(ci); return ret; }