Hi, all Expect your advice and any comments. Thanks. Regards Haijun. > -----Original Message----- > From: Zhang Haijun-B42677 > Sent: Wednesday, July 17, 2013 6:11 PM > To: linux-mmc@xxxxxxxxxxxxxxx; linuxppc-dev@xxxxxxxxxxxxxxxx > Cc: cbouatmailru@xxxxxxxxx; cjb@xxxxxxxxxx; Wood Scott-B07421; Fleming > Andy-AFLEMING; Zhang Haijun-B42677; Zhang Haijun-B42677 > Subject: [PATCH 2/4 V2] mmc: esdhc: workaround for dma err in the last > system transaction > > A-004388: eSDHC DMA might not stop if error occurs on system transaction > > eSDHC DMA(SDMA/ADMA) might not stop if an error occurs in the last system > transaction. It may continue initiating additional transactions until > software reset for data/all is issued during error recovery. There is not > any data corruption to the SD data. The IRQSTAT[DMAE] is set when the > erratum occurs. > The only conditions under which issues occur are the following: > 1. SDMA - For SD Write , the error occurs in the last system transaction. > No issue for SD read > 2. ADMA > a. Block count is enabled: For SD write, the error occurs in the last > system transaction. There is no issue for SD read when block count is > enabled. > b. Block count is disabled: Block count is designated by the ADMA > descriptor table, and the error occurs in the last system transaction > when ADMA is executing last descriptor line of table. > > eSDHC may initiate additional system transactions. There is no data > integrity issue for case 1 and 2a described below. For case 2b, system > data might be corrupted. > > Workaround: Set eSDHC_SYSCTL[RSTD] when IRQSTAT[DMAE] is set. For cases > 2a and 2b above, add an extra descriptor line with zero data next to the > last descriptor line. > > Signed-off-by: Haijun Zhang <haijun.zhang@xxxxxxxxxxxxx> > --- > changes for V2: > - Update the svr version list > > drivers/mmc/host/sdhci-of-esdhc.c | 112 > ++++++++++++++++++++++++++++++++++---- > 1 file changed, 102 insertions(+), 10 deletions(-) > > diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci- > of-esdhc.c > index 15039e2..adfaadd 100644 > --- a/drivers/mmc/host/sdhci-of-esdhc.c > +++ b/drivers/mmc/host/sdhci-of-esdhc.c > @@ -21,9 +21,13 @@ > #include <linux/mmc/host.h> > #include "sdhci-pltfm.h" > #include "sdhci-esdhc.h" > +#include <asm/mpc85xx.h> > > #define VENDOR_V_22 0x12 > #define VENDOR_V_23 0x13 > + > +static u32 svr; > + > static u32 esdhc_readl(struct sdhci_host *host, int reg) { > u32 ret; > @@ -142,6 +146,26 @@ static void esdhc_writeb(struct sdhci_host *host, u8 > val, int reg) > sdhci_be32bs_writeb(host, val, reg); > } > > +static void esdhc_reset(struct sdhci_host *host, u8 mask) { > + u32 ier; > + u32 uninitialized_var(isav); > + > + if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) > + isav = esdhc_readl(host, SDHCI_INT_ENABLE); > + > + esdhc_writeb(host, mask, SDHCI_SOFTWARE_RESET); > + mdelay(100); > + > + if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) { > + ier = esdhc_readl(host, SDHCI_INT_ENABLE); > + ier &= ~SDHCI_INT_ALL_MASK; > + ier |= isav; > + esdhc_writel(host, ier, SDHCI_INT_ENABLE); > + esdhc_writel(host, ier, SDHCI_SIGNAL_ENABLE); > + } > +} > + > /* > * For Abort or Suspend after Stop at Block Gap, ignore the ADMA > * error(IRQSTAT[ADMAE]) if both Transfer Complete(IRQSTAT[TC]) @@ - > 156,25 +180,92 @@ static void esdhci_of_adma_workaround(struct sdhci_host > *host, u32 intmask) > dma_addr_t dmastart; > dma_addr_t dmanow; > > - tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); > + tmp = esdhc_readl(host, SDHCI_SLOT_INT_STATUS); > tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; > > applicable = (intmask & SDHCI_INT_DATA_END) && > (intmask & SDHCI_INT_BLK_GAP) && > (tmp == VENDOR_V_23); > - if (!applicable) > + if (applicable) { > + > + esdhc_reset(host, SDHCI_RESET_DATA); > + host->data->error = 0; > + dmastart = sg_dma_address(host->data->sg); > + dmanow = dmastart + host->data->bytes_xfered; > + > + /* Force update to the next DMA block boundary. */ > + dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + > + SDHCI_DEFAULT_BOUNDARY_SIZE; > + host->data->bytes_xfered = dmanow - dmastart; > + esdhc_writel(host, dmanow, SDHCI_DMA_ADDRESS); > + > return; > + } > > - host->data->error = 0; > - dmastart = sg_dma_address(host->data->sg); > - dmanow = dmastart + host->data->bytes_xfered; > /* > - * Force update to the next DMA block boundary. > + * Check for A-004388: eSDHC DMA might not stop if error > + * occurs on system transaction > + * Impact list: > + * T4240-R1.0 B4860-R1.0 P1010-R1.0 > + * P3041-R1.0-R2.0-R1.1 P2041-R1.0-R1.1-R2.0 > + * P5040-R2.0 > */ > - dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + > - SDHCI_DEFAULT_BOUNDARY_SIZE; > - host->data->bytes_xfered = dmanow - dmastart; > - sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); > + if (!(((SVR_SOC_VER(svr) == SVR_T4240) && (SVR_REV(svr) == 0x10)) > || > + ((SVR_SOC_VER(svr) == SVR_B4860) && (SVR_REV(svr) == 0x10)) > || > + ((SVR_SOC_VER(svr) == SVR_P1010) && (SVR_REV(svr) == 0x10)) > || > + ((SVR_SOC_VER(svr) == SVR_P3041) && (SVR_REV(svr) <= 0x20)) > || > + ((SVR_SOC_VER(svr) == SVR_P2041) && (SVR_REV(svr) <= 0x20)) > || > + ((SVR_SOC_VER(svr) == SVR_P5040) && SVR_REV(svr) == 0x20))) > + return; > + > + esdhc_reset(host, SDHCI_RESET_DATA); > + > + if (host->flags & SDHCI_USE_ADMA) { > + u32 mod, i, offset; > + u8 *desc; > + dma_addr_t addr; > + struct scatterlist *sg; > + __le32 *dataddr; > + __le32 *cmdlen; > + > + /* > + * If block count was enabled, in case read transfer there > + * is no data was corrupted > + */ > + mod = esdhc_readl(host, SDHCI_TRANSFER_MODE); > + if ((mod & SDHCI_TRNS_BLK_CNT_EN) && > + (host->data->flags & MMC_DATA_READ)) > + host->data->error = 0; > + > + BUG_ON(!host->data); > + desc = host->adma_desc; > + for_each_sg(host->data->sg, sg, host->sg_count, i) { > + addr = sg_dma_address(sg); > + offset = (4 - (addr & 0x3)) & 0x3; > + if (offset) > + desc += 8; > + desc += 8; > + } > + > + /* > + * Add an extra zero descriptor next to the > + * terminating descriptor. > + */ > + desc += 8; > + WARN_ON((desc - host->adma_desc) > (128 * 2 + 1) * 4); > + > + dataddr = (__le32 __force *)(desc + 4); > + cmdlen = (__le32 __force *)desc; > + > + cmdlen[0] = cpu_to_le32(0); > + dataddr[0] = cpu_to_le32(0); > + } > + > + if ((host->flags & SDHCI_USE_SDMA) && > + (host->data->flags & MMC_DATA_READ)) > + host->data->error = 0; > + > + return; > } > > static int esdhc_of_enable_dma(struct sdhci_host *host) @@ -299,6 +390,7 > @@ static int sdhci_esdhc_probe(struct platform_device *pdev) > struct device_node *np; > int ret; > > + svr = mfspr(SPRN_SVR); > host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); > if (IS_ERR(host)) > return PTR_ERR(host); > -- > 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html