| /****************************************************************************** |
| ** Device driver for the PCI-SCSI NCR538XX controller family. |
| ** |
| ** Copyright (C) 1994 Wolfgang Stanglmeier |
| ** |
| ** This program is free software; you can redistribute it and/or modify |
| ** it under the terms of the GNU General Public License as published by |
| ** the Free Software Foundation; either version 2 of the License, or |
| ** (at your option) any later version. |
| ** |
| ** This program is distributed in the hope that it will be useful, |
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| ** GNU General Public License for more details. |
| ** |
| ** You should have received a copy of the GNU General Public License |
| ** along with this program; if not, write to the Free Software |
| ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| ** |
| **----------------------------------------------------------------------------- |
| ** |
| ** This driver has been ported to Linux from the FreeBSD NCR53C8XX driver |
| ** and is currently maintained by |
| ** |
| ** Gerard Roudier <groudier@free.fr> |
| ** |
| ** Being given that this driver originates from the FreeBSD version, and |
| ** in order to keep synergy on both, any suggested enhancements and corrections |
| ** received on Linux are automatically a potential candidate for the FreeBSD |
| ** version. |
| ** |
| ** The original driver has been written for 386bsd and FreeBSD by |
| ** Wolfgang Stanglmeier <wolf@cologne.de> |
| ** Stefan Esser <se@mi.Uni-Koeln.de> |
| ** |
| ** And has been ported to NetBSD by |
| ** Charles M. Hannum <mycroft@gnu.ai.mit.edu> |
| ** |
| **----------------------------------------------------------------------------- |
| ** |
| ** Brief history |
| ** |
| ** December 10 1995 by Gerard Roudier: |
| ** Initial port to Linux. |
| ** |
| ** June 23 1996 by Gerard Roudier: |
| ** Support for 64 bits architectures (Alpha). |
| ** |
| ** November 30 1996 by Gerard Roudier: |
| ** Support for Fast-20 scsi. |
| ** Support for large DMA fifo and 128 dwords bursting. |
| ** |
| ** February 27 1997 by Gerard Roudier: |
| ** Support for Fast-40 scsi. |
| ** Support for on-Board RAM. |
| ** |
| ** May 3 1997 by Gerard Roudier: |
| ** Full support for scsi scripts instructions pre-fetching. |
| ** |
| ** May 19 1997 by Richard Waltham <dormouse@farsrobt.demon.co.uk>: |
| ** Support for NvRAM detection and reading. |
| ** |
| ** August 18 1997 by Cort <cort@cs.nmt.edu>: |
| ** Support for Power/PC (Big Endian). |
| ** |
| ** June 20 1998 by Gerard Roudier |
| ** Support for up to 64 tags per lun. |
| ** O(1) everywhere (C and SCRIPTS) for normal cases. |
| ** Low PCI traffic for command handling when on-chip RAM is present. |
| ** Aggressive SCSI SCRIPTS optimizations. |
| ** |
| ** 2005 by Matthew Wilcox and James Bottomley |
| ** PCI-ectomy. This driver now supports only the 720 chip (see the |
| ** NCR_Q720 and zalon drivers for the bus probe logic). |
| ** |
| ******************************************************************************* |
| */ |
| |
| /* |
| ** Supported SCSI-II features: |
| ** Synchronous negotiation |
| ** Wide negotiation (depends on the NCR Chip) |
| ** Enable disconnection |
| ** Tagged command queuing |
| ** Parity checking |
| ** Etc... |
| ** |
| ** Supported NCR/SYMBIOS chips: |
| ** 53C720 (Wide, Fast SCSI-2, intfly problems) |
| */ |
| |
| /* Name and version of the driver */ |
| #define SCSI_NCR_DRIVER_NAME "ncr53c8xx-3.4.3g" |
| |
| #define SCSI_NCR_DEBUG_FLAGS (0) |
| |
| #include <linux/blkdev.h> |
| #include <linux/delay.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/errno.h> |
| #include <linux/gfp.h> |
| #include <linux/init.h> |
| #include <linux/interrupt.h> |
| #include <linux/ioport.h> |
| #include <linux/mm.h> |
| #include <linux/module.h> |
| #include <linux/sched.h> |
| #include <linux/signal.h> |
| #include <linux/spinlock.h> |
| #include <linux/stat.h> |
| #include <linux/string.h> |
| #include <linux/time.h> |
| #include <linux/timer.h> |
| #include <linux/types.h> |
| |
| #include <asm/dma.h> |
| #include <asm/io.h> |
| |
| #include <scsi/scsi.h> |
| #include <scsi/scsi_cmnd.h> |
| #include <scsi/scsi_dbg.h> |
| #include <scsi/scsi_device.h> |
| #include <scsi/scsi_tcq.h> |
| #include <scsi/scsi_transport.h> |
| #include <scsi/scsi_transport_spi.h> |
| |
| #include "ncr53c8xx.h" |
| |
| #define NAME53C8XX "ncr53c8xx" |
| |
| /*========================================================== |
| ** |
| ** Debugging tags |
| ** |
| **========================================================== |
| */ |
| |
| #define DEBUG_ALLOC (0x0001) |
| #define DEBUG_PHASE (0x0002) |
| #define DEBUG_QUEUE (0x0008) |
| #define DEBUG_RESULT (0x0010) |
| #define DEBUG_POINTER (0x0020) |
| #define DEBUG_SCRIPT (0x0040) |
| #define DEBUG_TINY (0x0080) |
| #define DEBUG_TIMING (0x0100) |
| #define DEBUG_NEGO (0x0200) |
| #define DEBUG_TAGS (0x0400) |
| #define DEBUG_SCATTER (0x0800) |
| #define DEBUG_IC (0x1000) |
| |
| /* |
| ** Enable/Disable debug messages. |
| ** Can be changed at runtime too. |
| */ |
| |
| #ifdef SCSI_NCR_DEBUG_INFO_SUPPORT |
| static int ncr_debug = SCSI_NCR_DEBUG_FLAGS; |
| #define DEBUG_FLAGS ncr_debug |
| #else |
| #define DEBUG_FLAGS SCSI_NCR_DEBUG_FLAGS |
| #endif |
| |
| static inline struct list_head *ncr_list_pop(struct list_head *head) |
| { |
| if (!list_empty(head)) { |
| struct list_head *elem = head->next; |
| |
| list_del(elem); |
| return elem; |
| } |
| |
| return NULL; |
| } |
| |
| /*========================================================== |
| ** |
| ** Simple power of two buddy-like allocator. |
| ** |
| ** This simple code is not intended to be fast, but to |
| ** provide power of 2 aligned memory allocations. |
| ** Since the SCRIPTS processor only supplies 8 bit |
| ** arithmetic, this allocator allows simple and fast |
| ** address calculations from the SCRIPTS code. |
| ** In addition, cache line alignment is guaranteed for |
| ** power of 2 cache line size. |
| ** Enhanced in linux-2.3.44 to provide a memory pool |
| ** per pcidev to support dynamic dma mapping. (I would |
| ** have preferred a real bus abstraction, btw). |
| ** |
| **========================================================== |
| */ |
| |
| #define MEMO_SHIFT 4 /* 16 bytes minimum memory chunk */ |
| #if PAGE_SIZE >= 8192 |
| #define MEMO_PAGE_ORDER 0 /* 1 PAGE maximum */ |
| #else |
| #define MEMO_PAGE_ORDER 1 /* 2 PAGES maximum */ |
| #endif |
| #define MEMO_FREE_UNUSED /* Free unused pages immediately */ |
| #define MEMO_WARN 1 |
| #define MEMO_GFP_FLAGS GFP_ATOMIC |
| #define MEMO_CLUSTER_SHIFT (PAGE_SHIFT+MEMO_PAGE_ORDER) |
| #define MEMO_CLUSTER_SIZE (1UL << MEMO_CLUSTER_SHIFT) |
| #define MEMO_CLUSTER_MASK (MEMO_CLUSTER_SIZE-1) |
| |
| typedef u_long m_addr_t; /* Enough bits to bit-hack addresses */ |
| typedef struct device *m_bush_t; /* Something that addresses DMAable */ |
| |
| typedef struct m_link { /* Link between free memory chunks */ |
| struct m_link *next; |
| } m_link_s; |
| |
| typedef struct m_vtob { /* Virtual to Bus address translation */ |
| struct m_vtob *next; |
| m_addr_t vaddr; |
| m_addr_t baddr; |
| } m_vtob_s; |
| #define VTOB_HASH_SHIFT 5 |
| #define VTOB_HASH_SIZE (1UL << VTOB_HASH_SHIFT) |
| #define VTOB_HASH_MASK (VTOB_HASH_SIZE-1) |
| #define VTOB_HASH_CODE(m) \ |
| ((((m_addr_t) (m)) >> MEMO_CLUSTER_SHIFT) & VTOB_HASH_MASK) |
| |
| typedef struct m_pool { /* Memory pool of a given kind */ |
| m_bush_t bush; |
| m_addr_t (*getp)(struct m_pool *); |
| void (*freep)(struct m_pool *, m_addr_t); |
| int nump; |
| m_vtob_s *(vtob[VTOB_HASH_SIZE]); |
| struct m_pool *next; |
| struct m_link h[PAGE_SHIFT-MEMO_SHIFT+MEMO_PAGE_ORDER+1]; |
| } m_pool_s; |
| |
| static void *___m_alloc(m_pool_s *mp, int size) |
| { |
| int i = 0; |
| int s = (1 << MEMO_SHIFT); |
| int j; |
| m_addr_t a; |
| m_link_s *h = mp->h; |
| |
| if (size > (PAGE_SIZE << MEMO_PAGE_ORDER)) |
| return NULL; |
| |
| while (size > s) { |
| s <<= 1; |
| ++i; |
| } |
| |
| j = i; |
| while (!h[j].next) { |
| if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) { |
| h[j].next = (m_link_s *)mp->getp(mp); |
| if (h[j].next) |
| h[j].next->next = NULL; |
| break; |
| } |
| ++j; |
| s <<= 1; |
| } |
| a = (m_addr_t) h[j].next; |
| if (a) { |
| h[j].next = h[j].next->next; |
| while (j > i) { |
| j -= 1; |
| s >>= 1; |
| h[j].next = (m_link_s *) (a+s); |
| h[j].next->next = NULL; |
| } |
| } |
| #ifdef DEBUG |
| printk("___m_alloc(%d) = %p\n", size, (void *) a); |
| #endif |
| return (void *) a; |
| } |
| |
| static void ___m_free(m_pool_s *mp, void *ptr, int size) |
| { |
| int i = 0; |
| int s = (1 << MEMO_SHIFT); |
| m_link_s *q; |
| m_addr_t a, b; |
| m_link_s *h = mp->h; |
| |
| #ifdef DEBUG |
| printk("___m_free(%p, %d)\n", ptr, size); |
| #endif |
| |
| if (size > (PAGE_SIZE << MEMO_PAGE_ORDER)) |
| return; |
| |
| while (size > s) { |
| s <<= 1; |
| ++i; |
| } |
| |
| a = (m_addr_t) ptr; |
| |
| while (1) { |
| #ifdef MEMO_FREE_UNUSED |
| if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) { |
| mp->freep(mp, a); |
| break; |
| } |
| #endif |
| b = a ^ s; |
| q = &h[i]; |
| while (q->next && q->next != (m_link_s *) b) { |
| q = q->next; |
| } |
| if (!q->next) { |
| ((m_link_s *) a)->next = h[i].next; |
| h[i].next = (m_link_s *) a; |
| break; |
| } |
| q->next = q->next->next; |
| a = a & b; |
| s <<= 1; |
| ++i; |
| } |
| } |
| |
| static DEFINE_SPINLOCK(ncr53c8xx_lock); |
| |
| static void *__m_calloc2(m_pool_s *mp, int size, char *name, int uflags) |
| { |
| void *p; |
| |
| p = ___m_alloc(mp, size); |
| |
| if (DEBUG_FLAGS & DEBUG_ALLOC) |
| printk ("new %-10s[%4d] @%p.\n", name, size, p); |
| |
| if (p) |
| memset(p, 0, size); |
| else if (uflags & MEMO_WARN) |
| printk (NAME53C8XX ": failed to allocate %s[%d]\n", name, size); |
| |
| return p; |
| } |
| |
| #define __m_calloc(mp, s, n) __m_calloc2(mp, s, n, MEMO_WARN) |
| |
| static void __m_free(m_pool_s *mp, void *ptr, int size, char *name) |
| { |
| if (DEBUG_FLAGS & DEBUG_ALLOC) |
| printk ("freeing %-10s[%4d] @%p.\n", name, size, ptr); |
| |
| ___m_free(mp, ptr, size); |
| |
| } |
| |
| /* |
| * With pci bus iommu support, we use a default pool of unmapped memory |
| * for memory we donnot need to DMA from/to and one pool per pcidev for |
| * memory accessed by the PCI chip. `mp0' is the default not DMAable pool. |
| */ |
| |
| static m_addr_t ___mp0_getp(m_pool_s *mp) |
| { |
| m_addr_t m = __get_free_pages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER); |
| if (m) |
| ++mp->nump; |
| return m; |
| } |
| |
| static void ___mp0_freep(m_pool_s *mp, m_addr_t m) |
| { |
| free_pages(m, MEMO_PAGE_ORDER); |
| --mp->nump; |
| } |
| |
| static m_pool_s mp0 = {NULL, ___mp0_getp, ___mp0_freep}; |
| |
| /* |
| * DMAable pools. |
| */ |
| |
| /* |
| * With pci bus iommu support, we maintain one pool per pcidev and a |
| * hashed reverse table for virtual to bus physical address translations. |
| */ |
| static m_addr_t ___dma_getp(m_pool_s *mp) |
| { |
| m_addr_t vp; |
| m_vtob_s *vbp; |
| |
| vbp = __m_calloc(&mp0, sizeof(*vbp), "VTOB"); |
| if (vbp) { |
| dma_addr_t daddr; |
| vp = (m_addr_t) dma_alloc_coherent(mp->bush, |
| PAGE_SIZE<<MEMO_PAGE_ORDER, |
| &daddr, GFP_ATOMIC); |
| if (vp) { |
| int hc = VTOB_HASH_CODE(vp); |
| vbp->vaddr = vp; |
| vbp->baddr = daddr; |
| vbp->next = mp->vtob[hc]; |
| mp->vtob[hc] = vbp; |
| ++mp->nump; |
| return vp; |
| } |
| } |
| if (vbp) |
| __m_free(&mp0, vbp, sizeof(*vbp), "VTOB"); |
| return 0; |
| } |
| |
| static void ___dma_freep(m_pool_s *mp, m_addr_t m) |
| { |
| m_vtob_s **vbpp, *vbp; |
| int hc = VTOB_HASH_CODE(m); |
| |
| vbpp = &mp->vtob[hc]; |
| while (*vbpp && (*vbpp)->vaddr != m) |
| vbpp = &(*vbpp)->next; |
| if (*vbpp) { |
| vbp = *vbpp; |
| *vbpp = (*vbpp)->next; |
| dma_free_coherent(mp->bush, PAGE_SIZE<<MEMO_PAGE_ORDER, |
| (void *)vbp->vaddr, (dma_addr_t)vbp->baddr); |
| __m_free(&mp0, vbp, sizeof(*vbp), "VTOB"); |
| --mp->nump; |
| } |
| } |
| |
| static inline m_pool_s *___get_dma_pool(m_bush_t bush) |
| { |
| m_pool_s *mp; |
| for (mp = mp0.next; mp && mp->bush != bush; mp = mp->next); |
| return mp; |
| } |
| |
| static m_pool_s *___cre_dma_pool(m_bush_t bush) |
| { |
| m_pool_s *mp; |
| mp = __m_calloc(&mp0, sizeof(*mp), "MPOOL"); |
| if (mp) { |
| memset(mp, 0, sizeof(*mp)); |
| mp->bush = bush; |
| mp->getp = ___dma_getp; |
| mp->freep = ___dma_freep; |
| mp->next = mp0.next; |
| mp0.next = mp; |
| } |
| return mp; |
| } |
| |
| static void ___del_dma_pool(m_pool_s *p) |
| { |
| struct m_pool **pp = &mp0.next; |
| |
| while (*pp && *pp != p) |
| pp = &(*pp)->next; |
| if (*pp) { |
| *pp = (*pp)->next; |
| __m_free(&mp0, p, sizeof(*p), "MPOOL"); |
| } |
| } |
| |
| static void *__m_calloc_dma(m_bush_t bush, int size, char *name) |
| { |
| u_long flags; |
| struct m_pool *mp; |
| void *m = NULL; |
| |
| spin_lock_irqsave(&ncr53c8xx_lock, flags); |
| mp = ___get_dma_pool(bush); |
| if (!mp) |
| mp = ___cre_dma_pool(bush); |
| if (mp) |
| m = __m_calloc(mp, size, name); |
| if (mp && !mp->nump) |
| ___del_dma_pool(mp); |
| spin_unlock_irqrestore(&ncr53c8xx_lock, flags); |
| |
| return m; |
| } |
| |
| static void __m_free_dma(m_bush_t bush, void *m, int size, char *name) |
| { |
| u_long flags; |
| struct m_pool *mp; |
| |
| spin_lock_irqsave(&ncr53c8xx_lock, flags); |
| mp = ___get_dma_pool(bush); |
| if (mp) |
| __m_free(mp, m, size, name); |
| if (mp && !mp->nump) |
| ___del_dma_pool(mp); |
| spin_unlock_irqrestore(&ncr53c8xx_lock, flags); |
| } |
| |
| static m_addr_t __vtobus(m_bush_t bush, void *m) |
| { |
| u_long flags; |
| m_pool_s *mp; |
| int hc = VTOB_HASH_CODE(m); |
| m_vtob_s *vp = NULL; |
| m_addr_t a = ((m_addr_t) m) & ~MEMO_CLUSTER_MASK; |
| |
| spin_lock_irqsave(&ncr53c8xx_lock, flags); |
| mp = ___get_dma_pool(bush); |
| if (mp) { |
| vp = mp->vtob[hc]; |
| while (vp && (m_addr_t) vp->vaddr != a) |
| vp = vp->next; |
| } |
| spin_unlock_irqrestore(&ncr53c8xx_lock, flags); |
| return vp ? vp->baddr + (((m_addr_t) m) - a) : 0; |
| } |
| |
| #define _m_calloc_dma(np, s, n) __m_calloc_dma(np->dev, s, n) |
| #define _m_free_dma(np, p, s, n) __m_free_dma(np->dev, p, s, n) |
| #define m_calloc_dma(s, n) _m_calloc_dma(np, s, n) |
| #define m_free_dma(p, s, n) _m_free_dma(np, p, s, n) |
| #define _vtobus(np, p) __vtobus(np->dev, p) |
| #define vtobus(p) _vtobus(np, p) |
| |
| /* |
| * Deal with DMA mapping/unmapping. |
| */ |
| |
| /* To keep track of the dma mapping (sg/single) that has been set */ |
| #define __data_mapped SCp.phase |
| #define __data_mapping SCp.have_data_in |
| |
| static void __unmap_scsi_data(struct device *dev, struct scsi_cmnd *cmd) |
| { |
| switch(cmd->__data_mapped) { |
| case 2: |
| scsi_dma_unmap(cmd); |
| break; |
| } |
| cmd->__data_mapped = 0; |
| } |
| |
| static int __map_scsi_sg_data(struct device *dev, struct scsi_cmnd *cmd) |
| { |
| int use_sg; |
| |
| use_sg = scsi_dma_map(cmd); |
| if (!use_sg) |
| return 0; |
| |
| cmd->__data_mapped = 2; |
| cmd->__data_mapping = use_sg; |
| |
| return use_sg; |
| } |
| |
| #define unmap_scsi_data(np, cmd) __unmap_scsi_data(np->dev, cmd) |
| #define map_scsi_sg_data(np, cmd) __map_scsi_sg_data(np->dev, cmd) |
| |
| /*========================================================== |
| ** |
| ** Driver setup. |
| ** |
| ** This structure is initialized from linux config |
| ** options. It can be overridden at boot-up by the boot |
| ** command line. |
| ** |
| **========================================================== |
| */ |
| static struct ncr_driver_setup |
| driver_setup = SCSI_NCR_DRIVER_SETUP; |
| |
| #ifndef MODULE |
| #ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT |
| static struct ncr_driver_setup |
| driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP; |
| #endif |
| #endif /* !MODULE */ |
| |
| #define initverbose (driver_setup.verbose) |
| #define bootverbose (np->verbose) |
| |
| |
| /*=================================================================== |
| ** |
| ** Driver setup from the boot command line |
| ** |
| **=================================================================== |
| */ |
| |
| #ifdef MODULE |
| #define ARG_SEP ' ' |
| #else |
| #define ARG_SEP ',' |
| #endif |
| |
| #define OPT_TAGS 1 |
| #define OPT_MASTER_PARITY 2 |
| #define OPT_SCSI_PARITY 3 |
| #define OPT_DISCONNECTION 4 |
| #define OPT_SPECIAL_FEATURES 5 |
| #define OPT_UNUSED_1 6 |
| #define OPT_FORCE_SYNC_NEGO 7 |
| #define OPT_REVERSE_PROBE 8 |
| #define OPT_DEFAULT_SYNC 9 |
| #define OPT_VERBOSE 10 |
| #define OPT_DEBUG 11 |
| #define OPT_BURST_MAX 12 |
| #define OPT_LED_PIN 13 |
| #define OPT_MAX_WIDE 14 |
| #define OPT_SETTLE_DELAY 15 |
| #define OPT_DIFF_SUPPORT 16 |
| #define OPT_IRQM 17 |
| #define OPT_PCI_FIX_UP 18 |
| #define OPT_BUS_CHECK 19 |
| #define OPT_OPTIMIZE 20 |
| #define OPT_RECOVERY 21 |
| #define OPT_SAFE_SETUP 22 |
| #define OPT_USE_NVRAM 23 |
| #define OPT_EXCLUDE 24 |
| #define OPT_HOST_ID 25 |
| |
| #ifdef SCSI_NCR_IARB_SUPPORT |
| #define OPT_IARB 26 |
| #endif |
| |
| #ifdef MODULE |
| #define ARG_SEP ' ' |
| #else |
| #define ARG_SEP ',' |
| #endif |
| |
| #ifndef MODULE |
| static char setup_token[] __initdata = |
| "tags:" "mpar:" |
| "spar:" "disc:" |
| "specf:" "ultra:" |
| "fsn:" "revprob:" |
| "sync:" "verb:" |
| "debug:" "burst:" |
| "led:" "wide:" |
| "settle:" "diff:" |
| "irqm:" "pcifix:" |
| "buschk:" "optim:" |
| "recovery:" |
| "safe:" "nvram:" |
| "excl:" "hostid:" |
| #ifdef SCSI_NCR_IARB_SUPPORT |
| "iarb:" |
| #endif |
| ; /* DONNOT REMOVE THIS ';' */ |
| |
| static int __init get_setup_token(char *p) |
| { |
| char *cur = setup_token; |
| char *pc; |
| int i = 0; |
| |
| while (cur != NULL && (pc = strchr(cur, ':')) != NULL) { |
| ++pc; |
| ++i; |
| if (!strncmp(p, cur, pc - cur)) |
| return i; |
| cur = pc; |
| } |
| return 0; |
| } |
| |
| static int __init sym53c8xx__setup(char *str) |
| { |
| #ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT |
| char *cur = str; |
| char *pc, *pv; |
| int i, val, c; |
| int xi = 0; |
| |
| while (cur != NULL && (pc = strchr(cur, ':')) != NULL) { |
| char *pe; |
| |
| val = 0; |
| pv = pc; |
| c = *++pv; |
| |
| if (c == 'n') |
| val = 0; |
| else if (c == 'y') |
| val = 1; |
| else |
| val = (int) simple_strtoul(pv, &pe, 0); |
| |
| switch (get_setup_token(cur)) { |
| case OPT_TAGS: |
| driver_setup.default_tags = val; |
| if (pe && *pe == '/') { |
| i = 0; |
| while (*pe && *pe != ARG_SEP && |
| i < sizeof(driver_setup.tag_ctrl)-1) { |
| driver_setup.tag_ctrl[i++] = *pe++; |
| } |
| driver_setup.tag_ctrl[i] = '\0'; |
| } |
| break; |
| case OPT_MASTER_PARITY: |
| driver_setup.master_parity = val; |
| break; |
| case OPT_SCSI_PARITY: |
| driver_setup.scsi_parity = val; |
| break; |
| case OPT_DISCONNECTION: |
| driver_setup.disconnection = val; |
| break; |
| case OPT_SPECIAL_FEATURES: |
| driver_setup.special_features = val; |
| break; |
| case OPT_FORCE_SYNC_NEGO: |
| driver_setup.force_sync_nego = val; |
| break; |
| case OPT_REVERSE_PROBE: |
| driver_setup.reverse_probe = val; |
| break; |
| case OPT_DEFAULT_SYNC: |
| driver_setup.default_sync = val; |
| break; |
| case OPT_VERBOSE: |
| driver_setup.verbose = val; |
| break; |
| case OPT_DEBUG: |
| driver_setup.debug = val; |
| break; |
| case OPT_BURST_MAX: |
| driver_setup.burst_max = val; |
| break; |
| case OPT_LED_PIN: |
| driver_setup.led_pin = val; |
| break; |
| case OPT_MAX_WIDE: |
| driver_setup.max_wide = val? 1:0; |
| break; |
| case OPT_SETTLE_DELAY: |
| driver_setup.settle_delay = val; |
| break; |
| case OPT_DIFF_SUPPORT: |
| driver_setup.diff_support = val; |
| break; |
| case OPT_IRQM: |
| driver_setup.irqm = val; |
| break; |
| case OPT_PCI_FIX_UP: |
| driver_setup.pci_fix_up = val; |
| break; |
| case OPT_BUS_CHECK: |
| driver_setup.bus_check = val; |
| break; |
| case OPT_OPTIMIZE: |
| driver_setup.optimize = val; |
| break; |
| case OPT_RECOVERY: |
| driver_setup.recovery = val; |
| break; |
| case OPT_USE_NVRAM: |
| driver_setup.use_nvram = val; |
| break; |
| case OPT_SAFE_SETUP: |
| memcpy(&driver_setup, &driver_safe_setup, |
| sizeof(driver_setup)); |
| break; |
| case OPT_EXCLUDE: |
| if (xi < SCSI_NCR_MAX_EXCLUDES) |
| driver_setup.excludes[xi++] = val; |
| break; |
| case OPT_HOST_ID: |
| driver_setup.host_id = val; |
| break; |
| #ifdef SCSI_NCR_IARB_SUPPORT |
| case OPT_IARB: |
| driver_setup.iarb = val; |
| break; |
| #endif |
| default: |
| printk("sym53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur); |
| break; |
| } |
| |
| if ((cur = strchr(cur, ARG_SEP)) != NULL) |
| ++cur; |
| } |
| #endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */ |
| return 1; |
| } |
| #endif /* !MODULE */ |
| |
| /*=================================================================== |
| ** |
| ** Get device queue depth from boot command line. |
| ** |
| **=================================================================== |
| */ |
| #define DEF_DEPTH (driver_setup.default_tags) |
| #define ALL_TARGETS -2 |
| #define NO_TARGET -1 |
| #define ALL_LUNS -2 |
| #define NO_LUN -1 |
| |
| static int device_queue_depth(int unit, int target, int lun) |
| { |
| int c, h, t, u, v; |
| char *p = driver_setup.tag_ctrl; |
| char *ep; |
| |
| h = -1; |
| t = NO_TARGET; |
| u = NO_LUN; |
| while ((c = *p++) != 0) { |
| v = simple_strtoul(p, &ep, 0); |
| switch(c) { |
| case '/': |
| ++h; |
| t = ALL_TARGETS; |
| u = ALL_LUNS; |
| break; |
| case 't': |
| if (t != target) |
| t = (target == v) ? v : NO_TARGET; |
| u = ALL_LUNS; |
| break; |
| case 'u': |
| if (u != lun) |
| u = (lun == v) ? v : NO_LUN; |
| break; |
| case 'q': |
| if (h == unit && |
| (t == ALL_TARGETS || t == target) && |
| (u == ALL_LUNS || u == lun)) |
| return v; |
| break; |
| case '-': |
| t = ALL_TARGETS; |
| u = ALL_LUNS; |
| break; |
| default: |
| break; |
| } |
| p = ep; |
| } |
| return DEF_DEPTH; |
| } |
| |
| |
| /*========================================================== |
| ** |
| ** The CCB done queue uses an array of CCB virtual |
| ** addresses. Empty entries are flagged using the bogus |
| ** virtual address 0xffffffff. |
| ** |
| ** Since PCI ensures that only aligned DWORDs are accessed |
| ** atomically, 64 bit little-endian architecture requires |
| ** to test the high order DWORD of the entry to determine |
| ** if it is empty or valid. |
| ** |
| ** BTW, I will make things differently as soon as I will |
| ** have a better idea, but this is simple and should work. |
| ** |
| **========================================================== |
| */ |
| |
| #define SCSI_NCR_CCB_DONE_SUPPORT |
| #ifdef SCSI_NCR_CCB_DONE_SUPPORT |
| |
| #define MAX_DONE 24 |
| #define CCB_DONE_EMPTY 0xffffffffUL |
| |
| /* All 32 bit architectures */ |
| #if BITS_PER_LONG == 32 |
| #define CCB_DONE_VALID(cp) (((u_long) cp) != CCB_DONE_EMPTY) |
| |
| /* All > 32 bit (64 bit) architectures regardless endian-ness */ |
| #else |
| #define CCB_DONE_VALID(cp) \ |
| ((((u_long) cp) & 0xffffffff00000000ul) && \ |
| (((u_long) cp) & 0xfffffffful) != CCB_DONE_EMPTY) |
| #endif |
| |
| #endif /* SCSI_NCR_CCB_DONE_SUPPORT */ |
| |
| /*========================================================== |
| ** |
| ** Configuration and Debugging |
| ** |
| **========================================================== |
| */ |
| |
| /* |
| ** SCSI address of this device. |
| ** The boot routines should have set it. |
| ** If not, use this. |
| */ |
| |
| #ifndef SCSI_NCR_MYADDR |
| #define SCSI_NCR_MYADDR (7) |
| #endif |
| |
| /* |
| ** The maximum number of tags per logic unit. |
| ** Used only for disk devices that support tags. |
| */ |
| |
| #ifndef SCSI_NCR_MAX_TAGS |
| #define SCSI_NCR_MAX_TAGS (8) |
| #endif |
| |
| /* |
| ** TAGS are actually limited to 64 tags/lun. |
| ** We need to deal with power of 2, for alignment constraints. |
| */ |
| #if SCSI_NCR_MAX_TAGS > 64 |
| #define MAX_TAGS (64) |
| #else |
| #define MAX_TAGS SCSI_NCR_MAX_TAGS |
| #endif |
| |
| #define NO_TAG (255) |
| |
| /* |
| ** Choose appropriate type for tag bitmap. |
| */ |
| #if MAX_TAGS > 32 |
| typedef u64 tagmap_t; |
| #else |
| typedef u32 tagmap_t; |
| #endif |
| |
| /* |
| ** Number of targets supported by the driver. |
| ** n permits target numbers 0..n-1. |
| ** Default is 16, meaning targets #0..#15. |
| ** #7 .. is myself. |
| */ |
| |
| #ifdef SCSI_NCR_MAX_TARGET |
| #define MAX_TARGET (SCSI_NCR_MAX_TARGET) |
| #else |
| #define MAX_TARGET (16) |
| #endif |
| |
| /* |
| ** Number of logic units supported by the driver. |
| ** n enables logic unit numbers 0..n-1. |
| ** The common SCSI devices require only |
| ** one lun, so take 1 as the default. |
| */ |
| |
| #ifdef SCSI_NCR_MAX_LUN |
| #define MAX_LUN SCSI_NCR_MAX_LUN |
| #else |
| #define MAX_LUN (1) |
| #endif |
| |
| /* |
| ** Asynchronous pre-scaler (ns). Shall be 40 |
| */ |
| |
| #ifndef SCSI_NCR_MIN_ASYNC |
| #define SCSI_NCR_MIN_ASYNC (40) |
| #endif |
| |
| /* |
| ** The maximum number of jobs scheduled for starting. |
| ** There should be one slot per target, and one slot |
| ** for each tag of each target in use. |
| ** The calculation below is actually quite silly ... |
| */ |
| |
| #ifdef SCSI_NCR_CAN_QUEUE |
| #define MAX_START (SCSI_NCR_CAN_QUEUE + 4) |
| #else |
| #define MAX_START (MAX_TARGET + 7 * MAX_TAGS) |
| #endif |
| |
| /* |
| ** We limit the max number of pending IO to 250. |
| ** since we donnot want to allocate more than 1 |
| ** PAGE for 'scripth'. |
| */ |
| #if MAX_START > 250 |
| #undef MAX_START |
| #define MAX_START 250 |
| #endif |
| |
| /* |
| ** The maximum number of segments a transfer is split into. |
| ** We support up to 127 segments for both read and write. |
| ** The data scripts are broken into 2 sub-scripts. |
| ** 80 (MAX_SCATTERL) segments are moved from a sub-script |
| ** in on-chip RAM. This makes data transfers shorter than |
| ** 80k (assuming 1k fs) as fast as possible. |
| */ |
| |
| #define MAX_SCATTER (SCSI_NCR_MAX_SCATTER) |
| |
| #if (MAX_SCATTER > 80) |
| #define MAX_SCATTERL 80 |
| #define MAX_SCATTERH (MAX_SCATTER - MAX_SCATTERL) |
| #else |
| #define MAX_SCATTERL (MAX_SCATTER-1) |
| #define MAX_SCATTERH 1 |
| #endif |
| |
| /* |
| ** other |
| */ |
| |
| #define NCR_SNOOP_TIMEOUT (1000000) |
| |
| /* |
| ** Other definitions |
| */ |
| |
| #define ScsiResult(host_code, scsi_code) (((host_code) << 16) + ((scsi_code) & 0x7f)) |
| |
| #define initverbose (driver_setup.verbose) |
| #define bootverbose (np->verbose) |
| |
| /*========================================================== |
| ** |
| ** Command control block states. |
| ** |
| **========================================================== |
| */ |
| |
| #define HS_IDLE (0) |
| #define HS_BUSY (1) |
| #define HS_NEGOTIATE (2) /* sync/wide data transfer*/ |
| #define HS_DISCONNECT (3) /* Disconnected by target */ |
| |
| #define HS_DONEMASK (0x80) |
| #define HS_COMPLETE (4|HS_DONEMASK) |
| #define HS_SEL_TIMEOUT (5|HS_DONEMASK) /* Selection timeout */ |
| #define HS_RESET (6|HS_DONEMASK) /* SCSI reset */ |
| #define HS_ABORTED (7|HS_DONEMASK) /* Transfer aborted */ |
| #define HS_TIMEOUT (8|HS_DONEMASK) /* Software timeout */ |
| #define HS_FAIL (9|HS_DONEMASK) /* SCSI or PCI bus errors */ |
| #define HS_UNEXPECTED (10|HS_DONEMASK)/* Unexpected disconnect */ |
| |
| /* |
| ** Invalid host status values used by the SCRIPTS processor |
| ** when the nexus is not fully identified. |
| ** Shall never appear in a CCB. |
| */ |
| |
| #define HS_INVALMASK (0x40) |
| #define HS_SELECTING (0|HS_INVALMASK) |
| #define HS_IN_RESELECT (1|HS_INVALMASK) |
| #define HS_STARTING (2|HS_INVALMASK) |
| |
| /* |
| ** Flags set by the SCRIPT processor for commands |
| ** that have been skipped. |
| */ |
| #define HS_SKIPMASK (0x20) |
| |
| /*========================================================== |
| ** |
| ** Software Interrupt Codes |
| ** |
| **========================================================== |
| */ |
| |
| #define SIR_BAD_STATUS (1) |
| #define SIR_XXXXXXXXXX (2) |
| #define SIR_NEGO_SYNC (3) |
| #define SIR_NEGO_WIDE (4) |
| #define SIR_NEGO_FAILED (5) |
| #define SIR_NEGO_PROTO (6) |
| #define SIR_REJECT_RECEIVED (7) |
| #define SIR_REJECT_SENT (8) |
| #define SIR_IGN_RESIDUE (9) |
| #define SIR_MISSING_SAVE (10) |
| #define SIR_RESEL_NO_MSG_IN (11) |
| #define SIR_RESEL_NO_IDENTIFY (12) |
| #define SIR_RESEL_BAD_LUN (13) |
| #define SIR_RESEL_BAD_TARGET (14) |
| #define SIR_RESEL_BAD_I_T_L (15) |
| #define SIR_RESEL_BAD_I_T_L_Q (16) |
| #define SIR_DONE_OVERFLOW (17) |
| #define SIR_INTFLY (18) |
| #define SIR_MAX (18) |
| |
| /*========================================================== |
| ** |
| ** Extended error codes. |
| ** xerr_status field of struct ccb. |
| ** |
| **========================================================== |
| */ |
| |
| #define XE_OK (0) |
| #define XE_EXTRA_DATA (1) /* unexpected data phase */ |
| #define XE_BAD_PHASE (2) /* illegal phase (4/5) */ |
| |
| /*========================================================== |
| ** |
| ** Negotiation status. |
| ** nego_status field of struct ccb. |
| ** |
| **========================================================== |
| */ |
| |
| #define NS_NOCHANGE (0) |
| #define NS_SYNC (1) |
| #define NS_WIDE (2) |
| #define NS_PPR (4) |
| |
| /*========================================================== |
| ** |
| ** Misc. |
| ** |
| **========================================================== |
| */ |
| |
| #define CCB_MAGIC (0xf2691ad2) |
| |
| /*========================================================== |
| ** |
| ** Declaration of structs. |
| ** |
| **========================================================== |
| */ |
| |
| static struct scsi_transport_template *ncr53c8xx_transport_template = NULL; |
| |
| struct tcb; |
| struct lcb; |
| struct ccb; |
| struct ncb; |
| struct script; |
| |
| struct link { |
| ncrcmd l_cmd; |
| ncrcmd l_paddr; |
| }; |
| |
| struct usrcmd { |
| u_long target; |
| u_long lun; |
| u_long data; |
| u_long cmd; |
| }; |
| |
| #define UC_SETSYNC 10 |
| #define UC_SETTAGS 11 |
| #define UC_SETDEBUG 12 |
| #define UC_SETORDER 13 |
| #define UC_SETWIDE 14 |
| #define UC_SETFLAG 15 |
| #define UC_SETVERBOSE 17 |
| |
| #define UF_TRACE (0x01) |
| #define UF_NODISC (0x02) |
| #define UF_NOSCAN (0x04) |
| |
| /*======================================================================== |
| ** |
| ** Declaration of structs: target control block |
| ** |
| **======================================================================== |
| */ |
| struct tcb { |
| /*---------------------------------------------------------------- |
| ** During reselection the ncr jumps to this point with SFBR |
| ** set to the encoded target number with bit 7 set. |
| ** if it's not this target, jump to the next. |
| ** |
| ** JUMP IF (SFBR != #target#), @(next tcb) |
| **---------------------------------------------------------------- |
| */ |
| struct link jump_tcb; |
| |
| /*---------------------------------------------------------------- |
| ** Load the actual values for the sxfer and the scntl3 |
| ** register (sync/wide mode). |
| ** |
| ** SCR_COPY (1), @(sval field of this tcb), @(sxfer register) |
| ** SCR_COPY (1), @(wval field of this tcb), @(scntl3 register) |
| **---------------------------------------------------------------- |
| */ |
| ncrcmd getscr[6]; |
| |
| /*---------------------------------------------------------------- |
| ** Get the IDENTIFY message and load the LUN to SFBR. |
| ** |
| ** CALL, <RESEL_LUN> |
| **---------------------------------------------------------------- |
| */ |
| struct link call_lun; |
| |
| /*---------------------------------------------------------------- |
| ** Now look for the right lun. |
| ** |
| ** For i = 0 to 3 |
| ** SCR_JUMP ^ IFTRUE(MASK(i, 3)), @(first lcb mod. i) |
| ** |
| ** Recent chips will prefetch the 4 JUMPS using only 1 burst. |
| ** It is kind of hashcoding. |
| **---------------------------------------------------------------- |
| */ |
| struct link jump_lcb[4]; /* JUMPs for reselection */ |
| struct lcb * lp[MAX_LUN]; /* The lcb's of this tcb */ |
| |
| /*---------------------------------------------------------------- |
| ** Pointer to the ccb used for negotiation. |
| ** Prevent from starting a negotiation for all queued commands |
| ** when tagged command queuing is enabled. |
| **---------------------------------------------------------------- |
| */ |
| struct ccb * nego_cp; |
| |
| /*---------------------------------------------------------------- |
| ** statistical data |
| **---------------------------------------------------------------- |
| */ |
| u_long transfers; |
| u_long bytes; |
| |
| /*---------------------------------------------------------------- |
| ** negotiation of wide and synch transfer and device quirks. |
| **---------------------------------------------------------------- |
| */ |
| #ifdef SCSI_NCR_BIG_ENDIAN |
| /*0*/ u16 period; |
| /*2*/ u_char sval; |
| /*3*/ u_char minsync; |
| /*0*/ u_char wval; |
| /*1*/ u_char widedone; |
| /*2*/ u_char quirks; |
| /*3*/ u_char maxoffs; |
| #else |
| /*0*/ u_char minsync; |
| /*1*/ u_char sval; |
| /*2*/ u16 period; |
| /*0*/ u_char maxoffs; |
| /*1*/ u_char quirks; |
| /*2*/ u_char widedone; |
| /*3*/ u_char wval; |
| #endif |
| |
| /* User settable limits and options. */ |
| u_char usrsync; |
| u_char usrwide; |
| u_char usrtags; |
| u_char usrflag; |
| struct scsi_target *starget; |
| }; |
| |
| /*======================================================================== |
| ** |
| ** Declaration of structs: lun control block |
| ** |
| **======================================================================== |
| */ |
| struct lcb { |
| /*---------------------------------------------------------------- |
| ** During reselection the ncr jumps to this point |
| ** with SFBR set to the "Identify" message. |
| ** if it's not this lun, jump to the next. |
| ** |
| ** JUMP IF (SFBR != #lun#), @(next lcb of this target) |
| ** |
| ** It is this lun. Load TEMP with the nexus jumps table |
| ** address and jump to RESEL_TAG (or RESEL_NOTAG). |
| ** |
| ** SCR_COPY (4), p_jump_ccb, TEMP, |
| ** SCR_JUMP, <RESEL_TAG> |
| **---------------------------------------------------------------- |
| */ |
| struct link jump_lcb; |
| ncrcmd load_jump_ccb[3]; |
| struct link jump_tag; |
| ncrcmd p_jump_ccb; /* Jump table bus address */ |
| |
| /*---------------------------------------------------------------- |
| ** Jump table used by the script processor to directly jump |
| ** to the CCB corresponding to the reselected nexus. |
| ** Address is allocated on 256 bytes boundary in order to |
| ** allow 8 bit calculation of the tag jump entry for up to |
| ** 64 possible tags. |
| **---------------------------------------------------------------- |
| */ |
| u32 jump_ccb_0; /* Default table if no tags */ |
| u32 *jump_ccb; /* Virtual address */ |
| |
| /*---------------------------------------------------------------- |
| ** CCB queue management. |
| **---------------------------------------------------------------- |
| */ |
| struct list_head free_ccbq; /* Queue of available CCBs */ |
| struct list_head busy_ccbq; /* Queue of busy CCBs */ |
| struct list_head wait_ccbq; /* Queue of waiting for IO CCBs */ |
| struct list_head skip_ccbq; /* Queue of skipped CCBs */ |
| u_char actccbs; /* Number of allocated CCBs */ |
| u_char busyccbs; /* CCBs busy for this lun */ |
| u_char queuedccbs; /* CCBs queued to the controller*/ |
| u_char queuedepth; /* Queue depth for this lun */ |
| u_char scdev_depth; /* SCSI device queue depth */ |
| u_char maxnxs; /* Max possible nexuses */ |
| |
| /*---------------------------------------------------------------- |
| ** Control of tagged command queuing. |
| ** Tags allocation is performed using a circular buffer. |
| ** This avoids using a loop for tag allocation. |
| **---------------------------------------------------------------- |
| */ |
| u_char ia_tag; /* Allocation index */ |
| u_char if_tag; /* Freeing index */ |
| u_char cb_tags[MAX_TAGS]; /* Circular tags buffer */ |
| u_char usetags; /* Command queuing is active */ |
| u_char maxtags; /* Max nr of tags asked by user */ |
| u_char numtags; /* Current number of tags */ |
| |
| /*---------------------------------------------------------------- |
| ** QUEUE FULL control and ORDERED tag control. |
| **---------------------------------------------------------------- |
| */ |
| /*---------------------------------------------------------------- |
| ** QUEUE FULL and ORDERED tag control. |
| **---------------------------------------------------------------- |
| */ |
| u16 num_good; /* Nr of GOOD since QUEUE FULL */ |
| tagmap_t tags_umap; /* Used tags bitmap */ |
| tagmap_t tags_smap; /* Tags in use at 'tag_stime' */ |
| u_long tags_stime; /* Last time we set smap=umap */ |
| struct ccb * held_ccb; /* CCB held for QUEUE FULL */ |
| }; |
| |
| /*======================================================================== |
| ** |
| ** Declaration of structs: the launch script. |
| ** |
| **======================================================================== |
| ** |
| ** It is part of the CCB and is called by the scripts processor to |
| ** start or restart the data structure (nexus). |
| ** This 6 DWORDs mini script makes use of prefetching. |
| ** |
| **------------------------------------------------------------------------ |
| */ |
| struct launch { |
| /*---------------------------------------------------------------- |
| ** SCR_COPY(4), @(p_phys), @(dsa register) |
| ** SCR_JUMP, @(scheduler_point) |
| **---------------------------------------------------------------- |
| */ |
| ncrcmd setup_dsa[3]; /* Copy 'phys' address to dsa */ |
| struct link schedule; /* Jump to scheduler point */ |
| ncrcmd p_phys; /* 'phys' header bus address */ |
| }; |
| |
| /*======================================================================== |
| ** |
| ** Declaration of structs: global HEADER. |
| ** |
| **======================================================================== |
| ** |
| ** This substructure is copied from the ccb to a global address after |
| ** selection (or reselection) and copied back before disconnect. |
| ** |
| ** These fields are accessible to the script processor. |
| ** |
| **------------------------------------------------------------------------ |
| */ |
| |
| struct head { |
| /*---------------------------------------------------------------- |
| ** Saved data pointer. |
| ** Points to the position in the script responsible for the |
| ** actual transfer transfer of data. |
| ** It's written after reception of a SAVE_DATA_POINTER message. |
| ** The goalpointer points after the last transfer command. |
| **---------------------------------------------------------------- |
| */ |
| u32 savep; |
| u32 lastp; |
| u32 goalp; |
| |
| /*---------------------------------------------------------------- |
| ** Alternate data pointer. |
| ** They are copied back to savep/lastp/goalp by the SCRIPTS |
| ** when the direction is unknown and the device claims data out. |
| **---------------------------------------------------------------- |
| */ |
| u32 wlastp; |
| u32 wgoalp; |
| |
| /*---------------------------------------------------------------- |
| ** The virtual address of the ccb containing this header. |
| **---------------------------------------------------------------- |
| */ |
| struct ccb * cp; |
| |
| /*---------------------------------------------------------------- |
| ** Status fields. |
| **---------------------------------------------------------------- |
| */ |
| u_char scr_st[4]; /* script status */ |
| u_char status[4]; /* host status. must be the */ |
| /* last DWORD of the header. */ |
| }; |
| |
| /* |
| ** The status bytes are used by the host and the script processor. |
| ** |
| ** The byte corresponding to the host_status must be stored in the |
| ** last DWORD of the CCB header since it is used for command |
| ** completion (ncr_wakeup()). Doing so, we are sure that the header |
| ** has been entirely copied back to the CCB when the host_status is |
| ** seen complete by the CPU. |
| ** |
| ** The last four bytes (status[4]) are copied to the scratchb register |
| ** (declared as scr0..scr3 in ncr_reg.h) just after the select/reselect, |
| ** and copied back just after disconnecting. |
| ** Inside the script the XX_REG are used. |
| ** |
| ** The first four bytes (scr_st[4]) are used inside the script by |
| ** "COPY" commands. |
| ** Because source and destination must have the same alignment |
| ** in a DWORD, the fields HAVE to be at the chosen offsets. |
| ** xerr_st 0 (0x34) scratcha |
| ** sync_st 1 (0x05) sxfer |
| ** wide_st 3 (0x03) scntl3 |
| */ |
| |
| /* |
| ** Last four bytes (script) |
| */ |
| #define QU_REG scr0 |
| #define HS_REG scr1 |
| #define HS_PRT nc_scr1 |
| #define SS_REG scr2 |
| #define SS_PRT nc_scr2 |
| #define PS_REG scr3 |
| |
| /* |
| ** Last four bytes (host) |
| */ |
| #ifdef SCSI_NCR_BIG_ENDIAN |
| #define actualquirks phys.header.status[3] |
| #define host_status phys.header.status[2] |
| #define scsi_status phys.header.status[1] |
| #define parity_status phys.header.status[0] |
| #else |
| #define actualquirks phys.header.status[0] |
| #define host_status phys.header.status[1] |
| #define scsi_status phys.header.status[2] |
| #define parity_status phys.header.status[3] |
| #endif |
| |
| /* |
| ** First four bytes (script) |
| */ |
| #define xerr_st header.scr_st[0] |
| #define sync_st header.scr_st[1] |
| #define nego_st header.scr_st[2] |
| #define wide_st header.scr_st[3] |
| |
| /* |
| ** First four bytes (host) |
| */ |
| #define xerr_status phys.xerr_st |
| #define nego_status phys.nego_st |
| |
| #if 0 |
| #define sync_status phys.sync_st |
| #define wide_status phys.wide_st |
| #endif |
| |
| /*========================================================== |
| ** |
| ** Declaration of structs: Data structure block |
| ** |
| **========================================================== |
| ** |
| ** During execution of a ccb by the script processor, |
| ** the DSA (data structure address) register points |
| ** to this substructure of the ccb. |
| ** This substructure contains the header with |
| ** the script-processor-changeable data and |
| ** data blocks for the indirect move commands. |
| ** |
| **---------------------------------------------------------- |
| */ |
| |
| struct dsb { |
| |
| /* |
| ** Header. |
| */ |
| |
| struct head header; |
| |
| /* |
| ** Table data for Script |
| */ |
| |
| struct scr_tblsel select; |
| struct scr_tblmove smsg ; |
| struct scr_tblmove cmd ; |
| struct scr_tblmove sense ; |
| struct scr_tblmove data[MAX_SCATTER]; |
| }; |
| |
| |
| /*======================================================================== |
| ** |
| ** Declaration of structs: Command control block. |
| ** |
| **======================================================================== |
| */ |
| struct ccb { |
| /*---------------------------------------------------------------- |
| ** This is the data structure which is pointed by the DSA |
| ** register when it is executed by the script processor. |
| ** It must be the first entry because it contains the header |
| ** as first entry that must be cache line aligned. |
| **---------------------------------------------------------------- |
| */ |
| struct dsb phys; |
| |
| /*---------------------------------------------------------------- |
| ** Mini-script used at CCB execution start-up. |
| ** Load the DSA with the data structure address (phys) and |
| ** jump to SELECT. Jump to CANCEL if CCB is to be canceled. |
| **---------------------------------------------------------------- |
| */ |
| struct launch start; |
| |
| /*---------------------------------------------------------------- |
| ** Mini-script used at CCB relection to restart the nexus. |
| ** Load the DSA with the data structure address (phys) and |
| ** jump to RESEL_DSA. Jump to ABORT if CCB is to be aborted. |
| **---------------------------------------------------------------- |
| */ |
| struct launch restart; |
| |
| /*---------------------------------------------------------------- |
| ** If a data transfer phase is terminated too early |
| ** (after reception of a message (i.e. DISCONNECT)), |
| ** we have to prepare a mini script to transfer |
| ** the rest of the data. |
| **---------------------------------------------------------------- |
| */ |
| ncrcmd patch[8]; |
| |
| /*---------------------------------------------------------------- |
| ** The general SCSI driver provides a |
| ** pointer to a control block. |
| **---------------------------------------------------------------- |
| */ |
| struct scsi_cmnd *cmd; /* SCSI command */ |
| u_char cdb_buf[16]; /* Copy of CDB */ |
| u_char sense_buf[64]; |
| int data_len; /* Total data length */ |
| |
| /*---------------------------------------------------------------- |
| ** Message areas. |
| ** We prepare a message to be sent after selection. |
| ** We may use a second one if the command is rescheduled |
| ** due to GETCC or QFULL. |
| ** Contents are IDENTIFY and SIMPLE_TAG. |
| ** While negotiating sync or wide transfer, |
| ** a SDTR or WDTR message is appended. |
| **---------------------------------------------------------------- |
| */ |
| u_char scsi_smsg [8]; |
| u_char scsi_smsg2[8]; |
| |
| /*---------------------------------------------------------------- |
| ** Other fields. |
| **---------------------------------------------------------------- |
| */ |
| u_long p_ccb; /* BUS address of this CCB */ |
| u_char sensecmd[6]; /* Sense command */ |
| u_char tag; /* Tag for this transfer */ |
| /* 255 means no tag */ |
| u_char target; |
| u_char lun; |
| u_char queued; |
| u_char auto_sense; |
| struct ccb * link_ccb; /* Host adapter CCB chain */ |
| struct list_head link_ccbq; /* Link to unit CCB queue */ |
| u32 startp; /* Initial data pointer */ |
| u_long magic; /* Free / busy CCB flag */ |
| }; |
| |
| #define CCB_PHYS(cp,lbl) (cp->p_ccb + offsetof(struct ccb, lbl)) |
| |
| |
| /*======================================================================== |
| ** |
| ** Declaration of structs: NCR device descriptor |
| ** |
| **======================================================================== |
| */ |
| struct ncb { |
| /*---------------------------------------------------------------- |
| ** The global header. |
| ** It is accessible to both the host and the script processor. |
| ** Must be cache line size aligned (32 for x86) in order to |
| ** allow cache line bursting when it is copied to/from CCB. |
| **---------------------------------------------------------------- |
| */ |
| struct head header; |
| |
| /*---------------------------------------------------------------- |
| ** CCBs management queues. |
| **---------------------------------------------------------------- |
| */ |
| struct scsi_cmnd *waiting_list; /* Commands waiting for a CCB */ |
| /* when lcb is not allocated. */ |
| struct scsi_cmnd *done_list; /* Commands waiting for done() */ |
| /* callback to be invoked. */ |
| spinlock_t smp_lock; /* Lock for SMP threading */ |
| |
| /*---------------------------------------------------------------- |
| ** Chip and controller identification. |
| **---------------------------------------------------------------- |
| */ |
| int unit; /* Unit number */ |
| char inst_name[16]; /* ncb instance name */ |
| |
| /*---------------------------------------------------------------- |
| ** Initial value of some IO register bits. |
| ** These values are assumed to have been set by BIOS, and may |
| ** be used for probing adapter implementation differences. |
| **---------------------------------------------------------------- |
| */ |
| u_char sv_scntl0, sv_scntl3, sv_dmode, sv_dcntl, sv_ctest0, sv_ctest3, |
| sv_ctest4, sv_ctest5, sv_gpcntl, sv_stest2, sv_stest4; |
| |
| /*---------------------------------------------------------------- |
| ** Actual initial value of IO register bits used by the |
| ** driver. They are loaded at initialisation according to |
| ** features that are to be enabled. |
| **---------------------------------------------------------------- |
| */ |
| u_char rv_scntl0, rv_scntl3, rv_dmode, rv_dcntl, rv_ctest0, rv_ctest3, |
| rv_ctest4, rv_ctest5, rv_stest2; |
| |
| /*---------------------------------------------------------------- |
| ** Targets management. |
| ** During reselection the ncr jumps to jump_tcb. |
| ** The SFBR register is loaded with the encoded target id. |
| ** For i = 0 to 3 |
| ** SCR_JUMP ^ IFTRUE(MASK(i, 3)), @(next tcb mod. i) |
| ** |
| ** Recent chips will prefetch the 4 JUMPS using only 1 burst. |
| ** It is kind of hashcoding. |
| **---------------------------------------------------------------- |
| */ |
| struct link jump_tcb[4]; /* JUMPs for reselection */ |
| struct tcb target[MAX_TARGET]; /* Target data */ |
| |
| /*---------------------------------------------------------------- |
| ** Virtual and physical bus addresses of the chip. |
| **---------------------------------------------------------------- |
| */ |
| void __iomem *vaddr; /* Virtual and bus address of */ |
| unsigned long paddr; /* chip's IO registers. */ |
| unsigned long paddr2; /* On-chip RAM bus address. */ |
| volatile /* Pointer to volatile for */ |
| struct ncr_reg __iomem *reg; /* memory mapped IO. */ |
| |
| /*---------------------------------------------------------------- |
| ** SCRIPTS virtual and physical bus addresses. |
| ** 'script' is loaded in the on-chip RAM if present. |
| ** 'scripth' stays in main memory. |
| **---------------------------------------------------------------- |
| */ |
| struct script *script0; /* Copies of script and scripth */ |
| struct scripth *scripth0; /* relocated for this ncb. */ |
| struct scripth *scripth; /* Actual scripth virt. address */ |
| u_long p_script; /* Actual script and scripth */ |
| u_long p_scripth; /* bus addresses. */ |
| |
| /*---------------------------------------------------------------- |
| ** General controller parameters and configuration. |
| **---------------------------------------------------------------- |
| */ |
| struct device *dev; |
| u_char revision_id; /* PCI device revision id */ |
| u32 irq; /* IRQ level */ |
| u32 features; /* Chip features map */ |
| u_char myaddr; /* SCSI id of the adapter */ |
| u_char maxburst; /* log base 2 of dwords burst */ |
| u_char maxwide; /* Maximum transfer width */ |
| u_char minsync; /* Minimum sync period factor */ |
| u_char maxsync; /* Maximum sync period factor */ |
| u_char maxoffs; /* Max scsi offset */ |
| u_char multiplier; /* Clock multiplier (1,2,4) */ |
| u_char clock_divn; /* Number of clock divisors */ |
| u_long clock_khz; /* SCSI clock frequency in KHz */ |
| |
| /*---------------------------------------------------------------- |
| ** Start queue management. |
| ** It is filled up by the host processor and accessed by the |
| ** SCRIPTS processor in order to start SCSI commands. |
| **---------------------------------------------------------------- |
| */ |
| u16 squeueput; /* Next free slot of the queue */ |
| u16 actccbs; /* Number of allocated CCBs */ |
| u16 queuedccbs; /* Number of CCBs in start queue*/ |
| u16 queuedepth; /* Start queue depth */ |
| |
| /*---------------------------------------------------------------- |
| ** Timeout handler. |
| **---------------------------------------------------------------- |
| */ |
| struct timer_list timer; /* Timer handler link header */ |
| u_long lasttime; |
| u_long settle_time; /* Resetting the SCSI BUS */ |
| |
| /*---------------------------------------------------------------- |
| ** Debugging and profiling. |
| **---------------------------------------------------------------- |
| */ |
| struct ncr_reg regdump; /* Register dump */ |
| u_long regtime; /* Time it has been done */ |
| |
| /*---------------------------------------------------------------- |
| ** Miscellaneous buffers accessed by the scripts-processor. |
| ** They shall be DWORD aligned, because they may be read or |
| ** written with a SCR_COPY script command. |
| **---------------------------------------------------------------- |
| */ |
| u_char msgout[8]; /* Buffer for MESSAGE OUT */ |
| u_char msgin [8]; /* Buffer for MESSAGE IN */ |
| u32 lastmsg; /* Last SCSI message sent */ |
| u_char scratch; /* Scratch for SCSI receive */ |
| |
| /*---------------------------------------------------------------- |
| ** Miscellaneous configuration and status parameters. |
| **---------------------------------------------------------------- |
| */ |
| u_char disc; /* Diconnection allowed */ |
| u_char scsi_mode; /* Current SCSI BUS mode */ |
| u_char order; /* Tag order to use */ |
| u_char verbose; /* Verbosity for this controller*/ |
| int ncr_cache; /* Used for cache test at init. */ |
| u_long p_ncb; /* BUS address of this NCB */ |
| |
| /*---------------------------------------------------------------- |
| ** Command completion handling. |
| **---------------------------------------------------------------- |
| */ |
| #ifdef SCSI_NCR_CCB_DONE_SUPPORT |
| struct ccb *(ccb_done[MAX_DONE]); |
| int ccb_done_ic; |
| #endif |
| /*---------------------------------------------------------------- |
| ** Fields that should be removed or changed. |
| **---------------------------------------------------------------- |
| */ |
| struct ccb *ccb; /* Global CCB */ |
| struct usrcmd user; /* Command from user */ |
| volatile u_char release_stage; /* Synchronisation stage on release */ |
| }; |
| |
| #define NCB_SCRIPT_PHYS(np,lbl) (np->p_script + offsetof (struct script, lbl)) |
| #define NCB_SCRIPTH_PHYS(np,lbl) (np->p_scripth + offsetof (struct scripth,lbl)) |
| |
| /*========================================================== |
| ** |
| ** |
| ** Script for NCR-Processor. |
| ** |
| ** Use ncr_script_fill() to create the variable parts. |
| ** Use ncr_script_copy_and_bind() to make a copy and |
| ** bind to physical addresses. |
| ** |
| ** |
| **========================================================== |
| ** |
| ** We have to know the offsets of all labels before |
| ** we reach them (for forward jumps). |
| ** Therefore we declare a struct here. |
| ** If you make changes inside the script, |
| ** DONT FORGET TO CHANGE THE LENGTHS HERE! |
| ** |
| **---------------------------------------------------------- |
| */ |
| |
| /* |
| ** For HP Zalon/53c720 systems, the Zalon interface |
| ** between CPU and 53c720 does prefetches, which causes |
| ** problems with self modifying scripts. The problem |
| ** is overcome by calling a dummy subroutine after each |
| ** modification, to force a refetch of the script on |
| ** return from the subroutine. |
| */ |
| |
| #ifdef CONFIG_NCR53C8XX_PREFETCH |
| #define PREFETCH_FLUSH_CNT 2 |
| #define PREFETCH_FLUSH SCR_CALL, PADDRH (wait_dma), |
| #else |
| #define PREFETCH_FLUSH_CNT 0 |
| #define PREFETCH_FLUSH |
| #endif |
| |
| /* |
| ** Script fragments which are loaded into the on-chip RAM |
| ** of 825A, 875 and 895 chips. |
| */ |
| struct script { |
| ncrcmd start [ 5]; |
| ncrcmd startpos [ 1]; |
| ncrcmd select [ 6]; |
| ncrcmd select2 [ 9 + PREFETCH_FLUSH_CNT]; |
| ncrcmd loadpos [ 4]; |
| ncrcmd send_ident [ 9]; |
| ncrcmd prepare [ 6]; |
| ncrcmd prepare2 [ 7]; |
| ncrcmd command [ 6]; |
| ncrcmd dispatch [ 32]; |
| ncrcmd clrack [ 4]; |
| ncrcmd no_data [ 17]; |
| ncrcmd status [ 8]; |
| ncrcmd msg_in [ 2]; |
| ncrcmd msg_in2 [ 16]; |
| ncrcmd msg_bad [ 4]; |
| ncrcmd setmsg [ 7]; |
| ncrcmd cleanup [ 6]; |
| ncrcmd complete [ 9]; |
| ncrcmd cleanup_ok [ 8 + PREFETCH_FLUSH_CNT]; |
| ncrcmd cleanup0 [ 1]; |
| #ifndef SCSI_NCR_CCB_DONE_SUPPORT |
| ncrcmd signal [ 12]; |
| #else |
| ncrcmd signal [ 9]; |
| ncrcmd done_pos [ 1]; |
| ncrcmd done_plug [ 2]; |
| ncrcmd done_end [ 7]; |
| #endif |
| ncrcmd save_dp [ 7]; |
| ncrcmd restore_dp [ 5]; |
| ncrcmd disconnect [ 10]; |
| ncrcmd msg_out [ 9]; |
| ncrcmd msg_out_done [ 7]; |
| ncrcmd idle [ 2]; |
| ncrcmd reselect [ 8]; |
| ncrcmd reselected [ 8]; |
| ncrcmd resel_dsa [ 6 + PREFETCH_FLUSH_CNT]; |
| ncrcmd loadpos1 [ 4]; |
| ncrcmd resel_lun [ 6]; |
| ncrcmd resel_tag [ 6]; |
| ncrcmd jump_to_nexus [ 4 + PREFETCH_FLUSH_CNT]; |
| ncrcmd nexus_indirect [ 4]; |
| ncrcmd resel_notag [ 4]; |
| ncrcmd data_in [MAX_SCATTERL * 4]; |
| ncrcmd data_in2 [ 4]; |
| ncrcmd data_out [MAX_SCATTERL * 4]; |
| ncrcmd data_out2 [ 4]; |
| }; |
| |
| /* |
| ** Script fragments which stay in main memory for all chips. |
| */ |
| struct scripth { |
| ncrcmd tryloop [MAX_START*2]; |
| ncrcmd tryloop2 [ 2]; |
| #ifdef SCSI_NCR_CCB_DONE_SUPPORT |
| ncrcmd done_queue [MAX_DONE*5]; |
| ncrcmd done_queue2 [ 2]; |
| #endif |
| ncrcmd select_no_atn [ 8]; |
| ncrcmd cancel [ 4]; |
| ncrcmd skip [ 9 + PREFETCH_FLUSH_CNT]; |
| ncrcmd skip2 [ 19]; |
| ncrcmd par_err_data_in [ 6]; |
| ncrcmd par_err_other [ 4]; |
| ncrcmd msg_reject [ 8]; |
| ncrcmd msg_ign_residue [ 24]; |
| ncrcmd msg_extended [ 10]; |
| ncrcmd msg_ext_2 [ 10]; |
| ncrcmd msg_wdtr [ 14]; |
| ncrcmd send_wdtr [ 7]; |
| ncrcmd msg_ext_3 [ 10]; |
| ncrcmd msg_sdtr [ 14]; |
| ncrcmd send_sdtr [ 7]; |
| ncrcmd nego_bad_phase [ 4]; |
| ncrcmd msg_out_abort [ 10]; |
| ncrcmd hdata_in [MAX_SCATTERH * 4]; |
| ncrcmd hdata_in2 [ 2]; |
| ncrcmd hdata_out [MAX_SCATTERH * 4]; |
| ncrcmd hdata_out2 [ 2]; |
| ncrcmd reset [ 4]; |
| ncrcmd aborttag [ 4]; |
| ncrcmd abort [ 2]; |
| ncrcmd abort_resel [ 20]; |
| ncrcmd resend_ident [ 4]; |
| ncrcmd clratn_go_on [ 3]; |
| ncrcmd nxtdsp_go_on [ 1]; |
| ncrcmd sdata_in [ 8]; |
| ncrcmd data_io [ 18]; |
| ncrcmd bad_identify [ 12]; |
| ncrcmd bad_i_t_l [ 4]; |
| ncrcmd bad_i_t_l_q [ 4]; |
| ncrcmd bad_target [ 8]; |
| ncrcmd bad_status [ 8]; |
| ncrcmd start_ram [ 4 + PREFETCH_FLUSH_CNT]; |
| ncrcmd start_ram0 [ 4]; |
| ncrcmd sto_restart [ 5]; |
| ncrcmd wait_dma [ 2]; |
| ncrcmd snooptest [ 9]; |
| ncrcmd snoopend [ 2]; |
| }; |
| |
| /*========================================================== |
| ** |
| ** |
| ** Function headers. |
| ** |
| ** |
| **========================================================== |
| */ |
| |
| static void ncr_alloc_ccb (struct ncb *np, u_char tn, u_char ln); |
| static void ncr_complete (struct ncb *np, struct ccb *cp); |
| static void ncr_exception (struct ncb *np); |
| static void ncr_free_ccb (struct ncb *np, struct ccb *cp); |
| static void ncr_init_ccb (struct ncb *np, struct ccb *cp); |
| static void ncr_init_tcb (struct ncb *np, u_char tn); |
| static struct lcb * ncr_alloc_lcb (struct ncb *np, u_char tn, u_char ln); |
| static struct lcb * ncr_setup_lcb (struct ncb *np, struct scsi_device *sdev); |
| static void ncr_getclock (struct ncb *np, int mult); |
| static void ncr_selectclock (struct ncb *np, u_char scntl3); |
| static struct ccb *ncr_get_ccb (struct ncb *np, struct scsi_cmnd *cmd); |
| static void ncr_chip_reset (struct ncb *np, int delay); |
| static void ncr_init (struct ncb *np, int reset, char * msg, u_long code); |
| static int ncr_int_sbmc (struct ncb *np); |
| static int ncr_int_par (struct ncb *np); |
| static void ncr_int_ma (struct ncb *np); |
| static void ncr_int_sir (struct ncb *np); |
| static void ncr_int_sto (struct ncb *np); |
| static void ncr_negotiate (struct ncb* np, struct tcb* tp); |
| static int ncr_prepare_nego(struct ncb *np, struct ccb *cp, u_char *msgptr); |
| |
| static void ncr_script_copy_and_bind |
| (struct ncb *np, ncrcmd *src, ncrcmd *dst, int len); |
| static void ncr_script_fill (struct script * scr, struct scripth * scripth); |
| static int ncr_scatter (struct ncb *np, struct ccb *cp, struct scsi_cmnd *cmd); |
| static void ncr_getsync (struct ncb *np, u_char sfac, u_char *fakp, u_char *scntl3p); |
| static void ncr_setsync (struct ncb *np, struct ccb *cp, u_char scntl3, u_char sxfer); |
| static void ncr_setup_tags (struct ncb *np, struct scsi_device *sdev); |
| static void ncr_setwide (struct ncb *np, struct ccb *cp, u_char wide, u_char ack); |
| static int ncr_snooptest (struct ncb *np); |
| static void ncr_timeout (struct ncb *np); |
| static void ncr_wakeup (struct ncb *np, u_long code); |
| static void ncr_wakeup_done (struct ncb *np); |
| static void ncr_start_next_ccb (struct ncb *np, struct lcb * lp, int maxn); |
| static void ncr_put_start_queue(struct ncb *np, struct ccb *cp); |
| |
| static void insert_into_waiting_list(struct ncb *np, struct scsi_cmnd *cmd); |
| static struct scsi_cmnd *retrieve_from_waiting_list(int to_remove, struct ncb *np, struct scsi_cmnd *cmd); |
| static void process_waiting_list(struct ncb *np, int sts); |
| |
| #define remove_from_waiting_list(np, cmd) \ |
| retrieve_from_waiting_list(1, (np), (cmd)) |
| #define requeue_waiting_list(np) process_waiting_list((np), DID_OK) |
| #define reset_waiting_list(np) process_waiting_list((np), DID_RESET) |
| |
| static inline char *ncr_name (struct ncb *np) |
| { |
| return np->inst_name; |
| } |
| |
| |
| /*========================================================== |
| ** |
| ** |
| ** Scripts for NCR-Processor. |
| ** |
| ** Use ncr_script_bind for binding to physical addresses. |
| ** |
| ** |
| **========================================================== |
| ** |
| ** NADDR generates a reference to a field of the controller data. |
| ** PADDR generates a reference to another part of the script. |
| ** RADDR generates a reference to a script processor register. |
| ** FADDR generates a reference to a script processor register |
| ** with offset. |
| ** |
| **---------------------------------------------------------- |
| */ |
| |
| #define RELOC_SOFTC 0x40000000 |
| #define RELOC_LABEL 0x50000000 |
| #define RELOC_REGISTER 0x60000000 |
| #if 0 |
| #define RELOC_KVAR 0x70000000 |
| #endif |
| #define RELOC_LABELH 0x80000000 |
| #define RELOC_MASK 0xf0000000 |
| |
| #define NADDR(label) (RELOC_SOFTC | offsetof(struct ncb, label)) |
| #define PADDR(label) (RELOC_LABEL | offsetof(struct script, label)) |
| #define PADDRH(label) (RELOC_LABELH | offsetof(struct scripth, label)) |
| #define RADDR(label) (RELOC_REGISTER | REG(label)) |
| #define FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs))) |
| #if 0 |
| #define KVAR(which) (RELOC_KVAR | (which)) |
| #endif |
| |
| #if 0 |
| #define SCRIPT_KVAR_JIFFIES (0) |
| #define SCRIPT_KVAR_FIRST SCRIPT_KVAR_JIFFIES |
| #define SCRIPT_KVAR_LAST SCRIPT_KVAR_JIFFIES |
| /* |
| * Kernel variables referenced in the scripts. |
| * THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY. |
| */ |
| static void *script_kvars[] __initdata = |
| { (void *)&jiffies }; |
| #endif |
| |
| static struct script script0 __initdata = { |
| /*--------------------------< START >-----------------------*/ { |
| /* |
| ** This NOP will be patched with LED ON |
| ** SCR_REG_REG (gpreg, SCR_AND, 0xfe) |
| */ |
| SCR_NO_OP, |
| 0, |
| /* |
| ** Clear SIGP. |
| */ |
| SCR_FROM_REG (ctest2), |
| 0, |
| /* |
| ** Then jump to a certain point in tryloop. |
| ** Due to the lack of indirect addressing the code |
| ** is self modifying here. |
| */ |
| SCR_JUMP, |
| }/*-------------------------< STARTPOS >--------------------*/,{ |
| PADDRH(tryloop), |
| |
| }/*-------------------------< SELECT >----------------------*/,{ |
| /* |
| ** DSA contains the address of a scheduled |
| ** data structure. |
| ** |
| ** SCRATCHA contains the address of the script, |
| ** which starts the next entry. |
| ** |
| ** Set Initiator mode. |
| ** |
| ** (Target mode is left as an exercise for the reader) |
| */ |
| |
| SCR_CLR (SCR_TRG), |
| 0, |
| SCR_LOAD_REG (HS_REG, HS_SELECTING), |
| 0, |
| |
| /* |
| ** And try to select this target. |
| */ |
| SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select), |
| PADDR (reselect), |
| |
| }/*-------------------------< SELECT2 >----------------------*/,{ |
| /* |
| ** Now there are 4 possibilities: |
| ** |
| ** (1) The ncr loses arbitration. |
| ** This is ok, because it will try again, |
| ** when the bus becomes idle. |
| ** (But beware of the timeout function!) |
| ** |
| ** (2) The ncr is reselected. |
| ** Then the script processor takes the jump |
| ** to the RESELECT label. |
| ** |
| ** (3) The ncr wins arbitration. |
| ** Then it will execute SCRIPTS instruction until |
| ** the next instruction that checks SCSI phase. |
| ** Then will stop and wait for selection to be |
| ** complete or selection time-out to occur. |
| ** As a result the SCRIPTS instructions until |
| ** LOADPOS + 2 should be executed in parallel with |
| ** the SCSI core performing selection. |
| */ |
| |
| /* |
| ** The MESSAGE_REJECT problem seems to be due to a selection |
| ** timing problem. |
| ** Wait immediately for the selection to complete. |
| ** (2.5x behaves so) |
| */ |
| SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_OUT)), |
| 0, |
| |
| /* |
| ** Next time use the next slot. |
| */ |
| SCR_COPY (4), |
| RADDR (temp), |
| PADDR (startpos), |
| /* |
| ** The ncr doesn't have an indirect load |
| ** or store command. So we have to |
| ** copy part of the control block to a |
| ** fixed place, where we can access it. |
| ** |
| ** We patch the address part of a |
| ** COPY command with the DSA-register. |
| */ |
| SCR_COPY_F (4), |
| RADDR (dsa), |
| PADDR (loadpos), |
| /* |
| ** Flush script prefetch if required |
| */ |
| PREFETCH_FLUSH |
| /* |
| ** then we do the actual copy. |
| */ |
| SCR_COPY (sizeof (struct head)), |
| /* |
| ** continued after the next label ... |
| */ |
| }/*-------------------------< LOADPOS >---------------------*/,{ |
| 0, |
| NADDR (header), |
| /* |
| ** Wait for the next phase or the selection |
| ** to complete or time-out. |
| */ |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)), |
| PADDR (prepare), |
| |
| }/*-------------------------< SEND_IDENT >----------------------*/,{ |
| /* |
| ** Selection complete. |
| ** Send the IDENTIFY and SIMPLE_TAG messages |
| ** (and the EXTENDED_SDTR message) |
| */ |
| SCR_MOVE_TBL ^ SCR_MSG_OUT, |
| offsetof (struct dsb, smsg), |
| SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)), |
| PADDRH (resend_ident), |
| SCR_LOAD_REG (scratcha, 0x80), |
| 0, |
| SCR_COPY (1), |
| RADDR (scratcha), |
| NADDR (lastmsg), |
| }/*-------------------------< PREPARE >----------------------*/,{ |
| /* |
| ** load the savep (saved pointer) into |
| ** the TEMP register (actual pointer) |
| */ |
| SCR_COPY (4), |
| NADDR (header.savep), |
| RADDR (temp), |
| /* |
| ** Initialize the status registers |
| */ |
| SCR_COPY (4), |
| NADDR (header.status), |
| RADDR (scr0), |
| }/*-------------------------< PREPARE2 >---------------------*/,{ |
| /* |
| ** Initialize the msgout buffer with a NOOP message. |
| */ |
| SCR_LOAD_REG (scratcha, NOP), |
| 0, |
| SCR_COPY (1), |
| RADDR (scratcha), |
| NADDR (msgout), |
| #if 0 |
| SCR_COPY (1), |
| RADDR (scratcha), |
| NADDR (msgin), |
| #endif |
| /* |
| ** Anticipate the COMMAND phase. |
| ** This is the normal case for initial selection. |
| */ |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_COMMAND)), |
| PADDR (dispatch), |
| |
| }/*-------------------------< COMMAND >--------------------*/,{ |
| /* |
| ** ... and send the command |
| */ |
| SCR_MOVE_TBL ^ SCR_COMMAND, |
| offsetof (struct dsb, cmd), |
| /* |
| ** If status is still HS_NEGOTIATE, negotiation failed. |
| ** We check this here, since we want to do that |
| ** only once. |
| */ |
| SCR_FROM_REG (HS_REG), |
| 0, |
| SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)), |
| SIR_NEGO_FAILED, |
| |
| }/*-----------------------< DISPATCH >----------------------*/,{ |
| /* |
| ** MSG_IN is the only phase that shall be |
| ** entered at least once for each (re)selection. |
| ** So we test it first. |
| */ |
| SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)), |
| PADDR (msg_in), |
| |
| SCR_RETURN ^ IFTRUE (IF (SCR_DATA_OUT)), |
| 0, |
| /* |
| ** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 4. |
| ** Possible data corruption during Memory Write and Invalidate. |
| ** This work-around resets the addressing logic prior to the |
| ** start of the first MOVE of a DATA IN phase. |
| ** (See Documentation/scsi/ncr53c8xx.txt for more information) |
| */ |
| SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)), |
| 20, |
| SCR_COPY (4), |
| RADDR (scratcha), |
| RADDR (scratcha), |
| SCR_RETURN, |
| 0, |
| SCR_JUMP ^ IFTRUE (IF (SCR_STATUS)), |
| PADDR (status), |
| SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)), |
| PADDR (command), |
| SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)), |
| PADDR (msg_out), |
| /* |
| ** Discard one illegal phase byte, if required. |
| */ |
| SCR_LOAD_REG (scratcha, XE_BAD_PHASE), |
| 0, |
| SCR_COPY (1), |
| RADDR (scratcha), |
| NADDR (xerr_st), |
| SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_OUT)), |
| 8, |
| SCR_MOVE_ABS (1) ^ SCR_ILG_OUT, |
| NADDR (scratch), |
| SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_IN)), |
| 8, |
| SCR_MOVE_ABS (1) ^ SCR_ILG_IN, |
| NADDR (scratch), |
| SCR_JUMP, |
| PADDR (dispatch), |
| |
| }/*-------------------------< CLRACK >----------------------*/,{ |
| /* |
| ** Terminate possible pending message phase. |
| */ |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP, |
| PADDR (dispatch), |
| |
| }/*-------------------------< NO_DATA >--------------------*/,{ |
| /* |
| ** The target wants to tranfer too much data |
| ** or in the wrong direction. |
| ** Remember that in extended error. |
| */ |
| SCR_LOAD_REG (scratcha, XE_EXTRA_DATA), |
| 0, |
| SCR_COPY (1), |
| RADDR (scratcha), |
| NADDR (xerr_st), |
| /* |
| ** Discard one data byte, if required. |
| */ |
| SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_OUT)), |
| 8, |
| SCR_MOVE_ABS (1) ^ SCR_DATA_OUT, |
| NADDR (scratch), |
| SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)), |
| 8, |
| SCR_MOVE_ABS (1) ^ SCR_DATA_IN, |
| NADDR (scratch), |
| /* |
| ** .. and repeat as required. |
| */ |
| SCR_CALL, |
| PADDR (dispatch), |
| SCR_JUMP, |
| PADDR (no_data), |
| |
| }/*-------------------------< STATUS >--------------------*/,{ |
| /* |
| ** get the status |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_STATUS, |
| NADDR (scratch), |
| /* |
| ** save status to scsi_status. |
| ** mark as complete. |
| */ |
| SCR_TO_REG (SS_REG), |
| 0, |
| SCR_LOAD_REG (HS_REG, HS_COMPLETE), |
| 0, |
| SCR_JUMP, |
| PADDR (dispatch), |
| }/*-------------------------< MSG_IN >--------------------*/,{ |
| /* |
| ** Get the first byte of the message |
| ** and save it to SCRATCHA. |
| ** |
| ** The script processor doesn't negate the |
| ** ACK signal after this transfer. |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin[0]), |
| }/*-------------------------< MSG_IN2 >--------------------*/,{ |
| /* |
| ** Handle this message. |
| */ |
| SCR_JUMP ^ IFTRUE (DATA (COMMAND_COMPLETE)), |
| PADDR (complete), |
| SCR_JUMP ^ IFTRUE (DATA (DISCONNECT)), |
| PADDR (disconnect), |
| SCR_JUMP ^ IFTRUE (DATA (SAVE_POINTERS)), |
| PADDR (save_dp), |
| SCR_JUMP ^ IFTRUE (DATA (RESTORE_POINTERS)), |
| PADDR (restore_dp), |
| SCR_JUMP ^ IFTRUE (DATA (EXTENDED_MESSAGE)), |
| PADDRH (msg_extended), |
| SCR_JUMP ^ IFTRUE (DATA (NOP)), |
| PADDR (clrack), |
| SCR_JUMP ^ IFTRUE (DATA (MESSAGE_REJECT)), |
| PADDRH (msg_reject), |
| SCR_JUMP ^ IFTRUE (DATA (IGNORE_WIDE_RESIDUE)), |
| PADDRH (msg_ign_residue), |
| /* |
| ** Rest of the messages left as |
| ** an exercise ... |
| ** |
| ** Unimplemented messages: |
| ** fall through to MSG_BAD. |
| */ |
| }/*-------------------------< MSG_BAD >------------------*/,{ |
| /* |
| ** unimplemented message - reject it. |
| */ |
| SCR_INT, |
| SIR_REJECT_SENT, |
| SCR_LOAD_REG (scratcha, MESSAGE_REJECT), |
| 0, |
| }/*-------------------------< SETMSG >----------------------*/,{ |
| SCR_COPY (1), |
| RADDR (scratcha), |
| NADDR (msgout), |
| SCR_SET (SCR_ATN), |
| 0, |
| SCR_JUMP, |
| PADDR (clrack), |
| }/*-------------------------< CLEANUP >-------------------*/,{ |
| /* |
| ** dsa: Pointer to ccb |
| ** or xxxxxxFF (no ccb) |
| ** |
| ** HS_REG: Host-Status (<>0!) |
| */ |
| SCR_FROM_REG (dsa), |
| 0, |
| SCR_JUMP ^ IFTRUE (DATA (0xff)), |
| PADDR (start), |
| /* |
| ** dsa is valid. |
| ** complete the cleanup. |
| */ |
| SCR_JUMP, |
| PADDR (cleanup_ok), |
| |
| }/*-------------------------< COMPLETE >-----------------*/,{ |
| /* |
| ** Complete message. |
| ** |
| ** Copy TEMP register to LASTP in header. |
| */ |
| SCR_COPY (4), |
| RADDR (temp), |
| NADDR (header.lastp), |
| /* |
| ** When we terminate the cycle by clearing ACK, |
| ** the target may disconnect immediately. |
| ** |
| ** We don't want to be told of an |
| ** "unexpected disconnect", |
| ** so we disable this feature. |
| */ |
| SCR_REG_REG (scntl2, SCR_AND, 0x7f), |
| 0, |
| /* |
| ** Terminate cycle ... |
| */ |
| SCR_CLR (SCR_ACK|SCR_ATN), |
| 0, |
| /* |
| ** ... and wait for the disconnect. |
| */ |
| SCR_WAIT_DISC, |
| 0, |
| }/*-------------------------< CLEANUP_OK >----------------*/,{ |
| /* |
| ** Save host status to header. |
| */ |
| SCR_COPY (4), |
| RADDR (scr0), |
| NADDR (header.status), |
| /* |
| ** and copy back the header to the ccb. |
| */ |
| SCR_COPY_F (4), |
| RADDR (dsa), |
| PADDR (cleanup0), |
| /* |
| ** Flush script prefetch if required |
| */ |
| PREFETCH_FLUSH |
| SCR_COPY (sizeof (struct head)), |
| NADDR (header), |
| }/*-------------------------< CLEANUP0 >--------------------*/,{ |
| 0, |
| }/*-------------------------< SIGNAL >----------------------*/,{ |
| /* |
| ** if job not completed ... |
| */ |
| SCR_FROM_REG (HS_REG), |
| 0, |
| /* |
| ** ... start the next command. |
| */ |
| SCR_JUMP ^ IFTRUE (MASK (0, (HS_DONEMASK|HS_SKIPMASK))), |
| PADDR(start), |
| /* |
| ** If command resulted in not GOOD status, |
| ** call the C code if needed. |
| */ |
| SCR_FROM_REG (SS_REG), |
| 0, |
| SCR_CALL ^ IFFALSE (DATA (S_GOOD)), |
| PADDRH (bad_status), |
| |
| #ifndef SCSI_NCR_CCB_DONE_SUPPORT |
| |
| /* |
| ** ... signal completion to the host |
| */ |
| SCR_INT, |
| SIR_INTFLY, |
| /* |
| ** Auf zu neuen Schandtaten! |
| */ |
| SCR_JUMP, |
| PADDR(start), |
| |
| #else /* defined SCSI_NCR_CCB_DONE_SUPPORT */ |
| |
| /* |
| ** ... signal completion to the host |
| */ |
| SCR_JUMP, |
| }/*------------------------< DONE_POS >---------------------*/,{ |
| PADDRH (done_queue), |
| }/*------------------------< DONE_PLUG >--------------------*/,{ |
| SCR_INT, |
| SIR_DONE_OVERFLOW, |
| }/*------------------------< DONE_END >---------------------*/,{ |
| SCR_INT, |
| SIR_INTFLY, |
| SCR_COPY (4), |
| RADDR (temp), |
| PADDR (done_pos), |
| SCR_JUMP, |
| PADDR (start), |
| |
| #endif /* SCSI_NCR_CCB_DONE_SUPPORT */ |
| |
| }/*-------------------------< SAVE_DP >------------------*/,{ |
| /* |
| ** SAVE_DP message: |
| ** Copy TEMP register to SAVEP in header. |
| */ |
| SCR_COPY (4), |
| RADDR (temp), |
| NADDR (header.savep), |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP, |
| PADDR (dispatch), |
| }/*-------------------------< RESTORE_DP >---------------*/,{ |
| /* |
| ** RESTORE_DP message: |
| ** Copy SAVEP in header to TEMP register. |
| */ |
| SCR_COPY (4), |
| NADDR (header.savep), |
| RADDR (temp), |
| SCR_JUMP, |
| PADDR (clrack), |
| |
| }/*-------------------------< DISCONNECT >---------------*/,{ |
| /* |
| ** DISCONNECTing ... |
| ** |
| ** disable the "unexpected disconnect" feature, |
| ** and remove the ACK signal. |
| */ |
| SCR_REG_REG (scntl2, SCR_AND, 0x7f), |
| 0, |
| SCR_CLR (SCR_ACK|SCR_ATN), |
| 0, |
| /* |
| ** Wait for the disconnect. |
| */ |
| SCR_WAIT_DISC, |
| 0, |
| /* |
| ** Status is: DISCONNECTED. |
| */ |
| SCR_LOAD_REG (HS_REG, HS_DISCONNECT), |
| 0, |
| SCR_JUMP, |
| PADDR (cleanup_ok), |
| |
| }/*-------------------------< MSG_OUT >-------------------*/,{ |
| /* |
| ** The target requests a message. |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_OUT, |
| NADDR (msgout), |
| SCR_COPY (1), |
| NADDR (msgout), |
| NADDR (lastmsg), |
| /* |
| ** If it was no ABORT message ... |
| */ |
| SCR_JUMP ^ IFTRUE (DATA (ABORT_TASK_SET)), |
| PADDRH (msg_out_abort), |
| /* |
| ** ... wait for the next phase |
| ** if it's a message out, send it again, ... |
| */ |
| SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)), |
| PADDR (msg_out), |
| }/*-------------------------< MSG_OUT_DONE >--------------*/,{ |
| /* |
| ** ... else clear the message ... |
| */ |
| SCR_LOAD_REG (scratcha, NOP), |
| 0, |
| SCR_COPY (4), |
| RADDR (scratcha), |
| NADDR (msgout), |
| /* |
| ** ... and process the next phase |
| */ |
| SCR_JUMP, |
| PADDR (dispatch), |
| }/*-------------------------< IDLE >------------------------*/,{ |
| /* |
| ** Nothing to do? |
| ** Wait for reselect. |
| ** This NOP will be patched with LED OFF |
| ** SCR_REG_REG (gpreg, SCR_OR, 0x01) |
| */ |
| SCR_NO_OP, |
| 0, |
| }/*-------------------------< RESELECT >--------------------*/,{ |
| /* |
| ** make the DSA invalid. |
| */ |
| SCR_LOAD_REG (dsa, 0xff), |
| 0, |
| SCR_CLR (SCR_TRG), |
| 0, |
| SCR_LOAD_REG (HS_REG, HS_IN_RESELECT), |
| 0, |
| /* |
| ** Sleep waiting for a reselection. |
| ** If SIGP is set, special treatment. |
| ** |
| ** Zu allem bereit .. |
| */ |
| SCR_WAIT_RESEL, |
| PADDR(start), |
| }/*-------------------------< RESELECTED >------------------*/,{ |
| /* |
| ** This NOP will be patched with LED ON |
| ** SCR_REG_REG (gpreg, SCR_AND, 0xfe) |
| */ |
| SCR_NO_OP, |
| 0, |
| /* |
| ** ... zu nichts zu gebrauchen ? |
| ** |
| ** load the target id into the SFBR |
| ** and jump to the control block. |
| ** |
| ** Look at the declarations of |
| ** - struct ncb |
| ** - struct tcb |
| ** - struct lcb |
| ** - struct ccb |
| ** to understand what's going on. |
| */ |
| SCR_REG_SFBR (ssid, SCR_AND, 0x8F), |
| 0, |
| SCR_TO_REG (sdid), |
| 0, |
| SCR_JUMP, |
| NADDR (jump_tcb), |
| |
| }/*-------------------------< RESEL_DSA >-------------------*/,{ |
| /* |
| ** Ack the IDENTIFY or TAG previously received. |
| */ |
| SCR_CLR (SCR_ACK), |
| 0, |
| /* |
| ** The ncr doesn't have an indirect load |
| ** or store command. So we have to |
| ** copy part of the control block to a |
| ** fixed place, where we can access it. |
| ** |
| ** We patch the address part of a |
| ** COPY command with the DSA-register. |
| */ |
| SCR_COPY_F (4), |
| RADDR (dsa), |
| PADDR (loadpos1), |
| /* |
| ** Flush script prefetch if required |
| */ |
| PREFETCH_FLUSH |
| /* |
| ** then we do the actual copy. |
| */ |
| SCR_COPY (sizeof (struct head)), |
| /* |
| ** continued after the next label ... |
| */ |
| |
| }/*-------------------------< LOADPOS1 >-------------------*/,{ |
| 0, |
| NADDR (header), |
| /* |
| ** The DSA contains the data structure address. |
| */ |
| SCR_JUMP, |
| PADDR (prepare), |
| |
| }/*-------------------------< RESEL_LUN >-------------------*/,{ |
| /* |
| ** come back to this point |
| ** to get an IDENTIFY message |
| ** Wait for a msg_in phase. |
| */ |
| SCR_INT ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| SIR_RESEL_NO_MSG_IN, |
| /* |
| ** message phase. |
| ** Read the data directly from the BUS DATA lines. |
| ** This helps to support very old SCSI devices that |
| ** may reselect without sending an IDENTIFY. |
| */ |
| SCR_FROM_REG (sbdl), |
| 0, |
| /* |
| ** It should be an Identify message. |
| */ |
| SCR_RETURN, |
| 0, |
| }/*-------------------------< RESEL_TAG >-------------------*/,{ |
| /* |
| ** Read IDENTIFY + SIMPLE + TAG using a single MOVE. |
| ** Aggressive optimization, is'nt it? |
| ** No need to test the SIMPLE TAG message, since the |
| ** driver only supports conformant devices for tags. ;-) |
| */ |
| SCR_MOVE_ABS (3) ^ SCR_MSG_IN, |
| NADDR (msgin), |
| /* |
| ** Read the TAG from the SIDL. |
| ** Still an aggressive optimization. ;-) |
| ** Compute the CCB indirect jump address which |
| ** is (#TAG*2 & 0xfc) due to tag numbering using |
| ** 1,3,5..MAXTAGS*2+1 actual values. |
| */ |
| SCR_REG_SFBR (sidl, SCR_SHL, 0), |
| 0, |
| SCR_SFBR_REG (temp, SCR_AND, 0xfc), |
| 0, |
| }/*-------------------------< JUMP_TO_NEXUS >-------------------*/,{ |
| SCR_COPY_F (4), |
| RADDR (temp), |
| PADDR (nexus_indirect), |
| /* |
| ** Flush script prefetch if required |
| */ |
| PREFETCH_FLUSH |
| SCR_COPY (4), |
| }/*-------------------------< NEXUS_INDIRECT >-------------------*/,{ |
| 0, |
| RADDR (temp), |
| SCR_RETURN, |
| 0, |
| }/*-------------------------< RESEL_NOTAG >-------------------*/,{ |
| /* |
| ** No tag expected. |
| ** Read an throw away the IDENTIFY. |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin), |
| SCR_JUMP, |
| PADDR (jump_to_nexus), |
| }/*-------------------------< DATA_IN >--------------------*/,{ |
| /* |
| ** Because the size depends on the |
| ** #define MAX_SCATTERL parameter, |
| ** it is filled in at runtime. |
| ** |
| ** ##===========< i=0; i<MAX_SCATTERL >========= |
| ** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)), |
| ** || PADDR (dispatch), |
| ** || SCR_MOVE_TBL ^ SCR_DATA_IN, |
| ** || offsetof (struct dsb, data[ i]), |
| ** ##========================================== |
| ** |
| **--------------------------------------------------------- |
| */ |
| 0 |
| }/*-------------------------< DATA_IN2 >-------------------*/,{ |
| SCR_CALL, |
| PADDR (dispatch), |
| SCR_JUMP, |
| PADDR (no_data), |
| }/*-------------------------< DATA_OUT >--------------------*/,{ |
| /* |
| ** Because the size depends on the |
| ** #define MAX_SCATTERL parameter, |
| ** it is filled in at runtime. |
| ** |
| ** ##===========< i=0; i<MAX_SCATTERL >========= |
| ** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)), |
| ** || PADDR (dispatch), |
| ** || SCR_MOVE_TBL ^ SCR_DATA_OUT, |
| ** || offsetof (struct dsb, data[ i]), |
| ** ##========================================== |
| ** |
| **--------------------------------------------------------- |
| */ |
| 0 |
| }/*-------------------------< DATA_OUT2 >-------------------*/,{ |
| SCR_CALL, |
| PADDR (dispatch), |
| SCR_JUMP, |
| PADDR (no_data), |
| }/*--------------------------------------------------------*/ |
| }; |
| |
| static struct scripth scripth0 __initdata = { |
| /*-------------------------< TRYLOOP >---------------------*/{ |
| /* |
| ** Start the next entry. |
| ** Called addresses point to the launch script in the CCB. |
| ** They are patched by the main processor. |
| ** |
| ** Because the size depends on the |
| ** #define MAX_START parameter, it is filled |
| ** in at runtime. |
| ** |
| **----------------------------------------------------------- |
| ** |
| ** ##===========< I=0; i<MAX_START >=========== |
| ** || SCR_CALL, |
| ** || PADDR (idle), |
| ** ##========================================== |
| ** |
| **----------------------------------------------------------- |
| */ |
| 0 |
| }/*------------------------< TRYLOOP2 >---------------------*/,{ |
| SCR_JUMP, |
| PADDRH(tryloop), |
| |
| #ifdef SCSI_NCR_CCB_DONE_SUPPORT |
| |
| }/*------------------------< DONE_QUEUE >-------------------*/,{ |
| /* |
| ** Copy the CCB address to the next done entry. |
| ** Because the size depends on the |
| ** #define MAX_DONE parameter, it is filled |
| ** in at runtime. |
| ** |
| **----------------------------------------------------------- |
| ** |
| ** ##===========< I=0; i<MAX_DONE >=========== |
| ** || SCR_COPY (sizeof(struct ccb *), |
| ** || NADDR (header.cp), |
| ** || NADDR (ccb_done[i]), |
| ** || SCR_CALL, |
| ** || PADDR (done_end), |
| ** ##========================================== |
| ** |
| **----------------------------------------------------------- |
| */ |
| 0 |
| }/*------------------------< DONE_QUEUE2 >------------------*/,{ |
| SCR_JUMP, |
| PADDRH (done_queue), |
| |
| #endif /* SCSI_NCR_CCB_DONE_SUPPORT */ |
| }/*------------------------< SELECT_NO_ATN >-----------------*/,{ |
| /* |
| ** Set Initiator mode. |
| ** And try to select this target without ATN. |
| */ |
| |
| SCR_CLR (SCR_TRG), |
| 0, |
| SCR_LOAD_REG (HS_REG, HS_SELECTING), |
| 0, |
| SCR_SEL_TBL ^ offsetof (struct dsb, select), |
| PADDR (reselect), |
| SCR_JUMP, |
| PADDR (select2), |
| |
| }/*-------------------------< CANCEL >------------------------*/,{ |
| |
| SCR_LOAD_REG (scratcha, HS_ABORTED), |
| 0, |
| SCR_JUMPR, |
| 8, |
| }/*-------------------------< SKIP >------------------------*/,{ |
| SCR_LOAD_REG (scratcha, 0), |
| 0, |
| /* |
| ** This entry has been canceled. |
| ** Next time use the next slot. |
| */ |
| SCR_COPY (4), |
| RADDR (temp), |
| PADDR (startpos), |
| /* |
| ** The ncr doesn't have an indirect load |
| ** or store command. So we have to |
| ** copy part of the control block to a |
| ** fixed place, where we can access it. |
| ** |
| ** We patch the address part of a |
| ** COPY command with the DSA-register. |
| */ |
| SCR_COPY_F (4), |
| RADDR (dsa), |
| PADDRH (skip2), |
| /* |
| ** Flush script prefetch if required |
| */ |
| PREFETCH_FLUSH |
| /* |
| ** then we do the actual copy. |
| */ |
| SCR_COPY (sizeof (struct head)), |
| /* |
| ** continued after the next label ... |
| */ |
| }/*-------------------------< SKIP2 >---------------------*/,{ |
| 0, |
| NADDR (header), |
| /* |
| ** Initialize the status registers |
| */ |
| SCR_COPY (4), |
| NADDR (header.status), |
| RADDR (scr0), |
| /* |
| ** Force host status. |
| */ |
| SCR_FROM_REG (scratcha), |
| 0, |
| SCR_JUMPR ^ IFFALSE (MASK (0, HS_DONEMASK)), |
| 16, |
| SCR_REG_REG (HS_REG, SCR_OR, HS_SKIPMASK), |
| 0, |
| SCR_JUMPR, |
| 8, |
| SCR_TO_REG (HS_REG), |
| 0, |
| SCR_LOAD_REG (SS_REG, S_GOOD), |
| 0, |
| SCR_JUMP, |
| PADDR (cleanup_ok), |
| |
| },/*-------------------------< PAR_ERR_DATA_IN >---------------*/{ |
| /* |
| ** Ignore all data in byte, until next phase |
| */ |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)), |
| PADDRH (par_err_other), |
| SCR_MOVE_ABS (1) ^ SCR_DATA_IN, |
| NADDR (scratch), |
| SCR_JUMPR, |
| -24, |
| },/*-------------------------< PAR_ERR_OTHER >------------------*/{ |
| /* |
| ** count it. |
| */ |
| SCR_REG_REG (PS_REG, SCR_ADD, 0x01), |
| 0, |
| /* |
| ** jump to dispatcher. |
| */ |
| SCR_JUMP, |
| PADDR (dispatch), |
| }/*-------------------------< MSG_REJECT >---------------*/,{ |
| /* |
| ** If a negotiation was in progress, |
| ** negotiation failed. |
| ** Otherwise, let the C code print |
| ** some message. |
| */ |
| SCR_FROM_REG (HS_REG), |
| 0, |
| SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)), |
| SIR_REJECT_RECEIVED, |
| SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)), |
| SIR_NEGO_FAILED, |
| SCR_JUMP, |
| PADDR (clrack), |
| |
| }/*-------------------------< MSG_IGN_RESIDUE >----------*/,{ |
| /* |
| ** Terminate cycle |
| */ |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| PADDR (dispatch), |
| /* |
| ** get residue size. |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin[1]), |
| /* |
| ** Size is 0 .. ignore message. |
| */ |
| SCR_JUMP ^ IFTRUE (DATA (0)), |
| PADDR (clrack), |
| /* |
| ** Size is not 1 .. have to interrupt. |
| */ |
| SCR_JUMPR ^ IFFALSE (DATA (1)), |
| 40, |
| /* |
| ** Check for residue byte in swide register |
| */ |
| SCR_FROM_REG (scntl2), |
| 0, |
| SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)), |
| 16, |
| /* |
| ** There IS data in the swide register. |
| ** Discard it. |
| */ |
| SCR_REG_REG (scntl2, SCR_OR, WSR), |
| 0, |
| SCR_JUMP, |
| PADDR (clrack), |
| /* |
| ** Load again the size to the sfbr register. |
| */ |
| SCR_FROM_REG (scratcha), |
| 0, |
| SCR_INT, |
| SIR_IGN_RESIDUE, |
| SCR_JUMP, |
| PADDR (clrack), |
| |
| }/*-------------------------< MSG_EXTENDED >-------------*/,{ |
| /* |
| ** Terminate cycle |
| */ |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| PADDR (dispatch), |
| /* |
| ** get length. |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin[1]), |
| /* |
| */ |
| SCR_JUMP ^ IFTRUE (DATA (3)), |
| PADDRH (msg_ext_3), |
| SCR_JUMP ^ IFFALSE (DATA (2)), |
| PADDR (msg_bad), |
| }/*-------------------------< MSG_EXT_2 >----------------*/,{ |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| PADDR (dispatch), |
| /* |
| ** get extended message code. |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin[2]), |
| SCR_JUMP ^ IFTRUE (DATA (EXTENDED_WDTR)), |
| PADDRH (msg_wdtr), |
| /* |
| ** unknown extended message |
| */ |
| SCR_JUMP, |
| PADDR (msg_bad) |
| }/*-------------------------< MSG_WDTR >-----------------*/,{ |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| PADDR (dispatch), |
| /* |
| ** get data bus width |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin[3]), |
| /* |
| ** let the host do the real work. |
| */ |
| SCR_INT, |
| SIR_NEGO_WIDE, |
| /* |
| ** let the target fetch our answer. |
| */ |
| SCR_SET (SCR_ATN), |
| 0, |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)), |
| PADDRH (nego_bad_phase), |
| |
| }/*-------------------------< SEND_WDTR >----------------*/,{ |
| /* |
| ** Send the EXTENDED_WDTR |
| */ |
| SCR_MOVE_ABS (4) ^ SCR_MSG_OUT, |
| NADDR (msgout), |
| SCR_COPY (1), |
| NADDR (msgout), |
| NADDR (lastmsg), |
| SCR_JUMP, |
| PADDR (msg_out_done), |
| |
| }/*-------------------------< MSG_EXT_3 >----------------*/,{ |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| PADDR (dispatch), |
| /* |
| ** get extended message code. |
| */ |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin[2]), |
| SCR_JUMP ^ IFTRUE (DATA (EXTENDED_SDTR)), |
| PADDRH (msg_sdtr), |
| /* |
| ** unknown extended message |
| */ |
| SCR_JUMP, |
| PADDR (msg_bad) |
| |
| }/*-------------------------< MSG_SDTR >-----------------*/,{ |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| PADDR (dispatch), |
| /* |
| ** get period and offset |
| */ |
| SCR_MOVE_ABS (2) ^ SCR_MSG_IN, |
| NADDR (msgin[3]), |
| /* |
| ** let the host do the real work. |
| */ |
| SCR_INT, |
| SIR_NEGO_SYNC, |
| /* |
| ** let the target fetch our answer. |
| */ |
| SCR_SET (SCR_ATN), |
| 0, |
| SCR_CLR (SCR_ACK), |
| 0, |
| SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)), |
| PADDRH (nego_bad_phase), |
| |
| }/*-------------------------< SEND_SDTR >-------------*/,{ |
| /* |
| ** Send the EXTENDED_SDTR |
| */ |
| SCR_MOVE_ABS (5) ^ SCR_MSG_OUT, |
| NADDR (msgout), |
| SCR_COPY (1), |
| NADDR (msgout), |
| NADDR (lastmsg), |
| SCR_JUMP, |
| PADDR (msg_out_done), |
| |
| }/*-------------------------< NEGO_BAD_PHASE >------------*/,{ |
| SCR_INT, |
| SIR_NEGO_PROTO, |
| SCR_JUMP, |
| PADDR (dispatch), |
| |
| }/*-------------------------< MSG_OUT_ABORT >-------------*/,{ |
| /* |
| ** After ABORT message, |
| ** |
| ** expect an immediate disconnect, ... |
| */ |
| SCR_REG_REG (scntl2, SCR_AND, 0x7f), |
| 0, |
| SCR_CLR (SCR_ACK|SCR_ATN), |
| 0, |
| SCR_WAIT_DISC, |
| 0, |
| /* |
| ** ... and set the status to "ABORTED" |
| */ |
| SCR_LOAD_REG (HS_REG, HS_ABORTED), |
| 0, |
| SCR_JUMP, |
| PADDR (cleanup), |
| |
| }/*-------------------------< HDATA_IN >-------------------*/,{ |
| /* |
| ** Because the size depends on the |
| ** #define MAX_SCATTERH parameter, |
| ** it is filled in at runtime. |
| ** |
| ** ##==< i=MAX_SCATTERL; i<MAX_SCATTERL+MAX_SCATTERH >== |
| ** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)), |
| ** || PADDR (dispatch), |
| ** || SCR_MOVE_TBL ^ SCR_DATA_IN, |
| ** || offsetof (struct dsb, data[ i]), |
| ** ##=================================================== |
| ** |
| **--------------------------------------------------------- |
| */ |
| 0 |
| }/*-------------------------< HDATA_IN2 >------------------*/,{ |
| SCR_JUMP, |
| PADDR (data_in), |
| |
| }/*-------------------------< HDATA_OUT >-------------------*/,{ |
| /* |
| ** Because the size depends on the |
| ** #define MAX_SCATTERH parameter, |
| ** it is filled in at runtime. |
| ** |
| ** ##==< i=MAX_SCATTERL; i<MAX_SCATTERL+MAX_SCATTERH >== |
| ** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)), |
| ** || PADDR (dispatch), |
| ** || SCR_MOVE_TBL ^ SCR_DATA_OUT, |
| ** || offsetof (struct dsb, data[ i]), |
| ** ##=================================================== |
| ** |
| **--------------------------------------------------------- |
| */ |
| 0 |
| }/*-------------------------< HDATA_OUT2 >------------------*/,{ |
| SCR_JUMP, |
| PADDR (data_out), |
| |
| }/*-------------------------< RESET >----------------------*/,{ |
| /* |
| ** Send a TARGET_RESET message if bad IDENTIFY |
| ** received on reselection. |
| */ |
| SCR_LOAD_REG (scratcha, ABORT_TASK), |
| 0, |
| SCR_JUMP, |
| PADDRH (abort_resel), |
| }/*-------------------------< ABORTTAG >-------------------*/,{ |
| /* |
| ** Abort a wrong tag received on reselection. |
| */ |
| SCR_LOAD_REG (scratcha, ABORT_TASK), |
| 0, |
| SCR_JUMP, |
| PADDRH (abort_resel), |
| }/*-------------------------< ABORT >----------------------*/,{ |
| /* |
| ** Abort a reselection when no active CCB. |
| */ |
| SCR_LOAD_REG (scratcha, ABORT_TASK_SET), |
| 0, |
| }/*-------------------------< ABORT_RESEL >----------------*/,{ |
| SCR_COPY (1), |
| RADDR (scratcha), |
| NADDR (msgout), |
| SCR_SET (SCR_ATN), |
| 0, |
| SCR_CLR (SCR_ACK), |
| 0, |
| /* |
| ** and send it. |
| ** we expect an immediate disconnect |
| */ |
| SCR_REG_REG (scntl2, SCR_AND, 0x7f), |
| 0, |
| SCR_MOVE_ABS (1) ^ SCR_MSG_OUT, |
| NADDR (msgout), |
| SCR_COPY (1), |
| NADDR (msgout), |
| NADDR (lastmsg), |
| SCR_CLR (SCR_ACK|SCR_ATN), |
| 0, |
| SCR_WAIT_DISC, |
| 0, |
| SCR_JUMP, |
| PADDR (start), |
| }/*-------------------------< RESEND_IDENT >-------------------*/,{ |
| /* |
| ** The target stays in MSG OUT phase after having acked |
| ** Identify [+ Tag [+ Extended message ]]. Targets shall |
| ** behave this way on parity error. |
| ** We must send it again all the messages. |
| */ |
| SCR_SET (SCR_ATN), /* Shall be asserted 2 deskew delays before the */ |
| 0, /* 1rst ACK = 90 ns. Hope the NCR is'nt too fast */ |
| SCR_JUMP, |
| PADDR (send_ident), |
| }/*-------------------------< CLRATN_GO_ON >-------------------*/,{ |
| SCR_CLR (SCR_ATN), |
| 0, |
| SCR_JUMP, |
| }/*-------------------------< NXTDSP_GO_ON >-------------------*/,{ |
| 0, |
| }/*-------------------------< SDATA_IN >-------------------*/,{ |
| SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)), |
| PADDR (dispatch), |
| SCR_MOVE_TBL ^ SCR_DATA_IN, |
| offsetof (struct dsb, sense), |
| SCR_CALL, |
| PADDR (dispatch), |
| SCR_JUMP, |
| PADDR (no_data), |
| }/*-------------------------< DATA_IO >--------------------*/,{ |
| /* |
| ** We jump here if the data direction was unknown at the |
| ** time we had to queue the command to the scripts processor. |
| ** Pointers had been set as follow in this situation: |
| ** savep --> DATA_IO |
| ** lastp --> start pointer when DATA_IN |
| ** goalp --> goal pointer when DATA_IN |
| ** wlastp --> start pointer when DATA_OUT |
| ** wgoalp --> goal pointer when DATA_OUT |
| ** This script sets savep/lastp/goalp according to the |
| ** direction chosen by the target. |
| */ |
| SCR_JUMPR ^ IFTRUE (WHEN (SCR_DATA_OUT)), |
| 32, |
| /* |
| ** Direction is DATA IN. |
| ** Warning: we jump here, even when phase is DATA OUT. |
| */ |
| SCR_COPY (4), |
| NADDR (header.lastp), |
| NADDR (header.savep), |
| |
| /* |
| ** Jump to the SCRIPTS according to actual direction. |
| */ |
| SCR_COPY (4), |
| NADDR (header.savep), |
| RADDR (temp), |
| SCR_RETURN, |
| 0, |
| /* |
| ** Direction is DATA OUT. |
| */ |
| SCR_COPY (4), |
| NADDR (header.wlastp), |
| NADDR (header.lastp), |
| SCR_COPY (4), |
| NADDR (header.wgoalp), |
| NADDR (header.goalp), |
| SCR_JUMPR, |
| -64, |
| }/*-------------------------< BAD_IDENTIFY >---------------*/,{ |
| /* |
| ** If message phase but not an IDENTIFY, |
| ** get some help from the C code. |
| ** Old SCSI device may behave so. |
| */ |
| SCR_JUMPR ^ IFTRUE (MASK (0x80, 0x80)), |
| 16, |
| SCR_INT, |
| SIR_RESEL_NO_IDENTIFY, |
| SCR_JUMP, |
| PADDRH (reset), |
| /* |
| ** Message is an IDENTIFY, but lun is unknown. |
| ** Read the message, since we got it directly |
| ** from the SCSI BUS data lines. |
| ** Signal problem to C code for logging the event. |
| ** Send an ABORT_TASK_SET to clear all pending tasks. |
| */ |
| SCR_INT, |
| SIR_RESEL_BAD_LUN, |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin), |
| SCR_JUMP, |
| PADDRH (abort), |
| }/*-------------------------< BAD_I_T_L >------------------*/,{ |
| /* |
| ** We donnot have a task for that I_T_L. |
| ** Signal problem to C code for logging the event. |
| ** Send an ABORT_TASK_SET message. |
| */ |
| SCR_INT, |
| SIR_RESEL_BAD_I_T_L, |
| SCR_JUMP, |
| PADDRH (abort), |
| }/*-------------------------< BAD_I_T_L_Q >----------------*/,{ |
| /* |
| ** We donnot have a task that matches the tag. |
| ** Signal problem to C code for logging the event. |
| ** Send an ABORT_TASK message. |
| */ |
| SCR_INT, |
| SIR_RESEL_BAD_I_T_L_Q, |
| SCR_JUMP, |
| PADDRH (aborttag), |
| }/*-------------------------< BAD_TARGET >-----------------*/,{ |
| /* |
| ** We donnot know the target that reselected us. |
| ** Grab the first message if any (IDENTIFY). |
| ** Signal problem to C code for logging the event. |
| ** TARGET_RESET message. |
| */ |
| SCR_INT, |
| SIR_RESEL_BAD_TARGET, |
| SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)), |
| 8, |
| SCR_MOVE_ABS (1) ^ SCR_MSG_IN, |
| NADDR (msgin), |
| SCR_JUMP, |
| PADDRH (reset), |
| }/*-------------------------< BAD_STATUS >-----------------*/,{ |
| /* |
| ** If command resulted in either QUEUE FULL, |
| ** CHECK CONDITION or COMMAND TERMINATED, |
| ** call the C code. |
| */ |
| SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)), |
| SIR_BAD_STATUS, |
| SCR_INT ^ IFTRUE (DATA (S_CHECK_COND)), |
| SIR_BAD_STATUS, |
| SCR_INT ^ IFTRUE (DATA (S_TERMINATED)), |
| SIR_BAD_STATUS, |
| SCR_RETURN, |
| 0, |
| }/*-------------------------< START_RAM >-------------------*/,{ |
| /* |
| ** Load the script into on-chip RAM, |
| ** and jump to start point. |
| */ |
| SCR_COPY_F (4), |
| RADDR (scratcha), |
| PADDRH (start_ram0), |
| /* |
| ** Flush script prefetch if required |
| */ |
| PREFETCH_FLUSH |
| SCR_COPY (sizeof (struct script)), |
| }/*-------------------------< START_RAM0 >--------------------*/,{ |
| 0, |
| PADDR (start), |
| SCR_JUMP, |
| PADDR (start), |
| }/*-------------------------< STO_RESTART >-------------------*/,{ |
| /* |
| ** |
| ** Repair start queue (e.g. next time use the next slot) |
| ** and jump to start point. |
| */ |
| SCR_COPY (4), |
| RADDR (temp), |
| PADDR (startpos), |
| SCR_JUMP, |
| PADDR (start), |
| }/*-------------------------< WAIT_DMA >-------------------*/,{ |
| /* |
| ** For HP Zalon/53c720 systems, the Zalon interface |
| ** between CPU and 53c720 does prefetches, which causes |
| ** problems with self modifying scripts. The problem |
| ** is overcome by calling a dummy subroutine after each |
| ** modification, to force a refetch of the script on |
| ** return from the subroutine. |
| */ |
| SCR_RETURN, |
| 0, |
| }/*-------------------------< SNOOPTEST >-------------------*/,{ |
| /* |
| ** Read the variable. |
| */ |
| SCR_COPY (4), |
| NADDR(ncr_cache), |
| RADDR (scratcha), |
| /* |
| ** Write the variable. |
| */ |
| SCR_COPY (4), |
| RADDR (temp), |
| NADDR(ncr_cache), |
| /* |
| ** Read back the variable. |
| */ |
| SCR_COPY (4), |
| NADDR(ncr_cache), |
| RADDR (temp), |
| }/*-------------------------< SNOOPEND >-------------------*/,{ |
| /* |
| ** And stop. |
| */ |
| SCR_INT, |
| 99, |
| }/*--------------------------------------------------------*/ |
| }; |
| |
| /*========================================================== |
| ** |
| ** |
| ** Fill in #define dependent parts of the script |
| ** |
| ** |
| **========================================================== |
| */ |
| |
| void __init ncr_script_fill (struct script * scr, struct scripth * scrh) |
| { |
| int i; |
| ncrcmd *p; |
| |
| p = scrh->tryloop; |
| for (i=0; i<MAX_START; i++) { |
| *p++ =SCR_CALL; |
| *p++ =PADDR (idle); |
| } |
| |
| BUG_ON((u_long)p != (u_long)&scrh->tryloop + sizeof (scrh->tryloop)); |
| |
| #ifdef SCSI_NCR_CCB_DONE_SUPPORT |
| |
| p = scrh->done_queue; |
| for (i = 0; i<MAX_DONE; i++) { |
| *p++ =SCR_COPY (sizeof(struct ccb *)); |
| *p++ =NADDR (header.cp); |
| *p++ =NADDR (ccb_done[i]); |
| *p++ =SCR_CALL; |
| *p++ =PADDR (done_end); |
| } |
| |
| BUG_ON((u_long)p != (u_long)&scrh->done_queue+sizeof(scrh->done_queue)); |
| |
| #endif /* SCSI_NCR_CCB_DONE_SUPPORT */ |
| |
| p = scrh->hdata_in; |
| for (i=0; i<MAX_SCATTERH; i++) { |
| *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)); |
| *p++ =PADDR (dispatch); |
| *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN; |
| *p++ =offsetof (struct dsb, data[i]); |
| } |
| |
| BUG_ON((u_long)p != (u_long)&scrh->hdata_in + sizeof (scrh->hdata_in)); |
| |
| p = scr->data_in; |
| for (i=MAX_SCATTERH; i<MAX_SCATTERH+MAX_SCATTERL; i++) { |
| *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)); |
| *p++ =PADDR (dispatch); |
| *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN; |
| *p++ =offsetof (struct dsb, data[i]); |
| } |
| |
| BUG_ON((u_long)p != (u_long)&scr->data_in + sizeof (scr->data_in)); |
| |
| p = scrh->hdata_out; |
| for (i=0; i<MAX_SCATTERH; i++) { |
| *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)); |
| *p++ =PADDR (dispatch); |
| *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT; |
| *p++ =offsetof (struct dsb, data[i]); |
| } |
| |
| BUG_ON((u_long)p != (u_long)&scrh->hdata_out + sizeof (scrh->hdata_out)); |
| |
| p = scr->data_out; |
| for (i=MAX_SCATTERH; i<MAX_SCATTERH+MAX_SCATTERL; i++) { |
| *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)); |
| *p++ =PADDR (dispatch); |
| *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT; |
| *p++ =offsetof (struct dsb, data[i]); |
| } |
| |
| BUG_ON((u_long) p != (u_long)&scr->data_out + sizeof (scr->data_out)); |
| } |
| |
| /*========================================================== |
| ** |
| ** |
| ** Copy and rebind a script. |
| ** |
| ** |
| **========================================================== |
| */ |
| |
| static void __init |
| ncr_script_copy_and_bind (struct ncb *np, ncrcmd *src, ncrcmd *dst, int len) |
| { |
| ncrcmd opcode, new, old, tmp1, tmp2; |
| ncrcmd *start, *end; |
| int relocs; |
| int opchanged = 0; |
| |
| start = src; |
| end = src + len/4; |
| |
| while (src < end) { |
| |
| opcode = *src++; |
| *dst++ = cpu_to_scr(opcode); |
| |
| /* |
| ** If we forget to change the length |
| ** in struct script, a field will be |
| ** padded with 0. This is an illegal |
| ** command. |
| */ |
| |
| if (opcode == 0) { |
| printk (KERN_ERR "%s: ERROR0 IN SCRIPT at %d.\n", |
| ncr_name(np), (int) (src-start-1)); |
| mdelay(1000); |
| } |
| |
|
|