Skip to content

Commit 2a68ea7

Browse files
hormsstorulf
authored andcommitted
mmc: renesas-sdhi: add support for R-Car Gen3 SDHI DMAC
Add a new variant of the SDHI driver to support R-Car Gen3 with DMA via on-chip bus mastering. Since the DMAC is in a part of the SDHI module it is not suitable to be used via DMA Engine. Clearing of DM_CM_INFO1 after DMA thanks to Dirk Behme Cc: Dirk Behme <dirk.behme@de.bosch.com> Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Signed-off-by: Ai Kyuse <ai.kyuse.uw@renesas.com> Signed-off-by: Simon Horman <horms+renesas@verge.net.au> Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
1 parent 92d0f92 commit 2a68ea7

File tree

5 files changed

+299
-2
lines changed

5 files changed

+299
-2
lines changed

drivers/mmc/host/Kconfig

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,10 +575,29 @@ config MMC_SDHI
575575
depends on SUPERH || ARM || ARM64
576576
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
577577
select MMC_TMIO_CORE
578+
select MMC_SDHI_SYS_DMAC if (SUPERH || ARM)
579+
select MMC_SDHI_INTERNAL_DMAC if ARM64
578580
help
579581
This provides support for the SDHI SD/SDIO controller found in
580582
Renesas SuperH, ARM and ARM64 based SoCs
581583

584+
config MMC_SDHI_SYS_DMAC
585+
tristate "DMA for SDHI SD/SDIO controllers using SYS-DMAC"
586+
depends on MMC_SDHI
587+
help
588+
This provides DMA support for SDHI SD/SDIO controllers
589+
using SYS-DMAC via DMA Engine. This supports the controllers
590+
found in SuperH and Renesas ARM based SoCs.
591+
592+
config MMC_SDHI_INTERNAL_DMAC
593+
tristate "DMA for SDHI SD/SDIO controllers using on-chip bus mastering"
594+
depends on ARM64 || COMPILE_TEST
595+
depends on MMC_SDHI
596+
help
597+
This provides DMA support for SDHI SD/SDIO controllers
598+
using on-chip bus mastering. This supports the controllers
599+
found in arm64 based SoCs.
600+
582601
config MMC_CB710
583602
tristate "ENE CB710 MMC/SD Interface support"
584603
depends on PCI

