Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/pc/ultrastor.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname pc/ultrastor.c 1993/0915
## diff -e /dev/null /n/fornaxdump/1993/0915/sys/src/brazil/pc/ultrastor.c
0a
/*
 * Ultrastor [13]4f SCSI Host Adapter.
 * Originally written by Charles Forsyth, [email protected].
 * Needs work:
 *	handle multiple controllers;
 *	set options from user-level;
 *	split out to handle adaptec too;
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

#include "ureg.h"

typedef struct Ctlr Ctlr;
typedef struct Mailbox Mailbox;
typedef struct Scatter Scatter;

enum {
	Nctl		= 1,		/* only support one */
	Nmbox		= 8,		/* number of Mailboxes; up to 16? */
	Nscatter 	= 33,		/* s/g list limit fixed by Ultrastor controller */

	Port		= 0x330,	/* factory defaults: I/O port */
	CtlrID		= 7,		/*	adapter SCSI id */
	Irq		= 14,		/*	interrupt request level */
};

struct Scatter {
	ulong	start;	/* physical address; Intel byte order */
	ulong	len;	/* length in bytes; Intel byte order */
};

/*
 * Ultrastor mailbox pointers point to one of these structures
 */
struct Mailbox {
	uchar	op;		/* Adapter operation */
	uchar	addr;		/* lun, chan, scsi ID */
	uchar	datap[4];	/* transfer data phys. pointer */
	uchar	datalen[4];	/* transfer data length */
	uchar	cmdlink[4];	/* command link phys. pointer */
	uchar	linkid;		/* SCSI command link ID */
	uchar	nscatter;	/* scatter/gather length (8 bytes per entry) */
	uchar	senselen;	/* length of sense data */
	uchar	cmdlen;		/* length of CDB */
	uchar	cmd[12];	/* SCSI command */
	uchar	adapterstatus;
	uchar	targetstatus;
	uchar	sensep[4];	/* sense data phys. pointer */

	/* the remainder is used by software, not the device */
	uchar	sensedata[64];	/* enough for AdapterInquiry data too */
	Scatter	sglist[Nscatter];
	Rendez	iodone;
	int	busy;		/* interrupt pending */
	ulong	physaddr;	/* physical address of this Mailbox */
	int	p9status;	/* p9 status value */
	Mailbox	*next;		/* next Mailbox in free list */
};

/* pack pointer as Intel order bytes (can simply assign) */
#define	PL(buf,p)	(*(ulong*)(buf)=(ulong)(p))

enum {
					/* bits in Mailbox.op[0] */
					/* 3 bit opcode */
	OpAdapter	= 0x01,		/* adapter command, not SCSI command */
	OpTarget	= 0x02,		/* SCSI command for device */
	OpReset		= 0x04,		/* device reset */
					/* 2 bit transfer direction */
	CmdDirection	= 0<<3,		/* transfer direction determined by SCSI command */
	DataIn		= 1<<3,		/* SCSI Data In */
	DataOut		= 2<<3,		/* SCSI Data Out */
	NoTransfer	= 3<<3,
	Nodiscon=	1<<5,		/* disable disconnect */
	Usecache=	1<<6,		/* use adapter cache (if available) */
	Scattered=	1<<7,		/* transfer pointer refers to S/G list */

					/* OpAdapter commands in Mailbox.cmd[0] */
	AdapterInquiry	= 0x01,
	AdapterSelftest	= 0x02,
	AdapterRead	= 0x03,		/* read adapter's buffer */
	AdapterWrite	= 0x04,		/* write to adapter's buffer */

