File:  [MW Coherent from dump] / coherent / d / PS2_KERNEL / io.286 / aha.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:39 2019 UTC (7 years ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 * This is the host adaptor specific portion of the
 * Adaptec AHA154x driver.
 *
 * $Log: aha.c,v $
 * Revision 1.1.1.1  2019/05/29 04:56:39  root
 * coherent
 *
 * Revision 1.1  92/07/17  15:24:01  bin
 * Initial revision
 * 
 * Revision 1.3  92/02/07  15:07:42  hal
 * Clean up includes et al.
 * 
 * Revision 1.2	91/05/01  04:54:43	root
 * Debug code and kalloc arg fixes.
 * 
 * Revision 1.1	91/04/30  11:01:41	root
 * Shipped with COH 3.1.0
 * 
 */
#include <sys/coherent.h>
#include <sys/buf.h>
#include <sys/sched.h>

#include <sys/scsiwork.h>
#include <sys/aha154x.h>

extern	saddr_t	sds;		/* System Data Selector */
static	paddr_t	sds_physical;	/* as physical address */
static	short	aha_i_o_base;
static	char	aha_loaded;	/* did load() find a host adaptor? */
static	char	dev_bit_map[8];	/* one byte per SCSI-ID; one bit per LUN */
char	drive_info[MAX_SCSI_ID * MAX_LUN]; /* "per drive" info/flags */

void	aha_intr();		/* interrupt service routine */

#define	MIN_MAILBOX	1
int	MAX_MAILBOX = { 8 };	/* tunable value */

static	scsi_work_t	*scsi_work_queue;
static	mailentry	*mailbox_in, *mailbox_out;
static	char		*aha_err_msg = { "no message" };

static	long	aha_timeout[] = { 
#define	TIMEOUT_PRESENT	0
	0x30000L,
#define	TIMEOUT_SENDCMD	1
	0x10000L,
#define	TIMEOUT_POLL	2
	0x100L,
};

#if	0
static
OUTB( port, value )
short port;
{	printf( "<O(%x,%x)>", port, value );
	outb( port, value );	}
INB( port )
short port;
{	register i = inb(port);
	printf( "<I(%x)=%x>", port, i );
	return i;	}
#else
#define	OUTB( port, value )	outb( port, value )
#define	INB(port)	inb(port)
#endif

#if	VERBOSE
#define	SETMSG(msg)	aha_err_msg = msg

static
char	*aha_last_msg()
{
	return aha_err_msg;
}

#else

#define	SETMSG(msg)
static
char	*aha_last_msg()
{
	return "error messages not verbose";
} 
#endif

static
int	no_mem()
{
	printf("aha154x: out of kernel memory\n");
}

int	aha_set_base( base )
{
	register i;

	i = aha_i_o_base;
	aha_i_o_base = base;
	return i;
}

int	aha_get_base()
{
	return aha_i_o_base;
}

aha_process( ccb )
ccb_t *ccb;
{
	register scsi_work_t *sw = ccb->ccb_sw;
	register BUF *bp;

#if	VERBOSE
	printf( "aha_process: ccb %x ", ccb );
	printf("sw=%x bp=%x\n", ccb->ccb_sw, ccb->ccb_sw->sw_bp);
	aha_ccb_print( ccb );
#endif
	if( ccb->ccb_sw == 0 ) {
#if	VERBOSE
		printf( "process: ccb %x with NULL sw\n", ccb );
#endif
		++ccb->opcode;
		wakeup( ccb );
		return;
	}

	bp = sw->sw_bp;
#if	VERBOSE
	printf( "bp = %x\n", bp );
#endif
	if( (ccb->hoststatus != 0) || (ccb->targetstatus != 0) ) {
		if( --sw->sw_retry > 0
		   || (ccb->targetstatus == CHECK_TARGET_STATUS
		   && ccb->cmd_status[12] == SENSE_UNIT_ATTENTION)) {
			int s = sphi();
			if( scsi_work_queue->sw_actf == NULL ) {
				scsi_work_queue->sw_actf = sw;
			} else {
				scsi_work_queue->sw_actl->sw_actf = sw;
			}
			scsi_work_queue->sw_actl = sw;
			spl(s);
			aha_start();
			return;
		}
		bp->b_flag |= BFERR;
	} else
		bp->b_resid = 0;
#if	VERBOSE
	printf( "bp flag = %o\n", bp->b_flag );
#endif
	bdone( bp );
	kfree( sw );
	kfree( ccb );
}

static
int	aha_1out( value )
{
	register i;

	while( (i = INB(aha_i_o_base + AHA_STATUS) & AHA_CDOPFULL) != 0 )
		if( (i & AHA_INVDCMD)
		 || (INB(aha_i_o_base+AHA_INTERRUPT) & AHA_CMD_DONE) )
			return -1;
	OUTB( aha_i_o_base + AHA_WRITE, value );
	return 0;
}

static
int	aha_1in()
{
	register i;

	while( (i = INB(aha_i_o_base + AHA_STATUS) & AHA_DIPFULL) == 0 )
		if( (i & AHA_INVDCMD)
		 || (INB(aha_i_o_base+AHA_INTERRUPT) & AHA_CMD_DONE) )
			return -1;
	return INB( aha_i_o_base + AHA_READ ) & 0xFF;
}

static
void	aha_cmd_out( value )
{
	register long l;
	register int i;

	for( l = aha_timeout[TIMEOUT_SENDCMD]; --l > 0; )
		if( ((i=INB(aha_i_o_base + AHA_STATUS))
		    & AHA_SCSIIDLE ) != 0 ) {
			aha_1out( value );
			return;
		}
#if	VERBOSE
		else
			printf( "aha: cmd_out status = %x\r", i );
#endif
	SETMSG( "timeout sending cmd byte" );
	printf( "aha154x: timeout sending cmd byte\n" );
}

static
int	aha_poll()
{
	register i;
	register l = aha_timeout[TIMEOUT_POLL];
	while( (--l > 0 )
	  &&  ((i = INB(aha_i_o_base + AHA_INTERRUPT)) & AHA_CMD_DONE) == 0 )
		;
	if( l == 0 )
		printf( "aha154x: aha_poll timed out\n" );

	i = INB(aha_i_o_base + AHA_STATUS);
	OUTB( aha_i_o_base + AHA_CONTROL, AHA_INTRRESET );
	return i;
}

static
void	aha_get_data( vec, cnt )
char	*vec;
int	cnt;
{
	while( --cnt >= 0 )
		*vec++ = aha_1in();
	aha_poll();
}

static
int	aha_present()
{
	long	l;

	if( INB(aha_i_o_base) == 0xFF ) {
		SETMSG( "no adapter found at io base" );
		return -3;
	}
	for( l = aha_timeout[TIMEOUT_PRESENT];
		(--l > 0) && (INB(aha_i_o_base + AHA_STATUS) & AHA_SELFTEST); )
			;
	if( l == 0 ) {
		SETMSG( "selftest not completed" );
		return -1;
	}
	if( INB(aha_i_o_base + AHA_STATUS) & AHA_DIAGFAIL ) {
		SETMSG( "diagnostics failed" );
		return -2;
	}
	if( INB(aha_i_o_base + AHA_STATUS) & AHA_INITMAIL ) {
		SETMSG( "mailbox initialization needed" );
		return 1;
	}
	if( INB(aha_i_o_base + AHA_STATUS) & AHA_SCSIIDLE ) {
		SETMSG( "adaptor okay, idle" );
		return 0;
	}
	SETMSG( "unknown status at start" );
	return -4;
}

static
void	aha_l_to_p3( value, vec )
paddr_t	value;
unsigned char *vec;
{
	register i;

	for( i = 3; --i >= 0; ) {
		vec[i] = value & 0xFF;
		value >>= 8;
	}
}

static
char	*aha_p3_to_v( vec )
unsigned char *vec;
{
	paddr_t adr;

	adr = vec[0];
	adr <<= 16;
	adr |= (vec[1]<<8) | vec[2];
	adr -= sds_physical;
	return (char *)adr;
}

aha_device_info()
{
	register i;
	static char buf[256];

	aha_cmd_out( AHA_DO_GET_DEVICES );
	aha_get_data( &buf[0], 8 );
	for( i = 0; i < 8; ++i )
		if( buf[i] != 0 )
			printf( "[%d] %x ", i, buf[i] );
	printf( "\n" );
}

int	aha_unload( ireq )
{
#if	VERBOSE
	printf( "aha_unload: %x\n", ireq );
#endif
	/*
	 *	we should really verify that everything
	 *	out there gets flushed.
	 */
	if (!aha_loaded)
		return;
	if( mailbox_out ) {
		kfree( mailbox_out );
		mailbox_out = 0;
	}
	clrivec( ireq );
}

int	aha_load( dma, ireq, base, head )
scsi_work_t *head;
{
	register int	i;
	unsigned char	adr[4];

#if	VERBOSE
	printf( "aha_load( %d, %d, 0x%x );\n", dma, ireq, base );
#endif
	aha_set_base(base);
	if( mailbox_out == 0 ) {
		if( (mailbox_out = 
		     kalloc(2 * MAX_MAILBOX * sizeof(mailentry))) == 0 ) {
			no_mem();
			return -1;
		} else
			mailbox_in = &mailbox_out[MAX_MAILBOX];
	}

	for( i = 0; i < MAX_MAILBOX; ++i )
		mailbox_out[i].cmd = mailbox_in[i].cmd = 0;

	sds_physical = VTOP2( 0, sds );
	aha_l_to_p3( VTOP2( mailbox_out, sds ), &adr[1] );
	adr[0] = MAX_MAILBOX;

	/*
	 * setup HW
	 */
	setivec( ireq, aha_intr );
	outb( 0xD6, 0xC1 );		/* DMA is currently hard coded for */
	outb( 0xD4, 0x01 );		/* DMA channel 5 */


	OUTB( aha_i_o_base+AHA_CONTROL, AHA_HARDRESET );
	if (aha_present() < 0) {
		printf("aha154x: initialization error or host adaptor not ");
		printf("found at 0x%x\n", aha_i_o_base);
		return -1;
	}
	aha_cmd_out( AHA_DO_MAILBOX_INIT );
	for( i = 0; i < 4; ++i )
		aha_1out( adr[i] );
	scsi_work_queue = head;
	++aha_loaded;
	return MAX_MAILBOX;
}

aha_command( sc )
register scsi_cmd_t *sc;
{
	register i;
	register ccb_t *ccb;

	short	count = sc->blklen;
	long	block = sc->block;

	ccb = (ccb_t *) kalloc(sizeof (ccb_t));
	if (ccb == (ccb_t *) 0) {
		no_mem();
		return -1;
	}
#if	VERBOSE
	printf( "aha_command( SCSI ID %d, LUN %d, c %x, b %D",
		sc->unit >> 2, sc->unit & 0x3, sc->cmd, sc->block ); 
	printf( " [%d] @%x:%x )\n",
		sc->buflen, sc->buffer );
#endif
	ccb->ccb_sw = 0;
	ccb->opcode = 0;			/* SCSI_INITIATOR*/
	ccb->target = (sc->unit & 0x1C) << 3;	/* SCSI ID */
	ccb->target |= sc->unit & 0x3;		/* LUN */
	if( (ccb->cmd_status[0] = sc->cmd) == ScmdWRITEXTENDED ) {
		ccb->target |= AHA_CCB_DATA_OUT;
	} else { /* READEXT, READCAP, INQUIRY */
		ccb->target |= AHA_CCB_DATA_IN;
	}
	ccb->cmd_status[1] = 0;
	ccb->cmd_status[2] = block;
	ccb->cmd_status[3] = block >>16;
	ccb->cmd_status[4] = block >> 8;
	ccb->cmd_status[5] = block;
	ccb->cmd_status[6] = 0;
	ccb->cmd_status[7] = count / 512;
	ccb->cmd_status[8] = count;
	ccb->cmd_status[9] = 0;
	ccb->cmdlen = 10;
	ccb->senselen = MAX_SENSEDATA;

	aha_l_to_p3( (long)sc->buflen, ccb->datalen );
	aha_l_to_p3( sc->buffer, ccb->dataptr );
	aha_l_to_p3( VTOP2( ccb, sds ), mailbox_out[0].adr );
#if	VERBOSE
	aha_ccb_print( ccb );
#endif
	mailbox_out[0].cmd = MBO_TO_START;
	aha_1out( AHA_DO_SCSI_START );
	while( ccb->opcode == 0 )
		sleep( ccb, CVBLKIO, IVBLKIO, SVBLKIO );
	
#if	VERBOSE
	printf( "done with status = %d, %d\n",
		ccb->hoststatus, ccb->targetstatus );
#endif
	if( (ccb->targetstatus == CHECK_TARGET_STATUS)
	   && (ccb->cmd_status[12] != SENSE_UNIT_ATTENTION) ) {
		printf( "aha: SCSI ID %d LUN %d. SCSI sense =",
		(sc->unit >> 2), sc->unit & 0x3 );
		for( i = 0; i < ccb->senselen; ++i )
			printf( " %x", ccb->cmd_status[10+i] );
		printf( "\n" );
	}
	i = ccb->hoststatus | ccb->targetstatus;
	kfree( ccb );
	return i;
}

ccb_t	*buildccb( sw )
register scsi_work_t *sw;
{
	register ccb_t *ccb;
	ccb = (ccb_t *)kalloc(sizeof(ccb_t));

#if	VERBOSE
	printf( "build: drv = %x, bno = %D  ",
		sw->sw_drv, sw->sw_bno );
#endif
	ccb->ccb_sw = sw;
	ccb->opcode = 0;			/* SCSI INITIATOR */
	ccb->target = (sw->sw_drv & 0x1C) << 3;	/* SCSI ID */
	ccb->target |= (sw->sw_drv) & 0x3;	/* LUN */
	if( sw->sw_bp->b_req == BREAD ) {
		ccb->target |= AHA_CCB_DATA_IN;
		ccb->cmd_status[0] = ScmdREADEXTENDED;
	} else {
		ccb->target |= AHA_CCB_DATA_OUT;
		ccb->cmd_status[0] = ScmdWRITEXTENDED;
	}
	ccb->cmd_status[2] = 0;
	ccb->cmd_status[3] = sw->sw_bno >>16;
	ccb->cmd_status[4] = sw->sw_bno >> 8;
	ccb->cmd_status[5] = sw->sw_bno;
	ccb->cmd_status[6] = 0;
	ccb->cmd_status[7] = sw->sw_bp->b_count / (512*256L);
	ccb->cmd_status[8] = sw->sw_bp->b_count / 512;
	ccb->cmd_status[9] = 0;
	ccb->cmdlen = 10;
	ccb->senselen = MAX_SENSEDATA;

	aha_l_to_p3( (long)sw->sw_bp->b_count, ccb->datalen );
	aha_l_to_p3( vtop(sw->sw_bp->b_faddr), ccb->dataptr );
	return ccb;
#if	0
/* start of ioctl code */
	if( f == SASI_CMD_IN )
		ccb->target |= AHA_CCB_DATA_IN;
	else if( f == SASI_CMD_OUT )
		ccb->target |= AHA_CCB_DATA_OUT;
	else
		ccb->target |=	 AHA_CCB_DATA_IN
				|AHA_CCB_DATA_OUT;
#endif
}

aha_start()
{
	register i, s, n = 0;
	scsi_work_t *sw;
	static char locked;

	s = sphi();
	if( locked ) {
		spl(s);
		return;
	}
	++locked;
	spl(s);

	while( (sw = scsi_work_queue->sw_actf) != NULL ) {
		for( i = MIN_MAILBOX; i < MAX_MAILBOX; ++i )
			if( mailbox_out[i].cmd == MBO_IS_FREE ) {
				register ccb_t *ccb;
				int s;

				++n;
				ccb = buildccb( sw );
#if	VERBOSE
				aha_ccb_print( ccb );
#endif
				aha_l_to_p3( VTOP2( ccb, sds ),
						mailbox_out[i].adr );
				mailbox_out[i].cmd = MBO_TO_START;
#if	VERBOSE
				printf( "MBO[%d] = %x:%x:%x:%x, ccb = %x ",
					i, mailbox_out[i].cmd,
					mailbox_out[i].adr[0],
					mailbox_out[i].adr[1],
					mailbox_out[i].adr[2], ccb );
	printf("sw=%x bp=%x\n", ccb->ccb_sw, ccb->ccb_sw->sw_bp);
#endif
				aha_1out( AHA_DO_SCSI_START );

				s = sphi();
				sw = scsi_work_queue->sw_actf = sw->sw_actf;
				if( sw == NULL )
					scsi_work_queue->sw_actl = NULL;
				spl(s);

				if( sw == NULL )
					break;
			}
		if( i == MAX_MAILBOX )
			break;
	}
	--locked;
	return n;
}

int	aha_completed()
{
	register i, n;

	for( n = 0, i = 0; i < MAX_MAILBOX; ++i )
		if( mailbox_in[i].cmd != MBI_IS_FREE ) {
#if	VERBOSE
			printf( "aha: mail[%d] = %x:%x:%x:%x\n",
				i, mailbox_in[i].cmd,
				mailbox_in[i].adr[0],
				mailbox_in[i].adr[1],
				mailbox_in[i].adr[2] );
#endif
			defer( aha_process,
				aha_p3_to_v( mailbox_in[i].adr ) );
			mailbox_in[i].cmd = MBI_IS_FREE;
			++n;
		}
	return n;
}

void	aha_intr()
{
	register i;

#if	VERBOSE
	printf( "aha_interrupt routine\n" );
#endif
	if( ((i = INB(aha_i_o_base+AHA_INTERRUPT)) & AHA_ANY_INTER) == 0 )
		printf( "aha: spurious interrupt %x\n", i );
#if	VERBOSE
	printf( "aha_interrupt: %x\n", i );
#endif
	switch( i & AHA_ALL_INTERRUPTS ) {
	case AHA_RESETED:
#if	VERBOSE
		printf( "aha: reseted\n" );
#endif
		break;
	case AHA_CMD_DONE:
#if	VERBOSE
		printf( "aha: adapter command completed\n" );
#endif
		break;
	case AHA_MBO_EMPTY:
#if	VERBOSE
		printf( "aha: MAILBOX emptied\n" );
#endif
		defer( aha_start, (char *)0 );
		break;
	case AHA_MBI_STORED:
#if	VERBOSE
		printf( "aha: MAILBOX in stored\n" );
#endif
		aha_completed();
		break;
	default:
		printf( "aha: multiple interrupts not yet handled\n" );
	}
	outb( aha_i_o_base+AHA_CONTROL, AHA_INTRRESET );
}

aha_ioctl()
{
	printf( "aha_ioctl: Not implemented\n" );
}

#if	VERBOSE
static	unsigned char vec[256];

static	aha_ports_are() {
	printf( "aha_ports_are: %x %x %x\n",
		INB(aha_i_o_base+0),
		INB(aha_i_o_base+1),
		INB(aha_i_o_base+2) );
}

static	aha_inquiry_is() {
	printf( "aha_inquiry:" );
	printf( "... aha_present = %d, ", aha_present() );
	printf( "%s\n", aha_last_msg() );
	aha_cmd_out( AHA_DO_INQUIRY );

	aha_get_data( &vec[0], 4 );
	printf( " board id '%c'", vec[0] );
	printf( ", options '%c'", vec[1] );
	printf( ", HW '%c'", vec[2] );
	printf( ", FW '%c'\n", vec[3] );
}

void	aha_setup_is() {
	register i;

	printf( "Setup and Data:\n" );
	aha_cmd_out( AHA_DO_GET_SETUP );
	aha_cmd_out( 16 );
	aha_get_data( &vec[0], 16 );
	printf( "  Data Xfer %s Sync (J1)\n", (vec[0]&1) ? "is" : "not" );
	printf( "  Parity %s Enabled (J1)\n", (vec[0]&2) ? "is" : "not" );
	switch( vec[1] ) {
	case AHA_SPEED_5_0_MB:
		printf( "  5.0 Mb/sec.\n" );	break;
	case AHA_SPEED_6_7_MB:
		printf( "  6.7 Mb/sec.\n" );	break;
	case AHA_SPEED_8_0_MB:
		printf( "  8.0 Mb/sec.\n" );	break;
	case AHA_SPEED_10_MB:
		printf( "  10 Mb/sec.\n" );	break;
	case AHA_SPEED_5_7_MB:
		printf( "  5.7 Mb/sec.\n" );	break;
	default:
		if( vec[1] & 0x80 )
			printf( "  Pulse Read %d, Write %d, Strobe off %d\n",
				50*(2+(vec[1]>>4)&0x7), 50*(2+(vec[1]&7)),
				vec[1] & 0x80 ? 150 : 100 );
	}
	printf( "  Bus Time ON %d, OFF %d\n", vec[2], vec[3] );
	printf( "  %d Mailboxes at %x|%x|%x\n", vec[4],
		vec[5], vec[6], vec[7] );
	for( i = 0; i < 8; ++i )
		if( vec[i+8] )
			printf( "  Target [%d] = Sync Neg %x\n", i, vec[i+8] );
}

static	aha_mailboxes_are( n, adr )
mailentry *adr;
{
	register i;

	printf( "addresses for mailbox is %x:%x\n", (long)adr );
	for( i = 0; i < n; ++i, ++adr )
		printf( "  mbo[%x] = %x %x|%x|%x\n",
			i, adr->cmd, adr->adr[0], adr->adr[1], adr->adr[2] );
	for( i = 0; i < n; ++i, ++adr )
		printf( "  mbi[%x] = %x %x|%x|%x\n",
			i, adr->cmd, adr->adr[0], adr->adr[1], adr->adr[2] );
}

void	aha_status()
{
	aha_ports_are();
	aha_inquiry_is();
	aha_devices_are();
	aha_setup_is();
	aha_mailboxes_are( MAX_MAILBOX, mailbox_out );
}

aha_ccb_print( ccb )
ccb_t	*ccb;
{
	register i;
	register unsigned char *cp;

	printf( "ccb @%x, sw @%x, bp @%x, flag %o\n",
		ccb, ccb->ccb_sw, ccb->ccb_sw->sw_bp,
		ccb->ccb_sw->sw_bp->b_flag );
	printf( "op %d, ", ccb->opcode );
	printf( "target ID=%d, ", (ccb->target>>5) & 0x7 );
	printf( "LUN=%d, ", (ccb->target & 0x7) );
	printf( "dir=%s%s\n",	(ccb->target&AHA_CCB_DATA_IN)?"IN":"",
				(ccb->target&AHA_CCB_DATA_OUT)?"OUT":"" );
	printf( "data len %x|%x|%x, adr %x|%x|%x\n",
		ccb->datalen[0],ccb->datalen[1],ccb->datalen[2],
		ccb->dataptr[0],ccb->dataptr[1],ccb->dataptr[2] );
	printf( "status host=%x, target=%x\n",
		ccb->hoststatus, ccb->targetstatus );
	printf( "cmddata[%d]:", ccb->cmdlen );
	for( i = 0, cp = ccb->cmd_status; i < ccb->cmdlen; ++i )
		printf( " %x", *cp++ );
	printf( "\nrequest sense[%d]:", ccb->senselen );
	for( i = 0; i < ccb->senselen; ++i )
		printf( " %x", *cp++ );
	if( i = cp[-1] ) {
		printf( "\n   + " );
		while( --i >= 0 )
			printf( " %x", *cp++ );
	}
	printf( "\n" );
}

#endif	/* VERBOSE */


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.