root/kernel/blk_drv/scsi/fdomain.c

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

DEFINITIONS

This source file includes following definitions.
  1. outw
  2. do_pause
  3. fdomain_make_bus_idle
  4. fdomain_is_valid_port
  5. fdomain_test_loopback
  6. fdomain_enable_interrupt
  7. fdomain_disable_interrupt
  8. fdomain_16x0_detect
  9. fdomain_16x0_info
  10. fdomain_arbitrate
  11. fdomain_select
  12. my_done
  13. fdomain_16x0_intr
  14. fdomain_16x0_queue
  15. fdomain_16x0_command
  16. fdomain_16x0_abort
  17. fdomain_16x0_reset

   1 /* fdomain.c -- Future Domain TMC-1660/TMC-1680 driver
   2  * Created: Sun May  3 18:53:19 1992
   3  * Revised: Tue Jul 28 19:45:25 1992 by root
   4  * Author: Rickard E. Faith, faith@cs.unc.edu
   5  * Copyright 1992 Rickard E. Faith
   6  *
   7  * $Log$
   8 
   9  * This program is free software; you can redistribute it and/or modify it
  10  * under the terms of the GNU General Public License as published by the
  11  * Free Software Foundation; either version 2, or (at your option) any
  12  * later version.
  13 
  14  * This program is distributed in the hope that it will be useful, but
  15  * WITHOUT ANY WARRANTY; without even the implied warranty of
  16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17  * General Public License for more details.
  18 
  19  * WARNING: THIS IS A BETA VERSION!
  20  *          USE AT YOUR OWN RISK!
  21  *          BACKUP YOUR SYSTEM BEFORE USING!
  22 
  23  * I would like to thank Maxtor, whose *free* 206 page manual on the LXT
  24  * drives was very helpful: "LXT SCSI Products: Specifications and OEM
  25  * Technical Manual (Revision B/September 1991)"
  26 
  27  * I wish that I could thank Future Domain for the necessary documentation,
  28  * but I can't.  I used the $25 "TMC-1800 SCSI Chip Specification" document
  29  * (FDC-1800T), which documents the *chip* and not the board.  Without it,
  30  * I would have been totally lost, but it would have been nice to have some
  31  * example source.  (The DOS BIOS source cost $250 and the UN*X driver
  32  * source was $750 [both required a non-disclosure agreement].  Ever wonder
  33  * why there are no freely available Future Domain drivers?)
  34 
  35  * Thanks to Todd Carrico (todd@wutc.wustl.edu), Dan Poirier
  36  * (poirier@cs.unc.edu ), Ken Corey (kenc@sol.acs.unt.edu), and C. de Bruin
  37  * (bruin@dutiba.tudelft.nl) for alpha testing.  Also thanks to Drew
  38  * Eckhardt (drew@cs.colorado.edu) for answering questions. */
  39 
  40 #include <linux/config.h>
  41 
  42 #ifdef CONFIG_SCSI_FUTURE_DOMAIN
  43 
  44 #include <linux/sched.h>
  45 #include <asm/io.h>
  46 #include "fdomain.h"
  47 #include "scsi.h"
  48 #include "hosts.h"
  49 #if QUEUE
  50 #include <asm/system.h>
  51 #include <linux/errno.h>
  52 #endif
  53 
  54 #define VERSION          "1.9"  /* Change with each revision */
  55 #define DEBUG            1      /* Enable debugging output */
  56 #define SEND_IDENTIFY    0      /* Send IDENTIFY message -- DOESN'T WORK! */
  57 #define USE_FIFO         1      /* Use the FIFO buffer for I/O */
  58 #define FAST_SYNCH       1      /* Enable Fast Synchronous */
  59 #define ALLOW_ALL_IRQ    0      /* Allow all IRQ's -- NOT RECOMMENDED */
  60 #define NEW_IRQ          1      /* Enable new IRQ handling */
  61 #define DECREASE_IL      1      /* Try to decrease interrupt latency */
  62 
  63 #if DEBUG
  64 #define EVERY_ACCESS     0      /* Write a line on every scsi access */
  65 #define ERRORS_ONLY      1      /* Only write a line if there is an error */
  66 #define DEBUG_DETECT     0      /* Debug fdomain_16x0_detect() */
  67 #else
  68 #define EVERY_ACCESS     0      /* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */
  69 #define ERRORS_ONLY      0
  70 #define DEBUG_DETECT     0
  71 #endif
  72 
  73 /* Errors are reported on the line, so we don't need to report them again */
  74 #if EVERY_ACCESS
  75 #undef ERRORS_ONLY
  76 #define ERRORS_ONLY      0
  77 #endif
  78 
  79 static int               port_base = 0;
  80 static void              *bios_base = NULL;
  81 static int               interrupt_level = 0;
  82 static volatile int      aborted = 0;
  83 
  84 static int               Data_Mode_Cntl_port;
  85 static int               FIFO_Data_Count_port;
  86 static int               Interrupt_Cntl_port;
  87 static int               Read_FIFO_port;
  88 static int               Read_SCSI_Data_port;
  89 static int               SCSI_Cntl_port;
  90 static int               SCSI_Status_port;
  91 static int               TMC_Cntl_port;
  92 static int               TMC_Status_port;
  93 static int               Write_FIFO_port;
  94 static int               Write_SCSI_Data_port;
  95 
  96 #if QUEUE
  97 static unsigned char     current_target = 0;
  98 static unsigned char     current_cmnd[10] = { 0, };
  99 static void              *current_buff = NULL;
 100 static int               current_bufflen = 0;
 101 static void              (*current_done)(int,int) = NULL;
 102 
 103 volatile static int      in_command = 0;
 104 volatile static int      current_phase;
 105 static int               this_host = 0;
 106 
 107 enum { in_arbitration, in_selection, in_other };
 108 
 109 #if NEW_IRQ
 110 extern void              fdomain_16x0_intr( int unused );
 111 #else
 112 extern void              fdomain_16x0_interrupt();
 113 #endif
 114 
 115 static const char        *cmd_pt;
 116 static const char        *the_command;
 117 static unsigned char     *out_buf_pt;
 118 static unsigned char     *in_buf_pt;
 119 volatile static int      Status;
 120 volatile static int      Message;
 121 volatile static unsigned data_sent;
 122 volatile static int      have_data_in;
 123 
 124 volatile static int      in_interrupt_code = 0;
 125 
 126 #endif
 127 
 128 
 129 enum in_port_type { Read_SCSI_Data = 0, SCSI_Status = 1, TMC_Status = 2,
 130                           LSB_ID_Code = 5, MSB_ID_Code = 6, Read_Loopback = 7,
 131                           SCSI_Data_NoACK = 8, Option_Select = 10,
 132                           Read_FIFO = 12, FIFO_Data_Count = 14 };
 133 
 134 enum out_port_type { Write_SCSI_Data = 0, SCSI_Cntl = 1, Interrupt_Cntl = 2,
 135                            Data_Mode_Cntl = 3, TMC_Cntl = 4, Write_Loopback = 7,
 136                            Write_FIFO = 12 };
 137 
 138 static void *addresses[] = {
 139    (void *)0xc8000,
 140    (void *)0xca000,
 141    (void *)0xce000,
 142    (void *)0xde000 };
 143 #define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned ))
 144                        
 145 static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
 146 #define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short ))
 147 
 148 static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
 149 
 150 /*
 151 
 152   READ THIS BEFORE YOU ADD A SIGNATURE!
 153 
 154   READING THIS SHORT NOTE CAN SAVE YOU LOTS OF TIME!
 155 
 156   READ EVERY WORD, ESPECIALLY THE WORD *NOT*
 157 
 158   This driver works *ONLY* for Future Domain cards using the
 159   TMC-1600 chip.  This includes models TMC-1660 and TMC-1680
 160   *ONLY*.
 161 
 162   The following is a BIOS signature for a TMC-950 board, which
 163   looks like it is a 16 bit board (based on card edge), but
 164   which only uses the extra lines for IRQ's (not for data):
 165 
 166   FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90
 167 
 168   THIS WILL *NOT* WORK WITH THIS DRIVER!
 169 
 170   Here is another BIOS signature for yet another Future
 171   Domain board WHICH WILL *NOT* WORK WITH THIS DRIVER:
 172 
 173   FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90
 174 
 175   Here is another BIOS signature for the TMC-88x series:
 176 
 177   FUTURE DOMAIN COPR. (C) 1986-1989 V6.0A7/28/90
 178 
 179   THIS WILL *NOT* WORK WITH THIS DRIVER, but it *WILL*
 180   work with the *SEAGATE* ST-01/ST-02 driver.
 181 
 182   */
 183 
 184 struct signature {
 185    char *signature;
 186    int  sig_offset;
 187    int  sig_length;
 188 } signatures[] = {
 189    { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.0 7/28/89", 5, 50 },
 190    { "FUTURE DOMAIN CORP. (C) 1986-1990 1800", 5, 37 },
 191    /* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGANTURE */
 192 };
 193 
 194 #define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature ))
 195 
 196 
 197 /* These functions are based on include/asm/io.h */
 198 
 199 #if 1
 200 static unsigned short inline inw( unsigned short port )
 201 {
 202    unsigned short _v;
 203    
 204    __asm__ volatile ("inw %1,%0"
 205                      :"=a" (_v):"d" ((unsigned short) port));
 206    return _v;
 207 }
 208 
 209 static void inline outw( unsigned short value, unsigned short port )
     /* [previous][next][first][last][top][bottom][index][help] */
 210 {
 211    __asm__ volatile ("outw %0,%1"
 212                      ::"a" ((unsigned short) value),
 213                      "d" ((unsigned short) port));
 214 }
 215 #else
 216 
 217 #define inw( port ) \
 218       ({ unsigned short _v; \
 219                __asm__ volatile ("inw %1,%0" \
 220                                 : "=a" (_v) : "d" ((unsigned short) port)); \
 221                                       _v; })
 222 
 223 #define outw( value ) \
 224       __asm__ volatile \
 225       ("outw %0,%1" : : "a" ((unsigned short) value), \
 226        "d" ((unsigned short) port))
 227 #endif
 228 
 229 
 230 /* These defines are copied from kernel/blk_drv/hd.c */
 231 
 232 #define insw( buf, count, port ) \
 233       __asm__ volatile \
 234       ( "cld;rep;insw"::"d" (port),"D" (buf),"c" (count):"cx","di" )
 235 
 236 #define outsw( buf, count, port) \
 237       __asm__ volatile \
 238       ("cld;rep;outsw"::"d" (port),"S" (buf),"c" (count):"cx","si")
 239 
 240 
 241 static void do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */
     /* [previous][next][first][last][top][bottom][index][help] */
 242 {
 243    unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */
 244 
 245    while (jiffies < the_time);
 246 }
 247 
 248 static void inline fdomain_make_bus_idle( void )
     /* [previous][next][first][last][top][bottom][index][help] */
 249 {
 250    outb( 0, SCSI_Cntl_port );
 251    outb( 0, Data_Mode_Cntl_port );
 252    outb( 1, TMC_Cntl_port );
 253 }
 254 
 255 static int fdomain_is_valid_port( int port )
     /* [previous][next][first][last][top][bottom][index][help] */
 256 {
 257    int options;
 258 
 259 #if DEBUG_DETECT 
 260    printk( " (%x%x),",
 261           inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) );
 262 #endif
 263 
 264    /* The MCA ID is a unique id for each MCA compatible board.  We
 265       are using ISA boards, but Future Domain provides the MCA ID
 266       anyway.  We can use this ID to ensure that this is a Future
 267       Domain TMC-1660/TMC-1680.
 268     */
 269 
 270    if (inb( port + LSB_ID_Code ) != 0xe9) { /* test for 0x6127 id */
 271       if (inb( port + LSB_ID_Code ) != 0x27) return 0;
 272       if (inb( port + MSB_ID_Code ) != 0x61) return 0;
 273    } else {                                 /* test for 0xe960 id */
 274       if (inb( port + MSB_ID_Code ) != 0x60) return 0;
 275    }
 276 
 277    /* We have a valid MCA ID for a TMC-1660/TMC-1680 Future Domain board.
 278       Now, check to be sure the bios_base matches these ports.
 279       If someone was unlucky enough to have purchased more than one
 280       Future Domain board, then they will have to modify this code, as
 281       we only detect one board here.  [The one with the lowest bios_base].
 282     */
 283 
 284    options = inb( port + Option_Select );
 285 
 286 #if DEBUG_DETECT
 287    printk( " Options = %x,", options );
 288 #endif
 289 
 290    if (addresses[ (options & 0xc0) >> 6 ] != bios_base) return 0;
 291    interrupt_level = ints[ (options & 0x0e) >> 1 ];
 292 
 293    return 1;
 294 }
 295 
 296 static int fdomain_test_loopback( void )
     /* [previous][next][first][last][top][bottom][index][help] */
 297 {
 298    int i;
 299    int result;
 300 
 301    for (i = 0; i < 255; i++) {
 302       outb( i, port_base + Write_Loopback );
 303       result = inb( port_base + Read_Loopback );
 304       if (i != result) return 1;
 305    }
 306    return 0;
 307 }
 308 
 309 #if !NEW_IRQ
 310 static void fdomain_enable_interrupt( void )
     /* [previous][next][first][last][top][bottom][index][help] */
 311 {
 312    if (!interrupt_level) return;
 313 
 314 #if ALLOW_ALL_IRQ
 315    if (interrupt_level < 8) {
 316       outb( inb_p( 0x21 ) & ~(1 << interrupt_level), 0x21 );
 317    } else
 318 #endif
 319          {
 320             outb( inb_p( 0xa1 ) & ~(1 << (interrupt_level - 8)), 0xa1 );
 321          }
 322 }
 323 
 324 static void fdomain_disable_interrupt( void )
     /* [previous][next][first][last][top][bottom][index][help] */
 325 {
 326    if (!interrupt_level) return;
 327 
 328 #if ALLOW_ALL_IRQ
 329    if (interrupt_level < 8) {
 330       outb( inb_p( 0x21 ) | (1 << interrupt_level), 0x21 );
 331    } else
 332 #endif
 333          {
 334             outb( inb_p( 0xa1 ) | (1 << (interrupt_level - 8)), 0xa1 );
 335          }
 336 }
 337 #endif
 338 
 339 int fdomain_16x0_detect( int hostnum )
     /* [previous][next][first][last][top][bottom][index][help] */
 340 {
 341    int           i, j;
 342    int           flag;
 343    unsigned char do_inquiry[] =       { 0x12, 0, 0, 0, 255, 0 };
 344    unsigned char do_request_sense[] = { 0x03, 0, 0, 0, 255, 0 };
 345    unsigned char do_read_capacity[] = { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 346    unsigned char buf[256];
 347    unsigned      retcode;
 348 
 349 #if DEBUG_DETECT
 350    printk( "SCSI: fdomain_16x0_detect()," );
 351 #endif
 352 
 353    for (i = 0; !bios_base && i < ADDRESS_COUNT; i++) {
 354 #if DEBUG_DETECT
 355       printk( " %x(%x),", (unsigned)addresses[i], (unsigned)bios_base );
 356 #endif
 357       for (j = 0; !bios_base && j < SIGNATURE_COUNT; j++) {
 358          if (!memcmp( ((char *)addresses[i] + signatures[j].sig_offset),
 359                     signatures[j].signature, signatures[j].sig_length )) {
 360             bios_base = addresses[i];
 361          }
 362       }
 363    }
 364 
 365    if (!bios_base) {
 366 #if DEBUG_DETECT
 367       printk( " FAILED: NO BIOS\n" );
 368 #endif
 369       return 0;
 370    }
 371 
 372    /* The TMC-1660/TMC-1680 has a RAM area just after the BIOS ROM.
 373       Assuming the ROM is enabled (otherwise we wouldn't have been
 374       able to read the ROM signature :-), then the ROM set up the
 375       RAM area with some magic numbers, such as a list of port
 376       base addresses and a list of the disk "geometry" reported to
 377       DOS (this geometry has nothing to do with physical geometry).
 378     */
 379 
 380    port_base = *((char *)bios_base + 0x1fcc)
 381          + (*((char *)bios_base + 0x1fcd) << 8);
 382    
 383 #if DEBUG_DETECT
 384    printk( " %x,", port_base );
 385 #endif
 386 
 387    for (flag = 0, i = 0; !flag && i < PORT_COUNT; i++) {
 388       if (port_base == ports[i]) ++flag;
 389    }
 390 
 391    if (flag) flag = fdomain_is_valid_port( port_base );
 392 
 393    if (!flag) {                 /* Cannot get port base from BIOS RAM */
 394       
 395       /* This is a bad sign.  It usually means that someone patched the
 396          BIOS signature list (the signatures variable) to contain a BIOS
 397          signature for a board *OTHER THAN* the TMC-1660/TMC-1680.
 398        */
 399       
 400 #if DEBUG_DETECT
 401       printk( " RAM FAILED, " );
 402 #endif
 403       /* Anyway, the alternative to finding the address in the RAM is
 404          to just search through every possible port address for one
 405          that is attached to the Future Domain card.  Don't panic,
 406          though, about reading all these random port addresses--there
 407          are rumors that the Future Domain BIOS does something very
 408          similar.
 409        */
 410 
 411       for (flag = 0, i = 0; !flag && i < PORT_COUNT; i++) {
 412          port_base = ports[i];
 413 #if DEBUG_DETECT
 414          printk( " %x,", port_base );
 415 #endif
 416          flag = fdomain_is_valid_port( port_base );
 417       }
 418    }
 419 
 420    if (!flag) {
 421 #if DEBUG_DETECT
 422       printk( " FAILED: NO PORT\n" );
 423 #endif
 424       return 0;         /* Cannot find valid set of ports */
 425    }
 426 
 427 #if DEBUG_DETECT
 428    printk( "\n" );
 429    printk( "SCSI: bios_base = %x, port_base = %x, interrupt_level = %d\n",
 430           (unsigned)bios_base, port_base, interrupt_level );
 431 #endif
 432 
 433    if (interrupt_level) {
 434       printk( "Future Domain BIOS at %x; port base at %x; using IRQ %d\n",
 435              (unsigned)bios_base, port_base, interrupt_level );
 436    } else {
 437       printk( "Future Domain BIOS at %x; port base at %x; *NO* IRQ\n",
 438              (unsigned)bios_base, port_base );
 439    }
 440    
 441    Data_Mode_Cntl_port  = port_base + Data_Mode_Cntl;
 442    FIFO_Data_Count_port = port_base + FIFO_Data_Count;
 443    Interrupt_Cntl_port  = port_base + Interrupt_Cntl;
 444    Read_FIFO_port       = port_base + Read_FIFO;
 445    Read_SCSI_Data_port  = port_base + Read_SCSI_Data;
 446    SCSI_Cntl_port       = port_base + SCSI_Cntl;
 447    SCSI_Status_port     = port_base + SCSI_Status;
 448    TMC_Cntl_port        = port_base + TMC_Cntl;
 449    TMC_Status_port      = port_base + TMC_Status;
 450    Write_FIFO_port      = port_base + Write_FIFO;
 451    Write_SCSI_Data_port = port_base + Write_SCSI_Data;
 452 
 453    fdomain_16x0_reset();
 454 
 455    if (fdomain_test_loopback()) {
 456 #if DEBUG_DETECT
 457       printk( "SCSI: LOOPBACK TEST FAILED, FAILING DETECT!\n" );
 458 #endif
 459       return 0;
 460    }
 461 
 462    /* These routines are here because of the way the SCSI bus behaves
 463       after a reset.  This appropriate behavior was not handled correctly
 464       by the higher level SCSI routines when I first wrote this driver.
 465     */
 466    
 467    printk( "Future Domain detection routine scanning for devices:\n" );
 468    for (i = 0; i < 8; i++) {
 469       if (i == 6) continue;     /* The host adapter is at SCSI ID 6 */
 470       retcode = fdomain_16x0_command( i, do_request_sense, buf, 255 );
 471       if (!retcode) {
 472          retcode = fdomain_16x0_command( i, do_inquiry, buf, 255 );
 473          if (!retcode) {
 474             printk( "     SCSI ID %d: ", i );
 475             for (j = 8; j < 32; j++) printk( "%c", buf[j] );
 476             retcode = fdomain_16x0_command( i, do_read_capacity, buf, 255 );
 477             if (!retcode) {
 478                unsigned long blocks, size, capacity;
 479                
 480                blocks = (buf[0] << 24) | (buf[1] << 16)
 481                      | (buf[2] << 8) | buf[3];
 482                size = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
 483                capacity = +(blocks * size * 10) / +(1024L * 1024L);
 484 
 485                printk( "%lu MB (%lu byte blocks)",
 486                       ((capacity + 5L) / 10L), size );
 487             }
 488             printk ("\n" );
 489          }
 490       }
 491    }
 492 
 493 #if QUEUE
 494 #if !ALLOW_ALL_IRQ
 495    if (interrupt_level < 8) {
 496       printk( "Future Domain: WILL NOT USE IRQ LESS THAN 8 FOR QUEUEING!\n" );
 497       scsi_hosts[hostnum].can_queue = 0;
 498    } else
 499 #endif
 500 #if NEW_IRQ
 501          {
 502             int              retcode;
 503             struct sigaction sa;
 504 
 505             this_host      = hostnum;
 506 
 507             sa.sa_handler  = fdomain_16x0_intr;
 508             sa.sa_flags    = SA_INTERRUPT;
 509             sa.sa_mask     = 0;
 510             sa.sa_restorer = NULL;
 511 
 512             retcode = irqaction( interrupt_level, &sa );
 513 
 514             if (retcode < 0) {
 515                if (retcode == -EINVAL) {
 516                   printk( "Future Domain: IRQ %d is bad!\n", interrupt_level );
 517                   printk( "       This shouldn't happen: REPORT TO RIK!\n" );
 518                } else if (retcode == -EBUSY) {
 519                   printk( "Future Domain: IRQ %d is already in use!\n",
 520                          interrupt_level );
 521                   printk( "       Please use another IRQ for the FD card!\n" );
 522                } else {
 523                   printk( "Future Domain: Error getting IRQ %d\n",
 524                          interrupt_level );
 525                   printk( "       This shouldn't happen: REPORT TO RIK!\n" );
 526                }
 527                printk( "       IRQs WILL NOT BE USED!\n" );
 528                
 529                scsi_hosts[this_host].can_queue = 0;
 530             } else {
 531                printk( "Future Domain: IRQ %d selected with retcode = %d\n",
 532                        interrupt_level, retcode );
 533             }
 534          }
 535 #else
 536          {
 537             this_host = hostnum;
 538             set_intr_gate( 0x20 + interrupt_level, &fdomain_16x0_interrupt );
 539             fdomain_enable_interrupt();
 540          }
 541 #endif
 542 #endif
 543 
 544    return 1;
 545 }
 546 
 547 char *fdomain_16x0_info(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 548 {
 549    static char buffer[] =
 550          "Future Domain TMC-1660/TMC-1680 SCSI driver version "
 551                VERSION
 552                      "\n";
 553    return buffer;
 554 }
 555 
 556 static int fdomain_arbitrate( void )
     /* [previous][next][first][last][top][bottom][index][help] */
 557 {
 558    int           status = 0;
 559    unsigned long timeout;
 560 
 561 #if VERBOSE
 562    printk( "SCSI: fdomain_arbitrate()\n" );
 563 #endif
 564    
 565    outb( 0x00, SCSI_Cntl_port );              /* Disable data drivers */
 566    outb( 0x40, port_base + SCSI_Data_NoACK ); /* Set our id bit */
 567    outb( 0x04, TMC_Cntl_port );               /* Start arbitration */
 568 
 569    timeout = jiffies + 50;                    /* 500 mS */
 570    while (jiffies < timeout) {
 571       status = inb( TMC_Status_port );        /* Read adapter status */
 572       if (status & 0x02) return 0;            /* Arbitration complete */
 573    }
 574 
 575    /* Make bus idle */
 576    fdomain_make_bus_idle();
 577 
 578 #if EVERY_ACCESS
 579    printk( "Arbitration failed, status = %x\n", status );
 580 #endif
 581 #if ERRORS_ONLY
 582    printk( "SCSI: Arbitration failed, status = %x", status );
 583 #endif
 584    return 1;
 585 }
 586 
 587 static int fdomain_select( int target )
     /* [previous][next][first][last][top][bottom][index][help] */
 588 {
 589    int           status;
 590    unsigned long timeout;
 591 
 592    outb( 0x80, SCSI_Cntl_port );        /* Bus Enable */
 593    outb( 0x8a, SCSI_Cntl_port );        /* Bus Enable + Attention + Select */
 594 
 595    /* Send our address OR'd with target address */
 596 #if SEND_IDENTIFY
 597    outb( 0x40 | (1 << target), port_base + SCSI_Data_NoACK );
 598 #else
 599    outb( (1 << target), port_base + SCSI_Data_NoACK );
 600 #endif
 601 
 602     /* Stop arbitration (also set FIFO for output and enable parity) */
 603    outb( 0xc8, TMC_Cntl_port ); 
 604 
 605    timeout = jiffies + 25;              /* 250mS */
 606    while (jiffies < timeout) {
 607       status = inb( SCSI_Status_port ); /* Read adapter status */
 608       if (status & 1) {                 /* Busy asserted */
 609          /* Enable SCSI Bus (on error, should make bus idle with 0) */
 610 #if SEND_IDENTIFY
 611          /* Also, set ATN so that the drive will make a MESSAGE OUT phase */
 612          outb( 0x88, SCSI_Cntl_port );
 613 #else
 614          outb( 0x80, SCSI_Cntl_port );
 615 #endif
 616          return 0;
 617       }
 618    }
 619    /* Make bus idle */
 620    fdomain_make_bus_idle();
 621 #if EVERY_ACCESS
 622    if (!target) printk( "Select failed\n" );
 623 #endif
 624 #if ERRORS_ONLY
 625    if (!target) printk( "SCSI: Select failed" );
 626 #endif
 627    return 1;
 628 }
 629 
 630 #if QUEUE
 631 
 632 #if !USE_FIFO
 633 #pragma error QUEUE requires USE_FIFO
 634 #endif
 635 
 636 void my_done( int error )
     /* [previous][next][first][last][top][bottom][index][help] */
 637 {
 638    if (in_command) {
 639       in_command = 0;
 640       in_interrupt_code = 0;
 641       outb( 0x00, Interrupt_Cntl_port );
 642       fdomain_make_bus_idle();
 643       if (current_done) current_done( this_host, error );
 644       else panic( "SCSI (Future Domain): current_done() == NULL" );
 645    } else {
 646       panic( "SCSI (Future Domain): my_done() called outside of command\n" );
 647    }
 648 }
 649 
 650 #if NEW_IRQ
 651 void fdomain_16x0_intr( int unused )
     /* [previous][next][first][last][top][bottom][index][help] */
 652 #else
 653 void fdomain_16x0_intr( void )
 654 #endif
 655 {
 656    int      status;
 657    int      done = 0;
 658    unsigned data_count;
 659 
 660 #if NEW_IRQ
 661    sti();
 662 #endif
 663 
 664    if (in_interrupt_code)
 665          panic( "SCSI (Future Domain): fdomain_16x0_intr() NOT REENTRANT!\n" );
 666    else
 667          ++in_interrupt_code;
 668    
 669    outb( 0x00, Interrupt_Cntl_port );
 670 
 671 #if EVERY_ACCESS
 672    printk( "aborted = %d, ", aborted );
 673 #endif
 674 
 675    if (aborted) {
 676       /* Force retry for timeouts after selection complete */
 677       if (current_phase == in_other)
 678             my_done( DID_BUS_BUSY << 16 );
 679       else
 680             my_done( aborted << 16 );
 681 #if NEW_IRQ && !DECREASE_IL
 682       cli();
 683 #endif
 684       return;
 685    }
 686 
 687    /* We usually have one spurious interrupt after each command.  Ignore it. */
 688    if (!in_command) {           /* Spurious interrupt */
 689       in_interrupt_code = 0;
 690 #if NEW_IRQ && !DECREASE_IL
 691       cli();
 692 #endif
 693       return;
 694    }
 695    
 696    if (current_phase == in_arbitration) {
 697       status = inb( TMC_Status_port );        /* Read adapter status */
 698       if (!(status & 0x02)) {
 699 #if EVERY_ACCESS
 700          printk( " AFAIL " );
 701 #endif
 702          my_done( DID_TIME_OUT << 16 );
 703 #if NEW_IRQ && !DECREASE_IL
 704          cli();
 705 #endif
 706          return;
 707       }
 708       current_phase = in_selection;
 709 
 710       outb( 0x80, SCSI_Cntl_port );     /* Bus Enable */
 711       outb( 0x8a, SCSI_Cntl_port );     /* Bus Enable + Attention + Select */
 712       
 713       outb( (1 << current_target), port_base + SCSI_Data_NoACK );
 714 
 715       outb( 0x40, Interrupt_Cntl_port );
 716       /* Stop arbitration (also set FIFO for output and enable parity) */
 717       in_interrupt_code = 0;
 718       outb( 0xd8, TMC_Cntl_port );
 719 #if NEW_IRQ && !DECREASE_IL
 720       cli();
 721 #endif
 722       return;
 723    } else if (current_phase == in_selection) {
 724       status = inb( SCSI_Status_port );
 725       if (!(status & 0x01)) {
 726 #if EVERY_ACCESS
 727          printk( " SFAIL " );
 728 #endif
 729          my_done( DID_NO_CONNECT << 16 );
 730 #if NEW_IRQ && !DECREASE_IL
 731          cli();
 732 #endif
 733          return;
 734       }
 735       current_phase = in_other;
 736 #if FAST_SYNCH
 737       outb( 0xc0, Data_Mode_Cntl_port );
 738 #endif
 739       in_interrupt_code = 0;
 740       outb( 0x90, Interrupt_Cntl_port );
 741       outb( 0x80, SCSI_Cntl_port );
 742 #if NEW_IRQ && !DECREASE_IL
 743       cli();
 744 #endif
 745       return;
 746    }
 747 
 748    /* current_phase == in_other: this is the body of the routine */
 749 
 750    switch ((unsigned char)*the_command) {
 751    case 0x04: case 0x07: case 0x0a: case 0x15: case 0x2a:
 752    case 0x2e: case 0x3b: case 0xea: case 0x3f:
 753       data_count = 0x2000 - inw( FIFO_Data_Count_port );
 754       if (current_bufflen - data_sent < data_count)
 755             data_count = current_bufflen - data_sent;
 756       if (data_count > 0) {
 757 /*       if (data_count > 512) data_count = 512; */
 758 #if EVERY_ACCESS
 759          printk( "%d OUT, ", data_count );
 760 #endif
 761          if (data_count == 1) {
 762             outb( *out_buf_pt++, Write_FIFO_port );
 763             ++data_sent;
 764          } else {
 765             data_count >>= 1;
 766             outsw( out_buf_pt, data_count, Write_FIFO_port );
 767             out_buf_pt += 2 * data_count;
 768             data_sent += 2 * data_count;
 769          }
 770       }
 771       break;
 772    default:
 773       if (!have_data_in) {
 774          outb( 0x98, TMC_Cntl_port );
 775          ++have_data_in;
 776       } else {
 777          data_count = inw( FIFO_Data_Count_port );
 778 /*       if (data_count > 512) data_count = 512; */
 779          if (data_count) {
 780 #if EVERY_ACCESS
 781             printk( "%d IN, ", data_count );
 782 #endif
 783             if (data_count == 1) {
 784                *in_buf_pt++ = inb( Read_FIFO_port );
 785             } else {
 786                data_count >>= 1; /* Number of words */
 787                insw( in_buf_pt, data_count, Read_FIFO_port );
 788                in_buf_pt += 2 * data_count;
 789             }
 790          }
 791       }
 792       break;
 793    }
 794 
 795    status = inb( SCSI_Status_port );
 796 
 797    if (status & 0x10) { /* REQ */
 798       
 799       switch (status & 0x0e) {
 800       case 0x08:                /* COMMAND OUT */
 801          outb( *cmd_pt++, Write_SCSI_Data_port );
 802 #if EVERY_ACCESS
 803          printk( "CMD = %x,", (unsigned char)cmd_pt[-1] );
 804 #endif
 805          break;
 806       case 0x0c:                /* STATUS IN */
 807          Status = inb( Read_SCSI_Data_port );
 808 #if EVERY_ACCESS
 809          printk( "Status = %x, ", Status );
 810 #endif
 811 #if ERRORS_ONLY
 812          if (Status) {
 813             printk( "SCSI: target = %d, command = %x, Status = %x\n",
 814                    current_target, (unsigned char)*the_command, Status );
 815          }
 816 #endif
 817          break;
 818       case 0x0a:                /* MESSAGE OUT */
 819 #if SEND_IDENTIFY
 820          /* On the first request, send an Identify message */
 821          if (!sent_identify) {
 822             outb( 0x80, SCSI_Cntl_port );          /* Lower ATN */
 823             outb( 0x80, Write_SCSI_Data_port );    /* Identify */
 824             ++sent_identify;
 825          } else
 826 #else
 827                outb( 0x07, Write_SCSI_Data_port ); /* Reject */
 828 #endif
 829          break;
 830       case 0x0e:                /* MESSAGE IN */
 831          Message = inb( Read_SCSI_Data_port );
 832 #if EVERY_ACCESS
 833          printk( "Message = %x, ", Message );
 834 #endif
 835          if (!Message) ++done;
 836          break;
 837       }
 838    }
 839 
 840    if (done) {
 841 #if EVERY_ACCESS
 842       printk( " ** IN DONE ** " );
 843 #endif
 844 
 845       if (have_data_in) {
 846          while (data_count = inw( FIFO_Data_Count_port )) {
 847             if (data_count == 1) {
 848                *in_buf_pt++ = inb( Read_FIFO_port );
 849             } else {
 850                data_count >>= 1; /* Number of words */
 851                insw( in_buf_pt, data_count, Read_FIFO_port );
 852                in_buf_pt += 2 * data_count;
 853             }
 854          }
 855       }
 856 #if EVERY_ACCESS
 857       printk( "AFTER DATA GET\n" );
 858 #endif
 859       
 860 #if ERRORS_ONLY
 861       if (*the_command == REQUEST_SENSE && !Status) {
 862          if ((unsigned char)(*((char *)current_buff + 2)) & 0x0f) {
 863             printk( "SCSI REQUEST SENSE: Sense Key = %x, Sense Code = %x\n",
 864                    (unsigned char)(*((char *)current_buff + 2)) & 0x0f,
 865                    (unsigned char)(*((char *)current_buff + 12)) );
 866          }
 867       }
 868 #endif
 869 #if EVERY_ACCESS
 870       printk( "BEFORE MY_DONE\n" );
 871 #endif
 872       my_done( (Status & 0xff) | ((Message & 0xff) << 8) | (DID_OK << 16) );
 873    } else {
 874       in_interrupt_code = 0;
 875       outb( 0x90, Interrupt_Cntl_port );
 876    }
 877 
 878 #if NEW_IRQ && !DECREASE_IL
 879    cli();
 880 #endif
 881    return;
 882 }
 883 
 884 int fdomain_16x0_queue( unsigned char target, const void *cmnd,
     /* [previous][next][first][last][top][bottom][index][help] */
 885                          void *buff, int bufflen, void (*done)(int,int) )
 886 {
 887    if (in_command) {
 888       panic( "SCSI (Future Domain): fdomain_16x0_queue() NOT REENTRANT!\n" );
 889    }
 890 #if EVERY_ACCESS
 891    printk( "queue %d %x\n", target, *(unsigned char *)cmnd );
 892 #endif
 893 
 894    fdomain_make_bus_idle();
 895 
 896    aborted         = 0;
 897    current_target  = target;
 898    memcpy( current_cmnd, cmnd, ((*(unsigned char *)cmnd) <= 0x1f ? 6 : 10 ) );
 899    current_buff    = buff;
 900    current_bufflen = bufflen;
 901    current_done    = done;
 902 
 903    /* Initialize static data */
 904    cmd_pt          = current_cmnd;
 905    the_command     = current_cmnd;
 906    out_buf_pt      = current_buff;
 907    in_buf_pt       = current_buff;
 908    
 909    Status          = 0;
 910    Message         = 0;
 911    data_sent       = 0;
 912    have_data_in    = 0;
 913 
 914    /* Start arbitration */
 915    current_phase = in_arbitration;
 916    outb( 0x00, Interrupt_Cntl_port );
 917    outb( 0x00, SCSI_Cntl_port );              /* Disable data drivers */
 918    outb( 0x40, port_base + SCSI_Data_NoACK ); /* Set our id bit */
 919    ++in_command;
 920    outb( 0x20, Interrupt_Cntl_port );
 921    outb( 0x1c, TMC_Cntl_port );               /* Start arbitration */
 922 
 923    return 0;
 924 }
 925 #endif
 926 
 927 int fdomain_16x0_command( unsigned char target, const void *cmnd,
     /* [previous][next][first][last][top][bottom][index][help] */
 928                          void *buff, int bufflen )
 929 {
 930    const char     *cmd_pt = cmnd;
 931    const char     *the_command = cmnd;
 932    unsigned char  *out_buf_pt = buff;
 933    unsigned char  *in_buf_pt = buff;
 934    int            Status = 0;
 935    int            Message = 0;
 936    int            status;
 937    int            done = 0;
 938    unsigned long  timeout;
 939    unsigned       data_sent = 0;
 940    unsigned       data_count;
 941 #if USE_FIFO
 942    int            have_data_in = 0;
 943 #endif
 944 #if SEND_IDENTITY
 945    int            sent_identify = 0;
 946 #endif
 947 
 948 #if EVERY_ACCESS
 949    printk( "fdomain_command(%d, %x): ", target, (unsigned char)*the_command );
 950 #endif
 951 
 952    if (fdomain_arbitrate()) {
 953 #if ERRORS_ONLY
 954       printk( ", target = %d, command = %x\n",
 955              target, (unsigned char)*the_command );
 956 #endif
 957       return DID_TIME_OUT << 16;
 958    }
 959 
 960    if (fdomain_select( target )) {
 961 #if ERRORS_ONLY
 962       if (!target) printk( ", target = %d, command = %x\n",
 963                          target, (unsigned char)*the_command );
 964 #endif
 965       return DID_NO_CONNECT << 16;
 966    }
 967 
 968    timeout = jiffies + 500;     /* 5000 mS -- For Maxtor after a RST */
 969    aborted = 0;                 /* How is this supposed to get reset??? */
 970 
 971 #if FAST_SYNCH
 972    outb( 0xc0, Data_Mode_Cntl_port );
 973 #endif
 974 
 975 #if USE_FIFO
 976    switch ((unsigned char)*the_command) {
 977    case 0x04: case 0x07: case 0x0a: case 0x15: case 0x2a:
 978    case 0x2e: case 0x3b: case 0xea: case 0x3f:
 979       data_count = 0x2000 - inw( FIFO_Data_Count_port );
 980       if (bufflen - data_sent < data_count)
 981             data_count = bufflen - data_sent;
 982       if (data_count == 1) {
 983          outb( *out_buf_pt++, Write_FIFO_port );
 984          ++data_sent;
 985       } else {
 986          data_count >>= 1;
 987          outsw( out_buf_pt, data_count, Write_FIFO_port );
 988          out_buf_pt += 2 * data_count;
 989          data_sent += 2 * data_count;
 990       }
 991       break;
 992    default:
 993       outb( 0x88, TMC_Cntl_port );
 994       ++have_data_in;
 995       break;
 996    }
 997 #endif
 998    
 999    while (((status = inb( SCSI_Status_port )) & 1)
1000           && !done && !aborted && jiffies < timeout) {
1001       
1002       if (status & 0x10) {      /* REQ */
1003 
1004          switch (status & 0x0e) {
1005          case 0x00:             /* DATA OUT */
1006 #if USE_FIFO
1007             data_count = 0x2000 - inw( FIFO_Data_Count_port );
1008             if (bufflen - data_sent < data_count)
1009                   data_count = bufflen - data_sent;
1010             if (data_count == 1) {
1011                outb( *out_buf_pt++, Write_FIFO_port );
1012                ++data_sent;
1013             } else {
1014                data_count >>= 1;
1015                outsw( out_buf_pt, data_count, Write_FIFO_port );
1016                out_buf_pt += 2 * data_count;
1017                data_sent += 2 * data_count;
1018             }
1019 #else
1020             outb( *out_buf_pt++, Write_SCSI_Data_port );
1021 #endif
1022             break;
1023          case 0x04:             /* DATA IN */
1024 #if USE_FIFO
1025             if (!have_data_in) {
1026                outb( 0x88, TMC_Cntl_port );
1027                ++have_data_in;
1028             }
1029             data_count = inw( FIFO_Data_Count_port );
1030             if (data_count == 1) {
1031                *in_buf_pt++ = inb( Read_FIFO_port );
1032             } else {
1033                data_count >>= 1; /* Number of words */
1034                insw( in_buf_pt, data_count, Read_FIFO_port );
1035                in_buf_pt += 2 * data_count;
1036             }
1037 #else
1038             *in_buf_pt++ = inb( Read_SCSI_Data_port );
1039 #endif
1040             break;
1041          case 0x08:             /* COMMAND OUT */
1042             outb( *cmd_pt++, Write_SCSI_Data_port );
1043 #if EVERY_ACCESS
1044             printk( "%x,", (unsigned char)cmd_pt[-1] );
1045 #endif
1046             break;
1047          case 0x0c:             /* STATUS IN */
1048             Status = inb( Read_SCSI_Data_port );
1049 #if EVERY_ACCESS
1050             printk( "Status = %x, ", Status );
1051 #endif
1052 #if ERRORS_ONLY
1053             if (Status) {
1054                printk( "SCSI: target = %d, command = %x, Status = %x\n",
1055                       target, (unsigned char)*the_command, Status );
1056             }
1057 #endif
1058             break;
1059          case 0x0a:             /* MESSAGE OUT */
1060 #if SEND_IDENTIFY
1061             /* On the first request, send an Identify message */
1062             if (!sent_identify) {
1063                outb( 0x80, SCSI_Cntl_port );          /* Lower ATN */
1064                outb( 0x80, Write_SCSI_Data_port );    /* Identify */
1065                ++sent_identify;
1066             } else
1067 #else
1068                   outb( 0x07, Write_SCSI_Data_port ); /* Reject */
1069 #endif
1070             break;
1071          case 0x0e:             /* MESSAGE IN */
1072             Message = inb( Read_SCSI_Data_port );
1073 #if EVERY_ACCESS
1074             printk( "Message = %x, ", Message );
1075 #endif
1076             if (!Message) ++done;
1077             break;
1078          }
1079       }
1080    }
1081 
1082    if (jiffies >= timeout) {
1083 #if EVERY_ACCESS
1084       printk( "Time out, status = %x\n", status );
1085 #endif
1086 #if ERRORS_ONLY
1087       printk( "SCSI: Time out, status = %x (target = %d, command = %x)\n",
1088              status, target, (unsigned char)*the_command );
1089 #endif
1090       fdomain_make_bus_idle();
1091       return DID_BUS_BUSY << 16;
1092    }
1093 
1094    if (aborted) {
1095 #if EVERY_ACCESS
1096       printk( "Aborted\n" );
1097 #endif
1098 #if ONLY_ERRORS
1099       printk( "SCSI: Aborted (command = %x)\n", (unsigned char)*the_command );
1100 #endif
1101       fdomain_16x0_reset();
1102       return DID_ABORT << 16;
1103    }
1104    
1105 #if USE_FIFO
1106    if (have_data_in) {
1107       while (data_count = inw( FIFO_Data_Count_port )) {
1108          if (data_count == 1) {
1109             *in_buf_pt++ = inb( Read_FIFO_port );
1110          } else {
1111             data_count >>= 1; /* Number of words */
1112             insw( in_buf_pt, data_count, Read_FIFO_port );
1113             in_buf_pt += 2 * data_count;
1114          }
1115       }
1116    }
1117 #endif
1118 
1119    fdomain_make_bus_idle();
1120 
1121 #if EVERY_ACCESS
1122    printk( "Retcode = %x\n",
1123           (Status & 0xff) | ((Message & 0xff) << 8) | (DID_OK << 16) );
1124 #endif
1125 #if ERRORS_ONLY
1126    if (*the_command == REQUEST_SENSE && !Status) {
1127       if ((unsigned char)(*((char *)buff + 2)) & 0x0f) {
1128          printk( "SCSI REQUEST SENSE: Sense Key = %x, Sense Code = %x\n",
1129                 (unsigned char)(*((char *)buff + 2)) & 0x0f,
1130                 (unsigned char)(*((char *)buff + 12)) );
1131       }
1132    }
1133 #endif
1134 
1135    return (Status & 0xff) | ((Message & 0xff) << 8) | (DID_OK << 16);
1136 }
1137 
1138 int fdomain_16x0_abort( int code )
     /* [previous][next][first][last][top][bottom][index][help] */
1139 {
1140 
1141 #if EVERY_ACCESS
1142    printk( " ABORT " );
1143 #endif
1144 
1145 #if QUEUE
1146    cli();
1147    if (!in_command) {
1148       sti();
1149       return 0;
1150    }
1151 
1152    aborted = code ? code : DID_ABORT;
1153 
1154    sti();
1155    fdomain_make_bus_idle();
1156 #else
1157    aborted = code ? code : DID_ABORT;
1158 #endif
1159 
1160    return 0;
1161 }
1162 
1163 int fdomain_16x0_reset( void )
     /* [previous][next][first][last][top][bottom][index][help] */
1164 {
1165    outb( 1, SCSI_Cntl_port );
1166    do_pause( 2 );
1167    outb( 0, SCSI_Cntl_port );
1168    do_pause( 115 );
1169    outb( 0, Data_Mode_Cntl_port );
1170    outb( 0, TMC_Cntl_port );
1171 
1172    aborted = DID_RESET;
1173 
1174    return 0;
1175 }
1176 
1177 #if QUEUE && !NEW_IRQ
1178 
1179 /* This is copied from kernel/sys_calls.s
1180    and from kernel/blk_drv/scsi/aha1542.c */
1181 
1182 __asm__("
1183 _fdomain_16x0_interrupt:
1184         cld
1185         push %gs
1186         push %fs
1187         push %es
1188         push %ds
1189         pushl %eax
1190         pushl %ebp
1191         pushl %edi
1192         pushl %esi
1193         pushl %edx
1194         pushl %ecx
1195         pushl %ebx
1196         movl $0x10,%edx
1197         mov %dx,%ds
1198         mov %dx,%es
1199         movl $0x17,%edx
1200         mov %dx,%fs
1201 
1202         movl $_fdomain_disable_interrupt,%edx
1203         call *%edx
1204 
1205         movb $0x20,%al
1206         outb %al,$0xA0          # EOI to interrupt controller #1
1207         jmp 1f                  # give port chance to breathe
1208 1:      jmp 1f
1209 1:      outb %al,$0x20
1210 
1211         sti
1212         movl $_fdomain_16x0_intr,%edx
1213         call *%edx              # ``interesting'' way of handling intr.
1214         cli
1215 
1216         movl $_fdomain_enable_interrupt,%edx
1217         call *%edx
1218 
1219         popl %ebx
1220         popl %ecx
1221         popl %edx
1222         popl %esi
1223         popl %edi
1224         popl %ebp
1225         popl %eax
1226         pop %ds
1227         pop %es
1228         pop %fs
1229         pop %gs
1230         iret
1231 ");
1232 #endif
1233 
1234 #endif

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