drivers/mmc/host/Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,13 @@ obj-$(CONFIG_MMC_S3C) += s3cmci.o
3636
obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o
3737
obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
3838
obj-$(CONFIG_MMC_TMIO_CORE) += tmio_mmc_core.o
39-
obj-$(CONFIG_MMC_SDHI) += renesas_sdhi_core.o renesas_sdhi_sys_dmac.o
39+
obj-$(CONFIG_MMC_SDHI) += renesas_sdhi_core.o
40+
ifeq ($(subst m,y,$(CONFIG_MMC_SDHI_SYS_DMAC)),y)
41+
obj-$(CONFIG_MMC_SDHI) += renesas_sdhi_sys_dmac.o
42+
endif
43+
ifeq ($(subst m,y,$(CONFIG_MMC_SDHI_INTERNAL_DMAC)),y)
44+
obj-$(CONFIG_MMC_SDHI) += renesas_sdhi_internal_dmac.o
45+
endif
4046
obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
4147
obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
4248
obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
/*
2+
* DMA support for Internal DMAC with SDHI SD/SDIO controller
3+
*
4+
* Copyright (C) 2016-17 Renesas Electronics Corporation
5+
* Copyright (C) 2016-17 Horms Solutions, Simon Horman
6+
*
7+
* This program is free software; you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License version 2 as
9+
* published by the Free Software Foundation.
10+
*/
11+
12+
#include <linux/device.h>
13+
#include <linux/dma-mapping.h>
14+
#include <linux/io-64-nonatomic-hi-lo.h>
15+
#include <linux/mfd/tmio.h>
16+
#include <linux/mmc/host.h>
17+
#include <linux/mod_devicetable.h>
18+
#include <linux/module.h>
19+
#include <linux/pagemap.h>
20+
#include <linux/scatterlist.h>
21+
22+
#include "renesas_sdhi.h"
23+
#include "tmio_mmc.h"
24+
25+
#define DM_CM_DTRAN_MODE 0x820
26+
#define DM_CM_DTRAN_CTRL 0x828
27+
#define DM_CM_RST 0x830
28+
#define DM_CM_INFO1 0x840
29+
#define DM_CM_INFO1_MASK 0x848
30+
#define DM_CM_INFO2 0x850
31+
#define DM_CM_INFO2_MASK 0x858
32+
#define DM_DTRAN_ADDR 0x880
33+
34+
/* DM_CM_DTRAN_MODE */
35+
#define DTRAN_MODE_CH_NUM_CH0 0 /* "downstream" = for write commands */
36+
#define DTRAN_MODE_CH_NUM_CH1 BIT(16) /* "uptream" = for read commands */
37+
#define DTRAN_MODE_BUS_WID_TH (BIT(5) | BIT(4))
38+
#define DTRAN_MODE_ADDR_MODE BIT(0) /* 1 = Increment address */
39+
40+
/* DM_CM_DTRAN_CTRL */
41+
#define DTRAN_CTRL_DM_START BIT(0)
42+
43+
/* DM_CM_RST */
44+
#define RST_DTRANRST1 BIT(9)
45+
#define RST_DTRANRST0 BIT(8)
46+
#define RST_RESERVED_BITS GENMASK_ULL(32, 0)
47+
48+
/* DM_CM_INFO1 and DM_CM_INFO1_MASK */
49+
#define INFO1_CLEAR 0
50+
#define INFO1_DTRANEND1 BIT(17)
51+
#define INFO1_DTRANEND0 BIT(16)
52+
53+
/* DM_CM_INFO2 and DM_CM_INFO2_MASK */
54+
#define INFO2_DTRANERR1 BIT(17)
55+
#define INFO2_DTRANERR0 BIT(16)
56+
57+
/*
58+
* Specification of this driver:
59+
* - host->chan_{rx,tx} will be used as a flag of enabling/disabling the dma
60+
* - Since this SDHI DMAC register set has 16 but 32-bit width, we
61+
* need a custom accessor.
62+
*/
63+
64+
/* Definitions for sampling clocks */
65+
static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
66+
{
67+
.clk_rate = 0,
68+
.tap = 0x00000300,
69+
},
70+
};
71+
72+
static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
73+
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
74+
TMIO_MMC_CLK_ACTUAL | TMIO_MMC_MIN_RCAR2,
75+
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
76+
MMC_CAP_CMD23,
77+
.bus_shift = 2,
78+
.scc_offset = 0x1000,
79+
.taps = rcar_gen3_scc_taps,
80+
.taps_num = ARRAY_SIZE(rcar_gen3_scc_taps),
81+
/* Gen3 SDHI DMAC can handle 0xffffffff blk count, but seg = 1 */
82+
.max_blk_count = 0xffffffff,
83+
.max_segs = 1,
84+
};
85+
86+
static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
87+
{ .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, },
88+
{ .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, },
89+
{},
90+
};
91+
MODULE_DEVICE_TABLE(of, renesas_sdhi_internal_dmac_of_match);
92+
93+
static void
94+
renesas_sdhi_internal_dmac_dm_write(struct tmio_mmc_host *host,
95+
int addr, u64 val)
96+
{
97+
writeq(val, host->ctl + addr);
98+
}
99+
100+
static void
101+
renesas_sdhi_internal_dmac_enable_dma(struct tmio_mmc_host *host, bool enable)
102+
{
103+
if (!host->chan_tx || !host->chan_rx)
104+
return;
105+
106+
if (!enable)
107+
renesas_sdhi_internal_dmac_dm_write(host, DM_CM_INFO1,
108+
INFO1_CLEAR);
109+
110+
if (host->dma->enable)
111+
host->dma->enable(host, enable);
112+
}
113+
114+
static void
115+
renesas_sdhi_internal_dmac_abort_dma(struct tmio_mmc_host *host) {
116+
u64 val = RST_DTRANRST1 | RST_DTRANRST0;
117+
118+
renesas_sdhi_internal_dmac_enable_dma(host, false);
119+
120+
renesas_sdhi_internal_dmac_dm_write(host, DM_CM_RST,
121+
RST_RESERVED_BITS & ~val);
122+
renesas_sdhi_internal_dmac_dm_write(host, DM_CM_RST,
123+
RST_RESERVED_BITS | val);
124+
125+
renesas_sdhi_internal_dmac_enable_dma(host, true);
126+
}
127+
128+
static void
129+
renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host) {
130+
tasklet_schedule(&host->dma_complete);
131+
}
132+
133+
static void
134+
renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
135+
struct mmc_data *data)
136+
{
137+
struct scatterlist *sg = host->sg_ptr;
138+
u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE;
139+
enum dma_data_direction dir;
140+
int ret;
141+
u32 irq_mask;
142+
143+
/* This DMAC cannot handle if sg_len is not 1 */
144+
WARN_ON(host->sg_len > 1);
145+
146+
/* This DMAC cannot handle if buffer is not 8-bytes alignment */
147+
if (!IS_ALIGNED(sg->offset, 8)) {
148+
host->force_pio = true;
149+
renesas_sdhi_internal_dmac_enable_dma(host, false);
150+
return;
151+
}
152+
153+
if (data->flags & MMC_DATA_READ) {
154+
dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
155+
dir = DMA_FROM_DEVICE;
156+
irq_mask = TMIO_STAT_RXRDY;
157+
} else {
158+
dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
159+
dir = DMA_TO_DEVICE;
160+
irq_mask = TMIO_STAT_TXRQ;
161+
}
162+
163+
ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir);
164+
if (ret < 0)
165+
return;
166+
167+
renesas_sdhi_internal_dmac_enable_dma(host, true);
168+
169+
/* disable PIO irqs to avoid "PIO IRQ in DMA mode!" */
170+
tmio_mmc_disable_mmc_irqs(host, irq_mask);
171+
172+
/* set dma parameters */
173+
renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE,
174+
dtran_mode);
175+
renesas_sdhi_internal_dmac_dm_write(host, DM_DTRAN_ADDR,
176+
sg->dma_address);
177+
}
178+
179+
static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg)
180+
{
181+
struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
182+
183+
tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND);
184+
185+
/* start the DMAC */
186+
renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_CTRL,
187+
DTRAN_CTRL_DM_START);
188+
}
189+
190+
static void renesas_sdhi_internal_dmac_complete_tasklet_fn(unsigned long arg)
191+
{
192+
struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
193+
enum dma_data_direction dir;
194+
195+
spin_lock_irq(&host->lock);
196+
197+
if (!host->data)
198+
goto out;
199+
200+
if (host->data->flags & MMC_DATA_READ)
201+
dir = DMA_FROM_DEVICE;
202+
else
203+
dir = DMA_TO_DEVICE;
204+
205+
renesas_sdhi_internal_dmac_enable_dma(host, false);
206+
dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->sg_len, dir);
207+
208+
tmio_mmc_do_data_irq(host);
209+
out:
210+
spin_unlock_irq(&host->lock);
211+
}
212+
213+
static void
214+
renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host,
215+
struct tmio_mmc_data *pdata)
216+
{
217+
/* Each value is set to non-zero to assume "enabling" each DMA */
218+
host->chan_rx = host->chan_tx = (void *)0xdeadbeaf;
219+
220+
tasklet_init(&host->dma_complete,
221+
renesas_sdhi_internal_dmac_complete_tasklet_fn,
222+
(unsigned long)host);
223+
tasklet_init(&host->dma_issue,
224+
renesas_sdhi_internal_dmac_issue_tasklet_fn,
225+
(unsigned long)host);
226+
}
227+
228+
static void
229+
renesas_sdhi_internal_dmac_release_dma(struct tmio_mmc_host *host)
230+
{
231+
/* Each value is set to zero to assume "disabling" each DMA */
232+
host->chan_rx = host->chan_tx = NULL;
233+
}
234+
235+
static struct tmio_mmc_dma_ops renesas_sdhi_internal_dmac_dma_ops = {
236+
.start = renesas_sdhi_internal_dmac_start_dma,
237+
.enable = renesas_sdhi_internal_dmac_enable_dma,
238+
.request = renesas_sdhi_internal_dmac_request_dma,
239+
.release = renesas_sdhi_internal_dmac_release_dma,
240+
.abort = renesas_sdhi_internal_dmac_abort_dma,
241+
.dataend = renesas_sdhi_internal_dmac_dataend_dma,
242+
};
243+
244+
static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
245+
{
246+
return renesas_sdhi_probe(pdev, &renesas_sdhi_internal_dmac_dma_ops);
247+
}
248+
249+
static const struct dev_pm_ops renesas_sdhi_internal_dmac_dev_pm_ops = {
250+
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
251+
pm_runtime_force_resume)
252+
SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
253+
tmio_mmc_host_runtime_resume,
254+
NULL)
255+
};
256+
257+
static struct platform_driver renesas_internal_dmac_sdhi_driver = {
258+
.driver = {
259+
.name = "renesas_sdhi_internal_dmac",
260+
.pm = &renesas_sdhi_internal_dmac_dev_pm_ops,
261+
.of_match_table = renesas_sdhi_internal_dmac_of_match,
262+
},
263+
.probe = renesas_sdhi_internal_dmac_probe,
264+
.remove = renesas_sdhi_remove,
265+
};
266+
267+
module_platform_driver(renesas_internal_dmac_sdhi_driver);
268+
269+
MODULE_DESCRIPTION("Renesas SDHI driver for internal DMAC");
270+
MODULE_AUTHOR("Yoshihiro Shimoda");
271+
MODULE_LICENSE("GPL v2");

drivers/mmc/host/renesas_sdhi_sys_dmac.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* DMA function for TMIO MMC implementations
2+
* DMA support use of SYS DMAC with SDHI SD/SDIO controller
33
*
44
* Copyright (C) 2016-17 Renesas Electronics Corporation
55
* Copyright (C) 2016-17 Sang Engineering, Wolfram Sang

drivers/mmc/host/tmio_mmc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ struct tmio_mmc_host {
152152
struct dma_chan *chan_rx;
153153
struct dma_chan *chan_tx;
154154
struct completion dma_dataend;
155+
struct tasklet_struct dma_complete;
155156
struct tasklet_struct dma_issue;
156157
struct scatterlist bounce_sg;
157158
u8 *bounce_buf;

0 commit comments

Comments
 (0)