Commit 4f8ee6ae authored by David Brownell's avatar David Brownell Committed by Kevin Hilman

MMC/SD dma fault handling fixes

Clean up MMC/SD fault handling for transfers with data stages:

 - After DMA errors, always issue any STOP needed; aborting DMA
   on the host is not sufficient to reset the card's state.

 - It does that already after data or command errors; but also:
     * Always reset the data and command state machines, instead
       of just the command machine (and just for data "CRC" errors)
     * Report how many blocks were transferred (resolving FIXMEs)

There are probably still a few holes in fault handling, but these
updates resolved some problems I was (briefly) able to reproduce
by running a DM6446 quite a bit faster than it's supposed to be
able to handle.
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarKevin Hilman <khilman@deeprootsystems.com>
parent bd417539
...@@ -423,6 +423,9 @@ static void davinci_abort_dma(struct mmc_davinci_host *host) ...@@ -423,6 +423,9 @@ static void davinci_abort_dma(struct mmc_davinci_host *host)
edma_clean_channel(sync_dev); edma_clean_channel(sync_dev);
} }
static void
mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data);
static void mmc_davinci_dma_cb(unsigned channel, u16 ch_status, void *data) static void mmc_davinci_dma_cb(unsigned channel, u16 ch_status, void *data)
{ {
if (DMA_COMPLETE != ch_status) { if (DMA_COMPLETE != ch_status) {
...@@ -435,8 +438,8 @@ static void mmc_davinci_dma_cb(unsigned channel, u16 ch_status, void *data) ...@@ -435,8 +438,8 @@ static void mmc_davinci_dma_cb(unsigned channel, u16 ch_status, void *data)
dev_warn(mmc_dev(host->mmc), "DMA %s error\n", dev_warn(mmc_dev(host->mmc), "DMA %s error\n",
(host->data->flags & MMC_DATA_WRITE) (host->data->flags & MMC_DATA_WRITE)
? "write" : "read"); ? "write" : "read");
davinci_abort_dma(host);
host->data->error = -EIO; host->data->error = -EIO;
mmc_davinci_xfer_done(host, host->data);
} }
} }
...@@ -868,6 +871,25 @@ static void mmc_davinci_cmd_done(struct mmc_davinci_host *host, ...@@ -868,6 +871,25 @@ static void mmc_davinci_cmd_done(struct mmc_davinci_host *host,
} }
} }
static void
davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data)
{
u32 temp;
/* record how much data we transferred */
temp = readl(host->base + DAVINCI_MMCNBLC);
data->bytes_xfered += (data->blocks - temp) * data->blksz;
/* reset command and data state machines */
temp = readl(host->base + DAVINCI_MMCCTL);
writel(temp | MMCCTL_CMDRST | MMCCTL_DATRST,
host->base + DAVINCI_MMCCTL);
temp &= ~(MMCCTL_CMDRST | MMCCTL_DATRST);
udelay(10);
writel(temp, host->base + DAVINCI_MMCCTL);
}
static inline int handle_core_command( static inline int handle_core_command(
struct mmc_davinci_host *host, unsigned int status) struct mmc_davinci_host *host, unsigned int status)
{ {
...@@ -906,30 +928,18 @@ static inline int handle_core_command( ...@@ -906,30 +928,18 @@ static inline int handle_core_command(
data->error = -ETIMEDOUT; data->error = -ETIMEDOUT;
end_transfer = 1; end_transfer = 1;
/* REVISIT report *actual* bytecount on errors */
dev_dbg(mmc_dev(host->mmc), dev_dbg(mmc_dev(host->mmc),
"read data timeout, status %x\n", "read data timeout, status %x\n",
qstatus); qstatus);
davinci_abort_data(host, data);
} }
if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) { if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) {
u32 temp;
/* DAT line portion is disabled and in reset state */
temp = readl(host->base + DAVINCI_MMCCTL);
writel(temp | MMCCTL_CMDRST,
host->base + DAVINCI_MMCCTL);
udelay(10);
writel(temp & ~MMCCTL_CMDRST,
host->base + DAVINCI_MMCCTL);
/* Data CRC error */ /* Data CRC error */
data->error = -EILSEQ; data->error = -EILSEQ;
end_transfer = 1; end_transfer = 1;
/* REVISIT report *actual* bytecount on errors */
/* NOTE: this controller uses CRCWR to report both CRC /* NOTE: this controller uses CRCWR to report both CRC
* errors and timeouts (on writes). MMCDRSP values are * errors and timeouts (on writes). MMCDRSP values are
* only weakly documented, but 0x9f was clearly a timeout * only weakly documented, but 0x9f was clearly a timeout
...@@ -937,13 +947,16 @@ static inline int handle_core_command( ...@@ -937,13 +947,16 @@ static inline int handle_core_command(
* (101, 010) aren't part of it ... * (101, 010) aren't part of it ...
*/ */
if (qstatus & MMCST0_CRCWR) { if (qstatus & MMCST0_CRCWR) {
temp = readb(host->base + DAVINCI_MMCDRSP); u32 temp = readb(host->base + DAVINCI_MMCDRSP);
if (temp == 0x9f) if (temp == 0x9f)
data->error = -ETIMEDOUT; data->error = -ETIMEDOUT;
} }
dev_dbg(mmc_dev(host->mmc), "data %s %s error\n", dev_dbg(mmc_dev(host->mmc), "data %s %s error\n",
(qstatus & MMCST0_CRCWR) ? "write" : "read", (qstatus & MMCST0_CRCWR) ? "write" : "read",
(data->error == -ETIMEDOUT) ? "timeout" : "CRC"); (data->error == -ETIMEDOUT) ? "timeout" : "CRC");
davinci_abort_data(host, data);
} }
if (qstatus & MMCST0_TOUTRS) { if (qstatus & MMCST0_TOUTRS) {
...@@ -953,9 +966,10 @@ static inline int handle_core_command( ...@@ -953,9 +966,10 @@ static inline int handle_core_command(
"timeout, status %x\n", "timeout, status %x\n",
host->cmd->opcode, qstatus); host->cmd->opcode, qstatus);
host->cmd->error = -ETIMEDOUT; host->cmd->error = -ETIMEDOUT;
if (data) if (data) {
end_transfer = 1; end_transfer = 1;
else davinci_abort_data(host, data);
} else
end_command = 1; end_command = 1;
} }
} }
......
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