Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Wolfram Sang and committed by
Ulf Hansson
0cbc94da 300ad899

+33 -6
+33 -6
drivers/mmc/host/renesas_sdhi_internal_dmac.c
··· 9 9 * published by the Free Software Foundation. 10 10 */ 11 11 12 + #include <linux/bitops.h> 12 13 #include <linux/device.h> 13 14 #include <linux/dma-mapping.h> 14 15 #include <linux/io-64-nonatomic-hi-lo.h> ··· 62 61 * - Since this SDHI DMAC register set has 16 but 32-bit width, we 63 62 * need a custom accessor. 64 63 */ 64 + 65 + static unsigned long global_flags; 66 + /* 67 + * Workaround for avoiding to use RX DMAC by multiple channels. 68 + * On R-Car H3 ES1.* and M3-W ES1.0, when multiple SDHI channels use 69 + * RX DMAC simultaneously, sometimes hundreds of bytes data are not 70 + * stored into the system memory even if the DMAC interrupt happened. 71 + * So, this driver then uses one RX DMAC channel only. 72 + */ 73 + #define SDHI_INTERNAL_DMAC_ONE_RX_ONLY 0 74 + #define SDHI_INTERNAL_DMAC_RX_IN_USE 1 65 75 66 76 /* Definitions for sampling clocks */ 67 77 static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { ··· 138 126 renesas_sdhi_internal_dmac_dm_write(host, DM_CM_RST, 139 127 RST_RESERVED_BITS | val); 140 128 129 + if (host->data && host->data->flags & MMC_DATA_READ) 130 + clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags); 131 + 141 132 renesas_sdhi_internal_dmac_enable_dma(host, true); 142 133 } 143 134 ··· 170 155 if (data->flags & MMC_DATA_READ) { 171 156 dtran_mode |= DTRAN_MODE_CH_NUM_CH1; 172 157 dir = DMA_FROM_DEVICE; 158 + if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) && 159 + test_and_set_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags)) 160 + goto force_pio; 173 161 } else { 174 162 dtran_mode |= DTRAN_MODE_CH_NUM_CH0; 175 163 dir = DMA_TO_DEVICE; ··· 226 208 renesas_sdhi_internal_dmac_enable_dma(host, false); 227 209 dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->sg_len, dir); 228 210 211 + if (dir == DMA_FROM_DEVICE) 212 + clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags); 213 + 229 214 tmio_mmc_do_data_irq(host); 230 215 out: 231 216 spin_unlock_irq(&host->lock); ··· 272 251 * implementation as others may use a different implementation. 273 252 */ 274 253 static const struct soc_device_attribute gen3_soc_whitelist[] = { 275 - { .soc_id = "r8a7795", .revision = "ES1.*" }, 276 - { .soc_id = "r8a7795", .revision = "ES2.0" }, 277 - { .soc_id = "r8a7796", .revision = "ES1.0" }, 278 - { .soc_id = "r8a77995", .revision = "ES1.0" }, 279 - { /* sentinel */ } 254 + { .soc_id = "r8a7795", .revision = "ES1.*", 255 + .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) }, 256 + { .soc_id = "r8a7795", .revision = "ES2.0" }, 257 + { .soc_id = "r8a7796", .revision = "ES1.0", 258 + .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) }, 259 + { .soc_id = "r8a77995", .revision = "ES1.0" }, 260 + { /* sentinel */ } 280 261 }; 281 262 282 263 static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev) 283 264 { 284 - if (!soc_device_match(gen3_soc_whitelist)) 265 + const struct soc_device_attribute *soc = soc_device_match(gen3_soc_whitelist); 266 + 267 + if (!soc) 285 268 return -ENODEV; 269 + 270 + global_flags |= (unsigned long)soc->data; 286 271 287 272 return renesas_sdhi_probe(pdev, &renesas_sdhi_internal_dmac_dma_ops); 288 273 }