1*25860Ssam /* vd.c 1.4 86/01/12 */ 224004Ssam 324004Ssam #include "fsd.h" 424004Ssam #if NVD > 0 524004Ssam /* 625675Ssam * VDDC - Versabus SMD/ESMD driver. 725675Ssam */ 825675Ssam #include "../tahoe/mtpr.h" 925675Ssam #include "../tahoe/pte.h" 1024004Ssam 1125675Ssam #include "param.h" 1225675Ssam #include "buf.h" 1325675Ssam #include "cmap.h" 1425675Ssam #include "conf.h" 1525675Ssam #include "dir.h" 1625675Ssam #include "dk.h" 1725675Ssam #include "map.h" 1825675Ssam #include "systm.h" 1925675Ssam #include "user.h" 2025675Ssam #include "vmmac.h" 2125675Ssam #include "proc.h" 2225675Ssam #include "uio.h" 2324004Ssam 2425675Ssam #include "../tahoevba/vbavar.h" 2525675Ssam #define VDGENDATA 2625675Ssam #include "../tahoevba/vddcreg.h" 2725675Ssam #undef VDGENDATA 2824004Ssam 2925675Ssam #define MAX_BLOCKSIZE (MAXBPTE*NBPG) 3025675Ssam #define DUMPSIZE 64 /* controller limit */ 3124004Ssam 3224004Ssam #define VDUNIT(x) (minor(x) >> 3) 3325675Ssam #define FILSYS(x) (minor(x) & 0x07) 3425675Ssam #define PHYS(x) (vtoph((struct proc *)0, (unsigned)(x))) 3525675Ssam #define TRUE 1 3625675Ssam #define FALSE 0 3724004Ssam 3825675Ssam #define CTLR_ERROR 1 3925675Ssam #define DRIVE_ERROR 2 4025675Ssam #define HARD_DATA_ERROR 3 4125675Ssam #define SOFT_DATA_ERROR 4 4224004Ssam 4325675Ssam #define b_cylin b_resid 4425675Ssam #define b_daddr b_error 4524004Ssam 4624004Ssam struct vba_ctlr *vdminfo[NVD]; 4724004Ssam struct vba_device *vddinfo[NFSD]; 4825675Ssam int vdprobe(), vdslave(), vdattach(), vddgo(); 4925675Ssam struct vba_driver vddriver = 5025675Ssam { vdprobe, vdslave, vdattach, vddgo, vddcaddr, "smd/fsd", 5125675Ssam vddinfo, "vd", vdminfo }; 5224004Ssam 5324004Ssam /* 5425675Ssam * Per-drive state. 5525675Ssam */ 5625675Ssam typedef struct { 5725675Ssam struct buf raw_q_element; 5825675Ssam short sec_per_blk; 5925675Ssam short sec_per_cyl; 6025675Ssam char status; 6125675Ssam struct buf xfer_queue; 6225675Ssam int drive_type; 6325675Ssam fs_tab info; 6425675Ssam } unit_tab; 6524004Ssam 6624004Ssam /* 6725675Ssam * Per-controller state. 6825675Ssam */ 6925675Ssam typedef struct { 7025675Ssam char ctlr_type; /* controller type */ 7125675Ssam char *map; /* i/o page map */ 7225675Ssam char *utl; /* mapped i/o space */ 7325675Ssam u_int cur_slave:8; /* last active unit number */ 7425675Ssam u_int int_expected:1; /* expect an interupt */ 7525675Ssam u_int ctlr_started:1; /* start command was issued */ 7625675Ssam u_int overlap_seeks:1;/* should overlap seeks */ 7725675Ssam u_int off_cylinder:16;/* off cylinder bit map */ 7825675Ssam u_int unit_type[16]; /* slave types */ 7925675Ssam u_int cur_cyl[16]; /* cylinder last selected */ 8025675Ssam long cur_trk[16]; /* track last selected */ 8125675Ssam fmt_mdcb ctlr_mdcb; /* controller mdcb */ 8225675Ssam fmt_dcb ctlr_dcb; /* r/w dcb */ 8325675Ssam fmt_dcb seek_dcb[4]; /* dcbs for overlapped seeks */ 8425675Ssam /* buffer for raw/swap i/o */ 8525675Ssam char rawbuf[MAX_BLOCKSIZE]; 8625675Ssam } ctlr_tab; 8724004Ssam 8825675Ssam extern char vd0utl[]; 8925675Ssam #if NVD > 1 9025675Ssam extern char vd1utl[]; 9125675Ssam #endif 9225675Ssam #if NVD > 2 9325675Ssam extern char vd2utl[]; 9425675Ssam #endif 9525675Ssam #if NVD > 3 9625675Ssam extern char vd3utl[]; 9725675Ssam #endif 9824004Ssam 9925675Ssam #define VDCINIT(map, utl) { \ 10025675Ssam UNKNOWN, (char *)map, utl, 0, FALSE, FALSE, TRUE, 0, \ 10125675Ssam { UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN, \ 10225675Ssam UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN } \ 10325675Ssam } 10425675Ssam ctlr_tab vdctlr_info[NVD] = { 10525675Ssam VDCINIT(VD0map, vd0utl), 10625675Ssam #if NVD > 1 10725675Ssam VDCINIT(VD1map, vd1utl), 10825675Ssam #endif 10925675Ssam #if NVD > 2 11025675Ssam VDCINIT(VD2map, vd2utl), 11125675Ssam #endif 11225675Ssam #if NVD > 3 11325675Ssam VDCINIT(VD3map, vd3utl), 11425675Ssam #endif 11524004Ssam }; 11624004Ssam 11725675Ssam unit_tab vdunit_info[NFSD]; 11824004Ssam 11924004Ssam /* 12025675Ssam * See if the controller is really there; if so, initialize it. 12125675Ssam */ 12225857Ssam vdprobe(reg, vm) 12325857Ssam caddr_t reg; 12425857Ssam struct vba_ctlr *vm; 12525675Ssam { 12625857Ssam register br, cvec; /* must be r12, r11 */ 12725857Ssam register cdr *cp = (cdr *)reg; 12825857Ssam 12925857Ssam if (badaddr((caddr_t)reg, 2)) 13025675Ssam return (0); 13125857Ssam cp->cdr_reset = 0xffffffff; 13225675Ssam DELAY(1000000); 13325857Ssam if (cp->cdr_reset != (unsigned)0xffffffff) { 13425675Ssam DELAY(1000000); 13525675Ssam } else { 13625857Ssam cp->cdr_reserved = 0x0; 13725675Ssam DELAY(3000000); 13825675Ssam } 13925857Ssam br = 0x17, cvec = 0xe0 + vm->um_ctlr; /* XXX */ 14025857Ssam return (sizeof (*cp)); 14125675Ssam } 14224004Ssam 14324004Ssam /* 14425675Ssam * See if a drive is really there 14525675Ssam * Try to reset/configure the drive, then test its status. 14625675Ssam */ 14725675Ssam vdslave(vi, addr) 14825675Ssam register struct vba_device *vi; 14925675Ssam register cdr *addr; 15025675Ssam { 15125675Ssam register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr]; 15225675Ssam register unit_tab *ui = &vdunit_info[vi->ui_unit]; 15325675Ssam register fmt_mdcb *mdcb = &ci->ctlr_mdcb; 15425675Ssam register fmt_dcb *dcb = &ci->ctlr_dcb; 15525675Ssam register int type; 15624004Ssam 15725675Ssam if (ci->ctlr_type == UNKNOWN) { 15825675Ssam addr->cdr_reset = 0xffffffff; 15925675Ssam DELAY(1000000); 16025675Ssam if (addr->cdr_reset != (unsigned)0xffffffff) { 16125675Ssam ci->ctlr_type = SMDCTLR; 16225675Ssam ci->overlap_seeks = 0; 16325675Ssam DELAY(1000000); 16425675Ssam } else { 16525675Ssam ci->overlap_seeks = 1; 16625675Ssam ci->ctlr_type = SMD_ECTLR; 16725675Ssam addr->cdr_reserved = 0x0; 16825675Ssam DELAY(3000000); 16925675Ssam addr->cdr_csr = 0; 17025675Ssam addr->mdcb_tcf = AM_ENPDA; 17125675Ssam addr->dcb_tcf = AM_ENPDA; 17225675Ssam addr->trail_tcf = AM_ENPDA; 17325675Ssam addr->data_tcf = AM_ENPDA; 17425675Ssam addr->cdr_ccf = CCF_STS | XMD_32BIT | BSZ_16WRD | 17525675Ssam CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR; 17625675Ssam } 177*25860Ssam printf("vd%d: %s controller\n", vi->ui_unit, 178*25860Ssam ci->ctlr_type == SMDCTLR ? "smd" : "xsmd"); 17925675Ssam if (vdnotrailer(addr, 18025675Ssam vi->ui_ctlr, vi->ui_slave, INIT, 10) & HRDERR) { 18125675Ssam printf("vd%d: init error\n", vi->ui_unit); 18225675Ssam return (0); 18325675Ssam } 18425675Ssam if (vdnotrailer(addr, 18525675Ssam vi->ui_ctlr, vi->ui_slave, DIAG, 10) & HRDERR) { 18625675Ssam printf("vd%d: diagnostic error\n", vi->ui_unit); 18725675Ssam return (0); 18825675Ssam } 18925675Ssam } 19025675Ssam /* 19125675Ssam * Seek on all drive types starting from the largest one. 19225675Ssam * a successful seek to the last sector/cylinder/track verifies 19325675Ssam * the drive type connected to this port. 19425675Ssam */ 19525675Ssam for (type = 0; type < nvddrv; type++) { 19625675Ssam /* XXX */ 19725675Ssam if (ci->ctlr_type == SMDCTLR && vdst[type].nsec != 32) 19825675Ssam continue; 19925675Ssam /* XXX */ 20025675Ssam if (!vdconfigure_drive(addr, vi->ui_ctlr, vi->ui_slave, type,0)) 20125675Ssam return (0); 20225675Ssam dcb->opcode = (short)RD; 20325675Ssam dcb->intflg = NOINT; 20425675Ssam dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */ 20525675Ssam dcb->operrsta = 0; 20625675Ssam dcb->devselect = (char)(vi->ui_slave); 20725675Ssam dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long)); 20825675Ssam dcb->trail.rwtrail.memadr = (char *)PHYS(ci->rawbuf); 20925675Ssam dcb->trail.rwtrail.wcount = vdst[type].secsize/sizeof(short); 21025675Ssam dcb->trail.rwtrail.disk.cylinder = vdst[type].ncyl - 2; 21125675Ssam dcb->trail.rwtrail.disk.track = vdst[type].ntrak - 1; 21225675Ssam dcb->trail.rwtrail.disk.sector = vdst[type].nsec - 1; 21325675Ssam mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); 21425675Ssam mdcb->vddcstat = 0; 21525675Ssam VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(mdcb)), ci->ctlr_type); 21625675Ssam POLLTILLDONE(addr, dcb, 60, ci->ctlr_type); 21725675Ssam if (vdtimeout <= 0) 21825675Ssam printf(" during probe\n"); 21925675Ssam if ((dcb->operrsta&HRDERR) == 0) 22025675Ssam break; 22125675Ssam } 22225675Ssam if (type >= nvddrv) { 22325675Ssam /* 22425675Ssam * If reached here, a drive which is not defined in the 22525675Ssam * 'vdst' tables is connected. Cannot set it's type. 22625675Ssam */ 22725675Ssam printf("vd%d: unknown drive type\n", vi->ui_unit); 22825675Ssam return (0); 22925675Ssam } 23025675Ssam ui->drive_type = type; 23125675Ssam ui->info = vdst[type]; 23225675Ssam ui->sec_per_blk = DEV_BSIZE / ui->info.secsize; 23325675Ssam vi->ui_type = type; 23425675Ssam vi->ui_dk = 1; 23525675Ssam vddriver.ud_dname = ui->info.type_name; 23625675Ssam return (1); 23724004Ssam } 23824004Ssam 23925675Ssam vdconfigure_drive(addr, ctlr, slave, type, pass) 24025675Ssam register cdr *addr; 24125675Ssam int ctlr, slave, type, pass; 24224004Ssam { 24325675Ssam register ctlr_tab *ci = &vdctlr_info[ctlr]; 24425675Ssam 24525675Ssam ci->ctlr_dcb.opcode = RSTCFG; /* command */ 24625675Ssam ci->ctlr_dcb.intflg = NOINT; 24725675Ssam ci->ctlr_dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */ 24825675Ssam ci->ctlr_dcb.operrsta = 0; 24925675Ssam ci->ctlr_dcb.devselect = (char)slave; 25025675Ssam ci->ctlr_dcb.trail.rstrail.ncyl = vdst[type].ncyl; 25125675Ssam ci->ctlr_dcb.trail.rstrail.nsurfaces = vdst[type].ntrak; 25225675Ssam if (ci->ctlr_type == SMD_ECTLR) { 25325675Ssam ci->ctlr_dcb.trailcnt = (char)4; 25425675Ssam ci->ctlr_dcb.trail.rstrail.nsectors = vdst[type].nsec; 25525675Ssam ci->ctlr_dcb.trail.rstrail.slip_sec = vdst[type].nslip; 25625675Ssam } else 25725675Ssam ci->ctlr_dcb.trailcnt = (char)2; 25825675Ssam ci->ctlr_mdcb.firstdcb = (fmt_dcb *)(PHYS(&ci->ctlr_dcb)); 25925675Ssam ci->ctlr_mdcb.vddcstat = 0; 26025675Ssam VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(&ci->ctlr_mdcb)), ci->ctlr_type); 26125675Ssam POLLTILLDONE(addr, &ci->ctlr_dcb, 5, ci->ctlr_type); 26225675Ssam if (vdtimeout <= 0) { 26325675Ssam printf(" during config\n"); 26425675Ssam return (0); 26525675Ssam } 26625675Ssam if (ci->ctlr_dcb.operrsta & HRDERR) { 26725675Ssam if ((ci->ctlr_dcb.operrsta & (NOTCYLERR|DRVNRDY)) == 0) 26825675Ssam printf("vd%d: drive %d: config error\n", ctlr, slave); 26925675Ssam else if (pass == 0) { 27025675Ssam vdstart_drive(addr, ctlr, slave); 27125675Ssam return (vdconfigure_drive(addr, ctlr, slave, type, 1)); 27225675Ssam } else if (pass == 2) 27325675Ssam return (vdconfigure_drive(addr, ctlr, slave, type, 3)); 27425675Ssam return (0); 27525675Ssam } 27625675Ssam return (1); 27724004Ssam } 27824004Ssam 27925675Ssam vdstart_drive(addr, ctlr, slave) 28025675Ssam cdr *addr; 28125675Ssam register int ctlr, slave; 28224004Ssam { 28325675Ssam int error = 0; 28424004Ssam 28525675Ssam printf("vd%d: starting drive %d, wait...", ctlr, slave); 28625675Ssam if (vdctlr_info[ctlr].ctlr_started) { 28725675Ssam printf("DELAY(5500000)..."); 28825675Ssam DELAY(5500000); 28925675Ssam goto done; 29024004Ssam } 29125675Ssam vdctlr_info[ctlr].ctlr_started = 1; 29225675Ssam error = vdnotrailer(addr, ctlr, 0, VDSTART, (slave*6)+62) & HRDERR; 29325675Ssam if (!error) { 29425675Ssam printf("DELAY(%d)...", (slave * 5500000) + 62000000); 29525675Ssam DELAY((slave * 5500000) + 62000000); 29624004Ssam } 29725675Ssam done: 29825675Ssam printf("\n"); 29925675Ssam return (error == 0); 30025675Ssam } 30124004Ssam 30225675Ssam vdnotrailer(addr, ctlr, unit, function, time) 30325675Ssam register cdr *addr; 30425675Ssam int ctlr, unit, function, time; 30524004Ssam { 30625675Ssam fmt_mdcb *mdcb = &vdctlr_info[ctlr].ctlr_mdcb; 30725675Ssam fmt_dcb *dcb = &vdctlr_info[ctlr].ctlr_dcb; 30825675Ssam int type = vdctlr_info[ctlr].ctlr_type; 30924004Ssam 31025675Ssam dcb->opcode = function; /* command */ 31124004Ssam dcb->intflg = NOINT; 31225675Ssam dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */ 31325675Ssam dcb->operrsta = 0; 31425675Ssam dcb->devselect = (char)unit; 31524004Ssam dcb->trailcnt = (char)0; 31625675Ssam mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); 31724004Ssam mdcb->vddcstat = 0; 31825675Ssam VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(mdcb)), type); 31925675Ssam POLLTILLDONE(addr, dcb, time, type); 32025675Ssam if (vdtimeout <= 0) { 32125675Ssam printf(" during init\n"); 32225675Ssam return (DCBCMP|ANYERR|HRDERR|OPABRT); 32324004Ssam } 32425675Ssam return (dcb->operrsta); 32525675Ssam } 32624004Ssam 32725675Ssam vdattach(vi) 32825675Ssam register struct vba_device *vi; 32925675Ssam { 33025675Ssam register unit_tab *ui = &vdunit_info[vi->ui_unit]; 33125675Ssam register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr]; 33225675Ssam register struct buf *cq = &vi->ui_mi->um_tab; 33325675Ssam register struct buf *uq = cq->b_forw; 33425675Ssam register struct buf *start_queue = uq; 33525675Ssam register fs_tab *fs = &ui->info; 33625675Ssam 33725675Ssam ui->info = vdst[vi->ui_type]; 33825675Ssam ui->sec_per_blk = DEV_BSIZE / ui->info.secsize; 33925675Ssam ui->sec_per_cyl = ui->info.nsec * ui->info.ntrak; 34025675Ssam ui->xfer_queue.b_dev = vi->ui_slave; 34125675Ssam ci->unit_type[vi->ui_slave] = vi->ui_type; 34225675Ssam /* load unit into controller's active unit list */ 34325675Ssam if (uq == NULL) { 34425675Ssam cq->b_forw = &ui->xfer_queue; 34525675Ssam ui->xfer_queue.b_forw = &ui->xfer_queue; 34625675Ssam ui->xfer_queue.b_back = &ui->xfer_queue; 34725675Ssam } else { 34825675Ssam while (uq->b_forw != start_queue) 34925675Ssam uq = uq->b_forw; 35025675Ssam ui->xfer_queue.b_forw = start_queue; 35125675Ssam ui->xfer_queue.b_back = uq; 35225675Ssam uq->b_forw = &ui->xfer_queue; 35325675Ssam start_queue->b_back = &ui->xfer_queue; 35425675Ssam } 35524004Ssam /* 35625675Ssam * (60 / rpm) / (number of sectors per track * (bytes per sector / 2)) 35724004Ssam */ 35825675Ssam dk_mspw[vi->ui_unit] = 120.0 / (fs->rpm * fs->nsec * fs->secsize); 35924004Ssam } 36024004Ssam 36125675Ssam /*ARGSUSED*/ 36225675Ssam vddgo(um) 36325675Ssam struct vba_ctlr *um; 36424004Ssam { 36524004Ssam 36624004Ssam } 36724004Ssam 36824004Ssam vdstrategy(bp) 36925675Ssam register struct buf *bp; 37024004Ssam { 37125675Ssam register int unit = VDUNIT(bp->b_dev); 37225675Ssam register struct vba_device *vi = vddinfo[unit]; 37325675Ssam register par_tab *par; 37425675Ssam register unit_tab *ui; 37525675Ssam register fs_tab *fs; 37625675Ssam register int blks, bn, s; 37724004Ssam 37825675Ssam if (bp->b_bcount == 0 || vi == 0 || vi->ui_alive == 0) 37925675Ssam goto bad; 38025675Ssam ui = &vdunit_info[unit]; 38125675Ssam fs = &ui->info; 38225675Ssam par = &fs->partition[FILSYS(bp->b_dev)]; 38325675Ssam blks = (bp->b_bcount + DEV_BSIZE-1) >> DEV_BSHIFT; 38425675Ssam if (bp->b_blkno + blks >= par->par_len) { 38525675Ssam blks = par->par_len - bp->b_blkno; 38625675Ssam if (blks <= 0) 38725675Ssam goto bad; 38825675Ssam bp->b_bcount = blks * DEV_BSIZE; 38925675Ssam } 39025675Ssam bn = bp->b_blkno + par->par_start; 39125675Ssam bn *= ui->sec_per_blk; 39225675Ssam bp->b_daddr = (bn / fs->nsec) % fs->ntrak; 39325675Ssam bp->b_cylin = bn / ui->sec_per_cyl; 39425675Ssam vbasetup(bp, ui->info.secsize); 39525675Ssam s = spl7(); 39625675Ssam if (ui->xfer_queue.av_forw == NULL) { 39725675Ssam register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr]; 39825675Ssam int slave = vi->ui_slave; 39924004Ssam 40025675Ssam if (bp->b_cylin != ci->cur_cyl[slave] || 40125675Ssam bp->b_daddr != ci->cur_trk[slave]) 40225675Ssam ci->off_cylinder |= 1 << slave; 40324004Ssam } 40425675Ssam bp->b_daddr |= (bn % fs->nsec) << 8; 40525675Ssam disksort(&ui->xfer_queue, bp); 40625675Ssam if (!vddinfo[unit]->ui_mi->um_tab.b_active++) { 40725675Ssam splx(s); 40825675Ssam vdstart(vddinfo[unit]->ui_mi); 40925675Ssam } else 41025675Ssam splx(s); 41124004Ssam return; 41225675Ssam bad: 41325675Ssam bp->b_flags |= B_ERROR, bp->b_error = ENXIO; 41425675Ssam bp->b_resid = bp->b_bcount; 41524004Ssam iodone(bp); 41624004Ssam } 41724004Ssam 41824004Ssam /* 41924004Ssam * Start up a transfer on a drive. 42024004Ssam */ 42125675Ssam vdstart(ci) 42225675Ssam register struct vba_ctlr *ci; 42324004Ssam { 42425675Ssam register struct buf *cq = &ci->um_tab; 42525675Ssam register struct buf *uq = cq->b_forw; 42624004Ssam 42725675Ssam /* search for next ready unit */ 42825675Ssam cq->b_forw = cq->b_forw->b_forw; 42925675Ssam uq = cq->b_forw; 43025675Ssam do { 43125675Ssam if (uq->av_forw != NULL) { 43225675Ssam cq->b_forw = uq; 43325675Ssam vdexecute(ci, uq); 43425675Ssam return; 43525675Ssam } 43625675Ssam uq = uq->b_forw; 43725675Ssam } while (uq != cq->b_forw); 43825675Ssam } 43925675Ssam 44025675Ssam /* 44125675Ssam * Initiate seeks for all drives off-cylinder. 44225675Ssam */ 44325675Ssam vdload_seeks(ci, uq) 44425675Ssam register ctlr_tab *ci; 44525675Ssam register struct buf *uq; 44625675Ssam { 44725675Ssam register int unit, slave, nseeks; 44825675Ssam register fmt_dcb *dcb; 44925675Ssam register struct buf *bp; 45025675Ssam register struct buf *start_queue = uq; 45125675Ssam 45225675Ssam nseeks = 0; 45325675Ssam do { 45425675Ssam bp = uq->av_forw; 45525675Ssam if (bp != NULL) { 45625675Ssam unit = VDUNIT(bp->b_dev); 45725675Ssam slave = vddinfo[unit]->ui_slave; 45825675Ssam if (ci->off_cylinder & (1 << slave)) { 45925675Ssam ci->off_cylinder &= ~(1 << slave); 46025675Ssam if (ci->cur_cyl[slave] != bp->b_cylin) { 46125675Ssam ci->cur_cyl[slave] = bp->b_cylin; 46225675Ssam dk_seek[unit]++; 46325675Ssam } 46425675Ssam ci->cur_trk[slave] = bp->b_daddr&0xff; 46525675Ssam dcb = &ci->seek_dcb[nseeks++]; 46625675Ssam dcb->opcode = SEEK; 46725675Ssam dcb->intflg = NOINT | INT_PBA; 46825675Ssam dcb->operrsta = 0; 46925675Ssam dcb->devselect = (char)slave; 47025675Ssam dcb->trailcnt = (char)1; 47125675Ssam dcb->trail.sktrail.skaddr.cylinder = 47225675Ssam bp->b_cylin; 47325675Ssam dcb->trail.sktrail.skaddr.track = 47425675Ssam bp->b_daddr & 0xff; 47525675Ssam dcb->trail.sktrail.skaddr.sector = 0; 47625675Ssam } 47725675Ssam } 47825675Ssam uq = uq->b_forw; 47925675Ssam } while (uq != start_queue && nseeks < 4); 48025675Ssam return (nseeks); 48125675Ssam } 48225675Ssam 48325675Ssam extern vd_int_timeout(); 48425675Ssam /* 48525675Ssam * Execute the next command on the unit queue uq. 48625675Ssam */ 48725675Ssam vdexecute(controller_info, uq) 48825675Ssam register struct vba_ctlr *controller_info; 48925675Ssam register struct buf *uq; 49025675Ssam { 49125675Ssam register struct buf *bp = uq->av_forw; 49225675Ssam register int ctlr = controller_info->um_ctlr; 49325675Ssam register ctlr_tab *ci = &vdctlr_info[ctlr]; 49425675Ssam register int unit = VDUNIT(bp->b_dev); 49525675Ssam register int slave = vddinfo[unit]->ui_slave; 49625675Ssam register fmt_mdcb *mdcb = &ci->ctlr_mdcb; 49725675Ssam register fmt_dcb *dcb = &ci->ctlr_dcb; 49825675Ssam 49924004Ssam /* 50025675Ssam * If there are overlapped seeks to perform, shuffle 50125675Ssam * them to the front of the queue and get them started 50225675Ssam * before any data transfers (to get some parallelism). 50324004Ssam */ 50425675Ssam if ((ci->off_cylinder & ~(1<<slave)) && ci->overlap_seeks) { 50525675Ssam register int i, nseeks; 50625675Ssam 50725675Ssam /* setup seek requests in seek-q */ 50825675Ssam nseeks = vdload_seeks(ci, uq); 50925675Ssam /* place at the front of the master q */ 51025675Ssam mdcb->firstdcb = (fmt_dcb *)PHYS(&ci->seek_dcb[0]); 51125675Ssam /* shuffle any remaining seeks up in the seek-q */ 51225675Ssam for (i = 1; i < nseeks; i++) 51325675Ssam ci->seek_dcb[i-1].nxtdcb = 51425675Ssam (fmt_dcb *)PHYS(&ci->seek_dcb[i]); 51525675Ssam ci->seek_dcb[nseeks-1].nxtdcb = (fmt_dcb *)PHYS(dcb); 51625675Ssam } else { 51725675Ssam if (bp->b_cylin != ci->cur_cyl[slave]) { 51825675Ssam ci->cur_cyl[slave] = bp->b_cylin; 51925675Ssam dk_seek[unit]++; 52025675Ssam } 52125675Ssam ci->cur_trk[slave] = bp->b_daddr & 0xff; 52225675Ssam ci->off_cylinder = 0; 52325675Ssam mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); 52424004Ssam } 52524004Ssam dcb->opcode = (bp->b_flags & B_READ) ? RD : WD; 52625675Ssam dcb->intflg = INTDONE; 52725675Ssam dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */ 52824004Ssam dcb->operrsta = 0; 52925675Ssam dcb->devselect = (char)slave; 53025675Ssam dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long)); 53125675Ssam dcb->trail.rwtrail.memadr = (char *) 53225675Ssam vbastart(bp, ci->rawbuf, (long *)ci->map, ci->utl); 53325675Ssam dcb->trail.rwtrail.wcount = (short)((bp->b_bcount+1) / sizeof (short)); 53425675Ssam dcb->trail.rwtrail.disk.cylinder = bp->b_cylin; 53525675Ssam dcb->trail.rwtrail.disk.track = bp->b_daddr & 0xff; 53625675Ssam dcb->trail.rwtrail.disk.sector = bp->b_daddr >> 8; 53725675Ssam mdcb->vddcstat = 0; 53825675Ssam dk_wds[unit] += bp->b_bcount / 32; 53925675Ssam ci->int_expected = 1; 54025675Ssam timeout(vd_int_timeout, (caddr_t)ctlr, 20*60); 54125675Ssam dk_busy |= 1 << unit; 54225675Ssam #ifdef VDDCPERF 54325675Ssam scope_out(1); 54425675Ssam #endif 54525675Ssam VDDC_ATTENTION((cdr *)(vdminfo[ctlr]->um_addr), 54625675Ssam (fmt_mdcb *)(PHYS(mdcb)), ci->ctlr_type); 54725675Ssam } 54824004Ssam 54924004Ssam /* 55025675Ssam * Watch for lost interrupts. 55125675Ssam */ 55225675Ssam vd_int_timeout(ctlr) 55325675Ssam register int ctlr; 55425675Ssam { 55525675Ssam register ctlr_tab *ci = &vdctlr_info[ctlr]; 55625675Ssam register fmt_dcb *dcb = &ci->ctlr_dcb; 55724004Ssam 55825675Ssam uncache(&dcb->operrsta); 55925675Ssam printf("vd%d: lost interupt, status %x", ctlr, dcb->operrsta); 56025675Ssam if (ci->ctlr_type == SMD_ECTLR) { 56125675Ssam uncache(&dcb->err_code); 56225675Ssam printf(", error code %x", dcb->err_code); 56324004Ssam } 56425675Ssam printf("\n"); 56525675Ssam if ((dcb->operrsta&DCBCMP) == 0) { 56625675Ssam VDDC_ABORT((cdr *)(vdminfo[ctlr]->um_addr), ci->ctlr_type); 56725675Ssam dcb->operrsta |= DCBUSC | DCBABT | ANYERR | HRDERR | CTLRERR; 56825675Ssam } 56925675Ssam vdintr(ctlr); 57024004Ssam } 57124004Ssam 57224004Ssam /* 57324004Ssam * Handle a disk interrupt. 57424004Ssam */ 57525675Ssam vdintr(ctlr) 57625675Ssam register int ctlr; 57724004Ssam { 57825675Ssam register ctlr_tab *ci; 57925675Ssam register struct buf *cq, *uq, *bp; 58025675Ssam register int slave, unit; 58125675Ssam register fmt_mdcb *mdcb; 58225675Ssam register fmt_dcb *dcb; 58325675Ssam int code, s; 58424004Ssam 58525675Ssam untimeout(vd_int_timeout, (caddr_t)ctlr); 58624004Ssam #ifdef VDDCPERF 58724004Ssam scope_out(2); 58824004Ssam #endif 58925675Ssam ci = &vdctlr_info[ctlr]; 59025675Ssam if (!ci->int_expected) { 59125675Ssam printf("vd%d: stray interrupt\n", ctlr); 59224004Ssam return; 59324004Ssam } 59425675Ssam /* 59525675Ssam * Take first request off controller's queue. 59625675Ssam */ 59725675Ssam cq = &vdminfo[ctlr]->um_tab; 59825675Ssam uq = cq->b_forw; 59925675Ssam bp = uq->av_forw; 60024004Ssam unit = VDUNIT(bp->b_dev); 60125675Ssam dk_busy &= ~(1 << unit); 60225675Ssam dk_xfer[unit]++; 60325675Ssam ci->int_expected = 0; 60425675Ssam /* find associated control blocks */ 60525675Ssam mdcb = &ci->ctlr_mdcb, uncache(&mdcb->intdcb); 60625675Ssam dcb = &ci->ctlr_dcb, uncache(&dcb->operrsta); 60725675Ssam if (ci->ctlr_type == SMD_ECTLR) 60825675Ssam uncache(&dcb->err_code); 60925675Ssam slave = uq->b_dev; 61025675Ssam switch (code = vddecode_error(dcb)) { 61124004Ssam 61225675Ssam case CTLR_ERROR: 61325675Ssam case DRIVE_ERROR: 61425675Ssam if (cq->b_errcnt >= 2) 61525675Ssam vdhard_error(ci, bp, dcb); 61625675Ssam if (code == CTLR_ERROR) 61725675Ssam vdreset_ctlr((cdr *)vdminfo[ctlr]->um_addr, ctlr); 61825675Ssam else 61925675Ssam reset_drive((cdr *)vdminfo[ctlr]->um_addr, ctlr, 62025675Ssam slave, 2); 62125675Ssam if (cq->b_errcnt++ < 2) { /* retry error */ 62225675Ssam cq->b_forw = uq->b_back; 62325675Ssam vdstart(vdminfo[ctlr]); 62425675Ssam return; 62525675Ssam } 62625675Ssam bp->b_resid = bp->b_bcount; 62725675Ssam break; 62825675Ssam 62925675Ssam case HARD_DATA_ERROR: 63025675Ssam vdhard_error(ci, bp, dcb); 63125675Ssam bp->b_resid = 0; 63225675Ssam break; 63325675Ssam 63425675Ssam case SOFT_DATA_ERROR: 63525675Ssam vdsoft_error(ci, bp, dcb); 63625675Ssam /* fall thru... */ 63725675Ssam 63825675Ssam default: /* operation completed */ 63925675Ssam bp->b_error = 0; 64025675Ssam bp->b_resid = 0; 64125675Ssam break; 64224004Ssam } 64325675Ssam vbadone(bp, ci->rawbuf, (long *)ci->map, ci->utl); 64425675Ssam /* 64525675Ssam * Take next request on this unit q, or, if none, 64625675Ssam * the next request on the next active unit q. 64725675Ssam */ 64825675Ssam s = spl7(); 64925675Ssam uq->av_forw = bp->av_forw; 65025675Ssam if (uq->av_back != bp) { 65125675Ssam register struct buf *next; 65224004Ssam 65325675Ssam unit = VDUNIT(uq->av_forw->b_dev); 65425675Ssam slave = vddinfo[unit]->ui_slave; 65525675Ssam next = uq->av_forw; 65625675Ssam if (next->b_cylin != ci->cur_cyl[slave] || 65725675Ssam (next->b_daddr & 0xff) != ci->cur_trk[slave]) 65825675Ssam ci->off_cylinder |= 1 << slave; 65925675Ssam } else 66025675Ssam uq->av_back = NULL; 66125675Ssam splx(s); 66225675Ssam /* reset controller state */ 66325675Ssam cq->b_errcnt = 0; 66425675Ssam cq->b_active--; 66524004Ssam #ifdef VDDCPERF 66624004Ssam scope_out(3); 66724004Ssam #endif 66825675Ssam if (bp->b_flags & B_ERROR) 66925675Ssam bp->b_error = EIO; 67024004Ssam iodone(bp); 67125675Ssam vdstart(vdminfo[ctlr]); 67224004Ssam } 67324004Ssam 67425675Ssam /* 67525675Ssam * Convert controller status to internal operation/error code. 67625675Ssam */ 67725675Ssam vddecode_error(dcb) 67825675Ssam register fmt_dcb *dcb; 67925675Ssam { 68024004Ssam 68125675Ssam if (dcb->operrsta & HRDERR) { 68225675Ssam if (dcb->operrsta & (HCRCERR | HCMPERR | UCDATERR | WPTERR | 68325675Ssam DSEEKERR | NOTCYLERR |DRVNRDY | INVDADR)) 68425675Ssam return (DRIVE_ERROR); 68525675Ssam if (dcb->operrsta & (CTLRERR | OPABRT | INVCMD | DNEMEM)) 68625675Ssam return (CTLR_ERROR); 68725675Ssam return (HARD_DATA_ERROR); 68825675Ssam } 68925675Ssam if (dcb->operrsta & SFTERR) 69025675Ssam return (SOFT_DATA_ERROR); 69125675Ssam return (0); 69225675Ssam } 69325675Ssam 69425675Ssam /* 69525675Ssam * Report a hard error. 69625675Ssam */ 69725675Ssam vdhard_error(ci, bp, dcb) 69825675Ssam ctlr_tab *ci; 69925675Ssam register struct buf *bp; 70025675Ssam register fmt_dcb *dcb; 70125675Ssam { 70225675Ssam unit_tab *ui = &vdunit_info[VDUNIT(bp->b_dev)]; 70325675Ssam 70425675Ssam bp->b_flags |= B_ERROR; 70525675Ssam harderr(bp, ui->info.type_name); 70625675Ssam printf("status %x", dcb->operrsta); 70725675Ssam if (ci->ctlr_type == SMD_ECTLR) 70825675Ssam printf(" ecode %x", dcb->err_code); 70925675Ssam printf("\n"); 71025675Ssam } 71125675Ssam 71225675Ssam /* 71325675Ssam * Report a soft error. 71425675Ssam */ 71525675Ssam vdsoft_error(ci, bp, dcb) 71625675Ssam ctlr_tab *ci; 71725675Ssam register struct buf *bp; 71825675Ssam register fmt_dcb *dcb; 71925675Ssam { 72025675Ssam unit_tab *ui = &vdunit_info[VDUNIT(bp->b_dev)]; 72125675Ssam 72225675Ssam printf("%s%d%c: soft error sn%d status %x", ui->info.type_name, 72325857Ssam minor(bp->b_dev) >> 3, 'a'+(minor(bp->b_dev)&07), bp->b_blkno, 72425675Ssam dcb->operrsta); 72525675Ssam if (ci->ctlr_type == SMD_ECTLR) 72625675Ssam printf(" ecode %x", dcb->err_code); 72725675Ssam printf("\n"); 72825675Ssam } 72925675Ssam 73025675Ssam /*ARGSUSED*/ 73125675Ssam vdopen(dev, flag) 73225675Ssam dev_t dev; 73325675Ssam int flag; 73425675Ssam { 73525675Ssam register unit = VDUNIT(dev); 73625675Ssam register struct vba_device *vi = vddinfo[unit]; 73725675Ssam 73825675Ssam if (vi == 0 || vi->ui_alive == 0 || vi->ui_type >= nvddrv) 73925675Ssam return (ENXIO); 74025675Ssam if (vdunit_info[unit].info.partition[FILSYS(dev)].par_len == 0) 74125675Ssam return (ENXIO); 74225675Ssam return (0); 74325675Ssam } 74425675Ssam 74524004Ssam vdread(dev, uio) 74625675Ssam dev_t dev; 74725675Ssam struct uio *uio; 74824004Ssam { 74924004Ssam register int unit = VDUNIT(dev); 75025675Ssam register unit_tab *ui = &vdunit_info[unit]; 75124004Ssam 75224004Ssam if (unit >= NFSD) 75325675Ssam return (ENXIO); 75425675Ssam return (physio(vdstrategy, &ui->raw_q_element, dev, B_READ, 75525675Ssam minphys, uio)); 75624004Ssam } 75724004Ssam 75824004Ssam vdwrite(dev, uio) 75925675Ssam dev_t dev; 76025675Ssam struct uio *uio; 76124004Ssam { 76224004Ssam register int unit = VDUNIT(dev); 76325675Ssam register unit_tab *ui = &vdunit_info[unit]; 76424004Ssam 76524004Ssam if (unit >= NFSD) 76625675Ssam return (ENXIO); 76725675Ssam return (physio(vdstrategy, &ui->raw_q_element, dev, B_WRITE, 76825675Ssam minphys, uio)); 76924004Ssam } 77024004Ssam 77124004Ssam /* 77225675Ssam * Crash dump. 77324004Ssam */ 77425675Ssam vddump(dev) 77525675Ssam dev_t dev; 77624004Ssam { 77725675Ssam register int unit = VDUNIT(dev); 77825675Ssam register unit_tab *ui = &vdunit_info[unit]; 77925675Ssam register fs_tab *fs = &ui->info; 78025675Ssam register int ctlr = vddinfo[unit]->ui_ctlr; 78125675Ssam register struct vba_ctlr *vba_vdctlr_info = vdminfo[ctlr]; 78225675Ssam register int filsys = FILSYS(dev); 78325675Ssam register cdr *addr = (cdr *)(vba_vdctlr_info->um_addr); 78425675Ssam register int cur_blk, blkcount, blocks; 78525675Ssam caddr_t memaddr; 78624004Ssam 78725675Ssam vdreset_ctlr(addr, ctlr); 78824004Ssam blkcount = maxfree - 2; /* In 1k byte pages */ 78925675Ssam if (dumplo + blkcount > fs->partition[filsys].par_len) { 79025675Ssam blkcount = fs->partition[filsys].par_len - dumplo; 79125675Ssam printf("vd%d: Dump truncated to %dMB\n", unit, blkcount/1024); 79225675Ssam } 79325675Ssam cur_blk = fs->partition[filsys].par_start + dumplo; 79425675Ssam memaddr = 0; 79524004Ssam while (blkcount > 0) { 79625675Ssam blocks = MIN(blkcount, DUMPSIZE); 79725675Ssam if (!vdwrite_block(addr, ctlr, unit, memaddr, cur_blk, blocks)) 79825675Ssam return (EIO); 79925675Ssam blkcount -= blocks; 80025675Ssam memaddr += blocks * NBPG; 80125675Ssam cur_blk += blocks; 80224004Ssam } 80325675Ssam return (0); 80424004Ssam } 80524004Ssam 80625675Ssam /* 80725675Ssam * Write a block to disk during a crash dump. 80825675Ssam */ 80925675Ssam vdwrite_block(caddr, ctlr, unit, addr, block, blocks) 81025675Ssam register cdr *caddr; 81125675Ssam register int ctlr, unit; 81225675Ssam register caddr_t addr; 81325675Ssam register int block, blocks; 81424004Ssam { 81525675Ssam register fmt_mdcb *mdcb = &vdctlr_info[ctlr].ctlr_mdcb; 81625675Ssam register fmt_dcb *dcb = &vdctlr_info[ctlr].ctlr_dcb; 81725675Ssam register unit_tab *ui = &vdunit_info[unit]; 81825675Ssam register fs_tab *fs = &ui->info; 81924004Ssam 82025675Ssam block *= (int)ui->sec_per_blk; 82125675Ssam blocks *= (int)ui->sec_per_blk; 82225675Ssam mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); 82325675Ssam dcb->intflg = NOINT; 82425675Ssam dcb->opcode = WD; 82525675Ssam dcb->operrsta = 0; 82625675Ssam dcb->devselect = (char)(vddinfo[unit])->ui_slave; 82725675Ssam dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long)); 82825675Ssam dcb->trail.rwtrail.memadr = addr; 82925675Ssam dcb->trail.rwtrail.wcount = (short) 83025675Ssam ((blocks * fs->secsize)/ sizeof (short)); 83125675Ssam dcb->trail.rwtrail.disk.cylinder = (short)(block / ui->sec_per_cyl); 83225675Ssam dcb->trail.rwtrail.disk.track = (char)((block / fs->nsec) % fs->ntrak); 83325675Ssam dcb->trail.rwtrail.disk.sector = (char)(block % fs->nsec); 83425675Ssam VDDC_ATTENTION(caddr, (fmt_mdcb *)(PHYS(mdcb)), 83525675Ssam vdctlr_info[ctlr].ctlr_type); 83625675Ssam POLLTILLDONE(caddr, dcb, 5, vdctlr_info[ctlr].ctlr_type); 83725675Ssam if (vdtimeout <= 0) { 83825675Ssam printf(" during dump\n"); 83925675Ssam return (0); 84025675Ssam } 84125675Ssam if (dcb->operrsta & HRDERR) { 84225675Ssam printf("vd%d: hard error, status %x\n", unit, dcb->operrsta); 84325675Ssam return (0); 84425675Ssam } 84525675Ssam return (1); 84624004Ssam } 84724004Ssam 84824004Ssam vdsize(dev) 84925675Ssam dev_t dev; 85024004Ssam { 85125675Ssam struct vba_device *vi = vddinfo[VDUNIT(dev)]; 85224004Ssam 85325675Ssam if (vi == 0 || vi->ui_alive == 0 || vi->ui_type >= nvddrv) 85425675Ssam return (-1); 85525675Ssam return (vdunit_info[VDUNIT(dev)].info.partition[FILSYS(dev)].par_len); 85624004Ssam } 85724004Ssam 85825675Ssam /* 85925675Ssam * Perform a controller reset. 86025675Ssam */ 86125675Ssam vdreset_ctlr(addr, ctlr) 86225675Ssam register cdr *addr; 86325675Ssam register int ctlr; 86424004Ssam { 86525675Ssam register struct buf *cq = &vdminfo[ctlr]->um_tab; 86625675Ssam register struct buf *uq = cq->b_forw; 86725675Ssam register ctlr_tab *ci = &vdctlr_info[ctlr]; 86825675Ssam 86925675Ssam VDDC_RESET(addr, ci->ctlr_type); 87025675Ssam ci->ctlr_started = 0; 87125675Ssam if (ci->ctlr_type == SMD_ECTLR) { 87225675Ssam addr->cdr_csr = 0; 87325675Ssam addr->mdcb_tcf = AM_ENPDA; 87425675Ssam addr->dcb_tcf = AM_ENPDA; 87525675Ssam addr->trail_tcf = AM_ENPDA; 87625675Ssam addr->data_tcf = AM_ENPDA; 87725675Ssam addr->cdr_ccf = CCF_STS | XMD_32BIT | BSZ_16WRD | 87825675Ssam CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR; 87925675Ssam } 88025675Ssam if (vdnotrailer(addr, ctlr, 0, INIT, 10) & HRDERR) { 88125675Ssam printf("failed to init\n"); 88225675Ssam return (0); 88325675Ssam } 88425675Ssam if (vdnotrailer(addr, ctlr, 0, DIAG, 10) & HRDERR) { 88525675Ssam printf("diagnostic error\n"); 88625675Ssam return (0); 88725675Ssam } 88825675Ssam /* reset all units attached to controller */ 88925675Ssam uq = cq->b_forw; 89025675Ssam do { 89125675Ssam reset_drive(addr, ctlr, uq->b_dev, 0); 89225675Ssam uq = uq->b_forw; 89325675Ssam } while (uq != cq->b_forw); 89425675Ssam return (1); 89525675Ssam } 89624004Ssam 89725675Ssam /* 89825675Ssam * Perform a reset on a drive. 89925675Ssam */ 90025675Ssam reset_drive(addr, ctlr, slave, start) 90125675Ssam register cdr *addr; 90225675Ssam register int ctlr, slave, start; 90325675Ssam { 90425675Ssam register int type = vdctlr_info[ctlr].unit_type[slave]; 90525675Ssam 90625675Ssam if (type == UNKNOWN) 90725675Ssam return; 90825675Ssam if (!vdconfigure_drive(addr, ctlr, slave, type, start)) 90925675Ssam printf("vd%d: drive %d: couldn't reset\n", ctlr, slave); 91025675Ssam } 91125675Ssam 91225675Ssam #ifdef notdef 91325675Ssam /* 91425675Ssam * Dump the mdcb and DCB for diagnostic purposes. 91525675Ssam */ 91625675Ssam vdprintdcb(lp) 91725675Ssam register long *lp; 91825675Ssam { 91925675Ssam register int i, dcb, tc; 92025675Ssam 92125675Ssam for (dcb = 0; lp; lp = (long *)(*lp), dcb++) { 92225675Ssam lp = (long *)((long)lp | 0xc0000000); 92325675Ssam printf("\nDump of dcb%d@%x:", dcb, lp); 92425675Ssam for (i = 0, tc = lp[3] & 0xff; i < tc+7; i++) 92525675Ssam printf(" %lx", lp[i]); 92625675Ssam printf("\n"); 92724004Ssam } 92825675Ssam DELAY(1750000); 92924004Ssam } 93024004Ssam #endif 93125675Ssam #endif 932