summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/in2000.c
blob: 1669732fe133f13e8411201a97d5d6bfb201dda0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
/*
 *  This file is in2000.c, written and
 *  Copyright (C) 1993  Brad McLean
 *	Last edit 1/19/95 TZ
 * Disclaimer:
 * Note:  This is ugly.  I know it, I wrote it, but my whole
 * focus was on getting the damn thing up and out quickly.
 * Future stuff that would be nice:  Command chaining, and
 * a local queue of commands would speed stuff up considerably.
 * Disconnection needs some supporting code.  All of this
 * is beyond the scope of what I wanted to address, but if you
 * have time and patience, more power to you.
 * Also, there are some constants scattered throughout that
 * should have defines, and I should have built functions to
 * address the registers on the WD chip.
 * Oh well, I'm out of time for this project.
 * The one good thing to be said is that you can use the card.
 */

/*
 * This module was updated by Shaun Savage first on 5-13-93
 * At that time the write was fixed, irq detection, and some
 * timing stuff.  since that time other problems were fixed.
 * On 7-20-93 this file was updated for patch level 11
 * There are still problems with it but it work on 95% of
 * the machines.  There are still problems with it working with
 * IDE drives, as swap drive and HD that support reselection.
 * But for most people it will work.
 */
/* More changes by Bill Earnest, wde@aluxpo.att.com
 * through 4/07/94. Includes rewrites of FIFO routines,
 * length-limited commands to make swap partitions work.
 * Merged the changes released by Larry Doolittle, based on input
 * from Jon Luckey, Roger Sunshine, John Shifflett. The FAST_FIFO
 * doesn't work for me. Scatter-gather code from Eric. The change to
 * an IF stmt. in the interrupt routine finally made it stable.
 * Limiting swap request size patch to ll_rw_blk.c not needed now.
 * Please ignore the clutter of debug stmts., pretty can come later.
 */
/* Merged code from Matt Postiff improving the auto-sense validation
 * for all I/O addresses. Some reports of problems still come in, but
 * have been unable to reproduce or localize the cause. Some are from
 * LUN > 0 problems, but that is not host specific. Now 6/6/94.
 */
/* Changes for 1.1.28 kernel made 7/19/94, code not affected. (WDE)
 */
/* Changes for 1.1.43+ kernels made 8/25/94, code added to check for
 * new BIOS version, derived by jshiffle@netcom.com. (WDE)
 *
 * 1/7/95 Fix from Peter Lu (swift@world.std.com) for datalen vs. dataptr
 * logic, much more stable under load.
 *
 * 1/19/95 (zerucha@shell.portal.com) Added module and biosparam support for
 * larger SCSI hard drives (untested).
 */

#include <linux/kernel.h>
#include <linux/head.h>
#include <linux/types.h>
#include <linux/string.h>

#include <linux/sched.h>
#include <asm/dma.h>

#include <asm/system.h>
#include <asm/io.h>
#include "../block/blk.h"
#include "scsi.h"
#include "hosts.h"
#include "sd.h"

#include "in2000.h"

/*#define FAST_FIFO_IO*/

/*#define DEBUG*/
#ifdef DEBUG
#define DEB(x) x
#else
#define DEB(x)
#endif

/* These functions are based on include/asm/io.h */
#ifndef inw
inline static unsigned short inw( unsigned short port )
{
   unsigned short _v;
   
   __asm__ volatile ("inw %1,%0"
		     :"=a" (_v):"d" ((unsigned short) port));
   return _v;
}
#endif

#ifndef outw
inline static void outw( unsigned short value, unsigned short port )
{
   __asm__ volatile ("outw %0,%1"
			: /* no outputs */
			:"a" ((unsigned short) value),
			"d" ((unsigned short) port));
}
#endif

/* These functions are lifted from drivers/block/hd.c */

#define port_read(port,buf,nr) \
__asm__("cld;rep;insw": :"d" (port),"D" (buf),"c" (nr):"cx","di")

#define port_write(port,buf,nr) \
__asm__("cld;rep;outsw": :"d" (port),"S" (buf),"c" (nr):"cx","si")

static unsigned int base;
static unsigned int ficmsk;
static unsigned char irq_level;
static int in2000_datalen;
static unsigned int in2000_nsegment;
static unsigned int in2000_current_segment;
static unsigned short *in2000_dataptr;
static char	in2000_datawrite;
static struct scatterlist * in2000_scatter;
static Scsi_Cmnd *in2000_SCptr = 0;

