1f707079dSWolfram Sang // SPDX-License-Identifier: GPL-2.0 2c2a96987SSimon Horman /* 32a68ea78SSimon Horman * DMA support use of SYS DMAC with SDHI SD/SDIO controller 4c2a96987SSimon Horman * 587317c4dSSimon Horman * Copyright (C) 2016-17 Renesas Electronics Corporation 687317c4dSSimon Horman * Copyright (C) 2016-17 Sang Engineering, Wolfram Sang 787317c4dSSimon Horman * Copyright (C) 2017 Horms Solutions, Simon Horman 8c2a96987SSimon Horman * Copyright (C) 2010-2011 Guennadi Liakhovetski 9c2a96987SSimon Horman */ 10c2a96987SSimon Horman 11c2a96987SSimon Horman #include <linux/device.h> 12c2a96987SSimon Horman #include <linux/dma-mapping.h> 13c2a96987SSimon Horman #include <linux/dmaengine.h> 14c2a96987SSimon Horman #include <linux/mfd/tmio.h> 15c2a96987SSimon Horman #include <linux/mmc/host.h> 169d08428aSSimon Horman #include <linux/mod_devicetable.h> 179d08428aSSimon Horman #include <linux/module.h> 18cd09780fSSimon Horman #include <linux/of_device.h> 19c2a96987SSimon Horman #include <linux/pagemap.h> 20c2a96987SSimon Horman #include <linux/scatterlist.h> 21cd09780fSSimon Horman #include <linux/sys_soc.h> 22c2a96987SSimon Horman 239d08428aSSimon Horman #include "renesas_sdhi.h" 24c2a96987SSimon Horman #include "tmio_mmc.h" 25c2a96987SSimon Horman 26c2a96987SSimon Horman #define TMIO_MMC_MIN_DMA_LEN 8 27c2a96987SSimon Horman 289d08428aSSimon Horman static const struct renesas_sdhi_of_data of_default_cfg = { 299d08428aSSimon Horman .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT, 309d08428aSSimon Horman }; 319d08428aSSimon Horman 329d08428aSSimon Horman static const struct renesas_sdhi_of_data of_rz_compatible = { 3392b7db8eSWolfram Sang .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_32BIT_DATA_PORT | 3492b7db8eSWolfram Sang TMIO_MMC_HAVE_CBSY, 359d08428aSSimon Horman .tmio_ocr_mask = MMC_VDD_32_33, 369d08428aSSimon Horman .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, 379d08428aSSimon Horman }; 389d08428aSSimon Horman 399d08428aSSimon Horman static const struct renesas_sdhi_of_data of_rcar_gen1_compatible = { 402ad1db05SMasahiro Yamada .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL, 419d08428aSSimon Horman .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, 42ef5332c1SWolfram Sang .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, 439d08428aSSimon Horman }; 449d08428aSSimon Horman 459d08428aSSimon Horman /* Definitions for sampling clocks */ 469d08428aSSimon Horman static struct renesas_sdhi_scc rcar_gen2_scc_taps[] = { 479d08428aSSimon Horman { 489d08428aSSimon Horman .clk_rate = 156000000, 499d08428aSSimon Horman .tap = 0x00000703, 509d08428aSSimon Horman }, 519d08428aSSimon Horman { 529d08428aSSimon Horman .clk_rate = 0, 539d08428aSSimon Horman .tap = 0x00000300, 549d08428aSSimon Horman }, 559d08428aSSimon Horman }; 569d08428aSSimon Horman 579d08428aSSimon Horman static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = { 582ad1db05SMasahiro Yamada .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | 592ad1db05SMasahiro Yamada TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, 60921579b2SWolfram Sang .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | 61921579b2SWolfram Sang MMC_CAP_CMD23, 62ef5332c1SWolfram Sang .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, 639d08428aSSimon Horman .dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES, 649d08428aSSimon Horman .dma_rx_offset = 0x2000, 659d08428aSSimon Horman .scc_offset = 0x0300, 669d08428aSSimon Horman .taps = rcar_gen2_scc_taps, 679d08428aSSimon Horman .taps_num = ARRAY_SIZE(rcar_gen2_scc_taps), 689d08428aSSimon Horman }; 699d08428aSSimon Horman 709d08428aSSimon Horman /* Definitions for sampling clocks */ 719d08428aSSimon Horman static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { 729d08428aSSimon Horman { 739d08428aSSimon Horman .clk_rate = 0, 749d08428aSSimon Horman .tap = 0x00000300, 759d08428aSSimon Horman }, 769d08428aSSimon Horman }; 779d08428aSSimon Horman 7826eb2607SMasaharu Hayakawa static const struct renesas_sdhi_of_data of_rcar_r8a7795_compatible = { 7926eb2607SMasaharu Hayakawa .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | 8026eb2607SMasaharu Hayakawa TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 | 8126eb2607SMasaharu Hayakawa TMIO_MMC_HAVE_4TAP_HS400, 8226eb2607SMasaharu Hayakawa .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | 8326eb2607SMasaharu Hayakawa MMC_CAP_CMD23, 8426eb2607SMasaharu Hayakawa .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, 8526eb2607SMasaharu Hayakawa .bus_shift = 2, 8626eb2607SMasaharu Hayakawa .scc_offset = 0x1000, 8726eb2607SMasaharu Hayakawa .taps = rcar_gen3_scc_taps, 8826eb2607SMasaharu Hayakawa .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), 8926eb2607SMasaharu Hayakawa }; 9026eb2607SMasaharu Hayakawa 919d08428aSSimon Horman static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { 922ad1db05SMasahiro Yamada .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | 932ad1db05SMasahiro Yamada TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, 94921579b2SWolfram Sang .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | 95921579b2SWolfram Sang MMC_CAP_CMD23, 96ef5332c1SWolfram Sang .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, 979d08428aSSimon Horman .bus_shift = 2, 989d08428aSSimon Horman .scc_offset = 0x1000, 999d08428aSSimon Horman .taps = rcar_gen3_scc_taps, 1009d08428aSSimon Horman .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), 1019d08428aSSimon Horman }; 1029d08428aSSimon Horman 1039d08428aSSimon Horman static const struct of_device_id renesas_sdhi_sys_dmac_of_match[] = { 1049d08428aSSimon Horman { .compatible = "renesas,sdhi-sh73a0", .data = &of_default_cfg, }, 1059d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a73a4", .data = &of_default_cfg, }, 1069d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7740", .data = &of_default_cfg, }, 1079d08428aSSimon Horman { .compatible = "renesas,sdhi-r7s72100", .data = &of_rz_compatible, }, 1089d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7778", .data = &of_rcar_gen1_compatible, }, 1099d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7779", .data = &of_rcar_gen1_compatible, }, 110c16a854eSBiju Das { .compatible = "renesas,sdhi-r8a7743", .data = &of_rcar_gen2_compatible, }, 111c16a854eSBiju Das { .compatible = "renesas,sdhi-r8a7745", .data = &of_rcar_gen2_compatible, }, 1129d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7790", .data = &of_rcar_gen2_compatible, }, 1139d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7791", .data = &of_rcar_gen2_compatible, }, 1149d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7792", .data = &of_rcar_gen2_compatible, }, 1159d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, }, 1169d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, }, 11726eb2607SMasaharu Hayakawa { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_r8a7795_compatible, }, 11826eb2607SMasaharu Hayakawa { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_r8a7795_compatible, }, 119d6dc425aSSimon Horman { .compatible = "renesas,rcar-gen1-sdhi", .data = &of_rcar_gen1_compatible, }, 120d6dc425aSSimon Horman { .compatible = "renesas,rcar-gen2-sdhi", .data = &of_rcar_gen2_compatible, }, 121d6dc425aSSimon Horman { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, 122d6dc425aSSimon Horman { .compatible = "renesas,sdhi-shmobile" }, 1239d08428aSSimon Horman {}, 1249d08428aSSimon Horman }; 1259d08428aSSimon Horman MODULE_DEVICE_TABLE(of, renesas_sdhi_sys_dmac_of_match); 1269d08428aSSimon Horman 127c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_enable_dma(struct tmio_mmc_host *host, 128c2a96987SSimon Horman bool enable) 129c2a96987SSimon Horman { 130058db286SMasahiro Yamada struct renesas_sdhi *priv = host_to_priv(host); 131058db286SMasahiro Yamada 132c2a96987SSimon Horman if (!host->chan_tx || !host->chan_rx) 133c2a96987SSimon Horman return; 134c2a96987SSimon Horman 135058db286SMasahiro Yamada if (priv->dma_priv.enable) 136058db286SMasahiro Yamada priv->dma_priv.enable(host, enable); 137c2a96987SSimon Horman } 138c2a96987SSimon Horman 139c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_abort_dma(struct tmio_mmc_host *host) 140c2a96987SSimon Horman { 141c2a96987SSimon Horman renesas_sdhi_sys_dmac_enable_dma(host, false); 142c2a96987SSimon Horman 143c2a96987SSimon Horman if (host->chan_rx) 144c2a96987SSimon Horman dmaengine_terminate_all(host->chan_rx); 145c2a96987SSimon Horman if (host->chan_tx) 146c2a96987SSimon Horman dmaengine_terminate_all(host->chan_tx); 147c2a96987SSimon Horman 148c2a96987SSimon Horman renesas_sdhi_sys_dmac_enable_dma(host, true); 149c2a96987SSimon Horman } 150c2a96987SSimon Horman 15192d0f925SSimon Horman static void renesas_sdhi_sys_dmac_dataend_dma(struct tmio_mmc_host *host) 15292d0f925SSimon Horman { 15390d95106SMasahiro Yamada struct renesas_sdhi *priv = host_to_priv(host); 15490d95106SMasahiro Yamada 15590d95106SMasahiro Yamada complete(&priv->dma_priv.dma_dataend); 15692d0f925SSimon Horman } 15792d0f925SSimon Horman 158c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_dma_callback(void *arg) 159c2a96987SSimon Horman { 160c2a96987SSimon Horman struct tmio_mmc_host *host = arg; 16190d95106SMasahiro Yamada struct renesas_sdhi *priv = host_to_priv(host); 162c2a96987SSimon Horman 163c2a96987SSimon Horman spin_lock_irq(&host->lock); 164c2a96987SSimon Horman 165c2a96987SSimon Horman if (!host->data) 166c2a96987SSimon Horman goto out; 167c2a96987SSimon Horman 168c2a96987SSimon Horman if (host->data->flags & MMC_DATA_READ) 169c2a96987SSimon Horman dma_unmap_sg(host->chan_rx->device->dev, 170c2a96987SSimon Horman host->sg_ptr, host->sg_len, 171c2a96987SSimon Horman DMA_FROM_DEVICE); 172c2a96987SSimon Horman else 173c2a96987SSimon Horman dma_unmap_sg(host->chan_tx->device->dev, 174c2a96987SSimon Horman host->sg_ptr, host->sg_len, 175c2a96987SSimon Horman DMA_TO_DEVICE); 176c2a96987SSimon Horman 177c2a96987SSimon Horman spin_unlock_irq(&host->lock); 178c2a96987SSimon Horman 17990d95106SMasahiro Yamada wait_for_completion(&priv->dma_priv.dma_dataend); 180c2a96987SSimon Horman 181c2a96987SSimon Horman spin_lock_irq(&host->lock); 182c2a96987SSimon Horman tmio_mmc_do_data_irq(host); 183c2a96987SSimon Horman out: 184c2a96987SSimon Horman spin_unlock_irq(&host->lock); 185c2a96987SSimon Horman } 186c2a96987SSimon Horman 187c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_start_dma_rx(struct tmio_mmc_host *host) 188c2a96987SSimon Horman { 18990d95106SMasahiro Yamada struct renesas_sdhi *priv = host_to_priv(host); 190c2a96987SSimon Horman struct scatterlist *sg = host->sg_ptr, *sg_tmp; 191c2a96987SSimon Horman struct dma_async_tx_descriptor *desc = NULL; 192c2a96987SSimon Horman struct dma_chan *chan = host->chan_rx; 193c2a96987SSimon Horman dma_cookie_t cookie; 194c2a96987SSimon Horman int ret, i; 195c2a96987SSimon Horman bool aligned = true, multiple = true; 196c2a96987SSimon Horman unsigned int align = (1 << host->pdata->alignment_shift) - 1; 197c2a96987SSimon Horman 198c2a96987SSimon Horman for_each_sg(sg, sg_tmp, host->sg_len, i) { 199c2a96987SSimon Horman if (sg_tmp->offset & align) 200c2a96987SSimon Horman aligned = false; 201c2a96987SSimon Horman if (sg_tmp->length & align) { 202c2a96987SSimon Horman multiple = false; 203c2a96987SSimon Horman break; 204c2a96987SSimon Horman } 205c2a96987SSimon Horman } 206c2a96987SSimon Horman 207c2a96987SSimon Horman if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_SIZE || 208c2a96987SSimon Horman (align & PAGE_MASK))) || !multiple) { 209c2a96987SSimon Horman ret = -EINVAL; 210c2a96987SSimon Horman goto pio; 211c2a96987SSimon Horman } 212c2a96987SSimon Horman 213d3dd5db0SMasahiro Yamada if (sg->length < TMIO_MMC_MIN_DMA_LEN) 214c2a96987SSimon Horman return; 215c2a96987SSimon Horman 216c2a96987SSimon Horman /* The only sg element can be unaligned, use our bounce buffer then */ 217c2a96987SSimon Horman if (!aligned) { 218c2a96987SSimon Horman sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length); 219c2a96987SSimon Horman host->sg_ptr = &host->bounce_sg; 220c2a96987SSimon Horman sg = host->sg_ptr; 221c2a96987SSimon Horman } 222c2a96987SSimon Horman 223c2a96987SSimon Horman ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_FROM_DEVICE); 224c2a96987SSimon Horman if (ret > 0) 2252fe35968SSimon Horman desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_DEV_TO_MEM, 2262fe35968SSimon Horman DMA_CTRL_ACK); 227c2a96987SSimon Horman 228c2a96987SSimon Horman if (desc) { 22990d95106SMasahiro Yamada reinit_completion(&priv->dma_priv.dma_dataend); 230c2a96987SSimon Horman desc->callback = renesas_sdhi_sys_dmac_dma_callback; 231c2a96987SSimon Horman desc->callback_param = host; 232c2a96987SSimon Horman 233c2a96987SSimon Horman cookie = dmaengine_submit(desc); 234c2a96987SSimon Horman if (cookie < 0) { 235c2a96987SSimon Horman desc = NULL; 236c2a96987SSimon Horman ret = cookie; 237c2a96987SSimon Horman } 238d3dd5db0SMasahiro Yamada host->dma_on = true; 239c2a96987SSimon Horman } 240c2a96987SSimon Horman pio: 241c2a96987SSimon Horman if (!desc) { 242c2a96987SSimon Horman /* DMA failed, fall back to PIO */ 243c2a96987SSimon Horman renesas_sdhi_sys_dmac_enable_dma(host, false); 244c2a96987SSimon Horman if (ret >= 0) 245c2a96987SSimon Horman ret = -EIO; 246c2a96987SSimon Horman host->chan_rx = NULL; 247c2a96987SSimon Horman dma_release_channel(chan); 248c2a96987SSimon Horman /* Free the Tx channel too */ 249c2a96987SSimon Horman chan = host->chan_tx; 250c2a96987SSimon Horman if (chan) { 251c2a96987SSimon Horman host->chan_tx = NULL; 252c2a96987SSimon Horman dma_release_channel(chan); 253c2a96987SSimon Horman } 254c2a96987SSimon Horman dev_warn(&host->pdev->dev, 255c2a96987SSimon Horman "DMA failed: %d, falling back to PIO\n", ret); 256c2a96987SSimon Horman } 257c2a96987SSimon Horman } 258c2a96987SSimon Horman 259c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_start_dma_tx(struct tmio_mmc_host *host) 260c2a96987SSimon Horman { 26190d95106SMasahiro Yamada struct renesas_sdhi *priv = host_to_priv(host); 262c2a96987SSimon Horman struct scatterlist *sg = host->sg_ptr, *sg_tmp; 263c2a96987SSimon Horman struct dma_async_tx_descriptor *desc = NULL; 264c2a96987SSimon Horman struct dma_chan *chan = host->chan_tx; 265c2a96987SSimon Horman dma_cookie_t cookie; 266c2a96987SSimon Horman int ret, i; 267c2a96987SSimon Horman bool aligned = true, multiple = true; 268c2a96987SSimon Horman unsigned int align = (1 << host->pdata->alignment_shift) - 1; 269c2a96987SSimon Horman 270c2a96987SSimon Horman for_each_sg(sg, sg_tmp, host->sg_len, i) { 271c2a96987SSimon Horman if (sg_tmp->offset & align) 272c2a96987SSimon Horman aligned = false; 273c2a96987SSimon Horman if (sg_tmp->length & align) { 274c2a96987SSimon Horman multiple = false; 275c2a96987SSimon Horman break; 276c2a96987SSimon Horman } 277c2a96987SSimon Horman } 278c2a96987SSimon Horman 279c2a96987SSimon Horman if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_SIZE || 280c2a96987SSimon Horman (align & PAGE_MASK))) || !multiple) { 281c2a96987SSimon Horman ret = -EINVAL; 282c2a96987SSimon Horman goto pio; 283c2a96987SSimon Horman } 284c2a96987SSimon Horman 285d3dd5db0SMasahiro Yamada if (sg->length < TMIO_MMC_MIN_DMA_LEN) 286c2a96987SSimon Horman return; 287c2a96987SSimon Horman 288c2a96987SSimon Horman /* The only sg element can be unaligned, use our bounce buffer then */ 289c2a96987SSimon Horman if (!aligned) { 290c2a96987SSimon Horman unsigned long flags; 291c2a96987SSimon Horman void *sg_vaddr = tmio_mmc_kmap_atomic(sg, &flags); 2922fe35968SSimon Horman 293c2a96987SSimon Horman sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length); 294c2a96987SSimon Horman memcpy(host->bounce_buf, sg_vaddr, host->bounce_sg.length); 295c2a96987SSimon Horman tmio_mmc_kunmap_atomic(sg, &flags, sg_vaddr); 296c2a96987SSimon Horman host->sg_ptr = &host->bounce_sg; 297c2a96987SSimon Horman sg = host->sg_ptr; 298c2a96987SSimon Horman } 299c2a96987SSimon Horman 300c2a96987SSimon Horman ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_TO_DEVICE); 301c2a96987SSimon Horman if (ret > 0) 3022fe35968SSimon Horman desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_MEM_TO_DEV, 3032fe35968SSimon Horman DMA_CTRL_ACK); 304c2a96987SSimon Horman 305c2a96987SSimon Horman if (desc) { 30690d95106SMasahiro Yamada reinit_completion(&priv->dma_priv.dma_dataend); 307c2a96987SSimon Horman desc->callback = renesas_sdhi_sys_dmac_dma_callback; 308c2a96987SSimon Horman desc->callback_param = host; 309c2a96987SSimon Horman 310c2a96987SSimon Horman cookie = dmaengine_submit(desc); 311c2a96987SSimon Horman if (cookie < 0) { 312c2a96987SSimon Horman desc = NULL; 313c2a96987SSimon Horman ret = cookie; 314c2a96987SSimon Horman } 315d3dd5db0SMasahiro Yamada host->dma_on = true; 316c2a96987SSimon Horman } 317c2a96987SSimon Horman pio: 318c2a96987SSimon Horman if (!desc) { 319c2a96987SSimon Horman /* DMA failed, fall back to PIO */ 320c2a96987SSimon Horman renesas_sdhi_sys_dmac_enable_dma(host, false); 321c2a96987SSimon Horman if (ret >= 0) 322c2a96987SSimon Horman ret = -EIO; 323c2a96987SSimon Horman host->chan_tx = NULL; 324c2a96987SSimon Horman dma_release_channel(chan); 325c2a96987SSimon Horman /* Free the Rx channel too */ 326c2a96987SSimon Horman chan = host->chan_rx; 327c2a96987SSimon Horman if (chan) { 328c2a96987SSimon Horman host->chan_rx = NULL; 329c2a96987SSimon Horman dma_release_channel(chan); 330c2a96987SSimon Horman } 331c2a96987SSimon Horman dev_warn(&host->pdev->dev, 332c2a96987SSimon Horman "DMA failed: %d, falling back to PIO\n", ret); 333c2a96987SSimon Horman } 334c2a96987SSimon Horman } 335c2a96987SSimon Horman 336c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_start_dma(struct tmio_mmc_host *host, 337c2a96987SSimon Horman struct mmc_data *data) 338c2a96987SSimon Horman { 339c2a96987SSimon Horman if (data->flags & MMC_DATA_READ) { 340c2a96987SSimon Horman if (host->chan_rx) 341c2a96987SSimon Horman renesas_sdhi_sys_dmac_start_dma_rx(host); 342c2a96987SSimon Horman } else { 343c2a96987SSimon Horman if (host->chan_tx) 344c2a96987SSimon Horman renesas_sdhi_sys_dmac_start_dma_tx(host); 345c2a96987SSimon Horman } 346c2a96987SSimon Horman } 347c2a96987SSimon Horman 348c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_issue_tasklet_fn(unsigned long priv) 349c2a96987SSimon Horman { 350c2a96987SSimon Horman struct tmio_mmc_host *host = (struct tmio_mmc_host *)priv; 351c2a96987SSimon Horman struct dma_chan *chan = NULL; 352c2a96987SSimon Horman 353c2a96987SSimon Horman spin_lock_irq(&host->lock); 354c2a96987SSimon Horman 355b8155d3fSDan Carpenter if (host->data) { 356c2a96987SSimon Horman if (host->data->flags & MMC_DATA_READ) 357c2a96987SSimon Horman chan = host->chan_rx; 358c2a96987SSimon Horman else 359c2a96987SSimon Horman chan = host->chan_tx; 360c2a96987SSimon Horman } 361c2a96987SSimon Horman 362c2a96987SSimon Horman spin_unlock_irq(&host->lock); 363c2a96987SSimon Horman 364c2a96987SSimon Horman tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND); 365c2a96987SSimon Horman 366c2a96987SSimon Horman if (chan) 367c2a96987SSimon Horman dma_async_issue_pending(chan); 368c2a96987SSimon Horman } 369c2a96987SSimon Horman 370c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_request_dma(struct tmio_mmc_host *host, 371c2a96987SSimon Horman struct tmio_mmc_data *pdata) 372c2a96987SSimon Horman { 373058db286SMasahiro Yamada struct renesas_sdhi *priv = host_to_priv(host); 374058db286SMasahiro Yamada 375c2a96987SSimon Horman /* We can only either use DMA for both Tx and Rx or not use it at all */ 3762487e7efSMasahiro Yamada if (!host->pdev->dev.of_node && 3772487e7efSMasahiro Yamada (!pdata->chan_priv_tx || !pdata->chan_priv_rx)) 378c2a96987SSimon Horman return; 379c2a96987SSimon Horman 380c2a96987SSimon Horman if (!host->chan_tx && !host->chan_rx) { 381c2a96987SSimon Horman struct resource *res = platform_get_resource(host->pdev, 382c2a96987SSimon Horman IORESOURCE_MEM, 0); 383c2a96987SSimon Horman struct dma_slave_config cfg = {}; 384c2a96987SSimon Horman dma_cap_mask_t mask; 385c2a96987SSimon Horman int ret; 386c2a96987SSimon Horman 387c2a96987SSimon Horman if (!res) 388c2a96987SSimon Horman return; 389c2a96987SSimon Horman 390c2a96987SSimon Horman dma_cap_zero(mask); 391c2a96987SSimon Horman dma_cap_set(DMA_SLAVE, mask); 392c2a96987SSimon Horman 393c2a96987SSimon Horman host->chan_tx = dma_request_slave_channel_compat(mask, 394058db286SMasahiro Yamada priv->dma_priv.filter, pdata->chan_priv_tx, 395c2a96987SSimon Horman &host->pdev->dev, "tx"); 396c2a96987SSimon Horman dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__, 397c2a96987SSimon Horman host->chan_tx); 398c2a96987SSimon Horman 399c2a96987SSimon Horman if (!host->chan_tx) 400c2a96987SSimon Horman return; 401c2a96987SSimon Horman 402c2a96987SSimon Horman cfg.direction = DMA_MEM_TO_DEV; 4032fe35968SSimon Horman cfg.dst_addr = res->start + 4042fe35968SSimon Horman (CTL_SD_DATA_PORT << host->bus_shift); 405058db286SMasahiro Yamada cfg.dst_addr_width = priv->dma_priv.dma_buswidth; 406c2a96987SSimon Horman if (!cfg.dst_addr_width) 407c2a96987SSimon Horman cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 408c2a96987SSimon Horman cfg.src_addr = 0; 409c2a96987SSimon Horman ret = dmaengine_slave_config(host->chan_tx, &cfg); 410c2a96987SSimon Horman if (ret < 0) 411c2a96987SSimon Horman goto ecfgtx; 412c2a96987SSimon Horman 413c2a96987SSimon Horman host->chan_rx = dma_request_slave_channel_compat(mask, 414058db286SMasahiro Yamada priv->dma_priv.filter, pdata->chan_priv_rx, 415c2a96987SSimon Horman &host->pdev->dev, "rx"); 416c2a96987SSimon Horman dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__, 417c2a96987SSimon Horman host->chan_rx); 418c2a96987SSimon Horman 419c2a96987SSimon Horman if (!host->chan_rx) 420c2a96987SSimon Horman goto ereqrx; 421c2a96987SSimon Horman 422c2a96987SSimon Horman cfg.direction = DMA_DEV_TO_MEM; 423c2a96987SSimon Horman cfg.src_addr = cfg.dst_addr + host->pdata->dma_rx_offset; 424058db286SMasahiro Yamada cfg.src_addr_width = priv->dma_priv.dma_buswidth; 425c2a96987SSimon Horman if (!cfg.src_addr_width) 426c2a96987SSimon Horman cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 427c2a96987SSimon Horman cfg.dst_addr = 0; 428c2a96987SSimon Horman ret = dmaengine_slave_config(host->chan_rx, &cfg); 429c2a96987SSimon Horman if (ret < 0) 430c2a96987SSimon Horman goto ecfgrx; 431c2a96987SSimon Horman 432c2a96987SSimon Horman host->bounce_buf = (u8 *)__get_free_page(GFP_KERNEL | GFP_DMA); 433c2a96987SSimon Horman if (!host->bounce_buf) 434c2a96987SSimon Horman goto ebouncebuf; 435c2a96987SSimon Horman 43690d95106SMasahiro Yamada init_completion(&priv->dma_priv.dma_dataend); 437c2a96987SSimon Horman tasklet_init(&host->dma_issue, 438c2a96987SSimon Horman renesas_sdhi_sys_dmac_issue_tasklet_fn, 439c2a96987SSimon Horman (unsigned long)host); 440c2a96987SSimon Horman } 441c2a96987SSimon Horman 442c2a96987SSimon Horman renesas_sdhi_sys_dmac_enable_dma(host, true); 443c2a96987SSimon Horman 444c2a96987SSimon Horman return; 445c2a96987SSimon Horman 446c2a96987SSimon Horman ebouncebuf: 447c2a96987SSimon Horman ecfgrx: 448c2a96987SSimon Horman dma_release_channel(host->chan_rx); 449c2a96987SSimon Horman host->chan_rx = NULL; 450c2a96987SSimon Horman ereqrx: 451c2a96987SSimon Horman ecfgtx: 452c2a96987SSimon Horman dma_release_channel(host->chan_tx); 453c2a96987SSimon Horman host->chan_tx = NULL; 454c2a96987SSimon Horman } 455c2a96987SSimon Horman 456c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_release_dma(struct tmio_mmc_host *host) 457c2a96987SSimon Horman { 458c2a96987SSimon Horman if (host->chan_tx) { 459c2a96987SSimon Horman struct dma_chan *chan = host->chan_tx; 4602fe35968SSimon Horman 461c2a96987SSimon Horman host->chan_tx = NULL; 462c2a96987SSimon Horman dma_release_channel(chan); 463c2a96987SSimon Horman } 464c2a96987SSimon Horman if (host->chan_rx) { 465c2a96987SSimon Horman struct dma_chan *chan = host->chan_rx; 4662fe35968SSimon Horman 467c2a96987SSimon Horman host->chan_rx = NULL; 468c2a96987SSimon Horman dma_release_channel(chan); 469c2a96987SSimon Horman } 470c2a96987SSimon Horman if (host->bounce_buf) { 471c2a96987SSimon Horman free_pages((unsigned long)host->bounce_buf, 0); 472c2a96987SSimon Horman host->bounce_buf = NULL; 473c2a96987SSimon Horman } 474c2a96987SSimon Horman } 475c2a96987SSimon Horman 476c2a96987SSimon Horman static const struct tmio_mmc_dma_ops renesas_sdhi_sys_dmac_dma_ops = { 477c2a96987SSimon Horman .start = renesas_sdhi_sys_dmac_start_dma, 478c2a96987SSimon Horman .enable = renesas_sdhi_sys_dmac_enable_dma, 479c2a96987SSimon Horman .request = renesas_sdhi_sys_dmac_request_dma, 480c2a96987SSimon Horman .release = renesas_sdhi_sys_dmac_release_dma, 481c2a96987SSimon Horman .abort = renesas_sdhi_sys_dmac_abort_dma, 48292d0f925SSimon Horman .dataend = renesas_sdhi_sys_dmac_dataend_dma, 483c2a96987SSimon Horman }; 484c2a96987SSimon Horman 485cd09780fSSimon Horman /* 486cd09780fSSimon Horman * Whitelist of specific R-Car Gen3 SoC ES versions to use this DMAC 487cd09780fSSimon Horman * implementation. Currently empty as all supported ES versions use 488cd09780fSSimon Horman * the internal DMAC. 489cd09780fSSimon Horman */ 490cd09780fSSimon Horman static const struct soc_device_attribute gen3_soc_whitelist[] = { 491cd09780fSSimon Horman { /* sentinel */ } 492cd09780fSSimon Horman }; 493cd09780fSSimon Horman 4949d08428aSSimon Horman static int renesas_sdhi_sys_dmac_probe(struct platform_device *pdev) 495c2a96987SSimon Horman { 4962ea15030SWolfram Sang if ((of_device_get_match_data(&pdev->dev) == &of_rcar_gen3_compatible || 4972ea15030SWolfram Sang of_device_get_match_data(&pdev->dev) == &of_rcar_r8a7795_compatible) && 498cd09780fSSimon Horman !soc_device_match(gen3_soc_whitelist)) 499cd09780fSSimon Horman return -ENODEV; 500cd09780fSSimon Horman 5019d08428aSSimon Horman return renesas_sdhi_probe(pdev, &renesas_sdhi_sys_dmac_dma_ops); 502c2a96987SSimon Horman } 5039d08428aSSimon Horman 5049d08428aSSimon Horman static const struct dev_pm_ops renesas_sdhi_sys_dmac_dev_pm_ops = { 5059d08428aSSimon Horman SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 5069d08428aSSimon Horman pm_runtime_force_resume) 5079d08428aSSimon Horman SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, 5089d08428aSSimon Horman tmio_mmc_host_runtime_resume, 5099d08428aSSimon Horman NULL) 5109d08428aSSimon Horman }; 5119d08428aSSimon Horman 5129d08428aSSimon Horman static struct platform_driver renesas_sys_dmac_sdhi_driver = { 5139d08428aSSimon Horman .driver = { 5149d08428aSSimon Horman .name = "sh_mobile_sdhi", 5159d08428aSSimon Horman .pm = &renesas_sdhi_sys_dmac_dev_pm_ops, 5169d08428aSSimon Horman .of_match_table = renesas_sdhi_sys_dmac_of_match, 5179d08428aSSimon Horman }, 5189d08428aSSimon Horman .probe = renesas_sdhi_sys_dmac_probe, 5199d08428aSSimon Horman .remove = renesas_sdhi_remove, 5209d08428aSSimon Horman }; 5219d08428aSSimon Horman 5229d08428aSSimon Horman module_platform_driver(renesas_sys_dmac_sdhi_driver); 5239d08428aSSimon Horman 5249d08428aSSimon Horman MODULE_DESCRIPTION("Renesas SDHI driver"); 5259d08428aSSimon Horman MODULE_AUTHOR("Magnus Damm"); 5269d08428aSSimon Horman MODULE_LICENSE("GPL v2"); 5279d08428aSSimon Horman MODULE_ALIAS("platform:sh_mobile_sdhi"); 528