diff options
Diffstat (limited to 'arch/sh/drivers/dma')
-rw-r--r-- | arch/sh/drivers/dma/Makefile | 4 | ||||
-rw-r--r-- | arch/sh/drivers/dma/dma-api.c | 274 | ||||
-rw-r--r-- | arch/sh/drivers/dma/dma-sh.c | 51 | ||||
-rw-r--r-- | arch/sh/drivers/dma/dma-sysfs.c | 23 |
4 files changed, 233 insertions, 119 deletions
diff --git a/arch/sh/drivers/dma/Makefile b/arch/sh/drivers/dma/Makefile index 065d4c90970e..db1295d32268 100644 --- a/arch/sh/drivers/dma/Makefile +++ b/arch/sh/drivers/dma/Makefile | |||
@@ -2,8 +2,8 @@ | |||
2 | # Makefile for the SuperH DMA specific kernel interface routines under Linux. | 2 | # Makefile for the SuperH DMA specific kernel interface routines under Linux. |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-y += dma-api.o dma-isa.o | 5 | obj-y += dma-api.o |
6 | obj-$(CONFIG_ISA_DMA_API) += dma-isa.o | ||
6 | obj-$(CONFIG_SYSFS) += dma-sysfs.o | 7 | obj-$(CONFIG_SYSFS) += dma-sysfs.o |
7 | obj-$(CONFIG_SH_DMA) += dma-sh.o | 8 | obj-$(CONFIG_SH_DMA) += dma-sh.o |
8 | obj-$(CONFIG_SH_DREAMCAST) += dma-pvr2.o dma-g2.o | 9 | 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 index 47c3e837599b..e062067edd24 100644 --- a/arch/sh/drivers/dma/dma-api.c +++ b/arch/sh/drivers/dma/dma-api.c | |||
@@ -11,61 +11,27 @@ | |||
11 | */ | 11 | */ |
12 | #include <linux/init.h> | 12 | #include <linux/init.h> |
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/spinlock.h> | 14 | #include <linux/spinlock.h> |
16 | #include <linux/proc_fs.h> | 15 | #include <linux/proc_fs.h> |
17 | #include <linux/list.h> | 16 | #include <linux/list.h> |
18 | #include <linux/platform_device.h> | 17 | #include <linux/platform_device.h> |
18 | #include <linux/mm.h> | ||
19 | #include <asm/dma.h> | 19 | #include <asm/dma.h> |
20 | 20 | ||
21 | DEFINE_SPINLOCK(dma_spin_lock); | 21 | DEFINE_SPINLOCK(dma_spin_lock); |
22 | static LIST_HEAD(registered_dmac_list); | 22 | static LIST_HEAD(registered_dmac_list); |
23 | 23 | ||
24 | /* | ||
25 | * A brief note about the reasons for this API as it stands. | ||
26 | * | ||
27 | * For starters, the old ISA DMA API didn't work for us for a number of | ||
28 | * reasons, for one, the vast majority of channels on the SH DMAC are | ||
29 | * dual-address mode only, and both the new and the old DMA APIs are after the | ||
30 | * concept of managing a DMA buffer, which doesn't overly fit this model very | ||
31 | * well. In addition to which, the new API is largely geared at IOMMUs and | ||
32 | * GARTs, and doesn't even support the channel notion very well. | ||
33 | * | ||
34 | * The other thing that's a marginal issue, is the sheer number of random DMA | ||
35 | * engines that are present (ie, in boards like the Dreamcast), some of which | ||
36 | * cascade off of the SH DMAC, and others do not. As such, there was a real | ||
37 | * need for a scalable subsystem that could deal with both single and | ||
38 | * dual-address mode usage, in addition to interoperating with cascaded DMACs. | ||
39 | * | ||
40 | * There really isn't any reason why this needs to be SH specific, though I'm | ||
41 | * not aware of too many other processors (with the exception of some MIPS) | ||
42 | * that have the same concept of a dual address mode, or any real desire to | ||
43 | * actually make use of the DMAC even if such a subsystem were exposed | ||
44 | * elsewhere. | ||
45 | * | ||
46 | * The idea for this was derived from the ARM port, which acted as an excellent | ||
47 | * reference when trying to address these issues. | ||
48 | * | ||
49 | * It should also be noted that the decision to add Yet Another DMA API(tm) to | ||
50 | * the kernel wasn't made easily, and was only decided upon after conferring | ||
51 | * with jejb with regards to the state of the old and new APIs as they applied | ||
52 | * to these circumstances. Philip Blundell was also a great help in figuring | ||
53 | * out some single-address mode DMA semantics that were otherwise rather | ||
54 | * confusing. | ||
55 | */ | ||
56 | |||
57 | struct dma_info *get_dma_info(unsigned int chan) | 24 | struct dma_info *get_dma_info(unsigned int chan) |
58 | { | 25 | { |
59 | struct dma_info *info; | 26 | struct dma_info *info; |
60 | unsigned int total = 0; | ||
61 | 27 | ||
62 | /* | 28 | /* |
63 | * Look for each DMAC's range to determine who the owner of | 29 | * Look for each DMAC's range to determine who the owner of |
64 | * the channel is. | 30 | * the channel is. |
65 | */ | 31 | */ |
66 | list_for_each_entry(info, ®istered_dmac_list, list) { | 32 | list_for_each_entry(info, ®istered_dmac_list, list) { |
67 | total += info->nr_channels; | 33 | if ((chan < info->first_channel_nr) || |
68 | if (chan > total) | 34 | (chan >= info->first_channel_nr + info->nr_channels)) |
69 | continue; | 35 | continue; |
70 | 36 | ||
71 | return info; | 37 | return info; |
@@ -73,6 +39,22 @@ struct dma_info *get_dma_info(unsigned int chan) | |||
73 | 39 | ||
74 | return NULL; | 40 | return NULL; |
75 | } | 41 | } |
42 | EXPORT_SYMBOL(get_dma_info); | ||
43 | |||
44 | struct dma_info *get_dma_info_by_name(const char *dmac_name) | ||
45 | { | ||
46 | struct dma_info *info; | ||
47 | |||
48 | list_for_each_entry(info, ®istered_dmac_list, list) { | ||
49 | if (dmac_name && (strcmp(dmac_name, info->name) != 0)) | ||
50 | continue; | ||
51 | else | ||
52 | return info; | ||
53 | } | ||
54 | |||
55 | return NULL; | ||
56 | } | ||
57 | EXPORT_SYMBOL(get_dma_info_by_name); | ||
76 | 58 | ||
77 | static unsigned int get_nr_channels(void) | 59 | static unsigned int get_nr_channels(void) |
78 | { | 60 | { |
@@ -91,63 +73,161 @@ static unsigned int get_nr_channels(void) | |||
91 | struct dma_channel *get_dma_channel(unsigned int chan) | 73 | struct dma_channel *get_dma_channel(unsigned int chan) |
92 | { | 74 | { |
93 | struct dma_info *info = get_dma_info(chan); | 75 | struct dma_info *info = get_dma_info(chan); |
76 | struct dma_channel *channel; | ||
77 | int i; | ||
94 | 78 | ||
95 | if (!info) | 79 | if (unlikely(!info)) |
96 | return ERR_PTR(-EINVAL); | 80 | return ERR_PTR(-EINVAL); |
97 | 81 | ||
98 | return info->channels + chan; | 82 | for (i = 0; i < info->nr_channels; i++) { |
83 | channel = &info->channels[i]; | ||
84 | if (channel->chan == chan) | ||
85 | return channel; | ||
86 | } | ||
87 | |||
88 | return NULL; | ||
99 | } | 89 | } |
90 | EXPORT_SYMBOL(get_dma_channel); | ||
100 | 91 | ||
101 | int get_dma_residue(unsigned int chan) | 92 | int get_dma_residue(unsigned int chan) |
102 | { | 93 | { |
103 | struct dma_info *info = get_dma_info(chan); | 94 | struct dma_info *info = get_dma_info(chan); |
104 | struct dma_channel *channel = &info->channels[chan]; | 95 | struct dma_channel *channel = get_dma_channel(chan); |
105 | 96 | ||
106 | if (info->ops->get_residue) | 97 | if (info->ops->get_residue) |
107 | return info->ops->get_residue(channel); | 98 | return info->ops->get_residue(channel); |
108 | 99 | ||
109 | return 0; | 100 | return 0; |
110 | } | 101 | } |
102 | EXPORT_SYMBOL(get_dma_residue); | ||
111 | 103 | ||
112 | int request_dma(unsigned int chan, const char *dev_id) | 104 | static int search_cap(const char **haystack, const char *needle) |
113 | { | 105 | { |
114 | struct dma_info *info = get_dma_info(chan); | 106 | const char **p; |
115 | struct dma_channel *channel = &info->channels[chan]; | 107 | |
108 | for (p = haystack; *p; p++) | ||
109 | if (strcmp(*p, needle) == 0) | ||
110 | return 1; | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * request_dma_bycap - Allocate a DMA channel based on its capabilities | ||
117 | * @dmac: List of DMA controllers to search | ||
118 | * @caps: List of capabilites | ||
119 | * | ||
120 | * Search all channels of all DMA controllers to find a channel which | ||
121 | * matches the requested capabilities. The result is the channel | ||
122 | * number if a match is found, or %-ENODEV if no match is found. | ||
123 | * | ||
124 | * Note that not all DMA controllers export capabilities, in which | ||
125 | * case they can never be allocated using this API, and so | ||
126 | * request_dma() must be used specifying the channel number. | ||
127 | */ | ||
128 | int request_dma_bycap(const char **dmac, const char **caps, const char *dev_id) | ||
129 | { | ||
130 | unsigned int found = 0; | ||
131 | struct dma_info *info; | ||
132 | const char **p; | ||
133 | int i; | ||
134 | |||
135 | BUG_ON(!dmac || !caps); | ||
136 | |||
137 | list_for_each_entry(info, ®istered_dmac_list, list) | ||
138 | if (strcmp(*dmac, info->name) == 0) { | ||
139 | found = 1; | ||
140 | break; | ||
141 | } | ||
142 | |||
143 | if (!found) | ||
144 | return -ENODEV; | ||
145 | |||
146 | for (i = 0; i < info->nr_channels; i++) { | ||
147 | struct dma_channel *channel = &info->channels[i]; | ||
148 | |||
149 | if (unlikely(!channel->caps)) | ||
150 | continue; | ||
151 | |||
152 | for (p = caps; *p; p++) { | ||
153 | if (!search_cap(channel->caps, *p)) | ||
154 | break; | ||
155 | if (request_dma(channel->chan, dev_id) == 0) | ||
156 | return channel->chan; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | return -EINVAL; | ||
161 | } | ||
162 | EXPORT_SYMBOL(request_dma_bycap); | ||
163 | |||
164 | int dmac_search_free_channel(const char *dev_id) | ||
165 | { | ||
166 | struct dma_channel *channel = { 0 }; | ||
167 | struct dma_info *info = get_dma_info(0); | ||
168 | int i; | ||
169 | |||
170 | for (i = 0; i < info->nr_channels; i++) { | ||
171 | channel = &info->channels[i]; | ||
172 | if (unlikely(!channel)) | ||
173 | return -ENODEV; | ||
174 | |||
175 | if (atomic_read(&channel->busy) == 0) | ||
176 | break; | ||
177 | } | ||
116 | 178 | ||
117 | down(&channel->sem); | 179 | if (info->ops->request) { |
180 | int result = info->ops->request(channel); | ||
181 | if (result) | ||
182 | return result; | ||
118 | 183 | ||
119 | if (!info->ops || chan >= MAX_DMA_CHANNELS) { | 184 | atomic_set(&channel->busy, 1); |
120 | up(&channel->sem); | 185 | return channel->chan; |
121 | return -EINVAL; | ||
122 | } | 186 | } |
123 | 187 | ||
124 | atomic_set(&channel->busy, 1); | 188 | return -ENOSYS; |
189 | } | ||
190 | |||
191 | int request_dma(unsigned int chan, const char *dev_id) | ||
192 | { | ||
193 | struct dma_channel *channel = { 0 }; | ||
194 | struct dma_info *info = get_dma_info(chan); | ||
195 | int result; | ||
196 | |||
197 | channel = get_dma_channel(chan); | ||
198 | if (atomic_xchg(&channel->busy, 1)) | ||
199 | return -EBUSY; | ||
125 | 200 | ||
126 | strlcpy(channel->dev_id, dev_id, sizeof(channel->dev_id)); | 201 | strlcpy(channel->dev_id, dev_id, sizeof(channel->dev_id)); |
127 | 202 | ||
128 | up(&channel->sem); | 203 | if (info->ops->request) { |
204 | result = info->ops->request(channel); | ||
205 | if (result) | ||
206 | atomic_set(&channel->busy, 0); | ||
129 | 207 | ||
130 | if (info->ops->request) | 208 | return result; |
131 | return info->ops->request(channel); | 209 | } |
132 | 210 | ||
133 | return 0; | 211 | return 0; |
134 | } | 212 | } |
213 | EXPORT_SYMBOL(request_dma); | ||
135 | 214 | ||
136 | void free_dma(unsigned int chan) | 215 | void free_dma(unsigned int chan) |
137 | { | 216 | { |
138 | struct dma_info *info = get_dma_info(chan); | 217 | struct dma_info *info = get_dma_info(chan); |
139 | struct dma_channel *channel = &info->channels[chan]; | 218 | struct dma_channel *channel = get_dma_channel(chan); |
140 | 219 | ||
141 | if (info->ops->free) | 220 | if (info->ops->free) |
142 | info->ops->free(channel); | 221 | info->ops->free(channel); |
143 | 222 | ||
144 | atomic_set(&channel->busy, 0); | 223 | atomic_set(&channel->busy, 0); |
145 | } | 224 | } |
225 | EXPORT_SYMBOL(free_dma); | ||
146 | 226 | ||
147 | void dma_wait_for_completion(unsigned int chan) | 227 | void dma_wait_for_completion(unsigned int chan) |
148 | { | 228 | { |
149 | struct dma_info *info = get_dma_info(chan); | 229 | struct dma_info *info = get_dma_info(chan); |
150 | struct dma_channel *channel = &info->channels[chan]; | 230 | struct dma_channel *channel = get_dma_channel(chan); |
151 | 231 | ||
152 | if (channel->flags & DMA_TEI_CAPABLE) { | 232 | if (channel->flags & DMA_TEI_CAPABLE) { |
153 | wait_event(channel->wait_queue, | 233 | wait_event(channel->wait_queue, |
@@ -158,21 +238,52 @@ void dma_wait_for_completion(unsigned int chan) | |||
158 | while (info->ops->get_residue(channel)) | 238 | while (info->ops->get_residue(channel)) |
159 | cpu_relax(); | 239 | cpu_relax(); |
160 | } | 240 | } |
241 | EXPORT_SYMBOL(dma_wait_for_completion); | ||
242 | |||
243 | int register_chan_caps(const char *dmac, struct dma_chan_caps *caps) | ||
244 | { | ||
245 | struct dma_info *info; | ||
246 | unsigned int found = 0; | ||
247 | int i; | ||
248 | |||
249 | list_for_each_entry(info, ®istered_dmac_list, list) | ||
250 | if (strcmp(dmac, info->name) == 0) { | ||
251 | found = 1; | ||
252 | break; | ||
253 | } | ||
254 | |||
255 | if (unlikely(!found)) | ||
256 | return -ENODEV; | ||
257 | |||
258 | for (i = 0; i < info->nr_channels; i++, caps++) { | ||
259 | struct dma_channel *channel; | ||
260 | |||
261 | if ((info->first_channel_nr + i) != caps->ch_num) | ||
262 | return -EINVAL; | ||
263 | |||
264 | channel = &info->channels[i]; | ||
265 | channel->caps = caps->caplist; | ||
266 | } | ||
267 | |||
268 | return 0; | ||
269 | } | ||
270 | EXPORT_SYMBOL(register_chan_caps); | ||
161 | 271 | ||
162 | void dma_configure_channel(unsigned int chan, unsigned long flags) | 272 | void dma_configure_channel(unsigned int chan, unsigned long flags) |
163 | { | 273 | { |
164 | struct dma_info *info = get_dma_info(chan); | 274 | struct dma_info *info = get_dma_info(chan); |
165 | struct dma_channel *channel = &info->channels[chan]; | 275 | struct dma_channel *channel = get_dma_channel(chan); |
166 | 276 | ||
167 | if (info->ops->configure) | 277 | if (info->ops->configure) |
168 | info->ops->configure(channel, flags); | 278 | info->ops->configure(channel, flags); |
169 | } | 279 | } |
280 | EXPORT_SYMBOL(dma_configure_channel); | ||
170 | 281 | ||
171 | int dma_xfer(unsigned int chan, unsigned long from, | 282 | int dma_xfer(unsigned int chan, unsigned long from, |
172 | unsigned long to, size_t size, unsigned int mode) | 283 | unsigned long to, size_t size, unsigned int mode) |
173 | { | 284 | { |
174 | struct dma_info *info = get_dma_info(chan); | 285 | struct dma_info *info = get_dma_info(chan); |
175 | struct dma_channel *channel = &info->channels[chan]; | 286 | struct dma_channel *channel = get_dma_channel(chan); |
176 | 287 | ||
177 | channel->sar = from; | 288 | channel->sar = from; |
178 | channel->dar = to; | 289 | channel->dar = to; |
@@ -181,8 +292,20 @@ int dma_xfer(unsigned int chan, unsigned long from, | |||
181 | 292 | ||
182 | return info->ops->xfer(channel); | 293 | return info->ops->xfer(channel); |
183 | } | 294 | } |
295 | EXPORT_SYMBOL(dma_xfer); | ||
296 | |||
297 | int dma_extend(unsigned int chan, unsigned long op, void *param) | ||
298 | { | ||
299 | struct dma_info *info = get_dma_info(chan); | ||
300 | struct dma_channel *channel = get_dma_channel(chan); | ||
301 | |||
302 | if (info->ops->extend) | ||
303 | return info->ops->extend(channel, op, param); | ||
304 | |||
305 | return -ENOSYS; | ||
306 | } | ||
307 | EXPORT_SYMBOL(dma_extend); | ||
184 | 308 | ||
185 | #ifdef CONFIG_PROC_FS | ||
186 | static int dma_read_proc(char *buf, char **start, off_t off, | 309 | static int dma_read_proc(char *buf, char **start, off_t off, |
187 | int len, int *eof, void *data) | 310 | int len, int *eof, void *data) |
188 | { | 311 | { |
@@ -214,8 +337,6 @@ static int dma_read_proc(char *buf, char **start, off_t off, | |||
214 | 337 | ||
215 | return p - buf; | 338 | return p - buf; |
216 | } | 339 | } |
217 | #endif | ||
218 | |||
219 | 340 | ||
220 | int register_dmac(struct dma_info *info) | 341 | int register_dmac(struct dma_info *info) |
221 | { | 342 | { |
@@ -224,8 +345,7 @@ int register_dmac(struct dma_info *info) | |||
224 | INIT_LIST_HEAD(&info->list); | 345 | INIT_LIST_HEAD(&info->list); |
225 | 346 | ||
226 | printk(KERN_INFO "DMA: Registering %s handler (%d channel%s).\n", | 347 | printk(KERN_INFO "DMA: Registering %s handler (%d channel%s).\n", |
227 | info->name, info->nr_channels, | 348 | info->name, info->nr_channels, info->nr_channels > 1 ? "s" : ""); |
228 | info->nr_channels > 1 ? "s" : ""); | ||
229 | 349 | ||
230 | BUG_ON((info->flags & DMAC_CHANNELS_CONFIGURED) && !info->channels); | 350 | BUG_ON((info->flags & DMAC_CHANNELS_CONFIGURED) && !info->channels); |
231 | 351 | ||
@@ -242,28 +362,26 @@ int register_dmac(struct dma_info *info) | |||
242 | 362 | ||
243 | size = sizeof(struct dma_channel) * info->nr_channels; | 363 | size = sizeof(struct dma_channel) * info->nr_channels; |
244 | 364 | ||
245 | info->channels = kmalloc(size, GFP_KERNEL); | 365 | info->channels = kzalloc(size, GFP_KERNEL); |
246 | if (!info->channels) | 366 | if (!info->channels) |
247 | return -ENOMEM; | 367 | return -ENOMEM; |
248 | |||
249 | memset(info->channels, 0, size); | ||
250 | } | 368 | } |
251 | 369 | ||
252 | total_channels = get_nr_channels(); | 370 | total_channels = get_nr_channels(); |
253 | for (i = 0; i < info->nr_channels; i++) { | 371 | for (i = 0; i < info->nr_channels; i++) { |
254 | struct dma_channel *chan = info->channels + i; | 372 | struct dma_channel *chan = &info->channels[i]; |
373 | |||
374 | atomic_set(&chan->busy, 0); | ||
255 | 375 | ||
256 | chan->chan = i; | 376 | chan->chan = info->first_channel_nr + i; |
257 | chan->vchan = i + total_channels; | 377 | chan->vchan = info->first_channel_nr + i + total_channels; |
258 | 378 | ||
259 | memcpy(chan->dev_id, "Unused", 7); | 379 | memcpy(chan->dev_id, "Unused", 7); |
260 | 380 | ||
261 | if (info->flags & DMAC_CHANNELS_TEI_CAPABLE) | 381 | if (info->flags & DMAC_CHANNELS_TEI_CAPABLE) |
262 | chan->flags |= DMA_TEI_CAPABLE; | 382 | chan->flags |= DMA_TEI_CAPABLE; |
263 | 383 | ||
264 | init_MUTEX(&chan->sem); | ||
265 | init_waitqueue_head(&chan->wait_queue); | 384 | init_waitqueue_head(&chan->wait_queue); |
266 | |||
267 | dma_create_sysfs_files(chan, info); | 385 | dma_create_sysfs_files(chan, info); |
268 | } | 386 | } |
269 | 387 | ||
@@ -271,6 +389,7 @@ int register_dmac(struct dma_info *info) | |||
271 | 389 | ||
272 | return 0; | 390 | return 0; |
273 | } | 391 | } |
392 | EXPORT_SYMBOL(register_dmac); | ||
274 | 393 | ||
275 | void unregister_dmac(struct dma_info *info) | 394 | void unregister_dmac(struct dma_info *info) |
276 | { | 395 | { |
@@ -285,31 +404,16 @@ void unregister_dmac(struct dma_info *info) | |||
285 | list_del(&info->list); | 404 | list_del(&info->list); |
286 | platform_device_unregister(info->pdev); | 405 | platform_device_unregister(info->pdev); |
287 | } | 406 | } |
407 | EXPORT_SYMBOL(unregister_dmac); | ||
288 | 408 | ||
289 | static int __init dma_api_init(void) | 409 | static int __init dma_api_init(void) |
290 | { | 410 | { |
291 | printk("DMA: Registering DMA API.\n"); | 411 | printk(KERN_NOTICE "DMA: Registering DMA API.\n"); |
292 | |||
293 | #ifdef CONFIG_PROC_FS | ||
294 | create_proc_read_entry("dma", 0, 0, dma_read_proc, 0); | 412 | create_proc_read_entry("dma", 0, 0, dma_read_proc, 0); |
295 | #endif | ||
296 | |||
297 | return 0; | 413 | return 0; |
298 | } | 414 | } |
299 | |||
300 | subsys_initcall(dma_api_init); | 415 | subsys_initcall(dma_api_init); |
301 | 416 | ||
302 | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); | 417 | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); |
303 | MODULE_DESCRIPTION("DMA API for SuperH"); | 418 | MODULE_DESCRIPTION("DMA API for SuperH"); |
304 | MODULE_LICENSE("GPL"); | 419 | MODULE_LICENSE("GPL"); |
305 | |||
306 | EXPORT_SYMBOL(request_dma); | ||
307 | EXPORT_SYMBOL(free_dma); | ||
308 | EXPORT_SYMBOL(register_dmac); | ||
309 | EXPORT_SYMBOL(get_dma_residue); | ||
310 | EXPORT_SYMBOL(get_dma_info); | ||
311 | EXPORT_SYMBOL(get_dma_channel); | ||
312 | EXPORT_SYMBOL(dma_xfer); | ||
313 | EXPORT_SYMBOL(dma_wait_for_completion); | ||
314 | EXPORT_SYMBOL(dma_configure_channel); | ||
315 | |||
diff --git a/arch/sh/drivers/dma/dma-sh.c b/arch/sh/drivers/dma/dma-sh.c index d8ece20bb2cf..f63721ed86c2 100644 --- a/arch/sh/drivers/dma/dma-sh.c +++ b/arch/sh/drivers/dma/dma-sh.c | |||
@@ -19,23 +19,34 @@ | |||
19 | #include <asm/io.h> | 19 | #include <asm/io.h> |
20 | #include "dma-sh.h" | 20 | #include "dma-sh.h" |
21 | 21 | ||
22 | static inline unsigned int get_dmte_irq(unsigned int chan) | ||
23 | { | ||
24 | unsigned int irq = 0; | ||
25 | 22 | ||
23 | |||
24 | #ifdef CONFIG_CPU_SH4 | ||
25 | static struct ipr_data dmae_ipr_map[] = { | ||
26 | { DMAE_IRQ, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY }, | ||
27 | }; | ||
28 | #endif | ||
29 | static struct ipr_data dmte_ipr_map[] = { | ||
26 | /* | 30 | /* |
27 | * Normally we could just do DMTE0_IRQ + chan outright, though in the | 31 | * Normally we could just do DMTE0_IRQ + chan outright, though in the |
28 | * case of the 7751R, the DMTE IRQs for channels > 4 start right above | 32 | * case of the 7751R, the DMTE IRQs for channels > 4 start right above |
29 | * the SCIF | 33 | * the SCIF |
30 | */ | 34 | */ |
31 | if (chan < 4) { | 35 | { DMTE0_IRQ + 0, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY }, |
32 | irq = DMTE0_IRQ + chan; | 36 | { DMTE0_IRQ + 1, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY }, |
33 | } else { | 37 | { DMTE0_IRQ + 2, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY }, |
34 | #ifdef DMTE4_IRQ | 38 | { DMTE0_IRQ + 3, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY }, |
35 | irq = DMTE4_IRQ + chan - 4; | 39 | { DMTE4_IRQ + 0, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY }, |
36 | #endif | 40 | { DMTE4_IRQ + 1, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY }, |
37 | } | 41 | { DMTE4_IRQ + 2, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY }, |
42 | { DMTE4_IRQ + 3, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY }, | ||
43 | }; | ||
38 | 44 | ||
45 | static inline unsigned int get_dmte_irq(unsigned int chan) | ||
46 | { | ||
47 | unsigned int irq = 0; | ||
48 | if (chan < ARRAY_SIZE(dmte_ipr_map)) | ||
49 | irq = dmte_ipr_map[chan].irq; | ||
39 | return irq; | 50 | return irq; |
40 | } | 51 | } |
41 | 52 | ||
@@ -83,20 +94,13 @@ static int sh_dmac_request_dma(struct dma_channel *chan) | |||
83 | if (unlikely(!chan->flags & DMA_TEI_CAPABLE)) | 94 | if (unlikely(!chan->flags & DMA_TEI_CAPABLE)) |
84 | return 0; | 95 | return 0; |
85 | 96 | ||
86 | chan->name = kzalloc(32, GFP_KERNEL); | ||
87 | if (unlikely(chan->name == NULL)) | ||
88 | return -ENOMEM; | ||
89 | snprintf(chan->name, 32, "DMAC Transfer End (Channel %d)", | ||
90 | chan->chan); | ||
91 | |||
92 | return request_irq(get_dmte_irq(chan->chan), dma_tei, | 97 | return request_irq(get_dmte_irq(chan->chan), dma_tei, |
93 | IRQF_DISABLED, chan->name, chan); | 98 | IRQF_DISABLED, chan->dev_id, chan); |
94 | } | 99 | } |
95 | 100 | ||
96 | static void sh_dmac_free_dma(struct dma_channel *chan) | 101 | static void sh_dmac_free_dma(struct dma_channel *chan) |
97 | { | 102 | { |
98 | free_irq(get_dmte_irq(chan->chan), chan); | 103 | free_irq(get_dmte_irq(chan->chan), chan); |
99 | kfree(chan->name); | ||
100 | } | 104 | } |
101 | 105 | ||
102 | static void | 106 | static void |
@@ -258,17 +262,16 @@ static int __init sh_dmac_init(void) | |||
258 | int i; | 262 | int i; |
259 | 263 | ||
260 | #ifdef CONFIG_CPU_SH4 | 264 | #ifdef CONFIG_CPU_SH4 |
261 | make_ipr_irq(DMAE_IRQ, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY); | 265 | make_ipr_irq(dmae_ipr_map, ARRAY_SIZE(dmae_ipr_map)); |
262 | i = request_irq(DMAE_IRQ, dma_err, IRQF_DISABLED, "DMAC Address Error", 0); | 266 | i = request_irq(DMAE_IRQ, dma_err, IRQF_DISABLED, "DMAC Address Error", 0); |
263 | if (unlikely(i < 0)) | 267 | if (unlikely(i < 0)) |
264 | return i; | 268 | return i; |
265 | #endif | 269 | #endif |
266 | 270 | ||
267 | for (i = 0; i < info->nr_channels; i++) { | 271 | i = info->nr_channels; |
268 | int irq = get_dmte_irq(i); | 272 | if (i > ARRAY_SIZE(dmte_ipr_map)) |
269 | 273 | i = ARRAY_SIZE(dmte_ipr_map); | |
270 | make_ipr_irq(irq, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY); | 274 | make_ipr_irq(dmte_ipr_map, i); |
271 | } | ||
272 | 275 | ||
273 | /* | 276 | /* |
274 | * Initialize DMAOR, and clean up any error flags that may have | 277 | * Initialize DMAOR, and clean up any error flags that may have |
diff --git a/arch/sh/drivers/dma/dma-sysfs.c b/arch/sh/drivers/dma/dma-sysfs.c index 29b8ef9873d1..eebcd4768bbf 100644 --- a/arch/sh/drivers/dma/dma-sysfs.c +++ b/arch/sh/drivers/dma/dma-sysfs.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * sysfs interface for SH DMA API | 4 | * sysfs interface for SH DMA API |
5 | * | 5 | * |
6 | * Copyright (C) 2004, 2005 Paul Mundt | 6 | * Copyright (C) 2004 - 2006 Paul Mundt |
7 | * | 7 | * |
8 | * This file is subject to the terms and conditions of the GNU General Public | 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 | 9 | * License. See the file "COPYING" in the main directory of this archive |
@@ -21,7 +21,6 @@ | |||
21 | static struct sysdev_class dma_sysclass = { | 21 | static struct sysdev_class dma_sysclass = { |
22 | set_kset_name("dma"), | 22 | set_kset_name("dma"), |
23 | }; | 23 | }; |
24 | |||
25 | EXPORT_SYMBOL(dma_sysclass); | 24 | EXPORT_SYMBOL(dma_sysclass); |
26 | 25 | ||
27 | static ssize_t dma_show_devices(struct sys_device *dev, char *buf) | 26 | static ssize_t dma_show_devices(struct sys_device *dev, char *buf) |
@@ -31,7 +30,10 @@ static ssize_t dma_show_devices(struct sys_device *dev, char *buf) | |||
31 | 30 | ||
32 | for (i = 0; i < MAX_DMA_CHANNELS; i++) { | 31 | for (i = 0; i < MAX_DMA_CHANNELS; i++) { |
33 | struct dma_info *info = get_dma_info(i); | 32 | struct dma_info *info = get_dma_info(i); |
34 | struct dma_channel *channel = &info->channels[i]; | 33 | struct dma_channel *channel = get_dma_channel(i); |
34 | |||
35 | if (unlikely(!info) || !channel) | ||
36 | continue; | ||
35 | 37 | ||
36 | len += sprintf(buf + len, "%2d: %14s %s\n", | 38 | len += sprintf(buf + len, "%2d: %14s %s\n", |
37 | channel->chan, info->name, | 39 | channel->chan, info->name, |
@@ -125,11 +127,16 @@ int dma_create_sysfs_files(struct dma_channel *chan, struct dma_info *info) | |||
125 | if (ret) | 127 | if (ret) |
126 | return ret; | 128 | return ret; |
127 | 129 | ||
128 | sysdev_create_file(dev, &attr_dev_id); | 130 | ret |= sysdev_create_file(dev, &attr_dev_id); |
129 | sysdev_create_file(dev, &attr_count); | 131 | ret |= sysdev_create_file(dev, &attr_count); |
130 | sysdev_create_file(dev, &attr_mode); | 132 | ret |= sysdev_create_file(dev, &attr_mode); |
131 | sysdev_create_file(dev, &attr_flags); | 133 | ret |= sysdev_create_file(dev, &attr_flags); |
132 | sysdev_create_file(dev, &attr_config); | 134 | ret |= sysdev_create_file(dev, &attr_config); |
135 | |||
136 | if (unlikely(ret)) { | ||
137 | dev_err(&info->pdev->dev, "Failed creating attrs\n"); | ||
138 | return ret; | ||
139 | } | ||
133 | 140 | ||
134 | snprintf(name, sizeof(name), "dma%d", chan->chan); | 141 | snprintf(name, sizeof(name), "dma%d", chan->chan); |
135 | return sysfs_create_link(&info->pdev->dev.kobj, &dev->kobj, name); | 142 | return sysfs_create_link(&info->pdev->dev.kobj, &dev->kobj, name); |