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