root/drivers/scsi/in2000.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. inw
  2. outw
  3. in2000_test_port
  4. in2000_txcnt
  5. in2000_fifo_out
  6. in2000_fifo_in
  7. in2000_intr_handle
  8. in2000_queuecommand
  9. internal_done
  10. in2000_command
  11. in2000_detect
  12. in2000_abort
  13. delay
  14. in2000_reset
  15. in2000_biosparam

   1 /*
   2  *  This file is in2000.c, written and
   3  *  Copyright (C) 1993  Brad McLean
   4  *      Last edit 08/25/94 WDE
   5  * Disclaimer:
   6  * Note:  This is ugly.  I know it, I wrote it, but my whole
   7  * focus was on getting the damn thing up and out quickly.
   8  * Future stuff that would be nice:  Command chaining, and
   9  * a local queue of commands would speed stuff up considerably.
  10  * Disconnection needs some supporting code.  All of this
  11  * is beyond the scope of what I wanted to address, but if you
  12  * have time and patience, more power to you.
  13  * Also, there are some constants scattered throughout that
  14  * should have defines, and I should have built functions to
  15  * address the registers on the WD chip.
  16  * Oh well, I'm out of time for this project.
  17  * The one good thing to be said is that you can use the card.
  18  */
  19 
  20 /*
  21  * This module was updated by Shaun Savage first on 5-13-93
  22  * At that time the write was fixed, irq detection, and some
  23  * timing stuff.  since that time other problems were fixed.
  24  * On 7-20-93 this file was updated for patch level 11
  25  * There are still problems with it but it work on 95% of
  26  * the machines.  There are still problems with it working with
  27  * IDE drives, as swap drive and HD that support reselection.
  28  * But for most people it will work.
  29  */
  30 /* More changes by Bill Earnest, wde@aluxpo.att.com
  31  * through 4/07/94. Includes rewrites of FIFO routines,
  32  * length-limited commands to make swap partitions work.
  33  * Merged the changes released by Larry Doolittle, based on input
  34  * from Jon Luckey, Roger Sunshine, John Shifflett. The FAST_FIFO
  35  * doesn't work for me. Scatter-gather code from Eric. The change to
  36  * an IF stmt. in the interrupt routine finally made it stable.
  37  * Limiting swap request size patch to ll_rw_blk.c not needed now.
  38  * Please ignore the clutter of debug stmts., pretty can come later.
  39  */
  40 /* Merged code from Matt Postiff improving the auto-sense validation
  41  * for all I/O addresses. Some reports of problems still come in, but
  42  * have been unable to reproduce or localize the cause. Some are from
  43  * LUN > 0 problems, but that is not host specific. Now 6/6/94.
  44  */
  45 /* Changes for 1.1.28 kernel made 7/19/94, code not affected. (WDE)
  46  */
  47 /* Changes for 1.1.43+ kernels made 8/25/94, code added to check for
  48  * new BIOS version, derived by jshiffle@netcom.com. (WDE)
  49  */
  50 
  51 #include <linux/kernel.h>
  52 #include <linux/head.h>
  53 #include <linux/types.h>
  54 #include <linux/string.h>
  55 
  56 #include <linux/sched.h>
  57 #include <asm/dma.h>
  58 
  59 #include <asm/system.h>
  60 #include <asm/io.h>
  61 #include "../block/blk.h"
  62 #include "scsi.h"
  63 #include "hosts.h"
  64 #include "sd.h"
  65 
  66 #include "in2000.h"
  67 
  68 /*#define FAST_FIFO_IO*/
  69 
  70 /*#define DEBUG*/
  71 #ifdef DEBUG
  72 #define DEB(x) x
  73 #else
  74 #define DEB(x)
  75 #endif
  76 
  77 /* These functions are based on include/asm/io.h */
  78 #ifndef inw
  79 inline static unsigned short inw( unsigned short port )
     /* [previous][next][first][last][top][bottom][index][help] */
  80 {
  81    unsigned short _v;
  82    
  83    __asm__ volatile ("inw %1,%0"
  84                      :"=a" (_v):"d" ((unsigned short) port));
  85    return _v;
  86 }
  87 #endif
  88 
  89 #ifndef outw
  90 inline static void outw( unsigned short value, unsigned short port )
     /* [previous][next][first][last][top][bottom][index][help] */
  91 {
  92    __asm__ volatile ("outw %0,%1"
  93                         : /* no outputs */
  94                         :"a" ((unsigned short) value),
  95                         "d" ((unsigned short) port));
  96 }
  97 #endif
  98 
  99 /* These functions are lifted from drivers/block/hd.c */
 100 
 101 #define port_read(port,buf,nr) \
 102 __asm__("cld;rep;insw": :"d" (port),"D" (buf),"c" (nr):"cx","di")
 103 
 104 #define port_write(port,buf,nr) \
 105 __asm__("cld;rep;outsw": :"d" (port),"S" (buf),"c" (nr):"cx","si")
 106 
 107 static unsigned int base;
 108 static unsigned int ficmsk;
 109 static unsigned char irq_level;
 110 static int in2000_datalen;
 111 static unsigned int in2000_nsegment;
 112 static unsigned int in2000_current_segment;
 113 static unsigned short *in2000_dataptr;
 114 static char     in2000_datawrite;
 115 static struct scatterlist * in2000_scatter;
 116 static Scsi_Cmnd *in2000_SCptr = 0;
 117 
 118 static void (*in2000_done)(Scsi_Cmnd *);
 119 
 120 static int in2000_test_port(int index)
     /* [previous][next][first][last][top][bottom][index][help] */
 121 {
 122     static const int *bios_tab[] = {
 123         (int *) 0xc8000, (int *) 0xd0000, (int *) 0xd8000 };
 124     int i;
 125     char    tmp;
 126 
 127     tmp = inb(INFLED);
 128         /* First, see if the DIP switch values are valid */
 129         /* The test of B7 may fail on some early boards, mine works. */
 130     if ( ((~tmp & 0x3) != index ) || (tmp & 0x80) || !(tmp & 0x4) )
 131         return 0;
 132     printk("IN-2000 probe got dip setting of %02X\n", tmp);
 133     tmp = inb(INVERS);
 134 /* Add some extra sanity checks here */
 135     for(i=0; i < 3; i++)
 136         if(*(bios_tab[i]+0x04) == 0x41564f4e ||
 137                 *(bios_tab[i]+0xc) == 0x61776c41) {
 138           printk("IN-2000 probe found hdw. vers. %02x, BIOS at %06x\n",
 139                 tmp, (unsigned int)bios_tab[i]);
 140                 return 1;
 141         }
 142     printk("in2000 BIOS not found.\n");
 143     return 0;
 144 }
 145 
 146 
 147 /*
 148  * retrieve the current transaction counter from the WD
 149  */
 150 
 151 static unsigned in2000_txcnt(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 152 {
 153     unsigned total=0;
 154 
 155     if(inb(INSTAT) & 0x20) return 0xffffff;     /* not readable now */
 156     outb(TXCNTH,INSTAT);        /* then autoincrement */
 157     total =  (inb(INDATA) & 0xff) << 16;
 158     outb(TXCNTM,INSTAT);
 159     total += (inb(INDATA) & 0xff) << 8;
 160     outb(TXCNTL,INSTAT);
 161     total += (inb(INDATA) & 0xff);
 162     return total;
 163 }
 164 
 165 /*
 166  * Note: the FIFO is screwy, and has a counter granularity of 16 bytes, so
 167  * we have to reconcile the FIFO counter, the transaction byte count from the
 168  * WD chip, and of course, our desired transaction size.  It may look strange,
 169  * and could probably use improvement, but it works, for now.
 170  */
 171 
 172 static void in2000_fifo_out(void)       /* uses FIFOCNTR */
     /* [previous][next][first][last][top][bottom][index][help] */
 173 {
 174     unsigned count, infcnt, txcnt;
 175 
 176     infcnt = inb(INFCNT)& 0xfe; /* FIFO counter */
 177     do {
 178         txcnt = in2000_txcnt();
 179 /*DEB(printk("FIw:%d %02x %d\n", in2000_datalen, infcnt, txcnt));*/
 180         count = (infcnt << 3) - 32;     /* don't fill completely */
 181         if ( count > in2000_datalen )
 182             count = in2000_datalen;     /* limit to actual data on hand */
 183         count >>= 1;            /* Words, not bytes */
 184 #ifdef FAST_FIFO_IO
 185         if ( count ) {
 186                 port_write(INFIFO, in2000_dataptr, count);
 187                 in2000_datalen -= (count<<1);
 188         }
 189 #else
 190         while ( count-- )
 191             {
 192                 outw(*in2000_dataptr++, INFIFO);
 193                 in2000_datalen -= 2;
 194             }
 195 #endif
 196     } while((in2000_datalen > 0) && ((infcnt = (inb(INFCNT)) & 0xfe) >= 0x20) );
 197     /* If scatter-gather, go on to next segment */
 198     if( !in2000_datalen && in2000_current_segment < in2000_nsegment)
 199       {
 200       in2000_scatter++;
 201       in2000_current_segment++;
 202       in2000_datalen = in2000_scatter->length;
 203       in2000_dataptr = (unsigned short*)in2000_scatter->address;
 204       }
 205     if ( in2000_datalen <= 0 )
 206     {
 207         ficmsk = 0;
 208         count = 32;     /* Always says to use this much flush */
 209         while ( count-- )
 210             outw(0, INFIFO);
 211         outb(2, ININTR); /* Mask FIFO Interrupts when done */
 212     }
 213 }
 214 
 215 static void in2000_fifo_in(void)        /* uses FIFOCNTR */
     /* [previous][next][first][last][top][bottom][index][help] */
 216 {
 217     unsigned fic, count, count2;
 218 
 219     count = inb(INFCNT) & 0xe1;
 220     do{
 221         count2 = count;
 222         count = (fic = inb(INFCNT)) & 0xe1;
 223     } while ( count != count2 );
 224 DEB(printk("FIir:%d %02x %08x\n", in2000_datalen,fic,(unsigned int )in2000_dataptr));
 225     do {
 226         count2 = in2000_txcnt();        /* bytes yet to come over SCSI bus */
 227 DEB(printk("FIr:%d %02x %08x %08x\n", in2000_datalen,fic,count2,(unsigned int)in2000_dataptr));
 228         if(count2 > 65536) count2 = 0;
 229         if(fic > 128) count = 1024;
 230           else if(fic > 64) count = 512;
 231             else if (fic > 32) count = 256;
 232               else if ( count2 < in2000_datalen ) /* if drive has < what we want */
 233                 count = in2000_datalen - count2;        /* FIFO has the rest */
 234         if ( count > in2000_datalen )   /* count2 is lesser of FIFO & rqst */
 235             count2 = in2000_datalen >> 1;       /* converted to word count */
 236         else
 237             count2 = count >> 1;
 238         count >>= 1;            /* also to words */
 239         count -= count2;        /* extra left over in FIFO */
 240 #ifdef FAST_FIFO_IO
 241         if ( count2 ) {
 242                 port_read(INFIFO, in2000_dataptr, count2);
 243                 in2000_datalen -= (count2<<1);
 244         }
 245 #else
 246         while ( count2-- )
 247         {
 248             *in2000_dataptr++ = inw(INFIFO);
 249             in2000_datalen -=2;
 250         }
 251 #endif
 252     } while((in2000_datalen > 0) && (fic = inb(INFCNT)) );
 253 DEB(printk("FIer:%d %02x %08x\n", in2000_datalen,fic,(unsigned int )in2000_dataptr));
 254 /*    while ( count-- )
 255         inw(INFIFO);*/  /* Throw away some extra stuff */
 256     if( !in2000_datalen && in2000_current_segment < in2000_nsegment)
 257       {
 258       in2000_scatter++;
 259       in2000_current_segment++;
 260       in2000_datalen = in2000_scatter->length;
 261       in2000_dataptr = (unsigned short*)in2000_scatter->address;
 262       }
 263     if ( ! in2000_datalen ){
 264         outb(2, ININTR); /* Mask FIFO Interrupts when done */
 265         ficmsk = 0;}
 266 }
 267 
 268 static void in2000_intr_handle(int foo)
     /* [previous][next][first][last][top][bottom][index][help] */
 269 {
 270     int result=0;
 271     unsigned int count,auxstatus,scsistatus,cmdphase,scsibyte;
 272     int action=0;
 273     Scsi_Cmnd *SCptr;
 274 
 275   DEB(printk("INT:%d %02x %08x\n", in2000_datalen, inb(INFCNT),(unsigned int)in2000_dataptr));
 276 
 277     if (( (ficmsk & (count = inb(INFCNT))) == 0xfe ) ||
 278                 ( (inb(INSTAT) & 0x8c) == 0x80))
 279         {       /* FIFO interrupt or WD interrupt */
 280         auxstatus = inb(INSTAT);        /* need to save now */
 281         outb(SCSIST,INSTAT);
 282         scsistatus = inb(INDATA); /* This clears the WD intrpt bit */
 283         outb(TARGETU,INSTAT);   /* then autoincrement */
 284         scsibyte = inb(INDATA); /* Get the scsi status byte */
 285         outb(CMDPHAS,INSTAT);
 286         cmdphase = inb(INDATA);
 287         DEB(printk("(int2000:%02x %02x %02x %02x %02x)\n",count,auxstatus,
 288                 scsistatus,cmdphase,scsibyte));
 289 
 290         /* Why do we assume that we need to send more data here??? ERY */
 291         if ( in2000_datalen && in2000_dataptr ) /* data xfer pending */
 292             {
 293             if ( in2000_datawrite )
 294                 in2000_fifo_out();
 295             else
 296                 in2000_fifo_in();
 297             } else ficmsk = 0;
 298         if ( (auxstatus & 0x8c) == 0x80 )
 299             {   /* There is a WD Chip interrupt & register read good */
 300             outb(2,ININTR);     /* Disable fifo interrupts */
 301             ficmsk = 0;
 302             result = DID_OK << 16;
 303             /* 16=Select & transfer complete, 85=got disconnect */
 304             if ((scsistatus != 0x16) && (scsistatus != 0x85)
 305                 && (scsistatus != 0x42)){
 306 /*              printk("(WDi2000:%02x %02x %02x %02x %02x)\n",count,auxstatus,
 307                         scsistatus,cmdphase,scsibyte);*/
 308 /*              printk("QDAT:%d %08x %02x\n",
 309                 in2000_datalen,(unsigned int)in2000_dataptr,ficmsk);*/
 310                 ;
 311             }
 312                 switch ( scsistatus & 0xf0 )
 313                     {
 314                     case        0x00:   /* Card Reset Completed */
 315                         action = 3;
 316                         break;
 317                     case        0x10:   /* Successful Command Completion */
 318                         if ( scsistatus & 0x8 )
 319                             action = 1;
 320                         break;
 321                     case        0x20:   /* Command Paused or Aborted */
 322                         if ( (scsistatus & 0x8) )
 323                             action = 1;
 324                         else if ( (scsistatus & 7) < 2 )
 325                                 action = 2;
 326                              else
 327                                 result = DID_ABORT << 16;
 328                         break;
 329                     case        0x40:   /* Terminated early */
 330                         if ( scsistatus & 0x8 )
 331                             action = 1;
 332                         else if ( (scsistatus & 7) > 2 )
 333                                 action = 2;
 334                              else
 335                                 result = DID_TIME_OUT << 16;
 336                         break;
 337                     case        0x80:   /* Service Required from SCSI bus */
 338                         if ( scsistatus & 0x8 )
 339                             action = 1;
 340                         else
 341                             action = 2;
 342                         break;
 343                     }           /* end switch(scsistatus) */
 344                 outb(0,INFLED);
 345                 switch ( action )
 346                     {
 347                     case        0x02:   /* Issue an abort */
 348                         outb(COMMAND,INSTAT);
 349                         outb(1,INDATA);         /* ABORT COMMAND */
 350                         result = DID_ABORT << 16;
 351                     case        0x00:   /* Basically all done */
 352                         if ( ! in2000_SCptr )
 353                             return;
 354                         in2000_SCptr->result = result | scsibyte;
 355                         SCptr = in2000_SCptr;
 356                         in2000_SCptr = 0;
 357                         if ( in2000_done )
 358                             (*in2000_done)(SCptr);
 359                         break;
 360                     case        0x01:   /* We need to reissue a command */
 361                         outb(CMDPHAS,INSTAT);
 362                         switch ( scsistatus & 7 )
 363                             {
 364                             case        0:      /* Data out phase */
 365                             case        1:      /* Data in phase */
 366                             case        4:      /* Unspec info out phase */
 367                             case        5:      /* Unspec info in phase */
 368                             case        6:      /* Message in phase */
 369                             case        7:      /* Message in phase */
 370                                 outb(0x41,INDATA); /* rdy to disconn */
 371                                 break;
 372                             case        2:      /* command phase */
 373                                 outb(0x30,INDATA); /* rdy to send cmd bytes */
 374                                 break;
 375                             case        3:      /* status phase */
 376                                 outb(0x45,INDATA); /* To go to status phase,*/
 377                                 outb(TXCNTH,INSTAT); /* elim. data, autoinc */
 378                                 outb(0,INDATA);
 379                                 outb(0,INDATA);
 380                                 outb(0,INDATA);
 381                                 in2000_datalen = 0;
 382                                 in2000_dataptr = 0;
 383                                 break;
 384                             }   /* end switch(scsistatus) */
 385                         outb(COMMAND,INSTAT);
 386                         outb(8,INDATA);  /* RESTART THE COMMAND */
 387                         break;
 388                     case        0x03:   /* Finish up a Card Reset */
 389                         outb(TIMEOUT,INSTAT);   /* I got these values */
 390                                                 /* by reverse Engineering */
 391                         outb(IN2000_TMOUT,INDATA); /* the Always' bios. */
 392                         outb(CONTROL,INSTAT);
 393                         outb(0,INDATA);
 394                         outb(SYNCTXR,INSTAT);
 395                         outb(0x40,INDATA);      /* async, 4 cyc xfer per. */
 396                         break;
 397                     }           /* end switch(action) */
 398             }                   /* end if auxstatus for WD int */
 399         }                       /* end while intrpt active */
 400 }
 401 
 402 int in2000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
     /* [previous][next][first][last][top][bottom][index][help] */
 403 {
 404     unchar direction;
 405     unchar *cmd = (unchar *) SCpnt->cmnd;
 406     unchar target = SCpnt->target;
 407     void *buff = SCpnt->request_buffer;
 408     int bufflen = SCpnt->request_bufflen;
 409     int timeout, size, loop;
 410     int i;
 411 
 412     /*
 413      * This SCSI command has no data phase, but unfortunately the mid-level
 414      * SCSI drivers ask for 256 bytes of data xfer.  Our card hangs if you
 415      * do this, so we protect against it here.  It would be nice if the mid-
 416      * level could be changed, but who knows if that would break other host
 417      * adapter drivers.
 418      */
 419     if ( *cmd == TEST_UNIT_READY )
 420         bufflen = 0;
 421 
 422     /*
 423      * What it looks like.  Boy did I get tired of reading it's output.
 424      */
 425     if (*cmd == READ_10 || *cmd == WRITE_10) {
 426         i = xscsi2int((cmd+1));
 427     } else if (*cmd == READ_6 || *cmd == WRITE_6) {
 428         i = scsi2int((cmd+1));
 429     } else {
 430         i = -1;
 431     }
 432 #ifdef DEBUG
 433     printk("in2000qcmd: pos %d len %d ", i, bufflen);
 434     printk("scsi cmd:");
 435     for (i = 0; i < SCpnt->cmd_len; i++) printk("%02x ", cmd[i]);
 436     printk("\n");
 437 #endif
 438     direction = 1;      /* assume for most commands */
 439     if (*cmd == WRITE_10 || *cmd == WRITE_6)
 440         direction = 0;
 441     size = SCpnt->cmd_len;      /* CDB length */ 
 442     /*
 443      * Setup our current pointers
 444      * This is where you would allocate a control structure in a queue,
 445      * If you were going to upgrade this to do multiple issue.
 446      * Note that datalen and dataptr exist because we can change the
 447      * values during the course of the operation, while managing the
 448      * FIFO.
 449      * Note the nasty little first clause.  In theory, the mid-level
 450      * drivers should never hand us more than one command at a time,
 451      * but just in case someone gets cute in configuring the driver,
 452      * we'll protect them, although not very politely.
 453      */
 454     if ( in2000_SCptr )
 455     {
 456         printk("in2000_queue_command waiting for free command block!\n");
 457         while ( in2000_SCptr );
 458     }
 459     for ( timeout = jiffies + 5; timeout > jiffies; )
 460     {
 461         if ( ! ( inb(INSTAT) & 0xb0 ) )
 462         {
 463             timeout = 0;
 464             break;
 465         }
 466         else
 467         {
 468             inb(INSTAT);
 469             outb(SCSIST,INSTAT);
 470             inb(INDATA);
 471             outb(TARGETU,INSTAT);       /* then autoinc */
 472             inb(INDATA);
 473             inb(INDATA);
 474         }
 475     }
 476     if ( timeout )
 477     {
 478         printk("in2000_queue_command timeout!\n");
 479         SCpnt->result = DID_TIME_OUT << 16;
 480         (*done)(SCpnt);
 481         return 1;
 482     }
 483     /* Added for scatter-gather support */
 484     in2000_nsegment = SCpnt->use_sg;
 485     in2000_current_segment = 0;
 486     if(SCpnt->use_sg){
 487       in2000_scatter = (struct scatterlist *) buff;
 488       in2000_datalen = in2000_scatter->length;
 489       in2000_dataptr = (unsigned short*)in2000_scatter->address;
 490     } else {
 491       in2000_scatter = NULL;
 492       in2000_datalen = bufflen;
 493       in2000_dataptr = (unsigned short*) buff;
 494     };
 495     in2000_done = done;
 496     in2000_SCptr = SCpnt;
 497     /*
 498      * Write the CDB to the card, then the LUN, the length, and the target.
 499      */
 500     outb(TOTSECT, INSTAT);      /* start here then autoincrement */
 501     for ( loop=0; loop < size; loop++ )
 502         outb(cmd[loop],INDATA);
 503     outb(TARGETU,INSTAT);
 504     outb(SCpnt->lun & 7,INDATA);
 505     SCpnt->host_scribble = NULL;
 506     outb(TXCNTH,INSTAT);        /* then autoincrement */
 507     outb(bufflen>>16,INDATA);
 508     outb(bufflen>>8,INDATA);
 509     outb(bufflen,INDATA);
 510     outb(target&7,INDATA);
 511     /*
 512      * Set up the FIFO
 513      */
 514     cli();              /* so FIFO init waits till WD set */
 515     outb(0,INFRST);
 516     if ( direction == 1 )
 517     {
 518         in2000_datawrite = 0;
 519         outb(0,INFWRT);
 520     }
 521     else
 522     {
 523         in2000_datawrite = 1;
 524         for ( loop=16; --loop; ) /* preload the outgoing fifo */
 525             {
 526                 outw(*in2000_dataptr++,INFIFO);
 527                 if(in2000_datalen > 0) in2000_datalen-=2;
 528             }
 529     }
 530     ficmsk = 0xff;
 531     /*
 532      * Start it up
 533      */
 534     outb(CONTROL,INSTAT);       /* WD BUS Mode */
 535     outb(0x4C,INDATA);
 536     if ( in2000_datalen )               /* if data xfer cmd */
 537         outb(0,ININTR);         /* Enable FIFO intrpt some boards? */
 538     outb(COMMAND,INSTAT);
 539     outb(0,INNLED);
 540     outb(8,INDATA);             /* Select w/ATN & Transfer */
 541     sti();                      /* let the intrpt rip */
 542     return 0;
 543 }
 544 
 545 static volatile int internal_done_flag = 0;
 546 static volatile int internal_done_errcode = 0;
 547 
 548 static void internal_done(Scsi_Cmnd * SCpnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 549 {
 550     internal_done_errcode = SCpnt->result;
 551     ++internal_done_flag;
 552 }
 553 
 554 int in2000_command(Scsi_Cmnd * SCpnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 555 {
 556     in2000_queuecommand(SCpnt, internal_done);
 557 
 558     while (!internal_done_flag);
 559     internal_done_flag = 0;
 560     return internal_done_errcode;
 561 }
 562 
 563 int in2000_detect(Scsi_Host_Template * tpnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 564 {
 565 /* Order chosen to reduce conflicts with some multi-port serial boards */
 566     int base_tab[] = { 0x220,0x200,0x110,0x100 };
 567     int int_tab[] = { 15,14,11,10 };
 568     struct Scsi_Host * shpnt;
 569     int loop, tmp;
 570 
 571     DEB(printk("in2000_detect: \n"));
 572     
 573     for ( loop=0; loop < 4; loop++ )
 574     {
 575         base = base_tab[loop];
 576         if ( in2000_test_port(loop))  break;
 577     }
 578     if ( loop == 4 )
 579         return 0;
 580 
 581   /* Read the dip switch values again for miscellaneous checking and
 582      informative messages */
 583   tmp = inb(INFLED);
 584 
 585   /* Bit 2 tells us if interrupts are disabled */
 586   if ( (tmp & 0x4) == 0 ) {
 587     printk("The IN-2000 is not configured for interrupt operation\n");
 588     printk("Change the DIP switch settings to enable interrupt operation\n");
 589   }
 590 
 591   /* Bit 6 tells us about floppy controller */
 592   printk("IN-2000 probe found floppy controller on IN-2000 ");
 593   if ( (tmp & 0x40) == 0)
 594     printk("enabled\n");
 595   else
 596     printk("disabled\n");
 597 
 598   /* Bit 5 tells us about synch/asynch mode */
 599   printk("IN-2000 probe found IN-2000 in ");
 600   if ( (tmp & 0x20) == 0)
 601     printk("synchronous mode\n");
 602   else
 603     printk("asynchronous mode\n");
 604 
 605     irq_level = int_tab [ ((~inb(INFLED)>>3)&0x3) ];
 606 
 607     printk("Configuring IN2000 at IO:%x, IRQ %d"
 608 #ifdef FAST_FIFO_IO
 609                 " (using fast FIFO I/O code)"
 610 #endif
 611                 "\n",base, irq_level);
 612 
 613     outb(2,ININTR);     /* Shut off the FIFO first, so it won't ask for data.*/
 614     if (request_irq(irq_level,in2000_intr_handle, 0, "in2000"))
 615     {
 616         printk("in2000_detect: Unable to allocate IRQ.\n");
 617         return 0;
 618     }
 619     outb(0,INFWRT);     /* read mode so WD can intrpt */
 620     outb(SCSIST,INSTAT);
 621     inb(INDATA);        /* free status reg, clear WD intrpt */
 622     outb(OWNID,INSTAT);
 623     outb(0x7,INDATA);   /* we use addr 7 */
 624     outb(COMMAND,INSTAT);
 625     outb(0,INDATA);     /* do chip reset */
 626     shpnt = scsi_register(tpnt, 0);
 627     /* Set these up so that we can unload the driver properly. */
 628     shpnt->io_port = base;
 629     shpnt->n_io_port = 12;
 630     shpnt->irq = irq_level;
 631     snarf_region(base, 12);  /* Prevent other drivers from using this space */
 632     return 1;
 633 }
 634 
 635 int in2000_abort(Scsi_Cmnd * SCpnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 636 {
 637     DEB(printk("in2000_abort\n"));
 638     /*
 639      * Ask no stupid questions, just order the abort.
 640      */
 641     outb(COMMAND,INSTAT);
 642     outb(1,INDATA);     /* Abort Command */
 643     return 0;
 644 }
 645 
 646 static inline void delay( unsigned how_long )
     /* [previous][next][first][last][top][bottom][index][help] */
 647 {
 648     unsigned long time = jiffies + how_long;
 649     while (jiffies < time) ;
 650 }
 651 
 652 int in2000_reset(Scsi_Cmnd * SCpnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 653 {
 654     DEB(printk("in2000_reset called\n"));
 655     /*
 656      * Note: this is finished off by an incoming interrupt
 657      */
 658     outb(0,INFWRT);     /* read mode so WD can intrpt */
 659     outb(SCSIST,INSTAT);
 660     inb(INDATA);
 661     outb(OWNID,INSTAT);
 662     outb(0x7,INDATA);   /* ID=7,noadv, no parity, clk div=2 (8-10Mhz clk) */
 663     outb(COMMAND,INSTAT);
 664     outb(0,INDATA);     /* reset WD chip */
 665     delay(2);
 666 #ifdef SCSI_RESET_PENDING
 667     return SCSI_RESET_PENDING;
 668 #else
 669     if(SCpnt) SCpnt->flags |= NEEDS_JUMPSTART;
 670     return 0;
 671 #endif
 672 }
 673 
 674 int in2000_biosparam(Disk * disk, int dev, int* iinfo)
     /* [previous][next][first][last][top][bottom][index][help] */
 675         {
 676           int size = disk->capacity;
 677     DEB(printk("in2000_biosparam\n"));
 678     iinfo[0] = 64;
 679     iinfo[1] = 32;
 680     iinfo[2] = size >> 11;
 681     return 0;
 682     }

/* [previous][next][first][last][top][bottom][index][help] */