From 39a2bccf80d801eb225b191f1e6ed7cd9ef872b4 Mon Sep 17 00:00:00 2001 From: Masaharu Hayakawa Date: Mon, 11 Dec 2017 20:51:50 +0900 Subject: [PATCH 53/61] mmc: tmio: Add SDHI-SEQUENCER support This is Workaround for SDHI-DMAC restriction of R-Car H3(WS1.x)/M3(WS1.0). Restriction: Mismatch of the transfer completion interrupt time and data transfer completion time. Overview: It does not take into account the bus response, the transfer completion interrupt IP outputs is in the early out. Therefore, when carrying out the data verification data read from the SD card, there is a possibility that the data of the last sector might be missing. (MMC Interface is also the same.) S/W Workaround: The last sector data is preserved by reading data for 2 sectors extra in the SDHI Driver of Linux kernel. SDHI Driver achieves a dummy read for 2 sectors by the SDHI-SEQ function. In case of CMD18(MMC_READ_MULTIPLE_BLOCK) were requested, 1024 bytes are read additionally by CMD18. In other cases commands, CMD17 and CMD53(SD_IO_RW_EXTENDED) is carried out additionally twice. Signed-off-by: Masaharu Hayakawa --- drivers/mmc/host/tmio_mmc.h | 8 ++ drivers/mmc/host/tmio_mmc_core.c | 157 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 159 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 815f650..a5d30b2 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -122,6 +122,7 @@ #define HS400_USE_4TAP BIT(1) #define FORCE_HS200 BIT(2) #define HS400_USE_MANUAL_CALIB BIT(3) +#define USE_SEQUENCER BIT(4) /* bit[31:16] reserved for HS400 manual calibration */ #define HS400_CALIB_MASK GENMASK_ULL(23, 16) #define HS400_OFFSET_MASK GENMASK_ULL(31, 24) @@ -144,6 +145,8 @@ struct tmio_mmc_dma_ops { void (*abort)(struct tmio_mmc_host *host); void (*dataend)(struct tmio_mmc_host *host); bool (*dma_irq)(struct tmio_mmc_host *host); + void (*seq_start)(struct tmio_mmc_host *host); + void (*seq_irq)(struct tmio_mmc_host *host, int status); }; struct tmio_mmc_host { @@ -175,10 +178,15 @@ struct tmio_mmc_host { struct dma_chan *chan_rx; struct dma_chan *chan_tx; struct tasklet_struct dma_issue; + struct tasklet_struct seq_complete; + bool bounce_sg_mapped; struct scatterlist bounce_sg; u8 *bounce_buf; u32 dma_tranend1; + /* Sequencer support */ + bool seq_enabled; + /* Track lost interrupts */ struct delayed_work delayed_reset_work; struct work_struct done; diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 2320183..5d3a12c 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -194,6 +194,13 @@ static int tmio_mmc_next_sg(struct tmio_mmc_host *host) static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) { struct tmio_mmc_host *host = mmc_priv(mmc); + unsigned int timeout = 1000; + unsigned long flags = 0; + + if (host->seq_enabled) + spin_lock_irqsave(&host->lock, flags); + else + __acquire(&host->lock); if (enable && !host->sdio_irq_enabled) { u16 sdio_status; @@ -210,15 +217,52 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) sdio_status |= TMIO_SDIO_SETBITS_MASK; sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status); - sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); + if (host->seq_enabled) { + while (timeout) { + sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, + host->sdio_irq_mask); + if (sd_ctrl_read16(host, CTL_SDIO_IRQ_MASK) == + host->sdio_irq_mask) + break; + + udelay(1); + timeout--; + } + if (!timeout) + dev_warn(&host->pdev->dev, + "failed to write CTL_SDIO_IRQ_MASK\n"); + } else { + sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, + host->sdio_irq_mask); + } } else if (!enable && host->sdio_irq_enabled) { host->sdio_irq_mask = TMIO_SDIO_MASK_ALL; - sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); - + if (host->seq_enabled) { + while (timeout) { + sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, + host->sdio_irq_mask); + if (sd_ctrl_read16(host, CTL_SDIO_IRQ_MASK) == + host->sdio_irq_mask) + break; + + udelay(1); + timeout--; + } + if (!timeout) + dev_warn(&host->pdev->dev, + "failed to write CTL_SDIO_IRQ_MASK\n"); + } else { + sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, + host->sdio_irq_mask); + } host->sdio_irq_enabled = false; pm_runtime_mark_last_busy(mmc_dev(mmc)); pm_runtime_put_autosuspend(mmc_dev(mmc)); } + if (host->seq_enabled) + spin_unlock_irqrestore(&host->lock, flags); + else + __release(&host->lock); } static void tmio_mmc_clk_start(struct tmio_mmc_host *host) @@ -791,6 +835,11 @@ static void __tmio_mmc_sdio_irq(struct tmio_mmc_host *host) if (!(pdata->flags & TMIO_MMC_SDIO_IRQ)) return; + if (host->seq_enabled) + spin_lock(&host->lock); + else + __acquire(&host->lock); + status = sd_ctrl_read16(host, CTL_SDIO_STATUS); ireg = status & TMIO_SDIO_MASK_ALL & ~host->sdio_irq_mask; @@ -800,6 +849,11 @@ static void __tmio_mmc_sdio_irq(struct tmio_mmc_host *host) sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status); + if (host->seq_enabled) + spin_unlock(&host->lock); + else + __release(&host->lock); + if (mmc->caps & MMC_CAP_SDIO_IRQ && ireg & TMIO_SDIO_STAT_IOIRQ) mmc_signal_sdio_irq(mmc); } @@ -818,6 +872,17 @@ irqreturn_t tmio_mmc_irq(int irq, void *devid) if (__tmio_mmc_card_detect_irq(host, ireg, status)) return IRQ_HANDLED; + /* Sequencer Operation End */ + if (host->seq_enabled) { + if (host->dma_ops) + if (host->dma_ops->dma_irq(host)) { + tmio_mmc_ack_mmc_irqs(host, TMIO_MASK_IRQ & + ~(TMIO_STAT_CARD_REMOVE | + TMIO_STAT_CARD_INSERT)); + host->dma_ops->seq_irq(host, status); + return IRQ_HANDLED; + } + } if (__tmio_mmc_sdcard_irq(host, ireg, status)) return IRQ_HANDLED; if (host->dma_ops) @@ -830,6 +895,60 @@ irqreturn_t tmio_mmc_irq(int irq, void *devid) } EXPORT_SYMBOL_GPL(tmio_mmc_irq); +static int tmio_mmc_start_seq(struct tmio_mmc_host *host, + struct mmc_request *mrq) +{ + struct tmio_mmc_data *pdata = host->pdata; + struct mmc_data *data = mrq->data; + + pr_debug("setup data transfer: blocksize %08x nr_blocks %d\n", + data->blksz, data->blocks); + + if (!host->chan_rx || !host->chan_tx) { + host->force_pio = true; + return 0; + } + + /* Some hardware cannot perform 2 byte requests in 4 bit mode */ + if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) { + int blksz_2bytes = pdata->flags & TMIO_MMC_BLKSZ_2BYTES; + + if (data->blksz < 2 || (data->blksz < 4 && !blksz_2bytes)) { + pr_err("%s: %d byte block unsupported in 4 bit mode\n", + mmc_hostname(host->mmc), data->blksz); + return -EINVAL; + } + } + + tmio_mmc_init_sg(host, data); + host->cmd = mrq->cmd; + host->data = data; + + sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0); + + if (host->dma_ops) + host->dma_ops->seq_start(host); + + if (host->force_pio) { + host->cmd = NULL; + host->data = NULL; + } + + return 0; +} + +static void tmio_mmc_set_blklen_and_blkcnt(struct tmio_mmc_host *host, + struct mmc_data *data) +{ + host->force_pio = true; + tmio_mmc_init_sg(host, data); + host->data = data; + + /* Set transfer length / blocksize */ + sd_ctrl_write16(host, CTL_SD_XFER_LEN, data->blksz); + sd_ctrl_write16(host, CTL_XFER_BLK_COUNT, data->blocks); +} + static int tmio_mmc_start_data(struct tmio_mmc_host *host, struct mmc_data *data) { @@ -958,14 +1077,40 @@ static void tmio_process_mrq(struct tmio_mmc_host *host, struct mmc_command *cmd; int ret; + if (host->seq_enabled) { + if (mrq->data) { + /* Start SEQ */ + ret = tmio_mmc_start_seq(host, mrq); + if (ret) { + goto fail; + } else if (!host->force_pio) { + /* + * Succeeded to start SEQ + * Wait SEQ interrupt + */ + schedule_delayed_work( + &host->delayed_reset_work, + msecs_to_jiffies(CMDREQ_TIMEOUT)); + return; + } + } + } if (mrq->sbc && host->cmd != mrq->sbc) { cmd = mrq->sbc; } else { cmd = mrq->cmd; if (mrq->data) { - ret = tmio_mmc_start_data(host, mrq->data); - if (ret) - goto fail; + if (host->seq_enabled) { + /* + * Failed to start SEQ + * Set blklen and blkcnt to transfer in PIO mode + */ + tmio_mmc_set_blklen_and_blkcnt(host, mrq->data); + } else { + ret = tmio_mmc_start_data(host, mrq->data); + if (ret) + goto fail; + } } } -- 2.7.4