/* #define HOWFAR */
/* #define UNISOFT		/* allow access to boot partition */
/*
 * 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/config.h"
#include "sys/mmu.h"
#include "sys/types.h"
#include "sys/sysmacros.h"
#include "sys/dir.h"
#include "sys/signal.h"
#include "sys/user.h"
#include "sys/errno.h"
#include "sys/utsname.h"
#include "sys/buf.h"
#include "sys/elog.h"
#include "sys/erec.h"
#include "sys/iobuf.h"
#include "sys/systm.h"
#include "sys/var.h"
#include "sys/uioctl.h"
#include "sys/al_ioctl.h"
#include "sys/diskformat.h"
#include "setjmp.h"
#include "sys/reg.h"
#include "sys/altblk.h"
#include "sys/cops.h"
#include "sys/pport.h"
#include "sys/priam.h"
#include "sys/swapsz.h"

#define logical(x)	(minor(x) & 7)		/* eight logicals per phys */
#define physical(x)	((minor(x) & 0xF0) >> 4)/* 10 physical devs */
#define splpm	spl5

/*
 * When the disk is first opened its size is determined and pm_sizes is
 * initialized accordingly (in pmdinit).
 *
 * The first 100 blocks are reserved for the boot program and
 * are inaccessible via unix.
 */
#define	MAXBOOT	100
struct pm_sizes {
	daddr_t	sz_offset;
	daddr_t	sz_size;
} pm_sizes[NPM][8];

#define GETBUF(bp)	splpm(); \
	while (bp->b_flags & B_BUSY) { \
		bp->b_flags |= B_WANTED; \
		(void)sleep((caddr_t)bp, PRIBIO+1); \
	} \
	bp->b_flags |= B_BUSY; \
	spl0()

#define FREEBUF(bp)	splpm(); \
	if (bp->b_flags & B_WANTED) \
		wakeup((caddr_t)bp); \
	bp->b_flags = 0; \
	spl0()

struct	iostat	pmstat[NPM];
struct	iobuf	pmtab = tabinit(PM3,pmstat);	/* active buffer header */
struct	buf	pmcbuf;		/* command buffer */
struct	buf	pmrbuf;
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 pmfmtparms pmfmt;	/* format parameters for packet-based format cmd */
struct pmfmtstat pmfstat;	/* format status from packet-based format cmd */
/* variables needed to share info for interrupt routines */
char	pmstate;		/* idling or waiting for interrupt */
#define	IDLING		0
#define	INITING		1
#define	READING1	2	/* 1st intr while reading */
#define	READING2	3	/* 2nd intr while reading */
#define	WRITING1	4	/* 1st intr while writing */
#define	WRITING2	5	/* 2nd intr while writing */
#define	FMTING1		6	/* 1st intr while formatting */
#define	FMTING2		7	/* 2nd intr while formatting */
#define	FMTING3		8	/* 3rd intr while formatting */
#define	FMTING4		9	/* 4th intr while formatting */
caddr_t	pmma;			/* current memory address */
char	pmierror;		/* error value from intr */

pmopen(dev)
register dev;
{
	register punit;
	extern char slot[];

	punit = physical(dev);
	if (punit >= NPM) {
		u.u_error = ENXIO;
		return(-1);
	}
	if (slot[punit] != PM3) {
		printf("Unix pmopen: no Priam card in slot %d\n", punit);
		u.u_error = ENODEV;
		return(-1);
	}
	if (pmiflag[punit] == 0) {
		if (pmdinit(dev)) {
			u.u_error = EIO;
			return(-1);
		}
		pmiflag[punit]++;
	}
	return(0);
}

/*
 * pmdinit - initialize device (sequence up)
 */
pmdinit(dev)
register dev_t dev;
{
	register struct buf *bp = &pmcbuf;
	register punit, i;
	register struct pm_sizes *sp;
	register daddr_t offset, size;

	punit = physical(dev);
	GETBUF(bp);
	bp->b_dev = dev;
	bp->b_resid = PMRDEVPMS;
	pmnblks[punit] = 0;
	pmstrategy(bp);
	iowait(bp);
	i = bp->b_flags & B_ERROR;
	FREEBUF(bp);
	if (i)
		return(-1);
	sp = pm_sizes[punit];
	offset = MAXBOOT + 1;			/* avoid boot area */
	size = pmnblks[punit] - offset;
	sp[7].sz_offset = offset;		/* h = entire */
	sp[7].sz_size = size;
	sp[1].sz_offset = offset;		/* b = swap */
	sp[1].sz_size = PMNSWAP;
	offset += PMNSWAP;
	size -= PMNSWAP;
	if (size < 0) {
		printf("Unix pmdinit: disk size = %d (insufficient)\n", pmnblks[punit]);
		return(-1);
	}
	sp[0].sz_offset = offset;		/* a = root */
	sp[0].sz_size = size;
	for (i = 2; i < 7; i++) {
		sp[i].sz_offset = (daddr_t)(0);	/* c-g =spare */
		sp[i].sz_size = (daddr_t)0;
	}
	return(0);
}

