18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * arch/sh/drivers/dma/dma-api.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * SuperH-specific DMA management API 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2003, 2004, 2005 Paul Mundt 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 128c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 138c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 148c2ecf20Sopenharmony_ci#include <linux/list.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/mm.h> 178c2ecf20Sopenharmony_ci#include <linux/sched.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <asm/dma.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciDEFINE_SPINLOCK(dma_spin_lock); 228c2ecf20Sopenharmony_cistatic LIST_HEAD(registered_dmac_list); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct dma_info *get_dma_info(unsigned int chan) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci struct dma_info *info; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci /* 298c2ecf20Sopenharmony_ci * Look for each DMAC's range to determine who the owner of 308c2ecf20Sopenharmony_ci * the channel is. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci list_for_each_entry(info, ®istered_dmac_list, list) { 338c2ecf20Sopenharmony_ci if ((chan < info->first_vchannel_nr) || 348c2ecf20Sopenharmony_ci (chan >= info->first_vchannel_nr + info->nr_channels)) 358c2ecf20Sopenharmony_ci continue; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return info; 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci return NULL; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_dma_info); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct dma_info *get_dma_info_by_name(const char *dmac_name) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct dma_info *info; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci list_for_each_entry(info, ®istered_dmac_list, list) { 498c2ecf20Sopenharmony_ci if (dmac_name && (strcmp(dmac_name, info->name) != 0)) 508c2ecf20Sopenharmony_ci continue; 518c2ecf20Sopenharmony_ci else 528c2ecf20Sopenharmony_ci return info; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return NULL; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_dma_info_by_name); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic unsigned int get_nr_channels(void) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct dma_info *info; 628c2ecf20Sopenharmony_ci unsigned int nr = 0; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (unlikely(list_empty(®istered_dmac_list))) 658c2ecf20Sopenharmony_ci return nr; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci list_for_each_entry(info, ®istered_dmac_list, list) 688c2ecf20Sopenharmony_ci nr += info->nr_channels; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return nr; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistruct dma_channel *get_dma_channel(unsigned int chan) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct dma_info *info = get_dma_info(chan); 768c2ecf20Sopenharmony_ci struct dma_channel *channel; 778c2ecf20Sopenharmony_ci int i; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (unlikely(!info)) 808c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci for (i = 0; i < info->nr_channels; i++) { 838c2ecf20Sopenharmony_ci channel = &info->channels[i]; 848c2ecf20Sopenharmony_ci if (channel->vchan == chan) 858c2ecf20Sopenharmony_ci return channel; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return NULL; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_dma_channel); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ciint get_dma_residue(unsigned int chan) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct dma_info *info = get_dma_info(chan); 958c2ecf20Sopenharmony_ci struct dma_channel *channel = get_dma_channel(chan); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (info->ops->get_residue) 988c2ecf20Sopenharmony_ci return info->ops->get_residue(channel); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_dma_residue); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int search_cap(const char **haystack, const char *needle) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci const char **p; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci for (p = haystack; *p; p++) 1098c2ecf20Sopenharmony_ci if (strcmp(*p, needle) == 0) 1108c2ecf20Sopenharmony_ci return 1; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/** 1168c2ecf20Sopenharmony_ci * request_dma_bycap - Allocate a DMA channel based on its capabilities 1178c2ecf20Sopenharmony_ci * @dmac: List of DMA controllers to search 1188c2ecf20Sopenharmony_ci * @caps: List of capabilities 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * Search all channels of all DMA controllers to find a channel which 1218c2ecf20Sopenharmony_ci * matches the requested capabilities. The result is the channel 1228c2ecf20Sopenharmony_ci * number if a match is found, or %-ENODEV if no match is found. 1238c2ecf20Sopenharmony_ci * 1248c2ecf20Sopenharmony_ci * Note that not all DMA controllers export capabilities, in which 1258c2ecf20Sopenharmony_ci * case they can never be allocated using this API, and so 1268c2ecf20Sopenharmony_ci * request_dma() must be used specifying the channel number. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ciint request_dma_bycap(const char **dmac, const char **caps, const char *dev_id) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci unsigned int found = 0; 1318c2ecf20Sopenharmony_ci struct dma_info *info; 1328c2ecf20Sopenharmony_ci const char **p; 1338c2ecf20Sopenharmony_ci int i; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci BUG_ON(!dmac || !caps); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci list_for_each_entry(info, ®istered_dmac_list, list) 1388c2ecf20Sopenharmony_ci if (strcmp(*dmac, info->name) == 0) { 1398c2ecf20Sopenharmony_ci found = 1; 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (!found) 1448c2ecf20Sopenharmony_ci return -ENODEV; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci for (i = 0; i < info->nr_channels; i++) { 1478c2ecf20Sopenharmony_ci struct dma_channel *channel = &info->channels[i]; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (unlikely(!channel->caps)) 1508c2ecf20Sopenharmony_ci continue; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci for (p = caps; *p; p++) { 1538c2ecf20Sopenharmony_ci if (!search_cap(channel->caps, *p)) 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci if (request_dma(channel->chan, dev_id) == 0) 1568c2ecf20Sopenharmony_ci return channel->chan; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return -EINVAL; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(request_dma_bycap); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ciint dmac_search_free_channel(const char *dev_id) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct dma_channel *channel = { 0 }; 1678c2ecf20Sopenharmony_ci struct dma_info *info = get_dma_info(0); 1688c2ecf20Sopenharmony_ci int i; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci for (i = 0; i < info->nr_channels; i++) { 1718c2ecf20Sopenharmony_ci channel = &info->channels[i]; 1728c2ecf20Sopenharmony_ci if (unlikely(!channel)) 1738c2ecf20Sopenharmony_ci return -ENODEV; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (atomic_read(&channel->busy) == 0) 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (info->ops->request) { 1808c2ecf20Sopenharmony_ci int result = info->ops->request(channel); 1818c2ecf20Sopenharmony_ci if (result) 1828c2ecf20Sopenharmony_ci return result; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci atomic_set(&channel->busy, 1); 1858c2ecf20Sopenharmony_ci return channel->chan; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return -ENOSYS; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ciint request_dma(unsigned int chan, const char *dev_id) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct dma_channel *channel = { 0 }; 1948c2ecf20Sopenharmony_ci struct dma_info *info = get_dma_info(chan); 1958c2ecf20Sopenharmony_ci int result; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci channel = get_dma_channel(chan); 1988c2ecf20Sopenharmony_ci if (atomic_xchg(&channel->busy, 1)) 1998c2ecf20Sopenharmony_ci return -EBUSY; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci strlcpy(channel->dev_id, dev_id, sizeof(channel->dev_id)); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (info->ops->request) { 2048c2ecf20Sopenharmony_ci result = info->ops->request(channel); 2058c2ecf20Sopenharmony_ci if (result) 2068c2ecf20Sopenharmony_ci atomic_set(&channel->busy, 0); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return result; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(request_dma); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_civoid free_dma(unsigned int chan) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct dma_info *info = get_dma_info(chan); 2188c2ecf20Sopenharmony_ci struct dma_channel *channel = get_dma_channel(chan); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (info->ops->free) 2218c2ecf20Sopenharmony_ci info->ops->free(channel); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci atomic_set(&channel->busy, 0); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(free_dma); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_civoid dma_wait_for_completion(unsigned int chan) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct dma_info *info = get_dma_info(chan); 2308c2ecf20Sopenharmony_ci struct dma_channel *channel = get_dma_channel(chan); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (channel->flags & DMA_TEI_CAPABLE) { 2338c2ecf20Sopenharmony_ci wait_event(channel->wait_queue, 2348c2ecf20Sopenharmony_ci (info->ops->get_residue(channel) == 0)); 2358c2ecf20Sopenharmony_ci return; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci while (info->ops->get_residue(channel)) 2398c2ecf20Sopenharmony_ci cpu_relax(); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dma_wait_for_completion); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ciint register_chan_caps(const char *dmac, struct dma_chan_caps *caps) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct dma_info *info; 2468c2ecf20Sopenharmony_ci unsigned int found = 0; 2478c2ecf20Sopenharmony_ci int i; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci list_for_each_entry(info, ®istered_dmac_list, list) 2508c2ecf20Sopenharmony_ci if (strcmp(dmac, info->name) == 0) { 2518c2ecf20Sopenharmony_ci found = 1; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (unlikely(!found)) 2568c2ecf20Sopenharmony_ci return -ENODEV; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci for (i = 0; i < info->nr_channels; i++, caps++) { 2598c2ecf20Sopenharmony_ci struct dma_channel *channel; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if ((info->first_channel_nr + i) != caps->ch_num) 2628c2ecf20Sopenharmony_ci return -EINVAL; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci channel = &info->channels[i]; 2658c2ecf20Sopenharmony_ci channel->caps = caps->caplist; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_chan_caps); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_civoid dma_configure_channel(unsigned int chan, unsigned long flags) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct dma_info *info = get_dma_info(chan); 2758c2ecf20Sopenharmony_ci struct dma_channel *channel = get_dma_channel(chan); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (info->ops->configure) 2788c2ecf20Sopenharmony_ci info->ops->configure(channel, flags); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dma_configure_channel); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ciint dma_xfer(unsigned int chan, unsigned long from, 2838c2ecf20Sopenharmony_ci unsigned long to, size_t size, unsigned int mode) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct dma_info *info = get_dma_info(chan); 2868c2ecf20Sopenharmony_ci struct dma_channel *channel = get_dma_channel(chan); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci channel->sar = from; 2898c2ecf20Sopenharmony_ci channel->dar = to; 2908c2ecf20Sopenharmony_ci channel->count = size; 2918c2ecf20Sopenharmony_ci channel->mode = mode; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return info->ops->xfer(channel); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dma_xfer); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ciint dma_extend(unsigned int chan, unsigned long op, void *param) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct dma_info *info = get_dma_info(chan); 3008c2ecf20Sopenharmony_ci struct dma_channel *channel = get_dma_channel(chan); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (info->ops->extend) 3038c2ecf20Sopenharmony_ci return info->ops->extend(channel, op, param); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return -ENOSYS; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dma_extend); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int dma_proc_show(struct seq_file *m, void *v) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct dma_info *info = v; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (list_empty(®istered_dmac_list)) 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* 3178c2ecf20Sopenharmony_ci * Iterate over each registered DMAC 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci list_for_each_entry(info, ®istered_dmac_list, list) { 3208c2ecf20Sopenharmony_ci int i; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* 3238c2ecf20Sopenharmony_ci * Iterate over each channel 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci for (i = 0; i < info->nr_channels; i++) { 3268c2ecf20Sopenharmony_ci struct dma_channel *channel = info->channels + i; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (!(channel->flags & DMA_CONFIGURED)) 3298c2ecf20Sopenharmony_ci continue; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci seq_printf(m, "%2d: %14s %s\n", i, 3328c2ecf20Sopenharmony_ci info->name, channel->dev_id); 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return 0; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ciint register_dmac(struct dma_info *info) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci unsigned int total_channels, i; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&info->list); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci printk(KERN_INFO "DMA: Registering %s handler (%d channel%s).\n", 3468c2ecf20Sopenharmony_ci info->name, info->nr_channels, info->nr_channels > 1 ? "s" : ""); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci BUG_ON((info->flags & DMAC_CHANNELS_CONFIGURED) && !info->channels); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci info->pdev = platform_device_register_simple(info->name, -1, 3518c2ecf20Sopenharmony_ci NULL, 0); 3528c2ecf20Sopenharmony_ci if (IS_ERR(info->pdev)) 3538c2ecf20Sopenharmony_ci return PTR_ERR(info->pdev); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* 3568c2ecf20Sopenharmony_ci * Don't touch pre-configured channels 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci if (!(info->flags & DMAC_CHANNELS_CONFIGURED)) { 3598c2ecf20Sopenharmony_ci unsigned int size; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci size = sizeof(struct dma_channel) * info->nr_channels; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci info->channels = kzalloc(size, GFP_KERNEL); 3648c2ecf20Sopenharmony_ci if (!info->channels) 3658c2ecf20Sopenharmony_ci return -ENOMEM; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci total_channels = get_nr_channels(); 3698c2ecf20Sopenharmony_ci info->first_vchannel_nr = total_channels; 3708c2ecf20Sopenharmony_ci for (i = 0; i < info->nr_channels; i++) { 3718c2ecf20Sopenharmony_ci struct dma_channel *chan = &info->channels[i]; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci atomic_set(&chan->busy, 0); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci chan->chan = info->first_channel_nr + i; 3768c2ecf20Sopenharmony_ci chan->vchan = info->first_channel_nr + i + total_channels; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci memcpy(chan->dev_id, "Unused", 7); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (info->flags & DMAC_CHANNELS_TEI_CAPABLE) 3818c2ecf20Sopenharmony_ci chan->flags |= DMA_TEI_CAPABLE; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci init_waitqueue_head(&chan->wait_queue); 3848c2ecf20Sopenharmony_ci dma_create_sysfs_files(chan, info); 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci list_add(&info->list, ®istered_dmac_list); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci return 0; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_dmac); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_civoid unregister_dmac(struct dma_info *info) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci unsigned int i; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci for (i = 0; i < info->nr_channels; i++) 3988c2ecf20Sopenharmony_ci dma_remove_sysfs_files(info->channels + i, info); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (!(info->flags & DMAC_CHANNELS_CONFIGURED)) 4018c2ecf20Sopenharmony_ci kfree(info->channels); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci list_del(&info->list); 4048c2ecf20Sopenharmony_ci platform_device_unregister(info->pdev); 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(unregister_dmac); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic int __init dma_api_init(void) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci printk(KERN_NOTICE "DMA: Registering DMA API.\n"); 4118c2ecf20Sopenharmony_ci return proc_create_single("dma", 0, NULL, dma_proc_show) ? 0 : -ENOMEM; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_cisubsys_initcall(dma_api_init); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); 4168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DMA API for SuperH"); 4178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 418