static void (*in2000_done)(Scsi_Cmnd *);

static int in2000_test_port(int index)
{
    static const int *bios_tab[] = {
	(int *) 0xc8000, (int *) 0xd0000, (int *) 0xd8000 };
    int	i;
    char    tmp;

    tmp = inb(INFLED);
	/* First, see if the DIP switch values are valid */
	/* The test of B7 may fail on some early boards, mine works. */
    if ( ((~tmp & 0x3) != index ) || (tmp & 0x80) || !(tmp & 0x4) )
    	return 0;
    printk("IN-2000 probe got dip setting of %02X\n", tmp);
    tmp = inb(INVERS);
/* Add some extra sanity checks here */
    for(i=0; i < 3; i++)
	if(*(bios_tab[i]+0x04) == 0x41564f4e ||
		*(bios_tab[i]+0xc) == 0x61776c41) {
	  printk("IN-2000 probe found hdw. vers. %02x, BIOS at %06x\n",
		tmp, (unsigned int)bios_tab[i]);
		return 1;
	}
    printk("in2000 BIOS not found.\n");
    return 0;
}


/*
 * retrieve the current transaction counter from the WD
 */

static unsigned in2000_txcnt(void)
{
    unsigned total=0;

    if(inb(INSTAT) & 0x20) return 0xffffff;	/* not readable now */
    outb(TXCNTH,INSTAT);	/* then autoincrement */
    total =  (inb(INDATA) & 0xff) << 16;
    outb(TXCNTM,INSTAT);
    total += (inb(INDATA) & 0xff) << 8;
    outb(TXCNTL,INSTAT);
    total += (inb(INDATA) & 0xff);
    return total;
}

/*
 * Note: the FIFO is screwy, and has a counter granularity of 16 bytes, so
 * we have to reconcile the FIFO counter, the transaction byte count from the
 * WD chip, and of course, our desired transaction size.  It may look strange,
 * and could probably use improvement, but it works, for now.
 */

static void in2000_fifo_out(void)	/* uses FIFOCNTR */
{
    unsigned count, infcnt, txcnt;

    infcnt = inb(INFCNT)& 0xfe;	/* FIFO counter */
    do {
	txcnt = in2000_txcnt();
/*DEB(printk("FIw:%d %02x %d\n", in2000_datalen, infcnt, txcnt));*/
	count = (infcnt << 3) - 32;	/* don't fill completely */
	if ( count > in2000_datalen )
	    count = in2000_datalen;	/* limit to actual data on hand */
	count >>= 1;		/* Words, not bytes */
#ifdef FAST_FIFO_IO
	if ( count ) {
		port_write(INFIFO, in2000_dataptr, count);
		in2000_datalen -= (count<<1);
	}
#else
	while ( count-- )
	    {
		outw(*in2000_dataptr++, INFIFO);
		in2000_datalen -= 2;
	    }
#endif
    } while((in2000_datalen > 0) && ((infcnt = (inb(INFCNT)) & 0xfe) >= 0x20) );
    /* If scatter-gather, go on to next segment */
    if( !in2000_datalen && ++in2000_current_segment < in2000_nsegment)
      {
      in2000_scatter++;
      in2000_datalen = in2000_scatter->length;
      in2000_dataptr = (unsigned short*)in2000_scatter->address;
      }
    if ( in2000_datalen <= 0 )
    {
	ficmsk = 0;
	count = 32;	/* Always says to use this much flush */
	while ( count-- )
	    outw(0, INFIFO);
	outb(2, ININTR); /* Mask FIFO Interrupts when done */
    }
}

