Patchwork [2/4,V2] mmc: esdhc: workaround for dma err in the last system transaction

login
register
mail settings
Submitter Haijun.Zhang
Date July 17, 2013, 10:11 a.m.
Message ID <1374055891-20703-1-git-send-email-Haijun.Zhang@freescale.com>
Download mbox | patch
Permalink /patch/259684/
State Not Applicable
Delegated to: Benjamin Herrenschmidt
Headers show

Comments

Haijun.Zhang - July 17, 2013, 10:11 a.m.
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@freescale.com>
---
changes for V2:
	- Update the svr version list

 drivers/mmc/host/sdhci-of-esdhc.c | 112 ++++++++++++++++++++++++++++++++++----
 1 file changed, 102 insertions(+), 10 deletions(-)
Scott Wood - Aug. 23, 2013, 3:40 p.m.
On Fri, 2013-08-23 at 14:39 +0800, Zhang Haijun wrote:
> Hi, Anton and all
> 
> Is there any advice on these two patches ?
> 
> [PATCH 2/4 V2] mmc: esdhc: workaround for dma err in the last system 
> transaction
> [PATCH 3/4 V3] mmc: esdhc: Correct host version of T4240-R1.0-R2.0.
> 
> 
> [PATCH 1/4 V4] powerpc/85xx: Add support for 85xx cpu type detection
> This patch is Act-by Scott.
> Patch 4/4 is split to four patches and Act-by Anton.
> 
> 
> Thanks all.
> 
> 
> 
[snip]
> >> +	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;

You need to include variants here.  If P5040 is affected, then P5021 is
affected.  If P2041 is affected, then P2040 is affected, etc.

-Scott
Scott Wood - Aug. 26, 2013, 4:36 p.m.
On Mon, 2013-08-26 at 09:03 +0800, Zhang Haijun wrote:
> On 08/23/2013 11:40 PM, Scott Wood wrote:
> 
> > On Fri, 2013-08-23 at 14:39 +0800, Zhang Haijun wrote:
> > > Hi, Anton and all
> > > 
> > > Is there any advice on these two patches ?
> > > 
> > > [PATCH 2/4 V2] mmc: esdhc: workaround for dma err in the last system 
> > > transaction
> > > [PATCH 3/4 V3] mmc: esdhc: Correct host version of T4240-R1.0-R2.0.
> > > 
> > > 
> > > [PATCH 1/4 V4] powerpc/85xx: Add support for 85xx cpu type detection
> > > This patch is Act-by Scott.
> > > Patch 4/4 is split to four patches and Act-by Anton.
> > > 
> > > 
> > > Thanks all.
> > > 
> > > 
> > > 
> > [snip]
> > > > > +	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;
> > You need to include variants here.  If P5040 is affected, then P5021 is
> > affected.  If P2041 is affected, then P2040 is affected, etc.
> > 
> > -Scott
> > 
> > 
> Hi, Scott
> 
> This workaround is for CR:ENGR00229586: A-005055, Configs Affected
> only list these soc and its version.
> I was also wonder why only these boards?
> 
> But I can't add soc like P5021 as I think it should be. Maybe there
> are some difference between them.

The only difference between P5040 and P5021 is the number of cores
enabled.  It is physically the same silicon.  Likewise with a lot of
other variants.

-Scott

Patch

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);