diff options
| author | Linus Torvalds <torvalds@home.transmeta.com> | 2003-04-26 02:15:32 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2003-04-26 02:15:32 -0700 |
| commit | fc53e6ecf04000814266b3b042f73daf0ca5dd72 (patch) | |
| tree | aa37697e61b951832e7cbf04dc136ae62923bf9a /drivers | |
| parent | 0c482202e06cf8ac668404c3468e0a211a8fa9e0 (diff) | |
| parent | a537cfc1a5df502699d9594e47750790ad6e2107 (diff) | |
Merge http://linux-scsi.bkbits.net/scsi-for-linus-2.5
into home.transmeta.com:/home/torvalds/v2.5/linux
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/char/drm/i810_dma.c | 2 | ||||
| -rw-r--r-- | drivers/sbus/char/openprom.c | 1 | ||||
| -rw-r--r-- | drivers/scsi/Kconfig | 17 | ||||
| -rw-r--r-- | drivers/scsi/Makefile | 1 | ||||
| -rw-r--r-- | drivers/scsi/dc395x.c | 6351 | ||||
| -rw-r--r-- | drivers/scsi/dc395x.h | 659 |
6 files changed, 7030 insertions, 1 deletions
diff --git a/drivers/char/drm/i810_dma.c b/drivers/char/drm/i810_dma.c index 8dfdede104c6..e32b8b20181c 100644 --- a/drivers/char/drm/i810_dma.c +++ b/drivers/char/drm/i810_dma.c @@ -414,7 +414,7 @@ static int i810_dma_initialize(drm_device_t *dev, return -ENOMEM; } memset(dev_priv->hw_status_page, 0, PAGE_SIZE); - DRM_DEBUG("hw status page @ %lx\n", dev_priv->hw_status_page); + DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page); I810_WRITE(0x02080, dev_priv->dma_status_page); DRM_DEBUG("Enabled hardware status page\n"); diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c index 9a054bf59b36..c140f63b366b 100644 --- a/drivers/sbus/char/openprom.c +++ b/drivers/sbus/char/openprom.c @@ -40,6 +40,7 @@ #include <linux/string.h> #include <linux/miscdevice.h> #include <linux/init.h> +#include <linux/fs.h> #include <asm/oplib.h> #include <asm/system.h> #include <asm/uaccess.h> diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 05329e7a859e..7c79bb82cc20 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1390,6 +1390,23 @@ config SCSI_SYM53C416 read <file:Documentation/modules.txt>. The module will be called sym53c416. +config SCSI_DC395x + tristate "Tekram DC395(U/UW/F) and DC315(U) SCSI support (EXPERIMENTAL)" + depends on EXPERIMENTAL && PCI && SCSI + ---help--- + This driver supports PCI SCSI host adapters based on the ASIC + TRM-S1040 chip, e.g Tekram DC395(U/UW/F) and DC315(U) variants. + + This driver works, but is still in experimental status. So better + have a bootable disk and a backup in case of emergency. + + Documentation can be found in <file:Documentation/scsi/dc395x.txt>. + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read <file:Documentation/modules.txt>. The module + will be called dc395x. + config SCSI_DC390T tristate "Tekram DC390(T) and Am53/79C974 SCSI support" depends on PCI && SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index d9f2723f69e3..908c44892a47 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_SCSI_7000FASST) += wd7000.o obj-$(CONFIG_SCSI_MCA_53C9X) += NCR53C9x.o mca_53c9x.o obj-$(CONFIG_SCSI_IBMMCA) += ibmmca.o obj-$(CONFIG_SCSI_EATA) += eata.o +obj-$(CONFIG_SCSI_DC395x) += dc395x.o obj-$(CONFIG_SCSI_DC390T) += tmscsim.o obj-$(CONFIG_SCSI_AM53C974) += AM53C974.o obj-$(CONFIG_SCSI_MEGARAID) += megaraid.o diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c new file mode 100644 index 000000000000..b184bf40de71 --- /dev/null +++ b/drivers/scsi/dc395x.c @@ -0,0 +1,6351 @@ +/* + * dc395x.c + * + * Device Driver for Tekram DC395(U/UW/F), DC315(U) + * PCI SCSI Bus Master Host Adapter + * (SCSI chip set used Tekram ASIC TRM-S1040) + * + * Authors: + * C.L. Huang <ching@tekram.com.tw> + * Erich Chen <erich@tekram.com.tw> + * (C) Copyright 1995-1999 Tekram Technology Co., Ltd. + * + * Kurt Garloff <garloff@suse.de> + * (C) 1999-2000 Kurt Garloff + * + * Oliver Neukum <oliver@neukum.name> + * Ali Akcaagac <aliakc@web.de> + * Jamie Lenehan <lenehan@twibble.org> + * (C) 2003 + * + * License: GNU GPL + * + ************************************************************************* + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************ + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/blk.h> +#include "scsi.h" +#include "hosts.h" +#include "dc395x.h" +#include <scsi/scsicam.h> /* needed for scsicam_bios_param */ +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/spinlock.h> + +/* Debugging */ +/*#define DC395x_DEBUG_KG */ +/*#define DC395x_DEBUG0*/ +/*#define DC395x_DEBUG1*/ +/*#define DC395x_DEBUGDCB*/ +#define DC395x_DEBUGTRACE +/*#define DC395x_DEBUGTRACEALL*/ +/*#define DC395x_DEBUGPARSE*/ +/*#define DC395x_SGPARANOIA*/ +/*#define DC395x_DEBUGFIFO*/ +/*#define DC395x_DEBUGRECURSION*/ +/*#define DC395x_DEBUGPIO*/ +/*#define DC395x_DEBUGMALLOC*/ + +/* DISable features */ +/*#define DC395x_NO_DISCONNECT*/ +/*#define DC395x_NO_TAGQ*/ +/*#define DC395x_NO_SYNC*/ +/*#define DC395x_NO_WIDE*/ + +#ifdef DC395x_DEBUG0 +# define DEBUG0(x) x +#else +# define DEBUG0(x) +#endif + +#ifdef DC395x_DEBUG1 +# define DEBUG1(x) x +#else +# define DEBUG1(x) +#endif + +#ifdef DC395x_DEBUGDCB +# define DCBDEBUG(x) x +#else +# define DCBDEBUG(x) +#endif + +#ifdef DC395x_DEBUGPARSE +# define PARSEDEBUG(x) x +#else +# define PARSEDEBUG(x) +#endif + +#ifdef DC395x_DEBUGRECURSION +# define DEBUGRECURSION(x) x +#else +# define DEBUGRECURSION(x) +#endif + +#ifdef DC395x_DEBUGPIO +# define DEBUGPIO(x) x +#else +# define DEBUGPIO(x) +#endif + +/* Here comes the joker of all debugging facilities! */ +#ifdef DC395x_DEBUGTRACEALL +# ifndef DC395x_DEBUGTRACE +# define DC395x_DEBUGTRACE +# endif +# define TRACEOUTALL(x...) printk ( x) +#else +# define TRACEOUTALL(x...) do {} while (0) +#endif +#ifdef DC395x_DEBUGTRACE +# define DEBUGTRACEBUFSZ 512 +char DC395x_tracebuf[64]; +char DC395x_traceoverflow[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +# define TRACEPRINTF(x...) \ +do { int ln = sprintf (DC395x_tracebuf, x); \ + if (pSRB->debugpos + ln >= DEBUGTRACEBUFSZ) \ + { pSRB->debugtrace[pSRB->debugpos] = 0; pSRB->debugpos = DEBUGTRACEBUFSZ/5; pSRB->debugtrace[pSRB->debugpos++] = '>'; }; \ + sprintf (pSRB->debugtrace + pSRB->debugpos, "%s", DC395x_tracebuf); \ + pSRB->debugpos += ln - 1; \ + } while (0) +# define TRACEOUT(x...) printk ( x) +#else +# define TRACEPRINTF(x...) do {} while (0) +# define TRACEOUT(x...) do {} while (0) +#endif + + +#ifdef DC395x_DEBUGMALLOC +inline void *dc395x_kmalloc(size_t sz, int fl) +{ + void *ptr = kmalloc(sz, fl); + printk(KERN_DEBUG DC395X_NAME ": Alloc %i bytes @ %p w/ fl %08x\n", + sz, ptr, fl); + return ptr; +} +inline void dc395x_kfree(const void *adr) +{ + printk(KERN_DEBUG DC395X_NAME ": Free mem @ %p\n", adr); + kfree(adr); +} + +# define KMALLOC(sz,fl) dc395x_kmalloc(sz,fl) +# define KFREE(adr) dc395x_kfree(adr) +#else +# define KMALLOC(sz,fl) kmalloc(sz,fl) +# define KFREE(adr) kfree(adr) +#endif + + +#ifndef PCI_VENDOR_ID_TEKRAM +#define PCI_VENDOR_ID_TEKRAM 0x1DE1 /* Vendor ID */ +#endif +#ifndef PCI_DEVICE_ID_TEKRAM_TRMS1040 +#define PCI_DEVICE_ID_TEKRAM_TRMS1040 0x0391 /* Device ID */ +#endif + +static struct pci_device_id dc395x_pci_tbl[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_TEKRAM, + .device = PCI_DEVICE_ID_TEKRAM_TRMS1040, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, dc395x_pci_tbl); + + +#define DC395x_LOCK_IO(dev) spin_lock_irqsave(((struct Scsi_Host *)dev)->host_lock, flags) +#define DC395x_UNLOCK_IO(dev) spin_unlock_irqrestore(((struct Scsi_Host *)dev)->host_lock, flags) + +#define DC395x_ACB_INITLOCK(pACB) spin_lock_init(&pACB->smp_lock) +#define DC395x_ACB_LOCK(pACB,acb_flags) if (!pACB->lock_level_count[cpuid]) { spin_lock_irqsave(&pACB->smp_lock,acb_flags); pACB->lock_level_count[cpuid]++; } else { pACB->lock_level_count[cpuid]++; } +#define DC395x_ACB_UNLOCK(pACB,acb_flags) if (--pACB->lock_level_count[cpuid] == 0) { spin_unlock_irqrestore(&pACB->smp_lock,acb_flags); } + +#define DC395x_SMP_IO_LOCK(dev,irq_flags) spin_lock_irqsave(((struct Scsi_Host*)dev)->host_lock,irq_flags) +#define DC395x_SMP_IO_UNLOCK(dev,irq_flags) spin_unlock_irqrestore(((struct Scsi_Host*)dev)->host_lock,irq_flags) +#define DC395x_SCSI_DONE_ACB_LOCK spin_lock(&(pACB->smp_lock)) +#define DC395x_SCSI_DONE_ACB_UNLOCK spin_unlock(&(pACB->smp_lock)) + + +#define DC395x_read8(address) (u8)(inb(pACB->IOPortBase + (address))) +#define DC395x_read8_(address, base) (u8)(inb((USHORT)(base) + (address))) +#define DC395x_read16(address) (u16)(inw(pACB->IOPortBase + (address))) +#define DC395x_read32(address) (u32)(inl(pACB->IOPortBase + (address))) +#define DC395x_write8(address,value) outb((value), pACB->IOPortBase + (address)) +#define DC395x_write8_(address,value,base) outb((value), (USHORT)(base) + (address)) +#define DC395x_write16(address,value) outw((value), pACB->IOPortBase + (address)) +#define DC395x_write32(address,value) outl((value), pACB->IOPortBase + (address)) + + +#define BUS_ADDR(sg) sg_dma_address(&(sg)) +#define CPU_ADDR(sg) (page_address((sg).page)+(sg).offset) +#define PAGE_ADDRESS(sg) page_address((sg)->page) +#define SET_DIR(dir,pcmd) dir = scsi_to_pci_dma_dir((pcmd)->sc_data_direction) + +/* cmd->result */ +#define RES_TARGET 0x000000FF /* Target State */ +#define RES_TARGET_LNX STATUS_MASK /* Only official ... */ +#define RES_ENDMSG 0x0000FF00 /* End Message */ +#define RES_DID 0x00FF0000 /* DID_ codes */ +#define RES_DRV 0xFF000000 /* DRIVER_ codes */ + +#define MK_RES(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt)) +#define MK_RES_LNX(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt)<<1) + +#define SET_RES_TARGET(who,tgt) { who &= ~RES_TARGET; who |= (int)(tgt); } +#define SET_RES_TARGET_LNX(who,tgt) { who &= ~RES_TARGET_LNX; who |= (int)(tgt) << 1; } +#define SET_RES_MSG(who,msg) { who &= ~RES_ENDMSG; who |= (int)(msg) << 8; } +#define SET_RES_DID(who,did) { who &= ~RES_DID; who |= (int)(did) << 16; } +#define SET_RES_DRV(who,drv) { who &= ~RES_DRV; who |= (int)(drv) << 24; } + +/* +************************************************************************** +*/ +#define NO_IRQ 255 +#define TAG_NONE 255 + +struct SGentry { + u32 address; /* bus! address */ + u32 length; +}; + + +/*----------------------------------------------------------------------- + SCSI Request Block + -----------------------------------------------------------------------*/ +struct ScsiReqBlk { + struct ScsiReqBlk *pNextSRB; + struct DeviceCtlBlk *pSRBDCB; + + /* HW scatter list (up to 64 entries) */ + struct SGentry *SegmentX; + Scsi_Cmnd *pcmd; + + /* Offset 0x20/0x10 */ + unsigned char *virt_addr; /* set by DC395x_update_SGlist */ + + u32 SRBTotalXferLength; + u32 Xferred; /* Backup for the already xferred len */ + + u32 SRBSGBusAddr; /* bus address of DC395x scatterlist */ + + u16 SRBState; + u8 SRBSGCount; + u8 SRBSGIndex; + + /* Offset 0x38/0x24 */ + u8 MsgInBuf[6]; + u8 MsgOutBuf[6]; + + u8 AdaptStatus; + u8 TargetStatus; + u8 MsgCnt; + u8 EndMessage; + + /* Offset 0x48/0x34 */ + u8 *pMsgPtr; + + u8 TagNumber; + u8 SRBStatus; + u8 RetryCnt; + u8 SRBFlag; + + u8 ScsiPhase; + u8 padding; + u16 debugpos; + /* Offset 0x58/0x40 */ +#ifdef DC395x_DEBUGTRACE + char *debugtrace; + /* Offset 0x60/0x44 */ +#endif +}; + + +/*----------------------------------------------------------------------- + Device Control Block + -----------------------------------------------------------------------*/ +struct DeviceCtlBlk { + struct DeviceCtlBlk *pNextDCB; + struct AdapterCtlBlk *pDCBACB; + + struct ScsiReqBlk *pGoingSRB; + struct ScsiReqBlk *pGoingLast; + +/* 0x10: */ + struct ScsiReqBlk *pWaitingSRB; + struct ScsiReqBlk *pWaitLast; + + struct ScsiReqBlk *pActiveSRB; + u32 TagMask; + +/* 0x20: */ + u16 MaxCommand; + u8 AdaptIndex; /* UnitInfo struc start */ + u8 UnitIndex; /* nth Unit on this card */ + + u16 GoingSRBCnt; + u16 WaitSRBCnt; + u8 TargetID; /* SCSI Target ID (SCSI Only) */ + u8 TargetLUN; /* SCSI Log. Unit (SCSI Only) */ + u8 IdentifyMsg; + u8 DevMode; + +/* 0x2c: */ +/* u8 AdpMode;*/ + u8 Inquiry7; /* To store Inquiry flags */ + u8 SyncMode; /* 0:async mode */ + u8 MinNegoPeriod; /* for nego. */ + u8 SyncPeriod; /* for reg. */ + + u8 SyncOffset; /* for reg. and nego.(low nibble) */ + u8 UnitCtrlFlag; + u8 DCBFlag; + u8 DevType; + u8 init_TCQ_flag; + + unsigned long last_derated; /* last time, when features were turned off in abort */ +/* 0x38: */ + /* u8 Reserved2[3]; for dword alignment */ +}; + +/*----------------------------------------------------------------------- + Adapter Control Block + -----------------------------------------------------------------------*/ +struct AdapterCtlBlk { + struct Scsi_Host *pScsiHost; + struct AdapterCtlBlk *pNextACB; + + u16 IOPortBase; + u16 Revxx1; + + struct DeviceCtlBlk *pLinkDCB; + struct DeviceCtlBlk *pLastDCB; + struct DeviceCtlBlk *pDCBRunRobin; + + struct DeviceCtlBlk *pActiveDCB; + + struct ScsiReqBlk *pFreeSRB; + struct ScsiReqBlk *pTmpSRB; + struct timer_list Waiting_Timer; + struct timer_list SelTO_Timer; + + u16 SRBCount; + u16 AdapterIndex; /* nth Adapter this driver */ + + u32 QueryCnt; + Scsi_Cmnd *pQueryHead; + Scsi_Cmnd *pQueryTail; + + u8 msgin123[4]; + + u8 status; + u8 DCBCnt; + u8 sel_timeout; + u8 dummy; + + u8 IRQLevel; + u8 TagMaxNum; + u8 ACBFlag; + u8 Gmode2; + + u8 Config; + u8 LUNchk; + u8 scan_devices; + u8 HostID_Bit; + + u8 DCBmap[DC395x_MAX_SCSI_ID]; + struct DeviceCtlBlk *children[DC395x_MAX_SCSI_ID][32]; + + u32 Cmds; + u32 SelLost; + u32 SelConn; + u32 CmdInQ; + u32 CmdOutOfSRB; + + /*struct DeviceCtlBlk DCB_array[DC395x_MAX_DCB]; *//* +74h, Len=3E8 */ + struct pci_dev *pdev; + + u8 MsgLen; + u8 DeviceCnt; + + struct ScsiReqBlk SRB_array[DC395x_MAX_SRB_CNT]; + struct ScsiReqBlk TmpSRB; +}; + + +/* + * The SEEPROM structure for TRM_S1040 + */ +struct NVRamTarget { + u8 NvmTarCfg0; /* Target configuration byte 0 */ + u8 NvmTarPeriod; /* Target period */ + u8 NvmTarCfg2; /* Target configuration byte 2 */ + u8 NvmTarCfg3; /* Target configuration byte 3 */ +}; + + +struct NvRamType { + u8 NvramSubVendorID[2]; /* 0,1 Sub Vendor ID */ + u8 NvramSubSysID[2]; /* 2,3 Sub System ID */ + u8 NvramSubClass; /* 4 Sub Class */ + u8 NvramVendorID[2]; /* 5,6 Vendor ID */ + u8 NvramDeviceID[2]; /* 7,8 Device ID */ + u8 NvramReserved; /* 9 Reserved */ + struct NVRamTarget NvramTarget[DC395x_MAX_SCSI_ID]; + /** 10,11,12,13 + ** 14,15,16,17 + ** .... + ** .... + ** 70,71,72,73 + */ + u8 NvramScsiId; /* 74 Host Adapter SCSI ID */ + u8 NvramChannelCfg; /* 75 Channel configuration */ + u8 NvramDelayTime; /* 76 Power on delay time */ + u8 NvramMaxTag; /* 77 Maximum tags */ + u8 NvramReserved0; /* 78 */ + u8 NvramBootTarget; /* 79 */ + u8 NvramBootLun; /* 80 */ + u8 NvramReserved1; /* 81 */ + u16 Reserved[22]; /* 82,..125 */ + u16 NvramCheckSum; /* 126,127 */ +}; + +/*------------------------------------------------------------------------------*/ + +void DC395x_DataOutPhase0(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, u16 * pscsi_status); +void DC395x_DataInPhase0(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, u16 * pscsi_status); +static void DC395x_CommandPhase0(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_StatusPhase0(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_MsgOutPhase0(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +void DC395x_MsgInPhase0(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, u16 * pscsi_status); +static void DC395x_DataOutPhase1(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_DataInPhase1(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_CommandPhase1(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_StatusPhase1(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_MsgOutPhase1(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_MsgInPhase1(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_Nop0(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, u16 * pscsi_status); +static void DC395x_Nop1(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, u16 * pscsi_status); +static void DC395x_basic_config(struct AdapterCtlBlk *pACB); +static void DC395x_cleanup_after_transfer(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB); +static void DC395x_ResetSCSIBus(struct AdapterCtlBlk *pACB); +void DC395x_DataIO_transfer(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, u16 ioDir); +void DC395x_Disconnect(struct AdapterCtlBlk *pACB); +void DC395x_Reselect(struct AdapterCtlBlk *pACB); +u8 DC395x_StartSCSI(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB); +static void DC395x_BuildSRB(Scsi_Cmnd * pcmd, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB); +void DC395x_DoingSRB_Done(struct AdapterCtlBlk *pACB, u8 did_code, + Scsi_Cmnd * pcmd, u8 force); +static void DC395x_ScsiRstDetect(struct AdapterCtlBlk *pACB); +static void DC395x_pci_unmap(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB); +static void DC395x_pci_unmap_sense(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB); +static inline void DC395x_EnableMsgOut_Abort(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB); +void DC395x_SRBdone(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB); +static void DC395x_RequestSense(struct AdapterCtlBlk *pACB, + struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB); +static inline void DC395x_SetXferRate(struct AdapterCtlBlk *pACB, + struct DeviceCtlBlk *pDCB); +void DC395x_initDCB(struct AdapterCtlBlk *pACB, + struct DeviceCtlBlk **ppDCB, u8 target, u8 lun); +int DC395x_shutdown(struct Scsi_Host *host); +static void DC395x_remove_dev(struct AdapterCtlBlk *pACB, + struct DeviceCtlBlk *pDCB); + + +static struct AdapterCtlBlk *DC395x_pACB_start = NULL; +static struct AdapterCtlBlk *DC395x_pACB_current = NULL; +static u16 DC395x_adapterCnt = 0; +static u16 DC395x_CurrSyncOffset = 0; + +DEBUGRECURSION(static char in_driver = 0; + ) +static char DC395x_monitor_next_IRQ = 0; + +/* + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + */ +static void *DC395x_SCSI_phase0[] = { + DC395x_DataOutPhase0, /* phase:0 */ + DC395x_DataInPhase0, /* phase:1 */ + DC395x_CommandPhase0, /* phase:2 */ + DC395x_StatusPhase0, /* phase:3 */ + DC395x_Nop0, /* phase:4 PH_BUS_FREE .. initial phase */ + DC395x_Nop0, /* phase:5 PH_BUS_FREE .. initial phase */ + DC395x_MsgOutPhase0, /* phase:6 */ + DC395x_MsgInPhase0, /* phase:7 */ +}; + +/* + * DC395x_stateV = (void *)DC395x_SCSI_phase1[phase] + */ +static void *DC395x_SCSI_phase1[] = { + DC395x_DataOutPhase1, /* phase:0 */ + DC395x_DataInPhase1, /* phase:1 */ + DC395x_CommandPhase1, /* phase:2 */ + DC395x_StatusPhase1, /* phase:3 */ + DC395x_Nop1, /* phase:4 PH_BUS_FREE .. initial phase */ + DC395x_Nop1, /* phase:5 PH_BUS_FREE .. initial phase */ + DC395x_MsgOutPhase1, /* phase:6 */ + DC395x_MsgInPhase1, /* phase:7 */ +}; + +struct NvRamType dc395x_trm_eepromBuf[DC395x_MAX_ADAPTER_NUM]; +/* + *Fast20: 000 50ns, 20.0 MHz + * 001 75ns, 13.3 MHz + * 010 100ns, 10.0 MHz + * 011 125ns, 8.0 MHz + * 100 150ns, 6.6 MHz + * 101 175ns, 5.7 MHz + * 110 200ns, 5.0 MHz + * 111 250ns, 4.0 MHz + * + *Fast40(LVDS): 000 25ns, 40.0 MHz + * 001 50ns, 20.0 MHz + * 010 75ns, 13.3 MHz + * 011 100ns, 10.0 MHz + * 100 125ns, 8.0 MHz + * 101 150ns, 6.6 MHz + * 110 175ns, 5.7 MHz + * 111 200ns, 5.0 MHz + */ +/*static u8 dc395x_clock_period[] = {12,19,25,31,37,44,50,62};*/ + +/* real period:48ns,76ns,100ns,124ns,148ns,176ns,200ns,248ns */ +static u8 dc395x_clock_period[] = { 12, 18, 25, 31, 37, 43, 50, 62 }; +static u16 dc395x_clock_speed[] = { 200, 133, 100, 80, 67, 58, 50, 40 }; +/* real period:48ns,72ns,100ns,124ns,148ns,172ns,200ns,248ns */ + +/* + * Override defaults on cmdline: + * dc395x_trm = AdaptID, MaxSpeed (Index), DevMode (Bitmapped), AdaptMode (Bitmapped), Tags (log2-1), DelayReset + */ +int dc395x_trm[] = { -2, -2, -2, -2, -2, -2 }; + +#if defined(MODULE) +MODULE_PARM(dc395x_trm, "1-6i"); +MODULE_PARM_DESC(dc395x_trm, + "Host SCSI ID, Speed (0=20MHz), Device Flags, Adapter Flags, Max Tags (log2(tags)-1), DelayReset (s)"); +#endif + +MODULE_AUTHOR("C.L. Huang / Erich Chen / Kurt Garloff"); +MODULE_DESCRIPTION + ("SCSI host adapter driver for Tekram TRM-S1040 based adapters: Tekram DC395 and DC315 series"); +MODULE_SUPPORTED_DEVICE("sd,sr,sg,st"); + +MODULE_LICENSE("GPL"); + +/* Delaying after a reset */ +static char __initdata DC395x_interpd[] = { 1, 3, 5, 10, 16, 30, 60, 120 }; + +/* Convert EEprom value to seconds */ +static void __init DC395x_interpret_delay(struct NvRamType *eeprom) +{ + /*printk (DC395X_NAME ": Debug: Delay: %i\n", eeprom->NvramDelayTime); */ + eeprom->NvramDelayTime = DC395x_interpd[eeprom->NvramDelayTime]; +} + +/* seconds to EEProm value */ +static int __init DC395x_uninterpret_delay(int delay) +{ + u8 idx = 0; + while (idx < 7 && DC395x_interpd[idx] < delay) + idx++; + return idx; +} + + +/* Handle "-1" case */ +static void __init DC395x_check_for_safe_settings(void) +{ + if (dc395x_trm[0] == -1 || dc395x_trm[0] > 15) { /* modules-2.0.0 passes -1 as string */ + dc395x_trm[0] = 7; + dc395x_trm[1] = 4; + dc395x_trm[2] = 0x09; + dc395x_trm[3] = 0x0f; + dc395x_trm[4] = 2; + dc395x_trm[5] = 10; + printk(KERN_INFO DC395X_NAME ": Using safe settings.\n"); + } +} + +/* Defaults, to be overriden by (a) BIOS and (b) Cmnd line (kernel/module) args */ +int __initdata dc395x_def[] = { 7, 1 /* 13.3MHz */ , + NTC_DO_PARITY_CHK | NTC_DO_DISCONNECT | NTC_DO_SYNC_NEGO | + NTC_DO_WIDE_NEGO | NTC_DO_TAG_QUEUEING | NTC_DO_SEND_START, + NAC_GT2DRIVES | NAC_GREATER_1G | NAC_POWERON_SCSI_RESET + /* | NAC_ACTIVE_NEG */ +#ifdef CONFIG_SCSI_MULTI_LUN + | NAC_SCANLUN +#endif + , 3 /* 16 Tags per LUN */ , 1 /* s delay after Reset */ +}; + +/* Copy defaults over set values where missing */ +static void __init DC395x_fill_with_defaults(void) +{ + int i; + PARSEDEBUG(printk + (KERN_INFO DC395X_NAME + ": setup %08x %08x %08x %08x %08x %08x\n", + dc395x_trm[0], dc395x_trm[1], dc395x_trm[2], + dc395x_trm[3], dc395x_trm[4], dc395x_trm[5]); + ) + for (i = 0; i < 6; i++) { + if (dc395x_trm[i] < 0 || dc395x_trm[i] > 255) + dc395x_trm[i] = dc395x_def[i]; + } + /* Sanity checks */ + if (dc395x_trm[0] > 15) + dc395x_trm[0] = 7; + if (dc395x_trm[1] > 7) + dc395x_trm[1] = 4; + if (dc395x_trm[4] > 5) + dc395x_trm[4] = 4; + if (dc395x_trm[5] > 180) + dc395x_trm[5] = 180; +} + + +/* Read the parameters from the command line */ +#if !defined(MODULE) +static int DC395x_trm_setup(char *str) +{ + int i; + int im; + int ints[8]; + (void) get_options(str, ARRAY_SIZE(ints), ints); + im = ints[0]; + if (im > 6) { + printk(KERN_NOTICE DC395X_NAME ": ignore extra params!\n"); + im = 6; + } + for (i = 0; i < im; i++) + dc395x_trm[i] = ints[i + 1]; + + return 1; +} + +__setup(DC395X_NAME "=", DC395x_trm_setup); + +#endif /* !MODULE */ + +/* Overrride BIOS values with the set ones */ +static void __init DC395x_EEprom_Override(struct NvRamType *eeprom) +{ + u8 id; + + /* Adapter Settings */ + if (dc395x_trm[0] != -2) + eeprom->NvramScsiId = (u8) dc395x_trm[0]; /* Adapter ID */ + if (dc395x_trm[3] != -2) + eeprom->NvramChannelCfg = (u8) dc395x_trm[3]; + if (dc395x_trm[5] != -2) + eeprom->NvramDelayTime = DC395x_uninterpret_delay(dc395x_trm[5]); /* Reset delay */ + if (dc395x_trm[4] != -2) + eeprom->NvramMaxTag = (u8) dc395x_trm[4]; /* Tagged Cmds */ + + /* Device Settings */ + for (id = 0; id < DC395x_MAX_SCSI_ID; id++) { + if (dc395x_trm[2] != -2) + eeprom->NvramTarget[id].NvmTarCfg0 = (u8) dc395x_trm[2]; /* Cfg0 */ + if (dc395x_trm[1] != -2) + eeprom->NvramTarget[id].NvmTarPeriod = (u8) dc395x_trm[1]; /* Speed */ + } +} + + +/* + * Queueing philosphy: + * There are a couple of lists: + * - Query: Contains the Scsi Commands not yet turned into SRBs (per ACB) + * (Note: For new EH, it is unecessary!) + * - Waiting: Contains a list of SRBs not yet sent (per DCB) + * - Free: List of free SRB slots + * + * If there are no waiting commands for the DCB, the new one is sent to the bus + * otherwise the oldest one is taken from the Waiting list and the new one is + * queued to the Waiting List + * + * Lists are managed using two pointers and eventually a counter + */ + +/* Nomen est omen ... */ +static inline void +DC395x_freetag(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB) +{ + if (pSRB->TagNumber < 255) { + pDCB->TagMask &= ~(1 << pSRB->TagNumber); /* free tag mask */ + pSRB->TagNumber = 255; + } +} + + +/* Find cmd in SRB list */ +inline static struct ScsiReqBlk *DC395x_find_cmd(Scsi_Cmnd * pcmd, + struct ScsiReqBlk *start) +{ + struct ScsiReqBlk *psrb = start; + if (!start) + return 0; + do { + if (psrb->pcmd == pcmd) + return psrb; + psrb = psrb->pNextSRB; + } while (psrb && psrb != start); + return 0; +} + + +/* Append to Query List */ +static void +DC395x_Query_append(Scsi_Cmnd * cmd, struct AdapterCtlBlk *pACB) +{ + DEBUG0(printk(DC395X_NAME ": Append cmd %li to Query\n", cmd->pid); + ) + + cmd->host_scribble = NULL; + + if (!pACB->QueryCnt) + pACB->pQueryHead = cmd; + else + pACB->pQueryTail->host_scribble = (void *) cmd; + + pACB->pQueryTail = cmd; + pACB->QueryCnt++; + pACB->CmdOutOfSRB++; +} + + +/* Return next cmd from Query list */ +static Scsi_Cmnd *DC395x_Query_get(struct AdapterCtlBlk *pACB) +{ + Scsi_Cmnd *pcmd; + + pcmd = pACB->pQueryHead; + if (!pcmd) + return pcmd; + DEBUG0(printk(DC395X_NAME ": Get cmd %li from Query\n", pcmd->pid); + ) + pACB->pQueryHead = (void *) pcmd->host_scribble; + pcmd->host_scribble = NULL; + if (!pACB->pQueryHead) + pACB->pQueryTail = NULL; + pACB->QueryCnt--; + return pcmd; +} + + +/* Return next free SRB */ +static __inline__ struct ScsiReqBlk *DC395x_Free_get(struct AdapterCtlBlk + *pACB) +{ + struct ScsiReqBlk *pSRB; + + /*DC395x_Free_integrity (pACB); */ + pSRB = pACB->pFreeSRB; + if (!pSRB) + printk(DC395X_NAME ": Out of Free SRBs :-(\n"); + if (pSRB) { + pACB->pFreeSRB = pSRB->pNextSRB; + pSRB->pNextSRB = NULL; + } + + return pSRB; +} + + +/* Insert SRB oin top of free list */ +static __inline__ void +DC395x_Free_insert(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + DEBUG0(printk(DC395X_NAME ": Free SRB %p\n", pSRB); + ) + pSRB->pNextSRB = pACB->pFreeSRB; + pACB->pFreeSRB = pSRB; +} + + +/* Inserts a SRB to the top of the Waiting list */ +static __inline__ void +DC395x_Waiting_insert(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB) +{ + DEBUG0(printk + (DC395X_NAME ": Insert pSRB %p cmd %li to Waiting\n", pSRB, + pSRB->pcmd->pid); + ) + pSRB->pNextSRB = pDCB->pWaitingSRB; + if (!pDCB->pWaitingSRB) + pDCB->pWaitLast = pSRB; + pDCB->pWaitingSRB = pSRB; + pDCB->WaitSRBCnt++; +} + + +/* Queue SRB to waiting list */ +static __inline__ void +DC395x_Waiting_append(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB) +{ + DEBUG0(printk + (DC395X_NAME ": Append pSRB %p cmd %li to Waiting\n", pSRB, + pSRB->pcmd->pid); + ) + if (pDCB->pWaitingSRB) + pDCB->pWaitLast->pNextSRB = pSRB; + else + pDCB->pWaitingSRB = pSRB; + + pDCB->pWaitLast = pSRB; + /* No next one in waiting list */ + pSRB->pNextSRB = NULL; + pDCB->WaitSRBCnt++; + /*pDCB->pDCBACB->CmdInQ++; */ +} + + +static __inline__ void +DC395x_Going_append(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB) +{ + DEBUG0(printk(DC395X_NAME ": Append SRB %p to Going\n", pSRB); + ) + /* Append to the list of Going commands */ + if (pDCB->pGoingSRB) + pDCB->pGoingLast->pNextSRB = pSRB; + else + pDCB->pGoingSRB = pSRB; + + pDCB->pGoingLast = pSRB; + /* No next one in sent list */ + pSRB->pNextSRB = NULL; + pDCB->GoingSRBCnt++; +} + + +/* Find predecessor SRB */ +inline static struct ScsiReqBlk *DC395x_find_SRBpre(struct ScsiReqBlk + *pSRB, + struct ScsiReqBlk + *start) +{ + struct ScsiReqBlk *srb = start; + if (!start) + return 0; + do { + if (srb->pNextSRB == pSRB) + return srb; + srb = srb->pNextSRB; + } while (srb && srb != start); + return 0; +} + + +/* Remove SRB from SRB queue */ +inline static struct ScsiReqBlk *DC395x_rmv_SRB(struct ScsiReqBlk *pSRB, + struct ScsiReqBlk *pre) +{ + if (pre->pNextSRB != pSRB) + pre = DC395x_find_SRBpre(pSRB, pre); + if (!pre) { + printk(DC395X_NAME + ": Internal ERROR: SRB to rmv not found in Q!\n"); + return 0; + } + pre->pNextSRB = pSRB->pNextSRB; + /*pSRB->pNextSRB = 0; */ + return pre; +} + + +/* Remove SRB from Going queue */ +static void +DC395x_Going_remove(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB, + struct ScsiReqBlk *hint) +{ + struct ScsiReqBlk *pre = 0; + DEBUG0(printk(DC395X_NAME ": Remove SRB %p from Going\n", pSRB); + ) + if (!pSRB) + printk(DC395X_NAME ": Going_remove %p!\n", pSRB); + if (pSRB == pDCB->pGoingSRB) + pDCB->pGoingSRB = pSRB->pNextSRB; + else if (hint && hint->pNextSRB == pSRB) + pre = DC395x_rmv_SRB(pSRB, hint); + else + pre = DC395x_rmv_SRB(pSRB, pDCB->pGoingSRB); + if (pSRB == pDCB->pGoingLast) + pDCB->pGoingLast = pre; + pDCB->GoingSRBCnt--; +} + + +/* Remove SRB from Waiting queue */ +static void +DC395x_Waiting_remove(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB, + struct ScsiReqBlk *hint) +{ + struct ScsiReqBlk *pre = 0; + DEBUG0(printk(DC395X_NAME ": Remove SRB %p from Waiting\n", pSRB); + ) + if (!pSRB) + printk(DC395X_NAME ": Waiting_remove %p!\n", pSRB); + if (pSRB == pDCB->pWaitingSRB) + pDCB->pWaitingSRB = pSRB->pNextSRB; + else if (hint && hint->pNextSRB == pSRB) + pre = DC395x_rmv_SRB(pSRB, hint); + else + pre = DC395x_rmv_SRB(pSRB, pDCB->pWaitingSRB); + if (pSRB == pDCB->pWaitLast) + pDCB->pWaitLast = pre; + pDCB->WaitSRBCnt--; +} + + +/* Moves SRB from Going list to the top of Waiting list */ +static void +DC395x_Going_to_Waiting(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB) +{ + DEBUG0(printk + (KERN_INFO DC395X_NAME + ": Going_to_Waiting (SRB %p) pid = %li\n", pSRB, + pSRB->pcmd->pid); + ) + /* Remove SRB from Going */ + DC395x_Going_remove(pDCB, pSRB, 0); + TRACEPRINTF("GtW *"); + /* Insert on top of Waiting */ + DC395x_Waiting_insert(pDCB, pSRB); + /* Tag Mask must be freed elsewhere ! (KG, 99/06/18) */ +} + + +/* Moves first SRB from Waiting list to Going list */ +static __inline__ void +DC395x_Waiting_to_Going(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB) +{ + /* Remove from waiting list */ + DEBUG0(printk + (DC395X_NAME ": Remove SRB %p from head of Waiting\n", + pSRB); + ) + DC395x_Waiting_remove(pDCB, pSRB, 0); + TRACEPRINTF("WtG *"); + DC395x_Going_append(pDCB, pSRB); +} + + +void DC395x_waiting_timed_out(unsigned long ptr); +/* Sets the timer to wake us up */ +static void +DC395x_waiting_timer(struct AdapterCtlBlk *pACB, unsigned long to) +{ + if (timer_pending(&pACB->Waiting_Timer)) + return; + init_timer(&pACB->Waiting_Timer); + pACB->Waiting_Timer.function = DC395x_waiting_timed_out; + pACB->Waiting_Timer.data = (unsigned long) pACB; + if (time_before + (jiffies + to, pACB->pScsiHost->last_reset - HZ / 2)) + pACB->Waiting_Timer.expires = + pACB->pScsiHost->last_reset - HZ / 2 + 1; + else + pACB->Waiting_Timer.expires = jiffies + to + 1; + add_timer(&pACB->Waiting_Timer); +} + + +/* Send the next command from the waiting list to the bus */ +void DC395x_Waiting_process(struct AdapterCtlBlk *pACB) +{ + struct DeviceCtlBlk *ptr; + struct DeviceCtlBlk *ptr1; + struct ScsiReqBlk *pSRB; + + if ((pACB->pActiveDCB) + || (pACB->ACBFlag & (RESET_DETECT + RESET_DONE + RESET_DEV))) + return; + if (timer_pending(&pACB->Waiting_Timer)) + del_timer(&pACB->Waiting_Timer); + ptr = pACB->pDCBRunRobin; + if (!ptr) { /* This can happen! */ + ptr = pACB->pLinkDCB; + pACB->pDCBRunRobin = ptr; + } + ptr1 = ptr; + if (!ptr1) + return; + do { + /* Make sure, the next another device gets scheduled ... */ + pACB->pDCBRunRobin = ptr1->pNextDCB; + if (!(pSRB = ptr1->pWaitingSRB) + || (ptr1->MaxCommand <= ptr1->GoingSRBCnt)) + ptr1 = ptr1->pNextDCB; + else { + /* Try to send to the bus */ + if (!DC395x_StartSCSI(pACB, ptr1, pSRB)) + DC395x_Waiting_to_Going(ptr1, pSRB); + else + DC395x_waiting_timer(pACB, HZ / 50); + break; + } + } while (ptr1 != ptr); + return; +} + + +/* Wake up waiting queue */ +void DC395x_waiting_timed_out(unsigned long ptr) +{ + unsigned long flags; + struct AdapterCtlBlk *pACB = (struct AdapterCtlBlk *) ptr; +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME ": Debug: Waiting queue woken up by timer.\n"); +#endif + DC395x_LOCK_IO(pACB->pScsiHost); + DC395x_Waiting_process(pACB); + DC395x_UNLOCK_IO(pACB->pScsiHost); +} + + +/* Get the DCB for a given ID/LUN combination */ +static inline struct DeviceCtlBlk *DC395x_findDCB(struct AdapterCtlBlk + *pACB, u8 id, u8 lun) +{ + return pACB->children[id][lun]; +} + + +/*********************************************************************** + * Function: static void DC395x_SendSRB (struct AdapterCtlBlk* pACB, struct ScsiReqBlk* pSRB) + * + * Purpose: Send SCSI Request Block (pSRB) to adapter (pACB) + * + * DC395x_queue_command + * DC395x_Waiting_process + * + ***********************************************************************/ +static void +DC395x_SendSRB(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + struct DeviceCtlBlk *pDCB; + + pDCB = pSRB->pSRBDCB; + if ((pDCB->MaxCommand <= pDCB->GoingSRBCnt) || (pACB->pActiveDCB) + || (pACB->ACBFlag & (RESET_DETECT + RESET_DONE + RESET_DEV))) { + DC395x_Waiting_append(pDCB, pSRB); + DC395x_Waiting_process(pACB); + return; + } +#if 0 + if (pDCB->pWaitingSRB) { + DC395x_Waiting_append(pDCB, pSRB); + /* pSRB = GetWaitingSRB(pDCB); *//* non-existent */ + pSRB = pDCB->pWaitingSRB; + /* Remove from waiting list */ + pDCB->pWaitingSRB = pSRB->pNextSRB; + pSRB->pNextSRB = NULL; + if (!pDCB->pWaitingSRB) + pDCB->pWaitLast = NULL; + } +#endif + + if (!DC395x_StartSCSI(pACB, pDCB, pSRB)) + DC395x_Going_append(pDCB, pSRB); + else { + DC395x_Waiting_insert(pDCB, pSRB); + DC395x_waiting_timer(pACB, HZ / 50); + } +} + + +/* + ********************************************************************* + * + * Function: static void DC395x_BuildSRB (Scsi_Cmd *pcmd, struct DeviceCtlBlk* pDCB, struct ScsiReqBlk* pSRB) + * + * Purpose: Prepare SRB for being sent to Device DCB w/ command *pcmd + * + ********************************************************************* + */ +static void +DC395x_BuildSRB(Scsi_Cmnd * pcmd, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB) +{ + int i, max; + struct SGentry *sgp; + struct scatterlist *sl; + u32 request_size; + int dir; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_BuildSRB..............\n "); +#endif + /*memset (pSRB, 0, sizeof (struct ScsiReqBlk)); */ + pSRB->pSRBDCB = pDCB; + pSRB->pcmd = pcmd; + /* Find out about direction */ + dir = scsi_to_pci_dma_dir(pcmd->sc_data_direction); + + if (pcmd->use_sg && dir != PCI_DMA_NONE) { + unsigned int len = 0; + /* TODO: In case usg_sg and the no of segments differ, things + * will probably go wrong. */ + max = pSRB->SRBSGCount = + pci_map_sg(pDCB->pDCBACB->pdev, + (struct scatterlist *) pcmd->request_buffer, + pcmd->use_sg, dir); + sgp = pSRB->SegmentX; + request_size = pcmd->request_bufflen; +#ifdef DC395x_SGPARANOIA + printk(KERN_INFO DC395X_NAME + ": BuildSRB: Bufflen = %d, buffer = %p, use_sg = %d\n", + pcmd->request_bufflen, pcmd->request_buffer, + pcmd->use_sg); + printk(KERN_INFO DC395X_NAME + ": Mapped %i Segments to %i\n", pcmd->use_sg, + pSRB->SRBSGCount); +#endif + sl = (struct scatterlist *) pcmd->request_buffer; + + pSRB->virt_addr = page_address(sl->page); + for (i = 0; i < max; i++) { + u32 busaddr = (u32) sg_dma_address(&sl[i]); + u32 seglen = (u32) sl[i].length; + sgp[i].address = busaddr; + sgp[i].length = seglen; + len += seglen; +#ifdef DC395x_SGPARANOIA + printk(KERN_INFO DC395X_NAME + ": Setting up sgp %d, address = 0x%08x, length = %d, tot len = %d\n", + i, busaddr, seglen, len); +#endif + } + sgp += max - 1; + /* Fixup for last buffer too big as it is allocated on even page boundaries */ + if (len > request_size) { +#if defined(DC395x_DEBUG_KG) || defined (DC395x_SGPARANOIA) + printk(KERN_INFO DC395X_NAME + ": Fixup SG total length: %d->%d, last seg %d->%d\n", + len, request_size, sgp->length, + sgp->length - (len - request_size)); +#endif + sgp->length -= (len - request_size); + len = request_size; + } + /* WIDE padding */ + if (pDCB->SyncPeriod & WIDE_SYNC && len % 2) { + len++; + sgp->length++; + } + pSRB->SRBTotalXferLength = len; /*? */ + /* Hopefully this does not cross a page boundary ... */ + pSRB->SRBSGBusAddr = + pci_map_single(pDCB->pDCBACB->pdev, pSRB->SegmentX, + sizeof(struct SGentry) * + DC395x_MAX_SG_LISTENTRY, + PCI_DMA_TODEVICE); +#ifdef DC395x_SGPARANOIA + printk(DC395X_NAME + ": Map SG descriptor list %p (%05x) to %08x\n", + pSRB->SegmentX, + sizeof(struct SGentry) * DC395x_MAX_SG_LISTENTRY, + pSRB->SRBSGBusAddr); +#endif + } else { + if (pcmd->request_buffer && dir != PCI_DMA_NONE) { + u32 len = pcmd->request_bufflen; /* Actual request size */ + pSRB->SRBSGCount = 1; + pSRB->SegmentX[0].address = + pci_map_single(pDCB->pDCBACB->pdev, + pcmd->request_buffer, len, dir); + /* WIDE padding */ + if (pDCB->SyncPeriod & WIDE_SYNC && len % 2) + len++; + pSRB->SegmentX[0].length = len; + pSRB->SRBTotalXferLength = len; + pSRB->virt_addr = pcmd->request_buffer; + pSRB->SRBSGBusAddr = 0; +#ifdef DC395x_SGPARANOIA + printk(KERN_INFO DC395X_NAME + ": BuildSRB: len = %d, buffer = %p, use_sg = %d, map %08x\n", + len, pcmd->request_buffer, pcmd->use_sg, + pSRB->SegmentX[0].address); +#endif + } else { + pSRB->SRBSGCount = 0; + pSRB->SRBTotalXferLength = 0; + pSRB->SRBSGBusAddr = 0; + pSRB->virt_addr = 0; +#ifdef DC395x_SGPARANOIA + printk(KERN_INFO DC395X_NAME + ": BuildSRB: buflen = %d, buffer = %p, use_sg = %d, NOMAP %08x\n", + pcmd->bufflen, pcmd->request_buffer, + pcmd->use_sg, pSRB->SegmentX[0].address); +#endif + } + } + + pSRB->SRBSGIndex = 0; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + pSRB->MsgCnt = 0; + pSRB->SRBStatus = 0; + pSRB->SRBFlag = 0; + pSRB->SRBState = 0; + pSRB->RetryCnt = 0; + +#if DC395x_SGPARANOIA + if ((unsigned long) pSRB->debugtrace & (DEBUGTRACEBUFSZ - 1)) { + printk(DC395X_NAME + ": SRB %i (%p): debugtrace %p corrupt!\n", + (pSRB - + pDCB->pDCBACB->SRB_array) / + sizeof(struct ScsiReqBlk), pSRB, pSRB->debugtrace); + } +#endif +#ifdef DC395x_TRACEDEBUG + pSRB->debugpos = 0; + pSRB->debugtrace = 0; +#endif + TRACEPRINTF("pid %li(%li):%02x %02x..(%i-%i) *", pcmd->pid, + jiffies, pcmd->cmnd[0], pcmd->cmnd[1], + pcmd->device->id, pcmd->device->lun); + pSRB->TagNumber = TAG_NONE; + + pSRB->ScsiPhase = PH_BUS_FREE; /* initial phase */ + pSRB->EndMessage = 0; + return; +} + + +/* Put cmnd from Query to Waiting list and send next Waiting cmnd */ +static void DC395x_Query_to_Waiting(struct AdapterCtlBlk *pACB) +{ + Scsi_Cmnd *pcmd; + struct ScsiReqBlk *pSRB; + struct DeviceCtlBlk *pDCB; + + if (pACB->ACBFlag & (RESET_DETECT + RESET_DONE + RESET_DEV)) + return; + + while (pACB->QueryCnt) { + pSRB = DC395x_Free_get(pACB); + if (!pSRB) + return; + pcmd = DC395x_Query_get(pACB); + if (!pcmd) { + DC395x_Free_insert(pACB, pSRB); + return; + } /* should not happen */ + pDCB = + DC395x_findDCB(pACB, pcmd->device->id, + pcmd->device->lun); + if (!pDCB) { + DC395x_Free_insert(pACB, pSRB); + printk(KERN_ERR DC395X_NAME + ": Command in queue to non-existing device!\n"); + pcmd->result = + MK_RES(DRIVER_ERROR, DID_ERROR, 0, 0); + /*DC395x_UNLOCK_ACB_NI; */ + pcmd->done(pcmd); + /*DC395x_LOCK_ACB_NI; */ + } + DC395x_BuildSRB(pcmd, pDCB, pSRB); + DC395x_Waiting_append(pDCB, pSRB); + } +} + + +/*********************************************************************** + * Function : static int DC395x_queue_command (Scsi_Cmnd *cmd, + * void (*done)(Scsi_Cmnd *)) + * + * Purpose : enqueues a SCSI command + * + * Inputs : cmd - SCSI command, done - callback function called on + * completion, with a pointer to the command descriptor. + * + * Returns : (depending on kernel version) + * 2.0.x: always return 0 + * 2.1.x: old model: (use_new_eh_code == 0): like 2.0.x + * new model: return 0 if successful + * return 1 if command cannot be queued (queue full) + * command will be inserted in midlevel queue then ... + * + ***********************************************************************/ +static int +DC395x_queue_command(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) +{ + struct DeviceCtlBlk *pDCB; + struct ScsiReqBlk *pSRB; + struct AdapterCtlBlk *pACB = + (struct AdapterCtlBlk *) cmd->device->host->hostdata; + + + DEBUG0( /* if(pACB->scan_devices) */ + printk(KERN_INFO DC395X_NAME + ": Queue Cmd=%02x,Tgt=%d,LUN=%d (pid=%li)\n", + cmd->cmnd[0], cmd->device->id, + cmd->device->lun, cmd->pid); + ) + + DEBUGRECURSION(if (in_driver++ > NORM_REC_LVL) + printk(DC395X_NAME + ": %i queue_command () recursion? (pid=%li)\n", + in_driver, cmd->pid);) + + /* Assume BAD_TARGET; will be cleared later */ + cmd->result = DID_BAD_TARGET << 16; + + if ((cmd->device->id >= pACB->pScsiHost->max_id) + || (cmd->device->lun >= pACB->pScsiHost->max_lun) + || (cmd->device->lun >31)) { + /* printk (KERN_INFO DC395X_NAME "Ignore target %d lun %d\n", + cmd->device->id, cmd->device->lun); */ + DEBUGRECURSION(in_driver--; + ) + /*return 1; */ + done(cmd); + return 0; + } + + if (!(pACB->DCBmap[cmd->device->id] & (1 << cmd->device->lun))) { + printk(KERN_INFO DC395X_NAME + ": Ignore target %02x lun %02x\n", cmd->device->id, + cmd->device->lun); + /*return 1; */ + DEBUGRECURSION(in_driver--; + ) + done(cmd); + return 0; + } else { + pDCB = + DC395x_findDCB(pACB, cmd->device->id, + cmd->device->lun); + if (!pDCB) { /* should never happen */ + printk(KERN_ERR DC395X_NAME + ": no DCB failed, target %02x lun %02x\n", + cmd->device->id, cmd->device->lun); + printk(DC395X_NAME + ": No DCB in queuecommand (2)!\n"); + DEBUGRECURSION(in_driver--; + ) + return 1; + } + } + + pACB->Cmds++; + cmd->scsi_done = done; + cmd->result = 0; + + DC395x_Query_to_Waiting(pACB); + + if (pACB->QueryCnt) { + /* Unsent commands ? */ + DEBUG0(printk(DC395X_NAME ": QueryCnt != 0\n"); + ) + DC395x_Query_append(cmd, pACB); + DC395x_Waiting_process(pACB); + } else { + if (pDCB->pWaitingSRB) { + pSRB = DC395x_Free_get(pACB); + DEBUG0(if (!pSRB) + printk(DC395X_NAME + ": No free SRB but Waiting\n"); + else + printk(DC395X_NAME + ": Free SRB w/ Waiting\n");) + if (!pSRB) { + DC395x_Query_append(cmd, pACB); + } else { + DC395x_BuildSRB(cmd, pDCB, pSRB); + DC395x_Waiting_append(pDCB, pSRB); + } + DC395x_Waiting_process(pACB); + } else { + pSRB = DC395x_Free_get(pACB); + DEBUG0(if (!pSRB) + printk(DC395X_NAME + ": No free SRB w/o Waiting\n"); + else + printk(DC395X_NAME + ": Free SRB w/o Waiting\n");) + if (!pSRB) { + DC395x_Query_append(cmd, pACB); + DC395x_Waiting_process(pACB); + } else { + DC395x_BuildSRB(cmd, pDCB, pSRB); + DC395x_SendSRB(pACB, pSRB); + } + } + } + + /*DC395x_ACB_LOCK(pACB,acb_flags); */ + DEBUG1(printk + (KERN_DEBUG " ... command (pid %li) queued successfully.\n", + cmd->pid); + ) + DEBUGRECURSION(in_driver--; + ) + return 0; +} + + +/*********************************************************************** + * Function static int DC395x_slave_alloc() + * + * Purpose: Allocate DCB + ***********************************************************************/ +static int DC395x_slave_alloc(struct scsi_device *sdp) +{ + struct AdapterCtlBlk *pACB; + struct DeviceCtlBlk *dummy; + + pACB = (struct AdapterCtlBlk *) sdp->host->hostdata; + + DC395x_initDCB(pACB, &dummy, sdp->id, sdp->lun); + + return dummy ? 0 : -ENOMEM; +} + + +static void DC395x_slave_destroy(struct scsi_device *sdp) +{ + struct AdapterCtlBlk *ACB; + struct DeviceCtlBlk *DCB; + + ACB = (struct AdapterCtlBlk *) sdp->host->hostdata; + DCB = DC395x_findDCB(ACB, sdp->id, sdp->lun); + + DC395x_remove_dev(ACB, DCB); +} + + +/*********************************************************************** + * Function : static void DC395_updateDCB() + * + * Purpose : Set the configuration dependent DCB parameters + ***********************************************************************/ +void +DC395x_updateDCB(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB) +{ + /* Prevent disconnection of narrow devices if this_id > 7 */ + if (!(pDCB->DevMode & NTC_DO_WIDE_NEGO) + && pACB->pScsiHost->this_id > 7) + pDCB->DevMode &= ~NTC_DO_DISCONNECT; + + /* TagQ w/o DisCn is impossible */ + if (!(pDCB->DevMode & NTC_DO_DISCONNECT)) + pDCB->DevMode &= ~NTC_DO_TAG_QUEUEING; + pDCB->IdentifyMsg = + IDENTIFY((pDCB->DevMode & NTC_DO_DISCONNECT), pDCB->TargetLUN); + + pDCB->SyncMode &= + EN_TAG_QUEUEING | SYNC_NEGO_DONE | WIDE_NEGO_DONE + /*| EN_ATN_STOP */ ; + if (pDCB->DevMode & NTC_DO_TAG_QUEUEING) { + if (pDCB->SyncMode & EN_TAG_QUEUEING) + pDCB->MaxCommand = pACB->TagMaxNum; + } else { + pDCB->SyncMode &= ~EN_TAG_QUEUEING; + pDCB->MaxCommand = 1; + } + + if (pDCB->DevMode & NTC_DO_SYNC_NEGO) + pDCB->SyncMode |= SYNC_NEGO_ENABLE; + else { + pDCB->SyncMode &= ~(SYNC_NEGO_DONE | SYNC_NEGO_ENABLE); + pDCB->SyncOffset &= ~0x0f; + } + + if (pDCB->DevMode & NTC_DO_WIDE_NEGO + && pACB->Config & HCC_WIDE_CARD) + pDCB->SyncMode |= WIDE_NEGO_ENABLE; + else { + pDCB->SyncMode &= ~(WIDE_NEGO_DONE | WIDE_NEGO_ENABLE); + pDCB->SyncPeriod &= ~WIDE_SYNC; + } + /*if (! (pDCB->DevMode & EN_DISCONNECT_)) pDCB->SyncMode &= ~EN_ATN_STOP; */ +} + + +/* + ********************************************************************* + * + * Function : DC395x_bios_param + * Description: Return the disk geometry for the given SCSI device. + ********************************************************************* + */ +static int +DC395x_bios_param(struct scsi_device *sdev, struct block_device *bdev, + sector_t capacity, int *info) +{ +#ifdef CONFIG_SCSI_DC395x_TRMS1040_TRADMAP + int heads, sectors, cylinders; + struct AdapterCtlBlk *pACB; + int size = capacity; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ":DC395x_bios_param..............\n "); +#endif + pACB = (struct AdapterCtlBlk *) sdev->host->hostdata; + heads = 64; + sectors = 32; + cylinders = size / (heads * sectors); + + if ((pACB->Gmode2 & NAC_GREATER_1G) && (cylinders > 1024)) { + heads = 255; + sectors = 63; + cylinders = size / (heads * sectors); + } + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + return 0; +#else + return scsicam_bios_param(bdev, capacity, info); +#endif +} + + +/* + * DC395x register dump + */ +void +DC395x_dumpinfo(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB) +{ + u16 pstat; + struct pci_dev *pdev = pACB->pdev; + pci_read_config_word(pdev, PCI_STATUS, &pstat); + if (!pDCB) + pDCB = pACB->pActiveDCB; + if (!pSRB && pDCB) + pSRB = pDCB->pActiveSRB; + if (pSRB) { + if (!(pSRB->pcmd)) + printk(DC395X_NAME + ": dump: SRB %p: cmd %p OOOPS!\n", pSRB, + pSRB->pcmd); + else + printk(DC395X_NAME + ": dump: SRB %p: cmd %p pid %li: %02x (%02i-%i)\n", + pSRB, pSRB->pcmd, pSRB->pcmd->pid, + pSRB->pcmd->cmnd[0], pSRB->pcmd->device->id, + pSRB->pcmd->device->lun); + printk(" SGList %p Cnt %i Idx %i Len %i\n", + pSRB->SegmentX, pSRB->SRBSGCount, pSRB->SRBSGIndex, + pSRB->SRBTotalXferLength); + printk + (" State %04x Status %02x Phase %02x (%sconn.)\n", + pSRB->SRBState, pSRB->SRBStatus, pSRB->ScsiPhase, + (pACB->pActiveDCB) ? "" : "not"); + TRACEOUT(" %s\n", pSRB->debugtrace); + } + printk(DC395X_NAME ": dump: SCSI block\n"); + printk + (" Status %04x FIFOCnt %02x Signals %02x IRQStat %02x\n", + DC395x_read16(TRM_S1040_SCSI_STATUS), + DC395x_read8(TRM_S1040_SCSI_FIFOCNT), + DC395x_read8(TRM_S1040_SCSI_SIGNAL), + DC395x_read8(TRM_S1040_SCSI_INTSTATUS)); + printk + (" Sync %02x Target %02x RSelID %02x SCSICtr %08x\n", + DC395x_read8(TRM_S1040_SCSI_SYNC), + DC395x_read8(TRM_S1040_SCSI_TARGETID), + DC395x_read8(TRM_S1040_SCSI_IDMSG), + DC395x_read32(TRM_S1040_SCSI_COUNTER)); + printk + (" IRQEn %02x Config %04x Cfg2 %02x Cmd %02x SelTO %02x\n", + DC395x_read8(TRM_S1040_SCSI_INTEN), + DC395x_read16(TRM_S1040_SCSI_CONFIG0), + DC395x_read8(TRM_S1040_SCSI_CONFIG2), + DC395x_read8(TRM_S1040_SCSI_COMMAND), + DC395x_read8(TRM_S1040_SCSI_TIMEOUT)); + printk(DC395X_NAME ": dump: DMA block\n"); + printk + (" Cmd %04x FIFOCnt %02x FStat %02x IRQStat %02x IRQEn %02x Cfg %04x\n", + DC395x_read16(TRM_S1040_DMA_COMMAND), + DC395x_read8(TRM_S1040_DMA_FIFOCNT), + DC395x_read8(TRM_S1040_DMA_FIFOSTAT), + DC395x_read8(TRM_S1040_DMA_STATUS), + DC395x_read8(TRM_S1040_DMA_INTEN), + DC395x_read16(TRM_S1040_DMA_CONFIG)); + printk(" TCtr %08x CTCtr %08x Addr %08x%08x\n", + DC395x_read32(TRM_S1040_DMA_XCNT), + DC395x_read32(TRM_S1040_DMA_CXCNT), + DC395x_read32(TRM_S1040_DMA_XHIGHADDR), + DC395x_read32(TRM_S1040_DMA_XLOWADDR)); + printk(DC395X_NAME + ": dump: Misc: GCtrl %02x GStat %02x GTmr %02x\n", + DC395x_read8(TRM_S1040_GEN_CONTROL), + DC395x_read8(TRM_S1040_GEN_STATUS), + DC395x_read8(TRM_S1040_GEN_TIMER)); + printk(DC395X_NAME ": dump: PCI Status %04x\n", pstat); + + +} + + +static inline void DC395x_clrfifo(struct AdapterCtlBlk *pACB, char *txt) +{ +#ifdef DC395x_DEBUGFIFO + u8 lines = DC395x_read8(TRM_S1040_SCSI_SIGNAL); + u8 fifocnt = DC395x_read8(TRM_S1040_SCSI_FIFOCNT); + if (!(fifocnt & 0x40)) + printk(DC395X_NAME + ": Clr FIFO (%i bytes) on phase %02x in %s\n", + fifocnt & 0x3f, lines, txt); +#endif + if (pACB->pActiveDCB && pACB->pActiveDCB->pActiveSRB) { + struct ScsiReqBlk *pSRB = pACB->pActiveDCB->pActiveSRB; + TRACEPRINTF("#*"); + } + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); +} + + +/* + ******************************************************************** + * + * DC395x_reset DC395x_ScsiRstDetect + * + ******************************************************************** + */ +static void DC395x_ResetDevParam(struct AdapterCtlBlk *pACB) +{ + struct DeviceCtlBlk *pDCB; + struct DeviceCtlBlk *pDCBTemp; + struct NvRamType *eeprom; + u8 PeriodIndex; + u16 index; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_ResetDevParam..............\n "); +#endif + pDCB = pACB->pLinkDCB; + if (pDCB == NULL) + return; + + pDCBTemp = pDCB; + do { + pDCB->SyncMode &= ~(SYNC_NEGO_DONE + WIDE_NEGO_DONE); + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + index = pACB->AdapterIndex; + eeprom = &dc395x_trm_eepromBuf[index]; + + pDCB->DevMode = + eeprom->NvramTarget[pDCB->TargetID].NvmTarCfg0; + /*pDCB->AdpMode = eeprom->NvramChannelCfg; */ + PeriodIndex = + eeprom->NvramTarget[pDCB->TargetID]. + NvmTarPeriod & 0x07; + pDCB->MinNegoPeriod = dc395x_clock_period[PeriodIndex]; + if (!(pDCB->DevMode & NTC_DO_WIDE_NEGO) + || !(pACB->Config & HCC_WIDE_CARD)) + pDCB->SyncMode &= ~WIDE_NEGO_ENABLE; + + pDCB = pDCB->pNextDCB; + } + while (pDCBTemp != pDCB && pDCB != NULL); +} + + +/* + ********************************************************************* + * Function : int DC395x_eh_bus_reset(Scsi_Cmnd *cmd) + * Purpose : perform a hard reset on the SCSI bus + * Inputs : cmd - some command for this host (for fetching hooks) + * Returns : SUCCESS (0x2002) on success, else FAILED (0x2003). + ********************************************************************* + */ +static int DC395x_eh_bus_reset(Scsi_Cmnd * cmd) +{ + struct AdapterCtlBlk *pACB; + /*u32 acb_flags=0; */ + + printk(KERN_INFO DC395X_NAME ": reset requested!\n"); + pACB = (struct AdapterCtlBlk *) cmd->device->host->hostdata; + /* mid level guarantees no recursion */ + /*DC395x_ACB_LOCK(pACB,acb_flags); */ + + if (timer_pending(&pACB->Waiting_Timer)) + del_timer(&pACB->Waiting_Timer); + + /* + * disable interrupt + */ + DC395x_write8(TRM_S1040_DMA_INTEN, 0x00); + DC395x_write8(TRM_S1040_SCSI_INTEN, 0x00); + DC395x_write8(TRM_S1040_SCSI_CONTROL, DO_RSTMODULE); + DC395x_write8(TRM_S1040_DMA_CONTROL, DMARESETMODULE); + + DC395x_ResetSCSIBus(pACB); + udelay(500); + + /* We may be in serious trouble. Wait some seconds */ + pACB->pScsiHost->last_reset = + jiffies + 3 * HZ / 2 + + HZ * dc395x_trm_eepromBuf[pACB->AdapterIndex].NvramDelayTime; + + /* + * re-enable interrupt + */ + /* Clear SCSI FIFO */ + DC395x_write8(TRM_S1040_DMA_CONTROL, CLRXFIFO); + DC395x_clrfifo(pACB, "reset"); + /* Delete pending IRQ */ + DC395x_read8(TRM_S1040_SCSI_INTSTATUS); + DC395x_basic_config(pACB); + + DC395x_ResetDevParam(pACB); + DC395x_DoingSRB_Done(pACB, DID_RESET, cmd, 0); + + pACB->pActiveDCB = NULL; + + pACB->ACBFlag = 0; /* RESET_DETECT, RESET_DONE ,RESET_DEV */ + DC395x_Waiting_process(pACB); + + /*DC395x_ACB_LOCK(pACB,acb_flags); */ + return SUCCESS; +} + + +/* + ********************************************************************* + * Function : int DC395x_eh_abort(Scsi_Cmnd *cmd) + * Purpose : abort an errant SCSI command + * Inputs : cmd - command to be aborted + * Returns : SUCCESS (0x2002) on success, else FAILED (0x2003). + ********************************************************************* + */ +static int DC395x_eh_abort(Scsi_Cmnd * cmd) +{ + /* + * Look into our command queues: If it has not been sent already, + * we remove it and return success. Otherwise fail. + * First check the Query Queues, then the Waiting ones + */ + struct AdapterCtlBlk *pACB = + (struct AdapterCtlBlk *) cmd->device->host->hostdata; + struct DeviceCtlBlk *pDCB; + struct ScsiReqBlk *pSRB; + int cnt = pACB->QueryCnt; + Scsi_Cmnd *pcmd; + Scsi_Cmnd *last = 0; + printk(DC395X_NAME ": DC395x_eh_abort: cmd %p (pid %li, %02i-%i) ", + cmd, cmd->pid, cmd->device->id, cmd->device->lun); + for (pcmd = pACB->pQueryHead; cnt--; + last = pcmd, pcmd = (Scsi_Cmnd *) pcmd->host_scribble) { + if (pcmd == cmd) { + /* unqueue */ + if (last) { + last->host_scribble = pcmd->host_scribble; + if (!pcmd->host_scribble) + pACB->pQueryTail = last; + } else { + pACB->pQueryHead = + (Scsi_Cmnd *) pcmd->host_scribble; + if (!pcmd->host_scribble) + pACB->pQueryTail = 0; + } + printk("found in Query queue :-)\n"); + pACB->QueryCnt--; + cmd->result = DID_ABORT << 16; + return SUCCESS; + } + } + pDCB = DC395x_findDCB(pACB, cmd->device->id, cmd->device->lun); + if (!pDCB) { + printk("no DCB !\n"); + return FAILED; + } + + pSRB = DC395x_find_cmd(cmd, pDCB->pWaitingSRB); + if (pSRB) { + DC395x_Waiting_remove(pDCB, pSRB, 0); + DC395x_pci_unmap_sense(pACB, pSRB); + DC395x_pci_unmap(pACB, pSRB); + DC395x_freetag(pDCB, pSRB); + DC395x_Free_insert(pACB, pSRB); + printk("found in waiting queue :-)\n"); + cmd->result = DID_ABORT << 16; + return SUCCESS; + } + pSRB = DC395x_find_cmd(cmd, pDCB->pGoingSRB); + if (pSRB) + printk("found in going queue :-(\n"); + else + printk("not found!\n"); + return FAILED; +} + + +/* + * TODO (new EH): + * int (*eh_device_reset_handler)(Scsi_Cmnd *); + * int (*eh_host_reset_handler)(Scsi_Cmnd *); + * + * remove Query Queue + * investigate whether/which commands need to be ffed back to mid-layer + * in _eh_reset() + */ + + +/* SDTR */ +static void +DC395x_Build_SDTR(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB) +{ + u8 *ptr = pSRB->MsgOutBuf + pSRB->MsgCnt; + if (pSRB->MsgCnt > 1) { + printk(DC395X_NAME + ": Build_SDTR: MsgOutBuf BUSY (%i: %02x %02x)\n", + pSRB->MsgCnt, pSRB->MsgOutBuf[0], + pSRB->MsgOutBuf[1]); + return; + } + if (!(pDCB->DevMode & NTC_DO_SYNC_NEGO)) { + pDCB->SyncOffset = 0; + pDCB->MinNegoPeriod = 200 >> 2; + } else if (pDCB->SyncOffset == 0) + pDCB->SyncOffset = SYNC_NEGO_OFFSET; + + *ptr++ = MSG_EXTENDED; /* (01h) */ + *ptr++ = 3; /* length */ + *ptr++ = EXTENDED_SDTR; /* (01h) */ + *ptr++ = pDCB->MinNegoPeriod; /* Transfer period (in 4ns) */ + *ptr++ = pDCB->SyncOffset; /* Transfer period (max. REQ/ACK dist) */ + pSRB->MsgCnt += 5; + pSRB->SRBState |= SRB_DO_SYNC_NEGO; + TRACEPRINTF("S *"); +} + + +/* SDTR */ +static void +DC395x_Build_WDTR(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB) +{ + u8 wide = + ((pDCB->DevMode & NTC_DO_WIDE_NEGO) & (pACB-> + Config & HCC_WIDE_CARD)) + ? 1 : 0; + u8 *ptr = pSRB->MsgOutBuf + pSRB->MsgCnt; + if (pSRB->MsgCnt > 1) { + printk(DC395X_NAME + ": Build_WDTR: MsgOutBuf BUSY (%i: %02x %02x)\n", + pSRB->MsgCnt, pSRB->MsgOutBuf[0], + pSRB->MsgOutBuf[1]); + return; + } + *ptr++ = MSG_EXTENDED; /* (01h) */ + *ptr++ = 2; /* length */ + *ptr++ = EXTENDED_WDTR; /* (03h) */ + *ptr++ = wide; + pSRB->MsgCnt += 4; + pSRB->SRBState |= SRB_DO_WIDE_NEGO; + TRACEPRINTF("W *"); +} + + +#if 0 +/* Timer to work around chip flaw: When selecting and the bus is + * busy, we sometimes miss a Selection timeout IRQ */ +void DC395x_selection_timeout_missed(unsigned long ptr); +/* Sets the timer to wake us up */ +static void DC395x_selto_timer(struct AdapterCtlBlk *pACB) +{ + if (timer_pending(&pACB->SelTO_Timer)) + return; + init_timer(&pACB->SelTO_Timer); + pACB->SelTO_Timer.function = DC395x_selection_timeout_missed; + pACB->SelTO_Timer.data = (unsigned long) pACB; + if (time_before + (jiffies + HZ, pACB->pScsiHost->last_reset + HZ / 2)) + pACB->SelTO_Timer.expires = + pACB->pScsiHost->last_reset + HZ / 2 + 1; + else + pACB->SelTO_Timer.expires = jiffies + HZ + 1; + add_timer(&pACB->SelTO_Timer); +} + + +void DC395x_selection_timeout_missed(unsigned long ptr) +{ + unsigned long flags; + struct AdapterCtlBlk *pACB = (struct AdapterCtlBlk *) ptr; + struct ScsiReqBlk *pSRB; + printk(DC395X_NAME ": Debug: Chip forgot to produce SelTO IRQ!\n"); + if (!pACB->pActiveDCB || !pACB->pActiveDCB->pActiveSRB) { + printk(DC395X_NAME ": ... but no cmd pending? Oops!\n"); + return; + } + DC395x_LOCK_IO(pACB->pScsiHost); + pSRB = pACB->pActiveDCB->pActiveSRB; + TRACEPRINTF("N/TO *"); + DC395x_Disconnect(pACB); + DC395x_UNLOCK_IO(pACB->pScsiHost); +} +#endif + + +/* + * scsiio + * DC395x_DoWaitingSRB DC395x_SRBdone + * DC395x_SendSRB DC395x_RequestSense + */ +u8 +DC395x_StartSCSI(struct AdapterCtlBlk * pACB, struct DeviceCtlBlk * pDCB, + struct ScsiReqBlk * pSRB) +{ + u16 s_stat2, return_code; + u8 s_stat, scsicommand, i, identify_message; + u8 *ptr; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_StartSCSI..............\n "); +#endif + pSRB->TagNumber = TAG_NONE; /* pACB->TagMaxNum: had error read in eeprom */ + + s_stat = DC395x_read8(TRM_S1040_SCSI_SIGNAL); + s_stat2 = 0; + s_stat2 = DC395x_read16(TRM_S1040_SCSI_STATUS); + TRACEPRINTF("Start %02x *", s_stat); +#if 1 + if (s_stat & 0x20 /* s_stat2 & 0x02000 */ ) { +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Debug: StartSCSI: pid %li(%02i-%i): BUSY %02x %04x\n", + pSRB->pcmd->pid, pDCB->TargetID, pDCB->TargetLUN, + s_stat, s_stat2); +#endif + /* + * Try anyway? + * + * We could, BUT: Sometimes the TRM_S1040 misses to produce a Selection + * Timeout, a Disconnect or a Reselction IRQ, so we would be screwed! + * (This is likely to be a bug in the hardware. Obviously, most people + * only have one initiator per SCSI bus.) + * Instead let this fail and have the timer make sure the command is + * tried again after a short time + */ + TRACEPRINTF("^*"); + /*DC395x_selto_timer (pACB); */ + /*DC395x_monitor_next_IRQ = 1; */ + return 1; + } +#endif + if (pACB->pActiveDCB) { + printk(DC395X_NAME + ": We try to start a SCSI command (%li)!\n", + pSRB->pcmd->pid); + printk(DC395X_NAME + ": While another one (%li) is active!!\n", + (pACB->pActiveDCB->pActiveSRB ? pACB->pActiveDCB-> + pActiveSRB->pcmd->pid : 0)); + TRACEOUT(" %s\n", pSRB->debugtrace); + if (pACB->pActiveDCB->pActiveSRB) + TRACEOUT(" %s\n", + pACB->pActiveDCB->pActiveSRB->debugtrace); + return 1; + } + if (DC395x_read16(TRM_S1040_SCSI_STATUS) & SCSIINTERRUPT) { +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Debug: StartSCSI failed (busy) for pid %li(%02i-%i)\n", + pSRB->pcmd->pid, pDCB->TargetID, pDCB->TargetLUN); +#endif + TRACEPRINTF("°*"); + return 1; + } + /* Allow starting of SCSI commands half a second before we allow the mid-level + * to queue them again after a reset */ + if (time_before(jiffies, pACB->pScsiHost->last_reset - HZ / 2)) { +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": We were just reset and don't accept commands yet!\n"); +#endif + return 1; + } + + /* Flush FIFO */ + DC395x_clrfifo(pACB, "Start"); + DC395x_write8(TRM_S1040_SCSI_HOSTID, pACB->pScsiHost->this_id); + DC395x_write8(TRM_S1040_SCSI_TARGETID, pDCB->TargetID); + DC395x_write8(TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod); + DC395x_write8(TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset); + pSRB->ScsiPhase = PH_BUS_FREE; /* initial phase */ + + identify_message = pDCB->IdentifyMsg; + /*DC395x_TRM_write8(TRM_S1040_SCSI_IDMSG, identify_message); */ + /* Don't allow disconnection for AUTO_REQSENSE: Cont.All.Cond.! */ + if (pSRB->SRBFlag & AUTO_REQSENSE) + identify_message &= 0xBF; + + if (((pSRB->pcmd->cmnd[0] == INQUIRY) + || (pSRB->pcmd->cmnd[0] == REQUEST_SENSE) + || (pSRB->SRBFlag & AUTO_REQSENSE)) + && (((pDCB->SyncMode & WIDE_NEGO_ENABLE) + && !(pDCB->SyncMode & WIDE_NEGO_DONE)) + || ((pDCB->SyncMode & SYNC_NEGO_ENABLE) + && !(pDCB->SyncMode & SYNC_NEGO_DONE))) + && (pDCB->TargetLUN == 0)) { + pSRB->MsgOutBuf[0] = identify_message; + pSRB->MsgCnt = 1; + scsicommand = SCMD_SEL_ATNSTOP; + pSRB->SRBState = SRB_MSGOUT; +#ifndef SYNC_FIRST + if (pDCB->SyncMode & WIDE_NEGO_ENABLE + && pDCB->Inquiry7 & SCSI_INQ_WBUS16) { + DC395x_Build_WDTR(pACB, pDCB, pSRB); + goto no_cmd; + } +#endif + if (pDCB->SyncMode & SYNC_NEGO_ENABLE + && pDCB->Inquiry7 & SCSI_INQ_SYNC) { + DC395x_Build_SDTR(pACB, pDCB, pSRB); + goto no_cmd; + } + if (pDCB->SyncMode & WIDE_NEGO_ENABLE + && pDCB->Inquiry7 & SCSI_INQ_WBUS16) { + DC395x_Build_WDTR(pACB, pDCB, pSRB); + goto no_cmd; + } + pSRB->MsgCnt = 0; + } + /* + ** Send identify message + */ + DC395x_write8(TRM_S1040_SCSI_FIFO, identify_message); + + scsicommand = SCMD_SEL_ATN; + pSRB->SRBState = SRB_START_; +#ifndef DC395x_NO_TAGQ + if ((pDCB->SyncMode & EN_TAG_QUEUEING) + && (identify_message & 0xC0)) { + /* Send Tag message */ + u32 tag_mask = 1; + u8 tag_number = 0; + while (tag_mask & pDCB->TagMask + && tag_number <= pDCB->MaxCommand) { + tag_mask = tag_mask << 1; + tag_number++; + } + if (tag_number >= pDCB->MaxCommand) { + printk(KERN_WARNING DC395X_NAME + ": Start_SCSI: Out of tags for pid %li (%i-%i)\n", + pSRB->pcmd->pid, pSRB->pcmd->device->id, + pSRB->pcmd->device->lun); + pSRB->SRBState = SRB_READY; + DC395x_write16(TRM_S1040_SCSI_CONTROL, + DO_HWRESELECT); + return 1; + } + /* + ** Send Tag id + */ + DC395x_write8(TRM_S1040_SCSI_FIFO, MSG_SIMPLE_QTAG); + DC395x_write8(TRM_S1040_SCSI_FIFO, tag_number); + pDCB->TagMask |= tag_mask; + pSRB->TagNumber = tag_number; + TRACEPRINTF("Tag %i *", tag_number); + + scsicommand = SCMD_SEL_ATN3; + pSRB->SRBState = SRB_START_; + } +#endif +/*polling:*/ + /* + * Send CDB ..command block ......... + */ +#ifdef DC395x_DEBUG_KG + printk(KERN_INFO DC395X_NAME + ": StartSCSI (pid %li) %02x (%i-%i): Tag %i\n", + pSRB->pcmd->pid, pSRB->pcmd->cmnd[0], + pSRB->pcmd->device->id, pSRB->pcmd->device->lun, + pSRB->TagNumber); +#endif + if (pSRB->SRBFlag & AUTO_REQSENSE) { + DC395x_write8(TRM_S1040_SCSI_FIFO, REQUEST_SENSE); + DC395x_write8(TRM_S1040_SCSI_FIFO, (pDCB->TargetLUN << 5)); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + DC395x_write8(TRM_S1040_SCSI_FIFO, + sizeof(pSRB->pcmd->sense_buffer)); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + } else { + ptr = (u8 *) pSRB->pcmd->cmnd; + for (i = 0; i < pSRB->pcmd->cmd_len; i++) + DC395x_write8(TRM_S1040_SCSI_FIFO, *ptr++); + } + no_cmd: + DC395x_write16(TRM_S1040_SCSI_CONTROL, + DO_HWRESELECT | DO_DATALATCH); + if (DC395x_read16(TRM_S1040_SCSI_STATUS) & SCSIINTERRUPT) { + /* + * If DC395x_StartSCSI return 1: + * we caught an interrupt (must be reset or reselection ... ) + * : Let's process it first! + */ + DEBUG0(printk + (DC395X_NAME + ": Debug: StartSCSI failed (busy) for pid %li(%02i-%i)!\n", + pSRB->pcmd->pid, pDCB->TargetID, pDCB->TargetLUN); + ) + /*DC395x_clrfifo (pACB, "Start2"); */ + /*DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_HWRESELECT | DO_DATALATCH); */ + pSRB->SRBState = SRB_READY; + DC395x_freetag(pDCB, pSRB); + pSRB->MsgCnt = 0; + return_code = 1; + /* This IRQ should NOT get lost, as we did not acknowledge it */ + } else { + /* + * If DC395x_StartSCSI returns 0: + * we know that the SCSI processor is free + */ + pSRB->ScsiPhase = PH_BUS_FREE; /* initial phase */ + pDCB->pActiveSRB = pSRB; + pACB->pActiveDCB = pDCB; + return_code = 0; + /* it's important for atn stop */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, + DO_DATALATCH | DO_HWRESELECT); + /* + ** SCSI command + */ + TRACEPRINTF("%02x *", scsicommand); + DC395x_write8(TRM_S1040_SCSI_COMMAND, scsicommand); + } + return return_code; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_initAdapter + ******************************************************************** + */ + +/** + * dc395x_handle_interrupt - Handle an interrupt that has been confirmed to + * have been triggered for this card. + * + * @pACB: a pointer to the adpter control block + * @scsi_status: the status return when we checked the card + **/ +static void dc395x_handle_interrupt(struct AdapterCtlBlk *pACB, u16 scsi_status) +{ + struct DeviceCtlBlk *pDCB; + struct ScsiReqBlk *pSRB; + u16 phase; + u8 scsi_intstatus; + unsigned long flags; + void (*DC395x_stateV) (struct AdapterCtlBlk *, struct ScsiReqBlk *, + u16 *); + + DC395x_LOCK_IO(pACB->pScsiHost); + + /* This acknowledges the IRQ */ + scsi_intstatus = DC395x_read8(TRM_S1040_SCSI_INTSTATUS); + if ((scsi_status & 0x2007) == 0x2002) + printk(DC395X_NAME ": COP after COP completed? %04x\n", + scsi_status); +#if 1 /*def DC395x_DEBUG0 */ + if (DC395x_monitor_next_IRQ) { + printk(KERN_INFO DC395X_NAME + ": status=%04x intstatus=%02x\n", scsi_status, + scsi_intstatus); + DC395x_monitor_next_IRQ--; + } +#endif + /*DC395x_ACB_LOCK(pACB,acb_flags); */ +#ifdef DC395x_DEBUG_KG + if (scsi_intstatus & INT_SELTIMEOUT) + printk(KERN_INFO DC395X_NAME ": Sel Timeout IRQ\n"); +#endif + /*printk (DC395X_NAME ": DC395x_IRQ: intstatus = %02x ", scsi_intstatus); */ + + if (timer_pending(&pACB->SelTO_Timer)) + del_timer(&pACB->SelTO_Timer); + + if (scsi_intstatus & (INT_SELTIMEOUT | INT_DISCONNECT)) { + DC395x_Disconnect(pACB); /* bus free interrupt */ + goto out_unlock; + } + if (scsi_intstatus & INT_RESELECTED) { + DC395x_Reselect(pACB); + goto out_unlock; + } + if (scsi_intstatus & INT_SELECT) { + printk(KERN_INFO DC395X_NAME + ": Host does not support target mode!\n"); + goto out_unlock; + } + if (scsi_intstatus & INT_SCSIRESET) { + DC395x_ScsiRstDetect(pACB); + goto out_unlock; + } + if (scsi_intstatus & (INT_BUSSERVICE | INT_CMDDONE)) { + pDCB = pACB->pActiveDCB; + if (!pDCB) { + printk(DC395X_NAME + ": Oops: BusService (%04x %02x) w/o ActiveDCB!\n", + scsi_status, scsi_intstatus); + goto out_unlock; + } + pSRB = pDCB->pActiveSRB; + if (pDCB->DCBFlag & ABORT_DEV_) { +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "MsgOut Abort Device..... "); +#endif + DC395x_EnableMsgOut_Abort(pACB, pSRB); + } + /* + ************************************************************ + * software sequential machine + ************************************************************ + */ + phase = (u16) pSRB->ScsiPhase; + /* + * 62037 or 62137 + * call DC395x_SCSI_phase0[]... "phase entry" + * handle every phase before start transfer + */ + /* DC395x_DataOutPhase0, phase:0 */ + /* DC395x_DataInPhase0, phase:1 */ + /* DC395x_CommandPhase0, phase:2 */ + /* DC395x_StatusPhase0, phase:3 */ + /* DC395x_Nop0, phase:4 PH_BUS_FREE .. initial phase */ + /* DC395x_Nop0, phase:5 PH_BUS_FREE .. initial phase */ + /* DC395x_MsgOutPhase0, phase:6 */ + /* DC395x_MsgInPhase0, phase:7 */ + DC395x_stateV = (void *) DC395x_SCSI_phase0[phase]; + DC395x_stateV(pACB, pSRB, &scsi_status); + /* + *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + * + * if there were any exception occured + * scsi_status will be modify to bus free phase + * new scsi_status transfer out from ... previous DC395x_stateV + * + *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + */ + pSRB->ScsiPhase = scsi_status & PHASEMASK; + phase = (u16) scsi_status & PHASEMASK; + /* + * call DC395x_SCSI_phase1[]... "phase entry" + * handle every phase do transfer + */ + /* DC395x_DataOutPhase1, phase:0 */ + /* DC395x_DataInPhase1, phase:1 */ + /* DC395x_CommandPhase1, phase:2 */ + /* DC395x_StatusPhase1, phase:3 */ + /* DC395x_Nop1, phase:4 PH_BUS_FREE .. initial phase */ + /* DC395x_Nop1, phase:5 PH_BUS_FREE .. initial phase */ + /* DC395x_MsgOutPhase1, phase:6 */ + /* DC395x_MsgInPhase1, phase:7 */ + DC395x_stateV = (void *) DC395x_SCSI_phase1[phase]; + DC395x_stateV(pACB, pSRB, &scsi_status); + } + out_unlock: + DC395x_UNLOCK_IO(pACB->pScsiHost); + return; +} + +/*inline */ +irqreturn_t DC395x_Interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct AdapterCtlBlk *pACB = DC395x_pACB_start; + u16 scsi_status; + u8 dma_status; + irqreturn_t handled = IRQ_NONE; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_Interrupt..............\n "); +#endif + DEBUGRECURSION(if (in_driver++ > NORM_REC_LVL) + printk(DC395X_NAME ": %i interrupt recursion?\n", + in_driver);) + + /* + * Find which card generated the interrupt. Note that it may have + * been something else that we share the interupt with which + * actually generated it. + * + * We'll check the interupt status register of each card that + * is on the IRQ that was responsible for this interupt. + */ + for (; pACB != NULL; pACB = pACB->pNextACB) { + if (pACB->IRQLevel != (u8) irq) { + /* card is not on the irq that triggered */ + continue; + } + + /* + * Ok, we've found a card on the correct irq, + * let's check if an interupt is pending + */ + scsi_status = DC395x_read16(TRM_S1040_SCSI_STATUS); + dma_status = DC395x_read8(TRM_S1040_DMA_STATUS); + if (scsi_status & SCSIINTERRUPT) { + /* interupt pending - let's process it! */ + dc395x_handle_interrupt(pACB, scsi_status); + handled = IRQ_HANDLED; + } + else if (dma_status & 0x20) { + /* Error from the DMA engine */ + printk(DC395X_NAME ": Interrupt from DMA engine: %02x!\n", + dma_status); +#if 0 + printk(DC395X_NAME ": This means DMA error! Try to handle ...\n"); + if (pACB->pActiveDCB) { + pACB->pActiveDCB-> DCBFlag |= ABORT_DEV_; + if (pACB->pActiveDCB->pActiveSRB) + DC395x_EnableMsgOut_Abort(pACB, pACB->pActiveDCB->pActiveSRB); + } + DC395x_write8(TRM_S1040_DMA_CONTROL, ABORTXFER | CLRXFIFO); +#else + printk(DC395X_NAME ": Ignoring DMA error (probably a bad thing) ...\n"); + pACB = (struct AdapterCtlBlk *)NULL; +#endif + handled = IRQ_HANDLED; + } + } + + DEBUGRECURSION(in_driver--;) + return handled; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_MsgOutPhase0: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =6 + ******************************************************************** + */ +static void +DC395x_MsgOutPhase0(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_MsgOutPhase0..... "); +#endif + if (pSRB->SRBState & (SRB_UNEXPECT_RESEL + SRB_ABORT_SENT)) { + *pscsi_status = PH_BUS_FREE; /*.. initial phase */ + } + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + pSRB->SRBState &= ~SRB_MSGOUT; + TRACEPRINTF("MOP0 *"); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_MsgOutPhase1: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =6 + ******************************************************************** + */ +static void +DC395x_MsgOutPhase1(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + u16 i; + u8 *ptr; + struct DeviceCtlBlk *pDCB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_MsgOutPhase1..............\n "); +#endif + TRACEPRINTF("MOP1*"); + pDCB = pACB->pActiveDCB; + DC395x_clrfifo(pACB, "MOP1"); + if (!(pSRB->SRBState & SRB_MSGOUT)) { + pSRB->SRBState |= SRB_MSGOUT; + printk(DC395X_NAME ": Debug: pid %li: MsgOut Phase unexpected.\n", pSRB->pcmd->pid); /* So what ? */ + } + if (!pSRB->MsgCnt) { + DEBUG0(printk + (DC395X_NAME + ": Debug: pid %li: NOP Msg (no output message there).\n", + pSRB->pcmd->pid); + ) + DC395x_write8(TRM_S1040_SCSI_FIFO, MSG_NOP); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); + TRACEPRINTF("\\*"); + TRACEOUT(" %s\n", pSRB->debugtrace); + return; + } + ptr = (u8 *) pSRB->MsgOutBuf; + TRACEPRINTF("(*"); + /*printk (DC395X_NAME ": Send msg: "); DC395x_printMsg (ptr, pSRB->MsgCnt); */ + /*printk (DC395X_NAME ": MsgOut: "); */ + for (i = 0; i < pSRB->MsgCnt; i++) { + TRACEPRINTF("%02x *", *ptr); + DC395x_write8(TRM_S1040_SCSI_FIFO, *ptr++); + } + TRACEPRINTF(")*"); + pSRB->MsgCnt = 0; + /*printk ("\n"); */ + if ( /*(pDCB->DCBFlag & ABORT_DEV_) && */ + (pSRB->MsgOutBuf[0] == MSG_ABORT)) + pSRB->SRBState = SRB_ABORT_SENT; + + /*1.25 */ + /*DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_DATALATCH); *//* it's important for atn stop */ + /* + ** SCSI command + */ + /*TRACEPRINTF (".*"); */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_CommandPhase0: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =2 + ******************************************************************** + */ +static void +DC395x_CommandPhase0(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + TRACEPRINTF("COP0 *"); + /*1.25 */ + /*DC395x_clrfifo (pACB, COP0); */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_CommandPhase1: one of DC395x_SCSI_phase1[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase1[phase] + * if phase =2 + ******************************************************************** + */ +static void +DC395x_CommandPhase1(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + struct DeviceCtlBlk *pDCB; + u8 *ptr; + u16 i; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_CommandPhase1..............\n "); +#endif + TRACEPRINTF("COP1*"); + DC395x_clrfifo(pACB, "COP1"); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_CLRATN); + if (!(pSRB->SRBFlag & AUTO_REQSENSE)) { + ptr = (u8 *) pSRB->pcmd->cmnd; + for (i = 0; i < pSRB->pcmd->cmd_len; i++) { + DC395x_write8(TRM_S1040_SCSI_FIFO, *ptr); + ptr++; + } + } else { + DC395x_write8(TRM_S1040_SCSI_FIFO, REQUEST_SENSE); + pDCB = pACB->pActiveDCB; + /* target id */ + DC395x_write8(TRM_S1040_SCSI_FIFO, (pDCB->TargetLUN << 5)); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + DC395x_write8(TRM_S1040_SCSI_FIFO, + sizeof(pSRB->pcmd->sense_buffer)); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + } + pSRB->SRBState |= SRB_COMMAND; + /* it's important for atn stop */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); + /* SCSI command */ + TRACEPRINTF(".*"); + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); +} + + +/* Do sanity checks for S/G list */ +#ifdef DC395x_SGPARANOIA +static inline void DC395x_check_SG(struct ScsiReqBlk *pSRB) +{ + unsigned Length = 0; + unsigned Idx = pSRB->SRBSGIndex; + struct SGentry *psge = pSRB->SegmentX + Idx; + for (; Idx < pSRB->SRBSGCount; psge++, Idx++) + Length += psge->length; + if (Length != pSRB->SRBTotalXferLength) + printk(DC395X_NAME + ": Inconsistent SRB S/G lengths (Tot=%i, Count=%i) !!\n", + pSRB->SRBTotalXferLength, Length); +} +#else +static inline void DC395x_check_SG(struct ScsiReqBlk *pSRB) +{ +} +#endif + + +/* + * Compute the next Scatter Gather list index and adjust its length + * and address if necessary; also compute virt_addr + */ +void DC395x_update_SGlist(struct ScsiReqBlk *pSRB, u32 Left) +{ + struct SGentry *psge; + u32 Xferred = 0; + u8 Idx; + Scsi_Cmnd *pcmd = pSRB->pcmd; + struct scatterlist *sg; + int segment = pcmd->use_sg; + +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME ": Update SG: Total %i, Left %i\n", + pSRB->SRBTotalXferLength, Left); +#endif + DC395x_check_SG(pSRB); + psge = pSRB->SegmentX + pSRB->SRBSGIndex; + /* data that has already been transferred */ + Xferred = pSRB->SRBTotalXferLength - Left; + if (pSRB->SRBTotalXferLength != Left) { + /*DC395x_check_SG_TX (pSRB, Xferred); */ + /* Remaining */ + pSRB->SRBTotalXferLength = Left; + /* parsing from last time disconnect SGIndex */ + for (Idx = pSRB->SRBSGIndex; Idx < pSRB->SRBSGCount; Idx++) { + /* Complete SG entries done */ + if (Xferred >= psge->length) + Xferred -= psge->length; + /* Partial SG entries done */ + else { + psge->length -= Xferred; /* residue data length */ + psge->address += Xferred; /* residue data pointer */ + pSRB->SRBSGIndex = Idx; + pci_dma_sync_single(pSRB->pSRBDCB-> + pDCBACB->pdev, + pSRB->SRBSGBusAddr, + sizeof(struct SGentry) + * + DC395x_MAX_SG_LISTENTRY, + PCI_DMA_TODEVICE); + break; + } + psge++; + } + DC395x_check_SG(pSRB); + } + /* We need the corresponding virtual address sg_to_virt */ + /*printk (DC395X_NAME ": sg_to_virt: bus %08x -> virt ", psge->address); */ + if (!segment) { + pSRB->virt_addr += Xferred; + /*printk ("%p\n", pSRB->virt_addr); */ + return; + } + /* We have to walk the scatterlist to find it */ + sg = (struct scatterlist *) pcmd->request_buffer; + while (segment--) { + /*printk ("(%08x)%p ", BUS_ADDR(*sg), PAGE_ADDRESS(sg)); */ + unsigned long mask = + ~((unsigned long) sg->length - 1) & PAGE_MASK; + if ((BUS_ADDR(*sg) & mask) == (psge->address & mask)) { + pSRB->virt_addr = (PAGE_ADDRESS(sg) + + psge->address - + (psge->address & PAGE_MASK)); + /*printk ("%p\n", pSRB->virt_addr); */ + return; + } + ++sg; + } + printk(DC395X_NAME ": sg_to_virt failed!\n"); + pSRB->virt_addr = 0; +} + + +/* + * DC395x_cleanup_after_transfer + * + * Makes sure, DMA and SCSI engine are empty, after the transfer has finished + * KG: Currently called from StatusPhase1 () + * Should probably also be called from other places + * Best might be to call it in DataXXPhase0, if new phase will differ + */ +static void +DC395x_cleanup_after_transfer(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB) +{ + TRACEPRINTF(" Cln*"); + /*DC395x_write8 (TRM_S1040_DMA_STATUS, FORCEDMACOMP); */ + if (DC395x_read16(TRM_S1040_DMA_COMMAND) & 0x0001) { /* read */ + if (!(DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x40)) + DC395x_clrfifo(pACB, "ClnIn"); + + if (!(DC395x_read8(TRM_S1040_DMA_FIFOSTAT) & 0x80)) + DC395x_write8(TRM_S1040_DMA_CONTROL, CLRXFIFO); + } else { /* write */ + if (!(DC395x_read8(TRM_S1040_DMA_FIFOSTAT) & 0x80)) + DC395x_write8(TRM_S1040_DMA_CONTROL, CLRXFIFO); + + if (!(DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x40)) + DC395x_clrfifo(pACB, "ClnOut"); + + } + /*1.25 */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); +} + + +/* + * Those no of bytes will be transfered w/ PIO through the SCSI FIFO + * Seems to be needed for unknown reasons; could be a hardware bug :-( + */ +#define DC395x_LASTPIO 4 +/* + ******************************************************************** + * scsiio + * DC395x_DataOutPhase0: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =0 + ******************************************************************** + */ +void +DC395x_DataOutPhase0(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + u16 scsi_status; + u32 dLeftCounter = 0; + struct DeviceCtlBlk *pDCB = pSRB->pSRBDCB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_DataOutPhase0.....\n "); +#endif + TRACEPRINTF("DOP0*"); + pDCB = pSRB->pSRBDCB; + scsi_status = *pscsi_status; + + /* + * KG: We need to drain the buffers before we draw any conclusions! + * This means telling the DMA to push the rest into SCSI, telling + * SCSI to push the rest to the bus. + * However, the device might have been the one to stop us (phase + * change), and the data in transit just needs to be accounted so + * it can be retransmitted.) + */ + /* + * KG: Stop DMA engine pushing more data into the SCSI FIFO + * If we need more data, the DMA SG list will be freshly set up, anyway + */ +#ifdef DC395x_DEBUGPIO + printk(DC395X_NAME + ": DOP0: DMA_FCNT: %02x, DMA_FSTAT: %02x, SCSI_FCNT: %02x, CTR %06x, stat %04x, Tot: %06x\n", + DC395x_read8(TRM_S1040_DMA_FIFOCNT), + DC395x_read8(TRM_S1040_DMA_FIFOSTAT), + DC395x_read8(TRM_S1040_SCSI_FIFOCNT), + DC395x_read32(TRM_S1040_SCSI_COUNTER), scsi_status, + pSRB->SRBTotalXferLength); + /*DC395x_dumpinfo(pACB, pDCB, pSRB); */ +#endif + DC395x_write8(TRM_S1040_DMA_CONTROL, STOPDMAXFER | CLRXFIFO); + + if (!(pSRB->SRBState & SRB_XFERPAD)) { + if (scsi_status & PARITYERROR) + pSRB->SRBStatus |= PARITY_ERROR; + + /* + * KG: Right, we can't just rely on the SCSI_COUNTER, because this + * is the no of bytes it got from the DMA engine not the no it + * transferred successfully to the device. (And the difference could + * be as much as the FIFO size, I guess ...) + */ + if (!(scsi_status & SCSIXFERDONE)) { + /* + * when data transfer from DMA FIFO to SCSI FIFO + * if there was some data left in SCSI FIFO + */ + dLeftCounter = + (u32) (DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & + 0x1F); + if (pDCB->SyncPeriod & WIDE_SYNC) + dLeftCounter <<= 1; + +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Debug: SCSI FIFO contains %i %s in DOP0\n", + DC395x_read8(TRM_S1040_SCSI_FIFOCNT), + (pDCB-> + SyncPeriod & WIDE_SYNC) ? "words" : + "bytes"); + printk(DC395X_NAME + ": SCSI FIFOCNT %02x, SCSI CTR %08x\n", + DC395x_read8(TRM_S1040_SCSI_FIFOCNT), + DC395x_read32(TRM_S1040_SCSI_COUNTER)); + printk(DC395X_NAME + ": DMA FIFOCNT %04x, FIFOSTAT %02x, DMA CTR %08x\n", + DC395x_read8(TRM_S1040_DMA_FIFOCNT), + DC395x_read8(TRM_S1040_DMA_FIFOSTAT), + DC395x_read32(TRM_S1040_DMA_CXCNT)); +#endif + /* + * if WIDE scsi SCSI FIFOCNT unit is word !!! + * so need to *= 2 + */ + } + /* + * calculate all the residue data that not yet tranfered + * SCSI transfer counter + left in SCSI FIFO data + * + * .....TRM_S1040_SCSI_COUNTER (24bits) + * The counter always decrement by one for every SCSI byte transfer. + * .....TRM_S1040_SCSI_FIFOCNT ( 5bits) + * The counter is SCSI FIFO offset counter (in units of bytes or! words) + */ + if (pSRB->SRBTotalXferLength > DC395x_LASTPIO) + dLeftCounter += + DC395x_read32(TRM_S1040_SCSI_COUNTER); + TRACEPRINTF("%06x *", dLeftCounter); + + /* Is this a good idea? */ + /*DC395x_clrfifo (pACB, "DOP1"); */ + /* KG: What is this supposed to be useful for? WIDE padding stuff? */ + if (dLeftCounter == 1 && pDCB->SyncPeriod & WIDE_SYNC + && pSRB->pcmd->request_bufflen % 2) { + dLeftCounter = 0; + printk(DC395X_NAME + ": DOP0: Discard 1 byte. (%02x)\n", + scsi_status); + } + /* + * KG: Oops again. Same thinko as above: The SCSI might have been + * faster than the DMA engine, so that it ran out of data. + * In that case, we have to do just nothing! + * But: Why the interrupt: No phase change. No XFERCNT_2_ZERO. Or? + */ + /* + * KG: This is nonsense: We have been WRITING data to the bus + * If the SCSI engine has no bytes left, how should the DMA engine? + */ + if ((dLeftCounter == + 0) /*|| (scsi_status & SCSIXFERCNT_2_ZERO) ) */ ) { + /* + * int ctr = 6000000; u8 TempDMAstatus; + * do + * { + * TempDMAstatus = DC395x_read8(TRM_S1040_DMA_STATUS); + * } while( !(TempDMAstatus & DMAXFERCOMP) && --ctr); + * if (ctr < 6000000-1) printk (DC395X_NAME ": DMA should be complete ... in DOP1\n"); + * if (!ctr) printk (KERN_ERR DC395X_NAME ": Deadlock in DataOutPhase0 !!\n"); + */ + pSRB->SRBTotalXferLength = 0; + } else { /* Update SG list */ + /* + * if transfer not yet complete + * there were some data residue in SCSI FIFO or + * SCSI transfer counter not empty + */ + long oldXferred = + pSRB->SRBTotalXferLength - dLeftCounter; + const int diff = + (pDCB->SyncPeriod & WIDE_SYNC) ? 2 : 1; + DC395x_update_SGlist(pSRB, dLeftCounter); + /* KG: Most ugly hack! Apparently, this works around a chip bug */ + if ((pSRB->SegmentX[pSRB->SRBSGIndex].length == + diff && pSRB->pcmd->use_sg) + || ((oldXferred & ~PAGE_MASK) == + (PAGE_SIZE - diff)) + ) { + printk(DC395X_NAME + ": Work around chip bug (%i)?\n", + diff); + dLeftCounter = + pSRB->SRBTotalXferLength - diff; + DC395x_update_SGlist(pSRB, dLeftCounter); + /*pSRB->SRBTotalXferLength -= diff; */ + /*pSRB->virt_addr += diff; */ + /*if (pSRB->pcmd->use_sg) */ + /* pSRB->SRBSGIndex++; */ + } + } + } +#if 0 + if (!(DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x40)) + printk(DC395X_NAME + ": DOP0(%li): %i bytes in SCSI FIFO! (Clear!)\n", + pSRB->pcmd->pid, + DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x1f); +#endif + /*DC395x_clrfifo (pACB, "DOP0"); */ + /*DC395x_write8 (TRM_S1040_DMA_CONTROL, CLRXFIFO | ABORTXFER); */ +#if 1 + if ((*pscsi_status & PHASEMASK) != PH_DATA_OUT) { + /*printk (DC395X_NAME ": Debug: Clean up after Data Out ...\n"); */ + DC395x_cleanup_after_transfer(pACB, pSRB); + } +#endif + TRACEPRINTF(".*"); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_DataOutPhase1: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =0 + * 62037 + ******************************************************************** + */ +static void +DC395x_DataOutPhase1(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_DataOutPhase1.....\n"); +#endif + /*1.25 */ + TRACEPRINTF("DOP1*"); + DC395x_clrfifo(pACB, "DOP1"); + /* + ** do prepare befor transfer when data out phase + */ + DC395x_DataIO_transfer(pACB, pSRB, XFERDATAOUT); + TRACEPRINTF(".*"); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_DataInPhase0: one of DC395x_SCSI_phase1[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase1[phase] + * if phase =1 + ******************************************************************** + */ +void +DC395x_DataInPhase0(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + u16 scsi_status; + u32 dLeftCounter = 0; + /*struct DeviceCtlBlk* pDCB = pSRB->pSRBDCB; */ + /*u8 bval; */ + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_DataInPhase0..............\n "); +#endif + TRACEPRINTF("DIP0*"); + scsi_status = *pscsi_status; + + /* + * KG: DataIn is much more tricky than DataOut. When the device is finished + * and switches to another phase, the SCSI engine should be finished too. + * But: There might still be bytes left in its FIFO to be fetched by the DMA + * engine and transferred to memory. + * We should wait for the FIFOs to be emptied by that (is there any way to + * enforce this?) and then stop the DMA engine, because it might think, that + * there are more bytes to follow. Yes, the device might disconnect prior to + * having all bytes transferred! + * Also we should make sure that all data from the DMA engine buffer's really + * made its way to the system memory! Some documentation on this would not + * seem to be a bad idea, actually. + */ + if (!(pSRB->SRBState & SRB_XFERPAD)) { + if (scsi_status & PARITYERROR) { + printk(DC395X_NAME + ": Parity Error (pid %li, target %02i-%i)\n", + pSRB->pcmd->pid, pSRB->pcmd->device->id, + pSRB->pcmd->device->lun); + pSRB->SRBStatus |= PARITY_ERROR; + } + /* + * KG: We should wait for the DMA FIFO to be empty ... + * but: it would be better to wait first for the SCSI FIFO and then the + * the DMA FIFO to become empty? How do we know, that the device not already + * sent data to the FIFO in a MsgIn phase, eg.? + */ + if (!(DC395x_read8(TRM_S1040_DMA_FIFOSTAT) & 0x80)) { +#if 0 + int ctr = 6000000; + printk(DC395X_NAME + ": DIP0: Wait for DMA FIFO to flush ...\n"); + /*DC395x_write8 (TRM_S1040_DMA_CONTROL, STOPDMAXFER); */ + /*DC395x_write32 (TRM_S1040_SCSI_COUNTER, 7); */ + /*DC395x_write8 (TRM_S1040_SCSI_COMMAND, SCMD_DMA_IN); */ + while (! + (DC395x_read16(TRM_S1040_DMA_FIFOSTAT) & + 0x80) && --ctr); + if (ctr < 6000000 - 1) + printk(DC395X_NAME + ": Debug: DIP0: Had to wait for DMA ...\n"); + if (!ctr) + printk(KERN_ERR DC395X_NAME + ": Deadlock in DIP0 waiting for DMA FIFO empty!!\n"); + /*DC395x_write32 (TRM_S1040_SCSI_COUNTER, 0); */ +#endif +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME ": DIP0: DMA_FIFO: %02x %02x\n", + DC395x_read8(TRM_S1040_DMA_FIFOCNT), + DC395x_read8(TRM_S1040_DMA_FIFOSTAT)); +#endif + } + /* Now: Check remainig data: The SCSI counters should tell us ... */ + dLeftCounter = DC395x_read32(TRM_S1040_SCSI_COUNTER) + + ((DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x1f) + << ((pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) ? 1 : + 0)); + +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Debug: SCSI FIFO contains %i %s in DIP0\n", + DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x1f, + (pSRB->pSRBDCB-> + SyncPeriod & WIDE_SYNC) ? "words" : "bytes"); + printk(DC395X_NAME ": SCSI FIFOCNT %02x, SCSI CTR %08x\n", + DC395x_read8(TRM_S1040_SCSI_FIFOCNT), + DC395x_read32(TRM_S1040_SCSI_COUNTER)); + printk(DC395X_NAME + ": DMA FIFOCNT %02x,%02x DMA CTR %08x\n", + DC395x_read8(TRM_S1040_DMA_FIFOCNT), + DC395x_read8(TRM_S1040_DMA_FIFOSTAT), + DC395x_read32(TRM_S1040_DMA_CXCNT)); + printk(DC395X_NAME + ": Remaining: TotXfer: %i, SCSI FIFO+Ctr: %i\n", + pSRB->SRBTotalXferLength, dLeftCounter); +#endif +#if DC395x_LASTPIO + /* KG: Less than or equal to 4 bytes can not be transfered via DMA, it seems. */ + if (dLeftCounter + && pSRB->SRBTotalXferLength <= DC395x_LASTPIO) { + /*u32 addr = (pSRB->SegmentX[pSRB->SRBSGIndex].address); */ + /*DC395x_update_SGlist (pSRB, dLeftCounter); */ + DEBUGPIO(printk + (DC395X_NAME + ": DIP0: PIO (%i %s) to %p for remaining %i bytes:", + DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & + 0x1f, + (pSRB->pSRBDCB-> + SyncPeriod & WIDE_SYNC) ? "words" : + "bytes", pSRB->virt_addr, + pSRB->SRBTotalXferLength); + ) + + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) + DC395x_write8(TRM_S1040_SCSI_CONFIG2, + CFG2_WIDEFIFO); + + while (DC395x_read8(TRM_S1040_SCSI_FIFOCNT) != + 0x40) { + u8 byte = + DC395x_read8(TRM_S1040_SCSI_FIFO); + *(pSRB->virt_addr)++ = byte; + DEBUGPIO(printk(" %02x", byte); + ) + pSRB->SRBTotalXferLength--; + dLeftCounter--; + pSRB->SegmentX[pSRB->SRBSGIndex].length--; + if (pSRB->SRBTotalXferLength + && !pSRB->SegmentX[pSRB->SRBSGIndex]. + length) { + DEBUGPIO(printk(" (next segment)"); + ) + pSRB->SRBSGIndex++; + DC395x_update_SGlist(pSRB, + dLeftCounter); + } + } + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) { +#if 1 /* Read the last byte ... */ + if (pSRB->SRBTotalXferLength > 0) { + u8 byte = + DC395x_read8 + (TRM_S1040_SCSI_FIFO); + *(pSRB->virt_addr)++ = byte; + pSRB->SRBTotalXferLength--; + DEBUGPIO(printk(" %02x", byte); + ) + } +#endif + DC395x_write8(TRM_S1040_SCSI_CONFIG2, 0); + } + /*printk (" %08x", *(u32*)(bus_to_virt (addr))); */ + /*pSRB->SRBTotalXferLength = 0; */ + DEBUGPIO(printk("\n"); + ) + } +#endif /* DC395x_LASTPIO */ + +#if 0 + /* + * KG: This was in DATAOUT. Does it also belong here? + * Nobody seems to know what counter and fifo_cnt count exactly ... + */ + if (!(scsi_status & SCSIXFERDONE)) { + /* + * when data transfer from DMA FIFO to SCSI FIFO + * if there was some data left in SCSI FIFO + */ + dLeftCounter = + (u32) (DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & + 0x1F); + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) + dLeftCounter <<= 1; + /* + * if WIDE scsi SCSI FIFOCNT unit is word !!! + * so need to *= 2 + * KG: Seems to be correct ... + */ + } +#endif + /*dLeftCounter += DC395x_read32(TRM_S1040_SCSI_COUNTER); */ +#if 0 + printk(DC395X_NAME + ": DIP0: ctr=%08x, DMA_FIFO=%02x,%02x SCSI_FIFO=%02x\n", + dLeftCounter, DC395x_read8(TRM_S1040_DMA_FIFOCNT), + DC395x_read8(TRM_S1040_DMA_FIFOSTAT), + DC395x_read8(TRM_S1040_SCSI_FIFOCNT)); + printk(DC395X_NAME ": DIP0: DMAStat %02x\n", + DC395x_read8(TRM_S1040_DMA_STATUS)); +#endif + + /* KG: This should not be needed any more! */ + if ((dLeftCounter == 0) + || (scsi_status & SCSIXFERCNT_2_ZERO)) { +#if 0 + int ctr = 6000000; + u8 TempDMAstatus; + do { + TempDMAstatus = + DC395x_read8(TRM_S1040_DMA_STATUS); + } while (!(TempDMAstatus & DMAXFERCOMP) && --ctr); + if (!ctr) + printk(KERN_ERR DC395X_NAME + ": Deadlock in DataInPhase0 waiting for DMA!!\n"); + pSRB->SRBTotalXferLength = 0; +#endif +#if 0 /*def DC395x_DEBUG_KG */ + printk(DC395X_NAME + ": DIP0: DMA not yet ready: %02x: %i -> %i bytes\n", + DC395x_read8(TRM_S1040_DMA_STATUS), + pSRB->SRBTotalXferLength, dLeftCounter); +#endif + pSRB->SRBTotalXferLength = dLeftCounter; + } else { /* phase changed */ + /* + * parsing the case: + * when a transfer not yet complete + * but be disconnected by target + * if transfer not yet complete + * there were some data residue in SCSI FIFO or + * SCSI transfer counter not empty + */ + DC395x_update_SGlist(pSRB, dLeftCounter); + } + } + /* KG: The target may decide to disconnect: Empty FIFO before! */ + if ((*pscsi_status & PHASEMASK) != PH_DATA_IN) { + /*printk (DC395X_NAME ": Debug: Clean up after Data In ...\n"); */ + DC395x_cleanup_after_transfer(pACB, pSRB); + } +#if 0 + /* KG: Make sure, no previous transfers are pending! */ + bval = DC395x_read8(TRM_S1040_SCSI_FIFOCNT); + if (!(bval & 0x40)) { + bval &= 0x1f; + printk(DC395X_NAME + ": DIP0(%li): %i bytes in SCSI FIFO (stat %04x) (left %08x)!!\n", + pSRB->pcmd->pid, bval & 0x1f, scsi_status, + dLeftCounter); + if ((dLeftCounter == 0) + || (scsi_status & SCSIXFERCNT_2_ZERO)) { + printk(DC395X_NAME ": Clear FIFO!\n"); + DC395x_clrfifo(pACB, "DIP0"); + } + } +#endif + /*DC395x_write8 (TRM_S1040_DMA_CONTROL, CLRXFIFO | ABORTXFER); */ + + /*DC395x_clrfifo (pACB, "DIP0"); */ + /*DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_DATALATCH); */ + TRACEPRINTF(".*"); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_DataInPhase1: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =1 + ******************************************************************** + */ +static void +DC395x_DataInPhase1(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_DataInPhase1..... "); +#endif + /* FIFO should be cleared, if previous phase was not DataPhase */ + /*DC395x_clrfifo (pACB, "DIP1"); */ + /* Allow data in! */ + /*DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_DATALATCH); */ + TRACEPRINTF("DIP1:*"); + /* + ** do prepare before transfer when data in phase + */ + DC395x_DataIO_transfer(pACB, pSRB, XFERDATAIN); + TRACEPRINTF(".*"); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_DataOutPhase1 + * DC395x_DataInPhase1 + ******************************************************************** + */ +void +DC395x_DataIO_transfer(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 ioDir) +{ + u8 bval; + struct DeviceCtlBlk *pDCB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DataIO_transfer %c (pid %li): len = %i, SG: %i/%i\n", + ((ioDir & DMACMD_DIR) ? 'r' : 'w'), pSRB->pcmd->pid, + pSRB->SRBTotalXferLength, pSRB->SRBSGIndex, + pSRB->SRBSGCount); +#endif + TRACEPRINTF("%05x(%i/%i)*", pSRB->SRBTotalXferLength, + pSRB->SRBSGIndex, pSRB->SRBSGCount); + pDCB = pSRB->pSRBDCB; + if (pSRB == pACB->pTmpSRB) { + printk(DC395X_NAME + ": ERROR! Using TmpSRB in DataPhase!\n"); + } + if (pSRB->SRBSGIndex < pSRB->SRBSGCount) { + if (pSRB->SRBTotalXferLength > DC395x_LASTPIO) { + u8 dma_status = DC395x_read8(TRM_S1040_DMA_STATUS); + /* + * KG: What should we do: Use SCSI Cmd 0x90/0x92? + * Maybe, even ABORTXFER would be appropriate + */ + if (dma_status & XFERPENDING) { + printk(DC395X_NAME + ": Xfer pending! Expect trouble!!\n"); + DC395x_dumpinfo(pACB, pDCB, pSRB); + DC395x_write8(TRM_S1040_DMA_CONTROL, + CLRXFIFO); + } + /*DC395x_clrfifo (pACB, "IO"); */ + /* + * load what physical address of Scatter/Gather list table want to be + * transfer + */ + pSRB->SRBState |= SRB_DATA_XFER; + DC395x_write32(TRM_S1040_DMA_XHIGHADDR, 0); + if (pSRB->pcmd->use_sg) { /* with S/G */ + ioDir |= DMACMD_SG; + DC395x_write32(TRM_S1040_DMA_XLOWADDR, + pSRB->SRBSGBusAddr + + sizeof(struct SGentry) * + pSRB->SRBSGIndex); + /* load how many bytes in the Scatter/Gather list table */ + DC395x_write32(TRM_S1040_DMA_XCNT, + ((u32) + (pSRB->SRBSGCount - + pSRB->SRBSGIndex) << 3)); + } else { /* without S/G */ + ioDir &= ~DMACMD_SG; + DC395x_write32(TRM_S1040_DMA_XLOWADDR, + pSRB->SegmentX[0].address); + DC395x_write32(TRM_S1040_DMA_XCNT, + pSRB->SegmentX[0].length); + } + /* load total transfer length (24bits) max value 16Mbyte */ + DC395x_write32(TRM_S1040_SCSI_COUNTER, + pSRB->SRBTotalXferLength); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + if (ioDir & DMACMD_DIR) { /* read */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, + SCMD_DMA_IN); + DC395x_write16(TRM_S1040_DMA_COMMAND, + ioDir); + } else { + DC395x_write16(TRM_S1040_DMA_COMMAND, + ioDir); + DC395x_write8(TRM_S1040_SCSI_COMMAND, + SCMD_DMA_OUT); + } + + } +#if DC395x_LASTPIO + else if (pSRB->SRBTotalXferLength > 0) { /* The last four bytes: Do PIO */ + /*DC395x_clrfifo (pACB, "IO"); */ + /* + * load what physical address of Scatter/Gather list table want to be + * transfer + */ + pSRB->SRBState |= SRB_DATA_XFER; + /* load total transfer length (24bits) max value 16Mbyte */ + DC395x_write32(TRM_S1040_SCSI_COUNTER, + pSRB->SRBTotalXferLength); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + if (ioDir & DMACMD_DIR) { /* read */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, + SCMD_FIFO_IN); + } else { /* write */ + int ln = pSRB->SRBTotalXferLength; + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) + DC395x_write8 + (TRM_S1040_SCSI_CONFIG2, + CFG2_WIDEFIFO); + DEBUGPIO(printk + (DC395X_NAME + ": DOP1: PIO %i bytes from %p:", + pSRB->SRBTotalXferLength, + pSRB->virt_addr); + ) + while (pSRB->SRBTotalXferLength) { + DEBUGPIO(printk + (" %02x", + (unsigned char) *(pSRB-> + virt_addr)); + ) + DC395x_write8 + (TRM_S1040_SCSI_FIFO, + *(pSRB->virt_addr)++); + pSRB->SRBTotalXferLength--; + pSRB->SegmentX[pSRB->SRBSGIndex]. + length--; + if (pSRB->SRBTotalXferLength + && !pSRB->SegmentX[pSRB-> + SRBSGIndex]. + length) { + DEBUGPIO(printk + (" (next segment)"); + ) + pSRB->SRBSGIndex++; + DC395x_update_SGlist(pSRB, + pSRB-> + SRBTotalXferLength); + } + } + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) { + if (ln % 2) { + DC395x_write8 + (TRM_S1040_SCSI_FIFO, + 0); + DEBUGPIO(printk(" |00"); + ) + } + DC395x_write8 + (TRM_S1040_SCSI_CONFIG2, 0); + } + /*DC395x_write32(TRM_S1040_SCSI_COUNTER, ln); */ + DEBUGPIO(printk("\n"); + ) + DC395x_write8(TRM_S1040_SCSI_COMMAND, + SCMD_FIFO_OUT); + } + } +#endif /* DC395x_LASTPIO */ + else { /* xfer pad */ + + u8 data = 0, data2 = 0; + if (pSRB->SRBSGCount) { + pSRB->AdaptStatus = H_OVER_UNDER_RUN; + pSRB->SRBStatus |= OVER_RUN; + } + /* + * KG: despite the fact that we are using 16 bits I/O ops + * the SCSI FIFO is only 8 bits according to the docs + * (we can set bit 1 in 0x8f to serialize FIFO access ...) + */ + if (pDCB->SyncPeriod & WIDE_SYNC) { + DC395x_write32(TRM_S1040_SCSI_COUNTER, 2); + DC395x_write8(TRM_S1040_SCSI_CONFIG2, + CFG2_WIDEFIFO); + if (ioDir & DMACMD_DIR) { /* read */ + data = + DC395x_read8 + (TRM_S1040_SCSI_FIFO); + data2 = + DC395x_read8 + (TRM_S1040_SCSI_FIFO); + /*printk (DC395X_NAME ": DataIO: Xfer pad: %02x %02x\n", data, data2); */ + } else { + /* Danger, Robinson: If you find KGs scattered over the wide + * disk, the driver or chip is to blame :-( */ + DC395x_write8(TRM_S1040_SCSI_FIFO, + 'K'); + DC395x_write8(TRM_S1040_SCSI_FIFO, + 'G'); + } + DC395x_write8(TRM_S1040_SCSI_CONFIG2, 0); + } else { + DC395x_write32(TRM_S1040_SCSI_COUNTER, 1); + /* Danger, Robinson: If you find a collection of Ks on your disk + * something broke :-( */ + if (ioDir & DMACMD_DIR) { /* read */ + data = + DC395x_read8 + (TRM_S1040_SCSI_FIFO); + /*printk (DC395X_NAME ": DataIO: Xfer pad: %02x\n", data); */ + } else { + DC395x_write8(TRM_S1040_SCSI_FIFO, + 'K'); + } + } + pSRB->SRBState |= SRB_XFERPAD; + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + /* + * SCSI command + */ + bval = + (ioDir & DMACMD_DIR) ? SCMD_FIFO_IN : + SCMD_FIFO_OUT; + DC395x_write8(TRM_S1040_SCSI_COMMAND, bval); + } + } + /*DC395x_monitor_next_IRQ = 2; */ + /*printk (" done\n"); */ +} + + +/* + ******************************************************************** + * scsiio + * DC395x_StatusPhase0: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =3 + ******************************************************************** + */ +static void +DC395x_StatusPhase0(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": StatusPhase0 (pid %li)\n", + pSRB->pcmd->pid); +#endif + TRACEPRINTF("STP0 *"); + pSRB->TargetStatus = DC395x_read8(TRM_S1040_SCSI_FIFO); + pSRB->EndMessage = DC395x_read8(TRM_S1040_SCSI_FIFO); /* get message */ + pSRB->SRBState = SRB_COMPLETED; + *pscsi_status = PH_BUS_FREE; /*.. initial phase */ + /*1.25 */ + /*DC395x_clrfifo (pACB, "STP0"); */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + /* + ** SCSI command + */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_StatusPhase1: one of DC395x_SCSI_phase1[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase1[phase] + * if phase =3 + ******************************************************************** + */ +static void +DC395x_StatusPhase1(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": StatusPhase1 (pid=%li)\n", + pSRB->pcmd->pid); +#endif + TRACEPRINTF("STP1 *"); + /* Cleanup is now done at the end of DataXXPhase0 */ + /*DC395x_cleanup_after_transfer (pACB, pSRB); */ + + pSRB->SRBState = SRB_STATUS; + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + /* + * SCSI command + */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_COMP); +} + +/* Message handling */ + +#if 0 +/* Print received message */ +static void DC395x_printMsg(u8 * MsgBuf, u32 len) +{ + int i; + printk(" %02x", MsgBuf[0]); + for (i = 1; i < len; i++) + printk(" %02x", MsgBuf[i]); + printk("\n"); +} +#endif + +/* Check if the message is complete */ +static inline u8 DC395x_MsgIn_complete(u8 * msgbuf, u32 len) +{ + if (*msgbuf == EXTENDED_MESSAGE) { + if (len < 2) + return 0; + if (len < msgbuf[1] + 2) + return 0; + } else if (*msgbuf >= 0x20 && *msgbuf <= 0x2f) /* two byte messages */ + if (len < 2) + return 0; + return 1; +} + +#define DC395x_ENABLE_MSGOUT \ + DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_SETATN); \ + pSRB->SRBState |= SRB_MSGOUT + + +/* reject_msg */ +static inline void +DC395x_MsgIn_reject(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + pSRB->MsgOutBuf[0] = MESSAGE_REJECT; + pSRB->MsgCnt = 1; + DC395x_ENABLE_MSGOUT; + pSRB->SRBState &= ~SRB_MSGIN; + pSRB->SRBState |= SRB_MSGOUT; + printk(KERN_INFO DC395X_NAME + ": Reject message %02x from %02i-%i\n", pSRB->MsgInBuf[0], + pSRB->pSRBDCB->TargetID, pSRB->pSRBDCB->TargetLUN); + TRACEPRINTF("\\*"); +} + + +/* abort command */ +static inline void +DC395x_EnableMsgOut_Abort(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB) +{ + pSRB->MsgOutBuf[0] = ABORT; + pSRB->MsgCnt = 1; + DC395x_ENABLE_MSGOUT; + pSRB->SRBState &= ~SRB_MSGIN; + pSRB->SRBState |= SRB_MSGOUT; + /* + if (pSRB->pSRBDCB) + pSRB->pSRBDCB->DCBFlag &= ~ABORT_DEV_; + */ + TRACEPRINTF("#*"); +} + + +static struct ScsiReqBlk *DC395x_MsgIn_QTag(struct AdapterCtlBlk *pACB, + struct DeviceCtlBlk *pDCB, + u8 tag) +{ + struct ScsiReqBlk *lastSRB = pDCB->pGoingLast; + struct ScsiReqBlk *pSRB = pDCB->pGoingSRB; +#ifdef DC395x_DEBUG0 + printk(DC395X_NAME ": QTag Msg (SRB %p): %i ", pSRB, tag); +#endif + if (!(pDCB->TagMask & (1 << tag))) + printk(DC395X_NAME + ": MsgIn_QTag: TagMask (%08x) does not reserve tag %i!\n", + pDCB->TagMask, tag); + + if (!pSRB) + goto mingx0; + while (pSRB) { + if (pSRB->TagNumber == tag) + break; + if (pSRB == lastSRB) + goto mingx0; + pSRB = pSRB->pNextSRB; + } +#ifdef DC395x_DEBUG0 + printk("pid %li (%i-%i)\n", pSRB->pcmd->pid, + pSRB->pSRBDCB->TargetID, pSRB->pSRBDCB->TargetLUN); +#endif + if (pDCB->DCBFlag & ABORT_DEV_) { + /*pSRB->SRBState = SRB_ABORT_SENT; */ + DC395x_EnableMsgOut_Abort(pACB, pSRB); + } + + if (!(pSRB->SRBState & SRB_DISCONNECT)) + goto mingx0; + + /* Tag found */ + TRACEPRINTF("[%s]*", pDCB->pActiveSRB->debugtrace); + TRACEPRINTF("RTag*"); + /* Just for debugging ... */ + lastSRB = pSRB; + pSRB = pDCB->pActiveSRB; + TRACEPRINTF("Found.*"); + pSRB = lastSRB; + + memcpy(pSRB->MsgInBuf, pDCB->pActiveSRB->MsgInBuf, pACB->MsgLen); + pSRB->SRBState |= pDCB->pActiveSRB->SRBState; + pSRB->SRBState |= SRB_DATA_XFER; + pDCB->pActiveSRB = pSRB; + /* How can we make the DORS happy? */ + return pSRB; + + mingx0: + pSRB = pACB->pTmpSRB; + pSRB->SRBState = SRB_UNEXPECT_RESEL; + pDCB->pActiveSRB = pSRB; + pSRB->MsgOutBuf[0] = MSG_ABORT_TAG; + pSRB->MsgCnt = 1; + DC395x_ENABLE_MSGOUT; + TRACEPRINTF("?*"); + printk(DC395X_NAME ": Unknown tag received: %i: abort !!\n", tag); + return pSRB; +} + + +/* Reprogram registers */ +static inline void +DC395x_reprog(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB) +{ + DC395x_write8(TRM_S1040_SCSI_TARGETID, pDCB->TargetID); + DC395x_write8(TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod); + DC395x_write8(TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset); + DC395x_SetXferRate(pACB, pDCB); +} + + +/* set async transfer mode */ +static void +DC395x_MsgIn_set_async(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + struct DeviceCtlBlk *pDCB = pSRB->pSRBDCB; + printk(DC395X_NAME ": Target %02i: No sync transfers\n", + pDCB->TargetID); + TRACEPRINTF("!S *"); + pDCB->SyncMode &= ~(SYNC_NEGO_ENABLE); + pDCB->SyncMode |= SYNC_NEGO_DONE; + /*pDCB->SyncPeriod &= 0; */ + pDCB->SyncOffset = 0; + pDCB->MinNegoPeriod = 200 >> 2; /* 200ns <=> 5 MHz */ + pSRB->SRBState &= ~SRB_DO_SYNC_NEGO; + DC395x_reprog(pACB, pDCB); + if ((pDCB->SyncMode & WIDE_NEGO_ENABLE) + && !(pDCB->SyncMode & WIDE_NEGO_DONE)) { + DC395x_Build_WDTR(pACB, pDCB, pSRB); + DC395x_ENABLE_MSGOUT; + DEBUG0(printk + (DC395X_NAME ": SDTR(rej): Try WDTR anyway ...\n"); + ) + } +} + + +/* set sync transfer mode */ +static void +DC395x_MsgIn_set_sync(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + u8 bval; + int fact; + struct DeviceCtlBlk *pDCB = pSRB->pSRBDCB; + /*u8 oldsyncperiod = pDCB->SyncPeriod; */ + /*u8 oldsyncoffset = pDCB->SyncOffset; */ + +#ifdef DC395x_DEBUG1 + printk(KERN_INFO DC395X_NAME + ": Target %02i: Sync: %ins (%02i.%01i MHz) Offset %i\n", + pDCB->TargetID, pSRB->MsgInBuf[3] << 2, + (250 / pSRB->MsgInBuf[3]), + ((250 % pSRB->MsgInBuf[3]) * 10) / pSRB->MsgInBuf[3], + pSRB->MsgInBuf[4]); +#endif + + if (pSRB->MsgInBuf[4] > 15) + pSRB->MsgInBuf[4] = 15; + if (!(pDCB->DevMode & NTC_DO_SYNC_NEGO)) + pDCB->SyncOffset = 0; + else if (pDCB->SyncOffset == 0) + pDCB->SyncOffset = pSRB->MsgInBuf[4]; + if (pSRB->MsgInBuf[4] > pDCB->SyncOffset) + pSRB->MsgInBuf[4] = pDCB->SyncOffset; + else + pDCB->SyncOffset = pSRB->MsgInBuf[4]; + bval = 0; + while (bval < 7 && (pSRB->MsgInBuf[3] > dc395x_clock_period[bval] + || pDCB->MinNegoPeriod > + dc395x_clock_period[bval])) + bval++; + if (pSRB->MsgInBuf[3] < dc395x_clock_period[bval]) + printk(KERN_INFO DC395X_NAME + ": Increase sync nego period to %ins\n", + dc395x_clock_period[bval] << 2); + pSRB->MsgInBuf[3] = dc395x_clock_period[bval]; + pDCB->SyncPeriod &= 0xf0; + pDCB->SyncPeriod |= ALT_SYNC | bval; + pDCB->MinNegoPeriod = pSRB->MsgInBuf[3]; + + if (pDCB->SyncPeriod & WIDE_SYNC) + fact = 500; + else + fact = 250; + + printk(KERN_INFO DC395X_NAME + ": Target %02i: %s Sync: %ins Offset %i (%02i.%01i MB/s)\n", + pDCB->TargetID, (fact == 500) ? "Wide16" : "", + pDCB->MinNegoPeriod << 2, pDCB->SyncOffset, + (fact / pDCB->MinNegoPeriod), + ((fact % pDCB->MinNegoPeriod) * 10 + + pDCB->MinNegoPeriod / 2) / pDCB->MinNegoPeriod); + + TRACEPRINTF("S%i *", pDCB->MinNegoPeriod << 2); + if (!(pSRB->SRBState & SRB_DO_SYNC_NEGO)) { + /* Reply with corrected SDTR Message */ + printk(DC395X_NAME ": .. answer w/ %ins %i\n", + pSRB->MsgInBuf[3] << 2, pSRB->MsgInBuf[4]); + + memcpy(pSRB->MsgOutBuf, pSRB->MsgInBuf, 5); + pSRB->MsgCnt = 5; + DC395x_ENABLE_MSGOUT; + pDCB->SyncMode |= SYNC_NEGO_DONE; + } else { + if ((pDCB->SyncMode & WIDE_NEGO_ENABLE) + && !(pDCB->SyncMode & WIDE_NEGO_DONE)) { + DC395x_Build_WDTR(pACB, pDCB, pSRB); + DC395x_ENABLE_MSGOUT; + DEBUG0(printk + (DC395X_NAME ": SDTR: Also try WDTR ...\n"); + ) + } + } + pSRB->SRBState &= ~SRB_DO_SYNC_NEGO; + pDCB->SyncMode |= SYNC_NEGO_DONE | SYNC_NEGO_ENABLE; + + DC395x_reprog(pACB, pDCB); +} + + +static inline void +DC395x_MsgIn_set_nowide(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB) +{ + struct DeviceCtlBlk *pDCB = pSRB->pSRBDCB; +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME ": WDTR got rejected from target %02i\n", + pDCB->TargetID); +#endif + TRACEPRINTF("!W *"); + pDCB->SyncPeriod &= ~WIDE_SYNC; + pDCB->SyncMode &= ~(WIDE_NEGO_ENABLE); + pDCB->SyncMode |= WIDE_NEGO_DONE; + pSRB->SRBState &= ~SRB_DO_WIDE_NEGO; + DC395x_reprog(pACB, pDCB); + if ((pDCB->SyncMode & SYNC_NEGO_ENABLE) + && !(pDCB->SyncMode & SYNC_NEGO_DONE)) { + DC395x_Build_SDTR(pACB, pDCB, pSRB); + DC395x_ENABLE_MSGOUT; + DEBUG0(printk + (DC395X_NAME ": WDTR(rej): Try SDTR anyway ...\n"); + ) + } +} + +static void +DC395x_MsgIn_set_wide(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + struct DeviceCtlBlk *pDCB = pSRB->pSRBDCB; + u8 wide = (pDCB->DevMode & NTC_DO_WIDE_NEGO + && pACB->Config & HCC_WIDE_CARD) ? 1 : 0; + if (pSRB->MsgInBuf[3] > wide) + pSRB->MsgInBuf[3] = wide; + /* Completed */ + if (!(pSRB->SRBState & SRB_DO_WIDE_NEGO)) { + printk(DC395X_NAME + ": Target %02i initiates Wide Nego ...\n", + pDCB->TargetID); + memcpy(pSRB->MsgOutBuf, pSRB->MsgInBuf, 4); + pSRB->MsgCnt = 4; + pSRB->SRBState |= SRB_DO_WIDE_NEGO; + DC395x_ENABLE_MSGOUT; + } + + pDCB->SyncMode |= (WIDE_NEGO_ENABLE | WIDE_NEGO_DONE); + if (pSRB->MsgInBuf[3] > 0) + pDCB->SyncPeriod |= WIDE_SYNC; + else + pDCB->SyncPeriod &= ~WIDE_SYNC; + pSRB->SRBState &= ~SRB_DO_WIDE_NEGO; + TRACEPRINTF("W%i *", (pDCB->SyncPeriod & WIDE_SYNC ? 1 : 0)); + /*pDCB->SyncMode &= ~(WIDE_NEGO_ENABLE+WIDE_NEGO_DONE); */ +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Wide transfers (%i bit) negotiated with target %02i\n", + (8 << pSRB->MsgInBuf[3]), pDCB->TargetID); +#endif + DC395x_reprog(pACB, pDCB); + if ((pDCB->SyncMode & SYNC_NEGO_ENABLE) + && !(pDCB->SyncMode & SYNC_NEGO_DONE)) { + DC395x_Build_SDTR(pACB, pDCB, pSRB); + DC395x_ENABLE_MSGOUT; + DEBUG0(printk(DC395X_NAME ": WDTR: Also try SDTR ...\n"); + ) + } +} + + +/* + ******************************************************************** + * scsiio + * DC395x_MsgInPhase0: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =7 + * + * extended message codes: + * + * code description + * + * 02h Reserved + * 00h MODIFY DATA POINTER + * 01h SYNCHRONOUS DATA TRANSFER REQUEST + * 03h WIDE DATA TRANSFER REQUEST + * 04h - 7Fh Reserved + * 80h - FFh Vendor specific + * + ******************************************************************** + */ +void +DC395x_MsgInPhase0(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + struct DeviceCtlBlk *pDCB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_MsgInPhase0..............\n "); +#endif + TRACEPRINTF("MIP0*"); + pDCB = pACB->pActiveDCB; + + pSRB->MsgInBuf[pACB->MsgLen++] = DC395x_read8(TRM_S1040_SCSI_FIFO); + if (DC395x_MsgIn_complete(pSRB->MsgInBuf, pACB->MsgLen)) { + TRACEPRINTF("(%02x)*", pSRB->MsgInBuf[0]); + /*printk (KERN_INFO DC395X_NAME ": MsgIn:"); */ + /*DC395x_printMsg (pSRB->MsgInBuf, pACB->MsgLen); */ + + /* Now eval the msg */ + switch (pSRB->MsgInBuf[0]) { + case DISCONNECT: + pSRB->SRBState = SRB_DISCONNECT; + break; + + case SIMPLE_QUEUE_TAG: + case HEAD_OF_QUEUE_TAG: + case ORDERED_QUEUE_TAG: + TRACEPRINTF("(%02x)*", pSRB->MsgInBuf[1]); + pSRB = + DC395x_MsgIn_QTag(pACB, pDCB, + pSRB->MsgInBuf[1]); + break; + + case MESSAGE_REJECT: + DC395x_write16(TRM_S1040_SCSI_CONTROL, + DO_CLRATN | DO_DATALATCH); + /* A sync nego message was rejected ! */ + if (pSRB->SRBState & SRB_DO_SYNC_NEGO) { + DC395x_MsgIn_set_async(pACB, pSRB); + break; + } + /* A wide nego message was rejected ! */ + if (pSRB->SRBState & SRB_DO_WIDE_NEGO) { + DC395x_MsgIn_set_nowide(pACB, pSRB); + break; + } + DC395x_EnableMsgOut_Abort(pACB, pSRB); + /*pSRB->SRBState |= SRB_ABORT_SENT */ + break; + + case EXTENDED_MESSAGE: + TRACEPRINTF("(%02x)*", pSRB->MsgInBuf[2]); + /* SDTR */ + if (pSRB->MsgInBuf[1] == 3 + && pSRB->MsgInBuf[2] == EXTENDED_SDTR) { + DC395x_MsgIn_set_sync(pACB, pSRB); + break; + } + /* WDTR */ + if (pSRB->MsgInBuf[1] == 2 && pSRB->MsgInBuf[2] == EXTENDED_WDTR && pSRB->MsgInBuf[3] <= 2) { /* sanity check ... */ + DC395x_MsgIn_set_wide(pACB, pSRB); + break; + } + DC395x_MsgIn_reject(pACB, pSRB); + break; + + /* Discard wide residual */ + case MSG_IGNOREWIDE: + DEBUG0(printk + (DC395X_NAME ": Ignore Wide Residual!\n"); + ) + /*DC395x_write32 (TRM_S1040_SCSI_COUNTER, 1); */ + /*DC395x_read8 (TRM_S1040_SCSI_FIFO); */ + break; + + /* nothing has to be done */ + case COMMAND_COMPLETE: + break; + + /* + * SAVE POINTER may be ignored as we have the struct ScsiReqBlk* associated with the + * scsi command. Thanks, Gérard, for pointing it out. + */ + case SAVE_POINTERS: +#ifdef DC395x_DEBUG0 + printk(DC395X_NAME + ": SAVE POINTER message received (pid %li: rem.%i) ... ignore :-(\n", + pSRB->pcmd->pid, pSRB->SRBTotalXferLength); +#endif + /*pSRB->Saved_Ptr = pSRB->TotalXferredLen; */ + break; + /* The device might want to restart transfer with a RESTORE */ + case RESTORE_POINTERS: + printk(DC395X_NAME + ": RESTORE POINTER message received ... ignore :-(\n"); + /*dc395x_restore_ptr (pACB, pSRB); */ + break; + case ABORT: + printk(DC395X_NAME + ": ABORT msg received (pid %li %02i-%i)\n", + pSRB->pcmd->pid, pDCB->TargetID, + pDCB->TargetLUN); + pDCB->DCBFlag |= ABORT_DEV_; + DC395x_EnableMsgOut_Abort(pACB, pSRB); + break; + /* reject unknown messages */ + default: + if (pSRB->MsgInBuf[0] & IDENTIFY_BASE) { + printk(DC395X_NAME + ": Identify Message received?\n"); + /*TRACEOUT (" %s\n", pSRB->debugtrace); */ + pSRB->MsgCnt = 1; + pSRB->MsgOutBuf[0] = pDCB->IdentifyMsg; + DC395x_ENABLE_MSGOUT; + pSRB->SRBState |= SRB_MSGOUT; + /*break; */ + } + DC395x_MsgIn_reject(pACB, pSRB); + TRACEOUT(" %s\n", pSRB->debugtrace); + } + TRACEPRINTF(".*"); + + /* Clear counter and MsgIn state */ + pSRB->SRBState &= ~SRB_MSGIN; + pACB->MsgLen = 0; + } + + /*1.25 */ + if ((*pscsi_status & PHASEMASK) != PH_MSG_IN) +#if 0 + DC395x_clrfifo(pACB, "MIP0_"); +#else + TRACEPRINTF("N/Cln *"); +#endif + *pscsi_status = PH_BUS_FREE; + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important ... you know! */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_MsgInPhase1: one of DC395x_SCSI_phase1[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase1[phase] + * if phase =7 + ******************************************************************** + */ +static void +DC395x_MsgInPhase1(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_MsgInPhase1..............\n "); +#endif + TRACEPRINTF("MIP1 *"); + DC395x_clrfifo(pACB, "MIP1"); + DC395x_write32(TRM_S1040_SCSI_COUNTER, 1); + if (!(pSRB->SRBState & SRB_MSGIN)) { + pSRB->SRBState &= ~SRB_DISCONNECT; + pSRB->SRBState |= SRB_MSGIN; + } + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + /* + * SCSI command + */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_FIFO_IN); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_Nop0: one of DC395x_SCSI_phase1[] ,DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * DC395x_stateV = (void *)DC395x_SCSI_phase1[phase] + * if phase =4 ..PH_BUS_FREE + ******************************************************************** + */ +static void +DC395x_Nop0(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + /*TRACEPRINTF("NOP0 *"); */ +} + + +/* + ******************************************************************** + * scsiio + * DC395x_Nop1: one of DC395x_SCSI_phase0[] ,DC395x_SCSI_phase1[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * DC395x_stateV = (void *)DC395x_SCSI_phase1[phase] + * if phase =5 + ******************************************************************** + */ +static void +DC395x_Nop1(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + /*TRACEPRINTF("NOP1 *"); */ +} + + +/* + ******************************************************************** + * scsiio + * DC395x_MsgInPhase0 + ******************************************************************** + */ +static void +DC395x_SetXferRate(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB) +{ + u8 bval; + u16 cnt, i; + struct DeviceCtlBlk *pDCBTemp; + + /* + ** set all lun device's period , offset + */ + if (!(pDCB->IdentifyMsg & 0x07)) { + if (pACB->scan_devices) + DC395x_CurrSyncOffset = pDCB->SyncOffset; + else { + pDCBTemp = pACB->pLinkDCB; + cnt = pACB->DCBCnt; + bval = pDCB->TargetID; + for (i = 0; i < cnt; i++) { + if (pDCBTemp->TargetID == bval) { + pDCBTemp->SyncPeriod = + pDCB->SyncPeriod; + pDCBTemp->SyncOffset = + pDCB->SyncOffset; + pDCBTemp->SyncMode = + pDCB->SyncMode; + pDCBTemp->MinNegoPeriod = + pDCB->MinNegoPeriod; + } + pDCBTemp = pDCBTemp->pNextDCB; + } + } + } + return; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_Interrupt + ******************************************************************** + */ +void DC395x_Disconnect(struct AdapterCtlBlk *pACB) +{ + struct DeviceCtlBlk *pDCB; + struct ScsiReqBlk *pSRB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": Disconnect (pid=%li)\n", + pACB->pActiveDCB->pActiveSRB->pcmd->pid); +#endif + pDCB = pACB->pActiveDCB; + if (!pDCB) { + printk(KERN_ERR DC395X_NAME + ": Disc: Exception Disconnect pDCB=NULL !!\n "); + udelay(500); + /* Suspend queue for a while */ + pACB->pScsiHost->last_reset = + jiffies + HZ / 2 + + HZ * + dc395x_trm_eepromBuf[pACB->AdapterIndex]. + NvramDelayTime; + DC395x_clrfifo(pACB, "DiscEx"); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_HWRESELECT); + return; + } + pSRB = pDCB->pActiveSRB; + pACB->pActiveDCB = 0; + TRACEPRINTF("DISC *"); + + pSRB->ScsiPhase = PH_BUS_FREE; /* initial phase */ + DC395x_clrfifo(pACB, "Disc"); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_HWRESELECT); + if (pSRB->SRBState & SRB_UNEXPECT_RESEL) { + printk(KERN_ERR DC395X_NAME + ": Disc: Unexpected Reselection (%i-%i)\n", + pDCB->TargetID, pDCB->TargetLUN); + pSRB->SRBState = 0; + DC395x_Waiting_process(pACB); + } else if (pSRB->SRBState & SRB_ABORT_SENT) { + /*Scsi_Cmnd* pcmd = pSRB->pcmd; */ + pDCB->DCBFlag &= ~ABORT_DEV_; + pACB->pScsiHost->last_reset = jiffies + HZ / 2 + 1; + printk(KERN_ERR DC395X_NAME ": Disc: SRB_ABORT_SENT!\n"); + DC395x_DoingSRB_Done(pACB, DID_ABORT, pSRB->pcmd, 1); + DC395x_Query_to_Waiting(pACB); + DC395x_Waiting_process(pACB); + } else { + if ((pSRB->SRBState & (SRB_START_ + SRB_MSGOUT)) + || !(pSRB-> + SRBState & (SRB_DISCONNECT + SRB_COMPLETED))) { + /* + * Selection time out + * SRB_START_ || SRB_MSGOUT || (!SRB_DISCONNECT && !SRB_COMPLETED) + */ + /* Unexp. Disc / Sel Timeout */ + if (pSRB->SRBState != SRB_START_ + && pSRB->SRBState != SRB_MSGOUT) { + pSRB->SRBState = SRB_READY; + printk(DC395X_NAME + ": Unexpected Disconnection (pid %li)!\n", + pSRB->pcmd->pid); + pSRB->TargetStatus = SCSI_STAT_SEL_TIMEOUT; + TRACEPRINTF("UnExpD *"); + TRACEOUT("%s\n", pSRB->debugtrace); + goto disc1; + } else { + /* Normal selection timeout */ + TRACEPRINTF("SlTO *"); +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Disc: SelTO (pid=%li) for dev %02i-%i\n", + pSRB->pcmd->pid, pDCB->TargetID, + pDCB->TargetLUN); +#endif + if (pSRB->RetryCnt++ > DC395x_MAX_RETRIES + || pACB->scan_devices) { + pSRB->TargetStatus = + SCSI_STAT_SEL_TIMEOUT; + goto disc1; + } + DC395x_freetag(pDCB, pSRB); + DC395x_Going_to_Waiting(pDCB, pSRB); +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME ": Retry pid %li ...\n", + pSRB->pcmd->pid); +#endif + DC395x_waiting_timer(pACB, HZ / 20); + } + } else if (pSRB->SRBState & SRB_DISCONNECT) { + u8 bval = DC395x_read8(TRM_S1040_SCSI_SIGNAL); + /* + * SRB_DISCONNECT (This is what we expect!) + */ + /* printk (DC395X_NAME ": DoWaitingSRB (pid=%li)\n", pSRB->pcmd->pid); */ + TRACEPRINTF("+*"); + if (bval & 0x40) { + DEBUG0(printk + (DC395X_NAME + ": Debug: DISC: SCSI bus stat %02x: ACK set! Other controllers?\n", + bval); + ) + /* It could come from another initiator, therefore don't do much ! */ + TRACEPRINTF("ACK(%02x) *", bval); + /*DC395x_dumpinfo (pACB, pDCB, pSRB); */ + /*TRACEOUT (" %s\n", pSRB->debugtrace); */ + /*pDCB->DCBFlag |= ABORT_DEV_; */ + /*DC395x_EnableMsgOut_Abort (pACB, pSRB); */ + /*DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_CLRFIFO | DO_CLRATN | DO_HWRESELECT); */ + } else + DC395x_Waiting_process(pACB); + } else if (pSRB->SRBState & SRB_COMPLETED) { + disc1: + /* + ** SRB_COMPLETED + */ + DC395x_freetag(pDCB, pSRB); + pDCB->pActiveSRB = 0; + pSRB->SRBState = SRB_FREE; + /*printk (DC395X_NAME ": done (pid=%li)\n", pSRB->pcmd->pid); */ + DC395x_SRBdone(pACB, pDCB, pSRB); + } + } + return; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_Reselect + ******************************************************************** + */ +void DC395x_Reselect(struct AdapterCtlBlk *pACB) +{ + struct DeviceCtlBlk *pDCB; + struct ScsiReqBlk *pSRB = 0; + u16 RselTarLunId; + u8 id, lun; + u8 arblostflag = 0; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_Reselect..............\n "); +#endif + + DC395x_clrfifo(pACB, "Resel"); + /*DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_HWRESELECT | DO_DATALATCH); */ + /* Read Reselected Target ID and LUN */ + RselTarLunId = DC395x_read16(TRM_S1040_SCSI_TARGETID); + pDCB = pACB->pActiveDCB; + if (pDCB) { /* Arbitration lost but Reselection win */ + pSRB = pDCB->pActiveSRB; + if (!pSRB) { + printk(DC395X_NAME + ": Arb lost Resel won, but pActiveSRB == 0!\n"); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + return; + } + /* Why the if ? */ + if (!(pACB->scan_devices)) { +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Arb lost but Resel win pid %li (%02i-%i) Rsel %04x Stat %04x\n", + pSRB->pcmd->pid, pDCB->TargetID, + pDCB->TargetLUN, RselTarLunId, + DC395x_read16(TRM_S1040_SCSI_STATUS)); +#endif + TRACEPRINTF("ArbLResel!*"); + /*TRACEOUT (" %s\n", pSRB->debugtrace); */ + arblostflag = 1; + /*pSRB->SRBState |= SRB_DISCONNECT; */ + + pSRB->SRBState = SRB_READY; + DC395x_freetag(pDCB, pSRB); + DC395x_Going_to_Waiting(pDCB, pSRB); + DC395x_waiting_timer(pACB, HZ / 20); + + /* return; */ + } + } + /* Read Reselected Target Id and LUN */ + if (!(RselTarLunId & (IDENTIFY_BASE << 8))) + printk(DC395X_NAME + ": Resel expects identify msg! Got %04x!\n", + RselTarLunId); + id = RselTarLunId & 0xff; + lun = (RselTarLunId >> 8) & 7; + pDCB = DC395x_findDCB(pACB, id, lun); + if (!pDCB) { + printk(KERN_ERR DC395X_NAME + ": Reselect from non existing device (%02i-%i)\n", + id, lun); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + return; + } + + pACB->pActiveDCB = pDCB; + + if (!(pDCB->DevMode & NTC_DO_DISCONNECT)) + printk(DC395X_NAME + ": Reselection in spite of forbidden disconnection? (%02i-%i)\n", + pDCB->TargetID, pDCB->TargetLUN); + + if ((pDCB->SyncMode & EN_TAG_QUEUEING) /*&& !arblostflag */ ) { + struct ScsiReqBlk *oldSRB = pSRB; + pSRB = pACB->pTmpSRB; +#ifdef DC395x_DEBUGTRACE + pSRB->debugpos = 0; + pSRB->debugtrace[0] = 0; +#endif + pDCB->pActiveSRB = pSRB; + if (oldSRB) + TRACEPRINTF("ArbLResel(%li):*", oldSRB->pcmd->pid); + /*if (arblostflag) printk (DC395X_NAME ": Reselect: Wait for Tag ... \n"); */ + } else { + /* There can be only one! */ + pSRB = pDCB->pActiveSRB; + if (pSRB) + TRACEPRINTF("RSel *"); + if (!pSRB || !(pSRB->SRBState & SRB_DISCONNECT)) { + /* + * abort command + */ + printk(DC395X_NAME + ": Reselected w/o disconnected cmds from %02i-%i?\n", + pDCB->TargetID, pDCB->TargetLUN); + pSRB = pACB->pTmpSRB; + pSRB->SRBState = SRB_UNEXPECT_RESEL; + pDCB->pActiveSRB = pSRB; + DC395x_EnableMsgOut_Abort(pACB, pSRB); + } else { + if (pDCB->DCBFlag & ABORT_DEV_) { + /*pSRB->SRBState = SRB_ABORT_SENT; */ + DC395x_EnableMsgOut_Abort(pACB, pSRB); + } else + pSRB->SRBState = SRB_DATA_XFER; + + } + /*if (arblostflag) TRACEOUT (" %s\n", pSRB->debugtrace); */ + } + pSRB->ScsiPhase = PH_BUS_FREE; /* initial phase */ + /* + *********************************************** + ** Program HA ID, target ID, period and offset + *********************************************** + */ + DC395x_write8(TRM_S1040_SCSI_HOSTID, pACB->pScsiHost->this_id); /* host ID */ + DC395x_write8(TRM_S1040_SCSI_TARGETID, pDCB->TargetID); /* target ID */ + DC395x_write8(TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset); /* offset */ + DC395x_write8(TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod); /* sync period, wide */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + /* SCSI command */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT); +} + + +/* Dynamic device handling */ + +/* Remove dev (and DCB) */ +static void +DC395x_remove_dev(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB) +{ + struct DeviceCtlBlk *pPrevDCB = pACB->pLinkDCB; + + if (pDCB->GoingSRBCnt > 1) { + DCBDEBUG(printk + (KERN_INFO DC395X_NAME + ": Driver won't free DCB (ID %i, LUN %i): 0x%08x because of SRBCnt %i\n", + pDCB->TargetID, pDCB->TargetLUN, (int) pDCB, + pDCB->GoingSRBCnt); + ) + return; + } + pACB->DCBmap[pDCB->TargetID] &= ~(1 << pDCB->TargetLUN); + pACB->children[pDCB->TargetID][pDCB->TargetLUN] = NULL; + + /* The first one */ + if (pDCB == pACB->pLinkDCB) { + /* The last one */ + if (pACB->pLastDCB == pDCB) { + pDCB->pNextDCB = 0; + pACB->pLastDCB = 0; + } + pACB->pLinkDCB = pDCB->pNextDCB; + } else { + while (pPrevDCB->pNextDCB != pDCB) + pPrevDCB = pPrevDCB->pNextDCB; + pPrevDCB->pNextDCB = pDCB->pNextDCB; + if (pDCB == pACB->pLastDCB) + pACB->pLastDCB = pPrevDCB; + } + + DCBDEBUG(printk + (KERN_INFO DC395X_NAME + ": Driver about to free DCB (ID %i, LUN %i): %p\n", + pDCB->TargetID, pDCB->TargetLUN, pDCB); + ) + if (pDCB == pACB->pActiveDCB) + pACB->pActiveDCB = 0; + if (pDCB == pACB->pLinkDCB) + pACB->pLinkDCB = pDCB->pNextDCB; + if (pDCB == pACB->pDCBRunRobin) + pACB->pDCBRunRobin = pDCB->pNextDCB; + pACB->DCBCnt--; + KFREE(pDCB); + /* pACB->DeviceCnt--; */ +} + + +static inline u8 DC395x_tagq_blacklist(char *name) +{ +#ifndef DC395x_NO_TAGQ +#if 0 + u8 i; + for (i = 0; i < BADDEVCNT; i++) + if (memcmp(name, DC395x_baddevname1[i], 28) == 0) + return 1; +#endif + return 0; +#else + return 1; +#endif +} + + +static void +DC395x_disc_tagq_set(struct DeviceCtlBlk *pDCB, struct ScsiInqData *ptr) +{ + /* Check for SCSI format (ANSI and Response data format) */ + if ((ptr->Vers & 0x07) >= 2 || (ptr->RDF & 0x0F) == 2) { + if ((ptr->Flags & SCSI_INQ_CMDQUEUE) + && (pDCB->DevMode & NTC_DO_TAG_QUEUEING) && + /*(pDCB->DevMode & NTC_DO_DISCONNECT) */ + /* ((pDCB->DevType == TYPE_DISK) + || (pDCB->DevType == TYPE_MOD)) && */ + !DC395x_tagq_blacklist(((char *) ptr) + 8)) { + if (pDCB->MaxCommand == 1) + pDCB->MaxCommand = + pDCB->pDCBACB->TagMaxNum; + pDCB->SyncMode |= EN_TAG_QUEUEING; + /*pDCB->TagMask = 0; */ + } else + pDCB->MaxCommand = 1; + } +} + + +static void +DC395x_add_dev(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiInqData *ptr) +{ + u8 bval1 = ptr->DevType & SCSI_DEVTYPE; + pDCB->DevType = bval1; + /* if (bval1 == TYPE_DISK || bval1 == TYPE_MOD) */ + DC395x_disc_tagq_set(pDCB, ptr); +} + + +/* + ******************************************************************** + * unmap mapped pci regions from SRB + ******************************************************************** + */ +static void +DC395x_pci_unmap(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + int dir; + Scsi_Cmnd *pcmd = pSRB->pcmd; + dir = scsi_to_pci_dma_dir(pcmd->sc_data_direction); + if (pcmd->use_sg && dir != PCI_DMA_NONE) { + /* unmap DC395x SG list */ +#ifdef DC395x_SGPARANOIA + printk(DC395X_NAME + ": Unmap SG descriptor list %08x (%05x)\n", + pSRB->SRBSGBusAddr, + sizeof(struct SGentry) * DC395x_MAX_SG_LISTENTRY); +#endif + pci_unmap_single(pACB->pdev, pSRB->SRBSGBusAddr, + sizeof(struct SGentry) * + DC395x_MAX_SG_LISTENTRY, + PCI_DMA_TODEVICE); +#ifdef DC395x_SGPARANOIA + printk(DC395X_NAME ": Unmap %i SG segments from %p\n", + pcmd->use_sg, pcmd->request_buffer); +#endif + /* unmap the sg segments */ + pci_unmap_sg(pACB->pdev, + (struct scatterlist *) pcmd->request_buffer, + pcmd->use_sg, dir); + } else if (pcmd->request_buffer && dir != PCI_DMA_NONE) { +#ifdef DC395x_SGPARANOIA + printk(DC395X_NAME ": Unmap buffer at %08x (%05x)\n", + pSRB->SegmentX[0].address, pcmd->request_bufflen); +#endif + pci_unmap_single(pACB->pdev, pSRB->SegmentX[0].address, + pcmd->request_bufflen, dir); + } +} + + +/* + ******************************************************************** + * unmap mapped pci sense buffer from SRB + ******************************************************************** + */ +static void +DC395x_pci_unmap_sense(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + if (!(pSRB->SRBFlag & AUTO_REQSENSE)) + return; + /* Unmap sense buffer */ +#ifdef DC395x_SGPARANOIA + printk(DC395X_NAME ": Unmap sense buffer from %08x (%05x)\n", + pSRB->SegmentX[0].address, sizeof(pcmd->sense_buffer)); +#endif + pci_unmap_single(pACB->pdev, pSRB->SegmentX[0].address, + pSRB->SegmentX[0].length, PCI_DMA_FROMDEVICE); + /* Restore SG stuff */ + /*printk ("Auto_ReqSense finished: Restore Counters ...\n"); */ + pSRB->SRBTotalXferLength = pSRB->Xferred; + pSRB->SegmentX[0].address = + pSRB->SegmentX[DC395x_MAX_SG_LISTENTRY - 1].address; + pSRB->SegmentX[0].length = + pSRB->SegmentX[DC395x_MAX_SG_LISTENTRY - 1].length; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_Disconnected + * Complete execution of a SCSI command + * Signal completion to the generic SCSI driver + ******************************************************************** + */ +void +DC395x_SRBdone(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB) +{ + u8 tempcnt, status; + Scsi_Cmnd *pcmd; + struct ScsiInqData *ptr; + /*u32 drv_flags=0; */ + int dir; + + pcmd = pSRB->pcmd; + TRACEPRINTF("DONE *"); + + dir = scsi_to_pci_dma_dir(pcmd->sc_data_direction); + ptr = (struct ScsiInqData *) (pcmd->request_buffer); + if (pcmd->use_sg) + ptr = + (struct ScsiInqData *) CPU_ADDR(*(struct scatterlist *) + ptr); +#ifdef DC395x_SGPARANOIA + printk(KERN_INFO DC395X_NAME + ": SRBdone SG=%i (%i/%i), req_buf = %p, adr = %p\n", + pcmd->use_sg, pSRB->SRBSGIndex, pSRB->SRBSGCount, + pcmd->request_buffer, ptr); +#endif +#ifdef DC395x_DEBUG_KG + printk(KERN_INFO DC395X_NAME + ": SRBdone (pid %li, target %02i-%i): ", pSRB->pcmd->pid, + pSRB->pcmd->device->id, pSRB->pcmd->device->lun); +#endif + status = pSRB->TargetStatus; + if (pSRB->SRBFlag & AUTO_REQSENSE) { +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "AUTO_REQSENSE1..............\n "); +#endif + DC395x_pci_unmap_sense(pACB, pSRB); + /* + ** target status.......................... + */ + pSRB->SRBFlag &= ~AUTO_REQSENSE; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = CHECK_CONDITION << 1; +#ifdef DC395x_DEBUG_KG + switch (pcmd->sense_buffer[2] & 0x0f) { + case NOT_READY: + printk + ("\nDC395x: ReqSense: NOT_READY (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, + pDCB->TargetLUN, status, pACB->scan_devices); + break; + case UNIT_ATTENTION: + printk + ("\nDC395x: ReqSense: UNIT_ATTENTION (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, + pDCB->TargetLUN, status, pACB->scan_devices); + break; + case ILLEGAL_REQUEST: + printk + ("\nDC395x: ReqSense: ILLEGAL_REQUEST (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, + pDCB->TargetLUN, status, pACB->scan_devices); + break; + case MEDIUM_ERROR: + printk + ("\nDC395x: ReqSense: MEDIUM_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, + pDCB->TargetLUN, status, pACB->scan_devices); + break; + case HARDWARE_ERROR: + printk + ("\nDC395x: ReqSense: HARDWARE_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, + pDCB->TargetLUN, status, pACB->scan_devices); + break; + } + if (pcmd->sense_buffer[7] >= 6) + printk + ("\nDC395x: Sense=%02x, ASC=%02x, ASCQ=%02x (%08x %08x) ", + pcmd->sense_buffer[2], pcmd->sense_buffer[12], + pcmd->sense_buffer[13], + *((unsigned int *) (pcmd->sense_buffer + 3)), + *((unsigned int *) (pcmd->sense_buffer + 8))); + else + printk + ("\nDC395x: Sense=%02x, No ASC/ASCQ (%08x) ", + pcmd->sense_buffer[2], + *((unsigned int *) (pcmd->sense_buffer + 3))); +#endif + + if (status == (CHECK_CONDITION << 1)) { + pcmd->result = DID_BAD_TARGET << 16; + goto ckc_e; + } +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "AUTO_REQSENSE2..............\n "); +#endif + + if ((pSRB->SRBTotalXferLength) + && (pSRB->SRBTotalXferLength >= pcmd->underflow)) + pcmd->result = + MK_RES_LNX(DRIVER_SENSE, DID_OK, + pSRB->EndMessage, CHECK_CONDITION); + /*SET_RES_DID(pcmd->result,DID_OK) */ + else + pcmd->result = + MK_RES_LNX(DRIVER_SENSE, DID_OK, + pSRB->EndMessage, CHECK_CONDITION); + + goto ckc_e; + } + +/*************************************************************/ + if (status) { + /* + * target status.......................... + */ + if (status_byte(status) == CHECK_CONDITION) { + DC395x_RequestSense(pACB, pDCB, pSRB); + return; + } else if (status_byte(status) == QUEUE_FULL) { + tempcnt = (u8) pDCB->GoingSRBCnt; + printk + ("\nDC395x: QUEUE_FULL for dev %02i-%i with %i cmnds\n", + pDCB->TargetID, pDCB->TargetLUN, tempcnt); + if (tempcnt > 1) + tempcnt--; + pDCB->MaxCommand = tempcnt; + DC395x_freetag(pDCB, pSRB); + DC395x_Going_to_Waiting(pDCB, pSRB); + DC395x_waiting_timer(pACB, HZ / 20); + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + return; + } else if (status == SCSI_STAT_SEL_TIMEOUT) { + pSRB->AdaptStatus = H_SEL_TIMEOUT; + pSRB->TargetStatus = 0; + pcmd->result = DID_NO_CONNECT << 16; + } else { + pSRB->AdaptStatus = 0; + SET_RES_DID(pcmd->result, DID_ERROR); + SET_RES_MSG(pcmd->result, pSRB->EndMessage); + SET_RES_TARGET(pcmd->result, status); + + } + } else { + /* + ** process initiator status.......................... + */ + status = pSRB->AdaptStatus; + if (status & H_OVER_UNDER_RUN) { + pSRB->TargetStatus = 0; + SET_RES_DID(pcmd->result, DID_OK); + SET_RES_MSG(pcmd->result, pSRB->EndMessage); + } else if (pSRB->SRBStatus & PARITY_ERROR) { + SET_RES_DID(pcmd->result, DID_PARITY); + SET_RES_MSG(pcmd->result, pSRB->EndMessage); + } else { /* No error */ + + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + SET_RES_DID(pcmd->result, DID_OK); + } + } + + if (dir != PCI_DMA_NONE) { + if (pcmd->use_sg) + pci_dma_sync_sg(pACB->pdev, + (struct scatterlist *) pcmd-> + request_buffer, pcmd->use_sg, dir); + else if (pcmd->request_buffer) + pci_dma_sync_single(pACB->pdev, + pSRB->SegmentX[0].address, + pcmd->request_bufflen, dir); + } + + if ((pcmd->result & RES_DID) == 0 && pcmd->cmnd[0] == INQUIRY + && pcmd->cmnd[2] == 0 && pcmd->request_bufflen >= 8 + && dir != PCI_DMA_NONE && ptr && (ptr->Vers & 0x07) >= 2) + pDCB->Inquiry7 = ptr->Flags; +/* Check Error Conditions */ + ckc_e: + + /*if( pSRB->pcmd->cmnd[0] == INQUIRY && */ + /* (host_byte(pcmd->result) == DID_OK || status_byte(pcmd->result) & CHECK_CONDITION) ) */ + if (pcmd->cmnd[0] == INQUIRY && (pcmd->result == (DID_OK << 16) + || status_byte(pcmd-> + result) & + CHECK_CONDITION)) { + + if (!pDCB->init_TCQ_flag) { + DC395x_add_dev(pACB, pDCB, ptr); + pDCB->init_TCQ_flag = 1; + } + + } + + + /* Here is the info for Doug Gilbert's sg3 ... */ + pcmd->resid = pSRB->SRBTotalXferLength; + /* This may be interpreted by sb. or not ... */ + pcmd->SCp.this_residual = pSRB->SRBTotalXferLength; + pcmd->SCp.buffers_residual = 0; +#ifdef DC395x_DEBUG_KG + if (pSRB->SRBTotalXferLength) + printk + ("\nDC395x: pid %li: %02x (%02i-%i): Missed %i bytes\n", + pcmd->pid, pcmd->cmnd[0], pcmd->device->id, + pcmd->device->lun, pSRB->SRBTotalXferLength); +#endif + + DC395x_Going_remove(pDCB, pSRB, 0); + /* Add to free list */ + if (pSRB == pACB->pTmpSRB) + printk("\nDC395x: ERROR! Completed Cmnd with TmpSRB!\n"); + else + DC395x_Free_insert(pACB, pSRB); + + DEBUG0(printk + (KERN_DEBUG DC395X_NAME ": SRBdone: done pid %li\n", + pcmd->pid); + ) +#ifdef DC395x_DEBUG_KG + printk(" 0x%08x\n", pcmd->result); +#endif + TRACEPRINTF("%08x(%li)*", pcmd->result, jiffies); + DC395x_pci_unmap(pACB, pSRB); + /*DC395x_UNLOCK_ACB_NI; */ + pcmd->scsi_done(pcmd); + /*DC395x_LOCK_ACB_NI; */ + TRACEOUTALL(KERN_INFO " %s\n", pSRB->debugtrace); + + DC395x_Query_to_Waiting(pACB); + DC395x_Waiting_process(pACB); + return; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_reset + * abort all cmds in our queues + ******************************************************************** + */ +void +DC395x_DoingSRB_Done(struct AdapterCtlBlk *pACB, u8 did_flag, + Scsi_Cmnd * cmd, u8 force) +{ + struct DeviceCtlBlk *pDCB; + struct ScsiReqBlk *pSRB; + struct ScsiReqBlk *pSRBTemp; + u16 cnt; + Scsi_Cmnd *pcmd; + + pDCB = pACB->pLinkDCB; + if (!pDCB) + return; + printk(KERN_INFO DC395X_NAME ": DC395x_DoingSRB_Done: pids "); + do { + /* As the ML may queue cmnds again, cache old values */ + struct ScsiReqBlk *pWaitingSRB = pDCB->pWaitingSRB; + /*struct ScsiReqBlk* pWaitLast = pDCB->pWaitLast; */ + u16 WaitSRBCnt = pDCB->WaitSRBCnt; + /* Going queue */ + cnt = pDCB->GoingSRBCnt; + pSRB = pDCB->pGoingSRB; + while (cnt--) { + int result; + int dir; + pSRBTemp = pSRB->pNextSRB; + pcmd = pSRB->pcmd; + dir = scsi_to_pci_dma_dir(pcmd->sc_data_direction); + result = MK_RES(0, did_flag, 0, 0); + /*result = MK_RES(0,DID_RESET,0,0); */ + TRACEPRINTF("Reset(%li):%08x*", jiffies, result); + printk(" (G)"); +#if 1 /*ndef DC395x_DEBUGTRACE */ + printk("%li(%02i-%i) ", pcmd->pid, + pcmd->device->id, pcmd->device->lun); +#endif + TRACEOUT("%s\n", pSRB->debugtrace); + pDCB->pGoingSRB = pSRBTemp; + pDCB->GoingSRBCnt--; + if (!pSRBTemp) + pDCB->pGoingLast = NULL; + DC395x_freetag(pDCB, pSRB); + DC395x_Free_insert(pACB, pSRB); + pcmd->result = result; + DC395x_pci_unmap_sense(pACB, pSRB); + DC395x_pci_unmap(pACB, pSRB); + if (force) { + /* For new EH, we normally don't need to give commands back, + * as they all complete or all time out */ + /* do we need the aic7xxx hack and conditionally decrease retry ? */ + /*DC395x_SCSI_DONE_ACB_UNLOCK; */ + pcmd->scsi_done(pcmd); + /*DC395x_SCSI_DONE_ACB_LOCK; */ + } + pSRB = pSRBTemp; + } + if (pDCB->pGoingSRB) + printk(DC395X_NAME + ": How could the ML send cmnds to the Going queue? (%02i-%i)!!\n", + pDCB->TargetID, pDCB->TargetLUN); + if (pDCB->TagMask) + printk(DC395X_NAME + ": TagMask for %02i-%i should be empty, is %08x!\n", + pDCB->TargetID, pDCB->TargetLUN, + pDCB->TagMask); + /*pDCB->GoingSRBCnt = 0;; */ + /*pDCB->pGoingSRB = NULL; pDCB->pGoingLast = NULL; */ + + /* Waiting queue */ + cnt = WaitSRBCnt; + pSRB = pWaitingSRB; + while (cnt--) { + int result; + pSRBTemp = pSRB->pNextSRB; + pcmd = pSRB->pcmd; + result = MK_RES(0, did_flag, 0, 0); + TRACEPRINTF("Reset(%li):%08x*", jiffies, result); + printk(" (W)"); +#if 1 /*ndef DC395x_DEBUGTRACE */ + printk("%li(%i-%i)", pcmd->pid, pcmd->device->id, + pcmd->device->lun); +#endif + TRACEOUT("%s\n", pSRB->debugtrace); + pDCB->pWaitingSRB = pSRBTemp; + pDCB->WaitSRBCnt--; + if (!pSRBTemp) + pDCB->pWaitLast = NULL; + DC395x_Free_insert(pACB, pSRB); + + pcmd->result = result; + DC395x_pci_unmap_sense(pACB, pSRB); + DC395x_pci_unmap(pACB, pSRB); + if (force) { + /* For new EH, we normally don't need to give commands back, + * as they all complete or all time out */ + /* do we need the aic7xxx hack and conditionally decrease retry ? */ + /*DC395x_SCSI_DONE_ACB_UNLOCK; */ + pcmd->scsi_done(pcmd); + /*DC395x_SCSI_DONE_ACB_LOCK; */ + pSRB = pSRBTemp; + } + } + if (pDCB->WaitSRBCnt) + printk + ("\nDC395x: Debug: ML queued %i cmnds again to %02i-%i\n", + pDCB->WaitSRBCnt, pDCB->TargetID, + pDCB->TargetLUN); + /* The ML could have queued the cmnds again! */ + /*pDCB->WaitSRBCnt = 0;; */ + /*pDCB->pWaitingSRB = NULL; pDCB->pWaitLast = NULL; */ + pDCB->DCBFlag &= ~ABORT_DEV_; + pDCB = pDCB->pNextDCB; + } + while (pDCB != pACB->pLinkDCB && pDCB); + printk("\n"); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_shutdown DC395x_reset + ******************************************************************** + */ +static void DC395x_ResetSCSIBus(struct AdapterCtlBlk *pACB) +{ + /*u32 drv_flags=0; */ + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_ResetSCSIBus..............\n "); +#endif + + /*DC395x_DRV_LOCK(drv_flags); */ + pACB->ACBFlag |= RESET_DEV; /* RESET_DETECT, RESET_DONE, RESET_DEV */ + + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_RSTSCSI); + while (!(DC395x_read8(TRM_S1040_SCSI_INTSTATUS) & INT_SCSIRESET)); + + /*DC395x_DRV_UNLOCK(drv_flags); */ + return; +} + + +/* Set basic config */ +static void DC395x_basic_config(struct AdapterCtlBlk *pACB) +{ + u8 bval; + u16 wval; + DC395x_write8(TRM_S1040_SCSI_TIMEOUT, pACB->sel_timeout); + if (pACB->Config & HCC_PARITY) + bval = PHASELATCH | INITIATOR | BLOCKRST | PARITYCHECK; + else + bval = PHASELATCH | INITIATOR | BLOCKRST; + + DC395x_write8(TRM_S1040_SCSI_CONFIG0, bval); + + /* program configuration 1: Act_Neg (+ Act_Neg_Enh? + Fast_Filter? + DataDis?) */ + DC395x_write8(TRM_S1040_SCSI_CONFIG1, 0x03); /* was 0x13: default */ + /* program Host ID */ + DC395x_write8(TRM_S1040_SCSI_HOSTID, pACB->pScsiHost->this_id); + /* set ansynchronous transfer */ + DC395x_write8(TRM_S1040_SCSI_OFFSET, 0x00); + /* Turn LED control off */ + wval = DC395x_read16(TRM_S1040_GEN_CONTROL) & 0x7F; + DC395x_write16(TRM_S1040_GEN_CONTROL, wval); + /* DMA config */ + wval = DC395x_read16(TRM_S1040_DMA_CONFIG) & ~DMA_FIFO_CTRL; + wval |= + DMA_FIFO_HALF_HALF | DMA_ENHANCE /*| DMA_MEM_MULTI_READ */ ; + /*printk (KERN_INFO DC395X_NAME "DMA_Config: %04x\n", wval); */ + DC395x_write16(TRM_S1040_DMA_CONFIG, wval); + /* Clear pending interrupt status */ + DC395x_read8(TRM_S1040_SCSI_INTSTATUS); + /* Enable SCSI interrupt */ + DC395x_write8(TRM_S1040_SCSI_INTEN, 0x7F); + DC395x_write8(TRM_S1040_DMA_INTEN, EN_SCSIINTR | EN_DMAXFERERROR + /*| EN_DMAXFERABORT | EN_DMAXFERCOMP | EN_FORCEDMACOMP */ + ); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_Interrupt + ******************************************************************** + */ +static void DC395x_ScsiRstDetect(struct AdapterCtlBlk *pACB) +{ + printk(KERN_INFO DC395X_NAME ": DC395x_ScsiRstDetect\n"); + /* delay half a second */ + if (timer_pending(&pACB->Waiting_Timer)) + del_timer(&pACB->Waiting_Timer); + + DC395x_write8(TRM_S1040_SCSI_CONTROL, DO_RSTMODULE); + DC395x_write8(TRM_S1040_DMA_CONTROL, DMARESETMODULE); + /*DC395x_write8(TRM_S1040_DMA_CONTROL,STOPDMAXFER); */ + udelay(500); + /* Maybe we locked up the bus? Then lets wait even longer ... */ + pACB->pScsiHost->last_reset = + jiffies + 5 * HZ / 2 + + HZ * dc395x_trm_eepromBuf[pACB->AdapterIndex].NvramDelayTime; + + DC395x_clrfifo(pACB, "RstDet"); + DC395x_basic_config(pACB); + /*1.25 */ + /*DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_HWRESELECT); */ + + if (pACB->ACBFlag & RESET_DEV) { /* RESET_DETECT, RESET_DONE, RESET_DEV */ + pACB->ACBFlag |= RESET_DONE; + } else { + pACB->ACBFlag |= RESET_DETECT; + DC395x_ResetDevParam(pACB); + DC395x_DoingSRB_Done(pACB, DID_RESET, 0, 1); + /*DC395x_RecoverSRB( pACB ); */ + pACB->pActiveDCB = NULL; + pACB->ACBFlag = 0; + DC395x_Waiting_process(pACB); + } + + return; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_SRBdone + ******************************************************************** + */ +static void +DC395x_RequestSense(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB) +{ + Scsi_Cmnd *pcmd; + + pcmd = pSRB->pcmd; +#ifdef DC395x_DEBUG_KG + printk(KERN_INFO DC395X_NAME + ": DC395x_RequestSense for pid %li, target %02i-%i\n", + pcmd->pid, pcmd->device->id, pcmd->device->lun); +#endif + TRACEPRINTF("RqSn*"); + pSRB->SRBFlag |= AUTO_REQSENSE; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + + /* KG: Can this prevent crap sense data ? */ + memset(pcmd->sense_buffer, 0, sizeof(pcmd->sense_buffer)); + + /* Save some data */ + pSRB->SegmentX[DC395x_MAX_SG_LISTENTRY - 1].address = + pSRB->SegmentX[0].address; + pSRB->SegmentX[DC395x_MAX_SG_LISTENTRY - 1].length = + pSRB->SegmentX[0].length; + pSRB->Xferred = pSRB->SRBTotalXferLength; + /* pSRB->SegmentX : a one entry of S/G list table */ + pSRB->SRBTotalXferLength = sizeof(pcmd->sense_buffer); + pSRB->SegmentX[0].length = sizeof(pcmd->sense_buffer); + /* Map sense buffer */ + pSRB->SegmentX[0].address = + pci_map_single(pACB->pdev, pcmd->sense_buffer, + sizeof(pcmd->sense_buffer), PCI_DMA_FROMDEVICE); +#ifdef DC395x_SGPARANOIA + printk(DC395X_NAME ": Map sense buffer at %p (%05x) to %08x\n", + pcmd->sense_buffer, sizeof(pcmd->sense_buffer), + pSRB->SegmentX[0].address); +#endif + pSRB->SRBSGCount = 1; + pSRB->SRBSGIndex = 0; + + if (DC395x_StartSCSI(pACB, pDCB, pSRB)) { /* Should only happen, if sb. else grabs the bus */ + printk(DC395X_NAME + ": Request Sense failed for pid %li (%02i-%i)!\n", + pSRB->pcmd->pid, pDCB->TargetID, pDCB->TargetLUN); + TRACEPRINTF("?*"); + DC395x_Going_to_Waiting(pDCB, pSRB); + DC395x_waiting_timer(pACB, HZ / 100); + } + TRACEPRINTF(".*"); +} + + +/* + ********************************************************************* + * DC395x_queue_command + * + * Function : void DC395x_initDCB + * Purpose : initialize the internal structures for a given DCB + * Inputs : cmd - pointer to this scsi cmd request block structure + ********************************************************************* + */ +void +DC395x_initDCB(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk **ppDCB, + u8 target, u8 lun) +{ + struct NvRamType *eeprom; + u8 PeriodIndex; + u16 index; + struct DeviceCtlBlk *pDCB; + struct DeviceCtlBlk *pDCB2; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_initDCB..............\n "); +#endif + pDCB = KMALLOC(sizeof(struct DeviceCtlBlk), GFP_ATOMIC); + /*pDCB = DC395x_findDCB (pACB, target, lun); */ + *ppDCB = pDCB; + pDCB2 = 0; + if (!pDCB) + return; + + if (pACB->DCBCnt == 0) { + pACB->pLinkDCB = pDCB; + pACB->pDCBRunRobin = pDCB; + } else { + pACB->pLastDCB->pNextDCB = pDCB; + } + + pACB->DCBCnt++; + pDCB->pNextDCB = pACB->pLinkDCB; + pACB->pLastDCB = pDCB; + + /* $$$$$$$ */ + pDCB->pDCBACB = pACB; + pDCB->TargetID = target; + pDCB->TargetLUN = lun; + /* $$$$$$$ */ + pDCB->pWaitingSRB = NULL; + pDCB->pGoingSRB = NULL; + pDCB->GoingSRBCnt = 0; + pDCB->WaitSRBCnt = 0; + pDCB->pActiveSRB = NULL; + /* $$$$$$$ */ + pDCB->TagMask = 0; + pDCB->DCBFlag = 0; + pDCB->MaxCommand = 1; + pDCB->AdaptIndex = pACB->AdapterIndex; + /* $$$$$$$ */ + index = pACB->AdapterIndex; + eeprom = &dc395x_trm_eepromBuf[index]; + pDCB->DevMode = eeprom->NvramTarget[target].NvmTarCfg0; + /*pDCB->AdpMode = eeprom->NvramChannelCfg; */ + pDCB->Inquiry7 = 0; + pDCB->SyncMode = 0; + pDCB->last_derated = pACB->pScsiHost->last_reset - 2; + /* $$$$$$$ */ + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + PeriodIndex = eeprom->NvramTarget[target].NvmTarPeriod & 0x07; + pDCB->MinNegoPeriod = dc395x_clock_period[PeriodIndex]; + +#ifndef DC395x_NO_WIDE + if ((pDCB->DevMode & NTC_DO_WIDE_NEGO) + && (pACB->Config & HCC_WIDE_CARD)) + pDCB->SyncMode |= WIDE_NEGO_ENABLE; +#endif +#ifndef DC395x_NO_SYNC + if (pDCB->DevMode & NTC_DO_SYNC_NEGO) + if (!(lun) || DC395x_CurrSyncOffset) + pDCB->SyncMode |= SYNC_NEGO_ENABLE; +#endif + /* $$$$$$$ */ +#ifndef DC395x_NO_DISCONNECT + pDCB->IdentifyMsg = + IDENTIFY(pDCB->DevMode & NTC_DO_DISCONNECT, lun); +#else + pDCB->IdentifyMsg = IDENTIFY(0, lun); +#endif + /* $$$$$$$ */ + if (pDCB->TargetLUN != 0) { + /* Copy settings */ + struct DeviceCtlBlk *prevDCB = pACB->pLinkDCB; + while (prevDCB->TargetID != pDCB->TargetID) + prevDCB = prevDCB->pNextDCB; +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Copy settings from %02i-%02i to %02i-%02i\n", + prevDCB->TargetID, prevDCB->TargetLUN, + pDCB->TargetID, pDCB->TargetLUN); +#endif + pDCB->SyncMode = prevDCB->SyncMode; + pDCB->SyncPeriod = prevDCB->SyncPeriod; + pDCB->MinNegoPeriod = prevDCB->MinNegoPeriod; + pDCB->SyncOffset = prevDCB->SyncOffset; + pDCB->Inquiry7 = prevDCB->Inquiry7; + }; + + pACB->DCBmap[target] |= (1 << lun); + pACB->children[target][lun] = pDCB; +} + + +/* Dynamically allocated memory handling */ + +#ifdef DC395x_DEBUGTRACE +/* Memory for trace buffers */ +void DC395x_free_tracebufs(struct AdapterCtlBlk *pACB, int SRBIdx) +{ + int srbidx; + const unsigned bufs_per_page = PAGE_SIZE / DEBUGTRACEBUFSZ; + for (srbidx = 0; srbidx < SRBIdx; srbidx += bufs_per_page) { + /*printk (DC395X_NAME ": Free tracebuf %p (for %i)\n", */ + /* pACB->SRB_array[srbidx].debugtrace, srbidx); */ + KFREE(pACB->SRB_array[srbidx].debugtrace); + } +} + + +int DC395x_alloc_tracebufs(struct AdapterCtlBlk *pACB) +{ + const unsigned mem_needed = + (DC395x_MAX_SRB_CNT + 1) * DEBUGTRACEBUFSZ; + int pages = (mem_needed + (PAGE_SIZE - 1)) / PAGE_SIZE; + const unsigned bufs_per_page = PAGE_SIZE / DEBUGTRACEBUFSZ; + int SRBIdx = 0; + unsigned i = 0; + unsigned char *ptr; + /*printk (DC395X_NAME ": Alloc %i pages for tracebufs\n", pages); */ + while (pages--) { + ptr = KMALLOC(PAGE_SIZE, GFP_KERNEL); + if (!ptr) { + DC395x_free_tracebufs(pACB, SRBIdx); + return 1; + } + /*printk (DC395X_NAME ": Alloc %li bytes at %p for tracebuf %i\n", */ + /* PAGE_SIZE, ptr, SRBIdx); */ + i = 0; + while (i < bufs_per_page && SRBIdx < DC395x_MAX_SRB_CNT) + pACB->SRB_array[SRBIdx++].debugtrace = + ptr + (i++ * DEBUGTRACEBUFSZ); + } + if (i < bufs_per_page) { + pACB->TmpSRB.debugtrace = ptr + (i * DEBUGTRACEBUFSZ); + pACB->TmpSRB.debugtrace[0] = 0; + } else + printk(DC395X_NAME + ": No space for tmpSRB tracebuf reserved?!\n"); + return 0; +} +#endif + + +/* Free SG tables */ +void DC395x_free_SG_tables(struct AdapterCtlBlk *pACB, int SRBIdx) +{ + int srbidx; + const unsigned SRBs_per_page = + PAGE_SIZE / (DC395x_MAX_SG_LISTENTRY * sizeof(struct SGentry)); + for (srbidx = 0; srbidx < SRBIdx; srbidx += SRBs_per_page) { + /*printk (DC395X_NAME ": Free SG segs %p (for %i)\n", */ + /* pACB->SRB_array[srbidx].SegmentX, srbidx); */ + KFREE(pACB->SRB_array[srbidx].SegmentX); + } +} + + +/* + * Allocate SG tables; as we have to pci_map them, an SG list (struct SGentry*) + * should never cross a page boundary */ +int DC395x_alloc_SG_tables(struct AdapterCtlBlk *pACB) +{ + const unsigned mem_needed = + (DC395x_MAX_SRB_CNT + + 1) * DC395x_MAX_SG_LISTENTRY * sizeof(struct SGentry); + int pages = (mem_needed + (PAGE_SIZE - 1)) / PAGE_SIZE; + const unsigned SRBs_per_page = + PAGE_SIZE / (DC395x_MAX_SG_LISTENTRY * sizeof(struct SGentry)); + int SRBIdx = 0; + unsigned i = 0; + struct SGentry *ptr; + /*printk (DC395X_NAME ": Alloc %i pages for SG tables\n", pages); */ + while (pages--) { + ptr = (struct SGentry *) KMALLOC(PAGE_SIZE, GFP_KERNEL); + if (!ptr) { + DC395x_free_SG_tables(pACB, SRBIdx); + return 1; + } + /*printk (DC395X_NAME ": Alloc %li bytes at %p for SG segments %i\n", */ + /* PAGE_SIZE, ptr, SRBIdx); */ + i = 0; + while (i < SRBs_per_page && SRBIdx < DC395x_MAX_SRB_CNT) + pACB->SRB_array[SRBIdx++].SegmentX = + ptr + (i++ * DC395x_MAX_SG_LISTENTRY); + } + if (i < SRBs_per_page) + pACB->TmpSRB.SegmentX = + ptr + (i * DC395x_MAX_SG_LISTENTRY); + else + printk(DC395X_NAME + ": No space for tmpSRB SG table reserved?!\n"); + return 0; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_initACB + ******************************************************************** + */ +void __init DC395x_linkSRB(struct AdapterCtlBlk *pACB) +{ + int i; + + for (i = 0; i < pACB->SRBCount - 1; i++) + pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i + 1]; + pACB->SRB_array[i].pNextSRB = NULL; + /*DC395x_Free_integrity (pACB); */ +} + + +/* + *********************************************************************** + * DC395x_init + * + * Function : static void DC395x_initACB + * Purpose : initialize the internal structures for a given SCSI host + * Inputs : host - pointer to this host adapter's structure + *********************************************************************** + */ +int __init +DC395x_initACB(struct Scsi_Host *host, u32 io_port, u8 irq, u16 index) +{ + struct NvRamType *eeprom; + struct AdapterCtlBlk *pACB; + u16 i; + + eeprom = &dc395x_trm_eepromBuf[index]; + host->max_cmd_len = 24; + host->can_queue = DC395x_MAX_CMD_QUEUE; + host->cmd_per_lun = DC395x_MAX_CMD_PER_LUN; + host->this_id = (int) eeprom->NvramScsiId; + host->io_port = io_port; + host->n_io_port = 0x80; + host->dma_channel = -1; + host->unique_id = io_port; + host->irq = irq; + host->last_reset = jiffies; + + pACB = (struct AdapterCtlBlk *) host->hostdata; + + host->max_id = 16; + if (host->max_id - 1 == eeprom->NvramScsiId) + host->max_id--; +#ifdef CONFIG_SCSI_MULTI_LUN + if (eeprom->NvramChannelCfg & NAC_SCANLUN) + host->max_lun = 8; + else + host->max_lun = 1; +#else + host->max_lun = 1; +#endif + /* + ******************************** + */ + pACB->pScsiHost = host; + pACB->IOPortBase = (u16) io_port; + pACB->pLinkDCB = NULL; + pACB->pDCBRunRobin = NULL; + pACB->pActiveDCB = NULL; + pACB->SRBCount = DC395x_MAX_SRB_CNT; + pACB->AdapterIndex = index; + pACB->status = 0; + pACB->pScsiHost->this_id = eeprom->NvramScsiId; + pACB->HostID_Bit = (1 << pACB->pScsiHost->this_id); + /*pACB->pScsiHost->this_lun = 0; */ + pACB->DCBCnt = 0; + pACB->DeviceCnt = 0; + pACB->IRQLevel = irq; + pACB->TagMaxNum = 1 << eeprom->NvramMaxTag; + if (pACB->TagMaxNum > 30) + pACB->TagMaxNum = 30; + pACB->ACBFlag = 0; /* RESET_DETECT, RESET_DONE, RESET_DEV */ + pACB->scan_devices = 1; + pACB->MsgLen = 0; + pACB->Gmode2 = eeprom->NvramChannelCfg; + if (eeprom->NvramChannelCfg & NAC_SCANLUN) + pACB->LUNchk = 1; + /* + * link all device's SRB Q of this adapter + */ + if (DC395x_alloc_SG_tables(pACB)) { + printk(DC395X_NAME ": SG table allocation failed!\n"); + return 1; + } +#ifdef DC395x_DEBUGTRACE + if (DC395x_alloc_tracebufs(pACB)) { + printk(DC395X_NAME + ": SG trace buffer allocation failed!\n"); + DC395x_free_SG_tables(pACB, DC395x_MAX_SRB_CNT); + return 1; + } +#endif + DC395x_linkSRB(pACB); + pACB->pFreeSRB = pACB->SRB_array; + /* + * temp SRB for Q tag used or abort command used + */ + pACB->pTmpSRB = &pACB->TmpSRB; + pACB->TmpSRB.pSRBDCB = 0; + pACB->TmpSRB.pNextSRB = 0; + init_timer(&pACB->Waiting_Timer); + + for (i = 0; i < DC395x_MAX_SCSI_ID; i++) + pACB->DCBmap[i] = 0; +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": pACB = %p, pDCBmap = %p, pSRB_array = %p\n", pACB, + pACB->DCBmap, pACB->SRB_array); + printk(KERN_INFO DC395X_NAME + ": ACB size= %04lx, DCB size= %04lx, SRB size= %04lx\n", + sizeof(struct AdapterCtlBlk), sizeof(struct DeviceCtlBlk), + sizeof(struct ScsiReqBlk)); +#endif + return 0; +} + + +/*=========================================================================== + Init + ===========================================================================*/ +/* + * Intialise the SCSI chip control registers + * + * @param host This hosts adapter strcuture + * @param io_port The base I/O port + * @param irq IRQ + * @param index Card number?? (for multiple cards?) + */ +static int __init +DC395x_initAdapter(struct Scsi_Host *host, u32 io_port, u8 irq, u16 index) +{ + struct NvRamType *eeprom; + struct AdapterCtlBlk *pACB; + struct AdapterCtlBlk *pTempACB; + u16 used_irq = 0; + + eeprom = &dc395x_trm_eepromBuf[index]; + pTempACB = DC395x_pACB_start; + if (pTempACB != NULL) { + for (; (pTempACB != (struct AdapterCtlBlk *) -1);) { + if (pTempACB->IRQLevel == irq) { + used_irq = 1; + break; + } else + pTempACB = pTempACB->pNextACB; + } + } + + if (!request_region(io_port, host->n_io_port, DC395X_NAME)) { + printk(KERN_ERR DC395X_NAME + ": Failed to reserve IO region 0x%x\n", io_port); + return -1; + } + if (!used_irq) { + if (request_irq + (irq, DC395x_Interrupt, SA_SHIRQ, DC395X_NAME, + (void *) host->hostdata)) { + printk(KERN_INFO DC395X_NAME + ": Failed to register IRQ!\n"); + return -1; + } + } + + pACB = (struct AdapterCtlBlk *) host->hostdata; + pACB->IOPortBase = io_port; + + /* selection timeout = 250 ms */ + pACB->sel_timeout = DC395x_SEL_TIMEOUT; + + /* Mask all the interrupt */ + DC395x_write8(TRM_S1040_DMA_INTEN, 0x00); + DC395x_write8(TRM_S1040_SCSI_INTEN, 0x00); + + /* Reset SCSI module */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_RSTMODULE); + + /* Reset PCI/DMA module */ + DC395x_write8(TRM_S1040_DMA_CONTROL, DMARESETMODULE); + udelay(20); + + /* program configuration 0 */ + pACB->Config = HCC_AUTOTERM | HCC_PARITY; + if (DC395x_read8(TRM_S1040_GEN_STATUS) & WIDESCSI) + pACB->Config |= HCC_WIDE_CARD; + + if (eeprom->NvramChannelCfg & NAC_POWERON_SCSI_RESET) + pACB->Config |= HCC_SCSI_RESET; + + if (pACB->Config & HCC_SCSI_RESET) { + printk(KERN_INFO DC395X_NAME + ": Performing initial SCSI bus reset\n"); + DC395x_write8(TRM_S1040_SCSI_CONTROL, DO_RSTSCSI); + + /*while (!( DC395x_read8(TRM_S1040_SCSI_INTSTATUS) & INT_SCSIRESET )); */ + /*spin_unlock_irq (&io_request_lock); */ + udelay(500); + + pACB->pScsiHost->last_reset = + jiffies + HZ / 2 + + HZ * + dc395x_trm_eepromBuf[pACB->AdapterIndex]. + NvramDelayTime; + + /*spin_lock_irq (&io_request_lock); */ + } + DC395x_basic_config(pACB); + return 0; +} + + +/* + * eeprom - wait 30 us + * + * Waits for 30us (using the chip by the looks of it..) + * + * @param io_port - base I/O address + */ +static void __init TRM_S1040_wait_30us(u16 io_port) +{ + /* ScsiPortStallExecution(30); wait 30 us */ + outb(5, io_port + TRM_S1040_GEN_TIMER); + while (!(inb(io_port + TRM_S1040_GEN_STATUS) & GTIMEOUT)) + /* nothing */ ; + return; +} + + +/* + * eeprom - write command and address to chip + * + * Write the specified command and address + * + * @param io_port - base I/O address + * @param cmd - SB + op code (command) to send + * @param addr - address to send + */ +static void __init TRM_S1040_write_cmd(u16 io_port, u8 cmd, u8 addr) +{ + int i; + u8 send_data; + + /* program SB + OP code */ + for (i = 0; i < 3; i++, cmd <<= 1) { + send_data = NVR_SELECT; + if (cmd & 0x04) /* Start from bit 2 */ + send_data |= NVR_BITOUT; + + outb(send_data, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + outb((send_data | NVR_CLOCK), + io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + } + + /* send address */ + for (i = 0; i < 7; i++, addr <<= 1) { + send_data = NVR_SELECT; + if (addr & 0x40) /* Start from bit 6 */ + send_data |= NVR_BITOUT; + + outb(send_data, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + outb((send_data | NVR_CLOCK), + io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + } + outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); +} + + +/* + * eeprom - store a single byte in the SEEPROM + * + * Called from write all to write a single byte into the SSEEPROM + * Which is done one bit at a time. + * + * @param io_port - base I/O address + * @param addr - offset into EEPROM + * @param byte - bytes to write + */ +static void __init TRM_S1040_set_data(u16 io_port, u8 addr, u8 byte) +{ + int i; + u8 send_data; + + /* Send write command & address */ + TRM_S1040_write_cmd(io_port, 0x05, addr); + + /* Write data */ + for (i = 0; i < 8; i++, byte <<= 1) { + send_data = NVR_SELECT; + if (byte & 0x80) /* Start from bit 7 */ + send_data |= NVR_BITOUT; + + outb(send_data, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + outb((send_data | NVR_CLOCK), + io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + } + outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + + /* Disable chip select */ + outb(0, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + + outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + + /* Wait for write ready */ + while (1) { + outb((NVR_SELECT | NVR_CLOCK), + io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + + outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + + if (inb(io_port + TRM_S1040_GEN_NVRAM) & NVR_BITIN) + break; + } + + /* Disable chip select */ + outb(0, io_port + TRM_S1040_GEN_NVRAM); +} + + +/* + * eeprom - write 128 bytes to the SEEPROM + * + * Write the supplied 128 bytes to the chips SEEPROM + * + * @param eeprom - the data to write + * @param io_port - the base io port + */ +static void __init +TRM_S1040_write_all(struct NvRamType *eeprom, u16 io_port) +{ + u8 *b_eeprom = (u8 *) eeprom; + u8 addr; + + /* Enable SEEPROM */ + outb((inb(io_port + TRM_S1040_GEN_CONTROL) | EN_EEPROM), + io_port + TRM_S1040_GEN_CONTROL); + + /* write enable */ + TRM_S1040_write_cmd(io_port, 0x04, 0xFF); + outb(0, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + + /* write */ + for (addr = 0; addr < 128; addr++, b_eeprom++) { + TRM_S1040_set_data(io_port, addr, *b_eeprom); + } + + /* write disable */ + TRM_S1040_write_cmd(io_port, 0x04, 0x00); + outb(0, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + + /* Disable SEEPROM */ + outb((inb(io_port + TRM_S1040_GEN_CONTROL) & ~EN_EEPROM), + io_port + TRM_S1040_GEN_CONTROL); +} + + +/* + * eeprom - get a single byte from the SEEPROM + * + * Called from read all to read a single byte into the SSEEPROM + * Which is done one bit at a time. + * + * @param io_port - base I/O address + * @param addr - offset into SEEPROM + * @return - the byte read + */ +static u8 __init TRM_S1040_get_data(u16 io_port, u8 addr) +{ + int i; + u8 read_byte; + u8 result = 0; + + /* Send read command & address */ + TRM_S1040_write_cmd(io_port, 0x06, addr); + + /* read data */ + for (i = 0; i < 8; i++) { + outb((NVR_SELECT | NVR_CLOCK), + io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM); + + /* Get data bit while falling edge */ + read_byte = inb(io_port + TRM_S1040_GEN_NVRAM); + result <<= 1; + if (read_byte & NVR_BITIN) + result |= 1; + + TRM_S1040_wait_30us(io_port); + } + + /* Disable chip select */ + outb(0, io_port + TRM_S1040_GEN_NVRAM); + return result; +} + + +/* + * eeprom - read_all + * + * Read the 128 bytes from the SEEPROM. + * + * @param eeprom - where to store the data + * @param io_port - the base io port + */ +static void __init +TRM_S1040_read_all(struct NvRamType *eeprom, u16 io_port) +{ + u8 *b_eeprom = (u8 *) eeprom; + u8 addr; + + /* Enable SEEPROM */ + outb((inb(io_port + TRM_S1040_GEN_CONTROL) | EN_EEPROM), + io_port + TRM_S1040_GEN_CONTROL); + + /* read details */ + for (addr = 0; addr < 128; addr++, b_eeprom++) { + *b_eeprom = TRM_S1040_get_data(io_port, addr); + } + + /* Disable SEEPROM */ + outb((inb(io_port + TRM_S1040_GEN_CONTROL) & ~EN_EEPROM), + io_port + TRM_S1040_GEN_CONTROL); +} + + + +/* + * eeprom - get and check contents + * + * Read seeprom 128 bytes into the memory provider in eeprom. + * Checks the checksum and if it's not correct it uses a set of default + * values. + * + * @param eeprom - caller allocated strcuture to read the eeprom data into + * @param io_port - io port to read from + */ +static void __init +DC395x_check_eeprom(struct NvRamType *eeprom, u16 io_port) +{ + u16 *w_eeprom = (u16 *) eeprom; + u16 w_addr; + u16 cksum; + u32 d_addr; + u32 *d_eeprom; + + TRM_S1040_read_all(eeprom, io_port); /* read eeprom */ + + cksum = 0; + for (w_addr = 0, w_eeprom = (u16 *) eeprom; w_addr < 64; + w_addr++, w_eeprom++) + cksum += *w_eeprom; + if (cksum != 0x1234) { + /* + * Checksum is wrong. + * Load a set of defaults into the eeprom buffer + */ + printk(KERN_WARNING DC395X_NAME + ": EEProm checksum error: using default values and options.\n"); + eeprom->NvramSubVendorID[0] = (u8) PCI_VENDOR_ID_TEKRAM; + eeprom->NvramSubVendorID[1] = + (u8) (PCI_VENDOR_ID_TEKRAM >> 8); + eeprom->NvramSubSysID[0] = + (u8) PCI_DEVICE_ID_TEKRAM_TRMS1040; + eeprom->NvramSubSysID[1] = + (u8) (PCI_DEVICE_ID_TEKRAM_TRMS1040 >> 8); + eeprom->NvramSubClass = 0x00; + eeprom->NvramVendorID[0] = (u8) PCI_VENDOR_ID_TEKRAM; + eeprom->NvramVendorID[1] = + (u8) (PCI_VENDOR_ID_TEKRAM >> 8); + eeprom->NvramDeviceID[0] = + (u8) PCI_DEVICE_ID_TEKRAM_TRMS1040; + eeprom->NvramDeviceID[1] = + (u8) (PCI_DEVICE_ID_TEKRAM_TRMS1040 >> 8); + eeprom->NvramReserved = 0x00; + + for (d_addr = 0, d_eeprom = (u32 *) eeprom->NvramTarget; + d_addr < 16; d_addr++, d_eeprom++) + *d_eeprom = 0x00000077; /* NvmTarCfg3,NvmTarCfg2,NvmTarPeriod,NvmTarCfg0 */ + + *d_eeprom++ = 0x04000F07; /* NvramMaxTag,NvramDelayTime,NvramChannelCfg,NvramScsiId */ + *d_eeprom++ = 0x00000015; /* NvramReserved1,NvramBootLun,NvramBootTarget,NvramReserved0 */ + for (d_addr = 0; d_addr < 12; d_addr++, d_eeprom++) + *d_eeprom = 0x00; + + /* Now load defaults (maybe set by boot/module params) */ + DC395x_check_for_safe_settings(); + DC395x_fill_with_defaults(); + DC395x_EEprom_Override(eeprom); + + eeprom->NvramCheckSum = 0x00; + for (w_addr = 0, cksum = 0, w_eeprom = (u16 *) eeprom; + w_addr < 63; w_addr++, w_eeprom++) + cksum += *w_eeprom; + + *w_eeprom = 0x1234 - cksum; + TRM_S1040_write_all(eeprom, io_port); + eeprom->NvramDelayTime = dc395x_trm[5]; + } else { + DC395x_check_for_safe_settings(); + DC395x_interpret_delay(eeprom); + DC395x_EEprom_Override(eeprom); + } +} + + +/* + * adapter - print connection and terminiation config + * + * @param pACB - adapter control block + */ +static void __init DC395x_print_config(struct AdapterCtlBlk *pACB) +{ + u8 bval; + + bval = DC395x_read8(TRM_S1040_GEN_STATUS); + printk(KERN_INFO DC395X_NAME "%c: Connectors: ", + ((bval & WIDESCSI) ? 'W' : ' ')); + if (!(bval & CON5068)) + printk("ext%s ", !(bval & EXT68HIGH) ? "68" : "50"); + if (!(bval & CON68)) + printk("int68%s ", !(bval & INT68HIGH) ? "" : "(50)"); + if (!(bval & CON50)) + printk("int50 "); + if ((bval & (CON5068 | CON50 | CON68)) == + 0 /*(CON5068 | CON50 | CON68) */ ) + printk(" Oops! (All 3?) "); + bval = DC395x_read8(TRM_S1040_GEN_CONTROL); + printk(" Termination: "); + if (bval & DIS_TERM) + printk("Disabled\n"); + else { + if (bval & AUTOTERM) + printk("Auto "); + if (bval & LOW8TERM) + printk("Low "); + if (bval & UP8TERM) + printk("High "); + printk("\n"); + } +} + + +/* + ********************************************************************* + * DC395x_detect + * + * Function : static int DC395x_init (struct Scsi_Host *host) + * Purpose : initialize the internal structures for a given SCSI host + * Inputs : host - pointer to this host adapter's structure/ + * Preconditions : when this function is called, the chip_type + * field of the pACB structure MUST have been set. + ********************************************************************* + */ +static struct Scsi_Host *__init +DC395x_init(Scsi_Host_Template * host_template, u32 io_port, u8 irq, + u16 index) +{ + struct Scsi_Host *host; + struct AdapterCtlBlk *pACB; + + /* + * Read the eeprom contents info the buffer we supply. Use + * defaults is eeprom checksum is wrong. + */ + DC395x_check_eeprom(&dc395x_trm_eepromBuf[index], (u16) io_port); + + /*$$$$$$$$$$$ MEMORY ALLOCATE FOR ADAPTER CONTROL BLOCK $$$$$$$$$$$$ */ + host = scsi_register(host_template, sizeof(struct AdapterCtlBlk)); + if (!host) { + printk(KERN_INFO DC395X_NAME + " : pSH scsi_register ERROR\n"); + return 0; + } + printk(KERN_INFO DC395X_NAME + ": Used settings: AdapterID=%02i, Speed=%i(%02i.%01iMHz), DevMode=0x%02x\n", + dc395x_trm_eepromBuf[index].NvramScsiId, + dc395x_trm_eepromBuf[index].NvramTarget[0].NvmTarPeriod, + dc395x_clock_speed[dc395x_trm_eepromBuf[index]. + NvramTarget[0].NvmTarPeriod] / 10, + dc395x_clock_speed[dc395x_trm_eepromBuf[index]. + NvramTarget[0].NvmTarPeriod] % 10, + dc395x_trm_eepromBuf[index].NvramTarget[0].NvmTarCfg0); + printk(KERN_INFO DC395X_NAME + ": AdaptMode=0x%02x, Tags=%i(%02i), DelayReset=%is\n", + dc395x_trm_eepromBuf[index].NvramChannelCfg, + dc395x_trm_eepromBuf[index].NvramMaxTag, + 1 << dc395x_trm_eepromBuf[index].NvramMaxTag, + dc395x_trm_eepromBuf[index].NvramDelayTime); + + pACB = (struct AdapterCtlBlk *) host->hostdata; + /*DC395x_ACB_INITLOCK(pACB); */ + /*DC395x_ACB_LOCK(pACB,acb_flags); */ + /*$$$$$$$$ INITIAL ADAPTER CONTROL BLOCK $$$$$$$$$$$$ */ + if (DC395x_initACB(host, io_port, irq, index)) { + scsi_unregister(host); + /*DC395x_ACB_UNLOCK(pACB,acb_flags); */ + return 0; + } + DC395x_print_config(pACB); + /*$$$$$$$$$$$$$$$$$ INITIAL ADAPTER $$$$$$$$$$$$$$$$$ */ + if (!DC395x_initAdapter(host, io_port, irq, index)) { + if (!DC395x_pACB_start) { + DC395x_pACB_start = pACB; + DC395x_pACB_current = pACB; + pACB->pNextACB = (struct AdapterCtlBlk *) -1; + } else { + DC395x_pACB_current->pNextACB = pACB; + DC395x_pACB_current = pACB; + pACB->pNextACB = (struct AdapterCtlBlk *) -1; + } + /*DC395x_ACB_UNLOCK(pACB,acb_flags); */ + return host; + } else { + printk(KERN_INFO DC395X_NAME + ": DC395x_initAdapter initial ERROR\n"); + scsi_unregister(host); + /*DC395x_ACB_UNLOCK(pACB,acb_flags); */ + return 0; + } +} + + +/* + * DC395x_detect + * + * Detect TRM-S1040 cards, acquire resources and initialise the card. + * Argument is a pointer to the host driver's scsi_hosts entry. + * + * Returns the number of adapters found. + * + * This function is called during system initialization and must not + * call SCSI mid-level functions including scsi_malloc() and + * scsi_free(). + */ +static int __init DC395x_detect(Scsi_Host_Template * host_template) +{ + struct pci_dev *pdev = NULL; + unsigned int io_port; + u8 irq; + DC395x_pACB_start = NULL; + + /* without PCI we cannot do anything */ + if (pci_present() == 0) { + printk(KERN_INFO DC395X_NAME ": PCI not present\n"); + return 0; + } + printk(KERN_INFO DC395X_NAME ": %s %s\n", DC395X_BANNER, + DC395X_VERSION); + + while ((pdev = + pci_find_device(PCI_VENDOR_ID_TEKRAM, + PCI_DEVICE_ID_TEKRAM_TRMS1040, pdev))) { + struct Scsi_Host *scsi_host; + if (pci_enable_device(pdev)) + continue; + + io_port = + pci_resource_start(pdev, 0) & PCI_BASE_ADDRESS_IO_MASK; + irq = pdev->irq; +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": IO_PORT=%04x,IRQ=%x\n", + (unsigned int) io_port, irq); +#endif + if ((scsi_host = + DC395x_init(host_template, io_port, irq, + DC395x_adapterCnt))) { + pci_set_master(pdev); + ((struct AdapterCtlBlk *) (scsi_host->hostdata))-> + pdev = pdev; + /*DC395x_set_pci_cfg(pdev); */ + DC395x_adapterCnt++; + } + } + + if (DC395x_adapterCnt) { + host_template->proc_name = DC395X_NAME; + } + printk(KERN_INFO DC395X_NAME ": %s: %i adapters found\n", + DC395X_BANNER, DC395x_adapterCnt); + + return DC395x_adapterCnt; +} + + +/* + * Functions: DC395x_inquiry(), DC395x_inquiry_done() + * + * Purpose: When changing speed etc., we have to issue an INQUIRY + * command to make sure, we agree upon the nego parameters + * with the device + */ +static void DC395x_inquiry_done(Scsi_Cmnd * cmd) +{ + struct AdapterCtlBlk *pACB = + (struct AdapterCtlBlk *) cmd->device->host->hostdata; + struct DeviceCtlBlk *pDCB = + DC395x_findDCB(pACB, cmd->device->id, cmd->device->lun); +#ifdef DC395x_DEBUGTRACE + struct ScsiReqBlk *pSRB = pACB->pFreeSRB; +#endif + printk(KERN_INFO DC395X_NAME + ": INQUIRY (%02i-%i) returned %08x: %02x %02x %02x %02x ...\n", + cmd->device->id, cmd->device->lun, cmd->result, + ((u8 *) cmd->request_buffer)[0], + ((u8 *) cmd->request_buffer)[1], + ((u8 *) cmd->request_buffer)[2], + ((u8 *) cmd->request_buffer)[3]); + /*TRACEOUT ("%s\n", pSRB->debugtrace); */ + if (cmd->result) { + printk(DC395X_NAME ": Unsetting Wide, Sync and TagQ!\n"); + if (pDCB) { + TRACEOUT("%s\n", pSRB->debugtrace); + pDCB->DevMode &= + ~(NTC_DO_SYNC_NEGO | NTC_DO_WIDE_NEGO | + NTC_DO_TAG_QUEUEING); + DC395x_updateDCB(pACB, pDCB); + } + } + if (pDCB) { + if (!(pDCB->SyncMode & SYNC_NEGO_DONE)) { + pDCB->SyncOffset = 0; /*pDCB->SyncMode &= ~SYNC_NEGO_ENABLE; */ + } + if (!(pDCB->SyncMode & WIDE_NEGO_DONE)) { + pDCB->SyncPeriod &= ~WIDE_SYNC; + pDCB->SyncMode &= ~WIDE_NEGO_ENABLE; + } + } else { + printk(DC395X_NAME + ": ERROR! No DCB existent for %02i-%i ?\n", + cmd->device->id, cmd->device->lun); + } + kfree(cmd->buffer); + kfree(cmd); +} + + +/* + * Perform INQUIRY + */ +void DC395x_inquiry(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB) +{ + char *buffer; + Scsi_Cmnd *cmd; + cmd = KMALLOC(sizeof(Scsi_Cmnd), GFP_ATOMIC); + if (!cmd) { + printk(DC395X_NAME ": kmalloc failed in inquiry!\n"); + return; + } + buffer = kmalloc(256, GFP_ATOMIC); + if (!buffer) { + kfree(cmd); + printk(DC395X_NAME ": kmalloc failed in inquiry!\n"); + return; + } + + memset(cmd, 0, sizeof(Scsi_Cmnd)); + cmd->cmnd[0] = INQUIRY; + cmd->cmnd[1] = (pDCB->TargetLUN << 5) & 0xe0; + cmd->cmnd[4] = 0xff; + + cmd->cmd_len = 6; + cmd->old_cmd_len = 6; + cmd->device->host = pACB->pScsiHost; + cmd->device->id = pDCB->TargetID; + cmd->device->lun = pDCB->TargetLUN; + cmd->serial_number = 1; + cmd->pid = 395; + cmd->bufflen = 128; + cmd->buffer = buffer; + cmd->request_bufflen = 128; + cmd->request_buffer = &buffer[128]; + cmd->done = DC395x_inquiry_done; + cmd->scsi_done = DC395x_inquiry_done; + cmd->timeout_per_command = HZ; + +#if 0 + /* XXX */ + cmd->request.rq_status = RQ_SCSI_BUSY; +#endif + + pDCB->SyncMode &= ~SYNC_NEGO_DONE; + pDCB->SyncMode |= SYNC_NEGO_ENABLE; + pDCB->SyncMode &= ~WIDE_NEGO_DONE; + pDCB->SyncMode |= WIDE_NEGO_ENABLE; + printk(KERN_INFO DC395X_NAME + ": Queue INQUIRY command to dev %02i-%i\n", pDCB->TargetID, + pDCB->TargetLUN); + DC395x_queue_command(cmd, DC395x_inquiry_done); +} + +#undef SEARCH +#undef YESNO +#undef SCANF + + +/* + ****************************************************************** + * Function: DC395x_proc_info(char* buffer, char **start, + * off_t offset, int length, int hostno, int inout) + * Purpose: return SCSI Adapter/Device Info + * Input: + * buffer: Pointer to a buffer where to write info + * start : + * offset: + * hostno: Host adapter index + * inout : Read (=0) or set(!=0) info + * Output: + * buffer: contains info length + * + * return value: length of info in buffer + * + ****************************************************************** + */ + +/* KG: proc_info taken from driver aha152x.c */ + +#undef SPRINTF +#define SPRINTF(args...) pos += sprintf(pos, args) + +#define YESNO(YN) \ + if (YN) SPRINTF(" Yes ");\ + else SPRINTF(" No ") + +static int +DC395x_proc_info(char *buffer, char **start, off_t offset, int length, + int hostno, int inout) +{ + int dev, spd, spd1; + char *pos = buffer; + struct Scsi_Host *shpnt = NULL; + struct AdapterCtlBlk *pACB; + struct DeviceCtlBlk *pDCB; + unsigned long flags; + Scsi_Cmnd *pcmd; + + /* Scsi_Cmnd *ptr; */ + + pACB = DC395x_pACB_start; + + while (pACB != (struct AdapterCtlBlk *) -1) { + shpnt = pACB->pScsiHost; + if (shpnt->host_no == hostno) + break; + pACB = pACB->pNextACB; + } + if (pACB == (struct AdapterCtlBlk *) -1) + return -ESRCH; + + if (!shpnt) + return -ESRCH; + + if (inout) /* Has data been written to the file ? */ + return -EPERM; + + SPRINTF(DC395X_BANNER " PCI SCSI Host Adapter\n"); + SPRINTF(" Driver Version " DC395X_VERSION "\n"); + + DC395x_LOCK_IO(pACB->pScsiHost); + + SPRINTF("SCSI Host Nr %i, ", shpnt->host_no); + SPRINTF("DC395U/UW/F DC315/U %s Adapter Nr %i\n", + (pACB->Config & HCC_WIDE_CARD) ? "Wide" : "", + pACB->AdapterIndex); + SPRINTF("IOPortBase 0x%04x, ", pACB->IOPortBase); + SPRINTF("IRQLevel 0x%02x, ", pACB->IRQLevel); + SPRINTF(" SelTimeout %ims\n", (1638 * pACB->sel_timeout) / 1000); + + SPRINTF("MaxID %i, MaxLUN %i, ", shpnt->max_id, shpnt->max_lun); + SPRINTF("AdapterID %i\n", shpnt->this_id); + + SPRINTF("TagMaxNum %i, Status %i", pACB->TagMaxNum, pACB->status); + /*SPRINTF(", DMA_Status %i\n", DC395x_read8(TRM_S1040_DMA_STATUS)); */ + SPRINTF(", FilterCfg 0x%02x", + DC395x_read8(TRM_S1040_SCSI_CONFIG1)); + SPRINTF(", DelayReset %is\n", + dc395x_trm_eepromBuf[pACB->AdapterIndex].NvramDelayTime); + /*SPRINTF("\n"); */ + + SPRINTF("Nr of attached devices: %i, Nr of DCBs: %i\n", + pACB->DeviceCnt, pACB->DCBCnt); + SPRINTF + ("Map of attached LUNs: %02x %02x %02x %02x %02x %02x %02x %02x\n", + pACB->DCBmap[0], pACB->DCBmap[1], pACB->DCBmap[2], + pACB->DCBmap[3], pACB->DCBmap[4], pACB->DCBmap[5], + pACB->DCBmap[6], pACB->DCBmap[7]); + SPRINTF + (" %02x %02x %02x %02x %02x %02x %02x %02x\n", + pACB->DCBmap[8], pACB->DCBmap[9], pACB->DCBmap[10], + pACB->DCBmap[11], pACB->DCBmap[12], pACB->DCBmap[13], + pACB->DCBmap[14], pACB->DCBmap[15]); + + SPRINTF + ("Un ID LUN Prty Sync Wide DsCn SndS TagQ NegoPeriod SyncFreq SyncOffs MaxCmd\n"); + + pDCB = pACB->pLinkDCB; + for (dev = 0; dev < pACB->DCBCnt; dev++) { + int NegoPeriod; + SPRINTF("%02i %02i %02i ", dev, pDCB->TargetID, + pDCB->TargetLUN); + YESNO(pDCB->DevMode & NTC_DO_PARITY_CHK); + YESNO(pDCB->SyncOffset); + YESNO(pDCB->SyncPeriod & WIDE_SYNC); + YESNO(pDCB->DevMode & NTC_DO_DISCONNECT); + YESNO(pDCB->DevMode & NTC_DO_SEND_START); + YESNO(pDCB->SyncMode & EN_TAG_QUEUEING); + NegoPeriod = + dc395x_clock_period[pDCB->SyncPeriod & 0x07] << 2; + if (pDCB->SyncOffset) + SPRINTF(" %03i ns ", NegoPeriod); + else + SPRINTF(" (%03i ns)", (pDCB->MinNegoPeriod << 2)); + + if (pDCB->SyncOffset & 0x0f) { + spd = 1000 / (NegoPeriod); + spd1 = 1000 % (NegoPeriod); + spd1 = (spd1 * 10 + NegoPeriod / 2) / (NegoPeriod); + SPRINTF(" %2i.%1i M %02i ", spd, spd1, + (pDCB->SyncOffset & 0x0f)); + } else + SPRINTF(" "); + + /* Add more info ... */ + SPRINTF(" %02i\n", pDCB->MaxCommand); + pDCB = pDCB->pNextDCB; + } + + SPRINTF("Commands in Queues: Query: %i:", pACB->QueryCnt); + for (pcmd = pACB->pQueryHead; pcmd; + pcmd = (Scsi_Cmnd *) pcmd->host_scribble) + SPRINTF(" %li", pcmd->pid); + if (timer_pending(&pACB->Waiting_Timer)) + SPRINTF("Waiting queue timer running\n"); + else + SPRINTF("\n"); + pDCB = pACB->pLinkDCB; + + for (dev = 0; dev < pACB->DCBCnt; dev++) { + struct ScsiReqBlk *pSRB; + if (pDCB->WaitSRBCnt) + SPRINTF("DCB (%02i-%i): Waiting: %i:", + pDCB->TargetID, pDCB->TargetLUN, + pDCB->WaitSRBCnt); + for (pSRB = pDCB->pWaitingSRB; pSRB; pSRB = pSRB->pNextSRB) + SPRINTF(" %li", pSRB->pcmd->pid); + if (pDCB->GoingSRBCnt) + SPRINTF("\nDCB (%02i-%i): Going : %i:", + pDCB->TargetID, pDCB->TargetLUN, + pDCB->GoingSRBCnt); + for (pSRB = pDCB->pGoingSRB; pSRB; pSRB = pSRB->pNextSRB) +#ifdef DC395x_DEBUGTRACE + SPRINTF("\n %s", pSRB->debugtrace); +#else + SPRINTF(" %li", pSRB->pcmd->pid); +#endif + if (pDCB->WaitSRBCnt || pDCB->GoingSRBCnt) + SPRINTF("\n"); + pDCB = pDCB->pNextDCB; + } + +#ifdef DC395x_DEBUGDCB + SPRINTF("DCB list for ACB %p:\n", pACB); + pDCB = pACB->pLinkDCB; + SPRINTF("%p", pDCB); + for (dev = 0; dev < pACB->DCBCnt; dev++, pDCB = pDCB->pNextDCB) + SPRINTF("->%p", pDCB->pNextDCB); + SPRINTF("\n"); +#endif + + *start = buffer + offset; + DC395x_UNLOCK_IO(pACB->pScsiHost); + + if (pos - buffer < offset) + return 0; + else if (pos - buffer - offset < length) + return pos - buffer - offset; + else + return length; +} + + +/* + * Function : int DC395x_shutdown (struct Scsi_Host *host) + * Purpose : does a clean (we hope) shutdown of the SCSI chip. + * Use prior to dumping core, unloading the driver, etc. + * Returns : 0 on success + */ +int DC395x_shutdown(struct Scsi_Host *host) +{ + struct AdapterCtlBlk *pACB; + pACB = (struct AdapterCtlBlk *) (host->hostdata); + + /* pACB->soft_reset(host); */ + + /* disable interrupt */ + DC395x_write8(TRM_S1040_DMA_INTEN, 0); + DC395x_write8(TRM_S1040_SCSI_INTEN, 0); + if (timer_pending(&pACB->Waiting_Timer)) + del_timer(&pACB->Waiting_Timer); + if (timer_pending(&pACB->SelTO_Timer)) + del_timer(&pACB->SelTO_Timer); + + if (1 || pACB->Config & HCC_SCSI_RESET) + DC395x_ResetSCSIBus(pACB); + + DC395x_read8(TRM_S1040_SCSI_INTSTATUS); +#ifdef DC395x_DEBUGTRACE + DC395x_free_tracebufs(pACB, DC395x_MAX_SRB_CNT); +#endif + DC395x_free_SG_tables(pACB, DC395x_MAX_SRB_CNT); + return 0; +} + + +/* + * Free all DCBs + */ +void DC395x_freeDCBs(struct Scsi_Host *host) +{ + struct DeviceCtlBlk *pDCB; + struct DeviceCtlBlk *nDCB; + struct AdapterCtlBlk *pACB = + (struct AdapterCtlBlk *) (host->hostdata); + + DCBDEBUG(printk + (KERN_INFO DC395X_NAME ": Free %i DCBs\n", pACB->DCBCnt); + ) + pDCB = pACB->pLinkDCB; + if (pDCB) { + do { + nDCB = pDCB->pNextDCB; + DCBDEBUG(printk + (KERN_INFO DC395X_NAME + ": Free DCB (ID %i, LUN %i): %p\n", + pDCB->TargetID, pDCB->TargetLUN, pDCB); + ) + DC395x_remove_dev(pACB, pDCB); /* includes a KFREE(pDCB); */ + printk("."); + pDCB = nDCB; + } while (pDCB && pACB->pLinkDCB); + } +} + + +/* + * Release method + * + * Called when we are to shutdown the controller and release all of + * it's resources. + */ +static int DC395x_release(struct Scsi_Host *host) +{ + struct AdapterCtlBlk *pACB = + (struct AdapterCtlBlk *) (host->hostdata); + unsigned long flags; + + printk(DC395X_NAME ": release"); + + DC395x_LOCK_IO(pACB->pScsiHost); + DC395x_shutdown(host); + DC395x_freeDCBs(host); + + if (host->irq != NO_IRQ) { + /* + * Find the IRQ to release. XXX Why didn't we just store the + * appropriate IRQ details when we request_irq it? + */ + int irq_count; + for (irq_count = 0, pACB = DC395x_pACB_start; + pACB != (struct AdapterCtlBlk *) -1; + pACB = pACB->pNextACB) { + if (pACB->IRQLevel == host->irq) + ++irq_count; + } + if (irq_count == 1) + free_irq(host->irq, DC395x_pACB_start); + } + release_region(host->io_port, host->n_io_port); + + DC395x_UNLOCK_IO(pACB->pScsiHost); + + return 1; +} + + +/* + * SCSI host template + */ +static Scsi_Host_Template driver_template = { + .proc_name = DC395X_NAME, + .proc_info = DC395x_proc_info, + .name = DC395X_BANNER " " DC395X_VERSION, + .detect = DC395x_detect, + .release = DC395x_release, + .queuecommand = DC395x_queue_command, + .bios_param = DC395x_bios_param, + .slave_alloc = DC395x_slave_alloc, + .slave_destroy = DC395x_slave_destroy, + .can_queue = DC395x_MAX_CAN_QUEUE, + .this_id = 7, + .sg_tablesize = DC395x_MAX_SG_TABLESIZE, + .cmd_per_lun = DC395x_MAX_CMD_PER_LUN, + .eh_abort_handler = DC395x_eh_abort, + .eh_bus_reset_handler = DC395x_eh_bus_reset, + .unchecked_isa_dma = 0, + .use_clustering = DISABLE_CLUSTERING, +}; +/* + * The following code deals with registering the above scsi host + * template with the higher level scsi code and results in the detect + * method from the template being called during initialisation. + */ +#include "scsi_module.c" diff --git a/drivers/scsi/dc395x.h b/drivers/scsi/dc395x.h new file mode 100644 index 000000000000..4feed970ba8d --- /dev/null +++ b/drivers/scsi/dc395x.h @@ -0,0 +1,659 @@ +/************************************************************************/ +/* */ +/* dc395x.h */ +/* */ +/* Device Driver for Tekram DC395(U/UW/F), DC315(U) */ +/* PCI SCSI Bus Master Host Adapter */ +/* (SCSI chip set used Tekram ASIC TRM-S1040) */ +/* */ +/************************************************************************/ + +#ifndef DC395x_H +#define DC395x_H + +/************************************************************************/ +/* */ +/* Name, Banner and Version */ +/* */ +/************************************************************************/ +#define DC395X_NAME "dc395x" +#define DC395X_BANNER "Tekram DC395(U/UW/F), DC315(U) - ASIC TRM-S1040" +#define DC395X_VERSION "v2.02, 2003/04/20" + +/************************************************************************/ +/* */ +/* Initial values */ +/* */ +/************************************************************************/ +#define DC395x_MAX_CMD_QUEUE 32 +/* #define DC395x_MAX_QTAGS 32 */ +#define DC395x_MAX_QTAGS 16 +#define DC395x_MAX_ADAPTER_NUM 4 +#define DC395x_MAX_SCSI_ID 16 +#define DC395x_MAX_CMD_PER_LUN DC395x_MAX_QTAGS +#define DC395x_MAX_SG_TABLESIZE 64 /* HW limitation */ +#define DC395x_MAX_SG_LISTENTRY 64 /* Must be equal or lower to previous */ + /* item */ +#define DC395x_MAX_SRB_CNT 63 +/* #define DC395x_MAX_CAN_QUEUE 7 * DC395x_MAX_QTAGS */ +#define DC395x_MAX_CAN_QUEUE DC395x_MAX_SRB_CNT +#define DC395x_END_SCAN 2 +#define DC395x_SEL_TIMEOUT 153 /* 250 ms selection timeout (@ 40 MHz) */ +#define DC395x_MAX_RETRIES 3 + +#if 0 +#define SYNC_FIRST +#endif + +#define NORM_REC_LVL 0 + +/************************************************************************/ +/* */ +/* Various definitions */ +/* */ +/************************************************************************/ +#define BIT31 0x80000000 +#define BIT30 0x40000000 +#define BIT29 0x20000000 +#define BIT28 0x10000000 +#define BIT27 0x08000000 +#define BIT26 0x04000000 +#define BIT25 0x02000000 +#define BIT24 0x01000000 +#define BIT23 0x00800000 +#define BIT22 0x00400000 +#define BIT21 0x00200000 +#define BIT20 0x00100000 +#define BIT19 0x00080000 +#define BIT18 0x00040000 +#define BIT17 0x00020000 +#define BIT16 0x00010000 +#define BIT15 0x00008000 +#define BIT14 0x00004000 +#define BIT13 0x00002000 +#define BIT12 0x00001000 +#define BIT11 0x00000800 +#define BIT10 0x00000400 +#define BIT9 0x00000200 +#define BIT8 0x00000100 +#define BIT7 0x00000080 +#define BIT6 0x00000040 +#define BIT5 0x00000020 +#define BIT4 0x00000010 +#define BIT3 0x00000008 +#define BIT2 0x00000004 +#define BIT1 0x00000002 +#define BIT0 0x00000001 + +/* UnitCtrlFlag */ +#define UNIT_ALLOCATED BIT0 +#define UNIT_INFO_CHANGED BIT1 +#define FORMATING_MEDIA BIT2 +#define UNIT_RETRY BIT3 + +/* UnitFlags */ +#define DASD_SUPPORT BIT0 +#define SCSI_SUPPORT BIT1 +#define ASPI_SUPPORT BIT2 + +/* SRBState machine definition */ +#define SRB_FREE 0x0000 +#define SRB_WAIT 0x0001 +#define SRB_READY 0x0002 +#define SRB_MSGOUT 0x0004 /* arbitration+msg_out 1st byte */ +#define SRB_MSGIN 0x0008 +#define SRB_EXTEND_MSGIN 0x0010 +#define SRB_COMMAND 0x0020 +#define SRB_START_ 0x0040 /* arbitration+msg_out+command_out */ +#define SRB_DISCONNECT 0x0080 +#define SRB_DATA_XFER 0x0100 +#define SRB_XFERPAD 0x0200 +#define SRB_STATUS 0x0400 +#define SRB_COMPLETED 0x0800 +#define SRB_ABORT_SENT 0x1000 +#define SRB_DO_SYNC_NEGO 0x2000 +#define SRB_DO_WIDE_NEGO 0x4000 +#define SRB_UNEXPECT_RESEL 0x8000 + +/************************************************************************/ +/* */ +/* ACB Config */ +/* */ +/************************************************************************/ +#define HCC_WIDE_CARD 0x20 +#define HCC_SCSI_RESET 0x10 +#define HCC_PARITY 0x08 +#define HCC_AUTOTERM 0x04 +#define HCC_LOW8TERM 0x02 +#define HCC_UP8TERM 0x01 + +/* ACBFlag */ +#define RESET_DEV BIT0 +#define RESET_DETECT BIT1 +#define RESET_DONE BIT2 + +/* DCBFlag */ +#define ABORT_DEV_ BIT0 + +/* SRBstatus */ +#define SRB_OK BIT0 +#define ABORTION BIT1 +#define OVER_RUN BIT2 +#define UNDER_RUN BIT3 +#define PARITY_ERROR BIT4 +#define SRB_ERROR BIT5 + +/* SRBFlag */ +#define DATAOUT BIT7 +#define DATAIN BIT6 +#define RESIDUAL_VALID BIT5 +#define ENABLE_TIMER BIT4 +#define RESET_DEV0 BIT2 +#define ABORT_DEV BIT1 +#define AUTO_REQSENSE BIT0 + +/* Adapter status */ +#define H_STATUS_GOOD 0 +#define H_SEL_TIMEOUT 0x11 +#define H_OVER_UNDER_RUN 0x12 +#define H_UNEXP_BUS_FREE 0x13 +#define H_TARGET_PHASE_F 0x14 +#define H_INVALID_CCB_OP 0x16 +#define H_LINK_CCB_BAD 0x17 +#define H_BAD_TARGET_DIR 0x18 +#define H_DUPLICATE_CCB 0x19 +#define H_BAD_CCB_OR_SG 0x1A +#define H_ABORT 0x0FF + +/* SCSI BUS Status byte codes */ +#define SCSI_STAT_GOOD 0x0 /* Good status */ +#define SCSI_STAT_CHECKCOND 0x02 /* SCSI Check Condition */ +#define SCSI_STAT_CONDMET 0x04 /* Condition Met */ +#define SCSI_STAT_BUSY 0x08 /* Target busy status */ +#define SCSI_STAT_INTER 0x10 /* Intermediate status */ +#define SCSI_STAT_INTERCONDMET 0x14 /* Intermediate condition met */ +#define SCSI_STAT_RESCONFLICT 0x18 /* Reservation conflict */ +#define SCSI_STAT_CMDTERM 0x22 /* Command Terminated */ +#define SCSI_STAT_QUEUEFULL 0x28 /* Queue Full */ +#define SCSI_STAT_UNEXP_BUS_F 0xFD /* Unexpect Bus Free */ +#define SCSI_STAT_BUS_RST_DETECT 0xFE /* Scsi Bus Reset detected */ +#define SCSI_STAT_SEL_TIMEOUT 0xFF /* Selection Time out */ + +/* Sync_Mode */ +#define SYNC_WIDE_TAG_ATNT_DISABLE 0 +#define SYNC_NEGO_ENABLE BIT0 +#define SYNC_NEGO_DONE BIT1 +#define WIDE_NEGO_ENABLE BIT2 +#define WIDE_NEGO_DONE BIT3 +#define WIDE_NEGO_STATE BIT4 +#define EN_TAG_QUEUEING BIT5 +#define EN_ATN_STOP BIT6 + +#define SYNC_NEGO_OFFSET 15 + +/* SCSI MSG BYTE */ +#define MSG_COMPLETE 0x00 +#define MSG_EXTENDED 0x01 +#define MSG_SAVE_PTR 0x02 +#define MSG_RESTORE_PTR 0x03 +#define MSG_DISCONNECT 0x04 +#define MSG_INITIATOR_ERROR 0x05 +#define MSG_ABORT 0x06 +#define MSG_REJECT_ 0x07 +#define MSG_NOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define MSG_LINK_CMD_COMPL 0x0A +#define MSG_LINK_CMD_COMPL_FLG 0x0B +#define MSG_BUS_RESET 0x0C +#define MSG_ABORT_TAG 0x0D +#define MSG_SIMPLE_QTAG 0x20 +#define MSG_HEAD_QTAG 0x21 +#define MSG_ORDER_QTAG 0x22 +#define MSG_IGNOREWIDE 0x23 +#define MSG_IDENTIFY 0x80 +#define MSG_HOST_ID 0xC0 + +/* SCSI STATUS BYTE */ +#define STATUS_GOOD 0x00 +#define CHECK_CONDITION_ 0x02 +#define STATUS_BUSY 0x08 +#define STATUS_INTERMEDIATE 0x10 +#define RESERVE_CONFLICT 0x18 + +/* cmd->result */ +#define STATUS_MASK_ 0xFF +#define MSG_MASK 0xFF00 +#define RETURN_MASK 0xFF0000 + +/************************************************************************/ +/* */ +/* Inquiry Data format */ +/* */ +/************************************************************************/ +struct ScsiInqData +{ /* INQ */ + u8 DevType; /* Periph Qualifier & Periph Dev Type */ + u8 RMB_TypeMod; /* rem media bit & Dev Type Modifier */ + u8 Vers; /* ISO, ECMA, & ANSI versions */ + u8 RDF; /* AEN, TRMIOP, & response data format */ + u8 AddLen; /* length of additional data */ + u8 Res1; /* reserved */ + u8 Res2; /* reserved */ + u8 Flags; /* RelADr, Wbus32, Wbus16, Sync, etc. */ + u8 VendorID[8]; /* Vendor Identification */ + u8 ProductID[16]; /* Product Identification */ + u8 ProductRev[4]; /* Product Revision */ +}; + + /* Inquiry byte 0 masks */ +#define SCSI_DEVTYPE 0x1F /* Peripheral Device Type */ +#define SCSI_PERIPHQUAL 0xE0 /* Peripheral Qualifier */ + /* Inquiry byte 1 mask */ +#define SCSI_REMOVABLE_MEDIA 0x80 /* Removable Media bit (1=removable) */ + /* Peripheral Device Type definitions */ + /* See include/scsi/scsi.h */ +#define TYPE_NODEV SCSI_DEVTYPE /* Unknown or no device type */ +#ifndef TYPE_PRINTER /* */ +# define TYPE_PRINTER 0x02 /* Printer device */ +#endif /* */ +#ifndef TYPE_COMM /* */ +# define TYPE_COMM 0x09 /* Communications device */ +#endif + +/************************************************************************/ +/* */ +/* Inquiry flag definitions (Inq data byte 7) */ +/* */ +/************************************************************************/ +#define SCSI_INQ_RELADR 0x80 /* device supports relative addressing */ +#define SCSI_INQ_WBUS32 0x40 /* device supports 32 bit data xfers */ +#define SCSI_INQ_WBUS16 0x20 /* device supports 16 bit data xfers */ +#define SCSI_INQ_SYNC 0x10 /* device supports synchronous xfer */ +#define SCSI_INQ_LINKED 0x08 /* device supports linked commands */ +#define SCSI_INQ_CMDQUEUE 0x02 /* device supports command queueing */ +#define SCSI_INQ_SFTRE 0x01 /* device supports soft resets */ + +#define ENABLE_CE 1 +#define DISABLE_CE 0 +#define EEPROM_READ 0x80 + +/************************************************************************/ +/* */ +/* The PCI configuration register offset for TRM_S1040 */ +/* */ +/************************************************************************/ +#define TRM_S1040_ID 0x00 /* Vendor and Device ID */ +#define TRM_S1040_COMMAND 0x04 /* PCI command register */ +#define TRM_S1040_IOBASE 0x10 /* I/O Space base address */ +#define TRM_S1040_ROMBASE 0x30 /* Expansion ROM Base Address */ +#define TRM_S1040_INTLINE 0x3C /* Interrupt line */ + +/************************************************************************/ +/* */ +/* The SCSI register offset for TRM_S1040 */ +/* */ +/************************************************************************/ +#define TRM_S1040_SCSI_STATUS 0x80 /* SCSI Status (R) */ +#define COMMANDPHASEDONE 0x2000 /* SCSI command phase done */ +#define SCSIXFERDONE 0x0800 /* SCSI SCSI transfer done */ +#define SCSIXFERCNT_2_ZERO 0x0100 /* SCSI SCSI transfer count to zero */ +#define SCSIINTERRUPT 0x0080 /* SCSI interrupt pending */ +#define COMMANDABORT 0x0040 /* SCSI command abort */ +#define SEQUENCERACTIVE 0x0020 /* SCSI sequencer active */ +#define PHASEMISMATCH 0x0010 /* SCSI phase mismatch */ +#define PARITYERROR 0x0008 /* SCSI parity error */ + +#define PHASEMASK 0x0007 /* Phase MSG/CD/IO */ +#define PH_DATA_OUT 0x00 /* Data out phase */ +#define PH_DATA_IN 0x01 /* Data in phase */ +#define PH_COMMAND 0x02 /* Command phase */ +#define PH_STATUS 0x03 /* Status phase */ +#define PH_BUS_FREE 0x05 /* Invalid phase used as bus free */ +#define PH_MSG_OUT 0x06 /* Message out phase */ +#define PH_MSG_IN 0x07 /* Message in phase */ + +#define TRM_S1040_SCSI_CONTROL 0x80 /* SCSI Control (W) */ +#define DO_CLRATN 0x0400 /* Clear ATN */ +#define DO_SETATN 0x0200 /* Set ATN */ +#define DO_CMDABORT 0x0100 /* Abort SCSI command */ +#define DO_RSTMODULE 0x0010 /* Reset SCSI chip */ +#define DO_RSTSCSI 0x0008 /* Reset SCSI bus */ +#define DO_CLRFIFO 0x0004 /* Clear SCSI transfer FIFO */ +#define DO_DATALATCH 0x0002 /* Enable SCSI bus data input (latched) */ +/* #define DO_DATALATCH 0x0000 */ /* KG: DISable SCSI bus data latch */ +#define DO_HWRESELECT 0x0001 /* Enable hardware reselection */ + +#define TRM_S1040_SCSI_FIFOCNT 0x82 /* SCSI FIFO Counter 5bits(R) */ +#define TRM_S1040_SCSI_SIGNAL 0x83 /* SCSI low level signal (R/W) */ + +#define TRM_S1040_SCSI_INTSTATUS 0x84 /* SCSI Interrupt Status (R) */ +#define INT_SCAM 0x80 /* SCAM selection interrupt */ +#define INT_SELECT 0x40 /* Selection interrupt */ +#define INT_SELTIMEOUT 0x20 /* Selection timeout interrupt */ +#define INT_DISCONNECT 0x10 /* Bus disconnected interrupt */ +#define INT_RESELECTED 0x08 /* Reselected interrupt */ +#define INT_SCSIRESET 0x04 /* SCSI reset detected interrupt */ +#define INT_BUSSERVICE 0x02 /* Bus service interrupt */ +#define INT_CMDDONE 0x01 /* SCSI command done interrupt */ + +#define TRM_S1040_SCSI_OFFSET 0x84 /* SCSI Offset Count (W) */ + +/************************************************************************/ +/* */ +/* Bit Name Definition */ +/* --------- ------------- ---------------------------- */ +/* 07-05 0 RSVD Reversed. Always 0. */ +/* 04 0 OFFSET4 Reversed for LVDS. Always 0. */ +/* 03-00 0 OFFSET[03:00] Offset number from 0 to 15 */ +/* */ +/************************************************************************/ + +#define TRM_S1040_SCSI_SYNC 0x85 /* SCSI Synchronous Control (R/W) */ +#define LVDS_SYNC 0x20 /* Enable LVDS synchronous */ +#define WIDE_SYNC 0x10 /* Enable WIDE synchronous */ +#define ALT_SYNC 0x08 /* Enable Fast-20 alternate synchronous */ + +/************************************************************************/ +/* */ +/* SYNCM 7 6 5 4 3 2 1 0 */ +/* Name RSVD RSVD LVDS WIDE ALTPERD PERIOD2 PERIOD1 PERIOD0 */ +/* Default 0 0 0 0 0 0 0 0 */ +/* */ +/* Bit Name Definition */ +/* --------- ------------- --------------------------- */ +/* 07-06 0 RSVD Reversed. Always read 0 */ +/* 05 0 LVDS Reversed. Always read 0 */ +/* 04 0 WIDE/WSCSI Enable wide (16-bits) SCSI */ +/* transfer. */ +/* 03 0 ALTPERD/ALTPD Alternate (Sync./Period) mode. */ +/* */ +/* @@ When this bit is set, */ +/* the synchronous period bits 2:0 */ +/* in the Synchronous Mode register */ +/* are used to transfer data */ +/* at the Fast-20 rate. */ +/* @@ When this bit is unset, */ +/* the synchronous period bits 2:0 */ +/* in the Synchronous Mode Register */ +/* are used to transfer data */ +/* at the Fast-10 rate (or Fast-40 w/ LVDS). */ +/* */ +/* 02-00 0 PERIOD[2:0]/ Synchronous SCSI Transfer Rate. */ +/* SXPD[02:00] These 3 bits specify */ +/* the Synchronous SCSI Transfer */ +/* Rate for Fast-20 and Fast-10. */ +/* These bits are also reset */ +/* by a SCSI Bus reset. */ +/* */ +/* For Fast-10 bit ALTPD = 0 and LVDS = 0 */ +/* and bit2,bit1,bit0 is defined as follows : */ +/* */ +/* 000 100ns, 10.0 MHz */ +/* 001 150ns, 6.6 MHz */ +/* 010 200ns, 5.0 MHz */ +/* 011 250ns, 4.0 MHz */ +/* 100 300ns, 3.3 MHz */ +/* 101 350ns, 2.8 MHz */ +/* 110 400ns, 2.5 MHz */ +/* 111 450ns, 2.2 MHz */ +/* */ +/* For Fast-20 bit ALTPD = 1 and LVDS = 0 */ +/* and bit2,bit1,bit0 is defined as follows : */ +/* */ +/* 000 50ns, 20.0 MHz */ +/* 001 75ns, 13.3 MHz */ +/* 010 100ns, 10.0 MHz */ +/* 011 125ns, 8.0 MHz */ +/* 100 150ns, 6.6 MHz */ +/* 101 175ns, 5.7 MHz */ +/* 110 200ns, 5.0 MHz */ +/* 111 250ns, 4.0 MHz KG: Maybe 225ns, 4.4 MHz */ +/* */ +/* For Fast-40 bit ALTPD = 0 and LVDS = 1 */ +/* and bit2,bit1,bit0 is defined as follows : */ +/* */ +/* 000 25ns, 40.0 MHz */ +/* 001 50ns, 20.0 MHz */ +/* 010 75ns, 13.3 MHz */ +/* 011 100ns, 10.0 MHz */ +/* 100 125ns, 8.0 MHz */ +/* 101 150ns, 6.6 MHz */ +/* 110 175ns, 5.7 MHz */ +/* 111 200ns, 5.0 MHz */ +/* */ +/************************************************************************/ + +#define TRM_S1040_SCSI_TARGETID 0x86 /* SCSI Target ID (R/W) */ +#define TRM_S1040_SCSI_IDMSG 0x87 /* SCSI Identify Message (R) */ +#define TRM_S1040_SCSI_HOSTID 0x87 /* SCSI Host ID (W) */ +#define TRM_S1040_SCSI_COUNTER 0x88 /* SCSI Transfer Counter 24bits(R/W) */ + +#define TRM_S1040_SCSI_INTEN 0x8C /* SCSI Interrupt Enable (R/W) */ +#define EN_SCAM 0x80 /* Enable SCAM selection interrupt */ +#define EN_SELECT 0x40 /* Enable selection interrupt */ +#define EN_SELTIMEOUT 0x20 /* Enable selection timeout interrupt */ +#define EN_DISCONNECT 0x10 /* Enable bus disconnected interrupt */ +#define EN_RESELECTED 0x08 /* Enable reselected interrupt */ +#define EN_SCSIRESET 0x04 /* Enable SCSI reset detected interrupt */ +#define EN_BUSSERVICE 0x02 /* Enable bus service interrupt */ +#define EN_CMDDONE 0x01 /* Enable SCSI command done interrupt */ + +#define TRM_S1040_SCSI_CONFIG0 0x8D /* SCSI Configuration 0 (R/W) */ +#define PHASELATCH 0x40 /* Enable phase latch */ +#define INITIATOR 0x20 /* Enable initiator mode */ +#define PARITYCHECK 0x10 /* Enable parity check */ +#define BLOCKRST 0x01 /* Disable SCSI reset1 */ + +#define TRM_S1040_SCSI_CONFIG1 0x8E /* SCSI Configuration 1 (R/W) */ +#define ACTIVE_NEGPLUS 0x10 /* Enhance active negation */ +#define FILTER_DISABLE 0x08 /* Disable SCSI data filter */ +#define FAST_FILTER 0x04 /* ? */ +#define ACTIVE_NEG 0x02 /* Enable active negation */ + +#define TRM_S1040_SCSI_CONFIG2 0x8F /* SCSI Configuration 2 (R/W) */ +#define CFG2_WIDEFIFO 0x02 /* */ + +#define TRM_S1040_SCSI_COMMAND 0x90 /* SCSI Command (R/W) */ +#define SCMD_COMP 0x12 /* Command complete */ +#define SCMD_SEL_ATN 0x60 /* Selection with ATN */ +#define SCMD_SEL_ATN3 0x64 /* Selection with ATN3 */ +#define SCMD_SEL_ATNSTOP 0xB8 /* Selection with ATN and Stop */ +#define SCMD_FIFO_OUT 0xC0 /* SCSI FIFO transfer out */ +#define SCMD_DMA_OUT 0xC1 /* SCSI DMA transfer out */ +#define SCMD_FIFO_IN 0xC2 /* SCSI FIFO transfer in */ +#define SCMD_DMA_IN 0xC3 /* SCSI DMA transfer in */ +#define SCMD_MSGACCEPT 0xD8 /* Message accept */ + +/************************************************************************/ +/* */ +/* Code Command Description */ +/* ---- ---------------------------------------- */ +/* 02 Enable reselection with FIFO */ +/* 40 Select without ATN with FIFO */ +/* 60 Select with ATN with FIFO */ +/* 64 Select with ATN3 with FIFO */ +/* A0 Select with ATN and stop with FIFO */ +/* C0 Transfer information out with FIFO */ +/* C1 Transfer information out with DMA */ +/* C2 Transfer information in with FIFO */ +/* C3 Transfer information in with DMA */ +/* 12 Initiator command complete with FIFO */ +/* 50 Initiator transfer information out sequence without ATN */ +/* with FIFO */ +/* 70 Initiator transfer information out sequence with ATN */ +/* with FIFO */ +/* 74 Initiator transfer information out sequence with ATN3 */ +/* with FIFO */ +/* 52 Initiator transfer information in sequence without ATN */ +/* with FIFO */ +/* 72 Initiator transfer information in sequence with ATN */ +/* with FIFO */ +/* 76 Initiator transfer information in sequence with ATN3 */ +/* with FIFO */ +/* 90 Initiator transfer information out command complete */ +/* with FIFO */ +/* 92 Initiator transfer information in command complete */ +/* with FIFO */ +/* D2 Enable selection */ +/* 08 Reselection */ +/* 48 Disconnect command with FIFO */ +/* 88 Terminate command with FIFO */ +/* C8 Target command complete with FIFO */ +/* 18 SCAM Arbitration/ Selection */ +/* 5A Enable reselection */ +/* 98 Select without ATN with FIFO */ +/* B8 Select with ATN with FIFO */ +/* D8 Message Accepted */ +/* 58 NOP */ +/* */ +/************************************************************************/ + +#define TRM_S1040_SCSI_TIMEOUT 0x91 /* SCSI Time Out Value (R/W) */ +#define TRM_S1040_SCSI_FIFO 0x98 /* SCSI FIFO (R/W) */ + +#define TRM_S1040_SCSI_TCR0 0x9C /* SCSI Target Control 0 (R/W) */ +#define TCR0_WIDE_NEGO_DONE 0x8000 /* Wide nego done */ +#define TCR0_SYNC_NEGO_DONE 0x4000 /* Synchronous nego done */ +#define TCR0_ENABLE_LVDS 0x2000 /* Enable LVDS synchronous */ +#define TCR0_ENABLE_WIDE 0x1000 /* Enable WIDE synchronous */ +#define TCR0_ENABLE_ALT 0x0800 /* Enable alternate synchronous */ +#define TCR0_PERIOD_MASK 0x0700 /* Transfer rate */ + +#define TCR0_DO_WIDE_NEGO 0x0080 /* Do wide NEGO */ +#define TCR0_DO_SYNC_NEGO 0x0040 /* Do sync NEGO */ +#define TCR0_DISCONNECT_EN 0x0020 /* Disconnection enable */ +#define TCR0_OFFSET_MASK 0x001F /* Offset number */ + +#define TRM_S1040_SCSI_TCR1 0x9E /* SCSI Target Control 1 (R/W) */ +#define MAXTAG_MASK 0x7F00 /* Maximum tags (127) */ +#define NON_TAG_BUSY 0x0080 /* Non tag command active */ +#define ACTTAG_MASK 0x007F /* Active tags */ + +/************************************************************************/ +/* */ +/* The DMA register offset for TRM_S1040 */ +/* */ +/************************************************************************/ +#define TRM_S1040_DMA_COMMAND 0xA0 /* DMA Command (R/W) */ +#define DMACMD_SG 0x02 /* Enable HW S/G support */ +#define DMACMD_DIR 0x01 /* 1 = read from SCSI write to Host */ +#define XFERDATAIN_SG 0x0103 /* Transfer data in w/ SG */ +#define XFERDATAOUT_SG 0x0102 /* Transfer data out w/ SG */ +#define XFERDATAIN 0x0101 /* Transfer data in w/o SG */ +#define XFERDATAOUT 0x0100 /* Transfer data out w/o SG */ + +#define TRM_S1040_DMA_FIFOCNT 0xA1 /* DMA FIFO Counter (R) */ + +#define TRM_S1040_DMA_CONTROL 0xA1 /* DMA Control (W) */ +#define DMARESETMODULE 0x10 /* Reset PCI/DMA module */ +#define STOPDMAXFER 0x08 /* Stop DMA transfer */ +#define ABORTXFER 0x04 /* Abort DMA transfer */ +#define CLRXFIFO 0x02 /* Clear DMA transfer FIFO */ +#define STARTDMAXFER 0x01 /* Start DMA transfer */ + +#define TRM_S1040_DMA_FIFOSTAT 0xA2 /* DMA FIFO Status (R) */ + +#define TRM_S1040_DMA_STATUS 0xA3 /* DMA Interrupt Status (R/W) */ +#define XFERPENDING 0x80 /* Transfer pending */ +#define SCSIBUSY 0x40 /* SCSI busy */ +#define GLOBALINT 0x20 /* DMA_INTEN bit 0-4 set */ +#define FORCEDMACOMP 0x10 /* Force DMA transfer complete */ +#define DMAXFERERROR 0x08 /* DMA transfer error */ +#define DMAXFERABORT 0x04 /* DMA transfer abort */ +#define DMAXFERCOMP 0x02 /* Bus Master XFER Complete status */ +#define SCSICOMP 0x01 /* SCSI complete interrupt */ + +#define TRM_S1040_DMA_INTEN 0xA4 /* DMA Interrupt Enable (R/W) */ +#define EN_FORCEDMACOMP 0x10 /* Force DMA transfer complete */ +#define EN_DMAXFERERROR 0x08 /* DMA transfer error */ +#define EN_DMAXFERABORT 0x04 /* DMA transfer abort */ +#define EN_DMAXFERCOMP 0x02 /* Bus Master XFER Complete status */ +#define EN_SCSIINTR 0x01 /* Enable SCSI complete interrupt */ + +#define TRM_S1040_DMA_CONFIG 0xA6 /* DMA Configuration (R/W) */ +#define DMA_ENHANCE 0x8000 /* Enable DMA enhance feature (SG?) */ +#define DMA_PCI_DUAL_ADDR 0x4000 /* */ +#define DMA_CFG_RES 0x2000 /* Always 1 */ +#define DMA_AUTO_CLR_FIFO 0x1000 /* DISable DMA auto clear FIFO */ +#define DMA_MEM_MULTI_READ 0x0800 /* */ +#define DMA_MEM_WRITE_INVAL 0x0400 /* Memory write and invalidate */ +#define DMA_FIFO_CTRL 0x0300 /* Control FIFO operation with DMA */ +#define DMA_FIFO_HALF_HALF 0x0200 /* Keep half filled on both read/write */ + +#define TRM_S1040_DMA_XCNT 0xA8 /* DMA Transfer Counter (R/W), 24bits */ +#define TRM_S1040_DMA_CXCNT 0xAC /* DMA Current Transfer Counter (R) */ +#define TRM_S1040_DMA_XLOWADDR 0xB0 /* DMA Transfer Physical Low Address */ +#define TRM_S1040_DMA_XHIGHADDR 0xB4 /* DMA Transfer Physical High Address */ + +/************************************************************************/ +/* */ +/* The general register offset for TRM_S1040 */ +/* */ +/************************************************************************/ +#define TRM_S1040_GEN_CONTROL 0xD4 /* Global Control */ +#define CTRL_LED 0x80 /* Control onboard LED */ +#define EN_EEPROM 0x10 /* Enable EEPROM programming */ +#define DIS_TERM 0x08 /* Disable onboard termination */ +#define AUTOTERM 0x04 /* Enable Auto SCSI terminator */ +#define LOW8TERM 0x02 /* Enable Lower 8 bit SCSI terminator */ +#define UP8TERM 0x01 /* Enable Upper 8 bit SCSI terminator */ + +#define TRM_S1040_GEN_STATUS 0xD5 /* Global Status */ +#define GTIMEOUT 0x80 /* Global timer reach 0 */ +#define EXT68HIGH 0x40 /* Higher 8 bit connected externally */ +#define INT68HIGH 0x20 /* Higher 8 bit connected internally */ +#define CON5068 0x10 /* External 50/68 pin connected (low) */ +#define CON68 0x08 /* Internal 68 pin connected (low) */ +#define CON50 0x04 /* Internal 50 pin connected (low!) */ +#define WIDESCSI 0x02 /* Wide SCSI card */ +#define STATUS_LOAD_DEFAULT 0x01 /* */ + +#define TRM_S1040_GEN_NVRAM 0xD6 /* Serial NON-VOLATILE RAM port */ +#define NVR_BITOUT 0x08 /* Serial data out */ +#define NVR_BITIN 0x04 /* Serial data in */ +#define NVR_CLOCK 0x02 /* Serial clock */ +#define NVR_SELECT 0x01 /* Serial select */ + +#define TRM_S1040_GEN_EDATA 0xD7 /* Parallel EEPROM data port */ +#define TRM_S1040_GEN_EADDRESS 0xD8 /* Parallel EEPROM address */ +#define TRM_S1040_GEN_TIMER 0xDB /* Global timer */ + +/************************************************************************/ +/* */ +/* NvmTarCfg0: Target configuration byte 0 :..pDCB->DevMode */ +/* */ +/************************************************************************/ +#define NTC_DO_WIDE_NEGO 0x20 /* Wide negotiate */ +#define NTC_DO_TAG_QUEUEING 0x10 /* Enable SCSI tag queuing */ +#define NTC_DO_SEND_START 0x08 /* Send start command SPINUP */ +#define NTC_DO_DISCONNECT 0x04 /* Enable SCSI disconnect */ +#define NTC_DO_SYNC_NEGO 0x02 /* Sync negotiation */ +#define NTC_DO_PARITY_CHK 0x01 /* (it sould define at NAC) */ + /* Parity check enable */ + +/************************************************************************/ +/* */ +/* Nvram Initiater bits definition */ +/* */ +/************************************************************************/ +#if 0 +#define MORE2_DRV BIT0 +#define GREATER_1G BIT1 +#define RST_SCSI_BUS BIT2 +#define ACTIVE_NEGATION BIT3 +#define NO_SEEK BIT4 +#define LUN_CHECK BIT5 +#endif + +/************************************************************************/ +/* */ +/* Nvram Adapter Cfg bits definition */ +/* */ +/************************************************************************/ +#define NAC_SCANLUN 0x20 /* Include LUN as BIOS device */ +#define NAC_POWERON_SCSI_RESET 0x04 /* Power on reset enable */ +#define NAC_GREATER_1G 0x02 /* > 1G support enable */ +#define NAC_GT2DRIVES 0x01 /* Support more than 2 drives */ +/* #define NAC_DO_PARITY_CHK 0x08 */ /* Parity check enable */ + +#endif |
