aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/sdhci-of-esdhc.c
diff options
context:
space:
mode:
authorHaijun Zhang <Haijun.Zhang@freescale.com>2012-12-03 21:41:28 -0500
committerChris Ball <cjb@laptop.org>2012-12-06 13:55:13 -0500
commita4071fbbb9edbc5a24985b2f64ed45f35b90dbeb (patch)
treeb4aed64d9190298577ead540d5b121603b15eadd /drivers/mmc/host/sdhci-of-esdhc.c
parentb2f7cb45c0ac0852b4b0ba938388ab24c91d6b2d (diff)
mmc: eSDHC: Recover from ADMA errors
A-003500: False ADMA Error might be reported when ADMA is used for multiple block read command with Stop at Block Gap. If PROCTL[SABGREQ] is set when the particular block's data is received by the System side logic before entire block (with CRC) data is received by the SD side logic, and also if ADMA descriptor line is fetched at the same time, then DMA engine might report false ADMA error. eSDHC might not be able to Continue (PROCTL[CREQ]=1) after Stop at Block Gap. This issue will impact the eSDHC IP VVN2.3. Signed-off-by: Haijun Zhang <Haijun.Zhang@freescale.com> Signed-off-by: Jerry Huang <Chang-Ming.Huang@freescale.com> Acked-by: Anton Vorontsov <cbouatmailru@gmail.com> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc/host/sdhci-of-esdhc.c')
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c51
1 files changed, 50 insertions, 1 deletions
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index cfabc43a4ff2..60de2eeb39b1 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -22,6 +22,7 @@
22#include "sdhci-esdhc.h" 22#include "sdhci-esdhc.h"
23 23
24#define VENDOR_V_22 0x12 24#define VENDOR_V_22 0x12
25#define VENDOR_V_23 0x13
25static u32 esdhc_readl(struct sdhci_host *host, int reg) 26static u32 esdhc_readl(struct sdhci_host *host, int reg)
26{ 27{
27 u32 ret; 28 u32 ret;
@@ -85,6 +86,18 @@ static u8 esdhc_readb(struct sdhci_host *host, int reg)
85 return ret; 86 return ret;
86} 87}
87 88
89static void esdhc_writel(struct sdhci_host *host, u32 val, int reg)
90{
91 /*
92 * Enable IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE]
93 * when SYSCTL[RSTD]) is set for some special operations.
94 * No any impact other operation.
95 */
96 if (reg == SDHCI_INT_ENABLE)
97 val |= SDHCI_INT_BLK_GAP;
98 sdhci_be32bs_writel(host, val, reg);
99}
100
88static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) 101static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
89{ 102{
90 if (reg == SDHCI_BLOCK_SIZE) { 103 if (reg == SDHCI_BLOCK_SIZE) {
@@ -121,6 +134,41 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
121 sdhci_be32bs_writeb(host, val, reg); 134 sdhci_be32bs_writeb(host, val, reg);
122} 135}
123 136
137/*
138 * For Abort or Suspend after Stop at Block Gap, ignore the ADMA
139 * error(IRQSTAT[ADMAE]) if both Transfer Complete(IRQSTAT[TC])
140 * and Block Gap Event(IRQSTAT[BGE]) are also set.
141 * For Continue, apply soft reset for data(SYSCTL[RSTD]);
142 * and re-issue the entire read transaction from beginning.
143 */
144static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask)
145{
146 u32 tmp;
147 bool applicable;
148 dma_addr_t dmastart;
149 dma_addr_t dmanow;
150
151 tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS);
152 tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
153
154 applicable = (intmask & SDHCI_INT_DATA_END) &&
155 (intmask & SDHCI_INT_BLK_GAP) &&
156 (tmp == VENDOR_V_23);
157 if (!applicable)
158 return;
159
160 host->data->error = 0;
161 dmastart = sg_dma_address(host->data->sg);
162 dmanow = dmastart + host->data->bytes_xfered;
163 /*
164 * Force update to the next DMA block boundary.
165 */
166 dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) +
167 SDHCI_DEFAULT_BOUNDARY_SIZE;
168 host->data->bytes_xfered = dmanow - dmastart;
169 sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS);
170}
171
124static int esdhc_of_enable_dma(struct sdhci_host *host) 172static int esdhc_of_enable_dma(struct sdhci_host *host)
125{ 173{
126 setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP); 174 setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
@@ -186,7 +234,7 @@ static struct sdhci_ops sdhci_esdhc_ops = {
186 .read_l = esdhc_readl, 234 .read_l = esdhc_readl,
187 .read_w = esdhc_readw, 235 .read_w = esdhc_readw,
188 .read_b = esdhc_readb, 236 .read_b = esdhc_readb,
189 .write_l = sdhci_be32bs_writel, 237 .write_l = esdhc_writel,
190 .write_w = esdhc_writew, 238 .write_w = esdhc_writew,
191 .write_b = esdhc_writeb, 239 .write_b = esdhc_writeb,
192 .set_clock = esdhc_of_set_clock, 240 .set_clock = esdhc_of_set_clock,
@@ -198,6 +246,7 @@ static struct sdhci_ops sdhci_esdhc_ops = {
198 .platform_suspend = esdhc_of_suspend, 246 .platform_suspend = esdhc_of_suspend,
199 .platform_resume = esdhc_of_resume, 247 .platform_resume = esdhc_of_resume,
200#endif 248#endif
249 .adma_workaround = esdhci_of_adma_workaround,
201}; 250};
202 251
203static struct sdhci_pltfm_data sdhci_esdhc_pdata = { 252static struct sdhci_pltfm_data sdhci_esdhc_pdata = {