/*
* generic mmc spi driver
*
* Copyright ( C ) 2010 Thomas Chou < thomas @ wytron . com . tw >
* Licensed under the GPL - 2 or later .
*/
# include <common.h>
# include <malloc.h>
# include <part.h>
# include <mmc.h>
# include <spi.h>
# include <crc.h>
# include <linux/crc7.h>
# include <asm/byteorder.h>
/* MMC/SD in SPI mode reports R1 status always */
# define R1_SPI_IDLE (1 << 0)
# define R1_SPI_ERASE_RESET (1 << 1)
# define R1_SPI_ILLEGAL_COMMAND (1 << 2)
# define R1_SPI_COM_CRC (1 << 3)
# define R1_SPI_ERASE_SEQ (1 << 4)
# define R1_SPI_ADDRESS (1 << 5)
# define R1_SPI_PARAMETER (1 << 6)
/* R1 bit 7 is always zero, reuse this bit for error */
# define R1_SPI_ERROR (1 << 7)
/* Response tokens used to ack each block written: */
# define SPI_MMC_RESPONSE_CODE(x) ((x) & 0x1f)
# define SPI_RESPONSE_ACCEPTED ((2 << 1)|1)
# define SPI_RESPONSE_CRC_ERR ((5 << 1)|1)
# define SPI_RESPONSE_WRITE_ERR ((6 << 1)|1)
/* Read and write blocks start with these tokens and end with crc;
* on error , read tokens act like a subset of R2_SPI_ * values .
*/
# define SPI_TOKEN_SINGLE 0xfe /* single block r/w, multiblock read */
# define SPI_TOKEN_MULTI_WRITE 0xfc /* multiblock write */
# define SPI_TOKEN_STOP_TRAN 0xfd /* terminate multiblock write */
/* MMC SPI commands start with a start bit "0" and a transmit bit "1" */
# define MMC_SPI_CMD(x) (0x40 | (x & 0x3f))
/* bus capability */
# define MMC_SPI_VOLTAGE (MMC_VDD_32_33 | MMC_VDD_33_34)
# define MMC_SPI_MIN_CLOCK 400000 /* 400KHz to meet MMC spec */
/* timeout value */
# define CTOUT 8
# define RTOUT 3000000 /* 1 sec */
# define WTOUT 3000000 /* 1 sec */
static uint mmc_spi_sendcmd ( struct mmc * mmc , ushort cmdidx , u32 cmdarg )
{
struct spi_slave * spi = mmc - > priv ;
u8 cmdo [ 7 ] ;
u8 r1 ;
int i ;
cmdo [ 0 ] = 0xff ;
cmdo [ 1 ] = MMC_SPI_CMD ( cmdidx ) ;
cmdo [ 2 ] = cmdarg > > 24 ;
cmdo [ 3 ] = cmdarg > > 16 ;
cmdo [ 4 ] = cmdarg > > 8 ;
cmdo [ 5 ] = cmdarg ;
cmdo [ 6 ] = ( crc7 ( 0 , & cmdo [ 1 ] , 5 ) < < 1 ) | 0x01 ;
spi_xfer ( spi , sizeof ( cmdo ) * 8 , cmdo , NULL , 0 ) ;
for ( i = 0 ; i < CTOUT ; i + + ) {
spi_xfer ( spi , 1 * 8 , NULL , & r1 , 0 ) ;
if ( i & & ( r1 & 0x80 ) = = 0 ) /* r1 response */
break ;
}
debug ( " %s:cmd%d resp%d %x \n " , __func__ , cmdidx , i , r1 ) ;
return r1 ;
}
static uint mmc_spi_readdata ( struct mmc * mmc , void * xbuf ,
u32 bcnt , u32 bsize )
{
struct spi_slave * spi = mmc - > priv ;
u8 * buf = xbuf ;
u8 r1 ;
u16 crc ;
int i ;
while ( bcnt - - ) {
for ( i = 0 ; i < RTOUT ; i + + ) {
spi_xfer ( spi , 1 * 8 , NULL , & r1 , 0 ) ;
if ( r1 ! = 0xff ) /* data token */
break ;
}
debug ( " %s:tok%d %x \n " , __func__ , i , r1 ) ;
if ( r1 = = SPI_TOKEN_SINGLE ) {
spi_xfer ( spi , bsize * 8 , NULL , buf , 0 ) ;
spi_xfer ( spi , 2 * 8 , NULL , & crc , 0 ) ;
# ifdef CONFIG_MMC_SPI_CRC_ON
if ( be_to_cpu16 ( crc16_ccitt ( 0 , buf , bsize ) ) ! = crc ) {
debug ( " %s: CRC error \n " , mmc - > cfg - > name ) ;
r1 = R1_SPI_COM_CRC ;
break ;
}
# endif
r1 = 0 ;
} else {
r1 = R1_SPI_ERROR ;
break ;
}
buf + = bsize ;
}
return r1 ;
}
static uint mmc_spi_writedata ( struct mmc * mmc , const void * xbuf ,
u32 bcnt , u32 bsize , int multi )
{
struct spi_slave * spi = mmc - > priv ;
const u8 * buf = xbuf ;
u8 r1 ;
u16 crc ;
u8 tok [ 2 ] ;
int i ;
tok [ 0 ] = 0xff ;
tok [ 1 ] = multi ? SPI_TOKEN_MULTI_WRITE : SPI_TOKEN_SINGLE ;
while ( bcnt - - ) {
# ifdef CONFIG_MMC_SPI_CRC_ON
crc = cpu_to_be16 ( crc16_ccitt ( 0 , ( u8 * ) buf , bsize ) ) ;
# endif
spi_xfer ( spi , 2 * 8 , tok , NULL , 0 ) ;
spi_xfer ( spi , bsize * 8 , buf , NULL , 0 ) ;
spi_xfer ( spi , 2 * 8 , & crc , NULL , 0 ) ;
for ( i = 0 ; i < CTOUT ; i + + ) {
spi_xfer ( spi , 1 * 8 , NULL , & r1 , 0 ) ;
if ( ( r1 & 0x10 ) = = 0 ) /* response token */
break ;
}
debug ( " %s:tok%d %x \n " , __func__ , i , r1 ) ;
if ( SPI_MMC_RESPONSE_CODE ( r1 ) = = SPI_RESPONSE_ACCEPTED ) {
for ( i = 0 ; i < WTOUT ; i + + ) { /* wait busy */
spi_xfer ( spi , 1 * 8 , NULL , & r1 , 0 ) ;
if ( i & & r1 = = 0xff ) {
r1 = 0 ;
break ;
}
}
if ( i = = WTOUT ) {
debug ( " %s:wtout %x \n " , __func__ , r1 ) ;
r1 = R1_SPI_ERROR ;
break ;
}
} else {
debug ( " %s: err %x \n " , __func__ , r1 ) ;
r1 = R1_SPI_COM_CRC ;
break ;
}
buf + = bsize ;
}
if ( multi & & bcnt = = - 1 ) { /* stop multi write */
tok [ 1 ] = SPI_TOKEN_STOP_TRAN ;
spi_xfer ( spi , 2 * 8 , tok , NULL , 0 ) ;
for ( i = 0 ; i < WTOUT ; i + + ) { /* wait busy */
spi_xfer ( spi , 1 * 8 , NULL , & r1 , 0 ) ;
if ( i & & r1 = = 0xff ) {
r1 = 0 ;
break ;
}
}
if ( i = = WTOUT ) {
debug ( " %s:wstop %x \n " , __func__ , r1 ) ;
r1 = R1_SPI_ERROR ;
}
}
return r1 ;
}
static int mmc_spi_request ( struct mmc * mmc , struct mmc_cmd * cmd ,
struct mmc_data * data )
{
struct spi_slave * spi = mmc - > priv ;
u8 r1 ;
int i ;
int ret = 0 ;
debug ( " %s:cmd%d %x %x \n " , __func__ ,
cmd - > cmdidx , cmd - > resp_type , cmd - > cmdarg ) ;
spi_claim_bus ( spi ) ;
spi_cs_activate ( spi ) ;
r1 = mmc_spi_sendcmd ( mmc , cmd - > cmdidx , cmd - > cmdarg ) ;
if ( r1 = = 0xff ) { /* no response */
ret = NO_CARD_ERR ;
goto done ;
} else if ( r1 & R1_SPI_COM_CRC ) {
ret = COMM_ERR ;
goto done ;
} else if ( r1 & ~ R1_SPI_IDLE ) { /* other errors */
ret = TIMEOUT ;
goto done ;
} else if ( cmd - > resp_type = = MMC_RSP_R2 ) {
r1 = mmc_spi_readdata ( mmc , cmd - > response , 1 , 16 ) ;
for ( i = 0 ; i < 4 ; i + + )
cmd - > response [ i ] = be32_to_cpu ( cmd - > response [ i ] ) ;
debug ( " r128 %x %x %x %x \n " , cmd - > response [ 0 ] , cmd - > response [ 1 ] ,
cmd - > response [ 2 ] , cmd - > response [ 3 ] ) ;
} else if ( ! data ) {
switch ( cmd - > cmdidx ) {
case SD_CMD_APP_SEND_OP_COND :
case MMC_CMD_SEND_OP_COND :
cmd - > response [ 0 ] = ( r1 & R1_SPI_IDLE ) ? 0 : OCR_BUSY ;
break ;
case SD_CMD_SEND_IF_COND :
case MMC_CMD_SPI_READ_OCR :
spi_xfer ( spi , 4 * 8 , NULL , cmd - > response , 0 ) ;
cmd - > response [ 0 ] = be32_to_cpu ( cmd - > response [ 0 ] ) ;
debug ( " r32 %x \n " , cmd - > response [ 0 ] ) ;
break ;
case MMC_CMD_SEND_STATUS :
spi_xfer ( spi , 1 * 8 , NULL , cmd - > response , 0 ) ;
cmd - > response [ 0 ] = ( cmd - > response [ 0 ] & 0xff ) ?
MMC_STATUS_ERROR : MMC_STATUS_RDY_FOR_DATA ;
break ;
}
} else {
debug ( " %s:data %x %x %x \n " , __func__ ,
data - > flags , data - > blocks , data - > blocksize ) ;
if ( data - > flags = = MMC_DATA_READ )
r1 = mmc_spi_readdata ( mmc , data - > dest ,
data - > blocks , data - > blocksize ) ;
else if ( data - > flags = = MMC_DATA_WRITE )
r1 = mmc_spi_writedata ( mmc , data - > src ,
data - > blocks , data - > blocksize ,
( cmd - > cmdidx = = MMC_CMD_WRITE_MULTIPLE_BLOCK ) ) ;
if ( r1 & R1_SPI_COM_CRC )
ret = COMM_ERR ;
else if ( r1 ) /* other errors */
ret = TIMEOUT ;
}
done :
spi_cs_deactivate ( spi ) ;
spi_release_bus ( spi ) ;
return ret ;
}
static void mmc_spi_set_ios ( struct mmc * mmc )
{
struct spi_slave * spi = mmc - > priv ;
debug ( " %s: clock %u \n " , __func__ , mmc - > clock ) ;
if ( mmc - > clock )
spi_set_speed ( spi , mmc - > clock ) ;
}
static int mmc_spi_init_p ( struct mmc * mmc )
{
struct spi_slave * spi = mmc - > priv ;
spi_set_speed ( spi , MMC_SPI_MIN_CLOCK ) ;
spi_claim_bus ( spi ) ;
/* cs deactivated for 100+ clock */
spi_xfer ( spi , 18 * 8 , NULL , NULL , 0 ) ;
spi_release_bus ( spi ) ;
return 0 ;
}
static const struct mmc_ops mmc_spi_ops = {
. send_cmd = mmc_spi_request ,
. set_ios = mmc_spi_set_ios ,
. init = mmc_spi_init_p ,
} ;
static struct mmc_config mmc_spi_cfg = {
. name = " MMC_SPI " ,
. ops = & mmc_spi_ops ,
. host_caps = MMC_MODE_SPI ,
. voltages = MMC_SPI_VOLTAGE ,
. f_min = MMC_SPI_MIN_CLOCK ,
. part_type = PART_TYPE_DOS ,
. b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT ,
} ;
struct mmc * mmc_spi_init ( uint bus , uint cs , uint speed , uint mode )
{
struct mmc * mmc ;
struct spi_slave * spi ;
spi = spi_setup_slave ( bus , cs , speed , mode ) ;
if ( spi = = NULL )
return NULL ;
mmc_spi_cfg . f_max = speed ;
mmc = mmc_create ( & mmc_spi_cfg , spi ) ;
if ( mmc = = NULL ) {
spi_free_slave ( spi ) ;
return NULL ;
}
return mmc ;
}