root/kernel/blk_drv/scsi/ultrastor.c

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

DEFINITIONS

This source file includes following definitions.
  1. ultrastor_14f_detect
  2. ultrastor_14f_info
  3. ultrastor_14f_queuecommand
  4. ultrastor_14f_command
  5. ultrastor_14f_abort
  6. ultrastor_14f_reset
  7. ultrastor_interrupt_service

   1 /*
   2  *      ultrastor.c     Copyright (C) 1991, 1992 David B. Gentzel
   3  *      Low-level SCSI driver for UltraStor 14F
   4  *      by David B. Gentzel, Whitfield Software Services, Carnegie, PA
   5  *          (gentzel@nova.enet.dec.com)
   6  *      Thanks to UltraStor for providing the necessary documentation
   7  */
   8 
   9 /*
  10  * NOTES:
  11  *    The UltraStor 14F is an intelligent, high performance ISA SCSI-2 host
  12  *    adapter.  It is essentially an ISA version of the UltraStor 24F EISA
  13  *    adapter.  It supports first-party DMA, command queueing, and
  14  *    scatter/gather I/O.  It can also emulate the standard AT MFM/RLL/IDE
  15  *    interface for use with OS's which don't support SCSI.
  16  *
  17  *    This driver may also work (with some small changes) with the UltraStor
  18  *    24F.  I have no way of confirming this...
  19  *
  20  *    Places flagged with a triple question-mark are things which are either
  21  *    unfinished, questionable, or wrong.
  22  */
  23 
  24 /*
  25  * CAVEATS: ???
  26  *    This driver is VERY stupid.  It takes no advantage of much of the power
  27  *    of the UltraStor controller.  We just sit-and-spin while waiting for
  28  *    commands to complete.  I hope to go back and beat it into shape, but
  29  *    PLEASE, anyone else who would like to, please make improvements!
  30  *
  31  *    By defining NO_QUEUEING in ultrastor.h, you disable the queueing feature
  32  *    of the mid-level SCSI driver.  Once I'm satisfied that the queueing
  33  *    version is as stable as the non-queueing version, I'll eliminate this
  34  *    option.
  35  */
  36 
  37 #include <linux/config.h>
  38 
  39 #ifdef CONFIG_SCSI_ULTRASTOR
  40 
  41 #include <linux/stddef.h>
  42 #include <linux/string.h>
  43 #include <linux/sched.h>
  44 #include <linux/kernel.h>
  45 
  46 #include <asm/io.h>
  47 #include <asm/system.h>
  48 
  49 #define ULTRASTOR_PRIVATE       /* Get the private stuff from ultrastor.h */
  50 #include "ultrastor.h"
  51 #include "scsi.h"
  52 #include "hosts.h"
  53 
  54 #define VERSION "1.0 beta"
  55 
  56 #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
  57 #define BIT(n) (1ul << (n))
  58 #define BYTE(num, n) ((unsigned char)((unsigned int)(num) >> ((n) * 8)))
  59 
  60 /* Simply using "unsigned long" in these structures won't work as it causes
  61    alignment.  Perhaps the "aligned" attribute may be used in GCC 2.0 to get
  62    around this, but for now I use this hack. */
  63 typedef struct {
  64     unsigned char bytes[4];
  65 } Longword;
  66 
  67 /* Used to fetch the configuration info from the config i/o registers.  We
  68    then store (in a friendlier format) in config. */
  69 struct config_1 {
  70     unsigned char bios_segment: 3;
  71     unsigned char reserved: 1;
  72     unsigned char interrupt: 2;
  73     unsigned char dma_channel: 2;
  74 };
  75 struct config_2 {
  76     unsigned char ha_scsi_id: 3;
  77     unsigned char mapping_mode: 2;
  78     unsigned char bios_drive_number: 1;
  79     unsigned char tfr_port: 2;
  80 };
  81 
  82 /* Used to store configuration info read from config i/o registers.  Most of
  83    this is not used yet, but might as well save it. */
  84 struct config {
  85     unsigned short port_address;
  86     const void *bios_segment;
  87     unsigned char interrupt: 4;
  88     unsigned char dma_channel: 3;
  89     unsigned char ha_scsi_id: 3;
  90     unsigned char heads: 6;
  91     unsigned char sectors: 6;
  92     unsigned char bios_drive_number: 1;
  93 };
  94 
  95 /* MailBox SCSI Command Packet.  Basic command structure for communicating
  96    with controller. */
  97 struct mscp {
  98     unsigned char opcode: 3;            /* type of command */
  99     unsigned char xdir: 2;              /* data transfer direction */
 100     unsigned char dcn: 1;               /* disable disconnect */
 101     unsigned char ca: 1;                /* use cache (if available) */
 102     unsigned char sg: 1;                /* scatter/gather operation */
 103     unsigned char target_id: 3;         /* target SCSI id */
 104     unsigned char ch_no: 2;             /* SCSI channel (always 0 for 14f) */
 105     unsigned char lun: 3;               /* logical unit number */
 106     Longword transfer_data;             /* transfer data pointer */
 107     Longword transfer_data_length;      /* length in bytes */
 108     Longword command_link;              /* for linking command chains */
 109     unsigned char scsi_command_link_id; /* identifies command in chain */
 110     unsigned char number_of_sg_list;    /* (if sg is set) 8 bytes per list */
 111     unsigned char length_of_sense_byte;
 112     unsigned char length_of_scsi_cdbs;  /* 6, 10, or 12 */
 113     unsigned char scsi_cdbs[12];        /* SCSI commands */
 114     unsigned char adapter_status;       /* non-zero indicates HA error */
 115     unsigned char target_status;        /* non-zero indicates target error */
 116     Longword sense_data;
 117 };
 118 
 119 /* Allowed BIOS base addresses for 14f (NULL indicates reserved) */
 120 static const void *const bios_segment_table[8] = {
 121     NULL,            (void *)0xC4000, (void *)0xC8000, (void *)0xCC000,
 122     (void *)0xD0000, (void *)0xD4000, (void *)0xD8000, (void *)0xDC000,
 123 };
 124 
 125 /* Allowed IRQs for 14f */
 126 static const unsigned char interrupt_table[4] = { 15, 14, 11, 10 };
 127 
 128 /* Allowed DMA channels for 14f (0 indicates reserved) */
 129 static const unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
 130 
 131 /* Head/sector mappings allowed by 14f */
 132 static const struct {
 133     unsigned char heads;
 134     unsigned char sectors;
 135 } mapping_table[4] = { { 16, 63 }, { 64, 32 }, { 64, 63 }, { 0, 0 } };
 136 
 137 /* Config info */
 138 static struct config config;
 139 
 140 /* Our index in the host adapter array maintained by higher-level driver */
 141 static int host_number;
 142 
 143 /* PORT_ADDRESS is first port address used for i/o of messages. */
 144 #ifdef PORT_OVERRIDE
 145 # define PORT_ADDRESS PORT_OVERRIDE
 146 #else
 147 # define PORT_ADDRESS (config.port_address)
 148 #endif
 149 
 150 static volatile int aborted = 0;
 151 
 152 #ifndef PORT_OVERRIDE
 153 static const unsigned short ultrastor_ports[] = {
 154     0x330, 0x340, 0x310, 0x230, 0x240, 0x210, 0x130, 0x140,
 155 };
 156 #endif
 157 
 158 void ultrastor_interrupt(void);
 159 
 160 static void (*ultrastor_done)(int, int) = 0;
 161 
 162 static const struct {
 163     const char *signature;
 164     size_t offset;
 165     size_t length;
 166 } signatures[] = {
 167     { "SBIOS 1.01 COPYRIGHT (C) UltraStor Corporation,1990-1992.", 0x10, 57 },
 168 };
 169 
 170 int ultrastor_14f_detect(int hostnum)
     /* [previous][next][first][last][top][bottom][index][help] */
 171 {
 172     size_t i;
 173     unsigned char in_byte;
 174     struct config_1 config_1;
 175     struct config_2 config_2;
 176 
 177 #if (ULTRASTOR_DEBUG & UD_DETECT)
 178     printk("US14F: detect: called\n");
 179 #endif
 180 
 181 #ifndef PORT_OVERRIDE
 182     PORT_ADDRESS = 0;
 183     for (i = 0; i < ARRAY_SIZE(ultrastor_ports); i++) {
 184         PORT_ADDRESS = ultrastor_ports[i];
 185 #endif
 186 
 187 #if (ULTRASTOR_DEBUG & UD_DETECT)
 188         printk("US14F: detect: testing port address %03X\n", PORT_ADDRESS);
 189 #endif
 190 
 191         in_byte = inb(PRODUCT_ID(PORT_ADDRESS + 0));
 192         if (in_byte != US14F_PRODUCT_ID_0) {
 193 #if (ULTRASTOR_DEBUG & UD_DETECT)
 194 # ifdef PORT_OVERRIDE
 195             printk("US14F: detect: wrong product ID 0 - %02X\n", in_byte);
 196 # else
 197             printk("US14F: detect: no adapter at port %03X\n", PORT_ADDRESS);
 198 # endif
 199 #endif
 200 #ifdef PORT_OVERRIDE
 201             return FALSE;
 202 #else
 203             continue;
 204 #endif
 205         }
 206         in_byte = inb(PRODUCT_ID(PORT_ADDRESS + 1));
 207         /* Only upper nibble is defined for Product ID 1 */
 208         if ((in_byte & 0xF0) != US14F_PRODUCT_ID_1) {
 209 #if (ULTRASTOR_DEBUG & UD_DETECT)
 210 # ifdef PORT_OVERRIDE
 211             printk("US14F: detect: wrong product ID 1 - %02X\n", in_byte);
 212 # else
 213             printk("US14F: detect: no adapter at port %03X\n", PORT_ADDRESS);
 214 # endif
 215 #endif
 216 #ifdef PORT_OVERRIDE
 217             return FALSE;
 218 #else
 219             continue;
 220 #endif
 221         }
 222 #ifndef PORT_OVERRIDE
 223         break;
 224     }
 225     if (i == ARRAY_SIZE(ultrastor_ports)) {
 226 # if (ULTRASTOR_DEBUG & UD_DETECT)
 227         printk("US14F: detect: no port address found!\n");
 228 # endif
 229         return FALSE;
 230     }
 231 #endif
 232 
 233 #if (ULTRASTOR_DEBUG & UD_DETECT)
 234     printk("US14F: detect: adapter found at port address %03X\n",
 235            PORT_ADDRESS);
 236 #endif
 237 
 238     /* All above tests passed, must be the right thing.  Get some useful
 239        info. */
 240     *(char *)&config_1 = inb(CONFIG(PORT_ADDRESS + 0));
 241     *(char *)&config_2 = inb(CONFIG(PORT_ADDRESS + 1));
 242     config.bios_segment = bios_segment_table[config_1.bios_segment];
 243     config.interrupt = interrupt_table[config_1.interrupt];
 244     config.dma_channel = dma_channel_table[config_1.dma_channel];
 245     config.ha_scsi_id = config_2.ha_scsi_id;
 246     config.heads = mapping_table[config_2.mapping_mode].heads;
 247     config.sectors = mapping_table[config_2.mapping_mode].sectors;
 248     config.bios_drive_number = config_2.bios_drive_number;
 249 
 250     /* To verify this card, we simply look for the UltraStor SCSI from the
 251        BIOS version notice. */
 252     if (config.bios_segment != NULL) {
 253         int found = 0;
 254 
 255         for (i = 0; !found && i < ARRAY_SIZE(signatures); i++)
 256             if (memcmp((char *)config.bios_segment + signatures[i].offset,
 257                        signatures[i].signature, signatures[i].length))
 258                 found = 1;
 259         if (!found)
 260             config.bios_segment = NULL;
 261     }
 262     if (!config.bios_segment) {
 263 #if (ULTRASTOR_DEBUG & UD_DETECT)
 264         printk("US14F: detect: not detected.\n");
 265 #endif
 266         return FALSE;
 267     }
 268 
 269     /* Final consistancy check, verify previous info. */
 270     if (!config.dma_channel || !(config_2.tfr_port & 0x2)) {
 271 #if (ULTRASTOR_DEBUG & UD_DETECT)
 272         printk("US14F: detect: consistancy check failed\n");
 273 #endif
 274         return FALSE;
 275     }
 276 
 277     /* If we were TRULY paranoid, we could issue a host adapter inquiry
 278        command here and verify the data returned.  But frankly, I'm
 279        exhausted! */
 280 
 281     /* Finally!  Now I'm satisfied... */
 282 #if (ULTRASTOR_DEBUG & UD_DETECT)
 283     printk("US14F: detect: detect succeeded\n"
 284            "  Port address: %03X\n"
 285            "  BIOS segment: %05X\n"
 286            "  Interrupt: %u\n"
 287            "  DMA channel: %u\n"
 288            "  H/A SCSI ID: %u\n",
 289            PORT_ADDRESS, config.bios_segment, config.interrupt,
 290            config.dma_channel, config.ha_scsi_id);
 291 #endif
 292     host_number = hostnum;
 293     scsi_hosts[hostnum].this_id = config.ha_scsi_id;
 294 #ifndef NO_QUEUEING
 295     set_intr_gate(0x20 + config.interrupt, ultrastor_interrupt);
 296     /* gate to PIC 2 */
 297     outb_p(inb_p(0x21) & ~BIT(2), 0x21);
 298     /* enable the interrupt */
 299     outb(inb_p(0xA1) & ~BIT(config.interrupt - 8), 0xA1);
 300 #endif
 301     return TRUE;
 302 }
 303 
 304 const char *ultrastor_14f_info(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 305 {
 306     return "UltraStor 14F SCSI driver version "
 307            VERSION
 308            " by David B. Gentzel\n";
 309 }
 310 
 311 static struct mscp mscp = {
 312     OP_SCSI, DTD_SCSI, FALSE, TRUE, FALSE       /* This stuff doesn't change */
 313 };
 314 
 315 int ultrastor_14f_queuecommand(unsigned char target, const void *cmnd,
     /* [previous][next][first][last][top][bottom][index][help] */
 316                                void *buff, int bufflen, void (*done)(int, int))
 317 {
 318     unsigned char in_byte;
 319 
 320 #if (ULTRASTOR_DEBUG & UD_COMMAND)
 321     printk("US14F: queuecommand: called\n");
 322 #endif
 323 
 324     /* Skip first (constant) byte */
 325     memset((char *)&mscp + 1, 0, sizeof (struct mscp) - 1);
 326     mscp.target_id = target;
 327     /* mscp.lun = ???; */
 328     mscp.transfer_data = *(Longword *)&buff;
 329     mscp.transfer_data_length = *(Longword *)&bufflen,
 330     mscp.length_of_scsi_cdbs = ((*(unsigned char *)cmnd <= 0x1F) ? 6 : 10);
 331     memcpy(mscp.scsi_cdbs, cmnd, mscp.length_of_scsi_cdbs);
 332 
 333     /* Find free OGM slot (OGMINT bit is 0) */
 334     do
 335         in_byte = inb_p(LCL_DOORBELL_INTR(PORT_ADDRESS));
 336     while (!aborted && (in_byte & 1));
 337     if (aborted) {
 338 #if (ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT))
 339         printk("US14F: queuecommand: aborted\n");
 340 #endif
 341         /* ??? is this right? */
 342         return (aborted << 16);
 343     }
 344 
 345     /* Store pointer in OGM address bytes */
 346     outb_p(BYTE(&mscp, 0), OGM_DATA_PTR(PORT_ADDRESS + 0));
 347     outb_p(BYTE(&mscp, 1), OGM_DATA_PTR(PORT_ADDRESS + 1));
 348     outb_p(BYTE(&mscp, 2), OGM_DATA_PTR(PORT_ADDRESS + 2));
 349     outb_p(BYTE(&mscp, 3), OGM_DATA_PTR(PORT_ADDRESS + 3));
 350 
 351     /* Issue OGM interrupt */
 352     outb_p(0x1, LCL_DOORBELL_INTR(PORT_ADDRESS));
 353 
 354     ultrastor_done = done;
 355 
 356 #if (ULTRASTOR_DEBUG & UD_COMMAND)
 357     printk("US14F: queuecommand: returning\n");
 358 #endif
 359 
 360     return 0;
 361 }
 362 
 363 #ifdef NO_QUEUEING
 364 int ultrastor_14f_command(unsigned char target, const void *cmnd,
     /* [previous][next][first][last][top][bottom][index][help] */
 365                           void *buff, int bufflen)
 366 {
 367     unsigned char in_byte;
 368 
 369 #if (ULTRASTOR_DEBUG & UD_COMMAND)
 370     printk("US14F: command: called\n");
 371 #endif
 372 
 373     (void)ultrastor_14f_queuecommand(target, cmnd, buff, bufflen, 0);
 374 
 375     /* Wait for ICM interrupt */
 376     do
 377         in_byte = inb_p(SYS_DOORBELL_INTR(PORT_ADDRESS));
 378     while (!aborted && !(in_byte & 1));
 379     if (aborted) {
 380 #if (ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT))
 381         printk("US14F: command: aborted\n");
 382 #endif
 383         /* ??? is this right? */
 384         return (aborted << 16);
 385     }
 386 
 387     /* Clean ICM slot (set ICMINT bit to 0) */
 388     outb_p(0x1, SYS_DOORBELL_INTR(PORT_ADDRESS));
 389 
 390 #if (ULTRASTOR_DEBUG & UD_COMMAND)
 391     printk("US14F: command: returning %08X\n",
 392            (mscp.adapter_status << 16) | mscp.target_status);
 393 #endif
 394 
 395     /* ??? not right, but okay for now? */
 396     return (mscp.adapter_status << 16) | mscp.target_status;
 397 }
 398 #endif
 399 
 400 int ultrastor_14f_abort(int code)
     /* [previous][next][first][last][top][bottom][index][help] */
 401 {
 402 #if (ULTRASTOR_DEBUG & UD_ABORT)
 403     printk("US14F: abort: called\n");
 404 #endif
 405 
 406     aborted = (code ? code : DID_ABORT);
 407 
 408 #if (ULTRASTOR_DEBUG & UD_ABORT)
 409     printk("US14F: abort: returning\n");
 410 #endif
 411 
 412     return 0;
 413 }
 414 
 415 int ultrastor_14f_reset(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 416 {
 417     unsigned char in_byte;
 418 
 419 #if (ULTRASTOR_DEBUG & UD_RESET)
 420     printk("US14F: reset: called\n");
 421 #endif
 422 
 423     /* Issue SCSI BUS reset */
 424     outb_p(0x20, LCL_DOORBELL_INTR(PORT_ADDRESS));
 425 
 426     /* Wait for completion... */
 427     do
 428         in_byte = inb_p(LCL_DOORBELL_INTR(PORT_ADDRESS));
 429     while (in_byte & 0x20);
 430 
 431     aborted = DID_RESET;
 432 
 433 #if (ULTRASTOR_DEBUG & UD_RESET)
 434     printk("US14F: reset: returning\n");
 435 #endif
 436     return 0;
 437 }
 438 
 439 #ifndef NO_QUEUEING
 440 void ultrastor_interrupt_service(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 441 {
 442 #if (ULTRASTOR_DEBUG & UD_INTERRUPT)
 443     printk("US14F: interrupt_service: called: status = %08X\n",
 444            (mscp.adapter_status << 16) | mscp.target_status);
 445 #endif
 446 
 447     if (ultrastor_done == 0)
 448         panic("US14F: interrupt_service: unexpected interrupt!\n");
 449     else {
 450         void (*done)(int, int);
 451 
 452         /* Save ultrastor_done locally and zero before calling.  This is needed
 453            as once we call done, we may get another command queued before this
 454            interrupt service routine can return. */
 455         done = ultrastor_done;
 456         ultrastor_done = 0;
 457 
 458         /* Clean ICM slot (set ICMINT bit to 0) */
 459         outb_p(0x1, SYS_DOORBELL_INTR(PORT_ADDRESS));
 460 
 461         /* Let the higher levels know that we're done */
 462         /* ??? status is wrong here... */
 463         done(host_number, (mscp.adapter_status << 16) | mscp.target_status);
 464     }
 465 
 466 #if (ULTRASTOR_DEBUG & UD_INTERRUPT)
 467     printk("US14F: interrupt_service: returning\n");
 468 #endif
 469 }
 470 
 471 __asm__("
 472 _ultrastor_interrupt:
 473         cld
 474         pushl %eax
 475         pushl %ecx
 476         pushl %edx
 477         push %ds
 478         push %es
 479         push %fs
 480         movl $0x10,%eax
 481         mov %ax,%ds
 482         mov %ax,%es
 483         movl $0x17,%eax
 484         mov %ax,%fs
 485         movb $0x20,%al
 486         outb %al,$0xA0          # EOI to interrupt controller #1
 487         outb %al,$0x80          # give port chance to breathe
 488         outb %al,$0x80
 489         outb %al,$0x80
 490         outb %al,$0x80
 491         outb %al,$0x20
 492         call _ultrastor_interrupt_service
 493         pop %fs
 494         pop %es
 495         pop %ds
 496         popl %edx
 497         popl %ecx
 498         popl %eax
 499         iret
 500 ");
 501 #endif
 502 
 503 #endif

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