From 1c21066fc996e08f4518c3efb461713a255a1a0a Mon Sep 17 00:00:00 2001 From: Thomas Bushnell Date: Thu, 1 May 1997 19:50:59 +0000 Subject: Mon Apr 28 12:09:53 1997 Thomas Bushnell, n/BSG * i386/Drivers.in (ncr53c7xx): Change file name to 53c78xx.o. * i386/i386at/gpl/linux/scsi/53c7,8xx.c: Moved to ... * i386/i386at/gpl/linux/scsi/53c78xx.c: ... here. * i386/Makefrag (linux-scsi-files): Change file name here too. Reported by xtest@postman.epa.go.jp. --- i386/Drivers.in | 2 +- i386/Makefrag | 2 +- i386/i386at/gpl/linux/scsi/53c7,8xx.c | 6381 --------------------------------- i386/i386at/gpl/linux/scsi/53c78xx.c | 6381 +++++++++++++++++++++++++++++++++ 4 files changed, 6383 insertions(+), 6383 deletions(-) delete mode 100644 i386/i386at/gpl/linux/scsi/53c7,8xx.c create mode 100644 i386/i386at/gpl/linux/scsi/53c78xx.c (limited to 'i386') diff --git a/i386/Drivers.in b/i386/Drivers.in index e604387..9fc53b1 100644 --- a/i386/Drivers.in +++ b/i386/Drivers.in @@ -84,7 +84,7 @@ AC_DRIVER(qlogic, CONFIG_SCSI_QLOGIC, qlogic.o, scsi) AC_DRIVER(pas16, CONFIG_SCSI_PASS16, pas16.o, scsi) AC_DRIVER(seagate, CONFIG_SCSI_SEAGATE, seagate.o, scsi) AC_DRIVER(t128, CONFIG_SCSI_T128, t128.o, scsi) -AC_DRIVER(ncr53c7xx, CONFIG_SCSI_NCR53C7xx, 53c7,8xx.o, scsi) +AC_DRIVER(ncr53c7xx, CONFIG_SCSI_NCR53C7xx, 53c78xx.o, scsi) AC_DRIVER(eatadma, CONFIG_SCSI_EATA_DMA, eata_dma.o, scsi) AC_DRIVER(eatapio, CONFIG_SCSI_EATA_PIO, eata_pio.o, scsi) AC_DRIVER(wd7000, CONFIG_SCSI_7000FASST, wd7000.o, scsi) diff --git a/i386/Makefrag b/i386/Makefrag index 18479c6..979365f 100644 --- a/i386/Makefrag +++ b/i386/Makefrag @@ -63,7 +63,7 @@ linux-net-files = 3c501.c 3c503.c 3c505.c 3c507.c 3c509.c 3c59x.c \ linux-pci-files = bios32.c pci.c -linux-scsi-files = 53c7,8xx.c AM53C974.c BusLogic.c NCR53c406a.c advansys.c \ +linux-scsi-files = 53c78xx.c AM53C974.c BusLogic.c NCR53c406a.c advansys.c \ aha152x.c aha1542.c aha1740.c aic7xxx.c constants.c eata.c eata_dma.c \ eata_pio.c fdomain.c g_NCR5380.c hosts.c in2000.c pas16.c qlogic.c \ scsi.c scsi_debug.c scsi_ioctl.c scsi_proc.c scsicam.c sd.c \ diff --git a/i386/i386at/gpl/linux/scsi/53c7,8xx.c b/i386/i386at/gpl/linux/scsi/53c7,8xx.c deleted file mode 100644 index 74350b0..0000000 --- a/i386/i386at/gpl/linux/scsi/53c7,8xx.c +++ /dev/null @@ -1,6381 +0,0 @@ -/* - * PERM_OPTIONS are driver options which will be enabled for all NCR boards - * in the system at driver initialization time. - * - * Don't THINK about touching these in PERM_OPTIONS : - * OPTION_IO_MAPPED - * Memory mapped IO does not work under i86 Linux. - * - * OPTION_DEBUG_TEST1 - * Test 1 does bus mastering and interrupt tests, which will help weed - * out brain damaged main boards. - * - * These are development kernel changes. Code for them included in this - * driver release may or may not work. If you turn them on, you should be - * running the latest copy of the development sources from - * - * ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/53c7,8xx - * - * and be subscribed to the ncr53c810@colorado.edu mailing list. To - * subscribe, send mail to majordomo@colorado.edu with - * - * subscribe ncr53c810 - * - * in the text. - * - * - * OPTION_NOASYNC - * Don't negotiate for asynchronous transfers on the first command - * when OPTION_ALWAYS_SYNCHRONOUS is set. Useful for dain bramaged - * devices which do something bad rather than sending a MESSAGE - * REJECT back to us like they should if they can't cope. - * - * OPTION_SYNCHRONOUS - * Enable support for synchronous transfers. Target negotiated - * synchronous transfers will be responded to. To initiate - * a synchronous transfer request, call - * - * request_synchronous (hostno, target) - * - * from within KGDB. - * - * OPTION_ALWAYS_SYNCHRONOUS - * Negotiate for synchronous transfers with every target after - * driver initialization or a SCSI bus reset. This is a bit dangerous, - * since there are some dain bramaged SCSI devices which will accept - * SDTR messages but keep talking asynchronously. - * - * OPTION_DISCONNECT - * Enable support for disconnect/reconnect. To change the - * default setting on a given host adapter, call - * - * request_disconnect (hostno, allow) - * - * where allow is non-zero to allow, 0 to disallow. - * - * If you really want to run 10MHz FAST SCSI-II transfers, you should - * know that the NCR driver currently ignores parity information. Most - * systems do 5MHz SCSI fine. I've seen a lot that have problems faster - * than 8MHz. To play it safe, we only request 5MHz transfers. - * - * If you'd rather get 10MHz transfers, edit sdtr_message and change - * the fourth byte from 50 to 25. - */ - -#define PERM_OPTIONS (OPTION_IO_MAPPED|OPTION_DEBUG_TEST1|OPTION_DISCONNECT|\ - OPTION_SYNCHRONOUS) - -/* - * Sponsored by - * iX Multiuser Multitasking Magazine - * Hannover, Germany - * hm@ix.de - * - * Copyright 1993, 1994, 1995 Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@PoohSticks.ORG - * +1 (303) 786-7975 - * - * TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation. - * - * For more information, please consult - * - * NCR53C810 - * SCSI I/O Processor - * Programmer's Guide - * - * NCR 53C810 - * PCI-SCSI I/O Processor - * Data Manual - * - * NCR 53C810/53C820 - * PCI-SCSI I/O Processor Design In Guide - * - * For literature on Symbios Logic Inc. formerly NCR, SCSI, - * and Communication products please call (800) 334-5454 or - * (719) 536-3300. - * - * PCI BIOS Specification Revision - * PCI Local Bus Specification - * PCI System Design Guide - * - * PCI Special Interest Group - * M/S HF3-15A - * 5200 N.E. Elam Young Parkway - * Hillsboro, Oregon 97124-6497 - * +1 (503) 696-2000 - * +1 (800) 433-5177 - */ - -/* - * Design issues : - * The cumulative latency needed to propagate a read/write request - * through the file system, buffer cache, driver stacks, SCSI host, and - * SCSI device is ultimately the limiting factor in throughput once we - * have a sufficiently fast host adapter. - * - * So, to maximize performance we want to keep the ratio of latency to data - * transfer time to a minimum by - * 1. Minimizing the total number of commands sent (typical command latency - * including drive and bus mastering host overhead is as high as 4.5ms) - * to transfer a given amount of data. - * - * This is accomplished by placing no arbitrary limit on the number - * of scatter/gather buffers supported, since we can transfer 1K - * per scatter/gather buffer without Eric's cluster patches, - * 4K with. - * - * 2. Minimizing the number of fatal interrupts serviced, since - * fatal interrupts halt the SCSI I/O processor. Basically, - * this means offloading the practical maximum amount of processing - * to the SCSI chip. - * - * On the NCR53c810/820/720, this is accomplished by using - * interrupt-on-the-fly signals when commands complete, - * and only handling fatal errors and SDTR / WDTR messages - * in the host code. - * - * On the NCR53c710, interrupts are generated as on the NCR53c8x0, - * only the lack of a interrupt-on-the-fly facility complicates - * things. Also, SCSI ID registers and commands are - * bit fielded rather than binary encoded. - * - * On the NCR53c700 and NCR53c700-66, operations that are done via - * indirect, table mode on the more advanced chips must be - * replaced by calls through a jump table which - * acts as a surrogate for the DSA. Unfortunately, this - * will mean that we must service an interrupt for each - * disconnect/reconnect. - * - * 3. Eliminating latency by pipelining operations at the different levels. - * - * This driver allows a configurable number of commands to be enqueued - * for each target/lun combination (experimentally, I have discovered - * that two seems to work best) and will ultimately allow for - * SCSI-II tagged queuing. - * - * - * Architecture : - * This driver is built around a Linux queue of commands waiting to - * be executed, and a shared Linux/NCR array of commands to start. Commands - * are transfered to the array by the run_process_issue_queue() function - * which is called whenever a command completes. - * - * As commands are completed, the interrupt routine is triggered, - * looks for commands in the linked list of completed commands with - * valid status, removes these commands from a list of running commands, - * calls the done routine, and flags their target/luns as not busy. - * - * Due to limitations in the intelligence of the NCR chips, certain - * concessions are made. In many cases, it is easier to dynamically - * generate/fix-up code rather than calculate on the NCR at run time. - * So, code is generated or fixed up for - * - * - Handling data transfers, using a variable number of MOVE instructions - * interspersed with CALL MSG_IN, WHEN MSGIN instructions. - * - * The DATAIN and DATAOUT routines are separate, so that an incorrect - * direction can be trapped, and space isn't wasted. - * - * It may turn out that we're better off using some sort - * of table indirect instruction in a loop with a variable - * sized table on the NCR53c710 and newer chips. - * - * - Checking for reselection (NCR53c710 and better) - * - * - Handling the details of SCSI context switches (NCR53c710 and better), - * such as reprogramming appropriate synchronous parameters, - * removing the dsa structure from the NCR's queue of outstanding - * commands, etc. - * - */ - -/* - * Accommodate differences between stock 1.2.x and 1.3.x asm-i386/types.h - * so lusers can drop in 53c7,8xx.* and get something which compiles - * without warnings. - */ - -#if !defined(LINUX_1_2) && !defined(LINUX_1_3) -#include -#if LINUX_VERSION_CODE > 65536 + 3 * 256 -#define LINUX_1_3 -#else -#define LINUX_1_2 -#endif -#endif - -#ifdef LINUX_1_2 -#define u32 bogus_u32 -#define s32 bogus_s32 -#include -#undef u32 -#undef s32 -typedef __signed__ int s32; -typedef unsigned int u32; -#endif /* def LINUX_1_2 */ - -#ifdef MODULE -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef LINUX_1_2 -#include "../block/blk.h" -#else -#include -#endif -#undef current - -#include "scsi.h" -#include "hosts.h" -#include "53c7,8xx.h" -#include "constants.h" -#include "sd.h" -#include -#include - -#ifndef LINUX_1_2 -struct proc_dir_entry proc_scsi_ncr53c7xx = { - PROC_SCSI_NCR53C7xx, 9, "ncr53c7xx", - S_IFDIR | S_IRUGO | S_IXUGO, 2 -}; -#endif - -static int check_address (unsigned long addr, int size); -static void dump_events (struct Scsi_Host *host, int count); -static Scsi_Cmnd * return_outstanding_commands (struct Scsi_Host *host, - int free, int issue); -static void hard_reset (struct Scsi_Host *host); -static void ncr_scsi_reset (struct Scsi_Host *host); -static void print_lots (struct Scsi_Host *host); -static void set_synchronous (struct Scsi_Host *host, int target, int sxfer, - int scntl3, int now_connected); -static int datapath_residual (struct Scsi_Host *host); -static const char * sbcl_to_phase (int sbcl); -static void print_progress (Scsi_Cmnd *cmd); -static void print_queues (struct Scsi_Host *host); -static void process_issue_queue (unsigned long flags); -static int shutdown (struct Scsi_Host *host); -static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int result); -static int disable (struct Scsi_Host *host); -static int NCR53c8xx_run_tests (struct Scsi_Host *host); -static int NCR53c8xx_script_len; -static int NCR53c8xx_dsa_len; -static void NCR53c7x0_intr(int irq, struct pt_regs * regs); -static int ncr_halt (struct Scsi_Host *host); -static void intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd - *cmd); -static void intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd); -static void print_dsa (struct Scsi_Host *host, u32 *dsa, - const char *prefix); -static int print_insn (struct Scsi_Host *host, const u32 *insn, - const char *prefix, int kernel); - -static void NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd); -static void NCR53c8x0_init_fixup (struct Scsi_Host *host); -static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct - NCR53c7x0_cmd *cmd); -static void NCR53c8x0_soft_reset (struct Scsi_Host *host); - -/* INSMOD variables */ -static long long perm_options = PERM_OPTIONS; -/* 14 = .5s; 15 is max; decreasing divides by two. */ -static int selection_timeout = 14; -/* Size of event list (per host adapter) */ -static int track_events = 0; - -static struct Scsi_Host *first_host = NULL; /* Head of list of NCR boards */ -static Scsi_Host_Template *the_template = NULL; - -/* - * KNOWN BUGS : - * - There is some sort of conflict when the PPP driver is compiled with - * support for 16 channels? - * - * - On systems which predate the 1.3.x initialization order change, - * the NCR driver will cause Cannot get free page messages to appear. - * These are harmless, but I don't know of an easy way to avoid them. - * - * - With OPTION_DISCONNECT, on two systems under unknown circumstances, - * we get a PHASE MISMATCH with DSA set to zero (suggests that we - * are occurring somewhere in the reselection code) where - * DSP=some value DCMD|DBC=same value. - * - * Closer inspection suggests that we may be trying to execute - * some portion of the DSA? - * scsi0 : handling residual transfer (+ 0 bytes from DMA FIFO) - * scsi0 : handling residual transfer (+ 0 bytes from DMA FIFO) - * scsi0 : no current command : unexpected phase MSGIN. - * DSP=0x1c46cc, DCMD|DBC=0x1c46ac, DSA=0x0 - * DSPS=0x0, TEMP=0x1c3e70, DMODE=0x80 - * scsi0 : DSP-> - * 001c46cc : 0x001c46cc 0x00000000 - * 001c46d4 : 0x001c5ea0 0x000011f8 - * - * Changed the print code in the phase_mismatch handler so - * that we call print_lots to try and diagnose this. - * - */ - -/* - * Possible future direction of architecture for max performance : - * - * We're using a single start array for the NCR chip. This is - * sub-optimal, because we cannot add a command which would conflict with - * an executing command to this start queue, and therefore must insert the - * next command for a given I/T/L combination after the first has completed; - * incurring our interrupt latency between SCSI commands. - * - * To allow furthur pipelining of the NCR and host CPU operation, we want - * to set things up so that immediately on termination of a command destined - * for a given LUN, we get that LUN busy again. - * - * To do this, we need to add a 32 bit pointer to which is jumped to - * on completion of a command. If no new command is available, this - * would point to the usual DSA issue queue select routine. - * - * If one were, it would point to a per-NCR53c7x0_cmd select routine - * which starts execution immediately, inserting the command at the head - * of the start queue if the NCR chip is selected or reselected. - * - * We would chanage so that we keep a list of outstanding commands - * for each unit, rather than a single running_list. We'd insert - * a new command into the right running list; if the NCR didn't - * have something running for that yet, we'd put it in the - * start queue as well. Some magic needs to happen to handle the - * race condition between the first command terminating before the - * new one is written. - * - * Potential for profiling : - * Call do_gettimeofday(struct timeval *tv) to get 800ns resolution. - */ - - -/* - * TODO : - * 1. To support WIDE transfers, not much needs to happen. We - * should do CHMOVE instructions instead of MOVEs when - * we have scatter/gather segments of uneven length. When - * we do this, we need to handle the case where we disconnect - * between segments. - * - * 2. Currently, when Icky things happen we do a FATAL(). Instead, - * we want to do an integrity check on the parts of the NCR hostdata - * structure which were initialized at boot time; FATAL() if that - * fails, and otherwise try to recover. Keep track of how many - * times this has happened within a single SCSI command; if it - * gets excessive, then FATAL(). - * - * 3. Parity checking is currently disabled, and a few things should - * happen here now that we support synchronous SCSI transfers : - * 1. On soft-reset, we shuld set the EPC (Enable Parity Checking) - * and AAP (Assert SATN/ on parity error) bits in SCNTL0. - * - * 2. We should enable the parity interrupt in the SIEN0 register. - * - * 3. intr_phase_mismatch() needs to believe that message out is - * allways an "acceptable" phase to have a mismatch in. If - * the old phase was MSG_IN, we should send a MESSAGE PARITY - * error. If the old phase was something else, we should send - * a INITIATOR_DETECTED_ERROR message. Note that this could - * cause a RESTORE POINTERS message; so we should handle that - * correctly first. Instead, we should probably do an - * initiator_abort. - * - * 4. MPEE bit of CTEST4 should be set so we get interrupted if - * we detect an error. - * - * - * 5. The initial code has been tested on the NCR53c810. I don't - * have access to NCR53c700, 700-66 (Forex boards), NCR53c710 - * (NCR Pentium systems), NCR53c720, NCR53c820, or NCR53c825 boards to - * finish development on those platforms. - * - * NCR53c820/825/720 - need to add wide transfer support, including WDTR - * negotiation, programming of wide transfer capabilities - * on reselection and table indirect selection. - * - * NCR53c710 - need to add fatal interrupt or GEN code for - * command completion signaling. Need to modify all - * SDID, SCID, etc. registers, and table indirect select code - * since these use bit fielded (ie 1<NOP_insn) ? - /* - * If the IF TRUE bit is set, it's a JUMP instruction. The - * operand is a bus pointer to the dsa_begin routine for this DSA. The - * dsa field of the NCR53c7x0_cmd structure starts with the - * DSA code template. By converting to a virtual address, - * subtracting the code template size, and offset of the - * dsa field, we end up with a pointer to the start of the - * structure (alternatively, we could use the - * dsa_cmnd field, an anachronism from when we weren't - * sure what the relationship between the NCR structures - * and host structures were going to be. - */ - (struct NCR53c7x0_cmd *) ((char *) bus_to_virt (issue[1]) - - (hostdata->E_dsa_code_begin - hostdata->E_dsa_code_template) - - offsetof(struct NCR53c7x0_cmd, dsa)) - /* If the IF TRUE bit is not set, it's a NOP */ - : NULL; -} - - -/* - * Function : static internal_setup(int board, int chip, char *str, int *ints) - * - * Purpose : LILO command line initialization of the overrides array, - * - * Inputs : board - currently, unsupported. chip - 700, 70066, 710, 720 - * 810, 815, 820, 825, although currently only the NCR53c810 is - * supported. - * - */ - -static void -internal_setup(int board, int chip, char *str, int *ints) { - unsigned char pci; /* Specifies a PCI override, with bus, device, - function */ - - pci = (str && !strcmp (str, "pci")) ? 1 : 0; - -/* - * Override syntaxes are as follows : - * ncr53c700,ncr53c700-66,ncr53c710,ncr53c720=mem,io,irq,dma - * ncr53c810,ncr53c820,ncr53c825=mem,io,irq or pci,bus,device,function - */ - - if (commandline_current < OVERRIDE_LIMIT) { - overrides[commandline_current].pci = pci ? 1 : 0; - if (!pci) { - overrides[commandline_current].data.normal.base = ints[1]; - overrides[commandline_current].data.normal.io_port = ints[2]; - overrides[commandline_current].data.normal.irq = ints[3]; - overrides[commandline_current].data.normal.dma = (ints[0] >= 4) ? - ints[4] : DMA_NONE; - /* FIXME: options is now a long long */ - overrides[commandline_current].options = (ints[0] >= 5) ? - ints[5] : 0; - } else { - overrides[commandline_current].data.pci.bus = ints[1]; - overrides[commandline_current].data.pci.device = ints[2]; - overrides[commandline_current].data.pci.function = ints[3]; - /* FIXME: options is now a long long */ - overrides[commandline_current].options = (ints[0] >= 4) ? - ints[4] : 0; - } - overrides[commandline_current].board = board; - overrides[commandline_current].chip = chip; - ++commandline_current; - ++no_overrides; - } else { - printk ("53c7,7x0.c:internal_setup() : too many overrides\n"); - } -} - -/* - * XXX - we might want to implement a single override function - * with a chip type field, revamp the command line configuration, - * etc. - */ - -#define setup_wrapper(x) \ -void ncr53c##x##_setup (char *str, int *ints) { \ - internal_setup (BOARD_GENERIC, x, str, ints); \ -} - -setup_wrapper(700) -setup_wrapper(70066) -setup_wrapper(710) -setup_wrapper(720) -setup_wrapper(810) -setup_wrapper(815) -setup_wrapper(820) -setup_wrapper(825) - -/* - * FIXME: we should junk these, in favor of synchronous_want and - * wide_want in the NCR53c7x0_hostdata structure. - */ - -/* Template for "preferred" synchronous transfer parameters. */ - -static const unsigned char sdtr_message[] = { - EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 50 /* *4ns */, 8 /* off */ -}; - -/* Template to request asynchronous transfers */ - -static const unsigned char async_message[] = { - EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 0, 0 /* asynchronous */ -}; - -/* Template for "preferred" WIDE transfer parameters */ - -static const unsigned char wdtr_message[] = { - EXTENDED_MESSAGE, 2 /* length */, EXTENDED_WDTR, 1 /* 2^1 bytes */ -}; - -/* - * Function : struct Scsi_Host *find_host (int host) - * - * Purpose : KGDB support function which translates a host number - * to a host structure. - * - * Inputs : host - number of SCSI host - * - * Returns : NULL on failure, pointer to host structure on success. - */ - -static struct Scsi_Host * -find_host (int host) { - struct Scsi_Host *h; - for (h = first_host; h && h->host_no != host; h = h->next); - if (!h) { - printk (KERN_ALERT "scsi%d not found\n", host); - return NULL; - } else if (h->hostt != the_template) { - printk (KERN_ALERT "scsi%d is not a NCR board\n", host); - return NULL; - } - return h; -} - -/* - * Function : request_synchronous (int host, int target) - * - * Purpose : KGDB interface which will allow us to negotiate for - * synchronous transfers. This ill be replaced with a more - * integrated function; perhaps a new entry in the scsi_host - * structure, accessable via an ioctl() or perhaps /proc/scsi. - * - * Inputs : host - number of SCSI host; target - number of target. - * - * Returns : 0 when negotiation has been setup for next SCSI command, - * -1 on failure. - */ - -static int -request_synchronous (int host, int target) { - struct Scsi_Host *h; - struct NCR53c7x0_hostdata *hostdata; - unsigned long flags; - if (target < 0) { - printk (KERN_ALERT "target %d is bogus\n", target); - return -1; - } - if (!(h = find_host (host))) - return -1; - else if (h->this_id == target) { - printk (KERN_ALERT "target %d is host ID\n", target); - return -1; - } -#ifndef LINUX_1_2 - else if (target > h->max_id) { - printk (KERN_ALERT "target %d exceeds maximum of %d\n", target, - h->max_id); - return -1; - } -#endif - hostdata = (struct NCR53c7x0_hostdata *)h->hostdata; - - save_flags(flags); - cli(); - if (hostdata->initiate_sdtr & (1 << target)) { - restore_flags(flags); - printk (KERN_ALERT "target %d allready doing SDTR\n", target); - return -1; - } - hostdata->initiate_sdtr |= (1 << target); - restore_flags(flags); - return 0; -} - -/* - * Function : request_disconnect (int host, int on_or_off) - * - * Purpose : KGDB support function, tells us to allow or disallow - * disconnections. - * - * Inputs : host - number of SCSI host; on_or_off - non-zero to allow, - * zero to disallow. - * - * Returns : 0 on success, * -1 on failure. - */ - -static int -request_disconnect (int host, int on_or_off) { - struct Scsi_Host *h; - struct NCR53c7x0_hostdata *hostdata; - if (!(h = find_host (host))) - return -1; - hostdata = (struct NCR53c7x0_hostdata *) h->hostdata; - if (on_or_off) - hostdata->options |= OPTION_DISCONNECT; - else - hostdata->options &= ~OPTION_DISCONNECT; - return 0; -} - -/* - * Function : static void NCR53c7x0_driver_init (struct Scsi_Host *host) - * - * Purpose : Initialize internal structures, as required on startup, or - * after a SCSI bus reset. - * - * Inputs : host - pointer to this host adapter's structure - */ - -static void -NCR53c7x0_driver_init (struct Scsi_Host *host) { - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - int i, j; - u32 *current; - for (i = 0; i < 16; ++i) { - hostdata->request_sense[i] = 0; - for (j = 0; j < 8; ++j) - hostdata->busy[i][j] = 0; - set_synchronous (host, i, /* sxfer */ 0, hostdata->saved_scntl3, 0); - } - hostdata->issue_queue = NULL; - hostdata->running_list = hostdata->finished_queue = - hostdata->current = NULL; - for (i = 0, current = (u32 *) hostdata->schedule; - i < host->can_queue; ++i, current += 2) { - current[0] = hostdata->NOP_insn; - current[1] = 0xdeadbeef; - } - current[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | DBC_TCI_TRUE; - current[1] = (u32) virt_to_bus (hostdata->script) + - hostdata->E_wait_reselect; - hostdata->reconnect_dsa_head = 0; - hostdata->addr_reconnect_dsa_head = (u32) - virt_to_bus((void *) &(hostdata->reconnect_dsa_head)); - hostdata->expecting_iid = 0; - hostdata->expecting_sto = 0; - if (hostdata->options & OPTION_ALWAYS_SYNCHRONOUS) - hostdata->initiate_sdtr = 0xffff; - else - hostdata->initiate_sdtr = 0; - hostdata->talked_to = 0; - hostdata->idle = 1; -} - -/* - * Function : static int ccf_to_clock (int ccf) - * - * Purpose : Return the largest SCSI clock allowable for a given - * clock conversion factor, allowing us to do synchronous periods - * when we don't know what the SCSI clock is by taking at least - * as long as the device says we can. - * - * Inputs : ccf - * - * Returns : clock on success, -1 on failure. - */ - -static int -ccf_to_clock (int ccf) { - switch (ccf) { - case 1: return 25000000; /* Divide by 1.0 */ - case 2: return 37500000; /* Divide by 1.5 */ - case 3: return 50000000; /* Divide by 2.0 */ - case 0: /* Divide by 3.0 */ - case 4: return 66000000; - default: return -1; - } -} - -/* - * Function : static int clock_to_ccf (int clock) - * - * Purpose : Return the clock conversion factor for a given SCSI clock. - * - * Inputs : clock - SCSI clock expressed in Hz. - * - * Returns : ccf on success, -1 on failure. - */ - -static int -clock_to_ccf (int clock) { - if (clock < 16666666) - return -1; - if (clock < 25000000) - return 1; /* Divide by 1.0 */ - else if (clock < 37500000) - return 2; /* Divide by 1.5 */ - else if (clock < 50000000) - return 3; /* Divide by 2.0 */ - else if (clock < 66000000) - return 4; /* Divide by 3.0 */ - else - return -1; -} - -/* - * Function : static int NCR53c7x0_init (struct Scsi_Host *host) - * - * Purpose : initialize the internal structures for a given SCSI host - * - * Inputs : host - pointer to this host adapter's structure - * - * Preconditions : when this function is called, the chip_type - * field of the hostdata structure MUST have been set. - * - * Returns : 0 on success, -1 on failure. - */ - -static int -NCR53c7x0_init (struct Scsi_Host *host) { - NCR53c7x0_local_declare(); - int i, ccf, expected_ccf; - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - struct Scsi_Host *search; - /* - * There are some things which we need to know about in order to provide - * a semblance of support. Print 'em if they aren't what we expect, - * otherwise don't add to the noise. - * - * -1 means we don't know what to expect. - */ - int expected_id = -1; - int expected_clock = -1; - int uninitialized = 0; - /* - * FIXME : this is only on Intel boxes. On other platforms, this - * will differ. - */ - int expected_mapping = OPTION_IO_MAPPED; - NCR53c7x0_local_setup(host); - - switch (hostdata->chip) { - case 820: - case 825: -#ifdef notyet - host->max_id = 15; -#endif - /* Fall through */ - case 810: - case 815: - hostdata->dstat_sir_intr = NCR53c8x0_dstat_sir_intr; - hostdata->init_save_regs = NULL; - hostdata->dsa_fixup = NCR53c8xx_dsa_fixup; - hostdata->init_fixup = NCR53c8x0_init_fixup; - hostdata->soft_reset = NCR53c8x0_soft_reset; - hostdata->run_tests = NCR53c8xx_run_tests; -/* Is the SCSI clock ever anything else on these chips? */ - expected_clock = hostdata->scsi_clock = 40000000; - expected_id = 7; - break; - default: - printk ("scsi%d : chip type of %d is not supported yet, detaching.\n", - host->host_no, hostdata->chip); - scsi_unregister (host); - return -1; - } - - /* Assign constants accessed by NCR */ - hostdata->NCR53c7xx_zero = 0; - hostdata->NCR53c7xx_msg_reject = MESSAGE_REJECT; - hostdata->NCR53c7xx_msg_abort = ABORT; - hostdata->NCR53c7xx_msg_nop = NOP; - hostdata->NOP_insn = (DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24; - - if (expected_mapping == -1 || - (hostdata->options & (OPTION_MEMORY_MAPPED)) != - (expected_mapping & OPTION_MEMORY_MAPPED)) - printk ("scsi%d : using %s mapped access\n", host->host_no, - (hostdata->options & OPTION_MEMORY_MAPPED) ? "memory" : - "io"); - - hostdata->dmode = (hostdata->chip == 700 || hostdata->chip == 70066) ? - DMODE_REG_00 : DMODE_REG_10; - hostdata->istat = ((hostdata->chip / 100) == 8) ? - ISTAT_REG_800 : ISTAT_REG_700; - -/* Only the ISTAT register is readable when the NCR is running, so make - sure it's halted. */ - ncr_halt(host); - -/* - * XXX - the NCR53c700 uses bitfielded registers for SCID, SDID, etc, - * as does the 710 with one bit per SCSI ID. Conversely, the NCR - * uses a normal, 3 bit binary representation of these values. - * - * Get the rest of the NCR documentation, and FIND OUT where the change - * was. - */ -#if 0 - tmp = hostdata->this_id_mask = NCR53c7x0_read8(SCID_REG); - for (host->this_id = 0; tmp != 1; tmp >>=1, ++host->this_id); -#else - host->this_id = NCR53c7x0_read8(SCID_REG) & 15; - if (host->this_id == 0) - host->this_id = 7; /* sanitize hostid---0 doesn't make sense */ - hostdata->this_id_mask = 1 << host->this_id; -#endif - -/* - * Note : we should never encounter a board setup for ID0. So, - * if we see ID0, assume that it was uninitialized and set it - * to the industry standard 7. - */ - if (!host->this_id) { - printk("scsi%d : initiator ID was %d, changing to 7\n", - host->host_no, host->this_id); - host->this_id = 7; - hostdata->this_id_mask = 1 << 7; - uninitialized = 1; - }; - - if (expected_id == -1 || host->this_id != expected_id) - printk("scsi%d : using initiator ID %d\n", host->host_no, - host->this_id); - - /* - * Save important registers to allow a soft reset. - */ - - if ((hostdata->chip / 100) == 8) { - /* - * CTEST4 controls burst mode disable. - */ - hostdata->saved_ctest4 = NCR53c7x0_read8(CTEST4_REG_800) & - CTEST4_800_SAVE; - } else { - /* - * CTEST7 controls cache snooping, burst mode, and support for - * external differential drivers. - */ - hostdata->saved_ctest7 = NCR53c7x0_read8(CTEST7_REG) & CTEST7_SAVE; - } - - /* - * On NCR53c700 series chips, DCNTL controls the SCSI clock divisor, - * on 800 series chips, it allows for a totem-pole IRQ driver. - */ - - hostdata->saved_dcntl = NCR53c7x0_read8(DCNTL_REG); - - /* - * DCNTL_800_IRQM controls weather we are using an open drain - * driver (reset) or totem pole driver (set). In all cases, - * it's level active. I suppose this is an issue when we're trying to - * wire-or the same PCI INTx line? - */ - if ((hostdata->chip / 100) == 8) - hostdata->saved_dcntl &= ~DCNTL_800_IRQM; - - /* - * DMODE controls DMA burst length, and on 700 series chips, - * 286 mode and bus width - */ - hostdata->saved_dmode = NCR53c7x0_read8(hostdata->dmode); - - /* - * Now that burst length and enabled/disabled status is known, - * clue the user in on it. - */ - - if ((hostdata->chip / 100) == 8) { - if (hostdata->saved_ctest4 & CTEST4_800_BDIS) { - printk ("scsi%d : burst mode disabled\n", host->host_no); - } else { - switch (hostdata->saved_dmode & DMODE_BL_MASK) { - case DMODE_BL_2: i = 2; break; - case DMODE_BL_4: i = 4; break; - case DMODE_BL_8: i = 8; break; - case DMODE_BL_16: i = 16; break; - default: i = 0; - } - printk ("scsi%d : burst length %d\n", host->host_no, i); - } - } - - /* - * On NCR53c810 and NCR53c820 chips, SCNTL3 contails the synchronous - * and normal clock conversion factors. - */ - if (hostdata->chip / 100 == 8) { - expected_ccf = clock_to_ccf (expected_clock); - hostdata->saved_scntl3 = NCR53c7x0_read8(SCNTL3_REG_800); - ccf = hostdata->saved_scntl3 & SCNTL3_800_CCF_MASK; - if (expected_ccf != -1 && ccf != expected_ccf && !ccf) { - hostdata->saved_scntl3 = (hostdata->saved_scntl3 & - ~SCNTL3_800_CCF_MASK) | expected_ccf; - if (!uninitialized) { - printk ("scsi%d : reset ccf to %d from %d\n", - host->host_no, expected_ccf, ccf); - uninitialized = 1; - } - } - } else - ccf = 0; - - /* - * If we don't have a SCSI clock programmed, pick one on the upper - * bound of that allowed by NCR so that our transfers err on the - * slow side, since transfer period must be >= the agreed - * upon period. - */ - - if ((!hostdata->scsi_clock) && (hostdata->scsi_clock = ccf_to_clock (ccf)) - == -1) { - printk ("scsi%d : clock conversion factor %d unknown.\n" - " synchronous transfers disabled\n", - host->host_no, ccf); - hostdata->options &= ~OPTION_SYNCHRONOUS; - hostdata->scsi_clock = 0; - } - - if (expected_clock == -1 || hostdata->scsi_clock != expected_clock) - printk ("scsi%d : using %dMHz SCSI clock\n", host->host_no, - hostdata->scsi_clock / 1000000); - - for (i = 0; i < 16; ++i) - hostdata->cmd_allocated[i] = 0; - - if (hostdata->init_save_regs) - hostdata->init_save_regs (host); - if (hostdata->init_fixup) - hostdata->init_fixup (host); - - if (!the_template) { - the_template = host->hostt; - first_host = host; - } - - /* - * Linux SCSI drivers have always been plagued with initialization - * problems - some didn't work with the BIOS disabled since they expected - * initialization from it, some didn't work when the networking code - * was enabled and registers got scrambled, etc. - * - * To avoid problems like this, in the future, we will do a soft - * reset on the SCSI chip, taking it back to a sane state. - */ - - hostdata->soft_reset (host); - -#if 1 - hostdata->debug_count_limit = -1; -#else - hostdata->debug_count_limit = 1; -#endif - hostdata->intrs = -1; - hostdata->resets = -1; - memcpy ((void *) hostdata->synchronous_want, (void *) sdtr_message, - sizeof (hostdata->synchronous_want)); - - NCR53c7x0_driver_init (host); - - /* - * Set up an interrupt handler if we aren't already sharing an IRQ - * with another board. - */ - - for (search = first_host; search && !(search->hostt == the_template && - search->irq == host->irq && search != host); search=search->next); - - if (!search) { - if (request_irq(host->irq, NCR53c7x0_intr, SA_INTERRUPT, "53c7,8xx")) { - printk("scsi%d : IRQ%d not free, detaching\n" - " You have either a configuration problem, or a\n" - " broken BIOS. You may wish to manually assign\n" - " an interrupt to the NCR board rather than using\n" - " an automatic setting.\n", - host->host_no, host->irq); - scsi_unregister (host); - return -1; - } - } else { - printk("scsi%d : using interrupt handler previously installed for scsi%d\n", - host->host_no, search->host_no); - } - - - if ((hostdata->run_tests && hostdata->run_tests(host) == -1) || - (hostdata->options & OPTION_DEBUG_TESTS_ONLY)) { - /* XXX Should disable interrupts, etc. here */ - scsi_unregister (host); - return -1; - } else { - if (host->io_port) { - host->n_io_port = 128; - request_region (host->io_port, host->n_io_port, "ncr53c7,8xx"); - } - } - - if (NCR53c7x0_read8 (SBCL_REG) & SBCL_BSY) { - printk ("scsi%d : bus wedge, doing SCSI reset\n", host->host_no); - hard_reset (host); - } - return 0; -} - -/* - * Function : static int normal_init(Scsi_Host_Template *tpnt, int board, - * int chip, u32 base, int io_port, int irq, int dma, int pcivalid, - * unsigned char pci_bus, unsigned char pci_device_fn, - * long long options); - * - * Purpose : initializes a NCR53c7,8x0 based on base addresses, - * IRQ, and DMA channel. - * - * Useful where a new NCR chip is backwards compatible with - * a supported chip, but the DEVICE ID has changed so it - * doesn't show up when the autoprobe does a pcibios_find_device. - * - * Inputs : tpnt - Template for this SCSI adapter, board - board level - * product, chip - 810, 820, or 825, bus - PCI bus, device_fn - - * device and function encoding as used by PCI BIOS calls. - * - * Returns : 0 on success, -1 on failure. - * - */ - -static int -normal_init (Scsi_Host_Template *tpnt, int board, int chip, - u32 base, int io_port, int irq, int dma, int pci_valid, - unsigned char pci_bus, unsigned char pci_device_fn, long long options) { - struct Scsi_Host *instance; - struct NCR53c7x0_hostdata *hostdata; - char chip_str[80]; - int script_len = 0, dsa_len = 0, size = 0, max_cmd_size = 0, - schedule_size = 0, ok = 0; - void *tmp; - - options |= perm_options; - - switch (chip) { - case 825: - case 820: - case 815: - case 810: - schedule_size = (tpnt->can_queue + 1) * 8 /* JUMP instruction size */; - script_len = NCR53c8xx_script_len; - dsa_len = NCR53c8xx_dsa_len; - options |= OPTION_INTFLY; - sprintf (chip_str, "NCR53c%d", chip); - break; - default: - printk("scsi-ncr53c7,8xx : unsupported SCSI chip %d\n", chip); - return -1; - } - - printk("scsi-ncr53c7,8xx : %s at memory 0x%x, io 0x%x, irq %d", - chip_str, (unsigned) base, io_port, irq); - if (dma == DMA_NONE) - printk("\n"); - else - printk(", dma %d\n", dma); - - if ((chip / 100 == 8) && !pci_valid) - printk ("scsi-ncr53c7,8xx : for better reliability and performance, please use the\n" - " PCI override instead.\n" - " Syntax : ncr53c8{10,15,20,25}=pci,,,\n" - " and are usually 0.\n"); - - if (options & OPTION_DEBUG_PROBE_ONLY) { - printk ("scsi-ncr53c7,8xx : probe only enabled, aborting initialization\n"); - return -1; - } - - max_cmd_size = sizeof(struct NCR53c7x0_cmd) + dsa_len + - /* Size of dynamic part of command structure : */ - 2 * /* Worst case : we don't know if we need DATA IN or DATA out */ - ( 2 * /* Current instructions per scatter/gather segment */ - tpnt->sg_tablesize + - 3 /* Current startup / termination required per phase */ - ) * - 8 /* Each instruction is eight bytes */; - - /* Allocate fixed part of hostdata, dynamic part to hold appropriate - SCSI SCRIPT(tm) plus a single, maximum-sized NCR53c7x0_cmd structure. - - We need a NCR53c7x0_cmd structure for scan_scsis() when we are - not loaded as a module, and when we're loaded as a module, we - can't use a non-dynamically allocated structure because modules - are vmalloc()'d, which can allow structures to cross page - boundaries and breaks our physical/virtual address assumptions - for DMA. - - So, we stick it past the end of our hostdata structure. - - ASSUMPTION : - Regardless of how many simultaneous SCSI commands we allow, - the probe code only executes a _single_ instruction at a time, - so we only need one here, and don't need to allocate NCR53c7x0_cmd - structures for each target until we are no longer in scan_scsis - and kmalloc() has become functional (memory_init() happens - after all device driver initialization). - */ - - size = sizeof(struct NCR53c7x0_hostdata) + script_len + - /* Note that alignment will be guaranteed, since we put the command - allocated at probe time after the fixed-up SCSI script, which - consists of 32 bit words, aligned on a 32 bit boundary. But - on a 64bit machine we need 8 byte alignment for hostdata->free, so - we add in another 4 bytes to take care of potential misalignment - */ - (sizeof(void *) - sizeof(u32)) + max_cmd_size + schedule_size; - - instance = scsi_register (tpnt, size); - if (!instance) - return -1; - - /* FIXME : if we ever support an ISA NCR53c7xx based board, we - need to check if the chip is running in a 16 bit mode, and if so - unregister it if it is past the 16M (0x1000000) mark */ - - hostdata = (struct NCR53c7x0_hostdata *) - instance->hostdata; - hostdata->size = size; - hostdata->script_count = script_len / sizeof(u32); - hostdata = (struct NCR53c7x0_hostdata *) instance->hostdata; - hostdata->board = board; - hostdata->chip = chip; - if ((hostdata->pci_valid = pci_valid)) { - hostdata->pci_bus = pci_bus; - hostdata->pci_device_fn = pci_device_fn; - } - - /* - * Being memory mapped is more desirable, since - * - * - Memory accesses may be faster. - * - * - The destination and source address spaces are the same for - * all instructions, meaning we don't have to twiddle dmode or - * any other registers. - * - * So, we try for memory mapped, and if we don't get it, - * we go for port mapped, and that failing we tell the user - * it can't work. - */ - - if (base) { - instance->base = (unsigned char *) (unsigned long) base; - /* Check for forced I/O mapping */ - if (!(options & OPTION_IO_MAPPED)) { - options |= OPTION_MEMORY_MAPPED; - ok = 1; - } - } else { - options &= ~OPTION_MEMORY_MAPPED; - } - - if (io_port) { - instance->io_port = io_port; - options |= OPTION_IO_MAPPED; - ok = 1; - } else { - options &= ~OPTION_IO_MAPPED; - } - - if (!ok) { - printk ("scsi%d : not initializing, no I/O or memory mapping known \n", - instance->host_no); - scsi_unregister (instance); - return -1; - } - instance->irq = irq; - instance->dma_channel = dma; - - hostdata->options = options; - hostdata->dsa_len = dsa_len; - hostdata->max_cmd_size = max_cmd_size; - hostdata->num_cmds = 1; - /* Initialize single command */ - tmp = (hostdata->script + hostdata->script_count); - hostdata->free = ROUNDUP(tmp, void *); - hostdata->free->real = tmp; - hostdata->free->size = max_cmd_size; - hostdata->free->free = NULL; - hostdata->free->next = NULL; - hostdata->extra_allocate = 0; - - /* Allocate command start code space */ - hostdata->schedule = (chip == 700 || chip == 70066) ? - NULL : (u32 *) ((char *)hostdata->free + max_cmd_size); - -/* - * For diagnostic purposes, we don't really care how fast things blaze. - * For profiling, we want to access the 800ns resolution system clock, - * using a 'C' call on the host processor. - * - * Therefore, there's no need for the NCR chip to directly manipulate - * this data, and we should put it wherever is most convienient for - * Linux. - */ - if (track_events) - hostdata->events = (struct NCR53c7x0_event *) (track_events ? - vmalloc (sizeof (struct NCR53c7x0_event) * track_events) : NULL); - else - hostdata->events = NULL; - - if (hostdata->events) { - memset ((void *) hostdata->events, 0, sizeof(struct NCR53c7x0_event) * - track_events); - hostdata->event_size = track_events; - hostdata->event_index = 0; - } else - hostdata->event_size = 0; - - return NCR53c7x0_init(instance); -} - - -/* - * Function : static int ncr_pci_init(Scsi_Host_Template *tpnt, int board, - * int chip, int bus, int device_fn, long long options) - * - * Purpose : initializes a NCR53c800 family based on the PCI - * bus, device, and function location of it. Allows - * reprogramming of latency timer and determining addresses - * and whether bus mastering, etc. are OK. - * - * Useful where a new NCR chip is backwards compatible with - * a supported chip, but the DEVICE ID has changed so it - * doesn't show up when the autoprobe does a pcibios_find_device. - * - * Inputs : tpnt - Template for this SCSI adapter, board - board level - * product, chip - 810, 820, or 825, bus - PCI bus, device_fn - - * device and function encoding as used by PCI BIOS calls. - * - * Returns : 0 on success, -1 on failure. - * - */ - -static int -ncr_pci_init (Scsi_Host_Template *tpnt, int board, int chip, - unsigned char bus, unsigned char device_fn, long long options) { - unsigned short vendor_id, device_id, command; -#ifdef LINUX_1_2 - unsigned long -#else - unsigned int -#endif - base, io_port; - unsigned char irq, revision; - int error, expected_chip; - int expected_id = -1, max_revision = -1, min_revision = -1; - int i; - - printk("scsi-ncr53c7,8xx : at PCI bus %d, device %d, function %d\n", - bus, (int) (device_fn & 0xf8) >> 3, - (int) device_fn & 7); - - if (!pcibios_present()) { - printk("scsi-ncr53c7,8xx : not initializing due to lack of PCI BIOS,\n" - " try using memory, port, irq override instead.\n"); - return -1; - } - - if ((error = pcibios_read_config_word (bus, device_fn, PCI_VENDOR_ID, - &vendor_id)) || - (error = pcibios_read_config_word (bus, device_fn, PCI_DEVICE_ID, - &device_id)) || - (error = pcibios_read_config_word (bus, device_fn, PCI_COMMAND, - &command)) || - (error = pcibios_read_config_dword (bus, device_fn, - PCI_BASE_ADDRESS_0, &io_port)) || - (error = pcibios_read_config_dword (bus, device_fn, - PCI_BASE_ADDRESS_1, &base)) || - (error = pcibios_read_config_byte (bus, device_fn, PCI_CLASS_REVISION, - &revision)) || - (error = pcibios_read_config_byte (bus, device_fn, PCI_INTERRUPT_LINE, - &irq))) { - printk ("scsi-ncr53c7,8xx : error %s not initializing due to error reading configuration space\n" - " perhaps you specified an incorrect PCI bus, device, or function.\n" - , pcibios_strerror(error)); - return -1; - } - - /* If any one ever clones the NCR chips, this will have to change */ - - if (vendor_id != PCI_VENDOR_ID_NCR) { - printk ("scsi-ncr53c7,8xx : not initializing, 0x%04x is not NCR vendor ID\n", - (int) vendor_id); - return -1; - } - - - /* - * Bit 0 is the address space indicator and must be one for I/O - * space mappings, bit 1 is reserved, discard them after checking - * that they have the correct value of 1. - */ - - if (command & PCI_COMMAND_IO) { - if ((io_port & 3) != 1) { - printk ("scsi-ncr53c7,8xx : disabling I/O mapping since base address 0 (0x%x)\n" - " bits 0..1 indicate a non-IO mapping\n", - (unsigned) io_port); - io_port = 0; - } else - io_port &= PCI_BASE_ADDRESS_IO_MASK; - } else { - io_port = 0; - } - - if (command & PCI_COMMAND_MEMORY) { - if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) { - printk("scsi-ncr53c7,8xx : disabling memory mapping since base address 1\n" - " contains a non-memory mapping\n"); - base = 0; - } else - base &= PCI_BASE_ADDRESS_MEM_MASK; - } else { - base = 0; - } - - if (!io_port && !base) { - printk ("scsi-ncr53c7,8xx : not initializing, both I/O and memory mappings disabled\n"); - return -1; - } - - if (!(command & PCI_COMMAND_MASTER)) { - printk ("scsi-ncr53c7,8xx : not initializing, BUS MASTERING was disabled\n"); - return -1; - } - - for (i = 0; i < NPCI_CHIP_IDS; ++i) { - if (device_id == pci_chip_ids[i].pci_device_id) { - max_revision = pci_chip_ids[i].max_revision; - min_revision = pci_chip_ids[i].min_revision; - expected_chip = pci_chip_ids[i].chip; - } - if (chip == pci_chip_ids[i].chip) - expected_id = pci_chip_ids[i].pci_device_id; - } - - if (chip && device_id != expected_id) - printk ("scsi-ncr53c7,8xx : warning : device id of 0x%04x doesn't\n" - " match expected 0x%04x\n", - (unsigned int) device_id, (unsigned int) expected_id ); - - if (max_revision != -1 && revision > max_revision) - printk ("scsi-ncr53c7,8xx : warning : revision of %d is greater than %d.\n", - (int) revision, max_revision); - else if (min_revision != -1 && revision < min_revision) - printk ("scsi-ncr53c7,8xx : warning : revision of %d is less than %d.\n", - (int) revision, min_revision); - - if (io_port && check_region (io_port, 128)) { - printk ("scsi-ncr53c7,8xx : IO region 0x%x to 0x%x is in use\n", - (unsigned) io_port, (unsigned) io_port + 127); - return -1; - } - - return normal_init (tpnt, board, chip, (int) base, io_port, - (int) irq, DMA_NONE, 1, bus, device_fn, options); -} - - -/* - * Function : int NCR53c7xx_detect(Scsi_Host_Template *tpnt) - * - * Purpose : detects and initializes NCR53c7,8x0 SCSI chips - * that were autoprobed, overridden on the LILO command line, - * or specified at compile time. - * - * Inputs : tpnt - template for this SCSI adapter - * - * Returns : number of host adapters detected - * - */ - -int -NCR53c7xx_detect(Scsi_Host_Template *tpnt) { - int i; - int current_override; - int count; /* Number of boards detected */ - unsigned char pci_bus, pci_device_fn; - static short pci_index=0; /* Device index to PCI BIOS calls */ - -#ifndef LINUX_1_2 - tpnt->proc_dir = &proc_scsi_ncr53c7xx; -#endif - - for (current_override = count = 0; current_override < OVERRIDE_LIMIT; - ++current_override) { - if (overrides[current_override].pci ? - !ncr_pci_init (tpnt, overrides[current_override].board, - overrides[current_override].chip, - (unsigned char) overrides[current_override].data.pci.bus, - (((overrides[current_override].data.pci.device - << 3) & 0xf8)|(overrides[current_override].data.pci.function & - 7)), overrides[current_override].options): - !normal_init (tpnt, overrides[current_override].board, - overrides[current_override].chip, - overrides[current_override].data.normal.base, - overrides[current_override].data.normal.io_port, - overrides[current_override].data.normal.irq, - overrides[current_override].data.normal.dma, - 0 /* PCI data invalid */, 0 /* PCI bus place holder */, - 0 /* PCI device_function place holder */, - overrides[current_override].options)) { - ++count; - } - } - - if (pcibios_present()) { - for (i = 0; i < NPCI_CHIP_IDS; ++i) - for (pci_index = 0; - !pcibios_find_device (PCI_VENDOR_ID_NCR, - pci_chip_ids[i].pci_device_id, pci_index, &pci_bus, - &pci_device_fn); - ++pci_index) - if (!ncr_pci_init (tpnt, BOARD_GENERIC, pci_chip_ids[i].chip, - pci_bus, pci_device_fn, /* no options */ 0)) - ++count; - } - return count; -} - -/* NCR53c810 and NCR53c820 script handling code */ - -#include "53c8xx_d.h" -#ifdef A_int_debug_sync -#define DEBUG_SYNC_INTR A_int_debug_sync -#endif -static int NCR53c8xx_script_len = sizeof (SCRIPT); -static int NCR53c8xx_dsa_len = A_dsa_end + Ent_dsa_zero - Ent_dsa_code_template; - -/* - * Function : static void NCR53c8x0_init_fixup (struct Scsi_Host *host) - * - * Purpose : copy and fixup the SCSI SCRIPTS(tm) code for this device. - * - * Inputs : host - pointer to this host adapter's structure - * - */ - -static void -NCR53c8x0_init_fixup (struct Scsi_Host *host) { - NCR53c7x0_local_declare(); - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - unsigned char tmp; - int i, ncr_to_memory, memory_to_ncr; - u32 base; - NCR53c7x0_local_setup(host); - - - /* XXX - NOTE : this code MUST be made endian aware */ - /* Copy code into buffer that was allocated at detection time. */ - memcpy ((void *) hostdata->script, (void *) SCRIPT, - sizeof(SCRIPT)); - /* Fixup labels */ - for (i = 0; i < PATCHES; ++i) - hostdata->script[LABELPATCHES[i]] += - virt_to_bus(hostdata->script); - /* Fixup addresses of constants that used to be EXTERNAL */ - - patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_abort, - virt_to_bus(&(hostdata->NCR53c7xx_msg_abort))); - patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_reject, - virt_to_bus(&(hostdata->NCR53c7xx_msg_reject))); - patch_abs_32 (hostdata->script, 0, NCR53c7xx_zero, - virt_to_bus(&(hostdata->NCR53c7xx_zero))); - patch_abs_32 (hostdata->script, 0, NCR53c7xx_sink, - virt_to_bus(&(hostdata->NCR53c7xx_sink))); - patch_abs_32 (hostdata->script, 0, NOP_insn, - virt_to_bus(&(hostdata->NOP_insn))); - patch_abs_32 (hostdata->script, 0, schedule, - virt_to_bus((void *) hostdata->schedule)); - - /* Fixup references to external variables: */ - for (i = 0; i < EXTERNAL_PATCHES_LEN; ++i) - hostdata->script[EXTERNAL_PATCHES[i].offset] += - virt_to_bus(EXTERNAL_PATCHES[i].address); - - /* - * Fixup absolutes set at boot-time. - * - * All non-code absolute variables suffixed with "dsa_" and "int_" - * are constants, and need no fixup provided the assembler has done - * it for us (I don't know what the "real" NCR assembler does in - * this case, my assembler does the right magic). - */ - - patch_abs_rwri_data (hostdata->script, 0, dsa_save_data_pointer, - Ent_dsa_code_save_data_pointer - Ent_dsa_zero); - patch_abs_rwri_data (hostdata->script, 0, dsa_restore_pointers, - Ent_dsa_code_restore_pointers - Ent_dsa_zero); - patch_abs_rwri_data (hostdata->script, 0, dsa_check_reselect, - Ent_dsa_code_check_reselect - Ent_dsa_zero); - - /* - * Just for the hell of it, preserve the settings of - * Burst Length and Enable Read Line bits from the DMODE - * register. Make sure SCRIPTS start automagically. - */ - - tmp = NCR53c7x0_read8(DMODE_REG_10); - tmp &= (DMODE_800_ERL | DMODE_BL_MASK); - - if (!(hostdata->options & OPTION_MEMORY_MAPPED)) { - base = (u32) host->io_port; - memory_to_ncr = tmp|DMODE_800_DIOM; - ncr_to_memory = tmp|DMODE_800_SIOM; - } else { - base = virt_to_bus(host->base); - memory_to_ncr = ncr_to_memory = tmp; - } - - patch_abs_32 (hostdata->script, 0, addr_scratch, base + SCRATCHA_REG_800); - patch_abs_32 (hostdata->script, 0, addr_temp, base + TEMP_REG); - - /* - * I needed some variables in the script to be accessible to - * both the NCR chip and the host processor. For these variables, - * I made the arbitrary decision to store them directly in the - * hostdata structure rather than in the RELATIVE area of the - * SCRIPTS. - */ - - - patch_abs_rwri_data (hostdata->script, 0, dmode_memory_to_memory, tmp); - patch_abs_rwri_data (hostdata->script, 0, dmode_memory_to_ncr, memory_to_ncr); - patch_abs_rwri_data (hostdata->script, 0, dmode_ncr_to_memory, ncr_to_memory); - - patch_abs_32 (hostdata->script, 0, msg_buf, - virt_to_bus((void *)&(hostdata->msg_buf))); - patch_abs_32 (hostdata->script, 0, reconnect_dsa_head, - virt_to_bus((void *)&(hostdata->reconnect_dsa_head))); - patch_abs_32 (hostdata->script, 0, addr_reconnect_dsa_head, - virt_to_bus((void *)&(hostdata->addr_reconnect_dsa_head))); - patch_abs_32 (hostdata->script, 0, reselected_identify, - virt_to_bus((void *)&(hostdata->reselected_identify))); -/* reselected_tag is currently unused */ -#if 0 - patch_abs_32 (hostdata->script, 0, reselected_tag, - virt_to_bus((void *)&(hostdata->reselected_tag))); -#endif - - patch_abs_32 (hostdata->script, 0, test_dest, - virt_to_bus((void*)&hostdata->test_dest)); - patch_abs_32 (hostdata->script, 0, test_src, - virt_to_bus(&hostdata->test_source)); - - patch_abs_rwri_data (hostdata->script, 0, dsa_check_reselect, - (unsigned char)(Ent_dsa_code_check_reselect - Ent_dsa_zero)); - -/* These are for event logging; the ncr_event enum contains the - actual interrupt numbers. */ -#ifdef A_int_EVENT_SELECT - patch_abs_32 (hostdata->script, 0, int_EVENT_SELECT, (u32) EVENT_SELECT); -#endif -#ifdef A_int_EVENT_DISCONNECT - patch_abs_32 (hostdata->script, 0, int_EVENT_DISCONNECT, (u32) EVENT_DISCONNECT); -#endif -#ifdef A_int_EVENT_RESELECT - patch_abs_32 (hostdata->script, 0, int_EVENT_RESELECT, (u32) EVENT_RESELECT); -#endif -#ifdef A_int_EVENT_COMPLETE - patch_abs_32 (hostdata->script, 0, int_EVENT_COMPLETE, (u32) EVENT_COMPLETE); -#endif -#ifdef A_int_EVENT_IDLE - patch_abs_32 (hostdata->script, 0, int_EVENT_IDLE, (u32) EVENT_IDLE); -#endif -#ifdef A_int_EVENT_SELECT_FAILED - patch_abs_32 (hostdata->script, 0, int_EVENT_SELECT_FAILED, - (u32) EVENT_SELECT_FAILED); -#endif -#ifdef A_int_EVENT_BEFORE_SELECT - patch_abs_32 (hostdata->script, 0, int_EVENT_BEFORE_SELECT, - (u32) EVENT_BEFORE_SELECT); -#endif -#ifdef A_int_EVENT_RESELECT_FAILED - patch_abs_32 (hostdata->script, 0, int_EVENT_RESELECT_FAILED, - (u32) EVENT_RESELECT_FAILED); -#endif - - /* - * Make sure the NCR and Linux code agree on the location of - * certain fields. - */ - - hostdata->E_accept_message = Ent_accept_message; - hostdata->E_command_complete = Ent_command_complete; - hostdata->E_cmdout_cmdout = Ent_cmdout_cmdout; - hostdata->E_data_transfer = Ent_data_transfer; - hostdata->E_debug_break = Ent_debug_break; - hostdata->E_dsa_code_template = Ent_dsa_code_template; - hostdata->E_dsa_code_template_end = Ent_dsa_code_template_end; - hostdata->E_end_data_transfer = Ent_end_data_transfer; - hostdata->E_initiator_abort = Ent_initiator_abort; - hostdata->E_msg_in = Ent_msg_in; - hostdata->E_other_transfer = Ent_other_transfer; - hostdata->E_other_in = Ent_other_in; - hostdata->E_other_out = Ent_other_out; - hostdata->E_reject_message = Ent_reject_message; - hostdata->E_respond_message = Ent_respond_message; - hostdata->E_select = Ent_select; - hostdata->E_select_msgout = Ent_select_msgout; - hostdata->E_target_abort = Ent_target_abort; -#ifdef Ent_test_0 - hostdata->E_test_0 = Ent_test_0; -#endif - hostdata->E_test_1 = Ent_test_1; - hostdata->E_test_2 = Ent_test_2; -#ifdef Ent_test_3 - hostdata->E_test_3 = Ent_test_3; -#endif - hostdata->E_wait_reselect = Ent_wait_reselect; - hostdata->E_dsa_code_begin = Ent_dsa_code_begin; - - hostdata->dsa_cmdout = A_dsa_cmdout; - hostdata->dsa_cmnd = A_dsa_cmnd; - hostdata->dsa_datain = A_dsa_datain; - hostdata->dsa_dataout = A_dsa_dataout; - hostdata->dsa_end = A_dsa_end; - hostdata->dsa_msgin = A_dsa_msgin; - hostdata->dsa_msgout = A_dsa_msgout; - hostdata->dsa_msgout_other = A_dsa_msgout_other; - hostdata->dsa_next = A_dsa_next; - hostdata->dsa_select = A_dsa_select; - hostdata->dsa_start = Ent_dsa_code_template - Ent_dsa_zero; - hostdata->dsa_status = A_dsa_status; - hostdata->dsa_jump_dest = Ent_dsa_code_fix_jump - Ent_dsa_zero + - 8 /* destination operand */; - - /* sanity check */ - if (A_dsa_fields_start != Ent_dsa_code_template_end - - Ent_dsa_zero) - printk("scsi%d : NCR dsa_fields start is %d not %d\n", - host->host_no, A_dsa_fields_start, Ent_dsa_code_template_end - - Ent_dsa_zero); - - printk("scsi%d : NCR code relocated to 0x%lx (virt 0x%p)\n", host->host_no, - virt_to_bus(hostdata->script), hostdata->script); -} - -/* - * Function : static int NCR53c8xx_run_tests (struct Scsi_Host *host) - * - * Purpose : run various verification tests on the NCR chip, - * including interrupt generation, and proper bus mastering - * operation. - * - * Inputs : host - a properly initialized Scsi_Host structure - * - * Preconditions : the NCR chip must be in a halted state. - * - * Returns : 0 if all tests were successful, -1 on error. - * - */ - -static int -NCR53c8xx_run_tests (struct Scsi_Host *host) { - NCR53c7x0_local_declare(); - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - unsigned long timeout; - u32 start; - int failed, i; - unsigned long flags; - NCR53c7x0_local_setup(host); - - /* The NCR chip _must_ be idle to run the test scripts */ - - save_flags(flags); - cli(); - if (!hostdata->idle) { - printk ("scsi%d : chip not idle, aborting tests\n", host->host_no); - restore_flags(flags); - return -1; - } - - /* - * Check for functional interrupts, this could work as an - * autoprobe routine. - */ - - if ((hostdata->options & OPTION_DEBUG_TEST1) && - hostdata->state != STATE_DISABLED) { - hostdata->idle = 0; - hostdata->test_running = 1; - hostdata->test_completed = -1; - hostdata->test_dest = 0; - hostdata->test_source = 0xdeadbeef; - start = virt_to_bus (hostdata->script) + hostdata->E_test_1; - hostdata->state = STATE_RUNNING; - printk ("scsi%d : test 1", host->host_no); - NCR53c7x0_write32 (DSP_REG, start); - printk (" started\n"); - sti(); - - /* - * This is currently a .5 second timeout, since (in theory) no slow - * board will take that long. In practice, we've seen one - * pentium which ocassionally fails with this, but works with - * 10 times as much? - */ - - timeout = jiffies + 5 * HZ / 10; - while ((hostdata->test_completed == -1) && jiffies < timeout) - barrier(); - - failed = 1; - if (hostdata->test_completed == -1) - printk ("scsi%d : driver test 1 timed out%s\n",host->host_no , - (hostdata->test_dest == 0xdeadbeef) ? - " due to lost interrupt.\n" - " Please verify that the correct IRQ is being used for your board,\n" - " and that the motherboard IRQ jumpering matches the PCI setup on\n" - " PCI systems.\n" - " If you are using a NCR53c810 board in a PCI system, you should\n" - " also verify that the board is jumpered to use PCI INTA, since\n" - " most PCI motherboards lack support for INTB, INTC, and INTD.\n" - : ""); - else if (hostdata->test_completed != 1) - printk ("scsi%d : test 1 bad interrupt value (%d)\n", - host->host_no, hostdata->test_completed); - else - failed = (hostdata->test_dest != 0xdeadbeef); - - if (hostdata->test_dest != 0xdeadbeef) { - printk ("scsi%d : driver test 1 read 0x%x instead of 0xdeadbeef indicating a\n" - " probable cache invalidation problem. Please configure caching\n" - " as write-through or disabled\n", - host->host_no, hostdata->test_dest); - } - - if (failed) { - printk ("scsi%d : DSP = 0x%p (script at 0x%p, start at 0x%x)\n", - host->host_no, bus_to_virt(NCR53c7x0_read32(DSP_REG)), - hostdata->script, start); - printk ("scsi%d : DSPS = 0x%x\n", host->host_no, - NCR53c7x0_read32(DSPS_REG)); - restore_flags(flags); - return -1; - } - hostdata->test_running = 0; - } - - if ((hostdata->options & OPTION_DEBUG_TEST2) && - hostdata->state != STATE_DISABLED) { - u32 dsa[48]; - unsigned char identify = IDENTIFY(0, 0); - unsigned char cmd[6]; - unsigned char data[36]; - unsigned char status = 0xff; - unsigned char msg = 0xff; - - cmd[0] = INQUIRY; - cmd[1] = cmd[2] = cmd[3] = cmd[5] = 0; - cmd[4] = sizeof(data); - - dsa[2] = 1; - dsa[3] = virt_to_bus(&identify); - dsa[4] = 6; - dsa[5] = virt_to_bus(&cmd); - dsa[6] = sizeof(data); - dsa[7] = virt_to_bus(&data); - dsa[8] = 1; - dsa[9] = virt_to_bus(&status); - dsa[10] = 1; - dsa[11] = virt_to_bus(&msg); - - for (i = 0; i < 3; ++i) { - cli(); - if (!hostdata->idle) { - printk ("scsi%d : chip not idle, aborting tests\n", host->host_no); - restore_flags(flags); - return -1; - } - - /* SCNTL3 SDID */ - dsa[0] = (0x33 << 24) | (i << 16) ; - hostdata->idle = 0; - hostdata->test_running = 2; - hostdata->test_completed = -1; - start = virt_to_bus(hostdata->script) + hostdata->E_test_2; - hostdata->state = STATE_RUNNING; - NCR53c7x0_write32 (DSA_REG, virt_to_bus(dsa)); - NCR53c7x0_write32 (DSP_REG, start); - sti(); - - timeout = jiffies + 5 * HZ; /* arbitrary */ - while ((hostdata->test_completed == -1) && jiffies < timeout) - barrier(); - NCR53c7x0_write32 (DSA_REG, 0); - - if (hostdata->test_completed == 2) { - data[35] = 0; - printk ("scsi%d : test 2 INQUIRY to target %d, lun 0 : %s\n", - host->host_no, i, data + 8); - printk ("scsi%d : status ", host->host_no); - print_status (status); - printk ("\nscsi%d : message ", host->host_no); - print_msg (&msg); - printk ("\n"); - } else if (hostdata->test_completed == 3) { - printk("scsi%d : test 2 no connection with target %d\n", - host->host_no, i); - if (!hostdata->idle) { - printk("scsi%d : not idle\n", host->host_no); - restore_flags(flags); - return -1; - } - } else if (hostdata->test_completed == -1) { - printk ("scsi%d : test 2 timed out\n", host->host_no); - restore_flags(flags); - return -1; - } - hostdata->test_running = 0; - } - } - - restore_flags(flags); - return 0; -} - -/* - * Function : static void NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd) - * - * Purpose : copy the NCR53c8xx dsa structure into cmd's dsa buffer, - * performing all necessary relocation. - * - * Inputs : cmd, a NCR53c7x0_cmd structure with a dsa area large - * enough to hold the NCR53c8xx dsa. - */ - -static void -NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd) { - Scsi_Cmnd *c = cmd->cmd; - struct Scsi_Host *host = c->host; - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - int i; - - memcpy (cmd->dsa, hostdata->script + (hostdata->E_dsa_code_template / 4), - hostdata->E_dsa_code_template_end - hostdata->E_dsa_code_template); - - /* - * Note : within the NCR 'C' code, dsa points to the _start_ - * of the DSA structure, and _not_ the offset of dsa_zero within - * that structure used to facilitate shorter signed offsets - * for the 8 bit ALU. - * - * The implications of this are that - * - * - 32 bit A_dsa_* absolute values require an additional - * dsa_zero added to their value to be correct, since they are - * relative to dsa_zero which is in essentially a separate - * space from the code symbols. - * - * - All other symbols require no special treatment. - */ - - patch_abs_tci_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32), - dsa_temp_lun, c->lun); - patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), - dsa_temp_addr_next, virt_to_bus(&cmd->dsa_next_addr)); - patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), - dsa_temp_next, virt_to_bus(cmd->dsa) + Ent_dsa_zero - - Ent_dsa_code_template + A_dsa_next); - patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), - dsa_temp_sync, virt_to_bus((void *)hostdata->sync[c->target].script)); - patch_abs_tci_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32), - dsa_temp_target, c->target); - /* XXX - new pointer stuff */ - patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), - dsa_temp_addr_saved_pointer, virt_to_bus(&cmd->saved_data_pointer)); - patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), - dsa_temp_addr_saved_residual, virt_to_bus(&cmd->saved_residual)); - patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), - dsa_temp_addr_residual, virt_to_bus(&cmd->residual)); - - /* XXX - new start stuff */ - patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), - dsa_temp_addr_dsa_value, virt_to_bus(&cmd->dsa_addr)); - -} - -/* - * Function : run_process_issue_queue (void) - * - * Purpose : insure that the coroutine is running and will process our - * request. process_issue_queue_running is checked/set here (in an - * inline function) rather than in process_issue_queue itself to reduce - * the chances of stack overflow. - * - */ - -static volatile int process_issue_queue_running = 0; - -static __inline__ void -run_process_issue_queue(void) { - unsigned long flags; - save_flags (flags); - cli(); - if (!process_issue_queue_running) { - process_issue_queue_running = 1; - process_issue_queue(flags); - /* - * process_issue_queue_running is cleared in process_issue_queue - * once it can't do more work, and process_issue_queue exits with - * interrupts disabled. - */ - } - restore_flags (flags); -} - -/* - * Function : static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int - * result) - * - * Purpose : mark SCSI command as finished, OR'ing the host portion - * of the result word into the result field of the corresponding - * Scsi_Cmnd structure, and removing it from the internal queues. - * - * Inputs : cmd - command, result - entire result field - * - * Preconditions : the NCR chip should be in a halted state when - * abnormal_finished is run, since it modifies structures which - * the NCR expects to have exclusive access to. - */ - -static void -abnormal_finished (struct NCR53c7x0_cmd *cmd, int result) { - Scsi_Cmnd *c = cmd->cmd; - struct Scsi_Host *host = c->host; - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - unsigned long flags; - int left, found; - volatile struct NCR53c7x0_cmd * linux_search; - volatile struct NCR53c7x0_cmd * volatile *linux_prev; - volatile u32 *ncr_prev, *current, ncr_search; - -#if 0 - printk ("scsi%d: abnormal finished\n", host->host_no); -#endif - - save_flags(flags); - cli(); - found = 0; - /* - * Traverse the NCR issue array until we find a match or run out - * of instructions. Instructions in the NCR issue array are - * either JUMP or NOP instructions, which are 2 words in length. - */ - - - for (found = 0, left = host->can_queue, current = hostdata->schedule; - left > 0; --left, current += 2) - { - if (issue_to_cmd (host, hostdata, (u32 *) current) == cmd) - { - current[0] = hostdata->NOP_insn; - current[1] = 0xdeadbeef; - ++found; - break; - } - } - - /* - * Traverse the NCR reconnect list of DSA structures until we find - * a pointer to this dsa or have found too many command structures. - * We let prev point at the next field of the previous element or - * head of the list, so we don't do anything different for removing - * the head element. - */ - - for (left = host->can_queue, - ncr_search = hostdata->reconnect_dsa_head, - ncr_prev = &hostdata->reconnect_dsa_head; - left >= 0 && ncr_search && - ((char*)bus_to_virt(ncr_search) + hostdata->dsa_start) - != (char *) cmd->dsa; - ncr_prev = (u32*) ((char*)bus_to_virt(ncr_search) + - hostdata->dsa_next), ncr_search = *ncr_prev, --left); - - if (left < 0) - printk("scsi%d: loop detected in ncr reonncect list\n", - host->host_no); - else if (ncr_search) - if (found) - printk("scsi%d: scsi %ld in ncr issue array and reconnect lists\n", - host->host_no, c->pid); - else { - volatile u32 * next = (u32 *) - ((char *)bus_to_virt(ncr_search) + hostdata->dsa_next); - *ncr_prev = *next; -/* If we're at the tail end of the issue queue, update that pointer too. */ - found = 1; - } - - /* - * Traverse the host running list until we find this command or discover - * we have too many elements, pointing linux_prev at the next field of the - * linux_previous element or head of the list, search at this element. - */ - - for (left = host->can_queue, linux_search = hostdata->running_list, - linux_prev = &hostdata->running_list; - left >= 0 && linux_search && linux_search != cmd; - linux_prev = &(linux_search->next), - linux_search = linux_search->next, --left); - - if (left < 0) - printk ("scsi%d: loop detected in host running list for scsi pid %ld\n", - host->host_no, c->pid); - else if (linux_search) { - *linux_prev = linux_search->next; - --hostdata->busy[c->target][c->lun]; - } - - /* Return the NCR command structure to the free list */ - cmd->next = hostdata->free; - hostdata->free = cmd; - c->host_scribble = NULL; - - /* And return */ - c->result = result; - c->scsi_done(c); - - restore_flags(flags); - run_process_issue_queue(); -} - -/* - * Function : static void intr_break (struct Scsi_Host *host, - * struct NCR53c7x0_cmd *cmd) - * - * Purpose : Handler for breakpoint interrupts from a SCSI script - * - * Inputs : host - pointer to this host adapter's structure, - * cmd - pointer to the command (if any) dsa was pointing - * to. - * - */ - -static void -intr_break (struct Scsi_Host *host, struct - NCR53c7x0_cmd *cmd) { - NCR53c7x0_local_declare(); - struct NCR53c7x0_break *bp; -#if 0 - Scsi_Cmnd *c = cmd ? cmd->cmd : NULL; -#endif - u32 *dsp; - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - unsigned long flags; - NCR53c7x0_local_setup(host); - - /* - * Find the break point corresponding to this address, and - * dump the appropriate debugging information to standard - * output. - */ - save_flags(flags); - cli(); - dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG)); - for (bp = hostdata->breakpoints; bp && bp->address != dsp; - bp = bp->next); - if (!bp) - panic("scsi%d : break point interrupt from %p with no breakpoint!", - host->host_no, dsp); - - /* - * Configure the NCR chip for manual start mode, so that we can - * point the DSP register at the instruction that follows the - * INT int_debug_break instruction. - */ - - NCR53c7x0_write8 (hostdata->dmode, - NCR53c7x0_read8(hostdata->dmode)|DMODE_MAN); - - /* - * And update the DSP register, using the size of the old - * instruction in bytes. - */ - - restore_flags(flags); -} -/* - * Function : static void print_synchronous (const char *prefix, - * const unsigned char *msg) - * - * Purpose : print a pretty, user and machine parsable representation - * of a SDTR message, including the "real" parameters, data - * clock so we can tell transfer rate at a glance. - * - * Inputs ; prefix - text to prepend, msg - SDTR message (5 bytes) - */ - -static void -print_synchronous (const char *prefix, const unsigned char *msg) { - if (msg[4]) { - int Hz = 1000000000 / (msg[3] * 4); - int integer = Hz / 1000000; - int fraction = (Hz - (integer * 1000000)) / 10000; - printk ("%speriod %dns offset %d %d.%02dMHz %s SCSI%s\n", - prefix, (int) msg[3] * 4, (int) msg[4], integer, fraction, - (((msg[3] * 4) < 200) ? "FAST" : "synchronous"), - (((msg[3] * 4) < 200) ? "-II" : "")); - } else - printk ("%sasynchronous SCSI\n", prefix); -} - -/* - * Function : static void set_synchronous (struct Scsi_Host *host, - * int target, int sxfer, int scntl3, int now_connected) - * - * Purpose : reprogram transfers between the selected SCSI initiator and - * target with the given register values; in the indirect - * select operand, reselection script, and chip registers. - * - * Inputs : host - NCR53c7,8xx SCSI host, target - number SCSI target id, - * sxfer and scntl3 - NCR registers. now_connected - if non-zero, - * we should reprogram the registers now too. - */ - -static void -set_synchronous (struct Scsi_Host *host, int target, int sxfer, int scntl3, - int now_connected) { - NCR53c7x0_local_declare(); - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - u32 *script; - NCR53c7x0_local_setup(host); - - /* These are eight bit registers */ - sxfer &= 0xff; - scntl3 &= 0xff; - - hostdata->sync[target].sxfer_sanity = sxfer; - hostdata->sync[target].scntl3_sanity = scntl3; - -/* - * HARD CODED : synchronous script is EIGHT words long. This - * must agree with 53c7.8xx.h - */ - - if ((hostdata->chip != 700) && (hostdata->chip != 70066)) { - hostdata->sync[target].select_indirect = (scntl3 << 24) | - (target << 16) | (sxfer << 8); - - script = (u32 *) hostdata->sync[target].script; - - /* XXX - add NCR53c7x0 code to reprogram SCF bits if we want to */ - if ((hostdata->chip / 100) == 8) { - script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY | - DCMD_RWRI_OP_MOVE) << 24) | - (SCNTL3_REG_800 << 16) | (scntl3 << 8); - script[1] = 0; - script += 2; - } - - script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY | - DCMD_RWRI_OP_MOVE) << 24) | - (SXFER_REG << 16) | (sxfer << 8); - script[1] = 0; - script += 2; - -#ifdef DEBUG_SYNC_INTR - if (hostdata->options & OPTION_DEBUG_DISCONNECT) { - script[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_INT) << 24) | DBC_TCI_TRUE; - script[1] = DEBUG_SYNC_INTR; - script += 2; - } -#endif - - script[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_RETURN) << 24) | DBC_TCI_TRUE; - script[1] = 0; - script += 2; - } - - if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS) - printk ("scsi%d : target %d sync parameters are sxfer=0x%x, scntl3=0x%x\n", - host->host_no, target, sxfer, scntl3); - - if (now_connected) { - if ((hostdata->chip / 100) == 8) - NCR53c7x0_write8(SCNTL3_REG_800, scntl3); - NCR53c7x0_write8(SXFER_REG, sxfer); - } -} - - -/* - * Function : static int asynchronous (struct Scsi_Host *host, int target) - * - * Purpose : reprogram between the selected SCSI Host adapter and target - * (assumed to be currently connected) for asynchronous transfers. - * - * Inputs : host - SCSI host structure, target - numeric target ID. - * - * Preconditions : the NCR chip should be in one of the halted states - */ - -static void -asynchronous (struct Scsi_Host *host, int target) { - NCR53c7x0_local_declare(); - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - NCR53c7x0_local_setup(host); - set_synchronous (host, target, /* no offset */ 0, hostdata->saved_scntl3, - 1); - printk ("scsi%d : setting target %d to asynchronous SCSI\n", - host->host_no, target); -} - -/* - * XXX - do we want to go out of our way (ie, add extra code to selection - * in the NCR53c710/NCR53c720 script) to reprogram the synchronous - * conversion bits, or can we be content in just setting the - * sxfer bits? - */ - -/* Table for NCR53c8xx synchronous values */ -static const struct { - int div; /* Total clock divisor * 10 */ - unsigned char scf; /* */ - unsigned char tp; /* 4 + tp = xferp divisor */ -} syncs[] = { -/* div scf tp div scf tp div scf tp */ - { 40, 1, 0}, { 50, 1, 1}, { 60, 1, 2}, - { 70, 1, 3}, { 75, 2, 1}, { 80, 1, 4}, - { 90, 1, 5}, { 100, 1, 6}, { 105, 2, 3}, - { 110, 1, 7}, { 120, 2, 4}, { 135, 2, 5}, - { 140, 3, 3}, { 150, 2, 6}, { 160, 3, 4}, - { 165, 2, 7}, { 180, 3, 5}, { 200, 3, 6}, - { 210, 4, 3}, { 220, 3, 7}, { 240, 4, 4}, - { 270, 4, 5}, { 300, 4, 6}, { 330, 4, 7} -}; - -/* - * Function : static void synchronous (struct Scsi_Host *host, int target, - * char *msg) - * - * Purpose : reprogram transfers between the selected SCSI initiator and - * target for synchronous SCSI transfers such that the synchronous - * offset is less than that requested and period at least as long - * as that requested. Also modify *msg such that it contains - * an appropriate response. - * - * Inputs : host - NCR53c7,8xx SCSI host, target - number SCSI target id, - * msg - synchronous transfer request. - */ - - -static void -synchronous (struct Scsi_Host *host, int target, char *msg) { - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - int desire, divisor, i, limit; - unsigned char scntl3, sxfer; -/* The diagnostic message fits on one line, even with max. width integers */ - char buf[80]; - -/* Desired transfer clock in Hz */ - desire = 1000000000L / (msg[3] * 4); -/* Scale the available SCSI clock by 10 so we get tenths */ - divisor = (hostdata->scsi_clock * 10) / desire; - -/* NCR chips can handle at most an offset of 8 */ - if (msg[4] > 8) - msg[4] = 8; - - if (hostdata->options & OPTION_DEBUG_SDTR) - printk("scsi%d : optimal synchronous divisor of %d.%01d\n", - host->host_no, divisor / 10, divisor % 10); - - limit = (sizeof(syncs) / sizeof(syncs[0]) -1); - for (i = 0; (i < limit) && (divisor > syncs[i].div); ++i); - - if (hostdata->options & OPTION_DEBUG_SDTR) - printk("scsi%d : selected synchronous divisor of %d.%01d\n", - host->host_no, syncs[i].div / 10, syncs[i].div % 10); - - msg[3] = ((1000000000L / hostdata->scsi_clock) * syncs[i].div / 10 / 4); - - if (hostdata->options & OPTION_DEBUG_SDTR) - printk("scsi%d : selected synchronous period of %dns\n", host->host_no, - msg[3] * 4); - - scntl3 = (hostdata->chip / 100 == 8) ? ((hostdata->saved_scntl3 & - ~SCNTL3_800_SCF_MASK) | (syncs[i].scf << SCNTL3_800_SCF_SHIFT)) : 0; - sxfer = (msg[4] << SXFER_MO_SHIFT) | ((syncs[i].tp) << SXFER_TP_SHIFT); - if (hostdata->options & OPTION_DEBUG_SDTR) - printk ("scsi%d : sxfer=0x%x scntl3=0x%x\n", - host->host_no, (int) sxfer, (int) scntl3); - set_synchronous (host, target, sxfer, scntl3, 1); - sprintf (buf, "scsi%d : setting target %d to ", host->host_no, target); - print_synchronous (buf, msg); -} - -/* - * Function : static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, - * struct NCR53c7x0_cmd *cmd) - * - * Purpose : Handler for INT generated instructions for the - * NCR53c810/820 SCSI SCRIPT - * - * Inputs : host - pointer to this host adapter's structure, - * cmd - pointer to the command (if any) dsa was pointing - * to. - * - */ - -static int -NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct - NCR53c7x0_cmd *cmd) { - NCR53c7x0_local_declare(); - int print; - Scsi_Cmnd *c = cmd ? cmd->cmd : NULL; - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - u32 dsps,*dsp; /* Argument of the INT instruction */ - NCR53c7x0_local_setup(host); - dsps = NCR53c7x0_read32(DSPS_REG); - dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG)); - - if (hostdata->options & OPTION_DEBUG_INTR) - printk ("scsi%d : DSPS = 0x%x\n", host->host_no, dsps); - - switch (dsps) { - case A_int_msg_1: - print = 1; - switch (hostdata->msg_buf[0]) { - /* - * Unless we've initiated synchronous negotiation, I don't - * think that this should happen. - */ - case MESSAGE_REJECT: - hostdata->dsp = hostdata->script + hostdata->E_accept_message / - sizeof(u32); - hostdata->dsp_changed = 1; - if (cmd && (cmd->flags & CMD_FLAG_SDTR)) { - printk ("scsi%d : target %d rejected SDTR\n", host->host_no, - c->target); - cmd->flags &= ~CMD_FLAG_SDTR; - asynchronous (host, c->target); - print = 0; - } - break; - case INITIATE_RECOVERY: - printk ("scsi%d : extended contingent allegiance not supported yet, rejecting\n", - host->host_no); - /* Fall through to default */ - hostdata->dsp = hostdata->script + hostdata->E_reject_message / - sizeof(u32); - hostdata->dsp_changed = 1; - break; - default: - printk ("scsi%d : unsupported message, resjecting\n", - host->host_no); - hostdata->dsp = hostdata->script + hostdata->E_reject_message / - sizeof(u32); - hostdata->dsp_changed = 1; - } - if (print) { - printk ("scsi%d : received message", host->host_no); - if (c) - printk (" from target %d lun %d ", c->target, c->lun); - print_msg ((unsigned char *) hostdata->msg_buf); - printk("\n"); - } - - return SPECIFIC_INT_NOTHING; - - - case A_int_msg_sdtr: -/* - * At this point, hostdata->msg_buf contains - * 0 EXTENDED MESSAGE - * 1 length - * 2 SDTR - * 3 period * 4ns - * 4 offset - */ - - if (cmd) { - char buf[80]; - sprintf (buf, "scsi%d : target %d %s ", host->host_no, c->target, - (cmd->flags & CMD_FLAG_SDTR) ? "accepting" : "requesting"); - print_synchronous (buf, (unsigned char *) hostdata->msg_buf); - - /* - * Initiator initiated, won't happen unless synchronous - * transfers are enabled. If we get a SDTR message in - * response to our SDTR, we should program our parameters - * such that - * offset <= requested offset - * period >= requested period - */ - if (cmd->flags & CMD_FLAG_SDTR) { - cmd->flags &= ~CMD_FLAG_SDTR; - if (hostdata->msg_buf[4]) - synchronous (host, c->target, (unsigned char *) - hostdata->msg_buf); - else - asynchronous (host, c->target); - hostdata->dsp = hostdata->script + hostdata->E_accept_message / - sizeof(u32); - hostdata->dsp_changed = 1; - return SPECIFIC_INT_NOTHING; - } else { - if (hostdata->options & OPTION_SYNCHRONOUS) { - cmd->flags |= CMD_FLAG_DID_SDTR; - synchronous (host, c->target, (unsigned char *) - hostdata->msg_buf); - } else { - hostdata->msg_buf[4] = 0; /* 0 offset = async */ - asynchronous (host, c->target); - } - patch_dsa_32 (cmd->dsa, dsa_msgout_other, 0, 5); - patch_dsa_32 (cmd->dsa, dsa_msgout_other, 1, (u32) - virt_to_bus ((void *)&hostdata->msg_buf)); - hostdata->dsp = hostdata->script + - hostdata->E_respond_message / sizeof(u32); - hostdata->dsp_changed = 1; - } - return SPECIFIC_INT_NOTHING; - } - /* Fall through to abort if we couldn't find a cmd, and - therefore a dsa structure to twiddle */ - case A_int_msg_wdtr: - hostdata->dsp = hostdata->script + hostdata->E_reject_message / - sizeof(u32); - hostdata->dsp_changed = 1; - return SPECIFIC_INT_NOTHING; - case A_int_err_unexpected_phase: - if (hostdata->options & OPTION_DEBUG_INTR) - printk ("scsi%d : unexpected phase\n", host->host_no); - return SPECIFIC_INT_ABORT; - case A_int_err_selected: - printk ("scsi%d : selected by target %d\n", host->host_no, - (int) NCR53c7x0_read8(SDID_REG_800) &7); - hostdata->dsp = hostdata->script + hostdata->E_target_abort / - sizeof(u32); - hostdata->dsp_changed = 1; - return SPECIFIC_INT_NOTHING; - case A_int_err_unexpected_reselect: - printk ("scsi%d : unexpected reselect by target %d lun %d\n", - host->host_no, (int) NCR53c7x0_read8(SDID_REG_800) & 7, - hostdata->reselected_identify & 7); - hostdata->dsp = hostdata->script + hostdata->E_initiator_abort / - sizeof(u32); - hostdata->dsp_changed = 1; - return SPECIFIC_INT_NOTHING; -/* - * Since contingent allegiance conditions are cleared by the next - * command issued to a target, we must issue a REQUEST SENSE - * command after receiving a CHECK CONDITION status, before - * another command is issued. - * - * Since this NCR53c7x0_cmd will be freed after use, we don't - * care if we step on the various fields, so modify a few things. - */ - case A_int_err_check_condition: -#if 0 - if (hostdata->options & OPTION_DEBUG_INTR) -#endif - printk ("scsi%d : CHECK CONDITION\n", host->host_no); - if (!c) { - printk("scsi%d : CHECK CONDITION with no SCSI command\n", - host->host_no); - return SPECIFIC_INT_PANIC; - } - - /* - * FIXME : this uses the normal one-byte selection message. - * We may want to renegotiate for synchronous & WIDE transfers - * since these could be the crux of our problem. - * - hostdata->NOP_insn* FIXME : once SCSI-II tagged queuing is implemented, we'll - * have to set this up so that the rest of the DSA - * agrees with this being an untagged queue'd command. - */ - - patch_dsa_32 (cmd->dsa, dsa_msgout, 0, 1); - - /* - * Modify the table indirect for COMMAND OUT phase, since - * Request Sense is a six byte command. - */ - - patch_dsa_32 (cmd->dsa, dsa_cmdout, 0, 6); - - c->cmnd[0] = REQUEST_SENSE; - c->cmnd[1] &= 0xe0; /* Zero all but LUN */ - c->cmnd[2] = 0; - c->cmnd[3] = 0; - c->cmnd[4] = sizeof(c->sense_buffer); - c->cmnd[5] = 0; - - /* - * Disable dataout phase, and program datain to transfer to the - * sense buffer, and add a jump to other_transfer after the - * command so overflow/underrun conditions are detected. - */ - - patch_dsa_32 (cmd->dsa, dsa_dataout, 0, - virt_to_bus(hostdata->script) + hostdata->E_other_transfer); - patch_dsa_32 (cmd->dsa, dsa_datain, 0, - virt_to_bus(cmd->data_transfer_start)); - cmd->data_transfer_start[0] = (((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | - DCMD_BMI_IO)) << 24) | sizeof(c->sense_buffer); - cmd->data_transfer_start[1] = (u32) virt_to_bus(c->sense_buffer); - - cmd->data_transfer_start[2] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) - << 24) | DBC_TCI_TRUE; - cmd->data_transfer_start[3] = (u32) virt_to_bus(hostdata->script) + - hostdata->E_other_transfer; - - /* - * Currently, this command is flagged as completed, ie - * it has valid status and message data. Reflag it as - * incomplete. Q - need to do something so that original - * status, etc are used. - */ - - cmd->cmd->result = 0xffff; - - /* - * Restart command as a REQUEST SENSE. - */ - hostdata->dsp = (u32 *) hostdata->script + hostdata->E_select / - sizeof(u32); - hostdata->dsp_changed = 1; - return SPECIFIC_INT_NOTHING; - case A_int_debug_break: - return SPECIFIC_INT_BREAK; - case A_int_norm_aborted: - hostdata->dsp = (u32 *) hostdata->schedule; - hostdata->dsp_changed = 1; - if (cmd) - abnormal_finished (cmd, DID_ERROR << 16); - return SPECIFIC_INT_NOTHING; - case A_int_test_1: - case A_int_test_2: - hostdata->idle = 1; - hostdata->test_completed = (dsps - A_int_test_1) / 0x00010000 + 1; - if (hostdata->options & OPTION_DEBUG_INTR) - printk("scsi%d : test%d complete\n", host->host_no, - hostdata->test_completed); - return SPECIFIC_INT_NOTHING; -#ifdef A_int_debug_reselected_ok - case A_int_debug_reselected_ok: - if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| - OPTION_DEBUG_DISCONNECT)) { - /* - * Note - this dsa is not based on location relative to - * the command structure, but to location relative to the - * DSA register - */ - u32 *dsa; - dsa = (u32 *) bus_to_virt (NCR53c7x0_read32(DSA_REG)); - - printk("scsi%d : reselected_ok (DSA = 0x%x (virt 0x%p)\n", - host->host_no, NCR53c7x0_read32(DSA_REG), dsa); - printk("scsi%d : resume address is 0x%x (virt 0x%p)\n", - host->host_no, cmd->saved_data_pointer, - bus_to_virt(cmd->saved_data_pointer)); - print_insn (host, hostdata->script + Ent_reselected_ok / - sizeof(u32), "", 1); - printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n", - host->host_no, NCR53c7x0_read8(SXFER_REG), - NCR53c7x0_read8(SCNTL3_REG_800)); - if (c) { - print_insn (host, (u32 *) - hostdata->sync[c->target].script, "", 1); - print_insn (host, (u32 *) - hostdata->sync[c->target].script + 2, "", 1); - } - } - return SPECIFIC_INT_RESTART; -#endif -#ifdef A_int_debug_reselect_check - case A_int_debug_reselect_check: - if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { - u32 *dsa; -#if 0 - u32 *code; -#endif - /* - * Note - this dsa is not based on location relative to - * the command structure, but to location relative to the - * DSA register - */ - dsa = bus_to_virt (NCR53c7x0_read32(DSA_REG)); - printk("scsi%d : reselected_check_next (DSA = 0x%lx (virt 0x%p))\n", - host->host_no, virt_to_bus(dsa), dsa); - if (dsa) { - printk("scsi%d : resume address is 0x%x (virt 0x%p)\n", - host->host_no, cmd->saved_data_pointer, - bus_to_virt (cmd->saved_data_pointer)); -#if 0 - printk("scsi%d : template code :\n", host->host_no); - for (code = dsa + (Ent_dsa_code_check_reselect - Ent_dsa_zero) - / sizeof(u32); code < (dsa + Ent_dsa_zero / sizeof(u32)); - code += print_insn (host, code, "", 1)); -#endif - } - print_insn (host, hostdata->script + Ent_reselected_ok / - sizeof(u32), "", 1); - } - return SPECIFIC_INT_RESTART; -#endif -#ifdef A_int_debug_dsa_schedule - case A_int_debug_dsa_schedule: - if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { - u32 *dsa; - /* - * Note - this dsa is not based on location relative to - * the command structure, but to location relative to the - * DSA register - */ - dsa = (u32 *) bus_to_virt (NCR53c7x0_read32(DSA_REG)); - printk("scsi%d : dsa_schedule (old DSA = 0x%lx (virt 0x%p))\n", - host->host_no, virt_to_bus(dsa), dsa); - if (dsa) - printk("scsi%d : resume address is 0x%x (virt 0x%p)\n" - " (temp was 0x%x (virt 0x%p))\n", - host->host_no, cmd->saved_data_pointer, - bus_to_virt (cmd->saved_data_pointer), - NCR53c7x0_read32 (TEMP_REG), - bus_to_virt (NCR53c7x0_read32(TEMP_REG))); - } - return SPECIFIC_INT_RESTART; -#endif -#ifdef A_int_debug_scheduled - case A_int_debug_scheduled: - if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { - printk("scsi%d : new I/O 0x%x (virt 0x%p) scheduled\n", - host->host_no, NCR53c7x0_read32(DSA_REG), - bus_to_virt(NCR53c7x0_read32(DSA_REG))); - } - return SPECIFIC_INT_RESTART; -#endif -#ifdef A_int_debug_idle - case A_int_debug_idle: - if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { - printk("scsi%d : idle\n", host->host_no); - } - return SPECIFIC_INT_RESTART; -#endif -#ifdef A_int_debug_cmd - case A_int_debug_cmd: - if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { - printk("scsi%d : command sent\n"); - } - return SPECIFIC_INT_RESTART; -#endif -#ifdef A_int_debug_dsa_loaded - case A_int_debug_dsa_loaded: - if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { - printk("scsi%d : DSA loaded with 0x%x (virt 0x%p)\n", host->host_no, - NCR53c7x0_read32(DSA_REG), - bus_to_virt(NCR53c7x0_read32(DSA_REG))); - } - return SPECIFIC_INT_RESTART; -#endif -#ifdef A_int_debug_reselected - case A_int_debug_reselected: - if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| - OPTION_DEBUG_DISCONNECT)) { - printk("scsi%d : reselected by target %d lun %d\n", - host->host_no, (int) NCR53c7x0_read8(SDID_REG_800) & ~0x80, - (int) hostdata->reselected_identify & 7); - print_queues(host); - } - return SPECIFIC_INT_RESTART; -#endif -#ifdef A_int_debug_disconnect_msg - case A_int_debug_disconnect_msg: - if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { - if (c) - printk("scsi%d : target %d lun %d disconnecting\n", - host->host_no, c->target, c->lun); - else - printk("scsi%d : unknown target disconnecting\n", - host->host_no); - } - return SPECIFIC_INT_RESTART; -#endif -#ifdef A_int_debug_disconnected - case A_int_debug_disconnected: - if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| - OPTION_DEBUG_DISCONNECT)) { - printk ("scsi%d : disconnected, new queues are\n", - host->host_no); - print_queues(host); -#if 0 - printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n", - host->host_no, NCR53c7x0_read8(SXFER_REG), - NCR53c7x0_read8(SCNTL3_REG_800)); -#endif - if (c) { - print_insn (host, (u32 *) - hostdata->sync[c->target].script, "", 1); - print_insn (host, (u32 *) - hostdata->sync[c->target].script + 2, "", 1); - } - } - return SPECIFIC_INT_RESTART; -#endif -#ifdef A_int_debug_panic - case A_int_debug_panic: - printk("scsi%d : int_debug_panic received\n", host->host_no); - print_lots (host); - return SPECIFIC_INT_PANIC; -#endif -#ifdef A_int_debug_saved - case A_int_debug_saved: - if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| - OPTION_DEBUG_DISCONNECT)) { - printk ("scsi%d : saved data pointer 0x%x (virt 0x%p)\n", - host->host_no, cmd->saved_data_pointer, - bus_to_virt (cmd->saved_data_pointer)); - print_progress (c); - } - return SPECIFIC_INT_RESTART; -#endif -#ifdef A_int_debug_restored - case A_int_debug_restored: - if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| - OPTION_DEBUG_DISCONNECT)) { - if (cmd) { - int size; - printk ("scsi%d : restored data pointer 0x%x (virt 0x%p)\n", - host->host_no, cmd->saved_data_pointer, bus_to_virt ( - cmd->saved_data_pointer)); - size = print_insn (host, (u32 *) - bus_to_virt(cmd->saved_data_pointer), "", 1); - size = print_insn (host, (u32 *) - bus_to_virt(cmd->saved_data_pointer) + size, "", 1); - print_progress (c); - } -#if 0 - printk ("scsi%d : datapath residual %d\n", - host->host_no, datapath_residual (host)) ; -#endif - } - return SPECIFIC_INT_RESTART; -#endif -#ifdef A_int_debug_sync - case A_int_debug_sync: - if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| - OPTION_DEBUG_DISCONNECT|OPTION_DEBUG_SDTR)) { - unsigned char sxfer = NCR53c7x0_read8 (SXFER_REG), - scntl3 = NCR53c7x0_read8 (SCNTL3_REG_800); - if (c) { - if (sxfer != hostdata->sync[c->target].sxfer_sanity || - scntl3 != hostdata->sync[c->target].scntl3_sanity) { - printk ("scsi%d : sync sanity check failed sxfer=0x%x, scntl3=0x%x", - host->host_no, sxfer, scntl3); - NCR53c7x0_write8 (SXFER_REG, sxfer); - NCR53c7x0_write8 (SCNTL3_REG_800, scntl3); - } - } else - printk ("scsi%d : unknown command sxfer=0x%x, scntl3=0x%x\n", - host->host_no, (int) sxfer, (int) scntl3); - } - return SPECIFIC_INT_RESTART; -#endif -#ifdef A_int_debug_datain - case A_int_debug_datain: - if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| - OPTION_DEBUG_DISCONNECT|OPTION_DEBUG_SDTR)) { - int size; - printk ("scsi%d : In do_datain (%s) sxfer=0x%x, scntl3=0x%x\n" - " datapath residual=%d\n", - host->host_no, sbcl_to_phase (NCR53c7x0_read8 (SBCL_REG)), - (int) NCR53c7x0_read8(SXFER_REG), - (int) NCR53c7x0_read8(SCNTL3_REG_800), - datapath_residual (host)) ; - print_insn (host, dsp, "", 1); - size = print_insn (host, (u32 *) bus_to_virt(dsp[1]), "", 1); - print_insn (host, (u32 *) bus_to_virt(dsp[1]) + size, "", 1); - } - return SPECIFIC_INT_RESTART; -#endif -/* - * FIXME : for 7xx support, we need to read SDID_REG_700 and handle - * the comparison as bitfielded, not binary. - */ -#ifdef A_int_debug_check_dsa - case A_int_debug_check_dsa: - if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) { - int sdid = NCR53c7x0_read8 (SDID_REG_800) & 15; - char *where = dsp - NCR53c7x0_insn_size(NCR53c7x0_read8 - (DCMD_REG)) == hostdata->script + - Ent_select_check_dsa / sizeof(u32) ? - "selection" : "reselection"; - if (c && sdid != c->target) { - printk ("scsi%d : SDID target %d != DSA target %d at %s\n", - host->host_no, sdid, c->target, where); - print_lots(host); - dump_events (host, 20); - return SPECIFIC_INT_PANIC; - } - } - return SPECIFIC_INT_RESTART; -#endif - default: - if ((dsps & 0xff000000) == 0x03000000) { - printk ("scsi%d : misc debug interrupt 0x%x\n", - host->host_no, dsps); - return SPECIFIC_INT_RESTART; - } else if ((dsps & 0xff000000) == 0x05000000) { - if (hostdata->events) { - struct NCR53c7x0_event *event; - ++hostdata->event_index; - if (hostdata->event_index >= hostdata->event_size) - hostdata->event_index = 0; - event = (struct NCR53c7x0_event *) hostdata->events + - hostdata->event_index; - event->event = (enum ncr_event) dsps; - event->dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG)); - /* FIXME : this needs to change for the '7xx family */ - if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) - event->target = NCR53c7x0_read8(SSID_REG_800); - else - event->target = 255; - - if (event->event == EVENT_RESELECT) - event->lun = hostdata->reselected_identify & 0xf; - else if (c) - event->lun = c->lun; - else - event->lun = 255; - do_gettimeofday(&(event->time)); - if (c) { - event->pid = c->pid; - memcpy ((void *) event->cmnd, (void *) c->cmnd, - sizeof (event->cmnd)); - } else { - event->pid = -1; - } - } - return SPECIFIC_INT_RESTART; - } - - printk ("scsi%d : unknown user interrupt 0x%x\n", - host->host_no, (unsigned) dsps); - return SPECIFIC_INT_PANIC; - } -} - -/* - * XXX - the stock NCR assembler won't output the scriptu.h file, - * which undefine's all #define'd CPP symbols from the script.h - * file, which will create problems if you use multiple scripts - * with the same symbol names. - * - * If you insist on using NCR's assembler, you could generate - * scriptu.h from script.h using something like - * - * grep #define script.h | \ - * sed 's/#define[ ][ ]*\([_a-zA-Z][_a-zA-Z0-9]*\).*$/#undefine \1/' \ - * > scriptu.h - */ - -#include "53c8xx_u.h" - -/* XXX - add alternate script handling code here */ - - -#ifdef NCR_DEBUG -/* - * Debugging without a debugger is no fun. So, I've provided - * a debugging interface in the NCR53c7x0 driver. To avoid - * kernel cruft, there's just enough here to act as an interface - * to a user level debugger (aka, GDB). - * - * - * The following restrictions apply to debugger commands : - * 1. The command must be terminated by a newline. - * 2. Command length must be less than 80 bytes including the - * newline. - * 3. The entire command must be written with one system call. - */ - -static const char debugger_help = -"bc - clear breakpoint\n" -"bl - list breakpoints\n" -"bs - set breakpoint\n" -"g - start\n" -"h - halt\n" -"? - this message\n" -"i - info\n" -"mp - print memory\n" -"ms - store memory\n" -"rp - print register\n" -"rs - store register\n" -"s - single step\n" -"tb - begin trace \n" -"te - end trace\n"; - -/* - * Whenever we change a break point, we should probably - * set the NCR up so that it is in a single step mode. - */ - -static int debugger_fn_bc (struct Scsi_Host *host, struct debugger_token *token, - u32 args[]) { - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - instance->hostdata; - struct NCR53c7x0_break *bp, **prev; - unsigned long flags; - save_flags(flags); - cli(); - for (bp = (struct NCR53c7x0_break *) instance->breakpoints, - prev = (struct NCR53c7x0_break **) &instance->breakpoints; - bp; prev = (struct NCR53c7x0_break **) &(bp->next), - bp = (struct NCR53c7x0_break *) bp->next); - - if (!bp) { - restore_flags(flags); - return -EIO; - } - - /* - * XXX - we need to insure that the processor is halted - * here in order to prevent a race condition. - */ - - memcpy ((void *) bp->addr, (void *) bp->old, sizeof(bp->old)); - if (prev) - *prev = bp->next; - - restore_flags(flags); - return 0; -} - - -static int -debugger_fn_bl (struct Scsi_Host *host, struct debugger_token *token, - u32 args[]) { - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - struct NCR53c7x0_break *bp; - char buf[80]; - size_t len; - unsigned long flags; - /* - * XXX - we need to insure that the processor is halted - * here in order to prevent a race condition. So, if the - * processor isn't halted, print an error message and continue. - */ - - sprintf (buf, "scsi%d : bp : warning : processor not halted\b", - host->host_no); - debugger_kernel_write (host, buf, strlen(buf)); - - save_flags(flags); - cli(); - for (bp = (struct NCR53c7x0_break *) host->breakpoints; - bp; bp = (struct NCR53c7x0_break *) bp->next); { - sprintf (buf, "scsi%d : bp : success : at %08x, replaces %08x %08x", - bp->addr, bp->old[0], bp->old[1]); - len = strlen(buf); - if ((bp->old[0] & (DCMD_TYPE_MASK << 24)) == - (DCMD_TYPE_MMI << 24)) { - sprintf(buf + len, "%08x\n", * (u32 *) bp->addr); - } else { - sprintf(buf + len, "\n"); - } - len = strlen(buf); - debugger_kernel_write (host, buf, len); - } - restore_flags(flags); - return 0; -} - -static int -debugger_fn_bs (struct Scsi_Host *host, struct debugger_token *token, - u32 args[]) { - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - struct NCR53c7x0_break *bp; - char buf[80]; - size_t len; - unsigned long flags; - save_flags(flags); - cli(); - - if (hostdata->state != STATE_HALTED) { - sprintf (buf, "scsi%d : bs : failure : NCR not halted\n", host->host_no); - debugger_kernel_write (host, buf, strlen(buf)); - restore_flags(flags); - return -1; - } - - if (!(bp = kmalloc (sizeof (struct NCR53c7x0_break)))) { - printk ("scsi%d : kmalloc(%d) of breakpoint structure failed, try again\n", - host->host_no, sizeof(struct NCR53c7x0_break)); - restore_flags(flags); - return -1; - } - - bp->address = (u32 *) args[0]; - memcpy ((void *) bp->old_instruction, (void *) bp->address, 8); - bp->old_size = (((bp->old_instruction[0] >> 24) & DCMD_TYPE_MASK) == - DCMD_TYPE_MMI ? 3 : 2; - bp->next = hostdata->breakpoints; - hostdata->breakpoints = bp->next; - memcpy ((void *) bp->address, (void *) hostdata->E_debug_break, 8); - - restore_flags(flags); - return 0; -} - -#define TOKEN(name,nargs) {#name, nargs, debugger_fn_##name} -static const struct debugger_token { - char *name; - int numargs; - int (*fn)(struct debugger_token *token, u32 args[]); -} debugger_tokens[] = { - TOKEN(bc,1), TOKEN(bl,0), TOKEN(bs,1), TOKEN(g,0), TOKEN(halt,0), - {DT_help, "?", 0} , TOKEN(h,0), TOKEN(i,0), TOKEN(mp,2), - TOKEN(ms,3), TOKEN(rp,2), TOKEN(rs,2), TOKEN(s,0), TOKEN(tb,0), TOKEN(te,0) -}; - -#define NDT sizeof(debugger_tokens / sizeof(struct debugger_token)) - -static struct Scsi_Host * inode_to_host (struct inode *inode) { - int dev; - struct Scsi_Host *tmp; - for (dev = MINOR(inode->rdev), host = first_host; - (host->hostt == the_template); --dev, host = host->next) - if (!dev) return host; - return NULL; -} - - -static int -debugger_user_write (struct inode *inode,struct file *filp, - char *buf,int count) { - struct Scsi_Host *host; /* This SCSI host */ - struct NCR53c7x0_hostadata *hostdata; - char input_buf[80], /* Kernel space copy of buf */ - *ptr; /* Pointer to argument list */ - u32 args[3]; /* Arguments */ - int i, j, error, len; - - if (!(host = inode_to_host(inode))) - return -ENXIO; - - hostdata = (struct NCR53c7x0_hostdata *) host->hostdata; - - if (error = verify_area(VERIFY_READ,buf,count)) - return error; - - if (count > 80) - return -EIO; - - memcpy_from_fs(input_buf, buf, count); - - if (input_buf[count - 1] != '\n') - return -EIO; - - input_buf[count - 1]=0; - - for (i = 0; i < NDT; ++i) { - len = strlen (debugger_tokens[i].name); - if (!strncmp(input_buf, debugger_tokens[i].name, len)) - break; - }; - - if (i == NDT) - return -EIO; - - for (ptr = input_buf + len, j = 0; j < debugger_tokens[i].nargs && *ptr;) { - if (*ptr == ' ' || *ptr == '\t') { - ++ptr; - } else if (isdigit(*ptr)) { - args[j++] = simple_strtoul (ptr, &ptr, 0); - } else { - return -EIO; - } - } - - if (j != debugger_tokens[i].nargs) - return -EIO; - - return count; -} - -static int -debugger_user_read (struct inode *inode,struct file *filp, - char *buf,int count) { - struct Scsi_Host *instance; - -} - -static int -debugger_kernel_write (struct Scsi_Host *host, char *buf, size_t - buflen) { - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - int copy, left; - unsigned long flags; - save_flags(flags); - cli(); - while (buflen) { - left = (hostdata->debug_buf + hostdata->debug_size - 1) - - hostdata->debug_write; - copy = (buflen <= left) ? buflen : left; - memcpy (hostdata->debug_write, buf, copy); - buf += copy; - buflen -= copy; - hostdata->debug_count += copy; - if ((hostdata->debug_write += copy) == - (hostdata->debug_buf + hostdata->debug_size)) - hosdata->debug_write = hostdata->debug_buf; - } - restore_flags(flags); -} - -#endif /* def NCRDEBUG */ - -/* - * Function : static void NCR538xx_soft_reset (struct Scsi_Host *host) - * - * Purpose : perform a soft reset of the NCR53c8xx chip - * - * Inputs : host - pointer to this host adapter's structure - * - * Preconditions : NCR53c7x0_init must have been called for this - * host. - * - */ - -static void -NCR53c8x0_soft_reset (struct Scsi_Host *host) { - NCR53c7x0_local_declare(); - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - NCR53c7x0_local_setup(host); - - - /* - * Do a soft reset of the chip so that everything is - * reinitialized to the power-on state. - * - * Basically follow the procedure outlined in the NCR53c700 - * data manual under Chapter Six, How to Use, Steps Necessary to - * Start SCRIPTS, with the exception of actually starting the - * script and setting up the synchronous transfer gunk. - */ - - NCR53c7x0_write8(ISTAT_REG_800, ISTAT_10_SRST); - NCR53c7x0_write8(ISTAT_REG_800, 0); - NCR53c7x0_write8(hostdata->dmode, hostdata->saved_dmode & ~DMODE_MAN); - - - /* - * Respond to reselection by targets and use our _initiator_ SCSI ID - * for arbitration. If notyet, also respond to SCSI selection. - * - * XXX - Note : we must reprogram this when reselecting as - * a target. - */ - -#ifdef notyet - NCR53c7x0_write8(SCID_REG, (host->this_id & 7)|SCID_800_RRE|SCID_800_SRE); -#else - NCR53c7x0_write8(SCID_REG, (host->this_id & 7)|SCID_800_RRE); -#endif - NCR53c7x0_write8(RESPID_REG_800, hostdata->this_id_mask); - - /* - * Use a maximum (1.6) second handshake to handshake timeout, - * and SCSI recommended .5s selection timeout. - */ - - /* - * The new gcc won't recognize preprocessing directives - * within macro args. - */ -#if 0 - NCR53c7x0_write8(STIME0_REG_800, - ((selection_timeout << STIME0_800_SEL_SHIFT) & STIME0_800_SEL_MASK) - | ((15 << STIME0_800_HTH_SHIFT) & STIME0_800_HTH_MASK)); -#else -/* Disable HTH interrupt */ - NCR53c7x0_write8(STIME0_REG_800, - ((selection_timeout << STIME0_800_SEL_SHIFT) & STIME0_800_SEL_MASK)); -#endif - - - /* - * Enable active negation for happy synchronous transfers. - */ - - NCR53c7x0_write8(STEST3_REG_800, STEST3_800_TE); - - /* - * Enable all interrupts, except parity which we only want when - * the user requests it. - */ - - NCR53c7x0_write8(DIEN_REG, DIEN_800_MDPE | DIEN_800_BF | - DIEN_ABRT | DIEN_SSI | DIEN_SIR | DIEN_800_IID); - - - NCR53c7x0_write8(SIEN0_REG_800, ((hostdata->options & OPTION_PARITY) ? - SIEN_PAR : 0) | SIEN_RST | SIEN_UDC | SIEN_SGE | SIEN_MA); - NCR53c7x0_write8(SIEN1_REG_800, SIEN1_800_STO | SIEN1_800_HTH); - - /* - * Use saved clock frequency divisor and scripts loaded in 16 bit - * mode flags from the saved dcntl. - */ - - NCR53c7x0_write8(DCNTL_REG, hostdata->saved_dcntl); - NCR53c7x0_write8(CTEST4_REG_800, hostdata->saved_ctest4); - - /* Enable active negation */ - NCR53c7x0_write8(STEST3_REG_800, STEST3_800_TE); -} - -/* - * Function static struct NCR53c7x0_cmd *allocate_cmd (Scsi_Cmnd *cmd) - * - * Purpose : Return the first free NCR53c7x0_cmd structure (which are - * reused in a LIFO maner to minimize cache thrashing). - * - * Side effects : If we haven't yet scheduled allocation of NCR53c7x0_cmd - * structures for this device, do so. Attempt to complete all scheduled - * allocations using kmalloc(), putting NCR53c7x0_cmd structures on - * the free list. Teach programmers not to drink and hack. - * - * Inputs : cmd - SCSI command - * - * Returns : NCR53c7x0_cmd structure allocated on behalf of cmd; - * NULL on failure. - */ - -static struct NCR53c7x0_cmd * -allocate_cmd (Scsi_Cmnd *cmd) { - struct Scsi_Host *host = cmd->host; - struct NCR53c7x0_hostdata *hostdata = - (struct NCR53c7x0_hostdata *) host->hostdata; - void *real; /* Real address */ - int size; /* Size of *tmp */ - struct NCR53c7x0_cmd *tmp; - unsigned long flags; - - if (hostdata->options & OPTION_DEBUG_ALLOCATION) - printk ("scsi%d : num_cmds = %d, can_queue = %d\n" - " target = %d, lun = %d, %s\n", - host->host_no, hostdata->num_cmds, host->can_queue, - cmd->target, cmd->lun, (hostdata->cmd_allocated[cmd->target] & - (1 << cmd->lun)) ? "allready allocated" : "not allocated"); - -/* - * If we have not yet reserved commands for this I_T_L nexus, and - * the device exists (as indicated by permanant Scsi_Cmnd structures - * being allocated under 1.3.x, or being outside of scan_scsis in - * 1.2.x), do so now. - */ - if (!(hostdata->cmd_allocated[cmd->target] & (1 << cmd->lun)) && -#ifdef LINUX_1_2 - !in_scan_scsis -#else - cmd->device && cmd->device->has_cmdblocks -#endif - ) { - if ((hostdata->extra_allocate + hostdata->num_cmds) < host->can_queue) - hostdata->extra_allocate += host->cmd_per_lun; - hostdata->cmd_allocated[cmd->target] |= (1 << cmd->lun); - } - - for (; hostdata->extra_allocate > 0 ; --hostdata->extra_allocate, - ++hostdata->num_cmds) { - /* historically, kmalloc has returned unaligned addresses; pad so we - have enough room to ROUNDUP */ - size = hostdata->max_cmd_size + sizeof (void *); -/* FIXME: for ISA bus '7xx chips, we need to or GFP_DMA in here */ - real = kmalloc (size, GFP_ATOMIC); - if (!real) { - if (hostdata->options & OPTION_DEBUG_ALLOCATION) - printk ("scsi%d : kmalloc(%d) failed\n", - host->host_no, size); - break; - } - tmp = ROUNDUP(real, void *); - tmp->real = real; - tmp->size = size; -#ifdef LINUX_1_2 - tmp->free = ((void (*)(void *, int)) kfree_s); -#else - tmp->free = ((void (*)(void *, int)) kfree); -#endif - save_flags (flags); - cli(); - tmp->next = hostdata->free; - hostdata->free = tmp; - restore_flags (flags); - } - save_flags(flags); - cli(); - tmp = (struct NCR53c7x0_cmd *) hostdata->free; - if (tmp) { - hostdata->free = tmp->next; - } - restore_flags(flags); - if (!tmp) - printk ("scsi%d : can't allocate command for target %d lun %d\n", - host->host_no, cmd->target, cmd->lun); - return tmp; -} - -/* - * Function static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd) - * - * - * Purpose : allocate a NCR53c7x0_cmd structure, initialize it based on the - * Scsi_Cmnd structure passed in cmd, including dsa and Linux field - * initialization, and dsa code relocation. - * - * Inputs : cmd - SCSI command - * - * Returns : NCR53c7x0_cmd structure corresponding to cmd, - * NULL on failure. - */ - -static struct NCR53c7x0_cmd * -create_cmd (Scsi_Cmnd *cmd) { - NCR53c7x0_local_declare(); - struct Scsi_Host *host = cmd->host; - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - struct NCR53c7x0_cmd *tmp; /* NCR53c7x0_cmd structure for this command */ - int datain, /* Number of instructions per phase */ - dataout; - int data_transfer_instructions, /* Count of dynamic instructions */ - i; /* Counter */ - u32 *cmd_datain, /* Address of datain/dataout code */ - *cmd_dataout; /* Incremented as we assemble */ -#ifdef notyet - unsigned char *msgptr; /* Current byte in select message */ - int msglen; /* Length of whole select message */ -#endif - unsigned long flags; - NCR53c7x0_local_setup(cmd->host); - - if (!(tmp = allocate_cmd (cmd))) - return NULL; - - - /* - * Decide whether we need to generate commands for DATA IN, - * DATA OUT, neither, or both based on the SCSI command - */ - - switch (cmd->cmnd[0]) { - /* These commands do DATA IN */ - case INQUIRY: - case MODE_SENSE: - case READ_6: - case READ_10: - case READ_CAPACITY: - case REQUEST_SENSE: - datain = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3; - dataout = 0; - break; - /* These commands do DATA OUT */ - case MODE_SELECT: - case WRITE_6: - case WRITE_10: -#if 0 - printk("scsi%d : command is ", host->host_no); - print_command(cmd->cmnd); -#endif -#if 0 - printk ("scsi%d : %d scatter/gather segments\n", host->host_no, - cmd->use_sg); -#endif - datain = 0; - dataout = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3; -#if 0 - hostdata->options |= OPTION_DEBUG_INTR; -#endif - break; - /* - * These commands do no data transfer, we should force an - * interrupt if a data phase is attempted on them. - */ - case START_STOP: - case TEST_UNIT_READY: - datain = dataout = 0; - break; - /* - * We don't know about these commands, so generate code to handle - * both DATA IN and DATA OUT phases. - */ - default: - datain = dataout = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3; - } - - /* - * New code : so that active pointers work correctly irregardless - * of where the saved data pointer is at, we want to immediately - * enter the dynamic code after selection, and on a non-data - * phase perform a CALL to the non-data phase handler, with - * returns back to this address. - * - * If a phase mismatch is encountered in the middle of a - * Block MOVE instruction, we want to _leave_ that instruction - * unchanged as the current case is, modify a temporary buffer, - * and point the active pointer (TEMP) at that. - * - * Furthermore, we want to implement a saved data pointer, - * set by the SAVE_DATA_POINTERs message. - * - * So, the data transfer segments will change to - * CALL data_transfer, WHEN NOT data phase - * MOVE x, x, WHEN data phase - * ( repeat ) - * JUMP other_transfer - */ - - data_transfer_instructions = datain + dataout; - - /* - * When we perform a request sense, we overwrite various things, - * including the data transfer code. Make sure we have enough - * space to do that. - */ - - if (data_transfer_instructions < 2) - data_transfer_instructions = 2; - - - /* - * The saved data pointer is set up so that a RESTORE POINTERS message - * will start the data transfer over at the beggining. - */ - - tmp->saved_data_pointer = virt_to_bus (hostdata->script) + - hostdata->E_data_transfer; - - /* - * Initialize Linux specific fields. - */ - - tmp->cmd = cmd; - tmp->next = NULL; - tmp->flags = 0; - tmp->dsa_next_addr = virt_to_bus(tmp->dsa) + hostdata->dsa_next - - hostdata->dsa_start; - tmp->dsa_addr = virt_to_bus(tmp->dsa) - hostdata->dsa_start; - - /* - * Calculate addresses of dynamic code to fill in DSA - */ - - tmp->data_transfer_start = tmp->dsa + (hostdata->dsa_end - - hostdata->dsa_start) / sizeof(u32); - tmp->data_transfer_end = tmp->data_transfer_start + - 2 * data_transfer_instructions; - - cmd_datain = datain ? tmp->data_transfer_start : NULL; - cmd_dataout = dataout ? (datain ? cmd_datain + 2 * datain : tmp-> - data_transfer_start) : NULL; - - /* - * Fill in the NCR53c7x0_cmd structure as follows - * dsa, with fixed up DSA code - * datain code - * dataout code - */ - - /* Copy template code into dsa and perform all necessary fixups */ - if (hostdata->dsa_fixup) - hostdata->dsa_fixup(tmp); - - patch_dsa_32(tmp->dsa, dsa_next, 0, 0); - patch_dsa_32(tmp->dsa, dsa_cmnd, 0, virt_to_bus(cmd)); - - if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS) - if (hostdata->sync[cmd->target].select_indirect != - ((hostdata->sync[cmd->target].scntl3_sanity << 24) | - (cmd->target << 16) | - (hostdata->sync[cmd->target].sxfer_sanity << 8))) { - printk ("scsi%d : sanity check failed select_indirect=0x%x\n", - host->host_no, hostdata->sync[cmd->target].select_indirect); - FATAL(host); - - } - - patch_dsa_32(tmp->dsa, dsa_select, 0, hostdata->sync[cmd->target]. - select_indirect); - /* - * Right now, we'll do the WIDE and SYNCHRONOUS negotiations on - * different commands; although it should be trivial to do them - * both at the same time. - */ - if (hostdata->initiate_wdtr & (1 << cmd->target)) { - memcpy ((void *) (tmp->select + 1), (void *) wdtr_message, - sizeof(wdtr_message)); - patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(wdtr_message)); - save_flags(flags); - cli(); - hostdata->initiate_wdtr &= ~(1 << cmd->target); - restore_flags(flags); - } else if (hostdata->initiate_sdtr & (1 << cmd->target)) { - memcpy ((void *) (tmp->select + 1), (void *) sdtr_message, - sizeof(sdtr_message)); - patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(sdtr_message)); - tmp->flags |= CMD_FLAG_SDTR; - save_flags(flags); - cli(); - hostdata->initiate_sdtr &= ~(1 << cmd->target); - restore_flags(flags); - - } -#if 1 - else if (!(hostdata->talked_to & (1 << cmd->target)) && - !(hostdata->options & OPTION_NO_ASYNC)) { - memcpy ((void *) (tmp->select + 1), (void *) async_message, - sizeof(async_message)); - patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(async_message)); - tmp->flags |= CMD_FLAG_SDTR; - } -#endif - else - patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1); - hostdata->talked_to |= (1 << cmd->target); - tmp->select[0] = (hostdata->options & OPTION_DISCONNECT) ? - IDENTIFY (1, cmd->lun) : IDENTIFY (0, cmd->lun); - patch_dsa_32(tmp->dsa, dsa_msgout, 1, virt_to_bus(tmp->select)); - patch_dsa_32(tmp->dsa, dsa_cmdout, 0, cmd->cmd_len); - patch_dsa_32(tmp->dsa, dsa_cmdout, 1, virt_to_bus(cmd->cmnd)); - patch_dsa_32(tmp->dsa, dsa_dataout, 0, cmd_dataout ? - virt_to_bus (cmd_dataout) - : virt_to_bus (hostdata->script) + hostdata->E_other_transfer); - patch_dsa_32(tmp->dsa, dsa_datain, 0, cmd_datain ? - virt_to_bus (cmd_datain) - : virt_to_bus (hostdata->script) + hostdata->E_other_transfer); - /* - * XXX - need to make endian aware, should use separate variables - * for both status and message bytes. - */ - patch_dsa_32(tmp->dsa, dsa_msgin, 0, 1); -/* - * FIXME : these only works for little endian. We probably want to - * provide message and status fields in the NCR53c7x0_cmd - * structure, and assign them to cmd->result when we're done. - */ - patch_dsa_32(tmp->dsa, dsa_msgin, 1, virt_to_bus(&cmd->result) + 1); - patch_dsa_32(tmp->dsa, dsa_status, 0, 1); - patch_dsa_32(tmp->dsa, dsa_status, 1, virt_to_bus(&cmd->result)); - patch_dsa_32(tmp->dsa, dsa_msgout_other, 0, 1); - patch_dsa_32(tmp->dsa, dsa_msgout_other, 1, - virt_to_bus(&(hostdata->NCR53c7xx_msg_nop))); - - /* - * Generate code for zero or more of the DATA IN, DATA OUT phases - * in the format - * - * CALL data_transfer, WHEN NOT phase - * MOVE first buffer length, first buffer address, WHEN phase - * ... - * MOVE last buffer length, last buffer address, WHEN phase - * JUMP other_transfer - */ - -/* - * See if we're getting to data transfer by generating an unconditional - * interrupt. - */ -#if 0 - if (datain) { - cmd_datain[0] = 0x98080000; - cmd_datain[1] = 0x03ffd00d; - cmd_datain += 2; - } -#endif - -/* - * XXX - I'm undecided whether all of this nonsense is faster - * in the long run, or whether I should just go and implement a loop - * on the NCR chip using table indirect mode? - * - * In any case, this is how it _must_ be done for 53c700/700-66 chips, - * so this stays even when we come up with something better. - * - * When we're limited to 1 simultaneous command, no overlapping processing, - * we're seeing 630K/sec, with 7% CPU usage on a slow Syquest 45M - * drive. - * - * Not bad, not good. We'll see. - */ - - for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; cmd_datain += 4, - cmd_dataout += 4, ++i) { - u32 buf = cmd->use_sg ? - virt_to_bus(((struct scatterlist *)cmd->buffer)[i].address) : - virt_to_bus(cmd->request_buffer); - u32 count = cmd->use_sg ? - ((struct scatterlist *)cmd->buffer)[i].length : - cmd->request_bufflen; - - if (datain) { - /* CALL other_in, WHEN NOT DATA_IN */ - cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL | - DCMD_TCI_IO) << 24) | - DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE; - cmd_datain[1] = virt_to_bus (hostdata->script) + - hostdata->E_other_in; - /* MOVE count, buf, WHEN DATA_IN */ - cmd_datain[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | DCMD_BMI_IO) - << 24) | count; - cmd_datain[3] = buf; -#if 0 - print_insn (host, cmd_datain, "dynamic ", 1); - print_insn (host, cmd_datain + 2, "dynamic ", 1); -#endif - } - if (dataout) { - /* CALL other_out, WHEN NOT DATA_OUT */ - cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL) << 24) | - DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE; - cmd_dataout[1] = virt_to_bus(hostdata->script) + - hostdata->E_other_out; - /* MOVE count, buf, WHEN DATA+OUT */ - cmd_dataout[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I) << 24) - | count; - cmd_dataout[3] = buf; -#if 0 - print_insn (host, cmd_dataout, "dynamic ", 1); - print_insn (host, cmd_dataout + 2, "dynamic ", 1); -#endif - } - } - - /* - * Install JUMP instructions after the data transfer routines to return - * control to the do_other_transfer routines. - */ - - - if (datain) { - cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) | - DBC_TCI_TRUE; - cmd_datain[1] = virt_to_bus(hostdata->script) + - hostdata->E_other_transfer; -#if 0 - print_insn (host, cmd_datain, "dynamic jump ", 1); -#endif - cmd_datain += 2; - } -#if 0 - if (datain) { - cmd_datain[0] = 0x98080000; - cmd_datain[1] = 0x03ffdeed; - cmd_datain += 2; - } -#endif - if (dataout) { - cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) | - DBC_TCI_TRUE; - cmd_dataout[1] = virt_to_bus(hostdata->script) + - hostdata->E_other_transfer; -#if 0 - print_insn (host, cmd_dataout, "dynamic jump ", 1); -#endif - cmd_dataout += 2; - } - return tmp; -} - -/* - * Function : int NCR53c7xx_queue_command (Scsi_Cmnd *cmd, - * void (*done)(Scsi_Cmnd *)) - * - * Purpose : enqueues a SCSI command - * - * Inputs : cmd - SCSI command, done - function called on completion, with - * a pointer to the command descriptor. - * - * Returns : 0 - * - * Side effects : - * cmd is added to the per instance driver issue_queue, with major - * twiddling done to the host specific fields of cmd. If the - * process_issue_queue corouting isn't running, it is restarted. - * - * NOTE : we use the host_scribble field of the Scsi_Cmnd structure to - * hold our own data, and pervert the ptr field of the SCp field - * to create a linked list. - */ - -int -NCR53c7xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) { - struct Scsi_Host *host = cmd->host; - struct NCR53c7x0_hostdata *hostdata = - (struct NCR53c7x0_hostdata *) host->hostdata; - unsigned long flags; - Scsi_Cmnd *tmp; - - cmd->scsi_done = done; - cmd->host_scribble = NULL; - cmd->SCp.ptr = NULL; - cmd->SCp.buffer = NULL; - - save_flags(flags); - cli(); - if ((hostdata->options & (OPTION_DEBUG_INIT_ONLY|OPTION_DEBUG_PROBE_ONLY)) - || ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) && - !(hostdata->debug_lun_limit[cmd->target] & (1 << cmd->lun))) -#ifdef LINUX_1_2 - || cmd->target > 7 -#else - || cmd->target > host->max_id -#endif - || cmd->target == host->this_id - || hostdata->state == STATE_DISABLED) { - printk("scsi%d : disabled or bad target %d lun %d\n", host->host_no, - cmd->target, cmd->lun); - cmd->result = (DID_BAD_TARGET << 16); - } else if ((hostdata->options & OPTION_DEBUG_NCOMMANDS_LIMIT) && - (hostdata->debug_count_limit == 0)) { - printk("scsi%d : maximum commands exceeded\n", host->host_no); - cmd->result = (DID_BAD_TARGET << 16); - cmd->result = (DID_BAD_TARGET << 16); - } else if (hostdata->options & OPTION_DEBUG_READ_ONLY) { - switch (cmd->cmnd[0]) { - case WRITE_6: - case WRITE_10: - printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n", - host->host_no); - cmd->result = (DID_BAD_TARGET << 16); - } - } else { - if ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) && - hostdata->debug_count_limit != -1) - --hostdata->debug_count_limit; - restore_flags (flags); - cmd->result = 0xffff; /* The NCR will overwrite message - and status with valid data */ - cmd->host_scribble = (unsigned char *) tmp = create_cmd (cmd); - } - cli(); - /* - * REQUEST SENSE commands are inserted at the head of the queue - * so that we do not clear the contingent allegience condition - * they may be looking at. - */ - - if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { - cmd->SCp.ptr = (unsigned char *) hostdata->issue_queue; - hostdata->issue_queue = cmd; - } else { - for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->SCp.ptr; - tmp = (Scsi_Cmnd *) tmp->SCp.ptr); - tmp->SCp.ptr = (unsigned char *) cmd; - } - restore_flags (flags); - run_process_issue_queue(); - return 0; -} - -/* - * Function : void to_schedule_list (struct Scsi_Host *host, - * struct NCR53c7x0_hostdata * hostdata, Scsi_Cmnd *cmd) - * - * Purpose : takes a SCSI command which was just removed from the - * issue queue, and deals with it by inserting it in the first - * free slot in the schedule list or by terminating it immediately. - * - * Inputs : - * host - SCSI host adater; hostdata - hostdata structure for - * this adapter; cmd - a pointer to the command; should have - * the host_scribble field initialized to point to a valid - * - * Side effects : - * cmd is added to the per instance schedule list, with minor - * twiddling done to the host specific fields of cmd. - * - */ - -static __inline__ void -to_schedule_list (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata, - struct NCR53c7x0_cmd *cmd) { - NCR53c7x0_local_declare(); - Scsi_Cmnd *tmp = cmd->cmd; - unsigned long flags; - /* dsa start is negative, so subtraction is used */ - volatile u32 *current; - - int i; - NCR53c7x0_local_setup(host); -#if 0 - printk("scsi%d : new dsa is 0x%lx (virt 0x%p)\n", host->host_no, - virt_to_bus(dsa), dsa); -#endif - - save_flags(flags); - cli(); - - /* - * Work arround race condition : if an interrupt fired and we - * got disabled forget about this command. - */ - - if (hostdata->state == STATE_DISABLED) { - printk("scsi%d : driver disabled\n", host->host_no); - tmp->result = (DID_BAD_TARGET << 16); - cmd->next = (struct NCR53c7x0_cmd *) hostdata->free; - hostdata->free = cmd; - tmp->scsi_done(tmp); - restore_flags (flags); - return; - } - - for (i = host->can_queue, current = hostdata->schedule; - i > 0 && current[0] != hostdata->NOP_insn; - --i, current += 2 /* JUMP instructions are two words */); - - if (i > 0) { - ++hostdata->busy[tmp->target][tmp->lun]; - cmd->next = hostdata->running_list; - hostdata->running_list = cmd; - - /* Restore this instruction to a NOP once the command starts */ - cmd->dsa [(hostdata->dsa_jump_dest - hostdata->dsa_start) / - sizeof(u32)] = (u32) virt_to_bus ((void *)current); - /* Replace the current jump operand. */ - current[1] = - virt_to_bus ((void *) cmd->dsa) + hostdata->E_dsa_code_begin - - hostdata->E_dsa_code_template; - /* Replace the NOP instruction with a JUMP */ - current[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | - DBC_TCI_TRUE; - } else { - printk ("scsi%d: no free slot\n", host->host_no); - disable(host); - tmp->result = (DID_ERROR << 16); - cmd->next = (struct NCR53c7x0_cmd *) hostdata->free; - hostdata->free = cmd; - tmp->scsi_done(tmp); - restore_flags (flags); - return; - } - - /* - * If the NCR chip is in an idle state, start it running the scheduler - * immediately. Otherwise, signal the chip to jump to schedule as - * soon as it is idle. - */ - if (hostdata->idle) { - hostdata->idle = 0; - hostdata->state = STATE_RUNNING; - NCR53c7x0_write32 (DSP_REG, virt_to_bus ((void *)hostdata->schedule)); - } else { - NCR53c7x0_write8(hostdata->istat, ISTAT_10_SIGP); - } - - restore_flags(flags); -} - -/* - * Function : busyp (struct Scsi_Host *host, struct NCR53c7x0_hostdata - * *hostdata, Scsi_Cmnd *cmd) - * - * Purpose : decide if we can pass the given SCSI command on to the - * device in question or not. - * - * Returns : non-zero when we're busy, 0 when we aren't. - */ - -static __inline__ int -busyp (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata, - Scsi_Cmnd *cmd) { - /* FIXME : in the future, this needs to accomodate SCSI-II tagged - queuing, and we may be able to play with fairness here a bit. - */ - return hostdata->busy[cmd->target][cmd->lun]; -} - -/* - * Function : process_issue_queue (void) - * - * Purpose : transfer commands from the issue queue to NCR start queue - * of each NCR53c7/8xx in the system, avoiding kernel stack - * overflows when the scsi_done() function is invoked recursively. - * - * NOTE : process_issue_queue exits with interrupts *disabled*, so the - * caller must renable them if it desires. - * - * NOTE : process_issue_queue should be called from both - * NCR53c7x0_queue_command() and from the interrupt handler - * after command completion in case NCR53c7x0_queue_command() - * isn't invoked again but we've freed up resources that are - * needed. - */ - -static void -process_issue_queue (unsigned long flags) { - Scsi_Cmnd *tmp, *prev; - struct Scsi_Host *host; - struct NCR53c7x0_hostdata *hostdata; - int done; - - /* - * We run (with interrupts disabled) until we're sure that none of - * the host adapters have anything that can be done, at which point - * we set process_issue_queue_running to 0 and exit. - * - * Interrupts are enabled before doing various other internal - * instructions, after we've decided that we need to run through - * the loop again. - * - */ - - do { - cli(); /* Freeze request queues */ - done = 1; - for (host = first_host; host && host->hostt == the_template; - host = host->next) { - hostdata = (struct NCR53c7x0_hostdata *) host->hostdata; - cli(); - if (hostdata->issue_queue) { - if (hostdata->state == STATE_DISABLED) { - tmp = (Scsi_Cmnd *) hostdata->issue_queue; - hostdata->issue_queue = (Scsi_Cmnd *) tmp->SCp.ptr; - tmp->result = (DID_BAD_TARGET << 16); - if (tmp->host_scribble) { - ((struct NCR53c7x0_cmd *)tmp->host_scribble)->next = - hostdata->free; - hostdata->free = - (struct NCR53c7x0_cmd *)tmp->host_scribble; - tmp->host_scribble = NULL; - } - tmp->scsi_done (tmp); - done = 0; - } else - for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, - prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) - tmp->SCp.ptr) - if (!tmp->host_scribble || - !busyp (host, hostdata, tmp)) { - if (prev) - prev->SCp.ptr = tmp->SCp.ptr; - else - hostdata->issue_queue = (Scsi_Cmnd *) - tmp->SCp.ptr; - tmp->SCp.ptr = NULL; - if (tmp->host_scribble) { - if (hostdata->options & OPTION_DEBUG_QUEUES) - printk ("scsi%d : moving command for target %d lun %d to start list\n", - host->host_no, tmp->target, tmp->lun); - - - to_schedule_list (host, hostdata, - (struct NCR53c7x0_cmd *) - tmp->host_scribble); - } else { - if (((tmp->result & 0xff) == 0xff) || - ((tmp->result & 0xff00) == 0xff00)) { - printk ("scsi%d : danger Will Robinson!\n", - host->host_no); - tmp->result = DID_ERROR << 16; - disable (host); - } - tmp->scsi_done(tmp); - } - done = 0; - } /* if target/lun is not busy */ - } /* if hostdata->issue_queue */ - if (!done) - restore_flags (flags); - } /* for host */ - } while (!done); - process_issue_queue_running = 0; -} - -/* - * Function : static void intr_scsi (struct Scsi_Host *host, - * struct NCR53c7x0_cmd *cmd) - * - * Purpose : handle all SCSI interrupts, indicated by the setting - * of the SIP bit in the ISTAT register. - * - * Inputs : host, cmd - host and NCR command causing the interrupt, cmd - * may be NULL. - */ - -static void -intr_scsi (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { - NCR53c7x0_local_declare(); - struct NCR53c7x0_hostdata *hostdata = - (struct NCR53c7x0_hostdata *) host->hostdata; - unsigned char sstat0_sist0, sist1, /* Registers */ - fatal; /* Did a fatal interrupt - occur ? */ - - int is_8xx_chip; - NCR53c7x0_local_setup(host); - - fatal = 0; - - is_8xx_chip = ((unsigned) (hostdata->chip - 800)) < 100; - if (is_8xx_chip) { - sstat0_sist0 = NCR53c7x0_read8(SIST0_REG_800); - udelay(1); - sist1 = NCR53c7x0_read8(SIST1_REG_800); - } else { - sstat0_sist0 = NCR53c7x0_read8(SSTAT0_REG); - sist1 = 0; - } - - if (hostdata->options & OPTION_DEBUG_INTR) - printk ("scsi%d : SIST0 0x%0x, SIST1 0x%0x\n", host->host_no, - sstat0_sist0, sist1); - - /* 250ms selection timeout */ - if ((is_8xx_chip && (sist1 & SIST1_800_STO)) || - (!is_8xx_chip && (sstat0_sist0 & SSTAT0_700_STO))) { - fatal = 1; - if (hostdata->options & OPTION_DEBUG_INTR) { - printk ("scsi%d : Selection Timeout\n", host->host_no); - if (cmd) { - printk("scsi%d : target %d, lun %d, command ", - host->host_no, cmd->cmd->target, cmd->cmd->lun); - print_command (cmd->cmd->cmnd); - printk("scsi%d : dsp = 0x%x (virt 0x%p)\n", host->host_no, - NCR53c7x0_read32(DSP_REG), - bus_to_virt(NCR53c7x0_read32(DSP_REG))); - } else { - printk("scsi%d : no command\n", host->host_no); - } - } -/* - * XXX - question : how do we want to handle the Illegal Instruction - * interrupt, which may occur before or after the Selection Timeout - * interrupt? - */ - - if (1) { - hostdata->idle = 1; - hostdata->expecting_sto = 0; - - if (hostdata->test_running) { - hostdata->test_running = 0; - hostdata->test_completed = 3; - } else if (cmd) { - abnormal_finished(cmd, DID_BAD_TARGET << 16); - } -#if 0 - hostdata->intrs = 0; -#endif - } - } - -/* - * FIXME : in theory, we can also get a UDC when a STO occurs. - */ - if (sstat0_sist0 & SSTAT0_UDC) { - fatal = 1; - if (cmd) { - printk("scsi%d : target %d lun %d unexpected disconnect\n", - host->host_no, cmd->cmd->target, cmd->cmd->lun); - print_lots (host); - abnormal_finished(cmd, DID_ERROR << 16); - } else - printk("scsi%d : unexpected disconnect (no command)\n", - host->host_no); - - hostdata->dsp = (u32 *) hostdata->schedule; - hostdata->dsp_changed = 1; - } - - /* SCSI PARITY error */ - if (sstat0_sist0 & SSTAT0_PAR) { - fatal = 1; - if (cmd && cmd->cmd) { - printk("scsi%d : target %d lun %d parity error.\n", - host->host_no, cmd->cmd->target, cmd->cmd->lun); - abnormal_finished (cmd, DID_PARITY << 16); - } else - printk("scsi%d : parity error\n", host->host_no); - /* Should send message out, parity error */ - - /* XXX - Reduce synchronous transfer rate! */ - hostdata->dsp = hostdata->script + hostdata->E_initiator_abort / - sizeof(u32); - hostdata->dsp_changed = 1; - /* SCSI GROSS error */ - } - - if (sstat0_sist0 & SSTAT0_SGE) { - fatal = 1; - printk("scsi%d : gross error\n", host->host_no); - /* Reset SCSI offset */ - if ((hostdata->chip / 100) == 8) { - NCR53c7x0_write8 (STEST2_REG_800, STEST2_800_ROF); - } - - /* - * A SCSI gross error may occur when we have - * - * - A synchronous offset which causes the SCSI FIFO to be overwritten. - * - * - A REQ which causes the maxmimum synchronous offset programmed in - * the SXFER register to be exceeded. - * - * - A phase change with an outstanding synchronous offset. - * - * - Residual data in the synchronous data FIFO, with a transfer - * other than a synchronous receive is started.$# - */ - - - /* XXX Should deduce synchronous transfer rate! */ - hostdata->dsp = hostdata->script + hostdata->E_initiator_abort / - sizeof(u32); - hostdata->dsp_changed = 1; - /* Phase mismatch */ - } - - if (sstat0_sist0 & SSTAT0_MA) { - fatal = 1; - if (hostdata->options & OPTION_DEBUG_INTR) - printk ("scsi%d : SSTAT0_MA\n", host->host_no); - intr_phase_mismatch (host, cmd); - } - -#if 0 - if (sstat0_sist0 & SIST0_800_RSL) - printk ("scsi%d : Oh no Mr. Bill!\n", host->host_no); -#endif - -/* - * If a fatal SCSI interrupt occurs, we must insure that the DMA and - * SCSI FIFOs were flushed. - */ - - if (fatal) { - if (!hostdata->dstat_valid) { - hostdata->dstat = NCR53c7x0_read8(DSTAT_REG); - hostdata->dstat_valid = 1; - } - -/* XXX - code check for 700/800 chips */ - if (!(hostdata->dstat & DSTAT_DFE)) { - printk ("scsi%d : DMA FIFO not empty\n", host->host_no); - if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) { - printk ("scsi%d: Flushing DMA FIFO\n", - host->host_no); - NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF); - while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) & - DSTAT_DFE)); - } else { - NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF); - while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF); - } - hostdata->dstat |= DSTAT_DFE; - } - } -} - -/* - * Function : static void NCR53c7x0_intr (int irq, struct pt_regs * regs) - * - * Purpose : handle NCR53c7x0 interrupts for all NCR devices sharing - * the same IRQ line. - * - * Inputs : Since we're using the SA_INTERRUPT interrupt handler - * semantics, irq indicates the interrupt which invoked - * this handler. - */ - -static void -NCR53c7x0_intr (int irq, struct pt_regs * regs) { - NCR53c7x0_local_declare(); - struct Scsi_Host *host; /* Host we are looking at */ - unsigned char istat; /* Values of interrupt regs */ - struct NCR53c7x0_hostdata *hostdata; /* host->hostdata */ - struct NCR53c7x0_cmd *cmd, /* command which halted */ - **cmd_prev_ptr; - u32 *dsa; /* DSA */ - int done = 1; /* Indicates when handler - should terminate */ - int interrupted = 0; /* This HA generated - an interrupt */ - int have_intfly; /* Don't print warning - messages when we stack - INTFLYs */ - unsigned long flags; - -#ifdef NCR_DEBUG - char buf[80]; /* Debugging sprintf buffer */ - size_t buflen; /* Length of same */ -#endif - - do { - done = 1; - for (host = first_host; host; host = host->next) - if (host->hostt == the_template && host->irq == irq) { - NCR53c7x0_local_setup(host); - - hostdata = (struct NCR53c7x0_hostdata *) host->hostdata; - hostdata->dsp_changed = 0; - interrupted = 0; - have_intfly = 0; - - do { - int is_8xx_chip; - - hostdata->dstat_valid = 0; - interrupted = 0; - /* - * Only read istat once, since reading it again will unstack - * interrupts? - */ - istat = NCR53c7x0_read8(hostdata->istat); - - /* - * INTFLY interrupts are used by the NCR53c720, NCR53c810, - * and NCR53c820 to signify completion of a command. Since - * the SCSI processor continues running, we can't just look - * at the contents of the DSA register and continue running. - */ -/* XXX - this is too big, offends my sense of aesthetics, and should - move to intr_intfly() */ - is_8xx_chip = ((unsigned) (hostdata->chip - 800)) < 100; - if ((hostdata->options & OPTION_INTFLY) && - (is_8xx_chip && (istat & ISTAT_800_INTF))) { - char search_found = 0; /* Got at least one ? */ - done = 0; - interrupted = 1; - - /* - * Clear the INTF bit by writing a one. - * This reset operation is self-clearing. - */ - NCR53c7x0_write8(hostdata->istat, istat|ISTAT_800_INTF); - - if (hostdata->options & OPTION_DEBUG_INTR) - printk ("scsi%d : INTFLY\n", host->host_no); - - /* - * Traverse our list of running commands, and look - * for those with valid (non-0xff ff) status and message - * bytes encoded in the result which signify command - * completion. - */ - - - save_flags(flags); - cli(); -restart: - for (cmd_prev_ptr = (struct NCR53c7x0_cmd **) - &(hostdata->running_list), cmd = - (struct NCR53c7x0_cmd *) hostdata->running_list; cmd ; - cmd_prev_ptr = (struct NCR53c7x0_cmd **) &(cmd->next), - cmd = (struct NCR53c7x0_cmd *) cmd->next) { - Scsi_Cmnd *tmp; - - if (!cmd) { - printk("scsi%d : very weird.\n", host->host_no); - break; - } - - if (!(tmp = cmd->cmd)) { - printk("scsi%d : weird. NCR53c7x0_cmd has no Scsi_Cmnd\n", - host->host_no); - continue; - } -#if 0 - printk ("scsi%d : looking at result of 0x%x\n", - host->host_no, cmd->cmd->result); -#endif - - if (((tmp->result & 0xff) == 0xff) || - ((tmp->result & 0xff00) == 0xff00)) - continue; - - search_found = 1; - - /* Important - remove from list _before_ done is called */ - if (cmd_prev_ptr) - *cmd_prev_ptr = (struct NCR53c7x0_cmd *) cmd->next; - - --hostdata->busy[tmp->target][tmp->lun]; - cmd->next = hostdata->free; - hostdata->free = cmd; - - tmp->host_scribble = NULL; - - if (hostdata->options & OPTION_DEBUG_INTR) { - printk ("scsi%d : command complete : pid %lu, id %d,lun %d result 0x%x ", - host->host_no, tmp->pid, tmp->target, tmp->lun, tmp->result); - print_command (tmp->cmnd); - } - -#if 0 - hostdata->options &= ~OPTION_DEBUG_INTR; -#endif - tmp->scsi_done(tmp); - goto restart; - - } - restore_flags(flags); - - /* - * I think that we're stacking INTFLY interrupts; taking care of - * all the finished commands on the first one, and then getting - * worried when we see the next one. The magic with have_intfly - * should tell if this is the case.. - */ - - if (!search_found && !have_intfly) { - printk ("scsi%d : WARNING : INTFLY with no completed commands.\n", - host->host_no); - } else if (!have_intfly) { - have_intfly = 1; - run_process_issue_queue(); - } - } - - if (istat & (ISTAT_SIP|ISTAT_DIP)) { - done = 0; - interrupted = 1; - hostdata->state = STATE_HALTED; - - if (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ? - SSTAT1_REG : SSTAT2_REG) & SSTAT2_FF_MASK) - printk ("scsi%d : SCSI FIFO not empty\n", - host->host_no); - - /* - * NCR53c700 and NCR53c700-66 change the current SCSI - * process, hostdata->current, in the Linux driver so - * cmd = hostdata->current. - * - * With other chips, we must look through the commands - * executing and find the command structure which - * corresponds to the DSA register. - */ - - if (hostdata->options & OPTION_700) { - cmd = (struct NCR53c7x0_cmd *) hostdata->current; - } else { - dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG)); - for (cmd = (struct NCR53c7x0_cmd *) - hostdata->running_list; cmd && - (dsa + (hostdata->dsa_start / sizeof(u32))) != - cmd->dsa; - cmd = (struct NCR53c7x0_cmd *)(cmd->next)); - } - if (hostdata->options & OPTION_DEBUG_INTR) { - if (cmd) { - printk("scsi%d : interrupt for pid %lu, id %d, lun %d ", - host->host_no, cmd->cmd->pid, (int) cmd->cmd->target, - (int) cmd->cmd->lun); - print_command (cmd->cmd->cmnd); - } else { - printk("scsi%d : no active command\n", host->host_no); - } - } - - if (istat & ISTAT_SIP) { - if (hostdata->options & OPTION_DEBUG_INTR) - printk ("scsi%d : ISTAT_SIP\n", host->host_no); - intr_scsi (host, cmd); - } - - if (istat & ISTAT_DIP) { - if (hostdata->options & OPTION_DEBUG_INTR) - printk ("scsi%d : ISTAT_DIP\n", host->host_no); - intr_dma (host, cmd); - } - - if (!hostdata->dstat_valid) { - hostdata->dstat = NCR53c7x0_read8(DSTAT_REG); - hostdata->dstat_valid = 1; - } - - /* XXX - code check for 700/800 chips */ - if (!(hostdata->dstat & DSTAT_DFE)) { - printk ("scsi%d : DMA FIFO not empty\n", host->host_no); - if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) { - printk ("scsi%d: Flushing DMA FIFO\n", - host->host_no); - NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF); - while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) & - DSTAT_DFE)); - } else - { - NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF); - while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF); - } - hostdata->dstat |= DSTAT_DFE; - } - } - } while (interrupted); - - - - if (hostdata->intrs != -1) - hostdata->intrs++; -#if 0 - if (hostdata->intrs > 40) { - printk("scsi%d : too many interrupts, halting", host->host_no); - disable(host); - } -#endif - - if (!hostdata->idle && hostdata->state == STATE_HALTED) { - if (!hostdata->dsp_changed) { - hostdata->dsp = (u32 *) - bus_to_virt(NCR53c7x0_read32(DSP_REG)); - } - -#if 0 - printk("scsi%d : new dsp is 0x%lx (virt 0x%p)\n", - host->host_no, virt_to_bus(hostdata->dsp), hostdata->dsp); -#endif - - hostdata->state = STATE_RUNNING; - NCR53c7x0_write32 (DSP_REG, virt_to_bus(hostdata->dsp)); - } - } - } while (!done); -} - - -/* - * Function : static int abort_connected (struct Scsi_Host *host) - * - * Purpose : Assuming that the NCR SCSI processor is currently - * halted, break the currently established nexus. Clean - * up of the NCR53c7x0_cmd and Scsi_Cmnd structures should - * be done on receipt of the abort interrupt. - * - * Inputs : host - SCSI host - * - */ - -static int -abort_connected (struct Scsi_Host *host) { -#ifdef NEW_ABORT - NCR53c7x0_local_declare(); -#endif - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; -/* FIXME : this probably should change for production kernels; at the - least, counter sould move to a per-host structure. */ - static int counter = 5; -#ifdef NEW_ABORT - int sstat, phase, offset; - u32 *script; - NCR53c7x0_local_setup(host); -#endif - - if (--counter <= 0) { - disable(host); - return 0; - } - - printk ("scsi%d : DANGER : abort_connected() called \n", - host->host_no); - -#ifdef NEW_ABORT - -/* - * New strategy : Rather than using a generic abort routine, - * we'll specifically try to source or sink the appropriate - * amount of data for the phase we're currently in (taking into - * account the current synchronous offset) - */ - - sstat = (NCR53c8x0_read8 ((chip / 100) == 8 ? SSTAT1_REG : SSTAT2_REG); - offset = OFFSET (sstat & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT; - phase = sstat & SSTAT2_PHASE_MASK; - -/* - * SET ATN - * MOVE source_or_sink, WHEN CURRENT PHASE - * < repeat for each outstanding byte > - * JUMP send_abort_message - */ - - script = hostdata->abort_script = kmalloc ( - 8 /* instruction size */ * ( - 1 /* set ATN */ + - (!offset ? 1 : offset) /* One transfer per outstanding byte */ + - 1 /* send abort message */), - GFP_ATOMIC); - - -#else /* def NEW_ABORT */ - hostdata->dsp = hostdata->script + hostdata->E_initiator_abort / - sizeof(u32); -#endif /* def NEW_ABORT */ - hostdata->dsp_changed = 1; - -/* XXX - need to flag the command as aborted after the abort_connected - code runs - */ - return 0; -} - -/* - * Function : static int datapath_residual (Scsi_Host *host) - * - * Purpose : return residual data count of what's in the chip. - * - * Inputs : host - SCSI host - */ - -static int -datapath_residual (struct Scsi_Host *host) { - NCR53c7x0_local_declare(); - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - int count, synchronous, sstat; - NCR53c7x0_local_setup(host); - /* COMPAT : the 700 and 700-66 need to use DFIFO_00_BO_MASK */ - count = ((NCR53c7x0_read8 (DFIFO_REG) & DFIFO_10_BO_MASK) - - (NCR53c7x0_read32 (DBC_REG) & DFIFO_10_BO_MASK)) & DFIFO_10_BO_MASK; - synchronous = NCR53c7x0_read8 (SXFER_REG) & SXFER_MO_MASK; - /* COMPAT : DDIR is elsewhere on non-'8xx chips. */ - if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) { - /* Receive */ - if (synchronous) - count += (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ? - SSTAT1_REG : SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT; - else - if (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ? - SSTAT0_REG : SSTAT1_REG) & SSTAT1_ILF) - ++count; - } else { - /* Send */ - sstat = ((hostdata->chip / 100) == 8) ? NCR53c7x0_read8 (SSTAT0_REG) : - NCR53c7x0_read8 (SSTAT1_REG); - if (sstat & SSTAT1_OLF) - ++count; - if (synchronous && (sstat & SSTAT1_ORF)) - ++count; - } - return count; -} - -/* - * Function : static const char * sbcl_to_phase (int sbcl)_ - * - * Purpose : Convert SBCL register to user-parsable phase representation - * - * Inputs : sbcl - value of sbcl register - */ - - -static const char * -sbcl_to_phase (int sbcl) { - switch (sbcl & SBCL_PHASE_MASK) { - case SBCL_PHASE_DATAIN: - return "DATAIN"; - case SBCL_PHASE_DATAOUT: - return "DATAOUT"; - case SBCL_PHASE_MSGIN: - return "MSGIN"; - case SBCL_PHASE_MSGOUT: - return "MSGOUT"; - case SBCL_PHASE_CMDOUT: - return "CMDOUT"; - case SBCL_PHASE_STATIN: - return "STATUSIN"; - default: - return "unknown"; - } -} - -/* - * Function : static const char * sstat2_to_phase (int sstat)_ - * - * Purpose : Convert SSTAT2 register to user-parsable phase representation - * - * Inputs : sstat - value of sstat register - */ - - -static const char * -sstat2_to_phase (int sstat) { - switch (sstat & SSTAT2_PHASE_MASK) { - case SSTAT2_PHASE_DATAIN: - return "DATAIN"; - case SSTAT2_PHASE_DATAOUT: - return "DATAOUT"; - case SSTAT2_PHASE_MSGIN: - return "MSGIN"; - case SSTAT2_PHASE_MSGOUT: - return "MSGOUT"; - case SSTAT2_PHASE_CMDOUT: - return "CMDOUT"; - case SSTAT2_PHASE_STATIN: - return "STATUSIN"; - default: - return "unknown"; - } -} - -/* - * Function : static void intr_phase_mismatch (struct Scsi_Host *host, - * struct NCR53c7x0_cmd *cmd) - * - * Purpose : Handle phase mismatch interrupts - * - * Inputs : host, cmd - host and NCR command causing the interrupt, cmd - * may be NULL. - * - * Side effects : The abort_connected() routine is called or the NCR chip - * is restarted, jumping to the command_complete entry point, or - * patching the address and transfer count of the current instruction - * and calling the msg_in entry point as appropriate. - */ - -static void -intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { - NCR53c7x0_local_declare(); - u32 dbc_dcmd, *dsp, *dsp_next; - unsigned char dcmd, sbcl; - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - int residual; - enum {ACTION_ABORT, ACTION_ABORT_PRINT, ACTION_CONTINUE} action = - ACTION_ABORT_PRINT; - const char *where = NULL; - NCR53c7x0_local_setup(host); - - /* - * Corrective action is based on where in the SCSI SCRIPT(tm) the error - * occurred, as well as which SCSI phase we are currently in. - */ - dsp_next = bus_to_virt(NCR53c7x0_read32(DSP_REG)); - - /* - * Fetch the current instruction, and remove the operands for easier - * interpretation. - */ - dbc_dcmd = NCR53c7x0_read32(DBC_REG); - dcmd = (dbc_dcmd & 0xff000000) >> 24; - /* - * Like other processors, the NCR adjusts the instruction pointer before - * instruction decode. Set the DSP address back to what it should - * be for this instruction based on its size (2 or 3 32 bit words). - */ - dsp = dsp_next - NCR53c7x0_insn_size(dcmd); - - - /* - * Read new SCSI phase from the SBCL lines. Since all of our code uses - * a WHEN conditional instead of an IF conditional, we don't need to - * wait for a new REQ. - */ - sbcl = NCR53c7x0_read8(SBCL_REG) & SBCL_PHASE_MASK; - - if (!cmd) { - action = ACTION_ABORT_PRINT; - where = "no current command"; - /* - * The way my SCSI SCRIPTS(tm) are architected, recoverable phase - * mismatches should only occur where we're doing a multi-byte - * BMI instruction. Specifically, this means - * - * - select messages (a SCSI-I target may ignore additional messages - * after the IDENTIFY; any target may reject a SDTR or WDTR) - * - * - command out (targets may send a message to signal an error - * condition, or go into STATUSIN after they've decided - * they don't like the command. - * - * - reply_message (targets may reject a multi-byte message in the - * middle) - * - * - data transfer routines (command completion with buffer space - * left, disconnect message, or error message) - */ - } else if (((dsp >= cmd->data_transfer_start && - dsp < cmd->data_transfer_end)) || dsp == (cmd->residual + 2)) { - if ((dcmd & (DCMD_TYPE_MASK|DCMD_BMI_OP_MASK|DCMD_BMI_INDIRECT| - DCMD_BMI_MSG|DCMD_BMI_CD)) == (DCMD_TYPE_BMI| - DCMD_BMI_OP_MOVE_I)) { - residual = datapath_residual (host); - if (hostdata->options & OPTION_DEBUG_DISCONNECT) - printk ("scsi%d : handling residual transfer (+ %d bytes from DMA FIFO)\n", - host->host_no, residual); - - /* - * The first instruction is a CALL to the alternate handler for - * this data transfer phase, so we can do calls to - * munge_msg_restart as we would if control were passed - * from normal dynamic code. - */ - if (dsp != cmd->residual + 2) { - cmd->residual[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL | - ((dcmd & DCMD_BMI_IO) ? DCMD_TCI_IO : 0)) << 24) | - DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE; - cmd->residual[1] = virt_to_bus(hostdata->script) - + ((dcmd & DCMD_BMI_IO) - ? hostdata->E_other_in : hostdata->E_other_out); - } - - /* - * The second instruction is the a data transfer block - * move instruction, reflecting the pointer and count at the - * time of the phase mismatch. - */ - cmd->residual[2] = dbc_dcmd + residual; - cmd->residual[3] = NCR53c7x0_read32(DNAD_REG) - residual; - - /* - * The third and final instruction is a jump to the instruction - * which follows the instruction which had to be 'split' - */ - if (dsp != cmd->residual + 2) { - cmd->residual[4] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) - << 24) | DBC_TCI_TRUE; - cmd->residual[5] = virt_to_bus(dsp_next); - } - - /* - * For the sake of simplicity, transfer control to the - * conditional CALL at the start of the residual buffer. - */ - hostdata->dsp = cmd->residual; - hostdata->dsp_changed = 1; - action = ACTION_CONTINUE; - } else { - where = "non-BMI dynamic DSA code"; - action = ACTION_ABORT_PRINT; - } - } else if (dsp == (hostdata->script + hostdata->E_select_msgout / 4)) { - /* Release ATN */ - NCR53c7x0_write8 (SOCL_REG, 0); - switch (sbcl) { - /* - * Some devices (SQ555 come to mind) grab the IDENTIFY message - * sent on selection, and decide to go into COMMAND OUT phase - * rather than accepting the rest of the messages or rejecting - * them. Handle these devices gracefully. - */ - case SBCL_PHASE_CMDOUT: - hostdata->dsp = dsp + 2 /* two _words_ */; - hostdata->dsp_changed = 1; - printk ("scsi%d : target %d ignored SDTR and went into COMMAND OUT\n", - host->host_no, cmd->cmd->target); - cmd->flags &= ~CMD_FLAG_SDTR; - action = ACTION_CONTINUE; - break; - case SBCL_PHASE_MSGIN: - hostdata->dsp = hostdata->script + hostdata->E_msg_in / - sizeof(u32); - hostdata->dsp_changed = 1; - action = ACTION_CONTINUE; - break; - default: - where="select message out"; - action = ACTION_ABORT_PRINT; - } - /* - * Some SCSI devices will interpret a command as they read the bytes - * off the SCSI bus, and may decide that the command is Bogus before - * they've read the entire commad off the bus. - */ - } else if (dsp == hostdata->script + hostdata->E_cmdout_cmdout / sizeof - (u32)) { - hostdata->dsp = hostdata->script + hostdata->E_data_transfer / - sizeof (u32); - hostdata->dsp_changed = 1; - action = ACTION_CONTINUE; - /* FIXME : we need to handle message reject, etc. within msg_respond. */ -#ifdef notyet - } else if (dsp == hostdata->script + hostdata->E_reply_message) { - switch (sbcl) { - /* Any other phase mismatches abort the currently executing command. */ -#endif - } else { - where = "unknown location"; - action = ACTION_ABORT_PRINT; - } - - /* Flush DMA FIFO */ - if (!hostdata->dstat_valid) { - hostdata->dstat = NCR53c7x0_read8(DSTAT_REG); - hostdata->dstat_valid = 1; - } - if (!(hostdata->dstat & DSTAT_DFE)) { - if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) { - printk ("scsi%d: Flushing DMA FIFO\n", - host->host_no); - NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF); - /* FIXME : what about stacked DMA interrupts? */ - while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) & - DSTAT_DFE)); - } else { - NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF); - while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF); - } - hostdata->dstat |= DSTAT_DFE; - } - - switch (action) { - case ACTION_ABORT_PRINT: - printk("scsi%d : %s : unexpected phase %s.\n", - host->host_no, where ? where : "unknown location", - sbcl_to_phase(sbcl)); - print_lots (host); - /* Fall through to ACTION_ABORT */ - case ACTION_ABORT: - abort_connected (host); - break; - case ACTION_CONTINUE: - break; - } - -#if 0 - if (hostdata->dsp_changed) { - printk("scsi%d: new dsp 0x%p\n", host->host_no, hostdata->dsp); - print_insn (host, hostdata->dsp, "", 1); - } -#endif - -} - -/* - * Function : static void intr_bf (struct Scsi_Host *host, - * struct NCR53c7x0_cmd *cmd) - * - * Purpose : handle BUS FAULT interrupts - * - * Inputs : host, cmd - host and NCR command causing the interrupt, cmd - * may be NULL. - */ - -static void -intr_bf (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { - NCR53c7x0_local_declare(); - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - u32 *dsp, - *next_dsp, /* Current dsp */ - *dsa, - dbc_dcmd; /* DCMD (high eight bits) + DBC */ - unsigned short pci_status; - int tmp; - unsigned long flags; - char *reason = NULL; - /* Default behavior is for a silent error, with a retry until we've - exhausted retries. */ - enum {MAYBE, ALWAYS, NEVER} retry = MAYBE; - int report = 0; - NCR53c7x0_local_setup(host); - - dbc_dcmd = NCR53c7x0_read32 (DBC_REG); - next_dsp = bus_to_virt (NCR53c7x0_read32(DSP_REG)); - dsp = next_dsp - NCR53c7x0_insn_size ((dbc_dcmd >> 24) & 0xff); -/* FIXME - check chip type */ - dsa = bus_to_virt (NCR53c7x0_read32(DSA_REG)); - - /* - * Bus faults can be caused by either a Bad Address or - * Target Abort. We should check the Received Target Abort - * bit of the PCI status register and Master Abort Bit. - * - * - Master Abort bit indicates that no device claimed - * the address with DEVSEL within five clocks - * - * - Target Abort bit indicates that a target claimed it, - * but changed its mind once it saw the byte enables. - * - */ - - if ((hostdata->chip / 100) == 8) { - save_flags (flags); - cli(); - tmp = pcibios_read_config_word (hostdata->pci_bus, - hostdata->pci_device_fn, PCI_STATUS, &pci_status); - restore_flags (flags); - if (tmp == PCIBIOS_SUCCESSFUL) { - if (pci_status & PCI_STATUS_REC_TARGET_ABORT) { - reason = "PCI target abort"; - pci_status &= ~PCI_STATUS_REC_TARGET_ABORT; - } else if (pci_status & PCI_STATUS_REC_MASTER_ABORT) { - reason = "No device asserted PCI DEVSEL within five bus clocks"; - pci_status &= ~PCI_STATUS_REC_MASTER_ABORT; - } else if (pci_status & PCI_STATUS_PARITY) { - report = 1; - pci_status &= ~PCI_STATUS_PARITY; - } - } else { - printk ("scsi%d : couldn't read status register : %s\n", - host->host_no, pcibios_strerror (tmp)); - retry = NEVER; - } - } - -#ifndef notyet - report = 1; -#endif - if (report && reason) { - printk(KERN_ALERT "scsi%d : BUS FAULT reason = %s\n", - host->host_no, reason ? reason : "unknown"); - print_lots (host); - } - -#ifndef notyet - retry = NEVER; -#endif - - /* - * TODO : we should attempt to recover from any spurious bus - * faults. After X retries, we should figure that things are - * sufficiently wedged, and call NCR53c7xx_reset. - * - * This code should only get executed once we've decided that we - * cannot retry. - */ - - if (retry == NEVER) { - printk(KERN_ALERT " mail drew@PoohSticks.ORG\n"); - FATAL (host); - } -} - -/* - * Function : static void intr_dma (struct Scsi_Host *host, - * struct NCR53c7x0_cmd *cmd) - * - * Purpose : handle all DMA interrupts, indicated by the setting - * of the DIP bit in the ISTAT register. - * - * Inputs : host, cmd - host and NCR command causing the interrupt, cmd - * may be NULL. - */ - -static void -intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { - NCR53c7x0_local_declare(); - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - unsigned char dstat; /* DSTAT */ - u32 *dsp, - *next_dsp, /* Current dsp */ - *dsa, - dbc_dcmd; /* DCMD (high eight bits) + DBC */ - int tmp; - unsigned long flags; - NCR53c7x0_local_setup(host); - - if (!hostdata->dstat_valid) { - hostdata->dstat = NCR53c7x0_read8(DSTAT_REG); - hostdata->dstat_valid = 1; - } - - dstat = hostdata->dstat; - - if (hostdata->options & OPTION_DEBUG_INTR) - printk("scsi%d : DSTAT=0x%x\n", host->host_no, (int) dstat); - - dbc_dcmd = NCR53c7x0_read32 (DBC_REG); - next_dsp = bus_to_virt(NCR53c7x0_read32(DSP_REG)); - dsp = next_dsp - NCR53c7x0_insn_size ((dbc_dcmd >> 24) & 0xff); -/* XXX - check chip type */ - dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG)); - - /* - * DSTAT_ABRT is the aborted interrupt. This is set whenever the - * SCSI chip is aborted. - * - * With NCR53c700 and NCR53c700-66 style chips, we should only - * get this when the chip is currently running the accept - * reselect/select code and we have set the abort bit in the - * ISTAT register. - * - */ - - if (dstat & DSTAT_ABRT) { -#if 0 - /* XXX - add code here to deal with normal abort */ - if ((hostdata->options & OPTION_700) && (hostdata->state == - STATE_ABORTING)) { - } else -#endif - { - printk(KERN_ALERT "scsi%d : unexpected abort interrupt at\n" - " ", host->host_no); - print_insn (host, dsp, KERN_ALERT "s ", 1); - FATAL (host); - } - } - - /* - * DSTAT_SSI is the single step interrupt. Should be generated - * whenever we have single stepped or are tracing. - */ - - if (dstat & DSTAT_SSI) { - if (hostdata->options & OPTION_DEBUG_TRACE) { - } else if (hostdata->options & OPTION_DEBUG_SINGLE) { - print_insn (host, dsp, "s ", 0); - save_flags(flags); - cli(); -/* XXX - should we do this, or can we get away with writing dsp? */ - - NCR53c7x0_write8 (DCNTL_REG, (NCR53c7x0_read8(DCNTL_REG) & - ~DCNTL_SSM) | DCNTL_STD); - restore_flags(flags); - } else { - printk(KERN_ALERT "scsi%d : unexpected single step interrupt at\n" - " ", host->host_no); - print_insn (host, dsp, KERN_ALERT "", 1); - printk(KERN_ALERT " mail drew@PoohSticks.ORG\n"); - FATAL (host); - } - } - - /* - * DSTAT_IID / DSTAT_OPC (same bit, same meaning, only the name - * is different) is generated whenever an illegal instruction is - * encountered. - * - * XXX - we may want to emulate INTFLY here, so we can use - * the same SCSI SCRIPT (tm) for NCR53c710 through NCR53c810 - * chips. - */ - - if (dstat & DSTAT_OPC) { - /* - * Ascertain if this IID interrupts occurred before or after a STO - * interrupt. Since the interrupt handling code now leaves - * DSP unmodified until _after_ all stacked interrupts have been - * processed, reading the DSP returns the original DSP register. - * This means that if dsp lies between the select code, and - * message out following the selection code (where the IID interrupt - * would have to have occurred by due to the implicit wait for REQ), - * we have an IID interrupt resulting from a STO condition and - * can ignore it. - */ - - if (((dsp >= (hostdata->script + hostdata->E_select / sizeof(u32))) && - (dsp <= (hostdata->script + hostdata->E_select_msgout / - sizeof(u32) + 8))) || (hostdata->test_running == 2)) { - if (hostdata->options & OPTION_DEBUG_INTR) - printk ("scsi%d : ignoring DSTAT_IID for SSTAT_STO\n", - host->host_no); - if (hostdata->expecting_iid) { - hostdata->expecting_iid = 0; - hostdata->idle = 1; - if (hostdata->test_running == 2) { - hostdata->test_running = 0; - hostdata->test_completed = 3; - } else if (cmd) - abnormal_finished (cmd, DID_BAD_TARGET << 16); - } else { - hostdata->expecting_sto = 1; - } - /* - * We can't guarantee we'll be able to execute the WAIT DISCONNECT - * instruction within the 3.4us of bus free and arbitration delay - * that a target can RESELECT in and assert REQ after we've dropped - * ACK. If this happens, we'll get an illegal instruction interrupt. - * Doing away with the WAIT DISCONNECT instructions broke everything, - * so instead I'll settle for moving one WAIT DISCONNECT a few - * instructions closer to the CLEAR ACK before it to minimize the - * chances of this happening, and handle it if it occurs anyway. - * - * Simply continue with what we were doing, and control should - * be transfered to the schedule routine which will ultimately - * pass control onto the reselection or selection (not yet) - * code. - */ - } else if (dbc_dcmd == 0x48000000 && (NCR53c7x0_read8 (SBCL_REG) & - SBCL_REQ)) { - if (!(hostdata->options & OPTION_NO_PRINT_RACE)) - { - printk("scsi%d: REQ before WAIT DISCONNECT IID\n", - host->host_no); - hostdata->options |= OPTION_NO_PRINT_RACE; - } - } else { - printk(KERN_ALERT "scsi%d : illegal instruction\n", host->host_no); - print_lots (host); - printk(KERN_ALERT " mail drew@PoohSticks.ORG with ALL\n" - " boot messages and diagnostic output\n"); - FATAL (host); - } - } - - /* - * DSTAT_BF are bus fault errors - */ - - if (dstat & DSTAT_800_BF) { - intr_bf (host, cmd); - } - - - /* - * DSTAT_SIR interrupts are generated by the execution of - * the INT instruction. Since the exact values available - * are determined entirely by the SCSI script running, - * and are local to a particular script, a unique handler - * is called for each script. - */ - - if (dstat & DSTAT_SIR) { - if (hostdata->options & OPTION_DEBUG_INTR) - printk ("scsi%d : DSTAT_SIR\n", host->host_no); - switch ((tmp = hostdata->dstat_sir_intr (host, cmd))) { - case SPECIFIC_INT_NOTHING: - case SPECIFIC_INT_RESTART: - break; - case SPECIFIC_INT_ABORT: - abort_connected(host); - break; - case SPECIFIC_INT_PANIC: - printk(KERN_ALERT "scsi%d : failure at ", host->host_no); - print_insn (host, dsp, KERN_ALERT "", 1); - printk(KERN_ALERT " dstat_sir_intr() returned SPECIFIC_INT_PANIC\n"); - FATAL (host); - break; - case SPECIFIC_INT_BREAK: - intr_break (host, cmd); - break; - default: - printk(KERN_ALERT "scsi%d : failure at ", host->host_no); - print_insn (host, dsp, KERN_ALERT "", 1); - printk(KERN_ALERT" dstat_sir_intr() returned unknown value %d\n", - tmp); - FATAL (host); - } - } - - if ((hostdata->chip / 100) == 8 && (dstat & DSTAT_800_MDPE)) { - printk(KERN_ALERT "scsi%d : Master Data Parity Error\n", - host->host_no); - FATAL (host); - } -} - -/* - * Function : static int print_insn (struct Scsi_Host *host, - * u32 *insn, int kernel) - * - * Purpose : print numeric representation of the instruction pointed - * to by insn to the debugging or kernel message buffer - * as appropriate. - * - * If desired, a user level program can interpret this - * information. - * - * Inputs : host, insn - host, pointer to instruction, prefix - - * string to prepend, kernel - use printk instead of debugging buffer. - * - * Returns : size, in u32s, of instruction printed. - */ - -/* - * FIXME: should change kernel parameter so that it takes an ENUM - * specifying severity - either KERN_ALERT or KERN_PANIC so - * all panic messages are output with the same severity. - */ - -static int -print_insn (struct Scsi_Host *host, const u32 *insn, - const char *prefix, int kernel) { - char buf[160], /* Temporary buffer and pointer. ICKY - arbitrary length. */ - - - *tmp; - unsigned char dcmd; /* dcmd register for *insn */ - int size; - - /* - * Check to see if the instruction pointer is not bogus before - * indirecting through it; avoiding red-zone at start of - * memory. - * - * FIXME: icky magic needs to happen here on non-intel boxes which - * don't have kernel memory mapped in like this. Might be reasonable - * to use vverify()? - */ - - if (MAP_NR(insn) < 1 || MAP_NR(insn + 8) > MAP_NR(high_memory) || - ((((dcmd = (insn[0] >> 24) & 0xff) & DCMD_TYPE_MMI) == DCMD_TYPE_MMI) && - MAP_NR(insn + 12) > MAP_NR(high_memory))) { - size = 0; - sprintf (buf, "%s%p: address out of range\n", - prefix, insn); - } else { -/* - * FIXME : (void *) cast in virt_to_bus should be unecessary, because - * it should take const void * as argument. - */ - sprintf(buf, "%s0x%lx (virt 0x%p) : 0x%08x 0x%08x (virt 0x%p)", - (prefix ? prefix : ""), virt_to_bus((void *) insn), insn, - insn[0], insn[1], bus_to_virt (insn[1])); - tmp = buf + strlen(buf); - if ((dcmd & DCMD_TYPE_MASK) == DCMD_TYPE_MMI) { - sprintf (tmp, " 0x%08x (virt 0x%p)\n", insn[2], - bus_to_virt(insn[2])); - size = 3; - } else { - sprintf (tmp, "\n"); - size = 2; - } - } - - if (kernel) - printk ("%s", buf); -#ifdef NCR_DEBUG - else { - size_t len = strlen(buf); - debugger_kernel_write(host, buf, len); - } -#endif - return size; -} - -/* - * Function : static const char *ncr_state (int state) - * - * Purpose : convert state (probably from hostdata->state) to a string - * - * Inputs : state - * - * Returns : char * representation of state, "unknown" on error. - */ - -static const char * -ncr_state (int state) { - switch (state) { - case STATE_HALTED: return "halted"; - case STATE_WAITING: return "waiting"; - case STATE_RUNNING: return "running"; - case STATE_ABORTING: return "aborting"; - case STATE_DISABLED: return "disabled"; - default: return "unknown"; - } -} - -/* - * Function : int NCR53c7xx_abort (Scsi_Cmnd *cmd) - * - * Purpose : Abort an errant SCSI command, doing all necessary - * cleanup of the issue_queue, running_list, shared Linux/NCR - * dsa issue and reconnect queues. - * - * Inputs : cmd - command to abort, code - entire result field - * - * Returns : 0 on success, -1 on failure. - */ - -int -NCR53c7xx_abort (Scsi_Cmnd *cmd) { - NCR53c7x0_local_declare(); - struct Scsi_Host *host = cmd->host; - struct NCR53c7x0_hostdata *hostdata = host ? (struct NCR53c7x0_hostdata *) - host->hostdata : NULL; - unsigned long flags; - struct NCR53c7x0_cmd *curr, **prev; - Scsi_Cmnd *me, **last; -#if 0 - static long cache_pid = -1; -#endif - - - if (!host) { - printk ("Bogus SCSI command pid %ld; no host structure\n", - cmd->pid); - return SCSI_ABORT_ERROR; - } else if (!hostdata) { - printk ("Bogus SCSI host %d; no hostdata\n", host->host_no); - return SCSI_ABORT_ERROR; - } - NCR53c7x0_local_setup(host); - -/* - * CHECK : I don't think that reading ISTAT will unstack any interrupts, - * since we need to write the INTF bit to clear it, and SCSI/DMA - * interrupts don't clear until we read SSTAT/SIST and DSTAT registers. - * - * See that this is the case. - * - * I suspect that several of our failures may be coming from a new fatal - * interrupt (possibly due to a phase mismatch) happening after we've left - * the interrupt handler, but before the PIC has had the interrupt condition - * cleared. - */ - - if (NCR53c7x0_read8(hostdata->istat) & - (ISTAT_DIP|ISTAT_SIP| - (hostdata->chip / 100 == 8 ? ISTAT_800_INTF : 0))) { - printk ("scsi%d : dropped interrupt for command %ld\n", host->host_no, - cmd->pid); - NCR53c7x0_intr (host->irq, NULL); - return SCSI_ABORT_BUSY; - } - - save_flags(flags); - cli(); -#if 0 - if (cache_pid == cmd->pid) - panic ("scsi%d : bloody fetus %d\n", host->host_no, cmd->pid); - else - cache_pid = cmd->pid; -#endif - - -/* - * The command could be hiding in the issue_queue. This would be very - * nice, as commands can't be moved from the high level driver's issue queue - * into the shared queue until an interrupt routine is serviced, and this - * moving is atomic. - * - * If this is the case, we don't have to worry about anything - we simply - * pull the command out of the old queue, and call it aborted. - */ - - for (me = (Scsi_Cmnd *) hostdata->issue_queue, - last = (Scsi_Cmnd **) &(hostdata->issue_queue); - me && me != cmd; last = (Scsi_Cmnd **)&(me->SCp.ptr), - me = (Scsi_Cmnd *)me->SCp.ptr); - - if (me) { - *last = (Scsi_Cmnd *) me->SCp.ptr; - if (me->host_scribble) { - ((struct NCR53c7x0_cmd *)me->host_scribble)->next = hostdata->free; - hostdata->free = (struct NCR53c7x0_cmd *) me->host_scribble; - me->host_scribble = NULL; - } - cmd->result = DID_ABORT << 16; - cmd->scsi_done(cmd); - printk ("scsi%d : found command %ld in Linux issue queue\n", - host->host_no, me->pid); - restore_flags(flags); - run_process_issue_queue(); - return SCSI_ABORT_SUCCESS; - } - -/* - * That failing, the command could be in our list of already executing - * commands. If this is the case, drastic measures are called for. - */ - - for (curr = (struct NCR53c7x0_cmd *) hostdata->running_list, - prev = (struct NCR53c7x0_cmd **) &(hostdata->running_list); - curr && curr->cmd != cmd; prev = (struct NCR53c7x0_cmd **) - &(curr->next), curr = (struct NCR53c7x0_cmd *) curr->next); - - if (curr) { - if ((cmd->result & 0xff) != 0xff && (cmd->result & 0xff00) != 0xff00) { - if (prev) - *prev = (struct NCR53c7x0_cmd *) curr->next; - curr->next = (struct NCR53c7x0_cmd *) hostdata->free; - cmd->host_scribble = NULL; - hostdata->free = curr; - cmd->scsi_done(cmd); - printk ("scsi%d : found finished command %ld in running list\n", - host->host_no, cmd->pid); - restore_flags(flags); - return SCSI_ABORT_NOT_RUNNING; - } else { - printk ("scsi%d : DANGER : command running, can not abort.\n", - cmd->host->host_no); - restore_flags(flags); - return SCSI_ABORT_BUSY; - } - } - -/* - * And if we couldn't find it in any of our queues, it must have been - * a dropped interrupt. - */ - - curr = (struct NCR53c7x0_cmd *) cmd->host_scribble; - if (curr) { - curr->next = hostdata->free; - hostdata->free = curr; - cmd->host_scribble = NULL; - } - - if (((cmd->result & 0xff00) == 0xff00) || - ((cmd->result & 0xff) == 0xff)) { - printk ("scsi%d : did this command ever run?\n", host->host_no); - cmd->result = DID_ABORT << 16; - } else { - printk ("scsi%d : probably lost INTFLY, normal completion\n", - host->host_no); -/* - * FIXME : We need to add an additional flag which indicates if a - * command was ever counted as BUSY, so if we end up here we can - * decrement the busy count if and only if it is necessary. - */ - --hostdata->busy[cmd->target][cmd->lun]; - } - restore_flags(flags); - cmd->scsi_done(cmd); - -/* - * We need to run process_issue_queue since termination of this command - * may allow another queued command to execute first? - */ - return SCSI_ABORT_NOT_RUNNING; -} - -/* - * Function : int NCR53c7xx_reset (Scsi_Cmnd *cmd) - * - * Purpose : perform a hard reset of the SCSI bus and NCR - * chip. - * - * Inputs : cmd - command which caused the SCSI RESET - * - * Returns : 0 on success. - */ - -int -NCR53c7xx_reset (Scsi_Cmnd *cmd) { - NCR53c7x0_local_declare(); - unsigned long flags; - int found = 0; - struct NCR53c7x0_cmd * c; - Scsi_Cmnd *tmp; - /* - * When we call scsi_done(), it's going to wake up anything sleeping on the - * resources which were in use by the aborted commands, and we'll start to - * get new commands. - * - * We can't let this happen until after we've re-initialized the driver - * structures, and can't reinitilize those structures until after we've - * dealt with their contents. - * - * So, we need to find all of the commands which were running, stick - * them on a linked list of completed commands (we'll use the host_scribble - * pointer), do our reinitialization, and then call the done function for - * each command. - */ - Scsi_Cmnd *nuke_list = NULL; - struct Scsi_Host *host = cmd->host; - struct NCR53c7x0_hostdata *hostdata = - (struct NCR53c7x0_hostdata *) host->hostdata; - - NCR53c7x0_local_setup(host); - save_flags(flags); - cli(); - ncr_halt (host); - print_lots (host); - dump_events (host, 30); - ncr_scsi_reset (host); - for (tmp = nuke_list = return_outstanding_commands (host, 1 /* free */, - 0 /* issue */ ); tmp; tmp = (Scsi_Cmnd *) tmp->SCp.buffer) - if (tmp == cmd) { - found = 1; - break; - } - - /* - * If we didn't find the command which caused this reset in our running - * list, then we've lost it. See that it terminates normally anyway. - */ - if (!found) { - c = (struct NCR53c7x0_cmd *) cmd->host_scribble; - if (c) { - cmd->host_scribble = NULL; - c->next = hostdata->free; - hostdata->free = c; - } else - printk ("scsi%d: lost command %ld\n", host->host_no, cmd->pid); - cmd->SCp.buffer = (struct scatterlist *) nuke_list; - nuke_list = cmd; - } - - NCR53c7x0_driver_init (host); - hostdata->soft_reset (host); - if (hostdata->resets == 0) - disable(host); - else if (hostdata->resets != -1) - --hostdata->resets; - sti(); - for (; nuke_list; nuke_list = tmp) { - tmp = (Scsi_Cmnd *) nuke_list->SCp.buffer; - nuke_list->result = DID_RESET << 16; - nuke_list->scsi_done (nuke_list); - } - restore_flags(flags); - return SCSI_RESET_SUCCESS; -} - -/* - * The NCR SDMS bios follows Annex A of the SCSI-CAM draft, and - * therefore shares the scsicam_bios_param function. - */ - -/* - * Function : int insn_to_offset (Scsi_Cmnd *cmd, u32 *insn) - * - * Purpose : convert instructions stored at NCR pointer into data - * pointer offset. - * - * Inputs : cmd - SCSI command; insn - pointer to instruction. Either current - * DSP, or saved data pointer. - * - * Returns : offset on success, -1 on failure. - */ - - -static int -insn_to_offset (Scsi_Cmnd *cmd, u32 *insn) { - struct NCR53c7x0_hostdata *hostdata = - (struct NCR53c7x0_hostdata *) cmd->host->hostdata; - struct NCR53c7x0_cmd *ncmd = - (struct NCR53c7x0_cmd *) cmd->host_scribble; - int offset = 0, buffers; - struct scatterlist *segment; - char *ptr; - int found = 0; - -/* - * With the current code implementation, if the insn is inside dynamically - * generated code, the data pointer will be the instruction preceeding - * the next transfer segment. - */ - - if (!check_address ((unsigned long) ncmd, sizeof (struct NCR53c7x0_cmd)) && - ((insn >= ncmd->data_transfer_start && - insn < ncmd->data_transfer_end) || - (insn >= ncmd->residual && - insn < (ncmd->residual + - sizeof(ncmd->residual))))) { - ptr = bus_to_virt(insn[3]); - - if ((buffers = cmd->use_sg)) { - for (offset = 0, - segment = (struct scatterlist *) cmd->buffer; - buffers && !((found = ((ptr >= segment->address) && - (ptr < (segment->address + segment->length))))); - --buffers, offset += segment->length, ++segment) -#if 0 - printk("scsi%d: comparing 0x%p to 0x%p\n", - cmd->host->host_no, saved, segment->address); -#else - ; -#endif - offset += ptr - segment->address; - } else { - found = 1; - offset = ptr - (char *) (cmd->request_buffer); - } - } else if ((insn >= hostdata->script + - hostdata->E_data_transfer / sizeof(u32)) && - (insn <= hostdata->script + - hostdata->E_end_data_transfer / sizeof(u32))) { - found = 1; - offset = 0; - } - return found ? offset : -1; -} - - - -/* - * Function : void print_progress (Scsi_Cmnd *cmd) - * - * Purpose : print the current location of the saved data pointer - * - * Inputs : cmd - command we are interested in - * - */ - -static void -print_progress (Scsi_Cmnd *cmd) { - NCR53c7x0_local_declare(); - struct NCR53c7x0_cmd *ncmd = - (struct NCR53c7x0_cmd *) cmd->host_scribble; - int offset, i; - char *where; - u32 *ptr; - NCR53c7x0_local_setup (cmd->host); - for (i = 0; i < 2; ++i) { - if (check_address ((unsigned long) ncmd, - sizeof (struct NCR53c7x0_cmd)) == -1) - continue; - if (!i) { - where = "saved"; - ptr = bus_to_virt(ncmd->saved_data_pointer); - } else { - where = "active"; - ptr = bus_to_virt (NCR53c7x0_read32 (DSP_REG) - - NCR53c7x0_insn_size (NCR53c7x0_read8 (DCMD_REG)) * - sizeof(u32)); - } - offset = insn_to_offset (cmd, ptr); - - if (offset != -1) - printk ("scsi%d : %s data pointer at offset %d\n", - cmd->host->host_no, where, offset); - else { - int size; - printk ("scsi%d : can't determine %s data pointer offset\n", - cmd->host->host_no, where); - if (ncmd) { - size = print_insn (cmd->host, - bus_to_virt(ncmd->saved_data_pointer), "", 1); - print_insn (cmd->host, - bus_to_virt(ncmd->saved_data_pointer) + size * sizeof(u32), - "", 1); - } - } - } -} - - -static void -print_dsa (struct Scsi_Host *host, u32 *dsa, const char *prefix) { - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - int i, len; - char *ptr; - Scsi_Cmnd *cmd; - - if (check_address ((unsigned long) dsa, hostdata->dsa_end - - hostdata->dsa_start) == -1) { - printk("scsi%d : bad dsa virt 0x%p\n", host->host_no, dsa); - return; - } - printk("%sscsi%d : dsa at phys 0x%lx (virt 0x%p)\n" - " + %d : dsa_msgout length = %u, data = 0x%x (virt 0x%p)\n" , - prefix ? prefix : "", - host->host_no, virt_to_bus (dsa), dsa, hostdata->dsa_msgout, - dsa[hostdata->dsa_msgout / sizeof(u32)], - dsa[hostdata->dsa_msgout / sizeof(u32) + 1], - bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1])); - - /* - * Only print messages if they're sane in length so we don't - * blow the kernel printk buffer on something which won't buy us - * anything. - */ - - if (dsa[hostdata->dsa_msgout / sizeof(u32)] < - sizeof (hostdata->free->select)) - for (i = dsa[hostdata->dsa_msgout / sizeof(u32)], - ptr = bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1]); - i > 0 && !check_address ((unsigned long) ptr, 1); - ptr += len, i -= len) { - printk(" "); - len = print_msg (ptr); - printk("\n"); - if (!len) - break; - } - - printk(" + %d : select_indirect = 0x%x\n", - hostdata->dsa_select, dsa[hostdata->dsa_select / sizeof(u32)]); - cmd = (Scsi_Cmnd *) bus_to_virt(dsa[hostdata->dsa_cmnd / sizeof(u32)]); - printk(" + %d : dsa_cmnd = 0x%x ", hostdata->dsa_cmnd, - (u32) virt_to_bus(cmd)); - if (cmd) { - printk(" result = 0x%x, target = %d, lun = %d, cmd = ", - cmd->result, cmd->target, cmd->lun); - print_command(cmd->cmnd); - } else - printk("\n"); - printk(" + %d : dsa_next = 0x%x\n", hostdata->dsa_next, - dsa[hostdata->dsa_next / sizeof(u32)]); - if (cmd) { - printk("scsi%d target %d : sxfer_sanity = 0x%x, scntl3_sanity = 0x%x\n" - " script : ", - host->host_no, cmd->target, - hostdata->sync[cmd->target].sxfer_sanity, - hostdata->sync[cmd->target].scntl3_sanity); - for (i = 0; i < (sizeof(hostdata->sync[cmd->target].script) / 4); ++i) - printk ("0x%x ", hostdata->sync[cmd->target].script[i]); - printk ("\n"); - print_progress (cmd); - } -} -/* - * Function : void print_queues (Scsi_Host *host) - * - * Purpose : print the contents of the NCR issue and reconnect queues - * - * Inputs : host - SCSI host we are interested in - * - */ - -static void -print_queues (struct Scsi_Host *host) { - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - u32 *dsa, *next_dsa; - volatile u32 *current; - int left; - Scsi_Cmnd *cmd, *next_cmd; - unsigned long flags; - - printk ("scsi%d : issue queue\n", host->host_no); - - for (left = host->can_queue, cmd = (Scsi_Cmnd *) hostdata->issue_queue; - left >= 0 && cmd; - cmd = next_cmd) { - next_cmd = (Scsi_Cmnd *) cmd->SCp.ptr; - save_flags(flags); - cli(); - if (cmd->host_scribble) { - if (check_address ((unsigned long) (cmd->host_scribble), - sizeof (cmd->host_scribble)) == -1) - printk ("scsi%d: scsi pid %ld bad pointer to NCR53c7x0_cmd\n", - host->host_no, cmd->pid); - /* print_dsa does sanity check on address, no need to check */ - else - print_dsa (host, ((struct NCR53c7x0_cmd *) cmd->host_scribble) - -> dsa, ""); - } else - printk ("scsi%d : scsi pid %ld for target %d lun %d has no NCR53c7x0_cmd\n", - host->host_no, cmd->pid, cmd->target, cmd->lun); - restore_flags(flags); - } - - if (left <= 0) { - printk ("scsi%d : loop detected in issue queue\n", - host->host_no); - } - - /* - * Traverse the NCR reconnect and start DSA structures, printing out - * each element until we hit the end or detect a loop. Currently, - * the reconnect structure is a linked list; and the start structure - * is an array. Eventually, the reconnect structure will become a - * list as well, since this simplifies the code. - */ - - printk ("scsi%d : schedule dsa array :\n", host->host_no); - for (left = host->can_queue, current = hostdata->schedule; - left > 0; current += 2, --left) - if (current[0] != hostdata->NOP_insn) -/* FIXME : convert pointer to dsa_begin to pointer to dsa. */ - print_dsa (host, bus_to_virt (current[1] - - (hostdata->E_dsa_code_begin - - hostdata->E_dsa_code_template)), ""); - printk ("scsi%d : end schedule dsa array\n", host->host_no); - - printk ("scsi%d : reconnect_dsa_head :\n", host->host_no); - - for (left = host->can_queue, - dsa = bus_to_virt (hostdata->reconnect_dsa_head); - left >= 0 && dsa; - dsa = next_dsa) { - save_flags (flags); - cli(); - if (check_address ((unsigned long) dsa, sizeof(dsa)) == -1) { - printk ("scsi%d: bad DSA pointer 0x%p", host->host_no, - dsa); - next_dsa = NULL; - } - else - { - next_dsa = bus_to_virt(dsa[hostdata->dsa_next / sizeof(u32)]); - print_dsa (host, dsa, ""); - } - restore_flags(flags); - } - printk ("scsi%d : end reconnect_dsa_head\n", host->host_no); - if (left < 0) - printk("scsi%d: possible loop in ncr reconnect list\n", - host->host_no); -} - -static void -print_lots (struct Scsi_Host *host) { - NCR53c7x0_local_declare(); - struct NCR53c7x0_hostdata *hostdata = - (struct NCR53c7x0_hostdata *) host->hostdata; - u32 *dsp_next, *dsp, *dsa, dbc_dcmd; - unsigned char dcmd, sbcl; - int i, size; - NCR53c7x0_local_setup(host); - - if ((dsp_next = bus_to_virt(NCR53c7x0_read32 (DSP_REG)))) { - dbc_dcmd = NCR53c7x0_read32(DBC_REG); - dcmd = (dbc_dcmd & 0xff000000) >> 24; - dsp = dsp_next - NCR53c7x0_insn_size(dcmd); - dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG)); - sbcl = NCR53c7x0_read8 (SBCL_REG); - - - printk ("scsi%d : DCMD|DBC=0x%x, DNAD=0x%x (virt 0x%p)\n" - " DSA=0x%lx (virt 0x%p)\n" - " DSPS=0x%x, TEMP=0x%x (virt 0x%p), DMODE=0x%x\n" - " SXFER=0x%x, SCNTL3=0x%x\n" - " %s%s%sphase=%s, %d bytes in SCSI FIFO\n" - " STEST0=0x%x\n", - host->host_no, dbc_dcmd, NCR53c7x0_read32(DNAD_REG), - bus_to_virt(NCR53c7x0_read32(DNAD_REG)), - virt_to_bus(dsa), dsa, - NCR53c7x0_read32(DSPS_REG), NCR53c7x0_read32(TEMP_REG), - bus_to_virt (NCR53c7x0_read32(TEMP_REG)), - (int) NCR53c7x0_read8(hostdata->dmode), - (int) NCR53c7x0_read8(SXFER_REG), - (int) NCR53c7x0_read8(SCNTL3_REG_800), - (sbcl & SBCL_BSY) ? "BSY " : "", - (sbcl & SBCL_SEL) ? "SEL " : "", - (sbcl & SBCL_REQ) ? "REQ " : "", - sstat2_to_phase(NCR53c7x0_read8 (((hostdata->chip / 100) == 8) ? - SSTAT1_REG : SSTAT2_REG)), - (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ? - SSTAT1_REG : SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT, - NCR53c7x0_read8 (STEST0_REG_800)); - printk ("scsi%d : DSP 0x%lx (virt 0x%p) ->\n", host->host_no, - virt_to_bus(dsp), dsp); - for (i = 6; i > 0; --i, dsp += size) - size = print_insn (host, dsp, "", 1); - if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) { - printk ("scsi%d : connected (SDID=0x%x, SSID=0x%x)\n", - host->host_no, NCR53c7x0_read8 (SDID_REG_800), - NCR53c7x0_read8 (SSID_REG_800)); - print_dsa (host, dsa, ""); - } - -#if 1 - print_queues (host); -#endif - } -} - -/* - * Function : static int shutdown (struct Scsi_Host *host) - * - * Purpose : does a clean (we hope) shutdown of the NCR SCSI - * chip. Use prior to dumping core, unloading the NCR driver, - * - * Returns : 0 on success - */ -static int -shutdown (struct Scsi_Host *host) { - NCR53c7x0_local_declare(); - unsigned long flags; - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - NCR53c7x0_local_setup(host); - save_flags (flags); - cli(); -/* Get in a state where we can reset the SCSI bus */ - ncr_halt (host); - ncr_scsi_reset (host); - hostdata->soft_reset(host); - - disable (host); - restore_flags (flags); - return 0; -} - -/* - * Function : void ncr_scsi_reset (struct Scsi_Host *host) - * - * Purpose : reset the SCSI bus. - */ - -static void -ncr_scsi_reset (struct Scsi_Host *host) { - NCR53c7x0_local_declare(); - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - unsigned long flags; - int sien = 0; - NCR53c7x0_local_setup(host); - save_flags (flags); - cli(); - if ((hostdata->chip / 100) == 8) { - sien = NCR53c7x0_read8(SIEN0_REG_800); - NCR53c7x0_write8(SIEN0_REG_800, sien & ~SIEN_RST); - } - NCR53c7x0_write8(SCNTL1_REG, SCNTL1_RST); - udelay(25); /* Minimum amount of time to assert RST */ - NCR53c7x0_write8(SCNTL1_REG, 0); - if ((hostdata->chip / 100) == 8) { - NCR53c7x0_write8(SIEN0_REG_800, sien); - } - restore_flags (flags); -} - -/* - * Function : void hard_reset (struct Scsi_Host *host) - * - */ - -static void -hard_reset (struct Scsi_Host *host) { - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - unsigned long flags; - save_flags (flags); - cli(); - ncr_scsi_reset(host); - NCR53c7x0_driver_init (host); - if (hostdata->soft_reset) - hostdata->soft_reset (host); - restore_flags(flags); -} - - -/* - * Function : Scsi_Cmnd *return_outstanding_commands (struct Scsi_Host *host, - * int free, int issue) - * - * Purpose : return a linked list (using the SCp.buffer field as next, - * so we don't perturb hostdata. We don't use a field of the - * NCR53c7x0_cmd structure since we may not have allocated one - * for the command causing the reset.) of Scsi_Cmnd structures that - * had propogated bellow the Linux issue queue level. If free is set, - * free the NCR53c7x0_cmd structures which are associated with - * the Scsi_Cmnd structures, and clean up any internal - * NCR lists that the commands were on. If issue is set, - * also return commands in the issue queue. - * - * Returns : linked list of commands - * - * NOTE : the caller should insure that the NCR chip is halted - * if the free flag is set. - */ - -static Scsi_Cmnd * -return_outstanding_commands (struct Scsi_Host *host, int free, int issue) { - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - struct NCR53c7x0_cmd *c; - int i; - u32 *current; - Scsi_Cmnd *list = NULL, *tmp; - for (c = (struct NCR53c7x0_cmd *) hostdata->running_list; c; - c = (struct NCR53c7x0_cmd *) c->next) { - if (c->cmd->SCp.buffer) { - printk ("scsi%d : loop detected in running list!\n", host->host_no); - break; - } else { - printk ("The sti() implicit in a printk() prevents hangs\n"); - break; - } - - c->cmd->SCp.buffer = (struct scatterlist *) list; - list = c->cmd; - if (free) { - c->next = hostdata->free; - hostdata->free = c; - } - } - - if (free) { - for (i = 0, current = (u32 *) hostdata->schedule; - i < host->can_queue; ++i, current += 2) { - current[0] = hostdata->NOP_insn; - current[1] = 0xdeadbeef; - } - hostdata->current = NULL; - } - - if (issue) { - for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp; tmp = tmp->next) { - if (tmp->SCp.buffer) { - printk ("scsi%d : loop detected in issue queue!\n", - host->host_no); - break; - } - tmp->SCp.buffer = (struct scatterlist *) list; - list = tmp; - } - if (free) - hostdata->issue_queue = NULL; - - } - return list; -} - -/* - * Function : static int disable (struct Scsi_Host *host) - * - * Purpose : disables the given NCR host, causing all commands - * to return a driver error. Call this so we can unload the - * module during development and try again. Eventually, - * we should be able to find clean workarrounds for these - * problems. - * - * Inputs : host - hostadapter to twiddle - * - * Returns : 0 on success. - */ - -static int -disable (struct Scsi_Host *host) { - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - unsigned long flags; - Scsi_Cmnd *nuke_list, *tmp; - save_flags(flags); - cli(); - if (hostdata->state != STATE_HALTED) - ncr_halt (host); - nuke_list = return_outstanding_commands (host, 1 /* free */, 1 /* issue */); - hard_reset (host); - hostdata->state = STATE_DISABLED; - restore_flags(flags); - printk ("scsi%d : nuking commands\n", host->host_no); - for (; nuke_list; nuke_list = tmp) { - tmp = (Scsi_Cmnd *) nuke_list->SCp.buffer; - nuke_list->result = DID_ERROR << 16; - nuke_list->scsi_done(nuke_list); - } - printk ("scsi%d : done. \n", host->host_no); - printk (KERN_ALERT "scsi%d : disabled. Unload and reload\n", - host->host_no); - return 0; -} - -/* - * Function : static int ncr_halt (struct Scsi_Host *host) - * - * Purpose : halts the SCSI SCRIPTS(tm) processor on the NCR chip - * - * Inputs : host - SCSI chip to halt - * - * Returns : 0 on success - */ - -static int -ncr_halt (struct Scsi_Host *host) { - NCR53c7x0_local_declare(); - unsigned long flags; - unsigned char istat, tmp; - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - int stage; - NCR53c7x0_local_setup(host); - - save_flags(flags); - cli(); - /* Stage 0 : eat all interrupts - Stage 1 : set ABORT - Stage 2 : eat all but abort interrupts - Stage 3 : eat all interrupts - */ - for (stage = 0;;) { - if (stage == 1) { - NCR53c7x0_write8(hostdata->istat, ISTAT_ABRT); - ++stage; - } - istat = NCR53c7x0_read8 (hostdata->istat); - if (istat & ISTAT_SIP) { - if ((hostdata->chip / 100) == 8) { - tmp = NCR53c7x0_read8(SIST0_REG_800); - udelay(1); - tmp = NCR53c7x0_read8(SIST1_REG_800); - } else { - tmp = NCR53c7x0_read8(SSTAT0_REG); - } - } else if (istat & ISTAT_DIP) { - tmp = NCR53c7x0_read8(DSTAT_REG); - if (stage == 2) { - if (tmp & DSTAT_ABRT) { - NCR53c7x0_write8(hostdata->istat, 0); - ++stage; - } else { - printk(KERN_ALERT "scsi%d : could not halt NCR chip\n", - host->host_no); - disable (host); - } - } - } - if (!(istat & (ISTAT_SIP|ISTAT_DIP))) - if (stage == 0) - ++stage; - else if (stage == 3) - break; - } - hostdata->state = STATE_HALTED; - restore_flags(flags); -#if 0 - print_lots (host); -#endif - return 0; -} - -/* - * Function: event_name (int event) - * - * Purpose: map event enum into user-readable strings. - */ - -static const char * -event_name (int event) { - switch (event) { - case EVENT_NONE: return "none"; - case EVENT_ISSUE_QUEUE: return "to issue queue"; - case EVENT_START_QUEUE: return "to start queue"; - case EVENT_SELECT: return "selected"; - case EVENT_DISCONNECT: return "disconnected"; - case EVENT_RESELECT: return "reselected"; - case EVENT_COMPLETE: return "completed"; - case EVENT_IDLE: return "idle"; - case EVENT_SELECT_FAILED: return "select failed"; - case EVENT_BEFORE_SELECT: return "before select"; - case EVENT_RESELECT_FAILED: return "reselect failed"; - default: return "unknown"; - } -} - -/* - * Function : void dump_events (struct Scsi_Host *host, count) - * - * Purpose : print last count events which have occurred. - */ -static void -dump_events (struct Scsi_Host *host, int count) { - struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) - host->hostdata; - struct NCR53c7x0_event event; - int i; - unsigned long flags; - if (hostdata->events) { - if (count > hostdata->event_size) - count = hostdata->event_size; - for (i = hostdata->event_index; count > 0; - i = (i ? i - 1 : hostdata->event_size -1), --count) { - save_flags(flags); -/* - * By copying the event we're currently examinging with interrupts - * disabled, we can do multiple printk(), etc. operations and - * still be guaranteed that they're happening on the same - * event structure. - */ - cli(); -#if 0 - event = hostdata->events[i]; -#else - memcpy ((void *) &event, (void *) &(hostdata->events[i]), - sizeof(event)); -#endif - - restore_flags(flags); - printk ("scsi%d : %s event %d at %ld secs %ld usecs target %d lun %d\n", - host->host_no, event_name (event.event), count, - (long) event.time.tv_sec, (long) event.time.tv_usec, - event.target, event.lun); - if (event.dsa) - printk (" event for dsa 0x%lx (virt 0x%p)\n", - virt_to_bus(event.dsa), event.dsa); - if (event.pid != -1) { - printk (" event for pid %ld ", event.pid); - print_command (event.cmnd); - } - } - } -} - -/* - * Function: check_address - * - * Purpose: Check to see if a possibly corrupt pointer will fault the - * kernel. - * - * Inputs: addr - address; size - size of area - * - * Returns: 0 if area is OK, -1 on error. - * - * NOTES: should be implemented in terms of vverify on kernels - * that have it. - */ - -static int -check_address (unsigned long addr, int size) { - return (MAP_NR(addr) < 1 || MAP_NR(addr + size) > MAP_NR(high_memory) ? - -1 : 0); -} - -#ifdef MODULE -int -NCR53c7x0_release(struct Scsi_Host *host) { - struct NCR53c7x0_hostdata *hostdata = - (struct NCR53c7x0_hostdata *) host->hostdata; - struct NCR53c7x0_cmd *cmd, *tmp; - shutdown (host); - if (host->irq != IRQ_NONE) - { - int irq_count; - struct Scsi_Host *tmp; - for (irq_count = 0, tmp = first_host; tmp; tmp = tmp->next) - if (tmp->hostt == the_template && tmp->irq == host->irq) - ++irq_count; - if (irq_count == 1) - free_irq(host->irq); - } - if (host->dma_channel != DMA_NONE) - free_dma(host->dma_channel); - if (host->io_port) - release_region(host->io_port, host->n_io_port); - - for (cmd = (struct NCR53c7x0_cmd *) hostdata->free; cmd; cmd = tmp, - --hostdata->num_cmds) { - tmp = (struct NCR53c7x0_cmd *) cmd->next; - /* - * If we're going to loop, try to stop it to get a more accurate - * count of the leaked commands. - */ - cmd->next = NULL; - if (cmd->free) - cmd->free ((void *) cmd->real, cmd->size); - } - if (hostdata->num_cmds) - printk ("scsi%d : leaked %d NCR53c7x0_cmd structures\n", - host->host_no, hostdata->num_cmds); - if (hostdata->events) - vfree ((void *)hostdata->events); - return 1; -} -Scsi_Host_Template driver_template = NCR53c7xx; -#include "scsi_module.c" -#endif /* def MODULE */ diff --git a/i386/i386at/gpl/linux/scsi/53c78xx.c b/i386/i386at/gpl/linux/scsi/53c78xx.c new file mode 100644 index 0000000..74350b0 --- /dev/null +++ b/i386/i386at/gpl/linux/scsi/53c78xx.c @@ -0,0 +1,6381 @@ +/* + * PERM_OPTIONS are driver options which will be enabled for all NCR boards + * in the system at driver initialization time. + * + * Don't THINK about touching these in PERM_OPTIONS : + * OPTION_IO_MAPPED + * Memory mapped IO does not work under i86 Linux. + * + * OPTION_DEBUG_TEST1 + * Test 1 does bus mastering and interrupt tests, which will help weed + * out brain damaged main boards. + * + * These are development kernel changes. Code for them included in this + * driver release may or may not work. If you turn them on, you should be + * running the latest copy of the development sources from + * + * ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/53c7,8xx + * + * and be subscribed to the ncr53c810@colorado.edu mailing list. To + * subscribe, send mail to majordomo@colorado.edu with + * + * subscribe ncr53c810 + * + * in the text. + * + * + * OPTION_NOASYNC + * Don't negotiate for asynchronous transfers on the first command + * when OPTION_ALWAYS_SYNCHRONOUS is set. Useful for dain bramaged + * devices which do something bad rather than sending a MESSAGE + * REJECT back to us like they should if they can't cope. + * + * OPTION_SYNCHRONOUS + * Enable support for synchronous transfers. Target negotiated + * synchronous transfers will be responded to. To initiate + * a synchronous transfer request, call + * + * request_synchronous (hostno, target) + * + * from within KGDB. + * + * OPTION_ALWAYS_SYNCHRONOUS + * Negotiate for synchronous transfers with every target after + * driver initialization or a SCSI bus reset. This is a bit dangerous, + * since there are some dain bramaged SCSI devices which will accept + * SDTR messages but keep talking asynchronously. + * + * OPTION_DISCONNECT + * Enable support for disconnect/reconnect. To change the + * default setting on a given host adapter, call + * + * request_disconnect (hostno, allow) + * + * where allow is non-zero to allow, 0 to disallow. + * + * If you really want to run 10MHz FAST SCSI-II transfers, you should + * know that the NCR driver currently ignores parity information. Most + * systems do 5MHz SCSI fine. I've seen a lot that have problems faster + * than 8MHz. To play it safe, we only request 5MHz transfers. + * + * If you'd rather get 10MHz transfers, edit sdtr_message and change + * the fourth byte from 50 to 25. + */ + +#define PERM_OPTIONS (OPTION_IO_MAPPED|OPTION_DEBUG_TEST1|OPTION_DISCONNECT|\ + OPTION_SYNCHRONOUS) + +/* + * Sponsored by + * iX Multiuser Multitasking Magazine + * Hannover, Germany + * hm@ix.de + * + * Copyright 1993, 1994, 1995 Drew Eckhardt + * Visionary Computing + * (Unix and Linux consulting and custom programming) + * drew@PoohSticks.ORG + * +1 (303) 786-7975 + * + * TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation. + * + * For more information, please consult + * + * NCR53C810 + * SCSI I/O Processor + * Programmer's Guide + * + * NCR 53C810 + * PCI-SCSI I/O Processor + * Data Manual + * + * NCR 53C810/53C820 + * PCI-SCSI I/O Processor Design In Guide + * + * For literature on Symbios Logic Inc. formerly NCR, SCSI, + * and Communication products please call (800) 334-5454 or + * (719) 536-3300. + * + * PCI BIOS Specification Revision + * PCI Local Bus Specification + * PCI System Design Guide + * + * PCI Special Interest Group + * M/S HF3-15A + * 5200 N.E. Elam Young Parkway + * Hillsboro, Oregon 97124-6497 + * +1 (503) 696-2000 + * +1 (800) 433-5177 + */ + +/* + * Design issues : + * The cumulative latency needed to propagate a read/write request + * through the file system, buffer cache, driver stacks, SCSI host, and + * SCSI device is ultimately the limiting factor in throughput once we + * have a sufficiently fast host adapter. + * + * So, to maximize performance we want to keep the ratio of latency to data + * transfer time to a minimum by + * 1. Minimizing the total number of commands sent (typical command latency + * including drive and bus mastering host overhead is as high as 4.5ms) + * to transfer a given amount of data. + * + * This is accomplished by placing no arbitrary limit on the number + * of scatter/gather buffers supported, since we can transfer 1K + * per scatter/gather buffer without Eric's cluster patches, + * 4K with. + * + * 2. Minimizing the number of fatal interrupts serviced, since + * fatal interrupts halt the SCSI I/O processor. Basically, + * this means offloading the practical maximum amount of processing + * to the SCSI chip. + * + * On the NCR53c810/820/720, this is accomplished by using + * interrupt-on-the-fly signals when commands complete, + * and only handling fatal errors and SDTR / WDTR messages + * in the host code. + * + * On the NCR53c710, interrupts are generated as on the NCR53c8x0, + * only the lack of a interrupt-on-the-fly facility complicates + * things. Also, SCSI ID registers and commands are + * bit fielded rather than binary encoded. + * + * On the NCR53c700 and NCR53c700-66, operations that are done via + * indirect, table mode on the more advanced chips must be + * replaced by calls through a jump table which + * acts as a surrogate for the DSA. Unfortunately, this + * will mean that we must service an interrupt for each + * disconnect/reconnect. + * + * 3. Eliminating latency by pipelining operations at the different levels. + * + * This driver allows a configurable number of commands to be enqueued + * for each target/lun combination (experimentally, I have discovered + * that two seems to work best) and will ultimately allow for + * SCSI-II tagged queuing. + * + * + * Architecture : + * This driver is built around a Linux queue of commands waiting to + * be executed, and a shared Linux/NCR array of commands to start. Commands + * are transfered to the array by the run_process_issue_queue() function + * which is called whenever a command completes. + * + * As commands are completed, the interrupt routine is triggered, + * looks for commands in the linked list of completed commands with + * valid status, removes these commands from a list of running commands, + * calls the done routine, and flags their target/luns as not busy. + * + * Due to limitations in the intelligence of the NCR chips, certain + * concessions are made. In many cases, it is easier to dynamically + * generate/fix-up code rather than calculate on the NCR at run time. + * So, code is generated or fixed up for + * + * - Handling data transfers, using a variable number of MOVE instructions + * interspersed with CALL MSG_IN, WHEN MSGIN instructions. + * + * The DATAIN and DATAOUT routines are separate, so that an incorrect + * direction can be trapped, and space isn't wasted. + * + * It may turn out that we're better off using some sort + * of table indirect instruction in a loop with a variable + * sized table on the NCR53c710 and newer chips. + * + * - Checking for reselection (NCR53c710 and better) + * + * - Handling the details of SCSI context switches (NCR53c710 and better), + * such as reprogramming appropriate synchronous parameters, + * removing the dsa structure from the NCR's queue of outstanding + * commands, etc. + * + */ + +/* + * Accommodate differences between stock 1.2.x and 1.3.x asm-i386/types.h + * so lusers can drop in 53c7,8xx.* and get something which compiles + * without warnings. + */ + +#if !defined(LINUX_1_2) && !defined(LINUX_1_3) +#include +#if LINUX_VERSION_CODE > 65536 + 3 * 256 +#define LINUX_1_3 +#else +#define LINUX_1_2 +#endif +#endif + +#ifdef LINUX_1_2 +#define u32 bogus_u32 +#define s32 bogus_s32 +#include +#undef u32 +#undef s32 +typedef __signed__ int s32; +typedef unsigned int u32; +#endif /* def LINUX_1_2 */ + +#ifdef MODULE +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef LINUX_1_2 +#include "../block/blk.h" +#else +#include +#endif +#undef current + +#include "scsi.h" +#include "hosts.h" +#include "53c7,8xx.h" +#include "constants.h" +#include "sd.h" +#include +#include + +#ifndef LINUX_1_2 +struct proc_dir_entry proc_scsi_ncr53c7xx = { + PROC_SCSI_NCR53C7xx, 9, "ncr53c7xx", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; +#endif + +static int check_address (unsigned long addr, int size); +static void dump_events (struct Scsi_Host *host, int count); +static Scsi_Cmnd * return_outstanding_commands (struct Scsi_Host *host, + int free, int issue); +static void hard_reset (struct Scsi_Host *host); +static void ncr_scsi_reset (struct Scsi_Host *host); +static void print_lots (struct Scsi_Host *host); +static void set_synchronous (struct Scsi_Host *host, int target, int sxfer, + int scntl3, int now_connected); +static int datapath_residual (struct Scsi_Host *host); +static const char * sbcl_to_phase (int sbcl); +static void print_progress (Scsi_Cmnd *cmd); +static void print_queues (struct Scsi_Host *host); +static void process_issue_queue (unsigned long flags); +static int shutdown (struct Scsi_Host *host); +static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int result); +static int disable (struct Scsi_Host *host); +static int NCR53c8xx_run_tests (struct Scsi_Host *host); +static int NCR53c8xx_script_len; +static int NCR53c8xx_dsa_len; +static void NCR53c7x0_intr(int irq, struct pt_regs * regs); +static int ncr_halt (struct Scsi_Host *host); +static void intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd + *cmd); +static void intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd); +static void print_dsa (struct Scsi_Host *host, u32 *dsa, + const char *prefix); +static int print_insn (struct Scsi_Host *host, const u32 *insn, + const char *prefix, int kernel); + +static void NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd); +static void NCR53c8x0_init_fixup (struct Scsi_Host *host); +static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct + NCR53c7x0_cmd *cmd); +static void NCR53c8x0_soft_reset (struct Scsi_Host *host); + +/* INSMOD variables */ +static long long perm_options = PERM_OPTIONS; +/* 14 = .5s; 15 is max; decreasing divides by two. */ +static int selection_timeout = 14; +/* Size of event list (per host adapter) */ +static int track_events = 0; + +static struct Scsi_Host *first_host = NULL; /* Head of list of NCR boards */ +static Scsi_Host_Template *the_template = NULL; + +/* + * KNOWN BUGS : + * - There is some sort of conflict when the PPP driver is compiled with + * support for 16 channels? + * + * - On systems which predate the 1.3.x initialization order change, + * the NCR driver will cause Cannot get free page messages to appear. + * These are harmless, but I don't know of an easy way to avoid them. + * + * - With OPTION_DISCONNECT, on two systems under unknown circumstances, + * we get a PHASE MISMATCH with DSA set to zero (suggests that we + * are occurring somewhere in the reselection code) where + * DSP=some value DCMD|DBC=same value. + * + * Closer inspection suggests that we may be trying to execute + * some portion of the DSA? + * scsi0 : handling residual transfer (+ 0 bytes from DMA FIFO) + * scsi0 : handling residual transfer (+ 0 bytes from DMA FIFO) + * scsi0 : no current command : unexpected phase MSGIN. + * DSP=0x1c46cc, DCMD|DBC=0x1c46ac, DSA=0x0 + * DSPS=0x0, TEMP=0x1c3e70, DMODE=0x80 + * scsi0 : DSP-> + * 001c46cc : 0x001c46cc 0x00000000 + * 001c46d4 : 0x001c5ea0 0x000011f8 + * + * Changed the print code in the phase_mismatch handler so + * that we call print_lots to try and diagnose this. + * + */ + +/* + * Possible future direction of architecture for max performance : + * + * We're using a single start array for the NCR chip. This is + * sub-optimal, because we cannot add a command which would conflict with + * an executing command to this start queue, and therefore must insert the + * next command for a given I/T/L combination after the first has completed; + * incurring our interrupt latency between SCSI commands. + * + * To allow furthur pipelining of the NCR and host CPU operation, we want + * to set things up so that immediately on termination of a command destined + * for a given LUN, we get that LUN busy again. + * + * To do this, we need to add a 32 bit pointer to which is jumped to + * on completion of a command. If no new command is available, this + * would point to the usual DSA issue queue select routine. + * + * If one were, it would point to a per-NCR53c7x0_cmd select routine + * which starts execution immediately, inserting the command at the head + * of the start queue if the NCR chip is selected or reselected. + * + * We would chanage so that we keep a list of outstanding commands + * for each unit, rather than a single running_list. We'd insert + * a new command into the right running list; if the NCR didn't + * have something running for that yet, we'd put it in the + * start queue as well. Some magic needs to happen to handle the + * race condition between the first command terminating before the + * new one is written. + * + * Potential for profiling : + * Call do_gettimeofday(struct timeval *tv) to get 800ns resolution. + */ + + +/* + * TODO : + * 1. To support WIDE transfers, not much needs to happen. We + * should do CHMOVE instructions instead of MOVEs when + * we have scatter/gather segments of uneven length. When + * we do this, we need to handle the case where we disconnect + * between segments. + * + * 2. Currently, when Icky things happen we do a FATAL(). Instead, + * we want to do an integrity check on the parts of the NCR hostdata + * structure which were initialized at boot time; FATAL() if that + * fails, and otherwise try to recover. Keep track of how many + * times this has happened within a single SCSI command; if it + * gets excessive, then FATAL(). + * + * 3. Parity checking is currently disabled, and a few things should + * happen here now that we support synchronous SCSI transfers : + * 1. On soft-reset, we shuld set the EPC (Enable Parity Checking) + * and AAP (Assert SATN/ on parity error) bits in SCNTL0. + * + * 2. We should enable the parity interrupt in the SIEN0 register. + * + * 3. intr_phase_mismatch() needs to believe that message out is + * allways an "acceptable" phase to have a mismatch in. If + * the old phase was MSG_IN, we should send a MESSAGE PARITY + * error. If the old phase was something else, we should send + * a INITIATOR_DETECTED_ERROR message. Note that this could + * cause a RESTORE POINTERS message; so we should handle that + * correctly first. Instead, we should probably do an + * initiator_abort. + * + * 4. MPEE bit of CTEST4 should be set so we get interrupted if + * we detect an error. + * + * + * 5. The initial code has been tested on the NCR53c810. I don't + * have access to NCR53c700, 700-66 (Forex boards), NCR53c710 + * (NCR Pentium systems), NCR53c720, NCR53c820, or NCR53c825 boards to + * finish development on those platforms. + * + * NCR53c820/825/720 - need to add wide transfer support, including WDTR + * negotiation, programming of wide transfer capabilities + * on reselection and table indirect selection. + * + * NCR53c710 - need to add fatal interrupt or GEN code for + * command completion signaling. Need to modify all + * SDID, SCID, etc. registers, and table indirect select code + * since these use bit fielded (ie 1<NOP_insn) ? + /* + * If the IF TRUE bit is set, it's a JUMP instruction. The + * operand is a bus pointer to the dsa_begin routine for this DSA. The + * dsa field of the NCR53c7x0_cmd structure starts with the + * DSA code template. By converting to a virtual address, + * subtracting the code template size, and offset of the + * dsa field, we end up with a pointer to the start of the + * structure (alternatively, we could use the + * dsa_cmnd field, an anachronism from when we weren't + * sure what the relationship between the NCR structures + * and host structures were going to be. + */ + (struct NCR53c7x0_cmd *) ((char *) bus_to_virt (issue[1]) - + (hostdata->E_dsa_code_begin - hostdata->E_dsa_code_template) - + offsetof(struct NCR53c7x0_cmd, dsa)) + /* If the IF TRUE bit is not set, it's a NOP */ + : NULL; +} + + +/* + * Function : static internal_setup(int board, int chip, char *str, int *ints) + * + * Purpose : LILO command line initialization of the overrides array, + * + * Inputs : board - currently, unsupported. chip - 700, 70066, 710, 720 + * 810, 815, 820, 825, although currently only the NCR53c810 is + * supported. + * + */ + +static void +internal_setup(int board, int chip, char *str, int *ints) { + unsigned char pci; /* Specifies a PCI override, with bus, device, + function */ + + pci = (str && !strcmp (str, "pci")) ? 1 : 0; + +/* + * Override syntaxes are as follows : + * ncr53c700,ncr53c700-66,ncr53c710,ncr53c720=mem,io,irq,dma + * ncr53c810,ncr53c820,ncr53c825=mem,io,irq or pci,bus,device,function + */ + + if (commandline_current < OVERRIDE_LIMIT) { + overrides[commandline_current].pci = pci ? 1 : 0; + if (!pci) { + overrides[commandline_current].data.normal.base = ints[1]; + overrides[commandline_current].data.normal.io_port = ints[2]; + overrides[commandline_current].data.normal.irq = ints[3]; + overrides[commandline_current].data.normal.dma = (ints[0] >= 4) ? + ints[4] : DMA_NONE; + /* FIXME: options is now a long long */ + overrides[commandline_current].options = (ints[0] >= 5) ? + ints[5] : 0; + } else { + overrides[commandline_current].data.pci.bus = ints[1]; + overrides[commandline_current].data.pci.device = ints[2]; + overrides[commandline_current].data.pci.function = ints[3]; + /* FIXME: options is now a long long */ + overrides[commandline_current].options = (ints[0] >= 4) ? + ints[4] : 0; + } + overrides[commandline_current].board = board; + overrides[commandline_current].chip = chip; + ++commandline_current; + ++no_overrides; + } else { + printk ("53c7,7x0.c:internal_setup() : too many overrides\n"); + } +} + +/* + * XXX - we might want to implement a single override function + * with a chip type field, revamp the command line configuration, + * etc. + */ + +#define setup_wrapper(x) \ +void ncr53c##x##_setup (char *str, int *ints) { \ + internal_setup (BOARD_GENERIC, x, str, ints); \ +} + +setup_wrapper(700) +setup_wrapper(70066) +setup_wrapper(710) +setup_wrapper(720) +setup_wrapper(810) +setup_wrapper(815) +setup_wrapper(820) +setup_wrapper(825) + +/* + * FIXME: we should junk these, in favor of synchronous_want and + * wide_want in the NCR53c7x0_hostdata structure. + */ + +/* Template for "preferred" synchronous transfer parameters. */ + +static const unsigned char sdtr_message[] = { + EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 50 /* *4ns */, 8 /* off */ +}; + +/* Template to request asynchronous transfers */ + +static const unsigned char async_message[] = { + EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 0, 0 /* asynchronous */ +}; + +/* Template for "preferred" WIDE transfer parameters */ + +static const unsigned char wdtr_message[] = { + EXTENDED_MESSAGE, 2 /* length */, EXTENDED_WDTR, 1 /* 2^1 bytes */ +}; + +/* + * Function : struct Scsi_Host *find_host (int host) + * + * Purpose : KGDB support function which translates a host number + * to a host structure. + * + * Inputs : host - number of SCSI host + * + * Returns : NULL on failure, pointer to host structure on success. + */ + +static struct Scsi_Host * +find_host (int host) { + struct Scsi_Host *h; + for (h = first_host; h && h->host_no != host; h = h->next); + if (!h) { + printk (KERN_ALERT "scsi%d not found\n", host); + return NULL; + } else if (h->hostt != the_template) { + printk (KERN_ALERT "scsi%d is not a NCR board\n", host); + return NULL; + } + return h; +} + +/* + * Function : request_synchronous (int host, int target) + * + * Purpose : KGDB interface which will allow us to negotiate for + * synchronous transfers. This ill be replaced with a more + * integrated function; perhaps a new entry in the scsi_host + * structure, accessable via an ioctl() or perhaps /proc/scsi. + * + * Inputs : host - number of SCSI host; target - number of target. + * + * Returns : 0 when negotiation has been setup for next SCSI command, + * -1 on failure. + */ + +static int +request_synchronous (int host, int target) { + struct Scsi_Host *h; + struct NCR53c7x0_hostdata *hostdata; + unsigned long flags; + if (target < 0) { + printk (KERN_ALERT "target %d is bogus\n", target); + return -1; + } + if (!(h = find_host (host))) + return -1; + else if (h->this_id == target) { + printk (KERN_ALERT "target %d is host ID\n", target); + return -1; + } +#ifndef LINUX_1_2 + else if (target > h->max_id) { + printk (KERN_ALERT "target %d exceeds maximum of %d\n", target, + h->max_id); + return -1; + } +#endif + hostdata = (struct NCR53c7x0_hostdata *)h->hostdata; + + save_flags(flags); + cli(); + if (hostdata->initiate_sdtr & (1 << target)) { + restore_flags(flags); + printk (KERN_ALERT "target %d allready doing SDTR\n", target); + return -1; + } + hostdata->initiate_sdtr |= (1 << target); + restore_flags(flags); + return 0; +} + +/* + * Function : request_disconnect (int host, int on_or_off) + * + * Purpose : KGDB support function, tells us to allow or disallow + * disconnections. + * + * Inputs : host - number of SCSI host; on_or_off - non-zero to allow, + * zero to disallow. + * + * Returns : 0 on success, * -1 on failure. + */ + +static int +request_disconnect (int host, int on_or_off) { + struct Scsi_Host *h; + struct NCR53c7x0_hostdata *hostdata; + if (!(h = find_host (host))) + return -1; + hostdata = (struct NCR53c7x0_hostdata *) h->hostdata; + if (on_or_off) + hostdata->options |= OPTION_DISCONNECT; + else + hostdata->options &= ~OPTION_DISCONNECT; + return 0; +} + +/* + * Function : static void NCR53c7x0_driver_init (struct Scsi_Host *host) + * + * Purpose : Initialize internal structures, as required on startup, or + * after a SCSI bus reset. + * + * Inputs : host - pointer to this host adapter's structure + */ + +static void +NCR53c7x0_driver_init (struct Scsi_Host *host) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + int i, j; + u32 *current; + for (i = 0; i < 16; ++i) { + hostdata->request_sense[i] = 0; + for (j = 0; j < 8; ++j) + hostdata->busy[i][j] = 0; + set_synchronous (host, i, /* sxfer */ 0, hostdata->saved_scntl3, 0); + } + hostdata->issue_queue = NULL; + hostdata->running_list = hostdata->finished_queue = + hostdata->current = NULL; + for (i = 0, current = (u32 *) hostdata->schedule; + i < host->can_queue; ++i, current += 2) { + current[0] = hostdata->NOP_insn; + current[1] = 0xdeadbeef; + } + current[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | DBC_TCI_TRUE; + current[1] = (u32) virt_to_bus (hostdata->script) + + hostdata->E_wait_reselect; + hostdata->reconnect_dsa_head = 0; + hostdata->addr_reconnect_dsa_head = (u32) + virt_to_bus((void *) &(hostdata->reconnect_dsa_head)); + hostdata->expecting_iid = 0; + hostdata->expecting_sto = 0; + if (hostdata->options & OPTION_ALWAYS_SYNCHRONOUS) + hostdata->initiate_sdtr = 0xffff; + else + hostdata->initiate_sdtr = 0; + hostdata->talked_to = 0; + hostdata->idle = 1; +} + +/* + * Function : static int ccf_to_clock (int ccf) + * + * Purpose : Return the largest SCSI clock allowable for a given + * clock conversion factor, allowing us to do synchronous periods + * when we don't know what the SCSI clock is by taking at least + * as long as the device says we can. + * + * Inputs : ccf + * + * Returns : clock on success, -1 on failure. + */ + +static int +ccf_to_clock (int ccf) { + switch (ccf) { + case 1: return 25000000; /* Divide by 1.0 */ + case 2: return 37500000; /* Divide by 1.5 */ + case 3: return 50000000; /* Divide by 2.0 */ + case 0: /* Divide by 3.0 */ + case 4: return 66000000; + default: return -1; + } +} + +/* + * Function : static int clock_to_ccf (int clock) + * + * Purpose : Return the clock conversion factor for a given SCSI clock. + * + * Inputs : clock - SCSI clock expressed in Hz. + * + * Returns : ccf on success, -1 on failure. + */ + +static int +clock_to_ccf (int clock) { + if (clock < 16666666) + return -1; + if (clock < 25000000) + return 1; /* Divide by 1.0 */ + else if (clock < 37500000) + return 2; /* Divide by 1.5 */ + else if (clock < 50000000) + return 3; /* Divide by 2.0 */ + else if (clock < 66000000) + return 4; /* Divide by 3.0 */ + else + return -1; +} + +/* + * Function : static int NCR53c7x0_init (struct Scsi_Host *host) + * + * Purpose : initialize the internal structures for a given SCSI host + * + * Inputs : host - pointer to this host adapter's structure + * + * Preconditions : when this function is called, the chip_type + * field of the hostdata structure MUST have been set. + * + * Returns : 0 on success, -1 on failure. + */ + +static int +NCR53c7x0_init (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + int i, ccf, expected_ccf; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + struct Scsi_Host *search; + /* + * There are some things which we need to know about in order to provide + * a semblance of support. Print 'em if they aren't what we expect, + * otherwise don't add to the noise. + * + * -1 means we don't know what to expect. + */ + int expected_id = -1; + int expected_clock = -1; + int uninitialized = 0; + /* + * FIXME : this is only on Intel boxes. On other platforms, this + * will differ. + */ + int expected_mapping = OPTION_IO_MAPPED; + NCR53c7x0_local_setup(host); + + switch (hostdata->chip) { + case 820: + case 825: +#ifdef notyet + host->max_id = 15; +#endif + /* Fall through */ + case 810: + case 815: + hostdata->dstat_sir_intr = NCR53c8x0_dstat_sir_intr; + hostdata->init_save_regs = NULL; + hostdata->dsa_fixup = NCR53c8xx_dsa_fixup; + hostdata->init_fixup = NCR53c8x0_init_fixup; + hostdata->soft_reset = NCR53c8x0_soft_reset; + hostdata->run_tests = NCR53c8xx_run_tests; +/* Is the SCSI clock ever anything else on these chips? */ + expected_clock = hostdata->scsi_clock = 40000000; + expected_id = 7; + break; + default: + printk ("scsi%d : chip type of %d is not supported yet, detaching.\n", + host->host_no, hostdata->chip); + scsi_unregister (host); + return -1; + } + + /* Assign constants accessed by NCR */ + hostdata->NCR53c7xx_zero = 0; + hostdata->NCR53c7xx_msg_reject = MESSAGE_REJECT; + hostdata->NCR53c7xx_msg_abort = ABORT; + hostdata->NCR53c7xx_msg_nop = NOP; + hostdata->NOP_insn = (DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24; + + if (expected_mapping == -1 || + (hostdata->options & (OPTION_MEMORY_MAPPED)) != + (expected_mapping & OPTION_MEMORY_MAPPED)) + printk ("scsi%d : using %s mapped access\n", host->host_no, + (hostdata->options & OPTION_MEMORY_MAPPED) ? "memory" : + "io"); + + hostdata->dmode = (hostdata->chip == 700 || hostdata->chip == 70066) ? + DMODE_REG_00 : DMODE_REG_10; + hostdata->istat = ((hostdata->chip / 100) == 8) ? + ISTAT_REG_800 : ISTAT_REG_700; + +/* Only the ISTAT register is readable when the NCR is running, so make + sure it's halted. */ + ncr_halt(host); + +/* + * XXX - the NCR53c700 uses bitfielded registers for SCID, SDID, etc, + * as does the 710 with one bit per SCSI ID. Conversely, the NCR + * uses a normal, 3 bit binary representation of these values. + * + * Get the rest of the NCR documentation, and FIND OUT where the change + * was. + */ +#if 0 + tmp = hostdata->this_id_mask = NCR53c7x0_read8(SCID_REG); + for (host->this_id = 0; tmp != 1; tmp >>=1, ++host->this_id); +#else + host->this_id = NCR53c7x0_read8(SCID_REG) & 15; + if (host->this_id == 0) + host->this_id = 7; /* sanitize hostid---0 doesn't make sense */ + hostdata->this_id_mask = 1 << host->this_id; +#endif + +/* + * Note : we should never encounter a board setup for ID0. So, + * if we see ID0, assume that it was uninitialized and set it + * to the industry standard 7. + */ + if (!host->this_id) { + printk("scsi%d : initiator ID was %d, changing to 7\n", + host->host_no, host->this_id); + host->this_id = 7; + hostdata->this_id_mask = 1 << 7; + uninitialized = 1; + }; + + if (expected_id == -1 || host->this_id != expected_id) + printk("scsi%d : using initiator ID %d\n", host->host_no, + host->this_id); + + /* + * Save important registers to allow a soft reset. + */ + + if ((hostdata->chip / 100) == 8) { + /* + * CTEST4 controls burst mode disable. + */ + hostdata->saved_ctest4 = NCR53c7x0_read8(CTEST4_REG_800) & + CTEST4_800_SAVE; + } else { + /* + * CTEST7 controls cache snooping, burst mode, and support for + * external differential drivers. + */ + hostdata->saved_ctest7 = NCR53c7x0_read8(CTEST7_REG) & CTEST7_SAVE; + } + + /* + * On NCR53c700 series chips, DCNTL controls the SCSI clock divisor, + * on 800 series chips, it allows for a totem-pole IRQ driver. + */ + + hostdata->saved_dcntl = NCR53c7x0_read8(DCNTL_REG); + + /* + * DCNTL_800_IRQM controls weather we are using an open drain + * driver (reset) or totem pole driver (set). In all cases, + * it's level active. I suppose this is an issue when we're trying to + * wire-or the same PCI INTx line? + */ + if ((hostdata->chip / 100) == 8) + hostdata->saved_dcntl &= ~DCNTL_800_IRQM; + + /* + * DMODE controls DMA burst length, and on 700 series chips, + * 286 mode and bus width + */ + hostdata->saved_dmode = NCR53c7x0_read8(hostdata->dmode); + + /* + * Now that burst length and enabled/disabled status is known, + * clue the user in on it. + */ + + if ((hostdata->chip / 100) == 8) { + if (hostdata->saved_ctest4 & CTEST4_800_BDIS) { + printk ("scsi%d : burst mode disabled\n", host->host_no); + } else { + switch (hostdata->saved_dmode & DMODE_BL_MASK) { + case DMODE_BL_2: i = 2; break; + case DMODE_BL_4: i = 4; break; + case DMODE_BL_8: i = 8; break; + case DMODE_BL_16: i = 16; break; + default: i = 0; + } + printk ("scsi%d : burst length %d\n", host->host_no, i); + } + } + + /* + * On NCR53c810 and NCR53c820 chips, SCNTL3 contails the synchronous + * and normal clock conversion factors. + */ + if (hostdata->chip / 100 == 8) { + expected_ccf = clock_to_ccf (expected_clock); + hostdata->saved_scntl3 = NCR53c7x0_read8(SCNTL3_REG_800); + ccf = hostdata->saved_scntl3 & SCNTL3_800_CCF_MASK; + if (expected_ccf != -1 && ccf != expected_ccf && !ccf) { + hostdata->saved_scntl3 = (hostdata->saved_scntl3 & + ~SCNTL3_800_CCF_MASK) | expected_ccf; + if (!uninitialized) { + printk ("scsi%d : reset ccf to %d from %d\n", + host->host_no, expected_ccf, ccf); + uninitialized = 1; + } + } + } else + ccf = 0; + + /* + * If we don't have a SCSI clock programmed, pick one on the upper + * bound of that allowed by NCR so that our transfers err on the + * slow side, since transfer period must be >= the agreed + * upon period. + */ + + if ((!hostdata->scsi_clock) && (hostdata->scsi_clock = ccf_to_clock (ccf)) + == -1) { + printk ("scsi%d : clock conversion factor %d unknown.\n" + " synchronous transfers disabled\n", + host->host_no, ccf); + hostdata->options &= ~OPTION_SYNCHRONOUS; + hostdata->scsi_clock = 0; + } + + if (expected_clock == -1 || hostdata->scsi_clock != expected_clock) + printk ("scsi%d : using %dMHz SCSI clock\n", host->host_no, + hostdata->scsi_clock / 1000000); + + for (i = 0; i < 16; ++i) + hostdata->cmd_allocated[i] = 0; + + if (hostdata->init_save_regs) + hostdata->init_save_regs (host); + if (hostdata->init_fixup) + hostdata->init_fixup (host); + + if (!the_template) { + the_template = host->hostt; + first_host = host; + } + + /* + * Linux SCSI drivers have always been plagued with initialization + * problems - some didn't work with the BIOS disabled since they expected + * initialization from it, some didn't work when the networking code + * was enabled and registers got scrambled, etc. + * + * To avoid problems like this, in the future, we will do a soft + * reset on the SCSI chip, taking it back to a sane state. + */ + + hostdata->soft_reset (host); + +#if 1 + hostdata->debug_count_limit = -1; +#else + hostdata->debug_count_limit = 1; +#endif + hostdata->intrs = -1; + hostdata->resets = -1; + memcpy ((void *) hostdata->synchronous_want, (void *) sdtr_message, + sizeof (hostdata->synchronous_want)); + + NCR53c7x0_driver_init (host); + + /* + * Set up an interrupt handler if we aren't already sharing an IRQ + * with another board. + */ + + for (search = first_host; search && !(search->hostt == the_template && + search->irq == host->irq && search != host); search=search->next); + + if (!search) { + if (request_irq(host->irq, NCR53c7x0_intr, SA_INTERRUPT, "53c7,8xx")) { + printk("scsi%d : IRQ%d not free, detaching\n" + " You have either a configuration problem, or a\n" + " broken BIOS. You may wish to manually assign\n" + " an interrupt to the NCR board rather than using\n" + " an automatic setting.\n", + host->host_no, host->irq); + scsi_unregister (host); + return -1; + } + } else { + printk("scsi%d : using interrupt handler previously installed for scsi%d\n", + host->host_no, search->host_no); + } + + + if ((hostdata->run_tests && hostdata->run_tests(host) == -1) || + (hostdata->options & OPTION_DEBUG_TESTS_ONLY)) { + /* XXX Should disable interrupts, etc. here */ + scsi_unregister (host); + return -1; + } else { + if (host->io_port) { + host->n_io_port = 128; + request_region (host->io_port, host->n_io_port, "ncr53c7,8xx"); + } + } + + if (NCR53c7x0_read8 (SBCL_REG) & SBCL_BSY) { + printk ("scsi%d : bus wedge, doing SCSI reset\n", host->host_no); + hard_reset (host); + } + return 0; +} + +/* + * Function : static int normal_init(Scsi_Host_Template *tpnt, int board, + * int chip, u32 base, int io_port, int irq, int dma, int pcivalid, + * unsigned char pci_bus, unsigned char pci_device_fn, + * long long options); + * + * Purpose : initializes a NCR53c7,8x0 based on base addresses, + * IRQ, and DMA channel. + * + * Useful where a new NCR chip is backwards compatible with + * a supported chip, but the DEVICE ID has changed so it + * doesn't show up when the autoprobe does a pcibios_find_device. + * + * Inputs : tpnt - Template for this SCSI adapter, board - board level + * product, chip - 810, 820, or 825, bus - PCI bus, device_fn - + * device and function encoding as used by PCI BIOS calls. + * + * Returns : 0 on success, -1 on failure. + * + */ + +static int +normal_init (Scsi_Host_Template *tpnt, int board, int chip, + u32 base, int io_port, int irq, int dma, int pci_valid, + unsigned char pci_bus, unsigned char pci_device_fn, long long options) { + struct Scsi_Host *instance; + struct NCR53c7x0_hostdata *hostdata; + char chip_str[80]; + int script_len = 0, dsa_len = 0, size = 0, max_cmd_size = 0, + schedule_size = 0, ok = 0; + void *tmp; + + options |= perm_options; + + switch (chip) { + case 825: + case 820: + case 815: + case 810: + schedule_size = (tpnt->can_queue + 1) * 8 /* JUMP instruction size */; + script_len = NCR53c8xx_script_len; + dsa_len = NCR53c8xx_dsa_len; + options |= OPTION_INTFLY; + sprintf (chip_str, "NCR53c%d", chip); + break; + default: + printk("scsi-ncr53c7,8xx : unsupported SCSI chip %d\n", chip); + return -1; + } + + printk("scsi-ncr53c7,8xx : %s at memory 0x%x, io 0x%x, irq %d", + chip_str, (unsigned) base, io_port, irq); + if (dma == DMA_NONE) + printk("\n"); + else + printk(", dma %d\n", dma); + + if ((chip / 100 == 8) && !pci_valid) + printk ("scsi-ncr53c7,8xx : for better reliability and performance, please use the\n" + " PCI override instead.\n" + " Syntax : ncr53c8{10,15,20,25}=pci,,,\n" + " and are usually 0.\n"); + + if (options & OPTION_DEBUG_PROBE_ONLY) { + printk ("scsi-ncr53c7,8xx : probe only enabled, aborting initialization\n"); + return -1; + } + + max_cmd_size = sizeof(struct NCR53c7x0_cmd) + dsa_len + + /* Size of dynamic part of command structure : */ + 2 * /* Worst case : we don't know if we need DATA IN or DATA out */ + ( 2 * /* Current instructions per scatter/gather segment */ + tpnt->sg_tablesize + + 3 /* Current startup / termination required per phase */ + ) * + 8 /* Each instruction is eight bytes */; + + /* Allocate fixed part of hostdata, dynamic part to hold appropriate + SCSI SCRIPT(tm) plus a single, maximum-sized NCR53c7x0_cmd structure. + + We need a NCR53c7x0_cmd structure for scan_scsis() when we are + not loaded as a module, and when we're loaded as a module, we + can't use a non-dynamically allocated structure because modules + are vmalloc()'d, which can allow structures to cross page + boundaries and breaks our physical/virtual address assumptions + for DMA. + + So, we stick it past the end of our hostdata structure. + + ASSUMPTION : + Regardless of how many simultaneous SCSI commands we allow, + the probe code only executes a _single_ instruction at a time, + so we only need one here, and don't need to allocate NCR53c7x0_cmd + structures for each target until we are no longer in scan_scsis + and kmalloc() has become functional (memory_init() happens + after all device driver initialization). + */ + + size = sizeof(struct NCR53c7x0_hostdata) + script_len + + /* Note that alignment will be guaranteed, since we put the command + allocated at probe time after the fixed-up SCSI script, which + consists of 32 bit words, aligned on a 32 bit boundary. But + on a 64bit machine we need 8 byte alignment for hostdata->free, so + we add in another 4 bytes to take care of potential misalignment + */ + (sizeof(void *) - sizeof(u32)) + max_cmd_size + schedule_size; + + instance = scsi_register (tpnt, size); + if (!instance) + return -1; + + /* FIXME : if we ever support an ISA NCR53c7xx based board, we + need to check if the chip is running in a 16 bit mode, and if so + unregister it if it is past the 16M (0x1000000) mark */ + + hostdata = (struct NCR53c7x0_hostdata *) + instance->hostdata; + hostdata->size = size; + hostdata->script_count = script_len / sizeof(u32); + hostdata = (struct NCR53c7x0_hostdata *) instance->hostdata; + hostdata->board = board; + hostdata->chip = chip; + if ((hostdata->pci_valid = pci_valid)) { + hostdata->pci_bus = pci_bus; + hostdata->pci_device_fn = pci_device_fn; + } + + /* + * Being memory mapped is more desirable, since + * + * - Memory accesses may be faster. + * + * - The destination and source address spaces are the same for + * all instructions, meaning we don't have to twiddle dmode or + * any other registers. + * + * So, we try for memory mapped, and if we don't get it, + * we go for port mapped, and that failing we tell the user + * it can't work. + */ + + if (base) { + instance->base = (unsigned char *) (unsigned long) base; + /* Check for forced I/O mapping */ + if (!(options & OPTION_IO_MAPPED)) { + options |= OPTION_MEMORY_MAPPED; + ok = 1; + } + } else { + options &= ~OPTION_MEMORY_MAPPED; + } + + if (io_port) { + instance->io_port = io_port; + options |= OPTION_IO_MAPPED; + ok = 1; + } else { + options &= ~OPTION_IO_MAPPED; + } + + if (!ok) { + printk ("scsi%d : not initializing, no I/O or memory mapping known \n", + instance->host_no); + scsi_unregister (instance); + return -1; + } + instance->irq = irq; + instance->dma_channel = dma; + + hostdata->options = options; + hostdata->dsa_len = dsa_len; + hostdata->max_cmd_size = max_cmd_size; + hostdata->num_cmds = 1; + /* Initialize single command */ + tmp = (hostdata->script + hostdata->script_count); + hostdata->free = ROUNDUP(tmp, void *); + hostdata->free->real = tmp; + hostdata->free->size = max_cmd_size; + hostdata->free->free = NULL; + hostdata->free->next = NULL; + hostdata->extra_allocate = 0; + + /* Allocate command start code space */ + hostdata->schedule = (chip == 700 || chip == 70066) ? + NULL : (u32 *) ((char *)hostdata->free + max_cmd_size); + +/* + * For diagnostic purposes, we don't really care how fast things blaze. + * For profiling, we want to access the 800ns resolution system clock, + * using a 'C' call on the host processor. + * + * Therefore, there's no need for the NCR chip to directly manipulate + * this data, and we should put it wherever is most convienient for + * Linux. + */ + if (track_events) + hostdata->events = (struct NCR53c7x0_event *) (track_events ? + vmalloc (sizeof (struct NCR53c7x0_event) * track_events) : NULL); + else + hostdata->events = NULL; + + if (hostdata->events) { + memset ((void *) hostdata->events, 0, sizeof(struct NCR53c7x0_event) * + track_events); + hostdata->event_size = track_events; + hostdata->event_index = 0; + } else + hostdata->event_size = 0; + + return NCR53c7x0_init(instance); +} + + +/* + * Function : static int ncr_pci_init(Scsi_Host_Template *tpnt, int board, + * int chip, int bus, int device_fn, long long options) + * + * Purpose : initializes a NCR53c800 family based on the PCI + * bus, device, and function location of it. Allows + * reprogramming of latency timer and determining addresses + * and whether bus mastering, etc. are OK. + * + * Useful where a new NCR chip is backwards compatible with + * a supported chip, but the DEVICE ID has changed so it + * doesn't show up when the autoprobe does a pcibios_find_device. + * + * Inputs : tpnt - Template for this SCSI adapter, board - board level + * product, chip - 810, 820, or 825, bus - PCI bus, device_fn - + * device and function encoding as used by PCI BIOS calls. + * + * Returns : 0 on success, -1 on failure. + * + */ + +static int +ncr_pci_init (Scsi_Host_Template *tpnt, int board, int chip, + unsigned char bus, unsigned char device_fn, long long options) { + unsigned short vendor_id, device_id, command; +#ifdef LINUX_1_2 + unsigned long +#else + unsigned int +#endif + base, io_port; + unsigned char irq, revision; + int error, expected_chip; + int expected_id = -1, max_revision = -1, min_revision = -1; + int i; + + printk("scsi-ncr53c7,8xx : at PCI bus %d, device %d, function %d\n", + bus, (int) (device_fn & 0xf8) >> 3, + (int) device_fn & 7); + + if (!pcibios_present()) { + printk("scsi-ncr53c7,8xx : not initializing due to lack of PCI BIOS,\n" + " try using memory, port, irq override instead.\n"); + return -1; + } + + if ((error = pcibios_read_config_word (bus, device_fn, PCI_VENDOR_ID, + &vendor_id)) || + (error = pcibios_read_config_word (bus, device_fn, PCI_DEVICE_ID, + &device_id)) || + (error = pcibios_read_config_word (bus, device_fn, PCI_COMMAND, + &command)) || + (error = pcibios_read_config_dword (bus, device_fn, + PCI_BASE_ADDRESS_0, &io_port)) || + (error = pcibios_read_config_dword (bus, device_fn, + PCI_BASE_ADDRESS_1, &base)) || + (error = pcibios_read_config_byte (bus, device_fn, PCI_CLASS_REVISION, + &revision)) || + (error = pcibios_read_config_byte (bus, device_fn, PCI_INTERRUPT_LINE, + &irq))) { + printk ("scsi-ncr53c7,8xx : error %s not initializing due to error reading configuration space\n" + " perhaps you specified an incorrect PCI bus, device, or function.\n" + , pcibios_strerror(error)); + return -1; + } + + /* If any one ever clones the NCR chips, this will have to change */ + + if (vendor_id != PCI_VENDOR_ID_NCR) { + printk ("scsi-ncr53c7,8xx : not initializing, 0x%04x is not NCR vendor ID\n", + (int) vendor_id); + return -1; + } + + + /* + * Bit 0 is the address space indicator and must be one for I/O + * space mappings, bit 1 is reserved, discard them after checking + * that they have the correct value of 1. + */ + + if (command & PCI_COMMAND_IO) { + if ((io_port & 3) != 1) { + printk ("scsi-ncr53c7,8xx : disabling I/O mapping since base address 0 (0x%x)\n" + " bits 0..1 indicate a non-IO mapping\n", + (unsigned) io_port); + io_port = 0; + } else + io_port &= PCI_BASE_ADDRESS_IO_MASK; + } else { + io_port = 0; + } + + if (command & PCI_COMMAND_MEMORY) { + if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) { + printk("scsi-ncr53c7,8xx : disabling memory mapping since base address 1\n" + " contains a non-memory mapping\n"); + base = 0; + } else + base &= PCI_BASE_ADDRESS_MEM_MASK; + } else { + base = 0; + } + + if (!io_port && !base) { + printk ("scsi-ncr53c7,8xx : not initializing, both I/O and memory mappings disabled\n"); + return -1; + } + + if (!(command & PCI_COMMAND_MASTER)) { + printk ("scsi-ncr53c7,8xx : not initializing, BUS MASTERING was disabled\n"); + return -1; + } + + for (i = 0; i < NPCI_CHIP_IDS; ++i) { + if (device_id == pci_chip_ids[i].pci_device_id) { + max_revision = pci_chip_ids[i].max_revision; + min_revision = pci_chip_ids[i].min_revision; + expected_chip = pci_chip_ids[i].chip; + } + if (chip == pci_chip_ids[i].chip) + expected_id = pci_chip_ids[i].pci_device_id; + } + + if (chip && device_id != expected_id) + printk ("scsi-ncr53c7,8xx : warning : device id of 0x%04x doesn't\n" + " match expected 0x%04x\n", + (unsigned int) device_id, (unsigned int) expected_id ); + + if (max_revision != -1 && revision > max_revision) + printk ("scsi-ncr53c7,8xx : warning : revision of %d is greater than %d.\n", + (int) revision, max_revision); + else if (min_revision != -1 && revision < min_revision) + printk ("scsi-ncr53c7,8xx : warning : revision of %d is less than %d.\n", + (int) revision, min_revision); + + if (io_port && check_region (io_port, 128)) { + printk ("scsi-ncr53c7,8xx : IO region 0x%x to 0x%x is in use\n", + (unsigned) io_port, (unsigned) io_port + 127); + return -1; + } + + return normal_init (tpnt, board, chip, (int) base, io_port, + (int) irq, DMA_NONE, 1, bus, device_fn, options); +} + + +/* + * Function : int NCR53c7xx_detect(Scsi_Host_Template *tpnt) + * + * Purpose : detects and initializes NCR53c7,8x0 SCSI chips + * that were autoprobed, overridden on the LILO command line, + * or specified at compile time. + * + * Inputs : tpnt - template for this SCSI adapter + * + * Returns : number of host adapters detected + * + */ + +int +NCR53c7xx_detect(Scsi_Host_Template *tpnt) { + int i; + int current_override; + int count; /* Number of boards detected */ + unsigned char pci_bus, pci_device_fn; + static short pci_index=0; /* Device index to PCI BIOS calls */ + +#ifndef LINUX_1_2 + tpnt->proc_dir = &proc_scsi_ncr53c7xx; +#endif + + for (current_override = count = 0; current_override < OVERRIDE_LIMIT; + ++current_override) { + if (overrides[current_override].pci ? + !ncr_pci_init (tpnt, overrides[current_override].board, + overrides[current_override].chip, + (unsigned char) overrides[current_override].data.pci.bus, + (((overrides[current_override].data.pci.device + << 3) & 0xf8)|(overrides[current_override].data.pci.function & + 7)), overrides[current_override].options): + !normal_init (tpnt, overrides[current_override].board, + overrides[current_override].chip, + overrides[current_override].data.normal.base, + overrides[current_override].data.normal.io_port, + overrides[current_override].data.normal.irq, + overrides[current_override].data.normal.dma, + 0 /* PCI data invalid */, 0 /* PCI bus place holder */, + 0 /* PCI device_function place holder */, + overrides[current_override].options)) { + ++count; + } + } + + if (pcibios_present()) { + for (i = 0; i < NPCI_CHIP_IDS; ++i) + for (pci_index = 0; + !pcibios_find_device (PCI_VENDOR_ID_NCR, + pci_chip_ids[i].pci_device_id, pci_index, &pci_bus, + &pci_device_fn); + ++pci_index) + if (!ncr_pci_init (tpnt, BOARD_GENERIC, pci_chip_ids[i].chip, + pci_bus, pci_device_fn, /* no options */ 0)) + ++count; + } + return count; +} + +/* NCR53c810 and NCR53c820 script handling code */ + +#include "53c8xx_d.h" +#ifdef A_int_debug_sync +#define DEBUG_SYNC_INTR A_int_debug_sync +#endif +static int NCR53c8xx_script_len = sizeof (SCRIPT); +static int NCR53c8xx_dsa_len = A_dsa_end + Ent_dsa_zero - Ent_dsa_code_template; + +/* + * Function : static void NCR53c8x0_init_fixup (struct Scsi_Host *host) + * + * Purpose : copy and fixup the SCSI SCRIPTS(tm) code for this device. + * + * Inputs : host - pointer to this host adapter's structure + * + */ + +static void +NCR53c8x0_init_fixup (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned char tmp; + int i, ncr_to_memory, memory_to_ncr; + u32 base; + NCR53c7x0_local_setup(host); + + + /* XXX - NOTE : this code MUST be made endian aware */ + /* Copy code into buffer that was allocated at detection time. */ + memcpy ((void *) hostdata->script, (void *) SCRIPT, + sizeof(SCRIPT)); + /* Fixup labels */ + for (i = 0; i < PATCHES; ++i) + hostdata->script[LABELPATCHES[i]] += + virt_to_bus(hostdata->script); + /* Fixup addresses of constants that used to be EXTERNAL */ + + patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_abort, + virt_to_bus(&(hostdata->NCR53c7xx_msg_abort))); + patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_reject, + virt_to_bus(&(hostdata->NCR53c7xx_msg_reject))); + patch_abs_32 (hostdata->script, 0, NCR53c7xx_zero, + virt_to_bus(&(hostdata->NCR53c7xx_zero))); + patch_abs_32 (hostdata->script, 0, NCR53c7xx_sink, + virt_to_bus(&(hostdata->NCR53c7xx_sink))); + patch_abs_32 (hostdata->script, 0, NOP_insn, + virt_to_bus(&(hostdata->NOP_insn))); + patch_abs_32 (hostdata->script, 0, schedule, + virt_to_bus((void *) hostdata->schedule)); + + /* Fixup references to external variables: */ + for (i = 0; i < EXTERNAL_PATCHES_LEN; ++i) + hostdata->script[EXTERNAL_PATCHES[i].offset] += + virt_to_bus(EXTERNAL_PATCHES[i].address); + + /* + * Fixup absolutes set at boot-time. + * + * All non-code absolute variables suffixed with "dsa_" and "int_" + * are constants, and need no fixup provided the assembler has done + * it for us (I don't know what the "real" NCR assembler does in + * this case, my assembler does the right magic). + */ + + patch_abs_rwri_data (hostdata->script, 0, dsa_save_data_pointer, + Ent_dsa_code_save_data_pointer - Ent_dsa_zero); + patch_abs_rwri_data (hostdata->script, 0, dsa_restore_pointers, + Ent_dsa_code_restore_pointers - Ent_dsa_zero); + patch_abs_rwri_data (hostdata->script, 0, dsa_check_reselect, + Ent_dsa_code_check_reselect - Ent_dsa_zero); + + /* + * Just for the hell of it, preserve the settings of + * Burst Length and Enable Read Line bits from the DMODE + * register. Make sure SCRIPTS start automagically. + */ + + tmp = NCR53c7x0_read8(DMODE_REG_10); + tmp &= (DMODE_800_ERL | DMODE_BL_MASK); + + if (!(hostdata->options & OPTION_MEMORY_MAPPED)) { + base = (u32) host->io_port; + memory_to_ncr = tmp|DMODE_800_DIOM; + ncr_to_memory = tmp|DMODE_800_SIOM; + } else { + base = virt_to_bus(host->base); + memory_to_ncr = ncr_to_memory = tmp; + } + + patch_abs_32 (hostdata->script, 0, addr_scratch, base + SCRATCHA_REG_800); + patch_abs_32 (hostdata->script, 0, addr_temp, base + TEMP_REG); + + /* + * I needed some variables in the script to be accessible to + * both the NCR chip and the host processor. For these variables, + * I made the arbitrary decision to store them directly in the + * hostdata structure rather than in the RELATIVE area of the + * SCRIPTS. + */ + + + patch_abs_rwri_data (hostdata->script, 0, dmode_memory_to_memory, tmp); + patch_abs_rwri_data (hostdata->script, 0, dmode_memory_to_ncr, memory_to_ncr); + patch_abs_rwri_data (hostdata->script, 0, dmode_ncr_to_memory, ncr_to_memory); + + patch_abs_32 (hostdata->script, 0, msg_buf, + virt_to_bus((void *)&(hostdata->msg_buf))); + patch_abs_32 (hostdata->script, 0, reconnect_dsa_head, + virt_to_bus((void *)&(hostdata->reconnect_dsa_head))); + patch_abs_32 (hostdata->script, 0, addr_reconnect_dsa_head, + virt_to_bus((void *)&(hostdata->addr_reconnect_dsa_head))); + patch_abs_32 (hostdata->script, 0, reselected_identify, + virt_to_bus((void *)&(hostdata->reselected_identify))); +/* reselected_tag is currently unused */ +#if 0 + patch_abs_32 (hostdata->script, 0, reselected_tag, + virt_to_bus((void *)&(hostdata->reselected_tag))); +#endif + + patch_abs_32 (hostdata->script, 0, test_dest, + virt_to_bus((void*)&hostdata->test_dest)); + patch_abs_32 (hostdata->script, 0, test_src, + virt_to_bus(&hostdata->test_source)); + + patch_abs_rwri_data (hostdata->script, 0, dsa_check_reselect, + (unsigned char)(Ent_dsa_code_check_reselect - Ent_dsa_zero)); + +/* These are for event logging; the ncr_event enum contains the + actual interrupt numbers. */ +#ifdef A_int_EVENT_SELECT + patch_abs_32 (hostdata->script, 0, int_EVENT_SELECT, (u32) EVENT_SELECT); +#endif +#ifdef A_int_EVENT_DISCONNECT + patch_abs_32 (hostdata->script, 0, int_EVENT_DISCONNECT, (u32) EVENT_DISCONNECT); +#endif +#ifdef A_int_EVENT_RESELECT + patch_abs_32 (hostdata->script, 0, int_EVENT_RESELECT, (u32) EVENT_RESELECT); +#endif +#ifdef A_int_EVENT_COMPLETE + patch_abs_32 (hostdata->script, 0, int_EVENT_COMPLETE, (u32) EVENT_COMPLETE); +#endif +#ifdef A_int_EVENT_IDLE + patch_abs_32 (hostdata->script, 0, int_EVENT_IDLE, (u32) EVENT_IDLE); +#endif +#ifdef A_int_EVENT_SELECT_FAILED + patch_abs_32 (hostdata->script, 0, int_EVENT_SELECT_FAILED, + (u32) EVENT_SELECT_FAILED); +#endif +#ifdef A_int_EVENT_BEFORE_SELECT + patch_abs_32 (hostdata->script, 0, int_EVENT_BEFORE_SELECT, + (u32) EVENT_BEFORE_SELECT); +#endif +#ifdef A_int_EVENT_RESELECT_FAILED + patch_abs_32 (hostdata->script, 0, int_EVENT_RESELECT_FAILED, + (u32) EVENT_RESELECT_FAILED); +#endif + + /* + * Make sure the NCR and Linux code agree on the location of + * certain fields. + */ + + hostdata->E_accept_message = Ent_accept_message; + hostdata->E_command_complete = Ent_command_complete; + hostdata->E_cmdout_cmdout = Ent_cmdout_cmdout; + hostdata->E_data_transfer = Ent_data_transfer; + hostdata->E_debug_break = Ent_debug_break; + hostdata->E_dsa_code_template = Ent_dsa_code_template; + hostdata->E_dsa_code_template_end = Ent_dsa_code_template_end; + hostdata->E_end_data_transfer = Ent_end_data_transfer; + hostdata->E_initiator_abort = Ent_initiator_abort; + hostdata->E_msg_in = Ent_msg_in; + hostdata->E_other_transfer = Ent_other_transfer; + hostdata->E_other_in = Ent_other_in; + hostdata->E_other_out = Ent_other_out; + hostdata->E_reject_message = Ent_reject_message; + hostdata->E_respond_message = Ent_respond_message; + hostdata->E_select = Ent_select; + hostdata->E_select_msgout = Ent_select_msgout; + hostdata->E_target_abort = Ent_target_abort; +#ifdef Ent_test_0 + hostdata->E_test_0 = Ent_test_0; +#endif + hostdata->E_test_1 = Ent_test_1; + hostdata->E_test_2 = Ent_test_2; +#ifdef Ent_test_3 + hostdata->E_test_3 = Ent_test_3; +#endif + hostdata->E_wait_reselect = Ent_wait_reselect; + hostdata->E_dsa_code_begin = Ent_dsa_code_begin; + + hostdata->dsa_cmdout = A_dsa_cmdout; + hostdata->dsa_cmnd = A_dsa_cmnd; + hostdata->dsa_datain = A_dsa_datain; + hostdata->dsa_dataout = A_dsa_dataout; + hostdata->dsa_end = A_dsa_end; + hostdata->dsa_msgin = A_dsa_msgin; + hostdata->dsa_msgout = A_dsa_msgout; + hostdata->dsa_msgout_other = A_dsa_msgout_other; + hostdata->dsa_next = A_dsa_next; + hostdata->dsa_select = A_dsa_select; + hostdata->dsa_start = Ent_dsa_code_template - Ent_dsa_zero; + hostdata->dsa_status = A_dsa_status; + hostdata->dsa_jump_dest = Ent_dsa_code_fix_jump - Ent_dsa_zero + + 8 /* destination operand */; + + /* sanity check */ + if (A_dsa_fields_start != Ent_dsa_code_template_end - + Ent_dsa_zero) + printk("scsi%d : NCR dsa_fields start is %d not %d\n", + host->host_no, A_dsa_fields_start, Ent_dsa_code_template_end - + Ent_dsa_zero); + + printk("scsi%d : NCR code relocated to 0x%lx (virt 0x%p)\n", host->host_no, + virt_to_bus(hostdata->script), hostdata->script); +} + +/* + * Function : static int NCR53c8xx_run_tests (struct Scsi_Host *host) + * + * Purpose : run various verification tests on the NCR chip, + * including interrupt generation, and proper bus mastering + * operation. + * + * Inputs : host - a properly initialized Scsi_Host structure + * + * Preconditions : the NCR chip must be in a halted state. + * + * Returns : 0 if all tests were successful, -1 on error. + * + */ + +static int +NCR53c8xx_run_tests (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned long timeout; + u32 start; + int failed, i; + unsigned long flags; + NCR53c7x0_local_setup(host); + + /* The NCR chip _must_ be idle to run the test scripts */ + + save_flags(flags); + cli(); + if (!hostdata->idle) { + printk ("scsi%d : chip not idle, aborting tests\n", host->host_no); + restore_flags(flags); + return -1; + } + + /* + * Check for functional interrupts, this could work as an + * autoprobe routine. + */ + + if ((hostdata->options & OPTION_DEBUG_TEST1) && + hostdata->state != STATE_DISABLED) { + hostdata->idle = 0; + hostdata->test_running = 1; + hostdata->test_completed = -1; + hostdata->test_dest = 0; + hostdata->test_source = 0xdeadbeef; + start = virt_to_bus (hostdata->script) + hostdata->E_test_1; + hostdata->state = STATE_RUNNING; + printk ("scsi%d : test 1", host->host_no); + NCR53c7x0_write32 (DSP_REG, start); + printk (" started\n"); + sti(); + + /* + * This is currently a .5 second timeout, since (in theory) no slow + * board will take that long. In practice, we've seen one + * pentium which ocassionally fails with this, but works with + * 10 times as much? + */ + + timeout = jiffies + 5 * HZ / 10; + while ((hostdata->test_completed == -1) && jiffies < timeout) + barrier(); + + failed = 1; + if (hostdata->test_completed == -1) + printk ("scsi%d : driver test 1 timed out%s\n",host->host_no , + (hostdata->test_dest == 0xdeadbeef) ? + " due to lost interrupt.\n" + " Please verify that the correct IRQ is being used for your board,\n" + " and that the motherboard IRQ jumpering matches the PCI setup on\n" + " PCI systems.\n" + " If you are using a NCR53c810 board in a PCI system, you should\n" + " also verify that the board is jumpered to use PCI INTA, since\n" + " most PCI motherboards lack support for INTB, INTC, and INTD.\n" + : ""); + else if (hostdata->test_completed != 1) + printk ("scsi%d : test 1 bad interrupt value (%d)\n", + host->host_no, hostdata->test_completed); + else + failed = (hostdata->test_dest != 0xdeadbeef); + + if (hostdata->test_dest != 0xdeadbeef) { + printk ("scsi%d : driver test 1 read 0x%x instead of 0xdeadbeef indicating a\n" + " probable cache invalidation problem. Please configure caching\n" + " as write-through or disabled\n", + host->host_no, hostdata->test_dest); + } + + if (failed) { + printk ("scsi%d : DSP = 0x%p (script at 0x%p, start at 0x%x)\n", + host->host_no, bus_to_virt(NCR53c7x0_read32(DSP_REG)), + hostdata->script, start); + printk ("scsi%d : DSPS = 0x%x\n", host->host_no, + NCR53c7x0_read32(DSPS_REG)); + restore_flags(flags); + return -1; + } + hostdata->test_running = 0; + } + + if ((hostdata->options & OPTION_DEBUG_TEST2) && + hostdata->state != STATE_DISABLED) { + u32 dsa[48]; + unsigned char identify = IDENTIFY(0, 0); + unsigned char cmd[6]; + unsigned char data[36]; + unsigned char status = 0xff; + unsigned char msg = 0xff; + + cmd[0] = INQUIRY; + cmd[1] = cmd[2] = cmd[3] = cmd[5] = 0; + cmd[4] = sizeof(data); + + dsa[2] = 1; + dsa[3] = virt_to_bus(&identify); + dsa[4] = 6; + dsa[5] = virt_to_bus(&cmd); + dsa[6] = sizeof(data); + dsa[7] = virt_to_bus(&data); + dsa[8] = 1; + dsa[9] = virt_to_bus(&status); + dsa[10] = 1; + dsa[11] = virt_to_bus(&msg); + + for (i = 0; i < 3; ++i) { + cli(); + if (!hostdata->idle) { + printk ("scsi%d : chip not idle, aborting tests\n", host->host_no); + restore_flags(flags); + return -1; + } + + /* SCNTL3 SDID */ + dsa[0] = (0x33 << 24) | (i << 16) ; + hostdata->idle = 0; + hostdata->test_running = 2; + hostdata->test_completed = -1; + start = virt_to_bus(hostdata->script) + hostdata->E_test_2; + hostdata->state = STATE_RUNNING; + NCR53c7x0_write32 (DSA_REG, virt_to_bus(dsa)); + NCR53c7x0_write32 (DSP_REG, start); + sti(); + + timeout = jiffies + 5 * HZ; /* arbitrary */ + while ((hostdata->test_completed == -1) && jiffies < timeout) + barrier(); + NCR53c7x0_write32 (DSA_REG, 0); + + if (hostdata->test_completed == 2) { + data[35] = 0; + printk ("scsi%d : test 2 INQUIRY to target %d, lun 0 : %s\n", + host->host_no, i, data + 8); + printk ("scsi%d : status ", host->host_no); + print_status (status); + printk ("\nscsi%d : message ", host->host_no); + print_msg (&msg); + printk ("\n"); + } else if (hostdata->test_completed == 3) { + printk("scsi%d : test 2 no connection with target %d\n", + host->host_no, i); + if (!hostdata->idle) { + printk("scsi%d : not idle\n", host->host_no); + restore_flags(flags); + return -1; + } + } else if (hostdata->test_completed == -1) { + printk ("scsi%d : test 2 timed out\n", host->host_no); + restore_flags(flags); + return -1; + } + hostdata->test_running = 0; + } + } + + restore_flags(flags); + return 0; +} + +/* + * Function : static void NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd) + * + * Purpose : copy the NCR53c8xx dsa structure into cmd's dsa buffer, + * performing all necessary relocation. + * + * Inputs : cmd, a NCR53c7x0_cmd structure with a dsa area large + * enough to hold the NCR53c8xx dsa. + */ + +static void +NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd) { + Scsi_Cmnd *c = cmd->cmd; + struct Scsi_Host *host = c->host; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + int i; + + memcpy (cmd->dsa, hostdata->script + (hostdata->E_dsa_code_template / 4), + hostdata->E_dsa_code_template_end - hostdata->E_dsa_code_template); + + /* + * Note : within the NCR 'C' code, dsa points to the _start_ + * of the DSA structure, and _not_ the offset of dsa_zero within + * that structure used to facilitate shorter signed offsets + * for the 8 bit ALU. + * + * The implications of this are that + * + * - 32 bit A_dsa_* absolute values require an additional + * dsa_zero added to their value to be correct, since they are + * relative to dsa_zero which is in essentially a separate + * space from the code symbols. + * + * - All other symbols require no special treatment. + */ + + patch_abs_tci_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_lun, c->lun); + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_addr_next, virt_to_bus(&cmd->dsa_next_addr)); + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_next, virt_to_bus(cmd->dsa) + Ent_dsa_zero - + Ent_dsa_code_template + A_dsa_next); + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_sync, virt_to_bus((void *)hostdata->sync[c->target].script)); + patch_abs_tci_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_target, c->target); + /* XXX - new pointer stuff */ + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_addr_saved_pointer, virt_to_bus(&cmd->saved_data_pointer)); + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_addr_saved_residual, virt_to_bus(&cmd->saved_residual)); + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_addr_residual, virt_to_bus(&cmd->residual)); + + /* XXX - new start stuff */ + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_addr_dsa_value, virt_to_bus(&cmd->dsa_addr)); + +} + +/* + * Function : run_process_issue_queue (void) + * + * Purpose : insure that the coroutine is running and will process our + * request. process_issue_queue_running is checked/set here (in an + * inline function) rather than in process_issue_queue itself to reduce + * the chances of stack overflow. + * + */ + +static volatile int process_issue_queue_running = 0; + +static __inline__ void +run_process_issue_queue(void) { + unsigned long flags; + save_flags (flags); + cli(); + if (!process_issue_queue_running) { + process_issue_queue_running = 1; + process_issue_queue(flags); + /* + * process_issue_queue_running is cleared in process_issue_queue + * once it can't do more work, and process_issue_queue exits with + * interrupts disabled. + */ + } + restore_flags (flags); +} + +/* + * Function : static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int + * result) + * + * Purpose : mark SCSI command as finished, OR'ing the host portion + * of the result word into the result field of the corresponding + * Scsi_Cmnd structure, and removing it from the internal queues. + * + * Inputs : cmd - command, result - entire result field + * + * Preconditions : the NCR chip should be in a halted state when + * abnormal_finished is run, since it modifies structures which + * the NCR expects to have exclusive access to. + */ + +static void +abnormal_finished (struct NCR53c7x0_cmd *cmd, int result) { + Scsi_Cmnd *c = cmd->cmd; + struct Scsi_Host *host = c->host; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned long flags; + int left, found; + volatile struct NCR53c7x0_cmd * linux_search; + volatile struct NCR53c7x0_cmd * volatile *linux_prev; + volatile u32 *ncr_prev, *current, ncr_search; + +#if 0 + printk ("scsi%d: abnormal finished\n", host->host_no); +#endif + + save_flags(flags); + cli(); + found = 0; + /* + * Traverse the NCR issue array until we find a match or run out + * of instructions. Instructions in the NCR issue array are + * either JUMP or NOP instructions, which are 2 words in length. + */ + + + for (found = 0, left = host->can_queue, current = hostdata->schedule; + left > 0; --left, current += 2) + { + if (issue_to_cmd (host, hostdata, (u32 *) current) == cmd) + { + current[0] = hostdata->NOP_insn; + current[1] = 0xdeadbeef; + ++found; + break; + } + } + + /* + * Traverse the NCR reconnect list of DSA structures until we find + * a pointer to this dsa or have found too many command structures. + * We let prev point at the next field of the previous element or + * head of the list, so we don't do anything different for removing + * the head element. + */ + + for (left = host->can_queue, + ncr_search = hostdata->reconnect_dsa_head, + ncr_prev = &hostdata->reconnect_dsa_head; + left >= 0 && ncr_search && + ((char*)bus_to_virt(ncr_search) + hostdata->dsa_start) + != (char *) cmd->dsa; + ncr_prev = (u32*) ((char*)bus_to_virt(ncr_search) + + hostdata->dsa_next), ncr_search = *ncr_prev, --left); + + if (left < 0) + printk("scsi%d: loop detected in ncr reonncect list\n", + host->host_no); + else if (ncr_search) + if (found) + printk("scsi%d: scsi %ld in ncr issue array and reconnect lists\n", + host->host_no, c->pid); + else { + volatile u32 * next = (u32 *) + ((char *)bus_to_virt(ncr_search) + hostdata->dsa_next); + *ncr_prev = *next; +/* If we're at the tail end of the issue queue, update that pointer too. */ + found = 1; + } + + /* + * Traverse the host running list until we find this command or discover + * we have too many elements, pointing linux_prev at the next field of the + * linux_previous element or head of the list, search at this element. + */ + + for (left = host->can_queue, linux_search = hostdata->running_list, + linux_prev = &hostdata->running_list; + left >= 0 && linux_search && linux_search != cmd; + linux_prev = &(linux_search->next), + linux_search = linux_search->next, --left); + + if (left < 0) + printk ("scsi%d: loop detected in host running list for scsi pid %ld\n", + host->host_no, c->pid); + else if (linux_search) { + *linux_prev = linux_search->next; + --hostdata->busy[c->target][c->lun]; + } + + /* Return the NCR command structure to the free list */ + cmd->next = hostdata->free; + hostdata->free = cmd; + c->host_scribble = NULL; + + /* And return */ + c->result = result; + c->scsi_done(c); + + restore_flags(flags); + run_process_issue_queue(); +} + +/* + * Function : static void intr_break (struct Scsi_Host *host, + * struct NCR53c7x0_cmd *cmd) + * + * Purpose : Handler for breakpoint interrupts from a SCSI script + * + * Inputs : host - pointer to this host adapter's structure, + * cmd - pointer to the command (if any) dsa was pointing + * to. + * + */ + +static void +intr_break (struct Scsi_Host *host, struct + NCR53c7x0_cmd *cmd) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_break *bp; +#if 0 + Scsi_Cmnd *c = cmd ? cmd->cmd : NULL; +#endif + u32 *dsp; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned long flags; + NCR53c7x0_local_setup(host); + + /* + * Find the break point corresponding to this address, and + * dump the appropriate debugging information to standard + * output. + */ + save_flags(flags); + cli(); + dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG)); + for (bp = hostdata->breakpoints; bp && bp->address != dsp; + bp = bp->next); + if (!bp) + panic("scsi%d : break point interrupt from %p with no breakpoint!", + host->host_no, dsp); + + /* + * Configure the NCR chip for manual start mode, so that we can + * point the DSP register at the instruction that follows the + * INT int_debug_break instruction. + */ + + NCR53c7x0_write8 (hostdata->dmode, + NCR53c7x0_read8(hostdata->dmode)|DMODE_MAN); + + /* + * And update the DSP register, using the size of the old + * instruction in bytes. + */ + + restore_flags(flags); +} +/* + * Function : static void print_synchronous (const char *prefix, + * const unsigned char *msg) + * + * Purpose : print a pretty, user and machine parsable representation + * of a SDTR message, including the "real" parameters, data + * clock so we can tell transfer rate at a glance. + * + * Inputs ; prefix - text to prepend, msg - SDTR message (5 bytes) + */ + +static void +print_synchronous (const char *prefix, const unsigned char *msg) { + if (msg[4]) { + int Hz = 1000000000 / (msg[3] * 4); + int integer = Hz / 1000000; + int fraction = (Hz - (integer * 1000000)) / 10000; + printk ("%speriod %dns offset %d %d.%02dMHz %s SCSI%s\n", + prefix, (int) msg[3] * 4, (int) msg[4], integer, fraction, + (((msg[3] * 4) < 200) ? "FAST" : "synchronous"), + (((msg[3] * 4) < 200) ? "-II" : "")); + } else + printk ("%sasynchronous SCSI\n", prefix); +} + +/* + * Function : static void set_synchronous (struct Scsi_Host *host, + * int target, int sxfer, int scntl3, int now_connected) + * + * Purpose : reprogram transfers between the selected SCSI initiator and + * target with the given register values; in the indirect + * select operand, reselection script, and chip registers. + * + * Inputs : host - NCR53c7,8xx SCSI host, target - number SCSI target id, + * sxfer and scntl3 - NCR registers. now_connected - if non-zero, + * we should reprogram the registers now too. + */ + +static void +set_synchronous (struct Scsi_Host *host, int target, int sxfer, int scntl3, + int now_connected) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + u32 *script; + NCR53c7x0_local_setup(host); + + /* These are eight bit registers */ + sxfer &= 0xff; + scntl3 &= 0xff; + + hostdata->sync[target].sxfer_sanity = sxfer; + hostdata->sync[target].scntl3_sanity = scntl3; + +/* + * HARD CODED : synchronous script is EIGHT words long. This + * must agree with 53c7.8xx.h + */ + + if ((hostdata->chip != 700) && (hostdata->chip != 70066)) { + hostdata->sync[target].select_indirect = (scntl3 << 24) | + (target << 16) | (sxfer << 8); + + script = (u32 *) hostdata->sync[target].script; + + /* XXX - add NCR53c7x0 code to reprogram SCF bits if we want to */ + if ((hostdata->chip / 100) == 8) { + script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY | + DCMD_RWRI_OP_MOVE) << 24) | + (SCNTL3_REG_800 << 16) | (scntl3 << 8); + script[1] = 0; + script += 2; + } + + script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY | + DCMD_RWRI_OP_MOVE) << 24) | + (SXFER_REG << 16) | (sxfer << 8); + script[1] = 0; + script += 2; + +#ifdef DEBUG_SYNC_INTR + if (hostdata->options & OPTION_DEBUG_DISCONNECT) { + script[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_INT) << 24) | DBC_TCI_TRUE; + script[1] = DEBUG_SYNC_INTR; + script += 2; + } +#endif + + script[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_RETURN) << 24) | DBC_TCI_TRUE; + script[1] = 0; + script += 2; + } + + if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS) + printk ("scsi%d : target %d sync parameters are sxfer=0x%x, scntl3=0x%x\n", + host->host_no, target, sxfer, scntl3); + + if (now_connected) { + if ((hostdata->chip / 100) == 8) + NCR53c7x0_write8(SCNTL3_REG_800, scntl3); + NCR53c7x0_write8(SXFER_REG, sxfer); + } +} + + +/* + * Function : static int asynchronous (struct Scsi_Host *host, int target) + * + * Purpose : reprogram between the selected SCSI Host adapter and target + * (assumed to be currently connected) for asynchronous transfers. + * + * Inputs : host - SCSI host structure, target - numeric target ID. + * + * Preconditions : the NCR chip should be in one of the halted states + */ + +static void +asynchronous (struct Scsi_Host *host, int target) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + NCR53c7x0_local_setup(host); + set_synchronous (host, target, /* no offset */ 0, hostdata->saved_scntl3, + 1); + printk ("scsi%d : setting target %d to asynchronous SCSI\n", + host->host_no, target); +} + +/* + * XXX - do we want to go out of our way (ie, add extra code to selection + * in the NCR53c710/NCR53c720 script) to reprogram the synchronous + * conversion bits, or can we be content in just setting the + * sxfer bits? + */ + +/* Table for NCR53c8xx synchronous values */ +static const struct { + int div; /* Total clock divisor * 10 */ + unsigned char scf; /* */ + unsigned char tp; /* 4 + tp = xferp divisor */ +} syncs[] = { +/* div scf tp div scf tp div scf tp */ + { 40, 1, 0}, { 50, 1, 1}, { 60, 1, 2}, + { 70, 1, 3}, { 75, 2, 1}, { 80, 1, 4}, + { 90, 1, 5}, { 100, 1, 6}, { 105, 2, 3}, + { 110, 1, 7}, { 120, 2, 4}, { 135, 2, 5}, + { 140, 3, 3}, { 150, 2, 6}, { 160, 3, 4}, + { 165, 2, 7}, { 180, 3, 5}, { 200, 3, 6}, + { 210, 4, 3}, { 220, 3, 7}, { 240, 4, 4}, + { 270, 4, 5}, { 300, 4, 6}, { 330, 4, 7} +}; + +/* + * Function : static void synchronous (struct Scsi_Host *host, int target, + * char *msg) + * + * Purpose : reprogram transfers between the selected SCSI initiator and + * target for synchronous SCSI transfers such that the synchronous + * offset is less than that requested and period at least as long + * as that requested. Also modify *msg such that it contains + * an appropriate response. + * + * Inputs : host - NCR53c7,8xx SCSI host, target - number SCSI target id, + * msg - synchronous transfer request. + */ + + +static void +synchronous (struct Scsi_Host *host, int target, char *msg) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + int desire, divisor, i, limit; + unsigned char scntl3, sxfer; +/* The diagnostic message fits on one line, even with max. width integers */ + char buf[80]; + +/* Desired transfer clock in Hz */ + desire = 1000000000L / (msg[3] * 4); +/* Scale the available SCSI clock by 10 so we get tenths */ + divisor = (hostdata->scsi_clock * 10) / desire; + +/* NCR chips can handle at most an offset of 8 */ + if (msg[4] > 8) + msg[4] = 8; + + if (hostdata->options & OPTION_DEBUG_SDTR) + printk("scsi%d : optimal synchronous divisor of %d.%01d\n", + host->host_no, divisor / 10, divisor % 10); + + limit = (sizeof(syncs) / sizeof(syncs[0]) -1); + for (i = 0; (i < limit) && (divisor > syncs[i].div); ++i); + + if (hostdata->options & OPTION_DEBUG_SDTR) + printk("scsi%d : selected synchronous divisor of %d.%01d\n", + host->host_no, syncs[i].div / 10, syncs[i].div % 10); + + msg[3] = ((1000000000L / hostdata->scsi_clock) * syncs[i].div / 10 / 4); + + if (hostdata->options & OPTION_DEBUG_SDTR) + printk("scsi%d : selected synchronous period of %dns\n", host->host_no, + msg[3] * 4); + + scntl3 = (hostdata->chip / 100 == 8) ? ((hostdata->saved_scntl3 & + ~SCNTL3_800_SCF_MASK) | (syncs[i].scf << SCNTL3_800_SCF_SHIFT)) : 0; + sxfer = (msg[4] << SXFER_MO_SHIFT) | ((syncs[i].tp) << SXFER_TP_SHIFT); + if (hostdata->options & OPTION_DEBUG_SDTR) + printk ("scsi%d : sxfer=0x%x scntl3=0x%x\n", + host->host_no, (int) sxfer, (int) scntl3); + set_synchronous (host, target, sxfer, scntl3, 1); + sprintf (buf, "scsi%d : setting target %d to ", host->host_no, target); + print_synchronous (buf, msg); +} + +/* + * Function : static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, + * struct NCR53c7x0_cmd *cmd) + * + * Purpose : Handler for INT generated instructions for the + * NCR53c810/820 SCSI SCRIPT + * + * Inputs : host - pointer to this host adapter's structure, + * cmd - pointer to the command (if any) dsa was pointing + * to. + * + */ + +static int +NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct + NCR53c7x0_cmd *cmd) { + NCR53c7x0_local_declare(); + int print; + Scsi_Cmnd *c = cmd ? cmd->cmd : NULL; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + u32 dsps,*dsp; /* Argument of the INT instruction */ + NCR53c7x0_local_setup(host); + dsps = NCR53c7x0_read32(DSPS_REG); + dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG)); + + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : DSPS = 0x%x\n", host->host_no, dsps); + + switch (dsps) { + case A_int_msg_1: + print = 1; + switch (hostdata->msg_buf[0]) { + /* + * Unless we've initiated synchronous negotiation, I don't + * think that this should happen. + */ + case MESSAGE_REJECT: + hostdata->dsp = hostdata->script + hostdata->E_accept_message / + sizeof(u32); + hostdata->dsp_changed = 1; + if (cmd && (cmd->flags & CMD_FLAG_SDTR)) { + printk ("scsi%d : target %d rejected SDTR\n", host->host_no, + c->target); + cmd->flags &= ~CMD_FLAG_SDTR; + asynchronous (host, c->target); + print = 0; + } + break; + case INITIATE_RECOVERY: + printk ("scsi%d : extended contingent allegiance not supported yet, rejecting\n", + host->host_no); + /* Fall through to default */ + hostdata->dsp = hostdata->script + hostdata->E_reject_message / + sizeof(u32); + hostdata->dsp_changed = 1; + break; + default: + printk ("scsi%d : unsupported message, resjecting\n", + host->host_no); + hostdata->dsp = hostdata->script + hostdata->E_reject_message / + sizeof(u32); + hostdata->dsp_changed = 1; + } + if (print) { + printk ("scsi%d : received message", host->host_no); + if (c) + printk (" from target %d lun %d ", c->target, c->lun); + print_msg ((unsigned char *) hostdata->msg_buf); + printk("\n"); + } + + return SPECIFIC_INT_NOTHING; + + + case A_int_msg_sdtr: +/* + * At this point, hostdata->msg_buf contains + * 0 EXTENDED MESSAGE + * 1 length + * 2 SDTR + * 3 period * 4ns + * 4 offset + */ + + if (cmd) { + char buf[80]; + sprintf (buf, "scsi%d : target %d %s ", host->host_no, c->target, + (cmd->flags & CMD_FLAG_SDTR) ? "accepting" : "requesting"); + print_synchronous (buf, (unsigned char *) hostdata->msg_buf); + + /* + * Initiator initiated, won't happen unless synchronous + * transfers are enabled. If we get a SDTR message in + * response to our SDTR, we should program our parameters + * such that + * offset <= requested offset + * period >= requested period + */ + if (cmd->flags & CMD_FLAG_SDTR) { + cmd->flags &= ~CMD_FLAG_SDTR; + if (hostdata->msg_buf[4]) + synchronous (host, c->target, (unsigned char *) + hostdata->msg_buf); + else + asynchronous (host, c->target); + hostdata->dsp = hostdata->script + hostdata->E_accept_message / + sizeof(u32); + hostdata->dsp_changed = 1; + return SPECIFIC_INT_NOTHING; + } else { + if (hostdata->options & OPTION_SYNCHRONOUS) { + cmd->flags |= CMD_FLAG_DID_SDTR; + synchronous (host, c->target, (unsigned char *) + hostdata->msg_buf); + } else { + hostdata->msg_buf[4] = 0; /* 0 offset = async */ + asynchronous (host, c->target); + } + patch_dsa_32 (cmd->dsa, dsa_msgout_other, 0, 5); + patch_dsa_32 (cmd->dsa, dsa_msgout_other, 1, (u32) + virt_to_bus ((void *)&hostdata->msg_buf)); + hostdata->dsp = hostdata->script + + hostdata->E_respond_message / sizeof(u32); + hostdata->dsp_changed = 1; + } + return SPECIFIC_INT_NOTHING; + } + /* Fall through to abort if we couldn't find a cmd, and + therefore a dsa structure to twiddle */ + case A_int_msg_wdtr: + hostdata->dsp = hostdata->script + hostdata->E_reject_message / + sizeof(u32); + hostdata->dsp_changed = 1; + return SPECIFIC_INT_NOTHING; + case A_int_err_unexpected_phase: + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : unexpected phase\n", host->host_no); + return SPECIFIC_INT_ABORT; + case A_int_err_selected: + printk ("scsi%d : selected by target %d\n", host->host_no, + (int) NCR53c7x0_read8(SDID_REG_800) &7); + hostdata->dsp = hostdata->script + hostdata->E_target_abort / + sizeof(u32); + hostdata->dsp_changed = 1; + return SPECIFIC_INT_NOTHING; + case A_int_err_unexpected_reselect: + printk ("scsi%d : unexpected reselect by target %d lun %d\n", + host->host_no, (int) NCR53c7x0_read8(SDID_REG_800) & 7, + hostdata->reselected_identify & 7); + hostdata->dsp = hostdata->script + hostdata->E_initiator_abort / + sizeof(u32); + hostdata->dsp_changed = 1; + return SPECIFIC_INT_NOTHING; +/* + * Since contingent allegiance conditions are cleared by the next + * command issued to a target, we must issue a REQUEST SENSE + * command after receiving a CHECK CONDITION status, before + * another command is issued. + * + * Since this NCR53c7x0_cmd will be freed after use, we don't + * care if we step on the various fields, so modify a few things. + */ + case A_int_err_check_condition: +#if 0 + if (hostdata->options & OPTION_DEBUG_INTR) +#endif + printk ("scsi%d : CHECK CONDITION\n", host->host_no); + if (!c) { + printk("scsi%d : CHECK CONDITION with no SCSI command\n", + host->host_no); + return SPECIFIC_INT_PANIC; + } + + /* + * FIXME : this uses the normal one-byte selection message. + * We may want to renegotiate for synchronous & WIDE transfers + * since these could be the crux of our problem. + * + hostdata->NOP_insn* FIXME : once SCSI-II tagged queuing is implemented, we'll + * have to set this up so that the rest of the DSA + * agrees with this being an untagged queue'd command. + */ + + patch_dsa_32 (cmd->dsa, dsa_msgout, 0, 1); + + /* + * Modify the table indirect for COMMAND OUT phase, since + * Request Sense is a six byte command. + */ + + patch_dsa_32 (cmd->dsa, dsa_cmdout, 0, 6); + + c->cmnd[0] = REQUEST_SENSE; + c->cmnd[1] &= 0xe0; /* Zero all but LUN */ + c->cmnd[2] = 0; + c->cmnd[3] = 0; + c->cmnd[4] = sizeof(c->sense_buffer); + c->cmnd[5] = 0; + + /* + * Disable dataout phase, and program datain to transfer to the + * sense buffer, and add a jump to other_transfer after the + * command so overflow/underrun conditions are detected. + */ + + patch_dsa_32 (cmd->dsa, dsa_dataout, 0, + virt_to_bus(hostdata->script) + hostdata->E_other_transfer); + patch_dsa_32 (cmd->dsa, dsa_datain, 0, + virt_to_bus(cmd->data_transfer_start)); + cmd->data_transfer_start[0] = (((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | + DCMD_BMI_IO)) << 24) | sizeof(c->sense_buffer); + cmd->data_transfer_start[1] = (u32) virt_to_bus(c->sense_buffer); + + cmd->data_transfer_start[2] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) + << 24) | DBC_TCI_TRUE; + cmd->data_transfer_start[3] = (u32) virt_to_bus(hostdata->script) + + hostdata->E_other_transfer; + + /* + * Currently, this command is flagged as completed, ie + * it has valid status and message data. Reflag it as + * incomplete. Q - need to do something so that original + * status, etc are used. + */ + + cmd->cmd->result = 0xffff; + + /* + * Restart command as a REQUEST SENSE. + */ + hostdata->dsp = (u32 *) hostdata->script + hostdata->E_select / + sizeof(u32); + hostdata->dsp_changed = 1; + return SPECIFIC_INT_NOTHING; + case A_int_debug_break: + return SPECIFIC_INT_BREAK; + case A_int_norm_aborted: + hostdata->dsp = (u32 *) hostdata->schedule; + hostdata->dsp_changed = 1; + if (cmd) + abnormal_finished (cmd, DID_ERROR << 16); + return SPECIFIC_INT_NOTHING; + case A_int_test_1: + case A_int_test_2: + hostdata->idle = 1; + hostdata->test_completed = (dsps - A_int_test_1) / 0x00010000 + 1; + if (hostdata->options & OPTION_DEBUG_INTR) + printk("scsi%d : test%d complete\n", host->host_no, + hostdata->test_completed); + return SPECIFIC_INT_NOTHING; +#ifdef A_int_debug_reselected_ok + case A_int_debug_reselected_ok: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| + OPTION_DEBUG_DISCONNECT)) { + /* + * Note - this dsa is not based on location relative to + * the command structure, but to location relative to the + * DSA register + */ + u32 *dsa; + dsa = (u32 *) bus_to_virt (NCR53c7x0_read32(DSA_REG)); + + printk("scsi%d : reselected_ok (DSA = 0x%x (virt 0x%p)\n", + host->host_no, NCR53c7x0_read32(DSA_REG), dsa); + printk("scsi%d : resume address is 0x%x (virt 0x%p)\n", + host->host_no, cmd->saved_data_pointer, + bus_to_virt(cmd->saved_data_pointer)); + print_insn (host, hostdata->script + Ent_reselected_ok / + sizeof(u32), "", 1); + printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n", + host->host_no, NCR53c7x0_read8(SXFER_REG), + NCR53c7x0_read8(SCNTL3_REG_800)); + if (c) { + print_insn (host, (u32 *) + hostdata->sync[c->target].script, "", 1); + print_insn (host, (u32 *) + hostdata->sync[c->target].script + 2, "", 1); + } + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_reselect_check + case A_int_debug_reselect_check: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { + u32 *dsa; +#if 0 + u32 *code; +#endif + /* + * Note - this dsa is not based on location relative to + * the command structure, but to location relative to the + * DSA register + */ + dsa = bus_to_virt (NCR53c7x0_read32(DSA_REG)); + printk("scsi%d : reselected_check_next (DSA = 0x%lx (virt 0x%p))\n", + host->host_no, virt_to_bus(dsa), dsa); + if (dsa) { + printk("scsi%d : resume address is 0x%x (virt 0x%p)\n", + host->host_no, cmd->saved_data_pointer, + bus_to_virt (cmd->saved_data_pointer)); +#if 0 + printk("scsi%d : template code :\n", host->host_no); + for (code = dsa + (Ent_dsa_code_check_reselect - Ent_dsa_zero) + / sizeof(u32); code < (dsa + Ent_dsa_zero / sizeof(u32)); + code += print_insn (host, code, "", 1)); +#endif + } + print_insn (host, hostdata->script + Ent_reselected_ok / + sizeof(u32), "", 1); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_dsa_schedule + case A_int_debug_dsa_schedule: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { + u32 *dsa; + /* + * Note - this dsa is not based on location relative to + * the command structure, but to location relative to the + * DSA register + */ + dsa = (u32 *) bus_to_virt (NCR53c7x0_read32(DSA_REG)); + printk("scsi%d : dsa_schedule (old DSA = 0x%lx (virt 0x%p))\n", + host->host_no, virt_to_bus(dsa), dsa); + if (dsa) + printk("scsi%d : resume address is 0x%x (virt 0x%p)\n" + " (temp was 0x%x (virt 0x%p))\n", + host->host_no, cmd->saved_data_pointer, + bus_to_virt (cmd->saved_data_pointer), + NCR53c7x0_read32 (TEMP_REG), + bus_to_virt (NCR53c7x0_read32(TEMP_REG))); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_scheduled + case A_int_debug_scheduled: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { + printk("scsi%d : new I/O 0x%x (virt 0x%p) scheduled\n", + host->host_no, NCR53c7x0_read32(DSA_REG), + bus_to_virt(NCR53c7x0_read32(DSA_REG))); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_idle + case A_int_debug_idle: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { + printk("scsi%d : idle\n", host->host_no); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_cmd + case A_int_debug_cmd: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { + printk("scsi%d : command sent\n"); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_dsa_loaded + case A_int_debug_dsa_loaded: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { + printk("scsi%d : DSA loaded with 0x%x (virt 0x%p)\n", host->host_no, + NCR53c7x0_read32(DSA_REG), + bus_to_virt(NCR53c7x0_read32(DSA_REG))); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_reselected + case A_int_debug_reselected: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| + OPTION_DEBUG_DISCONNECT)) { + printk("scsi%d : reselected by target %d lun %d\n", + host->host_no, (int) NCR53c7x0_read8(SDID_REG_800) & ~0x80, + (int) hostdata->reselected_identify & 7); + print_queues(host); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_disconnect_msg + case A_int_debug_disconnect_msg: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { + if (c) + printk("scsi%d : target %d lun %d disconnecting\n", + host->host_no, c->target, c->lun); + else + printk("scsi%d : unknown target disconnecting\n", + host->host_no); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_disconnected + case A_int_debug_disconnected: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| + OPTION_DEBUG_DISCONNECT)) { + printk ("scsi%d : disconnected, new queues are\n", + host->host_no); + print_queues(host); +#if 0 + printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n", + host->host_no, NCR53c7x0_read8(SXFER_REG), + NCR53c7x0_read8(SCNTL3_REG_800)); +#endif + if (c) { + print_insn (host, (u32 *) + hostdata->sync[c->target].script, "", 1); + print_insn (host, (u32 *) + hostdata->sync[c->target].script + 2, "", 1); + } + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_panic + case A_int_debug_panic: + printk("scsi%d : int_debug_panic received\n", host->host_no); + print_lots (host); + return SPECIFIC_INT_PANIC; +#endif +#ifdef A_int_debug_saved + case A_int_debug_saved: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| + OPTION_DEBUG_DISCONNECT)) { + printk ("scsi%d : saved data pointer 0x%x (virt 0x%p)\n", + host->host_no, cmd->saved_data_pointer, + bus_to_virt (cmd->saved_data_pointer)); + print_progress (c); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_restored + case A_int_debug_restored: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| + OPTION_DEBUG_DISCONNECT)) { + if (cmd) { + int size; + printk ("scsi%d : restored data pointer 0x%x (virt 0x%p)\n", + host->host_no, cmd->saved_data_pointer, bus_to_virt ( + cmd->saved_data_pointer)); + size = print_insn (host, (u32 *) + bus_to_virt(cmd->saved_data_pointer), "", 1); + size = print_insn (host, (u32 *) + bus_to_virt(cmd->saved_data_pointer) + size, "", 1); + print_progress (c); + } +#if 0 + printk ("scsi%d : datapath residual %d\n", + host->host_no, datapath_residual (host)) ; +#endif + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_sync + case A_int_debug_sync: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| + OPTION_DEBUG_DISCONNECT|OPTION_DEBUG_SDTR)) { + unsigned char sxfer = NCR53c7x0_read8 (SXFER_REG), + scntl3 = NCR53c7x0_read8 (SCNTL3_REG_800); + if (c) { + if (sxfer != hostdata->sync[c->target].sxfer_sanity || + scntl3 != hostdata->sync[c->target].scntl3_sanity) { + printk ("scsi%d : sync sanity check failed sxfer=0x%x, scntl3=0x%x", + host->host_no, sxfer, scntl3); + NCR53c7x0_write8 (SXFER_REG, sxfer); + NCR53c7x0_write8 (SCNTL3_REG_800, scntl3); + } + } else + printk ("scsi%d : unknown command sxfer=0x%x, scntl3=0x%x\n", + host->host_no, (int) sxfer, (int) scntl3); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_datain + case A_int_debug_datain: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| + OPTION_DEBUG_DISCONNECT|OPTION_DEBUG_SDTR)) { + int size; + printk ("scsi%d : In do_datain (%s) sxfer=0x%x, scntl3=0x%x\n" + " datapath residual=%d\n", + host->host_no, sbcl_to_phase (NCR53c7x0_read8 (SBCL_REG)), + (int) NCR53c7x0_read8(SXFER_REG), + (int) NCR53c7x0_read8(SCNTL3_REG_800), + datapath_residual (host)) ; + print_insn (host, dsp, "", 1); + size = print_insn (host, (u32 *) bus_to_virt(dsp[1]), "", 1); + print_insn (host, (u32 *) bus_to_virt(dsp[1]) + size, "", 1); + } + return SPECIFIC_INT_RESTART; +#endif +/* + * FIXME : for 7xx support, we need to read SDID_REG_700 and handle + * the comparison as bitfielded, not binary. + */ +#ifdef A_int_debug_check_dsa + case A_int_debug_check_dsa: + if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) { + int sdid = NCR53c7x0_read8 (SDID_REG_800) & 15; + char *where = dsp - NCR53c7x0_insn_size(NCR53c7x0_read8 + (DCMD_REG)) == hostdata->script + + Ent_select_check_dsa / sizeof(u32) ? + "selection" : "reselection"; + if (c && sdid != c->target) { + printk ("scsi%d : SDID target %d != DSA target %d at %s\n", + host->host_no, sdid, c->target, where); + print_lots(host); + dump_events (host, 20); + return SPECIFIC_INT_PANIC; + } + } + return SPECIFIC_INT_RESTART; +#endif + default: + if ((dsps & 0xff000000) == 0x03000000) { + printk ("scsi%d : misc debug interrupt 0x%x\n", + host->host_no, dsps); + return SPECIFIC_INT_RESTART; + } else if ((dsps & 0xff000000) == 0x05000000) { + if (hostdata->events) { + struct NCR53c7x0_event *event; + ++hostdata->event_index; + if (hostdata->event_index >= hostdata->event_size) + hostdata->event_index = 0; + event = (struct NCR53c7x0_event *) hostdata->events + + hostdata->event_index; + event->event = (enum ncr_event) dsps; + event->dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG)); + /* FIXME : this needs to change for the '7xx family */ + if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) + event->target = NCR53c7x0_read8(SSID_REG_800); + else + event->target = 255; + + if (event->event == EVENT_RESELECT) + event->lun = hostdata->reselected_identify & 0xf; + else if (c) + event->lun = c->lun; + else + event->lun = 255; + do_gettimeofday(&(event->time)); + if (c) { + event->pid = c->pid; + memcpy ((void *) event->cmnd, (void *) c->cmnd, + sizeof (event->cmnd)); + } else { + event->pid = -1; + } + } + return SPECIFIC_INT_RESTART; + } + + printk ("scsi%d : unknown user interrupt 0x%x\n", + host->host_no, (unsigned) dsps); + return SPECIFIC_INT_PANIC; + } +} + +/* + * XXX - the stock NCR assembler won't output the scriptu.h file, + * which undefine's all #define'd CPP symbols from the script.h + * file, which will create problems if you use multiple scripts + * with the same symbol names. + * + * If you insist on using NCR's assembler, you could generate + * scriptu.h from script.h using something like + * + * grep #define script.h | \ + * sed 's/#define[ ][ ]*\([_a-zA-Z][_a-zA-Z0-9]*\).*$/#undefine \1/' \ + * > scriptu.h + */ + +#include "53c8xx_u.h" + +/* XXX - add alternate script handling code here */ + + +#ifdef NCR_DEBUG +/* + * Debugging without a debugger is no fun. So, I've provided + * a debugging interface in the NCR53c7x0 driver. To avoid + * kernel cruft, there's just enough here to act as an interface + * to a user level debugger (aka, GDB). + * + * + * The following restrictions apply to debugger commands : + * 1. The command must be terminated by a newline. + * 2. Command length must be less than 80 bytes including the + * newline. + * 3. The entire command must be written with one system call. + */ + +static const char debugger_help = +"bc - clear breakpoint\n" +"bl - list breakpoints\n" +"bs - set breakpoint\n" +"g - start\n" +"h - halt\n" +"? - this message\n" +"i - info\n" +"mp - print memory\n" +"ms - store memory\n" +"rp - print register\n" +"rs - store register\n" +"s - single step\n" +"tb - begin trace \n" +"te - end trace\n"; + +/* + * Whenever we change a break point, we should probably + * set the NCR up so that it is in a single step mode. + */ + +static int debugger_fn_bc (struct Scsi_Host *host, struct debugger_token *token, + u32 args[]) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + instance->hostdata; + struct NCR53c7x0_break *bp, **prev; + unsigned long flags; + save_flags(flags); + cli(); + for (bp = (struct NCR53c7x0_break *) instance->breakpoints, + prev = (struct NCR53c7x0_break **) &instance->breakpoints; + bp; prev = (struct NCR53c7x0_break **) &(bp->next), + bp = (struct NCR53c7x0_break *) bp->next); + + if (!bp) { + restore_flags(flags); + return -EIO; + } + + /* + * XXX - we need to insure that the processor is halted + * here in order to prevent a race condition. + */ + + memcpy ((void *) bp->addr, (void *) bp->old, sizeof(bp->old)); + if (prev) + *prev = bp->next; + + restore_flags(flags); + return 0; +} + + +static int +debugger_fn_bl (struct Scsi_Host *host, struct debugger_token *token, + u32 args[]) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + struct NCR53c7x0_break *bp; + char buf[80]; + size_t len; + unsigned long flags; + /* + * XXX - we need to insure that the processor is halted + * here in order to prevent a race condition. So, if the + * processor isn't halted, print an error message and continue. + */ + + sprintf (buf, "scsi%d : bp : warning : processor not halted\b", + host->host_no); + debugger_kernel_write (host, buf, strlen(buf)); + + save_flags(flags); + cli(); + for (bp = (struct NCR53c7x0_break *) host->breakpoints; + bp; bp = (struct NCR53c7x0_break *) bp->next); { + sprintf (buf, "scsi%d : bp : success : at %08x, replaces %08x %08x", + bp->addr, bp->old[0], bp->old[1]); + len = strlen(buf); + if ((bp->old[0] & (DCMD_TYPE_MASK << 24)) == + (DCMD_TYPE_MMI << 24)) { + sprintf(buf + len, "%08x\n", * (u32 *) bp->addr); + } else { + sprintf(buf + len, "\n"); + } + len = strlen(buf); + debugger_kernel_write (host, buf, len); + } + restore_flags(flags); + return 0; +} + +static int +debugger_fn_bs (struct Scsi_Host *host, struct debugger_token *token, + u32 args[]) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + struct NCR53c7x0_break *bp; + char buf[80]; + size_t len; + unsigned long flags; + save_flags(flags); + cli(); + + if (hostdata->state != STATE_HALTED) { + sprintf (buf, "scsi%d : bs : failure : NCR not halted\n", host->host_no); + debugger_kernel_write (host, buf, strlen(buf)); + restore_flags(flags); + return -1; + } + + if (!(bp = kmalloc (sizeof (struct NCR53c7x0_break)))) { + printk ("scsi%d : kmalloc(%d) of breakpoint structure failed, try again\n", + host->host_no, sizeof(struct NCR53c7x0_break)); + restore_flags(flags); + return -1; + } + + bp->address = (u32 *) args[0]; + memcpy ((void *) bp->old_instruction, (void *) bp->address, 8); + bp->old_size = (((bp->old_instruction[0] >> 24) & DCMD_TYPE_MASK) == + DCMD_TYPE_MMI ? 3 : 2; + bp->next = hostdata->breakpoints; + hostdata->breakpoints = bp->next; + memcpy ((void *) bp->address, (void *) hostdata->E_debug_break, 8); + + restore_flags(flags); + return 0; +} + +#define TOKEN(name,nargs) {#name, nargs, debugger_fn_##name} +static const struct debugger_token { + char *name; + int numargs; + int (*fn)(struct debugger_token *token, u32 args[]); +} debugger_tokens[] = { + TOKEN(bc,1), TOKEN(bl,0), TOKEN(bs,1), TOKEN(g,0), TOKEN(halt,0), + {DT_help, "?", 0} , TOKEN(h,0), TOKEN(i,0), TOKEN(mp,2), + TOKEN(ms,3), TOKEN(rp,2), TOKEN(rs,2), TOKEN(s,0), TOKEN(tb,0), TOKEN(te,0) +}; + +#define NDT sizeof(debugger_tokens / sizeof(struct debugger_token)) + +static struct Scsi_Host * inode_to_host (struct inode *inode) { + int dev; + struct Scsi_Host *tmp; + for (dev = MINOR(inode->rdev), host = first_host; + (host->hostt == the_template); --dev, host = host->next) + if (!dev) return host; + return NULL; +} + + +static int +debugger_user_write (struct inode *inode,struct file *filp, + char *buf,int count) { + struct Scsi_Host *host; /* This SCSI host */ + struct NCR53c7x0_hostadata *hostdata; + char input_buf[80], /* Kernel space copy of buf */ + *ptr; /* Pointer to argument list */ + u32 args[3]; /* Arguments */ + int i, j, error, len; + + if (!(host = inode_to_host(inode))) + return -ENXIO; + + hostdata = (struct NCR53c7x0_hostdata *) host->hostdata; + + if (error = verify_area(VERIFY_READ,buf,count)) + return error; + + if (count > 80) + return -EIO; + + memcpy_from_fs(input_buf, buf, count); + + if (input_buf[count - 1] != '\n') + return -EIO; + + input_buf[count - 1]=0; + + for (i = 0; i < NDT; ++i) { + len = strlen (debugger_tokens[i].name); + if (!strncmp(input_buf, debugger_tokens[i].name, len)) + break; + }; + + if (i == NDT) + return -EIO; + + for (ptr = input_buf + len, j = 0; j < debugger_tokens[i].nargs && *ptr;) { + if (*ptr == ' ' || *ptr == '\t') { + ++ptr; + } else if (isdigit(*ptr)) { + args[j++] = simple_strtoul (ptr, &ptr, 0); + } else { + return -EIO; + } + } + + if (j != debugger_tokens[i].nargs) + return -EIO; + + return count; +} + +static int +debugger_user_read (struct inode *inode,struct file *filp, + char *buf,int count) { + struct Scsi_Host *instance; + +} + +static int +debugger_kernel_write (struct Scsi_Host *host, char *buf, size_t + buflen) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + int copy, left; + unsigned long flags; + save_flags(flags); + cli(); + while (buflen) { + left = (hostdata->debug_buf + hostdata->debug_size - 1) - + hostdata->debug_write; + copy = (buflen <= left) ? buflen : left; + memcpy (hostdata->debug_write, buf, copy); + buf += copy; + buflen -= copy; + hostdata->debug_count += copy; + if ((hostdata->debug_write += copy) == + (hostdata->debug_buf + hostdata->debug_size)) + hosdata->debug_write = hostdata->debug_buf; + } + restore_flags(flags); +} + +#endif /* def NCRDEBUG */ + +/* + * Function : static void NCR538xx_soft_reset (struct Scsi_Host *host) + * + * Purpose : perform a soft reset of the NCR53c8xx chip + * + * Inputs : host - pointer to this host adapter's structure + * + * Preconditions : NCR53c7x0_init must have been called for this + * host. + * + */ + +static void +NCR53c8x0_soft_reset (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + NCR53c7x0_local_setup(host); + + + /* + * Do a soft reset of the chip so that everything is + * reinitialized to the power-on state. + * + * Basically follow the procedure outlined in the NCR53c700 + * data manual under Chapter Six, How to Use, Steps Necessary to + * Start SCRIPTS, with the exception of actually starting the + * script and setting up the synchronous transfer gunk. + */ + + NCR53c7x0_write8(ISTAT_REG_800, ISTAT_10_SRST); + NCR53c7x0_write8(ISTAT_REG_800, 0); + NCR53c7x0_write8(hostdata->dmode, hostdata->saved_dmode & ~DMODE_MAN); + + + /* + * Respond to reselection by targets and use our _initiator_ SCSI ID + * for arbitration. If notyet, also respond to SCSI selection. + * + * XXX - Note : we must reprogram this when reselecting as + * a target. + */ + +#ifdef notyet + NCR53c7x0_write8(SCID_REG, (host->this_id & 7)|SCID_800_RRE|SCID_800_SRE); +#else + NCR53c7x0_write8(SCID_REG, (host->this_id & 7)|SCID_800_RRE); +#endif + NCR53c7x0_write8(RESPID_REG_800, hostdata->this_id_mask); + + /* + * Use a maximum (1.6) second handshake to handshake timeout, + * and SCSI recommended .5s selection timeout. + */ + + /* + * The new gcc won't recognize preprocessing directives + * within macro args. + */ +#if 0 + NCR53c7x0_write8(STIME0_REG_800, + ((selection_timeout << STIME0_800_SEL_SHIFT) & STIME0_800_SEL_MASK) + | ((15 << STIME0_800_HTH_SHIFT) & STIME0_800_HTH_MASK)); +#else +/* Disable HTH interrupt */ + NCR53c7x0_write8(STIME0_REG_800, + ((selection_timeout << STIME0_800_SEL_SHIFT) & STIME0_800_SEL_MASK)); +#endif + + + /* + * Enable active negation for happy synchronous transfers. + */ + + NCR53c7x0_write8(STEST3_REG_800, STEST3_800_TE); + + /* + * Enable all interrupts, except parity which we only want when + * the user requests it. + */ + + NCR53c7x0_write8(DIEN_REG, DIEN_800_MDPE | DIEN_800_BF | + DIEN_ABRT | DIEN_SSI | DIEN_SIR | DIEN_800_IID); + + + NCR53c7x0_write8(SIEN0_REG_800, ((hostdata->options & OPTION_PARITY) ? + SIEN_PAR : 0) | SIEN_RST | SIEN_UDC | SIEN_SGE | SIEN_MA); + NCR53c7x0_write8(SIEN1_REG_800, SIEN1_800_STO | SIEN1_800_HTH); + + /* + * Use saved clock frequency divisor and scripts loaded in 16 bit + * mode flags from the saved dcntl. + */ + + NCR53c7x0_write8(DCNTL_REG, hostdata->saved_dcntl); + NCR53c7x0_write8(CTEST4_REG_800, hostdata->saved_ctest4); + + /* Enable active negation */ + NCR53c7x0_write8(STEST3_REG_800, STEST3_800_TE); +} + +/* + * Function static struct NCR53c7x0_cmd *allocate_cmd (Scsi_Cmnd *cmd) + * + * Purpose : Return the first free NCR53c7x0_cmd structure (which are + * reused in a LIFO maner to minimize cache thrashing). + * + * Side effects : If we haven't yet scheduled allocation of NCR53c7x0_cmd + * structures for this device, do so. Attempt to complete all scheduled + * allocations using kmalloc(), putting NCR53c7x0_cmd structures on + * the free list. Teach programmers not to drink and hack. + * + * Inputs : cmd - SCSI command + * + * Returns : NCR53c7x0_cmd structure allocated on behalf of cmd; + * NULL on failure. + */ + +static struct NCR53c7x0_cmd * +allocate_cmd (Scsi_Cmnd *cmd) { + struct Scsi_Host *host = cmd->host; + struct NCR53c7x0_hostdata *hostdata = + (struct NCR53c7x0_hostdata *) host->hostdata; + void *real; /* Real address */ + int size; /* Size of *tmp */ + struct NCR53c7x0_cmd *tmp; + unsigned long flags; + + if (hostdata->options & OPTION_DEBUG_ALLOCATION) + printk ("scsi%d : num_cmds = %d, can_queue = %d\n" + " target = %d, lun = %d, %s\n", + host->host_no, hostdata->num_cmds, host->can_queue, + cmd->target, cmd->lun, (hostdata->cmd_allocated[cmd->target] & + (1 << cmd->lun)) ? "allready allocated" : "not allocated"); + +/* + * If we have not yet reserved commands for this I_T_L nexus, and + * the device exists (as indicated by permanant Scsi_Cmnd structures + * being allocated under 1.3.x, or being outside of scan_scsis in + * 1.2.x), do so now. + */ + if (!(hostdata->cmd_allocated[cmd->target] & (1 << cmd->lun)) && +#ifdef LINUX_1_2 + !in_scan_scsis +#else + cmd->device && cmd->device->has_cmdblocks +#endif + ) { + if ((hostdata->extra_allocate + hostdata->num_cmds) < host->can_queue) + hostdata->extra_allocate += host->cmd_per_lun; + hostdata->cmd_allocated[cmd->target] |= (1 << cmd->lun); + } + + for (; hostdata->extra_allocate > 0 ; --hostdata->extra_allocate, + ++hostdata->num_cmds) { + /* historically, kmalloc has returned unaligned addresses; pad so we + have enough room to ROUNDUP */ + size = hostdata->max_cmd_size + sizeof (void *); +/* FIXME: for ISA bus '7xx chips, we need to or GFP_DMA in here */ + real = kmalloc (size, GFP_ATOMIC); + if (!real) { + if (hostdata->options & OPTION_DEBUG_ALLOCATION) + printk ("scsi%d : kmalloc(%d) failed\n", + host->host_no, size); + break; + } + tmp = ROUNDUP(real, void *); + tmp->real = real; + tmp->size = size; +#ifdef LINUX_1_2 + tmp->free = ((void (*)(void *, int)) kfree_s); +#else + tmp->free = ((void (*)(void *, int)) kfree); +#endif + save_flags (flags); + cli(); + tmp->next = hostdata->free; + hostdata->free = tmp; + restore_flags (flags); + } + save_flags(flags); + cli(); + tmp = (struct NCR53c7x0_cmd *) hostdata->free; + if (tmp) { + hostdata->free = tmp->next; + } + restore_flags(flags); + if (!tmp) + printk ("scsi%d : can't allocate command for target %d lun %d\n", + host->host_no, cmd->target, cmd->lun); + return tmp; +} + +/* + * Function static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd) + * + * + * Purpose : allocate a NCR53c7x0_cmd structure, initialize it based on the + * Scsi_Cmnd structure passed in cmd, including dsa and Linux field + * initialization, and dsa code relocation. + * + * Inputs : cmd - SCSI command + * + * Returns : NCR53c7x0_cmd structure corresponding to cmd, + * NULL on failure. + */ + +static struct NCR53c7x0_cmd * +create_cmd (Scsi_Cmnd *cmd) { + NCR53c7x0_local_declare(); + struct Scsi_Host *host = cmd->host; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + struct NCR53c7x0_cmd *tmp; /* NCR53c7x0_cmd structure for this command */ + int datain, /* Number of instructions per phase */ + dataout; + int data_transfer_instructions, /* Count of dynamic instructions */ + i; /* Counter */ + u32 *cmd_datain, /* Address of datain/dataout code */ + *cmd_dataout; /* Incremented as we assemble */ +#ifdef notyet + unsigned char *msgptr; /* Current byte in select message */ + int msglen; /* Length of whole select message */ +#endif + unsigned long flags; + NCR53c7x0_local_setup(cmd->host); + + if (!(tmp = allocate_cmd (cmd))) + return NULL; + + + /* + * Decide whether we need to generate commands for DATA IN, + * DATA OUT, neither, or both based on the SCSI command + */ + + switch (cmd->cmnd[0]) { + /* These commands do DATA IN */ + case INQUIRY: + case MODE_SENSE: + case READ_6: + case READ_10: + case READ_CAPACITY: + case REQUEST_SENSE: + datain = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3; + dataout = 0; + break; + /* These commands do DATA OUT */ + case MODE_SELECT: + case WRITE_6: + case WRITE_10: +#if 0 + printk("scsi%d : command is ", host->host_no); + print_command(cmd->cmnd); +#endif +#if 0 + printk ("scsi%d : %d scatter/gather segments\n", host->host_no, + cmd->use_sg); +#endif + datain = 0; + dataout = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3; +#if 0 + hostdata->options |= OPTION_DEBUG_INTR; +#endif + break; + /* + * These commands do no data transfer, we should force an + * interrupt if a data phase is attempted on them. + */ + case START_STOP: + case TEST_UNIT_READY: + datain = dataout = 0; + break; + /* + * We don't know about these commands, so generate code to handle + * both DATA IN and DATA OUT phases. + */ + default: + datain = dataout = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3; + } + + /* + * New code : so that active pointers work correctly irregardless + * of where the saved data pointer is at, we want to immediately + * enter the dynamic code after selection, and on a non-data + * phase perform a CALL to the non-data phase handler, with + * returns back to this address. + * + * If a phase mismatch is encountered in the middle of a + * Block MOVE instruction, we want to _leave_ that instruction + * unchanged as the current case is, modify a temporary buffer, + * and point the active pointer (TEMP) at that. + * + * Furthermore, we want to implement a saved data pointer, + * set by the SAVE_DATA_POINTERs message. + * + * So, the data transfer segments will change to + * CALL data_transfer, WHEN NOT data phase + * MOVE x, x, WHEN data phase + * ( repeat ) + * JUMP other_transfer + */ + + data_transfer_instructions = datain + dataout; + + /* + * When we perform a request sense, we overwrite various things, + * including the data transfer code. Make sure we have enough + * space to do that. + */ + + if (data_transfer_instructions < 2) + data_transfer_instructions = 2; + + + /* + * The saved data pointer is set up so that a RESTORE POINTERS message + * will start the data transfer over at the beggining. + */ + + tmp->saved_data_pointer = virt_to_bus (hostdata->script) + + hostdata->E_data_transfer; + + /* + * Initialize Linux specific fields. + */ + + tmp->cmd = cmd; + tmp->next = NULL; + tmp->flags = 0; + tmp->dsa_next_addr = virt_to_bus(tmp->dsa) + hostdata->dsa_next - + hostdata->dsa_start; + tmp->dsa_addr = virt_to_bus(tmp->dsa) - hostdata->dsa_start; + + /* + * Calculate addresses of dynamic code to fill in DSA + */ + + tmp->data_transfer_start = tmp->dsa + (hostdata->dsa_end - + hostdata->dsa_start) / sizeof(u32); + tmp->data_transfer_end = tmp->data_transfer_start + + 2 * data_transfer_instructions; + + cmd_datain = datain ? tmp->data_transfer_start : NULL; + cmd_dataout = dataout ? (datain ? cmd_datain + 2 * datain : tmp-> + data_transfer_start) : NULL; + + /* + * Fill in the NCR53c7x0_cmd structure as follows + * dsa, with fixed up DSA code + * datain code + * dataout code + */ + + /* Copy template code into dsa and perform all necessary fixups */ + if (hostdata->dsa_fixup) + hostdata->dsa_fixup(tmp); + + patch_dsa_32(tmp->dsa, dsa_next, 0, 0); + patch_dsa_32(tmp->dsa, dsa_cmnd, 0, virt_to_bus(cmd)); + + if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS) + if (hostdata->sync[cmd->target].select_indirect != + ((hostdata->sync[cmd->target].scntl3_sanity << 24) | + (cmd->target << 16) | + (hostdata->sync[cmd->target].sxfer_sanity << 8))) { + printk ("scsi%d : sanity check failed select_indirect=0x%x\n", + host->host_no, hostdata->sync[cmd->target].select_indirect); + FATAL(host); + + } + + patch_dsa_32(tmp->dsa, dsa_select, 0, hostdata->sync[cmd->target]. + select_indirect); + /* + * Right now, we'll do the WIDE and SYNCHRONOUS negotiations on + * different commands; although it should be trivial to do them + * both at the same time. + */ + if (hostdata->initiate_wdtr & (1 << cmd->target)) { + memcpy ((void *) (tmp->select + 1), (void *) wdtr_message, + sizeof(wdtr_message)); + patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(wdtr_message)); + save_flags(flags); + cli(); + hostdata->initiate_wdtr &= ~(1 << cmd->target); + restore_flags(flags); + } else if (hostdata->initiate_sdtr & (1 << cmd->target)) { + memcpy ((void *) (tmp->select + 1), (void *) sdtr_message, + sizeof(sdtr_message)); + patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(sdtr_message)); + tmp->flags |= CMD_FLAG_SDTR; + save_flags(flags); + cli(); + hostdata->initiate_sdtr &= ~(1 << cmd->target); + restore_flags(flags); + + } +#if 1 + else if (!(hostdata->talked_to & (1 << cmd->target)) && + !(hostdata->options & OPTION_NO_ASYNC)) { + memcpy ((void *) (tmp->select + 1), (void *) async_message, + sizeof(async_message)); + patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(async_message)); + tmp->flags |= CMD_FLAG_SDTR; + } +#endif + else + patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1); + hostdata->talked_to |= (1 << cmd->target); + tmp->select[0] = (hostdata->options & OPTION_DISCONNECT) ? + IDENTIFY (1, cmd->lun) : IDENTIFY (0, cmd->lun); + patch_dsa_32(tmp->dsa, dsa_msgout, 1, virt_to_bus(tmp->select)); + patch_dsa_32(tmp->dsa, dsa_cmdout, 0, cmd->cmd_len); + patch_dsa_32(tmp->dsa, dsa_cmdout, 1, virt_to_bus(cmd->cmnd)); + patch_dsa_32(tmp->dsa, dsa_dataout, 0, cmd_dataout ? + virt_to_bus (cmd_dataout) + : virt_to_bus (hostdata->script) + hostdata->E_other_transfer); + patch_dsa_32(tmp->dsa, dsa_datain, 0, cmd_datain ? + virt_to_bus (cmd_datain) + : virt_to_bus (hostdata->script) + hostdata->E_other_transfer); + /* + * XXX - need to make endian aware, should use separate variables + * for both status and message bytes. + */ + patch_dsa_32(tmp->dsa, dsa_msgin, 0, 1); +/* + * FIXME : these only works for little endian. We probably want to + * provide message and status fields in the NCR53c7x0_cmd + * structure, and assign them to cmd->result when we're done. + */ + patch_dsa_32(tmp->dsa, dsa_msgin, 1, virt_to_bus(&cmd->result) + 1); + patch_dsa_32(tmp->dsa, dsa_status, 0, 1); + patch_dsa_32(tmp->dsa, dsa_status, 1, virt_to_bus(&cmd->result)); + patch_dsa_32(tmp->dsa, dsa_msgout_other, 0, 1); + patch_dsa_32(tmp->dsa, dsa_msgout_other, 1, + virt_to_bus(&(hostdata->NCR53c7xx_msg_nop))); + + /* + * Generate code for zero or more of the DATA IN, DATA OUT phases + * in the format + * + * CALL data_transfer, WHEN NOT phase + * MOVE first buffer length, first buffer address, WHEN phase + * ... + * MOVE last buffer length, last buffer address, WHEN phase + * JUMP other_transfer + */ + +/* + * See if we're getting to data transfer by generating an unconditional + * interrupt. + */ +#if 0 + if (datain) { + cmd_datain[0] = 0x98080000; + cmd_datain[1] = 0x03ffd00d; + cmd_datain += 2; + } +#endif + +/* + * XXX - I'm undecided whether all of this nonsense is faster + * in the long run, or whether I should just go and implement a loop + * on the NCR chip using table indirect mode? + * + * In any case, this is how it _must_ be done for 53c700/700-66 chips, + * so this stays even when we come up with something better. + * + * When we're limited to 1 simultaneous command, no overlapping processing, + * we're seeing 630K/sec, with 7% CPU usage on a slow Syquest 45M + * drive. + * + * Not bad, not good. We'll see. + */ + + for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; cmd_datain += 4, + cmd_dataout += 4, ++i) { + u32 buf = cmd->use_sg ? + virt_to_bus(((struct scatterlist *)cmd->buffer)[i].address) : + virt_to_bus(cmd->request_buffer); + u32 count = cmd->use_sg ? + ((struct scatterlist *)cmd->buffer)[i].length : + cmd->request_bufflen; + + if (datain) { + /* CALL other_in, WHEN NOT DATA_IN */ + cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL | + DCMD_TCI_IO) << 24) | + DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE; + cmd_datain[1] = virt_to_bus (hostdata->script) + + hostdata->E_other_in; + /* MOVE count, buf, WHEN DATA_IN */ + cmd_datain[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | DCMD_BMI_IO) + << 24) | count; + cmd_datain[3] = buf; +#if 0 + print_insn (host, cmd_datain, "dynamic ", 1); + print_insn (host, cmd_datain + 2, "dynamic ", 1); +#endif + } + if (dataout) { + /* CALL other_out, WHEN NOT DATA_OUT */ + cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL) << 24) | + DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE; + cmd_dataout[1] = virt_to_bus(hostdata->script) + + hostdata->E_other_out; + /* MOVE count, buf, WHEN DATA+OUT */ + cmd_dataout[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I) << 24) + | count; + cmd_dataout[3] = buf; +#if 0 + print_insn (host, cmd_dataout, "dynamic ", 1); + print_insn (host, cmd_dataout + 2, "dynamic ", 1); +#endif + } + } + + /* + * Install JUMP instructions after the data transfer routines to return + * control to the do_other_transfer routines. + */ + + + if (datain) { + cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) | + DBC_TCI_TRUE; + cmd_datain[1] = virt_to_bus(hostdata->script) + + hostdata->E_other_transfer; +#if 0 + print_insn (host, cmd_datain, "dynamic jump ", 1); +#endif + cmd_datain += 2; + } +#if 0 + if (datain) { + cmd_datain[0] = 0x98080000; + cmd_datain[1] = 0x03ffdeed; + cmd_datain += 2; + } +#endif + if (dataout) { + cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) | + DBC_TCI_TRUE; + cmd_dataout[1] = virt_to_bus(hostdata->script) + + hostdata->E_other_transfer; +#if 0 + print_insn (host, cmd_dataout, "dynamic jump ", 1); +#endif + cmd_dataout += 2; + } + return tmp; +} + +/* + * Function : int NCR53c7xx_queue_command (Scsi_Cmnd *cmd, + * void (*done)(Scsi_Cmnd *)) + * + * Purpose : enqueues a SCSI command + * + * Inputs : cmd - SCSI command, done - function called on completion, with + * a pointer to the command descriptor. + * + * Returns : 0 + * + * Side effects : + * cmd is added to the per instance driver issue_queue, with major + * twiddling done to the host specific fields of cmd. If the + * process_issue_queue corouting isn't running, it is restarted. + * + * NOTE : we use the host_scribble field of the Scsi_Cmnd structure to + * hold our own data, and pervert the ptr field of the SCp field + * to create a linked list. + */ + +int +NCR53c7xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) { + struct Scsi_Host *host = cmd->host; + struct NCR53c7x0_hostdata *hostdata = + (struct NCR53c7x0_hostdata *) host->hostdata; + unsigned long flags; + Scsi_Cmnd *tmp; + + cmd->scsi_done = done; + cmd->host_scribble = NULL; + cmd->SCp.ptr = NULL; + cmd->SCp.buffer = NULL; + + save_flags(flags); + cli(); + if ((hostdata->options & (OPTION_DEBUG_INIT_ONLY|OPTION_DEBUG_PROBE_ONLY)) + || ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) && + !(hostdata->debug_lun_limit[cmd->target] & (1 << cmd->lun))) +#ifdef LINUX_1_2 + || cmd->target > 7 +#else + || cmd->target > host->max_id +#endif + || cmd->target == host->this_id + || hostdata->state == STATE_DISABLED) { + printk("scsi%d : disabled or bad target %d lun %d\n", host->host_no, + cmd->target, cmd->lun); + cmd->result = (DID_BAD_TARGET << 16); + } else if ((hostdata->options & OPTION_DEBUG_NCOMMANDS_LIMIT) && + (hostdata->debug_count_limit == 0)) { + printk("scsi%d : maximum commands exceeded\n", host->host_no); + cmd->result = (DID_BAD_TARGET << 16); + cmd->result = (DID_BAD_TARGET << 16); + } else if (hostdata->options & OPTION_DEBUG_READ_ONLY) { + switch (cmd->cmnd[0]) { + case WRITE_6: + case WRITE_10: + printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n", + host->host_no); + cmd->result = (DID_BAD_TARGET << 16); + } + } else { + if ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) && + hostdata->debug_count_limit != -1) + --hostdata->debug_count_limit; + restore_flags (flags); + cmd->result = 0xffff; /* The NCR will overwrite message + and status with valid data */ + cmd->host_scribble = (unsigned char *) tmp = create_cmd (cmd); + } + cli(); + /* + * REQUEST SENSE commands are inserted at the head of the queue + * so that we do not clear the contingent allegience condition + * they may be looking at. + */ + + if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { + cmd->SCp.ptr = (unsigned char *) hostdata->issue_queue; + hostdata->issue_queue = cmd; + } else { + for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->SCp.ptr; + tmp = (Scsi_Cmnd *) tmp->SCp.ptr); + tmp->SCp.ptr = (unsigned char *) cmd; + } + restore_flags (flags); + run_process_issue_queue(); + return 0; +} + +/* + * Function : void to_schedule_list (struct Scsi_Host *host, + * struct NCR53c7x0_hostdata * hostdata, Scsi_Cmnd *cmd) + * + * Purpose : takes a SCSI command which was just removed from the + * issue queue, and deals with it by inserting it in the first + * free slot in the schedule list or by terminating it immediately. + * + * Inputs : + * host - SCSI host adater; hostdata - hostdata structure for + * this adapter; cmd - a pointer to the command; should have + * the host_scribble field initialized to point to a valid + * + * Side effects : + * cmd is added to the per instance schedule list, with minor + * twiddling done to the host specific fields of cmd. + * + */ + +static __inline__ void +to_schedule_list (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata, + struct NCR53c7x0_cmd *cmd) { + NCR53c7x0_local_declare(); + Scsi_Cmnd *tmp = cmd->cmd; + unsigned long flags; + /* dsa start is negative, so subtraction is used */ + volatile u32 *current; + + int i; + NCR53c7x0_local_setup(host); +#if 0 + printk("scsi%d : new dsa is 0x%lx (virt 0x%p)\n", host->host_no, + virt_to_bus(dsa), dsa); +#endif + + save_flags(flags); + cli(); + + /* + * Work arround race condition : if an interrupt fired and we + * got disabled forget about this command. + */ + + if (hostdata->state == STATE_DISABLED) { + printk("scsi%d : driver disabled\n", host->host_no); + tmp->result = (DID_BAD_TARGET << 16); + cmd->next = (struct NCR53c7x0_cmd *) hostdata->free; + hostdata->free = cmd; + tmp->scsi_done(tmp); + restore_flags (flags); + return; + } + + for (i = host->can_queue, current = hostdata->schedule; + i > 0 && current[0] != hostdata->NOP_insn; + --i, current += 2 /* JUMP instructions are two words */); + + if (i > 0) { + ++hostdata->busy[tmp->target][tmp->lun]; + cmd->next = hostdata->running_list; + hostdata->running_list = cmd; + + /* Restore this instruction to a NOP once the command starts */ + cmd->dsa [(hostdata->dsa_jump_dest - hostdata->dsa_start) / + sizeof(u32)] = (u32) virt_to_bus ((void *)current); + /* Replace the current jump operand. */ + current[1] = + virt_to_bus ((void *) cmd->dsa) + hostdata->E_dsa_code_begin - + hostdata->E_dsa_code_template; + /* Replace the NOP instruction with a JUMP */ + current[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | + DBC_TCI_TRUE; + } else { + printk ("scsi%d: no free slot\n", host->host_no); + disable(host); + tmp->result = (DID_ERROR << 16); + cmd->next = (struct NCR53c7x0_cmd *) hostdata->free; + hostdata->free = cmd; + tmp->scsi_done(tmp); + restore_flags (flags); + return; + } + + /* + * If the NCR chip is in an idle state, start it running the scheduler + * immediately. Otherwise, signal the chip to jump to schedule as + * soon as it is idle. + */ + if (hostdata->idle) { + hostdata->idle = 0; + hostdata->state = STATE_RUNNING; + NCR53c7x0_write32 (DSP_REG, virt_to_bus ((void *)hostdata->schedule)); + } else { + NCR53c7x0_write8(hostdata->istat, ISTAT_10_SIGP); + } + + restore_flags(flags); +} + +/* + * Function : busyp (struct Scsi_Host *host, struct NCR53c7x0_hostdata + * *hostdata, Scsi_Cmnd *cmd) + * + * Purpose : decide if we can pass the given SCSI command on to the + * device in question or not. + * + * Returns : non-zero when we're busy, 0 when we aren't. + */ + +static __inline__ int +busyp (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata, + Scsi_Cmnd *cmd) { + /* FIXME : in the future, this needs to accomodate SCSI-II tagged + queuing, and we may be able to play with fairness here a bit. + */ + return hostdata->busy[cmd->target][cmd->lun]; +} + +/* + * Function : process_issue_queue (void) + * + * Purpose : transfer commands from the issue queue to NCR start queue + * of each NCR53c7/8xx in the system, avoiding kernel stack + * overflows when the scsi_done() function is invoked recursively. + * + * NOTE : process_issue_queue exits with interrupts *disabled*, so the + * caller must renable them if it desires. + * + * NOTE : process_issue_queue should be called from both + * NCR53c7x0_queue_command() and from the interrupt handler + * after command completion in case NCR53c7x0_queue_command() + * isn't invoked again but we've freed up resources that are + * needed. + */ + +static void +process_issue_queue (unsigned long flags) { + Scsi_Cmnd *tmp, *prev; + struct Scsi_Host *host; + struct NCR53c7x0_hostdata *hostdata; + int done; + + /* + * We run (with interrupts disabled) until we're sure that none of + * the host adapters have anything that can be done, at which point + * we set process_issue_queue_running to 0 and exit. + * + * Interrupts are enabled before doing various other internal + * instructions, after we've decided that we need to run through + * the loop again. + * + */ + + do { + cli(); /* Freeze request queues */ + done = 1; + for (host = first_host; host && host->hostt == the_template; + host = host->next) { + hostdata = (struct NCR53c7x0_hostdata *) host->hostdata; + cli(); + if (hostdata->issue_queue) { + if (hostdata->state == STATE_DISABLED) { + tmp = (Scsi_Cmnd *) hostdata->issue_queue; + hostdata->issue_queue = (Scsi_Cmnd *) tmp->SCp.ptr; + tmp->result = (DID_BAD_TARGET << 16); + if (tmp->host_scribble) { + ((struct NCR53c7x0_cmd *)tmp->host_scribble)->next = + hostdata->free; + hostdata->free = + (struct NCR53c7x0_cmd *)tmp->host_scribble; + tmp->host_scribble = NULL; + } + tmp->scsi_done (tmp); + done = 0; + } else + for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, + prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) + tmp->SCp.ptr) + if (!tmp->host_scribble || + !busyp (host, hostdata, tmp)) { + if (prev) + prev->SCp.ptr = tmp->SCp.ptr; + else + hostdata->issue_queue = (Scsi_Cmnd *) + tmp->SCp.ptr; + tmp->SCp.ptr = NULL; + if (tmp->host_scribble) { + if (hostdata->options & OPTION_DEBUG_QUEUES) + printk ("scsi%d : moving command for target %d lun %d to start list\n", + host->host_no, tmp->target, tmp->lun); + + + to_schedule_list (host, hostdata, + (struct NCR53c7x0_cmd *) + tmp->host_scribble); + } else { + if (((tmp->result & 0xff) == 0xff) || + ((tmp->result & 0xff00) == 0xff00)) { + printk ("scsi%d : danger Will Robinson!\n", + host->host_no); + tmp->result = DID_ERROR << 16; + disable (host); + } + tmp->scsi_done(tmp); + } + done = 0; + } /* if target/lun is not busy */ + } /* if hostdata->issue_queue */ + if (!done) + restore_flags (flags); + } /* for host */ + } while (!done); + process_issue_queue_running = 0; +} + +/* + * Function : static void intr_scsi (struct Scsi_Host *host, + * struct NCR53c7x0_cmd *cmd) + * + * Purpose : handle all SCSI interrupts, indicated by the setting + * of the SIP bit in the ISTAT register. + * + * Inputs : host, cmd - host and NCR command causing the interrupt, cmd + * may be NULL. + */ + +static void +intr_scsi (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = + (struct NCR53c7x0_hostdata *) host->hostdata; + unsigned char sstat0_sist0, sist1, /* Registers */ + fatal; /* Did a fatal interrupt + occur ? */ + + int is_8xx_chip; + NCR53c7x0_local_setup(host); + + fatal = 0; + + is_8xx_chip = ((unsigned) (hostdata->chip - 800)) < 100; + if (is_8xx_chip) { + sstat0_sist0 = NCR53c7x0_read8(SIST0_REG_800); + udelay(1); + sist1 = NCR53c7x0_read8(SIST1_REG_800); + } else { + sstat0_sist0 = NCR53c7x0_read8(SSTAT0_REG); + sist1 = 0; + } + + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : SIST0 0x%0x, SIST1 0x%0x\n", host->host_no, + sstat0_sist0, sist1); + + /* 250ms selection timeout */ + if ((is_8xx_chip && (sist1 & SIST1_800_STO)) || + (!is_8xx_chip && (sstat0_sist0 & SSTAT0_700_STO))) { + fatal = 1; + if (hostdata->options & OPTION_DEBUG_INTR) { + printk ("scsi%d : Selection Timeout\n", host->host_no); + if (cmd) { + printk("scsi%d : target %d, lun %d, command ", + host->host_no, cmd->cmd->target, cmd->cmd->lun); + print_command (cmd->cmd->cmnd); + printk("scsi%d : dsp = 0x%x (virt 0x%p)\n", host->host_no, + NCR53c7x0_read32(DSP_REG), + bus_to_virt(NCR53c7x0_read32(DSP_REG))); + } else { + printk("scsi%d : no command\n", host->host_no); + } + } +/* + * XXX - question : how do we want to handle the Illegal Instruction + * interrupt, which may occur before or after the Selection Timeout + * interrupt? + */ + + if (1) { + hostdata->idle = 1; + hostdata->expecting_sto = 0; + + if (hostdata->test_running) { + hostdata->test_running = 0; + hostdata->test_completed = 3; + } else if (cmd) { + abnormal_finished(cmd, DID_BAD_TARGET << 16); + } +#if 0 + hostdata->intrs = 0; +#endif + } + } + +/* + * FIXME : in theory, we can also get a UDC when a STO occurs. + */ + if (sstat0_sist0 & SSTAT0_UDC) { + fatal = 1; + if (cmd) { + printk("scsi%d : target %d lun %d unexpected disconnect\n", + host->host_no, cmd->cmd->target, cmd->cmd->lun); + print_lots (host); + abnormal_finished(cmd, DID_ERROR << 16); + } else + printk("scsi%d : unexpected disconnect (no command)\n", + host->host_no); + + hostdata->dsp = (u32 *) hostdata->schedule; + hostdata->dsp_changed = 1; + } + + /* SCSI PARITY error */ + if (sstat0_sist0 & SSTAT0_PAR) { + fatal = 1; + if (cmd && cmd->cmd) { + printk("scsi%d : target %d lun %d parity error.\n", + host->host_no, cmd->cmd->target, cmd->cmd->lun); + abnormal_finished (cmd, DID_PARITY << 16); + } else + printk("scsi%d : parity error\n", host->host_no); + /* Should send message out, parity error */ + + /* XXX - Reduce synchronous transfer rate! */ + hostdata->dsp = hostdata->script + hostdata->E_initiator_abort / + sizeof(u32); + hostdata->dsp_changed = 1; + /* SCSI GROSS error */ + } + + if (sstat0_sist0 & SSTAT0_SGE) { + fatal = 1; + printk("scsi%d : gross error\n", host->host_no); + /* Reset SCSI offset */ + if ((hostdata->chip / 100) == 8) { + NCR53c7x0_write8 (STEST2_REG_800, STEST2_800_ROF); + } + + /* + * A SCSI gross error may occur when we have + * + * - A synchronous offset which causes the SCSI FIFO to be overwritten. + * + * - A REQ which causes the maxmimum synchronous offset programmed in + * the SXFER register to be exceeded. + * + * - A phase change with an outstanding synchronous offset. + * + * - Residual data in the synchronous data FIFO, with a transfer + * other than a synchronous receive is started.$# + */ + + + /* XXX Should deduce synchronous transfer rate! */ + hostdata->dsp = hostdata->script + hostdata->E_initiator_abort / + sizeof(u32); + hostdata->dsp_changed = 1; + /* Phase mismatch */ + } + + if (sstat0_sist0 & SSTAT0_MA) { + fatal = 1; + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : SSTAT0_MA\n", host->host_no); + intr_phase_mismatch (host, cmd); + } + +#if 0 + if (sstat0_sist0 & SIST0_800_RSL) + printk ("scsi%d : Oh no Mr. Bill!\n", host->host_no); +#endif + +/* + * If a fatal SCSI interrupt occurs, we must insure that the DMA and + * SCSI FIFOs were flushed. + */ + + if (fatal) { + if (!hostdata->dstat_valid) { + hostdata->dstat = NCR53c7x0_read8(DSTAT_REG); + hostdata->dstat_valid = 1; + } + +/* XXX - code check for 700/800 chips */ + if (!(hostdata->dstat & DSTAT_DFE)) { + printk ("scsi%d : DMA FIFO not empty\n", host->host_no); + if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) { + printk ("scsi%d: Flushing DMA FIFO\n", + host->host_no); + NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF); + while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) & + DSTAT_DFE)); + } else { + NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF); + while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF); + } + hostdata->dstat |= DSTAT_DFE; + } + } +} + +/* + * Function : static void NCR53c7x0_intr (int irq, struct pt_regs * regs) + * + * Purpose : handle NCR53c7x0 interrupts for all NCR devices sharing + * the same IRQ line. + * + * Inputs : Since we're using the SA_INTERRUPT interrupt handler + * semantics, irq indicates the interrupt which invoked + * this handler. + */ + +static void +NCR53c7x0_intr (int irq, struct pt_regs * regs) { + NCR53c7x0_local_declare(); + struct Scsi_Host *host; /* Host we are looking at */ + unsigned char istat; /* Values of interrupt regs */ + struct NCR53c7x0_hostdata *hostdata; /* host->hostdata */ + struct NCR53c7x0_cmd *cmd, /* command which halted */ + **cmd_prev_ptr; + u32 *dsa; /* DSA */ + int done = 1; /* Indicates when handler + should terminate */ + int interrupted = 0; /* This HA generated + an interrupt */ + int have_intfly; /* Don't print warning + messages when we stack + INTFLYs */ + unsigned long flags; + +#ifdef NCR_DEBUG + char buf[80]; /* Debugging sprintf buffer */ + size_t buflen; /* Length of same */ +#endif + + do { + done = 1; + for (host = first_host; host; host = host->next) + if (host->hostt == the_template && host->irq == irq) { + NCR53c7x0_local_setup(host); + + hostdata = (struct NCR53c7x0_hostdata *) host->hostdata; + hostdata->dsp_changed = 0; + interrupted = 0; + have_intfly = 0; + + do { + int is_8xx_chip; + + hostdata->dstat_valid = 0; + interrupted = 0; + /* + * Only read istat once, since reading it again will unstack + * interrupts? + */ + istat = NCR53c7x0_read8(hostdata->istat); + + /* + * INTFLY interrupts are used by the NCR53c720, NCR53c810, + * and NCR53c820 to signify completion of a command. Since + * the SCSI processor continues running, we can't just look + * at the contents of the DSA register and continue running. + */ +/* XXX - this is too big, offends my sense of aesthetics, and should + move to intr_intfly() */ + is_8xx_chip = ((unsigned) (hostdata->chip - 800)) < 100; + if ((hostdata->options & OPTION_INTFLY) && + (is_8xx_chip && (istat & ISTAT_800_INTF))) { + char search_found = 0; /* Got at least one ? */ + done = 0; + interrupted = 1; + + /* + * Clear the INTF bit by writing a one. + * This reset operation is self-clearing. + */ + NCR53c7x0_write8(hostdata->istat, istat|ISTAT_800_INTF); + + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : INTFLY\n", host->host_no); + + /* + * Traverse our list of running commands, and look + * for those with valid (non-0xff ff) status and message + * bytes encoded in the result which signify command + * completion. + */ + + + save_flags(flags); + cli(); +restart: + for (cmd_prev_ptr = (struct NCR53c7x0_cmd **) + &(hostdata->running_list), cmd = + (struct NCR53c7x0_cmd *) hostdata->running_list; cmd ; + cmd_prev_ptr = (struct NCR53c7x0_cmd **) &(cmd->next), + cmd = (struct NCR53c7x0_cmd *) cmd->next) { + Scsi_Cmnd *tmp; + + if (!cmd) { + printk("scsi%d : very weird.\n", host->host_no); + break; + } + + if (!(tmp = cmd->cmd)) { + printk("scsi%d : weird. NCR53c7x0_cmd has no Scsi_Cmnd\n", + host->host_no); + continue; + } +#if 0 + printk ("scsi%d : looking at result of 0x%x\n", + host->host_no, cmd->cmd->result); +#endif + + if (((tmp->result & 0xff) == 0xff) || + ((tmp->result & 0xff00) == 0xff00)) + continue; + + search_found = 1; + + /* Important - remove from list _before_ done is called */ + if (cmd_prev_ptr) + *cmd_prev_ptr = (struct NCR53c7x0_cmd *) cmd->next; + + --hostdata->busy[tmp->target][tmp->lun]; + cmd->next = hostdata->free; + hostdata->free = cmd; + + tmp->host_scribble = NULL; + + if (hostdata->options & OPTION_DEBUG_INTR) { + printk ("scsi%d : command complete : pid %lu, id %d,lun %d result 0x%x ", + host->host_no, tmp->pid, tmp->target, tmp->lun, tmp->result); + print_command (tmp->cmnd); + } + +#if 0 + hostdata->options &= ~OPTION_DEBUG_INTR; +#endif + tmp->scsi_done(tmp); + goto restart; + + } + restore_flags(flags); + + /* + * I think that we're stacking INTFLY interrupts; taking care of + * all the finished commands on the first one, and then getting + * worried when we see the next one. The magic with have_intfly + * should tell if this is the case.. + */ + + if (!search_found && !have_intfly) { + printk ("scsi%d : WARNING : INTFLY with no completed commands.\n", + host->host_no); + } else if (!have_intfly) { + have_intfly = 1; + run_process_issue_queue(); + } + } + + if (istat & (ISTAT_SIP|ISTAT_DIP)) { + done = 0; + interrupted = 1; + hostdata->state = STATE_HALTED; + + if (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ? + SSTAT1_REG : SSTAT2_REG) & SSTAT2_FF_MASK) + printk ("scsi%d : SCSI FIFO not empty\n", + host->host_no); + + /* + * NCR53c700 and NCR53c700-66 change the current SCSI + * process, hostdata->current, in the Linux driver so + * cmd = hostdata->current. + * + * With other chips, we must look through the commands + * executing and find the command structure which + * corresponds to the DSA register. + */ + + if (hostdata->options & OPTION_700) { + cmd = (struct NCR53c7x0_cmd *) hostdata->current; + } else { + dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG)); + for (cmd = (struct NCR53c7x0_cmd *) + hostdata->running_list; cmd && + (dsa + (hostdata->dsa_start / sizeof(u32))) != + cmd->dsa; + cmd = (struct NCR53c7x0_cmd *)(cmd->next)); + } + if (hostdata->options & OPTION_DEBUG_INTR) { + if (cmd) { + printk("scsi%d : interrupt for pid %lu, id %d, lun %d ", + host->host_no, cmd->cmd->pid, (int) cmd->cmd->target, + (int) cmd->cmd->lun); + print_command (cmd->cmd->cmnd); + } else { + printk("scsi%d : no active command\n", host->host_no); + } + } + + if (istat & ISTAT_SIP) { + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : ISTAT_SIP\n", host->host_no); + intr_scsi (host, cmd); + } + + if (istat & ISTAT_DIP) { + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : ISTAT_DIP\n", host->host_no); + intr_dma (host, cmd); + } + + if (!hostdata->dstat_valid) { + hostdata->dstat = NCR53c7x0_read8(DSTAT_REG); + hostdata->dstat_valid = 1; + } + + /* XXX - code check for 700/800 chips */ + if (!(hostdata->dstat & DSTAT_DFE)) { + printk ("scsi%d : DMA FIFO not empty\n", host->host_no); + if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) { + printk ("scsi%d: Flushing DMA FIFO\n", + host->host_no); + NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF); + while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) & + DSTAT_DFE)); + } else + { + NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF); + while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF); + } + hostdata->dstat |= DSTAT_DFE; + } + } + } while (interrupted); + + + + if (hostdata->intrs != -1) + hostdata->intrs++; +#if 0 + if (hostdata->intrs > 40) { + printk("scsi%d : too many interrupts, halting", host->host_no); + disable(host); + } +#endif + + if (!hostdata->idle && hostdata->state == STATE_HALTED) { + if (!hostdata->dsp_changed) { + hostdata->dsp = (u32 *) + bus_to_virt(NCR53c7x0_read32(DSP_REG)); + } + +#if 0 + printk("scsi%d : new dsp is 0x%lx (virt 0x%p)\n", + host->host_no, virt_to_bus(hostdata->dsp), hostdata->dsp); +#endif + + hostdata->state = STATE_RUNNING; + NCR53c7x0_write32 (DSP_REG, virt_to_bus(hostdata->dsp)); + } + } + } while (!done); +} + + +/* + * Function : static int abort_connected (struct Scsi_Host *host) + * + * Purpose : Assuming that the NCR SCSI processor is currently + * halted, break the currently established nexus. Clean + * up of the NCR53c7x0_cmd and Scsi_Cmnd structures should + * be done on receipt of the abort interrupt. + * + * Inputs : host - SCSI host + * + */ + +static int +abort_connected (struct Scsi_Host *host) { +#ifdef NEW_ABORT + NCR53c7x0_local_declare(); +#endif + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; +/* FIXME : this probably should change for production kernels; at the + least, counter sould move to a per-host structure. */ + static int counter = 5; +#ifdef NEW_ABORT + int sstat, phase, offset; + u32 *script; + NCR53c7x0_local_setup(host); +#endif + + if (--counter <= 0) { + disable(host); + return 0; + } + + printk ("scsi%d : DANGER : abort_connected() called \n", + host->host_no); + +#ifdef NEW_ABORT + +/* + * New strategy : Rather than using a generic abort routine, + * we'll specifically try to source or sink the appropriate + * amount of data for the phase we're currently in (taking into + * account the current synchronous offset) + */ + + sstat = (NCR53c8x0_read8 ((chip / 100) == 8 ? SSTAT1_REG : SSTAT2_REG); + offset = OFFSET (sstat & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT; + phase = sstat & SSTAT2_PHASE_MASK; + +/* + * SET ATN + * MOVE source_or_sink, WHEN CURRENT PHASE + * < repeat for each outstanding byte > + * JUMP send_abort_message + */ + + script = hostdata->abort_script = kmalloc ( + 8 /* instruction size */ * ( + 1 /* set ATN */ + + (!offset ? 1 : offset) /* One transfer per outstanding byte */ + + 1 /* send abort message */), + GFP_ATOMIC); + + +#else /* def NEW_ABORT */ + hostdata->dsp = hostdata->script + hostdata->E_initiator_abort / + sizeof(u32); +#endif /* def NEW_ABORT */ + hostdata->dsp_changed = 1; + +/* XXX - need to flag the command as aborted after the abort_connected + code runs + */ + return 0; +} + +/* + * Function : static int datapath_residual (Scsi_Host *host) + * + * Purpose : return residual data count of what's in the chip. + * + * Inputs : host - SCSI host + */ + +static int +datapath_residual (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + int count, synchronous, sstat; + NCR53c7x0_local_setup(host); + /* COMPAT : the 700 and 700-66 need to use DFIFO_00_BO_MASK */ + count = ((NCR53c7x0_read8 (DFIFO_REG) & DFIFO_10_BO_MASK) - + (NCR53c7x0_read32 (DBC_REG) & DFIFO_10_BO_MASK)) & DFIFO_10_BO_MASK; + synchronous = NCR53c7x0_read8 (SXFER_REG) & SXFER_MO_MASK; + /* COMPAT : DDIR is elsewhere on non-'8xx chips. */ + if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) { + /* Receive */ + if (synchronous) + count += (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ? + SSTAT1_REG : SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT; + else + if (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ? + SSTAT0_REG : SSTAT1_REG) & SSTAT1_ILF) + ++count; + } else { + /* Send */ + sstat = ((hostdata->chip / 100) == 8) ? NCR53c7x0_read8 (SSTAT0_REG) : + NCR53c7x0_read8 (SSTAT1_REG); + if (sstat & SSTAT1_OLF) + ++count; + if (synchronous && (sstat & SSTAT1_ORF)) + ++count; + } + return count; +} + +/* + * Function : static const char * sbcl_to_phase (int sbcl)_ + * + * Purpose : Convert SBCL register to user-parsable phase representation + * + * Inputs : sbcl - value of sbcl register + */ + + +static const char * +sbcl_to_phase (int sbcl) { + switch (sbcl & SBCL_PHASE_MASK) { + case SBCL_PHASE_DATAIN: + return "DATAIN"; + case SBCL_PHASE_DATAOUT: + return "DATAOUT"; + case SBCL_PHASE_MSGIN: + return "MSGIN"; + case SBCL_PHASE_MSGOUT: + return "MSGOUT"; + case SBCL_PHASE_CMDOUT: + return "CMDOUT"; + case SBCL_PHASE_STATIN: + return "STATUSIN"; + default: + return "unknown"; + } +} + +/* + * Function : static const char * sstat2_to_phase (int sstat)_ + * + * Purpose : Convert SSTAT2 register to user-parsable phase representation + * + * Inputs : sstat - value of sstat register + */ + + +static const char * +sstat2_to_phase (int sstat) { + switch (sstat & SSTAT2_PHASE_MASK) { + case SSTAT2_PHASE_DATAIN: + return "DATAIN"; + case SSTAT2_PHASE_DATAOUT: + return "DATAOUT"; + case SSTAT2_PHASE_MSGIN: + return "MSGIN"; + case SSTAT2_PHASE_MSGOUT: + return "MSGOUT"; + case SSTAT2_PHASE_CMDOUT: + return "CMDOUT"; + case SSTAT2_PHASE_STATIN: + return "STATUSIN"; + default: + return "unknown"; + } +} + +/* + * Function : static void intr_phase_mismatch (struct Scsi_Host *host, + * struct NCR53c7x0_cmd *cmd) + * + * Purpose : Handle phase mismatch interrupts + * + * Inputs : host, cmd - host and NCR command causing the interrupt, cmd + * may be NULL. + * + * Side effects : The abort_connected() routine is called or the NCR chip + * is restarted, jumping to the command_complete entry point, or + * patching the address and transfer count of the current instruction + * and calling the msg_in entry point as appropriate. + */ + +static void +intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { + NCR53c7x0_local_declare(); + u32 dbc_dcmd, *dsp, *dsp_next; + unsigned char dcmd, sbcl; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + int residual; + enum {ACTION_ABORT, ACTION_ABORT_PRINT, ACTION_CONTINUE} action = + ACTION_ABORT_PRINT; + const char *where = NULL; + NCR53c7x0_local_setup(host); + + /* + * Corrective action is based on where in the SCSI SCRIPT(tm) the error + * occurred, as well as which SCSI phase we are currently in. + */ + dsp_next = bus_to_virt(NCR53c7x0_read32(DSP_REG)); + + /* + * Fetch the current instruction, and remove the operands for easier + * interpretation. + */ + dbc_dcmd = NCR53c7x0_read32(DBC_REG); + dcmd = (dbc_dcmd & 0xff000000) >> 24; + /* + * Like other processors, the NCR adjusts the instruction pointer before + * instruction decode. Set the DSP address back to what it should + * be for this instruction based on its size (2 or 3 32 bit words). + */ + dsp = dsp_next - NCR53c7x0_insn_size(dcmd); + + + /* + * Read new SCSI phase from the SBCL lines. Since all of our code uses + * a WHEN conditional instead of an IF conditional, we don't need to + * wait for a new REQ. + */ + sbcl = NCR53c7x0_read8(SBCL_REG) & SBCL_PHASE_MASK; + + if (!cmd) { + action = ACTION_ABORT_PRINT; + where = "no current command"; + /* + * The way my SCSI SCRIPTS(tm) are architected, recoverable phase + * mismatches should only occur where we're doing a multi-byte + * BMI instruction. Specifically, this means + * + * - select messages (a SCSI-I target may ignore additional messages + * after the IDENTIFY; any target may reject a SDTR or WDTR) + * + * - command out (targets may send a message to signal an error + * condition, or go into STATUSIN after they've decided + * they don't like the command. + * + * - reply_message (targets may reject a multi-byte message in the + * middle) + * + * - data transfer routines (command completion with buffer space + * left, disconnect message, or error message) + */ + } else if (((dsp >= cmd->data_transfer_start && + dsp < cmd->data_transfer_end)) || dsp == (cmd->residual + 2)) { + if ((dcmd & (DCMD_TYPE_MASK|DCMD_BMI_OP_MASK|DCMD_BMI_INDIRECT| + DCMD_BMI_MSG|DCMD_BMI_CD)) == (DCMD_TYPE_BMI| + DCMD_BMI_OP_MOVE_I)) { + residual = datapath_residual (host); + if (hostdata->options & OPTION_DEBUG_DISCONNECT) + printk ("scsi%d : handling residual transfer (+ %d bytes from DMA FIFO)\n", + host->host_no, residual); + + /* + * The first instruction is a CALL to the alternate handler for + * this data transfer phase, so we can do calls to + * munge_msg_restart as we would if control were passed + * from normal dynamic code. + */ + if (dsp != cmd->residual + 2) { + cmd->residual[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL | + ((dcmd & DCMD_BMI_IO) ? DCMD_TCI_IO : 0)) << 24) | + DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE; + cmd->residual[1] = virt_to_bus(hostdata->script) + + ((dcmd & DCMD_BMI_IO) + ? hostdata->E_other_in : hostdata->E_other_out); + } + + /* + * The second instruction is the a data transfer block + * move instruction, reflecting the pointer and count at the + * time of the phase mismatch. + */ + cmd->residual[2] = dbc_dcmd + residual; + cmd->residual[3] = NCR53c7x0_read32(DNAD_REG) - residual; + + /* + * The third and final instruction is a jump to the instruction + * which follows the instruction which had to be 'split' + */ + if (dsp != cmd->residual + 2) { + cmd->residual[4] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) + << 24) | DBC_TCI_TRUE; + cmd->residual[5] = virt_to_bus(dsp_next); + } + + /* + * For the sake of simplicity, transfer control to the + * conditional CALL at the start of the residual buffer. + */ + hostdata->dsp = cmd->residual; + hostdata->dsp_changed = 1; + action = ACTION_CONTINUE; + } else { + where = "non-BMI dynamic DSA code"; + action = ACTION_ABORT_PRINT; + } + } else if (dsp == (hostdata->script + hostdata->E_select_msgout / 4)) { + /* Release ATN */ + NCR53c7x0_write8 (SOCL_REG, 0); + switch (sbcl) { + /* + * Some devices (SQ555 come to mind) grab the IDENTIFY message + * sent on selection, and decide to go into COMMAND OUT phase + * rather than accepting the rest of the messages or rejecting + * them. Handle these devices gracefully. + */ + case SBCL_PHASE_CMDOUT: + hostdata->dsp = dsp + 2 /* two _words_ */; + hostdata->dsp_changed = 1; + printk ("scsi%d : target %d ignored SDTR and went into COMMAND OUT\n", + host->host_no, cmd->cmd->target); + cmd->flags &= ~CMD_FLAG_SDTR; + action = ACTION_CONTINUE; + break; + case SBCL_PHASE_MSGIN: + hostdata->dsp = hostdata->script + hostdata->E_msg_in / + sizeof(u32); + hostdata->dsp_changed = 1; + action = ACTION_CONTINUE; + break; + default: + where="select message out"; + action = ACTION_ABORT_PRINT; + } + /* + * Some SCSI devices will interpret a command as they read the bytes + * off the SCSI bus, and may decide that the command is Bogus before + * they've read the entire commad off the bus. + */ + } else if (dsp == hostdata->script + hostdata->E_cmdout_cmdout / sizeof + (u32)) { + hostdata->dsp = hostdata->script + hostdata->E_data_transfer / + sizeof (u32); + hostdata->dsp_changed = 1; + action = ACTION_CONTINUE; + /* FIXME : we need to handle message reject, etc. within msg_respond. */ +#ifdef notyet + } else if (dsp == hostdata->script + hostdata->E_reply_message) { + switch (sbcl) { + /* Any other phase mismatches abort the currently executing command. */ +#endif + } else { + where = "unknown location"; + action = ACTION_ABORT_PRINT; + } + + /* Flush DMA FIFO */ + if (!hostdata->dstat_valid) { + hostdata->dstat = NCR53c7x0_read8(DSTAT_REG); + hostdata->dstat_valid = 1; + } + if (!(hostdata->dstat & DSTAT_DFE)) { + if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) { + printk ("scsi%d: Flushing DMA FIFO\n", + host->host_no); + NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF); + /* FIXME : what about stacked DMA interrupts? */ + while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) & + DSTAT_DFE)); + } else { + NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF); + while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF); + } + hostdata->dstat |= DSTAT_DFE; + } + + switch (action) { + case ACTION_ABORT_PRINT: + printk("scsi%d : %s : unexpected phase %s.\n", + host->host_no, where ? where : "unknown location", + sbcl_to_phase(sbcl)); + print_lots (host); + /* Fall through to ACTION_ABORT */ + case ACTION_ABORT: + abort_connected (host); + break; + case ACTION_CONTINUE: + break; + } + +#if 0 + if (hostdata->dsp_changed) { + printk("scsi%d: new dsp 0x%p\n", host->host_no, hostdata->dsp); + print_insn (host, hostdata->dsp, "", 1); + } +#endif + +} + +/* + * Function : static void intr_bf (struct Scsi_Host *host, + * struct NCR53c7x0_cmd *cmd) + * + * Purpose : handle BUS FAULT interrupts + * + * Inputs : host, cmd - host and NCR command causing the interrupt, cmd + * may be NULL. + */ + +static void +intr_bf (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + u32 *dsp, + *next_dsp, /* Current dsp */ + *dsa, + dbc_dcmd; /* DCMD (high eight bits) + DBC */ + unsigned short pci_status; + int tmp; + unsigned long flags; + char *reason = NULL; + /* Default behavior is for a silent error, with a retry until we've + exhausted retries. */ + enum {MAYBE, ALWAYS, NEVER} retry = MAYBE; + int report = 0; + NCR53c7x0_local_setup(host); + + dbc_dcmd = NCR53c7x0_read32 (DBC_REG); + next_dsp = bus_to_virt (NCR53c7x0_read32(DSP_REG)); + dsp = next_dsp - NCR53c7x0_insn_size ((dbc_dcmd >> 24) & 0xff); +/* FIXME - check chip type */ + dsa = bus_to_virt (NCR53c7x0_read32(DSA_REG)); + + /* + * Bus faults can be caused by either a Bad Address or + * Target Abort. We should check the Received Target Abort + * bit of the PCI status register and Master Abort Bit. + * + * - Master Abort bit indicates that no device claimed + * the address with DEVSEL within five clocks + * + * - Target Abort bit indicates that a target claimed it, + * but changed its mind once it saw the byte enables. + * + */ + + if ((hostdata->chip / 100) == 8) { + save_flags (flags); + cli(); + tmp = pcibios_read_config_word (hostdata->pci_bus, + hostdata->pci_device_fn, PCI_STATUS, &pci_status); + restore_flags (flags); + if (tmp == PCIBIOS_SUCCESSFUL) { + if (pci_status & PCI_STATUS_REC_TARGET_ABORT) { + reason = "PCI target abort"; + pci_status &= ~PCI_STATUS_REC_TARGET_ABORT; + } else if (pci_status & PCI_STATUS_REC_MASTER_ABORT) { + reason = "No device asserted PCI DEVSEL within five bus clocks"; + pci_status &= ~PCI_STATUS_REC_MASTER_ABORT; + } else if (pci_status & PCI_STATUS_PARITY) { + report = 1; + pci_status &= ~PCI_STATUS_PARITY; + } + } else { + printk ("scsi%d : couldn't read status register : %s\n", + host->host_no, pcibios_strerror (tmp)); + retry = NEVER; + } + } + +#ifndef notyet + report = 1; +#endif + if (report && reason) { + printk(KERN_ALERT "scsi%d : BUS FAULT reason = %s\n", + host->host_no, reason ? reason : "unknown"); + print_lots (host); + } + +#ifndef notyet + retry = NEVER; +#endif + + /* + * TODO : we should attempt to recover from any spurious bus + * faults. After X retries, we should figure that things are + * sufficiently wedged, and call NCR53c7xx_reset. + * + * This code should only get executed once we've decided that we + * cannot retry. + */ + + if (retry == NEVER) { + printk(KERN_ALERT " mail drew@PoohSticks.ORG\n"); + FATAL (host); + } +} + +/* + * Function : static void intr_dma (struct Scsi_Host *host, + * struct NCR53c7x0_cmd *cmd) + * + * Purpose : handle all DMA interrupts, indicated by the setting + * of the DIP bit in the ISTAT register. + * + * Inputs : host, cmd - host and NCR command causing the interrupt, cmd + * may be NULL. + */ + +static void +intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned char dstat; /* DSTAT */ + u32 *dsp, + *next_dsp, /* Current dsp */ + *dsa, + dbc_dcmd; /* DCMD (high eight bits) + DBC */ + int tmp; + unsigned long flags; + NCR53c7x0_local_setup(host); + + if (!hostdata->dstat_valid) { + hostdata->dstat = NCR53c7x0_read8(DSTAT_REG); + hostdata->dstat_valid = 1; + } + + dstat = hostdata->dstat; + + if (hostdata->options & OPTION_DEBUG_INTR) + printk("scsi%d : DSTAT=0x%x\n", host->host_no, (int) dstat); + + dbc_dcmd = NCR53c7x0_read32 (DBC_REG); + next_dsp = bus_to_virt(NCR53c7x0_read32(DSP_REG)); + dsp = next_dsp - NCR53c7x0_insn_size ((dbc_dcmd >> 24) & 0xff); +/* XXX - check chip type */ + dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG)); + + /* + * DSTAT_ABRT is the aborted interrupt. This is set whenever the + * SCSI chip is aborted. + * + * With NCR53c700 and NCR53c700-66 style chips, we should only + * get this when the chip is currently running the accept + * reselect/select code and we have set the abort bit in the + * ISTAT register. + * + */ + + if (dstat & DSTAT_ABRT) { +#if 0 + /* XXX - add code here to deal with normal abort */ + if ((hostdata->options & OPTION_700) && (hostdata->state == + STATE_ABORTING)) { + } else +#endif + { + printk(KERN_ALERT "scsi%d : unexpected abort interrupt at\n" + " ", host->host_no); + print_insn (host, dsp, KERN_ALERT "s ", 1); + FATAL (host); + } + } + + /* + * DSTAT_SSI is the single step interrupt. Should be generated + * whenever we have single stepped or are tracing. + */ + + if (dstat & DSTAT_SSI) { + if (hostdata->options & OPTION_DEBUG_TRACE) { + } else if (hostdata->options & OPTION_DEBUG_SINGLE) { + print_insn (host, dsp, "s ", 0); + save_flags(flags); + cli(); +/* XXX - should we do this, or can we get away with writing dsp? */ + + NCR53c7x0_write8 (DCNTL_REG, (NCR53c7x0_read8(DCNTL_REG) & + ~DCNTL_SSM) | DCNTL_STD); + restore_flags(flags); + } else { + printk(KERN_ALERT "scsi%d : unexpected single step interrupt at\n" + " ", host->host_no); + print_insn (host, dsp, KERN_ALERT "", 1); + printk(KERN_ALERT " mail drew@PoohSticks.ORG\n"); + FATAL (host); + } + } + + /* + * DSTAT_IID / DSTAT_OPC (same bit, same meaning, only the name + * is different) is generated whenever an illegal instruction is + * encountered. + * + * XXX - we may want to emulate INTFLY here, so we can use + * the same SCSI SCRIPT (tm) for NCR53c710 through NCR53c810 + * chips. + */ + + if (dstat & DSTAT_OPC) { + /* + * Ascertain if this IID interrupts occurred before or after a STO + * interrupt. Since the interrupt handling code now leaves + * DSP unmodified until _after_ all stacked interrupts have been + * processed, reading the DSP returns the original DSP register. + * This means that if dsp lies between the select code, and + * message out following the selection code (where the IID interrupt + * would have to have occurred by due to the implicit wait for REQ), + * we have an IID interrupt resulting from a STO condition and + * can ignore it. + */ + + if (((dsp >= (hostdata->script + hostdata->E_select / sizeof(u32))) && + (dsp <= (hostdata->script + hostdata->E_select_msgout / + sizeof(u32) + 8))) || (hostdata->test_running == 2)) { + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : ignoring DSTAT_IID for SSTAT_STO\n", + host->host_no); + if (hostdata->expecting_iid) { + hostdata->expecting_iid = 0; + hostdata->idle = 1; + if (hostdata->test_running == 2) { + hostdata->test_running = 0; + hostdata->test_completed = 3; + } else if (cmd) + abnormal_finished (cmd, DID_BAD_TARGET << 16); + } else { + hostdata->expecting_sto = 1; + } + /* + * We can't guarantee we'll be able to execute the WAIT DISCONNECT + * instruction within the 3.4us of bus free and arbitration delay + * that a target can RESELECT in and assert REQ after we've dropped + * ACK. If this happens, we'll get an illegal instruction interrupt. + * Doing away with the WAIT DISCONNECT instructions broke everything, + * so instead I'll settle for moving one WAIT DISCONNECT a few + * instructions closer to the CLEAR ACK before it to minimize the + * chances of this happening, and handle it if it occurs anyway. + * + * Simply continue with what we were doing, and control should + * be transfered to the schedule routine which will ultimately + * pass control onto the reselection or selection (not yet) + * code. + */ + } else if (dbc_dcmd == 0x48000000 && (NCR53c7x0_read8 (SBCL_REG) & + SBCL_REQ)) { + if (!(hostdata->options & OPTION_NO_PRINT_RACE)) + { + printk("scsi%d: REQ before WAIT DISCONNECT IID\n", + host->host_no); + hostdata->options |= OPTION_NO_PRINT_RACE; + } + } else { + printk(KERN_ALERT "scsi%d : illegal instruction\n", host->host_no); + print_lots (host); + printk(KERN_ALERT " mail drew@PoohSticks.ORG with ALL\n" + " boot messages and diagnostic output\n"); + FATAL (host); + } + } + + /* + * DSTAT_BF are bus fault errors + */ + + if (dstat & DSTAT_800_BF) { + intr_bf (host, cmd); + } + + + /* + * DSTAT_SIR interrupts are generated by the execution of + * the INT instruction. Since the exact values available + * are determined entirely by the SCSI script running, + * and are local to a particular script, a unique handler + * is called for each script. + */ + + if (dstat & DSTAT_SIR) { + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : DSTAT_SIR\n", host->host_no); + switch ((tmp = hostdata->dstat_sir_intr (host, cmd))) { + case SPECIFIC_INT_NOTHING: + case SPECIFIC_INT_RESTART: + break; + case SPECIFIC_INT_ABORT: + abort_connected(host); + break; + case SPECIFIC_INT_PANIC: + printk(KERN_ALERT "scsi%d : failure at ", host->host_no); + print_insn (host, dsp, KERN_ALERT "", 1); + printk(KERN_ALERT " dstat_sir_intr() returned SPECIFIC_INT_PANIC\n"); + FATAL (host); + break; + case SPECIFIC_INT_BREAK: + intr_break (host, cmd); + break; + default: + printk(KERN_ALERT "scsi%d : failure at ", host->host_no); + print_insn (host, dsp, KERN_ALERT "", 1); + printk(KERN_ALERT" dstat_sir_intr() returned unknown value %d\n", + tmp); + FATAL (host); + } + } + + if ((hostdata->chip / 100) == 8 && (dstat & DSTAT_800_MDPE)) { + printk(KERN_ALERT "scsi%d : Master Data Parity Error\n", + host->host_no); + FATAL (host); + } +} + +/* + * Function : static int print_insn (struct Scsi_Host *host, + * u32 *insn, int kernel) + * + * Purpose : print numeric representation of the instruction pointed + * to by insn to the debugging or kernel message buffer + * as appropriate. + * + * If desired, a user level program can interpret this + * information. + * + * Inputs : host, insn - host, pointer to instruction, prefix - + * string to prepend, kernel - use printk instead of debugging buffer. + * + * Returns : size, in u32s, of instruction printed. + */ + +/* + * FIXME: should change kernel parameter so that it takes an ENUM + * specifying severity - either KERN_ALERT or KERN_PANIC so + * all panic messages are output with the same severity. + */ + +static int +print_insn (struct Scsi_Host *host, const u32 *insn, + const char *prefix, int kernel) { + char buf[160], /* Temporary buffer and pointer. ICKY + arbitrary length. */ + + + *tmp; + unsigned char dcmd; /* dcmd register for *insn */ + int size; + + /* + * Check to see if the instruction pointer is not bogus before + * indirecting through it; avoiding red-zone at start of + * memory. + * + * FIXME: icky magic needs to happen here on non-intel boxes which + * don't have kernel memory mapped in like this. Might be reasonable + * to use vverify()? + */ + + if (MAP_NR(insn) < 1 || MAP_NR(insn + 8) > MAP_NR(high_memory) || + ((((dcmd = (insn[0] >> 24) & 0xff) & DCMD_TYPE_MMI) == DCMD_TYPE_MMI) && + MAP_NR(insn + 12) > MAP_NR(high_memory))) { + size = 0; + sprintf (buf, "%s%p: address out of range\n", + prefix, insn); + } else { +/* + * FIXME : (void *) cast in virt_to_bus should be unecessary, because + * it should take const void * as argument. + */ + sprintf(buf, "%s0x%lx (virt 0x%p) : 0x%08x 0x%08x (virt 0x%p)", + (prefix ? prefix : ""), virt_to_bus((void *) insn), insn, + insn[0], insn[1], bus_to_virt (insn[1])); + tmp = buf + strlen(buf); + if ((dcmd & DCMD_TYPE_MASK) == DCMD_TYPE_MMI) { + sprintf (tmp, " 0x%08x (virt 0x%p)\n", insn[2], + bus_to_virt(insn[2])); + size = 3; + } else { + sprintf (tmp, "\n"); + size = 2; + } + } + + if (kernel) + printk ("%s", buf); +#ifdef NCR_DEBUG + else { + size_t len = strlen(buf); + debugger_kernel_write(host, buf, len); + } +#endif + return size; +} + +/* + * Function : static const char *ncr_state (int state) + * + * Purpose : convert state (probably from hostdata->state) to a string + * + * Inputs : state + * + * Returns : char * representation of state, "unknown" on error. + */ + +static const char * +ncr_state (int state) { + switch (state) { + case STATE_HALTED: return "halted"; + case STATE_WAITING: return "waiting"; + case STATE_RUNNING: return "running"; + case STATE_ABORTING: return "aborting"; + case STATE_DISABLED: return "disabled"; + default: return "unknown"; + } +} + +/* + * Function : int NCR53c7xx_abort (Scsi_Cmnd *cmd) + * + * Purpose : Abort an errant SCSI command, doing all necessary + * cleanup of the issue_queue, running_list, shared Linux/NCR + * dsa issue and reconnect queues. + * + * Inputs : cmd - command to abort, code - entire result field + * + * Returns : 0 on success, -1 on failure. + */ + +int +NCR53c7xx_abort (Scsi_Cmnd *cmd) { + NCR53c7x0_local_declare(); + struct Scsi_Host *host = cmd->host; + struct NCR53c7x0_hostdata *hostdata = host ? (struct NCR53c7x0_hostdata *) + host->hostdata : NULL; + unsigned long flags; + struct NCR53c7x0_cmd *curr, **prev; + Scsi_Cmnd *me, **last; +#if 0 + static long cache_pid = -1; +#endif + + + if (!host) { + printk ("Bogus SCSI command pid %ld; no host structure\n", + cmd->pid); + return SCSI_ABORT_ERROR; + } else if (!hostdata) { + printk ("Bogus SCSI host %d; no hostdata\n", host->host_no); + return SCSI_ABORT_ERROR; + } + NCR53c7x0_local_setup(host); + +/* + * CHECK : I don't think that reading ISTAT will unstack any interrupts, + * since we need to write the INTF bit to clear it, and SCSI/DMA + * interrupts don't clear until we read SSTAT/SIST and DSTAT registers. + * + * See that this is the case. + * + * I suspect that several of our failures may be coming from a new fatal + * interrupt (possibly due to a phase mismatch) happening after we've left + * the interrupt handler, but before the PIC has had the interrupt condition + * cleared. + */ + + if (NCR53c7x0_read8(hostdata->istat) & + (ISTAT_DIP|ISTAT_SIP| + (hostdata->chip / 100 == 8 ? ISTAT_800_INTF : 0))) { + printk ("scsi%d : dropped interrupt for command %ld\n", host->host_no, + cmd->pid); + NCR53c7x0_intr (host->irq, NULL); + return SCSI_ABORT_BUSY; + } + + save_flags(flags); + cli(); +#if 0 + if (cache_pid == cmd->pid) + panic ("scsi%d : bloody fetus %d\n", host->host_no, cmd->pid); + else + cache_pid = cmd->pid; +#endif + + +/* + * The command could be hiding in the issue_queue. This would be very + * nice, as commands can't be moved from the high level driver's issue queue + * into the shared queue until an interrupt routine is serviced, and this + * moving is atomic. + * + * If this is the case, we don't have to worry about anything - we simply + * pull the command out of the old queue, and call it aborted. + */ + + for (me = (Scsi_Cmnd *) hostdata->issue_queue, + last = (Scsi_Cmnd **) &(hostdata->issue_queue); + me && me != cmd; last = (Scsi_Cmnd **)&(me->SCp.ptr), + me = (Scsi_Cmnd *)me->SCp.ptr); + + if (me) { + *last = (Scsi_Cmnd *) me->SCp.ptr; + if (me->host_scribble) { + ((struct NCR53c7x0_cmd *)me->host_scribble)->next = hostdata->free; + hostdata->free = (struct NCR53c7x0_cmd *) me->host_scribble; + me->host_scribble = NULL; + } + cmd->result = DID_ABORT << 16; + cmd->scsi_done(cmd); + printk ("scsi%d : found command %ld in Linux issue queue\n", + host->host_no, me->pid); + restore_flags(flags); + run_process_issue_queue(); + return SCSI_ABORT_SUCCESS; + } + +/* + * That failing, the command could be in our list of already executing + * commands. If this is the case, drastic measures are called for. + */ + + for (curr = (struct NCR53c7x0_cmd *) hostdata->running_list, + prev = (struct NCR53c7x0_cmd **) &(hostdata->running_list); + curr && curr->cmd != cmd; prev = (struct NCR53c7x0_cmd **) + &(curr->next), curr = (struct NCR53c7x0_cmd *) curr->next); + + if (curr) { + if ((cmd->result & 0xff) != 0xff && (cmd->result & 0xff00) != 0xff00) { + if (prev) + *prev = (struct NCR53c7x0_cmd *) curr->next; + curr->next = (struct NCR53c7x0_cmd *) hostdata->free; + cmd->host_scribble = NULL; + hostdata->free = curr; + cmd->scsi_done(cmd); + printk ("scsi%d : found finished command %ld in running list\n", + host->host_no, cmd->pid); + restore_flags(flags); + return SCSI_ABORT_NOT_RUNNING; + } else { + printk ("scsi%d : DANGER : command running, can not abort.\n", + cmd->host->host_no); + restore_flags(flags); + return SCSI_ABORT_BUSY; + } + } + +/* + * And if we couldn't find it in any of our queues, it must have been + * a dropped interrupt. + */ + + curr = (struct NCR53c7x0_cmd *) cmd->host_scribble; + if (curr) { + curr->next = hostdata->free; + hostdata->free = curr; + cmd->host_scribble = NULL; + } + + if (((cmd->result & 0xff00) == 0xff00) || + ((cmd->result & 0xff) == 0xff)) { + printk ("scsi%d : did this command ever run?\n", host->host_no); + cmd->result = DID_ABORT << 16; + } else { + printk ("scsi%d : probably lost INTFLY, normal completion\n", + host->host_no); +/* + * FIXME : We need to add an additional flag which indicates if a + * command was ever counted as BUSY, so if we end up here we can + * decrement the busy count if and only if it is necessary. + */ + --hostdata->busy[cmd->target][cmd->lun]; + } + restore_flags(flags); + cmd->scsi_done(cmd); + +/* + * We need to run process_issue_queue since termination of this command + * may allow another queued command to execute first? + */ + return SCSI_ABORT_NOT_RUNNING; +} + +/* + * Function : int NCR53c7xx_reset (Scsi_Cmnd *cmd) + * + * Purpose : perform a hard reset of the SCSI bus and NCR + * chip. + * + * Inputs : cmd - command which caused the SCSI RESET + * + * Returns : 0 on success. + */ + +int +NCR53c7xx_reset (Scsi_Cmnd *cmd) { + NCR53c7x0_local_declare(); + unsigned long flags; + int found = 0; + struct NCR53c7x0_cmd * c; + Scsi_Cmnd *tmp; + /* + * When we call scsi_done(), it's going to wake up anything sleeping on the + * resources which were in use by the aborted commands, and we'll start to + * get new commands. + * + * We can't let this happen until after we've re-initialized the driver + * structures, and can't reinitilize those structures until after we've + * dealt with their contents. + * + * So, we need to find all of the commands which were running, stick + * them on a linked list of completed commands (we'll use the host_scribble + * pointer), do our reinitialization, and then call the done function for + * each command. + */ + Scsi_Cmnd *nuke_list = NULL; + struct Scsi_Host *host = cmd->host; + struct NCR53c7x0_hostdata *hostdata = + (struct NCR53c7x0_hostdata *) host->hostdata; + + NCR53c7x0_local_setup(host); + save_flags(flags); + cli(); + ncr_halt (host); + print_lots (host); + dump_events (host, 30); + ncr_scsi_reset (host); + for (tmp = nuke_list = return_outstanding_commands (host, 1 /* free */, + 0 /* issue */ ); tmp; tmp = (Scsi_Cmnd *) tmp->SCp.buffer) + if (tmp == cmd) { + found = 1; + break; + } + + /* + * If we didn't find the command which caused this reset in our running + * list, then we've lost it. See that it terminates normally anyway. + */ + if (!found) { + c = (struct NCR53c7x0_cmd *) cmd->host_scribble; + if (c) { + cmd->host_scribble = NULL; + c->next = hostdata->free; + hostdata->free = c; + } else + printk ("scsi%d: lost command %ld\n", host->host_no, cmd->pid); + cmd->SCp.buffer = (struct scatterlist *) nuke_list; + nuke_list = cmd; + } + + NCR53c7x0_driver_init (host); + hostdata->soft_reset (host); + if (hostdata->resets == 0) + disable(host); + else if (hostdata->resets != -1) + --hostdata->resets; + sti(); + for (; nuke_list; nuke_list = tmp) { + tmp = (Scsi_Cmnd *) nuke_list->SCp.buffer; + nuke_list->result = DID_RESET << 16; + nuke_list->scsi_done (nuke_list); + } + restore_flags(flags); + return SCSI_RESET_SUCCESS; +} + +/* + * The NCR SDMS bios follows Annex A of the SCSI-CAM draft, and + * therefore shares the scsicam_bios_param function. + */ + +/* + * Function : int insn_to_offset (Scsi_Cmnd *cmd, u32 *insn) + * + * Purpose : convert instructions stored at NCR pointer into data + * pointer offset. + * + * Inputs : cmd - SCSI command; insn - pointer to instruction. Either current + * DSP, or saved data pointer. + * + * Returns : offset on success, -1 on failure. + */ + + +static int +insn_to_offset (Scsi_Cmnd *cmd, u32 *insn) { + struct NCR53c7x0_hostdata *hostdata = + (struct NCR53c7x0_hostdata *) cmd->host->hostdata; + struct NCR53c7x0_cmd *ncmd = + (struct NCR53c7x0_cmd *) cmd->host_scribble; + int offset = 0, buffers; + struct scatterlist *segment; + char *ptr; + int found = 0; + +/* + * With the current code implementation, if the insn is inside dynamically + * generated code, the data pointer will be the instruction preceeding + * the next transfer segment. + */ + + if (!check_address ((unsigned long) ncmd, sizeof (struct NCR53c7x0_cmd)) && + ((insn >= ncmd->data_transfer_start && + insn < ncmd->data_transfer_end) || + (insn >= ncmd->residual && + insn < (ncmd->residual + + sizeof(ncmd->residual))))) { + ptr = bus_to_virt(insn[3]); + + if ((buffers = cmd->use_sg)) { + for (offset = 0, + segment = (struct scatterlist *) cmd->buffer; + buffers && !((found = ((ptr >= segment->address) && + (ptr < (segment->address + segment->length))))); + --buffers, offset += segment->length, ++segment) +#if 0 + printk("scsi%d: comparing 0x%p to 0x%p\n", + cmd->host->host_no, saved, segment->address); +#else + ; +#endif + offset += ptr - segment->address; + } else { + found = 1; + offset = ptr - (char *) (cmd->request_buffer); + } + } else if ((insn >= hostdata->script + + hostdata->E_data_transfer / sizeof(u32)) && + (insn <= hostdata->script + + hostdata->E_end_data_transfer / sizeof(u32))) { + found = 1; + offset = 0; + } + return found ? offset : -1; +} + + + +/* + * Function : void print_progress (Scsi_Cmnd *cmd) + * + * Purpose : print the current location of the saved data pointer + * + * Inputs : cmd - command we are interested in + * + */ + +static void +print_progress (Scsi_Cmnd *cmd) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_cmd *ncmd = + (struct NCR53c7x0_cmd *) cmd->host_scribble; + int offset, i; + char *where; + u32 *ptr; + NCR53c7x0_local_setup (cmd->host); + for (i = 0; i < 2; ++i) { + if (check_address ((unsigned long) ncmd, + sizeof (struct NCR53c7x0_cmd)) == -1) + continue; + if (!i) { + where = "saved"; + ptr = bus_to_virt(ncmd->saved_data_pointer); + } else { + where = "active"; + ptr = bus_to_virt (NCR53c7x0_read32 (DSP_REG) - + NCR53c7x0_insn_size (NCR53c7x0_read8 (DCMD_REG)) * + sizeof(u32)); + } + offset = insn_to_offset (cmd, ptr); + + if (offset != -1) + printk ("scsi%d : %s data pointer at offset %d\n", + cmd->host->host_no, where, offset); + else { + int size; + printk ("scsi%d : can't determine %s data pointer offset\n", + cmd->host->host_no, where); + if (ncmd) { + size = print_insn (cmd->host, + bus_to_virt(ncmd->saved_data_pointer), "", 1); + print_insn (cmd->host, + bus_to_virt(ncmd->saved_data_pointer) + size * sizeof(u32), + "", 1); + } + } + } +} + + +static void +print_dsa (struct Scsi_Host *host, u32 *dsa, const char *prefix) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + int i, len; + char *ptr; + Scsi_Cmnd *cmd; + + if (check_address ((unsigned long) dsa, hostdata->dsa_end - + hostdata->dsa_start) == -1) { + printk("scsi%d : bad dsa virt 0x%p\n", host->host_no, dsa); + return; + } + printk("%sscsi%d : dsa at phys 0x%lx (virt 0x%p)\n" + " + %d : dsa_msgout length = %u, data = 0x%x (virt 0x%p)\n" , + prefix ? prefix : "", + host->host_no, virt_to_bus (dsa), dsa, hostdata->dsa_msgout, + dsa[hostdata->dsa_msgout / sizeof(u32)], + dsa[hostdata->dsa_msgout / sizeof(u32) + 1], + bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1])); + + /* + * Only print messages if they're sane in length so we don't + * blow the kernel printk buffer on something which won't buy us + * anything. + */ + + if (dsa[hostdata->dsa_msgout / sizeof(u32)] < + sizeof (hostdata->free->select)) + for (i = dsa[hostdata->dsa_msgout / sizeof(u32)], + ptr = bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1]); + i > 0 && !check_address ((unsigned long) ptr, 1); + ptr += len, i -= len) { + printk(" "); + len = print_msg (ptr); + printk("\n"); + if (!len) + break; + } + + printk(" + %d : select_indirect = 0x%x\n", + hostdata->dsa_select, dsa[hostdata->dsa_select / sizeof(u32)]); + cmd = (Scsi_Cmnd *) bus_to_virt(dsa[hostdata->dsa_cmnd / sizeof(u32)]); + printk(" + %d : dsa_cmnd = 0x%x ", hostdata->dsa_cmnd, + (u32) virt_to_bus(cmd)); + if (cmd) { + printk(" result = 0x%x, target = %d, lun = %d, cmd = ", + cmd->result, cmd->target, cmd->lun); + print_command(cmd->cmnd); + } else + printk("\n"); + printk(" + %d : dsa_next = 0x%x\n", hostdata->dsa_next, + dsa[hostdata->dsa_next / sizeof(u32)]); + if (cmd) { + printk("scsi%d target %d : sxfer_sanity = 0x%x, scntl3_sanity = 0x%x\n" + " script : ", + host->host_no, cmd->target, + hostdata->sync[cmd->target].sxfer_sanity, + hostdata->sync[cmd->target].scntl3_sanity); + for (i = 0; i < (sizeof(hostdata->sync[cmd->target].script) / 4); ++i) + printk ("0x%x ", hostdata->sync[cmd->target].script[i]); + printk ("\n"); + print_progress (cmd); + } +} +/* + * Function : void print_queues (Scsi_Host *host) + * + * Purpose : print the contents of the NCR issue and reconnect queues + * + * Inputs : host - SCSI host we are interested in + * + */ + +static void +print_queues (struct Scsi_Host *host) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + u32 *dsa, *next_dsa; + volatile u32 *current; + int left; + Scsi_Cmnd *cmd, *next_cmd; + unsigned long flags; + + printk ("scsi%d : issue queue\n", host->host_no); + + for (left = host->can_queue, cmd = (Scsi_Cmnd *) hostdata->issue_queue; + left >= 0 && cmd; + cmd = next_cmd) { + next_cmd = (Scsi_Cmnd *) cmd->SCp.ptr; + save_flags(flags); + cli(); + if (cmd->host_scribble) { + if (check_address ((unsigned long) (cmd->host_scribble), + sizeof (cmd->host_scribble)) == -1) + printk ("scsi%d: scsi pid %ld bad pointer to NCR53c7x0_cmd\n", + host->host_no, cmd->pid); + /* print_dsa does sanity check on address, no need to check */ + else + print_dsa (host, ((struct NCR53c7x0_cmd *) cmd->host_scribble) + -> dsa, ""); + } else + printk ("scsi%d : scsi pid %ld for target %d lun %d has no NCR53c7x0_cmd\n", + host->host_no, cmd->pid, cmd->target, cmd->lun); + restore_flags(flags); + } + + if (left <= 0) { + printk ("scsi%d : loop detected in issue queue\n", + host->host_no); + } + + /* + * Traverse the NCR reconnect and start DSA structures, printing out + * each element until we hit the end or detect a loop. Currently, + * the reconnect structure is a linked list; and the start structure + * is an array. Eventually, the reconnect structure will become a + * list as well, since this simplifies the code. + */ + + printk ("scsi%d : schedule dsa array :\n", host->host_no); + for (left = host->can_queue, current = hostdata->schedule; + left > 0; current += 2, --left) + if (current[0] != hostdata->NOP_insn) +/* FIXME : convert pointer to dsa_begin to pointer to dsa. */ + print_dsa (host, bus_to_virt (current[1] - + (hostdata->E_dsa_code_begin - + hostdata->E_dsa_code_template)), ""); + printk ("scsi%d : end schedule dsa array\n", host->host_no); + + printk ("scsi%d : reconnect_dsa_head :\n", host->host_no); + + for (left = host->can_queue, + dsa = bus_to_virt (hostdata->reconnect_dsa_head); + left >= 0 && dsa; + dsa = next_dsa) { + save_flags (flags); + cli(); + if (check_address ((unsigned long) dsa, sizeof(dsa)) == -1) { + printk ("scsi%d: bad DSA pointer 0x%p", host->host_no, + dsa); + next_dsa = NULL; + } + else + { + next_dsa = bus_to_virt(dsa[hostdata->dsa_next / sizeof(u32)]); + print_dsa (host, dsa, ""); + } + restore_flags(flags); + } + printk ("scsi%d : end reconnect_dsa_head\n", host->host_no); + if (left < 0) + printk("scsi%d: possible loop in ncr reconnect list\n", + host->host_no); +} + +static void +print_lots (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = + (struct NCR53c7x0_hostdata *) host->hostdata; + u32 *dsp_next, *dsp, *dsa, dbc_dcmd; + unsigned char dcmd, sbcl; + int i, size; + NCR53c7x0_local_setup(host); + + if ((dsp_next = bus_to_virt(NCR53c7x0_read32 (DSP_REG)))) { + dbc_dcmd = NCR53c7x0_read32(DBC_REG); + dcmd = (dbc_dcmd & 0xff000000) >> 24; + dsp = dsp_next - NCR53c7x0_insn_size(dcmd); + dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG)); + sbcl = NCR53c7x0_read8 (SBCL_REG); + + + printk ("scsi%d : DCMD|DBC=0x%x, DNAD=0x%x (virt 0x%p)\n" + " DSA=0x%lx (virt 0x%p)\n" + " DSPS=0x%x, TEMP=0x%x (virt 0x%p), DMODE=0x%x\n" + " SXFER=0x%x, SCNTL3=0x%x\n" + " %s%s%sphase=%s, %d bytes in SCSI FIFO\n" + " STEST0=0x%x\n", + host->host_no, dbc_dcmd, NCR53c7x0_read32(DNAD_REG), + bus_to_virt(NCR53c7x0_read32(DNAD_REG)), + virt_to_bus(dsa), dsa, + NCR53c7x0_read32(DSPS_REG), NCR53c7x0_read32(TEMP_REG), + bus_to_virt (NCR53c7x0_read32(TEMP_REG)), + (int) NCR53c7x0_read8(hostdata->dmode), + (int) NCR53c7x0_read8(SXFER_REG), + (int) NCR53c7x0_read8(SCNTL3_REG_800), + (sbcl & SBCL_BSY) ? "BSY " : "", + (sbcl & SBCL_SEL) ? "SEL " : "", + (sbcl & SBCL_REQ) ? "REQ " : "", + sstat2_to_phase(NCR53c7x0_read8 (((hostdata->chip / 100) == 8) ? + SSTAT1_REG : SSTAT2_REG)), + (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ? + SSTAT1_REG : SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT, + NCR53c7x0_read8 (STEST0_REG_800)); + printk ("scsi%d : DSP 0x%lx (virt 0x%p) ->\n", host->host_no, + virt_to_bus(dsp), dsp); + for (i = 6; i > 0; --i, dsp += size) + size = print_insn (host, dsp, "", 1); + if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) { + printk ("scsi%d : connected (SDID=0x%x, SSID=0x%x)\n", + host->host_no, NCR53c7x0_read8 (SDID_REG_800), + NCR53c7x0_read8 (SSID_REG_800)); + print_dsa (host, dsa, ""); + } + +#if 1 + print_queues (host); +#endif + } +} + +/* + * Function : static int shutdown (struct Scsi_Host *host) + * + * Purpose : does a clean (we hope) shutdown of the NCR SCSI + * chip. Use prior to dumping core, unloading the NCR driver, + * + * Returns : 0 on success + */ +static int +shutdown (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + unsigned long flags; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + NCR53c7x0_local_setup(host); + save_flags (flags); + cli(); +/* Get in a state where we can reset the SCSI bus */ + ncr_halt (host); + ncr_scsi_reset (host); + hostdata->soft_reset(host); + + disable (host); + restore_flags (flags); + return 0; +} + +/* + * Function : void ncr_scsi_reset (struct Scsi_Host *host) + * + * Purpose : reset the SCSI bus. + */ + +static void +ncr_scsi_reset (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned long flags; + int sien = 0; + NCR53c7x0_local_setup(host); + save_flags (flags); + cli(); + if ((hostdata->chip / 100) == 8) { + sien = NCR53c7x0_read8(SIEN0_REG_800); + NCR53c7x0_write8(SIEN0_REG_800, sien & ~SIEN_RST); + } + NCR53c7x0_write8(SCNTL1_REG, SCNTL1_RST); + udelay(25); /* Minimum amount of time to assert RST */ + NCR53c7x0_write8(SCNTL1_REG, 0); + if ((hostdata->chip / 100) == 8) { + NCR53c7x0_write8(SIEN0_REG_800, sien); + } + restore_flags (flags); +} + +/* + * Function : void hard_reset (struct Scsi_Host *host) + * + */ + +static void +hard_reset (struct Scsi_Host *host) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned long flags; + save_flags (flags); + cli(); + ncr_scsi_reset(host); + NCR53c7x0_driver_init (host); + if (hostdata->soft_reset) + hostdata->soft_reset (host); + restore_flags(flags); +} + + +/* + * Function : Scsi_Cmnd *return_outstanding_commands (struct Scsi_Host *host, + * int free, int issue) + * + * Purpose : return a linked list (using the SCp.buffer field as next, + * so we don't perturb hostdata. We don't use a field of the + * NCR53c7x0_cmd structure since we may not have allocated one + * for the command causing the reset.) of Scsi_Cmnd structures that + * had propogated bellow the Linux issue queue level. If free is set, + * free the NCR53c7x0_cmd structures which are associated with + * the Scsi_Cmnd structures, and clean up any internal + * NCR lists that the commands were on. If issue is set, + * also return commands in the issue queue. + * + * Returns : linked list of commands + * + * NOTE : the caller should insure that the NCR chip is halted + * if the free flag is set. + */ + +static Scsi_Cmnd * +return_outstanding_commands (struct Scsi_Host *host, int free, int issue) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + struct NCR53c7x0_cmd *c; + int i; + u32 *current; + Scsi_Cmnd *list = NULL, *tmp; + for (c = (struct NCR53c7x0_cmd *) hostdata->running_list; c; + c = (struct NCR53c7x0_cmd *) c->next) { + if (c->cmd->SCp.buffer) { + printk ("scsi%d : loop detected in running list!\n", host->host_no); + break; + } else { + printk ("The sti() implicit in a printk() prevents hangs\n"); + break; + } + + c->cmd->SCp.buffer = (struct scatterlist *) list; + list = c->cmd; + if (free) { + c->next = hostdata->free; + hostdata->free = c; + } + } + + if (free) { + for (i = 0, current = (u32 *) hostdata->schedule; + i < host->can_queue; ++i, current += 2) { + current[0] = hostdata->NOP_insn; + current[1] = 0xdeadbeef; + } + hostdata->current = NULL; + } + + if (issue) { + for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp; tmp = tmp->next) { + if (tmp->SCp.buffer) { + printk ("scsi%d : loop detected in issue queue!\n", + host->host_no); + break; + } + tmp->SCp.buffer = (struct scatterlist *) list; + list = tmp; + } + if (free) + hostdata->issue_queue = NULL; + + } + return list; +} + +/* + * Function : static int disable (struct Scsi_Host *host) + * + * Purpose : disables the given NCR host, causing all commands + * to return a driver error. Call this so we can unload the + * module during development and try again. Eventually, + * we should be able to find clean workarrounds for these + * problems. + * + * Inputs : host - hostadapter to twiddle + * + * Returns : 0 on success. + */ + +static int +disable (struct Scsi_Host *host) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned long flags; + Scsi_Cmnd *nuke_list, *tmp; + save_flags(flags); + cli(); + if (hostdata->state != STATE_HALTED) + ncr_halt (host); + nuke_list = return_outstanding_commands (host, 1 /* free */, 1 /* issue */); + hard_reset (host); + hostdata->state = STATE_DISABLED; + restore_flags(flags); + printk ("scsi%d : nuking commands\n", host->host_no); + for (; nuke_list; nuke_list = tmp) { + tmp = (Scsi_Cmnd *) nuke_list->SCp.buffer; + nuke_list->result = DID_ERROR << 16; + nuke_list->scsi_done(nuke_list); + } + printk ("scsi%d : done. \n", host->host_no); + printk (KERN_ALERT "scsi%d : disabled. Unload and reload\n", + host->host_no); + return 0; +} + +/* + * Function : static int ncr_halt (struct Scsi_Host *host) + * + * Purpose : halts the SCSI SCRIPTS(tm) processor on the NCR chip + * + * Inputs : host - SCSI chip to halt + * + * Returns : 0 on success + */ + +static int +ncr_halt (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + unsigned long flags; + unsigned char istat, tmp; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + int stage; + NCR53c7x0_local_setup(host); + + save_flags(flags); + cli(); + /* Stage 0 : eat all interrupts + Stage 1 : set ABORT + Stage 2 : eat all but abort interrupts + Stage 3 : eat all interrupts + */ + for (stage = 0;;) { + if (stage == 1) { + NCR53c7x0_write8(hostdata->istat, ISTAT_ABRT); + ++stage; + } + istat = NCR53c7x0_read8 (hostdata->istat); + if (istat & ISTAT_SIP) { + if ((hostdata->chip / 100) == 8) { + tmp = NCR53c7x0_read8(SIST0_REG_800); + udelay(1); + tmp = NCR53c7x0_read8(SIST1_REG_800); + } else { + tmp = NCR53c7x0_read8(SSTAT0_REG); + } + } else if (istat & ISTAT_DIP) { + tmp = NCR53c7x0_read8(DSTAT_REG); + if (stage == 2) { + if (tmp & DSTAT_ABRT) { + NCR53c7x0_write8(hostdata->istat, 0); + ++stage; + } else { + printk(KERN_ALERT "scsi%d : could not halt NCR chip\n", + host->host_no); + disable (host); + } + } + } + if (!(istat & (ISTAT_SIP|ISTAT_DIP))) + if (stage == 0) + ++stage; + else if (stage == 3) + break; + } + hostdata->state = STATE_HALTED; + restore_flags(flags); +#if 0 + print_lots (host); +#endif + return 0; +} + +/* + * Function: event_name (int event) + * + * Purpose: map event enum into user-readable strings. + */ + +static const char * +event_name (int event) { + switch (event) { + case EVENT_NONE: return "none"; + case EVENT_ISSUE_QUEUE: return "to issue queue"; + case EVENT_START_QUEUE: return "to start queue"; + case EVENT_SELECT: return "selected"; + case EVENT_DISCONNECT: return "disconnected"; + case EVENT_RESELECT: return "reselected"; + case EVENT_COMPLETE: return "completed"; + case EVENT_IDLE: return "idle"; + case EVENT_SELECT_FAILED: return "select failed"; + case EVENT_BEFORE_SELECT: return "before select"; + case EVENT_RESELECT_FAILED: return "reselect failed"; + default: return "unknown"; + } +} + +/* + * Function : void dump_events (struct Scsi_Host *host, count) + * + * Purpose : print last count events which have occurred. + */ +static void +dump_events (struct Scsi_Host *host, int count) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + struct NCR53c7x0_event event; + int i; + unsigned long flags; + if (hostdata->events) { + if (count > hostdata->event_size) + count = hostdata->event_size; + for (i = hostdata->event_index; count > 0; + i = (i ? i - 1 : hostdata->event_size -1), --count) { + save_flags(flags); +/* + * By copying the event we're currently examinging with interrupts + * disabled, we can do multiple printk(), etc. operations and + * still be guaranteed that they're happening on the same + * event structure. + */ + cli(); +#if 0 + event = hostdata->events[i]; +#else + memcpy ((void *) &event, (void *) &(hostdata->events[i]), + sizeof(event)); +#endif + + restore_flags(flags); + printk ("scsi%d : %s event %d at %ld secs %ld usecs target %d lun %d\n", + host->host_no, event_name (event.event), count, + (long) event.time.tv_sec, (long) event.time.tv_usec, + event.target, event.lun); + if (event.dsa) + printk (" event for dsa 0x%lx (virt 0x%p)\n", + virt_to_bus(event.dsa), event.dsa); + if (event.pid != -1) { + printk (" event for pid %ld ", event.pid); + print_command (event.cmnd); + } + } + } +} + +/* + * Function: check_address + * + * Purpose: Check to see if a possibly corrupt pointer will fault the + * kernel. + * + * Inputs: addr - address; size - size of area + * + * Returns: 0 if area is OK, -1 on error. + * + * NOTES: should be implemented in terms of vverify on kernels + * that have it. + */ + +static int +check_address (unsigned long addr, int size) { + return (MAP_NR(addr) < 1 || MAP_NR(addr + size) > MAP_NR(high_memory) ? + -1 : 0); +} + +#ifdef MODULE +int +NCR53c7x0_release(struct Scsi_Host *host) { + struct NCR53c7x0_hostdata *hostdata = + (struct NCR53c7x0_hostdata *) host->hostdata; + struct NCR53c7x0_cmd *cmd, *tmp; + shutdown (host); + if (host->irq != IRQ_NONE) + { + int irq_count; + struct Scsi_Host *tmp; + for (irq_count = 0, tmp = first_host; tmp; tmp = tmp->next) + if (tmp->hostt == the_template && tmp->irq == host->irq) + ++irq_count; + if (irq_count == 1) + free_irq(host->irq); + } + if (host->dma_channel != DMA_NONE) + free_dma(host->dma_channel); + if (host->io_port) + release_region(host->io_port, host->n_io_port); + + for (cmd = (struct NCR53c7x0_cmd *) hostdata->free; cmd; cmd = tmp, + --hostdata->num_cmds) { + tmp = (struct NCR53c7x0_cmd *) cmd->next; + /* + * If we're going to loop, try to stop it to get a more accurate + * count of the leaked commands. + */ + cmd->next = NULL; + if (cmd->free) + cmd->free ((void *) cmd->real, cmd->size); + } + if (hostdata->num_cmds) + printk ("scsi%d : leaked %d NCR53c7x0_cmd structures\n", + host->host_no, hostdata->num_cmds); + if (hostdata->events) + vfree ((void *)hostdata->events); + return 1; +} +Scsi_Host_Template driver_template = NCR53c7xx; +#include "scsi_module.c" +#endif /* def MODULE */ -- cgit v1.2.3