make it look more like the system version; controller addresses
[unix-history] / usr / src / sys / tahoe / stand / vd.c
/* vd.c 7.5 86/12/19 */
/*
* Stand alone driver for the VDDC controller
* TAHOE Version, Oct 1983.
*/
#include "../machine/mtpr.h"
#include "param.h"
#include "inode.h"
#include "fs.h"
#define VDGENDATA 1
#include "../tahoevba/vdreg.h"
#undef VDGENDATA
#include "../tahoevba/vbaparam.h"
#include "saio.h"
#define NVD 4 /* Max number of controllers */
#define VDUNIT(x) (minor(x) & 0x3)
#define VDCTLR(x) (minor(x) >> 2)
fmt_mdcb mdcb;
fmt_dcb dcb;
/*
* Drive specific information.
*/
struct dkinfo {
char configured;
fs_tab info;
} dkinfo[NVD][16];
/*
* Controller specific information.
*/
struct vdinfo {
u_short vd_flags;
#define VDF_INIT 0x1 /* controller initialized */
#define VDF_BUSY 0x2 /* controller running */
u_short vd_type; /* smd or smde */
char *vd_name;
} vdinfo[NVD];
static char junk[1024];
#define VDADDR(ctlr) ((cdr *)vddcaddr[(ctlr)])
vdopen(io)
register struct iob *io;
{
register int ctlr = VDCTLR(io->i_unit), unit = VDUNIT(io->i_unit);
struct vdinfo *vd;
register int i, j;
/* Make sure controller number is in range */
if (ctlr >= NVD) {
printf(
"dk%d: invalid controller (only configured for %d vd's)\n",
io->i_unit, NVD);
_stop("");
}
/* check file system for validity */
if ((unsigned)io->i_boff >= 8) {
printf("dk%d: invalid partition number (%d)\n",
io->i_unit, io->i_boff);
_stop("");
}
vd = &vdinfo[ctlr];
if ((vd->vd_flags&VDF_INIT) == 0) {
vdinit(io); /* initialize controller/drive */
vd->vd_flags |= VDF_INIT;
for (j = 0; j < 16; j++)
dkinfo[ctlr][j].configured = 0;
}
if (!dkinfo[ctlr][unit].configured) {
vdconfigure_drive(io);
dkinfo[ctlr][unit].configured = 1;
}
io->i_boff = dkinfo[ctlr][unit].info.partition[io->i_boff].par_start;
}
vdinit(io)
register struct iob *io;
{
register int ctlr = VDCTLR(io->i_unit), unit = VDUNIT(io->i_unit);
register cdr *ctlr_addr = VDADDR(ctlr);
register struct vdinfo *vd = &vdinfo[ctlr];
/* Check to see if controller is really there */
if (badaddr(ctlr_addr, 2)) {
printf("dk%d: vd%d csr doesn't respond\n", io->i_unit, ctlr);
_stop("");
}
/* Probe further to find what kind of controller it is */
ctlr_addr->cdr_reset = 0xffffffff;
DELAY(1000000);
if (ctlr_addr->cdr_reset != 0xffffffff) {
vd->vd_type = SMDCTLR;
vd->vd_name = "smd";
DELAY(1000000);
} else {
vd->vd_type = SMD_ECTLR;
vd->vd_name = "smde";
ctlr_addr->cdr_reserved = 0x0;
DELAY(3000000);
}
if (vd->vd_type == SMD_ECTLR) {
ctlr_addr->cdr_csr = 0;
ctlr_addr->mdcb_tcf = AM_ENPDA;
ctlr_addr->dcb_tcf = AM_ENPDA;
ctlr_addr->trail_tcf = AM_ENPDA;
ctlr_addr->data_tcf = AM_ENPDA;
ctlr_addr->cdr_ccf = CCF_STS | XMD_32BIT | BSZ_16WRD |
CCF_ENP | CCF_EPE /* | CCF_EDE */ | CCF_ECE | CCF_ERR;
}
if (vdnotrailer(io, INIT, 8) & HRDERR) {
vdprint_error(io->i_unit, "init error",
dcb.operrsta, dcb.err_code);
_stop("");
}
if (vdnotrailer(io, DIAG, 8) & HRDERR) {
vdprint_error(io->i_unit, "diagnostic error",
dcb.operrsta, dcb.err_code);
_stop("");
}
}
vdconfigure_drive(io)
register struct iob *io;
{
register int ctlr = VDCTLR(io->i_unit), unit = VDUNIT(io->i_unit);
register fs_tab *file_sys;
register struct dkinfo *dk = &dkinfo[ctlr][unit];
dskadr daddr;
register int i;
for (i = 0; i < nvddrv; i++) {
dk->info = vdst[i];
if (vdinfo[ctlr].vd_type == SMDCTLR)
if (dk->info.nsec != 32)
continue;
vdconfigure(io, 0);
daddr.cylinder = dk->info.ncyl - 2;
daddr.track = dk->info.ntrak - 1;
daddr.sector = dk->info.nsec - 1;
io->i_ma = junk;
io->i_cc = dk->info.secsize;
if ((vdaccess(io, &daddr, RD) & HRDERR) == 0)
return;
}
printf("dk%d: unknown drive type\n", io->i_unit);
_stop("");
}
vdstart_drive(io)
register struct iob *io;
{
register int ctlr = VDCTLR(io->i_unit), unit = VDUNIT(io->i_unit);
int ounit = io->i_unit;
io->i_unit &= ~3;
if (vdnotrailer(io, VDSTART, 62) & HRDERR) {
vdprint_error(io->i_unit, "start error",
dcb.operrsta, dcb.err_code);
_stop("");
}
vdinfo[ctlr].vd_flags |= VDF_BUSY;
io->i_unit = ounit;
DELAY(62000000);
}
/*
* This routine actually configures a particular drive.
*
* If the controller is an SMD/E controller then the number of sectors per
* track is loaded into the appropriate register, otherwise it is left
* alone because the old SMD controller requires a constant 32 sectors
* per track for it's drives. (an error would be returned if the value is
* loaded.)
*/
vdconfigure(io, pass)
register struct iob *io;
int pass;
{
register int ctlr = VDCTLR(io->i_unit), unit = VDUNIT(io->i_unit);
register cdr *ctlr_addr = VDADDR(ctlr);
dcb.opcode = RSTCFG; /* command */
dcb.intflg = NOINT;
dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */
dcb.operrsta = 0;
dcb.devselect = (char)unit;
dcb.trail.rstrail.ncyl = dkinfo[ctlr][unit].info.ncyl;
dcb.trail.rstrail.nsurfaces = dkinfo[ctlr][unit].info.ntrak;
if (vdinfo[ctlr].vd_type == SMD_ECTLR) {
dcb.trailcnt = (char)5;
dcb.trail.rstrail.nsectors = dkinfo[ctlr][unit].info.nsec;
dcb.trail.rstrail.slip_sec = dkinfo[ctlr][unit].info.nslip;
dcb.trail.rstrail.recovery = 0x18f;
} else
dcb.trailcnt = (char)2;
mdcb.firstdcb = &dcb;
mdcb.vddcstat = 0;
VDDC_ATTENTION(ctlr_addr, &mdcb, vdinfo[ctlr].vd_type);
if (!vdpoll(ctlr_addr,&dcb,10,vdinfo[ctlr].vd_type)) {
if (pass == 0) {
VDDC_RESET(ctlr_addr, vdinfo[ctlr].vd_type);
vdconfigure(io, 1);
} else
_stop(" during drive configuration.\n");
}
if (dcb.operrsta & HRDERR) {
if (vdinfo[ctlr].vd_type == SMD_ECTLR &&
(ctlr_addr->cdr_status[io->i_unit] & STA_US) == 0) {
printf("dk%d: ", io->i_unit);
_stop("nonexistent drive");
}
if ((dcb.operrsta & (NOTCYLERR|DRVNRDY)) == 0) {
vdprint_error(io->i_unit, "configuration error",
dcb.operrsta, dcb.err_code);
_stop("");
}
if (pass == 0) {
vdstart_drive(io);
vdconfigure(io, 1);
}
}
}
/*
* Strategy is called to do the actual I/O to the disk drives.
*
* Some simple checks are made to make sure we don't do anything rediculous,
* If everything is sane then the request is issued.
*
* If no errors occured then the original byte count is returned,
* otherwise -1 is returned to indicate an error occured.
*/
vdstrategy(io, func)
register struct iob *io;
register int func;
{
dskadr daddr;
register int ctlr = VDCTLR(io->i_unit), unit = VDUNIT(io->i_unit);
register fs_tab *u_info = &dkinfo[ctlr][unit].info;
register int op = (func == READ) ? RD : WD;
register int blk;
if (io->i_cc == 0 || io->i_cc > 65535) {
printf("dk%d: invalid transfer size %d\n", io->i_unit,
io->i_cc);
_stop("");
}
blk = io->i_bn * DEV_BSIZE / u_info->secsize;
daddr.sector = blk % u_info->nsec;
daddr.track = (blk / u_info->nsec) % u_info->ntrak;
daddr.cylinder = (blk/u_info->nsec) / u_info->ntrak;
if (vdaccess(io, &daddr, op) & HRDERR) {
vdprint_error(io->i_unit, "i/o error", dcb.operrsta,
dcb.err_code);
io->i_error = EIO;
return (-1);
}
mtpr(PADC, 0);
return (io->i_cc);
}
vdprint_error(unit, str, status, smde_status)
int unit;
char *str;
u_long status;
u_long smde_status;
{
register struct vdstatus *sp;
printf("dk%d: %s; status %b", unit, str, status, ERRBITS);
if (smde_status)
printf(", code %x", smde_status);
printf("\n");
}
vdnotrailer(io, function, time)
register struct iob *io;
register int function, time;
{
register int ctlr = VDCTLR(io->i_unit);
register cdr *ctlr_addr = VDADDR(ctlr);
dcb.opcode = function; /* command */
dcb.intflg = NOINT;
dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */
dcb.operrsta = 0;
dcb.devselect = (char)VDUNIT(io->i_unit);
dcb.trailcnt = (char)0;
mdcb.firstdcb = &dcb;
mdcb.vddcstat = 0;
VDDC_ATTENTION(ctlr_addr, &mdcb, vdinfo[ctlr].vd_type);
if (!vdpoll(ctlr_addr, &dcb, time, vdinfo[ctlr].vd_type))
_stop(" during initialization operation.\n");
return dcb.operrsta;
}
vdaccess(io, daddr, func)
register struct iob *io;
dskadr *daddr;
int func;
{
register int ctlr = VDCTLR(io->i_unit);
register cdr *ctlr_addr = VDADDR(ctlr);
dcb.opcode = (short)func; /* format sector command */
dcb.intflg = NOINT;
dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */
dcb.operrsta = 0;
dcb.devselect = (char)VDUNIT(io->i_unit);
dcb.trailcnt = (char)(sizeof(trrw) / 4);
dcb.trail.rwtrail.memadr = io->i_ma;
dcb.trail.rwtrail.wcount = ((io->i_cc + 1) / sizeof(short));
dcb.trail.rwtrail.disk.cylinder = daddr->cylinder;
dcb.trail.rwtrail.disk.track = daddr->track;
dcb.trail.rwtrail.disk.sector = daddr->sector;
mdcb.firstdcb = &dcb;
mdcb.vddcstat = 0;
VDDC_ATTENTION(ctlr_addr, &mdcb, vdinfo[ctlr].vd_type);
if (!vdpoll(ctlr_addr, &dcb, 60, vdinfo[ctlr].vd_type))
_stop(" during i/o operation.\n");
return (dcb.operrsta);
}
/*
* Dump the MDCB and DCB for diagnostic purposes. This
* routine is called whenever a fatal error is encountered.
*/
vdprintdcb(ptr)
register long *ptr;
{
register long i, trailer_count;
printf("mdcb: %lx %lx %lx %lx\n", ptr[0], ptr[1], ptr[2], ptr[3]);
if (ptr = (long *)*ptr) {
printf("dcb:");
trailer_count = ptr[3] & 0xff;
for (i=0; i<7+trailer_count; i++) {
uncache(&ptr[i]);
printf(" %lx", ptr[i]);
}
}
printf("\n");
DELAY(5000000);
}
/*
* Poll controller until operation completes
* or timeout expires.
*/
vdpoll(addr, dcb, t, type)
register cdr *addr;
register fmt_dcb *dcb;
register int t, type;
{
t *= 1000;
for (;;) {
uncache(&dcb->operrsta);
if (dcb->operrsta & (DCBCMP|DCBABT))
break;
if (--t <= 0) {
printf("vd: controller timeout");
VDDC_ABORT(addr, type);
DELAY(30000);
uncache(&dcb->operrsta);
return (0);
}
DELAY(1000);
}
if (type == SMD_ECTLR) {
for (;;) {
uncache(&addr->cdr_csr);
if ((addr->cdr_csr & CS_GO) == 0)
break;
DELAY(50);
}
DELAY(300);
uncache(&dcb->err_code);
}
DELAY(200);
uncache(&dcb->operrsta);
return (1);
}