2006-04-02 22:18:51 +04:00
/*
* linux / drivers / mmc / at91_mci . c - ATMEL AT91RM9200 MCI Driver
*
* Copyright ( C ) 2005 Cougar Creek Computing Devices Ltd , All Rights Reserved
*
* Copyright ( C ) 2006 Malcolm Noyes
*
* 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 .
*/
/*
This is the AT91RM9200 MCI driver that has been tested with both MMC cards
and SD - cards . Boards that support write protect are now supported .
The CCAT91SBC001 board does not support SD cards .
The three entry points are at91_mci_request , at91_mci_set_ios
and at91_mci_get_ro .
SET IOS
This configures the device to put it into the correct mode and clock speed
required .
MCI REQUEST
MCI request processes the commands sent in the mmc_request structure . This
can consist of a processing command and a stop command in the case of
multiple block transfers .
There are three main types of request , commands , reads and writes .
Commands are straight forward . The command is submitted to the controller and
the request function returns . When the controller generates an interrupt to indicate
the command is finished , the response to the command are read and the mmc_request_done
function called to end the request .
Reads and writes work in a similar manner to normal commands but involve the PDC ( DMA )
controller to manage the transfers .
A read is done from the controller directly to the scatterlist passed in from the request .
Due to a bug in the controller , when a read is completed , all the words are byte
swapped in the scatterlist buffers .
The sequence of read interrupts is : ENDRX , RXBUFF , CMDRDY
A write is slightly different in that the bytes to write are read from the scatterlist
into a dma memory buffer ( this is in case the source buffer should be read only ) . The
entire write buffer is then done from this single dma memory buffer .
The sequence of write interrupts is : ENDTX , TXBUFE , NOTBUSY , CMDRDY
GET RO
Gets the status of the write protect pin , if available .
*/
# 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/blkdev.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/dma-mapping.h>
# include <linux/clk.h>
# include <linux/mmc/host.h>
# include <linux/mmc/protocol.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/mach/mmc.h>
# include <asm/arch/board.h>
# include <asm/arch/gpio.h>
# include <asm/arch/at91rm9200_mci.h>
# include <asm/arch/at91rm9200_pdc.h>
# define DRIVER_NAME "at91_mci"
# undef SUPPORT_4WIRE
static struct clk * mci_clk ;
# define FL_SENT_COMMAND (1 << 0)
# define FL_SENT_STOP (1 << 1)
/*
* Read from a MCI register .
*/
static inline unsigned long at91_mci_read ( unsigned int reg )
{
void __iomem * mci_base = ( void __iomem * ) AT91_VA_BASE_MCI ;
return __raw_readl ( mci_base + reg ) ;
}
/*
* Write to a MCI register .
*/
static inline void at91_mci_write ( unsigned int reg , unsigned long value )
{
void __iomem * mci_base = ( void __iomem * ) AT91_VA_BASE_MCI ;
__raw_writel ( value , mci_base + reg ) ;
}
/*
* Low level type for this driver
*/
struct at91mci_host
{
struct mmc_host * mmc ;
struct mmc_command * cmd ;
struct mmc_request * request ;
struct at91_mmc_data * board ;
int present ;
/*
* Flag indicating when the command has been sent . This is used to
* work out whether or not to send the stop
*/
unsigned int flags ;
/* flag for current bus settings */
u32 bus_mode ;
/* DMA buffer used for transmitting */
unsigned int * buffer ;
dma_addr_t physical_address ;
unsigned int total_length ;
/* Latest in the scatterlist that has been enabled for transfer, but not freed */
int in_use_index ;
/* Latest in the scatterlist that has been enabled for transfer */
int transfer_index ;
} ;
/*
* Copy from sg to a dma block - used for transfers
*/
static inline void at91mci_sg_to_dma ( struct at91mci_host * host , struct mmc_data * data )
{
unsigned int len , i , size ;
unsigned * dmabuf = host - > buffer ;
size = host - > total_length ;
len = data - > sg_len ;
/*
* Just loop through all entries . Size might not
* be the entire list though so make sure that
* we do not transfer too much .
*/
for ( i = 0 ; i < len ; i + + ) {
struct scatterlist * sg ;
int amount ;
int index ;
unsigned int * sgbuffer ;
sg = & data - > sg [ i ] ;
sgbuffer = kmap_atomic ( sg - > page , KM_BIO_SRC_IRQ ) + sg - > offset ;
amount = min ( size , sg - > length ) ;
size - = amount ;
amount / = 4 ;
for ( index = 0 ; index < amount ; index + + )
* dmabuf + + = swab32 ( sgbuffer [ index ] ) ;
kunmap_atomic ( sgbuffer , KM_BIO_SRC_IRQ ) ;
if ( size = = 0 )
break ;
}
/*
* Check that we didn ' t get a request to transfer
* more data than can fit into the SG list .
*/
BUG_ON ( size ! = 0 ) ;
}
/*
* Prepare a dma read
*/
static void at91mci_pre_dma_read ( struct at91mci_host * host )
{
int i ;
struct scatterlist * sg ;
struct mmc_command * cmd ;
struct mmc_data * data ;
2006-06-19 16:06:05 +04:00
pr_debug ( " pre dma read \n " ) ;
2006-04-02 22:18:51 +04:00
cmd = host - > cmd ;
if ( ! cmd ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " no command \n " ) ;
2006-04-02 22:18:51 +04:00
return ;
}
data = cmd - > data ;
if ( ! data ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " no data \n " ) ;
2006-04-02 22:18:51 +04:00
return ;
}
for ( i = 0 ; i < 2 ; i + + ) {
/* nothing left to transfer */
if ( host - > transfer_index > = data - > sg_len ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " Nothing left to transfer (index = %d) \n " , host - > transfer_index ) ;
2006-04-02 22:18:51 +04:00
break ;
}
/* Check to see if this needs filling */
if ( i = = 0 ) {
if ( at91_mci_read ( AT91_PDC_RCR ) ! = 0 ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " Transfer active in current \n " ) ;
2006-04-02 22:18:51 +04:00
continue ;
}
}
else {
if ( at91_mci_read ( AT91_PDC_RNCR ) ! = 0 ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " Transfer active in next \n " ) ;
2006-04-02 22:18:51 +04:00
continue ;
}
}
/* Setup the next transfer */
2006-06-19 16:06:05 +04:00
pr_debug ( " Using transfer index %d \n " , host - > transfer_index ) ;
2006-04-02 22:18:51 +04:00
sg = & data - > sg [ host - > transfer_index + + ] ;
2006-06-19 16:06:05 +04:00
pr_debug ( " sg = %p \n " , sg ) ;
2006-04-02 22:18:51 +04:00
sg - > dma_address = dma_map_page ( NULL , sg - > page , sg - > offset , sg - > length , DMA_FROM_DEVICE ) ;
2006-06-19 16:06:05 +04:00
pr_debug ( " dma address = %08X, length = %d \n " , sg - > dma_address , sg - > length ) ;
2006-04-02 22:18:51 +04:00
if ( i = = 0 ) {
at91_mci_write ( AT91_PDC_RPR , sg - > dma_address ) ;
at91_mci_write ( AT91_PDC_RCR , sg - > length / 4 ) ;
}
else {
at91_mci_write ( AT91_PDC_RNPR , sg - > dma_address ) ;
at91_mci_write ( AT91_PDC_RNCR , sg - > length / 4 ) ;
}
}
2006-06-19 16:06:05 +04:00
pr_debug ( " pre dma read done \n " ) ;
2006-04-02 22:18:51 +04:00
}
/*
* Handle after a dma read
*/
static void at91mci_post_dma_read ( struct at91mci_host * host )
{
struct mmc_command * cmd ;
struct mmc_data * data ;
2006-06-19 16:06:05 +04:00
pr_debug ( " post dma read \n " ) ;
2006-04-02 22:18:51 +04:00
cmd = host - > cmd ;
if ( ! cmd ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " no command \n " ) ;
2006-04-02 22:18:51 +04:00
return ;
}
data = cmd - > data ;
if ( ! data ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " no data \n " ) ;
2006-04-02 22:18:51 +04:00
return ;
}
while ( host - > in_use_index < host - > transfer_index ) {
unsigned int * buffer ;
int index ;
int len ;
struct scatterlist * sg ;
2006-06-19 16:06:05 +04:00
pr_debug ( " finishing index %d \n " , host - > in_use_index ) ;
2006-04-02 22:18:51 +04:00
sg = & data - > sg [ host - > in_use_index + + ] ;
2006-06-19 16:06:05 +04:00
pr_debug ( " Unmapping page %08X \n " , sg - > dma_address ) ;
2006-04-02 22:18:51 +04:00
dma_unmap_page ( NULL , sg - > dma_address , sg - > length , DMA_FROM_DEVICE ) ;
/* Swap the contents of the buffer */
buffer = kmap_atomic ( sg - > page , KM_BIO_SRC_IRQ ) + sg - > offset ;
2006-06-19 16:06:05 +04:00
pr_debug ( " buffer = %p, length = %d \n " , buffer , sg - > length ) ;
2006-04-02 22:18:51 +04:00
data - > bytes_xfered + = sg - > length ;
len = sg - > length / 4 ;
for ( index = 0 ; index < len ; index + + ) {
buffer [ index ] = swab32 ( buffer [ index ] ) ;
}
kunmap_atomic ( buffer , KM_BIO_SRC_IRQ ) ;
flush_dcache_page ( sg - > page ) ;
}
/* Is there another transfer to trigger? */
if ( host - > transfer_index < data - > sg_len )
at91mci_pre_dma_read ( host ) ;
else {
at91_mci_write ( AT91_MCI_IER , AT91_MCI_RXBUFF ) ;
at91_mci_write ( AT91_PDC_PTCR , AT91_PDC_RXTDIS | AT91_PDC_TXTDIS ) ;
}
2006-06-19 16:06:05 +04:00
pr_debug ( " post dma read done \n " ) ;
2006-04-02 22:18:51 +04:00
}
/*
* Handle transmitted data
*/
static void at91_mci_handle_transmitted ( struct at91mci_host * host )
{
struct mmc_command * cmd ;
struct mmc_data * data ;
2006-06-19 16:06:05 +04:00
pr_debug ( " Handling the transmit \n " ) ;
2006-04-02 22:18:51 +04:00
/* Disable the transfer */
at91_mci_write ( AT91_PDC_PTCR , AT91_PDC_RXTDIS | AT91_PDC_TXTDIS ) ;
/* Now wait for cmd ready */
at91_mci_write ( AT91_MCI_IDR , AT91_MCI_TXBUFE ) ;
at91_mci_write ( AT91_MCI_IER , AT91_MCI_NOTBUSY ) ;
cmd = host - > cmd ;
if ( ! cmd ) return ;
data = cmd - > data ;
if ( ! data ) return ;
data - > bytes_xfered = host - > total_length ;
}
/*
* Enable the controller
*/
static void at91_mci_enable ( void )
{
at91_mci_write ( AT91_MCI_CR , AT91_MCI_MCIEN ) ;
at91_mci_write ( AT91_MCI_IDR , 0xFFFFFFFF ) ;
at91_mci_write ( AT91_MCI_DTOR , AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC ) ;
at91_mci_write ( AT91_MCI_MR , 0x834A ) ;
at91_mci_write ( AT91_MCI_SDCR , 0x0 ) ;
}
/*
* Disable the controller
*/
static void at91_mci_disable ( void )
{
at91_mci_write ( AT91_MCI_CR , AT91_MCI_MCIDIS | AT91_MCI_SWRST ) ;
}
/*
* Send a command
* return the interrupts to enable
*/
static unsigned int at91_mci_send_command ( struct at91mci_host * host , struct mmc_command * cmd )
{
unsigned int cmdr , mr ;
unsigned int block_length ;
struct mmc_data * data = cmd - > data ;
unsigned int blocks ;
unsigned int ier = 0 ;
host - > cmd = cmd ;
/* Not sure if this is needed */
#if 0
if ( ( at91_mci_read ( AT91_MCI_SR ) & AT91_MCI_RTOE ) & & ( cmd - > opcode = = 1 ) ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " Clearing timeout \n " ) ;
2006-04-02 22:18:51 +04:00
at91_mci_write ( AT91_MCI_ARGR , 0 ) ;
at91_mci_write ( AT91_MCI_CMDR , AT91_MCI_OPDCMD ) ;
while ( ! ( at91_mci_read ( AT91_MCI_SR ) & AT91_MCI_CMDRDY ) ) {
/* spin */
2006-06-19 16:06:05 +04:00
pr_debug ( " Clearing: SR = %08X \n " , at91_mci_read ( AT91_MCI_SR ) ) ;
2006-04-02 22:18:51 +04:00
}
}
# endif
cmdr = cmd - > opcode ;
if ( mmc_resp_type ( cmd ) = = MMC_RSP_NONE )
cmdr | = AT91_MCI_RSPTYP_NONE ;
else {
/* if a response is expected then allow maximum response latancy */
cmdr | = AT91_MCI_MAXLAT ;
/* set 136 bit response for R2, 48 bit response otherwise */
if ( mmc_resp_type ( cmd ) = = MMC_RSP_R2 )
cmdr | = AT91_MCI_RSPTYP_136 ;
else
cmdr | = AT91_MCI_RSPTYP_48 ;
}
if ( data ) {
2006-06-04 20:51:15 +04:00
block_length = data - > blksz ;
2006-04-02 22:18:51 +04:00
blocks = data - > blocks ;
/* always set data start - also set direction flag for read */
if ( data - > flags & MMC_DATA_READ )
cmdr | = ( AT91_MCI_TRDIR | AT91_MCI_TRCMD_START ) ;
else if ( data - > flags & MMC_DATA_WRITE )
cmdr | = AT91_MCI_TRCMD_START ;
if ( data - > flags & MMC_DATA_STREAM )
cmdr | = AT91_MCI_TRTYP_STREAM ;
if ( data - > flags & MMC_DATA_MULTI )
cmdr | = AT91_MCI_TRTYP_MULTIPLE ;
}
else {
block_length = 0 ;
blocks = 0 ;
}
if ( cmd - > opcode = = MMC_STOP_TRANSMISSION )
cmdr | = AT91_MCI_TRCMD_STOP ;
if ( host - > bus_mode = = MMC_BUSMODE_OPENDRAIN )
cmdr | = AT91_MCI_OPDCMD ;
/*
* Set the arguments and send the command
*/
2006-06-19 16:06:05 +04:00
pr_debug ( " Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08lX) \n " ,
2006-04-02 22:18:51 +04:00
cmd - > opcode , cmdr , cmd - > arg , blocks , block_length , at91_mci_read ( AT91_MCI_MR ) ) ;
if ( ! data ) {
at91_mci_write ( AT91_PDC_PTCR , AT91_PDC_TXTDIS | AT91_PDC_RXTDIS ) ;
at91_mci_write ( AT91_PDC_RPR , 0 ) ;
at91_mci_write ( AT91_PDC_RCR , 0 ) ;
at91_mci_write ( AT91_PDC_RNPR , 0 ) ;
at91_mci_write ( AT91_PDC_RNCR , 0 ) ;
at91_mci_write ( AT91_PDC_TPR , 0 ) ;
at91_mci_write ( AT91_PDC_TCR , 0 ) ;
at91_mci_write ( AT91_PDC_TNPR , 0 ) ;
at91_mci_write ( AT91_PDC_TNCR , 0 ) ;
at91_mci_write ( AT91_MCI_ARGR , cmd - > arg ) ;
at91_mci_write ( AT91_MCI_CMDR , cmdr ) ;
return AT91_MCI_CMDRDY ;
}
mr = at91_mci_read ( AT91_MCI_MR ) & 0x7fff ; /* zero block length and PDC mode */
at91_mci_write ( AT91_MCI_MR , mr | ( block_length < < 16 ) | AT91_MCI_PDCMODE ) ;
/*
* Disable the PDC controller
*/
at91_mci_write ( AT91_PDC_PTCR , AT91_PDC_RXTDIS | AT91_PDC_TXTDIS ) ;
if ( cmdr & AT91_MCI_TRCMD_START ) {
data - > bytes_xfered = 0 ;
host - > transfer_index = 0 ;
host - > in_use_index = 0 ;
if ( cmdr & AT91_MCI_TRDIR ) {
/*
* Handle a read
*/
host - > buffer = NULL ;
host - > total_length = 0 ;
at91mci_pre_dma_read ( host ) ;
ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */ ;
}
else {
/*
* Handle a write
*/
host - > total_length = block_length * blocks ;
host - > buffer = dma_alloc_coherent ( NULL ,
host - > total_length ,
& host - > physical_address , GFP_KERNEL ) ;
at91mci_sg_to_dma ( host , data ) ;
2006-06-19 16:06:05 +04:00
pr_debug ( " Transmitting %d bytes \n " , host - > total_length ) ;
2006-04-02 22:18:51 +04:00
at91_mci_write ( AT91_PDC_TPR , host - > physical_address ) ;
at91_mci_write ( AT91_PDC_TCR , host - > total_length / 4 ) ;
ier = AT91_MCI_TXBUFE ;
}
}
/*
* Send the command and then enable the PDC - not the other way round as
* the data sheet says
*/
at91_mci_write ( AT91_MCI_ARGR , cmd - > arg ) ;
at91_mci_write ( AT91_MCI_CMDR , cmdr ) ;
if ( cmdr & AT91_MCI_TRCMD_START ) {
if ( cmdr & AT91_MCI_TRDIR )
at91_mci_write ( AT91_PDC_PTCR , AT91_PDC_RXTEN ) ;
else
at91_mci_write ( AT91_PDC_PTCR , AT91_PDC_TXTEN ) ;
}
return ier ;
}
/*
* Wait for a command to complete
*/
static void at91mci_process_command ( struct at91mci_host * host , struct mmc_command * cmd )
{
unsigned int ier ;
ier = at91_mci_send_command ( host , cmd ) ;
2006-06-19 16:06:05 +04:00
pr_debug ( " setting ier to %08X \n " , ier ) ;
2006-04-02 22:18:51 +04:00
/* Stop on errors or the required value */
at91_mci_write ( AT91_MCI_IER , 0xffff0000 | ier ) ;
}
/*
* Process the next step in the request
*/
static void at91mci_process_next ( struct at91mci_host * host )
{
if ( ! ( host - > flags & FL_SENT_COMMAND ) ) {
host - > flags | = FL_SENT_COMMAND ;
at91mci_process_command ( host , host - > request - > cmd ) ;
}
else if ( ( ! ( host - > flags & FL_SENT_STOP ) ) & & host - > request - > stop ) {
host - > flags | = FL_SENT_STOP ;
at91mci_process_command ( host , host - > request - > stop ) ;
}
else
mmc_request_done ( host - > mmc , host - > request ) ;
}
/*
* Handle a command that has been completed
*/
static void at91mci_completed_command ( struct at91mci_host * host )
{
struct mmc_command * cmd = host - > cmd ;
unsigned int status ;
at91_mci_write ( AT91_MCI_IDR , 0xffffffff ) ;
cmd - > resp [ 0 ] = at91_mci_read ( AT91_MCI_RSPR ( 0 ) ) ;
cmd - > resp [ 1 ] = at91_mci_read ( AT91_MCI_RSPR ( 1 ) ) ;
cmd - > resp [ 2 ] = at91_mci_read ( AT91_MCI_RSPR ( 2 ) ) ;
cmd - > resp [ 3 ] = at91_mci_read ( AT91_MCI_RSPR ( 3 ) ) ;
if ( host - > buffer ) {
dma_free_coherent ( NULL , host - > total_length , host - > buffer , host - > physical_address ) ;
host - > buffer = NULL ;
}
status = at91_mci_read ( AT91_MCI_SR ) ;
2006-06-19 16:06:05 +04:00
pr_debug ( " Status = %08X [%08X %08X %08X %08X] \n " ,
2006-04-02 22:18:51 +04:00
status , cmd - > resp [ 0 ] , cmd - > resp [ 1 ] , cmd - > resp [ 2 ] , cmd - > resp [ 3 ] ) ;
if ( status & ( AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE |
AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE |
AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE ) ) {
if ( ( status & AT91_MCI_RCRCE ) & &
( ( cmd - > opcode = = MMC_SEND_OP_COND ) | | ( cmd - > opcode = = SD_APP_OP_COND ) ) ) {
cmd - > error = MMC_ERR_NONE ;
}
else {
if ( status & ( AT91_MCI_RTOE | AT91_MCI_DTOE ) )
cmd - > error = MMC_ERR_TIMEOUT ;
else if ( status & ( AT91_MCI_RCRCE | AT91_MCI_DCRCE ) )
cmd - > error = MMC_ERR_BADCRC ;
else if ( status & ( AT91_MCI_OVRE | AT91_MCI_UNRE ) )
cmd - > error = MMC_ERR_FIFO ;
else
cmd - > error = MMC_ERR_FAILED ;
2006-06-19 16:06:05 +04:00
pr_debug ( " Error detected and set to %d (cmd = %d, retries = %d) \n " ,
2006-04-02 22:18:51 +04:00
cmd - > error , cmd - > opcode , cmd - > retries ) ;
}
}
else
cmd - > error = MMC_ERR_NONE ;
at91mci_process_next ( host ) ;
}
/*
* Handle an MMC request
*/
static void at91_mci_request ( struct mmc_host * mmc , struct mmc_request * mrq )
{
struct at91mci_host * host = mmc_priv ( mmc ) ;
host - > request = mrq ;
host - > flags = 0 ;
at91mci_process_next ( host ) ;
}
/*
* Set the IOS
*/
static void at91_mci_set_ios ( struct mmc_host * mmc , struct mmc_ios * ios )
{
int clkdiv ;
struct at91mci_host * host = mmc_priv ( mmc ) ;
unsigned long at91_master_clock = clk_get_rate ( mci_clk ) ;
2006-06-19 16:06:05 +04:00
host - > bus_mode = ios - > bus_mode ;
2006-04-02 22:18:51 +04:00
if ( ios - > clock = = 0 ) {
/* Disable the MCI controller */
at91_mci_write ( AT91_MCI_CR , AT91_MCI_MCIDIS ) ;
clkdiv = 0 ;
}
else {
/* Enable the MCI controller */
at91_mci_write ( AT91_MCI_CR , AT91_MCI_MCIEN ) ;
if ( ( at91_master_clock % ( ios - > clock * 2 ) ) = = 0 )
clkdiv = ( ( at91_master_clock / ios - > clock ) / 2 ) - 1 ;
else
clkdiv = ( at91_master_clock / ios - > clock ) / 2 ;
2006-06-19 16:06:05 +04:00
pr_debug ( " clkdiv = %d. mcck = %ld \n " , clkdiv ,
2006-04-02 22:18:51 +04:00
at91_master_clock / ( 2 * ( clkdiv + 1 ) ) ) ;
}
if ( ios - > bus_width = = MMC_BUS_WIDTH_4 & & host - > board - > wire4 ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " MMC: Setting controller bus width to 4 \n " ) ;
2006-04-02 22:18:51 +04:00
at91_mci_write ( AT91_MCI_SDCR , at91_mci_read ( AT91_MCI_SDCR ) | AT91_MCI_SDCBUS ) ;
}
else {
2006-06-19 16:06:05 +04:00
pr_debug ( " MMC: Setting controller bus width to 1 \n " ) ;
2006-04-02 22:18:51 +04:00
at91_mci_write ( AT91_MCI_SDCR , at91_mci_read ( AT91_MCI_SDCR ) & ~ AT91_MCI_SDCBUS ) ;
}
/* Set the clock divider */
at91_mci_write ( AT91_MCI_MR , ( at91_mci_read ( AT91_MCI_MR ) & ~ AT91_MCI_CLKDIV ) | clkdiv ) ;
/* maybe switch power to the card */
2006-06-19 16:06:05 +04:00
if ( host - > board - > vcc_pin ) {
2006-04-02 22:18:51 +04:00
switch ( ios - > power_mode ) {
case MMC_POWER_OFF :
at91_set_gpio_output ( host - > board - > vcc_pin , 0 ) ;
break ;
case MMC_POWER_UP :
case MMC_POWER_ON :
at91_set_gpio_output ( host - > board - > vcc_pin , 1 ) ;
break ;
}
}
}
/*
* Handle an interrupt
*/
static irqreturn_t at91_mci_irq ( int irq , void * devid , struct pt_regs * regs )
{
struct at91mci_host * host = devid ;
int completed = 0 ;
unsigned int int_status ;
int_status = at91_mci_read ( AT91_MCI_SR ) ;
2006-06-19 16:06:05 +04:00
pr_debug ( " MCI irq: status = %08X, %08lX, %08lX \n " , int_status , at91_mci_read ( AT91_MCI_IMR ) ,
2006-04-02 22:18:51 +04:00
int_status & at91_mci_read ( AT91_MCI_IMR ) ) ;
if ( ( int_status & at91_mci_read ( AT91_MCI_IMR ) ) & 0xffff0000 )
completed = 1 ;
int_status & = at91_mci_read ( AT91_MCI_IMR ) ;
if ( int_status & AT91_MCI_UNRE )
2006-06-19 16:06:05 +04:00
pr_debug ( " MMC: Underrun error \n " ) ;
2006-04-02 22:18:51 +04:00
if ( int_status & AT91_MCI_OVRE )
2006-06-19 16:06:05 +04:00
pr_debug ( " MMC: Overrun error \n " ) ;
2006-04-02 22:18:51 +04:00
if ( int_status & AT91_MCI_DTOE )
2006-06-19 16:06:05 +04:00
pr_debug ( " MMC: Data timeout \n " ) ;
2006-04-02 22:18:51 +04:00
if ( int_status & AT91_MCI_DCRCE )
2006-06-19 16:06:05 +04:00
pr_debug ( " MMC: CRC error in data \n " ) ;
2006-04-02 22:18:51 +04:00
if ( int_status & AT91_MCI_RTOE )
2006-06-19 16:06:05 +04:00
pr_debug ( " MMC: Response timeout \n " ) ;
2006-04-02 22:18:51 +04:00
if ( int_status & AT91_MCI_RENDE )
2006-06-19 16:06:05 +04:00
pr_debug ( " MMC: Response end bit error \n " ) ;
2006-04-02 22:18:51 +04:00
if ( int_status & AT91_MCI_RCRCE )
2006-06-19 16:06:05 +04:00
pr_debug ( " MMC: Response CRC error \n " ) ;
2006-04-02 22:18:51 +04:00
if ( int_status & AT91_MCI_RDIRE )
2006-06-19 16:06:05 +04:00
pr_debug ( " MMC: Response direction error \n " ) ;
2006-04-02 22:18:51 +04:00
if ( int_status & AT91_MCI_RINDE )
2006-06-19 16:06:05 +04:00
pr_debug ( " MMC: Response index error \n " ) ;
2006-04-02 22:18:51 +04:00
/* Only continue processing if no errors */
if ( ! completed ) {
if ( int_status & AT91_MCI_TXBUFE ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " TX buffer empty \n " ) ;
2006-04-02 22:18:51 +04:00
at91_mci_handle_transmitted ( host ) ;
}
if ( int_status & AT91_MCI_RXBUFF ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " RX buffer full \n " ) ;
2006-04-02 22:18:51 +04:00
at91_mci_write ( AT91_MCI_IER , AT91_MCI_CMDRDY ) ;
}
if ( int_status & AT91_MCI_ENDTX ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " Transmit has ended \n " ) ;
2006-04-02 22:18:51 +04:00
}
if ( int_status & AT91_MCI_ENDRX ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " Receive has ended \n " ) ;
2006-04-02 22:18:51 +04:00
at91mci_post_dma_read ( host ) ;
}
if ( int_status & AT91_MCI_NOTBUSY ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " Card is ready \n " ) ;
2006-04-02 22:18:51 +04:00
at91_mci_write ( AT91_MCI_IER , AT91_MCI_CMDRDY ) ;
}
if ( int_status & AT91_MCI_DTIP ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " Data transfer in progress \n " ) ;
2006-04-02 22:18:51 +04:00
}
if ( int_status & AT91_MCI_BLKE ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " Block transfer has ended \n " ) ;
2006-04-02 22:18:51 +04:00
}
if ( int_status & AT91_MCI_TXRDY ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " Ready to transmit \n " ) ;
2006-04-02 22:18:51 +04:00
}
if ( int_status & AT91_MCI_RXRDY ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " Ready to receive \n " ) ;
2006-04-02 22:18:51 +04:00
}
if ( int_status & AT91_MCI_CMDRDY ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " Command ready \n " ) ;
2006-04-02 22:18:51 +04:00
completed = 1 ;
}
}
at91_mci_write ( AT91_MCI_IDR , int_status ) ;
if ( completed ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " Completed command \n " ) ;
2006-04-02 22:18:51 +04:00
at91_mci_write ( AT91_MCI_IDR , 0xffffffff ) ;
at91mci_completed_command ( host ) ;
}
return IRQ_HANDLED ;
}
static irqreturn_t at91_mmc_det_irq ( int irq , void * _host , struct pt_regs * regs )
{
struct at91mci_host * host = _host ;
int present = ! at91_get_gpio_value ( irq ) ;
/*
* we expect this irq on both insert and remove ,
* and use a short delay to debounce .
*/
if ( present ! = host - > present ) {
host - > present = present ;
2006-06-19 16:06:05 +04:00
pr_debug ( " %s: card %s \n " , mmc_hostname ( host - > mmc ) ,
2006-04-02 22:18:51 +04:00
present ? " insert " : " remove " ) ;
if ( ! present ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " ****** Resetting SD-card bus width ****** \n " ) ;
2006-04-02 22:18:51 +04:00
at91_mci_write ( AT91_MCI_SDCR , 0 ) ;
}
mmc_detect_change ( host - > mmc , msecs_to_jiffies ( 100 ) ) ;
}
return IRQ_HANDLED ;
}
int at91_mci_get_ro ( struct mmc_host * mmc )
{
int read_only = 0 ;
struct at91mci_host * host = mmc_priv ( mmc ) ;
if ( host - > board - > wp_pin ) {
read_only = at91_get_gpio_value ( host - > board - > wp_pin ) ;
printk ( KERN_WARNING " %s: card is %s \n " , mmc_hostname ( mmc ) ,
( read_only ? " read-only " : " read-write " ) ) ;
}
else {
printk ( KERN_WARNING " %s: host does not support reading read-only "
" switch. Assuming write-enable. \n " , mmc_hostname ( mmc ) ) ;
}
return read_only ;
}
static struct mmc_host_ops at91_mci_ops = {
. request = at91_mci_request ,
. set_ios = at91_mci_set_ios ,
. get_ro = at91_mci_get_ro ,
} ;
/*
* Probe for the device
*/
static int at91_mci_probe ( struct platform_device * pdev )
{
struct mmc_host * mmc ;
struct at91mci_host * host ;
int ret ;
2006-06-19 16:06:05 +04:00
pr_debug ( " Probe MCI devices \n " ) ;
2006-04-02 22:18:51 +04:00
at91_mci_disable ( ) ;
at91_mci_enable ( ) ;
mmc = mmc_alloc_host ( sizeof ( struct at91mci_host ) , & pdev - > dev ) ;
if ( ! mmc ) {
2006-06-19 16:06:05 +04:00
pr_debug ( " Failed to allocate mmc host \n " ) ;
2006-04-02 22:18:51 +04:00
return - ENOMEM ;
}
mmc - > ops = & at91_mci_ops ;
mmc - > f_min = 375000 ;
mmc - > f_max = 25000000 ;
mmc - > ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 ;
host = mmc_priv ( mmc ) ;
host - > mmc = mmc ;
host - > buffer = NULL ;
host - > bus_mode = 0 ;
host - > board = pdev - > dev . platform_data ;
if ( host - > board - > wire4 ) {
# ifdef SUPPORT_4WIRE
mmc - > caps | = MMC_CAP_4_BIT_DATA ;
# else
printk ( " MMC: 4 wire bus mode not supported by this driver - using 1 wire \n " ) ;
# endif
}
/*
* Get Clock
*/
mci_clk = clk_get ( & pdev - > dev , " mci_clk " ) ;
2006-06-19 16:06:05 +04:00
if ( IS_ERR ( mci_clk ) ) {
2006-04-02 22:18:51 +04:00
printk ( KERN_ERR " AT91 MMC: no clock defined. \n " ) ;
2006-06-19 16:06:05 +04:00
mmc_free_host ( mmc ) ;
2006-04-02 22:18:51 +04:00
return - ENODEV ;
}
clk_enable ( mci_clk ) ; /* Enable the peripheral clock */
/*
* Allocate the MCI interrupt
*/
2006-07-02 06:29:38 +04:00
ret = request_irq ( AT91_ID_MCI , at91_mci_irq , IRQF_SHARED , DRIVER_NAME , host ) ;
2006-04-02 22:18:51 +04:00
if ( ret ) {
2006-06-19 16:06:05 +04:00
printk ( KERN_ERR " Failed to request MCI interrupt \n " ) ;
clk_disable ( mci_clk ) ;
clk_put ( mci_clk ) ;
mmc_free_host ( mmc ) ;
2006-04-02 22:18:51 +04:00
return ret ;
}
platform_set_drvdata ( pdev , mmc ) ;
/*
* Add host to MMC layer
*/
if ( host - > board - > det_pin )
host - > present = ! at91_get_gpio_value ( host - > board - > det_pin ) ;
else
host - > present = - 1 ;
mmc_add_host ( mmc ) ;
/*
* monitor card insertion / removal if we can
*/
if ( host - > board - > det_pin ) {
ret = request_irq ( host - > board - > det_pin , at91_mmc_det_irq ,
2006-06-19 16:06:05 +04:00
0 , DRIVER_NAME , host ) ;
2006-04-02 22:18:51 +04:00
if ( ret )
2006-06-19 16:06:05 +04:00
printk ( KERN_ERR " couldn't allocate MMC detect irq \n " ) ;
2006-04-02 22:18:51 +04:00
}
2006-06-19 16:06:05 +04:00
pr_debug ( KERN_INFO " Added MCI driver \n " ) ;
2006-04-02 22:18:51 +04:00
return 0 ;
}
/*
* Remove a device
*/
static int at91_mci_remove ( struct platform_device * pdev )
{
struct mmc_host * mmc = platform_get_drvdata ( pdev ) ;
struct at91mci_host * host ;
if ( ! mmc )
return - 1 ;
host = mmc_priv ( mmc ) ;
if ( host - > present ! = - 1 ) {
free_irq ( host - > board - > det_pin , host ) ;
cancel_delayed_work ( & host - > mmc - > detect ) ;
}
mmc_remove_host ( mmc ) ;
at91_mci_disable ( ) ;
free_irq ( AT91_ID_MCI , host ) ;
mmc_free_host ( mmc ) ;
clk_disable ( mci_clk ) ; /* Disable the peripheral clock */
clk_put ( mci_clk ) ;
platform_set_drvdata ( pdev , NULL ) ;
2006-06-19 16:06:05 +04:00
pr_debug ( " MCI Removed \n " ) ;
2006-04-02 22:18:51 +04:00
return 0 ;
}
# ifdef CONFIG_PM
static int at91_mci_suspend ( struct platform_device * pdev , pm_message_t state )
{
struct mmc_host * mmc = platform_get_drvdata ( pdev ) ;
int ret = 0 ;
if ( mmc )
ret = mmc_suspend_host ( mmc , state ) ;
return ret ;
}
static int at91_mci_resume ( struct platform_device * pdev )
{
struct mmc_host * mmc = platform_get_drvdata ( pdev ) ;
int ret = 0 ;
if ( mmc )
ret = mmc_resume_host ( mmc ) ;
return ret ;
}
# else
# define at91_mci_suspend NULL
# define at91_mci_resume NULL
# endif
static struct platform_driver at91_mci_driver = {
. probe = at91_mci_probe ,
. remove = at91_mci_remove ,
. suspend = at91_mci_suspend ,
. resume = at91_mci_resume ,
. driver = {
. name = DRIVER_NAME ,
. owner = THIS_MODULE ,
} ,
} ;
static int __init at91_mci_init ( void )
{
return platform_driver_register ( & at91_mci_driver ) ;
}
static void __exit at91_mci_exit ( void )
{
platform_driver_unregister ( & at91_mci_driver ) ;
}
module_init ( at91_mci_init ) ;
module_exit ( at91_mci_exit ) ;
MODULE_DESCRIPTION ( " AT91 Multimedia Card Interface driver " ) ;
MODULE_AUTHOR ( " Nick Randell " ) ;
MODULE_LICENSE ( " GPL " ) ;