aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma
diff options
context:
space:
mode:
authorShimoda, Yoshihiro <yoshihiro.shimoda.uh@renesas.com>2013-04-23 07:00:12 -0400
committerVinod Koul <vinod.koul@intel.com>2013-04-30 06:20:12 -0400
commit18a1053f7b85acdda2428c9f694101070cb8e62a (patch)
tree483b29e4ac010e751e958ea9e310b6b49c47d384 /drivers/dma
parent189b4ee8e9daf349db80f47b81edec67d223a953 (diff)
sudmac: add support for SUDMAC
Some Renesas USB modules have SUDMAC. This patch supports it using the shdma-base driver. Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Reviewed-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Acked-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma')
-rw-r--r--drivers/dma/sh/Kconfig6
-rw-r--r--drivers/dma/sh/Makefile1
-rw-r--r--drivers/dma/sh/sudmac.c428
3 files changed, 435 insertions, 0 deletions
diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig
index c0f7a3763f3d..5c1dee20c13e 100644
--- a/drivers/dma/sh/Kconfig
+++ b/drivers/dma/sh/Kconfig
@@ -16,3 +16,9 @@ config SH_DMAE
16 depends on SH_DMAE_BASE 16 depends on SH_DMAE_BASE
17 help 17 help
18 Enable support for the Renesas SuperH DMA controllers. 18 Enable support for the Renesas SuperH DMA controllers.
19
20config SUDMAC
21 tristate "Renesas SUDMAC support"
22 depends on SH_DMAE_BASE
23 help
24 Enable support for the Renesas SUDMAC controllers.
diff --git a/drivers/dma/sh/Makefile b/drivers/dma/sh/Makefile
index eceaf469f5e3..c07ca4612e46 100644
--- a/drivers/dma/sh/Makefile
+++ b/drivers/dma/sh/Makefile
@@ -1,2 +1,3 @@
1obj-$(CONFIG_SH_DMAE_BASE) += shdma-base.o 1obj-$(CONFIG_SH_DMAE_BASE) += shdma-base.o
2obj-$(CONFIG_SH_DMAE) += shdma.o 2obj-$(CONFIG_SH_DMAE) += shdma.o
3obj-$(CONFIG_SUDMAC) += sudmac.o
diff --git a/drivers/dma/sh/sudmac.c b/drivers/dma/sh/sudmac.c
new file mode 100644
index 000000000000..e7c94bbddb53
--- /dev/null
+++ b/drivers/dma/sh/sudmac.c
@@ -0,0 +1,428 @@
1/*
2 * Renesas SUDMAC support
3 *
4 * Copyright (C) 2013 Renesas Solutions Corp.
5 *
6 * based on drivers/dma/sh/shdma.c:
7 * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
8 * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
9 * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.
10 * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
11 *
12 * This is free software; you can redistribute it and/or modify
13 * it under the terms of version 2 of the GNU General Public License as
14 * published by the Free Software Foundation.
15 */
16
17#include <linux/init.h>
18#include <linux/module.h>
19#include <linux/slab.h>
20#include <linux/interrupt.h>
21#include <linux/dmaengine.h>
22#include <linux/platform_device.h>
23#include <linux/sudmac.h>
24
25struct sudmac_chan {
26 struct shdma_chan shdma_chan;
27 void __iomem *base;
28 char dev_id[16]; /* unique name per DMAC of channel */
29
30 u32 offset; /* for CFG, BA, BBC, CA, CBC, DEN */
31 u32 cfg;
32 u32 dint_end_bit;
33};
34
35struct sudmac_device {
36 struct shdma_dev shdma_dev;
37 struct sudmac_pdata *pdata;
38 void __iomem *chan_reg;
39};
40
41struct sudmac_regs {
42 u32 base_addr;
43 u32 base_byte_count;
44};
45
46struct sudmac_desc {
47 struct sudmac_regs hw;
48 struct shdma_desc shdma_desc;
49};
50
51#define to_chan(schan) container_of(schan, struct sudmac_chan, shdma_chan)
52#define to_desc(sdesc) container_of(sdesc, struct sudmac_desc, shdma_desc)
53#define to_sdev(sc) container_of(sc->shdma_chan.dma_chan.device, \
54 struct sudmac_device, shdma_dev.dma_dev)
55
56/* SUDMAC register */
57#define SUDMAC_CH0CFG 0x00
58#define SUDMAC_CH0BA 0x10
59#define SUDMAC_CH0BBC 0x18
60#define SUDMAC_CH0CA 0x20
61#define SUDMAC_CH0CBC 0x28
62#define SUDMAC_CH0DEN 0x30
63#define SUDMAC_DSTSCLR 0x38
64#define SUDMAC_DBUFCTRL 0x3C
65#define SUDMAC_DINTCTRL 0x40
66#define SUDMAC_DINTSTS 0x44
67#define SUDMAC_DINTSTSCLR 0x48
68#define SUDMAC_CH0SHCTRL 0x50
69
70/* Definitions for the sudmac_channel.config */
71#define SUDMAC_SENDBUFM 0x1000 /* b12: Transmit Buffer Mode */
72#define SUDMAC_RCVENDM 0x0100 /* b8: Receive Data Transfer End Mode */
73#define SUDMAC_LBA_WAIT 0x0030 /* b5-4: Local Bus Access Wait */
74
75/* Definitions for the sudmac_channel.dint_end_bit */
76#define SUDMAC_CH1ENDE 0x0002 /* b1: Ch1 DMA Transfer End Int Enable */
77#define SUDMAC_CH0ENDE 0x0001 /* b0: Ch0 DMA Transfer End Int Enable */
78
79#define SUDMAC_DRV_NAME "sudmac"
80
81static void sudmac_writel(struct sudmac_chan *sc, u32 data, u32 reg)
82{
83 iowrite32(data, sc->base + reg);
84}
85
86static u32 sudmac_readl(struct sudmac_chan *sc, u32 reg)
87{
88 return ioread32(sc->base + reg);
89}
90
91static bool sudmac_is_busy(struct sudmac_chan *sc)
92{
93 u32 den = sudmac_readl(sc, SUDMAC_CH0DEN + sc->offset);
94
95 if (den)
96 return true; /* working */
97
98 return false; /* waiting */
99}
100
101static void sudmac_set_reg(struct sudmac_chan *sc, struct sudmac_regs *hw,
102 struct shdma_desc *sdesc)
103{
104 sudmac_writel(sc, sc->cfg, SUDMAC_CH0CFG + sc->offset);
105 sudmac_writel(sc, hw->base_addr, SUDMAC_CH0BA + sc->offset);
106 sudmac_writel(sc, hw->base_byte_count, SUDMAC_CH0BBC + sc->offset);
107}
108
109static void sudmac_start(struct sudmac_chan *sc)
110{
111 u32 dintctrl = sudmac_readl(sc, SUDMAC_DINTCTRL);
112
113 sudmac_writel(sc, dintctrl | sc->dint_end_bit, SUDMAC_DINTCTRL);
114 sudmac_writel(sc, 1, SUDMAC_CH0DEN + sc->offset);
115}
116
117static void sudmac_start_xfer(struct shdma_chan *schan,
118 struct shdma_desc *sdesc)
119{
120 struct sudmac_chan *sc = to_chan(schan);
121 struct sudmac_desc *sd = to_desc(sdesc);
122
123 sudmac_set_reg(sc, &sd->hw, sdesc);
124 sudmac_start(sc);
125}
126
127static bool sudmac_channel_busy(struct shdma_chan *schan)
128{
129 struct sudmac_chan *sc = to_chan(schan);
130
131 return sudmac_is_busy(sc);
132}
133
134static void sudmac_setup_xfer(struct shdma_chan *schan, int slave_id)
135{
136}
137
138static const struct sudmac_slave_config *sudmac_find_slave(
139 struct sudmac_chan *sc, int slave_id)
140{
141 struct sudmac_device *sdev = to_sdev(sc);
142 struct sudmac_pdata *pdata = sdev->pdata;
143 const struct sudmac_slave_config *cfg;
144 int i;
145
146 for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++)
147 if (cfg->slave_id == slave_id)
148 return cfg;
149
150 return NULL;
151}
152
153static int sudmac_set_slave(struct shdma_chan *schan, int slave_id, bool try)
154{
155 struct sudmac_chan *sc = to_chan(schan);
156 const struct sudmac_slave_config *cfg = sudmac_find_slave(sc, slave_id);
157
158 if (!cfg)
159 return -ENODEV;
160
161 return 0;
162}
163
164static inline void sudmac_dma_halt(struct sudmac_chan *sc)
165{
166 u32 dintctrl = sudmac_readl(sc, SUDMAC_DINTCTRL);
167
168 sudmac_writel(sc, 0, SUDMAC_CH0DEN + sc->offset);
169 sudmac_writel(sc, dintctrl & ~sc->dint_end_bit, SUDMAC_DINTCTRL);
170 sudmac_writel(sc, sc->dint_end_bit, SUDMAC_DINTSTSCLR);
171}
172
173static int sudmac_desc_setup(struct shdma_chan *schan,
174 struct shdma_desc *sdesc,
175 dma_addr_t src, dma_addr_t dst, size_t *len)
176{
177 struct sudmac_chan *sc = to_chan(schan);
178 struct sudmac_desc *sd = to_desc(sdesc);
179
180 dev_dbg(sc->shdma_chan.dev, "%s: src=%x, dst=%x, len=%d\n",
181 __func__, src, dst, *len);
182
183 if (*len > schan->max_xfer_len)
184 *len = schan->max_xfer_len;
185
186 if (dst)
187 sd->hw.base_addr = dst;
188 else if (src)
189 sd->hw.base_addr = src;
190 sd->hw.base_byte_count = *len;
191
192 return 0;
193}
194
195static void sudmac_halt(struct shdma_chan *schan)
196{
197 struct sudmac_chan *sc = to_chan(schan);
198
199 sudmac_dma_halt(sc);
200}
201
202static bool sudmac_chan_irq(struct shdma_chan *schan, int irq)
203{
204 struct sudmac_chan *sc = to_chan(schan);
205 u32 dintsts = sudmac_readl(sc, SUDMAC_DINTSTS);
206
207 if (!(dintsts & sc->dint_end_bit))
208 return false;
209
210 /* DMA stop */
211 sudmac_dma_halt(sc);
212
213 return true;
214}
215
216static size_t sudmac_get_partial(struct shdma_chan *schan,
217 struct shdma_desc *sdesc)
218{
219 struct sudmac_chan *sc = to_chan(schan);
220 struct sudmac_desc *sd = to_desc(sdesc);
221 u32 current_byte_count = sudmac_readl(sc, SUDMAC_CH0CBC + sc->offset);
222
223 return sd->hw.base_byte_count - current_byte_count;
224}
225
226static bool sudmac_desc_completed(struct shdma_chan *schan,
227 struct shdma_desc *sdesc)
228{
229 struct sudmac_chan *sc = to_chan(schan);
230 struct sudmac_desc *sd = to_desc(sdesc);
231 u32 current_addr = sudmac_readl(sc, SUDMAC_CH0CA + sc->offset);
232
233 return sd->hw.base_addr + sd->hw.base_byte_count == current_addr;
234}
235
236static int sudmac_chan_probe(struct sudmac_device *su_dev, int id, int irq,
237 unsigned long flags)
238{
239 struct shdma_dev *sdev = &su_dev->shdma_dev;
240 struct platform_device *pdev = to_platform_device(sdev->dma_dev.dev);
241 struct sudmac_chan *sc;
242 struct shdma_chan *schan;
243 int err;
244
245 sc = devm_kzalloc(&pdev->dev, sizeof(struct sudmac_chan), GFP_KERNEL);
246 if (!sc) {
247 dev_err(sdev->dma_dev.dev,
248 "No free memory for allocating dma channels!\n");
249 return -ENOMEM;
250 }
251
252 schan = &sc->shdma_chan;
253 schan->max_xfer_len = 64 * 1024 * 1024 - 1;
254
255 shdma_chan_probe(sdev, schan, id);
256
257 sc->base = su_dev->chan_reg;
258
259 /* get platform_data */
260 sc->offset = su_dev->pdata->channel->offset;
261 if (su_dev->pdata->channel->config & SUDMAC_TX_BUFFER_MODE)
262 sc->cfg |= SUDMAC_SENDBUFM;
263 if (su_dev->pdata->channel->config & SUDMAC_RX_END_MODE)
264 sc->cfg |= SUDMAC_RCVENDM;
265 sc->cfg |= (su_dev->pdata->channel->wait << 4) & SUDMAC_LBA_WAIT;
266
267 if (su_dev->pdata->channel->dint_end_bit & SUDMAC_DMA_BIT_CH0)
268 sc->dint_end_bit |= SUDMAC_CH0ENDE;
269 if (su_dev->pdata->channel->dint_end_bit & SUDMAC_DMA_BIT_CH1)
270 sc->dint_end_bit |= SUDMAC_CH1ENDE;
271
272 /* set up channel irq */
273 if (pdev->id >= 0)
274 snprintf(sc->dev_id, sizeof(sc->dev_id), "sudmac%d.%d",
275 pdev->id, id);
276 else
277 snprintf(sc->dev_id, sizeof(sc->dev_id), "sudmac%d", id);
278
279 err = shdma_request_irq(schan, irq, flags, sc->dev_id);
280 if (err) {
281 dev_err(sdev->dma_dev.dev,
282 "DMA channel %d request_irq failed %d\n", id, err);
283 goto err_no_irq;
284 }
285
286 return 0;
287
288err_no_irq:
289 /* remove from dmaengine device node */
290 shdma_chan_remove(schan);
291 return err;
292}
293
294static void sudmac_chan_remove(struct sudmac_device *su_dev)
295{
296 struct dma_device *dma_dev = &su_dev->shdma_dev.dma_dev;
297 struct shdma_chan *schan;
298 int i;
299
300 shdma_for_each_chan(schan, &su_dev->shdma_dev, i) {
301 struct sudmac_chan *sc = to_chan(schan);
302
303 BUG_ON(!schan);
304
305 shdma_free_irq(&sc->shdma_chan);
306 shdma_chan_remove(schan);
307 }
308 dma_dev->chancnt = 0;
309}
310
311static dma_addr_t sudmac_slave_addr(struct shdma_chan *schan)
312{
313 /* SUDMAC doesn't need the address */
314 return 0;
315}
316
317static struct shdma_desc *sudmac_embedded_desc(void *buf, int i)
318{
319 return &((struct sudmac_desc *)buf)[i].shdma_desc;
320}
321
322static const struct shdma_ops sudmac_shdma_ops = {
323 .desc_completed = sudmac_desc_completed,
324 .halt_channel = sudmac_halt,
325 .channel_busy = sudmac_channel_busy,
326 .slave_addr = sudmac_slave_addr,
327 .desc_setup = sudmac_desc_setup,
328 .set_slave = sudmac_set_slave,
329 .setup_xfer = sudmac_setup_xfer,
330 .start_xfer = sudmac_start_xfer,
331 .embedded_desc = sudmac_embedded_desc,
332 .chan_irq = sudmac_chan_irq,
333 .get_partial = sudmac_get_partial,
334};
335
336static int sudmac_probe(struct platform_device *pdev)
337{
338 struct sudmac_pdata *pdata = pdev->dev.platform_data;
339 int err, i;
340 struct sudmac_device *su_dev;
341 struct dma_device *dma_dev;
342 struct resource *chan, *irq_res;
343
344 /* get platform data */
345 if (!pdata)
346 return -ENODEV;
347
348 chan = platform_get_resource(pdev, IORESOURCE_MEM, 0);
349 irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
350 if (!chan || !irq_res)
351 return -ENODEV;
352
353 err = -ENOMEM;
354 su_dev = devm_kzalloc(&pdev->dev, sizeof(struct sudmac_device),
355 GFP_KERNEL);
356 if (!su_dev) {
357 dev_err(&pdev->dev, "Not enough memory\n");
358 return err;
359 }
360
361 dma_dev = &su_dev->shdma_dev.dma_dev;
362
363 su_dev->chan_reg = devm_request_and_ioremap(&pdev->dev, chan);
364 if (!su_dev->chan_reg)
365 return err;
366
367 dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
368
369 su_dev->shdma_dev.ops = &sudmac_shdma_ops;
370 su_dev->shdma_dev.desc_size = sizeof(struct sudmac_desc);
371 err = shdma_init(&pdev->dev, &su_dev->shdma_dev, pdata->channel_num);
372 if (err < 0)
373 return err;
374
375 /* platform data */
376 su_dev->pdata = pdev->dev.platform_data;
377
378 platform_set_drvdata(pdev, su_dev);
379
380 /* Create DMA Channel */
381 for (i = 0; i < pdata->channel_num; i++) {
382 err = sudmac_chan_probe(su_dev, i, irq_res->start, IRQF_SHARED);
383 if (err)
384 goto chan_probe_err;
385 }
386
387 err = dma_async_device_register(&su_dev->shdma_dev.dma_dev);
388 if (err < 0)
389 goto chan_probe_err;
390
391 return err;
392
393chan_probe_err:
394 sudmac_chan_remove(su_dev);
395
396 platform_set_drvdata(pdev, NULL);
397 shdma_cleanup(&su_dev->shdma_dev);
398
399 return err;
400}
401
402static int sudmac_remove(struct platform_device *pdev)
403{
404 struct sudmac_device *su_dev = platform_get_drvdata(pdev);
405 struct dma_device *dma_dev = &su_dev->shdma_dev.dma_dev;
406
407 dma_async_device_unregister(dma_dev);
408 sudmac_chan_remove(su_dev);
409 shdma_cleanup(&su_dev->shdma_dev);
410 platform_set_drvdata(pdev, NULL);
411
412 return 0;
413}
414
415static struct platform_driver sudmac_driver = {
416 .driver = {
417 .owner = THIS_MODULE,
418 .name = SUDMAC_DRV_NAME,
419 },
420 .probe = sudmac_probe,
421 .remove = sudmac_remove,
422};
423module_platform_driver(sudmac_driver);
424
425MODULE_AUTHOR("Yoshihiro Shimoda");
426MODULE_DESCRIPTION("Renesas SUDMAC driver");
427MODULE_LICENSE("GPL v2");
428MODULE_ALIAS("platform:" SUDMAC_DRV_NAME);