mmc: dw_mmc: rework the code related to cmd/data completion

Main change corresponds to dw_mci_command_complete().  And EBE is
divided into read and write.  Some minor changes for code readability.

Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
Tested-by: Alim Akhtar <alim.akhtar@samsung.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
This commit is contained in:
Seungwon Jeon 2013-08-31 00:14:17 +09:00 committed by Chris Ball
parent 90c2143a8f
commit e352c81311
1 changed files with 84 additions and 63 deletions

View File

@ -845,7 +845,9 @@ static void __dw_mci_start_request(struct dw_mci *host,
host->pending_events = 0;
host->completed_events = 0;
host->cmd_status = 0;
host->data_status = 0;
host->dir_status = 0;
data = cmd->data;
if (data) {
@ -1157,7 +1159,7 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
spin_lock(&host->lock);
}
static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd)
static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd)
{
u32 status = host->cmd_status;
@ -1192,6 +1194,57 @@ static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd
if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
mdelay(20);
}
return cmd->error;
}
static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)
{
u32 status = host->data_status, ctrl;
if (status & DW_MCI_DATA_ERROR_FLAGS) {
if (status & SDMMC_INT_DRTO) {
data->error = -ETIMEDOUT;
} else if (status & SDMMC_INT_DCRC) {
data->error = -EILSEQ;
} else if (status & SDMMC_INT_EBE) {
if (host->dir_status ==
DW_MCI_SEND_STATUS) {
/*
* No data CRC status was returned.
* The number of bytes transferred
* will be exaggerated in PIO mode.
*/
data->bytes_xfered = 0;
data->error = -ETIMEDOUT;
} else if (host->dir_status ==
DW_MCI_RECV_STATUS) {
data->error = -EIO;
}
} else {
/* SDMMC_INT_SBE is included */
data->error = -EIO;
}
dev_err(host->dev, "data error, status 0x%08x\n", status);
/*
* After an error, there may be data lingering
* in the FIFO, so reset it - doing so
* generates a block interrupt, hence setting
* the scatter-gather pointer to NULL.
*/
sg_miter_stop(&host->sg_miter);
host->sg = NULL;
ctrl = mci_readl(host, CTRL);
ctrl |= SDMMC_CTRL_FIFO_RESET;
mci_writel(host, CTRL, ctrl);
} else {
data->bytes_xfered = data->blocks * data->blksz;
data->error = 0;
}
return data->error;
}
static void dw_mci_tasklet_func(unsigned long priv)
@ -1199,14 +1252,17 @@ static void dw_mci_tasklet_func(unsigned long priv)
struct dw_mci *host = (struct dw_mci *)priv;
struct mmc_data *data;
struct mmc_command *cmd;
struct mmc_request *mrq;
enum dw_mci_state state;
enum dw_mci_state prev_state;
u32 status, ctrl;
u32 ctrl;
unsigned int err;
spin_lock(&host->lock);
state = host->state;
data = host->data;
mrq = host->mrq;
do {
prev_state = state;
@ -1223,23 +1279,23 @@ static void dw_mci_tasklet_func(unsigned long priv)
cmd = host->cmd;
host->cmd = NULL;
set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
dw_mci_command_complete(host, cmd);
if (cmd == host->mrq->sbc && !cmd->error) {
err = dw_mci_command_complete(host, cmd);
if (cmd == mrq->sbc && !err) {
prev_state = state = STATE_SENDING_CMD;
__dw_mci_start_request(host, host->cur_slot,
host->mrq->cmd);
mrq->cmd);
goto unlock;
}
if (cmd->data && cmd->error) {
if (cmd->data && err) {
dw_mci_stop_dma(host);
send_stop_abort(host, data);
state = STATE_SENDING_STOP;
break;
}
if (!host->mrq->data || cmd->error) {
dw_mci_request_end(host, host->mrq);
if (!cmd->data || err) {
dw_mci_request_end(host, mrq);
goto unlock;
}
@ -1270,62 +1326,27 @@ static void dw_mci_tasklet_func(unsigned long priv)
host->data = NULL;
set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
status = host->data_status;
err = dw_mci_data_complete(host, data);
if (status & DW_MCI_DATA_ERROR_FLAGS) {
if (status & SDMMC_INT_DRTO) {
data->error = -ETIMEDOUT;
} else if (status & SDMMC_INT_DCRC) {
data->error = -EILSEQ;
} else if (status & SDMMC_INT_EBE &&
host->dir_status ==
DW_MCI_SEND_STATUS) {
/*
* No data CRC status was returned.
* The number of bytes transferred will
* be exaggerated in PIO mode.
*/
data->bytes_xfered = 0;
data->error = -ETIMEDOUT;
} else {
dev_err(host->dev,
"data FIFO error "
"(status=%08x)\n",
status);
data->error = -EIO;
if (!err) {
if (!data->stop || mrq->sbc) {
if (mrq->sbc)
data->stop->error = 0;
dw_mci_request_end(host, mrq);
goto unlock;
}
/*
* After an error, there may be data lingering
* in the FIFO, so reset it - doing so
* generates a block interrupt, hence setting
* the scatter-gather pointer to NULL.
*/
sg_miter_stop(&host->sg_miter);
host->sg = NULL;
ctrl = mci_readl(host, CTRL);
ctrl |= SDMMC_CTRL_FIFO_RESET;
mci_writel(host, CTRL, ctrl);
} else {
data->bytes_xfered = data->blocks * data->blksz;
data->error = 0;
}
if (!data->stop && !data->error) {
dw_mci_request_end(host, host->mrq);
goto unlock;
}
if (host->mrq->sbc && !data->error) {
data->stop->error = 0;
dw_mci_request_end(host, host->mrq);
goto unlock;
}
prev_state = state = STATE_SENDING_STOP;
if (data->stop && !data->error) {
/* stop command for open-ended transfer*/
send_stop_abort(host, data);
if (data->stop)
send_stop_abort(host, data);
}
/*
* If err has non-zero,
* stop-abort command has been already issued.
*/
prev_state = state = STATE_SENDING_STOP;
/* fall through */
case STATE_SENDING_STOP:
@ -1334,7 +1355,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
break;
/* CMD error in data command */
if (host->mrq->cmd->error && host->mrq->data) {
if (mrq->cmd->error && mrq->data) {
sg_miter_stop(&host->sg_miter);
host->sg = NULL;
ctrl = mci_readl(host, CTRL);
@ -1345,12 +1366,12 @@ static void dw_mci_tasklet_func(unsigned long priv)
host->cmd = NULL;
host->data = NULL;
if (host->mrq->stop)
dw_mci_command_complete(host, host->mrq->stop);
if (mrq->stop)
dw_mci_command_complete(host, mrq->stop);
else
host->cmd_status = 0;
dw_mci_request_end(host, host->mrq);
dw_mci_request_end(host, mrq);
goto unlock;
case STATE_DATA_ERROR: