mmc: host: rk_sdmmc: add data-over-int timeout for data_busy

If devices fall in bad state make data_busy be hold too long,
dto interrupt will not be present, which leading  mmc-core wait for it forever.

 [<c06d42e4>] (__schedule+0x48c/0x554) from [<c06d275c>] (schedule_timeout+0x1c/0x260)
 [<c06d275c>] (schedule_timeout+0x1c/0x260) from [<c06d3c10>] (wait_for_common+0xd0/0x164)
 [<c06d3c10>] (wait_for_common+0xd0/0x164) from [<c044a4bc>] (mmc_wait_for_req_done+0x1c/0xe0)
 [<c044a4bc>] (mmc_wait_for_req_done+0x1c/0xe0) from [<c0453730>] (mmc_io_rw_extended+0x218/0x294)
 [<c0453730>] (mmc_io_rw_extended+0x218/0x294) from [<c0454590>] (sdio_io_rw_ext_helper+0xc8/0x194)
 [<c0454590>] (sdio_io_rw_ext_helper+0xc8/0x194) from [<c04546b0>] (sdio_memcpy_toio+0x1c/0x20)
 [<c04546b0>] (sdio_memcpy_toio+0x1c/0x20) from [<c032e96c>] (sdioh_request_packet+0x664/0x7e8)
 [<c032e96c>] (sdioh_request_packet+0x664/0x7e8) from [<c032fc78>] (sdioh_request_buffer+0x1a8/0x210)
This commit is contained in:
lintao
2014-06-24 17:20:42 +08:00
parent 42573d84b2
commit fbfdff1f46
2 changed files with 117 additions and 28 deletions

View File

