Hi All, Attached is a driver for the NCR Dual 700 Microchannel card. Since the chip engine of this card is the 53c700-66, which appeared in quite a few other SCSI cards as well, I've abstracted the chip function (in much the same way as the 8390 chip function is abstracted in network cards) so that it should be easy to link it into any other SCSI card using it. As you can see, the actual board specific code is about 150 lines. The chip driver is full featured (sync (where supported), disconnects and tag command queueing). It will drive both single ended and differential interfaces and uses the new SCSI error handler. I know we have two drivers that claim to do these chips (53c7xx and 53c7,8xx) but if you actually compile them for this chip, they are completely broken. The chip itself is extremely primitive (not having the table indirect mode, which is the backbone of most of the later drivers) so it makes much more sense to give it its own driver. The chip driver is currently I/O mapped (because the only cards I know using the chip are I/O mapped), but could easily be made memory mapped as well, just let me know. James Bottomley Hansen Partnership.
Index: linux/2.4/MAINTAINERS diff -u linux/2.4/MAINTAINERS:1.1.1.13 linux/2.4/MAINTAINERS:1.1.1.13.4.1 --- linux/2.4/MAINTAINERS:1.1.1.13 Fri May 4 11:27:28 2001 +++ linux/2.4/MAINTAINERS Fri May 11 20:15:24 2001 @@ -81,6 +81,12 @@ L: [EMAIL PROTECTED] S: Maintained +53C700 AND 53C700-66 SCSI DRIVER +P: James E.J. Bottomley +M: [EMAIL PROTECTED] +L: [EMAIL PROTECTED] +S: Maintained + 6PACK NETWORK DRIVER FOR AX.25 P: Andreas Koensgen M: [EMAIL PROTECTED] @@ -877,6 +883,12 @@ P: Volker Lendecke M: [EMAIL PROTECTED] L: [EMAIL PROTECTED] +S: Maintained + +NCR DUAL 700 SCSI DRIVER (MICROCHANNEL) +P: James E.J. Bottomley +M: [EMAIL PROTECTED] +L: [EMAIL PROTECTED] S: Maintained NETFILTER Index: linux/2.4/Documentation/Configure.help diff -u linux/2.4/Documentation/Configure.help:1.1.1.13 linux/2.4/Documentation/Configure.help:1.1.1.13.4.1 --- linux/2.4/Documentation/Configure.help:1.1.1.13 Fri May 4 11:29:34 2001 +++ linux/2.4/Documentation/Configure.help Fri May 11 20:15:24 2001 @@ -5934,6 +5934,15 @@ port or memory mapped. You should know what you have. The most common card, Trantor T130B, uses port mapped mode. +NCR Dual 700 MCA SCSI support +CONFIG_SCSI_NCR_D700 + This is a driver for the Microchannel Dual 700 card produced by + NCR and commonly used in 345x/35xx/4100 class machines. It always + tries to negotiate sync and uses tag command queueing. + + Unless you have an NCR manufactured machine, the chances are that you do + not have this SCSI card, so say N. + NCR53c7,8xx SCSI support CONFIG_SCSI_NCR53C7xx This is a driver for the 53c7 and 8xx NCR family of SCSI Index: linux/2.4/drivers/scsi/53c700.c diff -u /dev/null linux/2.4/drivers/scsi/53c700.c:1.1.4.2 --- /dev/null Sat May 12 12:05:15 2001 +++ linux/2.4/drivers/scsi/53c700.c Sat May 12 11:52:50 2001 @@ -0,0 +1,1749 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* NCR (or Symbios) 53c700 and 53c700-66 Driver + * + * Copyright (C) 2001 by [EMAIL PROTECTED] +**----------------------------------------------------------------------------- +** +** 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. +** +**----------------------------------------------------------------------------- + */ + +/* Notes: + * + * This driver is designed exclusively for these chips (virtually the + * earliest of the scripts engine chips). They need their own drivers + * because they are missing so many of the scripts and snazzy register + * features of their elder brothers (the 710, 720 and 770). + * + * The 700 is the lowliest of the line, it can only do async SCSI. + * The 700-66 can at least do synchronous SCSI up to 10MHz. + * + * The 700 chip has no host bus interface logic of its own. However, + * it is usually mapped to a location with well defined register + * offsets. Therefore, if you can determine the base address and the + * irq your board incorporating this chip uses, you can probably use + * this driver to run it (although you'll probably have to write a + * minimal wrapper for the purpose---see the NCR_D700 driver for + * details about how to do this). + * + * + * TODO List: + * + * 1. Better statistics in the proc fs + * + * 2. Implement message queue (queues SCSI messages like commands) and make + * the abort and device reset functions use them. + * */ + +/* CHANGELOG + * + * Version 2.1 + * + * Initial modularisation from the D700. See NCR_D700.c for the rest of + * the changelog. + * */ +#define NCR_700_VERSION "2.1" + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/proc_fs.h> +#include <linux/init.h> +#include <linux/mca.h> +#include <asm/dma.h> +#include <asm/system.h> +#include <asm/spinlock.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/byteorder.h> +#include <linux/blk.h> + +#ifdef MODULE +#include <linux/module.h> +#endif + + +#include "scsi.h" +#include "hosts.h" +#include "constants.h" + +#include "53c700.h" + +#ifdef NCR_700_DEBUG +#define STATIC +#else +#define STATIC static +#endif + +#ifdef MODULE +MODULE_AUTHOR("James Bottomley"); +MODULE_DESCRIPTION("53c700 and 53c700-66 Driver"); +#endif + +/* This is the script */ +#include "53c700_d.h" + + +STATIC int NCR_700_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +STATIC int NCR_700_abort(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_bus_reset(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_dev_reset(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_host_reset(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_proc_directory_info(char *, char **, off_t, int, int, int); +STATIC void NCR_700_chip_setup(struct Scsi_Host *host); +STATIC void NCR_700_chip_reset(struct Scsi_Host *host); + +static char *NCR_700_phase[] = { + "", + "after selection", + "before command phase", + "after command phase", + "after status phase", + "after data in phase", + "after data out phase", + "during data phase", +}; + +static char *NCR_700_condition[] = { + "", + "NOT MSG_OUT", + "UNEXPECTED PHASE", + "NOT MSG_IN", + "UNEXPECTED MSG", + "MSG_IN", + "SDTR_MSG RECEIVED", + "REJECT_MSG RECEIVED", + "DISCONNECT_MSG RECEIVED", + "MSG_OUT", + "DATA_IN", + +}; + +static char *NCR_700_fatal_messages[] = { + "unexpected message after reselection", + "still MSG_OUT after message injection", + "not MSG_IN after selection", + "Illegal message length received", +}; + +static char *NCR_700_SBCL_bits[] = { + "IO ", + "CD ", + "MSG ", + "ATN ", + "SEL ", + "BSY ", + "ACK ", + "REQ ", +}; + +static char *NCR_700_SBCL_to_phase[] = { + "DATA_OUT", + "DATA_IN", + "CMD_OUT", + "STATE", + "ILLEGAL PHASE", + "ILLEGAL PHASE", + "MSG OUT", + "MSG IN", +}; + +static __u8 NCR_700_SDTR_msg[] = { + 0x01, /* Extended message */ + 0x03, /* Extended message Length */ + 0x01, /* SDTR Extended message */ + NCR_700_MIN_PERIOD, + NCR_700_MAX_OFFSET +}; + +struct Scsi_Host * __init +NCR_700_detect(Scsi_Host_Template *tpnt, + struct NCR_700_Host_Parameters *hostdata) +{ + __u32 *script = kmalloc(sizeof(SCRIPT), GFP_KERNEL); + __u32 pScript; + struct Scsi_Host *host; + static int banner = 0; + int j; + + /* Fill in the missing routines from the host template */ + tpnt->queuecommand = NCR_700_queuecommand; + tpnt->eh_abort_handler = NCR_700_abort; + tpnt->eh_device_reset_handler = NCR_700_dev_reset; + tpnt->eh_bus_reset_handler = NCR_700_bus_reset; + tpnt->eh_host_reset_handler = NCR_700_host_reset; + tpnt->can_queue = NCR_700_COMMAND_SLOTS_PER_HOST; + tpnt->sg_tablesize = NCR_700_SG_SEGMENTS; + tpnt->cmd_per_lun = NCR_700_MAX_TAGS; + tpnt->use_clustering = DISABLE_CLUSTERING; + tpnt->use_new_eh_code = 1; + tpnt->proc_info = NCR_700_proc_directory_info; + + if(tpnt->name == NULL) + tpnt->name = "53c700"; + if(tpnt->proc_name == NULL) + tpnt->proc_name = "53c700"; + + + host = scsi_register(tpnt, 4); + if(script == NULL) { + printk(KERN_ERR "53c700: Failed to allocate script, detatching\n"); + scsi_unregister(host); + return NULL; + } + + hostdata->slots = kmalloc(sizeof(struct NCR_700_command_slot) * +NCR_700_COMMAND_SLOTS_PER_HOST, GFP_KERNEL); + if(hostdata->slots == NULL) { + printk(KERN_ERR "53c700: Failed to allocate command slots, +detatching\n"); + scsi_unregister(host); + return NULL; + } + memset(hostdata->slots, 0, sizeof(struct NCR_700_command_slot) * +NCR_700_COMMAND_SLOTS_PER_HOST); + for(j = 0; j < NCR_700_COMMAND_SLOTS_PER_HOST; j++) { + if(j == 0) + hostdata->free_list = &hostdata->slots[j]; + else + hostdata->slots[j-1].ITL_forw = &hostdata->slots[j]; + hostdata->slots[j].state = NCR_700_SLOT_FREE; + } + host->hostdata[0] = (__u32)hostdata; + memcpy(script, SCRIPT, sizeof(SCRIPT)); + /* bus physical address of script */ + pScript = virt_to_bus(script); + /* adjust all labels to be bus physical */ + for(j = 0; j < PATCHES; j++) { + script[LABELPATCHES[j]] += pScript; + } + /* now patch up fixed addresses */ + script_patch_32(script, MessageLocation, + virt_to_bus(&hostdata->msgout[0])); + script_patch_32(script, StatusAddress, + virt_to_bus(&hostdata->status)); + script_patch_32(script, ReceiveMsgAddress, + virt_to_bus(&hostdata->msgin[0])) + hostdata->script = script; + hostdata->pScript = pScript; + hostdata->state = NCR_700_HOST_FREE; + spin_lock_init(&hostdata->lock); + hostdata->cmd = NULL; + host->max_id = 7; + host->max_lun = NCR_700_MAX_LUNS; + host->unique_id = hostdata->base; + host->base = hostdata->base; + host->hostdata[0] = (unsigned long)hostdata; + /* kick the chip */ + outb(0xff, host->base + CTEST9_REG); + hostdata->rev = (inb(host->base + CTEST7_REG)<<4) & 0x0f; + hostdata->fast = (inb(host->base + CTEST9_REG) == 0); + if(banner == 0) { + printk(KERN_NOTICE "53c700: Version " NCR_700_VERSION " By [EMAIL PROTECTED]\n"); + banner = 1; + } + printk(KERN_NOTICE "scsi%d: %s rev %d %s\n", host->host_no, + hostdata->fast ? "53c700-66" : "53c700", + hostdata->rev, hostdata->differential ? + "(Differential)" : ""); + /* reset the chip */ + NCR_700_chip_reset(host); + outb(ASYNC_OPERATION , host->base + SXFER_REG); + + return host; +} + +int +NCR_700_release(struct Scsi_Host *host) +{ + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + + kfree(hostdata->script); + return 1; +} + +static inline __u8 +NCR_700_identify(int can_disconnect, __u8 lun) +{ + return IDENTIFY_BASE | + ((can_disconnect) ? 0x40 : 0) | + (lun & NCR_700_LUN_MASK); +} + +/* + * Function : static int datapath_residual (Scsi_Host *host) + * + * Purpose : return residual data count of what's in the chip. If you + * really want to know what this function is doing, it's almost a + * direct transcription of the algorithm described in the 53c710 + * guide, except that the DBC and DFIFO registers are only 6 bits + * wide. + * + * Inputs : host - SCSI host */ +static inline int +NCR_700_data_residual (struct Scsi_Host *host) { + int count, synchronous; + unsigned int ddir; + + count = ((inb(host->base + DFIFO_REG) & 0x3f) - + (inl(host->base + DBC_REG) & 0x3f)) & 0x3f; + + synchronous = inb(host->base + SXFER_REG) & 0x0f; + + /* get the data direction */ + ddir = inb(host->base + CTEST0_REG) & 0x01; + + if (ddir) { + /* Receive */ + if (synchronous) + count += (inb(host->base + SSTAT2_REG) & 0xf0) >> 4; + else + if (inb(host->base + SSTAT1_REG) & SIDL_REG_FULL) + ++count; + } else { + /* Send */ + __u8 sstat = inb(host->base + SSTAT1_REG); + if (sstat & SODL_REG_FULL) + ++count; + if (synchronous && (sstat & SODR_REG_FULL)) + ++count; + } + return count; +} + +/* print out the SCSI wires and corresponding phase from the SBCL register + * in the chip */ +static inline char * +sbcl_to_string(__u8 sbcl) +{ + int i; + static char ret[256]; + + ret[0]='\0'; + for(i=0; i<8; i++) { + if((1<<i) & sbcl) + strcat(ret, NCR_700_SBCL_bits[i]); + } + strcat(ret, NCR_700_SBCL_to_phase[sbcl & 0x07]); + return ret; +} + +static inline __u8 +bitmap_to_number(__u8 bitmap) +{ + __u8 i; + + for(i=0; i<8 && !(bitmap &(1<<i)); i++) + ; + return i; +} + +/* Pull a slot off the free list */ +STATIC struct NCR_700_command_slot * +find_empty_slot(struct NCR_700_Host_Parameters *hostdata) +{ + struct NCR_700_command_slot *slot = hostdata->free_list; + + if(slot == NULL) { + /* sanity check */ + if(hostdata->command_slot_count != NCR_700_COMMAND_SLOTS_PER_HOST) + printk(KERN_ERR "SLOTS FULL, but count is %d, should be %d\n", +hostdata->command_slot_count, NCR_700_COMMAND_SLOTS_PER_HOST); + return NULL; + } + + if(slot->state != NCR_700_SLOT_FREE) + /* should panic! */ + printk(KERN_ERR "BUSY SLOT ON FREE LIST!!!\n"); + + + hostdata->free_list = slot->ITL_forw; + slot->ITL_forw = NULL; + + + /* NOTE: set the state to busy here, not queued, since this + * indicates the slot is in use and cannot be run by the IRQ + * finish routine. If we cannot queue the command when it + * is properly build, we then change to NCR_700_SLOT_QUEUED */ + slot->state = NCR_700_SLOT_BUSY; + hostdata->command_slot_count++; + + return slot; +} + +STATIC void +free_slot(struct NCR_700_command_slot *slot, + struct NCR_700_Host_Parameters *hostdata) +{ + int hash; + struct NCR_700_command_slot **forw, **back; + + + if((slot->state & NCR_700_SLOT_MASK) != NCR_700_SLOT_MAGIC) { + printk(" SLOT %p is not MAGIC!!!\n", slot); + } + if(slot->state == NCR_700_SLOT_FREE) { + printk(" SLOT %p is FREE!!!\n", slot); + } + /* remove from queues */ + if(slot->tag != NCR_700_NO_TAG) { + hash = hash_ITLQ(slot->cmnd->target, slot->cmnd->lun, + slot->tag); + if(slot->ITLQ_forw == NULL) + back = &hostdata->ITLQ_Hash_back[hash]; + else + back = &slot->ITLQ_forw->ITLQ_back; + + if(slot->ITLQ_back == NULL) + forw = &hostdata->ITLQ_Hash_forw[hash]; + else + forw = &slot->ITLQ_back->ITLQ_forw; + + *forw = slot->ITLQ_forw; + *back = slot->ITLQ_back; + } + hash = hash_ITL(slot->cmnd->target, slot->cmnd->lun); + if(slot->ITL_forw == NULL) + back = &hostdata->ITL_Hash_back[hash]; + else + back = &slot->ITL_forw->ITL_back; + + if(slot->ITL_back == NULL) + forw = &hostdata->ITL_Hash_forw[hash]; + else + forw = &slot->ITL_back->ITL_forw; + + *forw = slot->ITL_forw; + *back = slot->ITL_back; + + slot->resume_offset = 0; + slot->cmnd = NULL; + slot->state = NCR_700_SLOT_FREE; + slot->ITL_forw = hostdata->free_list; + hostdata->free_list = slot; + hostdata->command_slot_count--; +} + + +/* This routine really does very little. The command is indexed on + the ITL and (if tagged) the ITLQ lists in _queuecommand */ +STATIC void +save_for_reselection(struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, __u32 dsp) +{ + /* Its just possible that this gets executed twice */ + if(SCp != NULL) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + + slot->resume_offset = dsp; + } + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; +} + +/* Most likely nexus is the oldest in each case */ +STATIC inline struct NCR_700_command_slot * +find_ITL_Nexus(struct NCR_700_Host_Parameters *hostdata, __u8 pun, __u8 lun) +{ + int hash = hash_ITL(pun, lun); + struct NCR_700_command_slot *slot = hostdata->ITL_Hash_back[hash]; + while(slot != NULL && !(slot->cmnd->target == pun && + slot->cmnd->lun == lun)) + slot = slot->ITL_back; + return slot; +} + +STATIC inline struct NCR_700_command_slot * +find_ITLQ_Nexus(struct NCR_700_Host_Parameters *hostdata, __u8 pun, + __u8 lun, __u8 tag) +{ + int hash = hash_ITLQ(pun, lun, tag); + struct NCR_700_command_slot *slot = hostdata->ITLQ_Hash_back[hash]; + + while(slot != NULL && !(slot->cmnd->target == pun + && slot->cmnd->lun == lun && slot->tag == tag)) + slot = slot->ITLQ_back; + +#ifdef NCR_700_TAG_DEBUG + if(slot != NULL) { + struct NCR_700_command_slot *n = slot->ITLQ_back; + while(n != NULL && n->cmnd->target != pun + && n->cmnd->lun != lun && n->tag != tag) + n = n->ITLQ_back; + + if(n != NULL && n->cmnd->target == pun && n->cmnd->lun == lun + && n->tag == tag) { + printk("\n\n**WARNING: DUPLICATE tag %d\n", + tag); + } + } +#endif + return slot; +} + + + +/* This translates the SDTR message offset and period to a value + * which can be loaded into the SXFER_REG. + * + * NOTE: According to SCSI-2, the true transfer period is actually + * four times this period value (in ns) */ +STATIC inline __u8 +NCR_700_offset_period_to_sxfer(struct NCR_700_Host_Parameters *hostdata, + __u8 offset, __u8 period) +{ + int XFERP; + + XFERP = (period*4 * hostdata->clock)/1000 - 4; + if(offset > NCR_700_MAX_OFFSET) { + printk(KERN_WARNING "53c700: Offset %d exceeds maximum, setting to +%d\n", + offset, NCR_700_MAX_OFFSET); + offset = NCR_700_MAX_OFFSET; + } + if(XFERP < NCR_700_MIN_XFERP) { + printk(KERN_WARNING "53c700: XFERP %d is less than minium, setting to +%d\n", + XFERP, NCR_700_MIN_XFERP); + XFERP = NCR_700_MIN_XFERP; + } + return (offset & 0x0f) | (XFERP & 0x07)<<4; +} + + +STATIC inline void +NCR_700_scsi_done(struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, int result) +{ + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; + + if(SCp != NULL) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + + if(SCp->cmnd[0] == REQUEST_SENSE && SCp->cmnd[6] == +NCR_700_INTERNAL_SENSE_MAGIC) { +#ifdef NCR_700_DEBUG + printk(" ORIGINAL CMD %p RETURNED %d, new return is %d sense +is", + SCp, SCp->cmnd[7], result); + print_sense("53c700", SCp); +#endif + if(result == 0) + result = SCp->cmnd[7]; + } + + free_slot(slot, hostdata); + + SCp->host_scribble = NULL; + SCp->result = result; + SCp->scsi_done(SCp); + if(NCR_700_get_depth(SCp->device) == 0 || + NCR_700_get_depth(SCp->device) > NCR_700_MAX_TAGS) + printk(KERN_ERR "Invalid depth in NCR_700_scsi_done(): %d\n", + NCR_700_get_depth(SCp->device)); + NCR_700_set_depth(SCp->device, NCR_700_get_depth(SCp->device) - 1); + } else { + printk(KERN_ERR " SCSI DONE HAS NULL SCp\n"); + } +} + + +STATIC void +NCR_700_internal_bus_reset(struct Scsi_Host *host) +{ + /* Bus reset */ + outb(ASSERT_RST, host->base + SCNTL1_REG); + udelay(50); + outb(0, host->base + SCNTL1_REG); + +} + +STATIC void +NCR_700_chip_setup(struct Scsi_Host *host) +{ + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + + outb(1 << host->this_id, host->base + SCID_REG); + outb(0, host->base + SBCL_REG); + outb(0, host->base + SXFER_REG); + + outb(PHASE_MM_INT | SEL_TIMEOUT_INT | GROSS_ERR_INT | UX_DISC_INT + | RST_INT | PAR_ERR_INT | SELECT_INT, host->base + SIEN_REG); + + outb(ABORT_INT | INT_INST_INT | ILGL_INST_INT, host->base + DIEN_REG); + outb(BURST_LENGTH_8, host->base + DMODE_REG); + outb(FULL_ARBITRATION | PARITY | AUTO_ATN, host->base + SCNTL0_REG); + outb(LAST_DIS_ENBL | ENABLE_ACTIVE_NEGATION|GENERATE_RECEIVE_PARITY, + host->base + CTEST8_REG); + outb(ENABLE_SELECT, host->base + SCNTL1_REG); + if(hostdata->clock > 50) + printk(KERN_ERR "53c700: Clock speed %dMHz is too high: contact [EMAIL PROTECTED] to modify the divider\n", hostdata->clock); + /* Set the synchronous SCSI divider to 1 so we drive the + * synchronous command clock at this speed */ + outb(1, host->base + SBCL_REG); + /* set the clock bits to 0 which is a divider of 2 so the + * async frequency is exactly half this speed */ + outb(0x00, host->base + DCNTL_REG); +} + +STATIC void +NCR_700_chip_reset(struct Scsi_Host *host) +{ + /* Chip reset */ + outb(SOFTWARE_RESET, host->base + DCNTL_REG); + udelay(100); + + outb(0, host->base + DCNTL_REG); + + mdelay(1000); + + NCR_700_chip_setup(host); +} + +/* The heart of the message processing engine is that the instruction + * immediately after the INT is the normal case (and so must be CLEAR + * ACK). If we want to do something else, we call that routine in + * scripts and set temp to be the normal case + 8 (skipping the CLEAR + * ACK) so that the routine returns correctly to resume its activity + * */ +STATIC __u32 +process_extended_message(struct Scsi_Host *host, + struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, __u32 dsp, __u32 dsps) +{ + __u32 resume_offset = dsp, temp = dsp + 8; + __u8 pun = 0xff, lun = 0xff; + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + + switch(hostdata->msgin[2]) { + case A_SDTR_MSG: + if(SCp != NULL && NCR_700_is_flag_set(SCp->device, +NCR_700_DEV_BEGIN_SYNC_NEGOTIATION)) { + __u8 period = hostdata->msgin[3]; + __u8 offset = hostdata->msgin[4]; + __u8 sxfer; + + if(offset != 0 && period != 0) + sxfer = NCR_700_offset_period_to_sxfer(hostdata, +offset, period); + else + sxfer = 0; + + if(sxfer != NCR_700_get_SXFER(SCp->device)) { + printk(KERN_INFO "scsi%d: (%d:%d) Synchronous at +offset %d, period %dns\n", + host->host_no, pun, lun, + offset, period*4); + + NCR_700_set_SXFER(SCp->device, sxfer); + } + + + NCR_700_set_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); + NCR_700_clear_flag(SCp->device, +NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + + outb(NCR_700_get_SXFER(SCp->device), + host->base + SXFER_REG); + + } else { + /* SDTR message out of the blue, reject it */ + printk(KERN_WARNING "scsi%d Unexpected SDTR msg\n", + host->host_no); + hostdata->msgout[0] = A_REJECT_MSG; + script_patch_16(hostdata->script, MessageCount, 1); + /* SendMsgOut returns, so set up the return + * address */ + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + } + break; + + case A_WDTR_MSG: + printk(KERN_INFO "scsi%d: (%d:%d), Unsolicited WDTR after CMD, +Rejecting\n", + host->host_no, pun, lun); + hostdata->msgout[0] = A_REJECT_MSG; + script_patch_16(hostdata->script, MessageCount, 1); + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + + break; + + default: + printk(KERN_INFO "scsi%d (%d:%d): Unexpected message %s: ", + host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + print_msg(hostdata->msgin); + printk("\n"); + /* just reject it */ + hostdata->msgout[0] = A_REJECT_MSG; + script_patch_16(hostdata->script, MessageCount, 1); + /* SendMsgOut returns, so set up the return + * address */ + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + } + outl(temp, host->base + TEMP_REG); + return resume_offset; +} + +STATIC __u32 +process_message(struct Scsi_Host *host, struct NCR_700_Host_Parameters +*hostdata, + Scsi_Cmnd *SCp, __u32 dsp, __u32 dsps) +{ + /* work out where to return to */ + __u32 temp = dsp + 8, resume_offset = dsp; + __u8 pun = 0xff, lun = 0xff; + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + +#ifdef NCR_700_DEBUG + printk("scsi%d (%d:%d): message %s: ", host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + print_msg(hostdata->msgin); + printk("\n"); +#endif + + switch(hostdata->msgin[0]) { + + case A_EXTENDED_MSG: + return process_extended_message(host, hostdata, SCp, + dsp, dsps); + + case A_REJECT_MSG: + if(SCp != NULL && NCR_700_is_flag_set(SCp->device, +NCR_700_DEV_BEGIN_SYNC_NEGOTIATION)) { + /* Rejected our sync negotiation attempt */ + NCR_700_set_SXFER(SCp->device, 0); + NCR_700_set_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); + NCR_700_clear_flag(SCp->device, +NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + } else { + printk(KERN_WARNING "scsi%d (%d:%d) Unexpected REJECT Message +%s\n", + host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + /* however, just ignore it */ + } + break; + + case A_PARITY_ERROR_MSG: + printk("scsi%d (%d:%d) Parity Error!\n", host->host_no, + pun, lun); + NCR_700_internal_bus_reset(host); + break; + case A_SIMPLE_TAG_MSG: + printk("scsi%d (%d:%d) SIMPLE TAG %d %s\n", host->host_no, + pun, lun, hostdata->msgin[1], + NCR_700_phase[(dsps & 0xf00) >> 8]); + /* just ignore it */ + break; + default: + printk(KERN_INFO "scsi%d (%d:%d): Unexpected message %s: ", + host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + + print_msg(hostdata->msgin); + printk("\n"); + /* just reject it */ + hostdata->msgout[0] = A_REJECT_MSG; + script_patch_16(hostdata->script, MessageCount, 1); + /* SendMsgOut returns, so set up the return + * address */ + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + + break; + } + outl(temp, host->base + TEMP_REG); + return resume_offset; +} + +STATIC __u32 +process_script_interrupt(__u32 dsps, __u32 dsp, Scsi_Cmnd *SCp, + struct Scsi_Host *host, + struct NCR_700_Host_Parameters *hostdata) +{ + __u32 resume_offset = 0; + __u8 pun = 0xff, lun=0xff; + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + + if(dsps == A_GOOD_STATUS_AFTER_STATUS) { + DEBUG((" COMMAND COMPLETE, status=%02x\n", + hostdata->status)); + /* check for contingent allegiance contitions */ + if(status_byte(hostdata->status) == CHECK_CONDITION || + status_byte(hostdata->status) == COMMAND_TERMINATED) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + + DEBUG((" cmd %p has status %d, requesting sense\n", + SCp, hostdata->status)); + /* we can destroy the command here because the + * contingent allegiance condition will cause a + * retry which will re-copy the command from the + * saved data_cmnd */ + SCp->cmnd[0] = REQUEST_SENSE; + SCp->cmnd[1] = (SCp->lun & 0x7) << 5; + SCp->cmnd[2] = 0; + SCp->cmnd[3] = 0; + SCp->cmnd[4] = sizeof(SCp->sense_buffer); + SCp->cmnd[5] = 0; + SCp->cmd_len = 6; + /* Here's a quiet hack: the REQUEST_SENSE command is + * six bytes, so store a flag indicating that this + * was an internal sense request and the original + * status at the end of the command */ + SCp->cmnd[6] = NCR_700_INTERNAL_SENSE_MAGIC; + SCp->cmnd[7] = hostdata->status; + slot->SG[0].ins = SCRIPT_MOVE_DATA_IN | +sizeof(SCp->sense_buffer); + slot->SG[0].pAddr = virt_to_bus(SCp->sense_buffer); + slot->SG[1].ins = SCRIPT_RETURN; + slot->SG[1].pAddr = 0; + slot->resume_offset = hostdata->pScript; + + /* queue the command for reissue */ + slot->state = NCR_700_SLOT_QUEUED; + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; + } else { + if(status_byte(hostdata->status) == GOOD && + SCp->cmnd[0] == INQUIRY && SCp->use_sg == 0) { + /* Piggy back the tag queueing support + * on this command */ + if(((char *)SCp->request_buffer)[7] & 0x02) { + printk(KERN_INFO "scsi%d: (%d:%d) Enabling Tag +Command Queuing\n", host->host_no, pun, lun); + hostdata->tag_negotiated |= (1<<SCp->target); + } else { + hostdata->tag_negotiated &= ~(1<<SCp->target); + } + } + NCR_700_scsi_done(hostdata, SCp, hostdata->status); + } + } else if((dsps & 0xfffff0f0) == A_UNEXPECTED_PHASE) { + __u8 i = (dsps & 0xf00) >> 8; + + printk(KERN_ERR "scsi%d: (%d:%d), UNEXPECTED PHASE %s (%s)\n", + host->host_no, pun, lun, + NCR_700_phase[i], + sbcl_to_string(inb(host->base + SBCL_REG))); + printk(" len = %d, cmd =", SCp->cmd_len); + print_command(SCp->cmnd); + + NCR_700_internal_bus_reset(host); + } else if((dsps & 0xfffff000) == A_FATAL) { + int i = (dsps & 0xfff); + + printk(KERN_ERR "scsi%d: (%d:%d) FATAL ERROR: %s\n", + host->host_no, pun, lun, NCR_700_fatal_messages[i]); + if(dsps == A_FATAL_ILLEGAL_MSG_LENGTH) { + printk(KERN_ERR " msg begins %02x %02x\n", + hostdata->msgin[0], hostdata->msgin[1]); + } + NCR_700_internal_bus_reset(host); + } else if((dsps & 0xfffff0f0) == A_DISCONNECT) { +#ifdef NCR_700_DEBUG + __u8 i = (dsps & 0xf00) >> 8; + + printk("scsi%d: (%d:%d), DISCONNECTED (%d) %s\n", + host->host_no, pun, lun, + i, NCR_700_phase[i]); +#endif + save_for_reselection(hostdata, SCp, dsp); + + } else if(dsps == A_RESELECTION_IDENTIFIED) { + __u8 lun = hostdata->msgin[0] & 0x1f; + struct NCR_700_command_slot *slot; + __u8 reselection_id = hostdata->reselection_id; + + hostdata->reselection_id = 0xff; + DEBUG(("scsi%d: (%d:%d) RESELECTED!\n", + host->host_no, reselection_id, lun)); + /* clear the reselection indicator */ + if(hostdata->msgin[1] == A_SIMPLE_TAG_MSG) { + slot = find_ITLQ_Nexus(hostdata, reselection_id, + lun, hostdata->msgin[2]); + } else { + slot = find_ITL_Nexus(hostdata, reselection_id, lun); + } + retry: + if(slot == NULL) { + struct NCR_700_command_slot *s = find_ITL_Nexus(hostdata, +reselection_id, lun); + printk(KERN_ERR "scsi%d: (%d:%d) RESELECTED but no saved +command (MSG = %02x %02x %02x)!!\n", + host->host_no, reselection_id, lun, + hostdata->msgin[0], hostdata->msgin[1], + hostdata->msgin[2]); + printk(KERN_ERR " OUTSTANDING TAGS:"); + while(s != NULL) { + if(s->cmnd->target == reselection_id && + s->cmnd->lun == lun) { + printk("%d ", s->tag); + if(s->tag == hostdata->msgin[2]) { + printk(" ***FOUND*** \n"); + slot = s; + goto retry; + } + + } + s = s->ITL_back; + } + printk("\n"); + } else { + if(hostdata->state != NCR_700_HOST_BUSY) + printk(KERN_ERR "scsi%d: FATAL, host not busy during +valid reselection!\n", + host->host_no); + resume_offset = slot->resume_offset; + hostdata->cmd = slot->cmnd; + + /* re-patch for this command */ + script_patch_32_abs(hostdata->script, CommandAddress, + virt_to_bus(slot->cmnd->cmnd)); + script_patch_16(hostdata->script, + CommandCount, slot->cmnd->cmd_len); + script_patch_32_abs(hostdata->script, SGScriptStartAddress, + virt_to_bus(&slot->SG[0].ins)); + + /* Note: setting SXFER only works if we're + * still in the MESSAGE phase, so it is vital + * that ACK is still asserted when we process + * the reselection message. The resume offset + * should therefore always clear ACK */ + outb(NCR_700_get_SXFER(hostdata->cmd->device), + host->base + SXFER_REG); + + } + } else if(dsps == A_RESELECTED_DURING_SELECTION) { + + /* This section is full of debugging code because I've + * never managed to reach it. I think what happens is + * that, because the 700 runs with selection + * interrupts enabled the whole time that we take a + * selection interrupt before we manage to get to the + * reselected script interrupt */ + + __u8 reselection_id = inb(host->base + SFBR_REG); + struct NCR_700_command_slot *slot; + + /* Take out our own ID */ + reselection_id &= ~(1<<host->this_id); + + printk("scsi%d: (%d:%d) RESELECTION DURING SELECTION, dsp=%p[%04x] +state=%d, count=%d\n", + host->host_no, reselection_id, lun, (void *)dsp, dsp - +hostdata->pScript, hostdata->state, hostdata->command_slot_count); + + { + /* FIXME: DEBUGGING CODE */ + __u32 SG = +(__u32)bus_to_virt(hostdata->script[A_SGScriptStartAddress_used[0]]); + int i; + + for(i=0; i< NCR_700_COMMAND_SLOTS_PER_HOST; i++) { + if(SG >= (__u32)(&hostdata->slots[i].SG[0]) + && SG <= +(__u32)(&hostdata->slots[i].SG[NCR_700_SG_SEGMENTS])) + break; + } + printk("IDENTIFIED SG segment as being %p in slot %p, cmd %p, +slot->resume_offset=%p\n", (void *)SG, &hostdata->slots[i], hostdata->slots[i].cmnd, +(void *)hostdata->slots[i].resume_offset); + SCp = hostdata->slots[i].cmnd; + } + + if(SCp != NULL) { + slot = (struct NCR_700_command_slot *)SCp->host_scribble; + /* change slot from busy to queued to redo command */ + slot->state = NCR_700_SLOT_QUEUED; + } + hostdata->cmd = NULL; + + if(reselection_id == 0) { + if(hostdata->reselection_id == 0xff) { + printk(KERN_ERR "scsi%d: Invalid reselection during +selection!!\n", host->host_no); + return 0; + } else { + printk(KERN_ERR "scsi%d: script reselected and we took +a selection interrupt\n", + host->host_no); + reselection_id = hostdata->reselection_id; + } + } else { + + /* convert to real ID */ + reselection_id = bitmap_to_number(reselection_id); + } + hostdata->reselection_id = reselection_id; + hostdata->msgin[1] = 0; + if(hostdata->tag_negotiated & (1<<reselection_id)) { + resume_offset = hostdata->pScript + Ent_GetReselectionWithTag; + } else { + resume_offset = hostdata->pScript + Ent_GetReselectionData; + } + } else if(dsps == A_COMPLETED_SELECTION_AS_TARGET) { + /* we've just disconnected from the bus, do nothing since + * a return here will re-run the queued command slot + * that may have been interrupted by the initial selection */ + DEBUG((" SELECTION COMPLETED\n")); + } else if((dsps & 0xfffff0f0) == A_MSG_IN) { + resume_offset = process_message(host, hostdata, SCp, + dsp, dsps); + } else if((dsps & 0xfffff000) == 0) { + __u8 i = (dsps & 0xf0) >> 4, j = (dsps & 0xf00) >> 8; + printk(KERN_ERR "scsi%d: (%d:%d), unhandled script condition %s %s at +%04x\n", + host->host_no, pun, lun, NCR_700_condition[i], + NCR_700_phase[j], dsp - hostdata->pScript); + if(SCp != NULL) { + print_command(SCp->cmnd); + + if(SCp->use_sg) { + for(i = 0; i < SCp->use_sg + 1; i++) { + printk(" SG[%d].length = %d, move_insn=%08x, +addr %08x\n", i, ((struct scatterlist *)SCp->buffer)[i].length, ((struct +NCR_700_command_slot *)SCp->host_scribble)->SG[i].ins, ((struct NCR_700_command_slot +*)SCp->host_scribble)->SG[i].pAddr); + } + } + } + NCR_700_internal_bus_reset(host); + } else if((dsps & 0xfffff000) == A_DEBUG_INTERRUPT) { + printk("scsi%d (%d:%d) DEBUG INTERRUPT %d AT %p[%04x], continuing\n", + host->host_no, pun, lun, dsps & 0xfff, (void *)dsp, dsp - +hostdata->pScript); + resume_offset = dsp; + } else { + printk(KERN_ERR "scsi%d: (%d:%d), unidentified script interrupt 0x%x +at %04x\n", + host->host_no, pun, lun, dsps, dsp - hostdata->pScript); + NCR_700_internal_bus_reset(host); + } + return resume_offset; +} + +/* We run the 53c700 with selection interrupts always enabled. This + * means that the chip may be selected as soon as the bus frees. On a + * busy bus, this can be before the scripts engine finishes its + * processing. Therefore, part of the selection processing has to be + * to find out what the scripts engine is doing and complete the + * function if necessary (i.e. process the pending disconnect or save + * the interrupted initial selection */ +STATIC inline __u32 +process_selection(struct Scsi_Host *host, __u32 dsp) +{ + __u8 id; + int count = 0; + __u32 resume_offset = 0; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + Scsi_Cmnd *SCp = hostdata->cmd; + __u8 sbcl; + + for(count = 0; count < 5; count++) { + id = inb(host->base + SFBR_REG); + + /* Take out our own ID */ + id &= ~(1<<host->this_id); + if(id != 0) + break; + udelay(5); + } + sbcl = inb(host->base + SBCL_REG); + if((sbcl & SBCL_IO) == 0) { + /* mark as having been selected rather than reselected */ + id = 0xff; + } else { + /* convert to real ID */ + hostdata->reselection_id = id = bitmap_to_number(id); + DEBUG(("scsi%d: Reselected by %d\n", + host->host_no, id)); + } + if(hostdata->state == NCR_700_HOST_BUSY && SCp != NULL) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + DEBUG((" ID %d WARNING: RESELECTION OF BUSY HOST, saving cmd %p, slot +%p, addr %p [%04x], resume %p!\n", id, hostdata->cmd, slot, dsp, dsp - +hostdata->pScript, resume_offset)); + + switch(dsp - hostdata->pScript) { + case Ent_Disconnect1: + case Ent_Disconnect2: + save_for_reselection(hostdata, SCp, Ent_Disconnect2 + +hostdata->pScript); + break; + case Ent_Disconnect3: + case Ent_Disconnect4: + save_for_reselection(hostdata, SCp, Ent_Disconnect4 + +hostdata->pScript); + break; + case Ent_Disconnect5: + case Ent_Disconnect6: + save_for_reselection(hostdata, SCp, Ent_Disconnect6 + +hostdata->pScript); + break; + case Ent_Disconnect7: + case Ent_Disconnect8: + save_for_reselection(hostdata, SCp, Ent_Disconnect8 + +hostdata->pScript); + break; + case Ent_Finish1: + case Ent_Finish2: + process_script_interrupt(A_GOOD_STATUS_AFTER_STATUS, dsp, SCp, +host, hostdata); + break; + + default: + slot->state = NCR_700_SLOT_QUEUED; + break; + } + } + hostdata->state = NCR_700_HOST_BUSY; + hostdata->cmd = NULL; + hostdata->msgin[1] = 0; + + if(id == 0xff) { + /* Selected as target, Ignore */ + resume_offset = hostdata->pScript + Ent_SelectedAsTarget; + } else if(hostdata->tag_negotiated & (1<<id)) { + resume_offset = hostdata->pScript + Ent_GetReselectionWithTag; + } else { + resume_offset = hostdata->pScript + Ent_GetReselectionData; + } + return resume_offset; +} + + +STATIC int +NCR_700_start_command(Scsi_Cmnd *SCp) +{ + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)SCp->host->hostdata[0]; + unsigned long flags; + __u16 count = 1; /* for IDENTIFY message */ + + save_flags(flags); + cli(); + if(hostdata->state != NCR_700_HOST_FREE) { + /* keep this inside the lock to close the race window where + * the running command finishes on another CPU while we don't + * change the state to queued on this one */ + slot->state = NCR_700_SLOT_QUEUED; + restore_flags(flags); + + DEBUG(("scsi%d: host busy, queueing command %p, slot %p\n", + SCp->host->host_no, slot->cmnd, slot)); + return 0; + } + hostdata->state = NCR_700_HOST_BUSY; + hostdata->cmd = SCp; + slot->state = NCR_700_SLOT_BUSY; + /* keep interrupts disabled until we have the command correctly + * set up so we cannot take a selection interrupt */ + + hostdata->msgout[0] = NCR_700_identify(SCp->cmnd[0] != REQUEST_SENSE, + SCp->lun); + /* for INQUIRY or REQUEST_SENSE commands, we cannot be sure + * if the negotiated transfer parameters still hold, so + * always renegotiate them */ + if(SCp->cmnd[0] == INQUIRY || SCp->cmnd[0] == REQUEST_SENSE) { + NCR_700_clear_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); + } + + /* REQUEST_SENSE is asking for contingent I_T_L status. If a + * contingent allegiance condition exists, the device will + * refuse all tags, so send the request sense as untagged */ + if((hostdata->tag_negotiated & (1<<SCp->target)) + && (slot->tag != NCR_700_NO_TAG && SCp->cmnd[0] != REQUEST_SENSE)) { + hostdata->msgout[count++] = A_SIMPLE_TAG_MSG; + hostdata->msgout[count++] = slot->tag; + } + + if(hostdata->fast && + NCR_700_is_flag_clear(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC)) { + memcpy(&hostdata->msgout[count], NCR_700_SDTR_msg, + sizeof(NCR_700_SDTR_msg)); + count += sizeof(NCR_700_SDTR_msg); + NCR_700_set_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + } + + script_patch_16(hostdata->script, MessageCount, count); + + + script_patch_ID(hostdata->script, + Device_ID, 1<<SCp->target); + + script_patch_32_abs(hostdata->script, CommandAddress, + virt_to_bus(SCp->cmnd)); + script_patch_16(hostdata->script, CommandCount, SCp->cmd_len); + /* finally plumb the beginning of the SG list into the script + * */ + script_patch_32_abs(hostdata->script, SGScriptStartAddress, + virt_to_bus(&slot->SG[0].ins)); + outb(CLR_FIFO, SCp->host->base + DFIFO_REG); + + /* set the synchronous period/offset */ + if(slot->resume_offset == 0) + slot->resume_offset = hostdata->pScript; + outb(NCR_700_get_SXFER(SCp->device), + SCp->host->base + SXFER_REG); + /* allow interrupts here so that if we're selected we can take + * a selection interrupt. The script start may not be + * effective in this case, but the selection interrupt will + * save our command in that case */ + outl(slot->temp, SCp->host->base + TEMP_REG); + outl(slot->resume_offset, SCp->host->base + DSP_REG); + restore_flags(flags); + + return 1; +} + +void +NCR_700_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct Scsi_Host *host = (struct Scsi_Host *)dev_id; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + __u8 istat; + __u32 resume_offset = 0; + __u8 pun = 0xff, lun = 0xff; + unsigned long flags; + + /* Unfortunately, we have to take the io_request_lock here + * rather than the host lock hostdata->lock because we're + * looking to exclude queuecommand from messing with the + * registers while we're processing the interrupt. Since + * queuecommand is called holding io_request_lock, and we have + * to take io_request_lock before we call the command + * scsi_done, we would get a deadlock if we took + * hostdata->lock here and in queuecommand (because the order + * of locking in queuecommand: 1) io_request_lock then 2) + * hostdata->lock would be the reverse of taking it in this + * routine */ + spin_lock_irqsave(&io_request_lock, flags); + if((istat = inb(host->base + ISTAT_REG)) + & (SCSI_INT_PENDING | DMA_INT_PENDING)) { + __u32 dsps; + __u8 sstat0 = 0, dstat = 0; + __u32 dsp; + Scsi_Cmnd *SCp = hostdata->cmd; + enum NCR_700_Host_State state; + + state = hostdata->state; + SCp = hostdata->cmd; + + if(istat & SCSI_INT_PENDING) { + udelay(10); + + sstat0 = inb(host->base + SSTAT0_REG); + } + + if(istat & DMA_INT_PENDING) { + udelay(10); + + dstat = inb(host->base + DSTAT_REG); + } + + dsps = inl(host->base + DSPS_REG); + dsp = inl(host->base + DSP_REG); + + DEBUG(("scsi%d: istat %02x sstat0 %02x dstat %02x dsp %04x[%08lx] dsps +0x%x\n", + host->host_no, istat, sstat0, dstat, + (dsp - (__u32)virt_to_bus(hostdata->script))/4, + dsp, dsps)); + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + + if(sstat0 & SCSI_RESET_DETECTED) { + Scsi_Device *SDp; + int i; + + hostdata->state = NCR_700_HOST_BUSY; + + printk(KERN_ERR "scsi%d: Bus Reset detected, executing command +%p, slot %p, dsp %p[%04x]\n", + host->host_no, SCp, SCp == NULL ? NULL : +SCp->host_scribble, (void *)dsp, dsp - hostdata->pScript); + + /* clear all the negotiated parameters */ + for(SDp = host->host_queue; SDp != NULL; SDp = SDp->next) + SDp->hostdata = 0; + + /* clear all the slots and their pending commands */ + for(i = 0; i < NCR_700_COMMAND_SLOTS_PER_HOST; i++) { + Scsi_Cmnd *SCp; + struct NCR_700_command_slot *slot = + &hostdata->slots[i]; + + if(slot->state == NCR_700_SLOT_FREE) + continue; + + SCp = slot->cmnd; + printk(KERN_ERR " failing command because of reset, +slot %p, cmnd %p\n", + slot, SCp); + free_slot(slot, hostdata); + SCp->host_scribble = NULL; + NCR_700_set_depth(SCp->device, 0); + /* NOTE: deadlock potential here: we + * rely on mid-layer guarantees that + * scsi_done won't try to issue the + * command again otherwise we'll + * deadlock on the + * hostdata->state_lock */ + SCp->result = DID_RESET << 16; + SCp->scsi_done(SCp); + } + mdelay(25); + NCR_700_chip_setup(host); + + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; + goto out_unlock; + } else if(sstat0 & SELECTION_TIMEOUT) { + DEBUG(("scsi%d: (%d:%d) selection timeout\n", + host->host_no, pun, lun)); + NCR_700_scsi_done(hostdata, SCp, DID_NO_CONNECT<<16); + } else if(sstat0 & PHASE_MISMATCH) { + struct NCR_700_command_slot *slot = (SCp == NULL) ? NULL : + (struct NCR_700_command_slot *)SCp->host_scribble; + + if(dsp == Ent_SendMessage + 8 + hostdata->pScript) { + /* It wants to reply to some part of + * our message */ + __u32 temp = inl(host->base + TEMP_REG); + + int count = (hostdata->script[Ent_SendMessage/4] & +0xffffff) - ((inl(host->base + DBC_REG) & 0xffffff) + NCR_700_data_residual(host)); + printk("scsi%d (%d:%d) PHASE MISMATCH IN SEND MESSAGE +%d remain, return %p[%04x], phase %s\n", host->host_no, pun, lun, count, (void +*)temp, temp - hostdata->pScript, sbcl_to_string(inb(host->base + SBCL_REG))); + resume_offset = hostdata->pScript + +Ent_SendMessagePhaseMismatch; + } else if(dsp >= virt_to_bus(&slot->SG[0].ins) && + dsp <= +virt_to_bus(&slot->SG[NCR_700_SG_SEGMENTS].ins)) { + int data_transfer = inl(host->base + DBC_REG) & +0xffffff; + int SGcount = (dsp - +virt_to_bus(&slot->SG[0].ins))/sizeof(struct NCR_700_SG_List); + int residual = NCR_700_data_residual(host); + int i; +#ifdef NCR_700_DEBUG + printk("scsi%d: (%d:%d) Expected phase mismatch in +slot->SG[%d], transferred 0x%x\n", + host->host_no, pun, lun, + SGcount, data_transfer); + print_command(SCp->cmnd); + if(residual) { + printk("scsi%d: (%d:%d) Expected phase +mismatch in slot->SG[%d], transferred 0x%x, residual %d\n", + host->host_no, pun, lun, + SGcount, data_transfer, residual); + } +#endif + data_transfer += residual; + + if(data_transfer != 0) { + int count; + SGcount--; + + count = (slot->SG[SGcount].ins & 0x00ffffff); + DEBUG(("DATA TRANSFER MISMATCH, count = %d, +transferred %d\n", count, count-data_transfer)); + slot->SG[SGcount].ins &= 0xff000000; + slot->SG[SGcount].ins |= data_transfer; + slot->SG[SGcount].pAddr += (count - +data_transfer); + } + /* set the executed moves to nops */ + for(i=0; i<SGcount; i++) { + slot->SG[i].ins = SCRIPT_NOP; + slot->SG[i].pAddr = 0; + } + /* and pretend we disconnected after + * the command phase */ + resume_offset = hostdata->pScript + +Ent_MsgInDuringData; + } else { + __u8 sbcl = inb(host->base + SBCL_REG); + printk(KERN_ERR "scsi%d: (%d:%d) phase mismatch at +%04x, phase %s\n", + host->host_no, pun, lun, dsp - +hostdata->pScript, sbcl_to_string(sbcl)); + NCR_700_internal_bus_reset(host); + } + + } else if(sstat0 & SCSI_GROSS_ERROR) { + printk(KERN_ERR "scsi%d: (%d:%d) GROSS ERROR\n", + host->host_no, pun, lun); + NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16); + } else if(dstat & SCRIPT_INT_RECEIVED) { + DEBUG(("scsi%d: (%d:%d) ====>SCRIPT INTERRUPT<====\n", + host->host_no, pun, lun)); + resume_offset = process_script_interrupt(dsps, dsp, SCp, host, +hostdata); + } else if(dstat & (ILGL_INST_DETECTED)) { + printk(KERN_ERR "scsi%d: (%d:%d) Illegal Instruction detected +at 0x%p[0x%x]!!!\n" + " Please email [EMAIL PROTECTED] with the details\n", + host->host_no, pun, lun, + (void *)dsp, dsp - hostdata->pScript); + NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16); + } else if(dstat & (WATCH_DOG_INTERRUPT|ABORTED)) { + printk(KERN_ERR "scsi%d: (%d:%d) serious DMA problem, +dstat=%02x\n", + host->host_no, pun, lun, dstat); + NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16); + } + + + /* NOTE: selection interrupt processing MUST occur + * after script interrupt processing to correctly cope + * with the case where we process a disconnect and + * then get reselected before we process the + * disconnection */ + if(sstat0 & SELECTED) { + /* FIXME: It currently takes at least FOUR + * interrupts to complete a command that + * disconnects: one for the disconnect, one + * for the reselection, one to get the + * reselection data and one to complete the + * command. If we guess the reselected + * command here and prepare it, we only need + * to get a reselection data interrupt if we + * guessed wrongly. Since the interrupt + * overhead is much greater than the command + * setup, this would be an efficient + * optimisation particularly as we probably + * only have one outstanding command on a + * target most of the time */ + + resume_offset = process_selection(host, dsp); + + } + + } + + if(resume_offset) { + if(hostdata->state != NCR_700_HOST_BUSY) { + printk(KERN_ERR "scsi%d: Driver error: resume at %p [%04x] +with non busy host!\n", + host->host_no, (void *)resume_offset, resume_offset - +hostdata->pScript); + hostdata->state = NCR_700_HOST_BUSY; + } + + DEBUG(("Attempting to resume at %x\n", resume_offset)); + outb(CLR_FIFO, host->base + DFIFO_REG); + outl(resume_offset, host->base + DSP_REG); + } + /* There is probably a technical no-no about this: If we're a + * shared interrupt and we got this interrupt because the + * other device needs servicing not us, we're still going to + * check our queued commands here---of course, there shouldn't + * be any outstanding.... */ + if(hostdata->state == NCR_700_HOST_FREE) { + int i; + + for(i = 0; i < NCR_700_COMMAND_SLOTS_PER_HOST; i++) { + /* fairness: always run the queue from the last + * position we left off */ + int j = (i + hostdata->saved_slot_position) + % NCR_700_COMMAND_SLOTS_PER_HOST; + + if(hostdata->slots[j].state != NCR_700_SLOT_QUEUED) + continue; + if(NCR_700_start_command(hostdata->slots[j].cmnd)) { + DEBUG(("scsi%d: Issuing saved command slot %p, cmd +%p\t\n", + host->host_no, &hostdata->slots[j], + hostdata->slots[j].cmnd)); + hostdata->saved_slot_position = j + 1; + } + + break; + } + } + out_unlock: + spin_unlock_irqrestore(&io_request_lock, flags); +} + +/* FIXME: Need to put some proc information in and plumb it + * into the scsi proc system */ +STATIC int +NCR_700_proc_directory_info(char *proc_buf, char **startp, + off_t offset, int bytes_available, + int host_no, int write) +{ + static char buf[4096]; /* 1 page should be sufficient */ + int len = 0; + struct Scsi_Host *host = scsi_hostlist; + struct NCR_700_Host_Parameters *hostdata; + Scsi_Device *SDp; + + while(host != NULL && host->host_no != host_no) + host = host->next; + + if(host == NULL) + return 0; + + if(write) { + /* FIXME: Clear internal statistics here */ + return 0; + } + hostdata = (struct NCR_700_Host_Parameters *)host->hostdata[0]; + len += sprintf(&buf[len], "Total commands outstanding: %d\n", +hostdata->command_slot_count); + len += sprintf(&buf[len],"\ +Target Depth Active Next Tag\n\ +====== ===== ====== ========\n"); + for(SDp = host->host_queue; SDp != NULL; SDp = SDp->next) { + len += sprintf(&buf[len],"%2d:%2d %4d %4d %4d\n", SDp->id, +SDp->lun, SDp->queue_depth, NCR_700_get_depth(SDp), SDp->current_tag); + } + if((len -= offset) <= 0) + return 0; + if(len > bytes_available) + len = bytes_available; + memcpy(proc_buf, buf + offset, len); + return len; +} + +STATIC int +NCR_700_queuecommand(Scsi_Cmnd *SCp, void (*done)(Scsi_Cmnd *)) +{ + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)SCp->host->hostdata[0]; + __u32 move_ins; + struct NCR_700_command_slot *slot; + int hash; + + if(hostdata->command_slot_count >= NCR_700_COMMAND_SLOTS_PER_HOST) { + /* We're over our allocation, this should never happen + * since we report the max allocation to the mid layer */ + printk(KERN_WARNING "scsi%d: Command depth has gone over queue +depth\n", SCp->host->host_no); + return 1; + } + if(NCR_700_get_depth(SCp->device) != 0 && !(hostdata->tag_negotiated & +(1<<SCp->target))) { + DEBUG((KERN_ERR "scsi%d (%d:%d) has non zero depth %d\n", + SCp->host->host_no, SCp->target, SCp->lun, + NCR_700_get_depth(SCp->device))); + return 1; + } + if(NCR_700_get_depth(SCp->device) >= NCR_700_MAX_TAGS) { + DEBUG((KERN_ERR "scsi%d (%d:%d) has max tag depth %d\n", + SCp->host->host_no, SCp->target, SCp->lun, + NCR_700_get_depth(SCp->device))); + return 1; + } + NCR_700_set_depth(SCp->device, NCR_700_get_depth(SCp->device) + 1); + + /* begin the command here */ + /* no need to check for NULL, test for command_slot_cound above + * ensures a slot is free */ + slot = find_empty_slot(hostdata); + + slot->cmnd = SCp; + + SCp->scsi_done = done; + SCp->host_scribble = (unsigned char *)slot; + SCp->SCp.ptr = NULL; + SCp->SCp.buffer = NULL; + +#ifdef NCR_700_DEBUG + printk("53c700: scsi%d, command ", SCp->host->host_no); + print_command(SCp->cmnd); +#endif + + if(hostdata->tag_negotiated &(1<<SCp->target)) { + + struct NCR_700_command_slot *old = + find_ITL_Nexus(hostdata, SCp->target, SCp->lun); +#ifdef NCR_700_TAG_DEBUG + struct NCR_700_command_slot *found; +#endif + + if(old != NULL && old->tag == SCp->device->current_tag) { + printk(KERN_WARNING "scsi%d (%d:%d) Tag clock back to current, +queueing\n", SCp->host->host_no, SCp->target, SCp->lun); + return 1; + } + slot->tag = SCp->device->current_tag++; +#ifdef NCR_700_TAG_DEBUG + while((found = find_ITLQ_Nexus(hostdata, SCp->target, SCp->lun, +slot->tag)) != NULL) { + printk("\n\n**ERROR** already using tag %d, but oldest is +%d\n", slot->tag, (old == NULL) ? -1 : old->tag); + printk(" FOUND = %p, tag = %d, pun = %d, lun = %d\n", + found, found->tag, found->cmnd->target, +found->cmnd->lun); + slot->tag = SCp->device->current_tag++; + printk(" Tag list is: "); + while(old != NULL) { + if(old->cmnd->target == SCp->target && + old->cmnd->lun == SCp->lun) + printk("%d ", old->tag); + old = old->ITL_back; + } + printk("\n\n"); + } +#endif + hash = hash_ITLQ(SCp->target, SCp->lun, slot->tag); + /* link into the ITLQ hash queues */ + slot->ITLQ_forw = hostdata->ITLQ_Hash_forw[hash]; + hostdata->ITLQ_Hash_forw[hash] = slot; +#ifdef NCR_700_TAG_DEBUG + if(slot->ITLQ_forw != NULL && slot->ITLQ_forw->ITLQ_back != NULL) { + printk(KERN_ERR "scsi%d (%d:%d) ITLQ_back is not NULL!!!!\n", +SCp->host->host_no, SCp->target, SCp->lun); + } +#endif + if(slot->ITLQ_forw != NULL) + slot->ITLQ_forw->ITLQ_back = slot; + else + hostdata->ITLQ_Hash_back[hash] = slot; + slot->ITLQ_back = NULL; + } else { + slot->tag = NCR_700_NO_TAG; + } + /* link into the ITL hash queues */ + hash = hash_ITL(SCp->target, SCp->lun); + slot->ITL_forw = hostdata->ITL_Hash_forw[hash]; + hostdata->ITL_Hash_forw[hash] = slot; +#ifdef NCR_700_TAG_DEBUG + if(slot->ITL_forw != NULL && slot->ITL_forw->ITL_back != NULL) { + printk(KERN_ERR "scsi%d (%d:%d) ITL_back is not NULL!!!!\n", + SCp->host->host_no, SCp->target, SCp->lun); + } +#endif + if(slot->ITL_forw != NULL) + slot->ITL_forw->ITL_back = slot; + else + hostdata->ITL_Hash_back[hash] = slot; + slot->ITL_back = NULL; + + + /* This is f****g ridiculous; every low level HBA driver has + * to determine the direction of the commands, why isn't this + * done inside the scsi_lib !!??? */ + switch (SCp->cmnd[0]) { + case REQUEST_SENSE: + /* clear the internal sense magic */ + SCp->cmnd[6] = 0; + /* fall through */ + case INQUIRY: + case MODE_SENSE: + case READ_6: + case READ_10: + case READ_12: + case READ_CAPACITY: + case READ_BLOCK_LIMITS: + case READ_TOC: + move_ins = SCRIPT_MOVE_DATA_IN; + break; + case MODE_SELECT: + case WRITE_6: + case WRITE_10: + case WRITE_12: + move_ins = SCRIPT_MOVE_DATA_OUT; + break; + case TEST_UNIT_READY: + case ALLOW_MEDIUM_REMOVAL: + case START_STOP: + move_ins = 0; + break; + default: + /* OK, get it from the command */ + switch(SCp->sc_data_direction) { + case SCSI_DATA_UNKNOWN: + default: + printk(KERN_ERR "53c700: Unknown command for data direction "); + print_command(SCp->cmnd); + + move_ins = 0; + break; + case SCSI_DATA_NONE: + move_ins = 0; + break; + case SCSI_DATA_READ: + move_ins = SCRIPT_MOVE_DATA_IN; + break; + case SCSI_DATA_WRITE: + move_ins = SCRIPT_MOVE_DATA_OUT; + break; + } + } + + /* now build the scatter gather list */ + if(move_ins != 0) { + int i; + + for(i = 0; i < (SCp->use_sg ? SCp->use_sg : 1); i++) { + void *vPtr; + __u32 count; + + if(SCp->use_sg) { + vPtr = (((struct scatterlist +*)SCp->buffer)[i].address); + count = ((struct scatterlist *)SCp->buffer)[i].length; + } else { + vPtr = SCp->request_buffer; + count = SCp->request_bufflen; + } + slot->SG[i].ins = move_ins | count; + DEBUG((" scatter block %d: move %d[%08lx] from 0x%lx\n", + i, count, slot->SG[i].move_ins, + virt_to_bus(vPtr))); + slot->SG[i].pAddr = virt_to_bus(vPtr); + } + slot->SG[i].ins = SCRIPT_RETURN; + slot->SG[i].pAddr = 0; + DEBUG((" SETTING %08x to %x\n", + virt_to_bus(&slot->SG[i].ins), + slot->SG[i].ins)); + } + slot->resume_offset = 0; + NCR_700_start_command(SCp); + return 0; +} + +STATIC int +NCR_700_abort(Scsi_Cmnd * SCp) +{ + struct NCR_700_command_slot *slot; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)SCp->host->hostdata[0]; + + printk("scsi%d (%d:%d) New error handler wants to abort command\n\t", + SCp->host->host_no, SCp->target, SCp->lun); + print_command(SCp->cmnd); + + slot = find_ITL_Nexus(hostdata, SCp->target, SCp->lun); + while(slot != NULL && slot->cmnd != SCp) + slot = slot->ITL_back; + + if(slot == NULL) + /* no outstanding command to abort */ + return SUCCESS; + if(SCp->cmnd[0] == TEST_UNIT_READY) { + /* FIXME: This is because of a problem in the new + * error handler. When it is in error recovery, it + * will send a TUR to a device it thinks may still be + * showing a problem. If the TUR isn't responded to, + * it will abort it and mark the device off line. + * Unfortunately, it does no other error recovery, so + * this would leave us with an outstanding command + * occupying a slot. Rather than allow this to + * happen, we issue a bus reset to force all + * outstanding commands to terminate here. */ + NCR_700_internal_bus_reset(SCp->host); + /* still drop through and return failed */ + } + return FAILED; + +} + +STATIC int +NCR_700_bus_reset(Scsi_Cmnd * SCp) +{ + printk("scsi%d (%d:%d) New error handler wants BUS reset, cmd %p\n\t", + SCp->host->host_no, SCp->target, SCp->lun, SCp); + print_command(SCp->cmnd); + NCR_700_internal_bus_reset(SCp->host); + return SUCCESS; +} + +STATIC int +NCR_700_dev_reset(Scsi_Cmnd * SCp) +{ + printk("scsi%d (%d:%d) New error handler wants device reset\n\t", + SCp->host->host_no, SCp->target, SCp->lun); + print_command(SCp->cmnd); + + return FAILED; +} + +STATIC int +NCR_700_host_reset(Scsi_Cmnd * SCp) +{ + printk("scsi%d (%d:%d) New error handler wants HOST reset\n\t", + SCp->host->host_no, SCp->target, SCp->lun); + print_command(SCp->cmnd); + + NCR_700_internal_bus_reset(SCp->host); + NCR_700_chip_reset(SCp->host); + return SUCCESS; +} + +EXPORT_SYMBOL(NCR_700_detect); +EXPORT_SYMBOL(NCR_700_release); +EXPORT_SYMBOL(NCR_700_intr); Index: linux/2.4/drivers/scsi/53c700.h diff -u /dev/null linux/2.4/drivers/scsi/53c700.h:1.1.4.1 --- /dev/null Sat May 12 12:05:15 2001 +++ linux/2.4/drivers/scsi/53c700.h Sat May 12 10:39:20 2001 @@ -0,0 +1,391 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* Driver for 53c700 and 53c700-66 chips from NCR and Symbios + * + * Copyright (C) 2001 by [EMAIL PROTECTED] + */ + +#ifndef _53C700_H +#define _53C700_H + +/* Turn on for general debugging---too verbose for normal use */ +#undef NCR_700_DEBUG +/* Debug the tag queues, checking hash queue allocation and deallocation + * and search for duplicate tags */ +#undef NCR_700_TAG_DEBUG + +#ifdef NCR_700_DEBUG +#define DEBUG(x) printk x +#else +#define DEBUG(x) +#endif + +/* The number of available command slots */ +#define NCR_700_COMMAND_SLOTS_PER_HOST 64 +/* The maximum number of Scatter Gathers we allow */ +#define NCR_700_SG_SEGMENTS 32 +/* The maximum number of luns (make this of the form 2^n) */ +#define NCR_700_MAX_LUNS 32 +#define NCR_700_LUN_MASK (NCR_700_MAX_LUNS - 1) +/* Alter this with care: too many tags won't give the elevator a chance to + * work; too few will cause the device to operate less efficiently */ +#define NCR_700_MAX_TAGS 16 +/* magic byte identifying an internally generated REQUEST_SENSE command */ +#define NCR_700_INTERNAL_SENSE_MAGIC 0x42 + + +struct NCR_700_Host_Parameters; + +/* These are the externally used routines */ +struct Scsi_Host *NCR_700_detect(Scsi_Host_Template *, struct NCR_700_Host_Parameters +*); +int NCR_700_release(struct Scsi_Host *host); +void NCR_700_intr(int, void *, struct pt_regs *); + + +enum NCR_700_Host_State { + NCR_700_HOST_BUSY, + NCR_700_HOST_FREE, +}; + +struct NCR_700_SG_List { + /* The following is a script fragment to move the buffer onto the + * bus and then link the next fragment or return */ + #define SCRIPT_MOVE_DATA_IN 0x09000000 + #define SCRIPT_MOVE_DATA_OUT 0x08000000 + __u32 ins; + __u32 pAddr; + #define SCRIPT_NOP 0x80000000 + #define SCRIPT_RETURN 0x90080000 +}; + +/* We use device->hostdata to store negotiated parameters. This is + * supposed to be a pointer to a device private area, but we cannot + * really use it as such since it will never be freed, so just use the + * 32 bits to cram the information. The SYNC negotiation sequence looks + * like: + * + * If DEV_NEGOTIATED_SYNC not set, tack and SDTR message on to the + * initial identify for the device and set DEV_BEGIN_SYNC_NEGOTATION + * If we get an SDTR reply, work out the SXFER parameters, squirrel + * them away here, clear DEV_BEGIN_SYNC_NEGOTIATION and set + * DEV_NEGOTIATED_SYNC. If we get a REJECT msg, squirrel + * + * + * 0:7 SXFER_REG negotiated value for this device + * 8:15 Current queue depth + * 16 negotiated SYNC flag + * 17 begin SYNC negotiation flag + * 18 device supports tag queueing */ +#define NCR_700_DEV_NEGOTIATED_SYNC (1<<16) +#define NCR_700_DEV_BEGIN_SYNC_NEGOTIATION (1<<17) +#define NCR_700_DEV_USES_TAG_QUEUEING (1<<18) + +static inline void +NCR_700_set_SXFER(Scsi_Device *SDp, __u8 sxfer) +{ + ((__u32)SDp->hostdata) &= 0xffffff00; + ((__u32)SDp->hostdata) |= sxfer & 0xff; +} +static inline __u8 NCR_700_get_SXFER(Scsi_Device *SDp) +{ + return (((__u32)SDp->hostdata) & 0xff); +} +static inline void +NCR_700_set_depth(Scsi_Device *SDp, __u8 depth) +{ + ((__u32)SDp->hostdata) &= 0xffff00ff; + ((__u32)SDp->hostdata) |= (0xff00 & (depth << 8)); +} +static inline __u8 +NCR_700_get_depth(Scsi_Device *SDp) +{ + return ((((__u32)SDp->hostdata) & 0xff00)>>8); +} +static inline int +NCR_700_is_flag_set(Scsi_Device *SDp, __u32 flag) +{ + return (((__u32)SDp->hostdata) & flag) == flag; +} +static inline int +NCR_700_is_flag_clear(Scsi_Device *SDp, __u32 flag) +{ + return (((__u32)SDp->hostdata) & flag) == 0; +} +static inline void +NCR_700_set_flag(Scsi_Device *SDp, __u32 flag) +{ + ((__u32)SDp->hostdata) |= (flag & 0xffff0000); +} +static inline void +NCR_700_clear_flag(Scsi_Device *SDp, __u32 flag) +{ + ((__u32)SDp->hostdata) &= ~(flag & 0xffff0000); +} + +/* These represent the Nexus hashing functions. A Nexus in SCSI terms + * just means the identification of an outstanding command, by ITL + * (Initiator Target Lun) or ITLQ (Initiator Target Lun Tag). I'm not + * very keen on XOR based hashes, so these are based on number theory + * instead. All you need to do is to fix your hash bucket size and + * then choose reasonable strides which are coprime with the chosen + * bucket size + * + * Note: this mathematical hash can be made very efficient, if the + * compiler is good at optimising: Choose the number of buckets to be + * 2^n and the modulo becomes a logical and with (2^n-1). + * Additionally, if you chose the coprimes of the form 2^n-2^n the + * multiplication can be done by a shift and an addition. */ +#define MAX_ITL_HASH_BUCKETS 16 +#define ITL_HASH_PRIME 7 + +#define MAX_ITLQ_HASH_BUCKETS 64 +#define ITLQ_PUN_PRIME 7 +#define ITLQ_LUN_PRIME 3 + +static inline int +hash_ITL(__u8 pun, __u8 lun) +{ + return (pun*ITL_HASH_PRIME + lun) % MAX_ITL_HASH_BUCKETS; +} + +static inline int +hash_ITLQ(__u8 pun, __u8 lun, __u8 tag) +{ + return (pun*ITLQ_PUN_PRIME + lun*ITLQ_LUN_PRIME + tag) % MAX_ITLQ_HASH_BUCKETS; +} + +struct NCR_700_command_slot { + #define NCR_700_SLOT_MASK 0xFC + #define NCR_700_SLOT_MAGIC 0xb8 + #define NCR_700_SLOT_FREE (0|NCR_700_SLOT_MAGIC) /* slot may be used */ + #define NCR_700_SLOT_BUSY (1|NCR_700_SLOT_MAGIC) /* slot has command active on +HA */ + #define NCR_700_SLOT_QUEUED (2|NCR_700_SLOT_MAGIC) /* slot has command to be +made active on HA */ + __u8 state; + #define NCR_700_NO_TAG 0xdead + __u16 tag; + struct NCR_700_SG_List SG[NCR_700_SG_SEGMENTS+1]; + __u32 resume_offset; + Scsi_Cmnd *cmnd; + __u32 temp; + /* Doubly linked ITL/ITLQ list kept in strict time order + * (latest at the back) */ + struct NCR_700_command_slot *ITL_forw; + struct NCR_700_command_slot *ITL_back; + struct NCR_700_command_slot *ITLQ_forw; + struct NCR_700_command_slot *ITLQ_back; +}; + +struct NCR_700_Host_Parameters { + /* These must be filled in by the calling driver */ + int clock; /* board clock speed in MHz */ + __u32 base; /* the base for the port (copied to host) */ + __u8 differential:1; /* if we are differential */ + + /* NOTHING BELOW HERE NEEDS ALTERING */ + __u8 fast:1; /* if we can alter the SCSI bus clock + speed (so can negiotiate sync) */ + + __u32 *script; /* pointer to script location */ + __u32 pScript; /* physical mem addr of script */ + + /* This will be the host lock. Unfortunately, we can't use it + * at the moment because of the necessity of holding the + * io_request_lock */ + spinlock_t lock; + enum NCR_700_Host_State state; /* protected by state lock */ + Scsi_Cmnd *cmd; + + __u8 msgout[8]; + __u8 msgout_size; + __u8 tag_negotiated; + __u8 status; + __u8 msgin[8]; + struct NCR_700_command_slot *slots; + int saved_slot_position; + int command_slot_count; /* protected by state lock */ + __u8 rev; + __u8 reselection_id; + /* flags for the host */ + + /* ITL list. ALL outstanding commands are hashed here in strict + * order, latest at the back */ + struct NCR_700_command_slot *ITL_Hash_forw[MAX_ITL_HASH_BUCKETS]; + struct NCR_700_command_slot *ITL_Hash_back[MAX_ITL_HASH_BUCKETS]; + + /* Only tagged outstanding commands are hashed here (also latest + * at the back) */ + struct NCR_700_command_slot *ITLQ_Hash_forw[MAX_ITLQ_HASH_BUCKETS]; + struct NCR_700_command_slot *ITLQ_Hash_back[MAX_ITLQ_HASH_BUCKETS]; + + /* Free list, singly linked by ITL_forw elements */ + struct NCR_700_command_slot *free_list; +}; + +/* + * 53C700 Register Interface - the offset from the Selected base + * I/O address */ + +#define SCNTL0_REG 0x00 +#define FULL_ARBITRATION 0xc0 +#define PARITY 0x08 +#define ENABLE_PARITY 0x04 +#define AUTO_ATN 0x02 +#define SCNTL1_REG 0x01 +#define SLOW_BUS 0x80 +#define ENABLE_SELECT 0x20 +#define ASSERT_RST 0x08 +#define ASSERT_EVEN_PARITY 0x04 +#define SDID_REG 0x02 +#define SIEN_REG 0x03 +#define PHASE_MM_INT 0x80 +#define FUNC_COMP_INT 0x40 +#define SEL_TIMEOUT_INT 0x20 +#define SELECT_INT 0x10 +#define GROSS_ERR_INT 0x08 +#define UX_DISC_INT 0x04 +#define RST_INT 0x02 +#define PAR_ERR_INT 0x01 +#define SCID_REG 0x04 +#define SXFER_REG 0x05 +#define ASYNC_OPERATION 0x00 +#define SODL_REG 0x06 +#define SOCL_REG 0x07 +#define SFBR_REG 0x08 +#define SIDL_REG 0x09 +#define SBDL_REG 0x0A +#define SBCL_REG 0x0B +#define SBCL_IO 0x01 +#define DSTAT_REG 0x0C +#define ILGL_INST_DETECTED 0x01 +#define WATCH_DOG_INTERRUPT 0x02 +#define SCRIPT_INT_RECEIVED 0x04 +#define ABORTED 0x10 +#define SSTAT0_REG 0x0D +#define PARITY_ERROR 0x01 +#define SCSI_RESET_DETECTED 0x02 +#define UNEXPECTED_DISCONNECT 0x04 +#define SCSI_GROSS_ERROR 0x08 +#define SELECTED 0x10 +#define SELECTION_TIMEOUT 0x20 +#define FUNCTION_COMPLETE 0x40 +#define PHASE_MISMATCH 0x80 +#define SSTAT1_REG 0x0E +#define SIDL_REG_FULL 0x80 +#define SODR_REG_FULL 0x40 +#define SODL_REG_FULL 0x20 +#define SSTAT2_REG 0x0F +#define CTEST0_REG 0x14 +#define CTEST1_REG 0x15 +#define CTEST2_REG 0x16 +#define CTEST3_REG 0x17 +#define CTEST4_REG 0x18 +#define DISABLE_FIFO 0x00 +#define SLBE 0x10 +#define SFWR 0x08 +#define BYTE_LANE0 0x04 +#define BYTE_LANE1 0x05 +#define BYTE_LANE2 0x06 +#define BYTE_LANE3 0x07 +#define SCSI_ZMODE 0x20 +#define ZMODE 0x40 +#define CTEST5_REG 0x19 +#define MASTER_CONTROL 0x10 +#define DMA_DIRECTION 0x08 +#define CTEST7_REG 0x1B +#define DFP 0x08 +#define EVP 0x04 +#define DIFF 0x01 +#define CTEST6_REG 0x1A +#define TEMP_REG 0x1C +#define DFIFO_REG 0x20 +#define FLUSH_DMA_FIFO 0x80 +#define CLR_FIFO 0x40 +#define ISTAT_REG 0x21 +#define ABORT_OPERATION 0x80 +#define DMA_INT_PENDING 0x01 +#define SCSI_INT_PENDING 0x02 +#define CONNECTED 0x08 +#define CTEST8_REG 0x22 +#define LAST_DIS_ENBL 0x01 +#define SHORTEN_FILTERING 0x04 +#define ENABLE_ACTIVE_NEGATION 0x10 +#define GENERATE_RECEIVE_PARITY 0x20 +#define CTEST9_REG 0x23 +#define DBC_REG 0x24 +#define DCMD_REG 0x27 +#define DNAD_REG 0x28 +#define DIEN_REG 0x39 +#define ABORT_INT 0x10 +#define INT_INST_INT 0x04 +#define WD_INT 0x02 +#define ILGL_INST_INT 0x01 +#define DCNTL_REG 0x3B +#define SOFTWARE_RESET 0x01 +#define SCRPTS_16BITS 0x20 +#define DMODE_REG 0x34 +#define BURST_LENGTH_1 0x00 +#define BURST_LENGTH_2 0x40 +#define BURST_LENGTH_4 0x80 +#define BURST_LENGTH_8 0xC0 +#define BW16 32 +#define MODE_286 16 +#define IO_XFER 8 +#define FIXED_ADDR 4 + +#define DSP_REG 0x2C +#define DSPS_REG 0x30 + +/* Parameters to begin SDTR negotiations. Empirically, I find that + * the 53c700-66 cannot handle an offset >8, so don't change this */ +#define NCR_700_MAX_OFFSET 8 +#define NCR_700_MIN_XFERP 1 +#define NCR_700_MIN_PERIOD 25 /* for SDTR message, 100ns */ + +#define script_patch_32(script, symbol, value) \ +{ \ + int i; \ + for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \ + (script)[A_##symbol##_used[i]] += (value); \ + DEBUG((" script, patching %s at %ld to 0x%lx\n", \ + #symbol, A_##symbol##_used[i], (value))); \ + } \ +} + +#define script_patch_32_abs(script, symbol, value) \ +{ \ + int i; \ + for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \ + (script)[A_##symbol##_used[i]] = (value); \ + DEBUG((" script, patching %s at %ld to 0x%lx\n", \ + #symbol, A_##symbol##_used[i], (value))); \ + } \ +} + +/* Used for patching the SCSI ID in the SELECT instruction */ +#define script_patch_ID(script, symbol, value) \ +{ \ + int i; \ + for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \ + __u32 val = (script)[A_##symbol##_used[i]]; \ + val &= 0xff00ffff; \ + val |= ((value) & 0xff) << 16; \ + (script)[A_##symbol##_used[i]] = val; \ + DEBUG((" script, patching ID field %s at %ld to 0x%lx\n", \ + #symbol, A_##symbol##_used[i], val)); \ + } \ +} + +#define script_patch_16(script, symbol, value) \ +{ \ + int i; \ + for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \ + __u32 val = (script)[A_##symbol##_used[i]]; \ + val &= 0xffff0000; \ + val |= ((value) & 0xffff); \ + (script)[A_##symbol##_used[i]] = val; \ + DEBUG((" script, patching ID field %s at %ld to 0x%lx\n", \ + #symbol, A_##symbol##_used[i], val)); \ + } \ +} + +#endif Index: linux/2.4/drivers/scsi/53c700.scr diff -u /dev/null linux/2.4/drivers/scsi/53c700.scr:1.1.4.1 --- /dev/null Sat May 12 12:05:16 2001 +++ linux/2.4/drivers/scsi/53c700.scr Sat May 12 10:39:20 2001 @@ -0,0 +1,404 @@ +; Script for the NCR (or symbios) 53c700 and 53c700-66 chip +; +; Copyright (C) 2001 [EMAIL PROTECTED] +;;----------------------------------------------------------------------------- +;; +;; 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 script is designed to be modified for the particular command in +; operation. The particular variables pertaining to the commands are: +; +ABSOLUTE Device_ID = 0 ; ID of target for command +ABSOLUTE MessageCount = 0 ; Number of bytes in message +ABSOLUTE MessageLocation = 0 ; Addr of message +ABSOLUTE CommandCount = 0 ; Number of bytes in command +ABSOLUTE CommandAddress = 0 ; Addr of Command +ABSOLUTE StatusAddress = 0 ; Addr to receive status return +ABSOLUTE ReceiveMsgAddress = 0 ; Addr to receive msg +; +; This is the magic component for handling scatter-gather. Each of the +; SG components is preceeded by a script fragment which moves the +; necessary amount of data and jumps to the next SG segment. The final +; SG segment jumps back to . However, this address is the first SG script +; segment. +; +ABSOLUTE SGScriptStartAddress = 0 + +; The following represent status interrupts we use 3 hex digits for +; this: 0xPRS where + +; P: +ABSOLUTE AFTER_SELECTION = 0x100 +ABSOLUTE BEFORE_CMD = 0x200 +ABSOLUTE AFTER_CMD = 0x300 +ABSOLUTE AFTER_STATUS = 0x400 +ABSOLUTE AFTER_DATA_IN = 0x500 +ABSOLUTE AFTER_DATA_OUT = 0x600 +ABSOLUTE DURING_DATA_IN = 0x700 + +; R: +ABSOLUTE NOT_MSG_OUT = 0x10 +ABSOLUTE UNEXPECTED_PHASE = 0x20 +ABSOLUTE NOT_MSG_IN = 0x30 +ABSOLUTE UNEXPECTED_MSG = 0x40 +ABSOLUTE MSG_IN = 0x50 +ABSOLUTE SDTR_MSG_R = 0x60 +ABSOLUTE REJECT_MSG_R = 0x70 +ABSOLUTE DISCONNECT = 0x80 +ABSOLUTE MSG_OUT = 0x90 +ABSOLUTE WDTR_MSG_R = 0xA0 + +; S: +ABSOLUTE GOOD_STATUS = 0x1 + +; Combinations, since the script assembler can't process | +ABSOLUTE NOT_MSG_OUT_AFTER_SELECTION = 0x110 +ABSOLUTE UNEXPECTED_PHASE_BEFORE_CMD = 0x220 +ABSOLUTE UNEXPECTED_PHASE_AFTER_CMD = 0x320 +ABSOLUTE NOT_MSG_IN_AFTER_STATUS = 0x430 +ABSOLUTE GOOD_STATUS_AFTER_STATUS = 0x401 +ABSOLUTE UNEXPECTED_PHASE_AFTER_DATA_IN = 0x520 +ABSOLUTE UNEXPECTED_PHASE_AFTER_DATA_OUT = 0x620 +ABSOLUTE UNEXPECTED_MSG_BEFORE_CMD = 0x240 +ABSOLUTE MSG_IN_BEFORE_CMD = 0x250 +ABSOLUTE MSG_IN_AFTER_CMD = 0x350 +ABSOLUTE SDTR_MSG_BEFORE_CMD = 0x260 +ABSOLUTE REJECT_MSG_BEFORE_CMD = 0x270 +ABSOLUTE DISCONNECT_AFTER_CMD = 0x380 +ABSOLUTE SDTR_MSG_AFTER_CMD = 0x360 +ABSOLUTE WDTR_MSG_AFTER_CMD = 0x3A0 +ABSOLUTE DISCONNECT_AFTER_DATA = 0x580 +ABSOLUTE MSG_IN_AFTER_DATA_IN = 0x550 +ABSOLUTE MSG_IN_AFTER_DATA_OUT = 0x650 +ABSOLUTE MSG_OUT_AFTER_DATA_IN = 0x590 +ABSOLUTE DATA_IN_AFTER_DATA_IN = 0x5a0 +ABSOLUTE MSG_IN_DURING_DATA_IN = 0x750 +ABSOLUTE DISCONNECT_DURING_DATA = 0x780 + +; +; Other interrupt conditions +; +ABSOLUTE RESELECTED_DURING_SELECTION = 0x1000 +ABSOLUTE COMPLETED_SELECTION_AS_TARGET = 0x1001 +ABSOLUTE RESELECTION_IDENTIFIED = 0x1003 +; +; Fatal interrupt conditions. If you add to this, also add to the +; array of corresponding messages +; +ABSOLUTE FATAL = 0x2000 +ABSOLUTE FATAL_UNEXPECTED_RESELECTION_MSG = 0x2000 +ABSOLUTE FATAL_SEND_MSG = 0x2001 +ABSOLUTE FATAL_NOT_MSG_IN_AFTER_SELECTION = 0x2002 +ABSOLUTE FATAL_ILLEGAL_MSG_LENGTH = 0x2003 + +ABSOLUTE DEBUG_INTERRUPT = 0x3000 +ABSOLUTE DEBUG_INTERRUPT1 = 0x3001 +ABSOLUTE DEBUG_INTERRUPT2 = 0x3002 +ABSOLUTE DEBUG_INTERRUPT3 = 0x3003 +ABSOLUTE DEBUG_INTERRUPT4 = 0x3004 +ABSOLUTE DEBUG_INTERRUPT5 = 0x3005 +ABSOLUTE DEBUG_INTERRUPT6 = 0x3006 + + +; +; SCSI Messages we interpret in the script +; +ABSOLUTE EXTENDED_MSG = 0x01 +ABSOLUTE SDTR_MSG = 0x01 +ABSOLUTE SAVE_DATA_PTRS_MSG = 0x02 +ABSOLUTE RESTORE_DATA_PTRS_MSG = 0x03 +ABSOLUTE WDTR_MSG = 0x03 +ABSOLUTE DISCONNECT_MSG = 0x04 +ABSOLUTE REJECT_MSG = 0x07 +ABSOLUTE PARITY_ERROR_MSG = 0x09 +ABSOLUTE SIMPLE_TAG_MSG = 0x20 +ABSOLUTE IDENTIFY_MSG = 0x80 +ABSOLUTE IDENTIFY_MSG_MASK = 0x7F +ABSOLUTE TWO_BYTE_MSG = 0x20 +ABSOLUTE TWO_BYTE_MSG_MASK = 0x0F + +; This is where the script begins + +ENTRY StartUp + +StartUp: + SELECT ATN Device_ID, Reselect + JUMP Finish, WHEN STATUS + JUMP SendIdentifyMsg, IF MSG_OUT + INT NOT_MSG_OUT_AFTER_SELECTION + +Reselect: + WAIT RESELECT SelectedAsTarget + INT RESELECTED_DURING_SELECTION, WHEN MSG_IN + INT FATAL_NOT_MSG_IN_AFTER_SELECTION + + ENTRY GetReselectionData +GetReselectionData: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + INT RESELECTION_IDENTIFIED + + ENTRY GetReselectionWithTag +GetReselectionWithTag: + MOVE 3, ReceiveMsgAddress, WHEN MSG_IN + INT RESELECTION_IDENTIFIED + + ENTRY SelectedAsTarget +SelectedAsTarget: +; Basically tell the selecting device that there's nothing here + SET TARGET + DISCONNECT + CLEAR TARGET + INT COMPLETED_SELECTION_AS_TARGET +; +; These are the messaging entries +; +; Send a message. Message count should be correctly patched + ENTRY SendMessage +SendMessage: + MOVE MessageCount, MessageLocation, WHEN MSG_OUT +ResumeSendMessage: + RETURN, WHEN NOT MSG_OUT + INT FATAL_SEND_MSG + + ENTRY SendMessagePhaseMismatch +SendMessagePhaseMismatch: + CLEAR ACK + JUMP ResumeSendMessage +; +; Receive a message. Need to identify the message to +; receive it correctly + ENTRY ReceiveMessage +ReceiveMessage: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN +; +; Use this entry if we've just tried to look at the first byte +; of the message and want to process it further +ProcessReceiveMessage: + JUMP ReceiveExtendedMessage, IF EXTENDED_MSG + RETURN, IF NOT TWO_BYTE_MSG, AND MASK TWO_BYTE_MSG_MASK + CLEAR ACK + MOVE 1, ReceiveMsgAddress + 1, WHEN MSG_IN + RETURN +ReceiveExtendedMessage: + CLEAR ACK + MOVE 1, ReceiveMsgAddress + 1, WHEN MSG_IN + JUMP Receive1Byte, IF 0x01 + JUMP Receive2Byte, IF 0x02 + JUMP Receive3Byte, IF 0x03 + JUMP Receive4Byte, IF 0x04 + JUMP Receive5Byte, IF 0x05 + INT FATAL_ILLEGAL_MSG_LENGTH +Receive1Byte: + CLEAR ACK + MOVE 1, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +Receive2Byte: + CLEAR ACK + MOVE 2, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +Receive3Byte: + CLEAR ACK + MOVE 3, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +Receive4Byte: + CLEAR ACK + MOVE 4, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +Receive5Byte: + CLEAR ACK + MOVE 5, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +; +; Come here from the message processor to ignore the message +; + ENTRY IgnoreMessage +IgnoreMessage: + CLEAR ACK + RETURN +; +; Come here to send a reply to a message +; + ENTRY SendMessageWithATN +SendMessageWithATN: + SET ATN + CLEAR ACK + JUMP SendMessage + +SendIdentifyMsg: + CALL SendMessage + JUMP SendCommand + +IgnoreMsgBeforeCommand: + CLEAR ACK + ENTRY SendCommand +SendCommand: + JUMP Finish, WHEN STATUS + JUMP MsgInBeforeCommand, IF MSG_IN + INT UNEXPECTED_PHASE_BEFORE_CMD, IF NOT CMD + MOVE CommandCount, CommandAddress, WHEN CMD +ResumeSendCommand: + JUMP Finish, WHEN STATUS + JUMP MsgInAfterCmd, IF MSG_IN + JUMP DataIn, IF DATA_IN + JUMP DataOut, IF DATA_OUT + INT UNEXPECTED_PHASE_AFTER_CMD + +IgnoreMsgDuringData: + CLEAR ACK + ; fall through to MsgInDuringData + +Entry MsgInDuringData +MsgInDuringData: +; +; Could be we have nothing more to transfer +; + JUMP Finish, WHEN STATUS + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP DisconnectDuringDataIn, IF DISCONNECT_MSG + JUMP IgnoreMsgDuringData, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgDuringData, IF RESTORE_DATA_PTRS_MSG + INT MSG_IN_DURING_DATA_IN + +MsgInAfterCmd: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP DisconnectAfterCmd, IF DISCONNECT_MSG + JUMP IgnoreMsgInAfterCmd, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgInAfterCmd, IF RESTORE_DATA_PTRS_MSG + CALL ProcessReceiveMessage + INT MSG_IN_AFTER_CMD + CLEAR ACK + JUMP ResumeSendCommand + +IgnoreMsgInAfterCmd: + CLEAR ACK + JUMP ResumeSendCommand + +DisconnectAfterCmd: + CLEAR ACK + WAIT DISCONNECT + ENTRY Disconnect1 +Disconnect1: + INT DISCONNECT_AFTER_CMD + ENTRY Disconnect2 +Disconnect2: +; We return here after a reselection + CLEAR ACK + JUMP ResumeSendCommand + +MsgInBeforeCommand: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP IgnoreMsgBeforeCommand, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgBeforeCommand, IF RESTORE_DATA_PTRS_MSG + CALL ProcessReceiveMessage + INT MSG_IN_BEFORE_CMD + CLEAR ACK + JUMP SendCommand + +DataIn: + CALL SGScriptStartAddress +ResumeDataIn: + JUMP Finish, WHEN STATUS + JUMP MsgInAfterDataIn, IF MSG_IN + JUMP DataInAfterDataIn, if DATA_IN + INT MSG_OUT_AFTER_DATA_IN, if MSG_OUT + INT UNEXPECTED_PHASE_AFTER_DATA_IN + +DataInAfterDataIn: + INT DATA_IN_AFTER_DATA_IN + JUMP ResumeDataIn + +DataOut: + CALL SGScriptStartAddress +ResumeDataOut: + JUMP Finish, WHEN STATUS + JUMP MsgInAfterDataOut, IF MSG_IN + INT UNEXPECTED_PHASE_AFTER_DATA_OUT + +MsgInAfterDataIn: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP DisconnectAfterDataIn, IF DISCONNECT_MSG + JUMP IgnoreMsgAfterData, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgAfterData, IF RESTORE_DATA_PTRS_MSG + CALL ProcessReceiveMessage + INT MSG_IN_AFTER_DATA_IN + CLEAR ACK + JUMP ResumeDataIn + +DisconnectDuringDataIn: + CLEAR ACK + WAIT DISCONNECT + ENTRY Disconnect3 +Disconnect3: + INT DISCONNECT_DURING_DATA + ENTRY Disconnect4 +Disconnect4: +; we return here after a reselection + CLEAR ACK + JUMP ResumeSendCommand + + +DisconnectAfterDataIn: + CLEAR ACK + WAIT DISCONNECT + ENTRY Disconnect5 +Disconnect5: + INT DISCONNECT_AFTER_DATA + ENTRY Disconnect6 +Disconnect6: +; we return here after a reselection + CLEAR ACK + JUMP ResumeDataIn + +MsgInAfterDataOut: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP DisconnectAfterDataOut, if DISCONNECT_MSG + JUMP IgnoreMsgAfterData, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgAfterData, IF RESTORE_DATA_PTRS_MSG + CALL ProcessReceiveMessage + INT MSG_IN_AFTER_DATA_OUT + CLEAR ACK + JUMP ResumeDataOut + +IgnoreMsgAfterData: + CLEAR ACK +; Data in and out do the same thing on resume, so pick one + JUMP ResumeDataIn + +DisconnectAfterDataOut: + CLEAR ACK + WAIT DISCONNECT + ENTRY Disconnect7 +Disconnect7: + INT DISCONNECT_AFTER_DATA + ENTRY Disconnect8 +Disconnect8: +; we return here after a reselection + CLEAR ACK + JUMP ResumeDataOut + +Finish: + MOVE 1, StatusAddress, WHEN STATUS + INT NOT_MSG_IN_AFTER_STATUS, WHEN NOT MSG_IN + CALL ReceiveMessage + CLEAR ACK + WAIT DISCONNECT + ENTRY Finish1 +Finish1: + INT GOOD_STATUS_AFTER_STATUS + ENTRY Finish2 +Finish2: + Index: linux/2.4/drivers/scsi/Config.in diff -u linux/2.4/drivers/scsi/Config.in:1.1.1.8 linux/2.4/drivers/scsi/Config.in:1.1.1.8.4.1 --- linux/2.4/drivers/scsi/Config.in:1.1.1.8 Fri May 4 11:29:02 2001 +++ linux/2.4/drivers/scsi/Config.in Fri May 11 20:15:26 2001 @@ -114,6 +114,7 @@ fi fi dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI +dep_tristate 'NCR Dual 700 MCA SCSI support' CONFIG_SCSI_NCR_D700 $CONFIG_SCSI +$CONFIG_MCA dep_tristate 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI $CONFIG_PCI if [ "$CONFIG_SCSI_NCR53C7xx" != "n" ]; then bool ' always negotiate synchronous transfers' CONFIG_SCSI_NCR53C7xx_sync Index: linux/2.4/drivers/scsi/Makefile diff -u linux/2.4/drivers/scsi/Makefile:1.1.1.8 linux/2.4/drivers/scsi/Makefile:1.1.1.8.4.1 --- linux/2.4/drivers/scsi/Makefile:1.1.1.8 Fri May 4 11:28:52 2001 +++ linux/2.4/drivers/scsi/Makefile Fri May 11 20:15:26 2001 @@ -25,7 +25,7 @@ endif endif -export-objs := scsi_syms.o +export-objs := scsi_syms.o 53c700.o CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS @@ -71,6 +71,7 @@ obj-$(CONFIG_SCSI_IN2000) += in2000.o obj-$(CONFIG_SCSI_GENERIC_NCR5380) += g_NCR5380.o obj-$(CONFIG_SCSI_NCR53C406A) += NCR53c406a.o +obj-$(CONFIG_SCSI_NCR_D700) += NCR_D700.o 53c700.o obj-$(CONFIG_SCSI_SYM53C416) += sym53c416.o obj-$(CONFIG_SCSI_QLOGIC_FAS) += qlogicfas.o obj-$(CONFIG_SCSI_QLOGIC_ISP) += qlogicisp.o @@ -184,3 +185,10 @@ sim710_u.h: sim710_d.h sim710.o : sim710_d.h + +53c700_d.h: 53c700.scr script_asm.pl + $(PERL) -s script_asm.pl -ncr7x0_family < 53c700.scr + mv script.h 53c700_d.h + mv scriptu.h 53c700_u.h + +53c700.o: 53c700_d.h Index: linux/2.4/drivers/scsi/NCR_D700.c diff -u /dev/null linux/2.4/drivers/scsi/NCR_D700.c:1.1.8.4 --- /dev/null Sat May 12 12:05:16 2001 +++ linux/2.4/drivers/scsi/NCR_D700.c Sat May 12 11:52:50 2001 @@ -0,0 +1,327 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* NCR Dual 700 MCA SCSI Driver + * + * Copyright (C) 2001 by [EMAIL PROTECTED] +**----------------------------------------------------------------------------- +** +** 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. +** +**----------------------------------------------------------------------------- + */ + +/* Notes: + * + * Most of the work is done in the chip specific module, 53c700.o + * + * TODO List: + * + * 1. Extract the SCSI ID from the voyager CMOS table (necessary to + * support multi-host environments. + * + * */ + + +/* CHANGELOG + * + * Version 2.1 + * + * Modularise the driver into a Board piece (this file) and a chip + * piece 53c700.[ch] and 53c700.scr, added module options. You can + * now specify the scsi id by the parameters + * + * NCR_D700=slot:<n> [siop:<n>] id:<n> .... + * + * They need to be comma separated if compiled into the kernel + * + * Version 2.0 + * + * Initial implementation of TCQ (Tag Command Queueing). TCQ is full + * featured and uses the clock algorithm to keep track of outstanding + * tags and guard against individual tag starvation. Also fixed a bug + * in all of the 1.x versions where the D700_data_residue() function + * was returning results off by 32 bytes (and thus causing the same 32 + * bytes to be written twice corrupting the data block). It turns out + * the 53c700 only has a 6 bit DBC and DFIFO registers not 7 bit ones + * like the 53c710 (The 710 is the only data manual still available, + * which I'd been using to program the 700). + * + * Version 1.2 + * + * Much improved message handling engine + * + * Version 1.1 + * + * Add code to handle selection reasonably correctly. By the time we + * get the selection interrupt, we've already responded, but drop off the + * bus and hope the selector will go away. + * + * Version 1.0: + * + * Initial release. Fully functional except for procfs and tag + * command queueing. Has only been tested on cards with 53c700-66 + * chips and only single ended. Features are + * + * 1. Synchronous data transfers to offset 8 (limit of 700-66) and + * 100ns (10MHz) limit of SCSI-2 + * + * 2. Disconnection and reselection + * + * Testing: + * + * I've only really tested this with the 700-66 chip, but have done + * soak tests in multi-device environments to verify that + * disconnections and reselections are being processed correctly. + * */ + +#define NCR_D700_VERSION "2.1" + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/proc_fs.h> +#include <linux/init.h> +#include <linux/mca.h> +#include <asm/dma.h> +#include <asm/system.h> +#include <asm/spinlock.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/byteorder.h> +#include <linux/blk.h> + +#ifdef MODULE +#include <linux/module.h> +#endif + + +#include "scsi.h" +#include "hosts.h" +#include "constants.h" + +#include "53c700.h" +#include "NCR_D700.h" + +#ifndef CONFIG_MCA +#error "NCR_D700 driver only compiles for MCA" +#endif + +#ifdef NCR_D700_DEBUG +#define STATIC +#else +#define STATIC static +#endif + +#ifdef MODULE + +char *NCR_D700; /* command line from insmod */ + +MODULE_AUTHOR("James Bottomley"); +MODULE_DESCRIPTION("NCR Dual700 SCSI Driver"); +MODULE_PARM(NCR_D700, "s"); + +#endif + +static __u8 __initdata id_array[2*(MCA_MAX_SLOT_NR + 1)] = + { [0 ... 2*(MCA_MAX_SLOT_NR + 1)-1] = 7 }; + +#ifdef MODULE +#define ARG_SEP ' ' +#else +#define ARG_SEP ',' +#endif + +static int __init +param_setup(char *string) +{ + char *pos = string, *next; + int slot = -1, siop = -1; + + while(pos != NULL && (next = strchr(pos, ':')) != NULL) { + int val = (int)simple_strtoul(++next, NULL, 0); + + if(!strncmp(pos, "slot:", 5)) + slot = val; + else if(!strncmp(pos, "siop:", 5)) + siop = val; + else if(!strncmp(pos, "id:", 3)) { + if(slot == -1) { + printk(KERN_WARNING "NCR D700: Must specify slot for +id parameter\n"); + } else if(slot > MCA_MAX_SLOT_NR) { + printk(KERN_WARNING "NCR D700: Illegal slot %d for id +%d\n", slot, val); + } else { + if(siop != 0 && siop != 1) { + id_array[slot*2] = val; + id_array[slot*2 + 1] =val; + } else { + id_array[slot*2 + siop] = val; + } + } + } + if((pos = strchr(pos, ARG_SEP)) != NULL) + pos++; + } + return 1; +} + +#ifndef MODULE +__setup("NCR_D700=", param_setup); +#endif + +/* Detect a D700 card. Note, because of the set up---the chips are + * essentially connectecd to the MCA bus independently, it is easier + * to set them up as two separate host adapters, rather than one + * adapter with two channels */ +STATIC int __init +D700_detect(Scsi_Host_Template *tpnt) +{ + int slot = 0; + int found = 0; + int differential; + int banner = 1; + + if(!MCA_bus) + return 0; + +#ifdef MODULE + if(NCR_D700) + param_setup(NCR_D700); +#endif + + for(slot = 0; (slot = mca_find_adapter(NCR_D700_MCA_ID, slot)) != +MCA_NOTFOUND; slot++) { + int irq, i; + int pos3j, pos3k, pos3a, pos3b, pos4; + __u32 base_addr, offset_addr; + struct Scsi_Host *host = NULL; + + /* enable board interrupt */ + pos4 = mca_read_pos(slot, 4); + pos4 |= 0x4; + mca_write_pos(slot, 4, pos4); + + mca_write_pos(slot, 6, 9); + pos3j = mca_read_pos(slot, 3); + mca_write_pos(slot, 6, 10); + pos3k = mca_read_pos(slot, 3); + mca_write_pos(slot, 6, 0); + pos3a = mca_read_pos(slot, 3); + mca_write_pos(slot, 6, 1); + pos3b = mca_read_pos(slot, 3); + + base_addr = ((pos3j << 8) | pos3k) & 0xfffffff0; + offset_addr = ((pos3a << 8) | pos3b) & 0xffffff70; + + irq = (pos4 & 0x3) + 11; + if(irq >= 13) + irq++; + if(banner) { + printk(KERN_NOTICE "NCR D700: Driver Version " +NCR_D700_VERSION "\n" + "NCR D700: Copyright (c) 2001 by [EMAIL PROTECTED]\n" + "NCR D700:\n"); + banner = 0; + } + printk(KERN_NOTICE "NCR D700: found in slot %d irq = %d I/O base = +0x%x\n", slot, irq, offset_addr); + + tpnt->proc_name = "NCR_D700"; + + /*outb(BOARD_RESET, base_addr);*/ + + /* clear any pending interrupts */ + (void)inb(base_addr + 0x08); + /* get modctl, used later for setting diff bits */ + switch(differential = (inb(base_addr + 0x08) >> 6)) { + case 0x00: + /* only SIOP1 differential */ + differential = 0x02; + break; + case 0x01: + /* Both SIOPs differential */ + differential = 0x03; + break; + case 0x03: + /* No SIOPs differential */ + differential = 0x00; + break; + default: + printk(KERN_ERR "D700: UNEXPECTED DIFFERENTIAL RESULT +0x%02x\n", + differential); + differential = 0x00; + break; + } + + /* plumb in both 700 chips */ + for(i=0; i<2; i++) { + __u32 region = offset_addr | (0x80 * i); + struct NCR_700_Host_Parameters *hostdata = + kmalloc(sizeof(struct NCR_700_Host_Parameters), + GFP_KERNEL); + if(hostdata == NULL) { + printk(KERN_ERR "NCR D700: Failed to allocate host +data for channel %d, detatching\n", i); + continue; + } + memset(hostdata, 0, sizeof(struct NCR_700_Host_Parameters)); + request_region(region, 64, "NCR_D700"); + + /* Fill in the three required pieces of hostdata */ + hostdata->base = region; + hostdata->differential = (((1<<i) & differential) != 0); + hostdata->clock = NCR_D700_CLOCK_MHZ; + /* and register the chip */ + if((host = NCR_700_detect(tpnt, hostdata)) == NULL) { + kfree(hostdata); + continue; + } + host->irq = irq; + /* FIXME: Read this from SUS */ + host->this_id = id_array[slot * 2 + i]; + printk(KERN_NOTICE "NCR D700: SIOP%d, SCSI id is %d\n", + i, host->this_id); + if(request_irq(irq, NCR_700_intr, SA_SHIRQ, "NCR_D700", host)) +{ + printk(KERN_ERR "NCR D700, channel %d: irq problem, +detatching\n", i); + NCR_700_release(host); + release_region(host->base, 64); + continue; + } + found++; + } + } + + return found; +} + + +STATIC int +D700_release(struct Scsi_Host *host) +{ + struct D700_Host_Parameters *hostdata = + (struct D700_Host_Parameters *)host->hostdata[0]; + + NCR_700_release(host); + kfree(hostdata); + free_irq(host->irq, host); + release_region(host->base, 64); + return 1; +} + + +static Scsi_Host_Template driver_template = NCR_D700_SCSI; + +#include "scsi_module.c" + Index: linux/2.4/drivers/scsi/NCR_D700.h diff -u /dev/null linux/2.4/drivers/scsi/NCR_D700.h:1.1.8.1 --- /dev/null Sat May 12 12:05:16 2001 +++ linux/2.4/drivers/scsi/NCR_D700.h Sat May 12 10:39:20 2001 @@ -0,0 +1,45 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* NCR Dual 700 MCA SCSI Driver + * + * Copyright (C) 2001 by [EMAIL PROTECTED] + */ + +#ifndef _NCR_D700_H +#define _NCR_D700_H + +/* Don't turn on debugging messages */ +#undef NCR_D700_DEBUG + +/* The MCA identifier */ +#define NCR_D700_MCA_ID 0x0092 + +static int D700_detect(Scsi_Host_Template *); +static int D700_release(struct Scsi_Host *host); + + +/* Host template. Note the name and proc_name are optional, all the + * remaining parameters shown below must be filled in. The 53c700 + * routine NCR_700_detect will fill in all of the missing routines */ +#define NCR_D700_SCSI { \ + name: "NCR Dual 700 MCA", \ + proc_name: "NCR_D700", \ + detect: D700_detect, \ + release: D700_release, \ + this_id: 7, \ +} + + +/* Defines for the Board registers */ +#define BOARD_RESET 0x80 /* board level reset */ +#define ADD_PARENB 0x04 /* Address Parity Enabled */ +#define DAT_PARENB 0x01 /* Data Parity Enabled */ +#define SFBK_ENB 0x10 /* SFDBK Interrupt Enabled */ +#define LED0GREEN 0x20 /* Led 0 (red 0; green 1) */ +#define LED1GREEN 0x40 /* Led 1 (red 0; green 1) */ +#define LED0RED 0xDF /* Led 0 (red 0; green 1) */ +#define LED1RED 0xBF /* Led 1 (red 0; green 1) */ + +#define NCR_D700_CLOCK_MHZ 50 + +#endif Index: linux/2.4/drivers/scsi/README.53c700 diff -u /dev/null linux/2.4/drivers/scsi/README.53c700:1.1.4.2 --- /dev/null Sat May 12 12:05:16 2001 +++ linux/2.4/drivers/scsi/README.53c700 Sat May 12 12:03:57 2001 @@ -0,0 +1,17 @@ +This driver supports the 53c700 and 53c700-66 chips only. It is full +featured and does sync (-66 only), disconnects and tag command +queueing. + +Since the 53c700 must be interfaced to a bus, you need to wrapper the +card detector around this driver. For an example, see the +NCR_D700.[ch] files. + +The comments in the 53c700.[ch] files tell you which parts you need to +fill in to get the driver working. + +The driver is currently I/O mapped only, but it should be easy enough +to memory map (just make the port reads #defines with MEM_MAPPED for +memory mapping or nothing for I/O mapping, specify an extra rule for +53c700-mem.o with the -DMEM_MAPPED flag and make your driver use it, +that way the make rules will generate the correct version). +