|
|
1.1 root 1: /* fdomain.c -- Future Domain TMC-1660/TMC-1680 driver
2: * Created: Sun May 3 18:53:19 1992
3: * Revised: Tue Jul 28 19:45:25 1992 by root
4: * Author: Rickard E. Faith, [email protected]
5: * Copyright 1992 Rickard E. Faith
6: *
7: * $Log$
8:
9: * This program is free software; you can redistribute it and/or modify it
10: * under the terms of the GNU General Public License as published by the
11: * Free Software Foundation; either version 2, or (at your option) any
12: * later version.
13:
14: * This program is distributed in the hope that it will be useful, but
15: * WITHOUT ANY WARRANTY; without even the implied warranty of
16: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17: * General Public License for more details.
18:
19: * WARNING: THIS IS A BETA VERSION!
20: * USE AT YOUR OWN RISK!
21: * BACKUP YOUR SYSTEM BEFORE USING!
22:
23: * I would like to thank Maxtor, whose *free* 206 page manual on the LXT
24: * drives was very helpful: "LXT SCSI Products: Specifications and OEM
25: * Technical Manual (Revision B/September 1991)"
26:
27: * I wish that I could thank Future Domain for the necessary documentation,
28: * but I can't. I used the $25 "TMC-1800 SCSI Chip Specification" document
29: * (FDC-1800T), which documents the *chip* and not the board. Without it,
30: * I would have been totally lost, but it would have been nice to have some
31: * example source. (The DOS BIOS source cost $250 and the UN*X driver
32: * source was $750 [both required a non-disclosure agreement]. Ever wonder
33: * why there are no freely available Future Domain drivers?)
34:
35: * Thanks to Todd Carrico ([email protected]), Dan Poirier
36: * ([email protected] ), Ken Corey ([email protected]), and C. de Bruin
37: * ([email protected]) for alpha testing. Also thanks to Drew
38: * Eckhardt ([email protected]) for answering questions. */
39:
40: #include <linux/config.h>
41:
42: #ifdef CONFIG_SCSI_FUTURE_DOMAIN
43:
44: #include <linux/sched.h>
45: #include <asm/io.h>
46: #include "fdomain.h"
47: #include "scsi.h"
48: #include "hosts.h"
49: #if QUEUE
50: #include <asm/system.h>
51: #include <linux/errno.h>
52: #endif
53:
54: #define VERSION "1.9" /* Change with each revision */
55: #define DEBUG 1 /* Enable debugging output */
56: #define SEND_IDENTIFY 0 /* Send IDENTIFY message -- DOESN'T WORK! */
57: #define USE_FIFO 1 /* Use the FIFO buffer for I/O */
58: #define FAST_SYNCH 1 /* Enable Fast Synchronous */
59: #define ALLOW_ALL_IRQ 0 /* Allow all IRQ's -- NOT RECOMMENDED */
60: #define NEW_IRQ 1 /* Enable new IRQ handling */
61: #define DECREASE_IL 1 /* Try to decrease interrupt latency */
62:
63: #if DEBUG
64: #define EVERY_ACCESS 0 /* Write a line on every scsi access */
65: #define ERRORS_ONLY 1 /* Only write a line if there is an error */
66: #define DEBUG_DETECT 0 /* Debug fdomain_16x0_detect() */
67: #else
68: #define EVERY_ACCESS 0 /* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */
69: #define ERRORS_ONLY 0
70: #define DEBUG_DETECT 0
71: #endif
72:
73: /* Errors are reported on the line, so we don't need to report them again */
74: #if EVERY_ACCESS
75: #undef ERRORS_ONLY
76: #define ERRORS_ONLY 0
77: #endif
78:
79: static int port_base = 0;
80: static void *bios_base = NULL;
81: static int interrupt_level = 0;
82: static volatile int aborted = 0;
83:
84: static int Data_Mode_Cntl_port;
85: static int FIFO_Data_Count_port;
86: static int Interrupt_Cntl_port;
87: static int Read_FIFO_port;
88: static int Read_SCSI_Data_port;
89: static int SCSI_Cntl_port;
90: static int SCSI_Status_port;
91: static int TMC_Cntl_port;
92: static int TMC_Status_port;
93: static int Write_FIFO_port;
94: static int Write_SCSI_Data_port;
95:
96: #if QUEUE
97: static unsigned char current_target = 0;
98: static unsigned char current_cmnd[10] = { 0, };
99: static void *current_buff = NULL;
100: static int current_bufflen = 0;
101: static void (*current_done)(int,int) = NULL;
102:
103: volatile static int in_command = 0;
104: volatile static int current_phase;
105: static int this_host = 0;
106:
107: enum { in_arbitration, in_selection, in_other };
108:
109: #if NEW_IRQ
110: extern void fdomain_16x0_intr( int unused );
111: #else
112: extern void fdomain_16x0_interrupt();
113: #endif
114:
115: static const char *cmd_pt;
116: static const char *the_command;
117: static unsigned char *out_buf_pt;
118: static unsigned char *in_buf_pt;
119: volatile static int Status;
120: volatile static int Message;
121: volatile static unsigned data_sent;
122: volatile static int have_data_in;
123:
124: volatile static int in_interrupt_code = 0;
125:
126: #endif
127:
128:
129: enum in_port_type { Read_SCSI_Data = 0, SCSI_Status = 1, TMC_Status = 2,
130: LSB_ID_Code = 5, MSB_ID_Code = 6, Read_Loopback = 7,
131: SCSI_Data_NoACK = 8, Option_Select = 10,
132: Read_FIFO = 12, FIFO_Data_Count = 14 };
133:
134: enum out_port_type { Write_SCSI_Data = 0, SCSI_Cntl = 1, Interrupt_Cntl = 2,
135: Data_Mode_Cntl = 3, TMC_Cntl = 4, Write_Loopback = 7,
136: Write_FIFO = 12 };
137:
138: static void *addresses[] = {
139: (void *)0xc8000,
140: (void *)0xca000,
141: (void *)0xce000,
142: (void *)0xde000 };
143: #define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned ))
144:
145: static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
146: #define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short ))
147:
148: static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
149:
150: /*
151:
152: READ THIS BEFORE YOU ADD A SIGNATURE!
153:
154: READING THIS SHORT NOTE CAN SAVE YOU LOTS OF TIME!
155:
156: READ EVERY WORD, ESPECIALLY THE WORD *NOT*
157:
158: This driver works *ONLY* for Future Domain cards using the
159: TMC-1600 chip. This includes models TMC-1660 and TMC-1680
160: *ONLY*.
161:
162: The following is a BIOS signature for a TMC-950 board, which
163: looks like it is a 16 bit board (based on card edge), but
164: which only uses the extra lines for IRQ's (not for data):
165:
166: FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90
167:
168: THIS WILL *NOT* WORK WITH THIS DRIVER!
169:
170: Here is another BIOS signature for yet another Future
171: Domain board WHICH WILL *NOT* WORK WITH THIS DRIVER:
172:
173: FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90
174:
175: Here is another BIOS signature for the TMC-88x series:
176:
177: FUTURE DOMAIN COPR. (C) 1986-1989 V6.0A7/28/90
178:
179: THIS WILL *NOT* WORK WITH THIS DRIVER, but it *WILL*
180: work with the *SEAGATE* ST-01/ST-02 driver.
181:
182: */
183:
184: struct signature {
185: char *signature;
186: int sig_offset;
187: int sig_length;
188: } signatures[] = {
189: { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.0 7/28/89", 5, 50 },
190: { "FUTURE DOMAIN CORP. (C) 1986-1990 1800", 5, 37 },
191: /* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGANTURE */
192: };
193:
194: #define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature ))
195:
196:
197: /* These functions are based on include/asm/io.h */
198:
199: #if 1
200: static unsigned short inline inw( unsigned short port )
201: {
202: unsigned short _v;
203:
204: __asm__ volatile ("inw %1,%0"
205: :"=a" (_v):"d" ((unsigned short) port));
206: return _v;
207: }
208:
209: static void inline outw( unsigned short value, unsigned short port )
210: {
211: __asm__ volatile ("outw %0,%1"
212: ::"a" ((unsigned short) value),
213: "d" ((unsigned short) port));
214: }
215: #else
216:
217: #define inw( port ) \
218: ({ unsigned short _v; \
219: __asm__ volatile ("inw %1,%0" \
220: : "=a" (_v) : "d" ((unsigned short) port)); \
221: _v; })
222:
223: #define outw( value ) \
224: __asm__ volatile \
225: ("outw %0,%1" : : "a" ((unsigned short) value), \
226: "d" ((unsigned short) port))
227: #endif
228:
229:
230: /* These defines are copied from kernel/blk_drv/hd.c */
231:
232: #define insw( buf, count, port ) \
233: __asm__ volatile \
234: ( "cld;rep;insw"::"d" (port),"D" (buf),"c" (count):"cx","di" )
235:
236: #define outsw( buf, count, port) \
237: __asm__ volatile \
238: ("cld;rep;outsw"::"d" (port),"S" (buf),"c" (count):"cx","si")
239:
240:
241: static void do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */
242: {
243: unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */
244:
245: while (jiffies < the_time);
246: }
247:
248: static void inline fdomain_make_bus_idle( void )
249: {
250: outb( 0, SCSI_Cntl_port );
251: outb( 0, Data_Mode_Cntl_port );
252: outb( 1, TMC_Cntl_port );
253: }
254:
255: static int fdomain_is_valid_port( int port )
256: {
257: int options;
258:
259: #if DEBUG_DETECT
260: printk( " (%x%x),",
261: inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) );
262: #endif
263:
264: /* The MCA ID is a unique id for each MCA compatible board. We
265: are using ISA boards, but Future Domain provides the MCA ID
266: anyway. We can use this ID to ensure that this is a Future
267: Domain TMC-1660/TMC-1680.
268: */
269:
270: if (inb( port + LSB_ID_Code ) != 0xe9) { /* test for 0x6127 id */
271: if (inb( port + LSB_ID_Code ) != 0x27) return 0;
272: if (inb( port + MSB_ID_Code ) != 0x61) return 0;
273: } else { /* test for 0xe960 id */
274: if (inb( port + MSB_ID_Code ) != 0x60) return 0;
275: }
276:
277: /* We have a valid MCA ID for a TMC-1660/TMC-1680 Future Domain board.
278: Now, check to be sure the bios_base matches these ports.
279: If someone was unlucky enough to have purchased more than one
280: Future Domain board, then they will have to modify this code, as
281: we only detect one board here. [The one with the lowest bios_base].
282: */
283:
284: options = inb( port + Option_Select );
285:
286: #if DEBUG_DETECT
287: printk( " Options = %x,", options );
288: #endif
289:
290: if (addresses[ (options & 0xc0) >> 6 ] != bios_base) return 0;
291: interrupt_level = ints[ (options & 0x0e) >> 1 ];
292:
293: return 1;
294: }
295:
296: static int fdomain_test_loopback( void )
297: {
298: int i;
299: int result;
300:
301: for (i = 0; i < 255; i++) {
302: outb( i, port_base + Write_Loopback );
303: result = inb( port_base + Read_Loopback );
304: if (i != result) return 1;
305: }
306: return 0;
307: }
308:
309: #if !NEW_IRQ
310: static void fdomain_enable_interrupt( void )
311: {
312: if (!interrupt_level) return;
313:
314: #if ALLOW_ALL_IRQ
315: if (interrupt_level < 8) {
316: outb( inb_p( 0x21 ) & ~(1 << interrupt_level), 0x21 );
317: } else
318: #endif
319: {
320: outb( inb_p( 0xa1 ) & ~(1 << (interrupt_level - 8)), 0xa1 );
321: }
322: }
323:
324: static void fdomain_disable_interrupt( void )
325: {
326: if (!interrupt_level) return;
327:
328: #if ALLOW_ALL_IRQ
329: if (interrupt_level < 8) {
330: outb( inb_p( 0x21 ) | (1 << interrupt_level), 0x21 );
331: } else
332: #endif
333: {
334: outb( inb_p( 0xa1 ) | (1 << (interrupt_level - 8)), 0xa1 );
335: }
336: }
337: #endif
338:
339: int fdomain_16x0_detect( int hostnum )
340: {
341: int i, j;
342: int flag;
343: unsigned char do_inquiry[] = { 0x12, 0, 0, 0, 255, 0 };
344: unsigned char do_request_sense[] = { 0x03, 0, 0, 0, 255, 0 };
345: unsigned char do_read_capacity[] = { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
346: unsigned char buf[256];
347: unsigned retcode;
348:
349: #if DEBUG_DETECT
350: printk( "SCSI: fdomain_16x0_detect()," );
351: #endif
352:
353: for (i = 0; !bios_base && i < ADDRESS_COUNT; i++) {
354: #if DEBUG_DETECT
355: printk( " %x(%x),", (unsigned)addresses[i], (unsigned)bios_base );
356: #endif
357: for (j = 0; !bios_base && j < SIGNATURE_COUNT; j++) {
358: if (!memcmp( ((char *)addresses[i] + signatures[j].sig_offset),
359: signatures[j].signature, signatures[j].sig_length )) {
360: bios_base = addresses[i];
361: }
362: }
363: }
364:
365: if (!bios_base) {
366: #if DEBUG_DETECT
367: printk( " FAILED: NO BIOS\n" );
368: #endif
369: return 0;
370: }
371:
372: /* The TMC-1660/TMC-1680 has a RAM area just after the BIOS ROM.
373: Assuming the ROM is enabled (otherwise we wouldn't have been
374: able to read the ROM signature :-), then the ROM set up the
375: RAM area with some magic numbers, such as a list of port
376: base addresses and a list of the disk "geometry" reported to
377: DOS (this geometry has nothing to do with physical geometry).
378: */
379:
380: port_base = *((char *)bios_base + 0x1fcc)
381: + (*((char *)bios_base + 0x1fcd) << 8);
382:
383: #if DEBUG_DETECT
384: printk( " %x,", port_base );
385: #endif
386:
387: for (flag = 0, i = 0; !flag && i < PORT_COUNT; i++) {
388: if (port_base == ports[i]) ++flag;
389: }
390:
391: if (flag) flag = fdomain_is_valid_port( port_base );
392:
393: if (!flag) { /* Cannot get port base from BIOS RAM */
394:
395: /* This is a bad sign. It usually means that someone patched the
396: BIOS signature list (the signatures variable) to contain a BIOS
397: signature for a board *OTHER THAN* the TMC-1660/TMC-1680.
398: */
399:
400: #if DEBUG_DETECT
401: printk( " RAM FAILED, " );
402: #endif
403: /* Anyway, the alternative to finding the address in the RAM is
404: to just search through every possible port address for one
405: that is attached to the Future Domain card. Don't panic,
406: though, about reading all these random port addresses--there
407: are rumors that the Future Domain BIOS does something very
408: similar.
409: */
410:
411: for (flag = 0, i = 0; !flag && i < PORT_COUNT; i++) {
412: port_base = ports[i];
413: #if DEBUG_DETECT
414: printk( " %x,", port_base );
415: #endif
416: flag = fdomain_is_valid_port( port_base );
417: }
418: }
419:
420: if (!flag) {
421: #if DEBUG_DETECT
422: printk( " FAILED: NO PORT\n" );
423: #endif
424: return 0; /* Cannot find valid set of ports */
425: }
426:
427: #if DEBUG_DETECT
428: printk( "\n" );
429: printk( "SCSI: bios_base = %x, port_base = %x, interrupt_level = %d\n",
430: (unsigned)bios_base, port_base, interrupt_level );
431: #endif
432:
433: if (interrupt_level) {
434: printk( "Future Domain BIOS at %x; port base at %x; using IRQ %d\n",
435: (unsigned)bios_base, port_base, interrupt_level );
436: } else {
437: printk( "Future Domain BIOS at %x; port base at %x; *NO* IRQ\n",
438: (unsigned)bios_base, port_base );
439: }
440:
441: Data_Mode_Cntl_port = port_base + Data_Mode_Cntl;
442: FIFO_Data_Count_port = port_base + FIFO_Data_Count;
443: Interrupt_Cntl_port = port_base + Interrupt_Cntl;
444: Read_FIFO_port = port_base + Read_FIFO;
445: Read_SCSI_Data_port = port_base + Read_SCSI_Data;
446: SCSI_Cntl_port = port_base + SCSI_Cntl;
447: SCSI_Status_port = port_base + SCSI_Status;
448: TMC_Cntl_port = port_base + TMC_Cntl;
449: TMC_Status_port = port_base + TMC_Status;
450: Write_FIFO_port = port_base + Write_FIFO;
451: Write_SCSI_Data_port = port_base + Write_SCSI_Data;
452:
453: fdomain_16x0_reset();
454:
455: if (fdomain_test_loopback()) {
456: #if DEBUG_DETECT
457: printk( "SCSI: LOOPBACK TEST FAILED, FAILING DETECT!\n" );
458: #endif
459: return 0;
460: }
461:
462: /* These routines are here because of the way the SCSI bus behaves
463: after a reset. This appropriate behavior was not handled correctly
464: by the higher level SCSI routines when I first wrote this driver.
465: */
466:
467: printk( "Future Domain detection routine scanning for devices:\n" );
468: for (i = 0; i < 8; i++) {
469: if (i == 6) continue; /* The host adapter is at SCSI ID 6 */
470: retcode = fdomain_16x0_command( i, do_request_sense, buf, 255 );
471: if (!retcode) {
472: retcode = fdomain_16x0_command( i, do_inquiry, buf, 255 );
473: if (!retcode) {
474: printk( " SCSI ID %d: ", i );
475: for (j = 8; j < 32; j++) printk( "%c", buf[j] );
476: retcode = fdomain_16x0_command( i, do_read_capacity, buf, 255 );
477: if (!retcode) {
478: unsigned long blocks, size, capacity;
479:
480: blocks = (buf[0] << 24) | (buf[1] << 16)
481: | (buf[2] << 8) | buf[3];
482: size = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
483: capacity = +(blocks * size * 10) / +(1024L * 1024L);
484:
485: printk( "%lu MB (%lu byte blocks)",
486: ((capacity + 5L) / 10L), size );
487: }
488: printk ("\n" );
489: }
490: }
491: }
492:
493: #if QUEUE
494: #if !ALLOW_ALL_IRQ
495: if (interrupt_level < 8) {
496: printk( "Future Domain: WILL NOT USE IRQ LESS THAN 8 FOR QUEUEING!\n" );
497: scsi_hosts[hostnum].can_queue = 0;
498: } else
499: #endif
500: #if NEW_IRQ
501: {
502: int retcode;
503: struct sigaction sa;
504:
505: this_host = hostnum;
506:
507: sa.sa_handler = fdomain_16x0_intr;
508: sa.sa_flags = SA_INTERRUPT;
509: sa.sa_mask = 0;
510: sa.sa_restorer = NULL;
511:
512: retcode = irqaction( interrupt_level, &sa );
513:
514: if (retcode < 0) {
515: if (retcode == -EINVAL) {
516: printk( "Future Domain: IRQ %d is bad!\n", interrupt_level );
517: printk( " This shouldn't happen: REPORT TO RIK!\n" );
518: } else if (retcode == -EBUSY) {
519: printk( "Future Domain: IRQ %d is already in use!\n",
520: interrupt_level );
521: printk( " Please use another IRQ for the FD card!\n" );
522: } else {
523: printk( "Future Domain: Error getting IRQ %d\n",
524: interrupt_level );
525: printk( " This shouldn't happen: REPORT TO RIK!\n" );
526: }
527: printk( " IRQs WILL NOT BE USED!\n" );
528:
529: scsi_hosts[this_host].can_queue = 0;
530: } else {
531: printk( "Future Domain: IRQ %d selected with retcode = %d\n",
532: interrupt_level, retcode );
533: }
534: }
535: #else
536: {
537: this_host = hostnum;
538: set_intr_gate( 0x20 + interrupt_level, &fdomain_16x0_interrupt );
539: fdomain_enable_interrupt();
540: }
541: #endif
542: #endif
543:
544: return 1;
545: }
546:
547: char *fdomain_16x0_info(void)
548: {
549: static char buffer[] =
550: "Future Domain TMC-1660/TMC-1680 SCSI driver version "
551: VERSION
552: "\n";
553: return buffer;
554: }
555:
556: static int fdomain_arbitrate( void )
557: {
558: int status = 0;
559: unsigned long timeout;
560:
561: #if VERBOSE
562: printk( "SCSI: fdomain_arbitrate()\n" );
563: #endif
564:
565: outb( 0x00, SCSI_Cntl_port ); /* Disable data drivers */
566: outb( 0x40, port_base + SCSI_Data_NoACK ); /* Set our id bit */
567: outb( 0x04, TMC_Cntl_port ); /* Start arbitration */
568:
569: timeout = jiffies + 50; /* 500 mS */
570: while (jiffies < timeout) {
571: status = inb( TMC_Status_port ); /* Read adapter status */
572: if (status & 0x02) return 0; /* Arbitration complete */
573: }
574:
575: /* Make bus idle */
576: fdomain_make_bus_idle();
577:
578: #if EVERY_ACCESS
579: printk( "Arbitration failed, status = %x\n", status );
580: #endif
581: #if ERRORS_ONLY
582: printk( "SCSI: Arbitration failed, status = %x", status );
583: #endif
584: return 1;
585: }
586:
587: static int fdomain_select( int target )
588: {
589: int status;
590: unsigned long timeout;
591:
592: outb( 0x80, SCSI_Cntl_port ); /* Bus Enable */
593: outb( 0x8a, SCSI_Cntl_port ); /* Bus Enable + Attention + Select */
594:
595: /* Send our address OR'd with target address */
596: #if SEND_IDENTIFY
597: outb( 0x40 | (1 << target), port_base + SCSI_Data_NoACK );
598: #else
599: outb( (1 << target), port_base + SCSI_Data_NoACK );
600: #endif
601:
602: /* Stop arbitration (also set FIFO for output and enable parity) */
603: outb( 0xc8, TMC_Cntl_port );
604:
605: timeout = jiffies + 25; /* 250mS */
606: while (jiffies < timeout) {
607: status = inb( SCSI_Status_port ); /* Read adapter status */
608: if (status & 1) { /* Busy asserted */
609: /* Enable SCSI Bus (on error, should make bus idle with 0) */
610: #if SEND_IDENTIFY
611: /* Also, set ATN so that the drive will make a MESSAGE OUT phase */
612: outb( 0x88, SCSI_Cntl_port );
613: #else
614: outb( 0x80, SCSI_Cntl_port );
615: #endif
616: return 0;
617: }
618: }
619: /* Make bus idle */
620: fdomain_make_bus_idle();
621: #if EVERY_ACCESS
622: if (!target) printk( "Select failed\n" );
623: #endif
624: #if ERRORS_ONLY
625: if (!target) printk( "SCSI: Select failed" );
626: #endif
627: return 1;
628: }
629:
630: #if QUEUE
631:
632: #if !USE_FIFO
633: #pragma error QUEUE requires USE_FIFO
634: #endif
635:
636: void my_done( int error )
637: {
638: if (in_command) {
639: in_command = 0;
640: in_interrupt_code = 0;
641: outb( 0x00, Interrupt_Cntl_port );
642: fdomain_make_bus_idle();
643: if (current_done) current_done( this_host, error );
644: else panic( "SCSI (Future Domain): current_done() == NULL" );
645: } else {
646: panic( "SCSI (Future Domain): my_done() called outside of command\n" );
647: }
648: }
649:
650: #if NEW_IRQ
651: void fdomain_16x0_intr( int unused )
652: #else
653: void fdomain_16x0_intr( void )
654: #endif
655: {
656: int status;
657: int done = 0;
658: unsigned data_count;
659:
660: #if NEW_IRQ
661: sti();
662: #endif
663:
664: if (in_interrupt_code)
665: panic( "SCSI (Future Domain): fdomain_16x0_intr() NOT REENTRANT!\n" );
666: else
667: ++in_interrupt_code;
668:
669: outb( 0x00, Interrupt_Cntl_port );
670:
671: #if EVERY_ACCESS
672: printk( "aborted = %d, ", aborted );
673: #endif
674:
675: if (aborted) {
676: /* Force retry for timeouts after selection complete */
677: if (current_phase == in_other)
678: my_done( DID_BUS_BUSY << 16 );
679: else
680: my_done( aborted << 16 );
681: #if NEW_IRQ && !DECREASE_IL
682: cli();
683: #endif
684: return;
685: }
686:
687: /* We usually have one spurious interrupt after each command. Ignore it. */
688: if (!in_command) { /* Spurious interrupt */
689: in_interrupt_code = 0;
690: #if NEW_IRQ && !DECREASE_IL
691: cli();
692: #endif
693: return;
694: }
695:
696: if (current_phase == in_arbitration) {
697: status = inb( TMC_Status_port ); /* Read adapter status */
698: if (!(status & 0x02)) {
699: #if EVERY_ACCESS
700: printk( " AFAIL " );
701: #endif
702: my_done( DID_TIME_OUT << 16 );
703: #if NEW_IRQ && !DECREASE_IL
704: cli();
705: #endif
706: return;
707: }
708: current_phase = in_selection;
709:
710: outb( 0x80, SCSI_Cntl_port ); /* Bus Enable */
711: outb( 0x8a, SCSI_Cntl_port ); /* Bus Enable + Attention + Select */
712:
713: outb( (1 << current_target), port_base + SCSI_Data_NoACK );
714:
715: outb( 0x40, Interrupt_Cntl_port );
716: /* Stop arbitration (also set FIFO for output and enable parity) */
717: in_interrupt_code = 0;
718: outb( 0xd8, TMC_Cntl_port );
719: #if NEW_IRQ && !DECREASE_IL
720: cli();
721: #endif
722: return;
723: } else if (current_phase == in_selection) {
724: status = inb( SCSI_Status_port );
725: if (!(status & 0x01)) {
726: #if EVERY_ACCESS
727: printk( " SFAIL " );
728: #endif
729: my_done( DID_NO_CONNECT << 16 );
730: #if NEW_IRQ && !DECREASE_IL
731: cli();
732: #endif
733: return;
734: }
735: current_phase = in_other;
736: #if FAST_SYNCH
737: outb( 0xc0, Data_Mode_Cntl_port );
738: #endif
739: in_interrupt_code = 0;
740: outb( 0x90, Interrupt_Cntl_port );
741: outb( 0x80, SCSI_Cntl_port );
742: #if NEW_IRQ && !DECREASE_IL
743: cli();
744: #endif
745: return;
746: }
747:
748: /* current_phase == in_other: this is the body of the routine */
749:
750: switch ((unsigned char)*the_command) {
751: case 0x04: case 0x07: case 0x0a: case 0x15: case 0x2a:
752: case 0x2e: case 0x3b: case 0xea: case 0x3f:
753: data_count = 0x2000 - inw( FIFO_Data_Count_port );
754: if (current_bufflen - data_sent < data_count)
755: data_count = current_bufflen - data_sent;
756: if (data_count > 0) {
757: /* if (data_count > 512) data_count = 512; */
758: #if EVERY_ACCESS
759: printk( "%d OUT, ", data_count );
760: #endif
761: if (data_count == 1) {
762: outb( *out_buf_pt++, Write_FIFO_port );
763: ++data_sent;
764: } else {
765: data_count >>= 1;
766: outsw( out_buf_pt, data_count, Write_FIFO_port );
767: out_buf_pt += 2 * data_count;
768: data_sent += 2 * data_count;
769: }
770: }
771: break;
772: default:
773: if (!have_data_in) {
774: outb( 0x98, TMC_Cntl_port );
775: ++have_data_in;
776: } else {
777: data_count = inw( FIFO_Data_Count_port );
778: /* if (data_count > 512) data_count = 512; */
779: if (data_count) {
780: #if EVERY_ACCESS
781: printk( "%d IN, ", data_count );
782: #endif
783: if (data_count == 1) {
784: *in_buf_pt++ = inb( Read_FIFO_port );
785: } else {
786: data_count >>= 1; /* Number of words */
787: insw( in_buf_pt, data_count, Read_FIFO_port );
788: in_buf_pt += 2 * data_count;
789: }
790: }
791: }
792: break;
793: }
794:
795: status = inb( SCSI_Status_port );
796:
797: if (status & 0x10) { /* REQ */
798:
799: switch (status & 0x0e) {
800: case 0x08: /* COMMAND OUT */
801: outb( *cmd_pt++, Write_SCSI_Data_port );
802: #if EVERY_ACCESS
803: printk( "CMD = %x,", (unsigned char)cmd_pt[-1] );
804: #endif
805: break;
806: case 0x0c: /* STATUS IN */
807: Status = inb( Read_SCSI_Data_port );
808: #if EVERY_ACCESS
809: printk( "Status = %x, ", Status );
810: #endif
811: #if ERRORS_ONLY
812: if (Status) {
813: printk( "SCSI: target = %d, command = %x, Status = %x\n",
814: current_target, (unsigned char)*the_command, Status );
815: }
816: #endif
817: break;
818: case 0x0a: /* MESSAGE OUT */
819: #if SEND_IDENTIFY
820: /* On the first request, send an Identify message */
821: if (!sent_identify) {
822: outb( 0x80, SCSI_Cntl_port ); /* Lower ATN */
823: outb( 0x80, Write_SCSI_Data_port ); /* Identify */
824: ++sent_identify;
825: } else
826: #else
827: outb( 0x07, Write_SCSI_Data_port ); /* Reject */
828: #endif
829: break;
830: case 0x0e: /* MESSAGE IN */
831: Message = inb( Read_SCSI_Data_port );
832: #if EVERY_ACCESS
833: printk( "Message = %x, ", Message );
834: #endif
835: if (!Message) ++done;
836: break;
837: }
838: }
839:
840: if (done) {
841: #if EVERY_ACCESS
842: printk( " ** IN DONE ** " );
843: #endif
844:
845: if (have_data_in) {
846: while (data_count = inw( FIFO_Data_Count_port )) {
847: if (data_count == 1) {
848: *in_buf_pt++ = inb( Read_FIFO_port );
849: } else {
850: data_count >>= 1; /* Number of words */
851: insw( in_buf_pt, data_count, Read_FIFO_port );
852: in_buf_pt += 2 * data_count;
853: }
854: }
855: }
856: #if EVERY_ACCESS
857: printk( "AFTER DATA GET\n" );
858: #endif
859:
860: #if ERRORS_ONLY
861: if (*the_command == REQUEST_SENSE && !Status) {
862: if ((unsigned char)(*((char *)current_buff + 2)) & 0x0f) {
863: printk( "SCSI REQUEST SENSE: Sense Key = %x, Sense Code = %x\n",
864: (unsigned char)(*((char *)current_buff + 2)) & 0x0f,
865: (unsigned char)(*((char *)current_buff + 12)) );
866: }
867: }
868: #endif
869: #if EVERY_ACCESS
870: printk( "BEFORE MY_DONE\n" );
871: #endif
872: my_done( (Status & 0xff) | ((Message & 0xff) << 8) | (DID_OK << 16) );
873: } else {
874: in_interrupt_code = 0;
875: outb( 0x90, Interrupt_Cntl_port );
876: }
877:
878: #if NEW_IRQ && !DECREASE_IL
879: cli();
880: #endif
881: return;
882: }
883:
884: int fdomain_16x0_queue( unsigned char target, const void *cmnd,
885: void *buff, int bufflen, void (*done)(int,int) )
886: {
887: if (in_command) {
888: panic( "SCSI (Future Domain): fdomain_16x0_queue() NOT REENTRANT!\n" );
889: }
890: #if EVERY_ACCESS
891: printk( "queue %d %x\n", target, *(unsigned char *)cmnd );
892: #endif
893:
894: fdomain_make_bus_idle();
895:
896: aborted = 0;
897: current_target = target;
898: memcpy( current_cmnd, cmnd, ((*(unsigned char *)cmnd) <= 0x1f ? 6 : 10 ) );
899: current_buff = buff;
900: current_bufflen = bufflen;
901: current_done = done;
902:
903: /* Initialize static data */
904: cmd_pt = current_cmnd;
905: the_command = current_cmnd;
906: out_buf_pt = current_buff;
907: in_buf_pt = current_buff;
908:
909: Status = 0;
910: Message = 0;
911: data_sent = 0;
912: have_data_in = 0;
913:
914: /* Start arbitration */
915: current_phase = in_arbitration;
916: outb( 0x00, Interrupt_Cntl_port );
917: outb( 0x00, SCSI_Cntl_port ); /* Disable data drivers */
918: outb( 0x40, port_base + SCSI_Data_NoACK ); /* Set our id bit */
919: ++in_command;
920: outb( 0x20, Interrupt_Cntl_port );
921: outb( 0x1c, TMC_Cntl_port ); /* Start arbitration */
922:
923: return 0;
924: }
925: #endif
926:
927: int fdomain_16x0_command( unsigned char target, const void *cmnd,
928: void *buff, int bufflen )
929: {
930: const char *cmd_pt = cmnd;
931: const char *the_command = cmnd;
932: unsigned char *out_buf_pt = buff;
933: unsigned char *in_buf_pt = buff;
934: int Status = 0;
935: int Message = 0;
936: int status;
937: int done = 0;
938: unsigned long timeout;
939: unsigned data_sent = 0;
940: unsigned data_count;
941: #if USE_FIFO
942: int have_data_in = 0;
943: #endif
944: #if SEND_IDENTITY
945: int sent_identify = 0;
946: #endif
947:
948: #if EVERY_ACCESS
949: printk( "fdomain_command(%d, %x): ", target, (unsigned char)*the_command );
950: #endif
951:
952: if (fdomain_arbitrate()) {
953: #if ERRORS_ONLY
954: printk( ", target = %d, command = %x\n",
955: target, (unsigned char)*the_command );
956: #endif
957: return DID_TIME_OUT << 16;
958: }
959:
960: if (fdomain_select( target )) {
961: #if ERRORS_ONLY
962: if (!target) printk( ", target = %d, command = %x\n",
963: target, (unsigned char)*the_command );
964: #endif
965: return DID_NO_CONNECT << 16;
966: }
967:
968: timeout = jiffies + 500; /* 5000 mS -- For Maxtor after a RST */
969: aborted = 0; /* How is this supposed to get reset??? */
970:
971: #if FAST_SYNCH
972: outb( 0xc0, Data_Mode_Cntl_port );
973: #endif
974:
975: #if USE_FIFO
976: switch ((unsigned char)*the_command) {
977: case 0x04: case 0x07: case 0x0a: case 0x15: case 0x2a:
978: case 0x2e: case 0x3b: case 0xea: case 0x3f:
979: data_count = 0x2000 - inw( FIFO_Data_Count_port );
980: if (bufflen - data_sent < data_count)
981: data_count = bufflen - data_sent;
982: if (data_count == 1) {
983: outb( *out_buf_pt++, Write_FIFO_port );
984: ++data_sent;
985: } else {
986: data_count >>= 1;
987: outsw( out_buf_pt, data_count, Write_FIFO_port );
988: out_buf_pt += 2 * data_count;
989: data_sent += 2 * data_count;
990: }
991: break;
992: default:
993: outb( 0x88, TMC_Cntl_port );
994: ++have_data_in;
995: break;
996: }
997: #endif
998:
999: while (((status = inb( SCSI_Status_port )) & 1)
1000: && !done && !aborted && jiffies < timeout) {
1001:
1002: if (status & 0x10) { /* REQ */
1003:
1004: switch (status & 0x0e) {
1005: case 0x00: /* DATA OUT */
1006: #if USE_FIFO
1007: data_count = 0x2000 - inw( FIFO_Data_Count_port );
1008: if (bufflen - data_sent < data_count)
1009: data_count = bufflen - data_sent;
1010: if (data_count == 1) {
1011: outb( *out_buf_pt++, Write_FIFO_port );
1012: ++data_sent;
1013: } else {
1014: data_count >>= 1;
1015: outsw( out_buf_pt, data_count, Write_FIFO_port );
1016: out_buf_pt += 2 * data_count;
1017: data_sent += 2 * data_count;
1018: }
1019: #else
1020: outb( *out_buf_pt++, Write_SCSI_Data_port );
1021: #endif
1022: break;
1023: case 0x04: /* DATA IN */
1024: #if USE_FIFO
1025: if (!have_data_in) {
1026: outb( 0x88, TMC_Cntl_port );
1027: ++have_data_in;
1028: }
1029: data_count = inw( FIFO_Data_Count_port );
1030: if (data_count == 1) {
1031: *in_buf_pt++ = inb( Read_FIFO_port );
1032: } else {
1033: data_count >>= 1; /* Number of words */
1034: insw( in_buf_pt, data_count, Read_FIFO_port );
1035: in_buf_pt += 2 * data_count;
1036: }
1037: #else
1038: *in_buf_pt++ = inb( Read_SCSI_Data_port );
1039: #endif
1040: break;
1041: case 0x08: /* COMMAND OUT */
1042: outb( *cmd_pt++, Write_SCSI_Data_port );
1043: #if EVERY_ACCESS
1044: printk( "%x,", (unsigned char)cmd_pt[-1] );
1045: #endif
1046: break;
1047: case 0x0c: /* STATUS IN */
1048: Status = inb( Read_SCSI_Data_port );
1049: #if EVERY_ACCESS
1050: printk( "Status = %x, ", Status );
1051: #endif
1052: #if ERRORS_ONLY
1053: if (Status) {
1054: printk( "SCSI: target = %d, command = %x, Status = %x\n",
1055: target, (unsigned char)*the_command, Status );
1056: }
1057: #endif
1058: break;
1059: case 0x0a: /* MESSAGE OUT */
1060: #if SEND_IDENTIFY
1061: /* On the first request, send an Identify message */
1062: if (!sent_identify) {
1063: outb( 0x80, SCSI_Cntl_port ); /* Lower ATN */
1064: outb( 0x80, Write_SCSI_Data_port ); /* Identify */
1065: ++sent_identify;
1066: } else
1067: #else
1068: outb( 0x07, Write_SCSI_Data_port ); /* Reject */
1069: #endif
1070: break;
1071: case 0x0e: /* MESSAGE IN */
1072: Message = inb( Read_SCSI_Data_port );
1073: #if EVERY_ACCESS
1074: printk( "Message = %x, ", Message );
1075: #endif
1076: if (!Message) ++done;
1077: break;
1078: }
1079: }
1080: }
1081:
1082: if (jiffies >= timeout) {
1083: #if EVERY_ACCESS
1084: printk( "Time out, status = %x\n", status );
1085: #endif
1086: #if ERRORS_ONLY
1087: printk( "SCSI: Time out, status = %x (target = %d, command = %x)\n",
1088: status, target, (unsigned char)*the_command );
1089: #endif
1090: fdomain_make_bus_idle();
1091: return DID_BUS_BUSY << 16;
1092: }
1093:
1094: if (aborted) {
1095: #if EVERY_ACCESS
1096: printk( "Aborted\n" );
1097: #endif
1098: #if ONLY_ERRORS
1099: printk( "SCSI: Aborted (command = %x)\n", (unsigned char)*the_command );
1100: #endif
1101: fdomain_16x0_reset();
1102: return DID_ABORT << 16;
1103: }
1104:
1105: #if USE_FIFO
1106: if (have_data_in) {
1107: while (data_count = inw( FIFO_Data_Count_port )) {
1108: if (data_count == 1) {
1109: *in_buf_pt++ = inb( Read_FIFO_port );
1110: } else {
1111: data_count >>= 1; /* Number of words */
1112: insw( in_buf_pt, data_count, Read_FIFO_port );
1113: in_buf_pt += 2 * data_count;
1114: }
1115: }
1116: }
1117: #endif
1118:
1119: fdomain_make_bus_idle();
1120:
1121: #if EVERY_ACCESS
1122: printk( "Retcode = %x\n",
1123: (Status & 0xff) | ((Message & 0xff) << 8) | (DID_OK << 16) );
1124: #endif
1125: #if ERRORS_ONLY
1126: if (*the_command == REQUEST_SENSE && !Status) {
1127: if ((unsigned char)(*((char *)buff + 2)) & 0x0f) {
1128: printk( "SCSI REQUEST SENSE: Sense Key = %x, Sense Code = %x\n",
1129: (unsigned char)(*((char *)buff + 2)) & 0x0f,
1130: (unsigned char)(*((char *)buff + 12)) );
1131: }
1132: }
1133: #endif
1134:
1135: return (Status & 0xff) | ((Message & 0xff) << 8) | (DID_OK << 16);
1136: }
1137:
1138: int fdomain_16x0_abort( int code )
1139: {
1140:
1141: #if EVERY_ACCESS
1142: printk( " ABORT " );
1143: #endif
1144:
1145: #if QUEUE
1146: cli();
1147: if (!in_command) {
1148: sti();
1149: return 0;
1150: }
1151:
1152: aborted = code ? code : DID_ABORT;
1153:
1154: sti();
1155: fdomain_make_bus_idle();
1156: #else
1157: aborted = code ? code : DID_ABORT;
1158: #endif
1159:
1160: return 0;
1161: }
1162:
1163: int fdomain_16x0_reset( void )
1164: {
1165: outb( 1, SCSI_Cntl_port );
1166: do_pause( 2 );
1167: outb( 0, SCSI_Cntl_port );
1168: do_pause( 115 );
1169: outb( 0, Data_Mode_Cntl_port );
1170: outb( 0, TMC_Cntl_port );
1171:
1172: aborted = DID_RESET;
1173:
1174: return 0;
1175: }
1176:
1177: #if QUEUE && !NEW_IRQ
1178:
1179: /* This is copied from kernel/sys_calls.s
1180: and from kernel/blk_drv/scsi/aha1542.c */
1181:
1182: __asm__("
1183: _fdomain_16x0_interrupt:
1184: cld
1185: push %gs
1186: push %fs
1187: push %es
1188: push %ds
1189: pushl %eax
1190: pushl %ebp
1191: pushl %edi
1192: pushl %esi
1193: pushl %edx
1194: pushl %ecx
1195: pushl %ebx
1196: movl $0x10,%edx
1197: mov %dx,%ds
1198: mov %dx,%es
1199: movl $0x17,%edx
1200: mov %dx,%fs
1201:
1202: movl $_fdomain_disable_interrupt,%edx
1203: call *%edx
1204:
1205: movb $0x20,%al
1206: outb %al,$0xA0 # EOI to interrupt controller #1
1207: jmp 1f # give port chance to breathe
1208: 1: jmp 1f
1209: 1: outb %al,$0x20
1210:
1211: sti
1212: movl $_fdomain_16x0_intr,%edx
1213: call *%edx # ``interesting'' way of handling intr.
1214: cli
1215:
1216: movl $_fdomain_enable_interrupt,%edx
1217: call *%edx
1218:
1219: popl %ebx
1220: popl %ecx
1221: popl %edx
1222: popl %esi
1223: popl %edi
1224: popl %ebp
1225: popl %eax
1226: pop %ds
1227: pop %es
1228: pop %fs
1229: pop %gs
1230: iret
1231: ");
1232: #endif
1233:
1234: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.