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