pmstrategy(bp)
register struct buf *bp;
{
	register punit, lunit, bn;

	punit = physical(bp->b_dev);
	if (bp == &pmcbuf) {			/* if command */
		pmstat[punit].io_misc++; /* errlog: */
		splpm();
		if (pmtab.b_actf == (struct buf *)NULL) /* set up links */
			pmtab.b_actf = bp;
		else
			pmtab.b_actl->av_forw = bp;
		pmtab.b_actl = bp;
		bp->av_forw = (struct buf *)NULL;
	} else {
		lunit = logical(bp->b_dev);
		bn = bp->b_blkno + pm_sizes[punit][lunit].sz_offset;
#ifdef UNISOFT
		if (bp->b_blkno < 0 ) {
#else UNISOFT
		if (bp->b_blkno < 0 || bn <= MAXBOOT) {
#endif UNISOFT
#ifdef HOWFAR
			prdev("pmstrategy: illegal blkno", bp->b_dev);
			printf("blkno=%d bcount=%d\n", bp->b_blkno, bp->b_bcount);
#endif HOWFAR
			bp->b_flags |= B_ERROR;
			iodone(bp);
			return;
		}
		pmstat[punit].io_ops++; /* errlog: */
		bp->b_resid = bn;	/* resid for disksort */
		splpm();
		disksort(&pmtab, bp);
	}
	if (pmtab.b_active == 0)
		pmstart();
	SPL0();
}

pmstart()
{
	register struct buf *bp;
	register offset, bn, lunit, punit;

loop:
	if ((bp = pmtab.b_actf) == (struct buf *)NULL)
		return;
	if (pmtab.b_active == 0) {
		pmtab.b_active = 1;
		if (bp != &pmcbuf)
			bp->b_resid = bp->b_bcount;
	}
	blkacty |= (1<<PM3);
	if (bp == &pmcbuf) {
		if (pmcmd(bp) != 0) {	/* b_resid holds the command */
			bp->b_flags |= B_ERROR;
			goto next;
		}
		return;
	}
	lunit = logical(bp->b_dev);
	punit = physical(bp->b_dev);
	offset = bp->b_bcount - bp->b_resid;
	bn = bp->b_blkno + btod(offset);	/* logical block number */
	if (bp->b_resid < BSIZE || bn >= pm_sizes[punit][lunit].sz_size) {
	next:
#ifdef HOWFAR
		if (bp->b_resid != 0)
			printf("Unix pmstart: blkno=%d resid=%d bn=%d\n",
				bp->b_blkno, bp->b_resid, bn);
#endif HOWFAR
		pmtab.b_active = 0;
		pmtab.b_errcnt = 0;
		blkacty &= ~(1<<PM3);
		pmtab.b_actf = bp->av_forw;
		iodone(bp);
		goto loop;
	}
	bn += pm_sizes[punit][lunit].sz_offset;	/* physical block number */
	if (pmrw(punit, bn, bp->b_flags&B_READ, bp->b_un.b_addr+offset) != 0) {
		bp->b_flags |= B_ERROR;
		goto next;
	}
}

/* ARGSUSED */
pmioctl(dev, cmd, addr, flag)
dev_t dev;
caddr_t addr;
{
	register punit;
	register struct buf *bp;

	punit = physical(dev);
	if (punit >= NPM) {
		u.u_error = EINVAL;
		return;
	}
	switch (cmd) {
	case UIOCSIZE:		/* get size of Priam disk */
		if (copyout((caddr_t)&pmnblks[punit], (caddr_t)addr, 4))
			u.u_error = EFAULT;
		break;
	case UIOCFORMAT:
		if (!suser()) {
			u.u_error = EPERM;
			return;
		}
		bp = &pmcbuf;
		GETBUF(bp);
		bp->b_dev = dev;
		bp->b_resid = PMPKTXFER;	/* stash the command in resid */
		pmstrategy(bp);
		iowait(bp);
		if (bp->b_flags & B_ERROR)
			u.u_error = EIO;
		FREEBUF(bp);
		break;
	default:
		u.u_error = ENOTTY;
		break;
	}
}

pmcmd(bp)
register struct buf *bp;
{
	register struct pm_base *pmhwbase;
	register struct pm_base *addr;
	register punit;
	register struct pmfmtparms *fmtp = &pmfmt;

	punit = physical(bp->b_dev);
	pmierror = 0;
	switch (bp->b_resid) {
	case PMRDEVPMS:		/* read device parameters (spin up if necessary) */
		pmhwbase = pmaddr(punit);	/* base address for this card */
		addr = pmBWaddr(pmhwbase);	/* byte mode - waiting */
		if (pmwaitrf(addr)) {
			printf("Unix pmcmd: timeout before read dev parms\n");
			return(-1);
		}
		addr->p0 = 0;			/* device always 0 */
		addr = pmBIaddr(pmhwbase);	/* byte mode - intr enabled */
		pmstate = INITING;
		addr->cmdreg = PMRDEVPMS;
		return(0);
	case PMPKTXFER:		/* format disk (done with packet-based command) */
		pmhwbase = pmaddr(punit);	/* base address for this card */
		addr = pmBWaddr(pmhwbase);	/* byte mode - waiting */
		if (pmwaitrf(addr)) {
			printf("Unix pmcmd: timeout before format\n");
			return(-1);
		}
		fmtp->pm_opcode = PMFMT;/* set up format parms packet */
		fmtp->pm_devsel = 0;	/* device select = 0 (no daisy chaining) */
		fmtp->pm_scntl = PMFBD;	/* fill byte disabled, media type = 0 */
		fmtp->pm_fill = 0;	/* fill byte */
		fmtp->pm_ssize = 536;	/* sector size */
		fmtp->pm_dcntl = 0;	/* defect mapping enabled, 0 spares/track */
		fmtp->pm_ncyl = 10;	/* # cyls for alt tracks and alt sectors */
		fmtp->pm_cif = 0;	/* cyl interleave factor */
		fmtp->pm_hif = 1;	/* head interleave factor */
		fmtp->pm_sif = 1;	/* sector interleave factor */
		fmtp->pm_sitl = 0;	/* sector interleave table length */
		addr->p0 = 0;			/* device always 0 */
		addr->p2 = 0;			/* MSB of packet length */
		addr->p3 = sizeof(struct pmfmtparms);	/* LSB of packet length */
		addr = pmBIaddr(pmhwbase);	/* byte mode - intr enabled */
		pmstate = FMTING1;
		addr->cmdreg = PMPKTXFER;
		return(0);
	default:
		return(-1);
	}
}

pmrw(unit, bn, rflag, addr)
register unit;
register bn;
register rflag;
register caddr_t addr;
{
	register struct pm_base *pmhwbase;
	register struct pm_base *hwaddr;
	register tmp;

	pmhwbase = pmaddr(unit);	/* base address for this card */
	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */
	if (pmwaitrf(hwaddr)) {
		printf("pmrw: timeout before setting %s parameters ",
			rflag?"read":"write");
		printf("card %d, bn %d, addr 0x%x\n", unit, bn, addr);
		return(-1);
	}
	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 */
	pmma = addr;
	pmierror = 0;
	pmstate = rflag ? READING1 : WRITING1;
	hwaddr->cmdreg = rflag ? PMREAD : PMWRITE;	/* start r/w */
	return(0);
}

pmintr(ap)
struct args *ap;
{
	register struct pm_base *pmhwbase;
	register struct pm_base *addr;
	register struct buf *bp;
	register char status;
	dev_t punit;

	(void) splpm();
	punit = ap->a_dev;
	pmhwbase = pmaddr(punit);	/* base address for this card */
	addr = pmBWaddr(pmhwbase);	/* byte mode - waiting */
	if (pmtab.b_active == 0) {
#ifdef HOWFAR
		printf("Unix pmintr: b_active == 0\n");
#endif HOWFAR
spurious:
		addr->cmdreg = 0;	/* clear spurious intr (clrb) */
		return;
	}
	if ((bp = pmtab.b_actf) == (struct buf *)NULL) {
#ifdef HOWFAR
		printf("Unix pmintr: b_actf == NULL\n");
#endif HOWFAR
		goto spurious;
	}
	if (bp == &pmcbuf) {		/* if command */
		switch (bp->b_resid) {
		case PMRDEVPMS:	/* read device parameters (spin up if necessary) */
			if (pmstate != INITING)
				goto spurious;
			if (pmierror = pmackcc(addr)) {
				printf("error on read dev parms: /dev/pm%d%c ",
					punit, 'a'+logical(bp->b_dev));
				printf("status 0x%x\n", pmierror);
				bp->b_flags |= B_ERROR;
			} else {
				register nheads, ncyls, nsecs;
				nheads = PMNH(pmresults[1]);
				ncyls = PMNC(pmresults[1], pmresults[2]);
				nsecs = PMNS(pmresults[3]);
				pmnblks[punit] = nheads * ncyls * nsecs;
			}
			break;
		case PMPKTXFER:	/* format disk (done with packet-based command) */
			switch (pmstate) {
			case FMTING1:		/* intr to transfer packet */
				if ((addr->status & DTREQ) == 0)
					goto ackcc;	/* if not waiting for data */
				{		/* send packet */
					register char *cp = (char *)&pmfmt;
					register i = sizeof(struct pmfmtparms);
					do {
						addr->pdata = *cp++;
					} while ( --i );
				}
				(void)pmackdt(addr);	/* Apple's code doesn't check */
		wait:		pmstate++;	/* not done yet - wait for next intr */
				return;
			case FMTING2:		/* intr after packet complete */
				(void)pmackcc(addr);
				(void)pmwaitrf(addr);
				addr->p0 = 0;	/* packet ID = 0 */
				addr = pmBIaddr(pmhwbase);
				addr->cmdreg = PMPKTRST;/* read packet status */
				goto wait;
			case FMTING3:		/* intr when status can be read */
				if ((addr->status & DTREQ) == 0)
					goto ackcc;	/* if not waiting for data */
				{		/* read packet status */
					register char *cp = (char *)&pmfstat;
					register i = sizeof(struct pmfmtstat);
					do {
						*cp++ = addr->pdata;
					} while ( --i );
				}
				(void)pmackdt(addr);	/* Apple's code doesn't check */
				goto wait;
			case FMTING4:		/* intr after read packet status */
		ackcc:		(void)pmackcc(addr);
				if ((pmfstat.pm_pstate != PMPKTCOMP)
					|| pmfstat.pm_pristat) {
					pmierror = pmfstat.pm_pristat;
					bp->b_flags |= B_ERROR;
				}
			}
			break;
		}
		goto out;
	}
	if ((addr->status & CMD_DONE) == 0) {	/* block transfer interrupt */
		if (!pmcblkcnt) {	/* 2nd interrupt */
			if ((pmstate==READING2) || (pmstate==WRITING2))
				goto done;
			printf("Unix pmintr: state=%d, expecting 2nd\n", pmstate);
			bp->b_flags |= B_ERROR;
			goto out;
		}
		/* 1st interrupt */
		if ((pmstate!=READING1) && (pmstate!=WRITING1)) {
			printf("Unix pmintr: state=%d, expecting 1st\n", pmstate);
			bp->b_flags |= B_ERROR;
			goto out;
		}
		pmcblkcnt--;
		if (pmstate == READING1)
			pmrsect(punit);	/* sets pmierror for parity */
		else
			pmwsect(punit);
		pmstate++;	/* set to READING2 or WRITING2 */
		addr->cmdreg = PMCBTI;	/* clear block transfer intr */
		addr = pmBWIaddr(pmhwbase);	/* setup for next intr */
		status = addr->status;
		return;
	} else {		/* cmd completed (error) */
		if ((pmstate!=READING2) && (pmstate!=WRITING2))
			printf("Unix pmintr: command complete, state=%d\n", pmstate);
done:
		if (status = pmackcc(addr)) {	/* ack intr */
			if ((pmstate==READING2) && (pmresults[0] == PMECCERR))
				/* ECC err requiring scavenger write */
				status = PMECCERR;
			else if (pmresults[0] == PMDTIMOUT)
				/* dev timeout - they reissue read/write */
				status = PMDTIMOUT;
		}
		if (pmierror |= status)
			bp->b_flags |= B_ERROR;
	}
out:
	/*
	 * because a single buffer can take several io operations, 
	 * we leave it to pmstart() to figure out when it's done
	 */
	if (bp->b_flags&B_ERROR || bp == &pmcbuf) {
		if (bp->b_flags & B_ERROR) {
			{
			register bn = 0;
			struct deverreg pmreg[2];

			printf("Unix: HARD I/O ERROR on /dev/pm%d%c ",
				punit, logical(bp->b_dev)+'a');
			if (pmierror) {
				if (pmierror & PMPERROR)
					printf("parity error ");
				if (status = (pmierror & ~PMPERROR))
					printf("error code 0x%x ", status);
			}
			if (bp != &pmcbuf) {
				bn = bp->b_blkno + btod(bp->b_bcount - bp->b_resid)
					+ pm_sizes[punit][logical(bp->b_dev)].sz_offset;
				printf("bn %d\n", bn);
			} else printf("\n");
			/* error logging */
			pmtab.io_stp = &pmstat[punit];
			pmreg[0].draddr = (long)0;
			pmreg[0].drvalue = pmierror;
			pmreg[0].drname = "pmierror";
			pmreg[0].drbits = "Priam disk status code";
			pmreg[1].draddr = (long)0;
			pmreg[1].drvalue = pmstate;
			pmreg[1].drname = "pmstate";
			pmreg[1].drbits = "Priam driver state";
			fmtberr(&pmtab,
				(unsigned)punit,
				(unsigned)0,		/* cylinder */
				(unsigned)0,		/* track */
				(unsigned)bn,		/* sector */
				(long)(sizeof(pmreg)/sizeof(pmreg[0])), /* regcnt */
				&pmreg[0],&pmreg[1]);
			}
			logberr(&pmtab, 1);	/* log hard (unrecovered) error */
		}
		blkacty &= ~(1<<PM3);
		pmtab.b_active = 0;
		pmtab.b_errcnt = 0;
		pmtab.b_actf = bp->av_forw;
		iodone(bp);
	} else
		bp->b_resid -= 512;
	pmstate = IDLING;
	pmstart();
}

/*
 * read block to memory at pmma
 * from PRIAMASM.TEXT - READ_SECT
 */
pmrsect(punit)
register punit;
{
	register struct pm_base *pmhwbase;
	register struct pm_base *hwaddr;
	register short *waddr;
	register i;

	pmhwbase = pmaddr(punit);	/* base address for this card */
	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 */
		*waddr = hwaddr->data;
	} while ( --i );		/* read 24-byte header */
	waddr = (short *)pmma;
	i = 255;
	do {
		*waddr++ = hwaddr->data;
	} while ( --i );		/* read 510 bytes */
	hwaddr = pmNaddr(hwaddr);	/* read last 2 avoiding device cycle */
	*waddr = hwaddr->data;
	hwaddr = pmpaddr(punit);	/* parity in lo select space */
	pmierror |= (hwaddr->parity & PMPERROR);
}

