aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
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
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')
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c51
-rw-r--r--drivers/mmc/host/sdhci.c2
-rw-r--r--drivers/mmc/host/sdhci.h5
3 files changed, 56 insertions, 2 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 = {
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index f78c5b1329eb..b2e4b1bab69d 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2282,6 +2282,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
2282 pr_err("%s: ADMA error\n", mmc_hostname(host->mmc)); 2282 pr_err("%s: ADMA error\n", mmc_hostname(host->mmc));
2283 sdhci_show_adma_error(host); 2283 sdhci_show_adma_error(host);
2284 host->data->error = -EIO; 2284 host->data->error = -EIO;
2285 if (host->ops->adma_workaround)
2286 host->ops->adma_workaround(host, intmask);
2285 } 2287 }
2286 2288
2287 if (host->data->error) 2289 if (host->data->error)
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 71a4a7ed46c5..a6d69b7bdea2 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -120,6 +120,7 @@
120#define SDHCI_SIGNAL_ENABLE 0x38 120#define SDHCI_SIGNAL_ENABLE 0x38
121#define SDHCI_INT_RESPONSE 0x00000001 121#define SDHCI_INT_RESPONSE 0x00000001
122#define SDHCI_INT_DATA_END 0x00000002 122#define SDHCI_INT_DATA_END 0x00000002
123#define SDHCI_INT_BLK_GAP 0x00000004
123#define SDHCI_INT_DMA_END 0x00000008 124#define SDHCI_INT_DMA_END 0x00000008
124#define SDHCI_INT_SPACE_AVAIL 0x00000010 125#define SDHCI_INT_SPACE_AVAIL 0x00000010
125#define SDHCI_INT_DATA_AVAIL 0x00000020 126#define SDHCI_INT_DATA_AVAIL 0x00000020
@@ -146,7 +147,8 @@
146#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \ 147#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
147 SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \ 148 SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
148 SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ 149 SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
149 SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR) 150 SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR | \
151 SDHCI_INT_BLK_GAP)
150#define SDHCI_INT_ALL_MASK ((unsigned int)-1) 152#define SDHCI_INT_ALL_MASK ((unsigned int)-1)
151 153
152#define SDHCI_ACMD12_ERR 0x3C 154#define SDHCI_ACMD12_ERR 0x3C
@@ -278,6 +280,7 @@ struct sdhci_ops {
278 void (*hw_reset)(struct sdhci_host *host); 280 void (*hw_reset)(struct sdhci_host *host);
279 void (*platform_suspend)(struct sdhci_host *host); 281 void (*platform_suspend)(struct sdhci_host *host);
280 void (*platform_resume)(struct sdhci_host *host); 282 void (*platform_resume)(struct sdhci_host *host);
283 void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
281 void (*platform_init)(struct sdhci_host *host); 284 void (*platform_init)(struct sdhci_host *host);
282}; 285};
283 286