| /****************************************************************************** |
| ** Device driver for the PCI-SCSI NCR538XX controller family. |
| ** |
| ** Copyright (C) 1994 Wolfgang Stanglmeier |
| ** |
| ** This program is free software; you can redistribute it and/or modify |
| ** it under the terms of the GNU General Public License as published by |
| ** the Free Software Foundation; either version 2 of the License, or |
| ** (at your option) any later version. |
| ** |
| ** This program is distributed in the hope that it will be useful, |
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| ** GNU General Public License for more details. |
| ** |
| ** You should have received a copy of the GNU General Public License |
| ** along with this program; if not, write to the Free Software |
| ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| ** |
| **----------------------------------------------------------------------------- |
| ** |
| ** This driver has been ported to Linux from the FreeBSD NCR53C8XX driver |
| ** and is currently maintained by |
| ** |
| ** Gerard Roudier <groudier@free.fr> |
| ** |
| ** Being given that this driver originates from the FreeBSD version, and |
| ** in order to keep synergy on both, any suggested enhancements and corrections |
| ** received on Linux are automatically a potential candidate for the FreeBSD |
| ** version. |
| ** |
| ** The original driver has been written for 386bsd and FreeBSD by |
| ** Wolfgang Stanglmeier <wolf@cologne.de> |
| ** Stefan Esser <se@mi.Uni-Koeln.de> |
| ** |
| ** And has been ported to NetBSD by |
| ** Charles M. Hannum <mycroft@gnu.ai.mit.edu> |
| ** |
| **----------------------------------------------------------------------------- |
| ** |
| ** Brief history |
| ** |
| ** December 10 1995 by Gerard Roudier: |
| ** Initial port to Linux. |
| ** |
| ** June 23 1996 by Gerard Roudier: |
| ** Support for 64 bits architectures (Alpha). |
| ** |
| ** November 30 1996 by Gerard Roudier: |
| ** Support for Fast-20 scsi. |
| ** Support for large DMA fifo and 128 dwords bursting. |
| ** |
| ** February 27 1997 by Gerard Roudier: |
| ** Support for Fast-40 scsi. |
| ** Support for on-Board RAM. |
| ** |
| ** May 3 1997 by Gerard Roudier: |
| ** Full support for scsi scripts instructions pre-fetching. |
| ** |
| ** May 19 1997 by Richard Waltham <dormouse@farsrobt.demon.co.uk>: |
| ** Support for NvRAM detection and reading. |
| ** |
| ** August 18 1997 by Cort <cort@cs.nmt.edu>: |
| ** Support for Power/PC (Big Endian). |
| ** |
| ** June 20 1998 by Gerard Roudier |
| ** Support for up to 64 tags per lun. |
| ** O(1) everywhere (C and SCRIPTS) for normal cases. |
| ** Low PCI traffic for command handling when on-chip RAM is present. |
| ** Aggressive SCSI SCRIPTS optimizations. |
| ** |
| ******************************************************************************* |
| */ |
| |
| /* |
| ** Supported SCSI-II features: |
| ** Synchronous negotiation |
| ** Wide negotiation (depends on the NCR Chip) |
| ** Enable disconnection |
| ** Tagged command queuing |
| ** Parity checking |
| ** Etc... |
| ** |
| ** Supported NCR/SYMBIOS chips: |
| ** 53C720 (Wide, Fast SCSI-2, intfly problems) |
| */ |
| |
| /* Name and version of the driver */ |
| #define SCSI_NCR_DRIVER_NAME "ncr53c8xx-3.4.3g" |
| |
| #define SCSI_NCR_DEBUG_FLAGS (0) |
| |
| /*========================================================== |
| ** |
| ** Include files |
| ** |
| **========================================================== |
| */ |
| |
| #include <linux/blkdev.h> |
| #include <linux/delay.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/errno.h> |
| #include <linux/init.h> |
| #include <linux/interrupt.h> |
| #include <linux/ioport.h> |
| #include <linux/mm.h> |
| #include <linux/module.h> |
| #include <linux/sched.h> |
| #include <linux/signal.h> |
| #include <linux/spinlock.h> |
| #include <linux/stat.h> |
| #include <linux/string.h> |
| #include <linux/time.h> |
| #include <linux/timer.h> |
| #include <linux/types.h> |
| |
| #include <asm/dma.h> |
| #include <asm/io.h> |
| #include <asm/system.h> |
| |
| #include <scsi/scsi.h> |
| #include <scsi/scsi_cmnd.h> |
| #include <scsi/scsi_device.h> |
| #include <scsi/scsi_tcq.h> |
| #include <scsi/scsi_transport.h> |
| #include <scsi/scsi_transport_spi.h> |
| |
| #include "ncr53c8xx.h" |
| |
| #define NAME53C "ncr53c" |
| #define NAME53C8XX "ncr53c8xx" |
| |
| #include "sym53c8xx_comm.h" |
| |
| |
| /*========================================================== |
| ** |
| ** The CCB done queue uses an array of CCB virtual |
| ** addresses. Empty entries are flagged using the bogus |
| ** virtual address 0xffffffff. |
| ** |
| ** Since PCI ensures that only aligned DWORDs are accessed |
| ** atomically, 64 bit little-endian architecture requires |
| ** to test the high order DWORD of the entry to determine |
| ** if it is empty or valid. |
| ** |
| ** BTW, I will make things differently as soon as I will |
| ** have a better idea, but this is simple and should work. |
| ** |
| **========================================================== |
| */ |
| |
| #define SCSI_NCR_CCB_DONE_SUPPORT |
| #ifdef SCSI_NCR_CCB_DONE_SUPPORT |
| |
| #define MAX_DONE 24 |
| #define CCB_DONE_EMPTY 0xffffffffUL |
| |
| /* All 32 bit architectures */ |
| #if BITS_PER_LONG == 32 |
| #define CCB_DONE_VALID(cp) (((u_long) cp) != CCB_DONE_EMPTY) |
| |
| /* All > 32 bit (64 bit) architectures regardless endian-ness */ |
| #else |
| #define CCB_DONE_VALID(cp) \ |
| ((((u_long) cp) & 0xffffffff00000000ul) && \ |
| (((u_long) cp) & 0xfffffffful) != CCB_DONE_EMPTY) |
| #endif |
| |
| #endif /* SCSI_NCR_CCB_DONE_SUPPORT */ |
| |
| /*========================================================== |
| ** |
| ** Configuration and Debugging |
| ** |
| **========================================================== |
| */ |
| |
| /* |
| ** SCSI address of this device. |
| ** The boot routines should have set it. |
| ** If not, use this. |
| */ |
| |
| #ifndef SCSI_NCR_MYADDR |
| #define SCSI_NCR_MYADDR (7) |
| #endif |
| |
| /* |
| ** The maximum number of tags per logic unit. |
| ** Used only for disk devices that support tags. |
| */ |
| |
| #ifndef SCSI_NCR_MAX_TAGS |
| #define SCSI_NCR_MAX_TAGS (8) |
| #endif |
| |
| /* |
| ** TAGS are actually limited to 64 tags/lun. |
| ** We need to deal with power of 2, for alignment constraints. |
| */ |
| #if SCSI_NCR_MAX_TAGS > 64 |
| #define MAX_TAGS (64) |
| #else |
| #define MAX_TAGS SCSI_NCR_MAX_TAGS |
| #endif |
| |
| #define NO_TAG (255) |
| |
| /* |
| ** Choose appropriate type for tag bitmap. |
| */ |
| #if MAX_TAGS > 32 |
| typedef u64 tagmap_t; |
| #else |
| typedef u32 tagmap_t; |
| #endif |
| |
| /* |
| ** Number of targets supported by the driver. |
| ** n permits target numbers 0..n-1. |
| ** Default is 16, meaning targets #0..#15. |
| ** #7 .. is myself. |
| */ |
| |
| #ifdef SCSI_NCR_MAX_TARGET |
| #define MAX_TARGET (SCSI_NCR_MAX_TARGET) |
| #else |
| #define MAX_TARGET (16) |
| #endif |
| |
| /* |
| ** Number of logic units supported by the driver. |
| ** n enables logic unit numbers 0..n-1. |
| ** The common SCSI devices require only |
| ** one lun, so take 1 as the default. |
| */ |
| |
| #ifdef SCSI_NCR_MAX_LUN |
| #define MAX_LUN SCSI_NCR_MAX_LUN |
| #else |
| #define MAX_LUN (1) |
| #endif |
| |
| /* |
| ** Asynchronous pre-scaler (ns). Shall be 40 |
| */ |
| |
| #ifndef SCSI_NCR_MIN_ASYNC |
| #define SCSI_NCR_MIN_ASYNC (40) |
| #endif |
| |
| /* |
| ** The maximum number of jobs scheduled for starting. |
| ** There should be one slot per target, and one slot |
| ** for each tag of each target in use. |
| ** The calculation below is actually quite silly ... |
| */ |
| |
| #ifdef SCSI_NCR_CAN_QUEUE |
| #define MAX_START (SCSI_NCR_CAN_QUEUE + 4) |
| #else |
| #define MAX_START (MAX_TARGET + 7 * MAX_TAGS) |
| #endif |
| |
| /* |
| ** We limit the max number of pending IO to 250. |
| ** since we donnot want to allocate more than 1 |
| ** PAGE for 'scripth'. |
| */ |
| #if MAX_START > 250 |
| #undef MAX_START |
| #define MAX_START 250 |
| #endif |
| |
| /* |
| ** The maximum number of segments a transfer is split into. |
| ** We support up to 127 segments for both read and write. |
| ** The data scripts are broken into 2 sub-scripts. |
| ** 80 (MAX_SCATTERL) segments are moved from a sub-script |
| ** in on-chip RAM. This makes data transfers shorter than |
| ** 80k (assuming 1k fs) as fast as possible. |
| */ |
| |
| #define MAX_SCATTER (SCSI_NCR_MAX_SCATTER) |
| |
| #if (MAX_SCATTER > 80) |
| #define MAX_SCATTERL 80 |
| #define MAX_SCATTERH (MAX_SCATTER - MAX_SCATTERL) |
| #else |
| #define MAX_SCATTERL (MAX_SCATTER-1) |
| #define MAX_SCATTERH 1 |
| #endif |
| |
| /* |
| ** other |
| */ |
| |
| #define NCR_SNOOP_TIMEOUT (1000000) |
| |
| /* |
| ** Other definitions |
| */ |
| |
| #define ScsiResult(host_code, scsi_code) (((host_code) << 16) + ((scsi_code) & 0x7f)) |
| |
| #define initverbose (driver_setup.verbose) |
| #define bootverbose (np->verbose) |
| |
| /*========================================================== |
| ** |
| ** Command control block states. |
| ** |
| **========================================================== |
| */ |
| |
| #define HS_IDLE (0) |
| #define HS_BUSY (1) |
| #define HS_NEGOTIATE (2) /* sync/wide data transfer*/ |
| #define HS_DISCONNECT (3) /* Disconnected by target */ |
| |
| #define HS_DONEMASK (0x80) |
| #define HS_COMPLETE (4|HS_DONEMASK) |
| #define HS_SEL_TIMEOUT (5|HS_DONEMASK) /* Selection timeout */ |
| #define HS_RESET (6|HS_DONEMASK) /* SCSI reset */ |
| #define HS_ABORTED (7|HS_DONEMASK) /* Transfer aborted */ |
| #define HS_TIMEOUT (8|HS_DONEMASK) /* Software timeout */ |
| #define HS_FAIL (9|HS_DONEMASK) /* SCSI or PCI bus errors */ |
| #define HS_UNEXPECTED (10|HS_DONEMASK)/* Unexpected disconnect */ |
| |
| /* |
| ** Invalid host status values used by the SCRIPTS processor |
| ** when the nexus is not fully identified. |
| ** Shall never appear in a CCB. |
| */ |
| |
| #define HS_INVALMASK (0x40) |
| #define HS_SELECTING (0|HS_INVALMASK) |
| #define HS_IN_RESELECT (1|HS_INVALMASK) |
| #define HS_STARTING (2|HS_INVALMASK) |
| |
| /* |
| ** Flags set by the SCRIPT processor for commands |
| ** that have been skipped. |
| */ |
| #define HS_SKIPMASK (0x20) |
| |
| /*========================================================== |
| ** |
| ** Software Interrupt Codes |
| ** |
| **========================================================== |
| */ |
| |
| #define SIR_BAD_STATUS (1) |
| #define SIR_XXXXXXXXXX (2) |
| #define SIR_NEGO_SYNC (3) |
| #define SIR_NEGO_WIDE (4) |
| #define SIR_NEGO_FAILED (5) |
| #define SIR_NEGO_PROTO (6) |
| #define SIR_REJECT_RECEIVED (7) |
| #define SIR_REJECT_SENT (8) |
| #define SIR_IGN_RESIDUE (9) |
| #define SIR_MISSING_SAVE (10) |
| #define SIR_RESEL_NO_MSG_IN (11) |
| #define SIR_RESEL_NO_IDENTIFY (12) |
| #define SIR_RESEL_BAD_LUN (13) |
| #define SIR_RESEL_BAD_TARGET (14) |
| #define SIR_RESEL_BAD_I_T_L (15) |
| #define SIR_RESEL_BAD_I_T_L_Q (16) |
| #define SIR_DONE_OVERFLOW (17) |
| #define SIR_INTFLY (18) |
| #define SIR_MAX (18) |
| |
| /*========================================================== |
| ** |
| ** Extended error codes. |
| ** xerr_status field of struct ccb. |
| ** |
| **========================================================== |
| */ |
| |
| #define XE_OK (0) |
| #define XE_EXTRA_DATA (1) /* unexpected data phase */ |
| #define XE_BAD_PHASE (2) /* illegal phase (4/5) */ |
| |
| /*========================================================== |
| ** |
| ** Negotiation status. |
| ** nego_status field of struct ccb. |
| ** |
| **========================================================== |
| */ |
| |
| #define NS_NOCHANGE (0) |
| #define NS_SYNC (1) |
| #define NS_WIDE (2) |
| #define NS_PPR (4) |
| |
| /*========================================================== |
| ** |
| ** Misc. |
| ** |
| **========================================================== |
| */ |
| |
| #define CCB_MAGIC (0xf2691ad2) |
| |
| /*========================================================== |
| ** |
| ** Declaration of structs. |
| ** |
| **========================================================== |
| */ |
| |
| static struct scsi_transport_template *ncr53c8xx_transport_template = NULL; |
| |
| struct tcb; |
| struct lcb; |
| struct ccb; |
| struct ncb; |
| struct script; |
| |
| struct link { |
| ncrcmd l_cmd; |
| ncrcmd l_paddr; |
| }; |
| |
| struct usrcmd { |
| u_long target; |
| u_long lun; |
| u_long data; |
| u_long cmd; |
| }; |
| |
| #define UC_SETSYNC 10 |
| #define UC_SETTAGS 11 |
| #define UC_SETDEBUG 12 |
| #define UC_SETORDER 13 |
| #define UC_SETWIDE 14 |
| #define UC_SETFLAG 15 |
| #define UC_SETVERBOSE 17 |
| |
| #define UF_TRACE (0x01) |
| #define UF_NODISC (0x02) |
| #define UF_NOSCAN (0x04) |
| |
| /*======================================================================== |
| ** |
| ** Declaration of structs: target control block |
| ** |
| **======================================================================== |
| */ |
| struct tcb { |
| /*---------------------------------------------------------------- |
| ** During reselection the ncr jumps to this point with SFBR |
| ** set to the encoded target number with bit 7 set. |
| ** if it's not this target, jump to the next. |
| ** |
| ** JUMP IF (SFBR != #target#), @(next tcb) |
| **---------------------------------------------------------------- |
| */ |
| struct link jump_tcb; |
| |
| /*---------------------------------------------------------------- |
| ** Load the actual values for the sxfer and the scntl3 |
| ** register (sync/wide mode). |
| ** |
| ** SCR_COPY (1), @(sval field of this tcb), @(sxfer register) |
| ** SCR_COPY (1), @(wval field of this tcb), @(scntl3 register) |
| **---------------------------------------------------------------- |
| */ |
| ncrcmd getscr[6]; |
| |
| /*---------------------------------------------------------------- |
| ** Get the IDENTIFY message and load the LUN to SFBR. |
| ** |
| ** CALL, <RESEL_LUN> |
| **---------------------------------------------------------------- |
| */ |
| struct link call_lun; |
| |
| /*---------------------------------------------------------------- |
| ** Now look for the right lun. |
| ** |
| ** For i = 0 to 3 |
| ** SCR_JUMP ^ IFTRUE(MASK(i, 3)), @(first lcb mod. i) |
| ** |
| ** Recent chips will prefetch the 4 JUMPS using only 1 burst. |
| ** It is kind of hashcoding. |
| **---------------------------------------------------------------- |
| */ |
| struct link jump_lcb[4]; /* JUMPs for reselection */ |
| struct lcb * lp[MAX_LUN]; /* The lcb's of this tcb */ |
| |
| /*---------------------------------------------------------------- |
| ** Pointer to the ccb used for negotiation. |
| ** Prevent from starting a negotiation for all queued commands |
| ** when tagged command queuing is enabled. |
| **---------------------------------------------------------------- |
| */ |
| struct ccb * nego_cp; |
| |
| /*---------------------------------------------------------------- |
| ** statistical data |
| **---------------------------------------------------------------- |
| */ |
| u_long transfers; |
| u_long bytes; |
| |
| /*---------------------------------------------------------------- |
| ** negotiation of wide and synch transfer and device quirks. |
| **---------------------------------------------------------------- |
| */ |
| #ifdef SCSI_NCR_BIG_ENDIAN |
| /*0*/ u16 period; |
| /*2*/ u_char sval; |
| /*3*/ u_char minsync; |
| /*0*/ u_char wval; |
| /*1*/ u_char widedone; |
| /*2*/ u_char quirks; |
| /*3*/ u_char maxoffs; |
| #else |
| /*0*/ u_char minsync; |
| /*1*/ u_char sval; |
| /*2*/ u16 period; |
| /*0*/ u_char maxoffs; |
| /*1*/ u_char quirks; |
| /*2*/ u_char widedone; |
| /*3*/ u_char wval; |
| #endif |
| |
| /* User settable limits and options. */ |
| u_char usrsync; |
| u_char usrwide; |
| u_char usrtags; |
| u_char usrflag; |
| struct scsi_target *starget; |
| }; |
| |
| /*======================================================================== |
| ** |
| ** Declaration of structs: lun control block |
| ** |
| **======================================================================== |
| */ |
| struct lcb { |
| /*---------------------------------------------------------------- |
| ** During reselection the ncr jumps to this point |
| ** with SFBR set to the "Identify" message. |
| ** if it's not this lun, jump to the next. |
| ** |
| ** JUMP IF (SFBR != #lun#), @(next lcb of this target) |
| ** |
| ** It is this lun. Load TEMP with the nexus jumps table |
| ** address and jump to RESEL_TAG (or RESEL_NOTAG). |
| ** |
| ** SCR_COPY (4), p_jump_ccb, TEMP, |
| ** SCR_JUMP, <RESEL_TAG> |
| **---------------------------------------------------------------- |
| */ |
| struct link jump_lcb; |
| ncrcmd load_jump_ccb[3]; |
| struct link jump_tag; |
| ncrcmd p_jump_ccb; /* Jump table bus address */ |
| |
| /*---------------------------------------------------------------- |
| ** Jump table used by the script processor to directly jump |
| ** to the CCB corresponding to the reselected nexus. |
| ** Address is allocated on 256 bytes boundary in order to |
| ** allow 8 bit calculation of the tag jump entry for up to |
| ** 64 possible tags. |
| **---------------------------------------------------------------- |
| */ |
| u32 jump_ccb_0; /* Default table if no tags */ |
| u32 *jump_ccb; /* Virtual address */ |
| |
| /*---------------------------------------------------------------- |
| ** CCB queue management. |
| **---------------------------------------------------------------- |
| */ |
| struct list_head free_ccbq; /* Queue of available CCBs */ |
| struct list_head busy_ccbq; /* Queue of busy CCBs */ |
| struct list_head wait_ccbq; /* Queue of waiting for IO CCBs */ |
| struct list_head skip_ccbq; /* Queue of skipped CCBs */ |
| u_char actccbs; /* Number of allocated CCBs */ |
| u_char busyccbs; /* CCBs busy for this lun */ |
| u_char queuedccbs; /* CCBs queued to the controller*/ |
| u_char queuedepth; /* Queue depth for this lun */ |
| u_char scdev_depth; /* SCSI device queue depth */ |
| u_char maxnxs; /* Max possible nexuses */ |
| |
| /*---------------------------------------------------------------- |
| ** Control of tagged command queuing. |
| ** Tags allocation is performed using a circular buffer. |
| ** This avoids using a loop for tag allocation. |
| **---------------------------------------------------------------- |
| */ |
| u_char ia_tag; /* Allocation index */ |
| u_char if_tag; /* Freeing index */ |
| u_char cb_tags[MAX_TAGS]; /* Circular tags buffer */ |
| u_char usetags; /* Command queuing is active */ |
| u_char maxtags; /* Max nr of tags asked by user */ |
| u_char numtags; /* Current number of tags */ |
| |
| /*---------------------------------------------------------------- |
| ** QUEUE FULL control and ORDERED tag control. |
| **---------------------------------------------------------------- |
| */ |
| /*---------------------------------------------------------------- |
| ** QUEUE FULL and ORDERED tag control. |
| **---------------------------------------------------------------- |
| */ |
| u16 num_good; /* Nr of GOOD since QUEUE FULL */ |
| tagmap_t tags_umap; /* Used tags bitmap */ |
| tagmap_t tags_smap; /* Tags in use at 'tag_stime' */ |
| u_long tags_stime; /* Last time we set smap=umap */ |
| struct ccb * held_ccb; /* CCB held for QUEUE FULL */ |
| }; |
| |
| /*======================================================================== |
| ** |
| ** Declaration of structs: the launch script. |
| ** |
| **======================================================================== |
| ** |
| ** It is part of the CCB and is called by the scripts processor to |
| ** start or restart the data structure (nexus). |
| ** This 6 DWORDs mini script makes use of prefetching. |
| ** |
| **------------------------------------------------------------------------ |
| */ |
| struct launch { |
| /*---------------------------------------------------------------- |
| ** SCR_COPY(4), @(p_phys), @(dsa register) |
| ** SCR_JUMP, @(scheduler_point) |
| **---------------------------------------------------------------- |
| */ |
| ncrcmd setup_dsa[3]; /* Copy 'phys' address to dsa */ |
| struct link schedule; /* Jump to scheduler point */ |
| ncrcmd p_phys; /* 'phys' header bus address */ |
| }; |
| |
| /*======================================================================== |
| ** |
| ** Declaration of structs: global HEADER. |
| ** |
| **======================================================================== |
| ** |
| ** This substructure is copied from the ccb to a global address after |
| ** selection (or reselection) and copied back before disconnect. |
| ** |
| ** These fields are accessible to the script processor. |
| ** |
| **------------------------------------------------------------------------ |
| */ |
| |
| struct head { |
| /*---------------------------------------------------------------- |
| ** Saved data pointer. |
| ** Points to the position in the script responsible for the |
| ** actual transfer transfer of data. |
| ** It's written after reception of a SAVE_DATA_POINTER message. |
| ** The goalpointer points after the last transfer command. |
| **---------------------------------------------------------------- |
| */ |
| u32 savep; |
| u32 lastp; |
| u32 goalp; |
| |
| /*---------------------------------------------------------------- |
| ** Alternate data pointer. |
| ** They are copied back to savep/lastp/goalp by the SCRIPTS |
| ** when the direction is unknown and the device claims data out. |
| **---------------------------------------------------------------- |
| */ |
| u32 wlastp; |
| u32 wgoalp; |
| |
| /*---------------------------------------------------------------- |
| ** The virtual address of the ccb containing this header. |
| **---------------------------------------------------------------- |
| */ |
| struct ccb * cp; |
| |
| /*---------------------------------------------------------------- |
| ** Status fields. |
| **---------------------------------------------------------------- |
| */ |
| u_char scr_st[4]; /* script status */ |
| u_char status[4]; /* host status. must be the */ |
| /* last DWORD of the header. */ |
| }; |
| |
| /* |
| ** The status bytes are used by the host and the script processor. |
| ** |
| ** The byte corresponding to the host_status must be stored in the |
| ** last DWORD of the CCB header since it is used for command |
| ** completion (ncr_wakeup()). Doing so, we are sure that the header |
| ** has been entirely copied back to the CCB when the host_status is |
| ** seen complete by the CPU. |
| ** |
| ** The last four bytes (status[4]) are copied to the scratchb register |
| ** (declared as scr0..scr3 in ncr_reg.h) just after the select/reselect, |
| ** and copied back just after disconnecting. |
| ** Inside the script the XX_REG are used. |
| ** |
| ** The first four bytes (scr_st[4]) are used inside the script by |
| ** "COPY" commands. |
| ** Because source and destination must have the same alignment |
| ** in a DWORD, the fields HAVE to be at the choosen offsets. |
| ** xerr_st 0 (0x34) scratcha |
| ** sync_st 1 (0x05) sxfer |
| ** wide_st 3 (0x03) scntl3 |
| */ |
| |
| /* |
| ** Last four bytes (script) |
| */ |
| #define QU_REG scr0 |
| #define HS_REG scr1 |
| #define HS_PRT nc_scr1 |
| #define SS_REG scr2 |
| #define SS_PRT nc_scr2 |
| #define PS_REG scr3 |
| |
| /* |
| ** Last four bytes (host) |
| */ |
| #ifdef SCSI_NCR_BIG_ENDIAN |
| #define actualquirks phys.header.status[3] |
| #define host_status phys.header.status[2] |
| #define scsi_status phys.header.status[1] |
| #define parity_status phys.header.status[0] |
| #else |
| #define actualquirks phys.header.status[0] |
| #define host_status phys.header.status[1] |
| #define scsi_status phys.header.status[2] |
| #define parity_status phys.header.status[3] |
| #endif |
| |
| /* |
| ** First four bytes (script) |
| */ |
| #define xerr_st header.scr_st[0] |
| #define sync_st header.scr_st[1] |
| #define nego_st header.scr_st[2] |
| #define wide_st header.scr_st[3] |
| |
| /* |
| ** First four bytes (host) |
| */ |
| #define xerr_status phys.xerr_st |
| #define nego_status phys.nego_st |
| |
| #if 0 |
| #define sync_status phys.sync_st |
| #define wide_status phys.wide_st |
| #endif |
| |
| /*========================================================== |
| ** |
| ** Declaration of structs: Data structure block |
| ** |
| **========================================================== |
| ** |
| ** During execution of a ccb by the script processor, |
| ** the DSA (data structure address) register points |
| ** to this substructure of the ccb. |
| ** This substructure contains the header with |
| ** the script-processor-changable data and |
| ** data blocks for the indirect move commands. |
| ** |
| **---------------------------------------------------------- |
| */ |
| |
| struct dsb { |
| |
| /* |
| ** Header. |
| */ |
| |
| struct head header; |
| |
| /* |
| ** Table data for Script |
| */ |
| |
| struct scr_tblsel select; |
| struct scr_tblmove smsg ; |
| struct scr_tblmove cmd ; |
| struct scr_tblmove sense ; |
| struct scr_tblmove data[MAX_SCATTER]; |
| }; |
| |
| |
| /*======================================================================== |
| ** |
| ** Declaration of structs: Command control block. |
| ** |
| **======================================================================== |
| */ |
| struct ccb { |
| /*---------------------------------------------------------------- |
| ** This is the data structure which is pointed by the DSA |
| ** register when it is executed by the script processor. |
| ** It must be the first entry because it contains the header |
| ** as first entry that must be cache line aligned. |
| **---------------------------------------------------------------- |
| */ |
| struct dsb phys; |
| |
| /*---------------------------------------------------------------- |
| ** Mini-script used at CCB execution start-up. |
| ** Load the DSA with the data structure address (phys) and |
| ** jump to SELECT. Jump to CANCEL if CCB is to be canceled. |
| **---------------------------------------------------------------- |
| */ |
| struct launch start; |
| |
| /*---------------------------------------------------------------- |
| ** Mini-script used at CCB relection to restart the nexus. |
| ** Load the DSA with the data structure address (phys) and |
| ** jump to RESEL_DSA. Jump to ABORT if CCB is to be aborted. |
| **---------------------------------------------------------------- |
| */ |
| struct launch restart; |
| |
| /*---------------------------------------------------------------- |
| ** If a data transfer phase is terminated too early |
| ** (after reception of a message (i.e. DISCONNECT)), |
| ** we have to prepare a mini script to transfer |
| ** the rest of the data. |
| **---------------------------------------------------------------- |
| */ |
| ncrcmd patch[8]; |
| |
| /*---------------------------------------------------------------- |
| ** The general SCSI driver provides a |
| ** pointer to a control block. |
| **---------------------------------------------------------------- |
| */ |
| struct scsi_cmnd *cmd; /* SCSI command */ |
| u_char cdb_buf[16]; /* Copy of CDB */ |
| u_char sense_buf[64]; |
| int data_len; /* Total data length */ |
| |
| /*---------------------------------------------------------------- |
| ** Message areas. |
| ** We prepare a message to be sent after selection. |
| ** We may use a second one if the command is rescheduled |
| ** due to GETCC or QFULL. |
| ** Contents are IDENTIFY and SIMPLE_TAG. |
| ** While negotiating sync or wide transfer, |
| ** a SDTR or WDTR message is appended. |
| **---------------------------------------------------------------- |
| */ |
| u_char scsi_smsg [8]; |
| u_char scsi_smsg2[8]; |
| |
| /*---------------------------------------------------------------- |
| ** Other fields. |
| **---------------------------------------------------------------- |
| */ |
| u_long p_ccb; /* BUS address of this CCB */ |
| u_char sensecmd[6]; /* Sense command */ |
| u_char tag; /* Tag for this transfer */ |
| /* 255 means no tag */ |
| u_char target; |
| u_char lun; |
| u_char queued; |
| u_char auto_sense; |
| struct ccb * link_ccb; /* Host adapter CCB chain */ |
| struct list_head link_ccbq; /* Link to unit CCB queue */ |
| u32 startp; /* Initial data pointer */ |
| u_long magic; /* Free / busy CCB flag */ |
| }; |
| |
| #define CCB_PHYS(cp,lbl) (cp->p_ccb + offsetof(struct ccb, lbl)) |
| |
| |
| /*======================================================================== |
| ** |
| ** Declaration of structs: NCR device descriptor |
| ** |
| **======================================================================== |
| */ |
| struct ncb { |
| /*---------------------------------------------------------------- |
| ** The global header. |
| ** It is accessible to both the host and the script processor. |
| ** Must be cache line size aligned (32 for x86) in order to |
| ** allow cache line bursting when it is copied to/from CCB. |
| **---------------------------------------------------------------- |
| */ |
| struct head header; |
| |
| /*---------------------------------------------------------------- |
| ** CCBs management queues. |
| **---------------------------------------------------------------- |
| */ |
| struct scsi_cmnd *waiting_list; /* Commands waiting for a CCB */ |
| /* when lcb is not allocated. */ |
| struct scsi_cmnd *done_list; /* Commands waiting for done() */ |
| /* callback to be invoked. */ |
| spinlock_t smp_lock; /* Lock for SMP threading */ |
| |
| /*---------------------------------------------------------------- |
| ** Chip and controller indentification. |
| **---------------------------------------------------------------- |
| */ |
| int unit; /* Unit number */ |
| char inst_name[16]; /* ncb instance name */ |
| |
| /*---------------------------------------------------------------- |
| ** Initial value of some IO register bits. |
| ** These values are assumed to have been set by BIOS, and may |
| ** be used for probing adapter implementation differences. |
| **---------------------------------------------------------------- |
| */ |
| u_char sv_scntl0, sv_scntl3, sv_dmode, sv_dcntl, sv_ctest0, sv_ctest3, |
| sv_ctest4, sv_ctest5, sv_gpcntl, sv_stest2, sv_stest4; |
| |
| /*---------------------------------------------------------------- |
| ** Actual initial value of IO register bits used by the |
| ** driver. They are loaded at initialisation according to |
| ** features that are to be enabled. |
| **---------------------------------------------------------------- |
| */ |
| u_char rv_scntl0, rv_scntl3, rv_dmode, rv_dcntl, rv_ctest0, rv_ctest3, |
| rv_ctest4, rv_ctest5, rv_stest2; |
| |
| /*---------------------------------------------------------------- |
| ** Targets management. |
| ** During reselection the ncr jumps to jump_tcb. |
| ** The SFBR register is loaded with the encoded target id. |
| ** For i = 0 to 3 |
| ** SCR_JUMP ^ IFTRUE(MASK(i, 3)), @(next tcb mod. i) |
| ** |
| ** Recent chips will prefetch the 4 JUMPS using only 1 burst. |
| ** It is kind of hashcoding. |
| **---------------------------------------------------------------- |
| */ |
| struct link jump_tcb[4]; /* JUMPs for reselection */ |
| struct tcb target[MAX_TARGET]; /* Target data */ |
| |
| /*---------------------------------------------------------------- |
| ** Virtual and physical bus addresses of the chip. |
| **---------------------------------------------------------------- |
| */ |
| void __iomem *vaddr; /* Virtual and bus address of */ |
| unsigned long paddr; /* chip's IO registers. */ |
| unsigned long paddr2; /* On-chip RAM bus address. */ |
| volatile /* Pointer to volatile for */ |
| struct ncr_reg __iomem *reg; /* memory mapped IO. */ |
| |
| /*---------------------------------------------------------------- |
| ** SCRIPTS virtual and physical bus addresses. |
| ** 'script' is loaded in the on-chip RAM if present. |
| ** 'scripth' stays in main memory. |
| **---------------------------------------------------------------- |
| */ |
| struct script *script0; /* Copies of script and scripth */ |
| struct scripth *scripth0; /* relocated for this ncb. */ |
| struct scripth *scripth; /* Actual scripth virt. address */ |
| u_long p_script; /* Actual script and scripth */ |
| u_long p_scripth; /* bus addresses. */ |
| |
| /*---------------------------------------------------------------- |
| ** General controller parameters and configuration. |
| **---------------------------------------------------------------- |
| */ |
| struct device *dev; |
| u_char revision_id; /* PCI device revision id */ |
| u32 irq; /* IRQ level */ |
| u32 features; /* Chip features map */ |
| u_char myaddr; /* SCSI id of the adapter */ |
| u_char maxburst; /* log base 2 of dwords burst */ |
| u_char maxwide; /* Maximum transfer width */ |
| u_char minsync; /* Minimum sync period factor */ |
| u_char maxsync; /* Maximum sync period factor */ |
| u_char maxoffs; /* Max scsi offset */ |
| u_char multiplier; /* Clock multiplier (1,2,4) */ |
| u_char clock_divn; /* Number of clock divisors */ |
| u_long clock_khz; /* SCSI clock frequency in KHz */ |
| |
| /*---------------------------------------------------------------- |
| ** Start queue management. |
| ** It is filled up by the host processor and accessed by the |
| ** SCRIPTS processor in order to start SCSI commands. |
| **---------------------------------------------------------------- |
| */ |
| u16 squeueput; /* Next free slot of the queue */ |
| u16 actccbs; /* Number of allocated CCBs */ |
| u16 queuedccbs; /* Number of CCBs in start queue*/ |
| u16 queuedepth; /* Start queue depth */ |
| |
| /*---------------------------------------------------------------- |
| ** Timeout handler. |
| **---------------------------------------------------------------- |
| */ |
| struct timer_list timer; /* Timer handler link header */ |
| u_long lasttime; |
| u_long settle_time; /* Resetting the SCSI BUS */ |
| |
| /*---------------------------------------------------------------- |
| ** Debugging and profiling. |
| **---------------------------------------------------------------- |
| */ |
| struct ncr_reg regdump; /* Register dump */ |
| u_long regtime; /* Time it has been done */ |
| |
| /*---------------------------------------------------------------- |
| ** Miscellaneous buffers accessed by the scripts-processor. |
| ** They shall be DWORD aligned, because they may be read or |
| ** written with a SCR_COPY script command. |
| **---------------------------------------------------------------- |
| */ |
| u_char msgout[8]; /* Buffer for MESSAGE OUT */ |
| u_char msgin [8]; /* Buffer for MESSAGE IN */ |
| u32 lastmsg; /* Last SCSI message sent */ |
| u_char scratch; /* Scratch for SCSI receive */ |
| |
| /*---------------------------------------------------------------- |
| ** Miscellaneous configuration and status parameters. |
| **---------------------------------------------------------------- |
| */ |
| u_char disc; /* Diconnection allowed */ |
| u_char scsi_mode; /* Current SCSI BUS mode */ |
| u_char order; /* Tag order to use */ |
| u_char verbose; /* Verbosity for this controller*/ |
| int ncr_cache; /* Used for cache test at init. */ |
| u_long p_ncb; /* BUS address of this NCB */ |
| |
| /*---------------------------------------------------------------- |
| ** Command completion handling. |
| **---------------------------------------------------------------- |
| */ |
| #ifdef SCSI_NCR_CCB_DONE_SUPPORT |
| struct ccb *(ccb_done[MAX_DONE]); |
| int ccb_done_ic; |
| #endif |
| /*---------------------------------------------------------------- |
| ** Fields that should be removed or changed. |
| **---------------------------------------------------------------- |
| */ |
| struct ccb *ccb; /* Global CCB */ |
| struct usrcmd user; /* Command from user */ |
| volatile u_char release_stage; /* Synchronisation stage on release */ |
| }; |
| |
| #define NCB_SCRIPT_PHYS(np,lbl) (np->p_script + offsetof (struct script, lbl)) |
| #define NCB_SCRIPTH_PHYS(np,lbl) (np->p_scripth + offsetof (struct scripth,lbl)) |
| |
| /*========================================================== |
| ** |
| ** |
| ** Script for NCR-Processor. |
| ** |
| ** Use ncr_script_fill() to create the variable parts. |
| ** Use ncr_script_copy_and_bind() to make a copy and |
| ** bind to physical addresses. |
| ** |
| ** |
| **========================================================== |
| ** |
| ** We have to know the offsets of all labels before |
| ** we reach them (for forward jumps). |
| ** Therefore we declare a struct here. |
| ** If you make changes inside the script, |
| ** DONT FORGET TO CHANGE THE LENGTHS HERE! |
| ** |
| **---------------------------------------------------------- |
| */ |
| |
| /* |
| ** For HP Zalon/53c720 systems, the Zalon interface |
| ** between CPU and 53c720 does prefetches, which causes |
| ** problems with self modifying scripts. The problem |
| ** is overcome by calling a dummy subroutine after each |
| ** modification, to force a refetch of the script on |
| ** return from the subroutine. |
| */ |
| |
| #ifdef CONFIG_NCR53C8XX_PREFETCH |
| #define PREFETCH_FLUSH_CNT 2 |
| #define PREFETCH_FLUSH SCR_CALL, PADDRH (wait_dma), |
| #else |
| #define PREFETCH_FLUSH_CNT 0 |
| #define PREFETCH_FLUSH |
| #endif |
| |
| /* |
| ** Script fragments which are loaded into the on-chip RAM |
| ** of 825A, 875 and 895 chips. |
| */ |
| struct script { |
| ncrcmd start [ 5]; |
| ncrcmd startpos [ 1]; |
| ncrcmd select [ 6]; |
| ncrcmd select2 [ 9 + PREFETCH_FLUSH_CNT]; |
| ncrcmd loadpos [ 4]; |
| ncrcmd send_ident [ 9]; |
| ncrcmd prepare [ 6]; |
| ncrcmd prepare2 [ 7]; |
| ncrcmd command [ 6]; |
| ncrcmd dispatch [ 32]; |
| ncrcmd clrack [ 4]; |
| ncrcmd no_data [ 17]; |
| ncrcmd status [ 8]; |
| ncrcmd msg_in [ 2]; |
| ncrcmd msg_in2 [ 16]; |
| ncrcmd msg_bad [ 4]; |
| ncrcmd setmsg [ 7]; |
| ncrcmd cleanup [ 6]; |
| ncrcmd complete [ 9]; |
| ncrcmd cleanup_ok [ 8 + PREFETCH_FLUSH_CNT]; |
| ncrcmd cleanup0 [ 1]; |
| #ifndef SCSI_NCR_CCB_DONE_SUPPORT |
| ncrcmd signal [ 12]; |
| #else |
| ncrcmd signal [ 9]; |
| ncrcmd done_pos [ 1]; |
| ncrcmd done_plug [ 2]; |
| ncrcmd done_end [ 7]; |
| #endif |
| ncrcmd save_dp [ 7]; |
| ncrcmd restore_dp [ 5]; |
| ncrcmd disconnect [ 10]; |
| ncrcmd msg_out [ 9]; |
| ncrcmd msg_out_done [ 7]; |
| ncrcmd idle [ 2]; |
| ncrcmd reselect [ 8]; |
| ncrcmd reselected [ 8]; |
| ncrcmd resel_dsa [ 6 + PREFETCH_FLUSH_CNT]; |
| ncrcmd loadpos1 [ 4]; |
| ncrcmd resel_lun [ 6]; |
| ncrcmd resel_tag [ 6]; |
| ncrcmd jump_to_nexus [ 4 + PREFETCH_FLUSH_CNT]; |
| ncrcmd nexus_indirect [ 4]; |
| ncrcmd resel_notag [ 4]; |
| ncrcmd data_in [MAX_SCATTERL * 4]; |
| ncrcmd data_in2 [ 4]; |
| ncrcmd data_out [MAX_SCATTERL * 4]; |
| ncrcmd data_out2 [ 4]; |
| }; |
| |
| /* |
| ** Script fragments which stay in main memory for all chips. |
| */ |
| struct scripth { |
| ncrcmd tryloop [MAX_START*2]; |
| ncrcmd tryloop2 [ 2]; |
| #ifdef SCSI_NCR_CCB_DONE_SUPPORT |
| ncrcmd done_queue [MAX_DONE*5]; |
| ncrcmd done_queue2 [ 2]; |
| #endif |
| ncrcmd select_no_atn [ 8]; |
| ncrcmd cancel [ 4]; |
| ncrcmd skip [ 9 + PREFETCH_FLUSH_CNT]; |
| ncrcmd skip2 [ 19]; |
| ncrcmd par_err_data_in [ 6]; |
| ncrcmd par_err_other [ 4]; |
| ncrcmd msg_reject [ 8]; |
| ncrcmd msg_ign_residue [ 24]; |
| ncrcmd msg_extended [ 10]; |
| ncrcmd msg_ext_2 [ 10]; |
| ncrcmd msg_wdtr [ 14]; |
| ncrcmd send_wdtr [ 7]; |
| ncrcmd msg_ext_3 [ 10]; |
| ncrcmd msg_sdtr [ 14]; |
| ncrcmd send_sdtr [ 7]; |
| ncrcmd nego_bad_phase [ 4]; |
| ncrcmd msg_out_abort [ 10]; |
| ncrcmd hdata_in [MAX_SCATTERH * 4]; |
| ncrcmd hdata_in2 [ 2]; |
| ncrcmd hdata_out [MAX_SCATTERH * 4]; |
| ncrcmd hdata_out2 [ 2]; |
| ncrcmd reset [ 4]; |
| ncrcmd aborttag [ 4]; |
| ncrcmd abort [ 2]; |
| ncrcmd abort_resel [ 20]; |
| ncrcmd resend_ident [ 4]; |
| ncrcmd clratn_go_on [ 3]; |
| ncrcmd nxtdsp_go_on [ 1]; |
| ncrcmd sdata_in [ 8]; |
| ncrcmd data_io [ 18]; |
| ncrcmd bad_identify [ 12]; |
| ncrcmd bad_i_t_l [ 4]; |
| ncrcmd bad_i_t_l_q [ 4]; |
| ncrcmd bad_target [ 8]; |
| ncrcmd bad_status [ 8]; |
| ncrcmd start_ram [ 4 + PREFETCH_FLUSH_CNT]; |
| ncrcmd start_ram0 [ 4]; |
| ncrcmd sto_restart [ 5]; |
| ncrcmd wait_dma [ 2]; |
| ncrcmd snooptest [ 9]; |
| ncrcmd snoopend [ 2]; |
| }; |
| |
| /*========================================================== |
| ** |
| ** |
| ** Function headers. |
| ** |
| ** |
| **========================================================== |
| */ |
| |
| static void ncr_alloc_ccb (struct ncb *np, u_char tn, u_char ln); |
| static void ncr_complete (struct ncb *np, struct ccb *cp); |
| static void ncr_exception (struct ncb *np); |
| static void ncr_free_ccb (struct ncb *np, struct ccb *cp); |
| static void ncr_init_ccb (struct ncb *np, struct ccb *cp); |
| static void ncr_init_tcb (struct ncb *np, u_char tn); |
| static struct lcb * ncr_alloc_lcb (struct ncb *np, u_char tn, u_char ln); |
| static struct lcb * ncr_setup_lcb (struct ncb *np, struct scsi_device *sdev); |
| static void ncr_getclock (struct ncb *np, int mult); |
| static void ncr_selectclock (struct ncb *np, u_char scntl3); |
| static struct ccb *ncr_get_ccb (struct ncb *np, struct scsi_cmnd *cmd); |
| static void ncr_chip_reset (struct ncb *np, int delay); |
| static void ncr_init (struct ncb *np, int reset, char * msg, u_long code); |
| static int ncr_int_sbmc (struct ncb *np); |
| static int ncr_int_par (struct ncb *np); |
| static void ncr_int_ma (struct ncb *np); |
| static void ncr_int_sir (struct ncb *np); |
| static void ncr_int_sto (struct ncb *np); |
| static void ncr_negotiate (struct ncb* np, struct tcb* tp); |
| static int ncr_prepare_nego(struct ncb *np, struct ccb *cp, u_char *msgptr); |
| |
| static void ncr_script_copy_and_bind |
| (struct ncb *np, ncrcmd *src, ncrcmd *dst, int len); |
| static void ncr_script_fill (struct script * scr, struct scripth * scripth); |
| static int ncr_scatter (struct ncb *np, struct ccb *cp, struct scsi_cmnd *cmd); |
| static void ncr_getsync (struct ncb *np, u_char sfac, u_char *fakp, u_char *scntl3p); |
| static void ncr_setsync (struct ncb *np, struct ccb *cp, u_char scntl3, u_char sxfer); |
| static void ncr_setup_tags (struct ncb *np, struct scsi_device *sdev); |
| static void ncr_setwide (struct ncb *np, struct ccb *cp, u_char wide, u_char ack); |
| static int ncr_snooptest (struct ncb *np); |
| static void ncr_timeout (struct ncb *np); |
| static void ncr_wakeup (struct ncb *np, u_long code); |
| static void ncr_wakeup_done (struct ncb *np); |
| static void ncr_start_next_ccb (struct ncb *np, struct lcb * lp, int maxn); |
| static void ncr_put_start_queue(struct ncb *np, struct ccb *cp); |
| |
| static void insert_into_waiting_list(struct ncb *np, struct scsi_cmnd *cmd); |
| static struct scsi_cmnd *retrieve_from_waiting_list(int to_remove, struct ncb *np, struct scsi_cmnd *cmd); |
| static void process_waiting_list(struct ncb *np, int sts); |
| |
| #define remove_from_waiting_list(np, cmd) \ |
| retrieve_from_waiting_list(1, (np), (cmd)) |
| #define requeue_waiting_list(np) process_waiting_list((np), DID_OK) |
| #define reset_waiting_list(np) process_waiting_list((np), DID_RESET) |
| |
| static inline char *ncr_name (struct ncb *np) |
| { |
| return np->inst_name; |
| } |
| |
| |
| /*========================================================== |
| ** |
| ** |
| ** Scripts for NCR-Processor. |
| ** |
| ** Use ncr_script_bind for binding to physical addresses. |
| ** |
| ** |
| **========================================================== |
| ** |
| ** NADDR generates a reference to a field of the controller data. |
| ** PADDR generates a reference to another part of the script. |
| ** RADDR generates a reference to a script processor register. |
| ** FADDR generates a reference to a script processor register |
| ** with offset. |
| ** |
| **---------------------------------------------------------- |
| */ |
| |
| #define RELOC_SOFTC 0x40000000 |
| #define RELOC_LABEL 0x50000000 |
| #define RELOC_REGISTER 0x60000000 |
| #if 0 |
| #define RELOC_KVAR 0x70000000 |
| #endif |
| #define RELOC_LABELH 0x80000000 |
| #define RELOC_MASK 0xf0000000 |
| |
| #define NADDR(label) (RELOC_SOFTC | offsetof(struct ncb, label)) |
| #define PADDR(label) (RELOC_LABEL | offsetof(struct script, label)) |
| #define PADDRH(label) (RELOC_LABELH | offsetof(struct scripth, label)) |
| #define RADDR(label) (RELOC_REGISTER | REG(label)) |
| #define FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs))) |
| #if 0 |
| #define KVAR(which) (RELOC_KVAR | (which)) |
| #endif |
| |
| #if 0 |
| #define SCRIPT_KVAR_JIFFIES (0) |
| #define SCRIPT_KVAR_FIRST SCRIPT_KVAR_JIFFIES |
| #define SCRIPT_KVAR_LAST SCRIPT_KVAR_JIFFIES |
| /* |
| * Kernel variables referenced in the scripts. |
| * THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY. |
| */ |
| static void *script_kvars[] __initdata = |
| { (void *)&jiffies }; |
| #endif |
| |
| static struct script script0 __initdata = { |
| /*--------------------------< START >-----------------------*/ { |
| /* |
| ** This NOP will be patched with LED ON |
| ** SCR_REG_REG (gpreg, SCR_AND, 0xfe) |
| */ |
| SCR_NO_OP, |
| 0, |
| /* |
| ** Clear SIGP. |
| */ |
| SCR_FROM_REG (ctest2), |
| 0, |
| /* |
| ** Then jump to a certain point in tryloop. |
| ** Due to the lack of indirect addressing the code |
| ** is self modifying here. |
| */ |
| SCR_JUMP, |
| }/*-------------------------< STARTPOS >--------------------*/,{ |
| PADDRH(tryloop), |
| |
| }/*-------------------------< SELECT >----------------------*/,{ |
| /* |
| ** DSA contains the address of a scheduled |
| ** data structure. |
| ** |
| ** SCRATCHA contains the address of the script, |
| ** which starts the next entry. |
| ** |
| ** Set Initiator mode. |
| ** |
| ** (Target mode is left as an exercise for the reader) |
| */ |
| |
| SCR_CLR (SCR_TRG), |
| 0, |
| SCR_LOAD_REG (HS_REG, HS_SELECTING), |
| 0, |
| |
| /* |
| ** And try to select this target. |
| */ |
| SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select), |
| PADDR (reselect), |
| |
| }/*-------------------------< SELECT2 >----------------------*/,{ |
| /* |
| ** Now there are 4 possibilities: |
| ** |
| ** (1) The ncr loses arbitration. |
| ** This is ok, because it will try again, |
| ** when the bus becomes idle. |
| ** (But beware of the timeout function!) |
| ** |
| ** (2) The ncr is reselected. |
| ** Then the script processor takes the jump |
| ** to the RESELECT label. |
| ** |
| ** (3) The ncr wins arbitration. |
| ** Then it will execute SCRIPTS instruction until |
| ** the next instruction that checks SCSI phase. |
| ** Then will stop and wait for selection to be |
| ** complete or selection time-out to occur. |
| ** As a result the SCRIPTS instructions until |
| ** LOADPOS + 2 should be executed in parallel with |
| ** the SCSI core performing selection. |
| */ |
| |
| /* |
| ** The M_REJECT problem seems to be due to a selection |
| ** timing problem. |
| ** Wait immediately for the selection to complete. |
| ** (2.5x behaves so) |
| */ |
| SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_OUT)), |
| 0, |
| |
| /* |
| ** Next time use the next slot. |
| */ |
| SCR_COPY (4), |
| RADDR (temp), |
| PADDR (startpos), |
| /* |
| ** The ncr doesn't have an indirect load |
| ** or store command. So we have to |
| ** copy part of the control block to a |
| ** fixed place, where we can access it. |
| ** |
| ** We patch the address part of a |
| ** COPY command with the DSA-register. |
| */ |
| SCR_COPY_F (4), |
| RADDR (dsa), |
| PADDR (loadpos), |
| /* |
| ** Flush script prefetch if required |
| */ |
| PREFETCH_FLUSH |
| /* |
| ** then we do the actual copy. |
| */ |
| SCR_COPY (sizeof (struct head)), |
| /* |
| ** continued after the next label ... |
| */ |
| }/*-------------------------< LOADPOS >---------------------*/,{ |
| 0, |
| NADDR (header), |
| /* |
| ** Wait for the next phase or the selection |
| ** to complete or time-out. |
| */ |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)), |
| PADDR (prepare), |
| |
| }/*-------------------------< SEND_IDENT >----------------------*/,{ |
| /* |
| ** Selection complete. |
| ** Send the IDENTIFY and SIMPLE_TAG messages |
| ** (and the M_X_SYNC_REQ message) |
| */ |
| SCR_MOVE_TBL ^ SCR_MSG_OUT, |
| offsetof (struct dsb, smsg), |
| SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)), |
| PADDRH (resend_ident), |
| SCR_LOAD_REG (scratcha, 0x80), |
| 0, |
| SCR_COPY (1), |
| RADDR (scratcha), |
| NADDR (lastmsg), |
| }/*-------------------------< PREPARE >----------------------*/,{ |
| /* |
| ** load the savep (saved pointer) into |
| ** the TEMP register (actual pointer) |
| */ |
| SCR_COPY (4), |
| NADDR (header.savep), |
| RADDR (temp), |
| /* |
| ** Initialize the status registers |
| */ |
| SCR_COPY (4), |
| NADDR (header.status), |
| RADDR (scr0), |
| }/*-------------------------< PREPARE2 >---------------------*/,{ |
| /* |
| ** Initialize the msgout buffer with a NOOP message. |
| */ |
| SCR_LOAD_REG (scratcha, M_NOOP), |
| 0, |
| SCR_COPY (1), |
| RADDR (scratcha), |
| NADDR (msgout), |
| #if 0 |
| SCR_COPY (1), |
| RADDR (scratcha), |
| NADDR (msgin), |
| #endif |
| /* |
| ** Anticipate the COMMAND phase. |
| ** This is the normal case for initial selection. |
| */ |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_COMMAND)), |
| PADDR (dispatch), |
| |
| }/*-------------------------< COMMAND >--------------------*/,{ |
| /* |
| ** ... and send the command |
| */ |
| SCR_MOVE_TBL ^ SCR_COMMAND, |
| offsetof (struct dsb, cmd), |
| /* |
| ** If status is still HS_NEGOTIATE, negotiation failed. |
| ** We check this here, since we want to do that |
| ** only once. |
| */ |
| SCR_FROM_REG (HS_REG), |
| 0, |
| SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)), |
| SIR_NEGO_FAILED, |
| |
| }/*-----------------------< DISPATCH >----------------------*/,{ |
| /* |
| ** MSG_IN is the only phase that shall be |
| ** entered at least once for each (re)selection. |
| ** So we test it first. |
| */ |
| SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)), |
| PADDR (msg_in), |
| |
| SCR_RETURN ^ IFTRUE (IF (SCR_DATA_OUT)), |
| 0, |
| /* |
| ** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 4. |
| ** Possible data corruption during Memory Write and Invalidate. |
| ** This work-around resets the addressing logic prior to the |
| ** start of the first MOVE of a DATA IN phase. |
| ** (See Documentation/scsi/ncr53c8xx.txt for more information) |
| */ |
| SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)), |
| 20, |
| SCR_COPY (4), |
| RADDR (scratcha), |
| RADDR (scratcha), |
| SCR_RETURN, |
| 0, |
| SCR_JUMP ^ IFTRUE (IF (SCR_STATUS)), |
| PADDR (status), |
| SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)), |
| PADDR (command), |
| SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)), |
| PADDR (msg_out), |
| /* |
| ** Discard one illegal phase byte, if required. |
| */ |
| SCR_LOAD_REG (scratcha, XE_BAD_PHASE), |
| 0, |
| SCR_COPY (1), |
| RADDR (scratcha), |
| NADDR (xerr_st), |
| SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_OUT)), |
| 8, |
| SCR_MOVE_ABS (1) ^ SCR_ILG_OUT, |
| NADDR (scratch), |
| SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_IN)), |
| 8, |
| SCR_MOVE_ABS (1) ^ SCR_ILG_IN, |
| NADDR (scratch), |
| SCR_JUMP, |
| PADDR (dispatch), |
| |
| }/*-------------------------< CLRACK >----------------------*/,{ |
| /* |
| ** Terminate possible pending message phase. |
| */ |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP, |
| PADDR (dispatch), |
| |
| }/*-------------------------< NO_DATA >--------------------*/,{ |
| /* |
| ** The target wants to tranfer too much data |
| ** or in the wrong direction. |
| ** Remember that in extended error. |
| */ |
| SCR_LOAD_REG (scratcha, XE_EXTRA_DATA), |
| 0, |
| SCR_COPY (1), |
| RADDR (scratcha), |
| NADDR (xerr_st), |
| /* |
| ** Discard one data byte, if required. |
| */ |
| SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_OUT)), |
| 8, |
| SCR_MOVE_ABS (1) ^ SCR_DATA_OUT, |
| NADDR (scratch), |
| SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)), |
| 8, |
| SCR_MOVE_ABS (1) ^ SCR_DATA_IN, |
| NADDR (scratch), |
| /* |
| ** .. and repeat as required. |
| */ |
| SCR_CALL, |
| PADDR (dispatch), |
| SCR_JUMP, |
| PADDR (no_data), |
| |
| }/*-------------------------< STATUS >--------------------*/,{ |
| /* |
| ** get the status |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_STATUS, |
| NADDR (scratch), |
| /* |
| ** save status to scsi_status. |
| ** mark as complete. |
| */ |
| SCR_TO_REG (SS_REG), |
| 0, |
| SCR_LOAD_REG (HS_REG, HS_COMPLETE), |
| 0, |
| SCR_JUMP, |
| PADDR (dispatch), |
| }/*-------------------------< MSG_IN >--------------------*/,{ |
| /* |
| ** Get the first byte of the message |
| ** and save it to SCRATCHA. |
| ** |
| ** The script processor doesn't negate the |
| ** ACK signal after this transfer. |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin[0]), |
| }/*-------------------------< MSG_IN2 >--------------------*/,{ |
| /* |
| ** Handle this message. |
| */ |
| SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE)), |
| PADDR (complete), |
| SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)), |
| PADDR (disconnect), |
| SCR_JUMP ^ IFTRUE (DATA (M_SAVE_DP)), |
| PADDR (save_dp), |
| SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP)), |
| PADDR (restore_dp), |
| SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)), |
| PADDRH (msg_extended), |
| SCR_JUMP ^ IFTRUE (DATA (M_NOOP)), |
| PADDR (clrack), |
| SCR_JUMP ^ IFTRUE (DATA (M_REJECT)), |
| PADDRH (msg_reject), |
| SCR_JUMP ^ IFTRUE (DATA (M_IGN_RESIDUE)), |
| PADDRH (msg_ign_residue), |
| /* |
| ** Rest of the messages left as |
| ** an exercise ... |
| ** |
| ** Unimplemented messages: |
| ** fall through to MSG_BAD. |
| */ |
| }/*-------------------------< MSG_BAD >------------------*/,{ |
| /* |
| ** unimplemented message - reject it. |
| */ |
| SCR_INT, |
| SIR_REJECT_SENT, |
| SCR_LOAD_REG (scratcha, M_REJECT), |
| 0, |
| }/*-------------------------< SETMSG >----------------------*/,{ |
| SCR_COPY (1), |
| RADDR (scratcha), |
| NADDR (msgout), |
| SCR_SET (SCR_ATN), |
| 0, |
| SCR_JUMP, |
| PADDR (clrack), |
| }/*-------------------------< CLEANUP >-------------------*/,{ |
| /* |
| ** dsa: Pointer to ccb |
| ** or xxxxxxFF (no ccb) |
| ** |
| ** HS_REG: Host-Status (<>0!) |
| */ |
| SCR_FROM_REG (dsa), |
| 0, |
| SCR_JUMP ^ IFTRUE (DATA (0xff)), |
| PADDR (start), |
| /* |
| ** dsa is valid. |
| ** complete the cleanup. |
| */ |
| SCR_JUMP, |
| PADDR (cleanup_ok), |
| |
| }/*-------------------------< COMPLETE >-----------------*/,{ |
| /* |
| ** Complete message. |
| ** |
| ** Copy TEMP register to LASTP in header. |
| */ |
| SCR_COPY (4), |
| RADDR (temp), |
| NADDR (header.lastp), |
| /* |
| ** When we terminate the cycle by clearing ACK, |
| ** the target may disconnect immediately. |
| ** |
| ** We don't want to be told of an |
| ** "unexpected disconnect", |
| ** so we disable this feature. |
| */ |
| SCR_REG_REG (scntl2, SCR_AND, 0x7f), |
| 0, |
| /* |
| ** Terminate cycle ... |
| */ |
| SCR_CLR (SCR_ACK|SCR_ATN), |
| 0, |
| /* |
| ** ... and wait for the disconnect. |
| */ |
| SCR_WAIT_DISC, |
| 0, |
| }/*-------------------------< CLEANUP_OK >----------------*/,{ |
| /* |
| ** Save host status to header. |
| */ |
| SCR_COPY (4), |
| RADDR (scr0), |
| NADDR (header.status), |
| /* |
| ** and copy back the header to the ccb. |
| */ |
| SCR_COPY_F (4), |
| RADDR (dsa), |
| PADDR (cleanup0), |
| /* |
| ** Flush script prefetch if required |
| */ |
| PREFETCH_FLUSH |
| SCR_COPY (sizeof (struct head)), |
| NADDR (header), |
| }/*-------------------------< CLEANUP0 >--------------------*/,{ |
| 0, |
| }/*-------------------------< SIGNAL >----------------------*/,{ |
| /* |
| ** if job not completed ... |
| */ |
| SCR_FROM_REG (HS_REG), |
| 0, |
| /* |
| ** ... start the next command. |
| */ |
| SCR_JUMP ^ IFTRUE (MASK (0, (HS_DONEMASK|HS_SKIPMASK))), |
| PADDR(start), |
| /* |
| ** If command resulted in not GOOD status, |
| ** call the C code if needed. |
| */ |
| SCR_FROM_REG (SS_REG), |
| 0, |
| SCR_CALL ^ IFFALSE (DATA (S_GOOD)), |
| PADDRH (bad_status), |
| |
| #ifndef SCSI_NCR_CCB_DONE_SUPPORT |
| |
| /* |
| ** ... signal completion to the host |
| */ |
| SCR_INT, |
| SIR_INTFLY, |
| /* |
| ** Auf zu neuen Schandtaten! |
| */ |
| SCR_JUMP, |
| PADDR(start), |
| |
| #else /* defined SCSI_NCR_CCB_DONE_SUPPORT */ |
| |
| /* |
| ** ... signal completion to the host |
| */ |
| SCR_JUMP, |
| }/*------------------------< DONE_POS >---------------------*/,{ |
| PADDRH (done_queue), |
| }/*------------------------< DONE_PLUG >--------------------*/,{ |
| SCR_INT, |
| SIR_DONE_OVERFLOW, |
| }/*------------------------< DONE_END >---------------------*/,{ |
| SCR_INT, |
| SIR_INTFLY, |
| SCR_COPY (4), |
| RADDR (temp), |
| PADDR (done_pos), |
| SCR_JUMP, |
| PADDR (start), |
| |
| #endif /* SCSI_NCR_CCB_DONE_SUPPORT */ |
| |
| }/*-------------------------< SAVE_DP >------------------*/,{ |
| /* |
| ** SAVE_DP message: |
| ** Copy TEMP register to SAVEP in header. |
| */ |
| SCR_COPY (4), |
| RADDR (temp), |
| NADDR (header.savep), |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP, |
| PADDR (dispatch), |
| }/*-------------------------< RESTORE_DP >---------------*/,{ |
| /* |
| ** RESTORE_DP message: |
| ** Copy SAVEP in header to TEMP register. |
| */ |
| SCR_COPY (4), |
| NADDR (header.savep), |
| RADDR (temp), |
| SCR_JUMP, |
| PADDR (clrack), |
| |
| }/*-------------------------< DISCONNECT >---------------*/,{ |
| /* |
| ** DISCONNECTing ... |
| ** |
| ** disable the "unexpected disconnect" feature, |
| ** and remove the ACK signal. |
| */ |
| SCR_REG_REG (scntl2, SCR_AND, 0x7f), |
| 0, |
| SCR_CLR (SCR_ACK|SCR_ATN), |
| 0, |
| /* |
| ** Wait for the disconnect. |
| */ |
| SCR_WAIT_DISC, |
| 0, |
| /* |
| ** Status is: DISCONNECTED. |
| */ |
| SCR_LOAD_REG (HS_REG, HS_DISCONNECT), |
| 0, |
| SCR_JUMP, |
| PADDR (cleanup_ok), |
| |
| }/*-------------------------< MSG_OUT >-------------------*/,{ |
| /* |
| ** The target requests a message. |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_OUT, |
| NADDR (msgout), |
| SCR_COPY (1), |
| NADDR (msgout), |
| NADDR (lastmsg), |
| /* |
| ** If it was no ABORT message ... |
| */ |
| SCR_JUMP ^ IFTRUE (DATA (M_ABORT)), |
| PADDRH (msg_out_abort), |
| /* |
| ** ... wait for the next phase |
| ** if it's a message out, send it again, ... |
| */ |
| SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)), |
| PADDR (msg_out), |
| }/*-------------------------< MSG_OUT_DONE >--------------*/,{ |
| /* |
| ** ... else clear the message ... |
| */ |
| SCR_LOAD_REG (scratcha, M_NOOP), |
| 0, |
| SCR_COPY (4), |
| RADDR (scratcha), |
| NADDR (msgout), |
| /* |
| ** ... and process the next phase |
| */ |
| SCR_JUMP, |
| PADDR (dispatch), |
| }/*-------------------------< IDLE >------------------------*/,{ |
| /* |
| ** Nothing to do? |
| ** Wait for reselect. |
| ** This NOP will be patched with LED OFF |
| ** SCR_REG_REG (gpreg, SCR_OR, 0x01) |
| */ |
| SCR_NO_OP, |
| 0, |
| }/*-------------------------< RESELECT >--------------------*/,{ |
| /* |
| ** make the DSA invalid. |
| */ |
| SCR_LOAD_REG (dsa, 0xff), |
| 0, |
| SCR_CLR (SCR_TRG), |
| 0, |
| SCR_LOAD_REG (HS_REG, HS_IN_RESELECT), |
| 0, |
| /* |
| ** Sleep waiting for a reselection. |
| ** If SIGP is set, special treatment. |
| ** |
| ** Zu allem bereit .. |
| */ |
| SCR_WAIT_RESEL, |
| PADDR(start), |
| }/*-------------------------< RESELECTED >------------------*/,{ |
| /* |
| ** This NOP will be patched with LED ON |
| ** SCR_REG_REG (gpreg, SCR_AND, 0xfe) |
| */ |
| SCR_NO_OP, |
| 0, |
| /* |
| ** ... zu nichts zu gebrauchen ? |
| ** |
| ** load the target id into the SFBR |
| ** and jump to the control block. |
| ** |
| ** Look at the declarations of |
| ** - struct ncb |
| ** - struct tcb |
| ** - struct lcb |
| ** - struct ccb |
| ** to understand what's going on. |
| */ |
| SCR_REG_SFBR (ssid, SCR_AND, 0x8F), |
| 0, |
| SCR_TO_REG (sdid), |
| 0, |
| SCR_JUMP, |
| NADDR (jump_tcb), |
| |
| }/*-------------------------< RESEL_DSA >-------------------*/,{ |
| /* |
| ** Ack the IDENTIFY or TAG previously received. |
| */ |
| SCR_CLR (SCR_ACK), |
| 0, |
| /* |
| ** The ncr doesn't have an indirect load |
| ** or store command. So we have to |
| ** copy part of the control block to a |
| ** fixed place, where we can access it. |
| ** |
| ** We patch the address part of a |
| ** COPY command with the DSA-register. |
| */ |
| SCR_COPY_F (4), |
| RADDR (dsa), |
| PADDR (loadpos1), |
| /* |
| ** Flush script prefetch if required |
| */ |
| PREFETCH_FLUSH |
| /* |
| ** then we do the actual copy. |
| */ |
| SCR_COPY (sizeof (struct head)), |
| /* |
| ** continued after the next label ... |
| */ |
| |
| }/*-------------------------< LOADPOS1 >-------------------*/,{ |
| 0, |
| NADDR (header), |
| /* |
| ** The DSA contains the data structure address. |
| */ |
| SCR_JUMP, |
| PADDR (prepare), |
| |
| }/*-------------------------< RESEL_LUN >-------------------*/,{ |
| /* |
| ** come back to this point |
| ** to get an IDENTIFY message |
| ** Wait for a msg_in phase. |
| */ |
| SCR_INT ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| SIR_RESEL_NO_MSG_IN, |
| /* |
| ** message phase. |
| ** Read the data directly from the BUS DATA lines. |
| ** This helps to support very old SCSI devices that |
| ** may reselect without sending an IDENTIFY. |
| */ |
| SCR_FROM_REG (sbdl), |
| 0, |
| /* |
| ** It should be an Identify message. |
| */ |
| SCR_RETURN, |
| 0, |
| }/*-------------------------< RESEL_TAG >-------------------*/,{ |
| /* |
| ** Read IDENTIFY + SIMPLE + TAG using a single MOVE. |
| ** Agressive optimization, is'nt it? |
| ** No need to test the SIMPLE TAG message, since the |
| ** driver only supports conformant devices for tags. ;-) |
| */ |
| SCR_MOVE_ABS (3) ^ SCR_MSG_IN, |
| NADDR (msgin), |
| /* |
| ** Read the TAG from the SIDL. |
| ** Still an aggressive optimization. ;-) |
| ** Compute the CCB indirect jump address which |
| ** is (#TAG*2 & 0xfc) due to tag numbering using |
| ** 1,3,5..MAXTAGS*2+1 actual values. |
| */ |
| SCR_REG_SFBR (sidl, SCR_SHL, 0), |
| 0, |
| SCR_SFBR_REG (temp, SCR_AND, 0xfc), |
| 0, |
| }/*-------------------------< JUMP_TO_NEXUS >-------------------*/,{ |
| SCR_COPY_F (4), |
| RADDR (temp), |
| PADDR (nexus_indirect), |
| /* |
| ** Flush script prefetch if required |
| */ |
| PREFETCH_FLUSH |
| SCR_COPY (4), |
| }/*-------------------------< NEXUS_INDIRECT >-------------------*/,{ |
| 0, |
| RADDR (temp), |
| SCR_RETURN, |
| 0, |
| }/*-------------------------< RESEL_NOTAG >-------------------*/,{ |
| /* |
| ** No tag expected. |
| ** Read an throw away the IDENTIFY. |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin), |
| SCR_JUMP, |
| PADDR (jump_to_nexus), |
| }/*-------------------------< DATA_IN >--------------------*/,{ |
| /* |
| ** Because the size depends on the |
| ** #define MAX_SCATTERL parameter, |
| ** it is filled in at runtime. |
| ** |
| ** ##===========< i=0; i<MAX_SCATTERL >========= |
| ** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)), |
| ** || PADDR (dispatch), |
| ** || SCR_MOVE_TBL ^ SCR_DATA_IN, |
| ** || offsetof (struct dsb, data[ i]), |
| ** ##========================================== |
| ** |
| **--------------------------------------------------------- |
| */ |
| 0 |
| }/*-------------------------< DATA_IN2 >-------------------*/,{ |
| SCR_CALL, |
| PADDR (dispatch), |
| SCR_JUMP, |
| PADDR (no_data), |
| }/*-------------------------< DATA_OUT >--------------------*/,{ |
| /* |
| ** Because the size depends on the |
| ** #define MAX_SCATTERL parameter, |
| ** it is filled in at runtime. |
| ** |
| ** ##===========< i=0; i<MAX_SCATTERL >========= |
| ** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)), |
| ** || PADDR (dispatch), |
| ** || SCR_MOVE_TBL ^ SCR_DATA_OUT, |
| ** || offsetof (struct dsb, data[ i]), |
| ** ##========================================== |
| ** |
| **--------------------------------------------------------- |
| */ |
| 0 |
| }/*-------------------------< DATA_OUT2 >-------------------*/,{ |
| SCR_CALL, |
| PADDR (dispatch), |
| SCR_JUMP, |
| PADDR (no_data), |
| }/*--------------------------------------------------------*/ |
| }; |
| |
| static struct scripth scripth0 __initdata = { |
| /*-------------------------< TRYLOOP >---------------------*/{ |
| /* |
| ** Start the next entry. |
| ** Called addresses point to the launch script in the CCB. |
| ** They are patched by the main processor. |
| ** |
| ** Because the size depends on the |
| ** #define MAX_START parameter, it is filled |
| ** in at runtime. |
| ** |
| **----------------------------------------------------------- |
| ** |
| ** ##===========< I=0; i<MAX_START >=========== |
| ** || SCR_CALL, |
| ** || PADDR (idle), |
| ** ##========================================== |
| ** |
| **----------------------------------------------------------- |
| */ |
| 0 |
| }/*------------------------< TRYLOOP2 >---------------------*/,{ |
| SCR_JUMP, |
| PADDRH(tryloop), |
| |
| #ifdef SCSI_NCR_CCB_DONE_SUPPORT |
| |
| }/*------------------------< DONE_QUEUE >-------------------*/,{ |
| /* |
| ** Copy the CCB address to the next done entry. |
| ** Because the size depends on the |
| ** #define MAX_DONE parameter, it is filled |
| ** in at runtime. |
| ** |
| **----------------------------------------------------------- |
| ** |
| ** ##===========< I=0; i<MAX_DONE >=========== |
| ** || SCR_COPY (sizeof(struct ccb *), |
| ** || NADDR (header.cp), |
| ** || NADDR (ccb_done[i]), |
| ** || SCR_CALL, |
| ** || PADDR (done_end), |
| ** ##========================================== |
| ** |
| **----------------------------------------------------------- |
| */ |
| 0 |
| }/*------------------------< DONE_QUEUE2 >------------------*/,{ |
| SCR_JUMP, |
| PADDRH (done_queue), |
| |
| #endif /* SCSI_NCR_CCB_DONE_SUPPORT */ |
| }/*------------------------< SELECT_NO_ATN >-----------------*/,{ |
| /* |
| ** Set Initiator mode. |
| ** And try to select this target without ATN. |
| */ |
| |
| SCR_CLR (SCR_TRG), |
| 0, |
| SCR_LOAD_REG (HS_REG, HS_SELECTING), |
| 0, |
| SCR_SEL_TBL ^ offsetof (struct dsb, select), |
| PADDR (reselect), |
| SCR_JUMP, |
| PADDR (select2), |
| |
| }/*-------------------------< CANCEL >------------------------*/,{ |
| |
| SCR_LOAD_REG (scratcha, HS_ABORTED), |
| 0, |
| SCR_JUMPR, |
| 8, |
| }/*-------------------------< SKIP >------------------------*/,{ |
| SCR_LOAD_REG (scratcha, 0), |
| 0, |
| /* |
| ** This entry has been canceled. |
| ** Next time use the next slot. |
| */ |
| SCR_COPY (4), |
| RADDR (temp), |
| PADDR (startpos), |
| /* |
| ** The ncr doesn't have an indirect load |
| ** or store command. So we have to |
| ** copy part of the control block to a |
| ** fixed place, where we can access it. |
| ** |
| ** We patch the address part of a |
| ** COPY command with the DSA-register. |
| */ |
| SCR_COPY_F (4), |
| RADDR (dsa), |
| PADDRH (skip2), |
| /* |
| ** Flush script prefetch if required |
| */ |
| PREFETCH_FLUSH |
| /* |
| ** then we do the actual copy. |
| */ |
| SCR_COPY (sizeof (struct head)), |
| /* |
| ** continued after the next label ... |
| */ |
| }/*-------------------------< SKIP2 >---------------------*/,{ |
| 0, |
| NADDR (header), |
| /* |
| ** Initialize the status registers |
| */ |
| SCR_COPY (4), |
| NADDR (header.status), |
| RADDR (scr0), |
| /* |
| ** Force host status. |
| */ |
| SCR_FROM_REG (scratcha), |
| 0, |
| SCR_JUMPR ^ IFFALSE (MASK (0, HS_DONEMASK)), |
| 16, |
| SCR_REG_REG (HS_REG, SCR_OR, HS_SKIPMASK), |
| 0, |
| SCR_JUMPR, |
| 8, |
| SCR_TO_REG (HS_REG), |
| 0, |
| SCR_LOAD_REG (SS_REG, S_GOOD), |
| 0, |
| SCR_JUMP, |
| PADDR (cleanup_ok), |
| |
| },/*-------------------------< PAR_ERR_DATA_IN >---------------*/{ |
| /* |
| ** Ignore all data in byte, until next phase |
| */ |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)), |
| PADDRH (par_err_other), |
| SCR_MOVE_ABS (1) ^ SCR_DATA_IN, |
| NADDR (scratch), |
| SCR_JUMPR, |
| -24, |
| },/*-------------------------< PAR_ERR_OTHER >------------------*/{ |
| /* |
| ** count it. |
| */ |
| SCR_REG_REG (PS_REG, SCR_ADD, 0x01), |
| 0, |
| /* |
| ** jump to dispatcher. |
| */ |
| SCR_JUMP, |
| PADDR (dispatch), |
| }/*-------------------------< MSG_REJECT >---------------*/,{ |
| /* |
| ** If a negotiation was in progress, |
| ** negotiation failed. |
| ** Otherwise, let the C code print |
| ** some message. |
| */ |
| SCR_FROM_REG (HS_REG), |
| 0, |
| SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)), |
| SIR_REJECT_RECEIVED, |
| SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)), |
| SIR_NEGO_FAILED, |
| SCR_JUMP, |
| PADDR (clrack), |
| |
| }/*-------------------------< MSG_IGN_RESIDUE >----------*/,{ |
| /* |
| ** Terminate cycle |
| */ |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| PADDR (dispatch), |
| /* |
| ** get residue size. |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin[1]), |
| /* |
| ** Size is 0 .. ignore message. |
| */ |
| SCR_JUMP ^ IFTRUE (DATA (0)), |
| PADDR (clrack), |
| /* |
| ** Size is not 1 .. have to interrupt. |
| */ |
| SCR_JUMPR ^ IFFALSE (DATA (1)), |
| 40, |
| /* |
| ** Check for residue byte in swide register |
| */ |
| SCR_FROM_REG (scntl2), |
| 0, |
| SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)), |
| 16, |
| /* |
| ** There IS data in the swide register. |
| ** Discard it. |
| */ |
| SCR_REG_REG (scntl2, SCR_OR, WSR), |
| 0, |
| SCR_JUMP, |
| PADDR (clrack), |
| /* |
| ** Load again the size to the sfbr register. |
| */ |
| SCR_FROM_REG (scratcha), |
| 0, |
| SCR_INT, |
| SIR_IGN_RESIDUE, |
| SCR_JUMP, |
| PADDR (clrack), |
| |
| }/*-------------------------< MSG_EXTENDED >-------------*/,{ |
| /* |
| ** Terminate cycle |
| */ |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| PADDR (dispatch), |
| /* |
| ** get length. |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin[1]), |
| /* |
| */ |
| SCR_JUMP ^ IFTRUE (DATA (3)), |
| PADDRH (msg_ext_3), |
| SCR_JUMP ^ IFFALSE (DATA (2)), |
| PADDR (msg_bad), |
| }/*-------------------------< MSG_EXT_2 >----------------*/,{ |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| PADDR (dispatch), |
| /* |
| ** get extended message code. |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin[2]), |
| SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)), |
| PADDRH (msg_wdtr), |
| /* |
| ** unknown extended message |
| */ |
| SCR_JUMP, |
| PADDR (msg_bad) |
| }/*-------------------------< MSG_WDTR >-----------------*/,{ |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| PADDR (dispatch), |
| /* |
| ** get data bus width |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin[3]), |
| /* |
| ** let the host do the real work. |
| */ |
| SCR_INT, |
| SIR_NEGO_WIDE, |
| /* |
| ** let the target fetch our answer. |
| */ |
| SCR_SET (SCR_ATN), |
| 0, |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)), |
| PADDRH (nego_bad_phase), |
| |
| }/*-------------------------< SEND_WDTR >----------------*/,{ |
| /* |
| ** Send the M_X_WIDE_REQ |
| */ |
| SCR_MOVE_ABS (4) ^ SCR_MSG_OUT, |
| NADDR (msgout), |
| SCR_COPY (1), |
| NADDR (msgout), |
| NADDR (lastmsg), |
| SCR_JUMP, |
| PADDR (msg_out_done), |
| |
| }/*-------------------------< MSG_EXT_3 >----------------*/,{ |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| PADDR (dispatch), |
| /* |
| ** get extended message code. |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin[2]), |
| SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)), |
| PADDRH (msg_sdtr), |
| /* |
| ** unknown extended message |
| */ |
| SCR_JUMP, |
| PADDR (msg_bad) |
| |
| }/*-------------------------< MSG_SDTR >-----------------*/,{ |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| PADDR (dispatch), |
| /* |
| ** get period and offset |
| */ |
| SCR_MOVE_ABS (2) ^ SCR_MSG_IN, |
| NADDR (msgin[3]), |
| /* |
| ** let the host do the real work. |
| */ |
| SCR_INT, |
| SIR_NEGO_SYNC, |
| /* |
| ** let the target fetch our answer. |
| */ |
| SCR_SET (SCR_ATN), |
| 0, |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)), |
| PADDRH (nego_bad_phase), |
| |
| }/*-------------------------< SEND_SDTR >-------------*/,{ |
| /* |
| ** Send the M_X_SYNC_REQ |
| */ |
| SCR_MOVE_ABS (5) ^ SCR_MSG_OUT, |
| NADDR (msgout), |
| SCR_COPY (1), |
| NADDR (msgout), |
| NADDR (lastmsg), |
| SCR_JUMP, |
| PADDR (msg_out_done), |
| |
| }/*-------------------------< NEGO_BAD_PHASE >------------*/,{ |
| SCR_INT, |
| SIR_NEGO_PROTO, |
| SCR_JUMP, |
| PADDR (dispatch), |
| |
| }/*-------------------------< MSG_OUT_ABORT >-------------*/,{ |
| /* |
| ** After ABORT message, |
| ** |
| ** expect an immediate disconnect, ... |
| */ |
| SCR_REG_REG (scntl2, SCR_AND, 0x7f), |
| 0, |
| SCR_CLR (SCR_ACK|SCR_ATN), |
| 0, |
| SCR_WAIT_DISC, |
| 0, |
| /* |
| ** ... and set the status to "ABORTED" |
| */ |
| SCR_LOAD_REG (HS_REG, HS_ABORTED), |
| 0, |
| SCR_JUMP, |
| PADDR (cleanup), |
| |
| }/*-------------------------< HDATA_IN >-------------------*/,{ |
| /* |
| ** Because the size depends on the |
| ** #define MAX_SCATTERH parameter, |
| ** it is filled in at runtime. |
| ** |
| ** ##==< i=MAX_SCATTERL; i<MAX_SCATTERL+MAX_SCATTERH >== |
| ** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)), |
| ** || PADDR (dispatch), |
| ** || SCR_MOVE_TBL ^ SCR_DATA_IN, |
| ** || offsetof (struct dsb, data[ i]), |
| ** ##=================================================== |
| ** |
| **--------------------------------------------------------- |
| */ |
| 0 |
| }/*-------------------------< HDATA_IN2 >------------------*/,{ |
| SCR_JUMP, |
| PADDR (data_in), |
| |
| }/*-------------------------< HDATA_OUT >-------------------*/,{ |
| /* |
| ** Because the size depends on the |
| ** #define MAX_SCATTERH parameter, |
| ** it is filled in at runtime. |
| ** |
| ** ##==< i=MAX_SCATTERL; i<MAX_SCATTERL+MAX_SCATTERH >== |
| ** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)), |
| ** || PADDR (dispatch), |
| ** || SCR_MOVE_TBL ^ SCR_DATA_OUT, |
| ** || offsetof (struct dsb, data[ i]), |
| ** ##=================================================== |
| ** |
| **--------------------------------------------------------- |
| */ |
| 0 |
| }/*-------------------------< HDATA_OUT2 >------------------*/,{ |
| SCR_JUMP, |
| PADDR (data_out), |
| |
| }/*-------------------------< RESET >----------------------*/,{ |
| /* |
| ** Send a M_RESET message if bad IDENTIFY |
| ** received on reselection. |
| */ |
| SCR_LOAD_REG (scratcha, M_ABORT_TAG), |
| 0, |
| SCR_JUMP, |
| PADDRH (abort_resel), |
| }/*-------------------------< ABORTTAG >-------------------*/,{ |
| /* |
| ** Abort a wrong tag received on reselection. |
| */ |
| SCR_LOAD_REG (scratcha, M_ABORT_TAG), |
| 0, |
| SCR_JUMP, |
| PADDRH (abort_resel), |
| }/*-------------------------< ABORT >----------------------*/,{ |
| /* |
| ** Abort a reselection when no active CCB. |
| */ |
| SCR_LOAD_REG (scratcha, M_ABORT), |
| 0, |
| }/*-------------------------< ABORT_RESEL >----------------*/,{ |
| SCR_COPY (1), |
| RADDR (scratcha), |
| NADDR (msgout), |
| SCR_SET (SCR_ATN), |
| 0, |
| SCR_CLR (SCR_ACK), |
| 0, |
| /* |
| ** and send it. |
| ** we expect an immediate disconnect |
| */ |
| SCR_REG_REG (scntl2, SCR_AND, 0x7f), |
| 0, |
| SCR_MOVE_ABS (1) ^ SCR_MSG_OUT, |
| NADDR (msgout), |
| SCR_COPY (1), |
| NADDR (msgout), |
| NADDR (lastmsg), |
| SCR_CLR (SCR_ACK|SCR_ATN), |
| 0, |
| SCR_WAIT_DISC, |
| 0, |
| SCR_JUMP, |
| PADDR (start), |
| }/*-------------------------< RESEND_IDENT >-------------------*/,{ |
| /* |
| ** The target stays in MSG OUT phase after having acked |
| ** Identify [+ Tag [+ Extended message ]]. Targets shall |
| ** behave this way on parity error. |
| ** We must send it again all the messages. |
| */ |
| SCR_SET (SCR_ATN), /* Shall be asserted 2 deskew delays before the */ |
| 0, /* 1rst ACK = 90 ns. Hope the NCR is'nt too fast */ |
| SCR_JUMP, |
| PADDR (send_ident), |
| }/*-------------------------< CLRATN_GO_ON >-------------------*/,{ |
| SCR_CLR (SCR_ATN), |
| 0, |
| SCR_JUMP, |
| }/*-------------------------< NXTDSP_GO_ON >-------------------*/,{ |
| 0, |
| }/*-------------------------< SDATA_IN >-------------------*/,{ |
| SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)), |
| PADDR (dispatch), |
| SCR_MOVE_TBL ^ SCR_DATA_IN, |
| offsetof (struct dsb, sense), |
| SCR_CALL, |
| PADDR (dispatch), |
| SCR_JUMP, |
| PADDR (no_data), |
| }/*-------------------------< DATA_IO >--------------------*/,{ |
| /* |
| ** We jump here if the data direction was unknown at the |
| ** time we had to queue the command to the scripts processor. |
| ** Pointers had been set as follow in this situation: |
| ** savep --> DATA_IO |
| ** lastp --> start pointer when DATA_IN |
| ** goalp --> goal pointer when DATA_IN |
| ** wlastp --> start pointer when DATA_OUT |
| ** wgoalp --> goal pointer when DATA_OUT |
| ** This script sets savep/lastp/goalp according to the |
| ** direction chosen by the target. |
| */ |
| SCR_JUMPR ^ IFTRUE (WHEN (SCR_DATA_OUT)), |
| 32, |
| /* |
| ** Direction is DATA IN. |
| ** Warning: we jump here, even when phase is DATA OUT. |
| */ |
| SCR_COPY (4), |
| NADDR (header.lastp), |
| NADDR (header.savep), |
| |
| /* |
| ** Jump to the SCRIPTS according to actual direction. |
| */ |
| SCR_COPY (4), |
| NADDR (header.savep), |
| RADDR (temp), |
| SCR_RETURN, |
| 0, |
| /* |
| ** Direction is DATA OUT. |
| */ |
| SCR_COPY (4), |
| NADDR (header.wlastp), |
| NADDR (header.lastp), |
| SCR_COPY (4), |
| NADDR (header.wgoalp), |
| NADDR (header.goalp), |
| SCR_JUMPR, |
| -64, |
| }/*-------------------------< BAD_IDENTIFY >---------------*/,{ |
| /* |
| ** If message phase but not an IDENTIFY, |
| ** get some help from the C code. |
| ** Old SCSI device may behave so. |
| */ |
| SCR_JUMPR ^ IFTRUE (MASK (0x80, 0x80)), |
| 16, |
| SCR_INT, |
| SIR_RESEL_NO_IDENTIFY, |
| SCR_JUMP, |
| PADDRH (reset), |
| /* |
| ** Message is an IDENTIFY, but lun is unknown. |
| ** Read the message, since we got it directly |
| ** from the SCSI BUS data lines. |
| ** Signal problem to C code for logging the event. |
| ** Send a M_ABORT to clear all pending tasks. |
| */ |
| SCR_INT, |
| SIR_RESEL_BAD_LUN, |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin), |
| SCR_JUMP, |
| PADDRH (abort), |
| }/*-------------------------< BAD_I_T_L >------------------*/,{ |
| /* |
| ** We donnot have a task for that I_T_L. |
| ** Signal problem to C code for logging the event. |
| ** Send a M_ABORT message. |
| */ |
| SCR_INT, |
| SIR_RESEL_BAD_I_T_L, |
| SCR_JUMP, |
| PADDRH (abort), |
| }/*-------------------------< BAD_I_T_L_Q >----------------*/,{ |
| /* |
| ** We donnot have a task that matches the tag. |
| ** Signal problem to C code for logging the event. |
| ** Send a M_ABORTTAG message. |
| */ |
| SCR_INT, |
| SIR_RESEL_BAD_I_T_L_Q, |
| SCR_JUMP, |
| PADDRH (aborttag), |
| }/*-------------------------< BAD_TARGET >-----------------*/,{ |
| /* |
| ** We donnot know the target that reselected us. |
| ** Grab the first message if any (IDENTIFY). |
| ** Signal problem to C code for logging the event. |
| ** M_RESET message. |
| */ |
| SCR_INT, |
| SIR_RESEL_BAD_TARGET, |
| SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| 8, |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin), |
| SCR_JUMP, |
| PADDRH (reset), |
| }/*-------------------------< BAD_STATUS >-----------------*/,{ |
| /* |
| ** If command resulted in either QUEUE FULL, |
| ** CHECK CONDITION or COMMAND TERMINATED, |
| ** call the C code. |
| */ |
| SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)), |
| SIR_BAD_STATUS, |
| SCR_INT ^ IFTRUE (DATA (S_CHECK_COND)), |
| SIR_BAD_STATUS, |
| SCR_INT ^ IFTRUE (DATA (S_TERMINATED)), |
| SIR_BAD_STATUS, |
| SCR_RETURN, |
| 0, |
| }/*-------------------------< START_RAM >-------------------*/,{ |
| /* |
| ** Load the script into on-chip RAM, |
| ** and jump to start point. |
| */ |
| SCR_COPY_F (4), |
| RADDR (scratcha), |
| PADDRH (start_ram0), |
| /* |
| ** Flush script prefetch if required |
| */ |
| PREFETCH_FLUSH |
| SCR_COPY (sizeof (struct script)), |
| }/*-------------------------< START_RAM0 >--------------------*/,{ |
| 0, |
| PADDR (start), |
| SCR_JUMP, |
| PADDR (start), |
| }/*-------------------------< STO_RESTART >-------------------*/,{ |
| /* |
| ** |
| ** Repair start queue (e.g. next time use the next slot) |
| ** and jump to start point. |
| */ |
| SCR_COPY (4), |
| RADDR (temp), |
| PADDR (startpos), |
| SCR_JUMP, |
| PADDR (start), |
| }/*-------------------------< WAIT_DMA >-------------------*/,{ |
| /* |
| ** For HP Zalon/53c720 systems, the Zalon interface |
| ** between CPU and 53c720 does prefetches, which causes |
| ** problems with self modifying scripts. The problem |
| ** is overcome by calling a dummy subroutine after each |
| ** modification, to force a refetch of the script on |
| ** return from the subroutine. |
| */ |
| SCR_RETURN, |
| 0, |
| }/*-------------------------< SNOOPTEST >-------------------*/,{ |
| /* |
| ** Read the variable. |
| */ |
| SCR_COPY (4), |
| NADDR(ncr_cache), |
| RADDR (scratcha), |
| /* |
| ** Write the variable. |
| */ |
| SCR_COPY (4), |
| RADDR (temp), |
| NADDR(ncr_cache), |
| /* |
| ** Read back the variable. |
| */ |
| SCR_COPY (4), |
| NADDR(ncr_cache), |
| RADDR (temp), |
| }/*-------------------------< SNOOPEND >-------------------*/,{ |
| /* |
| ** And stop. |
| */ |
| SCR_INT, |
| 99, |
| }/*--------------------------------------------------------*/ |
| }; |
| |
| /*========================================================== |
| ** |
| ** |
| ** Fill in #define dependent parts of the script |
| ** |
| ** |
| **========================================================== |
| */ |
| |
| void __init ncr_script_fill (struct script * scr, struct scripth * scrh) |
| { |
| int i; |
| ncrcmd *p; |
| |
| p = scrh->tryloop; |
| for (i=0; i<MAX_START; i++) { |
| *p++ =SCR_CALL; |
| *p++ =PADDR (idle); |
| } |
| |
| BUG_ON((u_long)p != (u_long)&scrh->tryloop + sizeof (scrh->tryloop)); |
| |
| #ifdef SCSI_NCR_CCB_DONE_SUPPORT |
| |
| p = scrh->done_queue; |
| for (i = 0; i<MAX_DONE; i++) { |
| *p++ =SCR_COPY (sizeof(struct ccb *)); |
| *p++ =NADDR (header.cp); |
| *p++ =NADDR (ccb_done[i]); |
| *p++ =SCR_CALL; |
| *p++ =PADDR (done_end); |
| } |
| |
| BUG_ON((u_long)p != (u_long)&scrh->done_queue+sizeof(scrh->done_queue)); |
| |
| #endif /* SCSI_NCR_CCB_DONE_SUPPORT */ |
| |
| p = scrh->hdata_in; |
| for (i=0; i<MAX_SCATTERH; i++) { |
| *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)); |
| *p++ =PADDR (dispatch); |
| *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN; |
| *p++ =offsetof (struct dsb, data[i]); |
| } |
| |
| BUG_ON((u_long)p != (u_long)&scrh->hdata_in + sizeof (scrh->hdata_in)); |
| |
| p = scr->data_in; |
| for (i=MAX_SCATTERH; i<MAX_SCATTERH+MAX_SCATTERL; i++) { |
| *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)); |
| *p++ =PADDR (dispatch); |
| *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN; |
| *p++ =offsetof (struct dsb, data[i]); |
| } |
| |
| BUG_ON((u_long)p != (u_long)&scr->data_in + sizeof (scr->data_in)); |
| |
| p = scrh->hdata_out; |
| for (i=0; i<MAX_SCATTERH; i++) { |
| *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)); |
| *p++ =PADDR (dispatch); |
| *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT; |
| *p++ =offsetof (struct dsb, data[i]); |
| } |
| |
| BUG_ON((u_long)p != (u_long)&scrh->hdata_out + sizeof (scrh->hdata_out)); |
| |
| p = scr->data_out; |
| for (i=MAX_SCATTERH; i<MAX_SCATTERH+MAX_SCATTERL; i++) { |
| *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)); |
| *p++ =PADDR (dispatch); |
| *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT; |
| *p++ =offsetof (struct dsb, data[i]); |
| } |
| |
| BUG_ON((u_long) p != (u_long)&scr->data_out + sizeof (scr->data_out)); |
| } |
| |
| /*========================================================== |
| ** |
| ** |
| ** Copy and rebind a script. |
| ** |
| ** |
| **========================================================== |
| */ |
| |
| static void __init |
| ncr_script_copy_and_bind (struct ncb *np, ncrcmd *src, ncrcmd *dst, int len) |
| { |
| ncrcmd opcode, new, old, tmp1, tmp2; |
| ncrcmd *start, *end; |
| int relocs; |
| int opchanged = 0; |
| |
| start = src; |
| end = src + len/4; |
| |
| while (src < end) { |
| |
| opcode = *src++; |
| *dst++ = cpu_to_scr(opcode); |
| |
| /* |
| ** If we forget to change the length |
| ** in struct script, a field will be |
| ** padded with 0. This is an illegal |
| ** command. |
| */ |
| |
| if (opcode == 0) { |
| printk (KERN_ERR "%s: ERROR0 IN SCRIPT at %d.\n", |
| ncr_name(np), (int) (src-start-1)); |
| mdelay(1000); |
| } |
| |
| if (DEBUG_FLAGS & DEBUG_SCRIPT) |
| printk (KERN_DEBUG "%p: <%x>\n", |
| (src-1), (unsigned)opcode); |
| |
| /* |
| ** We don't have to decode ALL commands |
| */ |
| switch (opcode >> 28) { |
| |
| case 0xc: |
| /* |
| ** COPY has TWO arguments. |
| */ |
| relocs = 2; |
| tmp1 = src[0]; |
| #ifdef RELOC_KVAR |
| if ((tmp1 & RELOC_MASK) == RELOC_KVAR) |
| tmp1 = 0; |
| #endif |
| tmp2 = src[1]; |
| #ifdef RELOC_KVAR |
| if ((tmp2 & RELOC_MASK) == RELOC_KVAR) |
| tmp2 = 0; |
| #endif |
| if ((tmp1 ^ tmp2) & 3) { |
| printk (KERN_ERR"%s: ERROR1 IN SCRIPT at %d.\n", |
| ncr_name(np), (int) (src-start-1)); |
| mdelay(1000); |
| } |
| /* |
| ** If PREFETCH feature not enabled, remove |
| ** the NO FLUSH bit if present. |
| */ |
| if ((opcode & SCR_NO_FLUSH) && !(np->features & FE_PFEN)) { |
| dst[-1] = cpu_to_scr(opcode & ~SCR_NO_FLUSH); |
| ++opchanged; |
| } |
| break; |
| |
| case 0x0: |
| /* |
| ** MOVE (absolute address) |
| */ |
| relocs = 1; |
| break; |
| |
| case 0x8: |
| /* |
| ** JUMP / CALL |
| ** don't relocate if relative :-) |
| */ |
| if (opcode & 0x00800000) |
| relocs = 0; |
| else |
| relocs = 1; |
| break; |
| |
| case 0x4: |
| case 0x5: |
| case 0x6: |
| case 0x7: |
| relocs = 1; |
| break; |
| |
| default: |
| relocs = 0; |
| break; |
| } |
| |
| if (relocs) { |
| while (relocs--) { |
| old = *src++; |
| |
| switch (old & RELOC_MASK) { |
| case RELOC_REGISTER: |
| new = (old & ~RELOC_MASK) + np->paddr; |
| break; |
| case RELOC_LABEL: |
| new = (old & ~RELOC_MASK) + np->p_script; |
| break; |
| case RELOC_LABELH: |
| new = (old & ~RELOC_MASK) + np->p_scripth; |
| break; |
| case RELOC_SOFTC: |
| new = (old & ~RELOC_MASK) + np->p_ncb; |
| break; |
| #ifdef RELOC_KVAR |
| case RELOC_KVAR: |
| if (((old & ~RELOC_MASK) < |
| SCRIPT_KVAR_FIRST) || |
| ((old & ~RELOC_MASK) > |
| SCRIPT_KVAR_LAST)) |
| panic("ncr KVAR out of range"); |
| new = vtophys(script_kvars[old & |
| ~RELOC_MASK]); |
| break; |
| #endif |
| case 0: |
| /* Don't relocate a 0 address. */ |
| if (old == 0) { |
| new = old; |
| break; |
| } |
| /* fall through */ |
| default: |
| panic("ncr_script_copy_and_bind: weird relocation %x\n", old); |
| break; |
| } |
| |
| *dst++ = cpu_to_scr(new); |
| } |
| } else |
| *dst++ = cpu_to_scr(*src++); |
| |
| } |
| } |
| |
| /* |
| ** Linux host data structure |
| */ |
| |
| struct host_data { |
| struct ncb *ncb; |
| }; |
| |
| #define PRINT_ADDR(cmd, arg...) dev_info(&cmd->device->sdev_gendev , ## arg) |
| |
| static void ncr_print_msg(struct ccb *cp, char *label, u_char *msg) |
| { |
| int i; |
| PRINT_ADDR(cp->cmd, "%s: ", label); |
| |
| printk ("%x",*msg); |
| if (*msg == M_EXTENDED) { |
| for (i = 1; i < 8; i++) { |
| if (i - 1 > msg[1]) |
| break; |
| printk ("-%x",msg[i]); |
| } |
| } else if ((*msg & 0xf0) == 0x20) { |
| printk ("-%x",msg[1]); |
| } |
| |
| printk(".\n"); |
| } |
| |
| /*========================================================== |
| ** |
| ** NCR chip clock divisor table. |
| ** Divisors are multiplied by 10,000,000 in order to make |
| ** calculations more simple. |
| ** |
| **========================================================== |
| */ |
| |
| #define _5M 5000000 |
| static u_long div_10M[] = |
| {2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M}; |
| |
| |
| /*=============================================================== |
| ** |
| ** Prepare io register values used by ncr_init() according |
| ** to selected and supported features. |
| ** |
| ** NCR chips allow burst lengths of 2, 4, 8, 16, 32, 64, 128 |
| ** transfers. 32,64,128 are only supported by 875 and 895 chips. |
| ** We use log base 2 (burst length) as internal code, with |
| ** value 0 meaning "burst disabled". |
| ** |
| **=============================================================== |
| */ |
| |
| /* |
| * Burst length from burst code. |
| */ |
| #define burst_length(bc) (!(bc))? 0 : 1 << (bc) |
| |
| /* |
| * Burst code from io register bits. Burst enable is ctest0 for c720 |
| */ |
| #define burst_code(dmode, ctest0) \ |
| (ctest0) & 0x80 ? 0 : (((dmode) & 0xc0) >> 6) + 1 |
| |
| /* |
| * Set initial io register bits from burst code. |
| */ |
| static inline void ncr_init_burst(struct ncb *np, u_char bc) |
| { |
| u_char *be = &np->rv_ctest0; |
| *be &= ~0x80; |
| np->rv_dmode &= ~(0x3 << 6); |
| np->rv_ctest5 &= ~0x4; |
| |
| if (!bc) { |
| *be |= 0x80; |
| } else { |
| --bc; |
| np->rv_dmode |= ((bc & 0x3) << 6); |
| np->rv_ctest5 |= (bc & 0x4); |
| } |
| } |
| |
| static void __init ncr_prepare_setting(struct ncb *np) |
| { |
| u_char burst_max; |
| u_long period; |
| int i; |
| |
| /* |
| ** Save assumed BIOS setting |
| */ |
| |
| np->sv_scntl0 = INB(nc_scntl0) & 0x0a; |
| np->sv_scntl3 = INB(nc_scntl3) & 0x07; |
| np->sv_dmode = INB(nc_dmode) & 0xce; |
| np->sv_dcntl = INB(nc_dcntl) & 0xa8; |
| np->sv_ctest0 = INB(nc_ctest0) & 0x84; |
| np->sv_ctest3 = INB(nc_ctest3) & 0x01; |
| np->sv_ctest4 = INB(nc_ctest4) & 0x80; |
| np->sv_ctest5 = INB(nc_ctest5) & 0x24; |
| np->sv_gpcntl = INB(nc_gpcntl); |
| np->sv_stest2 = INB(nc_stest2) & 0x20; |
| np->sv_stest4 = INB(nc_stest4); |
| |
| /* |
| ** Wide ? |
| */ |
| |
| np->maxwide = (np->features & FE_WIDE)? 1 : 0; |
| |
| /* |
| * Guess the frequency of the chip's clock. |
| */ |
| if (np->features & FE_ULTRA) |
| np->clock_khz = 80000; |
| else |
| np->clock_khz = 40000; |
| |
| /* |
| * Get the clock multiplier factor. |
| */ |
| if (np->features & FE_QUAD) |
| np->multiplier = 4; |
| else if (np->features & FE_DBLR) |
| np->multiplier = 2; |
| else |
| np->multiplier = 1; |
| |
| /* |
| * Measure SCSI clock frequency for chips |
| * it may vary from assumed one. |
| */ |
| if (np->features & FE_VARCLK) |
| ncr_getclock(np, np->multiplier); |
| |
| /* |
| * Divisor to be used for async (timer pre-scaler). |
| */ |
| i = np->clock_divn - 1; |
| while (--i >= 0) { |
| if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz > div_10M[i]) { |
| ++i; |
| break; |
| } |
| } |
| np->rv_scntl3 = i+1; |
| |
| /* |
| * Minimum synchronous period factor supported by the chip. |
| * Btw, 'period' is in tenths of nanoseconds. |
| */ |
| |
| period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz; |
| if (period <= 250) np->minsync = 10; |
| else if (period <= 303) np->minsync = 11; |
| else if (period <= 500) np->minsync = 12; |
| else np->minsync = (period + 40 - 1) / 40; |
| |
| /* |
| * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2). |
| */ |
| |
| if (np->minsync < 25 && !(np->features & FE_ULTRA)) |
| np->minsync = 25; |
| |
| /* |
| * Maximum synchronous period factor supported by the chip. |
| */ |
| |
| period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz); |
| np->maxsync = period > 2540 ? 254 : period / 10; |
| |
| /* |
| ** Prepare initial value of other IO registers |
| */ |
| #if defined SCSI_NCR_TRUST_BIOS_SETTING |
| np->rv_scntl0 = np->sv_scntl0; |
| np->rv_dmode = np->sv_dmode; |
| np->rv_dcntl = np->sv_dcntl; |
| np->rv_ctest0 = np->sv_ctest0; |
| np->rv_ctest3 = np->sv_ctest3; |
| np->rv_ctest4 = np->sv_ctest4; |
| np->rv_ctest5 = np->sv_ctest5; |
| burst_max = burst_code(np->sv_dmode, np->sv_ctest0); |
| #else |
| |
| /* |
| ** Select burst length (dwords) |
| */ |
| burst_max = driver_setup.burst_max; |
| if (burst_max == 255) |
| burst_max = burst_code(np->sv_dmode, np->sv_ctest0); |
| if (burst_max > 7) |
| burst_max = 7; |
| if (burst_max > np->maxburst) |
| burst_max = np->maxburst; |
| |
| /* |
| ** Select all supported special features |
| */ |
| if (np->features & FE_ERL) |
| np->rv_dmode |= ERL; /* Enable Read Line */ |
| if (np->features & FE_BOF) |
| np->rv_dmode |= BOF; /* Burst Opcode Fetch */ |
| if (np->features & FE_ERMP) |
| np->rv_dmode |= ERMP; /* Enable Read Multiple */ |
| if (np->features & FE_PFEN) |
| np->rv_dcntl |= PFEN; /* Prefetch Enable */ |
| if (np->features & FE_CLSE) |
| np->rv_dcntl |= CLSE; /* Cache Line Size Enable */ |
| if (np->features & FE_WRIE) |
| np->rv_ctest3 |= WRIE; /* Write and Invalidate */ |
| if (np->features & FE_DFS) |
| np->rv_ctest5 |= DFS; /* Dma Fifo Size */ |
| if (np->features & FE_MUX) |
| np->rv_ctest4 |= MUX; /* Host bus multiplex mode */ |
| if (np->features & FE_EA) |
| np->rv_dcntl |= EA; /* Enable ACK */ |
| if (np->features & FE_EHP) |
| np->rv_ctest0 |= EHP; /* Even host parity */ |
| |
| /* |
| ** Select some other |
| */ |
| if (driver_setup.master_parity) |
| np->rv_ctest4 |= MPEE; /* Master parity checking */ |
| if (driver_setup.scsi_parity) |
| np->rv_scntl0 |= 0x0a; /* full arb., ena parity, par->ATN */ |
| |
| /* |
| ** Get SCSI addr of host adapter (set by bios?). |
| */ |
| if (np->myaddr == 255) { |
| np->myaddr = INB(nc_scid) & 0x07; |
| if (!np->myaddr) |
| np->myaddr = SCSI_NCR_MYADDR; |
| } |
| |
| #endif /* SCSI_NCR_TRUST_BIOS_SETTING */ |
| |
| /* |
| * Prepare initial io register bits for burst length |
| */ |
| ncr_init_burst(np, burst_max); |
| |
| /* |
| ** Set SCSI BUS mode. |
| ** |
| ** - ULTRA2 chips (895/895A/896) report the current |
| ** BUS mode through the STEST4 IO register. |
| ** - For previous generation chips (825/825A/875), |
| ** user has to tell us how to check against HVD, |
| ** since a 100% safe algorithm is not possible. |
| */ |
| np->scsi_mode = SMODE_SE; |
| if (np->features & FE_DIFF) { |
| switch(driver_setup.diff_support) { |
| case 4: /* Trust previous settings if present, then GPIO3 */ |
| if (np->sv_scntl3) { |
| if (np->sv_stest2 & 0x20) |
| np->scsi_mode = SMODE_HVD; |
| break; |
| } |
| case 3: /* SYMBIOS controllers report HVD through GPIO3 */ |
| if (INB(nc_gpreg) & 0x08) |
| break; |
| case 2: /* Set HVD unconditionally */ |
| np->scsi_mode = SMODE_HVD; |
| case 1: /* Trust previous settings for HVD */ |
| if (np->sv_stest2 & 0x20) |
| np->scsi_mode = SMODE_HVD; |
| break; |
| default:/* Don't care about HVD */ |
| break; |
| } |
| } |
| if (np->scsi_mode == SMODE_HVD) |
| np->rv_stest2 |= 0x20; |
| |
| /* |
| ** Set LED support from SCRIPTS. |
| ** Ignore this feature for boards known to use a |
| ** specific GPIO wiring and for the 895A or 896 |
| ** that drive the LED directly. |
| ** Also probe initial setting of GPIO0 as output. |
| */ |
| if ((driver_setup.led_pin) && |
| !(np->features & FE_LEDC) && !(np->sv_gpcntl & 0x01)) |
| np->features |= FE_LED0; |
| |
| /* |
| ** Set irq mode. |
| */ |
| switch(driver_setup.irqm & 3) { |
| case 2: |
| np->rv_dcntl |= IRQM; |
| break; |
| case 1: |
| np->rv_dcntl |= (np->sv_dcntl & IRQM); |
| break; |
| default: |
| break; |
| } |
| |
| /* |
| ** Configure targets according to driver setup. |
| ** Allow to override sync, wide and NOSCAN from |
| ** boot command line. |
| */ |
| for (i = 0 ; i < MAX_TARGET ; i++) { |
| struct tcb *tp = &np->target[i]; |
| |
| tp->usrsync = driver_setup.default_sync; |
| tp->usrwide = driver_setup.max_wide; |
| tp->usrtags = MAX_TAGS; |
| tp->period = 0xffff; |
| if (!driver_setup.disconnection) |
| np->target[i].usrflag = UF_NODISC; |
| } |
| |
| /* |
| ** Announce all that stuff to user. |
| */ |
| |
| printk(KERN_INFO "%s: ID %d, Fast-%d%s%s\n", ncr_name(np), |
| np->myaddr, |
| np->minsync < 12 ? 40 : (np->minsync < 25 ? 20 : 10), |
| (np->rv_scntl0 & 0xa) ? ", Parity Checking" : ", NO Parity", |
| (np->rv_stest2 & 0x20) ? ", Differential" : ""); |
| |
| if (bootverbose > 1) { |
| printk (KERN_INFO "%s: initial SCNTL3/DMODE/DCNTL/CTEST3/4/5 = " |
| "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n", |
| ncr_name(np), np->sv_scntl3, np->sv_dmode, np->sv_dcntl, |
| np->sv_ctest3, np->sv_ctest4, np->sv_ctest5); |
| |
| printk (KERN_INFO "%s: final SCNTL3/DMODE/DCNTL/CTEST3/4/5 = " |
| "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n", |
| ncr_name(np), np->rv_scntl3, np->rv_dmode, np->rv_dcntl, |
| np->rv_ctest3, np->rv_ctest4, np->rv_ctest5); |
| } |
| |
| if (bootverbose && np->paddr2) |
| printk (KERN_INFO "%s: on-chip RAM at 0x%lx\n", |
| ncr_name(np), np->paddr2); |
| } |
| |
| /*========================================================== |
| ** |
| ** |
| ** Done SCSI commands list management. |
| ** |
| ** We donnot enter the scsi_done() callback immediately |
| ** after a command has been seen as completed but we |
| ** insert it into a list which is flushed outside any kind |
| ** of driver critical section. |
| ** This allows to do minimal stuff under interrupt and |
| ** inside critical sections and to also avoid locking up |
| ** on recursive calls to driver entry points under SMP. |
| ** In fact, the only kernel point which is entered by the |
| ** driver with a driver lock set is kmalloc(GFP_ATOMIC) |
| ** that shall not reenter the driver under any circumstances, |
| ** AFAIK. |
| ** |
| **========================================================== |
| */ |
| static inline void ncr_queue_done_cmd(struct ncb *np, struct scsi_cmnd *cmd) |
| { |
| unmap_scsi_data(np, cmd); |
| cmd->host_scribble = (char *) np->done_list; |
| np->done_list = cmd; |
| } |
| |
| static inline void ncr_flush_done_cmds(struct scsi_cmnd *lcmd) |
| { |
| struct scsi_cmnd *cmd; |
| |
| while (lcmd) { |
| cmd = lcmd; |
| lcmd = (struct scsi_cmnd *) cmd->host_scribble; |
| cmd->scsi_done(cmd); |
| } |
| } |
| |
| /*========================================================== |
| ** |
| ** |
| ** Prepare the next negotiation message if needed. |
| ** |
| ** Fill in the part of message buffer that contains the |
| ** negotiation and the nego_status field of the CCB. |
| ** Returns the size of the message in bytes. |
| ** |
| ** |
| **========================================================== |
| */ |
| |
| |
| static int ncr_prepare_nego(struct ncb *np, struct ccb *cp, u_char *msgptr) |
| { |
| struct tcb *tp = &np->target[cp->target]; |
| int msglen = 0; |
| int nego = 0; |
| struct scsi_target *starget = tp->starget; |
| |
| /* negotiate wide transfers ? */ |
| if (!tp->widedone) { |
| if (spi_support_wide(starget)) { |
| nego = NS_WIDE; |
| } else |
| tp->widedone=1; |
| } |
| |
| /* negotiate synchronous transfers? */ |
| if (!nego && !tp->period) { |
| if (spi_support_sync(starget)) { |
| nego = NS_SYNC; |
| } else { |
| tp->period =0xffff; |
| dev_info(&starget->dev, "target did not report SYNC.\n"); |
| } |
| } |
| |
| switch (nego) { |
| case NS_SYNC: |
| msgptr[msglen++] = M_EXTENDED; |
| msgptr[msglen++] = 3; |
| msgptr[msglen++] = M_X_SYNC_REQ; |
| msgptr[msglen++] = tp->maxoffs ? tp->minsync : 0; |
| msgptr[msglen++] = tp->maxoffs; |
| break; |
| case NS_WIDE: |
| msgptr[msglen++] = M_EXTENDED; |
| msgptr[msglen++] = 2; |
| msgptr[msglen++] = M_X_WIDE_REQ; |
| msgptr[msglen++] = tp->usrwide; |
| break; |
| } |
| |
| cp->nego_status = nego; |
| |
| if (nego) { |
| tp->nego_cp = cp; |
| if (DEBUG_FLAGS & DEBUG_NEGO) { |
| ncr_print_msg(cp, nego == NS_WIDE ? |
| "wide msgout":"sync_msgout", msgptr); |
| } |
| } |
| |
| return msglen; |
| } |
| |
| |
| |
| /*========================================================== |
| ** |
| ** |
| ** Start execution of a SCSI command. |
| ** This is called from the generic SCSI driver. |
| ** |
| ** |
| **========================================================== |
| */ |
| static int ncr_queue_command (struct ncb *np, struct scsi_cmnd *cmd) |
| { |
| struct scsi_device *sdev = cmd->device; |
| struct tcb *tp = &np->target[sdev->id]; |
| struct lcb *lp = tp->lp[sdev->lun]; |
| struct ccb *cp; |
| |
| int segments; |
| u_char idmsg, *msgptr; |
| u32 msglen; |
| int direction; |
| u32 lastp, goalp; |
| |
| /*--------------------------------------------- |
| ** |
| ** Some shortcuts ... |
| ** |
| **--------------------------------------------- |
| */ |
| if ((sdev->id == np->myaddr ) || |
| (sdev->id >= MAX_TARGET) || |
| (sdev->lun >= MAX_LUN )) { |
| return(DID_BAD_TARGET); |
| } |
| |
| /*--------------------------------------------- |
| ** |
| ** Complete the 1st TEST UNIT READY command |
| ** with error condition if the device is |
| ** flagged NOSCAN, in order to speed up |
| ** the boot. |
| ** |
| **--------------------------------------------- |
| */ |
| if ((cmd->cmnd[0] == 0 || cmd->cmnd[0] == 0x12) && |
| (tp->usrflag & UF_NOSCAN)) { |
| tp->usrflag &= ~UF_NOSCAN; |
| return DID_BAD_TARGET; |
| } |
| |
| if (DEBUG_FLAGS & DEBUG_TINY) { |
| PRINT_ADDR(cmd, "CMD=%x ", cmd->cmnd[0]); |
| } |
| |
| /*--------------------------------------------------- |
| ** |
| ** Assign a ccb / bind cmd. |
| ** If resetting, shorten settle_time if necessary |
| ** in order to avoid spurious timeouts. |
| ** If resetting or no free ccb, |
| ** insert cmd into the waiting list. |
| ** |
| **---------------------------------------------------- |
| */ |
| if (np->settle_time && cmd->timeout_per_command >= HZ) { |
| u_long tlimit = ktime_get(cmd->timeout_per_command - HZ); |
| if (ktime_dif(np->settle_time, tlimit) > 0) |
| np->settle_time = tlimit; |
| } |
| |
| if (np->settle_time || !(cp=ncr_get_ccb (np, cmd))) { |
| insert_into_waiting_list(np, cmd); |
| return(DID_OK); |
| } |
| cp->cmd = cmd; |
| |
| /*---------------------------------------------------- |
| ** |
| ** Build the identify / tag / sdtr message |
| ** |
| **---------------------------------------------------- |
| */ |
| |
| idmsg = M_IDENTIFY | sdev->lun; |
| |
| if (cp ->tag != NO_TAG || |
| (cp != np->ccb && np->disc && !(tp->usrflag & UF_NODISC))) |
| idmsg |= 0x40; |
| |
| msgptr = cp->scsi_smsg; |
| msglen = 0; |
| msgptr[msglen++] = idmsg; |
| |
| if (cp->tag != NO_TAG) { |
| char order = np->order; |
| |
| /* |
| ** Force ordered tag if necessary to avoid timeouts |
| ** and to preserve interactivity. |
| */ |
| if (lp && ktime_exp(lp->tags_stime)) { |
| if (lp->tags_smap) { |
| order = M_ORDERED_TAG; |
| if ((DEBUG_FLAGS & DEBUG_TAGS)||bootverbose>2){ |
| PRINT_ADDR(cmd, |
| "ordered tag forced.\n"); |
| } |
| } |
| lp->tags_stime = ktime_get(3*HZ); |
| lp->tags_smap = lp->tags_umap; |
| } |
| |
| if (order == 0) { |
| /* |
| ** Ordered write ops, unordered read ops. |
| */ |
| switch (cmd->cmnd[0]) { |
| case 0x08: /* READ_SMALL (6) */ |
| case 0x28: /* READ_BIG (10) */ |
| case 0xa8: /* READ_HUGE (12) */ |
| order = M_SIMPLE_TAG; |
| break; |
| default: |
| order = M_ORDERED_TAG; |
| } |
| } |
| msgptr[msglen++] |