2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2009-02-14 03:07:26 -05:00
/*
* Marvell MMC / SD / SDIO driver
*
* Authors : Maen Suleiman , Nicolas Pitre
* Copyright ( C ) 2008 - 2009 Marvell Ltd .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/platform_device.h>
# include <linux/mbus.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/dma-mapping.h>
# include <linux/scatterlist.h>
# include <linux/irq.h>
2012-02-19 11:39:27 +01:00
# include <linux/clk.h>
2013-01-16 14:13:59 +01:00
# include <linux/of_irq.h>
2009-02-14 03:07:26 -05:00
# include <linux/mmc/host.h>
2013-01-16 14:13:57 +01:00
# include <linux/mmc/slot-gpio.h>
2009-02-14 03:07:26 -05:00
2019-05-14 15:46:51 -07:00
# include <linux/sizes.h>
2009-02-14 03:07:26 -05:00
# include <asm/unaligned.h>
# include "mvsdio.h"
# define DRIVER_NAME "mvsdio"
2013-06-09 22:14:18 +02:00
static int maxfreq ;
2009-02-14 03:07:26 -05:00
static int nodma ;
struct mvsd_host {
void __iomem * base ;
struct mmc_request * mrq ;
spinlock_t lock ;
unsigned int xfer_mode ;
unsigned int intr_en ;
unsigned int ctrl ;
unsigned int pio_size ;
void * pio_ptr ;
unsigned int sg_frags ;
unsigned int ns_per_clk ;
unsigned int clock ;
unsigned int base_clock ;
struct timer_list timer ;
struct mmc_host * mmc ;
struct device * dev ;
2012-02-19 11:39:27 +01:00
struct clk * clk ;
2009-02-14 03:07:26 -05:00
} ;
# define mvsd_write(offs, val) writel(val, iobase + (offs))
# define mvsd_read(offs) readl(iobase + (offs))
static int mvsd_setup_data ( struct mvsd_host * host , struct mmc_data * data )
{
void __iomem * iobase = host - > base ;
unsigned int tmout ;
int tmout_index ;
2009-05-26 22:35:34 -04:00
/*
* Hardware weirdness . The FIFO_EMPTY bit of the HW_STATE
* register is sometimes not set before a while when some
* " unusual " data block sizes are used ( such as with the SWITCH
* command ) , even despite the fact that the XFER_DONE interrupt
* was raised . And if another data transfer starts before
* this bit comes to good sense ( which eventually happens by
* itself ) then the new transfer simply fails with a timeout .
*/
if ( ! ( mvsd_read ( MVSD_HW_STATE ) & ( 1 < < 13 ) ) ) {
unsigned long t = jiffies + HZ ;
unsigned int hw_state , count = 0 ;
do {
2014-06-05 23:14:39 +02:00
hw_state = mvsd_read ( MVSD_HW_STATE ) ;
2009-05-26 22:35:34 -04:00
if ( time_after ( jiffies , t ) ) {
dev_warn ( host - > dev , " FIFO_EMPTY bit missing \n " ) ;
break ;
}
count + + ;
} while ( ! ( hw_state & ( 1 < < 13 ) ) ) ;
dev_dbg ( host - > dev , " *** wait for FIFO_EMPTY bit "
" (hw=0x%04x, count=%d, jiffies=%ld) \n " ,
hw_state , count , jiffies - ( t - HZ ) ) ;
}
2009-02-14 03:07:26 -05:00
/* If timeout=0 then maximum timeout index is used. */
tmout = DIV_ROUND_UP ( data - > timeout_ns , host - > ns_per_clk ) ;
tmout + = data - > timeout_clks ;
tmout_index = fls ( tmout - 1 ) - 12 ;
if ( tmout_index < 0 )
tmout_index = 0 ;
if ( tmout_index > MVSD_HOST_CTRL_TMOUT_MAX )
tmout_index = MVSD_HOST_CTRL_TMOUT_MAX ;
dev_dbg ( host - > dev , " data %s at 0x%08x: blocks=%d blksz=%d tmout=%u (%d) \n " ,
( data - > flags & MMC_DATA_READ ) ? " read " : " write " ,
( u32 ) sg_virt ( data - > sg ) , data - > blocks , data - > blksz ,
tmout , tmout_index ) ;
host - > ctrl & = ~ MVSD_HOST_CTRL_TMOUT_MASK ;
host - > ctrl | = MVSD_HOST_CTRL_TMOUT ( tmout_index ) ;
mvsd_write ( MVSD_HOST_CTRL , host - > ctrl ) ;
mvsd_write ( MVSD_BLK_COUNT , data - > blocks ) ;
mvsd_write ( MVSD_BLK_SIZE , data - > blksz ) ;
2014-11-12 23:10:08 +01:00
if ( nodma | | ( data - > blksz | data - > sg - > offset ) & 3 | |
( ( ! ( data - > flags & MMC_DATA_READ ) & & data - > sg - > offset & 0x3f ) ) ) {
2009-02-14 03:07:26 -05:00
/*
* We cannot do DMA on a buffer which offset or size
* is not aligned on a 4 - byte boundary .
2014-11-12 23:10:08 +01:00
*
* It also appears the host to card DMA can corrupt
* data when the buffer is not aligned on a 64 byte
* boundary .
2009-02-14 03:07:26 -05:00
*/
host - > pio_size = data - > blocks * data - > blksz ;
host - > pio_ptr = sg_virt ( data - > sg ) ;
if ( ! nodma )
2013-03-22 14:37:21 +01:00
dev_dbg ( host - > dev , " fallback to PIO for data at 0x%p size %d \n " ,
host - > pio_ptr , host - > pio_size ) ;
2009-02-14 03:07:26 -05:00
return 1 ;
} else {
dma_addr_t phys_addr ;
2017-03-26 20:45:56 +02:00
host - > sg_frags = dma_map_sg ( mmc_dev ( host - > mmc ) ,
data - > sg , data - > sg_len ,
mmc_get_dma_dir ( data ) ) ;
2009-02-14 03:07:26 -05:00
phys_addr = sg_dma_address ( data - > sg ) ;
mvsd_write ( MVSD_SYS_ADDR_LOW , ( u32 ) phys_addr & 0xffff ) ;
mvsd_write ( MVSD_SYS_ADDR_HI , ( u32 ) phys_addr > > 16 ) ;
return 0 ;
}
}
static void mvsd_request ( struct mmc_host * mmc , struct mmc_request * mrq )
{
struct mvsd_host * host = mmc_priv ( mmc ) ;
void __iomem * iobase = host - > base ;
struct mmc_command * cmd = mrq - > cmd ;
u32 cmdreg = 0 , xfer = 0 , intr = 0 ;
unsigned long flags ;
2018-05-31 12:16:04 +02:00
unsigned int timeout ;
2009-02-14 03:07:26 -05:00
BUG_ON ( host - > mrq ! = NULL ) ;
host - > mrq = mrq ;
dev_dbg ( host - > dev , " cmd %d (hw state 0x%04x) \n " ,
cmd - > opcode , mvsd_read ( MVSD_HW_STATE ) ) ;
cmdreg = MVSD_CMD_INDEX ( cmd - > opcode ) ;
if ( cmd - > flags & MMC_RSP_BUSY )
cmdreg | = MVSD_CMD_RSP_48BUSY ;
else if ( cmd - > flags & MMC_RSP_136 )
cmdreg | = MVSD_CMD_RSP_136 ;
else if ( cmd - > flags & MMC_RSP_PRESENT )
cmdreg | = MVSD_CMD_RSP_48 ;
else
cmdreg | = MVSD_CMD_RSP_NONE ;
if ( cmd - > flags & MMC_RSP_CRC )
cmdreg | = MVSD_CMD_CHECK_CMDCRC ;
if ( cmd - > flags & MMC_RSP_OPCODE )
cmdreg | = MVSD_CMD_INDX_CHECK ;
if ( cmd - > flags & MMC_RSP_PRESENT ) {
cmdreg | = MVSD_UNEXPECTED_RESP ;
intr | = MVSD_NOR_UNEXP_RSP ;
}
if ( mrq - > data ) {
struct mmc_data * data = mrq - > data ;
int pio ;
cmdreg | = MVSD_CMD_DATA_PRESENT | MVSD_CMD_CHECK_DATACRC16 ;
xfer | = MVSD_XFER_MODE_HW_WR_DATA_EN ;
if ( data - > flags & MMC_DATA_READ )
xfer | = MVSD_XFER_MODE_TO_HOST ;
pio = mvsd_setup_data ( host , data ) ;
if ( pio ) {
xfer | = MVSD_XFER_MODE_PIO ;
/* PIO section of mvsd_irq has comments on those bits */
if ( data - > flags & MMC_DATA_WRITE )
intr | = MVSD_NOR_TX_AVAIL ;
else if ( host - > pio_size > 32 )
intr | = MVSD_NOR_RX_FIFO_8W ;
else
intr | = MVSD_NOR_RX_READY ;
}
if ( data - > stop ) {
struct mmc_command * stop = data - > stop ;
u32 cmd12reg = 0 ;
mvsd_write ( MVSD_AUTOCMD12_ARG_LOW , stop - > arg & 0xffff ) ;
mvsd_write ( MVSD_AUTOCMD12_ARG_HI , stop - > arg > > 16 ) ;
if ( stop - > flags & MMC_RSP_BUSY )
cmd12reg | = MVSD_AUTOCMD12_BUSY ;
if ( stop - > flags & MMC_RSP_OPCODE )
cmd12reg | = MVSD_AUTOCMD12_INDX_CHECK ;
cmd12reg | = MVSD_AUTOCMD12_INDEX ( stop - > opcode ) ;
mvsd_write ( MVSD_AUTOCMD12_CMD , cmd12reg ) ;
xfer | = MVSD_XFER_MODE_AUTO_CMD12 ;
intr | = MVSD_NOR_AUTOCMD12_DONE ;
} else {
intr | = MVSD_NOR_XFER_DONE ;
}
} else {
intr | = MVSD_NOR_CMD_DONE ;
}
mvsd_write ( MVSD_ARG_LOW , cmd - > arg & 0xffff ) ;
mvsd_write ( MVSD_ARG_HI , cmd - > arg > > 16 ) ;
spin_lock_irqsave ( & host - > lock , flags ) ;
host - > xfer_mode & = MVSD_XFER_MODE_INT_CHK_EN ;
host - > xfer_mode | = xfer ;
mvsd_write ( MVSD_XFER_MODE , host - > xfer_mode ) ;
mvsd_write ( MVSD_NOR_INTR_STATUS , ~ MVSD_NOR_CARD_INT ) ;
mvsd_write ( MVSD_ERR_INTR_STATUS , 0xffff ) ;
mvsd_write ( MVSD_CMD , cmdreg ) ;
host - > intr_en & = MVSD_NOR_CARD_INT ;
host - > intr_en | = intr | MVSD_NOR_ERROR ;
mvsd_write ( MVSD_NOR_INTR_EN , host - > intr_en ) ;
mvsd_write ( MVSD_ERR_INTR_EN , 0xffff ) ;
2018-05-31 12:16:04 +02:00
timeout = cmd - > busy_timeout ? cmd - > busy_timeout : 5000 ;
mod_timer ( & host - > timer , jiffies + msecs_to_jiffies ( timeout ) ) ;
2009-02-14 03:07:26 -05:00
spin_unlock_irqrestore ( & host - > lock , flags ) ;
}
static u32 mvsd_finish_cmd ( struct mvsd_host * host , struct mmc_command * cmd ,
u32 err_status )
{
void __iomem * iobase = host - > base ;
if ( cmd - > flags & MMC_RSP_136 ) {
unsigned int response [ 8 ] , i ;
for ( i = 0 ; i < 8 ; i + + )
response [ i ] = mvsd_read ( MVSD_RSP ( i ) ) ;
cmd - > resp [ 0 ] = ( ( response [ 0 ] & 0x03ff ) < < 22 ) |
( ( response [ 1 ] & 0xffff ) < < 6 ) |
( ( response [ 2 ] & 0xfc00 ) > > 10 ) ;
cmd - > resp [ 1 ] = ( ( response [ 2 ] & 0x03ff ) < < 22 ) |
( ( response [ 3 ] & 0xffff ) < < 6 ) |
( ( response [ 4 ] & 0xfc00 ) > > 10 ) ;
cmd - > resp [ 2 ] = ( ( response [ 4 ] & 0x03ff ) < < 22 ) |
( ( response [ 5 ] & 0xffff ) < < 6 ) |
( ( response [ 6 ] & 0xfc00 ) > > 10 ) ;
cmd - > resp [ 3 ] = ( ( response [ 6 ] & 0x03ff ) < < 22 ) |
( ( response [ 7 ] & 0x3fff ) < < 8 ) ;
} else if ( cmd - > flags & MMC_RSP_PRESENT ) {
unsigned int response [ 3 ] , i ;
for ( i = 0 ; i < 3 ; i + + )
response [ i ] = mvsd_read ( MVSD_RSP ( i ) ) ;
cmd - > resp [ 0 ] = ( ( response [ 2 ] & 0x003f ) < < ( 8 - 8 ) ) |
( ( response [ 1 ] & 0xffff ) < < ( 14 - 8 ) ) |
( ( response [ 0 ] & 0x03ff ) < < ( 30 - 8 ) ) ;
cmd - > resp [ 1 ] = ( ( response [ 0 ] & 0xfc00 ) > > 10 ) ;
cmd - > resp [ 2 ] = 0 ;
cmd - > resp [ 3 ] = 0 ;
}
if ( err_status & MVSD_ERR_CMD_TIMEOUT ) {
cmd - > error = - ETIMEDOUT ;
} else if ( err_status & ( MVSD_ERR_CMD_CRC | MVSD_ERR_CMD_ENDBIT |
MVSD_ERR_CMD_INDEX | MVSD_ERR_CMD_STARTBIT ) ) {
cmd - > error = - EILSEQ ;
}
err_status & = ~ ( MVSD_ERR_CMD_TIMEOUT | MVSD_ERR_CMD_CRC |
MVSD_ERR_CMD_ENDBIT | MVSD_ERR_CMD_INDEX |
MVSD_ERR_CMD_STARTBIT ) ;
return err_status ;
}
static u32 mvsd_finish_data ( struct mvsd_host * host , struct mmc_data * data ,
u32 err_status )
{
void __iomem * iobase = host - > base ;
if ( host - > pio_ptr ) {
host - > pio_ptr = NULL ;
host - > pio_size = 0 ;
} else {
dma_unmap_sg ( mmc_dev ( host - > mmc ) , data - > sg , host - > sg_frags ,
2017-03-26 20:45:56 +02:00
mmc_get_dma_dir ( data ) ) ;
2009-02-14 03:07:26 -05:00
}
if ( err_status & MVSD_ERR_DATA_TIMEOUT )
data - > error = - ETIMEDOUT ;
else if ( err_status & ( MVSD_ERR_DATA_CRC | MVSD_ERR_DATA_ENDBIT ) )
data - > error = - EILSEQ ;
else if ( err_status & MVSD_ERR_XFER_SIZE )
data - > error = - EBADE ;
err_status & = ~ ( MVSD_ERR_DATA_TIMEOUT | MVSD_ERR_DATA_CRC |
MVSD_ERR_DATA_ENDBIT | MVSD_ERR_XFER_SIZE ) ;
dev_dbg ( host - > dev , " data done: blocks_left=%d, bytes_left=%d \n " ,
mvsd_read ( MVSD_CURR_BLK_LEFT ) , mvsd_read ( MVSD_CURR_BYTE_LEFT ) ) ;
data - > bytes_xfered =
( data - > blocks - mvsd_read ( MVSD_CURR_BLK_LEFT ) ) * data - > blksz ;
/* We can't be sure about the last block when errors are detected */
if ( data - > bytes_xfered & & data - > error )
data - > bytes_xfered - = data - > blksz ;
/* Handle Auto cmd 12 response */
if ( data - > stop ) {
unsigned int response [ 3 ] , i ;
for ( i = 0 ; i < 3 ; i + + )
response [ i ] = mvsd_read ( MVSD_AUTO_RSP ( i ) ) ;
data - > stop - > resp [ 0 ] = ( ( response [ 2 ] & 0x003f ) < < ( 8 - 8 ) ) |
( ( response [ 1 ] & 0xffff ) < < ( 14 - 8 ) ) |
( ( response [ 0 ] & 0x03ff ) < < ( 30 - 8 ) ) ;
data - > stop - > resp [ 1 ] = ( ( response [ 0 ] & 0xfc00 ) > > 10 ) ;
data - > stop - > resp [ 2 ] = 0 ;
data - > stop - > resp [ 3 ] = 0 ;
if ( err_status & MVSD_ERR_AUTOCMD12 ) {
u32 err_cmd12 = mvsd_read ( MVSD_AUTOCMD12_ERR_STATUS ) ;
dev_dbg ( host - > dev , " c12err 0x%04x \n " , err_cmd12 ) ;
if ( err_cmd12 & MVSD_AUTOCMD12_ERR_NOTEXE )
data - > stop - > error = - ENOEXEC ;
else if ( err_cmd12 & MVSD_AUTOCMD12_ERR_TIMEOUT )
data - > stop - > error = - ETIMEDOUT ;
else if ( err_cmd12 )
data - > stop - > error = - EILSEQ ;
err_status & = ~ MVSD_ERR_AUTOCMD12 ;
}
}
return err_status ;
}
static irqreturn_t mvsd_irq ( int irq , void * dev )
{
struct mvsd_host * host = dev ;
void __iomem * iobase = host - > base ;
u32 intr_status , intr_done_mask ;
int irq_handled = 0 ;
intr_status = mvsd_read ( MVSD_NOR_INTR_STATUS ) ;
dev_dbg ( host - > dev , " intr 0x%04x intr_en 0x%04x hw_state 0x%04x \n " ,
intr_status , mvsd_read ( MVSD_NOR_INTR_EN ) ,
mvsd_read ( MVSD_HW_STATE ) ) ;
2014-04-26 21:34:14 +02:00
/*
* It looks like , SDIO IP can issue one late , spurious irq
* although all irqs should be disabled . To work around this ,
* bail out early , if we didn ' t expect any irqs to occur .
*/
if ( ! mvsd_read ( MVSD_NOR_INTR_EN ) & & ! mvsd_read ( MVSD_ERR_INTR_EN ) ) {
dev_dbg ( host - > dev , " spurious irq detected intr 0x%04x intr_en 0x%04x erri 0x%04x erri_en 0x%04x \n " ,
mvsd_read ( MVSD_NOR_INTR_STATUS ) ,
mvsd_read ( MVSD_NOR_INTR_EN ) ,
mvsd_read ( MVSD_ERR_INTR_STATUS ) ,
mvsd_read ( MVSD_ERR_INTR_EN ) ) ;
return IRQ_HANDLED ;
}
2009-02-14 03:07:26 -05:00
spin_lock ( & host - > lock ) ;
/* PIO handling, if needed. Messy business... */
if ( host - > pio_size & &
( intr_status & host - > intr_en &
( MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W ) ) ) {
u16 * p = host - > pio_ptr ;
int s = host - > pio_size ;
while ( s > = 32 & & ( intr_status & MVSD_NOR_RX_FIFO_8W ) ) {
readsw ( iobase + MVSD_FIFO , p , 16 ) ;
p + = 16 ;
s - = 32 ;
intr_status = mvsd_read ( MVSD_NOR_INTR_STATUS ) ;
}
/*
* Normally we ' d use < 32 here , but the RX_FIFO_8W bit
* doesn ' t appear to assert when there is exactly 32 bytes
* ( 8 words ) left to fetch in a transfer .
*/
if ( s < = 32 ) {
while ( s > = 4 & & ( intr_status & MVSD_NOR_RX_READY ) ) {
put_unaligned ( mvsd_read ( MVSD_FIFO ) , p + + ) ;
put_unaligned ( mvsd_read ( MVSD_FIFO ) , p + + ) ;
s - = 4 ;
intr_status = mvsd_read ( MVSD_NOR_INTR_STATUS ) ;
}
if ( s & & s < 4 & & ( intr_status & MVSD_NOR_RX_READY ) ) {
u16 val [ 2 ] = { 0 , 0 } ;
val [ 0 ] = mvsd_read ( MVSD_FIFO ) ;
val [ 1 ] = mvsd_read ( MVSD_FIFO ) ;
2009-07-18 20:34:37 -04:00
memcpy ( p , ( ( void * ) & val ) + 4 - s , s ) ;
2009-02-14 03:07:26 -05:00
s = 0 ;
intr_status = mvsd_read ( MVSD_NOR_INTR_STATUS ) ;
}
if ( s = = 0 ) {
host - > intr_en & =
~ ( MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W ) ;
mvsd_write ( MVSD_NOR_INTR_EN , host - > intr_en ) ;
} else if ( host - > intr_en & MVSD_NOR_RX_FIFO_8W ) {
host - > intr_en & = ~ MVSD_NOR_RX_FIFO_8W ;
host - > intr_en | = MVSD_NOR_RX_READY ;
mvsd_write ( MVSD_NOR_INTR_EN , host - > intr_en ) ;
}
}
dev_dbg ( host - > dev , " pio %d intr 0x%04x hw_state 0x%04x \n " ,
s , intr_status , mvsd_read ( MVSD_HW_STATE ) ) ;
host - > pio_ptr = p ;
host - > pio_size = s ;
irq_handled = 1 ;
} else if ( host - > pio_size & &
( intr_status & host - > intr_en &
( MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W ) ) ) {
u16 * p = host - > pio_ptr ;
int s = host - > pio_size ;
/*
* The TX_FIFO_8W bit is unreliable . When set , bursting
* 16 halfwords all at once in the FIFO drops data . Actually
* TX_AVAIL does go off after only one word is pushed even if
* TX_FIFO_8W remains set .
*/
while ( s > = 4 & & ( intr_status & MVSD_NOR_TX_AVAIL ) ) {
mvsd_write ( MVSD_FIFO , get_unaligned ( p + + ) ) ;
mvsd_write ( MVSD_FIFO , get_unaligned ( p + + ) ) ;
s - = 4 ;
intr_status = mvsd_read ( MVSD_NOR_INTR_STATUS ) ;
}
if ( s < 4 ) {
if ( s & & ( intr_status & MVSD_NOR_TX_AVAIL ) ) {
u16 val [ 2 ] = { 0 , 0 } ;
2009-07-18 20:34:37 -04:00
memcpy ( ( ( void * ) & val ) + 4 - s , p , s ) ;
2009-02-14 03:07:26 -05:00
mvsd_write ( MVSD_FIFO , val [ 0 ] ) ;
mvsd_write ( MVSD_FIFO , val [ 1 ] ) ;
s = 0 ;
intr_status = mvsd_read ( MVSD_NOR_INTR_STATUS ) ;
}
if ( s = = 0 ) {
host - > intr_en & =
~ ( MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W ) ;
mvsd_write ( MVSD_NOR_INTR_EN , host - > intr_en ) ;
}
}
dev_dbg ( host - > dev , " pio %d intr 0x%04x hw_state 0x%04x \n " ,
s , intr_status , mvsd_read ( MVSD_HW_STATE ) ) ;
host - > pio_ptr = p ;
host - > pio_size = s ;
irq_handled = 1 ;
}
mvsd_write ( MVSD_NOR_INTR_STATUS , intr_status ) ;
intr_done_mask = MVSD_NOR_CARD_INT | MVSD_NOR_RX_READY |
MVSD_NOR_RX_FIFO_8W | MVSD_NOR_TX_FIFO_8W ;
if ( intr_status & host - > intr_en & ~ intr_done_mask ) {
struct mmc_request * mrq = host - > mrq ;
struct mmc_command * cmd = mrq - > cmd ;
u32 err_status = 0 ;
del_timer ( & host - > timer ) ;
host - > mrq = NULL ;
host - > intr_en & = MVSD_NOR_CARD_INT ;
mvsd_write ( MVSD_NOR_INTR_EN , host - > intr_en ) ;
mvsd_write ( MVSD_ERR_INTR_EN , 0 ) ;
spin_unlock ( & host - > lock ) ;
if ( intr_status & MVSD_NOR_UNEXP_RSP ) {
cmd - > error = - EPROTO ;
} else if ( intr_status & MVSD_NOR_ERROR ) {
err_status = mvsd_read ( MVSD_ERR_INTR_STATUS ) ;
dev_dbg ( host - > dev , " err 0x%04x \n " , err_status ) ;
}
err_status = mvsd_finish_cmd ( host , cmd , err_status ) ;
if ( mrq - > data )
err_status = mvsd_finish_data ( host , mrq - > data , err_status ) ;
if ( err_status ) {
2013-03-22 14:37:21 +01:00
dev_err ( host - > dev , " unhandled error status %#04x \n " ,
err_status ) ;
2009-02-14 03:07:26 -05:00
cmd - > error = - ENOMSG ;
}
mmc_request_done ( host - > mmc , mrq ) ;
irq_handled = 1 ;
} else
spin_unlock ( & host - > lock ) ;
if ( intr_status & MVSD_NOR_CARD_INT ) {
mmc_signal_sdio_irq ( host - > mmc ) ;
irq_handled = 1 ;
}
if ( irq_handled )
return IRQ_HANDLED ;
2013-03-22 14:37:21 +01:00
dev_err ( host - > dev , " unhandled interrupt status=0x%04x en=0x%04x pio=%d \n " ,
intr_status , host - > intr_en , host - > pio_size ) ;
2009-02-14 03:07:26 -05:00
return IRQ_NONE ;
}
2017-10-24 08:03:45 -07:00
static void mvsd_timeout_timer ( struct timer_list * t )
2009-02-14 03:07:26 -05:00
{
2017-10-24 08:03:45 -07:00
struct mvsd_host * host = from_timer ( host , t , timer ) ;
2009-02-14 03:07:26 -05:00
void __iomem * iobase = host - > base ;
struct mmc_request * mrq ;
unsigned long flags ;
spin_lock_irqsave ( & host - > lock , flags ) ;
mrq = host - > mrq ;
if ( mrq ) {
2013-03-22 14:37:21 +01:00
dev_err ( host - > dev , " Timeout waiting for hardware interrupt. \n " ) ;
dev_err ( host - > dev , " hw_state=0x%04x, intr_status=0x%04x intr_en=0x%04x \n " ,
mvsd_read ( MVSD_HW_STATE ) ,
mvsd_read ( MVSD_NOR_INTR_STATUS ) ,
mvsd_read ( MVSD_NOR_INTR_EN ) ) ;
2009-02-14 03:07:26 -05:00
host - > mrq = NULL ;
mvsd_write ( MVSD_SW_RESET , MVSD_SW_RESET_NOW ) ;
host - > xfer_mode & = MVSD_XFER_MODE_INT_CHK_EN ;
mvsd_write ( MVSD_XFER_MODE , host - > xfer_mode ) ;
host - > intr_en & = MVSD_NOR_CARD_INT ;
mvsd_write ( MVSD_NOR_INTR_EN , host - > intr_en ) ;
mvsd_write ( MVSD_ERR_INTR_EN , 0 ) ;
mvsd_write ( MVSD_ERR_INTR_STATUS , 0xffff ) ;
mrq - > cmd - > error = - ETIMEDOUT ;
mvsd_finish_cmd ( host , mrq - > cmd , 0 ) ;
if ( mrq - > data ) {
mrq - > data - > error = - ETIMEDOUT ;
mvsd_finish_data ( host , mrq - > data , 0 ) ;
}
}
spin_unlock_irqrestore ( & host - > lock , flags ) ;
if ( mrq )
mmc_request_done ( host - > mmc , mrq ) ;
}
static void mvsd_enable_sdio_irq ( struct mmc_host * mmc , int enable )
{
struct mvsd_host * host = mmc_priv ( mmc ) ;
void __iomem * iobase = host - > base ;
unsigned long flags ;
spin_lock_irqsave ( & host - > lock , flags ) ;
if ( enable ) {
host - > xfer_mode | = MVSD_XFER_MODE_INT_CHK_EN ;
host - > intr_en | = MVSD_NOR_CARD_INT ;
} else {
host - > xfer_mode & = ~ MVSD_XFER_MODE_INT_CHK_EN ;
host - > intr_en & = ~ MVSD_NOR_CARD_INT ;
}
mvsd_write ( MVSD_XFER_MODE , host - > xfer_mode ) ;
mvsd_write ( MVSD_NOR_INTR_EN , host - > intr_en ) ;
spin_unlock_irqrestore ( & host - > lock , flags ) ;
}
static void mvsd_power_up ( struct mvsd_host * host )
{
void __iomem * iobase = host - > base ;
dev_dbg ( host - > dev , " power up \n " ) ;
mvsd_write ( MVSD_NOR_INTR_EN , 0 ) ;
mvsd_write ( MVSD_ERR_INTR_EN , 0 ) ;
mvsd_write ( MVSD_SW_RESET , MVSD_SW_RESET_NOW ) ;
mvsd_write ( MVSD_XFER_MODE , 0 ) ;
mvsd_write ( MVSD_NOR_STATUS_EN , 0xffff ) ;
mvsd_write ( MVSD_ERR_STATUS_EN , 0xffff ) ;
mvsd_write ( MVSD_NOR_INTR_STATUS , 0xffff ) ;
mvsd_write ( MVSD_ERR_INTR_STATUS , 0xffff ) ;
}
static void mvsd_power_down ( struct mvsd_host * host )
{
void __iomem * iobase = host - > base ;
dev_dbg ( host - > dev , " power down \n " ) ;
mvsd_write ( MVSD_NOR_INTR_EN , 0 ) ;
mvsd_write ( MVSD_ERR_INTR_EN , 0 ) ;
mvsd_write ( MVSD_SW_RESET , MVSD_SW_RESET_NOW ) ;
mvsd_write ( MVSD_XFER_MODE , MVSD_XFER_MODE_STOP_CLK ) ;
mvsd_write ( MVSD_NOR_STATUS_EN , 0 ) ;
mvsd_write ( MVSD_ERR_STATUS_EN , 0 ) ;
mvsd_write ( MVSD_NOR_INTR_STATUS , 0xffff ) ;
mvsd_write ( MVSD_ERR_INTR_STATUS , 0xffff ) ;
}
static void mvsd_set_ios ( struct mmc_host * mmc , struct mmc_ios * ios )
{
struct mvsd_host * host = mmc_priv ( mmc ) ;
void __iomem * iobase = host - > base ;
u32 ctrl_reg = 0 ;
if ( ios - > power_mode = = MMC_POWER_UP )
mvsd_power_up ( host ) ;
if ( ios - > clock = = 0 ) {
mvsd_write ( MVSD_XFER_MODE , MVSD_XFER_MODE_STOP_CLK ) ;
mvsd_write ( MVSD_CLK_DIV , MVSD_BASE_DIV_MAX ) ;
host - > clock = 0 ;
dev_dbg ( host - > dev , " clock off \n " ) ;
} else if ( ios - > clock ! = host - > clock ) {
u32 m = DIV_ROUND_UP ( host - > base_clock , ios - > clock ) - 1 ;
if ( m > MVSD_BASE_DIV_MAX )
m = MVSD_BASE_DIV_MAX ;
mvsd_write ( MVSD_CLK_DIV , m ) ;
host - > clock = ios - > clock ;
host - > ns_per_clk = 1000000000 / ( host - > base_clock / ( m + 1 ) ) ;
dev_dbg ( host - > dev , " clock=%d (%d), div=0x%04x \n " ,
ios - > clock , host - > base_clock / ( m + 1 ) , m ) ;
}
/* default transfer mode */
ctrl_reg | = MVSD_HOST_CTRL_BIG_ENDIAN ;
ctrl_reg & = ~ MVSD_HOST_CTRL_LSB_FIRST ;
/* default to maximum timeout */
ctrl_reg | = MVSD_HOST_CTRL_TMOUT_MASK ;
ctrl_reg | = MVSD_HOST_CTRL_TMOUT_EN ;
if ( ios - > bus_mode = = MMC_BUSMODE_PUSHPULL )
ctrl_reg | = MVSD_HOST_CTRL_PUSH_PULL_EN ;
if ( ios - > bus_width = = MMC_BUS_WIDTH_4 )
ctrl_reg | = MVSD_HOST_CTRL_DATA_WIDTH_4_BITS ;
2009-05-14 21:28:05 -04:00
/*
* The HI_SPEED_EN bit is causing trouble with many ( but not all )
* high speed SD , SDHC and SDIO cards . Not enabling that bit
* makes all cards work . So let ' s just ignore that bit for now
* and revisit this issue if problems for not enabling this bit
* are ever reported .
*/
#if 0
2009-02-14 03:07:26 -05:00
if ( ios - > timing = = MMC_TIMING_MMC_HS | |
ios - > timing = = MMC_TIMING_SD_HS )
ctrl_reg | = MVSD_HOST_CTRL_HI_SPEED_EN ;
2009-05-14 21:28:05 -04:00
# endif
2009-02-14 03:07:26 -05:00
host - > ctrl = ctrl_reg ;
mvsd_write ( MVSD_HOST_CTRL , ctrl_reg ) ;
dev_dbg ( host - > dev , " ctrl 0x%04x: %s %s %s \n " , ctrl_reg ,
( ctrl_reg & MVSD_HOST_CTRL_PUSH_PULL_EN ) ?
" push-pull " : " open-drain " ,
( ctrl_reg & MVSD_HOST_CTRL_DATA_WIDTH_4_BITS ) ?
" 4bit-width " : " 1bit-width " ,
( ctrl_reg & MVSD_HOST_CTRL_HI_SPEED_EN ) ?
" high-speed " : " " ) ;
if ( ios - > power_mode = = MMC_POWER_OFF )
mvsd_power_down ( host ) ;
}
static const struct mmc_host_ops mvsd_ops = {
. request = mvsd_request ,
2013-01-16 14:13:57 +01:00
. get_ro = mmc_gpio_get_ro ,
2009-02-14 03:07:26 -05:00
. set_ios = mvsd_set_ios ,
. enable_sdio_irq = mvsd_enable_sdio_irq ,
} ;
2013-09-23 16:27:27 +02:00
static void
2011-12-07 21:48:07 +01:00
mv_conf_mbus_windows ( struct mvsd_host * host ,
const struct mbus_dram_target_info * dram )
2009-02-14 03:07:26 -05:00
{
void __iomem * iobase = host - > base ;
int i ;
for ( i = 0 ; i < 4 ; i + + ) {
writel ( 0 , iobase + MVSD_WINDOW_CTRL ( i ) ) ;
writel ( 0 , iobase + MVSD_WINDOW_BASE ( i ) ) ;
}
for ( i = 0 ; i < dram - > num_cs ; i + + ) {
2011-12-07 21:48:07 +01:00
const struct mbus_dram_window * cs = dram - > cs + i ;
2009-02-14 03:07:26 -05:00
writel ( ( ( cs - > size - 1 ) & 0xffff0000 ) |
( cs - > mbus_attr < < 8 ) |
( dram - > mbus_dram_target_id < < 4 ) | 1 ,
iobase + MVSD_WINDOW_CTRL ( i ) ) ;
writel ( cs - > base , iobase + MVSD_WINDOW_BASE ( i ) ) ;
}
}
2013-09-23 16:27:27 +02:00
static int mvsd_probe ( struct platform_device * pdev )
2009-02-14 03:07:26 -05:00
{
2013-01-16 14:13:59 +01:00
struct device_node * np = pdev - > dev . of_node ;
2009-02-14 03:07:26 -05:00
struct mmc_host * mmc = NULL ;
struct mvsd_host * host = NULL ;
2011-12-07 21:48:07 +01:00
const struct mbus_dram_target_info * dram ;
2009-02-14 03:07:26 -05:00
int ret , irq ;
2015-11-25 14:57:57 +01:00
if ( ! np ) {
dev_err ( & pdev - > dev , " no DT node \n " ) ;
return - ENODEV ;
}
2009-02-14 03:07:26 -05:00
irq = platform_get_irq ( pdev , 0 ) ;
2019-12-15 17:51:18 +00:00
if ( irq < 0 )
2009-02-14 03:07:26 -05:00
return - ENXIO ;
mmc = mmc_alloc_host ( sizeof ( struct mvsd_host ) , & pdev - > dev ) ;
if ( ! mmc ) {
ret = - ENOMEM ;
goto out ;
}
host = mmc_priv ( mmc ) ;
host - > mmc = mmc ;
host - > dev = & pdev - > dev ;
2013-01-16 14:13:59 +01:00
/*
* Some non - DT platforms do not pass a clock , and the clock
* frequency is passed through platform_data . On DT platforms ,
* a clock must always be passed , even if there is no gatable
* clock associated to the SDIO interface ( it can simply be a
* fixed rate clock ) .
*/
host - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2015-11-25 14:57:57 +01:00
if ( IS_ERR ( host - > clk ) ) {
dev_err ( & pdev - > dev , " no clock associated \n " ) ;
ret = - EINVAL ;
goto out ;
}
clk_prepare_enable ( host - > clk ) ;
2013-01-16 14:13:59 +01:00
2013-06-09 22:14:18 +02:00
mmc - > ops = & mvsd_ops ;
mmc - > ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 ;
mmc - > f_min = DIV_ROUND_UP ( host - > base_clock , MVSD_BASE_DIV_MAX ) ;
mmc - > f_max = MVSD_CLOCKRATE_MAX ;
mmc - > max_blk_size = 2048 ;
mmc - > max_blk_count = 65535 ;
mmc - > max_segs = 1 ;
mmc - > max_seg_size = mmc - > max_blk_size * mmc - > max_blk_count ;
mmc - > max_req_size = mmc - > max_blk_size * mmc - > max_blk_count ;
2015-11-25 14:57:57 +01:00
host - > base_clock = clk_get_rate ( host - > clk ) / 2 ;
ret = mmc_of_parse ( mmc ) ;
if ( ret < 0 )
goto out ;
2013-06-09 22:14:18 +02:00
if ( maxfreq )
mmc - > f_max = maxfreq ;
2009-02-14 03:07:26 -05:00
spin_lock_init ( & host - > lock ) ;
2019-12-15 17:51:18 +00:00
host - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2013-09-12 12:16:34 +05:30
if ( IS_ERR ( host - > base ) ) {
ret = PTR_ERR ( host - > base ) ;
2009-02-14 03:07:26 -05:00
goto out ;
}
/* (Re-)program MBUS remapping windows if we are asked to. */
2011-12-07 21:48:07 +01:00
dram = mv_mbus_dram_info ( ) ;
if ( dram )
mv_conf_mbus_windows ( host , dram ) ;
2009-02-14 03:07:26 -05:00
mvsd_power_down ( host ) ;
2013-01-16 14:13:56 +01:00
ret = devm_request_irq ( & pdev - > dev , irq , mvsd_irq , 0 , DRIVER_NAME , host ) ;
2009-02-14 03:07:26 -05:00
if ( ret ) {
2013-03-22 14:37:21 +01:00
dev_err ( & pdev - > dev , " cannot assign irq %d \n " , irq ) ;
2009-02-14 03:07:26 -05:00
goto out ;
2013-01-16 14:13:56 +01:00
}
2009-02-14 03:07:26 -05:00
2017-10-24 08:03:45 -07:00
timer_setup ( & host - > timer , mvsd_timeout_timer , 0 ) ;
2009-02-14 03:07:26 -05:00
platform_set_drvdata ( pdev , mmc ) ;
ret = mmc_add_host ( mmc ) ;
if ( ret )
goto out ;
2013-01-16 14:13:58 +01:00
if ( ! ( mmc - > caps & MMC_CAP_NEEDS_POLL ) )
2014-04-26 21:34:13 +02:00
dev_dbg ( & pdev - > dev , " using GPIO for card detection \n " ) ;
2009-02-14 03:07:26 -05:00
else
2014-04-26 21:34:13 +02:00
dev_dbg ( & pdev - > dev , " lacking card detect (fall back to polling) \n " ) ;
2009-02-14 03:07:26 -05:00
return 0 ;
out :
2013-01-16 14:13:56 +01:00
if ( mmc ) {
if ( ! IS_ERR ( host - > clk ) )
2012-07-19 00:04:09 +02:00
clk_disable_unprepare ( host - > clk ) ;
2009-02-14 03:07:26 -05:00
mmc_free_host ( mmc ) ;
2013-01-16 14:13:56 +01:00
}
2009-02-14 03:07:26 -05:00
return ret ;
}
2013-09-23 16:27:27 +02:00
static int mvsd_remove ( struct platform_device * pdev )
2009-02-14 03:07:26 -05:00
{
struct mmc_host * mmc = platform_get_drvdata ( pdev ) ;
2013-01-16 14:13:56 +01:00
struct mvsd_host * host = mmc_priv ( mmc ) ;
2009-02-14 03:07:26 -05:00
2013-01-16 14:13:56 +01:00
mmc_remove_host ( mmc ) ;
del_timer_sync ( & host - > timer ) ;
mvsd_power_down ( host ) ;
if ( ! IS_ERR ( host - > clk ) )
clk_disable_unprepare ( host - > clk ) ;
mmc_free_host ( mmc ) ;
2012-02-19 11:39:27 +01:00
2009-02-14 03:07:26 -05:00
return 0 ;
}
2013-01-16 14:13:59 +01:00
static const struct of_device_id mvsdio_dt_ids [ ] = {
{ . compatible = " marvell,orion-sdio " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , mvsdio_dt_ids ) ;
2009-02-14 03:07:26 -05:00
static struct platform_driver mvsd_driver = {
2013-09-23 16:27:27 +02:00
. probe = mvsd_probe ,
. remove = mvsd_remove ,
2009-02-14 03:07:26 -05:00
. driver = {
. name = DRIVER_NAME ,
2020-09-03 16:24:36 -07:00
. probe_type = PROBE_PREFER_ASYNCHRONOUS ,
2013-01-16 14:13:59 +01:00
. of_match_table = mvsdio_dt_ids ,
2009-02-14 03:07:26 -05:00
} ,
} ;
2013-09-23 16:27:27 +02:00
module_platform_driver ( mvsd_driver ) ;
2009-02-14 03:07:26 -05:00
/* maximum card clock frequency (default 50MHz) */
module_param ( maxfreq , int , 0 ) ;
/* force PIO transfers all the time */
module_param ( nodma , int , 0 ) ;
MODULE_AUTHOR ( " Maen Suleiman, Nicolas Pitre " ) ;
MODULE_DESCRIPTION ( " Marvell MMC,SD,SDIO Host Controller driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2009-05-09 01:03:52 -04:00
MODULE_ALIAS ( " platform:mvsdio " ) ;