static void in2000_fifo_in(void)	/* uses FIFOCNTR */
{
    unsigned fic, count, count2;

    count = inb(INFCNT) & 0xe1;
    do{
	count2 = count;
	count = (fic = inb(INFCNT)) & 0xe1;
    } while ( count != count2 );
DEB(printk("FIir:%d %02x %08x\n", in2000_datalen,fic,(unsigned int )in2000_dataptr));
    do {
	count2 = in2000_txcnt();	/* bytes yet to come over SCSI bus */
DEB(printk("FIr:%d %02x %08x %08x\n", in2000_datalen,fic,count2,(unsigned int)in2000_dataptr));
	if(count2 > 65536) count2 = 0;
	if(fic > 128) count = 1024;
	  else if(fic > 64) count = 512;
	    else if (fic > 32) count = 256;
	      else if ( count2 < in2000_datalen ) /* if drive has < what we want */
	        count = in2000_datalen - count2;	/* FIFO has the rest */
	if ( count > in2000_datalen )	/* count2 is lesser of FIFO & rqst */
	    count2 = in2000_datalen >> 1;	/* converted to word count */
	else
	    count2 = count >> 1;
	count >>= 1;		/* also to words */
	count -= count2;	/* extra left over in FIFO */
#ifdef FAST_FIFO_IO
	if ( count2 ) {
		port_read(INFIFO, in2000_dataptr, count2);
		in2000_datalen -= (count2<<1);
	}
#else
	while ( count2-- )
	{
	    *in2000_dataptr++ = inw(INFIFO);
	    in2000_datalen -=2;
	}
#endif
    } while((in2000_datalen > 0) && (fic = inb(INFCNT)) );
DEB(printk("FIer:%d %02x %08x\n", in2000_datalen,fic,(unsigned int )in2000_dataptr));
/*    while ( count-- )
    	inw(INFIFO);*/	/* Throw away some extra stuff */
    if( !in2000_datalen && ++in2000_current_segment < in2000_nsegment)
      {
      in2000_scatter++;
      in2000_datalen = in2000_scatter->length;
      in2000_dataptr = (unsigned short*)in2000_scatter->address;
      }
    if ( ! in2000_datalen ){
	outb(2, ININTR); /* Mask FIFO Interrupts when done */
	ficmsk = 0;}
}

static void in2000_intr_handle(int irq, struct pt_regs *regs)
{
    int result=0;
    unsigned int count,auxstatus,scsistatus,cmdphase,scsibyte;
    int action=0;
    Scsi_Cmnd *SCptr;

  DEB(printk("INT:%d %02x %08x\n", in2000_datalen, inb(INFCNT),(unsigned int)in2000_dataptr));

    if (( (ficmsk & (count = inb(INFCNT))) == 0xfe ) ||
		( (inb(INSTAT) & 0x8c) == 0x80))
	{	/* FIFO interrupt or WD interrupt */
   	auxstatus = inb(INSTAT);	/* need to save now */
   	outb(SCSIST,INSTAT);
   	scsistatus = inb(INDATA); /* This clears the WD intrpt bit */
   	outb(TARGETU,INSTAT);	/* then autoincrement */
   	scsibyte = inb(INDATA);	/* Get the scsi status byte */
   	outb(CMDPHAS,INSTAT);
   	cmdphase = inb(INDATA);
   	DEB(printk("(int2000:%02x %02x %02x %02x %02x)\n",count,auxstatus,
		scsistatus,cmdphase,scsibyte));

	/* Why do we assume that we need to send more data here??? ERY */
   	if ( in2000_datalen )	/* data xfer pending */
   	    {
   	    if ( in2000_dataptr == NULL )
		printk("int2000: dataptr=NULL datalen=%d\n",
			in2000_datalen);
	    else if ( in2000_datawrite )
		in2000_fifo_out();
	    else
		in2000_fifo_in();
   	    } 
	if ( (auxstatus & 0x8c) == 0x80 )
	    {	/* There is a WD Chip interrupt & register read good */
	    outb(2,ININTR);	/* Disable fifo interrupts */
	    ficmsk = 0;
	    result = DID_OK << 16;
	    /* 16=Select & transfer complete, 85=got disconnect */
	    if ((scsistatus != 0x16) && (scsistatus != 0x85)
		&& (scsistatus != 0x42)){
/*	   	printk("(WDi2000:%02x %02x %02x %02x %02x)\n",count,auxstatus,
			scsistatus,cmdphase,scsibyte);*/
/*		printk("QDAT:%d %08x %02x\n",
		in2000_datalen,(unsigned int)in2000_dataptr,ficmsk);*/
		;
	    }
		switch ( scsistatus & 0xf0 )
		    {
		    case	0x00:	/* Card Reset Completed */
			action = 3;
			break;
		    case	0x10:	/* Successful Command Completion */
			if ( scsistatus & 0x8 )
		    	    action = 1;
			break;
		    case	0x20:	/* Command Paused or Aborted */
			if ( (scsistatus & 0x8) )
		    	    action = 1;
			else if ( (scsistatus & 7) < 2 )
		    		action = 2;
			     else
		    		result = DID_ABORT << 16;
			break;
		    case	0x40:	/* Terminated early */
			if ( scsistatus & 0x8 )
		     	    action = 1;
			else if ( (scsistatus & 7) > 2 )
		     		action = 2;
			     else
		    		result = DID_TIME_OUT << 16;
			break;
		    case	0x80:	/* Service Required from SCSI bus */
			if ( scsistatus & 0x8 )
			    action = 1;
			else
			    action = 2;
			break;
		    }		/* end switch(scsistatus) */
		outb(0,INFLED);
		switch ( action )
		    {
		    case	0x02:	/* Issue an abort */
			outb(COMMAND,INSTAT);
			outb(1,INDATA); 	/* ABORT COMMAND */
			result = DID_ABORT << 16;
		    case	0x00:	/* Basically all done */
			if ( ! in2000_SCptr )
			    return;
			in2000_SCptr->result = result | scsibyte;
			SCptr = in2000_SCptr;
			in2000_SCptr = 0;
			if ( in2000_done )
		     	    (*in2000_done)(SCptr);
			break;
		    case	0x01:	/* We need to reissue a command */
			outb(CMDPHAS,INSTAT);
			switch ( scsistatus & 7 )
			    {
			    case	0:	/* Data out phase */
		    	    case	1:	/* Data in phase */
		    	    case	4:	/* Unspec info out phase */
		    	    case	5:	/* Unspec info in phase */
		    	    case	6:	/* Message in phase */
		    	    case	7:	/* Message in phase */
				outb(0x41,INDATA); /* rdy to disconn */
				break;
		    	    case	2:	/* command phase */
				outb(0x30,INDATA); /* rdy to send cmd bytes */
				break;
		    	    case	3:	/* status phase */
				outb(0x45,INDATA); /* To go to status phase,*/
				outb(TXCNTH,INSTAT); /* elim. data, autoinc */
				outb(0,INDATA);
				outb(0,INDATA);
				outb(0,INDATA);
				in2000_datalen = 0;
				in2000_dataptr = 0;
				break;
			    }	/* end switch(scsistatus) */
			outb(COMMAND,INSTAT);
			outb(8,INDATA);	 /* RESTART THE COMMAND */
			break;
		    case	0x03:	/* Finish up a Card Reset */
			outb(TIMEOUT,INSTAT);	/* I got these values */
						/* by reverse Engineering */
			outb(IN2000_TMOUT,INDATA); /* the Always' bios. */
			outb(CONTROL,INSTAT);
			outb(0,INDATA);
			outb(SYNCTXR,INSTAT);
			outb(0x40,INDATA);	/* async, 4 cyc xfer per. */
			break;
		    }		/* end switch(action) */
	    }			/* end if auxstatus for WD int */
	}			/* end while intrpt active */
}

