1 /* 2 * arch/sh/drivers/dma/dma-api.c 3 * 4 * SuperH-specific DMA management API 5 * 6 * Copyright (C) 2003, 2004, 2005 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/spinlock.h> 15 #include <linux/proc_fs.h> 16 #include <linux/seq_file.h> 17 #include <linux/list.h> 18 #include <linux/platform_device.h> 19 #include <linux/mm.h> 20 #include <linux/sched.h> 21 #include <linux/slab.h> 22 #include <asm/dma.h> 23 24 DEFINE_SPINLOCK(dma_spin_lock); 25 static LIST_HEAD(registered_dmac_list); 26 27 struct dma_info *get_dma_info(unsigned int chan) 28 { 29 struct dma_info *info; 30 31 /* 32 * Look for each DMAC's range to determine who the owner of 33 * the channel is. 34 */ 35 list_for_each_entry(info, ®istered_dmac_list, list) { 36 if ((chan < info->first_vchannel_nr) || 37 (chan >= info->first_vchannel_nr + info->nr_channels)) 38 continue; 39 40 return info; 41 } 42 43 return NULL; 44 } 45 EXPORT_SYMBOL(get_dma_info); 46 47 struct dma_info *get_dma_info_by_name(const char *dmac_name) 48 { 49 struct dma_info *info; 50 51 list_for_each_entry(info, ®istered_dmac_list, list) { 52 if (dmac_name && (strcmp(dmac_name, info->name) != 0)) 53 continue; 54 else 55 return info; 56 } 57 58 return NULL; 59 } 60 EXPORT_SYMBOL(get_dma_info_by_name); 61 62 static unsigned int get_nr_channels(void) 63 { 64 struct dma_info *info; 65 unsigned int nr = 0; 66 67 if (unlikely(list_empty(®istered_dmac_list))) 68 return nr; 69 70 list_for_each_entry(info, ®istered_dmac_list, list) 71 nr += info->nr_channels; 72 73 return nr; 74 } 75 76 struct dma_channel *get_dma_channel(unsigned int chan) 77 { 78 struct dma_info *info = get_dma_info(chan); 79 struct dma_channel *channel; 80 int i; 81 82 if (unlikely(!info)) 83 return ERR_PTR(-EINVAL); 84 85 for (i = 0; i < info->nr_channels; i++) { 86 channel = &info->channels[i]; 87 if (channel->vchan == chan) 88 return channel; 89 } 90 91 return NULL; 92 } 93 EXPORT_SYMBOL(get_dma_channel); 94 95 int get_dma_residue(unsigned int chan) 96 { 97 struct dma_info *info = get_dma_info(chan); 98 struct dma_channel *channel = get_dma_channel(chan); 99 100 if (info->ops->get_residue) 101 return info->ops->get_residue(channel); 102 103 return 0; 104 } 105 EXPORT_SYMBOL(get_dma_residue); 106 107 static int search_cap(const char **haystack, const char *needle) 108 { 109 const char **p; 110 111 for (p = haystack; *p; p++) 112 if (strcmp(*p, needle) == 0) 113 return 1; 114 115 return 0; 116 } 117 118 /** 119 * request_dma_bycap - Allocate a DMA channel based on its capabilities 120 * @dmac: List of DMA controllers to search 121 * @caps: List of capabilities 122 * 123 * Search all channels of all DMA controllers to find a channel which 124 * matches the requested capabilities. The result is the channel 125 * number if a match is found, or %-ENODEV if no match is found. 126 * 127 * Note that not all DMA controllers export capabilities, in which 128 * case they can never be allocated using this API, and so 129 * request_dma() must be used specifying the channel number. 130 */ 131 int request_dma_bycap(const char **dmac, const char **caps, const char *dev_id) 132 { 133 unsigned int found = 0; 134 struct dma_info *info; 135 const char **p; 136 int i; 137 138 BUG_ON(!dmac || !caps); 139 140 list_for_each_entry(info, ®istered_dmac_list, list) 141 if (strcmp(*dmac, info->name) == 0) { 142 found = 1; 143 break; 144 } 145 146 if (!found) 147 return -ENODEV; 148 149 for (i = 0; i < info->nr_channels; i++) { 150 struct dma_channel *channel = &info->channels[i]; 151 152 if (unlikely(!channel->caps)) 153 continue; 154 155 for (p = caps; *p; p++) { 156 if (!search_cap(channel->caps, *p)) 157 break; 158 if (request_dma(channel->chan, dev_id) == 0) 159 return channel->chan; 160 } 161 } 162 163 return -EINVAL; 164 } 165 EXPORT_SYMBOL(request_dma_bycap); 166 167 int dmac_search_free_channel(const char *dev_id) 168 { 169 struct dma_channel *channel = { 0 }; 170 struct dma_info *info = get_dma_info(0); 171 int i; 172 173 for (i = 0; i < info->nr_channels; i++) { 174 channel = &info->channels[i]; 175 if (unlikely(!channel)) 176 return -ENODEV; 177 178 if (atomic_read(&channel->busy) == 0) 179 break; 180 } 181 182 if (info->ops->request) { 183 int result = info->ops->request(channel); 184 if (result) 185 return result; 186 187 atomic_set(&channel->busy, 1); 188 return channel->chan; 189 } 190 191 return -ENOSYS; 192 } 193 194 int request_dma(unsigned int chan, const char *dev_id) 195 { 196 struct dma_channel *channel = { 0 }; 197 struct dma_info *info = get_dma_info(chan); 198 int result; 199 200 channel = get_dma_channel(chan); 201 if (atomic_xchg(&channel->busy, 1)) 202 return -EBUSY; 203 204 strlcpy(channel->dev_id, dev_id, sizeof(channel->dev_id)); 205 206 if (info->ops->request) { 207 result = info->ops->request(channel); 208 if (result) 209 atomic_set(&channel->busy, 0); 210 211 return result; 212 } 213 214 return 0; 215 } 216 EXPORT_SYMBOL(request_dma); 217 218 void free_dma(unsigned int chan) 219 { 220 struct dma_info *info = get_dma_info(chan); 221 struct dma_channel *channel = get_dma_channel(chan); 222 223 if (info->ops->free) 224 info->ops->free(channel); 225 226 atomic_set(&channel->busy, 0); 227 } 228 EXPORT_SYMBOL(free_dma); 229 230 void dma_wait_for_completion(unsigned int chan) 231 { 232 struct dma_info *info = get_dma_info(chan); 233 struct dma_channel *channel = get_dma_channel(chan); 234 235 if (channel->flags & DMA_TEI_CAPABLE) { 236 wait_event(channel->wait_queue, 237 (info->ops->get_residue(channel) == 0)); 238 return; 239 } 240 241 while (info->ops->get_residue(channel)) 242 cpu_relax(); 243 } 244 EXPORT_SYMBOL(dma_wait_for_completion); 245 246 int register_chan_caps(const char *dmac, struct dma_chan_caps *caps) 247 { 248 struct dma_info *info; 249 unsigned int found = 0; 250 int i; 251 252 list_for_each_entry(info, ®istered_dmac_list, list) 253 if (strcmp(dmac, info->name) == 0) { 254 found = 1; 255 break; 256 } 257 258 if (unlikely(!found)) 259 return -ENODEV; 260 261 for (i = 0; i < info->nr_channels; i++, caps++) { 262 struct dma_channel *channel; 263 264 if ((info->first_channel_nr + i) != caps->ch_num) 265 return -EINVAL; 266 267 channel = &info->channels[i]; 268 channel->caps = caps->caplist; 269 } 270 271 return 0; 272 } 273 EXPORT_SYMBOL(register_chan_caps); 274 275 void dma_configure_channel(unsigned int chan, unsigned long flags) 276 { 277 struct dma_info *info = get_dma_info(chan); 278 struct dma_channel *channel = get_dma_channel(chan); 279 280 if (info->ops->configure) 281 info->ops->configure(channel, flags); 282 } 283 EXPORT_SYMBOL(dma_configure_channel); 284 285 int dma_xfer(unsigned int chan, unsigned long from, 286 unsigned long to, size_t size, unsigned int mode) 287 { 288 struct dma_info *info = get_dma_info(chan); 289 struct dma_channel *channel = get_dma_channel(chan); 290 291 channel->sar = from; 292 channel->dar = to; 293 channel->count = size; 294 channel->mode = mode; 295 296 return info->ops->xfer(channel); 297 } 298 EXPORT_SYMBOL(dma_xfer); 299 300 int dma_extend(unsigned int chan, unsigned long op, void *param) 301 { 302 struct dma_info *info = get_dma_info(chan); 303 struct dma_channel *channel = get_dma_channel(chan); 304 305 if (info->ops->extend) 306 return info->ops->extend(channel, op, param); 307 308 return -ENOSYS; 309 } 310 EXPORT_SYMBOL(dma_extend); 311 312 static int dma_proc_show(struct seq_file *m, void *v) 313 { 314 struct dma_info *info = v; 315 316 if (list_empty(®istered_dmac_list)) 317 return 0; 318 319 /* 320 * Iterate over each registered DMAC 321 */ 322 list_for_each_entry(info, ®istered_dmac_list, list) { 323 int i; 324 325 /* 326 * Iterate over each channel 327 */ 328 for (i = 0; i < info->nr_channels; i++) { 329 struct dma_channel *channel = info->channels + i; 330 331 if (!(channel->flags & DMA_CONFIGURED)) 332 continue; 333 334 seq_printf(m, "%2d: %14s %s\n", i, 335 info->name, channel->dev_id); 336 } 337 } 338 339 return 0; 340 } 341 342 int register_dmac(struct dma_info *info) 343 { 344 unsigned int total_channels, i; 345 346 INIT_LIST_HEAD(&info->list); 347 348 printk(KERN_INFO "DMA: Registering %s handler (%d channel%s).\n", 349 info->name, info->nr_channels, info->nr_channels > 1 ? "s" : ""); 350 351 BUG_ON((info->flags & DMAC_CHANNELS_CONFIGURED) && !info->channels); 352 353 info->pdev = platform_device_register_simple(info->name, -1, 354 NULL, 0); 355 if (IS_ERR(info->pdev)) 356 return PTR_ERR(info->pdev); 357 358 /* 359 * Don't touch pre-configured channels 360 */ 361 if (!(info->flags & DMAC_CHANNELS_CONFIGURED)) { 362 unsigned int size; 363 364 size = sizeof(struct dma_channel) * info->nr_channels; 365 366 info->channels = kzalloc(size, GFP_KERNEL); 367 if (!info->channels) 368 return -ENOMEM; 369 } 370 371 total_channels = get_nr_channels(); 372 info->first_vchannel_nr = total_channels; 373 for (i = 0; i < info->nr_channels; i++) { 374 struct dma_channel *chan = &info->channels[i]; 375 376 atomic_set(&chan->busy, 0); 377 378 chan->chan = info->first_channel_nr + i; 379 chan->vchan = info->first_channel_nr + i + total_channels; 380 381 memcpy(chan->dev_id, "Unused", 7); 382 383 if (info->flags & DMAC_CHANNELS_TEI_CAPABLE) 384 chan->flags |= DMA_TEI_CAPABLE; 385 386 init_waitqueue_head(&chan->wait_queue); 387 dma_create_sysfs_files(chan, info); 388 } 389 390 list_add(&info->list, ®istered_dmac_list); 391 392 return 0; 393 } 394 EXPORT_SYMBOL(register_dmac); 395 396 void unregister_dmac(struct dma_info *info) 397 { 398 unsigned int i; 399 400 for (i = 0; i < info->nr_channels; i++) 401 dma_remove_sysfs_files(info->channels + i, info); 402 403 if (!(info->flags & DMAC_CHANNELS_CONFIGURED)) 404 kfree(info->channels); 405 406 list_del(&info->list); 407 platform_device_unregister(info->pdev); 408 } 409 EXPORT_SYMBOL(unregister_dmac); 410 411 static int __init dma_api_init(void) 412 { 413 printk(KERN_NOTICE "DMA: Registering DMA API.\n"); 414 return proc_create_single("dma", 0, NULL, dma_proc_show) ? 0 : -ENOMEM; 415 } 416 subsys_initcall(dma_api_init); 417 418 MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); 419 MODULE_DESCRIPTION("DMA API for SuperH"); 420 MODULE_LICENSE("GPL"); 421