1c2a96987SSimon Horman /* 2c2a96987SSimon Horman * DMA function for TMIO MMC implementations 3c2a96987SSimon Horman * 4c2a96987SSimon Horman * Copyright (C) 2010-2011 Guennadi Liakhovetski 5c2a96987SSimon Horman * 6c2a96987SSimon Horman * This program is free software; you can redistribute it and/or modify 7c2a96987SSimon Horman * it under the terms of the GNU General Public License version 2 as 8c2a96987SSimon Horman * published by the Free Software Foundation. 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> 18c2a96987SSimon Horman #include <linux/pagemap.h> 19c2a96987SSimon Horman #include <linux/scatterlist.h> 20c2a96987SSimon Horman 219d08428aSSimon Horman #include "renesas_sdhi.h" 22c2a96987SSimon Horman #include "tmio_mmc.h" 23c2a96987SSimon Horman 24c2a96987SSimon Horman #define TMIO_MMC_MIN_DMA_LEN 8 25c2a96987SSimon Horman 269d08428aSSimon Horman static const struct renesas_sdhi_of_data of_default_cfg = { 279d08428aSSimon Horman .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT, 289d08428aSSimon Horman }; 299d08428aSSimon Horman 309d08428aSSimon Horman static const struct renesas_sdhi_of_data of_rz_compatible = { 319d08428aSSimon Horman .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_32BIT_DATA_PORT, 329d08428aSSimon Horman .tmio_ocr_mask = MMC_VDD_32_33, 339d08428aSSimon Horman .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, 349d08428aSSimon Horman }; 359d08428aSSimon Horman 369d08428aSSimon Horman static const struct renesas_sdhi_of_data of_rcar_gen1_compatible = { 379d08428aSSimon Horman .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE | 389d08428aSSimon Horman TMIO_MMC_CLK_ACTUAL, 399d08428aSSimon Horman .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, 409d08428aSSimon Horman }; 419d08428aSSimon Horman 429d08428aSSimon Horman /* Definitions for sampling clocks */ 439d08428aSSimon Horman static struct renesas_sdhi_scc rcar_gen2_scc_taps[] = { 449d08428aSSimon Horman { 459d08428aSSimon Horman .clk_rate = 156000000, 469d08428aSSimon Horman .tap = 0x00000703, 479d08428aSSimon Horman }, 489d08428aSSimon Horman { 499d08428aSSimon Horman .clk_rate = 0, 509d08428aSSimon Horman .tap = 0x00000300, 519d08428aSSimon Horman }, 529d08428aSSimon Horman }; 539d08428aSSimon Horman 549d08428aSSimon Horman static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = { 559d08428aSSimon Horman .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE | 569d08428aSSimon Horman TMIO_MMC_CLK_ACTUAL | TMIO_MMC_MIN_RCAR2, 579d08428aSSimon Horman .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, 589d08428aSSimon Horman .dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES, 599d08428aSSimon Horman .dma_rx_offset = 0x2000, 609d08428aSSimon Horman .scc_offset = 0x0300, 619d08428aSSimon Horman .taps = rcar_gen2_scc_taps, 629d08428aSSimon Horman .taps_num = ARRAY_SIZE(rcar_gen2_scc_taps), 639d08428aSSimon Horman }; 649d08428aSSimon Horman 659d08428aSSimon Horman /* Definitions for sampling clocks */ 669d08428aSSimon Horman static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { 679d08428aSSimon Horman { 689d08428aSSimon Horman .clk_rate = 0, 699d08428aSSimon Horman .tap = 0x00000300, 709d08428aSSimon Horman }, 719d08428aSSimon Horman }; 729d08428aSSimon Horman 739d08428aSSimon Horman static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { 749d08428aSSimon Horman .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE | 759d08428aSSimon Horman TMIO_MMC_CLK_ACTUAL | TMIO_MMC_MIN_RCAR2, 769d08428aSSimon Horman .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, 779d08428aSSimon Horman .bus_shift = 2, 789d08428aSSimon Horman .scc_offset = 0x1000, 799d08428aSSimon Horman .taps = rcar_gen3_scc_taps, 809d08428aSSimon Horman .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), 819d08428aSSimon Horman }; 829d08428aSSimon Horman 839d08428aSSimon Horman static const struct of_device_id renesas_sdhi_sys_dmac_of_match[] = { 849d08428aSSimon Horman { .compatible = "renesas,sdhi-shmobile" }, 859d08428aSSimon Horman { .compatible = "renesas,sdhi-sh73a0", .data = &of_default_cfg, }, 869d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a73a4", .data = &of_default_cfg, }, 879d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7740", .data = &of_default_cfg, }, 889d08428aSSimon Horman { .compatible = "renesas,sdhi-r7s72100", .data = &of_rz_compatible, }, 899d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7778", .data = &of_rcar_gen1_compatible, }, 909d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7779", .data = &of_rcar_gen1_compatible, }, 919d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7790", .data = &of_rcar_gen2_compatible, }, 929d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7791", .data = &of_rcar_gen2_compatible, }, 939d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7792", .data = &of_rcar_gen2_compatible, }, 949d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, }, 959d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, }, 969d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, }, 979d08428aSSimon Horman { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, }, 989d08428aSSimon Horman {}, 999d08428aSSimon Horman }; 1009d08428aSSimon Horman MODULE_DEVICE_TABLE(of, renesas_sdhi_sys_dmac_of_match); 1019d08428aSSimon Horman 1029d08428aSSimon Horman 103c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_enable_dma(struct tmio_mmc_host *host, 104c2a96987SSimon Horman bool enable) 105c2a96987SSimon Horman { 106c2a96987SSimon Horman if (!host->chan_tx || !host->chan_rx) 107c2a96987SSimon Horman return; 108c2a96987SSimon Horman 109c2a96987SSimon Horman if (host->dma->enable) 110c2a96987SSimon Horman host->dma->enable(host, enable); 111c2a96987SSimon Horman } 112c2a96987SSimon Horman 113c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_abort_dma(struct tmio_mmc_host *host) 114c2a96987SSimon Horman { 115c2a96987SSimon Horman renesas_sdhi_sys_dmac_enable_dma(host, false); 116c2a96987SSimon Horman 117c2a96987SSimon Horman if (host->chan_rx) 118c2a96987SSimon Horman dmaengine_terminate_all(host->chan_rx); 119c2a96987SSimon Horman if (host->chan_tx) 120c2a96987SSimon Horman dmaengine_terminate_all(host->chan_tx); 121c2a96987SSimon Horman 122c2a96987SSimon Horman renesas_sdhi_sys_dmac_enable_dma(host, true); 123c2a96987SSimon Horman } 124c2a96987SSimon Horman 125c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_dma_callback(void *arg) 126c2a96987SSimon Horman { 127c2a96987SSimon Horman struct tmio_mmc_host *host = arg; 128c2a96987SSimon Horman 129c2a96987SSimon Horman spin_lock_irq(&host->lock); 130c2a96987SSimon Horman 131c2a96987SSimon Horman if (!host->data) 132c2a96987SSimon Horman goto out; 133c2a96987SSimon Horman 134c2a96987SSimon Horman if (host->data->flags & MMC_DATA_READ) 135c2a96987SSimon Horman dma_unmap_sg(host->chan_rx->device->dev, 136c2a96987SSimon Horman host->sg_ptr, host->sg_len, 137c2a96987SSimon Horman DMA_FROM_DEVICE); 138c2a96987SSimon Horman else 139c2a96987SSimon Horman dma_unmap_sg(host->chan_tx->device->dev, 140c2a96987SSimon Horman host->sg_ptr, host->sg_len, 141c2a96987SSimon Horman DMA_TO_DEVICE); 142c2a96987SSimon Horman 143c2a96987SSimon Horman spin_unlock_irq(&host->lock); 144c2a96987SSimon Horman 145c2a96987SSimon Horman wait_for_completion(&host->dma_dataend); 146c2a96987SSimon Horman 147c2a96987SSimon Horman spin_lock_irq(&host->lock); 148c2a96987SSimon Horman tmio_mmc_do_data_irq(host); 149c2a96987SSimon Horman out: 150c2a96987SSimon Horman spin_unlock_irq(&host->lock); 151c2a96987SSimon Horman } 152c2a96987SSimon Horman 153c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_start_dma_rx(struct tmio_mmc_host *host) 154c2a96987SSimon Horman { 155c2a96987SSimon Horman struct scatterlist *sg = host->sg_ptr, *sg_tmp; 156c2a96987SSimon Horman struct dma_async_tx_descriptor *desc = NULL; 157c2a96987SSimon Horman struct dma_chan *chan = host->chan_rx; 158c2a96987SSimon Horman dma_cookie_t cookie; 159c2a96987SSimon Horman int ret, i; 160c2a96987SSimon Horman bool aligned = true, multiple = true; 161c2a96987SSimon Horman unsigned int align = (1 << host->pdata->alignment_shift) - 1; 162c2a96987SSimon Horman 163c2a96987SSimon Horman for_each_sg(sg, sg_tmp, host->sg_len, i) { 164c2a96987SSimon Horman if (sg_tmp->offset & align) 165c2a96987SSimon Horman aligned = false; 166c2a96987SSimon Horman if (sg_tmp->length & align) { 167c2a96987SSimon Horman multiple = false; 168c2a96987SSimon Horman break; 169c2a96987SSimon Horman } 170c2a96987SSimon Horman } 171c2a96987SSimon Horman 172c2a96987SSimon Horman if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_SIZE || 173c2a96987SSimon Horman (align & PAGE_MASK))) || !multiple) { 174c2a96987SSimon Horman ret = -EINVAL; 175c2a96987SSimon Horman goto pio; 176c2a96987SSimon Horman } 177c2a96987SSimon Horman 178c2a96987SSimon Horman if (sg->length < TMIO_MMC_MIN_DMA_LEN) { 179c2a96987SSimon Horman host->force_pio = true; 180c2a96987SSimon Horman return; 181c2a96987SSimon Horman } 182c2a96987SSimon Horman 183c2a96987SSimon Horman tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_RXRDY); 184c2a96987SSimon Horman 185c2a96987SSimon Horman /* The only sg element can be unaligned, use our bounce buffer then */ 186c2a96987SSimon Horman if (!aligned) { 187c2a96987SSimon Horman sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length); 188c2a96987SSimon Horman host->sg_ptr = &host->bounce_sg; 189c2a96987SSimon Horman sg = host->sg_ptr; 190c2a96987SSimon Horman } 191c2a96987SSimon Horman 192c2a96987SSimon Horman ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_FROM_DEVICE); 193c2a96987SSimon Horman if (ret > 0) 194c2a96987SSimon Horman desc = dmaengine_prep_slave_sg(chan, sg, ret, 195c2a96987SSimon Horman DMA_DEV_TO_MEM, DMA_CTRL_ACK); 196c2a96987SSimon Horman 197c2a96987SSimon Horman if (desc) { 198c2a96987SSimon Horman reinit_completion(&host->dma_dataend); 199c2a96987SSimon Horman desc->callback = renesas_sdhi_sys_dmac_dma_callback; 200c2a96987SSimon Horman desc->callback_param = host; 201c2a96987SSimon Horman 202c2a96987SSimon Horman cookie = dmaengine_submit(desc); 203c2a96987SSimon Horman if (cookie < 0) { 204c2a96987SSimon Horman desc = NULL; 205c2a96987SSimon Horman ret = cookie; 206c2a96987SSimon Horman } 207c2a96987SSimon Horman } 208c2a96987SSimon Horman pio: 209c2a96987SSimon Horman if (!desc) { 210c2a96987SSimon Horman /* DMA failed, fall back to PIO */ 211c2a96987SSimon Horman renesas_sdhi_sys_dmac_enable_dma(host, false); 212c2a96987SSimon Horman if (ret >= 0) 213c2a96987SSimon Horman ret = -EIO; 214c2a96987SSimon Horman host->chan_rx = NULL; 215c2a96987SSimon Horman dma_release_channel(chan); 216c2a96987SSimon Horman /* Free the Tx channel too */ 217c2a96987SSimon Horman chan = host->chan_tx; 218c2a96987SSimon Horman if (chan) { 219c2a96987SSimon Horman host->chan_tx = NULL; 220c2a96987SSimon Horman dma_release_channel(chan); 221c2a96987SSimon Horman } 222c2a96987SSimon Horman dev_warn(&host->pdev->dev, 223c2a96987SSimon Horman "DMA failed: %d, falling back to PIO\n", ret); 224c2a96987SSimon Horman } 225c2a96987SSimon Horman } 226c2a96987SSimon Horman 227c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_start_dma_tx(struct tmio_mmc_host *host) 228c2a96987SSimon Horman { 229c2a96987SSimon Horman struct scatterlist *sg = host->sg_ptr, *sg_tmp; 230c2a96987SSimon Horman struct dma_async_tx_descriptor *desc = NULL; 231c2a96987SSimon Horman struct dma_chan *chan = host->chan_tx; 232c2a96987SSimon Horman dma_cookie_t cookie; 233c2a96987SSimon Horman int ret, i; 234c2a96987SSimon Horman bool aligned = true, multiple = true; 235c2a96987SSimon Horman unsigned int align = (1 << host->pdata->alignment_shift) - 1; 236c2a96987SSimon Horman 237c2a96987SSimon Horman for_each_sg(sg, sg_tmp, host->sg_len, i) { 238c2a96987SSimon Horman if (sg_tmp->offset & align) 239c2a96987SSimon Horman aligned = false; 240c2a96987SSimon Horman if (sg_tmp->length & align) { 241c2a96987SSimon Horman multiple = false; 242c2a96987SSimon Horman break; 243c2a96987SSimon Horman } 244c2a96987SSimon Horman } 245c2a96987SSimon Horman 246c2a96987SSimon Horman if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_SIZE || 247c2a96987SSimon Horman (align & PAGE_MASK))) || !multiple) { 248c2a96987SSimon Horman ret = -EINVAL; 249c2a96987SSimon Horman goto pio; 250c2a96987SSimon Horman } 251c2a96987SSimon Horman 252c2a96987SSimon Horman if (sg->length < TMIO_MMC_MIN_DMA_LEN) { 253c2a96987SSimon Horman host->force_pio = true; 254c2a96987SSimon Horman return; 255c2a96987SSimon Horman } 256c2a96987SSimon Horman 257c2a96987SSimon Horman tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_TXRQ); 258c2a96987SSimon Horman 259c2a96987SSimon Horman /* The only sg element can be unaligned, use our bounce buffer then */ 260c2a96987SSimon Horman if (!aligned) { 261c2a96987SSimon Horman unsigned long flags; 262c2a96987SSimon Horman void *sg_vaddr = tmio_mmc_kmap_atomic(sg, &flags); 263c2a96987SSimon Horman sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length); 264c2a96987SSimon Horman memcpy(host->bounce_buf, sg_vaddr, host->bounce_sg.length); 265c2a96987SSimon Horman tmio_mmc_kunmap_atomic(sg, &flags, sg_vaddr); 266c2a96987SSimon Horman host->sg_ptr = &host->bounce_sg; 267c2a96987SSimon Horman sg = host->sg_ptr; 268c2a96987SSimon Horman } 269c2a96987SSimon Horman 270c2a96987SSimon Horman ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_TO_DEVICE); 271c2a96987SSimon Horman if (ret > 0) 272c2a96987SSimon Horman desc = dmaengine_prep_slave_sg(chan, sg, ret, 273c2a96987SSimon Horman DMA_MEM_TO_DEV, DMA_CTRL_ACK); 274c2a96987SSimon Horman 275c2a96987SSimon Horman if (desc) { 276c2a96987SSimon Horman reinit_completion(&host->dma_dataend); 277c2a96987SSimon Horman desc->callback = renesas_sdhi_sys_dmac_dma_callback; 278c2a96987SSimon Horman desc->callback_param = host; 279c2a96987SSimon Horman 280c2a96987SSimon Horman cookie = dmaengine_submit(desc); 281c2a96987SSimon Horman if (cookie < 0) { 282c2a96987SSimon Horman desc = NULL; 283c2a96987SSimon Horman ret = cookie; 284c2a96987SSimon Horman } 285c2a96987SSimon Horman } 286c2a96987SSimon Horman pio: 287c2a96987SSimon Horman if (!desc) { 288c2a96987SSimon Horman /* DMA failed, fall back to PIO */ 289c2a96987SSimon Horman renesas_sdhi_sys_dmac_enable_dma(host, false); 290c2a96987SSimon Horman if (ret >= 0) 291c2a96987SSimon Horman ret = -EIO; 292c2a96987SSimon Horman host->chan_tx = NULL; 293c2a96987SSimon Horman dma_release_channel(chan); 294c2a96987SSimon Horman /* Free the Rx channel too */ 295c2a96987SSimon Horman chan = host->chan_rx; 296c2a96987SSimon Horman if (chan) { 297c2a96987SSimon Horman host->chan_rx = NULL; 298c2a96987SSimon Horman dma_release_channel(chan); 299c2a96987SSimon Horman } 300c2a96987SSimon Horman dev_warn(&host->pdev->dev, 301c2a96987SSimon Horman "DMA failed: %d, falling back to PIO\n", ret); 302c2a96987SSimon Horman } 303c2a96987SSimon Horman } 304c2a96987SSimon Horman 305c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_start_dma(struct tmio_mmc_host *host, 306c2a96987SSimon Horman struct mmc_data *data) 307c2a96987SSimon Horman { 308c2a96987SSimon Horman if (data->flags & MMC_DATA_READ) { 309c2a96987SSimon Horman if (host->chan_rx) 310c2a96987SSimon Horman renesas_sdhi_sys_dmac_start_dma_rx(host); 311c2a96987SSimon Horman } else { 312c2a96987SSimon Horman if (host->chan_tx) 313c2a96987SSimon Horman renesas_sdhi_sys_dmac_start_dma_tx(host); 314c2a96987SSimon Horman } 315c2a96987SSimon Horman } 316c2a96987SSimon Horman 317c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_issue_tasklet_fn(unsigned long priv) 318c2a96987SSimon Horman { 319c2a96987SSimon Horman struct tmio_mmc_host *host = (struct tmio_mmc_host *)priv; 320c2a96987SSimon Horman struct dma_chan *chan = NULL; 321c2a96987SSimon Horman 322c2a96987SSimon Horman spin_lock_irq(&host->lock); 323c2a96987SSimon Horman 324c2a96987SSimon Horman if (host && host->data) { 325c2a96987SSimon Horman if (host->data->flags & MMC_DATA_READ) 326c2a96987SSimon Horman chan = host->chan_rx; 327c2a96987SSimon Horman else 328c2a96987SSimon Horman chan = host->chan_tx; 329c2a96987SSimon Horman } 330c2a96987SSimon Horman 331c2a96987SSimon Horman spin_unlock_irq(&host->lock); 332c2a96987SSimon Horman 333c2a96987SSimon Horman tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND); 334c2a96987SSimon Horman 335c2a96987SSimon Horman if (chan) 336c2a96987SSimon Horman dma_async_issue_pending(chan); 337c2a96987SSimon Horman } 338c2a96987SSimon Horman 339c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_request_dma(struct tmio_mmc_host *host, 340c2a96987SSimon Horman struct tmio_mmc_data *pdata) 341c2a96987SSimon Horman { 342c2a96987SSimon Horman /* We can only either use DMA for both Tx and Rx or not use it at all */ 343c2a96987SSimon Horman if (!host->dma || (!host->pdev->dev.of_node && 344c2a96987SSimon Horman (!pdata->chan_priv_tx || !pdata->chan_priv_rx))) 345c2a96987SSimon Horman return; 346c2a96987SSimon Horman 347c2a96987SSimon Horman if (!host->chan_tx && !host->chan_rx) { 348c2a96987SSimon Horman struct resource *res = platform_get_resource(host->pdev, 349c2a96987SSimon Horman IORESOURCE_MEM, 0); 350c2a96987SSimon Horman struct dma_slave_config cfg = {}; 351c2a96987SSimon Horman dma_cap_mask_t mask; 352c2a96987SSimon Horman int ret; 353c2a96987SSimon Horman 354c2a96987SSimon Horman if (!res) 355c2a96987SSimon Horman return; 356c2a96987SSimon Horman 357c2a96987SSimon Horman dma_cap_zero(mask); 358c2a96987SSimon Horman dma_cap_set(DMA_SLAVE, mask); 359c2a96987SSimon Horman 360c2a96987SSimon Horman host->chan_tx = dma_request_slave_channel_compat(mask, 361c2a96987SSimon Horman host->dma->filter, pdata->chan_priv_tx, 362c2a96987SSimon Horman &host->pdev->dev, "tx"); 363c2a96987SSimon Horman dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__, 364c2a96987SSimon Horman host->chan_tx); 365c2a96987SSimon Horman 366c2a96987SSimon Horman if (!host->chan_tx) 367c2a96987SSimon Horman return; 368c2a96987SSimon Horman 369c2a96987SSimon Horman cfg.direction = DMA_MEM_TO_DEV; 370c2a96987SSimon Horman cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift); 371c2a96987SSimon Horman cfg.dst_addr_width = host->dma->dma_buswidth; 372c2a96987SSimon Horman if (!cfg.dst_addr_width) 373c2a96987SSimon Horman cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 374c2a96987SSimon Horman cfg.src_addr = 0; 375c2a96987SSimon Horman ret = dmaengine_slave_config(host->chan_tx, &cfg); 376c2a96987SSimon Horman if (ret < 0) 377c2a96987SSimon Horman goto ecfgtx; 378c2a96987SSimon Horman 379c2a96987SSimon Horman host->chan_rx = dma_request_slave_channel_compat(mask, 380c2a96987SSimon Horman host->dma->filter, pdata->chan_priv_rx, 381c2a96987SSimon Horman &host->pdev->dev, "rx"); 382c2a96987SSimon Horman dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__, 383c2a96987SSimon Horman host->chan_rx); 384c2a96987SSimon Horman 385c2a96987SSimon Horman if (!host->chan_rx) 386c2a96987SSimon Horman goto ereqrx; 387c2a96987SSimon Horman 388c2a96987SSimon Horman cfg.direction = DMA_DEV_TO_MEM; 389c2a96987SSimon Horman cfg.src_addr = cfg.dst_addr + host->pdata->dma_rx_offset; 390c2a96987SSimon Horman cfg.src_addr_width = host->dma->dma_buswidth; 391c2a96987SSimon Horman if (!cfg.src_addr_width) 392c2a96987SSimon Horman cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 393c2a96987SSimon Horman cfg.dst_addr = 0; 394c2a96987SSimon Horman ret = dmaengine_slave_config(host->chan_rx, &cfg); 395c2a96987SSimon Horman if (ret < 0) 396c2a96987SSimon Horman goto ecfgrx; 397c2a96987SSimon Horman 398c2a96987SSimon Horman host->bounce_buf = (u8 *)__get_free_page(GFP_KERNEL | GFP_DMA); 399c2a96987SSimon Horman if (!host->bounce_buf) 400c2a96987SSimon Horman goto ebouncebuf; 401c2a96987SSimon Horman 402c2a96987SSimon Horman init_completion(&host->dma_dataend); 403c2a96987SSimon Horman tasklet_init(&host->dma_issue, 404c2a96987SSimon Horman renesas_sdhi_sys_dmac_issue_tasklet_fn, 405c2a96987SSimon Horman (unsigned long)host); 406c2a96987SSimon Horman } 407c2a96987SSimon Horman 408c2a96987SSimon Horman renesas_sdhi_sys_dmac_enable_dma(host, true); 409c2a96987SSimon Horman 410c2a96987SSimon Horman return; 411c2a96987SSimon Horman 412c2a96987SSimon Horman ebouncebuf: 413c2a96987SSimon Horman ecfgrx: 414c2a96987SSimon Horman dma_release_channel(host->chan_rx); 415c2a96987SSimon Horman host->chan_rx = NULL; 416c2a96987SSimon Horman ereqrx: 417c2a96987SSimon Horman ecfgtx: 418c2a96987SSimon Horman dma_release_channel(host->chan_tx); 419c2a96987SSimon Horman host->chan_tx = NULL; 420c2a96987SSimon Horman } 421c2a96987SSimon Horman 422c2a96987SSimon Horman static void renesas_sdhi_sys_dmac_release_dma(struct tmio_mmc_host *host) 423c2a96987SSimon Horman { 424c2a96987SSimon Horman if (host->chan_tx) { 425c2a96987SSimon Horman struct dma_chan *chan = host->chan_tx; 426c2a96987SSimon Horman host->chan_tx = NULL; 427c2a96987SSimon Horman dma_release_channel(chan); 428c2a96987SSimon Horman } 429c2a96987SSimon Horman if (host->chan_rx) { 430c2a96987SSimon Horman struct dma_chan *chan = host->chan_rx; 431c2a96987SSimon Horman host->chan_rx = NULL; 432c2a96987SSimon Horman dma_release_channel(chan); 433c2a96987SSimon Horman } 434c2a96987SSimon Horman if (host->bounce_buf) { 435c2a96987SSimon Horman free_pages((unsigned long)host->bounce_buf, 0); 436c2a96987SSimon Horman host->bounce_buf = NULL; 437c2a96987SSimon Horman } 438c2a96987SSimon Horman } 439c2a96987SSimon Horman 440c2a96987SSimon Horman static const struct tmio_mmc_dma_ops renesas_sdhi_sys_dmac_dma_ops = { 441c2a96987SSimon Horman .start = renesas_sdhi_sys_dmac_start_dma, 442c2a96987SSimon Horman .enable = renesas_sdhi_sys_dmac_enable_dma, 443c2a96987SSimon Horman .request = renesas_sdhi_sys_dmac_request_dma, 444c2a96987SSimon Horman .release = renesas_sdhi_sys_dmac_release_dma, 445c2a96987SSimon Horman .abort = renesas_sdhi_sys_dmac_abort_dma, 446c2a96987SSimon Horman }; 447c2a96987SSimon Horman 4489d08428aSSimon Horman static int renesas_sdhi_sys_dmac_probe(struct platform_device *pdev) 449c2a96987SSimon Horman { 4509d08428aSSimon Horman return renesas_sdhi_probe(pdev, &renesas_sdhi_sys_dmac_dma_ops); 451c2a96987SSimon Horman } 4529d08428aSSimon Horman 4539d08428aSSimon Horman static const struct dev_pm_ops renesas_sdhi_sys_dmac_dev_pm_ops = { 4549d08428aSSimon Horman SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 4559d08428aSSimon Horman pm_runtime_force_resume) 4569d08428aSSimon Horman SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, 4579d08428aSSimon Horman tmio_mmc_host_runtime_resume, 4589d08428aSSimon Horman NULL) 4599d08428aSSimon Horman }; 4609d08428aSSimon Horman 4619d08428aSSimon Horman static struct platform_driver renesas_sys_dmac_sdhi_driver = { 4629d08428aSSimon Horman .driver = { 4639d08428aSSimon Horman .name = "sh_mobile_sdhi", 4649d08428aSSimon Horman .pm = &renesas_sdhi_sys_dmac_dev_pm_ops, 4659d08428aSSimon Horman .of_match_table = renesas_sdhi_sys_dmac_of_match, 4669d08428aSSimon Horman }, 4679d08428aSSimon Horman .probe = renesas_sdhi_sys_dmac_probe, 4689d08428aSSimon Horman .remove = renesas_sdhi_remove, 4699d08428aSSimon Horman }; 4709d08428aSSimon Horman 4719d08428aSSimon Horman module_platform_driver(renesas_sys_dmac_sdhi_driver); 4729d08428aSSimon Horman 4739d08428aSSimon Horman MODULE_DESCRIPTION("Renesas SDHI driver"); 4749d08428aSSimon Horman MODULE_AUTHOR("Magnus Damm"); 4759d08428aSSimon Horman MODULE_LICENSE("GPL v2"); 4769d08428aSSimon Horman MODULE_ALIAS("platform:sh_mobile_sdhi"); 477