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

   1 /*
   2  *      ultrastor.c     Copyright (C) 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.  I hope to go back and beat it into shape,
  28  *    but PLEASE, anyone else who would like to, please make improvements!
  29  *
  30  *    By defining NO_QUEUEING in ultrastor.h, you disable the queueing feature
  31  *    of the mid-level SCSI driver.  Once I'm satisfied that the queueing
  32  *    version is as stable as the non-queueing version, I'll eliminate this
  33  *    option.
  34  */
  35 
  36 #include <linux/stddef.h>
  37 #include <linux/string.h>
  38 #include <linux/sched.h>
  39 #include <linux/kernel.h>
  40 
  41 #include <asm/io.h>
  42 #include <asm/system.h>
  43 #include <asm/dma.h>
  44 
  45 #define ULTRASTOR_PRIVATE       /* Get the private stuff from ultrastor.h */
  46 #include "../blk.h"
  47 #include "scsi.h"
  48 #include "hosts.h"
  49 #include "ultrastor.h"
  50 
  51 #define VERSION "1.1 alpha"
  52 
  53 #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
  54 #define BIT(n) (1ul << (n))
  55 #define BYTE(num, n) ((unsigned char)((unsigned int)(num) >> ((n) * 8)))
  56 
  57 /* Simply using "unsigned long" in these structures won't work as it causes
  58    alignment.  Perhaps the "aligned" attribute may be used in GCC 2.0 to get
  59    around this, but for now I use this hack. */
  60 typedef struct {
  61     unsigned char bytes[4];
  62 } Longword;
  63 
  64 /* Used to fetch the configuration info from the config i/o registers.  We
  65    then store (in a friendlier format) in config. */
  66 struct config_1 {
  67     unsigned char bios_segment: 3;
  68     unsigned char reserved: 1;
  69     unsigned char interrupt: 2;
  70     unsigned char dma_channel: 2;
  71 };
  72 struct config_2 {
  73     unsigned char ha_scsi_id: 3;
  74     unsigned char mapping_mode: 2;
  75     unsigned char bios_drive_number: 1;
  76     unsigned char tfr_port: 2;
  77 };
  78 
  79 /* Used to store configuration info read from config i/o registers.  Most of
  80    this is not used yet, but might as well save it. */
  81 struct config {
  82     unsigned short port_address;
  83     const void *bios_segment;
  84     unsigned char interrupt: 4;
  85     unsigned char dma_channel: 3;
  86     unsigned char ha_scsi_id: 3;
  87     unsigned char heads: 6;
  88     unsigned char sectors: 6;
  89     unsigned char bios_drive_number: 1;
  90 };
  91 
  92 /* MailBox SCSI Command Packet.  Basic command structure for communicating
  93    with controller. */
  94 struct mscp {
  95     unsigned char opcode: 3;            /* type of command */
  96     unsigned char xdir: 2;              /* data transfer direction */
  97     unsigned char dcn: 1;               /* disable disconnect */
  98     unsigned char ca: 1;                /* use cache (if available) */
  99     unsigned char sg: 1;                /* scatter/gather operation */
 100     unsigned char target_id: 3;         /* target SCSI id */
 101     unsigned char ch_no: 2;             /* SCSI channel (always 0 for 14f) */
 102     unsigned char lun: 3;               /* logical unit number */
 103     Longword transfer_data;             /* transfer data pointer */
 104     Longword transfer_data_length;      /* length in bytes */
 105     Longword command_link;              /* for linking command chains */
 106     unsigned char scsi_command_link_id; /* identifies command in chain */
 107     unsigned char number_of_sg_list;    /* (if sg is set) 8 bytes per list */
 108     unsigned char length_of_sense_byte;
 109     unsigned char length_of_scsi_cdbs;  /* 6, 10, or 12 */
 110     unsigned char scsi_cdbs[12];        /* SCSI commands */
 111     unsigned char adapter_status;       /* non-zero indicates HA error */
 112     unsigned char target_status;        /* non-zero indicates target error */
 113     Longword sense_data;
 114 };
 115 
 116 /* Allowed BIOS base addresses for 14f (NULL indicates reserved) */
 117 static const void *const bios_segment_table[8] = {
 118     NULL,            (void *)0xC4000, (void *)0xC8000, (void *)0xCC000,
 119     (void *)0xD0000, (void *)0xD4000, (void *)0xD8000, (void *)0xDC000,
 120 };
 121 
 122 /* Allowed IRQs for 14f */
 123 static const unsigned char interrupt_table[4] = { 15, 14, 11, 10 };
 124 
 125 /* Allowed DMA channels for 14f (0 indicates reserved) */
 126 static const unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
 127 
 128 /* Head/sector mappings allowed by 14f */
 129 static const struct {
 130     unsigned char heads;
 131     unsigned char sectors;
 132 } mapping_table[4] = { { 16, 63 }, { 64, 32 }, { 64, 63 }, { 0, 0 } };
 133 
 134 /* Config info */
 135 static struct config config;
 136 
 137 /* Our index in the host adapter array maintained by higher-level driver */
 138 static int host_number;
 139 
 140 /* PORT_ADDRESS is first port address used for i/o of messages. */
 141 #ifdef PORT_OVERRIDE
 142 # define PORT_ADDRESS PORT_OVERRIDE
 143 #else
 144 # define PORT_ADDRESS (config.port_address)
 145 #endif
 146 
 147 static volatile int aborted = 0;
 148 
 149 /* A probe of address 0x310 screws up NE2000 cards */
 150 
 151 #ifndef PORT_OVERRIDE
 152 static const unsigned short ultrastor_ports[] = {
 153     0x330, 0x340, /* 0x310,*/  0x230, 0x240, 0x210, 0x130, 0x140,
 154 };
 155 #endif
 156 
 157 static void ultrastor_interrupt(int cpl);
 158 
 159 static void (*ultrastor_done)(Scsi_Cmnd *) = 0;
 160 static Scsi_Cmnd *SCint = NULL;
 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 
 295 #ifndef NO_QUEUEING
 296     if (request_irq(config.interrupt, ultrastor_interrupt)) {
 297         printk("Unable to allocate IRQ%u for UltraStor controller.\n",
 298                config.interrupt);
 299         return FALSE;
 300     }
 301 #endif
 302     if (request_dma(config.dma_channel)) {
 303         printk("Unable to allocate DMA channel %u for UltraStor controller.\n",
 304                config.dma_channel);
 305 #ifndef NO_QUEUEING
 306         free_irq(config.interrupt);
 307 #endif
 308         return FALSE;
 309     }
 310 
 311     return TRUE;
 312 }
 313 
 314 const char *ultrastor_14f_info(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 315 {
 316     return "UltraStor 14F SCSI driver version " VERSION;
 317 }
 318 
 319 static struct mscp mscp = {
 320     OP_SCSI, DTD_SCSI, 0, 1, 0          /* This stuff doesn't change */
 321 };
 322 
 323 int ultrastor_14f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
     /* [previous][next][first][last][top][bottom][index][help] */
 324 {
 325     unsigned char in_byte;
 326 
 327 #if (ULTRASTOR_DEBUG & UD_COMMAND)
 328     printk("US14F: queuecommand: called\n");
 329 #endif
 330 
 331     /* Skip first (constant) byte */
 332     memset((char *)&mscp + 1, 0, sizeof (struct mscp) - 1);
 333     mscp.target_id = SCpnt->target;
 334     mscp.lun = SCpnt->lun;
 335     mscp.transfer_data = *(Longword *)&SCpnt->request_buffer;
 336     mscp.transfer_data_length = *(Longword *)&SCpnt->request_bufflen;
 337     mscp.length_of_scsi_cdbs
 338         = ((*(unsigned char *)SCpnt->cmnd <= 0x1F) ? 6 : 10);
 339     memcpy(mscp.scsi_cdbs, SCpnt->cmnd, mscp.length_of_scsi_cdbs);
 340 
 341     /* Find free OGM slot (OGMINT bit is 0) */
 342     do
 343         in_byte = inb_p(LCL_DOORBELL_INTR(PORT_ADDRESS));
 344     while (!aborted && (in_byte & 1));
 345     if (aborted) {
 346 #if (ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT))
 347         printk("US14F: queuecommand: aborted\n");
 348 #endif
 349         /* ??? is this right? */
 350         return (aborted << 16);
 351     }
 352 
 353     /* Store pointer in OGM address bytes */
 354     outb_p(BYTE(&mscp, 0), OGM_DATA_PTR(PORT_ADDRESS + 0));
 355     outb_p(BYTE(&mscp, 1), OGM_DATA_PTR(PORT_ADDRESS + 1));
 356     outb_p(BYTE(&mscp, 2), OGM_DATA_PTR(PORT_ADDRESS + 2));
 357     outb_p(BYTE(&mscp, 3), OGM_DATA_PTR(PORT_ADDRESS + 3));
 358 
 359     /* Issue OGM interrupt */
 360     outb_p(0x1, LCL_DOORBELL_INTR(PORT_ADDRESS));
 361 
 362     ultrastor_done = done;
 363     SCint = SCpnt;
 364 
 365 #if (ULTRASTOR_DEBUG & UD_COMMAND)
 366     printk("US14F: queuecommand: returning\n");
 367 #endif
 368 
 369     return 0;
 370 }
 371 
 372 #ifdef NO_QUEUEING
 373 int ultrastor_14f_command(Scsi_Cmnd SCpnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 374 {
 375     unsigned char in_byte;
 376 
 377 #if (ULTRASTOR_DEBUG & UD_COMMAND)
 378     printk("US14F: command: called\n");
 379 #endif
 380 
 381     (void)ultrastor_14f_queuecommand(SCpnt, NULL);
 382 
 383     /* Wait for ICM interrupt */
 384     do
 385         in_byte = inb_p(SYS_DOORBELL_INTR(PORT_ADDRESS));
 386     while (!aborted && !(in_byte & 1));
 387     if (aborted) {
 388 #if (ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT))
 389         printk("US14F: command: aborted\n");
 390 #endif
 391         /* ??? is this right? */
 392         return (aborted << 16);
 393     }
 394 
 395     /* Clean ICM slot (set ICMINT bit to 0) */
 396     outb_p(0x1, SYS_DOORBELL_INTR(PORT_ADDRESS));
 397 
 398 #if (ULTRASTOR_DEBUG & UD_COMMAND)
 399     printk("US14F: command: returning %08X\n",
 400            (mscp.adapter_status << 16) | mscp.target_status);
 401 #endif
 402 
 403     /* ??? not right, but okay for now? */
 404     return (mscp.adapter_status << 16) | mscp.target_status;
 405 }
 406 #endif
 407 
 408 int ultrastor_14f_abort(Scsi_Cmnd *SCpnt, int code)
     /* [previous][next][first][last][top][bottom][index][help] */
 409 {
 410 #if (ULTRASTOR_DEBUG & UD_ABORT)
 411     printk("US14F: abort: called\n");
 412 #endif
 413 
 414     aborted = (code ? code : DID_ABORT);
 415 
 416 #if (ULTRASTOR_DEBUG & UD_ABORT)
 417     printk("US14F: abort: returning\n");
 418 #endif
 419 
 420     return 0;
 421 }
 422 
 423 /* Most of this is commented out because people were getting kernel crashes
 424    with it enabled.  If you want to re-enable this, please figure out why
 425    the kernel was panicing. ERY */
 426 
 427 int ultrastor_14f_reset(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 428 {
 429     unsigned char in_byte;
 430 
 431 #if (ULTRASTOR_DEBUG & UD_RESET)
 432     printk("US14F: reset: called\n");
 433 #endif
 434 
 435 #if 0
 436     /* Issue SCSI BUS reset */
 437     outb_p(0x20, LCL_DOORBELL_INTR(PORT_ADDRESS));
 438 
 439     /* Wait for completion... */
 440     do
 441         in_byte = inb_p(LCL_DOORBELL_INTR(PORT_ADDRESS));
 442     while (in_byte & 0x20);
 443 
 444     aborted = DID_RESET;
 445 #endif
 446 #if (ULTRASTOR_DEBUG & UD_RESET)
 447     printk("US14F: reset: returning\n");
 448 #endif
 449     return 0;
 450 }
 451 
 452 #ifndef NO_QUEUEING
 453 static void ultrastor_interrupt(int cpl)
     /* [previous][next][first][last][top][bottom][index][help] */
 454 {
 455 #if (ULTRASTOR_DEBUG & UD_INTERRUPT)
 456     printk("US14F: interrupt: called: status = %08X\n",
 457            (mscp.adapter_status << 16) | mscp.target_status);
 458 #endif
 459 
 460     if (ultrastor_done == 0)
 461         panic("US14F: interrupt: unexpected interrupt!\n");
 462     else {
 463         void (*done)(Scsi_Cmnd *);
 464 
 465         /* Save ultrastor_done locally and zero before calling.  This is needed
 466            as once we call done, we may get another command queued before this
 467            interrupt service routine can return. */
 468         done = ultrastor_done;
 469         ultrastor_done = 0;
 470 
 471         /* Clean ICM slot (set ICMINT bit to 0) */
 472         outb_p(0x1, SYS_DOORBELL_INTR(PORT_ADDRESS));
 473 
 474         /* Let the higher levels know that we're done */
 475         /* ??? status is wrong here... */
 476         SCint->result = (mscp.adapter_status << 16) | mscp.target_status;
 477         done(SCint);
 478     }
 479 
 480 #if (ULTRASTOR_DEBUG & UD_INTERRUPT)
 481     printk("US14F: interrupt: returning\n");
 482 #endif
 483 }
 484 #endif

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