• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  // SPDX-License-Identifier: GPL-2.0-only
2  /*
3   *  linux/arch/arm/kernel/dma.c
4   *
5   *  Copyright (C) 1995-2000 Russell King
6   *
7   *  Front-end to the DMA handling.  This handles the allocation/freeing
8   *  of DMA channels, and provides a unified interface to the machines
9   *  DMA facilities.
10   */
11  #include <linux/module.h>
12  #include <linux/init.h>
13  #include <linux/spinlock.h>
14  #include <linux/errno.h>
15  #include <linux/scatterlist.h>
16  #include <linux/seq_file.h>
17  #include <linux/proc_fs.h>
18  
19  #include <asm/dma.h>
20  
21  #include <asm/mach/dma.h>
22  
23  DEFINE_RAW_SPINLOCK(dma_spin_lock);
24  EXPORT_SYMBOL(dma_spin_lock);
25  
26  static dma_t *dma_chan[MAX_DMA_CHANNELS];
27  
dma_channel(unsigned int chan)28  static inline dma_t *dma_channel(unsigned int chan)
29  {
30  	if (chan >= MAX_DMA_CHANNELS)
31  		return NULL;
32  
33  	return dma_chan[chan];
34  }
35  
isa_dma_add(unsigned int chan,dma_t * dma)36  int __init isa_dma_add(unsigned int chan, dma_t *dma)
37  {
38  	if (!dma->d_ops)
39  		return -EINVAL;
40  
41  	sg_init_table(&dma->buf, 1);
42  
43  	if (dma_chan[chan])
44  		return -EBUSY;
45  	dma_chan[chan] = dma;
46  	return 0;
47  }
48  
49  /*
50   * Request DMA channel
51   *
52   * On certain platforms, we have to allocate an interrupt as well...
53   */
request_dma(unsigned int chan,const char * device_id)54  int request_dma(unsigned int chan, const char *device_id)
55  {
56  	dma_t *dma = dma_channel(chan);
57  	int ret;
58  
59  	if (!dma)
60  		goto bad_dma;
61  
62  	if (xchg(&dma->lock, 1) != 0)
63  		goto busy;
64  
65  	dma->device_id = device_id;
66  	dma->active    = 0;
67  	dma->invalid   = 1;
68  
69  	ret = 0;
70  	if (dma->d_ops->request)
71  		ret = dma->d_ops->request(chan, dma);
72  
73  	if (ret)
74  		xchg(&dma->lock, 0);
75  
76  	return ret;
77  
78  bad_dma:
79  	pr_err("dma: trying to allocate DMA%d\n", chan);
80  	return -EINVAL;
81  
82  busy:
83  	return -EBUSY;
84  }
85  EXPORT_SYMBOL(request_dma);
86  
87  /*
88   * Free DMA channel
89   *
90   * On certain platforms, we have to free interrupt as well...
91   */
free_dma(unsigned int chan)92  void free_dma(unsigned int chan)
93  {
94  	dma_t *dma = dma_channel(chan);
95  
96  	if (!dma)
97  		goto bad_dma;
98  
99  	if (dma->active) {
100  		pr_err("dma%d: freeing active DMA\n", chan);
101  		dma->d_ops->disable(chan, dma);
102  		dma->active = 0;
103  	}
104  
105  	if (xchg(&dma->lock, 0) != 0) {
106  		if (dma->d_ops->free)
107  			dma->d_ops->free(chan, dma);
108  		return;
109  	}
110  
111  	pr_err("dma%d: trying to free free DMA\n", chan);
112  	return;
113  
114  bad_dma:
115  	pr_err("dma: trying to free DMA%d\n", chan);
116  }
117  EXPORT_SYMBOL(free_dma);
118  
119  /* Set DMA Scatter-Gather list
120   */
set_dma_sg(unsigned int chan,struct scatterlist * sg,int nr_sg)121  void set_dma_sg (unsigned int chan, struct scatterlist *sg, int nr_sg)
122  {
123  	dma_t *dma = dma_channel(chan);
124  
125  	if (dma->active)
126  		pr_err("dma%d: altering DMA SG while DMA active\n", chan);
127  
128  	dma->sg = sg;
129  	dma->sgcount = nr_sg;
130  	dma->invalid = 1;
131  }
132  EXPORT_SYMBOL(set_dma_sg);
133  
134  /* Set DMA address
135   *
136   * Copy address to the structure, and set the invalid bit
137   */
__set_dma_addr(unsigned int chan,void * addr)138  void __set_dma_addr (unsigned int chan, void *addr)
139  {
140  	dma_t *dma = dma_channel(chan);
141  
142  	if (dma->active)
143  		pr_err("dma%d: altering DMA address while DMA active\n", chan);
144  
145  	dma->sg = NULL;
146  	dma->addr = addr;
147  	dma->invalid = 1;
148  }
149  EXPORT_SYMBOL(__set_dma_addr);
150  
151  /* Set DMA byte count
152   *
153   * Copy address to the structure, and set the invalid bit
154   */
set_dma_count(unsigned int chan,unsigned long count)155  void set_dma_count (unsigned int chan, unsigned long count)
156  {
157  	dma_t *dma = dma_channel(chan);
158  
159  	if (dma->active)
160  		pr_err("dma%d: altering DMA count while DMA active\n", chan);
161  
162  	dma->sg = NULL;
163  	dma->count = count;
164  	dma->invalid = 1;
165  }
166  EXPORT_SYMBOL(set_dma_count);
167  
168  /* Set DMA direction mode
169   */
set_dma_mode(unsigned int chan,unsigned int mode)170  void set_dma_mode (unsigned int chan, unsigned int mode)
171  {
172  	dma_t *dma = dma_channel(chan);
173  
174  	if (dma->active)
175  		pr_err("dma%d: altering DMA mode while DMA active\n", chan);
176  
177  	dma->dma_mode = mode;
178  	dma->invalid = 1;
179  }
180  EXPORT_SYMBOL(set_dma_mode);
181  
182  /* Enable DMA channel
183   */
enable_dma(unsigned int chan)184  void enable_dma (unsigned int chan)
185  {
186  	dma_t *dma = dma_channel(chan);
187  
188  	if (!dma->lock)
189  		goto free_dma;
190  
191  	if (dma->active == 0) {
192  		dma->active = 1;
193  		dma->d_ops->enable(chan, dma);
194  	}
195  	return;
196  
197  free_dma:
198  	pr_err("dma%d: trying to enable free DMA\n", chan);
199  	BUG();
200  }
201  EXPORT_SYMBOL(enable_dma);
202  
203  /* Disable DMA channel
204   */
disable_dma(unsigned int chan)205  void disable_dma (unsigned int chan)
206  {
207  	dma_t *dma = dma_channel(chan);
208  
209  	if (!dma->lock)
210  		goto free_dma;
211  
212  	if (dma->active == 1) {
213  		dma->active = 0;
214  		dma->d_ops->disable(chan, dma);
215  	}
216  	return;
217  
218  free_dma:
219  	pr_err("dma%d: trying to disable free DMA\n", chan);
220  	BUG();
221  }
222  EXPORT_SYMBOL(disable_dma);
223  
224  /*
225   * Is the specified DMA channel active?
226   */
dma_channel_active(unsigned int chan)227  int dma_channel_active(unsigned int chan)
228  {
229  	dma_t *dma = dma_channel(chan);
230  	return dma->active;
231  }
232  EXPORT_SYMBOL(dma_channel_active);
233  
set_dma_page(unsigned int chan,char pagenr)234  void set_dma_page(unsigned int chan, char pagenr)
235  {
236  	pr_err("dma%d: trying to set_dma_page\n", chan);
237  }
238  EXPORT_SYMBOL(set_dma_page);
239  
set_dma_speed(unsigned int chan,int cycle_ns)240  void set_dma_speed(unsigned int chan, int cycle_ns)
241  {
242  	dma_t *dma = dma_channel(chan);
243  	int ret = 0;
244  
245  	if (dma->d_ops->setspeed)
246  		ret = dma->d_ops->setspeed(chan, dma, cycle_ns);
247  	dma->speed = ret;
248  }
249  EXPORT_SYMBOL(set_dma_speed);
250  
get_dma_residue(unsigned int chan)251  int get_dma_residue(unsigned int chan)
252  {
253  	dma_t *dma = dma_channel(chan);
254  	int ret = 0;
255  
256  	if (dma->d_ops->residue)
257  		ret = dma->d_ops->residue(chan, dma);
258  
259  	return ret;
260  }
261  EXPORT_SYMBOL(get_dma_residue);
262  
263  #ifdef CONFIG_PROC_FS
proc_dma_show(struct seq_file * m,void * v)264  static int proc_dma_show(struct seq_file *m, void *v)
265  {
266  	int i;
267  
268  	for (i = 0 ; i < MAX_DMA_CHANNELS ; i++) {
269  		dma_t *dma = dma_channel(i);
270  		if (dma && dma->lock)
271  			seq_printf(m, "%2d: %s\n", i, dma->device_id);
272  	}
273  	return 0;
274  }
275  
proc_dma_init(void)276  static int __init proc_dma_init(void)
277  {
278  	proc_create_single("dma", 0, NULL, proc_dma_show);
279  	return 0;
280  }
281  
282  __initcall(proc_dma_init);
283  #endif
284