Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.8 565 lines 16 kB view raw
1/* 2 * This driver adapted from Drew Eckhardt's Trantor T128 driver 3 * 4 * Copyright 1993, Drew Eckhardt 5 * Visionary Computing 6 * (Unix and Linux consulting and custom programming) 7 * drew@colorado.edu 8 * +1 (303) 666-5836 9 * 10 * ( Based on T128 - DISTRIBUTION RELEASE 3. ) 11 * 12 * Modified to work with the Pro Audio Spectrum/Studio 16 13 * by John Weidman. 14 * 15 * 16 * For more information, please consult 17 * 18 * Media Vision 19 * (510) 770-8600 20 * (800) 348-7116 21 */ 22 23/* 24 * The card is detected and initialized in one of several ways : 25 * 1. Autoprobe (default) - There are many different models of 26 * the Pro Audio Spectrum/Studio 16, and I only have one of 27 * them, so this may require a little tweaking. An interrupt 28 * is triggered to autoprobe for the interrupt line. Note: 29 * with the newer model boards, the interrupt is set via 30 * software after reset using the default_irq for the 31 * current board number. 32 * 33 * 2. With command line overrides - pas16=port,irq may be 34 * used on the LILO command line to override the defaults. 35 * 36 * 3. With the PAS16_OVERRIDE compile time define. This is 37 * specified as an array of address, irq tuples. Ie, for 38 * one board at the default 0x388 address, IRQ10, I could say 39 * -DPAS16_OVERRIDE={{0x388, 10}} 40 * NOTE: Untested. 41 * 42 * 4. When included as a module, with arguments passed on the command line: 43 * pas16_irq=xx the interrupt 44 * pas16_addr=xx the port 45 * e.g. "modprobe pas16 pas16_addr=0x388 pas16_irq=5" 46 * 47 * Note that if the override methods are used, place holders must 48 * be specified for other boards in the system. 49 * 50 * 51 * Configuration notes : 52 * The current driver does not support interrupt sharing with the 53 * sound portion of the card. If you use the same irq for the 54 * scsi port and sound you will have problems. Either use 55 * a different irq for the scsi port or don't use interrupts 56 * for the scsi port. 57 * 58 * If you have problems with your card not being recognized, use 59 * the LILO command line override. Try to get it recognized without 60 * interrupts. Ie, for a board at the default 0x388 base port, 61 * boot: linux pas16=0x388,0 62 * 63 * NO_IRQ (0) should be specified for no interrupt, 64 * IRQ_AUTO (254) to autoprobe for an IRQ line if overridden 65 * on the command line. 66 */ 67 68#include <linux/module.h> 69 70#include <asm/io.h> 71#include <asm/dma.h> 72#include <linux/blkdev.h> 73#include <linux/interrupt.h> 74#include <linux/init.h> 75 76#include <scsi/scsi_host.h> 77#include "pas16.h" 78#include "NCR5380.h" 79 80 81static unsigned short pas16_addr; 82static int pas16_irq; 83 84 85static const int scsi_irq_translate[] = 86 { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 }; 87 88/* The default_irqs array contains values used to set the irq into the 89 * board via software (as must be done on newer model boards without 90 * irq jumpers on the board). The first value in the array will be 91 * assigned to logical board 0, the next to board 1, etc. 92 */ 93static int default_irqs[] __initdata = 94 { PAS16_DEFAULT_BOARD_1_IRQ, 95 PAS16_DEFAULT_BOARD_2_IRQ, 96 PAS16_DEFAULT_BOARD_3_IRQ, 97 PAS16_DEFAULT_BOARD_4_IRQ 98 }; 99 100static struct override { 101 unsigned short io_port; 102 int irq; 103} overrides 104#ifdef PAS16_OVERRIDE 105 [] __initdata = PAS16_OVERRIDE; 106#else 107 [4] __initdata = {{0,IRQ_AUTO}, {0,IRQ_AUTO}, {0,IRQ_AUTO}, 108 {0,IRQ_AUTO}}; 109#endif 110 111#define NO_OVERRIDES ARRAY_SIZE(overrides) 112 113static struct base { 114 unsigned short io_port; 115 int noauto; 116} bases[] __initdata = 117 { {PAS16_DEFAULT_BASE_1, 0}, 118 {PAS16_DEFAULT_BASE_2, 0}, 119 {PAS16_DEFAULT_BASE_3, 0}, 120 {PAS16_DEFAULT_BASE_4, 0} 121 }; 122 123#define NO_BASES ARRAY_SIZE(bases) 124 125static const unsigned short pas16_offset[ 8 ] = 126 { 127 0x1c00, /* OUTPUT_DATA_REG */ 128 0x1c01, /* INITIATOR_COMMAND_REG */ 129 0x1c02, /* MODE_REG */ 130 0x1c03, /* TARGET_COMMAND_REG */ 131 0x3c00, /* STATUS_REG ro, SELECT_ENABLE_REG wo */ 132 0x3c01, /* BUS_AND_STATUS_REG ro, START_DMA_SEND_REG wo */ 133 0x3c02, /* INPUT_DATA_REGISTER ro, (N/A on PAS16 ?) 134 * START_DMA_TARGET_RECEIVE_REG wo 135 */ 136 0x3c03, /* RESET_PARITY_INTERRUPT_REG ro, 137 * START_DMA_INITIATOR_RECEIVE_REG wo 138 */ 139 }; 140 141 142/* 143 * Function : enable_board( int board_num, unsigned short port ) 144 * 145 * Purpose : set address in new model board 146 * 147 * Inputs : board_num - logical board number 0-3, port - base address 148 * 149 */ 150 151static void __init 152 enable_board( int board_num, unsigned short port ) 153{ 154 outb( 0xbc + board_num, MASTER_ADDRESS_PTR ); 155 outb( port >> 2, MASTER_ADDRESS_PTR ); 156} 157 158 159 160/* 161 * Function : init_board( unsigned short port, int irq ) 162 * 163 * Purpose : Set the board up to handle the SCSI interface 164 * 165 * Inputs : port - base address of the board, 166 * irq - irq to assign to the SCSI port 167 * force_irq - set it even if it conflicts with sound driver 168 * 169 */ 170 171static void __init 172 init_board( unsigned short io_port, int irq, int force_irq ) 173{ 174 unsigned int tmp; 175 unsigned int pas_irq_code; 176 177 /* Initialize the SCSI part of the board */ 178 179 outb( 0x30, io_port + P_TIMEOUT_COUNTER_REG ); /* Timeout counter */ 180 outb( 0x01, io_port + P_TIMEOUT_STATUS_REG_OFFSET ); /* Reset TC */ 181 outb( 0x01, io_port + WAIT_STATE ); /* 1 Wait state */ 182 183 inb(io_port + pas16_offset[RESET_PARITY_INTERRUPT_REG]); 184 185 /* Set the SCSI interrupt pointer without mucking up the sound 186 * interrupt pointer in the same byte. 187 */ 188 pas_irq_code = ( irq < 16 ) ? scsi_irq_translate[irq] : 0; 189 tmp = inb( io_port + IO_CONFIG_3 ); 190 191 if( (( tmp & 0x0f ) == pas_irq_code) && pas_irq_code > 0 192 && !force_irq ) 193 { 194 printk( "pas16: WARNING: Can't use same irq as sound " 195 "driver -- interrupts disabled\n" ); 196 /* Set up the drive parameters, disable 5380 interrupts */ 197 outb( 0x4d, io_port + SYS_CONFIG_4 ); 198 } 199 else 200 { 201 tmp = ( tmp & 0x0f ) | ( pas_irq_code << 4 ); 202 outb( tmp, io_port + IO_CONFIG_3 ); 203 204 /* Set up the drive parameters and enable 5380 interrupts */ 205 outb( 0x6d, io_port + SYS_CONFIG_4 ); 206 } 207} 208 209 210/* 211 * Function : pas16_hw_detect( unsigned short board_num ) 212 * 213 * Purpose : determine if a pas16 board is present 214 * 215 * Inputs : board_num - logical board number ( 0 - 3 ) 216 * 217 * Returns : 0 if board not found, 1 if found. 218 */ 219 220static int __init 221 pas16_hw_detect( unsigned short board_num ) 222{ 223 unsigned char board_rev, tmp; 224 unsigned short io_port = bases[ board_num ].io_port; 225 226 /* See if we can find a PAS16 board at the address associated 227 * with this logical board number. 228 */ 229 230 /* First, attempt to take a newer model board out of reset and 231 * give it a base address. This shouldn't affect older boards. 232 */ 233 enable_board( board_num, io_port ); 234 235 /* Now see if it looks like a PAS16 board */ 236 board_rev = inb( io_port + PCB_CONFIG ); 237 238 if( board_rev == 0xff ) 239 return 0; 240 241 tmp = board_rev ^ 0xe0; 242 243 outb( tmp, io_port + PCB_CONFIG ); 244 tmp = inb( io_port + PCB_CONFIG ); 245 outb( board_rev, io_port + PCB_CONFIG ); 246 247 if( board_rev != tmp ) /* Not a PAS-16 */ 248 return 0; 249 250 if( ( inb( io_port + OPERATION_MODE_1 ) & 0x03 ) != 0x03 ) 251 return 0; /* return if no SCSI interface found */ 252 253 /* Mediavision has some new model boards that return ID bits 254 * that indicate a SCSI interface, but they're not (LMS). We'll 255 * put in an additional test to try to weed them out. 256 */ 257 258 outb(0x01, io_port + WAIT_STATE); /* 1 Wait state */ 259 outb(0x20, io_port + pas16_offset[MODE_REG]); /* Is it really SCSI? */ 260 if (inb(io_port + pas16_offset[MODE_REG]) != 0x20) /* Write to a reg. */ 261 return 0; /* and try to read */ 262 outb(0x00, io_port + pas16_offset[MODE_REG]); /* it back. */ 263 if (inb(io_port + pas16_offset[MODE_REG]) != 0x00) 264 return 0; 265 266 return 1; 267} 268 269 270#ifndef MODULE 271/* 272 * Function : pas16_setup(char *str, int *ints) 273 * 274 * Purpose : LILO command line initialization of the overrides array, 275 * 276 * Inputs : str - unused, ints - array of integer parameters with ints[0] 277 * equal to the number of ints. 278 * 279 */ 280 281static int __init pas16_setup(char *str) 282{ 283 static int commandline_current; 284 int i; 285 int ints[10]; 286 287 get_options(str, ARRAY_SIZE(ints), ints); 288 if (ints[0] != 2) 289 printk("pas16_setup : usage pas16=io_port,irq\n"); 290 else 291 if (commandline_current < NO_OVERRIDES) { 292 overrides[commandline_current].io_port = (unsigned short) ints[1]; 293 overrides[commandline_current].irq = ints[2]; 294 for (i = 0; i < NO_BASES; ++i) 295 if (bases[i].io_port == (unsigned short) ints[1]) { 296 bases[i].noauto = 1; 297 break; 298 } 299 ++commandline_current; 300 } 301 return 1; 302} 303 304__setup("pas16=", pas16_setup); 305#endif 306 307/* 308 * Function : int pas16_detect(struct scsi_host_template * tpnt) 309 * 310 * Purpose : detects and initializes PAS16 controllers 311 * that were autoprobed, overridden on the LILO command line, 312 * or specified at compile time. 313 * 314 * Inputs : tpnt - template for this SCSI adapter. 315 * 316 * Returns : 1 if a host adapter was found, 0 if not. 317 * 318 */ 319 320static int __init pas16_detect(struct scsi_host_template *tpnt) 321{ 322 static int current_override; 323 static unsigned short current_base; 324 struct Scsi_Host *instance; 325 unsigned short io_port; 326 int count; 327 328 if (pas16_addr != 0) { 329 overrides[0].io_port = pas16_addr; 330 /* 331 * This is how we avoid seeing more than 332 * one host adapter at the same I/O port. 333 * Cribbed shamelessly from pas16_setup(). 334 */ 335 for (count = 0; count < NO_BASES; ++count) 336 if (bases[count].io_port == pas16_addr) { 337 bases[count].noauto = 1; 338 break; 339 } 340 } 341 if (pas16_irq != 0) 342 overrides[0].irq = pas16_irq; 343 344 for (count = 0; current_override < NO_OVERRIDES; ++current_override) { 345 io_port = 0; 346 347 if (overrides[current_override].io_port) 348 { 349 io_port = overrides[current_override].io_port; 350 enable_board( current_override, io_port ); 351 init_board( io_port, overrides[current_override].irq, 1 ); 352 } 353 else 354 for (; !io_port && (current_base < NO_BASES); ++current_base) { 355 dprintk(NDEBUG_INIT, "pas16: probing io_port 0x%04x\n", 356 (unsigned int)bases[current_base].io_port); 357 if ( !bases[current_base].noauto && 358 pas16_hw_detect( current_base ) ){ 359 io_port = bases[current_base].io_port; 360 init_board( io_port, default_irqs[ current_base ], 0 ); 361 dprintk(NDEBUG_INIT, "pas16: detected board\n"); 362 } 363 } 364 365 dprintk(NDEBUG_INIT, "pas16: io_port = 0x%04x\n", 366 (unsigned int)io_port); 367 368 if (!io_port) 369 break; 370 371 instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); 372 if(instance == NULL) 373 goto out; 374 375 instance->io_port = io_port; 376 377 if (NCR5380_init(instance, FLAG_DMA_FIXUP | FLAG_LATE_DMA_SETUP)) 378 goto out_unregister; 379 380 NCR5380_maybe_reset_bus(instance); 381 382 if (overrides[current_override].irq != IRQ_AUTO) 383 instance->irq = overrides[current_override].irq; 384 else 385 instance->irq = NCR5380_probe_irq(instance, PAS16_IRQS); 386 387 /* Compatibility with documented NCR5380 kernel parameters */ 388 if (instance->irq == 255) 389 instance->irq = NO_IRQ; 390 391 if (instance->irq != NO_IRQ) 392 if (request_irq(instance->irq, pas16_intr, 0, 393 "pas16", instance)) { 394 printk("scsi%d : IRQ%d not free, interrupts disabled\n", 395 instance->host_no, instance->irq); 396 instance->irq = NO_IRQ; 397 } 398 399 if (instance->irq == NO_IRQ) { 400 printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no); 401 printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no); 402 /* Disable 5380 interrupts, leave drive params the same */ 403 outb( 0x4d, io_port + SYS_CONFIG_4 ); 404 outb( (inb(io_port + IO_CONFIG_3) & 0x0f), io_port + IO_CONFIG_3 ); 405 } 406 407 dprintk(NDEBUG_INIT, "scsi%d : irq = %d\n", 408 instance->host_no, instance->irq); 409 410 ++current_override; 411 ++count; 412 } 413 return count; 414 415out_unregister: 416 scsi_unregister(instance); 417out: 418 return count; 419} 420 421/* 422 * Function : int pas16_biosparam(Disk *disk, struct block_device *dev, int *ip) 423 * 424 * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for 425 * the specified device / size. 426 * 427 * Inputs : size = size of device in sectors (512 bytes), dev = block device 428 * major / minor, ip[] = {heads, sectors, cylinders} 429 * 430 * Returns : always 0 (success), initializes ip 431 * 432 */ 433 434/* 435 * XXX Most SCSI boards use this mapping, I could be incorrect. Some one 436 * using hard disks on a trantor should verify that this mapping corresponds 437 * to that used by the BIOS / ASPI driver by running the linux fdisk program 438 * and matching the H_C_S coordinates to what DOS uses. 439 */ 440 441static int pas16_biosparam(struct scsi_device *sdev, struct block_device *dev, 442 sector_t capacity, int *ip) 443{ 444 int size = capacity; 445 ip[0] = 64; 446 ip[1] = 32; 447 ip[2] = size >> 11; /* I think I have it as /(32*64) */ 448 if( ip[2] > 1024 ) { /* yes, >, not >= */ 449 ip[0]=255; 450 ip[1]=63; 451 ip[2]=size/(63*255); 452 if( ip[2] > 1023 ) /* yes >1023... */ 453 ip[2] = 1023; 454 } 455 456 return 0; 457} 458 459/* 460 * Function : int pas16_pread (struct Scsi_Host *instance, 461 * unsigned char *dst, int len) 462 * 463 * Purpose : Fast 5380 pseudo-dma read function, transfers len bytes to 464 * dst 465 * 466 * Inputs : dst = destination, len = length in bytes 467 * 468 * Returns : 0 on success, non zero on a failure such as a watchdog 469 * timeout. 470 */ 471 472static inline int pas16_pread(struct Scsi_Host *instance, 473 unsigned char *dst, int len) 474{ 475 register unsigned char *d = dst; 476 register unsigned short reg = (unsigned short) (instance->io_port + 477 P_DATA_REG_OFFSET); 478 register int i = len; 479 int ii = 0; 480 481 while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) ) 482 ++ii; 483 484 insb( reg, d, i ); 485 486 if ( inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) { 487 outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET); 488 printk("scsi%d : watchdog timer fired in NCR5380_pread()\n", 489 instance->host_no); 490 return -1; 491 } 492 return 0; 493} 494 495/* 496 * Function : int pas16_pwrite (struct Scsi_Host *instance, 497 * unsigned char *src, int len) 498 * 499 * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from 500 * src 501 * 502 * Inputs : src = source, len = length in bytes 503 * 504 * Returns : 0 on success, non zero on a failure such as a watchdog 505 * timeout. 506 */ 507 508static inline int pas16_pwrite(struct Scsi_Host *instance, 509 unsigned char *src, int len) 510{ 511 register unsigned char *s = src; 512 register unsigned short reg = (instance->io_port + 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 outsb( reg, s, 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_pwrite()\n", 524 instance->host_no); 525 return -1; 526 } 527 return 0; 528} 529 530#include "NCR5380.c" 531 532static int pas16_release(struct Scsi_Host *shost) 533{ 534 if (shost->irq != NO_IRQ) 535 free_irq(shost->irq, shost); 536 NCR5380_exit(shost); 537 scsi_unregister(shost); 538 return 0; 539} 540 541static struct scsi_host_template driver_template = { 542 .name = "Pro Audio Spectrum-16 SCSI", 543 .detect = pas16_detect, 544 .release = pas16_release, 545 .proc_name = "pas16", 546 .info = pas16_info, 547 .queuecommand = pas16_queue_command, 548 .eh_abort_handler = pas16_abort, 549 .eh_bus_reset_handler = pas16_bus_reset, 550 .bios_param = pas16_biosparam, 551 .can_queue = 32, 552 .this_id = 7, 553 .sg_tablesize = SG_ALL, 554 .cmd_per_lun = 2, 555 .use_clustering = DISABLE_CLUSTERING, 556 .cmd_size = NCR5380_CMD_SIZE, 557 .max_sectors = 128, 558}; 559#include "scsi_module.c" 560 561#ifdef MODULE 562module_param(pas16_addr, ushort, 0); 563module_param(pas16_irq, int, 0); 564#endif 565MODULE_LICENSE("GPL");