From: David Brownell <[email protected]> Fix several problems uncovered by the mmc_test driver, so most tests pass (for DMA and for PIO) except two for unusual size block reads.
- Resolve a DMA test hang by removing most of the remaining (undesirable) cmd->opcode -specfic handlng. - Remove an oops in an IRQ handler message for a CRC error. - Never report data read timeout errors as command errors. - On data phase termination, clean up more carefully: always abort exactly once, always send any STOP command, only set the transfer length on success paths - Handle shared data phase error reporting paths for writes: both CRC and timeout errors get the same IRQ. We seem to be able to distinguish them using the MMCDRSP register though. Plus semi-related fixes: - Have some IRQ messages use the correct version of "status". - Comment some bits of the PIO logic and data. This still wimps out on reporting correct data transfer lengths after faults; it should use the MMCNBLC register. Signed-off-by: David Brownell <[email protected]> --- drivers/mmc/host/davinci_mmc.c | 102 +++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 47 deletions(-) --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -189,7 +189,13 @@ struct mmc_davinci_host { #define DAVINCI_MMC_DATADIR_READ 1 #define DAVINCI_MMC_DATADIR_WRITE 2 unsigned char data_dir; + + /* buffer is used during PIO of one scatterlist segment, and + * is updated along with buffer_bytes_left. bytes_left applies + * to all N blocks of the PIO transfer. + */ u8 *buffer; + u32 buffer_bytes_left; u32 bytes_left; u8 rxdma, txdma; @@ -200,7 +206,6 @@ struct mmc_davinci_host { unsigned int sg_len; int sg_idx; - unsigned int buffer_bytes_left; unsigned int option_read; unsigned int option_write; @@ -289,6 +294,9 @@ static void davinci_fifo_data_trans(stru host->buffer_bytes_left -= n; host->bytes_left -= n; + /* NOTE: we never transfer more than rw_threshold bytes + * to/from the fifo here; there's no I/O overlap. + */ if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) { DAVINCI_MMCSD_WRITE_FIFO(p, host->base, n); } else { @@ -356,10 +364,8 @@ static void mmc_davinci_start_command(st if (cmd->opcode == 0) cmd_reg |= MMCCMD_INITCK; - /* Set for generating DMA Xfer event */ - if ((host->do_dma == 1) && (host->data != NULL) - && ((cmd->opcode == 18) || (cmd->opcode == 25) - || (cmd->opcode == 24) || (cmd->opcode == 17))) + /* Enable EDMA transfer triggers */ + if (host->do_dma) cmd_reg |= MMCCMD_DMATRIG; /* Setting whether command involves data transfer or not */ @@ -417,7 +423,6 @@ static void davinci_abort_dma(struct mmc davinci_stop_dma(sync_dev); davinci_clean_channel(sync_dev); - } static void mmc_davinci_dma_cb(int lch, u16 ch_status, void *data) @@ -869,8 +874,6 @@ mmc_davinci_xfer_done(struct mmc_davinci { host->data = NULL; host->data_dir = DAVINCI_MMC_DATADIR_NONE; - if (data->error == 0) - data->bytes_xfered += data->blocks * data->blksz; if (host->do_dma) { davinci_abort_dma(host); @@ -879,18 +882,14 @@ mmc_davinci_xfer_done(struct mmc_davinci (data->flags & MMC_DATA_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + host->do_dma = false; } - if (data->error == -ETIMEDOUT) { - mmc_request_done(host->mmc, data->mrq); - return; - } - - if (!data->stop) { + if (!data->stop || (host->cmd && host->cmd->error)) { mmc_request_done(host->mmc, data->mrq); - return; - } - mmc_davinci_start_command(host, data->stop); + writel(0, host->base + DAVINCI_MMCIM); + } else + mmc_davinci_start_command(host, data->stop); } static void mmc_davinci_cmd_done(struct mmc_davinci_host *host, @@ -921,6 +920,7 @@ static void mmc_davinci_cmd_done(struct if (cmd->error == -ETIMEDOUT) cmd->mrq->cmd->retries = 0; mmc_request_done(host->mmc, cmd->mrq); + writel(0, host->base + DAVINCI_MMCIM); } } @@ -930,6 +930,7 @@ static inline int handle_core_command( int end_command = 0; int end_transfer = 0; unsigned int qstatus = status; + struct mmc_data *data = host->data; /* handle FIFO first when using PIO for data */ while (host->bytes_left && (status & (MMCST0_DXRDY | MMCST0_DRRDY))) { @@ -941,70 +942,77 @@ static inline int handle_core_command( } if (qstatus & MMCST0_DATDNE) { - /* Block sent/received */ - if (host->data != NULL) { + /* All blocks sent/received, and CRC checks passed */ + if (data != NULL) { if ((host->do_dma == 0) && (host->bytes_left > 0)) { /* if datasize < rw_threshold * no RX ints are generated */ - davinci_fifo_data_trans(host, - rw_threshold); + davinci_fifo_data_trans(host, host->bytes_left); } end_transfer = 1; + data->bytes_xfered += data->blocks * data->blksz; } else { dev_warn(mmc_dev(host->mmc), "TC:host->data is NULL\n"); } } if (qstatus & MMCST0_TOUTRD) { - /* Data timeout */ - if (host->data) { - host->data->error = -ETIMEDOUT; - dev_dbg(mmc_dev(host->mmc), "MMCSD: Data timeout, " - "CMD%d and status is %x\n", - host->cmd->opcode, status); + /* Read data timeout */ + data->error = -ETIMEDOUT; + end_transfer = 1; - if (host->cmd) - host->cmd->error = -ETIMEDOUT; - end_transfer = 1; - } + /* REVISIT report *actual* bytecount on errors */ + + dev_dbg(mmc_dev(host->mmc), + "read data timeout, status %x\n", + qstatus); } 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 */ - if (host->data) { - host->data->error = -EILSEQ; - dev_dbg(mmc_dev(host->mmc), "MMCSD: Data CRC error, " - "bytes left %d\n", host->bytes_left); - end_transfer = 1; - } else { - dev_dbg(mmc_dev(host->mmc), "MMCSD: Data CRC error\n"); + data->error = -EILSEQ; + end_transfer = 1; + + /* REVISIT report *actual* bytecount on errors */ + + /* NOTE: this controller uses CRCWR to report both CRC + * errors and timeouts (on writes). MMCDRSP values are + * only weakly documented, but 0x9f was clearly a timeout + * case and the two three-bit patterns in various SD specs + * (101, 010) aren't part of it ... + */ + if (qstatus & MMCST0_CRCWR) { + temp = readb(host->base + DAVINCI_MMCDRSP); + if (temp == 0x9f) + data->error = -ETIMEDOUT; } + dev_dbg(mmc_dev(host->mmc), "data %s %s error\n", + (qstatus & MMCST0_CRCWR) ? "write" : "read", + (data->error == -ETIMEDOUT) ? "timeout" : "CRC"); } if (qstatus & MMCST0_TOUTRS) { - if (host->do_dma) - davinci_abort_dma(host); - /* Command timeout */ if (host->cmd) { dev_dbg(mmc_dev(host->mmc), "MMCSD: CMD%d " "timeout, status %x\n", - host->cmd->opcode, status); + host->cmd->opcode, qstatus); host->cmd->error = -ETIMEDOUT; - end_command = 1; + if (data) + end_transfer = 1; + else + end_command = 1; } } @@ -1027,7 +1035,7 @@ static inline int handle_core_command( if (end_command) mmc_davinci_cmd_done(host, host->cmd); if (end_transfer) - mmc_davinci_xfer_done(host, host->data); + mmc_davinci_xfer_done(host, data); return 0; } _______________________________________________ Davinci-linux-open-source mailing list [email protected] http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
