1 /*
2  * linux/arch/unicore32/kernel/dma.c
3  *
4  * Code specific to PKUnity SoC and UniCore ISA
5  *
6  *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7  *	Copyright (C) 2001-2010 Guan Xuetao
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  */
13 
14 #include <linux/module.h>
15 #include <linux/init.h>
16 #include <linux/kernel.h>
17 #include <linux/interrupt.h>
18 #include <linux/errno.h>
19 #include <linux/io.h>
20 
21 #include <asm/system.h>
22 #include <asm/irq.h>
23 #include <mach/hardware.h>
24 #include <mach/dma.h>
25 
26 struct dma_channel {
27 	char *name;
28 	puv3_dma_prio prio;
29 	void (*irq_handler)(int, void *);
30 	void (*err_handler)(int, void *);
31 	void *data;
32 };
33 
34 static struct dma_channel dma_channels[MAX_DMA_CHANNELS];
35 
puv3_request_dma(char * name,puv3_dma_prio prio,void (* irq_handler)(int,void *),void (* err_handler)(int,void *),void * data)36 int puv3_request_dma(char *name, puv3_dma_prio prio,
37 			 void (*irq_handler)(int, void *),
38 			 void (*err_handler)(int, void *),
39 			 void *data)
40 {
41 	unsigned long flags;
42 	int i, found = 0;
43 
44 	/* basic sanity checks */
45 	if (!name)
46 		return -EINVAL;
47 
48 	local_irq_save(flags);
49 
50 	do {
51 		/* try grabbing a DMA channel with the requested priority */
52 		for (i = 0; i < MAX_DMA_CHANNELS; i++) {
53 			if ((dma_channels[i].prio == prio) &&
54 			    !dma_channels[i].name) {
55 				found = 1;
56 				break;
57 			}
58 		}
59 		/* if requested prio group is full, try a hier priority */
60 	} while (!found && prio--);
61 
62 	if (found) {
63 		dma_channels[i].name = name;
64 		dma_channels[i].irq_handler = irq_handler;
65 		dma_channels[i].err_handler = err_handler;
66 		dma_channels[i].data = data;
67 	} else {
68 		printk(KERN_WARNING "No more available DMA channels for %s\n",
69 				name);
70 		i = -ENODEV;
71 	}
72 
73 	local_irq_restore(flags);
74 	return i;
75 }
76 EXPORT_SYMBOL(puv3_request_dma);
77 
puv3_free_dma(int dma_ch)78 void puv3_free_dma(int dma_ch)
79 {
80 	unsigned long flags;
81 
82 	if (!dma_channels[dma_ch].name) {
83 		printk(KERN_CRIT
84 			"%s: trying to free channel %d which is already freed\n",
85 			__func__, dma_ch);
86 		return;
87 	}
88 
89 	local_irq_save(flags);
90 	dma_channels[dma_ch].name = NULL;
91 	dma_channels[dma_ch].err_handler = NULL;
92 	local_irq_restore(flags);
93 }
94 EXPORT_SYMBOL(puv3_free_dma);
95 
dma_irq_handler(int irq,void * dev_id)96 static irqreturn_t dma_irq_handler(int irq, void *dev_id)
97 {
98 	int i, dint;
99 
100 	dint = readl(DMAC_ITCSR);
101 	for (i = 0; i < MAX_DMA_CHANNELS; i++) {
102 		if (dint & DMAC_CHANNEL(i)) {
103 			struct dma_channel *channel = &dma_channels[i];
104 
105 			/* Clear TC interrupt of channel i */
106 			writel(DMAC_CHANNEL(i), DMAC_ITCCR);
107 			writel(0, DMAC_ITCCR);
108 
109 			if (channel->name && channel->irq_handler) {
110 				channel->irq_handler(i, channel->data);
111 			} else {
112 				/*
113 				 * IRQ for an unregistered DMA channel:
114 				 * let's clear the interrupts and disable it.
115 				 */
116 				printk(KERN_WARNING "spurious IRQ for"
117 						" DMA channel %d\n", i);
118 			}
119 		}
120 	}
121 	return IRQ_HANDLED;
122 }
123 
dma_err_handler(int irq,void * dev_id)124 static irqreturn_t dma_err_handler(int irq, void *dev_id)
125 {
126 	int i, dint;
127 
128 	dint = readl(DMAC_IESR);
129 	for (i = 0; i < MAX_DMA_CHANNELS; i++) {
130 		if (dint & DMAC_CHANNEL(i)) {
131 			struct dma_channel *channel = &dma_channels[i];
132 
133 			/* Clear Err interrupt of channel i */
134 			writel(DMAC_CHANNEL(i), DMAC_IECR);
135 			writel(0, DMAC_IECR);
136 
137 			if (channel->name && channel->err_handler) {
138 				channel->err_handler(i, channel->data);
139 			} else {
140 				/*
141 				 * IRQ for an unregistered DMA channel:
142 				 * let's clear the interrupts and disable it.
143 				 */
144 				printk(KERN_WARNING "spurious IRQ for"
145 						" DMA channel %d\n", i);
146 			}
147 		}
148 	}
149 	return IRQ_HANDLED;
150 }
151 
puv3_init_dma(void)152 int __init puv3_init_dma(void)
153 {
154 	int i, ret;
155 
156 	/* dma channel priorities on v8 processors:
157 	 * ch 0 - 1  <--> (0) DMA_PRIO_HIGH
158 	 * ch 2 - 3  <--> (1) DMA_PRIO_MEDIUM
159 	 * ch 4 - 5  <--> (2) DMA_PRIO_LOW
160 	 */
161 	for (i = 0; i < MAX_DMA_CHANNELS; i++) {
162 		puv3_stop_dma(i);
163 		dma_channels[i].name = NULL;
164 		dma_channels[i].prio = min((i & 0x7) >> 1, DMA_PRIO_LOW);
165 	}
166 
167 	ret = request_irq(IRQ_DMA, dma_irq_handler, 0, "DMA", NULL);
168 	if (ret) {
169 		printk(KERN_CRIT "Can't register IRQ for DMA\n");
170 		return ret;
171 	}
172 
173 	ret = request_irq(IRQ_DMAERR, dma_err_handler, 0, "DMAERR", NULL);
174 	if (ret) {
175 		printk(KERN_CRIT "Can't register IRQ for DMAERR\n");
176 		free_irq(IRQ_DMA, "DMA");
177 		return ret;
178 	}
179 
180 	return 0;
181 }
182 
183 postcore_initcall(puv3_init_dma);
184