MOXA linux-2.6.x / linux-2.6.9-uc0 from sdlinux-moxaart.tgz
[linux-2.6.9-moxart.git] / arch / arm / mach-moxacpu / apb_dma.c
blob8c5ae2dbd9cc32afed1e4300bc10dfb79c933a59
2 /*
3 * Copyright (C) 2005 Moxa Group All Rights Reserved.
5 * History:
6 * Date Author Comment
7 * 12-01-2005 Victor Yu. Create it.
8 */
9 #include <linux/config.h>
10 #include <asm/arch/cpe/cpe.h>
11 #include <linux/module.h>
12 #include <linux/init.h>
13 #include <linux/interrupt.h>
14 #include <linux/mm.h>
15 #include <linux/slab.h>
16 #include <asm/io.h>
17 #include <asm/dma.h>
18 #include <asm/cacheflush.h>
19 #include <asm/irq.h>
20 #include <asm/arch/cpe_int.h>
22 //#define MOXA_DEBUG
23 #ifdef MOXA_DEBUG
24 #define DBG(x...) printk(x)
25 #else
26 #define DBG(x...)
27 #endif
29 static apb_dma_priv apb_dma_channel[APB_DMA_MAX_CHANNEL];
30 static spinlock_t apb_dma_lock;
32 apb_dma_priv *apb_dma_alloc(int req_no)
34 int i;
35 unsigned long flags;
36 apb_dma_priv *priv=apb_dma_channel;
38 spin_lock_irqsave(&apb_dma_lock, flags);
39 for ( i=0; i<APB_DMA_MAX_CHANNEL; i++, priv++ ) {
40 if ( priv->used_flag == 0 ) {
41 priv->used_flag = 1;
42 priv->irq_handler = NULL;
43 priv->irq_handler_param = NULL;
44 priv->conf_param = NULL;
45 priv->error_flag = 0;
46 priv->req_no = req_no;
47 switch ( req_no ) {
48 case APB_DMA_SPI_TX_REQ_NO :
49 *(unsigned int *)(CPE_PMU_VA_BASE+0xA0) = 0;
50 break;
51 case APB_DMA_SPI_RX_REQ_NO :
52 *(unsigned int *)(CPE_PMU_VA_BASE+0xA4) = 0;
53 break;
54 case APB_DMA_SD_REQ_NO :
55 *(unsigned int *)(CPE_PMU_VA_BASE+0xB8) = 0;
56 break;
57 case APB_DMA_AC97_TX_REQ_NO :
58 *(unsigned int *)(CPE_PMU_VA_BASE+0xBC) = 0;
59 break;
60 case APB_DMA_AC97_RX_REQ_NO :
61 *(unsigned int *)(CPE_PMU_VA_BASE+0xC0) = 0;
62 break;
63 case APB_DMA_USB_DEVICE_REQ_NO :
64 *(unsigned int *)(CPE_PMU_VA_BASE+0xCC) = 0;
65 break;
67 spin_unlock_irqrestore(&apb_dma_lock, flags);
68 DBG("apb_dma_alloc uses DMA channel %d\n", i);
69 return priv;
72 spin_unlock_irqrestore(&apb_dma_lock, flags);
73 return NULL;
75 EXPORT_SYMBOL(apb_dma_alloc);
77 void apb_dma_release(apb_dma_priv *priv)
79 unsigned long flags;
81 spin_lock_irqsave(&apb_dma_lock, flags);
82 if ( priv == NULL ) {
83 spin_unlock_irqrestore(&apb_dma_lock, flags);
84 return;
86 priv->used_flag = 0;
87 priv->error_flag = 0;
88 priv->irq_handler = NULL;
89 priv->irq_handler_param = NULL;
90 priv->conf_param = NULL;
91 priv->req_no = 0;
92 spin_unlock_irqrestore(&apb_dma_lock, flags);
94 EXPORT_SYMBOL(apb_dma_release);
96 void apb_dma_set_irq(apb_dma_priv *priv, void (*func)(void *param), void *param)
98 unsigned long flags;
99 unsigned int cmd;
101 spin_lock_irqsave(&apb_dma_lock, flags);
102 if ( priv == NULL ) {
103 spin_unlock_irqrestore(&apb_dma_lock, flags);
104 return;
106 priv->irq_handler = func;
107 priv->irq_handler_param = param;
108 priv->error_flag = 0;
109 cmd = readl(&priv->reg->command.ul);
110 cmd |= (APB_DMA_FIN_INT_EN | APB_DMA_ERR_INT_EN);
111 writel(cmd, &priv->reg->command.ul);
112 spin_unlock_irqrestore(&apb_dma_lock, flags);
114 EXPORT_SYMBOL(apb_dma_set_irq);
116 void apb_dma_release_irq(apb_dma_priv *priv)
118 unsigned long flags;
119 unsigned int cmd;
121 spin_lock_irqsave(&apb_dma_lock, flags);
122 if ( priv == NULL ) {
123 spin_unlock_irqrestore(&apb_dma_lock, flags);
124 return;
126 cmd = readl(&priv->reg->command.ul);
127 cmd &= ~(APB_DMA_FIN_INT_EN | APB_DMA_ERR_INT_EN);
128 writel(cmd, &priv->reg->command.ul);
129 priv->irq_handler = NULL;
130 priv->irq_handler_param = NULL;
131 priv->error_flag = 0;
132 spin_unlock_irqrestore(&apb_dma_lock, flags);
134 EXPORT_SYMBOL(apb_dma_release_irq);
136 void apb_dma_conf(apb_dma_priv *priv, apb_dma_conf_param *param)
138 unsigned long flags;
139 union _command cmd;
140 unsigned int size;
142 if ( param == NULL || priv == NULL )
143 return;
144 spin_lock_irqsave(&apb_dma_lock, flags);
145 #ifdef CONFIG_UCLINUX
146 writel(param->source_addr, &priv->reg->source_addr);
147 writel(param->dest_addr, &priv->reg->dest_addr);
148 #else // CONFIG_UCLINUX
149 priv->conf_param = param;
150 if ( param->source_sel == APB_DMAB_SOURCE_AHB ) {
151 writel(virt_to_phys((void *)param->source_addr), &priv->reg->source_addr);
152 } else {
153 writel(PHY_ADDRESS(param->source_addr), &priv->reg->source_addr);
155 if ( param->dest_sel == APB_DMAB_DEST_AHB ) {
156 writel(virt_to_phys((void *)param->dest_addr), &priv->reg->dest_addr);
157 } else {
158 writel(PHY_ADDRESS(param->dest_addr), &priv->reg->dest_addr);
160 #endif // CONFIG_UCLINUX
161 size = param->size;
162 switch ( param->data_width ) {
163 case APB_DMAB_DATA_WIDTH_1 :
164 break;
165 case APB_DMAB_DATA_WIDTH_2 :
166 size >>= 1;
167 break;
168 case APB_DMAB_DATA_WIDTH_4 :
169 default :
170 size >>= 2;
171 break;
173 if ( param->burst_mode )
174 size >>= 2;
175 writel(size, &priv->reg->cycles);
176 cmd.ul = readl(&priv->reg->command.ul);
177 cmd.bits.data_width = param->data_width;
178 if ( param->dest_sel == APB_DMAB_DEST_AHB ) { // AHB
179 #ifndef CONFIG_UCLINUX // add by Victor Yu. 07-10-2007
180 dmac_inv_range(param->dest_addr, param->dest_addr+param->size);
181 #endif
182 cmd.bits.dest_req_no = 0;
183 } else { // APB
184 cmd.bits.dest_req_no = priv->req_no;
186 cmd.bits.dest_sel = param->dest_sel;
187 if ( param->source_sel == APB_DMAB_SOURCE_AHB ) { // AHB
188 #ifndef CONFIG_UCLINUX
189 dmac_flush_range(param->source_addr, param->source_addr+param->size);
190 #endif // CONFIG_UCLINUX
191 cmd.bits.source_req_no = 0;
192 } else { // APB
193 cmd.bits.source_req_no = priv->req_no;
195 cmd.bits.source_sel = param->source_sel;
196 cmd.bits.burst = param->burst_mode;
197 cmd.bits.dest_inc = param->dest_inc;
198 cmd.bits.source_inc = param->source_inc;
199 writel(cmd.ul, &priv->reg->command.ul);
200 spin_unlock_irqrestore(&apb_dma_lock, flags);
202 EXPORT_SYMBOL(apb_dma_conf);
204 void apb_dma_enable(apb_dma_priv *priv)
206 unsigned long flags;
207 unsigned int cmd;
209 spin_lock_irqsave(&apb_dma_lock, flags);
210 cmd = readl(&priv->reg->command.ul);
211 cmd |= APB_DMA_ENABLE;
212 writel(cmd, &priv->reg->command.ul);
213 spin_unlock_irqrestore(&apb_dma_lock, flags);
215 EXPORT_SYMBOL(apb_dma_enable);
217 void apb_dma_disable(apb_dma_priv *priv)
219 unsigned long flags;
220 unsigned int cmd;
222 spin_lock_irqsave(&apb_dma_lock, flags);
223 cmd = readl(&priv->reg->command.ul);
224 cmd &= ~APB_DMA_ENABLE;
225 writel(cmd, &priv->reg->command.ul);
226 spin_unlock_irqrestore(&apb_dma_lock, flags);
228 EXPORT_SYMBOL(apb_dma_disable);
230 static irqreturn_t apb_dma_irq(int irq, void *devid, struct pt_regs *regs)
232 int i;
233 unsigned int cmd;
234 apb_dma_priv *priv=apb_dma_channel;
236 DBG("apb_dma_irq test01\n");
237 for ( i=0; i<APB_DMA_MAX_CHANNEL; i++, priv++ ) {
238 cmd = readl(&priv->reg->command.ul);
239 if ( cmd & APB_DMA_FIN_INT_STS ) {
240 DBG("apb_dma_irq finish interrupt channel [%d]\n", i);
241 cmd &= ~APB_DMA_FIN_INT_STS;
242 #ifndef CONFIG_UCLINUX
244 apb_dma_conf_param *conf;
245 if ( (conf=priv->conf_param) != NULL ) {
246 if ( conf->dest_sel == APB_DMAB_DEST_AHB ) { // to DRAM
247 //dmac_inv_range(conf->dest_addr, conf->dest_addr+conf->size);
249 priv->conf_param = NULL;
252 #endif // CONFIG_UCLINUX
254 if ( cmd & APB_DMA_ERR_INT_STS ) {
255 DBG("apb_dma_irq error interrupt channel [%d]\n", i);
256 cmd &= ~APB_DMA_ERR_INT_STS;
257 priv->error_flag = 1;
259 if ( priv->used_flag && priv->irq_handler ) {
260 priv->irq_handler(priv->irq_handler_param);
262 priv->error_flag = 0;
263 writel(cmd, &priv->reg->command.ul);
266 return IRQ_HANDLED;
269 static int __init apb_dma_init(void)
271 int ret, i;
272 apb_dma_priv *priv=apb_dma_channel;
274 printk("Moxa CPU APB DMA Device Driver V1.0 load ");
276 memset(apb_dma_channel, 0, sizeof(apb_dma_channel));
277 spin_lock_init(&apb_dma_lock);
279 for ( i=0; i<APB_DMA_MAX_CHANNEL; i++, priv++ ) {
280 priv->reg = (apb_dma_reg *)(CPE_APBDMA_VA_BASE+0x80+i*sizeof(apb_dma_reg));
283 cpe_int_set_irq(IRQ_APBDMA, EDGE, H_ACTIVE);
284 ret = request_irq(IRQ_APBDMA, apb_dma_irq, SA_INTERRUPT, "APB DMA", NULL);
285 if ( ret )
286 printk("fail !\n");
287 else
288 printk("OK.\n");
290 return ret;
293 module_init(apb_dma_init);