diff options
Diffstat (limited to 'sys/arch/hp300/dev/dma.c')
-rw-r--r-- | sys/arch/hp300/dev/dma.c | 572 |
1 files changed, 0 insertions, 572 deletions
diff --git a/sys/arch/hp300/dev/dma.c b/sys/arch/hp300/dev/dma.c deleted file mode 100644 index 33a9551ba68..00000000000 --- a/sys/arch/hp300/dev/dma.c +++ /dev/null @@ -1,572 +0,0 @@ -/* $OpenBSD: dma.c,v 1.20 2008/10/15 19:12:19 blambert Exp $ */ -/* $NetBSD: dma.c,v 1.19 1997/05/05 21:02:39 thorpej Exp $ */ - -/* - * Copyright (c) 1995, 1996, 1997 - * Jason R. Thorpe. All rights reserved. - * Copyright (c) 1982, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)dma.c 8.1 (Berkeley) 6/10/93 - */ - -/* - * DMA driver - */ - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/time.h> -#include <sys/kernel.h> -#include <sys/proc.h> -#include <sys/device.h> -#include <sys/timeout.h> - -#include <machine/frame.h> -#include <machine/cpu.h> -#include <machine/intr.h> - -#include <hp300/dev/dmareg.h> -#include <hp300/dev/dmavar.h> - -#include <uvm/uvm_extern.h> - -/* - * The largest single request will be MAXPHYS bytes which will require - * at most MAXPHYS/NBPG+1 chain elements to describe, i.e. if none of - * the buffer pages are physically contiguous (MAXPHYS/NBPG) and the - * buffer is not page aligned (+1). - */ -#define DMAMAXIO (MAXPHYS/NBPG+1) - -struct dma_chain { - u_int dc_count; - paddr_t dc_addr; -}; - -struct dma_channel { - struct dmaqueue *dm_job; /* current job */ - struct dmadevice *dm_hwaddr; /* registers if DMA_C */ - struct dmaBdevice *dm_Bhwaddr; /* registers if not DMA_C */ - char dm_flags; /* misc. flags */ - u_short dm_cmd; /* DMA controller command */ - u_int dm_cur; /* current segment */ - u_int dm_last; /* last segment */ - struct dma_chain dm_chain[DMAMAXIO]; /* all segments */ -}; - -struct dma_softc { - struct dmareg *sc_dmareg; /* pointer to our hardware */ - struct isr sc_isr; - struct dma_channel sc_chan[NDMACHAN]; /* 2 channels */ -#ifdef DEBUG - struct timeout sc_timeout; /* DMA timeout */ -#endif - TAILQ_HEAD(, dmaqueue) sc_queue; /* job queue */ - char sc_type; /* A, B, or C */ -} dma_softc; - -/* types */ -#define DMA_B 0 -#define DMA_C 1 - -/* flags */ -#define DMAF_PCFLUSH 0x01 -#define DMAF_VCFLUSH 0x02 -#define DMAF_NOINTR 0x04 - -void dmacflush(struct dma_channel *); -int dmaintr(void *); - -#ifdef DEBUG -int dmadebug = 0; -#define DDB_WORD 0x01 /* same as DMAGO_WORD */ -#define DDB_LWORD 0x02 /* same as DMAGO_LWORD */ -#define DDB_FOLLOW 0x04 -#define DDB_IO 0x08 - -void dmatimeout(void *); -int dmatimo[NDMACHAN]; - -long dmahits[NDMACHAN]; -long dmamisses[NDMACHAN]; -long dmabyte[NDMACHAN]; -long dmaword[NDMACHAN]; -long dmalword[NDMACHAN]; -#endif - -/* - * Initialize the DMA engine, called by dioattach() - */ -void -dmainit() -{ - struct dma_softc *sc = &dma_softc; - struct dmareg *dma; - struct dma_channel *dc; - int i; - char rev; - - /* There's just one. */ - sc->sc_dmareg = (struct dmareg *)DMA_BASE; - dma = sc->sc_dmareg; - - /* - * Determine the DMA type. A DMA_A or DMA_B will fail the - * following probe. - * - * XXX Don't know how to easily differentiate the A and B cards, - * so we just hope nobody has an A card (A cards will work if - * splbio works out to ipl 3). - */ - if (badbaddr((char *)&dma->dma_id[2])) { - rev = 'B'; -#if !defined(HP320) - panic("dmainit: DMA card requires hp320 support"); -#endif - } else - rev = dma->dma_id[2]; - - sc->sc_type = (rev == 'B') ? DMA_B : DMA_C; - - TAILQ_INIT(&sc->sc_queue); - - for (i = 0; i < NDMACHAN; i++) { - dc = &sc->sc_chan[i]; - dc->dm_job = NULL; - switch (i) { - case 0: - dc->dm_hwaddr = &dma->dma_chan0; - dc->dm_Bhwaddr = &dma->dma_Bchan0; - break; - - case 1: - dc->dm_hwaddr = &dma->dma_chan1; - dc->dm_Bhwaddr = &dma->dma_Bchan1; - break; - - default: - panic("dmainit: more than 2 channels?"); - /* NOTREACHED */ - } - } - -#ifdef DEBUG - /* make sure timeout is really not needed */ - timeout_set(&sc->sc_timeout, dmatimeout, sc); - timeout_add_sec(&sc->sc_timeout, 30); -#endif - - printf("98620%c, 2 channels, %d bit DMA\n", - rev, (rev == 'B') ? 16 : 32); - - /* - * Defer hooking up our interrupt until the first - * DMA-using controller has hooked up theirs. - */ - sc->sc_isr.isr_func = NULL; - sc->sc_isr.isr_arg = sc; - sc->sc_isr.isr_priority = IPL_BIO; -} - -/* - * Compute the ipl and (re)establish the interrupt handler - * for the DMA controller. - */ -void -dmacomputeipl() -{ - struct dma_softc *sc = &dma_softc; - - if (sc->sc_isr.isr_func != NULL) - intr_disestablish(&sc->sc_isr); - - /* - * Our interrupt level must be as high as the highest - * device using DMA (i.e. splbio). - */ - sc->sc_isr.isr_ipl = PSLTOIPL(hp300_varpsl[IPL_BIO]); - - sc->sc_isr.isr_func = dmaintr; - intr_establish(&sc->sc_isr, "dma"); -} - -int -dmareq(struct dmaqueue *dq) -{ - struct dma_softc *sc = &dma_softc; - int i, chan, s; - - s = splvm(); - - chan = dq->dq_chan; - for (i = NDMACHAN - 1; i >= 0; i--) { - /* - * Can we use this channel? - */ - if ((chan & (1 << i)) == 0) - continue; - - /* - * We can use it; is it busy? - */ - if (sc->sc_chan[i].dm_job != NULL) - continue; - - /* - * Not busy; give the caller this channel. - */ - sc->sc_chan[i].dm_job = dq; - dq->dq_chan = i; - splx(s); - return (1); - } - - /* - * Couldn't get a channel now; put this in the queue. - */ - TAILQ_INSERT_TAIL(&sc->sc_queue, dq, dq_list); - splx(s); - return (0); -} - -void -dmacflush(struct dma_channel *dc) -{ -#if defined(CACHE_HAVE_PAC) || defined(M68040) - if (dc->dm_flags & DMAF_PCFLUSH) { - PCIA(); - dc->dm_flags &= ~DMAF_PCFLUSH; - } -#endif - -#if defined(CACHE_HAVE_VAC) - if (dc->dm_flags & DMAF_VCFLUSH) { - /* - * 320/350s have VACs that may also need flushing. - * In our case we only flush the supervisor side - * because we know that if we are DMAing to user - * space, the physical pages will also be mapped - * in kernel space (via vmapbuf) and hence cache- - * inhibited by the pmap module due to the multiple - * mapping. - */ - DCIS(); - dc->dm_flags &= ~DMAF_VCFLUSH; - } -#endif -} - -void -dmafree(struct dmaqueue *dq) -{ - int unit = dq->dq_chan; - struct dma_softc *sc = &dma_softc; - struct dma_channel *dc = &sc->sc_chan[unit]; - struct dmaqueue *dn; - int chan, s; - - s = splvm(); - -#ifdef DEBUG - dmatimo[unit] = 0; -#endif - - DMA_CLEAR(dc); - - /* - * XXX we may not always go through the flush code in dmastop() - */ - dmacflush(dc); - - /* - * Channel is now free. Look for another job to run on this - * channel. - */ - dc->dm_job = NULL; - chan = 1 << unit; - TAILQ_FOREACH(dn, &sc->sc_queue, dq_list) { - if (dn->dq_chan & chan) { - /* Found one... */ - TAILQ_REMOVE(&sc->sc_queue, dn, dq_list); - dc->dm_job = dn; - dn->dq_chan = dq->dq_chan; - splx(s); - - /* Start the initiator. */ - (*dn->dq_start)(dn->dq_softc); - return; - } - } - splx(s); -} - -void -dmago(int unit, char *addr, u_int count, int flags) -{ - struct dma_softc *sc = &dma_softc; - struct dma_channel *dc = &sc->sc_chan[unit]; - paddr_t dmaend = 0; - u_int seg, tcount; - -#ifdef DIAGNOSTIC - if (count > MAXPHYS) - panic("dmago: count > MAXPHYS"); -#endif - -#if defined(HP320) - if (sc->sc_type == DMA_B && (flags & DMAGO_LWORD)) - panic("dmago: no can do 32-bit DMA"); -#endif - -#ifdef DEBUG - if (dmadebug & DDB_FOLLOW) - printf("dmago(%d, %p, %x, %x)\n", - unit, addr, count, flags); - if (flags & DMAGO_LWORD) - dmalword[unit]++; - else if (flags & DMAGO_WORD) - dmaword[unit]++; - else - dmabyte[unit]++; -#endif - /* - * Build the DMA chain - */ - for (seg = 0; count > 0; seg++) { - if (pmap_extract(pmap_kernel(), (vaddr_t)addr, - &dc->dm_chain[seg].dc_addr) == FALSE) - panic("dmago: pmap_extract(%x) failed", addr); -#if defined(M68040) - /* - * Push back dirty cache lines - */ - if (mmutype == MMU_68040) - DCFP(dc->dm_chain[seg].dc_addr); -#endif - if (count < (tcount = PAGE_SIZE - ((int)addr & PAGE_MASK))) - tcount = count; - dc->dm_chain[seg].dc_count = tcount; - addr += tcount; - count -= tcount; - if (flags & DMAGO_LWORD) - tcount >>= 2; - else if (flags & DMAGO_WORD) - tcount >>= 1; - - /* - * Try to compact the DMA transfer if the pages are adjacent. - * Note: this will never happen on the first iteration. - */ - if (dc->dm_chain[seg].dc_addr == dmaend -#if defined(HP320) - /* only 16-bit count on 98620B */ - && (sc->sc_type != DMA_B || - dc->dm_chain[seg - 1].dc_count + tcount <= 65536) -#endif - ) { -#ifdef DEBUG - dmahits[unit]++; -#endif - dmaend += dc->dm_chain[seg].dc_count; - dc->dm_chain[--seg].dc_count += tcount; - } else { -#ifdef DEBUG - dmamisses[unit]++; -#endif - dmaend = dc->dm_chain[seg].dc_addr + - dc->dm_chain[seg].dc_count; - dc->dm_chain[seg].dc_count = tcount; - } - } - dc->dm_cur = 0; - dc->dm_last = --seg; - dc->dm_flags = 0; - /* - * Set up the command word based on flags - */ - dc->dm_cmd = DMA_ENAB | DMA_IPL(sc->sc_isr.isr_ipl) | DMA_START; - if ((flags & DMAGO_READ) == 0) - dc->dm_cmd |= DMA_WRT; - if (flags & DMAGO_LWORD) - dc->dm_cmd |= DMA_LWORD; - else if (flags & DMAGO_WORD) - dc->dm_cmd |= DMA_WORD; - if (flags & DMAGO_PRI) - dc->dm_cmd |= DMA_PRI; - - if (flags & DMAGO_READ) { -#if defined(M68040) - /* - * On the 68040 we need to flush (push) the data cache before a - * DMA (already done above) and flush again after DMA completes. - * In theory we should only need to flush prior to a write DMA - * and purge after a read DMA but if the entire page is not - * involved in the DMA we might purge some valid data. - */ - if (mmutype == MMU_68040) - dc->dm_flags |= DMAF_PCFLUSH; -#endif - -#if defined(CACHE_HAVE_PAC) - /* - * Remember if we need to flush external physical cache when - * DMA is done. We only do this if we are reading - * (writing memory). - */ - if (ectype == EC_PHYS) - dc->dm_flags |= DMAF_PCFLUSH; -#endif - -#if defined(CACHE_HAVE_VAC) - if (ectype == EC_VIRT) - dc->dm_flags |= DMAF_VCFLUSH; -#endif - } - - /* - * Remember if we can skip the dma completion interrupt on - * the last segment in the chain. - */ - if (flags & DMAGO_NOINT) { - if (dc->dm_cur == dc->dm_last) - dc->dm_cmd &= ~DMA_ENAB; - else - dc->dm_flags |= DMAF_NOINTR; - } -#ifdef DEBUG - if (dmadebug & DDB_IO) { - if (((dmadebug&DDB_WORD) && (dc->dm_cmd&DMA_WORD)) || - ((dmadebug&DDB_LWORD) && (dc->dm_cmd&DMA_LWORD))) { - printf("dmago: cmd %x, flags %x\n", - dc->dm_cmd, dc->dm_flags); - for (seg = 0; seg <= dc->dm_last; seg++) - printf(" %d: %d@%p\n", seg, - dc->dm_chain[seg].dc_count, - dc->dm_chain[seg].dc_addr); - } - } - dmatimo[unit] = 1; -#endif - DMA_ARM(sc, dc); -} - -void -dmastop(int unit) -{ - struct dma_softc *sc = &dma_softc; - struct dma_channel *dc = &sc->sc_chan[unit]; - -#ifdef DEBUG - if (dmadebug & DDB_FOLLOW) - printf("dmastop(%d)\n", unit); - dmatimo[unit] = 0; -#endif - DMA_CLEAR(dc); - - dmacflush(dc); - - /* - * We may get this interrupt after a device service routine - * has freed the dma channel. So, ignore the intr if there's - * nothing on the queue. - */ - if (dc->dm_job != NULL) - (*dc->dm_job->dq_done)(dc->dm_job->dq_softc); -} - -int -dmaintr(void *arg) -{ - struct dma_softc *sc = arg; - struct dma_channel *dc; - int i, stat; - int found = 0; - -#ifdef DEBUG - if (dmadebug & DDB_FOLLOW) - printf("dmaintr\n"); -#endif - for (i = 0; i < NDMACHAN; i++) { - dc = &sc->sc_chan[i]; - stat = DMA_STAT(dc); - if ((stat & DMA_INTR) == 0) - continue; - found++; -#ifdef DEBUG - if (dmadebug & DDB_IO) { - if (((dmadebug&DDB_WORD) && (dc->dm_cmd&DMA_WORD)) || - ((dmadebug&DDB_LWORD) && (dc->dm_cmd&DMA_LWORD))) - printf("dmaintr: flags %x unit %d stat %x next %d\n", - dc->dm_flags, i, stat, dc->dm_cur + 1); - } - if (stat & DMA_ARMED) - printf("dma channel %d: intr when armed\n", i); -#endif - /* - * Load the next segment, or finish up if we're done. - */ - dc->dm_cur++; - if (dc->dm_cur <= dc->dm_last) { -#ifdef DEBUG - dmatimo[i] = 1; -#endif - /* - * If we're the last segment, disable the - * completion interrupt, if necessary. - */ - if (dc->dm_cur == dc->dm_last && - (dc->dm_flags & DMAF_NOINTR)) - dc->dm_cmd &= ~DMA_ENAB; - DMA_CLEAR(dc); - DMA_ARM(sc, dc); - } else - dmastop(i); - } - return(found); -} - -#ifdef DEBUG -void -dmatimeout(void *arg) -{ - int i, s; - struct dma_softc *sc = arg; - - for (i = 0; i < NDMACHAN; i++) { - s = splvm(); - if (dmatimo[i]) { - if (dmatimo[i] > 1) - printf("dma channel %d timeout #%d\n", - i, dmatimo[i]-1); - dmatimo[i]++; - } - splx(s); - } - timeout_add_sec(&sc->sc_timeout, 30); -} -#endif |