2006-03-29 09:21:00 +01:00
/*
* linux / drivers / media / mmc / omap . c
*
* Copyright ( C ) 2004 Nokia Corporation
* Written by Tuukka Tikkanen and Juha Yrj <EFBFBD> l <EFBFBD> < juha . yrjola @ nokia . com >
* Misc hacks here and there by Tony Lindgren < tony @ atomide . com >
* Other hacks ( DMA , SD , etc ) by David Brownell
*
* 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 .
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/ioport.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/dma-mapping.h>
# include <linux/delay.h>
# include <linux/spinlock.h>
# include <linux/timer.h>
# include <linux/mmc/host.h>
# include <linux/mmc/protocol.h>
# include <linux/mmc/card.h>
# include <linux/clk.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/scatterlist.h>
# include <asm/mach-types.h>
# include <asm/arch/board.h>
# include <asm/arch/gpio.h>
# include <asm/arch/dma.h>
# include <asm/arch/mux.h>
# include <asm/arch/fpga.h>
# include <asm/arch/tps65010.h>
# include "omap.h"
# define DRIVER_NAME "mmci-omap"
# define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
/* Specifies how often in millisecs to poll for card status changes
* when the cover switch is open */
# define OMAP_MMC_SWITCH_POLL_DELAY 500
static int mmc_omap_enable_poll = 1 ;
struct mmc_omap_host {
int initialized ;
int suspended ;
struct mmc_request * mrq ;
struct mmc_command * cmd ;
struct mmc_data * data ;
struct mmc_host * mmc ;
struct device * dev ;
unsigned char id ; /* 16xx chips have 2 MMC blocks */
struct clk * iclk ;
struct clk * fclk ;
2006-07-01 19:56:44 +01:00
struct resource * res ;
2006-03-29 09:21:00 +01:00
void __iomem * base ;
int irq ;
unsigned char bus_mode ;
unsigned char hw_bus_mode ;
unsigned int sg_len ;
int sg_idx ;
u16 * buffer ;
u32 buffer_bytes_left ;
u32 total_bytes_left ;
unsigned use_dma : 1 ;
unsigned brs_received : 1 , dma_done : 1 ;
unsigned dma_is_read : 1 ;
unsigned dma_in_use : 1 ;
int dma_ch ;
spinlock_t dma_lock ;
struct timer_list dma_timer ;
unsigned dma_len ;
short power_pin ;
short wp_pin ;
int switch_pin ;
struct work_struct switch_work ;
struct timer_list switch_timer ;
int switch_last_state ;
} ;
static inline int
mmc_omap_cover_is_open ( struct mmc_omap_host * host )
{
if ( host - > switch_pin < 0 )
return 0 ;
return omap_get_gpio_datain ( host - > switch_pin ) ;
}
static ssize_t
mmc_omap_show_cover_switch ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct mmc_omap_host * host = dev_get_drvdata ( dev ) ;
return sprintf ( buf , " %s \n " , mmc_omap_cover_is_open ( host ) ? " open " :
" closed " ) ;
}
static DEVICE_ATTR ( cover_switch , S_IRUGO , mmc_omap_show_cover_switch , NULL ) ;
static ssize_t
mmc_omap_show_enable_poll ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return snprintf ( buf , PAGE_SIZE , " %d \n " , mmc_omap_enable_poll ) ;
}
static ssize_t
mmc_omap_store_enable_poll ( struct device * dev ,
struct device_attribute * attr , const char * buf ,
size_t size )
{
int enable_poll ;
if ( sscanf ( buf , " %10d " , & enable_poll ) ! = 1 )
return - EINVAL ;
if ( enable_poll ! = mmc_omap_enable_poll ) {
struct mmc_omap_host * host = dev_get_drvdata ( dev ) ;
mmc_omap_enable_poll = enable_poll ;
if ( enable_poll & & host - > switch_pin > = 0 )
schedule_work ( & host - > switch_work ) ;
}
return size ;
}
static DEVICE_ATTR ( enable_poll , 0664 ,
mmc_omap_show_enable_poll , mmc_omap_store_enable_poll ) ;
static void
mmc_omap_start_command ( struct mmc_omap_host * host , struct mmc_command * cmd )
{
u32 cmdreg ;
u32 resptype ;
u32 cmdtype ;
host - > cmd = cmd ;
resptype = 0 ;
cmdtype = 0 ;
/* Our hardware needs to know exact type */
switch ( RSP_TYPE ( mmc_resp_type ( cmd ) ) ) {
case RSP_TYPE ( MMC_RSP_R1 ) :
/* resp 1, resp 1b */
resptype = 1 ;
break ;
case RSP_TYPE ( MMC_RSP_R2 ) :
resptype = 2 ;
break ;
case RSP_TYPE ( MMC_RSP_R3 ) :
resptype = 3 ;
break ;
default :
break ;
}
if ( mmc_cmd_type ( cmd ) = = MMC_CMD_ADTC ) {
cmdtype = OMAP_MMC_CMDTYPE_ADTC ;
} else if ( mmc_cmd_type ( cmd ) = = MMC_CMD_BC ) {
cmdtype = OMAP_MMC_CMDTYPE_BC ;
} else if ( mmc_cmd_type ( cmd ) = = MMC_CMD_BCR ) {
cmdtype = OMAP_MMC_CMDTYPE_BCR ;
} else {
cmdtype = OMAP_MMC_CMDTYPE_AC ;
}
cmdreg = cmd - > opcode | ( resptype < < 8 ) | ( cmdtype < < 12 ) ;
if ( host - > bus_mode = = MMC_BUSMODE_OPENDRAIN )
cmdreg | = 1 < < 6 ;
if ( cmd - > flags & MMC_RSP_BUSY )
cmdreg | = 1 < < 11 ;
if ( host - > data & & ! ( host - > data - > flags & MMC_DATA_WRITE ) )
cmdreg | = 1 < < 15 ;
clk_enable ( host - > fclk ) ;
OMAP_MMC_WRITE ( host - > base , CTO , 200 ) ;
OMAP_MMC_WRITE ( host - > base , ARGL , cmd - > arg & 0xffff ) ;
OMAP_MMC_WRITE ( host - > base , ARGH , cmd - > arg > > 16 ) ;
OMAP_MMC_WRITE ( host - > base , IE ,
OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL |
OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT |
OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT |
OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR |
OMAP_MMC_STAT_END_OF_DATA ) ;
OMAP_MMC_WRITE ( host - > base , CMD , cmdreg ) ;
}
static void
mmc_omap_xfer_done ( struct mmc_omap_host * host , struct mmc_data * data )
{
if ( host - > dma_in_use ) {
enum dma_data_direction dma_data_dir ;
BUG_ON ( host - > dma_ch < 0 ) ;
if ( data - > error ! = MMC_ERR_NONE )
omap_stop_dma ( host - > dma_ch ) ;
/* Release DMA channel lazily */
mod_timer ( & host - > dma_timer , jiffies + HZ ) ;
if ( data - > flags & MMC_DATA_WRITE )
dma_data_dir = DMA_TO_DEVICE ;
else
dma_data_dir = DMA_FROM_DEVICE ;
dma_unmap_sg ( mmc_dev ( host - > mmc ) , data - > sg , host - > sg_len ,
dma_data_dir ) ;
}
host - > data = NULL ;
host - > sg_len = 0 ;
clk_disable ( host - > fclk ) ;
/* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing
* dozens of requests until the card finishes writing data .
* It ' d be cheaper to just wait till an EOFB interrupt arrives . . .
*/
if ( ! data - > stop ) {
host - > mrq = NULL ;
mmc_request_done ( host - > mmc , data - > mrq ) ;
return ;
}
mmc_omap_start_command ( host , data - > stop ) ;
}
static void
mmc_omap_end_of_data ( struct mmc_omap_host * host , struct mmc_data * data )
{
unsigned long flags ;
int done ;
if ( ! host - > dma_in_use ) {
mmc_omap_xfer_done ( host , data ) ;
return ;
}
done = 0 ;
spin_lock_irqsave ( & host - > dma_lock , flags ) ;
if ( host - > dma_done )
done = 1 ;
else
host - > brs_received = 1 ;
spin_unlock_irqrestore ( & host - > dma_lock , flags ) ;
if ( done )
mmc_omap_xfer_done ( host , data ) ;
}
static void
mmc_omap_dma_timer ( unsigned long data )
{
struct mmc_omap_host * host = ( struct mmc_omap_host * ) data ;
BUG_ON ( host - > dma_ch < 0 ) ;
omap_free_dma ( host - > dma_ch ) ;
host - > dma_ch = - 1 ;
}
static void
mmc_omap_dma_done ( struct mmc_omap_host * host , struct mmc_data * data )
{
unsigned long flags ;
int done ;
done = 0 ;
spin_lock_irqsave ( & host - > dma_lock , flags ) ;
if ( host - > brs_received )
done = 1 ;
else
host - > dma_done = 1 ;
spin_unlock_irqrestore ( & host - > dma_lock , flags ) ;
if ( done )
mmc_omap_xfer_done ( host , data ) ;
}
static void
mmc_omap_cmd_done ( struct mmc_omap_host * host , struct mmc_command * cmd )
{
host - > cmd = NULL ;
if ( cmd - > flags & MMC_RSP_PRESENT ) {
if ( cmd - > flags & MMC_RSP_136 ) {
/* response type 2 */
cmd - > resp [ 3 ] =
OMAP_MMC_READ ( host - > base , RSP0 ) |
( OMAP_MMC_READ ( host - > base , RSP1 ) < < 16 ) ;
cmd - > resp [ 2 ] =
OMAP_MMC_READ ( host - > base , RSP2 ) |
( OMAP_MMC_READ ( host - > base , RSP3 ) < < 16 ) ;
cmd - > resp [ 1 ] =
OMAP_MMC_READ ( host - > base , RSP4 ) |
( OMAP_MMC_READ ( host - > base , RSP5 ) < < 16 ) ;
cmd - > resp [ 0 ] =
OMAP_MMC_READ ( host - > base , RSP6 ) |
( OMAP_MMC_READ ( host - > base , RSP7 ) < < 16 ) ;
} else {
/* response types 1, 1b, 3, 4, 5, 6 */
cmd - > resp [ 0 ] =
OMAP_MMC_READ ( host - > base , RSP6 ) |
( OMAP_MMC_READ ( host - > base , RSP7 ) < < 16 ) ;
}
}
if ( host - > data = = NULL | | cmd - > error ! = MMC_ERR_NONE ) {
host - > mrq = NULL ;
clk_disable ( host - > fclk ) ;
mmc_request_done ( host - > mmc , cmd - > mrq ) ;
}
}
/* PIO only */
static void
mmc_omap_sg_to_buf ( struct mmc_omap_host * host )
{
struct scatterlist * sg ;
sg = host - > data - > sg + host - > sg_idx ;
host - > buffer_bytes_left = sg - > length ;
host - > buffer = page_address ( sg - > page ) + sg - > offset ;
if ( host - > buffer_bytes_left > host - > total_bytes_left )
host - > buffer_bytes_left = host - > total_bytes_left ;
}
/* PIO only */
static void
mmc_omap_xfer_data ( struct mmc_omap_host * host , int write )
{
int n ;
if ( host - > buffer_bytes_left = = 0 ) {
host - > sg_idx + + ;
BUG_ON ( host - > sg_idx = = host - > sg_len ) ;
mmc_omap_sg_to_buf ( host ) ;
}
n = 64 ;
if ( n > host - > buffer_bytes_left )
n = host - > buffer_bytes_left ;
host - > buffer_bytes_left - = n ;
host - > total_bytes_left - = n ;
host - > data - > bytes_xfered + = n ;
if ( write ) {
__raw_writesw ( host - > base + OMAP_MMC_REG_DATA , host - > buffer , n ) ;
} else {
__raw_readsw ( host - > base + OMAP_MMC_REG_DATA , host - > buffer , n ) ;
}
}
static inline void mmc_omap_report_irq ( u16 status )
{
static const char * mmc_omap_status_bits [ ] = {
" EOC " , " CD " , " CB " , " BRS " , " EOFB " , " DTO " , " DCRC " , " CTO " ,
" CCRC " , " CRW " , " AF " , " AE " , " OCRB " , " CIRQ " , " CERR "
} ;
int i , c = 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( mmc_omap_status_bits ) ; i + + )
if ( status & ( 1 < < i ) ) {
if ( c )
printk ( " " ) ;
printk ( " %s " , mmc_omap_status_bits [ i ] ) ;
c + + ;
}
}
static irqreturn_t mmc_omap_irq ( int irq , void * dev_id , struct pt_regs * regs )
{
struct mmc_omap_host * host = ( struct mmc_omap_host * ) dev_id ;
u16 status ;
int end_command ;
int end_transfer ;
int transfer_error ;
if ( host - > cmd = = NULL & & host - > data = = NULL ) {
status = OMAP_MMC_READ ( host - > base , STAT ) ;
dev_info ( mmc_dev ( host - > mmc ) , " spurious irq 0x%04x \n " , status ) ;
if ( status ! = 0 ) {
OMAP_MMC_WRITE ( host - > base , STAT , status ) ;
OMAP_MMC_WRITE ( host - > base , IE , 0 ) ;
}
return IRQ_HANDLED ;
}
end_command = 0 ;
end_transfer = 0 ;
transfer_error = 0 ;
while ( ( status = OMAP_MMC_READ ( host - > base , STAT ) ) ! = 0 ) {
OMAP_MMC_WRITE ( host - > base , STAT , status ) ;
# ifdef CONFIG_MMC_DEBUG
dev_dbg ( mmc_dev ( host - > mmc ) , " MMC IRQ %04x (CMD %d): " ,
status , host - > cmd ! = NULL ? host - > cmd - > opcode : - 1 ) ;
mmc_omap_report_irq ( status ) ;
printk ( " \n " ) ;
# endif
if ( host - > total_bytes_left ) {
if ( ( status & OMAP_MMC_STAT_A_FULL ) | |
( status & OMAP_MMC_STAT_END_OF_DATA ) )
mmc_omap_xfer_data ( host , 0 ) ;
if ( status & OMAP_MMC_STAT_A_EMPTY )
mmc_omap_xfer_data ( host , 1 ) ;
}
if ( status & OMAP_MMC_STAT_END_OF_DATA ) {
end_transfer = 1 ;
}
if ( status & OMAP_MMC_STAT_DATA_TOUT ) {
dev_dbg ( mmc_dev ( host - > mmc ) , " data timeout \n " ) ;
if ( host - > data ) {
host - > data - > error | = MMC_ERR_TIMEOUT ;
transfer_error = 1 ;
}
}
if ( status & OMAP_MMC_STAT_DATA_CRC ) {
if ( host - > data ) {
host - > data - > error | = MMC_ERR_BADCRC ;
dev_dbg ( mmc_dev ( host - > mmc ) ,
" data CRC error, bytes left %d \n " ,
host - > total_bytes_left ) ;
transfer_error = 1 ;
} else {
dev_dbg ( mmc_dev ( host - > mmc ) , " data CRC error \n " ) ;
}
}
if ( status & OMAP_MMC_STAT_CMD_TOUT ) {
/* Timeouts are routine with some commands */
if ( host - > cmd ) {
if ( host - > cmd - > opcode ! = MMC_ALL_SEND_CID & &
host - > cmd - > opcode ! =
MMC_SEND_OP_COND & &
host - > cmd - > opcode ! =
MMC_APP_CMD & &
! mmc_omap_cover_is_open ( host ) )
dev_err ( mmc_dev ( host - > mmc ) ,
" command timeout, CMD %d \n " ,
host - > cmd - > opcode ) ;
host - > cmd - > error = MMC_ERR_TIMEOUT ;
end_command = 1 ;
}
}
if ( status & OMAP_MMC_STAT_CMD_CRC ) {
if ( host - > cmd ) {
dev_err ( mmc_dev ( host - > mmc ) ,
" command CRC error (CMD%d, arg 0x%08x) \n " ,
host - > cmd - > opcode , host - > cmd - > arg ) ;
host - > cmd - > error = MMC_ERR_BADCRC ;
end_command = 1 ;
} else
dev_err ( mmc_dev ( host - > mmc ) ,
" command CRC error without cmd? \n " ) ;
}
if ( status & OMAP_MMC_STAT_CARD_ERR ) {
if ( host - > cmd & & host - > cmd - > opcode = = MMC_STOP_TRANSMISSION ) {
u32 response = OMAP_MMC_READ ( host - > base , RSP6 )
| ( OMAP_MMC_READ ( host - > base , RSP7 ) < < 16 ) ;
/* STOP sometimes sets must-ignore bits */
if ( ! ( response & ( R1_CC_ERROR
| R1_ILLEGAL_COMMAND
| R1_COM_CRC_ERROR ) ) ) {
end_command = 1 ;
continue ;
}
}
dev_dbg ( mmc_dev ( host - > mmc ) , " card status error (CMD%d) \n " ,
host - > cmd - > opcode ) ;
if ( host - > cmd ) {
host - > cmd - > error = MMC_ERR_FAILED ;
end_command = 1 ;
}
if ( host - > data ) {
host - > data - > error = MMC_ERR_FAILED ;
transfer_error = 1 ;
}
}
/*
* NOTE : On 1610 the END_OF_CMD may come too early when
* starting a write
*/
if ( ( status & OMAP_MMC_STAT_END_OF_CMD ) & &
( ! ( status & OMAP_MMC_STAT_A_EMPTY ) ) ) {
end_command = 1 ;
}
}
if ( end_command ) {
mmc_omap_cmd_done ( host , host - > cmd ) ;
}
if ( transfer_error )
mmc_omap_xfer_done ( host , host - > data ) ;
else if ( end_transfer )
mmc_omap_end_of_data ( host , host - > data ) ;
return IRQ_HANDLED ;
}
static irqreturn_t mmc_omap_switch_irq ( int irq , void * dev_id , struct pt_regs * regs )
{
struct mmc_omap_host * host = ( struct mmc_omap_host * ) dev_id ;
schedule_work ( & host - > switch_work ) ;
return IRQ_HANDLED ;
}
static void mmc_omap_switch_timer ( unsigned long arg )
{
struct mmc_omap_host * host = ( struct mmc_omap_host * ) arg ;
schedule_work ( & host - > switch_work ) ;
}
/* FIXME: Handle card insertion and removal properly. Maybe use a mask
* for MMC state ? */
static void mmc_omap_switch_callback ( unsigned long data , u8 mmc_mask )
{
}
static void mmc_omap_switch_handler ( void * data )
{
struct mmc_omap_host * host = ( struct mmc_omap_host * ) data ;
struct mmc_card * card ;
static int complained = 0 ;
int cards = 0 , cover_open ;
if ( host - > switch_pin = = - 1 )
return ;
cover_open = mmc_omap_cover_is_open ( host ) ;
if ( cover_open ! = host - > switch_last_state ) {
kobject_uevent ( & host - > dev - > kobj , KOBJ_CHANGE ) ;
host - > switch_last_state = cover_open ;
}
mmc_detect_change ( host - > mmc , 0 ) ;
list_for_each_entry ( card , & host - > mmc - > cards , node ) {
if ( mmc_card_present ( card ) )
cards + + ;
}
if ( mmc_omap_cover_is_open ( host ) ) {
if ( ! complained ) {
dev_info ( mmc_dev ( host - > mmc ) , " cover is open " ) ;
complained = 1 ;
}
if ( mmc_omap_enable_poll )
mod_timer ( & host - > switch_timer , jiffies +
msecs_to_jiffies ( OMAP_MMC_SWITCH_POLL_DELAY ) ) ;
} else {
complained = 0 ;
}
}
/* Prepare to transfer the next segment of a scatterlist */
static void
mmc_omap_prepare_dma ( struct mmc_omap_host * host , struct mmc_data * data )
{
int dma_ch = host - > dma_ch ;
unsigned long data_addr ;
u16 buf , frame ;
u32 count ;
struct scatterlist * sg = & data - > sg [ host - > sg_idx ] ;
int src_port = 0 ;
int dst_port = 0 ;
int sync_dev = 0 ;
data_addr = io_v2p ( ( u32 ) host - > base ) + OMAP_MMC_REG_DATA ;
2006-06-04 17:51:15 +01:00
frame = data - > blksz ;
2006-03-29 09:21:00 +01:00
count = sg_dma_len ( sg ) ;
2006-06-04 17:51:15 +01:00
if ( ( data - > blocks = = 1 ) & & ( count > data - > blksz ) )
2006-03-29 09:21:00 +01:00
count = frame ;
host - > dma_len = count ;
/* FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx and 24xx.
* Use 16 or 32 word frames when the blocksize is at least that large .
* Blocksize is usually 512 bytes ; but not for some SD reads .
*/
if ( cpu_is_omap15xx ( ) & & frame > 32 )
frame = 32 ;
else if ( frame > 64 )
frame = 64 ;
count / = frame ;
frame > > = 1 ;
if ( ! ( data - > flags & MMC_DATA_WRITE ) ) {
buf = 0x800f | ( ( frame - 1 ) < < 8 ) ;
if ( cpu_class_is_omap1 ( ) ) {
src_port = OMAP_DMA_PORT_TIPB ;
dst_port = OMAP_DMA_PORT_EMIFF ;
}
if ( cpu_is_omap24xx ( ) )
sync_dev = OMAP24XX_DMA_MMC1_RX ;
omap_set_dma_src_params ( dma_ch , src_port ,
OMAP_DMA_AMODE_CONSTANT ,
data_addr , 0 , 0 ) ;
omap_set_dma_dest_params ( dma_ch , dst_port ,
OMAP_DMA_AMODE_POST_INC ,
sg_dma_address ( sg ) , 0 , 0 ) ;
omap_set_dma_dest_data_pack ( dma_ch , 1 ) ;
omap_set_dma_dest_burst_mode ( dma_ch , OMAP_DMA_DATA_BURST_4 ) ;
} else {
buf = 0x0f80 | ( ( frame - 1 ) < < 0 ) ;
if ( cpu_class_is_omap1 ( ) ) {
src_port = OMAP_DMA_PORT_EMIFF ;
dst_port = OMAP_DMA_PORT_TIPB ;
}
if ( cpu_is_omap24xx ( ) )
sync_dev = OMAP24XX_DMA_MMC1_TX ;
omap_set_dma_dest_params ( dma_ch , dst_port ,
OMAP_DMA_AMODE_CONSTANT ,
data_addr , 0 , 0 ) ;
omap_set_dma_src_params ( dma_ch , src_port ,
OMAP_DMA_AMODE_POST_INC ,
sg_dma_address ( sg ) , 0 , 0 ) ;
omap_set_dma_src_data_pack ( dma_ch , 1 ) ;
omap_set_dma_src_burst_mode ( dma_ch , OMAP_DMA_DATA_BURST_4 ) ;
}
/* Max limit for DMA frame count is 0xffff */
if ( unlikely ( count > 0xffff ) )
BUG ( ) ;
OMAP_MMC_WRITE ( host - > base , BUF , buf ) ;
omap_set_dma_transfer_params ( dma_ch , OMAP_DMA_DATA_TYPE_S16 ,
frame , count , OMAP_DMA_SYNC_FRAME ,
sync_dev , 0 ) ;
}
/* A scatterlist segment completed */
static void mmc_omap_dma_cb ( int lch , u16 ch_status , void * data )
{
struct mmc_omap_host * host = ( struct mmc_omap_host * ) data ;
struct mmc_data * mmcdat = host - > data ;
if ( unlikely ( host - > dma_ch < 0 ) ) {
2006-07-01 19:56:44 +01:00
dev_err ( mmc_dev ( host - > mmc ) ,
" DMA callback while DMA not enabled \n " ) ;
2006-03-29 09:21:00 +01:00
return ;
}
/* FIXME: We really should do something to _handle_ the errors */
2006-06-26 16:16:15 -07:00
if ( ch_status & OMAP1_DMA_TOUT_IRQ ) {
2006-03-29 09:21:00 +01:00
dev_err ( mmc_dev ( host - > mmc ) , " DMA timeout \n " ) ;
return ;
}
if ( ch_status & OMAP_DMA_DROP_IRQ ) {
dev_err ( mmc_dev ( host - > mmc ) , " DMA sync error \n " ) ;
return ;
}
if ( ! ( ch_status & OMAP_DMA_BLOCK_IRQ ) ) {
return ;
}
mmcdat - > bytes_xfered + = host - > dma_len ;
host - > sg_idx + + ;
if ( host - > sg_idx < host - > sg_len ) {
mmc_omap_prepare_dma ( host , host - > data ) ;
omap_start_dma ( host - > dma_ch ) ;
} else
mmc_omap_dma_done ( host , host - > data ) ;
}
static int mmc_omap_get_dma_channel ( struct mmc_omap_host * host , struct mmc_data * data )
{
const char * dev_name ;
int sync_dev , dma_ch , is_read , r ;
is_read = ! ( data - > flags & MMC_DATA_WRITE ) ;
del_timer_sync ( & host - > dma_timer ) ;
if ( host - > dma_ch > = 0 ) {
if ( is_read = = host - > dma_is_read )
return 0 ;
omap_free_dma ( host - > dma_ch ) ;
host - > dma_ch = - 1 ;
}
if ( is_read ) {
if ( host - > id = = 1 ) {
sync_dev = OMAP_DMA_MMC_RX ;
dev_name = " MMC1 read " ;
} else {
sync_dev = OMAP_DMA_MMC2_RX ;
dev_name = " MMC2 read " ;
}
} else {
if ( host - > id = = 1 ) {
sync_dev = OMAP_DMA_MMC_TX ;
dev_name = " MMC1 write " ;
} else {
sync_dev = OMAP_DMA_MMC2_TX ;
dev_name = " MMC2 write " ;
}
}
r = omap_request_dma ( sync_dev , dev_name , mmc_omap_dma_cb ,
host , & dma_ch ) ;
if ( r ! = 0 ) {
dev_dbg ( mmc_dev ( host - > mmc ) , " omap_request_dma() failed with %d \n " , r ) ;
return r ;
}
host - > dma_ch = dma_ch ;
host - > dma_is_read = is_read ;
return 0 ;
}
static inline void set_cmd_timeout ( struct mmc_omap_host * host , struct mmc_request * req )
{
u16 reg ;
reg = OMAP_MMC_READ ( host - > base , SDIO ) ;
reg & = ~ ( 1 < < 5 ) ;
OMAP_MMC_WRITE ( host - > base , SDIO , reg ) ;
/* Set maximum timeout */
OMAP_MMC_WRITE ( host - > base , CTO , 0xff ) ;
}
static inline void set_data_timeout ( struct mmc_omap_host * host , struct mmc_request * req )
{
int timeout ;
u16 reg ;
/* Convert ns to clock cycles by assuming 20MHz frequency
* 1 cycle at 20 MHz = 500 ns
*/
timeout = req - > data - > timeout_clks + req - > data - > timeout_ns / 500 ;
/* Check if we need to use timeout multiplier register */
reg = OMAP_MMC_READ ( host - > base , SDIO ) ;
if ( timeout > 0xffff ) {
reg | = ( 1 < < 5 ) ;
timeout / = 1024 ;
} else
reg & = ~ ( 1 < < 5 ) ;
OMAP_MMC_WRITE ( host - > base , SDIO , reg ) ;
OMAP_MMC_WRITE ( host - > base , DTO , timeout ) ;
}
static void
mmc_omap_prepare_data ( struct mmc_omap_host * host , struct mmc_request * req )
{
struct mmc_data * data = req - > data ;
int i , use_dma , block_size ;
unsigned sg_len ;
host - > data = data ;
if ( data = = NULL ) {
OMAP_MMC_WRITE ( host - > base , BLEN , 0 ) ;
OMAP_MMC_WRITE ( host - > base , NBLK , 0 ) ;
OMAP_MMC_WRITE ( host - > base , BUF , 0 ) ;
host - > dma_in_use = 0 ;
set_cmd_timeout ( host , req ) ;
return ;
}
2006-06-04 17:51:15 +01:00
block_size = data - > blksz ;
2006-03-29 09:21:00 +01:00
OMAP_MMC_WRITE ( host - > base , NBLK , data - > blocks - 1 ) ;
OMAP_MMC_WRITE ( host - > base , BLEN , block_size - 1 ) ;
set_data_timeout ( host , req ) ;
/* cope with calling layer confusion; it issues "single
* block " writes using multi-block scatterlists.
*/
sg_len = ( data - > blocks = = 1 ) ? 1 : data - > sg_len ;
/* Only do DMA for entire blocks */
use_dma = host - > use_dma ;
if ( use_dma ) {
for ( i = 0 ; i < sg_len ; i + + ) {
if ( ( data - > sg [ i ] . length % block_size ) ! = 0 ) {
use_dma = 0 ;
break ;
}
}
}
host - > sg_idx = 0 ;
if ( use_dma ) {
if ( mmc_omap_get_dma_channel ( host , data ) = = 0 ) {
enum dma_data_direction dma_data_dir ;
if ( data - > flags & MMC_DATA_WRITE )
dma_data_dir = DMA_TO_DEVICE ;
else
dma_data_dir = DMA_FROM_DEVICE ;
host - > sg_len = dma_map_sg ( mmc_dev ( host - > mmc ) , data - > sg ,
sg_len , dma_data_dir ) ;
host - > total_bytes_left = 0 ;
mmc_omap_prepare_dma ( host , req - > data ) ;
host - > brs_received = 0 ;
host - > dma_done = 0 ;
host - > dma_in_use = 1 ;
} else
use_dma = 0 ;
}
/* Revert to PIO? */
if ( ! use_dma ) {
OMAP_MMC_WRITE ( host - > base , BUF , 0x1f1f ) ;
host - > total_bytes_left = data - > blocks * block_size ;
host - > sg_len = sg_len ;
mmc_omap_sg_to_buf ( host ) ;
host - > dma_in_use = 0 ;
}
}
static void mmc_omap_request ( struct mmc_host * mmc , struct mmc_request * req )
{
struct mmc_omap_host * host = mmc_priv ( mmc ) ;
WARN_ON ( host - > mrq ! = NULL ) ;
host - > mrq = req ;
/* only touch fifo AFTER the controller readies it */
mmc_omap_prepare_data ( host , req ) ;
mmc_omap_start_command ( host , req - > cmd ) ;
if ( host - > dma_in_use )
omap_start_dma ( host - > dma_ch ) ;
}
static void innovator_fpga_socket_power ( int on )
{
# if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX)
if ( on ) {
fpga_write ( fpga_read ( OMAP1510_FPGA_POWER ) | ( 1 < < 3 ) ,
OMAP1510_FPGA_POWER ) ;
} else {
fpga_write ( fpga_read ( OMAP1510_FPGA_POWER ) & ~ ( 1 < < 3 ) ,
OMAP1510_FPGA_POWER ) ;
}
# endif
}
/*
* Turn the socket power on / off . Innovator uses FPGA , most boards
* probably use GPIO .
*/
static void mmc_omap_power ( struct mmc_omap_host * host , int on )
{
if ( on ) {
if ( machine_is_omap_innovator ( ) )
innovator_fpga_socket_power ( 1 ) ;
else if ( machine_is_omap_h2 ( ) )
tps65010_set_gpio_out_value ( GPIO3 , HIGH ) ;
else if ( machine_is_omap_h3 ( ) )
/* GPIO 4 of TPS65010 sends SD_EN signal */
tps65010_set_gpio_out_value ( GPIO4 , HIGH ) ;
else if ( cpu_is_omap24xx ( ) ) {
u16 reg = OMAP_MMC_READ ( host - > base , CON ) ;
OMAP_MMC_WRITE ( host - > base , CON , reg | ( 1 < < 11 ) ) ;
} else
if ( host - > power_pin > = 0 )
omap_set_gpio_dataout ( host - > power_pin , 1 ) ;
} else {
if ( machine_is_omap_innovator ( ) )
innovator_fpga_socket_power ( 0 ) ;
else if ( machine_is_omap_h2 ( ) )
tps65010_set_gpio_out_value ( GPIO3 , LOW ) ;
else if ( machine_is_omap_h3 ( ) )
tps65010_set_gpio_out_value ( GPIO4 , LOW ) ;
else if ( cpu_is_omap24xx ( ) ) {
u16 reg = OMAP_MMC_READ ( host - > base , CON ) ;
OMAP_MMC_WRITE ( host - > base , CON , reg & ~ ( 1 < < 11 ) ) ;
} else
if ( host - > power_pin > = 0 )
omap_set_gpio_dataout ( host - > power_pin , 0 ) ;
}
}
static void mmc_omap_set_ios ( struct mmc_host * mmc , struct mmc_ios * ios )
{
struct mmc_omap_host * host = mmc_priv ( mmc ) ;
int dsor ;
int realclock , i ;
realclock = ios - > clock ;
if ( ios - > clock = = 0 )
dsor = 0 ;
else {
int func_clk_rate = clk_get_rate ( host - > fclk ) ;
dsor = func_clk_rate / realclock ;
if ( dsor < 1 )
dsor = 1 ;
if ( func_clk_rate / dsor > realclock )
dsor + + ;
if ( dsor > 250 )
dsor = 250 ;
dsor + + ;
if ( ios - > bus_width = = MMC_BUS_WIDTH_4 )
dsor | = 1 < < 15 ;
}
switch ( ios - > power_mode ) {
case MMC_POWER_OFF :
mmc_omap_power ( host , 0 ) ;
break ;
case MMC_POWER_UP :
case MMC_POWER_ON :
mmc_omap_power ( host , 1 ) ;
dsor | = 1 < < 11 ;
break ;
}
host - > bus_mode = ios - > bus_mode ;
host - > hw_bus_mode = host - > bus_mode ;
clk_enable ( host - > fclk ) ;
/* On insanely high arm_per frequencies something sometimes
* goes somehow out of sync , and the POW bit is not being set ,
* which results in the while loop below getting stuck .
* Writing to the CON register twice seems to do the trick . */
for ( i = 0 ; i < 2 ; i + + )
OMAP_MMC_WRITE ( host - > base , CON , dsor ) ;
if ( ios - > power_mode = = MMC_POWER_UP ) {
/* Send clock cycles, poll completion */
OMAP_MMC_WRITE ( host - > base , IE , 0 ) ;
OMAP_MMC_WRITE ( host - > base , STAT , 0xffff ) ;
OMAP_MMC_WRITE ( host - > base , CMD , 1 < < 7 ) ;
while ( 0 = = ( OMAP_MMC_READ ( host - > base , STAT ) & 1 ) ) ;
OMAP_MMC_WRITE ( host - > base , STAT , 1 ) ;
}
clk_disable ( host - > fclk ) ;
}
static int mmc_omap_get_ro ( struct mmc_host * mmc )
{
struct mmc_omap_host * host = mmc_priv ( mmc ) ;
return host - > wp_pin & & omap_get_gpio_datain ( host - > wp_pin ) ;
}
static struct mmc_host_ops mmc_omap_ops = {
. request = mmc_omap_request ,
. set_ios = mmc_omap_set_ios ,
. get_ro = mmc_omap_get_ro ,
} ;
static int __init mmc_omap_probe ( struct platform_device * pdev )
{
struct omap_mmc_conf * minfo = pdev - > dev . platform_data ;
struct mmc_host * mmc ;
struct mmc_omap_host * host = NULL ;
2006-07-01 19:56:44 +01:00
struct resource * r ;
2006-03-29 09:21:00 +01:00
int ret = 0 ;
2006-07-01 19:56:44 +01:00
int irq ;
2006-03-29 09:21:00 +01:00
2006-07-01 19:56:44 +01:00
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( ! r | | irq < 0 )
return - ENXIO ;
2006-03-29 09:21:00 +01:00
2006-07-01 19:56:44 +01:00
r = request_mem_region ( pdev - > resource [ 0 ] . start ,
2006-03-29 09:21:00 +01:00
pdev - > resource [ 0 ] . end - pdev - > resource [ 0 ] . start + 1 ,
2006-07-01 19:56:44 +01:00
pdev - > name ) ;
if ( ! r )
2006-03-29 09:21:00 +01:00
return - EBUSY ;
mmc = mmc_alloc_host ( sizeof ( struct mmc_omap_host ) , & pdev - > dev ) ;
if ( ! mmc ) {
ret = - ENOMEM ;
goto out ;
}
host = mmc_priv ( mmc ) ;
host - > mmc = mmc ;
spin_lock_init ( & host - > dma_lock ) ;
init_timer ( & host - > dma_timer ) ;
host - > dma_timer . function = mmc_omap_dma_timer ;
host - > dma_timer . data = ( unsigned long ) host ;
host - > id = pdev - > id ;
2006-07-01 19:56:44 +01:00
host - > res = r ;
host - > irq = irq ;
2006-03-29 09:21:00 +01:00
if ( cpu_is_omap24xx ( ) ) {
host - > iclk = clk_get ( & pdev - > dev , " mmc_ick " ) ;
if ( IS_ERR ( host - > iclk ) )
goto out ;
clk_enable ( host - > iclk ) ;
}
if ( ! cpu_is_omap24xx ( ) )
host - > fclk = clk_get ( & pdev - > dev , " mmc_ck " ) ;
else
host - > fclk = clk_get ( & pdev - > dev , " mmc_fck " ) ;
if ( IS_ERR ( host - > fclk ) ) {
ret = PTR_ERR ( host - > fclk ) ;
goto out ;
}
/* REVISIT:
* Also , use minfo - > cover to decide how to manage
* the card detect sensing .
*/
host - > power_pin = minfo - > power_pin ;
host - > switch_pin = minfo - > switch_pin ;
host - > wp_pin = minfo - > wp_pin ;
host - > use_dma = 1 ;
host - > dma_ch = - 1 ;
host - > irq = pdev - > resource [ 1 ] . start ;
2006-07-01 19:56:44 +01:00
host - > base = ( void __iomem * ) IO_ADDRESS ( r - > start ) ;
2006-03-29 09:21:00 +01:00
2006-07-01 19:56:44 +01:00
if ( minfo - > wire4 )
2006-03-29 09:21:00 +01:00
mmc - > caps | = MMC_CAP_4_BIT_DATA ;
mmc - > ops = & mmc_omap_ops ;
mmc - > f_min = 400000 ;
mmc - > f_max = 24000000 ;
mmc - > ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 ;
/* Use scatterlist DMA to reduce per-transfer costs.
* NOTE max_seg_size assumption that small blocks aren ' t
* normally used ( except e . g . for reading SD registers ) .
*/
mmc - > max_phys_segs = 32 ;
mmc - > max_hw_segs = 32 ;
mmc - > max_sectors = 256 ; /* NBLK max 11-bits, OMAP also limited by DMA */
mmc - > max_seg_size = mmc - > max_sectors * 512 ;
if ( host - > power_pin > = 0 ) {
if ( ( ret = omap_request_gpio ( host - > power_pin ) ) ! = 0 ) {
2006-07-01 19:56:44 +01:00
dev_err ( mmc_dev ( host - > mmc ) ,
" Unable to get GPIO pin for MMC power \n " ) ;
2006-03-29 09:21:00 +01:00
goto out ;
}
omap_set_gpio_direction ( host - > power_pin , 0 ) ;
}
ret = request_irq ( host - > irq , mmc_omap_irq , 0 , DRIVER_NAME , host ) ;
if ( ret )
goto out ;
host - > dev = & pdev - > dev ;
platform_set_drvdata ( pdev , host ) ;
mmc_add_host ( mmc ) ;
if ( host - > switch_pin > = 0 ) {
INIT_WORK ( & host - > switch_work , mmc_omap_switch_handler , host ) ;
init_timer ( & host - > switch_timer ) ;
host - > switch_timer . function = mmc_omap_switch_timer ;
host - > switch_timer . data = ( unsigned long ) host ;
if ( omap_request_gpio ( host - > switch_pin ) ! = 0 ) {
dev_warn ( mmc_dev ( host - > mmc ) , " Unable to get GPIO pin for MMC cover switch \n " ) ;
host - > switch_pin = - 1 ;
goto no_switch ;
}
omap_set_gpio_direction ( host - > switch_pin , 1 ) ;
ret = request_irq ( OMAP_GPIO_IRQ ( host - > switch_pin ) ,
2006-07-01 19:29:38 -07:00
mmc_omap_switch_irq , IRQF_TRIGGER_RISING , DRIVER_NAME , host ) ;
2006-03-29 09:21:00 +01:00
if ( ret ) {
dev_warn ( mmc_dev ( host - > mmc ) , " Unable to get IRQ for MMC cover switch \n " ) ;
omap_free_gpio ( host - > switch_pin ) ;
host - > switch_pin = - 1 ;
goto no_switch ;
}
ret = device_create_file ( & pdev - > dev , & dev_attr_cover_switch ) ;
if ( ret = = 0 ) {
ret = device_create_file ( & pdev - > dev , & dev_attr_enable_poll ) ;
if ( ret ! = 0 )
device_remove_file ( & pdev - > dev , & dev_attr_cover_switch ) ;
}
if ( ret ) {
2006-07-01 19:56:44 +01:00
dev_warn ( mmc_dev ( host - > mmc ) , " Unable to create sysfs attributes \n " ) ;
2006-03-29 09:21:00 +01:00
free_irq ( OMAP_GPIO_IRQ ( host - > switch_pin ) , host ) ;
omap_free_gpio ( host - > switch_pin ) ;
host - > switch_pin = - 1 ;
goto no_switch ;
}
if ( mmc_omap_enable_poll & & mmc_omap_cover_is_open ( host ) )
schedule_work ( & host - > switch_work ) ;
}
no_switch :
return 0 ;
out :
/* FIXME: Free other resources too. */
if ( host ) {
if ( host - > iclk & & ! IS_ERR ( host - > iclk ) )
clk_put ( host - > iclk ) ;
if ( host - > fclk & & ! IS_ERR ( host - > fclk ) )
clk_put ( host - > fclk ) ;
mmc_free_host ( host - > mmc ) ;
}
return ret ;
}
static int mmc_omap_remove ( struct platform_device * pdev )
{
struct mmc_omap_host * host = platform_get_drvdata ( pdev ) ;
platform_set_drvdata ( pdev , NULL ) ;
if ( host ) {
mmc_remove_host ( host - > mmc ) ;
free_irq ( host - > irq , host ) ;
if ( host - > power_pin > = 0 )
omap_free_gpio ( host - > power_pin ) ;
if ( host - > switch_pin > = 0 ) {
device_remove_file ( & pdev - > dev , & dev_attr_enable_poll ) ;
device_remove_file ( & pdev - > dev , & dev_attr_cover_switch ) ;
free_irq ( OMAP_GPIO_IRQ ( host - > switch_pin ) , host ) ;
omap_free_gpio ( host - > switch_pin ) ;
host - > switch_pin = - 1 ;
del_timer_sync ( & host - > switch_timer ) ;
flush_scheduled_work ( ) ;
}
if ( host - > iclk & & ! IS_ERR ( host - > iclk ) )
clk_put ( host - > iclk ) ;
if ( host - > fclk & & ! IS_ERR ( host - > fclk ) )
clk_put ( host - > fclk ) ;
mmc_free_host ( host - > mmc ) ;
}
release_mem_region ( pdev - > resource [ 0 ] . start ,
pdev - > resource [ 0 ] . end - pdev - > resource [ 0 ] . start + 1 ) ;
return 0 ;
}
# ifdef CONFIG_PM
static int mmc_omap_suspend ( struct platform_device * pdev , pm_message_t mesg )
{
int ret = 0 ;
struct mmc_omap_host * host = platform_get_drvdata ( pdev ) ;
if ( host & & host - > suspended )
return 0 ;
if ( host ) {
ret = mmc_suspend_host ( host - > mmc , mesg ) ;
if ( ret = = 0 )
host - > suspended = 1 ;
}
return ret ;
}
static int mmc_omap_resume ( struct platform_device * pdev )
{
int ret = 0 ;
struct mmc_omap_host * host = platform_get_drvdata ( pdev ) ;
if ( host & & ! host - > suspended )
return 0 ;
if ( host ) {
ret = mmc_resume_host ( host - > mmc ) ;
if ( ret = = 0 )
host - > suspended = 0 ;
}
return ret ;
}
# else
# define mmc_omap_suspend NULL
# define mmc_omap_resume NULL
# endif
static struct platform_driver mmc_omap_driver = {
. probe = mmc_omap_probe ,
. remove = mmc_omap_remove ,
. suspend = mmc_omap_suspend ,
. resume = mmc_omap_resume ,
. driver = {
. name = DRIVER_NAME ,
} ,
} ;
static int __init mmc_omap_init ( void )
{
return platform_driver_register ( & mmc_omap_driver ) ;
}
static void __exit mmc_omap_exit ( void )
{
platform_driver_unregister ( & mmc_omap_driver ) ;
}
module_init ( mmc_omap_init ) ;
module_exit ( mmc_omap_exit ) ;
MODULE_DESCRIPTION ( " OMAP Multimedia Card driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( DRIVER_NAME ) ;
MODULE_AUTHOR ( " Juha Yrj<72> l<EFBFBD> " ) ;