aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWolfram Sang <wsa+renesas@sang-engineering.com>2018-04-18 14:20:57 -0400
committerUlf Hansson <ulf.hansson@linaro.org>2018-04-19 08:57:17 -0400
commit0cbc94daa55441c21999e96a07061952d873dcb7 (patch)
tree49ae3f095d0785d496604f5079443d5ccddaacb6
parent300ad8992913025b4294d4fc37b6bfff4a8b7ad1 (diff)
mmc: renesas_sdhi_internal_dmac: limit DMA RX for old SoCs
Early revisions of certain SoCs cannot do multiple DMA RX streams in parallel. To avoid data corruption, only allow one DMA RX channel and fall back to PIO, if needed. Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Tested-by: Nguyen Viet Dung <dung.nguyen.aj@renesas.com> Reviewed-by: Simon Horman <horms+renesas@verge.net.au> Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
-rw-r--r--drivers/mmc/host/renesas_sdhi_internal_dmac.c39
1 files changed, 33 insertions, 6 deletions
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index 8e0acd197c43..6af946d16d24 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -9,6 +9,7 @@
9 * published by the Free Software Foundation. 9 * published by the Free Software Foundation.
10 */ 10 */
11 11
12#include <linux/bitops.h>
12#include <linux/device.h> 13#include <linux/device.h>
13#include <linux/dma-mapping.h> 14#include <linux/dma-mapping.h>
14#include <linux/io-64-nonatomic-hi-lo.h> 15#include <linux/io-64-nonatomic-hi-lo.h>
@@ -62,6 +63,17 @@
62 * need a custom accessor. 63 * need a custom accessor.
63 */ 64 */
64 65
66static unsigned long global_flags;
67/*
68 * Workaround for avoiding to use RX DMAC by multiple channels.
69 * On R-Car H3 ES1.* and M3-W ES1.0, when multiple SDHI channels use
70 * RX DMAC simultaneously, sometimes hundreds of bytes data are not
71 * stored into the system memory even if the DMAC interrupt happened.
72 * So, this driver then uses one RX DMAC channel only.
73 */
74#define SDHI_INTERNAL_DMAC_ONE_RX_ONLY 0
75#define SDHI_INTERNAL_DMAC_RX_IN_USE 1
76
65/* Definitions for sampling clocks */ 77/* Definitions for sampling clocks */
66static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { 78static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
67 { 79 {
@@ -126,6 +138,9 @@ renesas_sdhi_internal_dmac_abort_dma(struct tmio_mmc_host *host) {
126 renesas_sdhi_internal_dmac_dm_write(host, DM_CM_RST, 138 renesas_sdhi_internal_dmac_dm_write(host, DM_CM_RST,
127 RST_RESERVED_BITS | val); 139 RST_RESERVED_BITS | val);
128 140
141 if (host->data && host->data->flags & MMC_DATA_READ)
142 clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags);
143
129 renesas_sdhi_internal_dmac_enable_dma(host, true); 144 renesas_sdhi_internal_dmac_enable_dma(host, true);
130} 145}
131 146
@@ -155,6 +170,9 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
155 if (data->flags & MMC_DATA_READ) { 170 if (data->flags & MMC_DATA_READ) {
156 dtran_mode |= DTRAN_MODE_CH_NUM_CH1; 171 dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
157 dir = DMA_FROM_DEVICE; 172 dir = DMA_FROM_DEVICE;
173 if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) &&
174 test_and_set_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags))
175 goto force_pio;
158 } else { 176 } else {
159 dtran_mode |= DTRAN_MODE_CH_NUM_CH0; 177 dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
160 dir = DMA_TO_DEVICE; 178 dir = DMA_TO_DEVICE;
@@ -208,6 +226,9 @@ static void renesas_sdhi_internal_dmac_complete_tasklet_fn(unsigned long arg)
208 renesas_sdhi_internal_dmac_enable_dma(host, false); 226 renesas_sdhi_internal_dmac_enable_dma(host, false);
209 dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->sg_len, dir); 227 dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->sg_len, dir);
210 228
229 if (dir == DMA_FROM_DEVICE)
230 clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags);
231
211 tmio_mmc_do_data_irq(host); 232 tmio_mmc_do_data_irq(host);
212out: 233out:
213 spin_unlock_irq(&host->lock); 234 spin_unlock_irq(&host->lock);
@@ -251,18 +272,24 @@ static const struct tmio_mmc_dma_ops renesas_sdhi_internal_dmac_dma_ops = {
251 * implementation as others may use a different implementation. 272 * implementation as others may use a different implementation.
252 */ 273 */
253static const struct soc_device_attribute gen3_soc_whitelist[] = { 274static const struct soc_device_attribute gen3_soc_whitelist[] = {
254 { .soc_id = "r8a7795", .revision = "ES1.*" }, 275 { .soc_id = "r8a7795", .revision = "ES1.*",
255 { .soc_id = "r8a7795", .revision = "ES2.0" }, 276 .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
256 { .soc_id = "r8a7796", .revision = "ES1.0" }, 277 { .soc_id = "r8a7795", .revision = "ES2.0" },
257 { .soc_id = "r8a77995", .revision = "ES1.0" }, 278 { .soc_id = "r8a7796", .revision = "ES1.0",
258 { /* sentinel */ } 279 .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
280 { .soc_id = "r8a77995", .revision = "ES1.0" },
281 { /* sentinel */ }
259}; 282};
260 283
261static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev) 284static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
262{ 285{
263 if (!soc_device_match(gen3_soc_whitelist)) 286 const struct soc_device_attribute *soc = soc_device_match(gen3_soc_whitelist);
287
288 if (!soc)
264 return -ENODEV; 289 return -ENODEV;
265 290
291 global_flags |= (unsigned long)soc->data;
292
266 return renesas_sdhi_probe(pdev, &renesas_sdhi_internal_dmac_dma_ops); 293 return renesas_sdhi_probe(pdev, &renesas_sdhi_internal_dmac_dma_ops);
267} 294}
268 295