diff options
Diffstat (limited to 'drivers/scsi/53c7xx.c')
-rw-r--r-- | drivers/scsi/53c7xx.c | 6102 |
1 files changed, 6102 insertions, 0 deletions
diff --git a/drivers/scsi/53c7xx.c b/drivers/scsi/53c7xx.c new file mode 100644 index 000000000000..8ead55f75d02 --- /dev/null +++ b/drivers/scsi/53c7xx.c @@ -0,0 +1,6102 @@ +/* + * 53c710 driver. Modified from Drew Eckhardts driver + * for 53c810 by Richard Hirst [richard@sleepie.demon.co.uk] + * Check out PERM_OPTIONS and EXPECTED_CLOCK, which may be defined in the + * relevant machine specific file (eg. mvme16x.[ch], amiga7xx.[ch]). + * There are also currently some defines at the top of 53c7xx.scr. + * The chip type is #defined in script_asm.pl, as well as the Makefile. + * Host scsi ID expected to be 7 - see NCR53c7x0_init(). + * + * I have removed the PCI code and some of the 53c8xx specific code - + * simply to make this file smaller and easier to manage. + * + * MVME16x issues: + * Problems trying to read any chip registers in NCR53c7x0_init(), as they + * may never have been set by 16xBug (eg. If kernel has come in over tftp). + */ + +/* + * Adapted for Linux/m68k Amiga platforms for the A4000T/A4091 and + * WarpEngine SCSI controllers. + * By Alan Hourihane <alanh@fairlite.demon.co.uk> + * Thanks to Richard Hirst for making it possible with the MVME additions + */ + +/* + * 53c710 rev 0 doesn't support add with carry. Rev 1 and 2 does. To + * overcome this problem you can define FORCE_DSA_ALIGNMENT, which ensures + * that the DSA address is always xxxxxx00. If disconnection is not allowed, + * then the script only ever tries to add small (< 256) positive offsets to + * DSA, so lack of carry isn't a problem. FORCE_DSA_ALIGNMENT can, of course, + * be defined for all chip revisions at a small cost in memory usage. + */ + +#define FORCE_DSA_ALIGNMENT + +/* + * Selection timer does not always work on the 53c710, depending on the + * timing at the last disconnect, if this is a problem for you, try + * using validids as detailed below. + * + * Options for the NCR7xx driver + * + * noasync:0 - disables sync and asynchronous negotiation + * nosync:0 - disables synchronous negotiation (does async) + * nodisconnect:0 - disables disconnection + * validids:0x?? - Bitmask field that disallows certain ID's. + * - e.g. 0x03 allows ID 0,1 + * - 0x1F allows ID 0,1,2,3,4 + * opthi:n - replace top word of options with 'n' + * optlo:n - replace bottom word of options with 'n' + * - ALWAYS SPECIFY opthi THEN optlo <<<<<<<<<< + */ + +/* + * 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_MEMORY_MAPPED + * 680x0 doesn't have an IO map! + * + * OPTION_DEBUG_TEST1 + * Test 1 does bus mastering and interrupt tests, which will help weed + * out brain damaged main boards. + * + * Other PERM_OPTIONS settings are listed below. Note the actual options + * required are set in the relevant file (mvme16x.c, amiga7xx.c, etc): + * + * OPTION_NO_ASYNC + * 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. + */ + +/* + * 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 transferred 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. + * + */ + +#include <linux/module.h> + +#include <linux/config.h> + +#include <linux/types.h> +#include <asm/setup.h> +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/system.h> +#include <linux/delay.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> +#include <linux/ioport.h> +#include <linux/time.h> +#include <linux/blkdev.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <asm/pgtable.h> + +#ifdef CONFIG_AMIGA +#include <asm/amigahw.h> +#include <asm/amigaints.h> +#include <asm/irq.h> + +#define BIG_ENDIAN +#define NO_IO_SPACE +#endif + +#ifdef CONFIG_MVME16x +#include <asm/mvme16xhw.h> + +#define BIG_ENDIAN +#define NO_IO_SPACE +#define VALID_IDS +#endif + +#ifdef CONFIG_BVME6000 +#include <asm/bvme6000hw.h> + +#define BIG_ENDIAN +#define NO_IO_SPACE +#define VALID_IDS +#endif + +#include "scsi.h" +#include <scsi/scsi_host.h> +#include "53c7xx.h" +#include <linux/stat.h> +#include <linux/stddef.h> + +#ifdef NO_IO_SPACE +/* + * The following make the definitions in 53c7xx.h (write8, etc) smaller, + * we don't have separate i/o space anyway. + */ +#undef inb +#undef outb +#undef inw +#undef outw +#undef inl +#undef outl +#define inb(x) 1 +#define inw(x) 1 +#define inl(x) 1 +#define outb(x,y) 1 +#define outw(x,y) 1 +#define outl(x,y) 1 +#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 NCR53c7xx_run_tests (struct Scsi_Host *host); +static irqreturn_t NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs); +static void NCR53c7x0_intfly (struct Scsi_Host *host); +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 NCR53c7xx_dsa_fixup (struct NCR53c7x0_cmd *cmd); +static void NCR53c7x0_init_fixup (struct Scsi_Host *host); +static int NCR53c7x0_dstat_sir_intr (struct Scsi_Host *host, struct + NCR53c7x0_cmd *cmd); +static void NCR53c7x0_soft_reset (struct Scsi_Host *host); + +/* 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; + +/* NCR53c710 script handling code */ + +#include "53c7xx_d.h" +#ifdef A_int_debug_sync +#define DEBUG_SYNC_INTR A_int_debug_sync +#endif +int NCR53c7xx_script_len = sizeof (SCRIPT); +int NCR53c7xx_dsa_len = A_dsa_end + Ent_dsa_zero - Ent_dsa_code_template; +#ifdef FORCE_DSA_ALIGNMENT +int CmdPageStart = (0 - Ent_dsa_zero - sizeof(struct NCR53c7x0_cmd)) & 0xff; +#endif + +static char *setup_strings[] = + {"","","","","","","",""}; + +#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *)) +#define SETUP_BUFFER_SIZE 200 +static char setup_buffer[SETUP_BUFFER_SIZE]; +static char setup_used[MAX_SETUP_STRINGS]; + +void ncr53c7xx_setup (char *str, int *ints) +{ + int i; + char *p1, *p2; + + p1 = setup_buffer; + *p1 = '\0'; + if (str) + strncpy(p1, str, SETUP_BUFFER_SIZE - strlen(setup_buffer)); + setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0'; + p1 = setup_buffer; + i = 0; + while (*p1 && (i < MAX_SETUP_STRINGS)) { + p2 = strchr(p1, ','); + if (p2) { + *p2 = '\0'; + if (p1 != p2) + setup_strings[i] = p1; + p1 = p2 + 1; + i++; + } + else { + setup_strings[i] = p1; + break; + } + } + for (i=0; i<MAX_SETUP_STRINGS; i++) + setup_used[i] = 0; +} + + +/* check_setup_strings() returns index if key found, 0 if not + */ + +static int check_setup_strings(char *key, int *flags, int *val, char *buf) +{ +int x; +char *cp; + + for (x=0; x<MAX_SETUP_STRINGS; x++) { + if (setup_used[x]) + continue; + if (!strncmp(setup_strings[x], key, strlen(key))) + break; + if (!strncmp(setup_strings[x], "next", strlen("next"))) + return 0; + } + if (x == MAX_SETUP_STRINGS) + return 0; + setup_used[x] = 1; + cp = setup_strings[x] + strlen(key); + *val = -1; + if (*cp != ':') + return ++x; + cp++; + if ((*cp >= '0') && (*cp <= '9')) { + *val = simple_strtoul(cp,NULL,0); + } + return ++x; +} + + + +/* + * 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 to 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 further 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 change 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 shoould 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 + * always 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<<target) instead of + * binary encoded target ids. Need to accommodate + * different register mappings, probably scan through + * the SCRIPT code and change the non SFBR register operand + * of all MOVE instructions. + * + * It is rather worse than this actually, the 710 corrupts + * both TEMP and DSA when you do a MOVE MEMORY. This + * screws you up all over the place. MOVE MEMORY 4 with a + * destination of DSA seems to work OK, which helps some. + * Richard Hirst richard@sleepie.demon.co.uk + * + * NCR53c700/700-66 - need to add code to refix addresses on + * every nexus change, eliminate all table indirect code, + * very messy. + * + * 6. The NCR53c7x0 series is very popular on other platforms that + * could be running Linux - ie, some high performance AMIGA SCSI + * boards use it. + * + * So, I should include #ifdef'd code so that it is + * compatible with these systems. + * + * Specifically, the little Endian assumptions I made in my + * bit fields need to change, and if the NCR doesn't see memory + * the right way, we need to provide options to reverse words + * when the scripts are relocated. + * + * 7. Use vremap() to access memory mapped boards. + */ + +/* + * Allow for simultaneous existence of multiple SCSI scripts so we + * can have a single driver binary for all of the family. + * + * - one for NCR53c700 and NCR53c700-66 chips (not yet supported) + * - one for rest (only the NCR53c810, 815, 820, and 825 are currently + * supported) + * + * So that we only need two SCSI scripts, we need to modify things so + * that we fixup register accesses in READ/WRITE instructions, and + * we'll also have to accommodate the bit vs. binary encoding of IDs + * with the 7xx chips. + */ + +#define ROUNDUP(adr,type) \ + ((void *) (((long) (adr) + sizeof(type) - 1) & ~(sizeof(type) - 1))) + + +/* + * Function: issue_to_cmd + * + * Purpose: convert jump instruction in issue array to NCR53c7x0_cmd + * structure pointer. + * + * Inputs; issue - pointer to start of NOP or JUMP instruction + * in issue array. + * + * Returns: pointer to command on success; 0 if opcode is NOP. + */ + +static inline struct NCR53c7x0_cmd * +issue_to_cmd (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata, + u32 *issue) +{ + return (issue[0] != hostdata->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; +} + + +/* + * 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[] = { +#ifdef CONFIG_SCSI_NCR53C7xx_FAST + EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 25 /* *4ns */, 8 /* off */ +#else + EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 50 /* *4ns */, 8 /* off */ +#endif +}; + +/* 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 */ +}; + +#if 0 +/* + * 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; +} + +#if 0 +/* + * 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, accessible 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; + } + else if (target > h->max_id) { + printk (KERN_ALERT "target %d exceeds maximum of %d\n", target, + h->max_id); + return -1; + } + hostdata = (struct NCR53c7x0_hostdata *)h->hostdata[0]; + + local_irq_save(flags); + if (hostdata->initiate_sdtr & (1 << target)) { + local_irq_restore(flags); + printk (KERN_ALERT "target %d already doing SDTR\n", target); + return -1; + } + hostdata->initiate_sdtr |= (1 << target); + local_irq_restore(flags); + return 0; +} +#endif + +/* + * 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[0]; + if (on_or_off) + hostdata->options |= OPTION_DISCONNECT; + else + hostdata->options &= ~OPTION_DISCONNECT; + return 0; +} +#endif + +/* + * 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[0]; + int i, j; + u32 *ncrcurrent; + + 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->ncrcurrent = NULL; + for (i = 0, ncrcurrent = (u32 *) hostdata->schedule; + i < host->can_queue; ++i, ncrcurrent += 2) { + ncrcurrent[0] = hostdata->NOP_insn; + ncrcurrent[1] = 0xdeadbeef; + } + ncrcurrent[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | DBC_TCI_TRUE; + ncrcurrent[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 clock_to_ccf_710 (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_710 (int clock) { + if (clock <= 16666666) + return -1; + if (clock <= 25000000) + return 2; /* Divide by 1.0 */ + else if (clock <= 37500000) + return 1; /* Divide by 1.5 */ + else if (clock <= 50000000) + return 0; /* Divide by 2.0 */ + else if (clock <= 66000000) + return 3; /* 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. + */ + +int +NCR53c7x0_init (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + int i, ccf; + unsigned char revision; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata[0]; + /* + * 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 val, flags; + char buf[32]; + int expected_id = -1; + int expected_clock = -1; + int uninitialized = 0; +#ifdef NO_IO_SPACE + int expected_mapping = OPTION_MEMORY_MAPPED; +#else + int expected_mapping = OPTION_IO_MAPPED; +#endif + for (i=0;i<7;i++) + hostdata->valid_ids[i] = 1; /* Default all ID's to scan */ + + /* Parse commandline flags */ + if (check_setup_strings("noasync",&flags,&val,buf)) + { + hostdata->options |= OPTION_NO_ASYNC; + hostdata->options &= ~(OPTION_SYNCHRONOUS | OPTION_ALWAYS_SYNCHRONOUS); + } + + if (check_setup_strings("nosync",&flags,&val,buf)) + { + hostdata->options &= ~(OPTION_SYNCHRONOUS | OPTION_ALWAYS_SYNCHRONOUS); + } + + if (check_setup_strings("nodisconnect",&flags,&val,buf)) + hostdata->options &= ~OPTION_DISCONNECT; + + if (check_setup_strings("validids",&flags,&val,buf)) + { + for (i=0;i<7;i++) + hostdata->valid_ids[i] = val & (1<<i); + } + + if ((i = check_setup_strings("next",&flags,&val,buf))) + { + while (i) + setup_used[--i] = 1; + } + + if (check_setup_strings("opthi",&flags,&val,buf)) + hostdata->options = (long long)val << 32; + if (check_setup_strings("optlo",&flags,&val,buf)) + hostdata->options |= val; + + NCR53c7x0_local_setup(host); + switch (hostdata->chip) { + case 710: + case 770: + hostdata->dstat_sir_intr = NCR53c7x0_dstat_sir_intr; + hostdata->init_save_regs = NULL; + hostdata->dsa_fixup = NCR53c7xx_dsa_fixup; + hostdata->init_fixup = NCR53c7x0_init_fixup; + hostdata->soft_reset = NCR53c7x0_soft_reset; + hostdata->run_tests = NCR53c7xx_run_tests; + expected_clock = hostdata->scsi_clock; + 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; + +/* We have to assume that this may be the first access to the chip, so + * we must set EA in DCNTL. */ + + NCR53c7x0_write8 (DCNTL_REG, DCNTL_10_EA|DCNTL_10_COM); + + +/* 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 + /* May not be able to do this - chip my not have been set up yet */ + 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 = 7; +#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. + */ + + /* + * CTEST7 controls cache snooping, burst mode, and support for + * external differential drivers. This isn't currently used - the + * default value may not be optimal anyway. + * Even worse, it may never have been set up since reset. + */ + hostdata->saved_ctest7 = NCR53c7x0_read8(CTEST7_REG) & CTEST7_SAVE; + revision = (NCR53c7x0_read8(CTEST8_REG) & 0xF0) >> 4; + switch (revision) { + case 1: revision = 0; break; + case 2: revision = 1; break; + case 4: revision = 2; break; + case 8: revision = 3; break; + default: revision = 255; break; + } + printk("scsi%d: Revision 0x%x\n",host->host_no,revision); + + if ((revision == 0 || revision == 255) && (hostdata->options & (OPTION_SYNCHRONOUS|OPTION_DISCONNECT|OPTION_ALWAYS_SYNCHRONOUS))) + { + printk ("scsi%d: Disabling sync working and disconnect/reselect\n", + host->host_no); + hostdata->options &= ~(OPTION_SYNCHRONOUS|OPTION_DISCONNECT|OPTION_ALWAYS_SYNCHRONOUS); + } + + /* + * On NCR53c700 series chips, DCNTL controls the SCSI clock divisor, + * on 800 series chips, it allows for a totem-pole IRQ driver. + * NOTE saved_dcntl currently overwritten in init function. + * The value read here may be garbage anyway, MVME16x board at least + * does not initialise chip if kernel arrived via tftp. + */ + + hostdata->saved_dcntl = NCR53c7x0_read8(DCNTL_REG); + + /* + * DMODE controls DMA burst length, and on 700 series chips, + * 286 mode and bus width + * NOTE: On MVME16x, chip may have been reset, so this could be a + * power-on/reset default value. + */ + hostdata->saved_dmode = NCR53c7x0_read8(hostdata->dmode); + + /* + * Now that burst length and enabled/disabled status is known, + * clue the user in on it. + */ + + ccf = clock_to_ccf_710 (expected_clock); + + 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 |