xref: /linux/arch/arm/kernel/dma.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/arch/arm/kernel/dma.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 1995-2000 Russell King
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  Front-end to the DMA handling.  This handles the allocation/freeing
81da177e4SLinus Torvalds  *  of DMA channels, and provides a unified interface to the machines
91da177e4SLinus Torvalds  *  DMA facilities.
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/init.h>
131da177e4SLinus Torvalds #include <linux/spinlock.h>
141da177e4SLinus Torvalds #include <linux/errno.h>
15d667522fSRussell King #include <linux/scatterlist.h>
16e193ba29SRussell King #include <linux/seq_file.h>
17e193ba29SRussell King #include <linux/proc_fs.h>
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds #include <asm/dma.h>
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds #include <asm/mach/dma.h>
221da177e4SLinus Torvalds 
23bd31b859SThomas Gleixner DEFINE_RAW_SPINLOCK(dma_spin_lock);
24d7b4a756SRussell King EXPORT_SYMBOL(dma_spin_lock);
251da177e4SLinus Torvalds 
262f757f2aSRussell King static dma_t *dma_chan[MAX_DMA_CHANNELS];
271da177e4SLinus Torvalds 
dma_channel(unsigned int chan)283afb6e9cSRussell King static inline dma_t *dma_channel(unsigned int chan)
293afb6e9cSRussell King {
302f757f2aSRussell King 	if (chan >= MAX_DMA_CHANNELS)
313afb6e9cSRussell King 		return NULL;
323afb6e9cSRussell King 
332f757f2aSRussell King 	return dma_chan[chan];
342f757f2aSRussell King }
352f757f2aSRussell King 
isa_dma_add(unsigned int chan,dma_t * dma)362f757f2aSRussell King int __init isa_dma_add(unsigned int chan, dma_t *dma)
372f757f2aSRussell King {
382f757f2aSRussell King 	if (!dma->d_ops)
392f757f2aSRussell King 		return -EINVAL;
40d667522fSRussell King 
41d667522fSRussell King 	sg_init_table(&dma->buf, 1);
42d667522fSRussell King 
432f757f2aSRussell King 	if (dma_chan[chan])
442f757f2aSRussell King 		return -EBUSY;
452f757f2aSRussell King 	dma_chan[chan] = dma;
462f757f2aSRussell King 	return 0;
473afb6e9cSRussell King }
483afb6e9cSRussell King 
491da177e4SLinus Torvalds /*
501da177e4SLinus Torvalds  * Request DMA channel
511da177e4SLinus Torvalds  *
521da177e4SLinus Torvalds  * On certain platforms, we have to allocate an interrupt as well...
531da177e4SLinus Torvalds  */
request_dma(unsigned int chan,const char * device_id)541df81302SRussell King int request_dma(unsigned int chan, const char *device_id)
551da177e4SLinus Torvalds {
563afb6e9cSRussell King 	dma_t *dma = dma_channel(chan);
571da177e4SLinus Torvalds 	int ret;
581da177e4SLinus Torvalds 
593afb6e9cSRussell King 	if (!dma)
601da177e4SLinus Torvalds 		goto bad_dma;
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds 	if (xchg(&dma->lock, 1) != 0)
631da177e4SLinus Torvalds 		goto busy;
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds 	dma->device_id = device_id;
661da177e4SLinus Torvalds 	dma->active    = 0;
671da177e4SLinus Torvalds 	dma->invalid   = 1;
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds 	ret = 0;
701da177e4SLinus Torvalds 	if (dma->d_ops->request)
711df81302SRussell King 		ret = dma->d_ops->request(chan, dma);
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 	if (ret)
741da177e4SLinus Torvalds 		xchg(&dma->lock, 0);
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds 	return ret;
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds bad_dma:
794ed89f22SRussell King 	pr_err("dma: trying to allocate DMA%d\n", chan);
801da177e4SLinus Torvalds 	return -EINVAL;
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds busy:
831da177e4SLinus Torvalds 	return -EBUSY;
841da177e4SLinus Torvalds }
85d7b4a756SRussell King EXPORT_SYMBOL(request_dma);
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds /*
881da177e4SLinus Torvalds  * Free DMA channel
891da177e4SLinus Torvalds  *
901da177e4SLinus Torvalds  * On certain platforms, we have to free interrupt as well...
911da177e4SLinus Torvalds  */
free_dma(unsigned int chan)921df81302SRussell King void free_dma(unsigned int chan)
931da177e4SLinus Torvalds {
943afb6e9cSRussell King 	dma_t *dma = dma_channel(chan);
951da177e4SLinus Torvalds 
963afb6e9cSRussell King 	if (!dma)
971da177e4SLinus Torvalds 		goto bad_dma;
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds 	if (dma->active) {
1004ed89f22SRussell King 		pr_err("dma%d: freeing active DMA\n", chan);
1011df81302SRussell King 		dma->d_ops->disable(chan, dma);
1021da177e4SLinus Torvalds 		dma->active = 0;
1031da177e4SLinus Torvalds 	}
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 	if (xchg(&dma->lock, 0) != 0) {
1061da177e4SLinus Torvalds 		if (dma->d_ops->free)
1071df81302SRussell King 			dma->d_ops->free(chan, dma);
1081da177e4SLinus Torvalds 		return;
1091da177e4SLinus Torvalds 	}
1101da177e4SLinus Torvalds 
1114ed89f22SRussell King 	pr_err("dma%d: trying to free free DMA\n", chan);
1121da177e4SLinus Torvalds 	return;
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds bad_dma:
1154ed89f22SRussell King 	pr_err("dma: trying to free DMA%d\n", chan);
1161da177e4SLinus Torvalds }
117d7b4a756SRussell King EXPORT_SYMBOL(free_dma);
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds /* Set DMA Scatter-Gather list
1201da177e4SLinus Torvalds  */
set_dma_sg(unsigned int chan,struct scatterlist * sg,int nr_sg)1211df81302SRussell King void set_dma_sg (unsigned int chan, struct scatterlist *sg, int nr_sg)
1221da177e4SLinus Torvalds {
1233afb6e9cSRussell King 	dma_t *dma = dma_channel(chan);
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds 	if (dma->active)
1264ed89f22SRussell King 		pr_err("dma%d: altering DMA SG while DMA active\n", chan);
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds 	dma->sg = sg;
1291da177e4SLinus Torvalds 	dma->sgcount = nr_sg;
1301da177e4SLinus Torvalds 	dma->invalid = 1;
1311da177e4SLinus Torvalds }
132d7b4a756SRussell King EXPORT_SYMBOL(set_dma_sg);
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds /* Set DMA address
1351da177e4SLinus Torvalds  *
1361da177e4SLinus Torvalds  * Copy address to the structure, and set the invalid bit
1371da177e4SLinus Torvalds  */
__set_dma_addr(unsigned int chan,void * addr)1381df81302SRussell King void __set_dma_addr (unsigned int chan, void *addr)
1391da177e4SLinus Torvalds {
1403afb6e9cSRussell King 	dma_t *dma = dma_channel(chan);
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 	if (dma->active)
1434ed89f22SRussell King 		pr_err("dma%d: altering DMA address while DMA active\n", chan);
1441da177e4SLinus Torvalds 
1457cdad482SRussell King 	dma->sg = NULL;
1467cdad482SRussell King 	dma->addr = addr;
1471da177e4SLinus Torvalds 	dma->invalid = 1;
1481da177e4SLinus Torvalds }
149d7b4a756SRussell King EXPORT_SYMBOL(__set_dma_addr);
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds /* Set DMA byte count
1521da177e4SLinus Torvalds  *
1531da177e4SLinus Torvalds  * Copy address to the structure, and set the invalid bit
1541da177e4SLinus Torvalds  */
set_dma_count(unsigned int chan,unsigned long count)1551df81302SRussell King void set_dma_count (unsigned int chan, unsigned long count)
1561da177e4SLinus Torvalds {
1573afb6e9cSRussell King 	dma_t *dma = dma_channel(chan);
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	if (dma->active)
1604ed89f22SRussell King 		pr_err("dma%d: altering DMA count while DMA active\n", chan);
1611da177e4SLinus Torvalds 
1627cdad482SRussell King 	dma->sg = NULL;
1637cdad482SRussell King 	dma->count = count;
1641da177e4SLinus Torvalds 	dma->invalid = 1;
1651da177e4SLinus Torvalds }
166d7b4a756SRussell King EXPORT_SYMBOL(set_dma_count);
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds /* Set DMA direction mode
1691da177e4SLinus Torvalds  */
set_dma_mode(unsigned int chan,unsigned int mode)170f0ffc816SRussell King void set_dma_mode (unsigned int chan, unsigned int mode)
1711da177e4SLinus Torvalds {
1723afb6e9cSRussell King 	dma_t *dma = dma_channel(chan);
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds 	if (dma->active)
1754ed89f22SRussell King 		pr_err("dma%d: altering DMA mode while DMA active\n", chan);
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	dma->dma_mode = mode;
1781da177e4SLinus Torvalds 	dma->invalid = 1;
1791da177e4SLinus Torvalds }
180d7b4a756SRussell King EXPORT_SYMBOL(set_dma_mode);
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds /* Enable DMA channel
1831da177e4SLinus Torvalds  */
enable_dma(unsigned int chan)1841df81302SRussell King void enable_dma (unsigned int chan)
1851da177e4SLinus Torvalds {
1863afb6e9cSRussell King 	dma_t *dma = dma_channel(chan);
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds 	if (!dma->lock)
1891da177e4SLinus Torvalds 		goto free_dma;
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds 	if (dma->active == 0) {
1921da177e4SLinus Torvalds 		dma->active = 1;
1931df81302SRussell King 		dma->d_ops->enable(chan, dma);
1941da177e4SLinus Torvalds 	}
1951da177e4SLinus Torvalds 	return;
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds free_dma:
1984ed89f22SRussell King 	pr_err("dma%d: trying to enable free DMA\n", chan);
1991da177e4SLinus Torvalds 	BUG();
2001da177e4SLinus Torvalds }
201d7b4a756SRussell King EXPORT_SYMBOL(enable_dma);
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds /* Disable DMA channel
2041da177e4SLinus Torvalds  */
disable_dma(unsigned int chan)2051df81302SRussell King void disable_dma (unsigned int chan)
2061da177e4SLinus Torvalds {
2073afb6e9cSRussell King 	dma_t *dma = dma_channel(chan);
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds 	if (!dma->lock)
2101da177e4SLinus Torvalds 		goto free_dma;
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds 	if (dma->active == 1) {
2131da177e4SLinus Torvalds 		dma->active = 0;
2141df81302SRussell King 		dma->d_ops->disable(chan, dma);
2151da177e4SLinus Torvalds 	}
2161da177e4SLinus Torvalds 	return;
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds free_dma:
2194ed89f22SRussell King 	pr_err("dma%d: trying to disable free DMA\n", chan);
2201da177e4SLinus Torvalds 	BUG();
2211da177e4SLinus Torvalds }
222d7b4a756SRussell King EXPORT_SYMBOL(disable_dma);
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds /*
2251da177e4SLinus Torvalds  * Is the specified DMA channel active?
2261da177e4SLinus Torvalds  */
dma_channel_active(unsigned int chan)2271df81302SRussell King int dma_channel_active(unsigned int chan)
2281da177e4SLinus Torvalds {
2293afb6e9cSRussell King 	dma_t *dma = dma_channel(chan);
2303afb6e9cSRussell King 	return dma->active;
2311da177e4SLinus Torvalds }
232ec14d796SRussell King EXPORT_SYMBOL(dma_channel_active);
2331da177e4SLinus Torvalds 
set_dma_page(unsigned int chan,char pagenr)2341df81302SRussell King void set_dma_page(unsigned int chan, char pagenr)
2351da177e4SLinus Torvalds {
2364ed89f22SRussell King 	pr_err("dma%d: trying to set_dma_page\n", chan);
2371da177e4SLinus Torvalds }
238d7b4a756SRussell King EXPORT_SYMBOL(set_dma_page);
2391da177e4SLinus Torvalds 
set_dma_speed(unsigned int chan,int cycle_ns)2401df81302SRussell King void set_dma_speed(unsigned int chan, int cycle_ns)
2411da177e4SLinus Torvalds {
2423afb6e9cSRussell King 	dma_t *dma = dma_channel(chan);
2431da177e4SLinus Torvalds 	int ret = 0;
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	if (dma->d_ops->setspeed)
2461df81302SRussell King 		ret = dma->d_ops->setspeed(chan, dma, cycle_ns);
2471da177e4SLinus Torvalds 	dma->speed = ret;
2481da177e4SLinus Torvalds }
249d7b4a756SRussell King EXPORT_SYMBOL(set_dma_speed);
2501da177e4SLinus Torvalds 
get_dma_residue(unsigned int chan)2511df81302SRussell King int get_dma_residue(unsigned int chan)
2521da177e4SLinus Torvalds {
2533afb6e9cSRussell King 	dma_t *dma = dma_channel(chan);
2541da177e4SLinus Torvalds 	int ret = 0;
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds 	if (dma->d_ops->residue)
2571df81302SRussell King 		ret = dma->d_ops->residue(chan, dma);
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	return ret;
2601da177e4SLinus Torvalds }
261d7b4a756SRussell King EXPORT_SYMBOL(get_dma_residue);
262e193ba29SRussell King 
263e193ba29SRussell King #ifdef CONFIG_PROC_FS
proc_dma_show(struct seq_file * m,void * v)264e193ba29SRussell King static int proc_dma_show(struct seq_file *m, void *v)
265e193ba29SRussell King {
266e193ba29SRussell King 	int i;
267e193ba29SRussell King 
268e193ba29SRussell King 	for (i = 0 ; i < MAX_DMA_CHANNELS ; i++) {
269e193ba29SRussell King 		dma_t *dma = dma_channel(i);
270e193ba29SRussell King 		if (dma && dma->lock)
271e193ba29SRussell King 			seq_printf(m, "%2d: %s\n", i, dma->device_id);
272e193ba29SRussell King 	}
273e193ba29SRussell King 	return 0;
274e193ba29SRussell King }
275e193ba29SRussell King 
proc_dma_init(void)276e193ba29SRussell King static int __init proc_dma_init(void)
277e193ba29SRussell King {
2783f3942acSChristoph Hellwig 	proc_create_single("dma", 0, NULL, proc_dma_show);
279e193ba29SRussell King 	return 0;
280e193ba29SRussell King }
281e193ba29SRussell King 
282e193ba29SRussell King __initcall(proc_dma_init);
283e193ba29SRussell King #endif
284