/*
 * write block from memory at pmma
 * from PRIAMASM.TEXT - WRITE_SECT
 */
pmwsect(punit)
register punit;
{
	register struct pm_base *pmhwbase;
	register struct pm_base *hwaddr;
	register short *waddr;
	register i;
	register short tmp = 0;

	pmhwbase = pmaddr(punit);	/* base address for this card */
	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */
	while ((hwaddr->status & DTREQ) == 0)	;
	hwaddr = pmhwbase;
	i = 12;
	do {				/* write dummy header */
		hwaddr->data = tmp;
	} while ( --i );		/* write 24 bytes */
	waddr = (short *)pmma;
	i = 256;
	do {
		hwaddr->data = *waddr++;
	} while ( --i );		/* write 512 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 */
}

/*
 * acknowledge data transfer
 * from PRIAMASM.TEXT - ACKDT
 * Their code does not check the return value, so who knows what'll happen
 * if it times out?
 */
pmackdt(addr)
register struct pm_base *addr;	/* pmBWaddr */
{
	register i;

	addr->cmdreg = PMCBTI;		/* clear block transfer intr */
	i = TIMELIMIT;
	while ((addr->status & BTR_INT) != 0) {
		if (--i)
			continue;
		printf("Unix pmackdt: timeout\n");
		return(-1);
	}
	addr = pmIaddr(addr);		/* pmBWIaddr */
	i = addr->status & BTR_INT;	/* setup for next intr */
	return(0);
}

/*
 * pmcinit - initialize controller (turn on motor)
 * called from oem7init (config.c)
 */
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("Unix pmcinit: can't initialize controller\n");
	return(-1);
}

pmread(dev)
dev_t dev;
{
	physio(pmstrategy, &pmrbuf, dev, B_READ);
}

pmwrite(dev)
dev_t dev;
{
	physio(pmstrategy, &pmrbuf, dev, B_WRITE);
}

pmprint(dev, str)
char *str;
{
	printf("%s on priam drive %d, slice %d\n", str, (dev>>4)&0xF, dev&7);
}
