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