aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/drivers/dma
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/sh/drivers/dma
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/sh/drivers/dma')
-rw-r--r--arch/sh/drivers/dma/Kconfig55
-rw-r--r--arch/sh/drivers/dma/Makefile9
-rw-r--r--arch/sh/drivers/dma/dma-api.c292
-rw-r--r--arch/sh/drivers/dma/dma-g2.c171
-rw-r--r--arch/sh/drivers/dma/dma-isa.c106
-rw-r--r--arch/sh/drivers/dma/dma-pvr2.c109
-rw-r--r--arch/sh/drivers/dma/dma-sh.c267
-rw-r--r--arch/sh/drivers/dma/dma-sh.h52
-rw-r--r--arch/sh/drivers/dma/dma-sysfs.c133
9 files changed, 1194 insertions, 0 deletions
diff --git a/arch/sh/drivers/dma/Kconfig b/arch/sh/drivers/dma/Kconfig
new file mode 100644
index 000000000000..0f15216cd39d
--- /dev/null
+++ b/arch/sh/drivers/dma/Kconfig
@@ -0,0 +1,55 @@
1menu "DMA support"
2
3config SH_DMA
4 bool "DMA controller (DMAC) support"
5 help
6 Selecting this option will provide same API as PC's Direct Memory
7 Access Controller(8237A) for SuperH DMAC.
8
9 If unsure, say N.
10
11config NR_ONCHIP_DMA_CHANNELS
12 depends on SH_DMA
13 int "Number of on-chip DMAC channels"
14 default "4"
15 help
16 This allows you to specify the number of channels that the on-chip
17 DMAC supports. This will be 4 for SH7750/SH7751 and 8 for the
18 SH7750R/SH7751R.
19
20config NR_DMA_CHANNELS_BOOL
21 depends on SH_DMA
22 bool "Override default number of maximum DMA channels"
23 help
24 This allows you to forcibly update the maximum number of supported
25 DMA channels for a given board. If this is unset, this will default
26 to the number of channels that the on-chip DMAC has.
27
28config NR_DMA_CHANNELS
29 int "Maximum number of DMA channels"
30 depends on SH_DMA && NR_DMA_CHANNELS_BOOL
31 default NR_ONCHIP_DMA_CHANNELS
32 help
33 This allows you to specify the maximum number of DMA channels to
34 support. Setting this to a higher value allows for cascading DMACs
35 with additional channels.
36
37config DMA_PAGE_OPS
38 bool "Use DMAC for page copy/clear"
39 depends on SH_DMA && BROKEN
40 help
41 Selecting this option will use a dual-address mode configured channel
42 in the SH DMAC for copy_page()/clear_page(). Primarily a performance
43 hack.
44
45config DMA_PAGE_OPS_CHANNEL
46 depends on DMA_PAGE_OPS
47 int "DMA channel for sh memory-manager page copy/clear"
48 default "3"
49 help
50 This allows the specification of the dual address dma channel,
51 in case channel 3 is unavailable. On the SH4, channels 1,2, and 3
52 are dual-address capable.
53
54endmenu
55
diff --git a/arch/sh/drivers/dma/Makefile b/arch/sh/drivers/dma/Makefile
new file mode 100644
index 000000000000..065d4c90970e
--- /dev/null
+++ b/arch/sh/drivers/dma/Makefile
@@ -0,0 +1,9 @@
1#
2# Makefile for the SuperH DMA specific kernel interface routines under Linux.
3#
4
5obj-y += dma-api.o dma-isa.o
6obj-$(CONFIG_SYSFS) += dma-sysfs.o
7obj-$(CONFIG_SH_DMA) += dma-sh.o
8obj-$(CONFIG_SH_DREAMCAST) += dma-pvr2.o dma-g2.o
9
diff --git a/arch/sh/drivers/dma/dma-api.c b/arch/sh/drivers/dma/dma-api.c
new file mode 100644
index 000000000000..96e3036ec2bb
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-api.c
@@ -0,0 +1,292 @@
1/*
2 * arch/sh/drivers/dma/dma-api.c
3 *
4 * SuperH-specific DMA management API
5 *
6 * Copyright (C) 2003, 2004 Paul Mundt
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file "COPYING" in the main directory of this archive
10 * for more details.
11 */
12#include <linux/init.h>
13#include <linux/module.h>
14#include <linux/interrupt.h>
15#include <linux/spinlock.h>
16#include <linux/proc_fs.h>
17#include <linux/list.h>
18#include <asm/dma.h>
19
20DEFINE_SPINLOCK(dma_spin_lock);
21static LIST_HEAD(registered_dmac_list);
22
23/*
24 * A brief note about the reasons for this API as it stands.
25 *
26 * For starters, the old ISA DMA API didn't work for us for a number of
27 * reasons, for one, the vast majority of channels on the SH DMAC are
28 * dual-address mode only, and both the new and the old DMA APIs are after the
29 * concept of managing a DMA buffer, which doesn't overly fit this model very
30 * well. In addition to which, the new API is largely geared at IOMMUs and
31 * GARTs, and doesn't even support the channel notion very well.
32 *
33 * The other thing that's a marginal issue, is the sheer number of random DMA
34 * engines that are present (ie, in boards like the Dreamcast), some of which
35 * cascade off of the SH DMAC, and others do not. As such, there was a real
36 * need for a scalable subsystem that could deal with both single and
37 * dual-address mode usage, in addition to interoperating with cascaded DMACs.
38 *
39 * There really isn't any reason why this needs to be SH specific, though I'm
40 * not aware of too many other processors (with the exception of some MIPS)
41 * that have the same concept of a dual address mode, or any real desire to
42 * actually make use of the DMAC even if such a subsystem were exposed
43 * elsewhere.
44 *
45 * The idea for this was derived from the ARM port, which acted as an excellent
46 * reference when trying to address these issues.
47 *
48 * It should also be noted that the decision to add Yet Another DMA API(tm) to
49 * the kernel wasn't made easily, and was only decided upon after conferring
50 * with jejb with regards to the state of the old and new APIs as they applied
51 * to these circumstances. Philip Blundell was also a great help in figuring
52 * out some single-address mode DMA semantics that were otherwise rather
53 * confusing.
54 */
55
56struct dma_info *get_dma_info(unsigned int chan)
57{
58 struct list_head *pos, *tmp;
59 unsigned int total = 0;
60
61 /*
62 * Look for each DMAC's range to determine who the owner of
63 * the channel is.
64 */
65 list_for_each_safe(pos, tmp, &registered_dmac_list) {
66 struct dma_info *info = list_entry(pos, struct dma_info, list);
67
68 total += info->nr_channels;
69 if (chan > total)
70 continue;
71
72 return info;
73 }
74
75 return NULL;
76}
77
78struct dma_channel *get_dma_channel(unsigned int chan)
79{
80 struct dma_info *info = get_dma_info(chan);
81
82 if (!info)
83 return ERR_PTR(-EINVAL);
84
85 return info->channels + chan;
86}
87
88int get_dma_residue(unsigned int chan)
89{
90 struct dma_info *info = get_dma_info(chan);
91 struct dma_channel *channel = &info->channels[chan];
92
93 if (info->ops->get_residue)
94 return info->ops->get_residue(channel);
95
96 return 0;
97}
98
99int request_dma(unsigned int chan, const char *dev_id)
100{
101 struct dma_info *info = get_dma_info(chan);
102 struct dma_channel *channel = &info->channels[chan];
103
104 down(&channel->sem);
105
106 if (!info->ops || chan >= MAX_DMA_CHANNELS) {
107 up(&channel->sem);
108 return -EINVAL;
109 }
110
111 atomic_set(&channel->busy, 1);
112
113 strlcpy(channel->dev_id, dev_id, sizeof(channel->dev_id));
114
115 up(&channel->sem);
116
117 if (info->ops->request)
118 return info->ops->request(channel);
119
120 return 0;
121}
122
123void free_dma(unsigned int chan)
124{
125 struct dma_info *info = get_dma_info(chan);
126 struct dma_channel *channel = &info->channels[chan];
127
128 if (info->ops->free)
129 info->ops->free(channel);
130
131 atomic_set(&channel->busy, 0);
132}
133
134void dma_wait_for_completion(unsigned int chan)
135{
136 struct dma_info *info = get_dma_info(chan);
137 struct dma_channel *channel = &info->channels[chan];
138
139 if (channel->flags & DMA_TEI_CAPABLE) {
140 wait_event(channel->wait_queue,
141 (info->ops->get_residue(channel) == 0));
142 return;
143 }
144
145 while (info->ops->get_residue(channel))
146 cpu_relax();
147}
148
149void dma_configure_channel(unsigned int chan, unsigned long flags)
150{
151 struct dma_info *info = get_dma_info(chan);
152 struct dma_channel *channel = &info->channels[chan];
153
154 if (info->ops->configure)
155 info->ops->configure(channel, flags);
156}
157
158int dma_xfer(unsigned int chan, unsigned long from,
159 unsigned long to, size_t size, unsigned int mode)
160{
161 struct dma_info *info = get_dma_info(chan);
162 struct dma_channel *channel = &info->channels[chan];
163
164 channel->sar = from;
165 channel->dar = to;
166 channel->count = size;
167 channel->mode = mode;
168
169 return info->ops->xfer(channel);
170}
171
172#ifdef CONFIG_PROC_FS
173static int dma_read_proc(char *buf, char **start, off_t off,
174 int len, int *eof, void *data)
175{
176 struct list_head *pos, *tmp;
177 char *p = buf;
178
179 if (list_empty(&registered_dmac_list))
180 return 0;
181
182 /*
183 * Iterate over each registered DMAC
184 */
185 list_for_each_safe(pos, tmp, &registered_dmac_list) {
186 struct dma_info *info = list_entry(pos, struct dma_info, list);
187 int i;
188
189 /*
190 * Iterate over each channel
191 */
192 for (i = 0; i < info->nr_channels; i++) {
193 struct dma_channel *channel = info->channels + i;
194
195 if (!(channel->flags & DMA_CONFIGURED))
196 continue;
197
198 p += sprintf(p, "%2d: %14s %s\n", i,
199 info->name, channel->dev_id);
200 }
201 }
202
203 return p - buf;
204}
205#endif
206
207
208int __init register_dmac(struct dma_info *info)
209{
210 int i;
211
212 INIT_LIST_HEAD(&info->list);
213
214 printk(KERN_INFO "DMA: Registering %s handler (%d channel%s).\n",
215 info->name, info->nr_channels,
216 info->nr_channels > 1 ? "s" : "");
217
218 BUG_ON((info->flags & DMAC_CHANNELS_CONFIGURED) && !info->channels);
219
220 /*
221 * Don't touch pre-configured channels
222 */
223 if (!(info->flags & DMAC_CHANNELS_CONFIGURED)) {
224 unsigned int size;
225
226 size = sizeof(struct dma_channel) * info->nr_channels;
227
228 info->channels = kmalloc(size, GFP_KERNEL);
229 if (!info->channels)
230 return -ENOMEM;
231
232 memset(info->channels, 0, size);
233 }
234
235 for (i = 0; i < info->nr_channels; i++) {
236 struct dma_channel *chan = info->channels + i;
237
238 chan->chan = i;
239
240 memcpy(chan->dev_id, "Unused", 7);
241
242 if (info->flags & DMAC_CHANNELS_TEI_CAPABLE)
243 chan->flags |= DMA_TEI_CAPABLE;
244
245 init_MUTEX(&chan->sem);
246 init_waitqueue_head(&chan->wait_queue);
247
248#ifdef CONFIG_SYSFS
249 dma_create_sysfs_files(chan);
250#endif
251 }
252
253 list_add(&info->list, &registered_dmac_list);
254
255 return 0;
256}
257
258void __exit unregister_dmac(struct dma_info *info)
259{
260 if (!(info->flags & DMAC_CHANNELS_CONFIGURED))
261 kfree(info->channels);
262
263 list_del(&info->list);
264}
265
266static int __init dma_api_init(void)
267{
268 printk("DMA: Registering DMA API.\n");
269
270#ifdef CONFIG_PROC_FS
271 create_proc_read_entry("dma", 0, 0, dma_read_proc, 0);
272#endif
273
274 return 0;
275}
276
277subsys_initcall(dma_api_init);
278
279MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
280MODULE_DESCRIPTION("DMA API for SuperH");
281MODULE_LICENSE("GPL");
282
283EXPORT_SYMBOL(request_dma);
284EXPORT_SYMBOL(free_dma);
285EXPORT_SYMBOL(register_dmac);
286EXPORT_SYMBOL(get_dma_residue);
287EXPORT_SYMBOL(get_dma_info);
288EXPORT_SYMBOL(get_dma_channel);
289EXPORT_SYMBOL(dma_xfer);
290EXPORT_SYMBOL(dma_wait_for_completion);
291EXPORT_SYMBOL(dma_configure_channel);
292
diff --git a/arch/sh/drivers/dma/dma-g2.c b/arch/sh/drivers/dma/dma-g2.c
new file mode 100644
index 000000000000..231e3f6fb28f
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-g2.c
@@ -0,0 +1,171 @@
1/*
2 * arch/sh/drivers/dma/dma-g2.c
3 *
4 * G2 bus DMA support
5 *
6 * Copyright (C) 2003, 2004 Paul Mundt
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file "COPYING" in the main directory of this archive
10 * for more details.
11 */
12#include <linux/init.h>
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/interrupt.h>
16
17#include <asm/mach/sysasic.h>
18#include <asm/mach/dma.h>
19#include <asm/dma.h>
20
21struct g2_channel {
22 unsigned long g2_addr; /* G2 bus address */
23 unsigned long root_addr; /* Root bus (SH-4) address */
24 unsigned long size; /* Size (in bytes), 32-byte aligned */
25 unsigned long direction; /* Transfer direction */
26 unsigned long ctrl; /* Transfer control */
27 unsigned long chan_enable; /* Channel enable */
28 unsigned long xfer_enable; /* Transfer enable */
29 unsigned long xfer_stat; /* Transfer status */
30} __attribute__ ((aligned(32)));
31
32struct g2_status {
33 unsigned long g2_addr;
34 unsigned long root_addr;
35 unsigned long size;
36 unsigned long status;
37} __attribute__ ((aligned(16)));
38
39struct g2_dma_info {
40 struct g2_channel channel[G2_NR_DMA_CHANNELS];
41 unsigned long pad1[G2_NR_DMA_CHANNELS];
42 unsigned long wait_state;
43 unsigned long pad2[10];
44 unsigned long magic;
45 struct g2_status status[G2_NR_DMA_CHANNELS];
46} __attribute__ ((aligned(256)));
47
48static volatile struct g2_dma_info *g2_dma = (volatile struct g2_dma_info *)0xa05f7800;
49
50static irqreturn_t g2_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
51{
52 /* FIXME: Do some meaningful completion work here.. */
53 return IRQ_HANDLED;
54}
55
56static struct irqaction g2_dma_irq = {
57 .name = "g2 DMA handler",
58 .handler = g2_dma_interrupt,
59 .flags = SA_INTERRUPT,
60};
61
62static int g2_enable_dma(struct dma_channel *chan)
63{
64 unsigned int chan_nr = chan->chan;
65
66 g2_dma->channel[chan_nr].chan_enable = 1;
67 g2_dma->channel[chan_nr].xfer_enable = 1;
68
69 return 0;
70}
71
72static int g2_disable_dma(struct dma_channel *chan)
73{
74 unsigned int chan_nr = chan->chan;
75
76 g2_dma->channel[chan_nr].chan_enable = 0;
77 g2_dma->channel[chan_nr].xfer_enable = 0;
78
79 return 0;
80}
81
82static int g2_xfer_dma(struct dma_channel *chan)
83{
84 unsigned int chan_nr = chan->chan;
85
86 if (chan->sar & 31) {
87 printk("g2dma: unaligned source 0x%lx\n", chan->sar);
88 return -EINVAL;
89 }
90
91 if (chan->dar & 31) {
92 printk("g2dma: unaligned dest 0x%lx\n", chan->dar);
93 return -EINVAL;
94 }
95
96 /* Align the count */
97 if (chan->count & 31)
98 chan->count = (chan->count + (32 - 1)) & ~(32 - 1);
99
100 /* Fixup destination */
101 chan->dar += 0xa0800000;
102
103 /* Fixup direction */
104 chan->mode = !chan->mode;
105
106 flush_icache_range((unsigned long)chan->sar, chan->count);
107
108 g2_disable_dma(chan);
109
110 g2_dma->channel[chan_nr].g2_addr = chan->dar & 0x1fffffe0;
111 g2_dma->channel[chan_nr].root_addr = chan->sar & 0x1fffffe0;
112 g2_dma->channel[chan_nr].size = (chan->count & ~31) | 0x80000000;
113 g2_dma->channel[chan_nr].direction = chan->mode;
114
115 /*
116 * bit 0 - ???
117 * bit 1 - if set, generate a hardware event on transfer completion
118 * bit 2 - ??? something to do with suspend?
119 */
120 g2_dma->channel[chan_nr].ctrl = 5; /* ?? */
121
122 g2_enable_dma(chan);
123
124 /* debug cruft */
125 pr_debug("count, sar, dar, mode, ctrl, chan, xfer: %ld, 0x%08lx, "
126 "0x%08lx, %ld, %ld, %ld, %ld\n",
127 g2_dma->channel[chan_nr].size,
128 g2_dma->channel[chan_nr].root_addr,
129 g2_dma->channel[chan_nr].g2_addr,
130 g2_dma->channel[chan_nr].direction,
131 g2_dma->channel[chan_nr].ctrl,
132 g2_dma->channel[chan_nr].chan_enable,
133 g2_dma->channel[chan_nr].xfer_enable);
134
135 return 0;
136}
137
138static struct dma_ops g2_dma_ops = {
139 .xfer = g2_xfer_dma,
140};
141
142static struct dma_info g2_dma_info = {
143 .name = "G2 DMA",
144 .nr_channels = 4,
145 .ops = &g2_dma_ops,
146 .flags = DMAC_CHANNELS_TEI_CAPABLE,
147};
148
149static int __init g2_dma_init(void)
150{
151 setup_irq(HW_EVENT_G2_DMA, &g2_dma_irq);
152
153 /* Magic */
154 g2_dma->wait_state = 27;
155 g2_dma->magic = 0x4659404f;
156
157 return register_dmac(&g2_dma_info);
158}
159
160static void __exit g2_dma_exit(void)
161{
162 free_irq(HW_EVENT_G2_DMA, 0);
163}
164
165subsys_initcall(g2_dma_init);
166module_exit(g2_dma_exit);
167
168MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
169MODULE_DESCRIPTION("G2 bus DMA driver");
170MODULE_LICENSE("GPL");
171
diff --git a/arch/sh/drivers/dma/dma-isa.c b/arch/sh/drivers/dma/dma-isa.c
new file mode 100644
index 000000000000..1c9bc45b8bcb
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-isa.c
@@ -0,0 +1,106 @@
1/*
2 * arch/sh/drivers/dma/dma-isa.c
3 *
4 * Generic ISA DMA wrapper for SH DMA API
5 *
6 * Copyright (C) 2003, 2004 Paul Mundt
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file "COPYING" in the main directory of this archive
10 * for more details.
11 */
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <asm/dma.h>
15
16/*
17 * This implements a small wrapper set to make code using the old ISA DMA API
18 * work with the SH DMA API. Since most of the work in the new API happens
19 * at ops->xfer() time, we simply use the various set_dma_xxx() routines to
20 * fill in per-channel info, and then hand hand this off to ops->xfer() at
21 * enable_dma() time.
22 *
23 * For channels that are doing on-demand data transfer via cascading, the
24 * channel itself will still need to be configured through the new API. As
25 * such, this code is meant for only the simplest of tasks (and shouldn't be
26 * used in any new drivers at all).
27 *
28 * It should also be noted that various functions here are labelled as
29 * being deprecated. This is due to the fact that the ops->xfer() method is
30 * the preferred way of doing things (as well as just grabbing the spinlock
31 * directly). As such, any users of this interface will be warned rather
32 * loudly.
33 */
34
35unsigned long __deprecated claim_dma_lock(void)
36{
37 unsigned long flags;
38
39 spin_lock_irqsave(&dma_spin_lock, flags);
40
41 return flags;
42}
43EXPORT_SYMBOL(claim_dma_lock);
44
45void __deprecated release_dma_lock(unsigned long flags)
46{
47 spin_unlock_irqrestore(&dma_spin_lock, flags);
48}
49EXPORT_SYMBOL(release_dma_lock);
50
51void __deprecated disable_dma(unsigned int chan)
52{
53 /* Nothing */
54}
55EXPORT_SYMBOL(disable_dma);
56
57void __deprecated enable_dma(unsigned int chan)
58{
59 struct dma_info *info = get_dma_info(chan);
60 struct dma_channel *channel = &info->channels[chan];
61
62 info->ops->xfer(channel);
63}
64EXPORT_SYMBOL(enable_dma);
65
66void clear_dma_ff(unsigned int chan)
67{
68 /* Nothing */
69}
70EXPORT_SYMBOL(clear_dma_ff);
71
72void set_dma_mode(unsigned int chan, char mode)
73{
74 struct dma_info *info = get_dma_info(chan);
75 struct dma_channel *channel = &info->channels[chan];
76
77 channel->mode = mode;
78}
79EXPORT_SYMBOL(set_dma_mode);
80
81void set_dma_addr(unsigned int chan, unsigned int addr)
82{
83 struct dma_info *info = get_dma_info(chan);
84 struct dma_channel *channel = &info->channels[chan];
85
86 /*
87 * Single address mode is the only thing supported through
88 * this interface.
89 */
90 if ((channel->mode & DMA_MODE_MASK) == DMA_MODE_READ) {
91 channel->sar = addr;
92 } else {
93 channel->dar = addr;
94 }
95}
96EXPORT_SYMBOL(set_dma_addr);
97
98void set_dma_count(unsigned int chan, unsigned int count)
99{
100 struct dma_info *info = get_dma_info(chan);
101 struct dma_channel *channel = &info->channels[chan];
102
103 channel->count = count;
104}
105EXPORT_SYMBOL(set_dma_count);
106
diff --git a/arch/sh/drivers/dma/dma-pvr2.c b/arch/sh/drivers/dma/dma-pvr2.c
new file mode 100644
index 000000000000..2e1d58f2d1b9
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-pvr2.c
@@ -0,0 +1,109 @@
1/*
2 * arch/sh/boards/dreamcast/dma-pvr2.c
3 *
4 * NEC PowerVR 2 (Dreamcast) DMA support
5 *
6 * Copyright (C) 2003, 2004 Paul Mundt
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file "COPYING" in the main directory of this archive
10 * for more details.
11 */
12#include <linux/init.h>
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/interrupt.h>
16#include <asm/mach/sysasic.h>
17#include <asm/mach/dma.h>
18#include <asm/dma.h>
19#include <asm/io.h>
20
21static unsigned int xfer_complete = 0;
22static int count = 0;
23
24static irqreturn_t pvr2_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
25{
26 if (get_dma_residue(PVR2_CASCADE_CHAN)) {
27 printk(KERN_WARNING "DMA: SH DMAC did not complete transfer "
28 "on channel %d, waiting..\n", PVR2_CASCADE_CHAN);
29 dma_wait_for_completion(PVR2_CASCADE_CHAN);
30 }
31
32 if (count++ < 10)
33 pr_debug("Got a pvr2 dma interrupt for channel %d\n",
34 irq - HW_EVENT_PVR2_DMA);
35
36 xfer_complete = 1;
37
38 return IRQ_HANDLED;
39}
40
41static int pvr2_request_dma(struct dma_channel *chan)
42{
43 if (ctrl_inl(PVR2_DMA_MODE) != 0)
44 return -EBUSY;
45
46 ctrl_outl(0, PVR2_DMA_LMMODE0);
47
48 return 0;
49}
50
51static int pvr2_get_dma_residue(struct dma_channel *chan)
52{
53 return xfer_complete == 0;
54}
55
56static int pvr2_xfer_dma(struct dma_channel *chan)
57{
58 if (chan->sar || !chan->dar)
59 return -EINVAL;
60
61 xfer_complete = 0;
62
63 ctrl_outl(chan->dar, PVR2_DMA_ADDR);
64 ctrl_outl(chan->count, PVR2_DMA_COUNT);
65 ctrl_outl(chan->mode & DMA_MODE_MASK, PVR2_DMA_MODE);
66
67 return 0;
68}
69
70static struct irqaction pvr2_dma_irq = {
71 .name = "pvr2 DMA handler",
72 .handler = pvr2_dma_interrupt,
73 .flags = SA_INTERRUPT,
74};
75
76static struct dma_ops pvr2_dma_ops = {
77 .request = pvr2_request_dma,
78 .get_residue = pvr2_get_dma_residue,
79 .xfer = pvr2_xfer_dma,
80};
81
82static struct dma_info pvr2_dma_info = {
83 .name = "PowerVR 2 DMA",
84 .nr_channels = 1,
85 .ops = &pvr2_dma_ops,
86 .flags = DMAC_CHANNELS_TEI_CAPABLE,
87};
88
89static int __init pvr2_dma_init(void)
90{
91 setup_irq(HW_EVENT_PVR2_DMA, &pvr2_dma_irq);
92 request_dma(PVR2_CASCADE_CHAN, "pvr2 cascade");
93
94 return register_dmac(&pvr2_dma_info);
95}
96
97static void __exit pvr2_dma_exit(void)
98{
99 free_dma(PVR2_CASCADE_CHAN);
100 free_irq(HW_EVENT_PVR2_DMA, 0);
101}
102
103subsys_initcall(pvr2_dma_init);
104module_exit(pvr2_dma_exit);
105
106MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
107MODULE_DESCRIPTION("NEC PowerVR 2 DMA driver");
108MODULE_LICENSE("GPL");
109
diff --git a/arch/sh/drivers/dma/dma-sh.c b/arch/sh/drivers/dma/dma-sh.c
new file mode 100644
index 000000000000..31dacd4444b2
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-sh.c
@@ -0,0 +1,267 @@
1/*
2 * arch/sh/drivers/dma/dma-sh.c
3 *
4 * SuperH On-chip DMAC Support
5 *
6 * Copyright (C) 2000 Takashi YOSHII
7 * Copyright (C) 2003, 2004 Paul Mundt
8 *
9 * This file is subject to the terms and conditions of the GNU General Public
10 * License. See the file "COPYING" in the main directory of this archive
11 * for more details.
12 */
13
14#include <linux/config.h>
15#include <linux/init.h>
16#include <linux/irq.h>
17#include <linux/interrupt.h>
18#include <linux/module.h>
19#include <asm/signal.h>
20#include <asm/irq.h>
21#include <asm/dma.h>
22#include <asm/io.h>
23#include "dma-sh.h"
24
25/*
26 * The SuperH DMAC supports a number of transmit sizes, we list them here,
27 * with their respective values as they appear in the CHCR registers.
28 *
29 * Defaults to a 64-bit transfer size.
30 */
31enum {
32 XMIT_SZ_64BIT,
33 XMIT_SZ_8BIT,
34 XMIT_SZ_16BIT,
35 XMIT_SZ_32BIT,
36 XMIT_SZ_256BIT,
37};
38
39/*
40 * The DMA count is defined as the number of bytes to transfer.
41 */
42static unsigned int ts_shift[] = {
43 [XMIT_SZ_64BIT] = 3,
44 [XMIT_SZ_8BIT] = 0,
45 [XMIT_SZ_16BIT] = 1,
46 [XMIT_SZ_32BIT] = 2,
47 [XMIT_SZ_256BIT] = 5,
48};
49
50static inline unsigned int get_dmte_irq(unsigned int chan)
51{
52 unsigned int irq;
53
54 /*
55 * Normally we could just do DMTE0_IRQ + chan outright, though in the
56 * case of the 7751R, the DMTE IRQs for channels > 4 start right above
57 * the SCIF
58 */
59
60 if (chan < 4) {
61 irq = DMTE0_IRQ + chan;
62 } else {
63 irq = DMTE4_IRQ + chan - 4;
64 }
65
66 return irq;
67}
68
69/*
70 * We determine the correct shift size based off of the CHCR transmit size
71 * for the given channel. Since we know that it will take:
72 *
73 * info->count >> ts_shift[transmit_size]
74 *
75 * iterations to complete the transfer.
76 */
77static inline unsigned int calc_xmit_shift(struct dma_channel *chan)
78{
79 u32 chcr = ctrl_inl(CHCR[chan->chan]);
80
81 chcr >>= 4;
82
83 return ts_shift[chcr & 0x0007];
84}
85
86/*
87 * The transfer end interrupt must read the chcr register to end the
88 * hardware interrupt active condition.
89 * Besides that it needs to waken any waiting process, which should handle
90 * setting up the next transfer.
91 */
92static irqreturn_t dma_tei(int irq, void *dev_id, struct pt_regs *regs)
93{
94 struct dma_channel *chan = (struct dma_channel *)dev_id;
95 u32 chcr;
96
97 chcr = ctrl_inl(CHCR[chan->chan]);
98
99 if (!(chcr & CHCR_TE))
100 return IRQ_NONE;
101
102 chcr &= ~(CHCR_IE | CHCR_DE);
103 ctrl_outl(chcr, CHCR[chan->chan]);
104
105 wake_up(&chan->wait_queue);
106
107 return IRQ_HANDLED;
108}
109
110static int sh_dmac_request_dma(struct dma_channel *chan)
111{
112 return request_irq(get_dmte_irq(chan->chan), dma_tei,
113 SA_INTERRUPT, "DMAC Transfer End", chan);
114}
115
116static void sh_dmac_free_dma(struct dma_channel *chan)
117{
118 free_irq(get_dmte_irq(chan->chan), chan);
119}
120
121static void sh_dmac_configure_channel(struct dma_channel *chan, unsigned long chcr)
122{
123 if (!chcr)
124 chcr = RS_DUAL;
125
126 ctrl_outl(chcr, CHCR[chan->chan]);
127
128 chan->flags |= DMA_CONFIGURED;
129}
130
131static void sh_dmac_enable_dma(struct dma_channel *chan)
132{
133 int irq = get_dmte_irq(chan->chan);
134 u32 chcr;
135
136 chcr = ctrl_inl(CHCR[chan->chan]);
137 chcr |= CHCR_DE | CHCR_IE;
138 ctrl_outl(chcr, CHCR[chan->chan]);
139
140 enable_irq(irq);
141}
142
143static void sh_dmac_disable_dma(struct dma_channel *chan)
144{
145 int irq = get_dmte_irq(chan->chan);
146 u32 chcr;
147
148 disable_irq(irq);
149
150 chcr = ctrl_inl(CHCR[chan->chan]);
151 chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE);
152 ctrl_outl(chcr, CHCR[chan->chan]);
153}
154
155static int sh_dmac_xfer_dma(struct dma_channel *chan)
156{
157 /*
158 * If we haven't pre-configured the channel with special flags, use
159 * the defaults.
160 */
161 if (!(chan->flags & DMA_CONFIGURED))
162 sh_dmac_configure_channel(chan, 0);
163
164 sh_dmac_disable_dma(chan);
165
166 /*
167 * Single-address mode usage note!
168 *
169 * It's important that we don't accidentally write any value to SAR/DAR
170 * (this includes 0) that hasn't been directly specified by the user if
171 * we're in single-address mode.
172 *
173 * In this case, only one address can be defined, anything else will
174 * result in a DMA address error interrupt (at least on the SH-4),
175 * which will subsequently halt the transfer.
176 *
177 * Channel 2 on the Dreamcast is a special case, as this is used for
178 * cascading to the PVR2 DMAC. In this case, we still need to write
179 * SAR and DAR, regardless of value, in order for cascading to work.
180 */
181 if (chan->sar || (mach_is_dreamcast() && chan->chan == 2))
182 ctrl_outl(chan->sar, SAR[chan->chan]);
183 if (chan->dar || (mach_is_dreamcast() && chan->chan == 2))
184 ctrl_outl(chan->dar, DAR[chan->chan]);
185
186 ctrl_outl(chan->count >> calc_xmit_shift(chan), DMATCR[chan->chan]);
187
188 sh_dmac_enable_dma(chan);
189
190 return 0;
191}
192
193static int sh_dmac_get_dma_residue(struct dma_channel *chan)
194{
195 if (!(ctrl_inl(CHCR[chan->chan]) & CHCR_DE))
196 return 0;
197
198 return ctrl_inl(DMATCR[chan->chan]) << calc_xmit_shift(chan);
199}
200
201#if defined(CONFIG_CPU_SH4)
202static irqreturn_t dma_err(int irq, void *dev_id, struct pt_regs *regs)
203{
204 unsigned long dmaor = ctrl_inl(DMAOR);
205
206 printk("DMAE: DMAOR=%lx\n", dmaor);
207
208 ctrl_outl(ctrl_inl(DMAOR)&~DMAOR_NMIF, DMAOR);
209 ctrl_outl(ctrl_inl(DMAOR)&~DMAOR_AE, DMAOR);
210 ctrl_outl(ctrl_inl(DMAOR)|DMAOR_DME, DMAOR);
211
212 disable_irq(irq);
213
214 return IRQ_HANDLED;
215}
216#endif
217
218static struct dma_ops sh_dmac_ops = {
219 .request = sh_dmac_request_dma,
220 .free = sh_dmac_free_dma,
221 .get_residue = sh_dmac_get_dma_residue,
222 .xfer = sh_dmac_xfer_dma,
223 .configure = sh_dmac_configure_channel,
224};
225
226static struct dma_info sh_dmac_info = {
227 .name = "SuperH DMAC",
228 .nr_channels = 4,
229 .ops = &sh_dmac_ops,
230 .flags = DMAC_CHANNELS_TEI_CAPABLE,
231};
232
233static int __init sh_dmac_init(void)
234{
235 struct dma_info *info = &sh_dmac_info;
236 int i;
237
238#ifdef CONFIG_CPU_SH4
239 make_ipr_irq(DMAE_IRQ, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY);
240 i = request_irq(DMAE_IRQ, dma_err, SA_INTERRUPT, "DMAC Address Error", 0);
241 if (i < 0)
242 return i;
243#endif
244
245 for (i = 0; i < info->nr_channels; i++) {
246 int irq = get_dmte_irq(i);
247
248 make_ipr_irq(irq, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY);
249 }
250
251 ctrl_outl(0x8000 | DMAOR_DME, DMAOR);
252
253 return register_dmac(info);
254}
255
256static void __exit sh_dmac_exit(void)
257{
258#ifdef CONFIG_CPU_SH4
259 free_irq(DMAE_IRQ, 0);
260#endif
261}
262
263subsys_initcall(sh_dmac_init);
264module_exit(sh_dmac_exit);
265
266MODULE_LICENSE("GPL");
267
diff --git a/arch/sh/drivers/dma/dma-sh.h b/arch/sh/drivers/dma/dma-sh.h
new file mode 100644
index 000000000000..dd9d547539a2
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-sh.h
@@ -0,0 +1,52 @@
1/*
2 * arch/sh/drivers/dma/dma-sh.h
3 *
4 * Copyright (C) 2000 Takashi YOSHII
5 * Copyright (C) 2003 Paul Mundt
6 *
7 * This file is subject to the terms and conditions of the GNU General Public
8 * License. See the file "COPYING" in the main directory of this archive
9 * for more details.
10 */
11#ifndef __DMA_SH_H
12#define __DMA_SH_H
13
14/* Definitions for the SuperH DMAC */
15#define REQ_L 0x00000000
16#define REQ_E 0x00080000
17#define RACK_H 0x00000000
18#define RACK_L 0x00040000
19#define ACK_R 0x00000000
20#define ACK_W 0x00020000
21#define ACK_H 0x00000000
22#define ACK_L 0x00010000
23#define DM_INC 0x00004000
24#define DM_DEC 0x00008000
25#define SM_INC 0x00001000
26#define SM_DEC 0x00002000
27#define RS_IN 0x00000200
28#define RS_OUT 0x00000300
29#define TM_BURST 0x0000080
30#define TS_8 0x00000010
31#define TS_16 0x00000020
32#define TS_32 0x00000030
33#define TS_64 0x00000000
34#define TS_BLK 0x00000040
35#define CHCR_DE 0x00000001
36#define CHCR_TE 0x00000002
37#define CHCR_IE 0x00000004
38
39/* Define the default configuration for dual address memory-memory transfer.
40 * The 0x400 value represents auto-request, external->external.
41 */
42#define RS_DUAL (DM_INC | SM_INC | 0x400 | TS_32)
43
44#define DMAOR_COD 0x00000008
45#define DMAOR_AE 0x00000004
46#define DMAOR_NMIF 0x00000002
47#define DMAOR_DME 0x00000001
48
49#define MAX_DMAC_CHANNELS (CONFIG_NR_ONCHIP_DMA_CHANNELS)
50
51#endif /* __DMA_SH_H */
52
diff --git a/arch/sh/drivers/dma/dma-sysfs.c b/arch/sh/drivers/dma/dma-sysfs.c
new file mode 100644
index 000000000000..71a6d4e7809f
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-sysfs.c
@@ -0,0 +1,133 @@
1/*
2 * arch/sh/drivers/dma/dma-sysfs.c
3 *
4 * sysfs interface for SH DMA API
5 *
6 * Copyright (C) 2004 Paul Mundt
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file "COPYING" in the main directory of this archive
10 * for more details.
11 */
12#include <linux/kernel.h>
13#include <linux/init.h>
14#include <linux/sysdev.h>
15#include <linux/module.h>
16#include <asm/dma.h>
17
18static struct sysdev_class dma_sysclass = {
19 set_kset_name("dma"),
20};
21
22EXPORT_SYMBOL(dma_sysclass);
23
24static ssize_t dma_show_devices(struct sys_device *dev, char *buf)
25{
26 ssize_t len = 0;
27 int i;
28
29 for (i = 0; i < MAX_DMA_CHANNELS; i++) {
30 struct dma_info *info = get_dma_info(i);
31 struct dma_channel *channel = &info->channels[i];
32
33 len += sprintf(buf + len, "%2d: %14s %s\n",
34 channel->chan, info->name,
35 channel->dev_id);
36 }
37
38 return len;
39}
40
41static SYSDEV_ATTR(devices, S_IRUGO, dma_show_devices, NULL);
42
43static int __init dma_sysclass_init(void)
44{
45 int ret;
46
47 ret = sysdev_class_register(&dma_sysclass);
48 if (ret == 0)
49 sysfs_create_file(&dma_sysclass.kset.kobj, &attr_devices.attr);
50
51 return ret;
52}
53
54postcore_initcall(dma_sysclass_init);
55
56static ssize_t dma_show_dev_id(struct sys_device *dev, char *buf)
57{
58 struct dma_channel *channel = to_dma_channel(dev);
59 return sprintf(buf, "%s\n", channel->dev_id);
60}
61
62static ssize_t dma_store_dev_id(struct sys_device *dev,
63 const char *buf, size_t count)
64{
65 struct dma_channel *channel = to_dma_channel(dev);
66 strcpy(channel->dev_id, buf);
67 return count;
68}
69
70static SYSDEV_ATTR(dev_id, S_IRUGO | S_IWUSR, dma_show_dev_id, dma_store_dev_id);
71
72static ssize_t dma_store_config(struct sys_device *dev,
73 const char *buf, size_t count)
74{
75 struct dma_channel *channel = to_dma_channel(dev);
76 unsigned long config;
77
78 config = simple_strtoul(buf, NULL, 0);
79 dma_configure_channel(channel->chan, config);
80
81 return count;
82}
83
84static SYSDEV_ATTR(config, S_IWUSR, NULL, dma_store_config);
85
86static ssize_t dma_show_mode(struct sys_device *dev, char *buf)
87{
88 struct dma_channel *channel = to_dma_channel(dev);
89 return sprintf(buf, "0x%08x\n", channel->mode);
90}
91
92static ssize_t dma_store_mode(struct sys_device *dev,
93 const char *buf, size_t count)
94{
95 struct dma_channel *channel = to_dma_channel(dev);
96 channel->mode = simple_strtoul(buf, NULL, 0);
97 return count;
98}
99
100static SYSDEV_ATTR(mode, S_IRUGO | S_IWUSR, dma_show_mode, dma_store_mode);
101
102#define dma_ro_attr(field, fmt) \
103static ssize_t dma_show_##field(struct sys_device *dev, char *buf) \
104{ \
105 struct dma_channel *channel = to_dma_channel(dev); \
106 return sprintf(buf, fmt, channel->field); \
107} \
108static SYSDEV_ATTR(field, S_IRUGO, dma_show_##field, NULL);
109
110dma_ro_attr(count, "0x%08x\n");
111dma_ro_attr(flags, "0x%08lx\n");
112
113int __init dma_create_sysfs_files(struct dma_channel *chan)
114{
115 struct sys_device *dev = &chan->dev;
116 int ret;
117
118 dev->id = chan->chan;
119 dev->cls = &dma_sysclass;
120
121 ret = sysdev_register(dev);
122 if (ret)
123 return ret;
124
125 sysdev_create_file(dev, &attr_dev_id);
126 sysdev_create_file(dev, &attr_count);
127 sysdev_create_file(dev, &attr_mode);
128 sysdev_create_file(dev, &attr_flags);
129 sysdev_create_file(dev, &attr_config);
130
131 return 0;
132}
133