diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /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/Kconfig | 55 | ||||
-rw-r--r-- | arch/sh/drivers/dma/Makefile | 9 | ||||
-rw-r--r-- | arch/sh/drivers/dma/dma-api.c | 292 | ||||
-rw-r--r-- | arch/sh/drivers/dma/dma-g2.c | 171 | ||||
-rw-r--r-- | arch/sh/drivers/dma/dma-isa.c | 106 | ||||
-rw-r--r-- | arch/sh/drivers/dma/dma-pvr2.c | 109 | ||||
-rw-r--r-- | arch/sh/drivers/dma/dma-sh.c | 267 | ||||
-rw-r--r-- | arch/sh/drivers/dma/dma-sh.h | 52 | ||||
-rw-r--r-- | arch/sh/drivers/dma/dma-sysfs.c | 133 |
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 @@ | |||
1 | menu "DMA support" | ||
2 | |||
3 | config 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 | |||
11 | config 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 | |||
20 | config 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 | |||
28 | config 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 | |||
37 | config 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 | |||
45 | config 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 | |||
54 | endmenu | ||
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 | |||
5 | obj-y += dma-api.o dma-isa.o | ||
6 | obj-$(CONFIG_SYSFS) += dma-sysfs.o | ||
7 | obj-$(CONFIG_SH_DMA) += dma-sh.o | ||
8 | obj-$(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 | |||
20 | DEFINE_SPINLOCK(dma_spin_lock); | ||
21 | static 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 | |||
56 | struct 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, ®istered_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 | |||
78 | struct 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 | |||
88 | int 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 | |||
99 | int 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 | |||
123 | void 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 | |||
134 | void 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 | |||
149 | void 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 | |||
158 | int 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 | ||
173 | static 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(®istered_dmac_list)) | ||
180 | return 0; | ||
181 | |||
182 | /* | ||
183 | * Iterate over each registered DMAC | ||
184 | */ | ||
185 | list_for_each_safe(pos, tmp, ®istered_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 | |||
208 | int __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, ®istered_dmac_list); | ||
254 | |||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | void __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 | |||
266 | static 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 | |||
277 | subsys_initcall(dma_api_init); | ||
278 | |||
279 | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); | ||
280 | MODULE_DESCRIPTION("DMA API for SuperH"); | ||
281 | MODULE_LICENSE("GPL"); | ||
282 | |||
283 | EXPORT_SYMBOL(request_dma); | ||
284 | EXPORT_SYMBOL(free_dma); | ||
285 | EXPORT_SYMBOL(register_dmac); | ||
286 | EXPORT_SYMBOL(get_dma_residue); | ||
287 | EXPORT_SYMBOL(get_dma_info); | ||
288 | EXPORT_SYMBOL(get_dma_channel); | ||
289 | EXPORT_SYMBOL(dma_xfer); | ||
290 | EXPORT_SYMBOL(dma_wait_for_completion); | ||
291 | EXPORT_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 | |||
21 | struct 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 | |||
32 | struct 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 | |||
39 | struct 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 | |||
48 | static volatile struct g2_dma_info *g2_dma = (volatile struct g2_dma_info *)0xa05f7800; | ||
49 | |||
50 | static 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 | |||
56 | static struct irqaction g2_dma_irq = { | ||
57 | .name = "g2 DMA handler", | ||
58 | .handler = g2_dma_interrupt, | ||
59 | .flags = SA_INTERRUPT, | ||
60 | }; | ||
61 | |||
62 | static 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 | |||
72 | static 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 | |||
82 | static 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 | |||
138 | static struct dma_ops g2_dma_ops = { | ||
139 | .xfer = g2_xfer_dma, | ||
140 | }; | ||
141 | |||
142 | static 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 | |||
149 | static 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 | |||
160 | static void __exit g2_dma_exit(void) | ||
161 | { | ||
162 | free_irq(HW_EVENT_G2_DMA, 0); | ||
163 | } | ||
164 | |||
165 | subsys_initcall(g2_dma_init); | ||
166 | module_exit(g2_dma_exit); | ||
167 | |||
168 | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); | ||
169 | MODULE_DESCRIPTION("G2 bus DMA driver"); | ||
170 | MODULE_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 | |||
35 | unsigned 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 | } | ||
43 | EXPORT_SYMBOL(claim_dma_lock); | ||
44 | |||
45 | void __deprecated release_dma_lock(unsigned long flags) | ||
46 | { | ||
47 | spin_unlock_irqrestore(&dma_spin_lock, flags); | ||
48 | } | ||
49 | EXPORT_SYMBOL(release_dma_lock); | ||
50 | |||
51 | void __deprecated disable_dma(unsigned int chan) | ||
52 | { | ||
53 | /* Nothing */ | ||
54 | } | ||
55 | EXPORT_SYMBOL(disable_dma); | ||
56 | |||
57 | void __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 | } | ||
64 | EXPORT_SYMBOL(enable_dma); | ||
65 | |||
66 | void clear_dma_ff(unsigned int chan) | ||
67 | { | ||
68 | /* Nothing */ | ||
69 | } | ||
70 | EXPORT_SYMBOL(clear_dma_ff); | ||
71 | |||
72 | void 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 | } | ||
79 | EXPORT_SYMBOL(set_dma_mode); | ||
80 | |||
81 | void 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 | } | ||
96 | EXPORT_SYMBOL(set_dma_addr); | ||
97 | |||
98 | void 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 | } | ||
105 | EXPORT_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 | |||
21 | static unsigned int xfer_complete = 0; | ||
22 | static int count = 0; | ||
23 | |||
24 | static 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 | |||
41 | static 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 | |||
51 | static int pvr2_get_dma_residue(struct dma_channel *chan) | ||
52 | { | ||
53 | return xfer_complete == 0; | ||
54 | } | ||
55 | |||
56 | static 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 | |||
70 | static struct irqaction pvr2_dma_irq = { | ||
71 | .name = "pvr2 DMA handler", | ||
72 | .handler = pvr2_dma_interrupt, | ||
73 | .flags = SA_INTERRUPT, | ||
74 | }; | ||
75 | |||
76 | static 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 | |||
82 | static 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 | |||
89 | static 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 | |||
97 | static void __exit pvr2_dma_exit(void) | ||
98 | { | ||
99 | free_dma(PVR2_CASCADE_CHAN); | ||
100 | free_irq(HW_EVENT_PVR2_DMA, 0); | ||
101 | } | ||
102 | |||
103 | subsys_initcall(pvr2_dma_init); | ||
104 | module_exit(pvr2_dma_exit); | ||
105 | |||
106 | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); | ||
107 | MODULE_DESCRIPTION("NEC PowerVR 2 DMA driver"); | ||
108 | MODULE_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 | */ | ||
31 | enum { | ||
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 | */ | ||
42 | static 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 | |||
50 | static 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 | */ | ||
77 | static 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 | */ | ||
92 | static 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 | |||
110 | static 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 | |||
116 | static void sh_dmac_free_dma(struct dma_channel *chan) | ||
117 | { | ||
118 | free_irq(get_dmte_irq(chan->chan), chan); | ||
119 | } | ||
120 | |||
121 | static 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 | |||
131 | static 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 | |||
143 | static 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 | |||
155 | static 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 | |||
193 | static 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) | ||
202 | static 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 | |||
218 | static 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 | |||
226 | static 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 | |||
233 | static 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 | |||
256 | static void __exit sh_dmac_exit(void) | ||
257 | { | ||
258 | #ifdef CONFIG_CPU_SH4 | ||
259 | free_irq(DMAE_IRQ, 0); | ||
260 | #endif | ||
261 | } | ||
262 | |||
263 | subsys_initcall(sh_dmac_init); | ||
264 | module_exit(sh_dmac_exit); | ||
265 | |||
266 | MODULE_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 | |||
18 | static struct sysdev_class dma_sysclass = { | ||
19 | set_kset_name("dma"), | ||
20 | }; | ||
21 | |||
22 | EXPORT_SYMBOL(dma_sysclass); | ||
23 | |||
24 | static 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 | |||
41 | static SYSDEV_ATTR(devices, S_IRUGO, dma_show_devices, NULL); | ||
42 | |||
43 | static 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 | |||
54 | postcore_initcall(dma_sysclass_init); | ||
55 | |||
56 | static 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 | |||
62 | static 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 | |||
70 | static SYSDEV_ATTR(dev_id, S_IRUGO | S_IWUSR, dma_show_dev_id, dma_store_dev_id); | ||
71 | |||
72 | static 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 | |||
84 | static SYSDEV_ATTR(config, S_IWUSR, NULL, dma_store_config); | ||
85 | |||
86 | static 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 | |||
92 | static 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 | |||
100 | static SYSDEV_ATTR(mode, S_IRUGO | S_IWUSR, dma_show_mode, dma_store_mode); | ||
101 | |||
102 | #define dma_ro_attr(field, fmt) \ | ||
103 | static 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 | } \ | ||
108 | static SYSDEV_ATTR(field, S_IRUGO, dma_show_##field, NULL); | ||
109 | |||
110 | dma_ro_attr(count, "0x%08x\n"); | ||
111 | dma_ro_attr(flags, "0x%08lx\n"); | ||
112 | |||
113 | int __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 | |||