int in2000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
{
    unchar direction;
    unchar *cmd = (unchar *) SCpnt->cmnd;
    unchar target = SCpnt->target;
    void *buff = SCpnt->request_buffer;
    unsigned long flags;
    int bufflen = SCpnt->request_bufflen;
    int timeout, size, loop;
    int i;

    /*
     * This SCSI command has no data phase, but unfortunately the mid-level
     * SCSI drivers ask for 256 bytes of data xfer.  Our card hangs if you
     * do this, so we protect against it here.  It would be nice if the mid-
     * level could be changed, but who knows if that would break other host
     * adapter drivers.
     */
    if ( *cmd == TEST_UNIT_READY )
	bufflen = 0;

    /*
     * What it looks like.  Boy did I get tired of reading its output.
     */
    if (*cmd == READ_10 || *cmd == WRITE_10) {
	i = xscsi2int((cmd+1));
    } else if (*cmd == READ_6 || *cmd == WRITE_6) {
	i = scsi2int((cmd+1));
    } else {
	i = -1;
    }
#ifdef DEBUG
    printk("in2000qcmd: pos %d len %d ", i, bufflen);
    printk("scsi cmd:");
    for (i = 0; i < SCpnt->cmd_len; i++) printk("%02x ", cmd[i]);
    printk("\n");
#endif
    direction = 1;	/* assume for most commands */
    if (*cmd == WRITE_10 || *cmd == WRITE_6)
	direction = 0;
    size = SCpnt->cmd_len;	/* CDB length */ 
    /*
     * Setup our current pointers
     * This is where you would allocate a control structure in a queue,
     * If you were going to upgrade this to do multiple issue.
     * Note that datalen and dataptr exist because we can change the
     * values during the course of the operation, while managing the
     * FIFO.
     * Note the nasty little first clause.  In theory, the mid-level
     * drivers should never hand us more than one command at a time,
     * but just in case someone gets cute in configuring the driver,
     * we'll protect them, although not very politely.
     */
    if ( in2000_SCptr )
    {
	printk("in2000_queue_command waiting for free command block!\n");
	while ( in2000_SCptr )
	    barrier();
    }
    for ( timeout = jiffies + 5; timeout > jiffies; )
    {
	if ( ! ( inb(INSTAT) & 0xb0 ) )
	{
	    timeout = 0;
	    break;
	}
	else
	{
	    inb(INSTAT);
	    outb(SCSIST,INSTAT);
	    inb(INDATA);
	    outb(TARGETU,INSTAT); 	/* then autoinc */
	    inb(INDATA);
	    inb(INDATA);
	}
    }
    if ( timeout )
    {
	printk("in2000_queue_command timeout!\n");
	SCpnt->result = DID_TIME_OUT << 16;
	(*done)(SCpnt);
	return 1;
    }
    /* Added for scatter-gather support */
    in2000_nsegment = SCpnt->use_sg;
    in2000_current_segment = 0;
    if(SCpnt->use_sg){
      in2000_scatter = (struct scatterlist *) buff;
      in2000_datalen = in2000_scatter->length;
      in2000_dataptr = (unsigned short*)in2000_scatter->address;
    } else {
      in2000_scatter = NULL;
      in2000_datalen = bufflen;
      in2000_dataptr = (unsigned short*) buff;
    };
    in2000_done = done;
    in2000_SCptr = SCpnt;
    /*
     * Write the CDB to the card, then the LUN, the length, and the target.
     */
    outb(TOTSECT, INSTAT);	/* start here then autoincrement */
    for ( loop=0; loop < size; loop++ )
	outb(cmd[loop],INDATA);
    outb(TARGETU,INSTAT);
    outb(SCpnt->lun & 7,INDATA);
    SCpnt->host_scribble = NULL;
    outb(TXCNTH,INSTAT);	/* then autoincrement */
    outb(bufflen>>16,INDATA);
    outb(bufflen>>8,INDATA);
    outb(bufflen,INDATA);
    outb(target&7,INDATA);
    /*
     * Set up the FIFO
     */
    save_flags(flags);
    cli();		/* so FIFO init waits till WD set */
    outb(0,INFRST);
    if ( direction == 1 )
    {
	in2000_datawrite = 0;
	outb(0,INFWRT);
    }
    else
    {
	in2000_datawrite = 1;
	for ( loop=16; --loop; ) /* preload the outgoing fifo */
	    {
		outw(*in2000_dataptr++,INFIFO);
		if(in2000_datalen > 0) in2000_datalen-=2;
	    }
    }
    ficmsk = 0xff;
    /*
     * Start it up
     */
    outb(CONTROL,INSTAT);	/* WD BUS Mode */
    outb(0x4C,INDATA);
    if ( in2000_datalen )		/* if data xfer cmd */
        outb(0,ININTR);		/* Enable FIFO intrpt some boards? */
    outb(COMMAND,INSTAT);
    outb(0,INNLED);
    outb(8,INDATA);		/* Select w/ATN & Transfer */
    restore_flags(flags);			/* let the intrpt rip */
    return 0;
}

