|
|
BSD 4.3reno
/*
* Copyright (c) 1982, 1990 The Regents of the University of California.
* All rights reserved.
*
* Redistribution is only permitted until one year after the first shipment
* of 4.4BSD by the Regents. Otherwise, redistribution and use in source and
* binary forms are permitted provided that: (1) source distributions retain
* this entire copyright notice and comment, and (2) distributions including
* binaries display the following acknowledgement: This product includes
* software developed by the University of California, Berkeley and its
* contributors'' in the documentation or other materials provided with the
* distribution and in all advertising materials mentioning features or use
* of this software. 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 AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* @(#)dma.c 7.1 (Berkeley) 5/8/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);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.