BSD 4 development
[unix-history] / usr / src / sys / dev / hp.c
/* hp.c 4.1 11/9/80 */
#include "../conf/hp.h"
#if NHP > 0
/*
* RP06/RM03/RM05 disk driver
*/
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/dk.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/map.h"
#include "../h/pte.h"
#include "../h/mba.h"
#include "../h/mtpr.h"
#include "../h/vm.h"
struct device
{
int hpcs1; /* control and Status register 1 */
int hpds; /* Drive Status */
int hper1; /* Error register 1 */
int hpmr; /* Maintenance */
int hpas; /* Attention Summary */
int hpda; /* Desired address register */
int hpdt; /* Drive type */
int hpla; /* Look ahead */
int hpsn; /* serial number */
int hpof; /* Offset register */
int hpdc; /* Desired Cylinder address register */
int hpcc; /* Current Cylinder */
int hper2; /* Error register 2 */
int hper3; /* Error register 3 */
int hpec1; /* Burst error bit position */
int hpec2; /* Burst error bit pattern */
};
#define RP 022
#define RM 024
#define RM5 027
#define NSECT 22
#define NTRAC 19
#define NRMSECT 32
#define NRMTRAC 5
#define _hpSDIST 2
#define _hpRDIST 3
int hpSDIST = _hpSDIST;
int hpRDIST = _hpRDIST;
int hpseek;
struct size
{
daddr_t nblocks;
int cyloff;
} hp_sizes[8] =
{
15884, 0, /* A=cyl 0 thru 37 */
33440, 38, /* B=cyl 38 thru 117 */
340670, 0, /* C=cyl 0 thru 814 */
0, 0,
0, 0,
0, 0,
291346, 118, /* G=cyl 118 thru 814 */
0, 0,
}, rm_sizes[8] = {
15884, 0, /* A=cyl 0 thru 99 */
33440, 100, /* B=cyl 100 thru 309 */
131680, 0, /* C=cyl 0 thru 822 */
0, 0,
0, 0,
0, 0,
82080, 310, /* G=cyl 310 thru 822 */
0, 0,
}, rm5_sizes[8] = {
15884, 0, /* A=cyl 0 thru 26 */
33440, 27, /* B=cyl 27 thru 81 */
500992, 0, /* C=cyl 0 thru 823 */
15884, 562, /* D=cyl 562 thru 588 */
55936, 589, /* E=cyl 589 thru 680 */
86944, 681, /* F=cyl 681 thru 823 */
159296, 562, /* G=cyl 562 thru 823 */
291346, 82, /* H=cyl 82 thru 561 */
};
#define P400 020
#define M400 0220
#define P800 040
#define M800 0240
#define P1200 060
#define M1200 0260
int hp_offset[16] =
{
P400, M400, P400, M400,
P800, M800, P800, M800,
P1200, M1200, P1200, M1200,
0, 0, 0, 0,
};
struct buf hptab;
struct buf rhpbuf;
struct buf hputab[NHP];
char hp_type[NHP]; /* drive type */
#define GO 01
#define PRESET 020
#define RTC 016
#define OFFSET 014
#define SEEK 04
#define SEARCH 030
#define RECAL 06
#define DCLR 010
#define WCOM 060
#define RCOM 070
#define IE 0100
#define PIP 020000
#define DRY 0200
#define ERR 040000
#define TRE 040000
#define DCK 0100000
#define WLE 04000
#define ECH 0100
#define VV 0100
#define DPR 0400
#define MOL 010000
#define FMT22 010000
#define b_cylin b_resid
#ifdef INTRLVE
daddr_t dkblock();
#endif
hpstrategy(bp)
register struct buf *bp;
{
register struct buf *dp;
register unit, xunit, nspc;
long sz, bn;
struct size *sizes;
if ((mbaact&(1<<HPMBANUM)) == 0)
mbainit(HPMBANUM);
xunit = minor(bp->b_dev) & 077;
sz = bp->b_bcount;
sz = (sz+511) >> 9;
unit = dkunit(bp);
if (hp_type[unit] == 0) {
struct device *hpaddr;
double mspw;
/* determine device type */
hpaddr = mbadev(HPMBA, unit);
/* record transfer rate (these are guesstimates secs/word) */
switch (hp_type[unit] = hpaddr->hpdt) {
case RM: mspw = .0000019728; break;
case RM5: mspw = .0000020345; break;
case RP: mspw = .0000029592; break;
}
if (DK_N + unit <= DK_NMAX)
dk_mspw[DK_N+unit] = mspw;
}
switch (hp_type[unit]) {
case RM:
sizes = rm_sizes;
nspc = NRMSECT*NRMTRAC;
break;
case RM5:
sizes = rm5_sizes;
nspc = NRMSECT*NTRAC;
break;
case RP:
sizes = hp_sizes;
nspc = NSECT*NTRAC;
break;
default:
printf("hp: unknown device type 0%o\n", hp_type[unit]);
u.u_error = ENXIO;
unit = NHP+1; /* force error */
}
if (unit >= NHP ||
bp->b_blkno < 0 ||
(bn = dkblock(bp))+sz > sizes[xunit&07].nblocks) {
bp->b_flags |= B_ERROR;
iodone(bp);
return;
}
bp->b_cylin = bn/nspc + sizes[xunit&07].cyloff;
dp = &hputab[unit];
(void) spl5();
disksort(dp, bp);
if (dp->b_active == 0) {
hpustart(unit);
if(hptab.b_active == 0)
hpstart();
}
(void) spl0();
}
hpustart(unit)
register unit;
{
register struct buf *bp, *dp;
register struct device *hpaddr;
daddr_t bn;
int sn, cn, csn;
((struct mba_regs *)HPMBA)->mba_cr |= MBAIE;
hpaddr = mbadev(HPMBA, 0);
hpaddr->hpas = 1<<unit;
if(unit >= NHP)
return;
if (unit+DK_N <= DK_NMAX)
dk_busy &= ~(1<<(unit+DK_N));
dp = &hputab[unit];
if((bp=dp->b_actf) == NULL)
return;
hpaddr = mbadev(HPMBA, unit);
if((hpaddr->hpds & VV) == 0) {
hpaddr->hpcs1 = PRESET|GO;
hpaddr->hpof = FMT22;
}
if(dp->b_active)
goto done;
dp->b_active++;
if ((hpaddr->hpds & (DPR|MOL)) != (DPR|MOL))
goto done;
bn = dkblock(bp);
cn = bp->b_cylin;
switch (hp_type[unit]) {
case RM:
sn = bn%(NRMSECT*NRMTRAC);
sn = (sn+NRMSECT-hpSDIST)%NRMSECT;
break;
case RM5:
sn = bn%(NRMSECT*NTRAC);
sn = (sn+NRMSECT-hpSDIST)%NRMSECT;
break;
case RP:
sn = bn%(NSECT*NTRAC);
sn = (sn+NSECT-hpSDIST)%NSECT;
break;
default:
panic("hpustart");
}
if(cn - (hpaddr->hpdc & 0xffff))
goto search;
else if (hpseek)
goto done;
csn = ((hpaddr->hpla & 0xffff)>>6) - sn + 1;
if(csn < 0)
csn += NSECT;
if(csn > NSECT-hpRDIST)
goto done;
search:
hpaddr->hpdc = cn;
if (hpseek)
hpaddr->hpcs1 = SEEK|GO;
else {
hpaddr->hpda = sn;
hpaddr->hpcs1 = SEARCH|GO;
}
unit += DK_N;
if (unit <= DK_NMAX) {
dk_busy |= 1<<unit;
dk_seek[unit]++;
}
return;
done:
dp->b_forw = NULL;
if(hptab.b_actf == NULL)
hptab.b_actf = dp;
else
hptab.b_actl->b_forw = dp;
hptab.b_actl = dp;
}
hpstart()
{
register struct buf *bp, *dp;
register unit;
register struct device *hpaddr;
daddr_t bn;
int dn, sn, tn, cn, nspc, ns;
loop:
if ((dp = hptab.b_actf) == NULL)
return;
if ((bp = dp->b_actf) == NULL) {
hptab.b_actf = dp->b_forw;
goto loop;
}
hptab.b_active++;
unit = minor(bp->b_dev) & 077;
dn = dkunit(bp);
bn = dkblock(bp);
switch (hp_type[dn]) {
case RM:
nspc = NRMSECT*NRMTRAC;
ns = NRMSECT;
cn = rm_sizes[unit&07].cyloff;
break;
case RM5:
nspc = NRMSECT*NTRAC;
ns = NRMSECT;
cn = rm5_sizes[unit&07].cyloff;
break;
case RP:
nspc = NSECT*NTRAC;
ns = NSECT;
cn = hp_sizes[unit&07].cyloff;
break;
default:
panic("hpstart");
}
cn += bn/nspc;
sn = bn%nspc;
tn = sn/ns;
sn = sn%ns;
hpaddr = mbadev(HPMBA, dn);
if ((hpaddr->hpds & (DPR|MOL)) != (DPR|MOL)) {
hptab.b_active = 0;
hptab.b_errcnt = 0;
dp->b_actf = bp->av_forw;
bp->b_flags |= B_ERROR;
iodone(bp);
goto loop;
}
if(hptab.b_errcnt >= 16 && (bp->b_flags&B_WRITE) == 0) {
hpaddr->hpof = hp_offset[hptab.b_errcnt & 017] | FMT22;
HPMBA->mba_cr &= ~MBAIE;
hpaddr->hpcs1 = OFFSET|GO;
while(hpaddr->hpds & PIP)
;
HPMBA->mba_cr |= MBAIE;
}
hpaddr->hpdc = cn;
hpaddr->hpda = (tn << 8) + sn;
mbastart(bp, (int *)hpaddr);
unit = dn+DK_N;
if (unit <= DK_NMAX) {
dk_busy |= 1<<unit;
dk_xfer[unit]++;
dk_wds[unit] += bp->b_bcount>>6;
}
}
hpintr(mbastat, as)
{
register struct buf *bp, *dp;
register unit;
register struct device *hpaddr;
if(hptab.b_active) {
dp = hptab.b_actf;
bp = dp->b_actf;
unit = dkunit(bp);
if (DK_N+unit <= DK_NMAX)
dk_busy &= ~(1<<(DK_N+unit));
hpaddr = mbadev(HPMBA, unit);
if (hpaddr->hpds & ERR || mbastat & MBAEBITS) {
while((hpaddr->hpds & DRY) == 0)
;
if(++hptab.b_errcnt > 28 || hpaddr->hper1&WLE)
bp->b_flags |= B_ERROR;
else
hptab.b_active = 0;
if(hptab.b_errcnt > 27)
deverror(bp, mbastat, hpaddr->hper1);
if ((hpaddr->hper1&0xffff) == DCK) {
if (hpecc(hpaddr, bp))
return;
}
hpaddr->hpcs1 = DCLR|GO;
if((hptab.b_errcnt&07) == 4) {
HPMBA->mba_cr &= ~MBAIE;
hpaddr->hpcs1 = RECAL|GO;
while(hpaddr->hpds & PIP)
;
HPMBA->mba_cr |= MBAIE;
}
}
if(hptab.b_active) {
if(hptab.b_errcnt) {
HPMBA->mba_cr &= ~MBAIE;
hpaddr->hpcs1 = RTC|GO;
while(hpaddr->hpds & PIP)
;
HPMBA->mba_cr |= MBAIE;
}
hptab.b_active = 0;
hptab.b_errcnt = 0;
hptab.b_actf = dp->b_forw;
dp->b_active = 0;
dp->b_errcnt = 0;
dp->b_actf = bp->av_forw;
bp->b_resid = -HPMBA->mba_bcr & 0xffff;
iodone(bp);
if(dp->b_actf)
hpustart(unit);
}
as &= ~(1<<unit);
} else {
if(as == 0)
HPMBA->mba_cr |= MBAIE;
}
for(unit=0; unit<NHP; unit++)
if(as & (1<<unit))
hpustart(unit);
hpstart();
}
hpread(dev)
{
physio(hpstrategy, &rhpbuf, dev, B_READ, minphys);
}
hpwrite(dev)
{
physio(hpstrategy, &rhpbuf, dev, B_WRITE, minphys);
}
hpecc(rp, bp)
register struct device *rp;
register struct buf *bp;
{
struct mba_regs *mbp = HPMBA;
register int i;
caddr_t addr;
int reg, bit, byte, npf, mask, o;
int dn, bn, cn, tn, sn, ns, nt;
extern char buffers[NBUF][BSIZE];
struct pte mpte;
int bcr;
/*
* Npf is the number of sectors transferred before the sector
* containing the ECC error, and reg is the MBA register
* mapping (the first part of)the transfer.
* O is offset within a memory page of the first byte transferred.
*/
bcr = mbp->mba_bcr & 0xffff;
if (bcr)
bcr |= 0xffff0000; /* sxt */
npf = btop(bcr + bp->b_bcount) - 1;
reg = npf;
o = (int)bp->b_un.b_addr & PGOFSET;
printf("%D ", bp->b_blkno + npf);
prdev("ECC", bp->b_dev);
mask = rp->hpec2&0xffff;
if (mask == 0) {
rp->hpof = FMT22;
return (0);
}
/*
* Compute the byte and bit position of the error.
* The variable i is the byte offset in the transfer,
* the variable byte is the offset from a page boundary
* in main memory.
*/
i = (rp->hpec1&0xffff) - 1; /* -1 makes 0 origin */
bit = i&07;
i = (i&~07)>>3;
byte = i + o;
/*
* Correct while possible bits remain of mask. Since mask
* contains 11 bits, we continue while the bit offset is > -11.
* Also watch out for end of this block and the end of the whole
* transfer.
*/
while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) {
mpte = mbp->mba_map[reg+btop(byte)];
addr = ptob(mpte.pg_pfnum) + (byte & PGOFSET);
putmemc(addr, getmemc(addr)^(mask<<bit));
byte++;
i++;
bit -= 8;
}
hptab.b_active++; /* Either complete or continuing */
if (bcr == 0)
return (0);
/*
* Have to continue the transfer... clear the drive,
* and compute the position where the transfer is to continue.
* We have completed npf+1 sectores of the transfer already;
* restart at offset o of next sector (i.e. in MBA register reg+1).
*/
rp->hpcs1 = DCLR|GO;
dn = dkunit(bp);
bn = dkblock(bp);
switch (hp_type[dn]) {
case RM:
ns = NRMSECT; nt = NRMTRAC; break;
case RM5:
ns = NRMSECT; nt = NTRAC; break;
case RP:
ns = NSECT; nt = NTRAC; break;
default:
panic("hpecc");
}
cn = bp->b_cylin;
sn = bn%(ns*nt) + npf + 1;
tn = sn/ns;
sn %= ns;
cn += tn/nt;
tn %= nt;
rp->hpdc = cn;
rp->hpda = (tn<<8) + sn;
mbp->mba_sr = -1;
mbp->mba_var = (int)ptob(reg+1) + o;
rp->hpcs1 = RCOM|GO;
return (1);
}
#endif