Commit e352c813 authored by Seungwon Jeon's avatar Seungwon Jeon Committed by Chris Ball

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: default avatarSeungwon Jeon <tgih.jun@samsung.com>
Tested-by: default avatarAlim Akhtar <alim.akhtar@samsung.com>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent 90c2143a
...@@ -845,7 +845,9 @@ static void __dw_mci_start_request(struct dw_mci *host, ...@@ -845,7 +845,9 @@ static void __dw_mci_start_request(struct dw_mci *host,
host->pending_events = 0; host->pending_events = 0;
host->completed_events = 0; host->completed_events = 0;
host->cmd_status = 0;
host->data_status = 0; host->data_status = 0;
host->dir_status = 0;
data = cmd->data; data = cmd->data;
if (data) { if (data) {
...@@ -1157,7 +1159,7 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) ...@@ -1157,7 +1159,7 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
spin_lock(&host->lock); 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; u32 status = host->cmd_status;
...@@ -1192,6 +1194,57 @@ static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd ...@@ -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) if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
mdelay(20); 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) static void dw_mci_tasklet_func(unsigned long priv)
...@@ -1199,14 +1252,17 @@ 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 dw_mci *host = (struct dw_mci *)priv;
struct mmc_data *data; struct mmc_data *data;
struct mmc_command *cmd; struct mmc_command *cmd;
struct mmc_request *mrq;
enum dw_mci_state state; enum dw_mci_state state;
enum dw_mci_state prev_state; enum dw_mci_state prev_state;
u32 status, ctrl; u32 ctrl;
unsigned int err;
spin_lock(&host->lock); spin_lock(&host->lock);
state = host->state; state = host->state;
data = host->data; data = host->data;
mrq = host->mrq;
do { do {
prev_state = state; prev_state = state;
...@@ -1223,23 +1279,23 @@ static void dw_mci_tasklet_func(unsigned long priv) ...@@ -1223,23 +1279,23 @@ static void dw_mci_tasklet_func(unsigned long priv)
cmd = host->cmd; cmd = host->cmd;
host->cmd = NULL; host->cmd = NULL;
set_bit(EVENT_CMD_COMPLETE, &host->completed_events); set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
dw_mci_command_complete(host, cmd); err = dw_mci_command_complete(host, cmd);
if (cmd == host->mrq->sbc && !cmd->error) { if (cmd == mrq->sbc && !err) {
prev_state = state = STATE_SENDING_CMD; prev_state = state = STATE_SENDING_CMD;
__dw_mci_start_request(host, host->cur_slot, __dw_mci_start_request(host, host->cur_slot,
host->mrq->cmd); mrq->cmd);
goto unlock; goto unlock;
} }
if (cmd->data && cmd->error) { if (cmd->data && err) {
dw_mci_stop_dma(host); dw_mci_stop_dma(host);
send_stop_abort(host, data); send_stop_abort(host, data);
state = STATE_SENDING_STOP; state = STATE_SENDING_STOP;
break; break;
} }
if (!host->mrq->data || cmd->error) { if (!cmd->data || err) {
dw_mci_request_end(host, host->mrq); dw_mci_request_end(host, mrq);
goto unlock; goto unlock;
} }
...@@ -1270,62 +1326,27 @@ static void dw_mci_tasklet_func(unsigned long priv) ...@@ -1270,62 +1326,27 @@ static void dw_mci_tasklet_func(unsigned long priv)
host->data = NULL; host->data = NULL;
set_bit(EVENT_DATA_COMPLETE, &host->completed_events); 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;
}
/*
* 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) { if (!err) {
dw_mci_request_end(host, host->mrq); if (!data->stop || mrq->sbc) {
goto unlock; if (mrq->sbc)
} data->stop->error = 0;
dw_mci_request_end(host, mrq);
goto unlock;
}
if (host->mrq->sbc && !data->error) { /* stop command for open-ended transfer*/
data->stop->error = 0; if (data->stop)
dw_mci_request_end(host, host->mrq); send_stop_abort(host, data);
goto unlock;
} }
/*
* If err has non-zero,
* stop-abort command has been already issued.
*/
prev_state = state = STATE_SENDING_STOP; prev_state = state = STATE_SENDING_STOP;
if (data->stop && !data->error) {
/* stop command for open-ended transfer*/
send_stop_abort(host, data);
}
/* fall through */ /* fall through */
case STATE_SENDING_STOP: case STATE_SENDING_STOP:
...@@ -1334,7 +1355,7 @@ static void dw_mci_tasklet_func(unsigned long priv) ...@@ -1334,7 +1355,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
break; break;
/* CMD error in data command */ /* 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); sg_miter_stop(&host->sg_miter);
host->sg = NULL; host->sg = NULL;
ctrl = mci_readl(host, CTRL); ctrl = mci_readl(host, CTRL);
...@@ -1345,12 +1366,12 @@ static void dw_mci_tasklet_func(unsigned long priv) ...@@ -1345,12 +1366,12 @@ static void dw_mci_tasklet_func(unsigned long priv)
host->cmd = NULL; host->cmd = NULL;
host->data = NULL; host->data = NULL;
if (host->mrq->stop) if (mrq->stop)
dw_mci_command_complete(host, host->mrq->stop); dw_mci_command_complete(host, mrq->stop);
else else
host->cmd_status = 0; host->cmd_status = 0;
dw_mci_request_end(host, host->mrq); dw_mci_request_end(host, mrq);
goto unlock; goto unlock;
case STATE_DATA_ERROR: case STATE_DATA_ERROR:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment