|
|
1.1 root 1: /* 1.1.1.3 ! root 2: * ultrastor.c Copyright (C) 1991, 1992 David B. Gentzel 1.1.1.2 root 3: * Low-level SCSI driver for UltraStor 14F 1.1 root 4: * by David B. Gentzel, Whitfield Software Services, Carnegie, PA 5: * ([email protected]) 6: * Thanks to UltraStor for providing the necessary documentation 7: */ 8: 1.1.1.2 root 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: * 1.1.1.3 ! root 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. 1.1.1.2 root 35: */ 1.1 root 36: 37: #include <linux/config.h> 38: 39: #ifdef CONFIG_SCSI_ULTRASTOR 40: 1.1.1.3 ! root 41: #include <linux/stddef.h> 1.1 root 42: #include <linux/string.h> 43: #include <linux/sched.h> 44: #include <linux/kernel.h> 1.1.1.3 ! root 45: 1.1 root 46: #include <asm/io.h> 1.1.1.2 root 47: #include <asm/system.h> 1.1 root 48: 1.1.1.2 root 49: #define ULTRASTOR_PRIVATE /* Get the private stuff from ultrastor.h */ 1.1 root 50: #include "ultrastor.h" 51: #include "scsi.h" 52: #include "hosts.h" 53: 1.1.1.2 root 54: #define VERSION "1.0 beta" 1.1 root 55: 56: #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0]) 1.1.1.2 root 57: #define BIT(n) (1ul << (n)) 1.1 root 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: 1.1.1.2 root 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: 1.1 root 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 { 1.1.1.2 root 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; 1.1 root 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: 1.1.1.2 root 131: /* Head/sector mappings allowed by 14f */ 1.1 root 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 1.1.1.2 root 147: # define PORT_ADDRESS (config.port_address) 1.1 root 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: 1.1.1.2 root 158: void ultrastor_interrupt(void); 159: 160: static void (*ultrastor_done)(int, int) = 0; 161: 1.1 root 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) 171: { 172: size_t i; 173: unsigned char in_byte; 1.1.1.2 root 174: struct config_1 config_1; 175: struct config_2 config_2; 1.1 root 176: 1.1.1.2 root 177: #if (ULTRASTOR_DEBUG & UD_DETECT) 178: printk("US14F: detect: called\n"); 1.1 root 179: #endif 180: 181: #ifndef PORT_OVERRIDE 1.1.1.2 root 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 1.1.1.3 ! root 197: printk("US14F: detect: no adapter at port %03X\n", PORT_ADDRESS); 1.1.1.2 root 198: # endif 1.1 root 199: #endif 1.1.1.2 root 200: #ifdef PORT_OVERRIDE 201: return FALSE; 202: #else 203: continue; 1.1 root 204: #endif 1.1.1.2 root 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 1.1.1.3 ! root 213: printk("US14F: detect: no adapter at port %03X\n", PORT_ADDRESS); 1.1.1.2 root 214: # endif 1.1 root 215: #endif 1.1.1.2 root 216: #ifdef PORT_OVERRIDE 217: return FALSE; 218: #else 219: continue; 1.1 root 220: #endif 1.1.1.2 root 221: } 222: #ifndef PORT_OVERRIDE 223: break; 1.1 root 224: } 1.1.1.2 root 225: if (i == ARRAY_SIZE(ultrastor_ports)) { 226: # if (ULTRASTOR_DEBUG & UD_DETECT) 227: printk("US14F: detect: no port address found!\n"); 228: # endif 1.1 root 229: return FALSE; 230: } 1.1.1.2 root 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 1.1 root 237: 238: /* All above tests passed, must be the right thing. Get some useful 239: info. */ 1.1.1.2 root 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; 1.1 root 249: 250: /* To verify this card, we simply look for the UltraStor SCSI from the 251: BIOS version notice. */ 1.1.1.2 root 252: if (config.bios_segment != NULL) { 1.1 root 253: int found = 0; 254: 255: for (i = 0; !found && i < ARRAY_SIZE(signatures); i++) 1.1.1.2 root 256: if (memcmp((char *)config.bios_segment + signatures[i].offset, 1.1 root 257: signatures[i].signature, signatures[i].length)) 258: found = 1; 259: if (!found) 1.1.1.2 root 260: config.bios_segment = NULL; 1.1 root 261: } 1.1.1.2 root 262: if (!config.bios_segment) { 263: #if (ULTRASTOR_DEBUG & UD_DETECT) 264: printk("US14F: detect: not detected.\n"); 1.1 root 265: #endif 266: return FALSE; 267: } 268: 269: /* Final consistancy check, verify previous info. */ 1.1.1.2 root 270: if (!config.dma_channel || !(config_2.tfr_port & 0x2)) { 271: #if (ULTRASTOR_DEBUG & UD_DETECT) 272: printk("US14F: detect: consistancy check failed\n"); 1.1 root 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... */ 1.1.1.2 root 282: #if (ULTRASTOR_DEBUG & UD_DETECT) 283: printk("US14F: detect: detect succeeded\n" 284: " Port address: %03X\n" 1.1 root 285: " BIOS segment: %05X\n" 1.1.1.2 root 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); 1.1 root 291: #endif 292: host_number = hostnum; 1.1.1.2 root 293: scsi_hosts[hostnum].this_id = config.ha_scsi_id; 1.1.1.3 ! root 294: #ifndef NO_QUEUEING 1.1.1.2 root 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 1.1 root 301: return TRUE; 302: } 303: 304: const char *ultrastor_14f_info(void) 305: { 306: return "UltraStor 14F SCSI driver version " 307: VERSION 308: " by David B. Gentzel\n"; 309: } 310: 1.1.1.2 root 311: static struct mscp mscp = { 312: OP_SCSI, DTD_SCSI, FALSE, TRUE, FALSE /* This stuff doesn't change */ 313: }; 314: 1.1 root 315: int ultrastor_14f_queuecommand(unsigned char target, const void *cmnd, 316: void *buff, int bufflen, void (*done)(int, int)) 317: { 318: unsigned char in_byte; 319: 1.1.1.2 root 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); 1.1 root 331: memcpy(mscp.scsi_cdbs, cmnd, mscp.length_of_scsi_cdbs); 332: 333: /* Find free OGM slot (OGMINT bit is 0) */ 334: do 1.1.1.2 root 335: in_byte = inb_p(LCL_DOORBELL_INTR(PORT_ADDRESS)); 1.1 root 336: while (!aborted && (in_byte & 1)); 1.1.1.3 ! root 337: if (aborted) { ! 338: #if (ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT)) ! 339: printk("US14F: queuecommand: aborted\n"); ! 340: #endif 1.1 root 341: /* ??? is this right? */ 342: return (aborted << 16); 1.1.1.3 ! root 343: } 1.1 root 344: 345: /* Store pointer in OGM address bytes */ 1.1.1.2 root 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)); 1.1 root 350: 351: /* Issue OGM interrupt */ 1.1.1.2 root 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: 1.1.1.3 ! root 363: #ifdef NO_QUEUEING 1.1.1.2 root 364: int ultrastor_14f_command(unsigned char target, const void *cmnd, 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); 1.1 root 374: 375: /* Wait for ICM interrupt */ 376: do 1.1.1.2 root 377: in_byte = inb_p(SYS_DOORBELL_INTR(PORT_ADDRESS)); 1.1 root 378: while (!aborted && !(in_byte & 1)); 1.1.1.3 ! root 379: if (aborted) { ! 380: #if (ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT)) ! 381: printk("US14F: command: aborted\n"); ! 382: #endif 1.1 root 383: /* ??? is this right? */ 384: return (aborted << 16); 1.1.1.3 ! root 385: } 1.1 root 386: 387: /* Clean ICM slot (set ICMINT bit to 0) */ 1.1.1.2 root 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 1.1 root 394: 395: /* ??? not right, but okay for now? */ 396: return (mscp.adapter_status << 16) | mscp.target_status; 397: } 1.1.1.2 root 398: #endif 1.1 root 399: 400: int ultrastor_14f_abort(int code) 401: { 1.1.1.3 ! root 402: #if (ULTRASTOR_DEBUG & UD_ABORT) ! 403: printk("US14F: abort: called\n"); ! 404: #endif ! 405: 1.1 root 406: aborted = (code ? code : DID_ABORT); 1.1.1.3 ! root 407: ! 408: #if (ULTRASTOR_DEBUG & UD_ABORT) ! 409: printk("US14F: abort: returning\n"); ! 410: #endif ! 411: 1.1 root 412: return 0; 413: } 414: 415: int ultrastor_14f_reset(void) 416: { 417: unsigned char in_byte; 418: 1.1.1.2 root 419: #if (ULTRASTOR_DEBUG & UD_RESET) 420: printk("US14F: reset: called\n"); 1.1 root 421: #endif 422: 423: /* Issue SCSI BUS reset */ 1.1.1.2 root 424: outb_p(0x20, LCL_DOORBELL_INTR(PORT_ADDRESS)); 425: 1.1 root 426: /* Wait for completion... */ 427: do 1.1.1.2 root 428: in_byte = inb_p(LCL_DOORBELL_INTR(PORT_ADDRESS)); 1.1 root 429: while (in_byte & 0x20); 430: 431: aborted = DID_RESET; 432: 1.1.1.2 root 433: #if (ULTRASTOR_DEBUG & UD_RESET) 434: printk("US14F: reset: returning\n"); 1.1 root 435: #endif 436: return 0; 437: } 438: 1.1.1.3 ! root 439: #ifndef NO_QUEUEING 1.1.1.2 root 440: void ultrastor_interrupt_service(void) 441: { 1.1.1.3 ! root 442: #if (ULTRASTOR_DEBUG & UD_INTERRUPT) ! 443: printk("US14F: interrupt_service: called: status = %08X\n", 1.1.1.2 root 444: (mscp.adapter_status << 16) | mscp.target_status); 1.1.1.3 ! root 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 1.1.1.2 root 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: 1.1 root 503: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.