xref: /plan9/sys/src/9/pc/sdmylex.c (revision fececb924262ae5acb31c5c448a4a6a523887b15)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  * Mylex MultiMaster (Buslogic BT-*) SCSI Host Adapter
37dd7cddfSDavid du Colombier  * in both 24-bit and 32-bit mode.
47dd7cddfSDavid du Colombier  * 24-bit mode works for Adaptec AHA-154xx series too.
57dd7cddfSDavid du Colombier  *
67dd7cddfSDavid du Colombier  * To do:
77dd7cddfSDavid du Colombier  *	allocate more Ccb's as needed, up to NMbox-1;
87dd7cddfSDavid du Colombier  *	add nmbox and nccb to Ctlr struct for the above;
97dd7cddfSDavid du Colombier  *	64-bit LUN/explicit wide support necessary?
107dd7cddfSDavid du Colombier  *
117dd7cddfSDavid du Colombier  */
127dd7cddfSDavid du Colombier #include "u.h"
137dd7cddfSDavid du Colombier #include "../port/lib.h"
147dd7cddfSDavid du Colombier #include "mem.h"
157dd7cddfSDavid du Colombier #include "dat.h"
167dd7cddfSDavid du Colombier #include "fns.h"
177dd7cddfSDavid du Colombier #include "io.h"
187dd7cddfSDavid du Colombier #include "ureg.h"
197dd7cddfSDavid du Colombier #include "../port/error.h"
207dd7cddfSDavid du Colombier 
2180ee5cbfSDavid du Colombier #include "../port/sd.h"
227dd7cddfSDavid du Colombier 
237dd7cddfSDavid du Colombier #define K2BPA(va, tbdf)	PADDR(va)
247dd7cddfSDavid du Colombier #define BPA2K(pa, tbdf)	KADDR(pa)
257dd7cddfSDavid du Colombier 
267dd7cddfSDavid du Colombier extern SDifc sdmylexifc;
277dd7cddfSDavid du Colombier 
287dd7cddfSDavid du Colombier enum {					/* registers */
297dd7cddfSDavid du Colombier 	Rcontrol	= 0x00,		/* WO: control register */
307dd7cddfSDavid du Colombier 	Rstatus		= 0x00,		/* RO: status register */
317dd7cddfSDavid du Colombier 	Rcpr		= 0x01,		/* WO: command/parameter register */
327dd7cddfSDavid du Colombier 	Rdatain		= 0x01,		/* RO: data-in register */
337dd7cddfSDavid du Colombier 	Rinterrupt	= 0x02,		/* RO: interrupt register */
347dd7cddfSDavid du Colombier };
357dd7cddfSDavid du Colombier 
367dd7cddfSDavid du Colombier enum {					/* Rcontrol */
377dd7cddfSDavid du Colombier 	Rsbus		= 0x10,		/* SCSI Bus Reset */
387dd7cddfSDavid du Colombier 	Rint		= 0x20,		/* Interrupt Reset */
397dd7cddfSDavid du Colombier 	Rsoft		= 0x40,		/* Soft Reset */
407dd7cddfSDavid du Colombier 	Rhard		= 0x80,		/* Hard Reset */
417dd7cddfSDavid du Colombier };
427dd7cddfSDavid du Colombier 
437dd7cddfSDavid du Colombier enum {					/* Rstatus */
447dd7cddfSDavid du Colombier 	Cmdinv		= 0x01,		/* Command Invalid */
457dd7cddfSDavid du Colombier 	Dirrdy		= 0x04,		/* Data In Register Ready */
467dd7cddfSDavid du Colombier 	Cprbsy		= 0x08,		/* Command/Parameter Register Busy */
477dd7cddfSDavid du Colombier 	Hardy		= 0x10,		/* Host Adapter Ready */
487dd7cddfSDavid du Colombier 	Inreq		= 0x20,		/* Initialisation Required */
497dd7cddfSDavid du Colombier 	Dfail		= 0x40,		/* Diagnostic Failure */
507dd7cddfSDavid du Colombier 	Dact		= 0x80,		/* Diagnostic Active */
517dd7cddfSDavid du Colombier };
527dd7cddfSDavid du Colombier 
537dd7cddfSDavid du Colombier enum {					/* Rcpr */
547dd7cddfSDavid du Colombier 	Cinitialise	= 0x01,		/* Initialise Mailbox */
557dd7cddfSDavid du Colombier 	Cstart		= 0x02,		/* Start Mailbox Command */
5659cc4ca5SDavid du Colombier 	Cinquiry	= 0x04,		/* Adapter Inquiry */
577dd7cddfSDavid du Colombier 	Ceombri		= 0x05,		/* Enable OMBR Interrupt */
587dd7cddfSDavid du Colombier 	Cinquire	= 0x0B,		/* Inquire Configuration */
597dd7cddfSDavid du Colombier 	Cextbios	= 0x28,		/* AHA-1542: extended BIOS info. */
607dd7cddfSDavid du Colombier 	Cmbienable	= 0x29,		/* AHA-1542: Mailbox interface enable */
617dd7cddfSDavid du Colombier 	Ciem		= 0x81,		/* Initialise Extended Mailbox */
627dd7cddfSDavid du Colombier 	Ciesi		= 0x8D,		/* Inquire Extended Setup Information */
637dd7cddfSDavid du Colombier 	Cerrm		= 0x8F,		/* Enable strict round-robin mode */
647dd7cddfSDavid du Colombier 	Cwide		= 0x96,		/* Wide CCB */
657dd7cddfSDavid du Colombier };
667dd7cddfSDavid du Colombier 
677dd7cddfSDavid du Colombier enum {					/* Rinterrupt */
687dd7cddfSDavid du Colombier 	Imbl		= 0x01,		/* Incoming Mailbox Loaded */
697dd7cddfSDavid du Colombier 	Mbor		= 0x02,		/* Mailbox Out Ready */
707dd7cddfSDavid du Colombier 	Cmdc		= 0x04,		/* Command Complete */
717dd7cddfSDavid du Colombier 	Rsts		= 0x08,		/* SCSI Reset State */
727dd7cddfSDavid du Colombier 	Intv		= 0x80,		/* Interrupt Valid */
737dd7cddfSDavid du Colombier };
747dd7cddfSDavid du Colombier 
7559c21d95SDavid du Colombier typedef struct Mbox24 Mbox24;
7659c21d95SDavid du Colombier struct Mbox24 {
777dd7cddfSDavid du Colombier 	uchar	code;			/* action/completion code */
787dd7cddfSDavid du Colombier 	uchar	ccb[3];			/* CCB pointer (MSB, ..., LSB) */
7959c21d95SDavid du Colombier };
807dd7cddfSDavid du Colombier 
8159c21d95SDavid du Colombier typedef struct Mbox32 Mbox32;
8259c21d95SDavid du Colombier struct Mbox32 {
837dd7cddfSDavid du Colombier 	uchar	ccb[4];			/* CCB pointer (LSB, ..., MSB) */
847dd7cddfSDavid du Colombier 	uchar	btstat;			/* BT-7[45]7[SD] status */
857dd7cddfSDavid du Colombier 	uchar	sdstat;			/* SCSI device status */
867dd7cddfSDavid du Colombier 	uchar	pad;
877dd7cddfSDavid du Colombier 	uchar	code;			/* action/completion code */
8859c21d95SDavid du Colombier };
897dd7cddfSDavid du Colombier 
907dd7cddfSDavid du Colombier enum {					/* mailbox commands */
917dd7cddfSDavid du Colombier 	Mbfree		= 0x00,		/* Mailbox not in use */
927dd7cddfSDavid du Colombier 
937dd7cddfSDavid du Colombier 	Mbostart	= 0x01,		/* Start a mailbox command */
947dd7cddfSDavid du Colombier 	Mboabort	= 0x02,		/* Abort a mailbox command */
957dd7cddfSDavid du Colombier 
967dd7cddfSDavid du Colombier 	Mbiok		= 0x01,		/* CCB completed without error */
977dd7cddfSDavid du Colombier 	Mbiabort	= 0x02,		/* CCB aborted at request of host */
987dd7cddfSDavid du Colombier 	Mbinx		= 0x03,		/* Aborted CCB not found */
997dd7cddfSDavid du Colombier 	Mbierror	= 0x04,		/* CCB completed with error */
1007dd7cddfSDavid du Colombier };
1017dd7cddfSDavid du Colombier 
1027dd7cddfSDavid du Colombier typedef struct Ccb24 Ccb24;
1037dd7cddfSDavid du Colombier typedef struct Ccb32 Ccb32;
1047dd7cddfSDavid du Colombier typedef union Ccb Ccb;
1057dd7cddfSDavid du Colombier 
1067dd7cddfSDavid du Colombier typedef struct Ccb24 {
1077dd7cddfSDavid du Colombier 	uchar	opcode;			/* Operation code */
1087dd7cddfSDavid du Colombier 	uchar	datadir;		/* Data direction control */
1097dd7cddfSDavid du Colombier 	uchar	cdblen;			/* Length of CDB */
1107dd7cddfSDavid du Colombier 	uchar	senselen;		/* Length of sense area */
1117dd7cddfSDavid du Colombier 	uchar	datalen[3];		/* Data length (MSB, ..., LSB) */
1127dd7cddfSDavid du Colombier 	uchar	dataptr[3];		/* Data pointer (MSB, ..., LSB) */
1137dd7cddfSDavid du Colombier 	uchar	linkptr[3];		/* Link pointer (MSB, ..., LSB) */
1147dd7cddfSDavid du Colombier 	uchar	linkid;			/* command linking identifier */
1157dd7cddfSDavid du Colombier 	uchar	btstat;			/* BT-* adapter status */
1167dd7cddfSDavid du Colombier 	uchar	sdstat;			/* SCSI device status */
1177dd7cddfSDavid du Colombier 	uchar	reserved[2];		/* */
1187dd7cddfSDavid du Colombier 	uchar	cs[12+0xFF];		/* Command descriptor block + Sense */
1197dd7cddfSDavid du Colombier 
1207dd7cddfSDavid du Colombier 	void*	data;			/* buffer if address > 24-bits */
1217dd7cddfSDavid du Colombier 
1227dd7cddfSDavid du Colombier 	Rendez;
1237dd7cddfSDavid du Colombier 	int	done;			/* command completed */
1247dd7cddfSDavid du Colombier 
1257dd7cddfSDavid du Colombier 	Ccb*	ccb;			/* link on free list */
1267dd7cddfSDavid du Colombier } Ccb24;
1277dd7cddfSDavid du Colombier 
1287dd7cddfSDavid du Colombier 
1297dd7cddfSDavid du Colombier typedef struct Ccb32 {
1307dd7cddfSDavid du Colombier 	uchar	opcode;			/* Operation code */
1317dd7cddfSDavid du Colombier 	uchar	datadir;		/* Data direction control */
1327dd7cddfSDavid du Colombier 	uchar	cdblen;			/* Length of CDB */
1337dd7cddfSDavid du Colombier 	uchar	senselen;		/* Length of sense area */
1347dd7cddfSDavid du Colombier 	uchar	datalen[4];		/* Data length (LSB, ..., MSB) */
1357dd7cddfSDavid du Colombier 	uchar	dataptr[4];		/* Data pointer (LSB, ..., MSB) */
1367dd7cddfSDavid du Colombier 	uchar	reserved[2];
1377dd7cddfSDavid du Colombier 	uchar	btstat;			/* BT-* adapter status */
1387dd7cddfSDavid du Colombier 	uchar	sdstat;			/* SCSI device status */
1397dd7cddfSDavid du Colombier 	uchar	targetid;		/* Target ID */
1407dd7cddfSDavid du Colombier 	uchar	luntag;			/* LUN & tag */
1417dd7cddfSDavid du Colombier 	uchar	cdb[12];		/* Command descriptor block */
1427dd7cddfSDavid du Colombier 	uchar	ccbctl;			/* CCB control */
1437dd7cddfSDavid du Colombier 	uchar	linkid;			/* command linking identifier */
1447dd7cddfSDavid du Colombier 	uchar	linkptr[4];		/* Link pointer (LSB, ..., MSB) */
1457dd7cddfSDavid du Colombier 	uchar	senseptr[4];		/* Sense pointer (LSB, ..., MSB) */
1467dd7cddfSDavid du Colombier 	uchar	sense[0xFF];		/* Sense bytes */
1477dd7cddfSDavid du Colombier 
1487dd7cddfSDavid du Colombier 	Rendez;
1497dd7cddfSDavid du Colombier 	int	done;			/* command completed */
1507dd7cddfSDavid du Colombier 
1517dd7cddfSDavid du Colombier 	Ccb*	ccb;			/* link on free list */
1527dd7cddfSDavid du Colombier } Ccb32;
1537dd7cddfSDavid du Colombier 
1547dd7cddfSDavid du Colombier typedef union Ccb {
1557dd7cddfSDavid du Colombier 	Ccb24;
1567dd7cddfSDavid du Colombier 	Ccb32;
1577dd7cddfSDavid du Colombier } Ccb;
1587dd7cddfSDavid du Colombier 
1597dd7cddfSDavid du Colombier enum {					/* opcode */
1607dd7cddfSDavid du Colombier 	OInitiator	= 0x00,		/* initiator CCB */
1617dd7cddfSDavid du Colombier 	Ordl		= 0x03,		/* initiator CCB with
1627dd7cddfSDavid du Colombier 					 * residual data length returned
1637dd7cddfSDavid du Colombier 					 */
1647dd7cddfSDavid du Colombier };
1657dd7cddfSDavid du Colombier 
1667dd7cddfSDavid du Colombier enum {					/* datadir */
1677dd7cddfSDavid du Colombier 	CCBdatain	= 0x08,		/* inbound, length is checked */
1687dd7cddfSDavid du Colombier 	CCBdataout	= 0x10,		/* outbound, length is checked */
1697dd7cddfSDavid du Colombier };
1707dd7cddfSDavid du Colombier 
1717dd7cddfSDavid du Colombier enum {					/* btstat */
1727dd7cddfSDavid du Colombier 	Eok		= 0x00,		/* normal completion with no errors */
1737dd7cddfSDavid du Colombier };
1747dd7cddfSDavid du Colombier 
1757dd7cddfSDavid du Colombier enum {					/* luntag */
1767dd7cddfSDavid du Colombier 	TagEnable	= 0x20,		/* Tag enable */
1777dd7cddfSDavid du Colombier 	SQTag		= 0x00,		/* Simple Queue Tag */
1787dd7cddfSDavid du Colombier 	HQTag		= 0x40,		/* Head of Queue Tag */
1797dd7cddfSDavid du Colombier 	OQTag		= 0x80,		/* Ordered Queue Tag */
1807dd7cddfSDavid du Colombier };
1817dd7cddfSDavid du Colombier 
1827dd7cddfSDavid du Colombier enum {					/* CCB control */
1837dd7cddfSDavid du Colombier 	NoDisc		= 0x08,		/* No disconnect */
1847dd7cddfSDavid du Colombier 	NoUnd		= 0x10,		/* No underrrun error report */
1857dd7cddfSDavid du Colombier 	NoData		= 0x20,		/* No data transfer */
1867dd7cddfSDavid du Colombier 	NoStat		= 0x40,		/* No CCB status if zero */
1877dd7cddfSDavid du Colombier 	NoIntr		= 0x80,		/* No Interrupts */
1887dd7cddfSDavid du Colombier };
1897dd7cddfSDavid du Colombier 
19059c21d95SDavid du Colombier typedef struct Ctlr Ctlr;
19159c21d95SDavid du Colombier struct Ctlr {
1927dd7cddfSDavid du Colombier 	int	port;			/* I/O port */
1937dd7cddfSDavid du Colombier 	int	id;			/* adapter SCSI id */
1947dd7cddfSDavid du Colombier 	int	bus;			/* 24 or 32 -bit */
1957dd7cddfSDavid du Colombier 	int	irq;
1967dd7cddfSDavid du Colombier 	int	wide;
1977dd7cddfSDavid du Colombier 	Pcidev*	pcidev;
1987dd7cddfSDavid du Colombier 	SDev*	sdev;
1997dd7cddfSDavid du Colombier 	int	spurious;
2007dd7cddfSDavid du Colombier 
2017dd7cddfSDavid du Colombier 	Lock	issuelock;
2027dd7cddfSDavid du Colombier 
2037dd7cddfSDavid du Colombier 	Lock	ccblock;
2047dd7cddfSDavid du Colombier 	QLock	ccbq;
2057dd7cddfSDavid du Colombier 	Rendez	ccbr;
2067dd7cddfSDavid du Colombier 
2077dd7cddfSDavid du Colombier 	Lock	mboxlock;
2087dd7cddfSDavid du Colombier 	void*	mb;			/* mailbox out + mailbox in */
2097dd7cddfSDavid du Colombier 	int	mbox;			/* current mailbox out index into mb */
2107dd7cddfSDavid du Colombier 	int	mbix;			/* current mailbox in index into mb */
2117dd7cddfSDavid du Colombier 
2127dd7cddfSDavid du Colombier 	Lock	cachelock;
2137dd7cddfSDavid du Colombier 	Ccb*	ccb;			/* list of free Ccb's */
2147dd7cddfSDavid du Colombier 	Ccb**	cache;			/* last completed Ccb */
21559c21d95SDavid du Colombier };
2167dd7cddfSDavid du Colombier 
2177dd7cddfSDavid du Colombier /*
2187dd7cddfSDavid du Colombier  * The number of mailboxes should be a multiple of 8 (4 for Mbox32)
2197dd7cddfSDavid du Colombier  * to ensure the boundary between the out and in mailboxes doesn't
2207dd7cddfSDavid du Colombier  * straddle a cache-line boundary.
2217dd7cddfSDavid du Colombier  * The number of Ccb's should be less than the number of mailboxes to
2227dd7cddfSDavid du Colombier  * ensure no queueing is necessary on mailbox allocation.
2237dd7cddfSDavid du Colombier  */
2247dd7cddfSDavid du Colombier enum {
2257dd7cddfSDavid du Colombier 	NMbox		= 8*8,		/* number of Mbox's */
2267dd7cddfSDavid du Colombier 	NCcb		= NMbox-1,	/* number of Ccb's */
2277dd7cddfSDavid du Colombier };
2287dd7cddfSDavid du Colombier 
22959cc4ca5SDavid du Colombier #define PADDR24(a, n)	((PADDR(a)+(n)) <= (1<<24))
2307dd7cddfSDavid du Colombier 
2317dd7cddfSDavid du Colombier static void
ccbfree(Ctlr * ctlr,Ccb * ccb)2327dd7cddfSDavid du Colombier ccbfree(Ctlr* ctlr, Ccb* ccb)
2337dd7cddfSDavid du Colombier {
2347dd7cddfSDavid du Colombier 	lock(&ctlr->ccblock);
2357dd7cddfSDavid du Colombier 	if(ctlr->bus == 24)
2367dd7cddfSDavid du Colombier 		((Ccb24*)ccb)->ccb = ctlr->ccb;
2377dd7cddfSDavid du Colombier 	else
2387dd7cddfSDavid du Colombier 		((Ccb32*)ccb)->ccb = ctlr->ccb;
2397dd7cddfSDavid du Colombier 	if(ctlr->ccb == nil)
2407dd7cddfSDavid du Colombier 		wakeup(&ctlr->ccbr);
2417dd7cddfSDavid du Colombier 	ctlr->ccb = ccb;
2427dd7cddfSDavid du Colombier 	unlock(&ctlr->ccblock);
2437dd7cddfSDavid du Colombier }
2447dd7cddfSDavid du Colombier 
2457dd7cddfSDavid du Colombier static int
ccbavailable(void * a)2467dd7cddfSDavid du Colombier ccbavailable(void* a)
2477dd7cddfSDavid du Colombier {
2487dd7cddfSDavid du Colombier 	return ((Ctlr*)a)->ccb != nil;
2497dd7cddfSDavid du Colombier }
2507dd7cddfSDavid du Colombier 
2517dd7cddfSDavid du Colombier static Ccb*
ccballoc(Ctlr * ctlr)2527dd7cddfSDavid du Colombier ccballoc(Ctlr* ctlr)
2537dd7cddfSDavid du Colombier {
2547dd7cddfSDavid du Colombier 	Ccb *ccb;
2557dd7cddfSDavid du Colombier 
2567dd7cddfSDavid du Colombier 	for(;;){
2577dd7cddfSDavid du Colombier 		lock(&ctlr->ccblock);
25859cc4ca5SDavid du Colombier 		if((ccb = ctlr->ccb) != nil){
2597dd7cddfSDavid du Colombier 			if(ctlr->bus == 24)
2607dd7cddfSDavid du Colombier 				 ctlr->ccb = ((Ccb24*)ccb)->ccb;
2617dd7cddfSDavid du Colombier 			else
2627dd7cddfSDavid du Colombier 				 ctlr->ccb = ((Ccb32*)ccb)->ccb;
2637dd7cddfSDavid du Colombier 			unlock(&ctlr->ccblock);
2647dd7cddfSDavid du Colombier 			break;
2657dd7cddfSDavid du Colombier 		}
2667dd7cddfSDavid du Colombier 
2677dd7cddfSDavid du Colombier 		unlock(&ctlr->ccblock);
2687dd7cddfSDavid du Colombier 		qlock(&ctlr->ccbq);
2697dd7cddfSDavid du Colombier 		if(waserror()){
2707dd7cddfSDavid du Colombier 			qunlock(&ctlr->ccbq);
2717dd7cddfSDavid du Colombier 			continue;
2727dd7cddfSDavid du Colombier 		}
2737dd7cddfSDavid du Colombier 		sleep(&ctlr->ccbr, ccbavailable, ctlr);
2747dd7cddfSDavid du Colombier 		qunlock(&ctlr->ccbq);
2757dd7cddfSDavid du Colombier 		poperror();
2767dd7cddfSDavid du Colombier 	}
2777dd7cddfSDavid du Colombier 
2787dd7cddfSDavid du Colombier 	return ccb;
2797dd7cddfSDavid du Colombier }
2807dd7cddfSDavid du Colombier 
2817dd7cddfSDavid du Colombier static int
done24(void * arg)2827dd7cddfSDavid du Colombier done24(void* arg)
2837dd7cddfSDavid du Colombier {
2847dd7cddfSDavid du Colombier 	return ((Ccb24*)arg)->done;
2857dd7cddfSDavid du Colombier }
2867dd7cddfSDavid du Colombier 
2877dd7cddfSDavid du Colombier static int
mylex24rio(SDreq * r)2887dd7cddfSDavid du Colombier mylex24rio(SDreq* r)
2897dd7cddfSDavid du Colombier {
2907dd7cddfSDavid du Colombier 	ulong p;
2917dd7cddfSDavid du Colombier 	Ctlr *ctlr;
2927dd7cddfSDavid du Colombier 	Ccb24 *ccb;
29359cc4ca5SDavid du Colombier 	Mbox24 *mb;
2947dd7cddfSDavid du Colombier 	uchar *data, lun, *sense;
2957dd7cddfSDavid du Colombier 	int d, n, btstat, sdstat, target;
2967dd7cddfSDavid du Colombier 
2977dd7cddfSDavid du Colombier 	ctlr = r->unit->dev->ctlr;
2987dd7cddfSDavid du Colombier 	target = r->unit->subno;
2997dd7cddfSDavid du Colombier 	lun = (r->cmd[1]>>5) & 0x07;
3007dd7cddfSDavid du Colombier 
3017dd7cddfSDavid du Colombier 	/*
3027dd7cddfSDavid du Colombier 	 * Ctlr->cache holds the last completed Ccb for this target if it
3037dd7cddfSDavid du Colombier 	 * returned 'check condition'.
3047dd7cddfSDavid du Colombier 	 * If this command is a request-sense and there is valid sense data
3057dd7cddfSDavid du Colombier 	 * from the last completed Ccb, return it immediately.
3067dd7cddfSDavid du Colombier 	 */
3077dd7cddfSDavid du Colombier 	lock(&ctlr->cachelock);
30859cc4ca5SDavid du Colombier 	if((ccb = ctlr->cache[target]) != nil){
3097dd7cddfSDavid du Colombier 		ctlr->cache[target] = nil;
3107dd7cddfSDavid du Colombier 		if(r->cmd[0] == 0x03
3117dd7cddfSDavid du Colombier 		&& ccb->sdstat == SDcheck && lun == ((ccb->cs[1]>>5) & 0x07)){
3127dd7cddfSDavid du Colombier 			unlock(&ctlr->cachelock);
3137dd7cddfSDavid du Colombier 			if(r->dlen){
3147dd7cddfSDavid du Colombier 				sense = &ccb->cs[ccb->cdblen];
3157dd7cddfSDavid du Colombier 				n = 8+sense[7];
3167dd7cddfSDavid du Colombier 				if(n > r->dlen)
3177dd7cddfSDavid du Colombier 					n = r->dlen;
3187dd7cddfSDavid du Colombier 				memmove(r->data, sense, n);
3197dd7cddfSDavid du Colombier 				r->rlen = n;
3207dd7cddfSDavid du Colombier 			}
3217dd7cddfSDavid du Colombier 			ccbfree(ctlr, (Ccb*)ccb);
3227dd7cddfSDavid du Colombier 			return SDok;
3237dd7cddfSDavid du Colombier 		}
3247dd7cddfSDavid du Colombier 	}
3257dd7cddfSDavid du Colombier 	unlock(&ctlr->cachelock);
3267dd7cddfSDavid du Colombier 	if(ccb == nil)
3277dd7cddfSDavid du Colombier 		ccb = ccballoc(ctlr);
3287dd7cddfSDavid du Colombier 
3297dd7cddfSDavid du Colombier 	/*
3307dd7cddfSDavid du Colombier 	 * Check if the transfer is to memory above the 24-bit limit the
3317dd7cddfSDavid du Colombier 	 * controller can address. If it is, try to allocate a temporary
3327dd7cddfSDavid du Colombier 	 * buffer as a staging area.
3337dd7cddfSDavid du Colombier 	 */
3347dd7cddfSDavid du Colombier 	n = r->dlen;
3357dd7cddfSDavid du Colombier 	if(n && !PADDR24(r->data, n)){
3367dd7cddfSDavid du Colombier 		data = mallocz(n, 0);
3377dd7cddfSDavid du Colombier 		if(data == nil || !PADDR24(data, n)){
3387dd7cddfSDavid du Colombier 			if(data != nil){
3397dd7cddfSDavid du Colombier 				free(data);
3407dd7cddfSDavid du Colombier 				ccb->data = nil;
3417dd7cddfSDavid du Colombier 			}
3427dd7cddfSDavid du Colombier 			ccbfree(ctlr, (Ccb*)ccb);
3437dd7cddfSDavid du Colombier 			return SDmalloc;
3447dd7cddfSDavid du Colombier 		}
3457dd7cddfSDavid du Colombier 		if(r->write)
34659cc4ca5SDavid du Colombier 			memmove(data, r->data, n);
34759cc4ca5SDavid du Colombier 		ccb->data = r->data;
3487dd7cddfSDavid du Colombier 	}
3497dd7cddfSDavid du Colombier 	else
3507dd7cddfSDavid du Colombier 		data = r->data;
3517dd7cddfSDavid du Colombier 
3527dd7cddfSDavid du Colombier 	/*
3537dd7cddfSDavid du Colombier 	 * Fill in the ccb.
3547dd7cddfSDavid du Colombier 	 */
3557dd7cddfSDavid du Colombier 	ccb->opcode = Ordl;
3567dd7cddfSDavid du Colombier 
3577dd7cddfSDavid du Colombier 	ccb->datadir = (target<<5)|lun;
3587dd7cddfSDavid du Colombier 	if(n == 0)
3597dd7cddfSDavid du Colombier 		ccb->datadir |= CCBdataout|CCBdatain;
3607dd7cddfSDavid du Colombier 	else if(!r->write)
3617dd7cddfSDavid du Colombier 		ccb->datadir |= CCBdatain;
3627dd7cddfSDavid du Colombier 	else
3637dd7cddfSDavid du Colombier 		ccb->datadir |= CCBdataout;
3647dd7cddfSDavid du Colombier 
3657dd7cddfSDavid du Colombier 	ccb->cdblen = r->clen;
3667dd7cddfSDavid du Colombier 	ccb->senselen = 0xFF;
3677dd7cddfSDavid du Colombier 
3687dd7cddfSDavid du Colombier 	ccb->datalen[0] = n>>16;
3697dd7cddfSDavid du Colombier 	ccb->datalen[1] = n>>8;
3707dd7cddfSDavid du Colombier 	ccb->datalen[2] = n;
3712c16e87eSDavid du Colombier 	if(data == nil)
3722c16e87eSDavid du Colombier 		p = 0;
3732c16e87eSDavid du Colombier 	else
3747dd7cddfSDavid du Colombier 		p = PADDR(data);
3757dd7cddfSDavid du Colombier 	ccb->dataptr[0] = p>>16;
3767dd7cddfSDavid du Colombier 	ccb->dataptr[1] = p>>8;
3777dd7cddfSDavid du Colombier 	ccb->dataptr[2] = p;
3787dd7cddfSDavid du Colombier 
3797dd7cddfSDavid du Colombier 	ccb->linkptr[0] = ccb->linkptr[1] = ccb->linkptr[2] = 0;
3807dd7cddfSDavid du Colombier 	ccb->linkid = 0;
3817dd7cddfSDavid du Colombier 	ccb->btstat = ccb->sdstat = 0;
3827dd7cddfSDavid du Colombier 	ccb->reserved[0] = ccb->reserved[1] = 0;
3837dd7cddfSDavid du Colombier 
3847dd7cddfSDavid du Colombier 	memmove(ccb->cs, r->cmd, r->clen);
3857dd7cddfSDavid du Colombier 
3867dd7cddfSDavid du Colombier 	/*
3877dd7cddfSDavid du Colombier 	 * There's one more mbox than there there is
3887dd7cddfSDavid du Colombier 	 * ccb so there is always one free.
3897dd7cddfSDavid du Colombier 	 */
3907dd7cddfSDavid du Colombier 	lock(&ctlr->mboxlock);
3917dd7cddfSDavid du Colombier 	mb = ctlr->mb;
3927dd7cddfSDavid du Colombier 	mb += ctlr->mbox;
3937dd7cddfSDavid du Colombier 	p = PADDR(ccb);
3947dd7cddfSDavid du Colombier 	mb->ccb[0] = p>>16;
3957dd7cddfSDavid du Colombier 	mb->ccb[1] = p>>8;
3967dd7cddfSDavid du Colombier 	mb->ccb[2] = p;
3977dd7cddfSDavid du Colombier 	mb->code = Mbostart;
3987dd7cddfSDavid du Colombier 	ctlr->mbox++;
3997dd7cddfSDavid du Colombier 	if(ctlr->mbox >= NMbox)
4007dd7cddfSDavid du Colombier 		ctlr->mbox = 0;
4017dd7cddfSDavid du Colombier 
4027dd7cddfSDavid du Colombier 	/*
4037dd7cddfSDavid du Colombier 	 * This command does not require Hardy
4047dd7cddfSDavid du Colombier 	 * and doesn't generate a Cmdc interrupt.
4057dd7cddfSDavid du Colombier 	 */
4067dd7cddfSDavid du Colombier 	ccb->done = 0;
4077dd7cddfSDavid du Colombier 	outb(ctlr->port+Rcpr, Cstart);
4087dd7cddfSDavid du Colombier 	unlock(&ctlr->mboxlock);
4097dd7cddfSDavid du Colombier 
4107dd7cddfSDavid du Colombier 	/*
4117dd7cddfSDavid du Colombier 	 * Wait for the request to complete and return the status.
4127dd7cddfSDavid du Colombier 	 * Since the buffer is not reference counted cannot return
4137dd7cddfSDavid du Colombier 	 * until the DMA is done writing into the buffer so the caller
4147dd7cddfSDavid du Colombier 	 * cannot free the buffer prematurely.
4157dd7cddfSDavid du Colombier 	 */
4167dd7cddfSDavid du Colombier 	while(waserror())
4177dd7cddfSDavid du Colombier 		;
4187dd7cddfSDavid du Colombier 	sleep(ccb, done24, ccb);
4197dd7cddfSDavid du Colombier 	poperror();
4207dd7cddfSDavid du Colombier 
4217dd7cddfSDavid du Colombier 	/*
4227dd7cddfSDavid du Colombier 	 * Save the status and patch up the number of
4237dd7cddfSDavid du Colombier 	 * bytes actually transferred.
4247dd7cddfSDavid du Colombier 	 * There's a firmware bug on some 956C controllers
4257dd7cddfSDavid du Colombier 	 * which causes the return count from a successful
4267dd7cddfSDavid du Colombier 	 * READ CAPACITY not be updated, so fix it here.
4277dd7cddfSDavid du Colombier 	 */
4287dd7cddfSDavid du Colombier 	sdstat = ccb->sdstat;
4297dd7cddfSDavid du Colombier 	btstat = ccb->btstat;
4307dd7cddfSDavid du Colombier 
4317dd7cddfSDavid du Colombier 	d = ccb->datalen[0]<<16;
4327dd7cddfSDavid du Colombier 	d |= ccb->datalen[1]<<8;
4337dd7cddfSDavid du Colombier 	d |= ccb->datalen[2];
4347dd7cddfSDavid du Colombier 	if(ccb->cs[0] == 0x25 && sdstat == SDok)
4357dd7cddfSDavid du Colombier 		d = 0;
4367dd7cddfSDavid du Colombier 	n -= d;
4377dd7cddfSDavid du Colombier 	r->rlen = n;
4387dd7cddfSDavid du Colombier 
4397dd7cddfSDavid du Colombier 	/*
4407dd7cddfSDavid du Colombier 	 * Tidy things up if a staging area was used for the data,
4417dd7cddfSDavid du Colombier 	 */
44259cc4ca5SDavid du Colombier 	if(ccb->data != nil){
4437dd7cddfSDavid du Colombier 		if(sdstat == SDok && btstat == 0 && !r->write)
4447dd7cddfSDavid du Colombier 			memmove(ccb->data, data, n);
4457dd7cddfSDavid du Colombier 		free(data);
4467dd7cddfSDavid du Colombier 		ccb->data = nil;
4477dd7cddfSDavid du Colombier 	}
4487dd7cddfSDavid du Colombier 
4497dd7cddfSDavid du Colombier 	/*
4507dd7cddfSDavid du Colombier 	 * If there was a check-condition, save the
4517dd7cddfSDavid du Colombier 	 * ccb for a possible request-sense command.
4527dd7cddfSDavid du Colombier 	 */
4537dd7cddfSDavid du Colombier 	if(sdstat == SDcheck){
4547dd7cddfSDavid du Colombier 		if(r->flags & SDnosense){
4557dd7cddfSDavid du Colombier 			lock(&ctlr->cachelock);
4567dd7cddfSDavid du Colombier 			if(ctlr->cache[target])
4577dd7cddfSDavid du Colombier 				ccbfree(ctlr, ctlr->cache[target]);
4587dd7cddfSDavid du Colombier 			ctlr->cache[target] = (Ccb*)ccb;
4597dd7cddfSDavid du Colombier 			unlock(&ctlr->cachelock);
4607dd7cddfSDavid du Colombier 			return SDcheck;
4617dd7cddfSDavid du Colombier 		}
4627dd7cddfSDavid du Colombier 		sense = &ccb->cs[ccb->cdblen];
4637dd7cddfSDavid du Colombier 		n = 8+sense[7];
4647dd7cddfSDavid du Colombier 		if(n > sizeof(r->sense)-1)
4657dd7cddfSDavid du Colombier 			n = sizeof(r->sense)-1;
4667dd7cddfSDavid du Colombier 		memmove(r->sense, sense, n);
4677dd7cddfSDavid du Colombier 		r->flags |= SDvalidsense;
4687dd7cddfSDavid du Colombier 	}
4697dd7cddfSDavid du Colombier 	ccbfree(ctlr, (Ccb*)ccb);
4707dd7cddfSDavid du Colombier 
4717dd7cddfSDavid du Colombier 	if(btstat){
4727dd7cddfSDavid du Colombier 		if(btstat == 0x11)
4737dd7cddfSDavid du Colombier 			return SDtimeout;
4747dd7cddfSDavid du Colombier 		return SDeio;
4757dd7cddfSDavid du Colombier 	}
4767dd7cddfSDavid du Colombier 	return sdstat;
4777dd7cddfSDavid du Colombier }
4787dd7cddfSDavid du Colombier 
4797dd7cddfSDavid du Colombier static void
mylex24interrupt(Ureg *,void * arg)4807dd7cddfSDavid du Colombier mylex24interrupt(Ureg*, void* arg)
4817dd7cddfSDavid du Colombier {
4827dd7cddfSDavid du Colombier 	ulong pa;
4837dd7cddfSDavid du Colombier 	Ctlr *ctlr;
4847dd7cddfSDavid du Colombier 	Ccb24 *ccb;
4857dd7cddfSDavid du Colombier 	Mbox24 *mb, *mbox;
4867dd7cddfSDavid du Colombier 	int port, rinterrupt, rstatus;
4877dd7cddfSDavid du Colombier 
4887dd7cddfSDavid du Colombier 	ctlr = arg;
4897dd7cddfSDavid du Colombier 	port = ctlr->port;
4907dd7cddfSDavid du Colombier 
4917dd7cddfSDavid du Colombier 	/*
4927dd7cddfSDavid du Colombier 	 * Save and clear the interrupt(s). The only
4937dd7cddfSDavid du Colombier 	 * interrupts expected are Cmdc, which is ignored,
4947dd7cddfSDavid du Colombier 	 * and Imbl which means something completed.
4957dd7cddfSDavid du Colombier 	 * There's one spurious interrupt left over from
4967dd7cddfSDavid du Colombier 	 * initialisation, ignore it.
4977dd7cddfSDavid du Colombier 	 */
4987dd7cddfSDavid du Colombier 	rinterrupt = inb(port+Rinterrupt);
4997dd7cddfSDavid du Colombier 	rstatus = inb(port+Rstatus);
5007dd7cddfSDavid du Colombier 	outb(port+Rcontrol, Rint);
5017dd7cddfSDavid du Colombier 	if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++)
5027dd7cddfSDavid du Colombier 		print("%s: interrupt 0x%2.2ux\n",
5037dd7cddfSDavid du Colombier 			ctlr->sdev->name, rinterrupt);
5047dd7cddfSDavid du Colombier 	if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
5057dd7cddfSDavid du Colombier 		print("%s: command invalid\n", ctlr->sdev->name);
5067dd7cddfSDavid du Colombier 
5077dd7cddfSDavid du Colombier 	/*
5087dd7cddfSDavid du Colombier 	 * Look for something in the mail.
5097dd7cddfSDavid du Colombier 	 * If there is, save the status, free the mailbox
5107dd7cddfSDavid du Colombier 	 * and wakeup whoever.
5117dd7cddfSDavid du Colombier 	 */
5127dd7cddfSDavid du Colombier 	mb = ctlr->mb;
5137dd7cddfSDavid du Colombier 	for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){
5147dd7cddfSDavid du Colombier 		pa = (mbox->ccb[0]<<16)|(mbox->ccb[1]<<8)|mbox->ccb[2];
5157dd7cddfSDavid du Colombier 		ccb = BPA2K(pa, BUSUNKNOWN);
5167dd7cddfSDavid du Colombier 		mbox->code = 0;
5177dd7cddfSDavid du Colombier 		ccb->done = 1;
5187dd7cddfSDavid du Colombier 		wakeup(ccb);
5197dd7cddfSDavid du Colombier 
5207dd7cddfSDavid du Colombier 		ctlr->mbix++;
5217dd7cddfSDavid du Colombier 		if(ctlr->mbix >= NMbox+NMbox)
5227dd7cddfSDavid du Colombier 			ctlr->mbix = NMbox;
5237dd7cddfSDavid du Colombier 	}
5247dd7cddfSDavid du Colombier }
5257dd7cddfSDavid du Colombier 
5267dd7cddfSDavid du Colombier static int
done32(void * arg)5277dd7cddfSDavid du Colombier done32(void* arg)
5287dd7cddfSDavid du Colombier {
5297dd7cddfSDavid du Colombier 	return ((Ccb32*)arg)->done;
5307dd7cddfSDavid du Colombier }
5317dd7cddfSDavid du Colombier 
5327dd7cddfSDavid du Colombier static int
mylex32rio(SDreq * r)5337dd7cddfSDavid du Colombier mylex32rio(SDreq* r)
5347dd7cddfSDavid du Colombier {
5357dd7cddfSDavid du Colombier 	ulong p;
5367dd7cddfSDavid du Colombier 	uchar lun;
5377dd7cddfSDavid du Colombier 	Ctlr *ctlr;
5387dd7cddfSDavid du Colombier 	Ccb32 *ccb;
5397dd7cddfSDavid du Colombier 	Mbox32 *mb;
5407dd7cddfSDavid du Colombier 	int d, n, btstat, sdstat, target;
5417dd7cddfSDavid du Colombier 
5427dd7cddfSDavid du Colombier 	ctlr = r->unit->dev->ctlr;
5437dd7cddfSDavid du Colombier 	target = r->unit->subno;
5447dd7cddfSDavid du Colombier 	lun = (r->cmd[1]>>5) & 0x07;
5457dd7cddfSDavid du Colombier 
5467dd7cddfSDavid du Colombier 	/*
5477dd7cddfSDavid du Colombier 	 * Ctlr->cache holds the last completed Ccb for this target if it
5487dd7cddfSDavid du Colombier 	 * returned 'check condition'.
5497dd7cddfSDavid du Colombier 	 * If this command is a request-sense and there is valid sense data
5507dd7cddfSDavid du Colombier 	 * from the last completed Ccb, return it immediately.
5517dd7cddfSDavid du Colombier 	 */
5527dd7cddfSDavid du Colombier 	lock(&ctlr->cachelock);
55359cc4ca5SDavid du Colombier 	if((ccb = ctlr->cache[target]) != nil){
5547dd7cddfSDavid du Colombier 		ctlr->cache[target] = nil;
5557dd7cddfSDavid du Colombier 		if(r->cmd[0] == 0x03
5567dd7cddfSDavid du Colombier 		&& ccb->sdstat == SDcheck && lun == (ccb->luntag & 0x07)){
5577dd7cddfSDavid du Colombier 			unlock(&ctlr->cachelock);
5587dd7cddfSDavid du Colombier 			if(r->dlen){
5597dd7cddfSDavid du Colombier 				n = 8+ccb->sense[7];
5607dd7cddfSDavid du Colombier 				if(n > r->dlen)
5617dd7cddfSDavid du Colombier 					n = r->dlen;
5627dd7cddfSDavid du Colombier 				memmove(r->data, ccb->sense, n);
5637dd7cddfSDavid du Colombier 				r->rlen = n;
5647dd7cddfSDavid du Colombier 			}
5657dd7cddfSDavid du Colombier 			ccbfree(ctlr, (Ccb*)ccb);
5667dd7cddfSDavid du Colombier 			return SDok;
5677dd7cddfSDavid du Colombier 		}
5687dd7cddfSDavid du Colombier 	}
5697dd7cddfSDavid du Colombier 	unlock(&ctlr->cachelock);
5707dd7cddfSDavid du Colombier 	if(ccb == nil)
5717dd7cddfSDavid du Colombier 		ccb = ccballoc(ctlr);
5727dd7cddfSDavid du Colombier 
5737dd7cddfSDavid du Colombier 	/*
5747dd7cddfSDavid du Colombier 	 * Fill in the ccb.
5757dd7cddfSDavid du Colombier 	 */
5767dd7cddfSDavid du Colombier 	ccb->opcode = Ordl;
5777dd7cddfSDavid du Colombier 
5787dd7cddfSDavid du Colombier 	n = r->dlen;
5797dd7cddfSDavid du Colombier 	if(n == 0)
58059cc4ca5SDavid du Colombier 		ccb->datadir = CCBdataout|CCBdatain;
5817dd7cddfSDavid du Colombier 	else if(!r->write)
58259cc4ca5SDavid du Colombier 		ccb->datadir = CCBdatain;
5837dd7cddfSDavid du Colombier 	else
58459cc4ca5SDavid du Colombier 		ccb->datadir = CCBdataout;
5857dd7cddfSDavid du Colombier 
5867dd7cddfSDavid du Colombier 	ccb->cdblen = r->clen;
5877dd7cddfSDavid du Colombier 
5887dd7cddfSDavid du Colombier 	ccb->datalen[0] = n;
5897dd7cddfSDavid du Colombier 	ccb->datalen[1] = n>>8;
5907dd7cddfSDavid du Colombier 	ccb->datalen[2] = n>>16;
5917dd7cddfSDavid du Colombier 	ccb->datalen[3] = n>>24;
5922c16e87eSDavid du Colombier 	if(r->data == nil)
5932c16e87eSDavid du Colombier 		p = 0;
5942c16e87eSDavid du Colombier 	else
5957dd7cddfSDavid du Colombier 		p = PADDR(r->data);
5967dd7cddfSDavid du Colombier 	ccb->dataptr[0] = p;
5977dd7cddfSDavid du Colombier 	ccb->dataptr[1] = p>>8;
5987dd7cddfSDavid du Colombier 	ccb->dataptr[2] = p>>16;
5997dd7cddfSDavid du Colombier 	ccb->dataptr[3] = p>>24;
6007dd7cddfSDavid du Colombier 
6017dd7cddfSDavid du Colombier 	ccb->targetid = target;
6027dd7cddfSDavid du Colombier 	ccb->luntag = lun;
6037dd7cddfSDavid du Colombier 	if(r->unit->inquiry[7] & 0x02)
6046bfdd830SDavid du Colombier 		if(ctlr->wide)
6056bfdd830SDavid du Colombier 			ccb->datadir |= SQTag|TagEnable;
6066bfdd830SDavid du Colombier 		else
6077dd7cddfSDavid du Colombier 			ccb->luntag |= SQTag|TagEnable;
6087dd7cddfSDavid du Colombier 	memmove(ccb->cdb, r->cmd, r->clen);
6097dd7cddfSDavid du Colombier 	ccb->btstat = ccb->sdstat = 0;
6107dd7cddfSDavid du Colombier 	ccb->ccbctl = 0;
6117dd7cddfSDavid du Colombier 
6127dd7cddfSDavid du Colombier 	/*
6137dd7cddfSDavid du Colombier 	 * There's one more mbox than there there is
6147dd7cddfSDavid du Colombier 	 * ccb so there is always one free.
6157dd7cddfSDavid du Colombier 	 */
6167dd7cddfSDavid du Colombier 	lock(&ctlr->mboxlock);
6177dd7cddfSDavid du Colombier 	mb = ctlr->mb;
6187dd7cddfSDavid du Colombier 	mb += ctlr->mbox;
6197dd7cddfSDavid du Colombier 	p = PADDR(ccb);
6207dd7cddfSDavid du Colombier 	mb->ccb[0] = p;
6217dd7cddfSDavid du Colombier 	mb->ccb[1] = p>>8;
6227dd7cddfSDavid du Colombier 	mb->ccb[2] = p>>16;
6237dd7cddfSDavid du Colombier 	mb->ccb[3] = p>>24;
6247dd7cddfSDavid du Colombier 	mb->code = Mbostart;
6257dd7cddfSDavid du Colombier 	ctlr->mbox++;
6267dd7cddfSDavid du Colombier 	if(ctlr->mbox >= NMbox)
6277dd7cddfSDavid du Colombier 		ctlr->mbox = 0;
6287dd7cddfSDavid du Colombier 
6297dd7cddfSDavid du Colombier 	/*
6307dd7cddfSDavid du Colombier 	 * This command does not require Hardy
6317dd7cddfSDavid du Colombier 	 * and doesn't generate a Cmdc interrupt.
6327dd7cddfSDavid du Colombier 	 */
6337dd7cddfSDavid du Colombier 	ccb->done = 0;
6347dd7cddfSDavid du Colombier 	outb(ctlr->port+Rcpr, Cstart);
6357dd7cddfSDavid du Colombier 	unlock(&ctlr->mboxlock);
6367dd7cddfSDavid du Colombier 
6377dd7cddfSDavid du Colombier 	/*
6387dd7cddfSDavid du Colombier 	 * Wait for the request to complete and return the status.
6397dd7cddfSDavid du Colombier 	 * Since the buffer is not reference counted cannot return
6407dd7cddfSDavid du Colombier 	 * until the DMA is done writing into the buffer so the caller
6417dd7cddfSDavid du Colombier 	 * cannot free the buffer prematurely.
6427dd7cddfSDavid du Colombier 	 */
6437dd7cddfSDavid du Colombier 	while(waserror())
6447dd7cddfSDavid du Colombier 		;
64580ee5cbfSDavid du Colombier 	sleep(ccb, done32, ccb);
6467dd7cddfSDavid du Colombier 	poperror();
6477dd7cddfSDavid du Colombier 
6487dd7cddfSDavid du Colombier 	/*
6497dd7cddfSDavid du Colombier 	 * Save the status and patch up the number of
6507dd7cddfSDavid du Colombier 	 * bytes actually transferred.
6517dd7cddfSDavid du Colombier 	 * There's a firmware bug on some 956C controllers
6527dd7cddfSDavid du Colombier 	 * which causes the return count from a successful
6537dd7cddfSDavid du Colombier 	 * READ CAPACITY not to be updated, so fix it here.
6547dd7cddfSDavid du Colombier 	 */
6557dd7cddfSDavid du Colombier 	sdstat = ccb->sdstat;
6567dd7cddfSDavid du Colombier 	btstat = ccb->btstat;
6577dd7cddfSDavid du Colombier 
6587dd7cddfSDavid du Colombier 	d = ccb->datalen[0];
6597dd7cddfSDavid du Colombier 	d |= (ccb->datalen[1]<<8);
6607dd7cddfSDavid du Colombier 	d |= (ccb->datalen[2]<<16);
6617dd7cddfSDavid du Colombier 	d |= (ccb->datalen[3]<<24);
6627dd7cddfSDavid du Colombier 	if(ccb->cdb[0] == 0x25 && sdstat == SDok)
6637dd7cddfSDavid du Colombier 		d = 0;
6647dd7cddfSDavid du Colombier 	n -= d;
6657dd7cddfSDavid du Colombier 	r->rlen = n;
6667dd7cddfSDavid du Colombier 
6677dd7cddfSDavid du Colombier 	/*
6687dd7cddfSDavid du Colombier 	 * If there was a check-condition, save the
6697dd7cddfSDavid du Colombier 	 * ccb for a possible request-sense command.
6707dd7cddfSDavid du Colombier 	 */
6717dd7cddfSDavid du Colombier 	if(sdstat == SDcheck){
6727dd7cddfSDavid du Colombier 		if(r->flags & SDnosense){
6737dd7cddfSDavid du Colombier 			lock(&ctlr->cachelock);
6747dd7cddfSDavid du Colombier 			if(ctlr->cache[target])
6757dd7cddfSDavid du Colombier 				ccbfree(ctlr, ctlr->cache[target]);
6767dd7cddfSDavid du Colombier 			ctlr->cache[target] = (Ccb*)ccb;
6777dd7cddfSDavid du Colombier 			unlock(&ctlr->cachelock);
6787dd7cddfSDavid du Colombier 			return SDcheck;
6797dd7cddfSDavid du Colombier 		}
6807dd7cddfSDavid du Colombier 		n = 8+ccb->sense[7];
6817dd7cddfSDavid du Colombier 		if(n > sizeof(r->sense)-1)
6827dd7cddfSDavid du Colombier 			n = sizeof(r->sense)-1;
6837dd7cddfSDavid du Colombier 		memmove(r->sense, ccb->sense, n);
6847dd7cddfSDavid du Colombier 		r->flags |= SDvalidsense;
6857dd7cddfSDavid du Colombier 	}
6867dd7cddfSDavid du Colombier 	ccbfree(ctlr, (Ccb*)ccb);
6877dd7cddfSDavid du Colombier 
6887dd7cddfSDavid du Colombier 	if(btstat){
6897dd7cddfSDavid du Colombier 		if(btstat == 0x11)
6907dd7cddfSDavid du Colombier 			return SDtimeout;
6917dd7cddfSDavid du Colombier 		return SDeio;
6927dd7cddfSDavid du Colombier 	}
6937dd7cddfSDavid du Colombier 	return sdstat;
6947dd7cddfSDavid du Colombier }
6957dd7cddfSDavid du Colombier 
6967dd7cddfSDavid du Colombier static void
mylex32interrupt(Ureg *,void * arg)6977dd7cddfSDavid du Colombier mylex32interrupt(Ureg*, void* arg)
6987dd7cddfSDavid du Colombier {
6997dd7cddfSDavid du Colombier 	ulong pa;
7007dd7cddfSDavid du Colombier 	Ctlr *ctlr;
7017dd7cddfSDavid du Colombier 	Ccb32 *ccb;
7027dd7cddfSDavid du Colombier 	Mbox32 *mb, *mbox;
7037dd7cddfSDavid du Colombier 	int port, rinterrupt, rstatus;
7047dd7cddfSDavid du Colombier 
7057dd7cddfSDavid du Colombier 	ctlr = arg;
7067dd7cddfSDavid du Colombier 	port = ctlr->port;
7077dd7cddfSDavid du Colombier 
7087dd7cddfSDavid du Colombier 	/*
7097dd7cddfSDavid du Colombier 	 * Save and clear the interrupt(s). The only
7107dd7cddfSDavid du Colombier 	 * interrupts expected are Cmdc, which is ignored,
7117dd7cddfSDavid du Colombier 	 * and Imbl which means something completed.
7127dd7cddfSDavid du Colombier 	 * There's one spurious interrupt left over from
7137dd7cddfSDavid du Colombier 	 * initialisation, ignore it.
714d649fdd7SDavid du Colombier 	 * In order to share PCI IRQs, just ignore spurious interrupts.
7157dd7cddfSDavid du Colombier 	 */
7167dd7cddfSDavid du Colombier 	rinterrupt = inb(port+Rinterrupt);
7177dd7cddfSDavid du Colombier 	rstatus = inb(port+Rstatus);
7187dd7cddfSDavid du Colombier 	outb(port+Rcontrol, Rint);
719d649fdd7SDavid du Colombier 	if(0 && (rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++)
7207dd7cddfSDavid du Colombier 		print("%s: interrupt 0x%2.2ux\n",
7217dd7cddfSDavid du Colombier 			ctlr->sdev->name, rinterrupt);
7227dd7cddfSDavid du Colombier 	if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
7237dd7cddfSDavid du Colombier 		print("%s: command invalid\n", ctlr->sdev->name);
7247dd7cddfSDavid du Colombier 
7257dd7cddfSDavid du Colombier 	/*
7267dd7cddfSDavid du Colombier 	 * Look for something in the mail.
7277dd7cddfSDavid du Colombier 	 * If there is, free the mailbox and wakeup whoever.
7287dd7cddfSDavid du Colombier 	 */
7297dd7cddfSDavid du Colombier 	mb = ctlr->mb;
7307dd7cddfSDavid du Colombier 	for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){
7317dd7cddfSDavid du Colombier 		pa = (mbox->ccb[3]<<24)
7327dd7cddfSDavid du Colombier 		    |(mbox->ccb[2]<<16)
7337dd7cddfSDavid du Colombier 		    |(mbox->ccb[1]<<8)
7347dd7cddfSDavid du Colombier 		    |mbox->ccb[0];
7357dd7cddfSDavid du Colombier 		if(ctlr->pcidev)
7367dd7cddfSDavid du Colombier 			ccb = BPA2K(pa, ctlr->pcidev->tbdf);
7377dd7cddfSDavid du Colombier 		else
7387dd7cddfSDavid du Colombier 			ccb = BPA2K(pa, BUSUNKNOWN);
7397dd7cddfSDavid du Colombier 		mbox->code = 0;
7407dd7cddfSDavid du Colombier 		ccb->done = 1;
7417dd7cddfSDavid du Colombier 		wakeup(ccb);
7427dd7cddfSDavid du Colombier 
7437dd7cddfSDavid du Colombier 		ctlr->mbix++;
7447dd7cddfSDavid du Colombier 		if(ctlr->mbix >= NMbox+NMbox)
7457dd7cddfSDavid du Colombier 			ctlr->mbix = NMbox;
7467dd7cddfSDavid du Colombier 	}
7477dd7cddfSDavid du Colombier }
7487dd7cddfSDavid du Colombier 
7497dd7cddfSDavid du Colombier static int
mylexrio(SDreq * r)7507dd7cddfSDavid du Colombier mylexrio(SDreq* r)
7517dd7cddfSDavid du Colombier {
752223a736eSDavid du Colombier 	int subno;
7537dd7cddfSDavid du Colombier 	Ctlr *ctlr;
7547dd7cddfSDavid du Colombier 
755223a736eSDavid du Colombier 	subno = r->unit->subno;
7567dd7cddfSDavid du Colombier 	ctlr = r->unit->dev->ctlr;
757223a736eSDavid du Colombier 	if(subno == ctlr->id || (!ctlr->wide && subno >= 8))
7587dd7cddfSDavid du Colombier 		r->status = SDtimeout;
7597dd7cddfSDavid du Colombier 	else if(ctlr->bus == 24)
7607dd7cddfSDavid du Colombier 		r->status = mylex24rio(r);
7617dd7cddfSDavid du Colombier 	else
7627dd7cddfSDavid du Colombier 		r->status = mylex32rio(r);
7637dd7cddfSDavid du Colombier 	return r->status;
7647dd7cddfSDavid du Colombier }
7657dd7cddfSDavid du Colombier 
7667dd7cddfSDavid du Colombier /*
7677dd7cddfSDavid du Colombier  * Issue a command to a controller. The command and its length is
7687dd7cddfSDavid du Colombier  * contained in cmd and cmdlen. If any data is to be
7697dd7cddfSDavid du Colombier  * returned, datalen should be non-zero, and the returned data
7707dd7cddfSDavid du Colombier  * will be placed in data.
7717dd7cddfSDavid du Colombier  * If Cmdc is set, bail out, the invalid command will be handled
7727dd7cddfSDavid du Colombier  * when the interrupt is processed.
7737dd7cddfSDavid du Colombier  */
7747dd7cddfSDavid du Colombier static void
issueio(int port,uchar * cmd,int cmdlen,uchar * data,int datalen)7757dd7cddfSDavid du Colombier issueio(int port, uchar* cmd, int cmdlen, uchar* data, int datalen)
7767dd7cddfSDavid du Colombier {
7777dd7cddfSDavid du Colombier 	int len;
7787dd7cddfSDavid du Colombier 
7797dd7cddfSDavid du Colombier 	if(cmd[0] != Cstart && cmd[0] != Ceombri){
7807dd7cddfSDavid du Colombier 		while(!(inb(port+Rstatus) & Hardy))
7817dd7cddfSDavid du Colombier 			;
7827dd7cddfSDavid du Colombier 	}
7837dd7cddfSDavid du Colombier 	outb(port+Rcpr, cmd[0]);
7847dd7cddfSDavid du Colombier 
7857dd7cddfSDavid du Colombier 	len = 1;
7867dd7cddfSDavid du Colombier 	while(len < cmdlen){
7877dd7cddfSDavid du Colombier 		if(!(inb(port+Rstatus) & Cprbsy)){
7887dd7cddfSDavid du Colombier 			outb(port+Rcpr, cmd[len]);
7897dd7cddfSDavid du Colombier 			len++;
7907dd7cddfSDavid du Colombier 		}
7917dd7cddfSDavid du Colombier 		if(inb(port+Rinterrupt) & Cmdc)
7927dd7cddfSDavid du Colombier 			return;
7937dd7cddfSDavid du Colombier 	}
7947dd7cddfSDavid du Colombier 
7957dd7cddfSDavid du Colombier 	if(datalen){
7967dd7cddfSDavid du Colombier 		len = 0;
7977dd7cddfSDavid du Colombier 		while(len < datalen){
7987dd7cddfSDavid du Colombier 			if(inb(port+Rstatus) & Dirrdy){
7997dd7cddfSDavid du Colombier 				data[len] = inb(port+Rdatain);
8007dd7cddfSDavid du Colombier 				len++;
8017dd7cddfSDavid du Colombier 			}
8027dd7cddfSDavid du Colombier 			if(inb(port+Rinterrupt) & Cmdc)
8037dd7cddfSDavid du Colombier 				return;
8047dd7cddfSDavid du Colombier 		}
8057dd7cddfSDavid du Colombier 	}
8067dd7cddfSDavid du Colombier }
8077dd7cddfSDavid du Colombier 
8087dd7cddfSDavid du Colombier /*
8097dd7cddfSDavid du Colombier  * Issue a command to a controller, wait for it to complete then
8107dd7cddfSDavid du Colombier  * try to reset the interrupt. Should only be called at initialisation.
8117dd7cddfSDavid du Colombier  */
8127dd7cddfSDavid du Colombier static int
issue(Ctlr * ctlr,uchar * cmd,int cmdlen,uchar * data,int datalen)8137dd7cddfSDavid du Colombier issue(Ctlr* ctlr, uchar* cmd, int cmdlen, uchar* data, int datalen)
8147dd7cddfSDavid du Colombier {
8157dd7cddfSDavid du Colombier 	int port;
8167dd7cddfSDavid du Colombier 	uchar rinterrupt, rstatus;
8177dd7cddfSDavid du Colombier 	static Lock mylexissuelock;
8187dd7cddfSDavid du Colombier 
8197dd7cddfSDavid du Colombier 	port = ctlr->port;
8207dd7cddfSDavid du Colombier 
8217dd7cddfSDavid du Colombier 	ilock(&ctlr->issuelock);
8227dd7cddfSDavid du Colombier 	issueio(port, cmd, cmdlen, data, datalen);
8237dd7cddfSDavid du Colombier 
8247dd7cddfSDavid du Colombier 	while(!((rinterrupt = inb(port+Rinterrupt)) & Cmdc))
8257dd7cddfSDavid du Colombier 		;
8267dd7cddfSDavid du Colombier 
8277dd7cddfSDavid du Colombier 	rstatus = inb(port+Rstatus);
8287dd7cddfSDavid du Colombier 	outb(port+Rcontrol, Rint);
8297dd7cddfSDavid du Colombier 	iunlock(&ctlr->issuelock);
8307dd7cddfSDavid du Colombier 
8317dd7cddfSDavid du Colombier 	if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
8327dd7cddfSDavid du Colombier 		return 0;
8337dd7cddfSDavid du Colombier 	return 1;
8347dd7cddfSDavid du Colombier }
8357dd7cddfSDavid du Colombier 
8367dd7cddfSDavid du Colombier static SDev*
mylexprobe(int port,int irq)8377dd7cddfSDavid du Colombier mylexprobe(int port, int irq)
8387dd7cddfSDavid du Colombier {
8397dd7cddfSDavid du Colombier 	SDev *sdev;
8407dd7cddfSDavid du Colombier 	Ctlr *ctlr;
8417dd7cddfSDavid du Colombier 	uchar cmd[6], data[256];
8427dd7cddfSDavid du Colombier 	int clen, dlen, timeo;
843*fececb92SDavid du Colombier 	static int count;
8447dd7cddfSDavid du Colombier 
8457dd7cddfSDavid du Colombier 	if(ioalloc(port, 0x3, 0, "mylex") < 0)
8467dd7cddfSDavid du Colombier 		return nil;
8477dd7cddfSDavid du Colombier 	ctlr = nil;
8489a747e4fSDavid du Colombier 	sdev = nil;
8497dd7cddfSDavid du Colombier 	/*
8507dd7cddfSDavid du Colombier 	 * Attempt to hard-reset the board and reset
8517dd7cddfSDavid du Colombier 	 * the SCSI bus. If the board state doesn't settle to
8527dd7cddfSDavid du Colombier 	 * idle with mailbox initialisation required, either
8537dd7cddfSDavid du Colombier 	 * it isn't a compatible board or it's broken.
8547dd7cddfSDavid du Colombier 	 * If the controller has SCAM set this can take a while.
8557dd7cddfSDavid du Colombier 	 */
85680ee5cbfSDavid du Colombier 	if(getconf("*noscsireset") != nil)
85780ee5cbfSDavid du Colombier 		outb(port+Rcontrol, Rhard);
85880ee5cbfSDavid du Colombier 	else
8597dd7cddfSDavid du Colombier 		outb(port+Rcontrol, Rhard|Rsbus);
8607dd7cddfSDavid du Colombier 	for(timeo = 0; timeo < 100; timeo++){
8617dd7cddfSDavid du Colombier 		if(inb(port+Rstatus) == (Inreq|Hardy))
8627dd7cddfSDavid du Colombier 			break;
8637dd7cddfSDavid du Colombier 		delay(100);
8647dd7cddfSDavid du Colombier 	}
8657dd7cddfSDavid du Colombier 	if(inb(port+Rstatus) != (Inreq|Hardy)){
8667dd7cddfSDavid du Colombier buggery:
8677dd7cddfSDavid du Colombier 		if(ctlr != nil)
8687dd7cddfSDavid du Colombier 			free(ctlr);
8699a747e4fSDavid du Colombier 		if (sdev != nil)
8709a747e4fSDavid du Colombier 			free(sdev);
8717dd7cddfSDavid du Colombier 		iofree(port);
8727dd7cddfSDavid du Colombier 		return nil;
8737dd7cddfSDavid du Colombier 	}
8747dd7cddfSDavid du Colombier 
8757dd7cddfSDavid du Colombier 	if((ctlr = malloc(sizeof(Ctlr))) == nil)
8767dd7cddfSDavid du Colombier 		goto buggery;
8777dd7cddfSDavid du Colombier 	ctlr->port = port;
8787dd7cddfSDavid du Colombier 	ctlr->irq = irq;
8797dd7cddfSDavid du Colombier 	ctlr->bus = 24;
8807dd7cddfSDavid du Colombier 	ctlr->wide = 0;
8817dd7cddfSDavid du Colombier 
8827dd7cddfSDavid du Colombier 	/*
8837dd7cddfSDavid du Colombier 	 * Try to determine if this is a 32-bit MultiMaster controller
8847dd7cddfSDavid du Colombier 	 * by attempting to obtain the extended inquiry information;
8857dd7cddfSDavid du Colombier 	 * this command is not implemented on Adaptec 154xx
8867dd7cddfSDavid du Colombier 	 * controllers. If successful, the first byte of the returned
8877dd7cddfSDavid du Colombier 	 * data is the host adapter bus type, 'E' for 32-bit EISA,
8887dd7cddfSDavid du Colombier 	 * PCI and VLB buses.
8897dd7cddfSDavid du Colombier 	 */
8907dd7cddfSDavid du Colombier 	cmd[0] = Ciesi;
8916bfdd830SDavid du Colombier 	cmd[1] = 14;
8927dd7cddfSDavid du Colombier 	clen = 2;
8937dd7cddfSDavid du Colombier 	dlen = 256;
8947dd7cddfSDavid du Colombier 	if(issue(ctlr, cmd, clen, data, dlen)){
8957dd7cddfSDavid du Colombier 		if(data[0] == 'E')
8967dd7cddfSDavid du Colombier 			ctlr->bus = 32;
8977dd7cddfSDavid du Colombier 		ctlr->wide = data[0x0D] & 0x01;
898*fececb92SDavid du Colombier 		/*
899*fececb92SDavid du Colombier 		 * devsd doesn't pass us the `spec' argument, so
900*fececb92SDavid du Colombier 		 * we'll assume that sd0 goes to the first scsi host
901*fececb92SDavid du Colombier 		 * adapter found, etc.
902*fececb92SDavid du Colombier 		 */
903*fececb92SDavid du Colombier 		print("#S/sd%d: mylex SCSI: port 0x%ux: %d-bit, ",
904*fececb92SDavid du Colombier 			count++, ctlr->port, ctlr->bus);
9056bfdd830SDavid du Colombier 		if (ctlr->wide)
906*fececb92SDavid du Colombier 			print("wide\n");
9076bfdd830SDavid du Colombier 		else
908*fececb92SDavid du Colombier 			print("narrow\n");
9097dd7cddfSDavid du Colombier 	}
9107dd7cddfSDavid du Colombier 	else{
9117dd7cddfSDavid du Colombier 		/*
9127dd7cddfSDavid du Colombier 		 * Inconceivable though it may seem, a hard controller reset
9137dd7cddfSDavid du Colombier 		 * is necessary here to clear out the command queue. Every
9147dd7cddfSDavid du Colombier 		 * board seems to lock-up in a different way if you give an
9157dd7cddfSDavid du Colombier 		 * invalid command and then try to clear out the
9167dd7cddfSDavid du Colombier 		 * command/parameter and/or data-in register.
9177dd7cddfSDavid du Colombier 		 * Soft reset doesn't do the job either. Fortunately no
9187dd7cddfSDavid du Colombier 		 * serious initialisation has been done yet so there's nothing
9197dd7cddfSDavid du Colombier 		 * to tidy up.
9207dd7cddfSDavid du Colombier 		 */
9217dd7cddfSDavid du Colombier 		outb(port+Rcontrol, Rhard);
9227dd7cddfSDavid du Colombier 		for(timeo = 0; timeo < 100; timeo++){
9237dd7cddfSDavid du Colombier 			if(inb(port+Rstatus) == (Inreq|Hardy))
9247dd7cddfSDavid du Colombier 				break;
9257dd7cddfSDavid du Colombier 			delay(100);
9267dd7cddfSDavid du Colombier 		}
9277dd7cddfSDavid du Colombier 		if(inb(port+Rstatus) != (Inreq|Hardy))
9287dd7cddfSDavid du Colombier 			goto buggery;
9297dd7cddfSDavid du Colombier 	}
9307dd7cddfSDavid du Colombier 
9317dd7cddfSDavid du Colombier 	/*
9327dd7cddfSDavid du Colombier 	 * If the BIOS is enabled on the AHA-1542C/CF and BIOS options for
9337dd7cddfSDavid du Colombier 	 * support of drives > 1Gb, dynamic scanning of the SCSI bus or more
9347dd7cddfSDavid du Colombier 	 * than 2 drives under DOS 5.0 are enabled, the BIOS disables
9357dd7cddfSDavid du Colombier 	 * accepting Cmbinit to protect against running with drivers which
9367dd7cddfSDavid du Colombier 	 * don't support those options. In order to unlock the interface it
9377dd7cddfSDavid du Colombier 	 * is necessary to read a lock-code using Cextbios and write it back
9387dd7cddfSDavid du Colombier 	 * using Cmbienable; the lock-code is non-zero.
9397dd7cddfSDavid du Colombier 	 */
9407dd7cddfSDavid du Colombier 	cmd[0] = Cinquiry;
9417dd7cddfSDavid du Colombier 	clen = 1;
9427dd7cddfSDavid du Colombier 	dlen = 4;
9437dd7cddfSDavid du Colombier 	if(issue(ctlr, cmd, clen, data, dlen) == 0)
9447dd7cddfSDavid du Colombier 		goto buggery;
9457dd7cddfSDavid du Colombier 	if(data[0] >= 0x43){
9467dd7cddfSDavid du Colombier 		cmd[0] = Cextbios;
9477dd7cddfSDavid du Colombier 		clen = 1;
9487dd7cddfSDavid du Colombier 		dlen = 2;
9497dd7cddfSDavid du Colombier 		if(issue(ctlr, cmd, clen, data, dlen) == 0)
9507dd7cddfSDavid du Colombier 			goto buggery;
9517dd7cddfSDavid du Colombier 
9527dd7cddfSDavid du Colombier 		/*
9537dd7cddfSDavid du Colombier 		 * Lock-code returned in data[1]. If it's non-zero write
9547dd7cddfSDavid du Colombier 		 * it back along with bit 0 of byte 0 cleared to enable
9557dd7cddfSDavid du Colombier 		 * mailbox initialisation.
9567dd7cddfSDavid du Colombier 		 */
9577dd7cddfSDavid du Colombier 		if(data[1]){
9587dd7cddfSDavid du Colombier 			cmd[0] = Cmbienable;
9597dd7cddfSDavid du Colombier 			cmd[1] = 0;
9607dd7cddfSDavid du Colombier 			cmd[2] = data[1];
9617dd7cddfSDavid du Colombier 			clen = 3;
9627dd7cddfSDavid du Colombier 			if(issue(ctlr, cmd, clen, 0, 0) == 0)
9637dd7cddfSDavid du Colombier 				goto buggery;
9647dd7cddfSDavid du Colombier 		}
9657dd7cddfSDavid du Colombier 	}
9667dd7cddfSDavid du Colombier 
9677dd7cddfSDavid du Colombier 	/*
9687dd7cddfSDavid du Colombier 	 * Get the id, DMA and IRQ info from the board. This will
9697dd7cddfSDavid du Colombier 	 * cause an interrupt which will hopefully not cause any
9707dd7cddfSDavid du Colombier 	 * trouble because the interrupt number isn't known yet.
9717dd7cddfSDavid du Colombier 	 * This is necessary as the DMA won't be set up if the
9727dd7cddfSDavid du Colombier 	 * board has the BIOS disabled.
9737dd7cddfSDavid du Colombier 	 *
9747dd7cddfSDavid du Colombier 	 * If the IRQ is already known, this must be a 32-bit PCI
9757dd7cddfSDavid du Colombier 	 * or EISA card, in which case the returned DMA and IRQ can
9767dd7cddfSDavid du Colombier 	 * be ignored.
9777dd7cddfSDavid du Colombier 	 */
9787dd7cddfSDavid du Colombier 	cmd[0] = Cinquire;
9797dd7cddfSDavid du Colombier 	clen = 1;
9807dd7cddfSDavid du Colombier 	dlen = 3;
9817dd7cddfSDavid du Colombier 	if(issue(ctlr, cmd, clen, data, dlen) == 0)
9827dd7cddfSDavid du Colombier 		goto buggery;
9837dd7cddfSDavid du Colombier 
9847dd7cddfSDavid du Colombier 	ctlr->id = data[2] & 0x07;
9857dd7cddfSDavid du Colombier 	if(ctlr->irq < 0){
9867dd7cddfSDavid du Colombier 		switch(data[0]){		/* DMA Arbitration Priority */
9877dd7cddfSDavid du Colombier 		case 0x80:			/* Channel 7 */
9887dd7cddfSDavid du Colombier 			outb(0xD6, 0xC3);
9897dd7cddfSDavid du Colombier 			outb(0xD4, 0x03);
9907dd7cddfSDavid du Colombier 			break;
9917dd7cddfSDavid du Colombier 		case 0x40:			/* Channel 6 */
9927dd7cddfSDavid du Colombier 			outb(0xD6, 0xC2);
9937dd7cddfSDavid du Colombier 			outb(0xD4, 0x02);
9947dd7cddfSDavid du Colombier 			break;
9957dd7cddfSDavid du Colombier 		case 0x20:			/* Channel 5 */
9967dd7cddfSDavid du Colombier 			outb(0xD6, 0xC1);
9977dd7cddfSDavid du Colombier 			outb(0xD4, 0x01);
9987dd7cddfSDavid du Colombier 			break;
9997dd7cddfSDavid du Colombier 		case 0x01:			/* Channel 0 */
10007dd7cddfSDavid du Colombier 			outb(0x0B, 0xC0);
10017dd7cddfSDavid du Colombier 			outb(0x0A, 0x00);
10027dd7cddfSDavid du Colombier 			break;
10037dd7cddfSDavid du Colombier 		default:
100459cc4ca5SDavid du Colombier 			if(ctlr->bus == 24)
100559cc4ca5SDavid du Colombier 				goto buggery;
10067dd7cddfSDavid du Colombier 			break;
10077dd7cddfSDavid du Colombier 		}
10087dd7cddfSDavid du Colombier 
10097dd7cddfSDavid du Colombier 		switch(data[1]){		/* Interrupt Channel */
10107dd7cddfSDavid du Colombier 		case 0x40:
10117dd7cddfSDavid du Colombier 			ctlr->irq = 15;
10127dd7cddfSDavid du Colombier 			break;
10137dd7cddfSDavid du Colombier 		case 0x20:
10147dd7cddfSDavid du Colombier 			ctlr->irq = 14;
10157dd7cddfSDavid du Colombier 			break;
10167dd7cddfSDavid du Colombier 		case 0x08:
10177dd7cddfSDavid du Colombier 			ctlr->irq = 12;
10187dd7cddfSDavid du Colombier 			break;
10197dd7cddfSDavid du Colombier 		case 0x04:
10207dd7cddfSDavid du Colombier 			ctlr->irq = 11;
10217dd7cddfSDavid du Colombier 			break;
10227dd7cddfSDavid du Colombier 		case 0x02:
10237dd7cddfSDavid du Colombier 			ctlr->irq = 10;
10247dd7cddfSDavid du Colombier 			break;
10257dd7cddfSDavid du Colombier 		case 0x01:
10267dd7cddfSDavid du Colombier 			ctlr->irq = 9;
10277dd7cddfSDavid du Colombier 			break;
10287dd7cddfSDavid du Colombier 		default:
10297dd7cddfSDavid du Colombier 			goto buggery;
10307dd7cddfSDavid du Colombier 		}
10317dd7cddfSDavid du Colombier 	}
10327dd7cddfSDavid du Colombier 
10337dd7cddfSDavid du Colombier 	if((sdev = malloc(sizeof(SDev))) == nil)
10347dd7cddfSDavid du Colombier 		goto buggery;
10357dd7cddfSDavid du Colombier 	sdev->ifc = &sdmylexifc;
10367dd7cddfSDavid du Colombier 	sdev->ctlr = ctlr;
10374de34a7eSDavid du Colombier 	sdev->idno = '0';
10387dd7cddfSDavid du Colombier 	ctlr->sdev = sdev;
10397dd7cddfSDavid du Colombier 	if(!ctlr->wide)
10407dd7cddfSDavid du Colombier 		sdev->nunit = 8;
10417dd7cddfSDavid du Colombier 	else
10427dd7cddfSDavid du Colombier 		sdev->nunit = 16;
10437dd7cddfSDavid du Colombier 
10447dd7cddfSDavid du Colombier 	return sdev;
10457dd7cddfSDavid du Colombier }
10467dd7cddfSDavid du Colombier 
10477dd7cddfSDavid du Colombier static int mylexport[8] = {
10487dd7cddfSDavid du Colombier 	0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0x000, 0x000,
10497dd7cddfSDavid du Colombier };
10507dd7cddfSDavid du Colombier 
10517dd7cddfSDavid du Colombier static SDev*
mylexpnp(void)10527dd7cddfSDavid du Colombier mylexpnp(void)
10537dd7cddfSDavid du Colombier {
10547dd7cddfSDavid du Colombier 	Pcidev *p;
10557dd7cddfSDavid du Colombier 	Ctlr *ctlr;
105659cc4ca5SDavid du Colombier 	ISAConf isa;
105759cc4ca5SDavid du Colombier 	int cfg, ctlrno, i, x;
10587dd7cddfSDavid du Colombier 	SDev *sdev, *head, *tail;
10597dd7cddfSDavid du Colombier 
10607dd7cddfSDavid du Colombier 	p = nil;
10617dd7cddfSDavid du Colombier 	head = tail = nil;
10627dd7cddfSDavid du Colombier 	while(p = pcimatch(p, 0x104B, 0)){
10637dd7cddfSDavid du Colombier 		if((sdev = mylexprobe(p->mem[0].bar & ~0x01, p->intl)) == nil)
10647dd7cddfSDavid du Colombier 			continue;
10657dd7cddfSDavid du Colombier 
10667dd7cddfSDavid du Colombier 		ctlr = sdev->ctlr;
10677dd7cddfSDavid du Colombier 		ctlr->pcidev = p;
10687dd7cddfSDavid du Colombier 
10697dd7cddfSDavid du Colombier 		if(head != nil)
10707dd7cddfSDavid du Colombier 			tail->next = sdev;
10717dd7cddfSDavid du Colombier 		else
10727dd7cddfSDavid du Colombier 			head = sdev;
10737dd7cddfSDavid du Colombier 		tail = sdev;
10747dd7cddfSDavid du Colombier 	}
10757dd7cddfSDavid du Colombier 
107659cc4ca5SDavid du Colombier 	if(strncmp(KADDR(0xFFFD9), "EISA", 4) == 0){
10777dd7cddfSDavid du Colombier 		for(cfg = 0x1000; cfg < MaxEISA*0x1000; cfg += 0x1000){
10787dd7cddfSDavid du Colombier 			x = 0;
10797dd7cddfSDavid du Colombier 			for(i = 0; i < 4; i++)
10807dd7cddfSDavid du Colombier 				x |= inb(cfg+CfgEISA+i)<<(i*8);
10817dd7cddfSDavid du Colombier 			if(x != 0x0142B30A && x != 0x0242B30A)
10827dd7cddfSDavid du Colombier 				continue;
10837dd7cddfSDavid du Colombier 
10847dd7cddfSDavid du Colombier 			x = inb(cfg+0xC8C);
10857dd7cddfSDavid du Colombier 			if((sdev = mylexprobe(mylexport[x & 0x07], -1)) == nil)
10867dd7cddfSDavid du Colombier 				continue;
10877dd7cddfSDavid du Colombier 
10887dd7cddfSDavid du Colombier 			if(head != nil)
10897dd7cddfSDavid du Colombier 				tail->next = sdev;
10907dd7cddfSDavid du Colombier 			else
10917dd7cddfSDavid du Colombier 				head = sdev;
10927dd7cddfSDavid du Colombier 			tail = sdev;
10937dd7cddfSDavid du Colombier 		}
109459cc4ca5SDavid du Colombier 	}
109559cc4ca5SDavid du Colombier 
109659cc4ca5SDavid du Colombier 	for(ctlrno = 0; ctlrno < 4; ctlrno++){
109759cc4ca5SDavid du Colombier 		memset(&isa, 0, sizeof(isa));
109859cc4ca5SDavid du Colombier 		if(!isaconfig("scsi", ctlrno, &isa))
109959cc4ca5SDavid du Colombier 			continue;
110059cc4ca5SDavid du Colombier 		if(strcmp(isa.type, "aha1542"))
110159cc4ca5SDavid du Colombier 			continue;
110259cc4ca5SDavid du Colombier 		if((sdev = mylexprobe(isa.port, -1)) == nil)
110359cc4ca5SDavid du Colombier 			continue;
110459cc4ca5SDavid du Colombier 
110559cc4ca5SDavid du Colombier 		if(head != nil)
110659cc4ca5SDavid du Colombier 			tail->next = sdev;
110759cc4ca5SDavid du Colombier 		else
110859cc4ca5SDavid du Colombier 			head = sdev;
110959cc4ca5SDavid du Colombier 		tail = sdev;
111059cc4ca5SDavid du Colombier 	}
11117dd7cddfSDavid du Colombier 
11127dd7cddfSDavid du Colombier 	return head;
11137dd7cddfSDavid du Colombier }
11147dd7cddfSDavid du Colombier 
11157dd7cddfSDavid du Colombier static int
mylex24enable(Ctlr * ctlr)11167dd7cddfSDavid du Colombier mylex24enable(Ctlr* ctlr)
11177dd7cddfSDavid du Colombier {
11187dd7cddfSDavid du Colombier 	ulong p;
11197dd7cddfSDavid du Colombier 	Ccb24 *ccb, *ccbp;
11207dd7cddfSDavid du Colombier 	uchar cmd[6], *v;
11217dd7cddfSDavid du Colombier 	int len;
11227dd7cddfSDavid du Colombier 
11237dd7cddfSDavid du Colombier 	len = (sizeof(Mbox24)*NMbox*2)+(sizeof(Ccb24)*NCcb);
11247dd7cddfSDavid du Colombier 	v = xspanalloc(len, 32, 0);
11257dd7cddfSDavid du Colombier 
11267dd7cddfSDavid du Colombier 	if(!PADDR24(ctlr, sizeof(Ctlr)) || !PADDR24(v, len))
11277dd7cddfSDavid du Colombier 		return 0;
11287dd7cddfSDavid du Colombier 
11297dd7cddfSDavid du Colombier 	ctlr->mb = v;
11307dd7cddfSDavid du Colombier 	v += sizeof(Mbox24)*NMbox*2;
11317dd7cddfSDavid du Colombier 
11327dd7cddfSDavid du Colombier 	ccb = (Ccb24*)v;
11337dd7cddfSDavid du Colombier 	for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){
11347dd7cddfSDavid du Colombier 		ccbp->ccb = ctlr->ccb;
11357dd7cddfSDavid du Colombier 		ctlr->ccb = (Ccb*)ccbp;
11367dd7cddfSDavid du Colombier 	}
11377dd7cddfSDavid du Colombier 
11387dd7cddfSDavid du Colombier 	/*
11397dd7cddfSDavid du Colombier 	 * Initialise the software controller and
11407dd7cddfSDavid du Colombier 	 * set the board scanning the mailboxes.
11417dd7cddfSDavid du Colombier 	 */
11427dd7cddfSDavid du Colombier 	ctlr->mbix = NMbox;
11437dd7cddfSDavid du Colombier 
11447dd7cddfSDavid du Colombier 	cmd[0] = Cinitialise;
11457dd7cddfSDavid du Colombier 	cmd[1] = NMbox;
11467dd7cddfSDavid du Colombier 	p = K2BPA(ctlr->mb, BUSUNKNOWN);
11477dd7cddfSDavid du Colombier 	cmd[2] = p>>16;
11487dd7cddfSDavid du Colombier 	cmd[3] = p>>8;
11497dd7cddfSDavid du Colombier 	cmd[4] = p;
11507dd7cddfSDavid du Colombier 
115159cc4ca5SDavid du Colombier 	return issue(ctlr, cmd, 5, 0, 0);
11527dd7cddfSDavid du Colombier }
11537dd7cddfSDavid du Colombier 
11547dd7cddfSDavid du Colombier static int
mylex32enable(Ctlr * ctlr)11557dd7cddfSDavid du Colombier mylex32enable(Ctlr* ctlr)
11567dd7cddfSDavid du Colombier {
11577dd7cddfSDavid du Colombier 	ulong p;
11587dd7cddfSDavid du Colombier 	Ccb32 *ccb, *ccbp;
11597dd7cddfSDavid du Colombier 	uchar cmd[6], *v;
11607dd7cddfSDavid du Colombier 
11617dd7cddfSDavid du Colombier 	v = xspanalloc((sizeof(Mbox32)*NMbox*2)+(sizeof(Ccb32)*NCcb), 32, 0);
11627dd7cddfSDavid du Colombier 
11637dd7cddfSDavid du Colombier 	ctlr->mb = v;
11647dd7cddfSDavid du Colombier 	v += sizeof(Mbox32)*NMbox*2;
11657dd7cddfSDavid du Colombier 
11667dd7cddfSDavid du Colombier 	ccb = (Ccb32*)v;
11677dd7cddfSDavid du Colombier 	for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){
11687dd7cddfSDavid du Colombier 		/*
11697dd7cddfSDavid du Colombier 		 * Fill in some stuff that doesn't change.
11707dd7cddfSDavid du Colombier 		 */
11717dd7cddfSDavid du Colombier 		ccbp->senselen = sizeof(ccbp->sense);
11727dd7cddfSDavid du Colombier 		p = PADDR(ccbp->sense);
11737dd7cddfSDavid du Colombier 		ccbp->senseptr[0] = p;
11747dd7cddfSDavid du Colombier 		ccbp->senseptr[1] = p>>8;
11757dd7cddfSDavid du Colombier 		ccbp->senseptr[2] = p>>16;
11767dd7cddfSDavid du Colombier 		ccbp->senseptr[3] = p>>24;
11777dd7cddfSDavid du Colombier 
11787dd7cddfSDavid du Colombier 		ccbp->ccb = ctlr->ccb;
11797dd7cddfSDavid du Colombier 		ctlr->ccb = (Ccb*)ccbp;
11807dd7cddfSDavid du Colombier 	}
11817dd7cddfSDavid du Colombier 
11827dd7cddfSDavid du Colombier 	/*
11837dd7cddfSDavid du Colombier 	 * Attempt wide mode setup.
11847dd7cddfSDavid du Colombier 	 */
11857dd7cddfSDavid du Colombier 	if(ctlr->wide){
11867dd7cddfSDavid du Colombier 		cmd[0] = Cwide;
11877dd7cddfSDavid du Colombier 		cmd[1] = 1;
11886bfdd830SDavid du Colombier 		if(!issue(ctlr, cmd, 2, 0, 0)) {
11897dd7cddfSDavid du Colombier 			ctlr->wide = 0;
1190*fececb92SDavid du Colombier 			print("mylex32enable: port 0x%ux: scsi wide-mode setup "
1191*fececb92SDavid du Colombier 				"failed on wide host adapter", ctlr->port);
11926bfdd830SDavid du Colombier 		}
11937dd7cddfSDavid du Colombier 	}
11947dd7cddfSDavid du Colombier 
11957dd7cddfSDavid du Colombier 	/*
11967dd7cddfSDavid du Colombier 	 * Initialise the software controller and
11977dd7cddfSDavid du Colombier 	 * set the board scanning the mailboxes.
11987dd7cddfSDavid du Colombier 	 */
11997dd7cddfSDavid du Colombier 	ctlr->mbix = NMbox;
12007dd7cddfSDavid du Colombier 
12017dd7cddfSDavid du Colombier 	cmd[0] = Ciem;
12027dd7cddfSDavid du Colombier 	cmd[1] = NMbox;
12037dd7cddfSDavid du Colombier 	if(ctlr->pcidev)
12047dd7cddfSDavid du Colombier 		p = K2BPA(ctlr->mb, ctlr->tbdf);
12057dd7cddfSDavid du Colombier 	else
12067dd7cddfSDavid du Colombier 		p = K2BPA(ctlr->mb, BUSUNKNOWN);
12077dd7cddfSDavid du Colombier 	cmd[2] = p;
12087dd7cddfSDavid du Colombier 	cmd[3] = p>>8;
12097dd7cddfSDavid du Colombier 	cmd[4] = p>>16;
12107dd7cddfSDavid du Colombier 	cmd[5] = p>>24;
12117dd7cddfSDavid du Colombier 
12127dd7cddfSDavid du Colombier 	return issue(ctlr, cmd, 6, 0, 0);
12137dd7cddfSDavid du Colombier }
12147dd7cddfSDavid du Colombier 
12157dd7cddfSDavid du Colombier static int
mylexenable(SDev * sdev)12167dd7cddfSDavid du Colombier mylexenable(SDev* sdev)
12177dd7cddfSDavid du Colombier {
12187dd7cddfSDavid du Colombier 	int tbdf;
12197dd7cddfSDavid du Colombier 	Ctlr *ctlr;
12207dd7cddfSDavid du Colombier 	void (*interrupt)(Ureg*, void*);
12219a747e4fSDavid du Colombier 	char name[32];
12227dd7cddfSDavid du Colombier 
12237dd7cddfSDavid du Colombier 	ctlr = sdev->ctlr;
12247dd7cddfSDavid du Colombier 	if(ctlr->cache == nil){
12257dd7cddfSDavid du Colombier 		if((ctlr->cache = malloc(sdev->nunit*sizeof(Ccb*))) == nil)
12267dd7cddfSDavid du Colombier 			return 0;
12277dd7cddfSDavid du Colombier 	}
12287dd7cddfSDavid du Colombier 
12297dd7cddfSDavid du Colombier 	tbdf = BUSUNKNOWN;
12307dd7cddfSDavid du Colombier 	if(ctlr->bus == 32){
12317dd7cddfSDavid du Colombier 		if(ctlr->pcidev){
12327dd7cddfSDavid du Colombier 			tbdf = ctlr->pcidev->tbdf;
12337dd7cddfSDavid du Colombier 			pcisetbme(ctlr->pcidev);
12347dd7cddfSDavid du Colombier 		}
12357dd7cddfSDavid du Colombier 		if(!mylex32enable(ctlr))
12367dd7cddfSDavid du Colombier 			return 0;
12377dd7cddfSDavid du Colombier 		interrupt = mylex32interrupt;
12387dd7cddfSDavid du Colombier 	}
12397dd7cddfSDavid du Colombier 	else if(mylex24enable(ctlr))
12407dd7cddfSDavid du Colombier 		interrupt = mylex24interrupt;
12417dd7cddfSDavid du Colombier 	else
12427dd7cddfSDavid du Colombier 		return 0;
12437dd7cddfSDavid du Colombier 
12449a747e4fSDavid du Colombier 	snprint(name, sizeof(name), "sd%c (%s)", sdev->idno, sdev->ifc->name);
12457dd7cddfSDavid du Colombier 	intrenable(ctlr->irq, interrupt, ctlr, tbdf, name);
12467dd7cddfSDavid du Colombier 
12477dd7cddfSDavid du Colombier 	return 1;
12487dd7cddfSDavid du Colombier }
12497dd7cddfSDavid du Colombier 
12507dd7cddfSDavid du Colombier SDifc sdmylexifc = {
12517dd7cddfSDavid du Colombier 	"mylex",			/* name */
12527dd7cddfSDavid du Colombier 
12537dd7cddfSDavid du Colombier 	mylexpnp,			/* pnp */
12547dd7cddfSDavid du Colombier 	nil,				/* legacy */
12557dd7cddfSDavid du Colombier 	mylexenable,			/* enable */
12567dd7cddfSDavid du Colombier 	nil,				/* disable */
12577dd7cddfSDavid du Colombier 
12587dd7cddfSDavid du Colombier 	scsiverify,			/* verify */
12597dd7cddfSDavid du Colombier 	scsionline,			/* online */
12607dd7cddfSDavid du Colombier 	mylexrio,			/* rio */
12617dd7cddfSDavid du Colombier 	nil,				/* rctl */
12627dd7cddfSDavid du Colombier 	nil,				/* wctl */
12637dd7cddfSDavid du Colombier 
12647dd7cddfSDavid du Colombier 	scsibio,			/* bio */
12659a747e4fSDavid du Colombier 	nil,				/* probe */
12669a747e4fSDavid du Colombier 	nil,				/* clear */
1267d649fdd7SDavid du Colombier 	nil,				/* rtopctl */
1268d649fdd7SDavid du Colombier 	nil,				/* wtopctl */
12697dd7cddfSDavid du Colombier };
1270