					/* IO port offsets and bits */
	AdapterMask	= 0x00, 	/* local doorbell mask register */
	  OGMIntEnable	= 0x01,
	  SoftResetEnable = 0x40,
	  AdapterReady	= 0xe1,		/* adapter ready for work when all these set */
	AdapterIntr	= 0x01,		/* adapter interrupt/status; set by host, reset by adapter */
	  SignalAdapter	= 0x01,		/* tell adapter to read OGMpointer */
	  BusReset	= 0x20,		/* SCSI Bus Reset */
	  SoftReset	= 0x40,		/* Adapter Soft Reset */
	HostMask	= 0x02, 	/* host doorbell mask register */
	  ICMIntEnable	= 0x01,		/* incoming mail enable */
	  SIntEnable	= 0x80, 	/* enable interrupt from HostIntr reg */
	HostIntr	= 0x03, 	/* host doorbell interrupt/status; set by adapter, reset by host */
	  IntPending	= 0x80,		/* interrupt pending for host */
	  SignalHost	= 0x01,		/* Incoming Mail Interrupt */
	ProductID	= 0x04,		/* two byte product ID */
	Config1		= 0x06,
	Config2		= 0x07,
	OGMpointer	= 0x08,		/* pointer to Outgoing Mail */
	ICMpointer	= 0x0c,		/* pointer to Incoming Mail */
};

#define	OUT(c,v) outb(ctlr->io+(c), (v))
#define	IN(c)	inb(ctlr->io+(c))

struct Ctlr {
	Lock;
	QLock;
	int	io;		/* io port */
	int	irq;		/* interrupt vector */
	int	dma;		/* DMA channel */
	int	ownid;		/* adapter's SCSI ID */
	Mailbox	mbox[Nmbox];
	Mailbox	*free;		/* next free Mailbox */
	Rendez	mboxes;		/* wait for free mbox */
	QLock	mboxq;		/* mutex for mboxes */
	Rendez	ogm;		/* wait for free OGM slot with Ctlr qlocked */
};

static	Ctlr	ultra[Nctl];

int	scsidebugs[8] = {0};
int	scsiownid = 7;

static void
prmbox(Mailbox *m)
{
	int i;

	print("scsi %lx: op=%2.2x dir=%x cmd=%d [", m->physaddr, m->op&07, (m->op>>3)&3, m->cmdlen);
	for(i=0; i<m->cmdlen; i++)
		print(" %2.2x", m->cmd[i]);
	print("] id=%d lun=%d chan=%d", m->addr&7, (m->addr>>5)&7, (m->addr>>3)&3);
	print(" dlen=%lx dptr=%lx\n", *(long*)m->datalen, *(long*)m->datap);
	print(" astat=%2.2x dstat=%2.2x sc=%d [", m->adapterstatus, m->targetstatus, m->nscatter);
	for(i=0; i<m->nscatter; i++)
		print(" %lx,%ld", m->sglist[i].start, m->sglist[i].len);
	print("]\n");
	/*for(i=0; i<10; i++)
		print(" %2.2x", m->sensedata[i]);*/
	print("\n");
}

static void
resetadapter(int busreset)
{
	Ctlr *ctlr = &ultra[0];
	int i;

	if(IN(AdapterMask) & SoftResetEnable){
		OUT(AdapterIntr, SoftReset|busreset);
		for(i=50000; IN(AdapterIntr) & (SoftReset|busreset);)
			if(--i<0) {
				print("Ultrastor reset timed out\n");
				break;
			}
	}
}

static void
freebox(Ctlr *ctlr, Mailbox *m)
{
	lock(ctlr);
	if((m->next = ctlr->free) == 0)
		wakeup(&ctlr->mboxes);
	ctlr->free = m;
	unlock(ctlr);
}

static int
boxavail(void *a)
{
	return ((Ctlr*)a)->free != 0;
}

static int
scatter(Scatter *list, void *va, ulong nb)
{
	char *p = (char *)va;
	Scatter *sp = list;
	int limit = Nscatter;

	for(; nb != 0; sp++){
		if(--limit < 0)
			panic("Ultrastor I/O too scattered");
		sp->start = PADDR(p);
		sp->len = BY2PG - (sp->start&(BY2PG-1));	/* the rest of that page */
		if(sp->len > nb)
			sp->len = nb;
		nb -= sp->len;
		p += sp->len;
	}
	return sp - list;
}

static int
isready(void *a)
{
	Ctlr *ctlr = (Ctlr*)a;

	return (IN(AdapterIntr) & SignalAdapter) == 0;
}

static int
isdone(void *a)
{
	return ((Mailbox*)a)->busy == 0;
}

