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 <asm/dma.h> 21 22 DEFINE_SPINLOCK(dma_spin_lock); 23 static LIST_HEAD(registered_dmac_list); 24 25 struct dma_info *get_dma_info(unsigned int chan) 26 { 27 struct dma_info *info; 28 29 /* 30 * Look for each DMAC's range to determine who the owner of 31 * the channel is. 32 */ 33 list_for_each_entry(info, ®istered_dmac_list, list) { 34 if ((chan < info->first_vchannel_nr) || 35 (chan >= info->first_vchannel_nr + info->nr_channels)) 36 continue; 37 38 return info; 39 } 40 41 return NULL; 42 } 43 EXPORT_SYMBOL(get_dma_info); 44 45 struct dma_info *get_dma_info_by_name(const char *dmac_name) 46 { 47 struct dma_info *info; 48 49 list_for_each_entry(info, ®istered_dmac_list, list) { 50 if (dmac_name && (strcmp(dmac_name, info->name) != 0)) 51 continue; 52 else 53 return info; 54 } 55 56 return NULL; 57 } 58 EXPORT_SYMBOL(get_dma_info_by_name); 59 60 static unsigned int get_nr_channels(void) 61 { 62 struct dma_info *info; 63 unsigned int nr = 0; 64 65 if (unlikely(list_empty(®istered_dmac_list))) 66 return nr; 67 68 list_for_each_entry(info, ®istered_dmac_list, list) 69 nr += info->nr_channels; 70 71 return nr; 72 } 73 74 struct dma_channel *get_dma_channel(unsigned int chan) 75 { 76 struct dma_info *info = get_dma_info(chan); 77 struct dma_channel *channel; 78 int i; 79 80 if (unlikely(!info)) 81 return ERR_PTR(-EINVAL); 82 83 for (i = 0; i < info->nr_channels; i++) { 84 channel = &info->channels[i]; 85 if (channel->vchan == chan) 86 return channel; 87 } 88 89 return NULL; 90 } 91 EXPORT_SYMBOL(get_dma_channel); 92 93 int get_dma_residue(unsigned int chan) 94 { 95 struct dma_info *info = get_dma_info(chan); 96 struct dma_channel *channel = get_dma_channel(chan); 97 98 if (info->ops->get_residue) 99 return info->ops->get_residue(channel); 100 101 return 0; 102 } 103 EXPORT_SYMBOL(get_dma_residue); 104 105 static int search_cap(const char **haystack, const char *needle) 106 { 107 const char **p; 108 109 for (p = haystack; *p; p++) 110 if (strcmp(*p, needle) == 0) 111 return 1; 112 113 return 0; 114 } 115 116 /** 117 * request_dma_bycap - Allocate a DMA channel based on its capabilities 118 * @dmac: List of DMA controllers to search 119 * @caps: List of capabilities 120 * 121 * Search all channels of all DMA controllers to find a channel which 122 * matches the requested capabilities. The result is the channel 123 * number if a match is found, or %-ENODEV if no match is found. 124 * 125 * Note that not all DMA controllers export capabilities, in which 126 * case they can never be allocated using this API, and so 127 * request_dma() must be used specifying the channel number. 128 */ 129 int request_dma_bycap(const char **dmac, const char **caps, const char *dev_id) 130 { 131 unsigned int found = 0; 132 struct dma_info *info; 133 const char **p; 134 int i; 135 136 BUG_ON(!dmac || !caps); 137 138 list_for_each_entry(info, ®istered_dmac_list, list) 139 if (strcmp(*dmac, info->name) == 0) { 140 found = 1; 141 break; 142 } 143 144 if (!found) 145 return -ENODEV; 146 147 for (i = 0; i < info->nr_channels; i++) { 148 struct dma_channel *channel = &info->channels[i]; 149 150 if (unlikely(!channel->caps)) 151 continue; 152 153 for (p = caps; *p; p++) { 154 if (!search_cap(channel->caps, *p)) 155 break; 156 if (request_dma(channel->chan, dev_id) == 0) 157 return channel->chan; 158 } 159 } 160 161 return -EINVAL; 162 } 163 EXPORT_SYMBOL(request_dma_bycap); 164 165 int dmac_search_free_channel(const char *dev_id) 166 { 167 struct dma_channel *channel = { 0 }; 168 struct dma_info *info = get_dma_info(0); 169 int i; 170 171 for (i = 0; i < info->nr_channels; i++) { 172 channel = &info->channels[i]; 173 if (unlikely(!channel)) 174 return -ENODEV; 175 176 if (atomic_read(&channel->busy) == 0) 177 break; 178 } 179 180 if (info->ops->request) { 181 int result = info->ops->request(channel); 182 if (result) 183 return result; 184 185 atomic_set(&channel->busy, 1); 186 return channel->chan; 187 } 188 189 return -ENOSYS; 190 } 191 192 int request_dma(unsigned int chan, const char *dev_id) 193 { 194 struct dma_channel *channel = { 0 }; 195 struct dma_info *info = get_dma_info(chan); 196 int result; 197 198 channel = get_dma_channel(chan); 199 if (atomic_xchg(&channel->busy, 1)) 200 return -EBUSY; 201 202 strlcpy(channel->dev_id, dev_id, sizeof(channel->dev_id)); 203 204 if (info->ops->request) { 205 result = info->ops->request(channel); 206 if (result) 207 atomic_set(&channel->busy, 0); 208 209 return result; 210 } 211 212 return 0; 213 } 214 EXPORT_SYMBOL(request_dma); 215 216 void free_dma(unsigned int chan) 217 { 218 struct dma_info *info = get_dma_info(chan); 219 struct dma_channel *channel = get_dma_channel(chan); 220 221 if (info->ops->free) 222 info->ops->free(channel); 223 224 atomic_set(&channel->busy, 0); 225 } 226 EXPORT_SYMBOL(free_dma); 227 228 void dma_wait_for_completion(unsigned int chan) 229 { 230 struct dma_info *info = get_dma_info(chan); 231 struct dma_channel *channel = get_dma_channel(chan); 232 233 if (channel->flags & DMA_TEI_CAPABLE) { 234 wait_event(channel->wait_queue, 235 (info->ops->get_residue(channel) == 0)); 236 return; 237 } 238 239 while (info->ops->get_residue(channel)) 240 cpu_relax(); 241 } 242 EXPORT_SYMBOL(dma_wait_for_completion); 243 244 int register_chan_caps(const char *dmac, struct dma_chan_caps *caps) 245 { 246 struct dma_info *info; 247 unsigned int found = 0; 248 int i; 249 250 list_for_each_entry(info, ®istered_dmac_list, list) 251 if (strcmp(dmac, info->name) == 0) { 252 found = 1; 253 break; 254 } 255 256 if (unlikely(!found)) 257 return -ENODEV; 258 259 for (i = 0; i < info->nr_channels; i++, caps++) { 260 struct dma_channel *channel; 261 262 if ((info->first_channel_nr + i) != caps->ch_num) 263 return -EINVAL; 264 265 channel = &info->channels[i]; 266 channel->caps = caps->caplist; 267 } 268 269 return 0; 270 } 271 EXPORT_SYMBOL(register_chan_caps); 272 273 void dma_configure_channel(unsigned int chan, unsigned long flags) 274 { 275 struct dma_info *info = get_dma_info(chan); 276 struct dma_channel *channel = get_dma_channel(chan); 277 278 if (info->ops->configure) 279 info->ops->configure(channel, flags); 280 } 281 EXPORT_SYMBOL(dma_configure_channel); 282 283 int dma_xfer(unsigned int chan, unsigned long from, 284 unsigned long to, size_t size, unsigned int mode) 285 { 286 struct dma_info *info = get_dma_info(chan); 287 struct dma_channel *channel = get_dma_channel(chan); 288 289 channel->sar = from; 290 channel->dar = to; 291 channel->count = size; 292 channel->mode = mode; 293 294 return info->ops->xfer(channel); 295 } 296 EXPORT_SYMBOL(dma_xfer); 297 298 int dma_extend(unsigned int chan, unsigned long op, void *param) 299 { 300 struct dma_info *info = get_dma_info(chan); 301 struct dma_channel *channel = get_dma_channel(chan); 302 303 if (info->ops->extend) 304 return info->ops->extend(channel, op, param); 305 306 return -ENOSYS; 307 } 308 EXPORT_SYMBOL(dma_extend); 309 310 static int dma_read_proc(char *buf, char **start, off_t off, 311 int len, int *eof, void *data) 312 { 313 struct dma_info *info; 314 char *p = buf; 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 p += sprintf(p, "%2d: %14s %s\n", i, 335 info->name, channel->dev_id); 336 } 337 } 338 339 return p - buf; 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 create_proc_read_entry("dma", 0, 0, dma_read_proc, 0); 415 return 0; 416 } 417 subsys_initcall(dma_api_init); 418 419 MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); 420 MODULE_DESCRIPTION("DMA API for SuperH"); 421 MODULE_LICENSE("GPL"); 422