1/*
2 *  linux/arch/arm/mach-pxa/dma.c
3 *
4 *  PXA DMA registration and IRQ dispatching
5 *
6 *  Author:	Nicolas Pitre
7 *  Created:	Nov 15, 2001
8 *  Copyright:	MontaVista Software Inc.
9 *
10 *  This program is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 2 as
12 *  published by the Free Software Foundation.
13 */
14
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/kernel.h>
18#include <linux/interrupt.h>
19#include <linux/errno.h>
20
21#include <asm/system.h>
22#include <asm/irq.h>
23#include <asm/hardware.h>
24#include <asm/dma.h>
25
26#include <asm/arch/pxa-regs.h>
27
28static struct dma_channel {
29	char *name;
30	void (*irq_handler)(int, void *);
31	void *data;
32} dma_channels[PXA_DMA_CHANNELS];
33
34
35int pxa_request_dma (char *name, pxa_dma_prio prio,
36			 void (*irq_handler)(int, void *),
37		 	 void *data)
38{
39	unsigned long flags;
40	int i, found = 0;
41
42	/* basic sanity checks */
43	if (!name || !irq_handler)
44		return -EINVAL;
45
46	local_irq_save(flags);
47
48	do {
49		/* try grabbing a DMA channel with the requested priority */
50		pxa_for_each_dma_prio (i, prio) {
51			if (!dma_channels[i].name) {
52				found = 1;
53				break;
54			}
55		}
56		/* if requested prio group is full, try a hier priority */
57	} while (!found && prio--);
58
59	if (found) {
60		DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
61		dma_channels[i].name = name;
62		dma_channels[i].irq_handler = irq_handler;
63		dma_channels[i].data = data;
64	} else {
65		printk (KERN_WARNING "No more available DMA channels for %s\n", name);
66		i = -ENODEV;
67	}
68
69	local_irq_restore(flags);
70	return i;
71}
72
73void pxa_free_dma (int dma_ch)
74{
75	unsigned long flags;
76
77	if (!dma_channels[dma_ch].name) {
78		printk (KERN_CRIT
79			"%s: trying to free channel %d which is already freed\n",
80			__FUNCTION__, dma_ch);
81		return;
82	}
83
84	local_irq_save(flags);
85	DCSR(dma_ch) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
86	dma_channels[dma_ch].name = NULL;
87	local_irq_restore(flags);
88}
89
90static irqreturn_t dma_irq_handler(int irq, void *dev_id)
91{
92	int i, dint = DINT;
93
94	for (i = 0; i < PXA_DMA_CHANNELS; i++) {
95		if (dint & (1 << i)) {
96			struct dma_channel *channel = &dma_channels[i];
97			if (channel->name && channel->irq_handler) {
98				channel->irq_handler(i, channel->data);
99			} else {
100				/*
101				 * IRQ for an unregistered DMA channel:
102				 * let's clear the interrupts and disable it.
103				 */
104				printk (KERN_WARNING "spurious IRQ for DMA channel %d\n", i);
105				DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
106			}
107		}
108	}
109	return IRQ_HANDLED;
110}
111
112static int __init pxa_dma_init (void)
113{
114	int ret;
115
116	ret = request_irq (IRQ_DMA, dma_irq_handler, 0, "DMA", NULL);
117	if (ret)
118		printk (KERN_CRIT "Wow!  Can't register IRQ for DMA\n");
119	return ret;
120}
121
122arch_initcall(pxa_dma_init);
123
124EXPORT_SYMBOL(pxa_request_dma);
125EXPORT_SYMBOL(pxa_free_dma);
126