@ -9,6 +9,7 @@
*/
# include <common.h>
# include <dm.h>
# include <malloc.h>
# include <spi.h>
# include <os.h>
@ -19,6 +20,11 @@
# include <asm/getopt.h>
# include <asm/spi.h>
# include <asm/state.h>
# include <dm/device-internal.h>
# include <dm/lists.h>
# include <dm/uclass-internal.h>
DECLARE_GLOBAL_DATA_PTR ;
/*
* The different states that our SPI flash transitions between .
@ -34,12 +40,14 @@ enum sandbox_sf_state {
SF_ERASE , /* erase the flash */
SF_READ_STATUS , /* read the flash's status register */
SF_READ_STATUS1 , /* read the flash's status register upper 8 bits*/
SF_WRITE_STATUS , /* write the flash's status register */
} ;
static const char * sandbox_sf_state_name ( enum sandbox_sf_state state )
{
static const char * const states [ ] = {
" CMD " , " ID " , " ADDR " , " READ " , " WRITE " , " ERASE " , " READ_STATUS " ,
" READ_STATUS1 " , " WRITE_STATUS " ,
} ;
return states [ state ] ;
}
@ -58,6 +66,7 @@ static u8 sandbox_sf_0xff[0x1000];
/* Internal state data for each SPI flash */
struct sandbox_spi_flash {
unsigned int cs ; /* Chip select we are attached to */
/*
* As we receive data over the SPI bus , our flash transitions
* between states . For example , we start off in the SF_CMD
@ -84,71 +93,124 @@ struct sandbox_spi_flash {
int fd ;
} ;
static int sandbox_sf_setup ( void * * priv , const char * spec )
struct sandbox_spi_flash_plat_data {
const char * filename ;
const char * device_name ;
int bus ;
int cs ;
} ;
/**
* This is a very strange probe function . If it has platform data ( which may
* have come from the device tree ) then this function gets the filename and
* device type from there . Failing that it looks at the command line
* parameter .
*/
static int sandbox_sf_probe ( struct udevice * dev )
{
/* spec = idcode:file */
struct sandbox_spi_flash * sbsf ;
struct sandbox_spi_flash * sbsf = dev_get_priv ( dev ) ;
const char * file ;
size_t len , idname_len ;
const struct spi_flash_params * data ;
file = strchr ( spec , ' : ' ) ;
if ( ! file ) {
printf ( " sandbox_sf: unable to parse file \n " ) ;
goto error ;
struct sandbox_spi_flash_plat_data * pdata = dev_get_platdata ( dev ) ;
struct sandbox_state * state = state_get_current ( ) ;
struct udevice * bus = dev - > parent ;
const char * spec = NULL ;
int ret = 0 ;
int cs = - 1 ;
int i ;
debug ( " %s: bus %d, looking for emul=%p: " , __func__ , bus - > seq , dev ) ;
if ( bus - > seq > = 0 & & bus - > seq < CONFIG_SANDBOX_SPI_MAX_BUS ) {
for ( i = 0 ; i < CONFIG_SANDBOX_SPI_MAX_CS ; i + + ) {
if ( state - > spi [ bus - > seq ] [ i ] . emul = = dev )
cs = i ;
}
}
if ( cs = = - 1 ) {
printf ( " Error: Unknown chip select for device '%s' " ,
dev - > name ) ;
return - EINVAL ;
}
debug ( " found at cs %d \n " , cs ) ;
if ( ! pdata - > filename ) {
struct sandbox_state * state = state_get_current ( ) ;
assert ( bus - > seq ! = - 1 ) ;
if ( bus - > seq < CONFIG_SANDBOX_SPI_MAX_BUS )
spec = state - > spi [ bus - > seq ] [ cs ] . spec ;
if ( ! spec )
return - ENOENT ;
file = strchr ( spec , ' : ' ) ;
if ( ! file ) {
printf ( " sandbox_sf: unable to parse file \n " ) ;
ret = - EINVAL ;
goto error ;
}
idname_len = file - spec ;
pdata - > filename = file + 1 ;
pdata - > device_name = spec ;
+ + file ;
} else {
spec = strchr ( pdata - > device_name , ' , ' ) ;
if ( spec )
spec + + ;
else
spec = pdata - > device_name ;
idname_len = strlen ( spec ) ;
}
idname_len = file - spec ;
+ + file ;
debug ( " %s: device='%s' \n " , __func__ , spec ) ;
for ( data = spi_flash_params_table ; data - > name ; data + + ) {
len = strlen ( data - > name ) ;
if ( idname_len ! = len )
continue ;
if ( ! memcmp ( spec , data - > name , len ) )
if ( ! strncase cmp( spec , data - > name , len ) )
break ;
}
if ( ! data - > name ) {
printf ( " sandbox_sf: unknown flash '%*s' \n " , ( int ) idname_len ,
spec ) ;
ret = - EINVAL ;
goto error ;
}
if ( sandbox_sf_0xff [ 0 ] = = 0x00 )
memset ( sandbox_sf_0xff , 0xff , sizeof ( sandbox_sf_0xff ) ) ;
sbsf = calloc ( sizeof ( * sbsf ) , 1 ) ;
if ( ! sbsf ) {
printf ( " sandbox_sf: out of memory \n " ) ;
goto error ;
}
sbsf - > fd = os_open ( file , 02 ) ;
sbsf - > fd = os_open ( pdata - > filename , 02 ) ;
if ( sbsf - > fd = = - 1 ) {
free ( sbsf ) ;
printf ( " sandbox_sf: unable to open file '%s' \n " , file ) ;
printf ( " sandbox_sf: unable to open file '%s' \n " ,
pdata - > filename ) ;
ret = - EIO ;
goto error ;
}
sbsf - > data = data ;
sbsf - > cs = cs ;
* priv = sbsf ;
return 0 ;
error :
return 1 ;
return ret ;
}
static void sandbox_sf_free ( void * priv )
static int sandbox_sf_remove ( struct udevice * de v)
{
struct sandbox_spi_flash * sbsf = priv ;
struct sandbox_spi_flash * sbsf = dev_get_ priv( dev ) ;
os_close ( sbsf - > fd ) ;
free ( sbsf ) ;
return 0 ;
}
static void sandbox_sf_cs_activate ( void * pri v)
static void sandbox_sf_cs_activate ( struct udevice * de v)
{
struct sandbox_spi_flash * sbsf = priv ;
struct sandbox_spi_flash * sbsf = dev_get_ priv( dev ) ;
debug ( " sandbox_sf: CS activated; state is fresh! \n " ) ;
@ -160,11 +222,24 @@ static void sandbox_sf_cs_activate(void *priv)
sbsf - > cmd = SF_CMD ;
}
static void sandbox_sf_cs_deactivate ( void * pri v)
static void sandbox_sf_cs_deactivate ( struct udevice * de v)
{
debug ( " sandbox_sf: CS deactivated; cmd done processing! \n " ) ;
}
/*
* There are times when the data lines are allowed to tristate . What
* is actually sensed on the line depends on the hardware . It could
* always be 0xFF / 0x00 ( if there are pull ups / downs ) , or things could
* float and so we ' d get garbage back . This func encapsulates that
* scenario so we can worry about the details here .
*/
static void sandbox_spi_tristate ( u8 * buf , uint len )
{
/* XXX: make this into a user config option ? */
memset ( buf , 0xff , len ) ;
}
/* Figure out what command this stream is telling us to do */
static int sandbox_sf_process_cmd ( struct sandbox_spi_flash * sbsf , const u8 * rx ,
u8 * tx )
@ -172,7 +247,8 @@ static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx,
enum sandbox_sf_state oldstate = sbsf - > state ;
/* We need to output a byte for the cmd byte we just ate */
sandbox_spi_tristate ( tx , 1 ) ;
if ( tx )
sandbox_spi_tristate ( tx , 1 ) ;
sbsf - > cmd = rx [ 0 ] ;
switch ( sbsf - > cmd ) {
@ -200,6 +276,9 @@ static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx,
debug ( " write enabled \n " ) ;
sbsf - > status | = STAT_WEL ;
break ;
case CMD_WRITE_STATUS :
sbsf - > state = SF_WRITE_STATUS ;
break ;
default : {
int flags = sbsf - > data - > flags ;
@ -216,7 +295,7 @@ static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx,
sbsf - > erase_size = 64 < < 10 ;
} else {
debug ( " cmd unknown: %#x \n " , sbsf - > cmd ) ;
return 1 ;
return - EIO ;
}
sbsf - > state = SF_ADDR ;
break ;
@ -246,20 +325,27 @@ int sandbox_erase_part(struct sandbox_spi_flash *sbsf, int size)
return 0 ;
}
static int sandbox_sf_xfer ( void * priv , const u8 * rx , u8 * tx ,
uint byte s)
static int sandbox_sf_xfer ( struct udevice * dev , unsigned int bitlen ,
const void * rxp , void * txp , unsigned long flag s )
{
struct sandbox_spi_flash * sbsf = priv ;
struct sandbox_spi_flash * sbsf = dev_get_priv ( dev ) ;
const uint8_t * rx = rxp ;
uint8_t * tx = txp ;
uint cnt , pos = 0 ;
int bytes = bitlen / 8 ;
int ret ;
debug ( " sandbox_sf: state:%x(%s) bytes:%u \n " , sbsf - > state ,
sandbox_sf_state_name ( sbsf - > state ) , bytes ) ;
if ( ( flags & SPI_XFER_BEGIN ) )
sandbox_sf_cs_activate ( dev ) ;
if ( sbsf - > state = = SF_CMD ) {
/* Figure out the initial state */
if ( sandbox_sf_process_cmd ( sbsf , rx , tx ) )
return 1 ;
ret = sandbox_sf_process_cmd ( sbsf , rx , tx ) ;
if ( ret )
return ret ;
+ + pos ;
}
@ -290,7 +376,9 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx,
sbsf - > off = ( sbsf - > off < < 8 ) | rx [ pos ] ;
debug ( " addr:%06x \n " , sbsf - > off ) ;
sandbox_spi_tristate ( & tx [ pos + + ] , 1 ) ;
if ( tx )
sandbox_spi_tristate ( & tx [ pos ] , 1 ) ;
pos + + ;
/* See if we're done processing */
if ( sbsf - > addr_bytes <
@ -300,7 +388,7 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx,
/* Next state! */
if ( os_lseek ( sbsf - > fd , sbsf - > off , OS_SEEK_SET ) < 0 ) {
puts ( " sandbox_sf: os_lseek() failed " ) ;
return 1 ;
return - EIO ;
}
switch ( sbsf - > cmd ) {
case CMD_READ_ARRAY_FAST :
@ -326,10 +414,11 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx,
cnt = bytes - pos ;
debug ( " tx: read(%u) \n " , cnt ) ;
assert ( tx ) ;
ret = os_read ( sbsf - > fd , tx + pos , cnt ) ;
if ( ret < 0 ) {
puts ( " sandbox_spi : os_read() failed \n " ) ;
return 1 ;
puts ( " sandbox_sf : os_read() failed \n " ) ;
return - EIO ;
}
pos + = ret ;
break ;
@ -345,6 +434,10 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx,
memset ( tx + pos , sbsf - > status > > 8 , cnt ) ;
pos + = cnt ;
break ;
case SF_WRITE_STATUS :
debug ( " write status: %#x (ignored) \n " , rx [ pos ] ) ;
pos = bytes ;
break ;
case SF_WRITE :
/*
* XXX : need to handle exotic behavior :
@ -359,11 +452,12 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx,
cnt = bytes - pos ;
debug ( " rx: write(%u) \n " , cnt ) ;
sandbox_spi_tristate ( & tx [ pos ] , cnt ) ;
if ( tx )
sandbox_spi_tristate ( & tx [ pos ] , cnt ) ;
ret = os_write ( sbsf - > fd , rx + pos , cnt ) ;
if ( ret < 0 ) {
puts ( " sandbox_spi: os_write() failed \n " ) ;
return 1 ;
return - EIO ;
}
pos + = ret ;
sbsf - > status & = ~ STAT_WEL ;
@ -388,7 +482,8 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx,
sbsf - > erase_size ) ;
cnt = bytes - pos ;
sandbox_spi_tristate ( & tx [ pos ] , cnt ) ;
if ( tx )
sandbox_spi_tristate ( & tx [ pos ] , cnt ) ;
pos + = cnt ;
/*
@ -410,17 +505,33 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx,
}
done :
return pos = = bytes ? 0 : 1 ;
if ( flags & SPI_XFER_END )
sandbox_sf_cs_deactivate ( dev ) ;
return pos = = bytes ? 0 : - EIO ;
}
int sandbox_sf_ofdata_to_platdata ( struct udevice * dev )
{
struct sandbox_spi_flash_plat_data * pdata = dev_get_platdata ( dev ) ;
const void * blob = gd - > fdt_blob ;
int node = dev - > of_offset ;
pdata - > filename = fdt_getprop ( blob , node , " sandbox,filename " , NULL ) ;
pdata - > device_name = fdt_getprop ( blob , node , " compatible " , NULL ) ;
if ( ! pdata - > filename | | ! pdata - > device_name ) {
debug ( " %s: Missing properties, filename=%s, device_name=%s \n " ,
__func__ , pdata - > filename , pdata - > device_name ) ;
return - EINVAL ;
}
return 0 ;
}
static const struct sandbox_spi_emu_ops sandbox_sf_ops = {
. setup = sandbox_sf_setup ,
. free = sandbox_sf_free ,
. cs_activate = sandbox_sf_cs_activate ,
. cs_deactivate = sandbox_sf_cs_deactivate ,
static const struct dm_spi_emul_ops sandbox_sf_emul_ops = {
. xfer = sandbox_sf_xfer ,
} ;
# ifdef CONFIG_SPI_FLASH
static int sandbox_cmdline_cb_spi_sf ( struct sandbox_state * state ,
const char * arg )
{
@ -438,8 +549,141 @@ static int sandbox_cmdline_cb_spi_sf(struct sandbox_state *state,
* spec here , but the problem is that no U - Boot init has been done
* yet . Perhaps we can figure something out .
*/
state - > spi [ bus ] [ cs ] . ops = & sandbox_sf_ops ;
state - > spi [ bus ] [ cs ] . spec = spec ;
return 0 ;
}
SANDBOX_CMDLINE_OPT ( spi_sf , 1 , " connect a SPI flash: <bus>:<cs>:<id>:<file> " ) ;
int sandbox_sf_bind_emul ( struct sandbox_state * state , int busnum , int cs ,
struct udevice * bus , int of_offset , const char * spec )
{
struct udevice * emul ;
char name [ 20 ] , * str ;
struct driver * drv ;
int ret ;
/* now the emulator */
strncpy ( name , spec , sizeof ( name ) - 6 ) ;
name [ sizeof ( name ) - 6 ] = ' \0 ' ;
strcat ( name , " -emul " ) ;
str = strdup ( name ) ;
if ( ! str )
return - ENOMEM ;
drv = lists_driver_lookup_name ( " sandbox_sf_emul " ) ;
if ( ! drv ) {
puts ( " Cannot find sandbox_sf_emul driver \n " ) ;
return - ENOENT ;
}
ret = device_bind ( bus , drv , str , NULL , of_offset , & emul ) ;
if ( ret ) {
printf ( " Cannot create emul device for spec '%s' (err=%d) \n " ,
spec , ret ) ;
return ret ;
}
state - > spi [ busnum ] [ cs ] . emul = emul ;
return 0 ;
}
void sandbox_sf_unbind_emul ( struct sandbox_state * state , int busnum , int cs )
{
state - > spi [ busnum ] [ cs ] . emul = NULL ;
}
static int sandbox_sf_bind_bus_cs ( struct sandbox_state * state , int busnum ,
int cs , const char * spec )
{
struct udevice * bus , * slave ;
int ret ;
ret = uclass_find_device_by_seq ( UCLASS_SPI , busnum , true , & bus ) ;
if ( ret ) {
printf ( " Invalid bus %d for spec '%s' (err=%d) \n " , busnum ,
spec , ret ) ;
return ret ;
}
ret = device_find_child_by_seq ( bus , cs , true , & slave ) ;
if ( ! ret ) {
printf ( " Chip select %d already exists for spec '%s' \n " , cs ,
spec ) ;
return - EEXIST ;
}
ret = spi_bind_device ( bus , cs , " spi_flash_std " , spec , & slave ) ;
if ( ret )
return ret ;
return sandbox_sf_bind_emul ( state , busnum , cs , bus , - 1 , spec ) ;
}
int sandbox_spi_get_emul ( struct sandbox_state * state ,
struct udevice * bus , struct udevice * slave ,
struct udevice * * emulp )
{
struct sandbox_spi_info * info ;
int busnum = bus - > seq ;
int cs = spi_chip_select ( slave ) ;
int ret ;
info = & state - > spi [ busnum ] [ cs ] ;
if ( ! info - > emul ) {
/* Use the same device tree node as the SPI flash device */
debug ( " %s: busnum=%u, cs=%u: binding SPI flash emulation: " ,
__func__ , busnum , cs ) ;
ret = sandbox_sf_bind_emul ( state , busnum , cs , bus ,
slave - > of_offset , slave - > name ) ;
if ( ret ) {
debug ( " failed (err=%d) \n " , ret ) ;
return ret ;
}
debug ( " OK \n " ) ;
}
* emulp = info - > emul ;
return 0 ;
}
int dm_scan_other ( bool pre_reloc_only )
{
struct sandbox_state * state = state_get_current ( ) ;
int busnum , cs ;
if ( pre_reloc_only )
return 0 ;
for ( busnum = 0 ; busnum < CONFIG_SANDBOX_SPI_MAX_BUS ; busnum + + ) {
for ( cs = 0 ; cs < CONFIG_SANDBOX_SPI_MAX_CS ; cs + + ) {
const char * spec = state - > spi [ busnum ] [ cs ] . spec ;
int ret ;
if ( spec ) {
ret = sandbox_sf_bind_bus_cs ( state , busnum ,
cs , spec ) ;
if ( ret ) {
debug ( " %s: Bind failed for bus %d, cs %d \n " ,
__func__ , busnum , cs ) ;
return ret ;
}
}
}
}
return 0 ;
}
# endif
static const struct udevice_id sandbox_sf_ids [ ] = {
{ . compatible = " sandbox,spi-flash " } ,
{ }
} ;
U_BOOT_DRIVER ( sandbox_sf_emul ) = {
. name = " sandbox_sf_emul " ,
. id = UCLASS_SPI_EMUL ,
. of_match = sandbox_sf_ids ,
. ofdata_to_platdata = sandbox_sf_ofdata_to_platdata ,
. probe = sandbox_sf_probe ,
. remove = sandbox_sf_remove ,
. priv_auto_alloc_size = sizeof ( struct sandbox_spi_flash ) ,
. platdata_auto_alloc_size = sizeof ( struct sandbox_spi_flash_plat_data ) ,
. ops = & sandbox_sf_emul_ops ,
} ;