2009-01-06 19:04:14 +03:00
/*
* linux / drivers / mmc / host / mxcmmc . c - Freescale i . MX MMCI driver
*
* This is a driver for the SDHC controller found in Freescale MX2 / MX3
* SoCs . It is basically the same hardware as found on MX1 ( imxmmc . c ) .
* Unlike the hardware found on MX1 , this hardware just works and does
tree-wide: Assorted spelling fixes
In particular, several occurances of funny versions of 'success',
'unknown', 'therefore', 'acknowledge', 'argument', 'achieve', 'address',
'beginning', 'desirable', 'separate' and 'necessary' are fixed.
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Joe Perches <joe@perches.com>
Cc: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-02-03 03:01:28 +03:00
* not need all the quirks found in imxmmc . c , hence the separate driver .
2009-01-06 19:04:14 +03:00
*
* Copyright ( C ) 2008 Sascha Hauer , Pengutronix < s . hauer @ pengutronix . de >
* Copyright ( C ) 2006 Pavel Pisa , PiKRON < ppisa @ pikron . com >
*
* derived from pxamci . c by Russell King
*
* 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/init.h>
# include <linux/ioport.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/blkdev.h>
# include <linux/dma-mapping.h>
# include <linux/mmc/host.h>
# include <linux/mmc/card.h>
# include <linux/delay.h>
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/gpio.h>
2010-11-02 03:05:37 +03:00
# include <linux/regulator/consumer.h>
2011-02-18 12:21:09 +03:00
# include <linux/dmaengine.h>
2012-02-01 14:42:19 +04:00
# include <linux/types.h>
2013-03-31 22:07:38 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/of_dma.h>
# include <linux/of_gpio.h>
2013-04-09 01:28:07 +04:00
# include <linux/mmc/slot-gpio.h>
2009-01-06 19:04:14 +03:00
# include <asm/dma.h>
# include <asm/irq.h>
2012-08-24 17:14:29 +04:00
# include <linux/platform_data/mmc-mxcmmc.h>
2009-01-06 19:04:14 +03:00
2012-08-24 17:14:29 +04:00
# include <linux/platform_data/dma-imx.h>
2009-01-06 19:04:14 +03:00
2009-02-23 15:08:06 +03:00
# define DRIVER_NAME "mxc-mmc"
2012-09-07 14:43:37 +04:00
# define MXCMCI_TIMEOUT_MS 10000
2009-01-06 19:04:14 +03:00
# define MMC_REG_STR_STP_CLK 0x00
# define MMC_REG_STATUS 0x04
# define MMC_REG_CLK_RATE 0x08
# define MMC_REG_CMD_DAT_CONT 0x0C
# define MMC_REG_RES_TO 0x10
# define MMC_REG_READ_TO 0x14
# define MMC_REG_BLK_LEN 0x18
# define MMC_REG_NOB 0x1C
# define MMC_REG_REV_NO 0x20
# define MMC_REG_INT_CNTR 0x24
# define MMC_REG_CMD 0x28
# define MMC_REG_ARG 0x2C
# define MMC_REG_RES_FIFO 0x34
# define MMC_REG_BUFFER_ACCESS 0x38
# define STR_STP_CLK_RESET (1 << 3)
# define STR_STP_CLK_START_CLK (1 << 1)
# define STR_STP_CLK_STOP_CLK (1 << 0)
# define STATUS_CARD_INSERTION (1 << 31)
# define STATUS_CARD_REMOVAL (1 << 30)
# define STATUS_YBUF_EMPTY (1 << 29)
# define STATUS_XBUF_EMPTY (1 << 28)
# define STATUS_YBUF_FULL (1 << 27)
# define STATUS_XBUF_FULL (1 << 26)
# define STATUS_BUF_UND_RUN (1 << 25)
# define STATUS_BUF_OVFL (1 << 24)
# define STATUS_SDIO_INT_ACTIVE (1 << 14)
# define STATUS_END_CMD_RESP (1 << 13)
# define STATUS_WRITE_OP_DONE (1 << 12)
# define STATUS_DATA_TRANS_DONE (1 << 11)
# define STATUS_READ_OP_DONE (1 << 11)
# define STATUS_WR_CRC_ERROR_CODE_MASK (3 << 10)
# define STATUS_CARD_BUS_CLK_RUN (1 << 8)
# define STATUS_BUF_READ_RDY (1 << 7)
# define STATUS_BUF_WRITE_RDY (1 << 6)
# define STATUS_RESP_CRC_ERR (1 << 5)
# define STATUS_CRC_READ_ERR (1 << 3)
# define STATUS_CRC_WRITE_ERR (1 << 2)
# define STATUS_TIME_OUT_RESP (1 << 1)
# define STATUS_TIME_OUT_READ (1 << 0)
# define STATUS_ERR_MASK 0x2f
# define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1 << 12)
# define CMD_DAT_CONT_STOP_READWAIT (1 << 11)
# define CMD_DAT_CONT_START_READWAIT (1 << 10)
# define CMD_DAT_CONT_BUS_WIDTH_4 (2 << 8)
# define CMD_DAT_CONT_INIT (1 << 7)
# define CMD_DAT_CONT_WRITE (1 << 4)
# define CMD_DAT_CONT_DATA_ENABLE (1 << 3)
# define CMD_DAT_CONT_RESPONSE_48BIT_CRC (1 << 0)
# define CMD_DAT_CONT_RESPONSE_136BIT (2 << 0)
# define CMD_DAT_CONT_RESPONSE_48BIT (3 << 0)
# define INT_SDIO_INT_WKP_EN (1 << 18)
# define INT_CARD_INSERTION_WKP_EN (1 << 17)
# define INT_CARD_REMOVAL_WKP_EN (1 << 16)
# define INT_CARD_INSERTION_EN (1 << 15)
# define INT_CARD_REMOVAL_EN (1 << 14)
# define INT_SDIO_IRQ_EN (1 << 13)
# define INT_DAT0_EN (1 << 12)
# define INT_BUF_READ_EN (1 << 4)
# define INT_BUF_WRITE_EN (1 << 3)
# define INT_END_CMD_RES_EN (1 << 2)
# define INT_WRITE_OP_DONE_EN (1 << 1)
# define INT_READ_OP_EN (1 << 0)
2012-09-16 12:54:30 +04:00
enum mxcmci_type {
IMX21_MMC ,
IMX31_MMC ,
2013-04-09 01:28:06 +04:00
MPC512X_MMC ,
2012-09-16 12:54:30 +04:00
} ;
2009-01-06 19:04:14 +03:00
struct mxcmci_host {
struct mmc_host * mmc ;
void __iomem * base ;
2014-03-22 12:52:45 +04:00
dma_addr_t phys_base ;
2009-01-06 19:04:14 +03:00
int detect_irq ;
2011-02-18 12:21:09 +03:00
struct dma_chan * dma ;
struct dma_async_tx_descriptor * desc ;
2009-01-06 19:04:14 +03:00
int do_dma ;
2010-05-19 20:46:04 +04:00
int default_irq_mask ;
2010-04-01 12:03:24 +04:00
int use_sdio ;
2009-01-06 19:04:14 +03:00
unsigned int power_mode ;
struct imxmmc_platform_data * pdata ;
struct mmc_request * req ;
struct mmc_command * cmd ;
struct mmc_data * data ;
unsigned int datasize ;
unsigned int dma_dir ;
u16 rev_no ;
unsigned int cmdat ;
2012-03-07 12:31:14 +04:00
struct clk * clk_ipg ;
struct clk * clk_per ;
2009-01-06 19:04:14 +03:00
int clock ;
struct work_struct datawork ;
2010-04-01 12:03:24 +04:00
spinlock_t lock ;
2010-11-02 03:05:37 +03:00
struct regulator * vcc ;
2011-02-18 12:21:09 +03:00
int burstlen ;
int dmareq ;
struct dma_slave_config dma_slave_config ;
struct imx_dma_data dma_data ;
2012-09-07 14:43:37 +04:00
struct timer_list watchdog ;
2012-09-16 12:54:30 +04:00
enum mxcmci_type devtype ;
} ;
2013-04-09 01:28:08 +04:00
static const struct platform_device_id mxcmci_devtype [ ] = {
2012-09-16 12:54:30 +04:00
{
. name = " imx21-mmc " ,
. driver_data = IMX21_MMC ,
} , {
. name = " imx31-mmc " ,
. driver_data = IMX31_MMC ,
2013-04-09 01:28:06 +04:00
} , {
. name = " mpc512x-sdhc " ,
. driver_data = MPC512X_MMC ,
2012-09-16 12:54:30 +04:00
} , {
/* sentinel */
}
2009-01-06 19:04:14 +03:00
} ;
2012-09-16 12:54:30 +04:00
MODULE_DEVICE_TABLE ( platform , mxcmci_devtype ) ;
2013-03-31 22:07:38 +04:00
static const struct of_device_id mxcmci_of_match [ ] = {
{
. compatible = " fsl,imx21-mmc " ,
. data = & mxcmci_devtype [ IMX21_MMC ] ,
} , {
. compatible = " fsl,imx31-mmc " ,
. data = & mxcmci_devtype [ IMX31_MMC ] ,
2013-04-09 01:28:06 +04:00
} , {
. compatible = " fsl,mpc5121-sdhc " ,
. data = & mxcmci_devtype [ MPC512X_MMC ] ,
2013-03-31 22:07:38 +04:00
} , {
/* sentinel */
}
} ;
MODULE_DEVICE_TABLE ( of , mxcmci_of_match ) ;
2012-09-16 12:54:30 +04:00
static inline int is_imx31_mmc ( struct mxcmci_host * host )
{
return host - > devtype = = IMX31_MMC ;
}
2009-01-06 19:04:14 +03:00
2013-04-09 01:28:06 +04:00
static inline int is_mpc512x_mmc ( struct mxcmci_host * host )
{
return host - > devtype = = MPC512X_MMC ;
}
static inline u32 mxcmci_readl ( struct mxcmci_host * host , int reg )
{
if ( IS_ENABLED ( CONFIG_PPC_MPC512x ) )
return ioread32be ( host - > base + reg ) ;
else
return readl ( host - > base + reg ) ;
}
static inline void mxcmci_writel ( struct mxcmci_host * host , u32 val , int reg )
{
if ( IS_ENABLED ( CONFIG_PPC_MPC512x ) )
iowrite32be ( val , host - > base + reg ) ;
else
writel ( val , host - > base + reg ) ;
}
static inline u16 mxcmci_readw ( struct mxcmci_host * host , int reg )
{
if ( IS_ENABLED ( CONFIG_PPC_MPC512x ) )
return ioread32be ( host - > base + reg ) ;
else
return readw ( host - > base + reg ) ;
}
static inline void mxcmci_writew ( struct mxcmci_host * host , u16 val , int reg )
{
if ( IS_ENABLED ( CONFIG_PPC_MPC512x ) )
iowrite32be ( val , host - > base + reg ) ;
else
writew ( val , host - > base + reg ) ;
}
2009-04-17 00:00:36 +04:00
static void mxcmci_set_clk_rate ( struct mxcmci_host * host , unsigned int clk_ios ) ;
2014-03-22 12:52:45 +04:00
static void mxcmci_init_ocr ( struct mxcmci_host * host )
2010-11-02 03:05:37 +03:00
{
2014-03-22 12:52:45 +04:00
host - > vcc = devm_regulator_get ( mmc_dev ( host - > mmc ) , " vmmc " ) ;
2010-11-02 03:05:37 +03:00
if ( IS_ERR ( host - > vcc ) ) {
2014-03-22 12:52:45 +04:00
if ( host - > pdata & & host - > pdata - > ocr_avail )
host - > mmc - > ocr_avail = host - > pdata - > ocr_avail ;
else
host - > mmc - > ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 ;
2010-11-02 03:05:37 +03:00
} else {
host - > mmc - > ocr_avail = mmc_regulator_get_ocrmask ( host - > vcc ) ;
if ( host - > pdata & & host - > pdata - > ocr_avail )
dev_warn ( mmc_dev ( host - > mmc ) ,
" pdata->ocr_avail will not be used \n " ) ;
}
}
2014-03-22 12:52:45 +04:00
static void mxcmci_set_power ( struct mxcmci_host * host , unsigned char power_mode ,
unsigned int vdd )
2010-11-02 03:05:37 +03:00
{
2014-03-22 12:52:45 +04:00
if ( ! IS_ERR ( host - > vcc ) ) {
2010-11-09 13:35:37 +03:00
if ( power_mode = = MMC_POWER_UP )
mmc_regulator_set_ocr ( host - > mmc , host - > vcc , vdd ) ;
else if ( power_mode = = MMC_POWER_OFF )
mmc_regulator_set_ocr ( host - > mmc , host - > vcc , 0 ) ;
}
2010-11-02 03:05:37 +03:00
if ( host - > pdata & & host - > pdata - > setpower )
host - > pdata - > setpower ( mmc_dev ( host - > mmc ) , vdd ) ;
}
2009-01-06 19:04:14 +03:00
static inline int mxcmci_use_dma ( struct mxcmci_host * host )
{
return host - > do_dma ;
}
static void mxcmci_softreset ( struct mxcmci_host * host )
{
int i ;
2010-04-01 12:03:23 +04:00
dev_dbg ( mmc_dev ( host - > mmc ) , " mxcmci_softreset \n " ) ;
2009-01-06 19:04:14 +03:00
/* reset sequence */
2013-04-09 01:28:06 +04:00
mxcmci_writew ( host , STR_STP_CLK_RESET , MMC_REG_STR_STP_CLK ) ;
mxcmci_writew ( host , STR_STP_CLK_RESET | STR_STP_CLK_START_CLK ,
MMC_REG_STR_STP_CLK ) ;
2009-01-06 19:04:14 +03:00
for ( i = 0 ; i < 8 ; i + + )
2013-04-09 01:28:06 +04:00
mxcmci_writew ( host , STR_STP_CLK_START_CLK , MMC_REG_STR_STP_CLK ) ;
2009-01-06 19:04:14 +03:00
2013-04-09 01:28:06 +04:00
mxcmci_writew ( host , 0xff , MMC_REG_RES_TO ) ;
2009-01-06 19:04:14 +03:00
}
2013-04-09 01:28:09 +04:00
# if IS_ENABLED(CONFIG_PPC_MPC512x)
static inline void buffer_swap32 ( u32 * buf , int len )
{
int i ;
for ( i = 0 ; i < ( ( len + 3 ) / 4 ) ; i + + ) {
st_le32 ( buf , * buf ) ;
buf + + ;
}
}
static void mxcmci_swap_buffers ( struct mmc_data * data )
{
struct scatterlist * sg ;
int i ;
for_each_sg ( data - > sg , sg , data - > sg_len , i )
buffer_swap32 ( sg_virt ( sg ) , sg - > length ) ;
}
# else
static inline void mxcmci_swap_buffers ( struct mmc_data * data ) { }
# endif
2009-04-17 00:00:41 +04:00
static int mxcmci_setup_data ( struct mxcmci_host * host , struct mmc_data * data )
2009-01-06 19:04:14 +03:00
{
unsigned int nob = data - > blocks ;
unsigned int blksz = data - > blksz ;
unsigned int datasize = nob * blksz ;
struct scatterlist * sg ;
2011-10-14 09:15:11 +04:00
enum dma_transfer_direction slave_dirn ;
2011-02-18 12:21:09 +03:00
int i , nents ;
2009-01-06 19:04:14 +03:00
if ( data - > flags & MMC_DATA_STREAM )
nob = 0xffff ;
host - > data = data ;
data - > bytes_xfered = 0 ;
2013-04-09 01:28:06 +04:00
mxcmci_writew ( host , nob , MMC_REG_NOB ) ;
mxcmci_writew ( host , blksz , MMC_REG_BLK_LEN ) ;
2009-01-06 19:04:14 +03:00
host - > datasize = datasize ;
2011-02-18 12:21:09 +03:00
if ( ! mxcmci_use_dma ( host ) )
return 0 ;
2009-01-06 19:04:14 +03:00
for_each_sg ( data - > sg , sg , data - > sg_len , i ) {
2012-10-30 19:54:07 +04:00
if ( sg - > offset & 3 | | sg - > length & 3 | | sg - > length < 512 ) {
2009-01-06 19:04:14 +03:00
host - > do_dma = 0 ;
2009-04-17 00:00:41 +04:00
return 0 ;
2009-01-06 19:04:14 +03:00
}
}
2011-10-14 09:15:11 +04:00
if ( data - > flags & MMC_DATA_READ ) {
2009-01-06 19:04:14 +03:00
host - > dma_dir = DMA_FROM_DEVICE ;
2011-10-14 09:15:11 +04:00
slave_dirn = DMA_DEV_TO_MEM ;
} else {
2009-01-06 19:04:14 +03:00
host - > dma_dir = DMA_TO_DEVICE ;
2011-10-14 09:15:11 +04:00
slave_dirn = DMA_MEM_TO_DEV ;
2013-04-09 01:28:09 +04:00
mxcmci_swap_buffers ( data ) ;
2011-10-14 09:15:11 +04:00
}
2009-01-06 19:04:14 +03:00
2011-02-18 12:21:09 +03:00
nents = dma_map_sg ( host - > dma - > device - > dev , data - > sg ,
data - > sg_len , host - > dma_dir ) ;
if ( nents ! = data - > sg_len )
return - EINVAL ;
2012-03-09 01:11:18 +04:00
host - > desc = dmaengine_prep_slave_sg ( host - > dma ,
2011-10-14 09:15:11 +04:00
data - > sg , data - > sg_len , slave_dirn ,
2011-02-18 12:21:09 +03:00
DMA_PREP_INTERRUPT | DMA_CTRL_ACK ) ;
2009-01-06 19:04:14 +03:00
2011-02-18 12:21:09 +03:00
if ( ! host - > desc ) {
dma_unmap_sg ( host - > dma - > device - > dev , data - > sg , data - > sg_len ,
host - > dma_dir ) ;
host - > do_dma = 0 ;
return 0 ; /* Fall back to PIO */
2009-04-17 00:00:41 +04:00
}
2009-01-06 19:04:14 +03:00
wmb ( ) ;
2011-02-18 12:21:09 +03:00
dmaengine_submit ( host - > desc ) ;
2012-01-09 13:32:47 +04:00
dma_async_issue_pending ( host - > dma ) ;
2011-02-18 12:21:09 +03:00
2012-09-07 14:43:37 +04:00
mod_timer ( & host - > watchdog , jiffies + msecs_to_jiffies ( MXCMCI_TIMEOUT_MS ) ) ;
2009-04-17 00:00:41 +04:00
return 0 ;
2009-01-06 19:04:14 +03:00
}
2012-09-07 14:43:37 +04:00
static void mxcmci_cmd_done ( struct mxcmci_host * host , unsigned int stat ) ;
static void mxcmci_data_done ( struct mxcmci_host * host , unsigned int stat ) ;
static void mxcmci_dma_callback ( void * data )
{
struct mxcmci_host * host = data ;
u32 stat ;
del_timer ( & host - > watchdog ) ;
2013-04-09 01:28:06 +04:00
stat = mxcmci_readl ( host , MMC_REG_STATUS ) ;
mxcmci_writel ( host , stat & ~ STATUS_DATA_TRANS_DONE , MMC_REG_STATUS ) ;
2012-09-07 14:43:37 +04:00
dev_dbg ( mmc_dev ( host - > mmc ) , " %s: 0x%08x \n " , __func__ , stat ) ;
if ( stat & STATUS_READ_OP_DONE )
2013-04-09 01:28:06 +04:00
mxcmci_writel ( host , STATUS_READ_OP_DONE , MMC_REG_STATUS ) ;
2012-09-07 14:43:37 +04:00
mxcmci_data_done ( host , stat ) ;
}
2009-01-06 19:04:14 +03:00
static int mxcmci_start_cmd ( struct mxcmci_host * host , struct mmc_command * cmd ,
unsigned int cmdat )
{
2010-05-19 20:46:04 +04:00
u32 int_cntr = host - > default_irq_mask ;
2010-04-01 12:03:24 +04:00
unsigned long flags ;
2009-01-06 19:04:14 +03:00
WARN_ON ( host - > cmd ! = NULL ) ;
host - > cmd = cmd ;
switch ( mmc_resp_type ( cmd ) ) {
case MMC_RSP_R1 : /* short CRC, OPCODE */
case MMC_RSP_R1B : /* short CRC, OPCODE, BUSY */
cmdat | = CMD_DAT_CONT_RESPONSE_48BIT_CRC ;
break ;
case MMC_RSP_R2 : /* long 136 bit + CRC */
cmdat | = CMD_DAT_CONT_RESPONSE_136BIT ;
break ;
case MMC_RSP_R3 : /* short */
cmdat | = CMD_DAT_CONT_RESPONSE_48BIT ;
break ;
case MMC_RSP_NONE :
break ;
default :
dev_err ( mmc_dev ( host - > mmc ) , " unhandled response type 0x%x \n " ,
mmc_resp_type ( cmd ) ) ;
cmd - > error = - EINVAL ;
return - EINVAL ;
}
2010-04-01 12:03:24 +04:00
int_cntr = INT_END_CMD_RES_EN ;
2012-09-07 14:43:37 +04:00
if ( mxcmci_use_dma ( host ) ) {
if ( host - > dma_dir = = DMA_FROM_DEVICE ) {
host - > desc - > callback = mxcmci_dma_callback ;
host - > desc - > callback_param = host ;
} else {
int_cntr | = INT_WRITE_OP_DONE_EN ;
}
}
2010-04-01 12:03:24 +04:00
spin_lock_irqsave ( & host - > lock , flags ) ;
if ( host - > use_sdio )
int_cntr | = INT_SDIO_IRQ_EN ;
2013-04-09 01:28:06 +04:00
mxcmci_writel ( host , int_cntr , MMC_REG_INT_CNTR ) ;
2010-04-01 12:03:24 +04:00
spin_unlock_irqrestore ( & host - > lock , flags ) ;
2009-01-06 19:04:14 +03:00
2013-04-09 01:28:06 +04:00
mxcmci_writew ( host , cmd - > opcode , MMC_REG_CMD ) ;
mxcmci_writel ( host , cmd - > arg , MMC_REG_ARG ) ;
mxcmci_writew ( host , cmdat , MMC_REG_CMD_DAT_CONT ) ;
2009-01-06 19:04:14 +03:00
return 0 ;
}
static void mxcmci_finish_request ( struct mxcmci_host * host ,
struct mmc_request * req )
{
2010-05-19 20:46:04 +04:00
u32 int_cntr = host - > default_irq_mask ;
2010-04-01 12:03:24 +04:00
unsigned long flags ;
spin_lock_irqsave ( & host - > lock , flags ) ;
if ( host - > use_sdio )
int_cntr | = INT_SDIO_IRQ_EN ;
2013-04-09 01:28:06 +04:00
mxcmci_writel ( host , int_cntr , MMC_REG_INT_CNTR ) ;
2010-04-01 12:03:24 +04:00
spin_unlock_irqrestore ( & host - > lock , flags ) ;
2009-01-06 19:04:14 +03:00
host - > req = NULL ;
host - > cmd = NULL ;
host - > data = NULL ;
mmc_request_done ( host - > mmc , req ) ;
}
static int mxcmci_finish_data ( struct mxcmci_host * host , unsigned int stat )
{
struct mmc_data * data = host - > data ;
int data_error ;
2013-04-09 01:28:09 +04:00
if ( mxcmci_use_dma ( host ) ) {
2011-02-18 12:21:09 +03:00
dma_unmap_sg ( host - > dma - > device - > dev , data - > sg , data - > sg_len ,
2009-01-06 19:04:14 +03:00
host - > dma_dir ) ;
2013-04-09 01:28:09 +04:00
mxcmci_swap_buffers ( data ) ;
}
2009-01-06 19:04:14 +03:00
if ( stat & STATUS_ERR_MASK ) {
dev_dbg ( mmc_dev ( host - > mmc ) , " request failed. status: 0x%08x \n " ,
stat ) ;
if ( stat & STATUS_CRC_READ_ERR ) {
2010-04-01 12:03:23 +04:00
dev_err ( mmc_dev ( host - > mmc ) , " %s: -EILSEQ \n " , __func__ ) ;
2009-01-06 19:04:14 +03:00
data - > error = - EILSEQ ;
} else if ( stat & STATUS_CRC_WRITE_ERR ) {
u32 err_code = ( stat > > 9 ) & 0x3 ;
2010-04-01 12:03:23 +04:00
if ( err_code = = 2 ) { /* No CRC response */
dev_err ( mmc_dev ( host - > mmc ) ,
" %s: No CRC -ETIMEDOUT \n " , __func__ ) ;
2009-01-06 19:04:14 +03:00
data - > error = - ETIMEDOUT ;
2010-04-01 12:03:23 +04:00
} else {
dev_err ( mmc_dev ( host - > mmc ) ,
" %s: -EILSEQ \n " , __func__ ) ;
2009-01-06 19:04:14 +03:00
data - > error = - EILSEQ ;
2010-04-01 12:03:23 +04:00
}
2009-01-06 19:04:14 +03:00
} else if ( stat & STATUS_TIME_OUT_READ ) {
2010-04-01 12:03:23 +04:00
dev_err ( mmc_dev ( host - > mmc ) ,
" %s: read -ETIMEDOUT \n " , __func__ ) ;
2009-01-06 19:04:14 +03:00
data - > error = - ETIMEDOUT ;
} else {
2010-04-01 12:03:23 +04:00
dev_err ( mmc_dev ( host - > mmc ) , " %s: -EIO \n " , __func__ ) ;
2009-01-06 19:04:14 +03:00
data - > error = - EIO ;
}
} else {
data - > bytes_xfered = host - > datasize ;
}
data_error = data - > error ;
host - > data = NULL ;
return data_error ;
}
static void mxcmci_read_response ( struct mxcmci_host * host , unsigned int stat )
{
struct mmc_command * cmd = host - > cmd ;
int i ;
u32 a , b , c ;
if ( ! cmd )
return ;
if ( stat & STATUS_TIME_OUT_RESP ) {
dev_dbg ( mmc_dev ( host - > mmc ) , " CMD TIMEOUT \n " ) ;
cmd - > error = - ETIMEDOUT ;
} else if ( stat & STATUS_RESP_CRC_ERR & & cmd - > flags & MMC_RSP_CRC ) {
dev_dbg ( mmc_dev ( host - > mmc ) , " cmd crc error \n " ) ;
cmd - > error = - EILSEQ ;
}
if ( cmd - > flags & MMC_RSP_PRESENT ) {
if ( cmd - > flags & MMC_RSP_136 ) {
for ( i = 0 ; i < 4 ; i + + ) {
2013-04-09 01:28:06 +04:00
a = mxcmci_readw ( host , MMC_REG_RES_FIFO ) ;
b = mxcmci_readw ( host , MMC_REG_RES_FIFO ) ;
2009-01-06 19:04:14 +03:00
cmd - > resp [ i ] = a < < 16 | b ;
}
} else {
2013-04-09 01:28:06 +04:00
a = mxcmci_readw ( host , MMC_REG_RES_FIFO ) ;
b = mxcmci_readw ( host , MMC_REG_RES_FIFO ) ;
c = mxcmci_readw ( host , MMC_REG_RES_FIFO ) ;
2009-01-06 19:04:14 +03:00
cmd - > resp [ 0 ] = a < < 24 | b < < 8 | c > > 8 ;
}
}
}
static int mxcmci_poll_status ( struct mxcmci_host * host , u32 mask )
{
u32 stat ;
unsigned long timeout = jiffies + HZ ;
do {
2013-04-09 01:28:06 +04:00
stat = mxcmci_readl ( host , MMC_REG_STATUS ) ;
2009-01-06 19:04:14 +03:00
if ( stat & STATUS_ERR_MASK )
return stat ;
2009-04-17 00:00:36 +04:00
if ( time_after ( jiffies , timeout ) ) {
mxcmci_softreset ( host ) ;
mxcmci_set_clk_rate ( host , host - > clock ) ;
2009-01-06 19:04:14 +03:00
return STATUS_TIME_OUT_READ ;
2009-04-17 00:00:36 +04:00
}
2009-01-06 19:04:14 +03:00
if ( stat & mask )
return 0 ;
cpu_relax ( ) ;
} while ( 1 ) ;
}
static int mxcmci_pull ( struct mxcmci_host * host , void * _buf , int bytes )
{
unsigned int stat ;
u32 * buf = _buf ;
while ( bytes > 3 ) {
stat = mxcmci_poll_status ( host ,
STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE ) ;
if ( stat )
return stat ;
2013-04-09 01:28:06 +04:00
* buf + + = cpu_to_le32 ( mxcmci_readl ( host , MMC_REG_BUFFER_ACCESS ) ) ;
2009-01-06 19:04:14 +03:00
bytes - = 4 ;
}
if ( bytes ) {
u8 * b = ( u8 * ) buf ;
u32 tmp ;
stat = mxcmci_poll_status ( host ,
STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE ) ;
if ( stat )
return stat ;
2013-04-09 01:28:06 +04:00
tmp = cpu_to_le32 ( mxcmci_readl ( host , MMC_REG_BUFFER_ACCESS ) ) ;
2009-01-06 19:04:14 +03:00
memcpy ( b , & tmp , bytes ) ;
}
return 0 ;
}
static int mxcmci_push ( struct mxcmci_host * host , void * _buf , int bytes )
{
unsigned int stat ;
u32 * buf = _buf ;
while ( bytes > 3 ) {
stat = mxcmci_poll_status ( host , STATUS_BUF_WRITE_RDY ) ;
if ( stat )
return stat ;
2013-04-09 01:28:06 +04:00
mxcmci_writel ( host , cpu_to_le32 ( * buf + + ) , MMC_REG_BUFFER_ACCESS ) ;
2009-01-06 19:04:14 +03:00
bytes - = 4 ;
}
if ( bytes ) {
u8 * b = ( u8 * ) buf ;
u32 tmp ;
stat = mxcmci_poll_status ( host , STATUS_BUF_WRITE_RDY ) ;
if ( stat )
return stat ;
memcpy ( & tmp , b , bytes ) ;
2013-04-09 01:28:06 +04:00
mxcmci_writel ( host , cpu_to_le32 ( tmp ) , MMC_REG_BUFFER_ACCESS ) ;
2009-01-06 19:04:14 +03:00
}
stat = mxcmci_poll_status ( host , STATUS_BUF_WRITE_RDY ) ;
if ( stat )
return stat ;
return 0 ;
}
static int mxcmci_transfer_data ( struct mxcmci_host * host )
{
struct mmc_data * data = host - > req - > data ;
struct scatterlist * sg ;
int stat , i ;
host - > data = data ;
host - > datasize = 0 ;
if ( data - > flags & MMC_DATA_READ ) {
for_each_sg ( data - > sg , sg , data - > sg_len , i ) {
stat = mxcmci_pull ( host , sg_virt ( sg ) , sg - > length ) ;
if ( stat )
return stat ;
host - > datasize + = sg - > length ;
}
} else {
for_each_sg ( data - > sg , sg , data - > sg_len , i ) {
stat = mxcmci_push ( host , sg_virt ( sg ) , sg - > length ) ;
if ( stat )
return stat ;
host - > datasize + = sg - > length ;
}
stat = mxcmci_poll_status ( host , STATUS_WRITE_OP_DONE ) ;
if ( stat )
return stat ;
}
return 0 ;
}
static void mxcmci_datawork ( struct work_struct * work )
{
struct mxcmci_host * host = container_of ( work , struct mxcmci_host ,
datawork ) ;
int datastat = mxcmci_transfer_data ( host ) ;
2010-04-27 14:24:42 +04:00
2013-04-09 01:28:06 +04:00
mxcmci_writel ( host , STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE ,
MMC_REG_STATUS ) ;
2009-01-06 19:04:14 +03:00
mxcmci_finish_data ( host , datastat ) ;
if ( host - > req - > stop ) {
if ( mxcmci_start_cmd ( host , host - > req - > stop , 0 ) ) {
mxcmci_finish_request ( host , host - > req ) ;
return ;
}
} else {
mxcmci_finish_request ( host , host - > req ) ;
}
}
static void mxcmci_data_done ( struct mxcmci_host * host , unsigned int stat )
{
2013-04-09 01:28:05 +04:00
struct mmc_request * req ;
2009-01-06 19:04:14 +03:00
int data_error ;
2013-04-09 01:28:05 +04:00
unsigned long flags ;
spin_lock_irqsave ( & host - > lock , flags ) ;
2009-01-06 19:04:14 +03:00
2013-04-09 01:28:05 +04:00
if ( ! host - > data ) {
spin_unlock_irqrestore ( & host - > lock , flags ) ;
2009-01-06 19:04:14 +03:00
return ;
2013-04-09 01:28:05 +04:00
}
if ( ! host - > req ) {
spin_unlock_irqrestore ( & host - > lock , flags ) ;
return ;
}
req = host - > req ;
if ( ! req - > stop )
host - > req = NULL ; /* we will handle finish req below */
2009-01-06 19:04:14 +03:00
data_error = mxcmci_finish_data ( host , stat ) ;
2013-04-09 01:28:05 +04:00
spin_unlock_irqrestore ( & host - > lock , flags ) ;
2009-01-06 19:04:14 +03:00
mxcmci_read_response ( host , stat ) ;
host - > cmd = NULL ;
2013-04-09 01:28:05 +04:00
if ( req - > stop ) {
if ( mxcmci_start_cmd ( host , req - > stop , 0 ) ) {
mxcmci_finish_request ( host , req ) ;
2009-01-06 19:04:14 +03:00
return ;
}
} else {
2013-04-09 01:28:05 +04:00
mxcmci_finish_request ( host , req ) ;
2009-01-06 19:04:14 +03:00
}
}
static void mxcmci_cmd_done ( struct mxcmci_host * host , unsigned int stat )
{
mxcmci_read_response ( host , stat ) ;
host - > cmd = NULL ;
if ( ! host - > data & & host - > req ) {
mxcmci_finish_request ( host , host - > req ) ;
return ;
}
/* For the DMA case the DMA engine handles the data transfer
2009-07-16 19:13:03 +04:00
* automatically . For non DMA we have to do it ourselves .
2009-01-06 19:04:14 +03:00
* Don ' t do it in interrupt context though .
*/
if ( ! mxcmci_use_dma ( host ) & & host - > data )
schedule_work ( & host - > datawork ) ;
}
static irqreturn_t mxcmci_irq ( int irq , void * devid )
{
struct mxcmci_host * host = devid ;
2010-04-01 12:03:24 +04:00
unsigned long flags ;
bool sdio_irq ;
2009-01-06 19:04:14 +03:00
u32 stat ;
2013-04-09 01:28:06 +04:00
stat = mxcmci_readl ( host , MMC_REG_STATUS ) ;
mxcmci_writel ( host ,
stat & ~ ( STATUS_SDIO_INT_ACTIVE | STATUS_DATA_TRANS_DONE |
STATUS_WRITE_OP_DONE ) ,
MMC_REG_STATUS ) ;
2009-01-06 19:04:14 +03:00
dev_dbg ( mmc_dev ( host - > mmc ) , " %s: 0x%08x \n " , __func__ , stat ) ;
2010-04-01 12:03:24 +04:00
spin_lock_irqsave ( & host - > lock , flags ) ;
sdio_irq = ( stat & STATUS_SDIO_INT_ACTIVE ) & & host - > use_sdio ;
spin_unlock_irqrestore ( & host - > lock , flags ) ;
2010-04-27 14:24:42 +04:00
if ( mxcmci_use_dma ( host ) & &
( stat & ( STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE ) ) )
2013-04-09 01:28:06 +04:00
mxcmci_writel ( host , STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE ,
MMC_REG_STATUS ) ;
2010-04-27 14:24:42 +04:00
2010-04-01 12:03:24 +04:00
if ( sdio_irq ) {
2013-04-09 01:28:06 +04:00
mxcmci_writel ( host , STATUS_SDIO_INT_ACTIVE , MMC_REG_STATUS ) ;
2010-04-01 12:03:24 +04:00
mmc_signal_sdio_irq ( host - > mmc ) ;
}
2009-01-06 19:04:14 +03:00
if ( stat & STATUS_END_CMD_RESP )
mxcmci_cmd_done ( host , stat ) ;
2010-04-01 12:03:24 +04:00
2009-01-06 19:04:14 +03:00
if ( mxcmci_use_dma ( host ) & &
2012-09-07 14:43:37 +04:00
( stat & ( STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE ) ) ) {
del_timer ( & host - > watchdog ) ;
2009-01-06 19:04:14 +03:00
mxcmci_data_done ( host , stat ) ;
2012-09-07 14:43:37 +04:00
}
2011-02-18 12:21:09 +03:00
2010-05-19 20:46:04 +04:00
if ( host - > default_irq_mask & &
( stat & ( STATUS_CARD_INSERTION | STATUS_CARD_REMOVAL ) ) )
mmc_detect_change ( host - > mmc , msecs_to_jiffies ( 200 ) ) ;
2011-02-18 12:21:09 +03:00
2009-01-06 19:04:14 +03:00
return IRQ_HANDLED ;
}
static void mxcmci_request ( struct mmc_host * mmc , struct mmc_request * req )
{
struct mxcmci_host * host = mmc_priv ( mmc ) ;
unsigned int cmdat = host - > cmdat ;
2009-04-17 00:00:41 +04:00
int error ;
2009-01-06 19:04:14 +03:00
WARN_ON ( host - > req ! = NULL ) ;
host - > req = req ;
host - > cmdat & = ~ CMD_DAT_CONT_INIT ;
2011-02-18 12:21:09 +03:00
if ( host - > dma )
host - > do_dma = 1 ;
2009-01-06 19:04:14 +03:00
if ( req - > data ) {
2009-04-17 00:00:41 +04:00
error = mxcmci_setup_data ( host , req - > data ) ;
if ( error ) {
req - > cmd - > error = error ;
goto out ;
}
2009-01-06 19:04:14 +03:00
cmdat | = CMD_DAT_CONT_DATA_ENABLE ;
if ( req - > data - > flags & MMC_DATA_WRITE )
cmdat | = CMD_DAT_CONT_WRITE ;
}
2009-04-17 00:00:41 +04:00
error = mxcmci_start_cmd ( host , req - > cmd , cmdat ) ;
2011-02-18 12:21:09 +03:00
2009-04-17 00:00:41 +04:00
out :
if ( error )
2009-01-06 19:04:14 +03:00
mxcmci_finish_request ( host , req ) ;
}
static void mxcmci_set_clk_rate ( struct mxcmci_host * host , unsigned int clk_ios )
{
unsigned int divider ;
int prescaler = 0 ;
2012-03-07 12:31:14 +04:00
unsigned int clk_in = clk_get_rate ( host - > clk_per ) ;
2009-01-06 19:04:14 +03:00
while ( prescaler < = 0x800 ) {
for ( divider = 1 ; divider < = 0xF ; divider + + ) {
int x ;
x = ( clk_in / ( divider + 1 ) ) ;
if ( prescaler )
x / = ( prescaler * 2 ) ;
if ( x < = clk_ios )
break ;
}
if ( divider < 0x10 )
break ;
if ( prescaler = = 0 )
prescaler = 1 ;
else
prescaler < < = 1 ;
}
2013-04-09 01:28:06 +04:00
mxcmci_writew ( host , ( prescaler < < 4 ) | divider , MMC_REG_CLK_RATE ) ;
2009-01-06 19:04:14 +03:00
dev_dbg ( mmc_dev ( host - > mmc ) , " scaler: %d divider: %d in: %d out: %d \n " ,
prescaler , divider , clk_in , clk_ios ) ;
}
2011-02-18 12:21:09 +03:00
static int mxcmci_setup_dma ( struct mmc_host * mmc )
{
struct mxcmci_host * host = mmc_priv ( mmc ) ;
struct dma_slave_config * config = & host - > dma_slave_config ;
2014-03-22 12:52:45 +04:00
config - > dst_addr = host - > phys_base + MMC_REG_BUFFER_ACCESS ;
config - > src_addr = host - > phys_base + MMC_REG_BUFFER_ACCESS ;
2011-02-18 12:21:09 +03:00
config - > dst_addr_width = 4 ;
config - > src_addr_width = 4 ;
config - > dst_maxburst = host - > burstlen ;
config - > src_maxburst = host - > burstlen ;
2012-02-01 14:42:19 +04:00
config - > device_fc = false ;
2011-02-18 12:21:09 +03:00
return dmaengine_slave_config ( host - > dma , config ) ;
}
2009-01-06 19:04:14 +03:00
static void mxcmci_set_ios ( struct mmc_host * mmc , struct mmc_ios * ios )
{
struct mxcmci_host * host = mmc_priv ( mmc ) ;
2011-02-18 12:21:09 +03:00
int burstlen , ret ;
2009-01-06 19:04:14 +03:00
/*
2011-07-06 13:18:33 +04:00
* use burstlen of 64 ( 16 words ) in 4 bit mode ( - - > reg value 0 )
* use burstlen of 16 ( 4 words ) in 1 bit mode ( - - > reg value 16 )
2009-01-06 19:04:14 +03:00
*/
if ( ios - > bus_width = = MMC_BUS_WIDTH_4 )
2011-02-18 12:21:09 +03:00
burstlen = 16 ;
2011-07-06 13:18:33 +04:00
else
burstlen = 4 ;
2011-02-18 12:21:09 +03:00
if ( mxcmci_use_dma ( host ) & & burstlen ! = host - > burstlen ) {
host - > burstlen = burstlen ;
ret = mxcmci_setup_dma ( mmc ) ;
if ( ret ) {
dev_err ( mmc_dev ( host - > mmc ) ,
" failed to config DMA channel. Falling back to PIO \n " ) ;
dma_release_channel ( host - > dma ) ;
host - > do_dma = 0 ;
2011-11-11 19:28:05 +04:00
host - > dma = NULL ;
2011-02-18 12:21:09 +03:00
}
}
2009-01-06 19:04:14 +03:00
if ( ios - > bus_width = = MMC_BUS_WIDTH_4 )
host - > cmdat | = CMD_DAT_CONT_BUS_WIDTH_4 ;
else
host - > cmdat & = ~ CMD_DAT_CONT_BUS_WIDTH_4 ;
if ( host - > power_mode ! = ios - > power_mode ) {
2010-11-09 13:35:37 +03:00
mxcmci_set_power ( host , ios - > power_mode , ios - > vdd ) ;
2009-01-06 19:04:14 +03:00
host - > power_mode = ios - > power_mode ;
2010-11-02 03:05:37 +03:00
2009-01-06 19:04:14 +03:00
if ( ios - > power_mode = = MMC_POWER_ON )
host - > cmdat | = CMD_DAT_CONT_INIT ;
}
if ( ios - > clock ) {
mxcmci_set_clk_rate ( host , ios - > clock ) ;
2013-04-09 01:28:06 +04:00
mxcmci_writew ( host , STR_STP_CLK_START_CLK , MMC_REG_STR_STP_CLK ) ;
2009-01-06 19:04:14 +03:00
} else {
2013-04-09 01:28:06 +04:00
mxcmci_writew ( host , STR_STP_CLK_STOP_CLK , MMC_REG_STR_STP_CLK ) ;
2009-01-06 19:04:14 +03:00
}
host - > clock = ios - > clock ;
}
static irqreturn_t mxcmci_detect_irq ( int irq , void * data )
{
struct mmc_host * mmc = data ;
dev_dbg ( mmc_dev ( mmc ) , " %s \n " , __func__ ) ;
mmc_detect_change ( mmc , msecs_to_jiffies ( 250 ) ) ;
return IRQ_HANDLED ;
}
static int mxcmci_get_ro ( struct mmc_host * mmc )
{
struct mxcmci_host * host = mmc_priv ( mmc ) ;
if ( host - > pdata & & host - > pdata - > get_ro )
return ! ! host - > pdata - > get_ro ( mmc_dev ( mmc ) ) ;
/*
2013-04-09 01:28:07 +04:00
* If board doesn ' t support read only detection ( no mmc_gpio
* context or gpio is invalid ) , then let the mmc core decide
* what to do .
2009-01-06 19:04:14 +03:00
*/
2013-04-09 01:28:07 +04:00
return mmc_gpio_get_ro ( mmc ) ;
2009-01-06 19:04:14 +03:00
}
2010-04-01 12:03:24 +04:00
static void mxcmci_enable_sdio_irq ( struct mmc_host * mmc , int enable )
{
struct mxcmci_host * host = mmc_priv ( mmc ) ;
unsigned long flags ;
u32 int_cntr ;
spin_lock_irqsave ( & host - > lock , flags ) ;
host - > use_sdio = enable ;
2013-04-09 01:28:06 +04:00
int_cntr = mxcmci_readl ( host , MMC_REG_INT_CNTR ) ;
2010-04-01 12:03:24 +04:00
if ( enable )
int_cntr | = INT_SDIO_IRQ_EN ;
else
int_cntr & = ~ INT_SDIO_IRQ_EN ;
2013-04-09 01:28:06 +04:00
mxcmci_writel ( host , int_cntr , MMC_REG_INT_CNTR ) ;
2010-04-01 12:03:24 +04:00
spin_unlock_irqrestore ( & host - > lock , flags ) ;
}
2009-01-06 19:04:14 +03:00
2010-04-01 12:03:25 +04:00
static void mxcmci_init_card ( struct mmc_host * host , struct mmc_card * card )
{
2012-09-16 12:54:30 +04:00
struct mxcmci_host * mxcmci = mmc_priv ( host ) ;
2010-04-01 12:03:25 +04:00
/*
* MX3 SoCs have a silicon bug which corrupts CRC calculation of
* multi - block transfers when connected SDIO peripheral doesn ' t
* drive the BUSY line as required by the specs .
* One way to prevent this is to only allow 1 - bit transfers .
*/
2012-09-16 12:54:30 +04:00
if ( is_imx31_mmc ( mxcmci ) & & card - > type = = MMC_TYPE_SDIO )
2010-04-01 12:03:25 +04:00
host - > caps & = ~ MMC_CAP_4_BIT_DATA ;
else
host - > caps | = MMC_CAP_4_BIT_DATA ;
}
2011-02-18 12:21:09 +03:00
static bool filter ( struct dma_chan * chan , void * param )
{
struct mxcmci_host * host = param ;
if ( ! imx_dma_is_general_purpose ( chan ) )
return false ;
chan - > private = & host - > dma_data ;
return true ;
}
2012-09-07 14:43:37 +04:00
static void mxcmci_watchdog ( unsigned long data )
{
struct mmc_host * mmc = ( struct mmc_host * ) data ;
struct mxcmci_host * host = mmc_priv ( mmc ) ;
struct mmc_request * req = host - > req ;
2013-04-09 01:28:06 +04:00
unsigned int stat = mxcmci_readl ( host , MMC_REG_STATUS ) ;
2012-09-07 14:43:37 +04:00
if ( host - > dma_dir = = DMA_FROM_DEVICE ) {
dmaengine_terminate_all ( host - > dma ) ;
dev_err ( mmc_dev ( host - > mmc ) ,
" %s: read time out (status = 0x%08x) \n " ,
__func__ , stat ) ;
} else {
dev_err ( mmc_dev ( host - > mmc ) ,
" %s: write time out (status = 0x%08x) \n " ,
__func__ , stat ) ;
mxcmci_softreset ( host ) ;
}
/* Mark transfer as erroneus and inform the upper layers */
2013-04-09 01:28:05 +04:00
if ( host - > data )
host - > data - > error = - ETIMEDOUT ;
2012-09-07 14:43:37 +04:00
host - > req = NULL ;
host - > cmd = NULL ;
host - > data = NULL ;
mmc_request_done ( host - > mmc , req ) ;
}
2009-01-06 19:04:14 +03:00
static const struct mmc_host_ops mxcmci_ops = {
2010-04-01 12:03:24 +04:00
. request = mxcmci_request ,
. set_ios = mxcmci_set_ios ,
. get_ro = mxcmci_get_ro ,
. enable_sdio_irq = mxcmci_enable_sdio_irq ,
2010-04-01 12:03:25 +04:00
. init_card = mxcmci_init_card ,
2009-01-06 19:04:14 +03:00
} ;
static int mxcmci_probe ( struct platform_device * pdev )
{
struct mmc_host * mmc ;
2014-03-22 12:52:45 +04:00
struct mxcmci_host * host ;
struct resource * res ;
2009-01-06 19:04:14 +03:00
int ret = 0 , irq ;
2013-03-31 22:07:38 +04:00
bool dat3_card_detect = false ;
2011-02-18 12:21:09 +03:00
dma_cap_mask_t mask ;
2013-03-31 22:07:38 +04:00
const struct of_device_id * of_id ;
struct imxmmc_platform_data * pdata = pdev - > dev . platform_data ;
2009-01-06 19:04:14 +03:00
2013-04-09 01:28:06 +04:00
pr_info ( " i.MX/MPC512x SDHC driver \n " ) ;
2009-01-06 19:04:14 +03:00
2013-03-31 22:07:38 +04:00
of_id = of_match_device ( mxcmci_of_match , & pdev - > dev ) ;
2014-03-22 12:52:45 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2009-01-06 19:04:14 +03:00
irq = platform_get_irq ( pdev , 0 ) ;
2014-03-22 12:52:45 +04:00
if ( irq < 0 )
2009-01-06 19:04:14 +03:00
return - EINVAL ;
2014-03-22 12:52:45 +04:00
mmc = mmc_alloc_host ( sizeof ( * host ) , & pdev - > dev ) ;
if ( ! mmc )
return - ENOMEM ;
2009-01-06 19:04:14 +03:00
2014-03-22 12:52:45 +04:00
host = mmc_priv ( mmc ) ;
host - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( host - > base ) ) {
ret = PTR_ERR ( host - > base ) ;
goto out_free ;
2009-01-06 19:04:14 +03:00
}
2014-03-22 12:52:45 +04:00
host - > phys_base = res - > start ;
2013-06-10 00:14:14 +04:00
ret = mmc_of_parse ( mmc ) ;
if ( ret )
goto out_free ;
2009-01-06 19:04:14 +03:00
mmc - > ops = & mxcmci_ops ;
2013-03-31 22:07:38 +04:00
/* For devicetree parsing, the bus width is read from devicetree */
if ( pdata )
mmc - > caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ ;
else
mmc - > caps | = MMC_CAP_SDIO_IRQ ;
2009-01-06 19:04:14 +03:00
/* MMC core transfer sizes tunable parameters */
mmc - > max_blk_size = 2048 ;
mmc - > max_blk_count = 65535 ;
mmc - > max_req_size = mmc - > max_blk_size * mmc - > max_blk_count ;
2010-03-11 02:23:57 +03:00
mmc - > max_seg_size = mmc - > max_req_size ;
2009-01-06 19:04:14 +03:00
2013-03-31 22:07:38 +04:00
if ( of_id ) {
const struct platform_device_id * id_entry = of_id - > data ;
host - > devtype = id_entry - > driver_data ;
} else {
host - > devtype = pdev - > id_entry - > driver_data ;
}
2013-04-09 01:28:09 +04:00
/* adjust max_segs after devtype detection */
if ( ! is_mpc512x_mmc ( host ) )
mmc - > max_segs = 64 ;
2009-01-06 19:04:14 +03:00
host - > mmc = mmc ;
2013-03-31 22:07:38 +04:00
host - > pdata = pdata ;
2010-04-01 12:03:24 +04:00
spin_lock_init ( & host - > lock ) ;
2009-01-06 19:04:14 +03:00
2013-03-31 22:07:38 +04:00
if ( pdata )
dat3_card_detect = pdata - > dat3_card_detect ;
else if ( ! ( mmc - > caps & MMC_CAP_NONREMOVABLE )
& & ! of_property_read_bool ( pdev - > dev . of_node , " cd-gpios " ) )
dat3_card_detect = true ;
2010-11-02 03:05:37 +03:00
mxcmci_init_ocr ( host ) ;
2009-01-06 19:04:14 +03:00
2013-03-31 22:07:38 +04:00
if ( dat3_card_detect )
2010-05-19 20:46:04 +04:00
host - > default_irq_mask =
INT_CARD_INSERTION_EN | INT_CARD_REMOVAL_EN ;
else
host - > default_irq_mask = 0 ;
2012-03-07 12:31:14 +04:00
host - > clk_ipg = devm_clk_get ( & pdev - > dev , " ipg " ) ;
if ( IS_ERR ( host - > clk_ipg ) ) {
ret = PTR_ERR ( host - > clk_ipg ) ;
2014-03-22 12:52:45 +04:00
goto out_free ;
2009-01-06 19:04:14 +03:00
}
2012-03-07 12:31:14 +04:00
host - > clk_per = devm_clk_get ( & pdev - > dev , " per " ) ;
if ( IS_ERR ( host - > clk_per ) ) {
ret = PTR_ERR ( host - > clk_per ) ;
2014-03-22 12:52:45 +04:00
goto out_free ;
2012-03-07 12:31:14 +04:00
}
clk_prepare_enable ( host - > clk_per ) ;
clk_prepare_enable ( host - > clk_ipg ) ;
2009-01-06 19:04:14 +03:00
mxcmci_softreset ( host ) ;
2013-04-09 01:28:06 +04:00
host - > rev_no = mxcmci_readw ( host , MMC_REG_REV_NO ) ;
2009-01-06 19:04:14 +03:00
if ( host - > rev_no ! = 0x400 ) {
ret = - ENODEV ;
dev_err ( mmc_dev ( host - > mmc ) , " wrong rev.no. 0x%08x. aborting. \n " ,
host - > rev_no ) ;
goto out_clk_put ;
}
2012-03-07 12:31:14 +04:00
mmc - > f_min = clk_get_rate ( host - > clk_per ) > > 16 ;
mmc - > f_max = clk_get_rate ( host - > clk_per ) > > 1 ;
2009-01-06 19:04:14 +03:00
/* recommended in data sheet */
2013-04-09 01:28:06 +04:00
mxcmci_writew ( host , 0x2db4 , MMC_REG_READ_TO ) ;
2009-01-06 19:04:14 +03:00
2013-04-09 01:28:06 +04:00
mxcmci_writel ( host , host - > default_irq_mask , MMC_REG_INT_CNTR ) ;
2009-01-06 19:04:14 +03:00
2013-03-31 22:07:38 +04:00
if ( ! host - > pdata ) {
host - > dma = dma_request_slave_channel ( & pdev - > dev , " rx-tx " ) ;
} else {
2014-03-22 12:52:45 +04:00
res = platform_get_resource ( pdev , IORESOURCE_DMA , 0 ) ;
if ( res ) {
host - > dmareq = res - > start ;
2013-03-31 22:07:38 +04:00
host - > dma_data . peripheral_type = IMX_DMATYPE_SDHC ;
host - > dma_data . priority = DMA_PRIO_LOW ;
host - > dma_data . dma_request = host - > dmareq ;
dma_cap_zero ( mask ) ;
dma_cap_set ( DMA_SLAVE , mask ) ;
host - > dma = dma_request_channel ( mask , filter , host ) ;
}
2011-02-18 12:21:09 +03:00
}
2013-03-31 22:07:38 +04:00
if ( host - > dma )
mmc - > max_seg_size = dma_get_max_seg_size (
host - > dma - > device - > dev ) ;
else
2011-02-18 12:21:09 +03:00
dev_info ( mmc_dev ( host - > mmc ) , " dma not available. Using PIO \n " ) ;
2009-01-06 19:04:14 +03:00
INIT_WORK ( & host - > datawork , mxcmci_datawork ) ;
2014-03-22 12:52:45 +04:00
ret = devm_request_irq ( & pdev - > dev , irq , mxcmci_irq , 0 ,
dev_name ( & pdev - > dev ) , host ) ;
2009-01-06 19:04:14 +03:00
if ( ret )
goto out_free_dma ;
platform_set_drvdata ( pdev , mmc ) ;
if ( host - > pdata & & host - > pdata - > init ) {
ret = host - > pdata - > init ( & pdev - > dev , mxcmci_detect_irq ,
host - > mmc ) ;
if ( ret )
2014-03-22 12:52:45 +04:00
goto out_free_dma ;
2009-01-06 19:04:14 +03:00
}
2012-09-07 14:43:37 +04:00
init_timer ( & host - > watchdog ) ;
host - > watchdog . function = & mxcmci_watchdog ;
host - > watchdog . data = ( unsigned long ) mmc ;
2013-04-06 10:13:31 +04:00
mmc_add_host ( mmc ) ;
2009-01-06 19:04:14 +03:00
return 0 ;
out_free_dma :
2011-02-18 12:21:09 +03:00
if ( host - > dma )
dma_release_channel ( host - > dma ) ;
2014-03-22 12:52:45 +04:00
2009-01-06 19:04:14 +03:00
out_clk_put :
2012-03-07 12:31:14 +04:00
clk_disable_unprepare ( host - > clk_per ) ;
clk_disable_unprepare ( host - > clk_ipg ) ;
2014-03-22 12:52:45 +04:00
2009-01-06 19:04:14 +03:00
out_free :
mmc_free_host ( mmc ) ;
2014-03-22 12:52:45 +04:00
2009-01-06 19:04:14 +03:00
return ret ;
}
static int mxcmci_remove ( struct platform_device * pdev )
{
struct mmc_host * mmc = platform_get_drvdata ( pdev ) ;
struct mxcmci_host * host = mmc_priv ( mmc ) ;
mmc_remove_host ( mmc ) ;
if ( host - > pdata & & host - > pdata - > exit )
host - > pdata - > exit ( & pdev - > dev , mmc ) ;
2011-02-18 12:21:09 +03:00
if ( host - > dma )
dma_release_channel ( host - > dma ) ;
2012-03-07 12:31:14 +04:00
clk_disable_unprepare ( host - > clk_per ) ;
clk_disable_unprepare ( host - > clk_ipg ) ;
2009-01-06 19:04:14 +03:00
mmc_free_host ( mmc ) ;
return 0 ;
}
# ifdef CONFIG_PM
2010-06-17 22:59:07 +04:00
static int mxcmci_suspend ( struct device * dev )
2009-01-06 19:04:14 +03:00
{
2010-06-17 22:59:07 +04:00
struct mmc_host * mmc = dev_get_drvdata ( dev ) ;
struct mxcmci_host * host = mmc_priv ( mmc ) ;
2009-01-06 19:04:14 +03:00
2012-03-07 12:31:14 +04:00
clk_disable_unprepare ( host - > clk_per ) ;
clk_disable_unprepare ( host - > clk_ipg ) ;
2013-09-25 17:33:01 +04:00
return 0 ;
2009-01-06 19:04:14 +03:00
}
2010-06-17 22:59:07 +04:00
static int mxcmci_resume ( struct device * dev )
2009-01-06 19:04:14 +03:00
{
2010-06-17 22:59:07 +04:00
struct mmc_host * mmc = dev_get_drvdata ( dev ) ;
struct mxcmci_host * host = mmc_priv ( mmc ) ;
2009-01-06 19:04:14 +03:00
2012-03-07 12:31:14 +04:00
clk_prepare_enable ( host - > clk_per ) ;
clk_prepare_enable ( host - > clk_ipg ) ;
2013-09-25 17:33:01 +04:00
return 0 ;
2009-01-06 19:04:14 +03:00
}
2010-06-17 22:59:07 +04:00
static const struct dev_pm_ops mxcmci_pm_ops = {
. suspend = mxcmci_suspend ,
. resume = mxcmci_resume ,
} ;
# endif
2009-01-06 19:04:14 +03:00
static struct platform_driver mxcmci_driver = {
. probe = mxcmci_probe ,
. remove = mxcmci_remove ,
2012-09-16 12:54:30 +04:00
. id_table = mxcmci_devtype ,
2009-01-06 19:04:14 +03:00
. driver = {
. name = DRIVER_NAME ,
. owner = THIS_MODULE ,
2010-06-17 22:59:07 +04:00
# ifdef CONFIG_PM
. pm = & mxcmci_pm_ops ,
# endif
2013-03-31 22:07:38 +04:00
. of_match_table = mxcmci_of_match ,
2009-01-06 19:04:14 +03:00
}
} ;
2011-11-26 08:55:43 +04:00
module_platform_driver ( mxcmci_driver ) ;
2009-01-06 19:04:14 +03:00
MODULE_DESCRIPTION ( " i.MX Multimedia Card Interface Driver " ) ;
MODULE_AUTHOR ( " Sascha Hauer, Pengutronix " ) ;
MODULE_LICENSE ( " GPL " ) ;
2012-10-09 23:26:05 +04:00
MODULE_ALIAS ( " platform:mxc-mmc " ) ;