static int
ultra14fexec(Scsi *p, int rflag)
{
	Ctlr *ctlr = &ultra[0];
	Mailbox *m;
	long n;
	uchar *cp;

	if (p == 0 || ctlr->io == 0)
		return 0x0200;	/* device not available */

	/*
	 * get a free Mailbox and build an Ultrastor command
	 */
	while(lock(ctlr), (m = ctlr->free) == 0){
		unlock(ctlr);
		qlock(&ctlr->mboxq);
		if(waserror()){
			qunlock(&ctlr->mboxq);
			nexterror();
		}
		sleep(&ctlr->mboxes, boxavail, ctlr);
		poperror();
		qunlock(&ctlr->mboxq);
	}
	ctlr->free = m->next;
	unlock(ctlr);
	p->rflag = rflag;
	m->p9status = 0;
	m->op = OpTarget | CmdDirection | Usecache;	/* BUG? is it safe always to Usecache? */
	m->addr = p->target | (p->lun<<5);
	if(p->cmd.lim - p->cmd.ptr > sizeof(m->cmd))
		panic("scsiexec");
	m->cmdlen = p->cmd.lim - p->cmd.ptr;
	for(cp = m->cmd; p->cmd.ptr < p->cmd.lim;)
		*cp++ = *p->cmd.ptr++;
	n = p->data.lim - p->data.ptr;
	if(n){
		PL(m->datap, PADDR(m->sglist));
		m->op |= Scattered;
		m->nscatter = scatter(m->sglist, p->data.ptr, n);
	} else {
		m->nscatter = 0; /* controller will not allow s/g when n==0 */
		PL(m->datap, 0);
	}
	PL(m->datalen, n);
	m->adapterstatus = 0;
	m->targetstatus = 0;

	/*
	 * send the command to the host adapter
	 */
	while(lock(ctlr), IN(AdapterIntr) & SignalAdapter) {	/* adapter busy: infrequent? */
		static int fred;
		if(fred == 0){
			print("ultrastor busy\n");
			fred = 1;
		}
		unlock(ctlr);
		qlock(ctlr);
		if(waserror()) {
			freebox(ctlr, m);
			qunlock(ctlr);
			nexterror();
		}
		sleep(&ctlr->ogm, isready, ctlr);
		poperror();
		qunlock(ctlr);
	}
	m->busy = 1;
	outl(ctlr->io+OGMpointer, m->physaddr);
	OUT(AdapterIntr, SignalAdapter);
	unlock(ctlr);

	/*
	 * wait for reply
	 */
	while(waserror())
		;	/* could send MSCP abort request to adapter */
	while(m->busy)
		sleep(&m->iodone, isdone, m);
	poperror();
	p->status = m->p9status;
	if(scsidebugs[2])
		prmbox(m);
	if(p->status == 0x6000)
		p->data.ptr = p->data.lim;	/* can only assume no residue */
	freebox(ctlr, m);
	return p->status;
}

static void
scsimoan(char *msg, Mailbox *m)
{
	/*int i;*/

	print("SCSI error: %s:", msg);
	print("id=%d cmd=%2.2x status=%2.2x adapter=%2.2x", m->addr&7, m->cmd[0], m->targetstatus, m->adapterstatus);
	/*print(" sense:");
	for(i=0; i<10; i++)
		print(" %2.2x", m->sensedata[i]);*/
	print("\n");
}

static void
interrupt(Ureg *ur)
{
	Ctlr *ctlr = &ultra[0];
	Mailbox *m;
	ulong pa;

	USED(ur);
	if(ctlr->ogm.p && (IN(AdapterIntr) & SignalAdapter) == 0)
		wakeup(&ctlr->ogm);
	if((IN(HostIntr) & SignalHost) == 0)
		return;	/* no incoming mail */
	pa = inl(ctlr->io+ICMpointer);
	OUT(HostIntr, SignalHost);	/* release ICM slot */
	for(m = &ctlr->mbox[0]; m->physaddr != pa;)
		if(++m >= &ctlr->mbox[Nmbox]) {
			/* these sometimes happen in response to a scsi bus reset; ignore them */
			print("ultrastor: invalid ICM pointer #%lux\n", pa);
			return;
		}
	if(scsidebugs[1])
		prmbox(m);

	m->p9status = 0x1000 | m->adapterstatus;
	switch(m->adapterstatus){
	default:
		scsimoan("adapter", m);
		if(m->adapterstatus >= 0x92) /* ie, scsi command protocol error */
			resetadapter(BusReset);
		/* might adapter reset be required in other cases? */
		break;

	case 0x91:	/* selection timed out */
		m->p9status = 0x0200;
		if(scsidebugs[0])
			scsimoan("timeout", m);
		break;

	case 0xA3:
		scsimoan("SCSI bus reset error", m);
		break;

	case 0x00:
		m->p9status = 0x6000 | m->targetstatus;
		if(m->targetstatus && scsidebugs[0])
			scsimoan("device", m);
		break;
	}
	m->busy = 0;
	wakeup(&m->iodone);
}

