mirror of
https://github.com/hardkernel/linux.git
synced 2026-04-15 01:50:40 +09:00
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:
@@ -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 */
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user