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