static ISAConf ultra14f = {
	"ultra14f",
	Port,
	Irq,
	0,
	0,
};

static int irq[4] = {
	15, 14, 11, 10,
};

int (*
ultra14freset(void))(Scsi*, int)
{
	ISAConf *isa = &ultra14f;
	Ctlr *ctlr = &ultra[0];
	Mailbox *m;
	int i, model;

	/*
	 * See if we have any configuration info
	 * and check it is an Ultrastor [13]4f.
	 * Only one controller for now.
	 */
	if(isaconfig("scsi", 0, &ultra14f) == 0)
		return 0;
	memset(ctlr, 0, sizeof(Ctlr));
	ctlr->io = isa->port;

	if(IN(ProductID) != 0x56)
		return 0;
	model = IN(ProductID+1);
	switch(model){

	case 0x40:
		model = 14;
		break;

	case 0x41:
		model = 34;
		break;

	default:
		return 0;
	}

	i = IN(Config1);
	ctlr->irq = irq[((i>>4)&03)];
	if(model == 14)
		ctlr->dma = 5 + ((i>>6)&03);
	ctlr->ownid = IN(Config2)&07;
	/*print("Ultrastor %dF id %d io 0x%x irq %d dma %d\n",
		model, ctlr->ownid, ctlr->io, ctlr->irq, ctlr->dma);*/

	/*
	 * set the DMA controller to cascade mode for bus master
	 */
	switch(ctlr->dma){
	case 5:
		outb(0xd6, 0xc1); outb(0xd4, 1); break;
	case 6:
		outb(0xd6, 0xc2); outb(0xd4, 2); break;
	case 7:
		outb(0xd6, 0xc3); outb(0xd4, 3); break;
	}

	resetadapter(0);
	ctlr->free = 0;
	for(i=0; i<Nmbox; i++){
		m = &ctlr->mbox[i];
		memset(m, 0, sizeof(*m));
		m->physaddr = PADDR(m);
		PL(m->sensep, 0);	/* DON'T collect sense data automatically: messes up scuzz? */
		m->senselen = 0;
		m->next = ctlr->free;
		ctlr->free = m;
	}

	setvec(Int0vec+ctlr->irq, interrupt);

	/*
	 * issue AdapterInquiry command to check it's alive
	 * -- could check enquiry data
	 */
	OUT(HostMask, 0);	/* mask interrupts */
	m = &ctlr->mbox[0];
	m->op = OpAdapter | CmdDirection;
	m->addr = ctlr->ownid;
	m->cmdlen = 1;
	m->cmd[0] = AdapterInquiry;
	PL(m->datap, PADDR(m->sensedata));
	PL(m->datalen, 38);
	while(IN(AdapterIntr) & SignalAdapter)
		;
	outl(ctlr->io+OGMpointer, m->physaddr);
	OUT(AdapterIntr, SignalAdapter);
	for(i=100000; (IN(HostIntr)&SignalHost)==0;)
		if(--i < 0){
			print("No response to UltraStor Inquiry\n");
			ctlr->io = 0;
			return 0;
		}
	OUT(HostIntr, SignalHost);

	OUT(HostMask, ICMIntEnable|SIntEnable);
	scsiownid = ctlr->ownid;

	return ultra14fexec;
}
.
## diffname pc/ultrastor.c 1993/1113 # deleted
## diff -e /n/fornaxdump/1993/0915/sys/src/brazil/pc/ultrastor.c /n/fornaxdump/1993/1113/sys/src/brazil/pc/ultrastor.c
1,484d

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].