2008-02-09 21:20:54 +03:00
/*
* TI FlashMedia driver
*
* Copyright ( C ) 2007 Alex Dubov < oakad @ yahoo . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* Special thanks to Carlos Corbacho for providing various MemoryStick cards
* that made this driver possible .
*
*/
# include <linux/tifm.h>
# include <linux/memstick.h>
# include <linux/highmem.h>
# include <linux/scatterlist.h>
# include <linux/log2.h>
2011-07-03 23:12:37 +04:00
# include <linux/module.h>
2008-02-09 21:20:54 +03:00
# include <asm/io.h>
# define DRIVER_NAME "tifm_ms"
2012-01-13 03:02:20 +04:00
static bool no_dma ;
2008-02-09 21:20:54 +03:00
module_param ( no_dma , bool , 0644 ) ;
2008-03-10 21:43:40 +03:00
/*
* Some control bits of TIFM appear to conform to Sony ' s reference design ,
* so I ' m just assuming they all are .
*/
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
# define TIFM_MS_STAT_DRQ 0x04000
# define TIFM_MS_STAT_MSINT 0x02000
# define TIFM_MS_STAT_RDY 0x01000
# define TIFM_MS_STAT_CRC 0x00200
# define TIFM_MS_STAT_TOE 0x00100
# define TIFM_MS_STAT_EMP 0x00020
# define TIFM_MS_STAT_FUL 0x00010
# define TIFM_MS_STAT_CED 0x00008
# define TIFM_MS_STAT_ERR 0x00004
# define TIFM_MS_STAT_BRQ 0x00002
# define TIFM_MS_STAT_CNK 0x00001
# define TIFM_MS_SYS_DMA 0x10000
# define TIFM_MS_SYS_RESET 0x08000
# define TIFM_MS_SYS_SRAC 0x04000
# define TIFM_MS_SYS_INTEN 0x02000
# define TIFM_MS_SYS_NOCRC 0x01000
# define TIFM_MS_SYS_INTCLR 0x00800
# define TIFM_MS_SYS_MSIEN 0x00400
# define TIFM_MS_SYS_FCLR 0x00200
# define TIFM_MS_SYS_FDIR 0x00100
# define TIFM_MS_SYS_DAM 0x00080
# define TIFM_MS_SYS_DRM 0x00040
# define TIFM_MS_SYS_DRQSL 0x00020
# define TIFM_MS_SYS_REI 0x00010
# define TIFM_MS_SYS_REO 0x00008
# define TIFM_MS_SYS_BSY_MASK 0x00007
# define TIFM_MS_SYS_FIFO (TIFM_MS_SYS_INTEN | TIFM_MS_SYS_MSIEN \
| TIFM_MS_SYS_FCLR | TIFM_MS_SYS_BSY_MASK )
2008-02-09 21:20:54 +03:00
/* Hardware flags */
enum {
2008-03-10 21:43:40 +03:00
CMD_READY = 0x01 ,
FIFO_READY = 0x02 ,
CARD_INT = 0x04
2008-02-09 21:20:54 +03:00
} ;
struct tifm_ms {
struct tifm_dev * dev ;
2008-03-10 21:43:40 +03:00
struct timer_list timer ;
struct memstick_request * req ;
2008-07-26 06:45:02 +04:00
struct tasklet_struct notify ;
2008-02-09 21:20:54 +03:00
unsigned int mode_mask ;
unsigned int block_pos ;
unsigned long timeout_jiffies ;
2008-03-10 21:43:40 +03:00
unsigned char eject : 1 ,
use_dma : 1 ;
unsigned char cmd_flags ;
unsigned char io_pos ;
2008-02-09 21:20:54 +03:00
unsigned int io_word ;
} ;
2008-03-10 21:43:40 +03:00
static unsigned int tifm_ms_read_data ( struct tifm_ms * host ,
unsigned char * buf , unsigned int length )
2008-02-09 21:20:54 +03:00
{
struct tifm_dev * sock = host - > dev ;
2008-03-10 21:43:40 +03:00
unsigned int off = 0 ;
while ( host - > io_pos & & length ) {
buf [ off + + ] = host - > io_word & 0xff ;
host - > io_word > > = 8 ;
length - - ;
host - > io_pos - - ;
}
if ( ! length )
return off ;
while ( ! ( TIFM_MS_STAT_EMP & readl ( sock - > addr + SOCK_MS_STATUS ) ) ) {
if ( length < 4 )
break ;
* ( unsigned int * ) ( buf + off ) = __raw_readl ( sock - > addr
+ SOCK_MS_DATA ) ;
length - = 4 ;
off + = 4 ;
}
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
if ( length
& & ! ( TIFM_MS_STAT_EMP & readl ( sock - > addr + SOCK_MS_STATUS ) ) ) {
host - > io_word = readl ( sock - > addr + SOCK_MS_DATA ) ;
for ( host - > io_pos = 4 ; host - > io_pos ; - - host - > io_pos ) {
2008-02-09 21:20:54 +03:00
buf [ off + + ] = host - > io_word & 0xff ;
host - > io_word > > = 8 ;
length - - ;
2008-03-10 21:43:40 +03:00
if ( ! length )
break ;
2008-02-09 21:20:54 +03:00
}
}
2008-03-10 21:43:40 +03:00
return off ;
2008-02-09 21:20:54 +03:00
}
2008-03-10 21:43:40 +03:00
static unsigned int tifm_ms_write_data ( struct tifm_ms * host ,
unsigned char * buf , unsigned int length )
2008-02-09 21:20:54 +03:00
{
struct tifm_dev * sock = host - > dev ;
2008-03-10 21:43:40 +03:00
unsigned int off = 0 ;
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
if ( host - > io_pos ) {
while ( host - > io_pos < 4 & & length ) {
host - > io_word | = buf [ off + + ] < < ( host - > io_pos * 8 ) ;
host - > io_pos + + ;
2008-02-09 21:20:54 +03:00
length - - ;
}
}
2008-03-10 21:43:40 +03:00
if ( host - > io_pos = = 4
& & ! ( TIFM_MS_STAT_FUL & readl ( sock - > addr + SOCK_MS_STATUS ) ) ) {
writel ( TIFM_MS_SYS_FDIR | readl ( sock - > addr + SOCK_MS_SYSTEM ) ,
sock - > addr + SOCK_MS_SYSTEM ) ;
writel ( host - > io_word , sock - > addr + SOCK_MS_DATA ) ;
host - > io_pos = 0 ;
2008-02-09 21:20:54 +03:00
host - > io_word = 0 ;
2008-03-10 21:43:40 +03:00
} else if ( host - > io_pos ) {
return off ;
}
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
if ( ! length )
return off ;
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
while ( ! ( TIFM_MS_STAT_FUL & readl ( sock - > addr + SOCK_MS_STATUS ) ) ) {
if ( length < 4 )
break ;
writel ( TIFM_MS_SYS_FDIR | readl ( sock - > addr + SOCK_MS_SYSTEM ) ,
sock - > addr + SOCK_MS_SYSTEM ) ;
__raw_writel ( * ( unsigned int * ) ( buf + off ) ,
sock - > addr + SOCK_MS_DATA ) ;
length - = 4 ;
off + = 4 ;
}
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
switch ( length ) {
case 3 :
host - > io_word | = buf [ off + 2 ] < < 16 ;
host - > io_pos + + ;
case 2 :
host - > io_word | = buf [ off + 1 ] < < 8 ;
host - > io_pos + + ;
case 1 :
host - > io_word | = buf [ off ] ;
host - > io_pos + + ;
}
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
off + = host - > io_pos ;
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
return off ;
2008-02-09 21:20:54 +03:00
}
2008-03-10 21:43:40 +03:00
static unsigned int tifm_ms_transfer_data ( struct tifm_ms * host )
2008-02-09 21:20:54 +03:00
{
struct tifm_dev * sock = host - > dev ;
2008-03-10 21:43:40 +03:00
unsigned int length ;
unsigned int off ;
2008-03-29 00:16:09 +03:00
unsigned int t_size , p_cnt ;
2008-03-10 21:43:40 +03:00
unsigned char * buf ;
struct page * pg ;
unsigned long flags = 0 ;
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
if ( host - > req - > long_data ) {
length = host - > req - > sg . length - host - > block_pos ;
off = host - > req - > sg . offset + host - > block_pos ;
} else {
length = host - > req - > data_len - host - > block_pos ;
off = 0 ;
}
dev_dbg ( & sock - > dev , " fifo data transfer, %d, %d \n " , length ,
host - > block_pos ) ;
while ( length ) {
2008-03-29 00:16:09 +03:00
unsigned int uninitialized_var ( p_off ) ;
2008-03-10 21:43:40 +03:00
if ( host - > req - > long_data ) {
pg = nth_page ( sg_page ( & host - > req - > sg ) ,
off > > PAGE_SHIFT ) ;
p_off = offset_in_page ( off ) ;
p_cnt = PAGE_SIZE - p_off ;
p_cnt = min ( p_cnt , length ) ;
local_irq_save ( flags ) ;
2011-11-25 19:14:22 +04:00
buf = kmap_atomic ( pg ) + p_off ;
2008-03-10 21:43:40 +03:00
} else {
buf = host - > req - > data + host - > block_pos ;
p_cnt = host - > req - > data_len - host - > block_pos ;
}
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
t_size = host - > req - > data_dir = = WRITE
? tifm_ms_write_data ( host , buf , p_cnt )
: tifm_ms_read_data ( host , buf , p_cnt ) ;
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
if ( host - > req - > long_data ) {
2011-11-25 19:14:22 +04:00
kunmap_atomic ( buf - p_off ) ;
2008-03-10 21:43:40 +03:00
local_irq_restore ( flags ) ;
}
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
if ( ! t_size )
break ;
host - > block_pos + = t_size ;
length - = t_size ;
off + = t_size ;
}
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
dev_dbg ( & sock - > dev , " fifo data transfer, %d remaining \n " , length ) ;
if ( ! length & & ( host - > req - > data_dir = = WRITE ) ) {
if ( host - > io_pos ) {
writel ( TIFM_MS_SYS_FDIR
| readl ( sock - > addr + SOCK_MS_SYSTEM ) ,
sock - > addr + SOCK_MS_SYSTEM ) ;
writel ( host - > io_word , sock - > addr + SOCK_MS_DATA ) ;
}
writel ( TIFM_MS_SYS_FDIR
| readl ( sock - > addr + SOCK_MS_SYSTEM ) ,
sock - > addr + SOCK_MS_SYSTEM ) ;
writel ( 0 , sock - > addr + SOCK_MS_DATA ) ;
} else {
readl ( sock - > addr + SOCK_MS_DATA ) ;
}
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
return length ;
2008-02-09 21:20:54 +03:00
}
static int tifm_ms_issue_cmd ( struct tifm_ms * host )
{
struct tifm_dev * sock = host - > dev ;
unsigned char * data ;
2008-03-10 21:43:40 +03:00
unsigned int data_len , cmd , sys_param ;
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
host - > cmd_flags = 0 ;
host - > block_pos = 0 ;
host - > io_pos = 0 ;
host - > io_word = 0 ;
2008-02-09 21:20:54 +03:00
host - > cmd_flags = 0 ;
2008-03-10 21:43:40 +03:00
data = host - > req - > data ;
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
host - > use_dma = ! no_dma ;
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
if ( host - > req - > long_data ) {
data_len = host - > req - > sg . length ;
if ( ! is_power_of_2 ( data_len ) )
host - > use_dma = 0 ;
2008-03-10 21:43:37 +03:00
} else {
2008-02-09 21:20:54 +03:00
data_len = host - > req - > data_len ;
2008-03-10 21:43:40 +03:00
host - > use_dma = 0 ;
}
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
writel ( TIFM_FIFO_INT_SETALL ,
sock - > addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR ) ;
writel ( TIFM_FIFO_ENABLE ,
sock - > addr + SOCK_FIFO_CONTROL ) ;
if ( host - > use_dma ) {
if ( 1 ! = tifm_map_sg ( sock , & host - > req - > sg , 1 ,
host - > req - > data_dir = = READ
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE ) ) {
host - > req - > error = - ENOMEM ;
return host - > req - > error ;
}
data_len = sg_dma_len ( & host - > req - > sg ) ;
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
writel ( ilog2 ( data_len ) - 2 ,
sock - > addr + SOCK_FIFO_PAGE_SIZE ) ;
writel ( TIFM_FIFO_INTMASK ,
sock - > addr + SOCK_DMA_FIFO_INT_ENABLE_SET ) ;
sys_param = TIFM_DMA_EN | ( 1 < < 8 ) ;
if ( host - > req - > data_dir = = WRITE )
sys_param | = TIFM_DMA_TX ;
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
writel ( TIFM_FIFO_INTMASK ,
sock - > addr + SOCK_DMA_FIFO_INT_ENABLE_SET ) ;
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
writel ( sg_dma_address ( & host - > req - > sg ) ,
sock - > addr + SOCK_DMA_ADDRESS ) ;
writel ( sys_param , sock - > addr + SOCK_DMA_CONTROL ) ;
} else {
writel ( host - > mode_mask | TIFM_MS_SYS_FIFO ,
sock - > addr + SOCK_MS_SYSTEM ) ;
writel ( TIFM_FIFO_MORE ,
sock - > addr + SOCK_DMA_FIFO_INT_ENABLE_SET ) ;
2008-03-10 21:43:37 +03:00
}
2008-02-09 21:20:54 +03:00
mod_timer ( & host - > timer , jiffies + host - > timeout_jiffies ) ;
writel ( TIFM_CTRL_LED | readl ( sock - > addr + SOCK_CONTROL ) ,
sock - > addr + SOCK_CONTROL ) ;
host - > req - > error = 0 ;
2008-03-10 21:43:40 +03:00
sys_param = readl ( sock - > addr + SOCK_MS_SYSTEM ) ;
sys_param | = TIFM_MS_SYS_INTCLR ;
if ( host - > use_dma )
sys_param | = TIFM_MS_SYS_DMA ;
else
sys_param & = ~ TIFM_MS_SYS_DMA ;
writel ( sys_param , sock - > addr + SOCK_MS_SYSTEM ) ;
2008-02-09 21:20:54 +03:00
cmd = ( host - > req - > tpc & 0xf ) < < 12 ;
cmd | = data_len ;
writel ( cmd , sock - > addr + SOCK_MS_COMMAND ) ;
2008-03-10 21:43:40 +03:00
dev_dbg ( & sock - > dev , " executing TPC %x, %x \n " , cmd , sys_param ) ;
2008-02-09 21:20:54 +03:00
return 0 ;
}
static void tifm_ms_complete_cmd ( struct tifm_ms * host )
{
struct tifm_dev * sock = host - > dev ;
struct memstick_host * msh = tifm_get_drvdata ( sock ) ;
int rc ;
del_timer ( & host - > timer ) ;
2008-03-20 03:01:06 +03:00
host - > req - > int_reg = readl ( sock - > addr + SOCK_MS_STATUS ) & 0xff ;
host - > req - > int_reg = ( host - > req - > int_reg & 1 )
| ( ( host - > req - > int_reg < < 4 ) & 0xe0 ) ;
writel ( TIFM_FIFO_INT_SETALL ,
sock - > addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR ) ;
writel ( TIFM_DMA_RESET , sock - > addr + SOCK_DMA_CONTROL ) ;
if ( host - > use_dma ) {
2008-03-10 21:43:40 +03:00
tifm_unmap_sg ( sock , & host - > req - > sg , 1 ,
host - > req - > data_dir = = READ
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE ) ;
2008-03-20 03:01:06 +03:00
}
2008-02-09 21:20:54 +03:00
writel ( ( ~ TIFM_CTRL_LED ) & readl ( sock - > addr + SOCK_CONTROL ) ,
sock - > addr + SOCK_CONTROL ) ;
2008-03-10 21:43:40 +03:00
dev_dbg ( & sock - > dev , " TPC complete \n " ) ;
2008-02-09 21:20:54 +03:00
do {
rc = memstick_next_req ( msh , & host - > req ) ;
} while ( ! rc & & tifm_ms_issue_cmd ( host ) ) ;
}
static int tifm_ms_check_status ( struct tifm_ms * host )
{
if ( ! host - > req - > error ) {
if ( ! ( host - > cmd_flags & CMD_READY ) )
return 1 ;
2008-03-10 21:43:40 +03:00
if ( ! ( host - > cmd_flags & FIFO_READY ) )
2008-02-09 21:20:54 +03:00
return 1 ;
if ( host - > req - > need_card_int
2008-03-10 21:43:40 +03:00
& & ! ( host - > cmd_flags & CARD_INT ) )
2008-02-09 21:20:54 +03:00
return 1 ;
}
return 0 ;
}
/* Called from interrupt handler */
static void tifm_ms_data_event ( struct tifm_dev * sock )
{
struct tifm_ms * host ;
2008-03-10 21:43:40 +03:00
unsigned int fifo_status = 0 , host_status = 0 ;
2008-02-09 21:20:54 +03:00
int rc = 1 ;
spin_lock ( & sock - > lock ) ;
host = memstick_priv ( ( struct memstick_host * ) tifm_get_drvdata ( sock ) ) ;
fifo_status = readl ( sock - > addr + SOCK_DMA_FIFO_STATUS ) ;
2008-03-10 21:43:40 +03:00
host_status = readl ( sock - > addr + SOCK_MS_STATUS ) ;
dev_dbg ( & sock - > dev ,
" data event: fifo_status %x, host_status %x, flags %x \n " ,
fifo_status , host_status , host - > cmd_flags ) ;
2008-02-09 21:20:54 +03:00
if ( host - > req ) {
2008-03-10 21:43:40 +03:00
if ( host - > use_dma & & ( fifo_status & 1 ) ) {
host - > cmd_flags | = FIFO_READY ;
rc = tifm_ms_check_status ( host ) ;
}
if ( ! host - > use_dma & & ( fifo_status & TIFM_FIFO_MORE ) ) {
if ( ! tifm_ms_transfer_data ( host ) ) {
2008-02-09 21:20:54 +03:00
host - > cmd_flags | = FIFO_READY ;
rc = tifm_ms_check_status ( host ) ;
}
}
}
writel ( fifo_status , sock - > addr + SOCK_DMA_FIFO_STATUS ) ;
if ( ! rc )
tifm_ms_complete_cmd ( host ) ;
spin_unlock ( & sock - > lock ) ;
}
/* Called from interrupt handler */
static void tifm_ms_card_event ( struct tifm_dev * sock )
{
struct tifm_ms * host ;
unsigned int host_status = 0 ;
int rc = 1 ;
spin_lock ( & sock - > lock ) ;
host = memstick_priv ( ( struct memstick_host * ) tifm_get_drvdata ( sock ) ) ;
host_status = readl ( sock - > addr + SOCK_MS_STATUS ) ;
dev_dbg ( & sock - > dev , " host event: host_status %x, flags %x \n " ,
host_status , host - > cmd_flags ) ;
if ( host - > req ) {
2008-03-10 21:43:40 +03:00
if ( host_status & TIFM_MS_STAT_TOE )
2008-02-09 21:20:54 +03:00
host - > req - > error = - ETIME ;
2008-03-10 21:43:40 +03:00
else if ( host_status & TIFM_MS_STAT_CRC )
2008-02-09 21:20:54 +03:00
host - > req - > error = - EILSEQ ;
2008-03-10 21:43:40 +03:00
if ( host_status & TIFM_MS_STAT_RDY )
2008-02-09 21:20:54 +03:00
host - > cmd_flags | = CMD_READY ;
2008-03-10 21:43:40 +03:00
if ( host_status & TIFM_MS_STAT_MSINT )
host - > cmd_flags | = CARD_INT ;
2008-02-09 21:20:54 +03:00
rc = tifm_ms_check_status ( host ) ;
}
2008-03-10 21:43:40 +03:00
writel ( TIFM_MS_SYS_INTCLR | readl ( sock - > addr + SOCK_MS_SYSTEM ) ,
2008-02-09 21:20:54 +03:00
sock - > addr + SOCK_MS_SYSTEM ) ;
if ( ! rc )
tifm_ms_complete_cmd ( host ) ;
spin_unlock ( & sock - > lock ) ;
return ;
}
2008-07-26 06:45:02 +04:00
static void tifm_ms_req_tasklet ( unsigned long data )
2008-02-09 21:20:54 +03:00
{
2008-07-26 06:45:02 +04:00
struct memstick_host * msh = ( struct memstick_host * ) data ;
2008-02-09 21:20:54 +03:00
struct tifm_ms * host = memstick_priv ( msh ) ;
struct tifm_dev * sock = host - > dev ;
unsigned long flags ;
int rc ;
spin_lock_irqsave ( & sock - > lock , flags ) ;
2008-07-26 06:45:02 +04:00
if ( ! host - > req ) {
if ( host - > eject ) {
do {
rc = memstick_next_req ( msh , & host - > req ) ;
if ( ! rc )
host - > req - > error = - ETIME ;
} while ( ! rc ) ;
spin_unlock_irqrestore ( & sock - > lock , flags ) ;
return ;
}
2008-02-09 21:20:54 +03:00
do {
rc = memstick_next_req ( msh , & host - > req ) ;
2008-07-26 06:45:02 +04:00
} while ( ! rc & & tifm_ms_issue_cmd ( host ) ) ;
2008-02-09 21:20:54 +03:00
}
spin_unlock_irqrestore ( & sock - > lock , flags ) ;
2008-07-26 06:45:02 +04:00
}
static void tifm_ms_dummy_submit ( struct memstick_host * msh )
{
2008-02-09 21:20:54 +03:00
return ;
}
2008-07-26 06:45:02 +04:00
static void tifm_ms_submit_req ( struct memstick_host * msh )
{
struct tifm_ms * host = memstick_priv ( msh ) ;
tasklet_schedule ( & host - > notify ) ;
}
2008-07-26 06:45:00 +04:00
static int tifm_ms_set_param ( struct memstick_host * msh ,
enum memstick_param param ,
int value )
2008-02-09 21:20:54 +03:00
{
struct tifm_ms * host = memstick_priv ( msh ) ;
struct tifm_dev * sock = host - > dev ;
switch ( param ) {
case MEMSTICK_POWER :
2008-03-10 21:43:40 +03:00
/* also affected by media detection mechanism */
if ( value = = MEMSTICK_POWER_ON ) {
host - > mode_mask = TIFM_MS_SYS_SRAC | TIFM_MS_SYS_REI ;
writel ( TIFM_MS_SYS_RESET , sock - > addr + SOCK_MS_SYSTEM ) ;
writel ( TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR ,
sock - > addr + SOCK_MS_SYSTEM ) ;
writel ( 0xffffffff , sock - > addr + SOCK_MS_STATUS ) ;
} else if ( value = = MEMSTICK_POWER_OFF ) {
writel ( TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR ,
sock - > addr + SOCK_MS_SYSTEM ) ;
writel ( 0xffffffff , sock - > addr + SOCK_MS_STATUS ) ;
2008-07-26 06:45:00 +04:00
} else
return - EINVAL ;
2008-02-09 21:20:54 +03:00
break ;
case MEMSTICK_INTERFACE :
if ( value = = MEMSTICK_SERIAL ) {
2008-03-10 21:43:40 +03:00
host - > mode_mask = TIFM_MS_SYS_SRAC | TIFM_MS_SYS_REI ;
2008-02-09 21:20:54 +03:00
writel ( ( ~ TIFM_CTRL_FAST_CLK )
& readl ( sock - > addr + SOCK_CONTROL ) ,
sock - > addr + SOCK_CONTROL ) ;
2008-03-10 21:43:37 +03:00
} else if ( value = = MEMSTICK_PAR4 ) {
2008-02-09 21:20:54 +03:00
host - > mode_mask = 0 ;
writel ( TIFM_CTRL_FAST_CLK
| readl ( sock - > addr + SOCK_CONTROL ) ,
sock - > addr + SOCK_CONTROL ) ;
2008-07-26 06:45:00 +04:00
} else
return - EINVAL ;
2008-02-09 21:20:54 +03:00
break ;
} ;
2008-07-26 06:45:00 +04:00
return 0 ;
2008-02-09 21:20:54 +03:00
}
static void tifm_ms_abort ( unsigned long data )
{
struct tifm_ms * host = ( struct tifm_ms * ) data ;
dev_dbg ( & host - > dev - > dev , " status %x \n " ,
readl ( host - > dev - > addr + SOCK_MS_STATUS ) ) ;
printk ( KERN_ERR
" %s : card failed to respond for a long period of time "
" (%x, %x) \n " ,
2009-01-06 21:44:38 +03:00
dev_name ( & host - > dev - > dev ) , host - > req ? host - > req - > tpc : 0 ,
2008-02-09 21:20:54 +03:00
host - > cmd_flags ) ;
tifm_eject ( host - > dev ) ;
}
static int tifm_ms_probe ( struct tifm_dev * sock )
{
struct memstick_host * msh ;
struct tifm_ms * host ;
int rc = - EIO ;
if ( ! ( TIFM_SOCK_STATE_OCCUPIED
& readl ( sock - > addr + SOCK_PRESENT_STATE ) ) ) {
printk ( KERN_WARNING " %s : card gone, unexpectedly \n " ,
2009-01-06 21:44:38 +03:00
dev_name ( & sock - > dev ) ) ;
2008-02-09 21:20:54 +03:00
return rc ;
}
msh = memstick_alloc_host ( sizeof ( struct tifm_ms ) , & sock - > dev ) ;
if ( ! msh )
return - ENOMEM ;
host = memstick_priv ( msh ) ;
tifm_set_drvdata ( sock , msh ) ;
host - > dev = sock ;
host - > timeout_jiffies = msecs_to_jiffies ( 1000 ) ;
setup_timer ( & host - > timer , tifm_ms_abort , ( unsigned long ) host ) ;
2008-07-26 06:45:02 +04:00
tasklet_init ( & host - > notify , tifm_ms_req_tasklet , ( unsigned long ) msh ) ;
2008-02-09 21:20:54 +03:00
2008-07-26 06:45:02 +04:00
msh - > request = tifm_ms_submit_req ;
2008-02-09 21:20:54 +03:00
msh - > set_param = tifm_ms_set_param ;
sock - > card_event = tifm_ms_card_event ;
sock - > data_event = tifm_ms_data_event ;
2008-03-10 21:43:40 +03:00
if ( tifm_has_ms_pif ( sock ) )
msh - > caps | = MEMSTICK_CAP_PAR4 ;
2008-02-09 21:20:54 +03:00
2008-03-10 21:43:40 +03:00
rc = memstick_add_host ( msh ) ;
2008-02-09 21:20:54 +03:00
if ( ! rc )
return 0 ;
memstick_free_host ( msh ) ;
return rc ;
}
static void tifm_ms_remove ( struct tifm_dev * sock )
{
struct memstick_host * msh = tifm_get_drvdata ( sock ) ;
struct tifm_ms * host = memstick_priv ( msh ) ;
int rc = 0 ;
unsigned long flags ;
2008-07-26 06:45:02 +04:00
msh - > request = tifm_ms_dummy_submit ;
tasklet_kill ( & host - > notify ) ;
2008-02-09 21:20:54 +03:00
spin_lock_irqsave ( & sock - > lock , flags ) ;
host - > eject = 1 ;
if ( host - > req ) {
del_timer ( & host - > timer ) ;
writel ( TIFM_FIFO_INT_SETALL ,
sock - > addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR ) ;
writel ( TIFM_DMA_RESET , sock - > addr + SOCK_DMA_CONTROL ) ;
2008-03-10 21:43:40 +03:00
if ( host - > use_dma )
2008-02-09 21:20:54 +03:00
tifm_unmap_sg ( sock , & host - > req - > sg , 1 ,
host - > req - > data_dir = = READ
? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE ) ;
host - > req - > error = - ETIME ;
do {
rc = memstick_next_req ( msh , & host - > req ) ;
if ( ! rc )
host - > req - > error = - ETIME ;
} while ( ! rc ) ;
}
spin_unlock_irqrestore ( & sock - > lock , flags ) ;
memstick_remove_host ( msh ) ;
memstick_free_host ( msh ) ;
}
# ifdef CONFIG_PM
static int tifm_ms_suspend ( struct tifm_dev * sock , pm_message_t state )
{
2008-03-10 21:43:38 +03:00
struct memstick_host * msh = tifm_get_drvdata ( sock ) ;
memstick_suspend_host ( msh ) ;
2008-02-09 21:20:54 +03:00
return 0 ;
}
static int tifm_ms_resume ( struct tifm_dev * sock )
{
struct memstick_host * msh = tifm_get_drvdata ( sock ) ;
2008-03-10 21:43:38 +03:00
memstick_resume_host ( msh ) ;
2008-02-09 21:20:54 +03:00
return 0 ;
}
# else
# define tifm_ms_suspend NULL
# define tifm_ms_resume NULL
# endif /* CONFIG_PM */
static struct tifm_device_id tifm_ms_id_tbl [ ] = {
{ TIFM_TYPE_MS } , { 0 }
} ;
static struct tifm_driver tifm_ms_driver = {
. driver = {
. name = DRIVER_NAME ,
. owner = THIS_MODULE
} ,
. id_table = tifm_ms_id_tbl ,
. probe = tifm_ms_probe ,
. remove = tifm_ms_remove ,
. suspend = tifm_ms_suspend ,
. resume = tifm_ms_resume
} ;
static int __init tifm_ms_init ( void )
{
return tifm_register_driver ( & tifm_ms_driver ) ;
}
static void __exit tifm_ms_exit ( void )
{
tifm_unregister_driver ( & tifm_ms_driver ) ;
}
MODULE_AUTHOR ( " Alex Dubov " ) ;
MODULE_DESCRIPTION ( " TI FlashMedia MemoryStick driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DEVICE_TABLE ( tifm , tifm_ms_id_tbl ) ;
module_init ( tifm_ms_init ) ;
module_exit ( tifm_ms_exit ) ;