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