@@ -1790,7 +1790,8 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
struct mmc_host *prev_mmc = host->cur_slot->mmc;
WARN_ON(host->cmd || host->data);
del_timer_sync(&host->dto_timer);
dw_mci_deal_data_end(host, mrq);
if(mrq->cmd)
@@ -1851,19 +1852,23 @@ static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd
{
if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO)
host->cmd_rto += 1;
cmd->error = -ETIMEDOUT;
}
else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC))
cmd->error = -ETIMEDOUT;
del_timer_sync(&host->dto_timer);
}else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC)){
del_timer_sync(&host->dto_timer);
cmd->error = -EILSEQ;
else if (status & SDMMC_INT_RESP_ERR)
}else if (status & SDMMC_INT_RESP_ERR){
del_timer_sync(&host->dto_timer);
cmd->error = -EIO;
else
}else{
cmd->error = 0;
}
MMC_DBG_CMD_FUNC(host->mmc, " command complete, cmd=%d,cmdError=%d [%s]",
cmd->opcode, cmd->error,mmc_hostname(host->mmc));
if (cmd->error) {
del_timer_sync(&host->dto_timer);
if(MMC_SEND_STATUS != cmd->opcode)
if(host->cmd_rto >= SDMMC_CMD_RTO_MAX_HOLD){
MMC_DBG_ERR_FUNC(host->mmc, " command complete, cmd=%d,cmdError=%d [%s]",\
@@ -1918,21 +1923,23 @@ static void dw_mci_tasklet_func(unsigned long priv)
goto unlock;
}
if (cmd->data && cmd->error) {
if (cmd->data && cmd->error) {
del_timer_sync(&host->dto_timer); /* delete the timer for INT_DTO */
dw_mci_stop_dma(host);
#if 1
if (data->stop) {
send_stop_cmd(host, data);
state = STATE_SENDING_STOP;
break;
} else {
host->data = NULL;
}
if (data->stop) {
send_stop_cmd(host, data);
state = STATE_SENDING_STOP;
break;
}else{
host->data = NULL;
}
#else
send_stop_abort(host, data);
state = STATE_SENDING_STOP;
break;
#endif
set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
}
if (!host->mrq->data || cmd->error) {
@@ -2004,6 +2011,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
break;
dw_mci_deal_data_end(host, host->mrq);
del_timer_sync(&host->dto_timer); //delete the timer for INT_DTO
MMC_DBG_INFO_FUNC(host->mmc,
"Pre-state[%d]-->NowState[%d]: STATE_DATA_BUSY, after EVENT_DATA_COMPLETE. [%s]", \
prev_state,state,mmc_hostname(host->mmc));
@@ -2548,12 +2556,27 @@ host_put:
static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
{
if (!host->cmd_status)
u32 multi, unit;
if (!host->cmd_status)
host->cmd_status = status;
smp_wmb();
if(!host->cmd)
goto cmd_exit;
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
if((MMC_STOP_TRANSMISSION != host->cmd->opcode))
{
unit = 2*1024*1024;
multi = mci_readl(host, BYTCNT)/unit;
multi += ((mci_readl(host, BYTCNT) % unit) ? 1 :0 );
multi = (multi > 0) ? multi : 1;
multi += (host->cmd->retries > 2)? 2 : host->cmd->retries;
mod_timer(&host->dto_timer, jiffies + msecs_to_jiffies(4500 * multi));//max wait 8s larger
}
cmd_exit:
smp_wmb();
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
tasklet_schedule(&host->tasklet);
}
@@ -2564,15 +2587,13 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
int i;
pending = mci_readl(host, MINTSTS); /* read-only mask reg */
//if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD)
// printk("%s pending: 0x%08x\n",__func__,pending);
/*
* DTO fix - version 2.10a and below, and only if internal DMA
* is configured.
*/
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
if (!pending &&
((mci_readl(host, STATUS) >> 17) & 0x1fff))
* DTO fix - version 2.10a and below, and only if internal DMA
* is configured.
*/
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
if (!pending &&((mci_readl(host, STATUS) >> 17) & 0x1fff))
pending |= SDMMC_INT_DATA_OVER;
}
@@ -2601,6 +2622,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & SDMMC_INT_DATA_OVER) {
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
del_timer(&host->dto_timer); /* delete the timer for INT_DTO */
MMC_DBG_CMD_FUNC(host->mmc, "SDMMC_INT_DATA_OVER, INT-pending=0x%x. [%s]",pending,mmc_hostname(host->mmc));
if (!host->data_status)
host->data_status = pending;
@@ -2701,6 +2723,7 @@ static void dw_mci_work_routine_card(struct work_struct *work)
rk_send_wakeup_key();//wake up system
spin_lock_bh(&host->lock);
del_timer(&host->dto_timer); /* delete the timer for INT_DTO */
/* Card change detected */
slot->last_detect_state = present;
@@ -2744,7 +2767,8 @@ static void dw_mci_work_routine_card(struct work_struct *work)
if (mrq->stop)
mrq->stop->error = -ENOMEDIUM;
MMC_DBG_CMD_FUNC(host->mmc, "dw_mci_work--reqeuest done, cmd=%d [%s]",mrq->cmd->opcode, mmc_hostname(mmc));
MMC_DBG_CMD_FUNC(host->mmc, "dw_mci_work--reqeuest done, cmd=%d [%s]",
mrq->cmd->opcode, mmc_hostname(mmc));
spin_unlock(&host->lock);
mmc_request_done(slot->mmc, mrq);
@@ -2768,7 +2792,7 @@ static void dw_mci_work_routine_card(struct work_struct *work)
}
mmc_detect_change(slot->mmc,
msecs_to_jiffies(host->pdata->detect_delay_ms));
msecs_to_jiffies(host->pdata->detect_delay_ms));
}
}
@@ -3345,6 +3369,67 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
}
#endif /* CONFIG_OF */
static void dw_mci_dealwith_timeout(struct dw_mci *host)
{
u32 ret, i, regs;
switch(host->state){
case STATE_IDLE:
break;
case STATE_SENDING_DATA:
case STATE_DATA_BUSY:
host->data_status |= (SDMMC_INT_DCRC|SDMMC_INT_EBE);
mci_writel(host, RINTSTS, SDMMC_INT_DRTO); // clear interrupt
set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
host->state = STATE_DATA_BUSY;
if (!dw_mci_ctrl_all_reset(host)) {
ret = -ENODEV;
return ;
}
if (host->use_dma && host->dma_ops->init)
host->dma_ops->init(host);
/*
* Restore the initial value at FIFOTH register
* And Invalidate the prev_blksz with zero
*/
mci_writel(host, FIFOTH, host->fifoth_val);
host->prev_blksz = 0;
mci_writel(host, TMOUT, 0xFFFFFFFF);
mci_writel(host, RINTSTS, 0xFFFFFFFF);
regs = SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | SDMMC_INT_TXDR
| SDMMC_INT_RXDR | SDMMC_INT_VSI | DW_MCI_ERROR_FLAGS;
if(!(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO))
regs |= SDMMC_INT_CD;
mci_writel(host, INTMASK, regs);
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE);
for (i = 0; i < host->num_slots; i++) {
struct dw_mci_slot *slot = host->slot[i];
if (!slot)
continue;
if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) {
dw_mci_set_ios(slot->mmc, &slot->mmc->ios);
dw_mci_setup_bus(slot, true);
}
}
mci_writel(host, RINTSTS, 0xFFFFFFFF);
tasklet_schedule(&host->tasklet);
break;
default:
break;
}
}
static void dw_mci_dto_timeout(unsigned long host_data)
{
struct dw_mci *host = (struct dw_mci *) host_data;
disable_irq(host->irq);
host->data_status = SDMMC_INT_EBE;
mci_writel(host, RINTSTS, 0xFFFFFFFF);
dw_mci_dealwith_timeout(host);
enable_irq(host->irq);
}
int dw_mci_probe(struct dw_mci *host)
{
const struct dw_mci_drv_data *drv_data = host->drv_data;
@@ -3517,6 +3602,7 @@ int dw_mci_probe(struct dw_mci *host)
else
host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
setup_timer(&host->dto_timer, dw_mci_dto_timeout, (unsigned long)host);
/* We need at least one slot to succeed */
for (i = 0; i < host->num_slots; i++) {
ret = dw_mci_init_slot(host, i);
@@ -3587,6 +3673,7 @@ void dw_mci_remove(struct dw_mci *host)
{
int i;
del_timer_sync(&host->dto_timer);
mci_writel(host, RINTSTS, 0xFFFFFFFF);
mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */

View File

@@ -16,6 +16,7 @@
#include <linux/scatterlist.h>
#include <linux/mmc/core.h>
#include <linux/timer.h>
#define MAX_MCI_SLOTS 2
@@ -177,6 +178,7 @@ struct dw_mci {
struct mmc_command *pre_cmd;
unsigned int hold_reg_flag;//to fix the hold_reg value
struct timer_list dto_timer; //the timer for INT_DTO
/* FIFO push and pull */
int fifo_depth;
int data_shift;