/* * Copyright (c) 1982, 1990 The Regents of the University of California. * All rights reserved. * * %sccs.include.redist.c% * * @(#)dma.c 7.1 (Berkeley) 05/08/90 */ /* * DMA driver */ #include "param.h" #include "systm.h" #include "time.h" #include "kernel.h" #include "proc.h" #include "dmareg.h" #include "dmavar.h" #include "device.h" #include "machine/cpu.h" #include "machine/isr.h" extern void isrlink(); extern void printf(); extern void panic(); extern void _insque(); extern void _remque(); extern void timeout(); extern int splbio(); extern void splx(); extern u_int kvtop(); extern void PCIA(); /* * 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) #define DMATIMO 15 struct dma_softc { struct dmadevice *sc_hwaddr; struct dmaBdevice *sc_Bhwaddr; int sc_type; int sc_cur; int sc_cmd; int sc_timo; int sc_count[DMAMAXIO+1]; char *sc_addr[DMAMAXIO+1]; } dma_softc[NDMA]; /* types */ #define DMA_B 0 #define DMA_C 1 struct devqueue dmachan[NDMA + 1]; int dmaintr(); void dmatimo(); #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 long dmahits[NDMA]; long dmamisses[NDMA]; long dmabyte[NDMA]; long dmaword[NDMA]; long dmalword[NDMA]; #endif void dmainit() { register struct dmareg *dma = (struct dmareg *)DMA_BASE; register struct dma_softc *dc; register int i; char rev; /* * Determine the DMA type. * 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 * DMAINTLVL is set to 3). */ if (!badbaddr((char *)&dma->dma_id[2])) rev = dma->dma_id[2]; else { rev = 'B'; #if !defined(HP320) panic("dmainit: DMA card requires hp320 support"); #endif } dc = &dma_softc[0]; for (i = 0; i < NDMA; i++) { dc->sc_hwaddr = (i & 1) ? &dma->dma_chan1 : &dma->dma_chan0; dc->sc_Bhwaddr = (i & 1) ? &dma->dma_Bchan1 : &dma->dma_Bchan0; dc->sc_type = rev == 'B' ? DMA_B : DMA_C; dc++; dmachan[i].dq_forw = dmachan[i].dq_back = &dmachan[i]; } dmachan[i].dq_forw = dmachan[i].dq_back = &dmachan[i]; timeout(dmatimo, (caddr_t)0, DMATIMO * hz); printf("dma: 98620%c with 2 channels, %d bit DMA\n", rev, rev == 'B' ? 16 : 32); } int dmareq(dq) register struct devqueue *dq; { register int i; register int chan; register int s = splbio(); chan = dq->dq_ctlr; i = NDMA; while (--i >= 0) { if ((chan & (1 << i)) == 0) continue; if (dmachan[i].dq_forw != &dmachan[i]) continue; insque(dq, &dmachan[i]); dq->dq_ctlr = i; splx(s); return(1); } insque(dq, dmachan[NDMA].dq_back); splx(s); return(0); } void dmafree(dq) register struct devqueue *dq; { int unit = dq->dq_ctlr; register struct dma_softc *dc = &dma_softc[unit]; register struct devqueue *dn; register int chan, s; s = splbio(); dc->sc_timo = 0; DMA_CLEAR(dc); remque(dq); chan = 1 << unit; for (dn = dmachan[NDMA].dq_forw; dn != &dmachan[NDMA]; dn = dn->dq_forw) { if (dn->dq_ctlr & chan) { remque((caddr_t)dn); insque((caddr_t)dn, (caddr_t)dq->dq_back); splx(s); dn->dq_ctlr = dq->dq_ctlr; (dn->dq_driver->d_start)(dn->dq_unit); return; } } splx(s); } void dmago(unit, addr, count, flags) int unit; register char *addr; register int count; register int flags; { register struct dma_softc *dc = &dma_softc[unit]; register char *dmaend = NULL; register int tcount, i; #ifdef DEBUG if (dmadebug & DDB_FOLLOW) printf("dmago(%d, %x, %x, %x)\n", unit, addr, count, flags); if (flags & DMAGO_LWORD) dmalword[unit]++; else if (flags & DMAGO_WORD) dmaword[unit]++; else dmabyte[unit]++; #endif #if defined(HP320) if (dc->sc_type == DMA_B && (flags & DMAGO_LWORD)) panic("dmago: no can do 32-bit DMA"); #endif /* * Build the DMA chain */ for (i = 0; i < DMAMAXIO && count; i++) { dc->sc_addr[i] = (char *)kvtop(addr); tcount = dc->sc_count[i] = MIN(count, NBPG - ((int)addr & PGOFSET)); addr += dc->sc_count[i]; count -= tcount; if (flags & (DMAGO_WORD|DMAGO_LWORD)) tcount >>= (flags & DMAGO_WORD) ? 1 : 2; if (dc->sc_addr[i] == dmaend #if defined(HP320) /* only 16-bit count on 98620B */ && (dc->sc_type != DMA_B || dc->sc_count[i-1] + tcount <= 65536) #endif ) { #ifdef DEBUG dmahits[unit]++; #endif dmaend += dc->sc_count[i]; dc->sc_count[i-1] += tcount; i--; } else { #ifdef DEBUG dmamisses[unit]++; #endif dmaend = dc->sc_addr[i] + dc->sc_count[i]; dc->sc_count[i] = tcount; } } if (count) panic("dmago maxphys"); dc->sc_count[i] = 0; dc->sc_cur = 0; /* * Set up the command word based on flags */ dc->sc_cmd = DMA_ENAB | DMA_IPL(DMAINTLVL) | DMA_START; if ((flags & DMAGO_READ) == 0) dc->sc_cmd |= DMA_WRT; if (flags & DMAGO_LWORD) dc->sc_cmd |= DMA_LWORD; else if (flags & DMAGO_WORD) dc->sc_cmd |= DMA_WORD; if (flags & DMAGO_PRI) dc->sc_cmd |= DMA_PRI; /* * We should be able to skip the dma completion interrupt * if we only have one segment in the chain since many * devices generate their own completion interrupt. * However, on a 370 we have to take the interrupt on * read transfers to invalidate the external cache. */ if ((flags & DMAGO_NOINT) && i == 1 #if defined(HP370) && ((flags & DMAGO_READ) == 0 || ectype != EC_PHYS) #endif ) dc->sc_cmd &= ~DMA_ENAB; #ifdef DEBUG #if defined(HP320) /* would this hurt? */ if (dc->sc_type == DMA_B) dc->sc_cmd &= ~DMA_START; #endif if (dmadebug & DDB_IO) if ((dmadebug&DDB_WORD) && (dc->sc_cmd&DMA_WORD) || (dmadebug&DDB_LWORD) && (dc->sc_cmd&DMA_LWORD)) { printf("dmago: cmd %x\n", dc->sc_cmd); for (i = 0; dc->sc_count[i]; i++) printf(" %d: %d@%x\n", i, dc->sc_count[i], dc->sc_addr[i]); } #endif /* * Load and arm the channel */ dc->sc_timo = 1; DMA_ARM(dc, 0); } void dmastop(unit) register int unit; { register struct dma_softc *dc = &dma_softc[unit]; register struct devqueue *dq; #ifdef DEBUG if (dmadebug & DDB_FOLLOW) printf("dmastop(%d)\n", unit); #endif dc->sc_timo = 0; DMA_CLEAR(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. */ dq = dmachan[unit].dq_forw; if (dq != &dmachan[unit]) { #if defined(HP370) /* * The 370 has an 64k external physical address cache. * In theory, we should only need to flush it when * DMAing to memory. */ if (ectype == EC_PHYS && (dc->sc_cmd & DMA_WRT) == 0) PCIA(); #endif (dq->dq_driver->d_done)(dq->dq_unit); } } int dmaintr() { register struct dma_softc *dc; register int i, j, stat; int found = 0; #ifdef DEBUG if (dmadebug & DDB_FOLLOW) printf("dmaintr\n"); #endif for (i = 0, dc = dma_softc; i < NDMA; i++, dc++) { stat = DMA_STAT(dc); if ((stat & DMA_INTR) == 0) continue; found++; #ifdef DEBUG if (dmadebug & DDB_IO) { if ((dmadebug&DDB_WORD) && (dc->sc_cmd&DMA_WORD) || (dmadebug&DDB_LWORD) && (dc->sc_cmd&DMA_LWORD)) printf("dmaintr: unit %d stat %x next %d\n", i, stat, dc->sc_cur+1); } if (stat & DMA_ARMED) printf("dma%d: intr when armed\n", i); #endif j = ++dc->sc_cur; if (j < DMAMAXIO && dc->sc_count[j]) { dc->sc_timo = 1; DMA_CLEAR(dc); DMA_ARM(dc, j); } else dmastop(i); } return(found); } void dmatimo() { register int i, s; register struct dma_softc *dc = &dma_softc[0]; for (i = 0; i < NDMA; i++, dc++) { s = splbio(); if (dc->sc_timo) { if (dc->sc_timo == 1) dc->sc_timo++; else dmastop(i); } splx(s); } timeout(dmatimo, (caddr_t)0, DMATIMO * hz); }