File Index
Files and directories
Sign in
FILE

dma-api.c

File preview with syntax highlighting.

linux-6.19.14/arch/sh/drivers/dma/dma-api.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * arch/sh/drivers/dma/dma-api.c
  4 *
  5 * SuperH-specific DMA management API
  6 *
  7 * Copyright (C) 2003, 2004, 2005  Paul Mundt
  8 */
  9#include <linux/init.h>
 10#include <linux/module.h>
 11#include <linux/spinlock.h>
 12#include <linux/proc_fs.h>
 13#include <linux/seq_file.h>
 14#include <linux/list.h>
 15#include <linux/platform_device.h>
 16#include <linux/mm.h>
 17#include <linux/sched.h>
 18#include <linux/slab.h>
 19#include <asm/dma.h>
 20
 21DEFINE_SPINLOCK(dma_spin_lock);
 22static LIST_HEAD(registered_dmac_list);
 23
 24struct dma_info *get_dma_info(unsigned int chan)
 25{
 26	struct dma_info *info;
 27
 28	/*
 29	 * Look for each DMAC's range to determine who the owner of
 30	 * the channel is.
 31	 */
 32	list_for_each_entry(info, &registered_dmac_list, list) {
 33		if ((chan <  info->first_vchannel_nr) ||
 34		    (chan >= info->first_vchannel_nr + info->nr_channels))
 35			continue;
 36
 37		return info;
 38	}
 39
 40	return NULL;
 41}
 42EXPORT_SYMBOL(get_dma_info);
 43
 44static unsigned int get_nr_channels(void)
 45{
 46	struct dma_info *info;
 47	unsigned int nr = 0;
 48
 49	if (unlikely(list_empty(&registered_dmac_list)))
 50		return nr;
 51
 52	list_for_each_entry(info, &registered_dmac_list, list)
 53		nr += info->nr_channels;
 54
 55	return nr;
 56}
 57
 58struct dma_channel *get_dma_channel(unsigned int chan)
 59{
 60	struct dma_info *info = get_dma_info(chan);
 61	struct dma_channel *channel;
 62	int i;
 63
 64	if (unlikely(!info))
 65		return ERR_PTR(-EINVAL);
 66
 67	for (i = 0; i < info->nr_channels; i++) {
 68		channel = &info->channels[i];
 69		if (channel->vchan == chan)
 70			return channel;
 71	}
 72
 73	return NULL;
 74}
 75EXPORT_SYMBOL(get_dma_channel);
 76
 77int get_dma_residue(unsigned int chan)
 78{
 79	struct dma_info *info = get_dma_info(chan);
 80	struct dma_channel *channel = get_dma_channel(chan);
 81
 82	if (info->ops->get_residue)
 83		return info->ops->get_residue(channel);
 84
 85	return 0;
 86}
 87EXPORT_SYMBOL(get_dma_residue);
 88
 89int request_dma(unsigned int chan, const char *dev_id)
 90{
 91	struct dma_channel *channel = { 0 };
 92	struct dma_info *info = get_dma_info(chan);
 93	int result;
 94
 95	channel = get_dma_channel(chan);
 96	if (atomic_xchg(&channel->busy, 1))
 97		return -EBUSY;
 98
 99	strscpy(channel->dev_id, dev_id, sizeof(channel->dev_id));
100
101	if (info->ops->request) {
102		result = info->ops->request(channel);
103		if (result)
104			atomic_set(&channel->busy, 0);
105
106		return result;
107	}
108
109	return 0;
110}
111EXPORT_SYMBOL(request_dma);
112
113void free_dma(unsigned int chan)
114{
115	struct dma_info *info = get_dma_info(chan);
116	struct dma_channel *channel = get_dma_channel(chan);
117
118	if (info->ops->free)
119		info->ops->free(channel);
120
121	atomic_set(&channel->busy, 0);
122}
123EXPORT_SYMBOL(free_dma);
124
125void dma_wait_for_completion(unsigned int chan)
126{
127	struct dma_info *info = get_dma_info(chan);
128	struct dma_channel *channel = get_dma_channel(chan);
129
130	if (channel->flags & DMA_TEI_CAPABLE) {
131		wait_event(channel->wait_queue,
132			   (info->ops->get_residue(channel) == 0));
133		return;
134	}
135
136	while (info->ops->get_residue(channel))
137		cpu_relax();
138}
139EXPORT_SYMBOL(dma_wait_for_completion);
140
141void dma_configure_channel(unsigned int chan, unsigned long flags)
142{
143	struct dma_info *info = get_dma_info(chan);
144	struct dma_channel *channel = get_dma_channel(chan);
145
146	if (info->ops->configure)
147		info->ops->configure(channel, flags);
148}
149EXPORT_SYMBOL(dma_configure_channel);
150
151int dma_xfer(unsigned int chan, unsigned long from,
152	     unsigned long to, size_t size, unsigned int mode)
153{
154	struct dma_info *info = get_dma_info(chan);
155	struct dma_channel *channel = get_dma_channel(chan);
156
157	channel->sar	= from;
158	channel->dar	= to;
159	channel->count	= size;
160	channel->mode	= mode;
161
162	return info->ops->xfer(channel);
163}
164EXPORT_SYMBOL(dma_xfer);
165
166static int dma_proc_show(struct seq_file *m, void *v)
167{
168	struct dma_info *info = v;
169
170	if (list_empty(&registered_dmac_list))
171		return 0;
172
173	/*
174	 * Iterate over each registered DMAC
175	 */
176	list_for_each_entry(info, &registered_dmac_list, list) {
177		int i;
178
179		/*
180		 * Iterate over each channel
181		 */
182		for (i = 0; i < info->nr_channels; i++) {
183			struct dma_channel *channel = info->channels + i;
184
185			if (!(channel->flags & DMA_CONFIGURED))
186				continue;
187
188			seq_printf(m, "%2d: %14s    %s\n", i,
189				   info->name, channel->dev_id);
190		}
191	}
192
193	return 0;
194}
195
196int register_dmac(struct dma_info *info)
197{
198	unsigned int total_channels, i;
199
200	INIT_LIST_HEAD(&info->list);
201
202	printk(KERN_INFO "DMA: Registering %s handler (%d channel%s).\n",
203	       info->name, info->nr_channels, info->nr_channels > 1 ? "s" : "");
204
205	BUG_ON((info->flags & DMAC_CHANNELS_CONFIGURED) && !info->channels);
206
207	info->pdev = platform_device_register_simple(info->name, -1,
208						     NULL, 0);
209	if (IS_ERR(info->pdev))
210		return PTR_ERR(info->pdev);
211
212	/*
213	 * Don't touch pre-configured channels
214	 */
215	if (!(info->flags & DMAC_CHANNELS_CONFIGURED)) {
216		unsigned int size;
217
218		size = sizeof(struct dma_channel) * info->nr_channels;
219
220		info->channels = kzalloc(size, GFP_KERNEL);
221		if (!info->channels)
222			return -ENOMEM;
223	}
224
225	total_channels = get_nr_channels();
226	info->first_vchannel_nr = total_channels;
227	for (i = 0; i < info->nr_channels; i++) {
228		struct dma_channel *chan = &info->channels[i];
229
230		atomic_set(&chan->busy, 0);
231
232		chan->chan  = info->first_channel_nr + i;
233		chan->vchan = info->first_channel_nr + i + total_channels;
234
235		memcpy(chan->dev_id, "Unused", 7);
236
237		if (info->flags & DMAC_CHANNELS_TEI_CAPABLE)
238			chan->flags |= DMA_TEI_CAPABLE;
239
240		init_waitqueue_head(&chan->wait_queue);
241		dma_create_sysfs_files(chan, info);
242	}
243
244	list_add(&info->list, &registered_dmac_list);
245
246	return 0;
247}
248EXPORT_SYMBOL(register_dmac);
249
250void unregister_dmac(struct dma_info *info)
251{
252	unsigned int i;
253
254	for (i = 0; i < info->nr_channels; i++)
255		dma_remove_sysfs_files(info->channels + i, info);
256
257	if (!(info->flags & DMAC_CHANNELS_CONFIGURED))
258		kfree(info->channels);
259
260	list_del(&info->list);
261	platform_device_unregister(info->pdev);
262}
263EXPORT_SYMBOL(unregister_dmac);
264
265static int __init dma_api_init(void)
266{
267	printk(KERN_NOTICE "DMA: Registering DMA API.\n");
268	return proc_create_single("dma", 0, NULL, dma_proc_show) ? 0 : -ENOMEM;
269}
270subsys_initcall(dma_api_init);
271
272MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
273MODULE_DESCRIPTION("DMA API for SuperH");
274MODULE_LICENSE("GPL v2");
~/ Read-only file preview.