static volatile int internal_done_flag = 0;
static volatile int internal_done_errcode = 0;

static void internal_done(Scsi_Cmnd * SCpnt)
{
    internal_done_errcode = SCpnt->result;
    ++internal_done_flag;
}

int in2000_command(Scsi_Cmnd * SCpnt)
{
    in2000_queuecommand(SCpnt, internal_done);

    while (!internal_done_flag);
    internal_done_flag = 0;
    return internal_done_errcode;
}

int in2000_detect(Scsi_Host_Template * tpnt)
{
/* Order chosen to reduce conflicts with some multi-port serial boards */
    int base_tab[] = { 0x220,0x200,0x110,0x100 };
    int int_tab[] = { 15,14,11,10 };
    struct Scsi_Host * shpnt;
    int loop, tmp;

    DEB(printk("in2000_detect: \n"));
    
    for ( loop=0; loop < 4; loop++ )
    {
	base = base_tab[loop];
	if ( in2000_test_port(loop))  break;
    }
    if ( loop == 4 )
	return 0;

  /* Read the dip switch values again for miscellaneous checking and
     informative messages */
  tmp = inb(INFLED);

  /* Bit 2 tells us if interrupts are disabled */
  if ( (tmp & 0x4) == 0 ) {
    printk("The IN-2000 is not configured for interrupt operation\n");
    printk("Change the DIP switch settings to enable interrupt operation\n");
  }

  /* Bit 6 tells us about floppy controller */
  printk("IN-2000 probe found floppy controller on IN-2000 ");
  if ( (tmp & 0x40) == 0)
    printk("enabled\n");
  else
    printk("disabled\n");

  /* Bit 5 tells us about synch/asynch mode */
  printk("IN-2000 probe found IN-2000 in ");
  if ( (tmp & 0x20) == 0)
    printk("synchronous mode\n");
  else
    printk("asynchronous mode\n");

    irq_level = int_tab [ ((~inb(INFLED)>>3)&0x3) ];

    printk("Configuring IN2000 at IO:%x, IRQ %d"
#ifdef FAST_FIFO_IO
		" (using fast FIFO I/O code)"
#endif
		"\n",base, irq_level);

    outb(2,ININTR);	/* Shut off the FIFO first, so it won't ask for data.*/
    if (request_irq(irq_level,in2000_intr_handle, 0, "in2000"))
    {
	printk("in2000_detect: Unable to allocate IRQ.\n");
	return 0;
    }
    outb(0,INFWRT);	/* read mode so WD can intrpt */
    outb(SCSIST,INSTAT);
    inb(INDATA);	/* free status reg, clear WD intrpt */
    outb(OWNID,INSTAT);
    outb(0x7,INDATA);	/* we use addr 7 */
    outb(COMMAND,INSTAT);
    outb(0,INDATA);	/* do chip reset */
    shpnt = scsi_register(tpnt, 0);
    /* Set these up so that we can unload the driver properly. */
    shpnt->io_port = base;
    shpnt->n_io_port = 12;
    shpnt->irq = irq_level;
    request_region(base, 12,"in2000");  /* Prevent other drivers from using this space */
    return 1;
}

