| /* |
| * 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_dbg.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 struct 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. |
| * |
| * 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); |
| |
| if (request_irq(host->irq, NCR53c7x0_intr, SA_SHIRQ, "53c7xx", host)) |
| { |
| printk("scsi%d : IRQ%d not free, detaching\n", |
| host->host_no, host->irq); |
| goto err_unregister; |
| } |
| |
| if ((hostdata->run_tests && hostdata->run_tests(host) == -1) || |
| (hostdata->options & OPTION_DEBUG_TESTS_ONLY)) { |
| /* XXX Should disable interrupts, etc. here */ |
| goto err_free_irq; |
| } else { |
| if (host->io_port) { |
| host->n_io_port = 128; |
| if (!request_region (host->io_port, host->n_io_port, "ncr53c7xx")) |
| goto err_free_irq; |
| } |
| } |
| |
| if (NCR53c7x0_read8 (SBCL_REG) & SBCL_BSY) { |
| printk ("scsi%d : bus wedge, doing SCSI reset\n", host->host_no); |
| hard_reset (host); |
| } |
| return 0; |
| |
| err_free_irq: |
| free_irq(host->irq, NCR53c7x0_intr); |
| err_unregister: |
| scsi_unregister(host); |
| return -1; |
| } |
| |
| /* |
| * Function : int ncr53c7xx_init(struct scsi_host_template *tpnt, int board, int chip, |
| * unsigned long base, int io_port, int irq, int dma, long long options, |
| * int clock); |
| * |
| * Purpose : initializes a NCR53c7,8x0 based on base addresses, |
| * IRQ, and DMA channel. |
| * |
| * Inputs : tpnt - Template for this SCSI adapter, board - board level |
| * product, chip - 710 |
| * |
| * Returns : 0 on success, -1 on failure. |
| * |
| */ |
| |
| int |
| ncr53c7xx_init (struct scsi_host_template *tpnt, int board, int chip, |
| unsigned long base, int io_port, int irq, int dma, |
| long long options, int clock) |
| { |
| 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; |
| unsigned long page; |
| |
| switch (chip) { |
| case 710: |
| case 770: |
| schedule_size = (tpnt->can_queue + 1) * 8 /* JUMP instruction size */; |
| script_len = NCR53c7xx_script_len; |
| dsa_len = NCR53c7xx_dsa_len; |
| options |= OPTION_INTFLY; |
| sprintf (chip_str, "NCR53c%d", chip); |
| break; |
| default: |
| printk("scsi-ncr53c7xx : unsupported SCSI chip %d\n", chip); |
| return -1; |
| } |
| |
| printk("scsi-ncr53c7xx : %s at memory 0x%lx, io 0x%x, irq %d", |
| chip_str, base, io_port, irq); |
| if (dma == DMA_NONE) |
| printk("\n"); |
| else |
| printk(", dma %d\n", dma); |
| |
| if (options & OPTION_DEBUG_PROBE_ONLY) { |
| printk ("scsi-ncr53c7xx : 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; |
| |
| page = __get_free_pages(GFP_ATOMIC,1); |
| if(page==0) |
| { |
| printk(KERN_ERR "53c7xx: out of memory.\n"); |
| return -ENOMEM; |
| } |
| #ifdef FORCE_DSA_ALIGNMENT |
| /* |
| * 53c710 rev.0 doesn't have an add-with-carry instruction. |
| * Ensure we allocate enough memory to force DSA alignment. |
| */ |
| size += 256; |
| #endif |
| /* Size should be < 8K, so we can fit it in two pages. */ |
| if (size > 8192) { |
| printk(KERN_ERR "53c7xx: hostdata > 8K\n"); |
| return -1; |
| } |
| |
| instance = scsi_register (tpnt, 4); |
| if (!instance) |
| { |
| free_page(page); |
| return -1; |
| } |
| instance->hostdata[0] = page; |
| memset((void *)instance->hostdata[0], 0, 8192); |
| cache_push(virt_to_phys((void *)(instance->hostdata[0])), 8192); |
| cache_clear(virt_to_phys((void *)(instance->hostdata[0])), 8192); |
| kernel_set_cachemode((void *)instance->hostdata[0], 8192, IOMAP_NOCACHE_SER); |
| |
| /* 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[0]; |
| hostdata->size = size; |
| hostdata->script_count = script_len / sizeof(u32); |
| hostdata->board = board; |
| hostdata->chip = chip; |
| |
| /* |
| * 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 = 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; |
| hostdata->scsi_clock = clock; |
| /* Initialize single command */ |
| tmp = (hostdata->script + hostdata->script_count); |
| #ifdef FORCE_DSA_ALIGNMENT |
| { |
| void *t = ROUNDUP(tmp, void *); |
| if (((u32)t & 0xff) > CmdPageStart) |
| t = (void *)((u32)t + 255); |
| t = (void *)(((u32)t & ~0xff) + CmdPageStart); |
| hostdata->free = t; |
| #if 0 |
| printk ("scsi: Registered size increased by 256 to %d\n", size); |
| printk ("scsi: CmdPageStart = 0x%02x\n", CmdPageStart); |
| printk ("scsi: tmp = 0x%08x, hostdata->free set to 0x%08x\n", |
| (u32)tmp, (u32)t); |
| #endif |
| } |
| #else |
| hostdata->free = ROUNDUP(tmp, void *); |
| #endif |
| 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 convenient 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 void NCR53c7x0_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 |
| NCR53c7x0_init_fixup (struct Scsi_Host *host) { |
| NCR53c7x0_local_declare(); |
| struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) |
| host->hostdata[0]; |
| 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. |
| */ |
| |
| #if defined(CONFIG_MVME16x) || defined(CONFIG_BVME6000) |
| /* We know better what we want than 16xBug does! */ |
| tmp = DMODE_10_BL_8 | DMODE_10_FC2; |
| #else |
| tmp = NCR53c7x0_read8(DMODE_REG_10); |
| tmp &= (DMODE_BL_MASK | DMODE_10_FC2 | DMODE_10_FC1 | DMODE_710_PD | |
| DMODE_710_UO); |
| #endif |
| |
| 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((void *)host->base); |
| memory_to_ncr = ncr_to_memory = tmp; |
| } |
| |
| /* SCRATCHB_REG_10 == SCRATCHA_REG_800, as it happens */ |
| patch_abs_32 (hostdata->script, 0, addr_scratch, base + SCRATCHA_REG_800); |
| patch_abs_32 (hostdata->script, 0, addr_temp, base + TEMP_REG); |
| patch_abs_32 (hostdata->script, 0, addr_dsa, base + DSA_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_32 (hostdata->script, 0, saved_dsa, |
| virt_to_bus((void *)&hostdata->saved2_dsa)); |
| patch_abs_32 (hostdata->script, 0, emulfly, |
| virt_to_bus((void *)&hostdata->emulated_intfly)); |
| |
| 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 NCR53c7xx_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 |
| NCR53c7xx_run_tests (struct Scsi_Host *host) { |
| NCR53c7x0_local_declare(); |
| struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) |
| host->hostdata[0]; |
| 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 */ |
| |
| local_irq_save(flags); |
| if (!hostdata->idle) { |
| printk ("scsi%d : chip not idle, aborting tests\n", host->host_no); |
| local_irq_restore(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); |
| if (hostdata->options & OPTION_DEBUG_TRACE) |
| NCR53c7x0_write8 (DCNTL_REG, hostdata->saved_dcntl | DCNTL_SSM | |
| DCNTL_STD); |
| printk (" started\n"); |
| local_irq_restore(flags); |
| |
| /* |
| * 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 occassionally fails with this, but works with |
| * 10 times as much? |
| */ |
| |
| timeout = jiffies + 5 * HZ / 10; |
| while ((hostdata->test_completed == -1) && time_before(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" |
| : ""); |
| 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)); |
| local_irq_restore(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 < 6; ++i) { |
| #ifdef VALID_IDS |
| if (!hostdata->valid_ids[i]) |
| continue; |
| #endif |
| local_irq_disable(); |
| if (!hostdata->idle) { |
| printk ("scsi%d : chip not idle, aborting tests\n", host->host_no); |
| local_irq_restore(flags); |
| return -1; |
| } |
| |
| /* 710: bit mapped scsi ID, async */ |
| dsa[0] = (1 << 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); |
| if (hostdata->options & OPTION_DEBUG_TRACE) |
| NCR53c7x0_write8 (DCNTL_REG, hostdata->saved_dcntl | |
| DCNTL_SSM | DCNTL_STD); |
| local_irq_restore(flags); |
| |
| timeout = jiffies + 5 * HZ; /* arbitrary */ |
| while ((hostdata->test_completed == -1) && time_before(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); |
| scsi_print_status (status); |
| printk ("\nscsi%d : message ", host->host_no); |
| scsi_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); |
| local_irq_restore(flags); |
| return -1; |
| } |
| } else if (hostdata->test_completed == -1) { |
| printk ("scsi%d : test 2 timed out\n", host->host_no); |
| local_irq_restore(flags); |
| return -1; |
| } |
| hostdata->test_running = 0; |
| } |
| } |
| |
| local_irq_restore(flags); |
| return 0; |
| } |
| |
| /* |
| * Function : static void NCR53c7xx_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 |
| NCR53c7xx_dsa_fixup (struct NCR53c7x0_cmd *cmd) { |
| Scsi_Cmnd *c = cmd->cmd; |
| struct Scsi_Host *host = c->device->host; |
| struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) |
| host->hostdata[0]; |
| 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->device->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->device->id].script)); |
| patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), |
| dsa_sscf_710, virt_to_bus((void *)&hostdata->sync[c->device->id].sscf_710)); |
| patch_abs_tci_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32), |
| dsa_temp_target, 1 << c->device->id); |
| /* 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; |
| local_irq_save(flags); |
| 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. |
| */ |
| } |
| local_irq_restore(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->device->host; |
| struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) |
| host->hostdata[0]; |
| unsigned long flags; |
| int left, found; |
| volatile struct NCR53c7x0_cmd * linux_search; |
| volatile struct NCR53c7x0_cmd * volatile *linux_prev; |
| volatile u32 *ncr_prev, *ncrcurrent, ncr_search; |
| |
| #if 0 |
| printk ("scsi%d: abnormal finished\n", host->host_no); |
| #endif |
| |
| local_irq_save(flags); |
| 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, ncrcurrent = hostdata->schedule; |
| left > 0; --left, ncrcurrent += 2) |
| { |
| if (issue_to_cmd (host, hostdata, (u32 *) ncrcurrent) == cmd) |
| { |
| ncrcurrent[0] = hostdata->NOP_insn; |
| ncrcurrent[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 reconncect 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->device->id][c->device->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); |
| |
| local_irq_restore(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[0]; |
| unsigned long flags; |
| NCR53c7x0_local_setup(host); |
| |
| /* |
| * Find the break point corresponding to this address, and |
| * dump the appropriate debugging information to standard |
| * output. |
| */ |
| local_irq_save(flags); |
| 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. |
| */ |
| |
| local_irq_restore(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. |
| * |
| * NOTE: For 53c710, scntl3 is actually used for SCF bits from |
| * SBCL, as we don't have a SCNTL3. |
| */ |
| |
| 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[0]; |
| 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 = (1 << target) << 16 | |
| (sxfer << 8); |
| hostdata->sync[target].sscf_710 = scntl3; |
| |
| script = (u32 *) hostdata->sync[target].script; |
| |
| /* XXX - add NCR53c7x0 code to reprogram SCF bits if we want to */ |
| script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY | |
| DCMD_RWRI_OP_MOVE) << 24) | |
| (SBCL_REG << 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) { |
| NCR53c7x0_write8(SBCL_REG, 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[0]; |
| 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? I chose to do so [richard@sleepie.demon.co.uk] |
| */ |
| |
| /* Table for NCR53c8xx synchronous values */ |
| |
| /* This table is also correct for 710, allowing that scf=4 is equivalent |
| * of SSCF=0 (ie use DCNTL, divide by 3) for a 50.01-66.00MHz clock. |
| * For any other clock values, we cannot use entries with SCF values of |
| * 4. I guess that for a 66MHz clock, the slowest it will set is 2MHz, |
| * and for a 50MHz clock, the slowest will be 2.27Mhz. Should check |
| * that a device doesn't try and negotiate sync below these limits! |
| */ |
| |
| 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[0]; |
| 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 = syncs[i].scf; |
| sxfer = (msg[4] << SXFER_MO_SHIFT) | (syncs[i].tp << 4); |
| 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 NCR53c7x0_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 |
| NCR53c7x0_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[0]; |
| 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)); |
| |
| /* RGH 150597: Frig. Commands which fail with Check Condition are |
| * Flagged as successful - hack dsps to indicate check condition */ |
| #if 0 |
| /* RGH 200597: Need to disable for BVME6000, as it gets Check Conditions |
| * and then dies. Seems to handle Check Condition at startup, but |
| * not mid kernel build. */ |
| if (dsps == A_int_norm_emulateintfly && cmd && cmd->result == 2) |
| dsps = A_int_err_check_condition; |
| #endif |
| |
| 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->device->id); |
| cmd->flags &= ~CMD_FLAG_SDTR; |
| asynchronous (host, c->device->id); |
| 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, rejecting\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->device->id, c->device->lun); |
| scsi_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->device->id, |
| (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->device->id, (unsigned char *) |
| hostdata->msg_buf); |
| else |
| asynchronous (host, c->device->id); |
| 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->device->id, (unsigned char *) |
| hostdata->msg_buf); |
| } else { |
| hostdata->msg_buf[4] = 0; /* 0 offset = async */ |
| asynchronous (host, c->device->id); |
| } |
| 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: |
| if ((hostdata->chip / 100) == 8) |
| printk ("scsi%d : selected by target %d\n", host->host_no, |
| (int) NCR53c7x0_read8(SDID_REG_800) &7); |
| else |
| printk ("scsi%d : selected by target LCRC=0x%02x\n", host->host_no, |
| (int) NCR53c7x0_read8(LCRC_REG_10)); |
| hostdata->dsp = hostdata->script + hostdata->E_target_abort / |
| sizeof(u32); |
| hostdata->dsp_changed = 1; |
| return SPECIFIC_INT_NOTHING; |
| case A_int_err_unexpected_reselect: |
| if ((hostdata->chip / 100) == 8) |
| 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); |
| else |
| printk ("scsi%d : unexpected reselect LCRC=0x%02x\n", host->host_no, |
| (int) NCR53c7x0_read8(LCRC_REG_10)); |
| 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); |
| |
| /* |
| * The CDB is now mirrored in our local non-cached |
| * structure, but keep the old structure up to date as well, |
| * just in case anyone looks at it. |
| */ |
| |
| /* |
| * XXX Need to worry about data buffer alignment/cache state |
| * XXX here, but currently never get A_int_err_check_condition, |
| * XXX so ignore problem for now. |
| */ |
| cmd->cmnd[0] = c->cmnd[0] = REQUEST_SENSE; |
| cmd->cmnd[0] = c->cmnd[1] &= 0xe0; /* Zero all but LUN */ |
| cmd->cmnd[0] = c->cmnd[2] = 0; |
| cmd->cmnd[0] = c->cmnd[3] = 0; |
| cmd->cmnd[0] = c->cmnd[4] = sizeof(c->sense_buffer); |
| cmd->cmnd[0] = 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->result = 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_norm_emulateintfly: |
| NCR53c7x0_intfly(host); |
| 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); |
| if ((hostdata->chip / 100) == 8) |
| printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n", |
| host->host_no, NCR53c7x0_read8(SXFER_REG), |
| NCR53c7x0_read8(SCNTL3_REG_800)); |
| else |
| printk ("scsi%d : sxfer=0x%x, cannot read SBCL\n", |
| host->host_no, NCR53c7x0_read8(SXFER_REG)); |
| if (c) { |
| print_insn (host, (u32 *) |
| hostdata->sync[c->device->id].script, "", 1); |
| print_insn (host, (u32 *) |
| hostdata->sync[c->device->id].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)) { |
| if ((hostdata->chip / 100) == 8) |
| 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); |
| else |
| printk("scsi%d : reselected by LCRC=0x%02x lun %d\n", |
| host->host_no, (int) NCR53c7x0_read8(LCRC_REG_10), |
| (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->device->id, c->device->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 |
| /* Not valid on ncr53c710! */ |
| 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->device->id].script, "", 1); |
| print_insn (host, (u32 *) |
| hostdata->sync[c->device->id].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; |
| if ((hostdata->chip / 100) == 8) { |
| scntl3 = NCR53c7x0_read8 (SCNTL3_REG_800); |
| if (c) { |
| if (sxfer != hostdata->sync[c->device->id].sxfer_sanity || |
| scntl3 != hostdata->sync[c->device->id].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); |
| } else { |
| if (c) { |
| if (sxfer != hostdata->sync[c->device->id].sxfer_sanity) { |
| printk ("scsi%d : sync sanity check failed sxfer=0x%x", |
| host->host_no, sxfer); |
| NCR53c7x0_write8 (SXFER_REG, sxfer); |
| NCR53c7x0_write8 (SBCL_REG, |
| hostdata->sync[c->device->id].sscf_710); |
| } |
| } else |
| printk ("scsi%d : unknown command sxfer=0x%x\n", |
| host->host_no, (int) sxfer); |
| } |
| } |
| 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; |
| if ((hostdata->chip / 100) == 8) |
| 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)) ; |
| else |
| printk ("scsi%d : In do_datain (%s) sxfer=0x%x\n" |
| " datapath residual=%d\n", |
| host->host_no, sbcl_to_phase (NCR53c7x0_read8 (SBCL_REG)), |
| (int) NCR53c7x0_read8(SXFER_REG), |
| 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 |
| #ifdef A_int_debug_check_dsa |
| case A_int_debug_check_dsa: |
| if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) { |
| int sdid; |
| int tmp; |
| char *where; |
| if (hostdata->chip / 100 == 8) |
| sdid = NCR53c7x0_read8 (SDID_REG_800) & 15; |
| else { |
| tmp = NCR53c7x0_read8 (SDID_REG_700); |
| if (!tmp) |
| panic ("SDID_REG_700 = 0"); |
| tmp >>= 1; |
| sdid = 0; |
| while (tmp) { |
| tmp >>= 1; |
| sdid++; |
| } |
| } |
| where = dsp - NCR53c7x0_insn_size(NCR53c7x0_read8 |
| (DCMD_REG)) == hostdata->script + |
| Ent_select_check_dsa / sizeof(u32) ? |
| "selection" : "reselection"; |
| if (c && sdid != c->device->id) { |
| printk ("scsi%d : SDID target %d != DSA target %d at %s\n", |
| host->host_no, sdid, c->device->id, 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)); |
| if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) { |
| if (hostdata->chip / 100 == 8) |
| event->target = NCR53c7x0_read8(SSID_REG_800); |
| else { |
| unsigned char tmp, sdid; |
| tmp = NCR53c7x0_read8 (SDID_REG_700); |
| if (!tmp) |
| panic ("SDID_REG_700 = 0"); |
| tmp >>= 1; |
| sdid = 0; |
| while (tmp) { |
| tmp >>= 1; |
| sdid++; |
| } |
| event->target = sdid; |
| } |
| } |
| else |
| event->target = 255; |
| |
| if (event->event == EVENT_RESELECT) |
| event->lun = hostdata->reselected_identify & 0xf; |
| else if (c) |
| event->lun = c->device->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 "53c7xx_u.h" |
| |
| /* XXX - add alternate script handling code here */ |
| |
| |
| /* |
| * Function : static void NCR537xx_soft_reset (struct Scsi_Host *host) |
| * |
| * Purpose : perform a soft reset of the NCR53c7xx chip |
| * |
| * Inputs : host - pointer to this host adapter's structure |
| * |
| * Preconditions : NCR53c7x0_init must have been called for this |
| * host. |
| * |
| */ |
| |
| static void |
| NCR53c7x0_soft_reset (struct Scsi_Host *host) { |
| NCR53c7x0_local_declare(); |
| unsigned long flags; |
| struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) |
| host->hostdata[0]; |
| NCR53c7x0_local_setup(host); |
| |
| local_irq_save(flags); |
| |
| /* Disable scsi chip and s/w level 7 ints */ |
| |
| #ifdef CONFIG_MVME16x |
| if (MACH_IS_MVME16x) |
| { |
| volatile unsigned long v; |
| |
| v = *(volatile unsigned long *)0xfff4006c; |
| v &= ~0x8000; |
| *(volatile unsigned long *)0xfff4006c = v; |
| v = *(volatile unsigned long *)0xfff4202c; |
| v &= ~0x10; |
| *(volatile unsigned long *)0xfff4202c = v; |
| } |
| #endif |
| /* Anything specific for your hardware? */ |
| |
| /* |
| * 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. |
| */ |
| |
| /* Should we reset the scsi bus here??????????????????? */ |
| |
| NCR53c7x0_write8(ISTAT_REG_700, ISTAT_10_SRST); |
| NCR53c7x0_write8(ISTAT_REG_700, 0); |
| |
| /* |
| * saved_dcntl is set up in NCR53c7x0_init() before it is overwritten |
| * here. We should have some better way of working out the CF bit |
| * setting.. |
| */ |
| |
| hostdata->saved_dcntl = DCNTL_10_EA|DCNTL_10_COM; |
| if (hostdata->scsi_clock > 50000000) |
| hostdata->saved_dcntl |= DCNTL_700_CF_3; |
| else |
| if (hostdata->scsi_clock > 37500000) |
| hostdata->saved_dcntl |= DCNTL_700_CF_2; |
| #if 0 |
| else |
| /* Any clocks less than 37.5MHz? */ |
| #endif |
| |
| if (hostdata->options & OPTION_DEBUG_TRACE) |
| NCR53c7x0_write8(DCNTL_REG, hostdata->saved_dcntl | DCNTL_SSM); |
| else |
| NCR53c7x0_write8(DCNTL_REG, hostdata->saved_dcntl); |
| /* Following disables snooping - snooping is not required, as non- |
| * cached pages are used for shared data, and appropriate use is |
| * made of cache_push/cache_clear. Indeed, for 68060 |
| * enabling snooping causes disk corruption of ext2fs free block |
| * bitmaps and the like. If you have a 68060 with snooping hardwared |
| * on, then you need to enable CONFIG_060_WRITETHROUGH. |
| */ |
| NCR53c7x0_write8(CTEST7_REG, CTEST7_10_TT1|CTEST7_STD); |
| /* Actually burst of eight, according to my 53c710 databook */ |
| NCR53c7x0_write8(hostdata->dmode, DMODE_10_BL_8 | DMODE_10_FC2); |
| NCR53c7x0_write8(SCID_REG, 1 << host->this_id); |
| NCR53c7x0_write8(SBCL_REG, 0); |
| NCR53c7x0_write8(SCNTL1_REG, SCNTL1_ESR_700); |
| NCR53c7x0_write8(SCNTL0_REG, ((hostdata->options & OPTION_PARITY) ? |
| SCNTL0_EPC : 0) | SCNTL0_EPG_700 | SCNTL0_ARB1 | SCNTL0_ARB2); |
| |
| /* |
| * Enable all interrupts, except parity which we only want when |
| * the user requests it. |
| */ |
| |
| NCR53c7x0_write8(DIEN_REG, DIEN_700_BF | |
| DIEN_ABRT | DIEN_SSI | DIEN_SIR | DIEN_700_OPC); |
| |
| NCR53c7x0_write8(SIEN_REG_700, ((hostdata->options & OPTION_PARITY) ? |
| SIEN_PAR : 0) | SIEN_700_STO | SIEN_RST | SIEN_UDC | |
| SIEN_SGE | SIEN_MA); |
| |
| #ifdef CONFIG_MVME16x |
| if (MACH_IS_MVME16x) |
| { |
| volatile unsigned long v; |
| |
| /* Enable scsi chip and s/w level 7 ints */ |
| v = *(volatile unsigned long *)0xfff40080; |
| v = (v & ~(0xf << 28)) | (4 << 28); |
| *(volatile unsigned long *)0xfff40080 = v; |
| v = *(volatile unsigned long *)0xfff4006c; |
| v |= 0x8000; |
| *(volatile unsigned long *)0xfff4006c = v; |
| v = *(volatile unsigned long *)0xfff4202c; |
| v = (v & ~0xff) | 0x10 | 4; |
| *(volatile unsigned long *)0xfff4202c = v; |
| } |
| #endif |
| /* Anything needed for your hardware? */ |
| local_irq_restore(flags); |
| } |
| |
| |
| /* |
| * Function static struct NCR53c7x0_cmd *allocate_cmd (Scsi_Cmnd *cmd) |
| * |
| * Purpose : Return the first free NCR53c7x0_cmd structure (which are |
| * reused in a LIFO manner 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 get_zeroed_page(), 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 void |
| my_free_page (void *addr, int dummy) |
| { |
| /* XXX This assumes default cache mode to be IOMAP_FULL_CACHING, which |
| * XXX may be invalid (CONFIG_060_WRITETHROUGH) |
| */ |
| kernel_set_cachemode((void *)addr, 4096, IOMAP_FULL_CACHING); |
| free_page ((u32)addr); |
| } |
| |
| static struct NCR53c7x0_cmd * |
| allocate_cmd (Scsi_Cmnd *cmd) { |
| struct Scsi_Host *host = cmd->device->host; |
| struct NCR53c7x0_hostdata *hostdata = |
| (struct NCR53c7x0_hostdata *) host->hostdata[0]; |
| u32 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->device->id, cmd->device->lun, (hostdata->cmd_allocated[cmd->
|