/* * Priam Datatower * * (C) 1984 UniSoft Corp. of Berkeley CA * * UniPlus Source Code. This program is proprietary * with Unisoft Corporation and is not to be reproduced * or used in any manner except as authorized in * writing by Unisoft. */#include "sys/param.h"#include "sys/mmu.h"#include "sys/types.h"#include "sys/inode.h"#include "sys/ino.h"#include "saio.h"#include "sys/cops.h"#include "sys/local.h"#include "sys/priam.h"/*#define DELAY()	asm("\tnop");*/#define DELAY()	;char pmiflag[NPM];char pmresults[NPMRES];		/* result regs. for last command comp ack */int pmnblks[NPM];		/* number of blocks on disk */int pmcblkcnt;			/* current block count */struct pmheader pmhdr;		/* write header (write boot fileid for blk 0) *//* variables needed to share info for interrupt routines */struct pm_base *pmhwbase;char	pmstate;		/* idling or waiting for interrupt */#define	IDLING		0#define	INITING		1#define	READING		2#define	WRITING		3char	pmunit;			/* card (0,1,2) */daddr_t	pmbn;			/* bn for pmwsect, block 0 gets special ID in header */caddr_t	pmma;			/* memory address */char	pmierror;		/* error value from intr */pmopen(iobp)register struct iob *iobp;{	register unit = iobp->i_unit;	register short slotid;	int pm0intr(), pm1intr(), pm2intr();	register long *ip;	register short *waddr, i;	if (unit >= NPM) {		printf("Invalid device 0x%x\n", unit);		return -1;	}	slotid = (unit==0) ? *SLOTIDS : 		((unit==1) ? *SLOTID2 : *SLOTID3);	if ((slotid & SLOTMASK) != ID_PRIAM) {		printf("pmopen: not a Priam card\n");		return -1;	}	ip = &((long *) 0)[EXPIVECT+devtoslot(unit)];	/* point to int vect */	*ip = (unit==0) ? (long)pm0intr :	/* set to Priam intr */		((unit==1) ? (long)pm1intr : (long)pm2intr);	waddr = (short *)&pmhdr;	/* clear header to zeroes */	i = HDRSIZE / sizeof(short);	do {		*waddr++ = 0;	} while ( --i );	if (pmiflag[unit] == 0) {		if (pmcinit(unit))			return(-1);		if (pmdinit(unit))			return(-1);		pmiflag[unit]++;	}	return 0;}/* * pmcinit - initialize controller (turn on motor) * from PRIAMCARDASM.TEXT - INITCONT */pmcinit(unit)register unit;{	register struct pm_base *addr;	register i;	addr = pmaddr(unit);	/* hwbase */	addr = pmBWaddr(addr);	/* byte mode - waiting */	if ((addr->status & CMD_DONE) != 0) {	/* cmd complete must be false */		if (pmwaitrf(addr)) {			printf("timeout with status CMD_DONE\n");			goto err;		}		addr->cmdreg = 0;	/* command ack power up (clrb) */		i = TIMELIMIT;		while ((addr->status & CMD_DONE) == 0) {			if (--i)				continue;			printf("timeout waiting for power up\n");			goto err;		}	}	if (pmwaitrf(addr)) {		printf("timeout before software reset\n");		goto err;	}	addr->cmdreg = PMRESET;		/* issue software reset */	if ((i=pmackcc(addr)) != PMICOMP) {		printf("software reset error 0x%x\n", i);		goto err;	}	if (pmwaitrf(addr)) {		printf("timeout before read mode\n");		goto err;	}	addr->p0 = 0;		/* (clrb) */	addr->cmdreg = PMRMODE;	if (pmackcc(addr)) {		printf("read mode cmd ack failed\n");		goto err;	}	if (pmwaitrf(addr)) {		printf("timeout after read mode\n");		goto err;	}	addr->p0 = 0;		/* (clrb) */	if (pmresults[3] == 2) {		printf("Smart E controller not implemented\n");		goto err;	}	addr->p1 = PMLOGSECT;		/* set mode "logical sector mode" */	addr->p2 = 0;		/* (clrb) */	addr->cmdreg = PMSMODE;	if (pmackcc(addr)) {		printf("set mode cmd ack failed\n");		goto err;	}	if (pmwaitrf(addr)) {		printf("timeout after set mode\n");		goto err;	}	addr->p0 = 0x40;	addr->p1 = PMPSEL1;	addr->p2 = PM1PARMS;	addr->cmdreg = PMSETP;	if (pmackcc(addr)) {		printf("set parms 1 cmd ack failed\n");		goto err;	}	if (pmwaitrf(addr)) {		printf("timeout after set parms 1\n");		goto err;	}	addr->p0 = 0x40;	addr->p1 = PMPSEL0;	addr->p2 = PM0PARMS;	addr->cmdreg = PMSETP;	if (pmackcc(addr)) {		printf("set parms 0 cmd ack failed\n");		goto err;	}	return 0;err:	printf("can't initialize controller\n");	return(-1);}/* * pmdinit - initialize device (sequence up) * from PRIAMASM.TEXT - INITDEV */pmdinit(unit)register unit;{	register struct pm_base *addr;	register tmp;	register pmnheads, pmncyls, pmnsecs;	/* no. of heads, cylinders, sectors */	pmhwbase = pmaddr(unit);	/* base address for this card */	addr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	if (pmwaitrf(addr)) {		printf("timeout before read dev parms\n");		goto err;	}/*****/	addr->p0 = 0;			/* device always 0 */	addr = pmBIaddr(pmhwbase);	/* byte mode - intr enabled */	pmunit = unit;			/* save for pmiintr */	pmierror = 0;			/* clear for pmiintr */	pmstate = INITING;	tmp = spl2();	addr->cmdreg = PMRDEVPMS;	/* sequence up disk & read dev parms */	while (pmstate != IDLING)	;	splx(tmp);	if (pmierror) {		printf("transaction status = 0x%x\n", pmresults[0]);		goto err;	}	pmnheads = PMNH(pmresults[1]);	pmncyls = PMNC(pmresults[1], pmresults[2]);	pmnsecs = PMNS(pmresults[3]);	pmnblks[unit] = pmnheads * pmncyls * pmnsecs;	return 0;err:	printf("can't initialize disk\n");	return(-1);}pmstrategy(ip, func)register struct iob *ip;{	register unit = ip->i_unit;	register daddr_t bn;	register resid;	int offset;	caddr_t addr;	if (unit >= NPM)		return(-1);	pmhwbase = pmaddr(unit);	/* base address for this card */	if (ip->i_bn < 0) {		printf("pmstrategy: bn < 0\n");		return(-1);	}	resid = ip->i_cc;loop:	if (resid < PBSIZE)		goto out;	offset = ip->i_cc - resid;	addr = ip->i_ma + offset;	bn = ip->i_bn + (offset >> PBSHIFT);	if (bn >= pmnblks[unit]) {		printf("pmstrategy: %d blocks on disk\n", pmnblks[unit]);		goto out;	}	if (pmrw(unit, bn, func&F_READ, addr) != 0)		return(-1);	resid -= PBSIZE;	goto loop;out:	if (resid != 0)		printf("pmstrategy: bn=%d resid=%d\n", ip->i_bn, resid);	return(ip->i_cc - resid);}pmrw(unit, bn, rflag, addr)register unit;daddr_t bn;register rflag;register caddr_t addr;{	register struct pm_base *hwaddr;	register tmp;	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	if (pmwaitrf(hwaddr)) {		printf("pmrw: timeout before setting %s parameters ",			rflag?"read":"write");		goto err;	}/*****/	hwaddr->p0 = 0;			/* device always 0 */	tmp = bn;	hwaddr->p3 = tmp & 0xFF;	/* most sig. byte */	tmp = tmp >> 8;	hwaddr->p2 = tmp & 0xFF;	/* middle byte */	tmp = tmp >> 8;	hwaddr->p1 = tmp & 0xFF;	/* least sig. byte */	hwaddr->p4 = 1;			/* operation count = 1 sector */	pmcblkcnt = 1;	hwaddr = pmBIaddr(pmhwbase);	/* byte mode - intr enabled */	pmunit = unit;			/* save for pmrwintr */	pmbn = bn;			/* pmwsect - block 0 is special */	pmma = addr;			/* save for pmrwintr */	pmierror = 0;			/* clear for pmrwintr */	pmstate = rflag ? READING : WRITING;	tmp = spl2();	hwaddr->cmdreg = rflag ? PMREAD : PMWRITE;	/* start r/w */	while (pmstate != IDLING)	;	splx(tmp);	if (pmierror) {		if (pmierror & PMPERROR)			printf("parity error\n");		if (pmierror & 0x40)			printf("previous error\n");		if (tmp = (pmierror & ~PMPERROR))			printf("error code 0x%x\n", tmp);		printf("pmrw: %s failed on ", rflag?"read":"write");		goto err;	}	return(0);err:	printf("card %d, bn %d, addr 0x%x\n", unit, bn, addr);	return(-1);}/* interrupt on card 0 */pm0intr(){	if ((pmunit != 0) || (pmstate == IDLING)) {		register struct pm_base *addr;		printf("unexpected interrupt, unit 0, pmunit %d\n", pmunit);		addr = pmBWaddr(pmaddr(0));		addr->cmdreg = 0;	/* clear spurious intr (clrb) */	} else if (pmstate == INITING)		pmiintr();	else	/* pmstate is READING, WRITING, RFLUSH or WFLUSH */		pmrwintr();	asm("unlk a6");	asm("rte");}/* interrupt on card 1 */pm1intr(){	if ((pmunit != 1) || (pmstate == IDLING)) {		register struct pm_base *addr;		printf("unexpected interrupt, unit 1, pmunit %d\n", pmunit);		addr = pmBWaddr(pmaddr(1));		addr->cmdreg = 0;	/* clear spurious intr (clrb) */	} else if (pmstate == INITING)		pmiintr();	else	/* pmstate is READING, WRITING, RFLUSH or WFLUSH */		pmrwintr();	asm("unlk a6");	asm("rte");}/* interrupt on card 2 */pm2intr(){	if ((pmunit != 2) || (pmstate == IDLING)) {		register struct pm_base *addr;		printf("unexpected interrupt, unit 2, pmunit %d\n", pmunit);		addr = pmBWaddr(pmaddr(2));		addr->cmdreg = 0;	/* clear spurious intr (clrb) */	} else if (pmstate == INITING)		pmiintr();	else	/* pmstate is READING, WRITING, RFLUSH or WFLUSH */		pmrwintr();	asm("unlk a6");	asm("rte");}/* * interrupt on read/write operation */pmrwintr(){	register struct pm_base *hwaddr;	register status;	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	if ((hwaddr->status & CMD_DONE) == 0) {	/* block transfer interrupt */		if (!pmcblkcnt)		/* 2nd interrupt */			goto done;		/* 1st interrupt */		pmcblkcnt--;		if (pmstate == READING)			pmrsect();	/* sets pmierror for parity */		else			pmwsect();		hwaddr->cmdreg = PMCBTI;	/* clear block transfer intr */		hwaddr = pmBWIaddr(pmhwbase);	/* setup for next intr */		status = hwaddr->status;	} else {		/* cmd completed (error) */pmierror |= 0x40;	done:#ifdef notdef		if (pmierror) {			if (pmstate == READING)				pmrflush();			else				pmwflush();			hwaddr->cmdreg = PMCBTI;	/* clear block transfer intr */			hwaddr = pmBWIaddr(pmhwbase);	/* setup for next intr */			status = hwaddr->status;		}#endif notdef		if (status = pmackcc(hwaddr)) {	/* ack intr */			if ((pmstate==READING) && (pmresults[0] == PMECCERR))				/* ECC err requiring scavenger write */				status = PMECCERR;			else if (pmresults[0] == PMDTIMOUT)				/* dev timeout - they reissue read/write */				status = PMDTIMOUT;		}		pmierror |= status;		if (pmierror) {			pmcinit(pmunit);		}		pmstate = IDLING;	}}/* * interrupt on initialize (read drive parameters) operation */pmiintr(){	register struct pm_base *addr;	addr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	if (pmierror = pmackcc(addr)) {	/* ack intr */		printf("pmiintr: read dev parms failed\n");	}	pmstate = IDLING;}/* * read block to memory at pmma * from PRIAMASM.TEXT - READ_SECT */pmrsect(){	register struct pm_base *hwaddr;	register short *waddr;	register i;	if ((long)pmma & 0x1) {		/* cannot handle odd memory addr */		printf("odd addr=0x%x\n", pmma);		pmierror = 1;		pmstate = IDLING;		return;	}	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	while ((hwaddr->status & DTREQ) == 0)	;	hwaddr = pmPaddr(pmhwbase);	/* parity checking enabled */	waddr = (short *)pmma;	*waddr = hwaddr->data;		/* start read of first word */	i = 12;	do {				/* read and throw away header */		DELAY();		*waddr = hwaddr->data;	} while ( --i );		/* read 24-byte header */	waddr = (short *)pmma;	i = 255;	do {		DELAY();		*waddr++ = hwaddr->data;	} while ( --i );		/* read 510 bytes */	hwaddr = pmNaddr(hwaddr);	/* read last 2 avoiding device cycle */	*waddr = hwaddr->data;	hwaddr = pmpaddr(pmunit);	/* parity in lo select space */	pmierror |= (hwaddr->parity & PMPERROR);}/* * write block from memory at pmma * from PRIAMASM.TEXT - WRITE_SECT */pmwsect(){	register struct pm_base *hwaddr;	register short *waddr;	register i;	if ((long)pmma & 0x1) {		/* cannot handle odd memory addr */		printf("odd addr=0x%x\n", pmma);		pmierror = 1;		pmstate = IDLING;		return;	}	if (pmbn == 0)		/* boot block 0 gets boot ID in header */		pmhdr.pm_fileid = FILEID;	else		pmhdr.pm_fileid = 0;	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	while ((hwaddr->status & DTREQ) == 0)	;	hwaddr = pmhwbase;	waddr = (short *)&pmhdr;	i = HDRSIZE / sizeof(short);	do {		DELAY();		hwaddr->data = *waddr++;	} while ( --i );		/* write 24 byte header */	waddr = (short *)pmma;	i = 256;	do {		DELAY();		hwaddr->data = *waddr++;	} while ( --i );		/* write 512 bytes */}/* * after error flush controller with dummy read to complete request * from PRIAMASM.TEXT - READ_FLUSH */pmrflush(){	register struct pm_base *hwaddr;	register short tmp;	register i;	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	while ((hwaddr->status & DTREQ) == 0)	;	hwaddr = pmPaddr(pmhwbase);	/* parity checking enabled */	tmp = hwaddr->data;		/* start read of first word */	i = 267;	do {		DELAY();		tmp = hwaddr->data;	} while ( --i );		/* read 534 bytes */	hwaddr = pmNaddr(hwaddr);	/* read last 2 avoiding device cycle */	tmp = hwaddr->data;}/* * after error flush controller to complete request * from PRIAMASM.TEXT - WRITE_FLUSH */pmwflush(){	register struct pm_base *hwaddr;	register i;	register short tmp = 0;	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	while ((hwaddr->status & DTREQ) == 0)	;	hwaddr = pmhwbase;	i = 268;	do {		DELAY();		hwaddr->data = tmp;	} while ( --i );		/* write 536 bytes */}/* * wait for register file not busy * from PRIAMASM.TEXT and PRIAMCARDASM.TEXT - WAITRF */pmwaitrf(addr)register struct pm_base *addr;{	register i;	i = TIMELIMIT;	while ((addr->status & ISR_BUSY) != 0) {		if (--i)			continue;		return(-1);	}	return(0);}/* * acknowledge command completion * from PRIAMASM.TEXT and PRIAMCARDASM.TEXT - ACKCC */pmackcc(addr)register struct pm_base *addr;	/* pmBWaddr */{	register i;	i = TIMELIMIT;	while ((addr->status & CMD_DONE) == 0) {	/* wait for cmd done */		if (--i)			continue;		printf("pmackcc: timeout waiting for CMD_DONE\n");		return(-1);	}	pmresults[0] = addr->r0;	/* transaction status */	pmresults[1] = addr->r1;	pmresults[2] = addr->r2;	pmresults[3] = addr->r3;	pmresults[4] = addr->r4;	pmresults[5] = addr->r5;	addr->cmdreg = 0;		/* send command acknowledge (clrb) */	i = TIMELIMIT;	while ((addr->status & CMD_DONE) != 0) {	/* wait 'til reset */		if (--i)			continue;		printf("pmackcc: timeout waiting for CMD_DONE reset\n");		return(-1);	}	return(pmresults[0] & PMCTYPE);	/* error completion type */}