diff options
-rw-r--r-- | drivers/mmc/host/sdhci-of-esdhc.c | 51 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.c | 2 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.h | 5 |
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 | ||
25 | static u32 esdhc_readl(struct sdhci_host *host, int reg) | 26 | static 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 | ||
89 | static 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 | |||
88 | static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) | 101 | static 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 | */ | ||
144 | static 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 | |||
124 | static int esdhc_of_enable_dma(struct sdhci_host *host) | 172 | static 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 | ||
203 | static struct sdhci_pltfm_data sdhci_esdhc_pdata = { | 252 | static 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 | ||