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 | |
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 | |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | |
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 | |
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 | |
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 |
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 | |
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 | |