xref: /linux/kernel/dma.c (revision 3f3942ac)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2d9f3216bSAdrian Bunk /*
31da177e4SLinus Torvalds  * linux/kernel/dma.c: A DMA channel allocator. Inspired by linux/kernel/irq.c.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Written by Hennus Bergman, 1992.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * 1994/12/26: Changes by Alex Nash to fix a minor bug in /proc/dma.
81da177e4SLinus Torvalds  *   In the previous version the reported device could end up being wrong,
91da177e4SLinus Torvalds  *   if a device requested a DMA channel that was already in use.
101da177e4SLinus Torvalds  *   [It also happened to remove the sizeof(char *) == sizeof(int)
111da177e4SLinus Torvalds  *   assumption introduced because of those /proc/dma patches. -- Hennus]
121da177e4SLinus Torvalds  */
139984de1aSPaul Gortmaker #include <linux/export.h>
141da177e4SLinus Torvalds #include <linux/kernel.h>
151da177e4SLinus Torvalds #include <linux/errno.h>
161da177e4SLinus Torvalds #include <linux/spinlock.h>
171da177e4SLinus Torvalds #include <linux/string.h>
181da177e4SLinus Torvalds #include <linux/seq_file.h>
191da177e4SLinus Torvalds #include <linux/proc_fs.h>
201da177e4SLinus Torvalds #include <linux/init.h>
211da177e4SLinus Torvalds #include <asm/dma.h>
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds /* A note on resource allocation:
261da177e4SLinus Torvalds  *
271da177e4SLinus Torvalds  * All drivers needing DMA channels, should allocate and release them
281da177e4SLinus Torvalds  * through the public routines `request_dma()' and `free_dma()'.
291da177e4SLinus Torvalds  *
301da177e4SLinus Torvalds  * In order to avoid problems, all processes should allocate resources in
311da177e4SLinus Torvalds  * the same sequence and release them in the reverse order.
321da177e4SLinus Torvalds  *
331da177e4SLinus Torvalds  * So, when allocating DMAs and IRQs, first allocate the IRQ, then the DMA.
341da177e4SLinus Torvalds  * When releasing them, first release the DMA, then release the IRQ.
351da177e4SLinus Torvalds  * If you don't, you may cause allocation requests to fail unnecessarily.
361da177e4SLinus Torvalds  * This doesn't really matter now, but it will once we get real semaphores
371da177e4SLinus Torvalds  * in the kernel.
381da177e4SLinus Torvalds  */
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds DEFINE_SPINLOCK(dma_spin_lock);
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds /*
441da177e4SLinus Torvalds  *	If our port doesn't define this it has no PC like DMA
451da177e4SLinus Torvalds  */
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds #ifdef MAX_DMA_CHANNELS
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds /* Channel n is busy iff dma_chan_busy[n].lock != 0.
511da177e4SLinus Torvalds  * DMA0 used to be reserved for DRAM refresh, but apparently not any more...
521da177e4SLinus Torvalds  * DMA4 is reserved for cascading.
531da177e4SLinus Torvalds  */
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds struct dma_chan {
561da177e4SLinus Torvalds 	int  lock;
571da177e4SLinus Torvalds 	const char *device_id;
581da177e4SLinus Torvalds };
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds static struct dma_chan dma_chan_busy[MAX_DMA_CHANNELS] = {
611da177e4SLinus Torvalds 	[4] = { 1, "cascade" },
621da177e4SLinus Torvalds };
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds 
65eed34d0fSRandy Dunlap /**
66eed34d0fSRandy Dunlap  * request_dma - request and reserve a system DMA channel
67eed34d0fSRandy Dunlap  * @dmanr: DMA channel number
68eed34d0fSRandy Dunlap  * @device_id: reserving device ID string, used in /proc/dma
69eed34d0fSRandy Dunlap  */
request_dma(unsigned int dmanr,const char * device_id)701da177e4SLinus Torvalds int request_dma(unsigned int dmanr, const char * device_id)
711da177e4SLinus Torvalds {
721da177e4SLinus Torvalds 	if (dmanr >= MAX_DMA_CHANNELS)
731da177e4SLinus Torvalds 		return -EINVAL;
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 	if (xchg(&dma_chan_busy[dmanr].lock, 1) != 0)
761da177e4SLinus Torvalds 		return -EBUSY;
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds 	dma_chan_busy[dmanr].device_id = device_id;
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds 	/* old flag was 0, now contains 1 to indicate busy */
811da177e4SLinus Torvalds 	return 0;
821da177e4SLinus Torvalds } /* request_dma */
831da177e4SLinus Torvalds 
84eed34d0fSRandy Dunlap /**
85eed34d0fSRandy Dunlap  * free_dma - free a reserved system DMA channel
86eed34d0fSRandy Dunlap  * @dmanr: DMA channel number
87eed34d0fSRandy Dunlap  */
free_dma(unsigned int dmanr)881da177e4SLinus Torvalds void free_dma(unsigned int dmanr)
891da177e4SLinus Torvalds {
901da177e4SLinus Torvalds 	if (dmanr >= MAX_DMA_CHANNELS) {
911da177e4SLinus Torvalds 		printk(KERN_WARNING "Trying to free DMA%d\n", dmanr);
921da177e4SLinus Torvalds 		return;
931da177e4SLinus Torvalds 	}
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds 	if (xchg(&dma_chan_busy[dmanr].lock, 0) == 0) {
961da177e4SLinus Torvalds 		printk(KERN_WARNING "Trying to free free DMA%d\n", dmanr);
971da177e4SLinus Torvalds 		return;
981da177e4SLinus Torvalds 	}
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds } /* free_dma */
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds #else
1031da177e4SLinus Torvalds 
request_dma(unsigned int dmanr,const char * device_id)1041da177e4SLinus Torvalds int request_dma(unsigned int dmanr, const char *device_id)
1051da177e4SLinus Torvalds {
1061da177e4SLinus Torvalds 	return -EINVAL;
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds 
free_dma(unsigned int dmanr)1091da177e4SLinus Torvalds void free_dma(unsigned int dmanr)
1101da177e4SLinus Torvalds {
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds #endif
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds #ifdef MAX_DMA_CHANNELS
proc_dma_show(struct seq_file * m,void * v)1181da177e4SLinus Torvalds static int proc_dma_show(struct seq_file *m, void *v)
1191da177e4SLinus Torvalds {
1201da177e4SLinus Torvalds 	int i;
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds 	for (i = 0 ; i < MAX_DMA_CHANNELS ; i++) {
1231da177e4SLinus Torvalds 		if (dma_chan_busy[i].lock) {
1241da177e4SLinus Torvalds 			seq_printf(m, "%2d: %s\n", i,
1251da177e4SLinus Torvalds 				   dma_chan_busy[i].device_id);
1261da177e4SLinus Torvalds 		}
1271da177e4SLinus Torvalds 	}
1281da177e4SLinus Torvalds 	return 0;
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds #else
proc_dma_show(struct seq_file * m,void * v)1311da177e4SLinus Torvalds static int proc_dma_show(struct seq_file *m, void *v)
1321da177e4SLinus Torvalds {
1331da177e4SLinus Torvalds 	seq_puts(m, "No DMA\n");
1341da177e4SLinus Torvalds 	return 0;
1351da177e4SLinus Torvalds }
1361da177e4SLinus Torvalds #endif /* MAX_DMA_CHANNELS */
1371da177e4SLinus Torvalds 
proc_dma_init(void)1381da177e4SLinus Torvalds static int __init proc_dma_init(void)
1391da177e4SLinus Torvalds {
140*3f3942acSChristoph Hellwig 	proc_create_single("dma", 0, NULL, proc_dma_show);
1411da177e4SLinus Torvalds 	return 0;
1421da177e4SLinus Torvalds }
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds __initcall(proc_dma_init);
1451da177e4SLinus Torvalds #endif
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds EXPORT_SYMBOL(request_dma);
1481da177e4SLinus Torvalds EXPORT_SYMBOL(free_dma);
1491da177e4SLinus Torvalds EXPORT_SYMBOL(dma_spin_lock);
150