2011-11-04 22:23:28 +01:00
/*
* 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 ***** */
2012-01-15 00:38:44 -08:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2011-11-04 22:23:28 +01:00
# include <linux/types.h>
# include <linux/netdevice.h>
# include <linux/mmc/card.h>
2011-11-04 22:23:41 +01:00
# include <linux/ssb/ssb_regs.h>
2011-11-04 22:23:42 +01:00
# include <linux/bcma/bcma.h>
2011-11-04 22:23:41 +01:00
2011-11-04 22:23:28 +01:00
# include <chipcommon.h>
# include <brcm_hw_ids.h>
# include <brcmu_wifi.h>
# include <brcmu_utils.h>
2011-11-04 22:23:31 +01:00
# include <soc.h>
2011-11-04 22:23:28 +01:00
# 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
# define SBCOREREV(sbidh) \
2011-11-04 22:23:41 +01:00
( ( ( ( sbidh ) & SSB_IDHIGH_RCHI ) > > SSB_IDHIGH_RCHI_SHIFT ) | \
( ( sbidh ) & SSB_IDHIGH_RCLO ) )
2011-11-04 22:23:28 +01:00
2011-11-10 20:30:21 +01:00
/* SOC Interconnect types (aka chip types) */
# define SOCI_SB 0
# define SOCI_AI 1
2011-11-10 20:30:22 +01:00
/* EROM CompIdentB */
# define CIB_REV_MASK 0xff000000
# define CIB_REV_SHIFT 24
2011-11-04 22:23:40 +01:00
# define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
/* SDIO Pad drive strength to select value mappings */
struct sdiod_drive_str {
u8 strength ; /* Pad Drive Strength in mA */
u8 sel ; /* Chip-specific select value */
} ;
2011-12-08 15:06:39 -08:00
/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */
2011-12-08 15:06:40 -08:00
static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8 [ ] = {
2011-12-08 15:06:39 -08:00
{ 32 , 0x6 } ,
{ 26 , 0x7 } ,
{ 22 , 0x4 } ,
{ 16 , 0x5 } ,
{ 12 , 0x2 } ,
{ 8 , 0x3 } ,
{ 4 , 0x0 } ,
{ 0 , 0x1 }
} ;
2011-11-04 22:23:42 +01:00
u8
brcmf_sdio_chip_getinfidx ( struct chip_info * ci , u16 coreid )
{
u8 idx ;
for ( idx = 0 ; idx < BRCMF_MAX_CORENUM ; idx + + )
if ( coreid = = ci - > c_inf [ idx ] . id )
return idx ;
return BRCMF_MAX_CORENUM ;
}
2011-11-04 22:23:37 +01:00
static u32
2011-11-10 20:30:22 +01:00
brcmf_sdio_sb_corerev ( struct brcmf_sdio_dev * sdiodev ,
struct chip_info * ci , u16 coreid )
2011-11-04 22:23:37 +01:00
{
u32 regdata ;
2011-11-10 20:30:22 +01:00
u8 idx ;
idx = brcmf_sdio_chip_getinfidx ( ci , coreid ) ;
2011-11-04 22:23:37 +01:00
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev ,
CORE_SB ( ci - > c_inf [ idx ] . base , sbidhigh ) ,
NULL ) ;
2011-11-04 22:23:37 +01:00
return SBCOREREV ( regdata ) ;
}
2011-11-10 20:30:22 +01:00
static u32
brcmf_sdio_ai_corerev ( struct brcmf_sdio_dev * sdiodev ,
struct chip_info * ci , u16 coreid )
{
u8 idx ;
idx = brcmf_sdio_chip_getinfidx ( ci , coreid ) ;
return ( ci - > c_inf [ idx ] . cib & CIB_REV_MASK ) > > CIB_REV_SHIFT ;
}
2011-11-10 20:30:21 +01:00
static bool
brcmf_sdio_sb_iscoreup ( struct brcmf_sdio_dev * sdiodev ,
struct chip_info * ci , u16 coreid )
2011-11-04 22:23:36 +01:00
{
u32 regdata ;
2011-11-10 20:30:21 +01:00
u8 idx ;
idx = brcmf_sdio_chip_getinfidx ( ci , coreid ) ;
2011-11-04 22:23:36 +01:00
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev ,
CORE_SB ( ci - > c_inf [ idx ] . base , sbtmstatelow ) ,
NULL ) ;
2011-11-04 22:23:41 +01:00
regdata & = ( SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT |
SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK ) ;
return ( SSB_TMSLOW_CLOCK = = regdata ) ;
2011-11-04 22:23:36 +01:00
}
2011-11-10 20:30:21 +01:00
static bool
brcmf_sdio_ai_iscoreup ( struct brcmf_sdio_dev * sdiodev ,
struct chip_info * ci , u16 coreid )
{
u32 regdata ;
u8 idx ;
bool ret ;
idx = brcmf_sdio_chip_getinfidx ( ci , coreid ) ;
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev , ci - > c_inf [ idx ] . wrapbase + BCMA_IOCTL ,
NULL ) ;
2011-11-10 20:30:21 +01:00
ret = ( regdata & ( BCMA_IOCTL_FGC | BCMA_IOCTL_CLK ) ) = = BCMA_IOCTL_CLK ;
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev ,
ci - > c_inf [ idx ] . wrapbase + BCMA_RESET_CTL ,
NULL ) ;
2011-11-10 20:30:21 +01:00
ret = ret & & ( ( regdata & BCMA_RESET_CTL_RESET ) = = 0 ) ;
return ret ;
}
2011-11-10 20:30:23 +01:00
static void
brcmf_sdio_sb_coredisable ( struct brcmf_sdio_dev * sdiodev ,
struct chip_info * ci , u16 coreid )
2011-11-04 22:23:31 +01:00
{
2012-05-04 18:27:34 -07:00
u32 regdata , base ;
2011-11-10 20:30:23 +01:00
u8 idx ;
idx = brcmf_sdio_chip_getinfidx ( ci , coreid ) ;
2012-05-04 18:27:34 -07:00
base = ci - > c_inf [ idx ] . base ;
2011-11-04 22:23:31 +01:00
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev , CORE_SB ( base , sbtmstatelow ) , NULL ) ;
2011-11-04 22:23:41 +01:00
if ( regdata & SSB_TMSLOW_RESET )
2011-11-04 22:23:31 +01:00
return ;
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev , CORE_SB ( base , sbtmstatelow ) , NULL ) ;
2011-11-04 22:23:41 +01:00
if ( ( regdata & SSB_TMSLOW_CLOCK ) ! = 0 ) {
2011-11-04 22:23:31 +01:00
/*
* set target reject and spin until busy is clear
* ( preserve core - specific bits )
*/
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev , CORE_SB ( base , sbtmstatelow ) ,
NULL ) ;
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev , CORE_SB ( base , sbtmstatelow ) ,
regdata | SSB_TMSLOW_REJECT , NULL ) ;
2011-11-04 22:23:31 +01:00
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev , CORE_SB ( base , sbtmstatelow ) ,
NULL ) ;
2011-11-04 22:23:31 +01:00
udelay ( 1 ) ;
2012-05-04 18:27:34 -07:00
SPINWAIT ( ( brcmf_sdio_regrl ( sdiodev ,
CORE_SB ( base , sbtmstatehigh ) ,
NULL ) &
2011-11-04 22:23:41 +01:00
SSB_TMSHIGH_BUSY ) , 100000 ) ;
2011-11-04 22:23:31 +01:00
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev ,
CORE_SB ( base , sbtmstatehigh ) ,
NULL ) ;
2011-11-04 22:23:41 +01:00
if ( regdata & SSB_TMSHIGH_BUSY )
2011-11-04 22:23:31 +01:00
brcmf_dbg ( ERROR , " core state still busy \n " ) ;
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev , CORE_SB ( base , sbidlow ) ,
NULL ) ;
2011-11-04 22:23:41 +01:00
if ( regdata & SSB_IDLOW_INITIATOR ) {
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev ,
CORE_SB ( base , sbimstate ) ,
NULL ) ;
regdata | = SSB_IMSTATE_REJECT ;
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev , CORE_SB ( base , sbimstate ) ,
regdata , NULL ) ;
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev ,
CORE_SB ( base , sbimstate ) ,
NULL ) ;
2011-11-04 22:23:31 +01:00
udelay ( 1 ) ;
2012-05-04 18:27:34 -07:00
SPINWAIT ( ( brcmf_sdio_regrl ( sdiodev ,
CORE_SB ( base , sbimstate ) ,
NULL ) &
2011-11-04 22:23:41 +01:00
SSB_IMSTATE_BUSY ) , 100000 ) ;
2011-11-04 22:23:31 +01:00
}
/* set reset and reject while enabling the clocks */
2012-05-04 18:27:35 -07:00
regdata = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET ;
brcmf_sdio_regwl ( sdiodev , CORE_SB ( base , sbtmstatelow ) ,
regdata , NULL ) ;
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev , CORE_SB ( base , sbtmstatelow ) ,
NULL ) ;
2011-11-04 22:23:31 +01:00
udelay ( 10 ) ;
/* clear the initiator reject bit */
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev , CORE_SB ( base , sbidlow ) ,
NULL ) ;
2011-11-04 22:23:41 +01:00
if ( regdata & SSB_IDLOW_INITIATOR ) {
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev ,
CORE_SB ( base , sbimstate ) ,
NULL ) ;
regdata & = ~ SSB_IMSTATE_REJECT ;
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev , CORE_SB ( base , sbimstate ) ,
regdata , NULL ) ;
2011-11-04 22:23:31 +01:00
}
}
/* leave reset and reject asserted */
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev , CORE_SB ( base , sbtmstatelow ) ,
( SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET ) , NULL ) ;
2011-11-04 22:23:31 +01:00
udelay ( 1 ) ;
}
2011-11-10 20:30:23 +01:00
static void
brcmf_sdio_ai_coredisable ( struct brcmf_sdio_dev * sdiodev ,
struct chip_info * ci , u16 coreid )
{
u8 idx ;
u32 regdata ;
idx = brcmf_sdio_chip_getinfidx ( ci , coreid ) ;
/* if core is already in reset, just return */
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev ,
ci - > c_inf [ idx ] . wrapbase + BCMA_RESET_CTL ,
NULL ) ;
2011-11-10 20:30:23 +01:00
if ( ( regdata & BCMA_RESET_CTL_RESET ) ! = 0 )
return ;
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev , ci - > c_inf [ idx ] . wrapbase + BCMA_IOCTL , 0 , NULL ) ;
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev , ci - > c_inf [ idx ] . wrapbase + BCMA_IOCTL ,
NULL ) ;
2011-11-10 20:30:23 +01:00
udelay ( 10 ) ;
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev , ci - > c_inf [ idx ] . wrapbase + BCMA_RESET_CTL ,
BCMA_RESET_CTL_RESET , NULL ) ;
2011-11-10 20:30:23 +01:00
udelay ( 1 ) ;
}
2011-11-10 20:30:24 +01:00
static void
brcmf_sdio_sb_resetcore ( struct brcmf_sdio_dev * sdiodev ,
struct chip_info * ci , u16 coreid )
2011-11-04 22:23:38 +01:00
{
u32 regdata ;
2011-11-10 20:30:23 +01:00
u8 idx ;
idx = brcmf_sdio_chip_getinfidx ( ci , coreid ) ;
2011-11-04 22:23:38 +01:00
/*
* Must do the disable sequence first to work for
* arbitrary current core state .
*/
2011-11-10 20:30:24 +01:00
brcmf_sdio_sb_coredisable ( sdiodev , ci , coreid ) ;
2011-11-04 22:23:38 +01:00
/*
* Now do the initialization sequence .
* set reset while enabling the clock and
* forcing them on throughout the core
*/
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev ,
CORE_SB ( ci - > c_inf [ idx ] . base , sbtmstatelow ) ,
SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET ,
NULL ) ;
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev ,
CORE_SB ( ci - > c_inf [ idx ] . base , sbtmstatelow ) ,
NULL ) ;
2011-11-04 22:23:38 +01:00
udelay ( 1 ) ;
2011-11-10 20:30:24 +01:00
/* clear any serror */
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev ,
CORE_SB ( ci - > c_inf [ idx ] . base , sbtmstatehigh ) ,
NULL ) ;
2011-11-04 22:23:41 +01:00
if ( regdata & SSB_TMSHIGH_SERR )
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev ,
CORE_SB ( ci - > c_inf [ idx ] . base , sbtmstatehigh ) ,
0 , NULL ) ;
2011-11-04 22:23:38 +01:00
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev ,
CORE_SB ( ci - > c_inf [ idx ] . base , sbimstate ) ,
NULL ) ;
2011-11-04 22:23:41 +01:00
if ( regdata & ( SSB_IMSTATE_IBE | SSB_IMSTATE_TO ) )
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev ,
CORE_SB ( ci - > c_inf [ idx ] . base , sbimstate ) ,
regdata & ~ ( SSB_IMSTATE_IBE | SSB_IMSTATE_TO ) ,
NULL ) ;
2011-11-04 22:23:38 +01:00
/* clear reset and allow it to propagate throughout the core */
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev , CORE_SB ( ci - > c_inf [ idx ] . base , sbtmstatelow ) ,
SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK , NULL ) ;
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev ,
CORE_SB ( ci - > c_inf [ idx ] . base , sbtmstatelow ) ,
NULL ) ;
2011-11-04 22:23:38 +01:00
udelay ( 1 ) ;
/* leave clock enabled */
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev , CORE_SB ( ci - > c_inf [ idx ] . base , sbtmstatelow ) ,
SSB_TMSLOW_CLOCK , NULL ) ;
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev ,
CORE_SB ( ci - > c_inf [ idx ] . base , sbtmstatelow ) ,
NULL ) ;
2011-11-10 20:30:24 +01:00
udelay ( 1 ) ;
}
static void
brcmf_sdio_ai_resetcore ( struct brcmf_sdio_dev * sdiodev ,
struct chip_info * ci , u16 coreid )
{
u8 idx ;
u32 regdata ;
idx = brcmf_sdio_chip_getinfidx ( ci , coreid ) ;
/* must disable first to work for arbitrary current core state */
brcmf_sdio_ai_coredisable ( sdiodev , ci , coreid ) ;
/* now do initialization sequence */
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev , ci - > c_inf [ idx ] . wrapbase + BCMA_IOCTL ,
BCMA_IOCTL_FGC | BCMA_IOCTL_CLK , NULL ) ;
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev , ci - > c_inf [ idx ] . wrapbase + BCMA_IOCTL ,
NULL ) ;
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev , ci - > c_inf [ idx ] . wrapbase + BCMA_RESET_CTL ,
0 , NULL ) ;
2011-11-10 20:30:24 +01:00
udelay ( 1 ) ;
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev , ci - > c_inf [ idx ] . wrapbase + BCMA_IOCTL ,
BCMA_IOCTL_CLK , NULL ) ;
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev , ci - > c_inf [ idx ] . wrapbase + BCMA_IOCTL ,
NULL ) ;
2011-11-04 22:23:38 +01:00
udelay ( 1 ) ;
}
2011-11-04 22:23:28 +01:00
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 .
*/
2011-11-04 22:23:42 +01:00
ci - > c_inf [ 0 ] . id = BCMA_CORE_CHIPCOMMON ;
ci - > c_inf [ 0 ] . base = regs ;
2012-05-04 18:27:34 -07:00
regdata = brcmf_sdio_regrl ( sdiodev ,
CORE_CC_REG ( ci - > c_inf [ 0 ] . base , chipid ) ,
NULL ) ;
2011-11-04 22:23:28 +01:00
ci - > chip = regdata & CID_ID_MASK ;
ci - > chiprev = ( regdata & CID_REV_MASK ) > > CID_REV_SHIFT ;
2011-11-10 20:30:21 +01:00
ci - > socitype = ( regdata & CID_TYPE_MASK ) > > CID_TYPE_SHIFT ;
2011-11-04 22:23:28 +01:00
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 :
2011-11-04 22:23:42 +01:00
ci - > c_inf [ 1 ] . id = BCMA_CORE_SDIO_DEV ;
ci - > c_inf [ 1 ] . base = BCM4329_CORE_BUS_BASE ;
ci - > c_inf [ 2 ] . id = BCMA_CORE_INTERNAL_MEM ;
ci - > c_inf [ 2 ] . base = BCM4329_CORE_SOCRAM_BASE ;
ci - > c_inf [ 3 ] . id = BCMA_CORE_ARM_CM3 ;
ci - > c_inf [ 3 ] . base = BCM4329_CORE_ARM_BASE ;
2011-11-04 22:23:28 +01:00
ci - > ramsize = BCM4329_RAMSIZE ;
break ;
2011-12-08 15:06:39 -08:00
case BCM4330_CHIP_ID :
ci - > c_inf [ 0 ] . wrapbase = 0x18100000 ;
ci - > c_inf [ 0 ] . cib = 0x27004211 ;
ci - > c_inf [ 1 ] . id = BCMA_CORE_SDIO_DEV ;
ci - > c_inf [ 1 ] . base = 0x18002000 ;
ci - > c_inf [ 1 ] . wrapbase = 0x18102000 ;
ci - > c_inf [ 1 ] . cib = 0x07004211 ;
ci - > c_inf [ 2 ] . id = BCMA_CORE_INTERNAL_MEM ;
ci - > c_inf [ 2 ] . base = 0x18004000 ;
ci - > c_inf [ 2 ] . wrapbase = 0x18104000 ;
ci - > c_inf [ 2 ] . cib = 0x0d080401 ;
ci - > c_inf [ 3 ] . id = BCMA_CORE_ARM_CM3 ;
ci - > c_inf [ 3 ] . base = 0x18003000 ;
ci - > c_inf [ 3 ] . wrapbase = 0x18103000 ;
ci - > c_inf [ 3 ] . cib = 0x03004211 ;
ci - > ramsize = 0x48000 ;
break ;
2011-11-04 22:23:28 +01:00
default :
brcmf_dbg ( ERROR , " chipid 0x%x is not supported \n " , ci - > chip ) ;
return - ENODEV ;
}
2011-11-10 20:30:21 +01:00
switch ( ci - > socitype ) {
case SOCI_SB :
ci - > iscoreup = brcmf_sdio_sb_iscoreup ;
2011-11-10 20:30:22 +01:00
ci - > corerev = brcmf_sdio_sb_corerev ;
2011-11-10 20:30:23 +01:00
ci - > coredisable = brcmf_sdio_sb_coredisable ;
2011-11-10 20:30:24 +01:00
ci - > resetcore = brcmf_sdio_sb_resetcore ;
2011-11-10 20:30:21 +01:00
break ;
case SOCI_AI :
ci - > iscoreup = brcmf_sdio_ai_iscoreup ;
2011-11-10 20:30:22 +01:00
ci - > corerev = brcmf_sdio_ai_corerev ;
2011-11-10 20:30:23 +01:00
ci - > coredisable = brcmf_sdio_ai_coredisable ;
2011-11-10 20:30:24 +01:00
ci - > resetcore = brcmf_sdio_ai_resetcore ;
2011-11-10 20:30:21 +01:00
break ;
default :
brcmf_dbg ( ERROR , " socitype %u not supported \n " , ci - > socitype ) ;
return - ENODEV ;
}
2011-11-04 22:23:28 +01:00
return 0 ;
}
2011-11-04 22:23:29 +01:00
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 ;
2012-05-04 18:27:33 -07:00
brcmf_sdio_regwb ( sdiodev , SBSDIO_FUNC1_CHIPCLKCSR , clkset , & err ) ;
2011-11-04 22:23:29 +01:00
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 */
2012-05-04 18:27:32 -07:00
clkval = brcmf_sdio_regrb ( sdiodev ,
SBSDIO_FUNC1_CHIPCLKCSR , NULL ) ;
2011-11-04 22:23:29 +01:00
if ( ( clkval & ~ SBSDIO_AVBITS ) ! = clkset ) {
brcmf_dbg ( ERROR , " ChipClkCSR access: wrote 0x%02x read 0x%02x \n " ,
clkset , clkval ) ;
return - EACCES ;
}
2012-05-04 18:27:32 -07:00
SPINWAIT ( ( ( clkval = brcmf_sdio_regrb ( sdiodev ,
SBSDIO_FUNC1_CHIPCLKCSR , NULL ) ) ,
2011-11-04 22:23:29 +01:00
! 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 ;
2012-05-04 18:27:33 -07:00
brcmf_sdio_regwb ( sdiodev , SBSDIO_FUNC1_CHIPCLKCSR , clkset , & err ) ;
2011-11-04 22:23:29 +01:00
udelay ( 65 ) ;
/* Also, disable the extra SDIO pull-ups */
2012-05-04 18:27:33 -07:00
brcmf_sdio_regwb ( sdiodev , SBSDIO_FUNC1_SDIOPULLUP , 0 , NULL ) ;
2011-11-04 22:23:29 +01:00
return 0 ;
}
2011-11-04 22:23:30 +01:00
static void
brcmf_sdio_chip_buscoresetup ( struct brcmf_sdio_dev * sdiodev ,
struct chip_info * ci )
{
2012-05-04 18:27:34 -07:00
u32 base = ci - > c_inf [ 0 ] . base ;
2011-11-04 22:23:30 +01:00
/* get chipcommon rev */
2011-11-10 20:30:22 +01:00
ci - > c_inf [ 0 ] . rev = ci - > corerev ( sdiodev , ci , ci - > c_inf [ 0 ] . id ) ;
2011-11-04 22:23:30 +01:00
/* get chipcommon capabilites */
2012-05-04 18:27:34 -07:00
ci - > c_inf [ 0 ] . caps = brcmf_sdio_regrl ( sdiodev ,
CORE_CC_REG ( base , capabilities ) ,
NULL ) ;
2011-11-04 22:23:30 +01:00
/* get pmu caps & rev */
2011-11-04 22:23:42 +01:00
if ( ci - > c_inf [ 0 ] . caps & CC_CAP_PMU ) {
2012-05-04 18:27:34 -07:00
ci - > pmucaps =
brcmf_sdio_regrl ( sdiodev ,
CORE_CC_REG ( base , pmucapabilities ) ,
NULL ) ;
2011-11-04 22:23:30 +01:00
ci - > pmurev = ci - > pmucaps & PCAP_REV_MASK ;
}
2011-11-10 20:30:22 +01:00
ci - > c_inf [ 1 ] . rev = ci - > corerev ( sdiodev , ci , ci - > c_inf [ 1 ] . id ) ;
2011-11-04 22:23:30 +01:00
brcmf_dbg ( INFO , " ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x \n " ,
2011-11-04 22:23:42 +01:00
ci - > c_inf [ 0 ] . rev , ci - > pmurev ,
ci - > c_inf [ 1 ] . rev , ci - > c_inf [ 1 ] . id ) ;
2011-11-04 22:23:32 +01:00
/*
* Make sure any on - chip ARM is off ( in case strapping is wrong ) ,
* or downloaded code was already running .
*/
2011-11-10 20:30:23 +01:00
ci - > coredisable ( sdiodev , ci , BCMA_CORE_ARM_CM3 ) ;
2011-11-04 22:23:30 +01:00
}
2011-11-04 22:23:28 +01:00
int brcmf_sdio_chip_attach ( struct brcmf_sdio_dev * sdiodev ,
2011-11-04 22:23:35 +01:00
struct chip_info * * ci_ptr , u32 regs )
2011-11-04 22:23:28 +01:00
{
2011-11-04 22:23:35 +01:00
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 ;
2011-11-04 22:23:28 +01:00
2011-11-04 22:23:29 +01:00
ret = brcmf_sdio_chip_buscoreprep ( sdiodev ) ;
if ( ret ! = 0 )
2011-11-04 22:23:35 +01:00
goto err ;
2011-11-04 22:23:29 +01:00
2011-11-04 22:23:28 +01:00
ret = brcmf_sdio_chip_recognition ( sdiodev , ci , regs ) ;
if ( ret ! = 0 )
2011-11-04 22:23:35 +01:00
goto err ;
2011-11-04 22:23:28 +01:00
2011-11-04 22:23:30 +01:00
brcmf_sdio_chip_buscoresetup ( sdiodev , ci ) ;
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev , CORE_CC_REG ( ci - > c_inf [ 0 ] . base , gpiopullup ) ,
0 , NULL ) ;
brcmf_sdio_regwl ( sdiodev , CORE_CC_REG ( ci - > c_inf [ 0 ] . base , gpiopulldown ) ,
0 , NULL ) ;
2011-11-04 22:23:33 +01:00
2011-11-04 22:23:35 +01:00
* ci_ptr = ci ;
return 0 ;
err :
kfree ( ci ) ;
2011-11-04 22:23:28 +01:00
return ret ;
}
2011-11-04 22:23:39 +01:00
void
brcmf_sdio_chip_detach ( struct chip_info * * ci_ptr )
{
brcmf_dbg ( TRACE , " Enter \n " ) ;
kfree ( * ci_ptr ) ;
* ci_ptr = NULL ;
}
2011-11-04 22:23:40 +01:00
static char * brcmf_sdio_chip_name ( uint chipid , char * buf , uint len )
{
const char * fmt ;
fmt = ( ( chipid > 0xa000 ) | | ( chipid < 0x4000 ) ) ? " %d " : " %x " ;
snprintf ( buf , len , fmt , chipid ) ;
return buf ;
}
void
brcmf_sdio_chip_drivestrengthinit ( struct brcmf_sdio_dev * sdiodev ,
struct chip_info * ci , u32 drivestrength )
{
struct sdiod_drive_str * str_tab = NULL ;
u32 str_mask = 0 ;
u32 str_shift = 0 ;
char chn [ 8 ] ;
2012-05-04 18:27:34 -07:00
u32 base = ci - > c_inf [ 0 ] . base ;
2011-11-04 22:23:40 +01:00
2011-11-04 22:23:42 +01:00
if ( ! ( ci - > c_inf [ 0 ] . caps & CC_CAP_PMU ) )
2011-11-04 22:23:40 +01:00
return ;
switch ( SDIOD_DRVSTR_KEY ( ci - > chip , ci - > pmurev ) ) {
2011-12-08 15:06:39 -08:00
case SDIOD_DRVSTR_KEY ( BCM4330_CHIP_ID , 12 ) :
2011-12-08 15:06:40 -08:00
str_tab = ( struct sdiod_drive_str * ) & sdiod_drvstr_tab1_1v8 ;
2011-12-08 15:06:39 -08:00
str_mask = 0x00003800 ;
str_shift = 11 ;
break ;
2011-11-04 22:23:40 +01:00
default :
brcmf_dbg ( ERROR , " No SDIO Drive strength init done for chip %s rev %d pmurev %d \n " ,
brcmf_sdio_chip_name ( ci - > chip , chn , 8 ) ,
ci - > chiprev , ci - > pmurev ) ;
break ;
}
if ( str_tab ! = NULL ) {
u32 drivestrength_sel = 0 ;
u32 cc_data_temp ;
int i ;
for ( i = 0 ; str_tab [ i ] . strength ! = 0 ; i + + ) {
if ( drivestrength > = str_tab [ i ] . strength ) {
drivestrength_sel = str_tab [ i ] . sel ;
break ;
}
}
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev , CORE_CC_REG ( base , chipcontrol_addr ) ,
1 , NULL ) ;
2012-05-04 18:27:34 -07:00
cc_data_temp =
brcmf_sdio_regrl ( sdiodev ,
CORE_CC_REG ( base , chipcontrol_addr ) ,
NULL ) ;
2011-11-04 22:23:40 +01:00
cc_data_temp & = ~ str_mask ;
drivestrength_sel < < = str_shift ;
cc_data_temp | = drivestrength_sel ;
2012-05-04 18:27:35 -07:00
brcmf_sdio_regwl ( sdiodev , CORE_CC_REG ( base , chipcontrol_addr ) ,
cc_data_temp , NULL ) ;
2011-11-04 22:23:40 +01:00
brcmf_dbg ( INFO , " SDIO: %dmA drive strength selected, set to 0x%08x \n " ,
drivestrength , cc_data_temp ) ;
}
}