Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 1 | /* |
Simon Horman | 2a68ea7 | 2017-06-21 16:00:29 +0200 | [diff] [blame] | 2 | * DMA support use of SYS DMAC with SDHI SD/SDIO controller |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 3 | * |
Simon Horman | 87317c4 | 2017-05-30 14:50:52 +0200 | [diff] [blame] | 4 | * Copyright (C) 2016-17 Renesas Electronics Corporation |
| 5 | * Copyright (C) 2016-17 Sang Engineering, Wolfram Sang |
| 6 | * Copyright (C) 2017 Horms Solutions, Simon Horman |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 7 | * Copyright (C) 2010-2011 Guennadi Liakhovetski |
| 8 | * |
| 9 | * This program is free software; you can redistribute it and/or modify |
| 10 | * it under the terms of the GNU General Public License version 2 as |
| 11 | * published by the Free Software Foundation. |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 12 | */ |
| 13 | |
| 14 | #include <linux/device.h> |
Alexey Dobriyan | b7f080c | 2011-06-16 11:01:34 +0000 | [diff] [blame] | 15 | #include <linux/dma-mapping.h> |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 16 | #include <linux/dmaengine.h> |
| 17 | #include <linux/mfd/tmio.h> |
| 18 | #include <linux/mmc/host.h> |
Simon Horman | 9d08428 | 2017-05-10 11:25:30 +0200 | [diff] [blame] | 19 | #include <linux/mod_devicetable.h> |
| 20 | #include <linux/module.h> |
Simon Horman | cd09780 | 2017-08-02 14:48:42 +0200 | [diff] [blame] | 21 | #include <linux/of_device.h> |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 22 | #include <linux/pagemap.h> |
| 23 | #include <linux/scatterlist.h> |
Simon Horman | cd09780 | 2017-08-02 14:48:42 +0200 | [diff] [blame] | 24 | #include <linux/sys_soc.h> |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 25 | |
Simon Horman | 9d08428 | 2017-05-10 11:25:30 +0200 | [diff] [blame] | 26 | #include "renesas_sdhi.h" |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 27 | #include "tmio_mmc.h" |
| 28 | |
| 29 | #define TMIO_MMC_MIN_DMA_LEN 8 |
| 30 | |
Simon Horman | 9d08428 | 2017-05-10 11:25:30 +0200 | [diff] [blame] | 31 | static const struct renesas_sdhi_of_data of_default_cfg = { |
| 32 | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT, |
| 33 | }; |
| 34 | |
| 35 | static const struct renesas_sdhi_of_data of_rz_compatible = { |
Wolfram Sang | 92b7db8 | 2017-08-09 21:14:51 +0200 | [diff] [blame] | 36 | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_32BIT_DATA_PORT | |
| 37 | TMIO_MMC_HAVE_CBSY, |
Simon Horman | 9d08428 | 2017-05-10 11:25:30 +0200 | [diff] [blame] | 38 | .tmio_ocr_mask = MMC_VDD_32_33, |
| 39 | .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, |
| 40 | }; |
| 41 | |
| 42 | static const struct renesas_sdhi_of_data of_rcar_gen1_compatible = { |
| 43 | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE | |
| 44 | TMIO_MMC_CLK_ACTUAL, |
| 45 | .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, |
| 46 | }; |
| 47 | |
| 48 | /* Definitions for sampling clocks */ |
| 49 | static struct renesas_sdhi_scc rcar_gen2_scc_taps[] = { |
| 50 | { |
| 51 | .clk_rate = 156000000, |
| 52 | .tap = 0x00000703, |
| 53 | }, |
| 54 | { |
| 55 | .clk_rate = 0, |
| 56 | .tap = 0x00000300, |
| 57 | }, |
| 58 | }; |
| 59 | |
| 60 | static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = { |
| 61 | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE | |
Wolfram Sang | 5124b59 | 2017-08-09 21:00:41 +0200 | [diff] [blame] | 62 | TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY | |
| 63 | TMIO_MMC_MIN_RCAR2, |
Wolfram Sang | 921579b | 2017-05-19 15:31:55 +0200 | [diff] [blame] | 64 | .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | |
| 65 | MMC_CAP_CMD23, |
Simon Horman | 9d08428 | 2017-05-10 11:25:30 +0200 | [diff] [blame] | 66 | .dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES, |
| 67 | .dma_rx_offset = 0x2000, |
| 68 | .scc_offset = 0x0300, |
| 69 | .taps = rcar_gen2_scc_taps, |
| 70 | .taps_num = ARRAY_SIZE(rcar_gen2_scc_taps), |
| 71 | }; |
| 72 | |
| 73 | /* Definitions for sampling clocks */ |
| 74 | static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { |
| 75 | { |
| 76 | .clk_rate = 0, |
| 77 | .tap = 0x00000300, |
| 78 | }, |
| 79 | }; |
| 80 | |
| 81 | static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { |
| 82 | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE | |
Wolfram Sang | 5124b59 | 2017-08-09 21:00:41 +0200 | [diff] [blame] | 83 | TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY | |
| 84 | TMIO_MMC_MIN_RCAR2, |
Wolfram Sang | 921579b | 2017-05-19 15:31:55 +0200 | [diff] [blame] | 85 | .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | |
| 86 | MMC_CAP_CMD23, |
Simon Horman | 9d08428 | 2017-05-10 11:25:30 +0200 | [diff] [blame] | 87 | .bus_shift = 2, |
| 88 | .scc_offset = 0x1000, |
| 89 | .taps = rcar_gen3_scc_taps, |
| 90 | .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), |
| 91 | }; |
| 92 | |
| 93 | static const struct of_device_id renesas_sdhi_sys_dmac_of_match[] = { |
| 94 | { .compatible = "renesas,sdhi-shmobile" }, |
| 95 | { .compatible = "renesas,sdhi-sh73a0", .data = &of_default_cfg, }, |
| 96 | { .compatible = "renesas,sdhi-r8a73a4", .data = &of_default_cfg, }, |
| 97 | { .compatible = "renesas,sdhi-r8a7740", .data = &of_default_cfg, }, |
| 98 | { .compatible = "renesas,sdhi-r7s72100", .data = &of_rz_compatible, }, |
| 99 | { .compatible = "renesas,sdhi-r8a7778", .data = &of_rcar_gen1_compatible, }, |
| 100 | { .compatible = "renesas,sdhi-r8a7779", .data = &of_rcar_gen1_compatible, }, |
Biju Das | c16a854 | 2017-08-29 14:52:06 +0100 | [diff] [blame] | 101 | { .compatible = "renesas,sdhi-r8a7743", .data = &of_rcar_gen2_compatible, }, |
| 102 | { .compatible = "renesas,sdhi-r8a7745", .data = &of_rcar_gen2_compatible, }, |
Simon Horman | 9d08428 | 2017-05-10 11:25:30 +0200 | [diff] [blame] | 103 | { .compatible = "renesas,sdhi-r8a7790", .data = &of_rcar_gen2_compatible, }, |
| 104 | { .compatible = "renesas,sdhi-r8a7791", .data = &of_rcar_gen2_compatible, }, |
| 105 | { .compatible = "renesas,sdhi-r8a7792", .data = &of_rcar_gen2_compatible, }, |
| 106 | { .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, }, |
| 107 | { .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, }, |
| 108 | { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, }, |
| 109 | { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, }, |
| 110 | {}, |
| 111 | }; |
| 112 | MODULE_DEVICE_TABLE(of, renesas_sdhi_sys_dmac_of_match); |
| 113 | |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 114 | static void renesas_sdhi_sys_dmac_enable_dma(struct tmio_mmc_host *host, |
| 115 | bool enable) |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 116 | { |
Guennadi Liakhovetski | 162f43e | 2011-07-14 18:39:10 +0200 | [diff] [blame] | 117 | if (!host->chan_tx || !host->chan_rx) |
| 118 | return; |
| 119 | |
Kuninori Morimoto | 5add2ac | 2015-01-13 04:59:05 +0000 | [diff] [blame] | 120 | if (host->dma->enable) |
| 121 | host->dma->enable(host, enable); |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 122 | } |
| 123 | |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 124 | static void renesas_sdhi_sys_dmac_abort_dma(struct tmio_mmc_host *host) |
Guennadi Liakhovetski | e3de2be | 2012-01-06 13:06:51 +0100 | [diff] [blame] | 125 | { |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 126 | renesas_sdhi_sys_dmac_enable_dma(host, false); |
Guennadi Liakhovetski | e3de2be | 2012-01-06 13:06:51 +0100 | [diff] [blame] | 127 | |
| 128 | if (host->chan_rx) |
| 129 | dmaengine_terminate_all(host->chan_rx); |
| 130 | if (host->chan_tx) |
| 131 | dmaengine_terminate_all(host->chan_tx); |
| 132 | |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 133 | renesas_sdhi_sys_dmac_enable_dma(host, true); |
Guennadi Liakhovetski | e3de2be | 2012-01-06 13:06:51 +0100 | [diff] [blame] | 134 | } |
| 135 | |
Simon Horman | 92d0f925e | 2017-06-21 16:00:28 +0200 | [diff] [blame] | 136 | static void renesas_sdhi_sys_dmac_dataend_dma(struct tmio_mmc_host *host) |
| 137 | { |
| 138 | complete(&host->dma_dataend); |
| 139 | } |
| 140 | |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 141 | static void renesas_sdhi_sys_dmac_dma_callback(void *arg) |
Wolfram Sang | 52ad9a8 | 2017-02-17 19:22:41 +0100 | [diff] [blame] | 142 | { |
| 143 | struct tmio_mmc_host *host = arg; |
| 144 | |
Wolfram Sang | 52ad9a8 | 2017-02-17 19:22:41 +0100 | [diff] [blame] | 145 | spin_lock_irq(&host->lock); |
| 146 | |
| 147 | if (!host->data) |
| 148 | goto out; |
| 149 | |
| 150 | if (host->data->flags & MMC_DATA_READ) |
| 151 | dma_unmap_sg(host->chan_rx->device->dev, |
| 152 | host->sg_ptr, host->sg_len, |
| 153 | DMA_FROM_DEVICE); |
| 154 | else |
| 155 | dma_unmap_sg(host->chan_tx->device->dev, |
| 156 | host->sg_ptr, host->sg_len, |
| 157 | DMA_TO_DEVICE); |
| 158 | |
Wolfram Sang | 5f07ef8 | 2017-03-16 11:56:02 +0100 | [diff] [blame] | 159 | spin_unlock_irq(&host->lock); |
| 160 | |
| 161 | wait_for_completion(&host->dma_dataend); |
| 162 | |
| 163 | spin_lock_irq(&host->lock); |
Wolfram Sang | 52ad9a8 | 2017-02-17 19:22:41 +0100 | [diff] [blame] | 164 | tmio_mmc_do_data_irq(host); |
| 165 | out: |
| 166 | spin_unlock_irq(&host->lock); |
| 167 | } |
| 168 | |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 169 | static void renesas_sdhi_sys_dmac_start_dma_rx(struct tmio_mmc_host *host) |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 170 | { |
| 171 | struct scatterlist *sg = host->sg_ptr, *sg_tmp; |
| 172 | struct dma_async_tx_descriptor *desc = NULL; |
| 173 | struct dma_chan *chan = host->chan_rx; |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 174 | dma_cookie_t cookie; |
| 175 | int ret, i; |
| 176 | bool aligned = true, multiple = true; |
Kuninori Morimoto | e471df0 | 2015-01-13 04:58:46 +0000 | [diff] [blame] | 177 | unsigned int align = (1 << host->pdata->alignment_shift) - 1; |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 178 | |
| 179 | for_each_sg(sg, sg_tmp, host->sg_len, i) { |
| 180 | if (sg_tmp->offset & align) |
| 181 | aligned = false; |
| 182 | if (sg_tmp->length & align) { |
| 183 | multiple = false; |
| 184 | break; |
| 185 | } |
| 186 | } |
| 187 | |
Kirill A. Shutemov | 09cbfea | 2016-04-01 15:29:47 +0300 | [diff] [blame] | 188 | if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_SIZE || |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 189 | (align & PAGE_MASK))) || !multiple) { |
| 190 | ret = -EINVAL; |
| 191 | goto pio; |
| 192 | } |
| 193 | |
| 194 | if (sg->length < TMIO_MMC_MIN_DMA_LEN) { |
| 195 | host->force_pio = true; |
| 196 | return; |
| 197 | } |
| 198 | |
| 199 | tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_RXRDY); |
| 200 | |
| 201 | /* The only sg element can be unaligned, use our bounce buffer then */ |
| 202 | if (!aligned) { |
| 203 | sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length); |
| 204 | host->sg_ptr = &host->bounce_sg; |
| 205 | sg = host->sg_ptr; |
| 206 | } |
| 207 | |
| 208 | ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_FROM_DEVICE); |
| 209 | if (ret > 0) |
Simon Horman | 2fe3596 | 2017-06-16 18:11:04 +0200 | [diff] [blame] | 210 | desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_DEV_TO_MEM, |
| 211 | DMA_CTRL_ACK); |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 212 | |
| 213 | if (desc) { |
Wolfram Sang | 52ad9a8 | 2017-02-17 19:22:41 +0100 | [diff] [blame] | 214 | reinit_completion(&host->dma_dataend); |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 215 | desc->callback = renesas_sdhi_sys_dmac_dma_callback; |
Wolfram Sang | 52ad9a8 | 2017-02-17 19:22:41 +0100 | [diff] [blame] | 216 | desc->callback_param = host; |
| 217 | |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 218 | cookie = dmaengine_submit(desc); |
| 219 | if (cookie < 0) { |
| 220 | desc = NULL; |
| 221 | ret = cookie; |
| 222 | } |
| 223 | } |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 224 | pio: |
| 225 | if (!desc) { |
| 226 | /* DMA failed, fall back to PIO */ |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 227 | renesas_sdhi_sys_dmac_enable_dma(host, false); |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 228 | if (ret >= 0) |
| 229 | ret = -EIO; |
| 230 | host->chan_rx = NULL; |
| 231 | dma_release_channel(chan); |
| 232 | /* Free the Tx channel too */ |
| 233 | chan = host->chan_tx; |
| 234 | if (chan) { |
| 235 | host->chan_tx = NULL; |
| 236 | dma_release_channel(chan); |
| 237 | } |
| 238 | dev_warn(&host->pdev->dev, |
| 239 | "DMA failed: %d, falling back to PIO\n", ret); |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 240 | } |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 241 | } |
| 242 | |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 243 | static void renesas_sdhi_sys_dmac_start_dma_tx(struct tmio_mmc_host *host) |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 244 | { |
| 245 | struct scatterlist *sg = host->sg_ptr, *sg_tmp; |
| 246 | struct dma_async_tx_descriptor *desc = NULL; |
| 247 | struct dma_chan *chan = host->chan_tx; |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 248 | dma_cookie_t cookie; |
| 249 | int ret, i; |
| 250 | bool aligned = true, multiple = true; |
Kuninori Morimoto | e471df0 | 2015-01-13 04:58:46 +0000 | [diff] [blame] | 251 | unsigned int align = (1 << host->pdata->alignment_shift) - 1; |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 252 | |
| 253 | for_each_sg(sg, sg_tmp, host->sg_len, i) { |
| 254 | if (sg_tmp->offset & align) |
| 255 | aligned = false; |
| 256 | if (sg_tmp->length & align) { |
| 257 | multiple = false; |
| 258 | break; |
| 259 | } |
| 260 | } |
| 261 | |
Kirill A. Shutemov | 09cbfea | 2016-04-01 15:29:47 +0300 | [diff] [blame] | 262 | if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_SIZE || |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 263 | (align & PAGE_MASK))) || !multiple) { |
| 264 | ret = -EINVAL; |
| 265 | goto pio; |
| 266 | } |
| 267 | |
| 268 | if (sg->length < TMIO_MMC_MIN_DMA_LEN) { |
| 269 | host->force_pio = true; |
| 270 | return; |
| 271 | } |
| 272 | |
| 273 | tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_TXRQ); |
| 274 | |
| 275 | /* The only sg element can be unaligned, use our bounce buffer then */ |
| 276 | if (!aligned) { |
| 277 | unsigned long flags; |
| 278 | void *sg_vaddr = tmio_mmc_kmap_atomic(sg, &flags); |
Simon Horman | 2fe3596 | 2017-06-16 18:11:04 +0200 | [diff] [blame] | 279 | |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 280 | sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length); |
| 281 | memcpy(host->bounce_buf, sg_vaddr, host->bounce_sg.length); |
| 282 | tmio_mmc_kunmap_atomic(sg, &flags, sg_vaddr); |
| 283 | host->sg_ptr = &host->bounce_sg; |
| 284 | sg = host->sg_ptr; |
| 285 | } |
| 286 | |
| 287 | ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_TO_DEVICE); |
| 288 | if (ret > 0) |
Simon Horman | 2fe3596 | 2017-06-16 18:11:04 +0200 | [diff] [blame] | 289 | desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_MEM_TO_DEV, |
| 290 | DMA_CTRL_ACK); |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 291 | |
| 292 | if (desc) { |
Wolfram Sang | 52ad9a8 | 2017-02-17 19:22:41 +0100 | [diff] [blame] | 293 | reinit_completion(&host->dma_dataend); |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 294 | desc->callback = renesas_sdhi_sys_dmac_dma_callback; |
Wolfram Sang | 52ad9a8 | 2017-02-17 19:22:41 +0100 | [diff] [blame] | 295 | desc->callback_param = host; |
| 296 | |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 297 | cookie = dmaengine_submit(desc); |
| 298 | if (cookie < 0) { |
| 299 | desc = NULL; |
| 300 | ret = cookie; |
| 301 | } |
| 302 | } |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 303 | pio: |
| 304 | if (!desc) { |
| 305 | /* DMA failed, fall back to PIO */ |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 306 | renesas_sdhi_sys_dmac_enable_dma(host, false); |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 307 | if (ret >= 0) |
| 308 | ret = -EIO; |
| 309 | host->chan_tx = NULL; |
| 310 | dma_release_channel(chan); |
| 311 | /* Free the Rx channel too */ |
| 312 | chan = host->chan_rx; |
| 313 | if (chan) { |
| 314 | host->chan_rx = NULL; |
| 315 | dma_release_channel(chan); |
| 316 | } |
| 317 | dev_warn(&host->pdev->dev, |
| 318 | "DMA failed: %d, falling back to PIO\n", ret); |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 319 | } |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 320 | } |
| 321 | |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 322 | static void renesas_sdhi_sys_dmac_start_dma(struct tmio_mmc_host *host, |
Simon Horman | 2fe3596 | 2017-06-16 18:11:04 +0200 | [diff] [blame] | 323 | struct mmc_data *data) |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 324 | { |
| 325 | if (data->flags & MMC_DATA_READ) { |
| 326 | if (host->chan_rx) |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 327 | renesas_sdhi_sys_dmac_start_dma_rx(host); |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 328 | } else { |
| 329 | if (host->chan_tx) |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 330 | renesas_sdhi_sys_dmac_start_dma_tx(host); |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 331 | } |
| 332 | } |
| 333 | |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 334 | static void renesas_sdhi_sys_dmac_issue_tasklet_fn(unsigned long priv) |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 335 | { |
| 336 | struct tmio_mmc_host *host = (struct tmio_mmc_host *)priv; |
| 337 | struct dma_chan *chan = NULL; |
| 338 | |
| 339 | spin_lock_irq(&host->lock); |
| 340 | |
| 341 | if (host && host->data) { |
| 342 | if (host->data->flags & MMC_DATA_READ) |
| 343 | chan = host->chan_rx; |
| 344 | else |
| 345 | chan = host->chan_tx; |
| 346 | } |
| 347 | |
| 348 | spin_unlock_irq(&host->lock); |
| 349 | |
| 350 | tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND); |
| 351 | |
| 352 | if (chan) |
| 353 | dma_async_issue_pending(chan); |
| 354 | } |
| 355 | |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 356 | static void renesas_sdhi_sys_dmac_request_dma(struct tmio_mmc_host *host, |
| 357 | struct tmio_mmc_data *pdata) |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 358 | { |
| 359 | /* We can only either use DMA for both Tx and Rx or not use it at all */ |
Kuninori Morimoto | 7ecc09b | 2015-01-13 04:57:33 +0000 | [diff] [blame] | 360 | if (!host->dma || (!host->pdev->dev.of_node && |
Simon Horman | 2fe3596 | 2017-06-16 18:11:04 +0200 | [diff] [blame] | 361 | (!pdata->chan_priv_tx || !pdata->chan_priv_rx))) |
Guennadi Liakhovetski | e6ee718 | 2011-05-05 16:13:12 +0000 | [diff] [blame] | 362 | return; |
| 363 | |
| 364 | if (!host->chan_tx && !host->chan_rx) { |
Guennadi Liakhovetski | eec95ee | 2013-04-26 17:47:18 +0200 | [diff] [blame] | 365 | struct resource *res = platform_get_resource(host->pdev, |
| 366 | IORESOURCE_MEM, 0); |
| 367 | struct dma_slave_config cfg = {}; |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 368 | dma_cap_mask_t mask; |
Guennadi Liakhovetski | eec95ee | 2013-04-26 17:47:18 +0200 | [diff] [blame] | 369 | int ret; |
| 370 | |
| 371 | if (!res) |
| 372 | return; |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 373 | |
| 374 | dma_cap_zero(mask); |
| 375 | dma_cap_set(DMA_SLAVE, mask); |
| 376 | |
Guennadi Liakhovetski | 87ae7bb | 2013-04-26 17:47:19 +0200 | [diff] [blame] | 377 | host->chan_tx = dma_request_slave_channel_compat(mask, |
Kuninori Morimoto | f33c9d6 | 2015-02-24 02:06:43 +0000 | [diff] [blame] | 378 | host->dma->filter, pdata->chan_priv_tx, |
Guennadi Liakhovetski | 87ae7bb | 2013-04-26 17:47:19 +0200 | [diff] [blame] | 379 | &host->pdev->dev, "tx"); |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 380 | dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__, |
| 381 | host->chan_tx); |
| 382 | |
| 383 | if (!host->chan_tx) |
| 384 | return; |
| 385 | |
Guennadi Liakhovetski | eec95ee | 2013-04-26 17:47:18 +0200 | [diff] [blame] | 386 | cfg.direction = DMA_MEM_TO_DEV; |
Simon Horman | 2fe3596 | 2017-06-16 18:11:04 +0200 | [diff] [blame] | 387 | cfg.dst_addr = res->start + |
| 388 | (CTL_SD_DATA_PORT << host->bus_shift); |
Kuninori Morimoto | 361936e | 2015-01-13 04:59:14 +0000 | [diff] [blame] | 389 | cfg.dst_addr_width = host->dma->dma_buswidth; |
| 390 | if (!cfg.dst_addr_width) |
| 391 | cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
Guennadi Liakhovetski | eec95ee | 2013-04-26 17:47:18 +0200 | [diff] [blame] | 392 | cfg.src_addr = 0; |
| 393 | ret = dmaengine_slave_config(host->chan_tx, &cfg); |
| 394 | if (ret < 0) |
| 395 | goto ecfgtx; |
| 396 | |
Guennadi Liakhovetski | 87ae7bb | 2013-04-26 17:47:19 +0200 | [diff] [blame] | 397 | host->chan_rx = dma_request_slave_channel_compat(mask, |
Kuninori Morimoto | f33c9d6 | 2015-02-24 02:06:43 +0000 | [diff] [blame] | 398 | host->dma->filter, pdata->chan_priv_rx, |
Guennadi Liakhovetski | 87ae7bb | 2013-04-26 17:47:19 +0200 | [diff] [blame] | 399 | &host->pdev->dev, "rx"); |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 400 | dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__, |
| 401 | host->chan_rx); |
| 402 | |
| 403 | if (!host->chan_rx) |
| 404 | goto ereqrx; |
| 405 | |
Guennadi Liakhovetski | eec95ee | 2013-04-26 17:47:18 +0200 | [diff] [blame] | 406 | cfg.direction = DMA_DEV_TO_MEM; |
Kuninori Morimoto | 8b4c8f3 | 2015-01-13 04:58:56 +0000 | [diff] [blame] | 407 | cfg.src_addr = cfg.dst_addr + host->pdata->dma_rx_offset; |
Kuninori Morimoto | 361936e | 2015-01-13 04:59:14 +0000 | [diff] [blame] | 408 | cfg.src_addr_width = host->dma->dma_buswidth; |
| 409 | if (!cfg.src_addr_width) |
| 410 | cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
Guennadi Liakhovetski | eec95ee | 2013-04-26 17:47:18 +0200 | [diff] [blame] | 411 | cfg.dst_addr = 0; |
| 412 | ret = dmaengine_slave_config(host->chan_rx, &cfg); |
| 413 | if (ret < 0) |
| 414 | goto ecfgrx; |
| 415 | |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 416 | host->bounce_buf = (u8 *)__get_free_page(GFP_KERNEL | GFP_DMA); |
| 417 | if (!host->bounce_buf) |
| 418 | goto ebouncebuf; |
| 419 | |
Wolfram Sang | 52ad9a8 | 2017-02-17 19:22:41 +0100 | [diff] [blame] | 420 | init_completion(&host->dma_dataend); |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 421 | tasklet_init(&host->dma_issue, |
| 422 | renesas_sdhi_sys_dmac_issue_tasklet_fn, |
| 423 | (unsigned long)host); |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 424 | } |
Guennadi Liakhovetski | e6ee718 | 2011-05-05 16:13:12 +0000 | [diff] [blame] | 425 | |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 426 | renesas_sdhi_sys_dmac_enable_dma(host, true); |
Guennadi Liakhovetski | e6ee718 | 2011-05-05 16:13:12 +0000 | [diff] [blame] | 427 | |
| 428 | return; |
| 429 | |
| 430 | ebouncebuf: |
Guennadi Liakhovetski | eec95ee | 2013-04-26 17:47:18 +0200 | [diff] [blame] | 431 | ecfgrx: |
Guennadi Liakhovetski | e6ee718 | 2011-05-05 16:13:12 +0000 | [diff] [blame] | 432 | dma_release_channel(host->chan_rx); |
| 433 | host->chan_rx = NULL; |
| 434 | ereqrx: |
Guennadi Liakhovetski | eec95ee | 2013-04-26 17:47:18 +0200 | [diff] [blame] | 435 | ecfgtx: |
Guennadi Liakhovetski | e6ee718 | 2011-05-05 16:13:12 +0000 | [diff] [blame] | 436 | dma_release_channel(host->chan_tx); |
| 437 | host->chan_tx = NULL; |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 438 | } |
| 439 | |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 440 | static void renesas_sdhi_sys_dmac_release_dma(struct tmio_mmc_host *host) |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 441 | { |
| 442 | if (host->chan_tx) { |
| 443 | struct dma_chan *chan = host->chan_tx; |
Simon Horman | 2fe3596 | 2017-06-16 18:11:04 +0200 | [diff] [blame] | 444 | |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 445 | host->chan_tx = NULL; |
| 446 | dma_release_channel(chan); |
| 447 | } |
| 448 | if (host->chan_rx) { |
| 449 | struct dma_chan *chan = host->chan_rx; |
Simon Horman | 2fe3596 | 2017-06-16 18:11:04 +0200 | [diff] [blame] | 450 | |
Guennadi Liakhovetski | b614749 | 2011-03-23 12:42:44 +0100 | [diff] [blame] | 451 | host->chan_rx = NULL; |
| 452 | dma_release_channel(chan); |
| 453 | } |
| 454 | if (host->bounce_buf) { |
| 455 | free_pages((unsigned long)host->bounce_buf, 0); |
| 456 | host->bounce_buf = NULL; |
| 457 | } |
| 458 | } |
Simon Horman | 631fa73 | 2017-05-10 11:25:26 +0200 | [diff] [blame] | 459 | |
Simon Horman | c2a9698 | 2017-05-10 11:25:28 +0200 | [diff] [blame] | 460 | static const struct tmio_mmc_dma_ops renesas_sdhi_sys_dmac_dma_ops = { |
| 461 | .start = renesas_sdhi_sys_dmac_start_dma, |
| 462 | .enable = renesas_sdhi_sys_dmac_enable_dma, |
| 463 | .request = renesas_sdhi_sys_dmac_request_dma, |
| 464 | .release = renesas_sdhi_sys_dmac_release_dma, |
| 465 | .abort = renesas_sdhi_sys_dmac_abort_dma, |
Simon Horman | 92d0f925e | 2017-06-21 16:00:28 +0200 | [diff] [blame] | 466 | .dataend = renesas_sdhi_sys_dmac_dataend_dma, |
Simon Horman | 631fa73 | 2017-05-10 11:25:26 +0200 | [diff] [blame] | 467 | }; |
| 468 | |
Simon Horman | cd09780 | 2017-08-02 14:48:42 +0200 | [diff] [blame] | 469 | /* |
| 470 | * Whitelist of specific R-Car Gen3 SoC ES versions to use this DMAC |
| 471 | * implementation. Currently empty as all supported ES versions use |
| 472 | * the internal DMAC. |
| 473 | */ |
| 474 | static const struct soc_device_attribute gen3_soc_whitelist[] = { |
| 475 | { /* sentinel */ } |
| 476 | }; |
| 477 | |
Simon Horman | 9d08428 | 2017-05-10 11:25:30 +0200 | [diff] [blame] | 478 | static int renesas_sdhi_sys_dmac_probe(struct platform_device *pdev) |
Simon Horman | 631fa73 | 2017-05-10 11:25:26 +0200 | [diff] [blame] | 479 | { |
Simon Horman | cd09780 | 2017-08-02 14:48:42 +0200 | [diff] [blame] | 480 | if (of_device_get_match_data(&pdev->dev) == &of_rcar_gen3_compatible && |
| 481 | !soc_device_match(gen3_soc_whitelist)) |
| 482 | return -ENODEV; |
| 483 | |
Simon Horman | 9d08428 | 2017-05-10 11:25:30 +0200 | [diff] [blame] | 484 | return renesas_sdhi_probe(pdev, &renesas_sdhi_sys_dmac_dma_ops); |
Simon Horman | 631fa73 | 2017-05-10 11:25:26 +0200 | [diff] [blame] | 485 | } |
Simon Horman | 9d08428 | 2017-05-10 11:25:30 +0200 | [diff] [blame] | 486 | |
| 487 | static const struct dev_pm_ops renesas_sdhi_sys_dmac_dev_pm_ops = { |
| 488 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
Simon Horman | 2fe3596 | 2017-06-16 18:11:04 +0200 | [diff] [blame] | 489 | pm_runtime_force_resume) |
Simon Horman | 9d08428 | 2017-05-10 11:25:30 +0200 | [diff] [blame] | 490 | SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, |
Simon Horman | 2fe3596 | 2017-06-16 18:11:04 +0200 | [diff] [blame] | 491 | tmio_mmc_host_runtime_resume, |
| 492 | NULL) |
Simon Horman | 9d08428 | 2017-05-10 11:25:30 +0200 | [diff] [blame] | 493 | }; |
| 494 | |
| 495 | static struct platform_driver renesas_sys_dmac_sdhi_driver = { |
| 496 | .driver = { |
| 497 | .name = "sh_mobile_sdhi", |
| 498 | .pm = &renesas_sdhi_sys_dmac_dev_pm_ops, |
| 499 | .of_match_table = renesas_sdhi_sys_dmac_of_match, |
| 500 | }, |
| 501 | .probe = renesas_sdhi_sys_dmac_probe, |
| 502 | .remove = renesas_sdhi_remove, |
| 503 | }; |
| 504 | |
| 505 | module_platform_driver(renesas_sys_dmac_sdhi_driver); |
| 506 | |
| 507 | MODULE_DESCRIPTION("Renesas SDHI driver"); |
| 508 | MODULE_AUTHOR("Magnus Damm"); |
| 509 | MODULE_LICENSE("GPL v2"); |
| 510 | MODULE_ALIAS("platform:sh_mobile_sdhi"); |