int in2000_abort(Scsi_Cmnd * SCpnt)
{
    DEB(printk("in2000_abort\n"));
    /*
     * Ask no stupid questions, just order the abort.
     */
    outb(COMMAND,INSTAT);
    outb(1,INDATA);	/* Abort Command */
    return 0;
}

static inline void delay( unsigned how_long )
{
    unsigned long time = jiffies + how_long;
    while (jiffies < time) ;
}

int in2000_reset(Scsi_Cmnd * SCpnt)
{
    DEB(printk("in2000_reset called\n"));
    /*
     * Note: this is finished off by an incoming interrupt
     */
    outb(0,INFWRT);	/* read mode so WD can intrpt */
    outb(SCSIST,INSTAT);
    inb(INDATA);
    outb(OWNID,INSTAT);
    outb(0x7,INDATA);	/* ID=7,noadv, no parity, clk div=2 (8-10Mhz clk) */
    outb(COMMAND,INSTAT);
    outb(0,INDATA);	/* reset WD chip */
    delay(2);
#ifdef SCSI_RESET_PENDING
    return SCSI_RESET_PENDING;
#else
    if(SCpnt) SCpnt->flags |= NEEDS_JUMPSTART;
    return 0;
#endif
}

int in2000_biosparam(Disk * disk, int dev, int* iinfo)
	{
	  int size = disk->capacity;
    DEB(printk("in2000_biosparam\n"));
    iinfo[0] = 64;
    iinfo[1] = 32;
    iinfo[2] = size >> 11;
/* This should approximate the large drive handling that the DOS ASPI manager
   uses.  Drives very near the boundaries may not be handled correctly (i.e.
   near 2.0 Gb and 4.0 Gb) */
    if (iinfo[2] > 1024) {
	iinfo[0] = 64;
	iinfo[1] = 63;
	iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
	}
    if (iinfo[2] > 1024) {
	iinfo[0] = 128;
	iinfo[1] = 63;
	iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
	}
    if (iinfo[2] > 1024) {
	iinfo[0] = 255;
	iinfo[1] = 63;
	iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
	if (iinfo[2] > 1023)
	    iinfo[2] = 1023;
	}
    return 0;
    }

#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
Scsi_Host_Template driver_template = IN2000;

#include "scsi_module.c"
#endif