135473Sbostic /*
235473Sbostic * Copyright (c) 1988 The Regents of the University of California.
335473Sbostic * All rights reserved.
435473Sbostic *
535473Sbostic * This code is derived from software contributed to Berkeley by
635473Sbostic * Computer Consoles Inc.
735473Sbostic *
844532Sbostic * %sccs.include.redist.c%
935473Sbostic *
10*49096Sbostic * @(#)vd.c 7.18 (Berkeley) 05/04/91
1135473Sbostic */
1229567Ssam
1325873Ssam /*
1430521Ssam * Stand alone driver for the VDDC/SMDE controller
1529567Ssam */
1637505Smckusick #include "machine/mtpr.h"
1725873Ssam
1843457Sroot #include "sys/param.h"
1943457Sroot #include "sys/time.h"
2043457Sroot #include "sys/buf.h"
2143457Sroot #include "sys/disklabel.h"
2245796Sbostic #include "stand/saio.h"
2330521Ssam
2445796Sbostic #include "../vba/vdreg.h"
2545796Sbostic #include "../vba/vbaparam.h"
2625873Ssam
2730521Ssam #define COMPAT_42 1
2825873Ssam
2934466Sbostic #define NVD 4 /* controllers */
3030521Ssam #define NDRIVE 8 /* drives per controller */
3125873Ssam
3230521Ssam #define VDADDR(ctlr) ((struct vddevice *)vdaddrs[ctlr])
3330521Ssam long vdaddrs[NVD] = { 0xffff2000, 0xffff2100, 0xffff2200, 0xffff2300 };
3425873Ssam
3530521Ssam u_char vdinit[NVD]; /* controller initialized */
3630521Ssam u_char vdtype[NVD]; /* controller type */
3734466Sbostic u_char dkconfigured[NVD][NDRIVE]; /* unit configured */
3832604Skarels u_char dkflags[NVD][NDRIVE]; /* unit flags */
3925873Ssam
4034466Sbostic static struct disklabel dklabel[NVD][NDRIVE]; /* pack label */
4132604Skarels static struct mdcb mdcb;
4232604Skarels static struct dcb dcb;
4332604Skarels static char lbuf[DEV_BSIZE];
4425873Ssam
vdopen(io)4525873Ssam vdopen(io)
4629567Ssam register struct iob *io;
4725873Ssam {
4834466Sbostic register int ctlr = io->i_ctlr;
4930521Ssam register struct dkinfo *dk;
5030823Skarels register struct disklabel *lp, *dlp;
5130521Ssam int error;
5225873Ssam
5334466Sbostic if ((u_int)io->i_adapt)
5434466Sbostic return (EADAPT);
5534466Sbostic if ((u_int)ctlr >= NVD)
5634466Sbostic return (ECTLR);
5730521Ssam if (!vdinit[ctlr] && (error = vdreset_ctlr(ctlr, io->i_unit)))
5830521Ssam return (error);
5934466Sbostic lp = &dklabel[io->i_ctlr][io->i_unit];
6034466Sbostic if (!dkconfigured[io->i_ctlr][io->i_unit]) {
6130521Ssam struct iob tio;
6230521Ssam
6330521Ssam /*
6430521Ssam * Read in the pack label.
6530521Ssam */
6632604Skarels lp->d_secsize = 1024;
6732604Skarels lp->d_nsectors = 72;
6830521Ssam lp->d_ntracks = 24;
6930521Ssam lp->d_ncylinders = 711;
7032604Skarels lp->d_secpercyl = 72*24;
7130521Ssam if (!vdreset_drive(io))
7230521Ssam return (ENXIO);
7330521Ssam tio = *io;
7430521Ssam tio.i_bn = LABELSECTOR;
7530521Ssam tio.i_ma = lbuf;
7630521Ssam tio.i_cc = DEV_BSIZE;
7730521Ssam tio.i_flgs |= F_RDDATA;
78*49096Sbostic if (vdstrategy(&tio, F_READ) != DEV_BSIZE)
7934466Sbostic return (ERDLAB);
8030823Skarels dlp = (struct disklabel *)(lbuf + LABELOFFSET);
8134466Sbostic if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC)
8230521Ssam #ifdef COMPAT_42
8334466Sbostic {
8434466Sbostic printf("dk%d: unlabeled\n", io->i_unit);
8530521Ssam if (error = vdmaptype(io))
8630521Ssam return (error);
8734466Sbostic }
8830521Ssam #else
8934466Sbostic return (EUNLAB);
9030521Ssam #endif
9134466Sbostic else {
9230823Skarels *lp = *dlp;
9332604Skarels if (!vdreset_drive(io))
9432604Skarels return (ENXIO);
9532604Skarels }
9634466Sbostic dkconfigured[io->i_ctlr][io->i_unit] = 1;
9725873Ssam }
9834466Sbostic if (io->i_part < 0 || io->i_part >= lp->d_npartitions ||
9934466Sbostic lp->d_partitions[io->i_part].p_size == 0)
10034466Sbostic return (EPART);
10130521Ssam io->i_boff =
10234466Sbostic (lp->d_partitions[io->i_part].p_offset * lp->d_secsize) / DEV_BSIZE;
10330521Ssam return (0);
10425873Ssam }
10525873Ssam
10630521Ssam /*
10730521Ssam * Reset and initialize the controller.
10830521Ssam */
vdreset_ctlr(ctlr,unit)10930521Ssam vdreset_ctlr(ctlr, unit)
11030521Ssam register int ctlr, unit;
11125873Ssam {
11230521Ssam register int i;
11330521Ssam register struct vddevice *vdaddr = VDADDR(ctlr);
11425873Ssam
11530521Ssam if (badaddr(vdaddr, 2)) {
11630521Ssam printf("vd%d: %x: invalid csr\n", ctlr, vdaddr);
11730521Ssam return (ENXIO);
11825873Ssam }
11930521Ssam /* probe further to find what kind of controller it is */
12030521Ssam vdaddr->vdreset = 0xffffffff;
12125873Ssam DELAY(1000000);
12230521Ssam if (vdaddr->vdreset != 0xffffffff) {
12330521Ssam vdtype[ctlr] = VDTYPE_VDDC;
12425873Ssam DELAY(1000000);
12529567Ssam } else {
12630521Ssam vdtype[ctlr] = VDTYPE_SMDE;
12730521Ssam vdaddr->vdrstclr = 0;
12825873Ssam DELAY(3000000);
12930521Ssam vdaddr->vdcsr = 0;
13030521Ssam vdaddr->vdtcf_mdcb = AM_ENPDA;
13130521Ssam vdaddr->vdtcf_dcb = AM_ENPDA;
13230521Ssam vdaddr->vdtcf_trail = AM_ENPDA;
13330521Ssam vdaddr->vdtcf_data = AM_ENPDA;
13435711Sbostic vdaddr->vdccf = CCF_SEN | CCF_DIU | CCF_STS |
13530521Ssam XMD_32BIT | BSZ_16WRD |
13630521Ssam CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR;
13725873Ssam }
13830521Ssam if (!vdcmd(ctlr, 0, VDOP_INIT, 10) ||
13930521Ssam !vdcmd(ctlr, 0, VDOP_DIAG, 10)) {
14030521Ssam vderror(unit, dcb.opcode == VDOP_INIT ? "init" : "diag", &dcb);
14130521Ssam return (EIO);
14225873Ssam }
14330521Ssam vdinit[ctlr] = 1;
14434466Sbostic for (i = NDRIVE - 1; i >= 0; i--)
14534466Sbostic dkconfigured[ctlr][i] = 0;
14630521Ssam return (0);
14725873Ssam }
14825873Ssam
14930521Ssam /*
15030521Ssam * Reset and configure a drive's parameters.
15130521Ssam */
vdreset_drive(io)15230521Ssam vdreset_drive(io)
15329567Ssam register struct iob *io;
15425873Ssam {
15534466Sbostic register int ctlr = io->i_ctlr, slave = io->i_unit;
15634466Sbostic register struct disklabel *lp = &dklabel[io->i_ctlr][io->i_unit];
15730521Ssam register struct vddevice *vdaddr = VDADDR(ctlr);
15830521Ssam int pass = 0, type = vdtype[ctlr], error;
15932604Skarels int devflags = dkflags[ctlr][slave]; /* starts with 0 */
16025873Ssam
16130521Ssam again:
16230521Ssam dcb.opcode = VDOP_CONFIG; /* command */
16330521Ssam dcb.intflg = DCBINT_NONE;
16430521Ssam dcb.nxtdcb = (struct dcb *)0; /* end of chain */
16530521Ssam dcb.operrsta = 0;
16632604Skarels dcb.devselect = slave | devflags;
16730521Ssam dcb.trail.rstrail.ncyl = lp->d_ncylinders;
16830521Ssam dcb.trail.rstrail.nsurfaces = lp->d_ntracks;
16930521Ssam if (type == VDTYPE_SMDE) {
17030823Skarels dcb.trailcnt = sizeof (struct treset) / sizeof (long);
17130521Ssam dcb.trail.rstrail.nsectors = lp->d_nsectors;
17232604Skarels dcb.trail.rstrail.slip_sec = lp->d_trackskew;
17332604Skarels dcb.trail.rstrail.recovery = VDRF_NORMAL;
17430521Ssam } else
17530521Ssam dcb.trailcnt = 2; /* XXX */
17630521Ssam mdcb.mdcb_head = &dcb;
17730521Ssam mdcb.mdcb_status = 0;
17830521Ssam VDGO(vdaddr, (u_long)&mdcb, type);
17930521Ssam if (!vdpoll(vdaddr, &dcb, 10, type)) {
18030521Ssam if (pass++ != 0) {
18130521Ssam printf(" during drive configuration.\n");
18230521Ssam return (0);
18330521Ssam }
18430521Ssam VDRESET(vdaddr, type);
18530521Ssam if (error = vdreset_ctlr(ctlr, io->i_unit))
18630521Ssam return (error);
18730521Ssam goto again;
18825873Ssam }
18932604Skarels if ((dcb.operrsta & VDERR_HARD) == 0) { /* success */
19032604Skarels dkflags[ctlr][slave] = devflags;
19130521Ssam return (1);
19232604Skarels }
19332604Skarels if (devflags == 0) {
19432604Skarels devflags = VD_ESDI;
19532604Skarels goto again;
19632604Skarels }
19730521Ssam if (type == VDTYPE_SMDE && (vdaddr->vdstatus[slave] & STA_US) == 0) {
19830521Ssam printf("dk%d: nonexistent drive\n", io->i_unit);
19930521Ssam return (0);
20025873Ssam }
20130521Ssam if ((dcb.operrsta & (DCBS_OCYL|DCBS_NRDY)) == 0) {
20230521Ssam vderror(io->i_unit, "config", &dcb);
20330521Ssam return (0);
20430521Ssam }
20532604Skarels devflags = 0;
20630521Ssam if (pass++) /* give up */
20730521Ssam return (0);
20830521Ssam /*
20930521Ssam * Try to spin up drive with remote command.
21030521Ssam */
21130521Ssam if (!vdcmd(ctlr, 0, VDOP_START, 62)) {
21230521Ssam vderror(io->i_unit, "start", &dcb);
21330521Ssam return (0);
21430521Ssam }
21529984Skarels DELAY(62000000);
21630521Ssam goto again;
21725873Ssam }
21825873Ssam
vdcmd(ctlr,unit,cmd,time)21930521Ssam vdcmd(ctlr, unit, cmd, time)
22030521Ssam register int ctlr;
22130521Ssam int unit, cmd, time;
22225873Ssam {
22330521Ssam register struct vddevice *vdaddr = VDADDR(ctlr);
22425873Ssam
22530521Ssam dcb.opcode = cmd;
22630521Ssam dcb.intflg = DCBINT_NONE;
22730521Ssam dcb.nxtdcb = (struct dcb *)0; /* end of chain */
22825873Ssam dcb.operrsta = 0;
22932604Skarels dcb.devselect = unit | dkflags[ctlr][unit];
23030521Ssam dcb.trailcnt = 0;
23130521Ssam mdcb.mdcb_head = &dcb;
23230521Ssam mdcb.mdcb_status = 0;
23330521Ssam VDGO(vdaddr, (u_long)&mdcb, vdtype[ctlr]);
23430521Ssam if (!vdpoll(vdaddr, &dcb, time, vdtype[ctlr]))
23530521Ssam _stop(" during initialization operation.\n");
23630521Ssam return ((dcb.operrsta & VDERR_HARD) == 0);
23725873Ssam }
23825873Ssam
vdstrategy(io,cmd)23930521Ssam vdstrategy(io, cmd)
24029567Ssam register struct iob *io;
24130521Ssam int cmd;
24225873Ssam {
24330521Ssam register struct disklabel *lp;
24432604Skarels int ctlr, cn, tn, sn, slave, retries = 0;
24530521Ssam daddr_t bn;
24630521Ssam struct vddevice *vdaddr;
24725873Ssam
24829567Ssam if (io->i_cc == 0 || io->i_cc > 65535) {
24929567Ssam printf("dk%d: invalid transfer size %d\n", io->i_unit,
25029567Ssam io->i_cc);
25130521Ssam io->i_error = EIO;
25230521Ssam return (-1);
25329567Ssam }
25434466Sbostic lp = &dklabel[io->i_ctlr][io->i_unit];
25530521Ssam bn = io->i_bn * (DEV_BSIZE / lp->d_secsize);
25630521Ssam cn = bn / lp->d_secpercyl;
25730521Ssam sn = bn % lp->d_secpercyl;
25830521Ssam tn = sn / lp->d_nsectors;
25930521Ssam sn = sn % lp->d_nsectors;
26030521Ssam
26132604Skarels top:
262*49096Sbostic dcb.opcode = (cmd == F_READ ? VDOP_RD : VDOP_WD);
26330521Ssam dcb.intflg = DCBINT_NONE;
26430521Ssam dcb.nxtdcb = (struct dcb *)0; /* end of chain */
26530521Ssam dcb.operrsta = 0;
26634466Sbostic ctlr = io->i_ctlr;
26734466Sbostic slave = io->i_unit;
26832604Skarels dcb.devselect = slave | dkflags[ctlr][slave];
26930823Skarels dcb.trailcnt = sizeof (struct trrw) / sizeof (int);
27030823Skarels dcb.trail.rwtrail.memadr = (u_long)io->i_ma;
27130521Ssam dcb.trail.rwtrail.wcount = (io->i_cc + 1) / sizeof (short);
27230521Ssam dcb.trail.rwtrail.disk.cylinder = cn;
27330521Ssam dcb.trail.rwtrail.disk.track = tn;
27430521Ssam dcb.trail.rwtrail.disk.sector = sn;
27530521Ssam mdcb.mdcb_head = &dcb;
27630521Ssam mdcb.mdcb_status = 0;
27730521Ssam vdaddr = VDADDR(ctlr);
27830521Ssam VDGO(vdaddr, (u_long)&mdcb, vdtype[ctlr]);
27930521Ssam if (!vdpoll(vdaddr, &dcb, 60, vdtype[ctlr]))
28030521Ssam _stop(" during i/o operation.\n");
28130521Ssam if (dcb.operrsta & VDERR_HARD) {
28232604Skarels if (retries++ == 0 && vdreset_ctlr(ctlr, io->i_unit) == 0 &&
28332604Skarels vdreset_drive(io))
28432604Skarels goto top;
285*49096Sbostic vderror(io->i_unit, cmd == F_READ ? "read" : "write", &dcb);
28630312Ssam io->i_error = EIO;
28729567Ssam return (-1);
28825873Ssam }
28925873Ssam mtpr(PADC, 0);
29029567Ssam return (io->i_cc);
29125873Ssam }
29225873Ssam
vderror(unit,cmd,dcb)29330521Ssam vderror(unit, cmd, dcb)
29429567Ssam int unit;
29530521Ssam char *cmd;
29630521Ssam struct dcb *dcb;
29725873Ssam {
29829567Ssam
29930521Ssam printf("dk%d: %s error; status %b", unit, cmd,
30030521Ssam dcb->operrsta, VDERRBITS);
30130521Ssam if (dcb->err_code)
30230521Ssam printf(", code %x", dcb->err_code);
30325873Ssam printf("\n");
30425873Ssam }
30525873Ssam
30625873Ssam /*
30730521Ssam * Poll controller until operation
30830521Ssam * completes or timeout expires.
30929567Ssam */
vdpoll(vdaddr,dcb,t,type)31030521Ssam vdpoll(vdaddr, dcb, t, type)
31130521Ssam register struct vddevice *vdaddr;
31230521Ssam register struct dcb *dcb;
31325931Ssam register int t, type;
31425931Ssam {
31525931Ssam
31625931Ssam t *= 1000;
31730312Ssam for (;;) {
31825931Ssam uncache(&dcb->operrsta);
31930521Ssam if (dcb->operrsta & (DCBS_DONE|DCBS_ABORT))
32030312Ssam break;
32125931Ssam if (--t <= 0) {
32225931Ssam printf("vd: controller timeout");
32330521Ssam VDABORT(vdaddr, type);
32425931Ssam DELAY(30000);
32525931Ssam uncache(&dcb->operrsta);
32625931Ssam return (0);
32725931Ssam }
32830312Ssam DELAY(1000);
32925931Ssam }
33030521Ssam if (type == VDTYPE_SMDE) {
33130312Ssam for (;;) {
33230521Ssam uncache(&vdaddr->vdcsr);
33330521Ssam if ((vdaddr->vdcsr & CS_GO) == 0)
33430312Ssam break;
33525931Ssam DELAY(50);
33625931Ssam }
33725931Ssam DELAY(300);
33829984Skarels uncache(&dcb->err_code);
33925931Ssam }
34025931Ssam DELAY(200);
34125931Ssam uncache(&dcb->operrsta);
34225931Ssam return (1);
34325931Ssam }
34430521Ssam
34530521Ssam #ifdef COMPAT_42
34630521Ssam struct dkcompat {
34730521Ssam int nsectors; /* sectors per track */
34830521Ssam int ntracks; /* tracks per cylinder */
34930521Ssam int ncylinders; /* cylinders per drive */
35032604Skarels int secsize; /* sector size */
35130521Ssam #define NPART 2
35230521Ssam int poff[NPART]; /* [a+b] for bootstrapping */
35330521Ssam } dkcompat[] = {
35434738Sbostic { 64, 20, 842, 512, 0, 61440 }, /* 2361a eagle */
35532604Skarels { 48, 24, 711, 512, 0, 61056 }, /* xsd */
35632604Skarels { 44, 20, 842, 512, 0, 52800 }, /* eagle */
35732604Skarels { 64, 10, 823, 512, 0, 38400 }, /* fuji 360 */
35832604Skarels { 32, 24, 711, 512, 0, 40704 }, /* xfd */
35932604Skarels { 32, 19, 823, 512, 0, 40128 }, /* smd */
36032604Skarels { 32, 10, 823, 512, 0, 19200 }, /* fsd */
36132604Skarels { 18, 15, 1224, 1024, 0, 21600 }, /* mxd */
36230521Ssam };
36330521Ssam #define NDKCOMPAT (sizeof (dkcompat) / sizeof (dkcompat[0]))
36430521Ssam
36530521Ssam /*
36630521Ssam * Identify and configure drive from above table
36730521Ssam * by trying to read the last sector until a description
36830521Ssam * is found for which we're successful.
36930521Ssam */
37030521Ssam vdmaptype(io)
37130521Ssam struct iob *io;
37230521Ssam {
37334466Sbostic register struct disklabel *lp = &dklabel[io->i_ctlr][io->i_unit];
37430521Ssam register struct dkcompat *dp;
37532604Skarels int i, ctlr, slave, type;
37630521Ssam struct vddevice *vdaddr;
37730521Ssam
37834466Sbostic ctlr = io->i_ctlr;
37934466Sbostic slave = io->i_unit;
38030521Ssam vdaddr = VDADDR(ctlr);
38130521Ssam type = vdtype[ctlr];
38230521Ssam for (dp = dkcompat; dp < &dkcompat[NDKCOMPAT]; dp++) {
38330521Ssam if (type == VDTYPE_VDDC && dp->nsectors != 32)
38430521Ssam continue;
38530521Ssam lp->d_nsectors = dp->nsectors;
38630521Ssam lp->d_ntracks = dp->ntracks;
38730521Ssam lp->d_ncylinders = dp->ncylinders;
38832604Skarels lp->d_secsize = dp->secsize;
38930521Ssam if (!vdreset_drive(io)) /* set drive parameters */
39030521Ssam return (EIO);
39130521Ssam dcb.opcode = VDOP_RD;
39230521Ssam dcb.intflg = DCBINT_NONE;
39330521Ssam dcb.nxtdcb = (struct dcb *)0; /* end of chain */
39432604Skarels dcb.devselect = slave | dkflags[ctlr][slave];
39530521Ssam dcb.operrsta = 0;
39630823Skarels dcb.trailcnt = sizeof (struct trrw) / sizeof (long);
39730823Skarels dcb.trail.rwtrail.memadr = (u_long)lbuf;
39832604Skarels dcb.trail.rwtrail.wcount = lp->d_secsize / sizeof (short);
39930521Ssam dcb.trail.rwtrail.disk.cylinder = dp->ncylinders - 2;
40030521Ssam dcb.trail.rwtrail.disk.track = dp->ntracks - 1;
40130521Ssam dcb.trail.rwtrail.disk.sector = dp->nsectors - 1;
40230521Ssam mdcb.mdcb_head = &dcb;
40330521Ssam mdcb.mdcb_status = 0;
40430521Ssam VDGO(vdaddr, (u_long)&mdcb, type);
40530521Ssam if (!vdpoll(vdaddr, &dcb, 60, type))
40630521Ssam _stop(" during i/o operation.\n");
40730521Ssam if (dcb.operrsta & VDERR_HARD)
40830521Ssam continue;
40930521Ssam /* simulate necessary parts of disk label */
41030521Ssam lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
41130521Ssam lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
41230521Ssam lp->d_npartitions = NPART;
41330521Ssam for (i = 0; i < NPART; i++) {
41430521Ssam lp->d_partitions[i].p_offset = dp->poff[i];
41530521Ssam lp->d_partitions[i].p_size =
41630521Ssam lp->d_secperunit - dp->poff[i];
41730521Ssam }
41830521Ssam return (0);
41930521Ssam }
42030521Ssam printf("dk%d: unknown drive type\n", io->i_unit);
42130521Ssam return (ENXIO);
42230521Ssam }
42330521Ssam #endif
424