3 * Copyright (C) 2005 Moxa Group All Rights Reserved.
7 * 12-01-2005 Victor Yu. Create it.
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>
15 #include <linux/slab.h>
18 #include <asm/cacheflush.h>
20 #include <asm/arch/cpe_int.h>
24 #define DBG(x...) printk(x)
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
)
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 ) {
42 priv
->irq_handler
= NULL
;
43 priv
->irq_handler_param
= NULL
;
44 priv
->conf_param
= NULL
;
46 priv
->req_no
= req_no
;
48 case APB_DMA_SPI_TX_REQ_NO
:
49 *(unsigned int *)(CPE_PMU_VA_BASE
+0xA0) = 0;
51 case APB_DMA_SPI_RX_REQ_NO
:
52 *(unsigned int *)(CPE_PMU_VA_BASE
+0xA4) = 0;
54 case APB_DMA_SD_REQ_NO
:
55 *(unsigned int *)(CPE_PMU_VA_BASE
+0xB8) = 0;
57 case APB_DMA_AC97_TX_REQ_NO
:
58 *(unsigned int *)(CPE_PMU_VA_BASE
+0xBC) = 0;
60 case APB_DMA_AC97_RX_REQ_NO
:
61 *(unsigned int *)(CPE_PMU_VA_BASE
+0xC0) = 0;
63 case APB_DMA_USB_DEVICE_REQ_NO
:
64 *(unsigned int *)(CPE_PMU_VA_BASE
+0xCC) = 0;
67 spin_unlock_irqrestore(&apb_dma_lock
, flags
);
68 DBG("apb_dma_alloc uses DMA channel %d\n", i
);
72 spin_unlock_irqrestore(&apb_dma_lock
, flags
);
75 EXPORT_SYMBOL(apb_dma_alloc
);
77 void apb_dma_release(apb_dma_priv
*priv
)
81 spin_lock_irqsave(&apb_dma_lock
, flags
);
83 spin_unlock_irqrestore(&apb_dma_lock
, flags
);
88 priv
->irq_handler
= NULL
;
89 priv
->irq_handler_param
= NULL
;
90 priv
->conf_param
= NULL
;
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
)
101 spin_lock_irqsave(&apb_dma_lock
, flags
);
102 if ( priv
== NULL
) {
103 spin_unlock_irqrestore(&apb_dma_lock
, flags
);
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
)
121 spin_lock_irqsave(&apb_dma_lock
, flags
);
122 if ( priv
== NULL
) {
123 spin_unlock_irqrestore(&apb_dma_lock
, flags
);
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
)
142 if ( param
== NULL
|| priv
== NULL
)
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
);
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
);
158 writel(PHY_ADDRESS(param
->dest_addr
), &priv
->reg
->dest_addr
);
160 #endif // CONFIG_UCLINUX
162 switch ( param
->data_width
) {
163 case APB_DMAB_DATA_WIDTH_1
:
165 case APB_DMAB_DATA_WIDTH_2
:
168 case APB_DMAB_DATA_WIDTH_4
:
173 if ( param
->burst_mode
)
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
);
182 cmd
.bits
.dest_req_no
= 0;
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;
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
)
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
)
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
)
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
);
269 static int __init
apb_dma_init(void)
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
);
293 module_init(apb_dma_init
);