1 /* $NetBSD: dma.c,v 1.23 2009/07/08 12:23:09 tsutsui Exp $ */
4 * Copyright (c) 1995 Leo Weppelman.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * This file contains special code dealing with the DMA interface
32 * The DMA circuitry requires some special treatment for the peripheral
33 * devices which make use of the ST's DMA feature (the hard disk and the
35 * All devices using DMA need mutually exclusive access and can follow some
36 * standard pattern which will be provided in this file.
38 * The file contains the following entry points:
40 * st_dmagrab: ensure exclusive access to the DMA circuitry
41 * st_dmafree: free exclusive access to the DMA circuitry
42 * st_dmawanted: somebody is queued waiting for DMA-access
43 * dmaint: DMA interrupt routine, switches to the current driver
44 * st_dmaaddr_set: specify 24 bit RAM address
45 * st_dmaaddr_get: get address of last DMA-op
46 * st_dmacomm: program DMA, flush FIFO first
49 #include <sys/cdefs.h>
50 __KERNEL_RCSID(0, "$NetBSD: dma.c,v 1.23 2009/07/08 12:23:09 tsutsui Exp $");
52 #include <sys/param.h>
53 #include <sys/systm.h>
54 #include <sys/kernel.h>
56 #include <sys/queue.h>
58 #include <machine/cpu.h>
59 #include <machine/iomap.h>
60 #include <machine/dma.h>
61 #include <machine/intr.h>
63 #define NDMA_DEV 10 /* Max 2 floppy's, 8 hard-disks */
64 typedef struct dma_entry
{
65 TAILQ_ENTRY(dma_entry
) entries
; /* List pointers */
66 void (*call_func
)(void *); /* Call when lock granted */
67 void (*int_func
)(void *); /* Call on DMA interrupt */
68 void *softc
; /* Arg. to int_func */
69 int *lock_stat
; /* status of DMA lock */
73 * Preallocated entries. An allocator seem an overkill here.
75 static DMA_ENTRY dmatable
[NDMA_DEV
]; /* preallocated entries */
78 * Heads of free and active lists:
80 static TAILQ_HEAD(freehead
, dma_entry
) dma_free
;
81 static TAILQ_HEAD(acthead
, dma_entry
) dma_active
;
83 static int must_init
= 1; /* Must initialize */
85 int cdmaint(void *, int);
87 static void st_dma_init(void);
94 TAILQ_INIT(&dma_free
);
95 TAILQ_INIT(&dma_active
);
97 for(i
= 0; i
< NDMA_DEV
; i
++)
98 TAILQ_INSERT_HEAD(&dma_free
, &dmatable
[i
], entries
);
100 if (intr_establish(7, USER_VEC
, 0, cdmaint
, NULL
) == NULL
)
101 panic("st_dma_init: Can't establish interrupt");
105 st_dmagrab(dma_farg int_func
, dma_farg call_func
, void *softc
, int *lock_stat
, int rcaller
)
114 *lock_stat
= DMA_LOCK_REQ
;
119 * Create a request...
121 if(dma_free
.tqh_first
== NULL
)
122 panic("st_dmagrab: Too many outstanding requests");
123 req
= dma_free
.tqh_first
;
124 TAILQ_REMOVE(&dma_free
, dma_free
.tqh_first
, entries
);
125 req
->call_func
= call_func
;
126 req
->int_func
= int_func
;
128 req
->lock_stat
= lock_stat
;
129 TAILQ_INSERT_TAIL(&dma_active
, req
, entries
);
131 if(dma_active
.tqh_first
!= req
) {
132 if (call_func
== NULL
) {
134 tsleep(&dma_active
, PRIBIO
, "dmalck", 0);
135 } while (*req
->lock_stat
!= DMA_LOCK_GRANT
);
145 * We're at the head of the queue, ergo: we got the lock.
147 *lock_stat
= DMA_LOCK_GRANT
;
149 if(rcaller
|| (call_func
== NULL
)) {
151 * Just return to caller immediately without going
152 * through 'call_func' first.
157 (*call_func
)(softc
); /* Call followup function */
162 st_dmafree(void *softc
, int *lock_stat
)
170 * Some validity checks first.
172 if((req
= dma_active
.tqh_first
) == NULL
)
173 panic("st_dmafree: empty active queue");
174 if(req
->softc
!= softc
)
175 printf("Caller of st_dmafree is not lock-owner!\n");
178 * Clear lock status, move request from active to free queue.
181 TAILQ_REMOVE(&dma_active
, req
, entries
);
182 TAILQ_INSERT_HEAD(&dma_free
, req
, entries
);
184 if((req
= dma_active
.tqh_first
) != NULL
) {
185 *req
->lock_stat
= DMA_LOCK_GRANT
;
187 if (req
->call_func
== NULL
)
188 wakeup((void *)&dma_active
);
191 * Call next request through softint handler. This avoids
194 add_sicallback((si_farg
)req
->call_func
, req
->softc
, 0);
204 return(dma_active
.tqh_first
->entries
.tqe_next
!= NULL
);
208 cdmaint(void *unused
, int sr
)
209 /* sr: sr at time of interrupt */
214 if(dma_active
.tqh_first
!= NULL
) {
216 * Due to the logic of the ST-DMA chip, it is not possible to
217 * check for stray interrupts here...
219 int_func
= dma_active
.tqh_first
->int_func
;
220 softc
= dma_active
.tqh_first
->softc
;
223 add_sicallback((si_farg
)int_func
, softc
, 0);
235 * Setup address for DMA-transfer.
236 * Note: The order _is_ important!
239 st_dmaaddr_set(void * address
)
241 register u_long ad
= (u_long
)address
;
243 DMA
->dma_addr
[AD_LOW
] = (ad
) & 0xff;
244 DMA
->dma_addr
[AD_MID
] = (ad
>> 8) & 0xff;
245 DMA
->dma_addr
[AD_HIGH
] = (ad
>>16) & 0xff;
249 * Get address from DMA unit.
254 register u_long ad
= 0;
256 ad
= (DMA
->dma_addr
[AD_LOW
] & 0xff);
257 ad
|= (DMA
->dma_addr
[AD_MID
] & 0xff) << 8;
258 ad
|= (DMA
->dma_addr
[AD_HIGH
] & 0xff) <<16;
263 * Program the DMA-controller to transfer 'nblk' blocks of 512 bytes.
264 * The DMA_WRBIT trick flushes the FIFO before doing DMA.
267 st_dmacomm(int mode
, int nblk
)
269 DMA
->dma_mode
= mode
;
270 DMA
->dma_mode
= mode
^ DMA_WRBIT
;
271 DMA
->dma_mode
= mode
;
272 DMA
->dma_data
= nblk
;
273 delay(2); /* Needed for Falcon */
274 DMA
->dma_mode
= DMA_SCREG
| (mode
& DMA_WRBIT
);