|
|
1.1 ! root 1: #define AUTOSENSE ! 2: #define PSEUDO_DMA ! 3: #define FOO ! 4: #define UNSAFE /* Not unsafe for PAS16 -- use it */ ! 5: ! 6: /* ! 7: * This driver adapted from Drew Eckhardt's Trantor T128 driver ! 8: * ! 9: * Copyright 1993, Drew Eckhardt ! 10: * Visionary Computing ! 11: * (Unix and Linux consulting and custom programming) ! 12: * [email protected] ! 13: * +1 (303) 666-5836 ! 14: * ! 15: * ( Based on T128 - DISTRIBUTION RELEASE 3. ) ! 16: * ! 17: * Modified to work with the Pro Audio Spectrum/Studio 16 ! 18: * by John Weidman. ! 19: * ! 20: * ! 21: * For more information, please consult ! 22: * ! 23: * Media Vision ! 24: * (510) 770-8600 ! 25: * (800) 348-7116 ! 26: * ! 27: * and ! 28: * ! 29: * NCR 5380 Family ! 30: * SCSI Protocol Controller ! 31: * Databook ! 32: * ! 33: * NCR Microelectronics ! 34: * 1635 Aeroplaza Drive ! 35: * Colorado Springs, CO 80916 ! 36: * 1+ (719) 578-3400 ! 37: * 1+ (800) 334-5454 ! 38: */ ! 39: ! 40: /* ! 41: * Options : ! 42: * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically ! 43: * for commands that return with a CHECK CONDITION status. ! 44: * ! 45: * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512 ! 46: * bytes at a time. Since interrupts are disabled by default during ! 47: * these transfers, we might need this to give reasonable interrupt ! 48: * service time if the transfer size gets too large. ! 49: * ! 50: * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance ! 51: * increase compared to polled I/O. ! 52: * ! 53: * PARITY - enable parity checking. Not supported. ! 54: * ! 55: * SCSI2 - enable support for SCSI-II tagged queueing. Untested. ! 56: * ! 57: * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. This ! 58: * parameter comes from the NCR5380 code. It is NOT unsafe with ! 59: * the PAS16 and you should use it. If you don't you will have ! 60: * a problem with dropped characters during high speed ! 61: * communications during SCSI transfers. If you really don't ! 62: * want to use UNSAFE you can try defining LIMIT_TRANSFERSIZE or ! 63: * twiddle with the transfer size in the high level code. ! 64: * ! 65: * USLEEP - enable support for devices that don't disconnect. Untested. ! 66: * ! 67: * The card is detected and initialized in one of several ways : ! 68: * 1. Autoprobe (default) - There are many different models of ! 69: * the Pro Audio Spectrum/Studio 16, and I only have one of ! 70: * them, so this may require a little tweaking. An interrupt ! 71: * is triggered to autoprobe for the interrupt line. Note: ! 72: * with the newer model boards, the interrupt is set via ! 73: * software after reset using the default_irq for the ! 74: * current board number. ! 75: * ! 76: * ! 77: * 2. With command line overrides - pas16=port,irq may be ! 78: * used on the LILO command line to override the defaults. ! 79: * ! 80: * 3. With the PAS16_OVERRIDE compile time define. This is ! 81: * specified as an array of address, irq tuples. Ie, for ! 82: * one board at the default 0x388 address, IRQ10, I could say ! 83: * -DPAS16_OVERRIDE={{0x388, 10}} ! 84: * NOTE: Untested. ! 85: * ! 86: * Note that if the override methods are used, place holders must ! 87: * be specified for other boards in the system. ! 88: * ! 89: * ! 90: * Configuration notes : ! 91: * The current driver does not support interrupt sharing with the ! 92: * sound portion of the card. If you use the same irq for the ! 93: * scsi port and sound you will have problems. Either use ! 94: * a different irq for the scsi port or don't use interrupts ! 95: * for the scsi port. ! 96: * ! 97: * If you have problems with your card not being recognized, use ! 98: * the LILO command line override. Try to get it recognized without ! 99: * interrupts. Ie, for a board at the default 0x388 base port, ! 100: * boot: linux pas16=0x388,255 ! 101: * ! 102: * (255 is the IRQ_NONE constant in NCR5380.h) ! 103: */ ! 104: ! 105: #ifdef MODULE ! 106: #include <linux/module.h> ! 107: #endif ! 108: ! 109: #include <asm/system.h> ! 110: #include <linux/signal.h> ! 111: #include <linux/proc_fs.h> ! 112: #include <linux/sched.h> ! 113: #include <asm/io.h> ! 114: #include <linux/blk.h> ! 115: #include "scsi.h" ! 116: #include "hosts.h" ! 117: #include "pas16.h" ! 118: #define AUTOPROBE_IRQ ! 119: #include "NCR5380.h" ! 120: #include "constants.h" ! 121: #include "sd.h" ! 122: ! 123: #include<linux/stat.h> ! 124: ! 125: struct proc_dir_entry proc_scsi_pas16 = { ! 126: PROC_SCSI_PAS16, 5, "pas16", ! 127: S_IFDIR | S_IRUGO | S_IXUGO, 2 ! 128: }; ! 129: static int pas_maxi = 0; ! 130: static int pas_wmaxi = 0; ! 131: ! 132: ! 133: int scsi_irq_translate[] = ! 134: { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 }; ! 135: ! 136: /* The default_irqs array contains values used to set the irq into the ! 137: * board via software (as must be done on newer model boards without ! 138: * irq jumpers on the board). The first value in the array will be ! 139: * assigned to logical board 0, the next to board 1, etc. ! 140: */ ! 141: int default_irqs[] = { PAS16_DEFAULT_BOARD_1_IRQ, ! 142: PAS16_DEFAULT_BOARD_2_IRQ, ! 143: PAS16_DEFAULT_BOARD_3_IRQ, ! 144: PAS16_DEFAULT_BOARD_4_IRQ ! 145: }; ! 146: ! 147: static struct override { ! 148: unsigned short io_port; ! 149: int irq; ! 150: } overrides ! 151: #ifdef PAS16_OVERRIDE ! 152: [] = PAS16_OVERRIDE; ! 153: #else ! 154: [4] = {{0,IRQ_AUTO}, {0,IRQ_AUTO}, {0,IRQ_AUTO}, ! 155: {0,IRQ_AUTO}}; ! 156: #endif ! 157: ! 158: #define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override)) ! 159: ! 160: static struct base { ! 161: unsigned short io_port; ! 162: int noauto; ! 163: } bases[] = { {PAS16_DEFAULT_BASE_1, 0}, ! 164: {PAS16_DEFAULT_BASE_2, 0}, ! 165: {PAS16_DEFAULT_BASE_3, 0}, ! 166: {PAS16_DEFAULT_BASE_4, 0} ! 167: }; ! 168: ! 169: #define NO_BASES (sizeof (bases) / sizeof (struct base)) ! 170: ! 171: unsigned short pas16_offset[ 8 ] = ! 172: { ! 173: 0x1c00, /* OUTPUT_DATA_REG */ ! 174: 0x1c01, /* INITIATOR_COMMAND_REG */ ! 175: 0x1c02, /* MODE_REG */ ! 176: 0x1c03, /* TARGET_COMMAND_REG */ ! 177: 0x3c00, /* STATUS_REG ro, SELECT_ENABLE_REG wo */ ! 178: 0x3c01, /* BUS_AND_STATUS_REG ro, START_DMA_SEND_REG wo */ ! 179: 0x3c02, /* INPUT_DATA_REGISTER ro, (N/A on PAS16 ?) ! 180: * START_DMA_TARGET_RECEIVE_REG wo ! 181: */ ! 182: 0x3c03, /* RESET_PARITY_INTERRUPT_REG ro, ! 183: * START_DMA_INITIATOR_RECEIVE_REG wo ! 184: */ ! 185: }; ! 186: /*----------------------------------------------------------------*/ ! 187: /* the following will set the monitor border color (useful to find ! 188: where something crashed or gets stuck at */ ! 189: /* 1 = blue ! 190: 2 = green ! 191: 3 = cyan ! 192: 4 = red ! 193: 5 = magenta ! 194: 6 = yellow ! 195: 7 = white ! 196: */ ! 197: #if 1 ! 198: #define rtrc(i) {inb(0x3da); outb(0x31, 0x3c0); outb((i), 0x3c0);} ! 199: #else ! 200: #define rtrc(i) {} ! 201: #endif ! 202: ! 203: ! 204: /* ! 205: * Function : enable_board( int board_num, unsigned short port ) ! 206: * ! 207: * Purpose : set address in new model board ! 208: * ! 209: * Inputs : board_num - logical board number 0-3, port - base address ! 210: * ! 211: */ ! 212: ! 213: void enable_board( int board_num, unsigned short port ) ! 214: { ! 215: outb( 0xbc + board_num, MASTER_ADDRESS_PTR ); ! 216: outb( port >> 2, MASTER_ADDRESS_PTR ); ! 217: } ! 218: ! 219: ! 220: ! 221: /* ! 222: * Function : init_board( unsigned short port, int irq ) ! 223: * ! 224: * Purpose : Set the board up to handle the SCSI interface ! 225: * ! 226: * Inputs : port - base address of the board, ! 227: * irq - irq to assign to the SCSI port ! 228: * force_irq - set it even if it conflicts with sound driver ! 229: * ! 230: */ ! 231: ! 232: void init_board( unsigned short io_port, int irq, int force_irq ) ! 233: { ! 234: unsigned int tmp; ! 235: unsigned int pas_irq_code; ! 236: ! 237: /* Initialize the SCSI part of the board */ ! 238: ! 239: outb( 0x30, io_port + P_TIMEOUT_COUNTER_REG ); /* Timeout counter */ ! 240: outb( 0x01, io_port + P_TIMEOUT_STATUS_REG_OFFSET ); /* Reset TC */ ! 241: outb( 0x01, io_port + WAIT_STATE ); /* 1 Wait state */ ! 242: ! 243: NCR5380_read( RESET_PARITY_INTERRUPT_REG ); ! 244: ! 245: /* Set the SCSI interrupt pointer without mucking up the sound ! 246: * interrupt pointer in the same byte. ! 247: */ ! 248: pas_irq_code = ( irq < 16 ) ? scsi_irq_translate[irq] : 0; ! 249: tmp = inb( io_port + IO_CONFIG_3 ); ! 250: ! 251: if( (( tmp & 0x0f ) == pas_irq_code) && pas_irq_code > 0 ! 252: && !force_irq ) ! 253: { ! 254: printk( "pas16: WARNING: Can't use same irq as sound " ! 255: "driver -- interrupts disabled\n" ); ! 256: /* Set up the drive parameters, disable 5380 interrupts */ ! 257: outb( 0x4d, io_port + SYS_CONFIG_4 ); ! 258: } ! 259: else ! 260: { ! 261: tmp = ( tmp & 0x0f ) | ( pas_irq_code << 4 ); ! 262: outb( tmp, io_port + IO_CONFIG_3 ); ! 263: ! 264: /* Set up the drive parameters and enable 5380 interrupts */ ! 265: outb( 0x6d, io_port + SYS_CONFIG_4 ); ! 266: } ! 267: } ! 268: ! 269: ! 270: /* ! 271: * Function : pas16_hw_detect( unsigned short board_num ) ! 272: * ! 273: * Purpose : determine if a pas16 board is present ! 274: * ! 275: * Inputs : board_num - logical board number ( 0 - 3 ) ! 276: * ! 277: * Returns : 0 if board not found, 1 if found. ! 278: */ ! 279: ! 280: int pas16_hw_detect( unsigned short board_num ) ! 281: { ! 282: unsigned char board_rev, tmp; ! 283: unsigned short io_port = bases[ board_num ].io_port; ! 284: ! 285: /* See if we can find a PAS16 board at the address associated ! 286: * with this logical board number. ! 287: */ ! 288: ! 289: /* First, attempt to take a newer model board out of reset and ! 290: * give it a base address. This shouldn't affect older boards. ! 291: */ ! 292: enable_board( board_num, io_port ); ! 293: ! 294: /* Now see if it looks like a PAS16 board */ ! 295: board_rev = inb( io_port + PCB_CONFIG ); ! 296: ! 297: if( board_rev == 0xff ) ! 298: return 0; ! 299: ! 300: tmp = board_rev ^ 0xe0; ! 301: ! 302: outb( tmp, io_port + PCB_CONFIG ); ! 303: tmp = inb( io_port + PCB_CONFIG ); ! 304: outb( board_rev, io_port + PCB_CONFIG ); ! 305: ! 306: if( board_rev != tmp ) /* Not a PAS-16 */ ! 307: return 0; ! 308: ! 309: if( ( inb( io_port + OPERATION_MODE_1 ) & 0x03 ) != 0x03 ) ! 310: return 0; /* return if no SCSI interface found */ ! 311: ! 312: /* Mediavision has some new model boards that return ID bits ! 313: * that indicate a SCSI interface, but they're not (LMS). We'll ! 314: * put in an additional test to try to weed them out. ! 315: */ ! 316: ! 317: outb( 0x01, io_port + WAIT_STATE ); /* 1 Wait state */ ! 318: NCR5380_write( MODE_REG, 0x20 ); /* Is it really SCSI? */ ! 319: if( NCR5380_read( MODE_REG ) != 0x20 ) /* Write to a reg. */ ! 320: return 0; /* and try to read */ ! 321: NCR5380_write( MODE_REG, 0x00 ); /* it back. */ ! 322: if( NCR5380_read( MODE_REG ) != 0x00 ) ! 323: return 0; ! 324: ! 325: return 1; ! 326: } ! 327: ! 328: ! 329: /* ! 330: * Function : pas16_setup(char *str, int *ints) ! 331: * ! 332: * Purpose : LILO command line initialization of the overrides array, ! 333: * ! 334: * Inputs : str - unused, ints - array of integer parameters with ints[0] ! 335: * equal to the number of ints. ! 336: * ! 337: */ ! 338: ! 339: void pas16_setup(char *str, int *ints) { ! 340: static int commandline_current = 0; ! 341: int i; ! 342: if (ints[0] != 2) ! 343: printk("pas16_setup : usage pas16=io_port,irq\n"); ! 344: else ! 345: if (commandline_current < NO_OVERRIDES) { ! 346: overrides[commandline_current].io_port = (unsigned short) ints[1]; ! 347: overrides[commandline_current].irq = ints[2]; ! 348: for (i = 0; i < NO_BASES; ++i) ! 349: if (bases[i].io_port == (unsigned short) ints[1]) { ! 350: bases[i].noauto = 1; ! 351: break; ! 352: } ! 353: ++commandline_current; ! 354: } ! 355: } ! 356: ! 357: /* ! 358: * Function : int pas16_detect(Scsi_Host_Template * tpnt) ! 359: * ! 360: * Purpose : detects and initializes PAS16 controllers ! 361: * that were autoprobed, overridden on the LILO command line, ! 362: * or specified at compile time. ! 363: * ! 364: * Inputs : tpnt - template for this SCSI adapter. ! 365: * ! 366: * Returns : 1 if a host adapter was found, 0 if not. ! 367: * ! 368: */ ! 369: ! 370: int pas16_detect(Scsi_Host_Template * tpnt) { ! 371: static int current_override = 0; ! 372: static unsigned short current_base = 0; ! 373: struct Scsi_Host *instance; ! 374: unsigned short io_port; ! 375: int count; ! 376: ! 377: tpnt->proc_dir = &proc_scsi_pas16; ! 378: tpnt->proc_info = &pas16_proc_info; ! 379: ! 380: for (count = 0; current_override < NO_OVERRIDES; ++current_override) { ! 381: io_port = 0; ! 382: ! 383: if (overrides[current_override].io_port) ! 384: { ! 385: io_port = overrides[current_override].io_port; ! 386: enable_board( current_override, io_port ); ! 387: init_board( io_port, overrides[current_override].irq, 1 ); ! 388: } ! 389: else ! 390: for (; !io_port && (current_base < NO_BASES); ++current_base) { ! 391: #if (PDEBUG & PDEBUG_INIT) ! 392: printk("scsi-pas16 : probing io_port %04x\n", (unsigned int) bases[current_base].io_port); ! 393: #endif ! 394: if ( !bases[current_base].noauto && ! 395: pas16_hw_detect( current_base ) ){ ! 396: io_port = bases[current_base].io_port; ! 397: init_board( io_port, default_irqs[ current_base ], 0 ); ! 398: #if (PDEBUG & PDEBUG_INIT) ! 399: printk("scsi-pas16 : detected board.\n"); ! 400: #endif ! 401: } ! 402: } ! 403: ! 404: ! 405: #if defined(PDEBUG) && (PDEBUG & PDEBUG_INIT) ! 406: printk("scsi-pas16 : io_port = %04x\n", (unsigned int) io_port); ! 407: #endif ! 408: ! 409: if (!io_port) ! 410: break; ! 411: ! 412: instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); ! 413: instance->io_port = io_port; ! 414: ! 415: NCR5380_init(instance, 0); ! 416: ! 417: if (overrides[current_override].irq != IRQ_AUTO) ! 418: instance->irq = overrides[current_override].irq; ! 419: else ! 420: instance->irq = NCR5380_probe_irq(instance, PAS16_IRQS); ! 421: ! 422: if (instance->irq != IRQ_NONE) ! 423: if (request_irq(instance->irq, pas16_intr, SA_INTERRUPT, "pas16", NULL)) { ! 424: printk("scsi%d : IRQ%d not free, interrupts disabled\n", ! 425: instance->host_no, instance->irq); ! 426: instance->irq = IRQ_NONE; ! 427: } ! 428: ! 429: if (instance->irq == IRQ_NONE) { ! 430: printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no); ! 431: printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no); ! 432: /* Disable 5380 interrupts, leave drive params the same */ ! 433: outb( 0x4d, io_port + SYS_CONFIG_4 ); ! 434: outb( (inb(io_port + IO_CONFIG_3) & 0x0f), io_port + IO_CONFIG_3 ); ! 435: } ! 436: ! 437: #if defined(PDEBUG) && (PDEBUG & PDEBUG_INIT) ! 438: printk("scsi%d : irq = %d\n", instance->host_no, instance->irq); ! 439: #endif ! 440: ! 441: printk("scsi%d : at 0x%04x", instance->host_no, (int) ! 442: instance->io_port); ! 443: if (instance->irq == IRQ_NONE) ! 444: printk (" interrupts disabled"); ! 445: else ! 446: printk (" irq %d", instance->irq); ! 447: printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", ! 448: CAN_QUEUE, CMD_PER_LUN, PAS16_PUBLIC_RELEASE); ! 449: NCR5380_print_options(instance); ! 450: printk("\n"); ! 451: ! 452: ++current_override; ! 453: ++count; ! 454: } ! 455: return count; ! 456: } ! 457: ! 458: /* ! 459: * Function : int pas16_biosparam(Disk *disk, kdev_t dev, int *ip) ! 460: * ! 461: * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for ! 462: * the specified device / size. ! 463: * ! 464: * Inputs : size = size of device in sectors (512 bytes), dev = block device ! 465: * major / minor, ip[] = {heads, sectors, cylinders} ! 466: * ! 467: * Returns : always 0 (success), initializes ip ! 468: * ! 469: */ ! 470: ! 471: /* ! 472: * XXX Most SCSI boards use this mapping, I could be incorrect. Some one ! 473: * using hard disks on a trantor should verify that this mapping corresponds ! 474: * to that used by the BIOS / ASPI driver by running the linux fdisk program ! 475: * and matching the H_C_S coordinates to what DOS uses. ! 476: */ ! 477: ! 478: int pas16_biosparam(Disk * disk, kdev_t dev, int * ip) ! 479: { ! 480: int size = disk->capacity; ! 481: ip[0] = 64; ! 482: ip[1] = 32; ! 483: ip[2] = size >> 11; /* I think I have it as /(32*64) */ ! 484: if( ip[2] > 1024 ) { /* yes, >, not >= */ ! 485: ip[0]=255; ! 486: ip[1]=63; ! 487: ip[2]=size/(63*255); ! 488: if( ip[2] > 1023 ) /* yes >1023... */ ! 489: ip[2] = 1023; ! 490: } ! 491: ! 492: return 0; ! 493: } ! 494: ! 495: /* ! 496: * Function : int NCR5380_pread (struct Scsi_Host *instance, ! 497: * unsigned char *dst, int len) ! 498: * ! 499: * Purpose : Fast 5380 pseudo-dma read function, transfers len bytes to ! 500: * dst ! 501: * ! 502: * Inputs : dst = destination, len = length in bytes ! 503: * ! 504: * Returns : 0 on success, non zero on a failure such as a watchdog ! 505: * timeout. ! 506: */ ! 507: ! 508: static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst, ! 509: int len) { ! 510: register unsigned char *d = dst; ! 511: register unsigned short reg = (unsigned short) (instance->io_port + ! 512: P_DATA_REG_OFFSET); ! 513: register int i = len; ! 514: int ii = 0; ! 515: ! 516: while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) ) ! 517: ++ii; ! 518: ! 519: insb( reg, d, i ); ! 520: ! 521: if ( inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) { ! 522: outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET); ! 523: printk("scsi%d : watchdog timer fired in NCR5380_pread()\n", ! 524: instance->host_no); ! 525: return -1; ! 526: } ! 527: if (ii > pas_maxi) ! 528: pas_maxi = ii; ! 529: return 0; ! 530: } ! 531: ! 532: /* ! 533: * Function : int NCR5380_pwrite (struct Scsi_Host *instance, ! 534: * unsigned char *src, int len) ! 535: * ! 536: * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from ! 537: * src ! 538: * ! 539: * Inputs : src = source, len = length in bytes ! 540: * ! 541: * Returns : 0 on success, non zero on a failure such as a watchdog ! 542: * timeout. ! 543: */ ! 544: ! 545: static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src, ! 546: int len) { ! 547: register unsigned char *s = src; ! 548: register unsigned short reg = (instance->io_port + P_DATA_REG_OFFSET); ! 549: register int i = len; ! 550: int ii = 0; ! 551: ! 552: while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) ) ! 553: ++ii; ! 554: ! 555: outsb( reg, s, i ); ! 556: ! 557: if (inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) { ! 558: outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET); ! 559: printk("scsi%d : watchdog timer fired in NCR5380_pwrite()\n", ! 560: instance->host_no); ! 561: return -1; ! 562: } ! 563: if (ii > pas_maxi) ! 564: pas_wmaxi = ii; ! 565: return 0; ! 566: } ! 567: ! 568: #include "NCR5380.c" ! 569: ! 570: #ifdef MODULE ! 571: /* Eventually this will go into an include file, but this will be later */ ! 572: Scsi_Host_Template driver_template = MV_PAS16; ! 573: ! 574: #include <linux/module.h> ! 575: #include "scsi_module.c" ! 576: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.