blob: df4465439e13c44245c136fb3e02f3eb81545e6f [file] [log] [blame]
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +01001/*
Simon Horman2a68ea72017-06-21 16:00:29 +02002 * DMA support use of SYS DMAC with SDHI SD/SDIO controller
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +01003 *
Simon Horman87317c42017-05-30 14:50:52 +02004 * 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 Liakhovetskib6147492011-03-23 12:42:44 +01007 * 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 Liakhovetskib6147492011-03-23 12:42:44 +010012 */
13
14#include <linux/device.h>
Alexey Dobriyanb7f080c2011-06-16 11:01:34 +000015#include <linux/dma-mapping.h>
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +010016#include <linux/dmaengine.h>
17#include <linux/mfd/tmio.h>
18#include <linux/mmc/host.h>
Simon Horman9d084282017-05-10 11:25:30 +020019#include <linux/mod_devicetable.h>
20#include <linux/module.h>
Simon Hormancd097802017-08-02 14:48:42 +020021#include <linux/of_device.h>
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +010022#include <linux/pagemap.h>
23#include <linux/scatterlist.h>
Simon Hormancd097802017-08-02 14:48:42 +020024#include <linux/sys_soc.h>
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +010025
Simon Horman9d084282017-05-10 11:25:30 +020026#include "renesas_sdhi.h"
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +010027#include "tmio_mmc.h"
28
29#define TMIO_MMC_MIN_DMA_LEN 8
30
Simon Horman9d084282017-05-10 11:25:30 +020031static const struct renesas_sdhi_of_data of_default_cfg = {
32 .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT,
33};
34
35static const struct renesas_sdhi_of_data of_rz_compatible = {
Wolfram Sang92b7db82017-08-09 21:14:51 +020036 .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_32BIT_DATA_PORT |
37 TMIO_MMC_HAVE_CBSY,
Simon Horman9d084282017-05-10 11:25:30 +020038 .tmio_ocr_mask = MMC_VDD_32_33,
39 .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
40};
41
42static 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 */
49static 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
60static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
61 .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
Wolfram Sang5124b592017-08-09 21:00:41 +020062 TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
63 TMIO_MMC_MIN_RCAR2,
Wolfram Sang921579b2017-05-19 15:31:55 +020064 .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
65 MMC_CAP_CMD23,
Simon Horman9d084282017-05-10 11:25:30 +020066 .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 */
74static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
75 {
76 .clk_rate = 0,
77 .tap = 0x00000300,
78 },
79};
80
81static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
82 .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
Wolfram Sang5124b592017-08-09 21:00:41 +020083 TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
84 TMIO_MMC_MIN_RCAR2,
Wolfram Sang921579b2017-05-19 15:31:55 +020085 .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
86 MMC_CAP_CMD23,
Simon Horman9d084282017-05-10 11:25:30 +020087 .bus_shift = 2,
88 .scc_offset = 0x1000,
89 .taps = rcar_gen3_scc_taps,
90 .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps),
91};
92
93static 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 Dasc16a8542017-08-29 14:52:06 +0100101 { .compatible = "renesas,sdhi-r8a7743", .data = &of_rcar_gen2_compatible, },
102 { .compatible = "renesas,sdhi-r8a7745", .data = &of_rcar_gen2_compatible, },
Simon Horman9d084282017-05-10 11:25:30 +0200103 { .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};
112MODULE_DEVICE_TABLE(of, renesas_sdhi_sys_dmac_of_match);
113
Simon Hormanc2a96982017-05-10 11:25:28 +0200114static void renesas_sdhi_sys_dmac_enable_dma(struct tmio_mmc_host *host,
115 bool enable)
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100116{
Guennadi Liakhovetski162f43e2011-07-14 18:39:10 +0200117 if (!host->chan_tx || !host->chan_rx)
118 return;
119
Kuninori Morimoto5add2ac2015-01-13 04:59:05 +0000120 if (host->dma->enable)
121 host->dma->enable(host, enable);
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100122}
123
Simon Hormanc2a96982017-05-10 11:25:28 +0200124static void renesas_sdhi_sys_dmac_abort_dma(struct tmio_mmc_host *host)
Guennadi Liakhovetskie3de2be2012-01-06 13:06:51 +0100125{
Simon Hormanc2a96982017-05-10 11:25:28 +0200126 renesas_sdhi_sys_dmac_enable_dma(host, false);
Guennadi Liakhovetskie3de2be2012-01-06 13:06:51 +0100127
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 Hormanc2a96982017-05-10 11:25:28 +0200133 renesas_sdhi_sys_dmac_enable_dma(host, true);
Guennadi Liakhovetskie3de2be2012-01-06 13:06:51 +0100134}
135
Simon Horman92d0f925e2017-06-21 16:00:28 +0200136static void renesas_sdhi_sys_dmac_dataend_dma(struct tmio_mmc_host *host)
137{
138 complete(&host->dma_dataend);
139}
140
Simon Hormanc2a96982017-05-10 11:25:28 +0200141static void renesas_sdhi_sys_dmac_dma_callback(void *arg)
Wolfram Sang52ad9a82017-02-17 19:22:41 +0100142{
143 struct tmio_mmc_host *host = arg;
144
Wolfram Sang52ad9a82017-02-17 19:22:41 +0100145 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 Sang5f07ef82017-03-16 11:56:02 +0100159 spin_unlock_irq(&host->lock);
160
161 wait_for_completion(&host->dma_dataend);
162
163 spin_lock_irq(&host->lock);
Wolfram Sang52ad9a82017-02-17 19:22:41 +0100164 tmio_mmc_do_data_irq(host);
165out:
166 spin_unlock_irq(&host->lock);
167}
168
Simon Hormanc2a96982017-05-10 11:25:28 +0200169static void renesas_sdhi_sys_dmac_start_dma_rx(struct tmio_mmc_host *host)
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100170{
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 Liakhovetskib6147492011-03-23 12:42:44 +0100174 dma_cookie_t cookie;
175 int ret, i;
176 bool aligned = true, multiple = true;
Kuninori Morimotoe471df02015-01-13 04:58:46 +0000177 unsigned int align = (1 << host->pdata->alignment_shift) - 1;
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100178
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. Shutemov09cbfea2016-04-01 15:29:47 +0300188 if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_SIZE ||
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100189 (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 Horman2fe35962017-06-16 18:11:04 +0200210 desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_DEV_TO_MEM,
211 DMA_CTRL_ACK);
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100212
213 if (desc) {
Wolfram Sang52ad9a82017-02-17 19:22:41 +0100214 reinit_completion(&host->dma_dataend);
Simon Hormanc2a96982017-05-10 11:25:28 +0200215 desc->callback = renesas_sdhi_sys_dmac_dma_callback;
Wolfram Sang52ad9a82017-02-17 19:22:41 +0100216 desc->callback_param = host;
217
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100218 cookie = dmaengine_submit(desc);
219 if (cookie < 0) {
220 desc = NULL;
221 ret = cookie;
222 }
223 }
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100224pio:
225 if (!desc) {
226 /* DMA failed, fall back to PIO */
Simon Hormanc2a96982017-05-10 11:25:28 +0200227 renesas_sdhi_sys_dmac_enable_dma(host, false);
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100228 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 Liakhovetskib6147492011-03-23 12:42:44 +0100240 }
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100241}
242
Simon Hormanc2a96982017-05-10 11:25:28 +0200243static void renesas_sdhi_sys_dmac_start_dma_tx(struct tmio_mmc_host *host)
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100244{
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 Liakhovetskib6147492011-03-23 12:42:44 +0100248 dma_cookie_t cookie;
249 int ret, i;
250 bool aligned = true, multiple = true;
Kuninori Morimotoe471df02015-01-13 04:58:46 +0000251 unsigned int align = (1 << host->pdata->alignment_shift) - 1;
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100252
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. Shutemov09cbfea2016-04-01 15:29:47 +0300262 if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_SIZE ||
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100263 (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 Horman2fe35962017-06-16 18:11:04 +0200279
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100280 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 Horman2fe35962017-06-16 18:11:04 +0200289 desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_MEM_TO_DEV,
290 DMA_CTRL_ACK);
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100291
292 if (desc) {
Wolfram Sang52ad9a82017-02-17 19:22:41 +0100293 reinit_completion(&host->dma_dataend);
Simon Hormanc2a96982017-05-10 11:25:28 +0200294 desc->callback = renesas_sdhi_sys_dmac_dma_callback;
Wolfram Sang52ad9a82017-02-17 19:22:41 +0100295 desc->callback_param = host;
296
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100297 cookie = dmaengine_submit(desc);
298 if (cookie < 0) {
299 desc = NULL;
300 ret = cookie;
301 }
302 }
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100303pio:
304 if (!desc) {
305 /* DMA failed, fall back to PIO */
Simon Hormanc2a96982017-05-10 11:25:28 +0200306 renesas_sdhi_sys_dmac_enable_dma(host, false);
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100307 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 Liakhovetskib6147492011-03-23 12:42:44 +0100319 }
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100320}
321
Simon Hormanc2a96982017-05-10 11:25:28 +0200322static void renesas_sdhi_sys_dmac_start_dma(struct tmio_mmc_host *host,
Simon Horman2fe35962017-06-16 18:11:04 +0200323 struct mmc_data *data)
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100324{
325 if (data->flags & MMC_DATA_READ) {
326 if (host->chan_rx)
Simon Hormanc2a96982017-05-10 11:25:28 +0200327 renesas_sdhi_sys_dmac_start_dma_rx(host);
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100328 } else {
329 if (host->chan_tx)
Simon Hormanc2a96982017-05-10 11:25:28 +0200330 renesas_sdhi_sys_dmac_start_dma_tx(host);
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100331 }
332}
333
Simon Hormanc2a96982017-05-10 11:25:28 +0200334static void renesas_sdhi_sys_dmac_issue_tasklet_fn(unsigned long priv)
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100335{
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 Hormanc2a96982017-05-10 11:25:28 +0200356static void renesas_sdhi_sys_dmac_request_dma(struct tmio_mmc_host *host,
357 struct tmio_mmc_data *pdata)
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100358{
359 /* We can only either use DMA for both Tx and Rx or not use it at all */
Kuninori Morimoto7ecc09b2015-01-13 04:57:33 +0000360 if (!host->dma || (!host->pdev->dev.of_node &&
Simon Horman2fe35962017-06-16 18:11:04 +0200361 (!pdata->chan_priv_tx || !pdata->chan_priv_rx)))
Guennadi Liakhovetskie6ee7182011-05-05 16:13:12 +0000362 return;
363
364 if (!host->chan_tx && !host->chan_rx) {
Guennadi Liakhovetskieec95ee2013-04-26 17:47:18 +0200365 struct resource *res = platform_get_resource(host->pdev,
366 IORESOURCE_MEM, 0);
367 struct dma_slave_config cfg = {};
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100368 dma_cap_mask_t mask;
Guennadi Liakhovetskieec95ee2013-04-26 17:47:18 +0200369 int ret;
370
371 if (!res)
372 return;
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100373
374 dma_cap_zero(mask);
375 dma_cap_set(DMA_SLAVE, mask);
376
Guennadi Liakhovetski87ae7bb2013-04-26 17:47:19 +0200377 host->chan_tx = dma_request_slave_channel_compat(mask,
Kuninori Morimotof33c9d62015-02-24 02:06:43 +0000378 host->dma->filter, pdata->chan_priv_tx,
Guennadi Liakhovetski87ae7bb2013-04-26 17:47:19 +0200379 &host->pdev->dev, "tx");
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100380 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 Liakhovetskieec95ee2013-04-26 17:47:18 +0200386 cfg.direction = DMA_MEM_TO_DEV;
Simon Horman2fe35962017-06-16 18:11:04 +0200387 cfg.dst_addr = res->start +
388 (CTL_SD_DATA_PORT << host->bus_shift);
Kuninori Morimoto361936e2015-01-13 04:59:14 +0000389 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 Liakhovetskieec95ee2013-04-26 17:47:18 +0200392 cfg.src_addr = 0;
393 ret = dmaengine_slave_config(host->chan_tx, &cfg);
394 if (ret < 0)
395 goto ecfgtx;
396
Guennadi Liakhovetski87ae7bb2013-04-26 17:47:19 +0200397 host->chan_rx = dma_request_slave_channel_compat(mask,
Kuninori Morimotof33c9d62015-02-24 02:06:43 +0000398 host->dma->filter, pdata->chan_priv_rx,
Guennadi Liakhovetski87ae7bb2013-04-26 17:47:19 +0200399 &host->pdev->dev, "rx");
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100400 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 Liakhovetskieec95ee2013-04-26 17:47:18 +0200406 cfg.direction = DMA_DEV_TO_MEM;
Kuninori Morimoto8b4c8f32015-01-13 04:58:56 +0000407 cfg.src_addr = cfg.dst_addr + host->pdata->dma_rx_offset;
Kuninori Morimoto361936e2015-01-13 04:59:14 +0000408 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 Liakhovetskieec95ee2013-04-26 17:47:18 +0200411 cfg.dst_addr = 0;
412 ret = dmaengine_slave_config(host->chan_rx, &cfg);
413 if (ret < 0)
414 goto ecfgrx;
415
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100416 host->bounce_buf = (u8 *)__get_free_page(GFP_KERNEL | GFP_DMA);
417 if (!host->bounce_buf)
418 goto ebouncebuf;
419
Wolfram Sang52ad9a82017-02-17 19:22:41 +0100420 init_completion(&host->dma_dataend);
Simon Hormanc2a96982017-05-10 11:25:28 +0200421 tasklet_init(&host->dma_issue,
422 renesas_sdhi_sys_dmac_issue_tasklet_fn,
423 (unsigned long)host);
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100424 }
Guennadi Liakhovetskie6ee7182011-05-05 16:13:12 +0000425
Simon Hormanc2a96982017-05-10 11:25:28 +0200426 renesas_sdhi_sys_dmac_enable_dma(host, true);
Guennadi Liakhovetskie6ee7182011-05-05 16:13:12 +0000427
428 return;
429
430ebouncebuf:
Guennadi Liakhovetskieec95ee2013-04-26 17:47:18 +0200431ecfgrx:
Guennadi Liakhovetskie6ee7182011-05-05 16:13:12 +0000432 dma_release_channel(host->chan_rx);
433 host->chan_rx = NULL;
434ereqrx:
Guennadi Liakhovetskieec95ee2013-04-26 17:47:18 +0200435ecfgtx:
Guennadi Liakhovetskie6ee7182011-05-05 16:13:12 +0000436 dma_release_channel(host->chan_tx);
437 host->chan_tx = NULL;
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100438}
439
Simon Hormanc2a96982017-05-10 11:25:28 +0200440static void renesas_sdhi_sys_dmac_release_dma(struct tmio_mmc_host *host)
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100441{
442 if (host->chan_tx) {
443 struct dma_chan *chan = host->chan_tx;
Simon Horman2fe35962017-06-16 18:11:04 +0200444
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100445 host->chan_tx = NULL;
446 dma_release_channel(chan);
447 }
448 if (host->chan_rx) {
449 struct dma_chan *chan = host->chan_rx;
Simon Horman2fe35962017-06-16 18:11:04 +0200450
Guennadi Liakhovetskib6147492011-03-23 12:42:44 +0100451 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 Horman631fa732017-05-10 11:25:26 +0200459
Simon Hormanc2a96982017-05-10 11:25:28 +0200460static 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 Horman92d0f925e2017-06-21 16:00:28 +0200466 .dataend = renesas_sdhi_sys_dmac_dataend_dma,
Simon Horman631fa732017-05-10 11:25:26 +0200467};
468
Simon Hormancd097802017-08-02 14:48:42 +0200469/*
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 */
474static const struct soc_device_attribute gen3_soc_whitelist[] = {
475 { /* sentinel */ }
476};
477
Simon Horman9d084282017-05-10 11:25:30 +0200478static int renesas_sdhi_sys_dmac_probe(struct platform_device *pdev)
Simon Horman631fa732017-05-10 11:25:26 +0200479{
Simon Hormancd097802017-08-02 14:48:42 +0200480 if (of_device_get_match_data(&pdev->dev) == &of_rcar_gen3_compatible &&
481 !soc_device_match(gen3_soc_whitelist))
482 return -ENODEV;
483
Simon Horman9d084282017-05-10 11:25:30 +0200484 return renesas_sdhi_probe(pdev, &renesas_sdhi_sys_dmac_dma_ops);
Simon Horman631fa732017-05-10 11:25:26 +0200485}
Simon Horman9d084282017-05-10 11:25:30 +0200486
487static const struct dev_pm_ops renesas_sdhi_sys_dmac_dev_pm_ops = {
488 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
Simon Horman2fe35962017-06-16 18:11:04 +0200489 pm_runtime_force_resume)
Simon Horman9d084282017-05-10 11:25:30 +0200490 SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
Simon Horman2fe35962017-06-16 18:11:04 +0200491 tmio_mmc_host_runtime_resume,
492 NULL)
Simon Horman9d084282017-05-10 11:25:30 +0200493};
494
495static 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
505module_platform_driver(renesas_sys_dmac_sdhi_driver);
506
507MODULE_DESCRIPTION("Renesas SDHI driver");
508MODULE_AUTHOR("Magnus Damm");
509MODULE_LICENSE("GPL v2");
510MODULE_ALIAS("platform:sh_mobile_sdhi");